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

aiu-fifo-i2s.c (4593B)


      1// SPDX-License-Identifier: GPL-2.0
      2//
      3// Copyright (c) 2020 BayLibre, SAS.
      4// Author: Jerome Brunet <jbrunet@baylibre.com>
      5
      6#include <linux/bitfield.h>
      7#include <linux/clk.h>
      8#include <sound/pcm_params.h>
      9#include <sound/soc.h>
     10#include <sound/soc-dai.h>
     11
     12#include "aiu.h"
     13#include "aiu-fifo.h"
     14
     15#define AIU_I2S_SOURCE_DESC_MODE_8CH	BIT(0)
     16#define AIU_I2S_SOURCE_DESC_MODE_24BIT	BIT(5)
     17#define AIU_I2S_SOURCE_DESC_MODE_32BIT	BIT(9)
     18#define AIU_I2S_SOURCE_DESC_MODE_SPLIT	BIT(11)
     19#define AIU_MEM_I2S_MASKS_IRQ_BLOCK	GENMASK(31, 16)
     20#define AIU_MEM_I2S_CONTROL_MODE_16BIT	BIT(6)
     21#define AIU_MEM_I2S_BUF_CNTL_INIT	BIT(0)
     22#define AIU_RST_SOFT_I2S_FAST		BIT(0)
     23#define AIU_I2S_MISC_HOLD_EN		BIT(2)
     24#define AIU_I2S_MISC_FORCE_LEFT_RIGHT	BIT(4)
     25
     26#define AIU_FIFO_I2S_BLOCK		256
     27
     28static struct snd_pcm_hardware fifo_i2s_pcm = {
     29	.info = (SNDRV_PCM_INFO_INTERLEAVED |
     30		 SNDRV_PCM_INFO_MMAP |
     31		 SNDRV_PCM_INFO_MMAP_VALID |
     32		 SNDRV_PCM_INFO_PAUSE),
     33	.formats = AIU_FORMATS,
     34	.rate_min = 5512,
     35	.rate_max = 192000,
     36	.channels_min = 2,
     37	.channels_max = 8,
     38	.period_bytes_min = AIU_FIFO_I2S_BLOCK,
     39	.period_bytes_max = AIU_FIFO_I2S_BLOCK * USHRT_MAX,
     40	.periods_min = 2,
     41	.periods_max = UINT_MAX,
     42
     43	/* No real justification for this */
     44	.buffer_bytes_max = 1 * 1024 * 1024,
     45};
     46
     47static int aiu_fifo_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
     48				struct snd_soc_dai *dai)
     49{
     50	struct snd_soc_component *component = dai->component;
     51
     52	switch (cmd) {
     53	case SNDRV_PCM_TRIGGER_START:
     54	case SNDRV_PCM_TRIGGER_RESUME:
     55	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
     56		snd_soc_component_write(component, AIU_RST_SOFT,
     57					AIU_RST_SOFT_I2S_FAST);
     58		snd_soc_component_read(component, AIU_I2S_SYNC);
     59		break;
     60	}
     61
     62	return aiu_fifo_trigger(substream, cmd, dai);
     63}
     64
     65static int aiu_fifo_i2s_prepare(struct snd_pcm_substream *substream,
     66				struct snd_soc_dai *dai)
     67{
     68	struct snd_soc_component *component = dai->component;
     69	int ret;
     70
     71	ret = aiu_fifo_prepare(substream, dai);
     72	if (ret)
     73		return ret;
     74
     75	snd_soc_component_update_bits(component,
     76				      AIU_MEM_I2S_BUF_CNTL,
     77				      AIU_MEM_I2S_BUF_CNTL_INIT,
     78				      AIU_MEM_I2S_BUF_CNTL_INIT);
     79	snd_soc_component_update_bits(component,
     80				      AIU_MEM_I2S_BUF_CNTL,
     81				      AIU_MEM_I2S_BUF_CNTL_INIT, 0);
     82
     83	return 0;
     84}
     85
     86static int aiu_fifo_i2s_hw_params(struct snd_pcm_substream *substream,
     87				  struct snd_pcm_hw_params *params,
     88				  struct snd_soc_dai *dai)
     89{
     90	struct snd_soc_component *component = dai->component;
     91	struct aiu_fifo *fifo = dai->playback_dma_data;
     92	unsigned int val;
     93	int ret;
     94
     95	snd_soc_component_update_bits(component, AIU_I2S_MISC,
     96				      AIU_I2S_MISC_HOLD_EN,
     97				      AIU_I2S_MISC_HOLD_EN);
     98
     99	ret = aiu_fifo_hw_params(substream, params, dai);
    100	if (ret)
    101		return ret;
    102
    103	switch (params_physical_width(params)) {
    104	case 16:
    105		val = AIU_MEM_I2S_CONTROL_MODE_16BIT;
    106		break;
    107	case 32:
    108		val = 0;
    109		break;
    110	default:
    111		dev_err(dai->dev, "Unsupported physical width %u\n",
    112			params_physical_width(params));
    113		return -EINVAL;
    114	}
    115
    116	snd_soc_component_update_bits(component, AIU_MEM_I2S_CONTROL,
    117				      AIU_MEM_I2S_CONTROL_MODE_16BIT,
    118				      val);
    119
    120	/* Setup the irq periodicity */
    121	val = params_period_bytes(params) / fifo->fifo_block;
    122	val = FIELD_PREP(AIU_MEM_I2S_MASKS_IRQ_BLOCK, val);
    123	snd_soc_component_update_bits(component, AIU_MEM_I2S_MASKS,
    124				      AIU_MEM_I2S_MASKS_IRQ_BLOCK, val);
    125
    126	/*
    127	 * Most (all?) supported SoCs have this bit set by default. The vendor
    128	 * driver however sets it manually (depending on the version either
    129	 * while un-setting AIU_I2S_MISC_HOLD_EN or right before that). Follow
    130	 * the same approach for consistency with the vendor driver.
    131	 */
    132	snd_soc_component_update_bits(component, AIU_I2S_MISC,
    133				      AIU_I2S_MISC_FORCE_LEFT_RIGHT,
    134				      AIU_I2S_MISC_FORCE_LEFT_RIGHT);
    135
    136	snd_soc_component_update_bits(component, AIU_I2S_MISC,
    137				      AIU_I2S_MISC_HOLD_EN, 0);
    138
    139	return 0;
    140}
    141
    142const struct snd_soc_dai_ops aiu_fifo_i2s_dai_ops = {
    143	.trigger	= aiu_fifo_i2s_trigger,
    144	.prepare	= aiu_fifo_i2s_prepare,
    145	.hw_params	= aiu_fifo_i2s_hw_params,
    146	.startup	= aiu_fifo_startup,
    147	.shutdown	= aiu_fifo_shutdown,
    148};
    149
    150int aiu_fifo_i2s_dai_probe(struct snd_soc_dai *dai)
    151{
    152	struct snd_soc_component *component = dai->component;
    153	struct aiu *aiu = snd_soc_component_get_drvdata(component);
    154	struct aiu_fifo *fifo;
    155	int ret;
    156
    157	ret = aiu_fifo_dai_probe(dai);
    158	if (ret)
    159		return ret;
    160
    161	fifo = dai->playback_dma_data;
    162
    163	fifo->pcm = &fifo_i2s_pcm;
    164	fifo->mem_offset = AIU_MEM_I2S_START;
    165	fifo->fifo_block = AIU_FIFO_I2S_BLOCK;
    166	fifo->pclk = aiu->i2s.clks[PCLK].clk;
    167	fifo->irq = aiu->i2s.irq;
    168
    169	return 0;
    170}