xref: /openbmc/linux/sound/soc/codecs/jz4770.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
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