Merge "dsp: q6core: validate payload size before memory copy"
diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c
index 35b2bdb..417cbc0 100644
--- a/asoc/msm-pcm-routing-v2.c
+++ b/asoc/msm-pcm-routing-v2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -17445,6 +17445,33 @@
msm_aptx_dec_license_control_put),
};
+static int msm_routing_put_port_chmap_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ uint8_t channel_map[PCM_FORMAT_MAX_NUM_CHANNEL];
+ uint32_t be_idx = ucontrol->value.integer.value[0];
+ int i;
+
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++) {
+ channel_map[i] = (char)(ucontrol->value.integer.value[i + 1]);
+ if (channel_map[i] > PCM_MAX_CHMAP_ID) {
+ pr_err("%s: Invalid channel map %d\n",
+ __func__, channel_map[i]);
+ return -EINVAL;
+ }
+ }
+ adm_set_port_multi_ch_map(channel_map, msm_bedais[be_idx].port_id);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new port_multi_channel_map_mixer_controls[] = {
+ SOC_SINGLE_MULTI_EXT("Backend Device Channel Map", SND_SOC_NOPM, 0,
+ MSM_BACKEND_DAI_MAX, 0,
+ PCM_FORMAT_MAX_NUM_CHANNEL + 1, NULL,
+ msm_routing_put_port_chmap_mixer),
+};
+
static int msm_routing_be_dai_name_table_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -17636,6 +17663,10 @@
ARRAY_SIZE(aptx_dec_license_controls));
snd_soc_add_platform_controls(platform, stereo_channel_reverse_control,
ARRAY_SIZE(stereo_channel_reverse_control));
+ snd_soc_add_platform_controls(platform,
+ port_multi_channel_map_mixer_controls,
+ ARRAY_SIZE(port_multi_channel_map_mixer_controls));
+
return 0;
}
diff --git a/asoc/sdm660-common.c b/asoc/sdm660-common.c
index 093eef4..b2cbc2a 100644
--- a/asoc/sdm660-common.c
+++ b/asoc/sdm660-common.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -51,6 +51,18 @@
bool codec_reg_done;
+struct tdm_dai_data {
+ DECLARE_BITMAP(status_mask, 3);
+ u32 rate;
+ u32 channels;
+ u32 bitwidth;
+ u32 num_group_ports;
+ struct afe_clk_set clk_set; /* hold LPASS clock config. */
+ union afe_port_group_config group_cfg; /* hold tdm group config */
+ struct afe_tdm_port_config port_cfg; /* hold tdm config */
+};
+
+
/* TDM default config */
static struct dev_config tdm_rx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = {
{ /* PRI TDM */
@@ -2803,6 +2815,91 @@
}
EXPORT_SYMBOL(msm_mi2s_snd_shutdown);
+static int msm_get_tdm_mode(u32 port_id)
+{
+ int tdm_mode;
+
+ switch (port_id) {
+ case AFE_PORT_ID_PRIMARY_TDM_RX:
+ case AFE_PORT_ID_PRIMARY_TDM_TX:
+ tdm_mode = TDM_PRI;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX:
+ case AFE_PORT_ID_SECONDARY_TDM_TX:
+ tdm_mode = TDM_SEC;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX:
+ case AFE_PORT_ID_TERTIARY_TDM_TX:
+ tdm_mode = TDM_TERT;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX:
+ tdm_mode = TDM_QUAT;
+ break;
+ case AFE_PORT_ID_QUINARY_TDM_RX:
+ case AFE_PORT_ID_QUINARY_TDM_TX:
+ tdm_mode = TDM_QUIN;
+ break;
+ default:
+ pr_err("%s: Invalid port id: %d\n", __func__, port_id);
+ tdm_mode = -EINVAL;
+ }
+ return tdm_mode;
+}
+
+int msm_tdm_snd_startup(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_card *card = rtd->card;
+ struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+ struct tdm_dai_data *dai_data = dev_get_drvdata(cpu_dai->dev);
+ int tdm_mode = msm_get_tdm_mode(cpu_dai->id);
+
+ if (tdm_mode < 0) {
+ dev_err(rtd->card->dev, "%s: Invalid tdm_mode\n", __func__);
+ return tdm_mode;
+ }
+ dai_data->clk_set.enable = true;
+ ret = afe_set_lpass_clock_v2(cpu_dai->id, &dai_data->clk_set);
+ if (ret < 0)
+ pr_err("%s: afe lpass clock failed, err:%d\n",
+ __func__, ret);
+ /* currently only supporting TDM_RX_0 and TDM_TX_0 */
+ if (pdata->mi2s_gpio_p[tdm_mode])
+ ret = msm_cdc_pinctrl_select_active_state(
+ pdata->mi2s_gpio_p[tdm_mode]);
+ return ret;
+}
+EXPORT_SYMBOL(msm_tdm_snd_startup);
+
+void msm_tdm_snd_shutdown(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_card *card = rtd->card;
+ struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+ struct tdm_dai_data *dai_data = dev_get_drvdata(cpu_dai->dev);
+ int tdm_mode = msm_get_tdm_mode(cpu_dai->id);
+ int ret;
+
+ if (tdm_mode < 0) {
+ dev_err(rtd->card->dev, "%s: Invalid tdm_mode\n", __func__);
+ return;
+ }
+ dai_data->clk_set.enable = false;
+ ret = afe_set_lpass_clock_v2(cpu_dai->id, &dai_data->clk_set);
+ if (ret < 0)
+ pr_err("%s: afe lpass clock failed, err:%d\n", __func__, ret);
+
+ /* currently only supporting TDM_RX_0 and TDM_TX_0 */
+ if (pdata->mi2s_gpio_p[tdm_mode])
+ msm_cdc_pinctrl_select_sleep_state(
+ pdata->mi2s_gpio_p[tdm_mode]);
+}
+EXPORT_SYMBOL(msm_tdm_snd_shutdown);
+
/* Validate whether US EU switch is present or not */
static int msm_prepare_us_euro(struct snd_soc_card *card)
{
diff --git a/asoc/sdm660-common.h b/asoc/sdm660-common.h
index 3df6bf8..418bbf6 100644
--- a/asoc/sdm660-common.h
+++ b/asoc/sdm660-common.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -134,6 +134,8 @@
void msm_aux_pcm_snd_shutdown(struct snd_pcm_substream *substream);
int msm_mi2s_snd_startup(struct snd_pcm_substream *substream);
void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream);
+int msm_tdm_snd_startup(struct snd_pcm_substream *substream);
+void msm_tdm_snd_shutdown(struct snd_pcm_substream *substream);
int msm_common_snd_controls_size(void);
void msm_set_codec_reg_done(bool done);
#endif
diff --git a/asoc/sdm660-ext-dai-links.c b/asoc/sdm660-ext-dai-links.c
index 3568ebb..66182dd 100644
--- a/asoc/sdm660-ext-dai-links.c
+++ b/asoc/sdm660-ext-dai-links.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -278,7 +278,9 @@
}
static struct snd_soc_ops msm_tdm_be_ops = {
- .hw_params = msm_tdm_snd_hw_params
+ .startup = msm_tdm_snd_startup,
+ .shutdown = msm_tdm_snd_shutdown,
+ .hw_params = msm_tdm_snd_hw_params,
};
static int msm_fe_qos_prepare(struct snd_pcm_substream *substream)
diff --git a/asoc/sdm660-internal.c b/asoc/sdm660-internal.c
index ebcea54..b35179a 100644
--- a/asoc/sdm660-internal.c
+++ b/asoc/sdm660-internal.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1890,7 +1890,9 @@
}
static struct snd_soc_ops msm_tdm_be_ops = {
- .hw_params = msm_tdm_snd_hw_params
+ .startup = msm_tdm_snd_startup,
+ .shutdown = msm_tdm_snd_shutdown,
+ .hw_params = msm_tdm_snd_hw_params,
};
static struct snd_soc_ops msm_wcn_ops = {
diff --git a/dsp/q6adm.c b/dsp/q6adm.c
index e42e329..03fa6a6 100644
--- a/dsp/q6adm.c
+++ b/dsp/q6adm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -125,6 +125,8 @@
}
};
+static struct adm_multi_ch_map port_channel_map[AFE_MAX_PORTS];
+
static int adm_get_parameters[MAX_COPPS_PER_PORT * ADM_GET_PARAMETER_LENGTH];
static int adm_module_topo_list[
MAX_COPPS_PER_PORT * ADM_GET_TOPO_MODULE_LIST_LENGTH];
@@ -1459,6 +1461,31 @@
}
EXPORT_SYMBOL(adm_get_multi_ch_map);
+/**
+ * adm_set_port_multi_ch_map -
+ * Update port specific channel map info
+ *
+ * @channel_map: pointer with channel map info
+ * @port_id: port for which chmap is set
+ */
+void adm_set_port_multi_ch_map(char *channel_map, int port_id)
+{
+ int port_idx;
+
+ port_id = q6audio_convert_virtual_to_portid(port_id);
+ port_idx = adm_validate_and_get_port_index(port_id);
+
+ if (port_idx < 0) {
+ pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id);
+ return;
+ }
+
+ memcpy(port_channel_map[port_idx].channel_mapping, channel_map,
+ PCM_FORMAT_MAX_NUM_CHANNEL);
+ port_channel_map[port_idx].set_channel_map = true;
+}
+EXPORT_SYMBOL(adm_set_port_multi_ch_map);
+
static void adm_reset_data(void)
{
int i, j;
@@ -2441,7 +2468,7 @@
EXPORT_SYMBOL(adm_connect_afe_port);
int adm_arrange_mch_map(struct adm_cmd_device_open_v5 *open, int path,
- int channel_mode)
+ int channel_mode, int port_idx)
{
int rc = 0, idx;
@@ -2457,10 +2484,18 @@
default:
goto non_mch_path;
};
- if ((open->dev_num_channel > 2) && multi_ch_maps[idx].set_channel_map) {
- memcpy(open->dev_channel_mapping,
- multi_ch_maps[idx].channel_mapping,
- PCM_FORMAT_MAX_NUM_CHANNEL);
+
+ if ((open->dev_num_channel > 2) &&
+ (port_channel_map[port_idx].set_channel_map ||
+ multi_ch_maps[idx].set_channel_map)) {
+ if (port_channel_map[port_idx].set_channel_map)
+ memcpy(open->dev_channel_mapping,
+ port_channel_map[port_idx].channel_mapping,
+ PCM_FORMAT_MAX_NUM_CHANNEL);
+ else
+ memcpy(open->dev_channel_mapping,
+ multi_ch_maps[idx].channel_mapping,
+ PCM_FORMAT_MAX_NUM_CHANNEL);
} else {
if (channel_mode == 1) {
open->dev_channel_mapping[0] = PCM_CHANNEL_FC;
@@ -2750,7 +2785,8 @@
(rate != ULL_SUPPORTED_SAMPLE_RATE));
open.sample_rate = rate;
- ret = adm_arrange_mch_map(&open, path, channel_mode);
+ ret = adm_arrange_mch_map(&open, path, channel_mode,
+ port_idx);
if (ret)
return ret;
@@ -2898,7 +2934,7 @@
atomic_read(&this_adm.copp.channels[port_idx][copp_idx]);
rc = adm_arrange_mch_map(&open, ADM_PATH_PLAYBACK,
- mfc_cfg.num_channels);
+ mfc_cfg.num_channels, port_idx);
if (rc < 0) {
pr_err("%s: unable to get channal map\n", __func__);
goto fail_cmd;
@@ -3212,6 +3248,7 @@
return -EINVAL;
}
+ port_channel_map[port_idx].set_channel_map = false;
if (this_adm.copp.adm_delay[port_idx][copp_idx] && perf_mode
== LEGACY_PCM_MODE) {
atomic_set(&this_adm.copp.adm_delay_stat[port_idx][copp_idx],
diff --git a/dsp/q6afe.c b/dsp/q6afe.c
index 4ecceff..8e6fa1d 100644
--- a/dsp/q6afe.c
+++ b/dsp/q6afe.c
@@ -371,14 +371,20 @@
return -EINVAL;
}
+ if (rtac_make_afe_callback(data->payload,
+ data->payload_size))
+ return 0;
+
+ if (data->payload_size < 3 * sizeof(uint32_t)) {
+ pr_err("%s: Error: size %d is less than expected\n",
+ __func__, data->payload_size);
+ return -EINVAL;
+ }
+
if (payload[2] == AFE_PARAM_ID_DEV_TIMING_STATS) {
av_dev_drift_afe_cb_handler(data->payload,
data->payload_size);
} else {
- if (rtac_make_afe_callback(data->payload,
- data->payload_size))
- return 0;
-
if (sp_make_afe_callback(data->payload,
data->payload_size))
return -EINVAL;
@@ -393,6 +399,11 @@
payload = data->payload;
if (data->opcode == APR_BASIC_RSP_RESULT) {
+ if (data->payload_size < (2 * sizeof(uint32_t))) {
+ pr_err("%s: Error: size %d is less than expected\n",
+ __func__, data->payload_size);
+ return -EINVAL;
+ }
pr_debug("%s:opcode = 0x%x cmd = 0x%x status = 0x%x token=%d\n",
__func__, data->opcode,
payload[0], payload[1], data->token);
diff --git a/include/dsp/apr_audio-v2.h b/include/dsp/apr_audio-v2.h
index d313d64..dbeb6de 100644
--- a/include/dsp/apr_audio-v2.h
+++ b/include/dsp/apr_audio-v2.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -4306,9 +4306,14 @@
/* Rear left of center. */
#define PCM_CHANNEL_RLC 15
-/* Rear right of center. */
+/* Rear right of center. Update PCM_MAX_CHMAP_ID when
+ * this list is extended.
+ */
#define PCM_CHANNEL_RRC 16
+/* Max valid channel map index */
+#define PCM_MAX_CHMAP_ID PCM_CHANNEL_RRC
+
#define PCM_FORMAT_MAX_NUM_CHANNEL 8
#define ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2 0x00010DA5
diff --git a/include/dsp/q6adm-v2.h b/include/dsp/q6adm-v2.h
index 5308b6e..fcd426c 100644
--- a/include/dsp/q6adm-v2.h
+++ b/include/dsp/q6adm-v2.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -134,6 +134,8 @@
int adm_get_multi_ch_map(char *channel_map, int path);
+void adm_set_port_multi_ch_map(char *channel_map, int port_id);
+
int adm_validate_and_get_port_index(int port_id);
int adm_get_default_copp_idx(int port_id);
diff --git a/ipc/apr.c b/ipc/apr.c
index f1f3e49..2d3225f 100644
--- a/ipc/apr.c
+++ b/ipc/apr.c
@@ -28,6 +28,7 @@
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/ipc_logging.h>
+#include <linux/of_device.h>
#include <soc/qcom/subsystem_restart.h>
#include <soc/qcom/scm.h>
#include <dsp/apr_audio-v2.h>
@@ -56,6 +57,7 @@
};
static bool apr_cf_debug;
+static struct delayed_work add_chld_dev_work;
#ifdef CONFIG_DEBUG_FS
static struct dentry *debugfs_apr_debug;
@@ -282,6 +284,17 @@
dispatch_event(opcode, APR_DEST_QDSP6);
}
+static void apr_add_child_devices(struct work_struct *work)
+{
+ int ret;
+
+ ret = of_platform_populate(apr_dev_ptr->of_node,
+ NULL, NULL, apr_dev_ptr);
+ if (ret)
+ dev_err(apr_dev_ptr, "%s: failed to add child nodes, ret=%d\n",
+ __func__, ret);
+}
+
static void apr_adsp_up(void)
{
if (apr_cmpxchg_q6_state(APR_SUBSYS_DOWN, APR_SUBSYS_LOADED) ==
@@ -289,29 +302,8 @@
wake_up(&dsp_wait);
if (!is_child_devices_loaded) {
- struct platform_device *pdev;
- struct device_node *node;
- int ret;
-
- for_each_child_of_node(apr_dev_ptr->of_node, node) {
- pdev = platform_device_alloc(node->name, -1);
- if (!pdev) {
- dev_err(apr_dev_ptr, "%s: pdev memory alloc failed\n",
- __func__);
- return;
- }
- pdev->dev.parent = apr_dev_ptr;
- pdev->dev.of_node = node;
-
- ret = platform_device_add(pdev);
- if (ret) {
- dev_err(apr_dev_ptr,
- "%s: Cannot add platform device\n",
- __func__);
- platform_device_put(pdev);
- return;
- }
- }
+ schedule_delayed_work(&add_chld_dev_work,
+ msecs_to_jiffies(100));
is_child_devices_loaded = true;
}
}
@@ -1180,6 +1172,7 @@
apr_tal_init();
apr_dev_ptr = &pdev->dev;
+ INIT_DELAYED_WORK(&add_chld_dev_work, apr_add_child_devices);
return apr_debug_init();
}