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

bcm63xx-pcm-whistler.c (11347B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2// linux/sound/bcm/bcm63xx-pcm-whistler.c
      3// BCM63xx whistler pcm interface
      4// Copyright (c) 2020 Broadcom Corporation
      5// Author: Kevin-Ke Li <kevin-ke.li@broadcom.com>
      6
      7#include <linux/dma-mapping.h>
      8#include <linux/io.h>
      9#include <linux/irq.h>
     10#include <linux/module.h>
     11#include <sound/pcm_params.h>
     12#include <linux/regmap.h>
     13#include <linux/of_device.h>
     14#include <sound/soc.h>
     15#include "bcm63xx-i2s.h"
     16
     17
     18struct i2s_dma_desc {
     19	unsigned char *dma_area;
     20	dma_addr_t dma_addr;
     21	unsigned int dma_len;
     22};
     23
     24struct bcm63xx_runtime_data {
     25	int dma_len;
     26	dma_addr_t dma_addr;
     27	dma_addr_t dma_addr_next;
     28};
     29
     30static const struct snd_pcm_hardware bcm63xx_pcm_hardware = {
     31	.info = SNDRV_PCM_INFO_MMAP |
     32		SNDRV_PCM_INFO_MMAP_VALID |
     33		SNDRV_PCM_INFO_INTERLEAVED |
     34		SNDRV_PCM_INFO_PAUSE |
     35		SNDRV_PCM_INFO_RESUME,
     36	.formats = SNDRV_PCM_FMTBIT_S32_LE, /* support S32 only */
     37	.period_bytes_max = 8192 - 32,
     38	.periods_min = 1,
     39	.periods_max = PAGE_SIZE/sizeof(struct i2s_dma_desc),
     40	.buffer_bytes_max = 128 * 1024,
     41	.fifo_size = 32,
     42};
     43
     44static int bcm63xx_pcm_hw_params(struct snd_soc_component *component,
     45				 struct snd_pcm_substream *substream,
     46				 struct snd_pcm_hw_params *params)
     47{
     48	struct i2s_dma_desc *dma_desc;
     49	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
     50
     51	dma_desc = kzalloc(sizeof(*dma_desc), GFP_NOWAIT);
     52	if (!dma_desc)
     53		return -ENOMEM;
     54
     55	snd_soc_dai_set_dma_data(asoc_rtd_to_cpu(rtd, 0), substream, dma_desc);
     56
     57	return 0;
     58}
     59
     60static int bcm63xx_pcm_hw_free(struct snd_soc_component *component,
     61			struct snd_pcm_substream *substream)
     62{
     63	struct i2s_dma_desc	*dma_desc;
     64	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
     65
     66	dma_desc = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
     67	kfree(dma_desc);
     68
     69	return 0;
     70}
     71
     72static int bcm63xx_pcm_trigger(struct snd_soc_component *component,
     73			       struct snd_pcm_substream *substream, int cmd)
     74{
     75	int ret = 0;
     76	struct snd_soc_pcm_runtime *rtd;
     77	struct bcm_i2s_priv *i2s_priv;
     78	struct regmap   *regmap_i2s;
     79
     80	rtd = asoc_substream_to_rtd(substream);
     81	i2s_priv = dev_get_drvdata(asoc_rtd_to_cpu(rtd, 0)->dev);
     82	regmap_i2s = i2s_priv->regmap_i2s;
     83
     84	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
     85		switch (cmd) {
     86		case SNDRV_PCM_TRIGGER_START:
     87			regmap_update_bits(regmap_i2s,
     88					   I2S_TX_IRQ_EN,
     89					   I2S_TX_DESC_OFF_INTR_EN,
     90					   I2S_TX_DESC_OFF_INTR_EN);
     91			regmap_update_bits(regmap_i2s,
     92					   I2S_TX_CFG,
     93					   I2S_TX_ENABLE_MASK,
     94					   I2S_TX_ENABLE);
     95			break;
     96		case SNDRV_PCM_TRIGGER_STOP:
     97		case SNDRV_PCM_TRIGGER_SUSPEND:
     98		case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
     99			regmap_write(regmap_i2s,
    100				     I2S_TX_IRQ_EN,
    101				     0);
    102			regmap_update_bits(regmap_i2s,
    103					   I2S_TX_CFG,
    104					   I2S_TX_ENABLE_MASK,
    105					   0);
    106			break;
    107		default:
    108			ret = -EINVAL;
    109		}
    110	} else {
    111		switch (cmd) {
    112		case SNDRV_PCM_TRIGGER_START:
    113			regmap_update_bits(regmap_i2s,
    114					   I2S_RX_IRQ_EN,
    115					   I2S_RX_DESC_OFF_INTR_EN_MSK,
    116					   I2S_RX_DESC_OFF_INTR_EN);
    117			regmap_update_bits(regmap_i2s,
    118					   I2S_RX_CFG,
    119					   I2S_RX_ENABLE_MASK,
    120					   I2S_RX_ENABLE);
    121			break;
    122		case SNDRV_PCM_TRIGGER_STOP:
    123		case SNDRV_PCM_TRIGGER_SUSPEND:
    124		case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
    125			regmap_update_bits(regmap_i2s,
    126					   I2S_RX_IRQ_EN,
    127					   I2S_RX_DESC_OFF_INTR_EN_MSK,
    128					   0);
    129			regmap_update_bits(regmap_i2s,
    130					   I2S_RX_CFG,
    131					   I2S_RX_ENABLE_MASK,
    132					   0);
    133			break;
    134		default:
    135			ret = -EINVAL;
    136		}
    137	}
    138	return ret;
    139}
    140
    141static int bcm63xx_pcm_prepare(struct snd_soc_component *component,
    142			struct snd_pcm_substream *substream)
    143{
    144	struct i2s_dma_desc	*dma_desc;
    145	struct regmap		*regmap_i2s;
    146	struct bcm_i2s_priv	*i2s_priv;
    147	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    148	struct snd_pcm_runtime *runtime = substream->runtime;
    149	uint32_t regaddr_desclen, regaddr_descaddr;
    150
    151	dma_desc = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
    152	dma_desc->dma_len  = snd_pcm_lib_period_bytes(substream);
    153	dma_desc->dma_addr = runtime->dma_addr;
    154	dma_desc->dma_area = runtime->dma_area;
    155
    156	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
    157		regaddr_desclen = I2S_TX_DESC_IFF_LEN;
    158		regaddr_descaddr = I2S_TX_DESC_IFF_ADDR;
    159	} else {
    160		regaddr_desclen = I2S_RX_DESC_IFF_LEN;
    161		regaddr_descaddr = I2S_RX_DESC_IFF_ADDR;
    162	}
    163
    164	i2s_priv = dev_get_drvdata(asoc_rtd_to_cpu(rtd, 0)->dev);
    165	regmap_i2s = i2s_priv->regmap_i2s;
    166
    167	regmap_write(regmap_i2s, regaddr_desclen, dma_desc->dma_len);
    168	regmap_write(regmap_i2s, regaddr_descaddr, dma_desc->dma_addr);
    169
    170	return 0;
    171}
    172
    173static snd_pcm_uframes_t
    174bcm63xx_pcm_pointer(struct snd_soc_component *component,
    175		struct snd_pcm_substream *substream)
    176{
    177	snd_pcm_uframes_t x;
    178	struct bcm63xx_runtime_data *prtd = substream->runtime->private_data;
    179
    180	if (!prtd->dma_addr_next)
    181		prtd->dma_addr_next = substream->runtime->dma_addr;
    182
    183	x = bytes_to_frames(substream->runtime,
    184		prtd->dma_addr_next - substream->runtime->dma_addr);
    185
    186	return x == substream->runtime->buffer_size ? 0 : x;
    187}
    188
    189static int bcm63xx_pcm_open(struct snd_soc_component *component,
    190			struct snd_pcm_substream *substream)
    191{
    192	int ret = 0;
    193	struct snd_pcm_runtime *runtime = substream->runtime;
    194	struct bcm63xx_runtime_data *prtd;
    195
    196	runtime->hw = bcm63xx_pcm_hardware;
    197	ret = snd_pcm_hw_constraint_step(runtime, 0,
    198					 SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
    199	if (ret)
    200		goto out;
    201
    202	ret = snd_pcm_hw_constraint_step(runtime, 0,
    203					 SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
    204	if (ret)
    205		goto out;
    206
    207	ret = snd_pcm_hw_constraint_integer(runtime,
    208					    SNDRV_PCM_HW_PARAM_PERIODS);
    209	if (ret < 0)
    210		goto out;
    211
    212	ret = -ENOMEM;
    213	prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
    214	if (!prtd)
    215		goto out;
    216
    217	runtime->private_data = prtd;
    218	return 0;
    219out:
    220	return ret;
    221}
    222
    223static int bcm63xx_pcm_close(struct snd_soc_component *component,
    224			struct snd_pcm_substream *substream)
    225{
    226	struct snd_pcm_runtime *runtime = substream->runtime;
    227	struct bcm63xx_runtime_data *prtd = runtime->private_data;
    228
    229	kfree(prtd);
    230	return 0;
    231}
    232
    233static irqreturn_t i2s_dma_isr(int irq, void *bcm_i2s_priv)
    234{
    235	unsigned int availdepth, ifflevel, offlevel, int_status, val_1, val_2;
    236	struct bcm63xx_runtime_data *prtd;
    237	struct snd_pcm_substream *substream;
    238	struct snd_pcm_runtime *runtime;
    239	struct regmap *regmap_i2s;
    240	struct i2s_dma_desc *dma_desc;
    241	struct snd_soc_pcm_runtime *rtd;
    242	struct bcm_i2s_priv *i2s_priv;
    243
    244	i2s_priv = (struct bcm_i2s_priv *)bcm_i2s_priv;
    245	regmap_i2s = i2s_priv->regmap_i2s;
    246
    247	/* rx */
    248	regmap_read(regmap_i2s, I2S_RX_IRQ_CTL, &int_status);
    249
    250	if (int_status & I2S_RX_DESC_OFF_INTR_EN_MSK) {
    251		substream = i2s_priv->capture_substream;
    252		runtime = substream->runtime;
    253		rtd = asoc_substream_to_rtd(substream);
    254		prtd = runtime->private_data;
    255		dma_desc = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
    256
    257		offlevel = (int_status & I2S_RX_DESC_OFF_LEVEL_MASK) >>
    258			   I2S_RX_DESC_OFF_LEVEL_SHIFT;
    259		while (offlevel) {
    260			regmap_read(regmap_i2s, I2S_RX_DESC_OFF_ADDR, &val_1);
    261			regmap_read(regmap_i2s, I2S_RX_DESC_OFF_LEN, &val_2);
    262			offlevel--;
    263		}
    264		prtd->dma_addr_next = val_1 + val_2;
    265		ifflevel = (int_status & I2S_RX_DESC_IFF_LEVEL_MASK) >>
    266			   I2S_RX_DESC_IFF_LEVEL_SHIFT;
    267
    268		availdepth = I2S_DESC_FIFO_DEPTH - ifflevel;
    269		while (availdepth) {
    270			dma_desc->dma_addr +=
    271					snd_pcm_lib_period_bytes(substream);
    272			dma_desc->dma_area +=
    273					snd_pcm_lib_period_bytes(substream);
    274			if (dma_desc->dma_addr - runtime->dma_addr >=
    275						runtime->dma_bytes) {
    276				dma_desc->dma_addr = runtime->dma_addr;
    277				dma_desc->dma_area = runtime->dma_area;
    278			}
    279
    280			prtd->dma_addr = dma_desc->dma_addr;
    281			regmap_write(regmap_i2s, I2S_RX_DESC_IFF_LEN,
    282				     snd_pcm_lib_period_bytes(substream));
    283			regmap_write(regmap_i2s, I2S_RX_DESC_IFF_ADDR,
    284				     dma_desc->dma_addr);
    285			availdepth--;
    286		}
    287
    288		snd_pcm_period_elapsed(substream);
    289
    290		/* Clear interrupt by writing 0 */
    291		regmap_update_bits(regmap_i2s, I2S_RX_IRQ_CTL,
    292				   I2S_RX_INTR_MASK, 0);
    293	}
    294
    295	/* tx */
    296	regmap_read(regmap_i2s, I2S_TX_IRQ_CTL, &int_status);
    297
    298	if (int_status & I2S_TX_DESC_OFF_INTR_EN_MSK) {
    299		substream = i2s_priv->play_substream;
    300		runtime = substream->runtime;
    301		rtd = asoc_substream_to_rtd(substream);
    302		prtd = runtime->private_data;
    303		dma_desc = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
    304
    305		offlevel = (int_status & I2S_TX_DESC_OFF_LEVEL_MASK) >>
    306			   I2S_TX_DESC_OFF_LEVEL_SHIFT;
    307		while (offlevel) {
    308			regmap_read(regmap_i2s, I2S_TX_DESC_OFF_ADDR, &val_1);
    309			regmap_read(regmap_i2s, I2S_TX_DESC_OFF_LEN,  &val_2);
    310			prtd->dma_addr_next = val_1 + val_2;
    311			offlevel--;
    312		}
    313
    314		ifflevel = (int_status & I2S_TX_DESC_IFF_LEVEL_MASK) >>
    315			I2S_TX_DESC_IFF_LEVEL_SHIFT;
    316		availdepth = I2S_DESC_FIFO_DEPTH - ifflevel;
    317
    318		while (availdepth) {
    319			dma_desc->dma_addr +=
    320					snd_pcm_lib_period_bytes(substream);
    321			dma_desc->dma_area +=
    322					snd_pcm_lib_period_bytes(substream);
    323
    324			if (dma_desc->dma_addr - runtime->dma_addr >=
    325							runtime->dma_bytes) {
    326				dma_desc->dma_addr = runtime->dma_addr;
    327				dma_desc->dma_area = runtime->dma_area;
    328			}
    329
    330			prtd->dma_addr = dma_desc->dma_addr;
    331			regmap_write(regmap_i2s, I2S_TX_DESC_IFF_LEN,
    332				snd_pcm_lib_period_bytes(substream));
    333			regmap_write(regmap_i2s, I2S_TX_DESC_IFF_ADDR,
    334					dma_desc->dma_addr);
    335			availdepth--;
    336		}
    337
    338		snd_pcm_period_elapsed(substream);
    339
    340		/* Clear interrupt by writing 0 */
    341		regmap_update_bits(regmap_i2s, I2S_TX_IRQ_CTL,
    342				   I2S_TX_INTR_MASK, 0);
    343	}
    344
    345	return IRQ_HANDLED;
    346}
    347
    348static int bcm63xx_soc_pcm_new(struct snd_soc_component *component,
    349		struct snd_soc_pcm_runtime *rtd)
    350{
    351	struct snd_pcm *pcm = rtd->pcm;
    352	struct bcm_i2s_priv *i2s_priv;
    353	int ret;
    354
    355	i2s_priv = dev_get_drvdata(asoc_rtd_to_cpu(rtd, 0)->dev);
    356
    357	of_dma_configure(pcm->card->dev, pcm->card->dev->of_node, 1);
    358
    359	ret = dma_coerce_mask_and_coherent(pcm->card->dev, DMA_BIT_MASK(32));
    360	if (ret)
    361		return ret;
    362
    363	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream)
    364		i2s_priv->play_substream =
    365			pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
    366	if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream)
    367		i2s_priv->capture_substream =
    368			pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
    369
    370	return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_WC,
    371					    pcm->card->dev,
    372					    bcm63xx_pcm_hardware.buffer_bytes_max);
    373}
    374
    375static const struct snd_soc_component_driver bcm63xx_soc_platform = {
    376	.open = bcm63xx_pcm_open,
    377	.close = bcm63xx_pcm_close,
    378	.hw_params = bcm63xx_pcm_hw_params,
    379	.hw_free = bcm63xx_pcm_hw_free,
    380	.prepare = bcm63xx_pcm_prepare,
    381	.trigger = bcm63xx_pcm_trigger,
    382	.pointer = bcm63xx_pcm_pointer,
    383	.pcm_construct = bcm63xx_soc_pcm_new,
    384};
    385
    386int bcm63xx_soc_platform_probe(struct platform_device *pdev,
    387			       struct bcm_i2s_priv *i2s_priv)
    388{
    389	int ret;
    390
    391	ret = platform_get_irq(pdev, 0);
    392	if (ret < 0)
    393		return ret;
    394
    395	ret = devm_request_irq(&pdev->dev, ret, i2s_dma_isr,
    396			       irq_get_trigger_type(ret), "i2s_dma", (void *)i2s_priv);
    397	if (ret) {
    398		dev_err(&pdev->dev,
    399			"i2s_init: failed to request interrupt.ret=%d\n", ret);
    400		return ret;
    401	}
    402
    403	return devm_snd_soc_register_component(&pdev->dev,
    404					&bcm63xx_soc_platform, NULL, 0);
    405}
    406
    407int bcm63xx_soc_platform_remove(struct platform_device *pdev)
    408{
    409	return 0;
    410}
    411
    412MODULE_AUTHOR("Kevin,Li <kevin-ke.li@broadcom.com>");
    413MODULE_DESCRIPTION("Broadcom DSL XPON ASOC PCM Interface");
    414MODULE_LICENSE("GPL v2");