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

spdif_in.c (6849B)


      1/*
      2 * ALSA SoC SPDIF In Audio Layer for spear processors
      3 *
      4 * Copyright (C) 2012 ST Microelectronics
      5 * Vipin Kumar <vipin.kumar@st.com>
      6 *
      7 * This file is licensed under the terms of the GNU General Public
      8 * License version 2. This program is licensed "as is" without any
      9 * warranty of any kind, whether express or implied.
     10 */
     11
     12#include <linux/clk.h>
     13#include <linux/delay.h>
     14#include <linux/device.h>
     15#include <linux/kernel.h>
     16#include <linux/init.h>
     17#include <linux/io.h>
     18#include <linux/ioport.h>
     19#include <linux/module.h>
     20#include <linux/platform_device.h>
     21#include <sound/dmaengine_pcm.h>
     22#include <sound/pcm.h>
     23#include <sound/pcm_params.h>
     24#include <sound/soc.h>
     25#include <sound/spear_dma.h>
     26#include <sound/spear_spdif.h>
     27#include "spdif_in_regs.h"
     28#include "spear_pcm.h"
     29
     30struct spdif_in_params {
     31	u32 format;
     32};
     33
     34struct spdif_in_dev {
     35	struct clk *clk;
     36	struct spear_dma_data dma_params;
     37	struct spdif_in_params saved_params;
     38	void *io_base;
     39	struct device *dev;
     40	void (*reset_perip)(void);
     41	int irq;
     42	struct snd_dmaengine_dai_dma_data dma_params_rx;
     43	struct snd_dmaengine_pcm_config config;
     44};
     45
     46static void spdif_in_configure(struct spdif_in_dev *host)
     47{
     48	u32 ctrl = SPDIF_IN_PRTYEN | SPDIF_IN_STATEN | SPDIF_IN_USREN |
     49		SPDIF_IN_VALEN | SPDIF_IN_BLKEN;
     50	ctrl |= SPDIF_MODE_16BIT | SPDIF_FIFO_THRES_16;
     51
     52	writel(ctrl, host->io_base + SPDIF_IN_CTRL);
     53	writel(0xF, host->io_base + SPDIF_IN_IRQ_MASK);
     54}
     55
     56static int spdif_in_dai_probe(struct snd_soc_dai *dai)
     57{
     58	struct spdif_in_dev *host = snd_soc_dai_get_drvdata(dai);
     59
     60	host->dma_params_rx.filter_data = &host->dma_params;
     61	dai->capture_dma_data = &host->dma_params_rx;
     62
     63	return 0;
     64}
     65
     66static void spdif_in_shutdown(struct snd_pcm_substream *substream,
     67		struct snd_soc_dai *dai)
     68{
     69	struct spdif_in_dev *host = snd_soc_dai_get_drvdata(dai);
     70
     71	if (substream->stream != SNDRV_PCM_STREAM_CAPTURE)
     72		return;
     73
     74	writel(0x0, host->io_base + SPDIF_IN_IRQ_MASK);
     75}
     76
     77static void spdif_in_format(struct spdif_in_dev *host, u32 format)
     78{
     79	u32 ctrl = readl(host->io_base + SPDIF_IN_CTRL);
     80
     81	switch (format) {
     82	case SNDRV_PCM_FORMAT_S16_LE:
     83		ctrl |= SPDIF_XTRACT_16BIT;
     84		break;
     85
     86	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
     87		ctrl &= ~SPDIF_XTRACT_16BIT;
     88		break;
     89	}
     90
     91	writel(ctrl, host->io_base + SPDIF_IN_CTRL);
     92}
     93
     94static int spdif_in_hw_params(struct snd_pcm_substream *substream,
     95		struct snd_pcm_hw_params *params,
     96		struct snd_soc_dai *dai)
     97{
     98	struct spdif_in_dev *host = snd_soc_dai_get_drvdata(dai);
     99	u32 format;
    100
    101	if (substream->stream != SNDRV_PCM_STREAM_CAPTURE)
    102		return -EINVAL;
    103
    104	format = params_format(params);
    105	host->saved_params.format = format;
    106
    107	return 0;
    108}
    109
    110static int spdif_in_trigger(struct snd_pcm_substream *substream, int cmd,
    111		struct snd_soc_dai *dai)
    112{
    113	struct spdif_in_dev *host = snd_soc_dai_get_drvdata(dai);
    114	u32 ctrl;
    115	int ret = 0;
    116
    117	if (substream->stream != SNDRV_PCM_STREAM_CAPTURE)
    118		return -EINVAL;
    119
    120	switch (cmd) {
    121	case SNDRV_PCM_TRIGGER_START:
    122	case SNDRV_PCM_TRIGGER_RESUME:
    123	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
    124		clk_enable(host->clk);
    125		spdif_in_configure(host);
    126		spdif_in_format(host, host->saved_params.format);
    127
    128		ctrl = readl(host->io_base + SPDIF_IN_CTRL);
    129		ctrl |= SPDIF_IN_SAMPLE | SPDIF_IN_ENB;
    130		writel(ctrl, host->io_base + SPDIF_IN_CTRL);
    131		writel(0xF, host->io_base + SPDIF_IN_IRQ_MASK);
    132		break;
    133
    134	case SNDRV_PCM_TRIGGER_STOP:
    135	case SNDRV_PCM_TRIGGER_SUSPEND:
    136	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
    137		ctrl = readl(host->io_base + SPDIF_IN_CTRL);
    138		ctrl &= ~(SPDIF_IN_SAMPLE | SPDIF_IN_ENB);
    139		writel(ctrl, host->io_base + SPDIF_IN_CTRL);
    140		writel(0x0, host->io_base + SPDIF_IN_IRQ_MASK);
    141
    142		if (host->reset_perip)
    143			host->reset_perip();
    144		clk_disable(host->clk);
    145		break;
    146
    147	default:
    148		ret = -EINVAL;
    149		break;
    150	}
    151	return ret;
    152}
    153
    154static const struct snd_soc_dai_ops spdif_in_dai_ops = {
    155	.shutdown	= spdif_in_shutdown,
    156	.trigger	= spdif_in_trigger,
    157	.hw_params	= spdif_in_hw_params,
    158};
    159
    160static struct snd_soc_dai_driver spdif_in_dai = {
    161	.probe = spdif_in_dai_probe,
    162	.capture = {
    163		.channels_min = 2,
    164		.channels_max = 2,
    165		.rates = (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
    166				 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \
    167				 SNDRV_PCM_RATE_192000),
    168		.formats = SNDRV_PCM_FMTBIT_S16_LE | \
    169			   SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE,
    170	},
    171	.ops = &spdif_in_dai_ops,
    172};
    173
    174static const struct snd_soc_component_driver spdif_in_component = {
    175	.name		= "spdif-in",
    176};
    177
    178static irqreturn_t spdif_in_irq(int irq, void *arg)
    179{
    180	struct spdif_in_dev *host = (struct spdif_in_dev *)arg;
    181
    182	u32 irq_status = readl(host->io_base + SPDIF_IN_IRQ);
    183
    184	if (!irq_status)
    185		return IRQ_NONE;
    186
    187	if (irq_status & SPDIF_IRQ_FIFOWRITE)
    188		dev_err(host->dev, "spdif in: fifo write error");
    189	if (irq_status & SPDIF_IRQ_EMPTYFIFOREAD)
    190		dev_err(host->dev, "spdif in: empty fifo read error");
    191	if (irq_status & SPDIF_IRQ_FIFOFULL)
    192		dev_err(host->dev, "spdif in: fifo full error");
    193	if (irq_status & SPDIF_IRQ_OUTOFRANGE)
    194		dev_err(host->dev, "spdif in: out of range error");
    195
    196	writel(0, host->io_base + SPDIF_IN_IRQ);
    197
    198	return IRQ_HANDLED;
    199}
    200
    201static int spdif_in_probe(struct platform_device *pdev)
    202{
    203	struct spdif_in_dev *host;
    204	struct spear_spdif_platform_data *pdata;
    205	struct resource *res_fifo;
    206	void __iomem *io_base;
    207	int ret;
    208
    209	io_base = devm_platform_ioremap_resource(pdev, 0);
    210	if (IS_ERR(io_base))
    211		return PTR_ERR(io_base);
    212
    213	res_fifo = platform_get_resource(pdev, IORESOURCE_IO, 0);
    214	if (!res_fifo)
    215		return -EINVAL;
    216
    217	host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
    218	if (!host)
    219		return -ENOMEM;
    220
    221	host->io_base = io_base;
    222	host->irq = platform_get_irq(pdev, 0);
    223	if (host->irq < 0) {
    224		dev_warn(&pdev->dev, "failed to get IRQ: %d\n", host->irq);
    225		return host->irq;
    226	}
    227
    228	host->clk = devm_clk_get(&pdev->dev, NULL);
    229	if (IS_ERR(host->clk))
    230		return PTR_ERR(host->clk);
    231
    232	pdata = dev_get_platdata(&pdev->dev);
    233
    234	if (!pdata)
    235		return -EINVAL;
    236
    237	host->dma_params.data = pdata->dma_params;
    238	host->dma_params.addr = res_fifo->start;
    239	host->dma_params.max_burst = 16;
    240	host->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
    241	host->reset_perip = pdata->reset_perip;
    242
    243	host->dev = &pdev->dev;
    244	dev_set_drvdata(&pdev->dev, host);
    245
    246	ret = devm_request_irq(&pdev->dev, host->irq, spdif_in_irq, 0,
    247			"spdif-in", host);
    248	if (ret) {
    249		dev_warn(&pdev->dev, "request_irq failed\n");
    250		return ret;
    251	}
    252
    253	ret = devm_snd_soc_register_component(&pdev->dev, &spdif_in_component,
    254					      &spdif_in_dai, 1);
    255	if (ret)
    256		return ret;
    257
    258	return devm_spear_pcm_platform_register(&pdev->dev, &host->config,
    259						pdata->filter);
    260}
    261
    262static struct platform_driver spdif_in_driver = {
    263	.probe		= spdif_in_probe,
    264	.driver		= {
    265		.name	= "spdif-in",
    266	},
    267};
    268
    269module_platform_driver(spdif_in_driver);
    270
    271MODULE_AUTHOR("Vipin Kumar <vipin.kumar@st.com>");
    272MODULE_DESCRIPTION("SPEAr SPDIF IN SoC Interface");
    273MODULE_LICENSE("GPL");
    274MODULE_ALIAS("platform:spdif_in");