mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-22 09:11:26 +03:00
In Linux v6.7, commit b5a95c5bf6d69 ("ASoC: simple_card_utils.h: convert
not to use asoc_xxx()") add new APIs to convert asoc_simple_xxx() to
simple_util_xxx().
Use the conftest to find if soc_snd_util_xxx() present.
Bug 4346767
Change-Id: I3b282eb08d51c997760cc0b9752438c94e3caae5
Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3028578
(cherry picked from commit 3013b107c1)
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3036788
Reviewed-by: Jonathan Hunter <jonathanh@nvidia.com>
Tested-by: Jonathan Hunter <jonathanh@nvidia.com>
GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
288 lines
7.3 KiB
C
288 lines
7.3 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
//
|
|
// tegra_codecs.c - External audio codec setup
|
|
//
|
|
// Copyright (c) 2021-2023, NVIDIA CORPORATION. All rights reserved.
|
|
|
|
#include <nvidia/conftest.h>
|
|
|
|
#include <dt-bindings/sound/tas2552.h>
|
|
#include <linux/input.h>
|
|
#include <sound/jack.h>
|
|
#include <sound/soc.h>
|
|
#include <sound/simple_card_utils.h>
|
|
|
|
#include "tegra_asoc_machine.h"
|
|
#include "tegra_codecs.h"
|
|
|
|
#include <drivers-private/sound/soc/codecs/rt5640.h>
|
|
#include <drivers-private/sound/soc/codecs/rt5659.h>
|
|
#include <drivers-private/sound/soc/codecs/sgtl5000.h>
|
|
|
|
static int tegra_audio_dai_init(struct snd_soc_pcm_runtime *rtd)
|
|
{
|
|
/* Used for audio graph based sound cards only */
|
|
#if defined(NV_ASOC_SIMPLE_RENAMED_SIMPLE) /* Linux 6.7 */
|
|
if (rtd->card->component_chaining)
|
|
return simple_util_dai_init(rtd);
|
|
#else
|
|
if (rtd->card->component_chaining)
|
|
return asoc_simple_dai_init(rtd);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tegra_machine_rt56xx_init(struct snd_soc_pcm_runtime *rtd)
|
|
{
|
|
struct snd_soc_component *cmpnt;
|
|
struct snd_soc_card *card = rtd->card;
|
|
struct snd_soc_jack *jack;
|
|
int err;
|
|
|
|
cmpnt = rtd->dais[rtd->dai_link->num_cpus]->component;
|
|
if (!cmpnt->driver->set_jack)
|
|
goto dai_init;
|
|
|
|
jack = devm_kzalloc(card->dev, sizeof(struct snd_soc_jack), GFP_KERNEL);
|
|
if (!jack)
|
|
return -ENOMEM;
|
|
|
|
#if defined(NV_SND_SOC_CARD_JACK_NEW_HAS_NO_SND_SOC_JACK_PINS) /* Linux v5.19 */
|
|
err = snd_soc_card_jack_new(card, "Headset Jack", SND_JACK_HEADSET,
|
|
jack);
|
|
#else
|
|
err = snd_soc_card_jack_new(card, "Headset Jack", SND_JACK_HEADSET,
|
|
jack, NULL, 0);
|
|
#endif
|
|
if (err) {
|
|
dev_err(card->dev, "Headset Jack creation failed %d\n", err);
|
|
return err;
|
|
}
|
|
|
|
err = tegra_machine_add_codec_jack_control(card, rtd, jack);
|
|
if (err) {
|
|
dev_err(card->dev, "Failed to add jack control: %d\n", err);
|
|
return err;
|
|
}
|
|
|
|
err = cmpnt->driver->set_jack(cmpnt, jack, NULL);
|
|
if (err) {
|
|
dev_err(cmpnt->dev, "Failed to set jack: %d\n", err);
|
|
return err;
|
|
}
|
|
|
|
/* single button supporting play/pause */
|
|
snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_MEDIA);
|
|
|
|
/* multiple buttons supporting play/pause and volume up/down */
|
|
snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_MEDIA);
|
|
snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
|
|
snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
|
|
|
|
snd_soc_dapm_sync(&card->dapm);
|
|
|
|
|
|
dai_init:
|
|
return tegra_audio_dai_init(rtd);
|
|
}
|
|
|
|
static int tegra_machine_fepi_init(struct snd_soc_pcm_runtime *rtd)
|
|
{
|
|
struct device *dev = rtd->card->dev;
|
|
int err;
|
|
|
|
err = snd_soc_dai_set_sysclk(rtd->dais[rtd->dai_link->num_cpus],
|
|
SGTL5000_SYSCLK, 12288000,
|
|
SND_SOC_CLOCK_IN);
|
|
if (err) {
|
|
dev_err(dev, "failed to set sgtl5000 sysclk!\n");
|
|
return err;
|
|
}
|
|
|
|
return tegra_audio_dai_init(rtd);
|
|
}
|
|
|
|
static int tegra_machine_respeaker_init(struct snd_soc_pcm_runtime *rtd)
|
|
{
|
|
struct device *dev = rtd->card->dev;
|
|
int err;
|
|
|
|
/* ac108 codec driver hardcodes the freq as 24000000
|
|
* and source as PLL irrespective of args passed through
|
|
* this callback
|
|
*/
|
|
err = snd_soc_dai_set_sysclk(rtd->dais[rtd->dai_link->num_cpus],
|
|
0, 24000000, SND_SOC_CLOCK_IN);
|
|
if (err) {
|
|
dev_err(dev, "failed to set ac108 sysclk!\n");
|
|
return err;
|
|
}
|
|
|
|
return tegra_audio_dai_init(rtd);
|
|
}
|
|
|
|
static struct snd_soc_pcm_runtime *get_pcm_runtime(struct snd_soc_card *card,
|
|
const char *link_name)
|
|
{
|
|
struct snd_soc_pcm_runtime *rtd;
|
|
|
|
for_each_card_rtds(card, rtd) {
|
|
if (!strcmp(rtd->dai_link->name, link_name))
|
|
return rtd;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int set_pll_sysclk(struct device *dev, struct snd_soc_pcm_runtime *rtd,
|
|
int pll_src, int clk_id, unsigned int srate,
|
|
unsigned int channels)
|
|
{
|
|
struct snd_soc_pcm_stream *dai_params;
|
|
unsigned int bclk_rate;
|
|
int err;
|
|
|
|
#if defined(NV_SND_SOC_DAI_LINK_STRUCT_HAS_C2C_PARAMS_ARG) /* Linux v6.4 */
|
|
dai_params = (struct snd_soc_pcm_stream *)rtd->dai_link->c2c_params;
|
|
#else
|
|
dai_params = (struct snd_soc_pcm_stream *)rtd->dai_link->params;
|
|
#endif
|
|
|
|
switch (dai_params->formats) {
|
|
case SNDRV_PCM_FMTBIT_S8:
|
|
bclk_rate = srate * channels * 8;
|
|
break;
|
|
case SNDRV_PCM_FMTBIT_S16_LE:
|
|
bclk_rate = srate * channels * 16;
|
|
break;
|
|
case SNDRV_PCM_FMTBIT_S24_LE:
|
|
bclk_rate = srate * channels * 24;
|
|
break;
|
|
case SNDRV_PCM_FMTBIT_S32_LE:
|
|
bclk_rate = srate * channels * 32;
|
|
break;
|
|
default:
|
|
dev_err(dev, "invalid format %llu\n",
|
|
dai_params->formats);
|
|
return -EINVAL;
|
|
}
|
|
|
|
err = snd_soc_dai_set_pll(rtd->dais[rtd->dai_link->num_cpus], 0,
|
|
pll_src, bclk_rate, srate * 256);
|
|
if (err < 0) {
|
|
dev_err(dev, "failed to set codec pll\n");
|
|
return err;
|
|
}
|
|
|
|
err = snd_soc_dai_set_sysclk(rtd->dais[rtd->dai_link->num_cpus], clk_id,
|
|
srate * 256, SND_SOC_CLOCK_IN);
|
|
if (err < 0) {
|
|
dev_err(dev, "dais[%d] clock not set\n",
|
|
rtd->dai_link->num_cpus);
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int tegra_codecs_runtime_setup(struct snd_soc_card *card,
|
|
unsigned int srate,
|
|
unsigned int channels,
|
|
unsigned int aud_mclk)
|
|
{
|
|
struct snd_soc_pcm_runtime *rtd;
|
|
int i, err;
|
|
|
|
rtd = get_pcm_runtime(card, "rt565x-playback");
|
|
if (rtd) {
|
|
err = snd_soc_dai_set_sysclk(rtd->dais[rtd->dai_link->num_cpus],
|
|
RT5659_SCLK_S_MCLK,
|
|
aud_mclk, SND_SOC_CLOCK_IN);
|
|
if (err < 0) {
|
|
dev_err(card->dev, "dais[%d] clock not set\n",
|
|
rtd->dai_link->num_cpus);
|
|
return err;
|
|
}
|
|
}
|
|
|
|
rtd = get_pcm_runtime(card, "rt5640-playback");
|
|
if (rtd) {
|
|
err = snd_soc_dai_set_sysclk(rtd->dais[rtd->dai_link->num_cpus],
|
|
RT5640_SCLK_S_MCLK,
|
|
aud_mclk, SND_SOC_CLOCK_IN);
|
|
if (err < 0) {
|
|
dev_err(card->dev, "dais[%d] clock not set\n",
|
|
rtd->dai_link->num_cpus);
|
|
return err;
|
|
}
|
|
}
|
|
|
|
rtd = get_pcm_runtime(card, "rt565x-codec-sysclk-bclk1");
|
|
if (rtd) {
|
|
err = set_pll_sysclk(card->dev, rtd, RT5659_PLL1_S_BCLK1,
|
|
RT5659_SCLK_S_PLL1, srate, channels);
|
|
if (err < 0) {
|
|
dev_err(card->dev, "failed to set pll clk\n");
|
|
return err;
|
|
}
|
|
}
|
|
|
|
rtd = get_pcm_runtime(card, "rt5640-codec-sysclk-bclk1");
|
|
if (rtd) {
|
|
err = set_pll_sysclk(card->dev, rtd, RT5640_PLL1_S_BCLK1,
|
|
RT5640_SCLK_S_PLL1, srate, channels);
|
|
if (err < 0) {
|
|
dev_err(card->dev, "failed to set pll clk\n");
|
|
return err;
|
|
}
|
|
}
|
|
|
|
rtd = get_pcm_runtime(card, "dspk-playback-dual-tas2552");
|
|
if (rtd) {
|
|
for (i = 0; i < rtd->dai_link->num_codecs; i++) {
|
|
if (!strcmp(rtd->dais[rtd->dai_link->num_cpus + i]->name,
|
|
"tas2552-amplifier")) {
|
|
err = snd_soc_dai_set_sysclk(
|
|
rtd->dais[rtd->dai_link->num_cpus + i],
|
|
TAS2552_PDM_CLK_IVCLKIN, aud_mclk,
|
|
SND_SOC_CLOCK_IN);
|
|
if (err < 0) {
|
|
dev_err(card->dev,
|
|
"dais[%d] clock not set\n",
|
|
rtd->dai_link->num_cpus + i);
|
|
return err;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(tegra_codecs_runtime_setup);
|
|
|
|
int tegra_codecs_init(struct snd_soc_card *card)
|
|
{
|
|
struct snd_soc_dai_link *dai_links = card->dai_link;
|
|
int i;
|
|
|
|
if (!dai_links || !card->num_links)
|
|
return -EINVAL;
|
|
|
|
for (i = 0; i < card->num_links; i++) {
|
|
if (strstr(dai_links[i].name, "rt565x-playback") ||
|
|
strstr(dai_links[i].name, "rt5640-playback") ||
|
|
strstr(dai_links[i].name, "rt565x-codec-sysclk-bclk1") ||
|
|
strstr(dai_links[i].name, "rt5640-codec-sysclk-bclk1"))
|
|
dai_links[i].init = tegra_machine_rt56xx_init;
|
|
else if (strstr(dai_links[i].name, "fe-pi-audio-z-v2"))
|
|
dai_links[i].init = tegra_machine_fepi_init;
|
|
else if (strstr(dai_links[i].name, "respeaker-4-mic-array"))
|
|
dai_links[i].init = tegra_machine_respeaker_init;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(tegra_codecs_init);
|
|
|