SND_SOC_DAPM_OUTPUT("TX"),
};
- static const struct snd_soc_dapm_route hdmi_routes[] = {
- { "TX", NULL, "Playback" },
- };
-
enum {
DAI_ID_I2S = 0,
DAI_ID_SPDIF,
return snd_ctl_add(rtd->card->snd_card, kctl);
}
+ static int hdmi_dai_probe(struct snd_soc_dai *dai)
+ {
+ struct snd_soc_dapm_context *dapm;
+ struct snd_soc_dapm_route route = {
+ .sink = "TX",
+ .source = dai->driver->playback.stream_name,
+ };
+
+ dapm = snd_soc_component_get_dapm(dai->component);
+
+ return snd_soc_dapm_add_routes(dapm, &route, 1);
+ }
+
static const struct snd_soc_dai_driver hdmi_i2s_dai = {
.name = "i2s-hifi",
.id = DAI_ID_I2S,
+ .probe = hdmi_dai_probe,
.playback = {
.stream_name = "I2S Playback",
.channels_min = 2,
static const struct snd_soc_dai_driver hdmi_spdif_dai = {
.name = "spdif-hifi",
.id = DAI_ID_SPDIF,
+ .probe = hdmi_dai_probe,
.playback = {
.stream_name = "SPDIF Playback",
.channels_min = 2,
static const struct snd_soc_component_driver hdmi_driver = {
.dapm_widgets = hdmi_widgets,
.num_dapm_widgets = ARRAY_SIZE(hdmi_widgets),
- .dapm_routes = hdmi_routes,
- .num_dapm_routes = ARRAY_SIZE(hdmi_routes),
.of_xlate_dai_id = hdmi_of_xlate_dai_id,
.idle_bias_on = 1,
.use_pmdown_time = 1,
hcp->hcd = *hcd;
mutex_init(&hcp->current_stream_lock);
- hcp->daidrv = devm_kzalloc(dev, dai_count * sizeof(*hcp->daidrv),
+ hcp->daidrv = devm_kcalloc(dev, dai_count, sizeof(*hcp->daidrv),
GFP_KERNEL);
if (!hcp->daidrv)
return -ENOMEM;
{
.id = SND_AUDIOCODEC_BESPOKE,
.desc = {
- .max_ch = 1,
+ .max_ch = 8,
.sample_rates = { 16000 },
.num_sample_rates = 1,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
if (!root)
goto err;
- if (!debugfs_create_bool("booted", S_IRUGO, root, &dsp->booted))
+ if (!debugfs_create_bool("booted", 0444, root, &dsp->booted))
goto err;
- if (!debugfs_create_bool("running", S_IRUGO, root, &dsp->running))
+ if (!debugfs_create_bool("running", 0444, root, &dsp->running))
goto err;
- if (!debugfs_create_x32("fw_id", S_IRUGO, root, &dsp->fw_id))
+ if (!debugfs_create_x32("fw_id", 0444, root, &dsp->fw_id))
goto err;
- if (!debugfs_create_x32("fw_version", S_IRUGO, root,
- &dsp->fw_id_version))
+ if (!debugfs_create_x32("fw_version", 0444, root, &dsp->fw_id_version))
goto err;
for (i = 0; i < ARRAY_SIZE(wm_adsp_debugfs_fops); ++i) {
if (!debugfs_create_file(wm_adsp_debugfs_fops[i].name,
- S_IRUGO, root, dsp,
+ 0444, root, dsp,
&wm_adsp_debugfs_fops[i].fops))
goto err;
}
}
#endif
-static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
return 0;
}
+EXPORT_SYMBOL_GPL(wm_adsp_fw_get);
-static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
return ret;
}
+EXPORT_SYMBOL_GPL(wm_adsp_fw_put);
-static const struct soc_enum wm_adsp_fw_enum[] = {
+const struct soc_enum wm_adsp_fw_enum[] = {
SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
SOC_ENUM_SINGLE(0, 5, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
SOC_ENUM_SINGLE(0, 6, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
};
-
-const struct snd_kcontrol_new wm_adsp_fw_controls[] = {
- SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
- wm_adsp_fw_get, wm_adsp_fw_put),
- SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
- wm_adsp_fw_get, wm_adsp_fw_put),
- SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
- wm_adsp_fw_get, wm_adsp_fw_put),
- SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3],
- wm_adsp_fw_get, wm_adsp_fw_put),
- SOC_ENUM_EXT("DSP5 Firmware", wm_adsp_fw_enum[4],
- wm_adsp_fw_get, wm_adsp_fw_put),
- SOC_ENUM_EXT("DSP6 Firmware", wm_adsp_fw_enum[5],
- wm_adsp_fw_get, wm_adsp_fw_put),
- SOC_ENUM_EXT("DSP7 Firmware", wm_adsp_fw_enum[6],
- wm_adsp_fw_get, wm_adsp_fw_put),
-};
-EXPORT_SYMBOL_GPL(wm_adsp_fw_controls);
+EXPORT_SYMBOL_GPL(wm_adsp_fw_enum);
static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
int type)
int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2;
int skip = 0;
+ if (dsp->component->name_prefix)
+ avail -= strlen(dsp->component->name_prefix) + 1;
+
if (subname_len > avail)
skip = subname_len - avail;
if (ret)
return -EINVAL;
break;
+ case WMFW_CTL_TYPE_HOST_BUFFER:
+ ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk,
+ WMFW_CTL_FLAG_SYS |
+ WMFW_CTL_FLAG_VOLATILE |
+ WMFW_CTL_FLAG_READABLE,
+ 0);
+ if (ret)
+ return -EINVAL;
+ break;
default:
adsp_err(dsp, "Unknown control type: %d\n",
coeff_blk.ctl_type);
}
static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs,
+ const struct wm_adsp_region *mem,
unsigned int pos, unsigned int len)
{
void *alg;
+ unsigned int reg;
int ret;
__be32 val;
}
/* Read the terminator first to validate the length */
- ret = regmap_raw_read(dsp->regmap, pos + len, &val, sizeof(val));
+ reg = wm_adsp_region_to_reg(mem, pos + len);
+
+ ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val));
if (ret != 0) {
adsp_err(dsp, "Failed to read algorithm list end: %d\n",
ret);
if (be32_to_cpu(val) != 0xbedead)
adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbedead\n",
- pos + len, be32_to_cpu(val));
+ reg, be32_to_cpu(val));
- alg = kzalloc(len * 2, GFP_KERNEL | GFP_DMA);
+ /* Convert length from DSP words to bytes */
+ len *= sizeof(u32);
+
+ alg = kzalloc(len, GFP_KERNEL | GFP_DMA);
if (!alg)
return ERR_PTR(-ENOMEM);
- ret = regmap_raw_read(dsp->regmap, pos, alg, len * 2);
+ reg = wm_adsp_region_to_reg(mem, pos);
+
+ ret = regmap_raw_read(dsp->regmap, reg, alg, len);
if (ret != 0) {
adsp_err(dsp, "Failed to read algorithm list: %d\n", ret);
kfree(alg);
if (IS_ERR(alg_region))
return PTR_ERR(alg_region);
- pos = sizeof(adsp1_id) / 2;
- len = (sizeof(*adsp1_alg) * n_algs) / 2;
+ /* Calculate offset and length in DSP words */
+ pos = sizeof(adsp1_id) / sizeof(u32);
+ len = (sizeof(*adsp1_alg) * n_algs) / sizeof(u32);
- adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len);
+ adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len);
if (IS_ERR(adsp1_alg))
return PTR_ERR(adsp1_alg);
if (IS_ERR(alg_region))
return PTR_ERR(alg_region);
- pos = sizeof(adsp2_id) / 2;
- len = (sizeof(*adsp2_alg) * n_algs) / 2;
+ /* Calculate offset and length in DSP words */
+ pos = sizeof(adsp2_id) / sizeof(u32);
+ len = (sizeof(*adsp2_alg) * n_algs) / sizeof(u32);
- adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len);
+ adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len);
if (IS_ERR(adsp2_alg))
return PTR_ERR(adsp2_alg);
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
- struct wm_adsp *dsp = snd_soc_component_get_drvdata(component);
+ struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct wm_adsp *dsp = &dsps[mc->shift - 1];
ucontrol->value.integer.value[0] = dsp->preloaded;
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
- struct wm_adsp *dsp = snd_soc_component_get_drvdata(component);
+ struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
+ struct wm_adsp *dsp = &dsps[mc->shift - 1];
char preload[32];
snprintf(preload, ARRAY_SIZE(preload), "DSP%u Preload", mc->shift);
snd_soc_dapm_sync(dapm);
+ flush_work(&dsp->boot_work);
+
return 0;
}
EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put);
dsp->component = component;
- return snd_soc_add_component_controls(component,
- &wm_adsp_fw_controls[dsp->num - 1],
- 1);
+ return 0;
}
EXPORT_SYMBOL_GPL(wm_adsp2_component_probe);
buf->host_buf_ptr + field_offset, data);
}
-static int wm_adsp_buffer_locate(struct wm_adsp_compr_buf *buf)
+static int wm_adsp_legacy_host_buf_addr(struct wm_adsp_compr_buf *buf)
{
struct wm_adsp_alg_region *alg_region;
struct wm_adsp *dsp = buf->dsp;
return 0;
}
+static struct wm_coeff_ctl *
+wm_adsp_find_host_buffer_ctrl(struct wm_adsp_compr_buf *buf)
+{
+ struct wm_adsp *dsp = buf->dsp;
+ struct wm_coeff_ctl *ctl;
+
+ list_for_each_entry(ctl, &dsp->ctl_list, list) {
+ if (ctl->type != WMFW_CTL_TYPE_HOST_BUFFER)
+ continue;
+
+ if (!ctl->enabled)
+ continue;
+
+ return ctl;
+ }
+
+ return NULL;
+}
+
+static int wm_adsp_buffer_locate(struct wm_adsp_compr_buf *buf)
+{
+ struct wm_adsp *dsp = buf->dsp;
+ struct wm_coeff_ctl *ctl;
+ unsigned int reg;
+ u32 val;
+ int i, ret;
+
+ ctl = wm_adsp_find_host_buffer_ctrl(buf);
+ if (!ctl)
+ return wm_adsp_legacy_host_buf_addr(buf);
+
+ ret = wm_coeff_base_reg(ctl, ®);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < 5; ++i) {
+ ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val));
+ if (ret < 0)
+ return ret;
+
+ if (val)
+ break;
+
+ usleep_range(1000, 2000);
+ }
+
+ if (!val)
+ return -EIO;
+
+ buf->host_buf_ptr = be32_to_cpu(val);
+ adsp_dbg(dsp, "host_buf_ptr=%x\n", buf->host_buf_ptr);
+
+ return 0;
+}
+
static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf)
{
const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps;
#include <linux/err.h>
#include <linux/init.h>
-#include <linux/component.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/platform_device.h>
struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev);
struct q6afe_slim_cfg *slim = &dai_data->port_config[dai->id].slim;
- slim->num_channels = params_channels(params);
slim->sample_rate = params_rate(params);
switch (params_format(params)) {
struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev);
int rc;
+ if (!dai_data->is_port_started[dai->id])
+ return;
+
rc = q6afe_port_stop(dai_data->port[dai->id]);
if (rc < 0)
dev_err(dai->dev, "fail to close AFE port (%d)\n", rc);
struct q6afe_port_config *pcfg = &dai_data->port_config[dai->id];
int i;
- if (!rx_slot) {
- pr_err("%s: rx slot not found\n", __func__);
- return -EINVAL;
- }
+ if (dai->id & 0x1) {
+ /* TX */
+ if (!tx_slot) {
+ pr_err("%s: tx slot not found\n", __func__);
+ return -EINVAL;
+ }
- for (i = 0; i < rx_num; i++) {
- pcfg->slim.ch_mapping[i] = rx_slot[i];
- pr_debug("%s: find number of channels[%d] ch[%d]\n",
- __func__, i, rx_slot[i]);
- }
+ for (i = 0; i < tx_num; i++)
+ pcfg->slim.ch_mapping[i] = tx_slot[i];
- pcfg->slim.num_channels = rx_num;
+ pcfg->slim.num_channels = tx_num;
- pr_debug("%s: SLIMBUS_%d_RX cnt[%d] ch[%d %d]\n", __func__,
- (dai->id - SLIMBUS_0_RX) / 2, rx_num,
- pcfg->slim.ch_mapping[0],
- pcfg->slim.ch_mapping[1]);
+
+ } else {
+ if (!rx_slot) {
+ pr_err("%s: rx slot not found\n", __func__);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < rx_num; i++)
+ pcfg->slim.ch_mapping[i] = rx_slot[i];
+
+ pcfg->slim.num_channels = rx_num;
+
+ }
return 0;
}
{"Slimbus5 Playback", NULL, "SLIMBUS_5_RX"},
{"Slimbus6 Playback", NULL, "SLIMBUS_6_RX"},
+ {"SLIMBUS_0_TX", NULL, "Slimbus Capture"},
+ {"SLIMBUS_1_TX", NULL, "Slimbus1 Capture"},
+ {"SLIMBUS_2_TX", NULL, "Slimbus2 Capture"},
+ {"SLIMBUS_3_TX", NULL, "Slimbus3 Capture"},
+ {"SLIMBUS_4_TX", NULL, "Slimbus4 Capture"},
+ {"SLIMBUS_5_TX", NULL, "Slimbus5 Capture"},
+ {"SLIMBUS_6_TX", NULL, "Slimbus6 Capture"},
+
{"Primary MI2S Playback", NULL, "PRI_MI2S_RX"},
{"Secondary MI2S Playback", NULL, "SEC_MI2S_RX"},
{"Tertiary MI2S Playback", NULL, "TERT_MI2S_RX"},
.rate_min = 8000,
.rate_max = 192000,
},
+ }, {
+ .name = "SLIMBUS_0_TX",
+ .ops = &q6slim_ops,
+ .id = SLIMBUS_0_TX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ .capture = {
+ .stream_name = "Slimbus Capture",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
}, {
.playback = {
.stream_name = "Slimbus1 Playback",
.id = SLIMBUS_1_RX,
.probe = msm_dai_q6_dai_probe,
.remove = msm_dai_q6_dai_remove,
+ }, {
+ .name = "SLIMBUS_1_TX",
+ .ops = &q6slim_ops,
+ .id = SLIMBUS_1_TX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ .capture = {
+ .stream_name = "Slimbus1 Capture",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
}, {
.playback = {
.stream_name = "Slimbus2 Playback",
.id = SLIMBUS_2_RX,
.probe = msm_dai_q6_dai_probe,
.remove = msm_dai_q6_dai_remove,
+
+ }, {
+ .name = "SLIMBUS_2_TX",
+ .ops = &q6slim_ops,
+ .id = SLIMBUS_2_TX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ .capture = {
+ .stream_name = "Slimbus2 Capture",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
}, {
.playback = {
.stream_name = "Slimbus3 Playback",
.id = SLIMBUS_3_RX,
.probe = msm_dai_q6_dai_probe,
.remove = msm_dai_q6_dai_remove,
+
+ }, {
+ .name = "SLIMBUS_3_TX",
+ .ops = &q6slim_ops,
+ .id = SLIMBUS_3_TX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ .capture = {
+ .stream_name = "Slimbus3 Capture",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
}, {
.playback = {
.stream_name = "Slimbus4 Playback",
.id = SLIMBUS_4_RX,
.probe = msm_dai_q6_dai_probe,
.remove = msm_dai_q6_dai_remove,
+
+ }, {
+ .name = "SLIMBUS_4_TX",
+ .ops = &q6slim_ops,
+ .id = SLIMBUS_4_TX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ .capture = {
+ .stream_name = "Slimbus4 Capture",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
}, {
.playback = {
.stream_name = "Slimbus5 Playback",
.id = SLIMBUS_5_RX,
.probe = msm_dai_q6_dai_probe,
.remove = msm_dai_q6_dai_remove,
+
+ }, {
+ .name = "SLIMBUS_5_TX",
+ .ops = &q6slim_ops,
+ .id = SLIMBUS_5_TX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ .capture = {
+ .stream_name = "Slimbus5 Capture",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
}, {
.playback = {
.stream_name = "Slimbus6 Playback",
.id = SLIMBUS_6_RX,
.probe = msm_dai_q6_dai_probe,
.remove = msm_dai_q6_dai_remove,
+
+ }, {
+ .name = "SLIMBUS_6_TX",
+ .ops = &q6slim_ops,
+ .id = SLIMBUS_6_TX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ .capture = {
+ .stream_name = "Slimbus6 Capture",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
}, {
.playback = {
.stream_name = "Primary MI2S Playback",
SND_SOC_DAPM_AIF_OUT("SLIMBUS_4_RX", "Slimbus4 Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("SLIMBUS_5_RX", "Slimbus5 Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("SLIMBUS_6_RX", "Slimbus6 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIMBUS_0_TX", "Slimbus Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIMBUS_1_TX", "Slimbus1 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIMBUS_2_TX", "Slimbus2 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIMBUS_3_TX", "Slimbus3 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIMBUS_4_TX", "Slimbus4 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIMBUS_5_TX", "Slimbus5 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIMBUS_6_TX", "Slimbus6 Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("QUAT_MI2S_RX", "Quaternary MI2S Playback",
0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("QUAT_MI2S_TX", "Quaternary MI2S Capture",
int id, i, num_lines;
ret = of_property_read_u32(node, "reg", &id);
- if (ret || id > AFE_PORT_MAX) {
+ if (ret || id < 0 || id >= AFE_PORT_MAX) {
dev_err(dev, "valid dai id not found:%d\n", ret);
continue;
}
}
}
-static int q6afe_dai_bind(struct device *dev, struct device *master, void *data)
+static int q6afe_dai_dev_probe(struct platform_device *pdev)
{
struct q6afe_dai_data *dai_data;
+ struct device *dev = &pdev->dev;
- dai_data = kzalloc(sizeof(*dai_data), GFP_KERNEL);
+ dai_data = devm_kzalloc(dev, sizeof(*dai_data), GFP_KERNEL);
if (!dai_data)
return -ENOMEM;
of_q6afe_parse_dai_data(dev, dai_data);
- return snd_soc_register_component(dev, &q6afe_dai_component,
+ return devm_snd_soc_register_component(dev, &q6afe_dai_component,
q6afe_dais, ARRAY_SIZE(q6afe_dais));
}
-static void q6afe_dai_unbind(struct device *dev, struct device *master,
- void *data)
-{
- struct q6afe_dai_data *dai_data = dev_get_drvdata(dev);
-
- snd_soc_unregister_component(dev);
- kfree(dai_data);
-}
-
-static const struct component_ops q6afe_dai_comp_ops = {
- .bind = q6afe_dai_bind,
- .unbind = q6afe_dai_unbind,
+static const struct of_device_id q6afe_dai_device_id[] = {
+ { .compatible = "qcom,q6afe-dais" },
+ {},
};
-
-static int q6afe_dai_dev_probe(struct platform_device *pdev)
-{
- return component_add(&pdev->dev, &q6afe_dai_comp_ops);
-}
-
-static int q6afe_dai_dev_remove(struct platform_device *pdev)
-{
- component_del(&pdev->dev, &q6afe_dai_comp_ops);
- return 0;
-}
+MODULE_DEVICE_TABLE(of, q6afe_dai_device_id);
static struct platform_driver q6afe_dai_platform_driver = {
.driver = {
.name = "q6afe-dai",
+ .of_match_table = of_match_ptr(q6afe_dai_device_id),
},
.probe = q6afe_dai_dev_probe,
- .remove = q6afe_dai_dev_remove,
};
module_platform_driver(q6afe_dai_platform_driver);
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
-#include <linux/component.h>
#include <sound/soc.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
struct q6asm_dai_rtd *prtd = runtime->private_data;
if (prtd->audio_client) {
- q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+ if (prtd->state)
+ q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+
q6asm_unmap_memory_regions(substream->stream,
prtd->audio_client);
q6asm_audio_client_free(prtd->audio_client);
Q6ASM_FEDAI_DRIVER(8),
};
-static int q6asm_dai_bind(struct device *dev, struct device *master, void *data)
+static int q6asm_dai_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
struct of_phandle_args args;
struct q6asm_dai_data *pdata;
int rc;
- pdata = kzalloc(sizeof(struct q6asm_dai_data), GFP_KERNEL);
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
dev_set_drvdata(dev, pdata);
- return snd_soc_register_component(dev, &q6asm_fe_dai_component,
+ return devm_snd_soc_register_component(dev, &q6asm_fe_dai_component,
q6asm_fe_dais,
ARRAY_SIZE(q6asm_fe_dais));
}
-static void q6asm_dai_unbind(struct device *dev, struct device *master,
- void *data)
-{
- struct q6asm_dai_data *pdata = dev_get_drvdata(dev);
-
- snd_soc_unregister_component(dev);
-
- kfree(pdata);
-}
-
-static const struct component_ops q6asm_dai_comp_ops = {
- .bind = q6asm_dai_bind,
- .unbind = q6asm_dai_unbind,
+static const struct of_device_id q6asm_dai_device_id[] = {
+ { .compatible = "qcom,q6asm-dais" },
+ {},
};
-
-static int q6asm_dai_probe(struct platform_device *pdev)
-{
- return component_add(&pdev->dev, &q6asm_dai_comp_ops);
-}
-
-static int q6asm_dai_dev_remove(struct platform_device *pdev)
-{
- component_del(&pdev->dev, &q6asm_dai_comp_ops);
- return 0;
-}
+MODULE_DEVICE_TABLE(of, q6asm_dai_device_id);
static struct platform_driver q6asm_dai_platform_driver = {
.driver = {
.name = "q6asm-dai",
+ .of_match_table = of_match_ptr(q6asm_dai_device_id),
},
.probe = q6asm_dai_probe,
- .remove = q6asm_dai_dev_remove,
};
module_platform_driver(q6asm_dai_platform_driver);
#include <linux/platform_device.h>
#include <linux/of_platform.h>
#include <linux/bitops.h>
-#include <linux/component.h>
#include <linux/mutex.h>
#include <linux/of_device.h>
#include <linux/slab.h>
{ mix_name, "SEC_MI2S_TX", "SEC_MI2S_TX" }, \
{ mix_name, "QUAT_MI2S_TX", "QUAT_MI2S_TX" }, \
{ mix_name, "TERT_MI2S_TX", "TERT_MI2S_TX" }, \
+ { mix_name, "SLIMBUS_0_TX", "SLIMBUS_0_TX" }, \
+ { mix_name, "SLIMBUS_1_TX", "SLIMBUS_1_TX" }, \
+ { mix_name, "SLIMBUS_2_TX", "SLIMBUS_2_TX" }, \
+ { mix_name, "SLIMBUS_3_TX", "SLIMBUS_3_TX" }, \
+ { mix_name, "SLIMBUS_4_TX", "SLIMBUS_4_TX" }, \
+ { mix_name, "SLIMBUS_5_TX", "SLIMBUS_5_TX" }, \
+ { mix_name, "SLIMBUS_6_TX", "SLIMBUS_6_TX" }, \
{ mix_name, "PRIMARY_TDM_TX_0", "PRIMARY_TDM_TX_0"}, \
{ mix_name, "PRIMARY_TDM_TX_1", "PRIMARY_TDM_TX_1"}, \
{ mix_name, "PRIMARY_TDM_TX_2", "PRIMARY_TDM_TX_2"}, \
SOC_SINGLE_EXT("QUAT_MI2S_TX", QUATERNARY_MI2S_TX, \
id, 1, 0, msm_routing_get_audio_mixer, \
msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("SLIMBUS_0_TX", SLIMBUS_0_TX, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("SLIMBUS_1_TX", SLIMBUS_1_TX, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("SLIMBUS_2_TX", SLIMBUS_2_TX, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("SLIMBUS_3_TX", SLIMBUS_3_TX, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("SLIMBUS_4_TX", SLIMBUS_4_TX, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("SLIMBUS_5_TX", SLIMBUS_5_TX, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("SLIMBUS_6_TX", SLIMBUS_6_TX, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
SOC_SINGLE_EXT("PRIMARY_TDM_TX_0", PRIMARY_TDM_TX_0, \
id, 1, 0, msm_routing_get_audio_mixer, \
msm_routing_put_audio_mixer), \
session->channels, topology, perf_mode,
session->bits_per_sample, 0, 0);
- if (!copp) {
+ if (IS_ERR_OR_NULL(copp)) {
mutex_unlock(&routing_data->lock);
return -EINVAL;
}
else
path_type = ADM_PATH_LIVE_REC;
- if (be_id > AFE_MAX_PORTS)
+ if (be_id >= AFE_MAX_PORTS)
return -EINVAL;
session = &data->port_data[be_id];
.num_dapm_routes = ARRAY_SIZE(intercon),
};
-static int q6routing_dai_bind(struct device *dev, struct device *master,
- void *data)
+static int q6pcm_routing_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
+
routing_data = kzalloc(sizeof(*routing_data), GFP_KERNEL);
if (!routing_data)
return -ENOMEM;
mutex_init(&routing_data->lock);
dev_set_drvdata(dev, routing_data);
- return snd_soc_register_component(dev, &msm_soc_routing_component,
+ return devm_snd_soc_register_component(dev, &msm_soc_routing_component,
NULL, 0);
}
-static void q6routing_dai_unbind(struct device *dev, struct device *master,
- void *d)
+static int q6pcm_routing_remove(struct platform_device *pdev)
{
- struct msm_routing_data *data = dev_get_drvdata(dev);
-
- snd_soc_unregister_component(dev);
-
- kfree(data);
-
+ kfree(routing_data);
routing_data = NULL;
-}
-
-static const struct component_ops q6routing_dai_comp_ops = {
- .bind = q6routing_dai_bind,
- .unbind = q6routing_dai_unbind,
-};
-static int q6pcm_routing_probe(struct platform_device *pdev)
-{
- return component_add(&pdev->dev, &q6routing_dai_comp_ops);
-}
-
-static int q6pcm_routing_remove(struct platform_device *pdev)
-{
- component_del(&pdev->dev, &q6routing_dai_comp_ops);
return 0;
}
+static const struct of_device_id q6pcm_routing_device_id[] = {
+ { .compatible = "qcom,q6adm-routing" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, q6pcm_routing_device_id);
+
static struct platform_driver q6pcm_routing_platform_driver = {
.driver = {
.name = "q6routing",
+ .of_match_table = of_match_ptr(q6pcm_routing_device_id),
},
.probe = q6pcm_routing_probe,
.remove = q6pcm_routing_remove,
-/*
- * soc-pcm.c -- ALSA SoC PCM
- *
- * Copyright 2005 Wolfson Microelectronics PLC.
- * Copyright 2005 Openedhand Ltd.
- * Copyright (C) 2010 Slimlogic Ltd.
- * Copyright (C) 2010 Texas Instruments Inc.
- *
- * Authors: Liam Girdwood <lrg@ti.com>
- * Mark Brown <broonie@opensource.wolfsonmicro.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// soc-pcm.c -- ALSA SoC PCM
+//
+// Copyright 2005 Wolfson Microelectronics PLC.
+// Copyright 2005 Openedhand Ltd.
+// Copyright (C) 2010 Slimlogic Ltd.
+// Copyright (C) 2010 Texas Instruments Inc.
+//
+// Authors: Liam Girdwood <lrg@ti.com>
+// Mark Brown <broonie@opensource.wolfsonmicro.com>
#include <linux/kernel.h>
#include <linux/init.h>
hw->rate_max = min_not_zero(hw->rate_max, rate_max);
}
+static int soc_pcm_components_close(struct snd_pcm_substream *substream,
+ struct snd_soc_component *last)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_rtdcom_list *rtdcom;
+ struct snd_soc_component *component;
+
+ for_each_rtdcom(rtd, rtdcom) {
+ component = rtdcom->component;
+
+ if (component == last)
+ break;
+
+ if (!component->driver->ops ||
+ !component->driver->ops->close)
+ continue;
+
+ component->driver->ops->close(substream);
+ }
+
+ return 0;
+}
+
/*
* Called by ALSA when a PCM substream is opened, the runtime->hw record is
* then initialized and any private data can be allocated. This also calls
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai;
const char *codec_dai_name = "multicodec";
- int i, ret = 0, __ret;
+ int i, ret = 0;
pinctrl_pm_select_default_state(cpu_dai->dev);
for (i = 0; i < rtd->num_codecs; i++)
}
}
- ret = 0;
for_each_rtdcom(rtd, rtdcom) {
component = rtdcom->component;
!component->driver->ops->open)
continue;
- __ret = component->driver->ops->open(substream);
- if (__ret < 0) {
+ ret = component->driver->ops->open(substream);
+ if (ret < 0) {
dev_err(component->dev,
"ASoC: can't open component %s: %d\n",
- component->name, __ret);
- ret = __ret;
+ component->name, ret);
+ goto component_err;
}
}
- if (ret < 0)
- goto component_err;
+ component = NULL;
for (i = 0; i < rtd->num_codecs; i++) {
codec_dai = rtd->codec_dais[i];
}
component_err:
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
- if (!component->driver->ops ||
- !component->driver->ops->close)
- continue;
-
- component->driver->ops->close(substream);
- }
+ soc_pcm_components_close(substream, component);
if (cpu_dai->driver->ops->shutdown)
cpu_dai->driver->ops->shutdown(substream, cpu_dai);
if (rtd->dai_link->ops->shutdown)
rtd->dai_link->ops->shutdown(substream);
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
- if (!component->driver->ops ||
- !component->driver->ops->close)
- continue;
-
- component->driver->ops->close(substream);
- }
+ soc_pcm_components_close(substream, NULL);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
if (snd_soc_runtime_ignore_pmdown_time(rtd)) {
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
int ret;
+ /* perform any topology hw_params fixups before DAI */
+ if (rtd->dai_link->be_hw_params_fixup) {
+ ret = rtd->dai_link->be_hw_params_fixup(rtd, params);
+ if (ret < 0) {
+ dev_err(rtd->dev,
+ "ASoC: hw_params topology fixup failed %d\n",
+ ret);
+ return ret;
+ }
+ }
+
if (dai->driver->ops->hw_params) {
ret = dai->driver->ops->hw_params(substream, params, dai);
if (ret < 0) {
return 0;
}
+static int soc_pcm_components_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_component *last)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_rtdcom_list *rtdcom;
+ struct snd_soc_component *component;
+
+ for_each_rtdcom(rtd, rtdcom) {
+ component = rtdcom->component;
+
+ if (component == last)
+ break;
+
+ if (!component->driver->ops ||
+ !component->driver->ops->hw_free)
+ continue;
+
+ component->driver->ops->hw_free(substream);
+ }
+
+ return 0;
+}
+
/*
* Called by ALSA when the hardware params are set by application. This
* function can also be called multiple times and can allocate buffers
struct snd_soc_component *component;
struct snd_soc_rtdcom_list *rtdcom;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int i, ret = 0, __ret;
+ int i, ret = 0;
mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
if (rtd->dai_link->ops->hw_params) {
if (ret < 0)
goto interface_err;
- ret = 0;
for_each_rtdcom(rtd, rtdcom) {
component = rtdcom->component;
!component->driver->ops->hw_params)
continue;
- __ret = component->driver->ops->hw_params(substream, params);
- if (__ret < 0) {
+ ret = component->driver->ops->hw_params(substream, params);
+ if (ret < 0) {
dev_err(component->dev,
"ASoC: %s hw params failed: %d\n",
- component->name, __ret);
- ret = __ret;
+ component->name, ret);
+ goto component_err;
}
}
- if (ret < 0)
- goto component_err;
+ component = NULL;
/* store the parameters for each DAIs */
cpu_dai->rate = params_rate(params);
return ret;
component_err:
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
- if (!component->driver->ops ||
- !component->driver->ops->hw_free)
- continue;
-
- component->driver->ops->hw_free(substream);
- }
+ soc_pcm_components_hw_free(substream, component);
if (cpu_dai->driver->ops->hw_free)
cpu_dai->driver->ops->hw_free(substream, cpu_dai);
static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component;
- struct snd_soc_rtdcom_list *rtdcom;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai;
bool playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
rtd->dai_link->ops->hw_free(substream);
/* free any component resources */
- for_each_rtdcom(rtd, rtdcom) {
- component = rtdcom->component;
-
- if (!component->driver->ops ||
- !component->driver->ops->hw_free)
- continue;
-
- component->driver->ops->hw_free(substream);
- }
+ soc_pcm_components_hw_free(substream, NULL);
/* now free hw params for the DAIs */
for (i = 0; i < rtd->num_codecs; i++) {
snd_pcm_sframes_t codec_delay = 0;
int i;
+ /* clearing the previous total delay */
+ runtime->delay = 0;
+
for_each_rtdcom(rtd, rtdcom) {
component = rtdcom->component;
offset = component->driver->ops->pointer(substream);
break;
}
+ /* base delay if assigned in pointer callback */
+ delay = runtime->delay;
if (cpu_dai->driver->ops->delay)
delay += cpu_dai->driver->ops->delay(substream, cpu_dai);
}
static void dpcm_init_runtime_hw(struct snd_pcm_runtime *runtime,
- struct snd_soc_pcm_stream *stream,
- u64 formats)
+ struct snd_soc_pcm_stream *stream)
{
runtime->hw.rate_min = stream->rate_min;
runtime->hw.rate_max = stream->rate_max;
runtime->hw.channels_min = stream->channels_min;
runtime->hw.channels_max = stream->channels_max;
if (runtime->hw.formats)
- runtime->hw.formats &= formats & stream->formats;
+ runtime->hw.formats &= stream->formats;
else
- runtime->hw.formats = formats & stream->formats;
+ runtime->hw.formats = stream->formats;
runtime->hw.rates = stream->rates;
}
-static u64 dpcm_runtime_base_format(struct snd_pcm_substream *substream)
+static void dpcm_runtime_merge_format(struct snd_pcm_substream *substream,
+ u64 *formats)
{
struct snd_soc_pcm_runtime *fe = substream->private_data;
struct snd_soc_dpcm *dpcm;
- u64 formats = ULLONG_MAX;
int stream = substream->stream;
if (!fe->dai_link->dpcm_merged_format)
- return formats;
+ return;
/*
* It returns merged BE codec format
int i;
for (i = 0; i < be->num_codecs; i++) {
+ /*
+ * Skip CODECs which don't support the current stream
+ * type. See soc_pcm_init_runtime_hw() for more details
+ */
+ if (!snd_soc_dai_stream_valid(be->codec_dais[i],
+ stream))
+ continue;
+
codec_dai_drv = be->codec_dais[i]->driver;
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
codec_stream = &codec_dai_drv->playback;
else
codec_stream = &codec_dai_drv->capture;
- formats &= codec_stream->formats;
+ *formats &= codec_stream->formats;
+ }
+ }
+}
+
+static void dpcm_runtime_merge_chan(struct snd_pcm_substream *substream,
+ unsigned int *channels_min,
+ unsigned int *channels_max)
+{
+ struct snd_soc_pcm_runtime *fe = substream->private_data;
+ struct snd_soc_dpcm *dpcm;
+ int stream = substream->stream;
+
+ if (!fe->dai_link->dpcm_merged_chan)
+ return;
+
+ /*
+ * It returns merged BE codec channel;
+ * if FE want to use it (= dpcm_merged_chan)
+ */
+
+ list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+ struct snd_soc_pcm_runtime *be = dpcm->be;
+ struct snd_soc_dai_driver *cpu_dai_drv = be->cpu_dai->driver;
+ struct snd_soc_dai_driver *codec_dai_drv;
+ struct snd_soc_pcm_stream *codec_stream;
+ struct snd_soc_pcm_stream *cpu_stream;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ cpu_stream = &cpu_dai_drv->playback;
+ else
+ cpu_stream = &cpu_dai_drv->capture;
+
+ *channels_min = max(*channels_min, cpu_stream->channels_min);
+ *channels_max = min(*channels_max, cpu_stream->channels_max);
+
+ /*
+ * chan min/max cannot be enforced if there are multiple CODEC
+ * DAIs connected to a single CPU DAI, use CPU DAI's directly
+ */
+ if (be->num_codecs == 1) {
+ codec_dai_drv = be->codec_dais[0]->driver;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ codec_stream = &codec_dai_drv->playback;
+ else
+ codec_stream = &codec_dai_drv->capture;
+
+ *channels_min = max(*channels_min,
+ codec_stream->channels_min);
+ *channels_max = min(*channels_max,
+ codec_stream->channels_max);
}
}
+}
+
+static void dpcm_runtime_merge_rate(struct snd_pcm_substream *substream,
+ unsigned int *rates,
+ unsigned int *rate_min,
+ unsigned int *rate_max)
+{
+ struct snd_soc_pcm_runtime *fe = substream->private_data;
+ struct snd_soc_dpcm *dpcm;
+ int stream = substream->stream;
- return formats;
+ if (!fe->dai_link->dpcm_merged_rate)
+ return;
+
+ /*
+ * It returns merged BE codec channel;
+ * if FE want to use it (= dpcm_merged_chan)
+ */
+
+ list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+ struct snd_soc_pcm_runtime *be = dpcm->be;
+ struct snd_soc_dai_driver *cpu_dai_drv = be->cpu_dai->driver;
+ struct snd_soc_dai_driver *codec_dai_drv;
+ struct snd_soc_pcm_stream *codec_stream;
+ struct snd_soc_pcm_stream *cpu_stream;
+ int i;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ cpu_stream = &cpu_dai_drv->playback;
+ else
+ cpu_stream = &cpu_dai_drv->capture;
+
+ *rate_min = max(*rate_min, cpu_stream->rate_min);
+ *rate_max = min_not_zero(*rate_max, cpu_stream->rate_max);
+ *rates = snd_pcm_rate_mask_intersect(*rates, cpu_stream->rates);
+
+ for (i = 0; i < be->num_codecs; i++) {
+ /*
+ * Skip CODECs which don't support the current stream
+ * type. See soc_pcm_init_runtime_hw() for more details
+ */
+ if (!snd_soc_dai_stream_valid(be->codec_dais[i],
+ stream))
+ continue;
+
+ codec_dai_drv = be->codec_dais[i]->driver;
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ codec_stream = &codec_dai_drv->playback;
+ else
+ codec_stream = &codec_dai_drv->capture;
+
+ *rate_min = max(*rate_min, codec_stream->rate_min);
+ *rate_max = min_not_zero(*rate_max,
+ codec_stream->rate_max);
+ *rates = snd_pcm_rate_mask_intersect(*rates,
+ codec_stream->rates);
+ }
+ }
}
static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream)
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver;
- u64 format = dpcm_runtime_base_format(substream);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- dpcm_init_runtime_hw(runtime, &cpu_dai_drv->playback, format);
+ dpcm_init_runtime_hw(runtime, &cpu_dai_drv->playback);
else
- dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture, format);
+ dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture);
+
+ dpcm_runtime_merge_format(substream, &runtime->hw.formats);
+ dpcm_runtime_merge_chan(substream, &runtime->hw.channels_min,
+ &runtime->hw.channels_max);
+ dpcm_runtime_merge_rate(substream, &runtime->hw.rates,
+ &runtime->hw.rate_min, &runtime->hw.rate_max);
}
static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd);
return ret;
}
-/* Called by DAPM mixer/mux changes to update audio routing between PCMs and
- * any DAI links.
- */
-int soc_dpcm_runtime_update(struct snd_soc_card *card)
+static int soc_dpcm_fe_runtime_update(struct snd_soc_pcm_runtime *fe, int new)
{
- struct snd_soc_pcm_runtime *fe;
- int old, new, paths;
+ struct snd_soc_dapm_widget_list *list;
+ int count, paths;
- mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
- list_for_each_entry(fe, &card->rtd_list, list) {
- struct snd_soc_dapm_widget_list *list;
+ if (!fe->dai_link->dynamic)
+ return 0;
- /* make sure link is FE */
- if (!fe->dai_link->dynamic)
- continue;
+ /* only check active links */
+ if (!fe->cpu_dai->active)
+ return 0;
- /* only check active links */
- if (!fe->cpu_dai->active)
- continue;
+ /* DAPM sync will call this to update DSP paths */
+ dev_dbg(fe->dev, "ASoC: DPCM %s runtime update for FE %s\n",
+ new ? "new" : "old", fe->dai_link->name);
- /* DAPM sync will call this to update DSP paths */
- dev_dbg(fe->dev, "ASoC: DPCM runtime update for FE %s\n",
- fe->dai_link->name);
+ /* skip if FE doesn't have playback capability */
+ if (!fe->cpu_dai->driver->playback.channels_min ||
+ !fe->codec_dai->driver->playback.channels_min)
+ goto capture;
- /* skip if FE doesn't have playback capability */
- if (!fe->cpu_dai->driver->playback.channels_min
- || !fe->codec_dai->driver->playback.channels_min)
- goto capture;
-
- /* skip if FE isn't currently playing */
- if (!fe->cpu_dai->playback_active
- || !fe->codec_dai->playback_active)
- goto capture;
-
- paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_PLAYBACK, &list);
- if (paths < 0) {
- dev_warn(fe->dev, "ASoC: %s no valid %s path\n",
- fe->dai_link->name, "playback");
- mutex_unlock(&card->mutex);
- return paths;
- }
+ /* skip if FE isn't currently playing */
+ if (!fe->cpu_dai->playback_active || !fe->codec_dai->playback_active)
+ goto capture;
- /* update any new playback paths */
- new = dpcm_process_paths(fe, SNDRV_PCM_STREAM_PLAYBACK, &list, 1);
- if (new) {
- dpcm_run_new_update(fe, SNDRV_PCM_STREAM_PLAYBACK);
- dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_PLAYBACK);
- dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_PLAYBACK);
- }
+ paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_PLAYBACK, &list);
+ if (paths < 0) {
+ dev_warn(fe->dev, "ASoC: %s no valid %s path\n",
+ fe->dai_link->name, "playback");
+ return paths;
+ }
- /* update any old playback paths */
- old = dpcm_process_paths(fe, SNDRV_PCM_STREAM_PLAYBACK, &list, 0);
- if (old) {
+ /* update any playback paths */
+ count = dpcm_process_paths(fe, SNDRV_PCM_STREAM_PLAYBACK, &list, new);
+ if (count) {
+ if (new)
+ dpcm_run_new_update(fe, SNDRV_PCM_STREAM_PLAYBACK);
+ else
dpcm_run_old_update(fe, SNDRV_PCM_STREAM_PLAYBACK);
- dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_PLAYBACK);
- dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_PLAYBACK);
- }
- dpcm_path_put(&list);
+ dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_PLAYBACK);
+ dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_PLAYBACK);
+ }
+
+ dpcm_path_put(&list);
+
capture:
- /* skip if FE doesn't have capture capability */
- if (!fe->cpu_dai->driver->capture.channels_min
- || !fe->codec_dai->driver->capture.channels_min)
- continue;
+ /* skip if FE doesn't have capture capability */
+ if (!fe->cpu_dai->driver->capture.channels_min ||
+ !fe->codec_dai->driver->capture.channels_min)
+ return 0;
- /* skip if FE isn't currently capturing */
- if (!fe->cpu_dai->capture_active
- || !fe->codec_dai->capture_active)
- continue;
+ /* skip if FE isn't currently capturing */
+ if (!fe->cpu_dai->capture_active || !fe->codec_dai->capture_active)
+ return 0;
- paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_CAPTURE, &list);
- if (paths < 0) {
- dev_warn(fe->dev, "ASoC: %s no valid %s path\n",
- fe->dai_link->name, "capture");
- mutex_unlock(&card->mutex);
- return paths;
- }
+ paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_CAPTURE, &list);
+ if (paths < 0) {
+ dev_warn(fe->dev, "ASoC: %s no valid %s path\n",
+ fe->dai_link->name, "capture");
+ return paths;
+ }
- /* update any new capture paths */
- new = dpcm_process_paths(fe, SNDRV_PCM_STREAM_CAPTURE, &list, 1);
- if (new) {
+ /* update any old capture paths */
+ count = dpcm_process_paths(fe, SNDRV_PCM_STREAM_CAPTURE, &list, new);
+ if (count) {
+ if (new)
dpcm_run_new_update(fe, SNDRV_PCM_STREAM_CAPTURE);
- dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_CAPTURE);
- dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_CAPTURE);
- }
-
- /* update any old capture paths */
- old = dpcm_process_paths(fe, SNDRV_PCM_STREAM_CAPTURE, &list, 0);
- if (old) {
+ else
dpcm_run_old_update(fe, SNDRV_PCM_STREAM_CAPTURE);
- dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_CAPTURE);
- dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_CAPTURE);
- }
- dpcm_path_put(&list);
+ dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_CAPTURE);
+ dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_CAPTURE);
}
- mutex_unlock(&card->mutex);
+ dpcm_path_put(&list);
+
return 0;
}
+
+/* Called by DAPM mixer/mux changes to update audio routing between PCMs and
+ * any DAI links.
+ */
+int soc_dpcm_runtime_update(struct snd_soc_card *card)
+{
+ struct snd_soc_pcm_runtime *fe;
+ int ret = 0;
+
+ mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
+ /* shutdown all old paths first */
+ list_for_each_entry(fe, &card->rtd_list, list) {
+ ret = soc_dpcm_fe_runtime_update(fe, 0);
+ if (ret)
+ goto out;
+ }
+
+ /* bring new paths up */
+ list_for_each_entry(fe, &card->rtd_list, list) {
+ ret = soc_dpcm_fe_runtime_update(fe, 1);
+ if (ret)
+ goto out;
+ }
+
+out:
+ mutex_unlock(&card->mutex);
+ return ret;
+}
int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute)
{
struct snd_soc_dpcm *dpcm;
-/*
- * soc-util.c -- ALSA SoC Audio Layer utility functions
- *
- * Copyright 2009 Wolfson Microelectronics PLC.
- *
- * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
- * Liam Girdwood <lrg@slimlogic.co.uk>
- *
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// soc-util.c -- ALSA SoC Audio Layer utility functions
+//
+// Copyright 2009 Wolfson Microelectronics PLC.
+//
+// Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+// Liam Girdwood <lrg@slimlogic.co.uk>
#include <linux/platform_device.h>
#include <linux/export.h>
void __exit snd_soc_util_exit(void)
{
- platform_device_unregister(soc_dummy_dev);
platform_driver_unregister(&soc_dummy_driver);
+ platform_device_unregister(soc_dummy_dev);
}