18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright 2014 Emilio López <emilio@elopez.com.ar>
48c2ecf20Sopenharmony_ci * Copyright 2014 Jon Smirl <jonsmirl@gmail.com>
58c2ecf20Sopenharmony_ci * Copyright 2015 Maxime Ripard <maxime.ripard@free-electrons.com>
68c2ecf20Sopenharmony_ci * Copyright 2015 Adam Sampson <ats@offog.org>
78c2ecf20Sopenharmony_ci * Copyright 2016 Chen-Yu Tsai <wens@csie.org>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Based on the Allwinner SDK driver, released under the GPL.
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/init.h>
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
168c2ecf20Sopenharmony_ci#include <linux/delay.h>
178c2ecf20Sopenharmony_ci#include <linux/slab.h>
188c2ecf20Sopenharmony_ci#include <linux/of.h>
198c2ecf20Sopenharmony_ci#include <linux/of_address.h>
208c2ecf20Sopenharmony_ci#include <linux/of_device.h>
218c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
228c2ecf20Sopenharmony_ci#include <linux/clk.h>
238c2ecf20Sopenharmony_ci#include <linux/regmap.h>
248c2ecf20Sopenharmony_ci#include <linux/reset.h>
258c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include <sound/core.h>
288c2ecf20Sopenharmony_ci#include <sound/pcm.h>
298c2ecf20Sopenharmony_ci#include <sound/pcm_params.h>
308c2ecf20Sopenharmony_ci#include <sound/soc.h>
318c2ecf20Sopenharmony_ci#include <sound/tlv.h>
328c2ecf20Sopenharmony_ci#include <sound/initval.h>
338c2ecf20Sopenharmony_ci#include <sound/dmaengine_pcm.h>
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/* Codec DAC digital controls and FIFO registers */
368c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_DPC			(0x00)
378c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_DPC_EN_DA			(31)
388c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_DPC_DVOL			(12)
398c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_FIFOC			(0x04)
408c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_FIFOC_DAC_FS			(29)
418c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_FIFOC_FIR_VERSION		(28)
428c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_FIFOC_SEND_LASAT		(26)
438c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE		(24)
448c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT		(21)
458c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL		(8)
468c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_FIFOC_MONO_EN			(6)
478c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS		(5)
488c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN		(4)
498c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH		(0)
508c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_FIFOS			(0x08)
518c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_TXDATA			(0x0c)
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci/* Codec DAC side analog signal controls */
548c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL			(0x10)
558c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_DACAENR			(31)
568c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_DACAENL			(30)
578c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_MIXEN			(29)
588c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_LNG			(26)
598c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_FMG			(23)
608c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_MICG			(20)
618c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_LLNS			(19)
628c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_RLNS			(18)
638c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_LFMS			(17)
648c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_RFMS			(16)
658c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_LDACLMIXS			(15)
668c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_RDACRMIXS			(14)
678c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_LDACRMIXS			(13)
688c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_MIC1LS			(12)
698c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_MIC1RS			(11)
708c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_MIC2LS			(10)
718c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_MIC2RS			(9)
728c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_DACPAS			(8)
738c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_MIXPAS			(7)
748c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_PA_MUTE			(6)
758c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_PA_VOL			(0)
768c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_TUNE			(0x14)
778c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_DEBUG			(0x18)
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci/* Codec ADC digital controls and FIFO registers */
808c2ecf20Sopenharmony_ci#define SUN4I_CODEC_ADC_FIFOC			(0x1c)
818c2ecf20Sopenharmony_ci#define SUN4I_CODEC_ADC_FIFOC_ADC_FS			(29)
828c2ecf20Sopenharmony_ci#define SUN4I_CODEC_ADC_FIFOC_EN_AD			(28)
838c2ecf20Sopenharmony_ci#define SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE		(24)
848c2ecf20Sopenharmony_ci#define SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL		(8)
858c2ecf20Sopenharmony_ci#define SUN4I_CODEC_ADC_FIFOC_MONO_EN			(7)
868c2ecf20Sopenharmony_ci#define SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS		(6)
878c2ecf20Sopenharmony_ci#define SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN		(4)
888c2ecf20Sopenharmony_ci#define SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH		(0)
898c2ecf20Sopenharmony_ci#define SUN4I_CODEC_ADC_FIFOS			(0x20)
908c2ecf20Sopenharmony_ci#define SUN4I_CODEC_ADC_RXDATA			(0x24)
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci/* Codec ADC side analog signal controls */
938c2ecf20Sopenharmony_ci#define SUN4I_CODEC_ADC_ACTL			(0x28)
948c2ecf20Sopenharmony_ci#define SUN4I_CODEC_ADC_ACTL_ADC_R_EN			(31)
958c2ecf20Sopenharmony_ci#define SUN4I_CODEC_ADC_ACTL_ADC_L_EN			(30)
968c2ecf20Sopenharmony_ci#define SUN4I_CODEC_ADC_ACTL_PREG1EN			(29)
978c2ecf20Sopenharmony_ci#define SUN4I_CODEC_ADC_ACTL_PREG2EN			(28)
988c2ecf20Sopenharmony_ci#define SUN4I_CODEC_ADC_ACTL_VMICEN			(27)
998c2ecf20Sopenharmony_ci#define SUN4I_CODEC_ADC_ACTL_PREG1			(25)
1008c2ecf20Sopenharmony_ci#define SUN4I_CODEC_ADC_ACTL_PREG2			(23)
1018c2ecf20Sopenharmony_ci#define SUN4I_CODEC_ADC_ACTL_VADCG			(20)
1028c2ecf20Sopenharmony_ci#define SUN4I_CODEC_ADC_ACTL_ADCIS			(17)
1038c2ecf20Sopenharmony_ci#define SUN4I_CODEC_ADC_ACTL_LNPREG			(13)
1048c2ecf20Sopenharmony_ci#define SUN4I_CODEC_ADC_ACTL_PA_EN			(4)
1058c2ecf20Sopenharmony_ci#define SUN4I_CODEC_ADC_ACTL_DDE			(3)
1068c2ecf20Sopenharmony_ci#define SUN4I_CODEC_ADC_DEBUG			(0x2c)
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci/* FIFO counters */
1098c2ecf20Sopenharmony_ci#define SUN4I_CODEC_DAC_TXCNT			(0x30)
1108c2ecf20Sopenharmony_ci#define SUN4I_CODEC_ADC_RXCNT			(0x34)
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci/* Calibration register (sun7i only) */
1138c2ecf20Sopenharmony_ci#define SUN7I_CODEC_AC_DAC_CAL			(0x38)
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci/* Microphone controls (sun7i only) */
1168c2ecf20Sopenharmony_ci#define SUN7I_CODEC_AC_MIC_PHONE_CAL		(0x3c)
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci#define SUN7I_CODEC_AC_MIC_PHONE_CAL_PREG1		(29)
1198c2ecf20Sopenharmony_ci#define SUN7I_CODEC_AC_MIC_PHONE_CAL_PREG2		(26)
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci/*
1228c2ecf20Sopenharmony_ci * sun6i specific registers
1238c2ecf20Sopenharmony_ci *
1248c2ecf20Sopenharmony_ci * sun6i shares the same digital control and FIFO registers as sun4i,
1258c2ecf20Sopenharmony_ci * but only the DAC digital controls are at the same offset. The others
1268c2ecf20Sopenharmony_ci * have been moved around to accommodate extra analog controls.
1278c2ecf20Sopenharmony_ci */
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci/* Codec DAC digital controls and FIFO registers */
1308c2ecf20Sopenharmony_ci#define SUN6I_CODEC_ADC_FIFOC			(0x10)
1318c2ecf20Sopenharmony_ci#define SUN6I_CODEC_ADC_FIFOC_EN_AD			(28)
1328c2ecf20Sopenharmony_ci#define SUN6I_CODEC_ADC_FIFOS			(0x14)
1338c2ecf20Sopenharmony_ci#define SUN6I_CODEC_ADC_RXDATA			(0x18)
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci/* Output mixer and gain controls */
1368c2ecf20Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL		(0x20)
1378c2ecf20Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_DACAREN		(31)
1388c2ecf20Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_DACALEN		(30)
1398c2ecf20Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_RMIXEN			(29)
1408c2ecf20Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_LMIXEN			(28)
1418c2ecf20Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC1		(23)
1428c2ecf20Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC2		(22)
1438c2ecf20Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_PHONE		(21)
1448c2ecf20Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_PHONEP		(20)
1458c2ecf20Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_LINEINR		(19)
1468c2ecf20Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACR		(18)
1478c2ecf20Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACL		(17)
1488c2ecf20Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC1		(16)
1498c2ecf20Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC2		(15)
1508c2ecf20Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_PHONE		(14)
1518c2ecf20Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_PHONEN		(13)
1528c2ecf20Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_LINEINL		(12)
1538c2ecf20Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACL		(11)
1548c2ecf20Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACR		(10)
1558c2ecf20Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_RHPIS			(9)
1568c2ecf20Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_LHPIS			(8)
1578c2ecf20Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE		(7)
1588c2ecf20Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE		(6)
1598c2ecf20Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_HPVOL			(0)
1608c2ecf20Sopenharmony_ci#define SUN6I_CODEC_OM_PA_CTRL			(0x24)
1618c2ecf20Sopenharmony_ci#define SUN6I_CODEC_OM_PA_CTRL_HPPAEN			(31)
1628c2ecf20Sopenharmony_ci#define SUN6I_CODEC_OM_PA_CTRL_HPCOM_CTL		(29)
1638c2ecf20Sopenharmony_ci#define SUN6I_CODEC_OM_PA_CTRL_COMPTEN			(28)
1648c2ecf20Sopenharmony_ci#define SUN6I_CODEC_OM_PA_CTRL_MIC1G			(15)
1658c2ecf20Sopenharmony_ci#define SUN6I_CODEC_OM_PA_CTRL_MIC2G			(12)
1668c2ecf20Sopenharmony_ci#define SUN6I_CODEC_OM_PA_CTRL_LINEING			(9)
1678c2ecf20Sopenharmony_ci#define SUN6I_CODEC_OM_PA_CTRL_PHONEG			(6)
1688c2ecf20Sopenharmony_ci#define SUN6I_CODEC_OM_PA_CTRL_PHONEPG			(3)
1698c2ecf20Sopenharmony_ci#define SUN6I_CODEC_OM_PA_CTRL_PHONENG			(0)
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci/* Microphone, line out and phone out controls */
1728c2ecf20Sopenharmony_ci#define SUN6I_CODEC_MIC_CTRL			(0x28)
1738c2ecf20Sopenharmony_ci#define SUN6I_CODEC_MIC_CTRL_HBIASEN			(31)
1748c2ecf20Sopenharmony_ci#define SUN6I_CODEC_MIC_CTRL_MBIASEN			(30)
1758c2ecf20Sopenharmony_ci#define SUN6I_CODEC_MIC_CTRL_MIC1AMPEN			(28)
1768c2ecf20Sopenharmony_ci#define SUN6I_CODEC_MIC_CTRL_MIC1BOOST			(25)
1778c2ecf20Sopenharmony_ci#define SUN6I_CODEC_MIC_CTRL_MIC2AMPEN			(24)
1788c2ecf20Sopenharmony_ci#define SUN6I_CODEC_MIC_CTRL_MIC2BOOST			(21)
1798c2ecf20Sopenharmony_ci#define SUN6I_CODEC_MIC_CTRL_MIC2SLT			(20)
1808c2ecf20Sopenharmony_ci#define SUN6I_CODEC_MIC_CTRL_LINEOUTLEN			(19)
1818c2ecf20Sopenharmony_ci#define SUN6I_CODEC_MIC_CTRL_LINEOUTREN			(18)
1828c2ecf20Sopenharmony_ci#define SUN6I_CODEC_MIC_CTRL_LINEOUTLSRC		(17)
1838c2ecf20Sopenharmony_ci#define SUN6I_CODEC_MIC_CTRL_LINEOUTRSRC		(16)
1848c2ecf20Sopenharmony_ci#define SUN6I_CODEC_MIC_CTRL_LINEOUTVC			(11)
1858c2ecf20Sopenharmony_ci#define SUN6I_CODEC_MIC_CTRL_PHONEPREG			(8)
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci/* ADC mixer controls */
1888c2ecf20Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL			(0x2c)
1898c2ecf20Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_ADCREN			(31)
1908c2ecf20Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_ADCLEN			(30)
1918c2ecf20Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_ADCRG			(27)
1928c2ecf20Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_ADCLG			(24)
1938c2ecf20Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC1		(13)
1948c2ecf20Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC2		(12)
1958c2ecf20Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_RADCMIX_PHONE		(11)
1968c2ecf20Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_RADCMIX_PHONEP		(10)
1978c2ecf20Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_RADCMIX_LINEINR		(9)
1988c2ecf20Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXR		(8)
1998c2ecf20Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXL		(7)
2008c2ecf20Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC1		(6)
2018c2ecf20Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC2		(5)
2028c2ecf20Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_LADCMIX_PHONE		(4)
2038c2ecf20Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_LADCMIX_PHONEN		(3)
2048c2ecf20Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_LADCMIX_LINEINL		(2)
2058c2ecf20Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXL		(1)
2068c2ecf20Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXR		(0)
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci/* Analog performance tuning controls */
2098c2ecf20Sopenharmony_ci#define SUN6I_CODEC_ADDA_TUNE			(0x30)
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci/* Calibration controls */
2128c2ecf20Sopenharmony_ci#define SUN6I_CODEC_CALIBRATION			(0x34)
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci/* FIFO counters */
2158c2ecf20Sopenharmony_ci#define SUN6I_CODEC_DAC_TXCNT			(0x40)
2168c2ecf20Sopenharmony_ci#define SUN6I_CODEC_ADC_RXCNT			(0x44)
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci/* headset jack detection and button support registers */
2198c2ecf20Sopenharmony_ci#define SUN6I_CODEC_HMIC_CTL			(0x50)
2208c2ecf20Sopenharmony_ci#define SUN6I_CODEC_HMIC_DATA			(0x54)
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci/* TODO sun6i DAP (Digital Audio Processing) bits */
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci/* FIFO counters moved on A23 */
2258c2ecf20Sopenharmony_ci#define SUN8I_A23_CODEC_DAC_TXCNT		(0x1c)
2268c2ecf20Sopenharmony_ci#define SUN8I_A23_CODEC_ADC_RXCNT		(0x20)
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci/* TX FIFO moved on H3 */
2298c2ecf20Sopenharmony_ci#define SUN8I_H3_CODEC_DAC_TXDATA		(0x20)
2308c2ecf20Sopenharmony_ci#define SUN8I_H3_CODEC_DAC_DBG			(0x48)
2318c2ecf20Sopenharmony_ci#define SUN8I_H3_CODEC_ADC_DBG			(0x4c)
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci/* TODO H3 DAP (Digital Audio Processing) bits */
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cistruct sun4i_codec {
2368c2ecf20Sopenharmony_ci	struct device	*dev;
2378c2ecf20Sopenharmony_ci	struct regmap	*regmap;
2388c2ecf20Sopenharmony_ci	struct clk	*clk_apb;
2398c2ecf20Sopenharmony_ci	struct clk	*clk_module;
2408c2ecf20Sopenharmony_ci	struct reset_control *rst;
2418c2ecf20Sopenharmony_ci	struct gpio_desc *gpio_pa;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	/* ADC_FIFOC register is at different offset on different SoCs */
2448c2ecf20Sopenharmony_ci	struct regmap_field *reg_adc_fifoc;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	struct snd_dmaengine_dai_dma_data	capture_dma_data;
2478c2ecf20Sopenharmony_ci	struct snd_dmaengine_dai_dma_data	playback_dma_data;
2488c2ecf20Sopenharmony_ci};
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_cistatic void sun4i_codec_start_playback(struct sun4i_codec *scodec)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	/* Flush TX FIFO */
2538c2ecf20Sopenharmony_ci	regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
2548c2ecf20Sopenharmony_ci			   BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH),
2558c2ecf20Sopenharmony_ci			   BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH));
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	/* Enable DAC DRQ */
2588c2ecf20Sopenharmony_ci	regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
2598c2ecf20Sopenharmony_ci			   BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN),
2608c2ecf20Sopenharmony_ci			   BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN));
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_cistatic void sun4i_codec_stop_playback(struct sun4i_codec *scodec)
2648c2ecf20Sopenharmony_ci{
2658c2ecf20Sopenharmony_ci	/* Disable DAC DRQ */
2668c2ecf20Sopenharmony_ci	regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
2678c2ecf20Sopenharmony_ci			   BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN),
2688c2ecf20Sopenharmony_ci			   0);
2698c2ecf20Sopenharmony_ci}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic void sun4i_codec_start_capture(struct sun4i_codec *scodec)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	/* Enable ADC DRQ */
2748c2ecf20Sopenharmony_ci	regmap_field_update_bits(scodec->reg_adc_fifoc,
2758c2ecf20Sopenharmony_ci				 BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN),
2768c2ecf20Sopenharmony_ci				 BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN));
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_cistatic void sun4i_codec_stop_capture(struct sun4i_codec *scodec)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	/* Disable ADC DRQ */
2828c2ecf20Sopenharmony_ci	regmap_field_update_bits(scodec->reg_adc_fifoc,
2838c2ecf20Sopenharmony_ci				 BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN), 0);
2848c2ecf20Sopenharmony_ci}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_cistatic int sun4i_codec_trigger(struct snd_pcm_substream *substream, int cmd,
2878c2ecf20Sopenharmony_ci			       struct snd_soc_dai *dai)
2888c2ecf20Sopenharmony_ci{
2898c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
2908c2ecf20Sopenharmony_ci	struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	switch (cmd) {
2938c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
2948c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
2958c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
2968c2ecf20Sopenharmony_ci		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
2978c2ecf20Sopenharmony_ci			sun4i_codec_start_playback(scodec);
2988c2ecf20Sopenharmony_ci		else
2998c2ecf20Sopenharmony_ci			sun4i_codec_start_capture(scodec);
3008c2ecf20Sopenharmony_ci		break;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
3038c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
3048c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
3058c2ecf20Sopenharmony_ci		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
3068c2ecf20Sopenharmony_ci			sun4i_codec_stop_playback(scodec);
3078c2ecf20Sopenharmony_ci		else
3088c2ecf20Sopenharmony_ci			sun4i_codec_stop_capture(scodec);
3098c2ecf20Sopenharmony_ci		break;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	default:
3128c2ecf20Sopenharmony_ci		return -EINVAL;
3138c2ecf20Sopenharmony_ci	}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	return 0;
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_cistatic int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream,
3198c2ecf20Sopenharmony_ci				       struct snd_soc_dai *dai)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
3228c2ecf20Sopenharmony_ci	struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	/* Flush RX FIFO */
3268c2ecf20Sopenharmony_ci	regmap_field_update_bits(scodec->reg_adc_fifoc,
3278c2ecf20Sopenharmony_ci				 BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH),
3288c2ecf20Sopenharmony_ci				 BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH));
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	/* Set RX FIFO trigger level */
3328c2ecf20Sopenharmony_ci	regmap_field_update_bits(scodec->reg_adc_fifoc,
3338c2ecf20Sopenharmony_ci				 0xf << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL,
3348c2ecf20Sopenharmony_ci				 0x7 << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	/*
3378c2ecf20Sopenharmony_ci	 * FIXME: Undocumented in the datasheet, but
3388c2ecf20Sopenharmony_ci	 *        Allwinner's code mentions that it is
3398c2ecf20Sopenharmony_ci	 *        related to microphone gain
3408c2ecf20Sopenharmony_ci	 */
3418c2ecf20Sopenharmony_ci	if (of_device_is_compatible(scodec->dev->of_node,
3428c2ecf20Sopenharmony_ci				    "allwinner,sun4i-a10-codec") ||
3438c2ecf20Sopenharmony_ci	    of_device_is_compatible(scodec->dev->of_node,
3448c2ecf20Sopenharmony_ci				    "allwinner,sun7i-a20-codec")) {
3458c2ecf20Sopenharmony_ci		regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_ACTL,
3468c2ecf20Sopenharmony_ci				   0x3 << 25,
3478c2ecf20Sopenharmony_ci				   0x1 << 25);
3488c2ecf20Sopenharmony_ci	}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	if (of_device_is_compatible(scodec->dev->of_node,
3518c2ecf20Sopenharmony_ci				    "allwinner,sun7i-a20-codec"))
3528c2ecf20Sopenharmony_ci		/* FIXME: Undocumented bits */
3538c2ecf20Sopenharmony_ci		regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_TUNE,
3548c2ecf20Sopenharmony_ci				   0x3 << 8,
3558c2ecf20Sopenharmony_ci				   0x1 << 8);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	return 0;
3588c2ecf20Sopenharmony_ci}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic int sun4i_codec_prepare_playback(struct snd_pcm_substream *substream,
3618c2ecf20Sopenharmony_ci					struct snd_soc_dai *dai)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
3648c2ecf20Sopenharmony_ci	struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
3658c2ecf20Sopenharmony_ci	u32 val;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	/* Flush the TX FIFO */
3688c2ecf20Sopenharmony_ci	regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
3698c2ecf20Sopenharmony_ci			   BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH),
3708c2ecf20Sopenharmony_ci			   BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH));
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	/* Set TX FIFO Empty Trigger Level */
3738c2ecf20Sopenharmony_ci	regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
3748c2ecf20Sopenharmony_ci			   0x3f << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL,
3758c2ecf20Sopenharmony_ci			   0xf << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL);
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	if (substream->runtime->rate > 32000)
3788c2ecf20Sopenharmony_ci		/* Use 64 bits FIR filter */
3798c2ecf20Sopenharmony_ci		val = 0;
3808c2ecf20Sopenharmony_ci	else
3818c2ecf20Sopenharmony_ci		/* Use 32 bits FIR filter */
3828c2ecf20Sopenharmony_ci		val = BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
3858c2ecf20Sopenharmony_ci			   BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION),
3868c2ecf20Sopenharmony_ci			   val);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	/* Send zeros when we have an underrun */
3898c2ecf20Sopenharmony_ci	regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
3908c2ecf20Sopenharmony_ci			   BIT(SUN4I_CODEC_DAC_FIFOC_SEND_LASAT),
3918c2ecf20Sopenharmony_ci			   0);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	return 0;
3948c2ecf20Sopenharmony_ci};
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cistatic int sun4i_codec_prepare(struct snd_pcm_substream *substream,
3978c2ecf20Sopenharmony_ci			       struct snd_soc_dai *dai)
3988c2ecf20Sopenharmony_ci{
3998c2ecf20Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
4008c2ecf20Sopenharmony_ci		return sun4i_codec_prepare_playback(substream, dai);
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	return sun4i_codec_prepare_capture(substream, dai);
4038c2ecf20Sopenharmony_ci}
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_cistatic unsigned long sun4i_codec_get_mod_freq(struct snd_pcm_hw_params *params)
4068c2ecf20Sopenharmony_ci{
4078c2ecf20Sopenharmony_ci	unsigned int rate = params_rate(params);
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	switch (rate) {
4108c2ecf20Sopenharmony_ci	case 176400:
4118c2ecf20Sopenharmony_ci	case 88200:
4128c2ecf20Sopenharmony_ci	case 44100:
4138c2ecf20Sopenharmony_ci	case 33075:
4148c2ecf20Sopenharmony_ci	case 22050:
4158c2ecf20Sopenharmony_ci	case 14700:
4168c2ecf20Sopenharmony_ci	case 11025:
4178c2ecf20Sopenharmony_ci	case 7350:
4188c2ecf20Sopenharmony_ci		return 22579200;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	case 192000:
4218c2ecf20Sopenharmony_ci	case 96000:
4228c2ecf20Sopenharmony_ci	case 48000:
4238c2ecf20Sopenharmony_ci	case 32000:
4248c2ecf20Sopenharmony_ci	case 24000:
4258c2ecf20Sopenharmony_ci	case 16000:
4268c2ecf20Sopenharmony_ci	case 12000:
4278c2ecf20Sopenharmony_ci	case 8000:
4288c2ecf20Sopenharmony_ci		return 24576000;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	default:
4318c2ecf20Sopenharmony_ci		return 0;
4328c2ecf20Sopenharmony_ci	}
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic int sun4i_codec_get_hw_rate(struct snd_pcm_hw_params *params)
4368c2ecf20Sopenharmony_ci{
4378c2ecf20Sopenharmony_ci	unsigned int rate = params_rate(params);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	switch (rate) {
4408c2ecf20Sopenharmony_ci	case 192000:
4418c2ecf20Sopenharmony_ci	case 176400:
4428c2ecf20Sopenharmony_ci		return 6;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	case 96000:
4458c2ecf20Sopenharmony_ci	case 88200:
4468c2ecf20Sopenharmony_ci		return 7;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	case 48000:
4498c2ecf20Sopenharmony_ci	case 44100:
4508c2ecf20Sopenharmony_ci		return 0;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	case 32000:
4538c2ecf20Sopenharmony_ci	case 33075:
4548c2ecf20Sopenharmony_ci		return 1;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	case 24000:
4578c2ecf20Sopenharmony_ci	case 22050:
4588c2ecf20Sopenharmony_ci		return 2;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	case 16000:
4618c2ecf20Sopenharmony_ci	case 14700:
4628c2ecf20Sopenharmony_ci		return 3;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	case 12000:
4658c2ecf20Sopenharmony_ci	case 11025:
4668c2ecf20Sopenharmony_ci		return 4;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	case 8000:
4698c2ecf20Sopenharmony_ci	case 7350:
4708c2ecf20Sopenharmony_ci		return 5;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	default:
4738c2ecf20Sopenharmony_ci		return -EINVAL;
4748c2ecf20Sopenharmony_ci	}
4758c2ecf20Sopenharmony_ci}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_cistatic int sun4i_codec_hw_params_capture(struct sun4i_codec *scodec,
4788c2ecf20Sopenharmony_ci					 struct snd_pcm_hw_params *params,
4798c2ecf20Sopenharmony_ci					 unsigned int hwrate)
4808c2ecf20Sopenharmony_ci{
4818c2ecf20Sopenharmony_ci	/* Set ADC sample rate */
4828c2ecf20Sopenharmony_ci	regmap_field_update_bits(scodec->reg_adc_fifoc,
4838c2ecf20Sopenharmony_ci				 7 << SUN4I_CODEC_ADC_FIFOC_ADC_FS,
4848c2ecf20Sopenharmony_ci				 hwrate << SUN4I_CODEC_ADC_FIFOC_ADC_FS);
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	/* Set the number of channels we want to use */
4878c2ecf20Sopenharmony_ci	if (params_channels(params) == 1)
4888c2ecf20Sopenharmony_ci		regmap_field_update_bits(scodec->reg_adc_fifoc,
4898c2ecf20Sopenharmony_ci					 BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN),
4908c2ecf20Sopenharmony_ci					 BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN));
4918c2ecf20Sopenharmony_ci	else
4928c2ecf20Sopenharmony_ci		regmap_field_update_bits(scodec->reg_adc_fifoc,
4938c2ecf20Sopenharmony_ci					 BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN),
4948c2ecf20Sopenharmony_ci					 0);
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	/* Set the number of sample bits to either 16 or 24 bits */
4978c2ecf20Sopenharmony_ci	if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min == 32) {
4988c2ecf20Sopenharmony_ci		regmap_field_update_bits(scodec->reg_adc_fifoc,
4998c2ecf20Sopenharmony_ci				   BIT(SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS),
5008c2ecf20Sopenharmony_ci				   BIT(SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS));
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci		regmap_field_update_bits(scodec->reg_adc_fifoc,
5038c2ecf20Sopenharmony_ci				   BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE),
5048c2ecf20Sopenharmony_ci				   0);
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci		scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
5078c2ecf20Sopenharmony_ci	} else {
5088c2ecf20Sopenharmony_ci		regmap_field_update_bits(scodec->reg_adc_fifoc,
5098c2ecf20Sopenharmony_ci				   BIT(SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS),
5108c2ecf20Sopenharmony_ci				   0);
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci		/* Fill most significant bits with valid data MSB */
5138c2ecf20Sopenharmony_ci		regmap_field_update_bits(scodec->reg_adc_fifoc,
5148c2ecf20Sopenharmony_ci				   BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE),
5158c2ecf20Sopenharmony_ci				   BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE));
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci		scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
5188c2ecf20Sopenharmony_ci	}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	return 0;
5218c2ecf20Sopenharmony_ci}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_cistatic int sun4i_codec_hw_params_playback(struct sun4i_codec *scodec,
5248c2ecf20Sopenharmony_ci					  struct snd_pcm_hw_params *params,
5258c2ecf20Sopenharmony_ci					  unsigned int hwrate)
5268c2ecf20Sopenharmony_ci{
5278c2ecf20Sopenharmony_ci	u32 val;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	/* Set DAC sample rate */
5308c2ecf20Sopenharmony_ci	regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
5318c2ecf20Sopenharmony_ci			   7 << SUN4I_CODEC_DAC_FIFOC_DAC_FS,
5328c2ecf20Sopenharmony_ci			   hwrate << SUN4I_CODEC_DAC_FIFOC_DAC_FS);
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	/* Set the number of channels we want to use */
5358c2ecf20Sopenharmony_ci	if (params_channels(params) == 1)
5368c2ecf20Sopenharmony_ci		val = BIT(SUN4I_CODEC_DAC_FIFOC_MONO_EN);
5378c2ecf20Sopenharmony_ci	else
5388c2ecf20Sopenharmony_ci		val = 0;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
5418c2ecf20Sopenharmony_ci			   BIT(SUN4I_CODEC_DAC_FIFOC_MONO_EN),
5428c2ecf20Sopenharmony_ci			   val);
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	/* Set the number of sample bits to either 16 or 24 bits */
5458c2ecf20Sopenharmony_ci	if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min == 32) {
5468c2ecf20Sopenharmony_ci		regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
5478c2ecf20Sopenharmony_ci				   BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS),
5488c2ecf20Sopenharmony_ci				   BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS));
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci		/* Set TX FIFO mode to padding the LSBs with 0 */
5518c2ecf20Sopenharmony_ci		regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
5528c2ecf20Sopenharmony_ci				   BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE),
5538c2ecf20Sopenharmony_ci				   0);
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci		scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
5568c2ecf20Sopenharmony_ci	} else {
5578c2ecf20Sopenharmony_ci		regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
5588c2ecf20Sopenharmony_ci				   BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS),
5598c2ecf20Sopenharmony_ci				   0);
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci		/* Set TX FIFO mode to repeat the MSB */
5628c2ecf20Sopenharmony_ci		regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
5638c2ecf20Sopenharmony_ci				   BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE),
5648c2ecf20Sopenharmony_ci				   BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE));
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci		scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
5678c2ecf20Sopenharmony_ci	}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	return 0;
5708c2ecf20Sopenharmony_ci}
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_cistatic int sun4i_codec_hw_params(struct snd_pcm_substream *substream,
5738c2ecf20Sopenharmony_ci				 struct snd_pcm_hw_params *params,
5748c2ecf20Sopenharmony_ci				 struct snd_soc_dai *dai)
5758c2ecf20Sopenharmony_ci{
5768c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
5778c2ecf20Sopenharmony_ci	struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
5788c2ecf20Sopenharmony_ci	unsigned long clk_freq;
5798c2ecf20Sopenharmony_ci	int ret, hwrate;
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	clk_freq = sun4i_codec_get_mod_freq(params);
5828c2ecf20Sopenharmony_ci	if (!clk_freq)
5838c2ecf20Sopenharmony_ci		return -EINVAL;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	ret = clk_set_rate(scodec->clk_module, clk_freq);
5868c2ecf20Sopenharmony_ci	if (ret)
5878c2ecf20Sopenharmony_ci		return ret;
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	hwrate = sun4i_codec_get_hw_rate(params);
5908c2ecf20Sopenharmony_ci	if (hwrate < 0)
5918c2ecf20Sopenharmony_ci		return hwrate;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
5948c2ecf20Sopenharmony_ci		return sun4i_codec_hw_params_playback(scodec, params,
5958c2ecf20Sopenharmony_ci						      hwrate);
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	return sun4i_codec_hw_params_capture(scodec, params,
5988c2ecf20Sopenharmony_ci					     hwrate);
5998c2ecf20Sopenharmony_ci}
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_cistatic unsigned int sun4i_codec_src_rates[] = {
6038c2ecf20Sopenharmony_ci	8000, 11025, 12000, 16000, 22050, 24000, 32000,
6048c2ecf20Sopenharmony_ci	44100, 48000, 96000, 192000
6058c2ecf20Sopenharmony_ci};
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_cistatic struct snd_pcm_hw_constraint_list sun4i_codec_constraints = {
6098c2ecf20Sopenharmony_ci	.count  = ARRAY_SIZE(sun4i_codec_src_rates),
6108c2ecf20Sopenharmony_ci	.list   = sun4i_codec_src_rates,
6118c2ecf20Sopenharmony_ci};
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_cistatic int sun4i_codec_startup(struct snd_pcm_substream *substream,
6158c2ecf20Sopenharmony_ci			       struct snd_soc_dai *dai)
6168c2ecf20Sopenharmony_ci{
6178c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
6188c2ecf20Sopenharmony_ci	struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	snd_pcm_hw_constraint_list(substream->runtime, 0,
6218c2ecf20Sopenharmony_ci				SNDRV_PCM_HW_PARAM_RATE, &sun4i_codec_constraints);
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	/*
6248c2ecf20Sopenharmony_ci	 * Stop issuing DRQ when we have room for less than 16 samples
6258c2ecf20Sopenharmony_ci	 * in our TX FIFO
6268c2ecf20Sopenharmony_ci	 */
6278c2ecf20Sopenharmony_ci	regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
6288c2ecf20Sopenharmony_ci			   3 << SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT,
6298c2ecf20Sopenharmony_ci			   3 << SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT);
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	return clk_prepare_enable(scodec->clk_module);
6328c2ecf20Sopenharmony_ci}
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_cistatic void sun4i_codec_shutdown(struct snd_pcm_substream *substream,
6358c2ecf20Sopenharmony_ci				 struct snd_soc_dai *dai)
6368c2ecf20Sopenharmony_ci{
6378c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
6388c2ecf20Sopenharmony_ci	struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	clk_disable_unprepare(scodec->clk_module);
6418c2ecf20Sopenharmony_ci}
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops sun4i_codec_dai_ops = {
6448c2ecf20Sopenharmony_ci	.startup	= sun4i_codec_startup,
6458c2ecf20Sopenharmony_ci	.shutdown	= sun4i_codec_shutdown,
6468c2ecf20Sopenharmony_ci	.trigger	= sun4i_codec_trigger,
6478c2ecf20Sopenharmony_ci	.hw_params	= sun4i_codec_hw_params,
6488c2ecf20Sopenharmony_ci	.prepare	= sun4i_codec_prepare,
6498c2ecf20Sopenharmony_ci};
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver sun4i_codec_dai = {
6528c2ecf20Sopenharmony_ci	.name	= "Codec",
6538c2ecf20Sopenharmony_ci	.ops	= &sun4i_codec_dai_ops,
6548c2ecf20Sopenharmony_ci	.playback = {
6558c2ecf20Sopenharmony_ci		.stream_name	= "Codec Playback",
6568c2ecf20Sopenharmony_ci		.channels_min	= 1,
6578c2ecf20Sopenharmony_ci		.channels_max	= 2,
6588c2ecf20Sopenharmony_ci		.rate_min	= 8000,
6598c2ecf20Sopenharmony_ci		.rate_max	= 192000,
6608c2ecf20Sopenharmony_ci		.rates		= SNDRV_PCM_RATE_CONTINUOUS,
6618c2ecf20Sopenharmony_ci		.formats	= SNDRV_PCM_FMTBIT_S16_LE |
6628c2ecf20Sopenharmony_ci				  SNDRV_PCM_FMTBIT_S32_LE,
6638c2ecf20Sopenharmony_ci		.sig_bits	= 24,
6648c2ecf20Sopenharmony_ci	},
6658c2ecf20Sopenharmony_ci	.capture = {
6668c2ecf20Sopenharmony_ci		.stream_name	= "Codec Capture",
6678c2ecf20Sopenharmony_ci		.channels_min	= 1,
6688c2ecf20Sopenharmony_ci		.channels_max	= 2,
6698c2ecf20Sopenharmony_ci		.rate_min	= 8000,
6708c2ecf20Sopenharmony_ci		.rate_max	= 48000,
6718c2ecf20Sopenharmony_ci		.rates		= SNDRV_PCM_RATE_CONTINUOUS,
6728c2ecf20Sopenharmony_ci		.formats	= SNDRV_PCM_FMTBIT_S16_LE |
6738c2ecf20Sopenharmony_ci				  SNDRV_PCM_FMTBIT_S32_LE,
6748c2ecf20Sopenharmony_ci		.sig_bits	= 24,
6758c2ecf20Sopenharmony_ci	},
6768c2ecf20Sopenharmony_ci};
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci/*** sun4i Codec ***/
6798c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new sun4i_codec_pa_mute =
6808c2ecf20Sopenharmony_ci	SOC_DAPM_SINGLE("Switch", SUN4I_CODEC_DAC_ACTL,
6818c2ecf20Sopenharmony_ci			SUN4I_CODEC_DAC_ACTL_PA_MUTE, 1, 0);
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_cistatic DECLARE_TLV_DB_SCALE(sun4i_codec_pa_volume_scale, -6300, 100, 1);
6848c2ecf20Sopenharmony_cistatic DECLARE_TLV_DB_SCALE(sun4i_codec_linein_loopback_gain_scale, -150, 150,
6858c2ecf20Sopenharmony_ci			    0);
6868c2ecf20Sopenharmony_cistatic DECLARE_TLV_DB_SCALE(sun4i_codec_linein_preamp_gain_scale, -1200, 300,
6878c2ecf20Sopenharmony_ci			    0);
6888c2ecf20Sopenharmony_cistatic DECLARE_TLV_DB_SCALE(sun4i_codec_fmin_loopback_gain_scale, -450, 150,
6898c2ecf20Sopenharmony_ci			    0);
6908c2ecf20Sopenharmony_cistatic DECLARE_TLV_DB_SCALE(sun4i_codec_micin_loopback_gain_scale, -450, 150,
6918c2ecf20Sopenharmony_ci			    0);
6928c2ecf20Sopenharmony_cistatic DECLARE_TLV_DB_RANGE(sun4i_codec_micin_preamp_gain_scale,
6938c2ecf20Sopenharmony_ci			    0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
6948c2ecf20Sopenharmony_ci			    1, 7, TLV_DB_SCALE_ITEM(3500, 300, 0));
6958c2ecf20Sopenharmony_cistatic DECLARE_TLV_DB_RANGE(sun7i_codec_micin_preamp_gain_scale,
6968c2ecf20Sopenharmony_ci			    0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
6978c2ecf20Sopenharmony_ci			    1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0));
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new sun4i_codec_controls[] = {
7008c2ecf20Sopenharmony_ci	SOC_SINGLE_TLV("Power Amplifier Volume", SUN4I_CODEC_DAC_ACTL,
7018c2ecf20Sopenharmony_ci		       SUN4I_CODEC_DAC_ACTL_PA_VOL, 0x3F, 0,
7028c2ecf20Sopenharmony_ci		       sun4i_codec_pa_volume_scale),
7038c2ecf20Sopenharmony_ci	SOC_SINGLE_TLV("Line Playback Volume", SUN4I_CODEC_DAC_ACTL,
7048c2ecf20Sopenharmony_ci		       SUN4I_CODEC_DAC_ACTL_LNG, 1, 0,
7058c2ecf20Sopenharmony_ci		       sun4i_codec_linein_loopback_gain_scale),
7068c2ecf20Sopenharmony_ci	SOC_SINGLE_TLV("Line Boost Volume", SUN4I_CODEC_ADC_ACTL,
7078c2ecf20Sopenharmony_ci		       SUN4I_CODEC_ADC_ACTL_LNPREG, 7, 0,
7088c2ecf20Sopenharmony_ci		       sun4i_codec_linein_preamp_gain_scale),
7098c2ecf20Sopenharmony_ci	SOC_SINGLE_TLV("FM Playback Volume", SUN4I_CODEC_DAC_ACTL,
7108c2ecf20Sopenharmony_ci		       SUN4I_CODEC_DAC_ACTL_FMG, 3, 0,
7118c2ecf20Sopenharmony_ci		       sun4i_codec_fmin_loopback_gain_scale),
7128c2ecf20Sopenharmony_ci	SOC_SINGLE_TLV("Mic Playback Volume", SUN4I_CODEC_DAC_ACTL,
7138c2ecf20Sopenharmony_ci		       SUN4I_CODEC_DAC_ACTL_MICG, 7, 0,
7148c2ecf20Sopenharmony_ci		       sun4i_codec_micin_loopback_gain_scale),
7158c2ecf20Sopenharmony_ci	SOC_SINGLE_TLV("Mic1 Boost Volume", SUN4I_CODEC_ADC_ACTL,
7168c2ecf20Sopenharmony_ci		       SUN4I_CODEC_ADC_ACTL_PREG1, 3, 0,
7178c2ecf20Sopenharmony_ci		       sun4i_codec_micin_preamp_gain_scale),
7188c2ecf20Sopenharmony_ci	SOC_SINGLE_TLV("Mic2 Boost Volume", SUN4I_CODEC_ADC_ACTL,
7198c2ecf20Sopenharmony_ci		       SUN4I_CODEC_ADC_ACTL_PREG2, 3, 0,
7208c2ecf20Sopenharmony_ci		       sun4i_codec_micin_preamp_gain_scale),
7218c2ecf20Sopenharmony_ci};
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new sun7i_codec_controls[] = {
7248c2ecf20Sopenharmony_ci	SOC_SINGLE_TLV("Power Amplifier Volume", SUN4I_CODEC_DAC_ACTL,
7258c2ecf20Sopenharmony_ci		       SUN4I_CODEC_DAC_ACTL_PA_VOL, 0x3F, 0,
7268c2ecf20Sopenharmony_ci		       sun4i_codec_pa_volume_scale),
7278c2ecf20Sopenharmony_ci	SOC_SINGLE_TLV("Line Playback Volume", SUN4I_CODEC_DAC_ACTL,
7288c2ecf20Sopenharmony_ci		       SUN4I_CODEC_DAC_ACTL_LNG, 1, 0,
7298c2ecf20Sopenharmony_ci		       sun4i_codec_linein_loopback_gain_scale),
7308c2ecf20Sopenharmony_ci	SOC_SINGLE_TLV("Line Boost Volume", SUN4I_CODEC_ADC_ACTL,
7318c2ecf20Sopenharmony_ci		       SUN4I_CODEC_ADC_ACTL_LNPREG, 7, 0,
7328c2ecf20Sopenharmony_ci		       sun4i_codec_linein_preamp_gain_scale),
7338c2ecf20Sopenharmony_ci	SOC_SINGLE_TLV("FM Playback Volume", SUN4I_CODEC_DAC_ACTL,
7348c2ecf20Sopenharmony_ci		       SUN4I_CODEC_DAC_ACTL_FMG, 3, 0,
7358c2ecf20Sopenharmony_ci		       sun4i_codec_fmin_loopback_gain_scale),
7368c2ecf20Sopenharmony_ci	SOC_SINGLE_TLV("Mic Playback Volume", SUN4I_CODEC_DAC_ACTL,
7378c2ecf20Sopenharmony_ci		       SUN4I_CODEC_DAC_ACTL_MICG, 7, 0,
7388c2ecf20Sopenharmony_ci		       sun4i_codec_micin_loopback_gain_scale),
7398c2ecf20Sopenharmony_ci	SOC_SINGLE_TLV("Mic1 Boost Volume", SUN7I_CODEC_AC_MIC_PHONE_CAL,
7408c2ecf20Sopenharmony_ci		       SUN7I_CODEC_AC_MIC_PHONE_CAL_PREG1, 7, 0,
7418c2ecf20Sopenharmony_ci		       sun7i_codec_micin_preamp_gain_scale),
7428c2ecf20Sopenharmony_ci	SOC_SINGLE_TLV("Mic2 Boost Volume", SUN7I_CODEC_AC_MIC_PHONE_CAL,
7438c2ecf20Sopenharmony_ci		       SUN7I_CODEC_AC_MIC_PHONE_CAL_PREG2, 7, 0,
7448c2ecf20Sopenharmony_ci		       sun7i_codec_micin_preamp_gain_scale),
7458c2ecf20Sopenharmony_ci};
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new sun4i_codec_mixer_controls[] = {
7488c2ecf20Sopenharmony_ci	SOC_DAPM_SINGLE("Left Mixer Left DAC Playback Switch",
7498c2ecf20Sopenharmony_ci			SUN4I_CODEC_DAC_ACTL, SUN4I_CODEC_DAC_ACTL_LDACLMIXS,
7508c2ecf20Sopenharmony_ci			1, 0),
7518c2ecf20Sopenharmony_ci	SOC_DAPM_SINGLE("Right Mixer Right DAC Playback Switch",
7528c2ecf20Sopenharmony_ci			SUN4I_CODEC_DAC_ACTL, SUN4I_CODEC_DAC_ACTL_RDACRMIXS,
7538c2ecf20Sopenharmony_ci			1, 0),
7548c2ecf20Sopenharmony_ci	SOC_DAPM_SINGLE("Right Mixer Left DAC Playback Switch",
7558c2ecf20Sopenharmony_ci			SUN4I_CODEC_DAC_ACTL,
7568c2ecf20Sopenharmony_ci			SUN4I_CODEC_DAC_ACTL_LDACRMIXS, 1, 0),
7578c2ecf20Sopenharmony_ci	SOC_DAPM_DOUBLE("Line Playback Switch", SUN4I_CODEC_DAC_ACTL,
7588c2ecf20Sopenharmony_ci			SUN4I_CODEC_DAC_ACTL_LLNS,
7598c2ecf20Sopenharmony_ci			SUN4I_CODEC_DAC_ACTL_RLNS, 1, 0),
7608c2ecf20Sopenharmony_ci	SOC_DAPM_DOUBLE("FM Playback Switch", SUN4I_CODEC_DAC_ACTL,
7618c2ecf20Sopenharmony_ci			SUN4I_CODEC_DAC_ACTL_LFMS,
7628c2ecf20Sopenharmony_ci			SUN4I_CODEC_DAC_ACTL_RFMS, 1, 0),
7638c2ecf20Sopenharmony_ci	SOC_DAPM_DOUBLE("Mic1 Playback Switch", SUN4I_CODEC_DAC_ACTL,
7648c2ecf20Sopenharmony_ci			SUN4I_CODEC_DAC_ACTL_MIC1LS,
7658c2ecf20Sopenharmony_ci			SUN4I_CODEC_DAC_ACTL_MIC1RS, 1, 0),
7668c2ecf20Sopenharmony_ci	SOC_DAPM_DOUBLE("Mic2 Playback Switch", SUN4I_CODEC_DAC_ACTL,
7678c2ecf20Sopenharmony_ci			SUN4I_CODEC_DAC_ACTL_MIC2LS,
7688c2ecf20Sopenharmony_ci			SUN4I_CODEC_DAC_ACTL_MIC2RS, 1, 0),
7698c2ecf20Sopenharmony_ci};
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new sun4i_codec_pa_mixer_controls[] = {
7728c2ecf20Sopenharmony_ci	SOC_DAPM_SINGLE("DAC Playback Switch", SUN4I_CODEC_DAC_ACTL,
7738c2ecf20Sopenharmony_ci			SUN4I_CODEC_DAC_ACTL_DACPAS, 1, 0),
7748c2ecf20Sopenharmony_ci	SOC_DAPM_SINGLE("Mixer Playback Switch", SUN4I_CODEC_DAC_ACTL,
7758c2ecf20Sopenharmony_ci			SUN4I_CODEC_DAC_ACTL_MIXPAS, 1, 0),
7768c2ecf20Sopenharmony_ci};
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget sun4i_codec_codec_dapm_widgets[] = {
7798c2ecf20Sopenharmony_ci	/* Digital parts of the ADCs */
7808c2ecf20Sopenharmony_ci	SND_SOC_DAPM_SUPPLY("ADC", SUN4I_CODEC_ADC_FIFOC,
7818c2ecf20Sopenharmony_ci			    SUN4I_CODEC_ADC_FIFOC_EN_AD, 0,
7828c2ecf20Sopenharmony_ci			    NULL, 0),
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	/* Digital parts of the DACs */
7858c2ecf20Sopenharmony_ci	SND_SOC_DAPM_SUPPLY("DAC", SUN4I_CODEC_DAC_DPC,
7868c2ecf20Sopenharmony_ci			    SUN4I_CODEC_DAC_DPC_EN_DA, 0,
7878c2ecf20Sopenharmony_ci			    NULL, 0),
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	/* Analog parts of the ADCs */
7908c2ecf20Sopenharmony_ci	SND_SOC_DAPM_ADC("Left ADC", "Codec Capture", SUN4I_CODEC_ADC_ACTL,
7918c2ecf20Sopenharmony_ci			 SUN4I_CODEC_ADC_ACTL_ADC_L_EN, 0),
7928c2ecf20Sopenharmony_ci	SND_SOC_DAPM_ADC("Right ADC", "Codec Capture", SUN4I_CODEC_ADC_ACTL,
7938c2ecf20Sopenharmony_ci			 SUN4I_CODEC_ADC_ACTL_ADC_R_EN, 0),
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	/* Analog parts of the DACs */
7968c2ecf20Sopenharmony_ci	SND_SOC_DAPM_DAC("Left DAC", "Codec Playback", SUN4I_CODEC_DAC_ACTL,
7978c2ecf20Sopenharmony_ci			 SUN4I_CODEC_DAC_ACTL_DACAENL, 0),
7988c2ecf20Sopenharmony_ci	SND_SOC_DAPM_DAC("Right DAC", "Codec Playback", SUN4I_CODEC_DAC_ACTL,
7998c2ecf20Sopenharmony_ci			 SUN4I_CODEC_DAC_ACTL_DACAENR, 0),
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	/* Mixers */
8028c2ecf20Sopenharmony_ci	SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
8038c2ecf20Sopenharmony_ci			   sun4i_codec_mixer_controls,
8048c2ecf20Sopenharmony_ci			   ARRAY_SIZE(sun4i_codec_mixer_controls)),
8058c2ecf20Sopenharmony_ci	SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
8068c2ecf20Sopenharmony_ci			   sun4i_codec_mixer_controls,
8078c2ecf20Sopenharmony_ci			   ARRAY_SIZE(sun4i_codec_mixer_controls)),
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	/* Global Mixer Enable */
8108c2ecf20Sopenharmony_ci	SND_SOC_DAPM_SUPPLY("Mixer Enable", SUN4I_CODEC_DAC_ACTL,
8118c2ecf20Sopenharmony_ci			    SUN4I_CODEC_DAC_ACTL_MIXEN, 0, NULL, 0),
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	/* VMIC */
8148c2ecf20Sopenharmony_ci	SND_SOC_DAPM_SUPPLY("VMIC", SUN4I_CODEC_ADC_ACTL,
8158c2ecf20Sopenharmony_ci			    SUN4I_CODEC_ADC_ACTL_VMICEN, 0, NULL, 0),
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	/* Mic Pre-Amplifiers */
8188c2ecf20Sopenharmony_ci	SND_SOC_DAPM_PGA("MIC1 Pre-Amplifier", SUN4I_CODEC_ADC_ACTL,
8198c2ecf20Sopenharmony_ci			 SUN4I_CODEC_ADC_ACTL_PREG1EN, 0, NULL, 0),
8208c2ecf20Sopenharmony_ci	SND_SOC_DAPM_PGA("MIC2 Pre-Amplifier", SUN4I_CODEC_ADC_ACTL,
8218c2ecf20Sopenharmony_ci			 SUN4I_CODEC_ADC_ACTL_PREG2EN, 0, NULL, 0),
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	/* Power Amplifier */
8248c2ecf20Sopenharmony_ci	SND_SOC_DAPM_MIXER("Power Amplifier", SUN4I_CODEC_ADC_ACTL,
8258c2ecf20Sopenharmony_ci			   SUN4I_CODEC_ADC_ACTL_PA_EN, 0,
8268c2ecf20Sopenharmony_ci			   sun4i_codec_pa_mixer_controls,
8278c2ecf20Sopenharmony_ci			   ARRAY_SIZE(sun4i_codec_pa_mixer_controls)),
8288c2ecf20Sopenharmony_ci	SND_SOC_DAPM_SWITCH("Power Amplifier Mute", SND_SOC_NOPM, 0, 0,
8298c2ecf20Sopenharmony_ci			    &sun4i_codec_pa_mute),
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci	SND_SOC_DAPM_INPUT("Line Right"),
8328c2ecf20Sopenharmony_ci	SND_SOC_DAPM_INPUT("Line Left"),
8338c2ecf20Sopenharmony_ci	SND_SOC_DAPM_INPUT("FM Right"),
8348c2ecf20Sopenharmony_ci	SND_SOC_DAPM_INPUT("FM Left"),
8358c2ecf20Sopenharmony_ci	SND_SOC_DAPM_INPUT("Mic1"),
8368c2ecf20Sopenharmony_ci	SND_SOC_DAPM_INPUT("Mic2"),
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	SND_SOC_DAPM_OUTPUT("HP Right"),
8398c2ecf20Sopenharmony_ci	SND_SOC_DAPM_OUTPUT("HP Left"),
8408c2ecf20Sopenharmony_ci};
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_route sun4i_codec_codec_dapm_routes[] = {
8438c2ecf20Sopenharmony_ci	/* Left ADC / DAC Routes */
8448c2ecf20Sopenharmony_ci	{ "Left ADC", NULL, "ADC" },
8458c2ecf20Sopenharmony_ci	{ "Left DAC", NULL, "DAC" },
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	/* Right ADC / DAC Routes */
8488c2ecf20Sopenharmony_ci	{ "Right ADC", NULL, "ADC" },
8498c2ecf20Sopenharmony_ci	{ "Right DAC", NULL, "DAC" },
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	/* Right Mixer Routes */
8528c2ecf20Sopenharmony_ci	{ "Right Mixer", NULL, "Mixer Enable" },
8538c2ecf20Sopenharmony_ci	{ "Right Mixer", "Right Mixer Left DAC Playback Switch", "Left DAC" },
8548c2ecf20Sopenharmony_ci	{ "Right Mixer", "Right Mixer Right DAC Playback Switch", "Right DAC" },
8558c2ecf20Sopenharmony_ci	{ "Right Mixer", "Line Playback Switch", "Line Right" },
8568c2ecf20Sopenharmony_ci	{ "Right Mixer", "FM Playback Switch", "FM Right" },
8578c2ecf20Sopenharmony_ci	{ "Right Mixer", "Mic1 Playback Switch", "MIC1 Pre-Amplifier" },
8588c2ecf20Sopenharmony_ci	{ "Right Mixer", "Mic2 Playback Switch", "MIC2 Pre-Amplifier" },
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci	/* Left Mixer Routes */
8618c2ecf20Sopenharmony_ci	{ "Left Mixer", NULL, "Mixer Enable" },
8628c2ecf20Sopenharmony_ci	{ "Left Mixer", "Left Mixer Left DAC Playback Switch", "Left DAC" },
8638c2ecf20Sopenharmony_ci	{ "Left Mixer", "Line Playback Switch", "Line Left" },
8648c2ecf20Sopenharmony_ci	{ "Left Mixer", "FM Playback Switch", "FM Left" },
8658c2ecf20Sopenharmony_ci	{ "Left Mixer", "Mic1 Playback Switch", "MIC1 Pre-Amplifier" },
8668c2ecf20Sopenharmony_ci	{ "Left Mixer", "Mic2 Playback Switch", "MIC2 Pre-Amplifier" },
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	/* Power Amplifier Routes */
8698c2ecf20Sopenharmony_ci	{ "Power Amplifier", "Mixer Playback Switch", "Left Mixer" },
8708c2ecf20Sopenharmony_ci	{ "Power Amplifier", "Mixer Playback Switch", "Right Mixer" },
8718c2ecf20Sopenharmony_ci	{ "Power Amplifier", "DAC Playback Switch", "Left DAC" },
8728c2ecf20Sopenharmony_ci	{ "Power Amplifier", "DAC Playback Switch", "Right DAC" },
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	/* Headphone Output Routes */
8758c2ecf20Sopenharmony_ci	{ "Power Amplifier Mute", "Switch", "Power Amplifier" },
8768c2ecf20Sopenharmony_ci	{ "HP Right", NULL, "Power Amplifier Mute" },
8778c2ecf20Sopenharmony_ci	{ "HP Left", NULL, "Power Amplifier Mute" },
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	/* Mic1 Routes */
8808c2ecf20Sopenharmony_ci	{ "Left ADC", NULL, "MIC1 Pre-Amplifier" },
8818c2ecf20Sopenharmony_ci	{ "Right ADC", NULL, "MIC1 Pre-Amplifier" },
8828c2ecf20Sopenharmony_ci	{ "MIC1 Pre-Amplifier", NULL, "Mic1"},
8838c2ecf20Sopenharmony_ci	{ "Mic1", NULL, "VMIC" },
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci	/* Mic2 Routes */
8868c2ecf20Sopenharmony_ci	{ "Left ADC", NULL, "MIC2 Pre-Amplifier" },
8878c2ecf20Sopenharmony_ci	{ "Right ADC", NULL, "MIC2 Pre-Amplifier" },
8888c2ecf20Sopenharmony_ci	{ "MIC2 Pre-Amplifier", NULL, "Mic2"},
8898c2ecf20Sopenharmony_ci	{ "Mic2", NULL, "VMIC" },
8908c2ecf20Sopenharmony_ci};
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver sun4i_codec_codec = {
8938c2ecf20Sopenharmony_ci	.controls		= sun4i_codec_controls,
8948c2ecf20Sopenharmony_ci	.num_controls		= ARRAY_SIZE(sun4i_codec_controls),
8958c2ecf20Sopenharmony_ci	.dapm_widgets		= sun4i_codec_codec_dapm_widgets,
8968c2ecf20Sopenharmony_ci	.num_dapm_widgets	= ARRAY_SIZE(sun4i_codec_codec_dapm_widgets),
8978c2ecf20Sopenharmony_ci	.dapm_routes		= sun4i_codec_codec_dapm_routes,
8988c2ecf20Sopenharmony_ci	.num_dapm_routes	= ARRAY_SIZE(sun4i_codec_codec_dapm_routes),
8998c2ecf20Sopenharmony_ci	.idle_bias_on		= 1,
9008c2ecf20Sopenharmony_ci	.use_pmdown_time	= 1,
9018c2ecf20Sopenharmony_ci	.endianness		= 1,
9028c2ecf20Sopenharmony_ci	.non_legacy_dai_naming	= 1,
9038c2ecf20Sopenharmony_ci};
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver sun7i_codec_codec = {
9068c2ecf20Sopenharmony_ci	.controls		= sun7i_codec_controls,
9078c2ecf20Sopenharmony_ci	.num_controls		= ARRAY_SIZE(sun7i_codec_controls),
9088c2ecf20Sopenharmony_ci	.dapm_widgets		= sun4i_codec_codec_dapm_widgets,
9098c2ecf20Sopenharmony_ci	.num_dapm_widgets	= ARRAY_SIZE(sun4i_codec_codec_dapm_widgets),
9108c2ecf20Sopenharmony_ci	.dapm_routes		= sun4i_codec_codec_dapm_routes,
9118c2ecf20Sopenharmony_ci	.num_dapm_routes	= ARRAY_SIZE(sun4i_codec_codec_dapm_routes),
9128c2ecf20Sopenharmony_ci	.idle_bias_on		= 1,
9138c2ecf20Sopenharmony_ci	.use_pmdown_time	= 1,
9148c2ecf20Sopenharmony_ci	.endianness		= 1,
9158c2ecf20Sopenharmony_ci	.non_legacy_dai_naming	= 1,
9168c2ecf20Sopenharmony_ci};
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci/*** sun6i Codec ***/
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci/* mixer controls */
9218c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new sun6i_codec_mixer_controls[] = {
9228c2ecf20Sopenharmony_ci	SOC_DAPM_DOUBLE("DAC Playback Switch",
9238c2ecf20Sopenharmony_ci			SUN6I_CODEC_OM_DACA_CTRL,
9248c2ecf20Sopenharmony_ci			SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACL,
9258c2ecf20Sopenharmony_ci			SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACR, 1, 0),
9268c2ecf20Sopenharmony_ci	SOC_DAPM_DOUBLE("DAC Reversed Playback Switch",
9278c2ecf20Sopenharmony_ci			SUN6I_CODEC_OM_DACA_CTRL,
9288c2ecf20Sopenharmony_ci			SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACR,
9298c2ecf20Sopenharmony_ci			SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACL, 1, 0),
9308c2ecf20Sopenharmony_ci	SOC_DAPM_DOUBLE("Line In Playback Switch",
9318c2ecf20Sopenharmony_ci			SUN6I_CODEC_OM_DACA_CTRL,
9328c2ecf20Sopenharmony_ci			SUN6I_CODEC_OM_DACA_CTRL_LMIX_LINEINL,
9338c2ecf20Sopenharmony_ci			SUN6I_CODEC_OM_DACA_CTRL_RMIX_LINEINR, 1, 0),
9348c2ecf20Sopenharmony_ci	SOC_DAPM_DOUBLE("Mic1 Playback Switch",
9358c2ecf20Sopenharmony_ci			SUN6I_CODEC_OM_DACA_CTRL,
9368c2ecf20Sopenharmony_ci			SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC1,
9378c2ecf20Sopenharmony_ci			SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC1, 1, 0),
9388c2ecf20Sopenharmony_ci	SOC_DAPM_DOUBLE("Mic2 Playback Switch",
9398c2ecf20Sopenharmony_ci			SUN6I_CODEC_OM_DACA_CTRL,
9408c2ecf20Sopenharmony_ci			SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC2,
9418c2ecf20Sopenharmony_ci			SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC2, 1, 0),
9428c2ecf20Sopenharmony_ci};
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci/* ADC mixer controls */
9458c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new sun6i_codec_adc_mixer_controls[] = {
9468c2ecf20Sopenharmony_ci	SOC_DAPM_DOUBLE("Mixer Capture Switch",
9478c2ecf20Sopenharmony_ci			SUN6I_CODEC_ADC_ACTL,
9488c2ecf20Sopenharmony_ci			SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXL,
9498c2ecf20Sopenharmony_ci			SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXR, 1, 0),
9508c2ecf20Sopenharmony_ci	SOC_DAPM_DOUBLE("Mixer Reversed Capture Switch",
9518c2ecf20Sopenharmony_ci			SUN6I_CODEC_ADC_ACTL,
9528c2ecf20Sopenharmony_ci			SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXR,
9538c2ecf20Sopenharmony_ci			SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXL, 1, 0),
9548c2ecf20Sopenharmony_ci	SOC_DAPM_DOUBLE("Line In Capture Switch",
9558c2ecf20Sopenharmony_ci			SUN6I_CODEC_ADC_ACTL,
9568c2ecf20Sopenharmony_ci			SUN6I_CODEC_ADC_ACTL_LADCMIX_LINEINL,
9578c2ecf20Sopenharmony_ci			SUN6I_CODEC_ADC_ACTL_RADCMIX_LINEINR, 1, 0),
9588c2ecf20Sopenharmony_ci	SOC_DAPM_DOUBLE("Mic1 Capture Switch",
9598c2ecf20Sopenharmony_ci			SUN6I_CODEC_ADC_ACTL,
9608c2ecf20Sopenharmony_ci			SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC1,
9618c2ecf20Sopenharmony_ci			SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC1, 1, 0),
9628c2ecf20Sopenharmony_ci	SOC_DAPM_DOUBLE("Mic2 Capture Switch",
9638c2ecf20Sopenharmony_ci			SUN6I_CODEC_ADC_ACTL,
9648c2ecf20Sopenharmony_ci			SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC2,
9658c2ecf20Sopenharmony_ci			SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC2, 1, 0),
9668c2ecf20Sopenharmony_ci};
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci/* headphone controls */
9698c2ecf20Sopenharmony_cistatic const char * const sun6i_codec_hp_src_enum_text[] = {
9708c2ecf20Sopenharmony_ci	"DAC", "Mixer",
9718c2ecf20Sopenharmony_ci};
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_cistatic SOC_ENUM_DOUBLE_DECL(sun6i_codec_hp_src_enum,
9748c2ecf20Sopenharmony_ci			    SUN6I_CODEC_OM_DACA_CTRL,
9758c2ecf20Sopenharmony_ci			    SUN6I_CODEC_OM_DACA_CTRL_LHPIS,
9768c2ecf20Sopenharmony_ci			    SUN6I_CODEC_OM_DACA_CTRL_RHPIS,
9778c2ecf20Sopenharmony_ci			    sun6i_codec_hp_src_enum_text);
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new sun6i_codec_hp_src[] = {
9808c2ecf20Sopenharmony_ci	SOC_DAPM_ENUM("Headphone Source Playback Route",
9818c2ecf20Sopenharmony_ci		      sun6i_codec_hp_src_enum),
9828c2ecf20Sopenharmony_ci};
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_ci/* microphone controls */
9858c2ecf20Sopenharmony_cistatic const char * const sun6i_codec_mic2_src_enum_text[] = {
9868c2ecf20Sopenharmony_ci	"Mic2", "Mic3",
9878c2ecf20Sopenharmony_ci};
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(sun6i_codec_mic2_src_enum,
9908c2ecf20Sopenharmony_ci			    SUN6I_CODEC_MIC_CTRL,
9918c2ecf20Sopenharmony_ci			    SUN6I_CODEC_MIC_CTRL_MIC2SLT,
9928c2ecf20Sopenharmony_ci			    sun6i_codec_mic2_src_enum_text);
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new sun6i_codec_mic2_src[] = {
9958c2ecf20Sopenharmony_ci	SOC_DAPM_ENUM("Mic2 Amplifier Source Route",
9968c2ecf20Sopenharmony_ci		      sun6i_codec_mic2_src_enum),
9978c2ecf20Sopenharmony_ci};
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci/* line out controls */
10008c2ecf20Sopenharmony_cistatic const char * const sun6i_codec_lineout_src_enum_text[] = {
10018c2ecf20Sopenharmony_ci	"Stereo", "Mono Differential",
10028c2ecf20Sopenharmony_ci};
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_cistatic SOC_ENUM_DOUBLE_DECL(sun6i_codec_lineout_src_enum,
10058c2ecf20Sopenharmony_ci			    SUN6I_CODEC_MIC_CTRL,
10068c2ecf20Sopenharmony_ci			    SUN6I_CODEC_MIC_CTRL_LINEOUTLSRC,
10078c2ecf20Sopenharmony_ci			    SUN6I_CODEC_MIC_CTRL_LINEOUTRSRC,
10088c2ecf20Sopenharmony_ci			    sun6i_codec_lineout_src_enum_text);
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new sun6i_codec_lineout_src[] = {
10118c2ecf20Sopenharmony_ci	SOC_DAPM_ENUM("Line Out Source Playback Route",
10128c2ecf20Sopenharmony_ci		      sun6i_codec_lineout_src_enum),
10138c2ecf20Sopenharmony_ci};
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ci/* volume / mute controls */
10168c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(sun6i_codec_dvol_scale, -7308, 116, 0);
10178c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(sun6i_codec_hp_vol_scale, -6300, 100, 1);
10188c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(sun6i_codec_out_mixer_pregain_scale,
10198c2ecf20Sopenharmony_ci				  -450, 150, 0);
10208c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_RANGE(sun6i_codec_lineout_vol_scale,
10218c2ecf20Sopenharmony_ci	0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
10228c2ecf20Sopenharmony_ci	2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0),
10238c2ecf20Sopenharmony_ci);
10248c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_RANGE(sun6i_codec_mic_gain_scale,
10258c2ecf20Sopenharmony_ci	0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
10268c2ecf20Sopenharmony_ci	1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0),
10278c2ecf20Sopenharmony_ci);
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new sun6i_codec_codec_widgets[] = {
10308c2ecf20Sopenharmony_ci	SOC_SINGLE_TLV("DAC Playback Volume", SUN4I_CODEC_DAC_DPC,
10318c2ecf20Sopenharmony_ci		       SUN4I_CODEC_DAC_DPC_DVOL, 0x3f, 1,
10328c2ecf20Sopenharmony_ci		       sun6i_codec_dvol_scale),
10338c2ecf20Sopenharmony_ci	SOC_SINGLE_TLV("Headphone Playback Volume",
10348c2ecf20Sopenharmony_ci		       SUN6I_CODEC_OM_DACA_CTRL,
10358c2ecf20Sopenharmony_ci		       SUN6I_CODEC_OM_DACA_CTRL_HPVOL, 0x3f, 0,
10368c2ecf20Sopenharmony_ci		       sun6i_codec_hp_vol_scale),
10378c2ecf20Sopenharmony_ci	SOC_SINGLE_TLV("Line Out Playback Volume",
10388c2ecf20Sopenharmony_ci		       SUN6I_CODEC_MIC_CTRL,
10398c2ecf20Sopenharmony_ci		       SUN6I_CODEC_MIC_CTRL_LINEOUTVC, 0x1f, 0,
10408c2ecf20Sopenharmony_ci		       sun6i_codec_lineout_vol_scale),
10418c2ecf20Sopenharmony_ci	SOC_DOUBLE("Headphone Playback Switch",
10428c2ecf20Sopenharmony_ci		   SUN6I_CODEC_OM_DACA_CTRL,
10438c2ecf20Sopenharmony_ci		   SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE,
10448c2ecf20Sopenharmony_ci		   SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE, 1, 0),
10458c2ecf20Sopenharmony_ci	SOC_DOUBLE("Line Out Playback Switch",
10468c2ecf20Sopenharmony_ci		   SUN6I_CODEC_MIC_CTRL,
10478c2ecf20Sopenharmony_ci		   SUN6I_CODEC_MIC_CTRL_LINEOUTLEN,
10488c2ecf20Sopenharmony_ci		   SUN6I_CODEC_MIC_CTRL_LINEOUTREN, 1, 0),
10498c2ecf20Sopenharmony_ci	/* Mixer pre-gains */
10508c2ecf20Sopenharmony_ci	SOC_SINGLE_TLV("Line In Playback Volume",
10518c2ecf20Sopenharmony_ci		       SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_LINEING,
10528c2ecf20Sopenharmony_ci		       0x7, 0, sun6i_codec_out_mixer_pregain_scale),
10538c2ecf20Sopenharmony_ci	SOC_SINGLE_TLV("Mic1 Playback Volume",
10548c2ecf20Sopenharmony_ci		       SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_MIC1G,
10558c2ecf20Sopenharmony_ci		       0x7, 0, sun6i_codec_out_mixer_pregain_scale),
10568c2ecf20Sopenharmony_ci	SOC_SINGLE_TLV("Mic2 Playback Volume",
10578c2ecf20Sopenharmony_ci		       SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_MIC2G,
10588c2ecf20Sopenharmony_ci		       0x7, 0, sun6i_codec_out_mixer_pregain_scale),
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ci	/* Microphone Amp boost gains */
10618c2ecf20Sopenharmony_ci	SOC_SINGLE_TLV("Mic1 Boost Volume", SUN6I_CODEC_MIC_CTRL,
10628c2ecf20Sopenharmony_ci		       SUN6I_CODEC_MIC_CTRL_MIC1BOOST, 0x7, 0,
10638c2ecf20Sopenharmony_ci		       sun6i_codec_mic_gain_scale),
10648c2ecf20Sopenharmony_ci	SOC_SINGLE_TLV("Mic2 Boost Volume", SUN6I_CODEC_MIC_CTRL,
10658c2ecf20Sopenharmony_ci		       SUN6I_CODEC_MIC_CTRL_MIC2BOOST, 0x7, 0,
10668c2ecf20Sopenharmony_ci		       sun6i_codec_mic_gain_scale),
10678c2ecf20Sopenharmony_ci	SOC_DOUBLE_TLV("ADC Capture Volume",
10688c2ecf20Sopenharmony_ci		       SUN6I_CODEC_ADC_ACTL, SUN6I_CODEC_ADC_ACTL_ADCLG,
10698c2ecf20Sopenharmony_ci		       SUN6I_CODEC_ADC_ACTL_ADCRG, 0x7, 0,
10708c2ecf20Sopenharmony_ci		       sun6i_codec_out_mixer_pregain_scale),
10718c2ecf20Sopenharmony_ci};
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget sun6i_codec_codec_dapm_widgets[] = {
10748c2ecf20Sopenharmony_ci	/* Microphone inputs */
10758c2ecf20Sopenharmony_ci	SND_SOC_DAPM_INPUT("MIC1"),
10768c2ecf20Sopenharmony_ci	SND_SOC_DAPM_INPUT("MIC2"),
10778c2ecf20Sopenharmony_ci	SND_SOC_DAPM_INPUT("MIC3"),
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci	/* Microphone Bias */
10808c2ecf20Sopenharmony_ci	SND_SOC_DAPM_SUPPLY("HBIAS", SUN6I_CODEC_MIC_CTRL,
10818c2ecf20Sopenharmony_ci			    SUN6I_CODEC_MIC_CTRL_HBIASEN, 0, NULL, 0),
10828c2ecf20Sopenharmony_ci	SND_SOC_DAPM_SUPPLY("MBIAS", SUN6I_CODEC_MIC_CTRL,
10838c2ecf20Sopenharmony_ci			    SUN6I_CODEC_MIC_CTRL_MBIASEN, 0, NULL, 0),
10848c2ecf20Sopenharmony_ci
10858c2ecf20Sopenharmony_ci	/* Mic input path */
10868c2ecf20Sopenharmony_ci	SND_SOC_DAPM_MUX("Mic2 Amplifier Source Route",
10878c2ecf20Sopenharmony_ci			 SND_SOC_NOPM, 0, 0, sun6i_codec_mic2_src),
10888c2ecf20Sopenharmony_ci	SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN6I_CODEC_MIC_CTRL,
10898c2ecf20Sopenharmony_ci			 SUN6I_CODEC_MIC_CTRL_MIC1AMPEN, 0, NULL, 0),
10908c2ecf20Sopenharmony_ci	SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN6I_CODEC_MIC_CTRL,
10918c2ecf20Sopenharmony_ci			 SUN6I_CODEC_MIC_CTRL_MIC2AMPEN, 0, NULL, 0),
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci	/* Line In */
10948c2ecf20Sopenharmony_ci	SND_SOC_DAPM_INPUT("LINEIN"),
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci	/* Digital parts of the ADCs */
10978c2ecf20Sopenharmony_ci	SND_SOC_DAPM_SUPPLY("ADC Enable", SUN6I_CODEC_ADC_FIFOC,
10988c2ecf20Sopenharmony_ci			    SUN6I_CODEC_ADC_FIFOC_EN_AD, 0,
10998c2ecf20Sopenharmony_ci			    NULL, 0),
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci	/* Analog parts of the ADCs */
11028c2ecf20Sopenharmony_ci	SND_SOC_DAPM_ADC("Left ADC", "Codec Capture", SUN6I_CODEC_ADC_ACTL,
11038c2ecf20Sopenharmony_ci			 SUN6I_CODEC_ADC_ACTL_ADCLEN, 0),
11048c2ecf20Sopenharmony_ci	SND_SOC_DAPM_ADC("Right ADC", "Codec Capture", SUN6I_CODEC_ADC_ACTL,
11058c2ecf20Sopenharmony_ci			 SUN6I_CODEC_ADC_ACTL_ADCREN, 0),
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci	/* ADC Mixers */
11088c2ecf20Sopenharmony_ci	SOC_MIXER_ARRAY("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
11098c2ecf20Sopenharmony_ci			sun6i_codec_adc_mixer_controls),
11108c2ecf20Sopenharmony_ci	SOC_MIXER_ARRAY("Right ADC Mixer", SND_SOC_NOPM, 0, 0,
11118c2ecf20Sopenharmony_ci			sun6i_codec_adc_mixer_controls),
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	/* Digital parts of the DACs */
11148c2ecf20Sopenharmony_ci	SND_SOC_DAPM_SUPPLY("DAC Enable", SUN4I_CODEC_DAC_DPC,
11158c2ecf20Sopenharmony_ci			    SUN4I_CODEC_DAC_DPC_EN_DA, 0,
11168c2ecf20Sopenharmony_ci			    NULL, 0),
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci	/* Analog parts of the DACs */
11198c2ecf20Sopenharmony_ci	SND_SOC_DAPM_DAC("Left DAC", "Codec Playback",
11208c2ecf20Sopenharmony_ci			 SUN6I_CODEC_OM_DACA_CTRL,
11218c2ecf20Sopenharmony_ci			 SUN6I_CODEC_OM_DACA_CTRL_DACALEN, 0),
11228c2ecf20Sopenharmony_ci	SND_SOC_DAPM_DAC("Right DAC", "Codec Playback",
11238c2ecf20Sopenharmony_ci			 SUN6I_CODEC_OM_DACA_CTRL,
11248c2ecf20Sopenharmony_ci			 SUN6I_CODEC_OM_DACA_CTRL_DACAREN, 0),
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci	/* Mixers */
11278c2ecf20Sopenharmony_ci	SOC_MIXER_ARRAY("Left Mixer", SUN6I_CODEC_OM_DACA_CTRL,
11288c2ecf20Sopenharmony_ci			SUN6I_CODEC_OM_DACA_CTRL_LMIXEN, 0,
11298c2ecf20Sopenharmony_ci			sun6i_codec_mixer_controls),
11308c2ecf20Sopenharmony_ci	SOC_MIXER_ARRAY("Right Mixer", SUN6I_CODEC_OM_DACA_CTRL,
11318c2ecf20Sopenharmony_ci			SUN6I_CODEC_OM_DACA_CTRL_RMIXEN, 0,
11328c2ecf20Sopenharmony_ci			sun6i_codec_mixer_controls),
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_ci	/* Headphone output path */
11358c2ecf20Sopenharmony_ci	SND_SOC_DAPM_MUX("Headphone Source Playback Route",
11368c2ecf20Sopenharmony_ci			 SND_SOC_NOPM, 0, 0, sun6i_codec_hp_src),
11378c2ecf20Sopenharmony_ci	SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUN6I_CODEC_OM_PA_CTRL,
11388c2ecf20Sopenharmony_ci			     SUN6I_CODEC_OM_PA_CTRL_HPPAEN, 0, NULL, 0),
11398c2ecf20Sopenharmony_ci	SND_SOC_DAPM_SUPPLY("HPCOM Protection", SUN6I_CODEC_OM_PA_CTRL,
11408c2ecf20Sopenharmony_ci			    SUN6I_CODEC_OM_PA_CTRL_COMPTEN, 0, NULL, 0),
11418c2ecf20Sopenharmony_ci	SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPCOM", SUN6I_CODEC_OM_PA_CTRL,
11428c2ecf20Sopenharmony_ci			 SUN6I_CODEC_OM_PA_CTRL_HPCOM_CTL, 0x3, 0x3, 0),
11438c2ecf20Sopenharmony_ci	SND_SOC_DAPM_OUTPUT("HP"),
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ci	/* Line Out path */
11468c2ecf20Sopenharmony_ci	SND_SOC_DAPM_MUX("Line Out Source Playback Route",
11478c2ecf20Sopenharmony_ci			 SND_SOC_NOPM, 0, 0, sun6i_codec_lineout_src),
11488c2ecf20Sopenharmony_ci	SND_SOC_DAPM_OUTPUT("LINEOUT"),
11498c2ecf20Sopenharmony_ci};
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = {
11528c2ecf20Sopenharmony_ci	/* DAC Routes */
11538c2ecf20Sopenharmony_ci	{ "Left DAC", NULL, "DAC Enable" },
11548c2ecf20Sopenharmony_ci	{ "Right DAC", NULL, "DAC Enable" },
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci	/* Microphone Routes */
11578c2ecf20Sopenharmony_ci	{ "Mic1 Amplifier", NULL, "MIC1"},
11588c2ecf20Sopenharmony_ci	{ "Mic2 Amplifier Source Route", "Mic2", "MIC2" },
11598c2ecf20Sopenharmony_ci	{ "Mic2 Amplifier Source Route", "Mic3", "MIC3" },
11608c2ecf20Sopenharmony_ci	{ "Mic2 Amplifier", NULL, "Mic2 Amplifier Source Route"},
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci	/* Left Mixer Routes */
11638c2ecf20Sopenharmony_ci	{ "Left Mixer", "DAC Playback Switch", "Left DAC" },
11648c2ecf20Sopenharmony_ci	{ "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" },
11658c2ecf20Sopenharmony_ci	{ "Left Mixer", "Line In Playback Switch", "LINEIN" },
11668c2ecf20Sopenharmony_ci	{ "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
11678c2ecf20Sopenharmony_ci	{ "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ci	/* Right Mixer Routes */
11708c2ecf20Sopenharmony_ci	{ "Right Mixer", "DAC Playback Switch", "Right DAC" },
11718c2ecf20Sopenharmony_ci	{ "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" },
11728c2ecf20Sopenharmony_ci	{ "Right Mixer", "Line In Playback Switch", "LINEIN" },
11738c2ecf20Sopenharmony_ci	{ "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
11748c2ecf20Sopenharmony_ci	{ "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci	/* Left ADC Mixer Routes */
11778c2ecf20Sopenharmony_ci	{ "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" },
11788c2ecf20Sopenharmony_ci	{ "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" },
11798c2ecf20Sopenharmony_ci	{ "Left ADC Mixer", "Line In Capture Switch", "LINEIN" },
11808c2ecf20Sopenharmony_ci	{ "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
11818c2ecf20Sopenharmony_ci	{ "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci	/* Right ADC Mixer Routes */
11848c2ecf20Sopenharmony_ci	{ "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" },
11858c2ecf20Sopenharmony_ci	{ "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" },
11868c2ecf20Sopenharmony_ci	{ "Right ADC Mixer", "Line In Capture Switch", "LINEIN" },
11878c2ecf20Sopenharmony_ci	{ "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
11888c2ecf20Sopenharmony_ci	{ "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci	/* Headphone Routes */
11918c2ecf20Sopenharmony_ci	{ "Headphone Source Playback Route", "DAC", "Left DAC" },
11928c2ecf20Sopenharmony_ci	{ "Headphone Source Playback Route", "DAC", "Right DAC" },
11938c2ecf20Sopenharmony_ci	{ "Headphone Source Playback Route", "Mixer", "Left Mixer" },
11948c2ecf20Sopenharmony_ci	{ "Headphone Source Playback Route", "Mixer", "Right Mixer" },
11958c2ecf20Sopenharmony_ci	{ "Headphone Amp", NULL, "Headphone Source Playback Route" },
11968c2ecf20Sopenharmony_ci	{ "HP", NULL, "Headphone Amp" },
11978c2ecf20Sopenharmony_ci	{ "HPCOM", NULL, "HPCOM Protection" },
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ci	/* Line Out Routes */
12008c2ecf20Sopenharmony_ci	{ "Line Out Source Playback Route", "Stereo", "Left Mixer" },
12018c2ecf20Sopenharmony_ci	{ "Line Out Source Playback Route", "Stereo", "Right Mixer" },
12028c2ecf20Sopenharmony_ci	{ "Line Out Source Playback Route", "Mono Differential", "Left Mixer" },
12038c2ecf20Sopenharmony_ci	{ "Line Out Source Playback Route", "Mono Differential", "Right Mixer" },
12048c2ecf20Sopenharmony_ci	{ "LINEOUT", NULL, "Line Out Source Playback Route" },
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci	/* ADC Routes */
12078c2ecf20Sopenharmony_ci	{ "Left ADC", NULL, "ADC Enable" },
12088c2ecf20Sopenharmony_ci	{ "Right ADC", NULL, "ADC Enable" },
12098c2ecf20Sopenharmony_ci	{ "Left ADC", NULL, "Left ADC Mixer" },
12108c2ecf20Sopenharmony_ci	{ "Right ADC", NULL, "Right ADC Mixer" },
12118c2ecf20Sopenharmony_ci};
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver sun6i_codec_codec = {
12148c2ecf20Sopenharmony_ci	.controls		= sun6i_codec_codec_widgets,
12158c2ecf20Sopenharmony_ci	.num_controls		= ARRAY_SIZE(sun6i_codec_codec_widgets),
12168c2ecf20Sopenharmony_ci	.dapm_widgets		= sun6i_codec_codec_dapm_widgets,
12178c2ecf20Sopenharmony_ci	.num_dapm_widgets	= ARRAY_SIZE(sun6i_codec_codec_dapm_widgets),
12188c2ecf20Sopenharmony_ci	.dapm_routes		= sun6i_codec_codec_dapm_routes,
12198c2ecf20Sopenharmony_ci	.num_dapm_routes	= ARRAY_SIZE(sun6i_codec_codec_dapm_routes),
12208c2ecf20Sopenharmony_ci	.idle_bias_on		= 1,
12218c2ecf20Sopenharmony_ci	.use_pmdown_time	= 1,
12228c2ecf20Sopenharmony_ci	.endianness		= 1,
12238c2ecf20Sopenharmony_ci	.non_legacy_dai_naming	= 1,
12248c2ecf20Sopenharmony_ci};
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci/* sun8i A23 codec */
12278c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new sun8i_a23_codec_codec_controls[] = {
12288c2ecf20Sopenharmony_ci	SOC_SINGLE_TLV("DAC Playback Volume", SUN4I_CODEC_DAC_DPC,
12298c2ecf20Sopenharmony_ci		       SUN4I_CODEC_DAC_DPC_DVOL, 0x3f, 1,
12308c2ecf20Sopenharmony_ci		       sun6i_codec_dvol_scale),
12318c2ecf20Sopenharmony_ci};
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget sun8i_a23_codec_codec_widgets[] = {
12348c2ecf20Sopenharmony_ci	/* Digital parts of the ADCs */
12358c2ecf20Sopenharmony_ci	SND_SOC_DAPM_SUPPLY("ADC Enable", SUN6I_CODEC_ADC_FIFOC,
12368c2ecf20Sopenharmony_ci			    SUN6I_CODEC_ADC_FIFOC_EN_AD, 0, NULL, 0),
12378c2ecf20Sopenharmony_ci	/* Digital parts of the DACs */
12388c2ecf20Sopenharmony_ci	SND_SOC_DAPM_SUPPLY("DAC Enable", SUN4I_CODEC_DAC_DPC,
12398c2ecf20Sopenharmony_ci			    SUN4I_CODEC_DAC_DPC_EN_DA, 0, NULL, 0),
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci};
12428c2ecf20Sopenharmony_ci
12438c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver sun8i_a23_codec_codec = {
12448c2ecf20Sopenharmony_ci	.controls		= sun8i_a23_codec_codec_controls,
12458c2ecf20Sopenharmony_ci	.num_controls		= ARRAY_SIZE(sun8i_a23_codec_codec_controls),
12468c2ecf20Sopenharmony_ci	.dapm_widgets		= sun8i_a23_codec_codec_widgets,
12478c2ecf20Sopenharmony_ci	.num_dapm_widgets	= ARRAY_SIZE(sun8i_a23_codec_codec_widgets),
12488c2ecf20Sopenharmony_ci	.idle_bias_on		= 1,
12498c2ecf20Sopenharmony_ci	.use_pmdown_time	= 1,
12508c2ecf20Sopenharmony_ci	.endianness		= 1,
12518c2ecf20Sopenharmony_ci	.non_legacy_dai_naming	= 1,
12528c2ecf20Sopenharmony_ci};
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver sun4i_codec_component = {
12558c2ecf20Sopenharmony_ci	.name = "sun4i-codec",
12568c2ecf20Sopenharmony_ci};
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_ci#define SUN4I_CODEC_RATES	SNDRV_PCM_RATE_CONTINUOUS
12598c2ecf20Sopenharmony_ci#define SUN4I_CODEC_FORMATS	(SNDRV_PCM_FMTBIT_S16_LE | \
12608c2ecf20Sopenharmony_ci				 SNDRV_PCM_FMTBIT_S32_LE)
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_cistatic int sun4i_codec_dai_probe(struct snd_soc_dai *dai)
12638c2ecf20Sopenharmony_ci{
12648c2ecf20Sopenharmony_ci	struct snd_soc_card *card = snd_soc_dai_get_drvdata(dai);
12658c2ecf20Sopenharmony_ci	struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card);
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_ci	snd_soc_dai_init_dma_data(dai, &scodec->playback_dma_data,
12688c2ecf20Sopenharmony_ci				  &scodec->capture_dma_data);
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_ci	return 0;
12718c2ecf20Sopenharmony_ci}
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver dummy_cpu_dai = {
12748c2ecf20Sopenharmony_ci	.name	= "sun4i-codec-cpu-dai",
12758c2ecf20Sopenharmony_ci	.probe	= sun4i_codec_dai_probe,
12768c2ecf20Sopenharmony_ci	.playback = {
12778c2ecf20Sopenharmony_ci		.stream_name	= "Playback",
12788c2ecf20Sopenharmony_ci		.channels_min	= 1,
12798c2ecf20Sopenharmony_ci		.channels_max	= 2,
12808c2ecf20Sopenharmony_ci		.rates		= SUN4I_CODEC_RATES,
12818c2ecf20Sopenharmony_ci		.formats	= SUN4I_CODEC_FORMATS,
12828c2ecf20Sopenharmony_ci		.sig_bits	= 24,
12838c2ecf20Sopenharmony_ci	},
12848c2ecf20Sopenharmony_ci	.capture = {
12858c2ecf20Sopenharmony_ci		.stream_name	= "Capture",
12868c2ecf20Sopenharmony_ci		.channels_min	= 1,
12878c2ecf20Sopenharmony_ci		.channels_max	= 2,
12888c2ecf20Sopenharmony_ci		.rates 		= SUN4I_CODEC_RATES,
12898c2ecf20Sopenharmony_ci		.formats 	= SUN4I_CODEC_FORMATS,
12908c2ecf20Sopenharmony_ci		.sig_bits	= 24,
12918c2ecf20Sopenharmony_ci	 },
12928c2ecf20Sopenharmony_ci};
12938c2ecf20Sopenharmony_ci
12948c2ecf20Sopenharmony_cistatic struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev,
12958c2ecf20Sopenharmony_ci							int *num_links)
12968c2ecf20Sopenharmony_ci{
12978c2ecf20Sopenharmony_ci	struct snd_soc_dai_link *link = devm_kzalloc(dev, sizeof(*link),
12988c2ecf20Sopenharmony_ci						     GFP_KERNEL);
12998c2ecf20Sopenharmony_ci	struct snd_soc_dai_link_component *dlc = devm_kzalloc(dev,
13008c2ecf20Sopenharmony_ci						3 * sizeof(*dlc), GFP_KERNEL);
13018c2ecf20Sopenharmony_ci	if (!link || !dlc)
13028c2ecf20Sopenharmony_ci		return NULL;
13038c2ecf20Sopenharmony_ci
13048c2ecf20Sopenharmony_ci	link->cpus	= &dlc[0];
13058c2ecf20Sopenharmony_ci	link->codecs	= &dlc[1];
13068c2ecf20Sopenharmony_ci	link->platforms	= &dlc[2];
13078c2ecf20Sopenharmony_ci
13088c2ecf20Sopenharmony_ci	link->num_cpus		= 1;
13098c2ecf20Sopenharmony_ci	link->num_codecs	= 1;
13108c2ecf20Sopenharmony_ci	link->num_platforms	= 1;
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_ci	link->name		= "cdc";
13138c2ecf20Sopenharmony_ci	link->stream_name	= "CDC PCM";
13148c2ecf20Sopenharmony_ci	link->codecs->dai_name	= "Codec";
13158c2ecf20Sopenharmony_ci	link->cpus->dai_name	= dev_name(dev);
13168c2ecf20Sopenharmony_ci	link->codecs->name	= dev_name(dev);
13178c2ecf20Sopenharmony_ci	link->platforms->name	= dev_name(dev);
13188c2ecf20Sopenharmony_ci	link->dai_fmt		= SND_SOC_DAIFMT_I2S;
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_ci	*num_links = 1;
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_ci	return link;
13238c2ecf20Sopenharmony_ci};
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_cistatic int sun4i_codec_spk_event(struct snd_soc_dapm_widget *w,
13268c2ecf20Sopenharmony_ci				 struct snd_kcontrol *k, int event)
13278c2ecf20Sopenharmony_ci{
13288c2ecf20Sopenharmony_ci	struct sun4i_codec *scodec = snd_soc_card_get_drvdata(w->dapm->card);
13298c2ecf20Sopenharmony_ci
13308c2ecf20Sopenharmony_ci	gpiod_set_value_cansleep(scodec->gpio_pa,
13318c2ecf20Sopenharmony_ci				 !!SND_SOC_DAPM_EVENT_ON(event));
13328c2ecf20Sopenharmony_ci
13338c2ecf20Sopenharmony_ci	if (SND_SOC_DAPM_EVENT_ON(event)) {
13348c2ecf20Sopenharmony_ci		/*
13358c2ecf20Sopenharmony_ci		 * Need a delay to wait for DAC to push the data. 700ms seems
13368c2ecf20Sopenharmony_ci		 * to be the best compromise not to feel this delay while
13378c2ecf20Sopenharmony_ci		 * playing a sound.
13388c2ecf20Sopenharmony_ci		 */
13398c2ecf20Sopenharmony_ci		msleep(700);
13408c2ecf20Sopenharmony_ci	}
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_ci	return 0;
13438c2ecf20Sopenharmony_ci}
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget sun4i_codec_card_dapm_widgets[] = {
13468c2ecf20Sopenharmony_ci	SND_SOC_DAPM_SPK("Speaker", sun4i_codec_spk_event),
13478c2ecf20Sopenharmony_ci};
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_route sun4i_codec_card_dapm_routes[] = {
13508c2ecf20Sopenharmony_ci	{ "Speaker", NULL, "HP Right" },
13518c2ecf20Sopenharmony_ci	{ "Speaker", NULL, "HP Left" },
13528c2ecf20Sopenharmony_ci};
13538c2ecf20Sopenharmony_ci
13548c2ecf20Sopenharmony_cistatic struct snd_soc_card *sun4i_codec_create_card(struct device *dev)
13558c2ecf20Sopenharmony_ci{
13568c2ecf20Sopenharmony_ci	struct snd_soc_card *card;
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_ci	card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
13598c2ecf20Sopenharmony_ci	if (!card)
13608c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
13618c2ecf20Sopenharmony_ci
13628c2ecf20Sopenharmony_ci	card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
13638c2ecf20Sopenharmony_ci	if (!card->dai_link)
13648c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_ci	card->dev		= dev;
13678c2ecf20Sopenharmony_ci	card->owner		= THIS_MODULE;
13688c2ecf20Sopenharmony_ci	card->name		= "sun4i-codec";
13698c2ecf20Sopenharmony_ci	card->dapm_widgets	= sun4i_codec_card_dapm_widgets;
13708c2ecf20Sopenharmony_ci	card->num_dapm_widgets	= ARRAY_SIZE(sun4i_codec_card_dapm_widgets);
13718c2ecf20Sopenharmony_ci	card->dapm_routes	= sun4i_codec_card_dapm_routes;
13728c2ecf20Sopenharmony_ci	card->num_dapm_routes	= ARRAY_SIZE(sun4i_codec_card_dapm_routes);
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_ci	return card;
13758c2ecf20Sopenharmony_ci};
13768c2ecf20Sopenharmony_ci
13778c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget sun6i_codec_card_dapm_widgets[] = {
13788c2ecf20Sopenharmony_ci	SND_SOC_DAPM_HP("Headphone", NULL),
13798c2ecf20Sopenharmony_ci	SND_SOC_DAPM_LINE("Line In", NULL),
13808c2ecf20Sopenharmony_ci	SND_SOC_DAPM_LINE("Line Out", NULL),
13818c2ecf20Sopenharmony_ci	SND_SOC_DAPM_MIC("Headset Mic", NULL),
13828c2ecf20Sopenharmony_ci	SND_SOC_DAPM_MIC("Mic", NULL),
13838c2ecf20Sopenharmony_ci	SND_SOC_DAPM_SPK("Speaker", sun4i_codec_spk_event),
13848c2ecf20Sopenharmony_ci};
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_cistatic struct snd_soc_card *sun6i_codec_create_card(struct device *dev)
13878c2ecf20Sopenharmony_ci{
13888c2ecf20Sopenharmony_ci	struct snd_soc_card *card;
13898c2ecf20Sopenharmony_ci	int ret;
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_ci	card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
13928c2ecf20Sopenharmony_ci	if (!card)
13938c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
13948c2ecf20Sopenharmony_ci
13958c2ecf20Sopenharmony_ci	card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
13968c2ecf20Sopenharmony_ci	if (!card->dai_link)
13978c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_ci	card->dev		= dev;
14008c2ecf20Sopenharmony_ci	card->owner		= THIS_MODULE;
14018c2ecf20Sopenharmony_ci	card->name		= "A31 Audio Codec";
14028c2ecf20Sopenharmony_ci	card->dapm_widgets	= sun6i_codec_card_dapm_widgets;
14038c2ecf20Sopenharmony_ci	card->num_dapm_widgets	= ARRAY_SIZE(sun6i_codec_card_dapm_widgets);
14048c2ecf20Sopenharmony_ci	card->fully_routed	= true;
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci	ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing");
14078c2ecf20Sopenharmony_ci	if (ret)
14088c2ecf20Sopenharmony_ci		dev_warn(dev, "failed to parse audio-routing: %d\n", ret);
14098c2ecf20Sopenharmony_ci
14108c2ecf20Sopenharmony_ci	return card;
14118c2ecf20Sopenharmony_ci};
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_ci/* Connect digital side enables to analog side widgets */
14148c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_route sun8i_codec_card_routes[] = {
14158c2ecf20Sopenharmony_ci	/* ADC Routes */
14168c2ecf20Sopenharmony_ci	{ "Left ADC", NULL, "ADC Enable" },
14178c2ecf20Sopenharmony_ci	{ "Right ADC", NULL, "ADC Enable" },
14188c2ecf20Sopenharmony_ci	{ "Codec Capture", NULL, "Left ADC" },
14198c2ecf20Sopenharmony_ci	{ "Codec Capture", NULL, "Right ADC" },
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_ci	/* DAC Routes */
14228c2ecf20Sopenharmony_ci	{ "Left DAC", NULL, "DAC Enable" },
14238c2ecf20Sopenharmony_ci	{ "Right DAC", NULL, "DAC Enable" },
14248c2ecf20Sopenharmony_ci	{ "Left DAC", NULL, "Codec Playback" },
14258c2ecf20Sopenharmony_ci	{ "Right DAC", NULL, "Codec Playback" },
14268c2ecf20Sopenharmony_ci};
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_cistatic struct snd_soc_aux_dev aux_dev = {
14298c2ecf20Sopenharmony_ci	.dlc = COMP_EMPTY(),
14308c2ecf20Sopenharmony_ci};
14318c2ecf20Sopenharmony_ci
14328c2ecf20Sopenharmony_cistatic struct snd_soc_card *sun8i_a23_codec_create_card(struct device *dev)
14338c2ecf20Sopenharmony_ci{
14348c2ecf20Sopenharmony_ci	struct snd_soc_card *card;
14358c2ecf20Sopenharmony_ci	int ret;
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_ci	card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
14388c2ecf20Sopenharmony_ci	if (!card)
14398c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_ci	aux_dev.dlc.of_node = of_parse_phandle(dev->of_node,
14428c2ecf20Sopenharmony_ci						 "allwinner,codec-analog-controls",
14438c2ecf20Sopenharmony_ci						 0);
14448c2ecf20Sopenharmony_ci	if (!aux_dev.dlc.of_node) {
14458c2ecf20Sopenharmony_ci		dev_err(dev, "Can't find analog controls for codec.\n");
14468c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
14478c2ecf20Sopenharmony_ci	}
14488c2ecf20Sopenharmony_ci
14498c2ecf20Sopenharmony_ci	card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
14508c2ecf20Sopenharmony_ci	if (!card->dai_link)
14518c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_ci	card->dev		= dev;
14548c2ecf20Sopenharmony_ci	card->owner		= THIS_MODULE;
14558c2ecf20Sopenharmony_ci	card->name		= "A23 Audio Codec";
14568c2ecf20Sopenharmony_ci	card->dapm_widgets	= sun6i_codec_card_dapm_widgets;
14578c2ecf20Sopenharmony_ci	card->num_dapm_widgets	= ARRAY_SIZE(sun6i_codec_card_dapm_widgets);
14588c2ecf20Sopenharmony_ci	card->dapm_routes	= sun8i_codec_card_routes;
14598c2ecf20Sopenharmony_ci	card->num_dapm_routes	= ARRAY_SIZE(sun8i_codec_card_routes);
14608c2ecf20Sopenharmony_ci	card->aux_dev		= &aux_dev;
14618c2ecf20Sopenharmony_ci	card->num_aux_devs	= 1;
14628c2ecf20Sopenharmony_ci	card->fully_routed	= true;
14638c2ecf20Sopenharmony_ci
14648c2ecf20Sopenharmony_ci	ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing");
14658c2ecf20Sopenharmony_ci	if (ret)
14668c2ecf20Sopenharmony_ci		dev_warn(dev, "failed to parse audio-routing: %d\n", ret);
14678c2ecf20Sopenharmony_ci
14688c2ecf20Sopenharmony_ci	return card;
14698c2ecf20Sopenharmony_ci};
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_cistatic struct snd_soc_card *sun8i_h3_codec_create_card(struct device *dev)
14728c2ecf20Sopenharmony_ci{
14738c2ecf20Sopenharmony_ci	struct snd_soc_card *card;
14748c2ecf20Sopenharmony_ci	int ret;
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_ci	card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
14778c2ecf20Sopenharmony_ci	if (!card)
14788c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_ci	aux_dev.dlc.of_node = of_parse_phandle(dev->of_node,
14818c2ecf20Sopenharmony_ci						 "allwinner,codec-analog-controls",
14828c2ecf20Sopenharmony_ci						 0);
14838c2ecf20Sopenharmony_ci	if (!aux_dev.dlc.of_node) {
14848c2ecf20Sopenharmony_ci		dev_err(dev, "Can't find analog controls for codec.\n");
14858c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
14868c2ecf20Sopenharmony_ci	}
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_ci	card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
14898c2ecf20Sopenharmony_ci	if (!card->dai_link)
14908c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
14918c2ecf20Sopenharmony_ci
14928c2ecf20Sopenharmony_ci	card->dev		= dev;
14938c2ecf20Sopenharmony_ci	card->owner		= THIS_MODULE;
14948c2ecf20Sopenharmony_ci	card->name		= "H3 Audio Codec";
14958c2ecf20Sopenharmony_ci	card->dapm_widgets	= sun6i_codec_card_dapm_widgets;
14968c2ecf20Sopenharmony_ci	card->num_dapm_widgets	= ARRAY_SIZE(sun6i_codec_card_dapm_widgets);
14978c2ecf20Sopenharmony_ci	card->dapm_routes	= sun8i_codec_card_routes;
14988c2ecf20Sopenharmony_ci	card->num_dapm_routes	= ARRAY_SIZE(sun8i_codec_card_routes);
14998c2ecf20Sopenharmony_ci	card->aux_dev		= &aux_dev;
15008c2ecf20Sopenharmony_ci	card->num_aux_devs	= 1;
15018c2ecf20Sopenharmony_ci	card->fully_routed	= true;
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_ci	ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing");
15048c2ecf20Sopenharmony_ci	if (ret)
15058c2ecf20Sopenharmony_ci		dev_warn(dev, "failed to parse audio-routing: %d\n", ret);
15068c2ecf20Sopenharmony_ci
15078c2ecf20Sopenharmony_ci	return card;
15088c2ecf20Sopenharmony_ci};
15098c2ecf20Sopenharmony_ci
15108c2ecf20Sopenharmony_cistatic struct snd_soc_card *sun8i_v3s_codec_create_card(struct device *dev)
15118c2ecf20Sopenharmony_ci{
15128c2ecf20Sopenharmony_ci	struct snd_soc_card *card;
15138c2ecf20Sopenharmony_ci	int ret;
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_ci	card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
15168c2ecf20Sopenharmony_ci	if (!card)
15178c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
15188c2ecf20Sopenharmony_ci
15198c2ecf20Sopenharmony_ci	aux_dev.dlc.of_node = of_parse_phandle(dev->of_node,
15208c2ecf20Sopenharmony_ci						 "allwinner,codec-analog-controls",
15218c2ecf20Sopenharmony_ci						 0);
15228c2ecf20Sopenharmony_ci	if (!aux_dev.dlc.of_node) {
15238c2ecf20Sopenharmony_ci		dev_err(dev, "Can't find analog controls for codec.\n");
15248c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
15258c2ecf20Sopenharmony_ci	}
15268c2ecf20Sopenharmony_ci
15278c2ecf20Sopenharmony_ci	card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
15288c2ecf20Sopenharmony_ci	if (!card->dai_link)
15298c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
15308c2ecf20Sopenharmony_ci
15318c2ecf20Sopenharmony_ci	card->dev		= dev;
15328c2ecf20Sopenharmony_ci	card->owner		= THIS_MODULE;
15338c2ecf20Sopenharmony_ci	card->name		= "V3s Audio Codec";
15348c2ecf20Sopenharmony_ci	card->dapm_widgets	= sun6i_codec_card_dapm_widgets;
15358c2ecf20Sopenharmony_ci	card->num_dapm_widgets	= ARRAY_SIZE(sun6i_codec_card_dapm_widgets);
15368c2ecf20Sopenharmony_ci	card->dapm_routes	= sun8i_codec_card_routes;
15378c2ecf20Sopenharmony_ci	card->num_dapm_routes	= ARRAY_SIZE(sun8i_codec_card_routes);
15388c2ecf20Sopenharmony_ci	card->aux_dev		= &aux_dev;
15398c2ecf20Sopenharmony_ci	card->num_aux_devs	= 1;
15408c2ecf20Sopenharmony_ci	card->fully_routed	= true;
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_ci	ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing");
15438c2ecf20Sopenharmony_ci	if (ret)
15448c2ecf20Sopenharmony_ci		dev_warn(dev, "failed to parse audio-routing: %d\n", ret);
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_ci	return card;
15478c2ecf20Sopenharmony_ci};
15488c2ecf20Sopenharmony_ci
15498c2ecf20Sopenharmony_cistatic const struct regmap_config sun4i_codec_regmap_config = {
15508c2ecf20Sopenharmony_ci	.reg_bits	= 32,
15518c2ecf20Sopenharmony_ci	.reg_stride	= 4,
15528c2ecf20Sopenharmony_ci	.val_bits	= 32,
15538c2ecf20Sopenharmony_ci	.max_register	= SUN4I_CODEC_ADC_RXCNT,
15548c2ecf20Sopenharmony_ci};
15558c2ecf20Sopenharmony_ci
15568c2ecf20Sopenharmony_cistatic const struct regmap_config sun6i_codec_regmap_config = {
15578c2ecf20Sopenharmony_ci	.reg_bits	= 32,
15588c2ecf20Sopenharmony_ci	.reg_stride	= 4,
15598c2ecf20Sopenharmony_ci	.val_bits	= 32,
15608c2ecf20Sopenharmony_ci	.max_register	= SUN6I_CODEC_HMIC_DATA,
15618c2ecf20Sopenharmony_ci};
15628c2ecf20Sopenharmony_ci
15638c2ecf20Sopenharmony_cistatic const struct regmap_config sun7i_codec_regmap_config = {
15648c2ecf20Sopenharmony_ci	.reg_bits	= 32,
15658c2ecf20Sopenharmony_ci	.reg_stride	= 4,
15668c2ecf20Sopenharmony_ci	.val_bits	= 32,
15678c2ecf20Sopenharmony_ci	.max_register	= SUN7I_CODEC_AC_MIC_PHONE_CAL,
15688c2ecf20Sopenharmony_ci};
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_cistatic const struct regmap_config sun8i_a23_codec_regmap_config = {
15718c2ecf20Sopenharmony_ci	.reg_bits	= 32,
15728c2ecf20Sopenharmony_ci	.reg_stride	= 4,
15738c2ecf20Sopenharmony_ci	.val_bits	= 32,
15748c2ecf20Sopenharmony_ci	.max_register	= SUN8I_A23_CODEC_ADC_RXCNT,
15758c2ecf20Sopenharmony_ci};
15768c2ecf20Sopenharmony_ci
15778c2ecf20Sopenharmony_cistatic const struct regmap_config sun8i_h3_codec_regmap_config = {
15788c2ecf20Sopenharmony_ci	.reg_bits	= 32,
15798c2ecf20Sopenharmony_ci	.reg_stride	= 4,
15808c2ecf20Sopenharmony_ci	.val_bits	= 32,
15818c2ecf20Sopenharmony_ci	.max_register	= SUN8I_H3_CODEC_ADC_DBG,
15828c2ecf20Sopenharmony_ci};
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_cistatic const struct regmap_config sun8i_v3s_codec_regmap_config = {
15858c2ecf20Sopenharmony_ci	.reg_bits	= 32,
15868c2ecf20Sopenharmony_ci	.reg_stride	= 4,
15878c2ecf20Sopenharmony_ci	.val_bits	= 32,
15888c2ecf20Sopenharmony_ci	.max_register	= SUN8I_H3_CODEC_ADC_DBG,
15898c2ecf20Sopenharmony_ci};
15908c2ecf20Sopenharmony_ci
15918c2ecf20Sopenharmony_cistruct sun4i_codec_quirks {
15928c2ecf20Sopenharmony_ci	const struct regmap_config *regmap_config;
15938c2ecf20Sopenharmony_ci	const struct snd_soc_component_driver *codec;
15948c2ecf20Sopenharmony_ci	struct snd_soc_card * (*create_card)(struct device *dev);
15958c2ecf20Sopenharmony_ci	struct reg_field reg_adc_fifoc;	/* used for regmap_field */
15968c2ecf20Sopenharmony_ci	unsigned int reg_dac_txdata;	/* TX FIFO offset for DMA config */
15978c2ecf20Sopenharmony_ci	unsigned int reg_adc_rxdata;	/* RX FIFO offset for DMA config */
15988c2ecf20Sopenharmony_ci	bool has_reset;
15998c2ecf20Sopenharmony_ci};
16008c2ecf20Sopenharmony_ci
16018c2ecf20Sopenharmony_cistatic const struct sun4i_codec_quirks sun4i_codec_quirks = {
16028c2ecf20Sopenharmony_ci	.regmap_config	= &sun4i_codec_regmap_config,
16038c2ecf20Sopenharmony_ci	.codec		= &sun4i_codec_codec,
16048c2ecf20Sopenharmony_ci	.create_card	= sun4i_codec_create_card,
16058c2ecf20Sopenharmony_ci	.reg_adc_fifoc	= REG_FIELD(SUN4I_CODEC_ADC_FIFOC, 0, 31),
16068c2ecf20Sopenharmony_ci	.reg_dac_txdata	= SUN4I_CODEC_DAC_TXDATA,
16078c2ecf20Sopenharmony_ci	.reg_adc_rxdata	= SUN4I_CODEC_ADC_RXDATA,
16088c2ecf20Sopenharmony_ci};
16098c2ecf20Sopenharmony_ci
16108c2ecf20Sopenharmony_cistatic const struct sun4i_codec_quirks sun6i_a31_codec_quirks = {
16118c2ecf20Sopenharmony_ci	.regmap_config	= &sun6i_codec_regmap_config,
16128c2ecf20Sopenharmony_ci	.codec		= &sun6i_codec_codec,
16138c2ecf20Sopenharmony_ci	.create_card	= sun6i_codec_create_card,
16148c2ecf20Sopenharmony_ci	.reg_adc_fifoc	= REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31),
16158c2ecf20Sopenharmony_ci	.reg_dac_txdata	= SUN4I_CODEC_DAC_TXDATA,
16168c2ecf20Sopenharmony_ci	.reg_adc_rxdata	= SUN6I_CODEC_ADC_RXDATA,
16178c2ecf20Sopenharmony_ci	.has_reset	= true,
16188c2ecf20Sopenharmony_ci};
16198c2ecf20Sopenharmony_ci
16208c2ecf20Sopenharmony_cistatic const struct sun4i_codec_quirks sun7i_codec_quirks = {
16218c2ecf20Sopenharmony_ci	.regmap_config	= &sun7i_codec_regmap_config,
16228c2ecf20Sopenharmony_ci	.codec		= &sun7i_codec_codec,
16238c2ecf20Sopenharmony_ci	.create_card	= sun4i_codec_create_card,
16248c2ecf20Sopenharmony_ci	.reg_adc_fifoc	= REG_FIELD(SUN4I_CODEC_ADC_FIFOC, 0, 31),
16258c2ecf20Sopenharmony_ci	.reg_dac_txdata	= SUN4I_CODEC_DAC_TXDATA,
16268c2ecf20Sopenharmony_ci	.reg_adc_rxdata	= SUN4I_CODEC_ADC_RXDATA,
16278c2ecf20Sopenharmony_ci};
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_cistatic const struct sun4i_codec_quirks sun8i_a23_codec_quirks = {
16308c2ecf20Sopenharmony_ci	.regmap_config	= &sun8i_a23_codec_regmap_config,
16318c2ecf20Sopenharmony_ci	.codec		= &sun8i_a23_codec_codec,
16328c2ecf20Sopenharmony_ci	.create_card	= sun8i_a23_codec_create_card,
16338c2ecf20Sopenharmony_ci	.reg_adc_fifoc	= REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31),
16348c2ecf20Sopenharmony_ci	.reg_dac_txdata	= SUN4I_CODEC_DAC_TXDATA,
16358c2ecf20Sopenharmony_ci	.reg_adc_rxdata	= SUN6I_CODEC_ADC_RXDATA,
16368c2ecf20Sopenharmony_ci	.has_reset	= true,
16378c2ecf20Sopenharmony_ci};
16388c2ecf20Sopenharmony_ci
16398c2ecf20Sopenharmony_cistatic const struct sun4i_codec_quirks sun8i_h3_codec_quirks = {
16408c2ecf20Sopenharmony_ci	.regmap_config	= &sun8i_h3_codec_regmap_config,
16418c2ecf20Sopenharmony_ci	/*
16428c2ecf20Sopenharmony_ci	 * TODO Share the codec structure with A23 for now.
16438c2ecf20Sopenharmony_ci	 * This should be split out when adding digital audio
16448c2ecf20Sopenharmony_ci	 * processing support for the H3.
16458c2ecf20Sopenharmony_ci	 */
16468c2ecf20Sopenharmony_ci	.codec		= &sun8i_a23_codec_codec,
16478c2ecf20Sopenharmony_ci	.create_card	= sun8i_h3_codec_create_card,
16488c2ecf20Sopenharmony_ci	.reg_adc_fifoc	= REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31),
16498c2ecf20Sopenharmony_ci	.reg_dac_txdata	= SUN8I_H3_CODEC_DAC_TXDATA,
16508c2ecf20Sopenharmony_ci	.reg_adc_rxdata	= SUN6I_CODEC_ADC_RXDATA,
16518c2ecf20Sopenharmony_ci	.has_reset	= true,
16528c2ecf20Sopenharmony_ci};
16538c2ecf20Sopenharmony_ci
16548c2ecf20Sopenharmony_cistatic const struct sun4i_codec_quirks sun8i_v3s_codec_quirks = {
16558c2ecf20Sopenharmony_ci	.regmap_config	= &sun8i_v3s_codec_regmap_config,
16568c2ecf20Sopenharmony_ci	/*
16578c2ecf20Sopenharmony_ci	 * TODO The codec structure should be split out, like
16588c2ecf20Sopenharmony_ci	 * H3, when adding digital audio processing support.
16598c2ecf20Sopenharmony_ci	 */
16608c2ecf20Sopenharmony_ci	.codec		= &sun8i_a23_codec_codec,
16618c2ecf20Sopenharmony_ci	.create_card	= sun8i_v3s_codec_create_card,
16628c2ecf20Sopenharmony_ci	.reg_adc_fifoc	= REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31),
16638c2ecf20Sopenharmony_ci	.reg_dac_txdata	= SUN8I_H3_CODEC_DAC_TXDATA,
16648c2ecf20Sopenharmony_ci	.reg_adc_rxdata	= SUN6I_CODEC_ADC_RXDATA,
16658c2ecf20Sopenharmony_ci	.has_reset	= true,
16668c2ecf20Sopenharmony_ci};
16678c2ecf20Sopenharmony_ci
16688c2ecf20Sopenharmony_cistatic const struct of_device_id sun4i_codec_of_match[] = {
16698c2ecf20Sopenharmony_ci	{
16708c2ecf20Sopenharmony_ci		.compatible = "allwinner,sun4i-a10-codec",
16718c2ecf20Sopenharmony_ci		.data = &sun4i_codec_quirks,
16728c2ecf20Sopenharmony_ci	},
16738c2ecf20Sopenharmony_ci	{
16748c2ecf20Sopenharmony_ci		.compatible = "allwinner,sun6i-a31-codec",
16758c2ecf20Sopenharmony_ci		.data = &sun6i_a31_codec_quirks,
16768c2ecf20Sopenharmony_ci	},
16778c2ecf20Sopenharmony_ci	{
16788c2ecf20Sopenharmony_ci		.compatible = "allwinner,sun7i-a20-codec",
16798c2ecf20Sopenharmony_ci		.data = &sun7i_codec_quirks,
16808c2ecf20Sopenharmony_ci	},
16818c2ecf20Sopenharmony_ci	{
16828c2ecf20Sopenharmony_ci		.compatible = "allwinner,sun8i-a23-codec",
16838c2ecf20Sopenharmony_ci		.data = &sun8i_a23_codec_quirks,
16848c2ecf20Sopenharmony_ci	},
16858c2ecf20Sopenharmony_ci	{
16868c2ecf20Sopenharmony_ci		.compatible = "allwinner,sun8i-h3-codec",
16878c2ecf20Sopenharmony_ci		.data = &sun8i_h3_codec_quirks,
16888c2ecf20Sopenharmony_ci	},
16898c2ecf20Sopenharmony_ci	{
16908c2ecf20Sopenharmony_ci		.compatible = "allwinner,sun8i-v3s-codec",
16918c2ecf20Sopenharmony_ci		.data = &sun8i_v3s_codec_quirks,
16928c2ecf20Sopenharmony_ci	},
16938c2ecf20Sopenharmony_ci	{}
16948c2ecf20Sopenharmony_ci};
16958c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, sun4i_codec_of_match);
16968c2ecf20Sopenharmony_ci
16978c2ecf20Sopenharmony_cistatic int sun4i_codec_probe(struct platform_device *pdev)
16988c2ecf20Sopenharmony_ci{
16998c2ecf20Sopenharmony_ci	struct snd_soc_card *card;
17008c2ecf20Sopenharmony_ci	struct sun4i_codec *scodec;
17018c2ecf20Sopenharmony_ci	const struct sun4i_codec_quirks *quirks;
17028c2ecf20Sopenharmony_ci	struct resource *res;
17038c2ecf20Sopenharmony_ci	void __iomem *base;
17048c2ecf20Sopenharmony_ci	int ret;
17058c2ecf20Sopenharmony_ci
17068c2ecf20Sopenharmony_ci	scodec = devm_kzalloc(&pdev->dev, sizeof(*scodec), GFP_KERNEL);
17078c2ecf20Sopenharmony_ci	if (!scodec)
17088c2ecf20Sopenharmony_ci		return -ENOMEM;
17098c2ecf20Sopenharmony_ci
17108c2ecf20Sopenharmony_ci	scodec->dev = &pdev->dev;
17118c2ecf20Sopenharmony_ci
17128c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
17138c2ecf20Sopenharmony_ci	base = devm_ioremap_resource(&pdev->dev, res);
17148c2ecf20Sopenharmony_ci	if (IS_ERR(base)) {
17158c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to map the registers\n");
17168c2ecf20Sopenharmony_ci		return PTR_ERR(base);
17178c2ecf20Sopenharmony_ci	}
17188c2ecf20Sopenharmony_ci
17198c2ecf20Sopenharmony_ci	quirks = of_device_get_match_data(&pdev->dev);
17208c2ecf20Sopenharmony_ci	if (quirks == NULL) {
17218c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to determine the quirks to use\n");
17228c2ecf20Sopenharmony_ci		return -ENODEV;
17238c2ecf20Sopenharmony_ci	}
17248c2ecf20Sopenharmony_ci
17258c2ecf20Sopenharmony_ci	scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base,
17268c2ecf20Sopenharmony_ci					       quirks->regmap_config);
17278c2ecf20Sopenharmony_ci	if (IS_ERR(scodec->regmap)) {
17288c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to create our regmap\n");
17298c2ecf20Sopenharmony_ci		return PTR_ERR(scodec->regmap);
17308c2ecf20Sopenharmony_ci	}
17318c2ecf20Sopenharmony_ci
17328c2ecf20Sopenharmony_ci	/* Get the clocks from the DT */
17338c2ecf20Sopenharmony_ci	scodec->clk_apb = devm_clk_get(&pdev->dev, "apb");
17348c2ecf20Sopenharmony_ci	if (IS_ERR(scodec->clk_apb)) {
17358c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to get the APB clock\n");
17368c2ecf20Sopenharmony_ci		return PTR_ERR(scodec->clk_apb);
17378c2ecf20Sopenharmony_ci	}
17388c2ecf20Sopenharmony_ci
17398c2ecf20Sopenharmony_ci	scodec->clk_module = devm_clk_get(&pdev->dev, "codec");
17408c2ecf20Sopenharmony_ci	if (IS_ERR(scodec->clk_module)) {
17418c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to get the module clock\n");
17428c2ecf20Sopenharmony_ci		return PTR_ERR(scodec->clk_module);
17438c2ecf20Sopenharmony_ci	}
17448c2ecf20Sopenharmony_ci
17458c2ecf20Sopenharmony_ci	if (quirks->has_reset) {
17468c2ecf20Sopenharmony_ci		scodec->rst = devm_reset_control_get_exclusive(&pdev->dev,
17478c2ecf20Sopenharmony_ci							       NULL);
17488c2ecf20Sopenharmony_ci		if (IS_ERR(scodec->rst)) {
17498c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "Failed to get reset control\n");
17508c2ecf20Sopenharmony_ci			return PTR_ERR(scodec->rst);
17518c2ecf20Sopenharmony_ci		}
17528c2ecf20Sopenharmony_ci	}
17538c2ecf20Sopenharmony_ci
17548c2ecf20Sopenharmony_ci	scodec->gpio_pa = devm_gpiod_get_optional(&pdev->dev, "allwinner,pa",
17558c2ecf20Sopenharmony_ci						  GPIOD_OUT_LOW);
17568c2ecf20Sopenharmony_ci	if (IS_ERR(scodec->gpio_pa)) {
17578c2ecf20Sopenharmony_ci		ret = PTR_ERR(scodec->gpio_pa);
17588c2ecf20Sopenharmony_ci		if (ret != -EPROBE_DEFER)
17598c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "Failed to get pa gpio: %d\n", ret);
17608c2ecf20Sopenharmony_ci		return ret;
17618c2ecf20Sopenharmony_ci	}
17628c2ecf20Sopenharmony_ci
17638c2ecf20Sopenharmony_ci	/* reg_field setup */
17648c2ecf20Sopenharmony_ci	scodec->reg_adc_fifoc = devm_regmap_field_alloc(&pdev->dev,
17658c2ecf20Sopenharmony_ci							scodec->regmap,
17668c2ecf20Sopenharmony_ci							quirks->reg_adc_fifoc);
17678c2ecf20Sopenharmony_ci	if (IS_ERR(scodec->reg_adc_fifoc)) {
17688c2ecf20Sopenharmony_ci		ret = PTR_ERR(scodec->reg_adc_fifoc);
17698c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to create regmap fields: %d\n",
17708c2ecf20Sopenharmony_ci			ret);
17718c2ecf20Sopenharmony_ci		return ret;
17728c2ecf20Sopenharmony_ci	}
17738c2ecf20Sopenharmony_ci
17748c2ecf20Sopenharmony_ci	/* Enable the bus clock */
17758c2ecf20Sopenharmony_ci	if (clk_prepare_enable(scodec->clk_apb)) {
17768c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to enable the APB clock\n");
17778c2ecf20Sopenharmony_ci		return -EINVAL;
17788c2ecf20Sopenharmony_ci	}
17798c2ecf20Sopenharmony_ci
17808c2ecf20Sopenharmony_ci	/* Deassert the reset control */
17818c2ecf20Sopenharmony_ci	if (scodec->rst) {
17828c2ecf20Sopenharmony_ci		ret = reset_control_deassert(scodec->rst);
17838c2ecf20Sopenharmony_ci		if (ret) {
17848c2ecf20Sopenharmony_ci			dev_err(&pdev->dev,
17858c2ecf20Sopenharmony_ci				"Failed to deassert the reset control\n");
17868c2ecf20Sopenharmony_ci			goto err_clk_disable;
17878c2ecf20Sopenharmony_ci		}
17888c2ecf20Sopenharmony_ci	}
17898c2ecf20Sopenharmony_ci
17908c2ecf20Sopenharmony_ci	/* DMA configuration for TX FIFO */
17918c2ecf20Sopenharmony_ci	scodec->playback_dma_data.addr = res->start + quirks->reg_dac_txdata;
17928c2ecf20Sopenharmony_ci	scodec->playback_dma_data.maxburst = 8;
17938c2ecf20Sopenharmony_ci	scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
17948c2ecf20Sopenharmony_ci
17958c2ecf20Sopenharmony_ci	/* DMA configuration for RX FIFO */
17968c2ecf20Sopenharmony_ci	scodec->capture_dma_data.addr = res->start + quirks->reg_adc_rxdata;
17978c2ecf20Sopenharmony_ci	scodec->capture_dma_data.maxburst = 8;
17988c2ecf20Sopenharmony_ci	scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
17998c2ecf20Sopenharmony_ci
18008c2ecf20Sopenharmony_ci	ret = devm_snd_soc_register_component(&pdev->dev, quirks->codec,
18018c2ecf20Sopenharmony_ci				     &sun4i_codec_dai, 1);
18028c2ecf20Sopenharmony_ci	if (ret) {
18038c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to register our codec\n");
18048c2ecf20Sopenharmony_ci		goto err_assert_reset;
18058c2ecf20Sopenharmony_ci	}
18068c2ecf20Sopenharmony_ci
18078c2ecf20Sopenharmony_ci	ret = devm_snd_soc_register_component(&pdev->dev,
18088c2ecf20Sopenharmony_ci					      &sun4i_codec_component,
18098c2ecf20Sopenharmony_ci					      &dummy_cpu_dai, 1);
18108c2ecf20Sopenharmony_ci	if (ret) {
18118c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to register our DAI\n");
18128c2ecf20Sopenharmony_ci		goto err_assert_reset;
18138c2ecf20Sopenharmony_ci	}
18148c2ecf20Sopenharmony_ci
18158c2ecf20Sopenharmony_ci	ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
18168c2ecf20Sopenharmony_ci	if (ret) {
18178c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to register against DMAEngine\n");
18188c2ecf20Sopenharmony_ci		goto err_assert_reset;
18198c2ecf20Sopenharmony_ci	}
18208c2ecf20Sopenharmony_ci
18218c2ecf20Sopenharmony_ci	card = quirks->create_card(&pdev->dev);
18228c2ecf20Sopenharmony_ci	if (IS_ERR(card)) {
18238c2ecf20Sopenharmony_ci		ret = PTR_ERR(card);
18248c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to create our card\n");
18258c2ecf20Sopenharmony_ci		goto err_assert_reset;
18268c2ecf20Sopenharmony_ci	}
18278c2ecf20Sopenharmony_ci
18288c2ecf20Sopenharmony_ci	snd_soc_card_set_drvdata(card, scodec);
18298c2ecf20Sopenharmony_ci
18308c2ecf20Sopenharmony_ci	ret = snd_soc_register_card(card);
18318c2ecf20Sopenharmony_ci	if (ret) {
18328c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to register our card\n");
18338c2ecf20Sopenharmony_ci		goto err_assert_reset;
18348c2ecf20Sopenharmony_ci	}
18358c2ecf20Sopenharmony_ci
18368c2ecf20Sopenharmony_ci	return 0;
18378c2ecf20Sopenharmony_ci
18388c2ecf20Sopenharmony_cierr_assert_reset:
18398c2ecf20Sopenharmony_ci	if (scodec->rst)
18408c2ecf20Sopenharmony_ci		reset_control_assert(scodec->rst);
18418c2ecf20Sopenharmony_cierr_clk_disable:
18428c2ecf20Sopenharmony_ci	clk_disable_unprepare(scodec->clk_apb);
18438c2ecf20Sopenharmony_ci	return ret;
18448c2ecf20Sopenharmony_ci}
18458c2ecf20Sopenharmony_ci
18468c2ecf20Sopenharmony_cistatic int sun4i_codec_remove(struct platform_device *pdev)
18478c2ecf20Sopenharmony_ci{
18488c2ecf20Sopenharmony_ci	struct snd_soc_card *card = platform_get_drvdata(pdev);
18498c2ecf20Sopenharmony_ci	struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card);
18508c2ecf20Sopenharmony_ci
18518c2ecf20Sopenharmony_ci	snd_soc_unregister_card(card);
18528c2ecf20Sopenharmony_ci	if (scodec->rst)
18538c2ecf20Sopenharmony_ci		reset_control_assert(scodec->rst);
18548c2ecf20Sopenharmony_ci	clk_disable_unprepare(scodec->clk_apb);
18558c2ecf20Sopenharmony_ci
18568c2ecf20Sopenharmony_ci	return 0;
18578c2ecf20Sopenharmony_ci}
18588c2ecf20Sopenharmony_ci
18598c2ecf20Sopenharmony_cistatic struct platform_driver sun4i_codec_driver = {
18608c2ecf20Sopenharmony_ci	.driver = {
18618c2ecf20Sopenharmony_ci		.name = "sun4i-codec",
18628c2ecf20Sopenharmony_ci		.of_match_table = sun4i_codec_of_match,
18638c2ecf20Sopenharmony_ci	},
18648c2ecf20Sopenharmony_ci	.probe = sun4i_codec_probe,
18658c2ecf20Sopenharmony_ci	.remove = sun4i_codec_remove,
18668c2ecf20Sopenharmony_ci};
18678c2ecf20Sopenharmony_cimodule_platform_driver(sun4i_codec_driver);
18688c2ecf20Sopenharmony_ci
18698c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Allwinner A10 codec driver");
18708c2ecf20Sopenharmony_ciMODULE_AUTHOR("Emilio López <emilio@elopez.com.ar>");
18718c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>");
18728c2ecf20Sopenharmony_ciMODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
18738c2ecf20Sopenharmony_ciMODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
18748c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1875