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

s3c24xx-i2s.c (11704B)


      1// SPDX-License-Identifier: GPL-2.0+
      2//
      3// s3c24xx-i2s.c  --  ALSA Soc Audio Layer
      4//
      5// (c) 2006 Wolfson Microelectronics PLC.
      6// Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
      7//
      8// Copyright 2004-2005 Simtec Electronics
      9//	http://armlinux.simtec.co.uk/
     10//	Ben Dooks <ben@simtec.co.uk>
     11
     12#include <linux/delay.h>
     13#include <linux/clk.h>
     14#include <linux/io.h>
     15#include <linux/gpio.h>
     16#include <linux/module.h>
     17
     18#include <sound/soc.h>
     19#include <sound/pcm_params.h>
     20
     21#include "regs-iis.h"
     22#include "dma.h"
     23#include "s3c24xx-i2s.h"
     24
     25static struct snd_dmaengine_dai_dma_data s3c24xx_i2s_pcm_stereo_out = {
     26	.chan_name	= "tx",
     27	.addr_width	= 2,
     28};
     29
     30static struct snd_dmaengine_dai_dma_data s3c24xx_i2s_pcm_stereo_in = {
     31	.chan_name	= "rx",
     32	.addr_width	= 2,
     33};
     34
     35struct s3c24xx_i2s_info {
     36	void __iomem	*regs;
     37	struct clk	*iis_clk;
     38	u32		iiscon;
     39	u32		iismod;
     40	u32		iisfcon;
     41	u32		iispsr;
     42};
     43static struct s3c24xx_i2s_info s3c24xx_i2s;
     44
     45static void s3c24xx_snd_txctrl(int on)
     46{
     47	u32 iisfcon;
     48	u32 iiscon;
     49	u32 iismod;
     50
     51	iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
     52	iiscon  = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
     53	iismod  = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
     54
     55	pr_debug("r: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
     56
     57	if (on) {
     58		iisfcon |= S3C2410_IISFCON_TXDMA | S3C2410_IISFCON_TXENABLE;
     59		iiscon  |= S3C2410_IISCON_TXDMAEN | S3C2410_IISCON_IISEN;
     60		iiscon  &= ~S3C2410_IISCON_TXIDLE;
     61		iismod  |= S3C2410_IISMOD_TXMODE;
     62
     63		writel(iismod,  s3c24xx_i2s.regs + S3C2410_IISMOD);
     64		writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
     65		writel(iiscon,  s3c24xx_i2s.regs + S3C2410_IISCON);
     66	} else {
     67		/* note, we have to disable the FIFOs otherwise bad things
     68		 * seem to happen when the DMA stops. According to the
     69		 * Samsung supplied kernel, this should allow the DMA
     70		 * engine and FIFOs to reset. If this isn't allowed, the
     71		 * DMA engine will simply freeze randomly.
     72		 */
     73
     74		iisfcon &= ~S3C2410_IISFCON_TXENABLE;
     75		iisfcon &= ~S3C2410_IISFCON_TXDMA;
     76		iiscon  |=  S3C2410_IISCON_TXIDLE;
     77		iiscon  &= ~S3C2410_IISCON_TXDMAEN;
     78		iismod  &= ~S3C2410_IISMOD_TXMODE;
     79
     80		writel(iiscon,  s3c24xx_i2s.regs + S3C2410_IISCON);
     81		writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
     82		writel(iismod,  s3c24xx_i2s.regs + S3C2410_IISMOD);
     83	}
     84
     85	pr_debug("w: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
     86}
     87
     88static void s3c24xx_snd_rxctrl(int on)
     89{
     90	u32 iisfcon;
     91	u32 iiscon;
     92	u32 iismod;
     93
     94	iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
     95	iiscon  = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
     96	iismod  = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
     97
     98	pr_debug("r: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
     99
    100	if (on) {
    101		iisfcon |= S3C2410_IISFCON_RXDMA | S3C2410_IISFCON_RXENABLE;
    102		iiscon  |= S3C2410_IISCON_RXDMAEN | S3C2410_IISCON_IISEN;
    103		iiscon  &= ~S3C2410_IISCON_RXIDLE;
    104		iismod  |= S3C2410_IISMOD_RXMODE;
    105
    106		writel(iismod,  s3c24xx_i2s.regs + S3C2410_IISMOD);
    107		writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
    108		writel(iiscon,  s3c24xx_i2s.regs + S3C2410_IISCON);
    109	} else {
    110		/* note, we have to disable the FIFOs otherwise bad things
    111		 * seem to happen when the DMA stops. According to the
    112		 * Samsung supplied kernel, this should allow the DMA
    113		 * engine and FIFOs to reset. If this isn't allowed, the
    114		 * DMA engine will simply freeze randomly.
    115		 */
    116
    117		iisfcon &= ~S3C2410_IISFCON_RXENABLE;
    118		iisfcon &= ~S3C2410_IISFCON_RXDMA;
    119		iiscon  |= S3C2410_IISCON_RXIDLE;
    120		iiscon  &= ~S3C2410_IISCON_RXDMAEN;
    121		iismod  &= ~S3C2410_IISMOD_RXMODE;
    122
    123		writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
    124		writel(iiscon,  s3c24xx_i2s.regs + S3C2410_IISCON);
    125		writel(iismod,  s3c24xx_i2s.regs + S3C2410_IISMOD);
    126	}
    127
    128	pr_debug("w: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
    129}
    130
    131/*
    132 * Wait for the LR signal to allow synchronisation to the L/R clock
    133 * from the codec. May only be needed for slave mode.
    134 */
    135static int s3c24xx_snd_lrsync(void)
    136{
    137	u32 iiscon;
    138	int timeout = 50; /* 5ms */
    139
    140	while (1) {
    141		iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
    142		if (iiscon & S3C2410_IISCON_LRINDEX)
    143			break;
    144
    145		if (!timeout--)
    146			return -ETIMEDOUT;
    147		udelay(100);
    148	}
    149
    150	return 0;
    151}
    152
    153/*
    154 * Check whether CPU is the master or slave
    155 */
    156static inline int s3c24xx_snd_is_clkmaster(void)
    157{
    158	return (readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & S3C2410_IISMOD_SLAVE) ? 0:1;
    159}
    160
    161/*
    162 * Set S3C24xx I2S DAI format
    163 */
    164static int s3c24xx_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
    165		unsigned int fmt)
    166{
    167	u32 iismod;
    168
    169	iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
    170	pr_debug("hw_params r: IISMOD: %x \n", iismod);
    171
    172	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
    173	case SND_SOC_DAIFMT_CBM_CFM:
    174		iismod |= S3C2410_IISMOD_SLAVE;
    175		break;
    176	case SND_SOC_DAIFMT_CBS_CFS:
    177		iismod &= ~S3C2410_IISMOD_SLAVE;
    178		break;
    179	default:
    180		return -EINVAL;
    181	}
    182
    183	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
    184	case SND_SOC_DAIFMT_LEFT_J:
    185		iismod |= S3C2410_IISMOD_MSB;
    186		break;
    187	case SND_SOC_DAIFMT_I2S:
    188		iismod &= ~S3C2410_IISMOD_MSB;
    189		break;
    190	default:
    191		return -EINVAL;
    192	}
    193
    194	writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
    195	pr_debug("hw_params w: IISMOD: %x \n", iismod);
    196
    197	return 0;
    198}
    199
    200static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream,
    201				 struct snd_pcm_hw_params *params,
    202				 struct snd_soc_dai *dai)
    203{
    204	struct snd_dmaengine_dai_dma_data *dma_data;
    205	u32 iismod;
    206
    207	dma_data = snd_soc_dai_get_dma_data(dai, substream);
    208
    209	/* Working copies of register */
    210	iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
    211	pr_debug("hw_params r: IISMOD: %x\n", iismod);
    212
    213	switch (params_width(params)) {
    214	case 8:
    215		iismod &= ~S3C2410_IISMOD_16BIT;
    216		dma_data->addr_width = 1;
    217		break;
    218	case 16:
    219		iismod |= S3C2410_IISMOD_16BIT;
    220		dma_data->addr_width = 2;
    221		break;
    222	default:
    223		return -EINVAL;
    224	}
    225
    226	writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
    227	pr_debug("hw_params w: IISMOD: %x\n", iismod);
    228
    229	return 0;
    230}
    231
    232static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
    233			       struct snd_soc_dai *dai)
    234{
    235	int ret = 0;
    236
    237	switch (cmd) {
    238	case SNDRV_PCM_TRIGGER_START:
    239	case SNDRV_PCM_TRIGGER_RESUME:
    240	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
    241		if (!s3c24xx_snd_is_clkmaster()) {
    242			ret = s3c24xx_snd_lrsync();
    243			if (ret)
    244				goto exit_err;
    245		}
    246
    247		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
    248			s3c24xx_snd_rxctrl(1);
    249		else
    250			s3c24xx_snd_txctrl(1);
    251
    252		break;
    253	case SNDRV_PCM_TRIGGER_STOP:
    254	case SNDRV_PCM_TRIGGER_SUSPEND:
    255	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
    256		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
    257			s3c24xx_snd_rxctrl(0);
    258		else
    259			s3c24xx_snd_txctrl(0);
    260		break;
    261	default:
    262		ret = -EINVAL;
    263		break;
    264	}
    265
    266exit_err:
    267	return ret;
    268}
    269
    270/*
    271 * Set S3C24xx Clock source
    272 */
    273static int s3c24xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
    274	int clk_id, unsigned int freq, int dir)
    275{
    276	u32 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
    277
    278	iismod &= ~S3C2440_IISMOD_MPLL;
    279
    280	switch (clk_id) {
    281	case S3C24XX_CLKSRC_PCLK:
    282		break;
    283	case S3C24XX_CLKSRC_MPLL:
    284		iismod |= S3C2440_IISMOD_MPLL;
    285		break;
    286	default:
    287		return -EINVAL;
    288	}
    289
    290	writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
    291	return 0;
    292}
    293
    294/*
    295 * Set S3C24xx Clock dividers
    296 */
    297static int s3c24xx_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
    298	int div_id, int div)
    299{
    300	u32 reg;
    301
    302	switch (div_id) {
    303	case S3C24XX_DIV_BCLK:
    304		reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~S3C2410_IISMOD_FS_MASK;
    305		writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
    306		break;
    307	case S3C24XX_DIV_MCLK:
    308		reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~(S3C2410_IISMOD_384FS);
    309		writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
    310		break;
    311	case S3C24XX_DIV_PRESCALER:
    312		writel(div, s3c24xx_i2s.regs + S3C2410_IISPSR);
    313		reg = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
    314		writel(reg | S3C2410_IISCON_PSCEN, s3c24xx_i2s.regs + S3C2410_IISCON);
    315		break;
    316	default:
    317		return -EINVAL;
    318	}
    319
    320	return 0;
    321}
    322
    323/*
    324 * To avoid duplicating clock code, allow machine driver to
    325 * get the clockrate from here.
    326 */
    327u32 s3c24xx_i2s_get_clockrate(void)
    328{
    329	return clk_get_rate(s3c24xx_i2s.iis_clk);
    330}
    331EXPORT_SYMBOL_GPL(s3c24xx_i2s_get_clockrate);
    332
    333static int s3c24xx_i2s_probe(struct snd_soc_dai *dai)
    334{
    335	int ret;
    336	snd_soc_dai_init_dma_data(dai, &s3c24xx_i2s_pcm_stereo_out,
    337					&s3c24xx_i2s_pcm_stereo_in);
    338
    339	s3c24xx_i2s.iis_clk = devm_clk_get(dai->dev, "iis");
    340	if (IS_ERR(s3c24xx_i2s.iis_clk)) {
    341		pr_err("failed to get iis_clock\n");
    342		return PTR_ERR(s3c24xx_i2s.iis_clk);
    343	}
    344	ret = clk_prepare_enable(s3c24xx_i2s.iis_clk);
    345	if (ret)
    346		return ret;
    347
    348	writel(S3C2410_IISCON_IISEN, s3c24xx_i2s.regs + S3C2410_IISCON);
    349
    350	s3c24xx_snd_txctrl(0);
    351	s3c24xx_snd_rxctrl(0);
    352
    353	return 0;
    354}
    355
    356#ifdef CONFIG_PM
    357static int s3c24xx_i2s_suspend(struct snd_soc_component *component)
    358{
    359	s3c24xx_i2s.iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
    360	s3c24xx_i2s.iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
    361	s3c24xx_i2s.iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
    362	s3c24xx_i2s.iispsr = readl(s3c24xx_i2s.regs + S3C2410_IISPSR);
    363
    364	clk_disable_unprepare(s3c24xx_i2s.iis_clk);
    365
    366	return 0;
    367}
    368
    369static int s3c24xx_i2s_resume(struct snd_soc_component *component)
    370{
    371	int ret;
    372
    373	ret = clk_prepare_enable(s3c24xx_i2s.iis_clk);
    374	if (ret)
    375		return ret;
    376
    377	writel(s3c24xx_i2s.iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
    378	writel(s3c24xx_i2s.iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
    379	writel(s3c24xx_i2s.iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
    380	writel(s3c24xx_i2s.iispsr, s3c24xx_i2s.regs + S3C2410_IISPSR);
    381
    382	return 0;
    383}
    384#else
    385#define s3c24xx_i2s_suspend NULL
    386#define s3c24xx_i2s_resume NULL
    387#endif
    388
    389#define S3C24XX_I2S_RATES \
    390	(SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
    391	SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
    392	SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
    393
    394static const struct snd_soc_dai_ops s3c24xx_i2s_dai_ops = {
    395	.trigger	= s3c24xx_i2s_trigger,
    396	.hw_params	= s3c24xx_i2s_hw_params,
    397	.set_fmt	= s3c24xx_i2s_set_fmt,
    398	.set_clkdiv	= s3c24xx_i2s_set_clkdiv,
    399	.set_sysclk	= s3c24xx_i2s_set_sysclk,
    400};
    401
    402static struct snd_soc_dai_driver s3c24xx_i2s_dai = {
    403	.probe = s3c24xx_i2s_probe,
    404	.playback = {
    405		.channels_min = 2,
    406		.channels_max = 2,
    407		.rates = S3C24XX_I2S_RATES,
    408		.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
    409	.capture = {
    410		.channels_min = 2,
    411		.channels_max = 2,
    412		.rates = S3C24XX_I2S_RATES,
    413		.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
    414	.ops = &s3c24xx_i2s_dai_ops,
    415};
    416
    417static const struct snd_soc_component_driver s3c24xx_i2s_component = {
    418	.name		= "s3c24xx-i2s",
    419	.suspend	= s3c24xx_i2s_suspend,
    420	.resume		= s3c24xx_i2s_resume,
    421};
    422
    423static int s3c24xx_iis_dev_probe(struct platform_device *pdev)
    424{
    425	struct resource *res;
    426	int ret;
    427
    428	s3c24xx_i2s.regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
    429	if (IS_ERR(s3c24xx_i2s.regs))
    430		return PTR_ERR(s3c24xx_i2s.regs);
    431
    432	s3c24xx_i2s_pcm_stereo_out.addr = res->start + S3C2410_IISFIFO;
    433	s3c24xx_i2s_pcm_stereo_in.addr = res->start + S3C2410_IISFIFO;
    434
    435	ret = samsung_asoc_dma_platform_register(&pdev->dev, NULL,
    436						 "tx", "rx", NULL);
    437	if (ret) {
    438		dev_err(&pdev->dev, "Failed to register the DMA: %d\n", ret);
    439		return ret;
    440	}
    441
    442	ret = devm_snd_soc_register_component(&pdev->dev,
    443			&s3c24xx_i2s_component, &s3c24xx_i2s_dai, 1);
    444	if (ret)
    445		dev_err(&pdev->dev, "Failed to register the DAI\n");
    446
    447	return ret;
    448}
    449
    450static struct platform_driver s3c24xx_iis_driver = {
    451	.probe  = s3c24xx_iis_dev_probe,
    452	.driver = {
    453		.name = "s3c24xx-iis",
    454	},
    455};
    456
    457module_platform_driver(s3c24xx_iis_driver);
    458
    459/* Module information */
    460MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
    461MODULE_DESCRIPTION("s3c24xx I2S SoC Interface");
    462MODULE_LICENSE("GPL");
    463MODULE_ALIAS("platform:s3c24xx-iis");