diff options
-rw-r--r-- | Documentation/devicetree/bindings/sound/st,stm32-sai.txt | 89 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/sound/tas2552.txt | 10 | ||||
-rw-r--r-- | sound/soc/Kconfig | 1 | ||||
-rw-r--r-- | sound/soc/Makefile | 1 | ||||
-rw-r--r-- | sound/soc/codecs/sta529.c | 7 | ||||
-rw-r--r-- | sound/soc/codecs/tas2552.c | 6 | ||||
-rw-r--r-- | sound/soc/sti/uniperif_player.c | 2 | ||||
-rw-r--r-- | sound/soc/stm/Kconfig | 8 | ||||
-rw-r--r-- | sound/soc/stm/Makefile | 6 | ||||
-rw-r--r-- | sound/soc/stm/stm32_sai.c | 115 | ||||
-rw-r--r-- | sound/soc/stm/stm32_sai.h | 200 | ||||
-rw-r--r-- | sound/soc/stm/stm32_sai_sub.c | 884 | ||||
-rw-r--r-- | sound/soc/sunxi/sun8i-codec-analog.c | 168 | ||||
-rw-r--r-- | sound/soc/sunxi/sun8i-codec.c | 10 |
14 files changed, 1467 insertions, 40 deletions
diff --git a/Documentation/devicetree/bindings/sound/st,stm32-sai.txt b/Documentation/devicetree/bindings/sound/st,stm32-sai.txt new file mode 100644 index 000000000000..c59a3d779e06 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/st,stm32-sai.txt | |||
@@ -0,0 +1,89 @@ | |||
1 | STMicroelectronics STM32 Serial Audio Interface (SAI). | ||
2 | |||
3 | The SAI interface (Serial Audio Interface) offers a wide set of audio protocols | ||
4 | as I2S standards, LSB or MSB-justified, PCM/DSP, TDM, and AC'97. | ||
5 | The SAI contains two independent audio sub-blocks. Each sub-block has | ||
6 | its own clock generator and I/O lines controller. | ||
7 | |||
8 | Required properties: | ||
9 | - compatible: Should be "st,stm32f4-sai" | ||
10 | - reg: Base address and size of SAI common register set. | ||
11 | - clocks: Must contain phandle and clock specifier pairs for each entry | ||
12 | in clock-names. | ||
13 | - clock-names: Must contain "x8k" and "x11k" | ||
14 | "x8k": SAI parent clock for sampling rates multiple of 8kHz. | ||
15 | "x11k": SAI parent clock for sampling rates multiple of 11.025kHz. | ||
16 | - interrupts: cpu DAI interrupt line shared by SAI sub-blocks | ||
17 | |||
18 | Optional properties: | ||
19 | - resets: Reference to a reset controller asserting the SAI | ||
20 | |||
21 | SAI subnodes: | ||
22 | Two subnodes corresponding to SAI sub-block instances A et B can be defined. | ||
23 | Subnode can be omitted for unsused sub-block. | ||
24 | |||
25 | SAI subnodes required properties: | ||
26 | - compatible: Should be "st,stm32-sai-sub-a" or "st,stm32-sai-sub-b" | ||
27 | for SAI sub-block A or B respectively. | ||
28 | - reg: Base address and size of SAI sub-block register set. | ||
29 | - clocks: Must contain one phandle and clock specifier pair | ||
30 | for sai_ck which feeds the internal clock generator. | ||
31 | - clock-names: Must contain "sai_ck". | ||
32 | - dmas: see Documentation/devicetree/bindings/dma/stm32-dma.txt | ||
33 | - dma-names: identifier string for each DMA request line | ||
34 | "tx": if sai sub-block is configured as playback DAI | ||
35 | "rx": if sai sub-block is configured as capture DAI | ||
36 | - pinctrl-names: should contain only value "default" | ||
37 | - pinctrl-0: see Documentation/devicetree/bindings/pinctrl/pinctrl-stm32.txt | ||
38 | |||
39 | Example: | ||
40 | sound_card { | ||
41 | compatible = "audio-graph-card"; | ||
42 | dais = <&sai1b_port>; | ||
43 | }; | ||
44 | |||
45 | sai1: sai1@40015800 { | ||
46 | compatible = "st,stm32f4-sai"; | ||
47 | #address-cells = <1>; | ||
48 | #size-cells = <1>; | ||
49 | ranges; | ||
50 | reg = <0x40015800 0x4>; | ||
51 | clocks = <&rcc 1 CLK_SAIQ_PDIV>, <&rcc 1 CLK_I2SQ_PDIV>; | ||
52 | clock-names = "x8k", "x11k"; | ||
53 | interrupts = <87>; | ||
54 | |||
55 | sai1b: audio-controller@40015824 { | ||
56 | #sound-dai-cells = <0>; | ||
57 | compatible = "st,stm32-sai-sub-b"; | ||
58 | reg = <0x40015824 0x1C>; | ||
59 | clocks = <&rcc 1 CLK_SAI2>; | ||
60 | clock-names = "sai_ck"; | ||
61 | dmas = <&dma2 5 0 0x400 0x0>; | ||
62 | dma-names = "tx"; | ||
63 | pinctrl-names = "default"; | ||
64 | pinctrl-0 = <&pinctrl_sai1b>; | ||
65 | |||
66 | ports { | ||
67 | #address-cells = <1>; | ||
68 | #size-cells = <0>; | ||
69 | |||
70 | sai1b_port: port@0 { | ||
71 | reg = <0>; | ||
72 | cpu_endpoint: endpoint { | ||
73 | remote-endpoint = <&codec_endpoint>; | ||
74 | audio-graph-card,format = "i2s"; | ||
75 | audio-graph-card,bitclock-master = <&codec_endpoint>; | ||
76 | audio-graph-card,frame-master = <&codec_endpoint>; | ||
77 | }; | ||
78 | }; | ||
79 | }; | ||
80 | }; | ||
81 | }; | ||
82 | |||
83 | audio-codec { | ||
84 | codec_port: port { | ||
85 | codec_endpoint: endpoint { | ||
86 | remote-endpoint = <&cpu_endpoint>; | ||
87 | }; | ||
88 | }; | ||
89 | }; | ||
diff --git a/Documentation/devicetree/bindings/sound/tas2552.txt b/Documentation/devicetree/bindings/sound/tas2552.txt index c49992c0b62a..2d71eb05c1d3 100644 --- a/Documentation/devicetree/bindings/sound/tas2552.txt +++ b/Documentation/devicetree/bindings/sound/tas2552.txt | |||
@@ -5,7 +5,8 @@ The tas2552 serial control bus communicates through I2C protocols | |||
5 | Required properties: | 5 | Required properties: |
6 | - compatible - One of: | 6 | - compatible - One of: |
7 | "ti,tas2552" - TAS2552 | 7 | "ti,tas2552" - TAS2552 |
8 | - reg - I2C slave address | 8 | - reg - I2C slave address: it can be 0x40 if ADDR pin is 0 |
9 | or 0x41 if ADDR pin is 1. | ||
9 | - supply-*: Required supply regulators are: | 10 | - supply-*: Required supply regulators are: |
10 | "vbat" battery voltage | 11 | "vbat" battery voltage |
11 | "iovdd" I/O Voltage | 12 | "iovdd" I/O Voltage |
@@ -14,17 +15,20 @@ Required properties: | |||
14 | Optional properties: | 15 | Optional properties: |
15 | - enable-gpio - gpio pin to enable/disable the device | 16 | - enable-gpio - gpio pin to enable/disable the device |
16 | 17 | ||
17 | tas2552 can receive it's reference clock via MCLK, BCLK, IVCLKIN pin or use the | 18 | tas2552 can receive its reference clock via MCLK, BCLK, IVCLKIN pin or use the |
18 | internal 1.8MHz. This CLKIN is used by the PLL. In addition to PLL, the PDM | 19 | internal 1.8MHz. This CLKIN is used by the PLL. In addition to PLL, the PDM |
19 | reference clock is also selectable: PLL, IVCLKIN, BCLK or MCLK. | 20 | reference clock is also selectable: PLL, IVCLKIN, BCLK or MCLK. |
20 | For system integration the dt-bindings/sound/tas2552.h header file provides | 21 | For system integration the dt-bindings/sound/tas2552.h header file provides |
21 | defined values to selct and configure the PLL and PDM reference clocks. | 22 | defined values to select and configure the PLL and PDM reference clocks. |
22 | 23 | ||
23 | Example: | 24 | Example: |
24 | 25 | ||
25 | tas2552: tas2552@41 { | 26 | tas2552: tas2552@41 { |
26 | compatible = "ti,tas2552"; | 27 | compatible = "ti,tas2552"; |
27 | reg = <0x41>; | 28 | reg = <0x41>; |
29 | vbat-supply = <®_vbat>; | ||
30 | iovdd-supply = <®_iovdd>; | ||
31 | avdd-supply = <®_avdd>; | ||
28 | enable-gpio = <&gpio4 2 GPIO_ACTIVE_HIGH>; | 32 | enable-gpio = <&gpio4 2 GPIO_ACTIVE_HIGH>; |
29 | }; | 33 | }; |
30 | 34 | ||
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 9df9658b552b..c0abad2067e1 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig | |||
@@ -64,6 +64,7 @@ source "sound/soc/sh/Kconfig" | |||
64 | source "sound/soc/sirf/Kconfig" | 64 | source "sound/soc/sirf/Kconfig" |
65 | source "sound/soc/spear/Kconfig" | 65 | source "sound/soc/spear/Kconfig" |
66 | source "sound/soc/sti/Kconfig" | 66 | source "sound/soc/sti/Kconfig" |
67 | source "sound/soc/stm/Kconfig" | ||
67 | source "sound/soc/sunxi/Kconfig" | 68 | source "sound/soc/sunxi/Kconfig" |
68 | source "sound/soc/tegra/Kconfig" | 69 | source "sound/soc/tegra/Kconfig" |
69 | source "sound/soc/txx9/Kconfig" | 70 | source "sound/soc/txx9/Kconfig" |
diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 2f6aabb8b4c3..39c27a58158d 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile | |||
@@ -44,6 +44,7 @@ obj-$(CONFIG_SND_SOC) += sh/ | |||
44 | obj-$(CONFIG_SND_SOC) += sirf/ | 44 | obj-$(CONFIG_SND_SOC) += sirf/ |
45 | obj-$(CONFIG_SND_SOC) += spear/ | 45 | obj-$(CONFIG_SND_SOC) += spear/ |
46 | obj-$(CONFIG_SND_SOC) += sti/ | 46 | obj-$(CONFIG_SND_SOC) += sti/ |
47 | obj-$(CONFIG_SND_SOC) += stm/ | ||
47 | obj-$(CONFIG_SND_SOC) += sunxi/ | 48 | obj-$(CONFIG_SND_SOC) += sunxi/ |
48 | obj-$(CONFIG_SND_SOC) += tegra/ | 49 | obj-$(CONFIG_SND_SOC) += tegra/ |
49 | obj-$(CONFIG_SND_SOC) += txx9/ | 50 | obj-$(CONFIG_SND_SOC) += txx9/ |
diff --git a/sound/soc/codecs/sta529.c b/sound/soc/codecs/sta529.c index d4b384e4b266..660734359bf3 100644 --- a/sound/soc/codecs/sta529.c +++ b/sound/soc/codecs/sta529.c | |||
@@ -375,9 +375,16 @@ static const struct i2c_device_id sta529_i2c_id[] = { | |||
375 | }; | 375 | }; |
376 | MODULE_DEVICE_TABLE(i2c, sta529_i2c_id); | 376 | MODULE_DEVICE_TABLE(i2c, sta529_i2c_id); |
377 | 377 | ||
378 | static const struct of_device_id sta529_of_match[] = { | ||
379 | { .compatible = "st,sta529", }, | ||
380 | { } | ||
381 | }; | ||
382 | MODULE_DEVICE_TABLE(of, sta529_of_match); | ||
383 | |||
378 | static struct i2c_driver sta529_i2c_driver = { | 384 | static struct i2c_driver sta529_i2c_driver = { |
379 | .driver = { | 385 | .driver = { |
380 | .name = "sta529", | 386 | .name = "sta529", |
387 | .of_match_table = sta529_of_match, | ||
381 | }, | 388 | }, |
382 | .probe = sta529_i2c_probe, | 389 | .probe = sta529_i2c_probe, |
383 | .remove = sta529_i2c_remove, | 390 | .remove = sta529_i2c_remove, |
diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c index baf455e8c2f7..8840f72f3c4a 100644 --- a/sound/soc/codecs/tas2552.c +++ b/sound/soc/codecs/tas2552.c | |||
@@ -611,7 +611,7 @@ probe_fail: | |||
611 | 611 | ||
612 | regulator_bulk_disable(ARRAY_SIZE(tas2552->supplies), | 612 | regulator_bulk_disable(ARRAY_SIZE(tas2552->supplies), |
613 | tas2552->supplies); | 613 | tas2552->supplies); |
614 | return -EIO; | 614 | return ret; |
615 | } | 615 | } |
616 | 616 | ||
617 | static int tas2552_codec_remove(struct snd_soc_codec *codec) | 617 | static int tas2552_codec_remove(struct snd_soc_codec *codec) |
@@ -637,7 +637,7 @@ static int tas2552_suspend(struct snd_soc_codec *codec) | |||
637 | if (ret != 0) | 637 | if (ret != 0) |
638 | dev_err(codec->dev, "Failed to disable supplies: %d\n", | 638 | dev_err(codec->dev, "Failed to disable supplies: %d\n", |
639 | ret); | 639 | ret); |
640 | return 0; | 640 | return ret; |
641 | } | 641 | } |
642 | 642 | ||
643 | static int tas2552_resume(struct snd_soc_codec *codec) | 643 | static int tas2552_resume(struct snd_soc_codec *codec) |
@@ -653,7 +653,7 @@ static int tas2552_resume(struct snd_soc_codec *codec) | |||
653 | ret); | 653 | ret); |
654 | } | 654 | } |
655 | 655 | ||
656 | return 0; | 656 | return ret; |
657 | } | 657 | } |
658 | #else | 658 | #else |
659 | #define tas2552_suspend NULL | 659 | #define tas2552_suspend NULL |
diff --git a/sound/soc/sti/uniperif_player.c b/sound/soc/sti/uniperif_player.c index d7e8dd46d2cc..d8b6936e544e 100644 --- a/sound/soc/sti/uniperif_player.c +++ b/sound/soc/sti/uniperif_player.c | |||
@@ -1074,7 +1074,7 @@ int uni_player_init(struct platform_device *pdev, | |||
1074 | player->clk = of_clk_get(pdev->dev.of_node, 0); | 1074 | player->clk = of_clk_get(pdev->dev.of_node, 0); |
1075 | if (IS_ERR(player->clk)) { | 1075 | if (IS_ERR(player->clk)) { |
1076 | dev_err(player->dev, "Failed to get clock\n"); | 1076 | dev_err(player->dev, "Failed to get clock\n"); |
1077 | ret = PTR_ERR(player->clk); | 1077 | return PTR_ERR(player->clk); |
1078 | } | 1078 | } |
1079 | 1079 | ||
1080 | /* Select the frequency synthesizer clock */ | 1080 | /* Select the frequency synthesizer clock */ |
diff --git a/sound/soc/stm/Kconfig b/sound/soc/stm/Kconfig new file mode 100644 index 000000000000..972970f0890a --- /dev/null +++ b/sound/soc/stm/Kconfig | |||
@@ -0,0 +1,8 @@ | |||
1 | menuconfig SND_SOC_STM32 | ||
2 | tristate "STMicroelectronics STM32 SOC audio support" | ||
3 | depends on ARCH_STM32 || COMPILE_TEST | ||
4 | depends on SND_SOC | ||
5 | select SND_SOC_GENERIC_DMAENGINE_PCM | ||
6 | select REGMAP_MMIO | ||
7 | help | ||
8 | Say Y if you want to enable ASoC-support for STM32 | ||
diff --git a/sound/soc/stm/Makefile b/sound/soc/stm/Makefile new file mode 100644 index 000000000000..e466a4759698 --- /dev/null +++ b/sound/soc/stm/Makefile | |||
@@ -0,0 +1,6 @@ | |||
1 | # SAI | ||
2 | snd-soc-stm32-sai-sub-objs := stm32_sai_sub.o | ||
3 | obj-$(CONFIG_SND_SOC_STM32) += snd-soc-stm32-sai-sub.o | ||
4 | |||
5 | snd-soc-stm32-sai-objs := stm32_sai.o | ||
6 | obj-$(CONFIG_SND_SOC_STM32) += snd-soc-stm32-sai.o | ||
diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c new file mode 100644 index 000000000000..2a27a26bf7a1 --- /dev/null +++ b/sound/soc/stm/stm32_sai.c | |||
@@ -0,0 +1,115 @@ | |||
1 | /* | ||
2 | * STM32 ALSA SoC Digital Audio Interface (SAI) driver. | ||
3 | * | ||
4 | * Copyright (C) 2016, STMicroelectronics - All Rights Reserved | ||
5 | * Author(s): Olivier Moysan <olivier.moysan@st.com> for STMicroelectronics. | ||
6 | * | ||
7 | * License terms: GPL V2.0. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify it | ||
10 | * under the terms of the GNU General Public License version 2 as published by | ||
11 | * the Free Software Foundation. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, but | ||
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more | ||
16 | * details. | ||
17 | */ | ||
18 | |||
19 | #include <linux/clk.h> | ||
20 | #include <linux/delay.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/of_platform.h> | ||
23 | #include <linux/reset.h> | ||
24 | |||
25 | #include <sound/dmaengine_pcm.h> | ||
26 | #include <sound/core.h> | ||
27 | |||
28 | #include "stm32_sai.h" | ||
29 | |||
30 | static const struct of_device_id stm32_sai_ids[] = { | ||
31 | { .compatible = "st,stm32f4-sai", .data = (void *)SAI_STM32F4 }, | ||
32 | {} | ||
33 | }; | ||
34 | |||
35 | static int stm32_sai_probe(struct platform_device *pdev) | ||
36 | { | ||
37 | struct device_node *np = pdev->dev.of_node; | ||
38 | struct stm32_sai_data *sai; | ||
39 | struct reset_control *rst; | ||
40 | struct resource *res; | ||
41 | void __iomem *base; | ||
42 | const struct of_device_id *of_id; | ||
43 | |||
44 | sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); | ||
45 | if (!sai) | ||
46 | return -ENOMEM; | ||
47 | |||
48 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
49 | base = devm_ioremap_resource(&pdev->dev, res); | ||
50 | if (IS_ERR(base)) | ||
51 | return PTR_ERR(base); | ||
52 | |||
53 | of_id = of_match_device(stm32_sai_ids, &pdev->dev); | ||
54 | if (of_id) | ||
55 | sai->version = (enum stm32_sai_version)of_id->data; | ||
56 | else | ||
57 | return -EINVAL; | ||
58 | |||
59 | sai->clk_x8k = devm_clk_get(&pdev->dev, "x8k"); | ||
60 | if (IS_ERR(sai->clk_x8k)) { | ||
61 | dev_err(&pdev->dev, "missing x8k parent clock\n"); | ||
62 | return PTR_ERR(sai->clk_x8k); | ||
63 | } | ||
64 | |||
65 | sai->clk_x11k = devm_clk_get(&pdev->dev, "x11k"); | ||
66 | if (IS_ERR(sai->clk_x11k)) { | ||
67 | dev_err(&pdev->dev, "missing x11k parent clock\n"); | ||
68 | return PTR_ERR(sai->clk_x11k); | ||
69 | } | ||
70 | |||
71 | /* init irqs */ | ||
72 | sai->irq = platform_get_irq(pdev, 0); | ||
73 | if (sai->irq < 0) { | ||
74 | dev_err(&pdev->dev, "no irq for node %s\n", pdev->name); | ||
75 | return sai->irq; | ||
76 | } | ||
77 | |||
78 | /* reset */ | ||
79 | rst = reset_control_get(&pdev->dev, NULL); | ||
80 | if (!IS_ERR(rst)) { | ||
81 | reset_control_assert(rst); | ||
82 | udelay(2); | ||
83 | reset_control_deassert(rst); | ||
84 | } | ||
85 | |||
86 | sai->pdev = pdev; | ||
87 | platform_set_drvdata(pdev, sai); | ||
88 | |||
89 | return of_platform_populate(np, NULL, NULL, &pdev->dev); | ||
90 | } | ||
91 | |||
92 | static int stm32_sai_remove(struct platform_device *pdev) | ||
93 | { | ||
94 | of_platform_depopulate(&pdev->dev); | ||
95 | |||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | MODULE_DEVICE_TABLE(of, stm32_sai_ids); | ||
100 | |||
101 | static struct platform_driver stm32_sai_driver = { | ||
102 | .driver = { | ||
103 | .name = "st,stm32-sai", | ||
104 | .of_match_table = stm32_sai_ids, | ||
105 | }, | ||
106 | .probe = stm32_sai_probe, | ||
107 | .remove = stm32_sai_remove, | ||
108 | }; | ||
109 | |||
110 | module_platform_driver(stm32_sai_driver); | ||
111 | |||
112 | MODULE_DESCRIPTION("STM32 Soc SAI Interface"); | ||
113 | MODULE_AUTHOR("Olivier Moysan, <olivier.moysan@st.com>"); | ||
114 | MODULE_ALIAS("platform:st,stm32-sai"); | ||
115 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/sound/soc/stm/stm32_sai.h b/sound/soc/stm/stm32_sai.h new file mode 100644 index 000000000000..a801fda5066f --- /dev/null +++ b/sound/soc/stm/stm32_sai.h | |||
@@ -0,0 +1,200 @@ | |||
1 | /* | ||
2 | * STM32 ALSA SoC Digital Audio Interface (SAI) driver. | ||
3 | * | ||
4 | * Copyright (C) 2016, STMicroelectronics - All Rights Reserved | ||
5 | * Author(s): Olivier Moysan <olivier.moysan@st.com> for STMicroelectronics. | ||
6 | * | ||
7 | * License terms: GPL V2.0. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify it | ||
10 | * under the terms of the GNU General Public License version 2 as published by | ||
11 | * the Free Software Foundation. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, but | ||
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more | ||
16 | * details. | ||
17 | */ | ||
18 | |||
19 | /******************** SAI Register Map **************************************/ | ||
20 | |||
21 | /* common register */ | ||
22 | #define STM_SAI_GCR 0x00 | ||
23 | |||
24 | /* Sub-block A&B registers offsets, relative to A&B sub-block addresses */ | ||
25 | #define STM_SAI_CR1_REGX 0x00 /* A offset: 0x04. B offset: 0x24 */ | ||
26 | #define STM_SAI_CR2_REGX 0x04 | ||
27 | #define STM_SAI_FRCR_REGX 0x08 | ||
28 | #define STM_SAI_SLOTR_REGX 0x0C | ||
29 | #define STM_SAI_IMR_REGX 0x10 | ||
30 | #define STM_SAI_SR_REGX 0x14 | ||
31 | #define STM_SAI_CLRFR_REGX 0x18 | ||
32 | #define STM_SAI_DR_REGX 0x1C | ||
33 | |||
34 | /******************** Bit definition for SAI_GCR register *******************/ | ||
35 | #define SAI_GCR_SYNCIN_SHIFT 0 | ||
36 | #define SAI_GCR_SYNCIN_MASK GENMASK(1, SAI_GCR_SYNCIN_SHIFT) | ||
37 | #define SAI_GCR_SYNCIN_SET(x) ((x) << SAI_GCR_SYNCIN_SHIFT) | ||
38 | |||
39 | #define SAI_GCR_SYNCOUT_SHIFT 4 | ||
40 | #define SAI_GCR_SYNCOUT_MASK GENMASK(5, SAI_GCR_SYNCOUT_SHIFT) | ||
41 | #define SAI_GCR_SYNCOUT_SET(x) ((x) << SAI_GCR_SYNCOUT_SHIFT) | ||
42 | |||
43 | /******************* Bit definition for SAI_XCR1 register *******************/ | ||
44 | #define SAI_XCR1_RX_TX_SHIFT 0 | ||
45 | #define SAI_XCR1_RX_TX BIT(SAI_XCR1_RX_TX_SHIFT) | ||
46 | #define SAI_XCR1_SLAVE_SHIFT 1 | ||
47 | #define SAI_XCR1_SLAVE BIT(SAI_XCR1_SLAVE_SHIFT) | ||
48 | |||
49 | #define SAI_XCR1_PRTCFG_SHIFT 2 | ||
50 | #define SAI_XCR1_PRTCFG_MASK GENMASK(3, SAI_XCR1_PRTCFG_SHIFT) | ||
51 | #define SAI_XCR1_PRTCFG_SET(x) ((x) << SAI_XCR1_PRTCFG_SHIFT) | ||
52 | |||
53 | #define SAI_XCR1_DS_SHIFT 5 | ||
54 | #define SAI_XCR1_DS_MASK GENMASK(7, SAI_XCR1_DS_SHIFT) | ||
55 | #define SAI_XCR1_DS_SET(x) ((x) << SAI_XCR1_DS_SHIFT) | ||
56 | |||
57 | #define SAI_XCR1_LSBFIRST_SHIFT 8 | ||
58 | #define SAI_XCR1_LSBFIRST BIT(SAI_XCR1_LSBFIRST_SHIFT) | ||
59 | #define SAI_XCR1_CKSTR_SHIFT 9 | ||
60 | #define SAI_XCR1_CKSTR BIT(SAI_XCR1_CKSTR_SHIFT) | ||
61 | |||
62 | #define SAI_XCR1_SYNCEN_SHIFT 10 | ||
63 | #define SAI_XCR1_SYNCEN_MASK GENMASK(11, SAI_XCR1_SYNCEN_SHIFT) | ||
64 | #define SAI_XCR1_SYNCEN_SET(x) ((x) << SAI_XCR1_SYNCEN_SHIFT) | ||
65 | |||
66 | #define SAI_XCR1_MONO_SHIFT 12 | ||
67 | #define SAI_XCR1_MONO BIT(SAI_XCR1_MONO_SHIFT) | ||
68 | #define SAI_XCR1_OUTDRIV_SHIFT 13 | ||
69 | #define SAI_XCR1_OUTDRIV BIT(SAI_XCR1_OUTDRIV_SHIFT) | ||
70 | #define SAI_XCR1_SAIEN_SHIFT 16 | ||
71 | #define SAI_XCR1_SAIEN BIT(SAI_XCR1_SAIEN_SHIFT) | ||
72 | #define SAI_XCR1_DMAEN_SHIFT 17 | ||
73 | #define SAI_XCR1_DMAEN BIT(SAI_XCR1_DMAEN_SHIFT) | ||
74 | #define SAI_XCR1_NODIV_SHIFT 19 | ||
75 | #define SAI_XCR1_NODIV BIT(SAI_XCR1_NODIV_SHIFT) | ||
76 | |||
77 | #define SAI_XCR1_MCKDIV_SHIFT 20 | ||
78 | #define SAI_XCR1_MCKDIV_WIDTH 4 | ||
79 | #define SAI_XCR1_MCKDIV_MASK GENMASK(24, SAI_XCR1_MCKDIV_SHIFT) | ||
80 | #define SAI_XCR1_MCKDIV_SET(x) ((x) << SAI_XCR1_MCKDIV_SHIFT) | ||
81 | #define SAI_XCR1_MCKDIV_MAX ((1 << SAI_XCR1_MCKDIV_WIDTH) - 1) | ||
82 | |||
83 | #define SAI_XCR1_OSR_SHIFT 26 | ||
84 | #define SAI_XCR1_OSR BIT(SAI_XCR1_OSR_SHIFT) | ||
85 | |||
86 | /******************* Bit definition for SAI_XCR2 register *******************/ | ||
87 | #define SAI_XCR2_FTH_SHIFT 0 | ||
88 | #define SAI_XCR2_FTH_MASK GENMASK(2, SAI_XCR2_FTH_SHIFT) | ||
89 | #define SAI_XCR2_FTH_SET(x) ((x) << SAI_XCR2_FTH_SHIFT) | ||
90 | |||
91 | #define SAI_XCR2_FFLUSH_SHIFT 3 | ||
92 | #define SAI_XCR2_FFLUSH BIT(SAI_XCR2_FFLUSH_SHIFT) | ||
93 | #define SAI_XCR2_TRIS_SHIFT 4 | ||
94 | #define SAI_XCR2_TRIS BIT(SAI_XCR2_TRIS_SHIFT) | ||
95 | #define SAI_XCR2_MUTE_SHIFT 5 | ||
96 | #define SAI_XCR2_MUTE BIT(SAI_XCR2_MUTE_SHIFT) | ||
97 | #define SAI_XCR2_MUTEVAL_SHIFT 6 | ||
98 | #define SAI_XCR2_MUTEVAL BIT(SAI_XCR2_MUTEVAL_SHIFT) | ||
99 | |||
100 | #define SAI_XCR2_MUTECNT_SHIFT 7 | ||
101 | #define SAI_XCR2_MUTECNT_MASK GENMASK(12, SAI_XCR2_MUTECNT_SHIFT) | ||
102 | #define SAI_XCR2_MUTECNT_SET(x) ((x) << SAI_XCR2_MUTECNT_SHIFT) | ||
103 | |||
104 | #define SAI_XCR2_CPL_SHIFT 13 | ||
105 | #define SAI_XCR2_CPL BIT(SAI_XCR2_CPL_SHIFT) | ||
106 | |||
107 | #define SAI_XCR2_COMP_SHIFT 14 | ||
108 | #define SAI_XCR2_COMP_MASK GENMASK(15, SAI_XCR2_COMP_SHIFT) | ||
109 | #define SAI_XCR2_COMP_SET(x) ((x) << SAI_XCR2_COMP_SHIFT) | ||
110 | |||
111 | /****************** Bit definition for SAI_XFRCR register *******************/ | ||
112 | #define SAI_XFRCR_FRL_SHIFT 0 | ||
113 | #define SAI_XFRCR_FRL_MASK GENMASK(7, SAI_XFRCR_FRL_SHIFT) | ||
114 | #define SAI_XFRCR_FRL_SET(x) ((x) << SAI_XFRCR_FRL_SHIFT) | ||
115 | |||
116 | #define SAI_XFRCR_FSALL_SHIFT 8 | ||
117 | #define SAI_XFRCR_FSALL_MASK GENMASK(14, SAI_XFRCR_FSALL_SHIFT) | ||
118 | #define SAI_XFRCR_FSALL_SET(x) ((x) << SAI_XFRCR_FSALL_SHIFT) | ||
119 | |||
120 | #define SAI_XFRCR_FSDEF_SHIFT 16 | ||
121 | #define SAI_XFRCR_FSDEF BIT(SAI_XFRCR_FSDEF_SHIFT) | ||
122 | #define SAI_XFRCR_FSPOL_SHIFT 17 | ||
123 | #define SAI_XFRCR_FSPOL BIT(SAI_XFRCR_FSPOL_SHIFT) | ||
124 | #define SAI_XFRCR_FSOFF_SHIFT 18 | ||
125 | #define SAI_XFRCR_FSOFF BIT(SAI_XFRCR_FSOFF_SHIFT) | ||
126 | |||
127 | /****************** Bit definition for SAI_XSLOTR register ******************/ | ||
128 | |||
129 | #define SAI_XSLOTR_FBOFF_SHIFT 0 | ||
130 | #define SAI_XSLOTR_FBOFF_MASK GENMASK(4, SAI_XSLOTR_FBOFF_SHIFT) | ||
131 | #define SAI_XSLOTR_FBOFF_SET(x) ((x) << SAI_XSLOTR_FBOFF_SHIFT) | ||
132 | |||
133 | #define SAI_XSLOTR_SLOTSZ_SHIFT 6 | ||
134 | #define SAI_XSLOTR_SLOTSZ_MASK GENMASK(7, SAI_XSLOTR_SLOTSZ_SHIFT) | ||
135 | #define SAI_XSLOTR_SLOTSZ_SET(x) ((x) << SAI_XSLOTR_SLOTSZ_SHIFT) | ||
136 | |||
137 | #define SAI_XSLOTR_NBSLOT_SHIFT 8 | ||
138 | #define SAI_XSLOTR_NBSLOT_MASK GENMASK(11, SAI_XSLOTR_NBSLOT_SHIFT) | ||
139 | #define SAI_XSLOTR_NBSLOT_SET(x) ((x) << SAI_XSLOTR_NBSLOT_SHIFT) | ||
140 | |||
141 | #define SAI_XSLOTR_SLOTEN_SHIFT 16 | ||
142 | #define SAI_XSLOTR_SLOTEN_WIDTH 16 | ||
143 | #define SAI_XSLOTR_SLOTEN_MASK GENMASK(31, SAI_XSLOTR_SLOTEN_SHIFT) | ||
144 | #define SAI_XSLOTR_SLOTEN_SET(x) ((x) << SAI_XSLOTR_SLOTEN_SHIFT) | ||
145 | |||
146 | /******************* Bit definition for SAI_XIMR register *******************/ | ||
147 | #define SAI_XIMR_OVRUDRIE BIT(0) | ||
148 | #define SAI_XIMR_MUTEDETIE BIT(1) | ||
149 | #define SAI_XIMR_WCKCFGIE BIT(2) | ||
150 | #define SAI_XIMR_FREQIE BIT(3) | ||
151 | #define SAI_XIMR_CNRDYIE BIT(4) | ||
152 | #define SAI_XIMR_AFSDETIE BIT(5) | ||
153 | #define SAI_XIMR_LFSDETIE BIT(6) | ||
154 | |||
155 | #define SAI_XIMR_SHIFT 0 | ||
156 | #define SAI_XIMR_MASK GENMASK(6, SAI_XIMR_SHIFT) | ||
157 | |||
158 | /******************** Bit definition for SAI_XSR register *******************/ | ||
159 | #define SAI_XSR_OVRUDR BIT(0) | ||
160 | #define SAI_XSR_MUTEDET BIT(1) | ||
161 | #define SAI_XSR_WCKCFG BIT(2) | ||
162 | #define SAI_XSR_FREQ BIT(3) | ||
163 | #define SAI_XSR_CNRDY BIT(4) | ||
164 | #define SAI_XSR_AFSDET BIT(5) | ||
165 | #define SAI_XSR_LFSDET BIT(6) | ||
166 | |||
167 | #define SAI_XSR_SHIFT 0 | ||
168 | #define SAI_XSR_MASK GENMASK(6, SAI_XSR_SHIFT) | ||
169 | |||
170 | /****************** Bit definition for SAI_XCLRFR register ******************/ | ||
171 | #define SAI_XCLRFR_COVRUDR BIT(0) | ||
172 | #define SAI_XCLRFR_CMUTEDET BIT(1) | ||
173 | #define SAI_XCLRFR_CWCKCFG BIT(2) | ||
174 | #define SAI_XCLRFR_CFREQ BIT(3) | ||
175 | #define SAI_XCLRFR_CCNRDY BIT(4) | ||
176 | #define SAI_XCLRFR_CAFSDET BIT(5) | ||
177 | #define SAI_XCLRFR_CLFSDET BIT(6) | ||
178 | |||
179 | #define SAI_XCLRFR_SHIFT 0 | ||
180 | #define SAI_XCLRFR_MASK GENMASK(6, SAI_XCLRFR_SHIFT) | ||
181 | |||
182 | enum stm32_sai_version { | ||
183 | SAI_STM32F4 | ||
184 | }; | ||
185 | |||
186 | /** | ||
187 | * struct stm32_sai_data - private data of SAI instance driver | ||
188 | * @pdev: device data pointer | ||
189 | * @clk_x8k: SAI parent clock for sampling frequencies multiple of 8kHz | ||
190 | * @clk_x11k: SAI parent clock for sampling frequencies multiple of 11kHz | ||
191 | * @version: SOC version | ||
192 | * @irq: SAI interrupt line | ||
193 | */ | ||
194 | struct stm32_sai_data { | ||
195 | struct platform_device *pdev; | ||
196 | struct clk *clk_x8k; | ||
197 | struct clk *clk_x11k; | ||
198 | int version; | ||
199 | int irq; | ||
200 | }; | ||
diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c new file mode 100644 index 000000000000..ae4706ca265b --- /dev/null +++ b/sound/soc/stm/stm32_sai_sub.c | |||
@@ -0,0 +1,884 @@ | |||
1 | /* | ||
2 | * STM32 ALSA SoC Digital Audio Interface (SAI) driver. | ||
3 | * | ||
4 | * Copyright (C) 2016, STMicroelectronics - All Rights Reserved | ||
5 | * Author(s): Olivier Moysan <olivier.moysan@st.com> for STMicroelectronics. | ||
6 | * | ||
7 | * License terms: GPL V2.0. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify it | ||
10 | * under the terms of the GNU General Public License version 2 as published by | ||
11 | * the Free Software Foundation. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, but | ||
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more | ||
16 | * details. | ||
17 | */ | ||
18 | |||
19 | #include <linux/clk.h> | ||
20 | #include <linux/kernel.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/of_irq.h> | ||
23 | #include <linux/of_platform.h> | ||
24 | #include <linux/regmap.h> | ||
25 | |||
26 | #include <sound/core.h> | ||
27 | #include <sound/dmaengine_pcm.h> | ||
28 | #include <sound/pcm_params.h> | ||
29 | |||
30 | #include "stm32_sai.h" | ||
31 | |||
32 | #define SAI_FREE_PROTOCOL 0x0 | ||
33 | |||
34 | #define SAI_SLOT_SIZE_AUTO 0x0 | ||
35 | #define SAI_SLOT_SIZE_16 0x1 | ||
36 | #define SAI_SLOT_SIZE_32 0x2 | ||
37 | |||
38 | #define SAI_DATASIZE_8 0x2 | ||
39 | #define SAI_DATASIZE_10 0x3 | ||
40 | #define SAI_DATASIZE_16 0x4 | ||
41 | #define SAI_DATASIZE_20 0x5 | ||
42 | #define SAI_DATASIZE_24 0x6 | ||
43 | #define SAI_DATASIZE_32 0x7 | ||
44 | |||
45 | #define STM_SAI_FIFO_SIZE 8 | ||
46 | #define STM_SAI_DAI_NAME_SIZE 15 | ||
47 | |||
48 | #define STM_SAI_IS_PLAYBACK(ip) ((ip)->dir == SNDRV_PCM_STREAM_PLAYBACK) | ||
49 | #define STM_SAI_IS_CAPTURE(ip) ((ip)->dir == SNDRV_PCM_STREAM_CAPTURE) | ||
50 | |||
51 | #define STM_SAI_A_ID 0x0 | ||
52 | #define STM_SAI_B_ID 0x1 | ||
53 | |||
54 | #define STM_SAI_BLOCK_NAME(x) (((x)->id == STM_SAI_A_ID) ? "A" : "B") | ||
55 | |||
56 | /** | ||
57 | * struct stm32_sai_sub_data - private data of SAI sub block (block A or B) | ||
58 | * @pdev: device data pointer | ||
59 | * @regmap: SAI register map pointer | ||
60 | * @dma_params: dma configuration data for rx or tx channel | ||
61 | * @cpu_dai_drv: DAI driver data pointer | ||
62 | * @cpu_dai: DAI runtime data pointer | ||
63 | * @substream: PCM substream data pointer | ||
64 | * @pdata: SAI block parent data pointer | ||
65 | * @sai_ck: kernel clock feeding the SAI clock generator | ||
66 | * @phys_addr: SAI registers physical base address | ||
67 | * @mclk_rate: SAI block master clock frequency (Hz). set at init | ||
68 | * @id: SAI sub block id corresponding to sub-block A or B | ||
69 | * @dir: SAI block direction (playback or capture). set at init | ||
70 | * @master: SAI block mode flag. (true=master, false=slave) set at init | ||
71 | * @fmt: SAI block format. relevant only for custom protocols. set at init | ||
72 | * @sync: SAI block synchronization mode. (none, internal or external) | ||
73 | * @fs_length: frame synchronization length. depends on protocol settings | ||
74 | * @slots: rx or tx slot number | ||
75 | * @slot_width: rx or tx slot width in bits | ||
76 | * @slot_mask: rx or tx active slots mask. set at init or at runtime | ||
77 | * @data_size: PCM data width. corresponds to PCM substream width. | ||
78 | */ | ||
79 | struct stm32_sai_sub_data { | ||
80 | struct platform_device *pdev; | ||
81 | struct regmap *regmap; | ||
82 | struct snd_dmaengine_dai_dma_data dma_params; | ||
83 | struct snd_soc_dai_driver *cpu_dai_drv; | ||
84 | struct snd_soc_dai *cpu_dai; | ||
85 | struct snd_pcm_substream *substream; | ||
86 | struct stm32_sai_data *pdata; | ||
87 | struct clk *sai_ck; | ||
88 | dma_addr_t phys_addr; | ||
89 | unsigned int mclk_rate; | ||
90 | unsigned int id; | ||
91 | int dir; | ||
92 | bool master; | ||
93 | int fmt; | ||
94 | int sync; | ||
95 | int fs_length; | ||
96 | int slots; | ||
97 | int slot_width; | ||
98 | int slot_mask; | ||
99 | int data_size; | ||
100 | }; | ||
101 | |||
102 | enum stm32_sai_fifo_th { | ||
103 | STM_SAI_FIFO_TH_EMPTY, | ||
104 | STM_SAI_FIFO_TH_QUARTER, | ||
105 | STM_SAI_FIFO_TH_HALF, | ||
106 | STM_SAI_FIFO_TH_3_QUARTER, | ||
107 | STM_SAI_FIFO_TH_FULL, | ||
108 | }; | ||
109 | |||
110 | static bool stm32_sai_sub_readable_reg(struct device *dev, unsigned int reg) | ||
111 | { | ||
112 | switch (reg) { | ||
113 | case STM_SAI_CR1_REGX: | ||
114 | case STM_SAI_CR2_REGX: | ||
115 | case STM_SAI_FRCR_REGX: | ||
116 | case STM_SAI_SLOTR_REGX: | ||
117 | case STM_SAI_IMR_REGX: | ||
118 | case STM_SAI_SR_REGX: | ||
119 | case STM_SAI_CLRFR_REGX: | ||
120 | case STM_SAI_DR_REGX: | ||
121 | return true; | ||
122 | default: | ||
123 | return false; | ||
124 | } | ||
125 | } | ||
126 | |||
127 | static bool stm32_sai_sub_volatile_reg(struct device *dev, unsigned int reg) | ||
128 | { | ||
129 | switch (reg) { | ||
130 | case STM_SAI_DR_REGX: | ||
131 | return true; | ||
132 | default: | ||
133 | return false; | ||
134 | } | ||
135 | } | ||
136 | |||
137 | static bool stm32_sai_sub_writeable_reg(struct device *dev, unsigned int reg) | ||
138 | { | ||
139 | switch (reg) { | ||
140 | case STM_SAI_CR1_REGX: | ||
141 | case STM_SAI_CR2_REGX: | ||
142 | case STM_SAI_FRCR_REGX: | ||
143 | case STM_SAI_SLOTR_REGX: | ||
144 | case STM_SAI_IMR_REGX: | ||
145 | case STM_SAI_SR_REGX: | ||
146 | case STM_SAI_CLRFR_REGX: | ||
147 | case STM_SAI_DR_REGX: | ||
148 | return true; | ||
149 | default: | ||
150 | return false; | ||
151 | } | ||
152 | } | ||
153 | |||
154 | static const struct regmap_config stm32_sai_sub_regmap_config = { | ||
155 | .reg_bits = 32, | ||
156 | .reg_stride = 4, | ||
157 | .val_bits = 32, | ||
158 | .max_register = STM_SAI_DR_REGX, | ||
159 | .readable_reg = stm32_sai_sub_readable_reg, | ||
160 | .volatile_reg = stm32_sai_sub_volatile_reg, | ||
161 | .writeable_reg = stm32_sai_sub_writeable_reg, | ||
162 | .fast_io = true, | ||
163 | }; | ||
164 | |||
165 | static irqreturn_t stm32_sai_isr(int irq, void *devid) | ||
166 | { | ||
167 | struct stm32_sai_sub_data *sai = (struct stm32_sai_sub_data *)devid; | ||
168 | struct snd_pcm_substream *substream = sai->substream; | ||
169 | struct platform_device *pdev = sai->pdev; | ||
170 | unsigned int sr, imr, flags; | ||
171 | snd_pcm_state_t status = SNDRV_PCM_STATE_RUNNING; | ||
172 | |||
173 | regmap_read(sai->regmap, STM_SAI_IMR_REGX, &imr); | ||
174 | regmap_read(sai->regmap, STM_SAI_SR_REGX, &sr); | ||
175 | |||
176 | flags = sr & imr; | ||
177 | if (!flags) | ||
178 | return IRQ_NONE; | ||
179 | |||
180 | regmap_update_bits(sai->regmap, STM_SAI_CLRFR_REGX, SAI_XCLRFR_MASK, | ||
181 | SAI_XCLRFR_MASK); | ||
182 | |||
183 | if (flags & SAI_XIMR_OVRUDRIE) { | ||
184 | dev_err(&pdev->dev, "IT %s\n", | ||
185 | STM_SAI_IS_PLAYBACK(sai) ? "underrun" : "overrun"); | ||
186 | status = SNDRV_PCM_STATE_XRUN; | ||
187 | } | ||
188 | |||
189 | if (flags & SAI_XIMR_MUTEDETIE) | ||
190 | dev_dbg(&pdev->dev, "IT mute detected\n"); | ||
191 | |||
192 | if (flags & SAI_XIMR_WCKCFGIE) { | ||
193 | dev_err(&pdev->dev, "IT wrong clock configuration\n"); | ||
194 | status = SNDRV_PCM_STATE_DISCONNECTED; | ||
195 | } | ||
196 | |||
197 | if (flags & SAI_XIMR_CNRDYIE) | ||
198 | dev_warn(&pdev->dev, "IT Codec not ready\n"); | ||
199 | |||
200 | if (flags & SAI_XIMR_AFSDETIE) { | ||
201 | dev_warn(&pdev->dev, "IT Anticipated frame synchro\n"); | ||
202 | status = SNDRV_PCM_STATE_XRUN; | ||
203 | } | ||
204 | |||
205 | if (flags & SAI_XIMR_LFSDETIE) { | ||
206 | dev_warn(&pdev->dev, "IT Late frame synchro\n"); | ||
207 | status = SNDRV_PCM_STATE_XRUN; | ||
208 | } | ||
209 | |||
210 | if (status != SNDRV_PCM_STATE_RUNNING) { | ||
211 | snd_pcm_stream_lock(substream); | ||
212 | snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); | ||
213 | snd_pcm_stream_unlock(substream); | ||
214 | } | ||
215 | |||
216 | return IRQ_HANDLED; | ||
217 | } | ||
218 | |||
219 | static int stm32_sai_set_sysclk(struct snd_soc_dai *cpu_dai, | ||
220 | int clk_id, unsigned int freq, int dir) | ||
221 | { | ||
222 | struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); | ||
223 | |||
224 | if ((dir == SND_SOC_CLOCK_OUT) && sai->master) { | ||
225 | sai->mclk_rate = freq; | ||
226 | dev_dbg(cpu_dai->dev, "SAI MCLK frequency is %uHz\n", freq); | ||
227 | } | ||
228 | |||
229 | return 0; | ||
230 | } | ||
231 | |||
232 | static int stm32_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask, | ||
233 | u32 rx_mask, int slots, int slot_width) | ||
234 | { | ||
235 | struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); | ||
236 | int slotr, slotr_mask, slot_size; | ||
237 | |||
238 | dev_dbg(cpu_dai->dev, "masks tx/rx:%#x/%#x, slots:%d, width:%d\n", | ||
239 | tx_mask, rx_mask, slots, slot_width); | ||
240 | |||
241 | switch (slot_width) { | ||
242 | case 16: | ||
243 | slot_size = SAI_SLOT_SIZE_16; | ||
244 | break; | ||
245 | case 32: | ||
246 | slot_size = SAI_SLOT_SIZE_32; | ||
247 | break; | ||
248 | default: | ||
249 | slot_size = SAI_SLOT_SIZE_AUTO; | ||
250 | break; | ||
251 | } | ||
252 | |||
253 | slotr = SAI_XSLOTR_SLOTSZ_SET(slot_size) | | ||
254 | SAI_XSLOTR_NBSLOT_SET(slots - 1); | ||
255 | slotr_mask = SAI_XSLOTR_SLOTSZ_MASK | SAI_XSLOTR_NBSLOT_MASK; | ||
256 | |||
257 | /* tx/rx mask set in machine init, if slot number defined in DT */ | ||
258 | if (STM_SAI_IS_PLAYBACK(sai)) { | ||
259 | sai->slot_mask = tx_mask; | ||
260 | slotr |= SAI_XSLOTR_SLOTEN_SET(tx_mask); | ||
261 | } | ||
262 | |||
263 | if (STM_SAI_IS_CAPTURE(sai)) { | ||
264 | sai->slot_mask = rx_mask; | ||
265 | slotr |= SAI_XSLOTR_SLOTEN_SET(rx_mask); | ||
266 | } | ||
267 | |||
268 | slotr_mask |= SAI_XSLOTR_SLOTEN_MASK; | ||
269 | |||
270 | regmap_update_bits(sai->regmap, STM_SAI_SLOTR_REGX, slotr_mask, slotr); | ||
271 | |||
272 | sai->slot_width = slot_width; | ||
273 | sai->slots = slots; | ||
274 | |||
275 | return 0; | ||
276 | } | ||
277 | |||
278 | static int stm32_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) | ||
279 | { | ||
280 | struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); | ||
281 | int cr1 = 0, frcr = 0; | ||
282 | int cr1_mask = 0, frcr_mask = 0; | ||
283 | int ret; | ||
284 | |||
285 | dev_dbg(cpu_dai->dev, "fmt %x\n", fmt); | ||
286 | |||
287 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
288 | /* SCK active high for all protocols */ | ||
289 | case SND_SOC_DAIFMT_I2S: | ||
290 | cr1 |= SAI_XCR1_CKSTR; | ||
291 | frcr |= SAI_XFRCR_FSOFF | SAI_XFRCR_FSDEF; | ||
292 | break; | ||
293 | /* Left justified */ | ||
294 | case SND_SOC_DAIFMT_MSB: | ||
295 | frcr |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSDEF; | ||
296 | break; | ||
297 | /* Right justified */ | ||
298 | case SND_SOC_DAIFMT_LSB: | ||
299 | frcr |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSDEF; | ||
300 | break; | ||
301 | case SND_SOC_DAIFMT_DSP_A: | ||
302 | frcr |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSOFF; | ||
303 | break; | ||
304 | case SND_SOC_DAIFMT_DSP_B: | ||
305 | frcr |= SAI_XFRCR_FSPOL; | ||
306 | break; | ||
307 | default: | ||
308 | dev_err(cpu_dai->dev, "Unsupported protocol %#x\n", | ||
309 | fmt & SND_SOC_DAIFMT_FORMAT_MASK); | ||
310 | return -EINVAL; | ||
311 | } | ||
312 | |||
313 | cr1_mask |= SAI_XCR1_PRTCFG_MASK | SAI_XCR1_CKSTR; | ||
314 | frcr_mask |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSOFF | | ||
315 | SAI_XFRCR_FSDEF; | ||
316 | |||
317 | /* DAI clock strobing. Invert setting previously set */ | ||
318 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | ||
319 | case SND_SOC_DAIFMT_NB_NF: | ||
320 | break; | ||
321 | case SND_SOC_DAIFMT_IB_NF: | ||
322 | cr1 ^= SAI_XCR1_CKSTR; | ||
323 | break; | ||
324 | case SND_SOC_DAIFMT_NB_IF: | ||
325 | frcr ^= SAI_XFRCR_FSPOL; | ||
326 | break; | ||
327 | case SND_SOC_DAIFMT_IB_IF: | ||
328 | /* Invert fs & sck */ | ||
329 | cr1 ^= SAI_XCR1_CKSTR; | ||
330 | frcr ^= SAI_XFRCR_FSPOL; | ||
331 | break; | ||
332 | default: | ||
333 | dev_err(cpu_dai->dev, "Unsupported strobing %#x\n", | ||
334 | fmt & SND_SOC_DAIFMT_INV_MASK); | ||
335 | return -EINVAL; | ||
336 | } | ||
337 | cr1_mask |= SAI_XCR1_CKSTR; | ||
338 | frcr_mask |= SAI_XFRCR_FSPOL; | ||
339 | |||
340 | regmap_update_bits(sai->regmap, STM_SAI_FRCR_REGX, frcr_mask, frcr); | ||
341 | |||
342 | /* DAI clock master masks */ | ||
343 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
344 | case SND_SOC_DAIFMT_CBM_CFM: | ||
345 | /* codec is master */ | ||
346 | cr1 |= SAI_XCR1_SLAVE; | ||
347 | sai->master = false; | ||
348 | break; | ||
349 | case SND_SOC_DAIFMT_CBS_CFS: | ||
350 | sai->master = true; | ||
351 | break; | ||
352 | default: | ||
353 | dev_err(cpu_dai->dev, "Unsupported mode %#x\n", | ||
354 | fmt & SND_SOC_DAIFMT_MASTER_MASK); | ||
355 | return -EINVAL; | ||
356 | } | ||
357 | cr1_mask |= SAI_XCR1_SLAVE; | ||
358 | |||
359 | ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, cr1_mask, cr1); | ||
360 | if (ret < 0) { | ||
361 | dev_err(cpu_dai->dev, "Failed to update CR1 register\n"); | ||
362 | return ret; | ||
363 | } | ||
364 | |||
365 | sai->fmt = fmt; | ||
366 | |||
367 | return 0; | ||
368 | } | ||
369 | |||
370 | static int stm32_sai_startup(struct snd_pcm_substream *substream, | ||
371 | struct snd_soc_dai *cpu_dai) | ||
372 | { | ||
373 | struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); | ||
374 | int imr, cr2, ret; | ||
375 | |||
376 | sai->substream = substream; | ||
377 | |||
378 | ret = clk_prepare_enable(sai->sai_ck); | ||
379 | if (ret < 0) { | ||
380 | dev_err(cpu_dai->dev, "failed to enable clock: %d\n", ret); | ||
381 | return ret; | ||
382 | } | ||
383 | |||
384 | /* Enable ITs */ | ||
385 | regmap_update_bits(sai->regmap, STM_SAI_SR_REGX, | ||
386 | SAI_XSR_MASK, (unsigned int)~SAI_XSR_MASK); | ||
387 | |||
388 | regmap_update_bits(sai->regmap, STM_SAI_CLRFR_REGX, | ||
389 | SAI_XCLRFR_MASK, SAI_XCLRFR_MASK); | ||
390 | |||
391 | imr = SAI_XIMR_OVRUDRIE; | ||
392 | if (STM_SAI_IS_CAPTURE(sai)) { | ||
393 | regmap_read(sai->regmap, STM_SAI_CR2_REGX, &cr2); | ||
394 | if (cr2 & SAI_XCR2_MUTECNT_MASK) | ||
395 | imr |= SAI_XIMR_MUTEDETIE; | ||
396 | } | ||
397 | |||
398 | if (sai->master) | ||
399 | imr |= SAI_XIMR_WCKCFGIE; | ||
400 | else | ||
401 | imr |= SAI_XIMR_AFSDETIE | SAI_XIMR_LFSDETIE; | ||
402 | |||
403 | regmap_update_bits(sai->regmap, STM_SAI_IMR_REGX, | ||
404 | SAI_XIMR_MASK, imr); | ||
405 | |||
406 | return 0; | ||
407 | } | ||
408 | |||
409 | static int stm32_sai_set_config(struct snd_soc_dai *cpu_dai, | ||
410 | struct snd_pcm_substream *substream, | ||
411 | struct snd_pcm_hw_params *params) | ||
412 | { | ||
413 | struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); | ||
414 | int cr1, cr1_mask, ret; | ||
415 | int fth = STM_SAI_FIFO_TH_HALF; | ||
416 | |||
417 | /* FIFO config */ | ||
418 | regmap_update_bits(sai->regmap, STM_SAI_CR2_REGX, | ||
419 | SAI_XCR2_FFLUSH | SAI_XCR2_FTH_MASK, | ||
420 | SAI_XCR2_FFLUSH | SAI_XCR2_FTH_SET(fth)); | ||
421 | |||
422 | /* Mode, data format and channel config */ | ||
423 | cr1 = SAI_XCR1_PRTCFG_SET(SAI_FREE_PROTOCOL); | ||
424 | switch (params_format(params)) { | ||
425 | case SNDRV_PCM_FORMAT_S8: | ||
426 | cr1 |= SAI_XCR1_DS_SET(SAI_DATASIZE_8); | ||
427 | break; | ||
428 | case SNDRV_PCM_FORMAT_S16_LE: | ||
429 | cr1 |= SAI_XCR1_DS_SET(SAI_DATASIZE_16); | ||
430 | break; | ||
431 | case SNDRV_PCM_FORMAT_S32_LE: | ||
432 | cr1 |= SAI_XCR1_DS_SET(SAI_DATASIZE_32); | ||
433 | break; | ||
434 | default: | ||
435 | dev_err(cpu_dai->dev, "Data format not supported"); | ||
436 | return -EINVAL; | ||
437 | } | ||
438 | cr1_mask = SAI_XCR1_DS_MASK | SAI_XCR1_PRTCFG_MASK; | ||
439 | |||
440 | cr1_mask |= SAI_XCR1_RX_TX; | ||
441 | if (STM_SAI_IS_CAPTURE(sai)) | ||
442 | cr1 |= SAI_XCR1_RX_TX; | ||
443 | |||
444 | cr1_mask |= SAI_XCR1_MONO; | ||
445 | if ((sai->slots == 2) && (params_channels(params) == 1)) | ||
446 | cr1 |= SAI_XCR1_MONO; | ||
447 | |||
448 | ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, cr1_mask, cr1); | ||
449 | if (ret < 0) { | ||
450 | dev_err(cpu_dai->dev, "Failed to update CR1 register\n"); | ||
451 | return ret; | ||
452 | } | ||
453 | |||
454 | /* DMA config */ | ||
455 | sai->dma_params.maxburst = STM_SAI_FIFO_SIZE * fth / sizeof(u32); | ||
456 | snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)&sai->dma_params); | ||
457 | |||
458 | return 0; | ||
459 | } | ||
460 | |||
461 | static int stm32_sai_set_slots(struct snd_soc_dai *cpu_dai) | ||
462 | { | ||
463 | struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); | ||
464 | int slotr, slot_sz; | ||
465 | |||
466 | regmap_read(sai->regmap, STM_SAI_SLOTR_REGX, &slotr); | ||
467 | |||
468 | /* | ||
469 | * If SLOTSZ is set to auto in SLOTR, align slot width on data size | ||
470 | * By default slot width = data size, if not forced from DT | ||
471 | */ | ||
472 | slot_sz = slotr & SAI_XSLOTR_SLOTSZ_MASK; | ||
473 | if (slot_sz == SAI_XSLOTR_SLOTSZ_SET(SAI_SLOT_SIZE_AUTO)) | ||
474 | sai->slot_width = sai->data_size; | ||
475 | |||
476 | if (sai->slot_width < sai->data_size) { | ||
477 | dev_err(cpu_dai->dev, | ||
478 | "Data size %d larger than slot width\n", | ||
479 | sai->data_size); | ||
480 | return -EINVAL; | ||
481 | } | ||
482 | |||
483 | /* Slot number is set to 2, if not specified in DT */ | ||
484 | if (!sai->slots) | ||
485 | sai->slots = 2; | ||
486 | |||
487 | /* The number of slots in the audio frame is equal to NBSLOT[3:0] + 1*/ | ||
488 | regmap_update_bits(sai->regmap, STM_SAI_SLOTR_REGX, | ||
489 | SAI_XSLOTR_NBSLOT_MASK, | ||
490 | SAI_XSLOTR_NBSLOT_SET((sai->slots - 1))); | ||
491 | |||
492 | /* Set default slots mask if not already set from DT */ | ||
493 | if (!(slotr & SAI_XSLOTR_SLOTEN_MASK)) { | ||
494 | sai->slot_mask = (1 << sai->slots) - 1; | ||
495 | regmap_update_bits(sai->regmap, | ||
496 | STM_SAI_SLOTR_REGX, SAI_XSLOTR_SLOTEN_MASK, | ||
497 | SAI_XSLOTR_SLOTEN_SET(sai->slot_mask)); | ||
498 | } | ||
499 | |||
500 | dev_dbg(cpu_dai->dev, "slots %d, slot width %d\n", | ||
501 | sai->slots, sai->slot_width); | ||
502 | |||
503 | return 0; | ||
504 | } | ||
505 | |||
506 | static void stm32_sai_set_frame(struct snd_soc_dai *cpu_dai) | ||
507 | { | ||
508 | struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); | ||
509 | int fs_active, offset, format; | ||
510 | int frcr, frcr_mask; | ||
511 | |||
512 | format = sai->fmt & SND_SOC_DAIFMT_FORMAT_MASK; | ||
513 | sai->fs_length = sai->slot_width * sai->slots; | ||
514 | |||
515 | fs_active = sai->fs_length / 2; | ||
516 | if ((format == SND_SOC_DAIFMT_DSP_A) || | ||
517 | (format == SND_SOC_DAIFMT_DSP_B)) | ||
518 | fs_active = 1; | ||
519 | |||
520 | frcr = SAI_XFRCR_FRL_SET((sai->fs_length - 1)); | ||
521 | frcr |= SAI_XFRCR_FSALL_SET((fs_active - 1)); | ||
522 | frcr_mask = SAI_XFRCR_FRL_MASK | SAI_XFRCR_FSALL_MASK; | ||
523 | |||
524 | dev_dbg(cpu_dai->dev, "frame length %d, frame active %d\n", | ||
525 | sai->fs_length, fs_active); | ||
526 | |||
527 | regmap_update_bits(sai->regmap, STM_SAI_FRCR_REGX, frcr_mask, frcr); | ||
528 | |||
529 | if ((sai->fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_LSB) { | ||
530 | offset = sai->slot_width - sai->data_size; | ||
531 | |||
532 | regmap_update_bits(sai->regmap, STM_SAI_SLOTR_REGX, | ||
533 | SAI_XSLOTR_FBOFF_MASK, | ||
534 | SAI_XSLOTR_FBOFF_SET(offset)); | ||
535 | } | ||
536 | } | ||
537 | |||
538 | static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai, | ||
539 | struct snd_pcm_hw_params *params) | ||
540 | { | ||
541 | struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); | ||
542 | int cr1, mask, div = 0; | ||
543 | int sai_clk_rate, ret; | ||
544 | |||
545 | if (!sai->mclk_rate) { | ||
546 | dev_err(cpu_dai->dev, "Mclk rate is null\n"); | ||
547 | return -EINVAL; | ||
548 | } | ||
549 | |||
550 | if (!(params_rate(params) % 11025)) | ||
551 | clk_set_parent(sai->sai_ck, sai->pdata->clk_x11k); | ||
552 | else | ||
553 | clk_set_parent(sai->sai_ck, sai->pdata->clk_x8k); | ||
554 | sai_clk_rate = clk_get_rate(sai->sai_ck); | ||
555 | |||
556 | /* | ||
557 | * mclk_rate = 256 * fs | ||
558 | * MCKDIV = 0 if sai_ck < 3/2 * mclk_rate | ||
559 | * MCKDIV = sai_ck / (2 * mclk_rate) otherwise | ||
560 | */ | ||
561 | if (2 * sai_clk_rate >= 3 * sai->mclk_rate) | ||
562 | div = DIV_ROUND_CLOSEST(sai_clk_rate, 2 * sai->mclk_rate); | ||
563 | |||
564 | if (div > SAI_XCR1_MCKDIV_MAX) { | ||
565 | dev_err(cpu_dai->dev, "Divider %d out of range\n", div); | ||
566 | return -EINVAL; | ||
567 | } | ||
568 | dev_dbg(cpu_dai->dev, "SAI clock %d, divider %d\n", sai_clk_rate, div); | ||
569 | |||
570 | mask = SAI_XCR1_MCKDIV_MASK; | ||
571 | cr1 = SAI_XCR1_MCKDIV_SET(div); | ||
572 | ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, mask, cr1); | ||
573 | if (ret < 0) { | ||
574 | dev_err(cpu_dai->dev, "Failed to update CR1 register\n"); | ||
575 | return ret; | ||
576 | } | ||
577 | |||
578 | return 0; | ||
579 | } | ||
580 | |||
581 | static int stm32_sai_hw_params(struct snd_pcm_substream *substream, | ||
582 | struct snd_pcm_hw_params *params, | ||
583 | struct snd_soc_dai *cpu_dai) | ||
584 | { | ||
585 | struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); | ||
586 | int ret; | ||
587 | |||
588 | sai->data_size = params_width(params); | ||
589 | |||
590 | ret = stm32_sai_set_slots(cpu_dai); | ||
591 | if (ret < 0) | ||
592 | return ret; | ||
593 | stm32_sai_set_frame(cpu_dai); | ||
594 | |||
595 | ret = stm32_sai_set_config(cpu_dai, substream, params); | ||
596 | if (ret) | ||
597 | return ret; | ||
598 | |||
599 | if (sai->master) | ||
600 | ret = stm32_sai_configure_clock(cpu_dai, params); | ||
601 | |||
602 | return ret; | ||
603 | } | ||
604 | |||
605 | static int stm32_sai_trigger(struct snd_pcm_substream *substream, int cmd, | ||
606 | struct snd_soc_dai *cpu_dai) | ||
607 | { | ||
608 | struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); | ||
609 | int ret; | ||
610 | |||
611 | switch (cmd) { | ||
612 | case SNDRV_PCM_TRIGGER_START: | ||
613 | case SNDRV_PCM_TRIGGER_RESUME: | ||
614 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
615 | dev_dbg(cpu_dai->dev, "Enable DMA and SAI\n"); | ||
616 | |||
617 | regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, | ||
618 | SAI_XCR1_DMAEN, SAI_XCR1_DMAEN); | ||
619 | |||
620 | /* Enable SAI */ | ||
621 | ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, | ||
622 | SAI_XCR1_SAIEN, SAI_XCR1_SAIEN); | ||
623 | if (ret < 0) | ||
624 | dev_err(cpu_dai->dev, "Failed to update CR1 register\n"); | ||
625 | break; | ||
626 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
627 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
628 | case SNDRV_PCM_TRIGGER_STOP: | ||
629 | dev_dbg(cpu_dai->dev, "Disable DMA and SAI\n"); | ||
630 | |||
631 | regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, | ||
632 | SAI_XCR1_DMAEN, | ||
633 | (unsigned int)~SAI_XCR1_DMAEN); | ||
634 | |||
635 | ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, | ||
636 | SAI_XCR1_SAIEN, | ||
637 | (unsigned int)~SAI_XCR1_SAIEN); | ||
638 | if (ret < 0) | ||
639 | dev_err(cpu_dai->dev, "Failed to update CR1 register\n"); | ||
640 | break; | ||
641 | default: | ||
642 | return -EINVAL; | ||
643 | } | ||
644 | |||
645 | return ret; | ||
646 | } | ||
647 | |||
648 | static void stm32_sai_shutdown(struct snd_pcm_substream *substream, | ||
649 | struct snd_soc_dai *cpu_dai) | ||
650 | { | ||
651 | struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); | ||
652 | |||
653 | regmap_update_bits(sai->regmap, STM_SAI_IMR_REGX, SAI_XIMR_MASK, 0); | ||
654 | |||
655 | clk_disable_unprepare(sai->sai_ck); | ||
656 | sai->substream = NULL; | ||
657 | } | ||
658 | |||
659 | static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai) | ||
660 | { | ||
661 | struct stm32_sai_sub_data *sai = dev_get_drvdata(cpu_dai->dev); | ||
662 | |||
663 | sai->dma_params.addr = (dma_addr_t)(sai->phys_addr + STM_SAI_DR_REGX); | ||
664 | sai->dma_params.maxburst = 1; | ||
665 | /* Buswidth will be set by framework at runtime */ | ||
666 | sai->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED; | ||
667 | |||
668 | if (STM_SAI_IS_PLAYBACK(sai)) | ||
669 | snd_soc_dai_init_dma_data(cpu_dai, &sai->dma_params, NULL); | ||
670 | else | ||
671 | snd_soc_dai_init_dma_data(cpu_dai, NULL, &sai->dma_params); | ||
672 | |||
673 | return 0; | ||
674 | } | ||
675 | |||
676 | static const struct snd_soc_dai_ops stm32_sai_pcm_dai_ops = { | ||
677 | .set_sysclk = stm32_sai_set_sysclk, | ||
678 | .set_fmt = stm32_sai_set_dai_fmt, | ||
679 | .set_tdm_slot = stm32_sai_set_dai_tdm_slot, | ||
680 | .startup = stm32_sai_startup, | ||
681 | .hw_params = stm32_sai_hw_params, | ||
682 | .trigger = stm32_sai_trigger, | ||
683 | .shutdown = stm32_sai_shutdown, | ||
684 | }; | ||
685 | |||
686 | static const struct snd_pcm_hardware stm32_sai_pcm_hw = { | ||
687 | .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP, | ||
688 | .buffer_bytes_max = 8 * PAGE_SIZE, | ||
689 | .period_bytes_min = 1024, /* 5ms at 48kHz */ | ||
690 | .period_bytes_max = PAGE_SIZE, | ||
691 | .periods_min = 2, | ||
692 | .periods_max = 8, | ||
693 | }; | ||
694 | |||
695 | static struct snd_soc_dai_driver stm32_sai_playback_dai[] = { | ||
696 | { | ||
697 | .probe = stm32_sai_dai_probe, | ||
698 | .id = 1, /* avoid call to fmt_single_name() */ | ||
699 | .playback = { | ||
700 | .channels_min = 1, | ||
701 | .channels_max = 2, | ||
702 | .rate_min = 8000, | ||
703 | .rate_max = 192000, | ||
704 | .rates = SNDRV_PCM_RATE_CONTINUOUS, | ||
705 | /* DMA does not support 24 bits transfers */ | ||
706 | .formats = | ||
707 | SNDRV_PCM_FMTBIT_S8 | | ||
708 | SNDRV_PCM_FMTBIT_S16_LE | | ||
709 | SNDRV_PCM_FMTBIT_S32_LE, | ||
710 | }, | ||
711 | .ops = &stm32_sai_pcm_dai_ops, | ||
712 | } | ||
713 | }; | ||
714 | |||
715 | static struct snd_soc_dai_driver stm32_sai_capture_dai[] = { | ||
716 | { | ||
717 | .probe = stm32_sai_dai_probe, | ||
718 | .id = 1, /* avoid call to fmt_single_name() */ | ||
719 | .capture = { | ||
720 | .channels_min = 1, | ||
721 | .channels_max = 2, | ||
722 | .rate_min = 8000, | ||
723 | .rate_max = 192000, | ||
724 | .rates = SNDRV_PCM_RATE_CONTINUOUS, | ||
725 | /* DMA does not support 24 bits transfers */ | ||
726 | .formats = | ||
727 | SNDRV_PCM_FMTBIT_S8 | | ||
728 | SNDRV_PCM_FMTBIT_S16_LE | | ||
729 | SNDRV_PCM_FMTBIT_S32_LE, | ||
730 | }, | ||
731 | .ops = &stm32_sai_pcm_dai_ops, | ||
732 | } | ||
733 | }; | ||
734 | |||
735 | static const struct snd_dmaengine_pcm_config stm32_sai_pcm_config = { | ||
736 | .pcm_hardware = &stm32_sai_pcm_hw, | ||
737 | .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, | ||
738 | }; | ||
739 | |||
740 | static const struct snd_soc_component_driver stm32_component = { | ||
741 | .name = "stm32-sai", | ||
742 | }; | ||
743 | |||
744 | static const struct of_device_id stm32_sai_sub_ids[] = { | ||
745 | { .compatible = "st,stm32-sai-sub-a", | ||
746 | .data = (void *)STM_SAI_A_ID}, | ||
747 | { .compatible = "st,stm32-sai-sub-b", | ||
748 | .data = (void *)STM_SAI_B_ID}, | ||
749 | {} | ||
750 | }; | ||
751 | MODULE_DEVICE_TABLE(of, stm32_sai_sub_ids); | ||
752 | |||
753 | static int stm32_sai_sub_parse_of(struct platform_device *pdev, | ||
754 | struct stm32_sai_sub_data *sai) | ||
755 | { | ||
756 | struct device_node *np = pdev->dev.of_node; | ||
757 | struct resource *res; | ||
758 | void __iomem *base; | ||
759 | |||
760 | if (!np) | ||
761 | return -ENODEV; | ||
762 | |||
763 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
764 | |||
765 | dev_err(&pdev->dev, "res %pr\n", res); | ||
766 | |||
767 | base = devm_ioremap_resource(&pdev->dev, res); | ||
768 | if (IS_ERR(base)) | ||
769 | return PTR_ERR(base); | ||
770 | |||
771 | sai->phys_addr = res->start; | ||
772 | sai->regmap = devm_regmap_init_mmio(&pdev->dev, base, | ||
773 | &stm32_sai_sub_regmap_config); | ||
774 | |||
775 | /* Get direction property */ | ||
776 | if (of_property_match_string(np, "dma-names", "tx") >= 0) { | ||
777 | sai->dir = SNDRV_PCM_STREAM_PLAYBACK; | ||
778 | } else if (of_property_match_string(np, "dma-names", "rx") >= 0) { | ||
779 | sai->dir = SNDRV_PCM_STREAM_CAPTURE; | ||
780 | } else { | ||
781 | dev_err(&pdev->dev, "Unsupported direction\n"); | ||
782 | return -EINVAL; | ||
783 | } | ||
784 | |||
785 | sai->sai_ck = devm_clk_get(&pdev->dev, "sai_ck"); | ||
786 | if (IS_ERR(sai->sai_ck)) { | ||
787 | dev_err(&pdev->dev, "missing kernel clock sai_ck\n"); | ||
788 | return PTR_ERR(sai->sai_ck); | ||
789 | } | ||
790 | |||
791 | return 0; | ||
792 | } | ||
793 | |||
794 | static int stm32_sai_sub_dais_init(struct platform_device *pdev, | ||
795 | struct stm32_sai_sub_data *sai) | ||
796 | { | ||
797 | sai->cpu_dai_drv = devm_kzalloc(&pdev->dev, | ||
798 | sizeof(struct snd_soc_dai_driver), | ||
799 | GFP_KERNEL); | ||
800 | if (!sai->cpu_dai_drv) | ||
801 | return -ENOMEM; | ||
802 | |||
803 | sai->cpu_dai_drv->name = dev_name(&pdev->dev); | ||
804 | if (STM_SAI_IS_PLAYBACK(sai)) { | ||
805 | memcpy(sai->cpu_dai_drv, &stm32_sai_playback_dai, | ||
806 | sizeof(stm32_sai_playback_dai)); | ||
807 | sai->cpu_dai_drv->playback.stream_name = sai->cpu_dai_drv->name; | ||
808 | } else { | ||
809 | memcpy(sai->cpu_dai_drv, &stm32_sai_capture_dai, | ||
810 | sizeof(stm32_sai_capture_dai)); | ||
811 | sai->cpu_dai_drv->capture.stream_name = sai->cpu_dai_drv->name; | ||
812 | } | ||
813 | |||
814 | return 0; | ||
815 | } | ||
816 | |||
817 | static int stm32_sai_sub_probe(struct platform_device *pdev) | ||
818 | { | ||
819 | struct stm32_sai_sub_data *sai; | ||
820 | const struct of_device_id *of_id; | ||
821 | int ret; | ||
822 | |||
823 | sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); | ||
824 | if (!sai) | ||
825 | return -ENOMEM; | ||
826 | |||
827 | of_id = of_match_device(stm32_sai_sub_ids, &pdev->dev); | ||
828 | if (!of_id) | ||
829 | return -EINVAL; | ||
830 | sai->id = (uintptr_t)of_id->data; | ||
831 | |||
832 | sai->pdev = pdev; | ||
833 | platform_set_drvdata(pdev, sai); | ||
834 | |||
835 | sai->pdata = dev_get_drvdata(pdev->dev.parent); | ||
836 | if (!sai->pdata) { | ||
837 | dev_err(&pdev->dev, "Parent device data not available\n"); | ||
838 | return -EINVAL; | ||
839 | } | ||
840 | |||
841 | ret = stm32_sai_sub_parse_of(pdev, sai); | ||
842 | if (ret) | ||
843 | return ret; | ||
844 | |||
845 | ret = stm32_sai_sub_dais_init(pdev, sai); | ||
846 | if (ret) | ||
847 | return ret; | ||
848 | |||
849 | ret = devm_request_irq(&pdev->dev, sai->pdata->irq, stm32_sai_isr, | ||
850 | IRQF_SHARED, dev_name(&pdev->dev), sai); | ||
851 | if (ret) { | ||
852 | dev_err(&pdev->dev, "irq request returned %d\n", ret); | ||
853 | return ret; | ||
854 | } | ||
855 | |||
856 | ret = devm_snd_soc_register_component(&pdev->dev, &stm32_component, | ||
857 | sai->cpu_dai_drv, 1); | ||
858 | if (ret) | ||
859 | return ret; | ||
860 | |||
861 | ret = devm_snd_dmaengine_pcm_register(&pdev->dev, | ||
862 | &stm32_sai_pcm_config, 0); | ||
863 | if (ret) { | ||
864 | dev_err(&pdev->dev, "could not register pcm dma\n"); | ||
865 | return ret; | ||
866 | } | ||
867 | |||
868 | return 0; | ||
869 | } | ||
870 | |||
871 | static struct platform_driver stm32_sai_sub_driver = { | ||
872 | .driver = { | ||
873 | .name = "st,stm32-sai-sub", | ||
874 | .of_match_table = stm32_sai_sub_ids, | ||
875 | }, | ||
876 | .probe = stm32_sai_sub_probe, | ||
877 | }; | ||
878 | |||
879 | module_platform_driver(stm32_sai_sub_driver); | ||
880 | |||
881 | MODULE_DESCRIPTION("STM32 Soc SAI sub-block Interface"); | ||
882 | MODULE_AUTHOR("Olivier Moysan, <olivier.moysan@st.com>"); | ||
883 | MODULE_ALIAS("platform:st,stm32-sai-sub"); | ||
884 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/sound/soc/sunxi/sun8i-codec-analog.c b/sound/soc/sunxi/sun8i-codec-analog.c index 72331332b72e..6c17c99c2c8d 100644 --- a/sound/soc/sunxi/sun8i-codec-analog.c +++ b/sound/soc/sunxi/sun8i-codec-analog.c | |||
@@ -252,24 +252,15 @@ static const DECLARE_TLV_DB_RANGE(sun8i_codec_mic_gain_scale, | |||
252 | ); | 252 | ); |
253 | 253 | ||
254 | static const struct snd_kcontrol_new sun8i_codec_common_controls[] = { | 254 | static const struct snd_kcontrol_new sun8i_codec_common_controls[] = { |
255 | /* Mixer pre-gains */ | 255 | /* Mixer pre-gain */ |
256 | SOC_SINGLE_TLV("Line In Playback Volume", SUN8I_ADDA_LINEIN_GCTRL, | ||
257 | SUN8I_ADDA_LINEIN_GCTRL_LINEING, | ||
258 | 0x7, 0, sun8i_codec_out_mixer_pregain_scale), | ||
259 | SOC_SINGLE_TLV("Mic1 Playback Volume", SUN8I_ADDA_MICIN_GCTRL, | 256 | SOC_SINGLE_TLV("Mic1 Playback Volume", SUN8I_ADDA_MICIN_GCTRL, |
260 | SUN8I_ADDA_MICIN_GCTRL_MIC1G, | 257 | SUN8I_ADDA_MICIN_GCTRL_MIC1G, |
261 | 0x7, 0, sun8i_codec_out_mixer_pregain_scale), | 258 | 0x7, 0, sun8i_codec_out_mixer_pregain_scale), |
262 | SOC_SINGLE_TLV("Mic2 Playback Volume", | ||
263 | SUN8I_ADDA_MICIN_GCTRL, SUN8I_ADDA_MICIN_GCTRL_MIC2G, | ||
264 | 0x7, 0, sun8i_codec_out_mixer_pregain_scale), | ||
265 | 259 | ||
266 | /* Microphone Amp boost gains */ | 260 | /* Microphone Amp boost gain */ |
267 | SOC_SINGLE_TLV("Mic1 Boost Volume", SUN8I_ADDA_MIC1G_MICBIAS_CTRL, | 261 | SOC_SINGLE_TLV("Mic1 Boost Volume", SUN8I_ADDA_MIC1G_MICBIAS_CTRL, |
268 | SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1BOOST, 0x7, 0, | 262 | SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1BOOST, 0x7, 0, |
269 | sun8i_codec_mic_gain_scale), | 263 | sun8i_codec_mic_gain_scale), |
270 | SOC_SINGLE_TLV("Mic2 Boost Volume", SUN8I_ADDA_MIC2G_CTRL, | ||
271 | SUN8I_ADDA_MIC2G_CTRL_MIC2BOOST, 0x7, 0, | ||
272 | sun8i_codec_mic_gain_scale), | ||
273 | 264 | ||
274 | /* ADC */ | 265 | /* ADC */ |
275 | SOC_SINGLE_TLV("ADC Gain Capture Volume", SUN8I_ADDA_ADC_AP_EN, | 266 | SOC_SINGLE_TLV("ADC Gain Capture Volume", SUN8I_ADDA_ADC_AP_EN, |
@@ -295,12 +286,8 @@ static const struct snd_soc_dapm_widget sun8i_codec_common_widgets[] = { | |||
295 | * stream widgets at the card level. | 286 | * stream widgets at the card level. |
296 | */ | 287 | */ |
297 | 288 | ||
298 | /* Line In */ | 289 | /* Microphone input */ |
299 | SND_SOC_DAPM_INPUT("LINEIN"), | ||
300 | |||
301 | /* Microphone inputs */ | ||
302 | SND_SOC_DAPM_INPUT("MIC1"), | 290 | SND_SOC_DAPM_INPUT("MIC1"), |
303 | SND_SOC_DAPM_INPUT("MIC2"), | ||
304 | 291 | ||
305 | /* Microphone Bias */ | 292 | /* Microphone Bias */ |
306 | SND_SOC_DAPM_SUPPLY("MBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL, | 293 | SND_SOC_DAPM_SUPPLY("MBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL, |
@@ -310,8 +297,6 @@ static const struct snd_soc_dapm_widget sun8i_codec_common_widgets[] = { | |||
310 | /* Mic input path */ | 297 | /* Mic input path */ |
311 | SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN8I_ADDA_MIC1G_MICBIAS_CTRL, | 298 | SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN8I_ADDA_MIC1G_MICBIAS_CTRL, |
312 | SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1AMPEN, 0, NULL, 0), | 299 | SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1AMPEN, 0, NULL, 0), |
313 | SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN8I_ADDA_MIC2G_CTRL, | ||
314 | SUN8I_ADDA_MIC2G_CTRL_MIC2AMPEN, 0, NULL, 0), | ||
315 | 300 | ||
316 | /* Mixers */ | 301 | /* Mixers */ |
317 | SND_SOC_DAPM_MIXER("Left Mixer", SUN8I_ADDA_DAC_PA_SRC, | 302 | SND_SOC_DAPM_MIXER("Left Mixer", SUN8I_ADDA_DAC_PA_SRC, |
@@ -335,35 +320,26 @@ static const struct snd_soc_dapm_widget sun8i_codec_common_widgets[] = { | |||
335 | static const struct snd_soc_dapm_route sun8i_codec_common_routes[] = { | 320 | static const struct snd_soc_dapm_route sun8i_codec_common_routes[] = { |
336 | /* Microphone Routes */ | 321 | /* Microphone Routes */ |
337 | { "Mic1 Amplifier", NULL, "MIC1"}, | 322 | { "Mic1 Amplifier", NULL, "MIC1"}, |
338 | { "Mic2 Amplifier", NULL, "MIC2"}, | ||
339 | 323 | ||
340 | /* Left Mixer Routes */ | 324 | /* Left Mixer Routes */ |
341 | { "Left Mixer", "DAC Playback Switch", "Left DAC" }, | 325 | { "Left Mixer", "DAC Playback Switch", "Left DAC" }, |
342 | { "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" }, | 326 | { "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" }, |
343 | { "Left Mixer", "Line In Playback Switch", "LINEIN" }, | ||
344 | { "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" }, | 327 | { "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" }, |
345 | { "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" }, | ||
346 | 328 | ||
347 | /* Right Mixer Routes */ | 329 | /* Right Mixer Routes */ |
348 | { "Right Mixer", "DAC Playback Switch", "Right DAC" }, | 330 | { "Right Mixer", "DAC Playback Switch", "Right DAC" }, |
349 | { "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" }, | 331 | { "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" }, |
350 | { "Right Mixer", "Line In Playback Switch", "LINEIN" }, | ||
351 | { "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" }, | 332 | { "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" }, |
352 | { "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" }, | ||
353 | 333 | ||
354 | /* Left ADC Mixer Routes */ | 334 | /* Left ADC Mixer Routes */ |
355 | { "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" }, | 335 | { "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" }, |
356 | { "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" }, | 336 | { "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" }, |
357 | { "Left ADC Mixer", "Line In Capture Switch", "LINEIN" }, | ||
358 | { "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" }, | 337 | { "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" }, |
359 | { "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" }, | ||
360 | 338 | ||
361 | /* Right ADC Mixer Routes */ | 339 | /* Right ADC Mixer Routes */ |
362 | { "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" }, | 340 | { "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" }, |
363 | { "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" }, | 341 | { "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" }, |
364 | { "Right ADC Mixer", "Line In Capture Switch", "LINEIN" }, | ||
365 | { "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" }, | 342 | { "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" }, |
366 | { "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" }, | ||
367 | 343 | ||
368 | /* ADC Routes */ | 344 | /* ADC Routes */ |
369 | { "Left ADC", NULL, "Left ADC Mixer" }, | 345 | { "Left ADC", NULL, "Left ADC Mixer" }, |
@@ -498,6 +474,61 @@ static int sun8i_codec_add_hmic(struct snd_soc_component *cmpnt) | |||
498 | return ret; | 474 | return ret; |
499 | } | 475 | } |
500 | 476 | ||
477 | /* line in specific controls, widgets and rines */ | ||
478 | static const struct snd_kcontrol_new sun8i_codec_linein_controls[] = { | ||
479 | /* Mixer pre-gain */ | ||
480 | SOC_SINGLE_TLV("Line In Playback Volume", SUN8I_ADDA_LINEIN_GCTRL, | ||
481 | SUN8I_ADDA_LINEIN_GCTRL_LINEING, | ||
482 | 0x7, 0, sun8i_codec_out_mixer_pregain_scale), | ||
483 | }; | ||
484 | |||
485 | static const struct snd_soc_dapm_widget sun8i_codec_linein_widgets[] = { | ||
486 | /* Line input */ | ||
487 | SND_SOC_DAPM_INPUT("LINEIN"), | ||
488 | }; | ||
489 | |||
490 | static const struct snd_soc_dapm_route sun8i_codec_linein_routes[] = { | ||
491 | { "Left Mixer", "Line In Playback Switch", "LINEIN" }, | ||
492 | |||
493 | { "Right Mixer", "Line In Playback Switch", "LINEIN" }, | ||
494 | |||
495 | { "Left ADC Mixer", "Line In Capture Switch", "LINEIN" }, | ||
496 | |||
497 | { "Right ADC Mixer", "Line In Capture Switch", "LINEIN" }, | ||
498 | }; | ||
499 | |||
500 | static int sun8i_codec_add_linein(struct snd_soc_component *cmpnt) | ||
501 | { | ||
502 | struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt); | ||
503 | struct device *dev = cmpnt->dev; | ||
504 | int ret; | ||
505 | |||
506 | ret = snd_soc_add_component_controls(cmpnt, | ||
507 | sun8i_codec_linein_controls, | ||
508 | ARRAY_SIZE(sun8i_codec_linein_controls)); | ||
509 | if (ret) { | ||
510 | dev_err(dev, "Failed to add Line In controls: %d\n", ret); | ||
511 | return ret; | ||
512 | } | ||
513 | |||
514 | ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_linein_widgets, | ||
515 | ARRAY_SIZE(sun8i_codec_linein_widgets)); | ||
516 | if (ret) { | ||
517 | dev_err(dev, "Failed to add Line In DAPM widgets: %d\n", ret); | ||
518 | return ret; | ||
519 | } | ||
520 | |||
521 | ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_linein_routes, | ||
522 | ARRAY_SIZE(sun8i_codec_linein_routes)); | ||
523 | if (ret) { | ||
524 | dev_err(dev, "Failed to add Line In DAPM routes: %d\n", ret); | ||
525 | return ret; | ||
526 | } | ||
527 | |||
528 | return 0; | ||
529 | } | ||
530 | |||
531 | |||
501 | /* line out specific controls, widgets and routes */ | 532 | /* line out specific controls, widgets and routes */ |
502 | static const DECLARE_TLV_DB_RANGE(sun8i_codec_lineout_vol_scale, | 533 | static const DECLARE_TLV_DB_RANGE(sun8i_codec_lineout_vol_scale, |
503 | 0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), | 534 | 0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), |
@@ -578,19 +609,90 @@ static int sun8i_codec_add_lineout(struct snd_soc_component *cmpnt) | |||
578 | return 0; | 609 | return 0; |
579 | } | 610 | } |
580 | 611 | ||
612 | /* mic2 specific controls, widgets and routes */ | ||
613 | static const struct snd_kcontrol_new sun8i_codec_mic2_controls[] = { | ||
614 | /* Mixer pre-gain */ | ||
615 | SOC_SINGLE_TLV("Mic2 Playback Volume", | ||
616 | SUN8I_ADDA_MICIN_GCTRL, SUN8I_ADDA_MICIN_GCTRL_MIC2G, | ||
617 | 0x7, 0, sun8i_codec_out_mixer_pregain_scale), | ||
618 | |||
619 | /* Microphone Amp boost gain */ | ||
620 | SOC_SINGLE_TLV("Mic2 Boost Volume", SUN8I_ADDA_MIC2G_CTRL, | ||
621 | SUN8I_ADDA_MIC2G_CTRL_MIC2BOOST, 0x7, 0, | ||
622 | sun8i_codec_mic_gain_scale), | ||
623 | }; | ||
624 | |||
625 | static const struct snd_soc_dapm_widget sun8i_codec_mic2_widgets[] = { | ||
626 | /* Microphone input */ | ||
627 | SND_SOC_DAPM_INPUT("MIC2"), | ||
628 | |||
629 | /* Mic input path */ | ||
630 | SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN8I_ADDA_MIC2G_CTRL, | ||
631 | SUN8I_ADDA_MIC2G_CTRL_MIC2AMPEN, 0, NULL, 0), | ||
632 | }; | ||
633 | |||
634 | static const struct snd_soc_dapm_route sun8i_codec_mic2_routes[] = { | ||
635 | { "Mic2 Amplifier", NULL, "MIC2"}, | ||
636 | |||
637 | { "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" }, | ||
638 | |||
639 | { "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" }, | ||
640 | |||
641 | { "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" }, | ||
642 | |||
643 | { "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" }, | ||
644 | }; | ||
645 | |||
646 | static int sun8i_codec_add_mic2(struct snd_soc_component *cmpnt) | ||
647 | { | ||
648 | struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt); | ||
649 | struct device *dev = cmpnt->dev; | ||
650 | int ret; | ||
651 | |||
652 | ret = snd_soc_add_component_controls(cmpnt, | ||
653 | sun8i_codec_mic2_controls, | ||
654 | ARRAY_SIZE(sun8i_codec_mic2_controls)); | ||
655 | if (ret) { | ||
656 | dev_err(dev, "Failed to add MIC2 controls: %d\n", ret); | ||
657 | return ret; | ||
658 | } | ||
659 | |||
660 | ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_mic2_widgets, | ||
661 | ARRAY_SIZE(sun8i_codec_mic2_widgets)); | ||
662 | if (ret) { | ||
663 | dev_err(dev, "Failed to add MIC2 DAPM widgets: %d\n", ret); | ||
664 | return ret; | ||
665 | } | ||
666 | |||
667 | ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_mic2_routes, | ||
668 | ARRAY_SIZE(sun8i_codec_mic2_routes)); | ||
669 | if (ret) { | ||
670 | dev_err(dev, "Failed to add MIC2 DAPM routes: %d\n", ret); | ||
671 | return ret; | ||
672 | } | ||
673 | |||
674 | return 0; | ||
675 | } | ||
676 | |||
581 | struct sun8i_codec_analog_quirks { | 677 | struct sun8i_codec_analog_quirks { |
582 | bool has_headphone; | 678 | bool has_headphone; |
583 | bool has_hmic; | 679 | bool has_hmic; |
680 | bool has_linein; | ||
584 | bool has_lineout; | 681 | bool has_lineout; |
682 | bool has_mic2; | ||
585 | }; | 683 | }; |
586 | 684 | ||
587 | static const struct sun8i_codec_analog_quirks sun8i_a23_quirks = { | 685 | static const struct sun8i_codec_analog_quirks sun8i_a23_quirks = { |
588 | .has_headphone = true, | 686 | .has_headphone = true, |
589 | .has_hmic = true, | 687 | .has_hmic = true, |
688 | .has_linein = true, | ||
689 | .has_mic2 = true, | ||
590 | }; | 690 | }; |
591 | 691 | ||
592 | static const struct sun8i_codec_analog_quirks sun8i_h3_quirks = { | 692 | static const struct sun8i_codec_analog_quirks sun8i_h3_quirks = { |
693 | .has_linein = true, | ||
593 | .has_lineout = true, | 694 | .has_lineout = true, |
695 | .has_mic2 = true, | ||
594 | }; | 696 | }; |
595 | 697 | ||
596 | static int sun8i_codec_analog_cmpnt_probe(struct snd_soc_component *cmpnt) | 698 | static int sun8i_codec_analog_cmpnt_probe(struct snd_soc_component *cmpnt) |
@@ -620,12 +722,24 @@ static int sun8i_codec_analog_cmpnt_probe(struct snd_soc_component *cmpnt) | |||
620 | return ret; | 722 | return ret; |
621 | } | 723 | } |
622 | 724 | ||
725 | if (quirks->has_linein) { | ||
726 | ret = sun8i_codec_add_linein(cmpnt); | ||
727 | if (ret) | ||
728 | return ret; | ||
729 | } | ||
730 | |||
623 | if (quirks->has_lineout) { | 731 | if (quirks->has_lineout) { |
624 | ret = sun8i_codec_add_lineout(cmpnt); | 732 | ret = sun8i_codec_add_lineout(cmpnt); |
625 | if (ret) | 733 | if (ret) |
626 | return ret; | 734 | return ret; |
627 | } | 735 | } |
628 | 736 | ||
737 | if (quirks->has_mic2) { | ||
738 | ret = sun8i_codec_add_mic2(cmpnt); | ||
739 | if (ret) | ||
740 | return ret; | ||
741 | } | ||
742 | |||
629 | return 0; | 743 | return 0; |
630 | } | 744 | } |
631 | 745 | ||
diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c index 7527ba29a5a0..5723c3404f6b 100644 --- a/sound/soc/sunxi/sun8i-codec.c +++ b/sound/soc/sunxi/sun8i-codec.c | |||
@@ -290,12 +290,10 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = { | |||
290 | SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA, 0), | 290 | SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA, 0), |
291 | 291 | ||
292 | /* DAC Mixers */ | 292 | /* DAC Mixers */ |
293 | SND_SOC_DAPM_MIXER("Left Digital DAC Mixer", SND_SOC_NOPM, 0, 0, | 293 | SOC_MIXER_ARRAY("Left Digital DAC Mixer", SND_SOC_NOPM, 0, 0, |
294 | sun8i_dac_mixer_controls, | 294 | sun8i_dac_mixer_controls), |
295 | ARRAY_SIZE(sun8i_dac_mixer_controls)), | 295 | SOC_MIXER_ARRAY("Right Digital DAC Mixer", SND_SOC_NOPM, 0, 0, |
296 | SND_SOC_DAPM_MIXER("Right Digital DAC Mixer", SND_SOC_NOPM, 0, 0, | 296 | sun8i_dac_mixer_controls), |
297 | sun8i_dac_mixer_controls, | ||
298 | ARRAY_SIZE(sun8i_dac_mixer_controls)), | ||
299 | 297 | ||
300 | /* Clocks */ | 298 | /* Clocks */ |
301 | SND_SOC_DAPM_SUPPLY("MODCLK AFI1", SUN8I_MOD_CLK_ENA, | 299 | SND_SOC_DAPM_SUPPLY("MODCLK AFI1", SUN8I_MOD_CLK_ENA, |