1 // SPDX-License-Identifier: GPL-2.0
2 //
3 // Common functions for loongson I2S controller driver
4 //
5 // Copyright (C) 2023 Loongson Technology Corporation Limited.
6 // Author: Yingkun Meng <mengyingkun@loongson.cn>
7 //
8 
9 #include <linux/module.h>
10 #include <linux/platform_device.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 <linux/regmap.h>
16 #include <sound/pcm_params.h>
17 #include "loongson_i2s.h"
18 
19 #define LOONGSON_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
20 			SNDRV_PCM_FMTBIT_S16_LE | \
21 			SNDRV_PCM_FMTBIT_S20_3LE | \
22 			SNDRV_PCM_FMTBIT_S24_LE)
23 
loongson_i2s_trigger(struct snd_pcm_substream * substream,int cmd,struct snd_soc_dai * dai)24 static int loongson_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
25 				struct snd_soc_dai *dai)
26 {
27 	struct loongson_i2s *i2s = snd_soc_dai_get_drvdata(dai);
28 	int ret = 0;
29 
30 	switch (cmd) {
31 	case SNDRV_PCM_TRIGGER_START:
32 	case SNDRV_PCM_TRIGGER_RESUME:
33 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
34 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
35 			regmap_update_bits(i2s->regmap, LS_I2S_CTRL,
36 					   I2S_CTRL_TX_EN | I2S_CTRL_TX_DMA_EN,
37 					   I2S_CTRL_TX_EN | I2S_CTRL_TX_DMA_EN);
38 		else
39 			regmap_update_bits(i2s->regmap, LS_I2S_CTRL,
40 					   I2S_CTRL_RX_EN | I2S_CTRL_RX_DMA_EN,
41 					   I2S_CTRL_RX_EN | I2S_CTRL_RX_DMA_EN);
42 		break;
43 	case SNDRV_PCM_TRIGGER_STOP:
44 	case SNDRV_PCM_TRIGGER_SUSPEND:
45 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
46 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
47 			regmap_update_bits(i2s->regmap, LS_I2S_CTRL,
48 					I2S_CTRL_TX_EN | I2S_CTRL_TX_DMA_EN, 0);
49 		else
50 			regmap_update_bits(i2s->regmap, LS_I2S_CTRL,
51 					I2S_CTRL_RX_EN | I2S_CTRL_RX_DMA_EN, 0);
52 		break;
53 	default:
54 		ret = -EINVAL;
55 	}
56 
57 	return ret;
58 }
59 
loongson_i2s_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)60 static int loongson_i2s_hw_params(struct snd_pcm_substream *substream,
61 				  struct snd_pcm_hw_params *params,
62 				  struct snd_soc_dai *dai)
63 {
64 	struct loongson_i2s *i2s = snd_soc_dai_get_drvdata(dai);
65 	u32 clk_rate = i2s->clk_rate;
66 	u32 sysclk = i2s->sysclk;
67 	u32 bits = params_width(params);
68 	u32 chans = params_channels(params);
69 	u32 fs = params_rate(params);
70 	u32 bclk_ratio, mclk_ratio;
71 	u32 mclk_ratio_frac;
72 	u32 val = 0;
73 
74 	switch (i2s->rev_id) {
75 	case 0:
76 		bclk_ratio = DIV_ROUND_CLOSEST(clk_rate,
77 					       (bits * chans * fs * 2)) - 1;
78 		mclk_ratio = DIV_ROUND_CLOSEST(clk_rate, (sysclk * 2)) - 1;
79 
80 		/* According to 2k1000LA user manual, set bits == depth */
81 		val |= (bits << 24);
82 		val |= (bits << 16);
83 		val |= (bclk_ratio << 8);
84 		val |= mclk_ratio;
85 		regmap_write(i2s->regmap, LS_I2S_CFG, val);
86 
87 		break;
88 	case 1:
89 		bclk_ratio = DIV_ROUND_CLOSEST(sysclk,
90 					       (bits * chans * fs * 2)) - 1;
91 		mclk_ratio = clk_rate / sysclk;
92 		mclk_ratio_frac = DIV_ROUND_CLOSEST_ULL(((u64)clk_rate << 16),
93 						    sysclk) - (mclk_ratio << 16);
94 
95 		regmap_read(i2s->regmap, LS_I2S_CFG, &val);
96 		val |= (bits << 24);
97 		val |= (bclk_ratio << 8);
98 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
99 			val |= (bits << 16);
100 		else
101 			val |= bits;
102 		regmap_write(i2s->regmap, LS_I2S_CFG, val);
103 
104 		val = (mclk_ratio_frac << 16) | mclk_ratio;
105 		regmap_write(i2s->regmap, LS_I2S_CFG1, val);
106 
107 		break;
108 	default:
109 		dev_err(i2s->dev, "I2S revision invalid\n");
110 		return -EINVAL;
111 	}
112 
113 	return 0;
114 }
115 
loongson_i2s_set_dai_sysclk(struct snd_soc_dai * dai,int clk_id,unsigned int freq,int dir)116 static int loongson_i2s_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
117 				       unsigned int freq, int dir)
118 {
119 	struct loongson_i2s *i2s = snd_soc_dai_get_drvdata(dai);
120 
121 	i2s->sysclk = freq;
122 
123 	return 0;
124 }
125 
loongson_i2s_set_fmt(struct snd_soc_dai * dai,unsigned int fmt)126 static int loongson_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
127 {
128 	struct loongson_i2s *i2s = snd_soc_dai_get_drvdata(dai);
129 	u32 val;
130 	int ret;
131 
132 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
133 	case SND_SOC_DAIFMT_I2S:
134 		break;
135 	case SND_SOC_DAIFMT_RIGHT_J:
136 		regmap_update_bits(i2s->regmap, LS_I2S_CTRL, I2S_CTRL_MSB,
137 				   I2S_CTRL_MSB);
138 		break;
139 	default:
140 		return -EINVAL;
141 	}
142 
143 
144 	switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
145 	case SND_SOC_DAIFMT_BC_FC:
146 		break;
147 	case SND_SOC_DAIFMT_BP_FC:
148 		/* Enable master mode */
149 		regmap_update_bits(i2s->regmap, LS_I2S_CTRL, I2S_CTRL_MASTER,
150 				   I2S_CTRL_MASTER);
151 		if (i2s->rev_id == 1) {
152 			ret = regmap_read_poll_timeout_atomic(i2s->regmap,
153 						LS_I2S_CTRL, val,
154 						val & I2S_CTRL_CLK_READY,
155 						10, 500000);
156 			if (ret < 0)
157 				dev_warn(dai->dev, "wait BCLK ready timeout\n");
158 		}
159 		break;
160 	case SND_SOC_DAIFMT_BC_FP:
161 		/* Enable MCLK */
162 		if (i2s->rev_id == 1) {
163 			regmap_update_bits(i2s->regmap, LS_I2S_CTRL,
164 					   I2S_CTRL_MCLK_EN,
165 					   I2S_CTRL_MCLK_EN);
166 			ret = regmap_read_poll_timeout_atomic(i2s->regmap,
167 						LS_I2S_CTRL, val,
168 						val & I2S_CTRL_MCLK_READY,
169 						10, 500000);
170 			if (ret < 0)
171 				dev_warn(dai->dev, "wait MCLK ready timeout\n");
172 		}
173 		break;
174 	case SND_SOC_DAIFMT_BP_FP:
175 		/* Enable MCLK */
176 		if (i2s->rev_id == 1) {
177 			regmap_update_bits(i2s->regmap, LS_I2S_CTRL,
178 					   I2S_CTRL_MCLK_EN,
179 					   I2S_CTRL_MCLK_EN);
180 			ret = regmap_read_poll_timeout_atomic(i2s->regmap,
181 						LS_I2S_CTRL, val,
182 						val & I2S_CTRL_MCLK_READY,
183 						10, 500000);
184 			if (ret < 0)
185 				dev_warn(dai->dev, "wait MCLK ready timeout\n");
186 		}
187 
188 		/* Enable master mode */
189 		regmap_update_bits(i2s->regmap, LS_I2S_CTRL, I2S_CTRL_MASTER,
190 				   I2S_CTRL_MASTER);
191 		if (i2s->rev_id == 1) {
192 			ret = regmap_read_poll_timeout_atomic(i2s->regmap,
193 						LS_I2S_CTRL, val,
194 						val & I2S_CTRL_CLK_READY,
195 						10, 500000);
196 			if (ret < 0)
197 				dev_warn(dai->dev, "wait BCLK ready timeout\n");
198 		}
199 		break;
200 	default:
201 		return -EINVAL;
202 	}
203 
204 	return 0;
205 }
206 
loongson_i2s_dai_probe(struct snd_soc_dai * cpu_dai)207 static int loongson_i2s_dai_probe(struct snd_soc_dai *cpu_dai)
208 {
209 	struct loongson_i2s *i2s = dev_get_drvdata(cpu_dai->dev);
210 
211 	snd_soc_dai_init_dma_data(cpu_dai, &i2s->playback_dma_data,
212 				  &i2s->capture_dma_data);
213 	snd_soc_dai_set_drvdata(cpu_dai, i2s);
214 
215 	return 0;
216 }
217 
218 static const struct snd_soc_dai_ops loongson_i2s_dai_ops = {
219 	.probe		= loongson_i2s_dai_probe,
220 	.trigger	= loongson_i2s_trigger,
221 	.hw_params	= loongson_i2s_hw_params,
222 	.set_sysclk	= loongson_i2s_set_dai_sysclk,
223 	.set_fmt	= loongson_i2s_set_fmt,
224 };
225 
226 struct snd_soc_dai_driver loongson_i2s_dai = {
227 	.name = "loongson-i2s",
228 	.playback = {
229 		.stream_name = "CPU-Playback",
230 		.channels_min = 1,
231 		.channels_max = 2,
232 		.rates = SNDRV_PCM_RATE_8000_96000,
233 		.formats = LOONGSON_I2S_FORMATS,
234 	},
235 	.capture = {
236 		.stream_name = "CPU-Capture",
237 		.channels_min = 1,
238 		.channels_max = 2,
239 		.rates = SNDRV_PCM_RATE_8000_96000,
240 		.formats = LOONGSON_I2S_FORMATS,
241 	},
242 	.ops = &loongson_i2s_dai_ops,
243 	.symmetric_rate = 1,
244 };
245 
i2s_suspend(struct device * dev)246 static int i2s_suspend(struct device *dev)
247 {
248 	struct loongson_i2s *i2s = dev_get_drvdata(dev);
249 
250 	regcache_cache_only(i2s->regmap, true);
251 
252 	return 0;
253 }
254 
i2s_resume(struct device * dev)255 static int i2s_resume(struct device *dev)
256 {
257 	struct loongson_i2s *i2s = dev_get_drvdata(dev);
258 	int ret;
259 
260 	regcache_cache_only(i2s->regmap, false);
261 	regcache_mark_dirty(i2s->regmap);
262 	ret = regcache_sync(i2s->regmap);
263 
264 	return ret;
265 }
266 
267 const struct dev_pm_ops loongson_i2s_pm = {
268 	SYSTEM_SLEEP_PM_OPS(i2s_suspend, i2s_resume)
269 };
270