mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-11-03 22:44:27 -05:00 
			
		
		
		
	Remove patches that were upstreamed, and backport features from later kernels. Signed-off-by: Zoltan HERPAI <wigyori@uid0.hu>
		
			
				
	
	
		
			615 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			615 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
From 0963766bc665769aebf370d44ee3a97facfbca57 Mon Sep 17 00:00:00 2001
 | 
						|
From: Maksim Kiselev <bigunclemax@gmail.com>
 | 
						|
Date: Sat, 5 Aug 2023 21:05:03 +0300
 | 
						|
Subject: [PATCH 09/14] ASoC: sunxi: sun4i-codec: add basic support for D1
 | 
						|
 audio codec
 | 
						|
 | 
						|
Allwinner D1 has an audio codec similar to earlier ones, but it comes
 | 
						|
with 3 channel ADC instead of 2, and many registers are moved.
 | 
						|
 | 
						|
Add basic support for it.
 | 
						|
 | 
						|
Signed-off-by: Maksim Kiselev <bigunclemax@gmail.com>
 | 
						|
---
 | 
						|
 sound/soc/sunxi/sun4i-codec.c | 364 ++++++++++++++++++++++++++++------
 | 
						|
 1 file changed, 300 insertions(+), 64 deletions(-)
 | 
						|
 | 
						|
--- a/sound/soc/sunxi/sun4i-codec.c
 | 
						|
+++ b/sound/soc/sunxi/sun4i-codec.c
 | 
						|
@@ -232,15 +232,65 @@
 | 
						|
 
 | 
						|
 /* TODO H3 DAP (Digital Audio Processing) bits */
 | 
						|
 
 | 
						|
+/*
 | 
						|
+ * sun20i D1 and similar codecs specific registers
 | 
						|
+ *
 | 
						|
+ * Almost all registers moved on D1, including ADC digital controls,
 | 
						|
+ * FIFO and RX data registers. Only DAC control are at the same offset.
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#define SUN20I_D1_CODEC_DAC_VOL_CTRL		(0x04)
 | 
						|
+#define SUN20I_D1_CODEC_DAC_VOL_SEL			(16)
 | 
						|
+#define SUN20I_D1_CODEC_DAC_VOL_L			(8)
 | 
						|
+#define SUN20I_D1_CODEC_DAC_VOL_R			(0)
 | 
						|
+#define SUN20I_D1_CODEC_DAC_FIFOC		(0x10)
 | 
						|
+#define SUN20I_D1_CODEC_ADC_FIFOC		(0x30)
 | 
						|
+#define SUN20I_D1_CODEC_ADC_FIFOC_EN_AD			(28)
 | 
						|
+#define SUN20I_D1_CODEC_ADC_FIFOC_RX_SAMPLE_BITS	(16)
 | 
						|
+#define SUN20I_D1_CODEC_ADC_FIFOC_RX_TRIG_LEVEL		(4)
 | 
						|
+#define SUN20I_D1_CODEC_ADC_FIFOC_ADC_DRQ_EN		(3)
 | 
						|
+#define SUN20I_D1_CODEC_ADC_VOL_CTRL1		(0x34)
 | 
						|
+#define SUN20I_D1_CODEC_ADC_VOL_CTRL1_ADC3_VOL		(16)
 | 
						|
+#define SUN20I_D1_CODEC_ADC_VOL_CTRL1_ADC2_VOL		(8)
 | 
						|
+#define SUN20I_D1_CODEC_ADC_VOL_CTRL1_ADC1_VOL		(0)
 | 
						|
+#define SUN20I_D1_CODEC_ADC_RXDATA		(0x40)
 | 
						|
+#define SUN20I_D1_CODEC_ADC_DIG_CTRL		(0x50)
 | 
						|
+#define SUN20I_D1_CODEC_ADC_DIG_CTRL_ADC3_CH_EN		(2)
 | 
						|
+#define SUN20I_D1_CODEC_ADC_DIG_CTRL_ADC2_CH_EN		(1)
 | 
						|
+#define SUN20I_D1_CODEC_ADC_DIG_CTRL_ADC1_CH_EN		(0)
 | 
						|
+#define SUN20I_D1_CODEC_VRA1SPEEDUP_DOWN_CTRL	(0x54)
 | 
						|
+
 | 
						|
+/* TODO D1 DAP (Digital Audio Processing) bits */
 | 
						|
+
 | 
						|
+struct sun4i_codec;
 | 
						|
+
 | 
						|
+struct sun4i_codec_quirks {
 | 
						|
+	const struct regmap_config *regmap_config;
 | 
						|
+	const struct snd_soc_component_driver *codec;
 | 
						|
+	struct snd_soc_card * (*create_card)(struct device *dev);
 | 
						|
+	struct reg_field reg_dac_fifoc;	/* used for regmap_field */
 | 
						|
+	struct reg_field reg_adc_fifoc;	/* used for regmap_field */
 | 
						|
+	unsigned int adc_drq_en;
 | 
						|
+	unsigned int rx_sample_bits;
 | 
						|
+	unsigned int rx_trig_level;
 | 
						|
+	unsigned int reg_dac_txdata;	/* TX FIFO offset for DMA config */
 | 
						|
+	unsigned int reg_adc_rxdata;	/* RX FIFO offset for DMA config */
 | 
						|
+	bool has_reset;
 | 
						|
+	bool has_dual_clock;
 | 
						|
+};
 | 
						|
