Commit e7b6b3ec authored by Takashi Iwai's avatar Takashi Iwai

Merge tag 'asoc-fix-v5.7-rc2' of...

Merge tag 'asoc-fix-v5.7-rc2' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-linus

ASoC: Fixes for v5.7

Quite a lot of fixes here, a lot of driver specific ones but the biggest
one is the revert of changes to the startup and shutdown sequence for
DAIs that went in during the merge window - they broke some older x86
platforms and attempts to fix them didn't succeed so it's safer to just
roll them back and try to make sure those platforms are handled properly
in any future attempt.

The rockchip S/PDIF DT stuff was IIRC for validation issues.
parents cf9fb7b8 1e060a45
......@@ -56,6 +56,9 @@ properties:
- const: tx
- const: rx
power-domains:
maxItems: 1
rockchip,capture-channels:
allOf:
- $ref: /schemas/types.yaml#/definitions/uint32
......
* Rockchip SPDIF transceiver
The S/PDIF audio block is a stereo transceiver that allows the
processor to receive and transmit digital audio via an coaxial cable or
a fibre cable.
Required properties:
- compatible: should be one of the following:
- "rockchip,rk3066-spdif"
- "rockchip,rk3188-spdif"
- "rockchip,rk3228-spdif"
- "rockchip,rk3288-spdif"
- "rockchip,rk3328-spdif"
- "rockchip,rk3366-spdif"
- "rockchip,rk3368-spdif"
- "rockchip,rk3399-spdif"
- reg: physical base address of the controller and length of memory mapped
region.
- interrupts: should contain the SPDIF interrupt.
- dmas: DMA specifiers for tx dma. See the DMA client binding,
Documentation/devicetree/bindings/dma/dma.txt
- dma-names: should be "tx"
- clocks: a list of phandle + clock-specifier pairs, one for each entry
in clock-names.
- clock-names: should contain following:
- "hclk": clock for SPDIF controller
- "mclk" : clock for SPDIF bus
Required properties on RK3288:
- rockchip,grf: the phandle of the syscon node for the general register
file (GRF)
Example for the rk3188 SPDIF controller:
spdif: spdif@1011e000 {
compatible = "rockchip,rk3188-spdif", "rockchip,rk3066-spdif";
reg = <0x1011e000 0x2000>;
interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&dmac1_s 8>;
dma-names = "tx";
clock-names = "hclk", "mclk";
clocks = <&cru HCLK_SPDIF>, <&cru SCLK_SPDIF>;
#sound-dai-cells = <0>;
};
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/rockchip-spdif.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Rockchip SPDIF transceiver
description:
The S/PDIF audio block is a stereo transceiver that allows the
processor to receive and transmit digital audio via a coaxial or
fibre cable.
maintainers:
- Heiko Stuebner <heiko@sntech.de>
properties:
compatible:
oneOf:
- const: rockchip,rk3066-spdif
- const: rockchip,rk3228-spdif
- const: rockchip,rk3328-spdif
- const: rockchip,rk3366-spdif
- const: rockchip,rk3368-spdif
- const: rockchip,rk3399-spdif
- items:
- enum:
- rockchip,rk3188-spdif
- rockchip,rk3288-spdif
- const: rockchip,rk3066-spdif
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
items:
- description: clock for SPDIF bus
- description: clock for SPDIF controller
clock-names:
items:
- const: mclk
- const: hclk
dmas:
maxItems: 1
dma-names:
const: tx
power-domains:
maxItems: 1
rockchip,grf:
$ref: /schemas/types.yaml#/definitions/phandle
description:
The phandle of the syscon node for the GRF register.
Required property on RK3288.
"#sound-dai-cells":
const: 0
required:
- compatible
- reg
- interrupts
- clocks
- clock-names
- dmas
- dma-names
- "#sound-dai-cells"
if:
properties:
compatible:
contains:
const: rockchip,rk3288-spdif
then:
required:
- rockchip,grf
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/rk3188-cru.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
spdif: spdif@1011e000 {
compatible = "rockchip,rk3188-spdif", "rockchip,rk3066-spdif";
reg = <0x1011e000 0x2000>;
interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cru SCLK_SPDIF>, <&cru HCLK_SPDIF>;
clock-names = "mclk", "hclk";
dmas = <&dmac1_s 8>;
dma-names = "tx";
#sound-dai-cells = <0>;
};
......@@ -351,7 +351,6 @@ struct snd_soc_dai {
/* bit field */
unsigned int probed:1;
unsigned int started[SNDRV_PCM_STREAM_LAST + 1];
};
static inline struct snd_soc_pcm_stream *
......
......@@ -790,6 +790,9 @@ struct snd_soc_dai_link {
const struct snd_soc_pcm_stream *params;
unsigned int num_params;
struct snd_soc_dapm_widget *playback_widget;
struct snd_soc_dapm_widget *capture_widget;
unsigned int dai_fmt; /* format to set on init */
enum snd_soc_dpcm_trigger trigger[2]; /* trigger type for DPCM */
......
......@@ -89,9 +89,9 @@ static int acp3x_5682_init(struct snd_soc_pcm_runtime *rtd)
}
snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_1, KEY_VOLUMEUP);
snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);
snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_3, KEY_VOICECOMMAND);
snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
ret = snd_soc_component_set_jack(component, &pco_jack, NULL);
if (ret) {
......
......@@ -1525,6 +1525,7 @@ config SND_SOC_WM8804_SPI
config SND_SOC_WM8900
tristate
depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8903
tristate "Wolfson Microelectronics WM8903 CODEC"
......@@ -1576,6 +1577,7 @@ config SND_SOC_WM8985
config SND_SOC_WM8988
tristate
depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8990
tristate
......@@ -1594,6 +1596,7 @@ config SND_SOC_WM8994
config SND_SOC_WM8995
tristate
depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8996
tristate
......
......@@ -142,14 +142,14 @@ static struct hdac_hdmi_pcm *
hdac_hdmi_get_pcm_from_cvt(struct hdac_hdmi_priv *hdmi,
struct hdac_hdmi_cvt *cvt)
{
struct hdac_hdmi_pcm *pcm = NULL;
struct hdac_hdmi_pcm *pcm;
list_for_each_entry(pcm, &hdmi->pcm_list, head) {
if (pcm->cvt == cvt)
break;
return pcm;
}
return pcm;
return NULL;
}
static void hdac_hdmi_jack_report(struct hdac_hdmi_pcm *pcm,
......
......@@ -1903,7 +1903,6 @@ const struct soc_enum madera_isrc_fsh[] = {
MADERA_ISRC4_FSH_SHIFT, 0xf,
MADERA_RATE_ENUM_SIZE,
madera_rate_text, madera_rate_val),
};
EXPORT_SYMBOL_GPL(madera_isrc_fsh);
......@@ -1924,7 +1923,6 @@ const struct soc_enum madera_isrc_fsl[] = {
MADERA_ISRC4_FSL_SHIFT, 0xf,
MADERA_RATE_ENUM_SIZE,
madera_rate_text, madera_rate_val),
};
EXPORT_SYMBOL_GPL(madera_isrc_fsl);
......@@ -1938,7 +1936,6 @@ const struct soc_enum madera_asrc1_rate[] = {
MADERA_ASYNC_RATE_ENUM_SIZE,
madera_rate_text + MADERA_SYNC_RATE_ENUM_SIZE,
madera_rate_val + MADERA_SYNC_RATE_ENUM_SIZE),
};
EXPORT_SYMBOL_GPL(madera_asrc1_rate);
......@@ -1964,7 +1961,6 @@ const struct soc_enum madera_asrc2_rate[] = {
MADERA_ASYNC_RATE_ENUM_SIZE,
madera_rate_text + MADERA_SYNC_RATE_ENUM_SIZE,
madera_rate_val + MADERA_SYNC_RATE_ENUM_SIZE),
};
EXPORT_SYMBOL_GPL(madera_asrc2_rate);
......
......@@ -1653,6 +1653,40 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
dev_err(&client->dev,
"Error %d initializing CHIP_CLK_CTRL\n", ret);
/* Mute everything to avoid pop from the following power-up */
ret = regmap_write(sgtl5000->regmap, SGTL5000_CHIP_ANA_CTRL,
SGTL5000_CHIP_ANA_CTRL_DEFAULT);
if (ret) {
dev_err(&client->dev,
"Error %d muting outputs via CHIP_ANA_CTRL\n", ret);
goto disable_clk;
}
/*
* If VAG is powered-on (e.g. from previous boot), it would be disabled
* by the write to ANA_POWER in later steps of the probe code. This
* may create a loud pop even with all outputs muted. The proper way
* to circumvent this is disabling the bit first and waiting the proper
* cool-down time.
*/
ret = regmap_read(sgtl5000->regmap, SGTL5000_CHIP_ANA_POWER, &value);
if (ret) {
dev_err(&client->dev, "Failed to read ANA_POWER: %d\n", ret);
goto disable_clk;
}
if (value & SGTL5000_VAG_POWERUP) {
ret = regmap_update_bits(sgtl5000->regmap,
SGTL5000_CHIP_ANA_POWER,
SGTL5000_VAG_POWERUP,
0);
if (ret) {
dev_err(&client->dev, "Error %d disabling VAG\n", ret);
goto disable_clk;
}
msleep(SGTL5000_VAG_POWERDOWN_DELAY);
}
/* Follow section 2.2.1.1 of AN3663 */
ana_pwr = SGTL5000_ANA_POWER_DEFAULT;
if (sgtl5000->num_supplies <= VDDD) {
......
......@@ -233,6 +233,7 @@
/*
* SGTL5000_CHIP_ANA_CTRL
*/
#define SGTL5000_CHIP_ANA_CTRL_DEFAULT 0x0133
#define SGTL5000_LINE_OUT_MUTE 0x0100
#define SGTL5000_HP_SEL_MASK 0x0040
#define SGTL5000_HP_SEL_SHIFT 6
......
......@@ -820,8 +820,10 @@ static int tas571x_i2c_probe(struct i2c_client *client,
priv->regmap = devm_regmap_init(dev, NULL, client,
priv->chip->regmap_config);
if (IS_ERR(priv->regmap))
return PTR_ERR(priv->regmap);
if (IS_ERR(priv->regmap)) {
ret = PTR_ERR(priv->regmap);
goto disable_regs;
}
priv->pdn_gpio = devm_gpiod_get_optional(dev, "pdn", GPIOD_OUT_LOW);
if (IS_ERR(priv->pdn_gpio)) {
......@@ -845,7 +847,7 @@ static int tas571x_i2c_probe(struct i2c_client *client,
ret = regmap_write(priv->regmap, TAS571X_OSC_TRIM_REG, 0);
if (ret)
return ret;
goto disable_regs;
usleep_range(50000, 60000);
......@@ -861,12 +863,20 @@ static int tas571x_i2c_probe(struct i2c_client *client,
*/
ret = regmap_update_bits(priv->regmap, TAS571X_MVOL_REG, 1, 0);
if (ret)
return ret;
goto disable_regs;
}
return devm_snd_soc_register_component(&client->dev,
ret = devm_snd_soc_register_component(&client->dev,
&priv->component_driver,
&tas571x_dai, 1);
if (ret)
goto disable_regs;
return ret;
disable_regs:
regulator_bulk_disable(priv->chip->num_supply_names, priv->supplies);
return ret;
}
static int tas571x_i2c_remove(struct i2c_client *client)
......
......@@ -860,8 +860,7 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream,
wm8960->is_stream_in_use[tx] = true;
if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON &&
!wm8960->is_stream_in_use[!tx])
if (!wm8960->is_stream_in_use[!tx])
return wm8960_configure_clocking(component);
return 0;
......
......@@ -394,6 +394,7 @@ static struct sdw_dpn_prop wsa_sink_dpn_prop[WSA881X_MAX_SWR_PORTS] = {
.min_ch = 1,
.max_ch = 1,
.simple_ch_prep_sm = true,
.read_only_wordlength = true,
}, {
/* COMP */
.num = 2,
......@@ -401,6 +402,7 @@ static struct sdw_dpn_prop wsa_sink_dpn_prop[WSA881X_MAX_SWR_PORTS] = {
.min_ch = 1,
.max_ch = 1,
.simple_ch_prep_sm = true,
.read_only_wordlength = true,
}, {
/* BOOST */
.num = 3,
......@@ -408,6 +410,7 @@ static struct sdw_dpn_prop wsa_sink_dpn_prop[WSA881X_MAX_SWR_PORTS] = {
.min_ch = 1,
.max_ch = 1,
.simple_ch_prep_sm = true,
.read_only_wordlength = true,
}, {
/* VISENSE */
.num = 4,
......@@ -415,6 +418,7 @@ static struct sdw_dpn_prop wsa_sink_dpn_prop[WSA881X_MAX_SWR_PORTS] = {
.min_ch = 1,
.max_ch = 1,
.simple_ch_prep_sm = true,
.read_only_wordlength = true,
}
};
......
......@@ -113,14 +113,6 @@ static const struct snd_soc_acpi_adr_device rt1308_1_adr[] = {
}
};
static const struct snd_soc_acpi_adr_device rt1308_2_adr[] = {
{
.adr = 0x000210025D130800,
.num_endpoints = 1,
.endpoints = &single_endpoint,
}
};
static const struct snd_soc_acpi_adr_device rt1308_1_group1_adr[] = {
{
.adr = 0x000110025D130800,
......
......@@ -87,14 +87,6 @@ static const struct snd_soc_acpi_adr_device rt1308_1_adr[] = {
}
};
static const struct snd_soc_acpi_adr_device rt1308_2_adr[] = {
{
.adr = 0x000210025D130800,
.num_endpoints = 1,
.endpoints = &single_endpoint,
}
};
static const struct snd_soc_acpi_adr_device rt1308_1_group1_adr[] = {
{
.adr = 0x000110025D130800,
......
......@@ -338,8 +338,10 @@ static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np,
if (axg_card_cpu_is_tdm_iface(dai_link->cpus->of_node))
ret = axg_card_parse_tdm(card, np, index);
else if (axg_card_cpu_is_codec(dai_link->cpus->of_node))
else if (axg_card_cpu_is_codec(dai_link->cpus->of_node)) {
dai_link->params = &codec_params;
dai_link->no_pcm = 0; /* link is not a DPCM BE */
}
return ret;
}
......
......@@ -108,8 +108,10 @@ static int gx_card_add_link(struct snd_soc_card *card, struct device_node *np,
ret = gx_card_parse_i2s(card, np, index);
/* Or apply codec to codec params if necessary */
else if (gx_card_cpu_identify(dai_link->cpus, "CODEC CTRL"))
else if (gx_card_cpu_identify(dai_link->cpus, "CODEC CTRL")) {
dai_link->params = &codec_params;
dai_link->no_pcm = 0; /* link is not a DPCM BE */
}
return ret;
}
......
......@@ -116,10 +116,8 @@ static int apq8096_platform_probe(struct platform_device *pdev)
card->dev = dev;
dev_set_drvdata(dev, card);
ret = qcom_snd_parse_of(card);
if (ret) {
dev_err(dev, "Error parsing OF data\n");
if (ret)
goto err;
}
apq8096_add_be_ops(card);
ret = snd_soc_register_card(card);
......
......@@ -902,6 +902,8 @@ static struct snd_soc_dai_driver q6afe_dais[] = {
SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE,
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 48000,
},
......@@ -917,6 +919,8 @@ static struct snd_soc_dai_driver q6afe_dais[] = {
SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE,
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 48000,
},
......@@ -931,6 +935,8 @@ static struct snd_soc_dai_driver q6afe_dais[] = {
.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 48000,
},
......@@ -946,6 +952,8 @@ static struct snd_soc_dai_driver q6afe_dais[] = {
SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE,
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 48000,
},
......@@ -960,6 +968,8 @@ static struct snd_soc_dai_driver q6afe_dais[] = {
.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 48000,
},
......@@ -975,6 +985,8 @@ static struct snd_soc_dai_driver q6afe_dais[] = {
SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE,
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 48000,
},
......@@ -989,6 +1001,8 @@ static struct snd_soc_dai_driver q6afe_dais[] = {
.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 48000,
},
......@@ -1004,6 +1018,8 @@ static struct snd_soc_dai_driver q6afe_dais[] = {
SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE,
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 48000,
},
......
......@@ -559,10 +559,8 @@ static int sdm845_snd_platform_probe(struct platform_device *pdev)
card->dev = dev;
dev_set_drvdata(dev, card);
ret = qcom_snd_parse_of(card);
if (ret) {
dev_err(dev, "Error parsing OF data\n");
if (ret)
goto parse_dt_fail;
}
data->card = card;
snd_soc_card_set_drvdata(card, data);
......
......@@ -656,60 +656,6 @@ void s3c_i2sv2_cleanup(struct snd_soc_dai *dai,
}
EXPORT_SYMBOL_GPL(s3c_i2sv2_cleanup);
#ifdef CONFIG_PM
static int s3c2412_i2s_suspend(struct snd_soc_dai *dai)
{
struct s3c_i2sv2_info *i2s = to_info(dai);
u32 iismod;
if (dai->active) {
i2s->suspend_iismod = readl(i2s->regs + S3C2412_IISMOD);
i2s->suspend_iiscon = readl(i2s->regs + S3C2412_IISCON);
i2s->suspend_iispsr = readl(i2s->regs + S3C2412_IISPSR);
/* some basic suspend checks */
iismod = readl(i2s->regs + S3C2412_IISMOD);
if (iismod & S3C2412_IISCON_RXDMA_ACTIVE)
pr_warn("%s: RXDMA active?\n", __func__);
if (iismod & S3C2412_IISCON_TXDMA_ACTIVE)
pr_warn("%s: TXDMA active?\n", __func__);
if (iismod & S3C2412_IISCON_IIS_ACTIVE)
pr_warn("%s: IIS active\n", __func__);
}
return 0;
}
static int s3c2412_i2s_resume(struct snd_soc_dai *dai)
{
struct s3c_i2sv2_info *i2s = to_info(dai);
pr_info("dai_active %d, IISMOD %08x, IISCON %08x\n",
dai->active, i2s->suspend_iismod, i2s->suspend_iiscon);
if (dai->active) {
writel(i2s->suspend_iiscon, i2s->regs + S3C2412_IISCON);
writel(i2s->suspend_iismod, i2s->regs + S3C2412_IISMOD);
writel(i2s->suspend_iispsr, i2s->regs + S3C2412_IISPSR);
writel(S3C2412_IISFIC_RXFLUSH | S3C2412_IISFIC_TXFLUSH,
i2s->regs + S3C2412_IISFIC);
ndelay(250);
writel(0x0, i2s->regs + S3C2412_IISFIC);
}
return 0;
}
#else
#define s3c2412_i2s_suspend NULL
#define s3c2412_i2s_resume NULL
#endif
int s3c_i2sv2_register_component(struct device *dev, int id,
const struct snd_soc_component_driver *cmp_drv,
struct snd_soc_dai_driver *dai_drv)
......@@ -727,9 +673,6 @@ int s3c_i2sv2_register_component(struct device *dev, int id,
if (!ops->delay)
ops->delay = s3c2412_i2s_delay;
dai_drv->suspend = s3c2412_i2s_suspend;
dai_drv->resume = s3c2412_i2s_resume;
return devm_snd_soc_register_component(dev, cmp_drv, dai_drv, 1);
}
EXPORT_SYMBOL_GPL(s3c_i2sv2_register_component);
......
......@@ -117,6 +117,60 @@ static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream,
return 0;
}
#ifdef CONFIG_PM
static int s3c2412_i2s_suspend(struct snd_soc_component *component)
{
struct s3c_i2sv2_info *i2s = snd_soc_component_get_drvdata(component);
u32 iismod;
if (component->active) {
i2s->suspend_iismod = readl(i2s->regs + S3C2412_IISMOD);
i2s->suspend_iiscon = readl(i2s->regs + S3C2412_IISCON);
i2s->suspend_iispsr = readl(i2s->regs + S3C2412_IISPSR);
/* some basic suspend checks */
iismod = readl(i2s->regs + S3C2412_IISMOD);
if (iismod & S3C2412_IISCON_RXDMA_ACTIVE)
pr_warn("%s: RXDMA active?\n", __func__);
if (iismod & S3C2412_IISCON_TXDMA_ACTIVE)
pr_warn("%s: TXDMA active?\n", __func__);
if (iismod & S3C2412_IISCON_IIS_ACTIVE)
pr_warn("%s: IIS active\n", __func__);
}
return 0;
}
static int s3c2412_i2s_resume(struct snd_soc_component *component)
{
struct s3c_i2sv2_info *i2s = snd_soc_component_get_drvdata(component);
pr_info("component_active %d, IISMOD %08x, IISCON %08x\n",
component->active, i2s->suspend_iismod, i2s->suspend_iiscon);
if (component->active) {
writel(i2s->suspend_iiscon, i2s->regs + S3C2412_IISCON);
writel(i2s->suspend_iismod, i2s->regs + S3C2412_IISMOD);
writel(i2s->suspend_iispsr, i2s->regs + S3C2412_IISPSR);
writel(S3C2412_IISFIC_RXFLUSH | S3C2412_IISFIC_TXFLUSH,
i2s->regs + S3C2412_IISFIC);
ndelay(250);
writel(0x0, i2s->regs + S3C2412_IISFIC);
}
return 0;
}
#else
#define s3c2412_i2s_suspend NULL
#define s3c2412_i2s_resume NULL
#endif
#define S3C2412_I2S_RATES \
(SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
......@@ -146,6 +200,8 @@ static struct snd_soc_dai_driver s3c2412_i2s_dai = {
static const struct snd_soc_component_driver s3c2412_i2s_component = {
.name = "s3c2412-i2s",
.suspend = s3c2412_i2s_suspend,
.resume = s3c2412_i2s_resume,
};
static int s3c2412_iis_dev_probe(struct platform_device *pdev)
......
......@@ -594,10 +594,16 @@ static int rsnd_ssi_stop(struct rsnd_mod *mod,
* Capture: It might not receave data. Do nothing
*/
if (rsnd_io_is_play(io)) {
rsnd_mod_write(mod, SSICR, cr | EN);
rsnd_mod_write(mod, SSICR, cr | ssi->cr_en);
rsnd_ssi_status_check(mod, DIRQ);
}
/* In multi-SSI mode, stop is performed by setting ssi0129 in
* SSI_CONTROL to 0 (in rsnd_ssio_stop_gen2). Do nothing here.
*/
if (rsnd_ssi_multi_slaves_runtime(io))
return 0;
/*
* disable SSI,
* and, wait idle state
......@@ -737,6 +743,9 @@ static void rsnd_ssi_parent_attach(struct rsnd_mod *mod,
if (!rsnd_rdai_is_clk_master(rdai))
return;
if (rsnd_ssi_is_multi_slave(mod, io))
return;
switch (rsnd_mod_id(mod)) {
case 1:
case 2:
......
......@@ -221,7 +221,7 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod,
i;
for_each_rsnd_mod_array(i, pos, io, rsnd_ssi_array) {
shift = (i * 4) + 16;
shift = (i * 4) + 20;
val = (val & ~(0xF << shift)) |
rsnd_mod_id(pos) << shift;
}
......
......@@ -295,24 +295,17 @@ int snd_soc_dai_startup(struct snd_soc_dai *dai,
{
int ret = 0;
if (!dai->started[substream->stream] &&
dai->driver->ops->startup)
if (dai->driver->ops->startup)
ret = dai->driver->ops->startup(substream, dai);
if (ret == 0)
dai->started[substream->stream] = 1;
return ret;
}
void snd_soc_dai_shutdown(struct snd_soc_dai *dai,
struct snd_pcm_substream *substream)
{
if (dai->started[substream->stream] &&
dai->driver->ops->shutdown)
if (dai->driver->ops->shutdown)
dai->driver->ops->shutdown(substream, dai);
dai->started[substream->stream] = 0;
}
int snd_soc_dai_prepare(struct snd_soc_dai *dai,
......
......@@ -423,7 +423,7 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,
memset(&template, 0, sizeof(template));
template.reg = e->reg;
template.mask = e->mask << e->shift_l;
template.mask = e->mask;
template.shift = e->shift_l;
template.off_val = snd_soc_enum_item_to_val(e, 0);
template.on_val = template.off_val;
......@@ -546,8 +546,22 @@ static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol,
if (data->value == value)
return false;
if (data->widget)
if (data->widget) {
switch (dapm_kcontrol_get_wlist(kcontrol)->widgets[0]->id) {
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
case snd_soc_dapm_mixer_named_ctl:
data->widget->on_val = value & data->widget->mask;
break;
case snd_soc_dapm_demux:
case snd_soc_dapm_mux:
data->widget->on_val = value >> data->widget->shift;
break;
default:
data->widget->on_val = value;
break;
}
}
data->value = value;
......@@ -4165,6 +4179,8 @@ snd_soc_dapm_new_dai(struct snd_soc_card *card,
w = snd_soc_dapm_new_control_unlocked(&card->dapm, &template);
if (IS_ERR(w)) {
ret = PTR_ERR(w);
dev_err(rtd->dev, "ASoC: Failed to create %s widget: %d\n",
link_name, ret);
goto outfree_kcontrol_news;
}
......@@ -4283,52 +4299,58 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card)
return 0;
}
static void dapm_add_valid_dai_widget(struct snd_soc_card *card,
static void dapm_connect_dai_routes(struct snd_soc_dapm_context *dapm,
struct snd_soc_dai *src_dai,
struct snd_soc_dapm_widget *src,
struct snd_soc_dapm_widget *dai,
struct snd_soc_dai *sink_dai,
struct snd_soc_dapm_widget *sink)
{
dev_dbg(dapm->dev, "connected DAI link %s:%s -> %s:%s\n",
src_dai->component->name, src->name,
sink_dai->component->name, sink->name);
if (dai) {
snd_soc_dapm_add_path(dapm, src, dai, NULL, NULL);
src = dai;
}
snd_soc_dapm_add_path(dapm, src, sink, NULL, NULL);
}
static void dapm_connect_dai_pair(struct snd_soc_card *card,
struct snd_soc_pcm_runtime *rtd,
struct snd_soc_dai *codec_dai,
struct snd_soc_dai *cpu_dai)
{
struct snd_soc_dapm_widget *playback = NULL, *capture = NULL;
struct snd_soc_dapm_widget *codec, *playback_cpu, *capture_cpu;
struct snd_soc_dai_link *dai_link = rtd->dai_link;
struct snd_soc_dapm_widget *dai, *codec, *playback_cpu, *capture_cpu;
struct snd_pcm_substream *substream;
struct snd_pcm_str *streams = rtd->pcm->streams;
if (rtd->dai_link->params) {
if (dai_link->params) {
playback_cpu = cpu_dai->capture_widget;
capture_cpu = cpu_dai->playback_widget;
} else {
playback = cpu_dai->playback_widget;
capture = cpu_dai->capture_widget;
playback_cpu = playback;
capture_cpu = capture;
playback_cpu = cpu_dai->playback_widget;
capture_cpu = cpu_dai->capture_widget;
}
/* connect BE DAI playback if widgets are valid */
codec = codec_dai->playback_widget;
if (playback_cpu && codec) {
if (!playback) {
if (dai_link->params && !dai_link->playback_widget) {
substream = streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
playback = snd_soc_dapm_new_dai(card, substream,
"playback");
if (IS_ERR(playback)) {
dev_err(rtd->dev,
"ASoC: Failed to create DAI %s: %ld\n",
codec_dai->name,
PTR_ERR(playback));
dai = snd_soc_dapm_new_dai(card, substream, "playback");
if (IS_ERR(dai))
goto capture;
dai_link->playback_widget = dai;
}
snd_soc_dapm_add_path(&card->dapm, playback_cpu,
playback, NULL, NULL);
}
dev_dbg(rtd->dev, "connected DAI link %s:%s -> %s:%s\n",
cpu_dai->component->name, playback_cpu->name,
codec_dai->component->name, codec->name);
snd_soc_dapm_add_path(&card->dapm, playback, codec,
NULL, NULL);
dapm_connect_dai_routes(&card->dapm, cpu_dai, playback_cpu,
dai_link->playback_widget,
codec_dai, codec);
}
capture:
......@@ -4336,52 +4358,20 @@ static void dapm_add_valid_dai_widget(struct snd_soc_card *card,
codec = codec_dai->capture_widget;
if (codec && capture_cpu) {
if (!capture) {
if (dai_link->params && !dai_link->capture_widget) {
substream = streams[SNDRV_PCM_STREAM_CAPTURE].substream;
capture = snd_soc_dapm_new_dai(card, substream,
"capture");
if (IS_ERR(capture)) {
dev_err(rtd->dev,
"ASoC: Failed to create DAI %s: %ld\n",
codec_dai->name,
PTR_ERR(capture));
dai = snd_soc_dapm_new_dai(card, substream, "capture");
if (IS_ERR(dai))
return;
dai_link->capture_widget = dai;
}
snd_soc_dapm_add_path(&card->dapm, capture,
capture_cpu, NULL, NULL);
}
dev_dbg(rtd->dev, "connected DAI link %s:%s -> %s:%s\n",
codec_dai->component->name, codec->name,
cpu_dai->component->name, capture_cpu->name);
snd_soc_dapm_add_path(&card->dapm, codec, capture,
NULL, NULL);
dapm_connect_dai_routes(&card->dapm, codec_dai, codec,
dai_link->capture_widget,
cpu_dai, capture_cpu);
}
}
static void dapm_connect_dai_link_widgets(struct snd_soc_card *card,
struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dai *codec_dai;
int i;
if (rtd->num_cpus == 1) {
for_each_rtd_codec_dais(rtd, i, codec_dai)
dapm_add_valid_dai_widget(card, rtd, codec_dai,
rtd->cpu_dais[0]);
} else if (rtd->num_codecs == rtd->num_cpus) {
for_each_rtd_codec_dais(rtd, i, codec_dai)
dapm_add_valid_dai_widget(card, rtd, codec_dai,
rtd->cpu_dais[i]);
} else {
dev_err(card->dev,
"N cpus to M codecs link is not supported yet\n");
}
}
static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream,
int event)
{
......@@ -4422,6 +4412,8 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream,
void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card)
{
struct snd_soc_pcm_runtime *rtd;
struct snd_soc_dai *codec_dai;
int i;
/* for each BE DAI link... */
for_each_card_rtds(card, rtd) {
......@@ -4432,7 +4424,18 @@ void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card)
if (rtd->dai_link->dynamic)
continue;
dapm_connect_dai_link_widgets(card, rtd);
if (rtd->num_cpus == 1) {
for_each_rtd_codec_dais(rtd, i, codec_dai)
dapm_connect_dai_pair(card, rtd, codec_dai,
rtd->cpu_dais[0]);
} else if (rtd->num_codecs == rtd->num_cpus) {
for_each_rtd_codec_dais(rtd, i, codec_dai)
dapm_connect_dai_pair(card, rtd, codec_dai,
rtd->cpu_dais[i]);
} else {
dev_err(card->dev,
"N cpus to M codecs link is not supported yet\n");
}
}
}
......
......@@ -2911,8 +2911,17 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
int i;
if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) {
playback = rtd->dai_link->dpcm_playback;
capture = rtd->dai_link->dpcm_capture;
cpu_dai = asoc_rtd_to_cpu(rtd, 0);
if (rtd->num_cpus > 1) {
dev_err(rtd->dev,
"DPCM doesn't support Multi CPU yet\n");
return -EINVAL;
}
playback = rtd->dai_link->dpcm_playback &&
snd_soc_dai_stream_valid(cpu_dai, SNDRV_PCM_STREAM_PLAYBACK);
capture = rtd->dai_link->dpcm_capture &&
snd_soc_dai_stream_valid(cpu_dai, SNDRV_PCM_STREAM_CAPTURE);
} else {
/* Adapt stream for codec2codec links */
int cpu_capture = rtd->dai_link->params ?
......
......@@ -894,7 +894,13 @@ static int soc_tplg_dmixer_create(struct soc_tplg *tplg, unsigned int count,
}
/* create any TLV data */
soc_tplg_create_tlv(tplg, &kc, &mc->hdr);
err = soc_tplg_create_tlv(tplg, &kc, &mc->hdr);
if (err < 0) {
dev_err(tplg->dev, "ASoC: failed to create TLV %s\n",
mc->hdr.name);
kfree(sm);
continue;
}
/* pass control to driver for optional further init */
err = soc_tplg_init_kcontrol(tplg, &kc,
......@@ -1118,6 +1124,7 @@ static int soc_tplg_kcontrol_elems_load(struct soc_tplg *tplg,
struct snd_soc_tplg_hdr *hdr)
{
struct snd_soc_tplg_ctl_hdr *control_hdr;
int ret;
int i;
if (tplg->pass != SOC_TPLG_PASS_MIXER) {
......@@ -1146,7 +1153,7 @@ static int soc_tplg_kcontrol_elems_load(struct soc_tplg *tplg,
case SND_SOC_TPLG_CTL_RANGE:
case SND_SOC_TPLG_DAPM_CTL_VOLSW:
case SND_SOC_TPLG_DAPM_CTL_PIN:
soc_tplg_dmixer_create(tplg, 1,
ret = soc_tplg_dmixer_create(tplg, 1,
le32_to_cpu(hdr->payload_size));
break;
case SND_SOC_TPLG_CTL_ENUM:
......@@ -1154,17 +1161,22 @@ static int soc_tplg_kcontrol_elems_load(struct soc_tplg *tplg,
case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE:
case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT:
case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
soc_tplg_denum_create(tplg, 1,
ret = soc_tplg_denum_create(tplg, 1,
le32_to_cpu(hdr->payload_size));
break;
case SND_SOC_TPLG_CTL_BYTES:
soc_tplg_dbytes_create(tplg, 1,
ret = soc_tplg_dbytes_create(tplg, 1,
le32_to_cpu(hdr->payload_size));
break;
default:
soc_bind_err(tplg, control_hdr, i);
return -EINVAL;
}
if (ret < 0) {
dev_err(tplg->dev, "ASoC: invalid control\n");
return ret;
}
}
return 0;
......@@ -1272,7 +1284,9 @@ static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg,
routes[i]->dobj.index = tplg->index;
list_add(&routes[i]->dobj.list, &tplg->comp->dobj_list);
soc_tplg_add_route(tplg, routes[i]);
ret = soc_tplg_add_route(tplg, routes[i]);
if (ret < 0)
break;
/* add route, but keep going if some fail */
snd_soc_dapm_add_routes(dapm, routes[i], 1);
......@@ -1355,7 +1369,13 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_dmixer_create(
}
/* create any TLV data */
soc_tplg_create_tlv(tplg, &kc[i], &mc->hdr);
err = soc_tplg_create_tlv(tplg, &kc[i], &mc->hdr);
if (err < 0) {
dev_err(tplg->dev, "ASoC: failed to create TLV %s\n",
mc->hdr.name);
kfree(sm);
continue;
}
/* pass control to driver for optional further init */
err = soc_tplg_init_kcontrol(tplg, &kc[i],
......@@ -1766,10 +1786,13 @@ static int soc_tplg_dapm_complete(struct soc_tplg *tplg)
return 0;
}
static void set_stream_info(struct snd_soc_pcm_stream *stream,
static int set_stream_info(struct snd_soc_pcm_stream *stream,
struct snd_soc_tplg_stream_caps *caps)
{
stream->stream_name = kstrdup(caps->name, GFP_KERNEL);
if (!stream->stream_name)
return -ENOMEM;
stream->channels_min = le32_to_cpu(caps->channels_min);
stream->channels_max = le32_to_cpu(caps->channels_max);
stream->rates = le32_to_cpu(caps->rates);
......@@ -1777,6 +1800,8 @@ static void set_stream_info(struct snd_soc_pcm_stream *stream,
stream->rate_max = le32_to_cpu(caps->rate_max);
stream->formats = le64_to_cpu(caps->formats);
stream->sig_bits = le32_to_cpu(caps->sig_bits);
return 0;
}
static void set_dai_flags(struct snd_soc_dai_driver *dai_drv,
......@@ -1812,20 +1837,29 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg,
if (dai_drv == NULL)
return -ENOMEM;
if (strlen(pcm->dai_name))
if (strlen(pcm->dai_name)) {
dai_drv->name = kstrdup(pcm->dai_name, GFP_KERNEL);
if (!dai_drv->name) {
ret = -ENOMEM;
goto err;
}
}
dai_drv->id = le32_to_cpu(pcm->dai_id);
if (pcm->playback) {
stream = &dai_drv->playback;
caps = &pcm->caps[SND_SOC_TPLG_STREAM_PLAYBACK];
set_stream_info(stream, caps);
ret = set_stream_info(stream, caps);
if (ret < 0)
goto err;
}
if (pcm->capture) {
stream = &dai_drv->capture;
caps = &pcm->caps[SND_SOC_TPLG_STREAM_CAPTURE];
set_stream_info(stream, caps);
ret = set_stream_info(stream, caps);
if (ret < 0)
goto err;
}
if (pcm->compress)
......@@ -1835,11 +1869,7 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg,
ret = soc_tplg_dai_load(tplg, dai_drv, pcm, NULL);
if (ret < 0) {
dev_err(tplg->comp->dev, "ASoC: DAI loading failed\n");
kfree(dai_drv->playback.stream_name);
kfree(dai_drv->capture.stream_name);
kfree(dai_drv->name);
kfree(dai_drv);
return ret;
goto err;
}
dai_drv->dobj.index = tplg->index;
......@@ -1860,6 +1890,14 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg,
return ret;
}
return 0;
err:
kfree(dai_drv->playback.stream_name);
kfree(dai_drv->capture.stream_name);
kfree(dai_drv->name);
kfree(dai_drv);
return ret;
}
......@@ -1916,11 +1954,20 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg,
if (strlen(pcm->pcm_name)) {
link->name = kstrdup(pcm->pcm_name, GFP_KERNEL);
link->stream_name = kstrdup(pcm->pcm_name, GFP_KERNEL);
if (!link->name || !link->stream_name) {
ret = -ENOMEM;
goto err;
}
}
link->id = le32_to_cpu(pcm->pcm_id);
if (strlen(pcm->dai_name))
if (strlen(pcm->dai_name)) {
link->cpus->dai_name = kstrdup(pcm->dai_name, GFP_KERNEL);
if (!link->cpus->dai_name) {
ret = -ENOMEM;
goto err;
}
}
link->codecs->name = "snd-soc-dummy";
link->codecs->dai_name = "snd-soc-dummy-dai";
......@@ -2088,7 +2135,9 @@ static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg,
_pcm = pcm;
} else {
abi_match = false;
pcm_new_ver(tplg, pcm, &_pcm);
ret = pcm_new_ver(tplg, pcm, &_pcm);
if (ret < 0)
return ret;
}
/* create the FE DAIs and DAI links */
......@@ -2436,13 +2485,17 @@ static int soc_tplg_dai_config(struct soc_tplg *tplg,
if (d->playback) {
stream = &dai_drv->playback;
caps = &d->caps[SND_SOC_TPLG_STREAM_PLAYBACK];
set_stream_info(stream, caps);
ret = set_stream_info(stream, caps);
if (ret < 0)
goto err;
}
if (d->capture) {
stream = &dai_drv->capture;
caps = &d->caps[SND_SOC_TPLG_STREAM_CAPTURE];
set_stream_info(stream, caps);
ret = set_stream_info(stream, caps);
if (ret < 0)
goto err;
}
if (d->flag_mask)
......@@ -2454,10 +2507,15 @@ static int soc_tplg_dai_config(struct soc_tplg *tplg,
ret = soc_tplg_dai_load(tplg, dai_drv, NULL, dai);
if (ret < 0) {
dev_err(tplg->comp->dev, "ASoC: DAI loading failed\n");
return ret;
goto err;
}
return 0;
err:
kfree(dai_drv->playback.stream_name);
kfree(dai_drv->capture.stream_name);
return ret;
}
/* load physical DAI elements */
......@@ -2466,7 +2524,7 @@ static int soc_tplg_dai_elems_load(struct soc_tplg *tplg,
{
struct snd_soc_tplg_dai *dai;
int count;
int i;
int i, ret;
count = le32_to_cpu(hdr->count);
......@@ -2481,7 +2539,12 @@ static int soc_tplg_dai_elems_load(struct soc_tplg *tplg,
return -EINVAL;
}
soc_tplg_dai_config(tplg, dai);
ret = soc_tplg_dai_config(tplg, dai);
if (ret < 0) {
dev_err(tplg->dev, "ASoC: failed to configure DAI\n");
return ret;
}
tplg->pos += (sizeof(*dai) + le32_to_cpu(dai->priv.size));
}
......@@ -2589,7 +2652,7 @@ static int soc_valid_header(struct soc_tplg *tplg,
}
/* big endian firmware objects not supported atm */
if (hdr->magic == SOC_TPLG_MAGIC_BIG_ENDIAN) {
if (le32_to_cpu(hdr->magic) == SOC_TPLG_MAGIC_BIG_ENDIAN) {
dev_err(tplg->dev,
"ASoC: pass %d big endian not supported header got %x at offset 0x%lx size 0x%zx.\n",
tplg->pass, hdr->magic,
......
......@@ -567,9 +567,25 @@ static void bdw_set_mach_params(const struct snd_soc_acpi_mach *mach,
static struct snd_soc_dai_driver bdw_dai[] = {
{
.name = "ssp0-port",
.playback = {
.channels_min = 1,
.channels_max = 8,
},
.capture = {
.channels_min = 1,
.channels_max = 8,
},
},
{
.name = "ssp1-port",
.playback = {
.channels_min = 1,
.channels_max = 8,
},
.capture = {
.channels_min = 1,
.channels_max = 8,
},
},
};
......
......@@ -459,21 +459,69 @@ static void byt_set_mach_params(const struct snd_soc_acpi_mach *mach,
static struct snd_soc_dai_driver byt_dai[] = {
{
.name = "ssp0-port",
.playback = {
.channels_min = 1,
.channels_max = 8,
},
.capture = {
.channels_min = 1,
.channels_max = 8,
},
},
{
.name = "ssp1-port",
.playback = {
.channels_min = 1,
.channels_max = 8,
},
.capture = {
.channels_min = 1,
.channels_max = 8,
},
},
{
.name = "ssp2-port",
.playback = {
.channels_min = 1,
.channels_max = 8,
},
.capture = {
.channels_min = 1,
.channels_max = 8,
}
},
{
.name = "ssp3-port",
.playback = {
.channels_min = 1,
.channels_max = 8,
},
.capture = {
.channels_min = 1,
.channels_max = 8,
},
},
{
.name = "ssp4-port",
.playback = {
.channels_min = 1,
.channels_max = 8,
},
.capture = {
.channels_min = 1,
.channels_max = 8,
},
},
{
.name = "ssp5-port",
.playback = {
.channels_min = 1,
.channels_max = 8,
},
.capture = {
.channels_min = 1,
.channels_max = 8,
},
},
};
......
......@@ -837,7 +837,7 @@ static int stm32_sai_set_config(struct snd_soc_dai *cpu_dai,
cr1 = SAI_XCR1_DS_SET(SAI_DATASIZE_32);
break;
default:
dev_err(cpu_dai->dev, "Data format not supported");
dev_err(cpu_dai->dev, "Data format not supported\n");
return -EINVAL;
}
......@@ -1547,6 +1547,9 @@ static int stm32_sai_sub_probe(struct platform_device *pdev)
return ret;
}
if (STM_SAI_PROTOCOL_IS_SPDIF(sai))
conf = &stm32_sai_pcm_config_spdif;
ret = snd_dmaengine_pcm_register(&pdev->dev, conf, 0);
if (ret) {
if (ret != -EPROBE_DEFER)
......@@ -1556,15 +1559,10 @@ static int stm32_sai_sub_probe(struct platform_device *pdev)
ret = snd_soc_register_component(&pdev->dev, &stm32_component,
&sai->cpu_dai_drv, 1);
if (ret) {
if (ret)
snd_dmaengine_pcm_unregister(&pdev->dev);
return ret;
}
if (STM_SAI_PROTOCOL_IS_SPDIF(sai))
conf = &stm32_sai_pcm_config_spdif;
return 0;
return ret;
}
static int stm32_sai_sub_remove(struct platform_device *pdev)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment