1 // SPDX-License-Identifier: GPL-2.0
3 // Loongson ALSA SoC Platform (DMA) driver
5 // Copyright (C) 2023 Loongson Technology Corporation Limited
6 // Author: Yingkun Meng <mengyingkun@loongson.cn>
9 #include <linux/module.h>
10 #include <linux/io-64-nonatomic-lo-hi.h>
11 #include <linux/delay.h>
12 #include <linux/pm_runtime.h>
13 #include <linux/dma-mapping.h>
14 #include <sound/soc.h>
15 #include <sound/pcm.h>
16 #include <sound/pcm_params.h>
17 #include "loongson_i2s.h"
19 /* DMA dma_order Register */
20 #define DMA_ORDER_STOP BIT(4) /* DMA stop */
21 #define DMA_ORDER_START BIT(3) /* DMA start */
22 #define DMA_ORDER_ASK_VALID BIT(2) /* DMA ask valid flag */
23 #define DMA_ORDER_AXI_UNCO BIT(1) /* Uncache access */
24 #define DMA_ORDER_ADDR_64 BIT(0) /* 64bits address support */
26 #define DMA_ORDER_ASK_MASK (~0x1fUL) /* Ask addr mask */
27 #define DMA_ORDER_CTRL_MASK (0x0fUL) /* Control mask */
30 * DMA registers descriptor.
32 struct loongson_dma_desc
{
33 u32 order
; /* Next descriptor address register */
34 u32 saddr
; /* Source address register */
35 u32 daddr
; /* Device address register */
36 u32 length
; /* Total length register */
37 u32 step_length
; /* Memory stride register */
38 u32 step_times
; /* Repeat time register */
39 u32 cmd
; /* Command register */
40 u32 stats
; /* Status register */
41 u32 order_hi
; /* Next descriptor high address register */
42 u32 saddr_hi
; /* High source address register */
43 u32 res
[6]; /* Reserved */
46 struct loongson_runtime_data
{
47 struct loongson_dma_data
*dma_data
;
49 struct loongson_dma_desc
*dma_desc_arr
;
50 dma_addr_t dma_desc_arr_phy
;
51 int dma_desc_arr_size
;
53 struct loongson_dma_desc
*dma_pos_desc
;
54 dma_addr_t dma_pos_desc_phy
;
57 static const struct snd_pcm_hardware ls_pcm_hardware
= {
58 .info
= SNDRV_PCM_INFO_MMAP
|
59 SNDRV_PCM_INFO_INTERLEAVED
|
60 SNDRV_PCM_INFO_MMAP_VALID
|
61 SNDRV_PCM_INFO_RESUME
|
63 .formats
= (SNDRV_PCM_FMTBIT_S8
|
64 SNDRV_PCM_FMTBIT_S16_LE
|
65 SNDRV_PCM_FMTBIT_S20_3LE
|
66 SNDRV_PCM_FMTBIT_S24_LE
),
67 .period_bytes_min
= 128,
68 .period_bytes_max
= 128 * 1024,
70 .periods_max
= PAGE_SIZE
/ sizeof(struct loongson_dma_desc
),
71 .buffer_bytes_max
= 1024 * 1024,
75 loongson_dma_desc
*dma_desc_save(struct loongson_runtime_data
*prtd
)
77 void __iomem
*order_reg
= prtd
->dma_data
->order_addr
;
80 val
= (u64
)prtd
->dma_pos_desc_phy
& DMA_ORDER_ASK_MASK
;
81 val
|= (readq(order_reg
) & DMA_ORDER_CTRL_MASK
);
82 val
|= DMA_ORDER_ASK_VALID
;
83 writeq(val
, order_reg
);
85 while (readl(order_reg
) & DMA_ORDER_ASK_VALID
)
88 return prtd
->dma_pos_desc
;
91 static int loongson_pcm_trigger(struct snd_soc_component
*component
,
92 struct snd_pcm_substream
*substream
, int cmd
)
94 struct loongson_runtime_data
*prtd
= substream
->runtime
->private_data
;
95 struct device
*dev
= substream
->pcm
->card
->dev
;
96 void __iomem
*order_reg
= prtd
->dma_data
->order_addr
;
100 case SNDRV_PCM_TRIGGER_START
:
101 case SNDRV_PCM_TRIGGER_RESUME
:
102 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
103 val
= prtd
->dma_pos_desc_phy
& DMA_ORDER_ASK_MASK
;
104 if (dev
->coherent_dma_mask
== DMA_BIT_MASK(64))
105 val
|= DMA_ORDER_ADDR_64
;
107 val
&= ~DMA_ORDER_ADDR_64
;
108 val
|= (readq(order_reg
) & DMA_ORDER_CTRL_MASK
);
109 val
|= DMA_ORDER_START
;
110 writeq(val
, order_reg
);
112 while ((readl(order_reg
) & DMA_ORDER_START
))
115 case SNDRV_PCM_TRIGGER_STOP
:
116 case SNDRV_PCM_TRIGGER_SUSPEND
:
117 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
121 val
= readq(order_reg
) | DMA_ORDER_STOP
;
122 writeq(val
, order_reg
);
127 dev_err(dev
, "Invalid pcm trigger operation\n");
134 static int loongson_pcm_hw_params(struct snd_soc_component
*component
,
135 struct snd_pcm_substream
*substream
,
136 struct snd_pcm_hw_params
*params
)
138 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
139 struct device
*dev
= substream
->pcm
->card
->dev
;
140 struct loongson_runtime_data
*prtd
= runtime
->private_data
;
141 size_t buf_len
= params_buffer_bytes(params
);
142 size_t period_len
= params_period_bytes(params
);
143 dma_addr_t order_addr
, mem_addr
;
144 struct loongson_dma_desc
*desc
;
148 if (buf_len
% period_len
) {
149 dev_err(dev
, "buf len not multiply of period len\n");
153 num_periods
= buf_len
/ period_len
;
154 if (!num_periods
|| num_periods
> prtd
->dma_desc_arr_size
) {
155 dev_err(dev
, "dma data too small or too big\n");
159 snd_pcm_set_runtime_buffer(substream
, &substream
->dma_buffer
);
160 runtime
->dma_bytes
= buf_len
;
162 /* initialize dma descriptor array */
163 mem_addr
= runtime
->dma_addr
;
164 order_addr
= prtd
->dma_desc_arr_phy
;
165 for (i
= 0; i
< num_periods
; i
++) {
166 desc
= &prtd
->dma_desc_arr
[i
];
168 /* next descriptor physical address */
169 order_addr
+= sizeof(*desc
);
170 desc
->order
= lower_32_bits(order_addr
| BIT(0));
171 desc
->order_hi
= upper_32_bits(order_addr
);
173 desc
->saddr
= lower_32_bits(mem_addr
);
174 desc
->saddr_hi
= upper_32_bits(mem_addr
);
175 desc
->daddr
= prtd
->dma_data
->dev_addr
;
178 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
179 desc
->cmd
|= BIT(12);
181 desc
->length
= period_len
>> 2;
182 desc
->step_length
= 0;
183 desc
->step_times
= 1;
185 mem_addr
+= period_len
;
187 desc
= &prtd
->dma_desc_arr
[num_periods
- 1];
188 desc
->order
= lower_32_bits(prtd
->dma_desc_arr_phy
| BIT(0));
189 desc
->order_hi
= upper_32_bits(prtd
->dma_desc_arr_phy
);
191 /* init position descriptor */
192 *prtd
->dma_pos_desc
= *prtd
->dma_desc_arr
;
197 static snd_pcm_uframes_t
198 loongson_pcm_pointer(struct snd_soc_component
*component
,
199 struct snd_pcm_substream
*substream
)
201 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
202 struct loongson_runtime_data
*prtd
= runtime
->private_data
;
203 struct loongson_dma_desc
*desc
;
207 desc
= dma_desc_save(prtd
);
208 addr
= ((u64
)desc
->saddr_hi
<< 32) | desc
->saddr
;
210 x
= bytes_to_frames(runtime
, addr
- runtime
->dma_addr
);
211 if (x
== runtime
->buffer_size
)
216 static irqreturn_t
loongson_pcm_dma_irq(int irq
, void *devid
)
218 struct snd_pcm_substream
*substream
= devid
;
220 snd_pcm_period_elapsed(substream
);
224 static int loongson_pcm_open(struct snd_soc_component
*component
,
225 struct snd_pcm_substream
*substream
)
227 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
228 struct snd_soc_pcm_runtime
*rtd
= snd_soc_substream_to_rtd(substream
);
229 struct snd_card
*card
= substream
->pcm
->card
;
230 struct loongson_runtime_data
*prtd
;
231 struct loongson_dma_data
*dma_data
;
234 * For mysterious reasons (and despite what the manual says)
235 * playback samples are lost if the DMA count is not a multiple
236 * of the DMA burst size. Let's add a rule to enforce that.
238 snd_pcm_hw_constraint_step(runtime
, 0,
239 SNDRV_PCM_HW_PARAM_PERIOD_BYTES
, 128);
240 snd_pcm_hw_constraint_step(runtime
, 0,
241 SNDRV_PCM_HW_PARAM_BUFFER_BYTES
, 128);
242 snd_pcm_hw_constraint_integer(substream
->runtime
,
243 SNDRV_PCM_HW_PARAM_PERIODS
);
244 snd_soc_set_runtime_hwparams(substream
, &ls_pcm_hardware
);
246 prtd
= kzalloc(sizeof(*prtd
), GFP_KERNEL
);
250 prtd
->dma_desc_arr
= dma_alloc_coherent(card
->dev
, PAGE_SIZE
,
251 &prtd
->dma_desc_arr_phy
,
253 if (!prtd
->dma_desc_arr
)
256 prtd
->dma_desc_arr_size
= PAGE_SIZE
/ sizeof(*prtd
->dma_desc_arr
);
258 prtd
->dma_pos_desc
= dma_alloc_coherent(card
->dev
,
259 sizeof(*prtd
->dma_pos_desc
),
260 &prtd
->dma_pos_desc_phy
,
262 if (!prtd
->dma_pos_desc
)
265 dma_data
= snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd
, 0), substream
);
266 prtd
->dma_data
= dma_data
;
268 substream
->runtime
->private_data
= prtd
;
272 dma_free_coherent(card
->dev
, PAGE_SIZE
, prtd
->dma_desc_arr
,
273 prtd
->dma_desc_arr_phy
);
280 static int loongson_pcm_close(struct snd_soc_component
*component
,
281 struct snd_pcm_substream
*substream
)
283 struct snd_card
*card
= substream
->pcm
->card
;
284 struct loongson_runtime_data
*prtd
= substream
->runtime
->private_data
;
286 dma_free_coherent(card
->dev
, PAGE_SIZE
, prtd
->dma_desc_arr
,
287 prtd
->dma_desc_arr_phy
);
289 dma_free_coherent(card
->dev
, sizeof(*prtd
->dma_pos_desc
),
290 prtd
->dma_pos_desc
, prtd
->dma_pos_desc_phy
);
296 static int loongson_pcm_mmap(struct snd_soc_component
*component
,
297 struct snd_pcm_substream
*substream
,
298 struct vm_area_struct
*vma
)
300 return remap_pfn_range(vma
, vma
->vm_start
,
301 substream
->dma_buffer
.addr
>> PAGE_SHIFT
,
302 vma
->vm_end
- vma
->vm_start
, vma
->vm_page_prot
);
305 static int loongson_pcm_new(struct snd_soc_component
*component
,
306 struct snd_soc_pcm_runtime
*rtd
)
308 struct snd_card
*card
= rtd
->card
->snd_card
;
309 struct snd_pcm_substream
*substream
;
310 struct loongson_dma_data
*dma_data
;
314 for_each_pcm_streams(i
) {
315 substream
= rtd
->pcm
->streams
[i
].substream
;
319 dma_data
= snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd
, 0),
321 ret
= devm_request_irq(card
->dev
, dma_data
->irq
,
322 loongson_pcm_dma_irq
,
323 IRQF_TRIGGER_HIGH
, LS_I2S_DRVNAME
,
326 dev_err(card
->dev
, "request irq for DMA failed\n");
331 return snd_pcm_set_fixed_buffer_all(rtd
->pcm
, SNDRV_DMA_TYPE_DEV
,
333 ls_pcm_hardware
.buffer_bytes_max
);
336 const struct snd_soc_component_driver loongson_i2s_component
= {
337 .name
= LS_I2S_DRVNAME
,
338 .open
= loongson_pcm_open
,
339 .close
= loongson_pcm_close
,
340 .hw_params
= loongson_pcm_hw_params
,
341 .trigger
= loongson_pcm_trigger
,
342 .pointer
= loongson_pcm_pointer
,
343 .mmap
= loongson_pcm_mmap
,
344 .pcm_construct
= loongson_pcm_new
,