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