aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/sound/st,stm32-sai.txt89
-rw-r--r--Documentation/devicetree/bindings/sound/tas2552.txt10
-rw-r--r--sound/soc/Kconfig1
-rw-r--r--sound/soc/Makefile1
-rw-r--r--sound/soc/codecs/sta529.c7
-rw-r--r--sound/soc/codecs/tas2552.c6
-rw-r--r--sound/soc/sti/uniperif_player.c2
-rw-r--r--sound/soc/stm/Kconfig8
-rw-r--r--sound/soc/stm/Makefile6
-rw-r--r--sound/soc/stm/stm32_sai.c115
-rw-r--r--sound/soc/stm/stm32_sai.h200
-rw-r--r--sound/soc/stm/stm32_sai_sub.c884
-rw-r--r--sound/soc/sunxi/sun8i-codec-analog.c168
-rw-r--r--sound/soc/sunxi/sun8i-codec.c10
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 @@
1STMicroelectronics STM32 Serial Audio Interface (SAI).
2
3The SAI interface (Serial Audio Interface) offers a wide set of audio protocols
4as I2S standards, LSB or MSB-justified, PCM/DSP, TDM, and AC'97.
5The SAI contains two independent audio sub-blocks. Each sub-block has
6its own clock generator and I/O lines controller.
7
8Required 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
18Optional properties:
19 - resets: Reference to a reset controller asserting the SAI
20
21SAI subnodes:
22Two subnodes corresponding to SAI sub-block instances A et B can be defined.
23Subnode can be omitted for unsused sub-block.
24
25SAI 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
39Example:
40sound_card {
41 compatible = "audio-graph-card";
42 dais = <&sai1b_port>;
43};
44
45sai1: 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
83audio-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
5Required properties: 5Required 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:
14Optional properties: 15Optional properties:
15 - enable-gpio - gpio pin to enable/disable the device 16 - enable-gpio - gpio pin to enable/disable the device
16 17
17tas2552 can receive it's reference clock via MCLK, BCLK, IVCLKIN pin or use the 18tas2552 can receive its reference clock via MCLK, BCLK, IVCLKIN pin or use the
18internal 1.8MHz. This CLKIN is used by the PLL. In addition to PLL, the PDM 19internal 1.8MHz. This CLKIN is used by the PLL. In addition to PLL, the PDM
19reference clock is also selectable: PLL, IVCLKIN, BCLK or MCLK. 20reference clock is also selectable: PLL, IVCLKIN, BCLK or MCLK.
20For system integration the dt-bindings/sound/tas2552.h header file provides 21For system integration the dt-bindings/sound/tas2552.h header file provides
21defined values to selct and configure the PLL and PDM reference clocks. 22defined values to select and configure the PLL and PDM reference clocks.
22 23
23Example: 24Example:
24 25
25tas2552: tas2552@41 { 26tas2552: tas2552@41 {
26 compatible = "ti,tas2552"; 27 compatible = "ti,tas2552";
27 reg = <0x41>; 28 reg = <0x41>;
29 vbat-supply = <&reg_vbat>;
30 iovdd-supply = <&reg_iovdd>;
31 avdd-supply = <&reg_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"
64source "sound/soc/sirf/Kconfig" 64source "sound/soc/sirf/Kconfig"
65source "sound/soc/spear/Kconfig" 65source "sound/soc/spear/Kconfig"
66source "sound/soc/sti/Kconfig" 66source "sound/soc/sti/Kconfig"
67source "sound/soc/stm/Kconfig"
67source "sound/soc/sunxi/Kconfig" 68source "sound/soc/sunxi/Kconfig"
68source "sound/soc/tegra/Kconfig" 69source "sound/soc/tegra/Kconfig"
69source "sound/soc/txx9/Kconfig" 70source "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/
44obj-$(CONFIG_SND_SOC) += sirf/ 44obj-$(CONFIG_SND_SOC) += sirf/
45obj-$(CONFIG_SND_SOC) += spear/ 45obj-$(CONFIG_SND_SOC) += spear/
46obj-$(CONFIG_SND_SOC) += sti/ 46obj-$(CONFIG_SND_SOC) += sti/
47obj-$(CONFIG_SND_SOC) += stm/
47obj-$(CONFIG_SND_SOC) += sunxi/ 48obj-$(CONFIG_SND_SOC) += sunxi/
48obj-$(CONFIG_SND_SOC) += tegra/ 49obj-$(CONFIG_SND_SOC) += tegra/
49obj-$(CONFIG_SND_SOC) += txx9/ 50obj-$(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};
376MODULE_DEVICE_TABLE(i2c, sta529_i2c_id); 376MODULE_DEVICE_TABLE(i2c, sta529_i2c_id);
377 377
378static const struct of_device_id sta529_of_match[] = {
379 { .compatible = "st,sta529", },
380 { }
381};
382MODULE_DEVICE_TABLE(of, sta529_of_match);
383
378static struct i2c_driver sta529_i2c_driver = { 384static 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
617static int tas2552_codec_remove(struct snd_soc_codec *codec) 617static 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
643static int tas2552_resume(struct snd_soc_codec *codec) 643static 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 @@
1menuconfig 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
2snd-soc-stm32-sai-sub-objs := stm32_sai_sub.o
3obj-$(CONFIG_SND_SOC_STM32) += snd-soc-stm32-sai-sub.o
4
5snd-soc-stm32-sai-objs := stm32_sai.o
6obj-$(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
30static const struct of_device_id stm32_sai_ids[] = {
31 { .compatible = "st,stm32f4-sai", .data = (void *)SAI_STM32F4 },
32 {}
33};
34
35static 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
92static int stm32_sai_remove(struct platform_device *pdev)
93{
94 of_platform_depopulate(&pdev->dev);
95
96 return 0;
97}
98
99MODULE_DEVICE_TABLE(of, stm32_sai_ids);
100
101static 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
110module_platform_driver(stm32_sai_driver);
111
112MODULE_DESCRIPTION("STM32 Soc SAI Interface");
113MODULE_AUTHOR("Olivier Moysan, <olivier.moysan@st.com>");
114MODULE_ALIAS("platform:st,stm32-sai");
115MODULE_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
182enum 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 */
194struct 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 */
79struct 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
102enum 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
110static 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
127static 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
137static 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
154static 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
165static 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
219static 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
232static 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
278static 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
370static 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
409static 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
461static 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
506static 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
538static 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
581static 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
605static 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
648static 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
659static 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
676static 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
686static 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
695static 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
715static 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
735static 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
740static const struct snd_soc_component_driver stm32_component = {
741 .name = "stm32-sai",
742};
743
744static 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};
751MODULE_DEVICE_TABLE(of, stm32_sai_sub_ids);
752
753static 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
794static 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
817static 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
871static 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
879module_platform_driver(stm32_sai_sub_driver);
880
881MODULE_DESCRIPTION("STM32 Soc SAI sub-block Interface");
882MODULE_AUTHOR("Olivier Moysan, <olivier.moysan@st.com>");
883MODULE_ALIAS("platform:st,stm32-sai-sub");
884MODULE_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
254static const struct snd_kcontrol_new sun8i_codec_common_controls[] = { 254static 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[] = {
335static const struct snd_soc_dapm_route sun8i_codec_common_routes[] = { 320static 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 */
478static 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
485static const struct snd_soc_dapm_widget sun8i_codec_linein_widgets[] = {
486 /* Line input */
487 SND_SOC_DAPM_INPUT("LINEIN"),
488};
489
490static 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
500static 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 */
502static const DECLARE_TLV_DB_RANGE(sun8i_codec_lineout_vol_scale, 533static 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 */
613static 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
625static 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
634static 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
646static 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
581struct sun8i_codec_analog_quirks { 677struct 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
587static const struct sun8i_codec_analog_quirks sun8i_a23_quirks = { 685static 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
592static const struct sun8i_codec_analog_quirks sun8i_h3_quirks = { 692static 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
596static int sun8i_codec_analog_cmpnt_probe(struct snd_soc_component *cmpnt) 698static 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,