Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1ab4f851 authored by Mark Brown's avatar Mark Brown
Browse files

Merge remote-tracking branch 'asoc/topic/sunxi' into asoc-next

parents 10330401 6b803c61
Loading
Loading
Loading
Loading
+3 −0
Original line number Original line Diff line number Diff line
@@ -14,6 +14,9 @@ Required properties:
   - "apb": the parent APB clock for this controller
   - "apb": the parent APB clock for this controller
   - "codec": the parent module clock
   - "codec": the parent module clock


Optional properties:
- allwinner,pa-gpios: gpio to enable external amplifier

Example:
Example:
codec: codec@01c22c00 {
codec: codec@01c22c00 {
	#sound-dai-cells = <0>;
	#sound-dai-cells = <0>;
+230 −49
Original line number Original line Diff line number Diff line
@@ -28,6 +28,7 @@
#include <linux/of_address.h>
#include <linux/of_address.h>
#include <linux/clk.h>
#include <linux/clk.h>
#include <linux/regmap.h>
#include <linux/regmap.h>
#include <linux/gpio/consumer.h>


#include <sound/core.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm.h>
@@ -70,6 +71,7 @@


/* Codec ADC register offsets and bit fields */
/* Codec ADC register offsets and bit fields */
#define SUN4I_CODEC_ADC_FIFOC			(0x1c)
#define SUN4I_CODEC_ADC_FIFOC			(0x1c)
#define SUN4I_CODEC_ADC_FIFOC_ADC_FS			(29)
#define SUN4I_CODEC_ADC_FIFOC_EN_AD			(28)
#define SUN4I_CODEC_ADC_FIFOC_EN_AD			(28)
#define SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE		(24)
#define SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE		(24)
#define SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL		(8)
#define SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL		(8)
@@ -102,17 +104,14 @@ struct sun4i_codec {
	struct regmap	*regmap;
	struct regmap	*regmap;
	struct clk	*clk_apb;
	struct clk	*clk_apb;
	struct clk	*clk_module;
	struct clk	*clk_module;
	struct gpio_desc *gpio_pa;


	struct snd_dmaengine_dai_dma_data	capture_dma_data;
	struct snd_dmaengine_dai_dma_data	playback_dma_data;
	struct snd_dmaengine_dai_dma_data	playback_dma_data;
};
};


static void sun4i_codec_start_playback(struct sun4i_codec *scodec)
static void sun4i_codec_start_playback(struct sun4i_codec *scodec)
{
{
	/*
	 * FIXME: according to the BSP, we might need to drive a PA
	 *        GPIO high here on some boards
	 */

	/* Flush TX FIFO */
	/* Flush TX FIFO */
	regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
	regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
			   BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH),
			   BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH),
@@ -126,37 +125,50 @@ static void sun4i_codec_start_playback(struct sun4i_codec *scodec)


static void sun4i_codec_stop_playback(struct sun4i_codec *scodec)
static void sun4i_codec_stop_playback(struct sun4i_codec *scodec)
{
{
	/*
	 * FIXME: according to the BSP, we might need to drive a PA
	 *        GPIO low here on some boards
	 */

	/* Disable DAC DRQ */
	/* Disable DAC DRQ */
	regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
	regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
			   BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN),
			   BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN),
			   0);
			   0);
}
}


static void sun4i_codec_start_capture(struct sun4i_codec *scodec)
{
	/* Enable ADC DRQ */
	regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
			   BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN),
			   BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN));
}

static void sun4i_codec_stop_capture(struct sun4i_codec *scodec)
{
	/* Disable ADC DRQ */
	regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
			   BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN), 0);
}

static int sun4i_codec_trigger(struct snd_pcm_substream *substream, int cmd,
static int sun4i_codec_trigger(struct snd_pcm_substream *substream, int cmd,
			       struct snd_soc_dai *dai)
			       struct snd_soc_dai *dai)
{
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
	struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);


	if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
		return -ENOTSUPP;

	switch (cmd) {
	switch (cmd) {
	case SNDRV_PCM_TRIGGER_START:
	case SNDRV_PCM_TRIGGER_START:
	case SNDRV_PCM_TRIGGER_RESUME:
	case SNDRV_PCM_TRIGGER_RESUME:
	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
			sun4i_codec_start_playback(scodec);
			sun4i_codec_start_playback(scodec);
		else
			sun4i_codec_start_capture(scodec);
		break;
		break;


	case SNDRV_PCM_TRIGGER_STOP:
	case SNDRV_PCM_TRIGGER_STOP:
	case SNDRV_PCM_TRIGGER_SUSPEND:
	case SNDRV_PCM_TRIGGER_SUSPEND:
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
			sun4i_codec_stop_playback(scodec);
			sun4i_codec_stop_playback(scodec);
		else
			sun4i_codec_stop_capture(scodec);
		break;
		break;


	default:
	default:
@@ -166,15 +178,54 @@ static int sun4i_codec_trigger(struct snd_pcm_substream *substream, int cmd,
	return 0;
	return 0;
}
}


static int sun4i_codec_prepare(struct snd_pcm_substream *substream,
static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream,
				       struct snd_soc_dai *dai)
				       struct snd_soc_dai *dai)
{
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
	struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
	u32 val;


