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

tegra20_spdif.c (11039B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * tegra20_spdif.c - Tegra20 SPDIF driver
      4 *
      5 * Author: Stephen Warren <swarren@nvidia.com>
      6 * Copyright (C) 2011-2012 - NVIDIA, Inc.
      7 */
      8
      9#include <linux/clk.h>
     10#include <linux/delay.h>
     11#include <linux/device.h>
     12#include <linux/io.h>
     13#include <linux/module.h>
     14#include <linux/of_device.h>
     15#include <linux/platform_device.h>
     16#include <linux/pm_runtime.h>
     17#include <linux/regmap.h>
     18#include <linux/reset.h>
     19#include <linux/slab.h>
     20#include <sound/core.h>
     21#include <sound/pcm.h>
     22#include <sound/pcm_params.h>
     23#include <sound/soc.h>
     24#include <sound/dmaengine_pcm.h>
     25
     26#include "tegra20_spdif.h"
     27
     28static __maybe_unused int tegra20_spdif_runtime_suspend(struct device *dev)
     29{
     30	struct tegra20_spdif *spdif = dev_get_drvdata(dev);
     31
     32	regcache_cache_only(spdif->regmap, true);
     33
     34	clk_disable_unprepare(spdif->clk_spdif_out);
     35
     36	return 0;
     37}
     38
     39static __maybe_unused int tegra20_spdif_runtime_resume(struct device *dev)
     40{
     41	struct tegra20_spdif *spdif = dev_get_drvdata(dev);
     42	int ret;
     43
     44	ret = reset_control_assert(spdif->reset);
     45	if (ret)
     46		return ret;
     47
     48	ret = clk_prepare_enable(spdif->clk_spdif_out);
     49	if (ret) {
     50		dev_err(dev, "clk_enable failed: %d\n", ret);
     51		return ret;
     52	}
     53
     54	usleep_range(10, 100);
     55
     56	ret = reset_control_deassert(spdif->reset);
     57	if (ret)
     58		goto disable_clocks;
     59
     60	regcache_cache_only(spdif->regmap, false);
     61	regcache_mark_dirty(spdif->regmap);
     62
     63	ret = regcache_sync(spdif->regmap);
     64	if (ret)
     65		goto disable_clocks;
     66
     67	return 0;
     68
     69disable_clocks:
     70	clk_disable_unprepare(spdif->clk_spdif_out);
     71
     72	return ret;
     73}
     74
     75static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream,
     76				   struct snd_pcm_hw_params *params,
     77				   struct snd_soc_dai *dai)
     78{
     79	struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev);
     80	unsigned int mask = 0, val = 0;
     81	int ret, spdifclock;
     82	long rate;
     83
     84	mask |= TEGRA20_SPDIF_CTRL_PACK |
     85		TEGRA20_SPDIF_CTRL_BIT_MODE_MASK;
     86	switch (params_format(params)) {
     87	case SNDRV_PCM_FORMAT_S16_LE:
     88		val |= TEGRA20_SPDIF_CTRL_PACK |
     89		       TEGRA20_SPDIF_CTRL_BIT_MODE_16BIT;
     90		break;
     91	default:
     92		return -EINVAL;
     93	}
     94
     95	regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL, mask, val);
     96
     97	/*
     98	 * FIFO trigger level must be bigger than DMA burst or equal to it,
     99	 * otherwise data is discarded on overflow.
    100	 */
    101	regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_DATA_FIFO_CSR,
    102			   TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_MASK,
    103			   TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU4_WORD_FULL);
    104
    105	switch (params_rate(params)) {
    106	case 32000:
    107		spdifclock = 4096000;
    108		break;
    109	case 44100:
    110		spdifclock = 5644800;
    111		break;
    112	case 48000:
    113		spdifclock = 6144000;
    114		break;
    115	case 88200:
    116		spdifclock = 11289600;
    117		break;
    118	case 96000:
    119		spdifclock = 12288000;
    120		break;
    121	case 176400:
    122		spdifclock = 22579200;
    123		break;
    124	case 192000:
    125		spdifclock = 24576000;
    126		break;
    127	default:
    128		return -EINVAL;
    129	}
    130
    131	ret = clk_set_rate(spdif->clk_spdif_out, spdifclock);
    132	if (ret) {
    133		dev_err(dai->dev, "Can't set SPDIF clock rate: %d\n", ret);
    134		return ret;
    135	}
    136
    137	rate = clk_get_rate(spdif->clk_spdif_out);
    138	if (rate != spdifclock)
    139		dev_warn_once(dai->dev,
    140			      "SPDIF clock rate %d doesn't match requested rate %lu\n",
    141			      spdifclock, rate);
    142
    143	return 0;
    144}
    145
    146static void tegra20_spdif_start_playback(struct tegra20_spdif *spdif)
    147{
    148	regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL,
    149			   TEGRA20_SPDIF_CTRL_TX_EN,
    150			   TEGRA20_SPDIF_CTRL_TX_EN);
    151}
    152
    153static void tegra20_spdif_stop_playback(struct tegra20_spdif *spdif)
    154{
    155	regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL,
    156			   TEGRA20_SPDIF_CTRL_TX_EN, 0);
    157}
    158
    159static int tegra20_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
    160				 struct snd_soc_dai *dai)
    161{
    162	struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev);
    163
    164	switch (cmd) {
    165	case SNDRV_PCM_TRIGGER_START:
    166	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
    167	case SNDRV_PCM_TRIGGER_RESUME:
    168		tegra20_spdif_start_playback(spdif);
    169		break;
    170	case SNDRV_PCM_TRIGGER_STOP:
    171	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
    172	case SNDRV_PCM_TRIGGER_SUSPEND:
    173		tegra20_spdif_stop_playback(spdif);
    174		break;
    175	default:
    176		return -EINVAL;
    177	}
    178
    179	return 0;
    180}
    181
    182static int tegra20_spdif_filter_rates(struct snd_pcm_hw_params *params,
    183				      struct snd_pcm_hw_rule *rule)
    184{
    185	struct snd_interval *r = hw_param_interval(params, rule->var);
    186	struct snd_soc_dai *dai = rule->private;
    187	struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev);
    188	struct clk *parent = clk_get_parent(spdif->clk_spdif_out);
    189	static const unsigned int rates[] = { 32000, 44100, 48000 };
    190	long i, parent_rate, valid_rates = 0;
    191
    192	parent_rate = clk_get_rate(parent);
    193	if (parent_rate <= 0) {
    194		dev_err(dai->dev, "Can't get parent clock rate: %ld\n",
    195			parent_rate);
    196		return parent_rate ?: -EINVAL;
    197	}
    198
    199	for (i = 0; i < ARRAY_SIZE(rates); i++) {
    200		if (parent_rate % (rates[i] * 128) == 0)
    201			valid_rates |= BIT(i);
    202	}
    203
    204	/*
    205	 * At least one rate must be valid, otherwise the parent clock isn't
    206	 * audio PLL. Nothing should be filtered in this case.
    207	 */
    208	if (!valid_rates)
    209		valid_rates = BIT(ARRAY_SIZE(rates)) - 1;
    210
    211	return snd_interval_list(r, ARRAY_SIZE(rates), rates, valid_rates);
    212}
    213
    214static int tegra20_spdif_startup(struct snd_pcm_substream *substream,
    215				 struct snd_soc_dai *dai)
    216{
    217	if (!device_property_read_bool(dai->dev, "nvidia,fixed-parent-rate"))
    218		return 0;
    219
    220	/*
    221	 * SPDIF and I2S share audio PLL. HDMI takes audio packets from SPDIF
    222	 * and audio may not work on some TVs if clock rate isn't precise.
    223	 *
    224	 * PLL rate is controlled by I2S side. Filter out audio rates that
    225	 * don't match PLL rate at the start of stream to allow both SPDIF
    226	 * and I2S work simultaneously, assuming that PLL rate won't be
    227	 * changed later on.
    228	 */
    229	return snd_pcm_hw_rule_add(substream->runtime, 0,
    230				   SNDRV_PCM_HW_PARAM_RATE,
    231				   tegra20_spdif_filter_rates, dai,
    232				   SNDRV_PCM_HW_PARAM_RATE, -1);
    233}
    234
    235static int tegra20_spdif_probe(struct snd_soc_dai *dai)
    236{
    237	struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev);
    238
    239	dai->capture_dma_data = NULL;
    240	dai->playback_dma_data = &spdif->playback_dma_data;
    241
    242	return 0;
    243}
    244
    245static const struct snd_soc_dai_ops tegra20_spdif_dai_ops = {
    246	.hw_params = tegra20_spdif_hw_params,
    247	.trigger = tegra20_spdif_trigger,
    248	.startup = tegra20_spdif_startup,
    249};
    250
    251static struct snd_soc_dai_driver tegra20_spdif_dai = {
    252	.name = "tegra20-spdif",
    253	.probe = tegra20_spdif_probe,
    254	.playback = {
    255		.stream_name = "Playback",
    256		.channels_min = 2,
    257		.channels_max = 2,
    258		.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
    259			 SNDRV_PCM_RATE_48000,
    260		.formats = SNDRV_PCM_FMTBIT_S16_LE,
    261	},
    262	.ops = &tegra20_spdif_dai_ops,
    263};
    264
    265static const struct snd_soc_component_driver tegra20_spdif_component = {
    266	.name = "tegra20-spdif",
    267};
    268
    269static bool tegra20_spdif_wr_rd_reg(struct device *dev, unsigned int reg)
    270{
    271	switch (reg) {
    272	case TEGRA20_SPDIF_CTRL:
    273	case TEGRA20_SPDIF_STATUS:
    274	case TEGRA20_SPDIF_STROBE_CTRL:
    275	case TEGRA20_SPDIF_DATA_FIFO_CSR:
    276	case TEGRA20_SPDIF_DATA_OUT:
    277	case TEGRA20_SPDIF_DATA_IN:
    278	case TEGRA20_SPDIF_CH_STA_RX_A:
    279	case TEGRA20_SPDIF_CH_STA_RX_B:
    280	case TEGRA20_SPDIF_CH_STA_RX_C:
    281	case TEGRA20_SPDIF_CH_STA_RX_D:
    282	case TEGRA20_SPDIF_CH_STA_RX_E:
    283	case TEGRA20_SPDIF_CH_STA_RX_F:
    284	case TEGRA20_SPDIF_CH_STA_TX_A:
    285	case TEGRA20_SPDIF_CH_STA_TX_B:
    286	case TEGRA20_SPDIF_CH_STA_TX_C:
    287	case TEGRA20_SPDIF_CH_STA_TX_D:
    288	case TEGRA20_SPDIF_CH_STA_TX_E:
    289	case TEGRA20_SPDIF_CH_STA_TX_F:
    290	case TEGRA20_SPDIF_USR_STA_RX_A:
    291	case TEGRA20_SPDIF_USR_DAT_TX_A:
    292		return true;
    293	default:
    294		return false;
    295	}
    296}
    297
    298static bool tegra20_spdif_volatile_reg(struct device *dev, unsigned int reg)
    299{
    300	switch (reg) {
    301	case TEGRA20_SPDIF_STATUS:
    302	case TEGRA20_SPDIF_DATA_FIFO_CSR:
    303	case TEGRA20_SPDIF_DATA_OUT:
    304	case TEGRA20_SPDIF_DATA_IN:
    305	case TEGRA20_SPDIF_CH_STA_RX_A:
    306	case TEGRA20_SPDIF_CH_STA_RX_B:
    307	case TEGRA20_SPDIF_CH_STA_RX_C:
    308	case TEGRA20_SPDIF_CH_STA_RX_D:
    309	case TEGRA20_SPDIF_CH_STA_RX_E:
    310	case TEGRA20_SPDIF_CH_STA_RX_F:
    311	case TEGRA20_SPDIF_USR_STA_RX_A:
    312	case TEGRA20_SPDIF_USR_DAT_TX_A:
    313		return true;
    314	default:
    315		return false;
    316	}
    317}
    318
    319static bool tegra20_spdif_precious_reg(struct device *dev, unsigned int reg)
    320{
    321	switch (reg) {
    322	case TEGRA20_SPDIF_DATA_OUT:
    323	case TEGRA20_SPDIF_DATA_IN:
    324	case TEGRA20_SPDIF_USR_STA_RX_A:
    325	case TEGRA20_SPDIF_USR_DAT_TX_A:
    326		return true;
    327	default:
    328		return false;
    329	}
    330}
    331
    332static const struct regmap_config tegra20_spdif_regmap_config = {
    333	.reg_bits = 32,
    334	.reg_stride = 4,
    335	.val_bits = 32,
    336	.max_register = TEGRA20_SPDIF_USR_DAT_TX_A,
    337	.writeable_reg = tegra20_spdif_wr_rd_reg,
    338	.readable_reg = tegra20_spdif_wr_rd_reg,
    339	.volatile_reg = tegra20_spdif_volatile_reg,
    340	.precious_reg = tegra20_spdif_precious_reg,
    341	.cache_type = REGCACHE_FLAT,
    342};
    343
    344static int tegra20_spdif_platform_probe(struct platform_device *pdev)
    345{
    346	struct tegra20_spdif *spdif;
    347	struct resource *mem;
    348	void __iomem *regs;
    349	int ret;
    350
    351	spdif = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_spdif),
    352			     GFP_KERNEL);
    353	if (!spdif)
    354		return -ENOMEM;
    355
    356	dev_set_drvdata(&pdev->dev, spdif);
    357
    358	spdif->reset = devm_reset_control_get_exclusive(&pdev->dev, NULL);
    359	if (IS_ERR(spdif->reset)) {
    360		dev_err(&pdev->dev, "Can't retrieve spdif reset\n");
    361		return PTR_ERR(spdif->reset);
    362	}
    363
    364	spdif->clk_spdif_out = devm_clk_get(&pdev->dev, "out");
    365	if (IS_ERR(spdif->clk_spdif_out)) {
    366		dev_err(&pdev->dev, "Could not retrieve spdif clock\n");
    367		return PTR_ERR(spdif->clk_spdif_out);
    368	}
    369
    370	regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
    371	if (IS_ERR(regs))
    372		return PTR_ERR(regs);
    373
    374	spdif->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
    375					      &tegra20_spdif_regmap_config);
    376	if (IS_ERR(spdif->regmap)) {
    377		dev_err(&pdev->dev, "regmap init failed\n");
    378		return PTR_ERR(spdif->regmap);
    379	}
    380
    381	spdif->playback_dma_data.addr = mem->start + TEGRA20_SPDIF_DATA_OUT;
    382	spdif->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
    383	spdif->playback_dma_data.maxburst = 4;
    384
    385	ret = devm_pm_runtime_enable(&pdev->dev);
    386	if (ret)
    387		return ret;
    388
    389	ret = devm_snd_soc_register_component(&pdev->dev,
    390					      &tegra20_spdif_component,
    391					      &tegra20_spdif_dai, 1);
    392	if (ret) {
    393		dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
    394		return ret;
    395	}
    396
    397	ret = devm_tegra_pcm_platform_register(&pdev->dev);
    398	if (ret) {
    399		dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
    400		return ret;
    401	}
    402
    403	return 0;
    404}
    405
    406static const struct dev_pm_ops tegra20_spdif_pm_ops = {
    407	SET_RUNTIME_PM_OPS(tegra20_spdif_runtime_suspend,
    408			   tegra20_spdif_runtime_resume, NULL)
    409	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
    410				pm_runtime_force_resume)
    411};
    412
    413static const struct of_device_id tegra20_spdif_of_match[] = {
    414	{ .compatible = "nvidia,tegra20-spdif", },
    415	{},
    416};
    417MODULE_DEVICE_TABLE(of, tegra20_spdif_of_match);
    418
    419static struct platform_driver tegra20_spdif_driver = {
    420	.driver = {
    421		.name = "tegra20-spdif",
    422		.pm = &tegra20_spdif_pm_ops,
    423		.of_match_table = tegra20_spdif_of_match,
    424	},
    425	.probe = tegra20_spdif_platform_probe,
    426};
    427module_platform_driver(tegra20_spdif_driver);
    428
    429MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
    430MODULE_DESCRIPTION("Tegra20 SPDIF ASoC driver");
    431MODULE_LICENSE("GPL");