14c694f28SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
245fb6b6fSEmilio López /*
345fb6b6fSEmilio López * Copyright 2014 Emilio López <emilio@elopez.com.ar>
445fb6b6fSEmilio López * Copyright 2014 Jon Smirl <jonsmirl@gmail.com>
545fb6b6fSEmilio López * Copyright 2015 Maxime Ripard <maxime.ripard@free-electrons.com>
6474d147aSAdam Sampson * Copyright 2015 Adam Sampson <ats@offog.org>
7bc03f0d5SChen-Yu Tsai * Copyright 2016 Chen-Yu Tsai <wens@csie.org>
845fb6b6fSEmilio López *
945fb6b6fSEmilio López * Based on the Allwinner SDK driver, released under the GPL.
1045fb6b6fSEmilio López */
1145fb6b6fSEmilio López
1245fb6b6fSEmilio López #include <linux/init.h>
1345fb6b6fSEmilio López #include <linux/kernel.h>
1445fb6b6fSEmilio López #include <linux/module.h>
1545fb6b6fSEmilio López #include <linux/platform_device.h>
1645fb6b6fSEmilio López #include <linux/delay.h>
1745fb6b6fSEmilio López #include <linux/slab.h>
1845fb6b6fSEmilio López #include <linux/of.h>
1945fb6b6fSEmilio López #include <linux/of_address.h>
20bc03f0d5SChen-Yu Tsai #include <linux/of_device.h>
21bc03f0d5SChen-Yu Tsai #include <linux/of_platform.h>
2245fb6b6fSEmilio López #include <linux/clk.h>
2345fb6b6fSEmilio López #include <linux/regmap.h>
249aead156SChen-Yu Tsai #include <linux/reset.h>
2540592627SHans de Goede #include <linux/gpio/consumer.h>
2645fb6b6fSEmilio López
2745fb6b6fSEmilio López #include <sound/core.h>
2845fb6b6fSEmilio López #include <sound/pcm.h>
2945fb6b6fSEmilio López #include <sound/pcm_params.h>
3045fb6b6fSEmilio López #include <sound/soc.h>
3145fb6b6fSEmilio López #include <sound/tlv.h>
3245fb6b6fSEmilio López #include <sound/initval.h>
3345fb6b6fSEmilio López #include <sound/dmaengine_pcm.h>
3445fb6b6fSEmilio López
35bd720ecfSChen-Yu Tsai /* Codec DAC digital controls and FIFO registers */
3645fb6b6fSEmilio López #define SUN4I_CODEC_DAC_DPC (0x00)
3745fb6b6fSEmilio López #define SUN4I_CODEC_DAC_DPC_EN_DA (31)
3845fb6b6fSEmilio López #define SUN4I_CODEC_DAC_DPC_DVOL (12)
3945fb6b6fSEmilio López #define SUN4I_CODEC_DAC_FIFOC (0x04)
4045fb6b6fSEmilio López #define SUN4I_CODEC_DAC_FIFOC_DAC_FS (29)
4145fb6b6fSEmilio López #define SUN4I_CODEC_DAC_FIFOC_FIR_VERSION (28)
4245fb6b6fSEmilio López #define SUN4I_CODEC_DAC_FIFOC_SEND_LASAT (26)
4345fb6b6fSEmilio López #define SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE (24)
4445fb6b6fSEmilio López #define SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT (21)
4545fb6b6fSEmilio López #define SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL (8)
4645fb6b6fSEmilio López #define SUN4I_CODEC_DAC_FIFOC_MONO_EN (6)
4745fb6b6fSEmilio López #define SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS (5)
4845fb6b6fSEmilio López #define SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN (4)
4945fb6b6fSEmilio López #define SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH (0)
5045fb6b6fSEmilio López #define SUN4I_CODEC_DAC_FIFOS (0x08)
5145fb6b6fSEmilio López #define SUN4I_CODEC_DAC_TXDATA (0x0c)
52bd720ecfSChen-Yu Tsai
53bd720ecfSChen-Yu Tsai /* Codec DAC side analog signal controls */
5445fb6b6fSEmilio López #define SUN4I_CODEC_DAC_ACTL (0x10)
5545fb6b6fSEmilio López #define SUN4I_CODEC_DAC_ACTL_DACAENR (31)
5645fb6b6fSEmilio López #define SUN4I_CODEC_DAC_ACTL_DACAENL (30)
5745fb6b6fSEmilio López #define SUN4I_CODEC_DAC_ACTL_MIXEN (29)
5867690c28SDanny Milosavljevic #define SUN4I_CODEC_DAC_ACTL_LNG (26)
5950d16419SDanny Milosavljevic #define SUN4I_CODEC_DAC_ACTL_FMG (23)
6063bd8489SDanny Milosavljevic #define SUN4I_CODEC_DAC_ACTL_MICG (20)
6167690c28SDanny Milosavljevic #define SUN4I_CODEC_DAC_ACTL_LLNS (19)
6267690c28SDanny Milosavljevic #define SUN4I_CODEC_DAC_ACTL_RLNS (18)
6350d16419SDanny Milosavljevic #define SUN4I_CODEC_DAC_ACTL_LFMS (17)
6450d16419SDanny Milosavljevic #define SUN4I_CODEC_DAC_ACTL_RFMS (16)
6545fb6b6fSEmilio López #define SUN4I_CODEC_DAC_ACTL_LDACLMIXS (15)
6645fb6b6fSEmilio López #define SUN4I_CODEC_DAC_ACTL_RDACRMIXS (14)
6745fb6b6fSEmilio López #define SUN4I_CODEC_DAC_ACTL_LDACRMIXS (13)
6844a1f4e8SDanny Milosavljevic #define SUN4I_CODEC_DAC_ACTL_MIC1LS (12)
6944a1f4e8SDanny Milosavljevic #define SUN4I_CODEC_DAC_ACTL_MIC1RS (11)
7044a1f4e8SDanny Milosavljevic #define SUN4I_CODEC_DAC_ACTL_MIC2LS (10)
7144a1f4e8SDanny Milosavljevic #define SUN4I_CODEC_DAC_ACTL_MIC2RS (9)
7245fb6b6fSEmilio López #define SUN4I_CODEC_DAC_ACTL_DACPAS (8)
7345fb6b6fSEmilio López #define SUN4I_CODEC_DAC_ACTL_MIXPAS (7)
7445fb6b6fSEmilio López #define SUN4I_CODEC_DAC_ACTL_PA_MUTE (6)
7545fb6b6fSEmilio López #define SUN4I_CODEC_DAC_ACTL_PA_VOL (0)
7645fb6b6fSEmilio López #define SUN4I_CODEC_DAC_TUNE (0x14)
7745fb6b6fSEmilio López #define SUN4I_CODEC_DAC_DEBUG (0x18)
7845fb6b6fSEmilio López
79bd720ecfSChen-Yu Tsai /* Codec ADC digital controls and FIFO registers */
8045fb6b6fSEmilio López #define SUN4I_CODEC_ADC_FIFOC (0x1c)
811fb34b48SMaxime Ripard #define SUN4I_CODEC_ADC_FIFOC_ADC_FS (29)
8245fb6b6fSEmilio López #define SUN4I_CODEC_ADC_FIFOC_EN_AD (28)
8345fb6b6fSEmilio López #define SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE (24)
8445fb6b6fSEmilio López #define SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL (8)
8545fb6b6fSEmilio López #define SUN4I_CODEC_ADC_FIFOC_MONO_EN (7)
8645fb6b6fSEmilio López #define SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS (6)
8745fb6b6fSEmilio López #define SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN (4)
8845fb6b6fSEmilio López #define SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH (0)
8945fb6b6fSEmilio López #define SUN4I_CODEC_ADC_FIFOS (0x20)
9045fb6b6fSEmilio López #define SUN4I_CODEC_ADC_RXDATA (0x24)
91bd720ecfSChen-Yu Tsai
92bd720ecfSChen-Yu Tsai /* Codec ADC side analog signal controls */
9345fb6b6fSEmilio López #define SUN4I_CODEC_ADC_ACTL (0x28)
9445fb6b6fSEmilio López #define SUN4I_CODEC_ADC_ACTL_ADC_R_EN (31)
9545fb6b6fSEmilio López #define SUN4I_CODEC_ADC_ACTL_ADC_L_EN (30)
9645fb6b6fSEmilio López #define SUN4I_CODEC_ADC_ACTL_PREG1EN (29)
9745fb6b6fSEmilio López #define SUN4I_CODEC_ADC_ACTL_PREG2EN (28)
9845fb6b6fSEmilio López #define SUN4I_CODEC_ADC_ACTL_VMICEN (27)
99b329c78eSDanny Milosavljevic #define SUN4I_CODEC_ADC_ACTL_PREG1 (25)
100b329c78eSDanny Milosavljevic #define SUN4I_CODEC_ADC_ACTL_PREG2 (23)
10145fb6b6fSEmilio López #define SUN4I_CODEC_ADC_ACTL_VADCG (20)
10245fb6b6fSEmilio López #define SUN4I_CODEC_ADC_ACTL_ADCIS (17)
10367690c28SDanny Milosavljevic #define SUN4I_CODEC_ADC_ACTL_LNPREG (13)
10445fb6b6fSEmilio López #define SUN4I_CODEC_ADC_ACTL_PA_EN (4)
10545fb6b6fSEmilio López #define SUN4I_CODEC_ADC_ACTL_DDE (3)
10645fb6b6fSEmilio López #define SUN4I_CODEC_ADC_DEBUG (0x2c)
10745fb6b6fSEmilio López
108bd720ecfSChen-Yu Tsai /* FIFO counters */
10945fb6b6fSEmilio López #define SUN4I_CODEC_DAC_TXCNT (0x30)
11045fb6b6fSEmilio López #define SUN4I_CODEC_ADC_RXCNT (0x34)
111bd720ecfSChen-Yu Tsai
112bd720ecfSChen-Yu Tsai /* Calibration register (sun7i only) */
1134f0c4e99SDanny Milosavljevic #define SUN7I_CODEC_AC_DAC_CAL (0x38)
114bd720ecfSChen-Yu Tsai
115bd720ecfSChen-Yu Tsai /* Microphone controls (sun7i only) */
1164f0c4e99SDanny Milosavljevic #define SUN7I_CODEC_AC_MIC_PHONE_CAL (0x3c)
11745fb6b6fSEmilio López
118b329c78eSDanny Milosavljevic #define SUN7I_CODEC_AC_MIC_PHONE_CAL_PREG1 (29)
119b329c78eSDanny Milosavljevic #define SUN7I_CODEC_AC_MIC_PHONE_CAL_PREG2 (26)
120b329c78eSDanny Milosavljevic
1218d9e4c9eSChen-Yu Tsai /*
1228d9e4c9eSChen-Yu Tsai * sun6i specific registers
1238d9e4c9eSChen-Yu Tsai *
1248d9e4c9eSChen-Yu Tsai * sun6i shares the same digital control and FIFO registers as sun4i,
1258d9e4c9eSChen-Yu Tsai * but only the DAC digital controls are at the same offset. The others
1268d9e4c9eSChen-Yu Tsai * have been moved around to accommodate extra analog controls.
1278d9e4c9eSChen-Yu Tsai */
1288d9e4c9eSChen-Yu Tsai
1298d9e4c9eSChen-Yu Tsai /* Codec DAC digital controls and FIFO registers */
1308d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_ADC_FIFOC (0x10)
1318d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_ADC_FIFOC_EN_AD (28)
1328d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_ADC_FIFOS (0x14)
1338d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_ADC_RXDATA (0x18)
1348d9e4c9eSChen-Yu Tsai
1358d9e4c9eSChen-Yu Tsai /* Output mixer and gain controls */
1368d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_OM_DACA_CTRL (0x20)
1378d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_OM_DACA_CTRL_DACAREN (31)
1388d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_OM_DACA_CTRL_DACALEN (30)
1398d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_OM_DACA_CTRL_RMIXEN (29)
1408d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_OM_DACA_CTRL_LMIXEN (28)
1418d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC1 (23)
1428d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC2 (22)
1438d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_OM_DACA_CTRL_RMIX_PHONE (21)
1448d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_OM_DACA_CTRL_RMIX_PHONEP (20)
1458d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_OM_DACA_CTRL_RMIX_LINEINR (19)
1468d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACR (18)
1478d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACL (17)
1488d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC1 (16)
1498d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC2 (15)
1508d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_OM_DACA_CTRL_LMIX_PHONE (14)
1518d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_OM_DACA_CTRL_LMIX_PHONEN (13)
1528d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_OM_DACA_CTRL_LMIX_LINEINL (12)
1538d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACL (11)
1548d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACR (10)
1558d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_OM_DACA_CTRL_RHPIS (9)
1568d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_OM_DACA_CTRL_LHPIS (8)
1578d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE (7)
1588d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE (6)
1598d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_OM_DACA_CTRL_HPVOL (0)
1608d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_OM_PA_CTRL (0x24)
1618d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_OM_PA_CTRL_HPPAEN (31)
1628d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_OM_PA_CTRL_HPCOM_CTL (29)
1638d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_OM_PA_CTRL_COMPTEN (28)
1648d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_OM_PA_CTRL_MIC1G (15)
1658d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_OM_PA_CTRL_MIC2G (12)
1668d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_OM_PA_CTRL_LINEING (9)
1678d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_OM_PA_CTRL_PHONEG (6)
1688d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_OM_PA_CTRL_PHONEPG (3)
1698d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_OM_PA_CTRL_PHONENG (0)
1708d9e4c9eSChen-Yu Tsai
1718d9e4c9eSChen-Yu Tsai /* Microphone, line out and phone out controls */
1728d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_MIC_CTRL (0x28)
1738d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_MIC_CTRL_HBIASEN (31)
1748d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_MIC_CTRL_MBIASEN (30)
1758d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_MIC_CTRL_MIC1AMPEN (28)
1768d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_MIC_CTRL_MIC1BOOST (25)
1778d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_MIC_CTRL_MIC2AMPEN (24)
1788d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_MIC_CTRL_MIC2BOOST (21)
1798d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_MIC_CTRL_MIC2SLT (20)
1808d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_MIC_CTRL_LINEOUTLEN (19)
1818d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_MIC_CTRL_LINEOUTREN (18)
1828d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_MIC_CTRL_LINEOUTLSRC (17)
1838d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_MIC_CTRL_LINEOUTRSRC (16)
1848d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_MIC_CTRL_LINEOUTVC (11)
1858d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_MIC_CTRL_PHONEPREG (8)
1868d9e4c9eSChen-Yu Tsai
1878d9e4c9eSChen-Yu Tsai /* ADC mixer controls */
1888d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_ADC_ACTL (0x2c)
1898d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_ADC_ACTL_ADCREN (31)
1908d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_ADC_ACTL_ADCLEN (30)
1918d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_ADC_ACTL_ADCRG (27)
1928d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_ADC_ACTL_ADCLG (24)
1938d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC1 (13)
1948d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC2 (12)
1958d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_ADC_ACTL_RADCMIX_PHONE (11)
1968d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_ADC_ACTL_RADCMIX_PHONEP (10)
1978d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_ADC_ACTL_RADCMIX_LINEINR (9)
1988d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXR (8)
1998d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXL (7)
2008d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC1 (6)
2018d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC2 (5)
2028d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_ADC_ACTL_LADCMIX_PHONE (4)
2038d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_ADC_ACTL_LADCMIX_PHONEN (3)
2048d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_ADC_ACTL_LADCMIX_LINEINL (2)
2058d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXL (1)
2068d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXR (0)
2078d9e4c9eSChen-Yu Tsai
2088d9e4c9eSChen-Yu Tsai /* Analog performance tuning controls */
2098d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_ADDA_TUNE (0x30)
2108d9e4c9eSChen-Yu Tsai
2118d9e4c9eSChen-Yu Tsai /* Calibration controls */
2128d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_CALIBRATION (0x34)
2138d9e4c9eSChen-Yu Tsai
2148d9e4c9eSChen-Yu Tsai /* FIFO counters */
2158d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_DAC_TXCNT (0x40)
2168d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_ADC_RXCNT (0x44)
2178d9e4c9eSChen-Yu Tsai
2188d9e4c9eSChen-Yu Tsai /* headset jack detection and button support registers */
2198d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_HMIC_CTL (0x50)
2208d9e4c9eSChen-Yu Tsai #define SUN6I_CODEC_HMIC_DATA (0x54)
2218d9e4c9eSChen-Yu Tsai
2228d9e4c9eSChen-Yu Tsai /* TODO sun6i DAP (Digital Audio Processing) bits */
2238d9e4c9eSChen-Yu Tsai
224dac5f86bSChen-Yu Tsai /* FIFO counters moved on A23 */
225dac5f86bSChen-Yu Tsai #define SUN8I_A23_CODEC_DAC_TXCNT (0x1c)
226dac5f86bSChen-Yu Tsai #define SUN8I_A23_CODEC_ADC_RXCNT (0x20)
227dac5f86bSChen-Yu Tsai
2284a15b24aSChen-Yu Tsai /* TX FIFO moved on H3 */
2294a15b24aSChen-Yu Tsai #define SUN8I_H3_CODEC_DAC_TXDATA (0x20)
2304a15b24aSChen-Yu Tsai #define SUN8I_H3_CODEC_DAC_DBG (0x48)
2314a15b24aSChen-Yu Tsai #define SUN8I_H3_CODEC_ADC_DBG (0x4c)
2324a15b24aSChen-Yu Tsai
2334a15b24aSChen-Yu Tsai /* TODO H3 DAP (Digital Audio Processing) bits */
2344a15b24aSChen-Yu Tsai
23545fb6b6fSEmilio López struct sun4i_codec {
23645fb6b6fSEmilio López struct device *dev;
23745fb6b6fSEmilio López struct regmap *regmap;
23845fb6b6fSEmilio López struct clk *clk_apb;
23945fb6b6fSEmilio López struct clk *clk_module;
2409aead156SChen-Yu Tsai struct reset_control *rst;
24140592627SHans de Goede struct gpio_desc *gpio_pa;
24245fb6b6fSEmilio López
243bc03f0d5SChen-Yu Tsai /* ADC_FIFOC register is at different offset on different SoCs */
244bc03f0d5SChen-Yu Tsai struct regmap_field *reg_adc_fifoc;
245bc03f0d5SChen-Yu Tsai
2461fb34b48SMaxime Ripard struct snd_dmaengine_dai_dma_data capture_dma_data;
24745fb6b6fSEmilio López struct snd_dmaengine_dai_dma_data playback_dma_data;
24845fb6b6fSEmilio López };
24945fb6b6fSEmilio López
sun4i_codec_start_playback(struct sun4i_codec * scodec)25045fb6b6fSEmilio López static void sun4i_codec_start_playback(struct sun4i_codec *scodec)
25145fb6b6fSEmilio López {
25245fb6b6fSEmilio López /* Flush TX FIFO */
253b2366240SLi Chen regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
25445fb6b6fSEmilio López BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH));
25545fb6b6fSEmilio López
25645fb6b6fSEmilio López /* Enable DAC DRQ */
257b2366240SLi Chen regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
25845fb6b6fSEmilio López BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN));
25945fb6b6fSEmilio López }
26045fb6b6fSEmilio López
sun4i_codec_stop_playback(struct sun4i_codec * scodec)26145fb6b6fSEmilio López static void sun4i_codec_stop_playback(struct sun4i_codec *scodec)
26245fb6b6fSEmilio López {
26345fb6b6fSEmilio López /* Disable DAC DRQ */
264b2366240SLi Chen regmap_clear_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
265b2366240SLi Chen BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN));
26645fb6b6fSEmilio López }
26745fb6b6fSEmilio López
sun4i_codec_start_capture(struct sun4i_codec * scodec)2681fb34b48SMaxime Ripard static void sun4i_codec_start_capture(struct sun4i_codec *scodec)
2691fb34b48SMaxime Ripard {
2701fb34b48SMaxime Ripard /* Enable ADC DRQ */
271b2366240SLi Chen regmap_field_set_bits(scodec->reg_adc_fifoc,
2721fb34b48SMaxime Ripard BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN));
2731fb34b48SMaxime Ripard }
2741fb34b48SMaxime Ripard
sun4i_codec_stop_capture(struct sun4i_codec * scodec)2751fb34b48SMaxime Ripard static void sun4i_codec_stop_capture(struct sun4i_codec *scodec)
2761fb34b48SMaxime Ripard {
2771fb34b48SMaxime Ripard /* Disable ADC DRQ */
278b2366240SLi Chen regmap_field_clear_bits(scodec->reg_adc_fifoc,
279b2366240SLi Chen BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN));
2801fb34b48SMaxime Ripard }
2811fb34b48SMaxime Ripard
sun4i_codec_trigger(struct snd_pcm_substream * substream,int cmd,struct snd_soc_dai * dai)28245fb6b6fSEmilio López static int sun4i_codec_trigger(struct snd_pcm_substream *substream, int cmd,
28345fb6b6fSEmilio López struct snd_soc_dai *dai)
28445fb6b6fSEmilio López {
285c09e34b7SKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
28645fb6b6fSEmilio López struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
28745fb6b6fSEmilio López
28845fb6b6fSEmilio López switch (cmd) {
28945fb6b6fSEmilio López case SNDRV_PCM_TRIGGER_START:
29045fb6b6fSEmilio López case SNDRV_PCM_TRIGGER_RESUME:
29145fb6b6fSEmilio López case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
2921fb34b48SMaxime Ripard if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
29345fb6b6fSEmilio López sun4i_codec_start_playback(scodec);
2941fb34b48SMaxime Ripard else
2951fb34b48SMaxime Ripard sun4i_codec_start_capture(scodec);
29645fb6b6fSEmilio López break;
29745fb6b6fSEmilio López
29845fb6b6fSEmilio López case SNDRV_PCM_TRIGGER_STOP:
29945fb6b6fSEmilio López case SNDRV_PCM_TRIGGER_SUSPEND:
30045fb6b6fSEmilio López case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
3011fb34b48SMaxime Ripard if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
30245fb6b6fSEmilio López sun4i_codec_stop_playback(scodec);
3031fb34b48SMaxime Ripard else
3041fb34b48SMaxime Ripard sun4i_codec_stop_capture(scodec);
30545fb6b6fSEmilio López break;
30645fb6b6fSEmilio López
30745fb6b6fSEmilio López default:
30845fb6b6fSEmilio López return -EINVAL;
30945fb6b6fSEmilio López }
31045fb6b6fSEmilio López
31145fb6b6fSEmilio López return 0;
31245fb6b6fSEmilio López }
31345fb6b6fSEmilio López
sun4i_codec_prepare_capture(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)3141fb34b48SMaxime Ripard static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream,
3151fb34b48SMaxime Ripard struct snd_soc_dai *dai)
3161fb34b48SMaxime Ripard {
317c09e34b7SKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
3181fb34b48SMaxime Ripard struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
3191fb34b48SMaxime Ripard
3201fb34b48SMaxime Ripard
3211fb34b48SMaxime Ripard /* Flush RX FIFO */
322b2366240SLi Chen regmap_field_set_bits(scodec->reg_adc_fifoc,
3231fb34b48SMaxime Ripard BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH));
3241fb34b48SMaxime Ripard
3251fb34b48SMaxime Ripard
3261fb34b48SMaxime Ripard /* Set RX FIFO trigger level */
327bc03f0d5SChen-Yu Tsai regmap_field_update_bits(scodec->reg_adc_fifoc,
3281fb34b48SMaxime Ripard 0xf << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL,
3291fb34b48SMaxime Ripard 0x7 << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL);
3301fb34b48SMaxime Ripard
3311fb34b48SMaxime Ripard /*
3321fb34b48SMaxime Ripard * FIXME: Undocumented in the datasheet, but
3330d8aa2ccSRandy Dunlap * Allwinner's code mentions that it is
3341fb34b48SMaxime Ripard * related to microphone gain
3351fb34b48SMaxime Ripard */
3368d9e4c9eSChen-Yu Tsai if (of_device_is_compatible(scodec->dev->of_node,
3378d9e4c9eSChen-Yu Tsai "allwinner,sun4i-a10-codec") ||
3388d9e4c9eSChen-Yu Tsai of_device_is_compatible(scodec->dev->of_node,
3398d9e4c9eSChen-Yu Tsai "allwinner,sun7i-a20-codec")) {
3401fb34b48SMaxime Ripard regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_ACTL,
3411fb34b48SMaxime Ripard 0x3 << 25,
3421fb34b48SMaxime Ripard 0x1 << 25);
3438d9e4c9eSChen-Yu Tsai }
3441fb34b48SMaxime Ripard
3451fb34b48SMaxime Ripard if (of_device_is_compatible(scodec->dev->of_node,
3461fb34b48SMaxime Ripard "allwinner,sun7i-a20-codec"))
3471fb34b48SMaxime Ripard /* FIXME: Undocumented bits */
3481fb34b48SMaxime Ripard regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_TUNE,
3491fb34b48SMaxime Ripard 0x3 << 8,
3501fb34b48SMaxime Ripard 0x1 << 8);
3511fb34b48SMaxime Ripard
3521fb34b48SMaxime Ripard return 0;
3531fb34b48SMaxime Ripard }
3541fb34b48SMaxime Ripard
sun4i_codec_prepare_playback(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)3551fb34b48SMaxime Ripard static int sun4i_codec_prepare_playback(struct snd_pcm_substream *substream,
35645fb6b6fSEmilio López struct snd_soc_dai *dai)
35745fb6b6fSEmilio López {
358c09e34b7SKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
35945fb6b6fSEmilio López struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
36045fb6b6fSEmilio López u32 val;
36145fb6b6fSEmilio López
36245fb6b6fSEmilio López /* Flush the TX FIFO */
363b2366240SLi Chen regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
36445fb6b6fSEmilio López BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH));
36545fb6b6fSEmilio López
36645fb6b6fSEmilio López /* Set TX FIFO Empty Trigger Level */
36745fb6b6fSEmilio López regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
36845fb6b6fSEmilio López 0x3f << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL,
36945fb6b6fSEmilio López 0xf << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL);
37045fb6b6fSEmilio López
37145fb6b6fSEmilio López if (substream->runtime->rate > 32000)
37245fb6b6fSEmilio López /* Use 64 bits FIR filter */
37345fb6b6fSEmilio López val = 0;
37445fb6b6fSEmilio López else
37545fb6b6fSEmilio López /* Use 32 bits FIR filter */
37645fb6b6fSEmilio López val = BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION);
37745fb6b6fSEmilio López
37845fb6b6fSEmilio López regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
37945fb6b6fSEmilio López BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION),
38045fb6b6fSEmilio López val);
38145fb6b6fSEmilio López
38245fb6b6fSEmilio López /* Send zeros when we have an underrun */
383b2366240SLi Chen regmap_clear_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
384b2366240SLi Chen BIT(SUN4I_CODEC_DAC_FIFOC_SEND_LASAT));
38545fb6b6fSEmilio López
38645fb6b6fSEmilio López return 0;
3871fb34b48SMaxime Ripard };
3881fb34b48SMaxime Ripard
sun4i_codec_prepare(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)3891fb34b48SMaxime Ripard static int sun4i_codec_prepare(struct snd_pcm_substream *substream,
3901fb34b48SMaxime Ripard struct snd_soc_dai *dai)
3911fb34b48SMaxime Ripard {
3921fb34b48SMaxime Ripard if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
3931fb34b48SMaxime Ripard return sun4i_codec_prepare_playback(substream, dai);
3941fb34b48SMaxime Ripard
3951fb34b48SMaxime Ripard return sun4i_codec_prepare_capture(substream, dai);
39645fb6b6fSEmilio López }
39745fb6b6fSEmilio López
sun4i_codec_get_mod_freq(struct snd_pcm_hw_params * params)39845fb6b6fSEmilio López static unsigned long sun4i_codec_get_mod_freq(struct snd_pcm_hw_params *params)
39945fb6b6fSEmilio López {
40045fb6b6fSEmilio López unsigned int rate = params_rate(params);
40145fb6b6fSEmilio López
40245fb6b6fSEmilio López switch (rate) {
40345fb6b6fSEmilio López case 176400:
40445fb6b6fSEmilio López case 88200:
40545fb6b6fSEmilio López case 44100:
40645fb6b6fSEmilio López case 33075:
40745fb6b6fSEmilio López case 22050:
40845fb6b6fSEmilio López case 14700:
40945fb6b6fSEmilio López case 11025:
41045fb6b6fSEmilio López case 7350:
41145fb6b6fSEmilio López return 22579200;
41245fb6b6fSEmilio López
41345fb6b6fSEmilio López case 192000:
41445fb6b6fSEmilio López case 96000:
41545fb6b6fSEmilio López case 48000:
41645fb6b6fSEmilio López case 32000:
41745fb6b6fSEmilio López case 24000:
41845fb6b6fSEmilio López case 16000:
41945fb6b6fSEmilio López case 12000:
42045fb6b6fSEmilio López case 8000:
42145fb6b6fSEmilio López return 24576000;
42245fb6b6fSEmilio López
42345fb6b6fSEmilio López default:
42445fb6b6fSEmilio López return 0;
42545fb6b6fSEmilio López }
42645fb6b6fSEmilio López }
42745fb6b6fSEmilio López
sun4i_codec_get_hw_rate(struct snd_pcm_hw_params * params)42845fb6b6fSEmilio López static int sun4i_codec_get_hw_rate(struct snd_pcm_hw_params *params)
42945fb6b6fSEmilio López {
43045fb6b6fSEmilio López unsigned int rate = params_rate(params);
43145fb6b6fSEmilio López
43245fb6b6fSEmilio López switch (rate) {
43345fb6b6fSEmilio López case 192000:
43445fb6b6fSEmilio López case 176400:
43545fb6b6fSEmilio López return 6;
43645fb6b6fSEmilio López
43745fb6b6fSEmilio López case 96000:
43845fb6b6fSEmilio López case 88200:
43945fb6b6fSEmilio López return 7;
44045fb6b6fSEmilio López
44145fb6b6fSEmilio López case 48000:
44245fb6b6fSEmilio López case 44100:
44345fb6b6fSEmilio López return 0;
44445fb6b6fSEmilio López
44545fb6b6fSEmilio López case 32000:
44645fb6b6fSEmilio López case 33075:
44745fb6b6fSEmilio López return 1;
44845fb6b6fSEmilio López
44945fb6b6fSEmilio López case 24000:
45045fb6b6fSEmilio López case 22050:
45145fb6b6fSEmilio López return 2;
45245fb6b6fSEmilio López
45345fb6b6fSEmilio López case 16000:
45445fb6b6fSEmilio López case 14700:
45545fb6b6fSEmilio López return 3;
45645fb6b6fSEmilio López
45745fb6b6fSEmilio López case 12000:
45845fb6b6fSEmilio López case 11025:
45945fb6b6fSEmilio López return 4;
46045fb6b6fSEmilio López
46145fb6b6fSEmilio López case 8000:
46245fb6b6fSEmilio López case 7350:
46345fb6b6fSEmilio López return 5;
46445fb6b6fSEmilio López
46545fb6b6fSEmilio López default:
46645fb6b6fSEmilio López return -EINVAL;
46745fb6b6fSEmilio López }
46845fb6b6fSEmilio López }
46945fb6b6fSEmilio López
sun4i_codec_hw_params_capture(struct sun4i_codec * scodec,struct snd_pcm_hw_params * params,unsigned int hwrate)4701fb34b48SMaxime Ripard static int sun4i_codec_hw_params_capture(struct sun4i_codec *scodec,
47145fb6b6fSEmilio López struct snd_pcm_hw_params *params,
4721fb34b48SMaxime Ripard unsigned int hwrate)
47345fb6b6fSEmilio López {
4741fb34b48SMaxime Ripard /* Set ADC sample rate */
475bc03f0d5SChen-Yu Tsai regmap_field_update_bits(scodec->reg_adc_fifoc,
4761fb34b48SMaxime Ripard 7 << SUN4I_CODEC_ADC_FIFOC_ADC_FS,
4771fb34b48SMaxime Ripard hwrate << SUN4I_CODEC_ADC_FIFOC_ADC_FS);
47845fb6b6fSEmilio López
4791fb34b48SMaxime Ripard /* Set the number of channels we want to use */
4801fb34b48SMaxime Ripard if (params_channels(params) == 1)
481b2366240SLi Chen regmap_field_set_bits(scodec->reg_adc_fifoc,
4821fb34b48SMaxime Ripard BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN));
4831fb34b48SMaxime Ripard else
484b2366240SLi Chen regmap_field_clear_bits(scodec->reg_adc_fifoc,
485b2366240SLi Chen BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN));
48645fb6b6fSEmilio López
4878a0f95ceSAndrea Bondavalli /* Set the number of sample bits to either 16 or 24 bits */
4888a0f95ceSAndrea Bondavalli if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min == 32) {
489b2366240SLi Chen regmap_field_set_bits(scodec->reg_adc_fifoc,
4908a0f95ceSAndrea Bondavalli BIT(SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS));
4918a0f95ceSAndrea Bondavalli
492b2366240SLi Chen regmap_field_clear_bits(scodec->reg_adc_fifoc,
493b2366240SLi Chen BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE));
4948a0f95ceSAndrea Bondavalli
4958a0f95ceSAndrea Bondavalli scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
4968a0f95ceSAndrea Bondavalli } else {
497b2366240SLi Chen regmap_field_clear_bits(scodec->reg_adc_fifoc,
498b2366240SLi Chen BIT(SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS));
4998a0f95ceSAndrea Bondavalli
5008a0f95ceSAndrea Bondavalli /* Fill most significant bits with valid data MSB */
501b2366240SLi Chen regmap_field_set_bits(scodec->reg_adc_fifoc,
5028a0f95ceSAndrea Bondavalli BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE));
5038a0f95ceSAndrea Bondavalli
5048a0f95ceSAndrea Bondavalli scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
5058a0f95ceSAndrea Bondavalli }
5068a0f95ceSAndrea Bondavalli
5071fb34b48SMaxime Ripard return 0;
5081fb34b48SMaxime Ripard }
50945fb6b6fSEmilio López
sun4i_codec_hw_params_playback(struct sun4i_codec * scodec,struct snd_pcm_hw_params * params,unsigned int hwrate)5101fb34b48SMaxime Ripard static int sun4i_codec_hw_params_playback(struct sun4i_codec *scodec,
5111fb34b48SMaxime Ripard struct snd_pcm_hw_params *params,
5121fb34b48SMaxime Ripard unsigned int hwrate)
5131fb34b48SMaxime Ripard {
5141fb34b48SMaxime Ripard u32 val;
51545fb6b6fSEmilio López
51645fb6b6fSEmilio López /* Set DAC sample rate */
51745fb6b6fSEmilio López regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
51845fb6b6fSEmilio López 7 << SUN4I_CODEC_DAC_FIFOC_DAC_FS,
51945fb6b6fSEmilio López hwrate << SUN4I_CODEC_DAC_FIFOC_DAC_FS);
52045fb6b6fSEmilio López
52145fb6b6fSEmilio López /* Set the number of channels we want to use */
52245fb6b6fSEmilio López if (params_channels(params) == 1)
52345fb6b6fSEmilio López val = BIT(SUN4I_CODEC_DAC_FIFOC_MONO_EN);
52445fb6b6fSEmilio López else
52545fb6b6fSEmilio López val = 0;
52645fb6b6fSEmilio López
52745fb6b6fSEmilio López regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
52845fb6b6fSEmilio López BIT(SUN4I_CODEC_DAC_FIFOC_MONO_EN),
52945fb6b6fSEmilio López val);
53045fb6b6fSEmilio López
53145fb6b6fSEmilio López /* Set the number of sample bits to either 16 or 24 bits */
53245fb6b6fSEmilio López if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min == 32) {
533b2366240SLi Chen regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
53445fb6b6fSEmilio López BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS));
53545fb6b6fSEmilio López
53645fb6b6fSEmilio López /* Set TX FIFO mode to padding the LSBs with 0 */
537b2366240SLi Chen regmap_clear_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
538b2366240SLi Chen BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE));
53945fb6b6fSEmilio López
54045fb6b6fSEmilio López scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
54145fb6b6fSEmilio López } else {
542b2366240SLi Chen regmap_clear_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
543b2366240SLi Chen BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS));
54445fb6b6fSEmilio López
54545fb6b6fSEmilio López /* Set TX FIFO mode to repeat the MSB */
546b2366240SLi Chen regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
54745fb6b6fSEmilio López BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE));
54845fb6b6fSEmilio López
54945fb6b6fSEmilio López scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
55045fb6b6fSEmilio López }
55145fb6b6fSEmilio López
55245fb6b6fSEmilio López return 0;
55345fb6b6fSEmilio López }
55445fb6b6fSEmilio López
sun4i_codec_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)5551fb34b48SMaxime Ripard static int sun4i_codec_hw_params(struct snd_pcm_substream *substream,
5561fb34b48SMaxime Ripard struct snd_pcm_hw_params *params,
5571fb34b48SMaxime Ripard struct snd_soc_dai *dai)
5581fb34b48SMaxime Ripard {
559c09e34b7SKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
5601fb34b48SMaxime Ripard struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
5611fb34b48SMaxime Ripard unsigned long clk_freq;
5628400ddf4SMaxime Ripard int ret, hwrate;
5631fb34b48SMaxime Ripard
5641fb34b48SMaxime Ripard clk_freq = sun4i_codec_get_mod_freq(params);
5651fb34b48SMaxime Ripard if (!clk_freq)
5661fb34b48SMaxime Ripard return -EINVAL;
5671fb34b48SMaxime Ripard
5688400ddf4SMaxime Ripard ret = clk_set_rate(scodec->clk_module, clk_freq);
5698400ddf4SMaxime Ripard if (ret)
5708400ddf4SMaxime Ripard return ret;
5711fb34b48SMaxime Ripard
5721fb34b48SMaxime Ripard hwrate = sun4i_codec_get_hw_rate(params);
5731fb34b48SMaxime Ripard if (hwrate < 0)
5741fb34b48SMaxime Ripard return hwrate;
5751fb34b48SMaxime Ripard
5761fb34b48SMaxime Ripard if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
5771fb34b48SMaxime Ripard return sun4i_codec_hw_params_playback(scodec, params,
5781fb34b48SMaxime Ripard hwrate);
5791fb34b48SMaxime Ripard
5801fb34b48SMaxime Ripard return sun4i_codec_hw_params_capture(scodec, params,
5811fb34b48SMaxime Ripard hwrate);
5821fb34b48SMaxime Ripard }
5831fb34b48SMaxime Ripard
5845a0cf024SAndrea Bondavalli
5855a0cf024SAndrea Bondavalli static unsigned int sun4i_codec_src_rates[] = {
5865a0cf024SAndrea Bondavalli 8000, 11025, 12000, 16000, 22050, 24000, 32000,
5875a0cf024SAndrea Bondavalli 44100, 48000, 96000, 192000
5885a0cf024SAndrea Bondavalli };
5895a0cf024SAndrea Bondavalli
5905a0cf024SAndrea Bondavalli
5915a0cf024SAndrea Bondavalli static struct snd_pcm_hw_constraint_list sun4i_codec_constraints = {
5925a0cf024SAndrea Bondavalli .count = ARRAY_SIZE(sun4i_codec_src_rates),
5935a0cf024SAndrea Bondavalli .list = sun4i_codec_src_rates,
5945a0cf024SAndrea Bondavalli };
5955a0cf024SAndrea Bondavalli
5965a0cf024SAndrea Bondavalli
sun4i_codec_startup(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)59745fb6b6fSEmilio López static int sun4i_codec_startup(struct snd_pcm_substream *substream,
59845fb6b6fSEmilio López struct snd_soc_dai *dai)
59945fb6b6fSEmilio López {
600c09e34b7SKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
60145fb6b6fSEmilio López struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
60245fb6b6fSEmilio López
6035a0cf024SAndrea Bondavalli snd_pcm_hw_constraint_list(substream->runtime, 0,
6045a0cf024SAndrea Bondavalli SNDRV_PCM_HW_PARAM_RATE, &sun4i_codec_constraints);
6055a0cf024SAndrea Bondavalli
60645fb6b6fSEmilio López /*
60745fb6b6fSEmilio López * Stop issuing DRQ when we have room for less than 16 samples
60845fb6b6fSEmilio López * in our TX FIFO
60945fb6b6fSEmilio López */
610b2366240SLi Chen regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
61145fb6b6fSEmilio López 3 << SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT);
61245fb6b6fSEmilio López
61345fb6b6fSEmilio López return clk_prepare_enable(scodec->clk_module);
61445fb6b6fSEmilio López }
61545fb6b6fSEmilio López
sun4i_codec_shutdown(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)61645fb6b6fSEmilio López static void sun4i_codec_shutdown(struct snd_pcm_substream *substream,
61745fb6b6fSEmilio López struct snd_soc_dai *dai)
61845fb6b6fSEmilio López {
619c09e34b7SKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
62045fb6b6fSEmilio López struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
62145fb6b6fSEmilio López
62245fb6b6fSEmilio López clk_disable_unprepare(scodec->clk_module);
62345fb6b6fSEmilio López }
62445fb6b6fSEmilio López
62545fb6b6fSEmilio López static const struct snd_soc_dai_ops sun4i_codec_dai_ops = {
62645fb6b6fSEmilio López .startup = sun4i_codec_startup,
62745fb6b6fSEmilio López .shutdown = sun4i_codec_shutdown,
62845fb6b6fSEmilio López .trigger = sun4i_codec_trigger,
62945fb6b6fSEmilio López .hw_params = sun4i_codec_hw_params,
63045fb6b6fSEmilio López .prepare = sun4i_codec_prepare,
63145fb6b6fSEmilio López };
63245fb6b6fSEmilio López
63345fb6b6fSEmilio López static struct snd_soc_dai_driver sun4i_codec_dai = {
63445fb6b6fSEmilio López .name = "Codec",
63545fb6b6fSEmilio López .ops = &sun4i_codec_dai_ops,
63645fb6b6fSEmilio López .playback = {
63745fb6b6fSEmilio López .stream_name = "Codec Playback",
63845fb6b6fSEmilio López .channels_min = 1,
63945fb6b6fSEmilio López .channels_max = 2,
64045fb6b6fSEmilio López .rate_min = 8000,
64145fb6b6fSEmilio López .rate_max = 192000,
6425a0cf024SAndrea Bondavalli .rates = SNDRV_PCM_RATE_CONTINUOUS,
64345fb6b6fSEmilio López .formats = SNDRV_PCM_FMTBIT_S16_LE |
64445fb6b6fSEmilio López SNDRV_PCM_FMTBIT_S32_LE,
64545fb6b6fSEmilio López .sig_bits = 24,
64645fb6b6fSEmilio López },
6471fb34b48SMaxime Ripard .capture = {
6481fb34b48SMaxime Ripard .stream_name = "Codec Capture",
6491fb34b48SMaxime Ripard .channels_min = 1,
6501fb34b48SMaxime Ripard .channels_max = 2,
6511fb34b48SMaxime Ripard .rate_min = 8000,
6525a0cf024SAndrea Bondavalli .rate_max = 48000,
6535a0cf024SAndrea Bondavalli .rates = SNDRV_PCM_RATE_CONTINUOUS,
6541fb34b48SMaxime Ripard .formats = SNDRV_PCM_FMTBIT_S16_LE |
6551fb34b48SMaxime Ripard SNDRV_PCM_FMTBIT_S32_LE,
6561fb34b48SMaxime Ripard .sig_bits = 24,
6571fb34b48SMaxime Ripard },
65845fb6b6fSEmilio López };
65945fb6b6fSEmilio López
6608d9e4c9eSChen-Yu Tsai /*** sun4i Codec ***/
66145fb6b6fSEmilio López static const struct snd_kcontrol_new sun4i_codec_pa_mute =
66245fb6b6fSEmilio López SOC_DAPM_SINGLE("Switch", SUN4I_CODEC_DAC_ACTL,
66345fb6b6fSEmilio López SUN4I_CODEC_DAC_ACTL_PA_MUTE, 1, 0);
66445fb6b6fSEmilio López
66545fb6b6fSEmilio López static DECLARE_TLV_DB_SCALE(sun4i_codec_pa_volume_scale, -6300, 100, 1);
66667690c28SDanny Milosavljevic static DECLARE_TLV_DB_SCALE(sun4i_codec_linein_loopback_gain_scale, -150, 150,
66767690c28SDanny Milosavljevic 0);
66867690c28SDanny Milosavljevic static DECLARE_TLV_DB_SCALE(sun4i_codec_linein_preamp_gain_scale, -1200, 300,
66967690c28SDanny Milosavljevic 0);
67050d16419SDanny Milosavljevic static DECLARE_TLV_DB_SCALE(sun4i_codec_fmin_loopback_gain_scale, -450, 150,
67150d16419SDanny Milosavljevic 0);
67263bd8489SDanny Milosavljevic static DECLARE_TLV_DB_SCALE(sun4i_codec_micin_loopback_gain_scale, -450, 150,
67363bd8489SDanny Milosavljevic 0);
674b329c78eSDanny Milosavljevic static DECLARE_TLV_DB_RANGE(sun4i_codec_micin_preamp_gain_scale,
675b329c78eSDanny Milosavljevic 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
676b329c78eSDanny Milosavljevic 1, 7, TLV_DB_SCALE_ITEM(3500, 300, 0));
677b329c78eSDanny Milosavljevic static DECLARE_TLV_DB_RANGE(sun7i_codec_micin_preamp_gain_scale,
678b329c78eSDanny Milosavljevic 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
679b329c78eSDanny Milosavljevic 1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0));
68045fb6b6fSEmilio López
681c9e902f4SDanny Milosavljevic static const struct snd_kcontrol_new sun4i_codec_controls[] = {
682474d147aSAdam Sampson SOC_SINGLE_TLV("Power Amplifier Volume", SUN4I_CODEC_DAC_ACTL,
68345fb6b6fSEmilio López SUN4I_CODEC_DAC_ACTL_PA_VOL, 0x3F, 0,
68445fb6b6fSEmilio López sun4i_codec_pa_volume_scale),
68567690c28SDanny Milosavljevic SOC_SINGLE_TLV("Line Playback Volume", SUN4I_CODEC_DAC_ACTL,
68667690c28SDanny Milosavljevic SUN4I_CODEC_DAC_ACTL_LNG, 1, 0,
68767690c28SDanny Milosavljevic sun4i_codec_linein_loopback_gain_scale),
68867690c28SDanny Milosavljevic SOC_SINGLE_TLV("Line Boost Volume", SUN4I_CODEC_ADC_ACTL,
68967690c28SDanny Milosavljevic SUN4I_CODEC_ADC_ACTL_LNPREG, 7, 0,
69067690c28SDanny Milosavljevic sun4i_codec_linein_preamp_gain_scale),
69150d16419SDanny Milosavljevic SOC_SINGLE_TLV("FM Playback Volume", SUN4I_CODEC_DAC_ACTL,
69250d16419SDanny Milosavljevic SUN4I_CODEC_DAC_ACTL_FMG, 3, 0,
69350d16419SDanny Milosavljevic sun4i_codec_fmin_loopback_gain_scale),
694b5a65603SDanny Milosavljevic SOC_SINGLE_TLV("Mic Playback Volume", SUN4I_CODEC_DAC_ACTL,
695b5a65603SDanny Milosavljevic SUN4I_CODEC_DAC_ACTL_MICG, 7, 0,
696b5a65603SDanny Milosavljevic sun4i_codec_micin_loopback_gain_scale),
697b329c78eSDanny Milosavljevic SOC_SINGLE_TLV("Mic1 Boost Volume", SUN4I_CODEC_ADC_ACTL,
698b329c78eSDanny Milosavljevic SUN4I_CODEC_ADC_ACTL_PREG1, 3, 0,
699b329c78eSDanny Milosavljevic sun4i_codec_micin_preamp_gain_scale),
700b329c78eSDanny Milosavljevic SOC_SINGLE_TLV("Mic2 Boost Volume", SUN4I_CODEC_ADC_ACTL,
701b329c78eSDanny Milosavljevic SUN4I_CODEC_ADC_ACTL_PREG2, 3, 0,
702b329c78eSDanny Milosavljevic sun4i_codec_micin_preamp_gain_scale),
70345fb6b6fSEmilio López };
70445fb6b6fSEmilio López
705b71a7eb5SDanny Milosavljevic static const struct snd_kcontrol_new sun7i_codec_controls[] = {
706b71a7eb5SDanny Milosavljevic SOC_SINGLE_TLV("Power Amplifier Volume", SUN4I_CODEC_DAC_ACTL,
707b71a7eb5SDanny Milosavljevic SUN4I_CODEC_DAC_ACTL_PA_VOL, 0x3F, 0,
708b71a7eb5SDanny Milosavljevic sun4i_codec_pa_volume_scale),
70967690c28SDanny Milosavljevic SOC_SINGLE_TLV("Line Playback Volume", SUN4I_CODEC_DAC_ACTL,
71067690c28SDanny Milosavljevic SUN4I_CODEC_DAC_ACTL_LNG, 1, 0,
71167690c28SDanny Milosavljevic sun4i_codec_linein_loopback_gain_scale),
71267690c28SDanny Milosavljevic SOC_SINGLE_TLV("Line Boost Volume", SUN4I_CODEC_ADC_ACTL,
71367690c28SDanny Milosavljevic SUN4I_CODEC_ADC_ACTL_LNPREG, 7, 0,
71467690c28SDanny Milosavljevic sun4i_codec_linein_preamp_gain_scale),
71550d16419SDanny Milosavljevic SOC_SINGLE_TLV("FM Playback Volume", SUN4I_CODEC_DAC_ACTL,
71650d16419SDanny Milosavljevic SUN4I_CODEC_DAC_ACTL_FMG, 3, 0,
71750d16419SDanny Milosavljevic sun4i_codec_fmin_loopback_gain_scale),
718b71a7eb5SDanny Milosavljevic SOC_SINGLE_TLV("Mic Playback Volume", SUN4I_CODEC_DAC_ACTL,
719b71a7eb5SDanny Milosavljevic SUN4I_CODEC_DAC_ACTL_MICG, 7, 0,
720b71a7eb5SDanny Milosavljevic sun4i_codec_micin_loopback_gain_scale),
721b329c78eSDanny Milosavljevic SOC_SINGLE_TLV("Mic1 Boost Volume", SUN7I_CODEC_AC_MIC_PHONE_CAL,
722b329c78eSDanny Milosavljevic SUN7I_CODEC_AC_MIC_PHONE_CAL_PREG1, 7, 0,
723b329c78eSDanny Milosavljevic sun7i_codec_micin_preamp_gain_scale),
724b329c78eSDanny Milosavljevic SOC_SINGLE_TLV("Mic2 Boost Volume", SUN7I_CODEC_AC_MIC_PHONE_CAL,
725b329c78eSDanny Milosavljevic SUN7I_CODEC_AC_MIC_PHONE_CAL_PREG2, 7, 0,
726b329c78eSDanny Milosavljevic sun7i_codec_micin_preamp_gain_scale),
727b71a7eb5SDanny Milosavljevic };
728b71a7eb5SDanny Milosavljevic
7290bbb8e83SDanny Milosavljevic static const struct snd_kcontrol_new sun4i_codec_mixer_controls[] = {
7300bbb8e83SDanny Milosavljevic SOC_DAPM_SINGLE("Left Mixer Left DAC Playback Switch",
7310bbb8e83SDanny Milosavljevic SUN4I_CODEC_DAC_ACTL, SUN4I_CODEC_DAC_ACTL_LDACLMIXS,
7320bbb8e83SDanny Milosavljevic 1, 0),
7330bbb8e83SDanny Milosavljevic SOC_DAPM_SINGLE("Right Mixer Right DAC Playback Switch",
7340bbb8e83SDanny Milosavljevic SUN4I_CODEC_DAC_ACTL, SUN4I_CODEC_DAC_ACTL_RDACRMIXS,
7350bbb8e83SDanny Milosavljevic 1, 0),
7360bbb8e83SDanny Milosavljevic SOC_DAPM_SINGLE("Right Mixer Left DAC Playback Switch",
7370bbb8e83SDanny Milosavljevic SUN4I_CODEC_DAC_ACTL,
73845fb6b6fSEmilio López SUN4I_CODEC_DAC_ACTL_LDACRMIXS, 1, 0),
73967690c28SDanny Milosavljevic SOC_DAPM_DOUBLE("Line Playback Switch", SUN4I_CODEC_DAC_ACTL,
74067690c28SDanny Milosavljevic SUN4I_CODEC_DAC_ACTL_LLNS,
74167690c28SDanny Milosavljevic SUN4I_CODEC_DAC_ACTL_RLNS, 1, 0),
74250d16419SDanny Milosavljevic SOC_DAPM_DOUBLE("FM Playback Switch", SUN4I_CODEC_DAC_ACTL,
74350d16419SDanny Milosavljevic SUN4I_CODEC_DAC_ACTL_LFMS,
74450d16419SDanny Milosavljevic SUN4I_CODEC_DAC_ACTL_RFMS, 1, 0),
74544a1f4e8SDanny Milosavljevic SOC_DAPM_DOUBLE("Mic1 Playback Switch", SUN4I_CODEC_DAC_ACTL,
74644a1f4e8SDanny Milosavljevic SUN4I_CODEC_DAC_ACTL_MIC1LS,
74744a1f4e8SDanny Milosavljevic SUN4I_CODEC_DAC_ACTL_MIC1RS, 1, 0),
74844a1f4e8SDanny Milosavljevic SOC_DAPM_DOUBLE("Mic2 Playback Switch", SUN4I_CODEC_DAC_ACTL,
74944a1f4e8SDanny Milosavljevic SUN4I_CODEC_DAC_ACTL_MIC2LS,
75044a1f4e8SDanny Milosavljevic SUN4I_CODEC_DAC_ACTL_MIC2RS, 1, 0),
75145fb6b6fSEmilio López };
75245fb6b6fSEmilio López
75345fb6b6fSEmilio López static const struct snd_kcontrol_new sun4i_codec_pa_mixer_controls[] = {
75445fb6b6fSEmilio López SOC_DAPM_SINGLE("DAC Playback Switch", SUN4I_CODEC_DAC_ACTL,
75545fb6b6fSEmilio López SUN4I_CODEC_DAC_ACTL_DACPAS, 1, 0),
75645fb6b6fSEmilio López SOC_DAPM_SINGLE("Mixer Playback Switch", SUN4I_CODEC_DAC_ACTL,
75745fb6b6fSEmilio López SUN4I_CODEC_DAC_ACTL_MIXPAS, 1, 0),
75845fb6b6fSEmilio López };
75945fb6b6fSEmilio López
760e6415b48SHans de Goede static const struct snd_soc_dapm_widget sun4i_codec_codec_dapm_widgets[] = {
7611fb34b48SMaxime Ripard /* Digital parts of the ADCs */
7621fb34b48SMaxime Ripard SND_SOC_DAPM_SUPPLY("ADC", SUN4I_CODEC_ADC_FIFOC,
7631fb34b48SMaxime Ripard SUN4I_CODEC_ADC_FIFOC_EN_AD, 0,
7641fb34b48SMaxime Ripard NULL, 0),
7651fb34b48SMaxime Ripard
76645fb6b6fSEmilio López /* Digital parts of the DACs */
76745fb6b6fSEmilio López SND_SOC_DAPM_SUPPLY("DAC", SUN4I_CODEC_DAC_DPC,
76845fb6b6fSEmilio López SUN4I_CODEC_DAC_DPC_EN_DA, 0,
76945fb6b6fSEmilio López NULL, 0),
77045fb6b6fSEmilio López
7711fb34b48SMaxime Ripard /* Analog parts of the ADCs */
7721fb34b48SMaxime Ripard SND_SOC_DAPM_ADC("Left ADC", "Codec Capture", SUN4I_CODEC_ADC_ACTL,
7731fb34b48SMaxime Ripard SUN4I_CODEC_ADC_ACTL_ADC_L_EN, 0),
7741fb34b48SMaxime Ripard SND_SOC_DAPM_ADC("Right ADC", "Codec Capture", SUN4I_CODEC_ADC_ACTL,
7751fb34b48SMaxime Ripard SUN4I_CODEC_ADC_ACTL_ADC_R_EN, 0),
7761fb34b48SMaxime Ripard
77745fb6b6fSEmilio López /* Analog parts of the DACs */
77845fb6b6fSEmilio López SND_SOC_DAPM_DAC("Left DAC", "Codec Playback", SUN4I_CODEC_DAC_ACTL,
77945fb6b6fSEmilio López SUN4I_CODEC_DAC_ACTL_DACAENL, 0),
78045fb6b6fSEmilio López SND_SOC_DAPM_DAC("Right DAC", "Codec Playback", SUN4I_CODEC_DAC_ACTL,
78145fb6b6fSEmilio López SUN4I_CODEC_DAC_ACTL_DACAENR, 0),
78245fb6b6fSEmilio López
78345fb6b6fSEmilio López /* Mixers */
78445fb6b6fSEmilio López SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
7850bbb8e83SDanny Milosavljevic sun4i_codec_mixer_controls,
7860bbb8e83SDanny Milosavljevic ARRAY_SIZE(sun4i_codec_mixer_controls)),
78745fb6b6fSEmilio López SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
7880bbb8e83SDanny Milosavljevic sun4i_codec_mixer_controls,
7890bbb8e83SDanny Milosavljevic ARRAY_SIZE(sun4i_codec_mixer_controls)),
79045fb6b6fSEmilio López
79145fb6b6fSEmilio López /* Global Mixer Enable */
79245fb6b6fSEmilio López SND_SOC_DAPM_SUPPLY("Mixer Enable", SUN4I_CODEC_DAC_ACTL,
79345fb6b6fSEmilio López SUN4I_CODEC_DAC_ACTL_MIXEN, 0, NULL, 0),
79445fb6b6fSEmilio López
7951fb34b48SMaxime Ripard /* VMIC */
7961fb34b48SMaxime Ripard SND_SOC_DAPM_SUPPLY("VMIC", SUN4I_CODEC_ADC_ACTL,
7971fb34b48SMaxime Ripard SUN4I_CODEC_ADC_ACTL_VMICEN, 0, NULL, 0),
7981fb34b48SMaxime Ripard
7991fb34b48SMaxime Ripard /* Mic Pre-Amplifiers */
8001fb34b48SMaxime Ripard SND_SOC_DAPM_PGA("MIC1 Pre-Amplifier", SUN4I_CODEC_ADC_ACTL,
8011fb34b48SMaxime Ripard SUN4I_CODEC_ADC_ACTL_PREG1EN, 0, NULL, 0),
80263bd8489SDanny Milosavljevic SND_SOC_DAPM_PGA("MIC2 Pre-Amplifier", SUN4I_CODEC_ADC_ACTL,
80363bd8489SDanny Milosavljevic SUN4I_CODEC_ADC_ACTL_PREG2EN, 0, NULL, 0),
8041fb34b48SMaxime Ripard
805474d147aSAdam Sampson /* Power Amplifier */
806474d147aSAdam Sampson SND_SOC_DAPM_MIXER("Power Amplifier", SUN4I_CODEC_ADC_ACTL,
80745fb6b6fSEmilio López SUN4I_CODEC_ADC_ACTL_PA_EN, 0,
80845fb6b6fSEmilio López sun4i_codec_pa_mixer_controls,
80945fb6b6fSEmilio López ARRAY_SIZE(sun4i_codec_pa_mixer_controls)),
810474d147aSAdam Sampson SND_SOC_DAPM_SWITCH("Power Amplifier Mute", SND_SOC_NOPM, 0, 0,
81145fb6b6fSEmilio López &sun4i_codec_pa_mute),
81245fb6b6fSEmilio López
81367690c28SDanny Milosavljevic SND_SOC_DAPM_INPUT("Line Right"),
81467690c28SDanny Milosavljevic SND_SOC_DAPM_INPUT("Line Left"),
81550d16419SDanny Milosavljevic SND_SOC_DAPM_INPUT("FM Right"),
81650d16419SDanny Milosavljevic SND_SOC_DAPM_INPUT("FM Left"),
8171fb34b48SMaxime Ripard SND_SOC_DAPM_INPUT("Mic1"),
81863bd8489SDanny Milosavljevic SND_SOC_DAPM_INPUT("Mic2"),
8191fb34b48SMaxime Ripard
82045fb6b6fSEmilio López SND_SOC_DAPM_OUTPUT("HP Right"),
82145fb6b6fSEmilio López SND_SOC_DAPM_OUTPUT("HP Left"),
82245fb6b6fSEmilio López };
82345fb6b6fSEmilio López
824e6415b48SHans de Goede static const struct snd_soc_dapm_route sun4i_codec_codec_dapm_routes[] = {
8251fb34b48SMaxime Ripard /* Left ADC / DAC Routes */
8261fb34b48SMaxime Ripard { "Left ADC", NULL, "ADC" },
82745fb6b6fSEmilio López { "Left DAC", NULL, "DAC" },
82845fb6b6fSEmilio López
8291fb34b48SMaxime Ripard /* Right ADC / DAC Routes */
8301fb34b48SMaxime Ripard { "Right ADC", NULL, "ADC" },
83145fb6b6fSEmilio López { "Right DAC", NULL, "DAC" },
83245fb6b6fSEmilio López
83345fb6b6fSEmilio López /* Right Mixer Routes */
83445fb6b6fSEmilio López { "Right Mixer", NULL, "Mixer Enable" },
8350bbb8e83SDanny Milosavljevic { "Right Mixer", "Right Mixer Left DAC Playback Switch", "Left DAC" },
8360bbb8e83SDanny Milosavljevic { "Right Mixer", "Right Mixer Right DAC Playback Switch", "Right DAC" },
83767690c28SDanny Milosavljevic { "Right Mixer", "Line Playback Switch", "Line Right" },
83850d16419SDanny Milosavljevic { "Right Mixer", "FM Playback Switch", "FM Right" },
83944a1f4e8SDanny Milosavljevic { "Right Mixer", "Mic1 Playback Switch", "MIC1 Pre-Amplifier" },
84044a1f4e8SDanny Milosavljevic { "Right Mixer", "Mic2 Playback Switch", "MIC2 Pre-Amplifier" },
84145fb6b6fSEmilio López
84245fb6b6fSEmilio López /* Left Mixer Routes */
84345fb6b6fSEmilio López { "Left Mixer", NULL, "Mixer Enable" },
8440bbb8e83SDanny Milosavljevic { "Left Mixer", "Left Mixer Left DAC Playback Switch", "Left DAC" },
84567690c28SDanny Milosavljevic { "Left Mixer", "Line Playback Switch", "Line Left" },
84650d16419SDanny Milosavljevic { "Left Mixer", "FM Playback Switch", "FM Left" },
84744a1f4e8SDanny Milosavljevic { "Left Mixer", "Mic1 Playback Switch", "MIC1 Pre-Amplifier" },
84844a1f4e8SDanny Milosavljevic { "Left Mixer", "Mic2 Playback Switch", "MIC2 Pre-Amplifier" },
84945fb6b6fSEmilio López
850474d147aSAdam Sampson /* Power Amplifier Routes */
851474d147aSAdam Sampson { "Power Amplifier", "Mixer Playback Switch", "Left Mixer" },
852474d147aSAdam Sampson { "Power Amplifier", "Mixer Playback Switch", "Right Mixer" },
853474d147aSAdam Sampson { "Power Amplifier", "DAC Playback Switch", "Left DAC" },
854474d147aSAdam Sampson { "Power Amplifier", "DAC Playback Switch", "Right DAC" },
85545fb6b6fSEmilio López
856474d147aSAdam Sampson /* Headphone Output Routes */
857474d147aSAdam Sampson { "Power Amplifier Mute", "Switch", "Power Amplifier" },
858474d147aSAdam Sampson { "HP Right", NULL, "Power Amplifier Mute" },
859474d147aSAdam Sampson { "HP Left", NULL, "Power Amplifier Mute" },
8601fb34b48SMaxime Ripard
8611fb34b48SMaxime Ripard /* Mic1 Routes */
8621fb34b48SMaxime Ripard { "Left ADC", NULL, "MIC1 Pre-Amplifier" },
8631fb34b48SMaxime Ripard { "Right ADC", NULL, "MIC1 Pre-Amplifier" },
8641fb34b48SMaxime Ripard { "MIC1 Pre-Amplifier", NULL, "Mic1"},
8651fb34b48SMaxime Ripard { "Mic1", NULL, "VMIC" },
86663bd8489SDanny Milosavljevic
86763bd8489SDanny Milosavljevic /* Mic2 Routes */
86863bd8489SDanny Milosavljevic { "Left ADC", NULL, "MIC2 Pre-Amplifier" },
86963bd8489SDanny Milosavljevic { "Right ADC", NULL, "MIC2 Pre-Amplifier" },
87063bd8489SDanny Milosavljevic { "MIC2 Pre-Amplifier", NULL, "Mic2"},
87163bd8489SDanny Milosavljevic { "Mic2", NULL, "VMIC" },
87245fb6b6fSEmilio López };
87345fb6b6fSEmilio López
87477f4be16SKuninori Morimoto static const struct snd_soc_component_driver sun4i_codec_codec = {
875c9e902f4SDanny Milosavljevic .controls = sun4i_codec_controls,
876c9e902f4SDanny Milosavljevic .num_controls = ARRAY_SIZE(sun4i_codec_controls),
877e6415b48SHans de Goede .dapm_widgets = sun4i_codec_codec_dapm_widgets,
878e6415b48SHans de Goede .num_dapm_widgets = ARRAY_SIZE(sun4i_codec_codec_dapm_widgets),
879e6415b48SHans de Goede .dapm_routes = sun4i_codec_codec_dapm_routes,
880e6415b48SHans de Goede .num_dapm_routes = ARRAY_SIZE(sun4i_codec_codec_dapm_routes),
88177f4be16SKuninori Morimoto .idle_bias_on = 1,
88277f4be16SKuninori Morimoto .use_pmdown_time = 1,
88377f4be16SKuninori Morimoto .endianness = 1,
88445fb6b6fSEmilio López };
88545fb6b6fSEmilio López
886b71a7eb5SDanny Milosavljevic static const struct snd_soc_component_driver sun7i_codec_codec = {
887b71a7eb5SDanny Milosavljevic .controls = sun7i_codec_controls,
888b71a7eb5SDanny Milosavljevic .num_controls = ARRAY_SIZE(sun7i_codec_controls),
889b71a7eb5SDanny Milosavljevic .dapm_widgets = sun4i_codec_codec_dapm_widgets,
890b71a7eb5SDanny Milosavljevic .num_dapm_widgets = ARRAY_SIZE(sun4i_codec_codec_dapm_widgets),
891b71a7eb5SDanny Milosavljevic .dapm_routes = sun4i_codec_codec_dapm_routes,
892b71a7eb5SDanny Milosavljevic .num_dapm_routes = ARRAY_SIZE(sun4i_codec_codec_dapm_routes),
893b71a7eb5SDanny Milosavljevic .idle_bias_on = 1,
894b71a7eb5SDanny Milosavljevic .use_pmdown_time = 1,
895b71a7eb5SDanny Milosavljevic .endianness = 1,
896b71a7eb5SDanny Milosavljevic };
897b71a7eb5SDanny Milosavljevic
8988d9e4c9eSChen-Yu Tsai /*** sun6i Codec ***/
8998d9e4c9eSChen-Yu Tsai
9008d9e4c9eSChen-Yu Tsai /* mixer controls */
9018d9e4c9eSChen-Yu Tsai static const struct snd_kcontrol_new sun6i_codec_mixer_controls[] = {
9028d9e4c9eSChen-Yu Tsai SOC_DAPM_DOUBLE("DAC Playback Switch",
9038d9e4c9eSChen-Yu Tsai SUN6I_CODEC_OM_DACA_CTRL,
9048d9e4c9eSChen-Yu Tsai SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACL,
9058d9e4c9eSChen-Yu Tsai SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACR, 1, 0),
9068d9e4c9eSChen-Yu Tsai SOC_DAPM_DOUBLE("DAC Reversed Playback Switch",
9078d9e4c9eSChen-Yu Tsai SUN6I_CODEC_OM_DACA_CTRL,
9088d9e4c9eSChen-Yu Tsai SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACR,
9098d9e4c9eSChen-Yu Tsai SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACL, 1, 0),
910dff50512SChen-Yu Tsai SOC_DAPM_DOUBLE("Line In Playback Switch",
911dff50512SChen-Yu Tsai SUN6I_CODEC_OM_DACA_CTRL,
912dff50512SChen-Yu Tsai SUN6I_CODEC_OM_DACA_CTRL_LMIX_LINEINL,
913dff50512SChen-Yu Tsai SUN6I_CODEC_OM_DACA_CTRL_RMIX_LINEINR, 1, 0),
914ecd5cdb4SChen-Yu Tsai SOC_DAPM_DOUBLE("Mic1 Playback Switch",
915ecd5cdb4SChen-Yu Tsai SUN6I_CODEC_OM_DACA_CTRL,
916ecd5cdb4SChen-Yu Tsai SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC1,
917ecd5cdb4SChen-Yu Tsai SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC1, 1, 0),
918ecd5cdb4SChen-Yu Tsai SOC_DAPM_DOUBLE("Mic2 Playback Switch",
919ecd5cdb4SChen-Yu Tsai SUN6I_CODEC_OM_DACA_CTRL,
920ecd5cdb4SChen-Yu Tsai SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC2,
921ecd5cdb4SChen-Yu Tsai SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC2, 1, 0),
9228d9e4c9eSChen-Yu Tsai };
9238d9e4c9eSChen-Yu Tsai
92424c99f84SChen-Yu Tsai /* ADC mixer controls */
92524c99f84SChen-Yu Tsai static const struct snd_kcontrol_new sun6i_codec_adc_mixer_controls[] = {
92624c99f84SChen-Yu Tsai SOC_DAPM_DOUBLE("Mixer Capture Switch",
92724c99f84SChen-Yu Tsai SUN6I_CODEC_ADC_ACTL,
92824c99f84SChen-Yu Tsai SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXL,
92924c99f84SChen-Yu Tsai SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXR, 1, 0),
93024c99f84SChen-Yu Tsai SOC_DAPM_DOUBLE("Mixer Reversed Capture Switch",
93124c99f84SChen-Yu Tsai SUN6I_CODEC_ADC_ACTL,
93224c99f84SChen-Yu Tsai SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXR,
93324c99f84SChen-Yu Tsai SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXL, 1, 0),
93424c99f84SChen-Yu Tsai SOC_DAPM_DOUBLE("Line In Capture Switch",
93524c99f84SChen-Yu Tsai SUN6I_CODEC_ADC_ACTL,
93624c99f84SChen-Yu Tsai SUN6I_CODEC_ADC_ACTL_LADCMIX_LINEINL,
93724c99f84SChen-Yu Tsai SUN6I_CODEC_ADC_ACTL_RADCMIX_LINEINR, 1, 0),
93824c99f84SChen-Yu Tsai SOC_DAPM_DOUBLE("Mic1 Capture Switch",
93924c99f84SChen-Yu Tsai SUN6I_CODEC_ADC_ACTL,
94024c99f84SChen-Yu Tsai SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC1,
94124c99f84SChen-Yu Tsai SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC1, 1, 0),
94224c99f84SChen-Yu Tsai SOC_DAPM_DOUBLE("Mic2 Capture Switch",
94324c99f84SChen-Yu Tsai SUN6I_CODEC_ADC_ACTL,
94424c99f84SChen-Yu Tsai SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC2,
94524c99f84SChen-Yu Tsai SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC2, 1, 0),
94624c99f84SChen-Yu Tsai };
94724c99f84SChen-Yu Tsai
9488d9e4c9eSChen-Yu Tsai /* headphone controls */
9498d9e4c9eSChen-Yu Tsai static const char * const sun6i_codec_hp_src_enum_text[] = {
9508d9e4c9eSChen-Yu Tsai "DAC", "Mixer",
9518d9e4c9eSChen-Yu Tsai };
9528d9e4c9eSChen-Yu Tsai
9538d9e4c9eSChen-Yu Tsai static SOC_ENUM_DOUBLE_DECL(sun6i_codec_hp_src_enum,
9548d9e4c9eSChen-Yu Tsai SUN6I_CODEC_OM_DACA_CTRL,
9558d9e4c9eSChen-Yu Tsai SUN6I_CODEC_OM_DACA_CTRL_LHPIS,
9568d9e4c9eSChen-Yu Tsai SUN6I_CODEC_OM_DACA_CTRL_RHPIS,
9578d9e4c9eSChen-Yu Tsai sun6i_codec_hp_src_enum_text);
9588d9e4c9eSChen-Yu Tsai
9598d9e4c9eSChen-Yu Tsai static const struct snd_kcontrol_new sun6i_codec_hp_src[] = {
9608d9e4c9eSChen-Yu Tsai SOC_DAPM_ENUM("Headphone Source Playback Route",
9618d9e4c9eSChen-Yu Tsai sun6i_codec_hp_src_enum),
9628d9e4c9eSChen-Yu Tsai };
9638d9e4c9eSChen-Yu Tsai
964ecd5cdb4SChen-Yu Tsai /* microphone controls */
965ecd5cdb4SChen-Yu Tsai static const char * const sun6i_codec_mic2_src_enum_text[] = {
966ecd5cdb4SChen-Yu Tsai "Mic2", "Mic3",
967ecd5cdb4SChen-Yu Tsai };
968ecd5cdb4SChen-Yu Tsai
969ecd5cdb4SChen-Yu Tsai static SOC_ENUM_SINGLE_DECL(sun6i_codec_mic2_src_enum,
970ecd5cdb4SChen-Yu Tsai SUN6I_CODEC_MIC_CTRL,
971ecd5cdb4SChen-Yu Tsai SUN6I_CODEC_MIC_CTRL_MIC2SLT,
972ecd5cdb4SChen-Yu Tsai sun6i_codec_mic2_src_enum_text);
973ecd5cdb4SChen-Yu Tsai
974ecd5cdb4SChen-Yu Tsai static const struct snd_kcontrol_new sun6i_codec_mic2_src[] = {
975ecd5cdb4SChen-Yu Tsai SOC_DAPM_ENUM("Mic2 Amplifier Source Route",
976ecd5cdb4SChen-Yu Tsai sun6i_codec_mic2_src_enum),
977ecd5cdb4SChen-Yu Tsai };
978ecd5cdb4SChen-Yu Tsai
9790f909f98SChen-Yu Tsai /* line out controls */
9800f909f98SChen-Yu Tsai static const char * const sun6i_codec_lineout_src_enum_text[] = {
9810f909f98SChen-Yu Tsai "Stereo", "Mono Differential",
9820f909f98SChen-Yu Tsai };
9830f909f98SChen-Yu Tsai
9840f909f98SChen-Yu Tsai static SOC_ENUM_DOUBLE_DECL(sun6i_codec_lineout_src_enum,
9850f909f98SChen-Yu Tsai SUN6I_CODEC_MIC_CTRL,
9860f909f98SChen-Yu Tsai SUN6I_CODEC_MIC_CTRL_LINEOUTLSRC,
9870f909f98SChen-Yu Tsai SUN6I_CODEC_MIC_CTRL_LINEOUTRSRC,
9880f909f98SChen-Yu Tsai sun6i_codec_lineout_src_enum_text);
9890f909f98SChen-Yu Tsai
9900f909f98SChen-Yu Tsai static const struct snd_kcontrol_new sun6i_codec_lineout_src[] = {
9910f909f98SChen-Yu Tsai SOC_DAPM_ENUM("Line Out Source Playback Route",
9920f909f98SChen-Yu Tsai sun6i_codec_lineout_src_enum),
9930f909f98SChen-Yu Tsai };
9940f909f98SChen-Yu Tsai
9958d9e4c9eSChen-Yu Tsai /* volume / mute controls */
9968d9e4c9eSChen-Yu Tsai static const DECLARE_TLV_DB_SCALE(sun6i_codec_dvol_scale, -7308, 116, 0);
9978d9e4c9eSChen-Yu Tsai static const DECLARE_TLV_DB_SCALE(sun6i_codec_hp_vol_scale, -6300, 100, 1);
998dff50512SChen-Yu Tsai static const DECLARE_TLV_DB_SCALE(sun6i_codec_out_mixer_pregain_scale,
999dff50512SChen-Yu Tsai -450, 150, 0);
10000f909f98SChen-Yu Tsai static const DECLARE_TLV_DB_RANGE(sun6i_codec_lineout_vol_scale,
10010f909f98SChen-Yu Tsai 0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
10020f909f98SChen-Yu Tsai 2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0),
10030f909f98SChen-Yu Tsai );
1004ecd5cdb4SChen-Yu Tsai static const DECLARE_TLV_DB_RANGE(sun6i_codec_mic_gain_scale,
1005ecd5cdb4SChen-Yu Tsai 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
1006ecd5cdb4SChen-Yu Tsai 1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0),
1007ecd5cdb4SChen-Yu Tsai );
10088d9e4c9eSChen-Yu Tsai
10098d9e4c9eSChen-Yu Tsai static const struct snd_kcontrol_new sun6i_codec_codec_widgets[] = {
10108d9e4c9eSChen-Yu Tsai SOC_SINGLE_TLV("DAC Playback Volume", SUN4I_CODEC_DAC_DPC,
10118d9e4c9eSChen-Yu Tsai SUN4I_CODEC_DAC_DPC_DVOL, 0x3f, 1,
10128d9e4c9eSChen-Yu Tsai sun6i_codec_dvol_scale),
10138d9e4c9eSChen-Yu Tsai SOC_SINGLE_TLV("Headphone Playback Volume",
10148d9e4c9eSChen-Yu Tsai SUN6I_CODEC_OM_DACA_CTRL,
10158d9e4c9eSChen-Yu Tsai SUN6I_CODEC_OM_DACA_CTRL_HPVOL, 0x3f, 0,
10168d9e4c9eSChen-Yu Tsai sun6i_codec_hp_vol_scale),
10170f909f98SChen-Yu Tsai SOC_SINGLE_TLV("Line Out Playback Volume",
10180f909f98SChen-Yu Tsai SUN6I_CODEC_MIC_CTRL,
10190f909f98SChen-Yu Tsai SUN6I_CODEC_MIC_CTRL_LINEOUTVC, 0x1f, 0,
10200f909f98SChen-Yu Tsai sun6i_codec_lineout_vol_scale),
10218d9e4c9eSChen-Yu Tsai SOC_DOUBLE("Headphone Playback Switch",
10228d9e4c9eSChen-Yu Tsai SUN6I_CODEC_OM_DACA_CTRL,
10238d9e4c9eSChen-Yu Tsai SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE,
10248d9e4c9eSChen-Yu Tsai SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE, 1, 0),
10250f909f98SChen-Yu Tsai SOC_DOUBLE("Line Out Playback Switch",
10260f909f98SChen-Yu Tsai SUN6I_CODEC_MIC_CTRL,
10270f909f98SChen-Yu Tsai SUN6I_CODEC_MIC_CTRL_LINEOUTLEN,
10280f909f98SChen-Yu Tsai SUN6I_CODEC_MIC_CTRL_LINEOUTREN, 1, 0),
1029dff50512SChen-Yu Tsai /* Mixer pre-gains */
1030dff50512SChen-Yu Tsai SOC_SINGLE_TLV("Line In Playback Volume",
1031dff50512SChen-Yu Tsai SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_LINEING,
1032dff50512SChen-Yu Tsai 0x7, 0, sun6i_codec_out_mixer_pregain_scale),
1033ecd5cdb4SChen-Yu Tsai SOC_SINGLE_TLV("Mic1 Playback Volume",
1034ecd5cdb4SChen-Yu Tsai SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_MIC1G,
1035ecd5cdb4SChen-Yu Tsai 0x7, 0, sun6i_codec_out_mixer_pregain_scale),
1036ecd5cdb4SChen-Yu Tsai SOC_SINGLE_TLV("Mic2 Playback Volume",
1037ecd5cdb4SChen-Yu Tsai SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_MIC2G,
1038ecd5cdb4SChen-Yu Tsai 0x7, 0, sun6i_codec_out_mixer_pregain_scale),
1039ecd5cdb4SChen-Yu Tsai
1040ecd5cdb4SChen-Yu Tsai /* Microphone Amp boost gains */
1041ecd5cdb4SChen-Yu Tsai SOC_SINGLE_TLV("Mic1 Boost Volume", SUN6I_CODEC_MIC_CTRL,
1042ecd5cdb4SChen-Yu Tsai SUN6I_CODEC_MIC_CTRL_MIC1BOOST, 0x7, 0,
1043ecd5cdb4SChen-Yu Tsai sun6i_codec_mic_gain_scale),
1044ecd5cdb4SChen-Yu Tsai SOC_SINGLE_TLV("Mic2 Boost Volume", SUN6I_CODEC_MIC_CTRL,
1045ecd5cdb4SChen-Yu Tsai SUN6I_CODEC_MIC_CTRL_MIC2BOOST, 0x7, 0,
1046ecd5cdb4SChen-Yu Tsai sun6i_codec_mic_gain_scale),
104724c99f84SChen-Yu Tsai SOC_DOUBLE_TLV("ADC Capture Volume",
104824c99f84SChen-Yu Tsai SUN6I_CODEC_ADC_ACTL, SUN6I_CODEC_ADC_ACTL_ADCLG,
104924c99f84SChen-Yu Tsai SUN6I_CODEC_ADC_ACTL_ADCRG, 0x7, 0,
105024c99f84SChen-Yu Tsai sun6i_codec_out_mixer_pregain_scale),
10518d9e4c9eSChen-Yu Tsai };
10528d9e4c9eSChen-Yu Tsai
10538d9e4c9eSChen-Yu Tsai static const struct snd_soc_dapm_widget sun6i_codec_codec_dapm_widgets[] = {
1054ecd5cdb4SChen-Yu Tsai /* Microphone inputs */
1055ecd5cdb4SChen-Yu Tsai SND_SOC_DAPM_INPUT("MIC1"),
1056ecd5cdb4SChen-Yu Tsai SND_SOC_DAPM_INPUT("MIC2"),
1057ecd5cdb4SChen-Yu Tsai SND_SOC_DAPM_INPUT("MIC3"),
1058ecd5cdb4SChen-Yu Tsai
1059ecd5cdb4SChen-Yu Tsai /* Microphone Bias */
1060ecd5cdb4SChen-Yu Tsai SND_SOC_DAPM_SUPPLY("HBIAS", SUN6I_CODEC_MIC_CTRL,
1061ecd5cdb4SChen-Yu Tsai SUN6I_CODEC_MIC_CTRL_HBIASEN, 0, NULL, 0),
1062ecd5cdb4SChen-Yu Tsai SND_SOC_DAPM_SUPPLY("MBIAS", SUN6I_CODEC_MIC_CTRL,
1063ecd5cdb4SChen-Yu Tsai SUN6I_CODEC_MIC_CTRL_MBIASEN, 0, NULL, 0),
1064ecd5cdb4SChen-Yu Tsai
1065ecd5cdb4SChen-Yu Tsai /* Mic input path */
1066ecd5cdb4SChen-Yu Tsai SND_SOC_DAPM_MUX("Mic2 Amplifier Source Route",
1067ecd5cdb4SChen-Yu Tsai SND_SOC_NOPM, 0, 0, sun6i_codec_mic2_src),
1068ecd5cdb4SChen-Yu Tsai SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN6I_CODEC_MIC_CTRL,
1069ecd5cdb4SChen-Yu Tsai SUN6I_CODEC_MIC_CTRL_MIC1AMPEN, 0, NULL, 0),
1070ecd5cdb4SChen-Yu Tsai SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN6I_CODEC_MIC_CTRL,
1071ecd5cdb4SChen-Yu Tsai SUN6I_CODEC_MIC_CTRL_MIC2AMPEN, 0, NULL, 0),
1072ecd5cdb4SChen-Yu Tsai
1073dff50512SChen-Yu Tsai /* Line In */
1074dff50512SChen-Yu Tsai SND_SOC_DAPM_INPUT("LINEIN"),
1075dff50512SChen-Yu Tsai
107624c99f84SChen-Yu Tsai /* Digital parts of the ADCs */
107724c99f84SChen-Yu Tsai SND_SOC_DAPM_SUPPLY("ADC Enable", SUN6I_CODEC_ADC_FIFOC,
107824c99f84SChen-Yu Tsai SUN6I_CODEC_ADC_FIFOC_EN_AD, 0,
107924c99f84SChen-Yu Tsai NULL, 0),
108024c99f84SChen-Yu Tsai
108124c99f84SChen-Yu Tsai /* Analog parts of the ADCs */
108224c99f84SChen-Yu Tsai SND_SOC_DAPM_ADC("Left ADC", "Codec Capture", SUN6I_CODEC_ADC_ACTL,
108324c99f84SChen-Yu Tsai SUN6I_CODEC_ADC_ACTL_ADCLEN, 0),
108424c99f84SChen-Yu Tsai SND_SOC_DAPM_ADC("Right ADC", "Codec Capture", SUN6I_CODEC_ADC_ACTL,
108524c99f84SChen-Yu Tsai SUN6I_CODEC_ADC_ACTL_ADCREN, 0),
108624c99f84SChen-Yu Tsai
108724c99f84SChen-Yu Tsai /* ADC Mixers */
108824c99f84SChen-Yu Tsai SOC_MIXER_ARRAY("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
108924c99f84SChen-Yu Tsai sun6i_codec_adc_mixer_controls),
109024c99f84SChen-Yu Tsai SOC_MIXER_ARRAY("Right ADC Mixer", SND_SOC_NOPM, 0, 0,
109124c99f84SChen-Yu Tsai sun6i_codec_adc_mixer_controls),
109224c99f84SChen-Yu Tsai
10938d9e4c9eSChen-Yu Tsai /* Digital parts of the DACs */
10948d9e4c9eSChen-Yu Tsai SND_SOC_DAPM_SUPPLY("DAC Enable", SUN4I_CODEC_DAC_DPC,
10958d9e4c9eSChen-Yu Tsai SUN4I_CODEC_DAC_DPC_EN_DA, 0,
10968d9e4c9eSChen-Yu Tsai NULL, 0),
10978d9e4c9eSChen-Yu Tsai
10988d9e4c9eSChen-Yu Tsai /* Analog parts of the DACs */
10998d9e4c9eSChen-Yu Tsai SND_SOC_DAPM_DAC("Left DAC", "Codec Playback",
11008d9e4c9eSChen-Yu Tsai SUN6I_CODEC_OM_DACA_CTRL,
11018d9e4c9eSChen-Yu Tsai SUN6I_CODEC_OM_DACA_CTRL_DACALEN, 0),
11028d9e4c9eSChen-Yu Tsai SND_SOC_DAPM_DAC("Right DAC", "Codec Playback",
11038d9e4c9eSChen-Yu Tsai SUN6I_CODEC_OM_DACA_CTRL,
11048d9e4c9eSChen-Yu Tsai SUN6I_CODEC_OM_DACA_CTRL_DACAREN, 0),
11058d9e4c9eSChen-Yu Tsai
11068d9e4c9eSChen-Yu Tsai /* Mixers */
11078d9e4c9eSChen-Yu Tsai SOC_MIXER_ARRAY("Left Mixer", SUN6I_CODEC_OM_DACA_CTRL,
11088d9e4c9eSChen-Yu Tsai SUN6I_CODEC_OM_DACA_CTRL_LMIXEN, 0,
11098d9e4c9eSChen-Yu Tsai sun6i_codec_mixer_controls),
11108d9e4c9eSChen-Yu Tsai SOC_MIXER_ARRAY("Right Mixer", SUN6I_CODEC_OM_DACA_CTRL,
11118d9e4c9eSChen-Yu Tsai SUN6I_CODEC_OM_DACA_CTRL_RMIXEN, 0,
11128d9e4c9eSChen-Yu Tsai sun6i_codec_mixer_controls),
11138d9e4c9eSChen-Yu Tsai
11148d9e4c9eSChen-Yu Tsai /* Headphone output path */
11158d9e4c9eSChen-Yu Tsai SND_SOC_DAPM_MUX("Headphone Source Playback Route",
11168d9e4c9eSChen-Yu Tsai SND_SOC_NOPM, 0, 0, sun6i_codec_hp_src),
11178d9e4c9eSChen-Yu Tsai SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUN6I_CODEC_OM_PA_CTRL,
11188d9e4c9eSChen-Yu Tsai SUN6I_CODEC_OM_PA_CTRL_HPPAEN, 0, NULL, 0),
11198d9e4c9eSChen-Yu Tsai SND_SOC_DAPM_SUPPLY("HPCOM Protection", SUN6I_CODEC_OM_PA_CTRL,
11208d9e4c9eSChen-Yu Tsai SUN6I_CODEC_OM_PA_CTRL_COMPTEN, 0, NULL, 0),
11218d9e4c9eSChen-Yu Tsai SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPCOM", SUN6I_CODEC_OM_PA_CTRL,
11228d9e4c9eSChen-Yu Tsai SUN6I_CODEC_OM_PA_CTRL_HPCOM_CTL, 0x3, 0x3, 0),
11238d9e4c9eSChen-Yu Tsai SND_SOC_DAPM_OUTPUT("HP"),
11240f909f98SChen-Yu Tsai
11250f909f98SChen-Yu Tsai /* Line Out path */
11260f909f98SChen-Yu Tsai SND_SOC_DAPM_MUX("Line Out Source Playback Route",
11270f909f98SChen-Yu Tsai SND_SOC_NOPM, 0, 0, sun6i_codec_lineout_src),
11280f909f98SChen-Yu Tsai SND_SOC_DAPM_OUTPUT("LINEOUT"),
11298d9e4c9eSChen-Yu Tsai };
11308d9e4c9eSChen-Yu Tsai
11318d9e4c9eSChen-Yu Tsai static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = {
11328d9e4c9eSChen-Yu Tsai /* DAC Routes */
11338d9e4c9eSChen-Yu Tsai { "Left DAC", NULL, "DAC Enable" },
11348d9e4c9eSChen-Yu Tsai { "Right DAC", NULL, "DAC Enable" },
11358d9e4c9eSChen-Yu Tsai
1136ecd5cdb4SChen-Yu Tsai /* Microphone Routes */
1137ecd5cdb4SChen-Yu Tsai { "Mic1 Amplifier", NULL, "MIC1"},
1138ecd5cdb4SChen-Yu Tsai { "Mic2 Amplifier Source Route", "Mic2", "MIC2" },
1139ecd5cdb4SChen-Yu Tsai { "Mic2 Amplifier Source Route", "Mic3", "MIC3" },
1140ecd5cdb4SChen-Yu Tsai { "Mic2 Amplifier", NULL, "Mic2 Amplifier Source Route"},
1141ecd5cdb4SChen-Yu Tsai
11428d9e4c9eSChen-Yu Tsai /* Left Mixer Routes */
11438d9e4c9eSChen-Yu Tsai { "Left Mixer", "DAC Playback Switch", "Left DAC" },
11448d9e4c9eSChen-Yu Tsai { "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" },
1145dff50512SChen-Yu Tsai { "Left Mixer", "Line In Playback Switch", "LINEIN" },
1146ecd5cdb4SChen-Yu Tsai { "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
1147ecd5cdb4SChen-Yu Tsai { "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
11488d9e4c9eSChen-Yu Tsai
11498d9e4c9eSChen-Yu Tsai /* Right Mixer Routes */
11508d9e4c9eSChen-Yu Tsai { "Right Mixer", "DAC Playback Switch", "Right DAC" },
11518d9e4c9eSChen-Yu Tsai { "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" },
1152dff50512SChen-Yu Tsai { "Right Mixer", "Line In Playback Switch", "LINEIN" },
1153ecd5cdb4SChen-Yu Tsai { "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
1154ecd5cdb4SChen-Yu Tsai { "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
11558d9e4c9eSChen-Yu Tsai
115624c99f84SChen-Yu Tsai /* Left ADC Mixer Routes */
115724c99f84SChen-Yu Tsai { "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" },
115824c99f84SChen-Yu Tsai { "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" },
115924c99f84SChen-Yu Tsai { "Left ADC Mixer", "Line In Capture Switch", "LINEIN" },
116024c99f84SChen-Yu Tsai { "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
116124c99f84SChen-Yu Tsai { "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
116224c99f84SChen-Yu Tsai
116324c99f84SChen-Yu Tsai /* Right ADC Mixer Routes */
116424c99f84SChen-Yu Tsai { "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" },
116524c99f84SChen-Yu Tsai { "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" },
116624c99f84SChen-Yu Tsai { "Right ADC Mixer", "Line In Capture Switch", "LINEIN" },
116724c99f84SChen-Yu Tsai { "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
116824c99f84SChen-Yu Tsai { "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
116924c99f84SChen-Yu Tsai
11708d9e4c9eSChen-Yu Tsai /* Headphone Routes */
11718d9e4c9eSChen-Yu Tsai { "Headphone Source Playback Route", "DAC", "Left DAC" },
11728d9e4c9eSChen-Yu Tsai { "Headphone Source Playback Route", "DAC", "Right DAC" },
11738d9e4c9eSChen-Yu Tsai { "Headphone Source Playback Route", "Mixer", "Left Mixer" },
11748d9e4c9eSChen-Yu Tsai { "Headphone Source Playback Route", "Mixer", "Right Mixer" },
11758d9e4c9eSChen-Yu Tsai { "Headphone Amp", NULL, "Headphone Source Playback Route" },
11768d9e4c9eSChen-Yu Tsai { "HP", NULL, "Headphone Amp" },
11778d9e4c9eSChen-Yu Tsai { "HPCOM", NULL, "HPCOM Protection" },
11780f909f98SChen-Yu Tsai
11790f909f98SChen-Yu Tsai /* Line Out Routes */
11800f909f98SChen-Yu Tsai { "Line Out Source Playback Route", "Stereo", "Left Mixer" },
11810f909f98SChen-Yu Tsai { "Line Out Source Playback Route", "Stereo", "Right Mixer" },
11820f909f98SChen-Yu Tsai { "Line Out Source Playback Route", "Mono Differential", "Left Mixer" },
118319426bdeSChen-Yu Tsai { "Line Out Source Playback Route", "Mono Differential", "Right Mixer" },
11840f909f98SChen-Yu Tsai { "LINEOUT", NULL, "Line Out Source Playback Route" },
118524c99f84SChen-Yu Tsai
118624c99f84SChen-Yu Tsai /* ADC Routes */
118724c99f84SChen-Yu Tsai { "Left ADC", NULL, "ADC Enable" },
118824c99f84SChen-Yu Tsai { "Right ADC", NULL, "ADC Enable" },
118924c99f84SChen-Yu Tsai { "Left ADC", NULL, "Left ADC Mixer" },
119024c99f84SChen-Yu Tsai { "Right ADC", NULL, "Right ADC Mixer" },
11918d9e4c9eSChen-Yu Tsai };
11928d9e4c9eSChen-Yu Tsai
119377f4be16SKuninori Morimoto static const struct snd_soc_component_driver sun6i_codec_codec = {
11948d9e4c9eSChen-Yu Tsai .controls = sun6i_codec_codec_widgets,
11958d9e4c9eSChen-Yu Tsai .num_controls = ARRAY_SIZE(sun6i_codec_codec_widgets),
11968d9e4c9eSChen-Yu Tsai .dapm_widgets = sun6i_codec_codec_dapm_widgets,
11978d9e4c9eSChen-Yu Tsai .num_dapm_widgets = ARRAY_SIZE(sun6i_codec_codec_dapm_widgets),
11988d9e4c9eSChen-Yu Tsai .dapm_routes = sun6i_codec_codec_dapm_routes,
11998d9e4c9eSChen-Yu Tsai .num_dapm_routes = ARRAY_SIZE(sun6i_codec_codec_dapm_routes),
120077f4be16SKuninori Morimoto .idle_bias_on = 1,
120177f4be16SKuninori Morimoto .use_pmdown_time = 1,
120277f4be16SKuninori Morimoto .endianness = 1,
12038d9e4c9eSChen-Yu Tsai };
12048d9e4c9eSChen-Yu Tsai
1205dac5f86bSChen-Yu Tsai /* sun8i A23 codec */
1206dac5f86bSChen-Yu Tsai static const struct snd_kcontrol_new sun8i_a23_codec_codec_controls[] = {
1207dac5f86bSChen-Yu Tsai SOC_SINGLE_TLV("DAC Playback Volume", SUN4I_CODEC_DAC_DPC,
1208dac5f86bSChen-Yu Tsai SUN4I_CODEC_DAC_DPC_DVOL, 0x3f, 1,
1209dac5f86bSChen-Yu Tsai sun6i_codec_dvol_scale),
1210dac5f86bSChen-Yu Tsai };
1211dac5f86bSChen-Yu Tsai
1212dac5f86bSChen-Yu Tsai static const struct snd_soc_dapm_widget sun8i_a23_codec_codec_widgets[] = {
1213dac5f86bSChen-Yu Tsai /* Digital parts of the ADCs */
1214dac5f86bSChen-Yu Tsai SND_SOC_DAPM_SUPPLY("ADC Enable", SUN6I_CODEC_ADC_FIFOC,
1215dac5f86bSChen-Yu Tsai SUN6I_CODEC_ADC_FIFOC_EN_AD, 0, NULL, 0),
1216dac5f86bSChen-Yu Tsai /* Digital parts of the DACs */
1217dac5f86bSChen-Yu Tsai SND_SOC_DAPM_SUPPLY("DAC Enable", SUN4I_CODEC_DAC_DPC,
1218dac5f86bSChen-Yu Tsai SUN4I_CODEC_DAC_DPC_EN_DA, 0, NULL, 0),
1219dac5f86bSChen-Yu Tsai
1220dac5f86bSChen-Yu Tsai };
1221dac5f86bSChen-Yu Tsai
122277f4be16SKuninori Morimoto static const struct snd_soc_component_driver sun8i_a23_codec_codec = {
1223dac5f86bSChen-Yu Tsai .controls = sun8i_a23_codec_codec_controls,
1224dac5f86bSChen-Yu Tsai .num_controls = ARRAY_SIZE(sun8i_a23_codec_codec_controls),
1225dac5f86bSChen-Yu Tsai .dapm_widgets = sun8i_a23_codec_codec_widgets,
1226dac5f86bSChen-Yu Tsai .num_dapm_widgets = ARRAY_SIZE(sun8i_a23_codec_codec_widgets),
122777f4be16SKuninori Morimoto .idle_bias_on = 1,
122877f4be16SKuninori Morimoto .use_pmdown_time = 1,
122977f4be16SKuninori Morimoto .endianness = 1,
1230dac5f86bSChen-Yu Tsai };
1231dac5f86bSChen-Yu Tsai
123245fb6b6fSEmilio López static const struct snd_soc_component_driver sun4i_codec_component = {
123345fb6b6fSEmilio López .name = "sun4i-codec",
1234f450b5dbSCharles Keepax .legacy_dai_naming = 1,
1235717a8ff2SMikhail Rudenko #ifdef CONFIG_DEBUG_FS
1236717a8ff2SMikhail Rudenko .debugfs_prefix = "cpu",
1237717a8ff2SMikhail Rudenko #endif
123845fb6b6fSEmilio López };
123945fb6b6fSEmilio López
12405a0cf024SAndrea Bondavalli #define SUN4I_CODEC_RATES SNDRV_PCM_RATE_CONTINUOUS
124145fb6b6fSEmilio López #define SUN4I_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
124245fb6b6fSEmilio López SNDRV_PCM_FMTBIT_S32_LE)
124345fb6b6fSEmilio López
sun4i_codec_dai_probe(struct snd_soc_dai * dai)124445fb6b6fSEmilio López static int sun4i_codec_dai_probe(struct snd_soc_dai *dai)
124545fb6b6fSEmilio López {
124645fb6b6fSEmilio López struct snd_soc_card *card = snd_soc_dai_get_drvdata(dai);
124745fb6b6fSEmilio López struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card);
124845fb6b6fSEmilio López
124945fb6b6fSEmilio López snd_soc_dai_init_dma_data(dai, &scodec->playback_dma_data,
12501fb34b48SMaxime Ripard &scodec->capture_dma_data);
125145fb6b6fSEmilio López
125245fb6b6fSEmilio López return 0;
125345fb6b6fSEmilio López }
125445fb6b6fSEmilio López
1255*fc95a8a3SKuninori Morimoto static const struct snd_soc_dai_ops dummy_dai_ops = {
1256*fc95a8a3SKuninori Morimoto .probe = sun4i_codec_dai_probe,
1257*fc95a8a3SKuninori Morimoto };
1258*fc95a8a3SKuninori Morimoto
125945fb6b6fSEmilio López static struct snd_soc_dai_driver dummy_cpu_dai = {
126045fb6b6fSEmilio López .name = "sun4i-codec-cpu-dai",
126145fb6b6fSEmilio López .playback = {
126245fb6b6fSEmilio López .stream_name = "Playback",
126345fb6b6fSEmilio López .channels_min = 1,
126445fb6b6fSEmilio López .channels_max = 2,
126545fb6b6fSEmilio López .rates = SUN4I_CODEC_RATES,
126645fb6b6fSEmilio López .formats = SUN4I_CODEC_FORMATS,
126745fb6b6fSEmilio López .sig_bits = 24,
126845fb6b6fSEmilio López },
12691fb34b48SMaxime Ripard .capture = {
12701fb34b48SMaxime Ripard .stream_name = "Capture",
12711fb34b48SMaxime Ripard .channels_min = 1,
12721fb34b48SMaxime Ripard .channels_max = 2,
12731fb34b48SMaxime Ripard .rates = SUN4I_CODEC_RATES,
12741fb34b48SMaxime Ripard .formats = SUN4I_CODEC_FORMATS,
12751fb34b48SMaxime Ripard .sig_bits = 24,
12761fb34b48SMaxime Ripard },
1277*fc95a8a3SKuninori Morimoto .ops = &dummy_dai_ops,
127845fb6b6fSEmilio López };
127945fb6b6fSEmilio López
sun4i_codec_create_link(struct device * dev,int * num_links)128045fb6b6fSEmilio López static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev,
128145fb6b6fSEmilio López int *num_links)
128245fb6b6fSEmilio López {
128345fb6b6fSEmilio López struct snd_soc_dai_link *link = devm_kzalloc(dev, sizeof(*link),
128445fb6b6fSEmilio López GFP_KERNEL);
1285950d10e8SKuninori Morimoto struct snd_soc_dai_link_component *dlc = devm_kzalloc(dev,
1286d3569a47SKuninori Morimoto 3 * sizeof(*dlc), GFP_KERNEL);
1287950d10e8SKuninori Morimoto if (!link || !dlc)
128845fb6b6fSEmilio López return NULL;
128945fb6b6fSEmilio López
1290950d10e8SKuninori Morimoto link->cpus = &dlc[0];
1291950d10e8SKuninori Morimoto link->codecs = &dlc[1];
1292d3569a47SKuninori Morimoto link->platforms = &dlc[2];
1293950d10e8SKuninori Morimoto
1294950d10e8SKuninori Morimoto link->num_cpus = 1;
1295950d10e8SKuninori Morimoto link->num_codecs = 1;
1296d3569a47SKuninori Morimoto link->num_platforms = 1;
1297950d10e8SKuninori Morimoto
129845fb6b6fSEmilio López link->name = "cdc";
129945fb6b6fSEmilio López link->stream_name = "CDC PCM";
1300950d10e8SKuninori Morimoto link->codecs->dai_name = "Codec";
1301950d10e8SKuninori Morimoto link->cpus->dai_name = dev_name(dev);
1302950d10e8SKuninori Morimoto link->codecs->name = dev_name(dev);
1303d3569a47SKuninori Morimoto link->platforms->name = dev_name(dev);
130445fb6b6fSEmilio López link->dai_fmt = SND_SOC_DAIFMT_I2S;
130545fb6b6fSEmilio López
130645fb6b6fSEmilio López *num_links = 1;
130745fb6b6fSEmilio López
130845fb6b6fSEmilio López return link;
130945fb6b6fSEmilio López };
131045fb6b6fSEmilio López
sun4i_codec_spk_event(struct snd_soc_dapm_widget * w,struct snd_kcontrol * k,int event)131140592627SHans de Goede static int sun4i_codec_spk_event(struct snd_soc_dapm_widget *w,
131240592627SHans de Goede struct snd_kcontrol *k, int event)
131340592627SHans de Goede {
131440592627SHans de Goede struct sun4i_codec *scodec = snd_soc_card_get_drvdata(w->dapm->card);
131540592627SHans de Goede
131640592627SHans de Goede gpiod_set_value_cansleep(scodec->gpio_pa,
131740592627SHans de Goede !!SND_SOC_DAPM_EVENT_ON(event));
131840592627SHans de Goede
13191f2675f6SGeorgii Staroselskii if (SND_SOC_DAPM_EVENT_ON(event)) {
13201f2675f6SGeorgii Staroselskii /*
13211f2675f6SGeorgii Staroselskii * Need a delay to wait for DAC to push the data. 700ms seems
13221f2675f6SGeorgii Staroselskii * to be the best compromise not to feel this delay while
13231f2675f6SGeorgii Staroselskii * playing a sound.
13241f2675f6SGeorgii Staroselskii */
13251f2675f6SGeorgii Staroselskii msleep(700);
13261f2675f6SGeorgii Staroselskii }
13271f2675f6SGeorgii Staroselskii
132840592627SHans de Goede return 0;
132940592627SHans de Goede }
133040592627SHans de Goede
133140592627SHans de Goede static const struct snd_soc_dapm_widget sun4i_codec_card_dapm_widgets[] = {
133240592627SHans de Goede SND_SOC_DAPM_SPK("Speaker", sun4i_codec_spk_event),
133340592627SHans de Goede };
133440592627SHans de Goede
133540592627SHans de Goede static const struct snd_soc_dapm_route sun4i_codec_card_dapm_routes[] = {
13366b803c61SHans de Goede { "Speaker", NULL, "HP Right" },
13376b803c61SHans de Goede { "Speaker", NULL, "HP Left" },
133840592627SHans de Goede };
133940592627SHans de Goede
sun4i_codec_create_card(struct device * dev)134045fb6b6fSEmilio López static struct snd_soc_card *sun4i_codec_create_card(struct device *dev)
134145fb6b6fSEmilio López {
134245fb6b6fSEmilio López struct snd_soc_card *card;
134345fb6b6fSEmilio López
134445fb6b6fSEmilio López card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
134545fb6b6fSEmilio López if (!card)
134685915b63SChen-Yu Tsai return ERR_PTR(-ENOMEM);
134745fb6b6fSEmilio López
134845fb6b6fSEmilio López card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
134945fb6b6fSEmilio López if (!card->dai_link)
135085915b63SChen-Yu Tsai return ERR_PTR(-ENOMEM);
135145fb6b6fSEmilio López
135245fb6b6fSEmilio López card->dev = dev;
13537c0d6e48SBastian Germann card->owner = THIS_MODULE;
135445fb6b6fSEmilio López card->name = "sun4i-codec";
135540592627SHans de Goede card->dapm_widgets = sun4i_codec_card_dapm_widgets;
135640592627SHans de Goede card->num_dapm_widgets = ARRAY_SIZE(sun4i_codec_card_dapm_widgets);
135740592627SHans de Goede card->dapm_routes = sun4i_codec_card_dapm_routes;
135840592627SHans de Goede card->num_dapm_routes = ARRAY_SIZE(sun4i_codec_card_dapm_routes);
135945fb6b6fSEmilio López
136045fb6b6fSEmilio López return card;
136145fb6b6fSEmilio López };
136245fb6b6fSEmilio López
1363300a18d1SChen-Yu Tsai static const struct snd_soc_dapm_widget sun6i_codec_card_dapm_widgets[] = {
1364300a18d1SChen-Yu Tsai SND_SOC_DAPM_HP("Headphone", NULL),
1365300a18d1SChen-Yu Tsai SND_SOC_DAPM_LINE("Line In", NULL),
1366300a18d1SChen-Yu Tsai SND_SOC_DAPM_LINE("Line Out", NULL),
1367300a18d1SChen-Yu Tsai SND_SOC_DAPM_MIC("Headset Mic", NULL),
1368300a18d1SChen-Yu Tsai SND_SOC_DAPM_MIC("Mic", NULL),
1369300a18d1SChen-Yu Tsai SND_SOC_DAPM_SPK("Speaker", sun4i_codec_spk_event),
1370300a18d1SChen-Yu Tsai };
1371300a18d1SChen-Yu Tsai
sun6i_codec_create_card(struct device * dev)13728d9e4c9eSChen-Yu Tsai static struct snd_soc_card *sun6i_codec_create_card(struct device *dev)
13738d9e4c9eSChen-Yu Tsai {
13748d9e4c9eSChen-Yu Tsai struct snd_soc_card *card;
1375300a18d1SChen-Yu Tsai int ret;
13768d9e4c9eSChen-Yu Tsai
13778d9e4c9eSChen-Yu Tsai card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
13788d9e4c9eSChen-Yu Tsai if (!card)
13798d9e4c9eSChen-Yu Tsai return ERR_PTR(-ENOMEM);
13808d9e4c9eSChen-Yu Tsai
13818d9e4c9eSChen-Yu Tsai card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
13828d9e4c9eSChen-Yu Tsai if (!card->dai_link)
13838d9e4c9eSChen-Yu Tsai return ERR_PTR(-ENOMEM);
13848d9e4c9eSChen-Yu Tsai
13858d9e4c9eSChen-Yu Tsai card->dev = dev;
13867c0d6e48SBastian Germann card->owner = THIS_MODULE;
13878d9e4c9eSChen-Yu Tsai card->name = "A31 Audio Codec";
1388300a18d1SChen-Yu Tsai card->dapm_widgets = sun6i_codec_card_dapm_widgets;
1389300a18d1SChen-Yu Tsai card->num_dapm_widgets = ARRAY_SIZE(sun6i_codec_card_dapm_widgets);
1390300a18d1SChen-Yu Tsai card->fully_routed = true;
1391300a18d1SChen-Yu Tsai
1392300a18d1SChen-Yu Tsai ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing");
1393300a18d1SChen-Yu Tsai if (ret)
1394300a18d1SChen-Yu Tsai dev_warn(dev, "failed to parse audio-routing: %d\n", ret);
13958d9e4c9eSChen-Yu Tsai
13968d9e4c9eSChen-Yu Tsai return card;
13978d9e4c9eSChen-Yu Tsai };
13988d9e4c9eSChen-Yu Tsai
1399dac5f86bSChen-Yu Tsai /* Connect digital side enables to analog side widgets */
1400dac5f86bSChen-Yu Tsai static const struct snd_soc_dapm_route sun8i_codec_card_routes[] = {
1401dac5f86bSChen-Yu Tsai /* ADC Routes */
1402dac5f86bSChen-Yu Tsai { "Left ADC", NULL, "ADC Enable" },
1403dac5f86bSChen-Yu Tsai { "Right ADC", NULL, "ADC Enable" },
1404dac5f86bSChen-Yu Tsai { "Codec Capture", NULL, "Left ADC" },
1405dac5f86bSChen-Yu Tsai { "Codec Capture", NULL, "Right ADC" },
1406dac5f86bSChen-Yu Tsai
1407dac5f86bSChen-Yu Tsai /* DAC Routes */
1408dac5f86bSChen-Yu Tsai { "Left DAC", NULL, "DAC Enable" },
1409dac5f86bSChen-Yu Tsai { "Right DAC", NULL, "DAC Enable" },
1410dac5f86bSChen-Yu Tsai { "Left DAC", NULL, "Codec Playback" },
1411dac5f86bSChen-Yu Tsai { "Right DAC", NULL, "Codec Playback" },
1412dac5f86bSChen-Yu Tsai };
1413dac5f86bSChen-Yu Tsai
1414dac5f86bSChen-Yu Tsai static struct snd_soc_aux_dev aux_dev = {
14153d0d2d64SKuninori Morimoto .dlc = COMP_EMPTY(),
1416dac5f86bSChen-Yu Tsai };
1417dac5f86bSChen-Yu Tsai
sun8i_a23_codec_create_card(struct device * dev)1418dac5f86bSChen-Yu Tsai static struct snd_soc_card *sun8i_a23_codec_create_card(struct device *dev)
1419dac5f86bSChen-Yu Tsai {
1420dac5f86bSChen-Yu Tsai struct snd_soc_card *card;
1421dac5f86bSChen-Yu Tsai int ret;
1422dac5f86bSChen-Yu Tsai
1423dac5f86bSChen-Yu Tsai card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
1424dac5f86bSChen-Yu Tsai if (!card)
1425dac5f86bSChen-Yu Tsai return ERR_PTR(-ENOMEM);
1426dac5f86bSChen-Yu Tsai
14273d0d2d64SKuninori Morimoto aux_dev.dlc.of_node = of_parse_phandle(dev->of_node,
1428dac5f86bSChen-Yu Tsai "allwinner,codec-analog-controls",
1429dac5f86bSChen-Yu Tsai 0);
14303d0d2d64SKuninori Morimoto if (!aux_dev.dlc.of_node) {
1431dac5f86bSChen-Yu Tsai dev_err(dev, "Can't find analog controls for codec.\n");
1432dac5f86bSChen-Yu Tsai return ERR_PTR(-EINVAL);
143324d05966SYueHaibing }
1434dac5f86bSChen-Yu Tsai
1435dac5f86bSChen-Yu Tsai card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
1436dac5f86bSChen-Yu Tsai if (!card->dai_link)
1437dac5f86bSChen-Yu Tsai return ERR_PTR(-ENOMEM);
1438dac5f86bSChen-Yu Tsai
1439dac5f86bSChen-Yu Tsai card->dev = dev;
14407c0d6e48SBastian Germann card->owner = THIS_MODULE;
1441dac5f86bSChen-Yu Tsai card->name = "A23 Audio Codec";
1442dac5f86bSChen-Yu Tsai card->dapm_widgets = sun6i_codec_card_dapm_widgets;
1443dac5f86bSChen-Yu Tsai card->num_dapm_widgets = ARRAY_SIZE(sun6i_codec_card_dapm_widgets);
1444dac5f86bSChen-Yu Tsai card->dapm_routes = sun8i_codec_card_routes;
1445dac5f86bSChen-Yu Tsai card->num_dapm_routes = ARRAY_SIZE(sun8i_codec_card_routes);
1446dac5f86bSChen-Yu Tsai card->aux_dev = &aux_dev;
1447dac5f86bSChen-Yu Tsai card->num_aux_devs = 1;
1448dac5f86bSChen-Yu Tsai card->fully_routed = true;
1449dac5f86bSChen-Yu Tsai
1450dac5f86bSChen-Yu Tsai ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing");
1451dac5f86bSChen-Yu Tsai if (ret)
1452dac5f86bSChen-Yu Tsai dev_warn(dev, "failed to parse audio-routing: %d\n", ret);
1453dac5f86bSChen-Yu Tsai
1454dac5f86bSChen-Yu Tsai return card;
1455dac5f86bSChen-Yu Tsai };
1456dac5f86bSChen-Yu Tsai
sun8i_h3_codec_create_card(struct device * dev)14574a15b24aSChen-Yu Tsai static struct snd_soc_card *sun8i_h3_codec_create_card(struct device *dev)
14584a15b24aSChen-Yu Tsai {
14594a15b24aSChen-Yu Tsai struct snd_soc_card *card;
14604a15b24aSChen-Yu Tsai int ret;
14614a15b24aSChen-Yu Tsai
14624a15b24aSChen-Yu Tsai card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
14634a15b24aSChen-Yu Tsai if (!card)
14644a15b24aSChen-Yu Tsai return ERR_PTR(-ENOMEM);
14654a15b24aSChen-Yu Tsai
14663d0d2d64SKuninori Morimoto aux_dev.dlc.of_node = of_parse_phandle(dev->of_node,
14674a15b24aSChen-Yu Tsai "allwinner,codec-analog-controls",
14684a15b24aSChen-Yu Tsai 0);
14693d0d2d64SKuninori Morimoto if (!aux_dev.dlc.of_node) {
14704a15b24aSChen-Yu Tsai dev_err(dev, "Can't find analog controls for codec.\n");
14714a15b24aSChen-Yu Tsai return ERR_PTR(-EINVAL);
147224d05966SYueHaibing }
14734a15b24aSChen-Yu Tsai
14744a15b24aSChen-Yu Tsai card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
14754a15b24aSChen-Yu Tsai if (!card->dai_link)
14764a15b24aSChen-Yu Tsai return ERR_PTR(-ENOMEM);
14774a15b24aSChen-Yu Tsai
14784a15b24aSChen-Yu Tsai card->dev = dev;
14797c0d6e48SBastian Germann card->owner = THIS_MODULE;
14804a15b24aSChen-Yu Tsai card->name = "H3 Audio Codec";
14814a15b24aSChen-Yu Tsai card->dapm_widgets = sun6i_codec_card_dapm_widgets;
14824a15b24aSChen-Yu Tsai card->num_dapm_widgets = ARRAY_SIZE(sun6i_codec_card_dapm_widgets);
14834a15b24aSChen-Yu Tsai card->dapm_routes = sun8i_codec_card_routes;
14844a15b24aSChen-Yu Tsai card->num_dapm_routes = ARRAY_SIZE(sun8i_codec_card_routes);
14854a15b24aSChen-Yu Tsai card->aux_dev = &aux_dev;
14864a15b24aSChen-Yu Tsai card->num_aux_devs = 1;
14874a15b24aSChen-Yu Tsai card->fully_routed = true;
14884a15b24aSChen-Yu Tsai
14894a15b24aSChen-Yu Tsai ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing");
14904a15b24aSChen-Yu Tsai if (ret)
14914a15b24aSChen-Yu Tsai dev_warn(dev, "failed to parse audio-routing: %d\n", ret);
14924a15b24aSChen-Yu Tsai
14934a15b24aSChen-Yu Tsai return card;
14944a15b24aSChen-Yu Tsai };
14954a15b24aSChen-Yu Tsai
sun8i_v3s_codec_create_card(struct device * dev)14968b2840b6SIcenowy Zheng static struct snd_soc_card *sun8i_v3s_codec_create_card(struct device *dev)
14978b2840b6SIcenowy Zheng {
14988b2840b6SIcenowy Zheng struct snd_soc_card *card;
14998b2840b6SIcenowy Zheng int ret;
15008b2840b6SIcenowy Zheng
15018b2840b6SIcenowy Zheng card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
15028b2840b6SIcenowy Zheng if (!card)
15038b2840b6SIcenowy Zheng return ERR_PTR(-ENOMEM);
15048b2840b6SIcenowy Zheng
15053d0d2d64SKuninori Morimoto aux_dev.dlc.of_node = of_parse_phandle(dev->of_node,
15068b2840b6SIcenowy Zheng "allwinner,codec-analog-controls",
15078b2840b6SIcenowy Zheng 0);
15083d0d2d64SKuninori Morimoto if (!aux_dev.dlc.of_node) {
15098b2840b6SIcenowy Zheng dev_err(dev, "Can't find analog controls for codec.\n");
15108b2840b6SIcenowy Zheng return ERR_PTR(-EINVAL);
151124d05966SYueHaibing }
15128b2840b6SIcenowy Zheng
15138b2840b6SIcenowy Zheng card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
15148b2840b6SIcenowy Zheng if (!card->dai_link)
15158b2840b6SIcenowy Zheng return ERR_PTR(-ENOMEM);
15168b2840b6SIcenowy Zheng
15178b2840b6SIcenowy Zheng card->dev = dev;
15187c0d6e48SBastian Germann card->owner = THIS_MODULE;
15198b2840b6SIcenowy Zheng card->name = "V3s Audio Codec";
15208b2840b6SIcenowy Zheng card->dapm_widgets = sun6i_codec_card_dapm_widgets;
15218b2840b6SIcenowy Zheng card->num_dapm_widgets = ARRAY_SIZE(sun6i_codec_card_dapm_widgets);
15228b2840b6SIcenowy Zheng card->dapm_routes = sun8i_codec_card_routes;
15238b2840b6SIcenowy Zheng card->num_dapm_routes = ARRAY_SIZE(sun8i_codec_card_routes);
15248b2840b6SIcenowy Zheng card->aux_dev = &aux_dev;
15258b2840b6SIcenowy Zheng card->num_aux_devs = 1;
15268b2840b6SIcenowy Zheng card->fully_routed = true;
15278b2840b6SIcenowy Zheng
15288b2840b6SIcenowy Zheng ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing");
15298b2840b6SIcenowy Zheng if (ret)
15308b2840b6SIcenowy Zheng dev_warn(dev, "failed to parse audio-routing: %d\n", ret);
15318b2840b6SIcenowy Zheng
15328b2840b6SIcenowy Zheng return card;
15338b2840b6SIcenowy Zheng };
15348b2840b6SIcenowy Zheng
15352f2a3462SChen-Yu Tsai static const struct regmap_config sun4i_codec_regmap_config = {
15362f2a3462SChen-Yu Tsai .reg_bits = 32,
15372f2a3462SChen-Yu Tsai .reg_stride = 4,
15382f2a3462SChen-Yu Tsai .val_bits = 32,
15392f2a3462SChen-Yu Tsai .max_register = SUN4I_CODEC_ADC_RXCNT,
15402f2a3462SChen-Yu Tsai };
15412f2a3462SChen-Yu Tsai
15428d9e4c9eSChen-Yu Tsai static const struct regmap_config sun6i_codec_regmap_config = {
15438d9e4c9eSChen-Yu Tsai .reg_bits = 32,
15448d9e4c9eSChen-Yu Tsai .reg_stride = 4,
15458d9e4c9eSChen-Yu Tsai .val_bits = 32,
15468d9e4c9eSChen-Yu Tsai .max_register = SUN6I_CODEC_HMIC_DATA,
15478d9e4c9eSChen-Yu Tsai };
15488d9e4c9eSChen-Yu Tsai
15492f2a3462SChen-Yu Tsai static const struct regmap_config sun7i_codec_regmap_config = {
15502f2a3462SChen-Yu Tsai .reg_bits = 32,
15512f2a3462SChen-Yu Tsai .reg_stride = 4,
15522f2a3462SChen-Yu Tsai .val_bits = 32,
15532f2a3462SChen-Yu Tsai .max_register = SUN7I_CODEC_AC_MIC_PHONE_CAL,
15542f2a3462SChen-Yu Tsai };
15552f2a3462SChen-Yu Tsai
1556dac5f86bSChen-Yu Tsai static const struct regmap_config sun8i_a23_codec_regmap_config = {
1557dac5f86bSChen-Yu Tsai .reg_bits = 32,
1558dac5f86bSChen-Yu Tsai .reg_stride = 4,
1559dac5f86bSChen-Yu Tsai .val_bits = 32,
1560dac5f86bSChen-Yu Tsai .max_register = SUN8I_A23_CODEC_ADC_RXCNT,
1561dac5f86bSChen-Yu Tsai };
1562dac5f86bSChen-Yu Tsai
15634a15b24aSChen-Yu Tsai static const struct regmap_config sun8i_h3_codec_regmap_config = {
15644a15b24aSChen-Yu Tsai .reg_bits = 32,
15654a15b24aSChen-Yu Tsai .reg_stride = 4,
15664a15b24aSChen-Yu Tsai .val_bits = 32,
15674a15b24aSChen-Yu Tsai .max_register = SUN8I_H3_CODEC_ADC_DBG,
15684a15b24aSChen-Yu Tsai };
15694a15b24aSChen-Yu Tsai
15708b2840b6SIcenowy Zheng static const struct regmap_config sun8i_v3s_codec_regmap_config = {
15718b2840b6SIcenowy Zheng .reg_bits = 32,
15728b2840b6SIcenowy Zheng .reg_stride = 4,
15738b2840b6SIcenowy Zheng .val_bits = 32,
15748b2840b6SIcenowy Zheng .max_register = SUN8I_H3_CODEC_ADC_DBG,
15758b2840b6SIcenowy Zheng };
15768b2840b6SIcenowy Zheng
15772f2a3462SChen-Yu Tsai struct sun4i_codec_quirks {
15782f2a3462SChen-Yu Tsai const struct regmap_config *regmap_config;
157977f4be16SKuninori Morimoto const struct snd_soc_component_driver *codec;
1580bc03f0d5SChen-Yu Tsai struct snd_soc_card * (*create_card)(struct device *dev);
1581bc03f0d5SChen-Yu Tsai struct reg_field reg_adc_fifoc; /* used for regmap_field */
1582bc03f0d5SChen-Yu Tsai unsigned int reg_dac_txdata; /* TX FIFO offset for DMA config */
1583bc03f0d5SChen-Yu Tsai unsigned int reg_adc_rxdata; /* RX FIFO offset for DMA config */
15849aead156SChen-Yu Tsai bool has_reset;
15852f2a3462SChen-Yu Tsai };
15862f2a3462SChen-Yu Tsai
15872f2a3462SChen-Yu Tsai static const struct sun4i_codec_quirks sun4i_codec_quirks = {
15882f2a3462SChen-Yu Tsai .regmap_config = &sun4i_codec_regmap_config,
1589bc03f0d5SChen-Yu Tsai .codec = &sun4i_codec_codec,
1590bc03f0d5SChen-Yu Tsai .create_card = sun4i_codec_create_card,
1591bc03f0d5SChen-Yu Tsai .reg_adc_fifoc = REG_FIELD(SUN4I_CODEC_ADC_FIFOC, 0, 31),
1592bc03f0d5SChen-Yu Tsai .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA,
1593bc03f0d5SChen-Yu Tsai .reg_adc_rxdata = SUN4I_CODEC_ADC_RXDATA,
15942f2a3462SChen-Yu Tsai };
15952f2a3462SChen-Yu Tsai
15968d9e4c9eSChen-Yu Tsai static const struct sun4i_codec_quirks sun6i_a31_codec_quirks = {
15978d9e4c9eSChen-Yu Tsai .regmap_config = &sun6i_codec_regmap_config,
15988d9e4c9eSChen-Yu Tsai .codec = &sun6i_codec_codec,
15998d9e4c9eSChen-Yu Tsai .create_card = sun6i_codec_create_card,
16008d9e4c9eSChen-Yu Tsai .reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31),
16018d9e4c9eSChen-Yu Tsai .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA,
16028d9e4c9eSChen-Yu Tsai .reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA,
16038d9e4c9eSChen-Yu Tsai .has_reset = true,
16048d9e4c9eSChen-Yu Tsai };
16058d9e4c9eSChen-Yu Tsai
16062f2a3462SChen-Yu Tsai static const struct sun4i_codec_quirks sun7i_codec_quirks = {
16072f2a3462SChen-Yu Tsai .regmap_config = &sun7i_codec_regmap_config,
1608b71a7eb5SDanny Milosavljevic .codec = &sun7i_codec_codec,
1609bc03f0d5SChen-Yu Tsai .create_card = sun4i_codec_create_card,
1610bc03f0d5SChen-Yu Tsai .reg_adc_fifoc = REG_FIELD(SUN4I_CODEC_ADC_FIFOC, 0, 31),
1611bc03f0d5SChen-Yu Tsai .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA,
1612bc03f0d5SChen-Yu Tsai .reg_adc_rxdata = SUN4I_CODEC_ADC_RXDATA,
16132f2a3462SChen-Yu Tsai };
16142f2a3462SChen-Yu Tsai
1615dac5f86bSChen-Yu Tsai static const struct sun4i_codec_quirks sun8i_a23_codec_quirks = {
1616dac5f86bSChen-Yu Tsai .regmap_config = &sun8i_a23_codec_regmap_config,
1617dac5f86bSChen-Yu Tsai .codec = &sun8i_a23_codec_codec,
1618dac5f86bSChen-Yu Tsai .create_card = sun8i_a23_codec_create_card,
1619dac5f86bSChen-Yu Tsai .reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31),
1620dac5f86bSChen-Yu Tsai .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA,
1621dac5f86bSChen-Yu Tsai .reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA,
1622dac5f86bSChen-Yu Tsai .has_reset = true,
1623dac5f86bSChen-Yu Tsai };
1624dac5f86bSChen-Yu Tsai
16254a15b24aSChen-Yu Tsai static const struct sun4i_codec_quirks sun8i_h3_codec_quirks = {
16264a15b24aSChen-Yu Tsai .regmap_config = &sun8i_h3_codec_regmap_config,
16274a15b24aSChen-Yu Tsai /*
16284a15b24aSChen-Yu Tsai * TODO Share the codec structure with A23 for now.
16294a15b24aSChen-Yu Tsai * This should be split out when adding digital audio
16304a15b24aSChen-Yu Tsai * processing support for the H3.
16314a15b24aSChen-Yu Tsai */
16324a15b24aSChen-Yu Tsai .codec = &sun8i_a23_codec_codec,
16334a15b24aSChen-Yu Tsai .create_card = sun8i_h3_codec_create_card,
16344a15b24aSChen-Yu Tsai .reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31),
16354a15b24aSChen-Yu Tsai .reg_dac_txdata = SUN8I_H3_CODEC_DAC_TXDATA,
16364a15b24aSChen-Yu Tsai .reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA,
16374a15b24aSChen-Yu Tsai .has_reset = true,
16384a15b24aSChen-Yu Tsai };
16394a15b24aSChen-Yu Tsai
16408b2840b6SIcenowy Zheng static const struct sun4i_codec_quirks sun8i_v3s_codec_quirks = {
16418b2840b6SIcenowy Zheng .regmap_config = &sun8i_v3s_codec_regmap_config,
16428b2840b6SIcenowy Zheng /*
16438b2840b6SIcenowy Zheng * TODO The codec structure should be split out, like
16448b2840b6SIcenowy Zheng * H3, when adding digital audio processing support.
16458b2840b6SIcenowy Zheng */
16468b2840b6SIcenowy Zheng .codec = &sun8i_a23_codec_codec,
16478b2840b6SIcenowy Zheng .create_card = sun8i_v3s_codec_create_card,
16488b2840b6SIcenowy Zheng .reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31),
16498b2840b6SIcenowy Zheng .reg_dac_txdata = SUN8I_H3_CODEC_DAC_TXDATA,
16508b2840b6SIcenowy Zheng .reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA,
16518b2840b6SIcenowy Zheng .has_reset = true,
16528b2840b6SIcenowy Zheng };
16538b2840b6SIcenowy Zheng
16542f2a3462SChen-Yu Tsai static const struct of_device_id sun4i_codec_of_match[] = {
16552f2a3462SChen-Yu Tsai {
16562f2a3462SChen-Yu Tsai .compatible = "allwinner,sun4i-a10-codec",
16572f2a3462SChen-Yu Tsai .data = &sun4i_codec_quirks,
16582f2a3462SChen-Yu Tsai },
16592f2a3462SChen-Yu Tsai {
16608d9e4c9eSChen-Yu Tsai .compatible = "allwinner,sun6i-a31-codec",
16618d9e4c9eSChen-Yu Tsai .data = &sun6i_a31_codec_quirks,
16628d9e4c9eSChen-Yu Tsai },
16638d9e4c9eSChen-Yu Tsai {
16642f2a3462SChen-Yu Tsai .compatible = "allwinner,sun7i-a20-codec",
16652f2a3462SChen-Yu Tsai .data = &sun7i_codec_quirks,
16662f2a3462SChen-Yu Tsai },
1667dac5f86bSChen-Yu Tsai {
1668dac5f86bSChen-Yu Tsai .compatible = "allwinner,sun8i-a23-codec",
1669dac5f86bSChen-Yu Tsai .data = &sun8i_a23_codec_quirks,
1670dac5f86bSChen-Yu Tsai },
16714a15b24aSChen-Yu Tsai {
16724a15b24aSChen-Yu Tsai .compatible = "allwinner,sun8i-h3-codec",
16734a15b24aSChen-Yu Tsai .data = &sun8i_h3_codec_quirks,
16744a15b24aSChen-Yu Tsai },
16758b2840b6SIcenowy Zheng {
16768b2840b6SIcenowy Zheng .compatible = "allwinner,sun8i-v3s-codec",
16778b2840b6SIcenowy Zheng .data = &sun8i_v3s_codec_quirks,
16788b2840b6SIcenowy Zheng },
16792f2a3462SChen-Yu Tsai {}
16802f2a3462SChen-Yu Tsai };
16812f2a3462SChen-Yu Tsai MODULE_DEVICE_TABLE(of, sun4i_codec_of_match);
16822f2a3462SChen-Yu Tsai
sun4i_codec_probe(struct platform_device * pdev)168345fb6b6fSEmilio López static int sun4i_codec_probe(struct platform_device *pdev)
168445fb6b6fSEmilio López {
168545fb6b6fSEmilio López struct snd_soc_card *card;
168645fb6b6fSEmilio López struct sun4i_codec *scodec;
1687c1d5065aSDanny Milosavljevic const struct sun4i_codec_quirks *quirks;
168845fb6b6fSEmilio López struct resource *res;
168945fb6b6fSEmilio López void __iomem *base;
169045fb6b6fSEmilio López int ret;
169145fb6b6fSEmilio López
169245fb6b6fSEmilio López scodec = devm_kzalloc(&pdev->dev, sizeof(*scodec), GFP_KERNEL);
169345fb6b6fSEmilio López if (!scodec)
169445fb6b6fSEmilio López return -ENOMEM;
169545fb6b6fSEmilio López
169645fb6b6fSEmilio López scodec->dev = &pdev->dev;
169745fb6b6fSEmilio López
169837c617f1SYang Yingliang base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
1699a93799d5SMuhammad Usama Anjum if (IS_ERR(base))
170045fb6b6fSEmilio López return PTR_ERR(base);
170145fb6b6fSEmilio López
1702c1d5065aSDanny Milosavljevic quirks = of_device_get_match_data(&pdev->dev);
1703c1d5065aSDanny Milosavljevic if (quirks == NULL) {
1704c1d5065aSDanny Milosavljevic dev_err(&pdev->dev, "Failed to determine the quirks to use\n");
1705c1d5065aSDanny Milosavljevic return -ENODEV;
1706c1d5065aSDanny Milosavljevic }
1707c1d5065aSDanny Milosavljevic
170845fb6b6fSEmilio López scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base,
1709c1d5065aSDanny Milosavljevic quirks->regmap_config);
171045fb6b6fSEmilio López if (IS_ERR(scodec->regmap)) {
171145fb6b6fSEmilio López dev_err(&pdev->dev, "Failed to create our regmap\n");
171245fb6b6fSEmilio López return PTR_ERR(scodec->regmap);
171345fb6b6fSEmilio López }
171445fb6b6fSEmilio López
171545fb6b6fSEmilio López /* Get the clocks from the DT */
171645fb6b6fSEmilio López scodec->clk_apb = devm_clk_get(&pdev->dev, "apb");
171745fb6b6fSEmilio López if (IS_ERR(scodec->clk_apb)) {
171845fb6b6fSEmilio López dev_err(&pdev->dev, "Failed to get the APB clock\n");
171945fb6b6fSEmilio López return PTR_ERR(scodec->clk_apb);
172045fb6b6fSEmilio López }
172145fb6b6fSEmilio López
172245fb6b6fSEmilio López scodec->clk_module = devm_clk_get(&pdev->dev, "codec");
172345fb6b6fSEmilio López if (IS_ERR(scodec->clk_module)) {
172445fb6b6fSEmilio López dev_err(&pdev->dev, "Failed to get the module clock\n");
172545fb6b6fSEmilio López return PTR_ERR(scodec->clk_module);
172645fb6b6fSEmilio López }
172745fb6b6fSEmilio López
17289aead156SChen-Yu Tsai if (quirks->has_reset) {
172972bfa211SPhilipp Zabel scodec->rst = devm_reset_control_get_exclusive(&pdev->dev,
173072bfa211SPhilipp Zabel NULL);
17319aead156SChen-Yu Tsai if (IS_ERR(scodec->rst)) {
17329aead156SChen-Yu Tsai dev_err(&pdev->dev, "Failed to get reset control\n");
17339aead156SChen-Yu Tsai return PTR_ERR(scodec->rst);
17349aead156SChen-Yu Tsai }
173535db5762Skbuild test robot }
17369aead156SChen-Yu Tsai
173740592627SHans de Goede scodec->gpio_pa = devm_gpiod_get_optional(&pdev->dev, "allwinner,pa",
173840592627SHans de Goede GPIOD_OUT_LOW);
173940592627SHans de Goede if (IS_ERR(scodec->gpio_pa)) {
174040592627SHans de Goede ret = PTR_ERR(scodec->gpio_pa);
174111a95c58SKuninori Morimoto dev_err_probe(&pdev->dev, ret, "Failed to get pa gpio\n");
174240592627SHans de Goede return ret;
174340592627SHans de Goede }
174440592627SHans de Goede
1745bc03f0d5SChen-Yu Tsai /* reg_field setup */
1746bc03f0d5SChen-Yu Tsai scodec->reg_adc_fifoc = devm_regmap_field_alloc(&pdev->dev,
1747bc03f0d5SChen-Yu Tsai scodec->regmap,
1748bc03f0d5SChen-Yu Tsai quirks->reg_adc_fifoc);
1749bc03f0d5SChen-Yu Tsai if (IS_ERR(scodec->reg_adc_fifoc)) {
1750bc03f0d5SChen-Yu Tsai ret = PTR_ERR(scodec->reg_adc_fifoc);
1751bc03f0d5SChen-Yu Tsai dev_err(&pdev->dev, "Failed to create regmap fields: %d\n",
1752bc03f0d5SChen-Yu Tsai ret);
1753bc03f0d5SChen-Yu Tsai return ret;
1754bc03f0d5SChen-Yu Tsai }
1755bc03f0d5SChen-Yu Tsai
17563716a891SChen-Yu Tsai /* Enable the bus clock */
17573716a891SChen-Yu Tsai if (clk_prepare_enable(scodec->clk_apb)) {
17583716a891SChen-Yu Tsai dev_err(&pdev->dev, "Failed to enable the APB clock\n");
17593716a891SChen-Yu Tsai return -EINVAL;
17603716a891SChen-Yu Tsai }
17613716a891SChen-Yu Tsai
17629aead156SChen-Yu Tsai /* Deassert the reset control */
17639aead156SChen-Yu Tsai if (scodec->rst) {
17649aead156SChen-Yu Tsai ret = reset_control_deassert(scodec->rst);
17659aead156SChen-Yu Tsai if (ret) {
17669aead156SChen-Yu Tsai dev_err(&pdev->dev,
17679aead156SChen-Yu Tsai "Failed to deassert the reset control\n");
17689aead156SChen-Yu Tsai goto err_clk_disable;
17699aead156SChen-Yu Tsai }
17709aead156SChen-Yu Tsai }
17719aead156SChen-Yu Tsai
177245fb6b6fSEmilio López /* DMA configuration for TX FIFO */
1773bc03f0d5SChen-Yu Tsai scodec->playback_dma_data.addr = res->start + quirks->reg_dac_txdata;
1774730e2dd0SChen-Yu Tsai scodec->playback_dma_data.maxburst = 8;
177545fb6b6fSEmilio López scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
177645fb6b6fSEmilio López
17771fb34b48SMaxime Ripard /* DMA configuration for RX FIFO */
1778bc03f0d5SChen-Yu Tsai scodec->capture_dma_data.addr = res->start + quirks->reg_adc_rxdata;
1779730e2dd0SChen-Yu Tsai scodec->capture_dma_data.maxburst = 8;
17801fb34b48SMaxime Ripard scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
17811fb34b48SMaxime Ripard
178277f4be16SKuninori Morimoto ret = devm_snd_soc_register_component(&pdev->dev, quirks->codec,
178345fb6b6fSEmilio López &sun4i_codec_dai, 1);
178445fb6b6fSEmilio López if (ret) {
178545fb6b6fSEmilio López dev_err(&pdev->dev, "Failed to register our codec\n");
17869aead156SChen-Yu Tsai goto err_assert_reset;
178745fb6b6fSEmilio López }
178845fb6b6fSEmilio López
178945fb6b6fSEmilio López ret = devm_snd_soc_register_component(&pdev->dev,
179045fb6b6fSEmilio López &sun4i_codec_component,
179145fb6b6fSEmilio López &dummy_cpu_dai, 1);
179245fb6b6fSEmilio López if (ret) {
179345fb6b6fSEmilio López dev_err(&pdev->dev, "Failed to register our DAI\n");
179477f4be16SKuninori Morimoto goto err_assert_reset;
179545fb6b6fSEmilio López }
179645fb6b6fSEmilio López
179745fb6b6fSEmilio López ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
179845fb6b6fSEmilio López if (ret) {
179945fb6b6fSEmilio López dev_err(&pdev->dev, "Failed to register against DMAEngine\n");
180077f4be16SKuninori Morimoto goto err_assert_reset;
180145fb6b6fSEmilio López }
180245fb6b6fSEmilio López
1803bc03f0d5SChen-Yu Tsai card = quirks->create_card(&pdev->dev);
180485915b63SChen-Yu Tsai if (IS_ERR(card)) {
180585915b63SChen-Yu Tsai ret = PTR_ERR(card);
180645fb6b6fSEmilio López dev_err(&pdev->dev, "Failed to create our card\n");
180777f4be16SKuninori Morimoto goto err_assert_reset;
180845fb6b6fSEmilio López }
180945fb6b6fSEmilio López
181045fb6b6fSEmilio López snd_soc_card_set_drvdata(card, scodec);
181145fb6b6fSEmilio López
181245fb6b6fSEmilio López ret = snd_soc_register_card(card);
181345fb6b6fSEmilio López if (ret) {
181430248f61SMikhail Rudenko dev_err_probe(&pdev->dev, ret, "Failed to register our card\n");
181577f4be16SKuninori Morimoto goto err_assert_reset;
181645fb6b6fSEmilio López }
181745fb6b6fSEmilio López
181845fb6b6fSEmilio López return 0;
181945fb6b6fSEmilio López
18209aead156SChen-Yu Tsai err_assert_reset:
18219aead156SChen-Yu Tsai if (scodec->rst)
18229aead156SChen-Yu Tsai reset_control_assert(scodec->rst);
182345fb6b6fSEmilio López err_clk_disable:
182445fb6b6fSEmilio López clk_disable_unprepare(scodec->clk_apb);
182545fb6b6fSEmilio López return ret;
182645fb6b6fSEmilio López }
182745fb6b6fSEmilio López
sun4i_codec_remove(struct platform_device * pdev)1828f56a1b10SUwe Kleine-König static void sun4i_codec_remove(struct platform_device *pdev)
182945fb6b6fSEmilio López {
183045fb6b6fSEmilio López struct snd_soc_card *card = platform_get_drvdata(pdev);
183145fb6b6fSEmilio López struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card);
183245fb6b6fSEmilio López
183345fb6b6fSEmilio López snd_soc_unregister_card(card);
18349aead156SChen-Yu Tsai if (scodec->rst)
18359aead156SChen-Yu Tsai reset_control_assert(scodec->rst);
183645fb6b6fSEmilio López clk_disable_unprepare(scodec->clk_apb);
183745fb6b6fSEmilio López }
183845fb6b6fSEmilio López
183945fb6b6fSEmilio López static struct platform_driver sun4i_codec_driver = {
184045fb6b6fSEmilio López .driver = {
184145fb6b6fSEmilio López .name = "sun4i-codec",
184245fb6b6fSEmilio López .of_match_table = sun4i_codec_of_match,
184345fb6b6fSEmilio López },
184445fb6b6fSEmilio López .probe = sun4i_codec_probe,
1845f56a1b10SUwe Kleine-König .remove_new = sun4i_codec_remove,
184645fb6b6fSEmilio López };
184745fb6b6fSEmilio López module_platform_driver(sun4i_codec_driver);
184845fb6b6fSEmilio López
184945fb6b6fSEmilio López MODULE_DESCRIPTION("Allwinner A10 codec driver");
185045fb6b6fSEmilio López MODULE_AUTHOR("Emilio López <emilio@elopez.com.ar>");
185145fb6b6fSEmilio López MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>");
185245fb6b6fSEmilio López MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
1853bc03f0d5SChen-Yu Tsai MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
185445fb6b6fSEmilio López MODULE_LICENSE("GPL");
1855