	if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)

		return -ENOTSUPP;
	/* Flush RX FIFO */
	regmap_update_bits(scodec->regmap, SUN4I_CODEC_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,
			   0xf << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL,
			   0x7 << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL);

	/*
	 * FIXME: Undocumented in the datasheet, but
	 *        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,sun7i-a20-codec"))
		/* FIXME: Undocumented bits */
		regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_TUNE,
				   0x3 << 8,
				   0x1 << 8);

	/* Fill most significant bits with valid data MSB */
	regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
			   BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE),
			   BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE));

	return 0;
}

static int sun4i_codec_prepare_playback(struct snd_pcm_substream *substream,
					struct snd_soc_dai *dai)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
	u32 val;


	/* Flush the TX FIFO */
	/* Flush the TX FIFO */
	regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
	regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
@@ -203,6 +254,15 @@ static int sun4i_codec_prepare(struct snd_pcm_substream *substream,
			   0);
			   0);


	return 0;
	return 0;
};

static int sun4i_codec_prepare(struct snd_pcm_substream *substream,
			       struct snd_soc_dai *dai)
{
	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
		return sun4i_codec_prepare_playback(substream, dai);

	return sun4i_codec_prepare_capture(substream, dai);
}
}


static unsigned long sun4i_codec_get_mod_freq(struct snd_pcm_hw_params *params)
static unsigned long sun4i_codec_get_mod_freq(struct snd_pcm_hw_params *params)
@@ -277,30 +337,32 @@ static int sun4i_codec_get_hw_rate(struct snd_pcm_hw_params *params)
	}
	}
}
}


static int sun4i_codec_hw_params(struct snd_pcm_substream *substream,
static int sun4i_codec_hw_params_capture(struct sun4i_codec *scodec,
					 struct snd_pcm_hw_params *params,
					 struct snd_pcm_hw_params *params,
				 struct snd_soc_dai *dai)
					 unsigned int hwrate)
{
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	/* Set ADC sample rate */
	struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
	regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
	unsigned long clk_freq;
			   7 << SUN4I_CODEC_ADC_FIFOC_ADC_FS,
	int ret, hwrate;
			   hwrate << SUN4I_CODEC_ADC_FIFOC_ADC_FS);
	u32 val;


	if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
	/* Set the number of channels we want to use */
		return -ENOTSUPP;
	if (params_channels(params) == 1)

		regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
	clk_freq = sun4i_codec_get_mod_freq(params);
				   BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN),
	if (!clk_freq)
				   BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN));
		return -EINVAL;
	else
		regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
				   BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN), 0);


	ret = clk_set_rate(scodec->clk_module, clk_freq);
	return 0;
	if (ret)
}
		return ret;


	hwrate = sun4i_codec_get_hw_rate(params);
static int sun4i_codec_hw_params_playback(struct sun4i_codec *scodec,
	if (hwrate < 0)
					  struct snd_pcm_hw_params *params,
		return hwrate;
					  unsigned int hwrate)
{
	u32 val;


	/* Set DAC sample rate */
	/* Set DAC sample rate */
	regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
	regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
@@ -345,6 +407,35 @@ static int sun4i_codec_hw_params(struct snd_pcm_substream *substream,
	return 0;
	return 0;
}
}


static int sun4i_codec_hw_params(struct snd_pcm_substream *substream,
				 struct snd_pcm_hw_params *params,
				 struct snd_soc_dai *dai)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
	unsigned long clk_freq;
	int ret, hwrate;

	clk_freq = sun4i_codec_get_mod_freq(params);
	if (!clk_freq)
		return -EINVAL;

	ret = clk_set_rate(scodec->clk_module, clk_freq);
	if (ret)
		return ret;

	hwrate = sun4i_codec_get_hw_rate(params);
	if (hwrate < 0)
		return hwrate;

	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
		return sun4i_codec_hw_params_playback(scodec, params,
						      hwrate);

	return sun4i_codec_hw_params_capture(scodec, params,
					     hwrate);
}

