Loading Documentation/devicetree/bindings/sound/sun4i-codec.txt +3 −0 Original line number Original line Diff line number Diff line Loading @@ -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>; Loading sound/soc/sunxi/sun4i-codec.c +230 −49 Original line number Original line Diff line number Diff line Loading @@ -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> Loading Loading @@ -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) Loading Loading @@ -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), Loading @@ -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: Loading @@ -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, Loading Loading @@ -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) Loading Loading @@ -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, Loading Loading @@ -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) { { Loading Loading @@ -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 ***/ Loading Loading @@ -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), Loading @@ -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, Loading @@ -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 */ Loading @@ -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 = { Loading @@ -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; } } Loading @@ -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 = { Loading Loading @@ -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; Loading @@ -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; }; }; Loading Loading @@ -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) { Loading Loading
Documentation/devicetree/bindings/sound/sun4i-codec.txt +3 −0 Original line number Original line Diff line number Diff line Loading @@ -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>; Loading
sound/soc/sunxi/sun4i-codec.c +230 −49 Original line number Original line Diff line number Diff line Loading @@ -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> Loading Loading @@ -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) Loading Loading @@ -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), Loading @@ -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: Loading @@ -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, Loading Loading @@ -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) Loading Loading @@ -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, Loading Loading @@ -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) { { Loading Loading @@ -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 ***/ Loading Loading @@ -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), Loading @@ -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, Loading @@ -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 */ Loading @@ -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 = { Loading @@ -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; } } Loading @@ -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 = { Loading Loading @@ -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; Loading @@ -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; }; }; Loading Loading @@ -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) { Loading