Subject: [PATCH 05/12] ASoC: sun4i-codec: Add support for A31 playback through headphone output

From: [email-masked] (Maxime Ripard)
Date: Mon, 3 Oct 2016 13:47:09 +0200

Hi,

On Mon, Oct 03, 2016 at 07:07:57PM +0800, Chen-Yu Tsai wrote:
> The A31 has a similar codec to the A10/A20. The PCM parts are very
> similar, with just different register offsets. The analog paths are
> very different. There are more inputs and outputs.
> 
> The quirks structure is expanded to include different register offsets
> and separate callbacks for creating the ASoC card. Also the DMA burst
> length is increased to 8. While the A10 DMA engine supports bursts of
> 1, 4 and 8, the A31 engine only supports 1 and 8.
> 
> This patch adds support for the basic playback path of the A31 codec,
> from the DAC to the headphones. Headphone detection, microphone,
> signaling, other inputs/outputs and capture will be added later.
> 
> Signed-off-by: Chen-Yu Tsai <[email-masked]>
> ---
>  .../devicetree/bindings/sound/sun4i-codec.txt      |   6 +-
>  sound/soc/sunxi/sun4i-codec.c                      | 396 +++++++++++++++++----
>  2 files changed, 334 insertions(+), 68 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/sound/sun4i-codec.txt b/Documentation/devicetree/bindings/sound/sun4i-codec.txt
> index 0dce690f78f5..1d2411cea98d 100644
> --- a/Documentation/devicetree/bindings/sound/sun4i-codec.txt
> +++ b/Documentation/devicetree/bindings/sound/sun4i-codec.txt
> @@ -1,8 +1,10 @@
>  * Allwinner A10 Codec
>  
>  Required properties:
> -- compatible: must be either "allwinner,sun4i-a10-codec" or
> -  "allwinner,sun7i-a20-codec"
> +- compatible: must be one of the following compatibles:
> +		- "allwinner,sun4i-a10-codec"
> +		- "allwinner,sun6i-a31-codec"
> +		- "allwinner,sun7i-a20-codec"

I'm guessing it needs a reset line?

