|
@@ -28,6 +28,7 @@
|
|
|
#include <linux/of_address.h>
|
|
|
#include <linux/clk.h>
|
|
|
#include <linux/regmap.h>
|
|
|
+#include <linux/gpio/consumer.h>
|
|
|
|
|
|
#include <sound/core.h>
|
|
|
#include <sound/pcm.h>
|
|
@@ -70,6 +71,7 @@
|
|
|
|
|
|
/* Codec ADC register offsets and bit fields */
|
|
|
#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_RX_FIFO_MODE (24)
|
|
|
#define SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL (8)
|
|
@@ -102,17 +104,14 @@ struct sun4i_codec {
|
|
|
struct regmap *regmap;
|
|
|
struct clk *clk_apb;
|
|
|
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;
|
|
|
};
|
|
|
|
|
|
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 */
|
|
|
regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
|
|
|
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)
|
|
|
{
|
|
|
- /*
|
|
|
- * FIXME: according to the BSP, we might need to drive a PA
|
|
|
- * GPIO low here on some boards
|
|
|
- */
|
|
|
-
|
|
|
/* Disable DAC DRQ */
|
|
|
regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
|
|
|
BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN),
|
|
|
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,
|
|
|
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);
|
|
|
|
|
|
- if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
|
|
|
- return -ENOTSUPP;
|
|
|
-
|
|
|
switch (cmd) {
|
|
|
case SNDRV_PCM_TRIGGER_START:
|
|
|
case SNDRV_PCM_TRIGGER_RESUME:
|
|
|
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
|
|
- sun4i_codec_start_playback(scodec);
|
|
|
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
|
|
+ sun4i_codec_start_playback(scodec);
|
|
|
+ else
|
|
|
+ sun4i_codec_start_capture(scodec);
|
|
|
break;
|
|
|
|
|
|
case SNDRV_PCM_TRIGGER_STOP:
|
|
|
case SNDRV_PCM_TRIGGER_SUSPEND:
|
|
|
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
|
|
- sun4i_codec_stop_playback(scodec);
|
|
|
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
|
|
+ sun4i_codec_stop_playback(scodec);
|
|
|
+ else
|
|
|
+ sun4i_codec_stop_capture(scodec);
|
|
|
break;
|
|
|
|
|
|
default:
|
|
@@ -166,15 +178,54 @@ static int sun4i_codec_trigger(struct snd_pcm_substream *substream, int cmd,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int sun4i_codec_prepare(struct snd_pcm_substream *substream,
|
|
|
- struct snd_soc_dai *dai)
|
|
|
+static int sun4i_codec_prepare_capture(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;
|
|
|
|
|
|
- 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 */
|
|
|
regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
|
|
@@ -203,6 +254,15 @@ static int sun4i_codec_prepare(struct snd_pcm_substream *substream,
|
|
|
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)
|
|
@@ -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,
|
|
|
- struct snd_pcm_hw_params *params,
|
|
|
- struct snd_soc_dai *dai)
|
|
|
+static int sun4i_codec_hw_params_capture(struct sun4i_codec *scodec,
|
|
|
+ struct snd_pcm_hw_params *params,
|
|
|
+ unsigned int hwrate)
|
|
|
{
|
|
|
- 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;
|
|
|
- u32 val;
|
|
|
-
|
|
|
- if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
|
|
|
- return -ENOTSUPP;
|
|
|
+ /* Set ADC sample rate */
|
|
|
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
|
|
|
+ 7 << SUN4I_CODEC_ADC_FIFOC_ADC_FS,
|
|
|
+ hwrate << SUN4I_CODEC_ADC_FIFOC_ADC_FS);
|
|
|
|
|
|
- clk_freq = sun4i_codec_get_mod_freq(params);
|
|
|
- if (!clk_freq)
|
|
|
- return -EINVAL;
|
|
|
+ /* Set the number of channels we want to use */
|
|
|
+ if (params_channels(params) == 1)
|
|
|
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_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,
|
|
|
+ BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN), 0);
|
|
|
|
|
|
- ret = clk_set_rate(scodec->clk_module, clk_freq);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
|
|
|
- hwrate = sun4i_codec_get_hw_rate(params);
|
|
|
- if (hwrate < 0)
|
|
|
- return hwrate;
|
|
|
+static int sun4i_codec_hw_params_playback(struct sun4i_codec *scodec,
|
|
|
+ struct snd_pcm_hw_params *params,
|
|
|
+ unsigned int hwrate)
|
|
|
+{
|
|
|
+ u32 val;
|
|
|
|
|
|
/* Set DAC sample rate */
|
|
|
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;
|
|
|
}
|
|
|
|
|
|
+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,
|
|
|
struct snd_soc_dai *dai)
|
|
|
{
|
|
@@ -395,6 +486,20 @@ static struct snd_soc_dai_driver sun4i_codec_dai = {
|
|
|
SNDRV_PCM_FMTBIT_S32_LE,
|
|
|
.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 ***/
|
|
@@ -429,12 +534,23 @@ static const struct snd_kcontrol_new sun4i_codec_pa_mixer_controls[] = {
|
|
|
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 */
|
|
|
SND_SOC_DAPM_SUPPLY("DAC", SUN4I_CODEC_DAC_DPC,
|
|
|
SUN4I_CODEC_DAC_DPC_EN_DA, 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 */
|
|
|
SND_SOC_DAPM_DAC("Left DAC", "Codec Playback", SUN4I_CODEC_DAC_ACTL,
|
|
|
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,
|
|
|
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 */
|
|
|
SND_SOC_DAPM_MIXER("Power Amplifier", SUN4I_CODEC_ADC_ACTL,
|
|
|
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,
|
|
|
&sun4i_codec_pa_mute),
|
|
|
|
|
|
+ SND_SOC_DAPM_INPUT("Mic1"),
|
|
|
+
|
|
|
SND_SOC_DAPM_OUTPUT("HP Right"),
|
|
|
SND_SOC_DAPM_OUTPUT("HP Left"),
|
|
|
};
|
|
|
|
|
|
-static const struct snd_soc_dapm_route sun4i_codec_dapm_routes[] = {
|
|
|
- /* Left DAC Routes */
|
|
|
+static const struct snd_soc_dapm_route sun4i_codec_codec_dapm_routes[] = {
|
|
|
+ /* Left ADC / DAC Routes */
|
|
|
+ { "Left ADC", NULL, "ADC" },
|
|
|
{ "Left DAC", NULL, "DAC" },
|
|
|
|
|
|
- /* Right DAC Routes */
|
|
|
+ /* Right ADC / DAC Routes */
|
|
|
+ { "Right ADC", NULL, "ADC" },
|
|
|
{ "Right DAC", NULL, "DAC" },
|
|
|
|
|
|
/* Right Mixer Routes */
|
|
@@ -491,15 +619,21 @@ static const struct snd_soc_dapm_route sun4i_codec_dapm_routes[] = {
|
|
|
{ "Power Amplifier Mute", "Switch", "Power Amplifier" },
|
|
|
{ "HP Right", 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 = {
|
|
|
.controls = sun4i_codec_widgets,
|
|
|
.num_controls = ARRAY_SIZE(sun4i_codec_widgets),
|
|
|
- .dapm_widgets = sun4i_codec_dapm_widgets,
|
|
|
- .num_dapm_widgets = ARRAY_SIZE(sun4i_codec_dapm_widgets),
|
|
|
- .dapm_routes = sun4i_codec_dapm_routes,
|
|
|
- .num_dapm_routes = ARRAY_SIZE(sun4i_codec_dapm_routes),
|
|
|
+ .dapm_widgets = sun4i_codec_codec_dapm_widgets,
|
|
|
+ .num_dapm_widgets = ARRAY_SIZE(sun4i_codec_codec_dapm_widgets),
|
|
|
+ .dapm_routes = sun4i_codec_codec_dapm_routes,
|
|
|
+ .num_dapm_routes = ARRAY_SIZE(sun4i_codec_codec_dapm_routes),
|
|
|
};
|
|
|
|
|
|
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);
|
|
|
|
|
|
snd_soc_dai_init_dma_data(dai, &scodec->playback_dma_data,
|
|
|
- NULL);
|
|
|
+ &scodec->capture_dma_data);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -532,6 +666,14 @@ static struct snd_soc_dai_driver dummy_cpu_dai = {
|
|
|
.formats = SUN4I_CODEC_FORMATS,
|
|
|
.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 = {
|
|
@@ -569,6 +711,27 @@ static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev,
|
|
|
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)
|
|
|
{
|
|
|
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->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;
|
|
|
};
|
|
@@ -634,11 +801,25 @@ static int sun4i_codec_probe(struct platform_device *pdev)
|
|
|
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 */
|
|
|
scodec->playback_dma_data.addr = res->start + SUN4I_CODEC_DAC_TXDATA;
|
|
|
scodec->playback_dma_data.maxburst = 4;
|
|
|
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,
|
|
|
&sun4i_codec_dai, 1);
|
|
|
if (ret) {
|