diff --git a/sound/tegra-safety-audio/i2s.c b/sound/tegra-safety-audio/i2s.c index abeeb9e1..d296de12 100644 --- a/sound/tegra-safety-audio/i2s.c +++ b/sound/tegra-safety-audio/i2s.c @@ -1,6 +1,6 @@ // 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 @@ -90,6 +90,9 @@ unsigned int i2s_enable_tx(unsigned int id) { unsigned int val; + if (is_i2s_tx_enabled(id)) + return 0; + val = readl(I2S_BASE(id) + T234_I2S_TX_ENABLE); val |= T234_I2S_TX_EN; writel(val, I2S_BASE(id) + T234_I2S_TX_ENABLE); @@ -110,6 +113,9 @@ unsigned int i2s_enable_rx(unsigned int id) { unsigned int val; + if (is_i2s_rx_enabled(id)) + return 0; + val = readl(I2S_BASE(id) + T234_I2S_RX_ENABLE); val |= T234_I2S_RX_EN; 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); if (rx_enable) i2s_disable_rx(id); - - i2s_disable(id); } 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); if (enable) { - i2s_enable(id); - if (rx_enable) i2s_enable_rx(id); @@ -262,6 +264,9 @@ unsigned int i2s_disable_rx(unsigned int id) { unsigned int val; + if (i2s_inst(id)->config.rx_always_on) + return 0; + val = readl(I2S_BASE(id) + T234_I2S_RX_ENABLE); val &= ~T234_I2S_RX_EN; writel(val, I2S_BASE(id) + T234_I2S_RX_ENABLE); @@ -297,6 +302,9 @@ unsigned int i2s_disable_tx(unsigned int id) { unsigned int val; + if (i2s_inst(id)->config.tx_always_on) + return 0; + val = readl(I2S_BASE(id) + T234_I2S_TX_ENABLE); val &= ~T234_I2S_TX_EN; 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(threshold, i2s_base + T234_I2S_TX_START_THRESHOLD); + i2s_enable(id); + return 0; -} \ No newline at end of file +} diff --git a/sound/tegra-safety-audio/sound-card.c b/sound/tegra-safety-audio/sound-card.c index f03298c3..65be9d88 100644 --- a/sound/tegra-safety-audio/sound-card.c +++ b/sound/tegra-safety-audio/sound-card.c @@ -1,6 +1,6 @@ // 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 @@ -94,6 +94,41 @@ static int loopback_control_get(struct snd_kcontrol *kctl, 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, struct snd_ctl_elem_info *uinfo) { @@ -105,7 +140,27 @@ static int snd_myctl_mono_info(struct snd_kcontrol *kcontrol, 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, .name = "I2S7 Loopback", @@ -115,7 +170,10 @@ static const struct snd_kcontrol_new controls[] = { .get = loopback_control_get, .info = snd_myctl_mono_info, .private_value = 0 - }, + } +}; + +static const struct snd_kcontrol_new controls_i2s8[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "I2S8 Loopback", @@ -125,12 +183,43 @@ static const struct snd_kcontrol_new controls[] = { .get = loopback_control_get, .info = snd_myctl_mono_info, .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) { - 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) @@ -404,7 +493,6 @@ static void safety_i2s_start(struct snd_pcm_substream *substream) else i2s_enable_tx(id); - i2s_enable(id); 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) { + 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); return 0; diff --git a/sound/tegra-safety-audio/tegra_i2s.h b/sound/tegra-safety-audio/tegra_i2s.h index 73cde6df..61ed4055 100644 --- a/sound/tegra-safety-audio/tegra_i2s.h +++ b/sound/tegra-safety-audio/tegra_i2s.h @@ -1,6 +1,6 @@ /* 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_ @@ -61,6 +61,8 @@ struct i2s_config { unsigned int pcm_mask_bits; unsigned int highz_ctrl; unsigned int clock_trim; + unsigned int tx_always_on; + unsigned int rx_always_on; }; struct dma_data {