+
 | 
						|
 struct sun4i_codec {
 | 
						|
 	struct device	*dev;
 | 
						|
 	struct regmap	*regmap;
 | 
						|
 	struct clk	*clk_apb;
 | 
						|
-	struct clk	*clk_module;
 | 
						|
+	struct clk	*clk_module; /* used for ADC if clocks are separate */
 | 
						|
+	struct clk	*clk_module_dac;
 | 
						|
 	struct reset_control *rst;
 | 
						|
 	struct gpio_desc *gpio_pa;
 | 
						|
+	const struct sun4i_codec_quirks *quirks;
 | 
						|
 
 | 
						|
-	/* ADC_FIFOC register is at different offset on different SoCs */
 | 
						|
+	/* DAC/ADC FIFOC registers are at different offset on different SoCs */
 | 
						|
+	struct regmap_field *reg_dac_fifoc;
 | 
						|
 	struct regmap_field *reg_adc_fifoc;
 | 
						|
 
 | 
						|
 	struct snd_dmaengine_dai_dma_data	capture_dma_data;
 | 
						|
@@ -250,33 +300,33 @@ struct sun4i_codec {
 | 
						|
 static void sun4i_codec_start_playback(struct sun4i_codec *scodec)
 | 
						|
 {
 | 
						|
 	/* Flush TX FIFO */
 | 
						|
-	regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
 | 
						|
-			BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH));
 | 
						|
+	regmap_field_set_bits(scodec->reg_dac_fifoc,
 | 
						|
+			      BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH));
 | 
						|
 
 | 
						|
 	/* Enable DAC DRQ */
 | 
						|
-	regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
 | 
						|
-			BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN));
 | 
						|
+	regmap_field_set_bits(scodec->reg_dac_fifoc,
 | 
						|
+			      BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN));
 | 
						|
 }
 | 
						|
 
 | 
						|
 static void sun4i_codec_stop_playback(struct sun4i_codec *scodec)
 | 
						|
 {
 | 
						|
 	/* Disable DAC DRQ */
 | 
						|
-	regmap_clear_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
 | 
						|
-			  BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN));
 | 
						|
+	regmap_field_clear_bits(scodec->reg_dac_fifoc,
 | 
						|
+				BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN));
 | 
						|
 }
 | 
						|
 
 | 
						|
 static void sun4i_codec_start_capture(struct sun4i_codec *scodec)
 | 
						|
 {
 | 
						|
 	/* Enable ADC DRQ */
 | 
						|
 	regmap_field_set_bits(scodec->reg_adc_fifoc,
 | 
						|
-			      BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN));
 | 
						|
+			      BIT(scodec->quirks->adc_drq_en));
 | 
						|
 }
 | 
						|
 
 | 
						|
 static void sun4i_codec_stop_capture(struct sun4i_codec *scodec)
 | 
						|
 {
 | 
						|
 	/* Disable ADC DRQ */
 | 
						|
 	regmap_field_clear_bits(scodec->reg_adc_fifoc,
 | 
						|
-				 BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN));
 | 
						|
+				BIT(scodec->quirks->adc_drq_en));
 | 
						|
 }
 | 
						|
 
 | 
						|
 static int sun4i_codec_trigger(struct snd_pcm_substream *substream, int cmd,
 | 
						|
@@ -325,8 +375,8 @@ static int sun4i_codec_prepare_capture(s
 | 
						|
 
 | 
						|
 	/* Set RX FIFO trigger level */
 | 
						|
 	regmap_field_update_bits(scodec->reg_adc_fifoc,
 | 
						|
-				 0xf << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL,
 | 
						|
-				 0x7 << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL);
 | 
						|
+				 0xf << scodec->quirks->rx_trig_level,
 | 
						|
+				 0x7 << scodec->quirks->rx_trig_level);
 | 
						|
 
 | 
						|
 	/*
 | 
						|
 	 * FIXME: Undocumented in the datasheet, but
 | 
						|
@@ -360,13 +410,13 @@ static int sun4i_codec_prepare_playback(
 | 
						|
 	u32 val;
 | 
						|
 
 | 
						|
 	/* Flush the TX FIFO */
 | 
						|
-	regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
 | 
						|
-			   BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH));
 | 
						|
+	regmap_field_set_bits(scodec->reg_dac_fifoc,
 | 
						|
+			      BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH));
 | 
						|
 
 | 
						|
 	/* Set TX FIFO Empty Trigger Level */
 | 
						|
-	regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
 | 
						|
-			   0x3f << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL,
 | 
						|
-			   0xf << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL);
 | 
						|
+	regmap_field_update_bits(scodec->reg_dac_fifoc,
 | 
						|
+				 0x3f << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL,
 | 
						|
+				 0xf << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL);
 | 
						|
 
 | 
						|
 	if (substream->runtime->rate > 32000)
 | 
						|
 		/* Use 64 bits FIR filter */
 | 
						|
