mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-22 09:11:26 +03:00
ASoC: tegra-alt: ADSP driver enhancements
1. Fix ADSP crash due to reorder of mixer commands 2. Add support to track the PCM paths constructed, have a record of FE/BE connections made by the user. 3. Support ADSP pipeline breakage, linkage, modifications 4. Plugin params can be set at any time This change depends on http://git-master/r/#/c/709103/ functionality wise. Bug 1605750 Bug 1605316 Bug 200075850 Change-Id: I24af6177f8ae21e7a352a58056a7267a129c3846 Signed-off-by: Arun Shamanna Lakshmi <aruns@nvidia.com> Reviewed-on: http://git-master/r/710448
This commit is contained in:
committed by
Sameer Pujar
parent
fb6932c04a
commit
02d90673d9
@@ -112,6 +112,10 @@ struct tegra210_adsp {
|
||||
DECLARE_BITMAP(adma_usage, TEGRA210_ADSP_ADMA_CHANNEL_COUNT);
|
||||
struct mutex mutex;
|
||||
int init_done;
|
||||
struct tegra210_adsp_path {
|
||||
uint32_t fe_reg;
|
||||
uint32_t be_reg;
|
||||
} pcm_path[ADSP_FE_END+1][2];
|
||||
};
|
||||
|
||||
static const struct snd_pcm_hardware adsp_pcm_hardware = {
|
||||
@@ -352,6 +356,18 @@ static int tegra210_adsp_send_msg(apm_shared_state_t *apm,
|
||||
NVADSP_MBOX_SMSG);
|
||||
}
|
||||
|
||||
static int tegra210_adsp_send_remove_msg(struct tegra210_adsp_app *app,
|
||||
uint32_t flags)
|
||||
{
|
||||
apm_msg_t apm_msg;
|
||||
|
||||
apm_msg.msgq_msg.size = MSGQ_MSG_WSIZE(apm_fx_remove_params_t);
|
||||
apm_msg.msg.call_params.size = sizeof(apm_fx_remove_params_t);
|
||||
apm_msg.msg.call_params.method = nvfx_apm_method_fx_remove_all;
|
||||
|
||||
return tegra210_adsp_send_msg(app->apm, &apm_msg, flags);
|
||||
}
|
||||
|
||||
static int tegra210_adsp_send_connect_msg(struct tegra210_adsp_app *src,
|
||||
struct tegra210_adsp_app *dst,
|
||||
uint32_t flags)
|
||||
@@ -552,8 +568,6 @@ static void tegra210_adsp_app_deinit(struct tegra210_adsp *adsp,
|
||||
return;
|
||||
|
||||
if (app->info && !IS_APM_OUT(app->reg)) {
|
||||
nvadsp_mbox_close(&app->rx_mbox);
|
||||
nvadsp_app_stop(app->info);
|
||||
app->info = NULL;
|
||||
app->plugin = NULL;
|
||||
app->apm = NULL;
|
||||
@@ -564,6 +578,8 @@ static void tegra210_adsp_app_deinit(struct tegra210_adsp *adsp,
|
||||
struct tegra210_adsp_app *apm_out =
|
||||
&adsp->apps[apm_out_reg];
|
||||
|
||||
nvadsp_mbox_close(&app->rx_mbox);
|
||||
nvadsp_app_stop(app->info);
|
||||
apm_out->info = NULL;
|
||||
apm_out->plugin = NULL;
|
||||
apm_out->apm = NULL;
|
||||
@@ -571,9 +587,36 @@ static void tegra210_adsp_app_deinit(struct tegra210_adsp *adsp,
|
||||
}
|
||||
}
|
||||
|
||||
/* Recursive function to connect plugins under a APM */
|
||||
/* API to connect two APMs */
|
||||
static int tegra210_adsp_connect_apm(struct tegra210_adsp *adsp,
|
||||
struct tegra210_adsp_app *app)
|
||||
{
|
||||
uint32_t source = tegra210_adsp_get_source(adsp, app->reg);
|
||||
struct tegra210_adsp_app *src = &adsp->apps[source];
|
||||
int ret = 0;
|
||||
|
||||
/* If both APMs are in connected state no need to
|
||||
send connect message */
|
||||
if (app->connect && src->connect)
|
||||
return 0;
|
||||
|
||||
dev_vdbg(adsp->dev, "Connecting APM 0x%x -> 0x%x",
|
||||
src->reg, app->reg);
|
||||
|
||||
ret = tegra210_adsp_send_connect_msg(src, app,
|
||||
TEGRA210_ADSP_MSG_FLAG_HOLD);
|
||||
if (ret < 0) {
|
||||
dev_err(adsp->dev, "Connect msg failed. err %d.", ret);
|
||||
return ret;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Recursive function to connect plugins under a APM
|
||||
Returns BE/FE on the pcm path */
|
||||
static int tegra210_adsp_connect_plugin(struct tegra210_adsp *adsp,
|
||||
struct tegra210_adsp_app *app)
|
||||
struct tegra210_adsp_app *app,
|
||||
uint32_t *apm_in_src)
|
||||
{
|
||||
struct tegra210_adsp_app *src;
|
||||
uint32_t source;
|
||||
@@ -585,9 +628,30 @@ static int tegra210_adsp_connect_plugin(struct tegra210_adsp *adsp,
|
||||
|
||||
src = &adsp->apps[source];
|
||||
if (!IS_APM_IN(src->reg)) {
|
||||
ret = tegra210_adsp_connect_plugin(adsp, src);
|
||||
ret = tegra210_adsp_connect_plugin(adsp, src, apm_in_src);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
source = tegra210_adsp_get_source(adsp, src->reg);
|
||||
if (IS_APM_OUT(source)) {
|
||||
/* connect plugins inside next APM */
|
||||
ret = tegra210_adsp_connect_plugin(adsp,
|
||||
&adsp->apps[source], apm_in_src);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/* connect APM_IN to APM_OUT */
|
||||
ret = tegra210_adsp_connect_apm(adsp, src);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
/* return if APM_IN is not
|
||||
connected to valid inputs */
|
||||
if (!IS_ADSP_FE(source) &&
|
||||
!IS_ADSP_ADMAIF(source))
|
||||
return -ENODEV;
|
||||
if (apm_in_src)
|
||||
*apm_in_src = source;
|
||||
}
|
||||
}
|
||||
app->apm = src->apm;
|
||||
|
||||
@@ -611,51 +675,129 @@ static int tegra210_adsp_connect_plugin(struct tegra210_adsp *adsp,
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* API to connect two APMs */
|
||||
static int tegra210_adsp_connect_apm(struct tegra210_adsp *adsp,
|
||||
struct tegra210_adsp_app *app)
|
||||
/* Manages FE/BE plugins and deletes if fe_apm is specified */
|
||||
static void tegra210_adsp_manage_plugin(struct tegra210_adsp *adsp,
|
||||
uint32_t end_reg, uint32_t apm_out, struct tegra210_adsp_app *fe_apm)
|
||||
{
|
||||
struct tegra210_adsp_app *src;
|
||||
uint32_t source;
|
||||
int ret = 0;
|
||||
uint32_t j, fe_reg, be_reg;
|
||||
|
||||
source = tegra210_adsp_get_source(adsp, app->reg);
|
||||
if (!IS_ADSP_APP(source))
|
||||
return -EPIPE;
|
||||
|
||||
src = &adsp->apps[source];
|
||||
|
||||
if (IS_APM_OUT(src->reg)) {
|
||||
/* If both APMs are in connected state no need to
|
||||
send connect message */
|
||||
if (app->connect && src->connect)
|
||||
return 0;
|
||||
|
||||
dev_vdbg(adsp->dev, "Connecting APM 0x%x -> 0x%x",
|
||||
src->reg, app->reg);
|
||||
|
||||
ret = tegra210_adsp_send_connect_msg(src, app,
|
||||
TEGRA210_ADSP_MSG_FLAG_HOLD);
|
||||
if (ret < 0) {
|
||||
dev_err(adsp->dev, "Connect msg failed. err %d.", ret);
|
||||
return ret;
|
||||
if (IS_ADSP_FE(end_reg)) {
|
||||
/* manage playback path */
|
||||
fe_reg = end_reg;
|
||||
be_reg = 0;
|
||||
for (j = ADSP_ADMAIF_START; j <= ADSP_ADMAIF_END; j++) {
|
||||
if (tegra210_adsp_get_source(adsp, j) == apm_out) {
|
||||
be_reg = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (be_reg && fe_reg) {
|
||||
if (fe_apm) {
|
||||
dev_vdbg(adsp->dev, "Remove playback FE %d -- BE %d pair",
|
||||
fe_reg, be_reg);
|
||||
tegra210_adsp_send_remove_msg(fe_apm,
|
||||
TEGRA210_ADSP_MSG_FLAG_SEND);
|
||||
adsp->pcm_path[fe_reg][SNDRV_PCM_STREAM_PLAYBACK].fe_reg = 0;
|
||||
adsp->pcm_path[fe_reg][SNDRV_PCM_STREAM_PLAYBACK].be_reg = 0;
|
||||
} else {
|
||||
dev_vdbg(adsp->dev, "Found playback FE %d -- BE %d pair",
|
||||
fe_reg, be_reg);
|
||||
adsp->pcm_path[fe_reg][SNDRV_PCM_STREAM_PLAYBACK].fe_reg = fe_reg;
|
||||
adsp->pcm_path[fe_reg][SNDRV_PCM_STREAM_PLAYBACK].be_reg = be_reg;
|
||||
}
|
||||
}
|
||||
} else if (IS_ADSP_ADMAIF(end_reg)) {
|
||||
/* manage record path */
|
||||
fe_reg = 0;
|
||||
be_reg = end_reg;
|
||||
for (j = ADSP_FE_START; j <= ADSP_FE_END; j++) {
|
||||
if (tegra210_adsp_get_source(adsp, j) == apm_out) {
|
||||
fe_reg = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (be_reg && fe_reg) {
|
||||
if (fe_apm) {
|
||||
dev_vdbg(adsp->dev, "Remove record FE %d -- BE %d pair",
|
||||
fe_reg, be_reg);
|
||||
tegra210_adsp_send_remove_msg(fe_apm,
|
||||
TEGRA210_ADSP_MSG_FLAG_SEND);
|
||||
adsp->pcm_path[fe_reg][SNDRV_PCM_STREAM_CAPTURE].fe_reg = 0;
|
||||
adsp->pcm_path[fe_reg][SNDRV_PCM_STREAM_CAPTURE].be_reg = 0;
|
||||
} else {
|
||||
dev_vdbg(adsp->dev, "Found playback FE %d -- BE %d pair",
|
||||
fe_reg, be_reg);
|
||||
adsp->pcm_path[fe_reg][SNDRV_PCM_STREAM_CAPTURE].fe_reg = fe_reg;
|
||||
adsp->pcm_path[fe_reg][SNDRV_PCM_STREAM_CAPTURE].be_reg = be_reg;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return -EPIPE;
|
||||
}
|
||||
|
||||
/* Iterate over all APMs and establish pending connections */
|
||||
static int tegra210_adsp_update_connection(struct tegra210_adsp *adsp)
|
||||
{
|
||||
int i;
|
||||
int i, ret;
|
||||
uint32_t end_reg;
|
||||
|
||||
for (i = APM_OUT_START; i <= APM_OUT_END; i++)
|
||||
tegra210_adsp_connect_plugin(adsp, &adsp->apps[i]);
|
||||
for (i = APM_OUT_START; i <= APM_OUT_END; i++) {
|
||||
ret = tegra210_adsp_connect_plugin(adsp, &adsp->apps[i], &end_reg);
|
||||
if (ret >= 0) {
|
||||
/* Record FE/BE pair for every successful connection */
|
||||
tegra210_adsp_manage_plugin(adsp, end_reg, i, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = APM_IN_START; i <= APM_IN_END; i++)
|
||||
tegra210_adsp_connect_apm(adsp, &adsp->apps[i]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Remove the plugin connections inside associated APM */
|
||||
static int tegra210_adsp_remove_connection(struct tegra210_adsp *adsp,
|
||||
struct tegra210_adsp_app *plugin)
|
||||
{
|
||||
struct tegra210_adsp_app *app;
|
||||
uint32_t i, source, apm_out = 0;
|
||||
|
||||
if (!IS_ADSP_APP(plugin->reg))
|
||||
return 0;
|
||||
|
||||
for (i = APM_OUT_START; i <= APM_OUT_END; i++) {
|
||||
app = &adsp->apps[i];
|
||||
/* if the path is already broken, do not continue */
|
||||
if (!app->connect)
|
||||
continue;
|
||||
while (app->reg != TEGRA210_ADSP_NONE) {
|
||||
if (app->reg == plugin->reg) {
|
||||
apm_out = i;
|
||||
break;
|
||||
}
|
||||
source = tegra210_adsp_get_source(adsp, app->reg);
|
||||
app = &adsp->apps[source];
|
||||
}
|
||||
if (apm_out != TEGRA210_ADSP_NONE)
|
||||
break;
|
||||
}
|
||||
/* if plugin is not part of any APM, return here */
|
||||
if (apm_out == TEGRA210_ADSP_NONE)
|
||||
return 0;
|
||||
|
||||
/* disconnect the plugins inside APM */
|
||||
app = &adsp->apps[apm_out];
|
||||
while (!IS_APM_IN(app->reg)) {
|
||||
source = tegra210_adsp_get_source(adsp, app->reg);
|
||||
if (!IS_ADSP_APP(source))
|
||||
break;
|
||||
app->connect = 0;
|
||||
app = &adsp->apps[source];
|
||||
}
|
||||
|
||||
/* delete the plugins inside APM */
|
||||
if (IS_APM_IN(app->reg)) {
|
||||
/* clear the FE/BE list */
|
||||
tegra210_adsp_manage_plugin(adsp,
|
||||
tegra210_adsp_get_source(adsp, app->reg),
|
||||
apm_out, app);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -745,6 +887,12 @@ static int tegra210_adsp_compr_open(struct snd_compr_stream *cstream)
|
||||
if (!adsp->init_done)
|
||||
return -ENODEV;
|
||||
|
||||
if (!adsp->pcm_path[fe_reg][cstream->direction].fe_reg ||
|
||||
!adsp->pcm_path[fe_reg][cstream->direction].be_reg) {
|
||||
dev_err(adsp->dev, "Broken Path%d - FE not linked to BE", fe_reg);
|
||||
return -EPIPE;
|
||||
}
|
||||
|
||||
prtd = devm_kzalloc(adsp->dev, sizeof(struct tegra210_adsp_compr_rtd),
|
||||
GFP_KERNEL);
|
||||
if (!prtd) {
|
||||
@@ -1039,6 +1187,12 @@ static int tegra210_adsp_pcm_open(struct snd_pcm_substream *substream)
|
||||
|
||||
dev_vdbg(adsp->dev, "%s", __func__);
|
||||
|
||||
if (!adsp->pcm_path[fe_reg][substream->stream].fe_reg ||
|
||||
!adsp->pcm_path[fe_reg][substream->stream].be_reg) {
|
||||
dev_err(adsp->dev, "Broken Path%d - FE not linked to BE", fe_reg);
|
||||
return -EPIPE;
|
||||
}
|
||||
|
||||
prtd = devm_kzalloc(adsp->dev, sizeof(struct tegra210_adsp_pcm_rtd),
|
||||
GFP_KERNEL);
|
||||
if (!prtd) {
|
||||
@@ -1454,8 +1608,13 @@ static int tegra210_adsp_mux_put(struct snd_kcontrol *kcontrol,
|
||||
if (IS_ADSP_APP(e->reg)) {
|
||||
app = &adsp->apps[e->reg];
|
||||
cur_val = tegra210_adsp_get_source(adsp, e->reg);
|
||||
if (cur_val != val)
|
||||
if (cur_val != val) {
|
||||
if (app->connect) {
|
||||
/* remove existing connections if any */
|
||||
tegra210_adsp_remove_connection(adsp, app);
|
||||
}
|
||||
app->connect = 0;
|
||||
}
|
||||
|
||||
if (val == TEGRA210_ADSP_NONE) {
|
||||
tegra210_adsp_app_deinit(adsp, app);
|
||||
@@ -2197,7 +2356,7 @@ static int tegra210_adsp_audio_platform_probe(struct platform_device *pdev)
|
||||
|
||||
adsp = devm_kzalloc(&pdev->dev, sizeof(*adsp), GFP_KERNEL);
|
||||
if (!adsp) {
|
||||
dev_err(&pdev->dev, "Can't allocate tegra30_adsp_ctx\n");
|
||||
dev_err(&pdev->dev, "Can't allocate tegra210_adsp_ctx\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
dev_set_drvdata(&pdev->dev, adsp);
|
||||
|
||||
Reference in New Issue
Block a user