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

acp3x-i2s.c (8473B)


      1// SPDX-License-Identifier: GPL-2.0+
      2//
      3// AMD ALSA SoC PCM Driver
      4//
      5//Copyright 2016 Advanced Micro Devices, Inc.
      6
      7#include <linux/platform_device.h>
      8#include <linux/module.h>
      9#include <linux/err.h>
     10#include <linux/io.h>
     11#include <sound/pcm_params.h>
     12#include <sound/soc.h>
     13#include <sound/soc-dai.h>
     14#include <linux/dma-mapping.h>
     15
     16#include "acp3x.h"
     17
     18#define DRV_NAME "acp3x_i2s_playcap"
     19
     20static int acp3x_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
     21					unsigned int fmt)
     22{
     23	struct i2s_dev_data *adata;
     24	int mode;
     25
     26	adata = snd_soc_dai_get_drvdata(cpu_dai);
     27	mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
     28	switch (mode) {
     29	case SND_SOC_DAIFMT_I2S:
     30		adata->tdm_mode = TDM_DISABLE;
     31		break;
     32	case SND_SOC_DAIFMT_DSP_A:
     33		adata->tdm_mode = TDM_ENABLE;
     34		break;
     35	default:
     36		return -EINVAL;
     37	}
     38	return 0;
     39}
     40
     41static int acp3x_i2s_set_tdm_slot(struct snd_soc_dai *cpu_dai,
     42		u32 tx_mask, u32 rx_mask, int slots, int slot_width)
     43{
     44	struct i2s_dev_data *adata;
     45	u32 frm_len;
     46	u16 slot_len;
     47
     48	adata = snd_soc_dai_get_drvdata(cpu_dai);
     49
     50	/* These values are as per Hardware Spec */
     51	switch (slot_width) {
     52	case SLOT_WIDTH_8:
     53		slot_len = 8;
     54		break;
     55	case SLOT_WIDTH_16:
     56		slot_len = 16;
     57		break;
     58	case SLOT_WIDTH_24:
     59		slot_len = 24;
     60		break;
     61	case SLOT_WIDTH_32:
     62		slot_len = 0;
     63		break;
     64	default:
     65		return -EINVAL;
     66	}
     67	frm_len = FRM_LEN | (slots << 15) | (slot_len << 18);
     68	adata->tdm_fmt = frm_len;
     69	return 0;
     70}
     71
     72static int acp3x_i2s_hwparams(struct snd_pcm_substream *substream,
     73	struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
     74{
     75	struct i2s_stream_instance *rtd;
     76	struct snd_soc_pcm_runtime *prtd;
     77	struct snd_soc_card *card;
     78	struct acp3x_platform_info *pinfo;
     79	struct i2s_dev_data *adata;
     80	u32 val;
     81	u32 reg_val, frmt_reg;
     82
     83	prtd = asoc_substream_to_rtd(substream);
     84	rtd = substream->runtime->private_data;
     85	card = prtd->card;
     86	adata = snd_soc_dai_get_drvdata(dai);
     87	pinfo = snd_soc_card_get_drvdata(card);
     88	if (pinfo) {
     89		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
     90			rtd->i2s_instance = pinfo->play_i2s_instance;
     91		else
     92			rtd->i2s_instance = pinfo->cap_i2s_instance;
     93	}
     94
     95	/* These values are as per Hardware Spec */
     96	switch (params_format(params)) {
     97	case SNDRV_PCM_FORMAT_U8:
     98	case SNDRV_PCM_FORMAT_S8:
     99		rtd->xfer_resolution = 0x0;
    100		break;
    101	case SNDRV_PCM_FORMAT_S16_LE:
    102		rtd->xfer_resolution = 0x02;
    103		break;
    104	case SNDRV_PCM_FORMAT_S24_LE:
    105		rtd->xfer_resolution = 0x04;
    106		break;
    107	case SNDRV_PCM_FORMAT_S32_LE:
    108		rtd->xfer_resolution = 0x05;
    109		break;
    110	default:
    111		return -EINVAL;
    112	}
    113	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
    114		switch (rtd->i2s_instance) {
    115		case I2S_BT_INSTANCE:
    116			reg_val = mmACP_BTTDM_ITER;
    117			frmt_reg = mmACP_BTTDM_TXFRMT;
    118			break;
    119		case I2S_SP_INSTANCE:
    120		default:
    121			reg_val = mmACP_I2STDM_ITER;
    122			frmt_reg = mmACP_I2STDM_TXFRMT;
    123		}
    124	} else {
    125		switch (rtd->i2s_instance) {
    126		case I2S_BT_INSTANCE:
    127			reg_val = mmACP_BTTDM_IRER;
    128			frmt_reg = mmACP_BTTDM_RXFRMT;
    129			break;
    130		case I2S_SP_INSTANCE:
    131		default:
    132			reg_val = mmACP_I2STDM_IRER;
    133			frmt_reg = mmACP_I2STDM_RXFRMT;
    134		}
    135	}
    136	if (adata->tdm_mode) {
    137		val = rv_readl(rtd->acp3x_base + reg_val);
    138		rv_writel(val | 0x2, rtd->acp3x_base + reg_val);
    139		rv_writel(adata->tdm_fmt, rtd->acp3x_base + frmt_reg);
    140	}
    141	val = rv_readl(rtd->acp3x_base + reg_val);
    142	val &= ~ACP3x_ITER_IRER_SAMP_LEN_MASK;
    143	val = val | (rtd->xfer_resolution  << 3);
    144	rv_writel(val, rtd->acp3x_base + reg_val);
    145	return 0;
    146}
    147
    148static int acp3x_i2s_trigger(struct snd_pcm_substream *substream,
    149				int cmd, struct snd_soc_dai *dai)
    150{
    151	struct i2s_stream_instance *rtd;
    152	u32 ret, val, period_bytes, reg_val, ier_val, water_val;
    153	u32 buf_size, buf_reg;
    154
    155	rtd = substream->runtime->private_data;
    156	period_bytes = frames_to_bytes(substream->runtime,
    157			substream->runtime->period_size);
    158	buf_size = frames_to_bytes(substream->runtime,
    159			substream->runtime->buffer_size);
    160	switch (cmd) {
    161	case SNDRV_PCM_TRIGGER_START:
    162	case SNDRV_PCM_TRIGGER_RESUME:
    163	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
    164		rtd->bytescount = acp_get_byte_count(rtd,
    165						substream->stream);
    166		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
    167			switch (rtd->i2s_instance) {
    168			case I2S_BT_INSTANCE:
    169				water_val =
    170					mmACP_BT_TX_INTR_WATERMARK_SIZE;
    171				reg_val = mmACP_BTTDM_ITER;
    172				ier_val = mmACP_BTTDM_IER;
    173				buf_reg = mmACP_BT_TX_RINGBUFSIZE;
    174				break;
    175			case I2S_SP_INSTANCE:
    176			default:
    177				water_val =
    178					mmACP_I2S_TX_INTR_WATERMARK_SIZE;
    179				reg_val = mmACP_I2STDM_ITER;
    180				ier_val = mmACP_I2STDM_IER;
    181				buf_reg = mmACP_I2S_TX_RINGBUFSIZE;
    182			}
    183		} else {
    184			switch (rtd->i2s_instance) {
    185			case I2S_BT_INSTANCE:
    186				water_val =
    187					mmACP_BT_RX_INTR_WATERMARK_SIZE;
    188				reg_val = mmACP_BTTDM_IRER;
    189				ier_val = mmACP_BTTDM_IER;
    190				buf_reg = mmACP_BT_RX_RINGBUFSIZE;
    191				break;
    192			case I2S_SP_INSTANCE:
    193			default:
    194				water_val =
    195					mmACP_I2S_RX_INTR_WATERMARK_SIZE;
    196				reg_val = mmACP_I2STDM_IRER;
    197				ier_val = mmACP_I2STDM_IER;
    198				buf_reg = mmACP_I2S_RX_RINGBUFSIZE;
    199			}
    200		}
    201		rv_writel(period_bytes, rtd->acp3x_base + water_val);
    202		rv_writel(buf_size, rtd->acp3x_base + buf_reg);
    203		val = rv_readl(rtd->acp3x_base + reg_val);
    204		val = val | BIT(0);
    205		rv_writel(val, rtd->acp3x_base + reg_val);
    206		rv_writel(1, rtd->acp3x_base + ier_val);
    207		ret = 0;
    208		break;
    209	case SNDRV_PCM_TRIGGER_STOP:
    210	case SNDRV_PCM_TRIGGER_SUSPEND:
    211	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
    212		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
    213			switch (rtd->i2s_instance) {
    214			case I2S_BT_INSTANCE:
    215				reg_val = mmACP_BTTDM_ITER;
    216				break;
    217			case I2S_SP_INSTANCE:
    218			default:
    219				reg_val = mmACP_I2STDM_ITER;
    220			}
    221
    222		} else {
    223			switch (rtd->i2s_instance) {
    224			case I2S_BT_INSTANCE:
    225				reg_val = mmACP_BTTDM_IRER;
    226				break;
    227			case I2S_SP_INSTANCE:
    228			default:
    229				reg_val = mmACP_I2STDM_IRER;
    230			}
    231		}
    232		val = rv_readl(rtd->acp3x_base + reg_val);
    233		val = val & ~BIT(0);
    234		rv_writel(val, rtd->acp3x_base + reg_val);
    235
    236		if (!(rv_readl(rtd->acp3x_base + mmACP_BTTDM_ITER) & BIT(0)) &&
    237		     !(rv_readl(rtd->acp3x_base + mmACP_BTTDM_IRER) & BIT(0)))
    238			rv_writel(0, rtd->acp3x_base + mmACP_BTTDM_IER);
    239		if (!(rv_readl(rtd->acp3x_base + mmACP_I2STDM_ITER) & BIT(0)) &&
    240		     !(rv_readl(rtd->acp3x_base + mmACP_I2STDM_IRER) & BIT(0)))
    241			rv_writel(0, rtd->acp3x_base + mmACP_I2STDM_IER);
    242		ret = 0;
    243		break;
    244	default:
    245		ret = -EINVAL;
    246		break;
    247	}
    248
    249	return ret;
    250}
    251
    252static const struct snd_soc_dai_ops acp3x_i2s_dai_ops = {
    253	.hw_params = acp3x_i2s_hwparams,
    254	.trigger = acp3x_i2s_trigger,
    255	.set_fmt = acp3x_i2s_set_fmt,
    256	.set_tdm_slot = acp3x_i2s_set_tdm_slot,
    257};
    258
    259static const struct snd_soc_component_driver acp3x_dai_component = {
    260	.name           = DRV_NAME,
    261};
    262
    263static struct snd_soc_dai_driver acp3x_i2s_dai = {
    264	.playback = {
    265		.rates = SNDRV_PCM_RATE_8000_96000,
    266		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
    267			SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
    268		.channels_min = 2,
    269		.channels_max = 8,
    270		.rate_min = 8000,
    271		.rate_max = 96000,
    272	},
    273	.capture = {
    274		.rates = SNDRV_PCM_RATE_8000_48000,
    275		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
    276			SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
    277		.channels_min = 2,
    278		.channels_max = 2,
    279		.rate_min = 8000,
    280		.rate_max = 48000,
    281	},
    282	.ops = &acp3x_i2s_dai_ops,
    283};
    284
    285static int acp3x_dai_probe(struct platform_device *pdev)
    286{
    287	struct resource *res;
    288	struct i2s_dev_data *adata;
    289	int ret;
    290
    291	adata = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dev_data),
    292			GFP_KERNEL);
    293	if (!adata)
    294		return -ENOMEM;
    295
    296	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    297	if (!res) {
    298		dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
    299		return -ENOMEM;
    300	}
    301	adata->acp3x_base = devm_ioremap(&pdev->dev, res->start,
    302						resource_size(res));
    303	if (!adata->acp3x_base)
    304		return -ENOMEM;
    305
    306	adata->i2s_irq = res->start;
    307	dev_set_drvdata(&pdev->dev, adata);
    308	ret = devm_snd_soc_register_component(&pdev->dev,
    309			&acp3x_dai_component, &acp3x_i2s_dai, 1);
    310	if (ret) {
    311		dev_err(&pdev->dev, "Fail to register acp i2s dai\n");
    312		return -ENODEV;
    313	}
    314	return 0;
    315}
    316
    317static int acp3x_dai_remove(struct platform_device *pdev)
    318{
    319	/* As we use devm_ memory alloc there is nothing TBD here */
    320
    321	return 0;
    322}
    323
    324static struct platform_driver acp3x_dai_driver = {
    325	.probe = acp3x_dai_probe,
    326	.remove = acp3x_dai_remove,
    327	.driver = {
    328		.name = "acp3x_i2s_playcap",
    329	},
    330};
    331
    332module_platform_driver(acp3x_dai_driver);
    333
    334MODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com");
    335MODULE_DESCRIPTION("AMD ACP 3.x PCM Driver");
    336MODULE_LICENSE("GPL v2");
    337MODULE_ALIAS("platform:"DRV_NAME);