@@ -375,13 +425,12 @@ static int sun4i_codec_prepare_playback(
 | 
						|
 		/* Use 32 bits FIR filter */
 | 
						|
 		val = BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION);
 | 
						|
 
 | 
						|
-	regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
 | 
						|
-			   BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION),
 | 
						|
-			   val);
 | 
						|
+	regmap_field_update_bits(scodec->reg_dac_fifoc,
 | 
						|
+				 BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION), val);
 | 
						|
 
 | 
						|
 	/* Send zeros when we have an underrun */
 | 
						|
-	regmap_clear_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
 | 
						|
-			   BIT(SUN4I_CODEC_DAC_FIFOC_SEND_LASAT));
 | 
						|
+	regmap_field_clear_bits(scodec->reg_dac_fifoc,
 | 
						|
+				BIT(SUN4I_CODEC_DAC_FIFOC_SEND_LASAT));
 | 
						|
 
 | 
						|
 	return 0;
 | 
						|
 };
 | 
						|
@@ -476,30 +525,32 @@ static int sun4i_codec_hw_params_capture
 | 
						|
 				 7 << SUN4I_CODEC_ADC_FIFOC_ADC_FS,
 | 
						|
 				 hwrate << SUN4I_CODEC_ADC_FIFOC_ADC_FS);
 | 
						|
 
 | 
						|
-	/* Set the number of channels we want to use */
 | 
						|
-	if (params_channels(params) == 1)
 | 
						|
-		regmap_field_set_bits(scodec->reg_adc_fifoc,
 | 
						|
-					 BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN));
 | 
						|
-	else
 | 
						|
-		regmap_field_clear_bits(scodec->reg_adc_fifoc,
 | 
						|
-					 BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN));
 | 
						|
+	if (!scodec->quirks->has_dual_clock) {
 | 
						|
+		/* Set the number of channels we want to use */
 | 
						|
+		if (params_channels(params) == 1)
 | 
						|
+			regmap_field_set_bits(scodec->reg_adc_fifoc,
 | 
						|
+					      BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN));
 | 
						|
+		else
 | 
						|
+			regmap_field_clear_bits(scodec->reg_adc_fifoc,
 | 
						|
+						BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN));
 | 
						|
+	}
 | 
						|
 
 | 
						|
 	/* Set the number of sample bits to either 16 or 24 bits */
 | 
						|
 	if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min == 32) {
 | 
						|
 		regmap_field_set_bits(scodec->reg_adc_fifoc,
 | 
						|
-				   BIT(SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS));
 | 
						|
+				      BIT(scodec->quirks->rx_sample_bits));
 | 
						|
 
 | 
						|
 		regmap_field_clear_bits(scodec->reg_adc_fifoc,
 | 
						|
-				   BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE));
 | 
						|
+					BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE));
 | 
						|
 
 | 
						|
 		scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
 | 
						|
 	} else {
 | 
						|
 		regmap_field_clear_bits(scodec->reg_adc_fifoc,
 | 
						|
-				   BIT(SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS));
 | 
						|
+					BIT(scodec->quirks->rx_sample_bits));
 | 
						|
 
 | 
						|
 		/* Fill most significant bits with valid data MSB */
 | 
						|
 		regmap_field_set_bits(scodec->reg_adc_fifoc,
 | 
						|
-				   BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE));
 | 
						|
+				      BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE));
 | 
						|
 
 | 
						|
 		scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
 | 
						|
 	}
 | 
						|
@@ -514,9 +565,9 @@ static int sun4i_codec_hw_params_playbac
 | 
						|
 	u32 val;
 | 
						|
 
 | 
						|
 	/* Set DAC sample rate */
 | 
						|
-	regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
 | 
						|
-			   7 << SUN4I_CODEC_DAC_FIFOC_DAC_FS,
 | 
						|
-			   hwrate << SUN4I_CODEC_DAC_FIFOC_DAC_FS);
 | 
						|
+	regmap_field_update_bits(scodec->reg_dac_fifoc,
 | 
						|
+				 7 << SUN4I_CODEC_DAC_FIFOC_DAC_FS,
 | 
						|
+				 hwrate << SUN4I_CODEC_DAC_FIFOC_DAC_FS);
 | 
						|
 
 | 
						|
 	/* Set the number of channels we want to use */
 | 
						|
 	if (params_channels(params) == 1)
 | 
						|
@@ -524,27 +575,26 @@ static int sun4i_codec_hw_params_playbac
 | 
						|
 	else
 | 
						|
 		val = 0;
 | 
						|
 
 | 
						|
-	regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
 | 
						|
-			   BIT(SUN4I_CODEC_DAC_FIFOC_MONO_EN),
 | 
						|
-			   val);
 | 
						|
+	regmap_field_update_bits(scodec->reg_dac_fifoc,
 | 
						|
+				 BIT(SUN4I_CODEC_DAC_FIFOC_MONO_EN), val);
 | 
						|
 
 | 
						|
 	/* Set the number of sample bits to either 16 or 24 bits */
 | 
						|
 	if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min == 32) {
 | 
						|
-		regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
 | 
						|
-				   BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS));
 | 
						|
