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