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

i2sc.c (7516B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Au1000/Au1500/Au1100 I2S controller driver for ASoC
      4 *
      5 * (c) 2011 Manuel Lauss <manuel.lauss@googlemail.com>
      6 *
      7 * Note: clock supplied to the I2S controller must be 256x samplerate.
      8 */
      9
     10#include <linux/init.h>
     11#include <linux/module.h>
     12#include <linux/slab.h>
     13#include <linux/suspend.h>
     14#include <sound/core.h>
     15#include <sound/pcm.h>
     16#include <sound/initval.h>
     17#include <sound/soc.h>
     18#include <asm/mach-au1x00/au1000.h>
     19
     20#include "psc.h"
     21
     22#define I2S_RXTX	0x00
     23#define I2S_CFG		0x04
     24#define I2S_ENABLE	0x08
     25
     26#define CFG_XU		(1 << 25)	/* tx underflow */
     27#define CFG_XO		(1 << 24)
     28#define CFG_RU		(1 << 23)
     29#define CFG_RO		(1 << 22)
     30#define CFG_TR		(1 << 21)
     31#define CFG_TE		(1 << 20)
     32#define CFG_TF		(1 << 19)
     33#define CFG_RR		(1 << 18)
     34#define CFG_RF		(1 << 17)
     35#define CFG_ICK		(1 << 12)	/* clock invert */
     36#define CFG_PD		(1 << 11)	/* set to make I2SDIO INPUT */
     37#define CFG_LB		(1 << 10)	/* loopback */
     38#define CFG_IC		(1 << 9)	/* word select invert */
     39#define CFG_FM_I2S	(0 << 7)	/* I2S format */
     40#define CFG_FM_LJ	(1 << 7)	/* left-justified */
     41#define CFG_FM_RJ	(2 << 7)	/* right-justified */
     42#define CFG_FM_MASK	(3 << 7)
     43#define CFG_TN		(1 << 6)	/* tx fifo en */
     44#define CFG_RN		(1 << 5)	/* rx fifo en */
     45#define CFG_SZ_8	(0x08)
     46#define CFG_SZ_16	(0x10)
     47#define CFG_SZ_18	(0x12)
     48#define CFG_SZ_20	(0x14)
     49#define CFG_SZ_24	(0x18)
     50#define CFG_SZ_MASK	(0x1f)
     51#define EN_D		(1 << 1)	/* DISable */
     52#define EN_CE		(1 << 0)	/* clock enable */
     53
     54/* only limited by clock generator and board design */
     55#define AU1XI2SC_RATES \
     56	SNDRV_PCM_RATE_CONTINUOUS
     57
     58#define AU1XI2SC_FMTS \
     59	(SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |		\
     60	SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |	\
     61	SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE |	\
     62	SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_U18_3LE |	\
     63	SNDRV_PCM_FMTBIT_S18_3BE | SNDRV_PCM_FMTBIT_U18_3BE |	\
     64	SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE |	\
     65	SNDRV_PCM_FMTBIT_S20_3BE | SNDRV_PCM_FMTBIT_U20_3BE |	\
     66	SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |	\
     67	SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_U24_BE |	\
     68	0)
     69
     70static inline unsigned long RD(struct au1xpsc_audio_data *ctx, int reg)
     71{
     72	return __raw_readl(ctx->mmio + reg);
     73}
     74
     75static inline void WR(struct au1xpsc_audio_data *ctx, int reg, unsigned long v)
     76{
     77	__raw_writel(v, ctx->mmio + reg);
     78	wmb();
     79}
     80
     81static int au1xi2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
     82{
     83	struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(cpu_dai);
     84	unsigned long c;
     85	int ret;
     86
     87	ret = -EINVAL;
     88	c = ctx->cfg;
     89
     90	c &= ~CFG_FM_MASK;
     91	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
     92	case SND_SOC_DAIFMT_I2S:
     93		c |= CFG_FM_I2S;
     94		break;
     95	case SND_SOC_DAIFMT_MSB:
     96		c |= CFG_FM_RJ;
     97		break;
     98	case SND_SOC_DAIFMT_LSB:
     99		c |= CFG_FM_LJ;
    100		break;
    101	default:
    102		goto out;
    103	}
    104
    105	c &= ~(CFG_IC | CFG_ICK);		/* IB-IF */
    106	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
    107	case SND_SOC_DAIFMT_NB_NF:
    108		c |= CFG_IC | CFG_ICK;
    109		break;
    110	case SND_SOC_DAIFMT_NB_IF:
    111		c |= CFG_IC;
    112		break;
    113	case SND_SOC_DAIFMT_IB_NF:
    114		c |= CFG_ICK;
    115		break;
    116	case SND_SOC_DAIFMT_IB_IF:
    117		break;
    118	default:
    119		goto out;
    120	}
    121
    122	/* I2S controller only supports provider */
    123	switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
    124	case SND_SOC_DAIFMT_CBC_CFC:	/* CODEC consumer */
    125		break;
    126	default:
    127		goto out;
    128	}
    129
    130	ret = 0;
    131	ctx->cfg = c;
    132out:
    133	return ret;
    134}
    135
    136static int au1xi2s_trigger(struct snd_pcm_substream *substream,
    137			   int cmd, struct snd_soc_dai *dai)
    138{
    139	struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(dai);
    140	int stype = SUBSTREAM_TYPE(substream);
    141
    142	switch (cmd) {
    143	case SNDRV_PCM_TRIGGER_START:
    144	case SNDRV_PCM_TRIGGER_RESUME:
    145		/* power up */
    146		WR(ctx, I2S_ENABLE, EN_D | EN_CE);
    147		WR(ctx, I2S_ENABLE, EN_CE);
    148		ctx->cfg |= (stype == PCM_TX) ? CFG_TN : CFG_RN;
    149		WR(ctx, I2S_CFG, ctx->cfg);
    150		break;
    151	case SNDRV_PCM_TRIGGER_STOP:
    152	case SNDRV_PCM_TRIGGER_SUSPEND:
    153		ctx->cfg &= ~((stype == PCM_TX) ? CFG_TN : CFG_RN);
    154		WR(ctx, I2S_CFG, ctx->cfg);
    155		WR(ctx, I2S_ENABLE, EN_D);		/* power off */
    156		break;
    157	default:
    158		return -EINVAL;
    159	}
    160
    161	return 0;
    162}
    163
    164static unsigned long msbits_to_reg(int msbits)
    165{
    166	switch (msbits) {
    167	case 8:
    168		return CFG_SZ_8;
    169	case 16:
    170		return CFG_SZ_16;
    171	case 18:
    172		return CFG_SZ_18;
    173	case 20:
    174		return CFG_SZ_20;
    175	case 24:
    176		return CFG_SZ_24;
    177	}
    178	return 0;
    179}
    180
    181static int au1xi2s_hw_params(struct snd_pcm_substream *substream,
    182			     struct snd_pcm_hw_params *params,
    183			     struct snd_soc_dai *dai)
    184{
    185	struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(dai);
    186	unsigned long v;
    187
    188	v = msbits_to_reg(params->msbits);
    189	if (!v)
    190		return -EINVAL;
    191
    192	ctx->cfg &= ~CFG_SZ_MASK;
    193	ctx->cfg |= v;
    194	return 0;
    195}
    196
    197static int au1xi2s_startup(struct snd_pcm_substream *substream,
    198			   struct snd_soc_dai *dai)
    199{
    200	struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(dai);
    201	snd_soc_dai_set_dma_data(dai, substream, &ctx->dmaids[0]);
    202	return 0;
    203}
    204
    205static const struct snd_soc_dai_ops au1xi2s_dai_ops = {
    206	.startup	= au1xi2s_startup,
    207	.trigger	= au1xi2s_trigger,
    208	.hw_params	= au1xi2s_hw_params,
    209	.set_fmt	= au1xi2s_set_fmt,
    210};
    211
    212static struct snd_soc_dai_driver au1xi2s_dai_driver = {
    213	.symmetric_rate		= 1,
    214	.playback = {
    215		.rates		= AU1XI2SC_RATES,
    216		.formats	= AU1XI2SC_FMTS,
    217		.channels_min	= 2,
    218		.channels_max	= 2,
    219	},
    220	.capture = {
    221		.rates		= AU1XI2SC_RATES,
    222		.formats	= AU1XI2SC_FMTS,
    223		.channels_min	= 2,
    224		.channels_max	= 2,
    225	},
    226	.ops = &au1xi2s_dai_ops,
    227};
    228
    229static const struct snd_soc_component_driver au1xi2s_component = {
    230	.name		= "au1xi2s",
    231};
    232
    233static int au1xi2s_drvprobe(struct platform_device *pdev)
    234{
    235	struct resource *iores, *dmares;
    236	struct au1xpsc_audio_data *ctx;
    237
    238	ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
    239	if (!ctx)
    240		return -ENOMEM;
    241
    242	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    243	if (!iores)
    244		return -ENODEV;
    245
    246	if (!devm_request_mem_region(&pdev->dev, iores->start,
    247				     resource_size(iores),
    248				     pdev->name))
    249		return -EBUSY;
    250
    251	ctx->mmio = devm_ioremap(&pdev->dev, iores->start,
    252					 resource_size(iores));
    253	if (!ctx->mmio)
    254		return -EBUSY;
    255
    256	dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
    257	if (!dmares)
    258		return -EBUSY;
    259	ctx->dmaids[SNDRV_PCM_STREAM_PLAYBACK] = dmares->start;
    260
    261	dmares = platform_get_resource(pdev, IORESOURCE_DMA, 1);
    262	if (!dmares)
    263		return -EBUSY;
    264	ctx->dmaids[SNDRV_PCM_STREAM_CAPTURE] = dmares->start;
    265
    266	platform_set_drvdata(pdev, ctx);
    267
    268	return snd_soc_register_component(&pdev->dev, &au1xi2s_component,
    269					  &au1xi2s_dai_driver, 1);
    270}
    271
    272static int au1xi2s_drvremove(struct platform_device *pdev)
    273{
    274	struct au1xpsc_audio_data *ctx = platform_get_drvdata(pdev);
    275
    276	snd_soc_unregister_component(&pdev->dev);
    277
    278	WR(ctx, I2S_ENABLE, EN_D);	/* clock off, disable */
    279
    280	return 0;
    281}
    282
    283#ifdef CONFIG_PM
    284static int au1xi2s_drvsuspend(struct device *dev)
    285{
    286	struct au1xpsc_audio_data *ctx = dev_get_drvdata(dev);
    287
    288	WR(ctx, I2S_ENABLE, EN_D);	/* clock off, disable */
    289
    290	return 0;
    291}
    292
    293static int au1xi2s_drvresume(struct device *dev)
    294{
    295	return 0;
    296}
    297
    298static const struct dev_pm_ops au1xi2sc_pmops = {
    299	.suspend	= au1xi2s_drvsuspend,
    300	.resume		= au1xi2s_drvresume,
    301};
    302
    303#define AU1XI2SC_PMOPS (&au1xi2sc_pmops)
    304
    305#else
    306
    307#define AU1XI2SC_PMOPS NULL
    308
    309#endif
    310
    311static struct platform_driver au1xi2s_driver = {
    312	.driver	= {
    313		.name	= "alchemy-i2sc",
    314		.pm	= AU1XI2SC_PMOPS,
    315	},
    316	.probe		= au1xi2s_drvprobe,
    317	.remove		= au1xi2s_drvremove,
    318};
    319
    320module_platform_driver(au1xi2s_driver);
    321
    322MODULE_LICENSE("GPL");
    323MODULE_DESCRIPTION("Au1000/1500/1100 I2S ASoC driver");
    324MODULE_AUTHOR("Manuel Lauss");