+		regmap_field_set_bits(scodec->reg_dac_fifoc,
 | 
						|
+				      BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS));
 | 
						|
 
 | 
						|
 		/* Set TX FIFO mode to padding the LSBs with 0 */
 | 
						|
-		regmap_clear_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
 | 
						|
-				   BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE));
 | 
						|
+		regmap_field_clear_bits(scodec->reg_dac_fifoc,
 | 
						|
+					BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE));
 | 
						|
 
 | 
						|
 		scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
 | 
						|
 	} else {
 | 
						|
-		regmap_clear_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
 | 
						|
-				   BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS));
 | 
						|
+		regmap_field_clear_bits(scodec->reg_dac_fifoc,
 | 
						|
+					BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS));
 | 
						|
 
 | 
						|
 		/* Set TX FIFO mode to repeat the MSB */
 | 
						|
-		regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
 | 
						|
-				   BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE));
 | 
						|
+		regmap_field_set_bits(scodec->reg_dac_fifoc,
 | 
						|
+				      BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE));
 | 
						|
 
 | 
						|
 		scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
 | 
						|
 	}
 | 
						|
@@ -565,7 +615,11 @@ static int sun4i_codec_hw_params(struct
 | 
						|
 	if (!clk_freq)
 | 
						|
 		return -EINVAL;
 | 
						|
 
 | 
						|
-	ret = clk_set_rate(scodec->clk_module, clk_freq);
 | 
						|
+	if (scodec->clk_module_dac &&
 | 
						|
+	    substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 | 
						|
+		ret = clk_set_rate(scodec->clk_module_dac, clk_freq);
 | 
						|
+	else
 | 
						|
+		ret = clk_set_rate(scodec->clk_module, clk_freq);
 | 
						|
 	if (ret)
 | 
						|
 		return ret;
 | 
						|
 
 | 
						|
@@ -607,10 +661,14 @@ static int sun4i_codec_startup(struct sn
 | 
						|
 	 * Stop issuing DRQ when we have room for less than 16 samples
 | 
						|
 	 * in our TX FIFO
 | 
						|
 	 */
 | 
						|
-	regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
 | 
						|
-			   3 << SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT);
 | 
						|
+	regmap_field_set_bits(scodec->reg_dac_fifoc,
 | 
						|
+			      3 << SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT);
 | 
						|
 
 | 
						|
-	return clk_prepare_enable(scodec->clk_module);
 | 
						|
+	if (scodec->clk_module_dac &&
 | 
						|
+	    substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 | 
						|
+		return clk_prepare_enable(scodec->clk_module_dac);
 | 
						|
+	else
 | 
						|
+		return clk_prepare_enable(scodec->clk_module);
 | 
						|
 }
 | 
						|
 
 | 
						|
 static void sun4i_codec_shutdown(struct snd_pcm_substream *substream,
 | 
						|
@@ -619,7 +677,11 @@ static void sun4i_codec_shutdown(struct
 | 
						|
 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
 | 
						|
 	struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
 | 
						|
 
 | 
						|
-	clk_disable_unprepare(scodec->clk_module);
 | 
						|
+	if (scodec->clk_module_dac &&
 | 
						|
+	    substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 | 
						|
+		clk_disable_unprepare(scodec->clk_module_dac);
 | 
						|
+	else
 | 
						|
+		clk_disable_unprepare(scodec->clk_module);
 | 
						|
 }
 | 
						|
 
 | 
						|
 static const struct snd_soc_dai_ops sun4i_codec_dai_ops = {
 | 
						|
@@ -1229,6 +1291,55 @@ static const struct snd_soc_component_dr
 | 
						|
 	.endianness		= 1,
 | 
						|
 };
 | 
						|
 
 | 
						|
+/* sun20i D1 codec */
 | 
						|
+static const DECLARE_TLV_DB_SCALE(sun20i_d1_codec_dvol_scale, -12000, 75, 1);
 | 
						|
+
 | 
						|
+static const struct snd_kcontrol_new sun20i_d1_codec_codec_controls[] = {
 | 
						|
+	SOC_SINGLE_TLV("DAC Playback Volume", SUN4I_CODEC_DAC_DPC,
 | 
						|
+		       SUN4I_CODEC_DAC_DPC_DVOL, 0x3f, 1,
 | 
						|
+		       sun6i_codec_dvol_scale),
 | 
						|
+	SOC_DOUBLE_TLV("DAC Front Playback Volume", SUN20I_D1_CODEC_DAC_VOL_CTRL,
 | 
						|
+		       SUN20I_D1_CODEC_DAC_VOL_L, SUN20I_D1_CODEC_DAC_VOL_R,
 | 
						|
+		       0xFF, 0, sun20i_d1_codec_dvol_scale),
 | 
						|
+
 | 
						|
+	SOC_SINGLE_TLV("ADC1 Capture Volume", SUN20I_D1_CODEC_ADC_VOL_CTRL1,
 | 
						|
+		       SUN20I_D1_CODEC_ADC_VOL_CTRL1_ADC1_VOL, 0xff, 0,
 | 
						|
+		       sun20i_d1_codec_dvol_scale),
 | 
						|
+	SOC_SINGLE_TLV("ADC2 Capture Volume", SUN20I_D1_CODEC_ADC_VOL_CTRL1,
 | 
						|
+		       SUN20I_D1_CODEC_ADC_VOL_CTRL1_ADC2_VOL, 0xff, 0,
 | 
						|
+		       sun20i_d1_codec_dvol_scale),
 | 
						|
+	SOC_SINGLE_TLV("ADC3 Capture Volume", SUN20I_D1_CODEC_ADC_VOL_CTRL1,
 | 
						|
+		       SUN20I_D1_CODEC_ADC_VOL_CTRL1_ADC3_VOL, 0xff, 0,
 | 
						|
+		       sun20i_d1_codec_dvol_scale),
 | 
						|
+};
 | 
						|
+
 | 
						|
+static const struct snd_soc_dapm_widget sun20i_d1_codec_codec_widgets[] = {
 | 
						|
+	/* Digital parts of the ADCs */
 | 
						|
+	SND_SOC_DAPM_SUPPLY("ADC Enable", SUN20I_D1_CODEC_ADC_FIFOC,
 | 
						|
+			    SUN20I_D1_CODEC_ADC_FIFOC_EN_AD, 0, NULL, 0),
 | 
						|
+	SND_SOC_DAPM_SUPPLY("ADC1 CH Enable", SUN20I_D1_CODEC_ADC_DIG_CTRL,
 | 
						|
+			    SUN20I_D1_CODEC_ADC_DIG_CTRL_ADC1_CH_EN, 0, NULL, 0),
 | 
						|
+	SND_SOC_DAPM_SUPPLY("ADC2 CH Enable", SUN20I_D1_CODEC_ADC_DIG_CTRL,
 | 
						|
+			    SUN20I_D1_CODEC_ADC_DIG_CTRL_ADC2_CH_EN, 0, NULL, 0),
 | 
						|
+	SND_SOC_DAPM_SUPPLY("ADC3 CH Enable", SUN20I_D1_CODEC_ADC_DIG_CTRL,
 | 
						|
+			    SUN20I_D1_CODEC_ADC_DIG_CTRL_ADC3_CH_EN, 0, NULL, 0),
 | 
						|
+	/* Digital parts of the DACs */
 | 
						|
+	SND_SOC_DAPM_SUPPLY("DAC Enable", SUN4I_CODEC_DAC_DPC,
 | 
						|
+			    SUN4I_CODEC_DAC_DPC_EN_DA, 0, NULL, 0),
 | 
						|
+	SND_SOC_DAPM_SUPPLY("DAC VOL_SEL Enable", SUN20I_D1_CODEC_DAC_VOL_CTRL,
 | 
						|
+			    SUN20I_D1_CODEC_DAC_VOL_SEL, 0, NULL, 0),
 | 
						|
+};
 | 
						|
+
 | 
						|
+static const struct snd_soc_component_driver sun20i_d1_codec_codec = {
 | 
						|
+	.controls		= sun20i_d1_codec_codec_controls,
 | 
						|
+	.num_controls		= ARRAY_SIZE(sun20i_d1_codec_codec_controls),
 | 
						|
+	.dapm_widgets		= sun20i_d1_codec_codec_widgets,
 | 
						|
+	.num_dapm_widgets	= ARRAY_SIZE(sun20i_d1_codec_codec_widgets),
 | 
						|
+	.idle_bias_on		= 1,
 | 
						|
+	.use_pmdown_time	= 1,
 | 
						|
+	.endianness		= 1,
 | 
						|
+};
 | 
						|
+
 | 
						|
 static const struct snd_soc_component_driver sun4i_codec_component = {
 | 
						|
 	.name			= "sun4i-codec",
 | 
						|
 	.legacy_dai_naming	= 1,
 | 
						|
@@ -1532,6 +1643,66 @@ static struct snd_soc_card *sun8i_v3s_co
 | 
						|
 	return card;
 | 
						|
 };
 | 
						|
 
 | 
						|
+static const struct snd_soc_dapm_route sun20i_d1_codec_card_routes[] = {
 | 
						|
+	/* ADC Routes */
 | 
						|
+	{ "ADC1", NULL, "ADC Enable" },
 | 
						|
+	{ "ADC2", NULL, "ADC Enable" },
 | 
						|
+	{ "ADC3", NULL, "ADC Enable" },
 | 
						|
+	{ "ADC1", NULL, "ADC1 CH Enable" },
 | 
						|
+	{ "ADC2", NULL, "ADC2 CH Enable" },
 | 
						|
+	{ "ADC3", NULL, "ADC3 CH Enable" },
 | 
						|
+	{ "Codec Capture", NULL, "ADC1" },
 | 
						|
+	{ "Codec Capture", NULL, "ADC2" },
 | 
						|
+	{ "Codec Capture", NULL, "ADC3" },
 | 
						|
+
 | 
						|
+	/* DAC Routes */
 | 
						|
+	{ "Left DAC", NULL, "DAC Enable" },
 | 
						|
+	{ "Right DAC", NULL, "DAC Enable" },
 | 
						|
+	{ "Left DAC", NULL, "DAC VOL_SEL Enable" },
 | 
						|
+	{ "Right DAC", NULL, "DAC VOL_SEL Enable" },
 | 
						|
+	{ "Left DAC", NULL, "Codec Playback" },
 | 
						|
+	{ "Right DAC", NULL, "Codec Playback" },
 | 
						|
+};
 | 
						|
+
 | 
						|
+static struct snd_soc_card *sun20i_d1_codec_create_card(struct device *dev)
 | 
						|
+{
 | 
						|
+	struct snd_soc_card *card;
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
 | 
						|
+	if (!card)
 | 
						|
+		return ERR_PTR(-ENOMEM);
 | 
						|
+
 | 
						|
+	aux_dev.dlc.of_node = of_parse_phandle(dev->of_node,
 | 
						|
+					       "allwinner,codec-analog-controls",
 | 
						|
+					       0);
 | 
						|
+	if (!aux_dev.dlc.of_node) {
 | 
						|
+		dev_err(dev, "Can't find analog controls for codec.\n");
 | 
						|
+		return ERR_PTR(-EINVAL);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
 | 
						|
+	if (!card->dai_link)
 | 
						|
+		return ERR_PTR(-ENOMEM);
 | 
						|
+
 | 
						|
+	card->dev		= dev;
 | 
						|
+	card->owner		= THIS_MODULE;
 | 
						|
+	card->name		= "D1 Audio Codec";
 | 
						|
+	card->dapm_widgets	= sun6i_codec_card_dapm_widgets;
 | 
						|
+	card->num_dapm_widgets	= ARRAY_SIZE(sun6i_codec_card_dapm_widgets);
 | 
						|
+	card->dapm_routes	= sun20i_d1_codec_card_routes;
 | 
						|
+	card->num_dapm_routes	= ARRAY_SIZE(sun20i_d1_codec_card_routes);
 | 
						|
+	card->aux_dev		= &aux_dev;
 | 
						|
+	card->num_aux_devs	= 1;
 | 
						|
+	card->fully_routed	= true;
 | 
						|
+
 | 
						|
+	ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing");
 | 
						|
+	if (ret)
 | 
						|
+		dev_warn(dev, "failed to parse audio-routing: %d\n", ret);
 | 
						|
+
 | 
						|
+	return card;
 | 
						|
+};
 | 
						|
+
 | 
						|
 static const struct regmap_config sun4i_codec_regmap_config = {
 | 
						|
 	.reg_bits	= 32,
 | 
						|
 	.reg_stride	= 4,
 | 
						|
@@ -1574,21 +1745,22 @@ static const struct regmap_config sun8i_
 | 
						|
 	.max_register	= SUN8I_H3_CODEC_ADC_DBG,
 | 
						|
 };
 | 
						|
 
 | 
						|
-struct sun4i_codec_quirks {
 | 
						|
-	const struct regmap_config *regmap_config;
 | 
						|
-	const struct snd_soc_component_driver *codec;
 | 
						|
-	struct snd_soc_card * (*create_card)(struct device *dev);
 | 
						|
-	struct reg_field reg_adc_fifoc;	/* used for regmap_field */
 | 
						|
-	unsigned int reg_dac_txdata;	/* TX FIFO offset for DMA config */
 | 
						|
-	unsigned int reg_adc_rxdata;	/* RX FIFO offset for DMA config */
 | 
						|
-	bool has_reset;
 | 
						|
+static const struct regmap_config sun20i_d1_codec_regmap_config = {
 | 
						|
+	.reg_bits	= 32,
 | 
						|
+	.reg_stride	= 4,
 | 
						|
+	.val_bits	= 32,
 | 
						|
+	.max_register	= SUN20I_D1_CODEC_VRA1SPEEDUP_DOWN_CTRL,
 | 
						|
 };
 | 
						|
 
 | 
						|
 static const struct sun4i_codec_quirks sun4i_codec_quirks = {
 | 
						|
 	.regmap_config	= &sun4i_codec_regmap_config,
 | 
						|
 	.codec		= &sun4i_codec_codec,
 | 
						|
 	.create_card	= sun4i_codec_create_card,
 | 
						|
+	.reg_dac_fifoc	= REG_FIELD(SUN4I_CODEC_DAC_FIFOC, 0, 31),
 | 
						|
 	.reg_adc_fifoc	= REG_FIELD(SUN4I_CODEC_ADC_FIFOC, 0, 31),
 | 
						|
+	.adc_drq_en	= SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN,
 | 
						|
+	.rx_sample_bits	= SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS,
 | 
						|
+	.rx_trig_level	= SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL,
 | 
						|
 	.reg_dac_txdata	= SUN4I_CODEC_DAC_TXDATA,
 | 
						|
 	.reg_adc_rxdata	= SUN4I_CODEC_ADC_RXDATA,
 | 
						|
 };
 | 
						|
@@ -1597,7 +1769,11 @@ static const struct sun4i_codec_quirks s
 | 
						|
 	.regmap_config	= &sun6i_codec_regmap_config,
 | 
						|
 	.codec		= &sun6i_codec_codec,
 | 
						|
 	.create_card	= sun6i_codec_create_card,
 | 
						|
+	.reg_dac_fifoc	= REG_FIELD(SUN4I_CODEC_DAC_FIFOC, 0, 31),
 | 
						|
 	.reg_adc_fifoc	= REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31),
 | 
						|
+	.adc_drq_en	= SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN,
 | 
						|
+	.rx_sample_bits	= SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS,
 | 
						|
+	.rx_trig_level	= SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL,
 | 
						|
 	.reg_dac_txdata	= SUN4I_CODEC_DAC_TXDATA,
 | 
						|
 	.reg_adc_rxdata	= SUN6I_CODEC_ADC_RXDATA,
 | 
						|
 	.has_reset	= true,
 | 
						|
@@ -1607,7 +1783,11 @@ static const struct sun4i_codec_quirks s
 | 
						|
 	.regmap_config	= &sun7i_codec_regmap_config,
 | 
						|
 	.codec		= &sun7i_codec_codec,
 | 
						|
 	.create_card	= sun4i_codec_create_card,
 | 
						|
+	.reg_dac_fifoc	= REG_FIELD(SUN4I_CODEC_DAC_FIFOC, 0, 31),
 | 
						|
 	.reg_adc_fifoc	= REG_FIELD(SUN4I_CODEC_ADC_FIFOC, 0, 31),
 | 
						|
+	.adc_drq_en	= SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN,
 | 
						|
+	.rx_sample_bits	= SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS,
 | 
						|
+	.rx_trig_level	= SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL,
 | 
						|
 	.reg_dac_txdata	= SUN4I_CODEC_DAC_TXDATA,
 | 
						|
 	.reg_adc_rxdata	= SUN4I_CODEC_ADC_RXDATA,
 | 
						|
 };
 | 
						|
@@ -1616,7 +1796,11 @@ static const struct sun4i_codec_quirks s
 | 
						|
 	.regmap_config	= &sun8i_a23_codec_regmap_config,
 | 
						|
 	.codec		= &sun8i_a23_codec_codec,
 | 
						|
 	.create_card	= sun8i_a23_codec_create_card,
 | 
						|
+	.reg_dac_fifoc	= REG_FIELD(SUN4I_CODEC_DAC_FIFOC, 0, 31),
 | 
						|
 	.reg_adc_fifoc	= REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31),
 | 
						|
+	.adc_drq_en	= SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN,
 | 
						|
+	.rx_sample_bits	= SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS,
 | 
						|
+	.rx_trig_level	= SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL,
 | 
						|
 	.reg_dac_txdata	= SUN4I_CODEC_DAC_TXDATA,
 | 
						|
 	.reg_adc_rxdata	= SUN6I_CODEC_ADC_RXDATA,
 | 
						|
 	.has_reset	= true,
 | 
						|
@@ -1631,7 +1815,11 @@ static const struct sun4i_codec_quirks s
 | 
						|
 	 */
 | 
						|
 	.codec		= &sun8i_a23_codec_codec,
 | 
						|
 	.create_card	= sun8i_h3_codec_create_card,
 | 
						|
+	.reg_dac_fifoc	= REG_FIELD(SUN4I_CODEC_DAC_FIFOC, 0, 31),
 | 
						|
 	.reg_adc_fifoc	= REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31),
 | 
						|
+	.adc_drq_en	= SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN,
 | 
						|
+	.rx_sample_bits	= SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS,
 | 
						|
+	.rx_trig_level	= SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL,
 | 
						|
 	.reg_dac_txdata	= SUN8I_H3_CODEC_DAC_TXDATA,
 | 
						|
 	.reg_adc_rxdata	= SUN6I_CODEC_ADC_RXDATA,
 | 
						|
 	.has_reset	= true,
 | 
						|
@@ -1645,12 +1833,31 @@ static const struct sun4i_codec_quirks s
 | 
						|
 	 */
 | 
						|
 	.codec		= &sun8i_a23_codec_codec,
 | 
						|
 	.create_card	= sun8i_v3s_codec_create_card,
 | 
						|
+	.reg_dac_fifoc	= REG_FIELD(SUN4I_CODEC_DAC_FIFOC, 0, 31),
 | 
						|
 	.reg_adc_fifoc	= REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31),
 | 
						|
+	.adc_drq_en	= SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN,
 | 
						|
+	.rx_sample_bits	= SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS,
 | 
						|
+	.rx_trig_level	= SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL,
 | 
						|
 	.reg_dac_txdata	= SUN8I_H3_CODEC_DAC_TXDATA,
 | 
						|
 	.reg_adc_rxdata	= SUN6I_CODEC_ADC_RXDATA,
 | 
						|
 	.has_reset	= true,
 | 
						|
 };
 | 
						|
 
 | 
						|
