brintos

brintos / linux-shallow public Read only

0
0
Text · 4.6 KiB · eccbc16 Raw
174 lines · c
1// SPDX-License-Identifier: GPL-2.02//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		25627 28static const 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 = snd_soc_dai_dma_data_get_playback(dai);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 vendor128	 * driver however sets it manually (depending on the version either129	 * while un-setting AIU_I2S_MISC_HOLD_EN or right before that). Follow130	 * 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	.pcm_new	= aiu_fifo_pcm_new,144	.probe		= aiu_fifo_i2s_dai_probe,145	.remove		= aiu_fifo_dai_remove,146	.trigger	= aiu_fifo_i2s_trigger,147	.prepare	= aiu_fifo_i2s_prepare,148	.hw_params	= aiu_fifo_i2s_hw_params,149	.startup	= aiu_fifo_startup,150	.shutdown	= aiu_fifo_shutdown,151};152 153int aiu_fifo_i2s_dai_probe(struct snd_soc_dai *dai)154{155	struct snd_soc_component *component = dai->component;156	struct aiu *aiu = snd_soc_component_get_drvdata(component);157	struct aiu_fifo *fifo;158	int ret;159 160	ret = aiu_fifo_dai_probe(dai);161	if (ret)162		return ret;163 164	fifo = snd_soc_dai_dma_data_get_playback(dai);165 166	fifo->pcm = &fifo_i2s_pcm;167	fifo->mem_offset = AIU_MEM_I2S_START;168	fifo->fifo_block = AIU_FIFO_I2S_BLOCK;169	fifo->pclk = aiu->i2s.clks[PCLK].clk;170	fifo->irq = aiu->i2s.irq;171 172	return 0;173}174