static int sun4i_codec_startup(struct snd_pcm_substream *substream,
static int sun4i_codec_startup(struct snd_pcm_substream *substream,
			       struct snd_soc_dai *dai)
			       struct snd_soc_dai *dai)
{
{
@@ -395,6 +486,20 @@ static struct snd_soc_dai_driver sun4i_codec_dai = {
				  SNDRV_PCM_FMTBIT_S32_LE,
				  SNDRV_PCM_FMTBIT_S32_LE,
		.sig_bits	= 24,
		.sig_bits	= 24,
	},
	},
	.capture = {
		.stream_name	= "Codec Capture",
		.channels_min	= 1,
		.channels_max	= 2,
		.rate_min	= 8000,
		.rate_max	= 192000,
		.rates		= SNDRV_PCM_RATE_8000_48000 |
				  SNDRV_PCM_RATE_96000 |
				  SNDRV_PCM_RATE_192000 |
				  SNDRV_PCM_RATE_KNOT,
		.formats	= SNDRV_PCM_FMTBIT_S16_LE |
				  SNDRV_PCM_FMTBIT_S32_LE,
		.sig_bits	= 24,
	},
};
};


/*** Codec ***/
/*** Codec ***/
@@ -429,12 +534,23 @@ static const struct snd_kcontrol_new sun4i_codec_pa_mixer_controls[] = {
			SUN4I_CODEC_DAC_ACTL_MIXPAS, 1, 0),
			SUN4I_CODEC_DAC_ACTL_MIXPAS, 1, 0),
};
};


