ASoC: tegra-alt: switch support for adsp

Change allows support to select one FE among allowed
FE's to be routed to ahub, with rest of the inputs
in drain mode.

Jira EMA-389

Change-Id: I31889a52d67390dcfbf0f2d391acd86bb76cd64e
Signed-off-by: Dipesh Gandhi <dipeshg@nvidia.com>
Reviewed-on: http://git-master/r/1331535
Reviewed-by: Uday Gupta <udayg@nvidia.com>
Reviewed-by: Viraj Karandikar <vkarandikar@nvidia.com>
Reviewed-by: Nitin Pai <npai@nvidia.com>
GVS: Gerrit_Virtual_Submit
This commit is contained in:
Dipesh Gandhi
2017-03-30 18:29:57 +05:30
committed by Sameer Pujar
parent 3f5db4388d
commit 6b67bfa9a0

View File

@@ -125,6 +125,7 @@ struct tegra210_adsp_app {
void *private_data; void *private_data;
int (*msg_handler)(struct tegra210_adsp_app *, apm_msg_t *); int (*msg_handler)(struct tegra210_adsp_app *, apm_msg_t *);
struct work_struct *override_freq_work; struct work_struct *override_freq_work;
spinlock_t apm_msg_queue_lock;
}; };
struct tegra210_adsp_pcm_rtd { struct tegra210_adsp_pcm_rtd {
@@ -143,8 +144,17 @@ struct tegra210_adsp_compr_rtd {
int is_draining; int is_draining;
}; };
#ifdef CONFIG_SND_SOC_TEGRA_VIRT_IVC_COMM
struct tegra210_adsp_switch {
uint32_t admaif_id;
uint32_t allowed_fe;
uint32_t active_fe;
};
#endif
struct adsp_soc_data { struct adsp_soc_data {
bool is_soc_t210; bool is_soc_t210;
uint32_t max_adma_ch;
}; };
struct tegra210_adsp { struct tegra210_adsp {
@@ -152,7 +162,7 @@ struct tegra210_adsp {
struct adsp_soc_data *soc_data; struct adsp_soc_data *soc_data;
struct tegra210_adsp_app apps[TEGRA210_ADSP_VIRT_REG_MAX]; struct tegra210_adsp_app apps[TEGRA210_ADSP_VIRT_REG_MAX];
atomic_t reg_val[TEGRA210_ADSP_VIRT_REG_MAX]; atomic_t reg_val[TEGRA210_ADSP_VIRT_REG_MAX];
DECLARE_BITMAP(adma_usage, TEGRA210_ADSP_ADMA_CHANNEL_COUNT); DECLARE_BITMAP(adma_usage, TEGRA210_ADSP_ADMA_BITMAP_COUNT);
struct clk *ahub_clk; struct clk *ahub_clk;
struct clk *ape_clk; struct clk *ape_clk;
struct clk *apb2ape_clk; struct clk *apb2ape_clk;
@@ -163,6 +173,7 @@ struct tegra210_adsp {
int adsp_started; int adsp_started;
uint32_t adma_ch_page; uint32_t adma_ch_page;
uint32_t adma_ch_start; uint32_t adma_ch_start;
uint32_t adma_ch_cnt;
struct tegra210_adsp_path { struct tegra210_adsp_path {
uint32_t fe_reg; uint32_t fe_reg;
uint32_t be_reg; uint32_t be_reg;
@@ -173,6 +184,8 @@ struct tegra210_adsp {
#ifdef CONFIG_SND_SOC_TEGRA_VIRT_IVC_COMM #ifdef CONFIG_SND_SOC_TEGRA_VIRT_IVC_COMM
struct nvaudio_ivc_ctxt *hivc_client; struct nvaudio_ivc_ctxt *hivc_client;
uint32_t fe_to_admaif_map[ADSP_FE_END - ADSP_FE_START + 1][2]; uint32_t fe_to_admaif_map[ADSP_FE_END - ADSP_FE_START + 1][2];
struct tegra210_adsp_switch switches[MAX_ADSP_SWITCHES];
spinlock_t switch_lock;
#endif #endif
}; };
@@ -453,6 +466,7 @@ static int tegra210_adsp_send_msg(struct tegra210_adsp_app *app,
apm_msg_t *apm_msg, uint32_t flags) apm_msg_t *apm_msg, uint32_t flags)
{ {
int ret = 0; int ret = 0;
unsigned long flag;
if (flags & TEGRA210_ADSP_MSG_FLAG_NEED_ACK) { if (flags & TEGRA210_ADSP_MSG_FLAG_NEED_ACK) {
if (flags & TEGRA210_ADSP_MSG_FLAG_HOLD) { if (flags & TEGRA210_ADSP_MSG_FLAG_HOLD) {
@@ -465,7 +479,22 @@ static int tegra210_adsp_send_msg(struct tegra210_adsp_app *app,
} }
} }
if (!IS_APM_IN(app->reg)) {
uint32_t source;
while (IS_ADSP_APP(app->reg) && !IS_APM_IN(app->reg)) {
source = tegra210_adsp_get_source(app->adsp, app->reg);
app = &app->adsp->apps[source];
}
if (!IS_APM_IN(app->reg)) {
pr_err("%s: No APM found, skip msg sending\n",
__func__);
return ret;
}
}
spin_lock_irqsave(&app->apm_msg_queue_lock, flag);
ret = msgq_queue_message(&app->apm->msgq_recv.msgq, &apm_msg->msgq_msg); ret = msgq_queue_message(&app->apm->msgq_recv.msgq, &apm_msg->msgq_msg);
spin_unlock_irqrestore(&app->apm_msg_queue_lock, flag);
if (ret < 0) { if (ret < 0) {
/* Wakeup APM to consume messages and give it some time */ /* Wakeup APM to consume messages and give it some time */
ret = nvadsp_mbox_send(&app->apm_mbox, apm_cmd_msg_ready, ret = nvadsp_mbox_send(&app->apm_mbox, apm_cmd_msg_ready,
@@ -476,8 +505,10 @@ static int tegra210_adsp_send_msg(struct tegra210_adsp_app *app,
} }
mdelay(20); mdelay(20);
/* Attempt queueing again */ /* Attempt queueing again */
spin_lock_irqsave(&app->apm_msg_queue_lock, flag);
ret = msgq_queue_message(&app->apm->msgq_recv.msgq, ret = msgq_queue_message(&app->apm->msgq_recv.msgq,
&apm_msg->msgq_msg); &apm_msg->msgq_msg);
spin_unlock_irqrestore(&app->apm_msg_queue_lock, flag);
if (ret < 0) { if (ret < 0) {
pr_err("%s: Failed to queue message ret %d\n", pr_err("%s: Failed to queue message ret %d\n",
__func__, ret); __func__, ret);
@@ -498,19 +529,6 @@ static int tegra210_adsp_send_msg(struct tegra210_adsp_app *app,
if (!(flags & TEGRA210_ADSP_MSG_FLAG_NEED_ACK)) if (!(flags & TEGRA210_ADSP_MSG_FLAG_NEED_ACK))
return ret; return ret;
/* Find parent APM to wait for ACK*/
if (!IS_APM_IN(app->reg)) {
uint32_t source;
while (IS_ADSP_APP(app->reg) && !IS_APM_IN(app->reg)) {
source = tegra210_adsp_get_source(app->adsp, app->reg);
app = &app->adsp->apps[source];
}
if (!IS_APM_IN(app->reg)) {
pr_err("%s: No APM found, skip ACK wait\n", __func__);
return ret;
}
}
ret = wait_for_completion_interruptible_timeout( ret = wait_for_completion_interruptible_timeout(
app->msg_complete, app->msg_complete,
msecs_to_jiffies(ADSP_RESPONSE_TIMEOUT)); msecs_to_jiffies(ADSP_RESPONSE_TIMEOUT));
@@ -761,10 +779,6 @@ static int tegra210_adsp_send_data_request_msg(struct tegra210_adsp_app *app,
static int tegra210_adsp_app_init(struct tegra210_adsp *adsp, static int tegra210_adsp_app_init(struct tegra210_adsp *adsp,
struct tegra210_adsp_app *app) struct tegra210_adsp_app *app)
{ {
#ifdef CONFIG_SND_SOC_TEGRA_VIRT_IVC_COMM
struct device *dev = adsp->dev;
struct device_node *node = dev->of_node;
#endif
int ret = 0; int ret = 0;
/* If app is already open or it is APM output pin don't open app */ /* If app is already open or it is APM output pin don't open app */
@@ -783,6 +797,7 @@ static int tegra210_adsp_app_init(struct tegra210_adsp *adsp,
} }
spin_lock_init(&app->lock); spin_lock_init(&app->lock);
spin_lock_init(&app->apm_msg_queue_lock);
app->adsp = adsp; app->adsp = adsp;
app->msg_handler = tegra210_adsp_app_default_msg_handler; app->msg_handler = tegra210_adsp_app_default_msg_handler;
@@ -827,20 +842,13 @@ static int tegra210_adsp_app_init(struct tegra210_adsp *adsp,
apm_out->msg_complete = app->msg_complete; apm_out->msg_complete = app->msg_complete;
} else if (IS_ADMA(app->reg)) { } else if (IS_ADMA(app->reg)) {
app->adma_chan = find_first_zero_bit(adsp->adma_usage, app->adma_chan = find_first_zero_bit(adsp->adma_usage,
TEGRA210_ADSP_ADMA_CHANNEL_COUNT); adsp->adma_ch_cnt);
if (app->adma_chan >= TEGRA210_ADSP_ADMA_CHANNEL_COUNT) { if (app->adma_chan >= adsp->adma_ch_cnt) {
dev_err(adsp->dev, "All ADMA channels are busy"); dev_err(adsp->dev, "All ADMA channels are busy");
return -EBUSY; return -EBUSY;
} }
__set_bit(app->adma_chan, adsp->adma_usage); __set_bit(app->adma_chan, adsp->adma_usage);
#ifdef CONFIG_SND_SOC_TEGRA_VIRT_IVC_COMM
if (of_device_is_compatible(node,
"nvidia,tegra210-adsp-audio-hv"))
app->adma_chan += adsp->adma_ch_start; app->adma_chan += adsp->adma_ch_start;
else
#endif
app->adma_chan += TEGRA210_ADSP_ADMA_CHANNEL_START;
} }
return 0; return 0;
@@ -941,7 +949,7 @@ static int tegra210_adsp_connect_plugin(struct tegra210_adsp *adsp,
src->reg, app->reg); src->reg, app->reg);
ret = tegra210_adsp_send_connect_msg(src, app, ret = tegra210_adsp_send_connect_msg(src, app,
TEGRA210_ADSP_MSG_FLAG_SEND); TEGRA210_ADSP_MSG_FLAG_SEND | TEGRA210_ADSP_MSG_FLAG_NEED_ACK);
if (ret < 0) { if (ret < 0) {
dev_err(adsp->dev, "Connect msg failed. err %d.", ret); dev_err(adsp->dev, "Connect msg failed. err %d.", ret);
return ret; return ret;
@@ -1740,7 +1748,8 @@ static int32_t tegra_adsp_get_admaif_id(
static int tegra_ivc_start_playback( static int tegra_ivc_start_playback(
struct tegra210_adsp *adsp, struct tegra210_adsp *adsp,
uint32_t ivc_msg_admaif_id) uint32_t ivc_msg_admaif_id,
bool ack_required)
{ {
int err = 0; int err = 0;
struct nvaudio_ivc_msg msg; struct nvaudio_ivc_msg msg;
@@ -1748,31 +1757,45 @@ static int tegra_ivc_start_playback(
memset(&msg, 0, sizeof(struct nvaudio_ivc_msg)); memset(&msg, 0, sizeof(struct nvaudio_ivc_msg));
msg.cmd = NVAUDIO_START_PLAYBACK; msg.cmd = NVAUDIO_START_PLAYBACK;
msg.params.dmaif_info.id = ivc_msg_admaif_id; msg.params.dmaif_info.id = ivc_msg_admaif_id;
msg.ack_required = ack_required;
err = nvaudio_ivc_send(adsp->hivc_client, err = nvaudio_ivc_send(adsp->hivc_client,
&msg, &msg,
sizeof(struct nvaudio_ivc_msg)); sizeof(struct nvaudio_ivc_msg));
if (ack_required && err >= 0) {
nvaudio_ivc_receive(adsp->hivc_client,
&msg,
sizeof(struct nvaudio_ivc_msg));
}
return err; return err;
} }
static int tegra_ivc_start_capture( static int tegra_ivc_start_capture(
struct tegra210_adsp *adsp, struct tegra210_adsp *adsp,
uint32_t ivc_msg_admaif_id) uint32_t ivc_msg_admaif_id,
bool ack_required)
{ {
int err = 0; int err = 0;
struct nvaudio_ivc_msg msg; struct nvaudio_ivc_msg msg;
memset(&msg, 0, sizeof(struct nvaudio_ivc_msg)); memset(&msg, 0, sizeof(struct nvaudio_ivc_msg));
msg.cmd = NVAUDIO_START_CAPTURE; msg.cmd = NVAUDIO_START_CAPTURE;
msg.ack_required = ack_required;
msg.params.dmaif_info.id = ivc_msg_admaif_id; msg.params.dmaif_info.id = ivc_msg_admaif_id;
err = nvaudio_ivc_send(adsp->hivc_client, err = nvaudio_ivc_send(adsp->hivc_client,
&msg, &msg,
sizeof(struct nvaudio_ivc_msg)); sizeof(struct nvaudio_ivc_msg));
if (ack_required && err >= 0) {
nvaudio_ivc_receive(adsp->hivc_client,
&msg,
sizeof(struct nvaudio_ivc_msg));
}
return err; return err;
} }
static int tegra_ivc_stop_playback( static int tegra_ivc_stop_playback(
struct tegra210_adsp *adsp, struct tegra210_adsp *adsp,
uint32_t ivc_msg_admaif_id) uint32_t ivc_msg_admaif_id,
bool ack_required)
{ {
int err = 0; int err = 0;
struct nvaudio_ivc_msg msg; struct nvaudio_ivc_msg msg;
@@ -1780,15 +1803,22 @@ static int tegra_ivc_stop_playback(
memset(&msg, 0, sizeof(struct nvaudio_ivc_msg)); memset(&msg, 0, sizeof(struct nvaudio_ivc_msg));
msg.cmd = NVAUDIO_STOP_PLAYBACK; msg.cmd = NVAUDIO_STOP_PLAYBACK;
msg.params.dmaif_info.id = ivc_msg_admaif_id; msg.params.dmaif_info.id = ivc_msg_admaif_id;
msg.ack_required = ack_required;
err = nvaudio_ivc_send(adsp->hivc_client, err = nvaudio_ivc_send(adsp->hivc_client,
&msg, &msg,
sizeof(struct nvaudio_ivc_msg)); sizeof(struct nvaudio_ivc_msg));
if (ack_required && err >= 0) {
nvaudio_ivc_receive(adsp->hivc_client,
&msg,
sizeof(struct nvaudio_ivc_msg));
}
return err; return err;
} }
static int tegra_ivc_stop_capture( static int tegra_ivc_stop_capture(
struct tegra210_adsp *adsp, struct tegra210_adsp *adsp,
uint32_t ivc_msg_admaif_id) uint32_t ivc_msg_admaif_id,
bool ack_required)
{ {
int err = 0; int err = 0;
struct nvaudio_ivc_msg msg; struct nvaudio_ivc_msg msg;
@@ -1796,9 +1826,15 @@ static int tegra_ivc_stop_capture(
memset(&msg, 0, sizeof(struct nvaudio_ivc_msg)); memset(&msg, 0, sizeof(struct nvaudio_ivc_msg));
msg.cmd = NVAUDIO_STOP_CAPTURE; msg.cmd = NVAUDIO_STOP_CAPTURE;
msg.params.dmaif_info.id = ivc_msg_admaif_id; msg.params.dmaif_info.id = ivc_msg_admaif_id;
msg.ack_required = ack_required;
err = nvaudio_ivc_send(adsp->hivc_client, err = nvaudio_ivc_send(adsp->hivc_client,
&msg, &msg,
sizeof(struct nvaudio_ivc_msg)); sizeof(struct nvaudio_ivc_msg));
if (ack_required && err >= 0) {
nvaudio_ivc_receive(adsp->hivc_client,
&msg,
sizeof(struct nvaudio_ivc_msg));
}
return err; return err;
} }
@@ -1829,10 +1865,10 @@ static uint32_t tegra210_adsp_hv_pcm_trigger(
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (stream == SNDRV_PCM_STREAM_PLAYBACK) { if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
ret = tegra_ivc_start_playback(adsp, ret = tegra_ivc_start_playback(adsp,
ivc_msg_admaif_id); ivc_msg_admaif_id, false);
} else { } else {
ret = tegra_ivc_start_capture(adsp, ret = tegra_ivc_start_capture(adsp,
ivc_msg_admaif_id); ivc_msg_admaif_id, false);
} }
if (ret < 0) { if (ret < 0) {
@@ -1845,10 +1881,10 @@ static uint32_t tegra210_adsp_hv_pcm_trigger(
case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_SUSPEND:
if (stream == SNDRV_PCM_STREAM_PLAYBACK) { if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
ret = tegra_ivc_stop_playback(adsp, ret = tegra_ivc_stop_playback(adsp,
ivc_msg_admaif_id); ivc_msg_admaif_id, false);
} else { } else {
ret = tegra_ivc_stop_capture(adsp, ret = tegra_ivc_stop_capture(adsp,
ivc_msg_admaif_id); ivc_msg_admaif_id, false);
} }
if (ret < 0) { if (ret < 0) {
@@ -2310,6 +2346,10 @@ static int tegra210_adsp_null_sink_hw_params(struct snd_soc_dapm_widget *w,
int32_t source, be_reg = w->reg, apm_in_reg; int32_t source, be_reg = w->reg, apm_in_reg;
int ret, num_params; int ret, num_params;
apm_msg_t apm_msg; apm_msg_t apm_msg;
#ifdef CONFIG_SND_SOC_TEGRA_VIRT_IVC_COMM
int32_t admaif_id;
unsigned long flags;
#endif
uint32_t max_bytes = adsp_pcm_hardware.buffer_bytes_max; uint32_t max_bytes = adsp_pcm_hardware.buffer_bytes_max;
if (!adsp->adsp_started) if (!adsp->adsp_started)
@@ -2393,10 +2433,25 @@ static int tegra210_adsp_null_sink_hw_params(struct snd_soc_dapm_widget *w,
apm_msg.msg.fx_set_param_params.params[1] = apm_msg.msg.fx_set_param_params.params[1] =
nvfx_adma_set_null_sink_mode; nvfx_adma_set_null_sink_mode;
if (event == SND_SOC_DAPM_POST_PMD) if (event == SND_SOC_DAPM_POST_PMD) {
#ifdef CONFIG_SND_SOC_TEGRA_VIRT_IVC_COMM
spin_lock_irqsave(&adsp->switch_lock, flags);
ret = tegra_adsp_get_connected_fe(adsp, be_reg,
SNDRV_PCM_STREAM_PLAYBACK);
admaif_id = adsp->fe_to_admaif_map[ret - 1]
[SNDRV_PCM_STREAM_PLAYBACK];
if (admaif_id) {
tegra_adsp_set_admaif_id(adsp, 0, be_reg,
SNDRV_PCM_STREAM_CAPTURE);
spin_unlock_irqrestore(&adsp->switch_lock, flags);
tegra_ivc_stop_playback(adsp, admaif_id - 1, true);
return 0;
}
spin_unlock_irqrestore(&adsp->switch_lock, flags);
#endif
/* set adma in playback mode */ /* set adma in playback mode */
apm_msg.msg.fx_set_param_params.params[2] = 0; apm_msg.msg.fx_set_param_params.params[2] = 0;
else } else
/* set adma in drain mode */ /* set adma in drain mode */
apm_msg.msg.fx_set_param_params.params[2] = 1; apm_msg.msg.fx_set_param_params.params[2] = 1;
@@ -3600,7 +3655,12 @@ static const struct snd_soc_dapm_widget tegra210_adsp_widgets[] = {
{ name " MUX", "ADMA7-TX", "ADMA7-TX TX"}, \ { name " MUX", "ADMA7-TX", "ADMA7-TX TX"}, \
{ name " MUX", "ADMA8-TX", "ADMA8-TX TX"}, \ { name " MUX", "ADMA8-TX", "ADMA8-TX TX"}, \
{ name " MUX", "ADMA9-TX", "ADMA9-TX TX"}, \ { name " MUX", "ADMA9-TX", "ADMA9-TX TX"}, \
{ name " MUX", "ADMA10-TX", "ADMA10-TX TX"} { name " MUX", "ADMA10-TX", "ADMA10-TX TX"}, \
{ name " MUX", "ADMA11-TX", "ADMA11-TX TX"}, \
{ name " MUX", "ADMA12-TX", "ADMA12-TX TX"}, \
{ name " MUX", "ADMA13-TX", "ADMA13-TX TX"}, \
{ name " MUX", "ADMA14-TX", "ADMA14-TX TX"}, \
{ name " MUX", "ADMA15-TX", "ADMA15-TX TX"}
#define ADSP_PLUGIN_ROUTES(name) \ #define ADSP_PLUGIN_ROUTES(name) \
{ name " MUX", "PLUGIN1-PLACE-HOLDER", "PLUGIN1-PLACE-HOLDER TX"}, \ { name " MUX", "PLUGIN1-PLACE-HOLDER", "PLUGIN1-PLACE-HOLDER TX"}, \
{ name " MUX", "PLUGIN2-PLACE-HOLDER", "PLUGIN2-PLACE-HOLDER TX"}, \ { name " MUX", "PLUGIN2-PLACE-HOLDER", "PLUGIN2-PLACE-HOLDER TX"}, \
@@ -3647,6 +3707,7 @@ static const struct snd_soc_dapm_widget tegra210_adsp_widgets[] = {
#define ADSP_ADMA_OUT_MUX_ROUTES(name) \ #define ADSP_ADMA_OUT_MUX_ROUTES(name) \
{ name " TX", NULL, name " MUX"}, \ { name " TX", NULL, name " MUX"}, \
ADSP_APM_IN_ROUTES(name), \
ADSP_PLUGIN_ROUTES(name) ADSP_PLUGIN_ROUTES(name)
static const struct snd_soc_dapm_route tegra210_adsp_routes[] = { static const struct snd_soc_dapm_route tegra210_adsp_routes[] = {
@@ -3845,6 +3906,233 @@ static int tegra210_adsp_param_info(struct snd_kcontrol *kcontrol,
return 0; return 0;
} }
#ifdef CONFIG_SND_SOC_TEGRA_VIRT_IVC_COMM
static int tegra_adsp_get_connected_adma(struct tegra210_adsp *adsp,
uint32_t fe_reg)
{
int32_t i, src;
struct tegra210_adsp_app *app = NULL;
for (i = ADMA_START; i <= ADMA_TX_END; i++) {
app = &adsp->apps[i];
src = app->reg;
while (!IS_ADSP_FE(src) && src != 0) {
src = tegra210_adsp_get_source(
adsp, src);
}
if (src != fe_reg)
continue;
return i;
dev_vdbg(adsp->dev, "%s : connected adma %d to fe %d\n",
__func__, i, fe_reg);
}
return 0;
}
static int tegra210_adsp_get_switch(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct soc_enum *control = (void *)kcontrol->private_value;
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
struct tegra210_adsp *adsp = snd_soc_component_get_drvdata(cmpnt);
struct tegra210_adsp_switch *sch = &adsp->switches[control->reg - 1];
ucontrol->value.integer.value[0] = sch->active_fe;
return 0;
}
static int tegra210_adsp_set_switch(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct soc_enum *control = (void *)kcontrol->private_value;
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
struct tegra210_adsp *adsp = snd_soc_component_get_drvdata(cmpnt);
struct tegra210_adsp_switch *sch = &adsp->switches[control->reg - 1];
uint32_t fe_reg = (uint32_t)ucontrol->value.integer.value[0];
uint32_t conn_adma_id = 0, curr_adma_id;
struct tegra210_adsp_app *app_new = NULL, *app_curr = NULL, *app = NULL;
int32_t num_params, ret = 0;
uint32_t source, admaif_id, be_reg, active_be_reg;
apm_msg_t apm_msg_new, apm_msg_curr;
nvfx_adma_init_params_t adma_params;
struct device *dev = adsp->dev;
struct device_node *node = dev->of_node;
unsigned long flags;
if (!adsp->init_done) {
dev_warn(adsp->dev, "ADSP is not booted yet\n");
return -EPERM;
}
admaif_id = sch->admaif_id;
be_reg = adsp->pcm_path[fe_reg]
[SNDRV_PCM_STREAM_PLAYBACK].be_reg;
/* request to move all switch input to drain mode */
if (fe_reg == 0)
goto stop_playback;
if (!IS_ADSP_FE(fe_reg) || !IS_VALID_INPUT(fe_reg, sch->allowed_fe)) {
dev_err(adsp->dev, "Invalid FE\n");
return -EINVAL;
}
if ((sch->active_fe == fe_reg) &&
(adsp->fe_to_admaif_map[sch->active_fe - 1]
[SNDRV_PCM_STREAM_PLAYBACK])) {
dev_info(adsp->dev, "Switch input already active\n");
return 0;
}
if (!IS_NULL_SINK(be_reg)) {
dev_err(adsp->dev, "%s : fe ---> be path incomplete",
__func__);
return -EINVAL;
}
if (of_device_is_compatible(node,
"nvidia,tegra210-adsp-audio-hv")) {
ret = tegra210_adsp_admaif_hv_hw_params(adsp,
NULL,
admaif_id,
be_reg,
&adma_params,
SNDRV_PCM_STREAM_CAPTURE);
if (ret < 0) {
pr_err("error on ivc_send\n");
return -EPERM;
}
}
adma_params.mode = ADMA_MODE_CONTINUOUS;
adma_params.ahub_channel = admaif_id;
adma_params.periods = 4;
adma_params.adma_ch_page = adsp->adma_ch_page;
app = &adsp->apps[be_reg];
source = tegra210_adsp_get_source(adsp, app->reg);
app = &adsp->apps[source];
if (!IS_APM_OUT(app->reg))
return 0;
source = tegra210_adsp_get_source(adsp, app->reg);
app = &adsp->apps[source];
if (!IS_ADMA(app->reg))
return 0;
adma_params.adma_channel = app->adma_chan;
adma_params.direction = ADMA_MEMORY_TO_AHUB;
adma_params.event.pvoid = app->apm->output_event.pvoid;
ret = tegra210_adsp_adma_params_msg(app, &adma_params,
TEGRA210_ADSP_MSG_FLAG_SEND);
if (ret < 0) {
dev_err(adsp->dev, "ADMA params msg failed. %d.", ret);
return ret;
}
conn_adma_id = tegra_adsp_get_connected_adma(adsp, fe_reg);
if (!conn_adma_id) {
dev_vdbg(adsp->dev, "%s : fe%d not connected\n",
__func__, fe_reg);
return -EINVAL;
}
stop_playback:
if (sch->active_fe && (sch->active_fe != fe_reg)) {
active_be_reg = adsp->pcm_path[sch->active_fe]
[SNDRV_PCM_STREAM_PLAYBACK].be_reg;
if (!IS_NULL_SINK(active_be_reg))
dev_err(adsp->dev, "%s : fe ---> be path incomplete",
__func__);
/* skip if fe is active but playback has finished */
spin_lock_irqsave(&adsp->switch_lock, flags);
if ((adsp->fe_to_admaif_map[sch->active_fe - 1]
[SNDRV_PCM_STREAM_PLAYBACK]) == 0) {
spin_unlock_irqrestore(&adsp->switch_lock, flags);
goto start_playback;
}
/* This is needed so that stop trigger is ignored for be_reg */
tegra_adsp_set_admaif_id(adsp,
0, active_be_reg, SNDRV_PCM_STREAM_CAPTURE);
spin_unlock_irqrestore(&adsp->switch_lock, flags);
tegra_ivc_stop_playback(adsp, admaif_id - 1, true);
curr_adma_id = tegra_adsp_get_connected_adma(adsp,
sch->active_fe);
app_curr = &adsp->apps[curr_adma_id];
apm_msg_curr.msgq_msg.size =
MSGQ_MSG_WSIZE(apm_fx_set_param_params_t);
apm_msg_curr.msg.call_params.size =
sizeof(apm_fx_set_param_params_t);
apm_msg_curr.msg.call_params.method =
nvfx_apm_method_fx_set_param;
apm_msg_curr.msg.fx_set_param_params.plugin.pvoid =
app_curr->plugin->plugin.pvoid;
num_params = 3;
apm_msg_curr.msg.fx_set_param_params.params[0] =
(sizeof(nvfx_call_params_t) +
num_params * sizeof(int32_t));
/* initialize the method */
apm_msg_curr.msg.fx_set_param_params.params[1] =
nvfx_adma_set_null_sink_mode;
/* send active switch one to pause */
apm_msg_curr.msg.fx_set_param_params.params[2] = 1;
ret = pm_runtime_get_sync(adsp->dev);
if (ret < 0) {
dev_err(adsp->dev, "%s pm_runtime_get_sync err 0x%x\n",
__func__, ret);
return ret;
}
ret = tegra210_adsp_send_msg(app_curr, &apm_msg_curr,
TEGRA210_ADSP_MSG_FLAG_SEND |
TEGRA210_ADSP_MSG_FLAG_NEED_ACK);
pm_runtime_put(adsp->dev);
}
start_playback:
if (fe_reg == 0) {
sch->active_fe = fe_reg;
return 0;
}
tegra_ivc_start_playback(adsp, admaif_id - 1, false);
app_new = &adsp->apps[conn_adma_id];
apm_msg_new.msgq_msg.size = MSGQ_MSG_WSIZE(apm_fx_set_param_params_t);
apm_msg_new.msg.call_params.size = sizeof(apm_fx_set_param_params_t);
apm_msg_new.msg.call_params.method = nvfx_apm_method_fx_set_param;
apm_msg_new.msg.fx_set_param_params.plugin.pvoid =
app_new->plugin->plugin.pvoid;
num_params = 3;
apm_msg_new.msg.fx_set_param_params.params[0] =
(sizeof(nvfx_call_params_t) +
num_params * sizeof(int32_t));
/* initialize the method */
apm_msg_new.msg.fx_set_param_params.params[1] =
nvfx_adma_set_null_sink_mode;
/* send new switch one to unpause */
apm_msg_new.msg.fx_set_param_params.params[2] = 0;
ret = pm_runtime_get_sync(adsp->dev);
if (ret < 0) {
dev_err(adsp->dev, "%s pm_runtime_get_sync error 0x%x\n",
__func__, ret);
return ret;
}
ret = tegra210_adsp_send_msg(app_new, &apm_msg_new,
TEGRA210_ADSP_MSG_FLAG_SEND | TEGRA210_ADSP_MSG_FLAG_NEED_ACK);
pm_runtime_put(adsp->dev);
sch->active_fe = fe_reg;
return 0;
}
#endif
static int tegra210_adsp_get_param(struct snd_kcontrol *kcontrol, static int tegra210_adsp_get_param(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
@@ -4223,6 +4511,26 @@ static int tegra210_adsp_apm_put(struct snd_kcontrol *kcontrol,
SOC_SINGLE_EXT("APM15 " xname, TEGRA210_ADSP_APM_IN15, 0, xmax, 0,\ SOC_SINGLE_EXT("APM15 " xname, TEGRA210_ADSP_APM_IN15, 0, xmax, 0,\
tegra210_adsp_apm_get, tegra210_adsp_apm_put) tegra210_adsp_apm_get, tegra210_adsp_apm_put)
#ifdef CONFIG_SND_SOC_TEGRA_VIRT_IVC_COMM
static const struct soc_enum tegra210_adsp_switch1 =
SOC_ENUM_SINGLE(1/* corresponds to admaif id */, 0, ADSP_FE_END + 1,
tegra210_adsp_mux_texts);
static const struct soc_enum tegra210_adsp_switch2 =
SOC_ENUM_SINGLE(2/* corresponds to admaif id */, 0, ADSP_FE_END + 1,
tegra210_adsp_mux_texts);
static const struct soc_enum tegra210_adsp_switch3 =
SOC_ENUM_SINGLE(3/* corresponds to admaif id */, 0, ADSP_FE_END + 1,
tegra210_adsp_mux_texts);
#define SWITCH_CONTROL(xname, xreg) \
SOC_ENUM_EXT(xname, tegra210_adsp_switch##xreg, \
tegra210_adsp_get_switch, tegra210_adsp_set_switch)
#endif
/* Any new addition of control should be added after PLUGIN controls otherwise /* Any new addition of control should be added after PLUGIN controls otherwise
* the index of PLUGIN needs to be changed with define PLUGIN_SET_PARAMS_IDX and * the index of PLUGIN needs to be changed with define PLUGIN_SET_PARAMS_IDX and
* PLUGIN_SEND_BYTES_IDX */ * PLUGIN_SEND_BYTES_IDX */
@@ -4333,8 +4641,14 @@ static const struct snd_kcontrol_new tegra210_adsp_controls[] = {
APM_CONTROL("Priority", APM_PRIORITY_MAX), APM_CONTROL("Priority", APM_PRIORITY_MAX),
APM_CONTROL("Min ADSP Clock", INT_MAX), APM_CONTROL("Min ADSP Clock", INT_MAX),
APM_CONTROL("Input Mode", INT_MAX), APM_CONTROL("Input Mode", INT_MAX),
#ifdef CONFIG_SND_SOC_TEGRA_VIRT_IVC_COMM
SWITCH_CONTROL("MS_ENT", 1),
SWITCH_CONTROL("MS_PH_PHONE", 2),
SWITCH_CONTROL("MS_ANN", 3),
#endif
}; };
static int tegra210_adsp_component_probe(struct snd_soc_component *component) static int tegra210_adsp_component_probe(struct snd_soc_component *component)
{ {
component->read = tegra210_adsp_read; component->read = tegra210_adsp_read;
@@ -4385,10 +4699,12 @@ static u64 tegra_dma_mask = DMA_BIT_MASK(32);
static struct adsp_soc_data adsp_soc_data_t210 = { static struct adsp_soc_data adsp_soc_data_t210 = {
.is_soc_t210 = true, .is_soc_t210 = true,
.max_adma_ch = TEGRA210_MAX_ADMA_CHANNEL,
}; };
static struct adsp_soc_data adsp_soc_data_t186 = { static struct adsp_soc_data adsp_soc_data_t186 = {
.is_soc_t210 = false, .is_soc_t210 = false,
.max_adma_ch = TEGRA186_MAX_ADMA_CHANNEL,
}; };
static const struct of_device_id tegra210_adsp_audio_of_match[] = { static const struct of_device_id tegra210_adsp_audio_of_match[] = {
@@ -4430,7 +4746,13 @@ static int tegra210_adsp_audio_platform_probe(struct platform_device *pdev)
unsigned int compr_ops = 1; unsigned int compr_ops = 1;
uint32_t adma_ch_page = 0; uint32_t adma_ch_page = 0;
uint32_t adma_ch_start = TEGRA210_ADSP_ADMA_CHANNEL_START_HV; uint32_t adma_ch_start = TEGRA210_ADSP_ADMA_CHANNEL_START_HV;
uint32_t adma_ch_cnt = TEGRA210_ADSP_ADMA_CHANNEL_COUNT;
char plugin_info[20]; char plugin_info[20];
const char **fe_names;
#ifdef CONFIG_SND_SOC_TEGRA_VIRT_IVC_COMM
char switch_info[20];
uint32_t adsp_switch_count;
#endif
pr_info("tegra210_adsp_audio_platform_probe: platform probe started\n"); pr_info("tegra210_adsp_audio_platform_probe: platform probe started\n");
@@ -4608,12 +4930,33 @@ static int tegra210_adsp_audio_platform_probe(struct platform_device *pdev)
} }
adsp->adma_ch_page = adma_ch_page; adsp->adma_ch_page = adma_ch_page;
#ifdef CONFIG_SND_SOC_TEGRA_VIRT_IVC_COMM
adma_ch_start = TEGRA210_ADSP_ADMA_CHANNEL_START_HV;
#else
adma_ch_start = TEGRA210_ADSP_ADMA_CHANNEL_START;
#endif
if (of_property_read_u32_index(pdev->dev.of_node, if (of_property_read_u32_index(pdev->dev.of_node,
"nvidia,adma_ch_start", 0, &adma_ch_start)) { "nvidia,adma_ch_start", 0, &adma_ch_start)) {
dev_info(&pdev->dev, "adma channel start dt entry not found\n"); dev_info(&pdev->dev, "adma channel start dt entry not found\n");
dev_info(&pdev->dev, "using adma channels from 16\n"); dev_info(&pdev->dev, "using adma channels from 16\n");
} }
adsp->adma_ch_start = adma_ch_start; adsp->adma_ch_start = (adma_ch_start > adsp->soc_data->max_adma_ch)
? adsp->soc_data->max_adma_ch : adma_ch_start;
if (of_property_read_u32_index(pdev->dev.of_node,
"nvidia,adma_ch_cnt", 0, &adma_ch_cnt)) {
dev_info(&pdev->dev, "adma channel cnt dt entry not found\n");
dev_info(&pdev->dev, "using default adma channels count 10\n");
}
adsp->adma_ch_cnt = adma_ch_cnt;
if ((adsp->adma_ch_start + adsp->adma_ch_cnt) >
adsp->soc_data->max_adma_ch) {
adsp->adma_ch_cnt =
adsp->soc_data->max_adma_ch - adsp->adma_ch_start;
dev_info(&pdev->dev, "adma channel cnt set to %d\n",
adsp->adma_ch_cnt);
}
ret = snd_soc_register_platform(&pdev->dev, &tegra210_adsp_platform); ret = snd_soc_register_platform(&pdev->dev, &tegra210_adsp_platform);
if (ret) { if (ret) {
@@ -4636,6 +4979,54 @@ static int tegra210_adsp_audio_platform_probe(struct platform_device *pdev)
goto err_unregister_platform; goto err_unregister_platform;
} }
ret = device_property_read_string_array(&pdev->dev, "fe-info",
NULL, 0);
if (ret > 0) {
fe_names = kcalloc(ret, sizeof(*fe_names), GFP_KERNEL);
if (!fe_names)
goto exit;
ret = device_property_read_string_array(&pdev->dev, "fe-info",
fe_names, ret);
ret = ret > ADSP_FE_END ? ADSP_FE_END : ret;
for (i = 0 ; i < ret; i++)
strcpy((char *)tegra210_adsp_mux_texts[1+i],
fe_names[i]);
kfree(fe_names);
}
exit:
#ifdef CONFIG_SND_SOC_TEGRA_VIRT_IVC_COMM
spin_lock_init(&adsp->switch_lock);
/* get switch count */
if (of_property_read_u32(pdev->dev.of_node,
"num-switch",
&adsp_switch_count) < 0) {
dev_info(&pdev->dev, "Missing ADSP switch count\n");
adsp_switch_count = 0;
}
adsp_switch_count = adsp_switch_count > MAX_ADSP_SWITCHES ?
MAX_ADSP_SWITCHES : adsp_switch_count;
for (i = 0; i < adsp_switch_count; i++) {
memset((void *)switch_info, '\0', 20);
sprintf(switch_info, "switch-info-%d", i+1);
subnp = of_get_child_by_name(np, switch_info);
if (subnp) {
if (of_property_read_u32(subnp, "allowed-fe",
&adsp->switches[i].allowed_fe)) {
dev_err(&pdev->dev,
"Missing property allowed-fe\n");
adsp->switches[i].allowed_fe = 0x0;
}
if (of_property_read_u32(subnp, "admaif-id",
&adsp->switches[i].admaif_id)) {
dev_err(&pdev->dev,
"Missing property admaif-id\n");
adsp->switches[i].admaif_id = 0x1;
}
}
}
#endif
pr_info("tegra210_adsp_audio_platform_probe probe successfull."); pr_info("tegra210_adsp_audio_platform_probe probe successfull.");
return 0; return 0;