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_out.c (9007B)


      1/*
      2 * ALSA SoC SPDIF Out 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/soc.h>
     23#include <sound/spear_dma.h>
     24#include <sound/spear_spdif.h>
     25#include "spdif_out_regs.h"
     26#include "spear_pcm.h"
     27
     28struct spdif_out_params {
     29	u32 rate;
     30	u32 core_freq;
     31	u32 mute;
     32};
     33
     34struct spdif_out_dev {
     35	struct clk *clk;
     36	struct spear_dma_data dma_params;
     37	struct spdif_out_params saved_params;
     38	u32 running;
     39	void __iomem *io_base;
     40	struct snd_dmaengine_dai_dma_data dma_params_tx;
     41	struct snd_dmaengine_pcm_config config;
     42};
     43
     44static void spdif_out_configure(struct spdif_out_dev *host)
     45{
     46	writel(SPDIF_OUT_RESET, host->io_base + SPDIF_OUT_SOFT_RST);
     47	mdelay(1);
     48	writel(readl(host->io_base + SPDIF_OUT_SOFT_RST) & ~SPDIF_OUT_RESET,
     49			host->io_base + SPDIF_OUT_SOFT_RST);
     50
     51	writel(SPDIF_OUT_FDMA_TRIG_16 | SPDIF_OUT_MEMFMT_16_16 |
     52			SPDIF_OUT_VALID_HW | SPDIF_OUT_USER_HW |
     53			SPDIF_OUT_CHNLSTA_HW | SPDIF_OUT_PARITY_HW,
     54			host->io_base + SPDIF_OUT_CFG);
     55
     56	writel(0x7F, host->io_base + SPDIF_OUT_INT_STA_CLR);
     57	writel(0x7F, host->io_base + SPDIF_OUT_INT_EN_CLR);
     58}
     59
     60static int spdif_out_startup(struct snd_pcm_substream *substream,
     61		struct snd_soc_dai *cpu_dai)
     62{
     63	struct spdif_out_dev *host = snd_soc_dai_get_drvdata(cpu_dai);
     64	int ret;
     65
     66	if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
     67		return -EINVAL;
     68
     69	ret = clk_enable(host->clk);
     70	if (ret)
     71		return ret;
     72
     73	host->running = true;
     74	spdif_out_configure(host);
     75
     76	return 0;
     77}
     78
     79static void spdif_out_shutdown(struct snd_pcm_substream *substream,
     80		struct snd_soc_dai *dai)
     81{
     82	struct spdif_out_dev *host = snd_soc_dai_get_drvdata(dai);
     83
     84	if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
     85		return;
     86
     87	clk_disable(host->clk);
     88	host->running = false;
     89}
     90
     91static void spdif_out_clock(struct spdif_out_dev *host, u32 core_freq,
     92		u32 rate)
     93{
     94	u32 divider, ctrl;
     95
     96	clk_set_rate(host->clk, core_freq);
     97	divider = DIV_ROUND_CLOSEST(clk_get_rate(host->clk), (rate * 128));
     98
     99	ctrl = readl(host->io_base + SPDIF_OUT_CTRL);
    100	ctrl &= ~SPDIF_DIVIDER_MASK;
    101	ctrl |= (divider << SPDIF_DIVIDER_SHIFT) & SPDIF_DIVIDER_MASK;
    102	writel(ctrl, host->io_base + SPDIF_OUT_CTRL);
    103}
    104
    105static int spdif_out_hw_params(struct snd_pcm_substream *substream,
    106		struct snd_pcm_hw_params *params,
    107		struct snd_soc_dai *dai)
    108{
    109	struct spdif_out_dev *host = snd_soc_dai_get_drvdata(dai);
    110	u32 rate, core_freq;
    111
    112	if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
    113		return -EINVAL;
    114
    115	rate = params_rate(params);
    116
    117	switch (rate) {
    118	case 8000:
    119	case 16000:
    120	case 32000:
    121	case 64000:
    122		/*
    123		 * The clock is multiplied by 10 to bring it to feasible range
    124		 * of frequencies for sscg
    125		 */
    126		core_freq = 64000 * 128 * 10;	/* 81.92 MHz */
    127		break;
    128	case 5512:
    129	case 11025:
    130	case 22050:
    131	case 44100:
    132	case 88200:
    133	case 176400:
    134		core_freq = 176400 * 128;	/* 22.5792 MHz */
    135		break;
    136	case 48000:
    137	case 96000:
    138	case 192000:
    139	default:
    140		core_freq = 192000 * 128;	/* 24.576 MHz */
    141		break;
    142	}
    143
    144	spdif_out_clock(host, core_freq, rate);
    145	host->saved_params.core_freq = core_freq;
    146	host->saved_params.rate = rate;
    147
    148	return 0;
    149}
    150
    151static int spdif_out_trigger(struct snd_pcm_substream *substream, int cmd,
    152		struct snd_soc_dai *dai)
    153{
    154	struct spdif_out_dev *host = snd_soc_dai_get_drvdata(dai);
    155	u32 ctrl;
    156	int ret = 0;
    157
    158	if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
    159		return -EINVAL;
    160
    161	switch (cmd) {
    162	case SNDRV_PCM_TRIGGER_START:
    163	case SNDRV_PCM_TRIGGER_RESUME:
    164	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
    165			ctrl = readl(host->io_base + SPDIF_OUT_CTRL);
    166			ctrl &= ~SPDIF_OPMODE_MASK;
    167			if (!host->saved_params.mute)
    168				ctrl |= SPDIF_OPMODE_AUD_DATA |
    169					SPDIF_STATE_NORMAL;
    170			else
    171				ctrl |= SPDIF_OPMODE_MUTE_PCM;
    172			writel(ctrl, host->io_base + SPDIF_OUT_CTRL);
    173		break;
    174
    175	case SNDRV_PCM_TRIGGER_STOP:
    176	case SNDRV_PCM_TRIGGER_SUSPEND:
    177	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
    178		ctrl = readl(host->io_base + SPDIF_OUT_CTRL);
    179		ctrl &= ~SPDIF_OPMODE_MASK;
    180		ctrl |= SPDIF_OPMODE_OFF;
    181		writel(ctrl, host->io_base + SPDIF_OUT_CTRL);
    182		break;
    183
    184	default:
    185		ret = -EINVAL;
    186		break;
    187	}
    188	return ret;
    189}
    190
    191static int spdif_mute(struct snd_soc_dai *dai, int mute, int direction)
    192{
    193	struct spdif_out_dev *host = snd_soc_dai_get_drvdata(dai);
    194	u32 val;
    195
    196	host->saved_params.mute = mute;
    197	val = readl(host->io_base + SPDIF_OUT_CTRL);
    198	val &= ~SPDIF_OPMODE_MASK;
    199
    200	if (mute)
    201		val |= SPDIF_OPMODE_MUTE_PCM;
    202	else {
    203		if (host->running)
    204			val |= SPDIF_OPMODE_AUD_DATA | SPDIF_STATE_NORMAL;
    205		else
    206			val |= SPDIF_OPMODE_OFF;
    207	}
    208
    209	writel(val, host->io_base + SPDIF_OUT_CTRL);
    210	return 0;
    211}
    212
    213static int spdif_mute_get(struct snd_kcontrol *kcontrol,
    214		struct snd_ctl_elem_value *ucontrol)
    215{
    216	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
    217	struct spdif_out_dev *host = snd_soc_dai_get_drvdata(cpu_dai);
    218
    219	ucontrol->value.integer.value[0] = host->saved_params.mute;
    220	return 0;
    221}
    222
    223static int spdif_mute_put(struct snd_kcontrol *kcontrol,
    224		struct snd_ctl_elem_value *ucontrol)
    225{
    226	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
    227	struct spdif_out_dev *host = snd_soc_dai_get_drvdata(cpu_dai);
    228
    229	if (host->saved_params.mute == ucontrol->value.integer.value[0])
    230		return 0;
    231
    232	spdif_mute(cpu_dai, ucontrol->value.integer.value[0],
    233		   SNDRV_PCM_STREAM_PLAYBACK);
    234
    235	return 1;
    236}
    237static const struct snd_kcontrol_new spdif_out_controls[] = {
    238	SOC_SINGLE_BOOL_EXT("IEC958 Playback Switch", 0,
    239			spdif_mute_get, spdif_mute_put),
    240};
    241
    242static int spdif_soc_dai_probe(struct snd_soc_dai *dai)
    243{
    244	struct spdif_out_dev *host = snd_soc_dai_get_drvdata(dai);
    245
    246	host->dma_params_tx.filter_data = &host->dma_params;
    247	dai->playback_dma_data = &host->dma_params_tx;
    248
    249	return snd_soc_add_dai_controls(dai, spdif_out_controls,
    250				ARRAY_SIZE(spdif_out_controls));
    251}
    252
    253static const struct snd_soc_dai_ops spdif_out_dai_ops = {
    254	.mute_stream	= spdif_mute,
    255	.startup	= spdif_out_startup,
    256	.shutdown	= spdif_out_shutdown,
    257	.trigger	= spdif_out_trigger,
    258	.hw_params	= spdif_out_hw_params,
    259	.no_capture_mute = 1,
    260};
    261
    262static struct snd_soc_dai_driver spdif_out_dai = {
    263	.playback = {
    264		.channels_min = 2,
    265		.channels_max = 2,
    266		.rates = (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
    267				 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \
    268				 SNDRV_PCM_RATE_192000),
    269		.formats = SNDRV_PCM_FMTBIT_S16_LE,
    270	},
    271	.probe = spdif_soc_dai_probe,
    272	.ops = &spdif_out_dai_ops,
    273};
    274
    275static const struct snd_soc_component_driver spdif_out_component = {
    276	.name		= "spdif-out",
    277};
    278
    279static int spdif_out_probe(struct platform_device *pdev)
    280{
    281	struct spdif_out_dev *host;
    282	struct spear_spdif_platform_data *pdata;
    283	struct resource *res;
    284	int ret;
    285
    286	host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
    287	if (!host)
    288		return -ENOMEM;
    289
    290	host->io_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
    291	if (IS_ERR(host->io_base))
    292		return PTR_ERR(host->io_base);
    293
    294	host->clk = devm_clk_get(&pdev->dev, NULL);
    295	if (IS_ERR(host->clk))
    296		return PTR_ERR(host->clk);
    297
    298	pdata = dev_get_platdata(&pdev->dev);
    299
    300	host->dma_params.data = pdata->dma_params;
    301	host->dma_params.addr = res->start + SPDIF_OUT_FIFO_DATA;
    302	host->dma_params.max_burst = 16;
    303	host->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
    304
    305	dev_set_drvdata(&pdev->dev, host);
    306
    307	ret = devm_snd_soc_register_component(&pdev->dev, &spdif_out_component,
    308					      &spdif_out_dai, 1);
    309	if (ret)
    310		return ret;
    311
    312	return devm_spear_pcm_platform_register(&pdev->dev, &host->config,
    313						pdata->filter);
    314}
    315
    316#ifdef CONFIG_PM
    317static int spdif_out_suspend(struct device *dev)
    318{
    319	struct platform_device *pdev = to_platform_device(dev);
    320	struct spdif_out_dev *host = dev_get_drvdata(&pdev->dev);
    321
    322	if (host->running)
    323		clk_disable(host->clk);
    324
    325	return 0;
    326}
    327
    328static int spdif_out_resume(struct device *dev)
    329{
    330	struct platform_device *pdev = to_platform_device(dev);
    331	struct spdif_out_dev *host = dev_get_drvdata(&pdev->dev);
    332
    333	if (host->running) {
    334		clk_enable(host->clk);
    335		spdif_out_configure(host);
    336		spdif_out_clock(host, host->saved_params.core_freq,
    337				host->saved_params.rate);
    338	}
    339	return 0;
    340}
    341
    342static SIMPLE_DEV_PM_OPS(spdif_out_dev_pm_ops, spdif_out_suspend, \
    343		spdif_out_resume);
    344
    345#define SPDIF_OUT_DEV_PM_OPS (&spdif_out_dev_pm_ops)
    346
    347#else
    348#define SPDIF_OUT_DEV_PM_OPS NULL
    349
    350#endif
    351
    352static struct platform_driver spdif_out_driver = {
    353	.probe		= spdif_out_probe,
    354	.driver		= {
    355		.name	= "spdif-out",
    356		.pm	= SPDIF_OUT_DEV_PM_OPS,
    357	},
    358};
    359
    360module_platform_driver(spdif_out_driver);
    361
    362MODULE_AUTHOR("Vipin Kumar <vipin.kumar@st.com>");
    363MODULE_DESCRIPTION("SPEAr SPDIF OUT SoC Interface");
    364MODULE_LICENSE("GPL");
    365MODULE_ALIAS("platform:spdif_out");