xref: /OK3568_Linux_fs/kernel/sound/soc/meson/aiu-fifo.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun //
3*4882a593Smuzhiyun // Copyright (c) 2020 BayLibre, SAS.
4*4882a593Smuzhiyun // Author: Jerome Brunet <jbrunet@baylibre.com>
5*4882a593Smuzhiyun 
6*4882a593Smuzhiyun #include <linux/bitfield.h>
7*4882a593Smuzhiyun #include <linux/clk.h>
8*4882a593Smuzhiyun #include <linux/dma-mapping.h>
9*4882a593Smuzhiyun #include <sound/pcm_params.h>
10*4882a593Smuzhiyun #include <sound/soc.h>
11*4882a593Smuzhiyun #include <sound/soc-dai.h>
12*4882a593Smuzhiyun 
13*4882a593Smuzhiyun #include "aiu-fifo.h"
14*4882a593Smuzhiyun 
15*4882a593Smuzhiyun #define AIU_MEM_START	0x00
16*4882a593Smuzhiyun #define AIU_MEM_RD	0x04
17*4882a593Smuzhiyun #define AIU_MEM_END	0x08
18*4882a593Smuzhiyun #define AIU_MEM_MASKS	0x0c
19*4882a593Smuzhiyun #define  AIU_MEM_MASK_CH_RD GENMASK(7, 0)
20*4882a593Smuzhiyun #define  AIU_MEM_MASK_CH_MEM GENMASK(15, 8)
21*4882a593Smuzhiyun #define AIU_MEM_CONTROL	0x10
22*4882a593Smuzhiyun #define  AIU_MEM_CONTROL_INIT BIT(0)
23*4882a593Smuzhiyun #define  AIU_MEM_CONTROL_FILL_EN BIT(1)
24*4882a593Smuzhiyun #define  AIU_MEM_CONTROL_EMPTY_EN BIT(2)
25*4882a593Smuzhiyun 
aiu_fifo_dai(struct snd_pcm_substream * ss)26*4882a593Smuzhiyun static struct snd_soc_dai *aiu_fifo_dai(struct snd_pcm_substream *ss)
27*4882a593Smuzhiyun {
28*4882a593Smuzhiyun 	struct snd_soc_pcm_runtime *rtd = ss->private_data;
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun 	return asoc_rtd_to_cpu(rtd, 0);
31*4882a593Smuzhiyun }
32*4882a593Smuzhiyun 
aiu_fifo_pointer(struct snd_soc_component * component,struct snd_pcm_substream * substream)33*4882a593Smuzhiyun snd_pcm_uframes_t aiu_fifo_pointer(struct snd_soc_component *component,
34*4882a593Smuzhiyun 				   struct snd_pcm_substream *substream)
35*4882a593Smuzhiyun {
36*4882a593Smuzhiyun 	struct snd_soc_dai *dai = aiu_fifo_dai(substream);
37*4882a593Smuzhiyun 	struct aiu_fifo *fifo = dai->playback_dma_data;
38*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
39*4882a593Smuzhiyun 	unsigned int addr;
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun 	addr = snd_soc_component_read(component, fifo->mem_offset + AIU_MEM_RD);
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun 	return bytes_to_frames(runtime, addr - (unsigned int)runtime->dma_addr);
44*4882a593Smuzhiyun }
45*4882a593Smuzhiyun 
aiu_fifo_enable(struct snd_soc_dai * dai,bool enable)46*4882a593Smuzhiyun static void aiu_fifo_enable(struct snd_soc_dai *dai, bool enable)
47*4882a593Smuzhiyun {
48*4882a593Smuzhiyun 	struct snd_soc_component *component = dai->component;
49*4882a593Smuzhiyun 	struct aiu_fifo *fifo = dai->playback_dma_data;
50*4882a593Smuzhiyun 	unsigned int en_mask = (AIU_MEM_CONTROL_FILL_EN |
51*4882a593Smuzhiyun 				AIU_MEM_CONTROL_EMPTY_EN);
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun 	snd_soc_component_update_bits(component,
54*4882a593Smuzhiyun 				      fifo->mem_offset + AIU_MEM_CONTROL,
55*4882a593Smuzhiyun 				      en_mask, enable ? en_mask : 0);
56*4882a593Smuzhiyun }
57*4882a593Smuzhiyun 
aiu_fifo_trigger(struct snd_pcm_substream * substream,int cmd,struct snd_soc_dai * dai)58*4882a593Smuzhiyun int aiu_fifo_trigger(struct snd_pcm_substream *substream, int cmd,
59*4882a593Smuzhiyun 		     struct snd_soc_dai *dai)
60*4882a593Smuzhiyun {
61*4882a593Smuzhiyun 	switch (cmd) {
62*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_START:
63*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_RESUME:
64*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
65*4882a593Smuzhiyun 		aiu_fifo_enable(dai, true);
66*4882a593Smuzhiyun 		break;
67*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_SUSPEND:
68*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
69*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_STOP:
70*4882a593Smuzhiyun 		aiu_fifo_enable(dai, false);
71*4882a593Smuzhiyun 		break;
72*4882a593Smuzhiyun 	default:
73*4882a593Smuzhiyun 		return -EINVAL;
74*4882a593Smuzhiyun 	}
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun 	return 0;
77*4882a593Smuzhiyun }
78*4882a593Smuzhiyun 
aiu_fifo_prepare(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)79*4882a593Smuzhiyun int aiu_fifo_prepare(struct snd_pcm_substream *substream,
80*4882a593Smuzhiyun 		     struct snd_soc_dai *dai)
81*4882a593Smuzhiyun {
82*4882a593Smuzhiyun 	struct snd_soc_component *component = dai->component;
83*4882a593Smuzhiyun 	struct aiu_fifo *fifo = dai->playback_dma_data;
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 	snd_soc_component_update_bits(component,
86*4882a593Smuzhiyun 				      fifo->mem_offset + AIU_MEM_CONTROL,
87*4882a593Smuzhiyun 				      AIU_MEM_CONTROL_INIT,
88*4882a593Smuzhiyun 				      AIU_MEM_CONTROL_INIT);
89*4882a593Smuzhiyun 	snd_soc_component_update_bits(component,
90*4882a593Smuzhiyun 				      fifo->mem_offset + AIU_MEM_CONTROL,
91*4882a593Smuzhiyun 				      AIU_MEM_CONTROL_INIT, 0);
92*4882a593Smuzhiyun 	return 0;
93*4882a593Smuzhiyun }
94*4882a593Smuzhiyun 
aiu_fifo_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)95*4882a593Smuzhiyun int aiu_fifo_hw_params(struct snd_pcm_substream *substream,
96*4882a593Smuzhiyun 		       struct snd_pcm_hw_params *params,
97*4882a593Smuzhiyun 		       struct snd_soc_dai *dai)
98*4882a593Smuzhiyun {
99*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
100*4882a593Smuzhiyun 	struct snd_soc_component *component = dai->component;
101*4882a593Smuzhiyun 	struct aiu_fifo *fifo = dai->playback_dma_data;
102*4882a593Smuzhiyun 	dma_addr_t end;
103*4882a593Smuzhiyun 	int ret;
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun 	ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
106*4882a593Smuzhiyun 	if (ret < 0)
107*4882a593Smuzhiyun 		return ret;
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun 	/* Setup the fifo boundaries */
110*4882a593Smuzhiyun 	end = runtime->dma_addr + runtime->dma_bytes - fifo->fifo_block;
111*4882a593Smuzhiyun 	snd_soc_component_write(component, fifo->mem_offset + AIU_MEM_START,
112*4882a593Smuzhiyun 				runtime->dma_addr);
113*4882a593Smuzhiyun 	snd_soc_component_write(component, fifo->mem_offset + AIU_MEM_RD,
114*4882a593Smuzhiyun 				runtime->dma_addr);
115*4882a593Smuzhiyun 	snd_soc_component_write(component, fifo->mem_offset + AIU_MEM_END,
116*4882a593Smuzhiyun 				end);
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun 	/* Setup the fifo to read all the memory - no skip */
119*4882a593Smuzhiyun 	snd_soc_component_update_bits(component,
120*4882a593Smuzhiyun 				      fifo->mem_offset + AIU_MEM_MASKS,
121*4882a593Smuzhiyun 				      AIU_MEM_MASK_CH_RD | AIU_MEM_MASK_CH_MEM,
122*4882a593Smuzhiyun 				      FIELD_PREP(AIU_MEM_MASK_CH_RD, 0xff) |
123*4882a593Smuzhiyun 				      FIELD_PREP(AIU_MEM_MASK_CH_MEM, 0xff));
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun 	return 0;
126*4882a593Smuzhiyun }
127*4882a593Smuzhiyun 
aiu_fifo_hw_free(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)128*4882a593Smuzhiyun int aiu_fifo_hw_free(struct snd_pcm_substream *substream,
129*4882a593Smuzhiyun 		     struct snd_soc_dai *dai)
130*4882a593Smuzhiyun {
131*4882a593Smuzhiyun 	return snd_pcm_lib_free_pages(substream);
132*4882a593Smuzhiyun }
133*4882a593Smuzhiyun 
aiu_fifo_isr(int irq,void * dev_id)134*4882a593Smuzhiyun static irqreturn_t aiu_fifo_isr(int irq, void *dev_id)
135*4882a593Smuzhiyun {
136*4882a593Smuzhiyun 	struct snd_pcm_substream *playback = dev_id;
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun 	snd_pcm_period_elapsed(playback);
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun 	return IRQ_HANDLED;
141*4882a593Smuzhiyun }
142*4882a593Smuzhiyun 
aiu_fifo_startup(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)143*4882a593Smuzhiyun int aiu_fifo_startup(struct snd_pcm_substream *substream,
144*4882a593Smuzhiyun 		     struct snd_soc_dai *dai)
145*4882a593Smuzhiyun {
146*4882a593Smuzhiyun 	struct aiu_fifo *fifo = dai->playback_dma_data;
147*4882a593Smuzhiyun 	int ret;
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun 	snd_soc_set_runtime_hwparams(substream, fifo->pcm);
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun 	/*
152*4882a593Smuzhiyun 	 * Make sure the buffer and period size are multiple of the fifo burst
153*4882a593Smuzhiyun 	 * size
154*4882a593Smuzhiyun 	 */
155*4882a593Smuzhiyun 	ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
156*4882a593Smuzhiyun 					 SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
157*4882a593Smuzhiyun 					 fifo->fifo_block);
158*4882a593Smuzhiyun 	if (ret)
159*4882a593Smuzhiyun 		return ret;
160*4882a593Smuzhiyun 
161*4882a593Smuzhiyun 	ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
162*4882a593Smuzhiyun 					 SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
163*4882a593Smuzhiyun 					 fifo->fifo_block);
164*4882a593Smuzhiyun 	if (ret)
165*4882a593Smuzhiyun 		return ret;
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun 	ret = clk_prepare_enable(fifo->pclk);
168*4882a593Smuzhiyun 	if (ret)
169*4882a593Smuzhiyun 		return ret;
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun 	ret = request_irq(fifo->irq, aiu_fifo_isr, 0, dev_name(dai->dev),
172*4882a593Smuzhiyun 			  substream);
173*4882a593Smuzhiyun 	if (ret)
174*4882a593Smuzhiyun 		clk_disable_unprepare(fifo->pclk);
175*4882a593Smuzhiyun 
176*4882a593Smuzhiyun 	return ret;
177*4882a593Smuzhiyun }
178*4882a593Smuzhiyun 
aiu_fifo_shutdown(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)179*4882a593Smuzhiyun void aiu_fifo_shutdown(struct snd_pcm_substream *substream,
180*4882a593Smuzhiyun 		       struct snd_soc_dai *dai)
181*4882a593Smuzhiyun {
182*4882a593Smuzhiyun 	struct aiu_fifo *fifo = dai->playback_dma_data;
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun 	free_irq(fifo->irq, substream);
185*4882a593Smuzhiyun 	clk_disable_unprepare(fifo->pclk);
186*4882a593Smuzhiyun }
187*4882a593Smuzhiyun 
aiu_fifo_pcm_new(struct snd_soc_pcm_runtime * rtd,struct snd_soc_dai * dai)188*4882a593Smuzhiyun int aiu_fifo_pcm_new(struct snd_soc_pcm_runtime *rtd,
189*4882a593Smuzhiyun 		     struct snd_soc_dai *dai)
190*4882a593Smuzhiyun {
191*4882a593Smuzhiyun 	struct snd_pcm_substream *substream =
192*4882a593Smuzhiyun 		rtd->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
193*4882a593Smuzhiyun 	struct snd_card *card = rtd->card->snd_card;
194*4882a593Smuzhiyun 	struct aiu_fifo *fifo = dai->playback_dma_data;
195*4882a593Smuzhiyun 	size_t size = fifo->pcm->buffer_bytes_max;
196*4882a593Smuzhiyun 	int ret;
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun 	ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
199*4882a593Smuzhiyun 	if (ret)
200*4882a593Smuzhiyun 		return ret;
201*4882a593Smuzhiyun 
202*4882a593Smuzhiyun 	snd_pcm_lib_preallocate_pages(substream,
203*4882a593Smuzhiyun 				      SNDRV_DMA_TYPE_DEV,
204*4882a593Smuzhiyun 				      card->dev, size, size);
205*4882a593Smuzhiyun 
206*4882a593Smuzhiyun 	return 0;
207*4882a593Smuzhiyun }
208*4882a593Smuzhiyun 
aiu_fifo_dai_probe(struct snd_soc_dai * dai)209*4882a593Smuzhiyun int aiu_fifo_dai_probe(struct snd_soc_dai *dai)
210*4882a593Smuzhiyun {
211*4882a593Smuzhiyun 	struct aiu_fifo *fifo;
212*4882a593Smuzhiyun 
213*4882a593Smuzhiyun 	fifo = kzalloc(sizeof(*fifo), GFP_KERNEL);
214*4882a593Smuzhiyun 	if (!fifo)
215*4882a593Smuzhiyun 		return -ENOMEM;
216*4882a593Smuzhiyun 
217*4882a593Smuzhiyun 	dai->playback_dma_data = fifo;
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	return 0;
220*4882a593Smuzhiyun }
221*4882a593Smuzhiyun 
aiu_fifo_dai_remove(struct snd_soc_dai * dai)222*4882a593Smuzhiyun int aiu_fifo_dai_remove(struct snd_soc_dai *dai)
223*4882a593Smuzhiyun {
224*4882a593Smuzhiyun 	kfree(dai->playback_dma_data);
225*4882a593Smuzhiyun 
226*4882a593Smuzhiyun 	return 0;
227*4882a593Smuzhiyun }
228*4882a593Smuzhiyun 
229