12159a681SPaul Cercueil // SPDX-License-Identifier: GPL-2.0 22159a681SPaul Cercueil // 32159a681SPaul Cercueil // Ingenic JZ4770 CODEC driver 42159a681SPaul Cercueil // 52159a681SPaul Cercueil // Copyright (C) 2012, Maarten ter Huurne <maarten@treewalker.org> 62159a681SPaul Cercueil // Copyright (C) 2019, Paul Cercueil <paul@crapouillou.net> 72159a681SPaul Cercueil 82159a681SPaul Cercueil #include <linux/clk.h> 92159a681SPaul Cercueil #include <linux/delay.h> 102159a681SPaul Cercueil #include <linux/iopoll.h> 112159a681SPaul Cercueil #include <linux/module.h> 122159a681SPaul Cercueil #include <linux/regmap.h> 132159a681SPaul Cercueil #include <linux/time64.h> 142159a681SPaul Cercueil 152159a681SPaul Cercueil #include <sound/pcm_params.h> 162159a681SPaul Cercueil #include <sound/soc.h> 172159a681SPaul Cercueil #include <sound/soc-dai.h> 182159a681SPaul Cercueil #include <sound/soc-dapm.h> 192159a681SPaul Cercueil #include <sound/tlv.h> 202159a681SPaul Cercueil 212159a681SPaul Cercueil #define ICDC_RGADW_OFFSET 0x00 222159a681SPaul Cercueil #define ICDC_RGDATA_OFFSET 0x04 232159a681SPaul Cercueil 242159a681SPaul Cercueil /* ICDC internal register access control register(RGADW) */ 252159a681SPaul Cercueil #define ICDC_RGADW_RGWR BIT(16) 262159a681SPaul Cercueil 272159a681SPaul Cercueil #define ICDC_RGADW_RGADDR_OFFSET 8 282159a681SPaul Cercueil #define ICDC_RGADW_RGADDR_MASK GENMASK(14, ICDC_RGADW_RGADDR_OFFSET) 292159a681SPaul Cercueil 302159a681SPaul Cercueil #define ICDC_RGADW_RGDIN_OFFSET 0 312159a681SPaul Cercueil #define ICDC_RGADW_RGDIN_MASK GENMASK(7, ICDC_RGADW_RGDIN_OFFSET) 322159a681SPaul Cercueil 332159a681SPaul Cercueil /* ICDC internal register data output register (RGDATA)*/ 342159a681SPaul Cercueil #define ICDC_RGDATA_IRQ BIT(8) 352159a681SPaul Cercueil 362159a681SPaul Cercueil #define ICDC_RGDATA_RGDOUT_OFFSET 0 372159a681SPaul Cercueil #define ICDC_RGDATA_RGDOUT_MASK GENMASK(7, ICDC_RGDATA_RGDOUT_OFFSET) 382159a681SPaul Cercueil 392159a681SPaul Cercueil /* Internal register space, accessed through regmap */ 402159a681SPaul Cercueil enum { 412159a681SPaul Cercueil JZ4770_CODEC_REG_SR, 422159a681SPaul Cercueil JZ4770_CODEC_REG_AICR_DAC, 432159a681SPaul Cercueil JZ4770_CODEC_REG_AICR_ADC, 442159a681SPaul Cercueil JZ4770_CODEC_REG_CR_LO, 452159a681SPaul Cercueil JZ4770_CODEC_REG_CR_HP, 462159a681SPaul Cercueil 472159a681SPaul Cercueil JZ4770_CODEC_REG_MISSING_REG1, 482159a681SPaul Cercueil 492159a681SPaul Cercueil JZ4770_CODEC_REG_CR_DAC, 502159a681SPaul Cercueil JZ4770_CODEC_REG_CR_MIC, 512159a681SPaul Cercueil JZ4770_CODEC_REG_CR_LI, 522159a681SPaul Cercueil JZ4770_CODEC_REG_CR_ADC, 532159a681SPaul Cercueil JZ4770_CODEC_REG_CR_MIX, 542159a681SPaul Cercueil JZ4770_CODEC_REG_CR_VIC, 552159a681SPaul Cercueil JZ4770_CODEC_REG_CCR, 562159a681SPaul Cercueil JZ4770_CODEC_REG_FCR_DAC, 572159a681SPaul Cercueil JZ4770_CODEC_REG_FCR_ADC, 582159a681SPaul Cercueil JZ4770_CODEC_REG_ICR, 592159a681SPaul Cercueil JZ4770_CODEC_REG_IMR, 602159a681SPaul Cercueil JZ4770_CODEC_REG_IFR, 612159a681SPaul Cercueil JZ4770_CODEC_REG_GCR_HPL, 622159a681SPaul Cercueil JZ4770_CODEC_REG_GCR_HPR, 632159a681SPaul Cercueil JZ4770_CODEC_REG_GCR_LIBYL, 642159a681SPaul Cercueil JZ4770_CODEC_REG_GCR_LIBYR, 652159a681SPaul Cercueil JZ4770_CODEC_REG_GCR_DACL, 662159a681SPaul Cercueil JZ4770_CODEC_REG_GCR_DACR, 672159a681SPaul Cercueil JZ4770_CODEC_REG_GCR_MIC1, 682159a681SPaul Cercueil JZ4770_CODEC_REG_GCR_MIC2, 692159a681SPaul Cercueil JZ4770_CODEC_REG_GCR_ADCL, 702159a681SPaul Cercueil JZ4770_CODEC_REG_GCR_ADCR, 712159a681SPaul Cercueil 722159a681SPaul Cercueil JZ4770_CODEC_REG_MISSING_REG2, 732159a681SPaul Cercueil 742159a681SPaul Cercueil JZ4770_CODEC_REG_GCR_MIXADC, 752159a681SPaul Cercueil JZ4770_CODEC_REG_GCR_MIXDAC, 762159a681SPaul Cercueil JZ4770_CODEC_REG_AGC1, 772159a681SPaul Cercueil JZ4770_CODEC_REG_AGC2, 782159a681SPaul Cercueil JZ4770_CODEC_REG_AGC3, 792159a681SPaul Cercueil JZ4770_CODEC_REG_AGC4, 802159a681SPaul Cercueil JZ4770_CODEC_REG_AGC5, 812159a681SPaul Cercueil }; 822159a681SPaul Cercueil 832159a681SPaul Cercueil #define REG_AICR_DAC_ADWL_OFFSET 6 842159a681SPaul Cercueil #define REG_AICR_DAC_ADWL_MASK (0x3 << REG_AICR_DAC_ADWL_OFFSET) 852159a681SPaul Cercueil #define REG_AICR_DAC_SERIAL BIT(1) 862159a681SPaul Cercueil #define REG_AICR_DAC_I2S BIT(0) 872159a681SPaul Cercueil 882159a681SPaul Cercueil #define REG_AICR_ADC_ADWL_OFFSET 6 892159a681SPaul Cercueil #define REG_AICR_ADC_ADWL_MASK (0x3 << REG_AICR_ADC_ADWL_OFFSET) 902159a681SPaul Cercueil #define REG_AICR_ADC_SERIAL BIT(1) 912159a681SPaul Cercueil #define REG_AICR_ADC_I2S BIT(0) 922159a681SPaul Cercueil 932159a681SPaul Cercueil #define REG_CR_LO_MUTE_OFFSET 7 942159a681SPaul Cercueil #define REG_CR_LO_SB_OFFSET 4 952159a681SPaul Cercueil #define REG_CR_LO_SEL_OFFSET 0 962159a681SPaul Cercueil #define REG_CR_LO_SEL_MASK (0x3 << REG_CR_LO_SEL_OFFSET) 972159a681SPaul Cercueil 982159a681SPaul Cercueil #define REG_CR_HP_MUTE BIT(7) 992159a681SPaul Cercueil #define REG_CR_HP_LOAD BIT(6) 1002159a681SPaul Cercueil #define REG_CR_HP_SB_OFFSET 4 1012159a681SPaul Cercueil #define REG_CR_HP_SB_HPCM BIT(3) 1022159a681SPaul Cercueil #define REG_CR_HP_SEL_OFFSET 0 1032159a681SPaul Cercueil #define REG_CR_HP_SEL_MASK (0x3 << REG_CR_HP_SEL_OFFSET) 1042159a681SPaul Cercueil 1052159a681SPaul Cercueil #define REG_CR_DAC_MUTE BIT(7) 1062159a681SPaul Cercueil #define REG_CR_DAC_MONO BIT(6) 1072159a681SPaul Cercueil #define REG_CR_DAC_LEFT_ONLY BIT(5) 1082159a681SPaul Cercueil #define REG_CR_DAC_SB_OFFSET 4 1092159a681SPaul Cercueil #define REG_CR_DAC_LRSWAP BIT(3) 1102159a681SPaul Cercueil 1112159a681SPaul Cercueil #define REG_CR_MIC_STEREO_OFFSET 7 1122159a681SPaul Cercueil #define REG_CR_MIC_IDIFF_OFFSET 6 1132159a681SPaul Cercueil #define REG_CR_MIC_SB_MIC2_OFFSET 5 1142159a681SPaul Cercueil #define REG_CR_MIC_SB_MIC1_OFFSET 4 1152159a681SPaul Cercueil #define REG_CR_MIC_BIAS_V0_OFFSET 1 1162159a681SPaul Cercueil #define REG_CR_MIC_BIAS_SB_OFFSET 0 1172159a681SPaul Cercueil 1182159a681SPaul Cercueil #define REG_CR_LI_LIBY_OFFSET 4 1192159a681SPaul Cercueil #define REG_CR_LI_SB_OFFSET 0 1202159a681SPaul Cercueil 1212159a681SPaul Cercueil #define REG_CR_ADC_DMIC_SEL BIT(7) 1222159a681SPaul Cercueil #define REG_CR_ADC_MONO BIT(6) 1232159a681SPaul Cercueil #define REG_CR_ADC_LEFT_ONLY BIT(5) 1242159a681SPaul Cercueil #define REG_CR_ADC_SB_OFFSET 4 1252159a681SPaul Cercueil #define REG_CR_ADC_LRSWAP BIT(3) 1262159a681SPaul Cercueil #define REG_CR_ADC_IN_SEL_OFFSET 0 1272159a681SPaul Cercueil #define REG_CR_ADC_IN_SEL_MASK (0x3 << REG_CR_ADC_IN_SEL_OFFSET) 1282159a681SPaul Cercueil 1292159a681SPaul Cercueil #define REG_CR_VIC_SB_SLEEP BIT(1) 1302159a681SPaul Cercueil #define REG_CR_VIC_SB BIT(0) 1312159a681SPaul Cercueil 1322159a681SPaul Cercueil #define REG_CCR_CRYSTAL_OFFSET 0 1332159a681SPaul Cercueil #define REG_CCR_CRYSTAL_MASK (0xf << REG_CCR_CRYSTAL_OFFSET) 1342159a681SPaul Cercueil 1352159a681SPaul Cercueil #define REG_FCR_DAC_FREQ_OFFSET 0 1362159a681SPaul Cercueil #define REG_FCR_DAC_FREQ_MASK (0xf << REG_FCR_DAC_FREQ_OFFSET) 1372159a681SPaul Cercueil 1382159a681SPaul Cercueil #define REG_FCR_ADC_FREQ_OFFSET 0 1392159a681SPaul Cercueil #define REG_FCR_ADC_FREQ_MASK (0xf << REG_FCR_ADC_FREQ_OFFSET) 1402159a681SPaul Cercueil 1412159a681SPaul Cercueil #define REG_ICR_INT_FORM_OFFSET 6 1422159a681SPaul Cercueil #define REG_ICR_INT_FORM_MASK (0x3 << REG_ICR_INT_FORM_OFFSET) 1432159a681SPaul Cercueil 1442159a681SPaul Cercueil #define REG_IMR_ALL_MASK (0x7f) 1452159a681SPaul Cercueil #define REG_IMR_SCLR_MASK BIT(6) 1462159a681SPaul Cercueil #define REG_IMR_JACK_MASK BIT(5) 1472159a681SPaul Cercueil #define REG_IMR_SCMC_MASK BIT(4) 1482159a681SPaul Cercueil #define REG_IMR_RUP_MASK BIT(3) 1492159a681SPaul Cercueil #define REG_IMR_RDO_MASK BIT(2) 1502159a681SPaul Cercueil #define REG_IMR_GUP_MASK BIT(1) 1512159a681SPaul Cercueil #define REG_IMR_GDO_MASK BIT(0) 1522159a681SPaul Cercueil 1532159a681SPaul Cercueil #define REG_IFR_ALL_MASK (0x7f) 1542159a681SPaul Cercueil #define REG_IFR_SCLR BIT(6) 1552159a681SPaul Cercueil #define REG_IFR_JACK BIT(5) 1562159a681SPaul Cercueil #define REG_IFR_SCMC BIT(4) 1572159a681SPaul Cercueil #define REG_IFR_RUP BIT(3) 1582159a681SPaul Cercueil #define REG_IFR_RDO BIT(2) 1592159a681SPaul Cercueil #define REG_IFR_GUP BIT(1) 1602159a681SPaul Cercueil #define REG_IFR_GDO BIT(0) 1612159a681SPaul Cercueil 1622159a681SPaul Cercueil #define REG_GCR_HPL_LRGO BIT(7) 1632159a681SPaul Cercueil 1642159a681SPaul Cercueil #define REG_GCR_DACL_RLGOD BIT(7) 1652159a681SPaul Cercueil 1662159a681SPaul Cercueil #define REG_GCR_GAIN_OFFSET 0 1672159a681SPaul Cercueil #define REG_GCR_GAIN_MAX 0x1f 1682159a681SPaul Cercueil 1692159a681SPaul Cercueil #define REG_GCR_MIC_GAIN_OFFSET 0 1702159a681SPaul Cercueil #define REG_GCR_MIC_GAIN_MAX 5 1712159a681SPaul Cercueil 1722159a681SPaul Cercueil #define REG_GCR_ADC_GAIN_OFFSET 0 1732159a681SPaul Cercueil #define REG_GCR_ADC_GAIN_MAX 23 1742159a681SPaul Cercueil 1752159a681SPaul Cercueil #define REG_AGC1_EN BIT(7) 1762159a681SPaul Cercueil 1772159a681SPaul Cercueil /* codec private data */ 1782159a681SPaul Cercueil struct jz_codec { 1792159a681SPaul Cercueil struct device *dev; 1802159a681SPaul Cercueil struct regmap *regmap; 1812159a681SPaul Cercueil void __iomem *base; 1822159a681SPaul Cercueil struct clk *clk; 1832159a681SPaul Cercueil }; 1842159a681SPaul Cercueil 1852159a681SPaul Cercueil static int jz4770_codec_set_bias_level(struct snd_soc_component *codec, 1862159a681SPaul Cercueil enum snd_soc_bias_level level) 1872159a681SPaul Cercueil { 1882159a681SPaul Cercueil struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec); 1892159a681SPaul Cercueil struct regmap *regmap = jz_codec->regmap; 1902159a681SPaul Cercueil 1912159a681SPaul Cercueil switch (level) { 1922159a681SPaul Cercueil case SND_SOC_BIAS_PREPARE: 1932159a681SPaul Cercueil regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_VIC, 1942159a681SPaul Cercueil REG_CR_VIC_SB, 0); 1952159a681SPaul Cercueil msleep(250); 1962159a681SPaul Cercueil regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_VIC, 1972159a681SPaul Cercueil REG_CR_VIC_SB_SLEEP, 0); 1982159a681SPaul Cercueil msleep(400); 1992159a681SPaul Cercueil break; 2002159a681SPaul Cercueil case SND_SOC_BIAS_STANDBY: 2012159a681SPaul Cercueil regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_VIC, 2022159a681SPaul Cercueil REG_CR_VIC_SB_SLEEP, REG_CR_VIC_SB_SLEEP); 2032159a681SPaul Cercueil regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_VIC, 2042159a681SPaul Cercueil REG_CR_VIC_SB, REG_CR_VIC_SB); 205*df561f66SGustavo A. R. Silva fallthrough; 2062159a681SPaul Cercueil default: 2072159a681SPaul Cercueil break; 2082159a681SPaul Cercueil } 2092159a681SPaul Cercueil 2102159a681SPaul Cercueil return 0; 2112159a681SPaul Cercueil } 2122159a681SPaul Cercueil 2132159a681SPaul Cercueil static int jz4770_codec_startup(struct snd_pcm_substream *substream, 2142159a681SPaul Cercueil struct snd_soc_dai *dai) 2152159a681SPaul Cercueil { 2162159a681SPaul Cercueil struct snd_soc_component *codec = dai->component; 2172159a681SPaul Cercueil struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(codec); 2182159a681SPaul Cercueil 2192159a681SPaul Cercueil /* 2202159a681SPaul Cercueil * SYSCLK output from the codec to the AIC is required to keep the 2212159a681SPaul Cercueil * DMA transfer going during playback when all audible outputs have 2222159a681SPaul Cercueil * been disabled. 2232159a681SPaul Cercueil */ 2242159a681SPaul Cercueil if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 2252159a681SPaul Cercueil snd_soc_dapm_force_enable_pin(dapm, "SYSCLK"); 2262159a681SPaul Cercueil 2272159a681SPaul Cercueil return 0; 2282159a681SPaul Cercueil } 2292159a681SPaul Cercueil 2302159a681SPaul Cercueil static void jz4770_codec_shutdown(struct snd_pcm_substream *substream, 2312159a681SPaul Cercueil struct snd_soc_dai *dai) 2322159a681SPaul Cercueil { 2332159a681SPaul Cercueil struct snd_soc_component *codec = dai->component; 2342159a681SPaul Cercueil struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(codec); 2352159a681SPaul Cercueil 2362159a681SPaul Cercueil if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 2372159a681SPaul Cercueil snd_soc_dapm_disable_pin(dapm, "SYSCLK"); 2382159a681SPaul Cercueil } 2392159a681SPaul Cercueil 2402159a681SPaul Cercueil 2412159a681SPaul Cercueil static int jz4770_codec_pcm_trigger(struct snd_pcm_substream *substream, 2422159a681SPaul Cercueil int cmd, struct snd_soc_dai *dai) 2432159a681SPaul Cercueil { 2442159a681SPaul Cercueil struct snd_soc_component *codec = dai->component; 2452159a681SPaul Cercueil int ret = 0; 2462159a681SPaul Cercueil 2472159a681SPaul Cercueil switch (cmd) { 2482159a681SPaul Cercueil case SNDRV_PCM_TRIGGER_START: 2492159a681SPaul Cercueil case SNDRV_PCM_TRIGGER_RESUME: 2502159a681SPaul Cercueil case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 2512159a681SPaul Cercueil if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) 2522159a681SPaul Cercueil snd_soc_component_force_bias_level(codec, 2532159a681SPaul Cercueil SND_SOC_BIAS_ON); 2542159a681SPaul Cercueil break; 2552159a681SPaul Cercueil case SNDRV_PCM_TRIGGER_STOP: 2562159a681SPaul Cercueil case SNDRV_PCM_TRIGGER_SUSPEND: 2572159a681SPaul Cercueil case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 2582159a681SPaul Cercueil /* do nothing */ 2592159a681SPaul Cercueil break; 2602159a681SPaul Cercueil default: 2612159a681SPaul Cercueil ret = -EINVAL; 2622159a681SPaul Cercueil } 2632159a681SPaul Cercueil 2642159a681SPaul Cercueil return ret; 2652159a681SPaul Cercueil } 2662159a681SPaul Cercueil 26754b59270SKuninori Morimoto static int jz4770_codec_mute_stream(struct snd_soc_dai *dai, int mute, int direction) 2682159a681SPaul Cercueil { 2692159a681SPaul Cercueil struct snd_soc_component *codec = dai->component; 2702159a681SPaul Cercueil struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec); 2712159a681SPaul Cercueil unsigned int gain_bit = mute ? REG_IFR_GDO : REG_IFR_GUP; 2722159a681SPaul Cercueil unsigned int val; 2732159a681SPaul Cercueil int change, err; 2742159a681SPaul Cercueil 2752159a681SPaul Cercueil change = snd_soc_component_update_bits(codec, JZ4770_CODEC_REG_CR_DAC, 2762159a681SPaul Cercueil REG_CR_DAC_MUTE, 2772159a681SPaul Cercueil mute ? REG_CR_DAC_MUTE : 0); 2782159a681SPaul Cercueil if (change == 1) { 2792159a681SPaul Cercueil regmap_read(jz_codec->regmap, JZ4770_CODEC_REG_CR_DAC, &val); 2802159a681SPaul Cercueil 2812159a681SPaul Cercueil if (val & BIT(REG_CR_DAC_SB_OFFSET)) 2822159a681SPaul Cercueil return 1; 2832159a681SPaul Cercueil 2842159a681SPaul Cercueil err = regmap_read_poll_timeout(jz_codec->regmap, 2852159a681SPaul Cercueil JZ4770_CODEC_REG_IFR, 2862159a681SPaul Cercueil val, val & gain_bit, 2872159a681SPaul Cercueil 1000, 100 * USEC_PER_MSEC); 2882159a681SPaul Cercueil if (err) { 2892159a681SPaul Cercueil dev_err(jz_codec->dev, 2902159a681SPaul Cercueil "Timeout while setting digital mute: %d", err); 2912159a681SPaul Cercueil return err; 2922159a681SPaul Cercueil } 2932159a681SPaul Cercueil 2942159a681SPaul Cercueil /* clear GUP/GDO flag */ 2952159a681SPaul Cercueil regmap_update_bits(jz_codec->regmap, JZ4770_CODEC_REG_IFR, 2962159a681SPaul Cercueil gain_bit, gain_bit); 2972159a681SPaul Cercueil } 2982159a681SPaul Cercueil 2992159a681SPaul Cercueil return 0; 3002159a681SPaul Cercueil } 3012159a681SPaul Cercueil 3022159a681SPaul Cercueil /* unit: 0.01dB */ 3032159a681SPaul Cercueil static const DECLARE_TLV_DB_MINMAX_MUTE(dac_tlv, -3100, 0); 3042159a681SPaul Cercueil static const DECLARE_TLV_DB_SCALE(adc_tlv, 0, 100, 0); 3052159a681SPaul Cercueil static const DECLARE_TLV_DB_MINMAX(out_tlv, -2500, 600); 3062159a681SPaul Cercueil static const DECLARE_TLV_DB_SCALE(linein_tlv, -2500, 100, 0); 3072159a681SPaul Cercueil 3082159a681SPaul Cercueil /* Unconditional controls. */ 3092159a681SPaul Cercueil static const struct snd_kcontrol_new jz4770_codec_snd_controls[] = { 3102159a681SPaul Cercueil /* record gain control */ 3112159a681SPaul Cercueil SOC_DOUBLE_R_TLV("PCM Capture Volume", 3122159a681SPaul Cercueil JZ4770_CODEC_REG_GCR_ADCL, JZ4770_CODEC_REG_GCR_ADCR, 3132159a681SPaul Cercueil REG_GCR_ADC_GAIN_OFFSET, REG_GCR_ADC_GAIN_MAX, 3142159a681SPaul Cercueil 0, adc_tlv), 3152159a681SPaul Cercueil 3162159a681SPaul Cercueil SOC_DOUBLE_R_TLV("Line In Bypass Playback Volume", 3172159a681SPaul Cercueil JZ4770_CODEC_REG_GCR_LIBYL, JZ4770_CODEC_REG_GCR_LIBYR, 3182159a681SPaul Cercueil REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, linein_tlv), 3192159a681SPaul Cercueil }; 3202159a681SPaul Cercueil 3212159a681SPaul Cercueil static const struct snd_kcontrol_new jz4770_codec_pcm_playback_controls[] = { 3222159a681SPaul Cercueil { 3232159a681SPaul Cercueil .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3242159a681SPaul Cercueil .name = "Volume", 3252159a681SPaul Cercueil .info = snd_soc_info_volsw, 3262159a681SPaul Cercueil .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ 3272159a681SPaul Cercueil | SNDRV_CTL_ELEM_ACCESS_READWRITE, 3282159a681SPaul Cercueil .tlv.p = dac_tlv, 3292159a681SPaul Cercueil .get = snd_soc_dapm_get_volsw, 3302159a681SPaul Cercueil .put = snd_soc_dapm_put_volsw, 3312159a681SPaul Cercueil /* 3322159a681SPaul Cercueil * NOTE: DACR/DACL are inversed; the gain value written to DACR 3332159a681SPaul Cercueil * seems to affect the left channel, and the gain value written 3342159a681SPaul Cercueil * to DACL seems to affect the right channel. 3352159a681SPaul Cercueil */ 3362159a681SPaul Cercueil .private_value = SOC_DOUBLE_R_VALUE(JZ4770_CODEC_REG_GCR_DACR, 3372159a681SPaul Cercueil JZ4770_CODEC_REG_GCR_DACL, 3382159a681SPaul Cercueil REG_GCR_GAIN_OFFSET, 3392159a681SPaul Cercueil REG_GCR_GAIN_MAX, 1), 3402159a681SPaul Cercueil }, 3412159a681SPaul Cercueil }; 3422159a681SPaul Cercueil 3432159a681SPaul Cercueil static const struct snd_kcontrol_new jz4770_codec_hp_playback_controls[] = { 3442159a681SPaul Cercueil { 3452159a681SPaul Cercueil .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3462159a681SPaul Cercueil .name = "Volume", 3472159a681SPaul Cercueil .info = snd_soc_info_volsw, 3482159a681SPaul Cercueil .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ 3492159a681SPaul Cercueil | SNDRV_CTL_ELEM_ACCESS_READWRITE, 3502159a681SPaul Cercueil .tlv.p = out_tlv, 3512159a681SPaul Cercueil .get = snd_soc_dapm_get_volsw, 3522159a681SPaul Cercueil .put = snd_soc_dapm_put_volsw, 3532159a681SPaul Cercueil /* HPR/HPL inversed for the same reason as above */ 3542159a681SPaul Cercueil .private_value = SOC_DOUBLE_R_VALUE(JZ4770_CODEC_REG_GCR_HPR, 3552159a681SPaul Cercueil JZ4770_CODEC_REG_GCR_HPL, 3562159a681SPaul Cercueil REG_GCR_GAIN_OFFSET, 3572159a681SPaul Cercueil REG_GCR_GAIN_MAX, 1), 3582159a681SPaul Cercueil }, 3592159a681SPaul Cercueil }; 3602159a681SPaul Cercueil 3612159a681SPaul Cercueil static int hpout_event(struct snd_soc_dapm_widget *w, 3622159a681SPaul Cercueil struct snd_kcontrol *kcontrol, int event) 3632159a681SPaul Cercueil { 3642159a681SPaul Cercueil struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm); 3652159a681SPaul Cercueil struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec); 3662159a681SPaul Cercueil unsigned int val; 3672159a681SPaul Cercueil int err; 3682159a681SPaul Cercueil 3692159a681SPaul Cercueil switch (event) { 3702159a681SPaul Cercueil case SND_SOC_DAPM_PRE_PMU: 3712159a681SPaul Cercueil /* set cap-less, unmute HP */ 3722159a681SPaul Cercueil regmap_update_bits(jz_codec->regmap, JZ4770_CODEC_REG_CR_HP, 3732159a681SPaul Cercueil REG_CR_HP_SB_HPCM | REG_CR_HP_MUTE, 0); 3742159a681SPaul Cercueil break; 3752159a681SPaul Cercueil 3762159a681SPaul Cercueil case SND_SOC_DAPM_POST_PMU: 3772159a681SPaul Cercueil /* wait for ramp-up complete (RUP) */ 3782159a681SPaul Cercueil err = regmap_read_poll_timeout(jz_codec->regmap, 3792159a681SPaul Cercueil JZ4770_CODEC_REG_IFR, 3802159a681SPaul Cercueil val, val & REG_IFR_RUP, 3812159a681SPaul Cercueil 1000, 100 * USEC_PER_MSEC); 3822159a681SPaul Cercueil if (err) { 3832159a681SPaul Cercueil dev_err(jz_codec->dev, "RUP timeout: %d", err); 3842159a681SPaul Cercueil return err; 3852159a681SPaul Cercueil } 3862159a681SPaul Cercueil 3872159a681SPaul Cercueil /* clear RUP flag */ 3882159a681SPaul Cercueil regmap_update_bits(jz_codec->regmap, JZ4770_CODEC_REG_IFR, 3892159a681SPaul Cercueil REG_IFR_RUP, REG_IFR_RUP); 3902159a681SPaul Cercueil 3912159a681SPaul Cercueil break; 3922159a681SPaul Cercueil 3932159a681SPaul Cercueil case SND_SOC_DAPM_POST_PMD: 3942159a681SPaul Cercueil /* set cap-couple, mute HP */ 3952159a681SPaul Cercueil regmap_update_bits(jz_codec->regmap, JZ4770_CODEC_REG_CR_HP, 3962159a681SPaul Cercueil REG_CR_HP_SB_HPCM | REG_CR_HP_MUTE, 3972159a681SPaul Cercueil REG_CR_HP_SB_HPCM | REG_CR_HP_MUTE); 3982159a681SPaul Cercueil 3992159a681SPaul Cercueil err = regmap_read_poll_timeout(jz_codec->regmap, 4002159a681SPaul Cercueil JZ4770_CODEC_REG_IFR, 4012159a681SPaul Cercueil val, val & REG_IFR_RDO, 4022159a681SPaul Cercueil 1000, 100 * USEC_PER_MSEC); 4032159a681SPaul Cercueil if (err) { 4042159a681SPaul Cercueil dev_err(jz_codec->dev, "RDO timeout: %d", err); 4052159a681SPaul Cercueil return err; 4062159a681SPaul Cercueil } 4072159a681SPaul Cercueil 4082159a681SPaul Cercueil /* clear RDO flag */ 4092159a681SPaul Cercueil regmap_update_bits(jz_codec->regmap, JZ4770_CODEC_REG_IFR, 4102159a681SPaul Cercueil REG_IFR_RDO, REG_IFR_RDO); 4112159a681SPaul Cercueil 4122159a681SPaul Cercueil break; 4132159a681SPaul Cercueil } 4142159a681SPaul Cercueil 4152159a681SPaul Cercueil return 0; 4162159a681SPaul Cercueil } 4172159a681SPaul Cercueil 4182159a681SPaul Cercueil static int adc_poweron_event(struct snd_soc_dapm_widget *w, 4192159a681SPaul Cercueil struct snd_kcontrol *kcontrol, int event) 4202159a681SPaul Cercueil { 4212159a681SPaul Cercueil if (event == SND_SOC_DAPM_POST_PMU) 4222159a681SPaul Cercueil msleep(1000); 4232159a681SPaul Cercueil 4242159a681SPaul Cercueil return 0; 4252159a681SPaul Cercueil } 4262159a681SPaul Cercueil 4272159a681SPaul Cercueil static const char * const jz4770_codec_hp_texts[] = { 4282159a681SPaul Cercueil "PCM", "Line In", "Mic 1", "Mic 2" 4292159a681SPaul Cercueil }; 4302159a681SPaul Cercueil static const unsigned int jz4770_codec_hp_values[] = { 3, 2, 0, 1 }; 4312159a681SPaul Cercueil static SOC_VALUE_ENUM_SINGLE_DECL(jz4770_codec_hp_enum, 4322159a681SPaul Cercueil JZ4770_CODEC_REG_CR_HP, 4332159a681SPaul Cercueil REG_CR_HP_SEL_OFFSET, 4342159a681SPaul Cercueil REG_CR_HP_SEL_MASK, 4352159a681SPaul Cercueil jz4770_codec_hp_texts, 4362159a681SPaul Cercueil jz4770_codec_hp_values); 4372159a681SPaul Cercueil static const struct snd_kcontrol_new jz4770_codec_hp_source = 4382159a681SPaul Cercueil SOC_DAPM_ENUM("Route", jz4770_codec_hp_enum); 4392159a681SPaul Cercueil 4402159a681SPaul Cercueil static SOC_VALUE_ENUM_SINGLE_DECL(jz4770_codec_lo_enum, 4412159a681SPaul Cercueil JZ4770_CODEC_REG_CR_LO, 4422159a681SPaul Cercueil REG_CR_LO_SEL_OFFSET, 4432159a681SPaul Cercueil REG_CR_LO_SEL_MASK, 4442159a681SPaul Cercueil jz4770_codec_hp_texts, 4452159a681SPaul Cercueil jz4770_codec_hp_values); 4462159a681SPaul Cercueil static const struct snd_kcontrol_new jz4770_codec_lo_source = 4472159a681SPaul Cercueil SOC_DAPM_ENUM("Route", jz4770_codec_lo_enum); 4482159a681SPaul Cercueil 4492159a681SPaul Cercueil static const char * const jz4770_codec_cap_texts[] = { 4502159a681SPaul Cercueil "Line In", "Mic 1", "Mic 2" 4512159a681SPaul Cercueil }; 4522159a681SPaul Cercueil static const unsigned int jz4770_codec_cap_values[] = { 2, 0, 1 }; 4532159a681SPaul Cercueil static SOC_VALUE_ENUM_SINGLE_DECL(jz4770_codec_cap_enum, 4542159a681SPaul Cercueil JZ4770_CODEC_REG_CR_ADC, 4552159a681SPaul Cercueil REG_CR_ADC_IN_SEL_OFFSET, 4562159a681SPaul Cercueil REG_CR_ADC_IN_SEL_MASK, 4572159a681SPaul Cercueil jz4770_codec_cap_texts, 4582159a681SPaul Cercueil jz4770_codec_cap_values); 4592159a681SPaul Cercueil static const struct snd_kcontrol_new jz4770_codec_cap_source = 4602159a681SPaul Cercueil SOC_DAPM_ENUM("Route", jz4770_codec_cap_enum); 4612159a681SPaul Cercueil 4622159a681SPaul Cercueil static const struct snd_kcontrol_new jz4770_codec_mic_controls[] = { 4632159a681SPaul Cercueil SOC_DAPM_SINGLE("Stereo Capture Switch", JZ4770_CODEC_REG_CR_MIC, 4642159a681SPaul Cercueil REG_CR_MIC_STEREO_OFFSET, 1, 0), 4652159a681SPaul Cercueil }; 4662159a681SPaul Cercueil 4672159a681SPaul Cercueil static const struct snd_soc_dapm_widget jz4770_codec_dapm_widgets[] = { 4682159a681SPaul Cercueil SND_SOC_DAPM_PGA_E("HP Out", JZ4770_CODEC_REG_CR_HP, 4692159a681SPaul Cercueil REG_CR_HP_SB_OFFSET, 1, NULL, 0, hpout_event, 4702159a681SPaul Cercueil SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | 4712159a681SPaul Cercueil SND_SOC_DAPM_POST_PMD), 4722159a681SPaul Cercueil 4732159a681SPaul Cercueil SND_SOC_DAPM_PGA("Line Out", JZ4770_CODEC_REG_CR_LO, 4742159a681SPaul Cercueil REG_CR_LO_SB_OFFSET, 1, NULL, 0), 4752159a681SPaul Cercueil 4762159a681SPaul Cercueil SND_SOC_DAPM_PGA("Line Out Switch 2", JZ4770_CODEC_REG_CR_LO, 4772159a681SPaul Cercueil REG_CR_LO_MUTE_OFFSET, 1, NULL, 0), 4782159a681SPaul Cercueil 4792159a681SPaul Cercueil SND_SOC_DAPM_PGA("Line In", JZ4770_CODEC_REG_CR_LI, 4802159a681SPaul Cercueil REG_CR_LI_SB_OFFSET, 1, NULL, 0), 4812159a681SPaul Cercueil 4822159a681SPaul Cercueil SND_SOC_DAPM_MUX("Headphones Source", SND_SOC_NOPM, 0, 0, 4832159a681SPaul Cercueil &jz4770_codec_hp_source), 4842159a681SPaul Cercueil SND_SOC_DAPM_MUX("Capture Source", SND_SOC_NOPM, 0, 0, 4852159a681SPaul Cercueil &jz4770_codec_cap_source), 4862159a681SPaul Cercueil SND_SOC_DAPM_MUX("Line Out Source", SND_SOC_NOPM, 0, 0, 4872159a681SPaul Cercueil &jz4770_codec_lo_source), 4882159a681SPaul Cercueil 4892159a681SPaul Cercueil SND_SOC_DAPM_PGA("Mic 1", JZ4770_CODEC_REG_CR_MIC, 4902159a681SPaul Cercueil REG_CR_MIC_SB_MIC1_OFFSET, 1, NULL, 0), 4912159a681SPaul Cercueil SND_SOC_DAPM_PGA("Mic 2", JZ4770_CODEC_REG_CR_MIC, 4922159a681SPaul Cercueil REG_CR_MIC_SB_MIC2_OFFSET, 1, NULL, 0), 4932159a681SPaul Cercueil 4942159a681SPaul Cercueil SND_SOC_DAPM_PGA("Mic Diff", JZ4770_CODEC_REG_CR_MIC, 4952159a681SPaul Cercueil REG_CR_MIC_IDIFF_OFFSET, 0, NULL, 0), 4962159a681SPaul Cercueil 4972159a681SPaul Cercueil SND_SOC_DAPM_MIXER("Mic", SND_SOC_NOPM, 0, 0, 4982159a681SPaul Cercueil jz4770_codec_mic_controls, 4992159a681SPaul Cercueil ARRAY_SIZE(jz4770_codec_mic_controls)), 5002159a681SPaul Cercueil 5012159a681SPaul Cercueil SND_SOC_DAPM_PGA("Line In Bypass", JZ4770_CODEC_REG_CR_LI, 5022159a681SPaul Cercueil REG_CR_LI_LIBY_OFFSET, 1, NULL, 0), 5032159a681SPaul Cercueil 5042159a681SPaul Cercueil SND_SOC_DAPM_ADC_E("ADC", "HiFi Capture", JZ4770_CODEC_REG_CR_ADC, 5052159a681SPaul Cercueil REG_CR_ADC_SB_OFFSET, 1, adc_poweron_event, 5062159a681SPaul Cercueil SND_SOC_DAPM_POST_PMU), 5072159a681SPaul Cercueil SND_SOC_DAPM_DAC("DAC", "HiFi Playback", JZ4770_CODEC_REG_CR_DAC, 5082159a681SPaul Cercueil REG_CR_DAC_SB_OFFSET, 1), 5092159a681SPaul Cercueil 5102159a681SPaul Cercueil SND_SOC_DAPM_MIXER("PCM Playback", SND_SOC_NOPM, 0, 0, 5112159a681SPaul Cercueil jz4770_codec_pcm_playback_controls, 5122159a681SPaul Cercueil ARRAY_SIZE(jz4770_codec_pcm_playback_controls)), 5132159a681SPaul Cercueil SND_SOC_DAPM_MIXER("Headphones Playback", SND_SOC_NOPM, 0, 0, 5142159a681SPaul Cercueil jz4770_codec_hp_playback_controls, 5152159a681SPaul Cercueil ARRAY_SIZE(jz4770_codec_hp_playback_controls)), 5162159a681SPaul Cercueil 5172159a681SPaul Cercueil SND_SOC_DAPM_SUPPLY("MICBIAS", JZ4770_CODEC_REG_CR_MIC, 5182159a681SPaul Cercueil REG_CR_MIC_BIAS_SB_OFFSET, 1, NULL, 0), 5192159a681SPaul Cercueil 5202159a681SPaul Cercueil SND_SOC_DAPM_INPUT("MIC1P"), 5212159a681SPaul Cercueil SND_SOC_DAPM_INPUT("MIC1N"), 5222159a681SPaul Cercueil SND_SOC_DAPM_INPUT("MIC2P"), 5232159a681SPaul Cercueil SND_SOC_DAPM_INPUT("MIC2N"), 5242159a681SPaul Cercueil 5252159a681SPaul Cercueil SND_SOC_DAPM_OUTPUT("LOUT"), 5262159a681SPaul Cercueil SND_SOC_DAPM_OUTPUT("ROUT"), 5272159a681SPaul Cercueil 5282159a681SPaul Cercueil SND_SOC_DAPM_OUTPUT("LHPOUT"), 5292159a681SPaul Cercueil SND_SOC_DAPM_OUTPUT("RHPOUT"), 5302159a681SPaul Cercueil 5312159a681SPaul Cercueil SND_SOC_DAPM_INPUT("LLINEIN"), 5322159a681SPaul Cercueil SND_SOC_DAPM_INPUT("RLINEIN"), 5332159a681SPaul Cercueil 5342159a681SPaul Cercueil SND_SOC_DAPM_OUTPUT("SYSCLK"), 5352159a681SPaul Cercueil }; 5362159a681SPaul Cercueil 5372159a681SPaul Cercueil /* Unconditional routes. */ 5382159a681SPaul Cercueil static const struct snd_soc_dapm_route jz4770_codec_dapm_routes[] = { 5392159a681SPaul Cercueil { "Mic 1", NULL, "MIC1P" }, 5402159a681SPaul Cercueil { "Mic Diff", NULL, "MIC1N" }, 5412159a681SPaul Cercueil { "Mic 1", NULL, "Mic Diff" }, 5422159a681SPaul Cercueil { "Mic 2", NULL, "MIC2P" }, 5432159a681SPaul Cercueil { "Mic Diff", NULL, "MIC2N" }, 5442159a681SPaul Cercueil { "Mic 2", NULL, "Mic Diff" }, 5452159a681SPaul Cercueil 5462159a681SPaul Cercueil { "Line In", NULL, "LLINEIN" }, 5472159a681SPaul Cercueil { "Line In", NULL, "RLINEIN" }, 5482159a681SPaul Cercueil 5492159a681SPaul Cercueil { "Mic", "Stereo Capture Switch", "Mic 1" }, 5502159a681SPaul Cercueil { "Mic", "Stereo Capture Switch", "Mic 2" }, 5512159a681SPaul Cercueil { "Headphones Source", "Mic 1", "Mic" }, 5522159a681SPaul Cercueil { "Headphones Source", "Mic 2", "Mic" }, 5532159a681SPaul Cercueil { "Capture Source", "Mic 1", "Mic" }, 5542159a681SPaul Cercueil { "Capture Source", "Mic 2", "Mic" }, 5552159a681SPaul Cercueil 5562159a681SPaul Cercueil { "Headphones Source", "Mic 1", "Mic 1" }, 5572159a681SPaul Cercueil { "Headphones Source", "Mic 2", "Mic 2" }, 5582159a681SPaul Cercueil { "Headphones Source", "Line In", "Line In Bypass" }, 5592159a681SPaul Cercueil { "Headphones Source", "PCM", "Headphones Playback" }, 5602159a681SPaul Cercueil { "HP Out", NULL, "Headphones Source" }, 5612159a681SPaul Cercueil 5622159a681SPaul Cercueil { "Capture Source", "Line In", "Line In" }, 5632159a681SPaul Cercueil { "Capture Source", "Mic 1", "Mic 1" }, 5642159a681SPaul Cercueil { "Capture Source", "Mic 2", "Mic 2" }, 5652159a681SPaul Cercueil { "ADC", NULL, "Capture Source" }, 5662159a681SPaul Cercueil 5672159a681SPaul Cercueil { "Line In Bypass", NULL, "Line In" }, 5682159a681SPaul Cercueil { "Line Out Source", "Line In", "Line In Bypass" }, 5692159a681SPaul Cercueil { "Line Out Source", "PCM", "PCM Playback" }, 5702159a681SPaul Cercueil 5712159a681SPaul Cercueil { "LHPOUT", NULL, "HP Out"}, 5722159a681SPaul Cercueil { "RHPOUT", NULL, "HP Out"}, 5732159a681SPaul Cercueil 5742159a681SPaul Cercueil { "Line Out", NULL, "Line Out Source" }, 5752159a681SPaul Cercueil { "Line Out Switch 2", NULL, "Line Out" }, 5762159a681SPaul Cercueil 5772159a681SPaul Cercueil { "LOUT", NULL, "Line Out Switch 2"}, 5782159a681SPaul Cercueil { "ROUT", NULL, "Line Out Switch 2"}, 5792159a681SPaul Cercueil 5802159a681SPaul Cercueil { "PCM Playback", "Volume", "DAC" }, 5812159a681SPaul Cercueil { "Headphones Playback", "Volume", "PCM Playback" }, 5822159a681SPaul Cercueil 5832159a681SPaul Cercueil { "SYSCLK", NULL, "DAC" }, 5842159a681SPaul Cercueil }; 5852159a681SPaul Cercueil 5862159a681SPaul Cercueil static void jz4770_codec_codec_init_regs(struct snd_soc_component *codec) 5872159a681SPaul Cercueil { 5882159a681SPaul Cercueil struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec); 5892159a681SPaul Cercueil struct regmap *regmap = jz_codec->regmap; 5902159a681SPaul Cercueil 5912159a681SPaul Cercueil /* Collect updates for later sending. */ 5922159a681SPaul Cercueil regcache_cache_only(regmap, true); 5932159a681SPaul Cercueil 5942159a681SPaul Cercueil /* default HP output to PCM */ 5952159a681SPaul Cercueil regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_HP, 5962159a681SPaul Cercueil REG_CR_HP_SEL_MASK, REG_CR_HP_SEL_MASK); 5972159a681SPaul Cercueil 5982159a681SPaul Cercueil /* default line output to PCM */ 5992159a681SPaul Cercueil regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_LO, 6002159a681SPaul Cercueil REG_CR_LO_SEL_MASK, REG_CR_LO_SEL_MASK); 6012159a681SPaul Cercueil 6022159a681SPaul Cercueil /* Disable stereo mic */ 6032159a681SPaul Cercueil regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_MIC, 6042159a681SPaul Cercueil BIT(REG_CR_MIC_STEREO_OFFSET), 0); 6052159a681SPaul Cercueil 6062159a681SPaul Cercueil /* Set mic 1 as default source for ADC */ 6072159a681SPaul Cercueil regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_ADC, 6082159a681SPaul Cercueil REG_CR_ADC_IN_SEL_MASK, 0); 6092159a681SPaul Cercueil 6102159a681SPaul Cercueil /* ADC/DAC: serial + i2s */ 6112159a681SPaul Cercueil regmap_update_bits(regmap, JZ4770_CODEC_REG_AICR_ADC, 6122159a681SPaul Cercueil REG_AICR_ADC_SERIAL | REG_AICR_ADC_I2S, 6132159a681SPaul Cercueil REG_AICR_ADC_SERIAL | REG_AICR_ADC_I2S); 6142159a681SPaul Cercueil regmap_update_bits(regmap, JZ4770_CODEC_REG_AICR_DAC, 6152159a681SPaul Cercueil REG_AICR_DAC_SERIAL | REG_AICR_DAC_I2S, 6162159a681SPaul Cercueil REG_AICR_DAC_SERIAL | REG_AICR_DAC_I2S); 6172159a681SPaul Cercueil 6182159a681SPaul Cercueil /* The generated IRQ is a high level */ 6192159a681SPaul Cercueil regmap_update_bits(regmap, JZ4770_CODEC_REG_ICR, 6202159a681SPaul Cercueil REG_ICR_INT_FORM_MASK, 0); 6212159a681SPaul Cercueil regmap_update_bits(regmap, JZ4770_CODEC_REG_IMR, REG_IMR_ALL_MASK, 6222159a681SPaul Cercueil REG_IMR_JACK_MASK | REG_IMR_RUP_MASK | 6232159a681SPaul Cercueil REG_IMR_RDO_MASK | REG_IMR_GUP_MASK | 6242159a681SPaul Cercueil REG_IMR_GDO_MASK); 6252159a681SPaul Cercueil 6262159a681SPaul Cercueil /* 12M oscillator */ 6272159a681SPaul Cercueil regmap_update_bits(regmap, JZ4770_CODEC_REG_CCR, 6282159a681SPaul Cercueil REG_CCR_CRYSTAL_MASK, 0); 6292159a681SPaul Cercueil 6302159a681SPaul Cercueil /* 0: 16ohm/220uF, 1: 10kohm/1uF */ 6312159a681SPaul Cercueil regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_HP, 6322159a681SPaul Cercueil REG_CR_HP_LOAD, 0); 6332159a681SPaul Cercueil 6342159a681SPaul Cercueil /* disable automatic gain */ 6352159a681SPaul Cercueil regmap_update_bits(regmap, JZ4770_CODEC_REG_AGC1, REG_AGC1_EN, 0); 6362159a681SPaul Cercueil 6372159a681SPaul Cercueil /* Disable DAC lrswap */ 6382159a681SPaul Cercueil regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_DAC, 6392159a681SPaul Cercueil REG_CR_DAC_LRSWAP, REG_CR_DAC_LRSWAP); 6402159a681SPaul Cercueil 6412159a681SPaul Cercueil /* Independent L/R DAC gain control */ 6422159a681SPaul Cercueil regmap_update_bits(regmap, JZ4770_CODEC_REG_GCR_DACL, 6432159a681SPaul Cercueil REG_GCR_DACL_RLGOD, 0); 6442159a681SPaul Cercueil 6452159a681SPaul Cercueil /* Disable ADC lrswap */ 6462159a681SPaul Cercueil regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_ADC, 6472159a681SPaul Cercueil REG_CR_ADC_LRSWAP, REG_CR_ADC_LRSWAP); 6482159a681SPaul Cercueil 6492159a681SPaul Cercueil /* default to cap-less mode(0) */ 6502159a681SPaul Cercueil regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_HP, 6512159a681SPaul Cercueil REG_CR_HP_SB_HPCM, 0); 6522159a681SPaul Cercueil 6532159a681SPaul Cercueil /* Send collected updates. */ 6542159a681SPaul Cercueil regcache_cache_only(regmap, false); 6552159a681SPaul Cercueil regcache_sync(regmap); 6562159a681SPaul Cercueil 6572159a681SPaul Cercueil /* Reset all interrupt flags. */ 6582159a681SPaul Cercueil regmap_write(regmap, JZ4770_CODEC_REG_IFR, REG_IFR_ALL_MASK); 6592159a681SPaul Cercueil } 6602159a681SPaul Cercueil 6612159a681SPaul Cercueil static int jz4770_codec_codec_probe(struct snd_soc_component *codec) 6622159a681SPaul Cercueil { 6632159a681SPaul Cercueil struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec); 6642159a681SPaul Cercueil 6652159a681SPaul Cercueil clk_prepare_enable(jz_codec->clk); 6662159a681SPaul Cercueil 6672159a681SPaul Cercueil jz4770_codec_codec_init_regs(codec); 6682159a681SPaul Cercueil 6692159a681SPaul Cercueil return 0; 6702159a681SPaul Cercueil } 6712159a681SPaul Cercueil 6722159a681SPaul Cercueil static void jz4770_codec_codec_remove(struct snd_soc_component *codec) 6732159a681SPaul Cercueil { 6742159a681SPaul Cercueil struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec); 6752159a681SPaul Cercueil 6762159a681SPaul Cercueil clk_disable_unprepare(jz_codec->clk); 6772159a681SPaul Cercueil } 6782159a681SPaul Cercueil 6792159a681SPaul Cercueil static const struct snd_soc_component_driver jz4770_codec_soc_codec_dev = { 6802159a681SPaul Cercueil .probe = jz4770_codec_codec_probe, 6812159a681SPaul Cercueil .remove = jz4770_codec_codec_remove, 6822159a681SPaul Cercueil .set_bias_level = jz4770_codec_set_bias_level, 6832159a681SPaul Cercueil .controls = jz4770_codec_snd_controls, 6842159a681SPaul Cercueil .num_controls = ARRAY_SIZE(jz4770_codec_snd_controls), 6852159a681SPaul Cercueil .dapm_widgets = jz4770_codec_dapm_widgets, 6862159a681SPaul Cercueil .num_dapm_widgets = ARRAY_SIZE(jz4770_codec_dapm_widgets), 6872159a681SPaul Cercueil .dapm_routes = jz4770_codec_dapm_routes, 6882159a681SPaul Cercueil .num_dapm_routes = ARRAY_SIZE(jz4770_codec_dapm_routes), 6892159a681SPaul Cercueil .suspend_bias_off = 1, 6902159a681SPaul Cercueil .use_pmdown_time = 1, 6912159a681SPaul Cercueil }; 6922159a681SPaul Cercueil 6932159a681SPaul Cercueil static const unsigned int jz4770_codec_sample_rates[] = { 6942159a681SPaul Cercueil 96000, 48000, 44100, 32000, 6952159a681SPaul Cercueil 24000, 22050, 16000, 12000, 6962159a681SPaul Cercueil 11025, 9600, 8000, 6972159a681SPaul Cercueil }; 6982159a681SPaul Cercueil 6992159a681SPaul Cercueil static int jz4770_codec_hw_params(struct snd_pcm_substream *substream, 7002159a681SPaul Cercueil struct snd_pcm_hw_params *params, 7012159a681SPaul Cercueil struct snd_soc_dai *dai) 7022159a681SPaul Cercueil { 7032159a681SPaul Cercueil struct jz_codec *codec = snd_soc_component_get_drvdata(dai->component); 7042159a681SPaul Cercueil unsigned int rate, bit_width; 7052159a681SPaul Cercueil 7062159a681SPaul Cercueil switch (params_format(params)) { 7072159a681SPaul Cercueil case SNDRV_PCM_FORMAT_S16_LE: 7082159a681SPaul Cercueil bit_width = 0; 7092159a681SPaul Cercueil break; 7102159a681SPaul Cercueil case SNDRV_PCM_FORMAT_S18_3LE: 7112159a681SPaul Cercueil bit_width = 1; 7122159a681SPaul Cercueil break; 7132159a681SPaul Cercueil case SNDRV_PCM_FORMAT_S20_3LE: 7142159a681SPaul Cercueil bit_width = 2; 7152159a681SPaul Cercueil break; 7162159a681SPaul Cercueil case SNDRV_PCM_FORMAT_S24_3LE: 7172159a681SPaul Cercueil bit_width = 3; 7182159a681SPaul Cercueil break; 7192159a681SPaul Cercueil default: 7202159a681SPaul Cercueil return -EINVAL; 7212159a681SPaul Cercueil } 7222159a681SPaul Cercueil 7232159a681SPaul Cercueil for (rate = 0; rate < ARRAY_SIZE(jz4770_codec_sample_rates); rate++) { 7242159a681SPaul Cercueil if (jz4770_codec_sample_rates[rate] == params_rate(params)) 7252159a681SPaul Cercueil break; 7262159a681SPaul Cercueil } 7272159a681SPaul Cercueil 7282159a681SPaul Cercueil if (rate == ARRAY_SIZE(jz4770_codec_sample_rates)) 7292159a681SPaul Cercueil return -EINVAL; 7302159a681SPaul Cercueil 7312159a681SPaul Cercueil if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 7322159a681SPaul Cercueil regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_AICR_DAC, 7332159a681SPaul Cercueil REG_AICR_DAC_ADWL_MASK, 7342159a681SPaul Cercueil bit_width << REG_AICR_DAC_ADWL_OFFSET); 7352159a681SPaul Cercueil regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_FCR_DAC, 7362159a681SPaul Cercueil REG_FCR_DAC_FREQ_MASK, 7372159a681SPaul Cercueil rate << REG_FCR_DAC_FREQ_OFFSET); 7382159a681SPaul Cercueil } else { 7392159a681SPaul Cercueil regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_AICR_ADC, 7402159a681SPaul Cercueil REG_AICR_ADC_ADWL_MASK, 7412159a681SPaul Cercueil bit_width << REG_AICR_ADC_ADWL_OFFSET); 7422159a681SPaul Cercueil regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_FCR_ADC, 7432159a681SPaul Cercueil REG_FCR_ADC_FREQ_MASK, 7442159a681SPaul Cercueil rate << REG_FCR_ADC_FREQ_OFFSET); 7452159a681SPaul Cercueil } 7462159a681SPaul Cercueil 7472159a681SPaul Cercueil return 0; 7482159a681SPaul Cercueil } 7492159a681SPaul Cercueil 7502159a681SPaul Cercueil static const struct snd_soc_dai_ops jz4770_codec_dai_ops = { 7512159a681SPaul Cercueil .startup = jz4770_codec_startup, 7522159a681SPaul Cercueil .shutdown = jz4770_codec_shutdown, 7532159a681SPaul Cercueil .hw_params = jz4770_codec_hw_params, 7542159a681SPaul Cercueil .trigger = jz4770_codec_pcm_trigger, 75554b59270SKuninori Morimoto .mute_stream = jz4770_codec_mute_stream, 75654b59270SKuninori Morimoto .no_capture_mute = 1, 7572159a681SPaul Cercueil }; 7582159a681SPaul Cercueil 7592159a681SPaul Cercueil #define JZ_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ 7602159a681SPaul Cercueil SNDRV_PCM_FMTBIT_S18_3LE | \ 7612159a681SPaul Cercueil SNDRV_PCM_FMTBIT_S20_3LE | \ 7622159a681SPaul Cercueil SNDRV_PCM_FMTBIT_S24_3LE) 7632159a681SPaul Cercueil 7642159a681SPaul Cercueil static struct snd_soc_dai_driver jz4770_codec_dai = { 7652159a681SPaul Cercueil .name = "jz4770-hifi", 7662159a681SPaul Cercueil .playback = { 7672159a681SPaul Cercueil .stream_name = "Playback", 7682159a681SPaul Cercueil .channels_min = 2, 7692159a681SPaul Cercueil .channels_max = 2, 7702159a681SPaul Cercueil .rates = SNDRV_PCM_RATE_8000_96000, 7712159a681SPaul Cercueil .formats = JZ_CODEC_FORMATS, 7722159a681SPaul Cercueil }, 7732159a681SPaul Cercueil .capture = { 7742159a681SPaul Cercueil .stream_name = "Capture", 7752159a681SPaul Cercueil .channels_min = 2, 7762159a681SPaul Cercueil .channels_max = 2, 7772159a681SPaul Cercueil .rates = SNDRV_PCM_RATE_8000_96000, 7782159a681SPaul Cercueil .formats = JZ_CODEC_FORMATS, 7792159a681SPaul Cercueil }, 7802159a681SPaul Cercueil .ops = &jz4770_codec_dai_ops, 7812159a681SPaul Cercueil }; 7822159a681SPaul Cercueil 7832159a681SPaul Cercueil static bool jz4770_codec_volatile(struct device *dev, unsigned int reg) 7842159a681SPaul Cercueil { 7852159a681SPaul Cercueil return reg == JZ4770_CODEC_REG_SR || reg == JZ4770_CODEC_REG_IFR; 7862159a681SPaul Cercueil } 7872159a681SPaul Cercueil 7882159a681SPaul Cercueil static bool jz4770_codec_readable(struct device *dev, unsigned int reg) 7892159a681SPaul Cercueil { 7902159a681SPaul Cercueil switch (reg) { 7912159a681SPaul Cercueil case JZ4770_CODEC_REG_MISSING_REG1: 7922159a681SPaul Cercueil case JZ4770_CODEC_REG_MISSING_REG2: 7932159a681SPaul Cercueil return false; 7942159a681SPaul Cercueil default: 7952159a681SPaul Cercueil return true; 7962159a681SPaul Cercueil } 7972159a681SPaul Cercueil } 7982159a681SPaul Cercueil 7992159a681SPaul Cercueil static bool jz4770_codec_writeable(struct device *dev, unsigned int reg) 8002159a681SPaul Cercueil { 8012159a681SPaul Cercueil switch (reg) { 8022159a681SPaul Cercueil case JZ4770_CODEC_REG_SR: 8032159a681SPaul Cercueil case JZ4770_CODEC_REG_MISSING_REG1: 8042159a681SPaul Cercueil case JZ4770_CODEC_REG_MISSING_REG2: 8052159a681SPaul Cercueil return false; 8062159a681SPaul Cercueil default: 8072159a681SPaul Cercueil return true; 8082159a681SPaul Cercueil } 8092159a681SPaul Cercueil } 8102159a681SPaul Cercueil 8112159a681SPaul Cercueil static int jz4770_codec_io_wait(struct jz_codec *codec) 8122159a681SPaul Cercueil { 8132159a681SPaul Cercueil u32 reg; 8142159a681SPaul Cercueil 8152159a681SPaul Cercueil return readl_poll_timeout(codec->base + ICDC_RGADW_OFFSET, reg, 8162159a681SPaul Cercueil !(reg & ICDC_RGADW_RGWR), 8172159a681SPaul Cercueil 1000, 10 * USEC_PER_MSEC); 8182159a681SPaul Cercueil } 8192159a681SPaul Cercueil 8202159a681SPaul Cercueil static int jz4770_codec_reg_read(void *context, unsigned int reg, 8212159a681SPaul Cercueil unsigned int *val) 8222159a681SPaul Cercueil { 8232159a681SPaul Cercueil struct jz_codec *codec = context; 8242159a681SPaul Cercueil unsigned int i; 8252159a681SPaul Cercueil u32 tmp; 8262159a681SPaul Cercueil int ret; 8272159a681SPaul Cercueil 8282159a681SPaul Cercueil ret = jz4770_codec_io_wait(codec); 8292159a681SPaul Cercueil if (ret) 8302159a681SPaul Cercueil return ret; 8312159a681SPaul Cercueil 8322159a681SPaul Cercueil tmp = readl(codec->base + ICDC_RGADW_OFFSET); 8332159a681SPaul Cercueil tmp = (tmp & ~ICDC_RGADW_RGADDR_MASK) 8342159a681SPaul Cercueil | (reg << ICDC_RGADW_RGADDR_OFFSET); 8352159a681SPaul Cercueil writel(tmp, codec->base + ICDC_RGADW_OFFSET); 8362159a681SPaul Cercueil 8372159a681SPaul Cercueil /* wait 6+ cycles */ 8382159a681SPaul Cercueil for (i = 0; i < 6; i++) 8392159a681SPaul Cercueil *val = readl(codec->base + ICDC_RGDATA_OFFSET) & 8402159a681SPaul Cercueil ICDC_RGDATA_RGDOUT_MASK; 8412159a681SPaul Cercueil 8422159a681SPaul Cercueil return 0; 8432159a681SPaul Cercueil } 8442159a681SPaul Cercueil 8452159a681SPaul Cercueil static int jz4770_codec_reg_write(void *context, unsigned int reg, 8462159a681SPaul Cercueil unsigned int val) 8472159a681SPaul Cercueil { 8482159a681SPaul Cercueil struct jz_codec *codec = context; 8492159a681SPaul Cercueil int ret; 8502159a681SPaul Cercueil 8512159a681SPaul Cercueil ret = jz4770_codec_io_wait(codec); 8522159a681SPaul Cercueil if (ret) 8532159a681SPaul Cercueil return ret; 8542159a681SPaul Cercueil 8552159a681SPaul Cercueil writel(ICDC_RGADW_RGWR | (reg << ICDC_RGADW_RGADDR_OFFSET) | val, 8562159a681SPaul Cercueil codec->base + ICDC_RGADW_OFFSET); 8572159a681SPaul Cercueil 8582159a681SPaul Cercueil ret = jz4770_codec_io_wait(codec); 8592159a681SPaul Cercueil if (ret) 8602159a681SPaul Cercueil return ret; 8612159a681SPaul Cercueil 8622159a681SPaul Cercueil return 0; 8632159a681SPaul Cercueil } 8642159a681SPaul Cercueil 8652159a681SPaul Cercueil static const u8 jz4770_codec_reg_defaults[] = { 8662159a681SPaul Cercueil 0x00, 0xC3, 0xC3, 0x90, 0x98, 0xFF, 0x90, 0xB1, 8672159a681SPaul Cercueil 0x11, 0x10, 0x00, 0x03, 0x00, 0x00, 0x40, 0x00, 8682159a681SPaul Cercueil 0xFF, 0x00, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 8692159a681SPaul Cercueil 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x34, 8702159a681SPaul Cercueil 0x07, 0x44, 0x1F, 0x00 8712159a681SPaul Cercueil }; 8722159a681SPaul Cercueil 8732159a681SPaul Cercueil static struct regmap_config jz4770_codec_regmap_config = { 8742159a681SPaul Cercueil .reg_bits = 7, 8752159a681SPaul Cercueil .val_bits = 8, 8762159a681SPaul Cercueil 8772159a681SPaul Cercueil .max_register = JZ4770_CODEC_REG_AGC5, 8782159a681SPaul Cercueil .volatile_reg = jz4770_codec_volatile, 8792159a681SPaul Cercueil .readable_reg = jz4770_codec_readable, 8802159a681SPaul Cercueil .writeable_reg = jz4770_codec_writeable, 8812159a681SPaul Cercueil 8822159a681SPaul Cercueil .reg_read = jz4770_codec_reg_read, 8832159a681SPaul Cercueil .reg_write = jz4770_codec_reg_write, 8842159a681SPaul Cercueil 8852159a681SPaul Cercueil .reg_defaults_raw = jz4770_codec_reg_defaults, 8862159a681SPaul Cercueil .num_reg_defaults_raw = ARRAY_SIZE(jz4770_codec_reg_defaults), 8872159a681SPaul Cercueil .cache_type = REGCACHE_FLAT, 8882159a681SPaul Cercueil }; 8892159a681SPaul Cercueil 8902159a681SPaul Cercueil static int jz4770_codec_probe(struct platform_device *pdev) 8912159a681SPaul Cercueil { 8922159a681SPaul Cercueil struct device *dev = &pdev->dev; 8932159a681SPaul Cercueil struct jz_codec *codec; 8942159a681SPaul Cercueil int ret; 8952159a681SPaul Cercueil 8962159a681SPaul Cercueil codec = devm_kzalloc(dev, sizeof(*codec), GFP_KERNEL); 8972159a681SPaul Cercueil if (!codec) 8982159a681SPaul Cercueil return -ENOMEM; 8992159a681SPaul Cercueil 9002159a681SPaul Cercueil codec->dev = dev; 9012159a681SPaul Cercueil 9022159a681SPaul Cercueil codec->base = devm_platform_ioremap_resource(pdev, 0); 9032159a681SPaul Cercueil if (IS_ERR(codec->base)) { 9042159a681SPaul Cercueil ret = PTR_ERR(codec->base); 9052159a681SPaul Cercueil dev_err(dev, "Failed to ioremap mmio memory: %d\n", ret); 9062159a681SPaul Cercueil return ret; 9072159a681SPaul Cercueil } 9082159a681SPaul Cercueil 9092159a681SPaul Cercueil codec->regmap = devm_regmap_init(dev, NULL, codec, 9102159a681SPaul Cercueil &jz4770_codec_regmap_config); 9112159a681SPaul Cercueil if (IS_ERR(codec->regmap)) 9122159a681SPaul Cercueil return PTR_ERR(codec->regmap); 9132159a681SPaul Cercueil 9142159a681SPaul Cercueil codec->clk = devm_clk_get(dev, "aic"); 9152159a681SPaul Cercueil if (IS_ERR(codec->clk)) 9162159a681SPaul Cercueil return PTR_ERR(codec->clk); 9172159a681SPaul Cercueil 9182159a681SPaul Cercueil platform_set_drvdata(pdev, codec); 9192159a681SPaul Cercueil 9202159a681SPaul Cercueil ret = devm_snd_soc_register_component(dev, &jz4770_codec_soc_codec_dev, 9212159a681SPaul Cercueil &jz4770_codec_dai, 1); 9222159a681SPaul Cercueil if (ret) { 9232159a681SPaul Cercueil dev_err(dev, "Failed to register codec: %d\n", ret); 9242159a681SPaul Cercueil return ret; 9252159a681SPaul Cercueil } 9262159a681SPaul Cercueil 9272159a681SPaul Cercueil return 0; 9282159a681SPaul Cercueil } 9292159a681SPaul Cercueil 9302159a681SPaul Cercueil static const struct of_device_id jz4770_codec_of_matches[] = { 9312159a681SPaul Cercueil { .compatible = "ingenic,jz4770-codec", }, 9322159a681SPaul Cercueil { /* sentinel */ } 9332159a681SPaul Cercueil }; 9342159a681SPaul Cercueil MODULE_DEVICE_TABLE(of, jz4770_codec_of_matches); 9352159a681SPaul Cercueil 9362159a681SPaul Cercueil static struct platform_driver jz4770_codec_driver = { 9372159a681SPaul Cercueil .probe = jz4770_codec_probe, 9382159a681SPaul Cercueil .driver = { 9392159a681SPaul Cercueil .name = "jz4770-codec", 940e6825baeSPaul Cercueil .of_match_table = jz4770_codec_of_matches, 9412159a681SPaul Cercueil }, 9422159a681SPaul Cercueil }; 9432159a681SPaul Cercueil module_platform_driver(jz4770_codec_driver); 9442159a681SPaul Cercueil 9452159a681SPaul Cercueil MODULE_DESCRIPTION("JZ4770 SoC internal codec driver"); 9462159a681SPaul Cercueil MODULE_AUTHOR("Maarten ter Huurne <maarten@treewalker.org>"); 9472159a681SPaul Cercueil MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>"); 9482159a681SPaul Cercueil MODULE_LICENSE("GPL v2"); 949