ASoC: Updates for v3.18
- More componentisation work from Lars-Peter, this time mainly cleaning up the suspend and bias level transition callbacks. - Real system support for the Intel drivers and a bunch of fixes and enhancements for the associated CODEC drivers, this is going to need a lot quirks over time due to the lack of any firmware description of the boards. - Jack detect support for simple card from Dylan Reid. - A bunch of small fixes and enhancements for the Freescale drivers. - New drivers for Analog Devices SSM4567, Cirrus Logic CS35L32, Everest Semiconductor ES8328 and Freescale cards using the ASRC in newer i.MX processors. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQEcBAABAgAGBQJUMoHRAAoJECTWi3JdVIfQGXUH/RWQ6/Ey70SPgUdWWQ42PFey sBq/Hl69F8/JNxW6EDA4GEg6ue880Gek0oGqioxtN6Ku0Vm/WSqDWnKcTAGl4dDO AefC4FwekZWCYQi3VTNIvMEqfUWkcofTLVwjdh/PUZxniahkiGA81UJ1mQNXBxLF UusrK0fIAxQgiNsCcPZ94knJiqZVBWgbRv/mCXY9K1/jqITNKd/ZVEMkOPk/p00q cH9LIx8EknRV3HyJNZQ0xpmhpuMzLy6Agf7Oeq/m5kDqq1stmClvibPYkdqkdkto jYwKaPh18dNHlUmm1w/G7X20kCidhbiwRjS/iIzx3cfIrWkiz90/BSRFKs8pqSo= =7PPg -----END PGP SIGNATURE----- Merge tag 'asoc-v3.18' into asoc-linus ASoC: Updates for v3.18 - More componentisation work from Lars-Peter, this time mainly cleaning up the suspend and bias level transition callbacks. - Real system support for the Intel drivers and a bunch of fixes and enhancements for the associated CODEC drivers, this is going to need a lot quirks over time due to the lack of any firmware description of the boards. - Jack detect support for simple card from Dylan Reid. - A bunch of small fixes and enhancements for the Freescale drivers. - New drivers for Analog Devices SSM4567, Cirrus Logic CS35L32, Everest Semiconductor ES8328 and Freescale cards using the ASRC in newer i.MX processors. # gpg: Signature made Mon 06 Oct 2014 12:49:37 BST using RSA key ID 5D5487D0 # gpg: Good signature from "Mark Brown <broonie@sirena.org.uk>" # gpg: aka "Mark Brown <broonie@debian.org>" # gpg: aka "Mark Brown <broonie@kernel.org>" # gpg: aka "Mark Brown <broonie@tardis.ed.ac.uk>" # gpg: aka "Mark Brown <broonie@linaro.org>" # gpg: aka "Mark Brown <Mark.Brown@linaro.org>"
This commit is contained in:
commit
7b8ab38e8d
47
Documentation/devicetree/bindings/regmap/regmap.txt
Normal file
47
Documentation/devicetree/bindings/regmap/regmap.txt
Normal file
@ -0,0 +1,47 @@
|
||||
Device-Tree binding for regmap
|
||||
|
||||
The endianness mode of CPU & Device scenarios:
|
||||
Index Device Endianness properties
|
||||
---------------------------------------------------
|
||||
1 BE 'big-endian'
|
||||
2 LE 'little-endian'
|
||||
|
||||
For one device driver, which will run in different scenarios above
|
||||
on different SoCs using the devicetree, we need one way to simplify
|
||||
this.
|
||||
|
||||
Required properties:
|
||||
- {big,little}-endian: these are boolean properties, if absent
|
||||
meaning that the CPU and the Device are in the same endianness mode,
|
||||
these properties are for register values and all the buffers only.
|
||||
|
||||
Examples:
|
||||
Scenario 1 : CPU in LE mode & device in LE mode.
|
||||
dev: dev@40031000 {
|
||||
compatible = "name";
|
||||
reg = <0x40031000 0x1000>;
|
||||
...
|
||||
};
|
||||
|
||||
Scenario 2 : CPU in LE mode & device in BE mode.
|
||||
dev: dev@40031000 {
|
||||
compatible = "name";
|
||||
reg = <0x40031000 0x1000>;
|
||||
...
|
||||
big-endian;
|
||||
};
|
||||
|
||||
Scenario 3 : CPU in BE mode & device in BE mode.
|
||||
dev: dev@40031000 {
|
||||
compatible = "name";
|
||||
reg = <0x40031000 0x1000>;
|
||||
...
|
||||
};
|
||||
|
||||
Scenario 4 : CPU in BE mode & device in LE mode.
|
||||
dev: dev@40031000 {
|
||||
compatible = "name";
|
||||
reg = <0x40031000 0x1000>;
|
||||
...
|
||||
little-endian;
|
||||
};
|
19
Documentation/devicetree/bindings/sound/adi,ssm2602.txt
Normal file
19
Documentation/devicetree/bindings/sound/adi,ssm2602.txt
Normal file
@ -0,0 +1,19 @@
|
||||
Analog Devices SSM2602, SSM2603 and SSM2604 I2S audio CODEC devices
|
||||
|
||||
SSM2602 support both I2C and SPI as the configuration interface,
|
||||
the selection is made by the MODE strap-in pin.
|
||||
SSM2603 and SSM2604 only support I2C as the configuration interface.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : One of "adi,ssm2602", "adi,ssm2603" or "adi,ssm2604"
|
||||
|
||||
- reg : the I2C address of the device for I2C, the chip select
|
||||
number for SPI.
|
||||
|
||||
Example:
|
||||
|
||||
ssm2602: ssm2602@1a {
|
||||
compatible = "adi,ssm2602";
|
||||
reg = <0x1a>;
|
||||
};
|
62
Documentation/devicetree/bindings/sound/cs35l32.txt
Normal file
62
Documentation/devicetree/bindings/sound/cs35l32.txt
Normal file
@ -0,0 +1,62 @@
|
||||
CS35L32 audio CODEC
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "cirrus,cs35l32"
|
||||
|
||||
- reg : the I2C address of the device for I2C. Address is determined by the level
|
||||
of the AD0 pin. Level 0 is 0x40 while Level 1 is 0x41.
|
||||
|
||||
- VA-supply, VP-supply : power supplies for the device,
|
||||
as covered in Documentation/devicetree/bindings/regulator/regulator.txt.
|
||||
|
||||
Optional properties:
|
||||
|
||||
- reset-gpios : a GPIO spec for the reset pin. If specified, it will be
|
||||
deasserted before communication to the codec starts.
|
||||
|
||||
- cirrus,boost-manager : Boost voltage control.
|
||||
0 = Automatically managed. Boost-converter output voltage is the higher
|
||||
of the two: Class G or adaptive LED voltage.
|
||||
1 = Automatically managed irrespective of audio, adapting for low-power
|
||||
dissipation when LEDs are ON, and operating in Fixed-Boost Bypass Mode
|
||||
if LEDs are OFF (VBST = VP).
|
||||
2 = (Default) Boost voltage fixed in Bypass Mode (VBST = VP).
|
||||
3 = Boost voltage fixed at 5 V.
|
||||
|
||||
- cirrus,sdout-datacfg : Data configuration for dual CS35L32 applications only.
|
||||
Determines the data packed in a two-CS35L32 configuration.
|
||||
0 = Left/right channels VMON[11:0], IMON[11:0], VPMON[7:0].
|
||||
1 = Left/right channels VMON[11:0], IMON[11:0], STATUS.
|
||||
2 = (Default) left/right channels VMON[15:0], IMON [15:0].
|
||||
3 = Left/right channels VPMON[7:0], STATUS.
|
||||
|
||||
- cirrus,sdout-share : SDOUT sharing. Determines whether one or two CS35L32
|
||||
devices are on board sharing SDOUT.
|
||||
0 = (Default) One IC.
|
||||
1 = Two IC's.
|
||||
|
||||
- cirrus,battery-recovery : Low battery nominal recovery threshold, rising VP.
|
||||
0 = 3.1V
|
||||
1 = 3.2V
|
||||
2 = 3.3V (Default)
|
||||
3 = 3.4V
|
||||
|
||||
- cirrus,battery-threshold : Low battery nominal threshold, falling VP.
|
||||
0 = 3.1V
|
||||
1 = 3.2V
|
||||
2 = 3.3V
|
||||
3 = 3.4V (Default)
|
||||
4 = 3.5V
|
||||
5 = 3.6V
|
||||
|
||||
Example:
|
||||
|
||||
codec: codec@40 {
|
||||
compatible = "cirrus,cs35l32";
|
||||
reg = <0x40>;
|
||||
reset-gpios = <&gpio 10 0>;
|
||||
cirrus,boost-manager = <0x03>;
|
||||
cirrus,sdout-datacfg = <0x02>;
|
||||
VA-supply = <®_audio>;
|
||||
};
|
38
Documentation/devicetree/bindings/sound/es8328.txt
Normal file
38
Documentation/devicetree/bindings/sound/es8328.txt
Normal file
@ -0,0 +1,38 @@
|
||||
Everest ES8328 audio CODEC
|
||||
|
||||
This device supports both I2C and SPI.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "everest,es8328"
|
||||
- DVDD-supply : Regulator providing digital core supply voltage 1.8 - 3.6V
|
||||
- AVDD-supply : Regulator providing analog supply voltage 3.3V
|
||||
- PVDD-supply : Regulator providing digital IO supply voltage 1.8 - 3.6V
|
||||
- IPVDD-supply : Regulator providing analog output voltage 3.3V
|
||||
- clocks : A 22.5792 or 11.2896 MHz clock
|
||||
- reg : the I2C address of the device for I2C, the chip select number for SPI
|
||||
|
||||
Pins on the device (for linking into audio routes):
|
||||
|
||||
* LOUT1
|
||||
* LOUT2
|
||||
* ROUT1
|
||||
* ROUT2
|
||||
* LINPUT1
|
||||
* RINPUT1
|
||||
* LINPUT2
|
||||
* RINPUT2
|
||||
* Mic Bias
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
codec: es8328@11 {
|
||||
compatible = "everest,es8328";
|
||||
DVDD-supply = <®_3p3v>;
|
||||
AVDD-supply = <®_3p3v>;
|
||||
PVDD-supply = <®_3p3v>;
|
||||
HPVDD-supply = <®_3p3v>;
|
||||
clocks = <&clks 169>;
|
||||
reg = <0x11>;
|
||||
};
|
@ -7,7 +7,8 @@ other DSPs. It has up to six transmitters and four receivers.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : Compatible list, must contain "fsl,imx35-esai".
|
||||
- compatible : Compatible list, must contain "fsl,imx35-esai" or
|
||||
"fsl,vf610-esai"
|
||||
|
||||
- reg : Offset and length of the register set for the device.
|
||||
|
||||
|
@ -58,13 +58,7 @@ Optional properties:
|
||||
Documentation/devicetree/bindings/dma/dma.txt.
|
||||
- dma-names: Two dmas have to be defined, "tx" and "rx", if fsl,imx-fiq
|
||||
is not defined.
|
||||
- fsl,mode: The operating mode for the SSI interface.
|
||||
"i2s-slave" - I2S mode, SSI is clock slave
|
||||
"i2s-master" - I2S mode, SSI is clock master
|
||||
"lj-slave" - left-justified mode, SSI is clock slave
|
||||
"lj-master" - l.j. mode, SSI is clock master
|
||||
"rj-slave" - right-justified mode, SSI is clock slave
|
||||
"rj-master" - r.j., SSI is clock master
|
||||
- fsl,mode: The operating mode for the AC97 interface only.
|
||||
"ac97-slave" - AC97 mode, SSI is clock slave
|
||||
"ac97-master" - AC97 mode, SSI is clock master
|
||||
|
||||
|
82
Documentation/devicetree/bindings/sound/fsl-asoc-card.txt
Normal file
82
Documentation/devicetree/bindings/sound/fsl-asoc-card.txt
Normal file
@ -0,0 +1,82 @@
|
||||
Freescale Generic ASoC Sound Card with ASRC support
|
||||
|
||||
The Freescale Generic ASoC Sound Card can be used, ideally, for all Freescale
|
||||
SoCs connecting with external CODECs.
|
||||
|
||||
The idea of this generic sound card is a bit like ASoC Simple Card. However,
|
||||
for Freescale SoCs (especially those released in recent years), most of them
|
||||
have ASRC (Documentation/devicetree/bindings/sound/fsl,asrc.txt) inside. And
|
||||
this is a specific feature that might be painstakingly controlled and merged
|
||||
into the Simple Card.
|
||||
|
||||
So having this generic sound card allows all Freescale SoC users to benefit
|
||||
from the simplification of a new card support and the capability of the wide
|
||||
sample rates support through ASRC.
|
||||
|
||||
Note: The card is initially designed for those sound cards who use I2S and
|
||||
PCM DAI formats. However, it'll be also possible to support those non
|
||||
I2S/PCM type sound cards, such as S/PDIF audio and HDMI audio, as long
|
||||
as the driver has been properly upgraded.
|
||||
|
||||
|
||||
The compatible list for this generic sound card currently:
|
||||
"fsl,imx-audio-cs42888"
|
||||
|
||||
"fsl,imx-audio-wm8962"
|
||||
(compatible with Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt)
|
||||
|
||||
"fsl,imx-audio-sgtl5000"
|
||||
(compatible with Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt)
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : Contains one of entries in the compatible list.
|
||||
|
||||
- model : The user-visible name of this sound complex
|
||||
|
||||
- audio-cpu : The phandle of an CPU DAI controller
|
||||
|
||||
- audio-codec : The phandle of an audio codec
|
||||
|
||||
- audio-routing : A list of the connections between audio components.
|
||||
Each entry is a pair of strings, the first being the
|
||||
connection's sink, the second being the connection's
|
||||
source. There're a few pre-designed board connectors:
|
||||
* Line Out Jack
|
||||
* Line In Jack
|
||||
* Headphone Jack
|
||||
* Mic Jack
|
||||
* Ext Spk
|
||||
* AMIC (stands for Analog Microphone Jack)
|
||||
* DMIC (stands for Digital Microphone Jack)
|
||||
|
||||
Note: The "Mic Jack" and "AMIC" are redundant while
|
||||
coexsiting in order to support the old bindings
|
||||
of wm8962 and sgtl5000.
|
||||
|
||||
Optional properties:
|
||||
|
||||
- audio-asrc : The phandle of ASRC. It can be absent if there's no
|
||||
need to add ASRC support via DPCM.
|
||||
|
||||
Example:
|
||||
sound-cs42888 {
|
||||
compatible = "fsl,imx-audio-cs42888";
|
||||
model = "cs42888-audio";
|
||||
audio-cpu = <&esai>;
|
||||
audio-asrc = <&asrc>;
|
||||
audio-codec = <&cs42888>;
|
||||
audio-routing =
|
||||
"Line Out Jack", "AOUT1L",
|
||||
"Line Out Jack", "AOUT1R",
|
||||
"Line Out Jack", "AOUT2L",
|
||||
"Line Out Jack", "AOUT2R",
|
||||
"Line Out Jack", "AOUT3L",
|
||||
"Line Out Jack", "AOUT3R",
|
||||
"Line Out Jack", "AOUT4L",
|
||||
"Line Out Jack", "AOUT4R",
|
||||
"AIN1L", "Line In Jack",
|
||||
"AIN1R", "Line In Jack",
|
||||
"AIN2L", "Line In Jack",
|
||||
"AIN2R", "Line In Jack";
|
||||
};
|
@ -18,12 +18,26 @@ Required properties:
|
||||
- pinctrl-names: Must contain a "default" entry.
|
||||
- pinctrl-NNN: One property must exist for each entry in pinctrl-names.
|
||||
See ../pinctrl/pinctrl-bindings.txt for details of the property values.
|
||||
- big-endian-regs: If this property is absent, the little endian mode will
|
||||
be in use as default, or the big endian mode will be in use for all the
|
||||
device registers.
|
||||
- big-endian-data: If this property is absent, the little endian mode will
|
||||
be in use as default, or the big endian mode will be in use for all the
|
||||
fifo data.
|
||||
- big-endian: Boolean property, required if all the FTM_PWM registers
|
||||
are big-endian rather than little-endian.
|
||||
- lsb-first: Configures whether the LSB or the MSB is transmitted first for
|
||||
the fifo data. If this property is absent, the MSB is transmitted first as
|
||||
default, or the LSB is transmitted first.
|
||||
- fsl,sai-synchronous-rx: This is a boolean property. If present, indicating
|
||||
that SAI will work in the synchronous mode (sync Tx with Rx) which means
|
||||
both the transimitter and receiver will send and receive data by following
|
||||
receiver's bit clocks and frame sync clocks.
|
||||
- fsl,sai-asynchronous: This is a boolean property. If present, indicating
|
||||
that SAI will work in the asynchronous mode, which means both transimitter
|
||||
and receiver will send and receive data by following their own bit clocks
|
||||
and frame sync clocks separately.
|
||||
|
||||
Note:
|
||||
- If both fsl,sai-asynchronous and fsl,sai-synchronous-rx are absent, the
|
||||
default synchronous mode (sync Rx with Tx) will be used, which means both
|
||||
transimitter and receiver will send and receive data by following clocks
|
||||
of transimitter.
|
||||
- fsl,sai-asynchronous and fsl,sai-synchronous-rx are exclusive.
|
||||
|
||||
Example:
|
||||
sai2: sai@40031000 {
|
||||
@ -38,6 +52,6 @@ sai2: sai@40031000 {
|
||||
dma-names = "tx", "rx";
|
||||
dmas = <&edma0 0 VF610_EDMA_MUXID0_SAI2_TX>,
|
||||
<&edma0 0 VF610_EDMA_MUXID0_SAI2_RX>;
|
||||
big-endian-regs;
|
||||
big-endian-data;
|
||||
big-endian;
|
||||
lsb-first;
|
||||
};
|
||||
|
60
Documentation/devicetree/bindings/sound/imx-audio-es8328.txt
Normal file
60
Documentation/devicetree/bindings/sound/imx-audio-es8328.txt
Normal file
@ -0,0 +1,60 @@
|
||||
Freescale i.MX audio complex with ES8328 codec
|
||||
|
||||
Required properties:
|
||||
- compatible : "fsl,imx-audio-es8328"
|
||||
- model : The user-visible name of this sound complex
|
||||
- ssi-controller : The phandle of the i.MX SSI controller
|
||||
- jack-gpio : Optional GPIO for headphone jack
|
||||
- audio-amp-supply : Power regulator for speaker amps
|
||||
- audio-codec : The phandle of the ES8328 audio codec
|
||||
- audio-routing : A list of the connections between audio components.
|
||||
Each entry is a pair of strings, the first being the
|
||||
connection's sink, the second being the connection's
|
||||
source. Valid names could be power supplies, ES8328
|
||||
pins, and the jacks on the board:
|
||||
|
||||
Power supplies:
|
||||
* audio-amp
|
||||
|
||||
ES8328 pins:
|
||||
* LOUT1
|
||||
* LOUT2
|
||||
* ROUT1
|
||||
* ROUT2
|
||||
* LINPUT1
|
||||
* LINPUT2
|
||||
* RINPUT1
|
||||
* RINPUT2
|
||||
* Mic PGA
|
||||
|
||||
Board connectors:
|
||||
* Headphone
|
||||
* Speaker
|
||||
* Mic Jack
|
||||
- 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 (AUDMIX)
|
||||
|
||||
Note: The AUDMUX port numbering should start at 1, which is consistent with
|
||||
hardware manual.
|
||||
|
||||
Example:
|
||||
|
||||
sound {
|
||||
compatible = "fsl,imx-audio-es8328";
|
||||
model = "imx-audio-es8328";
|
||||
ssi-controller = <&ssi1>;
|
||||
audio-codec = <&codec>;
|
||||
jack-gpio = <&gpio5 15 0>;
|
||||
audio-amp-supply = <®_audio_amp>;
|
||||
audio-routing =
|
||||
"Speaker", "LOUT2",
|
||||
"Speaker", "ROUT2",
|
||||
"Speaker", "audio-amp",
|
||||
"Headphone", "ROUT1",
|
||||
"Headphone", "LOUT1",
|
||||
"LINPUT1", "Mic Jack",
|
||||
"RINPUT1", "Mic Jack",
|
||||
"Mic Jack", "Mic Bias";
|
||||
mux-int-port = <1>;
|
||||
mux-ext-port = <3>;
|
||||
};
|
@ -25,6 +25,7 @@ Required properties:
|
||||
|
||||
Optional properties:
|
||||
- nvidia,hp-det-gpios : The GPIO that detect headphones are plugged in
|
||||
- nvidia,mic-det-gpios : The GPIO that detect microphones are plugged in
|
||||
|
||||
Example:
|
||||
|
||||
|
59
Documentation/devicetree/bindings/sound/rt5677.txt
Normal file
59
Documentation/devicetree/bindings/sound/rt5677.txt
Normal file
@ -0,0 +1,59 @@
|
||||
RT5677 audio CODEC
|
||||
|
||||
This device supports I2C only.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "realtek,rt5677".
|
||||
|
||||
- reg : The I2C address of the device.
|
||||
|
||||
- interrupts : The CODEC's interrupt output.
|
||||
|
||||
- gpio-controller : Indicates this device is a GPIO controller.
|
||||
|
||||
- #gpio-cells : Should be two. The first cell is the pin number and the
|
||||
second cell is used to specify optional parameters (currently unused).
|
||||
|
||||
Optional properties:
|
||||
|
||||
- realtek,pow-ldo2-gpio : The GPIO that controls the CODEC's POW_LDO2 pin.
|
||||
|
||||
- realtek,in1-differential
|
||||
- realtek,in2-differential
|
||||
- realtek,lout1-differential
|
||||
- realtek,lout2-differential
|
||||
- realtek,lout3-differential
|
||||
Boolean. Indicate MIC1/2 input and LOUT1/2/3 outputs are differential,
|
||||
rather than single-ended.
|
||||
|
||||
Pins on the device (for linking into audio routes):
|
||||
|
||||
* IN1P
|
||||
* IN1N
|
||||
* IN2P
|
||||
* IN2N
|
||||
* MICBIAS1
|
||||
* DMIC1
|
||||
* DMIC2
|
||||
* DMIC3
|
||||
* DMIC4
|
||||
* LOUT1
|
||||
* LOUT2
|
||||
* LOUT3
|
||||
|
||||
Example:
|
||||
|
||||
rt5677 {
|
||||
compatible = "realtek,rt5677";
|
||||
reg = <0x2c>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <TEGRA_GPIO(W, 3) GPIO_ACTIVE_HIGH>;
|
||||
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
|
||||
realtek,pow-ldo2-gpio =
|
||||
<&gpio TEGRA_GPIO(V, 3) GPIO_ACTIVE_HIGH>;
|
||||
realtek,in1-differential = "true";
|
||||
};
|
@ -17,6 +17,10 @@ Optional properties:
|
||||
source.
|
||||
- simple-audio-card,mclk-fs : Multiplication factor between stream rate and codec
|
||||
mclk.
|
||||
- simple-audio-card,hp-det-gpio : Reference to GPIO that signals when
|
||||
headphones are attached.
|
||||
- simple-audio-card,mic-det-gpio : Reference to GPIO that signals when
|
||||
a microphone is attached.
|
||||
|
||||
Optional subnodes:
|
||||
|
||||
|
15
Documentation/devicetree/bindings/sound/ssm4567.txt
Normal file
15
Documentation/devicetree/bindings/sound/ssm4567.txt
Normal file
@ -0,0 +1,15 @@
|
||||
Analog Devices SSM4567 audio amplifier
|
||||
|
||||
This device supports I2C only.
|
||||
|
||||
Required properties:
|
||||
- compatible : Must be "adi,ssm4567"
|
||||
- reg : the I2C address of the device. This will either be 0x34 (LR_SEL/ADDR connected to AGND),
|
||||
0x35 (LR_SEL/ADDR connected to IOVDD) or 0x36 (LR_SEL/ADDR open).
|
||||
|
||||
Example:
|
||||
|
||||
ssm4567: ssm4567@34 {
|
||||
compatible = "adi,ssm4567";
|
||||
reg = <0x34>;
|
||||
};
|
@ -48,6 +48,7 @@ epfl Ecole Polytechnique Fédérale de Lausanne
|
||||
epson Seiko Epson Corp.
|
||||
est ESTeem Wireless Modems
|
||||
eukrea Eukréa Electromatique
|
||||
everest Everest Semiconductor Co. Ltd.
|
||||
excito Excito
|
||||
fsl Freescale Semiconductor
|
||||
GEFanuc GE Fanuc Intelligent Platforms Embedded Systems, Inc.
|
||||
|
@ -168,6 +168,8 @@ static struct regmap_bus regmap_i2c = {
|
||||
.write = regmap_i2c_write,
|
||||
.gather_write = regmap_i2c_gather_write,
|
||||
.read = regmap_i2c_read,
|
||||
.reg_format_endian_default = REGMAP_ENDIAN_BIG,
|
||||
.val_format_endian_default = REGMAP_ENDIAN_BIG,
|
||||
};
|
||||
|
||||
static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
|
||||
|
@ -109,6 +109,8 @@ static struct regmap_bus regmap_spi = {
|
||||
.async_alloc = regmap_spi_async_alloc,
|
||||
.read = regmap_spi_read,
|
||||
.read_flag_mask = 0x80,
|
||||
.reg_format_endian_default = REGMAP_ENDIAN_BIG,
|
||||
.val_format_endian_default = REGMAP_ENDIAN_BIG,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <linux/export.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
@ -448,6 +449,66 @@ int regmap_attach_dev(struct device *dev, struct regmap *map,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regmap_attach_dev);
|
||||
|
||||
static enum regmap_endian regmap_get_reg_endian(const struct regmap_bus *bus,
|
||||
const struct regmap_config *config)
|
||||
{
|
||||
enum regmap_endian endian;
|
||||
|
||||
/* Retrieve the endianness specification from the regmap config */
|
||||
endian = config->reg_format_endian;
|
||||
|
||||
/* If the regmap config specified a non-default value, use that */
|
||||
if (endian != REGMAP_ENDIAN_DEFAULT)
|
||||
return endian;
|
||||
|
||||
/* Retrieve the endianness specification from the bus config */
|
||||
if (bus && bus->reg_format_endian_default)
|
||||
endian = bus->reg_format_endian_default;
|
||||
|
||||
/* If the bus specified a non-default value, use that */
|
||||
if (endian != REGMAP_ENDIAN_DEFAULT)
|
||||
return endian;
|
||||
|
||||
/* Use this if no other value was found */
|
||||
return REGMAP_ENDIAN_BIG;
|
||||
}
|
||||
|
||||
static enum regmap_endian regmap_get_val_endian(struct device *dev,
|
||||
const struct regmap_bus *bus,
|
||||
const struct regmap_config *config)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
enum regmap_endian endian;
|
||||
|
||||
/* Retrieve the endianness specification from the regmap config */
|
||||
endian = config->val_format_endian;
|
||||
|
||||
/* If the regmap config specified a non-default value, use that */
|
||||
if (endian != REGMAP_ENDIAN_DEFAULT)
|
||||
return endian;
|
||||
|
||||
/* Parse the device's DT node for an endianness specification */
|
||||
if (of_property_read_bool(np, "big-endian"))
|
||||
endian = REGMAP_ENDIAN_BIG;
|
||||
else if (of_property_read_bool(np, "little-endian"))
|
||||
endian = REGMAP_ENDIAN_LITTLE;
|
||||
|
||||
/* If the endianness was specified in DT, use that */
|
||||
if (endian != REGMAP_ENDIAN_DEFAULT)
|
||||
return endian;
|
||||
|
||||
/* Retrieve the endianness specification from the bus config */
|
||||
if (bus && bus->val_format_endian_default)
|
||||
endian = bus->val_format_endian_default;
|
||||
|
||||
/* If the bus specified a non-default value, use that */
|
||||
if (endian != REGMAP_ENDIAN_DEFAULT)
|
||||
return endian;
|
||||
|
||||
/* Use this if no other value was found */
|
||||
return REGMAP_ENDIAN_BIG;
|
||||
}
|
||||
|
||||
/**
|
||||
* regmap_init(): Initialise register map
|
||||
*
|
||||
@ -551,17 +612,8 @@ struct regmap *regmap_init(struct device *dev,
|
||||
map->reg_read = _regmap_bus_read;
|
||||
}
|
||||
|
||||
reg_endian = config->reg_format_endian;
|
||||
if (reg_endian == REGMAP_ENDIAN_DEFAULT)
|
||||
reg_endian = bus->reg_format_endian_default;
|
||||
if (reg_endian == REGMAP_ENDIAN_DEFAULT)
|
||||
reg_endian = REGMAP_ENDIAN_BIG;
|
||||
|
||||
val_endian = config->val_format_endian;
|
||||
if (val_endian == REGMAP_ENDIAN_DEFAULT)
|
||||
val_endian = bus->val_format_endian_default;
|
||||
if (val_endian == REGMAP_ENDIAN_DEFAULT)
|
||||
val_endian = REGMAP_ENDIAN_BIG;
|
||||
reg_endian = regmap_get_reg_endian(bus, config);
|
||||
val_endian = regmap_get_val_endian(dev, bus, config);
|
||||
|
||||
switch (config->reg_bits + map->reg_shift) {
|
||||
case 2:
|
||||
|
26
include/dt-bindings/sound/cs35l32.h
Normal file
26
include/dt-bindings/sound/cs35l32.h
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef __DT_CS35L32_H
|
||||
#define __DT_CS35L32_H
|
||||
|
||||
#define CS35L32_BOOST_MGR_AUTO 0
|
||||
#define CS35L32_BOOST_MGR_AUTO_AUDIO 1
|
||||
#define CS35L32_BOOST_MGR_BYPASS 2
|
||||
#define CS35L32_BOOST_MGR_FIXED 3
|
||||
|
||||
#define CS35L32_DATA_CFG_LR_VP 0
|
||||
#define CS35L32_DATA_CFG_LR_STAT 1
|
||||
#define CS35L32_DATA_CFG_LR 2
|
||||
#define CS35L32_DATA_CFG_LR_VPSTAT 3
|
||||
|
||||
#define CS35L32_BATT_THRESH_3_1V 0
|
||||
#define CS35L32_BATT_THRESH_3_2V 1
|
||||
#define CS35L32_BATT_THRESH_3_3V 2
|
||||
#define CS35L32_BATT_THRESH_3_4V 3
|
||||
|
||||
#define CS35L32_BATT_RECOV_3_1V 0
|
||||
#define CS35L32_BATT_RECOV_3_2V 1
|
||||
#define CS35L32_BATT_RECOV_3_3V 2
|
||||
#define CS35L32_BATT_RECOV_3_4V 3
|
||||
#define CS35L32_BATT_RECOV_3_5V 4
|
||||
#define CS35L32_BATT_RECOV_3_6V 5
|
||||
|
||||
#endif /* __DT_CS35L32_H */
|
@ -20,6 +20,9 @@ struct rt5645_platform_data {
|
||||
/* 0 = IN2N; 1 = GPIO5; 2 = GPIO11 */
|
||||
unsigned int dmic2_data_pin;
|
||||
/* 0 = IN2P; 1 = GPIO6; 2 = GPIO10; 3 = GPIO12 */
|
||||
|
||||
unsigned int hp_det_gpio;
|
||||
bool gpio_hp_det_active_high;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -12,10 +12,21 @@
|
||||
#ifndef __LINUX_SND_RT5677_H
|
||||
#define __LINUX_SND_RT5677_H
|
||||
|
||||
enum rt5677_dmic2_clk {
|
||||
RT5677_DMIC_CLK1 = 0,
|
||||
RT5677_DMIC_CLK2 = 1,
|
||||
};
|
||||
|
||||
|
||||
struct rt5677_platform_data {
|
||||
/* IN1 IN2 can optionally be differential */
|
||||
/* IN1/IN2/LOUT1/LOUT2/LOUT3 can optionally be differential */
|
||||
bool in1_diff;
|
||||
bool in2_diff;
|
||||
bool lout1_diff;
|
||||
bool lout2_diff;
|
||||
bool lout3_diff;
|
||||
/* DMIC2 clock source selection */
|
||||
enum rt5677_dmic2_clk dmic2_clk_pin;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -432,6 +432,7 @@ int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm,
|
||||
int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm,
|
||||
const char *pin);
|
||||
void snd_soc_dapm_auto_nc_pins(struct snd_soc_card *card);
|
||||
unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol);
|
||||
|
||||
/* Mostly internal - should not normally be used */
|
||||
void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm);
|
||||
@ -587,13 +588,13 @@ struct snd_soc_dapm_context {
|
||||
enum snd_soc_bias_level suspend_bias_level;
|
||||
struct delayed_work delayed_work;
|
||||
unsigned int idle_bias_off:1; /* Use BIAS_OFF instead of STANDBY */
|
||||
|
||||
/* Go to BIAS_OFF in suspend if the DAPM context is idle */
|
||||
unsigned int suspend_bias_off:1;
|
||||
void (*seq_notifier)(struct snd_soc_dapm_context *,
|
||||
enum snd_soc_dapm_type, int);
|
||||
|
||||
struct device *dev; /* from parent - for debug */
|
||||
struct snd_soc_component *component; /* parent component */
|
||||
struct snd_soc_codec *codec; /* parent codec */
|
||||
struct snd_soc_card *card; /* parent card */
|
||||
|
||||
/* used during DAPM updates */
|
||||
|
@ -690,6 +690,17 @@ struct snd_soc_compr_ops {
|
||||
struct snd_soc_component_driver {
|
||||
const char *name;
|
||||
|
||||
/* Default control and setup, added after probe() is run */
|
||||
const struct snd_kcontrol_new *controls;
|
||||
unsigned int num_controls;
|
||||
const struct snd_soc_dapm_widget *dapm_widgets;
|
||||
unsigned int num_dapm_widgets;
|
||||
const struct snd_soc_dapm_route *dapm_routes;
|
||||
unsigned int num_dapm_routes;
|
||||
|
||||
int (*probe)(struct snd_soc_component *);
|
||||
void (*remove)(struct snd_soc_component *);
|
||||
|
||||
/* DT */
|
||||
int (*of_xlate_dai_name)(struct snd_soc_component *component,
|
||||
struct of_phandle_args *args,
|
||||
@ -697,6 +708,10 @@ struct snd_soc_component_driver {
|
||||
void (*seq_notifier)(struct snd_soc_component *, enum snd_soc_dapm_type,
|
||||
int subseq);
|
||||
int (*stream_event)(struct snd_soc_component *, int event);
|
||||
|
||||
/* probe ordering - for components with runtime dependencies */
|
||||
int probe_order;
|
||||
int remove_order;
|
||||
};
|
||||
|
||||
struct snd_soc_component {
|
||||
@ -710,6 +725,7 @@ struct snd_soc_component {
|
||||
|
||||
unsigned int ignore_pmdown_time:1; /* pmdown_time is ignored at stop */
|
||||
unsigned int registered_as_component:1;
|
||||
unsigned int probed:1;
|
||||
|
||||
struct list_head list;
|
||||
|
||||
@ -728,9 +744,35 @@ struct snd_soc_component {
|
||||
|
||||
struct mutex io_mutex;
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *debugfs_root;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* DO NOT use any of the fields below in drivers, they are temporary and
|
||||
* are going to be removed again soon. If you use them in driver code the
|
||||
* driver will be marked as BROKEN when these fields are removed.
|
||||
*/
|
||||
|
||||
/* Don't use these, use snd_soc_component_get_dapm() */
|
||||
struct snd_soc_dapm_context dapm;
|
||||
struct snd_soc_dapm_context *dapm_ptr;
|
||||
|
||||
const struct snd_kcontrol_new *controls;
|
||||
unsigned int num_controls;
|
||||
const struct snd_soc_dapm_widget *dapm_widgets;
|
||||
unsigned int num_dapm_widgets;
|
||||
const struct snd_soc_dapm_route *dapm_routes;
|
||||
unsigned int num_dapm_routes;
|
||||
struct snd_soc_codec *codec;
|
||||
|
||||
int (*probe)(struct snd_soc_component *);
|
||||
void (*remove)(struct snd_soc_component *);
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
void (*init_debugfs)(struct snd_soc_component *component);
|
||||
const char *debugfs_prefix;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* SoC Audio Codec device */
|
||||
@ -746,11 +788,9 @@ struct snd_soc_codec {
|
||||
struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */
|
||||
unsigned int cache_bypass:1; /* Suppress access to the cache */
|
||||
unsigned int suspended:1; /* Codec is in suspend PM state */
|
||||
unsigned int probed:1; /* Codec has been probed */
|
||||
unsigned int ac97_registered:1; /* Codec has been AC97 registered */
|
||||
unsigned int ac97_created:1; /* Codec has been created by SoC */
|
||||
unsigned int cache_init:1; /* codec cache has been initialized */
|
||||
u32 cache_only; /* Suppress writes to hardware */
|
||||
u32 cache_sync; /* Cache needs to be synced to hardware */
|
||||
|
||||
/* codec IO */
|
||||
@ -766,7 +806,6 @@ struct snd_soc_codec {
|
||||
struct snd_soc_dapm_context dapm;
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *debugfs_codec_root;
|
||||
struct dentry *debugfs_reg;
|
||||
#endif
|
||||
};
|
||||
@ -808,15 +847,12 @@ struct snd_soc_codec_driver {
|
||||
int (*set_bias_level)(struct snd_soc_codec *,
|
||||
enum snd_soc_bias_level level);
|
||||
bool idle_bias_off;
|
||||
bool suspend_bias_off;
|
||||
|
||||
void (*seq_notifier)(struct snd_soc_dapm_context *,
|
||||
enum snd_soc_dapm_type, int);
|
||||
|
||||
bool ignore_pmdown_time; /* Doesn't benefit from pmdown delay */
|
||||
|
||||
/* probe ordering - for components with runtime dependencies */
|
||||
int probe_order;
|
||||
int remove_order;
|
||||
};
|
||||
|
||||
/* SoC platform interface */
|
||||
@ -832,14 +868,6 @@ struct snd_soc_platform_driver {
|
||||
int (*pcm_new)(struct snd_soc_pcm_runtime *);
|
||||
void (*pcm_free)(struct snd_pcm *);
|
||||
|
||||
/* Default control and setup, added after probe() is run */
|
||||
const struct snd_kcontrol_new *controls;
|
||||
int num_controls;
|
||||
const struct snd_soc_dapm_widget *dapm_widgets;
|
||||
int num_dapm_widgets;
|
||||
const struct snd_soc_dapm_route *dapm_routes;
|
||||
int num_dapm_routes;
|
||||
|
||||
/*
|
||||
* For platform caused delay reporting.
|
||||
* Optional.
|
||||
@ -853,13 +881,6 @@ struct snd_soc_platform_driver {
|
||||
/* platform stream compress ops */
|
||||
const struct snd_compr_ops *compr_ops;
|
||||
|
||||
/* probe ordering - for components with runtime dependencies */
|
||||
int probe_order;
|
||||
int remove_order;
|
||||
|
||||
/* platform IO - used for platform DAPM */
|
||||
unsigned int (*read)(struct snd_soc_platform *, unsigned int);
|
||||
int (*write)(struct snd_soc_platform *, unsigned int, unsigned int);
|
||||
int (*bespoke_trigger)(struct snd_pcm_substream *, int);
|
||||
};
|
||||
|
||||
@ -874,15 +895,10 @@ struct snd_soc_platform {
|
||||
const struct snd_soc_platform_driver *driver;
|
||||
|
||||
unsigned int suspended:1; /* platform is suspended */
|
||||
unsigned int probed:1;
|
||||
|
||||
struct list_head list;
|
||||
|
||||
struct snd_soc_component component;
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *debugfs_platform_root;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct snd_soc_dai_link {
|
||||
@ -897,7 +913,7 @@ struct snd_soc_dai_link {
|
||||
* only for codec to codec links, or systems using device tree.
|
||||
*/
|
||||
const char *cpu_name;
|
||||
const struct device_node *cpu_of_node;
|
||||
struct device_node *cpu_of_node;
|
||||
/*
|
||||
* You MAY specify the DAI name of the CPU DAI. If this information is
|
||||
* omitted, the CPU-side DAI is matched using .cpu_name/.cpu_of_node
|
||||
@ -909,7 +925,7 @@ struct snd_soc_dai_link {
|
||||
* DT/OF node, but not both.
|
||||
*/
|
||||
const char *codec_name;
|
||||
const struct device_node *codec_of_node;
|
||||
struct device_node *codec_of_node;
|
||||
/* You MUST specify the DAI name within the codec */
|
||||
const char *codec_dai_name;
|
||||
|
||||
@ -922,7 +938,7 @@ struct snd_soc_dai_link {
|
||||
* do not need a platform.
|
||||
*/
|
||||
const char *platform_name;
|
||||
const struct device_node *platform_of_node;
|
||||
struct device_node *platform_of_node;
|
||||
int be_id; /* optional ID for machine driver BE identification */
|
||||
|
||||
const struct snd_soc_pcm_stream *params;
|
||||
@ -994,7 +1010,7 @@ struct snd_soc_aux_dev {
|
||||
const struct device_node *codec_of_node;
|
||||
|
||||
/* codec/machine specific init - e.g. add machine controls */
|
||||
int (*init)(struct snd_soc_dapm_context *dapm);
|
||||
int (*init)(struct snd_soc_component *component);
|
||||
};
|
||||
|
||||
/* SoC card */
|
||||
@ -1112,6 +1128,7 @@ struct snd_soc_pcm_runtime {
|
||||
struct snd_soc_platform *platform;
|
||||
struct snd_soc_dai *codec_dai;
|
||||
struct snd_soc_dai *cpu_dai;
|
||||
struct snd_soc_component *component; /* Only valid for AUX dev rtds */
|
||||
|
||||
struct snd_soc_dai **codec_dais;
|
||||
unsigned int num_codecs;
|
||||
@ -1260,9 +1277,6 @@ void snd_soc_component_async_complete(struct snd_soc_component *component);
|
||||
int snd_soc_component_test_bits(struct snd_soc_component *component,
|
||||
unsigned int reg, unsigned int mask, unsigned int value);
|
||||
|
||||
int snd_soc_component_init_io(struct snd_soc_component *component,
|
||||
struct regmap *regmap);
|
||||
|
||||
/* device driver data */
|
||||
|
||||
static inline void snd_soc_card_set_drvdata(struct snd_soc_card *card,
|
||||
@ -1276,26 +1290,37 @@ static inline void *snd_soc_card_get_drvdata(struct snd_soc_card *card)
|
||||
return card->drvdata;
|
||||
}
|
||||
|
||||
static inline void snd_soc_component_set_drvdata(struct snd_soc_component *c,
|
||||
void *data)
|
||||
{
|
||||
dev_set_drvdata(c->dev, data);
|
||||
}
|
||||
|
||||
static inline void *snd_soc_component_get_drvdata(struct snd_soc_component *c)
|
||||
{
|
||||
return dev_get_drvdata(c->dev);
|
||||
}
|
||||
|
||||
static inline void snd_soc_codec_set_drvdata(struct snd_soc_codec *codec,
|
||||
void *data)
|
||||
{
|
||||
dev_set_drvdata(codec->dev, data);
|
||||
snd_soc_component_set_drvdata(&codec->component, data);
|
||||
}
|
||||
|
||||
static inline void *snd_soc_codec_get_drvdata(struct snd_soc_codec *codec)
|
||||
{
|
||||
return dev_get_drvdata(codec->dev);
|
||||
return snd_soc_component_get_drvdata(&codec->component);
|
||||
}
|
||||
|
||||
static inline void snd_soc_platform_set_drvdata(struct snd_soc_platform *platform,
|
||||
void *data)
|
||||
{
|
||||
dev_set_drvdata(platform->dev, data);
|
||||
snd_soc_component_set_drvdata(&platform->component, data);
|
||||
}
|
||||
|
||||
static inline void *snd_soc_platform_get_drvdata(struct snd_soc_platform *platform)
|
||||
{
|
||||
return dev_get_drvdata(platform->dev);
|
||||
return snd_soc_component_get_drvdata(&platform->component);
|
||||
}
|
||||
|
||||
static inline void snd_soc_pcm_set_drvdata(struct snd_soc_pcm_runtime *rtd,
|
||||
|
@ -175,7 +175,7 @@ TRACE_EVENT(snd_soc_dapm_output_path,
|
||||
__entry->path_sink = (long)path->sink;
|
||||
),
|
||||
|
||||
TP_printk("%c%s -> %s -> %s\n",
|
||||
TP_printk("%c%s -> %s -> %s",
|
||||
(int) __entry->path_sink &&
|
||||
(int) __entry->path_connect ? '*' : ' ',
|
||||
__get_str(wname), __get_str(pname), __get_str(psname))
|
||||
@ -204,7 +204,7 @@ TRACE_EVENT(snd_soc_dapm_input_path,
|
||||
__entry->path_source = (long)path->source;
|
||||
),
|
||||
|
||||
TP_printk("%c%s <- %s <- %s\n",
|
||||
TP_printk("%c%s <- %s <- %s",
|
||||
(int) __entry->path_source &&
|
||||
(int) __entry->path_connect ? '*' : ' ',
|
||||
__get_str(wname), __get_str(pname), __get_str(psname))
|
||||
@ -226,7 +226,7 @@ TRACE_EVENT(snd_soc_dapm_connected,
|
||||
__entry->stream = stream;
|
||||
),
|
||||
|
||||
TP_printk("%s: found %d paths\n",
|
||||
TP_printk("%s: found %d paths",
|
||||
__entry->stream ? "capture" : "playback", __entry->paths)
|
||||
);
|
||||
|
||||
|
@ -1337,8 +1337,6 @@ static int pm860x_probe(struct snd_soc_codec *codec)
|
||||
}
|
||||
}
|
||||
|
||||
pm860x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
@ -1354,7 +1352,6 @@ static int pm860x_remove(struct snd_soc_codec *codec)
|
||||
|
||||
for (i = 3; i >= 0; i--)
|
||||
free_irq(pm860x->irq[i], pm860x);
|
||||
pm860x_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,7 @@ config SND_SOC_ALL_CODECS
|
||||
select SND_SOC_ALC5623 if I2C
|
||||
select SND_SOC_ALC5632 if I2C
|
||||
select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
|
||||
select SND_SOC_CS35L32 if I2C
|
||||
select SND_SOC_CS42L51_I2C if I2C
|
||||
select SND_SOC_CS42L52 if I2C && INPUT
|
||||
select SND_SOC_CS42L56 if I2C && INPUT
|
||||
@ -56,7 +57,10 @@ config SND_SOC_ALL_CODECS
|
||||
select SND_SOC_DA7213 if I2C
|
||||
select SND_SOC_DA732X if I2C
|
||||
select SND_SOC_DA9055 if I2C
|
||||
select SND_SOC_DMIC
|
||||
select SND_SOC_BT_SCO
|
||||
select SND_SOC_ES8328_SPI if SPI_MASTER
|
||||
select SND_SOC_ES8328_I2C if I2C
|
||||
select SND_SOC_ISABELLE if I2C
|
||||
select SND_SOC_JZ4740_CODEC
|
||||
select SND_SOC_LM4857 if I2C
|
||||
@ -90,6 +94,7 @@ config SND_SOC_ALL_CODECS
|
||||
select SND_SOC_SSM2518 if I2C
|
||||
select SND_SOC_SSM2602_SPI if SPI_MASTER
|
||||
select SND_SOC_SSM2602_I2C if I2C
|
||||
select SND_SOC_SSM4567 if I2C
|
||||
select SND_SOC_STA32X if I2C
|
||||
select SND_SOC_STA350 if I2C
|
||||
select SND_SOC_STA529 if I2C
|
||||
@ -323,6 +328,10 @@ config SND_SOC_ALC5632
|
||||
config SND_SOC_CQ0093VC
|
||||
tristate
|
||||
|
||||
config SND_SOC_CS35L32
|
||||
tristate "Cirrus Logic CS35L32 CODEC"
|
||||
depends on I2C
|
||||
|
||||
config SND_SOC_CS42L51
|
||||
tristate
|
||||
|
||||
@ -405,6 +414,17 @@ config SND_SOC_DMIC
|
||||
config SND_SOC_HDMI_CODEC
|
||||
tristate "HDMI stub CODEC"
|
||||
|
||||
config SND_SOC_ES8328
|
||||
tristate "Everest Semi ES8328 CODEC"
|
||||
|
||||
config SND_SOC_ES8328_I2C
|
||||
tristate
|
||||
select SND_SOC_ES8328
|
||||
|
||||
config SND_SOC_ES8328_SPI
|
||||
tristate
|
||||
select SND_SOC_ES8328
|
||||
|
||||
config SND_SOC_ISABELLE
|
||||
tristate
|
||||
|
||||
@ -464,6 +484,7 @@ config SND_SOC_RL6231
|
||||
|
||||
config SND_SOC_RT286
|
||||
tristate
|
||||
depends on I2C
|
||||
|
||||
config SND_SOC_RT5631
|
||||
tristate
|
||||
@ -520,12 +541,20 @@ config SND_SOC_SSM2602
|
||||
tristate
|
||||
|
||||
config SND_SOC_SSM2602_SPI
|
||||
tristate "Analog Devices SSM2602 CODEC - SPI"
|
||||
depends on SPI_MASTER
|
||||
select SND_SOC_SSM2602
|
||||
tristate
|
||||
select REGMAP_SPI
|
||||
|
||||
config SND_SOC_SSM2602_I2C
|
||||
tristate "Analog Devices SSM2602 CODEC - I2C"
|
||||
depends on I2C
|
||||
select SND_SOC_SSM2602
|
||||
tristate
|
||||
select REGMAP_I2C
|
||||
|
||||
config SND_SOC_SSM4567
|
||||
tristate "Analog Devices ssm4567 amplifier driver support"
|
||||
depends on I2C
|
||||
|
||||
config SND_SOC_STA32X
|
||||
tristate
|
||||
@ -712,7 +741,8 @@ config SND_SOC_WM8974
|
||||
tristate
|
||||
|
||||
config SND_SOC_WM8978
|
||||
tristate
|
||||
tristate "Wolfson Microelectronics WM8978 codec"
|
||||
depends on I2C
|
||||
|
||||
config SND_SOC_WM8983
|
||||
tristate
|
||||
|
@ -32,6 +32,7 @@ snd-soc-ak4671-objs := ak4671.o
|
||||
snd-soc-ak5386-objs := ak5386.o
|
||||
snd-soc-arizona-objs := arizona.o
|
||||
snd-soc-cq93vc-objs := cq93vc.o
|
||||
snd-soc-cs35l32-objs := cs35l32.o
|
||||
snd-soc-cs42l51-objs := cs42l51.o
|
||||
snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o
|
||||
snd-soc-cs42l52-objs := cs42l52.o
|
||||
@ -49,6 +50,9 @@ snd-soc-da732x-objs := da732x.o
|
||||
snd-soc-da9055-objs := da9055.o
|
||||
snd-soc-bt-sco-objs := bt-sco.o
|
||||
snd-soc-dmic-objs := dmic.o
|
||||
snd-soc-es8328-objs := es8328.o
|
||||
snd-soc-es8328-i2c-objs := es8328-i2c.o
|
||||
snd-soc-es8328-spi-objs := es8328-spi.o
|
||||
snd-soc-isabelle-objs := isabelle.o
|
||||
snd-soc-jz4740-codec-objs := jz4740.o
|
||||
snd-soc-l3-objs := l3.o
|
||||
@ -91,6 +95,7 @@ snd-soc-ssm2518-objs := ssm2518.o
|
||||
snd-soc-ssm2602-objs := ssm2602.o
|
||||
snd-soc-ssm2602-spi-objs := ssm2602-spi.o
|
||||
snd-soc-ssm2602-i2c-objs := ssm2602-i2c.o
|
||||
snd-soc-ssm4567-objs := ssm4567.o
|
||||
snd-soc-sta32x-objs := sta32x.o
|
||||
snd-soc-sta350-objs := sta350.o
|
||||
snd-soc-sta529-objs := sta529.o
|
||||
@ -203,6 +208,7 @@ obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o
|
||||
obj-$(CONFIG_SND_SOC_ALC5632) += snd-soc-alc5632.o
|
||||
obj-$(CONFIG_SND_SOC_ARIZONA) += snd-soc-arizona.o
|
||||
obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o
|
||||
obj-$(CONFIG_SND_SOC_CS35L32) += snd-soc-cs35l32.o
|
||||
obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o
|
||||
obj-$(CONFIG_SND_SOC_CS42L51_I2C) += snd-soc-cs42l51-i2c.o
|
||||
obj-$(CONFIG_SND_SOC_CS42L52) += snd-soc-cs42l52.o
|
||||
@ -220,6 +226,9 @@ obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o
|
||||
obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o
|
||||
obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o
|
||||
obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o
|
||||
obj-$(CONFIG_SND_SOC_ES8328) += snd-soc-es8328.o
|
||||
obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o
|
||||
obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o
|
||||
obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o
|
||||
obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
|
||||
obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
|
||||
@ -258,6 +267,7 @@ obj-$(CONFIG_SND_SOC_SSM2518) += snd-soc-ssm2518.o
|
||||
obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o
|
||||
obj-$(CONFIG_SND_SOC_SSM2602_SPI) += snd-soc-ssm2602-spi.o
|
||||
obj-$(CONFIG_SND_SOC_SSM2602_I2C) += snd-soc-ssm2602-i2c.o
|
||||
obj-$(CONFIG_SND_SOC_SSM4567) += snd-soc-ssm4567.o
|
||||
obj-$(CONFIG_SND_SOC_STA32X) += snd-soc-sta32x.o
|
||||
obj-$(CONFIG_SND_SOC_STA350) += snd-soc-sta350.o
|
||||
obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o
|
||||
|
@ -56,8 +56,7 @@
|
||||
#define GPIO31_DIR_OUTPUT 0x40
|
||||
|
||||
/* Macrocell register definitions */
|
||||
#define AB8500_CTRL3_REG 0x0200
|
||||
#define AB8500_GPIO_DIR4_REG 0x1013
|
||||
#define AB8500_GPIO_DIR4_REG 0x13 /* Bank AB8500_MISC */
|
||||
|
||||
/* Nr of FIR/IIR-coeff banks in ANC-block */
|
||||
#define AB8500_NR_OF_ANC_COEFF_BANKS 2
|
||||
@ -126,6 +125,8 @@ struct ab8500_codec_drvdata_dbg {
|
||||
|
||||
/* Private data for AB8500 device-driver */
|
||||
struct ab8500_codec_drvdata {
|
||||
struct regmap *regmap;
|
||||
|
||||
/* Sidetone */
|
||||
long *sid_fir_values;
|
||||
enum sid_state sid_status;
|
||||
@ -166,49 +167,35 @@ static inline const char *amic_type_str(enum amic_type type)
|
||||
*/
|
||||
|
||||
/* Read a register from the audio-bank of AB8500 */
|
||||
static unsigned int ab8500_codec_read_reg(struct snd_soc_codec *codec,
|
||||
unsigned int reg)
|
||||
static int ab8500_codec_read_reg(void *context, unsigned int reg,
|
||||
unsigned int *value)
|
||||
{
|
||||
struct device *dev = context;
|
||||
int status;
|
||||
unsigned int value = 0;
|
||||
|
||||
u8 value8;
|
||||
status = abx500_get_register_interruptible(codec->dev, AB8500_AUDIO,
|
||||
reg, &value8);
|
||||
if (status < 0) {
|
||||
dev_err(codec->dev,
|
||||
"%s: ERROR: Register (0x%02x:0x%02x) read failed (%d).\n",
|
||||
__func__, (u8)AB8500_AUDIO, (u8)reg, status);
|
||||
} else {
|
||||
dev_dbg(codec->dev,
|
||||
"%s: Read 0x%02x from register 0x%02x:0x%02x\n",
|
||||
__func__, value8, (u8)AB8500_AUDIO, (u8)reg);
|
||||
value = (unsigned int)value8;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/* Write to a register in the audio-bank of AB8500 */
|
||||
static int ab8500_codec_write_reg(struct snd_soc_codec *codec,
|
||||
unsigned int reg, unsigned int value)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = abx500_set_register_interruptible(codec->dev, AB8500_AUDIO,
|
||||
reg, value);
|
||||
if (status < 0)
|
||||
dev_err(codec->dev,
|
||||
"%s: ERROR: Register (%02x:%02x) write failed (%d).\n",
|
||||
__func__, (u8)AB8500_AUDIO, (u8)reg, status);
|
||||
else
|
||||
dev_dbg(codec->dev,
|
||||
"%s: Wrote 0x%02x into register %02x:%02x\n",
|
||||
__func__, (u8)value, (u8)AB8500_AUDIO, (u8)reg);
|
||||
status = abx500_get_register_interruptible(dev, AB8500_AUDIO,
|
||||
reg, &value8);
|
||||
*value = (unsigned int)value8;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Write to a register in the audio-bank of AB8500 */
|
||||
static int ab8500_codec_write_reg(void *context, unsigned int reg,
|
||||
unsigned int value)
|
||||
{
|
||||
struct device *dev = context;
|
||||
|
||||
return abx500_set_register_interruptible(dev, AB8500_AUDIO,
|
||||
reg, value);
|
||||
}
|
||||
|
||||
static const struct regmap_config ab8500_codec_regmap = {
|
||||
.reg_read = ab8500_codec_read_reg,
|
||||
.reg_write = ab8500_codec_write_reg,
|
||||
};
|
||||
|
||||
/*
|
||||
* Controls - DAPM
|
||||
*/
|
||||
@ -1968,16 +1955,16 @@ static int ab8500_audio_setup_mics(struct snd_soc_codec *codec,
|
||||
dev_dbg(codec->dev, "%s: Enter.\n", __func__);
|
||||
|
||||
/* Set DMic-clocks to outputs */
|
||||
status = abx500_get_register_interruptible(codec->dev, (u8)AB8500_MISC,
|
||||
(u8)AB8500_GPIO_DIR4_REG,
|
||||
status = abx500_get_register_interruptible(codec->dev, AB8500_MISC,
|
||||
AB8500_GPIO_DIR4_REG,
|
||||
&value8);
|
||||
if (status < 0)
|
||||
return status;
|
||||
value = value8 | GPIO27_DIR_OUTPUT | GPIO29_DIR_OUTPUT |
|
||||
GPIO31_DIR_OUTPUT;
|
||||
status = abx500_set_register_interruptible(codec->dev,
|
||||
(u8)AB8500_MISC,
|
||||
(u8)AB8500_GPIO_DIR4_REG,
|
||||
AB8500_MISC,
|
||||
AB8500_GPIO_DIR4_REG,
|
||||
value);
|
||||
if (status < 0)
|
||||
return status;
|
||||
@ -2565,9 +2552,6 @@ static int ab8500_codec_probe(struct snd_soc_codec *codec)
|
||||
|
||||
static struct snd_soc_codec_driver ab8500_codec_driver = {
|
||||
.probe = ab8500_codec_probe,
|
||||
.read = ab8500_codec_read_reg,
|
||||
.write = ab8500_codec_write_reg,
|
||||
.reg_word_size = sizeof(u8),
|
||||
.controls = ab8500_ctrls,
|
||||
.num_controls = ARRAY_SIZE(ab8500_ctrls),
|
||||
.dapm_widgets = ab8500_dapm_widgets,
|
||||
@ -2592,6 +2576,15 @@ static int ab8500_codec_driver_probe(struct platform_device *pdev)
|
||||
drvdata->anc_status = ANC_UNCONFIGURED;
|
||||
dev_set_drvdata(&pdev->dev, drvdata);
|
||||
|
||||
drvdata->regmap = devm_regmap_init(&pdev->dev, NULL, &pdev->dev,
|
||||
&ab8500_codec_regmap);
|
||||
if (IS_ERR(drvdata->regmap)) {
|
||||
status = PTR_ERR(drvdata->regmap);
|
||||
dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n",
|
||||
__func__, status);
|
||||
return status;
|
||||
}
|
||||
|
||||
dev_dbg(&pdev->dev, "%s: Register codec.\n", __func__);
|
||||
status = snd_soc_register_codec(&pdev->dev, &ab8500_codec_driver,
|
||||
ab8500_codec_dai,
|
||||
|
@ -69,19 +69,6 @@ static struct snd_soc_dai_driver ac97_dai = {
|
||||
.ops = &ac97_dai_ops,
|
||||
};
|
||||
|
||||
static unsigned int ac97_read(struct snd_soc_codec *codec,
|
||||
unsigned int reg)
|
||||
{
|
||||
return soc_ac97_ops->read(codec->ac97, reg);
|
||||
}
|
||||
|
||||
static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
soc_ac97_ops->write(codec->ac97, reg, val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ac97_soc_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct snd_ac97_bus *ac97_bus;
|
||||
@ -122,8 +109,6 @@ static int ac97_soc_resume(struct snd_soc_codec *codec)
|
||||
#endif
|
||||
|
||||
static struct snd_soc_codec_driver soc_codec_dev_ac97 = {
|
||||
.write = ac97_write,
|
||||
.read = ac97_read,
|
||||
.probe = ac97_soc_probe,
|
||||
.suspend = ac97_soc_suspend,
|
||||
.resume = ac97_soc_resume,
|
||||
|
@ -1448,29 +1448,10 @@ static int adau1373_set_bias_level(struct snd_soc_codec *codec,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adau1373_remove(struct snd_soc_codec *codec)
|
||||
{
|
||||
adau1373_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adau1373_suspend(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
|
||||
int ret;
|
||||
|
||||
ret = adau1373_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
regcache_cache_only(adau1373->regmap, true);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adau1373_resume(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
regcache_cache_only(adau1373->regmap, false);
|
||||
adau1373_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
regcache_sync(adau1373->regmap);
|
||||
|
||||
return 0;
|
||||
@ -1501,8 +1482,6 @@ static const struct regmap_config adau1373_regmap_config = {
|
||||
|
||||
static struct snd_soc_codec_driver adau1373_codec_driver = {
|
||||
.probe = adau1373_probe,
|
||||
.remove = adau1373_remove,
|
||||
.suspend = adau1373_suspend,
|
||||
.resume = adau1373_resume,
|
||||
.set_bias_level = adau1373_set_bias_level,
|
||||
.idle_bias_off = true,
|
||||
|
@ -714,9 +714,9 @@ static int adau1761_codec_probe(struct snd_soc_codec *codec)
|
||||
|
||||
static const struct snd_soc_codec_driver adau1761_codec_driver = {
|
||||
.probe = adau1761_codec_probe,
|
||||
.suspend = adau17x1_suspend,
|
||||
.resume = adau17x1_resume,
|
||||
.set_bias_level = adau1761_set_bias_level,
|
||||
.suspend_bias_off = true,
|
||||
|
||||
.controls = adau1761_controls,
|
||||
.num_controls = ARRAY_SIZE(adau1761_controls),
|
||||
|
@ -446,9 +446,9 @@ static int adau1781_codec_probe(struct snd_soc_codec *codec)
|
||||
|
||||
static const struct snd_soc_codec_driver adau1781_codec_driver = {
|
||||
.probe = adau1781_codec_probe,
|
||||
.suspend = adau17x1_suspend,
|
||||
.resume = adau17x1_resume,
|
||||
.set_bias_level = adau1781_set_bias_level,
|
||||
.suspend_bias_off = true,
|
||||
|
||||
.controls = adau1781_controls,
|
||||
.num_controls = ARRAY_SIZE(adau1781_controls),
|
||||
|
@ -815,13 +815,6 @@ int adau17x1_add_routes(struct snd_soc_codec *codec)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adau17x1_add_routes);
|
||||
|
||||
int adau17x1_suspend(struct snd_soc_codec *codec)
|
||||
{
|
||||
codec->driver->set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adau17x1_suspend);
|
||||
|
||||
int adau17x1_resume(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct adau *adau = snd_soc_codec_get_drvdata(codec);
|
||||
@ -829,7 +822,6 @@ int adau17x1_resume(struct snd_soc_codec *codec)
|
||||
if (adau->switch_mode)
|
||||
adau->switch_mode(codec->dev);
|
||||
|
||||
codec->driver->set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
regcache_sync(adau->regmap);
|
||||
|
||||
return 0;
|
||||
|
@ -52,7 +52,6 @@ int adau17x1_set_micbias_voltage(struct snd_soc_codec *codec,
|
||||
enum adau17x1_micbias_voltage micbias);
|
||||
bool adau17x1_readable_register(struct device *dev, unsigned int reg);
|
||||
bool adau17x1_volatile_register(struct device *dev, unsigned int reg);
|
||||
int adau17x1_suspend(struct snd_soc_codec *codec);
|
||||
int adau17x1_resume(struct snd_soc_codec *codec);
|
||||
|
||||
extern const struct snd_soc_dai_ops adau17x1_dai_ops;
|
||||
|
@ -812,42 +812,23 @@ static int adav80x_probe(struct snd_soc_codec *codec)
|
||||
/* Disable DAC zero flag */
|
||||
regmap_write(adav80x->regmap, ADAV80X_DAC_CTRL3, 0x6);
|
||||
|
||||
return adav80x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
}
|
||||
|
||||
static int adav80x_suspend(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
|
||||
int ret;
|
||||
|
||||
ret = adav80x_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
regcache_cache_only(adav80x->regmap, true);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adav80x_resume(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
regcache_cache_only(adav80x->regmap, false);
|
||||
adav80x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
regcache_sync(adav80x->regmap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adav80x_remove(struct snd_soc_codec *codec)
|
||||
{
|
||||
return adav80x_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
}
|
||||
|
||||
static struct snd_soc_codec_driver adav80x_codec_driver = {
|
||||
.probe = adav80x_probe,
|
||||
.remove = adav80x_remove,
|
||||
.suspend = adav80x_suspend,
|
||||
.resume = adav80x_resume,
|
||||
.set_bias_level = adav80x_set_bias_level,
|
||||
.suspend_bias_off = true,
|
||||
|
||||
.set_pll = adav80x_set_pll,
|
||||
.set_sysclk = adav80x_set_sysclk,
|
||||
|
631
sound/soc/codecs/cs35l32.c
Normal file
631
sound/soc/codecs/cs35l32.c
Normal file
@ -0,0 +1,631 @@
|
||||
/*
|
||||
* cs35l32.c -- CS35L32 ALSA SoC audio driver
|
||||
*
|
||||
* Copyright 2014 CirrusLogic, Inc.
|
||||
*
|
||||
* Author: Brian Austin <brian.austin@cirrus.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/tlv.h>
|
||||
#include <dt-bindings/sound/cs35l32.h>
|
||||
|
||||
#include "cs35l32.h"
|
||||
|
||||
#define CS35L32_NUM_SUPPLIES 2
|
||||
static const char *const cs35l32_supply_names[CS35L32_NUM_SUPPLIES] = {
|
||||
"VA",
|
||||
"VP",
|
||||
};
|
||||
|
||||
struct cs35l32_private {
|
||||
struct regmap *regmap;
|
||||
struct snd_soc_codec *codec;
|
||||
struct regulator_bulk_data supplies[CS35L32_NUM_SUPPLIES];
|
||||
struct cs35l32_platform_data pdata;
|
||||
struct gpio_desc *reset_gpio;
|
||||
};
|
||||
|
||||
static const struct reg_default cs35l32_reg_defaults[] = {
|
||||
|
||||
{ 0x06, 0x04 }, /* Power Ctl 1 */
|
||||
{ 0x07, 0xE8 }, /* Power Ctl 2 */
|
||||
{ 0x08, 0x40 }, /* Clock Ctl */
|
||||
{ 0x09, 0x20 }, /* Low Battery Threshold */
|
||||
{ 0x0A, 0x00 }, /* Voltage Monitor [RO] */
|
||||
{ 0x0B, 0x40 }, /* Conv Peak Curr Protection CTL */
|
||||
{ 0x0C, 0x07 }, /* IMON Scaling */
|
||||
{ 0x0D, 0x03 }, /* Audio/LED Pwr Manager */
|
||||
{ 0x0F, 0x20 }, /* Serial Port Control */
|
||||
{ 0x10, 0x14 }, /* Class D Amp CTL */
|
||||
{ 0x11, 0x00 }, /* Protection Release CTL */
|
||||
{ 0x12, 0xFF }, /* Interrupt Mask 1 */
|
||||
{ 0x13, 0xFF }, /* Interrupt Mask 2 */
|
||||
{ 0x14, 0xFF }, /* Interrupt Mask 3 */
|
||||
{ 0x19, 0x00 }, /* LED Flash Mode Current */
|
||||
{ 0x1A, 0x00 }, /* LED Movie Mode Current */
|
||||
{ 0x1B, 0x20 }, /* LED Flash Timer */
|
||||
{ 0x1C, 0x00 }, /* LED Flash Inhibit Current */
|
||||
};
|
||||
|
||||
static bool cs35l32_readable_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case CS35L32_DEVID_AB:
|
||||
case CS35L32_DEVID_CD:
|
||||
case CS35L32_DEVID_E:
|
||||
case CS35L32_FAB_ID:
|
||||
case CS35L32_REV_ID:
|
||||
case CS35L32_PWRCTL1:
|
||||
case CS35L32_PWRCTL2:
|
||||
case CS35L32_CLK_CTL:
|
||||
case CS35L32_BATT_THRESHOLD:
|
||||
case CS35L32_VMON:
|
||||
case CS35L32_BST_CPCP_CTL:
|
||||
case CS35L32_IMON_SCALING:
|
||||
case CS35L32_AUDIO_LED_MNGR:
|
||||
case CS35L32_ADSP_CTL:
|
||||
case CS35L32_CLASSD_CTL:
|
||||
case CS35L32_PROTECT_CTL:
|
||||
case CS35L32_INT_MASK_1:
|
||||
case CS35L32_INT_MASK_2:
|
||||
case CS35L32_INT_MASK_3:
|
||||
case CS35L32_INT_STATUS_1:
|
||||
case CS35L32_INT_STATUS_2:
|
||||
case CS35L32_INT_STATUS_3:
|
||||
case CS35L32_LED_STATUS:
|
||||
case CS35L32_FLASH_MODE:
|
||||
case CS35L32_MOVIE_MODE:
|
||||
case CS35L32_FLASH_TIMER:
|
||||
case CS35L32_FLASH_INHIBIT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool cs35l32_volatile_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case CS35L32_DEVID_AB:
|
||||
case CS35L32_DEVID_CD:
|
||||
case CS35L32_DEVID_E:
|
||||
case CS35L32_FAB_ID:
|
||||
case CS35L32_REV_ID:
|
||||
case CS35L32_INT_STATUS_1:
|
||||
case CS35L32_INT_STATUS_2:
|
||||
case CS35L32_INT_STATUS_3:
|
||||
case CS35L32_LED_STATUS:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool cs35l32_precious_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case CS35L32_INT_STATUS_1:
|
||||
case CS35L32_INT_STATUS_2:
|
||||
case CS35L32_INT_STATUS_3:
|
||||
case CS35L32_LED_STATUS:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static DECLARE_TLV_DB_SCALE(classd_ctl_tlv, 900, 300, 0);
|
||||
|
||||
static const struct snd_kcontrol_new imon_ctl =
|
||||
SOC_DAPM_SINGLE("Switch", CS35L32_PWRCTL2, 6, 1, 1);
|
||||
|
||||
static const struct snd_kcontrol_new vmon_ctl =
|
||||
SOC_DAPM_SINGLE("Switch", CS35L32_PWRCTL2, 7, 1, 1);
|
||||
|
||||
static const struct snd_kcontrol_new vpmon_ctl =
|
||||
SOC_DAPM_SINGLE("Switch", CS35L32_PWRCTL2, 5, 1, 1);
|
||||
|
||||
static const struct snd_kcontrol_new cs35l32_snd_controls[] = {
|
||||
SOC_SINGLE_TLV("Speaker Volume", CS35L32_CLASSD_CTL,
|
||||
3, 0x04, 1, classd_ctl_tlv),
|
||||
SOC_SINGLE("Zero Cross Switch", CS35L32_CLASSD_CTL, 2, 1, 0),
|
||||
SOC_SINGLE("Gain Manager Switch", CS35L32_AUDIO_LED_MNGR, 3, 1, 0),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget cs35l32_dapm_widgets[] = {
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("BOOST", CS35L32_PWRCTL1, 2, 1, NULL, 0),
|
||||
SND_SOC_DAPM_OUT_DRV("Speaker", CS35L32_PWRCTL1, 7, 1, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_AIF_OUT("SDOUT", NULL, 0, CS35L32_PWRCTL2, 3, 1),
|
||||
|
||||
SND_SOC_DAPM_INPUT("VP"),
|
||||
SND_SOC_DAPM_INPUT("ISENSE"),
|
||||
SND_SOC_DAPM_INPUT("VSENSE"),
|
||||
|
||||
SND_SOC_DAPM_SWITCH("VMON ADC", CS35L32_PWRCTL2, 7, 1, &vmon_ctl),
|
||||
SND_SOC_DAPM_SWITCH("IMON ADC", CS35L32_PWRCTL2, 6, 1, &imon_ctl),
|
||||
SND_SOC_DAPM_SWITCH("VPMON ADC", CS35L32_PWRCTL2, 5, 1, &vpmon_ctl),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route cs35l32_audio_map[] = {
|
||||
|
||||
{"Speaker", NULL, "BOOST"},
|
||||
|
||||
{"VMON ADC", NULL, "VSENSE"},
|
||||
{"IMON ADC", NULL, "ISENSE"},
|
||||
{"VPMON ADC", NULL, "VP"},
|
||||
|
||||
{"SDOUT", "Switch", "VMON ADC"},
|
||||
{"SDOUT", "Switch", "IMON ADC"},
|
||||
{"SDOUT", "Switch", "VPMON ADC"},
|
||||
|
||||
{"Capture", NULL, "SDOUT"},
|
||||
};
|
||||
|
||||
static int cs35l32_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
|
||||
{
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
snd_soc_update_bits(codec, CS35L32_ADSP_CTL,
|
||||
CS35L32_ADSP_MASTER_MASK,
|
||||
CS35L32_ADSP_MASTER_MASK);
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
snd_soc_update_bits(codec, CS35L32_ADSP_CTL,
|
||||
CS35L32_ADSP_MASTER_MASK, 0);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cs35l32_set_tristate(struct snd_soc_dai *dai, int tristate)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
|
||||
return snd_soc_update_bits(codec, CS35L32_PWRCTL2,
|
||||
CS35L32_SDOUT_3ST, tristate << 3);
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops cs35l32_ops = {
|
||||
.set_fmt = cs35l32_set_dai_fmt,
|
||||
.set_tristate = cs35l32_set_tristate,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver cs35l32_dai[] = {
|
||||
{
|
||||
.name = "cs35l32-monitor",
|
||||
.id = 0,
|
||||
.capture = {
|
||||
.stream_name = "Capture",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = CS35L32_RATES,
|
||||
.formats = CS35L32_FORMATS,
|
||||
},
|
||||
.ops = &cs35l32_ops,
|
||||
.symmetric_rates = 1,
|
||||
}
|
||||
};
|
||||
|
||||
static int cs35l32_codec_set_sysclk(struct snd_soc_codec *codec,
|
||||
int clk_id, int source, unsigned int freq, int dir)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
switch (freq) {
|
||||
case 6000000:
|
||||
val = CS35L32_MCLK_RATIO;
|
||||
break;
|
||||
case 12000000:
|
||||
val = CS35L32_MCLK_DIV2_MASK | CS35L32_MCLK_RATIO;
|
||||
break;
|
||||
case 6144000:
|
||||
val = 0;
|
||||
break;
|
||||
case 12288000:
|
||||
val = CS35L32_MCLK_DIV2_MASK;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return snd_soc_update_bits(codec, CS35L32_CLK_CTL,
|
||||
CS35L32_MCLK_DIV2_MASK | CS35L32_MCLK_RATIO_MASK, val);
|
||||
}
|
||||
|
||||
static struct snd_soc_codec_driver soc_codec_dev_cs35l32 = {
|
||||
.set_sysclk = cs35l32_codec_set_sysclk,
|
||||
|
||||
.dapm_widgets = cs35l32_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(cs35l32_dapm_widgets),
|
||||
.dapm_routes = cs35l32_audio_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(cs35l32_audio_map),
|
||||
|
||||
.controls = cs35l32_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(cs35l32_snd_controls),
|
||||
};
|
||||
|
||||
/* Current and threshold powerup sequence Pg37 in datasheet */
|
||||
static const struct reg_default cs35l32_monitor_patch[] = {
|
||||
|
||||
{ 0x00, 0x99 },
|
||||
{ 0x48, 0x17 },
|
||||
{ 0x49, 0x56 },
|
||||
{ 0x43, 0x01 },
|
||||
{ 0x3B, 0x62 },
|
||||
{ 0x3C, 0x80 },
|
||||
{ 0x00, 0x00 },
|
||||
};
|
||||
|
||||
static struct regmap_config cs35l32_regmap = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
|
||||
.max_register = CS35L32_MAX_REGISTER,
|
||||
.reg_defaults = cs35l32_reg_defaults,
|
||||
.num_reg_defaults = ARRAY_SIZE(cs35l32_reg_defaults),
|
||||
.volatile_reg = cs35l32_volatile_register,
|
||||
.readable_reg = cs35l32_readable_register,
|
||||
.precious_reg = cs35l32_precious_register,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
static int cs35l32_handle_of_data(struct i2c_client *i2c_client,
|
||||
struct cs35l32_platform_data *pdata)
|
||||
{
|
||||
struct device_node *np = i2c_client->dev.of_node;
|
||||
unsigned int val;
|
||||
|
||||
if (of_property_read_u32(np, "cirrus,sdout-share", &val) >= 0)
|
||||
pdata->sdout_share = val;
|
||||
|
||||
of_property_read_u32(np, "cirrus,boost-manager", &val);
|
||||
switch (val) {
|
||||
case CS35L32_BOOST_MGR_AUTO:
|
||||
case CS35L32_BOOST_MGR_AUTO_AUDIO:
|
||||
case CS35L32_BOOST_MGR_BYPASS:
|
||||
case CS35L32_BOOST_MGR_FIXED:
|
||||
pdata->boost_mng = val;
|
||||
break;
|
||||
default:
|
||||
dev_err(&i2c_client->dev,
|
||||
"Wrong cirrus,boost-manager DT value %d\n", val);
|
||||
pdata->boost_mng = CS35L32_BOOST_MGR_BYPASS;
|
||||
}
|
||||
|
||||
of_property_read_u32(np, "cirrus,sdout-datacfg", &val);
|
||||
switch (val) {
|
||||
case CS35L32_DATA_CFG_LR_VP:
|
||||
case CS35L32_DATA_CFG_LR_STAT:
|
||||
case CS35L32_DATA_CFG_LR:
|
||||
case CS35L32_DATA_CFG_LR_VPSTAT:
|
||||
pdata->sdout_datacfg = val;
|
||||
break;
|
||||
default:
|
||||
dev_err(&i2c_client->dev,
|
||||
"Wrong cirrus,sdout-datacfg DT value %d\n", val);
|
||||
pdata->sdout_datacfg = CS35L32_DATA_CFG_LR;
|
||||
}
|
||||
|
||||
of_property_read_u32(np, "cirrus,battery-threshold", &val);
|
||||
switch (val) {
|
||||
case CS35L32_BATT_THRESH_3_1V:
|
||||
case CS35L32_BATT_THRESH_3_2V:
|
||||
case CS35L32_BATT_THRESH_3_3V:
|
||||
case CS35L32_BATT_THRESH_3_4V:
|
||||
pdata->batt_thresh = val;
|
||||
break;
|
||||
default:
|
||||
dev_err(&i2c_client->dev,
|
||||
"Wrong cirrus,battery-threshold DT value %d\n", val);
|
||||
pdata->batt_thresh = CS35L32_BATT_THRESH_3_3V;
|
||||
}
|
||||
|
||||
of_property_read_u32(np, "cirrus,battery-recovery", &val);
|
||||
switch (val) {
|
||||
case CS35L32_BATT_RECOV_3_1V:
|
||||
case CS35L32_BATT_RECOV_3_2V:
|
||||
case CS35L32_BATT_RECOV_3_3V:
|
||||
case CS35L32_BATT_RECOV_3_4V:
|
||||
case CS35L32_BATT_RECOV_3_5V:
|
||||
case CS35L32_BATT_RECOV_3_6V:
|
||||
pdata->batt_recov = val;
|
||||
break;
|
||||
default:
|
||||
dev_err(&i2c_client->dev,
|
||||
"Wrong cirrus,battery-recovery DT value %d\n", val);
|
||||
pdata->batt_recov = CS35L32_BATT_RECOV_3_4V;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cs35l32_i2c_probe(struct i2c_client *i2c_client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct cs35l32_private *cs35l32;
|
||||
struct cs35l32_platform_data *pdata =
|
||||
dev_get_platdata(&i2c_client->dev);
|
||||
int ret, i;
|
||||
unsigned int devid = 0;
|
||||
unsigned int reg;
|
||||
|
||||
|
||||
cs35l32 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs35l32_private),
|
||||
GFP_KERNEL);
|
||||
if (!cs35l32) {
|
||||
dev_err(&i2c_client->dev, "could not allocate codec\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(i2c_client, cs35l32);
|
||||
|
||||
cs35l32->regmap = devm_regmap_init_i2c(i2c_client, &cs35l32_regmap);
|
||||
if (IS_ERR(cs35l32->regmap)) {
|
||||
ret = PTR_ERR(cs35l32->regmap);
|
||||
dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (pdata) {
|
||||
cs35l32->pdata = *pdata;
|
||||
} else {
|
||||
pdata = devm_kzalloc(&i2c_client->dev,
|
||||
sizeof(struct cs35l32_platform_data),
|
||||
GFP_KERNEL);
|
||||
if (!pdata) {
|
||||
dev_err(&i2c_client->dev, "could not allocate pdata\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (i2c_client->dev.of_node) {
|
||||
ret = cs35l32_handle_of_data(i2c_client,
|
||||
&cs35l32->pdata);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(cs35l32->supplies); i++)
|
||||
cs35l32->supplies[i].supply = cs35l32_supply_names[i];
|
||||
|
||||
ret = devm_regulator_bulk_get(&i2c_client->dev,
|
||||
ARRAY_SIZE(cs35l32->supplies),
|
||||
cs35l32->supplies);
|
||||
if (ret != 0) {
|
||||
dev_err(&i2c_client->dev,
|
||||
"Failed to request supplies: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(cs35l32->supplies),
|
||||
cs35l32->supplies);
|
||||
if (ret != 0) {
|
||||
dev_err(&i2c_client->dev,
|
||||
"Failed to enable supplies: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Reset the Device */
|
||||
cs35l32->reset_gpio = devm_gpiod_get(&i2c_client->dev,
|
||||
"reset-gpios");
|
||||
if (IS_ERR(cs35l32->reset_gpio)) {
|
||||
ret = PTR_ERR(cs35l32->reset_gpio);
|
||||
if (ret != -ENOENT && ret != -ENOSYS)
|
||||
return ret;
|
||||
|
||||
cs35l32->reset_gpio = NULL;
|
||||
} else {
|
||||
ret = gpiod_direction_output(cs35l32->reset_gpio, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
gpiod_set_value_cansleep(cs35l32->reset_gpio, 1);
|
||||
}
|
||||
|
||||
/* initialize codec */
|
||||
ret = regmap_read(cs35l32->regmap, CS35L32_DEVID_AB, ®);
|
||||
devid = (reg & 0xFF) << 12;
|
||||
|
||||
ret = regmap_read(cs35l32->regmap, CS35L32_DEVID_CD, ®);
|
||||
devid |= (reg & 0xFF) << 4;
|
||||
|
||||
ret = regmap_read(cs35l32->regmap, CS35L32_DEVID_E, ®);
|
||||
devid |= (reg & 0xF0) >> 4;
|
||||
|
||||
if (devid != CS35L32_CHIP_ID) {
|
||||
ret = -ENODEV;
|
||||
dev_err(&i2c_client->dev,
|
||||
"CS35L32 Device ID (%X). Expected %X\n",
|
||||
devid, CS35L32_CHIP_ID);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_read(cs35l32->regmap, CS35L32_REV_ID, ®);
|
||||
if (ret < 0) {
|
||||
dev_err(&i2c_client->dev, "Get Revision ID failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_register_patch(cs35l32->regmap, cs35l32_monitor_patch,
|
||||
ARRAY_SIZE(cs35l32_monitor_patch));
|
||||
if (ret < 0) {
|
||||
dev_err(&i2c_client->dev, "Failed to apply errata patch\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_info(&i2c_client->dev,
|
||||
"Cirrus Logic CS35L32, Revision: %02X\n", reg & 0xFF);
|
||||
|
||||
/* Setup VBOOST Management */
|
||||
if (cs35l32->pdata.boost_mng)
|
||||
regmap_update_bits(cs35l32->regmap, CS35L32_AUDIO_LED_MNGR,
|
||||
CS35L32_BOOST_MASK,
|
||||
cs35l32->pdata.boost_mng);
|
||||
|
||||
/* Setup ADSP Format Config */
|
||||
if (cs35l32->pdata.sdout_share)
|
||||
regmap_update_bits(cs35l32->regmap, CS35L32_ADSP_CTL,
|
||||
CS35L32_ADSP_SHARE_MASK,
|
||||
cs35l32->pdata.sdout_share << 3);
|
||||
|
||||
/* Setup ADSP Data Configuration */
|
||||
if (cs35l32->pdata.sdout_datacfg)
|
||||
regmap_update_bits(cs35l32->regmap, CS35L32_ADSP_CTL,
|
||||
CS35L32_ADSP_DATACFG_MASK,
|
||||
cs35l32->pdata.sdout_datacfg << 4);
|
||||
|
||||
/* Setup Low Battery Recovery */
|
||||
if (cs35l32->pdata.batt_recov)
|
||||
regmap_update_bits(cs35l32->regmap, CS35L32_BATT_THRESHOLD,
|
||||
CS35L32_BATT_REC_MASK,
|
||||
cs35l32->pdata.batt_recov << 1);
|
||||
|
||||
/* Setup Low Battery Threshold */
|
||||
if (cs35l32->pdata.batt_thresh)
|
||||
regmap_update_bits(cs35l32->regmap, CS35L32_BATT_THRESHOLD,
|
||||
CS35L32_BATT_THRESH_MASK,
|
||||
cs35l32->pdata.batt_thresh << 4);
|
||||
|
||||
/* Power down the AMP */
|
||||
regmap_update_bits(cs35l32->regmap, CS35L32_PWRCTL1, CS35L32_PDN_AMP,
|
||||
CS35L32_PDN_AMP);
|
||||
|
||||
/* Clear MCLK Error Bit since we don't have the clock yet */
|
||||
ret = regmap_read(cs35l32->regmap, CS35L32_INT_STATUS_1, ®);
|
||||
|
||||
ret = snd_soc_register_codec(&i2c_client->dev,
|
||||
&soc_codec_dev_cs35l32, cs35l32_dai,
|
||||
ARRAY_SIZE(cs35l32_dai));
|
||||
if (ret < 0)
|
||||
goto err_disable;
|
||||
|
||||
return 0;
|
||||
|
||||
err_disable:
|
||||
regulator_bulk_disable(ARRAY_SIZE(cs35l32->supplies),
|
||||
cs35l32->supplies);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cs35l32_i2c_remove(struct i2c_client *i2c_client)
|
||||
{
|
||||
struct cs35l32_private *cs35l32 = i2c_get_clientdata(i2c_client);
|
||||
|
||||
snd_soc_unregister_codec(&i2c_client->dev);
|
||||
|
||||
/* Hold down reset */
|
||||
if (cs35l32->reset_gpio)
|
||||
gpiod_set_value_cansleep(cs35l32->reset_gpio, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
static int cs35l32_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct cs35l32_private *cs35l32 = dev_get_drvdata(dev);
|
||||
|
||||
regcache_cache_only(cs35l32->regmap, true);
|
||||
regcache_mark_dirty(cs35l32->regmap);
|
||||
|
||||
/* Hold down reset */
|
||||
if (cs35l32->reset_gpio)
|
||||
gpiod_set_value_cansleep(cs35l32->reset_gpio, 0);
|
||||
|
||||
/* remove power */
|
||||
regulator_bulk_disable(ARRAY_SIZE(cs35l32->supplies),
|
||||
cs35l32->supplies);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cs35l32_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct cs35l32_private *cs35l32 = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
/* Enable power */
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(cs35l32->supplies),
|
||||
cs35l32->supplies);
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "Failed to enable supplies: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (cs35l32->reset_gpio)
|
||||
gpiod_set_value_cansleep(cs35l32->reset_gpio, 1);
|
||||
|
||||
regcache_cache_only(cs35l32->regmap, false);
|
||||
regcache_sync(cs35l32->regmap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops cs35l32_runtime_pm = {
|
||||
SET_RUNTIME_PM_OPS(cs35l32_runtime_suspend, cs35l32_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
|
||||
static const struct of_device_id cs35l32_of_match[] = {
|
||||
{ .compatible = "cirrus,cs35l32", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, cs35l32_of_match);
|
||||
|
||||
|
||||
static const struct i2c_device_id cs35l32_id[] = {
|
||||
{"cs35l32", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, cs35l32_id);
|
||||
|
||||
static struct i2c_driver cs35l32_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "cs35l32",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &cs35l32_runtime_pm,
|
||||
.of_match_table = cs35l32_of_match,
|
||||
},
|
||||
.id_table = cs35l32_id,
|
||||
.probe = cs35l32_i2c_probe,
|
||||
.remove = cs35l32_i2c_remove,
|
||||
};
|
||||
|
||||
module_i2c_driver(cs35l32_i2c_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC CS35L32 driver");
|
||||
MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>");
|
||||
MODULE_LICENSE("GPL");
|
93
sound/soc/codecs/cs35l32.h
Normal file
93
sound/soc/codecs/cs35l32.h
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* cs35l32.h -- CS35L32 ALSA SoC audio driver
|
||||
*
|
||||
* Copyright 2014 CirrusLogic, Inc.
|
||||
*
|
||||
* Author: Brian Austin <brian.austin@cirrus.com>
|
||||
*
|
||||
* 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 __CS35L32_H__
|
||||
#define __CS35L32_H__
|
||||
|
||||
struct cs35l32_platform_data {
|
||||
/* Low Battery Threshold */
|
||||
unsigned int batt_thresh;
|
||||
/* Low Battery Recovery */
|
||||
unsigned int batt_recov;
|
||||
/* LED Current Management*/
|
||||
unsigned int led_mng;
|
||||
/* Audio Gain w/ LED */
|
||||
unsigned int audiogain_mng;
|
||||
/* Boost Management */
|
||||
unsigned int boost_mng;
|
||||
/* Data CFG for DUAL device */
|
||||
unsigned int sdout_datacfg;
|
||||
/* SDOUT Sharing */
|
||||
unsigned int sdout_share;
|
||||
};
|
||||
|
||||
#define CS35L32_CHIP_ID 0x00035A32
|
||||
#define CS35L32_DEVID_AB 0x01 /* Device ID A & B [RO] */
|
||||
#define CS35L32_DEVID_CD 0x02 /* Device ID C & D [RO] */
|
||||
#define CS35L32_DEVID_E 0x03 /* Device ID E [RO] */
|
||||
#define CS35L32_FAB_ID 0x04 /* Fab ID [RO] */
|
||||
#define CS35L32_REV_ID 0x05 /* Revision ID [RO] */
|
||||
#define CS35L32_PWRCTL1 0x06 /* Power Ctl 1 */
|
||||
#define CS35L32_PWRCTL2 0x07 /* Power Ctl 2 */
|
||||
#define CS35L32_CLK_CTL 0x08 /* Clock Ctl */
|
||||
#define CS35L32_BATT_THRESHOLD 0x09 /* Low Battery Threshold */
|
||||
#define CS35L32_VMON 0x0A /* Voltage Monitor [RO] */
|
||||
#define CS35L32_BST_CPCP_CTL 0x0B /* Conv Peak Curr Protection CTL */
|
||||
#define CS35L32_IMON_SCALING 0x0C /* IMON Scaling */
|
||||
#define CS35L32_AUDIO_LED_MNGR 0x0D /* Audio/LED Pwr Manager */
|
||||
#define CS35L32_ADSP_CTL 0x0F /* Serial Port Control */
|
||||
#define CS35L32_CLASSD_CTL 0x10 /* Class D Amp CTL */
|
||||
#define CS35L32_PROTECT_CTL 0x11 /* Protection Release CTL */
|
||||
#define CS35L32_INT_MASK_1 0x12 /* Interrupt Mask 1 */
|
||||
#define CS35L32_INT_MASK_2 0x13 /* Interrupt Mask 2 */
|
||||
#define CS35L32_INT_MASK_3 0x14 /* Interrupt Mask 3 */
|
||||
#define CS35L32_INT_STATUS_1 0x15 /* Interrupt Status 1 [RO] */
|
||||
#define CS35L32_INT_STATUS_2 0x16 /* Interrupt Status 2 [RO] */
|
||||
#define CS35L32_INT_STATUS_3 0x17 /* Interrupt Status 3 [RO] */
|
||||
#define CS35L32_LED_STATUS 0x18 /* LED Lighting Status [RO] */
|
||||
#define CS35L32_FLASH_MODE 0x19 /* LED Flash Mode Current */
|
||||
#define CS35L32_MOVIE_MODE 0x1A /* LED Movie Mode Current */
|
||||
#define CS35L32_FLASH_TIMER 0x1B /* LED Flash Timer */
|
||||
#define CS35L32_FLASH_INHIBIT 0x1C /* LED Flash Inhibit Current */
|
||||
#define CS35L32_MAX_REGISTER 0x1C
|
||||
|
||||
#define CS35L32_MCLK_DIV2 0x01
|
||||
#define CS35L32_MCLK_RATIO 0x01
|
||||
#define CS35L32_MCLKDIS 0x80
|
||||
#define CS35L32_PDN_ALL 0x01
|
||||
#define CS35L32_PDN_AMP 0x80
|
||||
#define CS35L32_PDN_BOOST 0x04
|
||||
#define CS35L32_PDN_IMON 0x40
|
||||
#define CS35L32_PDN_VMON 0x80
|
||||
#define CS35L32_PDN_VPMON 0x20
|
||||
#define CS35L32_PDN_ADSP 0x08
|
||||
|
||||
#define CS35L32_MCLK_DIV2_MASK 0x40
|
||||
#define CS35L32_MCLK_RATIO_MASK 0x01
|
||||
#define CS35L32_MCLK_MASK 0x41
|
||||
#define CS35L32_ADSP_MASTER_MASK 0x40
|
||||
#define CS35L32_BOOST_MASK 0x03
|
||||
#define CS35L32_GAIN_MGR_MASK 0x08
|
||||
#define CS35L32_ADSP_SHARE_MASK 0x08
|
||||
#define CS35L32_ADSP_DATACFG_MASK 0x30
|
||||
#define CS35L32_SDOUT_3ST 0x80
|
||||
#define CS35L32_BATT_REC_MASK 0x0E
|
||||
#define CS35L32_BATT_THRESH_MASK 0x30
|
||||
|
||||
#define CS35L32_RATES (SNDRV_PCM_RATE_48000)
|
||||
#define CS35L32_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
|
||||
SNDRV_PCM_FMTBIT_S24_LE | \
|
||||
SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
|
||||
#endif
|
@ -77,6 +77,7 @@ static bool cs4265_readable_register(struct device *dev, unsigned int reg)
|
||||
case CS4265_INT_MASK:
|
||||
case CS4265_STATUS_MODE_MSB:
|
||||
case CS4265_STATUS_MODE_LSB:
|
||||
case CS4265_CHIP_ID:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
@ -176,9 +176,9 @@ static bool cs42l52_volatile_register(struct device *dev, unsigned int reg)
|
||||
case CS42L52_BATT_LEVEL:
|
||||
case CS42L52_SPK_STATUS:
|
||||
case CS42L52_CHARGE_PUMP:
|
||||
return 1;
|
||||
return true;
|
||||
default:
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -946,20 +946,6 @@ static struct snd_soc_dai_driver cs42l52_dai = {
|
||||
.ops = &cs42l52_ops,
|
||||
};
|
||||
|
||||
static int cs42l52_suspend(struct snd_soc_codec *codec)
|
||||
{
|
||||
cs42l52_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cs42l52_resume(struct snd_soc_codec *codec)
|
||||
{
|
||||
cs42l52_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int beep_rates[] = {
|
||||
261, 522, 585, 667, 706, 774, 889, 1000,
|
||||
1043, 1200, 1333, 1412, 1600, 1714, 2000, 2182
|
||||
@ -1104,8 +1090,6 @@ static int cs42l52_probe(struct snd_soc_codec *codec)
|
||||
|
||||
cs42l52_init_beep(codec);
|
||||
|
||||
cs42l52_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
|
||||
cs42l52->sysclk = CS42L52_DEFAULT_CLK;
|
||||
cs42l52->config.format = CS42L52_DEFAULT_FORMAT;
|
||||
|
||||
@ -1115,7 +1099,6 @@ static int cs42l52_probe(struct snd_soc_codec *codec)
|
||||
static int cs42l52_remove(struct snd_soc_codec *codec)
|
||||
{
|
||||
cs42l52_free_beep(codec);
|
||||
cs42l52_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1123,9 +1106,8 @@ static int cs42l52_remove(struct snd_soc_codec *codec)
|
||||
static struct snd_soc_codec_driver soc_codec_dev_cs42l52 = {
|
||||
.probe = cs42l52_probe,
|
||||
.remove = cs42l52_remove,
|
||||
.suspend = cs42l52_suspend,
|
||||
.resume = cs42l52_resume,
|
||||
.set_bias_level = cs42l52_set_bias_level,
|
||||
.suspend_bias_off = true,
|
||||
|
||||
.dapm_widgets = cs42l52_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(cs42l52_dapm_widgets),
|
||||
|
@ -171,9 +171,9 @@ static bool cs42l56_volatile_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case CS42L56_INT_STATUS:
|
||||
return 1;
|
||||
return true;
|
||||
default:
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1016,20 +1016,6 @@ static struct snd_soc_dai_driver cs42l56_dai = {
|
||||
.ops = &cs42l56_ops,
|
||||
};
|
||||
|
||||
static int cs42l56_suspend(struct snd_soc_codec *codec)
|
||||
{
|
||||
cs42l56_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cs42l56_resume(struct snd_soc_codec *codec)
|
||||
{
|
||||
cs42l56_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int beep_freq[] = {
|
||||
261, 522, 585, 667, 706, 774, 889, 1000,
|
||||
1043, 1200, 1333, 1412, 1600, 1714, 2000, 2182
|
||||
@ -1168,18 +1154,12 @@ static int cs42l56_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
cs42l56_init_beep(codec);
|
||||
|
||||
cs42l56_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cs42l56_remove(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
cs42l56_free_beep(codec);
|
||||
cs42l56_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
regulator_bulk_free(ARRAY_SIZE(cs42l56->supplies), cs42l56->supplies);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1187,9 +1167,8 @@ static int cs42l56_remove(struct snd_soc_codec *codec)
|
||||
static struct snd_soc_codec_driver soc_codec_dev_cs42l56 = {
|
||||
.probe = cs42l56_probe,
|
||||
.remove = cs42l56_remove,
|
||||
.suspend = cs42l56_suspend,
|
||||
.resume = cs42l56_resume,
|
||||
.set_bias_level = cs42l56_set_bias_level,
|
||||
.suspend_bias_off = true,
|
||||
|
||||
.dapm_widgets = cs42l56_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(cs42l56_dapm_widgets),
|
||||
|
@ -1330,25 +1330,10 @@ static struct snd_soc_dai_driver cs42l73_dai[] = {
|
||||
}
|
||||
};
|
||||
|
||||
static int cs42l73_suspend(struct snd_soc_codec *codec)
|
||||
{
|
||||
cs42l73_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cs42l73_resume(struct snd_soc_codec *codec)
|
||||
{
|
||||
cs42l73_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cs42l73_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct cs42l73_private *cs42l73 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
cs42l73_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
|
||||
/* Set Charge Pump Frequency */
|
||||
if (cs42l73->pdata.chgfreq)
|
||||
snd_soc_update_bits(codec, CS42L73_CPFCHC,
|
||||
@ -1362,18 +1347,10 @@ static int cs42l73_probe(struct snd_soc_codec *codec)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cs42l73_remove(struct snd_soc_codec *codec)
|
||||
{
|
||||
cs42l73_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_codec_driver soc_codec_dev_cs42l73 = {
|
||||
.probe = cs42l73_probe,
|
||||
.remove = cs42l73_remove,
|
||||
.suspend = cs42l73_suspend,
|
||||
.resume = cs42l73_resume,
|
||||
.set_bias_level = cs42l73_set_bias_level,
|
||||
.suspend_bias_off = true,
|
||||
|
||||
.dapm_widgets = cs42l73_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(cs42l73_dapm_widgets),
|
||||
|
@ -35,7 +35,6 @@
|
||||
|
||||
struct da732x_priv {
|
||||
struct regmap *regmap;
|
||||
struct snd_soc_codec *codec;
|
||||
|
||||
unsigned int sysclk;
|
||||
bool pll_en;
|
||||
@ -217,7 +216,7 @@ static void da732x_set_charge_pump(struct snd_soc_codec *codec, int state)
|
||||
snd_soc_write(codec, DA732X_REG_CP_CTRL1, DA723X_CP_DIS);
|
||||
break;
|
||||
default:
|
||||
pr_err(KERN_ERR "Wrong charge pump state\n");
|
||||
pr_err("Wrong charge pump state\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1508,31 +1507,7 @@ static int da732x_set_bias_level(struct snd_soc_codec *codec,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int da732x_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct da732x_priv *da732x = snd_soc_codec_get_drvdata(codec);
|
||||
struct snd_soc_dapm_context *dapm = &codec->dapm;
|
||||
|
||||
da732x->codec = codec;
|
||||
|
||||
dapm->idle_bias_off = false;
|
||||
|
||||
da732x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int da732x_remove(struct snd_soc_codec *codec)
|
||||
{
|
||||
|
||||
da732x_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_codec_driver soc_codec_dev_da732x = {
|
||||
.probe = da732x_probe,
|
||||
.remove = da732x_remove,
|
||||
.set_bias_level = da732x_set_bias_level,
|
||||
.controls = da732x_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(da732x_snd_controls),
|
||||
|
60
sound/soc/codecs/es8328-i2c.c
Normal file
60
sound/soc/codecs/es8328-i2c.c
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* es8328-i2c.c -- ES8328 ALSA SoC I2C Audio driver
|
||||
*
|
||||
* Copyright 2014 Sutajio Ko-Usagi PTE LTD
|
||||
*
|
||||
* Author: Sean Cross <xobs@kosagi.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include "es8328.h"
|
||||
|
||||
static const struct i2c_device_id es8328_id[] = {
|
||||
{ "everest,es8328", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, es8328_id);
|
||||
|
||||
static const struct of_device_id es8328_of_match[] = {
|
||||
{ .compatible = "everest,es8328", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, es8328_of_match);
|
||||
|
||||
static int es8328_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
return es8328_probe(&i2c->dev,
|
||||
devm_regmap_init_i2c(i2c, &es8328_regmap_config));
|
||||
}
|
||||
|
||||
static int es8328_i2c_remove(struct i2c_client *i2c)
|
||||
{
|
||||
snd_soc_unregister_codec(&i2c->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_driver es8328_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "es8328",
|
||||
.of_match_table = es8328_of_match,
|
||||
},
|
||||
.probe = es8328_i2c_probe,
|
||||
.remove = es8328_i2c_remove,
|
||||
.id_table = es8328_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(es8328_i2c_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC ES8328 audio CODEC I2C driver");
|
||||
MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
|
||||
MODULE_LICENSE("GPL");
|
49
sound/soc/codecs/es8328-spi.c
Normal file
49
sound/soc/codecs/es8328-spi.c
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* es8328.c -- ES8328 ALSA SoC SPI Audio driver
|
||||
*
|
||||
* Copyright 2014 Sutajio Ko-Usagi PTE LTD
|
||||
*
|
||||
* Author: Sean Cross <xobs@kosagi.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <sound/soc.h>
|
||||
#include "es8328.h"
|
||||
|
||||
static const struct of_device_id es8328_of_match[] = {
|
||||
{ .compatible = "everest,es8328", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, es8328_of_match);
|
||||
|
||||
static int es8328_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
return es8328_probe(&spi->dev,
|
||||
devm_regmap_init_spi(spi, &es8328_regmap_config));
|
||||
}
|
||||
|
||||
static int es8328_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
snd_soc_unregister_codec(&spi->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct spi_driver es8328_spi_driver = {
|
||||
.driver = {
|
||||
.name = "es8328",
|
||||
.of_match_table = es8328_of_match,
|
||||
},
|
||||
.probe = es8328_spi_probe,
|
||||
.remove = es8328_spi_remove,
|
||||
};
|
||||
|
||||
module_spi_driver(es8328_spi_driver);
|
||||
MODULE_DESCRIPTION("ASoC ES8328 audio CODEC SPI driver");
|
||||
MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
|
||||
MODULE_LICENSE("GPL");
|
756
sound/soc/codecs/es8328.c
Normal file
756
sound/soc/codecs/es8328.c
Normal file
@ -0,0 +1,756 @@
|
||||
/*
|
||||
* es8328.c -- ES8328 ALSA SoC Audio driver
|
||||
*
|
||||
* Copyright 2014 Sutajio Ko-Usagi PTE LTD
|
||||
*
|
||||
* Author: Sean Cross <xobs@kosagi.com>
|
||||
*
|
||||
* 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/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/tlv.h>
|
||||
#include "es8328.h"
|
||||
|
||||
#define ES8328_SYSCLK_RATE_1X 11289600
|
||||
#define ES8328_SYSCLK_RATE_2X 22579200
|
||||
|
||||
/* Run the codec at 22.5792 or 11.2896 MHz to support these rates */
|
||||
static struct {
|
||||
int rate;
|
||||
u8 ratio;
|
||||
} mclk_ratios[] = {
|
||||
{ 8000, 9 },
|
||||
{11025, 7 },
|
||||
{22050, 4 },
|
||||
{44100, 2 },
|
||||
};
|
||||
|
||||
/* regulator supplies for sgtl5000, VDDD is an optional external supply */
|
||||
enum sgtl5000_regulator_supplies {
|
||||
DVDD,
|
||||
AVDD,
|
||||
PVDD,
|
||||
HPVDD,
|
||||
ES8328_SUPPLY_NUM
|
||||
};
|
||||
|
||||
/* vddd is optional supply */
|
||||
static const char * const supply_names[ES8328_SUPPLY_NUM] = {
|
||||
"DVDD",
|
||||
"AVDD",
|
||||
"PVDD",
|
||||
"HPVDD",
|
||||
};
|
||||
|
||||
#define ES8328_RATES (SNDRV_PCM_RATE_44100 | \
|
||||
SNDRV_PCM_RATE_22050 | \
|
||||
SNDRV_PCM_RATE_11025)
|
||||
#define ES8328_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
|
||||
|
||||
struct es8328_priv {
|
||||
struct regmap *regmap;
|
||||
struct clk *clk;
|
||||
int playback_fs;
|
||||
bool deemph;
|
||||
struct regulator_bulk_data supplies[ES8328_SUPPLY_NUM];
|
||||
};
|
||||
|
||||
/*
|
||||
* ES8328 Controls
|
||||
*/
|
||||
|
||||
static const char * const adcpol_txt[] = {"Normal", "L Invert", "R Invert",
|
||||
"L + R Invert"};
|
||||
static SOC_ENUM_SINGLE_DECL(adcpol,
|
||||
ES8328_ADCCONTROL6, 6, adcpol_txt);
|
||||
|
||||
static const DECLARE_TLV_DB_SCALE(play_tlv, -3000, 100, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(dac_adc_tlv, -9600, 50, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 300, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 300, 0);
|
||||
|
||||
static const int deemph_settings[] = { 0, 32000, 44100, 48000 };
|
||||
|
||||
static int es8328_set_deemph(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
|
||||
int val, i, best;
|
||||
|
||||
/*
|
||||
* If we're using deemphasis select the nearest available sample
|
||||
* rate.
|
||||
*/
|
||||
if (es8328->deemph) {
|
||||
best = 1;
|
||||
for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) {
|
||||
if (abs(deemph_settings[i] - es8328->playback_fs) <
|
||||
abs(deemph_settings[best] - es8328->playback_fs))
|
||||
best = i;
|
||||
}
|
||||
|
||||
val = best << 1;
|
||||
} else {
|
||||
val = 0;
|
||||
}
|
||||
|
||||
dev_dbg(codec->dev, "Set deemphasis %d\n", val);
|
||||
|
||||
return snd_soc_update_bits(codec, ES8328_DACCONTROL6, 0x6, val);
|
||||
}
|
||||
|
||||
static int es8328_get_deemph(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
|
||||
struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
ucontrol->value.enumerated.item[0] = es8328->deemph;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int es8328_put_deemph(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
|
||||
struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
|
||||
int deemph = ucontrol->value.enumerated.item[0];
|
||||
int ret;
|
||||
|
||||
if (deemph > 1)
|
||||
return -EINVAL;
|
||||
|
||||
ret = es8328_set_deemph(codec);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
es8328->deemph = deemph;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const struct snd_kcontrol_new es8328_snd_controls[] = {
|
||||
SOC_DOUBLE_R_TLV("Capture Digital Volume",
|
||||
ES8328_ADCCONTROL8, ES8328_ADCCONTROL9,
|
||||
0, 0xc0, 1, dac_adc_tlv),
|
||||
SOC_SINGLE("Capture ZC Switch", ES8328_ADCCONTROL7, 6, 1, 0),
|
||||
|
||||
SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0,
|
||||
es8328_get_deemph, es8328_put_deemph),
|
||||
|
||||
SOC_ENUM("Capture Polarity", adcpol),
|
||||
|
||||
SOC_SINGLE_TLV("Left Mixer Left Bypass Volume",
|
||||
ES8328_DACCONTROL17, 3, 7, 1, bypass_tlv),
|
||||
SOC_SINGLE_TLV("Left Mixer Right Bypass Volume",
|
||||
ES8328_DACCONTROL19, 3, 7, 1, bypass_tlv),
|
||||
SOC_SINGLE_TLV("Right Mixer Left Bypass Volume",
|
||||
ES8328_DACCONTROL18, 3, 7, 1, bypass_tlv),
|
||||
SOC_SINGLE_TLV("Right Mixer Right Bypass Volume",
|
||||
ES8328_DACCONTROL20, 3, 7, 1, bypass_tlv),
|
||||
|
||||
SOC_DOUBLE_R_TLV("PCM Volume",
|
||||
ES8328_LDACVOL, ES8328_RDACVOL,
|
||||
0, ES8328_DACVOL_MAX, 1, dac_adc_tlv),
|
||||
|
||||
SOC_DOUBLE_R_TLV("Output 1 Playback Volume",
|
||||
ES8328_LOUT1VOL, ES8328_ROUT1VOL,
|
||||
0, ES8328_OUT1VOL_MAX, 0, play_tlv),
|
||||
|
||||
SOC_DOUBLE_R_TLV("Output 2 Playback Volume",
|
||||
ES8328_LOUT2VOL, ES8328_ROUT2VOL,
|
||||
0, ES8328_OUT2VOL_MAX, 0, play_tlv),
|
||||
|
||||
SOC_DOUBLE_TLV("Mic PGA Volume", ES8328_ADCCONTROL1,
|
||||
4, 0, 8, 0, mic_tlv),
|
||||
};
|
||||
|
||||
/*
|
||||
* DAPM Controls
|
||||
*/
|
||||
|
||||
static const char * const es8328_line_texts[] = {
|
||||
"Line 1", "Line 2", "PGA", "Differential"};
|
||||
|
||||
static const struct soc_enum es8328_lline_enum =
|
||||
SOC_ENUM_SINGLE(ES8328_DACCONTROL16, 3,
|
||||
ARRAY_SIZE(es8328_line_texts),
|
||||
es8328_line_texts);
|
||||
static const struct snd_kcontrol_new es8328_left_line_controls =
|
||||
SOC_DAPM_ENUM("Route", es8328_lline_enum);
|
||||
|
||||
static const struct soc_enum es8328_rline_enum =
|
||||
SOC_ENUM_SINGLE(ES8328_DACCONTROL16, 0,
|
||||
ARRAY_SIZE(es8328_line_texts),
|
||||
es8328_line_texts);
|
||||
static const struct snd_kcontrol_new es8328_right_line_controls =
|
||||
SOC_DAPM_ENUM("Route", es8328_lline_enum);
|
||||
|
||||
/* Left Mixer */
|
||||
static const struct snd_kcontrol_new es8328_left_mixer_controls[] = {
|
||||
SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL17, 8, 1, 0),
|
||||
SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL17, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("Right Playback Switch", ES8328_DACCONTROL18, 8, 1, 0),
|
||||
SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL18, 7, 1, 0),
|
||||
};
|
||||
|
||||
/* Right Mixer */
|
||||
static const struct snd_kcontrol_new es8328_right_mixer_controls[] = {
|
||||
SOC_DAPM_SINGLE("Left Playback Switch", ES8328_DACCONTROL19, 8, 1, 0),
|
||||
SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL19, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL20, 8, 1, 0),
|
||||
SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL20, 7, 1, 0),
|
||||
};
|
||||
|
||||
static const char * const es8328_pga_sel[] = {
|
||||
"Line 1", "Line 2", "Line 3", "Differential"};
|
||||
|
||||
/* Left PGA Mux */
|
||||
static const struct soc_enum es8328_lpga_enum =
|
||||
SOC_ENUM_SINGLE(ES8328_ADCCONTROL2, 6,
|
||||
ARRAY_SIZE(es8328_pga_sel),
|
||||
es8328_pga_sel);
|
||||
static const struct snd_kcontrol_new es8328_left_pga_controls =
|
||||
SOC_DAPM_ENUM("Route", es8328_lpga_enum);
|
||||
|
||||
/* Right PGA Mux */
|
||||
static const struct soc_enum es8328_rpga_enum =
|
||||
SOC_ENUM_SINGLE(ES8328_ADCCONTROL2, 4,
|
||||
ARRAY_SIZE(es8328_pga_sel),
|
||||
es8328_pga_sel);
|
||||
static const struct snd_kcontrol_new es8328_right_pga_controls =
|
||||
SOC_DAPM_ENUM("Route", es8328_rpga_enum);
|
||||
|
||||
/* Differential Mux */
|
||||
static const char * const es8328_diff_sel[] = {"Line 1", "Line 2"};
|
||||
static SOC_ENUM_SINGLE_DECL(diffmux,
|
||||
ES8328_ADCCONTROL3, 7, es8328_diff_sel);
|
||||
static const struct snd_kcontrol_new es8328_diffmux_controls =
|
||||
SOC_DAPM_ENUM("Route", diffmux);
|
||||
|
||||
/* Mono ADC Mux */
|
||||
static const char * const es8328_mono_mux[] = {"Stereo", "Mono (Left)",
|
||||
"Mono (Right)", "Digital Mono"};
|
||||
static SOC_ENUM_SINGLE_DECL(monomux,
|
||||
ES8328_ADCCONTROL3, 3, es8328_mono_mux);
|
||||
static const struct snd_kcontrol_new es8328_monomux_controls =
|
||||
SOC_DAPM_ENUM("Route", monomux);
|
||||
|
||||
static const struct snd_soc_dapm_widget es8328_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0,
|
||||
&es8328_diffmux_controls),
|
||||
SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0,
|
||||
&es8328_monomux_controls),
|
||||
SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0,
|
||||
&es8328_monomux_controls),
|
||||
|
||||
SND_SOC_DAPM_MUX("Left PGA Mux", ES8328_ADCPOWER,
|
||||
ES8328_ADCPOWER_AINL_OFF, 1,
|
||||
&es8328_left_pga_controls),
|
||||
SND_SOC_DAPM_MUX("Right PGA Mux", ES8328_ADCPOWER,
|
||||
ES8328_ADCPOWER_AINR_OFF, 1,
|
||||
&es8328_right_pga_controls),
|
||||
|
||||
SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0,
|
||||
&es8328_left_line_controls),
|
||||
SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0,
|
||||
&es8328_right_line_controls),
|
||||
|
||||
SND_SOC_DAPM_ADC("Right ADC", "Right Capture", ES8328_ADCPOWER,
|
||||
ES8328_ADCPOWER_ADCR_OFF, 1),
|
||||
SND_SOC_DAPM_ADC("Left ADC", "Left Capture", ES8328_ADCPOWER,
|
||||
ES8328_ADCPOWER_ADCL_OFF, 1),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("Mic Bias", ES8328_ADCPOWER,
|
||||
ES8328_ADCPOWER_MIC_BIAS_OFF, 1, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("Mic Bias Gen", ES8328_ADCPOWER,
|
||||
ES8328_ADCPOWER_ADC_BIAS_GEN_OFF, 1, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("DAC STM", ES8328_CHIPPOWER,
|
||||
ES8328_CHIPPOWER_DACSTM_RESET, 1, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("ADC STM", ES8328_CHIPPOWER,
|
||||
ES8328_CHIPPOWER_ADCSTM_RESET, 1, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("DAC DIG", ES8328_CHIPPOWER,
|
||||
ES8328_CHIPPOWER_DACDIG_OFF, 1, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("ADC DIG", ES8328_CHIPPOWER,
|
||||
ES8328_CHIPPOWER_ADCDIG_OFF, 1, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("DAC DLL", ES8328_CHIPPOWER,
|
||||
ES8328_CHIPPOWER_DACDLL_OFF, 1, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("ADC DLL", ES8328_CHIPPOWER,
|
||||
ES8328_CHIPPOWER_ADCDLL_OFF, 1, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("ADC Vref", ES8328_CHIPPOWER,
|
||||
ES8328_CHIPPOWER_ADCVREF_OFF, 1, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("DAC Vref", ES8328_CHIPPOWER,
|
||||
ES8328_CHIPPOWER_DACVREF_OFF, 1, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_DAC("Right DAC", "Right Playback", ES8328_DACPOWER,
|
||||
ES8328_DACPOWER_RDAC_OFF, 1),
|
||||
SND_SOC_DAPM_DAC("Left DAC", "Left Playback", ES8328_DACPOWER,
|
||||
ES8328_DACPOWER_LDAC_OFF, 1),
|
||||
|
||||
SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
|
||||
&es8328_left_mixer_controls[0],
|
||||
ARRAY_SIZE(es8328_left_mixer_controls)),
|
||||
SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
|
||||
&es8328_right_mixer_controls[0],
|
||||
ARRAY_SIZE(es8328_right_mixer_controls)),
|
||||
|
||||
SND_SOC_DAPM_PGA("Right Out 2", ES8328_DACPOWER,
|
||||
ES8328_DACPOWER_ROUT2_ON, 0, NULL, 0),
|
||||
SND_SOC_DAPM_PGA("Left Out 2", ES8328_DACPOWER,
|
||||
ES8328_DACPOWER_LOUT2_ON, 0, NULL, 0),
|
||||
SND_SOC_DAPM_PGA("Right Out 1", ES8328_DACPOWER,
|
||||
ES8328_DACPOWER_ROUT1_ON, 0, NULL, 0),
|
||||
SND_SOC_DAPM_PGA("Left Out 1", ES8328_DACPOWER,
|
||||
ES8328_DACPOWER_LOUT1_ON, 0, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_OUTPUT("LOUT1"),
|
||||
SND_SOC_DAPM_OUTPUT("ROUT1"),
|
||||
SND_SOC_DAPM_OUTPUT("LOUT2"),
|
||||
SND_SOC_DAPM_OUTPUT("ROUT2"),
|
||||
|
||||
SND_SOC_DAPM_INPUT("LINPUT1"),
|
||||
SND_SOC_DAPM_INPUT("LINPUT2"),
|
||||
SND_SOC_DAPM_INPUT("RINPUT1"),
|
||||
SND_SOC_DAPM_INPUT("RINPUT2"),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route es8328_dapm_routes[] = {
|
||||
|
||||
{ "Left Line Mux", "Line 1", "LINPUT1" },
|
||||
{ "Left Line Mux", "Line 2", "LINPUT2" },
|
||||
{ "Left Line Mux", "PGA", "Left PGA Mux" },
|
||||
{ "Left Line Mux", "Differential", "Differential Mux" },
|
||||
|
||||
{ "Right Line Mux", "Line 1", "RINPUT1" },
|
||||
{ "Right Line Mux", "Line 2", "RINPUT2" },
|
||||
{ "Right Line Mux", "PGA", "Right PGA Mux" },
|
||||
{ "Right Line Mux", "Differential", "Differential Mux" },
|
||||
|
||||
{ "Left PGA Mux", "Line 1", "LINPUT1" },
|
||||
{ "Left PGA Mux", "Line 2", "LINPUT2" },
|
||||
{ "Left PGA Mux", "Differential", "Differential Mux" },
|
||||
|
||||
{ "Right PGA Mux", "Line 1", "RINPUT1" },
|
||||
{ "Right PGA Mux", "Line 2", "RINPUT2" },
|
||||
{ "Right PGA Mux", "Differential", "Differential Mux" },
|
||||
|
||||
{ "Differential Mux", "Line 1", "LINPUT1" },
|
||||
{ "Differential Mux", "Line 1", "RINPUT1" },
|
||||
{ "Differential Mux", "Line 2", "LINPUT2" },
|
||||
{ "Differential Mux", "Line 2", "RINPUT2" },
|
||||
|
||||
{ "Left ADC Mux", "Stereo", "Left PGA Mux" },
|
||||
{ "Left ADC Mux", "Mono (Left)", "Left PGA Mux" },
|
||||
{ "Left ADC Mux", "Digital Mono", "Left PGA Mux" },
|
||||
|
||||
{ "Right ADC Mux", "Stereo", "Right PGA Mux" },
|
||||
{ "Right ADC Mux", "Mono (Right)", "Right PGA Mux" },
|
||||
{ "Right ADC Mux", "Digital Mono", "Right PGA Mux" },
|
||||
|
||||
{ "Left ADC", NULL, "Left ADC Mux" },
|
||||
{ "Right ADC", NULL, "Right ADC Mux" },
|
||||
|
||||
{ "ADC DIG", NULL, "ADC STM" },
|
||||
{ "ADC DIG", NULL, "ADC Vref" },
|
||||
{ "ADC DIG", NULL, "ADC DLL" },
|
||||
|
||||
{ "Left ADC", NULL, "ADC DIG" },
|
||||
{ "Right ADC", NULL, "ADC DIG" },
|
||||
|
||||
{ "Mic Bias", NULL, "Mic Bias Gen" },
|
||||
|
||||
{ "Left Line Mux", "Line 1", "LINPUT1" },
|
||||
{ "Left Line Mux", "Line 2", "LINPUT2" },
|
||||
{ "Left Line Mux", "PGA", "Left PGA Mux" },
|
||||
{ "Left Line Mux", "Differential", "Differential Mux" },
|
||||
|
||||
{ "Right Line Mux", "Line 1", "RINPUT1" },
|
||||
{ "Right Line Mux", "Line 2", "RINPUT2" },
|
||||
{ "Right Line Mux", "PGA", "Right PGA Mux" },
|
||||
{ "Right Line Mux", "Differential", "Differential Mux" },
|
||||
|
||||
{ "Left Out 1", NULL, "Left DAC" },
|
||||
{ "Right Out 1", NULL, "Right DAC" },
|
||||
{ "Left Out 2", NULL, "Left DAC" },
|
||||
{ "Right Out 2", NULL, "Right DAC" },
|
||||
|
||||
{ "Left Mixer", "Playback Switch", "Left DAC" },
|
||||
{ "Left Mixer", "Left Bypass Switch", "Left Line Mux" },
|
||||
{ "Left Mixer", "Right Playback Switch", "Right DAC" },
|
||||
{ "Left Mixer", "Right Bypass Switch", "Right Line Mux" },
|
||||
|
||||
{ "Right Mixer", "Left Playback Switch", "Left DAC" },
|
||||
{ "Right Mixer", "Left Bypass Switch", "Left Line Mux" },
|
||||
{ "Right Mixer", "Playback Switch", "Right DAC" },
|
||||
{ "Right Mixer", "Right Bypass Switch", "Right Line Mux" },
|
||||
|
||||
{ "DAC DIG", NULL, "DAC STM" },
|
||||
{ "DAC DIG", NULL, "DAC Vref" },
|
||||
{ "DAC DIG", NULL, "DAC DLL" },
|
||||
|
||||
{ "Left DAC", NULL, "DAC DIG" },
|
||||
{ "Right DAC", NULL, "DAC DIG" },
|
||||
|
||||
{ "Left Out 1", NULL, "Left Mixer" },
|
||||
{ "LOUT1", NULL, "Left Out 1" },
|
||||
{ "Right Out 1", NULL, "Right Mixer" },
|
||||
{ "ROUT1", NULL, "Right Out 1" },
|
||||
|
||||
{ "Left Out 2", NULL, "Left Mixer" },
|
||||
{ "LOUT2", NULL, "Left Out 2" },
|
||||
{ "Right Out 2", NULL, "Right Mixer" },
|
||||
{ "ROUT2", NULL, "Right Out 2" },
|
||||
};
|
||||
|
||||
static int es8328_mute(struct snd_soc_dai *dai, int mute)
|
||||
{
|
||||
return snd_soc_update_bits(dai->codec, ES8328_DACCONTROL3,
|
||||
ES8328_DACCONTROL3_DACMUTE,
|
||||
mute ? ES8328_DACCONTROL3_DACMUTE : 0);
|
||||
}
|
||||
|
||||
static int es8328_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 es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
|
||||
int clk_rate;
|
||||
int i;
|
||||
int reg;
|
||||
u8 ratio;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
reg = ES8328_DACCONTROL2;
|
||||
else
|
||||
reg = ES8328_ADCCONTROL5;
|
||||
|
||||
clk_rate = clk_get_rate(es8328->clk);
|
||||
|
||||
if ((clk_rate != ES8328_SYSCLK_RATE_1X) &&
|
||||
(clk_rate != ES8328_SYSCLK_RATE_2X)) {
|
||||
dev_err(codec->dev,
|
||||
"%s: clock is running at %d Hz, not %d or %d Hz\n",
|
||||
__func__, clk_rate,
|
||||
ES8328_SYSCLK_RATE_1X, ES8328_SYSCLK_RATE_2X);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* find master mode MCLK to sampling frequency ratio */
|
||||
ratio = mclk_ratios[0].rate;
|
||||
for (i = 1; i < ARRAY_SIZE(mclk_ratios); i++)
|
||||
if (params_rate(params) <= mclk_ratios[i].rate)
|
||||
ratio = mclk_ratios[i].ratio;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
es8328->playback_fs = params_rate(params);
|
||||
es8328_set_deemph(codec);
|
||||
}
|
||||
|
||||
return snd_soc_update_bits(codec, reg, ES8328_RATEMASK, ratio);
|
||||
}
|
||||
|
||||
static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
||||
unsigned int fmt)
|
||||
{
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
|
||||
int clk_rate;
|
||||
u8 mode = ES8328_DACCONTROL1_DACWL_16;
|
||||
|
||||
/* set master/slave audio interface */
|
||||
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBM_CFM)
|
||||
return -EINVAL;
|
||||
|
||||
/* interface format */
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
mode |= ES8328_DACCONTROL1_DACFORMAT_I2S;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_RIGHT_J:
|
||||
mode |= ES8328_DACCONTROL1_DACFORMAT_RJUST;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
mode |= ES8328_DACCONTROL1_DACFORMAT_LJUST;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* clock inversion */
|
||||
if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF)
|
||||
return -EINVAL;
|
||||
|
||||
snd_soc_write(codec, ES8328_DACCONTROL1, mode);
|
||||
snd_soc_write(codec, ES8328_ADCCONTROL4, mode);
|
||||
|
||||
/* Master serial port mode, with BCLK generated automatically */
|
||||
clk_rate = clk_get_rate(es8328->clk);
|
||||
if (clk_rate == ES8328_SYSCLK_RATE_1X)
|
||||
snd_soc_write(codec, ES8328_MASTERMODE,
|
||||
ES8328_MASTERMODE_MSC);
|
||||
else
|
||||
snd_soc_write(codec, ES8328_MASTERMODE,
|
||||
ES8328_MASTERMODE_MCLKDIV2 |
|
||||
ES8328_MASTERMODE_MSC);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int es8328_set_bias_level(struct snd_soc_codec *codec,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
switch (level) {
|
||||
case SND_SOC_BIAS_ON:
|
||||
break;
|
||||
|
||||
case SND_SOC_BIAS_PREPARE:
|
||||
/* VREF, VMID=2x50k, digital enabled */
|
||||
snd_soc_write(codec, ES8328_CHIPPOWER, 0);
|
||||
snd_soc_update_bits(codec, ES8328_CONTROL1,
|
||||
ES8328_CONTROL1_VMIDSEL_MASK |
|
||||
ES8328_CONTROL1_ENREF,
|
||||
ES8328_CONTROL1_VMIDSEL_50k |
|
||||
ES8328_CONTROL1_ENREF);
|
||||
break;
|
||||
|
||||
case SND_SOC_BIAS_STANDBY:
|
||||
if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
|
||||
snd_soc_update_bits(codec, ES8328_CONTROL1,
|
||||
ES8328_CONTROL1_VMIDSEL_MASK |
|
||||
ES8328_CONTROL1_ENREF,
|
||||
ES8328_CONTROL1_VMIDSEL_5k |
|
||||
ES8328_CONTROL1_ENREF);
|
||||
|
||||
/* Charge caps */
|
||||
msleep(100);
|
||||
}
|
||||
|
||||
snd_soc_write(codec, ES8328_CONTROL2,
|
||||
ES8328_CONTROL2_OVERCURRENT_ON |
|
||||
ES8328_CONTROL2_THERMAL_SHUTDOWN_ON);
|
||||
|
||||
/* VREF, VMID=2*500k, digital stopped */
|
||||
snd_soc_update_bits(codec, ES8328_CONTROL1,
|
||||
ES8328_CONTROL1_VMIDSEL_MASK |
|
||||
ES8328_CONTROL1_ENREF,
|
||||
ES8328_CONTROL1_VMIDSEL_500k |
|
||||
ES8328_CONTROL1_ENREF);
|
||||
break;
|
||||
|
||||
case SND_SOC_BIAS_OFF:
|
||||
snd_soc_update_bits(codec, ES8328_CONTROL1,
|
||||
ES8328_CONTROL1_VMIDSEL_MASK |
|
||||
ES8328_CONTROL1_ENREF,
|
||||
0);
|
||||
break;
|
||||
}
|
||||
codec->dapm.bias_level = level;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops es8328_dai_ops = {
|
||||
.hw_params = es8328_hw_params,
|
||||
.digital_mute = es8328_mute,
|
||||
.set_fmt = es8328_set_dai_fmt,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver es8328_dai = {
|
||||
.name = "es8328-hifi-analog",
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = ES8328_RATES,
|
||||
.formats = ES8328_FORMATS,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "Capture",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = ES8328_RATES,
|
||||
.formats = ES8328_FORMATS,
|
||||
},
|
||||
.ops = &es8328_dai_ops,
|
||||
};
|
||||
|
||||
static int es8328_suspend(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct es8328_priv *es8328;
|
||||
int ret;
|
||||
|
||||
es8328 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
clk_disable_unprepare(es8328->clk);
|
||||
|
||||
ret = regulator_bulk_disable(ARRAY_SIZE(es8328->supplies),
|
||||
es8328->supplies);
|
||||
if (ret) {
|
||||
dev_err(codec->dev, "unable to disable regulators\n");
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int es8328_resume(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
|
||||
struct es8328_priv *es8328;
|
||||
int ret;
|
||||
|
||||
es8328 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
ret = clk_prepare_enable(es8328->clk);
|
||||
if (ret) {
|
||||
dev_err(codec->dev, "unable to enable clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(es8328->supplies),
|
||||
es8328->supplies);
|
||||
if (ret) {
|
||||
dev_err(codec->dev, "unable to enable regulators\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
regcache_mark_dirty(regmap);
|
||||
ret = regcache_sync(regmap);
|
||||
if (ret) {
|
||||
dev_err(codec->dev, "unable to sync regcache\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int es8328_codec_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct es8328_priv *es8328;
|
||||
int ret;
|
||||
|
||||
es8328 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(es8328->supplies),
|
||||
es8328->supplies);
|
||||
if (ret) {
|
||||
dev_err(codec->dev, "unable to enable regulators\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Setup clocks */
|
||||
es8328->clk = devm_clk_get(codec->dev, NULL);
|
||||
if (IS_ERR(es8328->clk)) {
|
||||
dev_err(codec->dev, "codec clock missing or invalid\n");
|
||||
ret = PTR_ERR(es8328->clk);
|
||||
goto clk_fail;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(es8328->clk);
|
||||
if (ret) {
|
||||
dev_err(codec->dev, "unable to prepare codec clk\n");
|
||||
goto clk_fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
clk_fail:
|
||||
regulator_bulk_disable(ARRAY_SIZE(es8328->supplies),
|
||||
es8328->supplies);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int es8328_remove(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct es8328_priv *es8328;
|
||||
|
||||
es8328 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
if (es8328->clk)
|
||||
clk_disable_unprepare(es8328->clk);
|
||||
|
||||
regulator_bulk_disable(ARRAY_SIZE(es8328->supplies),
|
||||
es8328->supplies);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct regmap_config es8328_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = ES8328_REG_MAX,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(es8328_regmap_config);
|
||||
|
||||
static struct snd_soc_codec_driver es8328_codec_driver = {
|
||||
.probe = es8328_codec_probe,
|
||||
.suspend = es8328_suspend,
|
||||
.resume = es8328_resume,
|
||||
.remove = es8328_remove,
|
||||
.set_bias_level = es8328_set_bias_level,
|
||||
.suspend_bias_off = true,
|
||||
|
||||
.controls = es8328_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(es8328_snd_controls),
|
||||
.dapm_widgets = es8328_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(es8328_dapm_widgets),
|
||||
.dapm_routes = es8328_dapm_routes,
|
||||
.num_dapm_routes = ARRAY_SIZE(es8328_dapm_routes),
|
||||
};
|
||||
|
||||
int es8328_probe(struct device *dev, struct regmap *regmap)
|
||||
{
|
||||
struct es8328_priv *es8328;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
es8328 = devm_kzalloc(dev, sizeof(*es8328), GFP_KERNEL);
|
||||
if (es8328 == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
es8328->regmap = regmap;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(es8328->supplies); i++)
|
||||
es8328->supplies[i].supply = supply_names[i];
|
||||
|
||||
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(es8328->supplies),
|
||||
es8328->supplies);
|
||||
if (ret) {
|
||||
dev_err(dev, "unable to get regulators\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_set_drvdata(dev, es8328);
|
||||
|
||||
return snd_soc_register_codec(dev,
|
||||
&es8328_codec_driver, &es8328_dai, 1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(es8328_probe);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC ES8328 driver");
|
||||
MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
|
||||
MODULE_LICENSE("GPL");
|
314
sound/soc/codecs/es8328.h
Normal file
314
sound/soc/codecs/es8328.h
Normal file
@ -0,0 +1,314 @@
|
||||
/*
|
||||
* es8328.h -- ES8328 ALSA SoC Audio driver
|
||||
*/
|
||||
|
||||
#ifndef _ES8328_H
|
||||
#define _ES8328_H
|
||||
|
||||
#include <linux/regmap.h>
|
||||
|
||||
struct device;
|
||||
|
||||
extern const struct regmap_config es8328_regmap_config;
|
||||
int es8328_probe(struct device *dev, struct regmap *regmap);
|
||||
|
||||
#define ES8328_DACLVOL 46
|
||||
#define ES8328_DACRVOL 47
|
||||
#define ES8328_DACCTL 28
|
||||
#define ES8328_RATEMASK (0x1f << 0)
|
||||
|
||||
#define ES8328_CONTROL1 0x00
|
||||
#define ES8328_CONTROL1_VMIDSEL_OFF (0 << 0)
|
||||
#define ES8328_CONTROL1_VMIDSEL_50k (1 << 0)
|
||||
#define ES8328_CONTROL1_VMIDSEL_500k (2 << 0)
|
||||
#define ES8328_CONTROL1_VMIDSEL_5k (3 << 0)
|
||||
#define ES8328_CONTROL1_VMIDSEL_MASK (7 << 0)
|
||||
#define ES8328_CONTROL1_ENREF (1 << 2)
|
||||
#define ES8328_CONTROL1_SEQEN (1 << 3)
|
||||
#define ES8328_CONTROL1_SAMEFS (1 << 4)
|
||||
#define ES8328_CONTROL1_DACMCLK_ADC (0 << 5)
|
||||
#define ES8328_CONTROL1_DACMCLK_DAC (1 << 5)
|
||||
#define ES8328_CONTROL1_LRCM (1 << 6)
|
||||
#define ES8328_CONTROL1_SCP_RESET (1 << 7)
|
||||
|
||||
#define ES8328_CONTROL2 0x01
|
||||
#define ES8328_CONTROL2_VREF_BUF_OFF (1 << 0)
|
||||
#define ES8328_CONTROL2_VREF_LOWPOWER (1 << 1)
|
||||
#define ES8328_CONTROL2_IBIASGEN_OFF (1 << 2)
|
||||
#define ES8328_CONTROL2_ANALOG_OFF (1 << 3)
|
||||
#define ES8328_CONTROL2_VREF_BUF_LOWPOWER (1 << 4)
|
||||
#define ES8328_CONTROL2_VCM_MOD_LOWPOWER (1 << 5)
|
||||
#define ES8328_CONTROL2_OVERCURRENT_ON (1 << 6)
|
||||
#define ES8328_CONTROL2_THERMAL_SHUTDOWN_ON (1 << 7)
|
||||
|
||||
#define ES8328_CHIPPOWER 0x02
|
||||
#define ES8328_CHIPPOWER_DACVREF_OFF 0
|
||||
#define ES8328_CHIPPOWER_ADCVREF_OFF 1
|
||||
#define ES8328_CHIPPOWER_DACDLL_OFF 2
|
||||
#define ES8328_CHIPPOWER_ADCDLL_OFF 3
|
||||
#define ES8328_CHIPPOWER_DACSTM_RESET 4
|
||||
#define ES8328_CHIPPOWER_ADCSTM_RESET 5
|
||||
#define ES8328_CHIPPOWER_DACDIG_OFF 6
|
||||
#define ES8328_CHIPPOWER_ADCDIG_OFF 7
|
||||
|
||||
#define ES8328_ADCPOWER 0x03
|
||||
#define ES8328_ADCPOWER_INT1_LOWPOWER 0
|
||||
#define ES8328_ADCPOWER_FLASH_ADC_LOWPOWER 1
|
||||
#define ES8328_ADCPOWER_ADC_BIAS_GEN_OFF 2
|
||||
#define ES8328_ADCPOWER_MIC_BIAS_OFF 3
|
||||
#define ES8328_ADCPOWER_ADCR_OFF 4
|
||||
#define ES8328_ADCPOWER_ADCL_OFF 5
|
||||
#define ES8328_ADCPOWER_AINR_OFF 6
|
||||
#define ES8328_ADCPOWER_AINL_OFF 7
|
||||
|
||||
#define ES8328_DACPOWER 0x04
|
||||
#define ES8328_DACPOWER_OUT3_ON 0
|
||||
#define ES8328_DACPOWER_MONO_ON 1
|
||||
#define ES8328_DACPOWER_ROUT2_ON 2
|
||||
#define ES8328_DACPOWER_LOUT2_ON 3
|
||||
#define ES8328_DACPOWER_ROUT1_ON 4
|
||||
#define ES8328_DACPOWER_LOUT1_ON 5
|
||||
#define ES8328_DACPOWER_RDAC_OFF 6
|
||||
#define ES8328_DACPOWER_LDAC_OFF 7
|
||||
|
||||
#define ES8328_CHIPLOPOW1 0x05
|
||||
#define ES8328_CHIPLOPOW2 0x06
|
||||
#define ES8328_ANAVOLMANAG 0x07
|
||||
|
||||
#define ES8328_MASTERMODE 0x08
|
||||
#define ES8328_MASTERMODE_BCLKDIV (0 << 0)
|
||||
#define ES8328_MASTERMODE_BCLK_INV (1 << 5)
|
||||
#define ES8328_MASTERMODE_MCLKDIV2 (1 << 6)
|
||||
#define ES8328_MASTERMODE_MSC (1 << 7)
|
||||
|
||||
#define ES8328_ADCCONTROL1 0x09
|
||||
#define ES8328_ADCCONTROL2 0x0a
|
||||
#define ES8328_ADCCONTROL3 0x0b
|
||||
#define ES8328_ADCCONTROL4 0x0c
|
||||
#define ES8328_ADCCONTROL5 0x0d
|
||||
#define ES8328_ADCCONTROL5_RATEMASK (0x1f << 0)
|
||||
|
||||
#define ES8328_ADCCONTROL6 0x0e
|
||||
|
||||
#define ES8328_ADCCONTROL7 0x0f
|
||||
#define ES8328_ADCCONTROL7_ADC_MUTE (1 << 2)
|
||||
#define ES8328_ADCCONTROL7_ADC_LER (1 << 3)
|
||||
#define ES8328_ADCCONTROL7_ADC_ZERO_CROSS (1 << 4)
|
||||
#define ES8328_ADCCONTROL7_ADC_SOFT_RAMP (1 << 5)
|
||||
#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_4 (0 << 6)
|
||||
#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_8 (1 << 6)
|
||||
#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_16 (2 << 6)
|
||||
#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_32 (3 << 6)
|
||||
|
||||
#define ES8328_ADCCONTROL8 0x10
|
||||
#define ES8328_ADCCONTROL9 0x11
|
||||
#define ES8328_ADCCONTROL10 0x12
|
||||
#define ES8328_ADCCONTROL11 0x13
|
||||
#define ES8328_ADCCONTROL12 0x14
|
||||
#define ES8328_ADCCONTROL13 0x15
|
||||
#define ES8328_ADCCONTROL14 0x16
|
||||
|
||||
#define ES8328_DACCONTROL1 0x17
|
||||
#define ES8328_DACCONTROL1_DACFORMAT_I2S (0 << 1)
|
||||
#define ES8328_DACCONTROL1_DACFORMAT_LJUST (1 << 1)
|
||||
#define ES8328_DACCONTROL1_DACFORMAT_RJUST (2 << 1)
|
||||
#define ES8328_DACCONTROL1_DACFORMAT_PCM (3 << 1)
|
||||
#define ES8328_DACCONTROL1_DACWL_24 (0 << 3)
|
||||
#define ES8328_DACCONTROL1_DACWL_20 (1 << 3)
|
||||
#define ES8328_DACCONTROL1_DACWL_18 (2 << 3)
|
||||
#define ES8328_DACCONTROL1_DACWL_16 (3 << 3)
|
||||
#define ES8328_DACCONTROL1_DACWL_32 (4 << 3)
|
||||
#define ES8328_DACCONTROL1_DACLRP_I2S_POL_NORMAL (0 << 6)
|
||||
#define ES8328_DACCONTROL1_DACLRP_I2S_POL_INV (1 << 6)
|
||||
#define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK2 (0 << 6)
|
||||
#define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK1 (1 << 6)
|
||||
#define ES8328_DACCONTROL1_LRSWAP (1 << 7)
|
||||
|
||||
#define ES8328_DACCONTROL2 0x18
|
||||
#define ES8328_DACCONTROL2_RATEMASK (0x1f << 0)
|
||||
#define ES8328_DACCONTROL2_DOUBLESPEED (1 << 5)
|
||||
|
||||
#define ES8328_DACCONTROL3 0x19
|
||||
#define ES8328_DACCONTROL3_AUTOMUTE (1 << 2)
|
||||
#define ES8328_DACCONTROL3_DACMUTE (1 << 2)
|
||||
#define ES8328_DACCONTROL3_LEFTGAINVOL (1 << 3)
|
||||
#define ES8328_DACCONTROL3_DACZEROCROSS (1 << 4)
|
||||
#define ES8328_DACCONTROL3_DACSOFTRAMP (1 << 5)
|
||||
#define ES8328_DACCONTROL3_DACRAMPRATE (3 << 6)
|
||||
|
||||
#define ES8328_LDACVOL 0x1a
|
||||
#define ES8328_LDACVOL_MASK (0 << 0)
|
||||
#define ES8328_LDACVOL_MAX (0xc0)
|
||||
|
||||
#define ES8328_RDACVOL 0x1b
|
||||
#define ES8328_RDACVOL_MASK (0 << 0)
|
||||
#define ES8328_RDACVOL_MAX (0xc0)
|
||||
|
||||
#define ES8328_DACVOL_MAX (0xc0)
|
||||
|
||||
#define ES8328_DACCONTROL4 0x1a
|
||||
#define ES8328_DACCONTROL5 0x1b
|
||||
|
||||
#define ES8328_DACCONTROL6 0x1c
|
||||
#define ES8328_DACCONTROL6_CLICKFREE (1 << 3)
|
||||
#define ES8328_DACCONTROL6_DAC_INVR (1 << 4)
|
||||
#define ES8328_DACCONTROL6_DAC_INVL (1 << 5)
|
||||
#define ES8328_DACCONTROL6_DEEMPH_OFF (0 << 6)
|
||||
#define ES8328_DACCONTROL6_DEEMPH_32k (1 << 6)
|
||||
#define ES8328_DACCONTROL6_DEEMPH_44_1k (2 << 6)
|
||||
#define ES8328_DACCONTROL6_DEEMPH_48k (3 << 6)
|
||||
|
||||
#define ES8328_DACCONTROL7 0x1d
|
||||
#define ES8328_DACCONTROL7_VPP_SCALE_3p5 (0 << 0)
|
||||
#define ES8328_DACCONTROL7_VPP_SCALE_4p0 (1 << 0)
|
||||
#define ES8328_DACCONTROL7_VPP_SCALE_3p0 (2 << 0)
|
||||
#define ES8328_DACCONTROL7_VPP_SCALE_2p5 (3 << 0)
|
||||
#define ES8328_DACCONTROL7_SHELVING_STRENGTH (1 << 2) /* In eights */
|
||||
#define ES8328_DACCONTROL7_MONO (1 << 5)
|
||||
#define ES8328_DACCONTROL7_ZEROR (1 << 6)
|
||||
#define ES8328_DACCONTROL7_ZEROL (1 << 7)
|
||||
|
||||
/* Shelving filter */
|
||||
#define ES8328_DACCONTROL8 0x1e
|
||||
#define ES8328_DACCONTROL9 0x1f
|
||||
#define ES8328_DACCONTROL10 0x20
|
||||
#define ES8328_DACCONTROL11 0x21
|
||||
#define ES8328_DACCONTROL12 0x22
|
||||
#define ES8328_DACCONTROL13 0x23
|
||||
#define ES8328_DACCONTROL14 0x24
|
||||
#define ES8328_DACCONTROL15 0x25
|
||||
|
||||
#define ES8328_DACCONTROL16 0x26
|
||||
#define ES8328_DACCONTROL16_RMIXSEL_RIN1 (0 << 0)
|
||||
#define ES8328_DACCONTROL16_RMIXSEL_RIN2 (1 << 0)
|
||||
#define ES8328_DACCONTROL16_RMIXSEL_RIN3 (2 << 0)
|
||||
#define ES8328_DACCONTROL16_RMIXSEL_RADC (3 << 0)
|
||||
#define ES8328_DACCONTROL16_LMIXSEL_LIN1 (0 << 3)
|
||||
#define ES8328_DACCONTROL16_LMIXSEL_LIN2 (1 << 3)
|
||||
#define ES8328_DACCONTROL16_LMIXSEL_LIN3 (2 << 3)
|
||||
#define ES8328_DACCONTROL16_LMIXSEL_LADC (3 << 3)
|
||||
|
||||
#define ES8328_DACCONTROL17 0x27
|
||||
#define ES8328_DACCONTROL17_LI2LOVOL (7 << 3)
|
||||
#define ES8328_DACCONTROL17_LI2LO (1 << 6)
|
||||
#define ES8328_DACCONTROL17_LD2LO (1 << 7)
|
||||
|
||||
#define ES8328_DACCONTROL18 0x28
|
||||
#define ES8328_DACCONTROL18_RI2LOVOL (7 << 3)
|
||||
#define ES8328_DACCONTROL18_RI2LO (1 << 6)
|
||||
#define ES8328_DACCONTROL18_RD2LO (1 << 7)
|
||||
|
||||
#define ES8328_DACCONTROL19 0x29
|
||||
#define ES8328_DACCONTROL19_LI2ROVOL (7 << 3)
|
||||
#define ES8328_DACCONTROL19_LI2RO (1 << 6)
|
||||
#define ES8328_DACCONTROL19_LD2RO (1 << 7)
|
||||
|
||||
#define ES8328_DACCONTROL20 0x2a
|
||||
#define ES8328_DACCONTROL20_RI2ROVOL (7 << 3)
|
||||
#define ES8328_DACCONTROL20_RI2RO (1 << 6)
|
||||
#define ES8328_DACCONTROL20_RD2RO (1 << 7)
|
||||
|
||||
#define ES8328_DACCONTROL21 0x2b
|
||||
#define ES8328_DACCONTROL21_LI2MOVOL (7 << 3)
|
||||
#define ES8328_DACCONTROL21_LI2MO (1 << 6)
|
||||
#define ES8328_DACCONTROL21_LD2MO (1 << 7)
|
||||
|
||||
#define ES8328_DACCONTROL22 0x2c
|
||||
#define ES8328_DACCONTROL22_RI2MOVOL (7 << 3)
|
||||
#define ES8328_DACCONTROL22_RI2MO (1 << 6)
|
||||
#define ES8328_DACCONTROL22_RD2MO (1 << 7)
|
||||
|
||||
#define ES8328_DACCONTROL23 0x2d
|
||||
#define ES8328_DACCONTROL23_MOUTINV (1 << 1)
|
||||
#define ES8328_DACCONTROL23_HPSWPOL (1 << 2)
|
||||
#define ES8328_DACCONTROL23_HPSWEN (1 << 3)
|
||||
#define ES8328_DACCONTROL23_VROI_1p5k (0 << 4)
|
||||
#define ES8328_DACCONTROL23_VROI_40k (1 << 4)
|
||||
#define ES8328_DACCONTROL23_OUT3_VREF (0 << 5)
|
||||
#define ES8328_DACCONTROL23_OUT3_ROUT1 (1 << 5)
|
||||
#define ES8328_DACCONTROL23_OUT3_MONOOUT (2 << 5)
|
||||
#define ES8328_DACCONTROL23_OUT3_RIGHT_MIXER (3 << 5)
|
||||
#define ES8328_DACCONTROL23_ROUT2INV (1 << 7)
|
||||
|
||||
/* LOUT1 Amplifier */
|
||||
#define ES8328_LOUT1VOL 0x2e
|
||||
#define ES8328_LOUT1VOL_MASK (0 << 5)
|
||||
#define ES8328_LOUT1VOL_MAX (0x24)
|
||||
|
||||
/* ROUT1 Amplifier */
|
||||
#define ES8328_ROUT1VOL 0x2f
|
||||
#define ES8328_ROUT1VOL_MASK (0 << 5)
|
||||
#define ES8328_ROUT1VOL_MAX (0x24)
|
||||
|
||||
#define ES8328_OUT1VOL_MAX (0x24)
|
||||
|
||||
/* LOUT2 Amplifier */
|
||||
#define ES8328_LOUT2VOL 0x30
|
||||
#define ES8328_LOUT2VOL_MASK (0 << 5)
|
||||
#define ES8328_LOUT2VOL_MAX (0x24)
|
||||
|
||||
/* ROUT2 Amplifier */
|
||||
#define ES8328_ROUT2VOL 0x31
|
||||
#define ES8328_ROUT2VOL_MASK (0 << 5)
|
||||
#define ES8328_ROUT2VOL_MAX (0x24)
|
||||
|
||||
#define ES8328_OUT2VOL_MAX (0x24)
|
||||
|
||||
/* Mono Out Amplifier */
|
||||
#define ES8328_MONOOUTVOL 0x32
|
||||
#define ES8328_MONOOUTVOL_MASK (0 << 5)
|
||||
#define ES8328_MONOOUTVOL_MAX (0x24)
|
||||
|
||||
#define ES8328_DACCONTROL29 0x33
|
||||
#define ES8328_DACCONTROL30 0x34
|
||||
|
||||
#define ES8328_SYSCLK 0
|
||||
|
||||
#define ES8328_REG_MAX 0x35
|
||||
|
||||
#define ES8328_PLL1 0
|
||||
#define ES8328_PLL2 1
|
||||
|
||||
/* clock inputs */
|
||||
#define ES8328_MCLK 0
|
||||
#define ES8328_PCMCLK 1
|
||||
|
||||
/* clock divider id's */
|
||||
#define ES8328_PCMDIV 0
|
||||
#define ES8328_BCLKDIV 1
|
||||
#define ES8328_VXCLKDIV 2
|
||||
|
||||
/* PCM clock dividers */
|
||||
#define ES8328_PCM_DIV_1 (0 << 6)
|
||||
#define ES8328_PCM_DIV_3 (2 << 6)
|
||||
#define ES8328_PCM_DIV_5_5 (3 << 6)
|
||||
#define ES8328_PCM_DIV_2 (4 << 6)
|
||||
#define ES8328_PCM_DIV_4 (5 << 6)
|
||||
#define ES8328_PCM_DIV_6 (6 << 6)
|
||||
#define ES8328_PCM_DIV_8 (7 << 6)
|
||||
|
||||
/* BCLK clock dividers */
|
||||
#define ES8328_BCLK_DIV_1 (0 << 7)
|
||||
#define ES8328_BCLK_DIV_2 (1 << 7)
|
||||
#define ES8328_BCLK_DIV_4 (2 << 7)
|
||||
#define ES8328_BCLK_DIV_8 (3 << 7)
|
||||
|
||||
/* VXCLK clock dividers */
|
||||
#define ES8328_VXCLK_DIV_1 (0 << 6)
|
||||
#define ES8328_VXCLK_DIV_2 (1 << 6)
|
||||
#define ES8328_VXCLK_DIV_4 (2 << 6)
|
||||
#define ES8328_VXCLK_DIV_8 (3 << 6)
|
||||
#define ES8328_VXCLK_DIV_16 (4 << 6)
|
||||
|
||||
#define ES8328_DAI_HIFI 0
|
||||
#define ES8328_DAI_VOICE 1
|
||||
|
||||
#define ES8328_1536FS 1536
|
||||
#define ES8328_1024FS 1024
|
||||
#define ES8328_768FS 768
|
||||
#define ES8328_512FS 512
|
||||
#define ES8328_384FS 384
|
||||
#define ES8328_256FS 256
|
||||
#define ES8328_128FS 128
|
||||
|
||||
#endif
|
@ -293,41 +293,13 @@ static int jz4740_codec_dev_probe(struct snd_soc_codec *codec)
|
||||
regmap_update_bits(jz4740_codec->regmap, JZ4740_REG_CODEC_1,
|
||||
JZ4740_CODEC_1_SW2_ENABLE, JZ4740_CODEC_1_SW2_ENABLE);
|
||||
|
||||
jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jz4740_codec_dev_remove(struct snd_soc_codec *codec)
|
||||
{
|
||||
jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
static int jz4740_codec_suspend(struct snd_soc_codec *codec)
|
||||
{
|
||||
return jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
}
|
||||
|
||||
static int jz4740_codec_resume(struct snd_soc_codec *codec)
|
||||
{
|
||||
return jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
}
|
||||
|
||||
#else
|
||||
#define jz4740_codec_suspend NULL
|
||||
#define jz4740_codec_resume NULL
|
||||
#endif
|
||||
|
||||
static struct snd_soc_codec_driver soc_codec_dev_jz4740_codec = {
|
||||
.probe = jz4740_codec_dev_probe,
|
||||
.remove = jz4740_codec_dev_remove,
|
||||
.suspend = jz4740_codec_suspend,
|
||||
.resume = jz4740_codec_resume,
|
||||
.set_bias_level = jz4740_codec_set_bias_level,
|
||||
.suspend_bias_off = true,
|
||||
|
||||
.controls = jz4740_codec_controls,
|
||||
.num_controls = ARRAY_SIZE(jz4740_codec_controls),
|
||||
|
@ -1395,18 +1395,6 @@ static struct snd_soc_dai_driver lm49453_dai[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static int lm49453_suspend(struct snd_soc_codec *codec)
|
||||
{
|
||||
lm49453_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lm49453_resume(struct snd_soc_codec *codec)
|
||||
{
|
||||
lm49453_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* power down chip */
|
||||
static int lm49453_remove(struct snd_soc_codec *codec)
|
||||
{
|
||||
@ -1416,8 +1404,6 @@ static int lm49453_remove(struct snd_soc_codec *codec)
|
||||
|
||||
static struct snd_soc_codec_driver soc_codec_dev_lm49453 = {
|
||||
.remove = lm49453_remove,
|
||||
.suspend = lm49453_suspend,
|
||||
.resume = lm49453_resume,
|
||||
.set_bias_level = lm49453_set_bias_level,
|
||||
.controls = lm49453_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(lm49453_snd_controls),
|
||||
|
@ -1311,8 +1311,6 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
|
||||
{"MIC1 Input", NULL, "MIC1"},
|
||||
{"MIC2 Input", NULL, "MIC2"},
|
||||
|
||||
{"DMICL", NULL, "DMICL_ENA"},
|
||||
{"DMICR", NULL, "DMICR_ENA"},
|
||||
{"DMICL", NULL, "AHPF"},
|
||||
{"DMICR", NULL, "AHPF"},
|
||||
|
||||
@ -1370,6 +1368,8 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
|
||||
{"DMIC Mux", "ADC", "ADCR"},
|
||||
{"DMIC Mux", "DMIC", "DMICL"},
|
||||
{"DMIC Mux", "DMIC", "DMICR"},
|
||||
{"DMIC Mux", "DMIC", "DMICL_ENA"},
|
||||
{"DMIC Mux", "DMIC", "DMICR_ENA"},
|
||||
|
||||
{"LBENL Mux", "Normal", "DMIC Mux"},
|
||||
{"LBENL Mux", "Loopback", "LTENL Mux"},
|
||||
@ -2159,12 +2159,16 @@ static void max98090_jack_work(struct work_struct *work)
|
||||
|
||||
static irqreturn_t max98090_interrupt(int irq, void *data)
|
||||
{
|
||||
struct snd_soc_codec *codec = data;
|
||||
struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
|
||||
struct max98090_priv *max98090 = data;
|
||||
struct snd_soc_codec *codec = max98090->codec;
|
||||
int ret;
|
||||
unsigned int mask;
|
||||
unsigned int active;
|
||||
|
||||
/* Treat interrupt before codec is initialized as spurious */
|
||||
if (codec == NULL)
|
||||
return IRQ_NONE;
|
||||
|
||||
dev_dbg(codec->dev, "***** max98090_interrupt *****\n");
|
||||
|
||||
ret = regmap_read(max98090->regmap, M98090_REG_INTERRUPT_S, &mask);
|
||||
@ -2329,7 +2333,6 @@ static int max98090_probe(struct snd_soc_codec *codec)
|
||||
max98090->lin_state = 0;
|
||||
max98090->pa1en = 0;
|
||||
max98090->pa2en = 0;
|
||||
max98090->extmic_mux = 0;
|
||||
|
||||
ret = snd_soc_read(codec, M98090_REG_REVISION_ID);
|
||||
if (ret < 0) {
|
||||
@ -2367,17 +2370,6 @@ static int max98090_probe(struct snd_soc_codec *codec)
|
||||
snd_soc_write(codec, M98090_REG_JACK_DETECT,
|
||||
M98090_JDETEN_MASK | M98090_JDEB_25MS);
|
||||
|
||||
/* Register for interrupts */
|
||||
dev_dbg(codec->dev, "irq = %d\n", max98090->irq);
|
||||
|
||||
ret = devm_request_threaded_irq(codec->dev, max98090->irq, NULL,
|
||||
max98090_interrupt, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
"max98090_interrupt", codec);
|
||||
if (ret < 0) {
|
||||
dev_err(codec->dev, "request_irq failed: %d\n",
|
||||
ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear any old interrupts.
|
||||
* An old interrupt ocurring prior to installing the ISR
|
||||
@ -2417,6 +2409,7 @@ static int max98090_remove(struct snd_soc_codec *codec)
|
||||
cancel_delayed_work_sync(&max98090->pll_det_enable_work);
|
||||
cancel_work_sync(&max98090->pll_det_disable_work);
|
||||
cancel_work_sync(&max98090->pll_work);
|
||||
max98090->codec = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2469,7 +2462,6 @@ static int max98090_i2c_probe(struct i2c_client *i2c,
|
||||
max98090->devtype = driver_data;
|
||||
i2c_set_clientdata(i2c, max98090);
|
||||
max98090->pdata = i2c->dev.platform_data;
|
||||
max98090->irq = i2c->irq;
|
||||
|
||||
max98090->regmap = devm_regmap_init_i2c(i2c, &max98090_regmap);
|
||||
if (IS_ERR(max98090->regmap)) {
|
||||
@ -2478,6 +2470,15 @@ static int max98090_i2c_probe(struct i2c_client *i2c,
|
||||
goto err_enable;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL,
|
||||
max98090_interrupt, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
"max98090_interrupt", max98090);
|
||||
if (ret < 0) {
|
||||
dev_err(&i2c->dev, "request_irq failed: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = snd_soc_register_codec(&i2c->dev,
|
||||
&soc_codec_dev_max98090, max98090_dai,
|
||||
ARRAY_SIZE(max98090_dai));
|
||||
|
@ -11,11 +11,6 @@
|
||||
#ifndef _MAX98090_H
|
||||
#define _MAX98090_H
|
||||
|
||||
#include <linux/version.h>
|
||||
|
||||
/* One can override the Linux version here with an explicit version number */
|
||||
#define M98090_LINUX_VERSION LINUX_VERSION_CODE
|
||||
|
||||
/*
|
||||
* MAX98090 Register Definitions
|
||||
*/
|
||||
@ -1502,9 +1497,6 @@
|
||||
#define M98090_REVID_WIDTH 8
|
||||
#define M98090_REVID_NUM (1<<M98090_REVID_WIDTH)
|
||||
|
||||
#define M98090_BYTE1(w) ((w >> 8) & 0xff)
|
||||
#define M98090_BYTE0(w) (w & 0xff)
|
||||
|
||||
/* Silicon revision number */
|
||||
#define M98090_REVA 0x40
|
||||
#define M98091_REVA 0x50
|
||||
@ -1529,7 +1521,6 @@ struct max98090_priv {
|
||||
unsigned int bclk;
|
||||
unsigned int lrclk;
|
||||
struct max98090_cdata dai[1];
|
||||
int irq;
|
||||
int jack_state;
|
||||
struct delayed_work jack_work;
|
||||
struct delayed_work pll_det_enable_work;
|
||||
@ -1542,7 +1533,6 @@ struct max98090_priv {
|
||||
u8 lin_state;
|
||||
unsigned int pa1en;
|
||||
unsigned int pa2en;
|
||||
unsigned int extmic_mux;
|
||||
unsigned int sidetone;
|
||||
bool master;
|
||||
};
|
||||
|
@ -565,41 +565,19 @@ static struct snd_soc_dai_driver ml26124_dai = {
|
||||
.symmetric_rates = 1,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int ml26124_suspend(struct snd_soc_codec *codec)
|
||||
{
|
||||
ml26124_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ml26124_resume(struct snd_soc_codec *codec)
|
||||
{
|
||||
ml26124_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define ml26124_suspend NULL
|
||||
#define ml26124_resume NULL
|
||||
#endif
|
||||
|
||||
static int ml26124_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
/* Software Reset */
|
||||
snd_soc_update_bits(codec, ML26124_SW_RST, 0x01, 1);
|
||||
snd_soc_update_bits(codec, ML26124_SW_RST, 0x01, 0);
|
||||
|
||||
ml26124_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_codec_driver soc_codec_dev_ml26124 = {
|
||||
.probe = ml26124_probe,
|
||||
.suspend = ml26124_suspend,
|
||||
.resume = ml26124_resume,
|
||||
.set_bias_level = ml26124_set_bias_level,
|
||||
.suspend_bias_off = true,
|
||||
.dapm_widgets = ml26124_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(ml26124_dapm_widgets),
|
||||
.dapm_routes = ml26124_intercon,
|
||||
|
@ -269,6 +269,7 @@ static int rt286_hw_read(void *context, unsigned int reg, unsigned int *value)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static void rt286_index_sync(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
|
||||
@ -279,6 +280,7 @@ static void rt286_index_sync(struct snd_soc_codec *codec)
|
||||
rt286->index_cache[i].def);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static int rt286_support_power_controls[] = {
|
||||
RT286_DAC_OUT1,
|
||||
|
@ -1906,6 +1906,32 @@ static int rt5640_set_bias_level(struct snd_soc_codec *codec,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rt5640_dmic_enable(struct snd_soc_codec *codec,
|
||||
bool dmic1_data_pin, bool dmic2_data_pin)
|
||||
{
|
||||
struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
regmap_update_bits(rt5640->regmap, RT5640_GPIO_CTRL1,
|
||||
RT5640_GP2_PIN_MASK, RT5640_GP2_PIN_DMIC1_SCL);
|
||||
|
||||
if (dmic1_data_pin) {
|
||||
regmap_update_bits(rt5640->regmap, RT5640_DMIC,
|
||||
RT5640_DMIC_1_DP_MASK, RT5640_DMIC_1_DP_GPIO3);
|
||||
regmap_update_bits(rt5640->regmap, RT5640_GPIO_CTRL1,
|
||||
RT5640_GP3_PIN_MASK, RT5640_GP3_PIN_DMIC1_SDA);
|
||||
}
|
||||
|
||||
if (dmic2_data_pin) {
|
||||
regmap_update_bits(rt5640->regmap, RT5640_DMIC,
|
||||
RT5640_DMIC_2_DP_MASK, RT5640_DMIC_2_DP_GPIO4);
|
||||
regmap_update_bits(rt5640->regmap, RT5640_GPIO_CTRL1,
|
||||
RT5640_GP4_PIN_MASK, RT5640_GP4_PIN_DMIC2_SDA);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rt5640_dmic_enable);
|
||||
|
||||
static int rt5640_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
|
||||
@ -1945,6 +1971,10 @@ static int rt5640_probe(struct snd_soc_codec *codec)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (rt5640->pdata.dmic_en)
|
||||
rt5640_dmic_enable(codec, rt5640->pdata.dmic1_data_pin,
|
||||
rt5640->pdata.dmic2_data_pin);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2195,25 +2225,6 @@ static int rt5640_i2c_probe(struct i2c_client *i2c,
|
||||
regmap_update_bits(rt5640->regmap, RT5640_IN3_IN4,
|
||||
RT5640_IN_DF2, RT5640_IN_DF2);
|
||||
|
||||
if (rt5640->pdata.dmic_en) {
|
||||
regmap_update_bits(rt5640->regmap, RT5640_GPIO_CTRL1,
|
||||
RT5640_GP2_PIN_MASK, RT5640_GP2_PIN_DMIC1_SCL);
|
||||
|
||||
if (rt5640->pdata.dmic1_data_pin) {
|
||||
regmap_update_bits(rt5640->regmap, RT5640_DMIC,
|
||||
RT5640_DMIC_1_DP_MASK, RT5640_DMIC_1_DP_GPIO3);
|
||||
regmap_update_bits(rt5640->regmap, RT5640_GPIO_CTRL1,
|
||||
RT5640_GP3_PIN_MASK, RT5640_GP3_PIN_DMIC1_SDA);
|
||||
}
|
||||
|
||||
if (rt5640->pdata.dmic2_data_pin) {
|
||||
regmap_update_bits(rt5640->regmap, RT5640_DMIC,
|
||||
RT5640_DMIC_2_DP_MASK, RT5640_DMIC_2_DP_GPIO4);
|
||||
regmap_update_bits(rt5640->regmap, RT5640_GPIO_CTRL1,
|
||||
RT5640_GP4_PIN_MASK, RT5640_GP4_PIN_DMIC2_SDA);
|
||||
}
|
||||
}
|
||||
|
||||
rt5640->hp_mute = 1;
|
||||
|
||||
return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5640,
|
||||
|
@ -2097,4 +2097,7 @@ struct rt5640_priv {
|
||||
bool hp_mute;
|
||||
};
|
||||
|
||||
int rt5640_dmic_enable(struct snd_soc_codec *codec,
|
||||
bool dmic1_data_pin, bool dmic2_data_pin);
|
||||
|
||||
#endif
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
@ -2103,6 +2104,77 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt5645_jack_detect(struct snd_soc_codec *codec,
|
||||
struct snd_soc_jack *jack)
|
||||
{
|
||||
struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
|
||||
int gpio_state, jack_type = 0;
|
||||
unsigned int val;
|
||||
|
||||
gpio_state = gpio_get_value(rt5645->pdata.hp_det_gpio);
|
||||
|
||||
dev_dbg(codec->dev, "gpio = %d(%d)\n", rt5645->pdata.hp_det_gpio,
|
||||
gpio_state);
|
||||
|
||||
if ((rt5645->pdata.gpio_hp_det_active_high && gpio_state) ||
|
||||
(!rt5645->pdata.gpio_hp_det_active_high && !gpio_state)) {
|
||||
snd_soc_dapm_force_enable_pin(&codec->dapm, "micbias1");
|
||||
snd_soc_dapm_force_enable_pin(&codec->dapm, "micbias2");
|
||||
snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO2");
|
||||
snd_soc_dapm_force_enable_pin(&codec->dapm, "Mic Det Power");
|
||||
snd_soc_dapm_sync(&codec->dapm);
|
||||
|
||||
snd_soc_write(codec, RT5645_IN1_CTRL1, 0x0006);
|
||||
snd_soc_write(codec, RT5645_JD_CTRL3, 0x00b0);
|
||||
|
||||
snd_soc_update_bits(codec, RT5645_IN1_CTRL2,
|
||||
RT5645_CBJ_MN_JD, 0);
|
||||
snd_soc_update_bits(codec, RT5645_IN1_CTRL2,
|
||||
RT5645_CBJ_MN_JD, RT5645_CBJ_MN_JD);
|
||||
|
||||
msleep(400);
|
||||
val = snd_soc_read(codec, RT5645_IN1_CTRL3) & 0x7;
|
||||
dev_dbg(codec->dev, "val = %d\n", val);
|
||||
|
||||
if (val == 1 || val == 2)
|
||||
jack_type = SND_JACK_HEADSET;
|
||||
else
|
||||
jack_type = SND_JACK_HEADPHONE;
|
||||
|
||||
snd_soc_dapm_disable_pin(&codec->dapm, "micbias1");
|
||||
snd_soc_dapm_disable_pin(&codec->dapm, "micbias2");
|
||||
snd_soc_dapm_disable_pin(&codec->dapm, "LDO2");
|
||||
snd_soc_dapm_disable_pin(&codec->dapm, "Mic Det Power");
|
||||
snd_soc_dapm_sync(&codec->dapm);
|
||||
}
|
||||
|
||||
snd_soc_jack_report(rt5645->jack, jack_type, SND_JACK_HEADSET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rt5645_set_jack_detect(struct snd_soc_codec *codec,
|
||||
struct snd_soc_jack *jack)
|
||||
{
|
||||
struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
rt5645->jack = jack;
|
||||
|
||||
rt5645_jack_detect(codec, rt5645->jack);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rt5645_set_jack_detect);
|
||||
|
||||
static irqreturn_t rt5645_irq(int irq, void *data)
|
||||
{
|
||||
struct rt5645_priv *rt5645 = data;
|
||||
|
||||
rt5645_jack_detect(rt5645->codec, rt5645->jack);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int rt5645_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
|
||||
@ -2250,6 +2322,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
|
||||
if (rt5645 == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
rt5645->i2c = i2c;
|
||||
i2c_set_clientdata(i2c, rt5645);
|
||||
|
||||
if (pdata)
|
||||
@ -2345,12 +2418,38 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
|
||||
|
||||
}
|
||||
|
||||
if (rt5645->i2c->irq) {
|
||||
ret = request_threaded_irq(rt5645->i2c->irq, NULL, rt5645_irq,
|
||||
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
|
||||
| IRQF_ONESHOT, "rt5645", rt5645);
|
||||
if (ret)
|
||||
dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret);
|
||||
}
|
||||
|
||||
if (gpio_is_valid(rt5645->pdata.hp_det_gpio)) {
|
||||
ret = gpio_request(rt5645->pdata.hp_det_gpio, "rt5645");
|
||||
if (ret)
|
||||
dev_err(&i2c->dev, "Fail gpio_request hp_det_gpio\n");
|
||||
|
||||
ret = gpio_direction_input(rt5645->pdata.hp_det_gpio);
|
||||
if (ret)
|
||||
dev_err(&i2c->dev, "Fail gpio_direction hp_det_gpio\n");
|
||||
}
|
||||
|
||||
return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5645,
|
||||
rt5645_dai, ARRAY_SIZE(rt5645_dai));
|
||||
}
|
||||
|
||||
static int rt5645_i2c_remove(struct i2c_client *i2c)
|
||||
{
|
||||
struct rt5645_priv *rt5645 = i2c_get_clientdata(i2c);
|
||||
|
||||
if (i2c->irq)
|
||||
free_irq(i2c->irq, rt5645);
|
||||
|
||||
if (gpio_is_valid(rt5645->pdata.hp_det_gpio))
|
||||
gpio_free(rt5645->pdata.hp_det_gpio);
|
||||
|
||||
snd_soc_unregister_codec(&i2c->dev);
|
||||
|
||||
return 0;
|
||||
|
@ -2166,6 +2166,8 @@ struct rt5645_priv {
|
||||
struct snd_soc_codec *codec;
|
||||
struct rt5645_platform_data pdata;
|
||||
struct regmap *regmap;
|
||||
struct i2c_client *i2c;
|
||||
struct snd_soc_jack *jack;
|
||||
|
||||
int sysclk;
|
||||
int sysclk_src;
|
||||
@ -2178,4 +2180,7 @@ struct rt5645_priv {
|
||||
int pll_out;
|
||||
};
|
||||
|
||||
int rt5645_set_jack_detect(struct snd_soc_codec *codec,
|
||||
struct snd_soc_jack *jack);
|
||||
|
||||
#endif /* __RT5645_H__ */
|
||||
|
@ -15,10 +15,12 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
@ -540,6 +542,7 @@ static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(st_vol_tlv, -4650, 150, 0);
|
||||
|
||||
/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */
|
||||
static unsigned int bst_tlv[] = {
|
||||
@ -604,6 +607,10 @@ static const struct snd_kcontrol_new rt5677_snd_controls[] = {
|
||||
RT5677_MONO_ADC_L_VOL_SFT, RT5677_MONO_ADC_R_VOL_SFT, 127, 0,
|
||||
adc_vol_tlv),
|
||||
|
||||
/* Sidetone Control */
|
||||
SOC_SINGLE_TLV("Sidetone Volume", RT5677_SIDETONE_CTRL,
|
||||
RT5677_ST_VOL_SFT, 31, 0, st_vol_tlv),
|
||||
|
||||
/* ADC Boost Volume Control */
|
||||
SOC_DOUBLE_TLV("STO1 ADC Boost Volume", RT5677_STO1_2_ADC_BST,
|
||||
RT5677_STO1_ADC_L_BST_SFT, RT5677_STO1_ADC_R_BST_SFT, 3, 0,
|
||||
@ -1700,14 +1707,19 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
|
||||
|
||||
SND_SOC_DAPM_INPUT("Haptic Generator"),
|
||||
|
||||
SND_SOC_DAPM_PGA("DMIC1", RT5677_DMIC_CTRL1, RT5677_DMIC_1_EN_SFT, 0,
|
||||
NULL, 0),
|
||||
SND_SOC_DAPM_PGA("DMIC2", RT5677_DMIC_CTRL1, RT5677_DMIC_2_EN_SFT, 0,
|
||||
NULL, 0),
|
||||
SND_SOC_DAPM_PGA("DMIC3", RT5677_DMIC_CTRL1, RT5677_DMIC_3_EN_SFT, 0,
|
||||
NULL, 0),
|
||||
SND_SOC_DAPM_PGA("DMIC4", RT5677_DMIC_CTRL2, RT5677_DMIC_4_EN_SFT, 0,
|
||||
NULL, 0),
|
||||
SND_SOC_DAPM_PGA("DMIC1", SND_SOC_NOPM, 0, 0, NULL, 0),
|
||||
SND_SOC_DAPM_PGA("DMIC2", SND_SOC_NOPM, 0, 0, NULL, 0),
|
||||
SND_SOC_DAPM_PGA("DMIC3", SND_SOC_NOPM, 0, 0, NULL, 0),
|
||||
SND_SOC_DAPM_PGA("DMIC4", SND_SOC_NOPM, 0, 0, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("DMIC1 power", RT5677_DMIC_CTRL1,
|
||||
RT5677_DMIC_1_EN_SFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("DMIC2 power", RT5677_DMIC_CTRL1,
|
||||
RT5677_DMIC_2_EN_SFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("DMIC3 power", RT5677_DMIC_CTRL1,
|
||||
RT5677_DMIC_3_EN_SFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("DMIC4 power", RT5677_DMIC_CTRL2,
|
||||
RT5677_DMIC_4_EN_SFT, 0, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0,
|
||||
set_dmic_clk, SND_SOC_DAPM_PRE_PMU),
|
||||
@ -1987,6 +1999,9 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
|
||||
/* Sidetone Mux */
|
||||
SND_SOC_DAPM_MUX("Sidetone Mux", SND_SOC_NOPM, 0, 0,
|
||||
&rt5677_sidetone_mux),
|
||||
SND_SOC_DAPM_SUPPLY("Sidetone Power", RT5677_SIDETONE_CTRL,
|
||||
RT5677_ST_EN_SFT, 0, NULL, 0),
|
||||
|
||||
/* VAD Mux*/
|
||||
SND_SOC_DAPM_MUX("VAD ADC Mux", SND_SOC_NOPM, 0, 0,
|
||||
&rt5677_vad_src_mux),
|
||||
@ -2130,6 +2145,13 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
|
||||
{ "DMIC L4", NULL, "DMIC CLK" },
|
||||
{ "DMIC R4", NULL, "DMIC CLK" },
|
||||
|
||||
{ "DMIC L1", NULL, "DMIC1 power" },
|
||||
{ "DMIC R1", NULL, "DMIC1 power" },
|
||||
{ "DMIC L3", NULL, "DMIC3 power" },
|
||||
{ "DMIC R3", NULL, "DMIC3 power" },
|
||||
{ "DMIC L4", NULL, "DMIC4 power" },
|
||||
{ "DMIC R4", NULL, "DMIC4 power" },
|
||||
|
||||
{ "BST1", NULL, "IN1P" },
|
||||
{ "BST1", NULL, "IN1N" },
|
||||
{ "BST2", NULL, "IN2P" },
|
||||
@ -2691,6 +2713,7 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
|
||||
{ "Sidetone Mux", "DMIC4 L", "DMIC L4" },
|
||||
{ "Sidetone Mux", "ADC1", "ADC 1" },
|
||||
{ "Sidetone Mux", "ADC2", "ADC 2" },
|
||||
{ "Sidetone Mux", NULL, "Sidetone Power" },
|
||||
|
||||
{ "Stereo DAC MIXL", "ST L Switch", "Sidetone Mux" },
|
||||
{ "Stereo DAC MIXL", "DAC1 L Switch", "DAC1 MIXL" },
|
||||
@ -2793,6 +2816,16 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
|
||||
{ "PDM2R", NULL, "PDM2 R Mux" },
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route rt5677_dmic2_clk_1[] = {
|
||||
{ "DMIC L2", NULL, "DMIC1 power" },
|
||||
{ "DMIC R2", NULL, "DMIC1 power" },
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route rt5677_dmic2_clk_2[] = {
|
||||
{ "DMIC L2", NULL, "DMIC2 power" },
|
||||
{ "DMIC R2", NULL, "DMIC2 power" },
|
||||
};
|
||||
|
||||
static int rt5677_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
|
||||
{
|
||||
@ -3084,6 +3117,59 @@ static int rt5677_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt5677_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
|
||||
unsigned int rx_mask, int slots, int slot_width)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
unsigned int val = 0;
|
||||
|
||||
if (rx_mask || tx_mask)
|
||||
val |= (1 << 12);
|
||||
|
||||
switch (slots) {
|
||||
case 4:
|
||||
val |= (1 << 10);
|
||||
break;
|
||||
case 6:
|
||||
val |= (2 << 10);
|
||||
break;
|
||||
case 8:
|
||||
val |= (3 << 10);
|
||||
break;
|
||||
case 2:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (slot_width) {
|
||||
case 20:
|
||||
val |= (1 << 8);
|
||||
break;
|
||||
case 24:
|
||||
val |= (2 << 8);
|
||||
break;
|
||||
case 32:
|
||||
val |= (3 << 8);
|
||||
break;
|
||||
case 16:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (dai->id) {
|
||||
case RT5677_AIF1:
|
||||
snd_soc_update_bits(codec, RT5677_TDM1_CTRL1, 0x1f00, val);
|
||||
break;
|
||||
case RT5677_AIF2:
|
||||
snd_soc_update_bits(codec, RT5677_TDM2_CTRL1, 0x1f00, val);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt5677_set_bias_level(struct snd_soc_codec *codec,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
@ -3138,12 +3224,148 @@ static int rt5677_set_bias_level(struct snd_soc_codec *codec,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_GPIOLIB
|
||||
static inline struct rt5677_priv *gpio_to_rt5677(struct gpio_chip *chip)
|
||||
{
|
||||
return container_of(chip, struct rt5677_priv, gpio_chip);
|
||||
}
|
||||
|
||||
static void rt5677_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
|
||||
{
|
||||
struct rt5677_priv *rt5677 = gpio_to_rt5677(chip);
|
||||
|
||||
switch (offset) {
|
||||
case RT5677_GPIO1 ... RT5677_GPIO5:
|
||||
regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL2,
|
||||
0x1 << (offset * 3 + 1), !!value << (offset * 3 + 1));
|
||||
break;
|
||||
|
||||
case RT5677_GPIO6:
|
||||
regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL3,
|
||||
RT5677_GPIO6_OUT_MASK, !!value << RT5677_GPIO6_OUT_SFT);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int rt5677_gpio_direction_out(struct gpio_chip *chip,
|
||||
unsigned offset, int value)
|
||||
{
|
||||
struct rt5677_priv *rt5677 = gpio_to_rt5677(chip);
|
||||
|
||||
switch (offset) {
|
||||
case RT5677_GPIO1 ... RT5677_GPIO5:
|
||||
regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL2,
|
||||
0x3 << (offset * 3 + 1),
|
||||
(0x2 | !!value) << (offset * 3 + 1));
|
||||
break;
|
||||
|
||||
case RT5677_GPIO6:
|
||||
regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL3,
|
||||
RT5677_GPIO6_DIR_MASK | RT5677_GPIO6_OUT_MASK,
|
||||
RT5677_GPIO6_DIR_OUT | !!value << RT5677_GPIO6_OUT_SFT);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt5677_gpio_get(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct rt5677_priv *rt5677 = gpio_to_rt5677(chip);
|
||||
int value, ret;
|
||||
|
||||
ret = regmap_read(rt5677->regmap, RT5677_GPIO_ST, &value);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return (value & (0x1 << offset)) >> offset;
|
||||
}
|
||||
|
||||
static int rt5677_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct rt5677_priv *rt5677 = gpio_to_rt5677(chip);
|
||||
|
||||
switch (offset) {
|
||||
case RT5677_GPIO1 ... RT5677_GPIO5:
|
||||
regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL2,
|
||||
0x1 << (offset * 3 + 2), 0x0);
|
||||
break;
|
||||
|
||||
case RT5677_GPIO6:
|
||||
regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL3,
|
||||
RT5677_GPIO6_DIR_MASK, RT5677_GPIO6_DIR_IN);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct gpio_chip rt5677_template_chip = {
|
||||
.label = "rt5677",
|
||||
.owner = THIS_MODULE,
|
||||
.direction_output = rt5677_gpio_direction_out,
|
||||
.set = rt5677_gpio_set,
|
||||
.direction_input = rt5677_gpio_direction_in,
|
||||
.get = rt5677_gpio_get,
|
||||
.can_sleep = 1,
|
||||
};
|
||||
|
||||
static void rt5677_init_gpio(struct i2c_client *i2c)
|
||||
{
|
||||
struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c);
|
||||
int ret;
|
||||
|
||||
rt5677->gpio_chip = rt5677_template_chip;
|
||||
rt5677->gpio_chip.ngpio = RT5677_GPIO_NUM;
|
||||
rt5677->gpio_chip.dev = &i2c->dev;
|
||||
rt5677->gpio_chip.base = -1;
|
||||
|
||||
ret = gpiochip_add(&rt5677->gpio_chip);
|
||||
if (ret != 0)
|
||||
dev_err(&i2c->dev, "Failed to add GPIOs: %d\n", ret);
|
||||
}
|
||||
|
||||
static void rt5677_free_gpio(struct i2c_client *i2c)
|
||||
{
|
||||
struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c);
|
||||
|
||||
gpiochip_remove(&rt5677->gpio_chip);
|
||||
}
|
||||
#else
|
||||
static void rt5677_init_gpio(struct i2c_client *i2c)
|
||||
{
|
||||
}
|
||||
|
||||
static void rt5677_free_gpio(struct i2c_client *i2c)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static int rt5677_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
rt5677->codec = codec;
|
||||
|
||||
if (rt5677->pdata.dmic2_clk_pin == RT5677_DMIC_CLK2) {
|
||||
snd_soc_dapm_add_routes(&codec->dapm,
|
||||
rt5677_dmic2_clk_2,
|
||||
ARRAY_SIZE(rt5677_dmic2_clk_2));
|
||||
} else { /*use dmic1 clock by default*/
|
||||
snd_soc_dapm_add_routes(&codec->dapm,
|
||||
rt5677_dmic2_clk_1,
|
||||
ARRAY_SIZE(rt5677_dmic2_clk_1));
|
||||
}
|
||||
|
||||
rt5677_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
|
||||
regmap_write(rt5677->regmap, RT5677_DIG_MISC, 0x0020);
|
||||
@ -3157,6 +3379,8 @@ static int rt5677_remove(struct snd_soc_codec *codec)
|
||||
struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec);
|
||||
if (gpio_is_valid(rt5677->pow_ldo2))
|
||||
gpio_set_value_cansleep(rt5677->pow_ldo2, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -3168,6 +3392,8 @@ static int rt5677_suspend(struct snd_soc_codec *codec)
|
||||
|
||||
regcache_cache_only(rt5677->regmap, true);
|
||||
regcache_mark_dirty(rt5677->regmap);
|
||||
if (gpio_is_valid(rt5677->pow_ldo2))
|
||||
gpio_set_value_cansleep(rt5677->pow_ldo2, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -3176,6 +3402,10 @@ static int rt5677_resume(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
if (gpio_is_valid(rt5677->pow_ldo2)) {
|
||||
gpio_set_value_cansleep(rt5677->pow_ldo2, 1);
|
||||
msleep(10);
|
||||
}
|
||||
regcache_cache_only(rt5677->regmap, false);
|
||||
regcache_sync(rt5677->regmap);
|
||||
|
||||
@ -3195,6 +3425,7 @@ static struct snd_soc_dai_ops rt5677_aif_dai_ops = {
|
||||
.set_fmt = rt5677_set_dai_fmt,
|
||||
.set_sysclk = rt5677_set_dai_sysclk,
|
||||
.set_pll = rt5677_set_dai_pll,
|
||||
.set_tdm_slot = rt5677_set_tdm_slot,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver rt5677_dai[] = {
|
||||
@ -3333,6 +3564,35 @@ static const struct i2c_device_id rt5677_i2c_id[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, rt5677_i2c_id);
|
||||
|
||||
static int rt5677_parse_dt(struct rt5677_priv *rt5677, struct device_node *np)
|
||||
{
|
||||
rt5677->pdata.in1_diff = of_property_read_bool(np,
|
||||
"realtek,in1-differential");
|
||||
rt5677->pdata.in2_diff = of_property_read_bool(np,
|
||||
"realtek,in2-differential");
|
||||
rt5677->pdata.lout1_diff = of_property_read_bool(np,
|
||||
"realtek,lout1-differential");
|
||||
rt5677->pdata.lout2_diff = of_property_read_bool(np,
|
||||
"realtek,lout2-differential");
|
||||
rt5677->pdata.lout3_diff = of_property_read_bool(np,
|
||||
"realtek,lout3-differential");
|
||||
|
||||
rt5677->pow_ldo2 = of_get_named_gpio(np,
|
||||
"realtek,pow-ldo2-gpio", 0);
|
||||
|
||||
/*
|
||||
* POW_LDO2 is optional (it may be statically tied on the board).
|
||||
* -ENOENT means that the property doesn't exist, i.e. there is no
|
||||
* GPIO, so is not an error. Any other error code means the property
|
||||
* exists, but could not be parsed.
|
||||
*/
|
||||
if (!gpio_is_valid(rt5677->pow_ldo2) &&
|
||||
(rt5677->pow_ldo2 != -ENOENT))
|
||||
return rt5677->pow_ldo2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt5677_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
@ -3351,6 +3611,33 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
|
||||
if (pdata)
|
||||
rt5677->pdata = *pdata;
|
||||
|
||||
if (i2c->dev.of_node) {
|
||||
ret = rt5677_parse_dt(rt5677, i2c->dev.of_node);
|
||||
if (ret) {
|
||||
dev_err(&i2c->dev, "Failed to parse device tree: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
rt5677->pow_ldo2 = -EINVAL;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(rt5677->pow_ldo2)) {
|
||||
ret = devm_gpio_request_one(&i2c->dev, rt5677->pow_ldo2,
|
||||
GPIOF_OUT_INIT_HIGH,
|
||||
"RT5677 POW_LDO2");
|
||||
if (ret < 0) {
|
||||
dev_err(&i2c->dev, "Failed to request POW_LDO2 %d: %d\n",
|
||||
rt5677->pow_ldo2, ret);
|
||||
return ret;
|
||||
}
|
||||
/* Wait a while until I2C bus becomes available. The datasheet
|
||||
* does not specify the exact we should wait but startup
|
||||
* sequence mentiones at least a few milliseconds.
|
||||
*/
|
||||
msleep(10);
|
||||
}
|
||||
|
||||
rt5677->regmap = devm_regmap_init_i2c(i2c, &rt5677_regmap);
|
||||
if (IS_ERR(rt5677->regmap)) {
|
||||
ret = PTR_ERR(rt5677->regmap);
|
||||
@ -3381,6 +3668,29 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
|
||||
regmap_update_bits(rt5677->regmap, RT5677_IN1,
|
||||
RT5677_IN_DF2, RT5677_IN_DF2);
|
||||
|
||||
if (rt5677->pdata.lout1_diff)
|
||||
regmap_update_bits(rt5677->regmap, RT5677_LOUT1,
|
||||
RT5677_LOUT1_L_DF, RT5677_LOUT1_L_DF);
|
||||
|
||||
if (rt5677->pdata.lout2_diff)
|
||||
regmap_update_bits(rt5677->regmap, RT5677_LOUT1,
|
||||
RT5677_LOUT2_L_DF, RT5677_LOUT2_L_DF);
|
||||
|
||||
if (rt5677->pdata.lout3_diff)
|
||||
regmap_update_bits(rt5677->regmap, RT5677_LOUT1,
|
||||
RT5677_LOUT3_L_DF, RT5677_LOUT3_L_DF);
|
||||
|
||||
if (rt5677->pdata.dmic2_clk_pin == RT5677_DMIC_CLK2) {
|
||||
regmap_update_bits(rt5677->regmap, RT5677_GEN_CTRL2,
|
||||
RT5677_GPIO5_FUNC_MASK,
|
||||
RT5677_GPIO5_FUNC_DMIC);
|
||||
regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL2,
|
||||
RT5677_GPIO5_DIR_MASK,
|
||||
RT5677_GPIO5_DIR_OUT);
|
||||
}
|
||||
|
||||
rt5677_init_gpio(i2c);
|
||||
|
||||
return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5677,
|
||||
rt5677_dai, ARRAY_SIZE(rt5677_dai));
|
||||
}
|
||||
@ -3388,6 +3698,7 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
|
||||
static int rt5677_i2c_remove(struct i2c_client *i2c)
|
||||
{
|
||||
snd_soc_unregister_codec(&i2c->dev);
|
||||
rt5677_free_gpio(i2c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -382,6 +382,10 @@
|
||||
#define RT5677_ST_SEL_SFT 9
|
||||
#define RT5677_ST_EN (0x1 << 6)
|
||||
#define RT5677_ST_EN_SFT 6
|
||||
#define RT5677_ST_GAIN (0x1 << 5)
|
||||
#define RT5677_ST_GAIN_SFT 5
|
||||
#define RT5677_ST_VOL_MASK (0x1f << 0)
|
||||
#define RT5677_ST_VOL_SFT 0
|
||||
|
||||
/* Analog DAC1/2/3 Source Control (0x15) */
|
||||
#define RT5677_ANA_DAC3_SRC_SEL_MASK (0x3 << 4)
|
||||
@ -1287,16 +1291,16 @@
|
||||
#define RT5677_PLL1_PD_SFT 8
|
||||
#define RT5677_PLL1_PD_1 (0x0 << 8)
|
||||
#define RT5677_PLL1_PD_2 (0x1 << 8)
|
||||
#define RT5671_DAC_OSR_MASK (0x3 << 6)
|
||||
#define RT5671_DAC_OSR_SFT 6
|
||||
#define RT5671_DAC_OSR_128 (0x0 << 6)
|
||||
#define RT5671_DAC_OSR_64 (0x1 << 6)
|
||||
#define RT5671_DAC_OSR_32 (0x2 << 6)
|
||||
#define RT5671_ADC_OSR_MASK (0x3 << 4)
|
||||
#define RT5671_ADC_OSR_SFT 4
|
||||
#define RT5671_ADC_OSR_128 (0x0 << 4)
|
||||
#define RT5671_ADC_OSR_64 (0x1 << 4)
|
||||
#define RT5671_ADC_OSR_32 (0x2 << 4)
|
||||
#define RT5677_DAC_OSR_MASK (0x3 << 6)
|
||||
#define RT5677_DAC_OSR_SFT 6
|
||||
#define RT5677_DAC_OSR_128 (0x0 << 6)
|
||||
#define RT5677_DAC_OSR_64 (0x1 << 6)
|
||||
#define RT5677_DAC_OSR_32 (0x2 << 6)
|
||||
#define RT5677_ADC_OSR_MASK (0x3 << 4)
|
||||
#define RT5677_ADC_OSR_SFT 4
|
||||
#define RT5677_ADC_OSR_128 (0x0 << 4)
|
||||
#define RT5677_ADC_OSR_64 (0x1 << 4)
|
||||
#define RT5677_ADC_OSR_32 (0x2 << 4)
|
||||
|
||||
/* Global Clock Control 2 (0x81) */
|
||||
#define RT5677_PLL2_PR_SRC_MASK (0x1 << 15)
|
||||
@ -1312,18 +1316,18 @@
|
||||
#define RT5677_PLL2_SRC_BCLK4 (0x4 << 12)
|
||||
#define RT5677_PLL2_SRC_RCCLK (0x5 << 12)
|
||||
#define RT5677_PLL2_SRC_SLIM (0x6 << 12)
|
||||
#define RT5671_DSP_ASRC_O_SRC (0x3 << 10)
|
||||
#define RT5671_DSP_ASRC_O_SRC_SFT 10
|
||||
#define RT5671_DSP_ASRC_O_MCLK (0x0 << 10)
|
||||
#define RT5671_DSP_ASRC_O_PLL1 (0x1 << 10)
|
||||
#define RT5671_DSP_ASRC_O_SLIM (0x2 << 10)
|
||||
#define RT5671_DSP_ASRC_O_RCCLK (0x3 << 10)
|
||||
#define RT5671_DSP_ASRC_I_SRC (0x3 << 8)
|
||||
#define RT5671_DSP_ASRC_I_SRC_SFT 8
|
||||
#define RT5671_DSP_ASRC_I_MCLK (0x0 << 8)
|
||||
#define RT5671_DSP_ASRC_I_PLL1 (0x1 << 8)
|
||||
#define RT5671_DSP_ASRC_I_SLIM (0x2 << 8)
|
||||
#define RT5671_DSP_ASRC_I_RCCLK (0x3 << 8)
|
||||
#define RT5677_DSP_ASRC_O_SRC (0x3 << 10)
|
||||
#define RT5677_DSP_ASRC_O_SRC_SFT 10
|
||||
#define RT5677_DSP_ASRC_O_MCLK (0x0 << 10)
|
||||
#define RT5677_DSP_ASRC_O_PLL1 (0x1 << 10)
|
||||
#define RT5677_DSP_ASRC_O_SLIM (0x2 << 10)
|
||||
#define RT5677_DSP_ASRC_O_RCCLK (0x3 << 10)
|
||||
#define RT5677_DSP_ASRC_I_SRC (0x3 << 8)
|
||||
#define RT5677_DSP_ASRC_I_SRC_SFT 8
|
||||
#define RT5677_DSP_ASRC_I_MCLK (0x0 << 8)
|
||||
#define RT5677_DSP_ASRC_I_PLL1 (0x1 << 8)
|
||||
#define RT5677_DSP_ASRC_I_SLIM (0x2 << 8)
|
||||
#define RT5677_DSP_ASRC_I_RCCLK (0x3 << 8)
|
||||
#define RT5677_DSP_CLK_SRC_MASK (0x1 << 7)
|
||||
#define RT5677_DSP_CLK_SRC_SFT 7
|
||||
#define RT5677_DSP_CLK_SRC_PLL2 (0x0 << 7)
|
||||
@ -1363,6 +1367,110 @@
|
||||
#define RT5677_SEL_SRC_IB01 (0x1 << 0)
|
||||
#define RT5677_SEL_SRC_IB01_SFT 0
|
||||
|
||||
/* GPIO status (0xbf) */
|
||||
#define RT5677_GPIO6_STATUS_MASK (0x1 << 5)
|
||||
#define RT5677_GPIO6_STATUS_SFT 5
|
||||
#define RT5677_GPIO5_STATUS_MASK (0x1 << 4)
|
||||
#define RT5677_GPIO5_STATUS_SFT 4
|
||||
#define RT5677_GPIO4_STATUS_MASK (0x1 << 3)
|
||||
#define RT5677_GPIO4_STATUS_SFT 3
|
||||
#define RT5677_GPIO3_STATUS_MASK (0x1 << 2)
|
||||
#define RT5677_GPIO3_STATUS_SFT 2
|
||||
#define RT5677_GPIO2_STATUS_MASK (0x1 << 1)
|
||||
#define RT5677_GPIO2_STATUS_SFT 1
|
||||
#define RT5677_GPIO1_STATUS_MASK (0x1 << 0)
|
||||
#define RT5677_GPIO1_STATUS_SFT 0
|
||||
|
||||
/* GPIO Control 1 (0xc0) */
|
||||
#define RT5677_GPIO1_PIN_MASK (0x1 << 15)
|
||||
#define RT5677_GPIO1_PIN_SFT 15
|
||||
#define RT5677_GPIO1_PIN_GPIO1 (0x0 << 15)
|
||||
#define RT5677_GPIO1_PIN_IRQ (0x1 << 15)
|
||||
#define RT5677_IPTV_MODE_MASK (0x1 << 14)
|
||||
#define RT5677_IPTV_MODE_SFT 14
|
||||
#define RT5677_IPTV_MODE_GPIO (0x0 << 14)
|
||||
#define RT5677_IPTV_MODE_IPTV (0x1 << 14)
|
||||
#define RT5677_FUNC_MODE_MASK (0x1 << 13)
|
||||
#define RT5677_FUNC_MODE_SFT 13
|
||||
#define RT5677_FUNC_MODE_DMIC_GPIO (0x0 << 13)
|
||||
#define RT5677_FUNC_MODE_JTAG (0x1 << 13)
|
||||
|
||||
/* GPIO Control 2 (0xc1) */
|
||||
#define RT5677_GPIO5_DIR_MASK (0x1 << 14)
|
||||
#define RT5677_GPIO5_DIR_SFT 14
|
||||
#define RT5677_GPIO5_DIR_IN (0x0 << 14)
|
||||
#define RT5677_GPIO5_DIR_OUT (0x1 << 14)
|
||||
#define RT5677_GPIO5_OUT_MASK (0x1 << 13)
|
||||
#define RT5677_GPIO5_OUT_SFT 13
|
||||
#define RT5677_GPIO5_OUT_LO (0x0 << 13)
|
||||
#define RT5677_GPIO5_OUT_HI (0x1 << 13)
|
||||
#define RT5677_GPIO5_P_MASK (0x1 << 12)
|
||||
#define RT5677_GPIO5_P_SFT 12
|
||||
#define RT5677_GPIO5_P_NOR (0x0 << 12)
|
||||
#define RT5677_GPIO5_P_INV (0x1 << 12)
|
||||
#define RT5677_GPIO4_DIR_MASK (0x1 << 11)
|
||||
#define RT5677_GPIO4_DIR_SFT 11
|
||||
#define RT5677_GPIO4_DIR_IN (0x0 << 11)
|
||||
#define RT5677_GPIO4_DIR_OUT (0x1 << 11)
|
||||
#define RT5677_GPIO4_OUT_MASK (0x1 << 10)
|
||||
#define RT5677_GPIO4_OUT_SFT 10
|
||||
#define RT5677_GPIO4_OUT_LO (0x0 << 10)
|
||||
#define RT5677_GPIO4_OUT_HI (0x1 << 10)
|
||||
#define RT5677_GPIO4_P_MASK (0x1 << 9)
|
||||
#define RT5677_GPIO4_P_SFT 9
|
||||
#define RT5677_GPIO4_P_NOR (0x0 << 9)
|
||||
#define RT5677_GPIO4_P_INV (0x1 << 9)
|
||||
#define RT5677_GPIO3_DIR_MASK (0x1 << 8)
|
||||
#define RT5677_GPIO3_DIR_SFT 8
|
||||
#define RT5677_GPIO3_DIR_IN (0x0 << 8)
|
||||
#define RT5677_GPIO3_DIR_OUT (0x1 << 8)
|
||||
#define RT5677_GPIO3_OUT_MASK (0x1 << 7)
|
||||
#define RT5677_GPIO3_OUT_SFT 7
|
||||
#define RT5677_GPIO3_OUT_LO (0x0 << 7)
|
||||
#define RT5677_GPIO3_OUT_HI (0x1 << 7)
|
||||
#define RT5677_GPIO3_P_MASK (0x1 << 6)
|
||||
#define RT5677_GPIO3_P_SFT 6
|
||||
#define RT5677_GPIO3_P_NOR (0x0 << 6)
|
||||
#define RT5677_GPIO3_P_INV (0x1 << 6)
|
||||
#define RT5677_GPIO2_DIR_MASK (0x1 << 5)
|
||||
#define RT5677_GPIO2_DIR_SFT 5
|
||||
#define RT5677_GPIO2_DIR_IN (0x0 << 5)
|
||||
#define RT5677_GPIO2_DIR_OUT (0x1 << 5)
|
||||
#define RT5677_GPIO2_OUT_MASK (0x1 << 4)
|
||||
#define RT5677_GPIO2_OUT_SFT 4
|
||||
#define RT5677_GPIO2_OUT_LO (0x0 << 4)
|
||||
#define RT5677_GPIO2_OUT_HI (0x1 << 4)
|
||||
#define RT5677_GPIO2_P_MASK (0x1 << 3)
|
||||
#define RT5677_GPIO2_P_SFT 3
|
||||
#define RT5677_GPIO2_P_NOR (0x0 << 3)
|
||||
#define RT5677_GPIO2_P_INV (0x1 << 3)
|
||||
#define RT5677_GPIO1_DIR_MASK (0x1 << 2)
|
||||
#define RT5677_GPIO1_DIR_SFT 2
|
||||
#define RT5677_GPIO1_DIR_IN (0x0 << 2)
|
||||
#define RT5677_GPIO1_DIR_OUT (0x1 << 2)
|
||||
#define RT5677_GPIO1_OUT_MASK (0x1 << 1)
|
||||
#define RT5677_GPIO1_OUT_SFT 1
|
||||
#define RT5677_GPIO1_OUT_LO (0x0 << 1)
|
||||
#define RT5677_GPIO1_OUT_HI (0x1 << 1)
|
||||
#define RT5677_GPIO1_P_MASK (0x1 << 0)
|
||||
#define RT5677_GPIO1_P_SFT 0
|
||||
#define RT5677_GPIO1_P_NOR (0x0 << 0)
|
||||
#define RT5677_GPIO1_P_INV (0x1 << 0)
|
||||
|
||||
/* GPIO Control 3 (0xc2) */
|
||||
#define RT5677_GPIO6_DIR_MASK (0x1 << 2)
|
||||
#define RT5677_GPIO6_DIR_SFT 2
|
||||
#define RT5677_GPIO6_DIR_IN (0x0 << 2)
|
||||
#define RT5677_GPIO6_DIR_OUT (0x1 << 2)
|
||||
#define RT5677_GPIO6_OUT_MASK (0x1 << 1)
|
||||
#define RT5677_GPIO6_OUT_SFT 1
|
||||
#define RT5677_GPIO6_OUT_LO (0x0 << 1)
|
||||
#define RT5677_GPIO6_OUT_HI (0x1 << 1)
|
||||
#define RT5677_GPIO6_P_MASK (0x1 << 0)
|
||||
#define RT5677_GPIO6_P_SFT 0
|
||||
#define RT5677_GPIO6_P_NOR (0x0 << 0)
|
||||
#define RT5677_GPIO6_P_INV (0x1 << 0)
|
||||
|
||||
/* Virtual DSP Mixer Control (0xf7 0xf8 0xf9) */
|
||||
#define RT5677_DSP_IB_01_H (0x1 << 15)
|
||||
#define RT5677_DSP_IB_01_H_SFT 15
|
||||
@ -1393,6 +1501,11 @@
|
||||
#define RT5677_DSP_IB_9_L (0x1 << 1)
|
||||
#define RT5677_DSP_IB_9_L_SFT 1
|
||||
|
||||
/* General Control2 (0xfc)*/
|
||||
#define RT5677_GPIO5_FUNC_MASK (0x1 << 9)
|
||||
#define RT5677_GPIO5_FUNC_GPIO (0x0 << 9)
|
||||
#define RT5677_GPIO5_FUNC_DMIC (0x1 << 9)
|
||||
|
||||
/* System Clock Source */
|
||||
enum {
|
||||
RT5677_SCLK_S_MCLK,
|
||||
@ -1418,6 +1531,16 @@ enum {
|
||||
RT5677_AIFS,
|
||||
};
|
||||
|
||||
enum {
|
||||
RT5677_GPIO1,
|
||||
RT5677_GPIO2,
|
||||
RT5677_GPIO3,
|
||||
RT5677_GPIO4,
|
||||
RT5677_GPIO5,
|
||||
RT5677_GPIO6,
|
||||
RT5677_GPIO_NUM,
|
||||
};
|
||||
|
||||
struct rt5677_priv {
|
||||
struct snd_soc_codec *codec;
|
||||
struct rt5677_platform_data pdata;
|
||||
@ -1431,6 +1554,10 @@ struct rt5677_priv {
|
||||
int pll_src;
|
||||
int pll_in;
|
||||
int pll_out;
|
||||
int pow_ldo2; /* POW_LDO2 pin */
|
||||
#ifdef CONFIG_GPIOLIB
|
||||
struct gpio_chip gpio_chip;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif /* __RT5677_H__ */
|
||||
|
@ -626,6 +626,9 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
|
||||
} else {
|
||||
dev_err(codec->dev,
|
||||
"PLL not supported in slave mode\n");
|
||||
dev_err(codec->dev, "%d ratio is not supported. "
|
||||
"SYS_MCLK needs to be 256, 384 or 512 * fs\n",
|
||||
sgtl5000->sysclk / sys_fs);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
@ -1073,26 +1076,6 @@ static bool sgtl5000_readable(struct device *dev, unsigned int reg)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SUSPEND
|
||||
static int sgtl5000_suspend(struct snd_soc_codec *codec)
|
||||
{
|
||||
sgtl5000_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sgtl5000_resume(struct snd_soc_codec *codec)
|
||||
{
|
||||
/* Bring the codec back up to standby to enable regulators */
|
||||
sgtl5000_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define sgtl5000_suspend NULL
|
||||
#define sgtl5000_resume NULL
|
||||
#endif /* CONFIG_SUSPEND */
|
||||
|
||||
/*
|
||||
* sgtl5000 has 3 internal power supplies:
|
||||
* 1. VAG, normally set to vdda/2
|
||||
@ -1352,11 +1335,6 @@ static int sgtl5000_probe(struct snd_soc_codec *codec)
|
||||
*/
|
||||
snd_soc_write(codec, SGTL5000_DAP_CTRL, 0);
|
||||
|
||||
/* leading to standby state */
|
||||
ret = sgtl5000_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
@ -1373,8 +1351,6 @@ static int sgtl5000_remove(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
sgtl5000_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
|
||||
regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
|
||||
sgtl5000->supplies);
|
||||
regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
|
||||
@ -1387,9 +1363,8 @@ static int sgtl5000_remove(struct snd_soc_codec *codec)
|
||||
static struct snd_soc_codec_driver sgtl5000_driver = {
|
||||
.probe = sgtl5000_probe,
|
||||
.remove = sgtl5000_remove,
|
||||
.suspend = sgtl5000_suspend,
|
||||
.resume = sgtl5000_resume,
|
||||
.set_bias_level = sgtl5000_set_bias_level,
|
||||
.suspend_bias_off = true,
|
||||
.controls = sgtl5000_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(sgtl5000_snd_controls),
|
||||
.dapm_widgets = sgtl5000_dapm_widgets,
|
||||
@ -1442,6 +1417,7 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
|
||||
{
|
||||
struct sgtl5000_priv *sgtl5000;
|
||||
int ret, reg, rev;
|
||||
unsigned int mclk;
|
||||
|
||||
sgtl5000 = devm_kzalloc(&client->dev, sizeof(struct sgtl5000_priv),
|
||||
GFP_KERNEL);
|
||||
@ -1465,6 +1441,14 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* SGTL5000 SYS_MCLK should be between 8 and 27 MHz */
|
||||
mclk = clk_get_rate(sgtl5000->mclk);
|
||||
if (mclk < 8000000 || mclk > 27000000) {
|
||||
dev_err(&client->dev, "Invalid SYS_CLK frequency: %u.%03uMHz\n",
|
||||
mclk / 1000000, mclk / 1000 % 1000);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(sgtl5000->mclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -646,17 +646,6 @@ static struct snd_soc_dai_driver ssm2518_dai = {
|
||||
.ops = &ssm2518_dai_ops,
|
||||
};
|
||||
|
||||
static int ssm2518_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
return ssm2518_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
}
|
||||
|
||||
static int ssm2518_remove(struct snd_soc_codec *codec)
|
||||
{
|
||||
ssm2518_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ssm2518_set_sysclk(struct snd_soc_codec *codec, int clk_id,
|
||||
int source, unsigned int freq, int dir)
|
||||
{
|
||||
@ -727,8 +716,6 @@ static int ssm2518_set_sysclk(struct snd_soc_codec *codec, int clk_id,
|
||||
}
|
||||
|
||||
static struct snd_soc_codec_driver ssm2518_codec_driver = {
|
||||
.probe = ssm2518_probe,
|
||||
.remove = ssm2518_remove,
|
||||
.set_bias_level = ssm2518_set_bias_level,
|
||||
.set_sysclk = ssm2518_set_sysclk,
|
||||
.idle_bias_off = true,
|
||||
|
@ -41,10 +41,19 @@ static const struct i2c_device_id ssm2602_i2c_id[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id);
|
||||
|
||||
static const struct of_device_id ssm2602_of_match[] = {
|
||||
{ .compatible = "adi,ssm2602", },
|
||||
{ .compatible = "adi,ssm2603", },
|
||||
{ .compatible = "adi,ssm2604", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ssm2602_of_match);
|
||||
|
||||
static struct i2c_driver ssm2602_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "ssm2602",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = ssm2602_of_match,
|
||||
},
|
||||
.probe = ssm2602_i2c_probe,
|
||||
.remove = ssm2602_i2c_remove,
|
||||
|
@ -26,10 +26,17 @@ static int ssm2602_spi_remove(struct spi_device *spi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ssm2602_of_match[] = {
|
||||
{ .compatible = "adi,ssm2602", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ssm2602_of_match);
|
||||
|
||||
static struct spi_driver ssm2602_spi_driver = {
|
||||
.driver = {
|
||||
.name = "ssm2602",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = ssm2602_of_match,
|
||||
},
|
||||
.probe = ssm2602_spi_probe,
|
||||
.remove = ssm2602_spi_remove,
|
||||
|
@ -192,7 +192,7 @@ static const struct snd_pcm_hw_constraint_list ssm2602_constraints_12288000 = {
|
||||
};
|
||||
|
||||
static const unsigned int ssm2602_rates_11289600[] = {
|
||||
8000, 44100, 88200,
|
||||
8000, 11025, 22050, 44100, 88200,
|
||||
};
|
||||
|
||||
static const struct snd_pcm_hw_constraint_list ssm2602_constraints_11289600 = {
|
||||
@ -237,6 +237,16 @@ static const struct ssm2602_coeff ssm2602_coeff_table[] = {
|
||||
{18432000, 96000, SSM2602_COEFF_SRATE(0x7, 0x1, 0x0)},
|
||||
{12000000, 96000, SSM2602_COEFF_SRATE(0x7, 0x0, 0x1)},
|
||||
|
||||
/* 11.025k */
|
||||
{11289600, 11025, SSM2602_COEFF_SRATE(0xc, 0x0, 0x0)},
|
||||
{16934400, 11025, SSM2602_COEFF_SRATE(0xc, 0x1, 0x0)},
|
||||
{12000000, 11025, SSM2602_COEFF_SRATE(0xc, 0x1, 0x1)},
|
||||
|
||||
/* 22.05k */
|
||||
{11289600, 22050, SSM2602_COEFF_SRATE(0xd, 0x0, 0x0)},
|
||||
{16934400, 22050, SSM2602_COEFF_SRATE(0xd, 0x1, 0x0)},
|
||||
{12000000, 22050, SSM2602_COEFF_SRATE(0xd, 0x1, 0x1)},
|
||||
|
||||
/* 44.1k */
|
||||
{11289600, 44100, SSM2602_COEFF_SRATE(0x8, 0x0, 0x0)},
|
||||
{16934400, 44100, SSM2602_COEFF_SRATE(0x8, 0x1, 0x0)},
|
||||
@ -467,7 +477,8 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define SSM2602_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
|
||||
#define SSM2602_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
|
||||
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
|
||||
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
|
||||
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
|
||||
SNDRV_PCM_RATE_96000)
|
||||
@ -502,18 +513,11 @@ static struct snd_soc_dai_driver ssm2602_dai = {
|
||||
.symmetric_samplebits = 1,
|
||||
};
|
||||
|
||||
static int ssm2602_suspend(struct snd_soc_codec *codec)
|
||||
{
|
||||
ssm2602_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ssm2602_resume(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
regcache_sync(ssm2602->regmap);
|
||||
ssm2602_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -586,27 +590,14 @@ static int ssm260x_codec_probe(struct snd_soc_codec *codec)
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ssm2602_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* remove everything here */
|
||||
static int ssm2602_remove(struct snd_soc_codec *codec)
|
||||
{
|
||||
ssm2602_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = {
|
||||
.probe = ssm260x_codec_probe,
|
||||
.remove = ssm2602_remove,
|
||||
.suspend = ssm2602_suspend,
|
||||
.resume = ssm2602_resume,
|
||||
.set_bias_level = ssm2602_set_bias_level,
|
||||
.suspend_bias_off = true,
|
||||
|
||||
.controls = ssm260x_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(ssm260x_snd_controls),
|
||||
|
343
sound/soc/codecs/ssm4567.c
Normal file
343
sound/soc/codecs/ssm4567.c
Normal file
@ -0,0 +1,343 @@
|
||||
/*
|
||||
* SSM4567 amplifier audio driver
|
||||
*
|
||||
* Copyright 2014 Google Chromium project.
|
||||
* Author: Anatol Pomozov <anatol@chromium.org>
|
||||
*
|
||||
* Based on code copyright/by:
|
||||
* Copyright 2013 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/tlv.h>
|
||||
|
||||
#define SSM4567_REG_POWER_CTRL 0x00
|
||||
#define SSM4567_REG_AMP_SNS_CTRL 0x01
|
||||
#define SSM4567_REG_DAC_CTRL 0x02
|
||||
#define SSM4567_REG_DAC_VOLUME 0x03
|
||||
#define SSM4567_REG_SAI_CTRL_1 0x04
|
||||
#define SSM4567_REG_SAI_CTRL_2 0x05
|
||||
#define SSM4567_REG_SAI_PLACEMENT_1 0x06
|
||||
#define SSM4567_REG_SAI_PLACEMENT_2 0x07
|
||||
#define SSM4567_REG_SAI_PLACEMENT_3 0x08
|
||||
#define SSM4567_REG_SAI_PLACEMENT_4 0x09
|
||||
#define SSM4567_REG_SAI_PLACEMENT_5 0x0a
|
||||
#define SSM4567_REG_SAI_PLACEMENT_6 0x0b
|
||||
#define SSM4567_REG_BATTERY_V_OUT 0x0c
|
||||
#define SSM4567_REG_LIMITER_CTRL_1 0x0d
|
||||
#define SSM4567_REG_LIMITER_CTRL_2 0x0e
|
||||
#define SSM4567_REG_LIMITER_CTRL_3 0x0f
|
||||
#define SSM4567_REG_STATUS_1 0x10
|
||||
#define SSM4567_REG_STATUS_2 0x11
|
||||
#define SSM4567_REG_FAULT_CTRL 0x12
|
||||
#define SSM4567_REG_PDM_CTRL 0x13
|
||||
#define SSM4567_REG_MCLK_RATIO 0x14
|
||||
#define SSM4567_REG_BOOST_CTRL_1 0x15
|
||||
#define SSM4567_REG_BOOST_CTRL_2 0x16
|
||||
#define SSM4567_REG_SOFT_RESET 0xff
|
||||
|
||||
/* POWER_CTRL */
|
||||
#define SSM4567_POWER_APWDN_EN BIT(7)
|
||||
#define SSM4567_POWER_BSNS_PWDN BIT(6)
|
||||
#define SSM4567_POWER_VSNS_PWDN BIT(5)
|
||||
#define SSM4567_POWER_ISNS_PWDN BIT(4)
|
||||
#define SSM4567_POWER_BOOST_PWDN BIT(3)
|
||||
#define SSM4567_POWER_AMP_PWDN BIT(2)
|
||||
#define SSM4567_POWER_VBAT_ONLY BIT(1)
|
||||
#define SSM4567_POWER_SPWDN BIT(0)
|
||||
|
||||
/* DAC_CTRL */
|
||||
#define SSM4567_DAC_HV BIT(7)
|
||||
#define SSM4567_DAC_MUTE BIT(6)
|
||||
#define SSM4567_DAC_HPF BIT(5)
|
||||
#define SSM4567_DAC_LPM BIT(4)
|
||||
#define SSM4567_DAC_FS_MASK 0x7
|
||||
#define SSM4567_DAC_FS_8000_12000 0x0
|
||||
#define SSM4567_DAC_FS_16000_24000 0x1
|
||||
#define SSM4567_DAC_FS_32000_48000 0x2
|
||||
#define SSM4567_DAC_FS_64000_96000 0x3
|
||||
#define SSM4567_DAC_FS_128000_192000 0x4
|
||||
|
||||
struct ssm4567 {
|
||||
struct regmap *regmap;
|
||||
};
|
||||
|
||||
static const struct reg_default ssm4567_reg_defaults[] = {
|
||||
{ SSM4567_REG_POWER_CTRL, 0x81 },
|
||||
{ SSM4567_REG_AMP_SNS_CTRL, 0x09 },
|
||||
{ SSM4567_REG_DAC_CTRL, 0x32 },
|
||||
{ SSM4567_REG_DAC_VOLUME, 0x40 },
|
||||
{ SSM4567_REG_SAI_CTRL_1, 0x00 },
|
||||
{ SSM4567_REG_SAI_CTRL_2, 0x08 },
|
||||
{ SSM4567_REG_SAI_PLACEMENT_1, 0x01 },
|
||||
{ SSM4567_REG_SAI_PLACEMENT_2, 0x20 },
|
||||
{ SSM4567_REG_SAI_PLACEMENT_3, 0x32 },
|
||||
{ SSM4567_REG_SAI_PLACEMENT_4, 0x07 },
|
||||
{ SSM4567_REG_SAI_PLACEMENT_5, 0x07 },
|
||||
{ SSM4567_REG_SAI_PLACEMENT_6, 0x07 },
|
||||
{ SSM4567_REG_BATTERY_V_OUT, 0x00 },
|
||||
{ SSM4567_REG_LIMITER_CTRL_1, 0xa4 },
|
||||
{ SSM4567_REG_LIMITER_CTRL_2, 0x73 },
|
||||
{ SSM4567_REG_LIMITER_CTRL_3, 0x00 },
|
||||
{ SSM4567_REG_STATUS_1, 0x00 },
|
||||
{ SSM4567_REG_STATUS_2, 0x00 },
|
||||
{ SSM4567_REG_FAULT_CTRL, 0x30 },
|
||||
{ SSM4567_REG_PDM_CTRL, 0x40 },
|
||||
{ SSM4567_REG_MCLK_RATIO, 0x11 },
|
||||
{ SSM4567_REG_BOOST_CTRL_1, 0x03 },
|
||||
{ SSM4567_REG_BOOST_CTRL_2, 0x00 },
|
||||
{ SSM4567_REG_SOFT_RESET, 0x00 },
|
||||
};
|
||||
|
||||
|
||||
static bool ssm4567_readable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case SSM4567_REG_POWER_CTRL ... SSM4567_REG_BOOST_CTRL_2:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static bool ssm4567_writeable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case SSM4567_REG_POWER_CTRL ... SSM4567_REG_SAI_PLACEMENT_6:
|
||||
case SSM4567_REG_LIMITER_CTRL_1 ... SSM4567_REG_LIMITER_CTRL_3:
|
||||
case SSM4567_REG_FAULT_CTRL ... SSM4567_REG_BOOST_CTRL_2:
|
||||
/* The datasheet states that soft reset register is read-only,
|
||||
* but logically it is write-only. */
|
||||
case SSM4567_REG_SOFT_RESET:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ssm4567_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case SSM4567_REG_BATTERY_V_OUT:
|
||||
case SSM4567_REG_STATUS_1 ... SSM4567_REG_STATUS_2:
|
||||
case SSM4567_REG_SOFT_RESET:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static const DECLARE_TLV_DB_MINMAX_MUTE(ssm4567_vol_tlv, -7125, 2400);
|
||||
|
||||
static const struct snd_kcontrol_new ssm4567_snd_controls[] = {
|
||||
SOC_SINGLE_TLV("Master Playback Volume", SSM4567_REG_DAC_VOLUME, 0,
|
||||
0xff, 1, ssm4567_vol_tlv),
|
||||
SOC_SINGLE("DAC Low Power Mode Switch", SSM4567_REG_DAC_CTRL, 4, 1, 0),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget ssm4567_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM4567_REG_POWER_CTRL, 2, 1),
|
||||
|
||||
SND_SOC_DAPM_OUTPUT("OUT"),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route ssm4567_routes[] = {
|
||||
{ "OUT", NULL, "DAC" },
|
||||
};
|
||||
|
||||
static int ssm4567_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 ssm4567 *ssm4567 = snd_soc_codec_get_drvdata(codec);
|
||||
unsigned int rate = params_rate(params);
|
||||
unsigned int dacfs;
|
||||
|
||||
if (rate >= 8000 && rate <= 12000)
|
||||
dacfs = SSM4567_DAC_FS_8000_12000;
|
||||
else if (rate >= 16000 && rate <= 24000)
|
||||
dacfs = SSM4567_DAC_FS_16000_24000;
|
||||
else if (rate >= 32000 && rate <= 48000)
|
||||
dacfs = SSM4567_DAC_FS_32000_48000;
|
||||
else if (rate >= 64000 && rate <= 96000)
|
||||
dacfs = SSM4567_DAC_FS_64000_96000;
|
||||
else if (rate >= 128000 && rate <= 192000)
|
||||
dacfs = SSM4567_DAC_FS_128000_192000;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return regmap_update_bits(ssm4567->regmap, SSM4567_REG_DAC_CTRL,
|
||||
SSM4567_DAC_FS_MASK, dacfs);
|
||||
}
|
||||
|
||||
static int ssm4567_mute(struct snd_soc_dai *dai, int mute)
|
||||
{
|
||||
struct ssm4567 *ssm4567 = snd_soc_codec_get_drvdata(dai->codec);
|
||||
unsigned int val;
|
||||
|
||||
val = mute ? SSM4567_DAC_MUTE : 0;
|
||||
return regmap_update_bits(ssm4567->regmap, SSM4567_REG_DAC_CTRL,
|
||||
SSM4567_DAC_MUTE, val);
|
||||
}
|
||||
|
||||
static int ssm4567_set_power(struct ssm4567 *ssm4567, bool enable)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!enable) {
|
||||
ret = regmap_update_bits(ssm4567->regmap,
|
||||
SSM4567_REG_POWER_CTRL,
|
||||
SSM4567_POWER_SPWDN, SSM4567_POWER_SPWDN);
|
||||
regcache_mark_dirty(ssm4567->regmap);
|
||||
}
|
||||
|
||||
regcache_cache_only(ssm4567->regmap, !enable);
|
||||
|
||||
if (enable) {
|
||||
ret = regmap_update_bits(ssm4567->regmap,
|
||||
SSM4567_REG_POWER_CTRL,
|
||||
SSM4567_POWER_SPWDN, 0x00);
|
||||
regcache_sync(ssm4567->regmap);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ssm4567_set_bias_level(struct snd_soc_codec *codec,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
struct ssm4567 *ssm4567 = snd_soc_codec_get_drvdata(codec);
|
||||
int ret = 0;
|
||||
|
||||
switch (level) {
|
||||
case SND_SOC_BIAS_ON:
|
||||
break;
|
||||
case SND_SOC_BIAS_PREPARE:
|
||||
break;
|
||||
case SND_SOC_BIAS_STANDBY:
|
||||
if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
|
||||
ret = ssm4567_set_power(ssm4567, true);
|
||||
break;
|
||||
case SND_SOC_BIAS_OFF:
|
||||
ret = ssm4567_set_power(ssm4567, false);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
codec->dapm.bias_level = level;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops ssm4567_dai_ops = {
|
||||
.hw_params = ssm4567_hw_params,
|
||||
.digital_mute = ssm4567_mute,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver ssm4567_dai = {
|
||||
.name = "ssm4567-hifi",
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 1,
|
||||
.rates = SNDRV_PCM_RATE_8000_192000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
|
||||
SNDRV_PCM_FMTBIT_S32,
|
||||
},
|
||||
.ops = &ssm4567_dai_ops,
|
||||
};
|
||||
|
||||
static struct snd_soc_codec_driver ssm4567_codec_driver = {
|
||||
.set_bias_level = ssm4567_set_bias_level,
|
||||
.idle_bias_off = true,
|
||||
|
||||
.controls = ssm4567_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(ssm4567_snd_controls),
|
||||
.dapm_widgets = ssm4567_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(ssm4567_dapm_widgets),
|
||||
.dapm_routes = ssm4567_routes,
|
||||
.num_dapm_routes = ARRAY_SIZE(ssm4567_routes),
|
||||
};
|
||||
|
||||
static const struct regmap_config ssm4567_regmap_config = {
|
||||
.val_bits = 8,
|
||||
.reg_bits = 8,
|
||||
|
||||
.max_register = SSM4567_REG_SOFT_RESET,
|
||||
.readable_reg = ssm4567_readable_reg,
|
||||
.writeable_reg = ssm4567_writeable_reg,
|
||||
.volatile_reg = ssm4567_volatile_reg,
|
||||
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.reg_defaults = ssm4567_reg_defaults,
|
||||
.num_reg_defaults = ARRAY_SIZE(ssm4567_reg_defaults),
|
||||
};
|
||||
|
||||
static int ssm4567_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct ssm4567 *ssm4567;
|
||||
int ret;
|
||||
|
||||
ssm4567 = devm_kzalloc(&i2c->dev, sizeof(*ssm4567), GFP_KERNEL);
|
||||
if (ssm4567 == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(i2c, ssm4567);
|
||||
|
||||
ssm4567->regmap = devm_regmap_init_i2c(i2c, &ssm4567_regmap_config);
|
||||
if (IS_ERR(ssm4567->regmap))
|
||||
return PTR_ERR(ssm4567->regmap);
|
||||
|
||||
ret = regmap_write(ssm4567->regmap, SSM4567_REG_SOFT_RESET, 0x00);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ssm4567_set_power(ssm4567, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return snd_soc_register_codec(&i2c->dev, &ssm4567_codec_driver,
|
||||
&ssm4567_dai, 1);
|
||||
}
|
||||
|
||||
static int ssm4567_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
snd_soc_unregister_codec(&client->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ssm4567_i2c_ids[] = {
|
||||
{ "ssm4567", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ssm4567_i2c_ids);
|
||||
|
||||
static struct i2c_driver ssm4567_driver = {
|
||||
.driver = {
|
||||
.name = "ssm4567",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ssm4567_i2c_probe,
|
||||
.remove = ssm4567_i2c_remove,
|
||||
.id_table = ssm4567_i2c_ids,
|
||||
};
|
||||
module_i2c_driver(ssm4567_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC SSM4567 driver");
|
||||
MODULE_AUTHOR("Anatol Pomozov <anatol@chromium.org>");
|
||||
MODULE_LICENSE("GPL");
|
@ -78,6 +78,44 @@ struct tas2552_data {
|
||||
unsigned int mclk;
|
||||
};
|
||||
|
||||
/* Input mux controls */
|
||||
static const char *tas2552_input_texts[] = {
|
||||
"Digital", "Analog"
|
||||
};
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(tas2552_input_mux_enum, TAS2552_CFG_3, 7,
|
||||
tas2552_input_texts);
|
||||
|
||||
static const struct snd_kcontrol_new tas2552_input_mux_control[] = {
|
||||
SOC_DAPM_ENUM("Input selection", tas2552_input_mux_enum)
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget tas2552_dapm_widgets[] =
|
||||
{
|
||||
SND_SOC_DAPM_INPUT("IN"),
|
||||
|
||||
/* MUX Controls */
|
||||
SND_SOC_DAPM_MUX("Input selection", SND_SOC_NOPM, 0, 0,
|
||||
tas2552_input_mux_control),
|
||||
|
||||
SND_SOC_DAPM_AIF_IN("DAC IN", "DAC Playback", 0, SND_SOC_NOPM, 0, 0),
|
||||
SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
|
||||
SND_SOC_DAPM_OUT_DRV("ClassD", TAS2552_CFG_2, 7, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("PLL", TAS2552_CFG_2, 3, 0, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_OUTPUT("OUT")
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route tas2552_audio_map[] = {
|
||||
{"DAC", NULL, "DAC IN"},
|
||||
{"Input selection", "Digital", "DAC"},
|
||||
{"Input selection", "Analog", "IN"},
|
||||
{"ClassD", NULL, "Input selection"},
|
||||
{"OUT", NULL, "ClassD"},
|
||||
{"ClassD", NULL, "PLL"},
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
static void tas2552_sw_shutdown(struct tas2552_data *tas_data, int sw_shutdown)
|
||||
{
|
||||
u8 cfg1_reg;
|
||||
@ -90,6 +128,7 @@ static void tas2552_sw_shutdown(struct tas2552_data *tas_data, int sw_shutdown)
|
||||
snd_soc_update_bits(tas_data->codec, TAS2552_CFG_1,
|
||||
TAS2552_SWS_MASK, cfg1_reg);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int tas2552_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
@ -101,10 +140,6 @@ static int tas2552_hw_params(struct snd_pcm_substream *substream,
|
||||
int d;
|
||||
u8 p, j;
|
||||
|
||||
/* Turn on Class D amplifier */
|
||||
snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_CLASSD_EN_MASK,
|
||||
TAS2552_CLASSD_EN);
|
||||
|
||||
if (!tas2552->mclk)
|
||||
return -EINVAL;
|
||||
|
||||
@ -147,9 +182,6 @@ static int tas2552_hw_params(struct snd_pcm_substream *substream,
|
||||
|
||||
}
|
||||
|
||||
snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE,
|
||||
TAS2552_PLL_ENABLE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -269,19 +301,10 @@ static const struct dev_pm_ops tas2552_pm = {
|
||||
NULL)
|
||||
};
|
||||
|
||||
static void tas2552_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
|
||||
snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0);
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_ops tas2552_speaker_dai_ops = {
|
||||
.hw_params = tas2552_hw_params,
|
||||
.set_sysclk = tas2552_set_dai_sysclk,
|
||||
.set_fmt = tas2552_set_dai_fmt,
|
||||
.shutdown = tas2552_shutdown,
|
||||
.digital_mute = tas2552_mute,
|
||||
};
|
||||
|
||||
@ -294,7 +317,7 @@ static struct snd_soc_dai_driver tas2552_dai[] = {
|
||||
{
|
||||
.name = "tas2552-amplifier",
|
||||
.playback = {
|
||||
.stream_name = "Speaker",
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_8000_192000,
|
||||
@ -312,6 +335,7 @@ static DECLARE_TLV_DB_SCALE(dac_tlv, -7, 100, 24);
|
||||
static const struct snd_kcontrol_new tas2552_snd_controls[] = {
|
||||
SOC_SINGLE_TLV("Speaker Driver Playback Volume",
|
||||
TAS2552_PGA_GAIN, 0, 0x1f, 1, dac_tlv),
|
||||
SOC_DAPM_SINGLE("Playback AMP", SND_SOC_NOPM, 0, 1, 0),
|
||||
};
|
||||
|
||||
static const struct reg_default tas2552_init_regs[] = {
|
||||
@ -321,6 +345,7 @@ static const struct reg_default tas2552_init_regs[] = {
|
||||
static int tas2552_codec_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
|
||||
struct snd_soc_dapm_context *dapm = &codec->dapm;
|
||||
int ret;
|
||||
|
||||
tas2552->codec = codec;
|
||||
@ -362,9 +387,14 @@ static int tas2552_codec_probe(struct snd_soc_codec *codec)
|
||||
goto patch_fail;
|
||||
}
|
||||
|
||||
snd_soc_write(codec, TAS2552_CFG_2, TAS2552_CLASSD_EN |
|
||||
TAS2552_BOOST_EN | TAS2552_APT_EN |
|
||||
TAS2552_LIM_EN);
|
||||
snd_soc_write(codec, TAS2552_CFG_2, TAS2552_BOOST_EN |
|
||||
TAS2552_APT_EN | TAS2552_LIM_EN);
|
||||
|
||||
snd_soc_dapm_new_controls(dapm, tas2552_dapm_widgets,
|
||||
ARRAY_SIZE(tas2552_dapm_widgets));
|
||||
snd_soc_dapm_add_routes(dapm, tas2552_audio_map,
|
||||
ARRAY_SIZE(tas2552_audio_map));
|
||||
|
||||
return 0;
|
||||
|
||||
patch_fail:
|
||||
|
@ -167,13 +167,13 @@ struct aic31xx_priv {
|
||||
struct regulator_bulk_data supplies[AIC31XX_NUM_SUPPLIES];
|
||||
struct aic31xx_disable_nb disable_nb[AIC31XX_NUM_SUPPLIES];
|
||||
unsigned int sysclk;
|
||||
u8 p_div;
|
||||
int rate_div_line;
|
||||
};
|
||||
|
||||
struct aic31xx_rate_divs {
|
||||
u32 mclk;
|
||||
u32 mclk_p;
|
||||
u32 rate;
|
||||
u8 p_val;
|
||||
u8 pll_j;
|
||||
u16 pll_d;
|
||||
u16 dosr;
|
||||
@ -186,62 +186,51 @@ struct aic31xx_rate_divs {
|
||||
|
||||
/* ADC dividers can be disabled by cofiguring them to 0 */
|
||||
static const struct aic31xx_rate_divs aic31xx_divs[] = {
|
||||
/* mclk rate pll: p j d dosr ndac mdac aors nadc madc */
|
||||
/* mclk/p rate pll: j d dosr ndac mdac aors nadc madc */
|
||||
/* 8k rate */
|
||||
{12000000, 8000, 1, 8, 1920, 128, 48, 2, 128, 48, 2},
|
||||
{12000000, 8000, 1, 8, 1920, 128, 32, 3, 128, 32, 3},
|
||||
{24000000, 8000, 2, 8, 1920, 128, 48, 2, 128, 48, 2},
|
||||
{25000000, 8000, 2, 7, 8643, 128, 48, 2, 128, 48, 2},
|
||||
{12000000, 8000, 8, 1920, 128, 48, 2, 128, 48, 2},
|
||||
{12000000, 8000, 8, 1920, 128, 32, 3, 128, 32, 3},
|
||||
{12500000, 8000, 7, 8643, 128, 48, 2, 128, 48, 2},
|
||||
/* 11.025k rate */
|
||||
{12000000, 11025, 1, 7, 5264, 128, 32, 2, 128, 32, 2},
|
||||
{12000000, 11025, 1, 8, 4672, 128, 24, 3, 128, 24, 3},
|
||||
{24000000, 11025, 2, 7, 5264, 128, 32, 2, 128, 32, 2},
|
||||
{25000000, 11025, 2, 7, 2253, 128, 32, 2, 128, 32, 2},
|
||||
{12000000, 11025, 7, 5264, 128, 32, 2, 128, 32, 2},
|
||||
{12000000, 11025, 8, 4672, 128, 24, 3, 128, 24, 3},
|
||||
{12500000, 11025, 7, 2253, 128, 32, 2, 128, 32, 2},
|
||||
/* 16k rate */
|
||||
{12000000, 16000, 1, 8, 1920, 128, 24, 2, 128, 24, 2},
|
||||
{12000000, 16000, 1, 8, 1920, 128, 16, 3, 128, 16, 3},
|
||||
{24000000, 16000, 2, 8, 1920, 128, 24, 2, 128, 24, 2},
|
||||
{25000000, 16000, 2, 7, 8643, 128, 24, 2, 128, 24, 2},
|
||||
{12000000, 16000, 8, 1920, 128, 24, 2, 128, 24, 2},
|
||||
{12000000, 16000, 8, 1920, 128, 16, 3, 128, 16, 3},
|
||||
{12500000, 16000, 7, 8643, 128, 24, 2, 128, 24, 2},
|
||||
/* 22.05k rate */
|
||||
{12000000, 22050, 1, 7, 5264, 128, 16, 2, 128, 16, 2},
|
||||
{12000000, 22050, 1, 8, 4672, 128, 12, 3, 128, 12, 3},
|
||||
{24000000, 22050, 2, 7, 5264, 128, 16, 2, 128, 16, 2},
|
||||
{25000000, 22050, 2, 7, 2253, 128, 16, 2, 128, 16, 2},
|
||||
{12000000, 22050, 7, 5264, 128, 16, 2, 128, 16, 2},
|
||||
{12000000, 22050, 8, 4672, 128, 12, 3, 128, 12, 3},
|
||||
{12500000, 22050, 7, 2253, 128, 16, 2, 128, 16, 2},
|
||||
/* 32k rate */
|
||||
{12000000, 32000, 1, 8, 1920, 128, 12, 2, 128, 12, 2},
|
||||
{12000000, 32000, 1, 8, 1920, 128, 8, 3, 128, 8, 3},
|
||||
{24000000, 32000, 2, 8, 1920, 128, 12, 2, 128, 12, 2},
|
||||
{25000000, 32000, 2, 7, 8643, 128, 12, 2, 128, 12, 2},
|
||||
{12000000, 32000, 8, 1920, 128, 12, 2, 128, 12, 2},
|
||||
{12000000, 32000, 8, 1920, 128, 8, 3, 128, 8, 3},
|
||||
{12500000, 32000, 7, 8643, 128, 12, 2, 128, 12, 2},
|
||||
/* 44.1k rate */
|
||||
{12000000, 44100, 1, 7, 5264, 128, 8, 2, 128, 8, 2},
|
||||
{12000000, 44100, 1, 8, 4672, 128, 6, 3, 128, 6, 3},
|
||||
{24000000, 44100, 2, 7, 5264, 128, 8, 2, 128, 8, 2},
|
||||
{25000000, 44100, 2, 7, 2253, 128, 8, 2, 128, 8, 2},
|
||||
{12000000, 44100, 7, 5264, 128, 8, 2, 128, 8, 2},
|
||||
{12000000, 44100, 8, 4672, 128, 6, 3, 128, 6, 3},
|
||||
{12500000, 44100, 7, 2253, 128, 8, 2, 128, 8, 2},
|
||||
/* 48k rate */
|
||||
{12000000, 48000, 1, 8, 1920, 128, 8, 2, 128, 8, 2},
|
||||
{12000000, 48000, 1, 7, 6800, 96, 5, 4, 96, 5, 4},
|
||||
{24000000, 48000, 2, 8, 1920, 128, 8, 2, 128, 8, 2},
|
||||
{25000000, 48000, 2, 7, 8643, 128, 8, 2, 128, 8, 2},
|
||||
{12000000, 48000, 8, 1920, 128, 8, 2, 128, 8, 2},
|
||||
{12000000, 48000, 7, 6800, 96, 5, 4, 96, 5, 4},
|
||||
{12500000, 48000, 7, 8643, 128, 8, 2, 128, 8, 2},
|
||||
/* 88.2k rate */
|
||||
{12000000, 88200, 1, 7, 5264, 64, 8, 2, 64, 8, 2},
|
||||
{12000000, 88200, 1, 8, 4672, 64, 6, 3, 64, 6, 3},
|
||||
{24000000, 88200, 2, 7, 5264, 64, 8, 2, 64, 8, 2},
|
||||
{25000000, 88200, 2, 7, 2253, 64, 8, 2, 64, 8, 2},
|
||||
{12000000, 88200, 7, 5264, 64, 8, 2, 64, 8, 2},
|
||||
{12000000, 88200, 8, 4672, 64, 6, 3, 64, 6, 3},
|
||||
{12500000, 88200, 7, 2253, 64, 8, 2, 64, 8, 2},
|
||||
/* 96k rate */
|
||||
{12000000, 96000, 1, 8, 1920, 64, 8, 2, 64, 8, 2},
|
||||
{12000000, 96000, 1, 7, 6800, 48, 5, 4, 48, 5, 4},
|
||||
{24000000, 96000, 2, 8, 1920, 64, 8, 2, 64, 8, 2},
|
||||
{25000000, 96000, 2, 7, 8643, 64, 8, 2, 64, 8, 2},
|
||||
{12000000, 96000, 8, 1920, 64, 8, 2, 64, 8, 2},
|
||||
{12000000, 96000, 7, 6800, 48, 5, 4, 48, 5, 4},
|
||||
{12500000, 96000, 7, 8643, 64, 8, 2, 64, 8, 2},
|
||||
/* 176.4k rate */
|
||||
{12000000, 176400, 1, 7, 5264, 32, 8, 2, 32, 8, 2},
|
||||
{12000000, 176400, 1, 8, 4672, 32, 6, 3, 32, 6, 3},
|
||||
{24000000, 176400, 2, 7, 5264, 32, 8, 2, 32, 8, 2},
|
||||
{25000000, 176400, 2, 7, 2253, 32, 8, 2, 32, 8, 2},
|
||||
{12000000, 176400, 7, 5264, 32, 8, 2, 32, 8, 2},
|
||||
{12000000, 176400, 8, 4672, 32, 6, 3, 32, 6, 3},
|
||||
{12500000, 176400, 7, 2253, 32, 8, 2, 32, 8, 2},
|
||||
/* 192k rate */
|
||||
{12000000, 192000, 1, 8, 1920, 32, 8, 2, 32, 8, 2},
|
||||
{12000000, 192000, 1, 7, 6800, 24, 5, 4, 24, 5, 4},
|
||||
{24000000, 192000, 2, 8, 1920, 32, 8, 2, 32, 8, 2},
|
||||
{25000000, 192000, 2, 7, 8643, 32, 8, 2, 32, 8, 2},
|
||||
{12000000, 192000, 8, 1920, 32, 8, 2, 32, 8, 2},
|
||||
{12000000, 192000, 7, 6800, 24, 5, 4, 24, 5, 4},
|
||||
{12500000, 192000, 7, 8643, 32, 8, 2, 32, 8, 2},
|
||||
};
|
||||
|
||||
static const char * const ldac_in_text[] = {
|
||||
@ -692,6 +681,7 @@ static int aic31xx_setup_pll(struct snd_soc_codec *codec,
|
||||
{
|
||||
struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
|
||||
int bclk_score = snd_soc_params_to_frame_size(params);
|
||||
int mclk_p = aic31xx->sysclk / aic31xx->p_div;
|
||||
int bclk_n = 0;
|
||||
int match = -1;
|
||||
int i;
|
||||
@ -704,7 +694,7 @@ static int aic31xx_setup_pll(struct snd_soc_codec *codec,
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(aic31xx_divs); i++) {
|
||||
if (aic31xx_divs[i].rate == params_rate(params) &&
|
||||
aic31xx_divs[i].mclk == aic31xx->sysclk) {
|
||||
aic31xx_divs[i].mclk_p == mclk_p) {
|
||||
int s = (aic31xx_divs[i].dosr * aic31xx_divs[i].mdac) %
|
||||
snd_soc_params_to_frame_size(params);
|
||||
int bn = (aic31xx_divs[i].dosr * aic31xx_divs[i].mdac) /
|
||||
@ -738,7 +728,7 @@ static int aic31xx_setup_pll(struct snd_soc_codec *codec,
|
||||
|
||||
/* PLL configuration */
|
||||
snd_soc_update_bits(codec, AIC31XX_PLLPR, AIC31XX_PLL_MASK,
|
||||
(aic31xx_divs[i].p_val << 4) | 0x01);
|
||||
(aic31xx->p_div << 4) | 0x01);
|
||||
snd_soc_write(codec, AIC31XX_PLLJ, aic31xx_divs[i].pll_j);
|
||||
|
||||
snd_soc_write(codec, AIC31XX_PLLDMSB,
|
||||
@ -772,7 +762,7 @@ static int aic31xx_setup_pll(struct snd_soc_codec *codec,
|
||||
dev_dbg(codec->dev,
|
||||
"pll %d.%04d/%d dosr %d n %d m %d aosr %d n %d m %d bclk_n %d\n",
|
||||
aic31xx_divs[i].pll_j, aic31xx_divs[i].pll_d,
|
||||
aic31xx_divs[i].p_val, aic31xx_divs[i].dosr,
|
||||
aic31xx->p_div, aic31xx_divs[i].dosr,
|
||||
aic31xx_divs[i].ndac, aic31xx_divs[i].mdac,
|
||||
aic31xx_divs[i].aosr, aic31xx_divs[i].nadc,
|
||||
aic31xx_divs[i].madc, bclk_n);
|
||||
@ -840,7 +830,7 @@ static int aic31xx_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
||||
{
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
u8 iface_reg1 = 0;
|
||||
u8 iface_reg3 = 0;
|
||||
u8 iface_reg2 = 0;
|
||||
u8 dsp_a_val = 0;
|
||||
|
||||
dev_dbg(codec->dev, "## %s: fmt = 0x%x\n", __func__, fmt);
|
||||
@ -865,7 +855,7 @@ static int aic31xx_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
||||
/* NOTE: BCLKINV bit value 1 equas NB and 0 equals IB */
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
iface_reg3 |= AIC31XX_BCLKINV_MASK;
|
||||
iface_reg2 |= AIC31XX_BCLKINV_MASK;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
break;
|
||||
@ -897,7 +887,7 @@ static int aic31xx_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
||||
dsp_a_val);
|
||||
snd_soc_update_bits(codec, AIC31XX_IFACE2,
|
||||
AIC31XX_BCLKINV_MASK,
|
||||
iface_reg3);
|
||||
iface_reg2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -912,7 +902,16 @@ static int aic31xx_set_dai_sysclk(struct snd_soc_dai *codec_dai,
|
||||
dev_dbg(codec->dev, "## %s: clk_id = %d, freq = %d, dir = %d\n",
|
||||
__func__, clk_id, freq, dir);
|
||||
|
||||
for (i = 0; aic31xx_divs[i].mclk != freq; i++) {
|
||||
for (i = 1; freq/i > 20000000 && i < 8; i++)
|
||||
;
|
||||
if (freq/i > 20000000) {
|
||||
dev_err(aic31xx->dev, "%s: Too high mclk frequency %u\n",
|
||||
__func__, freq);
|
||||
return -EINVAL;
|
||||
}
|
||||
aic31xx->p_div = i;
|
||||
|
||||
for (i = 0; aic31xx_divs[i].mclk_p != freq/aic31xx->p_div; i++) {
|
||||
if (i == ARRAY_SIZE(aic31xx_divs)) {
|
||||
dev_err(aic31xx->dev, "%s: Unsupported frequency %d\n",
|
||||
__func__, freq);
|
||||
|
@ -18,7 +18,8 @@
|
||||
#define AIC31XX_RATES SNDRV_PCM_RATE_8000_192000
|
||||
|
||||
#define AIC31XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
|
||||
| SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
|
||||
| SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE \
|
||||
| SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
|
||||
#define AIC31XX_STEREO_CLASS_D_BIT 0x1
|
||||
|
@ -1121,6 +1121,7 @@ static int aic3x_regulator_event(struct notifier_block *nb,
|
||||
static int aic3x_set_power(struct snd_soc_codec *codec, int power)
|
||||
{
|
||||
struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
|
||||
unsigned int pll_c, pll_d;
|
||||
int ret;
|
||||
|
||||
if (power) {
|
||||
@ -1138,6 +1139,18 @@ static int aic3x_set_power(struct snd_soc_codec *codec, int power)
|
||||
/* Sync reg_cache with the hardware */
|
||||
regcache_cache_only(aic3x->regmap, false);
|
||||
regcache_sync(aic3x->regmap);
|
||||
|
||||
/* Rewrite paired PLL D registers in case cached sync skipped
|
||||
* writing one of them and thus caused other one also not
|
||||
* being written
|
||||
*/
|
||||
pll_c = snd_soc_read(codec, AIC3X_PLL_PROGC_REG);
|
||||
pll_d = snd_soc_read(codec, AIC3X_PLL_PROGD_REG);
|
||||
if (pll_c == aic3x_reg[AIC3X_PLL_PROGC_REG].def ||
|
||||
pll_d == aic3x_reg[AIC3X_PLL_PROGD_REG].def) {
|
||||
snd_soc_write(codec, AIC3X_PLL_PROGC_REG, pll_c);
|
||||
snd_soc_write(codec, AIC3X_PLL_PROGD_REG, pll_d);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Do soft reset to this codec instance in order to clear
|
||||
@ -1222,20 +1235,6 @@ static struct snd_soc_dai_driver aic3x_dai = {
|
||||
.symmetric_rates = 1,
|
||||
};
|
||||
|
||||
static int aic3x_suspend(struct snd_soc_codec *codec)
|
||||
{
|
||||
aic3x_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aic3x_resume(struct snd_soc_codec *codec)
|
||||
{
|
||||
aic3x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void aic3x_mono_init(struct snd_soc_codec *codec)
|
||||
{
|
||||
/* DAC to Mono Line Out default volume and route to Output mixer */
|
||||
@ -1429,8 +1428,6 @@ static struct snd_soc_codec_driver soc_codec_dev_aic3x = {
|
||||
.idle_bias_off = true,
|
||||
.probe = aic3x_probe,
|
||||
.remove = aic3x_remove,
|
||||
.suspend = aic3x_suspend,
|
||||
.resume = aic3x_resume,
|
||||
.controls = aic3x_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(aic3x_snd_controls),
|
||||
.dapm_widgets = aic3x_dapm_widgets,
|
||||
|
@ -2319,11 +2319,8 @@ static void wm5100_init_gpio(struct i2c_client *i2c)
|
||||
static void wm5100_free_gpio(struct i2c_client *i2c)
|
||||
{
|
||||
struct wm5100_priv *wm5100 = i2c_get_clientdata(i2c);
|
||||
int ret;
|
||||
|
||||
ret = gpiochip_remove(&wm5100->gpio_chip);
|
||||
if (ret != 0)
|
||||
dev_err(&i2c->dev, "Failed to remove GPIOs: %d\n", ret);
|
||||
gpiochip_remove(&wm5100->gpio_chip);
|
||||
}
|
||||
#else
|
||||
static void wm5100_init_gpio(struct i2c_client *i2c)
|
||||
|
@ -212,7 +212,7 @@ static void wm8350_pga_work(struct work_struct *work)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm =
|
||||
container_of(work, struct snd_soc_dapm_context, delayed_work.work);
|
||||
struct snd_soc_codec *codec = dapm->codec;
|
||||
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
|
||||
struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec);
|
||||
struct wm8350_output *out1 = &wm8350_data->out1,
|
||||
*out2 = &wm8350_data->out2;
|
||||
|
@ -413,7 +413,6 @@ static int wm8741_resume(struct snd_soc_codec *codec)
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define wm8741_suspend NULL
|
||||
#define wm8741_resume NULL
|
||||
#endif
|
||||
|
||||
|
@ -1433,7 +1433,7 @@ static void wm8753_work(struct work_struct *work)
|
||||
struct snd_soc_dapm_context *dapm =
|
||||
container_of(work, struct snd_soc_dapm_context,
|
||||
delayed_work.work);
|
||||
struct snd_soc_codec *codec = dapm->codec;
|
||||
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
|
||||
wm8753_set_bias_level(codec, dapm->bias_level);
|
||||
}
|
||||
|
||||
|
@ -518,23 +518,6 @@ static int wm8804_set_bias_level(struct snd_soc_codec *codec,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int wm8804_suspend(struct snd_soc_codec *codec)
|
||||
{
|
||||
wm8804_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wm8804_resume(struct snd_soc_codec *codec)
|
||||
{
|
||||
wm8804_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define wm8804_suspend NULL
|
||||
#define wm8804_resume NULL
|
||||
#endif
|
||||
|
||||
static int wm8804_remove(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct wm8804_priv *wm8804;
|
||||
@ -671,8 +654,6 @@ static struct snd_soc_dai_driver wm8804_dai = {
|
||||
static struct snd_soc_codec_driver soc_codec_dev_wm8804 = {
|
||||
.probe = wm8804_probe,
|
||||
.remove = wm8804_remove,
|
||||
.suspend = wm8804_suspend,
|
||||
.resume = wm8804_resume,
|
||||
.set_bias_level = wm8804_set_bias_level,
|
||||
.idle_bias_off = true,
|
||||
|
||||
|
@ -1877,11 +1877,7 @@ static void wm8903_init_gpio(struct wm8903_priv *wm8903)
|
||||
|
||||
static void wm8903_free_gpio(struct wm8903_priv *wm8903)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = gpiochip_remove(&wm8903->gpio_chip);
|
||||
if (ret != 0)
|
||||
dev_err(wm8903->dev, "Failed to remove GPIOs: %d\n", ret);
|
||||
gpiochip_remove(&wm8903->gpio_chip);
|
||||
}
|
||||
#else
|
||||
static void wm8903_init_gpio(struct wm8903_priv *wm8903)
|
||||
|
@ -3398,11 +3398,8 @@ static void wm8962_init_gpio(struct snd_soc_codec *codec)
|
||||
static void wm8962_free_gpio(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
|
||||
int ret;
|
||||
|
||||
ret = gpiochip_remove(&wm8962->gpio_chip);
|
||||
if (ret != 0)
|
||||
dev_err(codec->dev, "Failed to remove GPIOs: %d\n", ret);
|
||||
gpiochip_remove(&wm8962->gpio_chip);
|
||||
}
|
||||
#else
|
||||
static void wm8962_init_gpio(struct snd_soc_codec *codec)
|
||||
|
@ -615,7 +615,7 @@ static void wm8971_work(struct work_struct *work)
|
||||
struct snd_soc_dapm_context *dapm =
|
||||
container_of(work, struct snd_soc_dapm_context,
|
||||
delayed_work.work);
|
||||
struct snd_soc_codec *codec = dapm->codec;
|
||||
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
|
||||
wm8971_set_bias_level(codec, codec->dapm.bias_level);
|
||||
}
|
||||
|
||||
|
@ -4082,17 +4082,23 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
|
||||
|
||||
switch (control->type) {
|
||||
case WM8994:
|
||||
if (wm8994->micdet_irq) {
|
||||
if (wm8994->micdet_irq)
|
||||
ret = request_threaded_irq(wm8994->micdet_irq, NULL,
|
||||
wm8994_mic_irq,
|
||||
IRQF_TRIGGER_RISING,
|
||||
"Mic1 detect",
|
||||
wm8994);
|
||||
if (ret != 0)
|
||||
dev_warn(codec->dev,
|
||||
"Failed to request Mic1 detect IRQ: %d\n",
|
||||
ret);
|
||||
}
|
||||
else
|
||||
ret = wm8994_request_irq(wm8994->wm8994,
|
||||
WM8994_IRQ_MIC1_DET,
|
||||
wm8994_mic_irq, "Mic 1 detect",
|
||||
wm8994);
|
||||
|
||||
if (ret != 0)
|
||||
dev_warn(codec->dev,
|
||||
"Failed to request Mic1 detect IRQ: %d\n",
|
||||
ret);
|
||||
|
||||
|
||||
ret = wm8994_request_irq(wm8994->wm8994,
|
||||
WM8994_IRQ_MIC1_SHRT,
|
||||
|
@ -1998,23 +1998,6 @@ static int wm8995_set_bias_level(struct snd_soc_codec *codec,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int wm8995_suspend(struct snd_soc_codec *codec)
|
||||
{
|
||||
wm8995_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wm8995_resume(struct snd_soc_codec *codec)
|
||||
{
|
||||
wm8995_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define wm8995_suspend NULL
|
||||
#define wm8995_resume NULL
|
||||
#endif
|
||||
|
||||
static int wm8995_remove(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct wm8995_priv *wm8995;
|
||||
@ -2220,8 +2203,6 @@ static struct snd_soc_dai_driver wm8995_dai[] = {
|
||||
static struct snd_soc_codec_driver soc_codec_dev_wm8995 = {
|
||||
.probe = wm8995_probe,
|
||||
.remove = wm8995_remove,
|
||||
.suspend = wm8995_suspend,
|
||||
.resume = wm8995_resume,
|
||||
.set_bias_level = wm8995_set_bias_level,
|
||||
.idle_bias_off = true,
|
||||
};
|
||||
|
@ -2216,11 +2216,7 @@ static void wm8996_init_gpio(struct wm8996_priv *wm8996)
|
||||
|
||||
static void wm8996_free_gpio(struct wm8996_priv *wm8996)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = gpiochip_remove(&wm8996->gpio_chip);
|
||||
if (ret != 0)
|
||||
dev_err(wm8996->dev, "Failed to remove GPIOs: %d\n", ret);
|
||||
gpiochip_remove(&wm8996->gpio_chip);
|
||||
}
|
||||
#else
|
||||
static void wm8996_init_gpio(struct wm8996_priv *wm8996)
|
||||
|
@ -63,7 +63,8 @@ config SND_DM365_AIC3X_CODEC
|
||||
Say Y if you want to add support for AIC3101 audio codec
|
||||
|
||||
config SND_DM365_VOICE_CODEC
|
||||
bool "Voice Codec - CQ93VC"
|
||||
tristate "Voice Codec - CQ93VC"
|
||||
depends on SND_DAVINCI_SOC
|
||||
select MFD_DAVINCI_VOICECODEC
|
||||
select SND_DAVINCI_SOC_VCIF
|
||||
select SND_SOC_CQ0093VC
|
||||
|
@ -42,14 +42,26 @@
|
||||
|
||||
#define MCASP_MAX_AFIFO_DEPTH 64
|
||||
|
||||
static u32 context_regs[] = {
|
||||
DAVINCI_MCASP_TXFMCTL_REG,
|
||||
DAVINCI_MCASP_RXFMCTL_REG,
|
||||
DAVINCI_MCASP_TXFMT_REG,
|
||||
DAVINCI_MCASP_RXFMT_REG,
|
||||
DAVINCI_MCASP_ACLKXCTL_REG,
|
||||
DAVINCI_MCASP_ACLKRCTL_REG,
|
||||
DAVINCI_MCASP_AHCLKXCTL_REG,
|
||||
DAVINCI_MCASP_AHCLKRCTL_REG,
|
||||
DAVINCI_MCASP_PDIR_REG,
|
||||
DAVINCI_MCASP_RXMASK_REG,
|
||||
DAVINCI_MCASP_TXMASK_REG,
|
||||
DAVINCI_MCASP_RXTDM_REG,
|
||||
DAVINCI_MCASP_TXTDM_REG,
|
||||
};
|
||||
|
||||
struct davinci_mcasp_context {
|
||||
u32 txfmtctl;
|
||||
u32 rxfmtctl;
|
||||
u32 txfmt;
|
||||
u32 rxfmt;
|
||||
u32 aclkxctl;
|
||||
u32 aclkrctl;
|
||||
u32 pdir;
|
||||
u32 config_regs[ARRAY_SIZE(context_regs)];
|
||||
u32 afifo_regs[2]; /* for read/write fifo control registers */
|
||||
u32 *xrsr_regs; /* for serializer configuration */
|
||||
};
|
||||
|
||||
struct davinci_mcasp {
|
||||
@ -874,14 +886,24 @@ static int davinci_mcasp_suspend(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
|
||||
struct davinci_mcasp_context *context = &mcasp->context;
|
||||
u32 reg;
|
||||
int i;
|
||||
|
||||
context->txfmtctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_TXFMCTL_REG);
|
||||
context->rxfmtctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG);
|
||||
context->txfmt = mcasp_get_reg(mcasp, DAVINCI_MCASP_TXFMT_REG);
|
||||
context->rxfmt = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMT_REG);
|
||||
context->aclkxctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG);
|
||||
context->aclkrctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKRCTL_REG);
|
||||
context->pdir = mcasp_get_reg(mcasp, DAVINCI_MCASP_PDIR_REG);
|
||||
for (i = 0; i < ARRAY_SIZE(context_regs); i++)
|
||||
context->config_regs[i] = mcasp_get_reg(mcasp, context_regs[i]);
|
||||
|
||||
if (mcasp->txnumevt) {
|
||||
reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
|
||||
context->afifo_regs[0] = mcasp_get_reg(mcasp, reg);
|
||||
}
|
||||
if (mcasp->rxnumevt) {
|
||||
reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
|
||||
context->afifo_regs[1] = mcasp_get_reg(mcasp, reg);
|
||||
}
|
||||
|
||||
for (i = 0; i < mcasp->num_serializer; i++)
|
||||
context->xrsr_regs[i] = mcasp_get_reg(mcasp,
|
||||
DAVINCI_MCASP_XRSRCTL_REG(i));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -890,14 +912,24 @@ static int davinci_mcasp_resume(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
|
||||
struct davinci_mcasp_context *context = &mcasp->context;
|
||||
u32 reg;
|
||||
int i;
|
||||
|
||||
mcasp_set_reg(mcasp, DAVINCI_MCASP_TXFMCTL_REG, context->txfmtctl);
|
||||
mcasp_set_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG, context->rxfmtctl);
|
||||
mcasp_set_reg(mcasp, DAVINCI_MCASP_TXFMT_REG, context->txfmt);
|
||||
mcasp_set_reg(mcasp, DAVINCI_MCASP_RXFMT_REG, context->rxfmt);
|
||||
mcasp_set_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, context->aclkxctl);
|
||||
mcasp_set_reg(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, context->aclkrctl);
|
||||
mcasp_set_reg(mcasp, DAVINCI_MCASP_PDIR_REG, context->pdir);
|
||||
for (i = 0; i < ARRAY_SIZE(context_regs); i++)
|
||||
mcasp_set_reg(mcasp, context_regs[i], context->config_regs[i]);
|
||||
|
||||
if (mcasp->txnumevt) {
|
||||
reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
|
||||
mcasp_set_reg(mcasp, reg, context->afifo_regs[0]);
|
||||
}
|
||||
if (mcasp->rxnumevt) {
|
||||
reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
|
||||
mcasp_set_reg(mcasp, reg, context->afifo_regs[1]);
|
||||
}
|
||||
|
||||
for (i = 0; i < mcasp->num_serializer; i++)
|
||||
mcasp_set_reg(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i),
|
||||
context->xrsr_regs[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1216,6 +1248,11 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
|
||||
mcasp->op_mode = pdata->op_mode;
|
||||
mcasp->tdm_slots = pdata->tdm_slots;
|
||||
mcasp->num_serializer = pdata->num_serializer;
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
mcasp->context.xrsr_regs = devm_kzalloc(&pdev->dev,
|
||||
sizeof(u32) * mcasp->num_serializer,
|
||||
GFP_KERNEL);
|
||||
#endif
|
||||
mcasp->serial_dir = pdata->serial_dir;
|
||||
mcasp->version = pdata->version;
|
||||
mcasp->txnumevt = pdata->txnumevt;
|
||||
|
@ -25,6 +25,8 @@
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
#include <linux/edma.h>
|
||||
|
||||
#include "edma-pcm.h"
|
||||
|
||||
static const struct snd_pcm_hardware edma_pcm_hardware = {
|
||||
.info = SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
|
@ -240,6 +240,18 @@ config SND_SOC_IMX_WM8962
|
||||
Say Y if you want to add support for SoC audio on an i.MX board with
|
||||
a wm8962 codec.
|
||||
|
||||
config SND_SOC_IMX_ES8328
|
||||
tristate "SoC Audio support for i.MX boards with the ES8328 codec"
|
||||
depends on OF && (I2C || SPI)
|
||||
select SND_SOC_ES8328_I2C if I2C
|
||||
select SND_SOC_ES8328_SPI if SPI_MASTER
|
||||
select SND_SOC_IMX_PCM_DMA
|
||||
select SND_SOC_IMX_AUDMUX
|
||||
select SND_SOC_FSL_SSI
|
||||
help
|
||||
Say Y if you want to add support for the ES8328 audio codec connected
|
||||
via SSI/I2S over either SPI or I2C.
|
||||
|
||||
config SND_SOC_IMX_SGTL5000
|
||||
tristate "SoC Audio support for i.MX boards with sgtl5000"
|
||||
depends on OF && I2C
|
||||
@ -268,6 +280,20 @@ config SND_SOC_IMX_MC13783
|
||||
select SND_SOC_MC13783
|
||||
select SND_SOC_IMX_PCM_DMA
|
||||
|
||||
config SND_SOC_FSL_ASOC_CARD
|
||||
tristate "Generic ASoC Sound Card with ASRC support"
|
||||
depends on OF && I2C
|
||||
select SND_SOC_IMX_AUDMUX
|
||||
select SND_SOC_IMX_PCM_DMA
|
||||
select SND_SOC_FSL_ESAI
|
||||
select SND_SOC_FSL_SAI
|
||||
select SND_SOC_FSL_SSI
|
||||
help
|
||||
ALSA SoC Audio support with ASRC feature for Freescale SoCs that have
|
||||
ESAI/SAI/SSI and connect with external CODECs such as WM8962, CS42888
|
||||
and SGTL5000.
|
||||
Say Y if you want to add support for Freescale Generic ASoC Sound Card.
|
||||
|
||||
endif # SND_IMX_SOC
|
||||
|
||||
endmenu
|
||||
|
@ -11,6 +11,7 @@ snd-soc-p1022-rdk-objs := p1022_rdk.o
|
||||
obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o
|
||||
|
||||
# Freescale SSI/DMA/SAI/SPDIF Support
|
||||
snd-soc-fsl-asoc-card-objs := fsl-asoc-card.o
|
||||
snd-soc-fsl-asrc-objs := fsl_asrc.o fsl_asrc_dma.o
|
||||
snd-soc-fsl-sai-objs := fsl_sai.o
|
||||
snd-soc-fsl-ssi-y := fsl_ssi.o
|
||||
@ -19,6 +20,7 @@ snd-soc-fsl-spdif-objs := fsl_spdif.o
|
||||
snd-soc-fsl-esai-objs := fsl_esai.o
|
||||
snd-soc-fsl-utils-objs := fsl_utils.o
|
||||
snd-soc-fsl-dma-objs := fsl_dma.o
|
||||
obj-$(CONFIG_SND_SOC_FSL_ASOC_CARD) += snd-soc-fsl-asoc-card.o
|
||||
obj-$(CONFIG_SND_SOC_FSL_ASRC) += snd-soc-fsl-asrc.o
|
||||
obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o
|
||||
obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o
|
||||
@ -50,6 +52,7 @@ snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o
|
||||
snd-soc-phycore-ac97-objs := phycore-ac97.o
|
||||
snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o
|
||||
snd-soc-wm1133-ev1-objs := wm1133-ev1.o
|
||||
snd-soc-imx-es8328-objs := imx-es8328.o
|
||||
snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o
|
||||
snd-soc-imx-wm8962-objs := imx-wm8962.o
|
||||
snd-soc-imx-spdif-objs := imx-spdif.o
|
||||
@ -59,6 +62,7 @@ obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
|
||||
obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o
|
||||
obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o
|
||||
obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o
|
||||
obj-$(CONFIG_SND_SOC_IMX_ES8328) += snd-soc-imx-es8328.o
|
||||
obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o
|
||||
obj-$(CONFIG_SND_SOC_IMX_WM8962) += snd-soc-imx-wm8962.o
|
||||
obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o
|
||||
|
574
sound/soc/fsl/fsl-asoc-card.c
Normal file
574
sound/soc/fsl/fsl-asoc-card.c
Normal file
@ -0,0 +1,574 @@
|
||||
/*
|
||||
* Freescale Generic ASoC Sound Card driver with ASRC
|
||||
*
|
||||
* Copyright (C) 2014 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* Author: Nicolin Chen <nicoleotsuka@gmail.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public License
|
||||
* version 2. This program is licensed "as is" without any warranty of any
|
||||
* kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include "fsl_esai.h"
|
||||
#include "fsl_sai.h"
|
||||
#include "imx-audmux.h"
|
||||
|
||||
#include "../codecs/sgtl5000.h"
|
||||
#include "../codecs/wm8962.h"
|
||||
|
||||
#define RX 0
|
||||
#define TX 1
|
||||
|
||||
/* Default DAI format without Master and Slave flag */
|
||||
#define DAI_FMT_BASE (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF)
|
||||
|
||||
/**
|
||||
* CODEC private data
|
||||
*
|
||||
* @mclk_freq: Clock rate of MCLK
|
||||
* @mclk_id: MCLK (or main clock) id for set_sysclk()
|
||||
* @fll_id: FLL (or secordary clock) id for set_sysclk()
|
||||
* @pll_id: PLL id for set_pll()
|
||||
*/
|
||||
struct codec_priv {
|
||||
unsigned long mclk_freq;
|
||||
u32 mclk_id;
|
||||
u32 fll_id;
|
||||
u32 pll_id;
|
||||
};
|
||||
|
||||
/**
|
||||
* CPU private data
|
||||
*
|
||||
* @sysclk_freq[2]: SYSCLK rates for set_sysclk()
|
||||
* @sysclk_dir[2]: SYSCLK directions for set_sysclk()
|
||||
* @sysclk_id[2]: SYSCLK ids for set_sysclk()
|
||||
*
|
||||
* Note: [1] for tx and [0] for rx
|
||||
*/
|
||||
struct cpu_priv {
|
||||
unsigned long sysclk_freq[2];
|
||||
u32 sysclk_dir[2];
|
||||
u32 sysclk_id[2];
|
||||
};
|
||||
|
||||
/**
|
||||
* Freescale Generic ASOC card private data
|
||||
*
|
||||
* @dai_link[3]: DAI link structure including normal one and DPCM link
|
||||
* @pdev: platform device pointer
|
||||
* @codec_priv: CODEC private data
|
||||
* @cpu_priv: CPU private data
|
||||
* @card: ASoC card structure
|
||||
* @sample_rate: Current sample rate
|
||||
* @sample_format: Current sample format
|
||||
* @asrc_rate: ASRC sample rate used by Back-Ends
|
||||
* @asrc_format: ASRC sample format used by Back-Ends
|
||||
* @dai_fmt: DAI format between CPU and CODEC
|
||||
* @name: Card name
|
||||
*/
|
||||
|
||||
struct fsl_asoc_card_priv {
|
||||
struct snd_soc_dai_link dai_link[3];
|
||||
struct platform_device *pdev;
|
||||
struct codec_priv codec_priv;
|
||||
struct cpu_priv cpu_priv;
|
||||
struct snd_soc_card card;
|
||||
u32 sample_rate;
|
||||
u32 sample_format;
|
||||
u32 asrc_rate;
|
||||
u32 asrc_format;
|
||||
u32 dai_fmt;
|
||||
char name[32];
|
||||
};
|
||||
|
||||
/**
|
||||
* This dapm route map exsits for DPCM link only.
|
||||
* The other routes shall go through Device Tree.
|
||||
*/
|
||||
static const struct snd_soc_dapm_route audio_map[] = {
|
||||
{"CPU-Playback", NULL, "ASRC-Playback"},
|
||||
{"Playback", NULL, "CPU-Playback"},
|
||||
{"ASRC-Capture", NULL, "CPU-Capture"},
|
||||
{"CPU-Capture", NULL, "Capture"},
|
||||
};
|
||||
|
||||
/* Add all possible widgets into here without being redundant */
|
||||
static const struct snd_soc_dapm_widget fsl_asoc_card_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_LINE("Line Out Jack", NULL),
|
||||
SND_SOC_DAPM_LINE("Line In Jack", NULL),
|
||||
SND_SOC_DAPM_HP("Headphone Jack", NULL),
|
||||
SND_SOC_DAPM_SPK("Ext Spk", NULL),
|
||||
SND_SOC_DAPM_MIC("Mic Jack", NULL),
|
||||
SND_SOC_DAPM_MIC("AMIC", NULL),
|
||||
SND_SOC_DAPM_MIC("DMIC", NULL),
|
||||
};
|
||||
|
||||
static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
|
||||
struct cpu_priv *cpu_priv = &priv->cpu_priv;
|
||||
struct device *dev = rtd->card->dev;
|
||||
int ret;
|
||||
|
||||
priv->sample_rate = params_rate(params);
|
||||
priv->sample_format = params_format(params);
|
||||
|
||||
if (priv->card.set_bias_level)
|
||||
return 0;
|
||||
|
||||
/* Specific configurations of DAIs starts from here */
|
||||
ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, cpu_priv->sysclk_id[tx],
|
||||
cpu_priv->sysclk_freq[tx],
|
||||
cpu_priv->sysclk_dir[tx]);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set sysclk for cpu dai\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_ops fsl_asoc_card_ops = {
|
||||
.hw_params = fsl_asoc_card_hw_params,
|
||||
};
|
||||
|
||||
static int be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||
struct snd_interval *rate;
|
||||
struct snd_mask *mask;
|
||||
|
||||
rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
|
||||
rate->max = rate->min = priv->asrc_rate;
|
||||
|
||||
mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
|
||||
snd_mask_none(mask);
|
||||
snd_mask_set(mask, priv->asrc_format);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_link fsl_asoc_card_dai[] = {
|
||||
/* Default ASoC DAI Link*/
|
||||
{
|
||||
.name = "HiFi",
|
||||
.stream_name = "HiFi",
|
||||
.ops = &fsl_asoc_card_ops,
|
||||
},
|
||||
/* DPCM Link between Front-End and Back-End (Optional) */
|
||||
{
|
||||
.name = "HiFi-ASRC-FE",
|
||||
.stream_name = "HiFi-ASRC-FE",
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.dpcm_playback = 1,
|
||||
.dpcm_capture = 1,
|
||||
.dynamic = 1,
|
||||
},
|
||||
{
|
||||
.name = "HiFi-ASRC-BE",
|
||||
.stream_name = "HiFi-ASRC-BE",
|
||||
.platform_name = "snd-soc-dummy",
|
||||
.be_hw_params_fixup = be_hw_params_fixup,
|
||||
.ops = &fsl_asoc_card_ops,
|
||||
.dpcm_playback = 1,
|
||||
.dpcm_capture = 1,
|
||||
.no_pcm = 1,
|
||||
},
|
||||
};
|
||||
|
||||
static int fsl_asoc_card_set_bias_level(struct snd_soc_card *card,
|
||||
struct snd_soc_dapm_context *dapm,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card);
|
||||
struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
|
||||
struct codec_priv *codec_priv = &priv->codec_priv;
|
||||
struct device *dev = card->dev;
|
||||
unsigned int pll_out;
|
||||
int ret;
|
||||
|
||||
if (dapm->dev != codec_dai->dev)
|
||||
return 0;
|
||||
|
||||
switch (level) {
|
||||
case SND_SOC_BIAS_PREPARE:
|
||||
if (dapm->bias_level != SND_SOC_BIAS_STANDBY)
|
||||
break;
|
||||
|
||||
if (priv->sample_format == SNDRV_PCM_FORMAT_S24_LE)
|
||||
pll_out = priv->sample_rate * 384;
|
||||
else
|
||||
pll_out = priv->sample_rate * 256;
|
||||
|
||||
ret = snd_soc_dai_set_pll(codec_dai, codec_priv->pll_id,
|
||||
codec_priv->mclk_id,
|
||||
codec_priv->mclk_freq, pll_out);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to start FLL: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->fll_id,
|
||||
pll_out, SND_SOC_CLOCK_IN);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set SYSCLK: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
|
||||
case SND_SOC_BIAS_STANDBY:
|
||||
if (dapm->bias_level != SND_SOC_BIAS_PREPARE)
|
||||
break;
|
||||
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id,
|
||||
codec_priv->mclk_freq,
|
||||
SND_SOC_CLOCK_IN);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to switch away from FLL: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = snd_soc_dai_set_pll(codec_dai, codec_priv->pll_id, 0, 0, 0);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to stop FLL: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_asoc_card_audmux_init(struct device_node *np,
|
||||
struct fsl_asoc_card_priv *priv)
|
||||
{
|
||||
struct device *dev = &priv->pdev->dev;
|
||||
u32 int_ptcr = 0, ext_ptcr = 0;
|
||||
int int_port, ext_port;
|
||||
int ret;
|
||||
|
||||
ret = of_property_read_u32(np, "mux-int-port", &int_port);
|
||||
if (ret) {
|
||||
dev_err(dev, "mux-int-port missing or invalid\n");
|
||||
return ret;
|
||||
}
|
||||
ret = of_property_read_u32(np, "mux-ext-port", &ext_port);
|
||||
if (ret) {
|
||||
dev_err(dev, "mux-ext-port missing or invalid\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The port numbering in the hardware manual starts at 1, while
|
||||
* the AUDMUX API expects it starts at 0.
|
||||
*/
|
||||
int_port--;
|
||||
ext_port--;
|
||||
|
||||
/*
|
||||
* Use asynchronous mode (6 wires) for all cases.
|
||||
* If only 4 wires are needed, just set SSI into
|
||||
* synchronous mode and enable 4 PADs in IOMUX.
|
||||
*/
|
||||
switch (priv->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
int_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | ext_port) |
|
||||
IMX_AUDMUX_V2_PTCR_RCSEL(8 | ext_port) |
|
||||
IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) |
|
||||
IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) |
|
||||
IMX_AUDMUX_V2_PTCR_RFSDIR |
|
||||
IMX_AUDMUX_V2_PTCR_RCLKDIR |
|
||||
IMX_AUDMUX_V2_PTCR_TFSDIR |
|
||||
IMX_AUDMUX_V2_PTCR_TCLKDIR;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBM_CFS:
|
||||
int_ptcr = IMX_AUDMUX_V2_PTCR_RCSEL(8 | ext_port) |
|
||||
IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) |
|
||||
IMX_AUDMUX_V2_PTCR_RCLKDIR |
|
||||
IMX_AUDMUX_V2_PTCR_TCLKDIR;
|
||||
ext_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | int_port) |
|
||||
IMX_AUDMUX_V2_PTCR_TFSEL(int_port) |
|
||||
IMX_AUDMUX_V2_PTCR_RFSDIR |
|
||||
IMX_AUDMUX_V2_PTCR_TFSDIR;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBS_CFM:
|
||||
int_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | ext_port) |
|
||||
IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) |
|
||||
IMX_AUDMUX_V2_PTCR_RFSDIR |
|
||||
IMX_AUDMUX_V2_PTCR_TFSDIR;
|
||||
ext_ptcr = IMX_AUDMUX_V2_PTCR_RCSEL(8 | int_port) |
|
||||
IMX_AUDMUX_V2_PTCR_TCSEL(int_port) |
|
||||
IMX_AUDMUX_V2_PTCR_RCLKDIR |
|
||||
IMX_AUDMUX_V2_PTCR_TCLKDIR;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
ext_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | int_port) |
|
||||
IMX_AUDMUX_V2_PTCR_RCSEL(8 | int_port) |
|
||||
IMX_AUDMUX_V2_PTCR_TFSEL(int_port) |
|
||||
IMX_AUDMUX_V2_PTCR_TCSEL(int_port) |
|
||||
IMX_AUDMUX_V2_PTCR_RFSDIR |
|
||||
IMX_AUDMUX_V2_PTCR_RCLKDIR |
|
||||
IMX_AUDMUX_V2_PTCR_TFSDIR |
|
||||
IMX_AUDMUX_V2_PTCR_TCLKDIR;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Asynchronous mode can not be set along with RCLKDIR */
|
||||
ret = imx_audmux_v2_configure_port(int_port, 0,
|
||||
IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port));
|
||||
if (ret) {
|
||||
dev_err(dev, "audmux internal port setup failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = imx_audmux_v2_configure_port(int_port, int_ptcr,
|
||||
IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port));
|
||||
if (ret) {
|
||||
dev_err(dev, "audmux internal port setup failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = imx_audmux_v2_configure_port(ext_port, 0,
|
||||
IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
|
||||
if (ret) {
|
||||
dev_err(dev, "audmux external port setup failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = imx_audmux_v2_configure_port(ext_port, ext_ptcr,
|
||||
IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
|
||||
if (ret) {
|
||||
dev_err(dev, "audmux external port setup failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_asoc_card_late_probe(struct snd_soc_card *card)
|
||||
{
|
||||
struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card);
|
||||
struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
|
||||
struct codec_priv *codec_priv = &priv->codec_priv;
|
||||
struct device *dev = card->dev;
|
||||
int ret;
|
||||
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id,
|
||||
codec_priv->mclk_freq, SND_SOC_CLOCK_IN);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set sysclk in %s\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_asoc_card_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *cpu_np, *codec_np, *asrc_np;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct platform_device *asrc_pdev = NULL;
|
||||
struct platform_device *cpu_pdev;
|
||||
struct fsl_asoc_card_priv *priv;
|
||||
struct i2c_client *codec_dev;
|
||||
struct clk *codec_clk;
|
||||
u32 width;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
cpu_np = of_parse_phandle(np, "audio-cpu", 0);
|
||||
/* Give a chance to old DT binding */
|
||||
if (!cpu_np)
|
||||
cpu_np = of_parse_phandle(np, "ssi-controller", 0);
|
||||
codec_np = of_parse_phandle(np, "audio-codec", 0);
|
||||
if (!cpu_np || !codec_np) {
|
||||
dev_err(&pdev->dev, "phandle missing or invalid\n");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
cpu_pdev = of_find_device_by_node(cpu_np);
|
||||
if (!cpu_pdev) {
|
||||
dev_err(&pdev->dev, "failed to find CPU DAI device\n");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
codec_dev = of_find_i2c_device_by_node(codec_np);
|
||||
if (!codec_dev) {
|
||||
dev_err(&pdev->dev, "failed to find codec platform device\n");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
asrc_np = of_parse_phandle(np, "audio-asrc", 0);
|
||||
if (asrc_np)
|
||||
asrc_pdev = of_find_device_by_node(asrc_np);
|
||||
|
||||
/* Get the MCLK rate only, and leave it controlled by CODEC drivers */
|
||||
codec_clk = clk_get(&codec_dev->dev, NULL);
|
||||
if (!IS_ERR(codec_clk)) {
|
||||
priv->codec_priv.mclk_freq = clk_get_rate(codec_clk);
|
||||
clk_put(codec_clk);
|
||||
}
|
||||
|
||||
/* Default sample rate and format, will be updated in hw_params() */
|
||||
priv->sample_rate = 44100;
|
||||
priv->sample_format = SNDRV_PCM_FORMAT_S16_LE;
|
||||
|
||||
/* Assign a default DAI format, and allow each card to overwrite it */
|
||||
priv->dai_fmt = DAI_FMT_BASE;
|
||||
|
||||
/* Diversify the card configurations */
|
||||
if (of_device_is_compatible(np, "fsl,imx-audio-cs42888")) {
|
||||
priv->card.set_bias_level = NULL;
|
||||
priv->cpu_priv.sysclk_freq[TX] = priv->codec_priv.mclk_freq;
|
||||
priv->cpu_priv.sysclk_freq[RX] = priv->codec_priv.mclk_freq;
|
||||
priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT;
|
||||
priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT;
|
||||
priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
|
||||
} else if (of_device_is_compatible(np, "fsl,imx-audio-sgtl5000")) {
|
||||
priv->codec_priv.mclk_id = SGTL5000_SYSCLK;
|
||||
priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
|
||||
} else if (of_device_is_compatible(np, "fsl,imx-audio-wm8962")) {
|
||||
priv->card.set_bias_level = fsl_asoc_card_set_bias_level;
|
||||
priv->codec_priv.mclk_id = WM8962_SYSCLK_MCLK;
|
||||
priv->codec_priv.fll_id = WM8962_SYSCLK_FLL;
|
||||
priv->codec_priv.pll_id = WM8962_FLL;
|
||||
priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
|
||||
} else {
|
||||
dev_err(&pdev->dev, "unknown Device Tree compatible\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Common settings for corresponding Freescale CPU DAI driver */
|
||||
if (strstr(cpu_np->name, "ssi")) {
|
||||
/* Only SSI needs to configure AUDMUX */
|
||||
ret = fsl_asoc_card_audmux_init(np, priv);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to init audmux\n");
|
||||
goto asrc_fail;
|
||||
}
|
||||
} else if (strstr(cpu_np->name, "esai")) {
|
||||
priv->cpu_priv.sysclk_id[1] = ESAI_HCKT_EXTAL;
|
||||
priv->cpu_priv.sysclk_id[0] = ESAI_HCKR_EXTAL;
|
||||
} else if (strstr(cpu_np->name, "sai")) {
|
||||
priv->cpu_priv.sysclk_id[1] = FSL_SAI_CLK_MAST1;
|
||||
priv->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1;
|
||||
}
|
||||
|
||||
sprintf(priv->name, "%s-audio", codec_dev->name);
|
||||
|
||||
/* Initialize sound card */
|
||||
priv->pdev = pdev;
|
||||
priv->card.dev = &pdev->dev;
|
||||
priv->card.name = priv->name;
|
||||
priv->card.dai_link = priv->dai_link;
|
||||
priv->card.dapm_routes = audio_map;
|
||||
priv->card.late_probe = fsl_asoc_card_late_probe;
|
||||
priv->card.num_dapm_routes = ARRAY_SIZE(audio_map);
|
||||
priv->card.dapm_widgets = fsl_asoc_card_dapm_widgets;
|
||||
priv->card.num_dapm_widgets = ARRAY_SIZE(fsl_asoc_card_dapm_widgets);
|
||||
|
||||
memcpy(priv->dai_link, fsl_asoc_card_dai,
|
||||
sizeof(struct snd_soc_dai_link) * ARRAY_SIZE(priv->dai_link));
|
||||
|
||||
/* Normal DAI Link */
|
||||
priv->dai_link[0].cpu_of_node = cpu_np;
|
||||
priv->dai_link[0].codec_of_node = codec_np;
|
||||
priv->dai_link[0].codec_dai_name = codec_dev->name;
|
||||
priv->dai_link[0].platform_of_node = cpu_np;
|
||||
priv->dai_link[0].dai_fmt = priv->dai_fmt;
|
||||
priv->card.num_links = 1;
|
||||
|
||||
if (asrc_pdev) {
|
||||
/* DPCM DAI Links only if ASRC exsits */
|
||||
priv->dai_link[1].cpu_of_node = asrc_np;
|
||||
priv->dai_link[1].platform_of_node = asrc_np;
|
||||
priv->dai_link[2].codec_dai_name = codec_dev->name;
|
||||
priv->dai_link[2].codec_of_node = codec_np;
|
||||
priv->dai_link[2].cpu_of_node = cpu_np;
|
||||
priv->dai_link[2].dai_fmt = priv->dai_fmt;
|
||||
priv->card.num_links = 3;
|
||||
|
||||
ret = of_property_read_u32(asrc_np, "fsl,asrc-rate",
|
||||
&priv->asrc_rate);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to get output rate\n");
|
||||
ret = -EINVAL;
|
||||
goto asrc_fail;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(asrc_np, "fsl,asrc-width", &width);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to get output rate\n");
|
||||
ret = -EINVAL;
|
||||
goto asrc_fail;
|
||||
}
|
||||
|
||||
if (width == 24)
|
||||
priv->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
|
||||
else
|
||||
priv->asrc_format = SNDRV_PCM_FORMAT_S16_LE;
|
||||
}
|
||||
|
||||
/* Finish card registering */
|
||||
platform_set_drvdata(pdev, priv);
|
||||
snd_soc_card_set_drvdata(&priv->card, priv);
|
||||
|
||||
ret = devm_snd_soc_register_card(&pdev->dev, &priv->card);
|
||||
if (ret)
|
||||
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
|
||||
|
||||
asrc_fail:
|
||||
of_node_put(asrc_np);
|
||||
fail:
|
||||
of_node_put(codec_np);
|
||||
of_node_put(cpu_np);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id fsl_asoc_card_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx-audio-cs42888", },
|
||||
{ .compatible = "fsl,imx-audio-sgtl5000", },
|
||||
{ .compatible = "fsl,imx-audio-wm8962", },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver fsl_asoc_card_driver = {
|
||||
.probe = fsl_asoc_card_probe,
|
||||
.driver = {
|
||||
.name = "fsl-asoc-card",
|
||||
.pm = &snd_soc_pm_ops,
|
||||
.of_match_table = fsl_asoc_card_dt_ids,
|
||||
},
|
||||
};
|
||||
module_platform_driver(fsl_asoc_card_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Freescale Generic ASoC Sound Card driver with ASRC");
|
||||
MODULE_AUTHOR("Nicolin Chen <nicoleotsuka@gmail.com>");
|
||||
MODULE_ALIAS("platform:fsl-asoc-card");
|
||||
MODULE_LICENSE("GPL");
|
@ -684,7 +684,7 @@ static bool fsl_asrc_writeable_reg(struct device *dev, unsigned int reg)
|
||||
}
|
||||
}
|
||||
|
||||
static struct regmap_config fsl_asrc_regmap_config = {
|
||||
static const struct regmap_config fsl_asrc_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
@ -802,10 +802,6 @@ static int fsl_asrc_probe(struct platform_device *pdev)
|
||||
|
||||
asrc_priv->paddr = res->start;
|
||||
|
||||
/* Register regmap and let it prepare core clock */
|
||||
if (of_property_read_bool(np, "big-endian"))
|
||||
fsl_asrc_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG;
|
||||
|
||||
asrc_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "mem", regs,
|
||||
&fsl_asrc_regmap_config);
|
||||
if (IS_ERR(asrc_priv->regmap)) {
|
||||
|
@ -37,6 +37,7 @@
|
||||
* @fsysclk: system clock source to derive HCK, SCK and FS
|
||||
* @fifo_depth: depth of tx/rx FIFO
|
||||
* @slot_width: width of each DAI slot
|
||||
* @slots: number of slots
|
||||
* @hck_rate: clock rate of desired HCKx clock
|
||||
* @sck_rate: clock rate of desired SCKx clock
|
||||
* @hck_dir: the direction of HCKx pads
|
||||
@ -55,6 +56,7 @@ struct fsl_esai {
|
||||
struct clk *fsysclk;
|
||||
u32 fifo_depth;
|
||||
u32 slot_width;
|
||||
u32 slots;
|
||||
u32 hck_rate[2];
|
||||
u32 sck_rate[2];
|
||||
bool hck_dir[2];
|
||||
@ -362,6 +364,7 @@ static int fsl_esai_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask,
|
||||
ESAI_xSMB_xS_MASK, ESAI_xSMB_xS(rx_mask));
|
||||
|
||||
esai_priv->slot_width = slot_width;
|
||||
esai_priv->slots = slots;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -509,10 +512,11 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
|
||||
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
|
||||
u32 width = snd_pcm_format_width(params_format(params));
|
||||
u32 channels = params_channels(params);
|
||||
u32 pins = DIV_ROUND_UP(channels, esai_priv->slots);
|
||||
u32 bclk, mask, val;
|
||||
int ret;
|
||||
|
||||
bclk = params_rate(params) * esai_priv->slot_width * 2;
|
||||
bclk = params_rate(params) * esai_priv->slot_width * esai_priv->slots;
|
||||
|
||||
ret = fsl_esai_set_bclk(dai, tx, bclk);
|
||||
if (ret)
|
||||
@ -529,7 +533,7 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
|
||||
mask = ESAI_xFCR_xFR_MASK | ESAI_xFCR_xWA_MASK | ESAI_xFCR_xFWM_MASK |
|
||||
(tx ? ESAI_xFCR_TE_MASK | ESAI_xFCR_TIEN : ESAI_xFCR_RE_MASK);
|
||||
val = ESAI_xFCR_xWA(width) | ESAI_xFCR_xFWM(esai_priv->fifo_depth) |
|
||||
(tx ? ESAI_xFCR_TE(channels) | ESAI_xFCR_TIEN : ESAI_xFCR_RE(channels));
|
||||
(tx ? ESAI_xFCR_TE(pins) | ESAI_xFCR_TIEN : ESAI_xFCR_RE(pins));
|
||||
|
||||
regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), mask, val);
|
||||
|
||||
@ -564,6 +568,7 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
|
||||
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
|
||||
u8 i, channels = substream->runtime->channels;
|
||||
u32 pins = DIV_ROUND_UP(channels, esai_priv->slots);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
@ -578,7 +583,7 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
|
||||
regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),
|
||||
tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK,
|
||||
tx ? ESAI_xCR_TE(channels) : ESAI_xCR_RE(channels));
|
||||
tx ? ESAI_xCR_TE(pins) : ESAI_xCR_RE(pins));
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
@ -705,7 +710,7 @@ static bool fsl_esai_writeable_reg(struct device *dev, unsigned int reg)
|
||||
}
|
||||
}
|
||||
|
||||
static struct regmap_config fsl_esai_regmap_config = {
|
||||
static const struct regmap_config fsl_esai_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
@ -731,9 +736,6 @@ static int fsl_esai_probe(struct platform_device *pdev)
|
||||
esai_priv->pdev = pdev;
|
||||
strcpy(esai_priv->name, np->name);
|
||||
|
||||
if (of_property_read_bool(np, "big-endian"))
|
||||
fsl_esai_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG;
|
||||
|
||||
/* Get the addresses and IRQ */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
@ -781,6 +783,9 @@ static int fsl_esai_probe(struct platform_device *pdev)
|
||||
/* Set a default slot size */
|
||||
esai_priv->slot_width = 32;
|
||||
|
||||
/* Set a default slot number */
|
||||
esai_priv->slots = 2;
|
||||
|
||||
/* Set a default master/slave state */
|
||||
esai_priv->slave_mode = true;
|
||||
|
||||
|
@ -130,8 +130,8 @@
|
||||
#define ESAI_xFCR_RE_WIDTH 4
|
||||
#define ESAI_xFCR_TE_MASK (((1 << ESAI_xFCR_TE_WIDTH) - 1) << ESAI_xFCR_xE_SHIFT)
|
||||
#define ESAI_xFCR_RE_MASK (((1 << ESAI_xFCR_RE_WIDTH) - 1) << ESAI_xFCR_xE_SHIFT)
|
||||
#define ESAI_xFCR_TE(x) ((ESAI_xFCR_TE_MASK >> (ESAI_xFCR_TE_WIDTH - ((x + 1) >> 1))) & ESAI_xFCR_TE_MASK)
|
||||
#define ESAI_xFCR_RE(x) ((ESAI_xFCR_RE_MASK >> (ESAI_xFCR_RE_WIDTH - ((x + 1) >> 1))) & ESAI_xFCR_RE_MASK)
|
||||
#define ESAI_xFCR_TE(x) ((ESAI_xFCR_TE_MASK >> (ESAI_xFCR_TE_WIDTH - x)) & ESAI_xFCR_TE_MASK)
|
||||
#define ESAI_xFCR_RE(x) ((ESAI_xFCR_RE_MASK >> (ESAI_xFCR_RE_WIDTH - x)) & ESAI_xFCR_RE_MASK)
|
||||
#define ESAI_xFCR_xFR_SHIFT 1
|
||||
#define ESAI_xFCR_xFR_MASK (1 << ESAI_xFCR_xFR_SHIFT)
|
||||
#define ESAI_xFCR_xFR (1 << ESAI_xFCR_xFR_SHIFT)
|
||||
@ -272,8 +272,8 @@
|
||||
#define ESAI_xCR_RE_WIDTH 4
|
||||
#define ESAI_xCR_TE_MASK (((1 << ESAI_xCR_TE_WIDTH) - 1) << ESAI_xCR_xE_SHIFT)
|
||||
#define ESAI_xCR_RE_MASK (((1 << ESAI_xCR_RE_WIDTH) - 1) << ESAI_xCR_xE_SHIFT)
|
||||
#define ESAI_xCR_TE(x) ((ESAI_xCR_TE_MASK >> (ESAI_xCR_TE_WIDTH - ((x + 1) >> 1))) & ESAI_xCR_TE_MASK)
|
||||
#define ESAI_xCR_RE(x) ((ESAI_xCR_RE_MASK >> (ESAI_xCR_RE_WIDTH - ((x + 1) >> 1))) & ESAI_xCR_RE_MASK)
|
||||
#define ESAI_xCR_TE(x) ((ESAI_xCR_TE_MASK >> (ESAI_xCR_TE_WIDTH - x)) & ESAI_xCR_TE_MASK)
|
||||
#define ESAI_xCR_RE(x) ((ESAI_xCR_RE_MASK >> (ESAI_xCR_RE_WIDTH - x)) & ESAI_xCR_RE_MASK)
|
||||
|
||||
/*
|
||||
* Transmit Clock Control Register -- REG_ESAI_TCCR 0xD8
|
||||
|
@ -175,7 +175,7 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
|
||||
bool tx = fsl_dir == FSL_FMT_TRANSMITTER;
|
||||
u32 val_cr2 = 0, val_cr4 = 0;
|
||||
|
||||
if (!sai->big_endian_data)
|
||||
if (!sai->is_lsb_first)
|
||||
val_cr4 |= FSL_SAI_CR4_MF;
|
||||
|
||||
/* DAI mode */
|
||||
@ -304,7 +304,7 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
|
||||
val_cr5 |= FSL_SAI_CR5_WNW(word_width);
|
||||
val_cr5 |= FSL_SAI_CR5_W0W(word_width);
|
||||
|
||||
if (sai->big_endian_data)
|
||||
if (sai->is_lsb_first)
|
||||
val_cr5 |= FSL_SAI_CR5_FBT(0);
|
||||
else
|
||||
val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1);
|
||||
@ -330,13 +330,13 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
u32 xcsr, count = 100;
|
||||
|
||||
/*
|
||||
* The transmitter bit clock and frame sync are to be
|
||||
* used by both the transmitter and receiver.
|
||||
* Asynchronous mode: Clear SYNC for both Tx and Rx.
|
||||
* Rx sync with Tx clocks: Clear SYNC for Tx, set it for Rx.
|
||||
* Tx sync with Rx clocks: Clear SYNC for Rx, set it for Tx.
|
||||
*/
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_SYNC,
|
||||
~FSL_SAI_CR2_SYNC);
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_SYNC, 0);
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_RCR2, FSL_SAI_CR2_SYNC,
|
||||
FSL_SAI_CR2_SYNC);
|
||||
sai->synchronous[RX] ? FSL_SAI_CR2_SYNC : 0);
|
||||
|
||||
/*
|
||||
* It is recommended that the transmitter is the last enabled
|
||||
@ -437,8 +437,13 @@ static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct fsl_sai *sai = dev_get_drvdata(cpu_dai->dev);
|
||||
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_TCSR, 0xffffffff, 0x0);
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_RCSR, 0xffffffff, 0x0);
|
||||
/* Software Reset for both Tx and Rx */
|
||||
regmap_write(sai->regmap, FSL_SAI_TCSR, FSL_SAI_CSR_SR);
|
||||
regmap_write(sai->regmap, FSL_SAI_RCSR, FSL_SAI_CSR_SR);
|
||||
/* Clear SR bit to finish the reset */
|
||||
regmap_write(sai->regmap, FSL_SAI_TCSR, 0);
|
||||
regmap_write(sai->regmap, FSL_SAI_RCSR, 0);
|
||||
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_TCR1, FSL_SAI_CR1_RFW_MASK,
|
||||
FSL_SAI_MAXBURST_TX * 2);
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_RCR1, FSL_SAI_CR1_RFW_MASK,
|
||||
@ -539,7 +544,7 @@ static bool fsl_sai_writeable_reg(struct device *dev, unsigned int reg)
|
||||
}
|
||||
}
|
||||
|
||||
static struct regmap_config fsl_sai_regmap_config = {
|
||||
static const struct regmap_config fsl_sai_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
@ -568,11 +573,7 @@ static int fsl_sai_probe(struct platform_device *pdev)
|
||||
if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx6sx-sai"))
|
||||
sai->sai_on_imx = true;
|
||||
|
||||
sai->big_endian_regs = of_property_read_bool(np, "big-endian-regs");
|
||||
if (sai->big_endian_regs)
|
||||
fsl_sai_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG;
|
||||
|
||||
sai->big_endian_data = of_property_read_bool(np, "big-endian-data");
|
||||
sai->is_lsb_first = of_property_read_bool(np, "lsb-first");
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(&pdev->dev, res);
|
||||
@ -621,6 +622,33 @@ static int fsl_sai_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Sync Tx with Rx as default by following old DT binding */
|
||||
sai->synchronous[RX] = true;
|
||||
sai->synchronous[TX] = false;
|
||||
fsl_sai_dai.symmetric_rates = 1;
|
||||
fsl_sai_dai.symmetric_channels = 1;
|
||||
fsl_sai_dai.symmetric_samplebits = 1;
|
||||
|
||||
if (of_find_property(np, "fsl,sai-synchronous-rx", NULL) &&
|
||||
of_find_property(np, "fsl,sai-asynchronous", NULL)) {
|
||||
/* error out if both synchronous and asynchronous are present */
|
||||
dev_err(&pdev->dev, "invalid binding for synchronous mode\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (of_find_property(np, "fsl,sai-synchronous-rx", NULL)) {
|
||||
/* Sync Rx with Tx */
|
||||
sai->synchronous[RX] = false;
|
||||
sai->synchronous[TX] = true;
|
||||
} else if (of_find_property(np, "fsl,sai-asynchronous", NULL)) {
|
||||
/* Discard all settings for asynchronous mode */
|
||||
sai->synchronous[RX] = false;
|
||||
sai->synchronous[TX] = false;
|
||||
fsl_sai_dai.symmetric_rates = 0;
|
||||
fsl_sai_dai.symmetric_channels = 0;
|
||||
fsl_sai_dai.symmetric_samplebits = 0;
|
||||
}
|
||||
|
||||
sai->dma_params_rx.addr = res->start + FSL_SAI_RDR;
|
||||
sai->dma_params_tx.addr = res->start + FSL_SAI_TDR;
|
||||
sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX;
|
||||
|
@ -48,6 +48,7 @@
|
||||
/* SAI Transmit/Recieve Control Register */
|
||||
#define FSL_SAI_CSR_TERE BIT(31)
|
||||
#define FSL_SAI_CSR_FR BIT(25)
|
||||
#define FSL_SAI_CSR_SR BIT(24)
|
||||
#define FSL_SAI_CSR_xF_SHIFT 16
|
||||
#define FSL_SAI_CSR_xF_W_SHIFT 18
|
||||
#define FSL_SAI_CSR_xF_MASK (0x1f << FSL_SAI_CSR_xF_SHIFT)
|
||||
@ -131,13 +132,16 @@ struct fsl_sai {
|
||||
struct clk *bus_clk;
|
||||
struct clk *mclk_clk[FSL_SAI_MCLK_MAX];
|
||||
|
||||
bool big_endian_regs;
|
||||
bool big_endian_data;
|
||||
bool is_lsb_first;
|
||||
bool is_dsp_mode;
|
||||
bool sai_on_imx;
|
||||
bool synchronous[2];
|
||||
|
||||
struct snd_dmaengine_dai_dma_data dma_params_rx;
|
||||
struct snd_dmaengine_dai_dma_data dma_params_tx;
|
||||
};
|
||||
|
||||
#define TX 1
|
||||
#define RX 0
|
||||
|
||||
#endif /* __FSL_SAI_H */
|
||||
|
@ -15,7 +15,6 @@
|
||||
|
||||
#include <linux/bitrev.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-private.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
@ -1040,7 +1039,7 @@ static bool fsl_spdif_writeable_reg(struct device *dev, unsigned int reg)
|
||||
}
|
||||
}
|
||||
|
||||
static struct regmap_config fsl_spdif_regmap_config = {
|
||||
static const struct regmap_config fsl_spdif_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
@ -1184,9 +1183,6 @@ static int fsl_spdif_probe(struct platform_device *pdev)
|
||||
memcpy(&spdif_priv->cpu_dai_drv, &fsl_spdif_dai, sizeof(fsl_spdif_dai));
|
||||
spdif_priv->cpu_dai_drv.name = spdif_priv->name;
|
||||
|
||||
if (of_property_read_bool(np, "big-endian"))
|
||||
fsl_spdif_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG;
|
||||
|
||||
/* Get the addresses and IRQ */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
|
@ -169,6 +169,7 @@ struct fsl_ssi_private {
|
||||
u8 i2s_mode;
|
||||
bool use_dma;
|
||||
bool use_dual_fifo;
|
||||
bool has_ipg_clk_name;
|
||||
unsigned int fifo_depth;
|
||||
struct fsl_ssi_rxtx_reg_val rxtx_reg_val;
|
||||
|
||||
@ -259,6 +260,11 @@ static bool fsl_ssi_is_i2s_master(struct fsl_ssi_private *ssi_private)
|
||||
SND_SOC_DAIFMT_CBS_CFS;
|
||||
}
|
||||
|
||||
static bool fsl_ssi_is_i2s_cbm_cfs(struct fsl_ssi_private *ssi_private)
|
||||
{
|
||||
return (ssi_private->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) ==
|
||||
SND_SOC_DAIFMT_CBM_CFS;
|
||||
}
|
||||
/**
|
||||
* fsl_ssi_isr: SSI interrupt handler
|
||||
*
|
||||
@ -525,6 +531,11 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct fsl_ssi_private *ssi_private =
|
||||
snd_soc_dai_get_drvdata(rtd->cpu_dai);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(ssi_private->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* When using dual fifo mode, it is safer to ensure an even period
|
||||
* size. If appearing to an odd number while DMA always starts its
|
||||
@ -538,6 +549,21 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fsl_ssi_shutdown: shutdown the SSI
|
||||
*
|
||||
*/
|
||||
static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct fsl_ssi_private *ssi_private =
|
||||
snd_soc_dai_get_drvdata(rtd->cpu_dai);
|
||||
|
||||
clk_disable_unprepare(ssi_private->clk);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* fsl_ssi_set_bclk - configure Digital Audio Interface bit clock
|
||||
*
|
||||
@ -705,6 +731,23 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
|
||||
}
|
||||
}
|
||||
|
||||
if (!fsl_ssi_is_ac97(ssi_private)) {
|
||||
u8 i2smode;
|
||||
/*
|
||||
* Switch to normal net mode in order to have a frame sync
|
||||
* signal every 32 bits instead of 16 bits
|
||||
*/
|
||||
if (fsl_ssi_is_i2s_cbm_cfs(ssi_private) && sample_size == 16)
|
||||
i2smode = CCSR_SSI_SCR_I2S_MODE_NORMAL |
|
||||
CCSR_SSI_SCR_NET;
|
||||
else
|
||||
i2smode = ssi_private->i2s_mode;
|
||||
|
||||
regmap_update_bits(regs, CCSR_SSI_SCR,
|
||||
CCSR_SSI_SCR_NET | CCSR_SSI_SCR_I2S_MODE_MASK,
|
||||
channels == 1 ? 0 : i2smode);
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: The documentation says that SxCCR[WL] should not be
|
||||
* modified while the SSI is enabled. The only time this can
|
||||
@ -724,11 +767,6 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
|
||||
regmap_update_bits(regs, CCSR_SSI_SRCCR, CCSR_SSI_SxCCR_WL_MASK,
|
||||
wl);
|
||||
|
||||
if (!fsl_ssi_is_ac97(ssi_private))
|
||||
regmap_update_bits(regs, CCSR_SSI_SCR,
|
||||
CCSR_SSI_SCR_NET | CCSR_SSI_SCR_I2S_MODE_MASK,
|
||||
channels == 1 ? 0 : ssi_private->i2s_mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -781,6 +819,7 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev,
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBM_CFS:
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
ssi_private->i2s_mode |= CCSR_SSI_SCR_I2S_MODE_MASTER;
|
||||
regmap_update_bits(regs, CCSR_SSI_STCCR,
|
||||
@ -854,6 +893,11 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev,
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
scr &= ~CCSR_SSI_SCR_SYS_CLK_EN;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBM_CFS:
|
||||
strcr &= ~CCSR_SSI_STCR_TXDIR;
|
||||
strcr |= CCSR_SSI_STCR_TFDIR;
|
||||
scr &= ~CCSR_SSI_SCR_SYS_CLK_EN;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -1021,6 +1065,7 @@ static int fsl_ssi_dai_probe(struct snd_soc_dai *dai)
|
||||
|
||||
static const struct snd_soc_dai_ops fsl_ssi_dai_ops = {
|
||||
.startup = fsl_ssi_startup,
|
||||
.shutdown = fsl_ssi_shutdown,
|
||||
.hw_params = fsl_ssi_hw_params,
|
||||
.hw_free = fsl_ssi_hw_free,
|
||||
.set_fmt = fsl_ssi_set_dai_fmt,
|
||||
@ -1146,17 +1191,22 @@ static int fsl_ssi_imx_probe(struct platform_device *pdev,
|
||||
u32 dmas[4];
|
||||
int ret;
|
||||
|
||||
ssi_private->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (ssi_private->has_ipg_clk_name)
|
||||
ssi_private->clk = devm_clk_get(&pdev->dev, "ipg");
|
||||
else
|
||||
ssi_private->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(ssi_private->clk)) {
|
||||
ret = PTR_ERR(ssi_private->clk);
|
||||
dev_err(&pdev->dev, "could not get clock: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(ssi_private->clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret);
|
||||
return ret;
|
||||
if (!ssi_private->has_ipg_clk_name) {
|
||||
ret = clk_prepare_enable(ssi_private->clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* For those SLAVE implementations, we ingore non-baudclk cases
|
||||
@ -1214,8 +1264,9 @@ static int fsl_ssi_imx_probe(struct platform_device *pdev,
|
||||
return 0;
|
||||
|
||||
error_pcm:
|
||||
clk_disable_unprepare(ssi_private->clk);
|
||||
|
||||
if (!ssi_private->has_ipg_clk_name)
|
||||
clk_disable_unprepare(ssi_private->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1224,7 +1275,8 @@ static void fsl_ssi_imx_clean(struct platform_device *pdev,
|
||||
{
|
||||
if (!ssi_private->use_dma)
|
||||
imx_pcm_fiq_exit(pdev);
|
||||
clk_disable_unprepare(ssi_private->clk);
|
||||
if (!ssi_private->has_ipg_clk_name)
|
||||
clk_disable_unprepare(ssi_private->clk);
|
||||
}
|
||||
|
||||
static int fsl_ssi_probe(struct platform_device *pdev)
|
||||
@ -1263,9 +1315,6 @@ static int fsl_ssi_probe(struct platform_device *pdev)
|
||||
if (sprop) {
|
||||
if (!strcmp(sprop, "ac97-slave"))
|
||||
ssi_private->dai_fmt = SND_SOC_DAIFMT_AC97;
|
||||
else if (!strcmp(sprop, "i2s-slave"))
|
||||
ssi_private->dai_fmt = SND_SOC_DAIFMT_I2S |
|
||||
SND_SOC_DAIFMT_CBM_CFM;
|
||||
}
|
||||
|
||||
ssi_private->use_dma = !of_property_read_bool(np,
|
||||
@ -1299,8 +1348,16 @@ static int fsl_ssi_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ssi_private->regs = devm_regmap_init_mmio(&pdev->dev, iomem,
|
||||
ret = of_property_match_string(np, "clock-names", "ipg");
|
||||
if (ret < 0) {
|
||||
ssi_private->has_ipg_clk_name = false;
|
||||
ssi_private->regs = devm_regmap_init_mmio(&pdev->dev, iomem,
|
||||
&fsl_ssi_regconfig);
|
||||
} else {
|
||||
ssi_private->has_ipg_clk_name = true;
|
||||
ssi_private->regs = devm_regmap_init_mmio_clk(&pdev->dev,
|
||||
"ipg", iomem, &fsl_ssi_regconfig);
|
||||
}
|
||||
if (IS_ERR(ssi_private->regs)) {
|
||||
dev_err(&pdev->dev, "Failed to init register map\n");
|
||||
return PTR_ERR(ssi_private->regs);
|
||||
|
232
sound/soc/fsl/imx-es8328.c
Normal file
232
sound/soc/fsl/imx-es8328.c
Normal file
@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright 2012 Freescale Semiconductor, Inc.
|
||||
* Copyright 2012 Linaro Ltd.
|
||||
*
|
||||
* The code contained herein is licensed under the GNU General Public
|
||||
* License. You may obtain a copy of the GNU General Public License
|
||||
* Version 2 or later at the following locations:
|
||||
*
|
||||
* http://www.opensource.org/licenses/gpl-license.html
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/jack.h>
|
||||
|
||||
#include "imx-audmux.h"
|
||||
|
||||
#define DAI_NAME_SIZE 32
|
||||
#define MUX_PORT_MAX 7
|
||||
|
||||
struct imx_es8328_data {
|
||||
struct device *dev;
|
||||
struct snd_soc_dai_link dai;
|
||||
struct snd_soc_card card;
|
||||
char codec_dai_name[DAI_NAME_SIZE];
|
||||
char platform_name[DAI_NAME_SIZE];
|
||||
int jack_gpio;
|
||||
};
|
||||
|
||||
static struct snd_soc_jack_gpio headset_jack_gpios[] = {
|
||||
{
|
||||
.gpio = -1,
|
||||
.name = "headset-gpio",
|
||||
.report = SND_JACK_HEADSET,
|
||||
.invert = 0,
|
||||
.debounce_time = 200,
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_jack headset_jack;
|
||||
|
||||
static int imx_es8328_dai_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct imx_es8328_data *data = container_of(rtd->card,
|
||||
struct imx_es8328_data, card);
|
||||
int ret = 0;
|
||||
|
||||
/* Headphone jack detection */
|
||||
if (gpio_is_valid(data->jack_gpio)) {
|
||||
ret = snd_soc_jack_new(rtd->codec, "Headphone",
|
||||
SND_JACK_HEADPHONE | SND_JACK_BTN_0,
|
||||
&headset_jack);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
headset_jack_gpios[0].gpio = data->jack_gpio;
|
||||
ret = snd_soc_jack_add_gpios(&headset_jack,
|
||||
ARRAY_SIZE(headset_jack_gpios),
|
||||
headset_jack_gpios);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dapm_widget imx_es8328_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_MIC("Mic Jack", NULL),
|
||||
SND_SOC_DAPM_HP("Headphone", NULL),
|
||||
SND_SOC_DAPM_SPK("Speaker", NULL),
|
||||
SND_SOC_DAPM_REGULATOR_SUPPLY("audio-amp", 1, 0),
|
||||
};
|
||||
|
||||
static int imx_es8328_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device_node *ssi_np, *codec_np;
|
||||
struct platform_device *ssi_pdev;
|
||||
struct imx_es8328_data *data;
|
||||
u32 int_port, ext_port;
|
||||
int ret;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
ret = of_property_read_u32(np, "mux-int-port", &int_port);
|
||||
if (ret) {
|
||||
dev_err(dev, "mux-int-port missing or invalid\n");
|
||||
goto fail;
|
||||
}
|
||||
if (int_port > MUX_PORT_MAX || int_port == 0) {
|
||||
dev_err(dev, "mux-int-port: hardware only has %d mux ports\n",
|
||||
MUX_PORT_MAX);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "mux-ext-port", &ext_port);
|
||||
if (ret) {
|
||||
dev_err(dev, "mux-ext-port missing or invalid\n");
|
||||
goto fail;
|
||||
}
|
||||
if (ext_port > MUX_PORT_MAX || ext_port == 0) {
|
||||
dev_err(dev, "mux-ext-port: hardware only has %d mux ports\n",
|
||||
MUX_PORT_MAX);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* The port numbering in the hardware manual starts at 1, while
|
||||
* the audmux API expects it starts at 0.
|
||||
*/
|
||||
int_port--;
|
||||
ext_port--;
|
||||
ret = imx_audmux_v2_configure_port(int_port,
|
||||
IMX_AUDMUX_V2_PTCR_SYN |
|
||||
IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) |
|
||||
IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) |
|
||||
IMX_AUDMUX_V2_PTCR_TFSDIR |
|
||||
IMX_AUDMUX_V2_PTCR_TCLKDIR,
|
||||
IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port));
|
||||
if (ret) {
|
||||
dev_err(dev, "audmux internal port setup failed\n");
|
||||
return ret;
|
||||
}
|
||||
ret = imx_audmux_v2_configure_port(ext_port,
|
||||
IMX_AUDMUX_V2_PTCR_SYN,
|
||||
IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
|
||||
if (ret) {
|
||||
dev_err(dev, "audmux external port setup failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssi_np = of_parse_phandle(pdev->dev.of_node, "ssi-controller", 0);
|
||||
codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0);
|
||||
if (!ssi_np || !codec_np) {
|
||||
dev_err(dev, "phandle missing or invalid\n");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ssi_pdev = of_find_device_by_node(ssi_np);
|
||||
if (!ssi_pdev) {
|
||||
dev_err(dev, "failed to find SSI platform device\n");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
data->dev = dev;
|
||||
|
||||
data->jack_gpio = of_get_named_gpio(pdev->dev.of_node, "jack-gpio", 0);
|
||||
|
||||
data->dai.name = "hifi";
|
||||
data->dai.stream_name = "hifi";
|
||||
data->dai.codec_dai_name = "es8328-hifi-analog";
|
||||
data->dai.codec_of_node = codec_np;
|
||||
data->dai.cpu_of_node = ssi_np;
|
||||
data->dai.platform_of_node = ssi_np;
|
||||
data->dai.init = &imx_es8328_dai_init;
|
||||
data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBM_CFM;
|
||||
|
||||
data->card.dev = dev;
|
||||
data->card.dapm_widgets = imx_es8328_dapm_widgets;
|
||||
data->card.num_dapm_widgets = ARRAY_SIZE(imx_es8328_dapm_widgets);
|
||||
ret = snd_soc_of_parse_card_name(&data->card, "model");
|
||||
if (ret) {
|
||||
dev_err(dev, "Unable to parse card name\n");
|
||||
goto fail;
|
||||
}
|
||||
ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing");
|
||||
if (ret) {
|
||||
dev_err(dev, "Unable to parse routing: %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
data->card.num_links = 1;
|
||||
data->card.owner = THIS_MODULE;
|
||||
data->card.dai_link = &data->dai;
|
||||
|
||||
ret = snd_soc_register_card(&data->card);
|
||||
if (ret) {
|
||||
dev_err(dev, "Unable to register: %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, data);
|
||||
fail:
|
||||
of_node_put(ssi_np);
|
||||
of_node_put(codec_np);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int imx_es8328_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct imx_es8328_data *data = platform_get_drvdata(pdev);
|
||||
|
||||
snd_soc_jack_free_gpios(&headset_jack, ARRAY_SIZE(headset_jack_gpios),
|
||||
headset_jack_gpios);
|
||||
|
||||
snd_soc_unregister_card(&data->card);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id imx_es8328_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx-audio-es8328", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx_es8328_dt_ids);
|
||||
|
||||
static struct platform_driver imx_es8328_driver = {
|
||||
.driver = {
|
||||
.name = "imx-es8328",
|
||||
.of_match_table = imx_es8328_dt_ids,
|
||||
},
|
||||
.probe = imx_es8328_probe,
|
||||
.remove = imx_es8328_remove,
|
||||
};
|
||||
module_platform_driver(imx_es8328_driver);
|
||||
|
||||
MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
|
||||
MODULE_DESCRIPTION("Kosagi i.MX6 ES8328 ASoC machine driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:imx-audio-es8328");
|
@ -10,10 +10,13 @@
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/string.h>
|
||||
#include <sound/jack.h>
|
||||
#include <sound/simple_card.h>
|
||||
#include <sound/soc-dai.h>
|
||||
#include <sound/soc.h>
|
||||
@ -25,9 +28,15 @@ struct simple_card_data {
|
||||
struct asoc_simple_dai codec_dai;
|
||||
} *dai_props;
|
||||
unsigned int mclk_fs;
|
||||
int gpio_hp_det;
|
||||
int gpio_mic_det;
|
||||
struct snd_soc_dai_link dai_link[]; /* dynamically allocated */
|
||||
};
|
||||
|
||||
#define simple_priv_to_dev(priv) ((priv)->snd_card.dev)
|
||||
#define simple_priv_to_link(priv, i) ((priv)->snd_card.dai_link + i)
|
||||
#define simple_priv_to_props(priv, i) ((priv)->dai_props + i)
|
||||
|
||||
static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
@ -50,6 +59,32 @@ static struct snd_soc_ops asoc_simple_card_ops = {
|
||||
.hw_params = asoc_simple_card_hw_params,
|
||||
};
|
||||
|
||||
static struct snd_soc_jack simple_card_hp_jack;
|
||||
static struct snd_soc_jack_pin simple_card_hp_jack_pins[] = {
|
||||
{
|
||||
.pin = "Headphones",
|
||||
.mask = SND_JACK_HEADPHONE,
|
||||
},
|
||||
};
|
||||
static struct snd_soc_jack_gpio simple_card_hp_jack_gpio = {
|
||||
.name = "Headphone detection",
|
||||
.report = SND_JACK_HEADPHONE,
|
||||
.debounce_time = 150,
|
||||
};
|
||||
|
||||
static struct snd_soc_jack simple_card_mic_jack;
|
||||
static struct snd_soc_jack_pin simple_card_mic_jack_pins[] = {
|
||||
{
|
||||
.pin = "Mic Jack",
|
||||
.mask = SND_JACK_MICROPHONE,
|
||||
},
|
||||
};
|
||||
static struct snd_soc_jack_gpio simple_card_mic_jack_gpio = {
|
||||
.name = "Mic detection",
|
||||
.report = SND_JACK_MICROPHONE,
|
||||
.debounce_time = 150,
|
||||
};
|
||||
|
||||
static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai,
|
||||
struct asoc_simple_dai *set)
|
||||
{
|
||||
@ -105,42 +140,70 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (gpio_is_valid(priv->gpio_hp_det)) {
|
||||
snd_soc_jack_new(codec->codec, "Headphones", SND_JACK_HEADPHONE,
|
||||
&simple_card_hp_jack);
|
||||
snd_soc_jack_add_pins(&simple_card_hp_jack,
|
||||
ARRAY_SIZE(simple_card_hp_jack_pins),
|
||||
simple_card_hp_jack_pins);
|
||||
|
||||
simple_card_hp_jack_gpio.gpio = priv->gpio_hp_det;
|
||||
snd_soc_jack_add_gpios(&simple_card_hp_jack, 1,
|
||||
&simple_card_hp_jack_gpio);
|
||||
}
|
||||
|
||||
if (gpio_is_valid(priv->gpio_mic_det)) {
|
||||
snd_soc_jack_new(codec->codec, "Mic Jack", SND_JACK_MICROPHONE,
|
||||
&simple_card_mic_jack);
|
||||
snd_soc_jack_add_pins(&simple_card_mic_jack,
|
||||
ARRAY_SIZE(simple_card_mic_jack_pins),
|
||||
simple_card_mic_jack_pins);
|
||||
simple_card_mic_jack_gpio.gpio = priv->gpio_mic_det;
|
||||
snd_soc_jack_add_gpios(&simple_card_mic_jack, 1,
|
||||
&simple_card_mic_jack_gpio);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
asoc_simple_card_sub_parse_of(struct device_node *np,
|
||||
struct asoc_simple_dai *dai,
|
||||
const struct device_node **p_node,
|
||||
const char **name)
|
||||
struct device_node **p_node,
|
||||
const char **name,
|
||||
int *args_count)
|
||||
{
|
||||
struct device_node *node;
|
||||
struct of_phandle_args args;
|
||||
struct clk *clk;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* get node via "sound-dai = <&phandle port>"
|
||||
* Get node via "sound-dai = <&phandle port>"
|
||||
* it will be used as xxx_of_node on soc_bind_dai_link()
|
||||
*/
|
||||
node = of_parse_phandle(np, "sound-dai", 0);
|
||||
if (!node)
|
||||
return -ENODEV;
|
||||
*p_node = node;
|
||||
ret = of_parse_phandle_with_args(np, "sound-dai",
|
||||
"#sound-dai-cells", 0, &args);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* get dai->name */
|
||||
*p_node = args.np;
|
||||
|
||||
if (args_count)
|
||||
*args_count = args.args_count;
|
||||
|
||||
/* Get dai->name */
|
||||
ret = snd_soc_of_get_dai_name(np, name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* parse TDM slot */
|
||||
/* Parse TDM slot */
|
||||
ret = snd_soc_of_parse_tdm_slot(np, &dai->slots, &dai->slot_width);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* dai->sysclk come from
|
||||
* "clocks = <&xxx>" (if system has common clock)
|
||||
* Parse dai->sysclk come from "clocks = <&xxx>"
|
||||
* (if system has common clock)
|
||||
* or "system-clock-frequency = <xxx>"
|
||||
* or device's module clock.
|
||||
*/
|
||||
@ -155,7 +218,7 @@ asoc_simple_card_sub_parse_of(struct device_node *np,
|
||||
} else if (!of_property_read_u32(np, "system-clock-frequency", &val)) {
|
||||
dai->sysclk = val;
|
||||
} else {
|
||||
clk = of_clk_get(node, 0);
|
||||
clk = of_clk_get(args.np, 0);
|
||||
if (!IS_ERR(clk))
|
||||
dai->sysclk = clk_get_rate(clk);
|
||||
}
|
||||
@ -163,12 +226,14 @@ asoc_simple_card_sub_parse_of(struct device_node *np,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int simple_card_dai_link_of(struct device_node *node,
|
||||
struct device *dev,
|
||||
struct snd_soc_dai_link *dai_link,
|
||||
struct simple_dai_props *dai_props,
|
||||
bool is_top_level_node)
|
||||
static int asoc_simple_card_dai_link_of(struct device_node *node,
|
||||
struct simple_card_data *priv,
|
||||
int idx,
|
||||
bool is_top_level_node)
|
||||
{
|
||||
struct device *dev = simple_priv_to_dev(priv);
|
||||
struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx);
|
||||
struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx);
|
||||
struct device_node *np = NULL;
|
||||
struct device_node *bitclkmaster = NULL;
|
||||
struct device_node *framemaster = NULL;
|
||||
@ -176,8 +241,9 @@ static int simple_card_dai_link_of(struct device_node *node,
|
||||
char *name;
|
||||
char prop[128];
|
||||
char *prefix = "";
|
||||
int ret;
|
||||
int ret, cpu_args;
|
||||
|
||||
/* For single DAI link & old style of DT node */
|
||||
if (is_top_level_node)
|
||||
prefix = "simple-audio-card,";
|
||||
|
||||
@ -195,7 +261,8 @@ static int simple_card_dai_link_of(struct device_node *node,
|
||||
|
||||
ret = asoc_simple_card_sub_parse_of(np, &dai_props->cpu_dai,
|
||||
&dai_link->cpu_of_node,
|
||||
&dai_link->cpu_dai_name);
|
||||
&dai_link->cpu_dai_name,
|
||||
&cpu_args);
|
||||
if (ret < 0)
|
||||
goto dai_link_of_err;
|
||||
|
||||
@ -226,14 +293,16 @@ static int simple_card_dai_link_of(struct device_node *node,
|
||||
|
||||
ret = asoc_simple_card_sub_parse_of(np, &dai_props->codec_dai,
|
||||
&dai_link->codec_of_node,
|
||||
&dai_link->codec_dai_name);
|
||||
&dai_link->codec_dai_name, NULL);
|
||||
if (ret < 0)
|
||||
goto dai_link_of_err;
|
||||
|
||||
if (strlen(prefix) && !bitclkmaster && !framemaster) {
|
||||
/* No dai-link level and master setting was not found from
|
||||
sound node level, revert back to legacy DT parsing and
|
||||
take the settings from codec node. */
|
||||
/*
|
||||
* No DAI link level and master setting was found
|
||||
* from sound node level, revert back to legacy DT
|
||||
* parsing and take the settings from codec node.
|
||||
*/
|
||||
dev_dbg(dev, "%s: Revert to legacy daifmt parsing\n",
|
||||
__func__);
|
||||
dai_props->cpu_dai.fmt = dai_props->codec_dai.fmt =
|
||||
@ -262,10 +331,10 @@ static int simple_card_dai_link_of(struct device_node *node,
|
||||
goto dai_link_of_err;
|
||||
}
|
||||
|
||||
/* simple-card assumes platform == cpu */
|
||||
/* Simple Card assumes platform == cpu */
|
||||
dai_link->platform_of_node = dai_link->cpu_of_node;
|
||||
|
||||
/* Link name is created from CPU/CODEC dai name */
|
||||
/* DAI link name is created from CPU/CODEC dai name */
|
||||
name = devm_kzalloc(dev,
|
||||
strlen(dai_link->cpu_dai_name) +
|
||||
strlen(dai_link->codec_dai_name) + 2,
|
||||
@ -274,6 +343,7 @@ static int simple_card_dai_link_of(struct device_node *node,
|
||||
dai_link->codec_dai_name);
|
||||
dai_link->name = dai_link->stream_name = name;
|
||||
dai_link->ops = &asoc_simple_card_ops;
|
||||
dai_link->init = asoc_simple_card_dai_init;
|
||||
|
||||
dev_dbg(dev, "\tname : %s\n", dai_link->stream_name);
|
||||
dev_dbg(dev, "\tcpu : %s / %04x / %d\n",
|
||||
@ -285,6 +355,18 @@ static int simple_card_dai_link_of(struct device_node *node,
|
||||
dai_props->codec_dai.fmt,
|
||||
dai_props->codec_dai.sysclk);
|
||||
|
||||
/*
|
||||
* In soc_bind_dai_link() will check cpu name after
|
||||
* of_node matching if dai_link has cpu_dai_name.
|
||||
* but, it will never match if name was created by
|
||||
* fmt_single_name() remove cpu_dai_name if cpu_args
|
||||
* was 0. See:
|
||||
* fmt_single_name()
|
||||
* fmt_multiple_name()
|
||||
*/
|
||||
if (!cpu_args)
|
||||
dai_link->cpu_dai_name = NULL;
|
||||
|
||||
dai_link_of_err:
|
||||
if (np)
|
||||
of_node_put(np);
|
||||
@ -296,19 +378,19 @@ dai_link_of_err:
|
||||
}
|
||||
|
||||
static int asoc_simple_card_parse_of(struct device_node *node,
|
||||
struct simple_card_data *priv,
|
||||
struct device *dev,
|
||||
int multi)
|
||||
struct simple_card_data *priv)
|
||||
{
|
||||
struct snd_soc_dai_link *dai_link = priv->snd_card.dai_link;
|
||||
struct simple_dai_props *dai_props = priv->dai_props;
|
||||
struct device *dev = simple_priv_to_dev(priv);
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
/* parsing the card name from DT */
|
||||
if (!node)
|
||||
return -EINVAL;
|
||||
|
||||
/* Parse the card name from DT */
|
||||
snd_soc_of_parse_card_name(&priv->snd_card, "simple-audio-card,name");
|
||||
|
||||
/* off-codec widgets */
|
||||
/* The off-codec widgets */
|
||||
if (of_property_read_bool(node, "simple-audio-card,widgets")) {
|
||||
ret = snd_soc_of_parse_audio_simple_widgets(&priv->snd_card,
|
||||
"simple-audio-card,widgets");
|
||||
@ -332,32 +414,45 @@ static int asoc_simple_card_parse_of(struct device_node *node,
|
||||
dev_dbg(dev, "New simple-card: %s\n", priv->snd_card.name ?
|
||||
priv->snd_card.name : "");
|
||||
|
||||
if (multi) {
|
||||
/* Single/Muti DAI link(s) & New style of DT node */
|
||||
if (of_get_child_by_name(node, "simple-audio-card,dai-link")) {
|
||||
struct device_node *np = NULL;
|
||||
int i;
|
||||
for (i = 0; (np = of_get_next_child(node, np)); i++) {
|
||||
int i = 0;
|
||||
|
||||
for_each_child_of_node(node, np) {
|
||||
dev_dbg(dev, "\tlink %d:\n", i);
|
||||
ret = simple_card_dai_link_of(np, dev, dai_link + i,
|
||||
dai_props + i, false);
|
||||
ret = asoc_simple_card_dai_link_of(np, priv,
|
||||
i, false);
|
||||
if (ret < 0) {
|
||||
of_node_put(np);
|
||||
return ret;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
ret = simple_card_dai_link_of(node, dev, dai_link, dai_props,
|
||||
true);
|
||||
/* For single DAI link & old style of DT node */
|
||||
ret = asoc_simple_card_dai_link_of(node, priv, 0, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->gpio_hp_det = of_get_named_gpio(node,
|
||||
"simple-audio-card,hp-det-gpio", 0);
|
||||
if (priv->gpio_hp_det == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
priv->gpio_mic_det = of_get_named_gpio(node,
|
||||
"simple-audio-card,mic-det-gpio", 0);
|
||||
if (priv->gpio_mic_det == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
if (!priv->snd_card.name)
|
||||
priv->snd_card.name = priv->snd_card.dai_link->name;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* update the reference count of the devices nodes at end of probe */
|
||||
/* Decrease the reference count of the device nodes */
|
||||
static int asoc_simple_card_unref(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = platform_get_drvdata(pdev);
|
||||
@ -384,34 +479,29 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
|
||||
struct snd_soc_dai_link *dai_link;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
int num_links, multi, ret;
|
||||
int num_links, ret;
|
||||
|
||||
/* get the number of DAI links */
|
||||
if (np && of_get_child_by_name(np, "simple-audio-card,dai-link")) {
|
||||
/* Get the number of DAI links */
|
||||
if (np && of_get_child_by_name(np, "simple-audio-card,dai-link"))
|
||||
num_links = of_get_child_count(np);
|
||||
multi = 1;
|
||||
} else {
|
||||
else
|
||||
num_links = 1;
|
||||
multi = 0;
|
||||
}
|
||||
|
||||
/* allocate the private data and the DAI link array */
|
||||
/* Allocate the private data and the DAI link array */
|
||||
priv = devm_kzalloc(dev,
|
||||
sizeof(*priv) + sizeof(*dai_link) * num_links,
|
||||
GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* init snd_soc_card
|
||||
*/
|
||||
/* Init snd_soc_card */
|
||||
priv->snd_card.owner = THIS_MODULE;
|
||||
priv->snd_card.dev = dev;
|
||||
dai_link = priv->dai_link;
|
||||
priv->snd_card.dai_link = dai_link;
|
||||
priv->snd_card.num_links = num_links;
|
||||
|
||||
/* get room for the other properties */
|
||||
/* Get room for the other properties */
|
||||
priv->dai_props = devm_kzalloc(dev,
|
||||
sizeof(*priv->dai_props) * num_links,
|
||||
GFP_KERNEL);
|
||||
@ -420,25 +510,13 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
|
||||
|
||||
if (np && of_device_is_available(np)) {
|
||||
|
||||
ret = asoc_simple_card_parse_of(np, priv, dev, multi);
|
||||
ret = asoc_simple_card_parse_of(np, priv);
|
||||
if (ret < 0) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "parse error %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
* soc_bind_dai_link() will check cpu name
|
||||
* after of_node matching if dai_link has cpu_dai_name.
|
||||
* but, it will never match if name was created by fmt_single_name()
|
||||
* remove cpu_dai_name to escape name matching.
|
||||
* see
|
||||
* fmt_single_name()
|
||||
* fmt_multiple_name()
|
||||
*/
|
||||
if (num_links == 1)
|
||||
dai_link->cpu_dai_name = NULL;
|
||||
|
||||
} else {
|
||||
struct asoc_simple_card_info *cinfo;
|
||||
|
||||
@ -464,6 +542,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
|
||||
dai_link->codec_name = cinfo->codec;
|
||||
dai_link->cpu_dai_name = cinfo->cpu_dai.name;
|
||||
dai_link->codec_dai_name = cinfo->codec_dai.name;
|
||||
dai_link->init = asoc_simple_card_dai_init;
|
||||
memcpy(&priv->dai_props->cpu_dai, &cinfo->cpu_dai,
|
||||
sizeof(priv->dai_props->cpu_dai));
|
||||
memcpy(&priv->dai_props->codec_dai, &cinfo->codec_dai,
|
||||
@ -473,11 +552,6 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
|
||||
priv->dai_props->codec_dai.fmt |= cinfo->daifmt;
|
||||
}
|
||||
|
||||
/*
|
||||
* init snd_soc_dai_link
|
||||
*/
|
||||
dai_link->init = asoc_simple_card_dai_init;
|
||||
|
||||
snd_soc_card_set_drvdata(&priv->snd_card, priv);
|
||||
|
||||
ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card);
|
||||
@ -491,6 +565,16 @@ err:
|
||||
|
||||
static int asoc_simple_card_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = platform_get_drvdata(pdev);
|
||||
struct simple_card_data *priv = snd_soc_card_get_drvdata(card);
|
||||
|
||||
if (gpio_is_valid(priv->gpio_hp_det))
|
||||
snd_soc_jack_free_gpios(&simple_card_hp_jack, 1,
|
||||
&simple_card_hp_jack_gpio);
|
||||
if (gpio_is_valid(priv->gpio_mic_det))
|
||||
snd_soc_jack_free_gpios(&simple_card_mic_jack, 1,
|
||||
&simple_card_mic_jack_gpio);
|
||||
|
||||
return asoc_simple_card_unref(pdev);
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,8 @@
|
||||
snd-soc-sst-dsp-objs := sst-dsp.o sst-firmware.o
|
||||
snd-soc-sst-acpi-objs := sst-acpi.o
|
||||
|
||||
snd-soc-sst-mfld-platform-objs := sst-mfld-platform-pcm.o sst-mfld-platform-compress.o
|
||||
snd-soc-sst-mfld-platform-objs := sst-mfld-platform-pcm.o \
|
||||
sst-mfld-platform-compress.o sst-atom-controls.o
|
||||
snd-soc-mfld-machine-objs := mfld_machine.o
|
||||
|
||||
obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += snd-soc-sst-mfld-platform.o
|
||||
|
@ -139,6 +139,7 @@ static struct snd_soc_card byt_max98090_card = {
|
||||
.num_dapm_routes = ARRAY_SIZE(byt_max98090_audio_map),
|
||||
.controls = byt_max98090_controls,
|
||||
.num_controls = ARRAY_SIZE(byt_max98090_controls),
|
||||
.fully_routed = true,
|
||||
};
|
||||
|
||||
static int byt_max98090_probe(struct platform_device *pdev)
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
@ -36,8 +37,6 @@ static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = {
|
||||
static const struct snd_soc_dapm_route byt_rt5640_audio_map[] = {
|
||||
{"Headset Mic", NULL, "MICBIAS1"},
|
||||
{"IN2P", NULL, "Headset Mic"},
|
||||
{"IN2N", NULL, "Headset Mic"},
|
||||
{"DMIC1", NULL, "Internal Mic"},
|
||||
{"Headphone", NULL, "HPOL"},
|
||||
{"Headphone", NULL, "HPOR"},
|
||||
{"Speaker", NULL, "SPOLP"},
|
||||
@ -46,6 +45,31 @@ static const struct snd_soc_dapm_route byt_rt5640_audio_map[] = {
|
||||
{"Speaker", NULL, "SPORN"},
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic1_map[] = {
|
||||
{"DMIC1", NULL, "Internal Mic"},
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic2_map[] = {
|
||||
{"DMIC2", NULL, "Internal Mic"},
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route byt_rt5640_intmic_in1_map[] = {
|
||||
{"Internal Mic", NULL, "MICBIAS1"},
|
||||
{"IN1P", NULL, "Internal Mic"},
|
||||
};
|
||||
|
||||
enum {
|
||||
BYT_RT5640_DMIC1_MAP,
|
||||
BYT_RT5640_DMIC2_MAP,
|
||||
BYT_RT5640_IN1_MAP,
|
||||
};
|
||||
|
||||
#define BYT_RT5640_MAP(quirk) ((quirk) & 0xff)
|
||||
#define BYT_RT5640_DMIC_EN BIT(16)
|
||||
|
||||
static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP |
|
||||
BYT_RT5640_DMIC_EN;
|
||||
|
||||
static const struct snd_kcontrol_new byt_rt5640_controls[] = {
|
||||
SOC_DAPM_PIN_SWITCH("Headphone"),
|
||||
SOC_DAPM_PIN_SWITCH("Headset Mic"),
|
||||
@ -77,12 +101,41 @@ static int byt_rt5640_hw_params(struct snd_pcm_substream *substream,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int byt_rt5640_quirk_cb(const struct dmi_system_id *id)
|
||||
{
|
||||
byt_rt5640_quirk = (unsigned long)id->driver_data;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct dmi_system_id byt_rt5640_quirk_table[] = {
|
||||
{
|
||||
.callback = byt_rt5640_quirk_cb,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "T100TA"),
|
||||
},
|
||||
.driver_data = (unsigned long *)BYT_RT5640_IN1_MAP,
|
||||
},
|
||||
{
|
||||
.callback = byt_rt5640_quirk_cb,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "DellInc."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"),
|
||||
},
|
||||
.driver_data = (unsigned long *)(BYT_RT5640_DMIC2_MAP |
|
||||
BYT_RT5640_DMIC_EN),
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
|
||||
{
|
||||
int ret;
|
||||
struct snd_soc_codec *codec = runtime->codec;
|
||||
struct snd_soc_dapm_context *dapm = &codec->dapm;
|
||||
struct snd_soc_card *card = runtime->card;
|
||||
const struct snd_soc_dapm_route *custom_map;
|
||||
int num_routes;
|
||||
|
||||
card->dapm.idle_bias_off = true;
|
||||
|
||||
@ -93,6 +146,31 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
|
||||
return ret;
|
||||
}
|
||||
|
||||
dmi_check_system(byt_rt5640_quirk_table);
|
||||
switch (BYT_RT5640_MAP(byt_rt5640_quirk)) {
|
||||
case BYT_RT5640_IN1_MAP:
|
||||
custom_map = byt_rt5640_intmic_in1_map;
|
||||
num_routes = ARRAY_SIZE(byt_rt5640_intmic_in1_map);
|
||||
break;
|
||||
case BYT_RT5640_DMIC2_MAP:
|
||||
custom_map = byt_rt5640_intmic_dmic2_map;
|
||||
num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic2_map);
|
||||
break;
|
||||
default:
|
||||
custom_map = byt_rt5640_intmic_dmic1_map;
|
||||
num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic1_map);
|
||||
}
|
||||
|
||||
ret = snd_soc_dapm_add_routes(dapm, custom_map, num_routes);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (byt_rt5640_quirk & BYT_RT5640_DMIC_EN) {
|
||||
ret = rt5640_dmic_enable(codec, 0, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
snd_soc_dapm_ignore_suspend(dapm, "HPOL");
|
||||
snd_soc_dapm_ignore_suspend(dapm, "HPOR");
|
||||
|
||||
@ -131,6 +209,7 @@ static struct snd_soc_card byt_rt5640_card = {
|
||||
.num_dapm_widgets = ARRAY_SIZE(byt_rt5640_widgets),
|
||||
.dapm_routes = byt_rt5640_audio_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(byt_rt5640_audio_map),
|
||||
.fully_routed = true,
|
||||
};
|
||||
|
||||
static int byt_rt5640_probe(struct platform_device *pdev)
|
||||
|
218
sound/soc/intel/sst-atom-controls.c
Normal file
218
sound/soc/intel/sst-atom-controls.c
Normal file
@ -0,0 +1,218 @@
|
||||
/*
|
||||
* sst-atom-controls.c - Intel MID Platform driver DPCM ALSA controls for Mrfld
|
||||
*
|
||||
* Copyright (C) 2013-14 Intel Corp
|
||||
* Author: Omair Mohammed Abdullah <omair.m.abdullah@intel.com>
|
||||
* Vinod Koul <vinod.koul@intel.com>
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/tlv.h>
|
||||
#include "sst-mfld-platform.h"
|
||||
#include "sst-atom-controls.h"
|
||||
|
||||
static int sst_fill_byte_control(struct sst_data *drv,
|
||||
u8 ipc_msg, u8 block,
|
||||
u8 task_id, u8 pipe_id,
|
||||
u16 len, void *cmd_data)
|
||||
{
|
||||
struct snd_sst_bytes_v2 *byte_data = drv->byte_stream;
|
||||
|
||||
byte_data->type = SST_CMD_BYTES_SET;
|
||||
byte_data->ipc_msg = ipc_msg;
|
||||
byte_data->block = block;
|
||||
byte_data->task_id = task_id;
|
||||
byte_data->pipe_id = pipe_id;
|
||||
|
||||
if (len > SST_MAX_BIN_BYTES - sizeof(*byte_data)) {
|
||||
dev_err(&drv->pdev->dev, "command length too big (%u)", len);
|
||||
return -EINVAL;
|
||||
}
|
||||
byte_data->len = len;
|
||||
memcpy(byte_data->bytes, cmd_data, len);
|
||||
print_hex_dump_bytes("writing to lpe: ", DUMP_PREFIX_OFFSET,
|
||||
byte_data, len + sizeof(*byte_data));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sst_fill_and_send_cmd_unlocked(struct sst_data *drv,
|
||||
u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id,
|
||||
void *cmd_data, u16 len)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = sst_fill_byte_control(drv, ipc_msg,
|
||||
block, task_id, pipe_id, len, cmd_data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return sst->ops->send_byte_stream(sst->dev, drv->byte_stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* sst_fill_and_send_cmd - generate the IPC message and send it to the FW
|
||||
* @ipc_msg: type of IPC (CMD, SET_PARAMS, GET_PARAMS)
|
||||
* @cmd_data: the IPC payload
|
||||
*/
|
||||
static int sst_fill_and_send_cmd(struct sst_data *drv,
|
||||
u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id,
|
||||
void *cmd_data, u16 len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&drv->lock);
|
||||
ret = sst_fill_and_send_cmd_unlocked(drv, ipc_msg, block,
|
||||
task_id, pipe_id, cmd_data, len);
|
||||
mutex_unlock(&drv->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sst_send_algo_cmd(struct sst_data *drv,
|
||||
struct sst_algo_control *bc)
|
||||
{
|
||||
int len, ret = 0;
|
||||
struct sst_cmd_set_params *cmd;
|
||||
|
||||
/*bc->max includes sizeof algos + length field*/
|
||||
len = sizeof(cmd->dst) + sizeof(cmd->command_id) + bc->max;
|
||||
|
||||
cmd = kzalloc(len, GFP_KERNEL);
|
||||
if (cmd == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
SST_FILL_DESTINATION(2, cmd->dst, bc->pipe_id, bc->module_id);
|
||||
cmd->command_id = bc->cmd_id;
|
||||
memcpy(cmd->params, bc->params, bc->max);
|
||||
|
||||
ret = sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS,
|
||||
SST_FLAG_BLOCKED, bc->task_id, 0, cmd, len);
|
||||
kfree(cmd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sst_algo_bytes_ctl_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
struct sst_algo_control *bc = (void *)kcontrol->private_value;
|
||||
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
|
||||
uinfo->count = bc->max;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sst_algo_control_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct sst_algo_control *bc = (void *)kcontrol->private_value;
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
switch (bc->type) {
|
||||
case SST_ALGO_PARAMS:
|
||||
memcpy(ucontrol->value.bytes.data, bc->params, bc->max);
|
||||
break;
|
||||
default:
|
||||
dev_err(component->dev, "Invalid Input- algo type:%d\n",
|
||||
bc->type);
|
||||
return -EINVAL;
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sst_algo_control_set(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
int ret = 0;
|
||||
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
|
||||
struct sst_data *drv = snd_soc_component_get_drvdata(cmpnt);
|
||||
struct sst_algo_control *bc = (void *)kcontrol->private_value;
|
||||
|
||||
dev_dbg(cmpnt->dev, "control_name=%s\n", kcontrol->id.name);
|
||||
mutex_lock(&drv->lock);
|
||||
switch (bc->type) {
|
||||
case SST_ALGO_PARAMS:
|
||||
memcpy(bc->params, ucontrol->value.bytes.data, bc->max);
|
||||
break;
|
||||
default:
|
||||
mutex_unlock(&drv->lock);
|
||||
dev_err(cmpnt->dev, "Invalid Input- algo type:%d\n",
|
||||
bc->type);
|
||||
return -EINVAL;
|
||||
}
|
||||
/*if pipe is enabled, need to send the algo params from here*/
|
||||
if (bc->w && bc->w->power)
|
||||
ret = sst_send_algo_cmd(drv, bc);
|
||||
mutex_unlock(&drv->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new sst_algo_controls[] = {
|
||||
SST_ALGO_KCONTROL_BYTES("media_loop1_out", "fir", 272, SST_MODULE_ID_FIR_24,
|
||||
SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_VB_SET_FIR),
|
||||
SST_ALGO_KCONTROL_BYTES("media_loop1_out", "iir", 300, SST_MODULE_ID_IIR_24,
|
||||
SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_VB_SET_IIR),
|
||||
SST_ALGO_KCONTROL_BYTES("media_loop1_out", "mdrp", 286, SST_MODULE_ID_MDRP,
|
||||
SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_SET_MDRP),
|
||||
SST_ALGO_KCONTROL_BYTES("media_loop2_out", "fir", 272, SST_MODULE_ID_FIR_24,
|
||||
SST_PATH_INDEX_MEDIA_LOOP2_OUT, 0, SST_TASK_SBA, SBA_VB_SET_FIR),
|
||||
SST_ALGO_KCONTROL_BYTES("media_loop2_out", "iir", 300, SST_MODULE_ID_IIR_24,
|
||||
SST_PATH_INDEX_MEDIA_LOOP2_OUT, 0, SST_TASK_SBA, SBA_VB_SET_IIR),
|
||||
SST_ALGO_KCONTROL_BYTES("media_loop2_out", "mdrp", 286, SST_MODULE_ID_MDRP,
|
||||
SST_PATH_INDEX_MEDIA_LOOP2_OUT, 0, SST_TASK_SBA, SBA_SET_MDRP),
|
||||
SST_ALGO_KCONTROL_BYTES("sprot_loop_out", "lpro", 192, SST_MODULE_ID_SPROT,
|
||||
SST_PATH_INDEX_SPROT_LOOP_OUT, 0, SST_TASK_SBA, SBA_VB_LPRO),
|
||||
SST_ALGO_KCONTROL_BYTES("codec_in0", "dcr", 52, SST_MODULE_ID_FILT_DCR,
|
||||
SST_PATH_INDEX_CODEC_IN0, 0, SST_TASK_SBA, SBA_VB_SET_IIR),
|
||||
SST_ALGO_KCONTROL_BYTES("codec_in1", "dcr", 52, SST_MODULE_ID_FILT_DCR,
|
||||
SST_PATH_INDEX_CODEC_IN1, 0, SST_TASK_SBA, SBA_VB_SET_IIR),
|
||||
|
||||
};
|
||||
|
||||
static int sst_algo_control_init(struct device *dev)
|
||||
{
|
||||
int i = 0;
|
||||
struct sst_algo_control *bc;
|
||||
/*allocate space to cache the algo parameters in the driver*/
|
||||
for (i = 0; i < ARRAY_SIZE(sst_algo_controls); i++) {
|
||||
bc = (struct sst_algo_control *)sst_algo_controls[i].private_value;
|
||||
bc->params = devm_kzalloc(dev, bc->max, GFP_KERNEL);
|
||||
if (bc->params == NULL)
|
||||
return -ENOMEM;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform)
|
||||
{
|
||||
int ret = 0;
|
||||
struct sst_data *drv = snd_soc_platform_get_drvdata(platform);
|
||||
|
||||
drv->byte_stream = devm_kzalloc(platform->dev,
|
||||
SST_MAX_BIN_BYTES, GFP_KERNEL);
|
||||
if (!drv->byte_stream)
|
||||
return -ENOMEM;
|
||||
|
||||
/*Initialize algo control params*/
|
||||
ret = sst_algo_control_init(platform->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = snd_soc_add_platform_controls(platform, sst_algo_controls,
|
||||
ARRAY_SIZE(sst_algo_controls));
|
||||
return ret;
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
/*
|
||||
* sst-atom-controls.h - Intel MID Platform driver header file
|
||||
*
|
||||
* Copyright (C) 2013-14 Intel Corp
|
||||
* Author: Ramesh Babu <ramesh.babu.koul@intel.com>
|
||||
* Omair M Abdullah <omair.m.abdullah@intel.com>
|
||||
@ -18,13 +20,423 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __SST_CONTROLS_V2_H__
|
||||
#define __SST_CONTROLS_V2_H__
|
||||
#ifndef __SST_ATOM_CONTROLS_H__
|
||||
#define __SST_ATOM_CONTROLS_H__
|
||||
|
||||
enum {
|
||||
MERR_DPCM_AUDIO = 0,
|
||||
MERR_DPCM_COMPR,
|
||||
};
|
||||
|
||||
/* define a bit for each mixer input */
|
||||
#define SST_MIX_IP(x) (x)
|
||||
|
||||
#define SST_IP_CODEC0 SST_MIX_IP(2)
|
||||
#define SST_IP_CODEC1 SST_MIX_IP(3)
|
||||
#define SST_IP_LOOP0 SST_MIX_IP(4)
|
||||
#define SST_IP_LOOP1 SST_MIX_IP(5)
|
||||
#define SST_IP_LOOP2 SST_MIX_IP(6)
|
||||
#define SST_IP_PROBE SST_MIX_IP(7)
|
||||
#define SST_IP_VOIP SST_MIX_IP(12)
|
||||
#define SST_IP_PCM0 SST_MIX_IP(13)
|
||||
#define SST_IP_PCM1 SST_MIX_IP(14)
|
||||
#define SST_IP_MEDIA0 SST_MIX_IP(17)
|
||||
#define SST_IP_MEDIA1 SST_MIX_IP(18)
|
||||
#define SST_IP_MEDIA2 SST_MIX_IP(19)
|
||||
#define SST_IP_MEDIA3 SST_MIX_IP(20)
|
||||
|
||||
#define SST_IP_LAST SST_IP_MEDIA3
|
||||
|
||||
#define SST_SWM_INPUT_COUNT (SST_IP_LAST + 1)
|
||||
#define SST_CMD_SWM_MAX_INPUTS 6
|
||||
|
||||
#define SST_PATH_ID_SHIFT 8
|
||||
#define SST_DEFAULT_LOCATION_ID 0xFFFF
|
||||
#define SST_DEFAULT_CELL_NBR 0xFF
|
||||
#define SST_DEFAULT_MODULE_ID 0xFFFF
|
||||
|
||||
/*
|
||||
* Audio DSP Path Ids. Specified by the audio DSP FW
|
||||
*/
|
||||
enum sst_path_index {
|
||||
SST_PATH_INDEX_CODEC_OUT0 = (0x02 << SST_PATH_ID_SHIFT),
|
||||
SST_PATH_INDEX_CODEC_OUT1 = (0x03 << SST_PATH_ID_SHIFT),
|
||||
|
||||
SST_PATH_INDEX_SPROT_LOOP_OUT = (0x04 << SST_PATH_ID_SHIFT),
|
||||
SST_PATH_INDEX_MEDIA_LOOP1_OUT = (0x05 << SST_PATH_ID_SHIFT),
|
||||
SST_PATH_INDEX_MEDIA_LOOP2_OUT = (0x06 << SST_PATH_ID_SHIFT),
|
||||
|
||||
SST_PATH_INDEX_VOIP_OUT = (0x0C << SST_PATH_ID_SHIFT),
|
||||
SST_PATH_INDEX_PCM0_OUT = (0x0D << SST_PATH_ID_SHIFT),
|
||||
SST_PATH_INDEX_PCM1_OUT = (0x0E << SST_PATH_ID_SHIFT),
|
||||
SST_PATH_INDEX_PCM2_OUT = (0x0F << SST_PATH_ID_SHIFT),
|
||||
|
||||
SST_PATH_INDEX_MEDIA0_OUT = (0x12 << SST_PATH_ID_SHIFT),
|
||||
SST_PATH_INDEX_MEDIA1_OUT = (0x13 << SST_PATH_ID_SHIFT),
|
||||
|
||||
|
||||
/* Start of input paths */
|
||||
SST_PATH_INDEX_CODEC_IN0 = (0x82 << SST_PATH_ID_SHIFT),
|
||||
SST_PATH_INDEX_CODEC_IN1 = (0x83 << SST_PATH_ID_SHIFT),
|
||||
|
||||
SST_PATH_INDEX_SPROT_LOOP_IN = (0x84 << SST_PATH_ID_SHIFT),
|
||||
SST_PATH_INDEX_MEDIA_LOOP1_IN = (0x85 << SST_PATH_ID_SHIFT),
|
||||
SST_PATH_INDEX_MEDIA_LOOP2_IN = (0x86 << SST_PATH_ID_SHIFT),
|
||||
|
||||
SST_PATH_INDEX_VOIP_IN = (0x8C << SST_PATH_ID_SHIFT),
|
||||
|
||||
SST_PATH_INDEX_PCM0_IN = (0x8D << SST_PATH_ID_SHIFT),
|
||||
SST_PATH_INDEX_PCM1_IN = (0x8E << SST_PATH_ID_SHIFT),
|
||||
|
||||
SST_PATH_INDEX_MEDIA0_IN = (0x8F << SST_PATH_ID_SHIFT),
|
||||
SST_PATH_INDEX_MEDIA1_IN = (0x90 << SST_PATH_ID_SHIFT),
|
||||
SST_PATH_INDEX_MEDIA2_IN = (0x91 << SST_PATH_ID_SHIFT),
|
||||
|
||||
SST_PATH_INDEX_MEDIA3_IN = (0x9C << SST_PATH_ID_SHIFT),
|
||||
|
||||
SST_PATH_INDEX_RESERVED = (0xFF << SST_PATH_ID_SHIFT),
|
||||
};
|
||||
|
||||
/*
|
||||
* path IDs
|
||||
*/
|
||||
enum sst_swm_inputs {
|
||||
SST_SWM_IN_CODEC0 = (SST_PATH_INDEX_CODEC_IN0 | SST_DEFAULT_CELL_NBR),
|
||||
SST_SWM_IN_CODEC1 = (SST_PATH_INDEX_CODEC_IN1 | SST_DEFAULT_CELL_NBR),
|
||||
SST_SWM_IN_SPROT_LOOP = (SST_PATH_INDEX_SPROT_LOOP_IN | SST_DEFAULT_CELL_NBR),
|
||||
SST_SWM_IN_MEDIA_LOOP1 = (SST_PATH_INDEX_MEDIA_LOOP1_IN | SST_DEFAULT_CELL_NBR),
|
||||
SST_SWM_IN_MEDIA_LOOP2 = (SST_PATH_INDEX_MEDIA_LOOP2_IN | SST_DEFAULT_CELL_NBR),
|
||||
SST_SWM_IN_VOIP = (SST_PATH_INDEX_VOIP_IN | SST_DEFAULT_CELL_NBR),
|
||||
SST_SWM_IN_PCM0 = (SST_PATH_INDEX_PCM0_IN | SST_DEFAULT_CELL_NBR),
|
||||
SST_SWM_IN_PCM1 = (SST_PATH_INDEX_PCM1_IN | SST_DEFAULT_CELL_NBR),
|
||||
SST_SWM_IN_MEDIA0 = (SST_PATH_INDEX_MEDIA0_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */
|
||||
SST_SWM_IN_MEDIA1 = (SST_PATH_INDEX_MEDIA1_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */
|
||||
SST_SWM_IN_MEDIA2 = (SST_PATH_INDEX_MEDIA2_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */
|
||||
SST_SWM_IN_MEDIA3 = (SST_PATH_INDEX_MEDIA3_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */
|
||||
SST_SWM_IN_END = (SST_PATH_INDEX_RESERVED | SST_DEFAULT_CELL_NBR)
|
||||
};
|
||||
|
||||
/*
|
||||
* path IDs
|
||||
*/
|
||||
enum sst_swm_outputs {
|
||||
SST_SWM_OUT_CODEC0 = (SST_PATH_INDEX_CODEC_OUT0 | SST_DEFAULT_CELL_NBR),
|
||||
SST_SWM_OUT_CODEC1 = (SST_PATH_INDEX_CODEC_OUT1 | SST_DEFAULT_CELL_NBR),
|
||||
SST_SWM_OUT_SPROT_LOOP = (SST_PATH_INDEX_SPROT_LOOP_OUT | SST_DEFAULT_CELL_NBR),
|
||||
SST_SWM_OUT_MEDIA_LOOP1 = (SST_PATH_INDEX_MEDIA_LOOP1_OUT | SST_DEFAULT_CELL_NBR),
|
||||
SST_SWM_OUT_MEDIA_LOOP2 = (SST_PATH_INDEX_MEDIA_LOOP2_OUT | SST_DEFAULT_CELL_NBR),
|
||||
SST_SWM_OUT_VOIP = (SST_PATH_INDEX_VOIP_OUT | SST_DEFAULT_CELL_NBR),
|
||||
SST_SWM_OUT_PCM0 = (SST_PATH_INDEX_PCM0_OUT | SST_DEFAULT_CELL_NBR),
|
||||
SST_SWM_OUT_PCM1 = (SST_PATH_INDEX_PCM1_OUT | SST_DEFAULT_CELL_NBR),
|
||||
SST_SWM_OUT_PCM2 = (SST_PATH_INDEX_PCM2_OUT | SST_DEFAULT_CELL_NBR),
|
||||
SST_SWM_OUT_MEDIA0 = (SST_PATH_INDEX_MEDIA0_OUT | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */
|
||||
SST_SWM_OUT_MEDIA1 = (SST_PATH_INDEX_MEDIA1_OUT | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */
|
||||
SST_SWM_OUT_END = (SST_PATH_INDEX_RESERVED | SST_DEFAULT_CELL_NBR),
|
||||
};
|
||||
|
||||
enum sst_ipc_msg {
|
||||
SST_IPC_IA_CMD = 1,
|
||||
SST_IPC_IA_SET_PARAMS,
|
||||
SST_IPC_IA_GET_PARAMS,
|
||||
};
|
||||
|
||||
enum sst_cmd_type {
|
||||
SST_CMD_BYTES_SET = 1,
|
||||
SST_CMD_BYTES_GET = 2,
|
||||
};
|
||||
|
||||
enum sst_task {
|
||||
SST_TASK_SBA = 1,
|
||||
SST_TASK_MMX,
|
||||
};
|
||||
|
||||
enum sst_type {
|
||||
SST_TYPE_CMD = 1,
|
||||
SST_TYPE_PARAMS,
|
||||
};
|
||||
|
||||
enum sst_flag {
|
||||
SST_FLAG_BLOCKED = 1,
|
||||
SST_FLAG_NONBLOCK,
|
||||
};
|
||||
|
||||
/*
|
||||
* Enumeration for indexing the gain cells in VB_SET_GAIN DSP command
|
||||
*/
|
||||
enum sst_gain_index {
|
||||
/* GAIN IDs for SB task start here */
|
||||
SST_GAIN_INDEX_CODEC_OUT0,
|
||||
SST_GAIN_INDEX_CODEC_OUT1,
|
||||
SST_GAIN_INDEX_CODEC_IN0,
|
||||
SST_GAIN_INDEX_CODEC_IN1,
|
||||
|
||||
SST_GAIN_INDEX_SPROT_LOOP_OUT,
|
||||
SST_GAIN_INDEX_MEDIA_LOOP1_OUT,
|
||||
SST_GAIN_INDEX_MEDIA_LOOP2_OUT,
|
||||
|
||||
SST_GAIN_INDEX_PCM0_IN_LEFT,
|
||||
SST_GAIN_INDEX_PCM0_IN_RIGHT,
|
||||
|
||||
SST_GAIN_INDEX_PCM1_OUT_LEFT,
|
||||
SST_GAIN_INDEX_PCM1_OUT_RIGHT,
|
||||
SST_GAIN_INDEX_PCM1_IN_LEFT,
|
||||
SST_GAIN_INDEX_PCM1_IN_RIGHT,
|
||||
SST_GAIN_INDEX_PCM2_OUT_LEFT,
|
||||
|
||||
SST_GAIN_INDEX_PCM2_OUT_RIGHT,
|
||||
SST_GAIN_INDEX_VOIP_OUT,
|
||||
SST_GAIN_INDEX_VOIP_IN,
|
||||
|
||||
/* Gain IDs for MMX task start here */
|
||||
SST_GAIN_INDEX_MEDIA0_IN_LEFT,
|
||||
SST_GAIN_INDEX_MEDIA0_IN_RIGHT,
|
||||
SST_GAIN_INDEX_MEDIA1_IN_LEFT,
|
||||
SST_GAIN_INDEX_MEDIA1_IN_RIGHT,
|
||||
|
||||
SST_GAIN_INDEX_MEDIA2_IN_LEFT,
|
||||
SST_GAIN_INDEX_MEDIA2_IN_RIGHT,
|
||||
|
||||
SST_GAIN_INDEX_GAIN_END
|
||||
};
|
||||
|
||||
/*
|
||||
* Audio DSP module IDs specified by FW spec
|
||||
* TODO: Update with all modules
|
||||
*/
|
||||
enum sst_module_id {
|
||||
SST_MODULE_ID_PCM = 0x0001,
|
||||
SST_MODULE_ID_MP3 = 0x0002,
|
||||
SST_MODULE_ID_MP24 = 0x0003,
|
||||
SST_MODULE_ID_AAC = 0x0004,
|
||||
SST_MODULE_ID_AACP = 0x0005,
|
||||
SST_MODULE_ID_EAACP = 0x0006,
|
||||
SST_MODULE_ID_WMA9 = 0x0007,
|
||||
SST_MODULE_ID_WMA10 = 0x0008,
|
||||
SST_MODULE_ID_WMA10P = 0x0009,
|
||||
SST_MODULE_ID_RA = 0x000A,
|
||||
SST_MODULE_ID_DDAC3 = 0x000B,
|
||||
SST_MODULE_ID_TRUE_HD = 0x000C,
|
||||
SST_MODULE_ID_HD_PLUS = 0x000D,
|
||||
|
||||
SST_MODULE_ID_SRC = 0x0064,
|
||||
SST_MODULE_ID_DOWNMIX = 0x0066,
|
||||
SST_MODULE_ID_GAIN_CELL = 0x0067,
|
||||
SST_MODULE_ID_SPROT = 0x006D,
|
||||
SST_MODULE_ID_BASS_BOOST = 0x006E,
|
||||
SST_MODULE_ID_STEREO_WDNG = 0x006F,
|
||||
SST_MODULE_ID_AV_REMOVAL = 0x0070,
|
||||
SST_MODULE_ID_MIC_EQ = 0x0071,
|
||||
SST_MODULE_ID_SPL = 0x0072,
|
||||
SST_MODULE_ID_ALGO_VTSV = 0x0073,
|
||||
SST_MODULE_ID_NR = 0x0076,
|
||||
SST_MODULE_ID_BWX = 0x0077,
|
||||
SST_MODULE_ID_DRP = 0x0078,
|
||||
SST_MODULE_ID_MDRP = 0x0079,
|
||||
|
||||
SST_MODULE_ID_ANA = 0x007A,
|
||||
SST_MODULE_ID_AEC = 0x007B,
|
||||
SST_MODULE_ID_NR_SNS = 0x007C,
|
||||
SST_MODULE_ID_SER = 0x007D,
|
||||
SST_MODULE_ID_AGC = 0x007E,
|
||||
|
||||
SST_MODULE_ID_CNI = 0x007F,
|
||||
SST_MODULE_ID_CONTEXT_ALGO_AWARE = 0x0080,
|
||||
SST_MODULE_ID_FIR_24 = 0x0081,
|
||||
SST_MODULE_ID_IIR_24 = 0x0082,
|
||||
|
||||
SST_MODULE_ID_ASRC = 0x0083,
|
||||
SST_MODULE_ID_TONE_GEN = 0x0084,
|
||||
SST_MODULE_ID_BMF = 0x0086,
|
||||
SST_MODULE_ID_EDL = 0x0087,
|
||||
SST_MODULE_ID_GLC = 0x0088,
|
||||
|
||||
SST_MODULE_ID_FIR_16 = 0x0089,
|
||||
SST_MODULE_ID_IIR_16 = 0x008A,
|
||||
SST_MODULE_ID_DNR = 0x008B,
|
||||
|
||||
SST_MODULE_ID_VIRTUALIZER = 0x008C,
|
||||
SST_MODULE_ID_VISUALIZATION = 0x008D,
|
||||
SST_MODULE_ID_LOUDNESS_OPTIMIZER = 0x008E,
|
||||
SST_MODULE_ID_REVERBERATION = 0x008F,
|
||||
|
||||
SST_MODULE_ID_CNI_TX = 0x0090,
|
||||
SST_MODULE_ID_REF_LINE = 0x0091,
|
||||
SST_MODULE_ID_VOLUME = 0x0092,
|
||||
SST_MODULE_ID_FILT_DCR = 0x0094,
|
||||
SST_MODULE_ID_SLV = 0x009A,
|
||||
SST_MODULE_ID_NLF = 0x009B,
|
||||
SST_MODULE_ID_TNR = 0x009C,
|
||||
SST_MODULE_ID_WNR = 0x009D,
|
||||
|
||||
SST_MODULE_ID_LOG = 0xFF00,
|
||||
|
||||
SST_MODULE_ID_TASK = 0xFFFF,
|
||||
};
|
||||
|
||||
enum sst_cmd {
|
||||
SBA_IDLE = 14,
|
||||
SBA_VB_SET_SPEECH_PATH = 26,
|
||||
MMX_SET_GAIN = 33,
|
||||
SBA_VB_SET_GAIN = 33,
|
||||
FBA_VB_RX_CNI = 35,
|
||||
MMX_SET_GAIN_TIMECONST = 36,
|
||||
SBA_VB_SET_TIMECONST = 36,
|
||||
SBA_VB_START = 85,
|
||||
SBA_SET_SWM = 114,
|
||||
SBA_SET_MDRP = 116,
|
||||
SBA_HW_SET_SSP = 117,
|
||||
SBA_SET_MEDIA_LOOP_MAP = 118,
|
||||
SBA_SET_MEDIA_PATH = 119,
|
||||
MMX_SET_MEDIA_PATH = 119,
|
||||
SBA_VB_LPRO = 126,
|
||||
SBA_VB_SET_FIR = 128,
|
||||
SBA_VB_SET_IIR = 129,
|
||||
SBA_SET_SSP_SLOT_MAP = 130,
|
||||
};
|
||||
|
||||
enum sst_dsp_switch {
|
||||
SST_SWITCH_OFF = 0,
|
||||
SST_SWITCH_ON = 3,
|
||||
};
|
||||
|
||||
enum sst_path_switch {
|
||||
SST_PATH_OFF = 0,
|
||||
SST_PATH_ON = 1,
|
||||
};
|
||||
|
||||
enum sst_swm_state {
|
||||
SST_SWM_OFF = 0,
|
||||
SST_SWM_ON = 3,
|
||||
};
|
||||
|
||||
#define SST_FILL_LOCATION_IDS(dst, cell_idx, pipe_id) do { \
|
||||
dst.location_id.p.cell_nbr_idx = (cell_idx); \
|
||||
dst.location_id.p.path_id = (pipe_id); \
|
||||
} while (0)
|
||||
#define SST_FILL_LOCATION_ID(dst, loc_id) (\
|
||||
dst.location_id.f = (loc_id))
|
||||
#define SST_FILL_MODULE_ID(dst, mod_id) (\
|
||||
dst.module_id = (mod_id))
|
||||
|
||||
#define SST_FILL_DESTINATION1(dst, id) do { \
|
||||
SST_FILL_LOCATION_ID(dst, (id) & 0xFFFF); \
|
||||
SST_FILL_MODULE_ID(dst, ((id) & 0xFFFF0000) >> 16); \
|
||||
} while (0)
|
||||
#define SST_FILL_DESTINATION2(dst, loc_id, mod_id) do { \
|
||||
SST_FILL_LOCATION_ID(dst, loc_id); \
|
||||
SST_FILL_MODULE_ID(dst, mod_id); \
|
||||
} while (0)
|
||||
#define SST_FILL_DESTINATION3(dst, cell_idx, path_id, mod_id) do { \
|
||||
SST_FILL_LOCATION_IDS(dst, cell_idx, path_id); \
|
||||
SST_FILL_MODULE_ID(dst, mod_id); \
|
||||
} while (0)
|
||||
|
||||
#define SST_FILL_DESTINATION(level, dst, ...) \
|
||||
SST_FILL_DESTINATION##level(dst, __VA_ARGS__)
|
||||
#define SST_FILL_DEFAULT_DESTINATION(dst) \
|
||||
SST_FILL_DESTINATION(2, dst, SST_DEFAULT_LOCATION_ID, SST_DEFAULT_MODULE_ID)
|
||||
|
||||
struct sst_destination_id {
|
||||
union sst_location_id {
|
||||
struct {
|
||||
u8 cell_nbr_idx; /* module index */
|
||||
u8 path_id; /* pipe_id */
|
||||
} __packed p; /* part */
|
||||
u16 f; /* full */
|
||||
} __packed location_id;
|
||||
u16 module_id;
|
||||
} __packed;
|
||||
struct sst_dsp_header {
|
||||
struct sst_destination_id dst;
|
||||
u16 command_id;
|
||||
u16 length;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
*
|
||||
* Common Commands
|
||||
*
|
||||
*/
|
||||
struct sst_cmd_generic {
|
||||
struct sst_dsp_header header;
|
||||
} __packed;
|
||||
struct sst_cmd_set_params {
|
||||
struct sst_destination_id dst;
|
||||
u16 command_id;
|
||||
char params[0];
|
||||
} __packed;
|
||||
#define SST_CONTROL_NAME(xpname, xmname, xinstance, xtype) \
|
||||
xpname " " xmname " " #xinstance " " xtype
|
||||
|
||||
#define SST_COMBO_CONTROL_NAME(xpname, xmname, xinstance, xtype, xsubmodule) \
|
||||
xpname " " xmname " " #xinstance " " xtype " " xsubmodule
|
||||
enum sst_algo_kcontrol_type {
|
||||
SST_ALGO_PARAMS,
|
||||
SST_ALGO_BYPASS,
|
||||
};
|
||||
|
||||
struct sst_algo_control {
|
||||
enum sst_algo_kcontrol_type type;
|
||||
int max;
|
||||
u16 module_id;
|
||||
u16 pipe_id;
|
||||
u16 task_id;
|
||||
u16 cmd_id;
|
||||
bool bypass;
|
||||
unsigned char *params;
|
||||
struct snd_soc_dapm_widget *w;
|
||||
};
|
||||
|
||||
/* size of the control = size of params + size of length field */
|
||||
#define SST_ALGO_CTL_VALUE(xcount, xtype, xpipe, xmod, xtask, xcmd) \
|
||||
(struct sst_algo_control){ \
|
||||
.max = xcount + sizeof(u16), .type = xtype, .module_id = xmod, \
|
||||
.pipe_id = xpipe, .task_id = xtask, .cmd_id = xcmd, \
|
||||
}
|
||||
|
||||
#define SST_ALGO_KCONTROL(xname, xcount, xmod, xpipe, \
|
||||
xtask, xcmd, xtype, xinfo, xget, xput) \
|
||||
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
|
||||
.name = xname, \
|
||||
.info = xinfo, .get = xget, .put = xput, \
|
||||
.private_value = (unsigned long)& \
|
||||
SST_ALGO_CTL_VALUE(xcount, xtype, xpipe, \
|
||||
xmod, xtask, xcmd), \
|
||||
}
|
||||
|
||||
#define SST_ALGO_KCONTROL_BYTES(xpname, xmname, xcount, xmod, \
|
||||
xpipe, xinstance, xtask, xcmd) \
|
||||
SST_ALGO_KCONTROL(SST_CONTROL_NAME(xpname, xmname, xinstance, "params"), \
|
||||
xcount, xmod, xpipe, xtask, xcmd, SST_ALGO_PARAMS, \
|
||||
sst_algo_bytes_ctl_info, \
|
||||
sst_algo_control_get, sst_algo_control_set)
|
||||
|
||||
#define SST_ALGO_KCONTROL_BOOL(xpname, xmname, xmod, xpipe, xinstance, xtask) \
|
||||
SST_ALGO_KCONTROL(SST_CONTROL_NAME(xpname, xmname, xinstance, "bypass"), \
|
||||
0, xmod, xpipe, xtask, 0, SST_ALGO_BYPASS, \
|
||||
snd_soc_info_bool_ext, \
|
||||
sst_algo_control_get, sst_algo_control_set)
|
||||
|
||||
#define SST_ALGO_BYPASS_PARAMS(xpname, xmname, xcount, xmod, xpipe, \
|
||||
xinstance, xtask, xcmd) \
|
||||
SST_ALGO_KCONTROL_BOOL(xpname, xmname, xmod, xpipe, xinstance, xtask), \
|
||||
SST_ALGO_KCONTROL_BYTES(xpname, xmname, xcount, xmod, xpipe, xinstance, xtask, xcmd)
|
||||
|
||||
#define SST_COMBO_ALGO_KCONTROL_BYTES(xpname, xmname, xsubmod, xcount, xmod, \
|
||||
xpipe, xinstance, xtask, xcmd) \
|
||||
SST_ALGO_KCONTROL(SST_COMBO_CONTROL_NAME(xpname, xmname, xinstance, "params", \
|
||||
xsubmod), \
|
||||
xcount, xmod, xpipe, xtask, xcmd, SST_ALGO_PARAMS, \
|
||||
sst_algo_bytes_ctl_info, \
|
||||
sst_algo_control_get, sst_algo_control_set)
|
||||
|
||||
|
||||
struct sst_enum {
|
||||
bool tx;
|
||||
unsigned short reg;
|
||||
unsigned int max;
|
||||
const char * const *texts;
|
||||
struct snd_soc_dapm_widget *w;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -138,11 +138,10 @@ static inline unsigned int hsw_ipc_to_mixer(u32 value)
|
||||
static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
|
||||
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
|
||||
struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt);
|
||||
struct soc_mixer_control *mc =
|
||||
(struct soc_mixer_control *)kcontrol->private_value;
|
||||
struct hsw_priv_data *pdata =
|
||||
snd_soc_platform_get_drvdata(platform);
|
||||
struct hsw_pcm_data *pcm_data = &pdata->pcm[mc->reg];
|
||||
struct sst_hsw *hsw = pdata->hsw;
|
||||
u32 volume;
|
||||
@ -176,11 +175,10 @@ static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
|
||||
static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
|
||||
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
|
||||
struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt);
|
||||
struct soc_mixer_control *mc =
|
||||
(struct soc_mixer_control *)kcontrol->private_value;
|
||||
struct hsw_priv_data *pdata =
|
||||
snd_soc_platform_get_drvdata(platform);
|
||||
struct hsw_pcm_data *pcm_data = &pdata->pcm[mc->reg];
|
||||
struct sst_hsw *hsw = pdata->hsw;
|
||||
u32 volume;
|
||||
@ -208,8 +206,8 @@ static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol,
|
||||
static int hsw_volume_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
|
||||
struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
|
||||
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
|
||||
struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt);
|
||||
struct sst_hsw *hsw = pdata->hsw;
|
||||
u32 volume;
|
||||
|
||||
@ -233,8 +231,8 @@ static int hsw_volume_put(struct snd_kcontrol *kcontrol,
|
||||
static int hsw_volume_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
|
||||
struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
|
||||
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
|
||||
struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt);
|
||||
struct sst_hsw *hsw = pdata->hsw;
|
||||
unsigned int volume = 0;
|
||||
|
||||
@ -778,20 +776,11 @@ static const struct snd_soc_dapm_route graph[] = {
|
||||
|
||||
static int hsw_pcm_probe(struct snd_soc_platform *platform)
|
||||
{
|
||||
struct hsw_priv_data *priv_data = snd_soc_platform_get_drvdata(platform);
|
||||
struct sst_pdata *pdata = dev_get_platdata(platform->dev);
|
||||
struct hsw_priv_data *priv_data;
|
||||
struct device *dma_dev;
|
||||
struct device *dma_dev = pdata->dma_dev;
|
||||
int i, ret = 0;
|
||||
|
||||
if (!pdata)
|
||||
return -ENODEV;
|
||||
|
||||
dma_dev = pdata->dma_dev;
|
||||
|
||||
priv_data = devm_kzalloc(platform->dev, sizeof(*priv_data), GFP_KERNEL);
|
||||
priv_data->hsw = pdata->dsp;
|
||||
snd_soc_platform_set_drvdata(platform, priv_data);
|
||||
|
||||
/* allocate DSP buffer page tables */
|
||||
for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) {
|
||||
|
||||
@ -848,27 +837,38 @@ static struct snd_soc_platform_driver hsw_soc_platform = {
|
||||
.ops = &hsw_pcm_ops,
|
||||
.pcm_new = hsw_pcm_new,
|
||||
.pcm_free = hsw_pcm_free,
|
||||
.controls = hsw_volume_controls,
|
||||
.num_controls = ARRAY_SIZE(hsw_volume_controls),
|
||||
.dapm_widgets = widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(widgets),
|
||||
.dapm_routes = graph,
|
||||
.num_dapm_routes = ARRAY_SIZE(graph),
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver hsw_dai_component = {
|
||||
.name = "haswell-dai",
|
||||
.name = "haswell-dai",
|
||||
.controls = hsw_volume_controls,
|
||||
.num_controls = ARRAY_SIZE(hsw_volume_controls),
|
||||
.dapm_widgets = widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(widgets),
|
||||
.dapm_routes = graph,
|
||||
.num_dapm_routes = ARRAY_SIZE(graph),
|
||||
};
|
||||
|
||||
static int hsw_pcm_dev_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev);
|
||||
struct hsw_priv_data *priv_data;
|
||||
int ret;
|
||||
|
||||
if (!sst_pdata)
|
||||
return -EINVAL;
|
||||
|
||||
priv_data = devm_kzalloc(&pdev->dev, sizeof(*priv_data), GFP_KERNEL);
|
||||
if (!priv_data)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = sst_hsw_dsp_init(&pdev->dev, sst_pdata);
|
||||
if (ret < 0)
|
||||
return -ENODEV;
|
||||
|
||||
priv_data->hsw = sst_pdata->dsp;
|
||||
platform_set_drvdata(pdev, priv_data);
|
||||
|
||||
ret = snd_soc_register_platform(&pdev->dev, &hsw_soc_platform);
|
||||
if (ret < 0)
|
||||
goto err_plat;
|
||||
|
@ -86,7 +86,7 @@ static int sst_platform_compr_free(struct snd_compr_stream *cstream)
|
||||
/*need to check*/
|
||||
str_id = stream->id;
|
||||
if (str_id)
|
||||
ret_val = stream->compr_ops->close(str_id);
|
||||
ret_val = stream->compr_ops->close(sst->dev, str_id);
|
||||
module_put(sst->dev->driver->owner);
|
||||
kfree(stream);
|
||||
pr_debug("%s: %d\n", __func__, ret_val);
|
||||
@ -158,7 +158,7 @@ static int sst_platform_compr_set_params(struct snd_compr_stream *cstream,
|
||||
cb.drain_cb_param = cstream;
|
||||
cb.drain_notify = sst_drain_notify;
|
||||
|
||||
retval = stream->compr_ops->open(&str_params, &cb);
|
||||
retval = stream->compr_ops->open(sst->dev, &str_params, &cb);
|
||||
if (retval < 0) {
|
||||
pr_err("stream allocation failed %d\n", retval);
|
||||
return retval;
|
||||
@ -170,10 +170,30 @@ static int sst_platform_compr_set_params(struct snd_compr_stream *cstream,
|
||||
|
||||
static int sst_platform_compr_trigger(struct snd_compr_stream *cstream, int cmd)
|
||||
{
|
||||
struct sst_runtime_stream *stream =
|
||||
cstream->runtime->private_data;
|
||||
struct sst_runtime_stream *stream = cstream->runtime->private_data;
|
||||
|
||||
return stream->compr_ops->control(cmd, stream->id);
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
if (stream->compr_ops->stream_start)
|
||||
return stream->compr_ops->stream_start(sst->dev, stream->id);
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
if (stream->compr_ops->stream_drop)
|
||||
return stream->compr_ops->stream_drop(sst->dev, stream->id);
|
||||
case SND_COMPR_TRIGGER_DRAIN:
|
||||
if (stream->compr_ops->stream_drain)
|
||||
return stream->compr_ops->stream_drain(sst->dev, stream->id);
|
||||
case SND_COMPR_TRIGGER_PARTIAL_DRAIN:
|
||||
if (stream->compr_ops->stream_partial_drain)
|
||||
return stream->compr_ops->stream_partial_drain(sst->dev, stream->id);
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
if (stream->compr_ops->stream_pause)
|
||||
return stream->compr_ops->stream_pause(sst->dev, stream->id);
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
if (stream->compr_ops->stream_pause_release)
|
||||
return stream->compr_ops->stream_pause_release(sst->dev, stream->id);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int sst_platform_compr_pointer(struct snd_compr_stream *cstream,
|
||||
@ -182,7 +202,7 @@ static int sst_platform_compr_pointer(struct snd_compr_stream *cstream,
|
||||
struct sst_runtime_stream *stream;
|
||||
|
||||
stream = cstream->runtime->private_data;
|
||||
stream->compr_ops->tstamp(stream->id, tstamp);
|
||||
stream->compr_ops->tstamp(sst->dev, stream->id, tstamp);
|
||||
tstamp->byte_offset = tstamp->copied_total %
|
||||
(u32)cstream->runtime->buffer_size;
|
||||
pr_debug("calc bytes offset/copied bytes as %d\n", tstamp->byte_offset);
|
||||
@ -195,7 +215,7 @@ static int sst_platform_compr_ack(struct snd_compr_stream *cstream,
|
||||
struct sst_runtime_stream *stream;
|
||||
|
||||
stream = cstream->runtime->private_data;
|
||||
stream->compr_ops->ack(stream->id, (unsigned long)bytes);
|
||||
stream->compr_ops->ack(sst->dev, stream->id, (unsigned long)bytes);
|
||||
stream->bytes_written += bytes;
|
||||
|
||||
return 0;
|
||||
@ -225,7 +245,7 @@ static int sst_platform_compr_set_metadata(struct snd_compr_stream *cstream,
|
||||
struct sst_runtime_stream *stream =
|
||||
cstream->runtime->private_data;
|
||||
|
||||
return stream->compr_ops->set_metadata(stream->id, metadata);
|
||||
return stream->compr_ops->set_metadata(sst->dev, stream->id, metadata);
|
||||
}
|
||||
|
||||
struct snd_compr_ops sst_platform_compr_ops = {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user