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

axi-spdif.c (6338B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (C) 2012-2013, Analog Devices Inc.
      4 * Author: Lars-Peter Clausen <lars@metafoo.de>
      5 */
      6
      7#include <linux/init.h>
      8#include <linux/kernel.h>
      9#include <linux/module.h>
     10#include <linux/platform_device.h>
     11#include <linux/slab.h>
     12#include <linux/of.h>
     13#include <linux/clk.h>
     14#include <linux/regmap.h>
     15
     16#include <sound/core.h>
     17#include <sound/pcm.h>
     18#include <sound/pcm_params.h>
     19#include <sound/soc.h>
     20#include <sound/initval.h>
     21#include <sound/dmaengine_pcm.h>
     22
     23#define AXI_SPDIF_REG_CTRL	0x0
     24#define AXI_SPDIF_REG_STAT	0x4
     25#define AXI_SPDIF_REG_TX_FIFO	0xc
     26
     27#define AXI_SPDIF_CTRL_TXDATA BIT(1)
     28#define AXI_SPDIF_CTRL_TXEN BIT(0)
     29#define AXI_SPDIF_CTRL_CLKDIV_OFFSET 8
     30#define AXI_SPDIF_CTRL_CLKDIV_MASK (0xff << 8)
     31
     32#define AXI_SPDIF_FREQ_44100	(0x0 << 6)
     33#define AXI_SPDIF_FREQ_48000	(0x1 << 6)
     34#define AXI_SPDIF_FREQ_32000	(0x2 << 6)
     35#define AXI_SPDIF_FREQ_NA	(0x3 << 6)
     36
     37struct axi_spdif {
     38	struct regmap *regmap;
     39	struct clk *clk;
     40	struct clk *clk_ref;
     41
     42	struct snd_dmaengine_dai_dma_data dma_data;
     43
     44	struct snd_ratnum ratnum;
     45	struct snd_pcm_hw_constraint_ratnums rate_constraints;
     46};
     47
     48static int axi_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
     49	struct snd_soc_dai *dai)
     50{
     51	struct axi_spdif *spdif = snd_soc_dai_get_drvdata(dai);
     52	unsigned int val;
     53
     54	switch (cmd) {
     55	case SNDRV_PCM_TRIGGER_START:
     56	case SNDRV_PCM_TRIGGER_RESUME:
     57	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
     58		val = AXI_SPDIF_CTRL_TXDATA;
     59		break;
     60	case SNDRV_PCM_TRIGGER_STOP:
     61	case SNDRV_PCM_TRIGGER_SUSPEND:
     62	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
     63		val = 0;
     64		break;
     65	default:
     66		return -EINVAL;
     67	}
     68
     69	regmap_update_bits(spdif->regmap, AXI_SPDIF_REG_CTRL,
     70		AXI_SPDIF_CTRL_TXDATA, val);
     71
     72	return 0;
     73}
     74
     75static int axi_spdif_hw_params(struct snd_pcm_substream *substream,
     76	struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
     77{
     78	struct axi_spdif *spdif = snd_soc_dai_get_drvdata(dai);
     79	unsigned int rate = params_rate(params);
     80	unsigned int clkdiv, stat;
     81
     82	switch (params_rate(params)) {
     83	case 32000:
     84		stat = AXI_SPDIF_FREQ_32000;
     85		break;
     86	case 44100:
     87		stat = AXI_SPDIF_FREQ_44100;
     88		break;
     89	case 48000:
     90		stat = AXI_SPDIF_FREQ_48000;
     91		break;
     92	default:
     93		stat = AXI_SPDIF_FREQ_NA;
     94		break;
     95	}
     96
     97	clkdiv = DIV_ROUND_CLOSEST(clk_get_rate(spdif->clk_ref),
     98			rate * 64 * 2) - 1;
     99	clkdiv <<= AXI_SPDIF_CTRL_CLKDIV_OFFSET;
    100
    101	regmap_write(spdif->regmap, AXI_SPDIF_REG_STAT, stat);
    102	regmap_update_bits(spdif->regmap, AXI_SPDIF_REG_CTRL,
    103		AXI_SPDIF_CTRL_CLKDIV_MASK, clkdiv);
    104
    105	return 0;
    106}
    107
    108static int axi_spdif_dai_probe(struct snd_soc_dai *dai)
    109{
    110	struct axi_spdif *spdif = snd_soc_dai_get_drvdata(dai);
    111
    112	snd_soc_dai_init_dma_data(dai, &spdif->dma_data, NULL);
    113
    114	return 0;
    115}
    116
    117static int axi_spdif_startup(struct snd_pcm_substream *substream,
    118	struct snd_soc_dai *dai)
    119{
    120	struct axi_spdif *spdif = snd_soc_dai_get_drvdata(dai);
    121	int ret;
    122
    123	ret = snd_pcm_hw_constraint_ratnums(substream->runtime, 0,
    124			   SNDRV_PCM_HW_PARAM_RATE,
    125			   &spdif->rate_constraints);
    126	if (ret)
    127		return ret;
    128
    129	ret = clk_prepare_enable(spdif->clk_ref);
    130	if (ret)
    131		return ret;
    132
    133	regmap_update_bits(spdif->regmap, AXI_SPDIF_REG_CTRL,
    134		AXI_SPDIF_CTRL_TXEN, AXI_SPDIF_CTRL_TXEN);
    135
    136	return 0;
    137}
    138
    139static void axi_spdif_shutdown(struct snd_pcm_substream *substream,
    140	struct snd_soc_dai *dai)
    141{
    142	struct axi_spdif *spdif = snd_soc_dai_get_drvdata(dai);
    143
    144	regmap_update_bits(spdif->regmap, AXI_SPDIF_REG_CTRL,
    145		AXI_SPDIF_CTRL_TXEN, 0);
    146
    147	clk_disable_unprepare(spdif->clk_ref);
    148}
    149
    150static const struct snd_soc_dai_ops axi_spdif_dai_ops = {
    151	.startup = axi_spdif_startup,
    152	.shutdown = axi_spdif_shutdown,
    153	.trigger = axi_spdif_trigger,
    154	.hw_params = axi_spdif_hw_params,
    155};
    156
    157static struct snd_soc_dai_driver axi_spdif_dai = {
    158	.probe = axi_spdif_dai_probe,
    159	.playback = {
    160		.channels_min = 2,
    161		.channels_max = 2,
    162		.rates = SNDRV_PCM_RATE_KNOT,
    163		.formats = SNDRV_PCM_FMTBIT_S16_LE,
    164	},
    165	.ops = &axi_spdif_dai_ops,
    166};
    167
    168static const struct snd_soc_component_driver axi_spdif_component = {
    169	.name = "axi-spdif",
    170};
    171
    172static const struct regmap_config axi_spdif_regmap_config = {
    173	.reg_bits = 32,
    174	.reg_stride = 4,
    175	.val_bits = 32,
    176	.max_register = AXI_SPDIF_REG_STAT,
    177};
    178
    179static int axi_spdif_probe(struct platform_device *pdev)
    180{
    181	struct axi_spdif *spdif;
    182	struct resource *res;
    183	void __iomem *base;
    184	int ret;
    185
    186	spdif = devm_kzalloc(&pdev->dev, sizeof(*spdif), GFP_KERNEL);
    187	if (!spdif)
    188		return -ENOMEM;
    189
    190	platform_set_drvdata(pdev, spdif);
    191
    192	base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
    193	if (IS_ERR(base))
    194		return PTR_ERR(base);
    195
    196	spdif->regmap = devm_regmap_init_mmio(&pdev->dev, base,
    197					    &axi_spdif_regmap_config);
    198	if (IS_ERR(spdif->regmap))
    199		return PTR_ERR(spdif->regmap);
    200
    201	spdif->clk = devm_clk_get(&pdev->dev, "axi");
    202	if (IS_ERR(spdif->clk))
    203		return PTR_ERR(spdif->clk);
    204
    205	spdif->clk_ref = devm_clk_get(&pdev->dev, "ref");
    206	if (IS_ERR(spdif->clk_ref))
    207		return PTR_ERR(spdif->clk_ref);
    208
    209	ret = clk_prepare_enable(spdif->clk);
    210	if (ret)
    211		return ret;
    212
    213	spdif->dma_data.addr = res->start + AXI_SPDIF_REG_TX_FIFO;
    214	spdif->dma_data.addr_width = 4;
    215	spdif->dma_data.maxburst = 1;
    216
    217	spdif->ratnum.num = clk_get_rate(spdif->clk_ref) / 128;
    218	spdif->ratnum.den_step = 1;
    219	spdif->ratnum.den_min = 1;
    220	spdif->ratnum.den_max = 64;
    221
    222	spdif->rate_constraints.rats = &spdif->ratnum;
    223	spdif->rate_constraints.nrats = 1;
    224
    225	ret = devm_snd_soc_register_component(&pdev->dev, &axi_spdif_component,
    226					 &axi_spdif_dai, 1);
    227	if (ret)
    228		goto err_clk_disable;
    229
    230	ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
    231	if (ret)
    232		goto err_clk_disable;
    233
    234	return 0;
    235
    236err_clk_disable:
    237	clk_disable_unprepare(spdif->clk);
    238	return ret;
    239}
    240
    241static int axi_spdif_dev_remove(struct platform_device *pdev)
    242{
    243	struct axi_spdif *spdif = platform_get_drvdata(pdev);
    244
    245	clk_disable_unprepare(spdif->clk);
    246
    247	return 0;
    248}
    249
    250static const struct of_device_id axi_spdif_of_match[] = {
    251	{ .compatible = "adi,axi-spdif-tx-1.00.a", },
    252	{},
    253};
    254MODULE_DEVICE_TABLE(of, axi_spdif_of_match);
    255
    256static struct platform_driver axi_spdif_driver = {
    257	.driver = {
    258		.name = "axi-spdif",
    259		.of_match_table = axi_spdif_of_match,
    260	},
    261	.probe = axi_spdif_probe,
    262	.remove = axi_spdif_dev_remove,
    263};
    264module_platform_driver(axi_spdif_driver);
    265
    266MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
    267MODULE_DESCRIPTION("AXI SPDIF driver");
    268MODULE_LICENSE("GPL");