mirror of
https://github.com/torvalds/linux.git
synced 2024-11-24 05:02:12 +00:00
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
|
||||
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:
|
||||
|
||||
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 */
|
||||
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;
|
||||
|
||||
/* now codec parameters */
|
||||
|
@ -65,6 +65,7 @@ config SND_SOC_ALL_CODECS
|
||||
select SND_SOC_CS4271_SPI if SPI_MASTER
|
||||
select SND_SOC_CS42XX8_I2C 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_CS47L24 if MFD_CS47L24
|
||||
select SND_SOC_CS53L30 if I2C
|
||||
@ -129,6 +130,7 @@ config SND_SOC_ALL_CODECS
|
||||
select SND_SOC_PCM5102A
|
||||
select SND_SOC_PCM512x_I2C if I2C
|
||||
select SND_SOC_PCM512x_SPI if SPI_MASTER
|
||||
select SND_SOC_RK3328
|
||||
select SND_SOC_RT274 if I2C
|
||||
select SND_SOC_RT286 if I2C
|
||||
select SND_SOC_RT298 if I2C
|
||||
@ -542,6 +544,12 @@ config SND_SOC_CS43130
|
||||
tristate "Cirrus Logic CS43130 CODEC"
|
||||
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
|
||||
config SND_SOC_CS4349
|
||||
tristate "Cirrus Logic CS4349 CODEC"
|
||||
@ -799,6 +807,10 @@ config SND_SOC_PCM512x_SPI
|
||||
select SND_SOC_PCM512x
|
||||
select REGMAP_SPI
|
||||
|
||||
config SND_SOC_RK3328
|
||||
tristate "Rockchip RK3328 audio CODEC"
|
||||
select REGMAP_MMIO
|
||||
|
||||
config SND_SOC_RL6231
|
||||
tristate
|
||||
default y if SND_SOC_RT5514=y
|
||||
@ -1211,7 +1223,8 @@ config SND_SOC_WM8903
|
||||
depends on I2C
|
||||
|
||||
config SND_SOC_WM8904
|
||||
tristate
|
||||
tristate "Wolfson Microelectronics WM8904 CODEC"
|
||||
depends on I2C
|
||||
|
||||
config SND_SOC_WM8940
|
||||
tristate
|
||||
|
@ -60,6 +60,7 @@ snd-soc-cs4271-spi-objs := cs4271-spi.o
|
||||
snd-soc-cs42xx8-objs := cs42xx8.o
|
||||
snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o
|
||||
snd-soc-cs43130-objs := cs43130.o
|
||||
snd-soc-cs4341-objs := cs4341.o
|
||||
snd-soc-cs4349-objs := cs4349.o
|
||||
snd-soc-cs47l24-objs := cs47l24.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-i2c-objs := pcm512x-i2c.o
|
||||
snd-soc-pcm512x-spi-objs := pcm512x-spi.o
|
||||
snd-soc-rk3328-objs := rk3328_codec.o
|
||||
snd-soc-rl6231-objs := rl6231.o
|
||||
snd-soc-rl6347a-objs := rl6347a.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_I2C) += snd-soc-cs42xx8-i2c.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_CS47L24) += snd-soc-cs47l24.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_I2C) += snd-soc-pcm512x-i2c.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_RL6347A) += snd-soc-rl6347a.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/i2c.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/tlv.h>
|
||||
#include <sound/jack.h>
|
||||
#include "es8316.h"
|
||||
|
||||
/* 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 mutex lock;
|
||||
struct regmap *regmap;
|
||||
struct snd_soc_component *component;
|
||||
struct snd_soc_jack *jack;
|
||||
int irq;
|
||||
unsigned int sysclk;
|
||||
unsigned int allowed_rates[NR_SUPPORTED_MCLK_LRCK_RATIOS];
|
||||
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 Double Fs Switch", ES8316_DAC_SET2, 7, 1, 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_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,
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
|
||||
|
||||
es8316->component = component;
|
||||
|
||||
/* Reset codec and enable current state machine */
|
||||
snd_soc_component_write(component, ES8316_RESET, 0x3f);
|
||||
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 = {
|
||||
.probe = es8316_probe,
|
||||
.set_jack = es8316_set_jack,
|
||||
.controls = es8316_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(es8316_snd_controls),
|
||||
.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,
|
||||
};
|
||||
|
||||
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 = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = 0x53,
|
||||
.volatile_table = &es8316_volatile_table,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
static int es8316_i2c_probe(struct i2c_client *i2c_client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &i2c_client->dev;
|
||||
struct es8316_priv *es8316;
|
||||
struct regmap *regmap;
|
||||
int ret;
|
||||
|
||||
es8316 = devm_kzalloc(&i2c_client->dev, sizeof(struct es8316_priv),
|
||||
GFP_KERNEL);
|
||||
@ -586,9 +760,23 @@ static int es8316_i2c_probe(struct i2c_client *i2c_client,
|
||||
|
||||
i2c_set_clientdata(i2c_client, es8316);
|
||||
|
||||
regmap = devm_regmap_init_i2c(i2c_client, &es8316_regmap);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
es8316->regmap = devm_regmap_init_i2c(i2c_client, &es8316_regmap);
|
||||
if (IS_ERR(es8316->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,
|
||||
&soc_component_dev_es8316,
|
||||
|
@ -126,4 +126,11 @@
|
||||
#define ES8316_SERDATA2_LEN_16 0x0c
|
||||
#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
|
||||
|
@ -1400,24 +1400,20 @@ static int pcm512x_digital_mute(struct snd_soc_dai *dai, int mute)
|
||||
if (ret != 0) {
|
||||
dev_err(component->dev,
|
||||
"Failed to set digital mute: %d\n", ret);
|
||||
mutex_unlock(&pcm512x->mutex);
|
||||
return ret;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
regmap_read_poll_timeout(pcm512x->regmap,
|
||||
PCM512x_ANALOG_MUTE_DET,
|
||||
mute_det, (mute_det & 0x3) == 0,
|
||||
200, 10000);
|
||||
|
||||
mutex_unlock(&pcm512x->mutex);
|
||||
} else {
|
||||
pcm512x->mute &= ~0x1;
|
||||
ret = pcm512x_update_mute(pcm512x);
|
||||
if (ret != 0) {
|
||||
dev_err(component->dev,
|
||||
"Failed to update digital mute: %d\n", ret);
|
||||
mutex_unlock(&pcm512x->mutex);
|
||||
return ret;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
regmap_read_poll_timeout(pcm512x->regmap,
|
||||
@ -1428,9 +1424,10 @@ static int pcm512x_digital_mute(struct snd_soc_dai *dai, int mute)
|
||||
200, 10000);
|
||||
}
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&pcm512x->mutex);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
regmap_read(rt274->regmap,
|
||||
ret = regmap_read(rt274->regmap,
|
||||
RT274_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID), &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (val != RT274_VENDOR_ID) {
|
||||
dev_err(&i2c->dev,
|
||||
"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_CHOP_DAC, 0x2000);
|
||||
regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x2005);
|
||||
regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0xc0c4);
|
||||
|
||||
mutex_unlock(&rt5682->calibrate_mutex);
|
||||
|
||||
|
@ -1837,9 +1837,6 @@ static int wm8904_set_bias_level(struct snd_soc_component *component,
|
||||
|
||||
switch (level) {
|
||||
case SND_SOC_BIAS_ON:
|
||||
ret = clk_prepare_enable(wm8904->mclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
|
||||
case SND_SOC_BIAS_PREPARE:
|
||||
@ -1864,6 +1861,15 @@ static int wm8904_set_bias_level(struct snd_soc_component *component,
|
||||
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_sync(wm8904->regmap);
|
||||
|
||||
@ -2108,16 +2114,13 @@ static const struct regmap_config wm8904_regmap = {
|
||||
};
|
||||
|
||||
#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[] = {
|
||||
{
|
||||
.compatible = "wlf,wm8904",
|
||||
.data = &wm8904_data,
|
||||
.data = (void *)WM8904,
|
||||
}, {
|
||||
.compatible = "wlf,wm8912",
|
||||
.data = &wm8912_data,
|
||||
.data = (void *)WM8912,
|
||||
}, {
|
||||
/* 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);
|
||||
if (match == NULL)
|
||||
return -EINVAL;
|
||||
wm8904->devtype = *((enum wm8904_type *)match->data);
|
||||
wm8904->devtype = (enum wm8904_type)match->data;
|
||||
} else {
|
||||
wm8904->devtype = id->driver_data;
|
||||
}
|
||||
|
@ -8,14 +8,6 @@ config SND_SIMPLE_CARD
|
||||
This option enables generic simple sound card support
|
||||
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
|
||||
tristate "ASoC Audio Graph sound card support"
|
||||
depends on OF
|
||||
@ -24,12 +16,3 @@ config SND_AUDIO_GRAPH_CARD
|
||||
This option enables generic simple sound card support
|
||||
with OF-graph DT bindings.
|
||||
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
|
||||
snd-soc-simple-card-utils-objs := simple-card-utils.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-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) += 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_SCU_CARD) += snd-soc-audio-graph-scu-card.o
|
||||
|
@ -20,7 +20,7 @@
|
||||
#include <linux/string.h>
|
||||
#include <sound/simple_card_utils.h>
|
||||
|
||||
struct graph_card_data {
|
||||
struct graph_priv {
|
||||
struct snd_soc_card snd_card;
|
||||
struct graph_dai_props {
|
||||
struct asoc_simple_dai *cpu_dai;
|
||||
@ -39,6 +39,13 @@ struct graph_card_data {
|
||||
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_props(priv, i) ((priv)->dai_props + (i))
|
||||
#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,"
|
||||
|
||||
static int asoc_graph_card_outdrv_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol,
|
||||
int event)
|
||||
static int graph_outdrv_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol,
|
||||
int event)
|
||||
{
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
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,
|
||||
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),
|
||||
};
|
||||
|
||||
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 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);
|
||||
int ret;
|
||||
|
||||
@ -91,10 +98,10 @@ static int asoc_graph_card_startup(struct snd_pcm_substream *substream)
|
||||
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 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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static int asoc_graph_card_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
static int graph_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||
struct graph_priv *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||
struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
|
||||
unsigned int mclk, mclk_fs = 0;
|
||||
int ret = 0;
|
||||
@ -133,15 +140,15 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct snd_soc_ops asoc_graph_card_ops = {
|
||||
.startup = asoc_graph_card_startup,
|
||||
.shutdown = asoc_graph_card_shutdown,
|
||||
.hw_params = asoc_graph_card_hw_params,
|
||||
static const struct snd_soc_ops graph_ops = {
|
||||
.startup = graph_startup,
|
||||
.shutdown = graph_shutdown,
|
||||
.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);
|
||||
int ret = 0;
|
||||
|
||||
@ -158,10 +165,10 @@ static int asoc_graph_card_dai_init(struct snd_soc_pcm_runtime *rtd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int asoc_graph_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_pcm_hw_params *params)
|
||||
static int graph_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_priv *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);
|
||||
@ -169,41 +176,64 @@ static int asoc_graph_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int asoc_graph_card_dai_link_of_dpcm(struct device_node *top,
|
||||
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_cpu)
|
||||
static void graph_get_conversion(struct device *dev,
|
||||
struct device_node *ep,
|
||||
struct asoc_simple_card_data *adata)
|
||||
{
|
||||
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 device_node *ep = is_cpu ? cpu_ep : codec_ep;
|
||||
struct device_node *top = dev->of_node;
|
||||
struct device_node *port = of_get_parent(ep);
|
||||
struct device_node *ports = of_get_parent(port);
|
||||
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 snd_soc_dai_link_component *codecs = dai_link->codecs;
|
||||
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(ports, "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);
|
||||
|
||||
asoc_simple_card_parse_convert(dev, top, NULL, &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);
|
||||
graph_get_conversion(dev, ep, &dai_props->adata);
|
||||
|
||||
of_node_put(ports);
|
||||
of_node_put(port);
|
||||
of_node_put(node);
|
||||
|
||||
if (is_cpu) {
|
||||
if (li->cpu) {
|
||||
|
||||
/* BE is dummy */
|
||||
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 =
|
||||
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);
|
||||
if (ret)
|
||||
@ -244,13 +274,13 @@ static int asoc_graph_card_dai_link_of_dpcm(struct device_node *top,
|
||||
|
||||
/* BE settings */
|
||||
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_props->codec_dai = &priv->dais[(*dai_idx)++];
|
||||
dai_props->codec_dai = &priv->dais[li->dais++];
|
||||
|
||||
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);
|
||||
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_capture = 1;
|
||||
dai_link->ops = &asoc_graph_card_ops;
|
||||
dai_link->init = asoc_graph_card_dai_init;
|
||||
dai_link->ops = &graph_ops;
|
||||
dai_link->init = graph_dai_init;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int asoc_graph_card_dai_link_of(struct device_node *top,
|
||||
struct device_node *cpu_ep,
|
||||
struct device_node *codec_ep,
|
||||
struct graph_card_data *priv,
|
||||
int *dai_idx, int link_idx)
|
||||
static int graph_dai_link_of(struct graph_priv *priv,
|
||||
struct device_node *cpu_ep,
|
||||
struct device_node *codec_ep,
|
||||
struct link_info *li)
|
||||
{
|
||||
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 device_node *cpu_port = of_get_parent(cpu_ep);
|
||||
struct device_node *codec_port = of_get_parent(codec_ep);
|
||||
struct device_node *cpu_ports = of_get_parent(cpu_port);
|
||||
struct device_node *codec_ports = of_get_parent(codec_port);
|
||||
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 *cpu_port;
|
||||
struct device_node *cpu_ports;
|
||||
struct device_node *codec_port;
|
||||
struct device_node *codec_ports;
|
||||
struct asoc_simple_dai *cpu_dai;
|
||||
struct asoc_simple_dai *codec_dai;
|
||||
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 =
|
||||
dai_props->cpu_dai = &priv->dais[(*dai_idx)++];
|
||||
dai_props->cpu_dai = &priv->dais[li->dais++];
|
||||
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() */
|
||||
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)
|
||||
return ret;
|
||||
|
||||
dai_link->ops = &asoc_graph_card_ops;
|
||||
dai_link->init = asoc_graph_card_dai_init;
|
||||
dai_link->ops = &graph_ops;
|
||||
dai_link->init = graph_dai_init;
|
||||
|
||||
asoc_simple_card_canonicalize_cpu(dai_link,
|
||||
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;
|
||||
}
|
||||
|
||||
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 device *dev = graph_priv_to_dev(priv);
|
||||
struct snd_soc_card *card = graph_priv_to_card(priv);
|
||||
struct device_node *top = dev->of_node;
|
||||
struct device_node *node = top;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct device_node *cpu_port;
|
||||
struct device_node *cpu_ep = NULL;
|
||||
struct device_node *codec_ep = NULL;
|
||||
struct device_node *codec_port = NULL;
|
||||
struct device_node *codec_port_old = NULL;
|
||||
struct device_node *cpu_ep;
|
||||
struct device_node *codec_ep;
|
||||
struct device_node *codec_port;
|
||||
struct device_node *codec_port_old = NULL;
|
||||
struct asoc_simple_card_data adata;
|
||||
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);
|
||||
if (ret < 0)
|
||||
@ -408,11 +507,8 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
link_idx = 0;
|
||||
dai_idx = 0;
|
||||
conf_idx = 0;
|
||||
codec_port_old = NULL;
|
||||
for (cpu = 1; cpu >= 0; cpu--) {
|
||||
memset(&li, 0, sizeof(li));
|
||||
for (li.cpu = 1; li.cpu >= 0; li.cpu--) {
|
||||
/*
|
||||
* 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,
|
||||
* detect "dummy-Codec" in last;
|
||||
*/
|
||||
of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
|
||||
cpu_port = it.node;
|
||||
cpu_ep = NULL;
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
ret = graph_for_each_link(priv, &li,
|
||||
graph_dai_link_of,
|
||||
graph_dai_link_of_dpcm);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return asoc_simple_card_parse_card_name(card, NULL);
|
||||
}
|
||||
|
||||
static void asoc_graph_get_dais_count(struct device *dev,
|
||||
int *link_num,
|
||||
int *dais_num,
|
||||
int *ccnf_num)
|
||||
static int graph_count_noml(struct graph_priv *priv,
|
||||
struct device_node *cpu_ep,
|
||||
struct device_node *codec_ep,
|
||||
struct link_info *li)
|
||||
{
|
||||
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;
|
||||
struct device *dev = graph_priv_to_dev(priv);
|
||||
|
||||
li->link += 1; /* 1xCPU-Codec */
|
||||
li->dais += 2; /* 1xCPU + 1xCodec */
|
||||
|
||||
dev_dbg(dev, "Count As Normal\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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.
|
||||
@ -522,45 +609,26 @@ static void asoc_graph_get_dais_count(struct device *dev,
|
||||
* => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec
|
||||
* => 6 DAIs = 4xCPU + 2xCodec
|
||||
* => 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;
|
||||
codec_port_old2 = NULL;
|
||||
of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
|
||||
cpu_port = it.node;
|
||||
cpu_ep = NULL;
|
||||
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;
|
||||
}
|
||||
}
|
||||
graph_for_each_link(priv, li,
|
||||
graph_count_noml,
|
||||
graph_count_dpcm);
|
||||
dev_dbg(dev, "link %d, dais %d, ccnf %d\n",
|
||||
li->link, li->dais, li->conf);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 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;
|
||||
struct link_info li;
|
||||
int ret, i;
|
||||
|
||||
/* 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)
|
||||
return -ENOMEM;
|
||||
|
||||
asoc_graph_get_dais_count(dev, &lnum, &dnum, &cnum);
|
||||
if (!lnum || !dnum)
|
||||
card = graph_priv_to_card(priv);
|
||||
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;
|
||||
|
||||
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);
|
||||
dai_props = devm_kcalloc(dev, li.link, sizeof(*dai_props), GFP_KERNEL);
|
||||
dai_link = devm_kcalloc(dev, li.link, sizeof(*dai_link), GFP_KERNEL);
|
||||
dais = devm_kcalloc(dev, li.dais, sizeof(*dais), GFP_KERNEL);
|
||||
cconf = devm_kcalloc(dev, li.conf, sizeof(*cconf), GFP_KERNEL);
|
||||
if (!dai_props || !dai_link || !dais)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -608,7 +684,7 @@ static int asoc_graph_card_probe(struct platform_device *pdev)
|
||||
* see
|
||||
* 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].num_codecs = 1;
|
||||
dai_link[i].platform = &dai_props[i].platform;
|
||||
@ -621,24 +697,17 @@ static int asoc_graph_card_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->dai_props = dai_props;
|
||||
priv->dai_link = dai_link;
|
||||
priv->dais = dais;
|
||||
priv->codec_conf = cconf;
|
||||
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 = dai_link;
|
||||
card->num_links = lnum;
|
||||
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->num_links = li.link;
|
||||
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 != -EPROBE_DEFER)
|
||||
dev_err(dev, "parse error %d\n", ret);
|
||||
@ -658,30 +727,30 @@ err:
|
||||
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);
|
||||
|
||||
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-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 = {
|
||||
.name = "asoc-audio-graph-card",
|
||||
.pm = &snd_soc_pm_ops,
|
||||
.of_match_table = asoc_graph_of_match,
|
||||
.of_match_table = graph_of_match,
|
||||
},
|
||||
.probe = asoc_graph_card_probe,
|
||||
.remove = asoc_graph_card_remove,
|
||||
.probe = graph_probe,
|
||||
.remove = graph_remove,
|
||||
};
|
||||
module_platform_driver(asoc_graph_card);
|
||||
module_platform_driver(graph_card);
|
||||
|
||||
MODULE_ALIAS("platform:asoc-audio-graph-card");
|
||||
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 */
|
||||
ret = of_graph_parse_endpoint(ep, &info);
|
||||
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;
|
||||
if (info.port)
|
||||
|
||||
node = of_get_parent(ep);
|
||||
of_node_put(node);
|
||||
if (of_get_property(node, "reg", NULL))
|
||||
return info.port;
|
||||
}
|
||||
|
||||
node = of_graph_get_port_parent(ep);
|
||||
|
||||
/*
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include <sound/soc-dai.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
struct simple_card_data {
|
||||
struct simple_priv {
|
||||
struct snd_soc_card snd_card;
|
||||
struct simple_dai_props {
|
||||
struct asoc_simple_dai *cpu_dai;
|
||||
@ -33,6 +33,13 @@ struct simple_card_data {
|
||||
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_props(priv, i) ((priv)->dai_props + (i))
|
||||
#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 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 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);
|
||||
int ret;
|
||||
@ -61,10 +68,10 @@ static int asoc_simple_card_startup(struct snd_pcm_substream *substream)
|
||||
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 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);
|
||||
|
||||
@ -73,8 +80,8 @@ static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream)
|
||||
asoc_simple_card_clk_disable(dai_props->codec_dai);
|
||||
}
|
||||
|
||||
static int asoc_simple_set_clk_rate(struct asoc_simple_dai *simple_dai,
|
||||
unsigned long rate)
|
||||
static int simple_set_clk_rate(struct asoc_simple_dai *simple_dai,
|
||||
unsigned long rate)
|
||||
{
|
||||
if (!simple_dai)
|
||||
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);
|
||||
}
|
||||
|
||||
static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
static int simple_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct 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);
|
||||
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) {
|
||||
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)
|
||||
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)
|
||||
return ret;
|
||||
|
||||
@ -129,15 +136,15 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct snd_soc_ops asoc_simple_card_ops = {
|
||||
.startup = asoc_simple_card_startup,
|
||||
.shutdown = asoc_simple_card_shutdown,
|
||||
.hw_params = asoc_simple_card_hw_params,
|
||||
static const struct snd_soc_ops simple_ops = {
|
||||
.startup = simple_startup,
|
||||
.shutdown = simple_shutdown,
|
||||
.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);
|
||||
int ret;
|
||||
|
||||
@ -154,10 +161,10 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int asoc_simple_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_pcm_hw_params *params)
|
||||
static int simple_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_priv *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);
|
||||
@ -165,30 +172,58 @@ static int asoc_simple_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int asoc_simple_card_dai_link_of_dpcm(struct device_node *top,
|
||||
struct device_node *node,
|
||||
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)
|
||||
static void simple_get_conversion(struct device *dev,
|
||||
struct device_node *np,
|
||||
struct asoc_simple_card_data *adata)
|
||||
{
|
||||
struct device_node *top = dev->of_node;
|
||||
struct device_node *node = of_get_parent(np);
|
||||
|
||||
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 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_dai_link *dai_link = simple_priv_to_link(priv, li->link);
|
||||
struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
|
||||
struct asoc_simple_dai *dai;
|
||||
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 *prefix = "";
|
||||
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 */
|
||||
if (is_top_level_node)
|
||||
if (is_top)
|
||||
prefix = PREFIX;
|
||||
|
||||
if (is_fe) {
|
||||
if (li->cpu) {
|
||||
int is_single_links = 0;
|
||||
|
||||
/* 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 =
|
||||
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,
|
||||
&is_single_links);
|
||||
@ -229,13 +264,13 @@ static int asoc_simple_card_dai_link_of_dpcm(struct device_node *top,
|
||||
|
||||
/* BE settings */
|
||||
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_props->codec_dai = &priv->dais[(*dai_idx)++];
|
||||
dai_props->codec_dai = &priv->dais[li->dais++];
|
||||
|
||||
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);
|
||||
if (ret < 0)
|
||||
@ -260,9 +295,7 @@ static int asoc_simple_card_dai_link_of_dpcm(struct device_node *top,
|
||||
"prefix");
|
||||
}
|
||||
|
||||
asoc_simple_card_parse_convert(dev, top, PREFIX, &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);
|
||||
simple_get_conversion(dev, np, &dai_props->adata);
|
||||
|
||||
ret = asoc_simple_card_of_parse_tdm(np, dai);
|
||||
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_capture = 1;
|
||||
dai_link->ops = &asoc_simple_card_ops;
|
||||
dai_link->init = asoc_simple_card_dai_init;
|
||||
dai_link->ops = &simple_ops;
|
||||
dai_link->init = simple_dai_init;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int asoc_simple_card_dai_link_of(struct device_node *top,
|
||||
struct device_node *node,
|
||||
struct simple_card_data *priv,
|
||||
int *dai_idx, int link_idx,
|
||||
bool is_top_level_node)
|
||||
static int simple_dai_link_of(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 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_dai_link *dai_link = simple_priv_to_link(priv, li->link);
|
||||
struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
|
||||
struct asoc_simple_dai *cpu_dai;
|
||||
struct asoc_simple_dai *codec_dai;
|
||||
struct device_node *top = dev->of_node;
|
||||
struct device_node *cpu = NULL;
|
||||
struct device_node *node = NULL;
|
||||
struct device_node *plat = NULL;
|
||||
struct device_node *codec = NULL;
|
||||
char prop[128];
|
||||
char *prefix = "";
|
||||
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 */
|
||||
if (is_top_level_node)
|
||||
if (is_top)
|
||||
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);
|
||||
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 =
|
||||
dai_props->cpu_dai = &priv->dais[(*dai_idx)++];
|
||||
dai_props->cpu_dai = &priv->dais[li->dais++];
|
||||
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,
|
||||
prefix, &dai_link->dai_fmt);
|
||||
@ -389,20 +420,87 @@ static int asoc_simple_card_dai_link_of(struct device_node *top,
|
||||
if (ret < 0)
|
||||
goto dai_link_of_err;
|
||||
|
||||
dai_link->ops = &asoc_simple_card_ops;
|
||||
dai_link->init = asoc_simple_card_dai_init;
|
||||
dai_link->ops = &simple_ops;
|
||||
dai_link->init = simple_dai_init;
|
||||
|
||||
asoc_simple_card_canonicalize_cpu(dai_link, single_cpu);
|
||||
|
||||
dai_link_of_err:
|
||||
of_node_put(cpu);
|
||||
of_node_put(codec);
|
||||
of_node_put(node);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int asoc_simple_card_parse_aux_devs(struct device_node *node,
|
||||
struct simple_card_data *priv)
|
||||
static int simple_for_each_link(struct simple_priv *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_node *aux_node;
|
||||
@ -432,17 +530,13 @@ static int asoc_simple_card_parse_aux_devs(struct device_node *node,
|
||||
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_node *top = dev->of_node;
|
||||
struct snd_soc_card *card = simple_priv_to_card(priv);
|
||||
struct device_node *node;
|
||||
struct device_node *np;
|
||||
struct device_node *codec;
|
||||
bool is_fe;
|
||||
int ret, loop;
|
||||
int dai_idx, link_idx, conf_idx;
|
||||
struct link_info li;
|
||||
int ret;
|
||||
|
||||
if (!top)
|
||||
return -EINVAL;
|
||||
@ -456,62 +550,66 @@ static int asoc_simple_card_parse_of(struct simple_card_data *priv)
|
||||
return ret;
|
||||
|
||||
/* Single/Muti DAI link(s) & New style of DT node */
|
||||
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 {
|
||||
/* DPCM */
|
||||
if (of_get_child_count(node) > 2) {
|
||||
for_each_child_of_node(node, np) {
|
||||
codec = of_get_child_by_name(node,
|
||||
loop ? "codec" :
|
||||
PREFIX "codec");
|
||||
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);
|
||||
}
|
||||
memset(&li, 0, sizeof(li));
|
||||
for (li.cpu = 1; li.cpu >= 0; li.cpu--) {
|
||||
/*
|
||||
* Detect all CPU first, and Detect all Codec 2nd.
|
||||
*
|
||||
* In Normal sound case, all DAIs are detected
|
||||
* as "CPU-Codec".
|
||||
*
|
||||
* In DPCM sound case,
|
||||
* all CPUs are detected as "CPU-dummy", and
|
||||
* all Codecs are detected as "dummy-Codec".
|
||||
* To avoid random sub-device numbering,
|
||||
* detect "dummy-Codec" in last;
|
||||
*/
|
||||
ret = simple_for_each_link(priv, &li,
|
||||
simple_dai_link_of,
|
||||
simple_dai_link_of_dpcm);
|
||||
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;
|
||||
|
||||
ret = asoc_simple_card_parse_aux_devs(top, priv);
|
||||
ret = simple_parse_aux_devs(top, priv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void asoc_simple_card_get_dais_count(struct device *dev,
|
||||
int *link_num,
|
||||
int *dais_num,
|
||||
int *ccnf_num)
|
||||
static int simple_count_noml(struct simple_priv *priv,
|
||||
struct device_node *np,
|
||||
struct device_node *codec,
|
||||
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 *node;
|
||||
int loop;
|
||||
int num;
|
||||
|
||||
/*
|
||||
* 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 DAIs = 4xCPU + 2xCodec
|
||||
* => 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) {
|
||||
(*link_num) = 1;
|
||||
(*dais_num) = 2;
|
||||
(*ccnf_num) = 0;
|
||||
li->link = 1;
|
||||
li->dais = 2;
|
||||
li->conf = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
loop = 1;
|
||||
node = of_get_child_by_name(top, PREFIX "dai-link");
|
||||
if (!node) {
|
||||
node = top;
|
||||
loop = 0;
|
||||
}
|
||||
simple_for_each_link(priv, li,
|
||||
simple_count_noml,
|
||||
simple_count_dpcm);
|
||||
|
||||
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);
|
||||
dev_dbg(dev, "link %d, dais %d, ccnf %d\n",
|
||||
li->link, li->dais, li->conf);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 simple_dai_props *dai_props;
|
||||
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 snd_soc_card *card;
|
||||
struct snd_soc_codec_conf *cconf;
|
||||
int lnum = 0, dnum = 0, cnum = 0;
|
||||
struct link_info li;
|
||||
int ret, i;
|
||||
|
||||
/* 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)
|
||||
return -ENOMEM;
|
||||
|
||||
asoc_simple_card_get_dais_count(dev, &lnum, &dnum, &cnum);
|
||||
if (!lnum || !dnum)
|
||||
card = simple_priv_to_card(priv);
|
||||
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;
|
||||
|
||||
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);
|
||||
dai_props = devm_kcalloc(dev, li.link, sizeof(*dai_props), GFP_KERNEL);
|
||||
dai_link = devm_kcalloc(dev, li.link, sizeof(*dai_link), GFP_KERNEL);
|
||||
dais = devm_kcalloc(dev, li.dais, sizeof(*dais), GFP_KERNEL);
|
||||
cconf = devm_kcalloc(dev, li.conf, sizeof(*cconf), GFP_KERNEL);
|
||||
if (!dai_props || !dai_link || !dais)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -628,30 +729,25 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
|
||||
* see
|
||||
* 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].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;
|
||||
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->num_links = li.link;
|
||||
card->codec_conf = cconf;
|
||||
card->num_configs = cnum;
|
||||
card->probe = asoc_simple_soc_card_probe;
|
||||
card->num_configs = li.conf;
|
||||
|
||||
if (np && of_device_is_available(np)) {
|
||||
|
||||
ret = asoc_simple_card_parse_of(priv);
|
||||
ret = simple_parse_of(priv);
|
||||
if (ret < 0) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
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->cpu_dai_name = cinfo->cpu_dai.name;
|
||||
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,
|
||||
sizeof(*priv->dai_props->cpu_dai));
|
||||
memcpy(priv->dai_props->codec_dai, &cinfo->codec_dai,
|
||||
@ -714,28 +810,28 @@ err:
|
||||
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);
|
||||
|
||||
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-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 = {
|
||||
.driver = {
|
||||
.name = "asoc-simple-card",
|
||||
.pm = &snd_soc_pm_ops,
|
||||
.of_match_table = asoc_simple_of_match,
|
||||
.of_match_table = simple_of_match,
|
||||
},
|
||||
.probe = asoc_simple_card_probe,
|
||||
.remove = asoc_simple_card_remove,
|
||||
.probe = simple_probe,
|
||||
.remove = simple_remove,
|
||||
};
|
||||
|
||||
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
|
||||
tristate "ACPI HiFi2 (Baytrail, Cherrytrail) Platforms"
|
||||
default ACPI
|
||||
depends on X86 && ACPI
|
||||
depends on X86 && ACPI && PCI
|
||||
select SND_SST_IPC_ACPI
|
||||
select SND_SST_ATOM_HIFI2_PLATFORM
|
||||
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;
|
||||
}
|
||||
|
||||
if (set_mixer == false)
|
||||
if (!set_mixer)
|
||||
return 0;
|
||||
|
||||
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_size = ctx->pdata->strm_map_size;
|
||||
|
||||
if (is_compress == true)
|
||||
if (is_compress)
|
||||
cstream = (struct snd_compr_stream *)substream;
|
||||
else
|
||||
pstream = (struct snd_pcm_substream *)substream;
|
||||
|
@ -255,18 +255,16 @@ static int is_byt(void)
|
||||
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;
|
||||
|
||||
if (IS_ENABLED(CONFIG_IOSF_MBI)) {
|
||||
if (!is_byt())
|
||||
return false;
|
||||
|
||||
if (iosf_mbi_available()) {
|
||||
u32 bios_status;
|
||||
|
||||
if (!is_byt() || !iosf_mbi_available()) {
|
||||
/* bail silently */
|
||||
return status;
|
||||
}
|
||||
|
||||
status = iosf_mbi_read(BT_MBI_UNIT_PMC, /* 0x04 PUNIT */
|
||||
MBI_REG_READ, /* 0x10 */
|
||||
0x006, /* BIOS_CONFIG */
|
||||
@ -278,15 +276,28 @@ static int is_byt_cr(struct device *dev, bool *bytcr)
|
||||
/* bits 26:27 mirror PMIC options */
|
||||
bios_status = (bios_status >> 26) & 3;
|
||||
|
||||
if ((bios_status == 1) || (bios_status == 3))
|
||||
*bytcr = true;
|
||||
else
|
||||
dev_info(dev, "BYT-CR not detected\n");
|
||||
if (bios_status == 1 || bios_status == 3) {
|
||||
dev_info(dev, "Detected Baytrail-CR platform\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
dev_info(dev, "BYT-CR not detected\n");
|
||||
}
|
||||
} 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 sst_platform_info *pdata;
|
||||
unsigned int dev_id;
|
||||
bool bytcr = false;
|
||||
|
||||
id = acpi_match_device(dev->driver->acpi_match_table, dev);
|
||||
if (!id)
|
||||
@ -333,10 +343,7 @@ static int sst_acpi_probe(struct platform_device *pdev)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = is_byt_cr(dev, &bytcr);
|
||||
if (!((ret < 0) || (bytcr == false))) {
|
||||
dev_info(dev, "Detected Baytrail-CR platform\n");
|
||||
|
||||
if (is_byt_cr(pdev)) {
|
||||
/* override resource 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 usage_count = 0;
|
||||
|
||||
if (state == true) {
|
||||
if (state) {
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
usage_count = GET_USAGE_COUNT(dev);
|
||||
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;
|
||||
|
||||
list_for_each_entry(listnode, memcpy_list, memcpylist) {
|
||||
if (listnode->is_io == true)
|
||||
if (listnode->is_io)
|
||||
memcpy32_toio((void __iomem *)listnode->dstn,
|
||||
listnode->src, listnode->size);
|
||||
else
|
||||
|
@ -278,7 +278,6 @@ static int sst_byt_process_notification(struct sst_byt *byt,
|
||||
struct sst_byt_stream *stream;
|
||||
u64 header;
|
||||
u8 msg_id, stream_id;
|
||||
int handled = 1;
|
||||
|
||||
header = sst_dsp_shim_read64_unlocked(sst, SST_IPCD);
|
||||
msg_id = sst_byt_header_msg_id(header);
|
||||
@ -298,7 +297,7 @@ static int sst_byt_process_notification(struct sst_byt *byt,
|
||||
break;
|
||||
}
|
||||
|
||||
return handled;
|
||||
return 1;
|
||||
}
|
||||
|
||||
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);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
if (pdata->restore_stream == true)
|
||||
if (pdata->restore_stream)
|
||||
schedule_work(&pcm_data->work);
|
||||
else
|
||||
sst_byt_stream_resume(byt, pcm_data->stream);
|
||||
|
@ -192,7 +192,7 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = {
|
||||
.stream_name = "Loopback",
|
||||
.cpu_dai_name = "Loopback Pin",
|
||||
.platform_name = "haswell-pcm-audio",
|
||||
.dynamic = 0,
|
||||
.dynamic = 1,
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.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/input.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/intel-family.h>
|
||||
#include <asm/platform_sst_audio.h>
|
||||
#include <linux/clk.h>
|
||||
#include <sound/jack.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
@ -35,27 +42,96 @@
|
||||
|
||||
struct byt_cht_es8316_private {
|
||||
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[] = {
|
||||
SND_SOC_DAPM_HP("Headphone", NULL),
|
||||
enum {
|
||||
BYT_CHT_ES8316_INTMIC_IN1_MAP,
|
||||
BYT_CHT_ES8316_INTMIC_IN2_MAP,
|
||||
};
|
||||
|
||||
/*
|
||||
* The codec supports two analog microphone inputs. I have only
|
||||
* tested MIC1. A DMIC route could also potentially be added
|
||||
* if such functionality is found on another platform.
|
||||
*/
|
||||
SND_SOC_DAPM_MIC("Microphone 1", NULL),
|
||||
SND_SOC_DAPM_MIC("Microphone 2", NULL),
|
||||
#define BYT_CHT_ES8316_MAP(quirk) ((quirk) & GENMASK(3, 0))
|
||||
#define BYT_CHT_ES8316_SSP0 BIT(16)
|
||||
#define BYT_CHT_ES8316_MONO_SPEAKER BIT(17)
|
||||
|
||||
static int quirk;
|
||||
|
||||
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[] = {
|
||||
{"MIC1", NULL, "Microphone 1"},
|
||||
{"MIC2", NULL, "Microphone 2"},
|
||||
|
||||
{"Headphone", NULL, "HPOL"},
|
||||
{"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"},
|
||||
{"ssp2 Tx", NULL, "codec_out0"},
|
||||
{"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[] = {
|
||||
SOC_DAPM_PIN_SWITCH("Speaker"),
|
||||
SOC_DAPM_PIN_SWITCH("Headphone"),
|
||||
SOC_DAPM_PIN_SWITCH("Microphone 1"),
|
||||
SOC_DAPM_PIN_SWITCH("Microphone 2"),
|
||||
SOC_DAPM_PIN_SWITCH("Headset Mic"),
|
||||
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)
|
||||
{
|
||||
struct snd_soc_component *codec = runtime->codec_dai->component;
|
||||
struct snd_soc_card *card = runtime->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;
|
||||
|
||||
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
|
||||
* 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -123,14 +252,21 @@ static int byt_cht_es8316_codec_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
SNDRV_PCM_HW_PARAM_RATE);
|
||||
struct snd_interval *channels = hw_param_interval(params,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS);
|
||||
int ret;
|
||||
int ret, bits;
|
||||
|
||||
/* The DSP will covert the FE rate to 48k, stereo */
|
||||
rate->min = rate->max = 48000;
|
||||
channels->min = channels->max = 2;
|
||||
|
||||
/* set SSP2 to 24-bit */
|
||||
params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
|
||||
if (quirk & BYT_CHT_ES8316_SSP0) {
|
||||
/* 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
|
||||
@ -147,7 +283,7 @@ static int byt_cht_es8316_codec_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
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) {
|
||||
dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
|
||||
return ret;
|
||||
@ -218,6 +354,59 @@ static struct snd_soc_dai_link byt_cht_es8316_dais[] = {
|
||||
|
||||
|
||||
/* 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 = {
|
||||
.name = "bytcht-es8316",
|
||||
.owner = THIS_MODULE,
|
||||
@ -230,24 +419,39 @@ static struct snd_soc_card byt_cht_es8316_card = {
|
||||
.controls = byt_cht_es8316_controls,
|
||||
.num_controls = ARRAY_SIZE(byt_cht_es8316_controls),
|
||||
.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)
|
||||
{
|
||||
const char * const mic_name[] = { "in1", "in2" };
|
||||
struct byt_cht_es8316_private *priv;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct snd_soc_acpi_mach *mach;
|
||||
const char *i2c_name = NULL;
|
||||
struct device *codec_dev;
|
||||
int dai_index = 0;
|
||||
int i;
|
||||
int ret = 0;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
mach = (&pdev->dev)->platform_data;
|
||||
mach = dev->platform_data;
|
||||
/* fix index of codec dai */
|
||||
for (i = 0; i < ARRAY_SIZE(byt_cht_es8316_dais); i++) {
|
||||
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;
|
||||
}
|
||||
|
||||
/* register the soc card */
|
||||
byt_cht_es8316_card.dev = &pdev->dev;
|
||||
snd_soc_card_set_drvdata(&byt_cht_es8316_card, priv);
|
||||
/* Check for BYTCR or other platform and setup quirks */
|
||||
if (x86_match_cpu(baytrail_cpu_ids) &&
|
||||
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)) {
|
||||
ret = PTR_ERR(priv->mclk);
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to get MCLK from pmc_plt_clk_3: %d\n",
|
||||
ret);
|
||||
dev_err(dev, "clk_get pmc_plt_clk_3 failed: %d\n", 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) {
|
||||
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;
|
||||
}
|
||||
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 = {
|
||||
@ -292,6 +555,7 @@ static struct platform_driver snd_byt_cht_es8316_mc_driver = {
|
||||
.name = "bytcht_es8316",
|
||||
},
|
||||
.probe = snd_byt_cht_es8316_mc_probe,
|
||||
.remove = snd_byt_cht_es8316_mc_remove,
|
||||
};
|
||||
|
||||
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_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 = {
|
||||
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 */
|
||||
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;
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = {
|
||||
.stream_name = "Loopback",
|
||||
.cpu_dai_name = "Loopback Pin",
|
||||
.platform_name = "haswell-pcm-audio",
|
||||
.dynamic = 0,
|
||||
.dynamic = 1,
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.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;
|
||||
channels->min = channels->max = 2;
|
||||
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
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
@ -154,6 +154,15 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = {
|
||||
.sof_tplg_filename = "intel/sof-byt-da7213.tplg",
|
||||
.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 */
|
||||
{
|
||||
.id = "10EC5645",
|
||||
|
@ -1216,7 +1216,7 @@ int sst_hsw_stream_commit(struct sst_hsw *hsw, struct sst_hsw_stream *stream)
|
||||
return ret;
|
||||
}
|
||||
|
||||
stream->commited = 1;
|
||||
stream->commited = true;
|
||||
trace_hsw_stream_alloc_reply(stream);
|
||||
|
||||
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",
|
||||
rtd->cpu_dai->id);
|
||||
return -EINVAL;
|
||||
};
|
||||
}
|
||||
|
||||
ret = sst_hsw_stream_format(hsw, pcm_data->stream,
|
||||
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);
|
||||
goto out;
|
||||
}
|
||||
pcm_data->allocated = 0;
|
||||
pcm_data->allocated = false;
|
||||
pcm_data->stream = NULL;
|
||||
|
||||
out:
|
||||
|
@ -416,7 +416,7 @@ int skl_resume_dsp(struct skl *skl)
|
||||
snd_hdac_ext_bus_ppcap_int_enable(bus, true);
|
||||
|
||||
/* check if DSP 1st boot is done */
|
||||
if (skl->skl_sst->is_first_boot == true)
|
||||
if (skl->skl_sst->is_first_boot)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
|
@ -1423,7 +1423,7 @@ static int skl_platform_soc_probe(struct snd_soc_component *component)
|
||||
if (!ops)
|
||||
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");
|
||||
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;
|
||||
|
||||
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)
|
||||
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,
|
||||
(q6asm_cb)compress_event_handler,
|
||||
prtd, stream_id, LEGACY_PCM_MODE);
|
||||
if (!prtd->audio_client) {
|
||||
if (IS_ERR(prtd->audio_client)) {
|
||||
dev_err(dev, "Could not allocate memory\n");
|
||||
kfree(prtd);
|
||||
return -ENOMEM;
|
||||
ret = PTR_ERR(prtd->audio_client);
|
||||
goto free_prtd;
|
||||
}
|
||||
|
||||
size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE *
|
||||
@ -582,7 +582,7 @@ static int q6asm_dai_compr_open(struct snd_compr_stream *stream)
|
||||
&prtd->dma_buffer);
|
||||
if (ret) {
|
||||
dev_err(dev, "Cannot allocate buffer(s)\n");
|
||||
return ret;
|
||||
goto free_client;
|
||||
}
|
||||
|
||||
if (pdata->sid < 0)
|
||||
@ -595,6 +595,13 @@ static int q6asm_dai_compr_open(struct snd_compr_stream *stream)
|
||||
runtime->private_data = prtd;
|
||||
|
||||
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)
|
||||
@ -874,7 +881,7 @@ static int of_q6asm_parse_dai_data(struct device *dev,
|
||||
|
||||
for_each_child_of_node(dev->of_node, node) {
|
||||
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);
|
||||
continue;
|
||||
}
|
||||
|
@ -158,17 +158,24 @@ static int sdm845_snd_hw_params(struct snd_pcm_substream *substream,
|
||||
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)
|
||||
{
|
||||
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_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);
|
||||
int i, rval;
|
||||
struct snd_jack *jack;
|
||||
int rval;
|
||||
|
||||
if (!pdata->jack_setup) {
|
||||
struct snd_jack *jack;
|
||||
|
||||
rval = snd_soc_card_jack_new(card, "Headset Jack",
|
||||
SND_JACK_HEADSET |
|
||||
SND_JACK_HEADPHONE |
|
||||
@ -190,16 +197,22 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd)
|
||||
pdata->jack_setup = true;
|
||||
}
|
||||
|
||||
for (i = 0 ; i < dai_link->num_codecs; i++) {
|
||||
struct snd_soc_dai *dai = rtd->codec_dais[i];
|
||||
switch (cpu_dai->id) {
|
||||
case PRIMARY_MI2S_RX:
|
||||
jack = pdata->jack.jack;
|
||||
component = codec_dai->component;
|
||||
|
||||
component = dai->component;
|
||||
rval = snd_soc_component_set_jack(
|
||||
component, &pdata->jack, NULL);
|
||||
jack->private_data = component;
|
||||
jack->private_free = sdm845_jack_free;
|
||||
rval = snd_soc_component_set_jack(component,
|
||||
&pdata->jack, NULL);
|
||||
if (rval != 0 && rval != -ENOTSUPP) {
|
||||
dev_warn(card->dev, "Failed to set jack: %d\n", rval);
|
||||
return rval;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
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 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);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <linux/platform_data/davinci_asp.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
|
||||
#include <sound/asoundef.h>
|
||||
#include <sound/core.h>
|
||||
@ -54,6 +55,7 @@ static u32 context_regs[] = {
|
||||
DAVINCI_MCASP_AHCLKXCTL_REG,
|
||||
DAVINCI_MCASP_AHCLKRCTL_REG,
|
||||
DAVINCI_MCASP_PDIR_REG,
|
||||
DAVINCI_MCASP_PFUNC_REG,
|
||||
DAVINCI_MCASP_RXMASK_REG,
|
||||
DAVINCI_MCASP_TXMASK_REG,
|
||||
DAVINCI_MCASP_RXTDM_REG,
|
||||
@ -108,7 +110,11 @@ struct davinci_mcasp {
|
||||
/* Used for comstraint setting on the second stream */
|
||||
u32 channels;
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
#ifdef CONFIG_GPIOLIB
|
||||
struct gpio_chip gpio_chip;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
struct davinci_mcasp_context context;
|
||||
#endif
|
||||
|
||||
@ -818,9 +824,6 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream,
|
||||
if (mcasp->version < MCASP_VERSION_3)
|
||||
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) {
|
||||
mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
|
||||
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;
|
||||
}
|
||||
|
||||
#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_PCM_FMTS (SNDRV_PCM_FMTBIT_S8 | \
|
||||
@ -1571,8 +1506,6 @@ static struct snd_soc_dai_driver davinci_mcasp_dai[] = {
|
||||
{
|
||||
.name = "davinci-mcasp.0",
|
||||
.probe = davinci_mcasp_dai_probe,
|
||||
.suspend = davinci_mcasp_suspend,
|
||||
.resume = davinci_mcasp_resume,
|
||||
.playback = {
|
||||
.channels_min = 1,
|
||||
.channels_max = 32 * 16,
|
||||
@ -1915,6 +1848,147 @@ static u32 davinci_mcasp_rxdma_offset(struct davinci_mcasp_pdata *pdata)
|
||||
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)
|
||||
{
|
||||
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;
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
#ifdef CONFIG_PM
|
||||
mcasp->context.xrsr_regs = devm_kcalloc(&pdev->dev,
|
||||
mcasp->num_serializer, sizeof(u32),
|
||||
GFP_KERNEL);
|
||||
@ -2139,6 +2213,15 @@ static int davinci_mcasp_probe(struct platform_device *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,
|
||||
&davinci_mcasp_component,
|
||||
&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);
|
||||
switch (ret) {
|
||||
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);
|
||||
#else
|
||||
dev_err(&pdev->dev, "Missing SND_EDMA_SOC\n");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
#endif
|
||||
break;
|
||||
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);
|
||||
#else
|
||||
dev_err(&pdev->dev, "Missing SND_SDMA_SOC\n");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
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;
|
||||
}
|
||||
|
||||
#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 = {
|
||||
.probe = davinci_mcasp_probe,
|
||||
.remove = davinci_mcasp_remove,
|
||||
.driver = {
|
||||
.name = "davinci-mcasp",
|
||||
.pm = &davinci_mcasp_pm_ops,
|
||||
.of_match_table = mcasp_dt_ids,
|
||||
},
|
||||
};
|
||||
|
@ -1,8 +1,15 @@
|
||||
config SND_SOC_XILINX_I2S
|
||||
tristate "Audio support for the the Xilinx I2S"
|
||||
tristate "Audio support for the Xilinx I2S"
|
||||
help
|
||||
Select this option to enable Xilinx I2S Audio. This enables
|
||||
I2S playback and capture using xilinx soft IP. In transmitter
|
||||
mode, IP receives audio in AES format, extracts PCM and sends
|
||||
PCM data. In receiver mode, IP receives PCM audio and
|
||||
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
|
||||
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
|
||||
/*
|
||||
* Xilinx ASoC I2S audio support
|
||||
*
|
||||
* Copyright (C) 2018 Xilinx, Inc.
|
||||
*
|
||||
* Author: Praveen Vuppala <praveenv@xilinx.com>
|
||||
* Author: Maruthi Srinivas Bayyavarapu <maruthis@xilinx.com>
|
||||
*/
|
||||
//
|
||||
// Xilinx ASoC I2S audio support
|
||||
//
|
||||
// Copyright (C) 2018 Xilinx, Inc.
|
||||
//
|
||||
// Author: Praveen Vuppala <praveenv@xilinx.com>
|
||||
// Author: Maruthi Srinivas Bayyavarapu <maruthis@xilinx.com>
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
|
Loading…
Reference in New Issue
Block a user