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