+static const struct sun4i_codec_quirks sun20i_d1_codec_quirks = {
 | 
						|
+	.regmap_config	= &sun20i_d1_codec_regmap_config,
 | 
						|
+	.codec		= &sun20i_d1_codec_codec,
 | 
						|
+	.create_card	= sun20i_d1_codec_create_card,
 | 
						|
+	.reg_dac_fifoc	= REG_FIELD(SUN20I_D1_CODEC_DAC_FIFOC, 0, 31),
 | 
						|
+	.reg_adc_fifoc	= REG_FIELD(SUN20I_D1_CODEC_ADC_FIFOC, 0, 31),
 | 
						|
+	.adc_drq_en	= SUN20I_D1_CODEC_ADC_FIFOC_ADC_DRQ_EN,
 | 
						|
+	.rx_sample_bits	= SUN20I_D1_CODEC_ADC_FIFOC_RX_SAMPLE_BITS,
 | 
						|
+	.rx_trig_level	= SUN20I_D1_CODEC_ADC_FIFOC_RX_TRIG_LEVEL,
 | 
						|
+	.reg_dac_txdata	= SUN8I_H3_CODEC_DAC_TXDATA,
 | 
						|
+	.reg_adc_rxdata	= SUN20I_D1_CODEC_ADC_RXDATA,
 | 
						|
+	.has_reset	= true,
 | 
						|
+	.has_dual_clock = true,
 | 
						|
+};
 | 
						|
