cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

tegra_audio_graph_card.c (6997B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2//
      3// tegra_audio_graph_card.c - Audio Graph based Tegra Machine Driver
      4//
      5// Copyright (c) 2020-2021 NVIDIA CORPORATION.  All rights reserved.
      6
      7#include <linux/math64.h>
      8#include <linux/module.h>
      9#include <linux/of_device.h>
     10#include <linux/platform_device.h>
     11#include <sound/graph_card.h>
     12#include <sound/pcm_params.h>
     13
     14#define MAX_PLLA_OUT0_DIV 128
     15
     16#define simple_to_tegra_priv(simple) \
     17		container_of(simple, struct tegra_audio_priv, simple)
     18
     19enum srate_type {
     20	/*
     21	 * Sample rates multiple of 8000 Hz and below are supported:
     22	 * ( 8000, 16000, 32000, 48000, 96000, 192000 Hz )
     23	 */
     24	x8_RATE,
     25
     26	/*
     27	 * Sample rates multiple of 11025 Hz and below are supported:
     28	 * ( 11025, 22050, 44100, 88200, 176400 Hz )
     29	 */
     30	x11_RATE,
     31
     32	NUM_RATE_TYPE,
     33};
     34
     35struct tegra_audio_priv {
     36	struct asoc_simple_priv simple;
     37	struct clk *clk_plla_out0;
     38	struct clk *clk_plla;
     39};
     40
     41/* Tegra audio chip data */
     42struct tegra_audio_cdata {
     43	unsigned int plla_rates[NUM_RATE_TYPE];
     44	unsigned int plla_out0_rates[NUM_RATE_TYPE];
     45};
     46
     47/* Setup PLL clock as per the given sample rate */
     48static int tegra_audio_graph_update_pll(struct snd_pcm_substream *substream,
     49					struct snd_pcm_hw_params *params)
     50{
     51	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
     52	struct asoc_simple_priv *simple = snd_soc_card_get_drvdata(rtd->card);
     53	struct tegra_audio_priv *priv = simple_to_tegra_priv(simple);
     54	struct device *dev = rtd->card->dev;
     55	const struct tegra_audio_cdata *data = of_device_get_match_data(dev);
     56	unsigned int plla_rate, plla_out0_rate, bclk;
     57	unsigned int srate = params_rate(params);
     58	int err;
     59
     60	switch (srate) {
     61	case 11025:
     62	case 22050:
     63	case 44100:
     64	case 88200:
     65	case 176400:
     66		plla_out0_rate = data->plla_out0_rates[x11_RATE];
     67		plla_rate = data->plla_rates[x11_RATE];
     68		break;
     69	case 8000:
     70	case 16000:
     71	case 32000:
     72	case 48000:
     73	case 96000:
     74	case 192000:
     75		plla_out0_rate = data->plla_out0_rates[x8_RATE];
     76		plla_rate = data->plla_rates[x8_RATE];
     77		break;
     78	default:
     79		dev_err(rtd->card->dev, "Unsupported sample rate %u\n",
     80			srate);
     81		return -EINVAL;
     82	}
     83
     84	/*
     85	 * Below is the clock relation:
     86	 *
     87	 *	PLLA
     88	 *	  |
     89	 *	  |--> PLLA_OUT0
     90	 *		  |
     91	 *		  |---> I2S modules
     92	 *		  |
     93	 *		  |---> DMIC modules
     94	 *		  |
     95	 *		  |---> DSPK modules
     96	 *
     97	 *
     98	 * Default PLLA_OUT0 rate might be too high when I/O is running
     99	 * at minimum PCM configurations. This may result in incorrect
    100	 * clock rates and glitchy audio. The maximum divider is 128
    101	 * and any thing higher than that won't work. Thus reduce PLLA_OUT0
    102	 * to work for lower configurations.
    103	 *
    104	 * This problem is seen for I2S only, as DMIC and DSPK minimum
    105	 * clock requirements are under allowed divider limits.
    106	 */
    107	bclk = srate * params_channels(params) * params_width(params);
    108	if (div_u64(plla_out0_rate, bclk) > MAX_PLLA_OUT0_DIV)
    109		plla_out0_rate >>= 1;
    110
    111	dev_dbg(rtd->card->dev,
    112		"Update clock rates: PLLA(= %u Hz) and PLLA_OUT0(= %u Hz)\n",
    113		plla_rate, plla_out0_rate);
    114
    115	/* Set PLLA rate */
    116	err = clk_set_rate(priv->clk_plla, plla_rate);
    117	if (err) {
    118		dev_err(rtd->card->dev,
    119			"Can't set plla rate for %u, err: %d\n",
    120			plla_rate, err);
    121		return err;
    122	}
    123
    124	/* Set PLLA_OUT0 rate */
    125	err = clk_set_rate(priv->clk_plla_out0, plla_out0_rate);
    126	if (err) {
    127		dev_err(rtd->card->dev,
    128			"Can't set plla_out0 rate %u, err: %d\n",
    129			plla_out0_rate, err);
    130		return err;
    131	}
    132
    133	return err;
    134}
    135
    136static int tegra_audio_graph_hw_params(struct snd_pcm_substream *substream,
    137				       struct snd_pcm_hw_params *params)
    138{
    139	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    140	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
    141	int err;
    142
    143	/*
    144	 * This gets called for each DAI link (FE or BE) when DPCM is used.
    145	 * We may not want to update PLLA rate for each call. So PLLA update
    146	 * must be restricted to external I/O links (I2S, DMIC or DSPK) since
    147	 * they actually depend on it. I/O modules update their clocks in
    148	 * hw_param() of their respective component driver and PLLA rate
    149	 * update here helps them to derive appropriate rates.
    150	 *
    151	 * TODO: When more HW accelerators get added (like sample rate
    152	 * converter, volume gain controller etc., which don't really
    153	 * depend on PLLA) we need a better way to filter here.
    154	 */
    155	if (cpu_dai->driver->ops && rtd->dai_link->no_pcm) {
    156		err = tegra_audio_graph_update_pll(substream, params);
    157		if (err)
    158			return err;
    159	}
    160
    161	return asoc_simple_hw_params(substream, params);
    162}
    163
    164static const struct snd_soc_ops tegra_audio_graph_ops = {
    165	.startup	= asoc_simple_startup,
    166	.shutdown	= asoc_simple_shutdown,
    167	.hw_params	= tegra_audio_graph_hw_params,
    168};
    169
    170static int tegra_audio_graph_card_probe(struct snd_soc_card *card)
    171{
    172	struct asoc_simple_priv *simple = snd_soc_card_get_drvdata(card);
    173	struct tegra_audio_priv *priv = simple_to_tegra_priv(simple);
    174
    175	priv->clk_plla = devm_clk_get(card->dev, "pll_a");
    176	if (IS_ERR(priv->clk_plla)) {
    177		dev_err(card->dev, "Can't retrieve clk pll_a\n");
    178		return PTR_ERR(priv->clk_plla);
    179	}
    180
    181	priv->clk_plla_out0 = devm_clk_get(card->dev, "plla_out0");
    182	if (IS_ERR(priv->clk_plla_out0)) {
    183		dev_err(card->dev, "Can't retrieve clk plla_out0\n");
    184		return PTR_ERR(priv->clk_plla_out0);
    185	}
    186
    187	return asoc_graph_card_probe(card);
    188}
    189
    190static int tegra_audio_graph_probe(struct platform_device *pdev)
    191{
    192	struct tegra_audio_priv *priv;
    193	struct device *dev = &pdev->dev;
    194	struct snd_soc_card *card;
    195
    196	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
    197	if (!priv)
    198		return -ENOMEM;
    199
    200	card = simple_priv_to_card(&priv->simple);
    201	card->driver_name = "tegra-ape";
    202
    203	card->probe = tegra_audio_graph_card_probe;
    204
    205	/* audio_graph_parse_of() depends on below */
    206	card->component_chaining = 1;
    207	priv->simple.ops = &tegra_audio_graph_ops;
    208	priv->simple.force_dpcm = 1;
    209
    210	return audio_graph_parse_of(&priv->simple, dev);
    211}
    212
    213static const struct tegra_audio_cdata tegra210_data = {
    214	/* PLLA */
    215	.plla_rates[x8_RATE] = 368640000,
    216	.plla_rates[x11_RATE] = 338688000,
    217	/* PLLA_OUT0 */
    218	.plla_out0_rates[x8_RATE] = 49152000,
    219	.plla_out0_rates[x11_RATE] = 45158400,
    220};
    221
    222static const struct tegra_audio_cdata tegra186_data = {
    223	/* PLLA */
    224	.plla_rates[x8_RATE] = 245760000,
    225	.plla_rates[x11_RATE] = 270950400,
    226	/* PLLA_OUT0 */
    227	.plla_out0_rates[x8_RATE] = 49152000,
    228	.plla_out0_rates[x11_RATE] = 45158400,
    229};
    230
    231static const struct of_device_id graph_of_tegra_match[] = {
    232	{ .compatible = "nvidia,tegra210-audio-graph-card",
    233	  .data = &tegra210_data },
    234	{ .compatible = "nvidia,tegra186-audio-graph-card",
    235	  .data = &tegra186_data },
    236	{},
    237};
    238MODULE_DEVICE_TABLE(of, graph_of_tegra_match);
    239
    240static struct platform_driver tegra_audio_graph_card = {
    241	.driver = {
    242		.name = "tegra-audio-graph-card",
    243		.pm = &snd_soc_pm_ops,
    244		.of_match_table = graph_of_tegra_match,
    245	},
    246	.probe = tegra_audio_graph_probe,
    247	.remove = asoc_simple_remove,
    248};
    249module_platform_driver(tegra_audio_graph_card);
    250
    251MODULE_LICENSE("GPL v2");
    252MODULE_DESCRIPTION("ASoC Tegra Audio Graph Sound Card");
    253MODULE_AUTHOR("Sameer Pujar <spujar@nvidia.com>");