1 /*
2  * mtk-afe-fe-dais.c  --  Mediatek afe fe dai operator
3  *
4  * Copyright (c) 2016 MediaTek Inc.
5  * Author: Garlic Tseng <garlic.tseng@mediatek.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 and
9  * only version 2 as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  */
16 
17 #include <linux/module.h>
18 #include <linux/pm_runtime.h>
19 #include <linux/regmap.h>
20 #include <sound/soc.h>
21 #include "mtk-afe-platform-driver.h"
22 #include "mtk-afe-fe-dai.h"
23 #include "mtk-base-afe.h"
24 
25 #define AFE_BASE_END_OFFSET 8
26 
27 static int mtk_regmap_update_bits(struct regmap *map, int reg,
28 			   unsigned int mask,
29 			   unsigned int val)
30 {
31 	if (reg < 0)
32 		return 0;
33 	return regmap_update_bits(map, reg, mask, val);
34 }
35 
36 static int mtk_regmap_write(struct regmap *map, int reg, unsigned int val)
37 {
38 	if (reg < 0)
39 		return 0;
40 	return regmap_write(map, reg, val);
41 }
42 
43 int mtk_afe_fe_startup(struct snd_pcm_substream *substream,
44 		       struct snd_soc_dai *dai)
45 {
46 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
47 	struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
48 	struct snd_pcm_runtime *runtime = substream->runtime;
49 	int memif_num = rtd->cpu_dai->id;
50 	struct mtk_base_afe_memif *memif = &afe->memif[memif_num];
51 	const struct snd_pcm_hardware *mtk_afe_hardware = afe->mtk_afe_hardware;
52 	int ret;
53 
54 	memif->substream = substream;
55 
56 	snd_pcm_hw_constraint_step(substream->runtime, 0,
57 				   SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 16);
58 	/* enable agent */
59 	mtk_regmap_update_bits(afe->regmap, memif->data->agent_disable_reg,
60 			       1 << memif->data->agent_disable_shift,
61 			       0 << memif->data->agent_disable_shift);
62 
63 	snd_soc_set_runtime_hwparams(substream, mtk_afe_hardware);
64 
65 	/*
66 	 * Capture cannot use ping-pong buffer since hw_ptr at IRQ may be
67 	 * smaller than period_size due to AFE's internal buffer.
68 	 * This easily leads to overrun when avail_min is period_size.
69 	 * One more period can hold the possible unread buffer.
70 	 */
71 	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
72 		int periods_max = mtk_afe_hardware->periods_max;
73 
74 		ret = snd_pcm_hw_constraint_minmax(runtime,
75 						   SNDRV_PCM_HW_PARAM_PERIODS,
76 						   3, periods_max);
77 		if (ret < 0) {
78 			dev_err(afe->dev, "hw_constraint_minmax failed\n");
79 			return ret;
80 		}
81 	}
82 
83 	ret = snd_pcm_hw_constraint_integer(runtime,
84 					    SNDRV_PCM_HW_PARAM_PERIODS);
85 	if (ret < 0)
86 		dev_err(afe->dev, "snd_pcm_hw_constraint_integer failed\n");
87 
88 	/* dynamic allocate irq to memif */
89 	if (memif->irq_usage < 0) {
90 		int irq_id = mtk_dynamic_irq_acquire(afe);
91 
92 		if (irq_id != afe->irqs_size) {
93 			/* link */
94 			memif->irq_usage = irq_id;
95 		} else {
96 			dev_err(afe->dev, "%s() error: no more asys irq\n",
97 				__func__);
98 			ret = -EBUSY;
99 		}
100 	}
101 	return ret;
102 }
103 EXPORT_SYMBOL_GPL(mtk_afe_fe_startup);
104 
105 void mtk_afe_fe_shutdown(struct snd_pcm_substream *substream,
106 			 struct snd_soc_dai *dai)
107 {
108 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
109 	struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
110 	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
111 	int irq_id;
112 
113 	irq_id = memif->irq_usage;
114 
115 	mtk_regmap_update_bits(afe->regmap, memif->data->agent_disable_reg,
116 			       1 << memif->data->agent_disable_shift,
117 			       1 << memif->data->agent_disable_shift);
118 
119 	if (!memif->const_irq) {
120 		mtk_dynamic_irq_release(afe, irq_id);
121 		memif->irq_usage = -1;
122 		memif->substream = NULL;
123 	}
124 }
125 EXPORT_SYMBOL_GPL(mtk_afe_fe_shutdown);
126 
127 int mtk_afe_fe_hw_params(struct snd_pcm_substream *substream,
128 			 struct snd_pcm_hw_params *params,
129 			 struct snd_soc_dai *dai)
130 {
131 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
132 	struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
133 	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
134 	int msb_at_bit33 = 0;
135 	int ret, fs = 0;
136 
137 	ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
138 	if (ret < 0)
139 		return ret;
140 
141 	msb_at_bit33 = upper_32_bits(substream->runtime->dma_addr) ? 1 : 0;
142 	memif->phys_buf_addr = lower_32_bits(substream->runtime->dma_addr);
143 	memif->buffer_size = substream->runtime->dma_bytes;
144 
145 	/* start */
146 	mtk_regmap_write(afe->regmap, memif->data->reg_ofs_base,
147 			 memif->phys_buf_addr);
148 	/* end */
149 	mtk_regmap_write(afe->regmap,
150 			 memif->data->reg_ofs_base + AFE_BASE_END_OFFSET,
151 			 memif->phys_buf_addr + memif->buffer_size - 1);
152 
153 	/* set MSB to 33-bit */
154 	mtk_regmap_update_bits(afe->regmap, memif->data->msb_reg,
155 			       1 << memif->data->msb_shift,
156 			       msb_at_bit33 << memif->data->msb_shift);
157 
158 	/* set channel */
159 	if (memif->data->mono_shift >= 0) {
160 		unsigned int mono = (params_channels(params) == 1) ? 1 : 0;
161 
162 		mtk_regmap_update_bits(afe->regmap, memif->data->mono_reg,
163 				       1 << memif->data->mono_shift,
164 				       mono << memif->data->mono_shift);
165 	}
166 
167 	/* set rate */
168 	if (memif->data->fs_shift < 0)
169 		return 0;
170 
171 	fs = afe->memif_fs(substream, params_rate(params));
172 
173 	if (fs < 0)
174 		return -EINVAL;
175 
176 	mtk_regmap_update_bits(afe->regmap, memif->data->fs_reg,
177 			       memif->data->fs_maskbit << memif->data->fs_shift,
178 			       fs << memif->data->fs_shift);
179 
180 	return 0;
181 }
182 EXPORT_SYMBOL_GPL(mtk_afe_fe_hw_params);
183 
184 int mtk_afe_fe_hw_free(struct snd_pcm_substream *substream,
185 		       struct snd_soc_dai *dai)
186 {
187 	return snd_pcm_lib_free_pages(substream);
188 }
189 EXPORT_SYMBOL_GPL(mtk_afe_fe_hw_free);
190 
191 int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
192 		       struct snd_soc_dai *dai)
193 {
194 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
195 	struct snd_pcm_runtime * const runtime = substream->runtime;
196 	struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
197 	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
198 	struct mtk_base_afe_irq *irqs = &afe->irqs[memif->irq_usage];
199 	const struct mtk_base_irq_data *irq_data = irqs->irq_data;
200 	unsigned int counter = runtime->period_size;
201 	int fs;
202 
203 	dev_dbg(afe->dev, "%s %s cmd=%d\n", __func__, memif->data->name, cmd);
204 
205 	switch (cmd) {
206 	case SNDRV_PCM_TRIGGER_START:
207 	case SNDRV_PCM_TRIGGER_RESUME:
208 		if (memif->data->enable_shift >= 0)
209 			mtk_regmap_update_bits(afe->regmap,
210 					       memif->data->enable_reg,
211 					       1 << memif->data->enable_shift,
212 					       1 << memif->data->enable_shift);
213 
214 		/* set irq counter */
215 		mtk_regmap_update_bits(afe->regmap, irq_data->irq_cnt_reg,
216 				       irq_data->irq_cnt_maskbit
217 				       << irq_data->irq_cnt_shift,
218 				       counter << irq_data->irq_cnt_shift);
219 
220 		/* set irq fs */
221 		fs = afe->irq_fs(substream, runtime->rate);
222 
223 		if (fs < 0)
224 			return -EINVAL;
225 
226 		mtk_regmap_update_bits(afe->regmap, irq_data->irq_fs_reg,
227 				       irq_data->irq_fs_maskbit
228 				       << irq_data->irq_fs_shift,
229 				       fs << irq_data->irq_fs_shift);
230 
231 		/* enable interrupt */
232 		mtk_regmap_update_bits(afe->regmap, irq_data->irq_en_reg,
233 				       1 << irq_data->irq_en_shift,
234 				       1 << irq_data->irq_en_shift);
235 
236 		return 0;
237 	case SNDRV_PCM_TRIGGER_STOP:
238 	case SNDRV_PCM_TRIGGER_SUSPEND:
239 		mtk_regmap_update_bits(afe->regmap, memif->data->enable_reg,
240 				       1 << memif->data->enable_shift, 0);
241 		/* disable interrupt */
242 		mtk_regmap_update_bits(afe->regmap, irq_data->irq_en_reg,
243 				       1 << irq_data->irq_en_shift,
244 				       0 << irq_data->irq_en_shift);
245 		/* and clear pending IRQ */
246 		mtk_regmap_write(afe->regmap, irq_data->irq_clr_reg,
247 				 1 << irq_data->irq_clr_shift);
248 		return 0;
249 	default:
250 		return -EINVAL;
251 	}
252 }
253 EXPORT_SYMBOL_GPL(mtk_afe_fe_trigger);
254 
255 int mtk_afe_fe_prepare(struct snd_pcm_substream *substream,
256 		       struct snd_soc_dai *dai)
257 {
258 	struct snd_soc_pcm_runtime *rtd  = substream->private_data;
259 	struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
260 	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
261 	int hd_audio = 0;
262 
263 	/* set hd mode */
264 	switch (substream->runtime->format) {
265 	case SNDRV_PCM_FORMAT_S16_LE:
266 		hd_audio = 0;
267 		break;
268 	case SNDRV_PCM_FORMAT_S32_LE:
269 		hd_audio = 1;
270 		break;
271 	case SNDRV_PCM_FORMAT_S24_LE:
272 		hd_audio = 1;
273 		break;
274 	default:
275 		dev_err(afe->dev, "%s() error: unsupported format %d\n",
276 			__func__, substream->runtime->format);
277 		break;
278 	}
279 
280 	mtk_regmap_update_bits(afe->regmap, memif->data->hd_reg,
281 			       1 << memif->data->hd_shift,
282 			       hd_audio << memif->data->hd_shift);
283 
284 	return 0;
285 }
286 EXPORT_SYMBOL_GPL(mtk_afe_fe_prepare);
287 
288 const struct snd_soc_dai_ops mtk_afe_fe_ops = {
289 	.startup	= mtk_afe_fe_startup,
290 	.shutdown	= mtk_afe_fe_shutdown,
291 	.hw_params	= mtk_afe_fe_hw_params,
292 	.hw_free	= mtk_afe_fe_hw_free,
293 	.prepare	= mtk_afe_fe_prepare,
294 	.trigger	= mtk_afe_fe_trigger,
295 };
296 EXPORT_SYMBOL_GPL(mtk_afe_fe_ops);
297 
298 static DEFINE_MUTEX(irqs_lock);
299 int mtk_dynamic_irq_acquire(struct mtk_base_afe *afe)
300 {
301 	int i;
302 
303 	mutex_lock(&afe->irq_alloc_lock);
304 	for (i = 0; i < afe->irqs_size; ++i) {
305 		if (afe->irqs[i].irq_occupyed == 0) {
306 			afe->irqs[i].irq_occupyed = 1;
307 			mutex_unlock(&afe->irq_alloc_lock);
308 			return i;
309 		}
310 	}
311 	mutex_unlock(&afe->irq_alloc_lock);
312 	return afe->irqs_size;
313 }
314 EXPORT_SYMBOL_GPL(mtk_dynamic_irq_acquire);
315 
316 int mtk_dynamic_irq_release(struct mtk_base_afe *afe, int irq_id)
317 {
318 	mutex_lock(&afe->irq_alloc_lock);
319 	if (irq_id >= 0 && irq_id < afe->irqs_size) {
320 		afe->irqs[irq_id].irq_occupyed = 0;
321 		mutex_unlock(&afe->irq_alloc_lock);
322 		return 0;
323 	}
324 	mutex_unlock(&afe->irq_alloc_lock);
325 	return -EINVAL;
326 }
327 EXPORT_SYMBOL_GPL(mtk_dynamic_irq_release);
328 
329 int mtk_afe_dai_suspend(struct snd_soc_dai *dai)
330 {
331 	struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
332 	struct device *dev = afe->dev;
333 	struct regmap *regmap = afe->regmap;
334 	int i;
335 
336 	if (pm_runtime_status_suspended(dev) || afe->suspended)
337 		return 0;
338 
339 	if (!afe->reg_back_up)
340 		afe->reg_back_up =
341 			devm_kcalloc(dev, afe->reg_back_up_list_num,
342 				     sizeof(unsigned int), GFP_KERNEL);
343 
344 	for (i = 0; i < afe->reg_back_up_list_num; i++)
345 		regmap_read(regmap, afe->reg_back_up_list[i],
346 			    &afe->reg_back_up[i]);
347 
348 	afe->suspended = true;
349 	afe->runtime_suspend(dev);
350 	return 0;
351 }
352 EXPORT_SYMBOL_GPL(mtk_afe_dai_suspend);
353 
354 int mtk_afe_dai_resume(struct snd_soc_dai *dai)
355 {
356 	struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
357 	struct device *dev = afe->dev;
358 	struct regmap *regmap = afe->regmap;
359 	int i = 0;
360 
361 	if (pm_runtime_status_suspended(dev) || !afe->suspended)
362 		return 0;
363 
364 	afe->runtime_resume(dev);
365 
366 	if (!afe->reg_back_up)
367 		dev_dbg(dev, "%s no reg_backup\n", __func__);
368 
369 	for (i = 0; i < afe->reg_back_up_list_num; i++)
370 		mtk_regmap_write(regmap, afe->reg_back_up_list[i],
371 				 afe->reg_back_up[i]);
372 
373 	afe->suspended = false;
374 	return 0;
375 }
376 EXPORT_SYMBOL_GPL(mtk_afe_dai_resume);
377 
378 MODULE_DESCRIPTION("Mediatek simple fe dai operator");
379 MODULE_AUTHOR("Garlic Tseng <garlic.tseng@mediatek.com>");
380 MODULE_LICENSE("GPL v2");
381