+
 | 
						|
 static const struct of_device_id sun4i_codec_of_match[] = {
 | 
						|
 	{
 | 
						|
 		.compatible = "allwinner,sun4i-a10-codec",
 | 
						|
@@ -1676,6 +1883,10 @@ static const struct of_device_id sun4i_c
 | 
						|
 		.compatible = "allwinner,sun8i-v3s-codec",
 | 
						|
 		.data = &sun8i_v3s_codec_quirks,
 | 
						|
 	},
 | 
						|
+	{
 | 
						|
+		.compatible = "allwinner,sun20i-d1-codec",
 | 
						|
+		.data = &sun20i_d1_codec_quirks,
 | 
						|
+	},
 | 
						|
 	{}
 | 
						|
 };
 | 
						|
 MODULE_DEVICE_TABLE(of, sun4i_codec_of_match);
 | 
						|
@@ -1704,6 +1915,7 @@ static int sun4i_codec_probe(struct plat
 | 
						|
 		dev_err(&pdev->dev, "Failed to determine the quirks to use\n");
 | 
						|
 		return -ENODEV;
 | 
						|
 	}
 | 
						|
+	scodec->quirks = quirks;
 | 
						|
 
 | 
						|
 	scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base,
 | 
						|
 					       quirks->regmap_config);
 | 
						|