static const struct snd_soc_dapm_widget sun4i_codec_dapm_widgets[] = {
static const struct snd_soc_dapm_widget sun4i_codec_codec_dapm_widgets[] = {
	/* Digital parts of the ADCs */
	SND_SOC_DAPM_SUPPLY("ADC", SUN4I_CODEC_ADC_FIFOC,
			    SUN4I_CODEC_ADC_FIFOC_EN_AD, 0,
			    NULL, 0),

	/* Digital parts of the DACs */
	/* Digital parts of the DACs */
	SND_SOC_DAPM_SUPPLY("DAC", SUN4I_CODEC_DAC_DPC,
	SND_SOC_DAPM_SUPPLY("DAC", SUN4I_CODEC_DAC_DPC,
			    SUN4I_CODEC_DAC_DPC_EN_DA, 0,
			    SUN4I_CODEC_DAC_DPC_EN_DA, 0,
			    NULL, 0),
			    NULL, 0),


	/* Analog parts of the ADCs */
	SND_SOC_DAPM_ADC("Left ADC", "Codec Capture", SUN4I_CODEC_ADC_ACTL,
			 SUN4I_CODEC_ADC_ACTL_ADC_L_EN, 0),
	SND_SOC_DAPM_ADC("Right ADC", "Codec Capture", SUN4I_CODEC_ADC_ACTL,
			 SUN4I_CODEC_ADC_ACTL_ADC_R_EN, 0),

	/* Analog parts of the DACs */
	/* Analog parts of the DACs */
	SND_SOC_DAPM_DAC("Left DAC", "Codec Playback", SUN4I_CODEC_DAC_ACTL,
	SND_SOC_DAPM_DAC("Left DAC", "Codec Playback", SUN4I_CODEC_DAC_ACTL,
			 SUN4I_CODEC_DAC_ACTL_DACAENL, 0),
			 SUN4I_CODEC_DAC_ACTL_DACAENL, 0),
@@ -453,6 +569,14 @@ static const struct snd_soc_dapm_widget sun4i_codec_dapm_widgets[] = {
	SND_SOC_DAPM_SUPPLY("Mixer Enable", SUN4I_CODEC_DAC_ACTL,
	SND_SOC_DAPM_SUPPLY("Mixer Enable", SUN4I_CODEC_DAC_ACTL,
			    SUN4I_CODEC_DAC_ACTL_MIXEN, 0, NULL, 0),
			    SUN4I_CODEC_DAC_ACTL_MIXEN, 0, NULL, 0),


	/* VMIC */
	SND_SOC_DAPM_SUPPLY("VMIC", SUN4I_CODEC_ADC_ACTL,
			    SUN4I_CODEC_ADC_ACTL_VMICEN, 0, NULL, 0),

	/* Mic Pre-Amplifiers */
	SND_SOC_DAPM_PGA("MIC1 Pre-Amplifier", SUN4I_CODEC_ADC_ACTL,
			 SUN4I_CODEC_ADC_ACTL_PREG1EN, 0, NULL, 0),

	/* Power Amplifier */
	/* Power Amplifier */
	SND_SOC_DAPM_MIXER("Power Amplifier", SUN4I_CODEC_ADC_ACTL,
	SND_SOC_DAPM_MIXER("Power Amplifier", SUN4I_CODEC_ADC_ACTL,
			   SUN4I_CODEC_ADC_ACTL_PA_EN, 0,
			   SUN4I_CODEC_ADC_ACTL_PA_EN, 0,
@@ -461,15 +585,19 @@ static const struct snd_soc_dapm_widget sun4i_codec_dapm_widgets[] = {
	SND_SOC_DAPM_SWITCH("Power Amplifier Mute", SND_SOC_NOPM, 0, 0,
	SND_SOC_DAPM_SWITCH("Power Amplifier Mute", SND_SOC_NOPM, 0, 0,
			    &sun4i_codec_pa_mute),
			    &sun4i_codec_pa_mute),


	SND_SOC_DAPM_INPUT("Mic1"),

	SND_SOC_DAPM_OUTPUT("HP Right"),
	SND_SOC_DAPM_OUTPUT("HP Right"),
	SND_SOC_DAPM_OUTPUT("HP Left"),
	SND_SOC_DAPM_OUTPUT("HP Left"),
};
};


static const struct snd_soc_dapm_route sun4i_codec_dapm_routes[] = {
static const struct snd_soc_dapm_route sun4i_codec_codec_dapm_routes[] = {
	/* Left DAC Routes */
	/* Left ADC / DAC Routes */
	{ "Left ADC", NULL, "ADC" },
	{ "Left DAC", NULL, "DAC" },
	{ "Left DAC", NULL, "DAC" },


	/* Right DAC Routes */
	/* Right ADC / DAC Routes */
	{ "Right ADC", NULL, "ADC" },
	{ "Right DAC", NULL, "DAC" },
	{ "Right DAC", NULL, "DAC" },


	/* Right Mixer Routes */
	/* Right Mixer Routes */
@@ -491,15 +619,21 @@ static const struct snd_soc_dapm_route sun4i_codec_dapm_routes[] = {
	{ "Power Amplifier Mute", "Switch", "Power Amplifier" },
	{ "Power Amplifier Mute", "Switch", "Power Amplifier" },
	{ "HP Right", NULL, "Power Amplifier Mute" },
	{ "HP Right", NULL, "Power Amplifier Mute" },
	{ "HP Left", NULL, "Power Amplifier Mute" },
	{ "HP Left", NULL, "Power Amplifier Mute" },

	/* Mic1 Routes */
	{ "Left ADC", NULL, "MIC1 Pre-Amplifier" },
	{ "Right ADC", NULL, "MIC1 Pre-Amplifier" },
	{ "MIC1 Pre-Amplifier", NULL, "Mic1"},
	{ "Mic1", NULL, "VMIC" },
};
};


static struct snd_soc_codec_driver sun4i_codec_codec = {
static struct snd_soc_codec_driver sun4i_codec_codec = {
	.controls		= sun4i_codec_widgets,
	.controls		= sun4i_codec_widgets,
	.num_controls		= ARRAY_SIZE(sun4i_codec_widgets),
	.num_controls		= ARRAY_SIZE(sun4i_codec_widgets),
	.dapm_widgets		= sun4i_codec_dapm_widgets,
	.dapm_widgets		= sun4i_codec_codec_dapm_widgets,
	.num_dapm_widgets	= ARRAY_SIZE(sun4i_codec_dapm_widgets),
	.num_dapm_widgets	= ARRAY_SIZE(sun4i_codec_codec_dapm_widgets),
	.dapm_routes		= sun4i_codec_dapm_routes,
	.dapm_routes		= sun4i_codec_codec_dapm_routes,
	.num_dapm_routes	= ARRAY_SIZE(sun4i_codec_dapm_routes),
	.num_dapm_routes	= ARRAY_SIZE(sun4i_codec_codec_dapm_routes),
};
};


static const struct snd_soc_component_driver sun4i_codec_component = {
static const struct snd_soc_component_driver sun4i_codec_component = {
@@ -516,7 +650,7 @@ static int sun4i_codec_dai_probe(struct snd_soc_dai *dai)
	struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card);
	struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card);


	snd_soc_dai_init_dma_data(dai, &scodec->playback_dma_data,
	snd_soc_dai_init_dma_data(dai, &scodec->playback_dma_data,
				  NULL);
				  &scodec->capture_dma_data);


	return 0;
	return 0;
}
}
@@ -532,6 +666,14 @@ static struct snd_soc_dai_driver dummy_cpu_dai = {
		.formats	= SUN4I_CODEC_FORMATS,
		.formats	= SUN4I_CODEC_FORMATS,
		.sig_bits	= 24,
		.sig_bits	= 24,
	},
	},
	.capture = {
		.stream_name	= "Capture",
		.channels_min	= 1,
		.channels_max	= 2,
		.rates 		= SUN4I_CODEC_RATES,
		.formats 	= SUN4I_CODEC_FORMATS,
		.sig_bits	= 24,
	 },
};
};


