sound: safety-i2s: Add support for always-on rx/tx

Add support for keeping RX/TX always ON by using amixer commands
and to not disable clocks after playback/capture usecases finish.
This is to prevent A2B master from losing internal state.

Bug 3742979

Change-Id: I01dacaa3f54c98a7a14be101d8f0a315b006d7d8
Signed-off-by: Niranjan Dighe <ndighe@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2966000
Reviewed-by: Uday Gupta <udayg@nvidia.com>
Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com>
GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
Tested-by: Purvi Medawala <pmedawala@nvidia.com>
This commit is contained in:
Niranjan Dighe
2023-07-17 11:39:04 +00:00
committed by mobile promotions
parent 36531e7e1d
commit aaa19719df
3 changed files with 121 additions and 12 deletions

View File

@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only // SPDX-License-Identifier: GPL-2.0-only
/* /*
* Copyright (c) 2021-2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
@@ -90,6 +90,9 @@ unsigned int i2s_enable_tx(unsigned int id)
{ {
unsigned int val; unsigned int val;
if (is_i2s_tx_enabled(id))
return 0;
val = readl(I2S_BASE(id) + T234_I2S_TX_ENABLE); val = readl(I2S_BASE(id) + T234_I2S_TX_ENABLE);
val |= T234_I2S_TX_EN; val |= T234_I2S_TX_EN;
writel(val, I2S_BASE(id) + T234_I2S_TX_ENABLE); writel(val, I2S_BASE(id) + T234_I2S_TX_ENABLE);
@@ -110,6 +113,9 @@ unsigned int i2s_enable_rx(unsigned int id)
{ {
unsigned int val; unsigned int val;
if (is_i2s_rx_enabled(id))
return 0;
val = readl(I2S_BASE(id) + T234_I2S_RX_ENABLE); val = readl(I2S_BASE(id) + T234_I2S_RX_ENABLE);
val |= T234_I2S_RX_EN; val |= T234_I2S_RX_EN;
writel(val, I2S_BASE(id) + T234_I2S_RX_ENABLE); writel(val, I2S_BASE(id) + T234_I2S_RX_ENABLE);
@@ -150,8 +156,6 @@ unsigned int i2s_set_loopback(unsigned int id, unsigned int loopback_enable)
rx_enable = is_i2s_rx_enabled(id); rx_enable = is_i2s_rx_enabled(id);
if (rx_enable) if (rx_enable)
i2s_disable_rx(id); i2s_disable_rx(id);
i2s_disable(id);
} }
val = readl(I2S_BASE(id) + T234_I2S_CTRL); val = readl(I2S_BASE(id) + T234_I2S_CTRL);
@@ -160,8 +164,6 @@ unsigned int i2s_set_loopback(unsigned int id, unsigned int loopback_enable)
writel(val, I2S_BASE(id) + T234_I2S_CTRL); writel(val, I2S_BASE(id) + T234_I2S_CTRL);
if (enable) { if (enable) {
i2s_enable(id);
if (rx_enable) if (rx_enable)
i2s_enable_rx(id); i2s_enable_rx(id);
@@ -262,6 +264,9 @@ unsigned int i2s_disable_rx(unsigned int id)
{ {
unsigned int val; unsigned int val;
if (i2s_inst(id)->config.rx_always_on)
return 0;
val = readl(I2S_BASE(id) + T234_I2S_RX_ENABLE); val = readl(I2S_BASE(id) + T234_I2S_RX_ENABLE);
val &= ~T234_I2S_RX_EN; val &= ~T234_I2S_RX_EN;
writel(val, I2S_BASE(id) + T234_I2S_RX_ENABLE); writel(val, I2S_BASE(id) + T234_I2S_RX_ENABLE);
@@ -297,6 +302,9 @@ unsigned int i2s_disable_tx(unsigned int id)
{ {
unsigned int val; unsigned int val;
if (i2s_inst(id)->config.tx_always_on)
return 0;
val = readl(I2S_BASE(id) + T234_I2S_TX_ENABLE); val = readl(I2S_BASE(id) + T234_I2S_TX_ENABLE);
val &= ~T234_I2S_TX_EN; val &= ~T234_I2S_TX_EN;
writel(val, I2S_BASE(id) + T234_I2S_TX_ENABLE); writel(val, I2S_BASE(id) + T234_I2S_TX_ENABLE);
@@ -472,5 +480,7 @@ int i2s_configure(unsigned int id, struct i2s_config *config)
writel(fifo_ctrl, i2s_base + T234_I2S_TX_FIFO_CTRL); writel(fifo_ctrl, i2s_base + T234_I2S_TX_FIFO_CTRL);
writel(threshold, i2s_base + T234_I2S_TX_START_THRESHOLD); writel(threshold, i2s_base + T234_I2S_TX_START_THRESHOLD);
i2s_enable(id);
return 0; return 0;
} }

View File

@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only // SPDX-License-Identifier: GPL-2.0-only
/* /*
* Copyright (c) 2021-2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*/ */
#define pr_fmt(msg) "Safety I2S: " msg #define pr_fmt(msg) "Safety I2S: " msg
@@ -94,6 +94,41 @@ static int loopback_control_get(struct snd_kcontrol *kctl,
return 0; return 0;
} }
static int alwayson_control_put(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *uc)
{
int rx = (int)kctl->private_value & 1;
int id = (int)kctl->private_value >> 1;
int enable = uc->value.integer.value[0];
struct i2s_config *i2s_config = &i2s[id].config;
if (rx)
i2s_config->rx_always_on = enable;
else
i2s_config->tx_always_on = enable;
if (enable && rx)
i2s_enable_rx(id);
if (enable && !rx)
i2s_enable_tx(id);
return 0;
}
static int alwayson_control_get(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *uc)
{
int rx = (int)kctl->private_value & 1;
int id = (int)kctl->private_value >> 1;
struct i2s_config *i2s_config = &i2s[id].config;
if (rx)
return i2s_config->rx_always_on;
return i2s_config->tx_always_on;
}
static int snd_myctl_mono_info(struct snd_kcontrol *kcontrol, static int snd_myctl_mono_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo) struct snd_ctl_elem_info *uinfo)
{ {
@@ -105,7 +140,27 @@ static int snd_myctl_mono_info(struct snd_kcontrol *kcontrol,
return 0; return 0;
} }
static const struct snd_kcontrol_new controls[] = { static const struct snd_kcontrol_new controls_i2s7[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "I2S7-RX ON",
.index = 0,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.put = alwayson_control_put,
.get = alwayson_control_get,
.info = snd_myctl_mono_info,
.private_value = 0
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "I2S7-TX ON",
.index = 0,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.put = alwayson_control_put,
.get = alwayson_control_get,
.info = snd_myctl_mono_info,
.private_value = 1
},
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "I2S7 Loopback", .name = "I2S7 Loopback",
@@ -115,7 +170,10 @@ static const struct snd_kcontrol_new controls[] = {
.get = loopback_control_get, .get = loopback_control_get,
.info = snd_myctl_mono_info, .info = snd_myctl_mono_info,
.private_value = 0 .private_value = 0
}, }
};
static const struct snd_kcontrol_new controls_i2s8[] = {
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "I2S8 Loopback", .name = "I2S8 Loopback",
@@ -125,12 +183,43 @@ static const struct snd_kcontrol_new controls[] = {
.get = loopback_control_get, .get = loopback_control_get,
.info = snd_myctl_mono_info, .info = snd_myctl_mono_info,
.private_value = 1 .private_value = 1
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "I2S8-RX ON",
.index = 0,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.put = alwayson_control_put,
.get = alwayson_control_get,
.info = snd_myctl_mono_info,
.private_value = 2
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "I2S8-TX ON",
.index = 0,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.put = alwayson_control_put,
.get = alwayson_control_get,
.info = snd_myctl_mono_info,
.private_value = 3
} }
}; };
static int safety_i2s_add_kcontrols(struct snd_card *card, int id) static int safety_i2s_add_kcontrols(struct snd_card *card, int id)
{ {
return snd_ctl_add(card, snd_ctl_new1(&controls[id], i2s)); int num_of_controls, i, ret;
if (id == 0) {
num_of_controls = ARRAY_SIZE(controls_i2s7);
for (i = 0; i < num_of_controls; i++)
ret = snd_ctl_add(card, snd_ctl_new1(&controls_i2s7[i], i2s));
} else if (id == 1) {
num_of_controls = ARRAY_SIZE(controls_i2s8);
for (i = 0; i < num_of_controls; i++)
ret = snd_ctl_add(card, snd_ctl_new1(&controls_i2s8[i], i2s));
}
return ret;
} }
static int prealloc_dma_buff(struct snd_pcm *pcm, unsigned int stream, size_t size) static int prealloc_dma_buff(struct snd_pcm *pcm, unsigned int stream, size_t size)
@@ -404,7 +493,6 @@ static void safety_i2s_start(struct snd_pcm_substream *substream)
else else
i2s_enable_tx(id); i2s_enable_tx(id);
i2s_enable(id);
data->triggered = 1; data->triggered = 1;
} }
@@ -715,6 +803,15 @@ static int t234_safety_audio_probe(struct platform_device *pdev)
static int t234_safety_audio_remove(struct platform_device *pdev) static int t234_safety_audio_remove(struct platform_device *pdev)
{ {
int i;
for (i = 0; i < NUM_SAFETY_I2S_INST; i++) {
if (!enabled_i2s_mask[i])
continue;
i2s_disable(i);
}
snd_card_free(priv->card); snd_card_free(priv->card);
return 0; return 0;

View File

@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* /*
* Copyright (c) 2021-2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*/ */
#ifndef _TEGRA_I2S_H_ #ifndef _TEGRA_I2S_H_
@@ -61,6 +61,8 @@ struct i2s_config {
unsigned int pcm_mask_bits; unsigned int pcm_mask_bits;
unsigned int highz_ctrl; unsigned int highz_ctrl;
unsigned int clock_trim; unsigned int clock_trim;
unsigned int tx_always_on;
unsigned int rx_always_on;
}; };
struct dma_data { struct dma_data {