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

acp5x-i2s.c (9973B)


      1// SPDX-License-Identifier: GPL-2.0+
      2//
      3// AMD ALSA SoC PCM Driver
      4//
      5// Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved.
      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 "acp5x.h"
     17
     18#define DRV_NAME "acp5x_i2s_playcap"
     19
     20static int acp5x_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	mode = fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
     39	switch (mode) {
     40	case SND_SOC_DAIFMT_CBC_CFC:
     41		adata->master_mode = I2S_MASTER_MODE_ENABLE;
     42		break;
     43	case SND_SOC_DAIFMT_CBP_CFP:
     44		adata->master_mode = I2S_MASTER_MODE_DISABLE;
     45		break;
     46	}
     47	return 0;
     48}
     49
     50static int acp5x_i2s_set_tdm_slot(struct snd_soc_dai *cpu_dai,
     51				  u32 tx_mask, u32 rx_mask,
     52				  int slots, int slot_width)
     53{
     54	struct i2s_dev_data *adata;
     55	u32 frm_len;
     56	u16 slot_len;
     57
     58	adata = snd_soc_dai_get_drvdata(cpu_dai);
     59
     60	/* These values are as per Hardware Spec */
     61	switch (slot_width) {
     62	case SLOT_WIDTH_8:
     63		slot_len = 8;
     64		break;
     65	case SLOT_WIDTH_16:
     66		slot_len = 16;
     67		break;
     68	case SLOT_WIDTH_24:
     69		slot_len = 24;
     70		break;
     71	case SLOT_WIDTH_32:
     72		slot_len = 0;
     73		break;
     74	default:
     75		return -EINVAL;
     76	}
     77	frm_len = FRM_LEN | (slots << 15) | (slot_len << 18);
     78	adata->tdm_fmt = frm_len;
     79	return 0;
     80}
     81
     82static int acp5x_i2s_hwparams(struct snd_pcm_substream *substream,
     83			      struct snd_pcm_hw_params *params,
     84			      struct snd_soc_dai *dai)
     85{
     86	struct i2s_stream_instance *rtd;
     87	struct snd_soc_pcm_runtime *prtd;
     88	struct snd_soc_card *card;
     89	struct acp5x_platform_info *pinfo;
     90	struct i2s_dev_data *adata;
     91
     92	u32 val;
     93	u32 reg_val, frmt_reg;
     94	u32 lrclk_div_val, bclk_div_val;
     95
     96	lrclk_div_val = 0;
     97	bclk_div_val = 0;
     98	prtd = asoc_substream_to_rtd(substream);
     99	rtd = substream->runtime->private_data;
    100	card = prtd->card;
    101	adata = snd_soc_dai_get_drvdata(dai);
    102	pinfo = snd_soc_card_get_drvdata(card);
    103	if (pinfo) {
    104		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
    105			rtd->i2s_instance = pinfo->play_i2s_instance;
    106		else
    107			rtd->i2s_instance = pinfo->cap_i2s_instance;
    108	}
    109
    110	/* These values are as per Hardware Spec */
    111	switch (params_format(params)) {
    112	case SNDRV_PCM_FORMAT_U8:
    113	case SNDRV_PCM_FORMAT_S8:
    114		rtd->xfer_resolution = 0x0;
    115		break;
    116	case SNDRV_PCM_FORMAT_S16_LE:
    117		rtd->xfer_resolution = 0x02;
    118		break;
    119	case SNDRV_PCM_FORMAT_S24_LE:
    120		rtd->xfer_resolution = 0x04;
    121		break;
    122	case SNDRV_PCM_FORMAT_S32_LE:
    123		rtd->xfer_resolution = 0x05;
    124		break;
    125	default:
    126		return -EINVAL;
    127	}
    128	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
    129		switch (rtd->i2s_instance) {
    130		case I2S_HS_INSTANCE:
    131			reg_val = ACP_HSTDM_ITER;
    132			frmt_reg = ACP_HSTDM_TXFRMT;
    133			break;
    134		case I2S_SP_INSTANCE:
    135		default:
    136			reg_val = ACP_I2STDM_ITER;
    137			frmt_reg = ACP_I2STDM_TXFRMT;
    138		}
    139	} else {
    140		switch (rtd->i2s_instance) {
    141		case I2S_HS_INSTANCE:
    142			reg_val = ACP_HSTDM_IRER;
    143			frmt_reg = ACP_HSTDM_RXFRMT;
    144			break;
    145		case I2S_SP_INSTANCE:
    146		default:
    147			reg_val = ACP_I2STDM_IRER;
    148			frmt_reg = ACP_I2STDM_RXFRMT;
    149		}
    150	}
    151	if (adata->tdm_mode) {
    152		val = acp_readl(rtd->acp5x_base + reg_val);
    153		acp_writel(val | 0x2, rtd->acp5x_base + reg_val);
    154		acp_writel(adata->tdm_fmt, rtd->acp5x_base + frmt_reg);
    155	}
    156	val = acp_readl(rtd->acp5x_base + reg_val);
    157	val &= ~ACP5x_ITER_IRER_SAMP_LEN_MASK;
    158	val = val | (rtd->xfer_resolution  << 3);
    159	acp_writel(val, rtd->acp5x_base + reg_val);
    160
    161	if (adata->master_mode) {
    162		switch (params_format(params)) {
    163		case SNDRV_PCM_FORMAT_S16_LE:
    164			switch (params_rate(params)) {
    165			case 8000:
    166				bclk_div_val = 768;
    167				break;
    168			case 16000:
    169				bclk_div_val = 384;
    170				break;
    171			case 24000:
    172				bclk_div_val = 256;
    173				break;
    174			case 32000:
    175				bclk_div_val = 192;
    176				break;
    177			case 44100:
    178			case 48000:
    179				bclk_div_val = 128;
    180				break;
    181			case 88200:
    182			case 96000:
    183				bclk_div_val = 64;
    184				break;
    185			case 192000:
    186				bclk_div_val = 32;
    187				break;
    188			default:
    189				return -EINVAL;
    190			}
    191			lrclk_div_val = 32;
    192			break;
    193		case SNDRV_PCM_FORMAT_S32_LE:
    194			switch (params_rate(params)) {
    195			case 8000:
    196				bclk_div_val = 384;
    197				break;
    198			case 16000:
    199				bclk_div_val = 192;
    200				break;
    201			case 24000:
    202				bclk_div_val = 128;
    203				break;
    204			case 32000:
    205				bclk_div_val = 96;
    206				break;
    207			case 44100:
    208			case 48000:
    209				bclk_div_val = 64;
    210				break;
    211			case 88200:
    212			case 96000:
    213				bclk_div_val = 32;
    214				break;
    215			case 192000:
    216				bclk_div_val = 16;
    217				break;
    218			default:
    219				return -EINVAL;
    220			}
    221			lrclk_div_val = 64;
    222			break;
    223		default:
    224			return -EINVAL;
    225		}
    226		rtd->lrclk_div = lrclk_div_val;
    227		rtd->bclk_div = bclk_div_val;
    228	}
    229	return 0;
    230}
    231
    232static int acp5x_i2s_trigger(struct snd_pcm_substream *substream,
    233			     int cmd, struct snd_soc_dai *dai)
    234{
    235	struct i2s_stream_instance *rtd;
    236	struct i2s_dev_data *adata;
    237	u32 ret, val, period_bytes, reg_val, ier_val, water_val;
    238	u32 buf_size, buf_reg;
    239
    240	adata = snd_soc_dai_get_drvdata(dai);
    241	rtd = substream->runtime->private_data;
    242	period_bytes = frames_to_bytes(substream->runtime,
    243				       substream->runtime->period_size);
    244	buf_size = frames_to_bytes(substream->runtime,
    245				   substream->runtime->buffer_size);
    246	switch (cmd) {
    247	case SNDRV_PCM_TRIGGER_START:
    248	case SNDRV_PCM_TRIGGER_RESUME:
    249	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
    250		rtd->bytescount = acp_get_byte_count(rtd,
    251						     substream->stream);
    252		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
    253			switch (rtd->i2s_instance) {
    254			case I2S_HS_INSTANCE:
    255				water_val =
    256					ACP_HS_TX_INTR_WATERMARK_SIZE;
    257				reg_val = ACP_HSTDM_ITER;
    258				ier_val = ACP_HSTDM_IER;
    259				buf_reg = ACP_HS_TX_RINGBUFSIZE;
    260				break;
    261			case I2S_SP_INSTANCE:
    262			default:
    263				water_val =
    264					ACP_I2S_TX_INTR_WATERMARK_SIZE;
    265				reg_val = ACP_I2STDM_ITER;
    266				ier_val = ACP_I2STDM_IER;
    267				buf_reg = ACP_I2S_TX_RINGBUFSIZE;
    268			}
    269		} else {
    270			switch (rtd->i2s_instance) {
    271			case I2S_HS_INSTANCE:
    272				water_val =
    273					ACP_HS_RX_INTR_WATERMARK_SIZE;
    274				reg_val = ACP_HSTDM_IRER;
    275				ier_val = ACP_HSTDM_IER;
    276				buf_reg = ACP_HS_RX_RINGBUFSIZE;
    277				break;
    278			case I2S_SP_INSTANCE:
    279			default:
    280				water_val =
    281					ACP_I2S_RX_INTR_WATERMARK_SIZE;
    282				reg_val = ACP_I2STDM_IRER;
    283				ier_val = ACP_I2STDM_IER;
    284				buf_reg = ACP_I2S_RX_RINGBUFSIZE;
    285			}
    286		}
    287		acp_writel(period_bytes, rtd->acp5x_base + water_val);
    288		acp_writel(buf_size, rtd->acp5x_base + buf_reg);
    289		if (adata->master_mode)
    290			acp5x_set_i2s_clk(adata, rtd);
    291		val = acp_readl(rtd->acp5x_base + reg_val);
    292		val = val | BIT(0);
    293		acp_writel(val, rtd->acp5x_base + reg_val);
    294		acp_writel(1, rtd->acp5x_base + ier_val);
    295		ret = 0;
    296		break;
    297	case SNDRV_PCM_TRIGGER_STOP:
    298	case SNDRV_PCM_TRIGGER_SUSPEND:
    299	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
    300		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
    301			switch (rtd->i2s_instance) {
    302			case I2S_HS_INSTANCE:
    303				reg_val = ACP_HSTDM_ITER;
    304				break;
    305			case I2S_SP_INSTANCE:
    306			default:
    307				reg_val = ACP_I2STDM_ITER;
    308			}
    309
    310		} else {
    311			switch (rtd->i2s_instance) {
    312			case I2S_HS_INSTANCE:
    313				reg_val = ACP_HSTDM_IRER;
    314				break;
    315			case I2S_SP_INSTANCE:
    316			default:
    317				reg_val = ACP_I2STDM_IRER;
    318			}
    319		}
    320		val = acp_readl(rtd->acp5x_base + reg_val);
    321		val = val & ~BIT(0);
    322		acp_writel(val, rtd->acp5x_base + reg_val);
    323
    324		if (!(acp_readl(rtd->acp5x_base + ACP_HSTDM_ITER) & BIT(0)) &&
    325		    !(acp_readl(rtd->acp5x_base + ACP_HSTDM_IRER) & BIT(0)))
    326			acp_writel(0, rtd->acp5x_base + ACP_HSTDM_IER);
    327		if (!(acp_readl(rtd->acp5x_base + ACP_I2STDM_ITER) & BIT(0)) &&
    328		    !(acp_readl(rtd->acp5x_base + ACP_I2STDM_IRER) & BIT(0)))
    329			acp_writel(0, rtd->acp5x_base + ACP_I2STDM_IER);
    330		ret = 0;
    331		break;
    332	default:
    333		ret = -EINVAL;
    334		break;
    335	}
    336	return ret;
    337}
    338
    339static const struct snd_soc_dai_ops acp5x_i2s_dai_ops = {
    340	.hw_params = acp5x_i2s_hwparams,
    341	.trigger = acp5x_i2s_trigger,
    342	.set_fmt = acp5x_i2s_set_fmt,
    343	.set_tdm_slot = acp5x_i2s_set_tdm_slot,
    344};
    345
    346static const struct snd_soc_component_driver acp5x_dai_component = {
    347	.name = "acp5x-i2s",
    348};
    349
    350static struct snd_soc_dai_driver acp5x_i2s_dai = {
    351	.playback = {
    352		.rates = SNDRV_PCM_RATE_8000_96000,
    353		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
    354			SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
    355		.channels_min = 2,
    356		.channels_max = 2,
    357		.rate_min = 8000,
    358		.rate_max = 96000,
    359	},
    360	.capture = {
    361		.rates = SNDRV_PCM_RATE_8000_96000,
    362		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
    363			SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
    364		.channels_min = 2,
    365		.channels_max = 2,
    366		.rate_min = 8000,
    367		.rate_max = 96000,
    368	},
    369	.ops = &acp5x_i2s_dai_ops,
    370};
    371
    372static int acp5x_dai_probe(struct platform_device *pdev)
    373{
    374	struct resource *res;
    375	struct i2s_dev_data *adata;
    376	int ret;
    377
    378	adata = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dev_data),
    379			     GFP_KERNEL);
    380	if (!adata)
    381		return -ENOMEM;
    382
    383	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    384	if (!res) {
    385		dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
    386		return -ENOMEM;
    387	}
    388	adata->acp5x_base = devm_ioremap(&pdev->dev, res->start,
    389					 resource_size(res));
    390	if (!adata->acp5x_base)
    391		return -ENOMEM;
    392
    393	adata->master_mode = I2S_MASTER_MODE_ENABLE;
    394	dev_set_drvdata(&pdev->dev, adata);
    395	ret = devm_snd_soc_register_component(&pdev->dev,
    396					      &acp5x_dai_component,
    397					      &acp5x_i2s_dai, 1);
    398	if (ret)
    399		dev_err(&pdev->dev, "Fail to register acp i2s dai\n");
    400	return ret;
    401}
    402
    403static struct platform_driver acp5x_dai_driver = {
    404	.probe = acp5x_dai_probe,
    405	.driver = {
    406		.name = "acp5x_i2s_playcap",
    407	},
    408};
    409
    410module_platform_driver(acp5x_dai_driver);
    411
    412MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
    413MODULE_DESCRIPTION("AMD ACP5.x CPU DAI Driver");
    414MODULE_ALIAS("platform:" DRV_NAME);
    415MODULE_LICENSE("GPL v2");