You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge remote-tracking branches 'asoc/topic/stm32', 'asoc/topic/sunxi', 'asoc/topic/tlv320dac31xx', 'asoc/topic/topology' and 'asoc/topic/wm-adsp' into asoc-next
This commit is contained in:
@@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -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";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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>;
|
||||
};
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 *);
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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];
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
+26
-3
@@ -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
|
||||
|
||||
+10
-2
@@ -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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
+107
-34
@@ -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");
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user