162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2014 Emilio López <emilio@elopez.com.ar> 462306a36Sopenharmony_ci * Copyright 2014 Jon Smirl <jonsmirl@gmail.com> 562306a36Sopenharmony_ci * Copyright 2015 Maxime Ripard <maxime.ripard@free-electrons.com> 662306a36Sopenharmony_ci * Copyright 2015 Adam Sampson <ats@offog.org> 762306a36Sopenharmony_ci * Copyright 2016 Chen-Yu Tsai <wens@csie.org> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Based on the Allwinner SDK driver, released under the GPL. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/delay.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/of.h> 1962306a36Sopenharmony_ci#include <linux/of_address.h> 2062306a36Sopenharmony_ci#include <linux/of_device.h> 2162306a36Sopenharmony_ci#include <linux/of_platform.h> 2262306a36Sopenharmony_ci#include <linux/clk.h> 2362306a36Sopenharmony_ci#include <linux/regmap.h> 2462306a36Sopenharmony_ci#include <linux/reset.h> 2562306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <sound/core.h> 2862306a36Sopenharmony_ci#include <sound/pcm.h> 2962306a36Sopenharmony_ci#include <sound/pcm_params.h> 3062306a36Sopenharmony_ci#include <sound/soc.h> 3162306a36Sopenharmony_ci#include <sound/tlv.h> 3262306a36Sopenharmony_ci#include <sound/initval.h> 3362306a36Sopenharmony_ci#include <sound/dmaengine_pcm.h> 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* Codec DAC digital controls and FIFO registers */ 3662306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_DPC (0x00) 3762306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_DPC_EN_DA (31) 3862306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_DPC_DVOL (12) 3962306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_FIFOC (0x04) 4062306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_FIFOC_DAC_FS (29) 4162306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_FIFOC_FIR_VERSION (28) 4262306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_FIFOC_SEND_LASAT (26) 4362306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE (24) 4462306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT (21) 4562306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL (8) 4662306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_FIFOC_MONO_EN (6) 4762306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS (5) 4862306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN (4) 4962306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH (0) 5062306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_FIFOS (0x08) 5162306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_TXDATA (0x0c) 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* Codec DAC side analog signal controls */ 5462306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL (0x10) 5562306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_DACAENR (31) 5662306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_DACAENL (30) 5762306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_MIXEN (29) 5862306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_LNG (26) 5962306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_FMG (23) 6062306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_MICG (20) 6162306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_LLNS (19) 6262306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_RLNS (18) 6362306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_LFMS (17) 6462306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_RFMS (16) 6562306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_LDACLMIXS (15) 6662306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_RDACRMIXS (14) 6762306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_LDACRMIXS (13) 6862306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_MIC1LS (12) 6962306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_MIC1RS (11) 7062306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_MIC2LS (10) 7162306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_MIC2RS (9) 7262306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_DACPAS (8) 7362306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_MIXPAS (7) 7462306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_PA_MUTE (6) 7562306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_ACTL_PA_VOL (0) 7662306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_TUNE (0x14) 7762306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_DEBUG (0x18) 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* Codec ADC digital controls and FIFO registers */ 8062306a36Sopenharmony_ci#define SUN4I_CODEC_ADC_FIFOC (0x1c) 8162306a36Sopenharmony_ci#define SUN4I_CODEC_ADC_FIFOC_ADC_FS (29) 8262306a36Sopenharmony_ci#define SUN4I_CODEC_ADC_FIFOC_EN_AD (28) 8362306a36Sopenharmony_ci#define SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE (24) 8462306a36Sopenharmony_ci#define SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL (8) 8562306a36Sopenharmony_ci#define SUN4I_CODEC_ADC_FIFOC_MONO_EN (7) 8662306a36Sopenharmony_ci#define SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS (6) 8762306a36Sopenharmony_ci#define SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN (4) 8862306a36Sopenharmony_ci#define SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH (0) 8962306a36Sopenharmony_ci#define SUN4I_CODEC_ADC_FIFOS (0x20) 9062306a36Sopenharmony_ci#define SUN4I_CODEC_ADC_RXDATA (0x24) 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/* Codec ADC side analog signal controls */ 9362306a36Sopenharmony_ci#define SUN4I_CODEC_ADC_ACTL (0x28) 9462306a36Sopenharmony_ci#define SUN4I_CODEC_ADC_ACTL_ADC_R_EN (31) 9562306a36Sopenharmony_ci#define SUN4I_CODEC_ADC_ACTL_ADC_L_EN (30) 9662306a36Sopenharmony_ci#define SUN4I_CODEC_ADC_ACTL_PREG1EN (29) 9762306a36Sopenharmony_ci#define SUN4I_CODEC_ADC_ACTL_PREG2EN (28) 9862306a36Sopenharmony_ci#define SUN4I_CODEC_ADC_ACTL_VMICEN (27) 9962306a36Sopenharmony_ci#define SUN4I_CODEC_ADC_ACTL_PREG1 (25) 10062306a36Sopenharmony_ci#define SUN4I_CODEC_ADC_ACTL_PREG2 (23) 10162306a36Sopenharmony_ci#define SUN4I_CODEC_ADC_ACTL_VADCG (20) 10262306a36Sopenharmony_ci#define SUN4I_CODEC_ADC_ACTL_ADCIS (17) 10362306a36Sopenharmony_ci#define SUN4I_CODEC_ADC_ACTL_LNPREG (13) 10462306a36Sopenharmony_ci#define SUN4I_CODEC_ADC_ACTL_PA_EN (4) 10562306a36Sopenharmony_ci#define SUN4I_CODEC_ADC_ACTL_DDE (3) 10662306a36Sopenharmony_ci#define SUN4I_CODEC_ADC_DEBUG (0x2c) 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci/* FIFO counters */ 10962306a36Sopenharmony_ci#define SUN4I_CODEC_DAC_TXCNT (0x30) 11062306a36Sopenharmony_ci#define SUN4I_CODEC_ADC_RXCNT (0x34) 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci/* Calibration register (sun7i only) */ 11362306a36Sopenharmony_ci#define SUN7I_CODEC_AC_DAC_CAL (0x38) 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/* Microphone controls (sun7i only) */ 11662306a36Sopenharmony_ci#define SUN7I_CODEC_AC_MIC_PHONE_CAL (0x3c) 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci#define SUN7I_CODEC_AC_MIC_PHONE_CAL_PREG1 (29) 11962306a36Sopenharmony_ci#define SUN7I_CODEC_AC_MIC_PHONE_CAL_PREG2 (26) 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/* 12262306a36Sopenharmony_ci * sun6i specific registers 12362306a36Sopenharmony_ci * 12462306a36Sopenharmony_ci * sun6i shares the same digital control and FIFO registers as sun4i, 12562306a36Sopenharmony_ci * but only the DAC digital controls are at the same offset. The others 12662306a36Sopenharmony_ci * have been moved around to accommodate extra analog controls. 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci/* Codec DAC digital controls and FIFO registers */ 13062306a36Sopenharmony_ci#define SUN6I_CODEC_ADC_FIFOC (0x10) 13162306a36Sopenharmony_ci#define SUN6I_CODEC_ADC_FIFOC_EN_AD (28) 13262306a36Sopenharmony_ci#define SUN6I_CODEC_ADC_FIFOS (0x14) 13362306a36Sopenharmony_ci#define SUN6I_CODEC_ADC_RXDATA (0x18) 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/* Output mixer and gain controls */ 13662306a36Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL (0x20) 13762306a36Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_DACAREN (31) 13862306a36Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_DACALEN (30) 13962306a36Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_RMIXEN (29) 14062306a36Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_LMIXEN (28) 14162306a36Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC1 (23) 14262306a36Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC2 (22) 14362306a36Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_PHONE (21) 14462306a36Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_PHONEP (20) 14562306a36Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_LINEINR (19) 14662306a36Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACR (18) 14762306a36Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACL (17) 14862306a36Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC1 (16) 14962306a36Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC2 (15) 15062306a36Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_PHONE (14) 15162306a36Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_PHONEN (13) 15262306a36Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_LINEINL (12) 15362306a36Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACL (11) 15462306a36Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACR (10) 15562306a36Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_RHPIS (9) 15662306a36Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_LHPIS (8) 15762306a36Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE (7) 15862306a36Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE (6) 15962306a36Sopenharmony_ci#define SUN6I_CODEC_OM_DACA_CTRL_HPVOL (0) 16062306a36Sopenharmony_ci#define SUN6I_CODEC_OM_PA_CTRL (0x24) 16162306a36Sopenharmony_ci#define SUN6I_CODEC_OM_PA_CTRL_HPPAEN (31) 16262306a36Sopenharmony_ci#define SUN6I_CODEC_OM_PA_CTRL_HPCOM_CTL (29) 16362306a36Sopenharmony_ci#define SUN6I_CODEC_OM_PA_CTRL_COMPTEN (28) 16462306a36Sopenharmony_ci#define SUN6I_CODEC_OM_PA_CTRL_MIC1G (15) 16562306a36Sopenharmony_ci#define SUN6I_CODEC_OM_PA_CTRL_MIC2G (12) 16662306a36Sopenharmony_ci#define SUN6I_CODEC_OM_PA_CTRL_LINEING (9) 16762306a36Sopenharmony_ci#define SUN6I_CODEC_OM_PA_CTRL_PHONEG (6) 16862306a36Sopenharmony_ci#define SUN6I_CODEC_OM_PA_CTRL_PHONEPG (3) 16962306a36Sopenharmony_ci#define SUN6I_CODEC_OM_PA_CTRL_PHONENG (0) 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci/* Microphone, line out and phone out controls */ 17262306a36Sopenharmony_ci#define SUN6I_CODEC_MIC_CTRL (0x28) 17362306a36Sopenharmony_ci#define SUN6I_CODEC_MIC_CTRL_HBIASEN (31) 17462306a36Sopenharmony_ci#define SUN6I_CODEC_MIC_CTRL_MBIASEN (30) 17562306a36Sopenharmony_ci#define SUN6I_CODEC_MIC_CTRL_MIC1AMPEN (28) 17662306a36Sopenharmony_ci#define SUN6I_CODEC_MIC_CTRL_MIC1BOOST (25) 17762306a36Sopenharmony_ci#define SUN6I_CODEC_MIC_CTRL_MIC2AMPEN (24) 17862306a36Sopenharmony_ci#define SUN6I_CODEC_MIC_CTRL_MIC2BOOST (21) 17962306a36Sopenharmony_ci#define SUN6I_CODEC_MIC_CTRL_MIC2SLT (20) 18062306a36Sopenharmony_ci#define SUN6I_CODEC_MIC_CTRL_LINEOUTLEN (19) 18162306a36Sopenharmony_ci#define SUN6I_CODEC_MIC_CTRL_LINEOUTREN (18) 18262306a36Sopenharmony_ci#define SUN6I_CODEC_MIC_CTRL_LINEOUTLSRC (17) 18362306a36Sopenharmony_ci#define SUN6I_CODEC_MIC_CTRL_LINEOUTRSRC (16) 18462306a36Sopenharmony_ci#define SUN6I_CODEC_MIC_CTRL_LINEOUTVC (11) 18562306a36Sopenharmony_ci#define SUN6I_CODEC_MIC_CTRL_PHONEPREG (8) 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci/* ADC mixer controls */ 18862306a36Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL (0x2c) 18962306a36Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_ADCREN (31) 19062306a36Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_ADCLEN (30) 19162306a36Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_ADCRG (27) 19262306a36Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_ADCLG (24) 19362306a36Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC1 (13) 19462306a36Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC2 (12) 19562306a36Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_RADCMIX_PHONE (11) 19662306a36Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_RADCMIX_PHONEP (10) 19762306a36Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_RADCMIX_LINEINR (9) 19862306a36Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXR (8) 19962306a36Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXL (7) 20062306a36Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC1 (6) 20162306a36Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC2 (5) 20262306a36Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_LADCMIX_PHONE (4) 20362306a36Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_LADCMIX_PHONEN (3) 20462306a36Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_LADCMIX_LINEINL (2) 20562306a36Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXL (1) 20662306a36Sopenharmony_ci#define SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXR (0) 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci/* Analog performance tuning controls */ 20962306a36Sopenharmony_ci#define SUN6I_CODEC_ADDA_TUNE (0x30) 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci/* Calibration controls */ 21262306a36Sopenharmony_ci#define SUN6I_CODEC_CALIBRATION (0x34) 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci/* FIFO counters */ 21562306a36Sopenharmony_ci#define SUN6I_CODEC_DAC_TXCNT (0x40) 21662306a36Sopenharmony_ci#define SUN6I_CODEC_ADC_RXCNT (0x44) 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci/* headset jack detection and button support registers */ 21962306a36Sopenharmony_ci#define SUN6I_CODEC_HMIC_CTL (0x50) 22062306a36Sopenharmony_ci#define SUN6I_CODEC_HMIC_DATA (0x54) 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci/* TODO sun6i DAP (Digital Audio Processing) bits */ 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci/* FIFO counters moved on A23 */ 22562306a36Sopenharmony_ci#define SUN8I_A23_CODEC_DAC_TXCNT (0x1c) 22662306a36Sopenharmony_ci#define SUN8I_A23_CODEC_ADC_RXCNT (0x20) 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci/* TX FIFO moved on H3 */ 22962306a36Sopenharmony_ci#define SUN8I_H3_CODEC_DAC_TXDATA (0x20) 23062306a36Sopenharmony_ci#define SUN8I_H3_CODEC_DAC_DBG (0x48) 23162306a36Sopenharmony_ci#define SUN8I_H3_CODEC_ADC_DBG (0x4c) 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci/* TODO H3 DAP (Digital Audio Processing) bits */ 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistruct sun4i_codec { 23662306a36Sopenharmony_ci struct device *dev; 23762306a36Sopenharmony_ci struct regmap *regmap; 23862306a36Sopenharmony_ci struct clk *clk_apb; 23962306a36Sopenharmony_ci struct clk *clk_module; 24062306a36Sopenharmony_ci struct reset_control *rst; 24162306a36Sopenharmony_ci struct gpio_desc *gpio_pa; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci /* ADC_FIFOC register is at different offset on different SoCs */ 24462306a36Sopenharmony_ci struct regmap_field *reg_adc_fifoc; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci struct snd_dmaengine_dai_dma_data capture_dma_data; 24762306a36Sopenharmony_ci struct snd_dmaengine_dai_dma_data playback_dma_data; 24862306a36Sopenharmony_ci}; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic void sun4i_codec_start_playback(struct sun4i_codec *scodec) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci /* Flush TX FIFO */ 25362306a36Sopenharmony_ci regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, 25462306a36Sopenharmony_ci BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH)); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* Enable DAC DRQ */ 25762306a36Sopenharmony_ci regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, 25862306a36Sopenharmony_ci BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN)); 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic void sun4i_codec_stop_playback(struct sun4i_codec *scodec) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci /* Disable DAC DRQ */ 26462306a36Sopenharmony_ci regmap_clear_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, 26562306a36Sopenharmony_ci BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN)); 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic void sun4i_codec_start_capture(struct sun4i_codec *scodec) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci /* Enable ADC DRQ */ 27162306a36Sopenharmony_ci regmap_field_set_bits(scodec->reg_adc_fifoc, 27262306a36Sopenharmony_ci BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN)); 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic void sun4i_codec_stop_capture(struct sun4i_codec *scodec) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci /* Disable ADC DRQ */ 27862306a36Sopenharmony_ci regmap_field_clear_bits(scodec->reg_adc_fifoc, 27962306a36Sopenharmony_ci BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN)); 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic int sun4i_codec_trigger(struct snd_pcm_substream *substream, int cmd, 28362306a36Sopenharmony_ci struct snd_soc_dai *dai) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 28662306a36Sopenharmony_ci struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci switch (cmd) { 28962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 29062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 29162306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 29262306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 29362306a36Sopenharmony_ci sun4i_codec_start_playback(scodec); 29462306a36Sopenharmony_ci else 29562306a36Sopenharmony_ci sun4i_codec_start_capture(scodec); 29662306a36Sopenharmony_ci break; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 29962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 30062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 30162306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 30262306a36Sopenharmony_ci sun4i_codec_stop_playback(scodec); 30362306a36Sopenharmony_ci else 30462306a36Sopenharmony_ci sun4i_codec_stop_capture(scodec); 30562306a36Sopenharmony_ci break; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci default: 30862306a36Sopenharmony_ci return -EINVAL; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci return 0; 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream, 31562306a36Sopenharmony_ci struct snd_soc_dai *dai) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 31862306a36Sopenharmony_ci struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci /* Flush RX FIFO */ 32262306a36Sopenharmony_ci regmap_field_set_bits(scodec->reg_adc_fifoc, 32362306a36Sopenharmony_ci BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH)); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci /* Set RX FIFO trigger level */ 32762306a36Sopenharmony_ci regmap_field_update_bits(scodec->reg_adc_fifoc, 32862306a36Sopenharmony_ci 0xf << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL, 32962306a36Sopenharmony_ci 0x7 << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci /* 33262306a36Sopenharmony_ci * FIXME: Undocumented in the datasheet, but 33362306a36Sopenharmony_ci * Allwinner's code mentions that it is 33462306a36Sopenharmony_ci * related to microphone gain 33562306a36Sopenharmony_ci */ 33662306a36Sopenharmony_ci if (of_device_is_compatible(scodec->dev->of_node, 33762306a36Sopenharmony_ci "allwinner,sun4i-a10-codec") || 33862306a36Sopenharmony_ci of_device_is_compatible(scodec->dev->of_node, 33962306a36Sopenharmony_ci "allwinner,sun7i-a20-codec")) { 34062306a36Sopenharmony_ci regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_ACTL, 34162306a36Sopenharmony_ci 0x3 << 25, 34262306a36Sopenharmony_ci 0x1 << 25); 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (of_device_is_compatible(scodec->dev->of_node, 34662306a36Sopenharmony_ci "allwinner,sun7i-a20-codec")) 34762306a36Sopenharmony_ci /* FIXME: Undocumented bits */ 34862306a36Sopenharmony_ci regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_TUNE, 34962306a36Sopenharmony_ci 0x3 << 8, 35062306a36Sopenharmony_ci 0x1 << 8); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci return 0; 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic int sun4i_codec_prepare_playback(struct snd_pcm_substream *substream, 35662306a36Sopenharmony_ci struct snd_soc_dai *dai) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 35962306a36Sopenharmony_ci struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card); 36062306a36Sopenharmony_ci u32 val; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* Flush the TX FIFO */ 36362306a36Sopenharmony_ci regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, 36462306a36Sopenharmony_ci BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH)); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci /* Set TX FIFO Empty Trigger Level */ 36762306a36Sopenharmony_ci regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, 36862306a36Sopenharmony_ci 0x3f << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL, 36962306a36Sopenharmony_ci 0xf << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (substream->runtime->rate > 32000) 37262306a36Sopenharmony_ci /* Use 64 bits FIR filter */ 37362306a36Sopenharmony_ci val = 0; 37462306a36Sopenharmony_ci else 37562306a36Sopenharmony_ci /* Use 32 bits FIR filter */ 37662306a36Sopenharmony_ci val = BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, 37962306a36Sopenharmony_ci BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION), 38062306a36Sopenharmony_ci val); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci /* Send zeros when we have an underrun */ 38362306a36Sopenharmony_ci regmap_clear_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, 38462306a36Sopenharmony_ci BIT(SUN4I_CODEC_DAC_FIFOC_SEND_LASAT)); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci return 0; 38762306a36Sopenharmony_ci}; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cistatic int sun4i_codec_prepare(struct snd_pcm_substream *substream, 39062306a36Sopenharmony_ci struct snd_soc_dai *dai) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 39362306a36Sopenharmony_ci return sun4i_codec_prepare_playback(substream, dai); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci return sun4i_codec_prepare_capture(substream, dai); 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistatic unsigned long sun4i_codec_get_mod_freq(struct snd_pcm_hw_params *params) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci unsigned int rate = params_rate(params); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci switch (rate) { 40362306a36Sopenharmony_ci case 176400: 40462306a36Sopenharmony_ci case 88200: 40562306a36Sopenharmony_ci case 44100: 40662306a36Sopenharmony_ci case 33075: 40762306a36Sopenharmony_ci case 22050: 40862306a36Sopenharmony_ci case 14700: 40962306a36Sopenharmony_ci case 11025: 41062306a36Sopenharmony_ci case 7350: 41162306a36Sopenharmony_ci return 22579200; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci case 192000: 41462306a36Sopenharmony_ci case 96000: 41562306a36Sopenharmony_ci case 48000: 41662306a36Sopenharmony_ci case 32000: 41762306a36Sopenharmony_ci case 24000: 41862306a36Sopenharmony_ci case 16000: 41962306a36Sopenharmony_ci case 12000: 42062306a36Sopenharmony_ci case 8000: 42162306a36Sopenharmony_ci return 24576000; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci default: 42462306a36Sopenharmony_ci return 0; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_cistatic int sun4i_codec_get_hw_rate(struct snd_pcm_hw_params *params) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci unsigned int rate = params_rate(params); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci switch (rate) { 43362306a36Sopenharmony_ci case 192000: 43462306a36Sopenharmony_ci case 176400: 43562306a36Sopenharmony_ci return 6; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci case 96000: 43862306a36Sopenharmony_ci case 88200: 43962306a36Sopenharmony_ci return 7; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci case 48000: 44262306a36Sopenharmony_ci case 44100: 44362306a36Sopenharmony_ci return 0; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci case 32000: 44662306a36Sopenharmony_ci case 33075: 44762306a36Sopenharmony_ci return 1; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci case 24000: 45062306a36Sopenharmony_ci case 22050: 45162306a36Sopenharmony_ci return 2; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci case 16000: 45462306a36Sopenharmony_ci case 14700: 45562306a36Sopenharmony_ci return 3; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci case 12000: 45862306a36Sopenharmony_ci case 11025: 45962306a36Sopenharmony_ci return 4; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci case 8000: 46262306a36Sopenharmony_ci case 7350: 46362306a36Sopenharmony_ci return 5; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci default: 46662306a36Sopenharmony_ci return -EINVAL; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_cistatic int sun4i_codec_hw_params_capture(struct sun4i_codec *scodec, 47162306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 47262306a36Sopenharmony_ci unsigned int hwrate) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci /* Set ADC sample rate */ 47562306a36Sopenharmony_ci regmap_field_update_bits(scodec->reg_adc_fifoc, 47662306a36Sopenharmony_ci 7 << SUN4I_CODEC_ADC_FIFOC_ADC_FS, 47762306a36Sopenharmony_ci hwrate << SUN4I_CODEC_ADC_FIFOC_ADC_FS); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci /* Set the number of channels we want to use */ 48062306a36Sopenharmony_ci if (params_channels(params) == 1) 48162306a36Sopenharmony_ci regmap_field_set_bits(scodec->reg_adc_fifoc, 48262306a36Sopenharmony_ci BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN)); 48362306a36Sopenharmony_ci else 48462306a36Sopenharmony_ci regmap_field_clear_bits(scodec->reg_adc_fifoc, 48562306a36Sopenharmony_ci BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN)); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci /* Set the number of sample bits to either 16 or 24 bits */ 48862306a36Sopenharmony_ci if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min == 32) { 48962306a36Sopenharmony_ci regmap_field_set_bits(scodec->reg_adc_fifoc, 49062306a36Sopenharmony_ci BIT(SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS)); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci regmap_field_clear_bits(scodec->reg_adc_fifoc, 49362306a36Sopenharmony_ci BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE)); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 49662306a36Sopenharmony_ci } else { 49762306a36Sopenharmony_ci regmap_field_clear_bits(scodec->reg_adc_fifoc, 49862306a36Sopenharmony_ci BIT(SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS)); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci /* Fill most significant bits with valid data MSB */ 50162306a36Sopenharmony_ci regmap_field_set_bits(scodec->reg_adc_fifoc, 50262306a36Sopenharmony_ci BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE)); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci return 0; 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic int sun4i_codec_hw_params_playback(struct sun4i_codec *scodec, 51162306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 51262306a36Sopenharmony_ci unsigned int hwrate) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci u32 val; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci /* Set DAC sample rate */ 51762306a36Sopenharmony_ci regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, 51862306a36Sopenharmony_ci 7 << SUN4I_CODEC_DAC_FIFOC_DAC_FS, 51962306a36Sopenharmony_ci hwrate << SUN4I_CODEC_DAC_FIFOC_DAC_FS); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci /* Set the number of channels we want to use */ 52262306a36Sopenharmony_ci if (params_channels(params) == 1) 52362306a36Sopenharmony_ci val = BIT(SUN4I_CODEC_DAC_FIFOC_MONO_EN); 52462306a36Sopenharmony_ci else 52562306a36Sopenharmony_ci val = 0; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, 52862306a36Sopenharmony_ci BIT(SUN4I_CODEC_DAC_FIFOC_MONO_EN), 52962306a36Sopenharmony_ci val); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci /* Set the number of sample bits to either 16 or 24 bits */ 53262306a36Sopenharmony_ci if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min == 32) { 53362306a36Sopenharmony_ci regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, 53462306a36Sopenharmony_ci BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS)); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci /* Set TX FIFO mode to padding the LSBs with 0 */ 53762306a36Sopenharmony_ci regmap_clear_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, 53862306a36Sopenharmony_ci BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE)); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 54162306a36Sopenharmony_ci } else { 54262306a36Sopenharmony_ci regmap_clear_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, 54362306a36Sopenharmony_ci BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS)); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci /* Set TX FIFO mode to repeat the MSB */ 54662306a36Sopenharmony_ci regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, 54762306a36Sopenharmony_ci BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE)); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci return 0; 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_cistatic int sun4i_codec_hw_params(struct snd_pcm_substream *substream, 55662306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 55762306a36Sopenharmony_ci struct snd_soc_dai *dai) 55862306a36Sopenharmony_ci{ 55962306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 56062306a36Sopenharmony_ci struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card); 56162306a36Sopenharmony_ci unsigned long clk_freq; 56262306a36Sopenharmony_ci int ret, hwrate; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci clk_freq = sun4i_codec_get_mod_freq(params); 56562306a36Sopenharmony_ci if (!clk_freq) 56662306a36Sopenharmony_ci return -EINVAL; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci ret = clk_set_rate(scodec->clk_module, clk_freq); 56962306a36Sopenharmony_ci if (ret) 57062306a36Sopenharmony_ci return ret; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci hwrate = sun4i_codec_get_hw_rate(params); 57362306a36Sopenharmony_ci if (hwrate < 0) 57462306a36Sopenharmony_ci return hwrate; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 57762306a36Sopenharmony_ci return sun4i_codec_hw_params_playback(scodec, params, 57862306a36Sopenharmony_ci hwrate); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci return sun4i_codec_hw_params_capture(scodec, params, 58162306a36Sopenharmony_ci hwrate); 58262306a36Sopenharmony_ci} 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_cistatic unsigned int sun4i_codec_src_rates[] = { 58662306a36Sopenharmony_ci 8000, 11025, 12000, 16000, 22050, 24000, 32000, 58762306a36Sopenharmony_ci 44100, 48000, 96000, 192000 58862306a36Sopenharmony_ci}; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_cistatic struct snd_pcm_hw_constraint_list sun4i_codec_constraints = { 59262306a36Sopenharmony_ci .count = ARRAY_SIZE(sun4i_codec_src_rates), 59362306a36Sopenharmony_ci .list = sun4i_codec_src_rates, 59462306a36Sopenharmony_ci}; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_cistatic int sun4i_codec_startup(struct snd_pcm_substream *substream, 59862306a36Sopenharmony_ci struct snd_soc_dai *dai) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 60162306a36Sopenharmony_ci struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci snd_pcm_hw_constraint_list(substream->runtime, 0, 60462306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, &sun4i_codec_constraints); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci /* 60762306a36Sopenharmony_ci * Stop issuing DRQ when we have room for less than 16 samples 60862306a36Sopenharmony_ci * in our TX FIFO 60962306a36Sopenharmony_ci */ 61062306a36Sopenharmony_ci regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, 61162306a36Sopenharmony_ci 3 << SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci return clk_prepare_enable(scodec->clk_module); 61462306a36Sopenharmony_ci} 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_cistatic void sun4i_codec_shutdown(struct snd_pcm_substream *substream, 61762306a36Sopenharmony_ci struct snd_soc_dai *dai) 61862306a36Sopenharmony_ci{ 61962306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 62062306a36Sopenharmony_ci struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci clk_disable_unprepare(scodec->clk_module); 62362306a36Sopenharmony_ci} 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_cistatic const struct snd_soc_dai_ops sun4i_codec_dai_ops = { 62662306a36Sopenharmony_ci .startup = sun4i_codec_startup, 62762306a36Sopenharmony_ci .shutdown = sun4i_codec_shutdown, 62862306a36Sopenharmony_ci .trigger = sun4i_codec_trigger, 62962306a36Sopenharmony_ci .hw_params = sun4i_codec_hw_params, 63062306a36Sopenharmony_ci .prepare = sun4i_codec_prepare, 63162306a36Sopenharmony_ci}; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cistatic struct snd_soc_dai_driver sun4i_codec_dai = { 63462306a36Sopenharmony_ci .name = "Codec", 63562306a36Sopenharmony_ci .ops = &sun4i_codec_dai_ops, 63662306a36Sopenharmony_ci .playback = { 63762306a36Sopenharmony_ci .stream_name = "Codec Playback", 63862306a36Sopenharmony_ci .channels_min = 1, 63962306a36Sopenharmony_ci .channels_max = 2, 64062306a36Sopenharmony_ci .rate_min = 8000, 64162306a36Sopenharmony_ci .rate_max = 192000, 64262306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS, 64362306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | 64462306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE, 64562306a36Sopenharmony_ci .sig_bits = 24, 64662306a36Sopenharmony_ci }, 64762306a36Sopenharmony_ci .capture = { 64862306a36Sopenharmony_ci .stream_name = "Codec Capture", 64962306a36Sopenharmony_ci .channels_min = 1, 65062306a36Sopenharmony_ci .channels_max = 2, 65162306a36Sopenharmony_ci .rate_min = 8000, 65262306a36Sopenharmony_ci .rate_max = 48000, 65362306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS, 65462306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | 65562306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE, 65662306a36Sopenharmony_ci .sig_bits = 24, 65762306a36Sopenharmony_ci }, 65862306a36Sopenharmony_ci}; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci/*** sun4i Codec ***/ 66162306a36Sopenharmony_cistatic const struct snd_kcontrol_new sun4i_codec_pa_mute = 66262306a36Sopenharmony_ci SOC_DAPM_SINGLE("Switch", SUN4I_CODEC_DAC_ACTL, 66362306a36Sopenharmony_ci SUN4I_CODEC_DAC_ACTL_PA_MUTE, 1, 0); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_cistatic DECLARE_TLV_DB_SCALE(sun4i_codec_pa_volume_scale, -6300, 100, 1); 66662306a36Sopenharmony_cistatic DECLARE_TLV_DB_SCALE(sun4i_codec_linein_loopback_gain_scale, -150, 150, 66762306a36Sopenharmony_ci 0); 66862306a36Sopenharmony_cistatic DECLARE_TLV_DB_SCALE(sun4i_codec_linein_preamp_gain_scale, -1200, 300, 66962306a36Sopenharmony_ci 0); 67062306a36Sopenharmony_cistatic DECLARE_TLV_DB_SCALE(sun4i_codec_fmin_loopback_gain_scale, -450, 150, 67162306a36Sopenharmony_ci 0); 67262306a36Sopenharmony_cistatic DECLARE_TLV_DB_SCALE(sun4i_codec_micin_loopback_gain_scale, -450, 150, 67362306a36Sopenharmony_ci 0); 67462306a36Sopenharmony_cistatic DECLARE_TLV_DB_RANGE(sun4i_codec_micin_preamp_gain_scale, 67562306a36Sopenharmony_ci 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), 67662306a36Sopenharmony_ci 1, 7, TLV_DB_SCALE_ITEM(3500, 300, 0)); 67762306a36Sopenharmony_cistatic DECLARE_TLV_DB_RANGE(sun7i_codec_micin_preamp_gain_scale, 67862306a36Sopenharmony_ci 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), 67962306a36Sopenharmony_ci 1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0)); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_cistatic const struct snd_kcontrol_new sun4i_codec_controls[] = { 68262306a36Sopenharmony_ci SOC_SINGLE_TLV("Power Amplifier Volume", SUN4I_CODEC_DAC_ACTL, 68362306a36Sopenharmony_ci SUN4I_CODEC_DAC_ACTL_PA_VOL, 0x3F, 0, 68462306a36Sopenharmony_ci sun4i_codec_pa_volume_scale), 68562306a36Sopenharmony_ci SOC_SINGLE_TLV("Line Playback Volume", SUN4I_CODEC_DAC_ACTL, 68662306a36Sopenharmony_ci SUN4I_CODEC_DAC_ACTL_LNG, 1, 0, 68762306a36Sopenharmony_ci sun4i_codec_linein_loopback_gain_scale), 68862306a36Sopenharmony_ci SOC_SINGLE_TLV("Line Boost Volume", SUN4I_CODEC_ADC_ACTL, 68962306a36Sopenharmony_ci SUN4I_CODEC_ADC_ACTL_LNPREG, 7, 0, 69062306a36Sopenharmony_ci sun4i_codec_linein_preamp_gain_scale), 69162306a36Sopenharmony_ci SOC_SINGLE_TLV("FM Playback Volume", SUN4I_CODEC_DAC_ACTL, 69262306a36Sopenharmony_ci SUN4I_CODEC_DAC_ACTL_FMG, 3, 0, 69362306a36Sopenharmony_ci sun4i_codec_fmin_loopback_gain_scale), 69462306a36Sopenharmony_ci SOC_SINGLE_TLV("Mic Playback Volume", SUN4I_CODEC_DAC_ACTL, 69562306a36Sopenharmony_ci SUN4I_CODEC_DAC_ACTL_MICG, 7, 0, 69662306a36Sopenharmony_ci sun4i_codec_micin_loopback_gain_scale), 69762306a36Sopenharmony_ci SOC_SINGLE_TLV("Mic1 Boost Volume", SUN4I_CODEC_ADC_ACTL, 69862306a36Sopenharmony_ci SUN4I_CODEC_ADC_ACTL_PREG1, 3, 0, 69962306a36Sopenharmony_ci sun4i_codec_micin_preamp_gain_scale), 70062306a36Sopenharmony_ci SOC_SINGLE_TLV("Mic2 Boost Volume", SUN4I_CODEC_ADC_ACTL, 70162306a36Sopenharmony_ci SUN4I_CODEC_ADC_ACTL_PREG2, 3, 0, 70262306a36Sopenharmony_ci sun4i_codec_micin_preamp_gain_scale), 70362306a36Sopenharmony_ci}; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_cistatic const struct snd_kcontrol_new sun7i_codec_controls[] = { 70662306a36Sopenharmony_ci SOC_SINGLE_TLV("Power Amplifier Volume", SUN4I_CODEC_DAC_ACTL, 70762306a36Sopenharmony_ci SUN4I_CODEC_DAC_ACTL_PA_VOL, 0x3F, 0, 70862306a36Sopenharmony_ci sun4i_codec_pa_volume_scale), 70962306a36Sopenharmony_ci SOC_SINGLE_TLV("Line Playback Volume", SUN4I_CODEC_DAC_ACTL, 71062306a36Sopenharmony_ci SUN4I_CODEC_DAC_ACTL_LNG, 1, 0, 71162306a36Sopenharmony_ci sun4i_codec_linein_loopback_gain_scale), 71262306a36Sopenharmony_ci SOC_SINGLE_TLV("Line Boost Volume", SUN4I_CODEC_ADC_ACTL, 71362306a36Sopenharmony_ci SUN4I_CODEC_ADC_ACTL_LNPREG, 7, 0, 71462306a36Sopenharmony_ci sun4i_codec_linein_preamp_gain_scale), 71562306a36Sopenharmony_ci SOC_SINGLE_TLV("FM Playback Volume", SUN4I_CODEC_DAC_ACTL, 71662306a36Sopenharmony_ci SUN4I_CODEC_DAC_ACTL_FMG, 3, 0, 71762306a36Sopenharmony_ci sun4i_codec_fmin_loopback_gain_scale), 71862306a36Sopenharmony_ci SOC_SINGLE_TLV("Mic Playback Volume", SUN4I_CODEC_DAC_ACTL, 71962306a36Sopenharmony_ci SUN4I_CODEC_DAC_ACTL_MICG, 7, 0, 72062306a36Sopenharmony_ci sun4i_codec_micin_loopback_gain_scale), 72162306a36Sopenharmony_ci SOC_SINGLE_TLV("Mic1 Boost Volume", SUN7I_CODEC_AC_MIC_PHONE_CAL, 72262306a36Sopenharmony_ci SUN7I_CODEC_AC_MIC_PHONE_CAL_PREG1, 7, 0, 72362306a36Sopenharmony_ci sun7i_codec_micin_preamp_gain_scale), 72462306a36Sopenharmony_ci SOC_SINGLE_TLV("Mic2 Boost Volume", SUN7I_CODEC_AC_MIC_PHONE_CAL, 72562306a36Sopenharmony_ci SUN7I_CODEC_AC_MIC_PHONE_CAL_PREG2, 7, 0, 72662306a36Sopenharmony_ci sun7i_codec_micin_preamp_gain_scale), 72762306a36Sopenharmony_ci}; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_cistatic const struct snd_kcontrol_new sun4i_codec_mixer_controls[] = { 73062306a36Sopenharmony_ci SOC_DAPM_SINGLE("Left Mixer Left DAC Playback Switch", 73162306a36Sopenharmony_ci SUN4I_CODEC_DAC_ACTL, SUN4I_CODEC_DAC_ACTL_LDACLMIXS, 73262306a36Sopenharmony_ci 1, 0), 73362306a36Sopenharmony_ci SOC_DAPM_SINGLE("Right Mixer Right DAC Playback Switch", 73462306a36Sopenharmony_ci SUN4I_CODEC_DAC_ACTL, SUN4I_CODEC_DAC_ACTL_RDACRMIXS, 73562306a36Sopenharmony_ci 1, 0), 73662306a36Sopenharmony_ci SOC_DAPM_SINGLE("Right Mixer Left DAC Playback Switch", 73762306a36Sopenharmony_ci SUN4I_CODEC_DAC_ACTL, 73862306a36Sopenharmony_ci SUN4I_CODEC_DAC_ACTL_LDACRMIXS, 1, 0), 73962306a36Sopenharmony_ci SOC_DAPM_DOUBLE("Line Playback Switch", SUN4I_CODEC_DAC_ACTL, 74062306a36Sopenharmony_ci SUN4I_CODEC_DAC_ACTL_LLNS, 74162306a36Sopenharmony_ci SUN4I_CODEC_DAC_ACTL_RLNS, 1, 0), 74262306a36Sopenharmony_ci SOC_DAPM_DOUBLE("FM Playback Switch", SUN4I_CODEC_DAC_ACTL, 74362306a36Sopenharmony_ci SUN4I_CODEC_DAC_ACTL_LFMS, 74462306a36Sopenharmony_ci SUN4I_CODEC_DAC_ACTL_RFMS, 1, 0), 74562306a36Sopenharmony_ci SOC_DAPM_DOUBLE("Mic1 Playback Switch", SUN4I_CODEC_DAC_ACTL, 74662306a36Sopenharmony_ci SUN4I_CODEC_DAC_ACTL_MIC1LS, 74762306a36Sopenharmony_ci SUN4I_CODEC_DAC_ACTL_MIC1RS, 1, 0), 74862306a36Sopenharmony_ci SOC_DAPM_DOUBLE("Mic2 Playback Switch", SUN4I_CODEC_DAC_ACTL, 74962306a36Sopenharmony_ci SUN4I_CODEC_DAC_ACTL_MIC2LS, 75062306a36Sopenharmony_ci SUN4I_CODEC_DAC_ACTL_MIC2RS, 1, 0), 75162306a36Sopenharmony_ci}; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_cistatic const struct snd_kcontrol_new sun4i_codec_pa_mixer_controls[] = { 75462306a36Sopenharmony_ci SOC_DAPM_SINGLE("DAC Playback Switch", SUN4I_CODEC_DAC_ACTL, 75562306a36Sopenharmony_ci SUN4I_CODEC_DAC_ACTL_DACPAS, 1, 0), 75662306a36Sopenharmony_ci SOC_DAPM_SINGLE("Mixer Playback Switch", SUN4I_CODEC_DAC_ACTL, 75762306a36Sopenharmony_ci SUN4I_CODEC_DAC_ACTL_MIXPAS, 1, 0), 75862306a36Sopenharmony_ci}; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget sun4i_codec_codec_dapm_widgets[] = { 76162306a36Sopenharmony_ci /* Digital parts of the ADCs */ 76262306a36Sopenharmony_ci SND_SOC_DAPM_SUPPLY("ADC", SUN4I_CODEC_ADC_FIFOC, 76362306a36Sopenharmony_ci SUN4I_CODEC_ADC_FIFOC_EN_AD, 0, 76462306a36Sopenharmony_ci NULL, 0), 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci /* Digital parts of the DACs */ 76762306a36Sopenharmony_ci SND_SOC_DAPM_SUPPLY("DAC", SUN4I_CODEC_DAC_DPC, 76862306a36Sopenharmony_ci SUN4I_CODEC_DAC_DPC_EN_DA, 0, 76962306a36Sopenharmony_ci NULL, 0), 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci /* Analog parts of the ADCs */ 77262306a36Sopenharmony_ci SND_SOC_DAPM_ADC("Left ADC", "Codec Capture", SUN4I_CODEC_ADC_ACTL, 77362306a36Sopenharmony_ci SUN4I_CODEC_ADC_ACTL_ADC_L_EN, 0), 77462306a36Sopenharmony_ci SND_SOC_DAPM_ADC("Right ADC", "Codec Capture", SUN4I_CODEC_ADC_ACTL, 77562306a36Sopenharmony_ci SUN4I_CODEC_ADC_ACTL_ADC_R_EN, 0), 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci /* Analog parts of the DACs */ 77862306a36Sopenharmony_ci SND_SOC_DAPM_DAC("Left DAC", "Codec Playback", SUN4I_CODEC_DAC_ACTL, 77962306a36Sopenharmony_ci SUN4I_CODEC_DAC_ACTL_DACAENL, 0), 78062306a36Sopenharmony_ci SND_SOC_DAPM_DAC("Right DAC", "Codec Playback", SUN4I_CODEC_DAC_ACTL, 78162306a36Sopenharmony_ci SUN4I_CODEC_DAC_ACTL_DACAENR, 0), 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci /* Mixers */ 78462306a36Sopenharmony_ci SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0, 78562306a36Sopenharmony_ci sun4i_codec_mixer_controls, 78662306a36Sopenharmony_ci ARRAY_SIZE(sun4i_codec_mixer_controls)), 78762306a36Sopenharmony_ci SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0, 78862306a36Sopenharmony_ci sun4i_codec_mixer_controls, 78962306a36Sopenharmony_ci ARRAY_SIZE(sun4i_codec_mixer_controls)), 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci /* Global Mixer Enable */ 79262306a36Sopenharmony_ci SND_SOC_DAPM_SUPPLY("Mixer Enable", SUN4I_CODEC_DAC_ACTL, 79362306a36Sopenharmony_ci SUN4I_CODEC_DAC_ACTL_MIXEN, 0, NULL, 0), 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci /* VMIC */ 79662306a36Sopenharmony_ci SND_SOC_DAPM_SUPPLY("VMIC", SUN4I_CODEC_ADC_ACTL, 79762306a36Sopenharmony_ci SUN4I_CODEC_ADC_ACTL_VMICEN, 0, NULL, 0), 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci /* Mic Pre-Amplifiers */ 80062306a36Sopenharmony_ci SND_SOC_DAPM_PGA("MIC1 Pre-Amplifier", SUN4I_CODEC_ADC_ACTL, 80162306a36Sopenharmony_ci SUN4I_CODEC_ADC_ACTL_PREG1EN, 0, NULL, 0), 80262306a36Sopenharmony_ci SND_SOC_DAPM_PGA("MIC2 Pre-Amplifier", SUN4I_CODEC_ADC_ACTL, 80362306a36Sopenharmony_ci SUN4I_CODEC_ADC_ACTL_PREG2EN, 0, NULL, 0), 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci /* Power Amplifier */ 80662306a36Sopenharmony_ci SND_SOC_DAPM_MIXER("Power Amplifier", SUN4I_CODEC_ADC_ACTL, 80762306a36Sopenharmony_ci SUN4I_CODEC_ADC_ACTL_PA_EN, 0, 80862306a36Sopenharmony_ci sun4i_codec_pa_mixer_controls, 80962306a36Sopenharmony_ci ARRAY_SIZE(sun4i_codec_pa_mixer_controls)), 81062306a36Sopenharmony_ci SND_SOC_DAPM_SWITCH("Power Amplifier Mute", SND_SOC_NOPM, 0, 0, 81162306a36Sopenharmony_ci &sun4i_codec_pa_mute), 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci SND_SOC_DAPM_INPUT("Line Right"), 81462306a36Sopenharmony_ci SND_SOC_DAPM_INPUT("Line Left"), 81562306a36Sopenharmony_ci SND_SOC_DAPM_INPUT("FM Right"), 81662306a36Sopenharmony_ci SND_SOC_DAPM_INPUT("FM Left"), 81762306a36Sopenharmony_ci SND_SOC_DAPM_INPUT("Mic1"), 81862306a36Sopenharmony_ci SND_SOC_DAPM_INPUT("Mic2"), 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci SND_SOC_DAPM_OUTPUT("HP Right"), 82162306a36Sopenharmony_ci SND_SOC_DAPM_OUTPUT("HP Left"), 82262306a36Sopenharmony_ci}; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_cistatic const struct snd_soc_dapm_route sun4i_codec_codec_dapm_routes[] = { 82562306a36Sopenharmony_ci /* Left ADC / DAC Routes */ 82662306a36Sopenharmony_ci { "Left ADC", NULL, "ADC" }, 82762306a36Sopenharmony_ci { "Left DAC", NULL, "DAC" }, 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci /* Right ADC / DAC Routes */ 83062306a36Sopenharmony_ci { "Right ADC", NULL, "ADC" }, 83162306a36Sopenharmony_ci { "Right DAC", NULL, "DAC" }, 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci /* Right Mixer Routes */ 83462306a36Sopenharmony_ci { "Right Mixer", NULL, "Mixer Enable" }, 83562306a36Sopenharmony_ci { "Right Mixer", "Right Mixer Left DAC Playback Switch", "Left DAC" }, 83662306a36Sopenharmony_ci { "Right Mixer", "Right Mixer Right DAC Playback Switch", "Right DAC" }, 83762306a36Sopenharmony_ci { "Right Mixer", "Line Playback Switch", "Line Right" }, 83862306a36Sopenharmony_ci { "Right Mixer", "FM Playback Switch", "FM Right" }, 83962306a36Sopenharmony_ci { "Right Mixer", "Mic1 Playback Switch", "MIC1 Pre-Amplifier" }, 84062306a36Sopenharmony_ci { "Right Mixer", "Mic2 Playback Switch", "MIC2 Pre-Amplifier" }, 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci /* Left Mixer Routes */ 84362306a36Sopenharmony_ci { "Left Mixer", NULL, "Mixer Enable" }, 84462306a36Sopenharmony_ci { "Left Mixer", "Left Mixer Left DAC Playback Switch", "Left DAC" }, 84562306a36Sopenharmony_ci { "Left Mixer", "Line Playback Switch", "Line Left" }, 84662306a36Sopenharmony_ci { "Left Mixer", "FM Playback Switch", "FM Left" }, 84762306a36Sopenharmony_ci { "Left Mixer", "Mic1 Playback Switch", "MIC1 Pre-Amplifier" }, 84862306a36Sopenharmony_ci { "Left Mixer", "Mic2 Playback Switch", "MIC2 Pre-Amplifier" }, 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci /* Power Amplifier Routes */ 85162306a36Sopenharmony_ci { "Power Amplifier", "Mixer Playback Switch", "Left Mixer" }, 85262306a36Sopenharmony_ci { "Power Amplifier", "Mixer Playback Switch", "Right Mixer" }, 85362306a36Sopenharmony_ci { "Power Amplifier", "DAC Playback Switch", "Left DAC" }, 85462306a36Sopenharmony_ci { "Power Amplifier", "DAC Playback Switch", "Right DAC" }, 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci /* Headphone Output Routes */ 85762306a36Sopenharmony_ci { "Power Amplifier Mute", "Switch", "Power Amplifier" }, 85862306a36Sopenharmony_ci { "HP Right", NULL, "Power Amplifier Mute" }, 85962306a36Sopenharmony_ci { "HP Left", NULL, "Power Amplifier Mute" }, 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci /* Mic1 Routes */ 86262306a36Sopenharmony_ci { "Left ADC", NULL, "MIC1 Pre-Amplifier" }, 86362306a36Sopenharmony_ci { "Right ADC", NULL, "MIC1 Pre-Amplifier" }, 86462306a36Sopenharmony_ci { "MIC1 Pre-Amplifier", NULL, "Mic1"}, 86562306a36Sopenharmony_ci { "Mic1", NULL, "VMIC" }, 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci /* Mic2 Routes */ 86862306a36Sopenharmony_ci { "Left ADC", NULL, "MIC2 Pre-Amplifier" }, 86962306a36Sopenharmony_ci { "Right ADC", NULL, "MIC2 Pre-Amplifier" }, 87062306a36Sopenharmony_ci { "MIC2 Pre-Amplifier", NULL, "Mic2"}, 87162306a36Sopenharmony_ci { "Mic2", NULL, "VMIC" }, 87262306a36Sopenharmony_ci}; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_cistatic const struct snd_soc_component_driver sun4i_codec_codec = { 87562306a36Sopenharmony_ci .controls = sun4i_codec_controls, 87662306a36Sopenharmony_ci .num_controls = ARRAY_SIZE(sun4i_codec_controls), 87762306a36Sopenharmony_ci .dapm_widgets = sun4i_codec_codec_dapm_widgets, 87862306a36Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(sun4i_codec_codec_dapm_widgets), 87962306a36Sopenharmony_ci .dapm_routes = sun4i_codec_codec_dapm_routes, 88062306a36Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(sun4i_codec_codec_dapm_routes), 88162306a36Sopenharmony_ci .idle_bias_on = 1, 88262306a36Sopenharmony_ci .use_pmdown_time = 1, 88362306a36Sopenharmony_ci .endianness = 1, 88462306a36Sopenharmony_ci}; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_cistatic const struct snd_soc_component_driver sun7i_codec_codec = { 88762306a36Sopenharmony_ci .controls = sun7i_codec_controls, 88862306a36Sopenharmony_ci .num_controls = ARRAY_SIZE(sun7i_codec_controls), 88962306a36Sopenharmony_ci .dapm_widgets = sun4i_codec_codec_dapm_widgets, 89062306a36Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(sun4i_codec_codec_dapm_widgets), 89162306a36Sopenharmony_ci .dapm_routes = sun4i_codec_codec_dapm_routes, 89262306a36Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(sun4i_codec_codec_dapm_routes), 89362306a36Sopenharmony_ci .idle_bias_on = 1, 89462306a36Sopenharmony_ci .use_pmdown_time = 1, 89562306a36Sopenharmony_ci .endianness = 1, 89662306a36Sopenharmony_ci}; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci/*** sun6i Codec ***/ 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci/* mixer controls */ 90162306a36Sopenharmony_cistatic const struct snd_kcontrol_new sun6i_codec_mixer_controls[] = { 90262306a36Sopenharmony_ci SOC_DAPM_DOUBLE("DAC Playback Switch", 90362306a36Sopenharmony_ci SUN6I_CODEC_OM_DACA_CTRL, 90462306a36Sopenharmony_ci SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACL, 90562306a36Sopenharmony_ci SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACR, 1, 0), 90662306a36Sopenharmony_ci SOC_DAPM_DOUBLE("DAC Reversed Playback Switch", 90762306a36Sopenharmony_ci SUN6I_CODEC_OM_DACA_CTRL, 90862306a36Sopenharmony_ci SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACR, 90962306a36Sopenharmony_ci SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACL, 1, 0), 91062306a36Sopenharmony_ci SOC_DAPM_DOUBLE("Line In Playback Switch", 91162306a36Sopenharmony_ci SUN6I_CODEC_OM_DACA_CTRL, 91262306a36Sopenharmony_ci SUN6I_CODEC_OM_DACA_CTRL_LMIX_LINEINL, 91362306a36Sopenharmony_ci SUN6I_CODEC_OM_DACA_CTRL_RMIX_LINEINR, 1, 0), 91462306a36Sopenharmony_ci SOC_DAPM_DOUBLE("Mic1 Playback Switch", 91562306a36Sopenharmony_ci SUN6I_CODEC_OM_DACA_CTRL, 91662306a36Sopenharmony_ci SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC1, 91762306a36Sopenharmony_ci SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC1, 1, 0), 91862306a36Sopenharmony_ci SOC_DAPM_DOUBLE("Mic2 Playback Switch", 91962306a36Sopenharmony_ci SUN6I_CODEC_OM_DACA_CTRL, 92062306a36Sopenharmony_ci SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC2, 92162306a36Sopenharmony_ci SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC2, 1, 0), 92262306a36Sopenharmony_ci}; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci/* ADC mixer controls */ 92562306a36Sopenharmony_cistatic const struct snd_kcontrol_new sun6i_codec_adc_mixer_controls[] = { 92662306a36Sopenharmony_ci SOC_DAPM_DOUBLE("Mixer Capture Switch", 92762306a36Sopenharmony_ci SUN6I_CODEC_ADC_ACTL, 92862306a36Sopenharmony_ci SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXL, 92962306a36Sopenharmony_ci SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXR, 1, 0), 93062306a36Sopenharmony_ci SOC_DAPM_DOUBLE("Mixer Reversed Capture Switch", 93162306a36Sopenharmony_ci SUN6I_CODEC_ADC_ACTL, 93262306a36Sopenharmony_ci SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXR, 93362306a36Sopenharmony_ci SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXL, 1, 0), 93462306a36Sopenharmony_ci SOC_DAPM_DOUBLE("Line In Capture Switch", 93562306a36Sopenharmony_ci SUN6I_CODEC_ADC_ACTL, 93662306a36Sopenharmony_ci SUN6I_CODEC_ADC_ACTL_LADCMIX_LINEINL, 93762306a36Sopenharmony_ci SUN6I_CODEC_ADC_ACTL_RADCMIX_LINEINR, 1, 0), 93862306a36Sopenharmony_ci SOC_DAPM_DOUBLE("Mic1 Capture Switch", 93962306a36Sopenharmony_ci SUN6I_CODEC_ADC_ACTL, 94062306a36Sopenharmony_ci SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC1, 94162306a36Sopenharmony_ci SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC1, 1, 0), 94262306a36Sopenharmony_ci SOC_DAPM_DOUBLE("Mic2 Capture Switch", 94362306a36Sopenharmony_ci SUN6I_CODEC_ADC_ACTL, 94462306a36Sopenharmony_ci SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC2, 94562306a36Sopenharmony_ci SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC2, 1, 0), 94662306a36Sopenharmony_ci}; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci/* headphone controls */ 94962306a36Sopenharmony_cistatic const char * const sun6i_codec_hp_src_enum_text[] = { 95062306a36Sopenharmony_ci "DAC", "Mixer", 95162306a36Sopenharmony_ci}; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_cistatic SOC_ENUM_DOUBLE_DECL(sun6i_codec_hp_src_enum, 95462306a36Sopenharmony_ci SUN6I_CODEC_OM_DACA_CTRL, 95562306a36Sopenharmony_ci SUN6I_CODEC_OM_DACA_CTRL_LHPIS, 95662306a36Sopenharmony_ci SUN6I_CODEC_OM_DACA_CTRL_RHPIS, 95762306a36Sopenharmony_ci sun6i_codec_hp_src_enum_text); 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_cistatic const struct snd_kcontrol_new sun6i_codec_hp_src[] = { 96062306a36Sopenharmony_ci SOC_DAPM_ENUM("Headphone Source Playback Route", 96162306a36Sopenharmony_ci sun6i_codec_hp_src_enum), 96262306a36Sopenharmony_ci}; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci/* microphone controls */ 96562306a36Sopenharmony_cistatic const char * const sun6i_codec_mic2_src_enum_text[] = { 96662306a36Sopenharmony_ci "Mic2", "Mic3", 96762306a36Sopenharmony_ci}; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(sun6i_codec_mic2_src_enum, 97062306a36Sopenharmony_ci SUN6I_CODEC_MIC_CTRL, 97162306a36Sopenharmony_ci SUN6I_CODEC_MIC_CTRL_MIC2SLT, 97262306a36Sopenharmony_ci sun6i_codec_mic2_src_enum_text); 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_cistatic const struct snd_kcontrol_new sun6i_codec_mic2_src[] = { 97562306a36Sopenharmony_ci SOC_DAPM_ENUM("Mic2 Amplifier Source Route", 97662306a36Sopenharmony_ci sun6i_codec_mic2_src_enum), 97762306a36Sopenharmony_ci}; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci/* line out controls */ 98062306a36Sopenharmony_cistatic const char * const sun6i_codec_lineout_src_enum_text[] = { 98162306a36Sopenharmony_ci "Stereo", "Mono Differential", 98262306a36Sopenharmony_ci}; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_cistatic SOC_ENUM_DOUBLE_DECL(sun6i_codec_lineout_src_enum, 98562306a36Sopenharmony_ci SUN6I_CODEC_MIC_CTRL, 98662306a36Sopenharmony_ci SUN6I_CODEC_MIC_CTRL_LINEOUTLSRC, 98762306a36Sopenharmony_ci SUN6I_CODEC_MIC_CTRL_LINEOUTRSRC, 98862306a36Sopenharmony_ci sun6i_codec_lineout_src_enum_text); 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_cistatic const struct snd_kcontrol_new sun6i_codec_lineout_src[] = { 99162306a36Sopenharmony_ci SOC_DAPM_ENUM("Line Out Source Playback Route", 99262306a36Sopenharmony_ci sun6i_codec_lineout_src_enum), 99362306a36Sopenharmony_ci}; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci/* volume / mute controls */ 99662306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(sun6i_codec_dvol_scale, -7308, 116, 0); 99762306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(sun6i_codec_hp_vol_scale, -6300, 100, 1); 99862306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(sun6i_codec_out_mixer_pregain_scale, 99962306a36Sopenharmony_ci -450, 150, 0); 100062306a36Sopenharmony_cistatic const DECLARE_TLV_DB_RANGE(sun6i_codec_lineout_vol_scale, 100162306a36Sopenharmony_ci 0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), 100262306a36Sopenharmony_ci 2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0), 100362306a36Sopenharmony_ci); 100462306a36Sopenharmony_cistatic const DECLARE_TLV_DB_RANGE(sun6i_codec_mic_gain_scale, 100562306a36Sopenharmony_ci 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), 100662306a36Sopenharmony_ci 1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0), 100762306a36Sopenharmony_ci); 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_cistatic const struct snd_kcontrol_new sun6i_codec_codec_widgets[] = { 101062306a36Sopenharmony_ci SOC_SINGLE_TLV("DAC Playback Volume", SUN4I_CODEC_DAC_DPC, 101162306a36Sopenharmony_ci SUN4I_CODEC_DAC_DPC_DVOL, 0x3f, 1, 101262306a36Sopenharmony_ci sun6i_codec_dvol_scale), 101362306a36Sopenharmony_ci SOC_SINGLE_TLV("Headphone Playback Volume", 101462306a36Sopenharmony_ci SUN6I_CODEC_OM_DACA_CTRL, 101562306a36Sopenharmony_ci SUN6I_CODEC_OM_DACA_CTRL_HPVOL, 0x3f, 0, 101662306a36Sopenharmony_ci sun6i_codec_hp_vol_scale), 101762306a36Sopenharmony_ci SOC_SINGLE_TLV("Line Out Playback Volume", 101862306a36Sopenharmony_ci SUN6I_CODEC_MIC_CTRL, 101962306a36Sopenharmony_ci SUN6I_CODEC_MIC_CTRL_LINEOUTVC, 0x1f, 0, 102062306a36Sopenharmony_ci sun6i_codec_lineout_vol_scale), 102162306a36Sopenharmony_ci SOC_DOUBLE("Headphone Playback Switch", 102262306a36Sopenharmony_ci SUN6I_CODEC_OM_DACA_CTRL, 102362306a36Sopenharmony_ci SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE, 102462306a36Sopenharmony_ci SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE, 1, 0), 102562306a36Sopenharmony_ci SOC_DOUBLE("Line Out Playback Switch", 102662306a36Sopenharmony_ci SUN6I_CODEC_MIC_CTRL, 102762306a36Sopenharmony_ci SUN6I_CODEC_MIC_CTRL_LINEOUTLEN, 102862306a36Sopenharmony_ci SUN6I_CODEC_MIC_CTRL_LINEOUTREN, 1, 0), 102962306a36Sopenharmony_ci /* Mixer pre-gains */ 103062306a36Sopenharmony_ci SOC_SINGLE_TLV("Line In Playback Volume", 103162306a36Sopenharmony_ci SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_LINEING, 103262306a36Sopenharmony_ci 0x7, 0, sun6i_codec_out_mixer_pregain_scale), 103362306a36Sopenharmony_ci SOC_SINGLE_TLV("Mic1 Playback Volume", 103462306a36Sopenharmony_ci SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_MIC1G, 103562306a36Sopenharmony_ci 0x7, 0, sun6i_codec_out_mixer_pregain_scale), 103662306a36Sopenharmony_ci SOC_SINGLE_TLV("Mic2 Playback Volume", 103762306a36Sopenharmony_ci SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_MIC2G, 103862306a36Sopenharmony_ci 0x7, 0, sun6i_codec_out_mixer_pregain_scale), 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci /* Microphone Amp boost gains */ 104162306a36Sopenharmony_ci SOC_SINGLE_TLV("Mic1 Boost Volume", SUN6I_CODEC_MIC_CTRL, 104262306a36Sopenharmony_ci SUN6I_CODEC_MIC_CTRL_MIC1BOOST, 0x7, 0, 104362306a36Sopenharmony_ci sun6i_codec_mic_gain_scale), 104462306a36Sopenharmony_ci SOC_SINGLE_TLV("Mic2 Boost Volume", SUN6I_CODEC_MIC_CTRL, 104562306a36Sopenharmony_ci SUN6I_CODEC_MIC_CTRL_MIC2BOOST, 0x7, 0, 104662306a36Sopenharmony_ci sun6i_codec_mic_gain_scale), 104762306a36Sopenharmony_ci SOC_DOUBLE_TLV("ADC Capture Volume", 104862306a36Sopenharmony_ci SUN6I_CODEC_ADC_ACTL, SUN6I_CODEC_ADC_ACTL_ADCLG, 104962306a36Sopenharmony_ci SUN6I_CODEC_ADC_ACTL_ADCRG, 0x7, 0, 105062306a36Sopenharmony_ci sun6i_codec_out_mixer_pregain_scale), 105162306a36Sopenharmony_ci}; 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget sun6i_codec_codec_dapm_widgets[] = { 105462306a36Sopenharmony_ci /* Microphone inputs */ 105562306a36Sopenharmony_ci SND_SOC_DAPM_INPUT("MIC1"), 105662306a36Sopenharmony_ci SND_SOC_DAPM_INPUT("MIC2"), 105762306a36Sopenharmony_ci SND_SOC_DAPM_INPUT("MIC3"), 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci /* Microphone Bias */ 106062306a36Sopenharmony_ci SND_SOC_DAPM_SUPPLY("HBIAS", SUN6I_CODEC_MIC_CTRL, 106162306a36Sopenharmony_ci SUN6I_CODEC_MIC_CTRL_HBIASEN, 0, NULL, 0), 106262306a36Sopenharmony_ci SND_SOC_DAPM_SUPPLY("MBIAS", SUN6I_CODEC_MIC_CTRL, 106362306a36Sopenharmony_ci SUN6I_CODEC_MIC_CTRL_MBIASEN, 0, NULL, 0), 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci /* Mic input path */ 106662306a36Sopenharmony_ci SND_SOC_DAPM_MUX("Mic2 Amplifier Source Route", 106762306a36Sopenharmony_ci SND_SOC_NOPM, 0, 0, sun6i_codec_mic2_src), 106862306a36Sopenharmony_ci SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN6I_CODEC_MIC_CTRL, 106962306a36Sopenharmony_ci SUN6I_CODEC_MIC_CTRL_MIC1AMPEN, 0, NULL, 0), 107062306a36Sopenharmony_ci SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN6I_CODEC_MIC_CTRL, 107162306a36Sopenharmony_ci SUN6I_CODEC_MIC_CTRL_MIC2AMPEN, 0, NULL, 0), 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci /* Line In */ 107462306a36Sopenharmony_ci SND_SOC_DAPM_INPUT("LINEIN"), 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci /* Digital parts of the ADCs */ 107762306a36Sopenharmony_ci SND_SOC_DAPM_SUPPLY("ADC Enable", SUN6I_CODEC_ADC_FIFOC, 107862306a36Sopenharmony_ci SUN6I_CODEC_ADC_FIFOC_EN_AD, 0, 107962306a36Sopenharmony_ci NULL, 0), 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci /* Analog parts of the ADCs */ 108262306a36Sopenharmony_ci SND_SOC_DAPM_ADC("Left ADC", "Codec Capture", SUN6I_CODEC_ADC_ACTL, 108362306a36Sopenharmony_ci SUN6I_CODEC_ADC_ACTL_ADCLEN, 0), 108462306a36Sopenharmony_ci SND_SOC_DAPM_ADC("Right ADC", "Codec Capture", SUN6I_CODEC_ADC_ACTL, 108562306a36Sopenharmony_ci SUN6I_CODEC_ADC_ACTL_ADCREN, 0), 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci /* ADC Mixers */ 108862306a36Sopenharmony_ci SOC_MIXER_ARRAY("Left ADC Mixer", SND_SOC_NOPM, 0, 0, 108962306a36Sopenharmony_ci sun6i_codec_adc_mixer_controls), 109062306a36Sopenharmony_ci SOC_MIXER_ARRAY("Right ADC Mixer", SND_SOC_NOPM, 0, 0, 109162306a36Sopenharmony_ci sun6i_codec_adc_mixer_controls), 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci /* Digital parts of the DACs */ 109462306a36Sopenharmony_ci SND_SOC_DAPM_SUPPLY("DAC Enable", SUN4I_CODEC_DAC_DPC, 109562306a36Sopenharmony_ci SUN4I_CODEC_DAC_DPC_EN_DA, 0, 109662306a36Sopenharmony_ci NULL, 0), 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci /* Analog parts of the DACs */ 109962306a36Sopenharmony_ci SND_SOC_DAPM_DAC("Left DAC", "Codec Playback", 110062306a36Sopenharmony_ci SUN6I_CODEC_OM_DACA_CTRL, 110162306a36Sopenharmony_ci SUN6I_CODEC_OM_DACA_CTRL_DACALEN, 0), 110262306a36Sopenharmony_ci SND_SOC_DAPM_DAC("Right DAC", "Codec Playback", 110362306a36Sopenharmony_ci SUN6I_CODEC_OM_DACA_CTRL, 110462306a36Sopenharmony_ci SUN6I_CODEC_OM_DACA_CTRL_DACAREN, 0), 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci /* Mixers */ 110762306a36Sopenharmony_ci SOC_MIXER_ARRAY("Left Mixer", SUN6I_CODEC_OM_DACA_CTRL, 110862306a36Sopenharmony_ci SUN6I_CODEC_OM_DACA_CTRL_LMIXEN, 0, 110962306a36Sopenharmony_ci sun6i_codec_mixer_controls), 111062306a36Sopenharmony_ci SOC_MIXER_ARRAY("Right Mixer", SUN6I_CODEC_OM_DACA_CTRL, 111162306a36Sopenharmony_ci SUN6I_CODEC_OM_DACA_CTRL_RMIXEN, 0, 111262306a36Sopenharmony_ci sun6i_codec_mixer_controls), 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci /* Headphone output path */ 111562306a36Sopenharmony_ci SND_SOC_DAPM_MUX("Headphone Source Playback Route", 111662306a36Sopenharmony_ci SND_SOC_NOPM, 0, 0, sun6i_codec_hp_src), 111762306a36Sopenharmony_ci SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUN6I_CODEC_OM_PA_CTRL, 111862306a36Sopenharmony_ci SUN6I_CODEC_OM_PA_CTRL_HPPAEN, 0, NULL, 0), 111962306a36Sopenharmony_ci SND_SOC_DAPM_SUPPLY("HPCOM Protection", SUN6I_CODEC_OM_PA_CTRL, 112062306a36Sopenharmony_ci SUN6I_CODEC_OM_PA_CTRL_COMPTEN, 0, NULL, 0), 112162306a36Sopenharmony_ci SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPCOM", SUN6I_CODEC_OM_PA_CTRL, 112262306a36Sopenharmony_ci SUN6I_CODEC_OM_PA_CTRL_HPCOM_CTL, 0x3, 0x3, 0), 112362306a36Sopenharmony_ci SND_SOC_DAPM_OUTPUT("HP"), 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci /* Line Out path */ 112662306a36Sopenharmony_ci SND_SOC_DAPM_MUX("Line Out Source Playback Route", 112762306a36Sopenharmony_ci SND_SOC_NOPM, 0, 0, sun6i_codec_lineout_src), 112862306a36Sopenharmony_ci SND_SOC_DAPM_OUTPUT("LINEOUT"), 112962306a36Sopenharmony_ci}; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_cistatic const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = { 113262306a36Sopenharmony_ci /* DAC Routes */ 113362306a36Sopenharmony_ci { "Left DAC", NULL, "DAC Enable" }, 113462306a36Sopenharmony_ci { "Right DAC", NULL, "DAC Enable" }, 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci /* Microphone Routes */ 113762306a36Sopenharmony_ci { "Mic1 Amplifier", NULL, "MIC1"}, 113862306a36Sopenharmony_ci { "Mic2 Amplifier Source Route", "Mic2", "MIC2" }, 113962306a36Sopenharmony_ci { "Mic2 Amplifier Source Route", "Mic3", "MIC3" }, 114062306a36Sopenharmony_ci { "Mic2 Amplifier", NULL, "Mic2 Amplifier Source Route"}, 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci /* Left Mixer Routes */ 114362306a36Sopenharmony_ci { "Left Mixer", "DAC Playback Switch", "Left DAC" }, 114462306a36Sopenharmony_ci { "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" }, 114562306a36Sopenharmony_ci { "Left Mixer", "Line In Playback Switch", "LINEIN" }, 114662306a36Sopenharmony_ci { "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" }, 114762306a36Sopenharmony_ci { "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" }, 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci /* Right Mixer Routes */ 115062306a36Sopenharmony_ci { "Right Mixer", "DAC Playback Switch", "Right DAC" }, 115162306a36Sopenharmony_ci { "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" }, 115262306a36Sopenharmony_ci { "Right Mixer", "Line In Playback Switch", "LINEIN" }, 115362306a36Sopenharmony_ci { "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" }, 115462306a36Sopenharmony_ci { "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" }, 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci /* Left ADC Mixer Routes */ 115762306a36Sopenharmony_ci { "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" }, 115862306a36Sopenharmony_ci { "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" }, 115962306a36Sopenharmony_ci { "Left ADC Mixer", "Line In Capture Switch", "LINEIN" }, 116062306a36Sopenharmony_ci { "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" }, 116162306a36Sopenharmony_ci { "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" }, 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci /* Right ADC Mixer Routes */ 116462306a36Sopenharmony_ci { "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" }, 116562306a36Sopenharmony_ci { "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" }, 116662306a36Sopenharmony_ci { "Right ADC Mixer", "Line In Capture Switch", "LINEIN" }, 116762306a36Sopenharmony_ci { "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" }, 116862306a36Sopenharmony_ci { "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" }, 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci /* Headphone Routes */ 117162306a36Sopenharmony_ci { "Headphone Source Playback Route", "DAC", "Left DAC" }, 117262306a36Sopenharmony_ci { "Headphone Source Playback Route", "DAC", "Right DAC" }, 117362306a36Sopenharmony_ci { "Headphone Source Playback Route", "Mixer", "Left Mixer" }, 117462306a36Sopenharmony_ci { "Headphone Source Playback Route", "Mixer", "Right Mixer" }, 117562306a36Sopenharmony_ci { "Headphone Amp", NULL, "Headphone Source Playback Route" }, 117662306a36Sopenharmony_ci { "HP", NULL, "Headphone Amp" }, 117762306a36Sopenharmony_ci { "HPCOM", NULL, "HPCOM Protection" }, 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci /* Line Out Routes */ 118062306a36Sopenharmony_ci { "Line Out Source Playback Route", "Stereo", "Left Mixer" }, 118162306a36Sopenharmony_ci { "Line Out Source Playback Route", "Stereo", "Right Mixer" }, 118262306a36Sopenharmony_ci { "Line Out Source Playback Route", "Mono Differential", "Left Mixer" }, 118362306a36Sopenharmony_ci { "Line Out Source Playback Route", "Mono Differential", "Right Mixer" }, 118462306a36Sopenharmony_ci { "LINEOUT", NULL, "Line Out Source Playback Route" }, 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci /* ADC Routes */ 118762306a36Sopenharmony_ci { "Left ADC", NULL, "ADC Enable" }, 118862306a36Sopenharmony_ci { "Right ADC", NULL, "ADC Enable" }, 118962306a36Sopenharmony_ci { "Left ADC", NULL, "Left ADC Mixer" }, 119062306a36Sopenharmony_ci { "Right ADC", NULL, "Right ADC Mixer" }, 119162306a36Sopenharmony_ci}; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_cistatic const struct snd_soc_component_driver sun6i_codec_codec = { 119462306a36Sopenharmony_ci .controls = sun6i_codec_codec_widgets, 119562306a36Sopenharmony_ci .num_controls = ARRAY_SIZE(sun6i_codec_codec_widgets), 119662306a36Sopenharmony_ci .dapm_widgets = sun6i_codec_codec_dapm_widgets, 119762306a36Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(sun6i_codec_codec_dapm_widgets), 119862306a36Sopenharmony_ci .dapm_routes = sun6i_codec_codec_dapm_routes, 119962306a36Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(sun6i_codec_codec_dapm_routes), 120062306a36Sopenharmony_ci .idle_bias_on = 1, 120162306a36Sopenharmony_ci .use_pmdown_time = 1, 120262306a36Sopenharmony_ci .endianness = 1, 120362306a36Sopenharmony_ci}; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci/* sun8i A23 codec */ 120662306a36Sopenharmony_cistatic const struct snd_kcontrol_new sun8i_a23_codec_codec_controls[] = { 120762306a36Sopenharmony_ci SOC_SINGLE_TLV("DAC Playback Volume", SUN4I_CODEC_DAC_DPC, 120862306a36Sopenharmony_ci SUN4I_CODEC_DAC_DPC_DVOL, 0x3f, 1, 120962306a36Sopenharmony_ci sun6i_codec_dvol_scale), 121062306a36Sopenharmony_ci}; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget sun8i_a23_codec_codec_widgets[] = { 121362306a36Sopenharmony_ci /* Digital parts of the ADCs */ 121462306a36Sopenharmony_ci SND_SOC_DAPM_SUPPLY("ADC Enable", SUN6I_CODEC_ADC_FIFOC, 121562306a36Sopenharmony_ci SUN6I_CODEC_ADC_FIFOC_EN_AD, 0, NULL, 0), 121662306a36Sopenharmony_ci /* Digital parts of the DACs */ 121762306a36Sopenharmony_ci SND_SOC_DAPM_SUPPLY("DAC Enable", SUN4I_CODEC_DAC_DPC, 121862306a36Sopenharmony_ci SUN4I_CODEC_DAC_DPC_EN_DA, 0, NULL, 0), 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci}; 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_cistatic const struct snd_soc_component_driver sun8i_a23_codec_codec = { 122362306a36Sopenharmony_ci .controls = sun8i_a23_codec_codec_controls, 122462306a36Sopenharmony_ci .num_controls = ARRAY_SIZE(sun8i_a23_codec_codec_controls), 122562306a36Sopenharmony_ci .dapm_widgets = sun8i_a23_codec_codec_widgets, 122662306a36Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(sun8i_a23_codec_codec_widgets), 122762306a36Sopenharmony_ci .idle_bias_on = 1, 122862306a36Sopenharmony_ci .use_pmdown_time = 1, 122962306a36Sopenharmony_ci .endianness = 1, 123062306a36Sopenharmony_ci}; 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_cistatic const struct snd_soc_component_driver sun4i_codec_component = { 123362306a36Sopenharmony_ci .name = "sun4i-codec", 123462306a36Sopenharmony_ci .legacy_dai_naming = 1, 123562306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 123662306a36Sopenharmony_ci .debugfs_prefix = "cpu", 123762306a36Sopenharmony_ci#endif 123862306a36Sopenharmony_ci}; 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci#define SUN4I_CODEC_RATES SNDRV_PCM_RATE_CONTINUOUS 124162306a36Sopenharmony_ci#define SUN4I_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ 124262306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE) 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_cistatic int sun4i_codec_dai_probe(struct snd_soc_dai *dai) 124562306a36Sopenharmony_ci{ 124662306a36Sopenharmony_ci struct snd_soc_card *card = snd_soc_dai_get_drvdata(dai); 124762306a36Sopenharmony_ci struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card); 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci snd_soc_dai_init_dma_data(dai, &scodec->playback_dma_data, 125062306a36Sopenharmony_ci &scodec->capture_dma_data); 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci return 0; 125362306a36Sopenharmony_ci} 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_cistatic const struct snd_soc_dai_ops dummy_dai_ops = { 125662306a36Sopenharmony_ci .probe = sun4i_codec_dai_probe, 125762306a36Sopenharmony_ci}; 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_cistatic struct snd_soc_dai_driver dummy_cpu_dai = { 126062306a36Sopenharmony_ci .name = "sun4i-codec-cpu-dai", 126162306a36Sopenharmony_ci .playback = { 126262306a36Sopenharmony_ci .stream_name = "Playback", 126362306a36Sopenharmony_ci .channels_min = 1, 126462306a36Sopenharmony_ci .channels_max = 2, 126562306a36Sopenharmony_ci .rates = SUN4I_CODEC_RATES, 126662306a36Sopenharmony_ci .formats = SUN4I_CODEC_FORMATS, 126762306a36Sopenharmony_ci .sig_bits = 24, 126862306a36Sopenharmony_ci }, 126962306a36Sopenharmony_ci .capture = { 127062306a36Sopenharmony_ci .stream_name = "Capture", 127162306a36Sopenharmony_ci .channels_min = 1, 127262306a36Sopenharmony_ci .channels_max = 2, 127362306a36Sopenharmony_ci .rates = SUN4I_CODEC_RATES, 127462306a36Sopenharmony_ci .formats = SUN4I_CODEC_FORMATS, 127562306a36Sopenharmony_ci .sig_bits = 24, 127662306a36Sopenharmony_ci }, 127762306a36Sopenharmony_ci .ops = &dummy_dai_ops, 127862306a36Sopenharmony_ci}; 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_cistatic struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev, 128162306a36Sopenharmony_ci int *num_links) 128262306a36Sopenharmony_ci{ 128362306a36Sopenharmony_ci struct snd_soc_dai_link *link = devm_kzalloc(dev, sizeof(*link), 128462306a36Sopenharmony_ci GFP_KERNEL); 128562306a36Sopenharmony_ci struct snd_soc_dai_link_component *dlc = devm_kzalloc(dev, 128662306a36Sopenharmony_ci 3 * sizeof(*dlc), GFP_KERNEL); 128762306a36Sopenharmony_ci if (!link || !dlc) 128862306a36Sopenharmony_ci return NULL; 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci link->cpus = &dlc[0]; 129162306a36Sopenharmony_ci link->codecs = &dlc[1]; 129262306a36Sopenharmony_ci link->platforms = &dlc[2]; 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci link->num_cpus = 1; 129562306a36Sopenharmony_ci link->num_codecs = 1; 129662306a36Sopenharmony_ci link->num_platforms = 1; 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci link->name = "cdc"; 129962306a36Sopenharmony_ci link->stream_name = "CDC PCM"; 130062306a36Sopenharmony_ci link->codecs->dai_name = "Codec"; 130162306a36Sopenharmony_ci link->cpus->dai_name = dev_name(dev); 130262306a36Sopenharmony_ci link->codecs->name = dev_name(dev); 130362306a36Sopenharmony_ci link->platforms->name = dev_name(dev); 130462306a36Sopenharmony_ci link->dai_fmt = SND_SOC_DAIFMT_I2S; 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci *num_links = 1; 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci return link; 130962306a36Sopenharmony_ci}; 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_cistatic int sun4i_codec_spk_event(struct snd_soc_dapm_widget *w, 131262306a36Sopenharmony_ci struct snd_kcontrol *k, int event) 131362306a36Sopenharmony_ci{ 131462306a36Sopenharmony_ci struct sun4i_codec *scodec = snd_soc_card_get_drvdata(w->dapm->card); 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci gpiod_set_value_cansleep(scodec->gpio_pa, 131762306a36Sopenharmony_ci !!SND_SOC_DAPM_EVENT_ON(event)); 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci if (SND_SOC_DAPM_EVENT_ON(event)) { 132062306a36Sopenharmony_ci /* 132162306a36Sopenharmony_ci * Need a delay to wait for DAC to push the data. 700ms seems 132262306a36Sopenharmony_ci * to be the best compromise not to feel this delay while 132362306a36Sopenharmony_ci * playing a sound. 132462306a36Sopenharmony_ci */ 132562306a36Sopenharmony_ci msleep(700); 132662306a36Sopenharmony_ci } 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci return 0; 132962306a36Sopenharmony_ci} 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget sun4i_codec_card_dapm_widgets[] = { 133262306a36Sopenharmony_ci SND_SOC_DAPM_SPK("Speaker", sun4i_codec_spk_event), 133362306a36Sopenharmony_ci}; 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_cistatic const struct snd_soc_dapm_route sun4i_codec_card_dapm_routes[] = { 133662306a36Sopenharmony_ci { "Speaker", NULL, "HP Right" }, 133762306a36Sopenharmony_ci { "Speaker", NULL, "HP Left" }, 133862306a36Sopenharmony_ci}; 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_cistatic struct snd_soc_card *sun4i_codec_create_card(struct device *dev) 134162306a36Sopenharmony_ci{ 134262306a36Sopenharmony_ci struct snd_soc_card *card; 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); 134562306a36Sopenharmony_ci if (!card) 134662306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci card->dai_link = sun4i_codec_create_link(dev, &card->num_links); 134962306a36Sopenharmony_ci if (!card->dai_link) 135062306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci card->dev = dev; 135362306a36Sopenharmony_ci card->owner = THIS_MODULE; 135462306a36Sopenharmony_ci card->name = "sun4i-codec"; 135562306a36Sopenharmony_ci card->dapm_widgets = sun4i_codec_card_dapm_widgets; 135662306a36Sopenharmony_ci card->num_dapm_widgets = ARRAY_SIZE(sun4i_codec_card_dapm_widgets); 135762306a36Sopenharmony_ci card->dapm_routes = sun4i_codec_card_dapm_routes; 135862306a36Sopenharmony_ci card->num_dapm_routes = ARRAY_SIZE(sun4i_codec_card_dapm_routes); 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci return card; 136162306a36Sopenharmony_ci}; 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget sun6i_codec_card_dapm_widgets[] = { 136462306a36Sopenharmony_ci SND_SOC_DAPM_HP("Headphone", NULL), 136562306a36Sopenharmony_ci SND_SOC_DAPM_LINE("Line In", NULL), 136662306a36Sopenharmony_ci SND_SOC_DAPM_LINE("Line Out", NULL), 136762306a36Sopenharmony_ci SND_SOC_DAPM_MIC("Headset Mic", NULL), 136862306a36Sopenharmony_ci SND_SOC_DAPM_MIC("Mic", NULL), 136962306a36Sopenharmony_ci SND_SOC_DAPM_SPK("Speaker", sun4i_codec_spk_event), 137062306a36Sopenharmony_ci}; 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_cistatic struct snd_soc_card *sun6i_codec_create_card(struct device *dev) 137362306a36Sopenharmony_ci{ 137462306a36Sopenharmony_ci struct snd_soc_card *card; 137562306a36Sopenharmony_ci int ret; 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); 137862306a36Sopenharmony_ci if (!card) 137962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci card->dai_link = sun4i_codec_create_link(dev, &card->num_links); 138262306a36Sopenharmony_ci if (!card->dai_link) 138362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci card->dev = dev; 138662306a36Sopenharmony_ci card->owner = THIS_MODULE; 138762306a36Sopenharmony_ci card->name = "A31 Audio Codec"; 138862306a36Sopenharmony_ci card->dapm_widgets = sun6i_codec_card_dapm_widgets; 138962306a36Sopenharmony_ci card->num_dapm_widgets = ARRAY_SIZE(sun6i_codec_card_dapm_widgets); 139062306a36Sopenharmony_ci card->fully_routed = true; 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing"); 139362306a36Sopenharmony_ci if (ret) 139462306a36Sopenharmony_ci dev_warn(dev, "failed to parse audio-routing: %d\n", ret); 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci return card; 139762306a36Sopenharmony_ci}; 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_ci/* Connect digital side enables to analog side widgets */ 140062306a36Sopenharmony_cistatic const struct snd_soc_dapm_route sun8i_codec_card_routes[] = { 140162306a36Sopenharmony_ci /* ADC Routes */ 140262306a36Sopenharmony_ci { "Left ADC", NULL, "ADC Enable" }, 140362306a36Sopenharmony_ci { "Right ADC", NULL, "ADC Enable" }, 140462306a36Sopenharmony_ci { "Codec Capture", NULL, "Left ADC" }, 140562306a36Sopenharmony_ci { "Codec Capture", NULL, "Right ADC" }, 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci /* DAC Routes */ 140862306a36Sopenharmony_ci { "Left DAC", NULL, "DAC Enable" }, 140962306a36Sopenharmony_ci { "Right DAC", NULL, "DAC Enable" }, 141062306a36Sopenharmony_ci { "Left DAC", NULL, "Codec Playback" }, 141162306a36Sopenharmony_ci { "Right DAC", NULL, "Codec Playback" }, 141262306a36Sopenharmony_ci}; 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_cistatic struct snd_soc_aux_dev aux_dev = { 141562306a36Sopenharmony_ci .dlc = COMP_EMPTY(), 141662306a36Sopenharmony_ci}; 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_cistatic struct snd_soc_card *sun8i_a23_codec_create_card(struct device *dev) 141962306a36Sopenharmony_ci{ 142062306a36Sopenharmony_ci struct snd_soc_card *card; 142162306a36Sopenharmony_ci int ret; 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); 142462306a36Sopenharmony_ci if (!card) 142562306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci aux_dev.dlc.of_node = of_parse_phandle(dev->of_node, 142862306a36Sopenharmony_ci "allwinner,codec-analog-controls", 142962306a36Sopenharmony_ci 0); 143062306a36Sopenharmony_ci if (!aux_dev.dlc.of_node) { 143162306a36Sopenharmony_ci dev_err(dev, "Can't find analog controls for codec.\n"); 143262306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 143362306a36Sopenharmony_ci } 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci card->dai_link = sun4i_codec_create_link(dev, &card->num_links); 143662306a36Sopenharmony_ci if (!card->dai_link) 143762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci card->dev = dev; 144062306a36Sopenharmony_ci card->owner = THIS_MODULE; 144162306a36Sopenharmony_ci card->name = "A23 Audio Codec"; 144262306a36Sopenharmony_ci card->dapm_widgets = sun6i_codec_card_dapm_widgets; 144362306a36Sopenharmony_ci card->num_dapm_widgets = ARRAY_SIZE(sun6i_codec_card_dapm_widgets); 144462306a36Sopenharmony_ci card->dapm_routes = sun8i_codec_card_routes; 144562306a36Sopenharmony_ci card->num_dapm_routes = ARRAY_SIZE(sun8i_codec_card_routes); 144662306a36Sopenharmony_ci card->aux_dev = &aux_dev; 144762306a36Sopenharmony_ci card->num_aux_devs = 1; 144862306a36Sopenharmony_ci card->fully_routed = true; 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ci ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing"); 145162306a36Sopenharmony_ci if (ret) 145262306a36Sopenharmony_ci dev_warn(dev, "failed to parse audio-routing: %d\n", ret); 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci return card; 145562306a36Sopenharmony_ci}; 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_cistatic struct snd_soc_card *sun8i_h3_codec_create_card(struct device *dev) 145862306a36Sopenharmony_ci{ 145962306a36Sopenharmony_ci struct snd_soc_card *card; 146062306a36Sopenharmony_ci int ret; 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); 146362306a36Sopenharmony_ci if (!card) 146462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci aux_dev.dlc.of_node = of_parse_phandle(dev->of_node, 146762306a36Sopenharmony_ci "allwinner,codec-analog-controls", 146862306a36Sopenharmony_ci 0); 146962306a36Sopenharmony_ci if (!aux_dev.dlc.of_node) { 147062306a36Sopenharmony_ci dev_err(dev, "Can't find analog controls for codec.\n"); 147162306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 147262306a36Sopenharmony_ci } 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci card->dai_link = sun4i_codec_create_link(dev, &card->num_links); 147562306a36Sopenharmony_ci if (!card->dai_link) 147662306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci card->dev = dev; 147962306a36Sopenharmony_ci card->owner = THIS_MODULE; 148062306a36Sopenharmony_ci card->name = "H3 Audio Codec"; 148162306a36Sopenharmony_ci card->dapm_widgets = sun6i_codec_card_dapm_widgets; 148262306a36Sopenharmony_ci card->num_dapm_widgets = ARRAY_SIZE(sun6i_codec_card_dapm_widgets); 148362306a36Sopenharmony_ci card->dapm_routes = sun8i_codec_card_routes; 148462306a36Sopenharmony_ci card->num_dapm_routes = ARRAY_SIZE(sun8i_codec_card_routes); 148562306a36Sopenharmony_ci card->aux_dev = &aux_dev; 148662306a36Sopenharmony_ci card->num_aux_devs = 1; 148762306a36Sopenharmony_ci card->fully_routed = true; 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing"); 149062306a36Sopenharmony_ci if (ret) 149162306a36Sopenharmony_ci dev_warn(dev, "failed to parse audio-routing: %d\n", ret); 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_ci return card; 149462306a36Sopenharmony_ci}; 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_cistatic struct snd_soc_card *sun8i_v3s_codec_create_card(struct device *dev) 149762306a36Sopenharmony_ci{ 149862306a36Sopenharmony_ci struct snd_soc_card *card; 149962306a36Sopenharmony_ci int ret; 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); 150262306a36Sopenharmony_ci if (!card) 150362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci aux_dev.dlc.of_node = of_parse_phandle(dev->of_node, 150662306a36Sopenharmony_ci "allwinner,codec-analog-controls", 150762306a36Sopenharmony_ci 0); 150862306a36Sopenharmony_ci if (!aux_dev.dlc.of_node) { 150962306a36Sopenharmony_ci dev_err(dev, "Can't find analog controls for codec.\n"); 151062306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 151162306a36Sopenharmony_ci } 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci card->dai_link = sun4i_codec_create_link(dev, &card->num_links); 151462306a36Sopenharmony_ci if (!card->dai_link) 151562306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci card->dev = dev; 151862306a36Sopenharmony_ci card->owner = THIS_MODULE; 151962306a36Sopenharmony_ci card->name = "V3s Audio Codec"; 152062306a36Sopenharmony_ci card->dapm_widgets = sun6i_codec_card_dapm_widgets; 152162306a36Sopenharmony_ci card->num_dapm_widgets = ARRAY_SIZE(sun6i_codec_card_dapm_widgets); 152262306a36Sopenharmony_ci card->dapm_routes = sun8i_codec_card_routes; 152362306a36Sopenharmony_ci card->num_dapm_routes = ARRAY_SIZE(sun8i_codec_card_routes); 152462306a36Sopenharmony_ci card->aux_dev = &aux_dev; 152562306a36Sopenharmony_ci card->num_aux_devs = 1; 152662306a36Sopenharmony_ci card->fully_routed = true; 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing"); 152962306a36Sopenharmony_ci if (ret) 153062306a36Sopenharmony_ci dev_warn(dev, "failed to parse audio-routing: %d\n", ret); 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci return card; 153362306a36Sopenharmony_ci}; 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_cistatic const struct regmap_config sun4i_codec_regmap_config = { 153662306a36Sopenharmony_ci .reg_bits = 32, 153762306a36Sopenharmony_ci .reg_stride = 4, 153862306a36Sopenharmony_ci .val_bits = 32, 153962306a36Sopenharmony_ci .max_register = SUN4I_CODEC_ADC_RXCNT, 154062306a36Sopenharmony_ci}; 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_cistatic const struct regmap_config sun6i_codec_regmap_config = { 154362306a36Sopenharmony_ci .reg_bits = 32, 154462306a36Sopenharmony_ci .reg_stride = 4, 154562306a36Sopenharmony_ci .val_bits = 32, 154662306a36Sopenharmony_ci .max_register = SUN6I_CODEC_HMIC_DATA, 154762306a36Sopenharmony_ci}; 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_cistatic const struct regmap_config sun7i_codec_regmap_config = { 155062306a36Sopenharmony_ci .reg_bits = 32, 155162306a36Sopenharmony_ci .reg_stride = 4, 155262306a36Sopenharmony_ci .val_bits = 32, 155362306a36Sopenharmony_ci .max_register = SUN7I_CODEC_AC_MIC_PHONE_CAL, 155462306a36Sopenharmony_ci}; 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_cistatic const struct regmap_config sun8i_a23_codec_regmap_config = { 155762306a36Sopenharmony_ci .reg_bits = 32, 155862306a36Sopenharmony_ci .reg_stride = 4, 155962306a36Sopenharmony_ci .val_bits = 32, 156062306a36Sopenharmony_ci .max_register = SUN8I_A23_CODEC_ADC_RXCNT, 156162306a36Sopenharmony_ci}; 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_cistatic const struct regmap_config sun8i_h3_codec_regmap_config = { 156462306a36Sopenharmony_ci .reg_bits = 32, 156562306a36Sopenharmony_ci .reg_stride = 4, 156662306a36Sopenharmony_ci .val_bits = 32, 156762306a36Sopenharmony_ci .max_register = SUN8I_H3_CODEC_ADC_DBG, 156862306a36Sopenharmony_ci}; 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_cistatic const struct regmap_config sun8i_v3s_codec_regmap_config = { 157162306a36Sopenharmony_ci .reg_bits = 32, 157262306a36Sopenharmony_ci .reg_stride = 4, 157362306a36Sopenharmony_ci .val_bits = 32, 157462306a36Sopenharmony_ci .max_register = SUN8I_H3_CODEC_ADC_DBG, 157562306a36Sopenharmony_ci}; 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_cistruct sun4i_codec_quirks { 157862306a36Sopenharmony_ci const struct regmap_config *regmap_config; 157962306a36Sopenharmony_ci const struct snd_soc_component_driver *codec; 158062306a36Sopenharmony_ci struct snd_soc_card * (*create_card)(struct device *dev); 158162306a36Sopenharmony_ci struct reg_field reg_adc_fifoc; /* used for regmap_field */ 158262306a36Sopenharmony_ci unsigned int reg_dac_txdata; /* TX FIFO offset for DMA config */ 158362306a36Sopenharmony_ci unsigned int reg_adc_rxdata; /* RX FIFO offset for DMA config */ 158462306a36Sopenharmony_ci bool has_reset; 158562306a36Sopenharmony_ci}; 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_cistatic const struct sun4i_codec_quirks sun4i_codec_quirks = { 158862306a36Sopenharmony_ci .regmap_config = &sun4i_codec_regmap_config, 158962306a36Sopenharmony_ci .codec = &sun4i_codec_codec, 159062306a36Sopenharmony_ci .create_card = sun4i_codec_create_card, 159162306a36Sopenharmony_ci .reg_adc_fifoc = REG_FIELD(SUN4I_CODEC_ADC_FIFOC, 0, 31), 159262306a36Sopenharmony_ci .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA, 159362306a36Sopenharmony_ci .reg_adc_rxdata = SUN4I_CODEC_ADC_RXDATA, 159462306a36Sopenharmony_ci}; 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_cistatic const struct sun4i_codec_quirks sun6i_a31_codec_quirks = { 159762306a36Sopenharmony_ci .regmap_config = &sun6i_codec_regmap_config, 159862306a36Sopenharmony_ci .codec = &sun6i_codec_codec, 159962306a36Sopenharmony_ci .create_card = sun6i_codec_create_card, 160062306a36Sopenharmony_ci .reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31), 160162306a36Sopenharmony_ci .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA, 160262306a36Sopenharmony_ci .reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA, 160362306a36Sopenharmony_ci .has_reset = true, 160462306a36Sopenharmony_ci}; 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_cistatic const struct sun4i_codec_quirks sun7i_codec_quirks = { 160762306a36Sopenharmony_ci .regmap_config = &sun7i_codec_regmap_config, 160862306a36Sopenharmony_ci .codec = &sun7i_codec_codec, 160962306a36Sopenharmony_ci .create_card = sun4i_codec_create_card, 161062306a36Sopenharmony_ci .reg_adc_fifoc = REG_FIELD(SUN4I_CODEC_ADC_FIFOC, 0, 31), 161162306a36Sopenharmony_ci .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA, 161262306a36Sopenharmony_ci .reg_adc_rxdata = SUN4I_CODEC_ADC_RXDATA, 161362306a36Sopenharmony_ci}; 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_cistatic const struct sun4i_codec_quirks sun8i_a23_codec_quirks = { 161662306a36Sopenharmony_ci .regmap_config = &sun8i_a23_codec_regmap_config, 161762306a36Sopenharmony_ci .codec = &sun8i_a23_codec_codec, 161862306a36Sopenharmony_ci .create_card = sun8i_a23_codec_create_card, 161962306a36Sopenharmony_ci .reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31), 162062306a36Sopenharmony_ci .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA, 162162306a36Sopenharmony_ci .reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA, 162262306a36Sopenharmony_ci .has_reset = true, 162362306a36Sopenharmony_ci}; 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_cistatic const struct sun4i_codec_quirks sun8i_h3_codec_quirks = { 162662306a36Sopenharmony_ci .regmap_config = &sun8i_h3_codec_regmap_config, 162762306a36Sopenharmony_ci /* 162862306a36Sopenharmony_ci * TODO Share the codec structure with A23 for now. 162962306a36Sopenharmony_ci * This should be split out when adding digital audio 163062306a36Sopenharmony_ci * processing support for the H3. 163162306a36Sopenharmony_ci */ 163262306a36Sopenharmony_ci .codec = &sun8i_a23_codec_codec, 163362306a36Sopenharmony_ci .create_card = sun8i_h3_codec_create_card, 163462306a36Sopenharmony_ci .reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31), 163562306a36Sopenharmony_ci .reg_dac_txdata = SUN8I_H3_CODEC_DAC_TXDATA, 163662306a36Sopenharmony_ci .reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA, 163762306a36Sopenharmony_ci .has_reset = true, 163862306a36Sopenharmony_ci}; 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_cistatic const struct sun4i_codec_quirks sun8i_v3s_codec_quirks = { 164162306a36Sopenharmony_ci .regmap_config = &sun8i_v3s_codec_regmap_config, 164262306a36Sopenharmony_ci /* 164362306a36Sopenharmony_ci * TODO The codec structure should be split out, like 164462306a36Sopenharmony_ci * H3, when adding digital audio processing support. 164562306a36Sopenharmony_ci */ 164662306a36Sopenharmony_ci .codec = &sun8i_a23_codec_codec, 164762306a36Sopenharmony_ci .create_card = sun8i_v3s_codec_create_card, 164862306a36Sopenharmony_ci .reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31), 164962306a36Sopenharmony_ci .reg_dac_txdata = SUN8I_H3_CODEC_DAC_TXDATA, 165062306a36Sopenharmony_ci .reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA, 165162306a36Sopenharmony_ci .has_reset = true, 165262306a36Sopenharmony_ci}; 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_cistatic const struct of_device_id sun4i_codec_of_match[] = { 165562306a36Sopenharmony_ci { 165662306a36Sopenharmony_ci .compatible = "allwinner,sun4i-a10-codec", 165762306a36Sopenharmony_ci .data = &sun4i_codec_quirks, 165862306a36Sopenharmony_ci }, 165962306a36Sopenharmony_ci { 166062306a36Sopenharmony_ci .compatible = "allwinner,sun6i-a31-codec", 166162306a36Sopenharmony_ci .data = &sun6i_a31_codec_quirks, 166262306a36Sopenharmony_ci }, 166362306a36Sopenharmony_ci { 166462306a36Sopenharmony_ci .compatible = "allwinner,sun7i-a20-codec", 166562306a36Sopenharmony_ci .data = &sun7i_codec_quirks, 166662306a36Sopenharmony_ci }, 166762306a36Sopenharmony_ci { 166862306a36Sopenharmony_ci .compatible = "allwinner,sun8i-a23-codec", 166962306a36Sopenharmony_ci .data = &sun8i_a23_codec_quirks, 167062306a36Sopenharmony_ci }, 167162306a36Sopenharmony_ci { 167262306a36Sopenharmony_ci .compatible = "allwinner,sun8i-h3-codec", 167362306a36Sopenharmony_ci .data = &sun8i_h3_codec_quirks, 167462306a36Sopenharmony_ci }, 167562306a36Sopenharmony_ci { 167662306a36Sopenharmony_ci .compatible = "allwinner,sun8i-v3s-codec", 167762306a36Sopenharmony_ci .data = &sun8i_v3s_codec_quirks, 167862306a36Sopenharmony_ci }, 167962306a36Sopenharmony_ci {} 168062306a36Sopenharmony_ci}; 168162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, sun4i_codec_of_match); 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_cistatic int sun4i_codec_probe(struct platform_device *pdev) 168462306a36Sopenharmony_ci{ 168562306a36Sopenharmony_ci struct snd_soc_card *card; 168662306a36Sopenharmony_ci struct sun4i_codec *scodec; 168762306a36Sopenharmony_ci const struct sun4i_codec_quirks *quirks; 168862306a36Sopenharmony_ci struct resource *res; 168962306a36Sopenharmony_ci void __iomem *base; 169062306a36Sopenharmony_ci int ret; 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_ci scodec = devm_kzalloc(&pdev->dev, sizeof(*scodec), GFP_KERNEL); 169362306a36Sopenharmony_ci if (!scodec) 169462306a36Sopenharmony_ci return -ENOMEM; 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_ci scodec->dev = &pdev->dev; 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 169962306a36Sopenharmony_ci if (IS_ERR(base)) 170062306a36Sopenharmony_ci return PTR_ERR(base); 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_ci quirks = of_device_get_match_data(&pdev->dev); 170362306a36Sopenharmony_ci if (quirks == NULL) { 170462306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to determine the quirks to use\n"); 170562306a36Sopenharmony_ci return -ENODEV; 170662306a36Sopenharmony_ci } 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base, 170962306a36Sopenharmony_ci quirks->regmap_config); 171062306a36Sopenharmony_ci if (IS_ERR(scodec->regmap)) { 171162306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to create our regmap\n"); 171262306a36Sopenharmony_ci return PTR_ERR(scodec->regmap); 171362306a36Sopenharmony_ci } 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci /* Get the clocks from the DT */ 171662306a36Sopenharmony_ci scodec->clk_apb = devm_clk_get(&pdev->dev, "apb"); 171762306a36Sopenharmony_ci if (IS_ERR(scodec->clk_apb)) { 171862306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to get the APB clock\n"); 171962306a36Sopenharmony_ci return PTR_ERR(scodec->clk_apb); 172062306a36Sopenharmony_ci } 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci scodec->clk_module = devm_clk_get(&pdev->dev, "codec"); 172362306a36Sopenharmony_ci if (IS_ERR(scodec->clk_module)) { 172462306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to get the module clock\n"); 172562306a36Sopenharmony_ci return PTR_ERR(scodec->clk_module); 172662306a36Sopenharmony_ci } 172762306a36Sopenharmony_ci 172862306a36Sopenharmony_ci if (quirks->has_reset) { 172962306a36Sopenharmony_ci scodec->rst = devm_reset_control_get_exclusive(&pdev->dev, 173062306a36Sopenharmony_ci NULL); 173162306a36Sopenharmony_ci if (IS_ERR(scodec->rst)) { 173262306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to get reset control\n"); 173362306a36Sopenharmony_ci return PTR_ERR(scodec->rst); 173462306a36Sopenharmony_ci } 173562306a36Sopenharmony_ci } 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_ci scodec->gpio_pa = devm_gpiod_get_optional(&pdev->dev, "allwinner,pa", 173862306a36Sopenharmony_ci GPIOD_OUT_LOW); 173962306a36Sopenharmony_ci if (IS_ERR(scodec->gpio_pa)) { 174062306a36Sopenharmony_ci ret = PTR_ERR(scodec->gpio_pa); 174162306a36Sopenharmony_ci dev_err_probe(&pdev->dev, ret, "Failed to get pa gpio\n"); 174262306a36Sopenharmony_ci return ret; 174362306a36Sopenharmony_ci } 174462306a36Sopenharmony_ci 174562306a36Sopenharmony_ci /* reg_field setup */ 174662306a36Sopenharmony_ci scodec->reg_adc_fifoc = devm_regmap_field_alloc(&pdev->dev, 174762306a36Sopenharmony_ci scodec->regmap, 174862306a36Sopenharmony_ci quirks->reg_adc_fifoc); 174962306a36Sopenharmony_ci if (IS_ERR(scodec->reg_adc_fifoc)) { 175062306a36Sopenharmony_ci ret = PTR_ERR(scodec->reg_adc_fifoc); 175162306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to create regmap fields: %d\n", 175262306a36Sopenharmony_ci ret); 175362306a36Sopenharmony_ci return ret; 175462306a36Sopenharmony_ci } 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ci /* Enable the bus clock */ 175762306a36Sopenharmony_ci if (clk_prepare_enable(scodec->clk_apb)) { 175862306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to enable the APB clock\n"); 175962306a36Sopenharmony_ci return -EINVAL; 176062306a36Sopenharmony_ci } 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_ci /* Deassert the reset control */ 176362306a36Sopenharmony_ci if (scodec->rst) { 176462306a36Sopenharmony_ci ret = reset_control_deassert(scodec->rst); 176562306a36Sopenharmony_ci if (ret) { 176662306a36Sopenharmony_ci dev_err(&pdev->dev, 176762306a36Sopenharmony_ci "Failed to deassert the reset control\n"); 176862306a36Sopenharmony_ci goto err_clk_disable; 176962306a36Sopenharmony_ci } 177062306a36Sopenharmony_ci } 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ci /* DMA configuration for TX FIFO */ 177362306a36Sopenharmony_ci scodec->playback_dma_data.addr = res->start + quirks->reg_dac_txdata; 177462306a36Sopenharmony_ci scodec->playback_dma_data.maxburst = 8; 177562306a36Sopenharmony_ci scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_ci /* DMA configuration for RX FIFO */ 177862306a36Sopenharmony_ci scodec->capture_dma_data.addr = res->start + quirks->reg_adc_rxdata; 177962306a36Sopenharmony_ci scodec->capture_dma_data.maxburst = 8; 178062306a36Sopenharmony_ci scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 178162306a36Sopenharmony_ci 178262306a36Sopenharmony_ci ret = devm_snd_soc_register_component(&pdev->dev, quirks->codec, 178362306a36Sopenharmony_ci &sun4i_codec_dai, 1); 178462306a36Sopenharmony_ci if (ret) { 178562306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to register our codec\n"); 178662306a36Sopenharmony_ci goto err_assert_reset; 178762306a36Sopenharmony_ci } 178862306a36Sopenharmony_ci 178962306a36Sopenharmony_ci ret = devm_snd_soc_register_component(&pdev->dev, 179062306a36Sopenharmony_ci &sun4i_codec_component, 179162306a36Sopenharmony_ci &dummy_cpu_dai, 1); 179262306a36Sopenharmony_ci if (ret) { 179362306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to register our DAI\n"); 179462306a36Sopenharmony_ci goto err_assert_reset; 179562306a36Sopenharmony_ci } 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_ci ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); 179862306a36Sopenharmony_ci if (ret) { 179962306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to register against DMAEngine\n"); 180062306a36Sopenharmony_ci goto err_assert_reset; 180162306a36Sopenharmony_ci } 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_ci card = quirks->create_card(&pdev->dev); 180462306a36Sopenharmony_ci if (IS_ERR(card)) { 180562306a36Sopenharmony_ci ret = PTR_ERR(card); 180662306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to create our card\n"); 180762306a36Sopenharmony_ci goto err_assert_reset; 180862306a36Sopenharmony_ci } 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci snd_soc_card_set_drvdata(card, scodec); 181162306a36Sopenharmony_ci 181262306a36Sopenharmony_ci ret = snd_soc_register_card(card); 181362306a36Sopenharmony_ci if (ret) { 181462306a36Sopenharmony_ci dev_err_probe(&pdev->dev, ret, "Failed to register our card\n"); 181562306a36Sopenharmony_ci goto err_assert_reset; 181662306a36Sopenharmony_ci } 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_ci return 0; 181962306a36Sopenharmony_ci 182062306a36Sopenharmony_cierr_assert_reset: 182162306a36Sopenharmony_ci if (scodec->rst) 182262306a36Sopenharmony_ci reset_control_assert(scodec->rst); 182362306a36Sopenharmony_cierr_clk_disable: 182462306a36Sopenharmony_ci clk_disable_unprepare(scodec->clk_apb); 182562306a36Sopenharmony_ci return ret; 182662306a36Sopenharmony_ci} 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_cistatic void sun4i_codec_remove(struct platform_device *pdev) 182962306a36Sopenharmony_ci{ 183062306a36Sopenharmony_ci struct snd_soc_card *card = platform_get_drvdata(pdev); 183162306a36Sopenharmony_ci struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card); 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_ci snd_soc_unregister_card(card); 183462306a36Sopenharmony_ci if (scodec->rst) 183562306a36Sopenharmony_ci reset_control_assert(scodec->rst); 183662306a36Sopenharmony_ci clk_disable_unprepare(scodec->clk_apb); 183762306a36Sopenharmony_ci} 183862306a36Sopenharmony_ci 183962306a36Sopenharmony_cistatic struct platform_driver sun4i_codec_driver = { 184062306a36Sopenharmony_ci .driver = { 184162306a36Sopenharmony_ci .name = "sun4i-codec", 184262306a36Sopenharmony_ci .of_match_table = sun4i_codec_of_match, 184362306a36Sopenharmony_ci }, 184462306a36Sopenharmony_ci .probe = sun4i_codec_probe, 184562306a36Sopenharmony_ci .remove_new = sun4i_codec_remove, 184662306a36Sopenharmony_ci}; 184762306a36Sopenharmony_cimodule_platform_driver(sun4i_codec_driver); 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ciMODULE_DESCRIPTION("Allwinner A10 codec driver"); 185062306a36Sopenharmony_ciMODULE_AUTHOR("Emilio López <emilio@elopez.com.ar>"); 185162306a36Sopenharmony_ciMODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>"); 185262306a36Sopenharmony_ciMODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); 185362306a36Sopenharmony_ciMODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>"); 185462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1855