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