1 | // SPDX-License-Identifier: (GPL-2.0 OR MIT) |
2 | // |
3 | // Copyright (c) 2018 BayLibre, SAS. |
4 | // Author: Jerome Brunet <jbrunet@baylibre.com> |
5 | |
6 | #include <linux/bitfield.h> |
7 | #include <linux/clk.h> |
8 | #include <linux/of_irq.h> |
9 | #include <linux/of_platform.h> |
10 | #include <linux/module.h> |
11 | #include <linux/regmap.h> |
12 | #include <linux/reset.h> |
13 | #include <sound/pcm_params.h> |
14 | #include <sound/soc.h> |
15 | #include <sound/soc-dai.h> |
16 | |
17 | #include "axg-fifo.h" |
18 | |
19 | /* |
20 | * This file implements the platform operations common to the playback and |
21 | * capture frontend DAI. The logic behind this two types of fifo is very |
22 | * similar but some difference exist. |
23 | * These differences are handled in the respective DAI drivers |
24 | */ |
25 | |
26 | static struct snd_pcm_hardware axg_fifo_hw = { |
27 | .info = (SNDRV_PCM_INFO_INTERLEAVED | |
28 | SNDRV_PCM_INFO_MMAP | |
29 | SNDRV_PCM_INFO_MMAP_VALID | |
30 | SNDRV_PCM_INFO_BLOCK_TRANSFER | |
31 | SNDRV_PCM_INFO_PAUSE | |
32 | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP), |
33 | .formats = AXG_FIFO_FORMATS, |
34 | .rate_min = 5512, |
35 | .rate_max = 384000, |
36 | .channels_min = 1, |
37 | .channels_max = AXG_FIFO_CH_MAX, |
38 | .period_bytes_min = AXG_FIFO_BURST, |
39 | .period_bytes_max = UINT_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 | |
47 | static struct snd_soc_dai *axg_fifo_dai(struct snd_pcm_substream *ss) |
48 | { |
49 | struct snd_soc_pcm_runtime *rtd = ss->private_data; |
50 | |
51 | return snd_soc_rtd_to_cpu(rtd, 0); |
52 | } |
53 | |
54 | static struct axg_fifo *axg_fifo_data(struct snd_pcm_substream *ss) |
55 | { |
56 | struct snd_soc_dai *dai = axg_fifo_dai(ss); |
57 | |
58 | return snd_soc_dai_get_drvdata(dai); |
59 | } |
60 | |
61 | static struct device *axg_fifo_dev(struct snd_pcm_substream *ss) |
62 | { |
63 | struct snd_soc_dai *dai = axg_fifo_dai(ss); |
64 | |
65 | return dai->dev; |
66 | } |
67 | |
68 | static void __dma_enable(struct axg_fifo *fifo, bool enable) |
69 | { |
70 | regmap_update_bits(map: fifo->map, FIFO_CTRL0, CTRL0_DMA_EN, |
71 | val: enable ? CTRL0_DMA_EN : 0); |
72 | } |
73 | |
74 | int axg_fifo_pcm_trigger(struct snd_soc_component *component, |
75 | struct snd_pcm_substream *ss, int cmd) |
76 | { |
77 | struct axg_fifo *fifo = axg_fifo_data(ss); |
78 | |
79 | switch (cmd) { |
80 | case SNDRV_PCM_TRIGGER_START: |
81 | case SNDRV_PCM_TRIGGER_RESUME: |
82 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
83 | __dma_enable(fifo, enable: true); |
84 | break; |
85 | case SNDRV_PCM_TRIGGER_SUSPEND: |
86 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
87 | case SNDRV_PCM_TRIGGER_STOP: |
88 | __dma_enable(fifo, enable: false); |
89 | break; |
90 | default: |
91 | return -EINVAL; |
92 | } |
93 | |
94 | return 0; |
95 | } |
96 | EXPORT_SYMBOL_GPL(axg_fifo_pcm_trigger); |
97 | |
98 | snd_pcm_uframes_t axg_fifo_pcm_pointer(struct snd_soc_component *component, |
99 | struct snd_pcm_substream *ss) |
100 | { |
101 | struct axg_fifo *fifo = axg_fifo_data(ss); |
102 | struct snd_pcm_runtime *runtime = ss->runtime; |
103 | unsigned int addr; |
104 | |
105 | regmap_read(map: fifo->map, FIFO_STATUS2, val: &addr); |
106 | |
107 | return bytes_to_frames(runtime, size: addr - (unsigned int)runtime->dma_addr); |
108 | } |
109 | EXPORT_SYMBOL_GPL(axg_fifo_pcm_pointer); |
110 | |
111 | int axg_fifo_pcm_hw_params(struct snd_soc_component *component, |
112 | struct snd_pcm_substream *ss, |
113 | struct snd_pcm_hw_params *params) |
114 | { |
115 | struct snd_pcm_runtime *runtime = ss->runtime; |
116 | struct axg_fifo *fifo = axg_fifo_data(ss); |
117 | unsigned int burst_num, period, threshold, irq_en; |
118 | dma_addr_t end_ptr; |
119 | |
120 | period = params_period_bytes(p: params); |
121 | |
122 | /* Setup dma memory pointers */ |
123 | end_ptr = runtime->dma_addr + runtime->dma_bytes - AXG_FIFO_BURST; |
124 | regmap_write(map: fifo->map, FIFO_START_ADDR, val: runtime->dma_addr); |
125 | regmap_write(map: fifo->map, FIFO_FINISH_ADDR, val: end_ptr); |
126 | |
127 | /* Setup interrupt periodicity */ |
128 | burst_num = period / AXG_FIFO_BURST; |
129 | regmap_write(map: fifo->map, FIFO_INT_ADDR, val: burst_num); |
130 | |
131 | /* |
132 | * Start the fifo request on the smallest of the following: |
133 | * - Half the fifo size |
134 | * - Half the period size |
135 | */ |
136 | threshold = min(period / 2, fifo->depth / 2); |
137 | |
138 | /* |
139 | * With the threshold in bytes, register value is: |
140 | * V = (threshold / burst) - 1 |
141 | */ |
142 | threshold /= AXG_FIFO_BURST; |
143 | regmap_field_write(field: fifo->field_threshold, |
144 | val: threshold ? threshold - 1 : 0); |
145 | |
146 | /* Enable irq if necessary */ |
147 | irq_en = runtime->no_period_wakeup ? 0 : FIFO_INT_COUNT_REPEAT; |
148 | regmap_update_bits(map: fifo->map, FIFO_CTRL0, |
149 | CTRL0_INT_EN, |
150 | FIELD_PREP(CTRL0_INT_EN, irq_en)); |
151 | |
152 | return 0; |
153 | } |
154 | EXPORT_SYMBOL_GPL(axg_fifo_pcm_hw_params); |
155 | |
156 | int g12a_fifo_pcm_hw_params(struct snd_soc_component *component, |
157 | struct snd_pcm_substream *ss, |
158 | struct snd_pcm_hw_params *params) |
159 | { |
160 | struct axg_fifo *fifo = axg_fifo_data(ss); |
161 | struct snd_pcm_runtime *runtime = ss->runtime; |
162 | int ret; |
163 | |
164 | ret = axg_fifo_pcm_hw_params(component, ss, params); |
165 | if (ret) |
166 | return ret; |
167 | |
168 | /* Set the initial memory address of the DMA */ |
169 | regmap_write(map: fifo->map, FIFO_INIT_ADDR, val: runtime->dma_addr); |
170 | |
171 | return 0; |
172 | } |
173 | EXPORT_SYMBOL_GPL(g12a_fifo_pcm_hw_params); |
174 | |
175 | int axg_fifo_pcm_hw_free(struct snd_soc_component *component, |
176 | struct snd_pcm_substream *ss) |
177 | { |
178 | struct axg_fifo *fifo = axg_fifo_data(ss); |
179 | |
180 | /* Disable irqs */ |
181 | regmap_update_bits(map: fifo->map, FIFO_CTRL0, |
182 | CTRL0_INT_EN, val: 0); |
183 | |
184 | return 0; |
185 | } |
186 | EXPORT_SYMBOL_GPL(axg_fifo_pcm_hw_free); |
187 | |
188 | static void axg_fifo_ack_irq(struct axg_fifo *fifo, u8 mask) |
189 | { |
190 | regmap_update_bits(map: fifo->map, FIFO_CTRL1, |
191 | CTRL1_INT_CLR, |
192 | FIELD_PREP(CTRL1_INT_CLR, mask)); |
193 | |
194 | /* Clear must also be cleared */ |
195 | regmap_update_bits(map: fifo->map, FIFO_CTRL1, |
196 | CTRL1_INT_CLR, |
197 | FIELD_PREP(CTRL1_INT_CLR, 0)); |
198 | } |
199 | |
200 | static irqreturn_t axg_fifo_pcm_irq_block(int irq, void *dev_id) |
201 | { |
202 | struct snd_pcm_substream *ss = dev_id; |
203 | struct axg_fifo *fifo = axg_fifo_data(ss); |
204 | unsigned int status; |
205 | |
206 | regmap_read(map: fifo->map, FIFO_STATUS1, val: &status); |
207 | |
208 | status = FIELD_GET(STATUS1_INT_STS, status); |
209 | if (status & FIFO_INT_COUNT_REPEAT) |
210 | snd_pcm_period_elapsed(substream: ss); |
211 | else |
212 | dev_dbg(axg_fifo_dev(ss), "unexpected irq - STS 0x%02x\n" , |
213 | status); |
214 | |
215 | /* Ack irqs */ |
216 | axg_fifo_ack_irq(fifo, mask: status); |
217 | |
218 | return IRQ_RETVAL(status); |
219 | } |
220 | |
221 | int axg_fifo_pcm_open(struct snd_soc_component *component, |
222 | struct snd_pcm_substream *ss) |
223 | { |
224 | struct axg_fifo *fifo = axg_fifo_data(ss); |
225 | struct device *dev = axg_fifo_dev(ss); |
226 | int ret; |
227 | |
228 | snd_soc_set_runtime_hwparams(substream: ss, hw: &axg_fifo_hw); |
229 | |
230 | /* |
231 | * Make sure the buffer and period size are multiple of the FIFO |
232 | * burst |
233 | */ |
234 | ret = snd_pcm_hw_constraint_step(runtime: ss->runtime, cond: 0, |
235 | SNDRV_PCM_HW_PARAM_BUFFER_BYTES, |
236 | AXG_FIFO_BURST); |
237 | if (ret) |
238 | return ret; |
239 | |
240 | ret = snd_pcm_hw_constraint_step(runtime: ss->runtime, cond: 0, |
241 | SNDRV_PCM_HW_PARAM_PERIOD_BYTES, |
242 | AXG_FIFO_BURST); |
243 | if (ret) |
244 | return ret; |
245 | |
246 | ret = request_irq(irq: fifo->irq, handler: axg_fifo_pcm_irq_block, flags: 0, |
247 | name: dev_name(dev), dev: ss); |
248 | if (ret) |
249 | return ret; |
250 | |
251 | /* Enable pclk to access registers and clock the fifo ip */ |
252 | ret = clk_prepare_enable(clk: fifo->pclk); |
253 | if (ret) |
254 | goto free_irq; |
255 | |
256 | /* Setup status2 so it reports the memory pointer */ |
257 | regmap_update_bits(map: fifo->map, FIFO_CTRL1, |
258 | CTRL1_STATUS2_SEL, |
259 | FIELD_PREP(CTRL1_STATUS2_SEL, STATUS2_SEL_DDR_READ)); |
260 | |
261 | /* Make sure the dma is initially disabled */ |
262 | __dma_enable(fifo, enable: false); |
263 | |
264 | /* Disable irqs until params are ready */ |
265 | regmap_update_bits(map: fifo->map, FIFO_CTRL0, |
266 | CTRL0_INT_EN, val: 0); |
267 | |
268 | /* Clear any pending interrupt */ |
269 | axg_fifo_ack_irq(fifo, FIFO_INT_MASK); |
270 | |
271 | /* Take memory arbitror out of reset */ |
272 | ret = reset_control_deassert(rstc: fifo->arb); |
273 | if (ret) |
274 | goto free_clk; |
275 | |
276 | return 0; |
277 | |
278 | free_clk: |
279 | clk_disable_unprepare(clk: fifo->pclk); |
280 | free_irq: |
281 | free_irq(fifo->irq, ss); |
282 | return ret; |
283 | } |
284 | EXPORT_SYMBOL_GPL(axg_fifo_pcm_open); |
285 | |
286 | int axg_fifo_pcm_close(struct snd_soc_component *component, |
287 | struct snd_pcm_substream *ss) |
288 | { |
289 | struct axg_fifo *fifo = axg_fifo_data(ss); |
290 | int ret; |
291 | |
292 | /* Put the memory arbitror back in reset */ |
293 | ret = reset_control_assert(rstc: fifo->arb); |
294 | |
295 | /* Disable fifo ip and register access */ |
296 | clk_disable_unprepare(clk: fifo->pclk); |
297 | |
298 | /* remove IRQ */ |
299 | free_irq(fifo->irq, ss); |
300 | |
301 | return ret; |
302 | } |
303 | EXPORT_SYMBOL_GPL(axg_fifo_pcm_close); |
304 | |
305 | int axg_fifo_pcm_new(struct snd_soc_pcm_runtime *rtd, unsigned int type) |
306 | { |
307 | struct snd_card *card = rtd->card->snd_card; |
308 | size_t size = axg_fifo_hw.buffer_bytes_max; |
309 | |
310 | snd_pcm_set_managed_buffer(substream: rtd->pcm->streams[type].substream, |
311 | SNDRV_DMA_TYPE_DEV, data: card->dev, |
312 | size, max: size); |
313 | return 0; |
314 | } |
315 | EXPORT_SYMBOL_GPL(axg_fifo_pcm_new); |
316 | |
317 | static const struct regmap_config axg_fifo_regmap_cfg = { |
318 | .reg_bits = 32, |
319 | .val_bits = 32, |
320 | .reg_stride = 4, |
321 | .max_register = FIFO_CTRL2, |
322 | }; |
323 | |
324 | int axg_fifo_probe(struct platform_device *pdev) |
325 | { |
326 | struct device *dev = &pdev->dev; |
327 | const struct axg_fifo_match_data *data; |
328 | struct axg_fifo *fifo; |
329 | void __iomem *regs; |
330 | int ret; |
331 | |
332 | data = of_device_get_match_data(dev); |
333 | if (!data) { |
334 | dev_err(dev, "failed to match device\n" ); |
335 | return -ENODEV; |
336 | } |
337 | |
338 | fifo = devm_kzalloc(dev, size: sizeof(*fifo), GFP_KERNEL); |
339 | if (!fifo) |
340 | return -ENOMEM; |
341 | platform_set_drvdata(pdev, data: fifo); |
342 | |
343 | regs = devm_platform_ioremap_resource(pdev, index: 0); |
344 | if (IS_ERR(ptr: regs)) |
345 | return PTR_ERR(ptr: regs); |
346 | |
347 | fifo->map = devm_regmap_init_mmio(dev, regs, &axg_fifo_regmap_cfg); |
348 | if (IS_ERR(ptr: fifo->map)) { |
349 | dev_err(dev, "failed to init regmap: %ld\n" , |
350 | PTR_ERR(fifo->map)); |
351 | return PTR_ERR(ptr: fifo->map); |
352 | } |
353 | |
354 | fifo->pclk = devm_clk_get(dev, NULL); |
355 | if (IS_ERR(ptr: fifo->pclk)) |
356 | return dev_err_probe(dev, err: PTR_ERR(ptr: fifo->pclk), fmt: "failed to get pclk\n" ); |
357 | |
358 | fifo->arb = devm_reset_control_get_exclusive(dev, NULL); |
359 | if (IS_ERR(ptr: fifo->arb)) |
360 | return dev_err_probe(dev, err: PTR_ERR(ptr: fifo->arb), fmt: "failed to get arb reset\n" ); |
361 | |
362 | fifo->irq = of_irq_get(dev: dev->of_node, index: 0); |
363 | if (fifo->irq <= 0) { |
364 | dev_err(dev, "failed to get irq: %d\n" , fifo->irq); |
365 | return fifo->irq; |
366 | } |
367 | |
368 | fifo->field_threshold = |
369 | devm_regmap_field_alloc(dev, regmap: fifo->map, reg_field: data->field_threshold); |
370 | if (IS_ERR(ptr: fifo->field_threshold)) |
371 | return PTR_ERR(ptr: fifo->field_threshold); |
372 | |
373 | ret = of_property_read_u32(np: dev->of_node, propname: "amlogic,fifo-depth" , |
374 | out_value: &fifo->depth); |
375 | if (ret) { |
376 | /* Error out for anything but a missing property */ |
377 | if (ret != -EINVAL) |
378 | return ret; |
379 | /* |
380 | * If the property is missing, it might be because of an old |
381 | * DT. In such case, assume the smallest known fifo depth |
382 | */ |
383 | fifo->depth = 256; |
384 | dev_warn(dev, "fifo depth not found, assume %u bytes\n" , |
385 | fifo->depth); |
386 | } |
387 | |
388 | return devm_snd_soc_register_component(dev, component_driver: data->component_drv, |
389 | dai_drv: data->dai_drv, num_dai: 1); |
390 | } |
391 | EXPORT_SYMBOL_GPL(axg_fifo_probe); |
392 | |
393 | MODULE_DESCRIPTION("Amlogic AXG/G12A fifo driver" ); |
394 | MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>" ); |
395 | MODULE_LICENSE("GPL v2" ); |
396 | |