2
0
mirror of https://github.com/edk2-porting/linux-next.git synced 2024-12-21 19:53:59 +08:00

Merge remote-tracking branch 'asoc/topic/inte' into asoc-next

This commit is contained in:
Mark Brown 2017-07-03 16:51:38 +01:00
commit b821d298f8
64 changed files with 4397 additions and 498 deletions

View File

@ -10,6 +10,8 @@ see ${LINUX}/Documentation/devicetree/bindings/sound/simple-card.txt
Below are same as Simple-Card.
- label
- widgets
- routing
- dai-format
- frame-master
- bitclock-master
@ -24,6 +26,9 @@ Required properties:
- compatible : "audio-graph-card";
- dais : list of CPU DAI port{s}
Optional properties:
- pa-gpios: GPIO used to control external amplifier.
Example: Single DAI case
sound_card {

View File

@ -90,9 +90,12 @@ Example 2. 2 CPU 1 Codec (Mixing)
...
port {
codec_endpoint: endpoint {
codec_endpoint0: endpoint {
remote-endpoint = <&cpu_endpoint0>;
};
codec_endpoint1: endpoint {
remote-endpoint = <&cpu_endpoint1>;
};
};
};
@ -101,7 +104,7 @@ Example 2. 2 CPU 1 Codec (Mixing)
ports {
cpu_port0: port {
cpu_endpoint0: endpoint {
remote-endpoint = <&codec_endpoint>;
remote-endpoint = <&codec_endpoint0>;
dai-format = "left_j";
...
@ -109,6 +112,8 @@ Example 2. 2 CPU 1 Codec (Mixing)
};
cpu_port1: port {
cpu_endpoint1: endpoint {
remote-endpoint = <&codec_endpoint1>;
dai-format = "left_j";
...
};

View File

@ -69,6 +69,8 @@ Optional properties:
- nuvoton,jack-insert-debounce: number from 0 to 7 that sets debounce time to 2^(n+2) ms
- nuvoton,jack-eject-debounce: number from 0 to 7 that sets debounce time to 2^(n+2) ms
- nuvoton,crosstalk-bypass: make crosstalk function bypass if set.
- clocks: list of phandle and clock specifier pairs according to common clock bindings for the
clocks described in clock-names
- clock-names: should include "mclk" for the MCLK master clock
@ -96,6 +98,7 @@ Example:
nuvoton,short-key-debounce = <2>;
nuvoton,jack-insert-debounce = <7>;
nuvoton,jack-eject-debounce = <7>;
nuvoton,crosstalk-bypass;
clock-names = "mclk";
clocks = <&tegra_car TEGRA210_CLK_CLK_OUT_2>;

View File

@ -0,0 +1,39 @@
* Rockchip PDM controller
Required properties:
- compatible: "rockchip,pdm"
- reg: physical base address of the controller and length of memory mapped
region.
- dmas: DMA specifiers for rx dma. See the DMA client binding,
Documentation/devicetree/bindings/dma/dma.txt
- dma-names: should include "rx".
- clocks: a list of phandle + clock-specifer pairs, one for each entry in clock-names.
- clock-names: should contain following:
- "pdm_hclk": clock for PDM BUS
- "pdm_clk" : clock for PDM controller
- pinctrl-names: Must contain a "default" entry.
- pinctrl-N: One property must exist for each entry in
pinctrl-names. See ../pinctrl/pinctrl-bindings.txt
for details of the property values.
Example for rk3328 PDM controller:
pdm: pdm@ff040000 {
compatible = "rockchip,pdm";
reg = <0x0 0xff040000 0x0 0x1000>;
clocks = <&clk_pdm>, <&clk_gates28 0>;
clock-names = "pdm_clk", "pdm_hclk";
dmas = <&pdma 16>;
#dma-cells = <1>;
dma-names = "rx";
pinctrl-names = "default", "sleep";
pinctrl-0 = <&pdmm0_clk
&pdmm0_fsync
&pdmm0_sdi0
&pdmm0_sdi1
&pdmm0_sdi2
&pdmm0_sdi3>;
pinctrl-1 = <&pdmm0_sleep>;
status = "disabled";
};

View File

@ -9,7 +9,9 @@ Required properties:
- compatible: should be one of the following:
- "rockchip,rk3066-spdif"
- "rockchip,rk3188-spdif"
- "rockchip,rk3228-spdif"
- "rockchip,rk3288-spdif"
- "rockchip,rk3328-spdif"
- "rockchip,rk3366-spdif"
- "rockchip,rk3368-spdif"
- "rockchip,rk3399-spdif"

View File

@ -5,11 +5,6 @@ Required properties:
- compatible - "samsung,odroidxu3-audio" - for Odroid XU3 board,
"samsung,odroidxu4-audio" - for Odroid XU4 board
- model - the user-visible name of this sound complex
- 'cpu' subnode with a 'sound-dai' property containing the phandle of the I2S
controller
- 'codec' subnode with a 'sound-dai' property containing list of phandles
to the CODEC nodes, first entry must be corresponding to the MAX98090
CODEC and the second entry must be the phandle of the HDMI IP block node
- clocks - should contain entries matching clock names in the clock-names
property
- clock-names - should contain following entries:
@ -32,12 +27,18 @@ Required properties:
For Odroid XU4:
no entries
Required sub-nodes:
- 'cpu' subnode with a 'sound-dai' property containing the phandle of the I2S
controller
- 'codec' subnode with a 'sound-dai' property containing list of phandles
to the CODEC nodes, first entry must be corresponding to the MAX98090
CODEC and the second entry must be the phandle of the HDMI IP block node
Example:
sound {
compatible = "samsung,odroidxu3-audio";
samsung,cpu-dai = <&i2s0>;
samsung,codec-dai = <&max98090>;
model = "Odroid-XU3";
samsung,audio-routing =
"Headphone Jack", "HPL",

View File

@ -0,0 +1,62 @@
STMicroelectronics STM32 SPI/I2S Controller
The SPI/I2S block supports I2S/PCM protocols when configured on I2S mode.
Only some SPI instances support I2S.
Required properties:
- compatible: Must be "st,stm32h7-i2s"
- reg: Offset and length of the device's register set.
- interrupts: Must contain the interrupt line id.
- clocks: Must contain phandle and clock specifier pairs for each entry
in clock-names.
- clock-names: Must contain "i2sclk", "pclk", "x8k" and "x11k".
"i2sclk": clock which feeds the internal clock generator
"pclk": clock which feeds the peripheral bus interface
"x8k": I2S parent clock for sampling rates multiple of 8kHz.
"x11k": I2S parent clock for sampling rates multiple of 11.025kHz.
- dmas: DMA specifiers for tx and rx dma.
See Documentation/devicetree/bindings/dma/stm32-dma.txt.
- dma-names: Identifier for each DMA request line. Must be "tx" and "rx".
- pinctrl-names: should contain only value "default"
- pinctrl-0: see Documentation/devicetree/bindings/pinctrl/pinctrl-stm32.txt
Optional properties:
- resets: Reference to a reset controller asserting the reset controller
The device node should contain one 'port' child node with one child 'endpoint'
node, according to the bindings defined in Documentation/devicetree/bindings/
graph.txt.
Example:
sound_card {
compatible = "audio-graph-card";
dais = <&i2s2_port>;
};
i2s2: audio-controller@40003800 {
compatible = "st,stm32h7-i2s";
reg = <0x40003800 0x400>;
interrupts = <36>;
clocks = <&rcc PCLK1>, <&rcc SPI2_CK>, <&rcc PLL1_Q>, <&rcc PLL2_P>;
clock-names = "pclk", "i2sclk", "x8k", "x11k";
dmas = <&dmamux2 2 39 0x400 0x1>,
<&dmamux2 3 40 0x400 0x1>;
dma-names = "rx", "tx";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2s2>;
i2s2_port: port@0 {
cpu_endpoint: endpoint {
remote-endpoint = <&codec_endpoint>;
format = "i2s";
};
};
};
audio-codec {
codec_port: port@0 {
codec_endpoint: endpoint {
remote-endpoint = <&cpu_endpoint>;
};
};
};

View File

@ -6,7 +6,7 @@ The SAI contains two independent audio sub-blocks. Each sub-block has
its own clock generator and I/O lines controller.
Required properties:
- compatible: Should be "st,stm32f4-sai"
- compatible: Should be "st,stm32f4-sai" or "st,stm32h7-sai"
- reg: Base address and size of SAI common register set.
- clocks: Must contain phandle and clock specifier pairs for each entry
in clock-names.
@ -36,6 +36,10 @@ SAI subnodes required properties:
- pinctrl-names: should contain only value "default"
- pinctrl-0: see Documentation/devicetree/bindings/pinctrl/pinctrl-stm32.txt
The device node should contain one 'port' child node with one child 'endpoint'
node, according to the bindings defined in Documentation/devicetree/bindings/
graph.txt.
Example:
sound_card {
compatible = "audio-graph-card";
@ -43,38 +47,29 @@ sound_card {
};
sai1: sai1@40015800 {
compatible = "st,stm32f4-sai";
compatible = "st,stm32h7-sai";
#address-cells = <1>;
#size-cells = <1>;
ranges;
ranges = <0 0x40015800 0x400>;
reg = <0x40015800 0x4>;
clocks = <&rcc 1 CLK_SAIQ_PDIV>, <&rcc 1 CLK_I2SQ_PDIV>;
clocks = <&rcc PLL1_Q>, <&rcc PLL2_P>;
clock-names = "x8k", "x11k";
interrupts = <87>;
sai1b: audio-controller@40015824 {
#sound-dai-cells = <0>;
compatible = "st,stm32-sai-sub-b";
reg = <0x40015824 0x1C>;
clocks = <&rcc 1 CLK_SAI2>;
sai1a: audio-controller@40015804 {
compatible = "st,stm32-sai-sub-a";
reg = <0x4 0x1C>;
clocks = <&rcc SAI1_CK>;
clock-names = "sai_ck";
dmas = <&dma2 5 0 0x400 0x0>;
dmas = <&dmamux1 1 87 0x400 0x0>;
dma-names = "tx";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_sai1b>;
pinctrl-0 = <&pinctrl_sai1a>;
ports {
#address-cells = <1>;
#size-cells = <0>;
sai1b_port: port@0 {
reg = <0>;
cpu_endpoint: endpoint {
remote-endpoint = <&codec_endpoint>;
audio-graph-card,format = "i2s";
audio-graph-card,bitclock-master = <&codec_endpoint>;
audio-graph-card,frame-master = <&codec_endpoint>;
};
sai1b_port: port {
cpu_endpoint: endpoint {
remote-endpoint = <&codec_endpoint>;
format = "i2s";
};
};
};

View File

@ -0,0 +1,56 @@
STMicroelectronics STM32 S/PDIF receiver (SPDIFRX).
The SPDIFRX peripheral, is designed to receive an S/PDIF flow compliant with
IEC-60958 and IEC-61937.
Required properties:
- compatible: should be "st,stm32h7-spdifrx"
- reg: cpu DAI IP base address and size
- clocks: must contain an entry for kclk (used as S/PDIF signal reference)
- clock-names: must contain "kclk"
- interrupts: cpu DAI interrupt line
- dmas: DMA specifiers for audio data DMA and iec control flow DMA
See STM32 DMA bindings, Documentation/devicetree/bindings/dma/stm32-dma.txt
- dma-names: two dmas have to be defined, "rx" and "rx-ctrl"
Optional properties:
- resets: Reference to a reset controller asserting the SPDIFRX
The device node should contain one 'port' child node with one child 'endpoint'
node, according to the bindings defined in Documentation/devicetree/bindings/
graph.txt.
Example:
spdifrx: spdifrx@40004000 {
compatible = "st,stm32h7-spdifrx";
reg = <0x40004000 0x400>;
clocks = <&rcc SPDIFRX_CK>;
clock-names = "kclk";
interrupts = <97>;
dmas = <&dmamux1 2 93 0x400 0x0>,
<&dmamux1 3 94 0x400 0x0>;
dma-names = "rx", "rx-ctrl";
pinctrl-0 = <&spdifrx_pins>;
pinctrl-names = "default";
spdifrx_port: port {
cpu_endpoint: endpoint {
remote-endpoint = <&codec_endpoint>;
};
};
};
spdif_in: spdif-in {
compatible = "linux,spdif-dir";
codec_port: port {
codec_endpoint: endpoint {
remote-endpoint = <&cpu_endpoint>;
};
};
};
soundcard {
compatible = "audio-graph-card";
dais = <&spdifrx_port>;
};

View File

@ -7,6 +7,7 @@ Required properties:
- "allwinner,sun7i-a20-codec"
- "allwinner,sun8i-a23-codec"
- "allwinner,sun8i-h3-codec"
- "allwinner,sun8i-v3s-codec"
- reg: must contain the registers location and length
- interrupts: must contain the codec interrupt
- dmas: DMA channels for tx and rx dma. See the DMA client binding,
@ -25,6 +26,7 @@ Required properties for the following compatibles:
- "allwinner,sun6i-a31-codec"
- "allwinner,sun8i-a23-codec"
- "allwinner,sun8i-h3-codec"
- "allwinner,sun8i-v3s-codec"
- resets: phandle to the reset control for this device
- allwinner,audio-routing: A list of the connections between audio components.
Each entry is a pair of strings, the first being the
@ -34,15 +36,15 @@ Required properties for the following compatibles:
Audio pins on the SoC:
"HP"
"HPCOM"
"LINEIN"
"LINEOUT" (not on sun8i-a23)
"LINEIN" (not on sun8i-v3s)
"LINEOUT" (not on sun8i-a23 or sun8i-v3s)
"MIC1"
"MIC2"
"MIC2" (not on sun8i-v3s)
"MIC3" (sun6i-a31 only)
Microphone biases from the SoC:
"HBIAS"
"MBIAS"
"MBIAS" (not on sun8i-v3s)
Board connectors:
"Headphone"
@ -55,6 +57,7 @@ Required properties for the following compatibles:
Required properties for the following compatibles:
- "allwinner,sun8i-a23-codec"
- "allwinner,sun8i-h3-codec"
- "allwinner,sun8i-v3s-codec"
- allwinner,codec-analog-controls: A phandle to the codec analog controls
block in the PRCM.

View File

@ -4,6 +4,7 @@ Required properties:
- compatible: must be one of the following compatibles:
- "allwinner,sun8i-a23-codec-analog"
- "allwinner,sun8i-h3-codec-analog"
- "allwinner,sun8i-v3s-codec-analog"
Required properties if not a sub-node of the PRCM node:
- reg: must contain the registers location and length

View File

@ -0,0 +1,24 @@
ZTE ZX AUD96P22 Audio Codec
Required properties:
- compatible: Must be "zte,zx-aud96p22"
- #sound-dai-cells: Should be 0
- reg: I2C bus slave address of AUD96P22
Example:
i2c0: i2c@1486000 {
compatible = "zte,zx296718-i2c";
reg = <0x01486000 0x1000>;
interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&audiocrm AUDIO_I2C0_WCLK>;
clock-frequency = <1600000>;
aud96p22: codec@22 {
compatible = "zte,zx-aud96p22";
#sound-dai-cells = <0>;
reg = <0x22>;
};
};

View File

@ -18,9 +18,11 @@
#ifndef __HDMI_CODEC_H__
#define __HDMI_CODEC_H__
#include <linux/of_graph.h>
#include <linux/hdmi.h>
#include <drm/drm_edid.h>
#include <sound/asoundef.h>
#include <sound/soc.h>
#include <uapi/sound/asound.h>
/*
@ -87,6 +89,13 @@ struct hdmi_codec_ops {
*/
int (*get_eld)(struct device *dev, void *data,
uint8_t *buf, size_t len);
/*
* Getting DAI ID
* Optional
*/
int (*get_dai_id)(struct snd_soc_component *comment,
struct device_node *endpoint);
};
/* HDMI codec initalization data */

View File

@ -21,8 +21,10 @@ struct rt5645_platform_data {
/* 0 = IN2P; 1 = GPIO6; 2 = GPIO10; 3 = GPIO12 */
unsigned int jd_mode;
/* Invert JD when jack insert */
bool jd_invert;
/* Use level triggered irq */
bool level_trigger_irq;
/* Invert JD1_1 status polarity */
bool inv_jd1_1;
};
#endif

View File

@ -22,6 +22,11 @@ struct asoc_simple_dai {
struct clk *clk;
};
struct asoc_simple_card_data {
u32 convert_rate;
u32 convert_channels;
};
int asoc_simple_card_parse_daifmt(struct device *dev,
struct device_node *node,
struct device_node *codec,
@ -45,6 +50,8 @@ int asoc_simple_card_parse_clk(struct device *dev,
struct device_node *dai_of_node,
struct asoc_simple_dai *simple_dai,
const char *name);
int asoc_simple_card_clk_enable(struct asoc_simple_dai *dai);
void asoc_simple_card_clk_disable(struct asoc_simple_dai *dai);
#define asoc_simple_card_parse_cpu(node, dai_link, \
list_name, cells_name, is_single_link) \
@ -73,6 +80,12 @@ int asoc_simple_card_parse_graph_dai(struct device_node *ep,
struct device_node **endpoint_np,
const char **dai_name);
#define asoc_simple_card_of_parse_tdm(np, dai) \
snd_soc_of_parse_tdm_slot(np, &(dai)->tx_slot_mask, \
&(dai)->rx_slot_mask, \
&(dai)->slots, \
&(dai)->slot_width);
int asoc_simple_card_init_dai(struct snd_soc_dai *dai,
struct asoc_simple_dai *simple_dai);
@ -82,4 +95,15 @@ void asoc_simple_card_canonicalize_cpu(struct snd_soc_dai_link *dai_link,
int asoc_simple_card_clean_reference(struct snd_soc_card *card);
void asoc_simple_card_convert_fixup(struct asoc_simple_card_data *data,
struct snd_pcm_hw_params *params);
void asoc_simple_card_parse_convert(struct device *dev, char *prefix,
struct asoc_simple_card_data *data);
int asoc_simple_card_of_parse_routing(struct snd_soc_card *card,
char *prefix,
int optional);
int asoc_simple_card_of_parse_widgets(struct snd_soc_card *card,
char *prefix);
#endif /* __SIMPLE_CARD_UTILS_H */

View File

@ -28,6 +28,8 @@ struct snd_soc_component;
struct snd_soc_tplg_pcm_fe;
struct snd_soc_dapm_context;
struct snd_soc_card;
struct snd_kcontrol_new;
struct snd_soc_dai_link;
/* object scan be loaded and unloaded in groups with identfying indexes */
#define SND_SOC_TPLG_INDEX_ALL 0 /* ID that matches all FW objects */
@ -116,6 +118,9 @@ struct snd_soc_tplg_ops {
int (*widget_load)(struct snd_soc_component *,
struct snd_soc_dapm_widget *,
struct snd_soc_tplg_dapm_widget *);
int (*widget_ready)(struct snd_soc_component *,
struct snd_soc_dapm_widget *,
struct snd_soc_tplg_dapm_widget *);
int (*widget_unload)(struct snd_soc_component *,
struct snd_soc_dobj *);

View File

@ -1119,6 +1119,11 @@ config SND_SOC_WM9713
tristate
select REGMAP_AC97
config SND_SOC_ZX_AUD96P22
tristate "ZTE ZX AUD96P22 CODEC"
depends on I2C
select REGMAP_I2C
# Amp
config SND_SOC_LM4857
tristate

View File

@ -225,6 +225,7 @@ snd-soc-wm9705-objs := wm9705.o
snd-soc-wm9712-objs := wm9712.o
snd-soc-wm9713-objs := wm9713.o
snd-soc-wm-hubs-objs := wm_hubs.o
snd-soc-zx-aud96p22-objs := zx_aud96p22.o
# Amp
snd-soc-dio2125-objs := dio2125.o
snd-soc-max9877-objs := max9877.o
@ -457,6 +458,7 @@ obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
obj-$(CONFIG_SND_SOC_WM_ADSP) += snd-soc-wm-adsp.o
obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
obj-$(CONFIG_SND_SOC_ZX_AUD96P22) += snd-soc-zx-aud96p22.o
# Amp
obj-$(CONFIG_SND_SOC_DIO2125) += snd-soc-dio2125.o

View File

@ -25,17 +25,6 @@
#include <drm/drm_crtc.h> /* This is only to get MAX_ELD_BYTES */
struct hdmi_device {
struct device *dev;
struct list_head list;
int cnt;
};
#define pos_to_hdmi_device(pos) container_of((pos), struct hdmi_device, list)
LIST_HEAD(hdmi_device_list);
static DEFINE_MUTEX(hdmi_mutex);
#define DAI_NAME_SIZE 16
#define HDMI_CODEC_CHMAP_IDX_UNKNOWN -1
struct hdmi_codec_channel_map_table {
@ -293,7 +282,6 @@ struct hdmi_codec_priv {
struct hdmi_codec_daifmt daifmt[2];
struct mutex current_stream_lock;
struct snd_pcm_substream *current_stream;
struct snd_pcm_hw_constraint_list ratec;
uint8_t eld[MAX_ELD_BYTES];
struct snd_pcm_chmap *chmap_info;
unsigned int chmap_idx;
@ -702,6 +690,7 @@ static int hdmi_codec_pcm_new(struct snd_soc_pcm_runtime *rtd,
}
static struct snd_soc_dai_driver hdmi_i2s_dai = {
.name = "i2s-hifi",
.id = DAI_ID_I2S,
.playback = {
.stream_name = "Playback",
@ -716,6 +705,7 @@ static struct snd_soc_dai_driver hdmi_i2s_dai = {
};
static const struct snd_soc_dai_driver hdmi_spdif_dai = {
.name = "spdif-hifi",
.id = DAI_ID_SPDIF,
.playback = {
.stream_name = "Playback",
@ -728,30 +718,16 @@ static const struct snd_soc_dai_driver hdmi_spdif_dai = {
.pcm_new = hdmi_codec_pcm_new,
};
static char hdmi_dai_name[][DAI_NAME_SIZE] = {
"hdmi-hifi.0",
"hdmi-hifi.1",
"hdmi-hifi.2",
"hdmi-hifi.3",
};
static int hdmi_of_xlate_dai_name(struct snd_soc_component *component,
struct of_phandle_args *args,
const char **dai_name)
static int hdmi_of_xlate_dai_id(struct snd_soc_component *component,
struct device_node *endpoint)
{
int id;
struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
int ret = -ENOTSUPP; /* see snd_soc_get_dai_id() */
if (args->args_count)
id = args->args[0];
else
id = 0;
if (hcp->hcd.ops->get_dai_id)
ret = hcp->hcd.ops->get_dai_id(component, endpoint);
if (id < ARRAY_SIZE(hdmi_dai_name)) {
*dai_name = hdmi_dai_name[id];
return 0;
}
return -EAGAIN;
return ret;
}
static struct snd_soc_codec_driver hdmi_codec = {
@ -762,7 +738,7 @@ static struct snd_soc_codec_driver hdmi_codec = {
.num_dapm_widgets = ARRAY_SIZE(hdmi_widgets),
.dapm_routes = hdmi_routes,
.num_dapm_routes = ARRAY_SIZE(hdmi_routes),
.of_xlate_dai_name = hdmi_of_xlate_dai_name,
.of_xlate_dai_id = hdmi_of_xlate_dai_id,
},
};
@ -771,8 +747,6 @@ static int hdmi_codec_probe(struct platform_device *pdev)
struct hdmi_codec_pdata *hcd = pdev->dev.platform_data;
struct device *dev = &pdev->dev;
struct hdmi_codec_priv *hcp;
struct hdmi_device *hd;
struct list_head *pos;
int dai_count, i = 0;
int ret;
@ -794,35 +768,6 @@ static int hdmi_codec_probe(struct platform_device *pdev)
if (!hcp)
return -ENOMEM;
hd = NULL;
mutex_lock(&hdmi_mutex);
list_for_each(pos, &hdmi_device_list) {
struct hdmi_device *tmp = pos_to_hdmi_device(pos);
if (tmp->dev == dev->parent) {
hd = tmp;
break;
}
}
if (!hd) {
hd = devm_kzalloc(dev, sizeof(*hd), GFP_KERNEL);
if (!hd) {
mutex_unlock(&hdmi_mutex);
return -ENOMEM;
}
hd->dev = dev->parent;
list_add_tail(&hd->list, &hdmi_device_list);
}
mutex_unlock(&hdmi_mutex);
if (hd->cnt >= ARRAY_SIZE(hdmi_dai_name)) {
dev_err(dev, "too many hdmi codec are deteced\n");
return -EINVAL;
}
hcp->hcd = *hcd;
mutex_init(&hcp->current_stream_lock);
@ -835,14 +780,11 @@ static int hdmi_codec_probe(struct platform_device *pdev)
hcp->daidrv[i] = hdmi_i2s_dai;
hcp->daidrv[i].playback.channels_max =
hcd->max_i2s_channels;
hcp->daidrv[i].name = hdmi_dai_name[hd->cnt++];
i++;
}
if (hcd->spdif) {
if (hcd->spdif)
hcp->daidrv[i] = hdmi_spdif_dai;
hcp->daidrv[i].name = hdmi_dai_name[hd->cnt++];
}
ret = snd_soc_register_codec(dev, &hdmi_codec, hcp->daidrv,
dai_count);
@ -859,20 +801,8 @@ static int hdmi_codec_probe(struct platform_device *pdev)
static int hdmi_codec_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct list_head *pos;
struct hdmi_codec_priv *hcp;
mutex_lock(&hdmi_mutex);
list_for_each(pos, &hdmi_device_list) {
struct hdmi_device *tmp = pos_to_hdmi_device(pos);
if (tmp->dev == dev->parent) {
list_del(pos);
break;
}
}
mutex_unlock(&hdmi_mutex);
hcp = dev_get_drvdata(dev);
kfree(hcp->chmap_info);
snd_soc_unregister_codec(dev);

View File

@ -132,7 +132,7 @@ enum rates {
pcm_rate_48, max_pcm_rate,
};
struct ni_div_rates {
static const struct ni_div_rates {
u32 mclk;
u16 ni[max_pcm_rate];
} ni_div[] = {

View File

@ -223,8 +223,8 @@ struct pm8916_wcd_analog_priv {
u16 codec_version;
struct clk *mclk;
struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
bool micbias1_cap_mode;
bool micbias2_cap_mode;
unsigned int micbias1_cap_mode;
unsigned int micbias2_cap_mode;
};
static const char *const adc2_mux_text[] = { "ZERO", "INP2", "INP3" };
@ -285,7 +285,7 @@ static void pm8916_wcd_analog_micbias_enable(struct snd_soc_codec *codec)
static int pm8916_wcd_analog_enable_micbias_ext(struct snd_soc_codec
*codec, int event,
int reg, u32 cap_mode)
int reg, unsigned int cap_mode)
{
switch (event) {
case SND_SOC_DAPM_POST_PMU:

View File

@ -1124,6 +1124,57 @@ static int nau8824_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return 0;
}
/**
* nau8824_set_tdm_slot - configure DAI TDM.
* @dai: DAI
* @tx_mask: Bitmask representing active TX slots. Ex.
* 0xf for normal 4 channel TDM.
* 0xf0 for shifted 4 channel TDM
* @rx_mask: Bitmask [0:1] representing active DACR RX slots.
* Bitmask [2:3] representing active DACL RX slots.
* 00=CH0,01=CH1,10=CH2,11=CH3. Ex.
* 0xf for DACL/R selecting TDM CH3.
* 0xf0 for DACL/R selecting shifted TDM CH3.
* @slots: Number of slots in use.
* @slot_width: Width in bits for each slot.
*
* Configures a DAI for TDM operation. Only support 4 slots TDM.
*/
static int nau8824_set_tdm_slot(struct snd_soc_dai *dai,
unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
{
struct snd_soc_codec *codec = dai->codec;
struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
unsigned int tslot_l = 0, ctrl_val = 0;
if (slots > 4 || ((tx_mask & 0xf0) && (tx_mask & 0xf)) ||
((rx_mask & 0xf0) && (rx_mask & 0xf)) ||
((rx_mask & 0xf0) && (tx_mask & 0xf)) ||
((rx_mask & 0xf) && (tx_mask & 0xf0)))
return -EINVAL;
ctrl_val |= (NAU8824_TDM_MODE | NAU8824_TDM_OFFSET_EN);
if (tx_mask & 0xf0) {
tslot_l = 4 * slot_width;
ctrl_val |= (tx_mask >> 4);
} else {
ctrl_val |= tx_mask;
}
if (rx_mask & 0xf0)
ctrl_val |= ((rx_mask >> 4) << NAU8824_TDM_DACR_RX_SFT);
else
ctrl_val |= (rx_mask << NAU8824_TDM_DACR_RX_SFT);
regmap_update_bits(nau8824->regmap, NAU8824_REG_TDM_CTRL,
NAU8824_TDM_MODE | NAU8824_TDM_OFFSET_EN |
NAU8824_TDM_DACL_RX_MASK | NAU8824_TDM_DACR_RX_MASK |
NAU8824_TDM_TX_MASK, ctrl_val);
regmap_update_bits(nau8824->regmap, NAU8824_REG_PORT0_LEFT_TIME_SLOT,
NAU8824_TSLOT_L_MASK, tslot_l);
return 0;
}
/**
* nau8824_calc_fll_param - Calculate FLL parameters.
* @fll_in: external clock provided to codec.
@ -1440,6 +1491,7 @@ static struct snd_soc_codec_driver nau8824_codec_driver = {
static const struct snd_soc_dai_ops nau8824_dai_ops = {
.hw_params = nau8824_hw_params,
.set_fmt = nau8824_set_fmt,
.set_tdm_slot = nau8824_set_tdm_slot,
};
#define NAU8824_RATES SNDRV_PCM_RATE_8000_192000

View File

@ -258,6 +258,18 @@
#define NAU8824_I2S_MS_SLAVE (0 << NAU8824_I2S_MS_SFT)
#define NAU8824_I2S_BLK_DIV_MASK 0x7
/* PORT0_LEFT_TIME_SLOT (0x1E) */
#define NAU8824_TSLOT_L_MASK 0x3ff
/* TDM_CTRL (0x20) */
#define NAU8824_TDM_MODE (0x1 << 15)
#define NAU8824_TDM_OFFSET_EN (0x1 << 14)
#define NAU8824_TDM_DACL_RX_SFT 6
#define NAU8824_TDM_DACL_RX_MASK (0x3 << NAU8824_TDM_DACL_RX_SFT)
#define NAU8824_TDM_DACR_RX_SFT 4
#define NAU8824_TDM_DACR_RX_MASK (0x3 << NAU8824_TDM_DACR_RX_SFT)
#define NAU8824_TDM_TX_MASK 0xf
/* ADC_FILTER_CTRL (0x24) */
#define NAU8824_ADC_SYNC_DOWN_MASK 0x3
#define NAU8824_ADC_SYNC_DOWN_32 0

View File

@ -1612,7 +1612,6 @@ static int nau8825_jack_insert(struct nau8825 *nau8825)
snd_soc_dapm_sync(dapm);
break;
case 2:
case 3:
dev_dbg(nau8825->dev, "CTIA (micgnd2) mic connected\n");
type = SND_JACK_HEADSET;
@ -1632,6 +1631,11 @@ static int nau8825_jack_insert(struct nau8825 *nau8825)
snd_soc_dapm_force_enable_pin(dapm, "SAR");
snd_soc_dapm_sync(dapm);
break;
case 3:
/* detect error case */
dev_err(nau8825->dev, "detection error; disable mic function\n");
type = SND_JACK_HEADPHONE;
break;
}
/* Leaving HPOL/R grounded after jack insert by default. They will be
@ -1682,7 +1686,7 @@ static irqreturn_t nau8825_interrupt(int irq, void *data)
} else if (active_irq & NAU8825_HEADSET_COMPLETION_IRQ) {
if (nau8825_is_jack_inserted(regmap)) {
event |= nau8825_jack_insert(nau8825);
if (!nau8825->high_imped) {
if (!nau8825->xtalk_bypass && !nau8825->high_imped) {
/* Apply the cross talk suppression in the
* headset without high impedance.
*/
@ -2328,6 +2332,13 @@ static int nau8825_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_OFF:
/* Reset the configuration of jack type for detection */
/* Detach 2kOhm Resistors from MICBIAS to MICGND1/2 */
regmap_update_bits(nau8825->regmap, NAU8825_REG_MIC_BIAS,
NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2, 0);
/* ground HPL/HPR, MICGRND1/2 */
regmap_update_bits(nau8825->regmap,
NAU8825_REG_HSD_CTRL, 0xf, 0xf);
/* Cancel and reset cross talk detection funciton */
nau8825_xtalk_cancel(nau8825);
/* Turn off all interruptions before system shutdown. Keep the
@ -2351,6 +2362,10 @@ static int __maybe_unused nau8825_suspend(struct snd_soc_codec *codec)
disable_irq(nau8825->irq);
snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
/* Power down codec power; don't suppoet button wakeup */
snd_soc_dapm_disable_pin(nau8825->dapm, "SAR");
snd_soc_dapm_disable_pin(nau8825->dapm, "MICBIAS");
snd_soc_dapm_sync(nau8825->dapm);
regcache_cache_only(nau8825->regmap, true);
regcache_mark_dirty(nau8825->regmap);
@ -2425,10 +2440,13 @@ static void nau8825_print_device_properties(struct nau8825 *nau8825)
nau8825->jack_insert_debounce);
dev_dbg(dev, "jack-eject-debounce: %d\n",
nau8825->jack_eject_debounce);
dev_dbg(dev, "crosstalk-bypass: %d\n",
nau8825->xtalk_bypass);
}
static int nau8825_read_device_properties(struct device *dev,
struct nau8825 *nau8825) {
int ret;
nau8825->jkdet_enable = device_property_read_bool(dev,
"nuvoton,jkdet-enable");
@ -2436,30 +2454,60 @@ static int nau8825_read_device_properties(struct device *dev,
"nuvoton,jkdet-pull-enable");
nau8825->jkdet_pull_up = device_property_read_bool(dev,
"nuvoton,jkdet-pull-up");
device_property_read_u32(dev, "nuvoton,jkdet-polarity",
ret = device_property_read_u32(dev, "nuvoton,jkdet-polarity",
&nau8825->jkdet_polarity);
device_property_read_u32(dev, "nuvoton,micbias-voltage",
if (ret)
nau8825->jkdet_polarity = 1;
ret = device_property_read_u32(dev, "nuvoton,micbias-voltage",
&nau8825->micbias_voltage);
device_property_read_u32(dev, "nuvoton,vref-impedance",
if (ret)
nau8825->micbias_voltage = 6;
ret = device_property_read_u32(dev, "nuvoton,vref-impedance",
&nau8825->vref_impedance);
device_property_read_u32(dev, "nuvoton,sar-threshold-num",
if (ret)
nau8825->vref_impedance = 2;
ret = device_property_read_u32(dev, "nuvoton,sar-threshold-num",
&nau8825->sar_threshold_num);
device_property_read_u32_array(dev, "nuvoton,sar-threshold",
if (ret)
nau8825->sar_threshold_num = 4;
ret = device_property_read_u32_array(dev, "nuvoton,sar-threshold",
nau8825->sar_threshold, nau8825->sar_threshold_num);
device_property_read_u32(dev, "nuvoton,sar-hysteresis",
if (ret) {
nau8825->sar_threshold[0] = 0x08;
nau8825->sar_threshold[1] = 0x12;
nau8825->sar_threshold[2] = 0x26;
nau8825->sar_threshold[3] = 0x73;
}
ret = device_property_read_u32(dev, "nuvoton,sar-hysteresis",
&nau8825->sar_hysteresis);
device_property_read_u32(dev, "nuvoton,sar-voltage",
if (ret)
nau8825->sar_hysteresis = 0;
ret = device_property_read_u32(dev, "nuvoton,sar-voltage",
&nau8825->sar_voltage);
device_property_read_u32(dev, "nuvoton,sar-compare-time",
if (ret)
nau8825->sar_voltage = 6;
ret = device_property_read_u32(dev, "nuvoton,sar-compare-time",
&nau8825->sar_compare_time);
device_property_read_u32(dev, "nuvoton,sar-sampling-time",
if (ret)
nau8825->sar_compare_time = 1;
ret = device_property_read_u32(dev, "nuvoton,sar-sampling-time",
&nau8825->sar_sampling_time);
device_property_read_u32(dev, "nuvoton,short-key-debounce",
if (ret)
nau8825->sar_sampling_time = 1;
ret = device_property_read_u32(dev, "nuvoton,short-key-debounce",
&nau8825->key_debounce);
device_property_read_u32(dev, "nuvoton,jack-insert-debounce",
if (ret)
nau8825->key_debounce = 3;
ret = device_property_read_u32(dev, "nuvoton,jack-insert-debounce",
&nau8825->jack_insert_debounce);
device_property_read_u32(dev, "nuvoton,jack-eject-debounce",
if (ret)
nau8825->jack_insert_debounce = 7;
ret = device_property_read_u32(dev, "nuvoton,jack-eject-debounce",
&nau8825->jack_eject_debounce);
if (ret)
nau8825->jack_eject_debounce = 0;
nau8825->xtalk_bypass = device_property_read_bool(dev,
"nuvoton,crosstalk-bypass");
nau8825->mclk = devm_clk_get(dev, "mclk");
if (PTR_ERR(nau8825->mclk) == -EPROBE_DEFER) {

View File

@ -476,6 +476,7 @@ struct nau8825 {
int xtalk_event_mask;
bool xtalk_protect;
int imp_rms[NAU8825_XTALK_IMM];
int xtalk_bypass;
};
int nau8825_enable_jack_detect(struct snd_soc_codec *codec,

View File

@ -9,6 +9,7 @@
* published by the Free Software Foundation.
*/
#include <linux/acpi.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
@ -395,14 +396,14 @@ static const char * const rt5514_dmic_src[] = {
"DMIC1", "DMIC2"
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5514_stereo1_dmic_enum, RT5514_DIG_SOURCE_CTRL,
RT5514_AD0_DMIC_INPUT_SEL_SFT, rt5514_dmic_src);
static const struct snd_kcontrol_new rt5514_sto1_dmic_mux =
SOC_DAPM_ENUM("Stereo1 DMIC Source", rt5514_stereo1_dmic_enum);
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5514_stereo2_dmic_enum, RT5514_DIG_SOURCE_CTRL,
RT5514_AD1_DMIC_INPUT_SEL_SFT, rt5514_dmic_src);
@ -906,9 +907,23 @@ static int rt5514_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
if (rx_mask || tx_mask)
val |= RT5514_TDM_MODE;
if (slots == 4)
switch (slots) {
case 4:
val |= RT5514_TDMSLOT_SEL_RX_4CH | RT5514_TDMSLOT_SEL_TX_4CH;
break;
case 6:
val |= RT5514_TDMSLOT_SEL_RX_6CH | RT5514_TDMSLOT_SEL_TX_6CH;
break;
case 8:
val |= RT5514_TDMSLOT_SEL_RX_8CH | RT5514_TDMSLOT_SEL_TX_8CH;
break;
case 2:
default:
break;
}
switch (slot_width) {
case 20:
@ -919,6 +934,10 @@ static int rt5514_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
val |= RT5514_CH_LEN_RX_24 | RT5514_CH_LEN_TX_24;
break;
case 25:
val |= RT5514_TDM_MODE2;
break;
case 32:
val |= RT5514_CH_LEN_RX_32 | RT5514_CH_LEN_TX_32;
break;
@ -930,7 +949,8 @@ static int rt5514_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
regmap_update_bits(rt5514->regmap, RT5514_I2S_CTRL1, RT5514_TDM_MODE |
RT5514_TDMSLOT_SEL_RX_MASK | RT5514_TDMSLOT_SEL_TX_MASK |
RT5514_CH_LEN_RX_MASK | RT5514_CH_LEN_TX_MASK, val);
RT5514_CH_LEN_RX_MASK | RT5514_CH_LEN_TX_MASK |
RT5514_TDM_MODE2, val);
return 0;
}
@ -1076,6 +1096,14 @@ static const struct of_device_id rt5514_of_match[] = {
MODULE_DEVICE_TABLE(of, rt5514_of_match);
#endif
#ifdef CONFIG_ACPI
static struct acpi_device_id rt5514_acpi_match[] = {
{ "10EC5514", 0},
{},
};
MODULE_DEVICE_TABLE(acpi, rt5514_acpi_match);
#endif
static int rt5514_parse_dt(struct rt5514_priv *rt5514, struct device *dev)
{
device_property_read_u32(dev, "realtek,dmic-init-delay-ms",
@ -1179,6 +1207,7 @@ static const struct dev_pm_ops rt5514_i2_pm_ops = {
static struct i2c_driver rt5514_i2c_driver = {
.driver = {
.name = "rt5514",
.acpi_match_table = ACPI_PTR(rt5514_acpi_match),
.of_match_table = of_match_ptr(rt5514_of_match),
.pm = &rt5514_i2_pm_ops,
},

View File

@ -117,6 +117,8 @@
#define RT5514_POW_ADCFEDL_BIT 0
/* RT5514_I2S_CTRL1 (0x2010) */
#define RT5514_TDM_MODE2 (0x1 << 30)
#define RT5514_TDM_MODE2_SFT 30
#define RT5514_TDM_MODE (0x1 << 28)
#define RT5514_TDM_MODE_SFT 28
#define RT5514_I2S_LR_MASK (0x1 << 26)
@ -136,6 +138,8 @@
#define RT5514_TDMSLOT_SEL_RX_MASK (0x3 << 10)
#define RT5514_TDMSLOT_SEL_RX_SFT 10
#define RT5514_TDMSLOT_SEL_RX_4CH (0x1 << 10)
#define RT5514_TDMSLOT_SEL_RX_6CH (0x2 << 10)
#define RT5514_TDMSLOT_SEL_RX_8CH (0x3 << 10)
#define RT5514_CH_LEN_RX_MASK (0x3 << 8)
#define RT5514_CH_LEN_RX_SFT 8
#define RT5514_CH_LEN_RX_16 (0x0 << 8)
@ -145,6 +149,8 @@
#define RT5514_TDMSLOT_SEL_TX_MASK (0x3 << 6)
#define RT5514_TDMSLOT_SEL_TX_SFT 6
#define RT5514_TDMSLOT_SEL_TX_4CH (0x1 << 6)
#define RT5514_TDMSLOT_SEL_TX_6CH (0x2 << 6)
#define RT5514_TDMSLOT_SEL_TX_8CH (0x3 << 6)
#define RT5514_CH_LEN_TX_MASK (0x3 << 4)
#define RT5514_CH_LEN_TX_SFT 4
#define RT5514_CH_LEN_TX_16 (0x0 << 4)

View File

@ -34,6 +34,17 @@
#include "rl6231.h"
#include "rt5645.h"
#define QUIRK_INV_JD1_1(q) ((q) & 1)
#define QUIRK_LEVEL_IRQ(q) (((q) >> 1) & 1)
#define QUIRK_IN2_DIFF(q) (((q) >> 2) & 1)
#define QUIRK_JD_MODE(q) (((q) >> 4) & 7)
#define QUIRK_DMIC1_DATA_PIN(q) (((q) >> 8) & 3)
#define QUIRK_DMIC2_DATA_PIN(q) (((q) >> 12) & 3)
static unsigned int quirk = -1;
module_param(quirk, uint, 0444);
MODULE_PARM_DESC(quirk, "RT5645 pdata quirk override");
#define RT5645_DEVICE_ID 0x6308
#define RT5650_DEVICE_ID 0x6419
@ -59,7 +70,7 @@ static const struct regmap_range_cfg rt5645_ranges[] = {
static const struct reg_sequence init_list[] = {
{RT5645_PR_BASE + 0x3d, 0x3600},
{RT5645_PR_BASE + 0x1c, 0xfd20},
{RT5645_PR_BASE + 0x1c, 0xfd70},
{RT5645_PR_BASE + 0x20, 0x611f},
{RT5645_PR_BASE + 0x21, 0x4040},
{RT5645_PR_BASE + 0x23, 0x0004},
@ -3151,7 +3162,7 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert)
snd_soc_dapm_sync(dapm);
rt5645->jack_type = SND_JACK_HEADPHONE;
}
if (rt5645->pdata.jd_invert)
if (rt5645->pdata.level_trigger_irq)
regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
RT5645_JD_1_1_MASK, RT5645_JD_1_1_NOR);
} else { /* jack out */
@ -3172,7 +3183,7 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert)
snd_soc_dapm_disable_pin(dapm, "LDO2");
snd_soc_dapm_disable_pin(dapm, "Mic Det Power");
snd_soc_dapm_sync(dapm);
if (rt5645->pdata.jd_invert)
if (rt5645->pdata.level_trigger_irq)
regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV);
}
@ -3238,24 +3249,16 @@ static void rt5645_jack_detect_work(struct work_struct *work)
snd_soc_jack_report(rt5645->mic_jack,
report, SND_JACK_MICROPHONE);
return;
case 1: /* 2 port */
val = snd_soc_read(rt5645->codec, RT5645_A_JD_CTRL1) & 0x0070;
break;
default: /* 1 port */
val = snd_soc_read(rt5645->codec, RT5645_A_JD_CTRL1) & 0x0020;
default: /* read rt5645 jd1_1 status */
val = snd_soc_read(rt5645->codec, RT5645_INT_IRQ_ST) & 0x1000;
break;
}
switch (val) {
/* jack in */
case 0x30: /* 2 port */
case 0x0: /* 1 port or 2 port */
if (rt5645->jack_type == 0) {
report = rt5645_jack_detect(rt5645->codec, 1);
/* for push button and jack out */
break;
}
if (!val && (rt5645->jack_type == 0)) { /* jack in */
report = rt5645_jack_detect(rt5645->codec, 1);
} else if (!val && rt5645->jack_type != 0) {
/* for push button and jack out */
btn_type = 0;
if (snd_soc_read(rt5645->codec, RT5645_INT_IRQ_ST) & 0x4) {
/* button pressed */
@ -3302,19 +3305,12 @@ static void rt5645_jack_detect_work(struct work_struct *work)
mod_timer(&rt5645->btn_check_timer,
msecs_to_jiffies(100));
}
break;
/* jack out */
case 0x70: /* 2 port */
case 0x10: /* 2 port */
case 0x20: /* 1 port */
} else {
/* jack out */
report = 0;
snd_soc_update_bits(rt5645->codec,
RT5645_INT_IRQ_ST, 0x1, 0x0);
rt5645_jack_detect(rt5645->codec, 0);
break;
default:
break;
}
snd_soc_jack_report(rt5645->hp_jack, report, SND_JACK_HEADPHONE);
@ -3601,7 +3597,7 @@ static struct rt5645_platform_data buddy_platform_data = {
.dmic1_data_pin = RT5645_DMIC_DATA_GPIO5,
.dmic2_data_pin = RT5645_DMIC_DATA_IN2P,
.jd_mode = 3,
.jd_invert = true,
.level_trigger_irq = true,
};
static struct dmi_system_id dmi_platform_intel_broadwell[] = {
@ -3614,6 +3610,33 @@ static struct dmi_system_id dmi_platform_intel_broadwell[] = {
{ }
};
static struct rt5645_platform_data gpd_win_platform_data = {
.jd_mode = 3,
.inv_jd1_1 = true,
};
static const struct dmi_system_id dmi_platform_gpd_win[] = {
{
/*
* Match for the GPDwin which unfortunately uses somewhat
* generic dmi strings, which is why we test for 4 strings.
* Comparing against 23 other byt/cht boards, board_vendor
* and board_name are unique to the GPDwin, where as only one
* other board has the same board_serial and 3 others have
* the same default product_name. Also the GPDwin is the
* only device to have both board_ and product_name not set.
*/
.ident = "GPD Win",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
DMI_MATCH(DMI_BOARD_NAME, "Default string"),
DMI_MATCH(DMI_BOARD_SERIAL, "Default string"),
DMI_MATCH(DMI_PRODUCT_NAME, "Default string"),
},
},
{}
};
static bool rt5645_check_dp(struct device *dev)
{
if (device_property_present(dev, "realtek,in2-differential") ||
@ -3664,6 +3687,17 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
rt5645_parse_dt(rt5645, &i2c->dev);
else if (dmi_check_system(dmi_platform_intel_braswell))
rt5645->pdata = general_platform_data;
else if (dmi_check_system(dmi_platform_gpd_win))
rt5645->pdata = gpd_win_platform_data;
if (quirk != -1) {
rt5645->pdata.in2_diff = QUIRK_IN2_DIFF(quirk);
rt5645->pdata.level_trigger_irq = QUIRK_LEVEL_IRQ(quirk);
rt5645->pdata.inv_jd1_1 = QUIRK_INV_JD1_1(quirk);
rt5645->pdata.jd_mode = QUIRK_JD_MODE(quirk);
rt5645->pdata.dmic1_data_pin = QUIRK_DMIC1_DATA_PIN(quirk);
rt5645->pdata.dmic2_data_pin = QUIRK_DMIC2_DATA_PIN(quirk);
}
rt5645->gpiod_hp_det = devm_gpiod_get_optional(&i2c->dev, "hp-detect",
GPIOD_IN);
@ -3745,6 +3779,8 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
ret);
}
regmap_update_bits(rt5645->regmap, RT5645_CLSD_OUT_CTRL, 0xc0, 0xc0);
if (rt5645->pdata.in2_diff)
regmap_update_bits(rt5645->regmap, RT5645_IN2_CTRL,
RT5645_IN_DF2, RT5645_IN_DF2);
@ -3848,12 +3884,16 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
default:
break;
}
if (rt5645->pdata.inv_jd1_1) {
regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV);
}
}
regmap_update_bits(rt5645->regmap, RT5645_ADDA_CLK1,
RT5645_I2S_PD1_MASK, RT5645_I2S_PD1_2);
if (rt5645->pdata.jd_invert) {
if (rt5645->pdata.level_trigger_irq) {
regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV);
}

View File

@ -586,44 +586,6 @@ static const struct snd_kcontrol_new hpo_r_mute_control =
SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5651_HP_VOL,
RT5651_R_MUTE_SFT, 1, 1);
/* INL/R source */
static const char * const rt5651_inl_src[] = {"IN2P", "HPOVOLLP"};
static SOC_ENUM_SINGLE_DECL(
rt5651_inl_enum, RT5651_INL1_INR1_VOL,
RT5651_INL_SEL_SFT, rt5651_inl_src);
static const struct snd_kcontrol_new rt5651_inl1_mux =
SOC_DAPM_ENUM("INL1 source", rt5651_inl_enum);
static const char * const rt5651_inr1_src[] = {"IN2N", "HPOVOLRP"};
static SOC_ENUM_SINGLE_DECL(
rt5651_inr1_enum, RT5651_INL1_INR1_VOL,
RT5651_INR_SEL_SFT, rt5651_inr1_src);
static const struct snd_kcontrol_new rt5651_inr1_mux =
SOC_DAPM_ENUM("INR1 source", rt5651_inr1_enum);
static const char * const rt5651_inl2_src[] = {"IN3P", "OUTVOLLP"};
static SOC_ENUM_SINGLE_DECL(
rt5651_inl2_enum, RT5651_INL2_INR2_VOL,
RT5651_INL_SEL_SFT, rt5651_inl2_src);
static const struct snd_kcontrol_new rt5651_inl2_mux =
SOC_DAPM_ENUM("INL2 source", rt5651_inl2_enum);
static const char * const rt5651_inr2_src[] = {"IN3N", "OUTVOLRP"};
static SOC_ENUM_SINGLE_DECL(
rt5651_inr2_enum, RT5651_INL2_INR2_VOL,
RT5651_INR_SEL_SFT, rt5651_inr2_src);
static const struct snd_kcontrol_new rt5651_inr2_mux =
SOC_DAPM_ENUM("INR2 source", rt5651_inr2_enum);
/* Stereo ADC source */
static const char * const rt5651_stereo1_adc1_src[] = {"DD MIX", "ADC"};
@ -955,11 +917,7 @@ static const struct snd_soc_dapm_widget rt5651_dapm_widgets[] = {
RT5651_PWR_IN2_L_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("INR2 VOL", RT5651_PWR_VOL,
RT5651_PWR_IN2_R_BIT, 0, NULL, 0),
/* IN Mux */
SND_SOC_DAPM_MUX("INL1 Mux", SND_SOC_NOPM, 0, 0, &rt5651_inl1_mux),
SND_SOC_DAPM_MUX("INR1 Mux", SND_SOC_NOPM, 0, 0, &rt5651_inr1_mux),
SND_SOC_DAPM_MUX("INL2 Mux", SND_SOC_NOPM, 0, 0, &rt5651_inl2_mux),
SND_SOC_DAPM_MUX("INR2 Mux", SND_SOC_NOPM, 0, 0, &rt5651_inr2_mux),
/* REC Mixer */
SND_SOC_DAPM_MIXER("RECMIXL", RT5651_PWR_MIXER, RT5651_PWR_RM_L_BIT, 0,
rt5651_rec_l_mix, ARRAY_SIZE(rt5651_rec_l_mix)),

View File

@ -70,6 +70,7 @@ struct rt5665_priv {
int jack_type;
int irq_work_delay_time;
unsigned int sar_adc_value;
bool calibration_done;
};
static const struct reg_default rt5665_reg[] = {
@ -912,46 +913,46 @@ static const char * const rt5665_data_select[] = {
"L/R", "R/L", "L/L", "R/R"
};
static const SOC_ENUM_SINGLE_DECL(rt5665_if1_1_01_adc_enum,
static SOC_ENUM_SINGLE_DECL(rt5665_if1_1_01_adc_enum,
RT5665_TDM_CTRL_2, RT5665_I2S1_1_DS_ADC_SLOT01_SFT, rt5665_data_select);
static const SOC_ENUM_SINGLE_DECL(rt5665_if1_1_23_adc_enum,
static SOC_ENUM_SINGLE_DECL(rt5665_if1_1_23_adc_enum,
RT5665_TDM_CTRL_2, RT5665_I2S1_1_DS_ADC_SLOT23_SFT, rt5665_data_select);
static const SOC_ENUM_SINGLE_DECL(rt5665_if1_1_45_adc_enum,
static SOC_ENUM_SINGLE_DECL(rt5665_if1_1_45_adc_enum,
RT5665_TDM_CTRL_2, RT5665_I2S1_1_DS_ADC_SLOT45_SFT, rt5665_data_select);
static const SOC_ENUM_SINGLE_DECL(rt5665_if1_1_67_adc_enum,
static SOC_ENUM_SINGLE_DECL(rt5665_if1_1_67_adc_enum,
RT5665_TDM_CTRL_2, RT5665_I2S1_1_DS_ADC_SLOT67_SFT, rt5665_data_select);
static const SOC_ENUM_SINGLE_DECL(rt5665_if1_2_01_adc_enum,
static SOC_ENUM_SINGLE_DECL(rt5665_if1_2_01_adc_enum,
RT5665_TDM_CTRL_2, RT5665_I2S1_2_DS_ADC_SLOT01_SFT, rt5665_data_select);
static const SOC_ENUM_SINGLE_DECL(rt5665_if1_2_23_adc_enum,
static SOC_ENUM_SINGLE_DECL(rt5665_if1_2_23_adc_enum,
RT5665_TDM_CTRL_2, RT5665_I2S1_2_DS_ADC_SLOT23_SFT, rt5665_data_select);
static const SOC_ENUM_SINGLE_DECL(rt5665_if1_2_45_adc_enum,
static SOC_ENUM_SINGLE_DECL(rt5665_if1_2_45_adc_enum,
RT5665_TDM_CTRL_2, RT5665_I2S1_2_DS_ADC_SLOT45_SFT, rt5665_data_select);
static const SOC_ENUM_SINGLE_DECL(rt5665_if1_2_67_adc_enum,
static SOC_ENUM_SINGLE_DECL(rt5665_if1_2_67_adc_enum,
RT5665_TDM_CTRL_2, RT5665_I2S1_2_DS_ADC_SLOT67_SFT, rt5665_data_select);
static const SOC_ENUM_SINGLE_DECL(rt5665_if2_1_dac_enum,
static SOC_ENUM_SINGLE_DECL(rt5665_if2_1_dac_enum,
RT5665_DIG_INF2_DATA, RT5665_IF2_1_DAC_SEL_SFT, rt5665_data_select);
static const SOC_ENUM_SINGLE_DECL(rt5665_if2_1_adc_enum,
static SOC_ENUM_SINGLE_DECL(rt5665_if2_1_adc_enum,
RT5665_DIG_INF2_DATA, RT5665_IF2_1_ADC_SEL_SFT, rt5665_data_select);
static const SOC_ENUM_SINGLE_DECL(rt5665_if2_2_dac_enum,
static SOC_ENUM_SINGLE_DECL(rt5665_if2_2_dac_enum,
RT5665_DIG_INF2_DATA, RT5665_IF2_2_DAC_SEL_SFT, rt5665_data_select);
static const SOC_ENUM_SINGLE_DECL(rt5665_if2_2_adc_enum,
static SOC_ENUM_SINGLE_DECL(rt5665_if2_2_adc_enum,
RT5665_DIG_INF2_DATA, RT5665_IF2_2_ADC_SEL_SFT, rt5665_data_select);
static const SOC_ENUM_SINGLE_DECL(rt5665_if3_dac_enum,
static SOC_ENUM_SINGLE_DECL(rt5665_if3_dac_enum,
RT5665_DIG_INF3_DATA, RT5665_IF3_DAC_SEL_SFT, rt5665_data_select);
static const SOC_ENUM_SINGLE_DECL(rt5665_if3_adc_enum,
static SOC_ENUM_SINGLE_DECL(rt5665_if3_adc_enum,
RT5665_DIG_INF3_DATA, RT5665_IF3_ADC_SEL_SFT, rt5665_data_select);
static const struct snd_kcontrol_new rt5665_if1_1_01_adc_swap_mux =
@ -1305,6 +1306,11 @@ static void rt5665_jack_detect_handler(struct work_struct *work)
usleep_range(10000, 15000);
}
while (!rt5665->calibration_done) {
pr_debug("%s calibration not ready\n", __func__);
usleep_range(10000, 15000);
}
mutex_lock(&rt5665->calibrate_mutex);
val = snd_soc_read(rt5665->codec, RT5665_AJD1_CTRL) & 0x0010;
@ -1819,14 +1825,14 @@ static const char * const rt5665_dac2_src[] = {
"IF1 DAC2", "IF2_1 DAC", "IF2_2 DAC", "IF3 DAC", "Mono ADC MIX"
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_dac_l2_enum, RT5665_DAC2_CTRL,
RT5665_DAC_L2_SEL_SFT, rt5665_dac2_src);
static const struct snd_kcontrol_new rt5665_dac_l2_mux =
SOC_DAPM_ENUM("Digital DAC L2 Source", rt5665_dac_l2_enum);
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_dac_r2_enum, RT5665_DAC2_CTRL,
RT5665_DAC_R2_SEL_SFT, rt5665_dac2_src);
@ -1839,14 +1845,14 @@ static const char * const rt5665_dac3_src[] = {
"IF1 DAC2", "IF2_1 DAC", "IF2_2 DAC", "IF3 DAC", "STO2 ADC MIX"
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_dac_l3_enum, RT5665_DAC3_CTRL,
RT5665_DAC_L3_SEL_SFT, rt5665_dac3_src);
static const struct snd_kcontrol_new rt5665_dac_l3_mux =
SOC_DAPM_ENUM("Digital DAC L3 Source", rt5665_dac_l3_enum);
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_dac_r3_enum, RT5665_DAC3_CTRL,
RT5665_DAC_R3_SEL_SFT, rt5665_dac3_src);
@ -1859,14 +1865,14 @@ static const char * const rt5665_sto1_adc1_src[] = {
"DD Mux", "ADC"
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_sto1_adc1l_enum, RT5665_STO1_ADC_MIXER,
RT5665_STO1_ADC1L_SRC_SFT, rt5665_sto1_adc1_src);
static const struct snd_kcontrol_new rt5665_sto1_adc1l_mux =
SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5665_sto1_adc1l_enum);
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_sto1_adc1r_enum, RT5665_STO1_ADC_MIXER,
RT5665_STO1_ADC1R_SRC_SFT, rt5665_sto1_adc1_src);
@ -1879,14 +1885,14 @@ static const char * const rt5665_sto1_adc_src[] = {
"ADC1 L", "ADC1 R", "ADC2 L", "ADC2 R"
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_sto1_adcl_enum, RT5665_STO1_ADC_MIXER,
RT5665_STO1_ADCL_SRC_SFT, rt5665_sto1_adc_src);
static const struct snd_kcontrol_new rt5665_sto1_adcl_mux =
SOC_DAPM_ENUM("Stereo1 ADCL Source", rt5665_sto1_adcl_enum);
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_sto1_adcr_enum, RT5665_STO1_ADC_MIXER,
RT5665_STO1_ADCR_SRC_SFT, rt5665_sto1_adc_src);
@ -1899,14 +1905,14 @@ static const char * const rt5665_sto1_adc2_src[] = {
"DAC MIX", "DMIC"
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_sto1_adc2l_enum, RT5665_STO1_ADC_MIXER,
RT5665_STO1_ADC2L_SRC_SFT, rt5665_sto1_adc2_src);
static const struct snd_kcontrol_new rt5665_sto1_adc2l_mux =
SOC_DAPM_ENUM("Stereo1 ADC2L Source", rt5665_sto1_adc2l_enum);
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_sto1_adc2r_enum, RT5665_STO1_ADC_MIXER,
RT5665_STO1_ADC2R_SRC_SFT, rt5665_sto1_adc2_src);
@ -1919,7 +1925,7 @@ static const char * const rt5665_sto1_dmic_src[] = {
"DMIC1", "DMIC2"
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_sto1_dmic_enum, RT5665_STO1_ADC_MIXER,
RT5665_STO1_DMIC_SRC_SFT, rt5665_sto1_dmic_src);
@ -1931,7 +1937,7 @@ static const char * const rt5665_sto1_dd_l_src[] = {
"STO2 DAC", "MONO DAC"
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_sto1_dd_l_enum, RT5665_STO1_ADC_MIXER,
RT5665_STO1_DD_L_SRC_SFT, rt5665_sto1_dd_l_src);
@ -1943,7 +1949,7 @@ static const char * const rt5665_sto1_dd_r_src[] = {
"STO2 DAC", "MONO DAC", "AEC REF"
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_sto1_dd_r_enum, RT5665_STO1_ADC_MIXER,
RT5665_STO1_DD_R_SRC_SFT, rt5665_sto1_dd_r_src);
@ -1956,7 +1962,7 @@ static const char * const rt5665_mono_adc_l2_src[] = {
"DAC MIXL", "DMIC"
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_mono_adc_l2_enum, RT5665_MONO_ADC_MIXER,
RT5665_MONO_ADC_L2_SRC_SFT, rt5665_mono_adc_l2_src);
@ -1970,7 +1976,7 @@ static const char * const rt5665_mono_adc_l1_src[] = {
"DD Mux", "ADC"
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_mono_adc_l1_enum, RT5665_MONO_ADC_MIXER,
RT5665_MONO_ADC_L1_SRC_SFT, rt5665_mono_adc_l1_src);
@ -1982,14 +1988,14 @@ static const char * const rt5665_mono_dd_src[] = {
"STO2 DAC", "MONO DAC"
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_mono_dd_l_enum, RT5665_MONO_ADC_MIXER,
RT5665_MONO_DD_L_SRC_SFT, rt5665_mono_dd_src);
static const struct snd_kcontrol_new rt5665_mono_dd_l_mux =
SOC_DAPM_ENUM("Mono DD L Source", rt5665_mono_dd_l_enum);
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_mono_dd_r_enum, RT5665_MONO_ADC_MIXER,
RT5665_MONO_DD_R_SRC_SFT, rt5665_mono_dd_src);
@ -2002,14 +2008,14 @@ static const char * const rt5665_mono_adc_src[] = {
"ADC1 L", "ADC1 R", "ADC2 L", "ADC2 R"
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_mono_adc_l_enum, RT5665_MONO_ADC_MIXER,
RT5665_MONO_ADC_L_SRC_SFT, rt5665_mono_adc_src);
static const struct snd_kcontrol_new rt5665_mono_adc_l_mux =
SOC_DAPM_ENUM("Mono ADC L Source", rt5665_mono_adc_l_enum);
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_mono_adcr_enum, RT5665_MONO_ADC_MIXER,
RT5665_MONO_ADC_R_SRC_SFT, rt5665_mono_adc_src);
@ -2022,7 +2028,7 @@ static const char * const rt5665_mono_dmic_l_src[] = {
"DMIC1 L", "DMIC2 L"
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_mono_dmic_l_enum, RT5665_MONO_ADC_MIXER,
RT5665_MONO_DMIC_L_SRC_SFT, rt5665_mono_dmic_l_src);
@ -2035,7 +2041,7 @@ static const char * const rt5665_mono_adc_r2_src[] = {
"DAC MIXR", "DMIC"
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_mono_adc_r2_enum, RT5665_MONO_ADC_MIXER,
RT5665_MONO_ADC_R2_SRC_SFT, rt5665_mono_adc_r2_src);
@ -2048,7 +2054,7 @@ static const char * const rt5665_mono_adc_r1_src[] = {
"DD Mux", "ADC"
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_mono_adc_r1_enum, RT5665_MONO_ADC_MIXER,
RT5665_MONO_ADC_R1_SRC_SFT, rt5665_mono_adc_r1_src);
@ -2061,7 +2067,7 @@ static const char * const rt5665_mono_dmic_r_src[] = {
"DMIC1 R", "DMIC2 R"
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_mono_dmic_r_enum, RT5665_MONO_ADC_MIXER,
RT5665_MONO_DMIC_R_SRC_SFT, rt5665_mono_dmic_r_src);
@ -2075,14 +2081,14 @@ static const char * const rt5665_sto2_adc1_src[] = {
"DD Mux", "ADC"
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_sto2_adc1l_enum, RT5665_STO2_ADC_MIXER,
RT5665_STO2_ADC1L_SRC_SFT, rt5665_sto2_adc1_src);
static const struct snd_kcontrol_new rt5665_sto2_adc1l_mux =
SOC_DAPM_ENUM("Stereo2 ADC1L Source", rt5665_sto2_adc1l_enum);
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_sto2_adc1r_enum, RT5665_STO2_ADC_MIXER,
RT5665_STO2_ADC1R_SRC_SFT, rt5665_sto2_adc1_src);
@ -2095,14 +2101,14 @@ static const char * const rt5665_sto2_adc_src[] = {
"ADC1 L", "ADC1 R", "ADC2 L"
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_sto2_adcl_enum, RT5665_STO2_ADC_MIXER,
RT5665_STO2_ADCL_SRC_SFT, rt5665_sto2_adc_src);
static const struct snd_kcontrol_new rt5665_sto2_adcl_mux =
SOC_DAPM_ENUM("Stereo2 ADCL Source", rt5665_sto2_adcl_enum);
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_sto2_adcr_enum, RT5665_STO2_ADC_MIXER,
RT5665_STO2_ADCR_SRC_SFT, rt5665_sto2_adc_src);
@ -2115,14 +2121,14 @@ static const char * const rt5665_sto2_adc2_src[] = {
"DAC MIX", "DMIC"
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_sto2_adc2l_enum, RT5665_STO2_ADC_MIXER,
RT5665_STO2_ADC2L_SRC_SFT, rt5665_sto2_adc2_src);
static const struct snd_kcontrol_new rt5665_sto2_adc2l_mux =
SOC_DAPM_ENUM("Stereo2 ADC2L Source", rt5665_sto2_adc2l_enum);
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_sto2_adc2r_enum, RT5665_STO2_ADC_MIXER,
RT5665_STO2_ADC2R_SRC_SFT, rt5665_sto2_adc2_src);
@ -2135,7 +2141,7 @@ static const char * const rt5665_sto2_dmic_src[] = {
"DMIC1", "DMIC2"
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_sto2_dmic_enum, RT5665_STO2_ADC_MIXER,
RT5665_STO2_DMIC_SRC_SFT, rt5665_sto2_dmic_src);
@ -2147,7 +2153,7 @@ static const char * const rt5665_sto2_dd_l_src[] = {
"STO2 DAC", "MONO DAC"
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_sto2_dd_l_enum, RT5665_STO2_ADC_MIXER,
RT5665_STO2_DD_L_SRC_SFT, rt5665_sto2_dd_l_src);
@ -2159,7 +2165,7 @@ static const char * const rt5665_sto2_dd_r_src[] = {
"STO2 DAC", "MONO DAC"
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_sto2_dd_r_enum, RT5665_STO2_ADC_MIXER,
RT5665_STO2_DD_R_SRC_SFT, rt5665_sto2_dd_r_src);
@ -2172,14 +2178,14 @@ static const char * const rt5665_dac1_src[] = {
"IF1 DAC1", "IF2_1 DAC", "IF2_2 DAC", "IF3 DAC"
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_dac_r1_enum, RT5665_AD_DA_MIXER,
RT5665_DAC1_R_SEL_SFT, rt5665_dac1_src);
static const struct snd_kcontrol_new rt5665_dac_r1_mux =
SOC_DAPM_ENUM("DAC R1 Source", rt5665_dac_r1_enum);
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_dac_l1_enum, RT5665_AD_DA_MIXER,
RT5665_DAC1_L_SEL_SFT, rt5665_dac1_src);
@ -2192,14 +2198,14 @@ static const char * const rt5665_dig_dac_mix_src[] = {
"Stereo1 DAC Mixer", "Stereo2 DAC Mixer", "Mono DAC Mixer"
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_dig_dac_mixl_enum, RT5665_A_DAC1_MUX,
RT5665_DAC_MIX_L_SFT, rt5665_dig_dac_mix_src);
static const struct snd_kcontrol_new rt5665_dig_dac_mixl_mux =
SOC_DAPM_ENUM("DAC Digital Mixer L Source", rt5665_dig_dac_mixl_enum);
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_dig_dac_mixr_enum, RT5665_A_DAC1_MUX,
RT5665_DAC_MIX_R_SFT, rt5665_dig_dac_mix_src);
@ -2212,14 +2218,14 @@ static const char * const rt5665_alg_dac1_src[] = {
"Stereo1 DAC Mixer", "DAC1", "DMIC1"
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_alg_dac_l1_enum, RT5665_A_DAC1_MUX,
RT5665_A_DACL1_SFT, rt5665_alg_dac1_src);
static const struct snd_kcontrol_new rt5665_alg_dac_l1_mux =
SOC_DAPM_ENUM("Analog DAC L1 Source", rt5665_alg_dac_l1_enum);
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_alg_dac_r1_enum, RT5665_A_DAC1_MUX,
RT5665_A_DACR1_SFT, rt5665_alg_dac1_src);
@ -2232,14 +2238,14 @@ static const char * const rt5665_alg_dac2_src[] = {
"Mono DAC Mixer", "DAC2"
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_alg_dac_l2_enum, RT5665_A_DAC2_MUX,
RT5665_A_DACL2_SFT, rt5665_alg_dac2_src);
static const struct snd_kcontrol_new rt5665_alg_dac_l2_mux =
SOC_DAPM_ENUM("Analog DAC L2 Source", rt5665_alg_dac_l2_enum);
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_alg_dac_r2_enum, RT5665_A_DAC2_MUX,
RT5665_A_DACR2_SFT, rt5665_alg_dac2_src);
@ -2253,7 +2259,7 @@ static const char * const rt5665_if2_1_adc_in_src[] = {
"IF1 DAC2", "IF2_2 DAC", "IF3 DAC", "DAC1 MIX"
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_if2_1_adc_in_enum, RT5665_DIG_INF2_DATA,
RT5665_IF2_1_ADC_IN_SFT, rt5665_if2_1_adc_in_src);
@ -2266,7 +2272,7 @@ static const char * const rt5665_if2_2_adc_in_src[] = {
"IF1 DAC2", "IF2_1 DAC", "IF3 DAC", "DAC1 MIX"
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_if2_2_adc_in_enum, RT5665_DIG_INF2_DATA,
RT5665_IF2_2_ADC_IN_SFT, rt5665_if2_2_adc_in_src);
@ -2280,7 +2286,7 @@ static const char * const rt5665_if3_adc_in_src[] = {
"IF1 DAC2", "IF2_1 DAC", "IF2_2 DAC", "DAC1 MIX"
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_if3_adc_in_enum, RT5665_DIG_INF3_DATA,
RT5665_IF3_ADC_IN_SFT, rt5665_if3_adc_in_src);
@ -2293,14 +2299,14 @@ static const char * const rt5665_pdm_src[] = {
"Stereo1 DAC", "Stereo2 DAC", "Mono DAC"
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_pdm_l_enum, RT5665_PDM_OUT_CTRL,
RT5665_PDM1_L_SFT, rt5665_pdm_src);
static const struct snd_kcontrol_new rt5665_pdm_l_mux =
SOC_DAPM_ENUM("PDM L Source", rt5665_pdm_l_enum);
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_pdm_r_enum, RT5665_PDM_OUT_CTRL,
RT5665_PDM1_R_SFT, rt5665_pdm_src);
@ -2314,7 +2320,7 @@ static const char * const rt5665_if1_1_adc1_data_src[] = {
"STO1 ADC", "IF2_1 DAC",
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_if1_1_adc1_data_enum, RT5665_TDM_CTRL_3,
RT5665_IF1_ADC1_SEL_SFT, rt5665_if1_1_adc1_data_src);
@ -2326,7 +2332,7 @@ static const char * const rt5665_if1_1_adc2_data_src[] = {
"STO2 ADC", "IF2_2 DAC",
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_if1_1_adc2_data_enum, RT5665_TDM_CTRL_3,
RT5665_IF1_ADC2_SEL_SFT, rt5665_if1_1_adc2_data_src);
@ -2338,7 +2344,7 @@ static const char * const rt5665_if1_1_adc3_data_src[] = {
"MONO ADC", "IF3 DAC",
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_if1_1_adc3_data_enum, RT5665_TDM_CTRL_3,
RT5665_IF1_ADC3_SEL_SFT, rt5665_if1_1_adc3_data_src);
@ -2350,7 +2356,7 @@ static const char * const rt5665_if1_2_adc1_data_src[] = {
"STO1 ADC", "IF1 DAC",
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_if1_2_adc1_data_enum, RT5665_TDM_CTRL_4,
RT5665_IF1_ADC1_SEL_SFT, rt5665_if1_2_adc1_data_src);
@ -2362,7 +2368,7 @@ static const char * const rt5665_if1_2_adc2_data_src[] = {
"STO2 ADC", "IF2_1 DAC",
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_if1_2_adc2_data_enum, RT5665_TDM_CTRL_4,
RT5665_IF1_ADC2_SEL_SFT, rt5665_if1_2_adc2_data_src);
@ -2374,7 +2380,7 @@ static const char * const rt5665_if1_2_adc3_data_src[] = {
"MONO ADC", "IF2_2 DAC",
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_if1_2_adc3_data_enum, RT5665_TDM_CTRL_4,
RT5665_IF1_ADC3_SEL_SFT, rt5665_if1_2_adc3_data_src);
@ -2386,7 +2392,7 @@ static const char * const rt5665_if1_2_adc4_data_src[] = {
"DAC1", "IF3 DAC",
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_if1_2_adc4_data_enum, RT5665_TDM_CTRL_4,
RT5665_IF1_ADC4_SEL_SFT, rt5665_if1_2_adc4_data_src);
@ -2401,14 +2407,14 @@ static const char * const rt5665_tdm_adc_data_src[] = {
"4123", "4132", "4213", "4231", "4312", "4321"
};
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_tdm1_adc_data_enum, RT5665_TDM_CTRL_3,
RT5665_TDM_ADC_SEL_SFT, rt5665_tdm_adc_data_src);
static const struct snd_kcontrol_new rt5665_tdm1_adc_mux =
SOC_DAPM_ENUM("TDM1 ADC Mux", rt5665_tdm1_adc_data_enum);
static const SOC_ENUM_SINGLE_DECL(
static SOC_ENUM_SINGLE_DECL(
rt5665_tdm2_adc_data_enum, RT5665_TDM_CTRL_4,
RT5665_TDM_ADC_SEL_SFT, rt5665_tdm_adc_data_src);
@ -2607,7 +2613,7 @@ static int rt5665_i2s_pin_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
unsigned int val1, val2, mask1, mask2 = 0;
unsigned int val1, val2, mask1 = 0, mask2 = 0;
switch (w->shift) {
case RT5665_PWR_I2S2_1_BIT:
@ -2635,13 +2641,17 @@ static int rt5665_i2s_pin_event(struct snd_soc_dapm_widget *w,
}
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
snd_soc_update_bits(codec, RT5665_GPIO_CTRL_1, mask1, val1);
if (mask1)
snd_soc_update_bits(codec, RT5665_GPIO_CTRL_1,
mask1, val1);
if (mask2)
snd_soc_update_bits(codec, RT5665_GPIO_CTRL_2,
mask2, val2);
break;
case SND_SOC_DAPM_POST_PMD:
snd_soc_update_bits(codec, RT5665_GPIO_CTRL_1, mask1, 0);
if (mask1)
snd_soc_update_bits(codec, RT5665_GPIO_CTRL_1,
mask1, 0);
if (mask2)
snd_soc_update_bits(codec, RT5665_GPIO_CTRL_2,
mask2, 0);
@ -2684,6 +2694,8 @@ static const struct snd_soc_dapm_widget rt5665_dapm_widgets[] = {
RT5665_DAC_MONO_R_ASRC_SFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5665_ASRC_1,
RT5665_ADC_STO1_ASRC_SFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("ADC STO2 ASRC", 1, RT5665_ASRC_1,
RT5665_ADC_STO2_ASRC_SFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("ADC Mono L ASRC", 1, RT5665_ASRC_1,
RT5665_ADC_MONO_L_ASRC_SFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("ADC Mono R ASRC", 1, RT5665_ASRC_1,
@ -3227,6 +3239,7 @@ static const struct snd_soc_dapm_route rt5665_dapm_routes[] = {
/*ASRC*/
{"ADC Stereo1 Filter", NULL, "ADC STO1 ASRC", is_using_asrc},
{"ADC Stereo2 Filter", NULL, "ADC STO2 ASRC", is_using_asrc},
{"ADC Mono Left Filter", NULL, "ADC Mono L ASRC", is_using_asrc},
{"ADC Mono Right Filter", NULL, "ADC Mono R ASRC", is_using_asrc},
{"DAC Mono Left Filter", NULL, "DAC Mono L ASRC", is_using_asrc},
@ -4688,6 +4701,7 @@ static void rt5665_calibrate(struct rt5665_priv *rt5665)
regmap_write(rt5665->regmap, RT5665_ASRC_8, 0x0120);
out_unlock:
rt5665->calibration_done = true;
mutex_unlock(&rt5665->calibrate_mutex);
}
@ -4922,7 +4936,7 @@ static struct acpi_device_id rt5665_acpi_match[] = {
MODULE_DEVICE_TABLE(acpi, rt5665_acpi_match);
#endif
struct i2c_driver rt5665_i2c_driver = {
static struct i2c_driver rt5665_i2c_driver = {
.driver = {
.name = "rt5665",
.of_match_table = of_match_ptr(rt5665_of_match),

View File

@ -2022,7 +2022,6 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = {
{ "Stereo1 ADC MIXL", NULL, "Sto1 ADC MIXL" },
{ "Stereo1 ADC MIXL", NULL, "ADC Stereo1 Filter" },
{ "ADC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll },
{ "Stereo1 ADC MIXR", NULL, "Sto1 ADC MIXR" },
{ "Stereo1 ADC MIXR", NULL, "ADC Stereo1 Filter" },
@ -2061,7 +2060,6 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = {
{ "Stereo2 ADC MIXL", NULL, "Stereo2 ADC LR Mux" },
{ "Stereo2 ADC MIXL", NULL, "ADC Stereo2 Filter" },
{ "ADC Stereo2 Filter", NULL, "PLL1", is_sys_clk_from_pll },
{ "Stereo2 ADC MIXR", NULL, "Sto2 ADC MIXR" },
{ "Stereo2 ADC MIXR", NULL, "ADC Stereo2 Filter" },
@ -2444,10 +2442,9 @@ static int rt5670_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return 0;
}
static int rt5670_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
static int rt5670_set_codec_sysclk(struct snd_soc_codec *codec, int clk_id,
int source, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = dai->codec;
struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
unsigned int reg_val = 0;
@ -2471,7 +2468,7 @@ static int rt5670_set_dai_sysclk(struct snd_soc_dai *dai,
if (clk_id != RT5670_SCLK_S_RCCLK)
rt5670->sysclk_src = clk_id;
dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
dev_dbg(codec->dev, "Sysclk : %dHz clock id : %d\n", freq, clk_id);
return 0;
}
@ -2723,7 +2720,6 @@ static int rt5670_resume(struct snd_soc_codec *codec)
static const struct snd_soc_dai_ops rt5670_aif_dai_ops = {
.hw_params = rt5670_hw_params,
.set_fmt = rt5670_set_dai_fmt,
.set_sysclk = rt5670_set_dai_sysclk,
.set_tdm_slot = rt5670_set_tdm_slot,
.set_pll = rt5670_set_dai_pll,
};
@ -2776,6 +2772,7 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5670 = {
.resume = rt5670_resume,
.set_bias_level = rt5670_set_bias_level,
.idle_bias_off = true,
.set_sysclk = rt5670_set_codec_sysclk,
.component_driver = {
.controls = rt5670_snd_controls,
.num_controls = ARRAY_SIZE(rt5670_snd_controls),

View File

@ -74,6 +74,20 @@ static const struct reg_default sgtl5000_reg_defaults[] = {
{ SGTL5000_DAP_AVC_DECAY, 0x0050 },
};
/* AVC: Threshold dB -> register: pre-calculated values */
static const u16 avc_thr_db2reg[97] = {
0x5168, 0x488E, 0x40AA, 0x39A1, 0x335D, 0x2DC7, 0x28CC, 0x245D, 0x2068,
0x1CE2, 0x19BE, 0x16F1, 0x1472, 0x1239, 0x103E, 0x0E7A, 0x0CE6, 0x0B7F,
0x0A3F, 0x0922, 0x0824, 0x0741, 0x0677, 0x05C3, 0x0522, 0x0493, 0x0414,
0x03A2, 0x033D, 0x02E3, 0x0293, 0x024B, 0x020B, 0x01D2, 0x019F, 0x0172,
0x014A, 0x0126, 0x0106, 0x00E9, 0x00D0, 0x00B9, 0x00A5, 0x0093, 0x0083,
0x0075, 0x0068, 0x005D, 0x0052, 0x0049, 0x0041, 0x003A, 0x0034, 0x002E,
0x0029, 0x0025, 0x0021, 0x001D, 0x001A, 0x0017, 0x0014, 0x0012, 0x0010,
0x000E, 0x000D, 0x000B, 0x000A, 0x0009, 0x0008, 0x0007, 0x0006, 0x0005,
0x0005, 0x0004, 0x0004, 0x0003, 0x0003, 0x0002, 0x0002, 0x0002, 0x0002,
0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000};
/* regulator supplies for sgtl5000, VDDD is an optional external supply */
enum sgtl5000_regulator_supplies {
VDDA,
@ -382,6 +396,65 @@ static int dac_put_volsw(struct snd_kcontrol *kcontrol,
return 0;
}
/*
* custom function to get AVC threshold
*
* The threshold dB is calculated by rearranging the calculation from the
* avc_put_threshold function: register_value = 10^(dB/20) * 0.636 * 2^15 ==>
* dB = ( fls(register_value) - 14.347 ) * 6.02
*
* As this calculation is expensive and the threshold dB values may not exeed
* 0 to 96 we use pre-calculated values.
*/
static int avc_get_threshold(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
int db, i;
u16 reg = snd_soc_read(codec, SGTL5000_DAP_AVC_THRESHOLD);
/* register value 0 => -96dB */
if (!reg) {
ucontrol->value.integer.value[0] = 96;
ucontrol->value.integer.value[1] = 96;
return 0;
}
/* get dB from register value (rounded down) */
for (i = 0; avc_thr_db2reg[i] > reg; i++)
;
db = i;
ucontrol->value.integer.value[0] = db;
ucontrol->value.integer.value[1] = db;
return 0;
}
/*
* custom function to put AVC threshold
*
* The register value is calculated by following formula:
* register_value = 10^(dB/20) * 0.636 * 2^15
* As this calculation is expensive and the threshold dB values may not exeed
* 0 to 96 we use pre-calculated values.
*/
static int avc_put_threshold(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
int db;
u16 reg;
db = (int)ucontrol->value.integer.value[0];
if (db < 0 || db > 96)
return -EINVAL;
reg = avc_thr_db2reg[db];
snd_soc_write(codec, SGTL5000_DAP_AVC_THRESHOLD, reg);
return 0;
}
static const DECLARE_TLV_DB_SCALE(capture_6db_attenuate, -600, 600, 0);
/* tlv for mic gain, 0db 20db 30db 40db */
@ -396,6 +469,12 @@ static const DECLARE_TLV_DB_SCALE(headphone_volume, -5150, 50, 0);
/* tlv for lineout volume, 31 steps of .5db each */
static const DECLARE_TLV_DB_SCALE(lineout_volume, -1550, 50, 0);
/* tlv for dap avc max gain, 0db, 6db, 12db */
static const DECLARE_TLV_DB_SCALE(avc_max_gain, 0, 600, 0);
/* tlv for dap avc threshold, */
static const DECLARE_TLV_DB_MINMAX(avc_threshold, 0, 9600);
static const struct snd_kcontrol_new sgtl5000_snd_controls[] = {
/* SOC_DOUBLE_S8_TLV with invert */
{
@ -434,6 +513,16 @@ static const struct snd_kcontrol_new sgtl5000_snd_controls[] = {
0x1f, 1,
lineout_volume),
SOC_SINGLE("Lineout Playback Switch", SGTL5000_CHIP_ANA_CTRL, 8, 1, 1),
/* Automatic Volume Control (DAP AVC) */
SOC_SINGLE("AVC Switch", SGTL5000_DAP_AVC_CTRL, 0, 1, 0),
SOC_SINGLE("AVC Hard Limiter Switch", SGTL5000_DAP_AVC_CTRL, 5, 1, 0),
SOC_SINGLE_TLV("AVC Max Gain Volume", SGTL5000_DAP_AVC_CTRL, 12, 2, 0,
avc_max_gain),
SOC_SINGLE("AVC Integrator Response", SGTL5000_DAP_AVC_CTRL, 8, 3, 0),
SOC_SINGLE_EXT_TLV("AVC Threshold Volume", SGTL5000_DAP_AVC_THRESHOLD,
0, 96, 0, avc_get_threshold, avc_put_threshold,
avc_threshold),
};
/* mute the codec used by alsa core */

View File

@ -1210,7 +1210,7 @@ static const struct snd_soc_dai_ops aic31xx_dai_ops = {
static struct snd_soc_dai_driver dac31xx_dai_driver[] = {
{
.name = "tlv32dac31xx-hifi",
.name = "tlv320dac31xx-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 2,

View File

@ -482,8 +482,6 @@ struct wm_coeff_ctl_ops {
struct snd_ctl_elem_value *ucontrol);
int (*xput)(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int (*xinfo)(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo);
};
struct wm_coeff_ctl {
@ -1890,7 +1888,7 @@ static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs,
}
if (be32_to_cpu(val) != 0xbedead)
adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n",
adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbedead\n",
pos + len, be32_to_cpu(val));
alg = kzalloc(len * 2, GFP_KERNEL | GFP_DMA);
@ -2654,7 +2652,7 @@ int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol,
(struct soc_mixer_control *)kcontrol->private_value;
char preload[32];
snprintf(preload, ARRAY_SIZE(preload), "DSP%d Preload", mc->shift);
snprintf(preload, ARRAY_SIZE(preload), "DSP%u Preload", mc->shift);
dsp->preloaded = ucontrol->value.integer.value[0];

View File

@ -0,0 +1,403 @@
/*
* Copyright (C) 2017 Sanechips Technology Co., Ltd.
* Copyright 2017 Linaro Ltd.
*
* Author: Baoyou Xie <baoyou.xie@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dai.h>
#include <sound/tlv.h>
#define AUD96P22_RESET 0x00
#define RST_DAC_DPZ BIT(0)
#define RST_ADC_DPZ BIT(1)
#define AUD96P22_I2S1_CONFIG_0 0x03
#define I2S1_MS_MODE BIT(3)
#define I2S1_MODE_MASK 0x7
#define I2S1_MODE_RIGHT_J 0x0
#define I2S1_MODE_I2S 0x1
#define I2S1_MODE_LEFT_J 0x2
#define AUD96P22_PD_0 0x15
#define AUD96P22_PD_1 0x16
#define AUD96P22_PD_3 0x18
#define AUD96P22_PD_4 0x19
#define AUD96P22_MUTE_0 0x1d
#define AUD96P22_MUTE_2 0x1f
#define AUD96P22_MUTE_4 0x21
#define AUD96P22_RECVOL_0 0x24
#define AUD96P22_RECVOL_1 0x25
#define AUD96P22_PGA1VOL_0 0x26
#define AUD96P22_PGA1VOL_1 0x27
#define AUD96P22_LMVOL_0 0x34
#define AUD96P22_LMVOL_1 0x35
#define AUD96P22_HS1VOL_0 0x38
#define AUD96P22_HS1VOL_1 0x39
#define AUD96P22_PGA1SEL_0 0x47
#define AUD96P22_PGA1SEL_1 0x48
#define AUD96P22_LDR1SEL_0 0x59
#define AUD96P22_LDR1SEL_1 0x60
#define AUD96P22_LDR2SEL_0 0x5d
#define AUD96P22_REG_MAX 0xfb
struct aud96p22_priv {
struct regmap *regmap;
};
static int aud96p22_adc_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct aud96p22_priv *priv = snd_soc_codec_get_drvdata(codec);
struct regmap *regmap = priv->regmap;
if (event != SND_SOC_DAPM_POST_PMU)
return -EINVAL;
/* Assert/de-assert the bit to reset ADC data path */
regmap_update_bits(regmap, AUD96P22_RESET, RST_ADC_DPZ, 0);
regmap_update_bits(regmap, AUD96P22_RESET, RST_ADC_DPZ, RST_ADC_DPZ);
return 0;
}
static int aud96p22_dac_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct aud96p22_priv *priv = snd_soc_codec_get_drvdata(codec);
struct regmap *regmap = priv->regmap;
if (event != SND_SOC_DAPM_POST_PMU)
return -EINVAL;
/* Assert/de-assert the bit to reset DAC data path */
regmap_update_bits(regmap, AUD96P22_RESET, RST_DAC_DPZ, 0);
regmap_update_bits(regmap, AUD96P22_RESET, RST_DAC_DPZ, RST_DAC_DPZ);
return 0;
}
static const DECLARE_TLV_DB_SCALE(lm_tlv, -11550, 50, 0);
static const DECLARE_TLV_DB_SCALE(hs_tlv, -3900, 300, 0);
static const DECLARE_TLV_DB_SCALE(rec_tlv, -9550, 50, 0);
static const DECLARE_TLV_DB_SCALE(pga_tlv, -1800, 100, 0);
static const struct snd_kcontrol_new aud96p22_snd_controls[] = {
/* Volume control */
SOC_DOUBLE_R_TLV("Master Playback Volume", AUD96P22_LMVOL_0,
AUD96P22_LMVOL_1, 0, 0xff, 0, lm_tlv),
SOC_DOUBLE_R_TLV("Headphone Volume", AUD96P22_HS1VOL_0,
AUD96P22_HS1VOL_1, 0, 0xf, 0, hs_tlv),
SOC_DOUBLE_R_TLV("Master Capture Volume", AUD96P22_RECVOL_0,
AUD96P22_RECVOL_1, 0, 0xff, 0, rec_tlv),
SOC_DOUBLE_R_TLV("Analogue Capture Volume", AUD96P22_PGA1VOL_0,
AUD96P22_PGA1VOL_1, 0, 0x37, 0, pga_tlv),
/* Mute control */
SOC_DOUBLE("Master Playback Switch", AUD96P22_MUTE_2, 0, 1, 1, 1),
SOC_DOUBLE("Headphone Switch", AUD96P22_MUTE_2, 4, 5, 1, 1),
SOC_DOUBLE("Line Out Switch", AUD96P22_MUTE_4, 0, 1, 1, 1),
SOC_DOUBLE("Speaker Switch", AUD96P22_MUTE_4, 2, 3, 1, 1),
SOC_DOUBLE("Master Capture Switch", AUD96P22_MUTE_0, 0, 1, 1, 1),
SOC_DOUBLE("Analogue Capture Switch", AUD96P22_MUTE_0, 2, 3, 1, 1),
};
/* Input mux kcontrols */
static const unsigned int ain_mux_values[] = {
0, 1, 3, 4, 5,
};
static const char * const ainl_mux_texts[] = {
"AINL1 differential",
"AINL1 single-ended",
"AINL3 single-ended",
"AINL2 differential",
"AINL2 single-ended",
};
static const char * const ainr_mux_texts[] = {
"AINR1 differential",
"AINR1 single-ended",
"AINR3 single-ended",
"AINR2 differential",
"AINR2 single-ended",
};
static SOC_VALUE_ENUM_SINGLE_DECL(ainl_mux_enum, AUD96P22_PGA1SEL_0,
0, 0x7, ainl_mux_texts, ain_mux_values);
static SOC_VALUE_ENUM_SINGLE_DECL(ainr_mux_enum, AUD96P22_PGA1SEL_1,
0, 0x7, ainr_mux_texts, ain_mux_values);
static const struct snd_kcontrol_new ainl_mux_kcontrol =
SOC_DAPM_ENUM("AINL Mux", ainl_mux_enum);
static const struct snd_kcontrol_new ainr_mux_kcontrol =
SOC_DAPM_ENUM("AINR Mux", ainr_mux_enum);
/* Output mixer kcontrols */
static const struct snd_kcontrol_new ld1_left_kcontrols[] = {
SOC_DAPM_SINGLE("DACL LD1L Switch", AUD96P22_LDR1SEL_0, 0, 1, 0),
SOC_DAPM_SINGLE("AINL LD1L Switch", AUD96P22_LDR1SEL_0, 1, 1, 0),
SOC_DAPM_SINGLE("AINR LD1L Switch", AUD96P22_LDR1SEL_0, 2, 1, 0),
};
static const struct snd_kcontrol_new ld1_right_kcontrols[] = {
SOC_DAPM_SINGLE("DACR LD1R Switch", AUD96P22_LDR1SEL_1, 8, 1, 0),
SOC_DAPM_SINGLE("AINR LD1R Switch", AUD96P22_LDR1SEL_1, 9, 1, 0),
SOC_DAPM_SINGLE("AINL LD1R Switch", AUD96P22_LDR1SEL_1, 10, 1, 0),
};
static const struct snd_kcontrol_new ld2_kcontrols[] = {
SOC_DAPM_SINGLE("DACL LD2 Switch", AUD96P22_LDR2SEL_0, 0, 1, 0),
SOC_DAPM_SINGLE("AINL LD2 Switch", AUD96P22_LDR2SEL_0, 1, 1, 0),
SOC_DAPM_SINGLE("DACR LD2 Switch", AUD96P22_LDR2SEL_0, 2, 1, 0),
};
static const struct snd_soc_dapm_widget aud96p22_dapm_widgets[] = {
/* Overall power bit */
SND_SOC_DAPM_SUPPLY("POWER", AUD96P22_PD_0, 0, 0, NULL, 0),
/* Input pins */
SND_SOC_DAPM_INPUT("AINL1P"),
SND_SOC_DAPM_INPUT("AINL2P"),
SND_SOC_DAPM_INPUT("AINL3"),
SND_SOC_DAPM_INPUT("AINL1N"),
SND_SOC_DAPM_INPUT("AINL2N"),
SND_SOC_DAPM_INPUT("AINR2N"),
SND_SOC_DAPM_INPUT("AINR1N"),
SND_SOC_DAPM_INPUT("AINR3"),
SND_SOC_DAPM_INPUT("AINR2P"),
SND_SOC_DAPM_INPUT("AINR1P"),
/* Input muxes */
SND_SOC_DAPM_MUX("AINLMUX", AUD96P22_PD_1, 2, 0, &ainl_mux_kcontrol),
SND_SOC_DAPM_MUX("AINRMUX", AUD96P22_PD_1, 3, 0, &ainr_mux_kcontrol),
/* ADCs */
SND_SOC_DAPM_ADC_E("ADCL", "Capture Left", AUD96P22_PD_1, 0, 0,
aud96p22_adc_event, SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_ADC_E("ADCR", "Capture Right", AUD96P22_PD_1, 1, 0,
aud96p22_adc_event, SND_SOC_DAPM_POST_PMU),
/* DACs */
SND_SOC_DAPM_DAC_E("DACL", "Playback Left", AUD96P22_PD_3, 0, 0,
aud96p22_dac_event, SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_DAC_E("DACR", "Playback Right", AUD96P22_PD_3, 1, 0,
aud96p22_dac_event, SND_SOC_DAPM_POST_PMU),
/* Output mixers */
SND_SOC_DAPM_MIXER("LD1L", AUD96P22_PD_3, 6, 0, ld1_left_kcontrols,
ARRAY_SIZE(ld1_left_kcontrols)),
SND_SOC_DAPM_MIXER("LD1R", AUD96P22_PD_3, 7, 0, ld1_right_kcontrols,
ARRAY_SIZE(ld1_right_kcontrols)),
SND_SOC_DAPM_MIXER("LD2", AUD96P22_PD_4, 2, 0, ld2_kcontrols,
ARRAY_SIZE(ld2_kcontrols)),
/* Headset power switch */
SND_SOC_DAPM_SUPPLY("HS1L", AUD96P22_PD_3, 4, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("HS1R", AUD96P22_PD_3, 5, 0, NULL, 0),
/* Output pins */
SND_SOC_DAPM_OUTPUT("HSOUTL"),
SND_SOC_DAPM_OUTPUT("LINEOUTL"),
SND_SOC_DAPM_OUTPUT("LINEOUTMP"),
SND_SOC_DAPM_OUTPUT("LINEOUTMN"),
SND_SOC_DAPM_OUTPUT("LINEOUTR"),
SND_SOC_DAPM_OUTPUT("HSOUTR"),
};
static const struct snd_soc_dapm_route aud96p22_dapm_routes[] = {
{ "AINLMUX", "AINL1 differential", "AINL1N" },
{ "AINLMUX", "AINL1 single-ended", "AINL1P" },
{ "AINLMUX", "AINL3 single-ended", "AINL3" },
{ "AINLMUX", "AINL2 differential", "AINL2N" },
{ "AINLMUX", "AINL2 single-ended", "AINL2P" },
{ "AINRMUX", "AINR1 differential", "AINR1N" },
{ "AINRMUX", "AINR1 single-ended", "AINR1P" },
{ "AINRMUX", "AINR3 single-ended", "AINR3" },
{ "AINRMUX", "AINR2 differential", "AINR2N" },
{ "AINRMUX", "AINR2 single-ended", "AINR2P" },
{ "ADCL", NULL, "AINLMUX" },
{ "ADCR", NULL, "AINRMUX" },
{ "ADCL", NULL, "POWER" },
{ "ADCR", NULL, "POWER" },
{ "DACL", NULL, "POWER" },
{ "DACR", NULL, "POWER" },
{ "LD1L", "DACL LD1L Switch", "DACL" },
{ "LD1L", "AINL LD1L Switch", "AINLMUX" },
{ "LD1L", "AINR LD1L Switch", "AINRMUX" },
{ "LD1R", "DACR LD1R Switch", "DACR" },
{ "LD1R", "AINR LD1R Switch", "AINRMUX" },
{ "LD1R", "AINL LD1R Switch", "AINLMUX" },
{ "LD2", "DACL LD2 Switch", "DACL" },
{ "LD2", "AINL LD2 Switch", "AINLMUX" },
{ "LD2", "DACR LD2 Switch", "DACR" },
{ "HSOUTL", NULL, "LD1L" },
{ "HSOUTR", NULL, "LD1R" },
{ "HSOUTL", NULL, "HS1L" },
{ "HSOUTR", NULL, "HS1R" },
{ "LINEOUTL", NULL, "LD1L" },
{ "LINEOUTR", NULL, "LD1R" },
{ "LINEOUTMP", NULL, "LD2" },
{ "LINEOUTMN", NULL, "LD2" },
};
static struct snd_soc_codec_driver aud96p22_driver = {
.component_driver = {
.controls = aud96p22_snd_controls,
.num_controls = ARRAY_SIZE(aud96p22_snd_controls),
.dapm_widgets = aud96p22_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(aud96p22_dapm_widgets),
.dapm_routes = aud96p22_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(aud96p22_dapm_routes),
},
};
static int aud96p22_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct aud96p22_priv *priv = snd_soc_codec_get_drvdata(dai->codec);
struct regmap *regmap = priv->regmap;
unsigned int val;
/* Master/slave mode */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
val = 0;
break;
case SND_SOC_DAIFMT_CBM_CFM:
val = I2S1_MS_MODE;
break;
default:
return -EINVAL;
}
regmap_update_bits(regmap, AUD96P22_I2S1_CONFIG_0, I2S1_MS_MODE, val);
/* Audio format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_RIGHT_J:
val = I2S1_MODE_RIGHT_J;
break;
case SND_SOC_DAIFMT_I2S:
val = I2S1_MODE_I2S;
break;
case SND_SOC_DAIFMT_LEFT_J:
val = I2S1_MODE_LEFT_J;
break;
default:
return -EINVAL;
}
regmap_update_bits(regmap, AUD96P22_I2S1_CONFIG_0, I2S1_MODE_MASK, val);
return 0;
}
static struct snd_soc_dai_ops aud96p22_dai_ops = {
.set_fmt = aud96p22_set_fmt,
};
#define AUD96P22_RATES SNDRV_PCM_RATE_8000_192000
#define AUD96P22_FORMATS (\
SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE)
static struct snd_soc_dai_driver aud96p22_dai = {
.name = "aud96p22-dai",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = AUD96P22_RATES,
.formats = AUD96P22_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = AUD96P22_RATES,
.formats = AUD96P22_FORMATS,
},
.ops = &aud96p22_dai_ops,
};
static const struct regmap_config aud96p22_regmap = {
.reg_bits = 8,
.val_bits = 8,
.max_register = AUD96P22_REG_MAX,
.cache_type = REGCACHE_RBTREE,
};
static int aud96p22_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct device *dev = &i2c->dev;
struct aud96p22_priv *priv;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (priv == NULL)
return -ENOMEM;
priv->regmap = devm_regmap_init_i2c(i2c, &aud96p22_regmap);
if (IS_ERR(priv->regmap)) {
ret = PTR_ERR(priv->regmap);
dev_err(dev, "failed to init i2c regmap: %d\n", ret);
return ret;
}
i2c_set_clientdata(i2c, priv);
ret = snd_soc_register_codec(dev, &aud96p22_driver, &aud96p22_dai, 1);
if (ret) {
dev_err(dev, "failed to register codec: %d\n", ret);
return ret;
}
return 0;
}
static int aud96p22_i2c_remove(struct i2c_client *i2c)
{
snd_soc_unregister_codec(&i2c->dev);
return 0;
}
const struct of_device_id aud96p22_dt_ids[] = {
{ .compatible = "zte,zx-aud96p22", },
{ }
};
MODULE_DEVICE_TABLE(of, aud96p22_dt_ids);
static struct i2c_driver aud96p22_i2c_driver = {
.driver = {
.name = "zx_aud96p22",
.of_match_table = aud96p22_dt_ids,
},
.probe = aud96p22_i2c_probe,
.remove = aud96p22_i2c_remove,
};
module_i2c_driver(aud96p22_i2c_driver);
MODULE_DESCRIPTION("ZTE ASoC AUD96P22 CODEC driver");
MODULE_AUTHOR("Baoyou Xie <baoyou.xie@linaro.org>");
MODULE_LICENSE("GPL v2");

View File

@ -302,7 +302,6 @@ static int psc_dma_new(struct snd_soc_pcm_runtime *rtd)
struct snd_card *card = rtd->card->snd_card;
struct snd_soc_dai *dai = rtd->cpu_dai;
struct snd_pcm *pcm = rtd->pcm;
struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai);
size_t size = psc_dma_hardware.buffer_bytes_max;
int rc;

View File

@ -13,6 +13,7 @@
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
@ -30,6 +31,34 @@ struct graph_card_data {
struct asoc_simple_dai codec_dai;
} *dai_props;
struct snd_soc_dai_link *dai_link;
struct gpio_desc *pa_gpio;
};
static int asoc_graph_card_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);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
gpiod_set_value_cansleep(priv->pa_gpio, 1);
break;
case SND_SOC_DAPM_PRE_PMD:
gpiod_set_value_cansleep(priv->pa_gpio, 0);
break;
default:
return -EINVAL;
}
return 0;
}
static const struct snd_soc_dapm_widget asoc_graph_card_dapm_widgets[] = {
SND_SOC_DAPM_OUT_DRV_E("Amplifier", SND_SOC_NOPM,
0, 0, NULL, 0, asoc_graph_card_outdrv_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
};
#define graph_priv_to_card(priv) (&(priv)->snd_card)
@ -44,13 +73,13 @@ static int asoc_graph_card_startup(struct snd_pcm_substream *substream)
struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
int ret;
ret = clk_prepare_enable(dai_props->cpu_dai.clk);
ret = asoc_simple_card_clk_enable(&dai_props->cpu_dai);
if (ret)
return ret;
ret = clk_prepare_enable(dai_props->codec_dai.clk);
ret = asoc_simple_card_clk_enable(&dai_props->codec_dai);
if (ret)
clk_disable_unprepare(dai_props->cpu_dai.clk);
asoc_simple_card_clk_disable(&dai_props->cpu_dai);
return ret;
}
@ -61,9 +90,9 @@ static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream)
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);
clk_disable_unprepare(dai_props->cpu_dai.clk);
asoc_simple_card_clk_disable(&dai_props->cpu_dai);
clk_disable_unprepare(dai_props->codec_dai.clk);
asoc_simple_card_clk_disable(&dai_props->codec_dai);
}
static struct snd_soc_ops asoc_graph_card_ops = {
@ -100,7 +129,6 @@ static int asoc_graph_card_dai_link_of(struct device_node *cpu_port,
struct graph_dai_props *dai_props = graph_priv_to_props(priv, idx);
struct asoc_simple_dai *cpu_dai = &dai_props->cpu_dai;
struct asoc_simple_dai *codec_dai = &dai_props->codec_dai;
struct snd_soc_card *card = graph_priv_to_card(priv);
struct device_node *cpu_ep = of_get_next_child(cpu_port, NULL);
struct device_node *codec_ep = of_graph_get_remote_endpoint(cpu_ep);
struct device_node *rcpu_ep = of_graph_get_remote_endpoint(codec_ep);
@ -131,19 +159,11 @@ static int asoc_graph_card_dai_link_of(struct device_node *cpu_port,
if (ret < 0)
goto dai_link_of_err;
ret = snd_soc_of_parse_tdm_slot(cpu_ep,
&cpu_dai->tx_slot_mask,
&cpu_dai->rx_slot_mask,
&cpu_dai->slots,
&cpu_dai->slot_width);
ret = asoc_simple_card_of_parse_tdm(cpu_ep, cpu_dai);
if (ret < 0)
goto dai_link_of_err;
ret = snd_soc_of_parse_tdm_slot(codec_ep,
&codec_dai->tx_slot_mask,
&codec_dai->rx_slot_mask,
&codec_dai->slots,
&codec_dai->slot_width);
ret = asoc_simple_card_of_parse_tdm(codec_ep, codec_dai);
if (ret < 0)
goto dai_link_of_err;
@ -170,7 +190,7 @@ static int asoc_graph_card_dai_link_of(struct device_node *cpu_port,
dai_link->init = asoc_graph_card_dai_init;
asoc_simple_card_canonicalize_cpu(dai_link,
card->num_links == 1);
of_graph_get_endpoint_count(dai_link->cpu_of_node) == 1);
dai_link_of_err:
of_node_put(cpu_ep);
@ -189,8 +209,16 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv)
int rc, idx = 0;
int ret;
ret = asoc_simple_card_of_parse_widgets(card, NULL);
if (ret < 0)
return ret;
ret = asoc_simple_card_of_parse_routing(card, NULL, 1);
if (ret < 0)
return ret;
/*
* we need to consider "widgets", "routing", "mclk-fs" around here
* we need to consider "mclk-fs" around here
* see simple-card
*/
@ -242,6 +270,13 @@ static int asoc_graph_card_probe(struct platform_device *pdev)
if (!dai_props || !dai_link)
return -ENOMEM;
priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW);
if (IS_ERR(priv->pa_gpio)) {
ret = PTR_ERR(priv->pa_gpio);
dev_err(dev, "failed to get amplifier gpio: %d\n", ret);
return ret;
}
priv->dai_props = dai_props;
priv->dai_link = dai_link;
@ -251,6 +286,8 @@ static int asoc_graph_card_probe(struct platform_device *pdev)
card->dev = dev;
card->dai_link = dai_link;
card->num_links = num;
card->dapm_widgets = asoc_graph_card_dapm_widgets;
card->num_dapm_widgets = ARRAY_SIZE(asoc_graph_card_dapm_widgets);
ret = asoc_graph_card_parse_of(priv);
if (ret < 0) {

View File

@ -30,8 +30,7 @@ struct graph_card_data {
struct snd_soc_codec_conf codec_conf;
struct asoc_simple_dai *dai_props;
struct snd_soc_dai_link *dai_link;
u32 convert_rate;
u32 convert_channels;
struct asoc_simple_card_data adata;
};
#define graph_priv_to_card(priv) (&(priv)->snd_card)
@ -45,7 +44,7 @@ static int asoc_graph_card_startup(struct snd_pcm_substream *substream)
struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
struct asoc_simple_dai *dai_props = graph_priv_to_props(priv, rtd->num);
return clk_prepare_enable(dai_props->clk);
return asoc_simple_card_clk_enable(dai_props);
}
static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream)
@ -54,7 +53,7 @@ static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream)
struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
struct asoc_simple_dai *dai_props = graph_priv_to_props(priv, rtd->num);
clk_disable_unprepare(dai_props->clk);
asoc_simple_card_clk_disable(dai_props);
}
static struct snd_soc_ops asoc_graph_card_ops = {
@ -83,18 +82,8 @@ 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 snd_interval *rate = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_RATE);
struct snd_interval *channels = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_CHANNELS);
if (priv->convert_rate)
rate->min =
rate->max = priv->convert_rate;
if (priv->convert_channels)
channels->min =
channels->max = priv->convert_channels;
asoc_simple_card_convert_fixup(&priv->adata, params);
return 0;
}
@ -136,7 +125,7 @@ static int asoc_graph_card_dai_link_of(struct device_node *ep,
/* card->num_links includes Codec */
asoc_simple_card_canonicalize_cpu(dai_link,
(card->num_links - 1) == 1);
of_graph_get_endpoint_count(dai_link->cpu_of_node) == 1);
} else {
/* FE is dummy */
dai_link->cpu_of_node = NULL;
@ -167,11 +156,7 @@ static int asoc_graph_card_dai_link_of(struct device_node *ep,
"prefix");
}
ret = snd_soc_of_parse_tdm_slot(ep,
&dai_props->tx_slot_mask,
&dai_props->rx_slot_mask,
&dai_props->slots,
&dai_props->slot_width);
ret = asoc_simple_card_of_parse_tdm(ep, dai_props);
if (ret)
return ret;
@ -198,6 +183,8 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv)
struct device_node *cpu_ep;
struct device_node *codec_ep;
struct device_node *rcpu_ep;
struct device_node *codec_port;
struct device_node *codec_port_old;
unsigned int daifmt = 0;
int dai_idx, ret;
int rc, codec;
@ -210,15 +197,11 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv)
* see simple-card
*/
ret = snd_soc_of_parse_audio_routing(card, "routing");
if (ret)
ret = asoc_simple_card_of_parse_routing(card, NULL, 0);
if (ret < 0)
return ret;
/* sampling rate convert */
of_property_read_u32(node, "convert-rate", &priv->convert_rate);
/* channels transfer */
of_property_read_u32(node, "convert-channels", &priv->convert_channels);
asoc_simple_card_parse_convert(dev, NULL, &priv->adata);
/*
* it supports multi CPU, single CODEC only here
@ -254,6 +237,7 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv)
}
dai_idx = 0;
codec_port_old = NULL;
for (codec = 0; codec < 2; codec++) {
/*
* To listup valid sounds continuously,
@ -264,15 +248,22 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv)
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_port);
of_node_put(cpu_ep);
of_node_put(codec_ep);
of_node_put(codec_port);
if (codec) {
if (!codec_ep)
if (!codec_port)
continue;
if (codec_port_old == codec_port)
continue;
codec_port_old = codec_port;
/* Back-End (= Codec) */
ret = asoc_graph_card_dai_link_of(codec_ep, priv, daifmt, dai_idx++, 0);
if (ret < 0)
@ -290,9 +281,6 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv)
if (ret)
goto parse_of_err;
dev_dbg(dev, "convert_rate %d\n", priv->convert_rate);
dev_dbg(dev, "convert_channels %d\n", priv->convert_channels);
ret = 0;
parse_of_err:
@ -306,22 +294,34 @@ static int asoc_graph_get_dais_count(struct device *dev)
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 count = 0;
int rc;
codec_port_old = 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_port);
of_node_put(cpu_ep);
of_node_put(codec_ep);
of_node_put(codec_port);
if (cpu_ep)
count++;
if (codec_ep)
count++;
if (!codec_port)
continue;
if (codec_port_old == codec_port)
continue;
count++;
codec_port_old = codec_port;
}
return count;

View File

@ -13,6 +13,46 @@
#include <linux/of_graph.h>
#include <sound/simple_card_utils.h>
void asoc_simple_card_convert_fixup(struct asoc_simple_card_data *data,
struct snd_pcm_hw_params *params)
{
struct snd_interval *rate = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_RATE);
struct snd_interval *channels = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_CHANNELS);
if (data->convert_rate)
rate->min =
rate->max = data->convert_rate;
if (data->convert_channels)
channels->min =
channels->max = data->convert_channels;
}
EXPORT_SYMBOL_GPL(asoc_simple_card_convert_fixup);
void asoc_simple_card_parse_convert(struct device *dev, char *prefix,
struct asoc_simple_card_data *data)
{
struct device_node *np = dev->of_node;
char prop[128];
if (!prefix)
prefix = "";
/* sampling rate convert */
snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-rate");
of_property_read_u32(np, prop, &data->convert_rate);
/* channels transfer */
snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-channels");
of_property_read_u32(np, prop, &data->convert_channels);
dev_dbg(dev, "convert_rate %d\n", data->convert_rate);
dev_dbg(dev, "convert_channels %d\n", data->convert_channels);
}
EXPORT_SYMBOL_GPL(asoc_simple_card_parse_convert);
int asoc_simple_card_parse_daifmt(struct device *dev,
struct device_node *node,
struct device_node *codec,
@ -110,6 +150,24 @@ int asoc_simple_card_parse_card_name(struct snd_soc_card *card,
}
EXPORT_SYMBOL_GPL(asoc_simple_card_parse_card_name);
static void asoc_simple_card_clk_register(struct asoc_simple_dai *dai,
struct clk *clk)
{
dai->clk = clk;
}
int asoc_simple_card_clk_enable(struct asoc_simple_dai *dai)
{
return clk_prepare_enable(dai->clk);
}
EXPORT_SYMBOL_GPL(asoc_simple_card_clk_enable);
void asoc_simple_card_clk_disable(struct asoc_simple_dai *dai)
{
clk_disable_unprepare(dai->clk);
}
EXPORT_SYMBOL_GPL(asoc_simple_card_clk_disable);
int asoc_simple_card_parse_clk(struct device *dev,
struct device_node *node,
struct device_node *dai_of_node,
@ -128,7 +186,8 @@ int asoc_simple_card_parse_clk(struct device *dev,
clk = devm_get_clk_from_child(dev, node, NULL);
if (!IS_ERR(clk)) {
simple_dai->sysclk = clk_get_rate(clk);
simple_dai->clk = clk;
asoc_simple_card_clk_register(simple_dai, clk);
} else if (!of_property_read_u32(node, "system-clock-frequency", &val)) {
simple_dai->sysclk = val;
} else {
@ -316,6 +375,47 @@ int asoc_simple_card_clean_reference(struct snd_soc_card *card)
}
EXPORT_SYMBOL_GPL(asoc_simple_card_clean_reference);
int asoc_simple_card_of_parse_routing(struct snd_soc_card *card,
char *prefix,
int optional)
{
struct device_node *node = card->dev->of_node;
char prop[128];
if (!prefix)
prefix = "";
snprintf(prop, sizeof(prop), "%s%s", prefix, "routing");
if (!of_property_read_bool(node, prop)) {
if (optional)
return 0;
return -EINVAL;
}
return snd_soc_of_parse_audio_routing(card, prop);
}
EXPORT_SYMBOL_GPL(asoc_simple_card_of_parse_routing);
int asoc_simple_card_of_parse_widgets(struct snd_soc_card *card,
char *prefix)
{
struct device_node *node = card->dev->of_node;
char prop[128];
if (!prefix)
prefix = "";
snprintf(prop, sizeof(prop), "%s%s", prefix, "widgets");
if (of_property_read_bool(node, prop))
return snd_soc_of_parse_audio_simple_widgets(card, prop);
/* no widgets is not error */
return 0;
}
EXPORT_SYMBOL_GPL(asoc_simple_card_of_parse_widgets);
/* Module information */
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
MODULE_DESCRIPTION("ALSA SoC Simple Card Utils");

View File

@ -118,13 +118,13 @@ static int asoc_simple_card_startup(struct snd_pcm_substream *substream)
simple_priv_to_props(priv, rtd->num);
int ret;
ret = clk_prepare_enable(dai_props->cpu_dai.clk);
ret = asoc_simple_card_clk_enable(&dai_props->cpu_dai);
if (ret)
return ret;
ret = clk_prepare_enable(dai_props->codec_dai.clk);
ret = asoc_simple_card_clk_enable(&dai_props->codec_dai);
if (ret)
clk_disable_unprepare(dai_props->cpu_dai.clk);
asoc_simple_card_clk_disable(&dai_props->cpu_dai);
return ret;
}
@ -136,9 +136,9 @@ static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream)
struct simple_dai_props *dai_props =
simple_priv_to_props(priv, rtd->num);
clk_disable_unprepare(dai_props->cpu_dai.clk);
asoc_simple_card_clk_disable(&dai_props->cpu_dai);
clk_disable_unprepare(dai_props->codec_dai.clk);
asoc_simple_card_clk_disable(&dai_props->codec_dai);
}
static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream,
@ -233,13 +233,19 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
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 (!cpu || !codec) {
if (!codec) {
ret = -EINVAL;
dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
goto dai_link_of_err;
@ -265,17 +271,11 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
if (ret < 0)
goto dai_link_of_err;
ret = snd_soc_of_parse_tdm_slot(cpu, &cpu_dai->tx_slot_mask,
&cpu_dai->rx_slot_mask,
&cpu_dai->slots,
&cpu_dai->slot_width);
ret = asoc_simple_card_of_parse_tdm(cpu, cpu_dai);
if (ret < 0)
goto dai_link_of_err;
ret = snd_soc_of_parse_tdm_slot(codec, &codec_dai->tx_slot_mask,
&codec_dai->rx_slot_mask,
&codec_dai->slots,
&codec_dai->slot_width);
ret = asoc_simple_card_of_parse_tdm(codec, codec_dai);
if (ret < 0)
goto dai_link_of_err;
@ -341,12 +341,12 @@ static int asoc_simple_card_parse_aux_devs(struct device_node *node,
return 0;
}
static int asoc_simple_card_parse_of(struct device_node *node,
struct simple_card_data *priv)
static int asoc_simple_card_parse_of(struct simple_card_data *priv)
{
struct device *dev = simple_priv_to_dev(priv);
struct snd_soc_card *card = simple_priv_to_card(priv);
struct device_node *dai_link;
struct device_node *node = dev->of_node;
int ret;
if (!node)
@ -354,21 +354,13 @@ static int asoc_simple_card_parse_of(struct device_node *node,
dai_link = of_get_child_by_name(node, PREFIX "dai-link");
/* The off-codec widgets */
if (of_property_read_bool(node, PREFIX "widgets")) {
ret = snd_soc_of_parse_audio_simple_widgets(card,
PREFIX "widgets");
if (ret)
goto card_parse_end;
}
ret = asoc_simple_card_of_parse_widgets(card, PREFIX);
if (ret < 0)
goto card_parse_end;
/* DAPM routes */
if (of_property_read_bool(node, PREFIX "routing")) {
ret = snd_soc_of_parse_audio_routing(card,
PREFIX "routing");
if (ret)
goto card_parse_end;
}
ret = asoc_simple_card_of_parse_routing(card, PREFIX, 1);
if (ret < 0)
goto card_parse_end;
/* Factor to mclk, used in hw_params() */
of_property_read_u32(node, PREFIX "mclk-fs", &priv->mclk_fs);
@ -445,7 +437,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
if (np && of_device_is_available(np)) {
ret = asoc_simple_card_parse_of(np, priv);
ret = asoc_simple_card_parse_of(priv);
if (ret < 0) {
if (ret != -EPROBE_DEFER)
dev_err(dev, "parse error %d\n", ret);

View File

@ -27,8 +27,7 @@ struct simple_card_data {
struct snd_soc_codec_conf codec_conf;
struct asoc_simple_dai *dai_props;
struct snd_soc_dai_link *dai_link;
u32 convert_rate;
u32 convert_channels;
struct asoc_simple_card_data adata;
};
#define simple_priv_to_card(priv) (&(priv)->snd_card)
@ -47,7 +46,7 @@ static int asoc_simple_card_startup(struct snd_pcm_substream *substream)
struct asoc_simple_dai *dai_props =
simple_priv_to_props(priv, rtd->num);
return clk_prepare_enable(dai_props->clk);
return asoc_simple_card_clk_enable(dai_props);
}
static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream)
@ -57,7 +56,7 @@ static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream)
struct asoc_simple_dai *dai_props =
simple_priv_to_props(priv, rtd->num);
clk_disable_unprepare(dai_props->clk);
asoc_simple_card_clk_disable(dai_props);
}
static const struct snd_soc_ops asoc_simple_card_ops = {
@ -86,18 +85,8 @@ 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 snd_interval *rate = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_RATE);
struct snd_interval *channels = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_CHANNELS);
if (priv->convert_rate)
rate->min =
rate->max = priv->convert_rate;
if (priv->convert_channels)
channels->min =
channels->max = priv->convert_channels;
asoc_simple_card_convert_fixup(&priv->adata, params);
return 0;
}
@ -171,11 +160,7 @@ static int asoc_simple_card_dai_link_of(struct device_node *np,
PREFIX "prefix");
}
ret = snd_soc_of_parse_tdm_slot(np,
&dai_props->tx_slot_mask,
&dai_props->rx_slot_mask,
&dai_props->slots,
&dai_props->slot_width);
ret = asoc_simple_card_of_parse_tdm(np, dai_props);
if (ret)
return ret;
@ -206,15 +191,11 @@ static int asoc_simple_card_parse_of(struct simple_card_data *priv)
if (!node)
return -EINVAL;
ret = snd_soc_of_parse_audio_routing(card, PREFIX "routing");
ret = asoc_simple_card_of_parse_routing(card, PREFIX, 0);
if (ret < 0)
return ret;
/* sampling rate convert */
of_property_read_u32(node, PREFIX "convert-rate", &priv->convert_rate);
/* channels transfer */
of_property_read_u32(node, PREFIX "convert-channels", &priv->convert_channels);
asoc_simple_card_parse_convert(dev, PREFIX, &priv->adata);
/* find 1st codec */
np = of_get_child_by_name(node, PREFIX "codec");
@ -241,9 +222,6 @@ static int asoc_simple_card_parse_of(struct simple_card_data *priv)
if (ret < 0)
return ret;
dev_dbg(dev, "convert_rate %d\n", priv->convert_rate);
dev_dbg(dev, "convert_channels %d\n", priv->convert_channels);
return 0;
}

View File

@ -97,8 +97,8 @@ static inline u32 hi6210_read_reg(struct hi6210_i2s *i2s, int reg)
return readl(i2s->base + reg);
}
int hi6210_i2s_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
static int hi6210_i2s_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
struct hi6210_i2s *i2s = dev_get_drvdata(cpu_dai->dev);
int ret, n;
@ -175,8 +175,9 @@ int hi6210_i2s_startup(struct snd_pcm_substream *substream,
return 0;
}
void hi6210_i2s_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
static void hi6210_i2s_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
struct hi6210_i2s *i2s = dev_get_drvdata(cpu_dai->dev);
int n;
@ -524,7 +525,7 @@ static struct snd_soc_dai_ops hi6210_i2s_dai_ops = {
.shutdown = hi6210_i2s_shutdown,
};
struct snd_soc_dai_driver hi6210_i2s_dai_init = {
static const struct snd_soc_dai_driver hi6210_i2s_dai_init = {
.probe = hi6210_i2s_dai_probe,
.playback = {
.channels_min = 2,

View File

@ -107,7 +107,7 @@ static const struct snd_kcontrol_new mt2701_cs42448_controls[] = {
static const unsigned int mt2701_cs42448_sampling_rates[] = {48000};
static struct snd_pcm_hw_constraint_list mt2701_cs42448_constraints_rates = {
static const struct snd_pcm_hw_constraint_list mt2701_cs42448_constraints_rates = {
.count = ARRAY_SIZE(mt2701_cs42448_sampling_rates),
.list = mt2701_cs42448_sampling_rates,
.mask = 0,

View File

@ -835,15 +835,11 @@ static ssize_t dma_op_mode_store(struct device *dev,
const char *buf, size_t size)
{
struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
const char * const *s;
int i = 0;
int i;
for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++)
if (sysfs_streq(buf, *s))
break;
if (i == ARRAY_SIZE(dma_op_modes))
return -EINVAL;
i = sysfs_match_string(dma_op_modes, buf);
if (i < 0)
return i;
spin_lock_irq(&mcbsp->lock);
if (!mcbsp->free) {

View File

@ -1,6 +1,6 @@
config SND_PXA2XX_SOC
tristate "SoC Audio for the Intel PXA2xx chip"
depends on ARCH_PXA
depends on ARCH_PXA || COMPILE_TEST
select SND_PXA2XX_LIB
help
Say Y or M if you want to add support for codecs attached to

View File

@ -15,6 +15,15 @@ config SND_SOC_ROCKCHIP_I2S
Rockchip I2S device. The device supports upto maximum of
8 channels each for play and record.
config SND_SOC_ROCKCHIP_PDM
tristate "Rockchip PDM Controller Driver"
depends on CLKDEV_LOOKUP && SND_SOC_ROCKCHIP
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for PDM driver for
Rockchip PDM Controller. The Controller supports up to maximum of
8 channels record.
config SND_SOC_ROCKCHIP_SPDIF
tristate "Rockchip SPDIF Device Driver"
depends on CLKDEV_LOOKUP && SND_SOC_ROCKCHIP

View File

@ -1,8 +1,10 @@
# ROCKCHIP Platform Support
snd-soc-rockchip-i2s-objs := rockchip_i2s.o
snd-soc-rockchip-pdm-objs := rockchip_pdm.o
snd-soc-rockchip-spdif-objs := rockchip_spdif.o
obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-rockchip-i2s.o
obj-$(CONFIG_SND_SOC_ROCKCHIP_PDM) += snd-soc-rockchip-pdm.o
obj-$(CONFIG_SND_SOC_ROCKCHIP_SPDIF) += snd-soc-rockchip-spdif.o
snd-soc-rockchip-max98090-objs := rockchip_max98090.o

View File

@ -206,7 +206,21 @@ static int rockchip_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
regmap_update_bits(i2s->regmap, I2S_CKR, mask, val);
mask = I2S_TXCR_IBM_MASK;
mask = I2S_CKR_CKP_MASK;
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
val = I2S_CKR_CKP_NEG;
break;
case SND_SOC_DAIFMT_IB_NF:
val = I2S_CKR_CKP_POS;
break;
default:
return -EINVAL;
}
regmap_update_bits(i2s->regmap, I2S_CKR, mask, val);
mask = I2S_TXCR_IBM_MASK | I2S_TXCR_TFS_MASK | I2S_TXCR_PBM_MASK;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_RIGHT_J:
val = I2S_TXCR_IBM_RSJM;
@ -217,13 +231,19 @@ static int rockchip_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
case SND_SOC_DAIFMT_I2S:
val = I2S_TXCR_IBM_NORMAL;
break;
case SND_SOC_DAIFMT_DSP_A: /* PCM no delay mode */
val = I2S_TXCR_TFS_PCM;
break;
case SND_SOC_DAIFMT_DSP_B: /* PCM delay 1 mode */
val = I2S_TXCR_TFS_PCM | I2S_TXCR_PBM_MODE(1);
break;
default:
return -EINVAL;
}
regmap_update_bits(i2s->regmap, I2S_TXCR, mask, val);
mask = I2S_RXCR_IBM_MASK;
mask = I2S_RXCR_IBM_MASK | I2S_RXCR_TFS_MASK | I2S_RXCR_PBM_MASK;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_RIGHT_J:
val = I2S_RXCR_IBM_RSJM;
@ -234,6 +254,12 @@ static int rockchip_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
case SND_SOC_DAIFMT_I2S:
val = I2S_RXCR_IBM_NORMAL;
break;
case SND_SOC_DAIFMT_DSP_A: /* PCM no delay mode */
val = I2S_RXCR_TFS_PCM;
break;
case SND_SOC_DAIFMT_DSP_B: /* PCM delay 1 mode */
val = I2S_RXCR_TFS_PCM | I2S_RXCR_PBM_MODE(1);
break;
default:
return -EINVAL;
}
@ -617,12 +643,13 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
goto err_pm_disable;
}
soc_dai = devm_kzalloc(&pdev->dev,
soc_dai = devm_kmemdup(&pdev->dev, &rockchip_i2s_dai,
sizeof(*soc_dai), GFP_KERNEL);
if (!soc_dai)
return -ENOMEM;
if (!soc_dai) {
ret = -ENOMEM;
goto err_pm_disable;
}
memcpy(soc_dai, &rockchip_i2s_dai, sizeof(*soc_dai));
if (!of_property_read_u32(node, "rockchip,playback-channels", &val)) {
if (val >= 2 && val <= 8)
soc_dai->playback.channels_max = val;

View File

@ -41,6 +41,7 @@
#define I2S_TXCR_TFS_SHIFT 5
#define I2S_TXCR_TFS_I2S (0 << I2S_TXCR_TFS_SHIFT)
#define I2S_TXCR_TFS_PCM (1 << I2S_TXCR_TFS_SHIFT)
#define I2S_TXCR_TFS_MASK (1 << I2S_TXCR_TFS_SHIFT)
#define I2S_TXCR_VDW_SHIFT 0
#define I2S_TXCR_VDW(x) ((x - 1) << I2S_TXCR_VDW_SHIFT)
#define I2S_TXCR_VDW_MASK (0x1f << I2S_TXCR_VDW_SHIFT)
@ -70,6 +71,7 @@
#define I2S_RXCR_TFS_SHIFT 5
#define I2S_RXCR_TFS_I2S (0 << I2S_RXCR_TFS_SHIFT)
#define I2S_RXCR_TFS_PCM (1 << I2S_RXCR_TFS_SHIFT)
#define I2S_RXCR_TFS_MASK (1 << I2S_RXCR_TFS_SHIFT)
#define I2S_RXCR_VDW_SHIFT 0
#define I2S_RXCR_VDW(x) ((x - 1) << I2S_RXCR_VDW_SHIFT)
#define I2S_RXCR_VDW_MASK (0x1f << I2S_RXCR_VDW_SHIFT)
@ -91,6 +93,7 @@
#define I2S_CKR_CKP_SHIFT 26
#define I2S_CKR_CKP_NEG (0 << I2S_CKR_CKP_SHIFT)
#define I2S_CKR_CKP_POS (1 << I2S_CKR_CKP_SHIFT)
#define I2S_CKR_CKP_MASK (1 << I2S_CKR_CKP_SHIFT)
#define I2S_CKR_RLP_SHIFT 25
#define I2S_CKR_RLP_NORMAL (0 << I2S_CKR_RLP_SHIFT)
#define I2S_CKR_RLP_OPPSITE (1 << I2S_CKR_RLP_SHIFT)

View File

@ -0,0 +1,516 @@
/*
* Rockchip PDM ALSA SoC Digital Audio Interface(DAI) driver
*
* Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/module.h>
#include <linux/clk.h>
#include <linux/of.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h>
#include "rockchip_pdm.h"
#define PDM_DMA_BURST_SIZE (16) /* size * width: 16*4 = 64 bytes */
struct rk_pdm_dev {
struct device *dev;
struct clk *clk;
struct clk *hclk;
struct regmap *regmap;
struct snd_dmaengine_dai_dma_data capture_dma_data;
};
struct rk_pdm_clkref {
unsigned int sr;
unsigned int clk;
};
static struct rk_pdm_clkref clkref[] = {
{ 8000, 40960000 },
{ 11025, 56448000 },
{ 12000, 61440000 },
};
static unsigned int get_pdm_clk(unsigned int sr)
{
unsigned int i, count, clk, div;
clk = 0;
if (!sr)
return clk;
count = ARRAY_SIZE(clkref);
for (i = 0; i < count; i++) {
if (sr % clkref[i].sr)
continue;
div = sr / clkref[i].sr;
if ((div & (div - 1)) == 0) {
clk = clkref[i].clk;
break;
}
}
return clk;
}
static inline struct rk_pdm_dev *to_info(struct snd_soc_dai *dai)
{
return snd_soc_dai_get_drvdata(dai);
}
static void rockchip_pdm_rxctrl(struct rk_pdm_dev *pdm, int on)
{
if (on) {
regmap_update_bits(pdm->regmap, PDM_DMA_CTRL,
PDM_DMA_RD_MSK, PDM_DMA_RD_EN);
regmap_update_bits(pdm->regmap, PDM_SYSCONFIG,
PDM_RX_MASK, PDM_RX_START);
} else {
regmap_update_bits(pdm->regmap, PDM_DMA_CTRL,
PDM_DMA_RD_MSK, PDM_DMA_RD_DIS);
regmap_update_bits(pdm->regmap, PDM_SYSCONFIG,
PDM_RX_MASK | PDM_RX_CLR_MASK,
PDM_RX_STOP | PDM_RX_CLR_WR);
}
}
static int rockchip_pdm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct rk_pdm_dev *pdm = to_info(dai);
unsigned int val = 0;
unsigned int clk_rate, clk_div, samplerate;
int ret;
samplerate = params_rate(params);
clk_rate = get_pdm_clk(samplerate);
if (!clk_rate)
return -EINVAL;
ret = clk_set_rate(pdm->clk, clk_rate);
if (ret)
return -EINVAL;
clk_div = DIV_ROUND_CLOSEST(clk_rate, samplerate);
switch (clk_div) {
case 320:
val = PDM_CLK_320FS;
break;
case 640:
val = PDM_CLK_640FS;
break;
case 1280:
val = PDM_CLK_1280FS;
break;
case 2560:
val = PDM_CLK_2560FS;
break;
case 5120:
val = PDM_CLK_5120FS;
break;
default:
dev_err(pdm->dev, "unsupported div: %d\n", clk_div);
return -EINVAL;
}
regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, PDM_DS_RATIO_MSK, val);
regmap_update_bits(pdm->regmap, PDM_HPF_CTRL,
PDM_HPF_CF_MSK, PDM_HPF_60HZ);
regmap_update_bits(pdm->regmap, PDM_HPF_CTRL,
PDM_HPF_LE | PDM_HPF_RE, PDM_HPF_LE | PDM_HPF_RE);
regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, PDM_CLK_EN, PDM_CLK_EN);
val = 0;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8:
val |= PDM_VDW(8);
break;
case SNDRV_PCM_FORMAT_S16_LE:
val |= PDM_VDW(16);
break;
case SNDRV_PCM_FORMAT_S20_3LE:
val |= PDM_VDW(20);
break;
case SNDRV_PCM_FORMAT_S24_LE:
val |= PDM_VDW(24);
break;
case SNDRV_PCM_FORMAT_S32_LE:
val |= PDM_VDW(32);
break;
default:
return -EINVAL;
}
switch (params_channels(params)) {
case 8:
val |= PDM_PATH3_EN;
/* fallthrough */
case 6:
val |= PDM_PATH2_EN;
/* fallthrough */
case 4:
val |= PDM_PATH1_EN;
/* fallthrough */
case 2:
val |= PDM_PATH0_EN;
break;
default:
dev_err(pdm->dev, "invalid channel: %d\n",
params_channels(params));
return -EINVAL;
}
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
regmap_update_bits(pdm->regmap, PDM_CTRL0,
PDM_PATH_MSK | PDM_VDW_MSK,
val);
regmap_update_bits(pdm->regmap, PDM_DMA_CTRL, PDM_DMA_RDL_MSK,
PDM_DMA_RDL(16));
regmap_update_bits(pdm->regmap, PDM_SYSCONFIG,
PDM_RX_MASK | PDM_RX_CLR_MASK,
PDM_RX_STOP | PDM_RX_CLR_WR);
}
return 0;
}
static int rockchip_pdm_set_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
struct rk_pdm_dev *pdm = to_info(cpu_dai);
unsigned int mask = 0, val = 0;
mask = PDM_CKP_MSK;
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
val = PDM_CKP_NORMAL;
break;
case SND_SOC_DAIFMT_IB_NF:
val = PDM_CKP_INVERTED;
break;
default:
return -EINVAL;
}
regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, mask, val);
return 0;
}
static int rockchip_pdm_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct rk_pdm_dev *pdm = to_info(dai);
int ret = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
rockchip_pdm_rxctrl(pdm, 1);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
rockchip_pdm_rxctrl(pdm, 0);
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static int rockchip_pdm_dai_probe(struct snd_soc_dai *dai)
{
struct rk_pdm_dev *pdm = to_info(dai);
dai->capture_dma_data = &pdm->capture_dma_data;
return 0;
}
static struct snd_soc_dai_ops rockchip_pdm_dai_ops = {
.set_fmt = rockchip_pdm_set_fmt,
.trigger = rockchip_pdm_trigger,
.hw_params = rockchip_pdm_hw_params,
};
#define ROCKCHIP_PDM_RATES SNDRV_PCM_RATE_8000_192000
#define ROCKCHIP_PDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_driver rockchip_pdm_dai = {
.probe = rockchip_pdm_dai_probe,
.capture = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 8,
.rates = ROCKCHIP_PDM_RATES,
.formats = ROCKCHIP_PDM_FORMATS,
},
.ops = &rockchip_pdm_dai_ops,
.symmetric_rates = 1,
};
static const struct snd_soc_component_driver rockchip_pdm_component = {
.name = "rockchip-pdm",
};
static int rockchip_pdm_runtime_suspend(struct device *dev)
{
struct rk_pdm_dev *pdm = dev_get_drvdata(dev);
clk_disable_unprepare(pdm->clk);
clk_disable_unprepare(pdm->hclk);
return 0;
}
static int rockchip_pdm_runtime_resume(struct device *dev)
{
struct rk_pdm_dev *pdm = dev_get_drvdata(dev);
int ret;
ret = clk_prepare_enable(pdm->clk);
if (ret) {
dev_err(pdm->dev, "clock enable failed %d\n", ret);
return ret;
}
ret = clk_prepare_enable(pdm->hclk);
if (ret) {
dev_err(pdm->dev, "hclock enable failed %d\n", ret);
return ret;
}
return 0;
}
static bool rockchip_pdm_wr_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case PDM_SYSCONFIG:
case PDM_CTRL0:
case PDM_CTRL1:
case PDM_CLK_CTRL:
case PDM_HPF_CTRL:
case PDM_FIFO_CTRL:
case PDM_DMA_CTRL:
case PDM_INT_EN:
case PDM_INT_CLR:
case PDM_DATA_VALID:
return true;
default:
return false;
}
}
static bool rockchip_pdm_rd_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case PDM_SYSCONFIG:
case PDM_CTRL0:
case PDM_CTRL1:
case PDM_CLK_CTRL:
case PDM_HPF_CTRL:
case PDM_FIFO_CTRL:
case PDM_DMA_CTRL:
case PDM_INT_EN:
case PDM_INT_CLR:
case PDM_INT_ST:
case PDM_DATA_VALID:
case PDM_VERSION:
return true;
default:
return false;
}
}
static bool rockchip_pdm_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case PDM_SYSCONFIG:
case PDM_INT_CLR:
case PDM_INT_ST:
return true;
default:
return false;
}
}
static const struct regmap_config rockchip_pdm_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = PDM_VERSION,
.writeable_reg = rockchip_pdm_wr_reg,
.readable_reg = rockchip_pdm_rd_reg,
.volatile_reg = rockchip_pdm_volatile_reg,
.cache_type = REGCACHE_FLAT,
};
static int rockchip_pdm_probe(struct platform_device *pdev)
{
struct rk_pdm_dev *pdm;
struct resource *res;
void __iomem *regs;
int ret;
pdm = devm_kzalloc(&pdev->dev, sizeof(*pdm), GFP_KERNEL);
if (!pdm)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(regs))
return PTR_ERR(regs);
pdm->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
&rockchip_pdm_regmap_config);
if (IS_ERR(pdm->regmap))
return PTR_ERR(pdm->regmap);
pdm->capture_dma_data.addr = res->start + PDM_RXFIFO_DATA;
pdm->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
pdm->capture_dma_data.maxburst = PDM_DMA_BURST_SIZE;
pdm->dev = &pdev->dev;
dev_set_drvdata(&pdev->dev, pdm);
pdm->clk = devm_clk_get(&pdev->dev, "pdm_clk");
if (IS_ERR(pdm->clk))
return PTR_ERR(pdm->clk);
pdm->hclk = devm_clk_get(&pdev->dev, "pdm_hclk");
if (IS_ERR(pdm->hclk))
return PTR_ERR(pdm->hclk);
ret = clk_prepare_enable(pdm->hclk);
if (ret)
return ret;
pm_runtime_enable(&pdev->dev);
if (!pm_runtime_enabled(&pdev->dev)) {
ret = rockchip_pdm_runtime_resume(&pdev->dev);
if (ret)
goto err_pm_disable;
}
ret = devm_snd_soc_register_component(&pdev->dev,
&rockchip_pdm_component,
&rockchip_pdm_dai, 1);
if (ret) {
dev_err(&pdev->dev, "could not register dai: %d\n", ret);
goto err_suspend;
}
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
if (ret) {
dev_err(&pdev->dev, "could not register pcm: %d\n", ret);
goto err_suspend;
}
return 0;
err_suspend:
if (!pm_runtime_status_suspended(&pdev->dev))
rockchip_pdm_runtime_suspend(&pdev->dev);
err_pm_disable:
pm_runtime_disable(&pdev->dev);
clk_disable_unprepare(pdm->hclk);
return ret;
}
static int rockchip_pdm_remove(struct platform_device *pdev)
{
struct rk_pdm_dev *pdm = dev_get_drvdata(&pdev->dev);
pm_runtime_disable(&pdev->dev);
if (!pm_runtime_status_suspended(&pdev->dev))
rockchip_pdm_runtime_suspend(&pdev->dev);
clk_disable_unprepare(pdm->clk);
clk_disable_unprepare(pdm->hclk);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int rockchip_pdm_suspend(struct device *dev)
{
struct rk_pdm_dev *pdm = dev_get_drvdata(dev);
regcache_mark_dirty(pdm->regmap);
return 0;
}
static int rockchip_pdm_resume(struct device *dev)
{
struct rk_pdm_dev *pdm = dev_get_drvdata(dev);
int ret;
ret = pm_runtime_get_sync(dev);
if (ret < 0)
return ret;
ret = regcache_sync(pdm->regmap);
pm_runtime_put(dev);
return ret;
}
#endif
static const struct dev_pm_ops rockchip_pdm_pm_ops = {
SET_RUNTIME_PM_OPS(rockchip_pdm_runtime_suspend,
rockchip_pdm_runtime_resume, NULL)
SET_SYSTEM_SLEEP_PM_OPS(rockchip_pdm_suspend, rockchip_pdm_resume)
};
static const struct of_device_id rockchip_pdm_match[] = {
{ .compatible = "rockchip,pdm", },
{},
};
MODULE_DEVICE_TABLE(of, rockchip_pdm_match);
static struct platform_driver rockchip_pdm_driver = {
.probe = rockchip_pdm_probe,
.remove = rockchip_pdm_remove,
.driver = {
.name = "rockchip-pdm",
.of_match_table = of_match_ptr(rockchip_pdm_match),
.pm = &rockchip_pdm_pm_ops,
},
};
module_platform_driver(rockchip_pdm_driver);
MODULE_AUTHOR("Sugar <sugar.zhang@rock-chips.com>");
MODULE_DESCRIPTION("Rockchip PDM Controller Driver");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,83 @@
/*
* Rockchip PDM ALSA SoC Digital Audio Interface(DAI) driver
*
* Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef _ROCKCHIP_PDM_H
#define _ROCKCHIP_PDM_H
/* PDM REGS */
#define PDM_SYSCONFIG (0x0000)
#define PDM_CTRL0 (0x0004)
#define PDM_CTRL1 (0x0008)
#define PDM_CLK_CTRL (0x000c)
#define PDM_HPF_CTRL (0x0010)
#define PDM_FIFO_CTRL (0x0014)
#define PDM_DMA_CTRL (0x0018)
#define PDM_INT_EN (0x001c)
#define PDM_INT_CLR (0x0020)
#define PDM_INT_ST (0x0024)
#define PDM_RXFIFO_DATA (0x0030)
#define PDM_DATA_VALID (0x0054)
#define PDM_VERSION (0x0058)
/* PDM_SYSCONFIG */
#define PDM_RX_MASK (0x1 << 2)
#define PDM_RX_START (0x1 << 2)
#define PDM_RX_STOP (0x0 << 2)
#define PDM_RX_CLR_MASK (0x1 << 0)
#define PDM_RX_CLR_WR (0x1 << 0)
#define PDM_RX_CLR_DONE (0x0 << 0)
/* PDM CTRL0 */
#define PDM_PATH_MSK (0xf << 27)
#define PDM_PATH3_EN BIT(30)
#define PDM_PATH2_EN BIT(29)
#define PDM_PATH1_EN BIT(28)
#define PDM_PATH0_EN BIT(27)
#define PDM_HWT_EN BIT(26)
#define PDM_VDW_MSK (0x1f << 0)
#define PDM_VDW(X) ((X - 1) << 0)
/* PDM CLK CTRL */
#define PDM_CLK_MSK BIT(5)
#define PDM_CLK_EN BIT(5)
#define PDM_CLK_DIS (0x0 << 5)
#define PDM_CKP_MSK BIT(3)
#define PDM_CKP_NORMAL (0x0 << 3)
#define PDM_CKP_INVERTED BIT(3)
#define PDM_DS_RATIO_MSK (0x7 << 0)
#define PDM_CLK_320FS (0x0 << 0)
#define PDM_CLK_640FS (0x1 << 0)
#define PDM_CLK_1280FS (0x2 << 0)
#define PDM_CLK_2560FS (0x3 << 0)
#define PDM_CLK_5120FS (0x4 << 0)
/* PDM HPF CTRL */
#define PDM_HPF_LE BIT(3)
#define PDM_HPF_RE BIT(2)
#define PDM_HPF_CF_MSK (0x3 << 0)
#define PDM_HPF_3P79HZ (0x0 << 0)
#define PDM_HPF_60HZ (0x1 << 0)
#define PDM_HPF_243HZ (0x2 << 0)
#define PDM_HPF_493HZ (0x3 << 0)
/* PDM DMA CTRL */
#define PDM_DMA_RD_MSK BIT(8)
#define PDM_DMA_RD_EN BIT(8)
#define PDM_DMA_RD_DIS (0x0 << 8)
#define PDM_DMA_RDL_MSK (0x7f << 0)
#define PDM_DMA_RDL(X) ((X - 1) << 0)
#endif /* _ROCKCHIP_PDM_H */

View File

@ -49,8 +49,12 @@ static const struct of_device_id rk_spdif_match[] = {
.data = (void *)RK_SPDIF_RK3066 },
{ .compatible = "rockchip,rk3188-spdif",
.data = (void *)RK_SPDIF_RK3188 },
{ .compatible = "rockchip,rk3228-spdif",
.data = (void *)RK_SPDIF_RK3366 },
{ .compatible = "rockchip,rk3288-spdif",
.data = (void *)RK_SPDIF_RK3288 },
{ .compatible = "rockchip,rk3328-spdif",
.data = (void *)RK_SPDIF_RK3366 },
{ .compatible = "rockchip,rk3366-spdif",
.data = (void *)RK_SPDIF_RK3366 },
{ .compatible = "rockchip,rk3368-spdif",

View File

@ -44,7 +44,7 @@ struct s3c24xx_uda134x {
static unsigned int rates[33 * 2];
#ifdef ENFORCE_RATES
static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
static const struct snd_pcm_hw_constraint_list hw_constraints_rates = {
.count = ARRAY_SIZE(rates),
.list = rates,
.mask = 0,

View File

@ -301,7 +301,12 @@ struct fsi_master {
spinlock_t lock;
};
static int fsi_stream_is_play(struct fsi_priv *fsi, struct fsi_stream *io);
static inline int fsi_stream_is_play(struct fsi_priv *fsi,
struct fsi_stream *io)
{
return &fsi->playback == io;
}
/*
* basic read write function
@ -489,12 +494,6 @@ static void fsi_count_fifo_err(struct fsi_priv *fsi)
/*
* fsi_stream_xx() function
*/
static inline int fsi_stream_is_play(struct fsi_priv *fsi,
struct fsi_stream *io)
{
return &fsi->playback == io;
}
static inline struct fsi_stream *fsi_stream_get(struct fsi_priv *fsi,
struct snd_pcm_substream *substream)
{

View File

@ -352,6 +352,17 @@ static int soc_tplg_widget_load(struct soc_tplg *tplg,
return 0;
}
/* optionally pass new dynamic widget to component driver. This is mainly for
* external widgets where we can assign private data/ops */
static int soc_tplg_widget_ready(struct soc_tplg *tplg,
struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w)
{
if (tplg->comp && tplg->ops && tplg->ops->widget_ready)
return tplg->ops->widget_ready(tplg->comp, w, tplg_w);
return 0;
}
/* pass DAI configurations to component driver for extra initialization */
static int soc_tplg_dai_load(struct soc_tplg *tplg,
struct snd_soc_dai_driver *dai_drv)
@ -1160,7 +1171,8 @@ static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg,
return -EINVAL;
}
dev_dbg(tplg->dev, "ASoC: adding %d DAPM routes\n", count);
dev_dbg(tplg->dev, "ASoC: adding %d DAPM routes for index %d\n", count,
hdr->index);
for (i = 0; i < count; i++) {
elem = (struct snd_soc_tplg_dapm_graph_elem *)tplg->pos;
@ -1473,6 +1485,7 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg,
if (template.id < 0)
return template.id;
/* strings are allocated here, but used and freed by the widget */
template.name = kstrdup(w->name, GFP_KERNEL);
if (!template.name)
return -ENOMEM;
@ -1585,11 +1598,17 @@ widget:
widget->dobj.widget.kcontrol_type = kcontrol_type;
widget->dobj.ops = tplg->ops;
widget->dobj.index = tplg->index;
kfree(template.sname);
kfree(template.name);
list_add(&widget->dobj.list, &tplg->comp->dobj_list);
ret = soc_tplg_widget_ready(tplg, widget, w);
if (ret < 0)
goto ready_err;
return 0;
ready_err:
snd_soc_tplg_widget_remove(widget);
snd_soc_dapm_free_widget(widget);
hdr_err:
kfree(template.sname);
err:
@ -1636,7 +1655,7 @@ static int soc_tplg_dapm_complete(struct soc_tplg *tplg)
*/
if (!card || !card->instantiated) {
dev_warn(tplg->dev, "ASoC: Parent card not yet available,"
"Do not add new widgets now\n");
" widget card binding deferred\n");
return 0;
}
@ -2371,7 +2390,7 @@ static int soc_tplg_load_header(struct soc_tplg *tplg,
/* check for matching ID */
if (hdr->index != tplg->req_index &&
hdr->index != SND_SOC_TPLG_INDEX_ALL)
tplg->req_index != SND_SOC_TPLG_INDEX_ALL)
return 0;
tplg->index = hdr->index;

View File

@ -1,8 +1,31 @@
menuconfig SND_SOC_STM32
tristate "STMicroelectronics STM32 SOC audio support"
menu "STMicroelectronics STM32 SOC audio support"
config SND_SOC_STM32_SAI
tristate "STM32 SAI interface (Serial Audio Interface) support"
depends on ARCH_STM32 || COMPILE_TEST
depends on SND_SOC
select SND_SOC_GENERIC_DMAENGINE_PCM
select REGMAP_MMIO
help
Say Y if you want to enable ASoC-support for STM32
Say Y if you want to enable SAI for STM32
config SND_SOC_STM32_I2S
tristate "STM32 I2S interface (SPI/I2S block) support"
depends on ARCH_STM32 || COMPILE_TEST
depends on SND_SOC
select SND_SOC_GENERIC_DMAENGINE_PCM
select REGMAP_MMIO
help
Say Y if you want to enable I2S for STM32
config SND_SOC_STM32_SPDIFRX
tristate "STM32 S/PDIF receiver (SPDIFRX) support"
depends on ARCH_STM32 || COMPILE_TEST
depends on SND_SOC
select SND_SOC_GENERIC_DMAENGINE_PCM
select REGMAP_MMIO
select SND_SOC_SPDIF
help
Say Y if you want to enable S/PDIF capture for STM32
endmenu

View File

@ -1,6 +1,14 @@
# SAI
snd-soc-stm32-sai-sub-objs := stm32_sai_sub.o
obj-$(CONFIG_SND_SOC_STM32) += snd-soc-stm32-sai-sub.o
obj-$(CONFIG_SND_SOC_STM32_SAI) += snd-soc-stm32-sai-sub.o
snd-soc-stm32-sai-objs := stm32_sai.o
obj-$(CONFIG_SND_SOC_STM32) += snd-soc-stm32-sai.o
obj-$(CONFIG_SND_SOC_STM32_SAI) += snd-soc-stm32-sai.o
# I2S
snd-soc-stm32-i2s-objs := stm32_i2s.o
obj-$(CONFIG_SND_SOC_STM32_I2S) += snd-soc-stm32-i2s.o
# SPDIFRX
snd-soc-stm32-spdifrx-objs := stm32_spdifrx.o
obj-$(CONFIG_SND_SOC_STM32_SPDIFRX) += snd-soc-stm32-spdifrx.o

946
sound/soc/stm/stm32_i2s.c Normal file
View File

@ -0,0 +1,946 @@
/*
* STM32 ALSA SoC Digital Audio Interface (I2S) driver.
*
* Copyright (C) 2017, STMicroelectronics - All Rights Reserved
* Author(s): Olivier Moysan <olivier.moysan@st.com> for STMicroelectronics.
*
* License terms: GPL V2.0.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/spinlock.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h>
#define STM32_I2S_CR1_REG 0x0
#define STM32_I2S_CFG1_REG 0x08
#define STM32_I2S_CFG2_REG 0x0C
#define STM32_I2S_IER_REG 0x10
#define STM32_I2S_SR_REG 0x14
#define STM32_I2S_IFCR_REG 0x18
#define STM32_I2S_TXDR_REG 0X20
#define STM32_I2S_RXDR_REG 0x30
#define STM32_I2S_CGFR_REG 0X50
/* Bit definition for SPI2S_CR1 register */
#define I2S_CR1_SPE BIT(0)
#define I2S_CR1_CSTART BIT(9)
#define I2S_CR1_CSUSP BIT(10)
#define I2S_CR1_HDDIR BIT(11)
#define I2S_CR1_SSI BIT(12)
#define I2S_CR1_CRC33_17 BIT(13)
#define I2S_CR1_RCRCI BIT(14)
#define I2S_CR1_TCRCI BIT(15)
/* Bit definition for SPI_CFG2 register */
#define I2S_CFG2_IOSWP_SHIFT 15
#define I2S_CFG2_IOSWP BIT(I2S_CFG2_IOSWP_SHIFT)
#define I2S_CFG2_LSBFRST BIT(23)
#define I2S_CFG2_AFCNTR BIT(31)
/* Bit definition for SPI_CFG1 register */
#define I2S_CFG1_FTHVL_SHIFT 5
#define I2S_CFG1_FTHVL_MASK GENMASK(8, I2S_CFG1_FTHVL_SHIFT)
#define I2S_CFG1_FTHVL_SET(x) ((x) << I2S_CFG1_FTHVL_SHIFT)
#define I2S_CFG1_TXDMAEN BIT(15)
#define I2S_CFG1_RXDMAEN BIT(14)
/* Bit definition for SPI2S_IER register */
#define I2S_IER_RXPIE BIT(0)
#define I2S_IER_TXPIE BIT(1)
#define I2S_IER_DPXPIE BIT(2)
#define I2S_IER_EOTIE BIT(3)
#define I2S_IER_TXTFIE BIT(4)
#define I2S_IER_UDRIE BIT(5)
#define I2S_IER_OVRIE BIT(6)
#define I2S_IER_CRCEIE BIT(7)
#define I2S_IER_TIFREIE BIT(8)
#define I2S_IER_MODFIE BIT(9)
#define I2S_IER_TSERFIE BIT(10)
/* Bit definition for SPI2S_SR register */
#define I2S_SR_RXP BIT(0)
#define I2S_SR_TXP BIT(1)
#define I2S_SR_DPXP BIT(2)
#define I2S_SR_EOT BIT(3)
#define I2S_SR_TXTF BIT(4)
#define I2S_SR_UDR BIT(5)
#define I2S_SR_OVR BIT(6)
#define I2S_SR_CRCERR BIT(7)
#define I2S_SR_TIFRE BIT(8)
#define I2S_SR_MODF BIT(9)
#define I2S_SR_TSERF BIT(10)
#define I2S_SR_SUSP BIT(11)
#define I2S_SR_TXC BIT(12)
#define I2S_SR_RXPLVL GENMASK(14, 13)
#define I2S_SR_RXWNE BIT(15)
#define I2S_SR_MASK GENMASK(15, 0)
/* Bit definition for SPI_IFCR register */
#define I2S_IFCR_EOTC BIT(3)
#define I2S_IFCR_TXTFC BIT(4)
#define I2S_IFCR_UDRC BIT(5)
#define I2S_IFCR_OVRC BIT(6)
#define I2S_IFCR_CRCEC BIT(7)
#define I2S_IFCR_TIFREC BIT(8)
#define I2S_IFCR_MODFC BIT(9)
#define I2S_IFCR_TSERFC BIT(10)
#define I2S_IFCR_SUSPC BIT(11)
#define I2S_IFCR_MASK GENMASK(11, 3)
/* Bit definition for SPI_I2SCGFR register */
#define I2S_CGFR_I2SMOD BIT(0)
#define I2S_CGFR_I2SCFG_SHIFT 1
#define I2S_CGFR_I2SCFG_MASK GENMASK(3, I2S_CGFR_I2SCFG_SHIFT)
#define I2S_CGFR_I2SCFG_SET(x) ((x) << I2S_CGFR_I2SCFG_SHIFT)
#define I2S_CGFR_I2SSTD_SHIFT 4
#define I2S_CGFR_I2SSTD_MASK GENMASK(5, I2S_CGFR_I2SSTD_SHIFT)
#define I2S_CGFR_I2SSTD_SET(x) ((x) << I2S_CGFR_I2SSTD_SHIFT)
#define I2S_CGFR_PCMSYNC BIT(7)
#define I2S_CGFR_DATLEN_SHIFT 8
#define I2S_CGFR_DATLEN_MASK GENMASK(9, I2S_CGFR_DATLEN_SHIFT)
#define I2S_CGFR_DATLEN_SET(x) ((x) << I2S_CGFR_DATLEN_SHIFT)
#define I2S_CGFR_CHLEN_SHIFT 10
#define I2S_CGFR_CHLEN BIT(I2S_CGFR_CHLEN_SHIFT)
#define I2S_CGFR_CKPOL BIT(11)
#define I2S_CGFR_FIXCH BIT(12)
#define I2S_CGFR_WSINV BIT(13)
#define I2S_CGFR_DATFMT BIT(14)
#define I2S_CGFR_I2SDIV_SHIFT 16
#define I2S_CGFR_I2SDIV_BIT_H 23
#define I2S_CGFR_I2SDIV_MASK GENMASK(I2S_CGFR_I2SDIV_BIT_H,\
I2S_CGFR_I2SDIV_SHIFT)
#define I2S_CGFR_I2SDIV_SET(x) ((x) << I2S_CGFR_I2SDIV_SHIFT)
#define I2S_CGFR_I2SDIV_MAX ((1 << (I2S_CGFR_I2SDIV_BIT_H -\
I2S_CGFR_I2SDIV_SHIFT)) - 1)
#define I2S_CGFR_ODD_SHIFT 24
#define I2S_CGFR_ODD BIT(I2S_CGFR_ODD_SHIFT)
#define I2S_CGFR_MCKOE BIT(25)
enum i2s_master_mode {
I2S_MS_NOT_SET,
I2S_MS_MASTER,
I2S_MS_SLAVE,
};
enum i2s_mode {
I2S_I2SMOD_TX_SLAVE,
I2S_I2SMOD_RX_SLAVE,
I2S_I2SMOD_TX_MASTER,
I2S_I2SMOD_RX_MASTER,
I2S_I2SMOD_FD_SLAVE,
I2S_I2SMOD_FD_MASTER,
};
enum i2s_fifo_th {
I2S_FIFO_TH_NONE,
I2S_FIFO_TH_ONE_QUARTER,
I2S_FIFO_TH_HALF,
I2S_FIFO_TH_THREE_QUARTER,
I2S_FIFO_TH_FULL,
};
enum i2s_std {
I2S_STD_I2S,
I2S_STD_LEFT_J,
I2S_STD_RIGHT_J,
I2S_STD_DSP,
};
enum i2s_datlen {
I2S_I2SMOD_DATLEN_16,
I2S_I2SMOD_DATLEN_24,
I2S_I2SMOD_DATLEN_32,
};
#define STM32_I2S_DAI_NAME_SIZE 20
#define STM32_I2S_FIFO_SIZE 16
#define STM32_I2S_IS_MASTER(x) ((x)->ms_flg == I2S_MS_MASTER)
#define STM32_I2S_IS_SLAVE(x) ((x)->ms_flg == I2S_MS_SLAVE)
/**
* @regmap_conf: I2S register map configuration pointer
* @egmap: I2S register map pointer
* @pdev: device data pointer
* @dai_drv: DAI driver pointer
* @dma_data_tx: dma configuration data for tx channel
* @dma_data_rx: dma configuration data for tx channel
* @substream: PCM substream data pointer
* @i2sclk: kernel clock feeding the I2S clock generator
* @pclk: peripheral clock driving bus interface
* @x8kclk: I2S parent clock for sampling frequencies multiple of 8kHz
* @x11kclk: I2S parent clock for sampling frequencies multiple of 11kHz
* @base: mmio register base virtual address
* @phys_addr: I2S registers physical base address
* @lock_fd: lock to manage race conditions in full duplex mode
* @dais_name: DAI name
* @mclk_rate: master clock frequency (Hz)
* @fmt: DAI protocol
* @refcount: keep count of opened streams on I2S
* @ms_flg: master mode flag.
*/
struct stm32_i2s_data {
const struct regmap_config *regmap_conf;
struct regmap *regmap;
struct platform_device *pdev;
struct snd_soc_dai_driver *dai_drv;
struct snd_dmaengine_dai_dma_data dma_data_tx;
struct snd_dmaengine_dai_dma_data dma_data_rx;
struct snd_pcm_substream *substream;
struct clk *i2sclk;
struct clk *pclk;
struct clk *x8kclk;
struct clk *x11kclk;
void __iomem *base;
dma_addr_t phys_addr;
spinlock_t lock_fd; /* Manage race conditions for full duplex */
char dais_name[STM32_I2S_DAI_NAME_SIZE];
unsigned int mclk_rate;
unsigned int fmt;
int refcount;
int ms_flg;
};
static irqreturn_t stm32_i2s_isr(int irq, void *devid)
{
struct stm32_i2s_data *i2s = (struct stm32_i2s_data *)devid;
struct platform_device *pdev = i2s->pdev;
u32 sr, ier;
unsigned long flags;
int err = 0;
regmap_read(i2s->regmap, STM32_I2S_SR_REG, &sr);
regmap_read(i2s->regmap, STM32_I2S_IER_REG, &ier);
flags = sr & ier;
if (!flags) {
dev_dbg(&pdev->dev, "Spurious IRQ sr=0x%08x, ier=0x%08x\n",
sr, ier);
return IRQ_NONE;
}
regmap_update_bits(i2s->regmap, STM32_I2S_IFCR_REG,
I2S_IFCR_MASK, flags);
if (flags & I2S_SR_OVR) {
dev_dbg(&pdev->dev, "Overrun\n");
err = 1;
}
if (flags & I2S_SR_UDR) {
dev_dbg(&pdev->dev, "Underrun\n");
err = 1;
}
if (flags & I2S_SR_TIFRE)
dev_dbg(&pdev->dev, "Frame error\n");
if (err)
snd_pcm_stop_xrun(i2s->substream);
return IRQ_HANDLED;
}
static bool stm32_i2s_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case STM32_I2S_CR1_REG:
case STM32_I2S_CFG1_REG:
case STM32_I2S_CFG2_REG:
case STM32_I2S_IER_REG:
case STM32_I2S_SR_REG:
case STM32_I2S_IFCR_REG:
case STM32_I2S_TXDR_REG:
case STM32_I2S_RXDR_REG:
case STM32_I2S_CGFR_REG:
return true;
default:
return false;
}
}
static bool stm32_i2s_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case STM32_I2S_TXDR_REG:
case STM32_I2S_RXDR_REG:
return true;
default:
return false;
}
}
static bool stm32_i2s_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case STM32_I2S_CR1_REG:
case STM32_I2S_CFG1_REG:
case STM32_I2S_CFG2_REG:
case STM32_I2S_IER_REG:
case STM32_I2S_IFCR_REG:
case STM32_I2S_TXDR_REG:
case STM32_I2S_CGFR_REG:
return true;
default:
return false;
}
}
static int stm32_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
{
struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai);
u32 cgfr;
u32 cgfr_mask = I2S_CGFR_I2SSTD_MASK | I2S_CGFR_CKPOL |
I2S_CGFR_WSINV | I2S_CGFR_I2SCFG_MASK;
dev_dbg(cpu_dai->dev, "fmt %x\n", fmt);
/*
* winv = 0 : default behavior (high/low) for all standards
* ckpol = 0 for all standards.
*/
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
cgfr = I2S_CGFR_I2SSTD_SET(I2S_STD_I2S);
break;
case SND_SOC_DAIFMT_MSB:
cgfr = I2S_CGFR_I2SSTD_SET(I2S_STD_LEFT_J);
break;
case SND_SOC_DAIFMT_LSB:
cgfr = I2S_CGFR_I2SSTD_SET(I2S_STD_RIGHT_J);
break;
case SND_SOC_DAIFMT_DSP_A:
cgfr = I2S_CGFR_I2SSTD_SET(I2S_STD_DSP);
break;
/* DSP_B not mapped on I2S PCM long format. 1 bit offset does not fit */
default:
dev_err(cpu_dai->dev, "Unsupported protocol %#x\n",
fmt & SND_SOC_DAIFMT_FORMAT_MASK);
return -EINVAL;
}
/* DAI clock strobing */
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
case SND_SOC_DAIFMT_IB_NF:
cgfr |= I2S_CGFR_CKPOL;
break;
case SND_SOC_DAIFMT_NB_IF:
cgfr |= I2S_CGFR_WSINV;
break;
case SND_SOC_DAIFMT_IB_IF:
cgfr |= I2S_CGFR_CKPOL;
cgfr |= I2S_CGFR_WSINV;
break;
default:
dev_err(cpu_dai->dev, "Unsupported strobing %#x\n",
fmt & SND_SOC_DAIFMT_INV_MASK);
return -EINVAL;
}
/* DAI clock master masks */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
i2s->ms_flg = I2S_MS_SLAVE;
break;
case SND_SOC_DAIFMT_CBS_CFS:
i2s->ms_flg = I2S_MS_MASTER;
break;
default:
dev_err(cpu_dai->dev, "Unsupported mode %#x\n",
fmt & SND_SOC_DAIFMT_MASTER_MASK);
return -EINVAL;
}
i2s->fmt = fmt;
return regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG,
cgfr_mask, cgfr);
}
static int stm32_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
int clk_id, unsigned int freq, int dir)
{
struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai);
dev_dbg(cpu_dai->dev, "I2S MCLK frequency is %uHz\n", freq);
if ((dir == SND_SOC_CLOCK_OUT) && STM32_I2S_IS_MASTER(i2s)) {
i2s->mclk_rate = freq;
/* Enable master clock if master mode and mclk-fs are set */
return regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG,
I2S_CGFR_MCKOE, I2S_CGFR_MCKOE);
}
return 0;
}
static int stm32_i2s_configure_clock(struct snd_soc_dai *cpu_dai,
struct snd_pcm_hw_params *params)
{
struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai);
unsigned long i2s_clock_rate;
unsigned int tmp, div, real_div, nb_bits, frame_len;
unsigned int rate = params_rate(params);
int ret;
u32 cgfr, cgfr_mask;
bool odd;
if (!(rate % 11025))
clk_set_parent(i2s->i2sclk, i2s->x11kclk);
else
clk_set_parent(i2s->i2sclk, i2s->x8kclk);
i2s_clock_rate = clk_get_rate(i2s->i2sclk);
/*
* mckl = mclk_ratio x ws
* i2s mode : mclk_ratio = 256
* dsp mode : mclk_ratio = 128
*
* mclk on
* i2s mode : div = i2s_clk / (mclk_ratio * ws)
* dsp mode : div = i2s_clk / (mclk_ratio * ws)
* mclk off
* i2s mode : div = i2s_clk / (nb_bits x ws)
* dsp mode : div = i2s_clk / (nb_bits x ws)
*/
if (i2s->mclk_rate) {
tmp = DIV_ROUND_CLOSEST(i2s_clock_rate, i2s->mclk_rate);
} else {
frame_len = 32;
if ((i2s->fmt & SND_SOC_DAIFMT_FORMAT_MASK) ==
SND_SOC_DAIFMT_DSP_A)
frame_len = 16;
/* master clock not enabled */
ret = regmap_read(i2s->regmap, STM32_I2S_CGFR_REG, &cgfr);
if (ret < 0)
return ret;
nb_bits = frame_len * ((cgfr & I2S_CGFR_CHLEN) + 1);
tmp = DIV_ROUND_CLOSEST(i2s_clock_rate, (nb_bits * rate));
}
/* Check the parity of the divider */
odd = tmp & 0x1;
/* Compute the div prescaler */
div = tmp >> 1;
cgfr = I2S_CGFR_I2SDIV_SET(div) | (odd << I2S_CGFR_ODD_SHIFT);
cgfr_mask = I2S_CGFR_I2SDIV_MASK | I2S_CGFR_ODD;
real_div = ((2 * div) + odd);
dev_dbg(cpu_dai->dev, "I2S clk: %ld, SCLK: %d\n",
i2s_clock_rate, rate);
dev_dbg(cpu_dai->dev, "Divider: 2*%d(div)+%d(odd) = %d\n",
div, odd, real_div);
if (((div == 1) && odd) || (div > I2S_CGFR_I2SDIV_MAX)) {
dev_err(cpu_dai->dev, "Wrong divider setting\n");
return -EINVAL;
}
if (!div && !odd)
dev_warn(cpu_dai->dev, "real divider forced to 1\n");
ret = regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG,
cgfr_mask, cgfr);
if (ret < 0)
return ret;
/* Set bitclock and frameclock to their inactive state */
return regmap_update_bits(i2s->regmap, STM32_I2S_CFG2_REG,
I2S_CFG2_AFCNTR, I2S_CFG2_AFCNTR);
}
static int stm32_i2s_configure(struct snd_soc_dai *cpu_dai,
struct snd_pcm_hw_params *params,
struct snd_pcm_substream *substream)
{
struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai);
int format = params_width(params);
u32 cfgr, cfgr_mask, cfg1, cfg1_mask;
unsigned int fthlv;
int ret;
if ((params_channels(params) == 1) &&
((i2s->fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_DSP_A)) {
dev_err(cpu_dai->dev, "Mono mode supported only by DSP_A\n");
return -EINVAL;
}
switch (format) {
case 16:
cfgr = I2S_CGFR_DATLEN_SET(I2S_I2SMOD_DATLEN_16);
cfgr_mask = I2S_CGFR_DATLEN_MASK;
break;
case 32:
cfgr = I2S_CGFR_DATLEN_SET(I2S_I2SMOD_DATLEN_32) |
I2S_CGFR_CHLEN;
cfgr_mask = I2S_CGFR_DATLEN_MASK | I2S_CGFR_CHLEN;
break;
default:
dev_err(cpu_dai->dev, "Unexpected format %d", format);
return -EINVAL;
}
if (STM32_I2S_IS_SLAVE(i2s)) {
cfgr |= I2S_CGFR_I2SCFG_SET(I2S_I2SMOD_FD_SLAVE);
/* As data length is either 16 or 32 bits, fixch always set */
cfgr |= I2S_CGFR_FIXCH;
cfgr_mask |= I2S_CGFR_FIXCH;
} else {
cfgr |= I2S_CGFR_I2SCFG_SET(I2S_I2SMOD_FD_MASTER);
}
cfgr_mask |= I2S_CGFR_I2SCFG_MASK;
ret = regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG,
cfgr_mask, cfgr);
if (ret < 0)
return ret;
cfg1 = I2S_CFG1_RXDMAEN | I2S_CFG1_TXDMAEN;
cfg1_mask = cfg1;
fthlv = STM32_I2S_FIFO_SIZE * I2S_FIFO_TH_ONE_QUARTER / 4;
cfg1 |= I2S_CFG1_FTHVL_SET(fthlv - 1);
cfg1_mask |= I2S_CFG1_FTHVL_MASK;
return regmap_update_bits(i2s->regmap, STM32_I2S_CFG1_REG,
cfg1_mask, cfg1);
}
static int stm32_i2s_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai);
i2s->substream = substream;
spin_lock(&i2s->lock_fd);
i2s->refcount++;
spin_unlock(&i2s->lock_fd);
return regmap_update_bits(i2s->regmap, STM32_I2S_IFCR_REG,
I2S_IFCR_MASK, I2S_IFCR_MASK);
}
static int stm32_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *cpu_dai)
{
struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai);
int ret;
ret = stm32_i2s_configure(cpu_dai, params, substream);
if (ret < 0) {
dev_err(cpu_dai->dev, "Configuration returned error %d\n", ret);
return ret;
}
if (STM32_I2S_IS_MASTER(i2s))
ret = stm32_i2s_configure_clock(cpu_dai, params);
return ret;
}
static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *cpu_dai)
{
struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai);
bool playback_flg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
u32 cfg1_mask, ier;
int ret;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
/* Enable i2s */
dev_dbg(cpu_dai->dev, "start I2S\n");
ret = regmap_update_bits(i2s->regmap, STM32_I2S_CR1_REG,
I2S_CR1_SPE, I2S_CR1_SPE);
if (ret < 0) {
dev_err(cpu_dai->dev, "Error %d enabling I2S\n", ret);
return ret;
}
ret = regmap_update_bits(i2s->regmap, STM32_I2S_CR1_REG,
I2S_CR1_CSTART, I2S_CR1_CSTART);
if (ret < 0) {
dev_err(cpu_dai->dev, "Error %d starting I2S\n", ret);
return ret;
}
regmap_update_bits(i2s->regmap, STM32_I2S_IFCR_REG,
I2S_IFCR_MASK, I2S_IFCR_MASK);
if (playback_flg) {
ier = I2S_IER_UDRIE;
} else {
ier = I2S_IER_OVRIE;
spin_lock(&i2s->lock_fd);
if (i2s->refcount == 1)
/* dummy write to trigger capture */
regmap_write(i2s->regmap,
STM32_I2S_TXDR_REG, 0);
spin_unlock(&i2s->lock_fd);
}
if (STM32_I2S_IS_SLAVE(i2s))
ier |= I2S_IER_TIFREIE;
regmap_update_bits(i2s->regmap, STM32_I2S_IER_REG, ier, ier);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (playback_flg)
regmap_update_bits(i2s->regmap, STM32_I2S_IER_REG,
I2S_IER_UDRIE,
(unsigned int)~I2S_IER_UDRIE);
else
regmap_update_bits(i2s->regmap, STM32_I2S_IER_REG,
I2S_IER_OVRIE,
(unsigned int)~I2S_IER_OVRIE);
spin_lock(&i2s->lock_fd);
i2s->refcount--;
if (i2s->refcount) {
spin_unlock(&i2s->lock_fd);
break;
}
spin_unlock(&i2s->lock_fd);
dev_dbg(cpu_dai->dev, "stop I2S\n");
ret = regmap_update_bits(i2s->regmap, STM32_I2S_CR1_REG,
I2S_CR1_SPE, 0);
if (ret < 0) {
dev_err(cpu_dai->dev, "Error %d disabling I2S\n", ret);
return ret;
}
cfg1_mask = I2S_CFG1_RXDMAEN | I2S_CFG1_TXDMAEN;
regmap_update_bits(i2s->regmap, STM32_I2S_CFG1_REG,
cfg1_mask, 0);
break;
default:
return -EINVAL;
}
return 0;
}
static void stm32_i2s_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai);
i2s->substream = NULL;
regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG,
I2S_CGFR_MCKOE, (unsigned int)~I2S_CGFR_MCKOE);
}
static int stm32_i2s_dai_probe(struct snd_soc_dai *cpu_dai)
{
struct stm32_i2s_data *i2s = dev_get_drvdata(cpu_dai->dev);
struct snd_dmaengine_dai_dma_data *dma_data_tx = &i2s->dma_data_tx;
struct snd_dmaengine_dai_dma_data *dma_data_rx = &i2s->dma_data_rx;
/* Buswidth will be set by framework */
dma_data_tx->addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED;
dma_data_tx->addr = (dma_addr_t)(i2s->phys_addr) + STM32_I2S_TXDR_REG;
dma_data_tx->maxburst = 1;
dma_data_rx->addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED;
dma_data_rx->addr = (dma_addr_t)(i2s->phys_addr) + STM32_I2S_RXDR_REG;
dma_data_rx->maxburst = 1;
snd_soc_dai_init_dma_data(cpu_dai, dma_data_tx, dma_data_rx);
return 0;
}
static const struct regmap_config stm32_h7_i2s_regmap_conf = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = STM32_I2S_CGFR_REG,
.readable_reg = stm32_i2s_readable_reg,
.volatile_reg = stm32_i2s_volatile_reg,
.writeable_reg = stm32_i2s_writeable_reg,
.fast_io = true,
};
static const struct snd_soc_dai_ops stm32_i2s_pcm_dai_ops = {
.set_sysclk = stm32_i2s_set_sysclk,
.set_fmt = stm32_i2s_set_dai_fmt,
.startup = stm32_i2s_startup,
.hw_params = stm32_i2s_hw_params,
.trigger = stm32_i2s_trigger,
.shutdown = stm32_i2s_shutdown,
};
static const struct snd_pcm_hardware stm32_i2s_pcm_hw = {
.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP,
.buffer_bytes_max = 8 * PAGE_SIZE,
.period_bytes_max = 2048,
.periods_min = 2,
.periods_max = 8,
};
static const struct snd_dmaengine_pcm_config stm32_i2s_pcm_config = {
.pcm_hardware = &stm32_i2s_pcm_hw,
.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
.prealloc_buffer_size = PAGE_SIZE * 8,
};
static const struct snd_soc_component_driver stm32_i2s_component = {
.name = "stm32-i2s",
};
static void stm32_i2s_dai_init(struct snd_soc_pcm_stream *stream,
char *stream_name)
{
stream->stream_name = stream_name;
stream->channels_min = 1;
stream->channels_max = 2;
stream->rates = SNDRV_PCM_RATE_8000_192000;
stream->formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE;
}
static int stm32_i2s_dais_init(struct platform_device *pdev,
struct stm32_i2s_data *i2s)
{
struct snd_soc_dai_driver *dai_ptr;
dai_ptr = devm_kzalloc(&pdev->dev, sizeof(struct snd_soc_dai_driver),
GFP_KERNEL);
if (!dai_ptr)
return -ENOMEM;
snprintf(i2s->dais_name, STM32_I2S_DAI_NAME_SIZE,
"%s", dev_name(&pdev->dev));
dai_ptr->probe = stm32_i2s_dai_probe;
dai_ptr->ops = &stm32_i2s_pcm_dai_ops;
dai_ptr->name = i2s->dais_name;
dai_ptr->id = 1;
stm32_i2s_dai_init(&dai_ptr->playback, "playback");
stm32_i2s_dai_init(&dai_ptr->capture, "capture");
i2s->dai_drv = dai_ptr;
return 0;
}
static const struct of_device_id stm32_i2s_ids[] = {
{
.compatible = "st,stm32h7-i2s",
.data = &stm32_h7_i2s_regmap_conf
},
{},
};
static int stm32_i2s_parse_dt(struct platform_device *pdev,
struct stm32_i2s_data *i2s)
{
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *of_id;
struct reset_control *rst;
struct resource *res;
int irq, ret;
if (!np)
return -ENODEV;
of_id = of_match_device(stm32_i2s_ids, &pdev->dev);
if (of_id)
i2s->regmap_conf = (const struct regmap_config *)of_id->data;
else
return -EINVAL;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
i2s->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(i2s->base))
return PTR_ERR(i2s->base);
i2s->phys_addr = res->start;
/* Get clocks */
i2s->pclk = devm_clk_get(&pdev->dev, "pclk");
if (IS_ERR(i2s->pclk)) {
dev_err(&pdev->dev, "Could not get pclk\n");
return PTR_ERR(i2s->pclk);
}
i2s->i2sclk = devm_clk_get(&pdev->dev, "i2sclk");
if (IS_ERR(i2s->i2sclk)) {
dev_err(&pdev->dev, "Could not get i2sclk\n");
return PTR_ERR(i2s->i2sclk);
}
i2s->x8kclk = devm_clk_get(&pdev->dev, "x8k");
if (IS_ERR(i2s->x8kclk)) {
dev_err(&pdev->dev, "missing x8k parent clock\n");
return PTR_ERR(i2s->x8kclk);
}
i2s->x11kclk = devm_clk_get(&pdev->dev, "x11k");
if (IS_ERR(i2s->x11kclk)) {
dev_err(&pdev->dev, "missing x11k parent clock\n");
return PTR_ERR(i2s->x11kclk);
}
/* Get irqs */
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "no irq for node %s\n", pdev->name);
return -ENOENT;
}
ret = devm_request_irq(&pdev->dev, irq, stm32_i2s_isr, IRQF_ONESHOT,
dev_name(&pdev->dev), i2s);
if (ret) {
dev_err(&pdev->dev, "irq request returned %d\n", ret);
return ret;
}
/* Reset */
rst = devm_reset_control_get(&pdev->dev, NULL);
if (!IS_ERR(rst)) {
reset_control_assert(rst);
udelay(2);
reset_control_deassert(rst);
}
return 0;
}
static int stm32_i2s_probe(struct platform_device *pdev)
{
struct stm32_i2s_data *i2s;
int ret;
i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
if (!i2s)
return -ENOMEM;
ret = stm32_i2s_parse_dt(pdev, i2s);
if (ret)
return ret;
i2s->pdev = pdev;
i2s->ms_flg = I2S_MS_NOT_SET;
spin_lock_init(&i2s->lock_fd);
platform_set_drvdata(pdev, i2s);
ret = stm32_i2s_dais_init(pdev, i2s);
if (ret)
return ret;
i2s->regmap = devm_regmap_init_mmio(&pdev->dev, i2s->base,
i2s->regmap_conf);
if (IS_ERR(i2s->regmap)) {
dev_err(&pdev->dev, "regmap init failed\n");
return PTR_ERR(i2s->regmap);
}
ret = clk_prepare_enable(i2s->pclk);
if (ret) {
dev_err(&pdev->dev, "Enable pclk failed: %d\n", ret);
return ret;
}
ret = clk_prepare_enable(i2s->i2sclk);
if (ret) {
dev_err(&pdev->dev, "Enable i2sclk failed: %d\n", ret);
goto err_pclk_disable;
}
ret = devm_snd_soc_register_component(&pdev->dev, &stm32_i2s_component,
i2s->dai_drv, 1);
if (ret)
goto err_clocks_disable;
ret = devm_snd_dmaengine_pcm_register(&pdev->dev,
&stm32_i2s_pcm_config, 0);
if (ret)
goto err_clocks_disable;
/* Set SPI/I2S in i2s mode */
ret = regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG,
I2S_CGFR_I2SMOD, I2S_CGFR_I2SMOD);
if (ret)
goto err_clocks_disable;
return ret;
err_clocks_disable:
clk_disable_unprepare(i2s->i2sclk);
err_pclk_disable:
clk_disable_unprepare(i2s->pclk);
return ret;
}
static int stm32_i2s_remove(struct platform_device *pdev)
{
struct stm32_i2s_data *i2s = platform_get_drvdata(pdev);
clk_disable_unprepare(i2s->i2sclk);
clk_disable_unprepare(i2s->pclk);
return 0;
}
MODULE_DEVICE_TABLE(of, stm32_i2s_ids);
static struct platform_driver stm32_i2s_driver = {
.driver = {
.name = "st,stm32-i2s",
.of_match_table = stm32_i2s_ids,
},
.probe = stm32_i2s_probe,
.remove = stm32_i2s_remove,
};
module_platform_driver(stm32_i2s_driver);
MODULE_DESCRIPTION("STM32 Soc i2s Interface");
MODULE_AUTHOR("Olivier Moysan, <olivier.moysan@st.com>");
MODULE_ALIAS("platform:stm32-i2s");
MODULE_LICENSE("GPL v2");

