xref: /openbmc/linux/sound/soc/codecs/jz4770.c (revision 6b4da5374b10a48be18df26288125746f1858507)
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:
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 
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 
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 
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 
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,
290*6b4da537SChristophe 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);
3102159a681SPaul Cercueil 
3112159a681SPaul Cercueil /* Unconditional controls. */
3122159a681SPaul Cercueil static const struct snd_kcontrol_new jz4770_codec_snd_controls[] = {
3132159a681SPaul Cercueil 	/* record gain control */
3142159a681SPaul Cercueil 	SOC_DOUBLE_R_TLV("PCM Capture Volume",
3152159a681SPaul Cercueil 			 JZ4770_CODEC_REG_GCR_ADCL, JZ4770_CODEC_REG_GCR_ADCR,
3162159a681SPaul Cercueil 			 REG_GCR_ADC_GAIN_OFFSET, REG_GCR_ADC_GAIN_MAX,
3172159a681SPaul Cercueil 			 0, adc_tlv),
3182159a681SPaul Cercueil 
3192159a681SPaul Cercueil 	SOC_DOUBLE_R_TLV("Line In Bypass Playback Volume",
3202159a681SPaul Cercueil 			 JZ4770_CODEC_REG_GCR_LIBYL, JZ4770_CODEC_REG_GCR_LIBYR,
3212159a681SPaul Cercueil 			 REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, linein_tlv),
3222159a681SPaul Cercueil };
3232159a681SPaul Cercueil 
3242159a681SPaul Cercueil static const struct snd_kcontrol_new jz4770_codec_pcm_playback_controls[] = {
3252159a681SPaul Cercueil 	{
3262159a681SPaul Cercueil 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3272159a681SPaul Cercueil 		.name = "Volume",
3282159a681SPaul Cercueil 		.info = snd_soc_info_volsw,
3292159a681SPaul Cercueil 		.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ
3302159a681SPaul Cercueil 			| SNDRV_CTL_ELEM_ACCESS_READWRITE,
3312159a681SPaul Cercueil 		.tlv.p = dac_tlv,
3322159a681SPaul Cercueil 		.get = snd_soc_dapm_get_volsw,
3332159a681SPaul Cercueil 		.put = snd_soc_dapm_put_volsw,
3342159a681SPaul Cercueil 		/*
3352159a681SPaul Cercueil 		 * NOTE: DACR/DACL are inversed; the gain value written to DACR
3362159a681SPaul Cercueil 		 * seems to affect the left channel, and the gain value written
3372159a681SPaul Cercueil 		 * to DACL seems to affect the right channel.
3382159a681SPaul Cercueil 		 */
3392159a681SPaul Cercueil 		.private_value = SOC_DOUBLE_R_VALUE(JZ4770_CODEC_REG_GCR_DACR,
3402159a681SPaul Cercueil 						    JZ4770_CODEC_REG_GCR_DACL,
3412159a681SPaul Cercueil 						    REG_GCR_GAIN_OFFSET,
3422159a681SPaul Cercueil 						    REG_GCR_GAIN_MAX, 1),
3432159a681SPaul Cercueil 	},
3442159a681SPaul Cercueil };
3452159a681SPaul Cercueil 
3462159a681SPaul Cercueil static const struct snd_kcontrol_new jz4770_codec_hp_playback_controls[] = {
3472159a681SPaul Cercueil 	{
3482159a681SPaul Cercueil 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3492159a681SPaul Cercueil 		.name = "Volume",
3502159a681SPaul Cercueil 		.info = snd_soc_info_volsw,
3512159a681SPaul Cercueil 		.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ
3522159a681SPaul Cercueil 			| SNDRV_CTL_ELEM_ACCESS_READWRITE,
3532159a681SPaul Cercueil 		.tlv.p = out_tlv,
3542159a681SPaul Cercueil 		.get = snd_soc_dapm_get_volsw,
3552159a681SPaul Cercueil 		.put = snd_soc_dapm_put_volsw,
3562159a681SPaul Cercueil 		/* HPR/HPL inversed for the same reason as above */
3572159a681SPaul Cercueil 		.private_value = SOC_DOUBLE_R_VALUE(JZ4770_CODEC_REG_GCR_HPR,
3582159a681SPaul Cercueil 						    JZ4770_CODEC_REG_GCR_HPL,
3592159a681SPaul Cercueil 						    REG_GCR_GAIN_OFFSET,
3602159a681SPaul Cercueil 						    REG_GCR_GAIN_MAX, 1),
3612159a681SPaul Cercueil 	},
3622159a681SPaul Cercueil };
3632159a681SPaul Cercueil 
3642159a681SPaul Cercueil static int hpout_event(struct snd_soc_dapm_widget *w,
3652159a681SPaul Cercueil 		       struct snd_kcontrol *kcontrol, int event)
3662159a681SPaul Cercueil {
3672159a681SPaul Cercueil 	struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm);
3682159a681SPaul Cercueil 	struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
3692159a681SPaul Cercueil 	unsigned int val;
3702159a681SPaul Cercueil 	int err;
3712159a681SPaul Cercueil 
3722159a681SPaul Cercueil 	switch (event) {
3732159a681SPaul Cercueil 	case SND_SOC_DAPM_PRE_PMU:
3742159a681SPaul Cercueil 		/* set cap-less, unmute HP */
375ad13c835SPaul Cercueil 		regmap_clear_bits(jz_codec->regmap, JZ4770_CODEC_REG_CR_HP,
376ad13c835SPaul Cercueil 				  REG_CR_HP_SB_HPCM | REG_CR_HP_MUTE);
3772159a681SPaul Cercueil 		break;
3782159a681SPaul Cercueil 
3792159a681SPaul Cercueil 	case SND_SOC_DAPM_POST_PMU:
3802159a681SPaul Cercueil 		/* wait for ramp-up complete (RUP) */
3812159a681SPaul Cercueil 		err = regmap_read_poll_timeout(jz_codec->regmap,
3822159a681SPaul Cercueil 					       JZ4770_CODEC_REG_IFR,
3832159a681SPaul Cercueil 					       val, val & REG_IFR_RUP,
384*6b4da537SChristophe Branchereau 					       1000, 1 * USEC_PER_SEC);
3852159a681SPaul Cercueil 		if (err) {
3862159a681SPaul Cercueil 			dev_err(jz_codec->dev, "RUP timeout: %d", err);
3872159a681SPaul Cercueil 			return err;
3882159a681SPaul Cercueil 		}
3892159a681SPaul Cercueil 
3902159a681SPaul Cercueil 		/* clear RUP flag */
391ad13c835SPaul Cercueil 		regmap_set_bits(jz_codec->regmap, JZ4770_CODEC_REG_IFR,
392ad13c835SPaul Cercueil 				REG_IFR_RUP);
3932159a681SPaul Cercueil 
3942159a681SPaul Cercueil 		break;
3952159a681SPaul Cercueil 
3962159a681SPaul Cercueil 	case SND_SOC_DAPM_POST_PMD:
3972159a681SPaul Cercueil 		/* set cap-couple, mute HP */
398ad13c835SPaul Cercueil 		regmap_set_bits(jz_codec->regmap, JZ4770_CODEC_REG_CR_HP,
3992159a681SPaul Cercueil 				REG_CR_HP_SB_HPCM | REG_CR_HP_MUTE);
4002159a681SPaul Cercueil 
4012159a681SPaul Cercueil 		err = regmap_read_poll_timeout(jz_codec->regmap,
4022159a681SPaul Cercueil 					       JZ4770_CODEC_REG_IFR,
4032159a681SPaul Cercueil 					       val, val & REG_IFR_RDO,
404*6b4da537SChristophe Branchereau 					       1000, 1 * USEC_PER_SEC);
4052159a681SPaul Cercueil 		if (err) {
4062159a681SPaul Cercueil 			dev_err(jz_codec->dev, "RDO timeout: %d", err);
4072159a681SPaul Cercueil 			return err;
4082159a681SPaul Cercueil 		}
4092159a681SPaul Cercueil 
4102159a681SPaul Cercueil 		/* clear RDO flag */
411ad13c835SPaul Cercueil 		regmap_set_bits(jz_codec->regmap, JZ4770_CODEC_REG_IFR,
412ad13c835SPaul Cercueil 				REG_IFR_RDO);
4132159a681SPaul Cercueil 
4142159a681SPaul Cercueil 		break;
4152159a681SPaul Cercueil 	}
4162159a681SPaul Cercueil 
4172159a681SPaul Cercueil 	return 0;
4182159a681SPaul Cercueil }
4192159a681SPaul Cercueil 
4202159a681SPaul Cercueil static int adc_poweron_event(struct snd_soc_dapm_widget *w,
4212159a681SPaul Cercueil 			     struct snd_kcontrol *kcontrol, int event)
4222159a681SPaul Cercueil {
4232159a681SPaul Cercueil 	if (event == SND_SOC_DAPM_POST_PMU)
4242159a681SPaul Cercueil 		msleep(1000);
4252159a681SPaul Cercueil 
4262159a681SPaul Cercueil 	return 0;
4272159a681SPaul Cercueil }
4282159a681SPaul Cercueil 
4292159a681SPaul Cercueil static const char * const jz4770_codec_hp_texts[] = {
4302159a681SPaul Cercueil 	"PCM", "Line In", "Mic 1", "Mic 2"
4312159a681SPaul Cercueil };
4322159a681SPaul Cercueil static const unsigned int jz4770_codec_hp_values[] = { 3, 2, 0, 1 };
4332159a681SPaul Cercueil static SOC_VALUE_ENUM_SINGLE_DECL(jz4770_codec_hp_enum,
4342159a681SPaul Cercueil 				  JZ4770_CODEC_REG_CR_HP,
4352159a681SPaul Cercueil 				  REG_CR_HP_SEL_OFFSET,
4362159a681SPaul Cercueil 				  REG_CR_HP_SEL_MASK,
4372159a681SPaul Cercueil 				  jz4770_codec_hp_texts,
4382159a681SPaul Cercueil 				  jz4770_codec_hp_values);
4392159a681SPaul Cercueil static const struct snd_kcontrol_new jz4770_codec_hp_source =
4402159a681SPaul Cercueil 			SOC_DAPM_ENUM("Route", jz4770_codec_hp_enum);
4412159a681SPaul Cercueil 
4422159a681SPaul Cercueil static SOC_VALUE_ENUM_SINGLE_DECL(jz4770_codec_lo_enum,
4432159a681SPaul Cercueil 				  JZ4770_CODEC_REG_CR_LO,
4442159a681SPaul Cercueil 				  REG_CR_LO_SEL_OFFSET,
4452159a681SPaul Cercueil 				  REG_CR_LO_SEL_MASK,
4462159a681SPaul Cercueil 				  jz4770_codec_hp_texts,
4472159a681SPaul Cercueil 				  jz4770_codec_hp_values);
4482159a681SPaul Cercueil static const struct snd_kcontrol_new jz4770_codec_lo_source =
4492159a681SPaul Cercueil 			SOC_DAPM_ENUM("Route", jz4770_codec_lo_enum);
4502159a681SPaul Cercueil 
4512159a681SPaul Cercueil static const char * const jz4770_codec_cap_texts[] = {
4522159a681SPaul Cercueil 	"Line In", "Mic 1", "Mic 2"
4532159a681SPaul Cercueil };
4542159a681SPaul Cercueil static const unsigned int jz4770_codec_cap_values[] = { 2, 0, 1 };
4552159a681SPaul Cercueil static SOC_VALUE_ENUM_SINGLE_DECL(jz4770_codec_cap_enum,
4562159a681SPaul Cercueil 				  JZ4770_CODEC_REG_CR_ADC,
4572159a681SPaul Cercueil 				  REG_CR_ADC_IN_SEL_OFFSET,
4582159a681SPaul Cercueil 				  REG_CR_ADC_IN_SEL_MASK,
4592159a681SPaul Cercueil 				  jz4770_codec_cap_texts,
4602159a681SPaul Cercueil 				  jz4770_codec_cap_values);
4612159a681SPaul Cercueil static const struct snd_kcontrol_new jz4770_codec_cap_source =
4622159a681SPaul Cercueil 			SOC_DAPM_ENUM("Route", jz4770_codec_cap_enum);
4632159a681SPaul Cercueil 
4642159a681SPaul Cercueil static const struct snd_kcontrol_new jz4770_codec_mic_controls[] = {
4652159a681SPaul Cercueil 	SOC_DAPM_SINGLE("Stereo Capture Switch", JZ4770_CODEC_REG_CR_MIC,
4662159a681SPaul Cercueil 			REG_CR_MIC_STEREO_OFFSET, 1, 0),
4672159a681SPaul Cercueil };
4682159a681SPaul Cercueil 
4692159a681SPaul Cercueil static const struct snd_soc_dapm_widget jz4770_codec_dapm_widgets[] = {
4702159a681SPaul Cercueil 	SND_SOC_DAPM_PGA_E("HP Out", JZ4770_CODEC_REG_CR_HP,
4712159a681SPaul Cercueil 			   REG_CR_HP_SB_OFFSET, 1, NULL, 0, hpout_event,
4722159a681SPaul Cercueil 			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
4732159a681SPaul Cercueil 			   SND_SOC_DAPM_POST_PMD),
4742159a681SPaul Cercueil 
4752159a681SPaul Cercueil 	SND_SOC_DAPM_PGA("Line Out", JZ4770_CODEC_REG_CR_LO,
4762159a681SPaul Cercueil 			 REG_CR_LO_SB_OFFSET, 1, NULL, 0),
4772159a681SPaul Cercueil 
4782159a681SPaul Cercueil 	SND_SOC_DAPM_PGA("Line Out Switch 2", JZ4770_CODEC_REG_CR_LO,
4792159a681SPaul Cercueil 			 REG_CR_LO_MUTE_OFFSET, 1, NULL, 0),
4802159a681SPaul Cercueil 
4812159a681SPaul Cercueil 	SND_SOC_DAPM_PGA("Line In", JZ4770_CODEC_REG_CR_LI,
4822159a681SPaul Cercueil 			 REG_CR_LI_SB_OFFSET, 1, NULL, 0),
4832159a681SPaul Cercueil 
4842159a681SPaul Cercueil 	SND_SOC_DAPM_MUX("Headphones Source", SND_SOC_NOPM, 0, 0,
4852159a681SPaul Cercueil 			 &jz4770_codec_hp_source),
4862159a681SPaul Cercueil 	SND_SOC_DAPM_MUX("Capture Source", SND_SOC_NOPM, 0, 0,
4872159a681SPaul Cercueil 			 &jz4770_codec_cap_source),
4882159a681SPaul Cercueil 	SND_SOC_DAPM_MUX("Line Out Source", SND_SOC_NOPM, 0, 0,
4892159a681SPaul Cercueil 			 &jz4770_codec_lo_source),
4902159a681SPaul Cercueil 
4912159a681SPaul Cercueil 	SND_SOC_DAPM_PGA("Mic 1", JZ4770_CODEC_REG_CR_MIC,
4922159a681SPaul Cercueil 			 REG_CR_MIC_SB_MIC1_OFFSET, 1, NULL, 0),
4932159a681SPaul Cercueil 	SND_SOC_DAPM_PGA("Mic 2", JZ4770_CODEC_REG_CR_MIC,
4942159a681SPaul Cercueil 			 REG_CR_MIC_SB_MIC2_OFFSET, 1, NULL, 0),
4952159a681SPaul Cercueil 
4962159a681SPaul Cercueil 	SND_SOC_DAPM_PGA("Mic Diff", JZ4770_CODEC_REG_CR_MIC,
4972159a681SPaul Cercueil 			 REG_CR_MIC_IDIFF_OFFSET, 0, NULL, 0),
4982159a681SPaul Cercueil 
4992159a681SPaul Cercueil 	SND_SOC_DAPM_MIXER("Mic", SND_SOC_NOPM, 0, 0,
5002159a681SPaul Cercueil 			   jz4770_codec_mic_controls,
5012159a681SPaul Cercueil 			   ARRAY_SIZE(jz4770_codec_mic_controls)),
5022159a681SPaul Cercueil 
5032159a681SPaul Cercueil 	SND_SOC_DAPM_PGA("Line In Bypass", JZ4770_CODEC_REG_CR_LI,
5042159a681SPaul Cercueil 			 REG_CR_LI_LIBY_OFFSET, 1, NULL, 0),
5052159a681SPaul Cercueil 
5062159a681SPaul Cercueil 	SND_SOC_DAPM_ADC_E("ADC", "HiFi Capture", JZ4770_CODEC_REG_CR_ADC,
5072159a681SPaul Cercueil 			   REG_CR_ADC_SB_OFFSET, 1, adc_poweron_event,
5082159a681SPaul Cercueil 			   SND_SOC_DAPM_POST_PMU),
5092159a681SPaul Cercueil 	SND_SOC_DAPM_DAC("DAC", "HiFi Playback", JZ4770_CODEC_REG_CR_DAC,
5102159a681SPaul Cercueil 			 REG_CR_DAC_SB_OFFSET, 1),
5112159a681SPaul Cercueil 
5122159a681SPaul Cercueil 	SND_SOC_DAPM_MIXER("PCM Playback", SND_SOC_NOPM, 0, 0,
5132159a681SPaul Cercueil 			   jz4770_codec_pcm_playback_controls,
5142159a681SPaul Cercueil 			   ARRAY_SIZE(jz4770_codec_pcm_playback_controls)),
5152159a681SPaul Cercueil 	SND_SOC_DAPM_MIXER("Headphones Playback", SND_SOC_NOPM, 0, 0,
5162159a681SPaul Cercueil 			   jz4770_codec_hp_playback_controls,
5172159a681SPaul Cercueil 			   ARRAY_SIZE(jz4770_codec_hp_playback_controls)),
5182159a681SPaul Cercueil 
5192159a681SPaul Cercueil 	SND_SOC_DAPM_SUPPLY("MICBIAS", JZ4770_CODEC_REG_CR_MIC,
5202159a681SPaul Cercueil 			    REG_CR_MIC_BIAS_SB_OFFSET, 1, NULL, 0),
5212159a681SPaul Cercueil 
5222159a681SPaul Cercueil 	SND_SOC_DAPM_INPUT("MIC1P"),
5232159a681SPaul Cercueil 	SND_SOC_DAPM_INPUT("MIC1N"),
5242159a681SPaul Cercueil 	SND_SOC_DAPM_INPUT("MIC2P"),
5252159a681SPaul Cercueil 	SND_SOC_DAPM_INPUT("MIC2N"),
5262159a681SPaul Cercueil 
5272159a681SPaul Cercueil 	SND_SOC_DAPM_OUTPUT("LOUT"),
5282159a681SPaul Cercueil 	SND_SOC_DAPM_OUTPUT("ROUT"),
5292159a681SPaul Cercueil 
5302159a681SPaul Cercueil 	SND_SOC_DAPM_OUTPUT("LHPOUT"),
5312159a681SPaul Cercueil 	SND_SOC_DAPM_OUTPUT("RHPOUT"),
5322159a681SPaul Cercueil 
5332159a681SPaul Cercueil 	SND_SOC_DAPM_INPUT("LLINEIN"),
5342159a681SPaul Cercueil 	SND_SOC_DAPM_INPUT("RLINEIN"),
5352159a681SPaul Cercueil 
5362159a681SPaul Cercueil 	SND_SOC_DAPM_OUTPUT("SYSCLK"),
5372159a681SPaul Cercueil };
5382159a681SPaul Cercueil 
5392159a681SPaul Cercueil /* Unconditional routes. */
5402159a681SPaul Cercueil static const struct snd_soc_dapm_route jz4770_codec_dapm_routes[] = {
5412159a681SPaul Cercueil 	{ "Mic 1", NULL, "MIC1P" },
5422159a681SPaul Cercueil 	{ "Mic Diff", NULL, "MIC1N" },
5432159a681SPaul Cercueil 	{ "Mic 1", NULL, "Mic Diff" },
5442159a681SPaul Cercueil 	{ "Mic 2", NULL, "MIC2P" },
5452159a681SPaul Cercueil 	{ "Mic Diff", NULL, "MIC2N" },
5462159a681SPaul Cercueil 	{ "Mic 2", NULL, "Mic Diff" },
5472159a681SPaul Cercueil 
5482159a681SPaul Cercueil 	{ "Line In", NULL, "LLINEIN" },
5492159a681SPaul Cercueil 	{ "Line In", NULL, "RLINEIN" },
5502159a681SPaul Cercueil 
5512159a681SPaul Cercueil 	{ "Mic", "Stereo Capture Switch", "Mic 1" },
5522159a681SPaul Cercueil 	{ "Mic", "Stereo Capture Switch", "Mic 2" },
5532159a681SPaul Cercueil 	{ "Headphones Source", "Mic 1", "Mic" },
5542159a681SPaul Cercueil 	{ "Headphones Source", "Mic 2", "Mic" },
5552159a681SPaul Cercueil 	{ "Capture Source", "Mic 1", "Mic" },
5562159a681SPaul Cercueil 	{ "Capture Source", "Mic 2", "Mic" },
5572159a681SPaul Cercueil 
5582159a681SPaul Cercueil 	{ "Headphones Source", "Mic 1", "Mic 1" },
5592159a681SPaul Cercueil 	{ "Headphones Source", "Mic 2", "Mic 2" },
5602159a681SPaul Cercueil 	{ "Headphones Source", "Line In", "Line In Bypass" },
5612159a681SPaul Cercueil 	{ "Headphones Source", "PCM", "Headphones Playback" },
5622159a681SPaul Cercueil 	{ "HP Out", NULL, "Headphones Source" },
5632159a681SPaul Cercueil 
5642159a681SPaul Cercueil 	{ "Capture Source", "Line In", "Line In" },
5652159a681SPaul Cercueil 	{ "Capture Source", "Mic 1", "Mic 1" },
5662159a681SPaul Cercueil 	{ "Capture Source", "Mic 2", "Mic 2" },
5672159a681SPaul Cercueil 	{ "ADC", NULL, "Capture Source" },
5682159a681SPaul Cercueil 
5692159a681SPaul Cercueil 	{ "Line In Bypass", NULL, "Line In" },
5702159a681SPaul Cercueil 	{ "Line Out Source", "Line In", "Line In Bypass" },
5712159a681SPaul Cercueil 	{ "Line Out Source", "PCM", "PCM Playback" },
5722159a681SPaul Cercueil 
5732159a681SPaul Cercueil 	{ "LHPOUT", NULL, "HP Out"},
5742159a681SPaul Cercueil 	{ "RHPOUT", NULL, "HP Out"},
5752159a681SPaul Cercueil 
5762159a681SPaul Cercueil 	{ "Line Out", NULL, "Line Out Source" },
5772159a681SPaul Cercueil 	{ "Line Out Switch 2", NULL, "Line Out" },
5782159a681SPaul Cercueil 
5792159a681SPaul Cercueil 	{ "LOUT", NULL, "Line Out Switch 2"},
5802159a681SPaul Cercueil 	{ "ROUT", NULL, "Line Out Switch 2"},
5812159a681SPaul Cercueil 
5822159a681SPaul Cercueil 	{ "PCM Playback", "Volume", "DAC" },
5832159a681SPaul Cercueil 	{ "Headphones Playback", "Volume", "PCM Playback" },
5842159a681SPaul Cercueil 
5852159a681SPaul Cercueil 	{ "SYSCLK", NULL, "DAC" },
5862159a681SPaul Cercueil };
5872159a681SPaul Cercueil 
5882159a681SPaul Cercueil static void jz4770_codec_codec_init_regs(struct snd_soc_component *codec)
5892159a681SPaul Cercueil {
5902159a681SPaul Cercueil 	struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
5912159a681SPaul Cercueil 	struct regmap *regmap = jz_codec->regmap;
5922159a681SPaul Cercueil 
5932159a681SPaul Cercueil 	/* Collect updates for later sending. */
5942159a681SPaul Cercueil 	regcache_cache_only(regmap, true);
5952159a681SPaul Cercueil 
5962159a681SPaul Cercueil 	/* default HP output to PCM */
597ad13c835SPaul Cercueil 	regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_HP, REG_CR_HP_SEL_MASK);
5982159a681SPaul Cercueil 
5992159a681SPaul Cercueil 	/* default line output to PCM */
600ad13c835SPaul Cercueil 	regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_LO, REG_CR_LO_SEL_MASK);
6012159a681SPaul Cercueil 
6022159a681SPaul Cercueil 	/* Disable stereo mic */
603ad13c835SPaul Cercueil 	regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_MIC,
604ad13c835SPaul Cercueil 			  BIT(REG_CR_MIC_STEREO_OFFSET));
6052159a681SPaul Cercueil 
6062159a681SPaul Cercueil 	/* Set mic 1 as default source for ADC */
607ad13c835SPaul Cercueil 	regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_ADC,
608ad13c835SPaul Cercueil 			  REG_CR_ADC_IN_SEL_MASK);
6092159a681SPaul Cercueil 
6102159a681SPaul Cercueil 	/* ADC/DAC: serial + i2s */
611ad13c835SPaul Cercueil 	regmap_set_bits(regmap, JZ4770_CODEC_REG_AICR_ADC,
6122159a681SPaul Cercueil 			REG_AICR_ADC_SERIAL | REG_AICR_ADC_I2S);
613ad13c835SPaul Cercueil 	regmap_set_bits(regmap, JZ4770_CODEC_REG_AICR_DAC,
6142159a681SPaul Cercueil 			REG_AICR_DAC_SERIAL | REG_AICR_DAC_I2S);
6152159a681SPaul Cercueil 
6162159a681SPaul Cercueil 	/* The generated IRQ is a high level */
617ad13c835SPaul Cercueil 	regmap_clear_bits(regmap, JZ4770_CODEC_REG_ICR, REG_ICR_INT_FORM_MASK);
6182159a681SPaul Cercueil 	regmap_update_bits(regmap, JZ4770_CODEC_REG_IMR, REG_IMR_ALL_MASK,
6192159a681SPaul Cercueil 			   REG_IMR_JACK_MASK | REG_IMR_RUP_MASK |
6202159a681SPaul Cercueil 			   REG_IMR_RDO_MASK | REG_IMR_GUP_MASK |
6212159a681SPaul Cercueil 			   REG_IMR_GDO_MASK);
6222159a681SPaul Cercueil 
6232159a681SPaul Cercueil 	/* 12M oscillator */
624ad13c835SPaul Cercueil 	regmap_clear_bits(regmap, JZ4770_CODEC_REG_CCR, REG_CCR_CRYSTAL_MASK);
6252159a681SPaul Cercueil 
6262159a681SPaul Cercueil 	/* 0: 16ohm/220uF, 1: 10kohm/1uF */
627ad13c835SPaul Cercueil 	regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_HP, REG_CR_HP_LOAD);
6282159a681SPaul Cercueil 
6292159a681SPaul Cercueil 	/* disable automatic gain */
630ad13c835SPaul Cercueil 	regmap_clear_bits(regmap, JZ4770_CODEC_REG_AGC1, REG_AGC1_EN);
6312159a681SPaul Cercueil 
6322159a681SPaul Cercueil 	/* Disable DAC lrswap */
633ad13c835SPaul Cercueil 	regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_DAC, REG_CR_DAC_LRSWAP);
6342159a681SPaul Cercueil 
6352159a681SPaul Cercueil 	/* Independent L/R DAC gain control */
636ad13c835SPaul Cercueil 	regmap_clear_bits(regmap, JZ4770_CODEC_REG_GCR_DACL,
637ad13c835SPaul Cercueil 			  REG_GCR_DACL_RLGOD);
6382159a681SPaul Cercueil 
6392159a681SPaul Cercueil 	/* Disable ADC lrswap */
640ad13c835SPaul Cercueil 	regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_ADC, REG_CR_ADC_LRSWAP);
6412159a681SPaul Cercueil 
6422159a681SPaul Cercueil 	/* default to cap-less mode(0) */
643ad13c835SPaul Cercueil 	regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_HP, REG_CR_HP_SB_HPCM);
6442159a681SPaul Cercueil 
6452159a681SPaul Cercueil 	/* Send collected updates. */
6462159a681SPaul Cercueil 	regcache_cache_only(regmap, false);
6472159a681SPaul Cercueil 	regcache_sync(regmap);
6482159a681SPaul Cercueil }
6492159a681SPaul Cercueil 
6502159a681SPaul Cercueil static int jz4770_codec_codec_probe(struct snd_soc_component *codec)
6512159a681SPaul Cercueil {
6522159a681SPaul Cercueil 	struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
6532159a681SPaul Cercueil 
6542159a681SPaul Cercueil 	clk_prepare_enable(jz_codec->clk);
6552159a681SPaul Cercueil 
6562159a681SPaul Cercueil 	jz4770_codec_codec_init_regs(codec);
6572159a681SPaul Cercueil 
6582159a681SPaul Cercueil 	return 0;
6592159a681SPaul Cercueil }
6602159a681SPaul Cercueil 
6612159a681SPaul Cercueil static void jz4770_codec_codec_remove(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_disable_unprepare(jz_codec->clk);
6662159a681SPaul Cercueil }
6672159a681SPaul Cercueil 
6682159a681SPaul Cercueil static const struct snd_soc_component_driver jz4770_codec_soc_codec_dev = {
6692159a681SPaul Cercueil 	.probe			= jz4770_codec_codec_probe,
6702159a681SPaul Cercueil 	.remove			= jz4770_codec_codec_remove,
6712159a681SPaul Cercueil 	.set_bias_level		= jz4770_codec_set_bias_level,
6722159a681SPaul Cercueil 	.controls		= jz4770_codec_snd_controls,
6732159a681SPaul Cercueil 	.num_controls		= ARRAY_SIZE(jz4770_codec_snd_controls),
6742159a681SPaul Cercueil 	.dapm_widgets		= jz4770_codec_dapm_widgets,
6752159a681SPaul Cercueil 	.num_dapm_widgets	= ARRAY_SIZE(jz4770_codec_dapm_widgets),
6762159a681SPaul Cercueil 	.dapm_routes		= jz4770_codec_dapm_routes,
6772159a681SPaul Cercueil 	.num_dapm_routes	= ARRAY_SIZE(jz4770_codec_dapm_routes),
6782159a681SPaul Cercueil 	.suspend_bias_off	= 1,
6792159a681SPaul Cercueil 	.use_pmdown_time	= 1,
6802159a681SPaul Cercueil };
6812159a681SPaul Cercueil 
6822159a681SPaul Cercueil static const unsigned int jz4770_codec_sample_rates[] = {
6832159a681SPaul Cercueil 	96000, 48000, 44100, 32000,
6842159a681SPaul Cercueil 	24000, 22050, 16000, 12000,
6852159a681SPaul Cercueil 	11025, 9600, 8000,
6862159a681SPaul Cercueil };
6872159a681SPaul Cercueil 
6882159a681SPaul Cercueil static int jz4770_codec_hw_params(struct snd_pcm_substream *substream,
6892159a681SPaul Cercueil 				  struct snd_pcm_hw_params *params,
6902159a681SPaul Cercueil 				  struct snd_soc_dai *dai)
6912159a681SPaul Cercueil {
6922159a681SPaul Cercueil 	struct jz_codec *codec = snd_soc_component_get_drvdata(dai->component);
6932159a681SPaul Cercueil 	unsigned int rate, bit_width;
6942159a681SPaul Cercueil 
6952159a681SPaul Cercueil 	switch (params_format(params)) {
6962159a681SPaul Cercueil 	case SNDRV_PCM_FORMAT_S16_LE:
6972159a681SPaul Cercueil 		bit_width = 0;
6982159a681SPaul Cercueil 		break;
6992159a681SPaul Cercueil 	case SNDRV_PCM_FORMAT_S18_3LE:
7002159a681SPaul Cercueil 		bit_width = 1;
7012159a681SPaul Cercueil 		break;
7022159a681SPaul Cercueil 	case SNDRV_PCM_FORMAT_S20_3LE:
7032159a681SPaul Cercueil 		bit_width = 2;
7042159a681SPaul Cercueil 		break;
7052159a681SPaul Cercueil 	case SNDRV_PCM_FORMAT_S24_3LE:
7062159a681SPaul Cercueil 		bit_width = 3;
7072159a681SPaul Cercueil 		break;
7082159a681SPaul Cercueil 	default:
7092159a681SPaul Cercueil 		return -EINVAL;
7102159a681SPaul Cercueil 	}
7112159a681SPaul Cercueil 
7122159a681SPaul Cercueil 	for (rate = 0; rate < ARRAY_SIZE(jz4770_codec_sample_rates); rate++) {
7132159a681SPaul Cercueil 		if (jz4770_codec_sample_rates[rate] == params_rate(params))
7142159a681SPaul Cercueil 			break;
7152159a681SPaul Cercueil 	}
7162159a681SPaul Cercueil 
7172159a681SPaul Cercueil 	if (rate == ARRAY_SIZE(jz4770_codec_sample_rates))
7182159a681SPaul Cercueil 		return -EINVAL;
7192159a681SPaul Cercueil 
7202159a681SPaul Cercueil 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
7212159a681SPaul Cercueil 		regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_AICR_DAC,
7222159a681SPaul Cercueil 				   REG_AICR_DAC_ADWL_MASK,
7232159a681SPaul Cercueil 				   bit_width << REG_AICR_DAC_ADWL_OFFSET);
7242159a681SPaul Cercueil 		regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_FCR_DAC,
7252159a681SPaul Cercueil 				   REG_FCR_DAC_FREQ_MASK,
7262159a681SPaul Cercueil 				   rate << REG_FCR_DAC_FREQ_OFFSET);
7272159a681SPaul Cercueil 	} else {
7282159a681SPaul Cercueil 		regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_AICR_ADC,
7292159a681SPaul Cercueil 				   REG_AICR_ADC_ADWL_MASK,
7302159a681SPaul Cercueil 				   bit_width << REG_AICR_ADC_ADWL_OFFSET);
7312159a681SPaul Cercueil 		regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_FCR_ADC,
7322159a681SPaul Cercueil 				   REG_FCR_ADC_FREQ_MASK,
7332159a681SPaul Cercueil 				   rate << REG_FCR_ADC_FREQ_OFFSET);
7342159a681SPaul Cercueil 	}
7352159a681SPaul Cercueil 
7362159a681SPaul Cercueil 	return 0;
7372159a681SPaul Cercueil }
7382159a681SPaul Cercueil 
7392159a681SPaul Cercueil static const struct snd_soc_dai_ops jz4770_codec_dai_ops = {
7402159a681SPaul Cercueil 	.startup	= jz4770_codec_startup,
7412159a681SPaul Cercueil 	.shutdown	= jz4770_codec_shutdown,
7422159a681SPaul Cercueil 	.hw_params	= jz4770_codec_hw_params,
7432159a681SPaul Cercueil 	.trigger	= jz4770_codec_pcm_trigger,
74454b59270SKuninori Morimoto 	.mute_stream	= jz4770_codec_mute_stream,
74554b59270SKuninori Morimoto 	.no_capture_mute = 1,
7462159a681SPaul Cercueil };
7472159a681SPaul Cercueil 
7482159a681SPaul Cercueil #define JZ_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE  | \
7492159a681SPaul Cercueil 			  SNDRV_PCM_FMTBIT_S18_3LE | \
7502159a681SPaul Cercueil 			  SNDRV_PCM_FMTBIT_S20_3LE | \
7512159a681SPaul Cercueil 			  SNDRV_PCM_FMTBIT_S24_3LE)
7522159a681SPaul Cercueil 
7532159a681SPaul Cercueil static struct snd_soc_dai_driver jz4770_codec_dai = {
7542159a681SPaul Cercueil 	.name = "jz4770-hifi",
7552159a681SPaul Cercueil 	.playback = {
7562159a681SPaul Cercueil 		.stream_name = "Playback",
7572159a681SPaul Cercueil 		.channels_min = 2,
7582159a681SPaul Cercueil 		.channels_max = 2,
7592159a681SPaul Cercueil 		.rates = SNDRV_PCM_RATE_8000_96000,
7602159a681SPaul Cercueil 		.formats = JZ_CODEC_FORMATS,
7612159a681SPaul Cercueil 	},
7622159a681SPaul Cercueil 	.capture = {
7632159a681SPaul Cercueil 		.stream_name = "Capture",
7642159a681SPaul Cercueil 		.channels_min = 2,
7652159a681SPaul Cercueil 		.channels_max = 2,
7662159a681SPaul Cercueil 		.rates = SNDRV_PCM_RATE_8000_96000,
7672159a681SPaul Cercueil 		.formats = JZ_CODEC_FORMATS,
7682159a681SPaul Cercueil 	},
7692159a681SPaul Cercueil 	.ops = &jz4770_codec_dai_ops,
7702159a681SPaul Cercueil };
7712159a681SPaul Cercueil 
7722159a681SPaul Cercueil static bool jz4770_codec_volatile(struct device *dev, unsigned int reg)
7732159a681SPaul Cercueil {
7742159a681SPaul Cercueil 	return reg == JZ4770_CODEC_REG_SR || reg == JZ4770_CODEC_REG_IFR;
7752159a681SPaul Cercueil }
7762159a681SPaul Cercueil 
7772159a681SPaul Cercueil static bool jz4770_codec_readable(struct device *dev, unsigned int reg)
7782159a681SPaul Cercueil {
7792159a681SPaul Cercueil 	switch (reg) {
7802159a681SPaul Cercueil 	case JZ4770_CODEC_REG_MISSING_REG1:
7812159a681SPaul Cercueil 	case JZ4770_CODEC_REG_MISSING_REG2:
7822159a681SPaul Cercueil 		return false;
7832159a681SPaul Cercueil 	default:
7842159a681SPaul Cercueil 		return true;
7852159a681SPaul Cercueil 	}
7862159a681SPaul Cercueil }
7872159a681SPaul Cercueil 
7882159a681SPaul Cercueil static bool jz4770_codec_writeable(struct device *dev, unsigned int reg)
7892159a681SPaul Cercueil {
7902159a681SPaul Cercueil 	switch (reg) {
7912159a681SPaul Cercueil 	case JZ4770_CODEC_REG_SR:
7922159a681SPaul Cercueil 	case JZ4770_CODEC_REG_MISSING_REG1:
7932159a681SPaul Cercueil 	case JZ4770_CODEC_REG_MISSING_REG2:
7942159a681SPaul Cercueil 		return false;
7952159a681SPaul Cercueil 	default:
7962159a681SPaul Cercueil 		return true;
7972159a681SPaul Cercueil 	}
7982159a681SPaul Cercueil }
7992159a681SPaul Cercueil 
8002159a681SPaul Cercueil static int jz4770_codec_io_wait(struct jz_codec *codec)
8012159a681SPaul Cercueil {
8022159a681SPaul Cercueil 	u32 reg;
8032159a681SPaul Cercueil 
8042159a681SPaul Cercueil 	return readl_poll_timeout(codec->base + ICDC_RGADW_OFFSET, reg,
8052159a681SPaul Cercueil 				  !(reg & ICDC_RGADW_RGWR),
806*6b4da537SChristophe Branchereau 				  1000, 1 * USEC_PER_SEC);
8072159a681SPaul Cercueil }
8082159a681SPaul Cercueil 
8092159a681SPaul Cercueil static int jz4770_codec_reg_read(void *context, unsigned int reg,
8102159a681SPaul Cercueil 				 unsigned int *val)
8112159a681SPaul Cercueil {
8122159a681SPaul Cercueil 	struct jz_codec *codec = context;
8132159a681SPaul Cercueil 	unsigned int i;
8142159a681SPaul Cercueil 	u32 tmp;
8152159a681SPaul Cercueil 	int ret;
8162159a681SPaul Cercueil 
8172159a681SPaul Cercueil 	ret = jz4770_codec_io_wait(codec);
8182159a681SPaul Cercueil 	if (ret)
8192159a681SPaul Cercueil 		return ret;
8202159a681SPaul Cercueil 
8212159a681SPaul Cercueil 	tmp = readl(codec->base + ICDC_RGADW_OFFSET);
8222159a681SPaul Cercueil 	tmp = (tmp & ~ICDC_RGADW_RGADDR_MASK)
8232159a681SPaul Cercueil 	    | (reg << ICDC_RGADW_RGADDR_OFFSET);
8242159a681SPaul Cercueil 	writel(tmp, codec->base + ICDC_RGADW_OFFSET);
8252159a681SPaul Cercueil 
8262159a681SPaul Cercueil 	/* wait 6+ cycles */
8272159a681SPaul Cercueil 	for (i = 0; i < 6; i++)
8282159a681SPaul Cercueil 		*val = readl(codec->base + ICDC_RGDATA_OFFSET) &
8292159a681SPaul Cercueil 			ICDC_RGDATA_RGDOUT_MASK;
8302159a681SPaul Cercueil 
8312159a681SPaul Cercueil 	return 0;
8322159a681SPaul Cercueil }
8332159a681SPaul Cercueil 
8342159a681SPaul Cercueil static int jz4770_codec_reg_write(void *context, unsigned int reg,
8352159a681SPaul Cercueil 				  unsigned int val)
8362159a681SPaul Cercueil {
8372159a681SPaul Cercueil 	struct jz_codec *codec = context;
8382159a681SPaul Cercueil 	int ret;
8392159a681SPaul Cercueil 
8402159a681SPaul Cercueil 	ret = jz4770_codec_io_wait(codec);
8412159a681SPaul Cercueil 	if (ret)
8422159a681SPaul Cercueil 		return ret;
8432159a681SPaul Cercueil 
8442159a681SPaul Cercueil 	writel(ICDC_RGADW_RGWR | (reg << ICDC_RGADW_RGADDR_OFFSET) | val,
8452159a681SPaul Cercueil 	       codec->base + ICDC_RGADW_OFFSET);
8462159a681SPaul Cercueil 
8472159a681SPaul Cercueil 	ret = jz4770_codec_io_wait(codec);
8482159a681SPaul Cercueil 	if (ret)
8492159a681SPaul Cercueil 		return ret;
8502159a681SPaul Cercueil 
8512159a681SPaul Cercueil 	return 0;
8522159a681SPaul Cercueil }
8532159a681SPaul Cercueil 
8542159a681SPaul Cercueil static const u8 jz4770_codec_reg_defaults[] = {
8552159a681SPaul Cercueil 	0x00, 0xC3, 0xC3, 0x90, 0x98, 0xFF, 0x90, 0xB1,
8562159a681SPaul Cercueil 	0x11, 0x10, 0x00, 0x03, 0x00, 0x00, 0x40, 0x00,
8572159a681SPaul Cercueil 	0xFF, 0x00, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00,
8582159a681SPaul Cercueil 	0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x34,
8592159a681SPaul Cercueil 	0x07, 0x44, 0x1F, 0x00
8602159a681SPaul Cercueil };
8612159a681SPaul Cercueil 
8622159a681SPaul Cercueil static struct regmap_config jz4770_codec_regmap_config = {
8632159a681SPaul Cercueil 	.reg_bits = 7,
8642159a681SPaul Cercueil 	.val_bits = 8,
8652159a681SPaul Cercueil 
8662159a681SPaul Cercueil 	.max_register = JZ4770_CODEC_REG_AGC5,
8672159a681SPaul Cercueil 	.volatile_reg = jz4770_codec_volatile,
8682159a681SPaul Cercueil 	.readable_reg = jz4770_codec_readable,
8692159a681SPaul Cercueil 	.writeable_reg = jz4770_codec_writeable,
8702159a681SPaul Cercueil 
8712159a681SPaul Cercueil 	.reg_read = jz4770_codec_reg_read,
8722159a681SPaul Cercueil 	.reg_write = jz4770_codec_reg_write,
8732159a681SPaul Cercueil 
8742159a681SPaul Cercueil 	.reg_defaults_raw = jz4770_codec_reg_defaults,
8752159a681SPaul Cercueil 	.num_reg_defaults_raw = ARRAY_SIZE(jz4770_codec_reg_defaults),
8762159a681SPaul Cercueil 	.cache_type = REGCACHE_FLAT,
8772159a681SPaul Cercueil };
8782159a681SPaul Cercueil 
8792159a681SPaul Cercueil static int jz4770_codec_probe(struct platform_device *pdev)
8802159a681SPaul Cercueil {
8812159a681SPaul Cercueil 	struct device *dev = &pdev->dev;
8822159a681SPaul Cercueil 	struct jz_codec *codec;
8832159a681SPaul Cercueil 	int ret;
8842159a681SPaul Cercueil 
8852159a681SPaul Cercueil 	codec = devm_kzalloc(dev, sizeof(*codec), GFP_KERNEL);
8862159a681SPaul Cercueil 	if (!codec)
8872159a681SPaul Cercueil 		return -ENOMEM;
8882159a681SPaul Cercueil 
8892159a681SPaul Cercueil 	codec->dev = dev;
8902159a681SPaul Cercueil 
8912159a681SPaul Cercueil 	codec->base = devm_platform_ioremap_resource(pdev, 0);
8922159a681SPaul Cercueil 	if (IS_ERR(codec->base)) {
8932159a681SPaul Cercueil 		ret = PTR_ERR(codec->base);
8942159a681SPaul Cercueil 		dev_err(dev, "Failed to ioremap mmio memory: %d\n", ret);
8952159a681SPaul Cercueil 		return ret;
8962159a681SPaul Cercueil 	}
8972159a681SPaul Cercueil 
8982159a681SPaul Cercueil 	codec->regmap = devm_regmap_init(dev, NULL, codec,
8992159a681SPaul Cercueil 					&jz4770_codec_regmap_config);
9002159a681SPaul Cercueil 	if (IS_ERR(codec->regmap))
9012159a681SPaul Cercueil 		return PTR_ERR(codec->regmap);
9022159a681SPaul Cercueil 
9032159a681SPaul Cercueil 	codec->clk = devm_clk_get(dev, "aic");
9042159a681SPaul Cercueil 	if (IS_ERR(codec->clk))
9052159a681SPaul Cercueil 		return PTR_ERR(codec->clk);
9062159a681SPaul Cercueil 
9072159a681SPaul Cercueil 	platform_set_drvdata(pdev, codec);
9082159a681SPaul Cercueil 
9092159a681SPaul Cercueil 	ret = devm_snd_soc_register_component(dev, &jz4770_codec_soc_codec_dev,
9102159a681SPaul Cercueil 					      &jz4770_codec_dai, 1);
9112159a681SPaul Cercueil 	if (ret) {
9122159a681SPaul Cercueil 		dev_err(dev, "Failed to register codec: %d\n", ret);
9132159a681SPaul Cercueil 		return ret;
9142159a681SPaul Cercueil 	}
9152159a681SPaul Cercueil 
9162159a681SPaul Cercueil 	return 0;
9172159a681SPaul Cercueil }
9182159a681SPaul Cercueil 
9192159a681SPaul Cercueil static const struct of_device_id jz4770_codec_of_matches[] = {
9202159a681SPaul Cercueil 	{ .compatible = "ingenic,jz4770-codec", },
9212159a681SPaul Cercueil 	{ /* sentinel */ }
9222159a681SPaul Cercueil };
9232159a681SPaul Cercueil MODULE_DEVICE_TABLE(of, jz4770_codec_of_matches);
9242159a681SPaul Cercueil 
9252159a681SPaul Cercueil static struct platform_driver jz4770_codec_driver = {
9262159a681SPaul Cercueil 	.probe			= jz4770_codec_probe,
9272159a681SPaul Cercueil 	.driver			= {
9282159a681SPaul Cercueil 		.name		= "jz4770-codec",
929e6825baeSPaul Cercueil 		.of_match_table = jz4770_codec_of_matches,
9302159a681SPaul Cercueil 	},
9312159a681SPaul Cercueil };
9322159a681SPaul Cercueil module_platform_driver(jz4770_codec_driver);
9332159a681SPaul Cercueil 
9342159a681SPaul Cercueil MODULE_DESCRIPTION("JZ4770 SoC internal codec driver");
9352159a681SPaul Cercueil MODULE_AUTHOR("Maarten ter Huurne <maarten@treewalker.org>");
9362159a681SPaul Cercueil MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
9372159a681SPaul Cercueil MODULE_LICENSE("GPL v2");
938