Merge branch 'asoc-4.22' into asoc-5.0
This commit is contained in:
commit
aa07e38b0a
@ -1,123 +0,0 @@
|
|||||||
Audio-Graph-SCU-Card:
|
|
||||||
|
|
||||||
Audio-Graph-SCU-Card is "Audio-Graph-Card" + "ALSA DPCM".
|
|
||||||
|
|
||||||
It is based on common bindings for device graphs.
|
|
||||||
see ${LINUX}/Documentation/devicetree/bindings/graph.txt
|
|
||||||
|
|
||||||
Basically, Audio-Graph-SCU-Card property is same as
|
|
||||||
Simple-Card / Simple-SCU-Card / Audio-Graph-Card.
|
|
||||||
see ${LINUX}/Documentation/devicetree/bindings/sound/simple-card.txt
|
|
||||||
${LINUX}/Documentation/devicetree/bindings/sound/simple-scu-card.txt
|
|
||||||
${LINUX}/Documentation/devicetree/bindings/sound/audio-graph-card.txt
|
|
||||||
|
|
||||||
Below are same as Simple-Card / Audio-Graph-Card.
|
|
||||||
|
|
||||||
- label
|
|
||||||
- dai-format
|
|
||||||
- frame-master
|
|
||||||
- bitclock-master
|
|
||||||
- bitclock-inversion
|
|
||||||
- frame-inversion
|
|
||||||
- dai-tdm-slot-num
|
|
||||||
- dai-tdm-slot-width
|
|
||||||
- clocks / system-clock-frequency
|
|
||||||
|
|
||||||
Below are same as Simple-SCU-Card.
|
|
||||||
|
|
||||||
- convert-rate
|
|
||||||
- convert-channels
|
|
||||||
- prefix
|
|
||||||
- routing
|
|
||||||
|
|
||||||
Required properties:
|
|
||||||
|
|
||||||
- compatible : "audio-graph-scu-card";
|
|
||||||
- dais : list of CPU DAI port{s}
|
|
||||||
|
|
||||||
Example 1. Sampling Rate Conversion
|
|
||||||
|
|
||||||
sound_card {
|
|
||||||
compatible = "audio-graph-scu-card";
|
|
||||||
|
|
||||||
label = "sound-card";
|
|
||||||
prefix = "codec";
|
|
||||||
routing = "codec Playback", "DAI0 Playback",
|
|
||||||
"DAI0 Capture", "codec Capture";
|
|
||||||
convert-rate = <48000>;
|
|
||||||
|
|
||||||
dais = <&cpu_port>;
|
|
||||||
};
|
|
||||||
|
|
||||||
audio-codec {
|
|
||||||
...
|
|
||||||
|
|
||||||
port {
|
|
||||||
codec_endpoint: endpoint {
|
|
||||||
remote-endpoint = <&cpu_endpoint>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
dai-controller {
|
|
||||||
...
|
|
||||||
cpu_port: port {
|
|
||||||
cpu_endpoint: endpoint {
|
|
||||||
remote-endpoint = <&codec_endpoint>;
|
|
||||||
|
|
||||||
dai-format = "left_j";
|
|
||||||
...
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
Example 2. 2 CPU 1 Codec (Mixing)
|
|
||||||
|
|
||||||
sound_card {
|
|
||||||
compatible = "audio-graph-scu-card";
|
|
||||||
|
|
||||||
label = "sound-card";
|
|
||||||
routing = "codec Playback", "DAI0 Playback",
|
|
||||||
"codec Playback", "DAI1 Playback",
|
|
||||||
"DAI0 Capture", "codec Capture";
|
|
||||||
|
|
||||||
dais = <&cpu_port0
|
|
||||||
&cpu_port1>;
|
|
||||||
};
|
|
||||||
|
|
||||||
audio-codec {
|
|
||||||
...
|
|
||||||
|
|
||||||
audio-graph-card,prefix = "codec";
|
|
||||||
audio-graph-card,convert-rate = <48000>;
|
|
||||||
port {
|
|
||||||
codec_endpoint0: endpoint {
|
|
||||||
remote-endpoint = <&cpu_endpoint0>;
|
|
||||||
};
|
|
||||||
codec_endpoint1: endpoint {
|
|
||||||
remote-endpoint = <&cpu_endpoint1>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
dai-controller {
|
|
||||||
...
|
|
||||||
ports {
|
|
||||||
cpu_port0: port {
|
|
||||||
cpu_endpoint0: endpoint {
|
|
||||||
remote-endpoint = <&codec_endpoint0>;
|
|
||||||
|
|
||||||
dai-format = "left_j";
|
|
||||||
...
|
|
||||||
};
|
|
||||||
};
|
|
||||||
cpu_port1: port {
|
|
||||||
cpu_endpoint1: endpoint {
|
|
||||||
remote-endpoint = <&codec_endpoint1>;
|
|
||||||
|
|
||||||
dai-format = "left_j";
|
|
||||||
...
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
22
Documentation/devicetree/bindings/sound/cs4341.txt
Normal file
22
Documentation/devicetree/bindings/sound/cs4341.txt
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
Cirrus Logic CS4341 audio DAC
|
||||||
|
|
||||||
|
This device supports both I2C and SPI (configured with pin strapping
|
||||||
|
on the board).
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: "cirrus,cs4341a"
|
||||||
|
- reg : the I2C address of the device for I2C, the chip select
|
||||||
|
number for SPI.
|
||||||
|
|
||||||
|
For required properties on I2C-bus, please consult
|
||||||
|
Documentation/devicetree/bindings/i2c/i2c.txt
|
||||||
|
For required properties on SPI-bus, please consult
|
||||||
|
Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||||
|
|
||||||
|
Example:
|
||||||
|
codec: cs4341@0 {
|
||||||
|
#sound-dai-cells = <0>;
|
||||||
|
compatible = "cirrus,cs4341a";
|
||||||
|
reg = <0>;
|
||||||
|
spi-max-frequency = <6000000>;
|
||||||
|
};
|
@ -45,6 +45,23 @@ Optional properties:
|
|||||||
- fck_parent : Should contain a valid clock name which will be used as parent
|
- fck_parent : Should contain a valid clock name which will be used as parent
|
||||||
for the McASP fck
|
for the McASP fck
|
||||||
|
|
||||||
|
Optional GPIO support:
|
||||||
|
If any McASP pin need to be used as GPIO then the McASP node must have:
|
||||||
|
...
|
||||||
|
gpio-controller
|
||||||
|
#gpio-cells = <2>;
|
||||||
|
...
|
||||||
|
|
||||||
|
When requesting a GPIO, the first parameter is the PIN index in McASP_P*
|
||||||
|
registers.
|
||||||
|
For example to request the AXR2 pin of mcasp8:
|
||||||
|
function-gpios = <&mcasp8 2 0>;
|
||||||
|
|
||||||
|
Or to request the ACLKR pin of mcasp8:
|
||||||
|
function-gpios = <&mcasp8 29 0>;
|
||||||
|
|
||||||
|
For generic gpio information, please refer to bindings/gpio/gpio.txt
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
mcasp0: mcasp0@1d00000 {
|
mcasp0: mcasp0@1d00000 {
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
* Rockchip Rk3328 internal codec
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
|
||||||
|
- compatible: "rockchip,rk3328-codec"
|
||||||
|
- reg: physical base address of the controller and length of memory mapped
|
||||||
|
region.
|
||||||
|
- rockchip,grf: the phandle of the syscon node for GRF register.
|
||||||
|
- clocks: a list of phandle + clock-specifer pairs, one for each entry in clock-names.
|
||||||
|
- clock-names: should be "pclk".
|
||||||
|
- spk-depop-time-ms: speak depop time msec.
|
||||||
|
|
||||||
|
Example for rk3328 internal codec:
|
||||||
|
|
||||||
|
codec: codec@ff410000 {
|
||||||
|
compatible = "rockchip,rk3328-codec";
|
||||||
|
reg = <0x0 0xff410000 0x0 0x1000>;
|
||||||
|
rockchip,grf = <&grf>;
|
||||||
|
clocks = <&cru PCLK_ACODEC>;
|
||||||
|
clock-names = "pclk";
|
||||||
|
spk-depop-time-ms = 100;
|
||||||
|
status = "disabled";
|
||||||
|
};
|
@ -1,94 +0,0 @@
|
|||||||
ASoC Simple SCU Sound Card
|
|
||||||
|
|
||||||
Simple SCU Sound Card is "Simple Sound Card" + "ALSA DPCM".
|
|
||||||
For example, you can use this driver if you want to exchange sampling rate convert,
|
|
||||||
Mixing, etc...
|
|
||||||
|
|
||||||
Required properties:
|
|
||||||
|
|
||||||
- compatible : "simple-scu-audio-card"
|
|
||||||
"renesas,rsrc-card"
|
|
||||||
Optional properties:
|
|
||||||
|
|
||||||
- simple-audio-card,name : see simple-audio-card.txt
|
|
||||||
- simple-audio-card,cpu : see simple-audio-card.txt
|
|
||||||
- simple-audio-card,codec : see simple-audio-card.txt
|
|
||||||
|
|
||||||
Optional subnode properties:
|
|
||||||
|
|
||||||
- simple-audio-card,format : see simple-audio-card.txt
|
|
||||||
- simple-audio-card,frame-master : see simple-audio-card.txt
|
|
||||||
- simple-audio-card,bitclock-master : see simple-audio-card.txt
|
|
||||||
- simple-audio-card,bitclock-inversion : see simple-audio-card.txt
|
|
||||||
- simple-audio-card,frame-inversion : see simple-audio-card.txt
|
|
||||||
- simple-audio-card,convert-rate : platform specified sampling rate convert
|
|
||||||
- simple-audio-card,convert-channels : platform specified converted channel size (2 - 8 ch)
|
|
||||||
- simple-audio-card,prefix : see routing
|
|
||||||
- simple-audio-card,widgets : Please refer to widgets.txt.
|
|
||||||
- simple-audio-card,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 for sources.
|
|
||||||
use audio-prefix if some components is using same sink/sources naming.
|
|
||||||
it can be used if compatible was "renesas,rsrc-card";
|
|
||||||
|
|
||||||
Required CPU/CODEC subnodes properties:
|
|
||||||
|
|
||||||
- sound-dai : see simple-audio-card.txt
|
|
||||||
|
|
||||||
Optional CPU/CODEC subnodes properties:
|
|
||||||
|
|
||||||
- clocks / system-clock-frequency : see simple-audio-card.txt
|
|
||||||
|
|
||||||
Example 1. Sampling Rate Conversion
|
|
||||||
|
|
||||||
sound {
|
|
||||||
compatible = "simple-scu-audio-card";
|
|
||||||
|
|
||||||
simple-audio-card,name = "rsnd-ak4643";
|
|
||||||
simple-audio-card,format = "left_j";
|
|
||||||
simple-audio-card,bitclock-master = <&sndcodec>;
|
|
||||||
simple-audio-card,frame-master = <&sndcodec>;
|
|
||||||
|
|
||||||
simple-audio-card,convert-rate = <48000>;
|
|
||||||
|
|
||||||
simple-audio-card,prefix = "ak4642";
|
|
||||||
simple-audio-card,routing = "ak4642 Playback", "DAI0 Playback",
|
|
||||||
"DAI0 Capture", "ak4642 Capture";
|
|
||||||
|
|
||||||
sndcpu: simple-audio-card,cpu {
|
|
||||||
sound-dai = <&rcar_sound>;
|
|
||||||
};
|
|
||||||
|
|
||||||
sndcodec: simple-audio-card,codec {
|
|
||||||
sound-dai = <&ak4643>;
|
|
||||||
system-clock-frequency = <11289600>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
Example 2. 2 CPU 1 Codec (Mixing)
|
|
||||||
|
|
||||||
sound {
|
|
||||||
compatible = "simple-scu-audio-card";
|
|
||||||
|
|
||||||
simple-audio-card,name = "rsnd-ak4643";
|
|
||||||
simple-audio-card,format = "left_j";
|
|
||||||
simple-audio-card,bitclock-master = <&dpcmcpu>;
|
|
||||||
simple-audio-card,frame-master = <&dpcmcpu>;
|
|
||||||
|
|
||||||
simple-audio-card,routing = "ak4642 Playback", "DAI0 Playback",
|
|
||||||
"ak4642 Playback", "DAI1 Playback";
|
|
||||||
|
|
||||||
dpcmcpu: cpu@0 {
|
|
||||||
sound-dai = <&rcar_sound 0>;
|
|
||||||
};
|
|
||||||
|
|
||||||
cpu@1 {
|
|
||||||
sound-dai = <&rcar_sound 1>;
|
|
||||||
};
|
|
||||||
|
|
||||||
codec {
|
|
||||||
prefix = "ak4642";
|
|
||||||
sound-dai = <&ak4643>;
|
|
||||||
clocks = <&audio_clock>;
|
|
||||||
};
|
|
||||||
};
|
|
@ -0,0 +1,29 @@
|
|||||||
|
Device-Tree bindings for Xilinx PL audio formatter
|
||||||
|
|
||||||
|
The IP core supports DMA, data formatting(AES<->PCM conversion)
|
||||||
|
of audio samples.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: "xlnx,audio-formatter-1.0"
|
||||||
|
- interrupt-names: Names specified to list of interrupts in same
|
||||||
|
order mentioned under "interrupts".
|
||||||
|
List of supported interrupt names are:
|
||||||
|
"irq_mm2s" : interrupt from MM2S block
|
||||||
|
"irq_s2mm" : interrupt from S2MM block
|
||||||
|
- interrupts-parent: Phandle for interrupt controller.
|
||||||
|
- interrupts: List of Interrupt numbers.
|
||||||
|
- reg: Base address and size of the IP core instance.
|
||||||
|
- clock-names: List of input clocks.
|
||||||
|
Required elements: "s_axi_lite_aclk", "aud_mclk"
|
||||||
|
- clocks: Input clock specifier. Refer to common clock bindings.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
audio_ss_0_audio_formatter_0: audio_formatter@80010000 {
|
||||||
|
compatible = "xlnx,audio-formatter-1.0";
|
||||||
|
interrupt-names = "irq_mm2s", "irq_s2mm";
|
||||||
|
interrupt-parent = <&gic>;
|
||||||
|
interrupts = <0 104 4>, <0 105 4>;
|
||||||
|
reg = <0x0 0x80010000 0x0 0x1000>;
|
||||||
|
clock-names = "s_axi_lite_aclk", "aud_mclk";
|
||||||
|
clocks = <&clk 71>, <&clk_wiz_1 0>;
|
||||||
|
};
|
@ -541,7 +541,8 @@ static int snd_compress_check_input(struct snd_compr_params *params)
|
|||||||
{
|
{
|
||||||
/* first let's check the buffer parameter's */
|
/* first let's check the buffer parameter's */
|
||||||
if (params->buffer.fragment_size == 0 ||
|
if (params->buffer.fragment_size == 0 ||
|
||||||
params->buffer.fragments > INT_MAX / params->buffer.fragment_size)
|
params->buffer.fragments > INT_MAX / params->buffer.fragment_size ||
|
||||||
|
params->buffer.fragments == 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
/* now codec parameters */
|
/* now codec parameters */
|
||||||
|
@ -65,6 +65,7 @@ config SND_SOC_ALL_CODECS
|
|||||||
select SND_SOC_CS4271_SPI if SPI_MASTER
|
select SND_SOC_CS4271_SPI if SPI_MASTER
|
||||||
select SND_SOC_CS42XX8_I2C if I2C
|
select SND_SOC_CS42XX8_I2C if I2C
|
||||||
select SND_SOC_CS43130 if I2C
|
select SND_SOC_CS43130 if I2C
|
||||||
|
select SND_SOC_CS4341 if SND_SOC_I2C_AND_SPI
|
||||||
select SND_SOC_CS4349 if I2C
|
select SND_SOC_CS4349 if I2C
|
||||||
select SND_SOC_CS47L24 if MFD_CS47L24
|
select SND_SOC_CS47L24 if MFD_CS47L24
|
||||||
select SND_SOC_CS53L30 if I2C
|
select SND_SOC_CS53L30 if I2C
|
||||||
@ -129,6 +130,7 @@ config SND_SOC_ALL_CODECS
|
|||||||
select SND_SOC_PCM5102A
|
select SND_SOC_PCM5102A
|
||||||
select SND_SOC_PCM512x_I2C if I2C
|
select SND_SOC_PCM512x_I2C if I2C
|
||||||
select SND_SOC_PCM512x_SPI if SPI_MASTER
|
select SND_SOC_PCM512x_SPI if SPI_MASTER
|
||||||
|
select SND_SOC_RK3328
|
||||||
select SND_SOC_RT274 if I2C
|
select SND_SOC_RT274 if I2C
|
||||||
select SND_SOC_RT286 if I2C
|
select SND_SOC_RT286 if I2C
|
||||||
select SND_SOC_RT298 if I2C
|
select SND_SOC_RT298 if I2C
|
||||||
@ -542,6 +544,12 @@ config SND_SOC_CS43130
|
|||||||
tristate "Cirrus Logic CS43130 CODEC"
|
tristate "Cirrus Logic CS43130 CODEC"
|
||||||
depends on I2C
|
depends on I2C
|
||||||
|
|
||||||
|
config SND_SOC_CS4341
|
||||||
|
tristate "Cirrus Logic CS4341 CODEC"
|
||||||
|
depends on I2C || SPI_MASTER
|
||||||
|
select REGMAP_I2C if I2C
|
||||||
|
select REGMAP_SPI if SPI_MASTER
|
||||||
|
|
||||||
# Cirrus Logic CS4349 HiFi DAC
|
# Cirrus Logic CS4349 HiFi DAC
|
||||||
config SND_SOC_CS4349
|
config SND_SOC_CS4349
|
||||||
tristate "Cirrus Logic CS4349 CODEC"
|
tristate "Cirrus Logic CS4349 CODEC"
|
||||||
@ -799,6 +807,10 @@ config SND_SOC_PCM512x_SPI
|
|||||||
select SND_SOC_PCM512x
|
select SND_SOC_PCM512x
|
||||||
select REGMAP_SPI
|
select REGMAP_SPI
|
||||||
|
|
||||||
|
config SND_SOC_RK3328
|
||||||
|
tristate "Rockchip RK3328 audio CODEC"
|
||||||
|
select REGMAP_MMIO
|
||||||
|
|
||||||
config SND_SOC_RL6231
|
config SND_SOC_RL6231
|
||||||
tristate
|
tristate
|
||||||
default y if SND_SOC_RT5514=y
|
default y if SND_SOC_RT5514=y
|
||||||
@ -1211,7 +1223,8 @@ config SND_SOC_WM8903
|
|||||||
depends on I2C
|
depends on I2C
|
||||||
|
|
||||||
config SND_SOC_WM8904
|
config SND_SOC_WM8904
|
||||||
tristate
|
tristate "Wolfson Microelectronics WM8904 CODEC"
|
||||||
|
depends on I2C
|
||||||
|
|
||||||
config SND_SOC_WM8940
|
config SND_SOC_WM8940
|
||||||
tristate
|
tristate
|
||||||
|
@ -60,6 +60,7 @@ snd-soc-cs4271-spi-objs := cs4271-spi.o
|
|||||||
snd-soc-cs42xx8-objs := cs42xx8.o
|
snd-soc-cs42xx8-objs := cs42xx8.o
|
||||||
snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o
|
snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o
|
||||||
snd-soc-cs43130-objs := cs43130.o
|
snd-soc-cs43130-objs := cs43130.o
|
||||||
|
snd-soc-cs4341-objs := cs4341.o
|
||||||
snd-soc-cs4349-objs := cs4349.o
|
snd-soc-cs4349-objs := cs4349.o
|
||||||
snd-soc-cs47l24-objs := cs47l24.o
|
snd-soc-cs47l24-objs := cs47l24.o
|
||||||
snd-soc-cs53l30-objs := cs53l30.o
|
snd-soc-cs53l30-objs := cs53l30.o
|
||||||
@ -132,6 +133,7 @@ snd-soc-pcm5102a-objs := pcm5102a.o
|
|||||||
snd-soc-pcm512x-objs := pcm512x.o
|
snd-soc-pcm512x-objs := pcm512x.o
|
||||||
snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o
|
snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o
|
||||||
snd-soc-pcm512x-spi-objs := pcm512x-spi.o
|
snd-soc-pcm512x-spi-objs := pcm512x-spi.o
|
||||||
|
snd-soc-rk3328-objs := rk3328_codec.o
|
||||||
snd-soc-rl6231-objs := rl6231.o
|
snd-soc-rl6231-objs := rl6231.o
|
||||||
snd-soc-rl6347a-objs := rl6347a.o
|
snd-soc-rl6347a-objs := rl6347a.o
|
||||||
snd-soc-rt1305-objs := rt1305.o
|
snd-soc-rt1305-objs := rt1305.o
|
||||||
@ -326,6 +328,7 @@ obj-$(CONFIG_SND_SOC_CS4271_SPI) += snd-soc-cs4271-spi.o
|
|||||||
obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o
|
obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o
|
||||||
obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o
|
obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o
|
||||||
obj-$(CONFIG_SND_SOC_CS43130) += snd-soc-cs43130.o
|
obj-$(CONFIG_SND_SOC_CS43130) += snd-soc-cs43130.o
|
||||||
|
obj-$(CONFIG_SND_SOC_CS4341) += snd-soc-cs4341.o
|
||||||
obj-$(CONFIG_SND_SOC_CS4349) += snd-soc-cs4349.o
|
obj-$(CONFIG_SND_SOC_CS4349) += snd-soc-cs4349.o
|
||||||
obj-$(CONFIG_SND_SOC_CS47L24) += snd-soc-cs47l24.o
|
obj-$(CONFIG_SND_SOC_CS47L24) += snd-soc-cs47l24.o
|
||||||
obj-$(CONFIG_SND_SOC_CS53L30) += snd-soc-cs53l30.o
|
obj-$(CONFIG_SND_SOC_CS53L30) += snd-soc-cs53l30.o
|
||||||
@ -398,6 +401,7 @@ obj-$(CONFIG_SND_SOC_PCM5102A) += snd-soc-pcm5102a.o
|
|||||||
obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o
|
obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o
|
||||||
obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o
|
obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o
|
||||||
obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o
|
obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o
|
||||||
|
obj-$(CONFIG_SND_SOC_RK3328) += snd-soc-rk3328.o
|
||||||
obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o
|
obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o
|
||||||
obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o
|
obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o
|
||||||
obj-$(CONFIG_SND_SOC_RT1305) += snd-soc-rt1305.o
|
obj-$(CONFIG_SND_SOC_RT1305) += snd-soc-rt1305.o
|
||||||
|
346
sound/soc/codecs/cs4341.c
Normal file
346
sound/soc/codecs/cs4341.c
Normal file
@ -0,0 +1,346 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||||
|
/*
|
||||||
|
* Cirrus Logic CS4341A ALSA SoC Codec Driver
|
||||||
|
* Author: Alexander Shiyan <shc_work@mail.ru>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/spi/spi.h>
|
||||||
|
|
||||||
|
#include <sound/pcm.h>
|
||||||
|
#include <sound/pcm_params.h>
|
||||||
|
#include <sound/soc.h>
|
||||||
|
#include <sound/tlv.h>
|
||||||
|
|
||||||
|
#define CS4341_REG_MODE1 0x00
|
||||||
|
#define CS4341_REG_MODE2 0x01
|
||||||
|
#define CS4341_REG_MIX 0x02
|
||||||
|
#define CS4341_REG_VOLA 0x03
|
||||||
|
#define CS4341_REG_VOLB 0x04
|
||||||
|
|
||||||
|
#define CS4341_MODE2_DIF (7 << 4)
|
||||||
|
#define CS4341_MODE2_DIF_I2S_24 (0 << 4)
|
||||||
|
#define CS4341_MODE2_DIF_I2S_16 (1 << 4)
|
||||||
|
#define CS4341_MODE2_DIF_LJ_24 (2 << 4)
|
||||||
|
#define CS4341_MODE2_DIF_RJ_24 (3 << 4)
|
||||||
|
#define CS4341_MODE2_DIF_RJ_16 (5 << 4)
|
||||||
|
#define CS4341_VOLX_MUTE (1 << 7)
|
||||||
|
|
||||||
|
struct cs4341_priv {
|
||||||
|
unsigned int fmt;
|
||||||
|
struct regmap *regmap;
|
||||||
|
struct regmap_config regcfg;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct reg_default cs4341_reg_defaults[] = {
|
||||||
|
{ CS4341_REG_MODE1, 0x00 },
|
||||||
|
{ CS4341_REG_MODE2, 0x82 },
|
||||||
|
{ CS4341_REG_MIX, 0x49 },
|
||||||
|
{ CS4341_REG_VOLA, 0x80 },
|
||||||
|
{ CS4341_REG_VOLB, 0x80 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static int cs4341_set_fmt(struct snd_soc_dai *dai, unsigned int format)
|
||||||
|
{
|
||||||
|
struct snd_soc_component *component = dai->component;
|
||||||
|
struct cs4341_priv *cs4341 = snd_soc_component_get_drvdata(component);
|
||||||
|
|
||||||
|
switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||||
|
case SND_SOC_DAIFMT_CBS_CFS:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (format & SND_SOC_DAIFMT_INV_MASK) {
|
||||||
|
case SND_SOC_DAIFMT_NB_NF:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||||
|
case SND_SOC_DAIFMT_I2S:
|
||||||
|
case SND_SOC_DAIFMT_LEFT_J:
|
||||||
|
case SND_SOC_DAIFMT_RIGHT_J:
|
||||||
|
cs4341->fmt = format & SND_SOC_DAIFMT_FORMAT_MASK;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cs4341_hw_params(struct snd_pcm_substream *substream,
|
||||||
|
struct snd_pcm_hw_params *params,
|
||||||
|
struct snd_soc_dai *dai)
|
||||||
|
{
|
||||||
|
struct snd_soc_component *component = dai->component;
|
||||||
|
struct cs4341_priv *cs4341 = snd_soc_component_get_drvdata(component);
|
||||||
|
unsigned int mode = 0;
|
||||||
|
int b24 = 0;
|
||||||
|
|
||||||
|
switch (params_format(params)) {
|
||||||
|
case SNDRV_PCM_FORMAT_S24_LE:
|
||||||
|
b24 = 1;
|
||||||
|
break;
|
||||||
|
case SNDRV_PCM_FORMAT_S16_LE:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dev_err(component->dev, "Unsupported PCM format 0x%08x.\n",
|
||||||
|
params_format(params));
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (cs4341->fmt) {
|
||||||
|
case SND_SOC_DAIFMT_I2S:
|
||||||
|
mode = b24 ? CS4341_MODE2_DIF_I2S_24 : CS4341_MODE2_DIF_I2S_16;
|
||||||
|
break;
|
||||||
|
case SND_SOC_DAIFMT_LEFT_J:
|
||||||
|
mode = CS4341_MODE2_DIF_LJ_24;
|
||||||
|
break;
|
||||||
|
case SND_SOC_DAIFMT_RIGHT_J:
|
||||||
|
mode = b24 ? CS4341_MODE2_DIF_RJ_24 : CS4341_MODE2_DIF_RJ_16;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dev_err(component->dev, "Unsupported DAI format 0x%08x.\n",
|
||||||
|
cs4341->fmt);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return snd_soc_component_update_bits(component, CS4341_REG_MODE2,
|
||||||
|
CS4341_MODE2_DIF, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cs4341_digital_mute(struct snd_soc_dai *dai, int mute)
|
||||||
|
{
|
||||||
|
struct snd_soc_component *component = dai->component;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = snd_soc_component_update_bits(component, CS4341_REG_VOLA,
|
||||||
|
CS4341_VOLX_MUTE,
|
||||||
|
mute ? CS4341_VOLX_MUTE : 0);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return snd_soc_component_update_bits(component, CS4341_REG_VOLB,
|
||||||
|
CS4341_VOLX_MUTE,
|
||||||
|
mute ? CS4341_VOLX_MUTE : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static DECLARE_TLV_DB_SCALE(out_tlv, -9000, 100, 0);
|
||||||
|
|
||||||
|
static const char * const deemph[] = {
|
||||||
|
"None", "44.1k", "48k", "32k",
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct soc_enum deemph_enum =
|
||||||
|
SOC_ENUM_SINGLE(CS4341_REG_MODE2, 2, 4, deemph);
|
||||||
|
|
||||||
|
static const char * const srzc[] = {
|
||||||
|
"Immediate", "Zero Cross", "Soft Ramp", "SR on ZC",
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct soc_enum srzc_enum =
|
||||||
|
SOC_ENUM_SINGLE(CS4341_REG_MIX, 5, 4, srzc);
|
||||||
|
|
||||||
|
|
||||||
|
static const struct snd_soc_dapm_widget cs4341_dapm_widgets[] = {
|
||||||
|
SND_SOC_DAPM_DAC("HiFi DAC", NULL, SND_SOC_NOPM, 0, 0),
|
||||||
|
SND_SOC_DAPM_OUTPUT("OutA"),
|
||||||
|
SND_SOC_DAPM_OUTPUT("OutB"),
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct snd_soc_dapm_route cs4341_routes[] = {
|
||||||
|
{ "OutA", NULL, "HiFi DAC" },
|
||||||
|
{ "OutB", NULL, "HiFi DAC" },
|
||||||
|
{ "DAC Playback", NULL, "OutA" },
|
||||||
|
{ "DAC Playback", NULL, "OutB" },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct snd_kcontrol_new cs4341_controls[] = {
|
||||||
|
SOC_DOUBLE_R_TLV("Master Playback Volume",
|
||||||
|
CS4341_REG_VOLA, CS4341_REG_VOLB, 0, 90, 1, out_tlv),
|
||||||
|
SOC_ENUM("De-Emphasis Control", deemph_enum),
|
||||||
|
SOC_ENUM("Soft Ramp Zero Cross Control", srzc_enum),
|
||||||
|
SOC_SINGLE("Auto-Mute Switch", CS4341_REG_MODE2, 7, 1, 0),
|
||||||
|
SOC_SINGLE("Popguard Transient Switch", CS4341_REG_MODE2, 1, 1, 0),
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct snd_soc_dai_ops cs4341_dai_ops = {
|
||||||
|
.set_fmt = cs4341_set_fmt,
|
||||||
|
.hw_params = cs4341_hw_params,
|
||||||
|
.digital_mute = cs4341_digital_mute,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct snd_soc_dai_driver cs4341_dai = {
|
||||||
|
.name = "cs4341a-hifi",
|
||||||
|
.playback = {
|
||||||
|
.stream_name = "DAC Playback",
|
||||||
|
.channels_min = 1,
|
||||||
|
.channels_max = 2,
|
||||||
|
.rates = SNDRV_PCM_RATE_8000_96000,
|
||||||
|
.formats = SNDRV_PCM_FMTBIT_S16_LE |
|
||||||
|
SNDRV_PCM_FMTBIT_S24_LE,
|
||||||
|
},
|
||||||
|
.ops = &cs4341_dai_ops,
|
||||||
|
.symmetric_rates = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct snd_soc_component_driver soc_component_cs4341 = {
|
||||||
|
.controls = cs4341_controls,
|
||||||
|
.num_controls = ARRAY_SIZE(cs4341_controls),
|
||||||
|
.dapm_widgets = cs4341_dapm_widgets,
|
||||||
|
.num_dapm_widgets = ARRAY_SIZE(cs4341_dapm_widgets),
|
||||||
|
.dapm_routes = cs4341_routes,
|
||||||
|
.num_dapm_routes = ARRAY_SIZE(cs4341_routes),
|
||||||
|
.idle_bias_on = 1,
|
||||||
|
.use_pmdown_time = 1,
|
||||||
|
.endianness = 1,
|
||||||
|
.non_legacy_dai_naming = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct of_device_id __maybe_unused cs4341_dt_ids[] = {
|
||||||
|
{ .compatible = "cirrus,cs4341a", },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, cs4341_dt_ids);
|
||||||
|
|
||||||
|
static int cs4341_probe(struct device *dev)
|
||||||
|
{
|
||||||
|
struct cs4341_priv *cs4341 = dev_get_drvdata(dev);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(cs4341_reg_defaults); i++)
|
||||||
|
regmap_write(cs4341->regmap, cs4341_reg_defaults[i].reg,
|
||||||
|
cs4341_reg_defaults[i].def);
|
||||||
|
|
||||||
|
return devm_snd_soc_register_component(dev, &soc_component_cs4341,
|
||||||
|
&cs4341_dai, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(CONFIG_I2C)
|
||||||
|
static int cs4341_i2c_probe(struct i2c_client *i2c,
|
||||||
|
const struct i2c_device_id *id)
|
||||||
|
{
|
||||||
|
struct cs4341_priv *cs4341;
|
||||||
|
|
||||||
|
cs4341 = devm_kzalloc(&i2c->dev, sizeof(*cs4341), GFP_KERNEL);
|
||||||
|
if (!cs4341)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
i2c_set_clientdata(i2c, cs4341);
|
||||||
|
|
||||||
|
cs4341->regcfg.reg_bits = 8;
|
||||||
|
cs4341->regcfg.val_bits = 8;
|
||||||
|
cs4341->regcfg.max_register = CS4341_REG_VOLB;
|
||||||
|
cs4341->regcfg.cache_type = REGCACHE_FLAT;
|
||||||
|
cs4341->regcfg.reg_defaults = cs4341_reg_defaults;
|
||||||
|
cs4341->regcfg.num_reg_defaults = ARRAY_SIZE(cs4341_reg_defaults);
|
||||||
|
cs4341->regmap = devm_regmap_init_i2c(i2c, &cs4341->regcfg);
|
||||||
|
if (IS_ERR(cs4341->regmap))
|
||||||
|
return PTR_ERR(cs4341->regmap);
|
||||||
|
|
||||||
|
return cs4341_probe(&i2c->dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct i2c_device_id cs4341_i2c_id[] = {
|
||||||
|
{ "cs4341", 0 },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(i2c, cs4341_i2c_id);
|
||||||
|
|
||||||
|
static struct i2c_driver cs4341_i2c_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "cs4341-i2c",
|
||||||
|
.of_match_table = of_match_ptr(cs4341_dt_ids),
|
||||||
|
},
|
||||||
|
.probe = cs4341_i2c_probe,
|
||||||
|
.id_table = cs4341_i2c_id,
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(CONFIG_SPI_MASTER)
|
||||||
|
static bool cs4341_reg_readable(struct device *dev, unsigned int reg)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cs4341_spi_probe(struct spi_device *spi)
|
||||||
|
{
|
||||||
|
struct cs4341_priv *cs4341;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
cs4341 = devm_kzalloc(&spi->dev, sizeof(*cs4341), GFP_KERNEL);
|
||||||
|
if (!cs4341)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (!spi->bits_per_word)
|
||||||
|
spi->bits_per_word = 8;
|
||||||
|
if (!spi->max_speed_hz)
|
||||||
|
spi->max_speed_hz = 6000000;
|
||||||
|
ret = spi_setup(spi);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
spi_set_drvdata(spi, cs4341);
|
||||||
|
|
||||||
|
cs4341->regcfg.reg_bits = 16;
|
||||||
|
cs4341->regcfg.val_bits = 8;
|
||||||
|
cs4341->regcfg.write_flag_mask = 0x20;
|
||||||
|
cs4341->regcfg.max_register = CS4341_REG_VOLB;
|
||||||
|
cs4341->regcfg.cache_type = REGCACHE_FLAT;
|
||||||
|
cs4341->regcfg.readable_reg = cs4341_reg_readable;
|
||||||
|
cs4341->regcfg.reg_defaults = cs4341_reg_defaults;
|
||||||
|
cs4341->regcfg.num_reg_defaults = ARRAY_SIZE(cs4341_reg_defaults);
|
||||||
|
cs4341->regmap = devm_regmap_init_spi(spi, &cs4341->regcfg);
|
||||||
|
if (IS_ERR(cs4341->regmap))
|
||||||
|
return PTR_ERR(cs4341->regmap);
|
||||||
|
|
||||||
|
return cs4341_probe(&spi->dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct spi_driver cs4341_spi_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "cs4341-spi",
|
||||||
|
.of_match_table = of_match_ptr(cs4341_dt_ids),
|
||||||
|
},
|
||||||
|
.probe = cs4341_spi_probe,
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int __init cs4341_init(void)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
#if defined(CONFIG_I2C)
|
||||||
|
ret = i2c_add_driver(&cs4341_i2c_driver);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
#endif
|
||||||
|
#if defined(CONFIG_SPI_MASTER)
|
||||||
|
ret = spi_register_driver(&cs4341_spi_driver);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
module_init(cs4341_init);
|
||||||
|
|
||||||
|
static void __exit cs4341_exit(void)
|
||||||
|
{
|
||||||
|
#if defined(CONFIG_I2C)
|
||||||
|
i2c_del_driver(&cs4341_i2c_driver);
|
||||||
|
#endif
|
||||||
|
#if defined(CONFIG_SPI_MASTER)
|
||||||
|
spi_unregister_driver(&cs4341_spi_driver);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
module_exit(cs4341_exit);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
|
||||||
|
MODULE_DESCRIPTION("Cirrus Logic CS4341 ALSA SoC Codec Driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
@ -15,12 +15,14 @@
|
|||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/mod_devicetable.h>
|
#include <linux/mod_devicetable.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
#include <sound/pcm.h>
|
#include <sound/pcm.h>
|
||||||
#include <sound/pcm_params.h>
|
#include <sound/pcm_params.h>
|
||||||
#include <sound/soc.h>
|
#include <sound/soc.h>
|
||||||
#include <sound/soc-dapm.h>
|
#include <sound/soc-dapm.h>
|
||||||
#include <sound/tlv.h>
|
#include <sound/tlv.h>
|
||||||
|
#include <sound/jack.h>
|
||||||
#include "es8316.h"
|
#include "es8316.h"
|
||||||
|
|
||||||
/* In slave mode at single speed, the codec is documented as accepting 5
|
/* In slave mode at single speed, the codec is documented as accepting 5
|
||||||
@ -33,6 +35,11 @@ static const unsigned int supported_mclk_lrck_ratios[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct es8316_priv {
|
struct es8316_priv {
|
||||||
|
struct mutex lock;
|
||||||
|
struct regmap *regmap;
|
||||||
|
struct snd_soc_component *component;
|
||||||
|
struct snd_soc_jack *jack;
|
||||||
|
int irq;
|
||||||
unsigned int sysclk;
|
unsigned int sysclk;
|
||||||
unsigned int allowed_rates[NR_SUPPORTED_MCLK_LRCK_RATIOS];
|
unsigned int allowed_rates[NR_SUPPORTED_MCLK_LRCK_RATIOS];
|
||||||
struct snd_pcm_hw_constraint_list sysclk_constraints;
|
struct snd_pcm_hw_constraint_list sysclk_constraints;
|
||||||
@ -94,6 +101,7 @@ static const struct snd_kcontrol_new es8316_snd_controls[] = {
|
|||||||
SOC_SINGLE("DAC Notch Filter Switch", ES8316_DAC_SET2, 6, 1, 0),
|
SOC_SINGLE("DAC Notch Filter Switch", ES8316_DAC_SET2, 6, 1, 0),
|
||||||
SOC_SINGLE("DAC Double Fs Switch", ES8316_DAC_SET2, 7, 1, 0),
|
SOC_SINGLE("DAC Double Fs Switch", ES8316_DAC_SET2, 7, 1, 0),
|
||||||
SOC_SINGLE("DAC Stereo Enhancement", ES8316_DAC_SET3, 0, 7, 0),
|
SOC_SINGLE("DAC Stereo Enhancement", ES8316_DAC_SET3, 0, 7, 0),
|
||||||
|
SOC_SINGLE("DAC Mono Mix Switch", ES8316_DAC_SET3, 3, 1, 0),
|
||||||
|
|
||||||
SOC_ENUM("Capture Polarity", adcpol),
|
SOC_ENUM("Capture Polarity", adcpol),
|
||||||
SOC_SINGLE("Mic Boost Switch", ES8316_ADC_D2SEPGA, 0, 1, 0),
|
SOC_SINGLE("Mic Boost Switch", ES8316_ADC_D2SEPGA, 0, 1, 0),
|
||||||
@ -529,8 +537,162 @@ static struct snd_soc_dai_driver es8316_dai = {
|
|||||||
.symmetric_rates = 1,
|
.symmetric_rates = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void es8316_enable_micbias_for_mic_gnd_short_detect(
|
||||||
|
struct snd_soc_component *component)
|
||||||
|
{
|
||||||
|
struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
|
||||||
|
|
||||||
|
snd_soc_dapm_mutex_lock(dapm);
|
||||||
|
snd_soc_dapm_force_enable_pin_unlocked(dapm, "Bias");
|
||||||
|
snd_soc_dapm_force_enable_pin_unlocked(dapm, "Analog power");
|
||||||
|
snd_soc_dapm_force_enable_pin_unlocked(dapm, "Mic Bias");
|
||||||
|
snd_soc_dapm_sync_unlocked(dapm);
|
||||||
|
snd_soc_dapm_mutex_unlock(dapm);
|
||||||
|
|
||||||
|
msleep(20);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void es8316_disable_micbias_for_mic_gnd_short_detect(
|
||||||
|
struct snd_soc_component *component)
|
||||||
|
{
|
||||||
|
struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
|
||||||
|
|
||||||
|
snd_soc_dapm_mutex_lock(dapm);
|
||||||
|
snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Bias");
|
||||||
|
snd_soc_dapm_disable_pin_unlocked(dapm, "Analog power");
|
||||||
|
snd_soc_dapm_disable_pin_unlocked(dapm, "Bias");
|
||||||
|
snd_soc_dapm_sync_unlocked(dapm);
|
||||||
|
snd_soc_dapm_mutex_unlock(dapm);
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t es8316_irq(int irq, void *data)
|
||||||
|
{
|
||||||
|
struct es8316_priv *es8316 = data;
|
||||||
|
struct snd_soc_component *comp = es8316->component;
|
||||||
|
unsigned int flags;
|
||||||
|
|
||||||
|
mutex_lock(&es8316->lock);
|
||||||
|
|
||||||
|
regmap_read(es8316->regmap, ES8316_GPIO_FLAG, &flags);
|
||||||
|
if (flags == 0x00)
|
||||||
|
goto out; /* Powered-down / reset */
|
||||||
|
|
||||||
|
/* Catch spurious IRQ before set_jack is called */
|
||||||
|
if (!es8316->jack)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
dev_dbg(comp->dev, "gpio flags %#04x\n", flags);
|
||||||
|
if (flags & ES8316_GPIO_FLAG_HP_NOT_INSERTED) {
|
||||||
|
/* Jack removed, or spurious IRQ? */
|
||||||
|
if (es8316->jack->status & SND_JACK_MICROPHONE)
|
||||||
|
es8316_disable_micbias_for_mic_gnd_short_detect(comp);
|
||||||
|
|
||||||
|
if (es8316->jack->status & SND_JACK_HEADPHONE) {
|
||||||
|
snd_soc_jack_report(es8316->jack, 0,
|
||||||
|
SND_JACK_HEADSET | SND_JACK_BTN_0);
|
||||||
|
dev_dbg(comp->dev, "jack unplugged\n");
|
||||||
|
}
|
||||||
|
} else if (!(es8316->jack->status & SND_JACK_HEADPHONE)) {
|
||||||
|
/* Jack inserted, determine type */
|
||||||
|
es8316_enable_micbias_for_mic_gnd_short_detect(comp);
|
||||||
|
regmap_read(es8316->regmap, ES8316_GPIO_FLAG, &flags);
|
||||||
|
dev_dbg(comp->dev, "gpio flags %#04x\n", flags);
|
||||||
|
if (flags & ES8316_GPIO_FLAG_HP_NOT_INSERTED) {
|
||||||
|
/* Jack unplugged underneath us */
|
||||||
|
es8316_disable_micbias_for_mic_gnd_short_detect(comp);
|
||||||
|
} else if (flags & ES8316_GPIO_FLAG_GM_NOT_SHORTED) {
|
||||||
|
/* Open, headset */
|
||||||
|
snd_soc_jack_report(es8316->jack,
|
||||||
|
SND_JACK_HEADSET,
|
||||||
|
SND_JACK_HEADSET);
|
||||||
|
/* Keep mic-gnd-short detection on for button press */
|
||||||
|
} else {
|
||||||
|
/* Shorted, headphones */
|
||||||
|
snd_soc_jack_report(es8316->jack,
|
||||||
|
SND_JACK_HEADPHONE,
|
||||||
|
SND_JACK_HEADSET);
|
||||||
|
/* No longer need mic-gnd-short detection */
|
||||||
|
es8316_disable_micbias_for_mic_gnd_short_detect(comp);
|
||||||
|
}
|
||||||
|
} else if (es8316->jack->status & SND_JACK_MICROPHONE) {
|
||||||
|
/* Interrupt while jack inserted, report button state */
|
||||||
|
if (flags & ES8316_GPIO_FLAG_GM_NOT_SHORTED) {
|
||||||
|
/* Open, button release */
|
||||||
|
snd_soc_jack_report(es8316->jack, 0, SND_JACK_BTN_0);
|
||||||
|
} else {
|
||||||
|
/* Short, button press */
|
||||||
|
snd_soc_jack_report(es8316->jack,
|
||||||
|
SND_JACK_BTN_0,
|
||||||
|
SND_JACK_BTN_0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
mutex_unlock(&es8316->lock);
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void es8316_enable_jack_detect(struct snd_soc_component *component,
|
||||||
|
struct snd_soc_jack *jack)
|
||||||
|
{
|
||||||
|
struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
|
||||||
|
|
||||||
|
mutex_lock(&es8316->lock);
|
||||||
|
|
||||||
|
es8316->jack = jack;
|
||||||
|
|
||||||
|
if (es8316->jack->status & SND_JACK_MICROPHONE)
|
||||||
|
es8316_enable_micbias_for_mic_gnd_short_detect(component);
|
||||||
|
|
||||||
|
snd_soc_component_update_bits(component, ES8316_GPIO_DEBOUNCE,
|
||||||
|
ES8316_GPIO_ENABLE_INTERRUPT,
|
||||||
|
ES8316_GPIO_ENABLE_INTERRUPT);
|
||||||
|
|
||||||
|
mutex_unlock(&es8316->lock);
|
||||||
|
|
||||||
|
/* Enable irq and sync initial jack state */
|
||||||
|
enable_irq(es8316->irq);
|
||||||
|
es8316_irq(es8316->irq, es8316);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void es8316_disable_jack_detect(struct snd_soc_component *component)
|
||||||
|
{
|
||||||
|
struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
|
||||||
|
|
||||||
|
disable_irq(es8316->irq);
|
||||||
|
|
||||||
|
mutex_lock(&es8316->lock);
|
||||||
|
|
||||||
|
snd_soc_component_update_bits(component, ES8316_GPIO_DEBOUNCE,
|
||||||
|
ES8316_GPIO_ENABLE_INTERRUPT, 0);
|
||||||
|
|
||||||
|
if (es8316->jack->status & SND_JACK_MICROPHONE) {
|
||||||
|
es8316_disable_micbias_for_mic_gnd_short_detect(component);
|
||||||
|
snd_soc_jack_report(es8316->jack, 0, SND_JACK_BTN_0);
|
||||||
|
}
|
||||||
|
|
||||||
|
es8316->jack = NULL;
|
||||||
|
|
||||||
|
mutex_unlock(&es8316->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int es8316_set_jack(struct snd_soc_component *component,
|
||||||
|
struct snd_soc_jack *jack, void *data)
|
||||||
|
{
|
||||||
|
if (jack)
|
||||||
|
es8316_enable_jack_detect(component, jack);
|
||||||
|
else
|
||||||
|
es8316_disable_jack_detect(component);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int es8316_probe(struct snd_soc_component *component)
|
static int es8316_probe(struct snd_soc_component *component)
|
||||||
{
|
{
|
||||||
|
struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
|
||||||
|
|
||||||
|
es8316->component = component;
|
||||||
|
|
||||||
/* Reset codec and enable current state machine */
|
/* Reset codec and enable current state machine */
|
||||||
snd_soc_component_write(component, ES8316_RESET, 0x3f);
|
snd_soc_component_write(component, ES8316_RESET, 0x3f);
|
||||||
usleep_range(5000, 5500);
|
usleep_range(5000, 5500);
|
||||||
@ -555,6 +717,7 @@ static int es8316_probe(struct snd_soc_component *component)
|
|||||||
|
|
||||||
static const struct snd_soc_component_driver soc_component_dev_es8316 = {
|
static const struct snd_soc_component_driver soc_component_dev_es8316 = {
|
||||||
.probe = es8316_probe,
|
.probe = es8316_probe,
|
||||||
|
.set_jack = es8316_set_jack,
|
||||||
.controls = es8316_snd_controls,
|
.controls = es8316_snd_controls,
|
||||||
.num_controls = ARRAY_SIZE(es8316_snd_controls),
|
.num_controls = ARRAY_SIZE(es8316_snd_controls),
|
||||||
.dapm_widgets = es8316_dapm_widgets,
|
.dapm_widgets = es8316_dapm_widgets,
|
||||||
@ -566,18 +729,29 @@ static const struct snd_soc_component_driver soc_component_dev_es8316 = {
|
|||||||
.non_legacy_dai_naming = 1,
|
.non_legacy_dai_naming = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct regmap_range es8316_volatile_ranges[] = {
|
||||||
|
regmap_reg_range(ES8316_GPIO_FLAG, ES8316_GPIO_FLAG),
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct regmap_access_table es8316_volatile_table = {
|
||||||
|
.yes_ranges = es8316_volatile_ranges,
|
||||||
|
.n_yes_ranges = ARRAY_SIZE(es8316_volatile_ranges),
|
||||||
|
};
|
||||||
|
|
||||||
static const struct regmap_config es8316_regmap = {
|
static const struct regmap_config es8316_regmap = {
|
||||||
.reg_bits = 8,
|
.reg_bits = 8,
|
||||||
.val_bits = 8,
|
.val_bits = 8,
|
||||||
.max_register = 0x53,
|
.max_register = 0x53,
|
||||||
|
.volatile_table = &es8316_volatile_table,
|
||||||
.cache_type = REGCACHE_RBTREE,
|
.cache_type = REGCACHE_RBTREE,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int es8316_i2c_probe(struct i2c_client *i2c_client,
|
static int es8316_i2c_probe(struct i2c_client *i2c_client,
|
||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
|
struct device *dev = &i2c_client->dev;
|
||||||
struct es8316_priv *es8316;
|
struct es8316_priv *es8316;
|
||||||
struct regmap *regmap;
|
int ret;
|
||||||
|
|
||||||
es8316 = devm_kzalloc(&i2c_client->dev, sizeof(struct es8316_priv),
|
es8316 = devm_kzalloc(&i2c_client->dev, sizeof(struct es8316_priv),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
@ -586,9 +760,23 @@ static int es8316_i2c_probe(struct i2c_client *i2c_client,
|
|||||||
|
|
||||||
i2c_set_clientdata(i2c_client, es8316);
|
i2c_set_clientdata(i2c_client, es8316);
|
||||||
|
|
||||||
regmap = devm_regmap_init_i2c(i2c_client, &es8316_regmap);
|
es8316->regmap = devm_regmap_init_i2c(i2c_client, &es8316_regmap);
|
||||||
if (IS_ERR(regmap))
|
if (IS_ERR(es8316->regmap))
|
||||||
return PTR_ERR(regmap);
|
return PTR_ERR(es8316->regmap);
|
||||||
|
|
||||||
|
es8316->irq = i2c_client->irq;
|
||||||
|
mutex_init(&es8316->lock);
|
||||||
|
|
||||||
|
ret = devm_request_threaded_irq(dev, es8316->irq, NULL, es8316_irq,
|
||||||
|
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
|
||||||
|
"es8316", es8316);
|
||||||
|
if (ret == 0) {
|
||||||
|
/* Gets re-enabled by es8316_set_jack() */
|
||||||
|
disable_irq(es8316->irq);
|
||||||
|
} else {
|
||||||
|
dev_warn(dev, "Failed to get IRQ %d: %d\n", es8316->irq, ret);
|
||||||
|
es8316->irq = -ENXIO;
|
||||||
|
}
|
||||||
|
|
||||||
return devm_snd_soc_register_component(&i2c_client->dev,
|
return devm_snd_soc_register_component(&i2c_client->dev,
|
||||||
&soc_component_dev_es8316,
|
&soc_component_dev_es8316,
|
||||||
|
@ -126,4 +126,11 @@
|
|||||||
#define ES8316_SERDATA2_LEN_16 0x0c
|
#define ES8316_SERDATA2_LEN_16 0x0c
|
||||||
#define ES8316_SERDATA2_LEN_32 0x10
|
#define ES8316_SERDATA2_LEN_32 0x10
|
||||||
|
|
||||||
|
/* ES8316_GPIO_DEBOUNCE */
|
||||||
|
#define ES8316_GPIO_ENABLE_INTERRUPT 0x02
|
||||||
|
|
||||||
|
/* ES8316_GPIO_FLAG */
|
||||||
|
#define ES8316_GPIO_FLAG_GM_NOT_SHORTED 0x02
|
||||||
|
#define ES8316_GPIO_FLAG_HP_NOT_INSERTED 0x04
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1400,24 +1400,20 @@ static int pcm512x_digital_mute(struct snd_soc_dai *dai, int mute)
|
|||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
dev_err(component->dev,
|
dev_err(component->dev,
|
||||||
"Failed to set digital mute: %d\n", ret);
|
"Failed to set digital mute: %d\n", ret);
|
||||||
mutex_unlock(&pcm512x->mutex);
|
goto unlock;
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
regmap_read_poll_timeout(pcm512x->regmap,
|
regmap_read_poll_timeout(pcm512x->regmap,
|
||||||
PCM512x_ANALOG_MUTE_DET,
|
PCM512x_ANALOG_MUTE_DET,
|
||||||
mute_det, (mute_det & 0x3) == 0,
|
mute_det, (mute_det & 0x3) == 0,
|
||||||
200, 10000);
|
200, 10000);
|
||||||
|
|
||||||
mutex_unlock(&pcm512x->mutex);
|
|
||||||
} else {
|
} else {
|
||||||
pcm512x->mute &= ~0x1;
|
pcm512x->mute &= ~0x1;
|
||||||
ret = pcm512x_update_mute(pcm512x);
|
ret = pcm512x_update_mute(pcm512x);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
dev_err(component->dev,
|
dev_err(component->dev,
|
||||||
"Failed to update digital mute: %d\n", ret);
|
"Failed to update digital mute: %d\n", ret);
|
||||||
mutex_unlock(&pcm512x->mutex);
|
goto unlock;
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
regmap_read_poll_timeout(pcm512x->regmap,
|
regmap_read_poll_timeout(pcm512x->regmap,
|
||||||
@ -1428,9 +1424,10 @@ static int pcm512x_digital_mute(struct snd_soc_dai *dai, int mute)
|
|||||||
200, 10000);
|
200, 10000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unlock:
|
||||||
mutex_unlock(&pcm512x->mutex);
|
mutex_unlock(&pcm512x->mutex);
|
||||||
|
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct snd_soc_dai_ops pcm512x_dai_ops = {
|
static const struct snd_soc_dai_ops pcm512x_dai_ops = {
|
||||||
|
519
sound/soc/codecs/rk3328_codec.c
Normal file
519
sound/soc/codecs/rk3328_codec.c
Normal file
@ -0,0 +1,519 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
//
|
||||||
|
// rk3328 ALSA SoC Audio driver
|
||||||
|
//
|
||||||
|
// Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd All rights reserved.
|
||||||
|
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/mfd/syscon.h>
|
||||||
|
#include <sound/dmaengine_pcm.h>
|
||||||
|
#include <sound/pcm_params.h>
|
||||||
|
#include "rk3328_codec.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* volume setting
|
||||||
|
* 0: -39dB
|
||||||
|
* 26: 0dB
|
||||||
|
* 31: 6dB
|
||||||
|
* Step: 1.5dB
|
||||||
|
*/
|
||||||
|
#define OUT_VOLUME (0x18)
|
||||||
|
#define RK3328_GRF_SOC_CON2 (0x0408)
|
||||||
|
#define RK3328_GRF_SOC_CON10 (0x0428)
|
||||||
|
#define INITIAL_FREQ (11289600)
|
||||||
|
|
||||||
|
struct rk3328_codec_priv {
|
||||||
|
struct regmap *regmap;
|
||||||
|
struct regmap *grf;
|
||||||
|
struct clk *mclk;
|
||||||
|
struct clk *pclk;
|
||||||
|
unsigned int sclk;
|
||||||
|
int spk_depop_time; /* msec */
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct reg_default rk3328_codec_reg_defaults[] = {
|
||||||
|
{ CODEC_RESET, 0x03 },
|
||||||
|
{ DAC_INIT_CTRL1, 0x00 },
|
||||||
|
{ DAC_INIT_CTRL2, 0x50 },
|
||||||
|
{ DAC_INIT_CTRL3, 0x0e },
|
||||||
|
{ DAC_PRECHARGE_CTRL, 0x01 },
|
||||||
|
{ DAC_PWR_CTRL, 0x00 },
|
||||||
|
{ DAC_CLK_CTRL, 0x00 },
|
||||||
|
{ HPMIX_CTRL, 0x00 },
|
||||||
|
{ HPOUT_CTRL, 0x00 },
|
||||||
|
{ HPOUTL_GAIN_CTRL, 0x00 },
|
||||||
|
{ HPOUTR_GAIN_CTRL, 0x00 },
|
||||||
|
{ HPOUT_POP_CTRL, 0x11 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static int rk3328_codec_reset(struct rk3328_codec_priv *rk3328)
|
||||||
|
{
|
||||||
|
regmap_write(rk3328->regmap, CODEC_RESET, 0x00);
|
||||||
|
mdelay(10);
|
||||||
|
regmap_write(rk3328->regmap, CODEC_RESET, 0x03);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rk3328_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||||
|
{
|
||||||
|
struct rk3328_codec_priv *rk3328 =
|
||||||
|
snd_soc_component_get_drvdata(dai->component);
|
||||||
|
unsigned int val;
|
||||||
|
|
||||||
|
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||||
|
case SND_SOC_DAIFMT_CBS_CFS:
|
||||||
|
val = PIN_DIRECTION_IN | DAC_I2S_MODE_SLAVE;
|
||||||
|
break;
|
||||||
|
case SND_SOC_DAIFMT_CBM_CFM:
|
||||||
|
val = PIN_DIRECTION_OUT | DAC_I2S_MODE_MASTER;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
regmap_update_bits(rk3328->regmap, DAC_INIT_CTRL1,
|
||||||
|
PIN_DIRECTION_MASK | DAC_I2S_MODE_MASK, val);
|
||||||
|
|
||||||
|
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||||
|
case SND_SOC_DAIFMT_DSP_A:
|
||||||
|
case SND_SOC_DAIFMT_DSP_B:
|
||||||
|
val = DAC_MODE_PCM;
|
||||||
|
break;
|
||||||
|
case SND_SOC_DAIFMT_I2S:
|
||||||
|
val = DAC_MODE_I2S;
|
||||||
|
break;
|
||||||
|
case SND_SOC_DAIFMT_RIGHT_J:
|
||||||
|
val = DAC_MODE_RJM;
|
||||||
|
break;
|
||||||
|
case SND_SOC_DAIFMT_LEFT_J:
|
||||||
|
val = DAC_MODE_LJM;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
regmap_update_bits(rk3328->regmap, DAC_INIT_CTRL2,
|
||||||
|
DAC_MODE_MASK, val);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rk3328_analog_output(struct rk3328_codec_priv *rk3328, int mute)
|
||||||
|
{
|
||||||
|
unsigned int val = BIT(17);
|
||||||
|
|
||||||
|
if (mute)
|
||||||
|
val |= BIT(1);
|
||||||
|
|
||||||
|
regmap_write(rk3328->grf, RK3328_GRF_SOC_CON10, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rk3328_digital_mute(struct snd_soc_dai *dai, int mute)
|
||||||
|
{
|
||||||
|
struct rk3328_codec_priv *rk3328 =
|
||||||
|
snd_soc_component_get_drvdata(dai->component);
|
||||||
|
unsigned int val;
|
||||||
|
|
||||||
|
if (mute)
|
||||||
|
val = HPOUTL_MUTE | HPOUTR_MUTE;
|
||||||
|
else
|
||||||
|
val = HPOUTL_UNMUTE | HPOUTR_UNMUTE;
|
||||||
|
|
||||||
|
regmap_update_bits(rk3328->regmap, HPOUT_CTRL,
|
||||||
|
HPOUTL_MUTE_MASK | HPOUTR_MUTE_MASK, val);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rk3328_codec_power_on(struct rk3328_codec_priv *rk3328, int wait_ms)
|
||||||
|
{
|
||||||
|
regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
|
||||||
|
DAC_CHARGE_XCHARGE_MASK, DAC_CHARGE_PRECHARGE);
|
||||||
|
mdelay(10);
|
||||||
|
regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
|
||||||
|
DAC_CHARGE_CURRENT_ALL_MASK,
|
||||||
|
DAC_CHARGE_CURRENT_ALL_ON);
|
||||||
|
mdelay(wait_ms);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rk3328_codec_power_off(struct rk3328_codec_priv *rk3328, int wait_ms)
|
||||||
|
{
|
||||||
|
regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
|
||||||
|
DAC_CHARGE_XCHARGE_MASK, DAC_CHARGE_DISCHARGE);
|
||||||
|
mdelay(10);
|
||||||
|
regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
|
||||||
|
DAC_CHARGE_CURRENT_ALL_MASK,
|
||||||
|
DAC_CHARGE_CURRENT_ALL_ON);
|
||||||
|
mdelay(wait_ms);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct rk3328_reg_msk_val playback_open_list[] = {
|
||||||
|
{ DAC_PWR_CTRL, DAC_PWR_MASK, DAC_PWR_ON },
|
||||||
|
{ DAC_PWR_CTRL, DACL_PATH_REFV_MASK | DACR_PATH_REFV_MASK,
|
||||||
|
DACL_PATH_REFV_ON | DACR_PATH_REFV_ON },
|
||||||
|
{ DAC_PWR_CTRL, HPOUTL_ZERO_CROSSING_MASK | HPOUTR_ZERO_CROSSING_MASK,
|
||||||
|
HPOUTL_ZERO_CROSSING_ON | HPOUTR_ZERO_CROSSING_ON },
|
||||||
|
{ HPOUT_POP_CTRL, HPOUTR_POP_MASK | HPOUTL_POP_MASK,
|
||||||
|
HPOUTR_POP_WORK | HPOUTL_POP_WORK },
|
||||||
|
{ HPMIX_CTRL, HPMIXL_MASK | HPMIXR_MASK, HPMIXL_EN | HPMIXR_EN },
|
||||||
|
{ HPMIX_CTRL, HPMIXL_INIT_MASK | HPMIXR_INIT_MASK,
|
||||||
|
HPMIXL_INIT_EN | HPMIXR_INIT_EN },
|
||||||
|
{ HPOUT_CTRL, HPOUTL_MASK | HPOUTR_MASK, HPOUTL_EN | HPOUTR_EN },
|
||||||
|
{ HPOUT_CTRL, HPOUTL_INIT_MASK | HPOUTR_INIT_MASK,
|
||||||
|
HPOUTL_INIT_EN | HPOUTR_INIT_EN },
|
||||||
|
{ DAC_CLK_CTRL, DACL_REFV_MASK | DACR_REFV_MASK,
|
||||||
|
DACL_REFV_ON | DACR_REFV_ON },
|
||||||
|
{ DAC_CLK_CTRL, DACL_CLK_MASK | DACR_CLK_MASK,
|
||||||
|
DACL_CLK_ON | DACR_CLK_ON },
|
||||||
|
{ DAC_CLK_CTRL, DACL_MASK | DACR_MASK, DACL_ON | DACR_ON },
|
||||||
|
{ DAC_CLK_CTRL, DACL_INIT_MASK | DACR_INIT_MASK,
|
||||||
|
DACL_INIT_ON | DACR_INIT_ON },
|
||||||
|
{ DAC_SELECT, DACL_SELECT_MASK | DACR_SELECT_MASK,
|
||||||
|
DACL_SELECT | DACR_SELECT },
|
||||||
|
{ HPMIX_CTRL, HPMIXL_INIT2_MASK | HPMIXR_INIT2_MASK,
|
||||||
|
HPMIXL_INIT2_EN | HPMIXR_INIT2_EN },
|
||||||
|
{ HPOUT_CTRL, HPOUTL_MUTE_MASK | HPOUTR_MUTE_MASK,
|
||||||
|
HPOUTL_UNMUTE | HPOUTR_UNMUTE },
|
||||||
|
};
|
||||||
|
|
||||||
|
static int rk3328_codec_open_playback(struct rk3328_codec_priv *rk3328)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
|
||||||
|
DAC_CHARGE_CURRENT_ALL_MASK,
|
||||||
|
DAC_CHARGE_CURRENT_I);
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(playback_open_list); i++) {
|
||||||
|
regmap_update_bits(rk3328->regmap,
|
||||||
|
playback_open_list[i].reg,
|
||||||
|
playback_open_list[i].msk,
|
||||||
|
playback_open_list[i].val);
|
||||||
|
mdelay(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
msleep(rk3328->spk_depop_time);
|
||||||
|
rk3328_analog_output(rk3328, 1);
|
||||||
|
|
||||||
|
regmap_update_bits(rk3328->regmap, HPOUTL_GAIN_CTRL,
|
||||||
|
HPOUTL_GAIN_MASK, OUT_VOLUME);
|
||||||
|
regmap_update_bits(rk3328->regmap, HPOUTR_GAIN_CTRL,
|
||||||
|
HPOUTR_GAIN_MASK, OUT_VOLUME);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct rk3328_reg_msk_val playback_close_list[] = {
|
||||||
|
{ HPMIX_CTRL, HPMIXL_INIT2_MASK | HPMIXR_INIT2_MASK,
|
||||||
|
HPMIXL_INIT2_DIS | HPMIXR_INIT2_DIS },
|
||||||
|
{ DAC_SELECT, DACL_SELECT_MASK | DACR_SELECT_MASK,
|
||||||
|
DACL_UNSELECT | DACR_UNSELECT },
|
||||||
|
{ HPOUT_CTRL, HPOUTL_MUTE_MASK | HPOUTR_MUTE_MASK,
|
||||||
|
HPOUTL_MUTE | HPOUTR_MUTE },
|
||||||
|
{ HPOUT_CTRL, HPOUTL_INIT_MASK | HPOUTR_INIT_MASK,
|
||||||
|
HPOUTL_INIT_DIS | HPOUTR_INIT_DIS },
|
||||||
|
{ HPOUT_CTRL, HPOUTL_MASK | HPOUTR_MASK, HPOUTL_DIS | HPOUTR_DIS },
|
||||||
|
{ HPMIX_CTRL, HPMIXL_MASK | HPMIXR_MASK, HPMIXL_DIS | HPMIXR_DIS },
|
||||||
|
{ DAC_CLK_CTRL, DACL_MASK | DACR_MASK, DACL_OFF | DACR_OFF },
|
||||||
|
{ DAC_CLK_CTRL, DACL_CLK_MASK | DACR_CLK_MASK,
|
||||||
|
DACL_CLK_OFF | DACR_CLK_OFF },
|
||||||
|
{ DAC_CLK_CTRL, DACL_REFV_MASK | DACR_REFV_MASK,
|
||||||
|
DACL_REFV_OFF | DACR_REFV_OFF },
|
||||||
|
{ HPOUT_POP_CTRL, HPOUTR_POP_MASK | HPOUTL_POP_MASK,
|
||||||
|
HPOUTR_POP_XCHARGE | HPOUTL_POP_XCHARGE },
|
||||||
|
{ DAC_PWR_CTRL, DACL_PATH_REFV_MASK | DACR_PATH_REFV_MASK,
|
||||||
|
DACL_PATH_REFV_OFF | DACR_PATH_REFV_OFF },
|
||||||
|
{ DAC_PWR_CTRL, DAC_PWR_MASK, DAC_PWR_OFF },
|
||||||
|
{ HPMIX_CTRL, HPMIXL_INIT_MASK | HPMIXR_INIT_MASK,
|
||||||
|
HPMIXL_INIT_DIS | HPMIXR_INIT_DIS },
|
||||||
|
{ DAC_CLK_CTRL, DACL_INIT_MASK | DACR_INIT_MASK,
|
||||||
|
DACL_INIT_OFF | DACR_INIT_OFF },
|
||||||
|
};
|
||||||
|
|
||||||
|
static int rk3328_codec_close_playback(struct rk3328_codec_priv *rk3328)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
rk3328_analog_output(rk3328, 0);
|
||||||
|
|
||||||
|
regmap_update_bits(rk3328->regmap, HPOUTL_GAIN_CTRL,
|
||||||
|
HPOUTL_GAIN_MASK, 0);
|
||||||
|
regmap_update_bits(rk3328->regmap, HPOUTR_GAIN_CTRL,
|
||||||
|
HPOUTR_GAIN_MASK, 0);
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(playback_close_list); i++) {
|
||||||
|
regmap_update_bits(rk3328->regmap,
|
||||||
|
playback_close_list[i].reg,
|
||||||
|
playback_close_list[i].msk,
|
||||||
|
playback_close_list[i].val);
|
||||||
|
mdelay(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Workaround for silence when changed Fs 48 -> 44.1kHz */
|
||||||
|
rk3328_codec_reset(rk3328);
|
||||||
|
|
||||||
|
regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
|
||||||
|
DAC_CHARGE_CURRENT_ALL_MASK,
|
||||||
|
DAC_CHARGE_CURRENT_ALL_ON);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rk3328_hw_params(struct snd_pcm_substream *substream,
|
||||||
|
struct snd_pcm_hw_params *params,
|
||||||
|
struct snd_soc_dai *dai)
|
||||||
|
{
|
||||||
|
struct rk3328_codec_priv *rk3328 =
|
||||||
|
snd_soc_component_get_drvdata(dai->component);
|
||||||
|
unsigned int val = 0;
|
||||||
|
|
||||||
|
switch (params_format(params)) {
|
||||||
|
case SNDRV_PCM_FORMAT_S16_LE:
|
||||||
|
val = DAC_VDL_16BITS;
|
||||||
|
break;
|
||||||
|
case SNDRV_PCM_FORMAT_S20_3LE:
|
||||||
|
val = DAC_VDL_20BITS;
|
||||||
|
break;
|
||||||
|
case SNDRV_PCM_FORMAT_S24_LE:
|
||||||
|
val = DAC_VDL_24BITS;
|
||||||
|
break;
|
||||||
|
case SNDRV_PCM_FORMAT_S32_LE:
|
||||||
|
val = DAC_VDL_32BITS;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
regmap_update_bits(rk3328->regmap, DAC_INIT_CTRL2, DAC_VDL_MASK, val);
|
||||||
|
|
||||||
|
val = DAC_WL_32BITS | DAC_RST_DIS;
|
||||||
|
regmap_update_bits(rk3328->regmap, DAC_INIT_CTRL3,
|
||||||
|
DAC_WL_MASK | DAC_RST_MASK, val);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rk3328_pcm_startup(struct snd_pcm_substream *substream,
|
||||||
|
struct snd_soc_dai *dai)
|
||||||
|
{
|
||||||
|
struct rk3328_codec_priv *rk3328 =
|
||||||
|
snd_soc_component_get_drvdata(dai->component);
|
||||||
|
|
||||||
|
return rk3328_codec_open_playback(rk3328);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rk3328_pcm_shutdown(struct snd_pcm_substream *substream,
|
||||||
|
struct snd_soc_dai *dai)
|
||||||
|
{
|
||||||
|
struct rk3328_codec_priv *rk3328 =
|
||||||
|
snd_soc_component_get_drvdata(dai->component);
|
||||||
|
|
||||||
|
rk3328_codec_close_playback(rk3328);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct snd_soc_dai_ops rk3328_dai_ops = {
|
||||||
|
.hw_params = rk3328_hw_params,
|
||||||
|
.set_fmt = rk3328_set_dai_fmt,
|
||||||
|
.digital_mute = rk3328_digital_mute,
|
||||||
|
.startup = rk3328_pcm_startup,
|
||||||
|
.shutdown = rk3328_pcm_shutdown,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct snd_soc_dai_driver rk3328_dai[] = {
|
||||||
|
{
|
||||||
|
.name = "rk3328-hifi",
|
||||||
|
.id = RK3328_HIFI,
|
||||||
|
.playback = {
|
||||||
|
.stream_name = "HIFI Playback",
|
||||||
|
.channels_min = 1,
|
||||||
|
.channels_max = 2,
|
||||||
|
.rates = SNDRV_PCM_RATE_8000_96000,
|
||||||
|
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
|
||||||
|
SNDRV_PCM_FMTBIT_S20_3LE |
|
||||||
|
SNDRV_PCM_FMTBIT_S24_LE |
|
||||||
|
SNDRV_PCM_FMTBIT_S32_LE),
|
||||||
|
},
|
||||||
|
.capture = {
|
||||||
|
.stream_name = "HIFI Capture",
|
||||||
|
.channels_min = 2,
|
||||||
|
.channels_max = 8,
|
||||||
|
.rates = SNDRV_PCM_RATE_8000_96000,
|
||||||
|
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
|
||||||
|
SNDRV_PCM_FMTBIT_S20_3LE |
|
||||||
|
SNDRV_PCM_FMTBIT_S24_LE |
|
||||||
|
SNDRV_PCM_FMTBIT_S32_LE),
|
||||||
|
},
|
||||||
|
.ops = &rk3328_dai_ops,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int rk3328_codec_probe(struct snd_soc_component *component)
|
||||||
|
{
|
||||||
|
struct rk3328_codec_priv *rk3328 =
|
||||||
|
snd_soc_component_get_drvdata(component);
|
||||||
|
|
||||||
|
rk3328_codec_reset(rk3328);
|
||||||
|
rk3328_codec_power_on(rk3328, 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rk3328_codec_remove(struct snd_soc_component *component)
|
||||||
|
{
|
||||||
|
struct rk3328_codec_priv *rk3328 =
|
||||||
|
snd_soc_component_get_drvdata(component);
|
||||||
|
|
||||||
|
rk3328_codec_close_playback(rk3328);
|
||||||
|
rk3328_codec_power_off(rk3328, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct snd_soc_component_driver soc_codec_rk3328 = {
|
||||||
|
.probe = rk3328_codec_probe,
|
||||||
|
.remove = rk3328_codec_remove,
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool rk3328_codec_write_read_reg(struct device *dev, unsigned int reg)
|
||||||
|
{
|
||||||
|
switch (reg) {
|
||||||
|
case CODEC_RESET:
|
||||||
|
case DAC_INIT_CTRL1:
|
||||||
|
case DAC_INIT_CTRL2:
|
||||||
|
case DAC_INIT_CTRL3:
|
||||||
|
case DAC_PRECHARGE_CTRL:
|
||||||
|
case DAC_PWR_CTRL:
|
||||||
|
case DAC_CLK_CTRL:
|
||||||
|
case HPMIX_CTRL:
|
||||||
|
case DAC_SELECT:
|
||||||
|
case HPOUT_CTRL:
|
||||||
|
case HPOUTL_GAIN_CTRL:
|
||||||
|
case HPOUTR_GAIN_CTRL:
|
||||||
|
case HPOUT_POP_CTRL:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool rk3328_codec_volatile_reg(struct device *dev, unsigned int reg)
|
||||||
|
{
|
||||||
|
switch (reg) {
|
||||||
|
case CODEC_RESET:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct regmap_config rk3328_codec_regmap_config = {
|
||||||
|
.reg_bits = 32,
|
||||||
|
.reg_stride = 4,
|
||||||
|
.val_bits = 32,
|
||||||
|
.max_register = HPOUT_POP_CTRL,
|
||||||
|
.writeable_reg = rk3328_codec_write_read_reg,
|
||||||
|
.readable_reg = rk3328_codec_write_read_reg,
|
||||||
|
.volatile_reg = rk3328_codec_volatile_reg,
|
||||||
|
.reg_defaults = rk3328_codec_reg_defaults,
|
||||||
|
.num_reg_defaults = ARRAY_SIZE(rk3328_codec_reg_defaults),
|
||||||
|
.cache_type = REGCACHE_FLAT,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int rk3328_platform_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct device_node *rk3328_np = pdev->dev.of_node;
|
||||||
|
struct rk3328_codec_priv *rk3328;
|
||||||
|
struct resource *res;
|
||||||
|
struct regmap *grf;
|
||||||
|
void __iomem *base;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
rk3328 = devm_kzalloc(&pdev->dev, sizeof(*rk3328), GFP_KERNEL);
|
||||||
|
if (!rk3328)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
grf = syscon_regmap_lookup_by_phandle(rk3328_np,
|
||||||
|
"rockchip,grf");
|
||||||
|
if (IS_ERR(grf)) {
|
||||||
|
dev_err(&pdev->dev, "missing 'rockchip,grf'\n");
|
||||||
|
return PTR_ERR(grf);
|
||||||
|
}
|
||||||
|
rk3328->grf = grf;
|
||||||
|
/* enable i2s_acodec_en */
|
||||||
|
regmap_write(grf, RK3328_GRF_SOC_CON2,
|
||||||
|
(BIT(14) << 16 | BIT(14)));
|
||||||
|
|
||||||
|
ret = of_property_read_u32(rk3328_np, "spk-depop-time-ms",
|
||||||
|
&rk3328->spk_depop_time);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_info(&pdev->dev, "spk_depop_time use default value.\n");
|
||||||
|
rk3328->spk_depop_time = 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
rk3328_analog_output(rk3328, 0);
|
||||||
|
|
||||||
|
rk3328->mclk = devm_clk_get(&pdev->dev, "mclk");
|
||||||
|
if (IS_ERR(rk3328->mclk))
|
||||||
|
return PTR_ERR(rk3328->mclk);
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(rk3328->mclk);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
clk_set_rate(rk3328->mclk, INITIAL_FREQ);
|
||||||
|
|
||||||
|
rk3328->pclk = devm_clk_get(&pdev->dev, "pclk");
|
||||||
|
if (IS_ERR(rk3328->pclk)) {
|
||||||
|
dev_err(&pdev->dev, "can't get acodec pclk\n");
|
||||||
|
return PTR_ERR(rk3328->pclk);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(rk3328->pclk);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&pdev->dev, "failed to enable acodec pclk\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
base = devm_ioremap_resource(&pdev->dev, res);
|
||||||
|
if (IS_ERR(base))
|
||||||
|
return PTR_ERR(base);
|
||||||
|
|
||||||
|
rk3328->regmap = devm_regmap_init_mmio(&pdev->dev, base,
|
||||||
|
&rk3328_codec_regmap_config);
|
||||||
|
if (IS_ERR(rk3328->regmap))
|
||||||
|
return PTR_ERR(rk3328->regmap);
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, rk3328);
|
||||||
|
|
||||||
|
return devm_snd_soc_register_component(&pdev->dev, &soc_codec_rk3328,
|
||||||
|
rk3328_dai,
|
||||||
|
ARRAY_SIZE(rk3328_dai));
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id rk3328_codec_of_match[] = {
|
||||||
|
{ .compatible = "rockchip,rk3328-codec", },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, rk3328_codec_of_match);
|
||||||
|
|
||||||
|
static struct platform_driver rk3328_codec_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "rk3328-codec",
|
||||||
|
.of_match_table = of_match_ptr(rk3328_codec_of_match),
|
||||||
|
},
|
||||||
|
.probe = rk3328_platform_probe,
|
||||||
|
};
|
||||||
|
module_platform_driver(rk3328_codec_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Sugar Zhang <sugar.zhang@rock-chips.com>");
|
||||||
|
MODULE_DESCRIPTION("ASoC rk3328 codec driver");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
210
sound/soc/codecs/rk3328_codec.h
Normal file
210
sound/soc/codecs/rk3328_codec.h
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
/*
|
||||||
|
* rk3328 ALSA SoC Audio driver
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _RK3328_CODEC_H
|
||||||
|
#define _RK3328_CODEC_H
|
||||||
|
|
||||||
|
#include <linux/bitfield.h>
|
||||||
|
|
||||||
|
/* codec register */
|
||||||
|
#define CODEC_RESET (0x00 << 2)
|
||||||
|
#define DAC_INIT_CTRL1 (0x03 << 2)
|
||||||
|
#define DAC_INIT_CTRL2 (0x04 << 2)
|
||||||
|
#define DAC_INIT_CTRL3 (0x05 << 2)
|
||||||
|
#define DAC_PRECHARGE_CTRL (0x22 << 2)
|
||||||
|
#define DAC_PWR_CTRL (0x23 << 2)
|
||||||
|
#define DAC_CLK_CTRL (0x24 << 2)
|
||||||
|
#define HPMIX_CTRL (0x25 << 2)
|
||||||
|
#define DAC_SELECT (0x26 << 2)
|
||||||
|
#define HPOUT_CTRL (0x27 << 2)
|
||||||
|
#define HPOUTL_GAIN_CTRL (0x28 << 2)
|
||||||
|
#define HPOUTR_GAIN_CTRL (0x29 << 2)
|
||||||
|
#define HPOUT_POP_CTRL (0x2a << 2)
|
||||||
|
|
||||||
|
/* REG00: CODEC_RESET */
|
||||||
|
#define PWR_RST_BYPASS_DIS (0x0 << 6)
|
||||||
|
#define PWR_RST_BYPASS_EN (0x1 << 6)
|
||||||
|
#define DIG_CORE_RST (0x0 << 1)
|
||||||
|
#define DIG_CORE_WORK (0x1 << 1)
|
||||||
|
#define SYS_RST (0x0 << 0)
|
||||||
|
#define SYS_WORK (0x1 << 0)
|
||||||
|
|
||||||
|
/* REG03: DAC_INIT_CTRL1 */
|
||||||
|
#define PIN_DIRECTION_MASK BIT(5)
|
||||||
|
#define PIN_DIRECTION_IN (0x0 << 5)
|
||||||
|
#define PIN_DIRECTION_OUT (0x1 << 5)
|
||||||
|
#define DAC_I2S_MODE_MASK BIT(4)
|
||||||
|
#define DAC_I2S_MODE_SLAVE (0x0 << 4)
|
||||||
|
#define DAC_I2S_MODE_MASTER (0x1 << 4)
|
||||||
|
|
||||||
|
/* REG04: DAC_INIT_CTRL2 */
|
||||||
|
#define DAC_I2S_LRP_MASK BIT(7)
|
||||||
|
#define DAC_I2S_LRP_NORMAL (0x0 << 7)
|
||||||
|
#define DAC_I2S_LRP_REVERSAL (0x1 << 7)
|
||||||
|
#define DAC_VDL_MASK GENMASK(6, 5)
|
||||||
|
#define DAC_VDL_16BITS (0x0 << 5)
|
||||||
|
#define DAC_VDL_20BITS (0x1 << 5)
|
||||||
|
#define DAC_VDL_24BITS (0x2 << 5)
|
||||||
|
#define DAC_VDL_32BITS (0x3 << 5)
|
||||||
|
#define DAC_MODE_MASK GENMASK(4, 3)
|
||||||
|
#define DAC_MODE_RJM (0x0 << 3)
|
||||||
|
#define DAC_MODE_LJM (0x1 << 3)
|
||||||
|
#define DAC_MODE_I2S (0x2 << 3)
|
||||||
|
#define DAC_MODE_PCM (0x3 << 3)
|
||||||
|
#define DAC_LR_SWAP_MASK BIT(2)
|
||||||
|
#define DAC_LR_SWAP_DIS (0x0 << 2)
|
||||||
|
#define DAC_LR_SWAP_EN (0x1 << 2)
|
||||||
|
|
||||||
|
/* REG05: DAC_INIT_CTRL3 */
|
||||||
|
#define DAC_WL_MASK GENMASK(3, 2)
|
||||||
|
#define DAC_WL_16BITS (0x0 << 2)
|
||||||
|
#define DAC_WL_20BITS (0x1 << 2)
|
||||||
|
#define DAC_WL_24BITS (0x2 << 2)
|
||||||
|
#define DAC_WL_32BITS (0x3 << 2)
|
||||||
|
#define DAC_RST_MASK BIT(1)
|
||||||
|
#define DAC_RST_EN (0x0 << 1)
|
||||||
|
#define DAC_RST_DIS (0x1 << 1)
|
||||||
|
#define DAC_BCP_MASK BIT(0)
|
||||||
|
#define DAC_BCP_NORMAL (0x0 << 0)
|
||||||
|
#define DAC_BCP_REVERSAL (0x1 << 0)
|
||||||
|
|
||||||
|
/* REG22: DAC_PRECHARGE_CTRL */
|
||||||
|
#define DAC_CHARGE_XCHARGE_MASK BIT(7)
|
||||||
|
#define DAC_CHARGE_DISCHARGE (0x0 << 7)
|
||||||
|
#define DAC_CHARGE_PRECHARGE (0x1 << 7)
|
||||||
|
#define DAC_CHARGE_CURRENT_64I_MASK BIT(6)
|
||||||
|
#define DAC_CHARGE_CURRENT_64I (0x1 << 6)
|
||||||
|
#define DAC_CHARGE_CURRENT_32I_MASK BIT(5)
|
||||||
|
#define DAC_CHARGE_CURRENT_32I (0x1 << 5)
|
||||||
|
#define DAC_CHARGE_CURRENT_16I_MASK BIT(4)
|
||||||
|
#define DAC_CHARGE_CURRENT_16I (0x1 << 4)
|
||||||
|
#define DAC_CHARGE_CURRENT_08I_MASK BIT(3)
|
||||||
|
#define DAC_CHARGE_CURRENT_08I (0x1 << 3)
|
||||||
|
#define DAC_CHARGE_CURRENT_04I_MASK BIT(2)
|
||||||
|
#define DAC_CHARGE_CURRENT_04I (0x1 << 2)
|
||||||
|
#define DAC_CHARGE_CURRENT_02I_MASK BIT(1)
|
||||||
|
#define DAC_CHARGE_CURRENT_02I (0x1 << 1)
|
||||||
|
#define DAC_CHARGE_CURRENT_I_MASK BIT(0)
|
||||||
|
#define DAC_CHARGE_CURRENT_I (0x1 << 0)
|
||||||
|
#define DAC_CHARGE_CURRENT_ALL_MASK GENMASK(6, 0)
|
||||||
|
#define DAC_CHARGE_CURRENT_ALL_OFF 0x00
|
||||||
|
#define DAC_CHARGE_CURRENT_ALL_ON 0x7f
|
||||||
|
|
||||||
|
/* REG23: DAC_PWR_CTRL */
|
||||||
|
#define DAC_PWR_MASK BIT(6)
|
||||||
|
#define DAC_PWR_OFF (0x0 << 6)
|
||||||
|
#define DAC_PWR_ON (0x1 << 6)
|
||||||
|
#define DACL_PATH_REFV_MASK BIT(5)
|
||||||
|
#define DACL_PATH_REFV_OFF (0x0 << 5)
|
||||||
|
#define DACL_PATH_REFV_ON (0x1 << 5)
|
||||||
|
#define HPOUTL_ZERO_CROSSING_MASK BIT(4)
|
||||||
|
#define HPOUTL_ZERO_CROSSING_OFF (0x0 << 4)
|
||||||
|
#define HPOUTL_ZERO_CROSSING_ON (0x1 << 4)
|
||||||
|
#define DACR_PATH_REFV_MASK BIT(1)
|
||||||
|
#define DACR_PATH_REFV_OFF (0x0 << 1)
|
||||||
|
#define DACR_PATH_REFV_ON (0x1 << 1)
|
||||||
|
#define HPOUTR_ZERO_CROSSING_MASK BIT(0)
|
||||||
|
#define HPOUTR_ZERO_CROSSING_OFF (0x0 << 0)
|
||||||
|
#define HPOUTR_ZERO_CROSSING_ON (0x1 << 0)
|
||||||
|
|
||||||
|
/* REG24: DAC_CLK_CTRL */
|
||||||
|
#define DACL_REFV_MASK BIT(7)
|
||||||
|
#define DACL_REFV_OFF (0x0 << 7)
|
||||||
|
#define DACL_REFV_ON (0x1 << 7)
|
||||||
|
#define DACL_CLK_MASK BIT(6)
|
||||||
|
#define DACL_CLK_OFF (0x0 << 6)
|
||||||
|
#define DACL_CLK_ON (0x1 << 6)
|
||||||
|
#define DACL_MASK BIT(5)
|
||||||
|
#define DACL_OFF (0x0 << 5)
|
||||||
|
#define DACL_ON (0x1 << 5)
|
||||||
|
#define DACL_INIT_MASK BIT(4)
|
||||||
|
#define DACL_INIT_OFF (0x0 << 4)
|
||||||
|
#define DACL_INIT_ON (0x1 << 4)
|
||||||
|
#define DACR_REFV_MASK BIT(3)
|
||||||
|
#define DACR_REFV_OFF (0x0 << 3)
|
||||||
|
#define DACR_REFV_ON (0x1 << 3)
|
||||||
|
#define DACR_CLK_MASK BIT(2)
|
||||||
|
#define DACR_CLK_OFF (0x0 << 2)
|
||||||
|
#define DACR_CLK_ON (0x1 << 2)
|
||||||
|
#define DACR_MASK BIT(1)
|
||||||
|
#define DACR_OFF (0x0 << 1)
|
||||||
|
#define DACR_ON (0x1 << 1)
|
||||||
|
#define DACR_INIT_MASK BIT(0)
|
||||||
|
#define DACR_INIT_OFF (0x0 << 0)
|
||||||
|
#define DACR_INIT_ON (0x1 << 0)
|
||||||
|
|
||||||
|
/* REG25: HPMIX_CTRL*/
|
||||||
|
#define HPMIXL_MASK BIT(6)
|
||||||
|
#define HPMIXL_DIS (0x0 << 6)
|
||||||
|
#define HPMIXL_EN (0x1 << 6)
|
||||||
|
#define HPMIXL_INIT_MASK BIT(5)
|
||||||
|
#define HPMIXL_INIT_DIS (0x0 << 5)
|
||||||
|
#define HPMIXL_INIT_EN (0x1 << 5)
|
||||||
|
#define HPMIXL_INIT2_MASK BIT(4)
|
||||||
|
#define HPMIXL_INIT2_DIS (0x0 << 4)
|
||||||
|
#define HPMIXL_INIT2_EN (0x1 << 4)
|
||||||
|
#define HPMIXR_MASK BIT(2)
|
||||||
|
#define HPMIXR_DIS (0x0 << 2)
|
||||||
|
#define HPMIXR_EN (0x1 << 2)
|
||||||
|
#define HPMIXR_INIT_MASK BIT(1)
|
||||||
|
#define HPMIXR_INIT_DIS (0x0 << 1)
|
||||||
|
#define HPMIXR_INIT_EN (0x1 << 1)
|
||||||
|
#define HPMIXR_INIT2_MASK BIT(0)
|
||||||
|
#define HPMIXR_INIT2_DIS (0x0 << 0)
|
||||||
|
#define HPMIXR_INIT2_EN (0x1 << 0)
|
||||||
|
|
||||||
|
/* REG26: DAC_SELECT */
|
||||||
|
#define DACL_SELECT_MASK BIT(4)
|
||||||
|
#define DACL_UNSELECT (0x0 << 4)
|
||||||
|
#define DACL_SELECT (0x1 << 4)
|
||||||
|
#define DACR_SELECT_MASK BIT(0)
|
||||||
|
#define DACR_UNSELECT (0x0 << 0)
|
||||||
|
#define DACR_SELECT (0x1 << 0)
|
||||||
|
|
||||||
|
/* REG27: HPOUT_CTRL */
|
||||||
|
#define HPOUTL_MASK BIT(7)
|
||||||
|
#define HPOUTL_DIS (0x0 << 7)
|
||||||
|
#define HPOUTL_EN (0x1 << 7)
|
||||||
|
#define HPOUTL_INIT_MASK BIT(6)
|
||||||
|
#define HPOUTL_INIT_DIS (0x0 << 6)
|
||||||
|
#define HPOUTL_INIT_EN (0x1 << 6)
|
||||||
|
#define HPOUTL_MUTE_MASK BIT(5)
|
||||||
|
#define HPOUTL_MUTE (0x0 << 5)
|
||||||
|
#define HPOUTL_UNMUTE (0x1 << 5)
|
||||||
|
#define HPOUTR_MASK BIT(4)
|
||||||
|
#define HPOUTR_DIS (0x0 << 4)
|
||||||
|
#define HPOUTR_EN (0x1 << 4)
|
||||||
|
#define HPOUTR_INIT_MASK BIT(3)
|
||||||
|
#define HPOUTR_INIT_DIS (0x0 << 3)
|
||||||
|
#define HPOUTR_INIT_EN (0x1 << 3)
|
||||||
|
#define HPOUTR_MUTE_MASK BIT(2)
|
||||||
|
#define HPOUTR_MUTE (0x0 << 2)
|
||||||
|
#define HPOUTR_UNMUTE (0x1 << 2)
|
||||||
|
|
||||||
|
/* REG28: HPOUTL_GAIN_CTRL */
|
||||||
|
#define HPOUTL_GAIN_MASK GENMASK(4, 0)
|
||||||
|
|
||||||
|
/* REG29: HPOUTR_GAIN_CTRL */
|
||||||
|
#define HPOUTR_GAIN_MASK GENMASK(4, 0)
|
||||||
|
|
||||||
|
/* REG2a: HPOUT_POP_CTRL */
|
||||||
|
#define HPOUTR_POP_MASK GENMASK(5, 4)
|
||||||
|
#define HPOUTR_POP_XCHARGE (0x1 << 4)
|
||||||
|
#define HPOUTR_POP_WORK (0x2 << 4)
|
||||||
|
#define HPOUTL_POP_MASK GENMASK(1, 0)
|
||||||
|
#define HPOUTL_POP_XCHARGE (0x1 << 0)
|
||||||
|
#define HPOUTL_POP_WORK (0x2 << 0)
|
||||||
|
|
||||||
|
#define RK3328_HIFI 0
|
||||||
|
|
||||||
|
struct rk3328_reg_msk_val {
|
||||||
|
unsigned int reg;
|
||||||
|
unsigned int msk;
|
||||||
|
unsigned int val;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -1128,8 +1128,11 @@ static int rt274_i2c_probe(struct i2c_client *i2c,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
regmap_read(rt274->regmap,
|
ret = regmap_read(rt274->regmap,
|
||||||
RT274_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID), &val);
|
RT274_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID), &val);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
if (val != RT274_VENDOR_ID) {
|
if (val != RT274_VENDOR_ID) {
|
||||||
dev_err(&i2c->dev,
|
dev_err(&i2c->dev,
|
||||||
"Device with ID register %#x is not rt274\n", val);
|
"Device with ID register %#x is not rt274\n", val);
|
||||||
|
@ -2512,6 +2512,7 @@ static void rt5682_calibrate(struct rt5682_priv *rt5682)
|
|||||||
regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x0000);
|
regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x0000);
|
||||||
regmap_write(rt5682->regmap, RT5682_CHOP_DAC, 0x2000);
|
regmap_write(rt5682->regmap, RT5682_CHOP_DAC, 0x2000);
|
||||||
regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x2005);
|
regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x2005);
|
||||||
|
regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0xc0c4);
|
||||||
|
|
||||||
mutex_unlock(&rt5682->calibrate_mutex);
|
mutex_unlock(&rt5682->calibrate_mutex);
|
||||||
|
|
||||||
|
@ -1837,9 +1837,6 @@ static int wm8904_set_bias_level(struct snd_soc_component *component,
|
|||||||
|
|
||||||
switch (level) {
|
switch (level) {
|
||||||
case SND_SOC_BIAS_ON:
|
case SND_SOC_BIAS_ON:
|
||||||
ret = clk_prepare_enable(wm8904->mclk);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SND_SOC_BIAS_PREPARE:
|
case SND_SOC_BIAS_PREPARE:
|
||||||
@ -1864,6 +1861,15 @@ static int wm8904_set_bias_level(struct snd_soc_component *component,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(wm8904->mclk);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(component->dev,
|
||||||
|
"Failed to enable MCLK: %d\n", ret);
|
||||||
|
regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies),
|
||||||
|
wm8904->supplies);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
regcache_cache_only(wm8904->regmap, false);
|
regcache_cache_only(wm8904->regmap, false);
|
||||||
regcache_sync(wm8904->regmap);
|
regcache_sync(wm8904->regmap);
|
||||||
|
|
||||||
@ -2108,16 +2114,13 @@ static const struct regmap_config wm8904_regmap = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
#ifdef CONFIG_OF
|
||||||
static enum wm8904_type wm8904_data = WM8904;
|
|
||||||
static enum wm8904_type wm8912_data = WM8912;
|
|
||||||
|
|
||||||
static const struct of_device_id wm8904_of_match[] = {
|
static const struct of_device_id wm8904_of_match[] = {
|
||||||
{
|
{
|
||||||
.compatible = "wlf,wm8904",
|
.compatible = "wlf,wm8904",
|
||||||
.data = &wm8904_data,
|
.data = (void *)WM8904,
|
||||||
}, {
|
}, {
|
||||||
.compatible = "wlf,wm8912",
|
.compatible = "wlf,wm8912",
|
||||||
.data = &wm8912_data,
|
.data = (void *)WM8912,
|
||||||
}, {
|
}, {
|
||||||
/* sentinel */
|
/* sentinel */
|
||||||
}
|
}
|
||||||
@ -2158,7 +2161,7 @@ static int wm8904_i2c_probe(struct i2c_client *i2c,
|
|||||||
match = of_match_node(wm8904_of_match, i2c->dev.of_node);
|
match = of_match_node(wm8904_of_match, i2c->dev.of_node);
|
||||||
if (match == NULL)
|
if (match == NULL)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
wm8904->devtype = *((enum wm8904_type *)match->data);
|
wm8904->devtype = (enum wm8904_type)match->data;
|
||||||
} else {
|
} else {
|
||||||
wm8904->devtype = id->driver_data;
|
wm8904->devtype = id->driver_data;
|
||||||
}
|
}
|
||||||
|
@ -8,14 +8,6 @@ config SND_SIMPLE_CARD
|
|||||||
This option enables generic simple sound card support
|
This option enables generic simple sound card support
|
||||||
It also support DPCM of multi CPU single Codec ststem.
|
It also support DPCM of multi CPU single Codec ststem.
|
||||||
|
|
||||||
config SND_SIMPLE_SCU_CARD
|
|
||||||
tristate "ASoC Simple SCU sound card support"
|
|
||||||
depends on OF
|
|
||||||
select SND_SIMPLE_CARD_UTILS
|
|
||||||
help
|
|
||||||
This option enables generic simple SCU sound card support.
|
|
||||||
It supports DPCM of multi CPU single Codec system.
|
|
||||||
|
|
||||||
config SND_AUDIO_GRAPH_CARD
|
config SND_AUDIO_GRAPH_CARD
|
||||||
tristate "ASoC Audio Graph sound card support"
|
tristate "ASoC Audio Graph sound card support"
|
||||||
depends on OF
|
depends on OF
|
||||||
@ -24,12 +16,3 @@ config SND_AUDIO_GRAPH_CARD
|
|||||||
This option enables generic simple sound card support
|
This option enables generic simple sound card support
|
||||||
with OF-graph DT bindings.
|
with OF-graph DT bindings.
|
||||||
It also support DPCM of multi CPU single Codec ststem.
|
It also support DPCM of multi CPU single Codec ststem.
|
||||||
|
|
||||||
config SND_AUDIO_GRAPH_SCU_CARD
|
|
||||||
tristate "ASoC Audio Graph SCU sound card support"
|
|
||||||
depends on OF
|
|
||||||
select SND_SIMPLE_CARD_UTILS
|
|
||||||
help
|
|
||||||
This option enables generic simple SCU sound card support
|
|
||||||
with OF-graph DT bindings.
|
|
||||||
It supports DPCM of multi CPU single Codec ststem.
|
|
||||||
|
@ -1,12 +1,8 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
snd-soc-simple-card-utils-objs := simple-card-utils.o
|
snd-soc-simple-card-utils-objs := simple-card-utils.o
|
||||||
snd-soc-simple-card-objs := simple-card.o
|
snd-soc-simple-card-objs := simple-card.o
|
||||||
snd-soc-simple-scu-card-objs := simple-scu-card.o
|
|
||||||
snd-soc-audio-graph-card-objs := audio-graph-card.o
|
snd-soc-audio-graph-card-objs := audio-graph-card.o
|
||||||
snd-soc-audio-graph-scu-card-objs := audio-graph-scu-card.o
|
|
||||||
|
|
||||||
obj-$(CONFIG_SND_SIMPLE_CARD_UTILS) += snd-soc-simple-card-utils.o
|
obj-$(CONFIG_SND_SIMPLE_CARD_UTILS) += snd-soc-simple-card-utils.o
|
||||||
obj-$(CONFIG_SND_SIMPLE_CARD) += snd-soc-simple-card.o
|
obj-$(CONFIG_SND_SIMPLE_CARD) += snd-soc-simple-card.o
|
||||||
obj-$(CONFIG_SND_SIMPLE_SCU_CARD) += snd-soc-simple-scu-card.o
|
|
||||||
obj-$(CONFIG_SND_AUDIO_GRAPH_CARD) += snd-soc-audio-graph-card.o
|
obj-$(CONFIG_SND_AUDIO_GRAPH_CARD) += snd-soc-audio-graph-card.o
|
||||||
obj-$(CONFIG_SND_AUDIO_GRAPH_SCU_CARD) += snd-soc-audio-graph-scu-card.o
|
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
#include <sound/simple_card_utils.h>
|
#include <sound/simple_card_utils.h>
|
||||||
|
|
||||||
struct graph_card_data {
|
struct graph_priv {
|
||||||
struct snd_soc_card snd_card;
|
struct snd_soc_card snd_card;
|
||||||
struct graph_dai_props {
|
struct graph_dai_props {
|
||||||
struct asoc_simple_dai *cpu_dai;
|
struct asoc_simple_dai *cpu_dai;
|
||||||
@ -39,6 +39,13 @@ struct graph_card_data {
|
|||||||
struct gpio_desc *pa_gpio;
|
struct gpio_desc *pa_gpio;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct link_info {
|
||||||
|
int dais; /* number of dai */
|
||||||
|
int link; /* number of link */
|
||||||
|
int conf; /* number of codec_conf */
|
||||||
|
int cpu; /* turn for CPU / Codec */
|
||||||
|
};
|
||||||
|
|
||||||
#define graph_priv_to_card(priv) (&(priv)->snd_card)
|
#define graph_priv_to_card(priv) (&(priv)->snd_card)
|
||||||
#define graph_priv_to_props(priv, i) ((priv)->dai_props + (i))
|
#define graph_priv_to_props(priv, i) ((priv)->dai_props + (i))
|
||||||
#define graph_priv_to_dev(priv) (graph_priv_to_card(priv)->dev)
|
#define graph_priv_to_dev(priv) (graph_priv_to_card(priv)->dev)
|
||||||
@ -46,12 +53,12 @@ struct graph_card_data {
|
|||||||
|
|
||||||
#define PREFIX "audio-graph-card,"
|
#define PREFIX "audio-graph-card,"
|
||||||
|
|
||||||
static int asoc_graph_card_outdrv_event(struct snd_soc_dapm_widget *w,
|
static int graph_outdrv_event(struct snd_soc_dapm_widget *w,
|
||||||
struct snd_kcontrol *kcontrol,
|
struct snd_kcontrol *kcontrol,
|
||||||
int event)
|
int event)
|
||||||
{
|
{
|
||||||
struct snd_soc_dapm_context *dapm = w->dapm;
|
struct snd_soc_dapm_context *dapm = w->dapm;
|
||||||
struct graph_card_data *priv = snd_soc_card_get_drvdata(dapm->card);
|
struct graph_priv *priv = snd_soc_card_get_drvdata(dapm->card);
|
||||||
|
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case SND_SOC_DAPM_POST_PMU:
|
case SND_SOC_DAPM_POST_PMU:
|
||||||
@ -67,16 +74,16 @@ static int asoc_graph_card_outdrv_event(struct snd_soc_dapm_widget *w,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct snd_soc_dapm_widget asoc_graph_card_dapm_widgets[] = {
|
static const struct snd_soc_dapm_widget graph_dapm_widgets[] = {
|
||||||
SND_SOC_DAPM_OUT_DRV_E("Amplifier", SND_SOC_NOPM,
|
SND_SOC_DAPM_OUT_DRV_E("Amplifier", SND_SOC_NOPM,
|
||||||
0, 0, NULL, 0, asoc_graph_card_outdrv_event,
|
0, 0, NULL, 0, graph_outdrv_event,
|
||||||
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
|
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
|
||||||
};
|
};
|
||||||
|
|
||||||
static int asoc_graph_card_startup(struct snd_pcm_substream *substream)
|
static int graph_startup(struct snd_pcm_substream *substream)
|
||||||
{
|
{
|
||||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||||
struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
struct graph_priv *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||||
struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
|
struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@ -91,10 +98,10 @@ static int asoc_graph_card_startup(struct snd_pcm_substream *substream)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream)
|
static void graph_shutdown(struct snd_pcm_substream *substream)
|
||||||
{
|
{
|
||||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||||
struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
struct graph_priv *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||||
struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
|
struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
|
||||||
|
|
||||||
asoc_simple_card_clk_disable(dai_props->cpu_dai);
|
asoc_simple_card_clk_disable(dai_props->cpu_dai);
|
||||||
@ -102,13 +109,13 @@ static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream)
|
|||||||
asoc_simple_card_clk_disable(dai_props->codec_dai);
|
asoc_simple_card_clk_disable(dai_props->codec_dai);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int asoc_graph_card_hw_params(struct snd_pcm_substream *substream,
|
static int graph_hw_params(struct snd_pcm_substream *substream,
|
||||||
struct snd_pcm_hw_params *params)
|
struct snd_pcm_hw_params *params)
|
||||||
{
|
{
|
||||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||||
struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
struct graph_priv *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||||
struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
|
struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
|
||||||
unsigned int mclk, mclk_fs = 0;
|
unsigned int mclk, mclk_fs = 0;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
@ -133,15 +140,15 @@ err:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct snd_soc_ops asoc_graph_card_ops = {
|
static const struct snd_soc_ops graph_ops = {
|
||||||
.startup = asoc_graph_card_startup,
|
.startup = graph_startup,
|
||||||
.shutdown = asoc_graph_card_shutdown,
|
.shutdown = graph_shutdown,
|
||||||
.hw_params = asoc_graph_card_hw_params,
|
.hw_params = graph_hw_params,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int asoc_graph_card_dai_init(struct snd_soc_pcm_runtime *rtd)
|
static int graph_dai_init(struct snd_soc_pcm_runtime *rtd)
|
||||||
{
|
{
|
||||||
struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
struct graph_priv *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||||
struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
|
struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
@ -158,10 +165,10 @@ static int asoc_graph_card_dai_init(struct snd_soc_pcm_runtime *rtd)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int asoc_graph_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
|
static int graph_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||||
struct snd_pcm_hw_params *params)
|
struct snd_pcm_hw_params *params)
|
||||||
{
|
{
|
||||||
struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
struct graph_priv *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||||
struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
|
struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
|
||||||
|
|
||||||
asoc_simple_card_convert_fixup(&dai_props->adata, params);
|
asoc_simple_card_convert_fixup(&dai_props->adata, params);
|
||||||
@ -169,41 +176,64 @@ static int asoc_graph_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int asoc_graph_card_dai_link_of_dpcm(struct device_node *top,
|
static void graph_get_conversion(struct device *dev,
|
||||||
struct device_node *cpu_ep,
|
struct device_node *ep,
|
||||||
struct device_node *codec_ep,
|
struct asoc_simple_card_data *adata)
|
||||||
struct graph_card_data *priv,
|
|
||||||
int *dai_idx, int link_idx,
|
|
||||||
int *conf_idx, int is_cpu)
|
|
||||||
{
|
{
|
||||||
struct device *dev = graph_priv_to_dev(priv);
|
struct device_node *top = dev->of_node;
|
||||||
struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, link_idx);
|
|
||||||
struct graph_dai_props *dai_props = graph_priv_to_props(priv, link_idx);
|
|
||||||
struct device_node *ep = is_cpu ? cpu_ep : codec_ep;
|
|
||||||
struct device_node *port = of_get_parent(ep);
|
struct device_node *port = of_get_parent(ep);
|
||||||
struct device_node *ports = of_get_parent(port);
|
struct device_node *ports = of_get_parent(port);
|
||||||
struct device_node *node = of_graph_get_port_parent(ep);
|
struct device_node *node = of_graph_get_port_parent(ep);
|
||||||
|
|
||||||
|
asoc_simple_card_parse_convert(dev, top, NULL, adata);
|
||||||
|
asoc_simple_card_parse_convert(dev, node, PREFIX, adata);
|
||||||
|
asoc_simple_card_parse_convert(dev, ports, NULL, adata);
|
||||||
|
asoc_simple_card_parse_convert(dev, port, NULL, adata);
|
||||||
|
asoc_simple_card_parse_convert(dev, ep, NULL, adata);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int graph_dai_link_of_dpcm(struct graph_priv *priv,
|
||||||
|
struct device_node *cpu_ep,
|
||||||
|
struct device_node *codec_ep,
|
||||||
|
struct link_info *li,
|
||||||
|
int dup_codec)
|
||||||
|
{
|
||||||
|
struct device *dev = graph_priv_to_dev(priv);
|
||||||
|
struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, li->link);
|
||||||
|
struct graph_dai_props *dai_props = graph_priv_to_props(priv, li->link);
|
||||||
|
struct device_node *top = dev->of_node;
|
||||||
|
struct device_node *ep = li->cpu ? cpu_ep : codec_ep;
|
||||||
|
struct device_node *port;
|
||||||
|
struct device_node *ports;
|
||||||
|
struct device_node *node;
|
||||||
struct asoc_simple_dai *dai;
|
struct asoc_simple_dai *dai;
|
||||||
struct snd_soc_dai_link_component *codecs = dai_link->codecs;
|
struct snd_soc_dai_link_component *codecs = dai_link->codecs;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
dev_dbg(dev, "link_of DPCM (for %s)\n", is_cpu ? "CPU" : "Codec");
|
/* Do it all CPU endpoint, and 1st Codec endpoint */
|
||||||
|
if (!li->cpu && dup_codec)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
port = of_get_parent(ep);
|
||||||
|
ports = of_get_parent(port);
|
||||||
|
node = of_graph_get_port_parent(ep);
|
||||||
|
|
||||||
|
li->link++;
|
||||||
|
|
||||||
|
dev_dbg(dev, "link_of DPCM (%pOF)\n", ep);
|
||||||
|
|
||||||
of_property_read_u32(top, "mclk-fs", &dai_props->mclk_fs);
|
of_property_read_u32(top, "mclk-fs", &dai_props->mclk_fs);
|
||||||
of_property_read_u32(ports, "mclk-fs", &dai_props->mclk_fs);
|
of_property_read_u32(ports, "mclk-fs", &dai_props->mclk_fs);
|
||||||
of_property_read_u32(port, "mclk-fs", &dai_props->mclk_fs);
|
of_property_read_u32(port, "mclk-fs", &dai_props->mclk_fs);
|
||||||
of_property_read_u32(ep, "mclk-fs", &dai_props->mclk_fs);
|
of_property_read_u32(ep, "mclk-fs", &dai_props->mclk_fs);
|
||||||
|
|
||||||
asoc_simple_card_parse_convert(dev, top, NULL, &dai_props->adata);
|
graph_get_conversion(dev, ep, &dai_props->adata);
|
||||||
asoc_simple_card_parse_convert(dev, node, PREFIX, &dai_props->adata);
|
|
||||||
asoc_simple_card_parse_convert(dev, ports, NULL, &dai_props->adata);
|
|
||||||
asoc_simple_card_parse_convert(dev, port, NULL, &dai_props->adata);
|
|
||||||
asoc_simple_card_parse_convert(dev, ep, NULL, &dai_props->adata);
|
|
||||||
|
|
||||||
of_node_put(ports);
|
of_node_put(ports);
|
||||||
of_node_put(port);
|
of_node_put(port);
|
||||||
|
of_node_put(node);
|
||||||
|
|
||||||
if (is_cpu) {
|
if (li->cpu) {
|
||||||
|
|
||||||
/* BE is dummy */
|
/* BE is dummy */
|
||||||
codecs->of_node = NULL;
|
codecs->of_node = NULL;
|
||||||
@ -215,7 +245,7 @@ static int asoc_graph_card_dai_link_of_dpcm(struct device_node *top,
|
|||||||
dai_link->dpcm_merged_format = 1;
|
dai_link->dpcm_merged_format = 1;
|
||||||
|
|
||||||
dai =
|
dai =
|
||||||
dai_props->cpu_dai = &priv->dais[(*dai_idx)++];
|
dai_props->cpu_dai = &priv->dais[li->dais++];
|
||||||
|
|
||||||
ret = asoc_simple_card_parse_graph_cpu(ep, dai_link);
|
ret = asoc_simple_card_parse_graph_cpu(ep, dai_link);
|
||||||
if (ret)
|
if (ret)
|
||||||
@ -244,13 +274,13 @@ static int asoc_graph_card_dai_link_of_dpcm(struct device_node *top,
|
|||||||
|
|
||||||
/* BE settings */
|
/* BE settings */
|
||||||
dai_link->no_pcm = 1;
|
dai_link->no_pcm = 1;
|
||||||
dai_link->be_hw_params_fixup = asoc_graph_card_be_hw_params_fixup;
|
dai_link->be_hw_params_fixup = graph_be_hw_params_fixup;
|
||||||
|
|
||||||
dai =
|
dai =
|
||||||
dai_props->codec_dai = &priv->dais[(*dai_idx)++];
|
dai_props->codec_dai = &priv->dais[li->dais++];
|
||||||
|
|
||||||
cconf =
|
cconf =
|
||||||
dai_props->codec_conf = &priv->codec_conf[(*conf_idx)++];
|
dai_props->codec_conf = &priv->codec_conf[li->conf++];
|
||||||
|
|
||||||
ret = asoc_simple_card_parse_graph_codec(ep, dai_link);
|
ret = asoc_simple_card_parse_graph_codec(ep, dai_link);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
@ -292,35 +322,46 @@ static int asoc_graph_card_dai_link_of_dpcm(struct device_node *top,
|
|||||||
|
|
||||||
dai_link->dpcm_playback = 1;
|
dai_link->dpcm_playback = 1;
|
||||||
dai_link->dpcm_capture = 1;
|
dai_link->dpcm_capture = 1;
|
||||||
dai_link->ops = &asoc_graph_card_ops;
|
dai_link->ops = &graph_ops;
|
||||||
dai_link->init = asoc_graph_card_dai_init;
|
dai_link->init = graph_dai_init;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int asoc_graph_card_dai_link_of(struct device_node *top,
|
static int graph_dai_link_of(struct graph_priv *priv,
|
||||||
struct device_node *cpu_ep,
|
struct device_node *cpu_ep,
|
||||||
struct device_node *codec_ep,
|
struct device_node *codec_ep,
|
||||||
struct graph_card_data *priv,
|
struct link_info *li)
|
||||||
int *dai_idx, int link_idx)
|
|
||||||
{
|
{
|
||||||
struct device *dev = graph_priv_to_dev(priv);
|
struct device *dev = graph_priv_to_dev(priv);
|
||||||
struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, link_idx);
|
struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, li->link);
|
||||||
struct graph_dai_props *dai_props = graph_priv_to_props(priv, link_idx);
|
struct graph_dai_props *dai_props = graph_priv_to_props(priv, li->link);
|
||||||
struct device_node *cpu_port = of_get_parent(cpu_ep);
|
struct device_node *top = dev->of_node;
|
||||||
struct device_node *codec_port = of_get_parent(codec_ep);
|
struct device_node *cpu_port;
|
||||||
struct device_node *cpu_ports = of_get_parent(cpu_port);
|
struct device_node *cpu_ports;
|
||||||
struct device_node *codec_ports = of_get_parent(codec_port);
|
struct device_node *codec_port;
|
||||||
|
struct device_node *codec_ports;
|
||||||
struct asoc_simple_dai *cpu_dai;
|
struct asoc_simple_dai *cpu_dai;
|
||||||
struct asoc_simple_dai *codec_dai;
|
struct asoc_simple_dai *codec_dai;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
dev_dbg(dev, "link_of\n");
|
/* Do it only CPU turn */
|
||||||
|
if (!li->cpu)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
cpu_port = of_get_parent(cpu_ep);
|
||||||
|
cpu_ports = of_get_parent(cpu_port);
|
||||||
|
codec_port = of_get_parent(codec_ep);
|
||||||
|
codec_ports = of_get_parent(codec_port);
|
||||||
|
|
||||||
|
dev_dbg(dev, "link_of (%pOF)\n", cpu_ep);
|
||||||
|
|
||||||
|
li->link++;
|
||||||
|
|
||||||
cpu_dai =
|
cpu_dai =
|
||||||
dai_props->cpu_dai = &priv->dais[(*dai_idx)++];
|
dai_props->cpu_dai = &priv->dais[li->dais++];
|
||||||
codec_dai =
|
codec_dai =
|
||||||
dai_props->codec_dai = &priv->dais[(*dai_idx)++];
|
dai_props->codec_dai = &priv->dais[li->dais++];
|
||||||
|
|
||||||
/* Factor to mclk, used in hw_params() */
|
/* Factor to mclk, used in hw_params() */
|
||||||
of_property_read_u32(top, "mclk-fs", &dai_props->mclk_fs);
|
of_property_read_u32(top, "mclk-fs", &dai_props->mclk_fs);
|
||||||
@ -375,8 +416,8 @@ static int asoc_graph_card_dai_link_of(struct device_node *top,
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
dai_link->ops = &asoc_graph_card_ops;
|
dai_link->ops = &graph_ops;
|
||||||
dai_link->init = asoc_graph_card_dai_init;
|
dai_link->init = graph_dai_init;
|
||||||
|
|
||||||
asoc_simple_card_canonicalize_cpu(dai_link,
|
asoc_simple_card_canonicalize_cpu(dai_link,
|
||||||
of_graph_get_endpoint_count(dai_link->cpu_of_node) == 1);
|
of_graph_get_endpoint_count(dai_link->cpu_of_node) == 1);
|
||||||
@ -384,21 +425,79 @@ static int asoc_graph_card_dai_link_of(struct device_node *top,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int asoc_graph_card_parse_of(struct graph_card_data *priv)
|
static int graph_for_each_link(struct graph_priv *priv,
|
||||||
|
struct link_info *li,
|
||||||
|
int (*func_noml)(struct graph_priv *priv,
|
||||||
|
struct device_node *cpu_ep,
|
||||||
|
struct device_node *codec_ep,
|
||||||
|
struct link_info *li),
|
||||||
|
int (*func_dpcm)(struct graph_priv *priv,
|
||||||
|
struct device_node *cpu_ep,
|
||||||
|
struct device_node *codec_ep,
|
||||||
|
struct link_info *li, int dup_codec))
|
||||||
{
|
{
|
||||||
struct of_phandle_iterator it;
|
struct of_phandle_iterator it;
|
||||||
struct device *dev = graph_priv_to_dev(priv);
|
struct device *dev = graph_priv_to_dev(priv);
|
||||||
struct snd_soc_card *card = graph_priv_to_card(priv);
|
struct device_node *node = dev->of_node;
|
||||||
struct device_node *top = dev->of_node;
|
|
||||||
struct device_node *node = top;
|
|
||||||
struct device_node *cpu_port;
|
struct device_node *cpu_port;
|
||||||
struct device_node *cpu_ep = NULL;
|
struct device_node *cpu_ep;
|
||||||
struct device_node *codec_ep = NULL;
|
struct device_node *codec_ep;
|
||||||
struct device_node *codec_port = NULL;
|
struct device_node *codec_port;
|
||||||
struct device_node *codec_port_old = NULL;
|
struct device_node *codec_port_old = NULL;
|
||||||
|
struct asoc_simple_card_data adata;
|
||||||
int rc, ret;
|
int rc, ret;
|
||||||
int link_idx, dai_idx, conf_idx;
|
|
||||||
int cpu;
|
/* loop for all listed CPU port */
|
||||||
|
of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
|
||||||
|
cpu_port = it.node;
|
||||||
|
cpu_ep = NULL;
|
||||||
|
|
||||||
|
/* loop for all CPU endpoint */
|
||||||
|
while (1) {
|
||||||
|
cpu_ep = of_get_next_child(cpu_port, cpu_ep);
|
||||||
|
if (!cpu_ep)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* get codec */
|
||||||
|
codec_ep = of_graph_get_remote_endpoint(cpu_ep);
|
||||||
|
codec_port = of_get_parent(codec_ep);
|
||||||
|
|
||||||
|
of_node_put(codec_ep);
|
||||||
|
of_node_put(codec_port);
|
||||||
|
|
||||||
|
/* get convert-xxx property */
|
||||||
|
memset(&adata, 0, sizeof(adata));
|
||||||
|
graph_get_conversion(dev, codec_ep, &adata);
|
||||||
|
graph_get_conversion(dev, cpu_ep, &adata);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* It is DPCM
|
||||||
|
* if Codec port has many endpoints,
|
||||||
|
* or has convert-xxx property
|
||||||
|
*/
|
||||||
|
if ((of_get_child_count(codec_port) > 1) ||
|
||||||
|
adata.convert_rate || adata.convert_channels)
|
||||||
|
ret = func_dpcm(priv, cpu_ep, codec_ep, li,
|
||||||
|
(codec_port_old == codec_port));
|
||||||
|
/* else normal sound */
|
||||||
|
else
|
||||||
|
ret = func_noml(priv, cpu_ep, codec_ep, li);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
codec_port_old = codec_port;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int graph_parse_of(struct graph_priv *priv)
|
||||||
|
{
|
||||||
|
struct snd_soc_card *card = graph_priv_to_card(priv);
|
||||||
|
struct link_info li;
|
||||||
|
int ret;
|
||||||
|
|
||||||
ret = asoc_simple_card_of_parse_widgets(card, NULL);
|
ret = asoc_simple_card_of_parse_widgets(card, NULL);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
@ -408,11 +507,8 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv)
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
link_idx = 0;
|
memset(&li, 0, sizeof(li));
|
||||||
dai_idx = 0;
|
for (li.cpu = 1; li.cpu >= 0; li.cpu--) {
|
||||||
conf_idx = 0;
|
|
||||||
codec_port_old = NULL;
|
|
||||||
for (cpu = 1; cpu >= 0; cpu--) {
|
|
||||||
/*
|
/*
|
||||||
* Detect all CPU first, and Detect all Codec 2nd.
|
* Detect all CPU first, and Detect all Codec 2nd.
|
||||||
*
|
*
|
||||||
@ -425,66 +521,57 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv)
|
|||||||
* To avoid random sub-device numbering,
|
* To avoid random sub-device numbering,
|
||||||
* detect "dummy-Codec" in last;
|
* detect "dummy-Codec" in last;
|
||||||
*/
|
*/
|
||||||
of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
|
ret = graph_for_each_link(priv, &li,
|
||||||
cpu_port = it.node;
|
graph_dai_link_of,
|
||||||
cpu_ep = NULL;
|
graph_dai_link_of_dpcm);
|
||||||
while (1) {
|
if (ret < 0)
|
||||||
cpu_ep = of_get_next_child(cpu_port, cpu_ep);
|
return ret;
|
||||||
if (!cpu_ep)
|
|
||||||
break;
|
|
||||||
|
|
||||||
codec_ep = of_graph_get_remote_endpoint(cpu_ep);
|
|
||||||
codec_port = of_get_parent(codec_ep);
|
|
||||||
|
|
||||||
of_node_put(codec_ep);
|
|
||||||
of_node_put(codec_port);
|
|
||||||
|
|
||||||
dev_dbg(dev, "%pOFf <-> %pOFf\n", cpu_ep, codec_ep);
|
|
||||||
|
|
||||||
if (of_get_child_count(codec_port) > 1) {
|
|
||||||
/*
|
|
||||||
* for DPCM sound
|
|
||||||
*/
|
|
||||||
if (!cpu) {
|
|
||||||
if (codec_port_old == codec_port)
|
|
||||||
continue;
|
|
||||||
codec_port_old = codec_port;
|
|
||||||
}
|
|
||||||
ret = asoc_graph_card_dai_link_of_dpcm(
|
|
||||||
top, cpu_ep, codec_ep, priv,
|
|
||||||
&dai_idx, link_idx++,
|
|
||||||
&conf_idx, cpu);
|
|
||||||
} else if (cpu) {
|
|
||||||
/*
|
|
||||||
* for Normal sound
|
|
||||||
*/
|
|
||||||
ret = asoc_graph_card_dai_link_of(
|
|
||||||
top, cpu_ep, codec_ep, priv,
|
|
||||||
&dai_idx, link_idx++);
|
|
||||||
}
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return asoc_simple_card_parse_card_name(card, NULL);
|
return asoc_simple_card_parse_card_name(card, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void asoc_graph_get_dais_count(struct device *dev,
|
static int graph_count_noml(struct graph_priv *priv,
|
||||||
int *link_num,
|
struct device_node *cpu_ep,
|
||||||
int *dais_num,
|
struct device_node *codec_ep,
|
||||||
int *ccnf_num)
|
struct link_info *li)
|
||||||
{
|
{
|
||||||
struct of_phandle_iterator it;
|
struct device *dev = graph_priv_to_dev(priv);
|
||||||
struct device_node *node = dev->of_node;
|
|
||||||
struct device_node *cpu_port;
|
li->link += 1; /* 1xCPU-Codec */
|
||||||
struct device_node *cpu_ep;
|
li->dais += 2; /* 1xCPU + 1xCodec */
|
||||||
struct device_node *codec_ep;
|
|
||||||
struct device_node *codec_port;
|
dev_dbg(dev, "Count As Normal\n");
|
||||||
struct device_node *codec_port_old;
|
|
||||||
struct device_node *codec_port_old2;
|
return 0;
|
||||||
int rc;
|
}
|
||||||
|
|
||||||
|
static int graph_count_dpcm(struct graph_priv *priv,
|
||||||
|
struct device_node *cpu_ep,
|
||||||
|
struct device_node *codec_ep,
|
||||||
|
struct link_info *li,
|
||||||
|
int dup_codec)
|
||||||
|
{
|
||||||
|
struct device *dev = graph_priv_to_dev(priv);
|
||||||
|
|
||||||
|
li->link++; /* 1xCPU-dummy */
|
||||||
|
li->dais++; /* 1xCPU */
|
||||||
|
|
||||||
|
if (!dup_codec) {
|
||||||
|
li->link++; /* 1xdummy-Codec */
|
||||||
|
li->conf++; /* 1xdummy-Codec */
|
||||||
|
li->dais++; /* 1xCodec */
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_dbg(dev, "Count As DPCM\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void graph_get_dais_count(struct graph_priv *priv,
|
||||||
|
struct link_info *li)
|
||||||
|
{
|
||||||
|
struct device *dev = graph_priv_to_dev(priv);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* link_num : number of links.
|
* link_num : number of links.
|
||||||
@ -522,45 +609,26 @@ static void asoc_graph_get_dais_count(struct device *dev,
|
|||||||
* => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec
|
* => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec
|
||||||
* => 6 DAIs = 4xCPU + 2xCodec
|
* => 6 DAIs = 4xCPU + 2xCodec
|
||||||
* => 2 ccnf = 2xdummy-Codec
|
* => 2 ccnf = 2xdummy-Codec
|
||||||
|
*
|
||||||
|
* ex4)
|
||||||
|
* CPU0 --- Codec0 (convert-rate) link : 3
|
||||||
|
* CPU1 --- Codec1 dais : 4
|
||||||
|
* ccnf : 1
|
||||||
|
*
|
||||||
|
* => 3 links = 1xCPU-Codec + 1xCPU-dummy + 1xdummy-Codec
|
||||||
|
* => 4 DAIs = 2xCPU + 2xCodec
|
||||||
|
* => 1 ccnf = 1xdummy-Codec
|
||||||
*/
|
*/
|
||||||
codec_port_old = NULL;
|
graph_for_each_link(priv, li,
|
||||||
codec_port_old2 = NULL;
|
graph_count_noml,
|
||||||
of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
|
graph_count_dpcm);
|
||||||
cpu_port = it.node;
|
dev_dbg(dev, "link %d, dais %d, ccnf %d\n",
|
||||||
cpu_ep = NULL;
|
li->link, li->dais, li->conf);
|
||||||
while (1) {
|
|
||||||
cpu_ep = of_get_next_child(cpu_port, cpu_ep);
|
|
||||||
if (!cpu_ep)
|
|
||||||
break;
|
|
||||||
|
|
||||||
codec_ep = of_graph_get_remote_endpoint(cpu_ep);
|
|
||||||
codec_port = of_get_parent(codec_ep);
|
|
||||||
|
|
||||||
of_node_put(codec_ep);
|
|
||||||
of_node_put(codec_port);
|
|
||||||
|
|
||||||
(*link_num)++;
|
|
||||||
(*dais_num)++;
|
|
||||||
|
|
||||||
if (codec_port_old == codec_port) {
|
|
||||||
if (codec_port_old2 != codec_port_old) {
|
|
||||||
(*link_num)++;
|
|
||||||
(*ccnf_num)++;
|
|
||||||
}
|
|
||||||
|
|
||||||
codec_port_old2 = codec_port_old;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*dais_num)++;
|
|
||||||
codec_port_old = codec_port;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int asoc_graph_soc_card_probe(struct snd_soc_card *card)
|
static int graph_card_probe(struct snd_soc_card *card)
|
||||||
{
|
{
|
||||||
struct graph_card_data *priv = snd_soc_card_get_drvdata(card);
|
struct graph_priv *priv = snd_soc_card_get_drvdata(card);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = asoc_simple_card_init_hp(card, &priv->hp_jack, NULL);
|
ret = asoc_simple_card_init_hp(card, &priv->hp_jack, NULL);
|
||||||
@ -574,16 +642,16 @@ static int asoc_graph_soc_card_probe(struct snd_soc_card *card)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int asoc_graph_card_probe(struct platform_device *pdev)
|
static int graph_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct graph_card_data *priv;
|
struct graph_priv *priv;
|
||||||
struct snd_soc_dai_link *dai_link;
|
struct snd_soc_dai_link *dai_link;
|
||||||
struct graph_dai_props *dai_props;
|
struct graph_dai_props *dai_props;
|
||||||
struct asoc_simple_dai *dais;
|
struct asoc_simple_dai *dais;
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct snd_soc_card *card;
|
struct snd_soc_card *card;
|
||||||
struct snd_soc_codec_conf *cconf;
|
struct snd_soc_codec_conf *cconf;
|
||||||
int lnum = 0, dnum = 0, cnum = 0;
|
struct link_info li;
|
||||||
int ret, i;
|
int ret, i;
|
||||||
|
|
||||||
/* Allocate the private data and the DAI link array */
|
/* Allocate the private data and the DAI link array */
|
||||||
@ -591,14 +659,22 @@ static int asoc_graph_card_probe(struct platform_device *pdev)
|
|||||||
if (!priv)
|
if (!priv)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
asoc_graph_get_dais_count(dev, &lnum, &dnum, &cnum);
|
card = graph_priv_to_card(priv);
|
||||||
if (!lnum || !dnum)
|
card->owner = THIS_MODULE;
|
||||||
|
card->dev = dev;
|
||||||
|
card->dapm_widgets = graph_dapm_widgets;
|
||||||
|
card->num_dapm_widgets = ARRAY_SIZE(graph_dapm_widgets);
|
||||||
|
card->probe = graph_card_probe;
|
||||||
|
|
||||||
|
memset(&li, 0, sizeof(li));
|
||||||
|
graph_get_dais_count(priv, &li);
|
||||||
|
if (!li.link || !li.dais)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
dai_props = devm_kcalloc(dev, lnum, sizeof(*dai_props), GFP_KERNEL);
|
dai_props = devm_kcalloc(dev, li.link, sizeof(*dai_props), GFP_KERNEL);
|
||||||
dai_link = devm_kcalloc(dev, lnum, sizeof(*dai_link), GFP_KERNEL);
|
dai_link = devm_kcalloc(dev, li.link, sizeof(*dai_link), GFP_KERNEL);
|
||||||
dais = devm_kcalloc(dev, dnum, sizeof(*dais), GFP_KERNEL);
|
dais = devm_kcalloc(dev, li.dais, sizeof(*dais), GFP_KERNEL);
|
||||||
cconf = devm_kcalloc(dev, cnum, sizeof(*cconf), GFP_KERNEL);
|
cconf = devm_kcalloc(dev, li.conf, sizeof(*cconf), GFP_KERNEL);
|
||||||
if (!dai_props || !dai_link || !dais)
|
if (!dai_props || !dai_link || !dais)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
@ -608,7 +684,7 @@ static int asoc_graph_card_probe(struct platform_device *pdev)
|
|||||||
* see
|
* see
|
||||||
* soc-core.c :: snd_soc_init_multicodec()
|
* soc-core.c :: snd_soc_init_multicodec()
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < lnum; i++) {
|
for (i = 0; i < li.link; i++) {
|
||||||
dai_link[i].codecs = &dai_props[i].codecs;
|
dai_link[i].codecs = &dai_props[i].codecs;
|
||||||
dai_link[i].num_codecs = 1;
|
dai_link[i].num_codecs = 1;
|
||||||
dai_link[i].platform = &dai_props[i].platform;
|
dai_link[i].platform = &dai_props[i].platform;
|
||||||
@ -621,24 +697,17 @@ static int asoc_graph_card_probe(struct platform_device *pdev)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
priv->dai_props = dai_props;
|
priv->dai_props = dai_props;
|
||||||
priv->dai_link = dai_link;
|
priv->dai_link = dai_link;
|
||||||
priv->dais = dais;
|
priv->dais = dais;
|
||||||
priv->codec_conf = cconf;
|
priv->codec_conf = cconf;
|
||||||
|
|
||||||
/* Init snd_soc_card */
|
|
||||||
card = graph_priv_to_card(priv);
|
|
||||||
card->owner = THIS_MODULE;
|
|
||||||
card->dev = dev;
|
|
||||||
card->dai_link = dai_link;
|
card->dai_link = dai_link;
|
||||||
card->num_links = lnum;
|
card->num_links = li.link;
|
||||||
card->dapm_widgets = asoc_graph_card_dapm_widgets;
|
|
||||||
card->num_dapm_widgets = ARRAY_SIZE(asoc_graph_card_dapm_widgets);
|
|
||||||
card->probe = asoc_graph_soc_card_probe;
|
|
||||||
card->codec_conf = cconf;
|
card->codec_conf = cconf;
|
||||||
card->num_configs = cnum;
|
card->num_configs = li.conf;
|
||||||
|
|
||||||
ret = asoc_graph_card_parse_of(priv);
|
ret = graph_parse_of(priv);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (ret != -EPROBE_DEFER)
|
if (ret != -EPROBE_DEFER)
|
||||||
dev_err(dev, "parse error %d\n", ret);
|
dev_err(dev, "parse error %d\n", ret);
|
||||||
@ -658,30 +727,30 @@ err:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int asoc_graph_card_remove(struct platform_device *pdev)
|
static int graph_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct snd_soc_card *card = platform_get_drvdata(pdev);
|
struct snd_soc_card *card = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
return asoc_simple_card_clean_reference(card);
|
return asoc_simple_card_clean_reference(card);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct of_device_id asoc_graph_of_match[] = {
|
static const struct of_device_id graph_of_match[] = {
|
||||||
{ .compatible = "audio-graph-card", },
|
{ .compatible = "audio-graph-card", },
|
||||||
{ .compatible = "audio-graph-scu-card", },
|
{ .compatible = "audio-graph-scu-card", },
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, asoc_graph_of_match);
|
MODULE_DEVICE_TABLE(of, graph_of_match);
|
||||||
|
|
||||||
static struct platform_driver asoc_graph_card = {
|
static struct platform_driver graph_card = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "asoc-audio-graph-card",
|
.name = "asoc-audio-graph-card",
|
||||||
.pm = &snd_soc_pm_ops,
|
.pm = &snd_soc_pm_ops,
|
||||||
.of_match_table = asoc_graph_of_match,
|
.of_match_table = graph_of_match,
|
||||||
},
|
},
|
||||||
.probe = asoc_graph_card_probe,
|
.probe = graph_probe,
|
||||||
.remove = asoc_graph_card_remove,
|
.remove = graph_remove,
|
||||||
};
|
};
|
||||||
module_platform_driver(asoc_graph_card);
|
module_platform_driver(graph_card);
|
||||||
|
|
||||||
MODULE_ALIAS("platform:asoc-audio-graph-card");
|
MODULE_ALIAS("platform:asoc-audio-graph-card");
|
||||||
MODULE_LICENSE("GPL v2");
|
MODULE_LICENSE("GPL v2");
|
||||||
|
@ -1,501 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
|
||||||
//
|
|
||||||
// ASoC audio graph SCU sound card support
|
|
||||||
//
|
|
||||||
// Copyright (C) 2017 Renesas Solutions Corp.
|
|
||||||
// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
|
|
||||||
//
|
|
||||||
// based on
|
|
||||||
// ${LINUX}/sound/soc/generic/simple-scu-card.c
|
|
||||||
// ${LINUX}/sound/soc/generic/audio-graph-card.c
|
|
||||||
|
|
||||||
#include <linux/clk.h>
|
|
||||||
#include <linux/device.h>
|
|
||||||
#include <linux/gpio.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/of.h>
|
|
||||||
#include <linux/of_device.h>
|
|
||||||
#include <linux/of_gpio.h>
|
|
||||||
#include <linux/of_graph.h>
|
|
||||||
#include <linux/platform_device.h>
|
|
||||||
#include <linux/string.h>
|
|
||||||
#include <sound/jack.h>
|
|
||||||
#include <sound/simple_card_utils.h>
|
|
||||||
|
|
||||||
struct graph_card_data {
|
|
||||||
struct snd_soc_card snd_card;
|
|
||||||
struct graph_dai_props {
|
|
||||||
struct asoc_simple_dai *cpu_dai;
|
|
||||||
struct asoc_simple_dai *codec_dai;
|
|
||||||
struct snd_soc_dai_link_component codecs;
|
|
||||||
struct snd_soc_dai_link_component platform;
|
|
||||||
struct asoc_simple_card_data adata;
|
|
||||||
struct snd_soc_codec_conf *codec_conf;
|
|
||||||
} *dai_props;
|
|
||||||
struct snd_soc_dai_link *dai_link;
|
|
||||||
struct asoc_simple_dai *dais;
|
|
||||||
struct asoc_simple_card_data adata;
|
|
||||||
struct snd_soc_codec_conf *codec_conf;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define graph_priv_to_card(priv) (&(priv)->snd_card)
|
|
||||||
#define graph_priv_to_props(priv, i) ((priv)->dai_props + (i))
|
|
||||||
#define graph_priv_to_dev(priv) (graph_priv_to_card(priv)->dev)
|
|
||||||
#define graph_priv_to_link(priv, i) (graph_priv_to_card(priv)->dai_link + (i))
|
|
||||||
|
|
||||||
#define PREFIX "audio-graph-card,"
|
|
||||||
|
|
||||||
static int asoc_graph_card_startup(struct snd_pcm_substream *substream)
|
|
||||||
{
|
|
||||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
||||||
struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
|
||||||
struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
ret = asoc_simple_card_clk_enable(dai_props->cpu_dai);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = asoc_simple_card_clk_enable(dai_props->codec_dai);
|
|
||||||
if (ret)
|
|
||||||
asoc_simple_card_clk_disable(dai_props->cpu_dai);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream)
|
|
||||||
{
|
|
||||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
||||||
struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
|
||||||
struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
|
|
||||||
|
|
||||||
asoc_simple_card_clk_disable(dai_props->cpu_dai);
|
|
||||||
|
|
||||||
asoc_simple_card_clk_disable(dai_props->codec_dai);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct snd_soc_ops asoc_graph_card_ops = {
|
|
||||||
.startup = asoc_graph_card_startup,
|
|
||||||
.shutdown = asoc_graph_card_shutdown,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int asoc_graph_card_dai_init(struct snd_soc_pcm_runtime *rtd)
|
|
||||||
{
|
|
||||||
struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
|
||||||
struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
ret = asoc_simple_card_init_dai(rtd->codec_dai,
|
|
||||||
dai_props->codec_dai);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = asoc_simple_card_init_dai(rtd->cpu_dai,
|
|
||||||
dai_props->cpu_dai);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int asoc_graph_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
|
|
||||||
struct snd_pcm_hw_params *params)
|
|
||||||
{
|
|
||||||
struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
|
||||||
struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
|
|
||||||
|
|
||||||
asoc_simple_card_convert_fixup(&dai_props->adata, params);
|
|
||||||
|
|
||||||
/* overwrite by top level adata if exist */
|
|
||||||
asoc_simple_card_convert_fixup(&priv->adata, params);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int asoc_graph_card_dai_link_of(struct device_node *cpu_ep,
|
|
||||||
struct device_node *codec_ep,
|
|
||||||
struct graph_card_data *priv,
|
|
||||||
int *dai_idx, int link_idx,
|
|
||||||
int *conf_idx, int is_fe)
|
|
||||||
{
|
|
||||||
struct device *dev = graph_priv_to_dev(priv);
|
|
||||||
struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, link_idx);
|
|
||||||
struct graph_dai_props *dai_props = graph_priv_to_props(priv, link_idx);
|
|
||||||
struct snd_soc_card *card = graph_priv_to_card(priv);
|
|
||||||
struct device_node *ep = is_fe ? cpu_ep : codec_ep;
|
|
||||||
struct device_node *node = of_graph_get_port_parent(ep);
|
|
||||||
struct asoc_simple_dai *dai;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (is_fe) {
|
|
||||||
struct snd_soc_dai_link_component *codecs;
|
|
||||||
|
|
||||||
/* BE is dummy */
|
|
||||||
codecs = dai_link->codecs;
|
|
||||||
codecs->of_node = NULL;
|
|
||||||
codecs->dai_name = "snd-soc-dummy-dai";
|
|
||||||
codecs->name = "snd-soc-dummy";
|
|
||||||
|
|
||||||
/* FE settings */
|
|
||||||
dai_link->dynamic = 1;
|
|
||||||
dai_link->dpcm_merged_format = 1;
|
|
||||||
|
|
||||||
dai =
|
|
||||||
dai_props->cpu_dai = &priv->dais[(*dai_idx)++];
|
|
||||||
|
|
||||||
ret = asoc_simple_card_parse_graph_cpu(ep, dai_link);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = asoc_simple_card_parse_clk_cpu(dev, ep, dai_link, dai);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = asoc_simple_card_set_dailink_name(dev, dai_link,
|
|
||||||
"fe.%s",
|
|
||||||
dai_link->cpu_dai_name);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
/* card->num_links includes Codec */
|
|
||||||
asoc_simple_card_canonicalize_cpu(dai_link,
|
|
||||||
of_graph_get_endpoint_count(dai_link->cpu_of_node) == 1);
|
|
||||||
} else {
|
|
||||||
struct snd_soc_codec_conf *cconf;
|
|
||||||
|
|
||||||
/* FE is dummy */
|
|
||||||
dai_link->cpu_of_node = NULL;
|
|
||||||
dai_link->cpu_dai_name = "snd-soc-dummy-dai";
|
|
||||||
dai_link->cpu_name = "snd-soc-dummy";
|
|
||||||
|
|
||||||
/* BE settings */
|
|
||||||
dai_link->no_pcm = 1;
|
|
||||||
dai_link->be_hw_params_fixup = asoc_graph_card_be_hw_params_fixup;
|
|
||||||
|
|
||||||
dai =
|
|
||||||
dai_props->codec_dai = &priv->dais[(*dai_idx)++];
|
|
||||||
|
|
||||||
cconf =
|
|
||||||
dai_props->codec_conf = &priv->codec_conf[(*conf_idx)++];
|
|
||||||
|
|
||||||
ret = asoc_simple_card_parse_graph_codec(ep, dai_link);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = asoc_simple_card_parse_clk_codec(dev, ep, dai_link, dai);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = asoc_simple_card_set_dailink_name(dev, dai_link,
|
|
||||||
"be.%s",
|
|
||||||
dai_link->codecs->dai_name);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
/* check "prefix" from top node */
|
|
||||||
snd_soc_of_parse_audio_prefix(card, cconf,
|
|
||||||
dai_link->codecs->of_node,
|
|
||||||
"prefix");
|
|
||||||
/* check "prefix" from each node if top doesn't have */
|
|
||||||
if (!cconf->of_node)
|
|
||||||
snd_soc_of_parse_node_prefix(node, cconf,
|
|
||||||
dai_link->codecs->of_node,
|
|
||||||
PREFIX "prefix");
|
|
||||||
}
|
|
||||||
|
|
||||||
asoc_simple_card_parse_convert(dev, node, PREFIX, &dai_props->adata);
|
|
||||||
|
|
||||||
ret = asoc_simple_card_of_parse_tdm(ep, dai);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = asoc_simple_card_canonicalize_dailink(dai_link);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep,
|
|
||||||
NULL, &dai_link->dai_fmt);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
dai_link->dpcm_playback = 1;
|
|
||||||
dai_link->dpcm_capture = 1;
|
|
||||||
dai_link->ops = &asoc_graph_card_ops;
|
|
||||||
dai_link->init = asoc_graph_card_dai_init;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int asoc_graph_card_parse_of(struct graph_card_data *priv)
|
|
||||||
{
|
|
||||||
struct of_phandle_iterator it;
|
|
||||||
struct device *dev = graph_priv_to_dev(priv);
|
|
||||||
struct snd_soc_card *card = graph_priv_to_card(priv);
|
|
||||||
struct device_node *node = dev->of_node;
|
|
||||||
struct device_node *cpu_port;
|
|
||||||
struct device_node *cpu_ep;
|
|
||||||
struct device_node *codec_ep;
|
|
||||||
struct device_node *codec_port;
|
|
||||||
struct device_node *codec_port_old;
|
|
||||||
int dai_idx, link_idx, conf_idx, ret;
|
|
||||||
int rc, codec;
|
|
||||||
|
|
||||||
if (!node)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* we need to consider "widgets", "mclk-fs" around here
|
|
||||||
* see simple-card
|
|
||||||
*/
|
|
||||||
|
|
||||||
ret = asoc_simple_card_of_parse_routing(card, NULL);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
asoc_simple_card_parse_convert(dev, node, NULL, &priv->adata);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* it supports multi CPU, single CODEC only here
|
|
||||||
* see asoc_graph_get_dais_count
|
|
||||||
*/
|
|
||||||
|
|
||||||
link_idx = 0;
|
|
||||||
dai_idx = 0;
|
|
||||||
conf_idx = 0;
|
|
||||||
codec_port_old = NULL;
|
|
||||||
for (codec = 0; codec < 2; codec++) {
|
|
||||||
/*
|
|
||||||
* To listup valid sounds continuously,
|
|
||||||
* detect all CPU-dummy first, and
|
|
||||||
* detect all dummy-Codec second
|
|
||||||
*/
|
|
||||||
of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
|
|
||||||
cpu_port = it.node;
|
|
||||||
cpu_ep = of_get_next_child(cpu_port, NULL);
|
|
||||||
codec_ep = of_graph_get_remote_endpoint(cpu_ep);
|
|
||||||
codec_port = of_graph_get_port_parent(codec_ep);
|
|
||||||
|
|
||||||
of_node_put(cpu_ep);
|
|
||||||
of_node_put(codec_ep);
|
|
||||||
of_node_put(cpu_port);
|
|
||||||
of_node_put(codec_port);
|
|
||||||
it.node = NULL;
|
|
||||||
|
|
||||||
if (codec) {
|
|
||||||
if (codec_port_old == codec_port)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
codec_port_old = codec_port;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = asoc_graph_card_dai_link_of(cpu_ep, codec_ep,
|
|
||||||
priv, &dai_idx,
|
|
||||||
link_idx++, &conf_idx,
|
|
||||||
!codec);
|
|
||||||
if (ret < 0)
|
|
||||||
goto parse_of_err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = asoc_simple_card_parse_card_name(card, NULL);
|
|
||||||
if (ret)
|
|
||||||
goto parse_of_err;
|
|
||||||
|
|
||||||
if ((card->num_links != link_idx) ||
|
|
||||||
(card->num_configs != conf_idx)) {
|
|
||||||
dev_err(dev, "dai_link or codec_config wrong (%d/%d, %d/%d)\n",
|
|
||||||
card->num_links, link_idx, card->num_configs, conf_idx);
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto parse_of_err;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = 0;
|
|
||||||
|
|
||||||
parse_of_err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void asoc_graph_get_dais_count(struct device *dev,
|
|
||||||
int *link_num,
|
|
||||||
int *dais_num,
|
|
||||||
int *ccnf_num)
|
|
||||||
{
|
|
||||||
struct of_phandle_iterator it;
|
|
||||||
struct device_node *node = dev->of_node;
|
|
||||||
struct device_node *cpu_port;
|
|
||||||
struct device_node *cpu_ep;
|
|
||||||
struct device_node *codec_ep;
|
|
||||||
struct device_node *codec_port;
|
|
||||||
struct device_node *codec_port_old;
|
|
||||||
struct device_node *codec_port_old2;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* link_num : number of links.
|
|
||||||
* CPU-Codec / CPU-dummy / dummy-Codec
|
|
||||||
* dais_num : number of DAIs
|
|
||||||
* ccnf_num : number of codec_conf
|
|
||||||
* same number for dummy-Codec
|
|
||||||
*
|
|
||||||
* ex1)
|
|
||||||
* CPU0 --- Codec0 link : 5
|
|
||||||
* CPU1 --- Codec1 dais : 7
|
|
||||||
* CPU2 -/ ccnf : 1
|
|
||||||
* CPU3 --- Codec2
|
|
||||||
*
|
|
||||||
* => 5 links = 2xCPU-Codec + 2xCPU-dummy + 1xdummy-Codec
|
|
||||||
* => 7 DAIs = 4xCPU + 3xCodec
|
|
||||||
* => 1 ccnf = 1xdummy-Codec
|
|
||||||
*
|
|
||||||
* ex2)
|
|
||||||
* CPU0 --- Codec0 link : 5
|
|
||||||
* CPU1 --- Codec1 dais : 6
|
|
||||||
* CPU2 -/ ccnf : 1
|
|
||||||
* CPU3 -/
|
|
||||||
*
|
|
||||||
* => 5 links = 1xCPU-Codec + 3xCPU-dummy + 1xdummy-Codec
|
|
||||||
* => 6 DAIs = 4xCPU + 2xCodec
|
|
||||||
* => 1 ccnf = 1xdummy-Codec
|
|
||||||
*
|
|
||||||
* ex3)
|
|
||||||
* CPU0 --- Codec0 link : 6
|
|
||||||
* CPU1 -/ dais : 6
|
|
||||||
* CPU2 --- Codec1 ccnf : 2
|
|
||||||
* CPU3 -/
|
|
||||||
*
|
|
||||||
* => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec
|
|
||||||
* => 6 DAIs = 4xCPU + 2xCodec
|
|
||||||
* => 2 ccnf = 2xdummy-Codec
|
|
||||||
*/
|
|
||||||
codec_port_old = NULL;
|
|
||||||
codec_port_old2 = NULL;
|
|
||||||
of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
|
|
||||||
cpu_port = it.node;
|
|
||||||
cpu_ep = of_get_next_child(cpu_port, NULL);
|
|
||||||
codec_ep = of_graph_get_remote_endpoint(cpu_ep);
|
|
||||||
codec_port = of_graph_get_port_parent(codec_ep);
|
|
||||||
|
|
||||||
of_node_put(cpu_ep);
|
|
||||||
of_node_put(codec_ep);
|
|
||||||
of_node_put(codec_port);
|
|
||||||
|
|
||||||
(*link_num)++;
|
|
||||||
(*dais_num)++;
|
|
||||||
|
|
||||||
if (codec_port_old == codec_port) {
|
|
||||||
if (codec_port_old2 != codec_port_old) {
|
|
||||||
(*link_num)++;
|
|
||||||
(*ccnf_num)++;
|
|
||||||
}
|
|
||||||
|
|
||||||
codec_port_old2 = codec_port_old;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*dais_num)++;
|
|
||||||
codec_port_old = codec_port;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int asoc_graph_card_probe(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
struct graph_card_data *priv;
|
|
||||||
struct snd_soc_dai_link *dai_link;
|
|
||||||
struct graph_dai_props *dai_props;
|
|
||||||
struct asoc_simple_dai *dais;
|
|
||||||
struct device *dev = &pdev->dev;
|
|
||||||
struct snd_soc_card *card;
|
|
||||||
struct snd_soc_codec_conf *cconf;
|
|
||||||
int lnum = 0, dnum = 0, cnum = 0;
|
|
||||||
int ret, i;
|
|
||||||
|
|
||||||
/* Allocate the private data and the DAI link array */
|
|
||||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
|
||||||
if (!priv)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
asoc_graph_get_dais_count(dev, &lnum, &dnum, &cnum);
|
|
||||||
if (!lnum || !dnum)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
dai_props = devm_kcalloc(dev, lnum, sizeof(*dai_props), GFP_KERNEL);
|
|
||||||
dai_link = devm_kcalloc(dev, lnum, sizeof(*dai_link), GFP_KERNEL);
|
|
||||||
dais = devm_kcalloc(dev, dnum, sizeof(*dais), GFP_KERNEL);
|
|
||||||
cconf = devm_kcalloc(dev, cnum, sizeof(*cconf), GFP_KERNEL);
|
|
||||||
if (!dai_props || !dai_link || !dais)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Use snd_soc_dai_link_component instead of legacy style
|
|
||||||
* It is codec only. but cpu/platform will be supported in the future.
|
|
||||||
* see
|
|
||||||
* soc-core.c :: snd_soc_init_multicodec()
|
|
||||||
*/
|
|
||||||
for (i = 0; i < lnum; i++) {
|
|
||||||
dai_link[i].codecs = &dai_props[i].codecs;
|
|
||||||
dai_link[i].num_codecs = 1;
|
|
||||||
dai_link[i].platform = &dai_props[i].platform;
|
|
||||||
}
|
|
||||||
|
|
||||||
priv->dai_props = dai_props;
|
|
||||||
priv->dai_link = dai_link;
|
|
||||||
priv->dais = dais;
|
|
||||||
priv->codec_conf = cconf;
|
|
||||||
|
|
||||||
/* Init snd_soc_card */
|
|
||||||
card = graph_priv_to_card(priv);
|
|
||||||
card->owner = THIS_MODULE;
|
|
||||||
card->dev = dev;
|
|
||||||
card->dai_link = priv->dai_link;
|
|
||||||
card->num_links = lnum;
|
|
||||||
card->codec_conf = cconf;
|
|
||||||
card->num_configs = cnum;
|
|
||||||
|
|
||||||
ret = asoc_graph_card_parse_of(priv);
|
|
||||||
if (ret < 0) {
|
|
||||||
if (ret != -EPROBE_DEFER)
|
|
||||||
dev_err(dev, "parse error %d\n", ret);
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
snd_soc_card_set_drvdata(card, priv);
|
|
||||||
|
|
||||||
ret = devm_snd_soc_register_card(dev, card);
|
|
||||||
if (ret < 0)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
err:
|
|
||||||
asoc_simple_card_clean_reference(card);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int asoc_graph_card_remove(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
struct snd_soc_card *card = platform_get_drvdata(pdev);
|
|
||||||
|
|
||||||
return asoc_simple_card_clean_reference(card);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct of_device_id asoc_graph_of_match[] = {
|
|
||||||
{ .compatible = "audio-graph-scu-card", },
|
|
||||||
{},
|
|
||||||
};
|
|
||||||
MODULE_DEVICE_TABLE(of, asoc_graph_of_match);
|
|
||||||
|
|
||||||
static struct platform_driver asoc_graph_card = {
|
|
||||||
.driver = {
|
|
||||||
.name = "asoc-audio-graph-scu-card",
|
|
||||||
.pm = &snd_soc_pm_ops,
|
|
||||||
.of_match_table = asoc_graph_of_match,
|
|
||||||
},
|
|
||||||
.probe = asoc_graph_card_probe,
|
|
||||||
.remove = asoc_graph_card_remove,
|
|
||||||
};
|
|
||||||
module_platform_driver(asoc_graph_card);
|
|
||||||
|
|
||||||
MODULE_ALIAS("platform:asoc-audio-graph-scu-card");
|
|
||||||
MODULE_LICENSE("GPL v2");
|
|
||||||
MODULE_DESCRIPTION("ASoC Audio Graph SCU Sound Card");
|
|
||||||
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
|
|
@ -283,12 +283,20 @@ static int asoc_simple_card_get_dai_id(struct device_node *ep)
|
|||||||
/* use endpoint/port reg if exist */
|
/* use endpoint/port reg if exist */
|
||||||
ret = of_graph_parse_endpoint(ep, &info);
|
ret = of_graph_parse_endpoint(ep, &info);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
if (info.id)
|
/*
|
||||||
|
* Because it will count port/endpoint if it doesn't have "reg".
|
||||||
|
* But, we can't judge whether it has "no reg", or "reg = <0>"
|
||||||
|
* only of_graph_parse_endpoint().
|
||||||
|
* We need to check "reg" property
|
||||||
|
*/
|
||||||
|
if (of_get_property(ep, "reg", NULL))
|
||||||
return info.id;
|
return info.id;
|
||||||
if (info.port)
|
|
||||||
|
node = of_get_parent(ep);
|
||||||
|
of_node_put(node);
|
||||||
|
if (of_get_property(node, "reg", NULL))
|
||||||
return info.port;
|
return info.port;
|
||||||
}
|
}
|
||||||
|
|
||||||
node = of_graph_get_port_parent(ep);
|
node = of_graph_get_port_parent(ep);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
#include <sound/soc-dai.h>
|
#include <sound/soc-dai.h>
|
||||||
#include <sound/soc.h>
|
#include <sound/soc.h>
|
||||||
|
|
||||||
struct simple_card_data {
|
struct simple_priv {
|
||||||
struct snd_soc_card snd_card;
|
struct snd_soc_card snd_card;
|
||||||
struct simple_dai_props {
|
struct simple_dai_props {
|
||||||
struct asoc_simple_dai *cpu_dai;
|
struct asoc_simple_dai *cpu_dai;
|
||||||
@ -33,6 +33,13 @@ struct simple_card_data {
|
|||||||
struct snd_soc_codec_conf *codec_conf;
|
struct snd_soc_codec_conf *codec_conf;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct link_info {
|
||||||
|
int dais; /* number of dai */
|
||||||
|
int link; /* number of link */
|
||||||
|
int conf; /* number of codec_conf */
|
||||||
|
int cpu; /* turn for CPU / Codec */
|
||||||
|
};
|
||||||
|
|
||||||
#define simple_priv_to_card(priv) (&(priv)->snd_card)
|
#define simple_priv_to_card(priv) (&(priv)->snd_card)
|
||||||
#define simple_priv_to_props(priv, i) ((priv)->dai_props + (i))
|
#define simple_priv_to_props(priv, i) ((priv)->dai_props + (i))
|
||||||
#define simple_priv_to_dev(priv) (simple_priv_to_card(priv)->dev)
|
#define simple_priv_to_dev(priv) (simple_priv_to_card(priv)->dev)
|
||||||
@ -42,10 +49,10 @@ struct simple_card_data {
|
|||||||
#define CELL "#sound-dai-cells"
|
#define CELL "#sound-dai-cells"
|
||||||
#define PREFIX "simple-audio-card,"
|
#define PREFIX "simple-audio-card,"
|
||||||
|
|
||||||
static int asoc_simple_card_startup(struct snd_pcm_substream *substream)
|
static int simple_startup(struct snd_pcm_substream *substream)
|
||||||
{
|
{
|
||||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||||
struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
struct simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||||
struct simple_dai_props *dai_props =
|
struct simple_dai_props *dai_props =
|
||||||
simple_priv_to_props(priv, rtd->num);
|
simple_priv_to_props(priv, rtd->num);
|
||||||
int ret;
|
int ret;
|
||||||
@ -61,10 +68,10 @@ static int asoc_simple_card_startup(struct snd_pcm_substream *substream)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream)
|
static void simple_shutdown(struct snd_pcm_substream *substream)
|
||||||
{
|
{
|
||||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||||
struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
struct simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||||
struct simple_dai_props *dai_props =
|
struct simple_dai_props *dai_props =
|
||||||
simple_priv_to_props(priv, rtd->num);
|
simple_priv_to_props(priv, rtd->num);
|
||||||
|
|
||||||
@ -73,8 +80,8 @@ static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream)
|
|||||||
asoc_simple_card_clk_disable(dai_props->codec_dai);
|
asoc_simple_card_clk_disable(dai_props->codec_dai);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int asoc_simple_set_clk_rate(struct asoc_simple_dai *simple_dai,
|
static int simple_set_clk_rate(struct asoc_simple_dai *simple_dai,
|
||||||
unsigned long rate)
|
unsigned long rate)
|
||||||
{
|
{
|
||||||
if (!simple_dai)
|
if (!simple_dai)
|
||||||
return 0;
|
return 0;
|
||||||
@ -88,13 +95,13 @@ static int asoc_simple_set_clk_rate(struct asoc_simple_dai *simple_dai,
|
|||||||
return clk_set_rate(simple_dai->clk, rate);
|
return clk_set_rate(simple_dai->clk, rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream,
|
static int simple_hw_params(struct snd_pcm_substream *substream,
|
||||||
struct snd_pcm_hw_params *params)
|
struct snd_pcm_hw_params *params)
|
||||||
{
|
{
|
||||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||||
struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
struct simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||||
struct simple_dai_props *dai_props =
|
struct simple_dai_props *dai_props =
|
||||||
simple_priv_to_props(priv, rtd->num);
|
simple_priv_to_props(priv, rtd->num);
|
||||||
unsigned int mclk, mclk_fs = 0;
|
unsigned int mclk, mclk_fs = 0;
|
||||||
@ -106,11 +113,11 @@ static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream,
|
|||||||
if (mclk_fs) {
|
if (mclk_fs) {
|
||||||
mclk = params_rate(params) * mclk_fs;
|
mclk = params_rate(params) * mclk_fs;
|
||||||
|
|
||||||
ret = asoc_simple_set_clk_rate(dai_props->codec_dai, mclk);
|
ret = simple_set_clk_rate(dai_props->codec_dai, mclk);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = asoc_simple_set_clk_rate(dai_props->cpu_dai, mclk);
|
ret = simple_set_clk_rate(dai_props->cpu_dai, mclk);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@ -129,15 +136,15 @@ err:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct snd_soc_ops asoc_simple_card_ops = {
|
static const struct snd_soc_ops simple_ops = {
|
||||||
.startup = asoc_simple_card_startup,
|
.startup = simple_startup,
|
||||||
.shutdown = asoc_simple_card_shutdown,
|
.shutdown = simple_shutdown,
|
||||||
.hw_params = asoc_simple_card_hw_params,
|
.hw_params = simple_hw_params,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
|
static int simple_dai_init(struct snd_soc_pcm_runtime *rtd)
|
||||||
{
|
{
|
||||||
struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
struct simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||||
struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num);
|
struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@ -154,10 +161,10 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int asoc_simple_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
|
static int simple_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||||
struct snd_pcm_hw_params *params)
|
struct snd_pcm_hw_params *params)
|
||||||
{
|
{
|
||||||
struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
struct simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||||
struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num);
|
struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num);
|
||||||
|
|
||||||
asoc_simple_card_convert_fixup(&dai_props->adata, params);
|
asoc_simple_card_convert_fixup(&dai_props->adata, params);
|
||||||
@ -165,30 +172,58 @@ static int asoc_simple_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int asoc_simple_card_dai_link_of_dpcm(struct device_node *top,
|
static void simple_get_conversion(struct device *dev,
|
||||||
struct device_node *node,
|
struct device_node *np,
|
||||||
struct device_node *np,
|
struct asoc_simple_card_data *adata)
|
||||||
struct device_node *codec,
|
{
|
||||||
struct simple_card_data *priv,
|
struct device_node *top = dev->of_node;
|
||||||
int *dai_idx, int link_idx,
|
struct device_node *node = of_get_parent(np);
|
||||||
int *conf_idx, int is_fe,
|
|
||||||
bool is_top_level_node)
|
asoc_simple_card_parse_convert(dev, top, PREFIX, adata);
|
||||||
|
asoc_simple_card_parse_convert(dev, node, PREFIX, adata);
|
||||||
|
asoc_simple_card_parse_convert(dev, node, NULL, adata);
|
||||||
|
asoc_simple_card_parse_convert(dev, np, NULL, adata);
|
||||||
|
|
||||||
|
of_node_put(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int simple_dai_link_of_dpcm(struct simple_priv *priv,
|
||||||
|
struct device_node *np,
|
||||||
|
struct device_node *codec,
|
||||||
|
struct link_info *li,
|
||||||
|
bool is_top)
|
||||||
{
|
{
|
||||||
struct device *dev = simple_priv_to_dev(priv);
|
struct device *dev = simple_priv_to_dev(priv);
|
||||||
struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, link_idx);
|
struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
|
||||||
struct simple_dai_props *dai_props = simple_priv_to_props(priv, link_idx);
|
struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
|
||||||
struct asoc_simple_dai *dai;
|
struct asoc_simple_dai *dai;
|
||||||
struct snd_soc_dai_link_component *codecs = dai_link->codecs;
|
struct snd_soc_dai_link_component *codecs = dai_link->codecs;
|
||||||
|
struct device_node *top = dev->of_node;
|
||||||
|
struct device_node *node = of_get_parent(np);
|
||||||
char prop[128];
|
char prop[128];
|
||||||
char *prefix = "";
|
char *prefix = "";
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* |CPU |Codec : turn
|
||||||
|
* CPU |Pass |return
|
||||||
|
* Codec |return|Pass
|
||||||
|
* np
|
||||||
|
*/
|
||||||
|
if (li->cpu == (np == codec))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
dev_dbg(dev, "link_of DPCM (%pOF)\n", np);
|
||||||
|
|
||||||
|
li->link++;
|
||||||
|
|
||||||
|
of_node_put(node);
|
||||||
|
|
||||||
/* For single DAI link & old style of DT node */
|
/* For single DAI link & old style of DT node */
|
||||||
if (is_top_level_node)
|
if (is_top)
|
||||||
prefix = PREFIX;
|
prefix = PREFIX;
|
||||||
|
|
||||||
if (is_fe) {
|
if (li->cpu) {
|
||||||
int is_single_links = 0;
|
int is_single_links = 0;
|
||||||
|
|
||||||
/* BE is dummy */
|
/* BE is dummy */
|
||||||
@ -201,7 +236,7 @@ static int asoc_simple_card_dai_link_of_dpcm(struct device_node *top,
|
|||||||
dai_link->dpcm_merged_format = 1;
|
dai_link->dpcm_merged_format = 1;
|
||||||
|
|
||||||
dai =
|
dai =
|
||||||
dai_props->cpu_dai = &priv->dais[(*dai_idx)++];
|
dai_props->cpu_dai = &priv->dais[li->dais++];
|
||||||
|
|
||||||
ret = asoc_simple_card_parse_cpu(np, dai_link, DAI, CELL,
|
ret = asoc_simple_card_parse_cpu(np, dai_link, DAI, CELL,
|
||||||
&is_single_links);
|
&is_single_links);
|
||||||
@ -229,13 +264,13 @@ static int asoc_simple_card_dai_link_of_dpcm(struct device_node *top,
|
|||||||
|
|
||||||
/* BE settings */
|
/* BE settings */
|
||||||
dai_link->no_pcm = 1;
|
dai_link->no_pcm = 1;
|
||||||
dai_link->be_hw_params_fixup = asoc_simple_card_be_hw_params_fixup;
|
dai_link->be_hw_params_fixup = simple_be_hw_params_fixup;
|
||||||
|
|
||||||
dai =
|
dai =
|
||||||
dai_props->codec_dai = &priv->dais[(*dai_idx)++];
|
dai_props->codec_dai = &priv->dais[li->dais++];
|
||||||
|
|
||||||
cconf =
|
cconf =
|
||||||
dai_props->codec_conf = &priv->codec_conf[(*conf_idx)++];
|
dai_props->codec_conf = &priv->codec_conf[li->conf++];
|
||||||
|
|
||||||
ret = asoc_simple_card_parse_codec(np, dai_link, DAI, CELL);
|
ret = asoc_simple_card_parse_codec(np, dai_link, DAI, CELL);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
@ -260,9 +295,7 @@ static int asoc_simple_card_dai_link_of_dpcm(struct device_node *top,
|
|||||||
"prefix");
|
"prefix");
|
||||||
}
|
}
|
||||||
|
|
||||||
asoc_simple_card_parse_convert(dev, top, PREFIX, &dai_props->adata);
|
simple_get_conversion(dev, np, &dai_props->adata);
|
||||||
asoc_simple_card_parse_convert(dev, node, prefix, &dai_props->adata);
|
|
||||||
asoc_simple_card_parse_convert(dev, np, NULL, &dai_props->adata);
|
|
||||||
|
|
||||||
ret = asoc_simple_card_of_parse_tdm(np, dai);
|
ret = asoc_simple_card_of_parse_tdm(np, dai);
|
||||||
if (ret)
|
if (ret)
|
||||||
@ -284,59 +317,57 @@ static int asoc_simple_card_dai_link_of_dpcm(struct device_node *top,
|
|||||||
|
|
||||||
dai_link->dpcm_playback = 1;
|
dai_link->dpcm_playback = 1;
|
||||||
dai_link->dpcm_capture = 1;
|
dai_link->dpcm_capture = 1;
|
||||||
dai_link->ops = &asoc_simple_card_ops;
|
dai_link->ops = &simple_ops;
|
||||||
dai_link->init = asoc_simple_card_dai_init;
|
dai_link->init = simple_dai_init;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int asoc_simple_card_dai_link_of(struct device_node *top,
|
static int simple_dai_link_of(struct simple_priv *priv,
|
||||||
struct device_node *node,
|
struct device_node *np,
|
||||||
struct simple_card_data *priv,
|
struct device_node *codec,
|
||||||
int *dai_idx, int link_idx,
|
struct link_info *li,
|
||||||
bool is_top_level_node)
|
bool is_top)
|
||||||
{
|
{
|
||||||
struct device *dev = simple_priv_to_dev(priv);
|
struct device *dev = simple_priv_to_dev(priv);
|
||||||
struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, link_idx);
|
struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
|
||||||
struct simple_dai_props *dai_props = simple_priv_to_props(priv, link_idx);
|
struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
|
||||||
struct asoc_simple_dai *cpu_dai;
|
struct asoc_simple_dai *cpu_dai;
|
||||||
struct asoc_simple_dai *codec_dai;
|
struct asoc_simple_dai *codec_dai;
|
||||||
|
struct device_node *top = dev->of_node;
|
||||||
struct device_node *cpu = NULL;
|
struct device_node *cpu = NULL;
|
||||||
|
struct device_node *node = NULL;
|
||||||
struct device_node *plat = NULL;
|
struct device_node *plat = NULL;
|
||||||
struct device_node *codec = NULL;
|
|
||||||
char prop[128];
|
char prop[128];
|
||||||
char *prefix = "";
|
char *prefix = "";
|
||||||
int ret, single_cpu;
|
int ret, single_cpu;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* |CPU |Codec : turn
|
||||||
|
* CPU |Pass |return
|
||||||
|
* Codec |return|return
|
||||||
|
* np
|
||||||
|
*/
|
||||||
|
if (!li->cpu || np == codec)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
cpu = np;
|
||||||
|
node = of_get_parent(np);
|
||||||
|
li->link++;
|
||||||
|
|
||||||
|
dev_dbg(dev, "link_of (%pOF)\n", node);
|
||||||
|
|
||||||
/* For single DAI link & old style of DT node */
|
/* For single DAI link & old style of DT node */
|
||||||
if (is_top_level_node)
|
if (is_top)
|
||||||
prefix = PREFIX;
|
prefix = PREFIX;
|
||||||
|
|
||||||
snprintf(prop, sizeof(prop), "%scpu", prefix);
|
|
||||||
cpu = of_get_child_by_name(node, prop);
|
|
||||||
|
|
||||||
if (!cpu) {
|
|
||||||
ret = -EINVAL;
|
|
||||||
dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
|
|
||||||
goto dai_link_of_err;
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(prop, sizeof(prop), "%splat", prefix);
|
snprintf(prop, sizeof(prop), "%splat", prefix);
|
||||||
plat = of_get_child_by_name(node, prop);
|
plat = of_get_child_by_name(node, prop);
|
||||||
|
|
||||||
snprintf(prop, sizeof(prop), "%scodec", prefix);
|
|
||||||
codec = of_get_child_by_name(node, prop);
|
|
||||||
|
|
||||||
if (!codec) {
|
|
||||||
ret = -EINVAL;
|
|
||||||
dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
|
|
||||||
goto dai_link_of_err;
|
|
||||||
}
|
|
||||||
|
|
||||||
cpu_dai =
|
cpu_dai =
|
||||||
dai_props->cpu_dai = &priv->dais[(*dai_idx)++];
|
dai_props->cpu_dai = &priv->dais[li->dais++];
|
||||||
codec_dai =
|
codec_dai =
|
||||||
dai_props->codec_dai = &priv->dais[(*dai_idx)++];
|
dai_props->codec_dai = &priv->dais[li->dais++];
|
||||||
|
|
||||||
ret = asoc_simple_card_parse_daifmt(dev, node, codec,
|
ret = asoc_simple_card_parse_daifmt(dev, node, codec,
|
||||||
prefix, &dai_link->dai_fmt);
|
prefix, &dai_link->dai_fmt);
|
||||||
@ -389,20 +420,87 @@ static int asoc_simple_card_dai_link_of(struct device_node *top,
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto dai_link_of_err;
|
goto dai_link_of_err;
|
||||||
|
|
||||||
dai_link->ops = &asoc_simple_card_ops;
|
dai_link->ops = &simple_ops;
|
||||||
dai_link->init = asoc_simple_card_dai_init;
|
dai_link->init = simple_dai_init;
|
||||||
|
|
||||||
asoc_simple_card_canonicalize_cpu(dai_link, single_cpu);
|
asoc_simple_card_canonicalize_cpu(dai_link, single_cpu);
|
||||||
|
|
||||||
dai_link_of_err:
|
dai_link_of_err:
|
||||||
of_node_put(cpu);
|
of_node_put(node);
|
||||||
of_node_put(codec);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int asoc_simple_card_parse_aux_devs(struct device_node *node,
|
static int simple_for_each_link(struct simple_priv *priv,
|
||||||
struct simple_card_data *priv)
|
struct link_info *li,
|
||||||
|
int (*func_noml)(struct simple_priv *priv,
|
||||||
|
struct device_node *np,
|
||||||
|
struct device_node *codec,
|
||||||
|
struct link_info *li, bool is_top),
|
||||||
|
int (*func_dpcm)(struct simple_priv *priv,
|
||||||
|
struct device_node *np,
|
||||||
|
struct device_node *codec,
|
||||||
|
struct link_info *li, bool is_top))
|
||||||
|
{
|
||||||
|
struct device *dev = simple_priv_to_dev(priv);
|
||||||
|
struct device_node *top = dev->of_node;
|
||||||
|
struct device_node *node;
|
||||||
|
bool is_top = 0;
|
||||||
|
|
||||||
|
/* Check if it has dai-link */
|
||||||
|
node = of_get_child_by_name(top, PREFIX "dai-link");
|
||||||
|
if (!node) {
|
||||||
|
node = top;
|
||||||
|
is_top = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* loop for all dai-link */
|
||||||
|
do {
|
||||||
|
struct asoc_simple_card_data adata;
|
||||||
|
struct device_node *codec;
|
||||||
|
struct device_node *np;
|
||||||
|
int num = of_get_child_count(node);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* get codec */
|
||||||
|
codec = of_get_child_by_name(node, is_top ?
|
||||||
|
PREFIX "codec" : "codec");
|
||||||
|
if (!codec)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
of_node_put(codec);
|
||||||
|
|
||||||
|
/* get convert-xxx property */
|
||||||
|
memset(&adata, 0, sizeof(adata));
|
||||||
|
for_each_child_of_node(node, np)
|
||||||
|
simple_get_conversion(dev, np, &adata);
|
||||||
|
|
||||||
|
/* loop for all CPU/Codec node */
|
||||||
|
for_each_child_of_node(node, np) {
|
||||||
|
/*
|
||||||
|
* It is DPCM
|
||||||
|
* if it has many CPUs,
|
||||||
|
* or has convert-xxx property
|
||||||
|
*/
|
||||||
|
if (num > 2 ||
|
||||||
|
adata.convert_rate || adata.convert_channels)
|
||||||
|
ret = func_dpcm(priv, np, codec, li, is_top);
|
||||||
|
/* else normal sound */
|
||||||
|
else
|
||||||
|
ret = func_noml(priv, np, codec, li, is_top);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
node = of_get_next_child(top, node);
|
||||||
|
} while (!is_top && node);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int simple_parse_aux_devs(struct device_node *node,
|
||||||
|
struct simple_priv *priv)
|
||||||
{
|
{
|
||||||
struct device *dev = simple_priv_to_dev(priv);
|
struct device *dev = simple_priv_to_dev(priv);
|
||||||
struct device_node *aux_node;
|
struct device_node *aux_node;
|
||||||
@ -432,17 +530,13 @@ static int asoc_simple_card_parse_aux_devs(struct device_node *node,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int asoc_simple_card_parse_of(struct simple_card_data *priv)
|
static int simple_parse_of(struct simple_priv *priv)
|
||||||
{
|
{
|
||||||
struct device *dev = simple_priv_to_dev(priv);
|
struct device *dev = simple_priv_to_dev(priv);
|
||||||
struct device_node *top = dev->of_node;
|
struct device_node *top = dev->of_node;
|
||||||
struct snd_soc_card *card = simple_priv_to_card(priv);
|
struct snd_soc_card *card = simple_priv_to_card(priv);
|
||||||
struct device_node *node;
|
struct link_info li;
|
||||||
struct device_node *np;
|
int ret;
|
||||||
struct device_node *codec;
|
|
||||||
bool is_fe;
|
|
||||||
int ret, loop;
|
|
||||||
int dai_idx, link_idx, conf_idx;
|
|
||||||
|
|
||||||
if (!top)
|
if (!top)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -456,62 +550,66 @@ static int asoc_simple_card_parse_of(struct simple_card_data *priv)
|
|||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* Single/Muti DAI link(s) & New style of DT node */
|
/* Single/Muti DAI link(s) & New style of DT node */
|
||||||
loop = 1;
|
memset(&li, 0, sizeof(li));
|
||||||
link_idx = 0;
|
for (li.cpu = 1; li.cpu >= 0; li.cpu--) {
|
||||||
dai_idx = 0;
|
/*
|
||||||
conf_idx = 0;
|
* Detect all CPU first, and Detect all Codec 2nd.
|
||||||
node = of_get_child_by_name(top, PREFIX "dai-link");
|
*
|
||||||
if (!node) {
|
* In Normal sound case, all DAIs are detected
|
||||||
node = dev->of_node;
|
* as "CPU-Codec".
|
||||||
loop = 0;
|
*
|
||||||
}
|
* In DPCM sound case,
|
||||||
|
* all CPUs are detected as "CPU-dummy", and
|
||||||
do {
|
* all Codecs are detected as "dummy-Codec".
|
||||||
/* DPCM */
|
* To avoid random sub-device numbering,
|
||||||
if (of_get_child_count(node) > 2) {
|
* detect "dummy-Codec" in last;
|
||||||
for_each_child_of_node(node, np) {
|
*/
|
||||||
codec = of_get_child_by_name(node,
|
ret = simple_for_each_link(priv, &li,
|
||||||
loop ? "codec" :
|
simple_dai_link_of,
|
||||||
PREFIX "codec");
|
simple_dai_link_of_dpcm);
|
||||||
if (!codec)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
is_fe = (np != codec);
|
|
||||||
|
|
||||||
ret = asoc_simple_card_dai_link_of_dpcm(
|
|
||||||
top, node, np, codec, priv,
|
|
||||||
&dai_idx, link_idx++, &conf_idx,
|
|
||||||
is_fe, !loop);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ret = asoc_simple_card_dai_link_of(
|
|
||||||
top, node, priv,
|
|
||||||
&dai_idx, link_idx++, !loop);
|
|
||||||
}
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
node = of_get_next_child(top, node);
|
|
||||||
} while (loop && node);
|
|
||||||
|
|
||||||
ret = asoc_simple_card_parse_card_name(card, PREFIX);
|
ret = asoc_simple_card_parse_card_name(card, PREFIX);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = asoc_simple_card_parse_aux_devs(top, priv);
|
ret = simple_parse_aux_devs(top, priv);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void asoc_simple_card_get_dais_count(struct device *dev,
|
static int simple_count_noml(struct simple_priv *priv,
|
||||||
int *link_num,
|
struct device_node *np,
|
||||||
int *dais_num,
|
struct device_node *codec,
|
||||||
int *ccnf_num)
|
struct link_info *li, bool is_top)
|
||||||
{
|
{
|
||||||
|
li->dais++; /* CPU or Codec */
|
||||||
|
if (np != codec)
|
||||||
|
li->link++; /* CPU-Codec */
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int simple_count_dpcm(struct simple_priv *priv,
|
||||||
|
struct device_node *np,
|
||||||
|
struct device_node *codec,
|
||||||
|
struct link_info *li, bool is_top)
|
||||||
|
{
|
||||||
|
li->dais++; /* CPU or Codec */
|
||||||
|
li->link++; /* CPU-dummy or dummy-Codec */
|
||||||
|
if (np == codec)
|
||||||
|
li->conf++;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void simple_get_dais_count(struct simple_priv *priv,
|
||||||
|
struct link_info *li)
|
||||||
|
{
|
||||||
|
struct device *dev = simple_priv_to_dev(priv);
|
||||||
struct device_node *top = dev->of_node;
|
struct device_node *top = dev->of_node;
|
||||||
struct device_node *node;
|
|
||||||
int loop;
|
|
||||||
int num;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* link_num : number of links.
|
* link_num : number of links.
|
||||||
@ -549,37 +647,34 @@ static void asoc_simple_card_get_dais_count(struct device *dev,
|
|||||||
* => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec
|
* => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec
|
||||||
* => 6 DAIs = 4xCPU + 2xCodec
|
* => 6 DAIs = 4xCPU + 2xCodec
|
||||||
* => 2 ccnf = 2xdummy-Codec
|
* => 2 ccnf = 2xdummy-Codec
|
||||||
|
*
|
||||||
|
* ex4)
|
||||||
|
* CPU0 --- Codec0 (convert-rate) link : 3
|
||||||
|
* CPU1 --- Codec1 dais : 4
|
||||||
|
* ccnf : 1
|
||||||
|
*
|
||||||
|
* => 3 links = 1xCPU-Codec + 1xCPU-dummy + 1xdummy-Codec
|
||||||
|
* => 4 DAIs = 2xCPU + 2xCodec
|
||||||
|
* => 1 ccnf = 1xdummy-Codec
|
||||||
*/
|
*/
|
||||||
if (!top) {
|
if (!top) {
|
||||||
(*link_num) = 1;
|
li->link = 1;
|
||||||
(*dais_num) = 2;
|
li->dais = 2;
|
||||||
(*ccnf_num) = 0;
|
li->conf = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
loop = 1;
|
simple_for_each_link(priv, li,
|
||||||
node = of_get_child_by_name(top, PREFIX "dai-link");
|
simple_count_noml,
|
||||||
if (!node) {
|
simple_count_dpcm);
|
||||||
node = top;
|
|
||||||
loop = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
dev_dbg(dev, "link %d, dais %d, ccnf %d\n",
|
||||||
num = of_get_child_count(node);
|
li->link, li->dais, li->conf);
|
||||||
(*dais_num) += num;
|
|
||||||
if (num > 2) {
|
|
||||||
(*link_num) += num;
|
|
||||||
(*ccnf_num)++;
|
|
||||||
} else {
|
|
||||||
(*link_num)++;
|
|
||||||
}
|
|
||||||
node = of_get_next_child(top, node);
|
|
||||||
} while (loop && node);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int asoc_simple_soc_card_probe(struct snd_soc_card *card)
|
static int simple_soc_probe(struct snd_soc_card *card)
|
||||||
{
|
{
|
||||||
struct simple_card_data *priv = snd_soc_card_get_drvdata(card);
|
struct simple_priv *priv = snd_soc_card_get_drvdata(card);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = asoc_simple_card_init_hp(card, &priv->hp_jack, PREFIX);
|
ret = asoc_simple_card_init_hp(card, &priv->hp_jack, PREFIX);
|
||||||
@ -593,9 +688,9 @@ static int asoc_simple_soc_card_probe(struct snd_soc_card *card)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int asoc_simple_card_probe(struct platform_device *pdev)
|
static int simple_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct simple_card_data *priv;
|
struct simple_priv *priv;
|
||||||
struct snd_soc_dai_link *dai_link;
|
struct snd_soc_dai_link *dai_link;
|
||||||
struct simple_dai_props *dai_props;
|
struct simple_dai_props *dai_props;
|
||||||
struct asoc_simple_dai *dais;
|
struct asoc_simple_dai *dais;
|
||||||
@ -603,7 +698,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
|
|||||||
struct device_node *np = dev->of_node;
|
struct device_node *np = dev->of_node;
|
||||||
struct snd_soc_card *card;
|
struct snd_soc_card *card;
|
||||||
struct snd_soc_codec_conf *cconf;
|
struct snd_soc_codec_conf *cconf;
|
||||||
int lnum = 0, dnum = 0, cnum = 0;
|
struct link_info li;
|
||||||
int ret, i;
|
int ret, i;
|
||||||
|
|
||||||
/* Allocate the private data and the DAI link array */
|
/* Allocate the private data and the DAI link array */
|
||||||
@ -611,14 +706,20 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
|
|||||||
if (!priv)
|
if (!priv)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
asoc_simple_card_get_dais_count(dev, &lnum, &dnum, &cnum);
|
card = simple_priv_to_card(priv);
|
||||||
if (!lnum || !dnum)
|
card->owner = THIS_MODULE;
|
||||||
|
card->dev = dev;
|
||||||
|
card->probe = simple_soc_probe;
|
||||||
|
|
||||||
|
memset(&li, 0, sizeof(li));
|
||||||
|
simple_get_dais_count(priv, &li);
|
||||||
|
if (!li.link || !li.dais)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
dai_props = devm_kcalloc(dev, lnum, sizeof(*dai_props), GFP_KERNEL);
|
dai_props = devm_kcalloc(dev, li.link, sizeof(*dai_props), GFP_KERNEL);
|
||||||
dai_link = devm_kcalloc(dev, lnum, sizeof(*dai_link), GFP_KERNEL);
|
dai_link = devm_kcalloc(dev, li.link, sizeof(*dai_link), GFP_KERNEL);
|
||||||
dais = devm_kcalloc(dev, dnum, sizeof(*dais), GFP_KERNEL);
|
dais = devm_kcalloc(dev, li.dais, sizeof(*dais), GFP_KERNEL);
|
||||||
cconf = devm_kcalloc(dev, cnum, sizeof(*cconf), GFP_KERNEL);
|
cconf = devm_kcalloc(dev, li.conf, sizeof(*cconf), GFP_KERNEL);
|
||||||
if (!dai_props || !dai_link || !dais)
|
if (!dai_props || !dai_link || !dais)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
@ -628,30 +729,25 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
|
|||||||
* see
|
* see
|
||||||
* soc-core.c :: snd_soc_init_multicodec()
|
* soc-core.c :: snd_soc_init_multicodec()
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < lnum; i++) {
|
for (i = 0; i < li.link; i++) {
|
||||||
dai_link[i].codecs = &dai_props[i].codecs;
|
dai_link[i].codecs = &dai_props[i].codecs;
|
||||||
dai_link[i].num_codecs = 1;
|
dai_link[i].num_codecs = 1;
|
||||||
dai_link[i].platform = &dai_props[i].platform;
|
dai_link[i].platform = &dai_props[i].platform;
|
||||||
}
|
}
|
||||||
|
|
||||||
priv->dai_props = dai_props;
|
priv->dai_props = dai_props;
|
||||||
priv->dai_link = dai_link;
|
priv->dai_link = dai_link;
|
||||||
priv->dais = dais;
|
priv->dais = dais;
|
||||||
priv->codec_conf = cconf;
|
priv->codec_conf = cconf;
|
||||||
|
|
||||||
/* Init snd_soc_card */
|
|
||||||
card = simple_priv_to_card(priv);
|
|
||||||
card->owner = THIS_MODULE;
|
|
||||||
card->dev = dev;
|
|
||||||
card->dai_link = priv->dai_link;
|
card->dai_link = priv->dai_link;
|
||||||
card->num_links = lnum;
|
card->num_links = li.link;
|
||||||
card->codec_conf = cconf;
|
card->codec_conf = cconf;
|
||||||
card->num_configs = cnum;
|
card->num_configs = li.conf;
|
||||||
card->probe = asoc_simple_soc_card_probe;
|
|
||||||
|
|
||||||
if (np && of_device_is_available(np)) {
|
if (np && of_device_is_available(np)) {
|
||||||
|
|
||||||
ret = asoc_simple_card_parse_of(priv);
|
ret = simple_parse_of(priv);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (ret != -EPROBE_DEFER)
|
if (ret != -EPROBE_DEFER)
|
||||||
dev_err(dev, "parse error %d\n", ret);
|
dev_err(dev, "parse error %d\n", ret);
|
||||||
@ -694,7 +790,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
|
|||||||
dai_link->stream_name = cinfo->name;
|
dai_link->stream_name = cinfo->name;
|
||||||
dai_link->cpu_dai_name = cinfo->cpu_dai.name;
|
dai_link->cpu_dai_name = cinfo->cpu_dai.name;
|
||||||
dai_link->dai_fmt = cinfo->daifmt;
|
dai_link->dai_fmt = cinfo->daifmt;
|
||||||
dai_link->init = asoc_simple_card_dai_init;
|
dai_link->init = simple_dai_init;
|
||||||
memcpy(priv->dai_props->cpu_dai, &cinfo->cpu_dai,
|
memcpy(priv->dai_props->cpu_dai, &cinfo->cpu_dai,
|
||||||
sizeof(*priv->dai_props->cpu_dai));
|
sizeof(*priv->dai_props->cpu_dai));
|
||||||
memcpy(priv->dai_props->codec_dai, &cinfo->codec_dai,
|
memcpy(priv->dai_props->codec_dai, &cinfo->codec_dai,
|
||||||
@ -714,28 +810,28 @@ err:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int asoc_simple_card_remove(struct platform_device *pdev)
|
static int simple_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct snd_soc_card *card = platform_get_drvdata(pdev);
|
struct snd_soc_card *card = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
return asoc_simple_card_clean_reference(card);
|
return asoc_simple_card_clean_reference(card);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct of_device_id asoc_simple_of_match[] = {
|
static const struct of_device_id simple_of_match[] = {
|
||||||
{ .compatible = "simple-audio-card", },
|
{ .compatible = "simple-audio-card", },
|
||||||
{ .compatible = "simple-scu-audio-card", },
|
{ .compatible = "simple-scu-audio-card", },
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, asoc_simple_of_match);
|
MODULE_DEVICE_TABLE(of, simple_of_match);
|
||||||
|
|
||||||
static struct platform_driver asoc_simple_card = {
|
static struct platform_driver asoc_simple_card = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "asoc-simple-card",
|
.name = "asoc-simple-card",
|
||||||
.pm = &snd_soc_pm_ops,
|
.pm = &snd_soc_pm_ops,
|
||||||
.of_match_table = asoc_simple_of_match,
|
.of_match_table = simple_of_match,
|
||||||
},
|
},
|
||||||
.probe = asoc_simple_card_probe,
|
.probe = simple_probe,
|
||||||
.remove = asoc_simple_card_remove,
|
.remove = simple_remove,
|
||||||
};
|
};
|
||||||
|
|
||||||
module_platform_driver(asoc_simple_card);
|
module_platform_driver(asoc_simple_card);
|
||||||
|
@ -1,474 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
|
||||||
//
|
|
||||||
// ASoC simple SCU sound card support
|
|
||||||
//
|
|
||||||
// Copyright (C) 2015 Renesas Solutions Corp.
|
|
||||||
// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
|
|
||||||
//
|
|
||||||
// based on ${LINUX}/sound/soc/generic/simple-card.c
|
|
||||||
|
|
||||||
#include <linux/clk.h>
|
|
||||||
#include <linux/device.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/of.h>
|
|
||||||
#include <linux/of_device.h>
|
|
||||||
#include <linux/platform_device.h>
|
|
||||||
#include <linux/string.h>
|
|
||||||
#include <sound/jack.h>
|
|
||||||
#include <sound/soc.h>
|
|
||||||
#include <sound/soc-dai.h>
|
|
||||||
#include <sound/simple_card_utils.h>
|
|
||||||
|
|
||||||
struct simple_card_data {
|
|
||||||
struct snd_soc_card snd_card;
|
|
||||||
struct simple_dai_props {
|
|
||||||
struct asoc_simple_dai *cpu_dai;
|
|
||||||
struct asoc_simple_dai *codec_dai;
|
|
||||||
struct snd_soc_dai_link_component codecs;
|
|
||||||
struct snd_soc_dai_link_component platform;
|
|
||||||
struct asoc_simple_card_data adata;
|
|
||||||
struct snd_soc_codec_conf *codec_conf;
|
|
||||||
} *dai_props;
|
|
||||||
struct snd_soc_dai_link *dai_link;
|
|
||||||
struct asoc_simple_dai *dais;
|
|
||||||
struct asoc_simple_card_data adata;
|
|
||||||
struct snd_soc_codec_conf *codec_conf;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define simple_priv_to_card(priv) (&(priv)->snd_card)
|
|
||||||
#define simple_priv_to_props(priv, i) ((priv)->dai_props + (i))
|
|
||||||
#define simple_priv_to_dev(priv) (simple_priv_to_card(priv)->dev)
|
|
||||||
#define simple_priv_to_link(priv, i) (simple_priv_to_card(priv)->dai_link + (i))
|
|
||||||
|
|
||||||
#define DAI "sound-dai"
|
|
||||||
#define CELL "#sound-dai-cells"
|
|
||||||
#define PREFIX "simple-audio-card,"
|
|
||||||
|
|
||||||
static int asoc_simple_card_startup(struct snd_pcm_substream *substream)
|
|
||||||
{
|
|
||||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
||||||
struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
|
||||||
struct simple_dai_props *dai_props =
|
|
||||||
simple_priv_to_props(priv, rtd->num);
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = asoc_simple_card_clk_enable(dai_props->cpu_dai);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = asoc_simple_card_clk_enable(dai_props->codec_dai);
|
|
||||||
if (ret)
|
|
||||||
asoc_simple_card_clk_disable(dai_props->cpu_dai);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream)
|
|
||||||
{
|
|
||||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
||||||
struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
|
||||||
struct simple_dai_props *dai_props =
|
|
||||||
simple_priv_to_props(priv, rtd->num);
|
|
||||||
|
|
||||||
asoc_simple_card_clk_disable(dai_props->cpu_dai);
|
|
||||||
|
|
||||||
asoc_simple_card_clk_disable(dai_props->codec_dai);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct snd_soc_ops asoc_simple_card_ops = {
|
|
||||||
.startup = asoc_simple_card_startup,
|
|
||||||
.shutdown = asoc_simple_card_shutdown,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
|
|
||||||
{
|
|
||||||
struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
|
||||||
struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num);
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = asoc_simple_card_init_dai(rtd->codec_dai,
|
|
||||||
dai_props->codec_dai);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = asoc_simple_card_init_dai(rtd->cpu_dai,
|
|
||||||
dai_props->cpu_dai);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int asoc_simple_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
|
|
||||||
struct snd_pcm_hw_params *params)
|
|
||||||
{
|
|
||||||
struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
|
||||||
struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num);
|
|
||||||
|
|
||||||
asoc_simple_card_convert_fixup(&dai_props->adata, params);
|
|
||||||
|
|
||||||
/* overwrite by top level adata if exist */
|
|
||||||
asoc_simple_card_convert_fixup(&priv->adata, params);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int asoc_simple_card_dai_link_of(struct device_node *link,
|
|
||||||
struct device_node *np,
|
|
||||||
struct device_node *codec,
|
|
||||||
struct simple_card_data *priv,
|
|
||||||
int *dai_idx, int link_idx,
|
|
||||||
int *conf_idx, int is_fe,
|
|
||||||
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, link_idx);
|
|
||||||
struct simple_dai_props *dai_props = simple_priv_to_props(priv, link_idx);
|
|
||||||
struct snd_soc_card *card = simple_priv_to_card(priv);
|
|
||||||
struct asoc_simple_dai *dai;
|
|
||||||
char *prefix = "";
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
/* For single DAI link & old style of DT node */
|
|
||||||
if (is_top_level_node)
|
|
||||||
prefix = PREFIX;
|
|
||||||
|
|
||||||
if (is_fe) {
|
|
||||||
int is_single_links = 0;
|
|
||||||
struct snd_soc_dai_link_component *codecs;
|
|
||||||
|
|
||||||
/* BE is dummy */
|
|
||||||
codecs = dai_link->codecs;
|
|
||||||
codecs->of_node = NULL;
|
|
||||||
codecs->dai_name = "snd-soc-dummy-dai";
|
|
||||||
codecs->name = "snd-soc-dummy";
|
|
||||||
|
|
||||||
/* FE settings */
|
|
||||||
dai_link->dynamic = 1;
|
|
||||||
dai_link->dpcm_merged_format = 1;
|
|
||||||
|
|
||||||
dai =
|
|
||||||
dai_props->cpu_dai = &priv->dais[(*dai_idx)++];
|
|
||||||
|
|
||||||
ret = asoc_simple_card_parse_cpu(np, dai_link, DAI, CELL,
|
|
||||||
&is_single_links);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = asoc_simple_card_parse_clk_cpu(dev, np, dai_link, dai);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = asoc_simple_card_set_dailink_name(dev, dai_link,
|
|
||||||
"fe.%s",
|
|
||||||
dai_link->cpu_dai_name);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
asoc_simple_card_canonicalize_cpu(dai_link, is_single_links);
|
|
||||||
} else {
|
|
||||||
struct snd_soc_codec_conf *cconf;
|
|
||||||
|
|
||||||
/* FE is dummy */
|
|
||||||
dai_link->cpu_of_node = NULL;
|
|
||||||
dai_link->cpu_dai_name = "snd-soc-dummy-dai";
|
|
||||||
dai_link->cpu_name = "snd-soc-dummy";
|
|
||||||
|
|
||||||
/* BE settings */
|
|
||||||
dai_link->no_pcm = 1;
|
|
||||||
dai_link->be_hw_params_fixup = asoc_simple_card_be_hw_params_fixup;
|
|
||||||
|
|
||||||
dai =
|
|
||||||
dai_props->codec_dai = &priv->dais[(*dai_idx)++];
|
|
||||||
|
|
||||||
cconf =
|
|
||||||
dai_props->codec_conf = &priv->codec_conf[(*conf_idx)++];
|
|
||||||
|
|
||||||
ret = asoc_simple_card_parse_codec(np, dai_link, DAI, CELL);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = asoc_simple_card_parse_clk_codec(dev, np, dai_link, dai);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = asoc_simple_card_set_dailink_name(dev, dai_link,
|
|
||||||
"be.%s",
|
|
||||||
dai_link->codecs->dai_name);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
/* check "prefix" from top node */
|
|
||||||
snd_soc_of_parse_audio_prefix(card, cconf,
|
|
||||||
dai_link->codecs->of_node,
|
|
||||||
PREFIX "prefix");
|
|
||||||
/* check "prefix" from each node if top doesn't have */
|
|
||||||
if (!cconf->of_node)
|
|
||||||
snd_soc_of_parse_node_prefix(np, cconf,
|
|
||||||
dai_link->codecs->of_node,
|
|
||||||
"prefix");
|
|
||||||
}
|
|
||||||
|
|
||||||
asoc_simple_card_parse_convert(dev, link, prefix, &dai_props->adata);
|
|
||||||
|
|
||||||
ret = asoc_simple_card_of_parse_tdm(np, dai);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = asoc_simple_card_canonicalize_dailink(dai_link);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = asoc_simple_card_parse_daifmt(dev, link, codec,
|
|
||||||
prefix, &dai_link->dai_fmt);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
dai_link->dpcm_playback = 1;
|
|
||||||
dai_link->dpcm_capture = 1;
|
|
||||||
dai_link->ops = &asoc_simple_card_ops;
|
|
||||||
dai_link->init = asoc_simple_card_dai_init;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int asoc_simple_card_parse_of(struct simple_card_data *priv)
|
|
||||||
|
|
||||||
{
|
|
||||||
struct device *dev = simple_priv_to_dev(priv);
|
|
||||||
struct device_node *top = dev->of_node;
|
|
||||||
struct device_node *node;
|
|
||||||
struct device_node *np;
|
|
||||||
struct device_node *codec;
|
|
||||||
struct snd_soc_card *card = simple_priv_to_card(priv);
|
|
||||||
bool is_fe;
|
|
||||||
int ret, loop;
|
|
||||||
int dai_idx, link_idx, conf_idx;
|
|
||||||
|
|
||||||
if (!top)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
ret = asoc_simple_card_of_parse_widgets(card, PREFIX);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = asoc_simple_card_of_parse_routing(card, PREFIX);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
asoc_simple_card_parse_convert(dev, top, PREFIX, &priv->adata);
|
|
||||||
|
|
||||||
loop = 1;
|
|
||||||
link_idx = 0;
|
|
||||||
dai_idx = 0;
|
|
||||||
conf_idx = 0;
|
|
||||||
node = of_get_child_by_name(top, PREFIX "dai-link");
|
|
||||||
if (!node) {
|
|
||||||
node = dev->of_node;
|
|
||||||
loop = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
codec = of_get_child_by_name(node,
|
|
||||||
loop ? "codec" : PREFIX "codec");
|
|
||||||
if (!codec)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
for_each_child_of_node(node, np) {
|
|
||||||
is_fe = (np != codec);
|
|
||||||
|
|
||||||
ret = asoc_simple_card_dai_link_of(node, np, codec, priv,
|
|
||||||
&dai_idx, link_idx++,
|
|
||||||
&conf_idx,
|
|
||||||
is_fe, !loop);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
node = of_get_next_child(top, node);
|
|
||||||
} while (loop && node);
|
|
||||||
|
|
||||||
ret = asoc_simple_card_parse_card_name(card, PREFIX);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void asoc_simple_card_get_dais_count(struct device *dev,
|
|
||||||
int *link_num,
|
|
||||||
int *dais_num,
|
|
||||||
int *ccnf_num)
|
|
||||||
{
|
|
||||||
struct device_node *top = dev->of_node;
|
|
||||||
struct device_node *node;
|
|
||||||
int loop;
|
|
||||||
int num;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* link_num : number of links.
|
|
||||||
* CPU-Codec / CPU-dummy / dummy-Codec
|
|
||||||
* dais_num : number of DAIs
|
|
||||||
* ccnf_num : number of codec_conf
|
|
||||||
* same number for "dummy-Codec"
|
|
||||||
*
|
|
||||||
* ex1)
|
|
||||||
* CPU0 --- Codec0 link : 5
|
|
||||||
* CPU1 --- Codec1 dais : 7
|
|
||||||
* CPU2 -/ ccnf : 1
|
|
||||||
* CPU3 --- Codec2
|
|
||||||
*
|
|
||||||
* => 5 links = 2xCPU-Codec + 2xCPU-dummy + 1xdummy-Codec
|
|
||||||
* => 7 DAIs = 4xCPU + 3xCodec
|
|
||||||
* => 1 ccnf = 1xdummy-Codec
|
|
||||||
*
|
|
||||||
* ex2)
|
|
||||||
* CPU0 --- Codec0 link : 5
|
|
||||||
* CPU1 --- Codec1 dais : 6
|
|
||||||
* CPU2 -/ ccnf : 1
|
|
||||||
* CPU3 -/
|
|
||||||
*
|
|
||||||
* => 5 links = 1xCPU-Codec + 3xCPU-dummy + 1xdummy-Codec
|
|
||||||
* => 6 DAIs = 4xCPU + 2xCodec
|
|
||||||
* => 1 ccnf = 1xdummy-Codec
|
|
||||||
*
|
|
||||||
* ex3)
|
|
||||||
* CPU0 --- Codec0 link : 6
|
|
||||||
* CPU1 -/ dais : 6
|
|
||||||
* CPU2 --- Codec1 ccnf : 2
|
|
||||||
* CPU3 -/
|
|
||||||
*
|
|
||||||
* => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec
|
|
||||||
* => 6 DAIs = 4xCPU + 2xCodec
|
|
||||||
* => 2 ccnf = 2xdummy-Codec
|
|
||||||
*/
|
|
||||||
if (!top) {
|
|
||||||
(*link_num) = 1;
|
|
||||||
(*dais_num) = 2;
|
|
||||||
(*ccnf_num) = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
loop = 1;
|
|
||||||
node = of_get_child_by_name(top, PREFIX "dai-link");
|
|
||||||
if (!node) {
|
|
||||||
node = top;
|
|
||||||
loop = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
num = of_get_child_count(node);
|
|
||||||
(*dais_num) += num;
|
|
||||||
if (num > 2) {
|
|
||||||
(*link_num) += num;
|
|
||||||
(*ccnf_num)++;
|
|
||||||
} else {
|
|
||||||
(*link_num)++;
|
|
||||||
}
|
|
||||||
node = of_get_next_child(top, node);
|
|
||||||
} while (loop && node);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int asoc_simple_card_probe(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
struct simple_card_data *priv;
|
|
||||||
struct snd_soc_dai_link *dai_link;
|
|
||||||
struct simple_dai_props *dai_props;
|
|
||||||
struct asoc_simple_dai *dais;
|
|
||||||
struct snd_soc_card *card;
|
|
||||||
struct snd_soc_codec_conf *cconf;
|
|
||||||
struct device *dev = &pdev->dev;
|
|
||||||
int ret, i;
|
|
||||||
int lnum = 0, dnum = 0, cnum = 0;
|
|
||||||
|
|
||||||
/* Allocate the private data */
|
|
||||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
|
||||||
if (!priv)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
asoc_simple_card_get_dais_count(dev, &lnum, &dnum, &cnum);
|
|
||||||
if (!lnum || !dnum)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
dai_props = devm_kcalloc(dev, lnum, sizeof(*dai_props), GFP_KERNEL);
|
|
||||||
dai_link = devm_kcalloc(dev, lnum, sizeof(*dai_link), GFP_KERNEL);
|
|
||||||
dais = devm_kcalloc(dev, dnum, sizeof(*dais), GFP_KERNEL);
|
|
||||||
cconf = devm_kcalloc(dev, cnum, sizeof(*cconf), GFP_KERNEL);
|
|
||||||
if (!dai_props || !dai_link || !dais)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Use snd_soc_dai_link_component instead of legacy style
|
|
||||||
* It is codec only. but cpu/platform will be supported in the future.
|
|
||||||
* see
|
|
||||||
* soc-core.c :: snd_soc_init_multicodec()
|
|
||||||
*/
|
|
||||||
for (i = 0; i < lnum; i++) {
|
|
||||||
dai_link[i].codecs = &dai_props[i].codecs;
|
|
||||||
dai_link[i].num_codecs = 1;
|
|
||||||
dai_link[i].platform = &dai_props[i].platform;
|
|
||||||
}
|
|
||||||
|
|
||||||
priv->dai_props = dai_props;
|
|
||||||
priv->dai_link = dai_link;
|
|
||||||
priv->dais = dais;
|
|
||||||
priv->codec_conf = cconf;
|
|
||||||
|
|
||||||
/* Init snd_soc_card */
|
|
||||||
card = simple_priv_to_card(priv);
|
|
||||||
card->owner = THIS_MODULE;
|
|
||||||
card->dev = dev;
|
|
||||||
card->dai_link = priv->dai_link;
|
|
||||||
card->num_links = lnum;
|
|
||||||
card->codec_conf = cconf;
|
|
||||||
card->num_configs = cnum;
|
|
||||||
|
|
||||||
ret = asoc_simple_card_parse_of(priv);
|
|
||||||
if (ret < 0) {
|
|
||||||
if (ret != -EPROBE_DEFER)
|
|
||||||
dev_err(dev, "parse error %d\n", ret);
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
snd_soc_card_set_drvdata(card, priv);
|
|
||||||
|
|
||||||
ret = devm_snd_soc_register_card(dev, card);
|
|
||||||
if (ret < 0)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
err:
|
|
||||||
asoc_simple_card_clean_reference(card);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int asoc_simple_card_remove(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
struct snd_soc_card *card = platform_get_drvdata(pdev);
|
|
||||||
|
|
||||||
return asoc_simple_card_clean_reference(card);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct of_device_id asoc_simple_of_match[] = {
|
|
||||||
{ .compatible = "renesas,rsrc-card", },
|
|
||||||
{ .compatible = "simple-scu-audio-card", },
|
|
||||||
{},
|
|
||||||
};
|
|
||||||
MODULE_DEVICE_TABLE(of, asoc_simple_of_match);
|
|
||||||
|
|
||||||
static struct platform_driver asoc_simple_card = {
|
|
||||||
.driver = {
|
|
||||||
.name = "simple-scu-audio-card",
|
|
||||||
.pm = &snd_soc_pm_ops,
|
|
||||||
.of_match_table = asoc_simple_of_match,
|
|
||||||
},
|
|
||||||
.probe = asoc_simple_card_probe,
|
|
||||||
.remove = asoc_simple_card_remove,
|
|
||||||
};
|
|
||||||
|
|
||||||
module_platform_driver(asoc_simple_card);
|
|
||||||
|
|
||||||
MODULE_ALIAS("platform:asoc-simple-scu-card");
|
|
||||||
MODULE_LICENSE("GPL v2");
|
|
||||||
MODULE_DESCRIPTION("ASoC Simple SCU Sound Card");
|
|
||||||
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
|
|
@ -91,7 +91,7 @@ config SND_SST_ATOM_HIFI2_PLATFORM_PCI
|
|||||||
config SND_SST_ATOM_HIFI2_PLATFORM_ACPI
|
config SND_SST_ATOM_HIFI2_PLATFORM_ACPI
|
||||||
tristate "ACPI HiFi2 (Baytrail, Cherrytrail) Platforms"
|
tristate "ACPI HiFi2 (Baytrail, Cherrytrail) Platforms"
|
||||||
default ACPI
|
default ACPI
|
||||||
depends on X86 && ACPI
|
depends on X86 && ACPI && PCI
|
||||||
select SND_SST_IPC_ACPI
|
select SND_SST_IPC_ACPI
|
||||||
select SND_SST_ATOM_HIFI2_PLATFORM
|
select SND_SST_ATOM_HIFI2_PLATFORM
|
||||||
select SND_SOC_ACPI_INTEL_MATCH
|
select SND_SOC_ACPI_INTEL_MATCH
|
||||||
|
@ -647,7 +647,7 @@ static int sst_swm_mixer_event(struct snd_soc_dapm_widget *w,
|
|||||||
set_mixer = false;
|
set_mixer = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (set_mixer == false)
|
if (!set_mixer)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (SND_SOC_DAPM_EVENT_ON(event) ||
|
if (SND_SOC_DAPM_EVENT_ON(event) ||
|
||||||
|
@ -190,7 +190,7 @@ int sst_fill_stream_params(void *substream,
|
|||||||
map = ctx->pdata->pdev_strm_map;
|
map = ctx->pdata->pdev_strm_map;
|
||||||
map_size = ctx->pdata->strm_map_size;
|
map_size = ctx->pdata->strm_map_size;
|
||||||
|
|
||||||
if (is_compress == true)
|
if (is_compress)
|
||||||
cstream = (struct snd_compr_stream *)substream;
|
cstream = (struct snd_compr_stream *)substream;
|
||||||
else
|
else
|
||||||
pstream = (struct snd_pcm_substream *)substream;
|
pstream = (struct snd_pcm_substream *)substream;
|
||||||
|
@ -255,18 +255,16 @@ static int is_byt(void)
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int is_byt_cr(struct device *dev, bool *bytcr)
|
static bool is_byt_cr(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
int status = 0;
|
int status = 0;
|
||||||
|
|
||||||
if (IS_ENABLED(CONFIG_IOSF_MBI)) {
|
if (!is_byt())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (iosf_mbi_available()) {
|
||||||
u32 bios_status;
|
u32 bios_status;
|
||||||
|
|
||||||
if (!is_byt() || !iosf_mbi_available()) {
|
|
||||||
/* bail silently */
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = iosf_mbi_read(BT_MBI_UNIT_PMC, /* 0x04 PUNIT */
|
status = iosf_mbi_read(BT_MBI_UNIT_PMC, /* 0x04 PUNIT */
|
||||||
MBI_REG_READ, /* 0x10 */
|
MBI_REG_READ, /* 0x10 */
|
||||||
0x006, /* BIOS_CONFIG */
|
0x006, /* BIOS_CONFIG */
|
||||||
@ -278,15 +276,28 @@ static int is_byt_cr(struct device *dev, bool *bytcr)
|
|||||||
/* bits 26:27 mirror PMIC options */
|
/* bits 26:27 mirror PMIC options */
|
||||||
bios_status = (bios_status >> 26) & 3;
|
bios_status = (bios_status >> 26) & 3;
|
||||||
|
|
||||||
if ((bios_status == 1) || (bios_status == 3))
|
if (bios_status == 1 || bios_status == 3) {
|
||||||
*bytcr = true;
|
dev_info(dev, "Detected Baytrail-CR platform\n");
|
||||||
else
|
return true;
|
||||||
dev_info(dev, "BYT-CR not detected\n");
|
}
|
||||||
|
|
||||||
|
dev_info(dev, "BYT-CR not detected\n");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dev_info(dev, "IOSF_MBI not enabled, no BYT-CR detection\n");
|
dev_info(dev, "IOSF_MBI not available, no BYT-CR detection\n");
|
||||||
}
|
}
|
||||||
return status;
|
|
||||||
|
if (platform_get_resource(pdev, IORESOURCE_IRQ, 5) == NULL) {
|
||||||
|
/*
|
||||||
|
* Some devices detected as BYT-T have only a single IRQ listed,
|
||||||
|
* causing platform_get_irq with index 5 to return -ENXIO.
|
||||||
|
* The correct IRQ in this case is at index 0, as on BYT-CR.
|
||||||
|
*/
|
||||||
|
dev_info(dev, "Falling back to Baytrail-CR platform\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -301,7 +312,6 @@ static int sst_acpi_probe(struct platform_device *pdev)
|
|||||||
struct platform_device *plat_dev;
|
struct platform_device *plat_dev;
|
||||||
struct sst_platform_info *pdata;
|
struct sst_platform_info *pdata;
|
||||||
unsigned int dev_id;
|
unsigned int dev_id;
|
||||||
bool bytcr = false;
|
|
||||||
|
|
||||||
id = acpi_match_device(dev->driver->acpi_match_table, dev);
|
id = acpi_match_device(dev->driver->acpi_match_table, dev);
|
||||||
if (!id)
|
if (!id)
|
||||||
@ -333,10 +343,7 @@ static int sst_acpi_probe(struct platform_device *pdev)
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = is_byt_cr(dev, &bytcr);
|
if (is_byt_cr(pdev)) {
|
||||||
if (!((ret < 0) || (bytcr == false))) {
|
|
||||||
dev_info(dev, "Detected Baytrail-CR platform\n");
|
|
||||||
|
|
||||||
/* override resource info */
|
/* override resource info */
|
||||||
byt_rvp_platform_data.res_info = &bytcr_res_info;
|
byt_rvp_platform_data.res_info = &bytcr_res_info;
|
||||||
}
|
}
|
||||||
|
@ -146,7 +146,7 @@ static int sst_power_control(struct device *dev, bool state)
|
|||||||
int ret = 0;
|
int ret = 0;
|
||||||
int usage_count = 0;
|
int usage_count = 0;
|
||||||
|
|
||||||
if (state == true) {
|
if (state) {
|
||||||
ret = pm_runtime_get_sync(dev);
|
ret = pm_runtime_get_sync(dev);
|
||||||
usage_count = GET_USAGE_COUNT(dev);
|
usage_count = GET_USAGE_COUNT(dev);
|
||||||
dev_dbg(ctx->dev, "Enable: pm usage count: %d\n", usage_count);
|
dev_dbg(ctx->dev, "Enable: pm usage count: %d\n", usage_count);
|
||||||
|
@ -269,7 +269,7 @@ static void sst_do_memcpy(struct list_head *memcpy_list)
|
|||||||
struct sst_memcpy_list *listnode;
|
struct sst_memcpy_list *listnode;
|
||||||
|
|
||||||
list_for_each_entry(listnode, memcpy_list, memcpylist) {
|
list_for_each_entry(listnode, memcpy_list, memcpylist) {
|
||||||
if (listnode->is_io == true)
|
if (listnode->is_io)
|
||||||
memcpy32_toio((void __iomem *)listnode->dstn,
|
memcpy32_toio((void __iomem *)listnode->dstn,
|
||||||
listnode->src, listnode->size);
|
listnode->src, listnode->size);
|
||||||
else
|
else
|
||||||
|
@ -278,7 +278,6 @@ static int sst_byt_process_notification(struct sst_byt *byt,
|
|||||||
struct sst_byt_stream *stream;
|
struct sst_byt_stream *stream;
|
||||||
u64 header;
|
u64 header;
|
||||||
u8 msg_id, stream_id;
|
u8 msg_id, stream_id;
|
||||||
int handled = 1;
|
|
||||||
|
|
||||||
header = sst_dsp_shim_read64_unlocked(sst, SST_IPCD);
|
header = sst_dsp_shim_read64_unlocked(sst, SST_IPCD);
|
||||||
msg_id = sst_byt_header_msg_id(header);
|
msg_id = sst_byt_header_msg_id(header);
|
||||||
@ -298,7 +297,7 @@ static int sst_byt_process_notification(struct sst_byt *byt,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return handled;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static irqreturn_t sst_byt_irq_thread(int irq, void *context)
|
static irqreturn_t sst_byt_irq_thread(int irq, void *context)
|
||||||
|
@ -188,7 +188,7 @@ static int sst_byt_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
|||||||
sst_byt_stream_start(byt, pcm_data->stream, 0);
|
sst_byt_stream_start(byt, pcm_data->stream, 0);
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_TRIGGER_RESUME:
|
case SNDRV_PCM_TRIGGER_RESUME:
|
||||||
if (pdata->restore_stream == true)
|
if (pdata->restore_stream)
|
||||||
schedule_work(&pcm_data->work);
|
schedule_work(&pcm_data->work);
|
||||||
else
|
else
|
||||||
sst_byt_stream_resume(byt, pcm_data->stream);
|
sst_byt_stream_resume(byt, pcm_data->stream);
|
||||||
|
@ -192,7 +192,7 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = {
|
|||||||
.stream_name = "Loopback",
|
.stream_name = "Loopback",
|
||||||
.cpu_dai_name = "Loopback Pin",
|
.cpu_dai_name = "Loopback Pin",
|
||||||
.platform_name = "haswell-pcm-audio",
|
.platform_name = "haswell-pcm-audio",
|
||||||
.dynamic = 0,
|
.dynamic = 1,
|
||||||
.codec_name = "snd-soc-dummy",
|
.codec_name = "snd-soc-dummy",
|
||||||
.codec_dai_name = "snd-soc-dummy-dai",
|
.codec_dai_name = "snd-soc-dummy-dai",
|
||||||
.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
|
.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
|
||||||
|
@ -19,13 +19,20 @@
|
|||||||
*
|
*
|
||||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
*/
|
*/
|
||||||
|
#include <linux/acpi.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/gpio/consumer.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
|
#include <linux/input.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/device.h>
|
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <asm/cpu_device_id.h>
|
||||||
|
#include <asm/intel-family.h>
|
||||||
#include <asm/platform_sst_audio.h>
|
#include <asm/platform_sst_audio.h>
|
||||||
#include <linux/clk.h>
|
#include <sound/jack.h>
|
||||||
#include <sound/pcm.h>
|
#include <sound/pcm.h>
|
||||||
#include <sound/pcm_params.h>
|
#include <sound/pcm_params.h>
|
||||||
#include <sound/soc.h>
|
#include <sound/soc.h>
|
||||||
@ -35,27 +42,96 @@
|
|||||||
|
|
||||||
struct byt_cht_es8316_private {
|
struct byt_cht_es8316_private {
|
||||||
struct clk *mclk;
|
struct clk *mclk;
|
||||||
|
struct snd_soc_jack jack;
|
||||||
|
struct gpio_desc *speaker_en_gpio;
|
||||||
|
bool speaker_en;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct snd_soc_dapm_widget byt_cht_es8316_widgets[] = {
|
enum {
|
||||||
SND_SOC_DAPM_HP("Headphone", NULL),
|
BYT_CHT_ES8316_INTMIC_IN1_MAP,
|
||||||
|
BYT_CHT_ES8316_INTMIC_IN2_MAP,
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
#define BYT_CHT_ES8316_MAP(quirk) ((quirk) & GENMASK(3, 0))
|
||||||
* The codec supports two analog microphone inputs. I have only
|
#define BYT_CHT_ES8316_SSP0 BIT(16)
|
||||||
* tested MIC1. A DMIC route could also potentially be added
|
#define BYT_CHT_ES8316_MONO_SPEAKER BIT(17)
|
||||||
* if such functionality is found on another platform.
|
|
||||||
*/
|
static int quirk;
|
||||||
SND_SOC_DAPM_MIC("Microphone 1", NULL),
|
|
||||||
SND_SOC_DAPM_MIC("Microphone 2", NULL),
|
static int quirk_override = -1;
|
||||||
|
module_param_named(quirk, quirk_override, int, 0444);
|
||||||
|
MODULE_PARM_DESC(quirk, "Board-specific quirk override");
|
||||||
|
|
||||||
|
static void log_quirks(struct device *dev)
|
||||||
|
{
|
||||||
|
if (BYT_CHT_ES8316_MAP(quirk) == BYT_CHT_ES8316_INTMIC_IN1_MAP)
|
||||||
|
dev_info(dev, "quirk IN1_MAP enabled");
|
||||||
|
if (BYT_CHT_ES8316_MAP(quirk) == BYT_CHT_ES8316_INTMIC_IN2_MAP)
|
||||||
|
dev_info(dev, "quirk IN2_MAP enabled");
|
||||||
|
if (quirk & BYT_CHT_ES8316_SSP0)
|
||||||
|
dev_info(dev, "quirk SSP0 enabled");
|
||||||
|
if (quirk & BYT_CHT_ES8316_MONO_SPEAKER)
|
||||||
|
dev_info(dev, "quirk MONO_SPEAKER enabled\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int byt_cht_es8316_speaker_power_event(struct snd_soc_dapm_widget *w,
|
||||||
|
struct snd_kcontrol *kcontrol, int event)
|
||||||
|
{
|
||||||
|
struct snd_soc_card *card = w->dapm->card;
|
||||||
|
struct byt_cht_es8316_private *priv = snd_soc_card_get_drvdata(card);
|
||||||
|
|
||||||
|
if (SND_SOC_DAPM_EVENT_ON(event))
|
||||||
|
priv->speaker_en = true;
|
||||||
|
else
|
||||||
|
priv->speaker_en = false;
|
||||||
|
|
||||||
|
gpiod_set_value_cansleep(priv->speaker_en_gpio, priv->speaker_en);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct snd_soc_dapm_widget byt_cht_es8316_widgets[] = {
|
||||||
|
SND_SOC_DAPM_SPK("Speaker", NULL),
|
||||||
|
SND_SOC_DAPM_HP("Headphone", NULL),
|
||||||
|
SND_SOC_DAPM_MIC("Headset Mic", NULL),
|
||||||
|
SND_SOC_DAPM_MIC("Internal Mic", NULL),
|
||||||
|
|
||||||
|
SND_SOC_DAPM_SUPPLY("Speaker Power", SND_SOC_NOPM, 0, 0,
|
||||||
|
byt_cht_es8316_speaker_power_event,
|
||||||
|
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct snd_soc_dapm_route byt_cht_es8316_audio_map[] = {
|
static const struct snd_soc_dapm_route byt_cht_es8316_audio_map[] = {
|
||||||
{"MIC1", NULL, "Microphone 1"},
|
|
||||||
{"MIC2", NULL, "Microphone 2"},
|
|
||||||
|
|
||||||
{"Headphone", NULL, "HPOL"},
|
{"Headphone", NULL, "HPOL"},
|
||||||
{"Headphone", NULL, "HPOR"},
|
{"Headphone", NULL, "HPOR"},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There is no separate speaker output instead the speakers are muxed to
|
||||||
|
* the HP outputs. The mux is controlled by the "Speaker Power" supply.
|
||||||
|
*/
|
||||||
|
{"Speaker", NULL, "HPOL"},
|
||||||
|
{"Speaker", NULL, "HPOR"},
|
||||||
|
{"Speaker", NULL, "Speaker Power"},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct snd_soc_dapm_route byt_cht_es8316_intmic_in1_map[] = {
|
||||||
|
{"MIC1", NULL, "Internal Mic"},
|
||||||
|
{"MIC2", NULL, "Headset Mic"},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct snd_soc_dapm_route byt_cht_es8316_intmic_in2_map[] = {
|
||||||
|
{"MIC2", NULL, "Internal Mic"},
|
||||||
|
{"MIC1", NULL, "Headset Mic"},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct snd_soc_dapm_route byt_cht_es8316_ssp0_map[] = {
|
||||||
|
{"Playback", NULL, "ssp0 Tx"},
|
||||||
|
{"ssp0 Tx", NULL, "modem_out"},
|
||||||
|
{"modem_in", NULL, "ssp0 Rx"},
|
||||||
|
{"ssp0 Rx", NULL, "Capture"},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct snd_soc_dapm_route byt_cht_es8316_ssp2_map[] = {
|
||||||
{"Playback", NULL, "ssp2 Tx"},
|
{"Playback", NULL, "ssp2 Tx"},
|
||||||
{"ssp2 Tx", NULL, "codec_out0"},
|
{"ssp2 Tx", NULL, "codec_out0"},
|
||||||
{"ssp2 Tx", NULL, "codec_out1"},
|
{"ssp2 Tx", NULL, "codec_out1"},
|
||||||
@ -65,19 +141,60 @@ static const struct snd_soc_dapm_route byt_cht_es8316_audio_map[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const struct snd_kcontrol_new byt_cht_es8316_controls[] = {
|
static const struct snd_kcontrol_new byt_cht_es8316_controls[] = {
|
||||||
|
SOC_DAPM_PIN_SWITCH("Speaker"),
|
||||||
SOC_DAPM_PIN_SWITCH("Headphone"),
|
SOC_DAPM_PIN_SWITCH("Headphone"),
|
||||||
SOC_DAPM_PIN_SWITCH("Microphone 1"),
|
SOC_DAPM_PIN_SWITCH("Headset Mic"),
|
||||||
SOC_DAPM_PIN_SWITCH("Microphone 2"),
|
SOC_DAPM_PIN_SWITCH("Internal Mic"),
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct snd_soc_jack_pin byt_cht_es8316_jack_pins[] = {
|
||||||
|
{
|
||||||
|
.pin = "Headphone",
|
||||||
|
.mask = SND_JACK_HEADPHONE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.pin = "Headset Mic",
|
||||||
|
.mask = SND_JACK_MICROPHONE,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static int byt_cht_es8316_init(struct snd_soc_pcm_runtime *runtime)
|
static int byt_cht_es8316_init(struct snd_soc_pcm_runtime *runtime)
|
||||||
{
|
{
|
||||||
|
struct snd_soc_component *codec = runtime->codec_dai->component;
|
||||||
struct snd_soc_card *card = runtime->card;
|
struct snd_soc_card *card = runtime->card;
|
||||||
struct byt_cht_es8316_private *priv = snd_soc_card_get_drvdata(card);
|
struct byt_cht_es8316_private *priv = snd_soc_card_get_drvdata(card);
|
||||||
|
const struct snd_soc_dapm_route *custom_map;
|
||||||
|
int num_routes;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
card->dapm.idle_bias_off = true;
|
card->dapm.idle_bias_off = true;
|
||||||
|
|
||||||
|
switch (BYT_CHT_ES8316_MAP(quirk)) {
|
||||||
|
case BYT_CHT_ES8316_INTMIC_IN1_MAP:
|
||||||
|
default:
|
||||||
|
custom_map = byt_cht_es8316_intmic_in1_map;
|
||||||
|
num_routes = ARRAY_SIZE(byt_cht_es8316_intmic_in1_map);
|
||||||
|
break;
|
||||||
|
case BYT_CHT_ES8316_INTMIC_IN2_MAP:
|
||||||
|
custom_map = byt_cht_es8316_intmic_in2_map;
|
||||||
|
num_routes = ARRAY_SIZE(byt_cht_es8316_intmic_in2_map);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (quirk & BYT_CHT_ES8316_SSP0) {
|
||||||
|
custom_map = byt_cht_es8316_ssp0_map;
|
||||||
|
num_routes = ARRAY_SIZE(byt_cht_es8316_ssp0_map);
|
||||||
|
} else {
|
||||||
|
custom_map = byt_cht_es8316_ssp2_map;
|
||||||
|
num_routes = ARRAY_SIZE(byt_cht_es8316_ssp2_map);
|
||||||
|
}
|
||||||
|
ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The firmware might enable the clock at boot (this information
|
* The firmware might enable the clock at boot (this information
|
||||||
* may or may not be reflected in the enable clock register).
|
* may or may not be reflected in the enable clock register).
|
||||||
@ -105,6 +222,18 @@ static int byt_cht_es8316_init(struct snd_soc_pcm_runtime *runtime)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = snd_soc_card_jack_new(card, "Headset",
|
||||||
|
SND_JACK_HEADSET | SND_JACK_BTN_0,
|
||||||
|
&priv->jack, byt_cht_es8316_jack_pins,
|
||||||
|
ARRAY_SIZE(byt_cht_es8316_jack_pins));
|
||||||
|
if (ret) {
|
||||||
|
dev_err(card->dev, "jack creation failed %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
snd_jack_set_key(priv->jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
|
||||||
|
snd_soc_component_set_jack(codec, &priv->jack, NULL);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,14 +252,21 @@ static int byt_cht_es8316_codec_fixup(struct snd_soc_pcm_runtime *rtd,
|
|||||||
SNDRV_PCM_HW_PARAM_RATE);
|
SNDRV_PCM_HW_PARAM_RATE);
|
||||||
struct snd_interval *channels = hw_param_interval(params,
|
struct snd_interval *channels = hw_param_interval(params,
|
||||||
SNDRV_PCM_HW_PARAM_CHANNELS);
|
SNDRV_PCM_HW_PARAM_CHANNELS);
|
||||||
int ret;
|
int ret, bits;
|
||||||
|
|
||||||
/* The DSP will covert the FE rate to 48k, stereo */
|
/* The DSP will covert the FE rate to 48k, stereo */
|
||||||
rate->min = rate->max = 48000;
|
rate->min = rate->max = 48000;
|
||||||
channels->min = channels->max = 2;
|
channels->min = channels->max = 2;
|
||||||
|
|
||||||
/* set SSP2 to 24-bit */
|
if (quirk & BYT_CHT_ES8316_SSP0) {
|
||||||
params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
|
/* set SSP0 to 16-bit */
|
||||||
|
params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
|
||||||
|
bits = 16;
|
||||||
|
} else {
|
||||||
|
/* set SSP2 to 24-bit */
|
||||||
|
params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
|
||||||
|
bits = 24;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Default mode for SSP configuration is TDM 4 slot, override config
|
* Default mode for SSP configuration is TDM 4 slot, override config
|
||||||
@ -147,7 +283,7 @@ static int byt_cht_es8316_codec_fixup(struct snd_soc_pcm_runtime *rtd,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24);
|
ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, bits);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
|
dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
|
||||||
return ret;
|
return ret;
|
||||||
@ -218,6 +354,59 @@ static struct snd_soc_dai_link byt_cht_es8316_dais[] = {
|
|||||||
|
|
||||||
|
|
||||||
/* SoC card */
|
/* SoC card */
|
||||||
|
static char codec_name[SND_ACPI_I2C_ID_LEN];
|
||||||
|
static char long_name[50]; /* = "bytcht-es8316-*-spk-*-mic" */
|
||||||
|
|
||||||
|
static int byt_cht_es8316_suspend(struct snd_soc_card *card)
|
||||||
|
{
|
||||||
|
struct snd_soc_component *component;
|
||||||
|
|
||||||
|
for_each_card_components(card, component) {
|
||||||
|
if (!strcmp(component->name, codec_name)) {
|
||||||
|
dev_dbg(component->dev, "disabling jack detect before suspend\n");
|
||||||
|
snd_soc_component_set_jack(component, NULL, NULL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int byt_cht_es8316_resume(struct snd_soc_card *card)
|
||||||
|
{
|
||||||
|
struct byt_cht_es8316_private *priv = snd_soc_card_get_drvdata(card);
|
||||||
|
struct snd_soc_component *component;
|
||||||
|
|
||||||
|
for_each_card_components(card, component) {
|
||||||
|
if (!strcmp(component->name, codec_name)) {
|
||||||
|
dev_dbg(component->dev, "re-enabling jack detect after resume\n");
|
||||||
|
snd_soc_component_set_jack(component, &priv->jack, NULL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Some Cherry Trail boards with an ES8316 codec have a bug in their
|
||||||
|
* ACPI tables where the MSSL1680 touchscreen's _PS0 and _PS3 methods
|
||||||
|
* wrongly also set the speaker-enable GPIO to 1/0. Testing has shown
|
||||||
|
* that this really is a bug and the GPIO has no influence on the
|
||||||
|
* touchscreen at all.
|
||||||
|
*
|
||||||
|
* The silead.c touchscreen driver does not support runtime suspend, so
|
||||||
|
* the GPIO can only be changed underneath us during a system suspend.
|
||||||
|
* This resume() function runs from a pm complete() callback, and thus
|
||||||
|
* is guaranteed to run after the touchscreen driver/ACPI-subsys has
|
||||||
|
* brought the touchscreen back up again (and thus changed the GPIO).
|
||||||
|
*
|
||||||
|
* So to work around this we pass GPIOD_FLAGS_BIT_NONEXCLUSIVE when
|
||||||
|
* requesting the GPIO and we set its value here to undo any changes
|
||||||
|
* done by the touchscreen's broken _PS0 ACPI method.
|
||||||
|
*/
|
||||||
|
gpiod_set_value_cansleep(priv->speaker_en_gpio, priv->speaker_en);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static struct snd_soc_card byt_cht_es8316_card = {
|
static struct snd_soc_card byt_cht_es8316_card = {
|
||||||
.name = "bytcht-es8316",
|
.name = "bytcht-es8316",
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
@ -230,24 +419,39 @@ static struct snd_soc_card byt_cht_es8316_card = {
|
|||||||
.controls = byt_cht_es8316_controls,
|
.controls = byt_cht_es8316_controls,
|
||||||
.num_controls = ARRAY_SIZE(byt_cht_es8316_controls),
|
.num_controls = ARRAY_SIZE(byt_cht_es8316_controls),
|
||||||
.fully_routed = true,
|
.fully_routed = true,
|
||||||
|
.suspend_pre = byt_cht_es8316_suspend,
|
||||||
|
.resume_post = byt_cht_es8316_resume,
|
||||||
};
|
};
|
||||||
|
|
||||||
static char codec_name[SND_ACPI_I2C_ID_LEN];
|
static const struct x86_cpu_id baytrail_cpu_ids[] = {
|
||||||
|
{ X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_SILVERMONT }, /* Valleyview */
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct acpi_gpio_params first_gpio = { 0, 0, false };
|
||||||
|
|
||||||
|
static const struct acpi_gpio_mapping byt_cht_es8316_gpios[] = {
|
||||||
|
{ "speaker-enable-gpios", &first_gpio, 1 },
|
||||||
|
{ },
|
||||||
|
};
|
||||||
|
|
||||||
static int snd_byt_cht_es8316_mc_probe(struct platform_device *pdev)
|
static int snd_byt_cht_es8316_mc_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
|
const char * const mic_name[] = { "in1", "in2" };
|
||||||
struct byt_cht_es8316_private *priv;
|
struct byt_cht_es8316_private *priv;
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
struct snd_soc_acpi_mach *mach;
|
struct snd_soc_acpi_mach *mach;
|
||||||
const char *i2c_name = NULL;
|
const char *i2c_name = NULL;
|
||||||
|
struct device *codec_dev;
|
||||||
int dai_index = 0;
|
int dai_index = 0;
|
||||||
int i;
|
int i;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||||
if (!priv)
|
if (!priv)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
mach = (&pdev->dev)->platform_data;
|
mach = dev->platform_data;
|
||||||
/* fix index of codec dai */
|
/* fix index of codec dai */
|
||||||
for (i = 0; i < ARRAY_SIZE(byt_cht_es8316_dais); i++) {
|
for (i = 0; i < ARRAY_SIZE(byt_cht_es8316_dais); i++) {
|
||||||
if (!strcmp(byt_cht_es8316_dais[i].codec_name,
|
if (!strcmp(byt_cht_es8316_dais[i].codec_name,
|
||||||
@ -265,26 +469,85 @@ static int snd_byt_cht_es8316_mc_probe(struct platform_device *pdev)
|
|||||||
byt_cht_es8316_dais[dai_index].codec_name = codec_name;
|
byt_cht_es8316_dais[dai_index].codec_name = codec_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* register the soc card */
|
/* Check for BYTCR or other platform and setup quirks */
|
||||||
byt_cht_es8316_card.dev = &pdev->dev;
|
if (x86_match_cpu(baytrail_cpu_ids) &&
|
||||||
snd_soc_card_set_drvdata(&byt_cht_es8316_card, priv);
|
mach->mach_params.acpi_ipc_irq_index == 0) {
|
||||||
|
/* On BYTCR default to SSP0, internal-mic-in2-map, mono-spk */
|
||||||
|
quirk = BYT_CHT_ES8316_SSP0 | BYT_CHT_ES8316_INTMIC_IN2_MAP |
|
||||||
|
BYT_CHT_ES8316_MONO_SPEAKER;
|
||||||
|
} else {
|
||||||
|
/* Others default to internal-mic-in1-map, mono-speaker */
|
||||||
|
quirk = BYT_CHT_ES8316_INTMIC_IN1_MAP |
|
||||||
|
BYT_CHT_ES8316_MONO_SPEAKER;
|
||||||
|
}
|
||||||
|
if (quirk_override != -1) {
|
||||||
|
dev_info(dev, "Overriding quirk 0x%x => 0x%x\n", quirk,
|
||||||
|
quirk_override);
|
||||||
|
quirk = quirk_override;
|
||||||
|
}
|
||||||
|
log_quirks(dev);
|
||||||
|
|
||||||
priv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3");
|
if (quirk & BYT_CHT_ES8316_SSP0)
|
||||||
|
byt_cht_es8316_dais[dai_index].cpu_dai_name = "ssp0-port";
|
||||||
|
|
||||||
|
/* get the clock */
|
||||||
|
priv->mclk = devm_clk_get(dev, "pmc_plt_clk_3");
|
||||||
if (IS_ERR(priv->mclk)) {
|
if (IS_ERR(priv->mclk)) {
|
||||||
ret = PTR_ERR(priv->mclk);
|
ret = PTR_ERR(priv->mclk);
|
||||||
dev_err(&pdev->dev,
|
dev_err(dev, "clk_get pmc_plt_clk_3 failed: %d\n", ret);
|
||||||
"Failed to get MCLK from pmc_plt_clk_3: %d\n",
|
|
||||||
ret);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = devm_snd_soc_register_card(&pdev->dev, &byt_cht_es8316_card);
|
/* get speaker enable GPIO */
|
||||||
|
codec_dev = bus_find_device_by_name(&i2c_bus_type, NULL, codec_name);
|
||||||
|
if (!codec_dev)
|
||||||
|
return -EPROBE_DEFER;
|
||||||
|
|
||||||
|
devm_acpi_dev_add_driver_gpios(codec_dev, byt_cht_es8316_gpios);
|
||||||
|
priv->speaker_en_gpio =
|
||||||
|
gpiod_get_index(codec_dev, "speaker-enable", 0,
|
||||||
|
/* see comment in byt_cht_es8316_resume */
|
||||||
|
GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE);
|
||||||
|
put_device(codec_dev);
|
||||||
|
|
||||||
|
if (IS_ERR(priv->speaker_en_gpio)) {
|
||||||
|
ret = PTR_ERR(priv->speaker_en_gpio);
|
||||||
|
switch (ret) {
|
||||||
|
case -ENOENT:
|
||||||
|
priv->speaker_en_gpio = NULL;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dev_err(dev, "get speaker GPIO failed: %d\n", ret);
|
||||||
|
/* fall through */
|
||||||
|
case -EPROBE_DEFER:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* register the soc card */
|
||||||
|
snprintf(long_name, sizeof(long_name), "bytcht-es8316-%s-spk-%s-mic",
|
||||||
|
(quirk & BYT_CHT_ES8316_MONO_SPEAKER) ? "mono" : "stereo",
|
||||||
|
mic_name[BYT_CHT_ES8316_MAP(quirk)]);
|
||||||
|
byt_cht_es8316_card.long_name = long_name;
|
||||||
|
byt_cht_es8316_card.dev = dev;
|
||||||
|
snd_soc_card_set_drvdata(&byt_cht_es8316_card, priv);
|
||||||
|
|
||||||
|
ret = devm_snd_soc_register_card(dev, &byt_cht_es8316_card);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "snd_soc_register_card failed %d\n", ret);
|
gpiod_put(priv->speaker_en_gpio);
|
||||||
|
dev_err(dev, "snd_soc_register_card failed: %d\n", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
platform_set_drvdata(pdev, &byt_cht_es8316_card);
|
platform_set_drvdata(pdev, &byt_cht_es8316_card);
|
||||||
return ret;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int snd_byt_cht_es8316_mc_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct byt_cht_es8316_private *priv = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
gpiod_put(priv->speaker_en_gpio);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct platform_driver snd_byt_cht_es8316_mc_driver = {
|
static struct platform_driver snd_byt_cht_es8316_mc_driver = {
|
||||||
@ -292,6 +555,7 @@ static struct platform_driver snd_byt_cht_es8316_mc_driver = {
|
|||||||
.name = "bytcht_es8316",
|
.name = "bytcht_es8316",
|
||||||
},
|
},
|
||||||
.probe = snd_byt_cht_es8316_mc_probe,
|
.probe = snd_byt_cht_es8316_mc_probe,
|
||||||
|
.remove = snd_byt_cht_es8316_mc_remove,
|
||||||
};
|
};
|
||||||
|
|
||||||
module_platform_driver(snd_byt_cht_es8316_mc_driver);
|
module_platform_driver(snd_byt_cht_es8316_mc_driver);
|
||||||
|
@ -428,6 +428,18 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = {
|
|||||||
BYT_RT5640_SSP0_AIF1 |
|
BYT_RT5640_SSP0_AIF1 |
|
||||||
BYT_RT5640_MCLK_EN),
|
BYT_RT5640_MCLK_EN),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.matches = {
|
||||||
|
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
||||||
|
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ME176C"),
|
||||||
|
},
|
||||||
|
.driver_data = (void *)(BYT_RT5640_IN1_MAP |
|
||||||
|
BYT_RT5640_JD_SRC_JD2_IN4N |
|
||||||
|
BYT_RT5640_OVCD_TH_2000UA |
|
||||||
|
BYT_RT5640_OVCD_SF_0P75 |
|
||||||
|
BYT_RT5640_SSP0_AIF1 |
|
||||||
|
BYT_RT5640_MCLK_EN),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.matches = {
|
.matches = {
|
||||||
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
||||||
|
@ -164,7 +164,7 @@ static int geminilake_ssp_fixup(struct snd_soc_pcm_runtime *rtd,
|
|||||||
|
|
||||||
/* set SSP to 24 bit */
|
/* set SSP to 24 bit */
|
||||||
snd_mask_none(fmt);
|
snd_mask_none(fmt);
|
||||||
snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE);
|
snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -146,7 +146,7 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = {
|
|||||||
.stream_name = "Loopback",
|
.stream_name = "Loopback",
|
||||||
.cpu_dai_name = "Loopback Pin",
|
.cpu_dai_name = "Loopback Pin",
|
||||||
.platform_name = "haswell-pcm-audio",
|
.platform_name = "haswell-pcm-audio",
|
||||||
.dynamic = 0,
|
.dynamic = 1,
|
||||||
.codec_name = "snd-soc-dummy",
|
.codec_name = "snd-soc-dummy",
|
||||||
.codec_dai_name = "snd-soc-dummy-dai",
|
.codec_dai_name = "snd-soc-dummy-dai",
|
||||||
.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
|
.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
|
||||||
|
@ -221,7 +221,7 @@ static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd,
|
|||||||
rate->min = rate->max = 48000;
|
rate->min = rate->max = 48000;
|
||||||
channels->min = channels->max = 2;
|
channels->min = channels->max = 2;
|
||||||
snd_mask_none(fmt);
|
snd_mask_none(fmt);
|
||||||
snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE);
|
snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -229,7 +229,7 @@ static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd,
|
|||||||
* thus changing the mask here
|
* thus changing the mask here
|
||||||
*/
|
*/
|
||||||
if (!strcmp(be_dai_link->name, "SSP0-Codec"))
|
if (!strcmp(be_dai_link->name, "SSP0-Codec"))
|
||||||
snd_mask_set(fmt, SNDRV_PCM_FORMAT_S16_LE);
|
snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -154,6 +154,15 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = {
|
|||||||
.sof_tplg_filename = "intel/sof-byt-da7213.tplg",
|
.sof_tplg_filename = "intel/sof-byt-da7213.tplg",
|
||||||
.asoc_plat_name = "sst-mfld-platform",
|
.asoc_plat_name = "sst-mfld-platform",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.id = "ESSX8316",
|
||||||
|
.drv_name = "bytcht_es8316",
|
||||||
|
.fw_filename = "intel/fw_sst_0f28.bin",
|
||||||
|
.board = "bytcht_es8316",
|
||||||
|
.sof_fw_filename = "intel/sof-byt.ri",
|
||||||
|
.sof_tplg_filename = "intel/sof-byt-es8316.tplg",
|
||||||
|
.asoc_plat_name = "sst-mfld-platform",
|
||||||
|
},
|
||||||
/* some Baytrail platforms rely on RT5645, use CHT machine driver */
|
/* some Baytrail platforms rely on RT5645, use CHT machine driver */
|
||||||
{
|
{
|
||||||
.id = "10EC5645",
|
.id = "10EC5645",
|
||||||
|
@ -1216,7 +1216,7 @@ int sst_hsw_stream_commit(struct sst_hsw *hsw, struct sst_hsw_stream *stream)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream->commited = 1;
|
stream->commited = true;
|
||||||
trace_hsw_stream_alloc_reply(stream);
|
trace_hsw_stream_alloc_reply(stream);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -544,7 +544,7 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
|
|||||||
dev_err(rtd->dev, "error: invalid DAI ID %d\n",
|
dev_err(rtd->dev, "error: invalid DAI ID %d\n",
|
||||||
rtd->cpu_dai->id);
|
rtd->cpu_dai->id);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
};
|
}
|
||||||
|
|
||||||
ret = sst_hsw_stream_format(hsw, pcm_data->stream,
|
ret = sst_hsw_stream_format(hsw, pcm_data->stream,
|
||||||
path_id, stream_type, SST_HSW_STREAM_FORMAT_PCM_FORMAT);
|
path_id, stream_type, SST_HSW_STREAM_FORMAT_PCM_FORMAT);
|
||||||
@ -861,7 +861,7 @@ static int hsw_pcm_close(struct snd_pcm_substream *substream)
|
|||||||
dev_dbg(rtd->dev, "error: free stream failed %d\n", ret);
|
dev_dbg(rtd->dev, "error: free stream failed %d\n", ret);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
pcm_data->allocated = 0;
|
pcm_data->allocated = false;
|
||||||
pcm_data->stream = NULL;
|
pcm_data->stream = NULL;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
@ -416,7 +416,7 @@ int skl_resume_dsp(struct skl *skl)
|
|||||||
snd_hdac_ext_bus_ppcap_int_enable(bus, true);
|
snd_hdac_ext_bus_ppcap_int_enable(bus, true);
|
||||||
|
|
||||||
/* check if DSP 1st boot is done */
|
/* check if DSP 1st boot is done */
|
||||||
if (skl->skl_sst->is_first_boot == true)
|
if (skl->skl_sst->is_first_boot)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1423,7 +1423,7 @@ static int skl_platform_soc_probe(struct snd_soc_component *component)
|
|||||||
if (!ops)
|
if (!ops)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
if (skl->skl_sst->is_first_boot == false) {
|
if (!skl->skl_sst->is_first_boot) {
|
||||||
dev_err(component->dev, "DSP reports first boot done!!!\n");
|
dev_err(component->dev, "DSP reports first boot done!!!\n");
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
@ -3103,7 +3103,7 @@ static int skl_init_algo_data(struct device *dev, struct soc_bytes_ext *be,
|
|||||||
ac->size = dfw_ac->max;
|
ac->size = dfw_ac->max;
|
||||||
|
|
||||||
if (ac->max) {
|
if (ac->max) {
|
||||||
ac->params = (char *) devm_kzalloc(dev, ac->max, GFP_KERNEL);
|
ac->params = devm_kzalloc(dev, ac->max, GFP_KERNEL);
|
||||||
if (!ac->params)
|
if (!ac->params)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -570,10 +570,10 @@ static int q6asm_dai_compr_open(struct snd_compr_stream *stream)
|
|||||||
prtd->audio_client = q6asm_audio_client_alloc(dev,
|
prtd->audio_client = q6asm_audio_client_alloc(dev,
|
||||||
(q6asm_cb)compress_event_handler,
|
(q6asm_cb)compress_event_handler,
|
||||||
prtd, stream_id, LEGACY_PCM_MODE);
|
prtd, stream_id, LEGACY_PCM_MODE);
|
||||||
if (!prtd->audio_client) {
|
if (IS_ERR(prtd->audio_client)) {
|
||||||
dev_err(dev, "Could not allocate memory\n");
|
dev_err(dev, "Could not allocate memory\n");
|
||||||
kfree(prtd);
|
ret = PTR_ERR(prtd->audio_client);
|
||||||
return -ENOMEM;
|
goto free_prtd;
|
||||||
}
|
}
|
||||||
|
|
||||||
size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE *
|
size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE *
|
||||||
@ -582,7 +582,7 @@ static int q6asm_dai_compr_open(struct snd_compr_stream *stream)
|
|||||||
&prtd->dma_buffer);
|
&prtd->dma_buffer);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "Cannot allocate buffer(s)\n");
|
dev_err(dev, "Cannot allocate buffer(s)\n");
|
||||||
return ret;
|
goto free_client;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pdata->sid < 0)
|
if (pdata->sid < 0)
|
||||||
@ -595,6 +595,13 @@ static int q6asm_dai_compr_open(struct snd_compr_stream *stream)
|
|||||||
runtime->private_data = prtd;
|
runtime->private_data = prtd;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
free_client:
|
||||||
|
q6asm_audio_client_free(prtd->audio_client);
|
||||||
|
free_prtd:
|
||||||
|
kfree(prtd);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int q6asm_dai_compr_free(struct snd_compr_stream *stream)
|
static int q6asm_dai_compr_free(struct snd_compr_stream *stream)
|
||||||
@ -874,7 +881,7 @@ static int of_q6asm_parse_dai_data(struct device *dev,
|
|||||||
|
|
||||||
for_each_child_of_node(dev->of_node, node) {
|
for_each_child_of_node(dev->of_node, node) {
|
||||||
ret = of_property_read_u32(node, "reg", &id);
|
ret = of_property_read_u32(node, "reg", &id);
|
||||||
if (ret || id > MAX_SESSIONS || id < 0) {
|
if (ret || id >= MAX_SESSIONS || id < 0) {
|
||||||
dev_err(dev, "valid dai id not found:%d\n", ret);
|
dev_err(dev, "valid dai id not found:%d\n", ret);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -158,17 +158,24 @@ static int sdm845_snd_hw_params(struct snd_pcm_substream *substream,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sdm845_jack_free(struct snd_jack *jack)
|
||||||
|
{
|
||||||
|
struct snd_soc_component *component = jack->private_data;
|
||||||
|
|
||||||
|
snd_soc_component_set_jack(component, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd)
|
static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd)
|
||||||
{
|
{
|
||||||
struct snd_soc_component *component;
|
struct snd_soc_component *component;
|
||||||
struct snd_soc_dai_link *dai_link = rtd->dai_link;
|
|
||||||
struct snd_soc_card *card = rtd->card;
|
struct snd_soc_card *card = rtd->card;
|
||||||
|
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||||
|
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||||
struct sdm845_snd_data *pdata = snd_soc_card_get_drvdata(card);
|
struct sdm845_snd_data *pdata = snd_soc_card_get_drvdata(card);
|
||||||
int i, rval;
|
struct snd_jack *jack;
|
||||||
|
int rval;
|
||||||
|
|
||||||
if (!pdata->jack_setup) {
|
if (!pdata->jack_setup) {
|
||||||
struct snd_jack *jack;
|
|
||||||
|
|
||||||
rval = snd_soc_card_jack_new(card, "Headset Jack",
|
rval = snd_soc_card_jack_new(card, "Headset Jack",
|
||||||
SND_JACK_HEADSET |
|
SND_JACK_HEADSET |
|
||||||
SND_JACK_HEADPHONE |
|
SND_JACK_HEADPHONE |
|
||||||
@ -190,16 +197,22 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd)
|
|||||||
pdata->jack_setup = true;
|
pdata->jack_setup = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0 ; i < dai_link->num_codecs; i++) {
|
switch (cpu_dai->id) {
|
||||||
struct snd_soc_dai *dai = rtd->codec_dais[i];
|
case PRIMARY_MI2S_RX:
|
||||||
|
jack = pdata->jack.jack;
|
||||||
|
component = codec_dai->component;
|
||||||
|
|
||||||
component = dai->component;
|
jack->private_data = component;
|
||||||
rval = snd_soc_component_set_jack(
|
jack->private_free = sdm845_jack_free;
|
||||||
component, &pdata->jack, NULL);
|
rval = snd_soc_component_set_jack(component,
|
||||||
|
&pdata->jack, NULL);
|
||||||
if (rval != 0 && rval != -ENOTSUPP) {
|
if (rval != 0 && rval != -ENOTSUPP) {
|
||||||
dev_warn(card->dev, "Failed to set jack: %d\n", rval);
|
dev_warn(card->dev, "Failed to set jack: %d\n", rval);
|
||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -202,7 +202,7 @@ static int camelot_prepare(struct snd_pcm_substream *substream)
|
|||||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||||
struct camelot_pcm *cam = &cam_pcm_data[rtd->cpu_dai->id];
|
struct camelot_pcm *cam = &cam_pcm_data[rtd->cpu_dai->id];
|
||||||
|
|
||||||
pr_debug("PCM data: addr 0x%08ulx len %d\n",
|
pr_debug("PCM data: addr 0x%08lx len %d\n",
|
||||||
(u32)runtime->dma_addr, runtime->dma_bytes);
|
(u32)runtime->dma_addr, runtime->dma_bytes);
|
||||||
|
|
||||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include <linux/platform_data/davinci_asp.h>
|
#include <linux/platform_data/davinci_asp.h>
|
||||||
#include <linux/math64.h>
|
#include <linux/math64.h>
|
||||||
#include <linux/bitmap.h>
|
#include <linux/bitmap.h>
|
||||||
|
#include <linux/gpio/driver.h>
|
||||||
|
|
||||||
#include <sound/asoundef.h>
|
#include <sound/asoundef.h>
|
||||||
#include <sound/core.h>
|
#include <sound/core.h>
|
||||||
@ -54,6 +55,7 @@ static u32 context_regs[] = {
|
|||||||
DAVINCI_MCASP_AHCLKXCTL_REG,
|
DAVINCI_MCASP_AHCLKXCTL_REG,
|
||||||
DAVINCI_MCASP_AHCLKRCTL_REG,
|
DAVINCI_MCASP_AHCLKRCTL_REG,
|
||||||
DAVINCI_MCASP_PDIR_REG,
|
DAVINCI_MCASP_PDIR_REG,
|
||||||
|
DAVINCI_MCASP_PFUNC_REG,
|
||||||
DAVINCI_MCASP_RXMASK_REG,
|
DAVINCI_MCASP_RXMASK_REG,
|
||||||
DAVINCI_MCASP_TXMASK_REG,
|
DAVINCI_MCASP_TXMASK_REG,
|
||||||
DAVINCI_MCASP_RXTDM_REG,
|
DAVINCI_MCASP_RXTDM_REG,
|
||||||
@ -108,7 +110,11 @@ struct davinci_mcasp {
|
|||||||
/* Used for comstraint setting on the second stream */
|
/* Used for comstraint setting on the second stream */
|
||||||
u32 channels;
|
u32 channels;
|
||||||
|
|
||||||
#ifdef CONFIG_PM_SLEEP
|
#ifdef CONFIG_GPIOLIB
|
||||||
|
struct gpio_chip gpio_chip;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
struct davinci_mcasp_context context;
|
struct davinci_mcasp_context context;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -818,9 +824,6 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream,
|
|||||||
if (mcasp->version < MCASP_VERSION_3)
|
if (mcasp->version < MCASP_VERSION_3)
|
||||||
mcasp_set_bits(mcasp, DAVINCI_MCASP_PWREMUMGT_REG, MCASP_SOFT);
|
mcasp_set_bits(mcasp, DAVINCI_MCASP_PWREMUMGT_REG, MCASP_SOFT);
|
||||||
|
|
||||||
/* All PINS as McASP */
|
|
||||||
mcasp_set_reg(mcasp, DAVINCI_MCASP_PFUNC_REG, 0x00000000);
|
|
||||||
|
|
||||||
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||||
mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
|
mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
|
||||||
mcasp_clr_bits(mcasp, DAVINCI_MCASP_XEVTCTL_REG, TXDATADMADIS);
|
mcasp_clr_bits(mcasp, DAVINCI_MCASP_XEVTCTL_REG, TXDATADMADIS);
|
||||||
@ -1486,74 +1489,6 @@ static int davinci_mcasp_dai_probe(struct snd_soc_dai *dai)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM_SLEEP
|
|
||||||
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->pm_state = pm_runtime_active(mcasp->dev);
|
|
||||||
if (!context->pm_state)
|
|
||||||
pm_runtime_get_sync(mcasp->dev);
|
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
pm_runtime_put_sync(mcasp->dev);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
pm_runtime_get_sync(mcasp->dev);
|
|
||||||
|
|
||||||
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]);
|
|
||||||
|
|
||||||
if (!context->pm_state)
|
|
||||||
pm_runtime_put_sync(mcasp->dev);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#define davinci_mcasp_suspend NULL
|
|
||||||
#define davinci_mcasp_resume NULL
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define DAVINCI_MCASP_RATES SNDRV_PCM_RATE_8000_192000
|
#define DAVINCI_MCASP_RATES SNDRV_PCM_RATE_8000_192000
|
||||||
|
|
||||||
#define DAVINCI_MCASP_PCM_FMTS (SNDRV_PCM_FMTBIT_S8 | \
|
#define DAVINCI_MCASP_PCM_FMTS (SNDRV_PCM_FMTBIT_S8 | \
|
||||||
@ -1571,8 +1506,6 @@ static struct snd_soc_dai_driver davinci_mcasp_dai[] = {
|
|||||||
{
|
{
|
||||||
.name = "davinci-mcasp.0",
|
.name = "davinci-mcasp.0",
|
||||||
.probe = davinci_mcasp_dai_probe,
|
.probe = davinci_mcasp_dai_probe,
|
||||||
.suspend = davinci_mcasp_suspend,
|
|
||||||
.resume = davinci_mcasp_resume,
|
|
||||||
.playback = {
|
.playback = {
|
||||||
.channels_min = 1,
|
.channels_min = 1,
|
||||||
.channels_max = 32 * 16,
|
.channels_max = 32 * 16,
|
||||||
@ -1915,6 +1848,147 @@ static u32 davinci_mcasp_rxdma_offset(struct davinci_mcasp_pdata *pdata)
|
|||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_GPIOLIB
|
||||||
|
static int davinci_mcasp_gpio_request(struct gpio_chip *chip, unsigned offset)
|
||||||
|
{
|
||||||
|
struct davinci_mcasp *mcasp = gpiochip_get_data(chip);
|
||||||
|
|
||||||
|
if (mcasp->num_serializer && offset < mcasp->num_serializer &&
|
||||||
|
mcasp->serial_dir[offset] != INACTIVE_MODE) {
|
||||||
|
dev_err(mcasp->dev, "AXR%u pin is used for audio\n", offset);
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do not change the PIN yet */
|
||||||
|
|
||||||
|
return pm_runtime_get_sync(mcasp->dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void davinci_mcasp_gpio_free(struct gpio_chip *chip, unsigned offset)
|
||||||
|
{
|
||||||
|
struct davinci_mcasp *mcasp = gpiochip_get_data(chip);
|
||||||
|
|
||||||
|
/* Set the direction to input */
|
||||||
|
mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(offset));
|
||||||
|
|
||||||
|
/* Set the pin as McASP pin */
|
||||||
|
mcasp_clr_bits(mcasp, DAVINCI_MCASP_PFUNC_REG, BIT(offset));
|
||||||
|
|
||||||
|
pm_runtime_put_sync(mcasp->dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int davinci_mcasp_gpio_direction_out(struct gpio_chip *chip,
|
||||||
|
unsigned offset, int value)
|
||||||
|
{
|
||||||
|
struct davinci_mcasp *mcasp = gpiochip_get_data(chip);
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
if (value)
|
||||||
|
mcasp_set_bits(mcasp, DAVINCI_MCASP_PDOUT_REG, BIT(offset));
|
||||||
|
else
|
||||||
|
mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDOUT_REG, BIT(offset));
|
||||||
|
|
||||||
|
val = mcasp_get_reg(mcasp, DAVINCI_MCASP_PFUNC_REG);
|
||||||
|
if (!(val & BIT(offset))) {
|
||||||
|
/* Set the pin as GPIO pin */
|
||||||
|
mcasp_set_bits(mcasp, DAVINCI_MCASP_PFUNC_REG, BIT(offset));
|
||||||
|
|
||||||
|
/* Set the direction to output */
|
||||||
|
mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void davinci_mcasp_gpio_set(struct gpio_chip *chip, unsigned offset,
|
||||||
|
int value)
|
||||||
|
{
|
||||||
|
struct davinci_mcasp *mcasp = gpiochip_get_data(chip);
|
||||||
|
|
||||||
|
if (value)
|
||||||
|
mcasp_set_bits(mcasp, DAVINCI_MCASP_PDOUT_REG, BIT(offset));
|
||||||
|
else
|
||||||
|
mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDOUT_REG, BIT(offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int davinci_mcasp_gpio_direction_in(struct gpio_chip *chip,
|
||||||
|
unsigned offset)
|
||||||
|
{
|
||||||
|
struct davinci_mcasp *mcasp = gpiochip_get_data(chip);
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
val = mcasp_get_reg(mcasp, DAVINCI_MCASP_PFUNC_REG);
|
||||||
|
if (!(val & BIT(offset))) {
|
||||||
|
/* Set the direction to input */
|
||||||
|
mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(offset));
|
||||||
|
|
||||||
|
/* Set the pin as GPIO pin */
|
||||||
|
mcasp_set_bits(mcasp, DAVINCI_MCASP_PFUNC_REG, BIT(offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int davinci_mcasp_gpio_get(struct gpio_chip *chip, unsigned offset)
|
||||||
|
{
|
||||||
|
struct davinci_mcasp *mcasp = gpiochip_get_data(chip);
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
val = mcasp_get_reg(mcasp, DAVINCI_MCASP_PDSET_REG);
|
||||||
|
if (val & BIT(offset))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int davinci_mcasp_gpio_get_direction(struct gpio_chip *chip,
|
||||||
|
unsigned offset)
|
||||||
|
{
|
||||||
|
struct davinci_mcasp *mcasp = gpiochip_get_data(chip);
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
val = mcasp_get_reg(mcasp, DAVINCI_MCASP_PDIR_REG);
|
||||||
|
if (val & BIT(offset))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct gpio_chip davinci_mcasp_template_chip = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.request = davinci_mcasp_gpio_request,
|
||||||
|
.free = davinci_mcasp_gpio_free,
|
||||||
|
.direction_output = davinci_mcasp_gpio_direction_out,
|
||||||
|
.set = davinci_mcasp_gpio_set,
|
||||||
|
.direction_input = davinci_mcasp_gpio_direction_in,
|
||||||
|
.get = davinci_mcasp_gpio_get,
|
||||||
|
.get_direction = davinci_mcasp_gpio_get_direction,
|
||||||
|
.base = -1,
|
||||||
|
.ngpio = 32,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int davinci_mcasp_init_gpiochip(struct davinci_mcasp *mcasp)
|
||||||
|
{
|
||||||
|
if (!of_property_read_bool(mcasp->dev->of_node, "gpio-controller"))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
mcasp->gpio_chip = davinci_mcasp_template_chip;
|
||||||
|
mcasp->gpio_chip.label = dev_name(mcasp->dev);
|
||||||
|
mcasp->gpio_chip.parent = mcasp->dev;
|
||||||
|
#ifdef CONFIG_OF_GPIO
|
||||||
|
mcasp->gpio_chip.of_node = mcasp->dev->of_node;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return devm_gpiochip_add_data(mcasp->dev, &mcasp->gpio_chip, mcasp);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* CONFIG_GPIOLIB */
|
||||||
|
static inline int davinci_mcasp_init_gpiochip(struct davinci_mcasp *mcasp)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_GPIOLIB */
|
||||||
|
|
||||||
static int davinci_mcasp_probe(struct platform_device *pdev)
|
static int davinci_mcasp_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct snd_dmaengine_dai_dma_data *dma_data;
|
struct snd_dmaengine_dai_dma_data *dma_data;
|
||||||
@ -1976,7 +2050,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
mcasp->num_serializer = pdata->num_serializer;
|
mcasp->num_serializer = pdata->num_serializer;
|
||||||
#ifdef CONFIG_PM_SLEEP
|
#ifdef CONFIG_PM
|
||||||
mcasp->context.xrsr_regs = devm_kcalloc(&pdev->dev,
|
mcasp->context.xrsr_regs = devm_kcalloc(&pdev->dev,
|
||||||
mcasp->num_serializer, sizeof(u32),
|
mcasp->num_serializer, sizeof(u32),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
@ -2139,6 +2213,15 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
mcasp_reparent_fck(pdev);
|
mcasp_reparent_fck(pdev);
|
||||||
|
|
||||||
|
/* All PINS as McASP */
|
||||||
|
pm_runtime_get_sync(mcasp->dev);
|
||||||
|
mcasp_set_reg(mcasp, DAVINCI_MCASP_PFUNC_REG, 0x00000000);
|
||||||
|
pm_runtime_put(mcasp->dev);
|
||||||
|
|
||||||
|
ret = davinci_mcasp_init_gpiochip(mcasp);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
ret = devm_snd_soc_register_component(&pdev->dev,
|
ret = devm_snd_soc_register_component(&pdev->dev,
|
||||||
&davinci_mcasp_component,
|
&davinci_mcasp_component,
|
||||||
&davinci_mcasp_dai[pdata->op_mode], 1);
|
&davinci_mcasp_dai[pdata->op_mode], 1);
|
||||||
@ -2149,26 +2232,10 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
|
|||||||
ret = davinci_mcasp_get_dma_type(mcasp);
|
ret = davinci_mcasp_get_dma_type(mcasp);
|
||||||
switch (ret) {
|
switch (ret) {
|
||||||
case PCM_EDMA:
|
case PCM_EDMA:
|
||||||
#if IS_BUILTIN(CONFIG_SND_SOC_TI_EDMA_PCM) || \
|
|
||||||
(IS_MODULE(CONFIG_SND_SOC_DAVINCI_MCASP) && \
|
|
||||||
IS_MODULE(CONFIG_SND_SOC_TI_EDMA_PCM))
|
|
||||||
ret = edma_pcm_platform_register(&pdev->dev);
|
ret = edma_pcm_platform_register(&pdev->dev);
|
||||||
#else
|
|
||||||
dev_err(&pdev->dev, "Missing SND_EDMA_SOC\n");
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto err;
|
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
case PCM_SDMA:
|
case PCM_SDMA:
|
||||||
#if IS_BUILTIN(CONFIG_SND_SOC_TI_SDMA_PCM) || \
|
|
||||||
(IS_MODULE(CONFIG_SND_SOC_DAVINCI_MCASP) && \
|
|
||||||
IS_MODULE(CONFIG_SND_SOC_TI_SDMA_PCM))
|
|
||||||
ret = sdma_pcm_platform_register(&pdev->dev, NULL, NULL);
|
ret = sdma_pcm_platform_register(&pdev->dev, NULL, NULL);
|
||||||
#else
|
|
||||||
dev_err(&pdev->dev, "Missing SND_SDMA_SOC\n");
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto err;
|
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
dev_err(&pdev->dev, "No DMA controller found (%d)\n", ret);
|
dev_err(&pdev->dev, "No DMA controller found (%d)\n", ret);
|
||||||
@ -2196,11 +2263,73 @@ static int davinci_mcasp_remove(struct platform_device *pdev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
static int davinci_mcasp_runtime_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct davinci_mcasp *mcasp = dev_get_drvdata(dev);
|
||||||
|
struct davinci_mcasp_context *context = &mcasp->context;
|
||||||
|
u32 reg;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int davinci_mcasp_runtime_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct davinci_mcasp *mcasp = dev_get_drvdata(dev);
|
||||||
|
struct davinci_mcasp_context *context = &mcasp->context;
|
||||||
|
u32 reg;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const struct dev_pm_ops davinci_mcasp_pm_ops = {
|
||||||
|
SET_RUNTIME_PM_OPS(davinci_mcasp_runtime_suspend,
|
||||||
|
davinci_mcasp_runtime_resume,
|
||||||
|
NULL)
|
||||||
|
};
|
||||||
|
|
||||||
static struct platform_driver davinci_mcasp_driver = {
|
static struct platform_driver davinci_mcasp_driver = {
|
||||||
.probe = davinci_mcasp_probe,
|
.probe = davinci_mcasp_probe,
|
||||||
.remove = davinci_mcasp_remove,
|
.remove = davinci_mcasp_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "davinci-mcasp",
|
.name = "davinci-mcasp",
|
||||||
|
.pm = &davinci_mcasp_pm_ops,
|
||||||
.of_match_table = mcasp_dt_ids,
|
.of_match_table = mcasp_dt_ids,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,8 +1,15 @@
|
|||||||
config SND_SOC_XILINX_I2S
|
config SND_SOC_XILINX_I2S
|
||||||
tristate "Audio support for the the Xilinx I2S"
|
tristate "Audio support for the Xilinx I2S"
|
||||||
help
|
help
|
||||||
Select this option to enable Xilinx I2S Audio. This enables
|
Select this option to enable Xilinx I2S Audio. This enables
|
||||||
I2S playback and capture using xilinx soft IP. In transmitter
|
I2S playback and capture using xilinx soft IP. In transmitter
|
||||||
mode, IP receives audio in AES format, extracts PCM and sends
|
mode, IP receives audio in AES format, extracts PCM and sends
|
||||||
PCM data. In receiver mode, IP receives PCM audio and
|
PCM data. In receiver mode, IP receives PCM audio and
|
||||||
encapsulates PCM in AES format and sends AES data.
|
encapsulates PCM in AES format and sends AES data.
|
||||||
|
|
||||||
|
config SND_SOC_XILINX_AUDIO_FORMATTER
|
||||||
|
tristate "Audio support for the the Xilinx audio formatter"
|
||||||
|
help
|
||||||
|
Select this option to enable Xilinx audio formatter
|
||||||
|
support. This provides DMA platform device support for
|
||||||
|
audio functionality.
|
||||||
|
@ -1,2 +1,4 @@
|
|||||||
snd-soc-xlnx-i2s-objs := xlnx_i2s.o
|
snd-soc-xlnx-i2s-objs := xlnx_i2s.o
|
||||||
obj-$(CONFIG_SND_SOC_XILINX_I2S) += snd-soc-xlnx-i2s.o
|
obj-$(CONFIG_SND_SOC_XILINX_I2S) += snd-soc-xlnx-i2s.o
|
||||||
|
snd-soc-xlnx-formatter-pcm-objs := xlnx_formatter_pcm.o
|
||||||
|
obj-$(CONFIG_SND_SOC_XILINX_AUDIO_FORMATTER) += snd-soc-xlnx-formatter-pcm.o
|
||||||
|
565
sound/soc/xilinx/xlnx_formatter_pcm.c
Normal file
565
sound/soc/xilinx/xlnx_formatter_pcm.c
Normal file
@ -0,0 +1,565 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
//
|
||||||
|
// Xilinx ASoC audio formatter support
|
||||||
|
//
|
||||||
|
// Copyright (C) 2018 Xilinx, Inc.
|
||||||
|
//
|
||||||
|
// Author: Maruthi Srinivas Bayyavarapu <maruthis@xilinx.com>
|
||||||
|
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of_address.h>
|
||||||
|
#include <linux/of_irq.h>
|
||||||
|
#include <linux/sizes.h>
|
||||||
|
|
||||||
|
#include <sound/soc.h>
|
||||||
|
#include <sound/pcm_params.h>
|
||||||
|
|
||||||
|
#define DRV_NAME "xlnx_formatter_pcm"
|
||||||
|
|
||||||
|
#define XLNX_S2MM_OFFSET 0
|
||||||
|
#define XLNX_MM2S_OFFSET 0x100
|
||||||
|
|
||||||
|
#define XLNX_AUD_CORE_CONFIG 0x4
|
||||||
|
#define XLNX_AUD_CTRL 0x10
|
||||||
|
#define XLNX_AUD_STS 0x14
|
||||||
|
|
||||||
|
#define AUD_CTRL_RESET_MASK BIT(1)
|
||||||
|
#define AUD_CFG_MM2S_MASK BIT(15)
|
||||||
|
#define AUD_CFG_S2MM_MASK BIT(31)
|
||||||
|
|
||||||
|
#define XLNX_AUD_FS_MULTIPLIER 0x18
|
||||||
|
#define XLNX_AUD_PERIOD_CONFIG 0x1C
|
||||||
|
#define XLNX_AUD_BUFF_ADDR_LSB 0x20
|
||||||
|
#define XLNX_AUD_BUFF_ADDR_MSB 0x24
|
||||||
|
#define XLNX_AUD_XFER_COUNT 0x28
|
||||||
|
#define XLNX_AUD_CH_STS_START 0x2C
|
||||||
|
#define XLNX_BYTES_PER_CH 0x44
|
||||||
|
|
||||||
|
#define AUD_STS_IOC_IRQ_MASK BIT(31)
|
||||||
|
#define AUD_STS_CH_STS_MASK BIT(29)
|
||||||
|
#define AUD_CTRL_IOC_IRQ_MASK BIT(13)
|
||||||
|
#define AUD_CTRL_TOUT_IRQ_MASK BIT(14)
|
||||||
|
#define AUD_CTRL_DMA_EN_MASK BIT(0)
|
||||||
|
|
||||||
|
#define CFG_MM2S_CH_MASK GENMASK(11, 8)
|
||||||
|
#define CFG_MM2S_CH_SHIFT 8
|
||||||
|
#define CFG_MM2S_XFER_MASK GENMASK(14, 13)
|
||||||
|
#define CFG_MM2S_XFER_SHIFT 13
|
||||||
|
#define CFG_MM2S_PKG_MASK BIT(12)
|
||||||
|
|
||||||
|
#define CFG_S2MM_CH_MASK GENMASK(27, 24)
|
||||||
|
#define CFG_S2MM_CH_SHIFT 24
|
||||||
|
#define CFG_S2MM_XFER_MASK GENMASK(30, 29)
|
||||||
|
#define CFG_S2MM_XFER_SHIFT 29
|
||||||
|
#define CFG_S2MM_PKG_MASK BIT(28)
|
||||||
|
|
||||||
|
#define AUD_CTRL_DATA_WIDTH_SHIFT 16
|
||||||
|
#define AUD_CTRL_ACTIVE_CH_SHIFT 19
|
||||||
|
#define PERIOD_CFG_PERIODS_SHIFT 16
|
||||||
|
|
||||||
|
#define PERIODS_MIN 2
|
||||||
|
#define PERIODS_MAX 6
|
||||||
|
#define PERIOD_BYTES_MIN 192
|
||||||
|
#define PERIOD_BYTES_MAX (50 * 1024)
|
||||||
|
|
||||||
|
enum bit_depth {
|
||||||
|
BIT_DEPTH_8,
|
||||||
|
BIT_DEPTH_16,
|
||||||
|
BIT_DEPTH_20,
|
||||||
|
BIT_DEPTH_24,
|
||||||
|
BIT_DEPTH_32,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct xlnx_pcm_drv_data {
|
||||||
|
void __iomem *mmio;
|
||||||
|
bool s2mm_presence;
|
||||||
|
bool mm2s_presence;
|
||||||
|
int s2mm_irq;
|
||||||
|
int mm2s_irq;
|
||||||
|
struct snd_pcm_substream *play_stream;
|
||||||
|
struct snd_pcm_substream *capture_stream;
|
||||||
|
struct clk *axi_clk;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* struct xlnx_pcm_stream_param - stream configuration
|
||||||
|
* @mmio: base address offset
|
||||||
|
* @interleaved: audio channels arrangement in buffer
|
||||||
|
* @xfer_mode: data formatting mode during transfer
|
||||||
|
* @ch_limit: Maximum channels supported
|
||||||
|
* @buffer_size: stream ring buffer size
|
||||||
|
*/
|
||||||
|
struct xlnx_pcm_stream_param {
|
||||||
|
void __iomem *mmio;
|
||||||
|
bool interleaved;
|
||||||
|
u32 xfer_mode;
|
||||||
|
u32 ch_limit;
|
||||||
|
u64 buffer_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct snd_pcm_hardware xlnx_pcm_hardware = {
|
||||||
|
.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||||
|
SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_PAUSE |
|
||||||
|
SNDRV_PCM_INFO_RESUME,
|
||||||
|
.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |
|
||||||
|
SNDRV_PCM_FMTBIT_S24_LE,
|
||||||
|
.channels_min = 2,
|
||||||
|
.channels_max = 2,
|
||||||
|
.rates = SNDRV_PCM_RATE_8000_192000,
|
||||||
|
.rate_min = 8000,
|
||||||
|
.rate_max = 192000,
|
||||||
|
.buffer_bytes_max = PERIODS_MAX * PERIOD_BYTES_MAX,
|
||||||
|
.period_bytes_min = PERIOD_BYTES_MIN,
|
||||||
|
.period_bytes_max = PERIOD_BYTES_MAX,
|
||||||
|
.periods_min = PERIODS_MIN,
|
||||||
|
.periods_max = PERIODS_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int xlnx_formatter_pcm_reset(void __iomem *mmio_base)
|
||||||
|
{
|
||||||
|
u32 val, retries = 0;
|
||||||
|
|
||||||
|
val = readl(mmio_base + XLNX_AUD_CTRL);
|
||||||
|
val |= AUD_CTRL_RESET_MASK;
|
||||||
|
writel(val, mmio_base + XLNX_AUD_CTRL);
|
||||||
|
|
||||||
|
val = readl(mmio_base + XLNX_AUD_CTRL);
|
||||||
|
/* Poll for maximum timeout of approximately 100ms (1 * 100)*/
|
||||||
|
while ((val & AUD_CTRL_RESET_MASK) && (retries < 100)) {
|
||||||
|
mdelay(1);
|
||||||
|
retries++;
|
||||||
|
val = readl(mmio_base + XLNX_AUD_CTRL);
|
||||||
|
}
|
||||||
|
if (val & AUD_CTRL_RESET_MASK)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xlnx_formatter_disable_irqs(void __iomem *mmio_base, int stream)
|
||||||
|
{
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
val = readl(mmio_base + XLNX_AUD_CTRL);
|
||||||
|
val &= ~AUD_CTRL_IOC_IRQ_MASK;
|
||||||
|
if (stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||||
|
val &= ~AUD_CTRL_TOUT_IRQ_MASK;
|
||||||
|
|
||||||
|
writel(val, mmio_base + XLNX_AUD_CTRL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t xlnx_mm2s_irq_handler(int irq, void *arg)
|
||||||
|
{
|
||||||
|
u32 val;
|
||||||
|
void __iomem *reg;
|
||||||
|
struct device *dev = arg;
|
||||||
|
struct xlnx_pcm_drv_data *adata = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
reg = adata->mmio + XLNX_MM2S_OFFSET + XLNX_AUD_STS;
|
||||||
|
val = readl(reg);
|
||||||
|
if (val & AUD_STS_IOC_IRQ_MASK) {
|
||||||
|
writel(val & AUD_STS_IOC_IRQ_MASK, reg);
|
||||||
|
if (adata->play_stream)
|
||||||
|
snd_pcm_period_elapsed(adata->play_stream);
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return IRQ_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t xlnx_s2mm_irq_handler(int irq, void *arg)
|
||||||
|
{
|
||||||
|
u32 val;
|
||||||
|
void __iomem *reg;
|
||||||
|
struct device *dev = arg;
|
||||||
|
struct xlnx_pcm_drv_data *adata = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
reg = adata->mmio + XLNX_S2MM_OFFSET + XLNX_AUD_STS;
|
||||||
|
val = readl(reg);
|
||||||
|
if (val & AUD_STS_IOC_IRQ_MASK) {
|
||||||
|
writel(val & AUD_STS_IOC_IRQ_MASK, reg);
|
||||||
|
if (adata->capture_stream)
|
||||||
|
snd_pcm_period_elapsed(adata->capture_stream);
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return IRQ_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xlnx_formatter_pcm_open(struct snd_pcm_substream *substream)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
u32 val, data_format_mode;
|
||||||
|
u32 ch_count_mask, ch_count_shift, data_xfer_mode, data_xfer_shift;
|
||||||
|
struct xlnx_pcm_stream_param *stream_data;
|
||||||
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||||
|
struct snd_soc_pcm_runtime *prtd = substream->private_data;
|
||||||
|
struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd,
|
||||||
|
DRV_NAME);
|
||||||
|
struct xlnx_pcm_drv_data *adata = dev_get_drvdata(component->dev);
|
||||||
|
|
||||||
|
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
|
||||||
|
!adata->mm2s_presence)
|
||||||
|
return -ENODEV;
|
||||||
|
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE &&
|
||||||
|
!adata->s2mm_presence)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
stream_data = kzalloc(sizeof(*stream_data), GFP_KERNEL);
|
||||||
|
if (!stream_data)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||||
|
ch_count_mask = CFG_MM2S_CH_MASK;
|
||||||
|
ch_count_shift = CFG_MM2S_CH_SHIFT;
|
||||||
|
data_xfer_mode = CFG_MM2S_XFER_MASK;
|
||||||
|
data_xfer_shift = CFG_MM2S_XFER_SHIFT;
|
||||||
|
data_format_mode = CFG_MM2S_PKG_MASK;
|
||||||
|
stream_data->mmio = adata->mmio + XLNX_MM2S_OFFSET;
|
||||||
|
adata->play_stream = substream;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ch_count_mask = CFG_S2MM_CH_MASK;
|
||||||
|
ch_count_shift = CFG_S2MM_CH_SHIFT;
|
||||||
|
data_xfer_mode = CFG_S2MM_XFER_MASK;
|
||||||
|
data_xfer_shift = CFG_S2MM_XFER_SHIFT;
|
||||||
|
data_format_mode = CFG_S2MM_PKG_MASK;
|
||||||
|
stream_data->mmio = adata->mmio + XLNX_S2MM_OFFSET;
|
||||||
|
adata->capture_stream = substream;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = readl(adata->mmio + XLNX_AUD_CORE_CONFIG);
|
||||||
|
|
||||||
|
if (!(val & data_format_mode))
|
||||||
|
stream_data->interleaved = true;
|
||||||
|
|
||||||
|
stream_data->xfer_mode = (val & data_xfer_mode) >> data_xfer_shift;
|
||||||
|
stream_data->ch_limit = (val & ch_count_mask) >> ch_count_shift;
|
||||||
|
dev_info(component->dev,
|
||||||
|
"stream %d : format = %d mode = %d ch_limit = %d\n",
|
||||||
|
substream->stream, stream_data->interleaved,
|
||||||
|
stream_data->xfer_mode, stream_data->ch_limit);
|
||||||
|
|
||||||
|
snd_soc_set_runtime_hwparams(substream, &xlnx_pcm_hardware);
|
||||||
|
runtime->private_data = stream_data;
|
||||||
|
|
||||||
|
/* Resize the period size divisible by 64 */
|
||||||
|
err = snd_pcm_hw_constraint_step(runtime, 0,
|
||||||
|
SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64);
|
||||||
|
if (err) {
|
||||||
|
dev_err(component->dev,
|
||||||
|
"unable to set constraint on period bytes\n");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* enable DMA IOC irq */
|
||||||
|
val = readl(stream_data->mmio + XLNX_AUD_CTRL);
|
||||||
|
val |= AUD_CTRL_IOC_IRQ_MASK;
|
||||||
|
writel(val, stream_data->mmio + XLNX_AUD_CTRL);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xlnx_formatter_pcm_close(struct snd_pcm_substream *substream)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct xlnx_pcm_stream_param *stream_data =
|
||||||
|
substream->runtime->private_data;
|
||||||
|
struct snd_soc_pcm_runtime *prtd = substream->private_data;
|
||||||
|
struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd,
|
||||||
|
DRV_NAME);
|
||||||
|
|
||||||
|
ret = xlnx_formatter_pcm_reset(stream_data->mmio);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(component->dev, "audio formatter reset failed\n");
|
||||||
|
goto err_reset;
|
||||||
|
}
|
||||||
|
xlnx_formatter_disable_irqs(stream_data->mmio, substream->stream);
|
||||||
|
|
||||||
|
err_reset:
|
||||||
|
kfree(stream_data);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static snd_pcm_uframes_t
|
||||||
|
xlnx_formatter_pcm_pointer(struct snd_pcm_substream *substream)
|
||||||
|
{
|
||||||
|
u32 pos;
|
||||||
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||||
|
struct xlnx_pcm_stream_param *stream_data = runtime->private_data;
|
||||||
|
|
||||||
|
pos = readl(stream_data->mmio + XLNX_AUD_XFER_COUNT);
|
||||||
|
|
||||||
|
if (pos >= stream_data->buffer_size)
|
||||||
|
pos = 0;
|
||||||
|
|
||||||
|
return bytes_to_frames(runtime, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xlnx_formatter_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||||
|
struct snd_pcm_hw_params *params)
|
||||||
|
{
|
||||||
|
u32 low, high, active_ch, val, bytes_per_ch, bits_per_sample;
|
||||||
|
int status;
|
||||||
|
u64 size;
|
||||||
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||||
|
struct xlnx_pcm_stream_param *stream_data = runtime->private_data;
|
||||||
|
|
||||||
|
active_ch = params_channels(params);
|
||||||
|
if (active_ch > stream_data->ch_limit)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
size = params_buffer_bytes(params);
|
||||||
|
status = snd_pcm_lib_malloc_pages(substream, size);
|
||||||
|
if (status < 0)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
stream_data->buffer_size = size;
|
||||||
|
|
||||||
|
low = lower_32_bits(substream->dma_buffer.addr);
|
||||||
|
high = upper_32_bits(substream->dma_buffer.addr);
|
||||||
|
writel(low, stream_data->mmio + XLNX_AUD_BUFF_ADDR_LSB);
|
||||||
|
writel(high, stream_data->mmio + XLNX_AUD_BUFF_ADDR_MSB);
|
||||||
|
|
||||||
|
val = readl(stream_data->mmio + XLNX_AUD_CTRL);
|
||||||
|
bits_per_sample = params_width(params);
|
||||||
|
switch (bits_per_sample) {
|
||||||
|
case 8:
|
||||||
|
val |= (BIT_DEPTH_8 << AUD_CTRL_DATA_WIDTH_SHIFT);
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
val |= (BIT_DEPTH_16 << AUD_CTRL_DATA_WIDTH_SHIFT);
|
||||||
|
break;
|
||||||
|
case 20:
|
||||||
|
val |= (BIT_DEPTH_20 << AUD_CTRL_DATA_WIDTH_SHIFT);
|
||||||
|
break;
|
||||||
|
case 24:
|
||||||
|
val |= (BIT_DEPTH_24 << AUD_CTRL_DATA_WIDTH_SHIFT);
|
||||||
|
break;
|
||||||
|
case 32:
|
||||||
|
val |= (BIT_DEPTH_32 << AUD_CTRL_DATA_WIDTH_SHIFT);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
val |= active_ch << AUD_CTRL_ACTIVE_CH_SHIFT;
|
||||||
|
writel(val, stream_data->mmio + XLNX_AUD_CTRL);
|
||||||
|
|
||||||
|
val = (params_periods(params) << PERIOD_CFG_PERIODS_SHIFT)
|
||||||
|
| params_period_bytes(params);
|
||||||
|
writel(val, stream_data->mmio + XLNX_AUD_PERIOD_CONFIG);
|
||||||
|
bytes_per_ch = DIV_ROUND_UP(params_period_bytes(params), active_ch);
|
||||||
|
writel(bytes_per_ch, stream_data->mmio + XLNX_BYTES_PER_CH);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xlnx_formatter_pcm_hw_free(struct snd_pcm_substream *substream)
|
||||||
|
{
|
||||||
|
return snd_pcm_lib_free_pages(substream);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xlnx_formatter_pcm_trigger(struct snd_pcm_substream *substream,
|
||||||
|
int cmd)
|
||||||
|
{
|
||||||
|
u32 val;
|
||||||
|
struct xlnx_pcm_stream_param *stream_data =
|
||||||
|
substream->runtime->private_data;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case SNDRV_PCM_TRIGGER_START:
|
||||||
|
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||||
|
case SNDRV_PCM_TRIGGER_RESUME:
|
||||||
|
val = readl(stream_data->mmio + XLNX_AUD_CTRL);
|
||||||
|
val |= AUD_CTRL_DMA_EN_MASK;
|
||||||
|
writel(val, stream_data->mmio + XLNX_AUD_CTRL);
|
||||||
|
break;
|
||||||
|
case SNDRV_PCM_TRIGGER_STOP:
|
||||||
|
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||||
|
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||||
|
val = readl(stream_data->mmio + XLNX_AUD_CTRL);
|
||||||
|
val &= ~AUD_CTRL_DMA_EN_MASK;
|
||||||
|
writel(val, stream_data->mmio + XLNX_AUD_CTRL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xlnx_formatter_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||||
|
{
|
||||||
|
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd,
|
||||||
|
DRV_NAME);
|
||||||
|
return snd_pcm_lib_preallocate_pages_for_all(rtd->pcm,
|
||||||
|
SNDRV_DMA_TYPE_DEV, component->dev,
|
||||||
|
xlnx_pcm_hardware.buffer_bytes_max,
|
||||||
|
xlnx_pcm_hardware.buffer_bytes_max);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct snd_pcm_ops xlnx_formatter_pcm_ops = {
|
||||||
|
.open = xlnx_formatter_pcm_open,
|
||||||
|
.close = xlnx_formatter_pcm_close,
|
||||||
|
.ioctl = snd_pcm_lib_ioctl,
|
||||||
|
.hw_params = xlnx_formatter_pcm_hw_params,
|
||||||
|
.hw_free = xlnx_formatter_pcm_hw_free,
|
||||||
|
.trigger = xlnx_formatter_pcm_trigger,
|
||||||
|
.pointer = xlnx_formatter_pcm_pointer,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct snd_soc_component_driver xlnx_asoc_component = {
|
||||||
|
.name = DRV_NAME,
|
||||||
|
.ops = &xlnx_formatter_pcm_ops,
|
||||||
|
.pcm_new = xlnx_formatter_pcm_new,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int xlnx_formatter_pcm_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
u32 val;
|
||||||
|
struct xlnx_pcm_drv_data *aud_drv_data;
|
||||||
|
struct resource *res;
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
|
||||||
|
aud_drv_data = devm_kzalloc(dev, sizeof(*aud_drv_data), GFP_KERNEL);
|
||||||
|
if (!aud_drv_data)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
aud_drv_data->axi_clk = devm_clk_get(dev, "s_axi_lite_aclk");
|
||||||
|
if (IS_ERR(aud_drv_data->axi_clk)) {
|
||||||
|
ret = PTR_ERR(aud_drv_data->axi_clk);
|
||||||
|
dev_err(dev, "failed to get s_axi_lite_aclk(%d)\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
ret = clk_prepare_enable(aud_drv_data->axi_clk);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev,
|
||||||
|
"failed to enable s_axi_lite_aclk(%d)\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
if (!res) {
|
||||||
|
dev_err(dev, "audio formatter node:addr to resource failed\n");
|
||||||
|
ret = -ENXIO;
|
||||||
|
goto clk_err;
|
||||||
|
}
|
||||||
|
aud_drv_data->mmio = devm_ioremap_resource(dev, res);
|
||||||
|
if (IS_ERR(aud_drv_data->mmio)) {
|
||||||
|
dev_err(dev, "audio formatter ioremap failed\n");
|
||||||
|
ret = PTR_ERR(aud_drv_data->mmio);
|
||||||
|
goto clk_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = readl(aud_drv_data->mmio + XLNX_AUD_CORE_CONFIG);
|
||||||
|
if (val & AUD_CFG_MM2S_MASK) {
|
||||||
|
aud_drv_data->mm2s_presence = true;
|
||||||
|
ret = xlnx_formatter_pcm_reset(aud_drv_data->mmio +
|
||||||
|
XLNX_MM2S_OFFSET);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "audio formatter reset failed\n");
|
||||||
|
goto clk_err;
|
||||||
|
}
|
||||||
|
xlnx_formatter_disable_irqs(aud_drv_data->mmio +
|
||||||
|
XLNX_MM2S_OFFSET,
|
||||||
|
SNDRV_PCM_STREAM_PLAYBACK);
|
||||||
|
|
||||||
|
aud_drv_data->mm2s_irq = platform_get_irq_byname(pdev,
|
||||||
|
"irq_mm2s");
|
||||||
|
if (aud_drv_data->mm2s_irq < 0) {
|
||||||
|
dev_err(dev, "xlnx audio mm2s irq resource failed\n");
|
||||||
|
ret = aud_drv_data->mm2s_irq;
|
||||||
|
goto clk_err;
|
||||||
|
}
|
||||||
|
ret = devm_request_irq(dev, aud_drv_data->mm2s_irq,
|
||||||
|
xlnx_mm2s_irq_handler, 0,
|
||||||
|
"xlnx_formatter_pcm_mm2s_irq", dev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "xlnx audio mm2s irq request failed\n");
|
||||||
|
goto clk_err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (val & AUD_CFG_S2MM_MASK) {
|
||||||
|
aud_drv_data->s2mm_presence = true;
|
||||||
|
ret = xlnx_formatter_pcm_reset(aud_drv_data->mmio +
|
||||||
|
XLNX_S2MM_OFFSET);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "audio formatter reset failed\n");
|
||||||
|
goto clk_err;
|
||||||
|
}
|
||||||
|
xlnx_formatter_disable_irqs(aud_drv_data->mmio +
|
||||||
|
XLNX_S2MM_OFFSET,
|
||||||
|
SNDRV_PCM_STREAM_CAPTURE);
|
||||||
|
|
||||||
|
aud_drv_data->s2mm_irq = platform_get_irq_byname(pdev,
|
||||||
|
"irq_s2mm");
|
||||||
|
if (aud_drv_data->s2mm_irq < 0) {
|
||||||
|
dev_err(dev, "xlnx audio s2mm irq resource failed\n");
|
||||||
|
ret = aud_drv_data->s2mm_irq;
|
||||||
|
goto clk_err;
|
||||||
|
}
|
||||||
|
ret = devm_request_irq(dev, aud_drv_data->s2mm_irq,
|
||||||
|
xlnx_s2mm_irq_handler, 0,
|
||||||
|
"xlnx_formatter_pcm_s2mm_irq",
|
||||||
|
dev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "xlnx audio s2mm irq request failed\n");
|
||||||
|
goto clk_err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_set_drvdata(dev, aud_drv_data);
|
||||||
|
|
||||||
|
ret = devm_snd_soc_register_component(dev, &xlnx_asoc_component,
|
||||||
|
NULL, 0);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "pcm platform device register failed\n");
|
||||||
|
goto clk_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
clk_err:
|
||||||
|
clk_disable_unprepare(aud_drv_data->axi_clk);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xlnx_formatter_pcm_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
struct xlnx_pcm_drv_data *adata = dev_get_drvdata(&pdev->dev);
|
||||||
|
|
||||||
|
if (adata->s2mm_presence)
|
||||||
|
ret = xlnx_formatter_pcm_reset(adata->mmio + XLNX_S2MM_OFFSET);
|
||||||
|
|
||||||
|
/* Try MM2S reset, even if S2MM reset fails */
|
||||||
|
if (adata->mm2s_presence)
|
||||||
|
ret = xlnx_formatter_pcm_reset(adata->mmio + XLNX_MM2S_OFFSET);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
dev_err(&pdev->dev, "audio formatter reset failed\n");
|
||||||
|
|
||||||
|
clk_disable_unprepare(adata->axi_clk);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id xlnx_formatter_pcm_of_match[] = {
|
||||||
|
{ .compatible = "xlnx,audio-formatter-1.0"},
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, xlnx_formatter_pcm_of_match);
|
||||||
|
|
||||||
|
static struct platform_driver xlnx_formatter_pcm_driver = {
|
||||||
|
.probe = xlnx_formatter_pcm_probe,
|
||||||
|
.remove = xlnx_formatter_pcm_remove,
|
||||||
|
.driver = {
|
||||||
|
.name = DRV_NAME,
|
||||||
|
.of_match_table = xlnx_formatter_pcm_of_match,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(xlnx_formatter_pcm_driver);
|
||||||
|
MODULE_AUTHOR("Maruthi Srinivas Bayyavarapu <maruthis@xilinx.com>");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
@ -1,12 +1,11 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
//
|
||||||
* Xilinx ASoC I2S audio support
|
// Xilinx ASoC I2S audio support
|
||||||
*
|
//
|
||||||
* Copyright (C) 2018 Xilinx, Inc.
|
// Copyright (C) 2018 Xilinx, Inc.
|
||||||
*
|
//
|
||||||
* Author: Praveen Vuppala <praveenv@xilinx.com>
|
// Author: Praveen Vuppala <praveenv@xilinx.com>
|
||||||
* Author: Maruthi Srinivas Bayyavarapu <maruthis@xilinx.com>
|
// Author: Maruthi Srinivas Bayyavarapu <maruthis@xilinx.com>
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
Loading…
Reference in New Issue
Block a user