View File

@ -27,8 +27,17 @@
#include "stm32_sai.h"
static const struct stm32_sai_conf stm32_sai_conf_f4 = {
.version = SAI_STM32F4,
};
static const struct stm32_sai_conf stm32_sai_conf_h7 = {
.version = SAI_STM32H7,
};
static const struct of_device_id stm32_sai_ids[] = {
{ .compatible = "st,stm32f4-sai", .data = (void *)SAI_STM32F4 },
{ .compatible = "st,stm32f4-sai", .data = (void *)&stm32_sai_conf_f4 },
{ .compatible = "st,stm32h7-sai", .data = (void *)&stm32_sai_conf_h7 },
{}
};
@ -52,7 +61,7 @@ static int stm32_sai_probe(struct platform_device *pdev)
of_id = of_match_device(stm32_sai_ids, &pdev->dev);
if (of_id)
sai->version = (enum stm32_sai_version)of_id->data;
sai->conf = (struct stm32_sai_conf *)of_id->data;
else
return -EINVAL;
@ -110,6 +119,6 @@ static struct platform_driver stm32_sai_driver = {
module_platform_driver(stm32_sai_driver);
MODULE_DESCRIPTION("STM32 Soc SAI Interface");
MODULE_AUTHOR("Olivier Moysan, <olivier.moysan@st.com>");
MODULE_AUTHOR("Olivier Moysan <olivier.moysan@st.com>");
MODULE_ALIAS("platform:st,stm32-sai");
MODULE_LICENSE("GPL v2");

View File

@ -31,6 +31,10 @@
#define STM_SAI_CLRFR_REGX 0x18
#define STM_SAI_DR_REGX 0x1C
/* Sub-block A registers, relative to sub-block A address */
#define STM_SAI_PDMCR_REGX 0x40
#define STM_SAI_PDMLY_REGX 0x44
/******************** Bit definition for SAI_GCR register *******************/
#define SAI_GCR_SYNCIN_SHIFT 0
#define SAI_GCR_SYNCIN_MASK GENMASK(1, SAI_GCR_SYNCIN_SHIFT)
@ -75,10 +79,11 @@
#define SAI_XCR1_NODIV BIT(SAI_XCR1_NODIV_SHIFT)
#define SAI_XCR1_MCKDIV_SHIFT 20
#define SAI_XCR1_MCKDIV_WIDTH 4
#define SAI_XCR1_MCKDIV_MASK GENMASK(24, SAI_XCR1_MCKDIV_SHIFT)
#define SAI_XCR1_MCKDIV_WIDTH(x) (((x) == SAI_STM32F4) ? 4 : 6)
#define SAI_XCR1_MCKDIV_MASK(x) GENMASK((SAI_XCR1_MCKDIV_SHIFT + (x) - 1),\
SAI_XCR1_MCKDIV_SHIFT)
#define SAI_XCR1_MCKDIV_SET(x) ((x) << SAI_XCR1_MCKDIV_SHIFT)
#define SAI_XCR1_MCKDIV_MAX ((1 << SAI_XCR1_MCKDIV_WIDTH) - 1)
#define SAI_XCR1_MCKDIV_MAX(x) ((1 << SAI_XCR1_MCKDIV_WIDTH(x)) - 1)
#define SAI_XCR1_OSR_SHIFT 26
#define SAI_XCR1_OSR BIT(SAI_XCR1_OSR_SHIFT)
@ -125,7 +130,6 @@
#define SAI_XFRCR_FSOFF BIT(SAI_XFRCR_FSOFF_SHIFT)
/****************** Bit definition for SAI_XSLOTR register ******************/
#define SAI_XSLOTR_FBOFF_SHIFT 0
#define SAI_XSLOTR_FBOFF_MASK GENMASK(4, SAI_XSLOTR_FBOFF_SHIFT)
#define SAI_XSLOTR_FBOFF_SET(x) ((x) << SAI_XSLOTR_FBOFF_SHIFT)
@ -179,8 +183,65 @@
#define SAI_XCLRFR_SHIFT 0
#define SAI_XCLRFR_MASK GENMASK(6, SAI_XCLRFR_SHIFT)
/****************** Bit definition for SAI_PDMCR register ******************/
#define SAI_PDMCR_PDMEN BIT(0)
#define SAI_PDMCR_MICNBR_SHIFT 4
#define SAI_PDMCR_MICNBR_MASK GENMASK(5, SAI_PDMCR_MICNBR_SHIFT)
#define SAI_PDMCR_MICNBR_SET(x) ((x) << SAI_PDMCR_MICNBR_SHIFT)
#define SAI_PDMCR_CKEN1 BIT(8)
#define SAI_PDMCR_CKEN2 BIT(9)
#define SAI_PDMCR_CKEN3 BIT(10)
#define SAI_PDMCR_CKEN4 BIT(11)
/****************** Bit definition for (SAI_PDMDLY register ****************/
#define SAI_PDMDLY_1L_SHIFT 0
#define SAI_PDMDLY_1L_MASK GENMASK(2, SAI_PDMDLY_1L_SHIFT)
#define SAI_PDMDLY_1L_WIDTH 3
#define SAI_PDMDLY_1R_SHIFT 4
#define SAI_PDMDLY_1R_MASK GENMASK(6, SAI_PDMDLY_1R_SHIFT)
#define SAI_PDMDLY_1R_WIDTH 3
#define SAI_PDMDLY_2L_SHIFT 8
#define SAI_PDMDLY_2L_MASK GENMASK(10, SAI_PDMDLY_2L_SHIFT)
#define SAI_PDMDLY_2L_WIDTH 3
#define SAI_PDMDLY_2R_SHIFT 12
#define SAI_PDMDLY_2R_MASK GENMASK(14, SAI_PDMDLY_2R_SHIFT)
#define SAI_PDMDLY_2R_WIDTH 3
#define SAI_PDMDLY_3L_SHIFT 16
#define SAI_PDMDLY_3L_MASK GENMASK(18, SAI_PDMDLY_3L_SHIFT)
#define SAI_PDMDLY_3L_WIDTH 3
#define SAI_PDMDLY_3R_SHIFT 20
#define SAI_PDMDLY_3R_MASK GENMASK(22, SAI_PDMDLY_3R_SHIFT)
#define SAI_PDMDLY_3R_WIDTH 3
#define SAI_PDMDLY_4L_SHIFT 24
#define SAI_PDMDLY_4L_MASK GENMASK(26, SAI_PDMDLY_4L_SHIFT)
#define SAI_PDMDLY_4L_WIDTH 3
#define SAI_PDMDLY_4R_SHIFT 28
#define SAI_PDMDLY_4R_MASK GENMASK(30, SAI_PDMDLY_4R_SHIFT)
#define SAI_PDMDLY_4R_WIDTH 3
#define STM_SAI_IS_F4(ip) ((ip)->conf->version == SAI_STM32F4)
#define STM_SAI_IS_H7(ip) ((ip)->conf->version == SAI_STM32H7)
enum stm32_sai_version {
SAI_STM32F4
SAI_STM32F4,
SAI_STM32H7
};
/**
* struct stm32_sai_conf - SAI configuration
* @version: SAI version
*/
struct stm32_sai_conf {
int version;
};
/**
@ -195,6 +256,6 @@ struct stm32_sai_data {
struct platform_device *pdev;
struct clk *clk_x8k;
struct clk *clk_x11k;
int version;
struct stm32_sai_conf *conf;
int irq;
};

View File

@ -51,12 +51,15 @@
#define STM_SAI_A_ID 0x0
#define STM_SAI_B_ID 0x1
#define STM_SAI_IS_SUB_A(x) ((x)->id == STM_SAI_A_ID)
#define STM_SAI_IS_SUB_B(x) ((x)->id == STM_SAI_B_ID)
#define STM_SAI_BLOCK_NAME(x) (((x)->id == STM_SAI_A_ID) ? "A" : "B")
/**
* struct stm32_sai_sub_data - private data of SAI sub block (block A or B)
* @pdev: device data pointer
* @regmap: SAI register map pointer
* @regmap_config: SAI sub block register map configuration pointer
* @dma_params: dma configuration data for rx or tx channel
* @cpu_dai_drv: DAI driver data pointer
* @cpu_dai: DAI runtime data pointer
@ -79,6 +82,7 @@
struct stm32_sai_sub_data {
struct platform_device *pdev;
struct regmap *regmap;
const struct regmap_config *regmap_config;
struct snd_dmaengine_dai_dma_data dma_params;
struct snd_soc_dai_driver *cpu_dai_drv;
struct snd_soc_dai *cpu_dai;
@ -118,6 +122,8 @@ static bool stm32_sai_sub_readable_reg(struct device *dev, unsigned int reg)
case STM_SAI_SR_REGX:
case STM_SAI_CLRFR_REGX:
case STM_SAI_DR_REGX:
case STM_SAI_PDMCR_REGX:
case STM_SAI_PDMLY_REGX:
return true;
default:
return false;
@ -145,13 +151,15 @@ static bool stm32_sai_sub_writeable_reg(struct device *dev, unsigned int reg)
case STM_SAI_SR_REGX:
case STM_SAI_CLRFR_REGX:
case STM_SAI_DR_REGX:
case STM_SAI_PDMCR_REGX:
case STM_SAI_PDMLY_REGX:
return true;
default:
return false;
}
}
static const struct regmap_config stm32_sai_sub_regmap_config = {
static const struct regmap_config stm32_sai_sub_regmap_config_f4 = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
@ -162,6 +170,17 @@ static const struct regmap_config stm32_sai_sub_regmap_config = {
.fast_io = true,
};
static const struct regmap_config stm32_sai_sub_regmap_config_h7 = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = STM_SAI_PDMLY_REGX,
.readable_reg = stm32_sai_sub_readable_reg,
.volatile_reg = stm32_sai_sub_volatile_reg,
.writeable_reg = stm32_sai_sub_writeable_reg,
.fast_io = true,
};
static irqreturn_t stm32_sai_isr(int irq, void *devid)
{
struct stm32_sai_sub_data *sai = (struct stm32_sai_sub_data *)devid;
@ -181,29 +200,29 @@ static irqreturn_t stm32_sai_isr(int irq, void *devid)
SAI_XCLRFR_MASK);
if (flags & SAI_XIMR_OVRUDRIE) {
dev_err(&pdev->dev, "IT %s\n",
dev_err(&pdev->dev, "IRQ %s\n",
STM_SAI_IS_PLAYBACK(sai) ? "underrun" : "overrun");
status = SNDRV_PCM_STATE_XRUN;
}
if (flags & SAI_XIMR_MUTEDETIE)
dev_dbg(&pdev->dev, "IT mute detected\n");
dev_dbg(&pdev->dev, "IRQ mute detected\n");
if (flags & SAI_XIMR_WCKCFGIE) {
dev_err(&pdev->dev, "IT wrong clock configuration\n");
dev_err(&pdev->dev, "IRQ wrong clock configuration\n");
status = SNDRV_PCM_STATE_DISCONNECTED;
}
if (flags & SAI_XIMR_CNRDYIE)
dev_warn(&pdev->dev, "IT Codec not ready\n");
dev_err(&pdev->dev, "IRQ Codec not ready\n");
if (flags & SAI_XIMR_AFSDETIE) {
dev_warn(&pdev->dev, "IT Anticipated frame synchro\n");
dev_err(&pdev->dev, "IRQ Anticipated frame synchro\n");
status = SNDRV_PCM_STATE_XRUN;
}
if (flags & SAI_XIMR_LFSDETIE) {
dev_warn(&pdev->dev, "IT Late frame synchro\n");
dev_err(&pdev->dev, "IRQ Late frame synchro\n");
status = SNDRV_PCM_STATE_XRUN;
}
@ -220,8 +239,15 @@ static int stm32_sai_set_sysclk(struct snd_soc_dai *cpu_dai,
int clk_id, unsigned int freq, int dir)
{
struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
int ret;
if ((dir == SND_SOC_CLOCK_OUT) && sai->master) {
ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX,
SAI_XCR1_NODIV,
(unsigned int)~SAI_XCR1_NODIV);
if (ret < 0)
return ret;
sai->mclk_rate = freq;
dev_dbg(cpu_dai->dev, "SAI MCLK frequency is %uHz\n", freq);
}
@ -235,7 +261,7 @@ static int stm32_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask,
struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
int slotr, slotr_mask, slot_size;
dev_dbg(cpu_dai->dev, "masks tx/rx:%#x/%#x, slots:%d, width:%d\n",
dev_dbg(cpu_dai->dev, "Masks tx/rx:%#x/%#x, slots:%d, width:%d\n",
tx_mask, rx_mask, slots, slot_width);
switch (slot_width) {
@ -356,6 +382,10 @@ static int stm32_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
}
cr1_mask |= SAI_XCR1_SLAVE;
/* do not generate master by default */
cr1 |= SAI_XCR1_NODIV;
cr1_mask |= SAI_XCR1_NODIV;
ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, cr1_mask, cr1);
if (ret < 0) {
dev_err(cpu_dai->dev, "Failed to update CR1 register\n");
@ -377,7 +407,7 @@ static int stm32_sai_startup(struct snd_pcm_substream *substream,
ret = clk_prepare_enable(sai->sai_ck);
if (ret < 0) {
dev_err(cpu_dai->dev, "failed to enable clock: %d\n", ret);
dev_err(cpu_dai->dev, "Failed to enable clock: %d\n", ret);
return ret;
}
@ -497,7 +527,7 @@ static int stm32_sai_set_slots(struct snd_soc_dai *cpu_dai)
SAI_XSLOTR_SLOTEN_SET(sai->slot_mask));
}
dev_dbg(cpu_dai->dev, "slots %d, slot width %d\n",
dev_dbg(cpu_dai->dev, "Slots %d, slot width %d\n",
sai->slots, sai->slot_width);
return 0;
@ -521,7 +551,7 @@ static void stm32_sai_set_frame(struct snd_soc_dai *cpu_dai)
frcr |= SAI_XFRCR_FSALL_SET((fs_active - 1));
frcr_mask = SAI_XFRCR_FRL_MASK | SAI_XFRCR_FSALL_MASK;
dev_dbg(cpu_dai->dev, "frame length %d, frame active %d\n",
dev_dbg(cpu_dai->dev, "Frame length %d, frame active %d\n",
sai->fs_length, fs_active);
regmap_update_bits(sai->regmap, STM_SAI_FRCR_REGX, frcr_mask, frcr);
@ -540,7 +570,8 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai,
{
struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
int cr1, mask, div = 0;
int sai_clk_rate, ret;
int sai_clk_rate, mclk_ratio, den, ret;
int version = sai->pdata->conf->version;
if (!sai->mclk_rate) {
dev_err(cpu_dai->dev, "Mclk rate is null\n");
@ -553,21 +584,53 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai,
clk_set_parent(sai->sai_ck, sai->pdata->clk_x8k);
sai_clk_rate = clk_get_rate(sai->sai_ck);
/*
* mclk_rate = 256 * fs
* MCKDIV = 0 if sai_ck < 3/2 * mclk_rate
* MCKDIV = sai_ck / (2 * mclk_rate) otherwise
*/
if (2 * sai_clk_rate >= 3 * sai->mclk_rate)
div = DIV_ROUND_CLOSEST(sai_clk_rate, 2 * sai->mclk_rate);
if (STM_SAI_IS_F4(sai->pdata)) {
/*
* mclk_rate = 256 * fs
* MCKDIV = 0 if sai_ck < 3/2 * mclk_rate
* MCKDIV = sai_ck / (2 * mclk_rate) otherwise
*/
if (2 * sai_clk_rate >= 3 * sai->mclk_rate)
div = DIV_ROUND_CLOSEST(sai_clk_rate,
2 * sai->mclk_rate);
} else {
/*
* TDM mode :
* mclk on
* MCKDIV = sai_ck / (ws x 256) (NOMCK=0. OSR=0)
* MCKDIV = sai_ck / (ws x 512) (NOMCK=0. OSR=1)
* mclk off
* MCKDIV = sai_ck / (frl x ws) (NOMCK=1)
* Note: NOMCK/NODIV correspond to same bit.
*/
if (sai->mclk_rate) {
mclk_ratio = sai->mclk_rate / params_rate(params);
if (mclk_ratio != 256) {
if (mclk_ratio == 512) {
mask = SAI_XCR1_OSR;
cr1 = SAI_XCR1_OSR;
} else {
dev_err(cpu_dai->dev,
"Wrong mclk ratio %d\n",
mclk_ratio);
return -EINVAL;
}
}
div = DIV_ROUND_CLOSEST(sai_clk_rate, sai->mclk_rate);
} else {
/* mclk-fs not set, master clock not active. NOMCK=1 */
den = sai->fs_length * params_rate(params);
div = DIV_ROUND_CLOSEST(sai_clk_rate, den);
}
}
if (div > SAI_XCR1_MCKDIV_MAX) {
if (div > SAI_XCR1_MCKDIV_MAX(version)) {
dev_err(cpu_dai->dev, "Divider %d out of range\n", div);
return -EINVAL;
}
dev_dbg(cpu_dai->dev, "SAI clock %d, divider %d\n", sai_clk_rate, div);
mask = SAI_XCR1_MCKDIV_MASK;
mask = SAI_XCR1_MCKDIV_MASK(SAI_XCR1_MCKDIV_WIDTH(version));
cr1 = SAI_XCR1_MCKDIV_SET(div);
ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, mask, cr1);
if (ret < 0) {
@ -629,12 +692,12 @@ static int stm32_sai_trigger(struct snd_pcm_substream *substream, int cmd,
dev_dbg(cpu_dai->dev, "Disable DMA and SAI\n");
regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX,
SAI_XCR1_DMAEN,
(unsigned int)~SAI_XCR1_DMAEN);
SAI_XCR1_SAIEN,
(unsigned int)~SAI_XCR1_SAIEN);
ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX,
SAI_XCR1_SAIEN,
(unsigned int)~SAI_XCR1_SAIEN);
SAI_XCR1_DMAEN,
(unsigned int)~SAI_XCR1_DMAEN);
if (ret < 0)
dev_err(cpu_dai->dev, "Failed to update CR1 register\n");
break;
@ -652,6 +715,9 @@ static void stm32_sai_shutdown(struct snd_pcm_substream *substream,
regmap_update_bits(sai->regmap, STM_SAI_IMR_REGX, SAI_XIMR_MASK, 0);
regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, SAI_XCR1_NODIV,
SAI_XCR1_NODIV);
clk_disable_unprepare(sai->sai_ck);
sai->substream = NULL;
}
@ -761,16 +827,23 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev,
return -ENODEV;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dev_err(&pdev->dev, "res %pr\n", res);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
sai->phys_addr = res->start;
sai->regmap = devm_regmap_init_mmio(&pdev->dev, base,
&stm32_sai_sub_regmap_config);
sai->regmap_config = &stm32_sai_sub_regmap_config_f4;
/* Note: PDM registers not available for H7 sub-block B */
if (STM_SAI_IS_H7(sai->pdata) && STM_SAI_IS_SUB_A(sai))
sai->regmap_config = &stm32_sai_sub_regmap_config_h7;
sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "sai_ck",
base, sai->regmap_config);
if (IS_ERR(sai->regmap)) {
dev_err(&pdev->dev, "Failed to initialize MMIO\n");
return PTR_ERR(sai->regmap);
}
/* Get direction property */
if (of_property_match_string(np, "dma-names", "tx") >= 0) {
@ -784,7 +857,7 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev,
sai->sai_ck = devm_clk_get(&pdev->dev, "sai_ck");
if (IS_ERR(sai->sai_ck)) {
dev_err(&pdev->dev, "missing kernel clock sai_ck\n");
dev_err(&pdev->dev, "Missing kernel clock sai_ck\n");
return PTR_ERR(sai->sai_ck);
}
@ -849,7 +922,7 @@ static int stm32_sai_sub_probe(struct platform_device *pdev)
ret = devm_request_irq(&pdev->dev, sai->pdata->irq, stm32_sai_isr,
IRQF_SHARED, dev_name(&pdev->dev), sai);
if (ret) {
dev_err(&pdev->dev, "irq request returned %d\n", ret);
dev_err(&pdev->dev, "IRQ request returned %d\n", ret);
return ret;
}
@ -861,7 +934,7 @@ static int stm32_sai_sub_probe(struct platform_device *pdev)
ret = devm_snd_dmaengine_pcm_register(&pdev->dev,
&stm32_sai_pcm_config, 0);
if (ret) {
dev_err(&pdev->dev, "could not register pcm dma\n");
dev_err(&pdev->dev, "Could not register pcm dma\n");
return ret;
}
@ -879,6 +952,6 @@ static struct platform_driver stm32_sai_sub_driver = {
module_platform_driver(stm32_sai_sub_driver);
MODULE_DESCRIPTION("STM32 Soc SAI sub-block Interface");
MODULE_AUTHOR("Olivier Moysan, <olivier.moysan@st.com>");
MODULE_AUTHOR("Olivier Moysan <olivier.moysan@st.com>");
MODULE_ALIAS("platform:st,stm32-sai-sub");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,998 @@
/*
* STM32 ALSA SoC Digital Audio Interface (SPDIF-rx) driver.
*
* Copyright (C) 2017, STMicroelectronics - All Rights Reserved
* Author(s): Olivier Moysan <olivier.moysan@st.com> for STMicroelectronics.
*
* License terms: GPL V2.0.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*/
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h>
/* SPDIF-rx Register Map */
#define STM32_SPDIFRX_CR 0x00
#define STM32_SPDIFRX_IMR 0x04
#define STM32_SPDIFRX_SR 0x08
#define STM32_SPDIFRX_IFCR 0x0C
#define STM32_SPDIFRX_DR 0x10
#define STM32_SPDIFRX_CSR 0x14
#define STM32_SPDIFRX_DIR 0x18
/* Bit definition for SPDIF_CR register */
#define SPDIFRX_CR_SPDIFEN_SHIFT 0
#define SPDIFRX_CR_SPDIFEN_MASK GENMASK(1, SPDIFRX_CR_SPDIFEN_SHIFT)
#define SPDIFRX_CR_SPDIFENSET(x) ((x) << SPDIFRX_CR_SPDIFEN_SHIFT)
#define SPDIFRX_CR_RXDMAEN BIT(2)
#define SPDIFRX_CR_RXSTEO BIT(3)
#define SPDIFRX_CR_DRFMT_SHIFT 4
#define SPDIFRX_CR_DRFMT_MASK GENMASK(5, SPDIFRX_CR_DRFMT_SHIFT)
#define SPDIFRX_CR_DRFMTSET(x) ((x) << SPDIFRX_CR_DRFMT_SHIFT)
#define SPDIFRX_CR_PMSK BIT(6)
#define SPDIFRX_CR_VMSK BIT(7)
#define SPDIFRX_CR_CUMSK BIT(8)
#define SPDIFRX_CR_PTMSK BIT(9)
#define SPDIFRX_CR_CBDMAEN BIT(10)
#define SPDIFRX_CR_CHSEL_SHIFT 11
#define SPDIFRX_CR_CHSEL BIT(SPDIFRX_CR_CHSEL_SHIFT)
#define SPDIFRX_CR_NBTR_SHIFT 12
#define SPDIFRX_CR_NBTR_MASK GENMASK(13, SPDIFRX_CR_NBTR_SHIFT)
#define SPDIFRX_CR_NBTRSET(x) ((x) << SPDIFRX_CR_NBTR_SHIFT)
#define SPDIFRX_CR_WFA BIT(14)
#define SPDIFRX_CR_INSEL_SHIFT 16
#define SPDIFRX_CR_INSEL_MASK GENMASK(18, PDIFRX_CR_INSEL_SHIFT)
#define SPDIFRX_CR_INSELSET(x) ((x) << SPDIFRX_CR_INSEL_SHIFT)
#define SPDIFRX_CR_CKSEN_SHIFT 20
#define SPDIFRX_CR_CKSEN BIT(20)
#define SPDIFRX_CR_CKSBKPEN BIT(21)
/* Bit definition for SPDIFRX_IMR register */
#define SPDIFRX_IMR_RXNEI BIT(0)
#define SPDIFRX_IMR_CSRNEIE BIT(1)
#define SPDIFRX_IMR_PERRIE BIT(2)
#define SPDIFRX_IMR_OVRIE BIT(3)
#define SPDIFRX_IMR_SBLKIE BIT(4)
#define SPDIFRX_IMR_SYNCDIE BIT(5)
#define SPDIFRX_IMR_IFEIE BIT(6)
#define SPDIFRX_XIMR_MASK GENMASK(6, 0)
/* Bit definition for SPDIFRX_SR register */
#define SPDIFRX_SR_RXNE BIT(0)
#define SPDIFRX_SR_CSRNE BIT(1)
#define SPDIFRX_SR_PERR BIT(2)
#define SPDIFRX_SR_OVR BIT(3)
#define SPDIFRX_SR_SBD BIT(4)
#define SPDIFRX_SR_SYNCD BIT(5)
#define SPDIFRX_SR_FERR BIT(6)
#define SPDIFRX_SR_SERR BIT(7)
#define SPDIFRX_SR_TERR BIT(8)
#define SPDIFRX_SR_WIDTH5_SHIFT 16
#define SPDIFRX_SR_WIDTH5_MASK GENMASK(30, PDIFRX_SR_WIDTH5_SHIFT)
#define SPDIFRX_SR_WIDTH5SET(x) ((x) << SPDIFRX_SR_WIDTH5_SHIFT)
/* Bit definition for SPDIFRX_IFCR register */
#define SPDIFRX_IFCR_PERRCF BIT(2)
#define SPDIFRX_IFCR_OVRCF BIT(3)
#define SPDIFRX_IFCR_SBDCF BIT(4)
#define SPDIFRX_IFCR_SYNCDCF BIT(5)
#define SPDIFRX_XIFCR_MASK GENMASK(5, 2)
/* Bit definition for SPDIFRX_DR register (DRFMT = 0b00) */
#define SPDIFRX_DR0_DR_SHIFT 0
#define SPDIFRX_DR0_DR_MASK GENMASK(23, SPDIFRX_DR0_DR_SHIFT)
#define SPDIFRX_DR0_DRSET(x) ((x) << SPDIFRX_DR0_DR_SHIFT)
#define SPDIFRX_DR0_PE BIT(24)
#define SPDIFRX_DR0_V BIT(25)
#define SPDIFRX_DR0_U BIT(26)
#define SPDIFRX_DR0_C BIT(27)
#define SPDIFRX_DR0_PT_SHIFT 28
#define SPDIFRX_DR0_PT_MASK GENMASK(29, SPDIFRX_DR0_PT_SHIFT)
#define SPDIFRX_DR0_PTSET(x) ((x) << SPDIFRX_DR0_PT_SHIFT)
/* Bit definition for SPDIFRX_DR register (DRFMT = 0b01) */
#define SPDIFRX_DR1_PE BIT(0)
#define SPDIFRX_DR1_V BIT(1)
#define SPDIFRX_DR1_U BIT(2)
#define SPDIFRX_DR1_C BIT(3)
#define SPDIFRX_DR1_PT_SHIFT 4
#define SPDIFRX_DR1_PT_MASK GENMASK(5, SPDIFRX_DR1_PT_SHIFT)
#define SPDIFRX_DR1_PTSET(x) ((x) << SPDIFRX_DR1_PT_SHIFT)
#define SPDIFRX_DR1_DR_SHIFT 8
#define SPDIFRX_DR1_DR_MASK GENMASK(31, SPDIFRX_DR1_DR_SHIFT)
#define SPDIFRX_DR1_DRSET(x) ((x) << SPDIFRX_DR1_DR_SHIFT)
/* Bit definition for SPDIFRX_DR register (DRFMT = 0b10) */
#define SPDIFRX_DR1_DRNL1_SHIFT 0
#define SPDIFRX_DR1_DRNL1_MASK GENMASK(15, SPDIFRX_DR1_DRNL1_SHIFT)
#define SPDIFRX_DR1_DRNL1SET(x) ((x) << SPDIFRX_DR1_DRNL1_SHIFT)
#define SPDIFRX_DR1_DRNL2_SHIFT 16
#define SPDIFRX_DR1_DRNL2_MASK GENMASK(31, SPDIFRX_DR1_DRNL2_SHIFT)
#define SPDIFRX_DR1_DRNL2SET(x) ((x) << SPDIFRX_DR1_DRNL2_SHIFT)
/* Bit definition for SPDIFRX_CSR register */
#define SPDIFRX_CSR_USR_SHIFT 0
#define SPDIFRX_CSR_USR_MASK GENMASK(15, SPDIFRX_CSR_USR_SHIFT)
#define SPDIFRX_CSR_USRGET(x) (((x) & SPDIFRX_CSR_USR_MASK)\
>> SPDIFRX_CSR_USR_SHIFT)
#define SPDIFRX_CSR_CS_SHIFT 16
#define SPDIFRX_CSR_CS_MASK GENMASK(23, SPDIFRX_CSR_CS_SHIFT)
#define SPDIFRX_CSR_CSGET(x) (((x) & SPDIFRX_CSR_CS_MASK)\
>> SPDIFRX_CSR_CS_SHIFT)
#define SPDIFRX_CSR_SOB BIT(24)
/* Bit definition for SPDIFRX_DIR register */
#define SPDIFRX_DIR_THI_SHIFT 0
#define SPDIFRX_DIR_THI_MASK GENMASK(12, SPDIFRX_DIR_THI_SHIFT)
#define SPDIFRX_DIR_THI_SET(x) ((x) << SPDIFRX_DIR_THI_SHIFT)
#define SPDIFRX_DIR_TLO_SHIFT 16
#define SPDIFRX_DIR_TLO_MASK GENMASK(28, SPDIFRX_DIR_TLO_SHIFT)
#define SPDIFRX_DIR_TLO_SET(x) ((x) << SPDIFRX_DIR_TLO_SHIFT)
#define SPDIFRX_SPDIFEN_DISABLE 0x0
#define SPDIFRX_SPDIFEN_SYNC 0x1
#define SPDIFRX_SPDIFEN_ENABLE 0x3
#define SPDIFRX_IN1 0x1
#define SPDIFRX_IN2 0x2
#define SPDIFRX_IN3 0x3
#define SPDIFRX_IN4 0x4
#define SPDIFRX_IN5 0x5
#define SPDIFRX_IN6 0x6
#define SPDIFRX_IN7 0x7
#define SPDIFRX_IN8 0x8
#define SPDIFRX_NBTR_NONE 0x0
#define SPDIFRX_NBTR_3 0x1
#define SPDIFRX_NBTR_15 0x2
#define SPDIFRX_NBTR_63 0x3
#define SPDIFRX_DRFMT_RIGHT 0x0
#define SPDIFRX_DRFMT_LEFT 0x1
#define SPDIFRX_DRFMT_PACKED 0x2
/* 192 CS bits in S/PDIF frame. i.e 24 CS bytes */
#define SPDIFRX_CS_BYTES_NB 24
#define SPDIFRX_UB_BYTES_NB 48
/*
* CSR register is retrieved as a 32 bits word
* It contains 1 channel status byte and 2 user data bytes
* 2 S/PDIF frames are acquired to get all CS/UB bits
*/
#define SPDIFRX_CSR_BUF_LENGTH (SPDIFRX_CS_BYTES_NB * 4 * 2)
/**
* struct stm32_spdifrx_data - private data of SPDIFRX
* @pdev: device data pointer
* @base: mmio register base virtual address
* @regmap: SPDIFRX register map pointer
* @regmap_conf: SPDIFRX register map configuration pointer
* @cs_completion: channel status retrieving completion
* @kclk: kernel clock feeding the SPDIFRX clock generator
* @dma_params: dma configuration data for rx channel
* @substream: PCM substream data pointer
* @dmab: dma buffer info pointer
* @ctrl_chan: dma channel for S/PDIF control bits
* @desc:dma async transaction descriptor
* @slave_config: dma slave channel runtime config pointer
* @phys_addr: SPDIFRX registers physical base address
* @lock: synchronization enabling lock
* @cs: channel status buffer
* @ub: user data buffer
* @irq: SPDIFRX interrupt line
* @refcount: keep count of opened DMA channels
*/
struct stm32_spdifrx_data {
struct platform_device *pdev;
void __iomem *base;
struct regmap *regmap;
const struct regmap_config *regmap_conf;
struct completion cs_completion;
struct clk *kclk;
struct snd_dmaengine_dai_dma_data dma_params;
struct snd_pcm_substream *substream;
struct snd_dma_buffer *dmab;
struct dma_chan *ctrl_chan;
struct dma_async_tx_descriptor *desc;
struct dma_slave_config slave_config;
dma_addr_t phys_addr;
spinlock_t lock; /* Sync enabling lock */
unsigned char cs[SPDIFRX_CS_BYTES_NB];
unsigned char ub[SPDIFRX_UB_BYTES_NB];
int irq;
int refcount;
};
static void stm32_spdifrx_dma_complete(void *data)
{
struct stm32_spdifrx_data *spdifrx = (struct stm32_spdifrx_data *)data;
struct platform_device *pdev = spdifrx->pdev;
u32 *p_start = (u32 *)spdifrx->dmab->area;
u32 *p_end = p_start + (2 * SPDIFRX_CS_BYTES_NB) - 1;
u32 *ptr = p_start;
u16 *ub_ptr = (short *)spdifrx->ub;
int i = 0;
regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR,
SPDIFRX_CR_CBDMAEN,
(unsigned int)~SPDIFRX_CR_CBDMAEN);
if (!spdifrx->dmab->area)
return;
while (ptr <= p_end) {
if (*ptr & SPDIFRX_CSR_SOB)
break;
ptr++;
}
if (ptr > p_end) {
dev_err(&pdev->dev, "Start of S/PDIF block not found\n");
return;
}
while (i < SPDIFRX_CS_BYTES_NB) {
spdifrx->cs[i] = (unsigned char)SPDIFRX_CSR_CSGET(*ptr);
*ub_ptr++ = SPDIFRX_CSR_USRGET(*ptr++);
if (ptr > p_end) {
dev_err(&pdev->dev, "Failed to get channel status\n");
return;
}
i++;
}
complete(&spdifrx->cs_completion);
}
static int stm32_spdifrx_dma_ctrl_start(struct stm32_spdifrx_data *spdifrx)
{
dma_cookie_t cookie;
int err;
spdifrx->desc = dmaengine_prep_slave_single(spdifrx->ctrl_chan,
spdifrx->dmab->addr,
SPDIFRX_CSR_BUF_LENGTH,
DMA_DEV_TO_MEM,
DMA_CTRL_ACK);
if (!spdifrx->desc)
return -EINVAL;
spdifrx->desc->callback = stm32_spdifrx_dma_complete;
spdifrx->desc->callback_param = spdifrx;
cookie = dmaengine_submit(spdifrx->desc);
err = dma_submit_error(cookie);
if (err)
return -EINVAL;
dma_async_issue_pending(spdifrx->ctrl_chan);
return 0;
}
static void stm32_spdifrx_dma_ctrl_stop(struct stm32_spdifrx_data *spdifrx)
{
dmaengine_terminate_async(spdifrx->ctrl_chan);
}
static int stm32_spdifrx_start_sync(struct stm32_spdifrx_data *spdifrx)
{
int cr, cr_mask, imr, ret;
/* Enable IRQs */
imr = SPDIFRX_IMR_IFEIE | SPDIFRX_IMR_SYNCDIE | SPDIFRX_IMR_PERRIE;
ret = regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_IMR, imr, imr);
if (ret)
return ret;
spin_lock(&spdifrx->lock);
spdifrx->refcount++;
regmap_read(spdifrx->regmap, STM32_SPDIFRX_CR, &cr);
if (!(cr & SPDIFRX_CR_SPDIFEN_MASK)) {
/*
* Start sync if SPDIFRX is still in idle state.
* SPDIFRX reception enabled when sync done
*/
dev_dbg(&spdifrx->pdev->dev, "start synchronization\n");
/*
* SPDIFRX configuration:
* Wait for activity before starting sync process. This avoid
* to issue sync errors when spdif signal is missing on input.
* Preamble, CS, user, validity and parity error bits not copied
* to DR register.
*/
cr = SPDIFRX_CR_WFA | SPDIFRX_CR_PMSK | SPDIFRX_CR_VMSK |
SPDIFRX_CR_CUMSK | SPDIFRX_CR_PTMSK | SPDIFRX_CR_RXSTEO;
cr_mask = cr;
cr |= SPDIFRX_CR_SPDIFENSET(SPDIFRX_SPDIFEN_SYNC);
cr_mask |= SPDIFRX_CR_SPDIFEN_MASK;
ret = regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR,
cr_mask, cr);
if (ret < 0)
dev_err(&spdifrx->pdev->dev,
"Failed to start synchronization\n");
}
spin_unlock(&spdifrx->lock);
return ret;
}
static void stm32_spdifrx_stop(struct stm32_spdifrx_data *spdifrx)
{
int cr, cr_mask, reg;
spin_lock(&spdifrx->lock);
if (--spdifrx->refcount) {
spin_unlock(&spdifrx->lock);
return;
}
cr = SPDIFRX_CR_SPDIFENSET(SPDIFRX_SPDIFEN_DISABLE);
cr_mask = SPDIFRX_CR_SPDIFEN_MASK | SPDIFRX_CR_RXDMAEN;
regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR, cr_mask, cr);
regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_IMR,
SPDIFRX_XIMR_MASK, 0);
regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_IFCR,
SPDIFRX_XIFCR_MASK, SPDIFRX_XIFCR_MASK);
/* dummy read to clear CSRNE and RXNE in status register */
regmap_read(spdifrx->regmap, STM32_SPDIFRX_DR, &reg);
regmap_read(spdifrx->regmap, STM32_SPDIFRX_CSR, &reg);
spin_unlock(&spdifrx->lock);
}
static int stm32_spdifrx_dma_ctrl_register(struct device *dev,
struct stm32_spdifrx_data *spdifrx)
{
int ret;
spdifrx->dmab = devm_kzalloc(dev, sizeof(struct snd_dma_buffer),
GFP_KERNEL);
if (!spdifrx->dmab)
return -ENOMEM;
spdifrx->dmab->dev.type = SNDRV_DMA_TYPE_DEV_IRAM;
spdifrx->dmab->dev.dev = dev;
ret = snd_dma_alloc_pages(spdifrx->dmab->dev.type, dev,
SPDIFRX_CSR_BUF_LENGTH, spdifrx->dmab);
if (ret < 0) {
dev_err(dev, "snd_dma_alloc_pages returned error %d\n", ret);
return ret;
}
spdifrx->ctrl_chan = dma_request_chan(dev, "rx-ctrl");
if (!spdifrx->ctrl_chan) {
dev_err(dev, "dma_request_slave_channel failed\n");
return -EINVAL;
}
spdifrx->slave_config.direction = DMA_DEV_TO_MEM;
spdifrx->slave_config.src_addr = (dma_addr_t)(spdifrx->phys_addr +
STM32_SPDIFRX_CSR);
spdifrx->slave_config.dst_addr = spdifrx->dmab->addr;
spdifrx->slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
spdifrx->slave_config.src_maxburst = 1;
ret = dmaengine_slave_config(spdifrx->ctrl_chan,
&spdifrx->slave_config);
if (ret < 0) {
dev_err(dev, "dmaengine_slave_config returned error %d\n", ret);
dma_release_channel(spdifrx->ctrl_chan);
spdifrx->ctrl_chan = NULL;
}
return ret;
};
static const char * const spdifrx_enum_input[] = {
"in0", "in1", "in2", "in3"
};
/* By default CS bits are retrieved from channel A */
static const char * const spdifrx_enum_cs_channel[] = {
"A", "B"
};
static SOC_ENUM_SINGLE_DECL(ctrl_enum_input,
STM32_SPDIFRX_CR, SPDIFRX_CR_INSEL_SHIFT,
spdifrx_enum_input);
static SOC_ENUM_SINGLE_DECL(ctrl_enum_cs_channel,
STM32_SPDIFRX_CR, SPDIFRX_CR_CHSEL_SHIFT,
spdifrx_enum_cs_channel);
static int stm32_spdifrx_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
uinfo->count = 1;
return 0;
}
static int stm32_spdifrx_ub_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
uinfo->count = 1;
return 0;
}
static int stm32_spdifrx_get_ctrl_data(struct stm32_spdifrx_data *spdifrx)
{
int ret = 0;
memset(spdifrx->cs, 0, SPDIFRX_CS_BYTES_NB);
memset(spdifrx->ub, 0, SPDIFRX_UB_BYTES_NB);
ret = stm32_spdifrx_dma_ctrl_start(spdifrx);
if (ret < 0)
return ret;
ret = clk_prepare_enable(spdifrx->kclk);
if (ret) {
dev_err(&spdifrx->pdev->dev, "Enable kclk failed: %d\n", ret);
return ret;
}
ret = regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR,
SPDIFRX_CR_CBDMAEN, SPDIFRX_CR_CBDMAEN);
if (ret < 0)
goto end;
ret = stm32_spdifrx_start_sync(spdifrx);
if (ret < 0)
goto end;
if (wait_for_completion_interruptible_timeout(&spdifrx->cs_completion,
msecs_to_jiffies(100))
<= 0) {
dev_err(&spdifrx->pdev->dev, "Failed to get control data\n");
ret = -EAGAIN;
}
stm32_spdifrx_stop(spdifrx);
stm32_spdifrx_dma_ctrl_stop(spdifrx);
end:
clk_disable_unprepare(spdifrx->kclk);
return ret;
}
static int stm32_spdifrx_capture_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
struct stm32_spdifrx_data *spdifrx = snd_soc_dai_get_drvdata(cpu_dai);
stm32_spdifrx_get_ctrl_data(spdifrx);
ucontrol->value.iec958.status[0] = spdifrx->cs[0];
ucontrol->value.iec958.status[1] = spdifrx->cs[1];
ucontrol->value.iec958.status[2] = spdifrx->cs[2];
ucontrol->value.iec958.status[3] = spdifrx->cs[3];
ucontrol->value.iec958.status[4] = spdifrx->cs[4];
return 0;
}
static int stm32_spdif_user_bits_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
struct stm32_spdifrx_data *spdifrx = snd_soc_dai_get_drvdata(cpu_dai);
stm32_spdifrx_get_ctrl_data(spdifrx);
ucontrol->value.iec958.status[0] = spdifrx->ub[0];
ucontrol->value.iec958.status[1] = spdifrx->ub[1];
ucontrol->value.iec958.status[2] = spdifrx->ub[2];
ucontrol->value.iec958.status[3] = spdifrx->ub[3];
ucontrol->value.iec958.status[4] = spdifrx->ub[4];
return 0;
}
static struct snd_kcontrol_new stm32_spdifrx_iec_ctrls[] = {
/* Channel status control */
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
.access = SNDRV_CTL_ELEM_ACCESS_READ |
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = stm32_spdifrx_info,
.get = stm32_spdifrx_capture_get,
},
/* User bits control */
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "IEC958 User Bit Capture Default",
.access = SNDRV_CTL_ELEM_ACCESS_READ |
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = stm32_spdifrx_ub_info,
.get = stm32_spdif_user_bits_get,
},
};
static struct snd_kcontrol_new stm32_spdifrx_ctrls[] = {
SOC_ENUM("SPDIFRX input", ctrl_enum_input),
SOC_ENUM("SPDIFRX CS channel", ctrl_enum_cs_channel),
};
static int stm32_spdifrx_dai_register_ctrls(struct snd_soc_dai *cpu_dai)
{
int ret;
ret = snd_soc_add_dai_controls(cpu_dai, stm32_spdifrx_iec_ctrls,
ARRAY_SIZE(stm32_spdifrx_iec_ctrls));
if (ret < 0)
return ret;
return snd_soc_add_component_controls(cpu_dai->component,
stm32_spdifrx_ctrls,
ARRAY_SIZE(stm32_spdifrx_ctrls));
}
static int stm32_spdifrx_dai_probe(struct snd_soc_dai *cpu_dai)
{
struct stm32_spdifrx_data *spdifrx = dev_get_drvdata(cpu_dai->dev);
spdifrx->dma_params.addr = (dma_addr_t)(spdifrx->phys_addr +
STM32_SPDIFRX_DR);
spdifrx->dma_params.maxburst = 1;
snd_soc_dai_init_dma_data(cpu_dai, NULL, &spdifrx->dma_params);
return stm32_spdifrx_dai_register_ctrls(cpu_dai);
}
static bool stm32_spdifrx_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case STM32_SPDIFRX_CR:
case STM32_SPDIFRX_IMR:
case STM32_SPDIFRX_SR:
case STM32_SPDIFRX_IFCR:
case STM32_SPDIFRX_DR:
case STM32_SPDIFRX_CSR:
case STM32_SPDIFRX_DIR:
return true;
default:
return false;
}
}
static bool stm32_spdifrx_volatile_reg(struct device *dev, unsigned int reg)
{
if (reg == STM32_SPDIFRX_DR)
return true;
return false;
}
static bool stm32_spdifrx_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case STM32_SPDIFRX_CR:
case STM32_SPDIFRX_IMR:
case STM32_SPDIFRX_IFCR:
return true;
default:
return false;
}
}
static const struct regmap_config stm32_h7_spdifrx_regmap_conf = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = STM32_SPDIFRX_DIR,
.readable_reg = stm32_spdifrx_readable_reg,
.volatile_reg = stm32_spdifrx_volatile_reg,
.writeable_reg = stm32_spdifrx_writeable_reg,
.fast_io = true,
};
static irqreturn_t stm32_spdifrx_isr(int irq, void *devid)
{
struct stm32_spdifrx_data *spdifrx = (struct stm32_spdifrx_data *)devid;
struct snd_pcm_substream *substream = spdifrx->substream;
struct platform_device *pdev = spdifrx->pdev;
unsigned int cr, mask, sr, imr;
unsigned int flags;
int err = 0, err_xrun = 0;
regmap_read(spdifrx->regmap, STM32_SPDIFRX_SR, &sr);
regmap_read(spdifrx->regmap, STM32_SPDIFRX_IMR, &imr);
mask = imr & SPDIFRX_XIMR_MASK;
/* SERR, TERR, FERR IRQs are generated if IFEIE is set */
if (mask & SPDIFRX_IMR_IFEIE)
mask |= (SPDIFRX_IMR_IFEIE << 1) | (SPDIFRX_IMR_IFEIE << 2);
flags = sr & mask;
if (!flags) {
dev_err(&pdev->dev, "Unexpected IRQ. rflags=%#x, imr=%#x\n",
sr, imr);
return IRQ_NONE;
}
/* Clear IRQs */
regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_IFCR,
SPDIFRX_XIFCR_MASK, flags);
if (flags & SPDIFRX_SR_PERR) {
dev_dbg(&pdev->dev, "Parity error\n");
err_xrun = 1;
}
if (flags & SPDIFRX_SR_OVR) {
dev_dbg(&pdev->dev, "Overrun error\n");
err_xrun = 1;
}
if (flags & SPDIFRX_SR_SBD)
dev_dbg(&pdev->dev, "Synchronization block detected\n");
if (flags & SPDIFRX_SR_SYNCD) {
dev_dbg(&pdev->dev, "Synchronization done\n");
/* Enable spdifrx */
cr = SPDIFRX_CR_SPDIFENSET(SPDIFRX_SPDIFEN_ENABLE);
regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR,
SPDIFRX_CR_SPDIFEN_MASK, cr);
}
if (flags & SPDIFRX_SR_FERR) {
dev_dbg(&pdev->dev, "Frame error\n");
err = 1;
}
if (flags & SPDIFRX_SR_SERR) {
dev_dbg(&pdev->dev, "Synchronization error\n");
err = 1;
}
if (flags & SPDIFRX_SR_TERR) {
dev_dbg(&pdev->dev, "Timeout error\n");
err = 1;
}
if (err) {
/* SPDIFRX in STATE_STOP. Disable SPDIFRX to clear errors */
cr = SPDIFRX_CR_SPDIFENSET(SPDIFRX_SPDIFEN_DISABLE);
regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR,
SPDIFRX_CR_SPDIFEN_MASK, cr);
if (substream)
snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
return IRQ_HANDLED;
}
if (err_xrun && substream)
snd_pcm_stop_xrun(substream);
return IRQ_HANDLED;
}
static int stm32_spdifrx_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
struct stm32_spdifrx_data *spdifrx = snd_soc_dai_get_drvdata(cpu_dai);
int ret;
spdifrx->substream = substream;
ret = clk_prepare_enable(spdifrx->kclk);
if (ret)
dev_err(&spdifrx->pdev->dev, "Enable kclk failed: %d\n", ret);
return ret;
}
static int stm32_spdifrx_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *cpu_dai)
{
struct stm32_spdifrx_data *spdifrx = snd_soc_dai_get_drvdata(cpu_dai);
int data_size = params_width(params);
int fmt;
switch (data_size) {
case 16:
fmt = SPDIFRX_DRFMT_PACKED;
spdifrx->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
break;
case 32:
fmt = SPDIFRX_DRFMT_LEFT;
spdifrx->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
break;
default:
dev_err(&spdifrx->pdev->dev, "Unexpected data format\n");
return -EINVAL;
}
snd_soc_dai_init_dma_data(cpu_dai, NULL, &spdifrx->dma_params);
return regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR,
SPDIFRX_CR_DRFMT_MASK,
SPDIFRX_CR_DRFMTSET(fmt));
}
static int stm32_spdifrx_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *cpu_dai)
{
struct stm32_spdifrx_data *spdifrx = snd_soc_dai_get_drvdata(cpu_dai);
int ret = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_IMR,
SPDIFRX_IMR_OVRIE, SPDIFRX_IMR_OVRIE);
regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR,
SPDIFRX_CR_RXDMAEN, SPDIFRX_CR_RXDMAEN);
ret = stm32_spdifrx_start_sync(spdifrx);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_STOP:
stm32_spdifrx_stop(spdifrx);
break;
default:
return -EINVAL;
}
return ret;
}
static void stm32_spdifrx_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
struct stm32_spdifrx_data *spdifrx = snd_soc_dai_get_drvdata(cpu_dai);
spdifrx->substream = NULL;
clk_disable_unprepare(spdifrx->kclk);
}
static const struct snd_soc_dai_ops stm32_spdifrx_pcm_dai_ops = {
.startup = stm32_spdifrx_startup,
.hw_params = stm32_spdifrx_hw_params,
.trigger = stm32_spdifrx_trigger,
.shutdown = stm32_spdifrx_shutdown,
};
static struct snd_soc_dai_driver stm32_spdifrx_dai[] = {
{
.name = "spdifrx-capture-cpu-dai",
.probe = stm32_spdifrx_dai_probe,
.capture = {
.stream_name = "CPU-Capture",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S32_LE |
SNDRV_PCM_FMTBIT_S16_LE,
},
.ops = &stm32_spdifrx_pcm_dai_ops,
}
};
static const struct snd_pcm_hardware stm32_spdifrx_pcm_hw = {
.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP,
.buffer_bytes_max = 8 * PAGE_SIZE,
.period_bytes_max = 2048, /* MDMA constraint */
.periods_min = 2,
.periods_max = 8,
};
static const struct snd_soc_component_driver stm32_spdifrx_component = {
.name = "stm32-spdifrx",
};
static const struct snd_dmaengine_pcm_config stm32_spdifrx_pcm_config = {
.pcm_hardware = &stm32_spdifrx_pcm_hw,
.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
};
static const struct of_device_id stm32_spdifrx_ids[] = {
{
.compatible = "st,stm32h7-spdifrx",
.data = &stm32_h7_spdifrx_regmap_conf
},
{}
};
static int stm_spdifrx_parse_of(struct platform_device *pdev,
struct stm32_spdifrx_data *spdifrx)
{
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *of_id;
struct resource *res;
if (!np)
return -ENODEV;
of_id = of_match_device(stm32_spdifrx_ids, &pdev->dev);
if (of_id)
spdifrx->regmap_conf =
(const struct regmap_config *)of_id->data;
else
return -EINVAL;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
spdifrx->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(spdifrx->base))
return PTR_ERR(spdifrx->base);
spdifrx->phys_addr = res->start;
spdifrx->kclk = devm_clk_get(&pdev->dev, "kclk");
if (IS_ERR(spdifrx->kclk)) {
dev_err(&pdev->dev, "Could not get kclk\n");
return PTR_ERR(spdifrx->kclk);
}
spdifrx->irq = platform_get_irq(pdev, 0);
if (spdifrx->irq < 0) {
dev_err(&pdev->dev, "No irq for node %s\n", pdev->name);
return spdifrx->irq;
}
return 0;
}
static int stm32_spdifrx_probe(struct platform_device *pdev)
{
struct stm32_spdifrx_data *spdifrx;
struct reset_control *rst;
const struct snd_dmaengine_pcm_config *pcm_config = NULL;
int ret;
spdifrx = devm_kzalloc(&pdev->dev, sizeof(*spdifrx), GFP_KERNEL);
if (!spdifrx)
return -ENOMEM;
spdifrx->pdev = pdev;
init_completion(&spdifrx->cs_completion);
spin_lock_init(&spdifrx->lock);
platform_set_drvdata(pdev, spdifrx);
ret = stm_spdifrx_parse_of(pdev, spdifrx);
if (ret)
return ret;
spdifrx->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "kclk",
spdifrx->base,
spdifrx->regmap_conf);
if (IS_ERR(spdifrx->regmap)) {
dev_err(&pdev->dev, "Regmap init failed\n");
return PTR_ERR(spdifrx->regmap);
}
ret = devm_request_irq(&pdev->dev, spdifrx->irq, stm32_spdifrx_isr, 0,
dev_name(&pdev->dev), spdifrx);
if (ret) {
dev_err(&pdev->dev, "IRQ request returned %d\n", ret);
return ret;
}
rst = devm_reset_control_get(&pdev->dev, NULL);
if (!IS_ERR(rst)) {
reset_control_assert(rst);
udelay(2);
reset_control_deassert(rst);
}
ret = devm_snd_soc_register_component(&pdev->dev,
&stm32_spdifrx_component,
stm32_spdifrx_dai,
ARRAY_SIZE(stm32_spdifrx_dai));
if (ret)
return ret;
ret = stm32_spdifrx_dma_ctrl_register(&pdev->dev, spdifrx);
if (ret)
goto error;
pcm_config = &stm32_spdifrx_pcm_config;
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, pcm_config, 0);
if (ret) {
dev_err(&pdev->dev, "PCM DMA register returned %d\n", ret);
goto error;
}
return 0;
error:
if (spdifrx->ctrl_chan)
dma_release_channel(spdifrx->ctrl_chan);
if (spdifrx->dmab)
snd_dma_free_pages(spdifrx->dmab);
return ret;
}
static int stm32_spdifrx_remove(struct platform_device *pdev)
{
struct stm32_spdifrx_data *spdifrx = platform_get_drvdata(pdev);
if (spdifrx->ctrl_chan)
dma_release_channel(spdifrx->ctrl_chan);
if (spdifrx->dmab)
snd_dma_free_pages(spdifrx->dmab);
return 0;
}
MODULE_DEVICE_TABLE(of, stm32_spdifrx_ids);
static struct platform_driver stm32_spdifrx_driver = {
.driver = {
.name = "st,stm32-spdifrx",
.of_match_table = stm32_spdifrx_ids,
},
.probe = stm32_spdifrx_probe,
.remove = stm32_spdifrx_remove,
};
module_platform_driver(stm32_spdifrx_driver);
MODULE_DESCRIPTION("STM32 Soc spdifrx Interface");
MODULE_AUTHOR("Olivier Moysan, <olivier.moysan@st.com>");
MODULE_ALIAS("platform:stm32-spdifrx");
MODULE_LICENSE("GPL v2");

