1 // SPDX-License-Identifier: GPL-2.0
3 // Loongson I2S controller master mode dirver(platform device)
5 // Copyright (C) 2023-2024 Loongson Technology Corporation Limited
7 // Author: Yingkun Meng <mengyingkun@loongson.cn>
8 // Binbin Zhou <zhoubinbin@loongson.cn>
10 #include <linux/clk.h>
11 #include <linux/dma-mapping.h>
12 #include <linux/module.h>
13 #include <linux/of_dma.h>
14 #include <linux/platform_device.h>
15 #include <linux/pm_runtime.h>
16 #include <sound/dmaengine_pcm.h>
17 #include <sound/pcm.h>
18 #include <sound/pcm_params.h>
19 #include <sound/soc.h>
21 #include "loongson_i2s.h"
23 #define LOONGSON_I2S_RX_DMA_OFFSET 21
24 #define LOONGSON_I2S_TX_DMA_OFFSET 18
26 #define LOONGSON_DMA0_CONF 0x0
27 #define LOONGSON_DMA1_CONF 0x1
28 #define LOONGSON_DMA2_CONF 0x2
29 #define LOONGSON_DMA3_CONF 0x3
30 #define LOONGSON_DMA4_CONF 0x4
32 /* periods_max = PAGE_SIZE / sizeof(struct ls_dma_chan_reg) */
33 static const struct snd_pcm_hardware loongson_pcm_hardware
= {
34 .info
= SNDRV_PCM_INFO_MMAP
|
35 SNDRV_PCM_INFO_INTERLEAVED
|
36 SNDRV_PCM_INFO_MMAP_VALID
|
37 SNDRV_PCM_INFO_RESUME
|
39 .formats
= SNDRV_PCM_FMTBIT_S16_LE
|
40 SNDRV_PCM_FMTBIT_S20_3LE
|
41 SNDRV_PCM_FMTBIT_S24_LE
,
42 .period_bytes_min
= 128,
43 .period_bytes_max
= 128 * 1024,
46 .buffer_bytes_max
= 1024 * 1024,
49 static const struct snd_dmaengine_pcm_config loongson_dmaengine_pcm_config
= {
50 .pcm_hardware
= &loongson_pcm_hardware
,
51 .prepare_slave_config
= snd_dmaengine_pcm_prepare_slave_config
,
52 .prealloc_buffer_size
= 128 * 1024,
55 static int loongson_pcm_open(struct snd_soc_component
*component
,
56 struct snd_pcm_substream
*substream
)
58 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
60 if (substream
->pcm
->device
& 1) {
61 runtime
->hw
.info
&= ~SNDRV_PCM_INFO_INTERLEAVED
;
62 runtime
->hw
.info
|= SNDRV_PCM_INFO_NONINTERLEAVED
;
65 if (substream
->pcm
->device
& 2)
66 runtime
->hw
.info
&= ~(SNDRV_PCM_INFO_MMAP
|
67 SNDRV_PCM_INFO_MMAP_VALID
);
69 * For mysterious reasons (and despite what the manual says)
70 * playback samples are lost if the DMA count is not a multiple
71 * of the DMA burst size. Let's add a rule to enforce that.
73 snd_pcm_hw_constraint_step(runtime
, 0,
74 SNDRV_PCM_HW_PARAM_PERIOD_BYTES
, 128);
75 snd_pcm_hw_constraint_step(runtime
, 0,
76 SNDRV_PCM_HW_PARAM_BUFFER_BYTES
, 128);
77 snd_pcm_hw_constraint_integer(substream
->runtime
,
78 SNDRV_PCM_HW_PARAM_PERIODS
);
83 static const struct snd_soc_component_driver loongson_i2s_component_driver
= {
84 .name
= LS_I2S_DRVNAME
,
85 .open
= loongson_pcm_open
,
88 static const struct regmap_config loongson_i2s_regmap_config
= {
93 .cache_type
= REGCACHE_FLAT
,
96 static int loongson_i2s_apbdma_config(struct platform_device
*pdev
)
101 regs
= devm_platform_ioremap_resource(pdev
, 1);
103 return PTR_ERR(regs
);
106 val
|= LOONGSON_DMA2_CONF
<< LOONGSON_I2S_TX_DMA_OFFSET
;
107 val
|= LOONGSON_DMA3_CONF
<< LOONGSON_I2S_RX_DMA_OFFSET
;
113 static int loongson_i2s_plat_probe(struct platform_device
*pdev
)
115 struct device
*dev
= &pdev
->dev
;
116 struct loongson_i2s
*i2s
;
117 struct resource
*res
;
121 i2s
= devm_kzalloc(dev
, sizeof(*i2s
), GFP_KERNEL
);
125 ret
= loongson_i2s_apbdma_config(pdev
);
129 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
130 i2s
->reg_base
= devm_ioremap_resource(&pdev
->dev
, res
);
131 if (IS_ERR(i2s
->reg_base
))
132 return dev_err_probe(dev
, PTR_ERR(i2s
->reg_base
),
133 "devm_ioremap_resource failed\n");
135 i2s
->regmap
= devm_regmap_init_mmio(dev
, i2s
->reg_base
,
136 &loongson_i2s_regmap_config
);
137 if (IS_ERR(i2s
->regmap
))
138 return dev_err_probe(dev
, PTR_ERR(i2s
->regmap
),
139 "devm_regmap_init_mmio failed\n");
141 i2s
->playback_dma_data
.addr
= res
->start
+ LS_I2S_TX_DATA
;
142 i2s
->playback_dma_data
.addr_width
= DMA_SLAVE_BUSWIDTH_4_BYTES
;
143 i2s
->playback_dma_data
.maxburst
= 4;
145 i2s
->capture_dma_data
.addr
= res
->start
+ LS_I2S_RX_DATA
;
146 i2s
->capture_dma_data
.addr_width
= DMA_SLAVE_BUSWIDTH_4_BYTES
;
147 i2s
->capture_dma_data
.maxburst
= 4;
149 i2s_clk
= devm_clk_get_enabled(dev
, NULL
);
151 return dev_err_probe(dev
, PTR_ERR(i2s_clk
), "clock property invalid\n");
152 i2s
->clk_rate
= clk_get_rate(i2s_clk
);
154 dma_set_mask_and_coherent(dev
, DMA_BIT_MASK(64));
155 dev_set_name(dev
, LS_I2S_DRVNAME
);
156 dev_set_drvdata(dev
, i2s
);
158 ret
= devm_snd_soc_register_component(dev
, &loongson_i2s_component_driver
,
159 &loongson_i2s_dai
, 1);
161 return dev_err_probe(dev
, ret
, "failed to register DAI\n");
163 return devm_snd_dmaengine_pcm_register(dev
, &loongson_dmaengine_pcm_config
,
164 SND_DMAENGINE_PCM_FLAG_COMPAT
);
167 static const struct of_device_id loongson_i2s_ids
[] = {
168 { .compatible
= "loongson,ls2k1000-i2s" },
171 MODULE_DEVICE_TABLE(of
, loongson_i2s_ids
);
173 static struct platform_driver loongson_i2s_driver
= {
174 .probe
= loongson_i2s_plat_probe
,
176 .name
= "loongson-i2s-plat",
177 .pm
= pm_sleep_ptr(&loongson_i2s_pm
),
178 .of_match_table
= loongson_i2s_ids
,
181 module_platform_driver(loongson_i2s_driver
);
183 MODULE_DESCRIPTION("Loongson I2S Master Mode ASoC Driver");
184 MODULE_AUTHOR("Loongson Technology Corporation Limited");
185 MODULE_LICENSE("GPL");