ASoC: add Allwinner H616 audio codec support

Merge series from Ryan Walklin <ryan@testtoast.com>:

The Allwinner H616 has a playback-only audio codec, with a single stereo
or differential-mono line output.  This patch series adds support for
the H616 (and H313/H618/H700/T507) SoC.  Based on the Allwinner kernel
SDK driver, and tested on the H700.
This commit is contained in:
Mark Brown 2024-10-24 19:21:06 +01:00
commit 7fc18ae228
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
2 changed files with 309 additions and 42 deletions

View File

@ -22,6 +22,7 @@ properties:
- allwinner,sun8i-a23-codec
- allwinner,sun8i-h3-codec
- allwinner,sun8i-v3s-codec
- allwinner,sun50i-h616-codec
reg:
maxItems: 1
@ -40,14 +41,20 @@ properties:
- const: codec
dmas:
items:
- description: RX DMA Channel
- description: TX DMA Channel
oneOf:
- items:
- description: RX DMA Channel
- description: TX DMA Channel
- items:
- description: TX DMA Channel
dma-names:
items:
- const: rx
- const: tx
oneOf:
- items:
- const: rx
- const: tx
- items:
- const: tx
resets:
maxItems: 1
@ -229,6 +236,40 @@ allOf:
- Mic
- Speaker
- if:
properties:
compatible:
enum:
- allwinner,sun50i-h616-codec
then:
properties:
allwinner,audio-routing:
items:
enum:
- LINEOUT
- Line Out
dmas:
items:
- description: TX DMA Channel
dma-names:
items:
- const: tx
else:
properties:
dmas:
items:
- description: RX DMA Channel
- description: TX DMA Channel
dma-names:
items:
- const: rx
- const: tx
unevaluatedProperties: false
examples:

View File

