mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-22 17:25:35 +03:00
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:
committed by
mobile promotions
parent
36531e7e1d
commit
aaa19719df
@@ -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;
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user