@@ -1719,10 +1931,24 @@ static int sun4i_codec_probe(struct plat
 | 
						|
 		return PTR_ERR(scodec->clk_apb);
 | 
						|
 	}
 | 
						|
 
 | 
						|
-	scodec->clk_module = devm_clk_get(&pdev->dev, "codec");
 | 
						|
-	if (IS_ERR(scodec->clk_module)) {
 | 
						|
-		dev_err(&pdev->dev, "Failed to get the module clock\n");
 | 
						|
-		return PTR_ERR(scodec->clk_module);
 | 
						|
+	if (quirks->has_dual_clock) {
 | 
						|
+		scodec->clk_module = devm_clk_get(&pdev->dev, "adc");
 | 
						|
+		if (IS_ERR(scodec->clk_module)) {
 | 
						|
+			dev_err(&pdev->dev, "Failed to get the ADC module clock\n");
 | 
						|
+			return PTR_ERR(scodec->clk_module);
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		scodec->clk_module_dac = devm_clk_get(&pdev->dev, "dac");
 | 
						|
+		if (IS_ERR(scodec->clk_module_dac)) {
 | 
						|
+			dev_err(&pdev->dev, "Failed to get the DAC module clock\n");
 | 
						|
+			return PTR_ERR(scodec->clk_module_dac);
 | 
						|
+		}
 | 
						|
+	} else {
 | 
						|
+		scodec->clk_module = devm_clk_get(&pdev->dev, "codec");
 | 
						|
+		if (IS_ERR(scodec->clk_module)) {
 | 
						|
+			dev_err(&pdev->dev, "Failed to get the module clock\n");
 | 
						|
+			return PTR_ERR(scodec->clk_module);
 | 
						|
+		}
 | 
						|
 	}
 | 
						|
 
 | 
						|
 	if (quirks->has_reset) {
 | 
						|
@@ -1751,6 +1977,16 @@ static int sun4i_codec_probe(struct plat
 | 
						|
 		dev_err(&pdev->dev, "Failed to create regmap fields: %d\n",
 | 
						|
 			ret);
 | 
						|
 		return ret;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	scodec->reg_dac_fifoc = devm_regmap_field_alloc(&pdev->dev,
 | 
						|
+							scodec->regmap,
 | 
						|
+							quirks->reg_dac_fifoc);
 | 
						|
+	if (IS_ERR(scodec->reg_dac_fifoc)) {
 | 
						|
+		ret = PTR_ERR(scodec->reg_dac_fifoc);
 | 
						|
+		dev_err(&pdev->dev, "Failed to create regmap fields: %d\n",
 | 
						|
+			ret);
 | 
						|
+		return ret;
 | 
						|
 	}
 | 
						|
 
 | 
						|
 	/* Enable the bus clock */
 |