>  - reg: must contain the registers location and length
>  - interrupts: must contain the codec interrupt
>  - dmas: DMA channels for tx and rx dma. See the DMA client binding,
> diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c
> index e047ec06d538..9916714ecb71 100644
> --- a/sound/soc/sunxi/sun4i-codec.c
> +++ b/sound/soc/sunxi/sun4i-codec.c
> @@ -3,6 +3,7 @@
>   * Copyright 2014 Jon Smirl <[email-masked]>
>   * Copyright 2015 Maxime Ripard <[email-masked]>
>   * Copyright 2015 Adam Sampson <[email-masked]>
> + * Copyright 2016 Chen-Yu Tsai <[email-masked]>
>   *
>   * Based on the Allwinner SDK driver, released under the GPL.
>   *
> @@ -24,8 +25,9 @@
>  #include <linux/delay.h>
>  #include <linux/slab.h>
>  #include <linux/of.h>
> -#include <linux/of_platform.h>
>  #include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/of_platform.h>
>  #include <linux/clk.h>
>  #include <linux/regmap.h>
>  #include <linux/gpio/consumer.h>
> @@ -55,6 +57,8 @@
>  #define SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH		(0)
>  #define SUN4I_CODEC_DAC_FIFOS			(0x08)
>  #define SUN4I_CODEC_DAC_TXDATA			(0x0c)
> +
> +/* Codec DAC side analog signal controls */
>  #define SUN4I_CODEC_DAC_ACTL			(0x10)
>  #define SUN4I_CODEC_DAC_ACTL_DACAENR			(31)
>  #define SUN4I_CODEC_DAC_ACTL_DACAENL			(30)
> @@ -81,6 +85,8 @@
>  #define SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH		(0)
>  #define SUN4I_CODEC_ADC_FIFOS			(0x20)
>  #define SUN4I_CODEC_ADC_RXDATA			(0x24)
> +
> +/* Codec ADC side analog signal controls */
>  #define SUN4I_CODEC_ADC_ACTL			(0x28)
>  #define SUN4I_CODEC_ADC_ACTL_ADC_R_EN			(31)
>  #define SUN4I_CODEC_ADC_ACTL_ADC_L_EN			(30)
> @@ -93,18 +99,106 @@
>  #define SUN4I_CODEC_ADC_ACTL_DDE			(3)
>  #define SUN4I_CODEC_ADC_DEBUG			(0x2c)
>  
> -/* Other various ADC registers */
> +/* FIFO counters */
>  #define SUN4I_CODEC_DAC_TXCNT			(0x30)
>  #define SUN4I_CODEC_ADC_RXCNT			(0x34)
> +
> +/* Other various ADC registers */
>  #define SUN7I_CODEC_AC_DAC_CAL			(0x38)
>  #define SUN7I_CODEC_AC_MIC_PHONE_CAL		(0x3c)
>  
> +/*** sun6i specific register offsets ***/
> +#define SUN6I_CODEC_ADC_FIFOC			(0x10)
> +#define SUN6I_CODEC_ADC_FIFOC_EN_AD			(28)
> +#define SUN6I_CODEC_ADC_FIFOS			(0x14)
> +#define SUN6I_CODEC_ADC_RXDATA			(0x18)
> +#define SUN6I_CODEC_OM_DACA_CTRL		(0x20)
> +#define SUN6I_CODEC_OM_DACA_CTRL_DACAREN		(31)
> +#define SUN6I_CODEC_OM_DACA_CTRL_DACALEN		(30)
> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIXEN			(29)
> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIXEN			(28)
> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC1		(23)
> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC2		(22)
> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_PHONE		(21)
> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_PHONEP		(20)
> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_LINEINR		(19)
> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACR		(18)
> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACL		(17)
> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC1		(16)
> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC2		(15)
> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_PHONE		(14)
> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_PHONEN		(13)
> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_LINEINL		(12)
> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACL		(11)
> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACR		(10)
> +#define SUN6I_CODEC_OM_DACA_CTRL_RHPIS			(9)
> +#define SUN6I_CODEC_OM_DACA_CTRL_LHPIS			(8)
> +#define SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE		(7)
> +#define SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE		(6)
> +#define SUN6I_CODEC_OM_DACA_CTRL_HPVOL			(0)
> +#define SUN6I_CODEC_OM_PA_CTRL			(0x24)
> +#define SUN6I_CODEC_OM_PA_CTRL_HPPAEN			(31)
> +#define SUN6I_CODEC_OM_PA_CTRL_MIC1G			(15)
> +#define SUN6I_CODEC_OM_PA_CTRL_MIC2G			(12)
> +#define SUN6I_CODEC_OM_PA_CTRL_LINEING			(9)
> +#define SUN6I_CODEC_OM_PA_CTRL_PHONEG			(6)
> +#define SUN6I_CODEC_OM_PA_CTRL_PHONEPG			(3)
> +#define SUN6I_CODEC_OM_PA_CTRL_PHONENG			(0)
> +#define SUN6I_CODEC_MIC_CTRL			(0x28)
> +#define SUN6I_CODEC_MIC_CTRL_HBIASEN			(31)
> +#define SUN6I_CODEC_MIC_CTRL_MBIASEN			(30)
> +#define SUN6I_CODEC_MIC_CTRL_MIC1AMPEN			(28)
> +#define SUN6I_CODEC_MIC_CTRL_MIC1BOOST			(25)
> +#define SUN6I_CODEC_MIC_CTRL_MIC2AMPEN			(24)
> +#define SUN6I_CODEC_MIC_CTRL_MIC2BOOST			(21)
> +#define SUN6I_CODEC_MIC_CTRL_MIC2SLT			(20)
> +#define SUN6I_CODEC_MIC_CTRL_LINEOUTLEN			(19)
> +#define SUN6I_CODEC_MIC_CTRL_LINEOUTREN			(18)
> +#define SUN6I_CODEC_MIC_CTRL_LINEOUTLSRC		(17)
> +#define SUN6I_CODEC_MIC_CTRL_LINEOUTRSRC		(16)
> +#define SUN6I_CODEC_MIC_CTRL_LINEOUTVC			(11)
> +#define SUN6I_CODEC_MIC_CTRL_PHONEPREG			(8)
> +#define SUN6I_CODEC_ADC_ACTL			(0x2c)
> +#define SUN6I_CODEC_ADC_ACTL_ADCREN			(31)
> +#define SUN6I_CODEC_ADC_ACTL_ADCLEN			(30)
> +#define SUN6I_CODEC_ADC_ACTL_ADCRG			(27)
> +#define SUN6I_CODEC_ADC_ACTL_ADCLG			(24)
> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC1		(13)
> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC2		(12)
> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_PHONE		(11)
> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_PHONEP		(10)
> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_LINEINR		(9)
> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXR		(8)
> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXL		(7)
> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC1		(6)
> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC2		(5)
> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_PHONE		(4)
> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_PHONEN		(3)
> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_LINEINL		(2)
> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXL		(1)
> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXR		(0)
> +#define SUN6I_CODEC_ADDA_TUNE			(0x30)
> +#define SUN6I_CODEC_CALIBRATION			(0x34)
> +#define SUN6I_CODEC_DAC_TXCNT			(0x40)
> +#define SUN6I_CODEC_ADC_RXCNT			(0x44)
> +#define SUN6I_CODEC_HMIC_CTL			(0x50)
> +#define SUN6I_CODEC_HMIC_DATA			(0x54)
> +
> +/* TODO sun6i DAP (Digital Audio Processing) bits */
> +
> +struct sun4i_codec_regs {
> +	u32	adc_fifoc;
> +	u32	adc_fifos;
> +	u32	adc_rxdata;
> +};
> +
>  struct sun4i_codec {
>  	struct device	*dev;
>  	struct regmap	*regmap;
>  	struct clk	*clk_apb;
>  	struct clk	*clk_module;
>  	struct gpio_desc *gpio_pa;
> +	const struct sun4i_codec_regs *regs;

You're reimplementing reg_field here.

>  
>  	struct snd_dmaengine_dai_dma_data	capture_dma_data;
>  	struct snd_dmaengine_dai_dma_data	playback_dma_data;
> @@ -134,7 +228,7 @@ static void sun4i_codec_stop_playback(struct sun4i_codec *scodec)
>  static void sun4i_codec_start_capture(struct sun4i_codec *scodec)
>  {
>  	/* Enable ADC DRQ */
> -	regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
> +	regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
>  			   BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN),
>  			   BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN));
>  }
> @@ -142,7 +236,7 @@ static void sun4i_codec_start_capture(struct sun4i_codec *scodec)
>  static void sun4i_codec_stop_capture(struct sun4i_codec *scodec)
>  {
>  	/* Disable ADC DRQ */
> -	regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
> +	regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
>  			   BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN), 0);
>  }
>  
> @@ -186,13 +280,13 @@ static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream,
>  
>  
>  	/* Flush RX FIFO */
> -	regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
> +	regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
>  			   BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH),
>  			   BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH));
>  
>  
>  	/* Set RX FIFO trigger level */
> -	regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
> +	regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
>  			   0xf << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL,
>  			   0x7 << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL);
>  
> @@ -201,9 +295,12 @@ static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream,
>  	 *        Allwinner's code mentions that it is related
>  	 *        related to microphone gain
>  	 */
> -	regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_ACTL,
> -			   0x3 << 25,
> -			   0x1 << 25);
> +	if (!of_device_is_compatible(scodec->dev->of_node,
> +				     "allwinner,sun6i-a31-codec")) {
> +		regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_ACTL,
> +				   0x3 << 25,
> +				   0x1 << 25);
> +	}
>  
>  	if (of_device_is_compatible(scodec->dev->of_node,
>  				    "allwinner,sun7i-a20-codec"))
> @@ -213,7 +310,7 @@ static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream,
>  				   0x1 << 8);
>  
>  	/* Fill most significant bits with valid data MSB */
> -	regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
> +	regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
>  			   BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE),
>  			   BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE));
>  
> @@ -342,17 +439,17 @@ static int sun4i_codec_hw_params_capture(struct sun4i_codec *scodec,
>  					 unsigned int hwrate)
>  {
>  	/* Set ADC sample rate */
> -	regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
> +	regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
>  			   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_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
> +		regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
>  				   BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN),
>  				   BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN));
>  	else
> -		regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
> +		regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
>  				   BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN), 0);
>  
>  	return 0;
> @@ -385,7 +482,7 @@ static int sun4i_codec_hw_params_playback(struct sun4i_codec *scodec,
>  				   BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS),
>  				   BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS));
>  
> -		/* Set TX FIFO mode to padding the LSBs with 0 */
> +		/* Use higher 24 bits of TX FIFO */
>  		regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
>  				   BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE),
>  				   0);
> @@ -396,7 +493,7 @@ static int sun4i_codec_hw_params_playback(struct sun4i_codec *scodec,
>  				   BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS),
>  				   0);
>  
> -		/* Set TX FIFO mode to repeat the MSB */
> +		/* Use lower 16 bits of TX FIFO */
>  		regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
>  				   BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE),
>  				   BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE));
> @@ -502,7 +599,7 @@ static struct snd_soc_dai_driver sun4i_codec_dai = {
>  	},
>  };
>  
> -/*** Codec ***/
> +/*** sun4i Codec ***/
>  static const struct snd_kcontrol_new sun4i_codec_pa_mute =
>  	SOC_DAPM_SINGLE("Switch", SUN4I_CODEC_DAC_ACTL,
>  			SUN4I_CODEC_DAC_ACTL_PA_MUTE, 1, 0);
> @@ -638,6 +735,114 @@ static struct snd_soc_codec_driver sun4i_codec_codec = {
>  	},
>  };
>  
> +/*** sun6i Codec ***/
> +
> +/* mixer controls */
> +static const struct snd_kcontrol_new sun6i_codec_mixer_controls[] = {
> +	SOC_DAPM_DOUBLE("DAC Playback Switch",
> +			SUN6I_CODEC_OM_DACA_CTRL,
> +			SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACL,
> +			SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACR, 1, 0),
> +	SOC_DAPM_DOUBLE("DAC Reversed Playback Switch",
> +			SUN6I_CODEC_OM_DACA_CTRL,
> +			SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACR,
> +			SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACL, 1, 0),
> +};
> +
> +/* headphone controls */
> +static const char * const sun6i_codec_hp_src_enum_text[] = {
> +	"DAC", "Mixer",
> +};
> +
> +static SOC_ENUM_DOUBLE_DECL(sun6i_codec_hp_src_enum,
> +			    SUN6I_CODEC_OM_DACA_CTRL,
> +			    SUN6I_CODEC_OM_DACA_CTRL_LHPIS,
> +			    SUN6I_CODEC_OM_DACA_CTRL_RHPIS,
> +			    sun6i_codec_hp_src_enum_text);
> +
> +static const struct snd_kcontrol_new sun6i_codec_hp_src[] = {
> +	SOC_DAPM_ENUM("Headphone Source Playback Route", sun6i_codec_hp_src_enum),
> +};
> +
> +/* volume / mute controls */
> +static const DECLARE_TLV_DB_SCALE(sun6i_codec_dvol_scale, -7308, 116, 0);
> +static const DECLARE_TLV_DB_SCALE(sun6i_codec_hp_vol_scale, -6300, 100, 1);
> +
> +static const struct snd_kcontrol_new sun6i_codec_codec_widgets[] = {
> +	SOC_SINGLE_TLV("DAC Playback Volume", SUN4I_CODEC_DAC_DPC,
> +		       SUN4I_CODEC_DAC_DPC_DVOL, 0x3f, 1,
> +		       sun6i_codec_dvol_scale),
> +	SOC_SINGLE_TLV("Headphone Playback Volume",
> +		       SUN6I_CODEC_OM_DACA_CTRL,
> +		       SUN6I_CODEC_OM_DACA_CTRL_HPVOL, 0x3f, 0,
> +		       sun6i_codec_hp_vol_scale),
> +	SOC_DOUBLE("Headphone Playback Switch",
> +		   SUN6I_CODEC_OM_DACA_CTRL,
> +		   SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE,
> +		   SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE, 1, 0),
> +};
> +
> +static const struct snd_soc_dapm_widget sun6i_codec_codec_dapm_widgets[] = {
> +	/* Digital parts of the DACs */
> +	SND_SOC_DAPM_SUPPLY("DAC Enable", SUN4I_CODEC_DAC_DPC,
> +			    SUN4I_CODEC_DAC_DPC_EN_DA, 0,
> +			    NULL, 0),
> +
> +	/* Analog parts of the DACs */
> +	SND_SOC_DAPM_DAC("Left DAC", "Codec Playback", SUN6I_CODEC_OM_DACA_CTRL,
> +			 SUN6I_CODEC_OM_DACA_CTRL_DACALEN, 0),
> +	SND_SOC_DAPM_DAC("Right DAC", "Codec Playback", SUN6I_CODEC_OM_DACA_CTRL,
> +			 SUN6I_CODEC_OM_DACA_CTRL_DACAREN, 0),
> +
> +	/* Mixers */
> +	SOC_MIXER_ARRAY("Left Mixer", SUN6I_CODEC_OM_DACA_CTRL,
> +			SUN6I_CODEC_OM_DACA_CTRL_LMIXEN, 0,
> +			sun6i_codec_mixer_controls),
> +	SOC_MIXER_ARRAY("Right Mixer", SUN6I_CODEC_OM_DACA_CTRL,
> +			SUN6I_CODEC_OM_DACA_CTRL_RMIXEN, 0,
> +			sun6i_codec_mixer_controls),
> +
> +	/* Headphone output path */
> +	SND_SOC_DAPM_MUX("Headphone Source Playback Route",
> +			 SND_SOC_NOPM, 0, 0, sun6i_codec_hp_src),
> +	SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUN6I_CODEC_OM_PA_CTRL,
> +			     SUN6I_CODEC_OM_PA_CTRL_HPPAEN, 0, NULL, 0),
> +	SND_SOC_DAPM_OUTPUT("HP"),
> +};
> +
> +static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = {
> +	/* DAC Routes */
> +	{ "Left DAC", NULL, "DAC Enable" },
> +	{ "Right DAC", NULL, "DAC Enable" },
> +
> +	/* Left Mixer Routes */
> +	{ "Left Mixer", "DAC Playback Switch", "Left DAC" },
> +	{ "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" },
> +
> +	/* Right Mixer Routes */
> +	{ "Right Mixer", "DAC Playback Switch", "Right DAC" },
> +	{ "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" },
> +
> +	/* Headphone Routes */
> +	{ "Headphone Source Playback Route", "DAC", "Left DAC" },
> +	{ "Headphone Source Playback Route", "DAC", "Right DAC" },
> +	{ "Headphone Source Playback Route", "Mixer", "Left Mixer" },
> +	{ "Headphone Source Playback Route", "Mixer", "Right Mixer" },
> +	{ "Headphone Amp", NULL, "Headphone Source Playback Route" },
> +	{ "HP", NULL, "Headphone Amp" },
> +};
> +
> +static struct snd_soc_codec_driver sun6i_codec_codec = {
> +	.component_driver = {
> +		.controls		= sun6i_codec_codec_widgets,
> +		.num_controls		= ARRAY_SIZE(sun6i_codec_codec_widgets),
> +		.dapm_widgets		= sun6i_codec_codec_dapm_widgets,
> +		.num_dapm_widgets	= ARRAY_SIZE(sun6i_codec_codec_dapm_widgets),
> +		.dapm_routes		= sun6i_codec_codec_dapm_routes,
> +		.num_dapm_routes	= ARRAY_SIZE(sun6i_codec_codec_dapm_routes),
> +	},
> +};
> +
>  static const struct snd_soc_component_driver sun4i_codec_component = {
>  	.name = "sun4i-codec",
>  };
> @@ -678,45 +883,6 @@ static struct snd_soc_dai_driver dummy_cpu_dai = {
>  	 },
>  };
>  
> -static const struct regmap_config sun4i_codec_regmap_config = {
> -	.reg_bits	= 32,
> -	.reg_stride	= 4,
> -	.val_bits	= 32,
> -	.max_register	= SUN4I_CODEC_ADC_RXCNT,
> -};
> -
> -static const struct regmap_config sun7i_codec_regmap_config = {
> -	.reg_bits	= 32,
> -	.reg_stride	= 4,
> -	.val_bits	= 32,
> -	.max_register	= SUN7I_CODEC_AC_MIC_PHONE_CAL,
> -};
> -
> -struct sun4i_codec_quirks {
> -	const struct regmap_config *regmap_config;
> -};
> -
> -static const struct sun4i_codec_quirks sun4i_codec_quirks = {
> -	.regmap_config = &sun4i_codec_regmap_config,
> -};
> -
> -static const struct sun4i_codec_quirks sun7i_codec_quirks = {
> -	.regmap_config = &sun7i_codec_regmap_config,
> -};
> -
> -static const struct of_device_id sun4i_codec_of_match[] = {
> -	{
> -		.compatible = "allwinner,sun4i-a10-codec",
> -		.data = &sun4i_codec_quirks,
> -	},
> -	{
> -		.compatible = "allwinner,sun7i-a20-codec",
> -		.data = &sun7i_codec_quirks,
> -	},
> -	{}
> -};
> -MODULE_DEVICE_TABLE(of, sun4i_codec_of_match);
> -
>  static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev,
>  							int *num_links)
>  {
> @@ -781,6 +947,102 @@ static struct snd_soc_card *sun4i_codec_create_card(struct device *dev)
>  	return card;
>  };
>  
> +static struct snd_soc_card *sun6i_codec_create_card(struct device *dev)
> +{
> +	struct snd_soc_card *card;
> +
> +	card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
> +	if (!card)
> +		return NULL;
> +
> +	card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
> +	if (!card->dai_link)
> +		return NULL;
> +
> +	card->dev	= dev;
> +	card->name	= "sun4i-codec";
> +
> +	return card;
> +};
> +
> +static const struct regmap_config sun4i_codec_regmap_config = {
> +	.reg_bits	= 32,
> +	.reg_stride	= 4,
> +	.val_bits	= 32,
> +	.max_register	= SUN4I_CODEC_ADC_RXCNT,
> +};
> +
> +static const struct regmap_config sun6i_codec_regmap_config = {
> +	.reg_bits	= 32,
> +	.reg_stride	= 4,
> +	.val_bits	= 32,
> +	.max_register	= SUN6I_CODEC_HMIC_DATA,
> +};
> +
> +static const struct regmap_config sun7i_codec_regmap_config = {
> +	.reg_bits	= 32,
> +	.reg_stride	= 4,
> +	.val_bits	= 32,
> +	.max_register	= SUN7I_CODEC_AC_MIC_PHONE_CAL,
> +};
> +
> +static const struct sun4i_codec_regs sun4i_codec_regs = {
> +	.adc_fifoc	= SUN4I_CODEC_ADC_FIFOC,
> +	.adc_fifos	= SUN4I_CODEC_ADC_FIFOS,
> +	.adc_rxdata	= SUN4I_CODEC_ADC_RXDATA,
> +};
> +
> +static const struct sun4i_codec_regs sun6i_codec_regs = {
> +	.adc_fifoc	= SUN6I_CODEC_ADC_FIFOC,
> +	.adc_fifos	= SUN6I_CODEC_ADC_FIFOS,
> +	.adc_rxdata	= SUN6I_CODEC_ADC_RXDATA,
> +};
> +
> +struct sun4i_codec_quirks {
> +	const struct regmap_config *regmap_config;
> +	const struct snd_soc_codec_driver *codec;
> +	const struct sun4i_codec_regs *regs;
> +	struct snd_soc_card * (*create_card)(struct device *dev);
> +};
> +
> +static const struct sun4i_codec_quirks sun4i_codec_quirks = {
> +	.regmap_config	= &sun4i_codec_regmap_config,
> +	.regs		= &sun4i_codec_regs,
> +	.codec		= &sun4i_codec_codec,
> +	.create_card	= sun4i_codec_create_card,
> +};
> +
> +static const struct sun4i_codec_quirks sun6i_a31_codec_quirks = {
> +	.regmap_config	= &sun6i_codec_regmap_config,
> +	.regs		= &sun6i_codec_regs,
> +	.codec		= &sun6i_codec_codec,
> +	.create_card	= sun6i_codec_create_card,
> +};
> +
> +static const struct sun4i_codec_quirks sun7i_codec_quirks = {
> +	.regmap_config	= &sun7i_codec_regmap_config,
> +	.regs		= &sun4i_codec_regs,
> +	.codec		= &sun4i_codec_codec,
> +	.create_card	= sun4i_codec_create_card,
> +};
> +
> +static const struct of_device_id sun4i_codec_of_match[] = {
> +	{
> +		.compatible = "allwinner,sun4i-a10-codec",
> +		.data = &sun4i_codec_quirks,
> +	},
> +	{
> +		.compatible = "allwinner,sun6i-a31-codec",
> +		.data = &sun6i_a31_codec_quirks,
> +	},
> +	{
> +		.compatible = "allwinner,sun7i-a20-codec",
> +		.data = &sun7i_codec_quirks,
> +	},
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, sun4i_codec_of_match);
> +

I don't really like moving blocks of code over and over again,
especially in the middle of an unrelated patch.

Thanks!
Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161003/b00e6c86/attachment.sig>