static const struct regmap_config sun4i_codec_regmap_config = {
static const struct regmap_config sun4i_codec_regmap_config = {
@@ -569,6 +711,27 @@ static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev,
	return link;
	return link;
};
};


static int sun4i_codec_spk_event(struct snd_soc_dapm_widget *w,
				 struct snd_kcontrol *k, int event)
{
	struct sun4i_codec *scodec = snd_soc_card_get_drvdata(w->dapm->card);

	if (scodec->gpio_pa)
		gpiod_set_value_cansleep(scodec->gpio_pa,
					 !!SND_SOC_DAPM_EVENT_ON(event));

	return 0;
}

static const struct snd_soc_dapm_widget sun4i_codec_card_dapm_widgets[] = {
	SND_SOC_DAPM_SPK("Speaker", sun4i_codec_spk_event),
};

static const struct snd_soc_dapm_route sun4i_codec_card_dapm_routes[] = {
	{ "Speaker", NULL, "HP Right" },
	{ "Speaker", NULL, "HP Left" },
};

static struct snd_soc_card *sun4i_codec_create_card(struct device *dev)
static struct snd_soc_card *sun4i_codec_create_card(struct device *dev)
{
{
	struct snd_soc_card *card;
	struct snd_soc_card *card;
@@ -583,6 +746,10 @@ static struct snd_soc_card *sun4i_codec_create_card(struct device *dev)


	card->dev		= dev;
	card->dev		= dev;
	card->name		= "sun4i-codec";
	card->name		= "sun4i-codec";
	card->dapm_widgets	= sun4i_codec_card_dapm_widgets;
	card->num_dapm_widgets	= ARRAY_SIZE(sun4i_codec_card_dapm_widgets);
	card->dapm_routes	= sun4i_codec_card_dapm_routes;
	card->num_dapm_routes	= ARRAY_SIZE(sun4i_codec_card_dapm_routes);


	return card;
	return card;
};
};
@@ -634,11 +801,25 @@ static int sun4i_codec_probe(struct platform_device *pdev)
		return -EINVAL;
		return -EINVAL;
	}
	}


	scodec->gpio_pa = devm_gpiod_get_optional(&pdev->dev, "allwinner,pa",
						  GPIOD_OUT_LOW);
	if (IS_ERR(scodec->gpio_pa)) {
		ret = PTR_ERR(scodec->gpio_pa);
		if (ret != -EPROBE_DEFER)
			dev_err(&pdev->dev, "Failed to get pa gpio: %d\n", ret);
		return ret;
	}

	/* DMA configuration for TX FIFO */
	/* DMA configuration for TX FIFO */
	scodec->playback_dma_data.addr = res->start + SUN4I_CODEC_DAC_TXDATA;
	scodec->playback_dma_data.addr = res->start + SUN4I_CODEC_DAC_TXDATA;
	scodec->playback_dma_data.maxburst = 4;
	scodec->playback_dma_data.maxburst = 4;
	scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
	scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;


	/* DMA configuration for RX FIFO */
	scodec->capture_dma_data.addr = res->start + SUN4I_CODEC_ADC_RXDATA;
	scodec->capture_dma_data.maxburst = 4;
	scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;

	ret = snd_soc_register_codec(&pdev->dev, &sun4i_codec_codec,
	ret = snd_soc_register_codec(&pdev->dev, &sun4i_codec_codec,
				     &sun4i_codec_dai, 1);
				     &sun4i_codec_dai, 1);
	if (ret) {
	if (ret) {