This source file includes following definitions.
- omap_mcbsp_st_write
- omap_mcbsp_st_read
- omap_mcbsp_st_on
- omap_mcbsp_st_off
- omap_mcbsp_st_fir_write
- omap_mcbsp_st_chgain
- omap_mcbsp_st_set_chgain
- omap_mcbsp_st_get_chgain
- omap_mcbsp_st_enable
- omap_mcbsp_st_disable
- omap_mcbsp_st_is_enabled
- st_taps_show
- st_taps_store
- omap_mcbsp_st_start
- omap_mcbsp_st_stop
- omap_mcbsp_st_init
- omap_mcbsp_st_cleanup
- omap_mcbsp_st_info_volsw
- OMAP_MCBSP_ST_CHANNEL_VOLUME
- omap_mcbsp_st_get_mode
- omap_mcbsp_st_add_controls
1
2
3
4
5
6
7
8
9
10
11
12 #include <linux/module.h>
13 #include <linux/init.h>
14 #include <linux/device.h>
15 #include <linux/platform_device.h>
16 #include <linux/interrupt.h>
17 #include <linux/err.h>
18 #include <linux/clk.h>
19 #include <linux/delay.h>
20 #include <linux/io.h>
21 #include <linux/slab.h>
22 #include <linux/pm_runtime.h>
23
24 #include "omap-mcbsp.h"
25 #include "omap-mcbsp-priv.h"
26
27
28 #define OMAP_ST_REG_REV 0x00
29 #define OMAP_ST_REG_SYSCONFIG 0x10
30 #define OMAP_ST_REG_IRQSTATUS 0x18
31 #define OMAP_ST_REG_IRQENABLE 0x1C
32 #define OMAP_ST_REG_SGAINCR 0x24
33 #define OMAP_ST_REG_SFIRCR 0x28
34 #define OMAP_ST_REG_SSELCR 0x2C
35
36
37 #define SIDETONEEN BIT(10)
38
39
40 #define ST_AUTOIDLE BIT(0)
41
42
43 #define ST_CH0GAIN(value) ((value) & 0xffff)
44 #define ST_CH1GAIN(value) (((value) & 0xffff) << 16)
45
46
47 #define ST_FIRCOEFF(value) ((value) & 0xffff)
48
49
50 #define ST_SIDETONEEN BIT(0)
51 #define ST_COEFFWREN BIT(1)
52 #define ST_COEFFWRDONE BIT(2)
53
54 struct omap_mcbsp_st_data {
55 void __iomem *io_base_st;
56 struct clk *mcbsp_iclk;
57 bool running;
58 bool enabled;
59 s16 taps[128];
60 int nr_taps;
61 s16 ch0gain;
62 s16 ch1gain;
63 };
64
65 static void omap_mcbsp_st_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val)
66 {
67 writel_relaxed(val, mcbsp->st_data->io_base_st + reg);
68 }
69
70 static int omap_mcbsp_st_read(struct omap_mcbsp *mcbsp, u16 reg)
71 {
72 return readl_relaxed(mcbsp->st_data->io_base_st + reg);
73 }
74
75 #define MCBSP_ST_READ(mcbsp, reg) omap_mcbsp_st_read(mcbsp, OMAP_ST_REG_##reg)
76 #define MCBSP_ST_WRITE(mcbsp, reg, val) \
77 omap_mcbsp_st_write(mcbsp, OMAP_ST_REG_##reg, val)
78
79 static void omap_mcbsp_st_on(struct omap_mcbsp *mcbsp)
80 {
81 unsigned int w;
82
83 if (mcbsp->pdata->force_ick_on)
84 mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, true);
85
86
87 w = MCBSP_ST_READ(mcbsp, SYSCONFIG);
88 MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w & ~(ST_AUTOIDLE));
89
90
91 w = MCBSP_READ(mcbsp, SSELCR);
92 MCBSP_WRITE(mcbsp, SSELCR, w | SIDETONEEN);
93
94
95 w = MCBSP_ST_READ(mcbsp, SSELCR);
96 MCBSP_ST_WRITE(mcbsp, SSELCR, w | ST_SIDETONEEN);
97 }
98
99 static void omap_mcbsp_st_off(struct omap_mcbsp *mcbsp)
100 {
101 unsigned int w;
102
103 w = MCBSP_ST_READ(mcbsp, SSELCR);
104 MCBSP_ST_WRITE(mcbsp, SSELCR, w & ~(ST_SIDETONEEN));
105
106 w = MCBSP_READ(mcbsp, SSELCR);
107 MCBSP_WRITE(mcbsp, SSELCR, w & ~(SIDETONEEN));
108
109
110 w = MCBSP_ST_READ(mcbsp, SYSCONFIG);
111 MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w | ST_AUTOIDLE);
112
113 if (mcbsp->pdata->force_ick_on)
114 mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, false);
115 }
116
117 static void omap_mcbsp_st_fir_write(struct omap_mcbsp *mcbsp, s16 *fir)
118 {
119 u16 val, i;
120
121 val = MCBSP_ST_READ(mcbsp, SSELCR);
122
123 if (val & ST_COEFFWREN)
124 MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN));
125
126 MCBSP_ST_WRITE(mcbsp, SSELCR, val | ST_COEFFWREN);
127
128 for (i = 0; i < 128; i++)
129 MCBSP_ST_WRITE(mcbsp, SFIRCR, fir[i]);
130
131 i = 0;
132
133 val = MCBSP_ST_READ(mcbsp, SSELCR);
134 while (!(val & ST_COEFFWRDONE) && (++i < 1000))
135 val = MCBSP_ST_READ(mcbsp, SSELCR);
136
137 MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN));
138
139 if (i == 1000)
140 dev_err(mcbsp->dev, "McBSP FIR load error!\n");
141 }
142
143 static void omap_mcbsp_st_chgain(struct omap_mcbsp *mcbsp)
144 {
145 u16 w;
146 struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
147
148 w = MCBSP_ST_READ(mcbsp, SSELCR);
149
150 MCBSP_ST_WRITE(mcbsp, SGAINCR, ST_CH0GAIN(st_data->ch0gain) |
151 ST_CH1GAIN(st_data->ch1gain));
152 }
153
154 static int omap_mcbsp_st_set_chgain(struct omap_mcbsp *mcbsp, int channel,
155 s16 chgain)
156 {
157 struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
158 int ret = 0;
159
160 if (!st_data)
161 return -ENOENT;
162
163 spin_lock_irq(&mcbsp->lock);
164 if (channel == 0)
165 st_data->ch0gain = chgain;
166 else if (channel == 1)
167 st_data->ch1gain = chgain;
168 else
169 ret = -EINVAL;
170
171 if (st_data->enabled)
172 omap_mcbsp_st_chgain(mcbsp);
173 spin_unlock_irq(&mcbsp->lock);
174
175 return ret;
176 }
177
178 static int omap_mcbsp_st_get_chgain(struct omap_mcbsp *mcbsp, int channel,
179 s16 *chgain)
180 {
181 struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
182 int ret = 0;
183
184 if (!st_data)
185 return -ENOENT;
186
187 spin_lock_irq(&mcbsp->lock);
188 if (channel == 0)
189 *chgain = st_data->ch0gain;
190 else if (channel == 1)
191 *chgain = st_data->ch1gain;
192 else
193 ret = -EINVAL;
194 spin_unlock_irq(&mcbsp->lock);
195
196 return ret;
197 }
198
199 static int omap_mcbsp_st_enable(struct omap_mcbsp *mcbsp)
200 {
201 struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
202
203 if (!st_data)
204 return -ENODEV;
205
206 spin_lock_irq(&mcbsp->lock);
207 st_data->enabled = 1;
208 omap_mcbsp_st_start(mcbsp);
209 spin_unlock_irq(&mcbsp->lock);
210
211 return 0;
212 }
213
214 static int omap_mcbsp_st_disable(struct omap_mcbsp *mcbsp)
215 {
216 struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
217 int ret = 0;
218
219 if (!st_data)
220 return -ENODEV;
221
222 spin_lock_irq(&mcbsp->lock);
223 omap_mcbsp_st_stop(mcbsp);
224 st_data->enabled = 0;
225 spin_unlock_irq(&mcbsp->lock);
226
227 return ret;
228 }
229
230 static int omap_mcbsp_st_is_enabled(struct omap_mcbsp *mcbsp)
231 {
232 struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
233
234 if (!st_data)
235 return -ENODEV;
236
237 return st_data->enabled;
238 }
239
240 static ssize_t st_taps_show(struct device *dev,
241 struct device_attribute *attr, char *buf)
242 {
243 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
244 struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
245 ssize_t status = 0;
246 int i;
247
248 spin_lock_irq(&mcbsp->lock);
249 for (i = 0; i < st_data->nr_taps; i++)
250 status += sprintf(&buf[status], (i ? ", %d" : "%d"),
251 st_data->taps[i]);
252 if (i)
253 status += sprintf(&buf[status], "\n");
254 spin_unlock_irq(&mcbsp->lock);
255
256 return status;
257 }
258
259 static ssize_t st_taps_store(struct device *dev,
260 struct device_attribute *attr,
261 const char *buf, size_t size)
262 {
263 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
264 struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
265 int val, tmp, status, i = 0;
266
267 spin_lock_irq(&mcbsp->lock);
268 memset(st_data->taps, 0, sizeof(st_data->taps));
269 st_data->nr_taps = 0;
270
271 do {
272 status = sscanf(buf, "%d%n", &val, &tmp);
273 if (status < 0 || status == 0) {
274 size = -EINVAL;
275 goto out;
276 }
277 if (val < -32768 || val > 32767) {
278 size = -EINVAL;
279 goto out;
280 }
281 st_data->taps[i++] = val;
282 buf += tmp;
283 if (*buf != ',')
284 break;
285 buf++;
286 } while (1);
287
288 st_data->nr_taps = i;
289
290 out:
291 spin_unlock_irq(&mcbsp->lock);
292
293 return size;
294 }
295
296 static DEVICE_ATTR_RW(st_taps);
297
298 static const struct attribute *sidetone_attrs[] = {
299 &dev_attr_st_taps.attr,
300 NULL,
301 };
302
303 static const struct attribute_group sidetone_attr_group = {
304 .attrs = (struct attribute **)sidetone_attrs,
305 };
306
307 int omap_mcbsp_st_start(struct omap_mcbsp *mcbsp)
308 {
309 struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
310
311 if (st_data->enabled && !st_data->running) {
312 omap_mcbsp_st_fir_write(mcbsp, st_data->taps);
313 omap_mcbsp_st_chgain(mcbsp);
314
315 if (!mcbsp->free) {
316 omap_mcbsp_st_on(mcbsp);
317 st_data->running = 1;
318 }
319 }
320
321 return 0;
322 }
323
324 int omap_mcbsp_st_stop(struct omap_mcbsp *mcbsp)
325 {
326 struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
327
328 if (st_data->running) {
329 if (!mcbsp->free) {
330 omap_mcbsp_st_off(mcbsp);
331 st_data->running = 0;
332 }
333 }
334
335 return 0;
336 }
337
338 int omap_mcbsp_st_init(struct platform_device *pdev)
339 {
340 struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev);
341 struct omap_mcbsp_st_data *st_data;
342 struct resource *res;
343 int ret;
344
345 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sidetone");
346 if (!res)
347 return 0;
348
349 st_data = devm_kzalloc(mcbsp->dev, sizeof(*mcbsp->st_data), GFP_KERNEL);
350 if (!st_data)
351 return -ENOMEM;
352
353 st_data->mcbsp_iclk = clk_get(mcbsp->dev, "ick");
354 if (IS_ERR(st_data->mcbsp_iclk)) {
355 dev_warn(mcbsp->dev,
356 "Failed to get ick, sidetone might be broken\n");
357 st_data->mcbsp_iclk = NULL;
358 }
359
360 st_data->io_base_st = devm_ioremap(mcbsp->dev, res->start,
361 resource_size(res));
362 if (!st_data->io_base_st)
363 return -ENOMEM;
364
365 ret = sysfs_create_group(&mcbsp->dev->kobj, &sidetone_attr_group);
366 if (ret)
367 return ret;
368
369 mcbsp->st_data = st_data;
370
371 return 0;
372 }
373
374 void omap_mcbsp_st_cleanup(struct platform_device *pdev)
375 {
376 struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev);
377
378 if (mcbsp->st_data) {
379 sysfs_remove_group(&mcbsp->dev->kobj, &sidetone_attr_group);
380 clk_put(mcbsp->st_data->mcbsp_iclk);
381 }
382 }
383
384 static int omap_mcbsp_st_info_volsw(struct snd_kcontrol *kcontrol,
385 struct snd_ctl_elem_info *uinfo)
386 {
387 struct soc_mixer_control *mc =
388 (struct soc_mixer_control *)kcontrol->private_value;
389 int max = mc->max;
390 int min = mc->min;
391
392 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
393 uinfo->count = 1;
394 uinfo->value.integer.min = min;
395 uinfo->value.integer.max = max;
396 return 0;
397 }
398
399 #define OMAP_MCBSP_ST_CHANNEL_VOLUME(channel) \
400 static int \
401 omap_mcbsp_set_st_ch##channel##_volume(struct snd_kcontrol *kc, \
402 struct snd_ctl_elem_value *uc) \
403 { \
404 struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \
405 struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); \
406 struct soc_mixer_control *mc = \
407 (struct soc_mixer_control *)kc->private_value; \
408 int max = mc->max; \
409 int min = mc->min; \
410 int val = uc->value.integer.value[0]; \
411 \
412 if (val < min || val > max) \
413 return -EINVAL; \
414 \
415 \
416 return omap_mcbsp_st_set_chgain(mcbsp, channel, val); \
417 } \
418 \
419 static int \
420 omap_mcbsp_get_st_ch##channel##_volume(struct snd_kcontrol *kc, \
421 struct snd_ctl_elem_value *uc) \
422 { \
423 struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \
424 struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); \
425 s16 chgain; \
426 \
427 if (omap_mcbsp_st_get_chgain(mcbsp, channel, &chgain)) \
428 return -EAGAIN; \
429 \
430 uc->value.integer.value[0] = chgain; \
431 return 0; \
432 }
433
434 OMAP_MCBSP_ST_CHANNEL_VOLUME(0)
435 OMAP_MCBSP_ST_CHANNEL_VOLUME(1)
436
437 static int omap_mcbsp_st_put_mode(struct snd_kcontrol *kcontrol,
438 struct snd_ctl_elem_value *ucontrol)
439 {
440 struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
441 struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
442 u8 value = ucontrol->value.integer.value[0];
443
444 if (value == omap_mcbsp_st_is_enabled(mcbsp))
445 return 0;
446
447 if (value)
448 omap_mcbsp_st_enable(mcbsp);
449 else
450 omap_mcbsp_st_disable(mcbsp);
451
452 return 1;
453 }
454
455 static int omap_mcbsp_st_get_mode(struct snd_kcontrol *kcontrol,
456 struct snd_ctl_elem_value *ucontrol)
457 {
458 struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
459 struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
460
461 ucontrol->value.integer.value[0] = omap_mcbsp_st_is_enabled(mcbsp);
462 return 0;
463 }
464
465 #define OMAP_MCBSP_SOC_SINGLE_S16_EXT(xname, xmin, xmax, \
466 xhandler_get, xhandler_put) \
467 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
468 .info = omap_mcbsp_st_info_volsw, \
469 .get = xhandler_get, .put = xhandler_put, \
470 .private_value = (unsigned long)&(struct soc_mixer_control) \
471 {.min = xmin, .max = xmax} }
472
473 #define OMAP_MCBSP_ST_CONTROLS(port) \
474 static const struct snd_kcontrol_new omap_mcbsp##port##_st_controls[] = { \
475 SOC_SINGLE_EXT("McBSP" #port " Sidetone Switch", 1, 0, 1, 0, \
476 omap_mcbsp_st_get_mode, omap_mcbsp_st_put_mode), \
477 OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 0 Volume", \
478 -32768, 32767, \
479 omap_mcbsp_get_st_ch0_volume, \
480 omap_mcbsp_set_st_ch0_volume), \
481 OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 1 Volume", \
482 -32768, 32767, \
483 omap_mcbsp_get_st_ch1_volume, \
484 omap_mcbsp_set_st_ch1_volume), \
485 }
486
487 OMAP_MCBSP_ST_CONTROLS(2);
488 OMAP_MCBSP_ST_CONTROLS(3);
489
490 int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd, int port_id)
491 {
492 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
493 struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
494
495 if (!mcbsp->st_data) {
496 dev_warn(mcbsp->dev, "No sidetone data for port\n");
497 return 0;
498 }
499
500 switch (port_id) {
501 case 2:
502 return snd_soc_add_dai_controls(cpu_dai,
503 omap_mcbsp2_st_controls,
504 ARRAY_SIZE(omap_mcbsp2_st_controls));
505 case 3:
506 return snd_soc_add_dai_controls(cpu_dai,
507 omap_mcbsp3_st_controls,
508 ARRAY_SIZE(omap_mcbsp3_st_controls));
509 default:
510 dev_err(mcbsp->dev, "Port %d not supported\n", port_id);
511 break;
512 }
513
514 return -EINVAL;
515 }
516 EXPORT_SYMBOL_GPL(omap_mcbsp_st_add_controls);