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
101e648e3f1SPaul Cercueil #define REG_CR_HP_SB_HPCM_OFFSET 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
jz4770_codec_set_bias_level(struct snd_soc_component * codec,enum snd_soc_bias_level level)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:
193a346c778SChristophe Branchereau /* Reset all interrupt flags. */
194a346c778SChristophe Branchereau regmap_write(regmap, JZ4770_CODEC_REG_IFR, REG_IFR_ALL_MASK);
195a346c778SChristophe Branchereau
196ad13c835SPaul Cercueil regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_VIC,
197ad13c835SPaul Cercueil REG_CR_VIC_SB);
1982159a681SPaul Cercueil msleep(250);
199ad13c835SPaul Cercueil regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_VIC,
200ad13c835SPaul Cercueil REG_CR_VIC_SB_SLEEP);
2012159a681SPaul Cercueil msleep(400);
2022159a681SPaul Cercueil break;
2032159a681SPaul Cercueil case SND_SOC_BIAS_STANDBY:
204ad13c835SPaul Cercueil regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_VIC,
205ad13c835SPaul Cercueil REG_CR_VIC_SB_SLEEP);
206ad13c835SPaul Cercueil regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_VIC,
207ad13c835SPaul Cercueil REG_CR_VIC_SB);
208df561f66SGustavo A. R. Silva fallthrough;
2092159a681SPaul Cercueil default:
2102159a681SPaul Cercueil break;
2112159a681SPaul Cercueil }
2122159a681SPaul Cercueil
2132159a681SPaul Cercueil return 0;
2142159a681SPaul Cercueil }
2152159a681SPaul Cercueil
jz4770_codec_startup(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)2162159a681SPaul Cercueil static int jz4770_codec_startup(struct snd_pcm_substream *substream,
2172159a681SPaul Cercueil struct snd_soc_dai *dai)
2182159a681SPaul Cercueil {
2192159a681SPaul Cercueil struct snd_soc_component *codec = dai->component;
2202159a681SPaul Cercueil struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(codec);
2212159a681SPaul Cercueil
2222159a681SPaul Cercueil /*
2232159a681SPaul Cercueil * SYSCLK output from the codec to the AIC is required to keep the
2242159a681SPaul Cercueil * DMA transfer going during playback when all audible outputs have
2252159a681SPaul Cercueil * been disabled.
2262159a681SPaul Cercueil */
2272159a681SPaul Cercueil if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
2282159a681SPaul Cercueil snd_soc_dapm_force_enable_pin(dapm, "SYSCLK");
2292159a681SPaul Cercueil
2302159a681SPaul Cercueil return 0;
2312159a681SPaul Cercueil }
2322159a681SPaul Cercueil
jz4770_codec_shutdown(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)2332159a681SPaul Cercueil static void jz4770_codec_shutdown(struct snd_pcm_substream *substream,
2342159a681SPaul Cercueil struct snd_soc_dai *dai)
2352159a681SPaul Cercueil {
2362159a681SPaul Cercueil struct snd_soc_component *codec = dai->component;
2372159a681SPaul Cercueil struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(codec);
2382159a681SPaul Cercueil
2392159a681SPaul Cercueil if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
2402159a681SPaul Cercueil snd_soc_dapm_disable_pin(dapm, "SYSCLK");
2412159a681SPaul Cercueil }
2422159a681SPaul Cercueil
2432159a681SPaul Cercueil
jz4770_codec_pcm_trigger(struct snd_pcm_substream * substream,int cmd,struct snd_soc_dai * dai)2442159a681SPaul Cercueil static int jz4770_codec_pcm_trigger(struct snd_pcm_substream *substream,
2452159a681SPaul Cercueil int cmd, struct snd_soc_dai *dai)
2462159a681SPaul Cercueil {
2472159a681SPaul Cercueil struct snd_soc_component *codec = dai->component;
2482159a681SPaul Cercueil int ret = 0;
2492159a681SPaul Cercueil
2502159a681SPaul Cercueil switch (cmd) {
2512159a681SPaul Cercueil case SNDRV_PCM_TRIGGER_START:
2522159a681SPaul Cercueil case SNDRV_PCM_TRIGGER_RESUME:
2532159a681SPaul Cercueil case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
2542159a681SPaul Cercueil if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
2552159a681SPaul Cercueil snd_soc_component_force_bias_level(codec,
2562159a681SPaul Cercueil SND_SOC_BIAS_ON);
2572159a681SPaul Cercueil break;
2582159a681SPaul Cercueil case SNDRV_PCM_TRIGGER_STOP:
2592159a681SPaul Cercueil case SNDRV_PCM_TRIGGER_SUSPEND:
2602159a681SPaul Cercueil case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
2612159a681SPaul Cercueil /* do nothing */
2622159a681SPaul Cercueil break;
2632159a681SPaul Cercueil default:
2642159a681SPaul Cercueil ret = -EINVAL;
2652159a681SPaul Cercueil }
2662159a681SPaul Cercueil
2672159a681SPaul Cercueil return ret;
2682159a681SPaul Cercueil }
2692159a681SPaul Cercueil
jz4770_codec_mute_stream(struct snd_soc_dai * dai,int mute,int direction)27054b59270SKuninori Morimoto static int jz4770_codec_mute_stream(struct snd_soc_dai *dai, int mute, int direction)
2712159a681SPaul Cercueil {
2722159a681SPaul Cercueil struct snd_soc_component *codec = dai->component;
2732159a681SPaul Cercueil struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
2742159a681SPaul Cercueil unsigned int gain_bit = mute ? REG_IFR_GDO : REG_IFR_GUP;
2752159a681SPaul Cercueil unsigned int val;
2762159a681SPaul Cercueil int change, err;
2772159a681SPaul Cercueil
2782159a681SPaul Cercueil change = snd_soc_component_update_bits(codec, JZ4770_CODEC_REG_CR_DAC,
2792159a681SPaul Cercueil REG_CR_DAC_MUTE,
2802159a681SPaul Cercueil mute ? REG_CR_DAC_MUTE : 0);
2812159a681SPaul Cercueil if (change == 1) {
2822159a681SPaul Cercueil regmap_read(jz_codec->regmap, JZ4770_CODEC_REG_CR_DAC, &val);
2832159a681SPaul Cercueil
2842159a681SPaul Cercueil if (val & BIT(REG_CR_DAC_SB_OFFSET))
2852159a681SPaul Cercueil return 1;
2862159a681SPaul Cercueil
2872159a681SPaul Cercueil err = regmap_read_poll_timeout(jz_codec->regmap,
2882159a681SPaul Cercueil JZ4770_CODEC_REG_IFR,
2892159a681SPaul Cercueil val, val & gain_bit,
2906b4da537SChristophe Branchereau 1000, 1 * USEC_PER_SEC);
2912159a681SPaul Cercueil if (err) {
2922159a681SPaul Cercueil dev_err(jz_codec->dev,
2932159a681SPaul Cercueil "Timeout while setting digital mute: %d", err);
2942159a681SPaul Cercueil return err;
2952159a681SPaul Cercueil }
2962159a681SPaul Cercueil
2972159a681SPaul Cercueil /* clear GUP/GDO flag */
298ad13c835SPaul Cercueil regmap_set_bits(jz_codec->regmap, JZ4770_CODEC_REG_IFR,
299ad13c835SPaul Cercueil gain_bit);
3002159a681SPaul Cercueil }
3012159a681SPaul Cercueil
3022159a681SPaul Cercueil return 0;
3032159a681SPaul Cercueil }
3042159a681SPaul Cercueil
3052159a681SPaul Cercueil /* unit: 0.01dB */
3062159a681SPaul Cercueil static const DECLARE_TLV_DB_MINMAX_MUTE(dac_tlv, -3100, 0);
3072159a681SPaul Cercueil static const DECLARE_TLV_DB_SCALE(adc_tlv, 0, 100, 0);
3082159a681SPaul Cercueil static const DECLARE_TLV_DB_MINMAX(out_tlv, -2500, 600);
3092159a681SPaul Cercueil static const DECLARE_TLV_DB_SCALE(linein_tlv, -2500, 100, 0);
310*0b189395SPaul Cercueil static const DECLARE_TLV_DB_MINMAX(mixer_tlv, -3100, 0);
3112159a681SPaul Cercueil
3122159a681SPaul Cercueil /* Unconditional controls. */
3132159a681SPaul Cercueil static const struct snd_kcontrol_new jz4770_codec_snd_controls[] = {
3142159a681SPaul Cercueil /* record gain control */
3152159a681SPaul Cercueil SOC_DOUBLE_R_TLV("PCM Capture Volume",
3162159a681SPaul Cercueil JZ4770_CODEC_REG_GCR_ADCL, JZ4770_CODEC_REG_GCR_ADCR,
3172159a681SPaul Cercueil REG_GCR_ADC_GAIN_OFFSET, REG_GCR_ADC_GAIN_MAX,
3182159a681SPaul Cercueil 0, adc_tlv),
3192159a681SPaul Cercueil
3202159a681SPaul Cercueil SOC_DOUBLE_R_TLV("Line In Bypass Playback Volume",
3212159a681SPaul Cercueil JZ4770_CODEC_REG_GCR_LIBYL, JZ4770_CODEC_REG_GCR_LIBYR,
3222159a681SPaul Cercueil REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, linein_tlv),
323*0b189395SPaul Cercueil
324*0b189395SPaul Cercueil SOC_SINGLE_TLV("Mixer Capture Volume",
325*0b189395SPaul Cercueil JZ4770_CODEC_REG_GCR_MIXADC,
326*0b189395SPaul Cercueil REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, mixer_tlv),
327*0b189395SPaul Cercueil
328*0b189395SPaul Cercueil SOC_SINGLE_TLV("Mixer Playback Volume",
329*0b189395SPaul Cercueil JZ4770_CODEC_REG_GCR_MIXDAC,
330*0b189395SPaul Cercueil REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, mixer_tlv),
3312159a681SPaul Cercueil };
3322159a681SPaul Cercueil
3332159a681SPaul Cercueil static const struct snd_kcontrol_new jz4770_codec_pcm_playback_controls[] = {
3342159a681SPaul Cercueil {
3352159a681SPaul Cercueil .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3362159a681SPaul Cercueil .name = "Volume",
3372159a681SPaul Cercueil .info = snd_soc_info_volsw,
3382159a681SPaul Cercueil .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ
3392159a681SPaul Cercueil | SNDRV_CTL_ELEM_ACCESS_READWRITE,
3402159a681SPaul Cercueil .tlv.p = dac_tlv,
3412159a681SPaul Cercueil .get = snd_soc_dapm_get_volsw,
3422159a681SPaul Cercueil .put = snd_soc_dapm_put_volsw,
3432159a681SPaul Cercueil /*
3442159a681SPaul Cercueil * NOTE: DACR/DACL are inversed; the gain value written to DACR
3452159a681SPaul Cercueil * seems to affect the left channel, and the gain value written
3462159a681SPaul Cercueil * to DACL seems to affect the right channel.
3472159a681SPaul Cercueil */
3482159a681SPaul Cercueil .private_value = SOC_DOUBLE_R_VALUE(JZ4770_CODEC_REG_GCR_DACR,
3492159a681SPaul Cercueil JZ4770_CODEC_REG_GCR_DACL,
3502159a681SPaul Cercueil REG_GCR_GAIN_OFFSET,
3512159a681SPaul Cercueil REG_GCR_GAIN_MAX, 1),
3522159a681SPaul Cercueil },
3532159a681SPaul Cercueil };
3542159a681SPaul Cercueil
3552159a681SPaul Cercueil static const struct snd_kcontrol_new jz4770_codec_hp_playback_controls[] = {
3562159a681SPaul Cercueil {
3572159a681SPaul Cercueil .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3582159a681SPaul Cercueil .name = "Volume",
3592159a681SPaul Cercueil .info = snd_soc_info_volsw,
3602159a681SPaul Cercueil .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ
3612159a681SPaul Cercueil | SNDRV_CTL_ELEM_ACCESS_READWRITE,
3622159a681SPaul Cercueil .tlv.p = out_tlv,
3632159a681SPaul Cercueil .get = snd_soc_dapm_get_volsw,
3642159a681SPaul Cercueil .put = snd_soc_dapm_put_volsw,
3652159a681SPaul Cercueil /* HPR/HPL inversed for the same reason as above */
3662159a681SPaul Cercueil .private_value = SOC_DOUBLE_R_VALUE(JZ4770_CODEC_REG_GCR_HPR,
3672159a681SPaul Cercueil JZ4770_CODEC_REG_GCR_HPL,
3682159a681SPaul Cercueil REG_GCR_GAIN_OFFSET,
3692159a681SPaul Cercueil REG_GCR_GAIN_MAX, 1),
3702159a681SPaul Cercueil },
3712159a681SPaul Cercueil };
3722159a681SPaul Cercueil
hpout_event(struct snd_soc_dapm_widget * w,struct snd_kcontrol * kcontrol,int event)3732159a681SPaul Cercueil static int hpout_event(struct snd_soc_dapm_widget *w,
3742159a681SPaul Cercueil struct snd_kcontrol *kcontrol, int event)
3752159a681SPaul Cercueil {
3762159a681SPaul Cercueil struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm);
3772159a681SPaul Cercueil struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
3782159a681SPaul Cercueil unsigned int val;
3792159a681SPaul Cercueil int err;
3802159a681SPaul Cercueil
3812159a681SPaul Cercueil switch (event) {
3822159a681SPaul Cercueil case SND_SOC_DAPM_PRE_PMU:
3834f293dfeSPaul Cercueil /* unmute HP */
384ad13c835SPaul Cercueil regmap_clear_bits(jz_codec->regmap, JZ4770_CODEC_REG_CR_HP,
3854f293dfeSPaul Cercueil REG_CR_HP_MUTE);
3862159a681SPaul Cercueil break;
3872159a681SPaul Cercueil
3882159a681SPaul Cercueil case SND_SOC_DAPM_POST_PMU:
3892159a681SPaul Cercueil /* wait for ramp-up complete (RUP) */
3902159a681SPaul Cercueil err = regmap_read_poll_timeout(jz_codec->regmap,
3912159a681SPaul Cercueil JZ4770_CODEC_REG_IFR,
3922159a681SPaul Cercueil val, val & REG_IFR_RUP,
3936b4da537SChristophe Branchereau 1000, 1 * USEC_PER_SEC);
3942159a681SPaul Cercueil if (err) {
3952159a681SPaul Cercueil dev_err(jz_codec->dev, "RUP timeout: %d", err);
3962159a681SPaul Cercueil return err;
3972159a681SPaul Cercueil }
3982159a681SPaul Cercueil
3992159a681SPaul Cercueil /* clear RUP flag */
400ad13c835SPaul Cercueil regmap_set_bits(jz_codec->regmap, JZ4770_CODEC_REG_IFR,
401ad13c835SPaul Cercueil REG_IFR_RUP);
4022159a681SPaul Cercueil
4032159a681SPaul Cercueil break;
4042159a681SPaul Cercueil
4052159a681SPaul Cercueil case SND_SOC_DAPM_POST_PMD:
4064f293dfeSPaul Cercueil /* mute HP */
407ad13c835SPaul Cercueil regmap_set_bits(jz_codec->regmap, JZ4770_CODEC_REG_CR_HP,
4084f293dfeSPaul Cercueil REG_CR_HP_MUTE);
4092159a681SPaul Cercueil
4102159a681SPaul Cercueil err = regmap_read_poll_timeout(jz_codec->regmap,
4112159a681SPaul Cercueil JZ4770_CODEC_REG_IFR,
4122159a681SPaul Cercueil val, val & REG_IFR_RDO,
4136b4da537SChristophe Branchereau 1000, 1 * USEC_PER_SEC);
4142159a681SPaul Cercueil if (err) {
4152159a681SPaul Cercueil dev_err(jz_codec->dev, "RDO timeout: %d", err);
4162159a681SPaul Cercueil return err;
4172159a681SPaul Cercueil }
4182159a681SPaul Cercueil
4192159a681SPaul Cercueil /* clear RDO flag */
420ad13c835SPaul Cercueil regmap_set_bits(jz_codec->regmap, JZ4770_CODEC_REG_IFR,
421ad13c835SPaul Cercueil REG_IFR_RDO);
4222159a681SPaul Cercueil
4232159a681SPaul Cercueil break;
4242159a681SPaul Cercueil }
4252159a681SPaul Cercueil
4262159a681SPaul Cercueil return 0;
4272159a681SPaul Cercueil }
4282159a681SPaul Cercueil
adc_poweron_event(struct snd_soc_dapm_widget * w,struct snd_kcontrol * kcontrol,int event)4292159a681SPaul Cercueil static int adc_poweron_event(struct snd_soc_dapm_widget *w,
4302159a681SPaul Cercueil struct snd_kcontrol *kcontrol, int event)
4312159a681SPaul Cercueil {
4322159a681SPaul Cercueil if (event == SND_SOC_DAPM_POST_PMU)
4332159a681SPaul Cercueil msleep(1000);
4342159a681SPaul Cercueil
4352159a681SPaul Cercueil return 0;
4362159a681SPaul Cercueil }
4372159a681SPaul Cercueil
4382159a681SPaul Cercueil static const char * const jz4770_codec_hp_texts[] = {
4392159a681SPaul Cercueil "PCM", "Line In", "Mic 1", "Mic 2"
4402159a681SPaul Cercueil };
4412159a681SPaul Cercueil static const unsigned int jz4770_codec_hp_values[] = { 3, 2, 0, 1 };
4422159a681SPaul Cercueil static SOC_VALUE_ENUM_SINGLE_DECL(jz4770_codec_hp_enum,
4432159a681SPaul Cercueil JZ4770_CODEC_REG_CR_HP,
4442159a681SPaul Cercueil REG_CR_HP_SEL_OFFSET,
4452159a681SPaul Cercueil REG_CR_HP_SEL_MASK,
4462159a681SPaul Cercueil jz4770_codec_hp_texts,
4472159a681SPaul Cercueil jz4770_codec_hp_values);
4482159a681SPaul Cercueil static const struct snd_kcontrol_new jz4770_codec_hp_source =
4492159a681SPaul Cercueil SOC_DAPM_ENUM("Route", jz4770_codec_hp_enum);
4502159a681SPaul Cercueil
4512159a681SPaul Cercueil static SOC_VALUE_ENUM_SINGLE_DECL(jz4770_codec_lo_enum,
4522159a681SPaul Cercueil JZ4770_CODEC_REG_CR_LO,
4532159a681SPaul Cercueil REG_CR_LO_SEL_OFFSET,
4542159a681SPaul Cercueil REG_CR_LO_SEL_MASK,
4552159a681SPaul Cercueil jz4770_codec_hp_texts,
4562159a681SPaul Cercueil jz4770_codec_hp_values);
4572159a681SPaul Cercueil static const struct snd_kcontrol_new jz4770_codec_lo_source =
4582159a681SPaul Cercueil SOC_DAPM_ENUM("Route", jz4770_codec_lo_enum);
4592159a681SPaul Cercueil
4602159a681SPaul Cercueil static const char * const jz4770_codec_cap_texts[] = {
4612159a681SPaul Cercueil "Line In", "Mic 1", "Mic 2"
4622159a681SPaul Cercueil };
4632159a681SPaul Cercueil static const unsigned int jz4770_codec_cap_values[] = { 2, 0, 1 };
4642159a681SPaul Cercueil static SOC_VALUE_ENUM_SINGLE_DECL(jz4770_codec_cap_enum,
4652159a681SPaul Cercueil JZ4770_CODEC_REG_CR_ADC,
4662159a681SPaul Cercueil REG_CR_ADC_IN_SEL_OFFSET,
4672159a681SPaul Cercueil REG_CR_ADC_IN_SEL_MASK,
4682159a681SPaul Cercueil jz4770_codec_cap_texts,
4692159a681SPaul Cercueil jz4770_codec_cap_values);
4702159a681SPaul Cercueil static const struct snd_kcontrol_new jz4770_codec_cap_source =
4712159a681SPaul Cercueil SOC_DAPM_ENUM("Route", jz4770_codec_cap_enum);
4722159a681SPaul Cercueil
4732159a681SPaul Cercueil static const struct snd_kcontrol_new jz4770_codec_mic_controls[] = {
4742159a681SPaul Cercueil SOC_DAPM_SINGLE("Stereo Capture Switch", JZ4770_CODEC_REG_CR_MIC,
4752159a681SPaul Cercueil REG_CR_MIC_STEREO_OFFSET, 1, 0),
4762159a681SPaul Cercueil };
4772159a681SPaul Cercueil
4782159a681SPaul Cercueil static const struct snd_soc_dapm_widget jz4770_codec_dapm_widgets[] = {
4792159a681SPaul Cercueil SND_SOC_DAPM_PGA_E("HP Out", JZ4770_CODEC_REG_CR_HP,
4802159a681SPaul Cercueil REG_CR_HP_SB_OFFSET, 1, NULL, 0, hpout_event,
4812159a681SPaul Cercueil SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
4822159a681SPaul Cercueil SND_SOC_DAPM_POST_PMD),
4832159a681SPaul Cercueil
4842159a681SPaul Cercueil SND_SOC_DAPM_PGA("Line Out", JZ4770_CODEC_REG_CR_LO,
4852159a681SPaul Cercueil REG_CR_LO_SB_OFFSET, 1, NULL, 0),
4862159a681SPaul Cercueil
4872159a681SPaul Cercueil SND_SOC_DAPM_PGA("Line Out Switch 2", JZ4770_CODEC_REG_CR_LO,
4882159a681SPaul Cercueil REG_CR_LO_MUTE_OFFSET, 1, NULL, 0),
4892159a681SPaul Cercueil
4902159a681SPaul Cercueil SND_SOC_DAPM_PGA("Line In", JZ4770_CODEC_REG_CR_LI,
4912159a681SPaul Cercueil REG_CR_LI_SB_OFFSET, 1, NULL, 0),
4922159a681SPaul Cercueil
4932159a681SPaul Cercueil SND_SOC_DAPM_MUX("Headphones Source", SND_SOC_NOPM, 0, 0,
4942159a681SPaul Cercueil &jz4770_codec_hp_source),
4952159a681SPaul Cercueil SND_SOC_DAPM_MUX("Capture Source", SND_SOC_NOPM, 0, 0,
4962159a681SPaul Cercueil &jz4770_codec_cap_source),
4972159a681SPaul Cercueil SND_SOC_DAPM_MUX("Line Out Source", SND_SOC_NOPM, 0, 0,
4982159a681SPaul Cercueil &jz4770_codec_lo_source),
4992159a681SPaul Cercueil
5002159a681SPaul Cercueil SND_SOC_DAPM_PGA("Mic 1", JZ4770_CODEC_REG_CR_MIC,
5012159a681SPaul Cercueil REG_CR_MIC_SB_MIC1_OFFSET, 1, NULL, 0),
5022159a681SPaul Cercueil SND_SOC_DAPM_PGA("Mic 2", JZ4770_CODEC_REG_CR_MIC,
5032159a681SPaul Cercueil REG_CR_MIC_SB_MIC2_OFFSET, 1, NULL, 0),
5042159a681SPaul Cercueil
5052159a681SPaul Cercueil SND_SOC_DAPM_PGA("Mic Diff", JZ4770_CODEC_REG_CR_MIC,
5062159a681SPaul Cercueil REG_CR_MIC_IDIFF_OFFSET, 0, NULL, 0),
5072159a681SPaul Cercueil
5082159a681SPaul Cercueil SND_SOC_DAPM_MIXER("Mic", SND_SOC_NOPM, 0, 0,
5092159a681SPaul Cercueil jz4770_codec_mic_controls,
5102159a681SPaul Cercueil ARRAY_SIZE(jz4770_codec_mic_controls)),
5112159a681SPaul Cercueil
5122159a681SPaul Cercueil SND_SOC_DAPM_PGA("Line In Bypass", JZ4770_CODEC_REG_CR_LI,
5132159a681SPaul Cercueil REG_CR_LI_LIBY_OFFSET, 1, NULL, 0),
5142159a681SPaul Cercueil
5152159a681SPaul Cercueil SND_SOC_DAPM_ADC_E("ADC", "HiFi Capture", JZ4770_CODEC_REG_CR_ADC,
5162159a681SPaul Cercueil REG_CR_ADC_SB_OFFSET, 1, adc_poweron_event,
5172159a681SPaul Cercueil SND_SOC_DAPM_POST_PMU),
5182159a681SPaul Cercueil SND_SOC_DAPM_DAC("DAC", "HiFi Playback", JZ4770_CODEC_REG_CR_DAC,
5192159a681SPaul Cercueil REG_CR_DAC_SB_OFFSET, 1),
5202159a681SPaul Cercueil
5212159a681SPaul Cercueil SND_SOC_DAPM_MIXER("PCM Playback", SND_SOC_NOPM, 0, 0,
5222159a681SPaul Cercueil jz4770_codec_pcm_playback_controls,
5232159a681SPaul Cercueil ARRAY_SIZE(jz4770_codec_pcm_playback_controls)),
5242159a681SPaul Cercueil SND_SOC_DAPM_MIXER("Headphones Playback", SND_SOC_NOPM, 0, 0,
5252159a681SPaul Cercueil jz4770_codec_hp_playback_controls,
5262159a681SPaul Cercueil ARRAY_SIZE(jz4770_codec_hp_playback_controls)),
5272159a681SPaul Cercueil
5282159a681SPaul Cercueil SND_SOC_DAPM_SUPPLY("MICBIAS", JZ4770_CODEC_REG_CR_MIC,
5292159a681SPaul Cercueil REG_CR_MIC_BIAS_SB_OFFSET, 1, NULL, 0),
5302159a681SPaul Cercueil
531e648e3f1SPaul Cercueil SND_SOC_DAPM_SUPPLY("Cap-less", JZ4770_CODEC_REG_CR_HP,
532e648e3f1SPaul Cercueil REG_CR_HP_SB_HPCM_OFFSET, 1, NULL, 0),
533e648e3f1SPaul Cercueil
5342159a681SPaul Cercueil SND_SOC_DAPM_INPUT("MIC1P"),
5352159a681SPaul Cercueil SND_SOC_DAPM_INPUT("MIC1N"),
5362159a681SPaul Cercueil SND_SOC_DAPM_INPUT("MIC2P"),
5372159a681SPaul Cercueil SND_SOC_DAPM_INPUT("MIC2N"),
5382159a681SPaul Cercueil
5392159a681SPaul Cercueil SND_SOC_DAPM_OUTPUT("LOUT"),
5402159a681SPaul Cercueil SND_SOC_DAPM_OUTPUT("ROUT"),
5412159a681SPaul Cercueil
5422159a681SPaul Cercueil SND_SOC_DAPM_OUTPUT("LHPOUT"),
5432159a681SPaul Cercueil SND_SOC_DAPM_OUTPUT("RHPOUT"),
5442159a681SPaul Cercueil
5452159a681SPaul Cercueil SND_SOC_DAPM_INPUT("LLINEIN"),
5462159a681SPaul Cercueil SND_SOC_DAPM_INPUT("RLINEIN"),
5472159a681SPaul Cercueil
5482159a681SPaul Cercueil SND_SOC_DAPM_OUTPUT("SYSCLK"),
5492159a681SPaul Cercueil };
5502159a681SPaul Cercueil
5512159a681SPaul Cercueil /* Unconditional routes. */
5522159a681SPaul Cercueil static const struct snd_soc_dapm_route jz4770_codec_dapm_routes[] = {
5532159a681SPaul Cercueil { "Mic 1", NULL, "MIC1P" },
5542159a681SPaul Cercueil { "Mic Diff", NULL, "MIC1N" },
5552159a681SPaul Cercueil { "Mic 1", NULL, "Mic Diff" },
5562159a681SPaul Cercueil { "Mic 2", NULL, "MIC2P" },
5572159a681SPaul Cercueil { "Mic Diff", NULL, "MIC2N" },
5582159a681SPaul Cercueil { "Mic 2", NULL, "Mic Diff" },
5592159a681SPaul Cercueil
5602159a681SPaul Cercueil { "Line In", NULL, "LLINEIN" },
5612159a681SPaul Cercueil { "Line In", NULL, "RLINEIN" },
5622159a681SPaul Cercueil
5632159a681SPaul Cercueil { "Mic", "Stereo Capture Switch", "Mic 1" },
5642159a681SPaul Cercueil { "Mic", "Stereo Capture Switch", "Mic 2" },
5652159a681SPaul Cercueil { "Headphones Source", "Mic 1", "Mic" },
5662159a681SPaul Cercueil { "Headphones Source", "Mic 2", "Mic" },
5672159a681SPaul Cercueil { "Capture Source", "Mic 1", "Mic" },
5682159a681SPaul Cercueil { "Capture Source", "Mic 2", "Mic" },
5692159a681SPaul Cercueil
5702159a681SPaul Cercueil { "Headphones Source", "Mic 1", "Mic 1" },
5712159a681SPaul Cercueil { "Headphones Source", "Mic 2", "Mic 2" },
5722159a681SPaul Cercueil { "Headphones Source", "Line In", "Line In Bypass" },
5732159a681SPaul Cercueil { "Headphones Source", "PCM", "Headphones Playback" },
5742159a681SPaul Cercueil { "HP Out", NULL, "Headphones Source" },
5752159a681SPaul Cercueil
5762159a681SPaul Cercueil { "Capture Source", "Line In", "Line In" },
5772159a681SPaul Cercueil { "Capture Source", "Mic 1", "Mic 1" },
5782159a681SPaul Cercueil { "Capture Source", "Mic 2", "Mic 2" },
5792159a681SPaul Cercueil { "ADC", NULL, "Capture Source" },
5802159a681SPaul Cercueil
5812159a681SPaul Cercueil { "Line In Bypass", NULL, "Line In" },
5822159a681SPaul Cercueil { "Line Out Source", "Line In", "Line In Bypass" },
5832159a681SPaul Cercueil { "Line Out Source", "PCM", "PCM Playback" },
5842159a681SPaul Cercueil
5852159a681SPaul Cercueil { "LHPOUT", NULL, "HP Out"},
5862159a681SPaul Cercueil { "RHPOUT", NULL, "HP Out"},
5872159a681SPaul Cercueil
5882159a681SPaul Cercueil { "Line Out", NULL, "Line Out Source" },
5892159a681SPaul Cercueil { "Line Out Switch 2", NULL, "Line Out" },
5902159a681SPaul Cercueil
5912159a681SPaul Cercueil { "LOUT", NULL, "Line Out Switch 2"},
5922159a681SPaul Cercueil { "ROUT", NULL, "Line Out Switch 2"},
5932159a681SPaul Cercueil
5942159a681SPaul Cercueil { "PCM Playback", "Volume", "DAC" },
5952159a681SPaul Cercueil { "Headphones Playback", "Volume", "PCM Playback" },
5962159a681SPaul Cercueil
5972159a681SPaul Cercueil { "SYSCLK", NULL, "DAC" },
5982159a681SPaul Cercueil };
5992159a681SPaul Cercueil
jz4770_codec_codec_init_regs(struct snd_soc_component * codec)6002159a681SPaul Cercueil static void jz4770_codec_codec_init_regs(struct snd_soc_component *codec)
6012159a681SPaul Cercueil {
6022159a681SPaul Cercueil struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
6032159a681SPaul Cercueil struct regmap *regmap = jz_codec->regmap;
6042159a681SPaul Cercueil
6052159a681SPaul Cercueil /* Collect updates for later sending. */
6062159a681SPaul Cercueil regcache_cache_only(regmap, true);
6072159a681SPaul Cercueil
6082159a681SPaul Cercueil /* default HP output to PCM */
609ad13c835SPaul Cercueil regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_HP, REG_CR_HP_SEL_MASK);
6102159a681SPaul Cercueil
6112159a681SPaul Cercueil /* default line output to PCM */
612ad13c835SPaul Cercueil regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_LO, REG_CR_LO_SEL_MASK);
6132159a681SPaul Cercueil
6142159a681SPaul Cercueil /* Disable stereo mic */
615ad13c835SPaul Cercueil regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_MIC,
616ad13c835SPaul Cercueil BIT(REG_CR_MIC_STEREO_OFFSET));
6172159a681SPaul Cercueil
6182159a681SPaul Cercueil /* Set mic 1 as default source for ADC */
619ad13c835SPaul Cercueil regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_ADC,
620ad13c835SPaul Cercueil REG_CR_ADC_IN_SEL_MASK);
6212159a681SPaul Cercueil
6222159a681SPaul Cercueil /* ADC/DAC: serial + i2s */
623ad13c835SPaul Cercueil regmap_set_bits(regmap, JZ4770_CODEC_REG_AICR_ADC,
6242159a681SPaul Cercueil REG_AICR_ADC_SERIAL | REG_AICR_ADC_I2S);
625ad13c835SPaul Cercueil regmap_set_bits(regmap, JZ4770_CODEC_REG_AICR_DAC,
6262159a681SPaul Cercueil REG_AICR_DAC_SERIAL | REG_AICR_DAC_I2S);
6272159a681SPaul Cercueil
6282159a681SPaul Cercueil /* The generated IRQ is a high level */
629ad13c835SPaul Cercueil regmap_clear_bits(regmap, JZ4770_CODEC_REG_ICR, REG_ICR_INT_FORM_MASK);
6302159a681SPaul Cercueil regmap_update_bits(regmap, JZ4770_CODEC_REG_IMR, REG_IMR_ALL_MASK,
6312159a681SPaul Cercueil REG_IMR_JACK_MASK | REG_IMR_RUP_MASK |
6322159a681SPaul Cercueil REG_IMR_RDO_MASK | REG_IMR_GUP_MASK |
6332159a681SPaul Cercueil REG_IMR_GDO_MASK);
6342159a681SPaul Cercueil
6352159a681SPaul Cercueil /* 12M oscillator */
636ad13c835SPaul Cercueil regmap_clear_bits(regmap, JZ4770_CODEC_REG_CCR, REG_CCR_CRYSTAL_MASK);
6372159a681SPaul Cercueil
6382159a681SPaul Cercueil /* 0: 16ohm/220uF, 1: 10kohm/1uF */
639ad13c835SPaul Cercueil regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_HP, REG_CR_HP_LOAD);
6402159a681SPaul Cercueil
6412159a681SPaul Cercueil /* disable automatic gain */
642ad13c835SPaul Cercueil regmap_clear_bits(regmap, JZ4770_CODEC_REG_AGC1, REG_AGC1_EN);
6432159a681SPaul Cercueil
6442159a681SPaul Cercueil /* Disable DAC lrswap */
645ad13c835SPaul Cercueil regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_DAC, REG_CR_DAC_LRSWAP);
6462159a681SPaul Cercueil
6472159a681SPaul Cercueil /* Independent L/R DAC gain control */
648ad13c835SPaul Cercueil regmap_clear_bits(regmap, JZ4770_CODEC_REG_GCR_DACL,
649ad13c835SPaul Cercueil REG_GCR_DACL_RLGOD);
6502159a681SPaul Cercueil
6512159a681SPaul Cercueil /* Disable ADC lrswap */
652ad13c835SPaul Cercueil regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_ADC, REG_CR_ADC_LRSWAP);
6532159a681SPaul Cercueil
6542159a681SPaul Cercueil /* default to cap-less mode(0) */
655e648e3f1SPaul Cercueil regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_HP,
656e648e3f1SPaul Cercueil BIT(REG_CR_HP_SB_HPCM_OFFSET));
6572159a681SPaul Cercueil
6582159a681SPaul Cercueil /* Send collected updates. */
6592159a681SPaul Cercueil regcache_cache_only(regmap, false);
6602159a681SPaul Cercueil regcache_sync(regmap);
6612159a681SPaul Cercueil }
6622159a681SPaul Cercueil
jz4770_codec_codec_probe(struct snd_soc_component * codec)6632159a681SPaul Cercueil static int jz4770_codec_codec_probe(struct snd_soc_component *codec)
6642159a681SPaul Cercueil {
6652159a681SPaul Cercueil struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
6662159a681SPaul Cercueil
6672159a681SPaul Cercueil clk_prepare_enable(jz_codec->clk);
6682159a681SPaul Cercueil
6692159a681SPaul Cercueil jz4770_codec_codec_init_regs(codec);
6702159a681SPaul Cercueil
6712159a681SPaul Cercueil return 0;
6722159a681SPaul Cercueil }
6732159a681SPaul Cercueil
jz4770_codec_codec_remove(struct snd_soc_component * codec)6742159a681SPaul Cercueil static void jz4770_codec_codec_remove(struct snd_soc_component *codec)
6752159a681SPaul Cercueil {
6762159a681SPaul Cercueil struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
6772159a681SPaul Cercueil
6782159a681SPaul Cercueil clk_disable_unprepare(jz_codec->clk);
6792159a681SPaul Cercueil }
6802159a681SPaul Cercueil
6812159a681SPaul Cercueil static const struct snd_soc_component_driver jz4770_codec_soc_codec_dev = {
6822159a681SPaul Cercueil .probe = jz4770_codec_codec_probe,
6832159a681SPaul Cercueil .remove = jz4770_codec_codec_remove,
6842159a681SPaul Cercueil .set_bias_level = jz4770_codec_set_bias_level,
6852159a681SPaul Cercueil .controls = jz4770_codec_snd_controls,
6862159a681SPaul Cercueil .num_controls = ARRAY_SIZE(jz4770_codec_snd_controls),
6872159a681SPaul Cercueil .dapm_widgets = jz4770_codec_dapm_widgets,
6882159a681SPaul Cercueil .num_dapm_widgets = ARRAY_SIZE(jz4770_codec_dapm_widgets),
6892159a681SPaul Cercueil .dapm_routes = jz4770_codec_dapm_routes,
6902159a681SPaul Cercueil .num_dapm_routes = ARRAY_SIZE(jz4770_codec_dapm_routes),
6912159a681SPaul Cercueil .suspend_bias_off = 1,
6922159a681SPaul Cercueil .use_pmdown_time = 1,
6932159a681SPaul Cercueil };
6942159a681SPaul Cercueil
6952159a681SPaul Cercueil static const unsigned int jz4770_codec_sample_rates[] = {
6962159a681SPaul Cercueil 96000, 48000, 44100, 32000,
6972159a681SPaul Cercueil 24000, 22050, 16000, 12000,
6982159a681SPaul Cercueil 11025, 9600, 8000,
6992159a681SPaul Cercueil };
7002159a681SPaul Cercueil
jz4770_codec_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)7012159a681SPaul Cercueil static int jz4770_codec_hw_params(struct snd_pcm_substream *substream,
7022159a681SPaul Cercueil struct snd_pcm_hw_params *params,
7032159a681SPaul Cercueil struct snd_soc_dai *dai)
7042159a681SPaul Cercueil {
7052159a681SPaul Cercueil struct jz_codec *codec = snd_soc_component_get_drvdata(dai->component);
7062159a681SPaul Cercueil unsigned int rate, bit_width;
7072159a681SPaul Cercueil
7082159a681SPaul Cercueil switch (params_format(params)) {
7092159a681SPaul Cercueil case SNDRV_PCM_FORMAT_S16_LE:
7102159a681SPaul Cercueil bit_width = 0;
7112159a681SPaul Cercueil break;
7122159a681SPaul Cercueil case SNDRV_PCM_FORMAT_S18_3LE:
7132159a681SPaul Cercueil bit_width = 1;
7142159a681SPaul Cercueil break;
7152159a681SPaul Cercueil case SNDRV_PCM_FORMAT_S20_3LE:
7162159a681SPaul Cercueil bit_width = 2;
7172159a681SPaul Cercueil break;
7182159a681SPaul Cercueil case SNDRV_PCM_FORMAT_S24_3LE:
7192159a681SPaul Cercueil bit_width = 3;
7202159a681SPaul Cercueil break;
7212159a681SPaul Cercueil default:
7222159a681SPaul Cercueil return -EINVAL;
7232159a681SPaul Cercueil }
7242159a681SPaul Cercueil
7252159a681SPaul Cercueil for (rate = 0; rate < ARRAY_SIZE(jz4770_codec_sample_rates); rate++) {
7262159a681SPaul Cercueil if (jz4770_codec_sample_rates[rate] == params_rate(params))
7272159a681SPaul Cercueil break;
7282159a681SPaul Cercueil }
7292159a681SPaul Cercueil
7302159a681SPaul Cercueil if (rate == ARRAY_SIZE(jz4770_codec_sample_rates))
7312159a681SPaul Cercueil return -EINVAL;
7322159a681SPaul Cercueil
7332159a681SPaul Cercueil if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
7342159a681SPaul Cercueil regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_AICR_DAC,
7352159a681SPaul Cercueil REG_AICR_DAC_ADWL_MASK,
7362159a681SPaul Cercueil bit_width << REG_AICR_DAC_ADWL_OFFSET);
7372159a681SPaul Cercueil regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_FCR_DAC,
7382159a681SPaul Cercueil REG_FCR_DAC_FREQ_MASK,
7392159a681SPaul Cercueil rate << REG_FCR_DAC_FREQ_OFFSET);
7402159a681SPaul Cercueil } else {
7412159a681SPaul Cercueil regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_AICR_ADC,
7422159a681SPaul Cercueil REG_AICR_ADC_ADWL_MASK,
7432159a681SPaul Cercueil bit_width << REG_AICR_ADC_ADWL_OFFSET);
7442159a681SPaul Cercueil regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_FCR_ADC,
7452159a681SPaul Cercueil REG_FCR_ADC_FREQ_MASK,
7462159a681SPaul Cercueil rate << REG_FCR_ADC_FREQ_OFFSET);
7472159a681SPaul Cercueil }
7482159a681SPaul Cercueil
7492159a681SPaul Cercueil return 0;
7502159a681SPaul Cercueil }
7512159a681SPaul Cercueil
7522159a681SPaul Cercueil static const struct snd_soc_dai_ops jz4770_codec_dai_ops = {
7532159a681SPaul Cercueil .startup = jz4770_codec_startup,
7542159a681SPaul Cercueil .shutdown = jz4770_codec_shutdown,
7552159a681SPaul Cercueil .hw_params = jz4770_codec_hw_params,
7562159a681SPaul Cercueil .trigger = jz4770_codec_pcm_trigger,
75754b59270SKuninori Morimoto .mute_stream = jz4770_codec_mute_stream,
75854b59270SKuninori Morimoto .no_capture_mute = 1,
7592159a681SPaul Cercueil };
7602159a681SPaul Cercueil
7612159a681SPaul Cercueil #define JZ_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
7622159a681SPaul Cercueil SNDRV_PCM_FMTBIT_S18_3LE | \
7632159a681SPaul Cercueil SNDRV_PCM_FMTBIT_S20_3LE | \
7642159a681SPaul Cercueil SNDRV_PCM_FMTBIT_S24_3LE)
7652159a681SPaul Cercueil
7662159a681SPaul Cercueil static struct snd_soc_dai_driver jz4770_codec_dai = {
7672159a681SPaul Cercueil .name = "jz4770-hifi",
7682159a681SPaul Cercueil .playback = {
7692159a681SPaul Cercueil .stream_name = "Playback",
7702159a681SPaul Cercueil .channels_min = 2,
7712159a681SPaul Cercueil .channels_max = 2,
7722159a681SPaul Cercueil .rates = SNDRV_PCM_RATE_8000_96000,
7732159a681SPaul Cercueil .formats = JZ_CODEC_FORMATS,
7742159a681SPaul Cercueil },
7752159a681SPaul Cercueil .capture = {
7762159a681SPaul Cercueil .stream_name = "Capture",
7772159a681SPaul Cercueil .channels_min = 2,
7782159a681SPaul Cercueil .channels_max = 2,
7792159a681SPaul Cercueil .rates = SNDRV_PCM_RATE_8000_96000,
7802159a681SPaul Cercueil .formats = JZ_CODEC_FORMATS,
7812159a681SPaul Cercueil },
7822159a681SPaul Cercueil .ops = &jz4770_codec_dai_ops,
7832159a681SPaul Cercueil };
7842159a681SPaul Cercueil
jz4770_codec_volatile(struct device * dev,unsigned int reg)7852159a681SPaul Cercueil static bool jz4770_codec_volatile(struct device *dev, unsigned int reg)
7862159a681SPaul Cercueil {
7872159a681SPaul Cercueil return reg == JZ4770_CODEC_REG_SR || reg == JZ4770_CODEC_REG_IFR;
7882159a681SPaul Cercueil }
7892159a681SPaul Cercueil
jz4770_codec_readable(struct device * dev,unsigned int reg)7902159a681SPaul Cercueil static bool jz4770_codec_readable(struct device *dev, unsigned int reg)
7912159a681SPaul Cercueil {
7922159a681SPaul Cercueil switch (reg) {
7932159a681SPaul Cercueil case JZ4770_CODEC_REG_MISSING_REG1:
7942159a681SPaul Cercueil case JZ4770_CODEC_REG_MISSING_REG2:
7952159a681SPaul Cercueil return false;
7962159a681SPaul Cercueil default:
7972159a681SPaul Cercueil return true;
7982159a681SPaul Cercueil }
7992159a681SPaul Cercueil }
8002159a681SPaul Cercueil
jz4770_codec_writeable(struct device * dev,unsigned int reg)8012159a681SPaul Cercueil static bool jz4770_codec_writeable(struct device *dev, unsigned int reg)
8022159a681SPaul Cercueil {
8032159a681SPaul Cercueil switch (reg) {
8042159a681SPaul Cercueil case JZ4770_CODEC_REG_SR:
8052159a681SPaul Cercueil case JZ4770_CODEC_REG_MISSING_REG1:
8062159a681SPaul Cercueil case JZ4770_CODEC_REG_MISSING_REG2:
8072159a681SPaul Cercueil return false;
8082159a681SPaul Cercueil default:
8092159a681SPaul Cercueil return true;
8102159a681SPaul Cercueil }
8112159a681SPaul Cercueil }
8122159a681SPaul Cercueil
jz4770_codec_io_wait(struct jz_codec * codec)8132159a681SPaul Cercueil static int jz4770_codec_io_wait(struct jz_codec *codec)
8142159a681SPaul Cercueil {
8152159a681SPaul Cercueil u32 reg;
8162159a681SPaul Cercueil
8172159a681SPaul Cercueil return readl_poll_timeout(codec->base + ICDC_RGADW_OFFSET, reg,
8182159a681SPaul Cercueil !(reg & ICDC_RGADW_RGWR),
8196b4da537SChristophe Branchereau 1000, 1 * USEC_PER_SEC);
8202159a681SPaul Cercueil }
8212159a681SPaul Cercueil
jz4770_codec_reg_read(void * context,unsigned int reg,unsigned int * val)8222159a681SPaul Cercueil static int jz4770_codec_reg_read(void *context, unsigned int reg,
8232159a681SPaul Cercueil unsigned int *val)
8242159a681SPaul Cercueil {
8252159a681SPaul Cercueil struct jz_codec *codec = context;
8262159a681SPaul Cercueil unsigned int i;
8272159a681SPaul Cercueil u32 tmp;
8282159a681SPaul Cercueil int ret;
8292159a681SPaul Cercueil
8302159a681SPaul Cercueil ret = jz4770_codec_io_wait(codec);
8312159a681SPaul Cercueil if (ret)
8322159a681SPaul Cercueil return ret;
8332159a681SPaul Cercueil
8342159a681SPaul Cercueil tmp = readl(codec->base + ICDC_RGADW_OFFSET);
8352159a681SPaul Cercueil tmp = (tmp & ~ICDC_RGADW_RGADDR_MASK)
8362159a681SPaul Cercueil | (reg << ICDC_RGADW_RGADDR_OFFSET);
8372159a681SPaul Cercueil writel(tmp, codec->base + ICDC_RGADW_OFFSET);
8382159a681SPaul Cercueil
8392159a681SPaul Cercueil /* wait 6+ cycles */
8402159a681SPaul Cercueil for (i = 0; i < 6; i++)
8412159a681SPaul Cercueil *val = readl(codec->base + ICDC_RGDATA_OFFSET) &
8422159a681SPaul Cercueil ICDC_RGDATA_RGDOUT_MASK;
8432159a681SPaul Cercueil
8442159a681SPaul Cercueil return 0;
8452159a681SPaul Cercueil }
8462159a681SPaul Cercueil
jz4770_codec_reg_write(void * context,unsigned int reg,unsigned int val)8472159a681SPaul Cercueil static int jz4770_codec_reg_write(void *context, unsigned int reg,
8482159a681SPaul Cercueil unsigned int val)
8492159a681SPaul Cercueil {
8502159a681SPaul Cercueil struct jz_codec *codec = context;
8512159a681SPaul Cercueil int ret;
8522159a681SPaul Cercueil
8532159a681SPaul Cercueil ret = jz4770_codec_io_wait(codec);
8542159a681SPaul Cercueil if (ret)
8552159a681SPaul Cercueil return ret;
8562159a681SPaul Cercueil
8572159a681SPaul Cercueil writel(ICDC_RGADW_RGWR | (reg << ICDC_RGADW_RGADDR_OFFSET) | val,
8582159a681SPaul Cercueil codec->base + ICDC_RGADW_OFFSET);
8592159a681SPaul Cercueil
8602159a681SPaul Cercueil ret = jz4770_codec_io_wait(codec);
8612159a681SPaul Cercueil if (ret)
8622159a681SPaul Cercueil return ret;
8632159a681SPaul Cercueil
8642159a681SPaul Cercueil return 0;
8652159a681SPaul Cercueil }
8662159a681SPaul Cercueil
8672159a681SPaul Cercueil static const u8 jz4770_codec_reg_defaults[] = {
8682159a681SPaul Cercueil 0x00, 0xC3, 0xC3, 0x90, 0x98, 0xFF, 0x90, 0xB1,
8692159a681SPaul Cercueil 0x11, 0x10, 0x00, 0x03, 0x00, 0x00, 0x40, 0x00,
8702159a681SPaul Cercueil 0xFF, 0x00, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00,
8712159a681SPaul Cercueil 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x34,
8722159a681SPaul Cercueil 0x07, 0x44, 0x1F, 0x00
8732159a681SPaul Cercueil };
8742159a681SPaul Cercueil
8752159a681SPaul Cercueil static struct regmap_config jz4770_codec_regmap_config = {
8762159a681SPaul Cercueil .reg_bits = 7,
8772159a681SPaul Cercueil .val_bits = 8,
8782159a681SPaul Cercueil
8792159a681SPaul Cercueil .max_register = JZ4770_CODEC_REG_AGC5,
8802159a681SPaul Cercueil .volatile_reg = jz4770_codec_volatile,
8812159a681SPaul Cercueil .readable_reg = jz4770_codec_readable,
8822159a681SPaul Cercueil .writeable_reg = jz4770_codec_writeable,
8832159a681SPaul Cercueil
8842159a681SPaul Cercueil .reg_read = jz4770_codec_reg_read,
8852159a681SPaul Cercueil .reg_write = jz4770_codec_reg_write,
8862159a681SPaul Cercueil
8872159a681SPaul Cercueil .reg_defaults_raw = jz4770_codec_reg_defaults,
8882159a681SPaul Cercueil .num_reg_defaults_raw = ARRAY_SIZE(jz4770_codec_reg_defaults),
8892159a681SPaul Cercueil .cache_type = REGCACHE_FLAT,
8902159a681SPaul Cercueil };
8912159a681SPaul Cercueil
jz4770_codec_probe(struct platform_device * pdev)8922159a681SPaul Cercueil static int jz4770_codec_probe(struct platform_device *pdev)
8932159a681SPaul Cercueil {
8942159a681SPaul Cercueil struct device *dev = &pdev->dev;
8952159a681SPaul Cercueil struct jz_codec *codec;
8962159a681SPaul Cercueil int ret;
8972159a681SPaul Cercueil
8982159a681SPaul Cercueil codec = devm_kzalloc(dev, sizeof(*codec), GFP_KERNEL);
8992159a681SPaul Cercueil if (!codec)
9002159a681SPaul Cercueil return -ENOMEM;
9012159a681SPaul Cercueil
9022159a681SPaul Cercueil codec->dev = dev;
9032159a681SPaul Cercueil
9042159a681SPaul Cercueil codec->base = devm_platform_ioremap_resource(pdev, 0);
905cc2d7429STang Bin if (IS_ERR(codec->base))
906cc2d7429STang Bin return PTR_ERR(codec->base);
9072159a681SPaul Cercueil
9082159a681SPaul Cercueil codec->regmap = devm_regmap_init(dev, NULL, codec,
9092159a681SPaul Cercueil &jz4770_codec_regmap_config);
9102159a681SPaul Cercueil if (IS_ERR(codec->regmap))
9112159a681SPaul Cercueil return PTR_ERR(codec->regmap);
9122159a681SPaul Cercueil
9132159a681SPaul Cercueil codec->clk = devm_clk_get(dev, "aic");
9142159a681SPaul Cercueil if (IS_ERR(codec->clk))
9152159a681SPaul Cercueil return PTR_ERR(codec->clk);
9162159a681SPaul Cercueil
9172159a681SPaul Cercueil platform_set_drvdata(pdev, codec);
9182159a681SPaul Cercueil
9192159a681SPaul Cercueil ret = devm_snd_soc_register_component(dev, &jz4770_codec_soc_codec_dev,
9202159a681SPaul Cercueil &jz4770_codec_dai, 1);
9212159a681SPaul Cercueil if (ret) {
9222159a681SPaul Cercueil dev_err(dev, "Failed to register codec: %d\n", ret);
9232159a681SPaul Cercueil return ret;
9242159a681SPaul Cercueil }
9252159a681SPaul Cercueil
9262159a681SPaul Cercueil return 0;
9272159a681SPaul Cercueil }
9282159a681SPaul Cercueil
9292159a681SPaul Cercueil static const struct of_device_id jz4770_codec_of_matches[] = {
9302159a681SPaul Cercueil { .compatible = "ingenic,jz4770-codec", },
9312159a681SPaul Cercueil { /* sentinel */ }
9322159a681SPaul Cercueil };
9332159a681SPaul Cercueil MODULE_DEVICE_TABLE(of, jz4770_codec_of_matches);
9342159a681SPaul Cercueil
9352159a681SPaul Cercueil static struct platform_driver jz4770_codec_driver = {
9362159a681SPaul Cercueil .probe = jz4770_codec_probe,
9372159a681SPaul Cercueil .driver = {
9382159a681SPaul Cercueil .name = "jz4770-codec",
939e6825baeSPaul Cercueil .of_match_table = jz4770_codec_of_matches,
9402159a681SPaul Cercueil },
9412159a681SPaul Cercueil };
9422159a681SPaul Cercueil module_platform_driver(jz4770_codec_driver);
9432159a681SPaul Cercueil
9442159a681SPaul Cercueil MODULE_DESCRIPTION("JZ4770 SoC internal codec driver");
9452159a681SPaul Cercueil MODULE_AUTHOR("Maarten ter Huurne <maarten@treewalker.org>");
9462159a681SPaul Cercueil MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
9472159a681SPaul Cercueil MODULE_LICENSE("GPL v2");
948