View File

@ -1339,6 +1339,44 @@ static struct snd_soc_card *sun8i_h3_codec_create_card(struct device *dev)
return card;
};
static struct snd_soc_card *sun8i_v3s_codec_create_card(struct device *dev)
{
struct snd_soc_card *card;
int ret;
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card)
return ERR_PTR(-ENOMEM);
aux_dev.codec_of_node = of_parse_phandle(dev->of_node,
"allwinner,codec-analog-controls",
0);
if (!aux_dev.codec_of_node) {
dev_err(dev, "Can't find analog controls for codec.\n");
return ERR_PTR(-EINVAL);
};
card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
if (!card->dai_link)
return ERR_PTR(-ENOMEM);
card->dev = dev;
card->name = "V3s Audio Codec";
card->dapm_widgets = sun6i_codec_card_dapm_widgets;
card->num_dapm_widgets = ARRAY_SIZE(sun6i_codec_card_dapm_widgets);
card->dapm_routes = sun8i_codec_card_routes;
card->num_dapm_routes = ARRAY_SIZE(sun8i_codec_card_routes);
card->aux_dev = &aux_dev;
card->num_aux_devs = 1;
card->fully_routed = true;
ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing");
if (ret)
dev_warn(dev, "failed to parse audio-routing: %d\n", ret);
return card;
};
static const struct regmap_config sun4i_codec_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
@ -1374,6 +1412,13 @@ static const struct regmap_config sun8i_h3_codec_regmap_config = {
.max_register = SUN8I_H3_CODEC_ADC_DBG,
};
static const struct regmap_config sun8i_v3s_codec_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = SUN8I_H3_CODEC_ADC_DBG,
};
struct sun4i_codec_quirks {
const struct regmap_config *regmap_config;
const struct snd_soc_codec_driver *codec;
@ -1437,6 +1482,20 @@ static const struct sun4i_codec_quirks sun8i_h3_codec_quirks = {
.has_reset = true,
};
static const struct sun4i_codec_quirks sun8i_v3s_codec_quirks = {
.regmap_config = &sun8i_v3s_codec_regmap_config,
/*
* TODO The codec structure should be split out, like
* H3, when adding digital audio processing support.
*/
.codec = &sun8i_a23_codec_codec,
.create_card = sun8i_v3s_codec_create_card,
.reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31),
.reg_dac_txdata = SUN8I_H3_CODEC_DAC_TXDATA,
.reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA,
.has_reset = true,
};
static const struct of_device_id sun4i_codec_of_match[] = {
{
.compatible = "allwinner,sun4i-a10-codec",
@ -1458,6 +1517,10 @@ static const struct of_device_id sun4i_codec_of_match[] = {
.compatible = "allwinner,sun8i-h3-codec",
.data = &sun8i_h3_codec_quirks,
},
{
.compatible = "allwinner,sun8i-v3s-codec",
.data = &sun8i_v3s_codec_quirks,
},
{}
};
MODULE_DEVICE_TABLE(of, sun4i_codec_of_match);

View File

@ -219,6 +219,22 @@ static const struct snd_kcontrol_new sun8i_codec_mixer_controls[] = {
SUN8I_ADDA_LOMIXSC_MIC2, 1, 0),
};
/* mixer controls */
static const struct snd_kcontrol_new sun8i_v3s_codec_mixer_controls[] = {
SOC_DAPM_DOUBLE_R("DAC Playback Switch",
SUN8I_ADDA_LOMIXSC,
SUN8I_ADDA_ROMIXSC,
SUN8I_ADDA_LOMIXSC_DACL, 1, 0),
SOC_DAPM_DOUBLE_R("DAC Reversed Playback Switch",
SUN8I_ADDA_LOMIXSC,
SUN8I_ADDA_ROMIXSC,
SUN8I_ADDA_LOMIXSC_DACR, 1, 0),
SOC_DAPM_DOUBLE_R("Mic1 Playback Switch",
SUN8I_ADDA_LOMIXSC,
SUN8I_ADDA_ROMIXSC,
SUN8I_ADDA_LOMIXSC_MIC1, 1, 0),
};
/* ADC mixer controls */
static const struct snd_kcontrol_new sun8i_codec_adc_mixer_controls[] = {
SOC_DAPM_DOUBLE_R("Mixer Capture Switch",
@ -243,6 +259,22 @@ static const struct snd_kcontrol_new sun8i_codec_adc_mixer_controls[] = {
SUN8I_ADDA_LADCMIXSC_MIC2, 1, 0),
};
/* ADC mixer controls */
static const struct snd_kcontrol_new sun8i_v3s_codec_adc_mixer_controls[] = {
SOC_DAPM_DOUBLE_R("Mixer Capture Switch",
SUN8I_ADDA_LADCMIXSC,
SUN8I_ADDA_RADCMIXSC,
SUN8I_ADDA_LADCMIXSC_OMIXRL, 1, 0),
SOC_DAPM_DOUBLE_R("Mixer Reversed Capture Switch",
SUN8I_ADDA_LADCMIXSC,
SUN8I_ADDA_RADCMIXSC,
SUN8I_ADDA_LADCMIXSC_OMIXRR, 1, 0),
SOC_DAPM_DOUBLE_R("Mic1 Capture Switch",
SUN8I_ADDA_LADCMIXSC,
SUN8I_ADDA_RADCMIXSC,
SUN8I_ADDA_LADCMIXSC_MIC1, 1, 0),
};
/* volume / mute controls */
static const DECLARE_TLV_DB_SCALE(sun8i_codec_out_mixer_pregain_scale,
-450, 150, 0);
@ -289,16 +321,12 @@ static const struct snd_soc_dapm_widget sun8i_codec_common_widgets[] = {
/* Microphone input */
SND_SOC_DAPM_INPUT("MIC1"),
/* Microphone Bias */
SND_SOC_DAPM_SUPPLY("MBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MMICBIASEN,
0, NULL, 0),
/* Mic input path */
SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1AMPEN, 0, NULL, 0),
};
/* Mixers */
static const struct snd_soc_dapm_widget sun8i_codec_mixer_widgets[] = {
SND_SOC_DAPM_MIXER("Left Mixer", SUN8I_ADDA_DAC_PA_SRC,
SUN8I_ADDA_DAC_PA_SRC_LMIXEN, 0,
sun8i_codec_mixer_controls,
@ -317,10 +345,31 @@ static const struct snd_soc_dapm_widget sun8i_codec_common_widgets[] = {
ARRAY_SIZE(sun8i_codec_adc_mixer_controls)),
};
static const struct snd_soc_dapm_widget sun8i_v3s_codec_mixer_widgets[] = {
SND_SOC_DAPM_MIXER("Left Mixer", SUN8I_ADDA_DAC_PA_SRC,
SUN8I_ADDA_DAC_PA_SRC_LMIXEN, 0,
sun8i_v3s_codec_mixer_controls,
ARRAY_SIZE(sun8i_v3s_codec_mixer_controls)),
SND_SOC_DAPM_MIXER("Right Mixer", SUN8I_ADDA_DAC_PA_SRC,
SUN8I_ADDA_DAC_PA_SRC_RMIXEN, 0,
sun8i_v3s_codec_mixer_controls,
ARRAY_SIZE(sun8i_v3s_codec_mixer_controls)),
SND_SOC_DAPM_MIXER("Left ADC Mixer", SUN8I_ADDA_ADC_AP_EN,
SUN8I_ADDA_ADC_AP_EN_ADCLEN, 0,
sun8i_v3s_codec_adc_mixer_controls,
ARRAY_SIZE(sun8i_v3s_codec_adc_mixer_controls)),
SND_SOC_DAPM_MIXER("Right ADC Mixer", SUN8I_ADDA_ADC_AP_EN,
SUN8I_ADDA_ADC_AP_EN_ADCREN, 0,
sun8i_v3s_codec_adc_mixer_controls,
ARRAY_SIZE(sun8i_v3s_codec_adc_mixer_controls)),
};
static const struct snd_soc_dapm_route sun8i_codec_common_routes[] = {
/* Microphone Routes */
{ "Mic1 Amplifier", NULL, "MIC1"},
};
static const struct snd_soc_dapm_route sun8i_codec_mixer_routes[] = {
/* Left Mixer Routes */
{ "Left Mixer", "DAC Playback Switch", "Left DAC" },
{ "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" },
@ -453,6 +502,27 @@ static int sun8i_codec_add_headphone(struct snd_soc_component *cmpnt)
return 0;
}
/* mbias specific widget */
static const struct snd_soc_dapm_widget sun8i_codec_mbias_widgets[] = {
SND_SOC_DAPM_SUPPLY("MBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MMICBIASEN,
0, NULL, 0),
};
static int sun8i_codec_add_mbias(struct snd_soc_component *cmpnt)
{
struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
struct device *dev = cmpnt->dev;
int ret;
ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_mbias_widgets,
ARRAY_SIZE(sun8i_codec_mbias_widgets));
if (ret)
dev_err(dev, "Failed to add MBIAS DAPM widgets: %d\n", ret);
return ret;
}
/* hmic specific widget */
static const struct snd_soc_dapm_widget sun8i_codec_hmic_widgets[] = {
SND_SOC_DAPM_SUPPLY("HBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
@ -679,6 +749,7 @@ struct sun8i_codec_analog_quirks {
bool has_hmic;
bool has_linein;
bool has_lineout;
bool has_mbias;
bool has_mic2;
};
@ -686,15 +757,64 @@ static const struct sun8i_codec_analog_quirks sun8i_a23_quirks = {
.has_headphone = true,
.has_hmic = true,
.has_linein = true,
.has_mbias = true,
.has_mic2 = true,
};
static const struct sun8i_codec_analog_quirks sun8i_h3_quirks = {
.has_linein = true,
.has_lineout = true,
.has_mbias = true,
.has_mic2 = true,
};
static int sun8i_codec_analog_add_mixer(struct snd_soc_component *cmpnt,
const struct sun8i_codec_analog_quirks *quirks)
{
struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
struct device *dev = cmpnt->dev;
int ret;
if (!quirks->has_mic2 && !quirks->has_linein) {
/*
* Apply the special widget set which has uses a control
* without MIC2 and Line In, for SoCs without these.
* TODO: not all special cases are supported now, this case
* is present because it's the case of V3s.
*/
ret = snd_soc_dapm_new_controls(dapm,
sun8i_v3s_codec_mixer_widgets,
ARRAY_SIZE(sun8i_v3s_codec_mixer_widgets));
if (ret) {
dev_err(dev, "Failed to add V3s Mixer DAPM widgets: %d\n", ret);
return ret;
}
} else {
/* Apply the generic mixer widget set. */
ret = snd_soc_dapm_new_controls(dapm,
sun8i_codec_mixer_widgets,
ARRAY_SIZE(sun8i_codec_mixer_widgets));
if (ret) {
dev_err(dev, "Failed to add Mixer DAPM widgets: %d\n", ret);
return ret;
}
}
ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_mixer_routes,
ARRAY_SIZE(sun8i_codec_mixer_routes));
if (ret) {
dev_err(dev, "Failed to add Mixer DAPM routes: %d\n", ret);
return ret;
}
return 0;
}
static const struct sun8i_codec_analog_quirks sun8i_v3s_quirks = {
.has_headphone = true,
.has_hmic = true,
};
static int sun8i_codec_analog_cmpnt_probe(struct snd_soc_component *cmpnt)
{
struct device *dev = cmpnt->dev;
@ -709,6 +829,9 @@ static int sun8i_codec_analog_cmpnt_probe(struct snd_soc_component *cmpnt)
quirks = of_device_get_match_data(dev);
/* Add controls, widgets, and routes for individual features */
ret = sun8i_codec_analog_add_mixer(cmpnt, quirks);
if (ret)
return ret;
if (quirks->has_headphone) {
ret = sun8i_codec_add_headphone(cmpnt);
@ -734,6 +857,12 @@ static int sun8i_codec_analog_cmpnt_probe(struct snd_soc_component *cmpnt)
return ret;
}
if (quirks->has_mbias) {
ret = sun8i_codec_add_mbias(cmpnt);
if (ret)
return ret;
}
if (quirks->has_mic2) {
ret = sun8i_codec_add_mic2(cmpnt);
if (ret)
@ -762,6 +891,10 @@ static const struct of_device_id sun8i_codec_analog_of_match[] = {
.compatible = "allwinner,sun8i-h3-codec-analog",
.data = &sun8i_h3_quirks,
},
{
.compatible = "allwinner,sun8i-v3s-codec-analog",
.data = &sun8i_v3s_quirks,
},
{}
};
MODULE_DEVICE_TABLE(of, sun8i_codec_analog_of_match);