@ -226,6 +226,43 @@
#define SUN8I_H3_CODEC_DAC_DBG (0x48)
#define SUN8I_H3_CODEC_ADC_DBG (0x4c)
/* H616 specific registers */
#define SUN50I_H616_CODEC_DAC_FIFOC (0x10)
#define SUN50I_DAC_FIFO_STA (0x14)
#define SUN50I_DAC_TXE_INT (3)
#define SUN50I_DAC_TXU_INT (2)
#define SUN50I_DAC_TXO_INT (1)
#define SUN50I_DAC_CNT (0x24)
#define SUN50I_DAC_DG_REG (0x28)
#define SUN50I_DAC_DAP_CTL (0xf0)
#define SUN50I_H616_DAC_AC_DAC_REG (0x310)
#define SUN50I_H616_DAC_LEN (15)
#define SUN50I_H616_DAC_REN (14)
#define SUN50I_H616_LINEOUTL_EN (13)
#define SUN50I_H616_LMUTE (12)
#define SUN50I_H616_LINEOUTR_EN (11)
#define SUN50I_H616_RMUTE (10)
#define SUN50I_H616_RSWITCH (9)
#define SUN50I_H616_RAMPEN (8)
#define SUN50I_H616_LINEOUTL_SEL (6)
#define SUN50I_H616_LINEOUTR_SEL (5)
#define SUN50I_H616_LINEOUT_VOL (0)
#define SUN50I_H616_DAC_AC_MIXER_REG (0x314)
#define SUN50I_H616_LMIX_LDAC (21)
#define SUN50I_H616_LMIX_RDAC (20)
#define SUN50I_H616_RMIX_RDAC (17)
#define SUN50I_H616_RMIX_LDAC (16)
#define SUN50I_H616_LMIXEN (11)
#define SUN50I_H616_RMIXEN (10)
#define SUN50I_H616_DAC_AC_RAMP_REG (0x31c)
#define SUN50I_H616_RAMP_STEP (4)
#define SUN50I_H616_RDEN (0)
/* TODO H3 DAP (Digital Audio Processing) bits */
struct sun4i_codec {
@ -238,6 +275,8 @@ struct sun4i_codec {
/* ADC_FIFOC register is at different offset on different SoCs */
struct regmap_field *reg_adc_fifoc;
/* DAC_FIFOC register is at different offset on different SoCs */
struct regmap_field *reg_dac_fifoc;
struct snd_dmaengine_dai_dma_data capture_dma_data;
struct snd_dmaengine_dai_dma_data playback_dma_data;
@ -246,19 +285,19 @@ struct sun4i_codec {
static void sun4i_codec_start_playback(struct sun4i_codec *scodec)
{
/* Flush TX FIFO */
regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH));
regmap_field_set_bits(scodec->reg_dac_fifoc,
BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH));
/* Enable DAC DRQ */
regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN));
regmap_field_set_bits(scodec->reg_dac_fifoc,
BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN));
}
static void sun4i_codec_stop_playback(struct sun4i_codec *scodec)
{
/* Disable DAC DRQ */
regmap_clear_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN));
regmap_field_clear_bits(scodec->reg_dac_fifoc,
BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN));
}
static void sun4i_codec_start_capture(struct sun4i_codec *scodec)
@ -356,13 +395,13 @@ static int sun4i_codec_prepare_playback(struct snd_pcm_substream *substream,
u32 val;
/* Flush the TX FIFO */
regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH));
regmap_field_set_bits(scodec->reg_dac_fifoc,
BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH));
/* Set TX FIFO Empty Trigger Level */
regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
0x3f << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL,
0xf << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL);
regmap_field_update_bits(scodec->reg_dac_fifoc,
0x3f << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL,
0xf << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL);
if (substream->runtime->rate > 32000)
/* Use 64 bits FIR filter */
@ -371,13 +410,13 @@ static int sun4i_codec_prepare_playback(struct snd_pcm_substream *substream,
/* Use 32 bits FIR filter */
val = BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION);
regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION),
val);
regmap_field_update_bits(scodec->reg_dac_fifoc,
BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION),
val);
/* Send zeros when we have an underrun */
regmap_clear_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
BIT(SUN4I_CODEC_DAC_FIFOC_SEND_LASAT));
regmap_field_clear_bits(scodec->reg_dac_fifoc,
BIT(SUN4I_CODEC_DAC_FIFOC_SEND_LASAT));
return 0;
};
@ -510,9 +549,9 @@ static int sun4i_codec_hw_params_playback(struct sun4i_codec *scodec,
u32 val;
/* Set DAC sample rate */
regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
7 << SUN4I_CODEC_DAC_FIFOC_DAC_FS,
hwrate << SUN4I_CODEC_DAC_FIFOC_DAC_FS);
regmap_field_update_bits(scodec->reg_dac_fifoc,
7 << SUN4I_CODEC_DAC_FIFOC_DAC_FS,
hwrate << SUN4I_CODEC_DAC_FIFOC_DAC_FS);
/* Set the number of channels we want to use */
if (params_channels(params) == 1)
@ -520,27 +559,27 @@ static int sun4i_codec_hw_params_playback(struct sun4i_codec *scodec,
else
val = 0;
regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
BIT(SUN4I_CODEC_DAC_FIFOC_MONO_EN),
val);
regmap_field_update_bits(scodec->reg_dac_fifoc,
BIT(SUN4I_CODEC_DAC_FIFOC_MONO_EN),
val);
/* Set the number of sample bits to either 16 or 24 bits */
if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min == 32) {
regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS));
regmap_field_set_bits(scodec->reg_dac_fifoc,
BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS));
/* Set TX FIFO mode to padding the LSBs with 0 */
regmap_clear_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE));
regmap_field_clear_bits(scodec->reg_dac_fifoc,
BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE));
scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
} else {
regmap_clear_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS));
regmap_field_clear_bits(scodec->reg_dac_fifoc,
BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS));
/* Set TX FIFO mode to repeat the MSB */
regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE));
regmap_field_set_bits(scodec->reg_dac_fifoc,
BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE));
scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
}
@ -587,8 +626,8 @@ static int sun4i_codec_startup(struct snd_pcm_substream *substream,
* Stop issuing DRQ when we have room for less than 16 samples
* in our TX FIFO
*/
regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
3 << SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT);
regmap_field_set_bits(scodec->reg_dac_fifoc,
3 << SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT);
return clk_prepare_enable(scodec->clk_module);
}
@ -1518,6 +1557,150 @@ static struct snd_soc_card *sun8i_v3s_codec_create_card(struct device *dev)
return card;
};
static const struct snd_kcontrol_new sun50i_h616_codec_codec_controls[] = {
SOC_SINGLE_TLV("DAC Playback Volume", SUN4I_CODEC_DAC_DPC,
SUN4I_CODEC_DAC_DPC_DVOL, 0x3f, 1,
sun6i_codec_dvol_scale),
SOC_SINGLE_TLV("Line Out Playback Volume",
SUN50I_H616_DAC_AC_DAC_REG,
SUN50I_H616_LINEOUT_VOL, 0x1f, 0,
sun6i_codec_lineout_vol_scale),
SOC_DOUBLE("Line Out Playback Switch",
SUN50I_H616_DAC_AC_DAC_REG,
SUN50I_H616_LINEOUTL_EN,
SUN50I_H616_LINEOUTR_EN, 1, 0),
};
static const struct snd_kcontrol_new sun50i_h616_codec_mixer_controls[] = {
SOC_DAPM_DOUBLE("DAC Playback Switch",
SUN50I_H616_DAC_AC_MIXER_REG,
SUN50I_H616_LMIX_LDAC,
SUN50I_H616_RMIX_RDAC, 1, 0),
SOC_DAPM_DOUBLE("DAC Reversed Playback Switch",
SUN50I_H616_DAC_AC_MIXER_REG,
SUN50I_H616_LMIX_RDAC,
SUN50I_H616_RMIX_LDAC, 1, 0),
};
static SOC_ENUM_DOUBLE_DECL(sun50i_h616_codec_lineout_src_enum,
SUN50I_H616_DAC_AC_DAC_REG,
SUN50I_H616_LINEOUTL_SEL,
SUN50I_H616_LINEOUTR_SEL,
sun6i_codec_lineout_src_enum_text);
static const struct snd_kcontrol_new sun50i_h616_codec_lineout_src[] = {
SOC_DAPM_ENUM("Line Out Source Playback Route",
sun50i_h616_codec_lineout_src_enum),
};
static const struct snd_soc_dapm_widget sun50i_h616_codec_codec_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",
SUN50I_H616_DAC_AC_DAC_REG,
SUN50I_H616_DAC_LEN, 0),
SND_SOC_DAPM_DAC("Right DAC", "Codec Playback",
SUN50I_H616_DAC_AC_DAC_REG,
SUN50I_H616_DAC_REN, 0),
/* Mixers */
SOC_MIXER_ARRAY("Left Mixer", SUN50I_H616_DAC_AC_MIXER_REG,
SUN50I_H616_LMIXEN, 0,
sun50i_h616_codec_mixer_controls),
SOC_MIXER_ARRAY("Right Mixer", SUN50I_H616_DAC_AC_MIXER_REG,
SUN50I_H616_RMIXEN, 0,
sun50i_h616_codec_mixer_controls),
/* Line Out path */
SND_SOC_DAPM_MUX("Line Out Source Playback Route",
SND_SOC_NOPM, 0, 0, sun50i_h616_codec_lineout_src),
SND_SOC_DAPM_OUT_DRV("Line Out Ramp Controller",
SUN50I_H616_DAC_AC_RAMP_REG,
SUN50I_H616_RDEN, 0, NULL, 0),
SND_SOC_DAPM_OUTPUT("LINEOUT"),
};
static const struct snd_soc_component_driver sun50i_h616_codec_codec = {
.controls = sun50i_h616_codec_codec_controls,
.num_controls = ARRAY_SIZE(sun50i_h616_codec_codec_controls),
.dapm_widgets = sun50i_h616_codec_codec_widgets,
.num_dapm_widgets = ARRAY_SIZE(sun50i_h616_codec_codec_widgets),
.idle_bias_on = 1,
.use_pmdown_time = 1,
.endianness = 1,
};
static const struct snd_kcontrol_new sun50i_h616_card_controls[] = {
SOC_DAPM_PIN_SWITCH("LINEOUT"),
};
static const struct snd_soc_dapm_widget sun50i_h616_codec_card_dapm_widgets[] = {
SND_SOC_DAPM_LINE("Line Out", NULL),
SND_SOC_DAPM_SPK("Speaker", sun4i_codec_spk_event),
};
/* Connect digital side enables to analog side widgets */
static const struct snd_soc_dapm_route sun50i_h616_codec_card_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" },
/* Line Out Routes */
{ "Line Out Source Playback Route", "Stereo", "Left Mixer" },
{ "Line Out Source Playback Route", "Stereo", "Right Mixer" },
{ "Line Out Source Playback Route", "Mono Differential", "Left Mixer" },
{ "Line Out Source Playback Route", "Mono Differential", "Right Mixer" },
{ "Line Out Ramp Controller", NULL, "Line Out Source Playback Route" },
{ "LINEOUT", NULL, "Line Out Ramp Controller" },
};
static struct snd_soc_card *sun50i_h616_codec_create_card(struct device *dev)
{
struct snd_soc_card *card;
int ret;
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card)
return ERR_PTR(-ENOMEM);
card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
if (!card->dai_link)
return ERR_PTR(-ENOMEM);
card->dai_link->playback_only = true;
card->dai_link->capture_only = false;
card->dev = dev;
card->owner = THIS_MODULE;
card->name = "H616 Audio Codec";
card->driver_name = "sun4i-codec";
card->controls = sun50i_h616_card_controls;
card->num_controls = ARRAY_SIZE(sun50i_h616_card_controls);
card->dapm_widgets = sun50i_h616_codec_card_dapm_widgets;
card->num_dapm_widgets = ARRAY_SIZE(sun50i_h616_codec_card_dapm_widgets);
card->dapm_routes = sun50i_h616_codec_card_routes;
card->num_dapm_routes = ARRAY_SIZE(sun50i_h616_codec_card_routes);
card->fully_routed = true;
ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing");
if (ret)
dev_warn(dev, "failed to parse audio-routing: %d\n", ret);
return card;
};
static const struct regmap_config sun4i_codec_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
@ -1560,14 +1743,24 @@ static const struct regmap_config sun8i_v3s_codec_regmap_config = {
.max_register = SUN8I_H3_CODEC_ADC_DBG,
};
static const struct regmap_config sun50i_h616_codec_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = SUN50I_H616_DAC_AC_RAMP_REG,
.cache_type = REGCACHE_NONE,
};
struct sun4i_codec_quirks {
const struct regmap_config *regmap_config;
const struct snd_soc_component_driver *codec;
struct snd_soc_card * (*create_card)(struct device *dev);
struct reg_field reg_adc_fifoc; /* used for regmap_field */
struct reg_field reg_dac_fifoc; /* used for regmap_field */
unsigned int reg_dac_txdata; /* TX FIFO offset for DMA config */
unsigned int reg_adc_rxdata; /* RX FIFO offset for DMA config */
bool has_reset;
bool playback_only;
};
static const struct sun4i_codec_quirks sun4i_codec_quirks = {
@ -1575,6 +1768,7 @@ static const struct sun4i_codec_quirks sun4i_codec_quirks = {
.codec = &sun4i_codec_codec,
.create_card = sun4i_codec_create_card,
.reg_adc_fifoc = REG_FIELD(SUN4I_CODEC_ADC_FIFOC, 0, 31),
.reg_dac_fifoc = REG_FIELD(SUN4I_CODEC_DAC_FIFOC, 0, 31),
.reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA,
.reg_adc_rxdata = SUN4I_CODEC_ADC_RXDATA,
};
@ -1584,6 +1778,7 @@ static const struct sun4i_codec_quirks sun6i_a31_codec_quirks = {
.codec = &sun6i_codec_codec,
.create_card = sun6i_codec_create_card,
.reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31),
.reg_dac_fifoc = REG_FIELD(SUN4I_CODEC_DAC_FIFOC, 0, 31),
.reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA,
.reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA,
.has_reset = true,
@ -1594,6 +1789,7 @@ static const struct sun4i_codec_quirks sun7i_codec_quirks = {
.codec = &sun7i_codec_codec,
.create_card = sun4i_codec_create_card,
.reg_adc_fifoc = REG_FIELD(SUN4I_CODEC_ADC_FIFOC, 0, 31),
.reg_dac_fifoc = REG_FIELD(SUN4I_CODEC_DAC_FIFOC, 0, 31),
.reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA,
.reg_adc_rxdata = SUN4I_CODEC_ADC_RXDATA,
};
@ -1603,6 +1799,7 @@ static const struct sun4i_codec_quirks sun8i_a23_codec_quirks = {
.codec = &sun8i_a23_codec_codec,
.create_card = sun8i_a23_codec_create_card,
.reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31),
.reg_dac_fifoc = REG_FIELD(SUN4I_CODEC_DAC_FIFOC, 0, 31),
.reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA,
.reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA,
.has_reset = true,
@ -1618,6 +1815,7 @@ static const struct sun4i_codec_quirks sun8i_h3_codec_quirks = {
.codec = &sun8i_a23_codec_codec,
.create_card = sun8i_h3_codec_create_card,
.reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31),
.reg_dac_fifoc = REG_FIELD(SUN4I_CODEC_DAC_FIFOC, 0, 31),
.reg_dac_txdata = SUN8I_H3_CODEC_DAC_TXDATA,
.reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA,
.has_reset = true,
@ -1632,11 +1830,21 @@ static const struct sun4i_codec_quirks sun8i_v3s_codec_quirks = {
.codec = &sun8i_a23_codec_codec,
.create_card = sun8i_v3s_codec_create_card,
.reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31),
.reg_dac_fifoc = REG_FIELD(SUN4I_CODEC_DAC_FIFOC, 0, 31),
.reg_dac_txdata = SUN8I_H3_CODEC_DAC_TXDATA,
.reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA,
.has_reset = true,
};
static const struct sun4i_codec_quirks sun50i_h616_codec_quirks = {
.regmap_config = &sun50i_h616_codec_regmap_config,
.codec = &sun50i_h616_codec_codec,
.create_card = sun50i_h616_codec_create_card,
.reg_dac_fifoc = REG_FIELD(SUN50I_H616_CODEC_DAC_FIFOC, 0, 31),
.reg_dac_txdata = SUN8I_H3_CODEC_DAC_TXDATA,
.has_reset = true,
};
static const struct of_device_id sun4i_codec_of_match[] = {
{
.compatible = "allwinner,sun4i-a10-codec",
@ -1662,6 +1870,10 @@ static const struct of_device_id sun4i_codec_of_match[] = {
.compatible = "allwinner,sun8i-v3s-codec",
.data = &sun8i_v3s_codec_quirks,
},
{
.compatible = "allwinner,sun50i-h616-codec",
.data = &sun50i_h616_codec_quirks,
},
{}
};
MODULE_DEVICE_TABLE(of, sun4i_codec_of_match);
@ -1739,6 +1951,16 @@ static int sun4i_codec_probe(struct platform_device *pdev)
return ret;
}
scodec->reg_dac_fifoc = devm_regmap_field_alloc(&pdev->dev,
scodec->regmap,
quirks->reg_dac_fifoc);
if (IS_ERR(scodec->reg_dac_fifoc)) {
ret = PTR_ERR(scodec->reg_dac_fifoc);
dev_err(&pdev->dev, "Failed to create regmap fields: %d\n",
ret);
return ret;
}
/* Enable the bus clock */
if (clk_prepare_enable(scodec->clk_apb)) {
dev_err(&pdev->dev, "Failed to enable the APB clock\n");
@ -1760,10 +1982,13 @@ static int sun4i_codec_probe(struct platform_device *pdev)
scodec->playback_dma_data.maxburst = 8;
scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
/* DMA configuration for RX FIFO */
scodec->capture_dma_data.addr = res->start + quirks->reg_adc_rxdata;
scodec->capture_dma_data.maxburst = 8;
scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
if (!quirks->playback_only) {
/* DMA configuration for RX FIFO */
scodec->capture_dma_data.addr = res->start +
quirks->reg_adc_rxdata;
scodec->capture_dma_data.maxburst = 8;
scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
}
ret = devm_snd_soc_register_component(&pdev->dev, quirks->codec,
&sun4i_codec_dai, 1);
@ -1837,4 +2062,5 @@ MODULE_AUTHOR("Emilio López <emilio@elopez.com.ar>");
MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>");
MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
MODULE_AUTHOR("Ryan Walklin <ryan@testtoast.com");
MODULE_LICENSE("GPL");