2 * sound/soc/sprd/dai/vbc/vbc.c
4 * SPRD SoC CPU-DAI -- SpreadTrum SOC DAI with EQ&ALC and some loop.
6 * Copyright (C) 2012 SpreadTrum Ltd.
8 * This software is licensed under the terms of the GNU General Public
9 * License version 2, as published by the Free Software Foundation, and
10 * may be copied, distributed, and modified under those terms.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 #define pr_fmt(fmt) "[audio: vbc ] " fmt
21 #include <asm/atomic.h>
22 #include <ubi_uboot.h>
25 #include "../sound_common.h"
27 #ifdef CONFIG_SPRD_AUDIO_DEBUG
28 #define vbc_dbg pr_info
33 #define FUN_REG(f) ((unsigned short)(-((f) + 1)))
35 #define DEFINE_SPINLOCK(...)
36 #define DEFINE_MUTEX(...)
38 #define VBC_DG_VAL_MAX (0x7F)
40 struct vbc_fw_header {
41 char magic[VBC_EQ_FIRMWARE_MAGIC_LEN];
46 struct vbc_eq_profile {
47 char magic[VBC_EQ_FIRMWARE_MAGIC_LEN];
48 char name[VBC_EQ_PROFILE_NAME_MAX];
50 u32 effect_paras[VBC_EFFECT_PARAS_LEN];
53 static const u32 vbc_eq_profile_default[VBC_EFFECT_PARAS_LEN] = {
54 /* TODO the default register value */
55 0x00000000, /* DAPATCHCTL */
56 0x00001818, /* DADGCTL */
57 0x0000007F, /* DAHPCTL */
58 0x00000000, /* DAALCCTL0 */
59 0x00000000, /* DAALCCTL1 */
60 0x00000000, /* DAALCCTL2 */
61 0x00000000, /* DAALCCTL3 */
62 0x00000000, /* DAALCCTL4 */
63 0x00000000, /* DAALCCTL5 */
64 0x00000000, /* DAALCCTL6 */
65 0x00000000, /* DAALCCTL7 */
66 0x00000000, /* DAALCCTL8 */
67 0x00000000, /* DAALCCTL9 */
68 0x00000000, /* DAALCCTL10 */
69 0x00000183, /* STCTL0 */
70 0x00000183, /* STCTL1 */
71 0x00000000, /* ADPATCHCTL */
72 0x00001818, /* ADDGCTL */
73 0x00000000, /* HPCOEF0 */
82 struct vbc_fw_header hdr;
83 struct vbc_eq_profile *data;
84 void (*vbc_eq_apply) (void *data);
87 typedef int (*vbc_dma_set) (int enable);
88 typedef int (*vbc_dg_set) (int enable, int dg);
89 DEFINE_MUTEX(vbc_mutex);
91 vbc_dma_set dma_set[2];
93 int (*arch_enable) (int chan);
94 int (*arch_disable) (int chan);
100 vbc_dg_set dg_set[2];
103 DEFINE_MUTEX(load_mutex);
104 static struct vbc_equ vbc_eq_setting = { 0 };
106 static void vbc_eq_try_apply(void);
107 static struct vbc_priv vbc[2];
109 DEFINE_SPINLOCK(vbc_lock);
110 /* local register setting */
111 static int vbc_reg_write(int reg, int val, int mask)
114 spin_lock(&vbc_lock);
115 tmp = __raw_readl(reg);
119 __raw_writel(tmp, reg);
120 spin_unlock(&vbc_lock);
124 static inline int vbc_reg_read(int reg)
127 tmp = __raw_readl(reg);
131 static int vbc_set_buffer_size(int ad_buffer_size, int da_buffer_size)
133 int val = vbc_reg_read(VBBUFFSIZE);
134 WARN_ON(ad_buffer_size > VBC_FIFO_FRAME_NUM);
135 WARN_ON(da_buffer_size > VBC_FIFO_FRAME_NUM);
136 if ((ad_buffer_size > 0)
137 && (ad_buffer_size <= VBC_FIFO_FRAME_NUM)) {
138 val &= ~(VBADBUFFERSIZE_MASK);
139 val |= (((ad_buffer_size - 1) << VBADBUFFERSIZE_SHIFT)
140 & VBADBUFFERSIZE_MASK);
142 if ((da_buffer_size > 0)
143 && (da_buffer_size <= VBC_FIFO_FRAME_NUM)) {
144 val &= ~(VBDABUFFERSIZE_MASK);
145 val |= (((da_buffer_size - 1) << VBDABUFFERSIZE_SHIFT)
146 & VBDABUFFERSIZE_MASK);
148 vbc_reg_write(VBBUFFSIZE, val,
149 (VBDABUFFERSIZE_MASK | VBADBUFFERSIZE_MASK));
153 static inline int vbc_sw_write_buffer(int enable)
155 /* Software access ping-pong buffer enable when VBENABE bit low */
156 vbc_reg_write(VBDABUFFDTA, ((enable ? 1 : 0) << RAMSW_EN),
161 static inline int vbc_enable_set(int enable)
163 vbc_reg_write(VBADBUFFDTA, (0 << VBIIS_LRCK), (1 << VBIIS_LRCK));
164 vbc_reg_write(VBDABUFFDTA, ((enable ? 1 : 0) << VBENABLE),
169 static inline int vbc_ad0_dma_set(int enable)
171 vbc_reg_write(VBDABUFFDTA, ((enable ? 1 : 0) << VBAD0DMA_EN),
176 static inline int vbc_ad1_dma_set(int enable)
178 vbc_reg_write(VBDABUFFDTA, ((enable ? 1 : 0) << VBAD1DMA_EN),
183 static inline int vbc_da0_dma_set(int enable)
185 vbc_reg_write(VBDABUFFDTA, ((enable ? 1 : 0) << VBDA0DMA_EN),
190 static inline int vbc_da1_dma_set(int enable)
192 vbc_reg_write(VBDABUFFDTA, ((enable ? 1 : 0) << VBDA1DMA_EN),
197 static void vbc_da_buffer_clear(int id)
200 vbc_reg_write(VBDABUFFDTA, ((id ? 1 : 0) << RAMSW_NUMB),
202 for (i = 0; i < VBC_FIFO_FRAME_NUM; i++) {
203 __raw_writel(0, VBDA0);
204 __raw_writel(0, VBDA1);
208 static void vbc_da_buffer_clear_all(void)
213 ret = arch_audio_vbc_enable();
215 pr_err("Failed to enable VBC\n");
217 save_enable |= (ret << 2);
218 for (i = 0; i < 2; i++) {
219 ret = arch_audio_vbc_da_enable(i);
221 pr_err("Failed to enable VBC DA%d\n", i);
223 save_enable |= (ret << i);
225 vbc_sw_write_buffer(true);
226 vbc_set_buffer_size(0, VBC_FIFO_FRAME_NUM);
227 vbc_da_buffer_clear(1); /* clear data buffer 1 */
228 vbc_da_buffer_clear(0); /* clear data buffer 0 */
229 vbc_sw_write_buffer(false);
230 for (i = 0; i < 2; i++, save_enable >>= 1) {
231 if (save_enable & 0x1) {
232 arch_audio_vbc_da_disable(i);
236 if (save_enable & 0x1) {
237 arch_audio_vbc_disable();
241 static inline int vbc_str_2_index(int stream);
243 static int vbc_da_arch_enable(int chan)
246 ret = arch_audio_vbc_da_enable(chan);
248 pr_err("VBC da enable error:%i\n", ret);
251 arch_audio_vbc_enable();
252 vbc[vbc_str_2_index(SNDRV_PCM_STREAM_PLAYBACK)].is_active = 1;
257 static int vbc_da_arch_disable(int chan)
260 ret = arch_audio_vbc_da_disable(chan);
262 pr_err("VBC da disable error:%i\n", ret);
265 vbc[vbc_str_2_index(SNDRV_PCM_STREAM_PLAYBACK)].is_active = 0;
270 static int vbc_ad_arch_enable(int chan)
273 ret = arch_audio_vbc_ad_enable(chan);
275 pr_err("VBC ad enable error:%i\n", ret);
278 arch_audio_vbc_enable();
279 vbc[vbc_str_2_index(SNDRV_PCM_STREAM_CAPTURE)].is_active = 1;
284 static int vbc_ad_arch_disable(int chan)
287 ret = arch_audio_vbc_ad_disable(chan);
289 pr_err("VBC ad disable error:%i\n", ret);
292 vbc[vbc_str_2_index(SNDRV_PCM_STREAM_CAPTURE)].is_active = 0;
297 static inline int vbc_da0_dg_set(int enable, int dg)
300 vbc_reg_write(DADGCTL, 0x80 | (0xFF & dg), 0xFF);
302 vbc_reg_write(DADGCTL, 0, 0x80);
307 static inline int vbc_da1_dg_set(int enable, int dg)
310 vbc_reg_write(DADGCTL, (0x80 | (0xFF & dg)) << 8, 0xFF00);
312 vbc_reg_write(DADGCTL, 0, 0x8000);
317 static inline int vbc_ad0_dg_set(int enable, int dg)
320 vbc_reg_write(ADDGCTL, 0x80 | (0xFF & dg), 0xFF);
322 vbc_reg_write(ADDGCTL, 0, 0x80);
327 static inline int vbc_ad1_dg_set(int enable, int dg)
330 vbc_reg_write(ADDGCTL, (0x80 | (0xFF & dg)) << 8, 0xFF00);
332 vbc_reg_write(ADDGCTL, 0, 0x8000);
337 static int vbc_try_dg_set(int vbc_idx, int id)
339 int dg = vbc[vbc_idx].dg_val[id];
340 if (vbc[vbc_idx].dg_switch[id]) {
341 vbc[vbc_idx].dg_set[id] (1, dg);
343 vbc[vbc_idx].dg_set[id] (0, dg);
348 static struct vbc_priv vbc[2] = {
350 .dma_set = {vbc_da0_dma_set, vbc_da1_dma_set},
351 .arch_enable = vbc_da_arch_enable,
352 .arch_disable = vbc_da_arch_disable,
355 .dg_val = {0x18, 0x18},
356 .dg_set = {vbc_da0_dg_set, vbc_da1_dg_set},
359 .dma_set = {vbc_ad0_dma_set, vbc_ad1_dma_set},
360 .arch_enable = vbc_ad_arch_enable,
361 .arch_disable = vbc_ad_arch_disable,
364 .dg_val = {0x18, 0x18},
365 .dg_set = {vbc_ad0_dg_set, vbc_ad1_dg_set},
370 this index need use for the [struct vbc_priv] vbc[2] index
371 default MUST return 0.
373 static inline int vbc_str_2_index(int stream)
375 if (stream == SNDRV_PCM_STREAM_CAPTURE) {
381 static inline void vbc_reg_enable(void)
383 arch_audio_vbc_reg_enable();
386 int vbc_startup(int stream)
390 vbc_dbg("Entering %s\n", __func__);
391 vbc_idx = vbc_str_2_index(stream);
393 if (vbc[vbc_idx].is_open || vbc[vbc_idx].is_active) {
394 pr_err("vbc is actived:%d\n", stream);
397 mutex_lock(&vbc_mutex);
398 vbc[vbc_idx].is_open = 1;
399 mutex_unlock(&vbc_mutex);
403 if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
404 vbc_da_buffer_clear_all();
405 vbc_set_buffer_size(0, VBC_FIFO_FRAME_NUM);
408 vbc_set_buffer_size(VBC_FIFO_FRAME_NUM, 0);
411 vbc_try_dg_set(vbc_idx, VBC_LEFT);
412 vbc_try_dg_set(vbc_idx, VBC_RIGHT);
414 vbc[vbc_idx].used_chan_count = 2;
416 WARN_ON(!vbc[vbc_idx].arch_enable);
417 WARN_ON(!vbc[vbc_idx].arch_disable);
418 WARN_ON(!vbc[vbc_idx].dma_set[0]);
419 WARN_ON(!vbc[vbc_idx].dma_set[1]);
421 vbc_dbg("Leaving %s\n", __func__);
425 static inline int vbc_can_close(void)
427 return !(vbc[vbc_str_2_index(SNDRV_PCM_STREAM_PLAYBACK)].is_open
428 || vbc[vbc_str_2_index(SNDRV_PCM_STREAM_CAPTURE)].is_open);
431 void vbc_shutdown(int stream)
436 vbc_dbg("Entering %s\n", __func__);
438 /* vbc da close MUST clear da buffer */
439 if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
440 vbc_da_buffer_clear_all();
443 vbc_idx = vbc_str_2_index(stream);
445 for (i = 0; i < 2; i++) {
446 vbc[vbc_idx].arch_disable(i);
447 vbc[vbc_idx].dma_set[i] (0);
450 mutex_lock(&vbc_mutex);
451 vbc[vbc_idx].is_open = 0;
452 if (vbc_can_close()) {
454 arch_audio_vbc_reset();
455 arch_audio_vbc_disable();
456 arch_audio_vbc_reg_disable();
457 pr_info("close the VBC\n");
459 mutex_unlock(&vbc_mutex);
461 vbc_dbg("Leaving %s\n", __func__);
464 int vbc_trigger(int stream, int enable)
471 vbc_dbg("Entering %s\n", __func__);
474 vbc_idx = vbc_str_2_index(stream);
477 for (i = 0; i < vbc[vbc_idx].used_chan_count; i++) {
478 vbc[vbc_idx].arch_enable(i);
479 vbc[vbc_idx].dma_set[i] (1);
483 for (i = 0; i < vbc[vbc_idx].used_chan_count; i++) {
484 vbc[vbc_idx].arch_disable(i);
485 vbc[vbc_idx].dma_set[i] (0);
488 if (vbc_can_close()) {
490 arch_audio_vbc_disable();
495 vbc_dbg("Leaving %s\n", __func__);
500 static int vbc_eq_reg_offset(u32 reg)
503 if ((reg >= DAPATCHCTL) && (reg <= ADDGCTL)) {
504 i = (reg - DAPATCHCTL) >> 2;
505 } else if ((reg >= HPCOEF0) && (reg <= HPCOEF42)) {
506 i = ((reg - HPCOEF0) >> 2) + ((ADDGCTL - DAPATCHCTL) >> 2) + 1;
508 BUG_ON(i >= VBC_EFFECT_PARAS_LEN);
512 static inline void vbc_eq_reg_set(u32 reg, void *data)
514 u32 *effect_paras = data;
515 vbc_dbg("reg(0x%x) = (0x%x)\n", reg,
516 effect_paras[vbc_eq_reg_offset(reg)]);
517 __raw_writel(effect_paras[vbc_eq_reg_offset(reg)], reg);
520 static inline void vbc_eq_reg_set_range(u32 reg_start, u32 reg_end, void *data)
523 for (reg_addr = reg_start; reg_addr <= reg_end; reg_addr += 4) {
524 vbc_eq_reg_set(reg_addr, data);
528 static void vbc_eq_reg_apply(void *data)
530 vbc_eq_reg_set_range(DAALCCTL0, DAALCCTL10, data);
531 vbc_eq_reg_set_range(HPCOEF0, HPCOEF42, data);
533 vbc_eq_reg_set(DAHPCTL, data);
534 vbc_eq_reg_set(DAPATCHCTL, data);
536 vbc_eq_reg_set(STCTL0, data);
537 vbc_eq_reg_set(STCTL1, data);
539 vbc_eq_reg_set(ADPATCHCTL, data);
542 static void vbc_eq_profile_apply(void *data)
544 vbc_eq_reg_apply(data);
547 static void vbc_eq_profile_close(void)
549 vbc_eq_profile_apply(&vbc_eq_profile_default);
552 static void vbc_eq_try_apply(void)
555 vbc_dbg("Entering %s 0x%x\n", __func__,
556 (int)vbc_eq_setting.vbc_eq_apply);
557 if (vbc_eq_setting.vbc_eq_apply) {
558 mutex_lock(&load_mutex);
559 if (vbc_eq_setting.is_loaded) {
560 struct vbc_eq_profile *now =
561 &vbc_eq_setting.data[vbc_eq_setting.now_profile];
562 data = now->effect_paras;
563 pr_info("vbc eq apply '%s'\n", now->name);
564 vbc_eq_setting.vbc_eq_apply(data);
566 mutex_unlock(&load_mutex);
568 vbc_dbg("Leaving %s\n", __func__);
571 static int vbc_eq_profile_get(void)
573 return vbc_eq_setting.now_profile;
576 static int vbc_eq_profile_put(int select)
580 pr_info("vbc eq select %d max %d\n", select,
581 vbc_eq_setting.hdr.num_profile);
584 if (ret == vbc_eq_setting.now_profile) {
587 if (ret < vbc_eq_setting.hdr.num_profile) {
588 vbc_eq_setting.now_profile = ret;
593 vbc_dbg("Leaving %s\n", __func__);
597 static int vbc_switch_get(void)
600 ret = arch_audio_vbc_switch(AUDIO_NO_CHANGE);
602 return (ret == AUDIO_TO_DSP_CTRL ? 0 : 1);
606 static int vbc_switch_put(int is_arm)
609 pr_info("VBC switch to %s\n", is_arm ? "ARM" : "DSP");
612 ret = arch_audio_vbc_switch(ret == 0 ?
613 AUDIO_TO_DSP_CTRL : AUDIO_TO_ARM_CTRL);
615 vbc_dbg("Leaving %s\n", __func__);
619 static int vbc_eq_switch_get(void)
621 return vbc_eq_setting.is_active;
624 static int vbc_eq_switch_put(int is_active)
627 pr_info("VBC eq switch %s\n", is_active ? "ON" : "OFF");
630 if (ret == vbc_eq_setting.is_active) {
633 if ((ret == 0) || (ret == 1)) {
634 vbc_eq_setting.is_active = ret;
635 if (vbc_eq_setting.is_active) {
636 vbc_eq_setting.vbc_eq_apply = vbc_eq_profile_apply;
639 vbc_eq_setting.vbc_eq_apply = 0;
640 vbc_eq_profile_close();
644 vbc_dbg("Leaving %s\n", __func__);
648 static int vbc_dg_get(int stream, int id)
650 int vbc_idx = vbc_str_2_index(stream);
651 return vbc[vbc_idx].dg_val[id];
654 static int vbc_dg_put(int stream, int id, int dg)
657 int vbc_idx = vbc_str_2_index(stream);
659 pr_info("VBC %s%s DG set 0x%02x\n", vbc_idx == 1 ? "ADC" : "DAC",
660 id == VBC_LEFT ? "L" : "R", dg);
663 if (ret == vbc[vbc_idx].dg_val[id]) {
666 if (ret <= VBC_DG_VAL_MAX) {
667 vbc[vbc_idx].dg_val[id] = ret;
670 vbc_try_dg_set(vbc_idx, id);
672 vbc_dbg("Leaving %s\n", __func__);
676 static int vbc_dg_switch_get(int stream, int id)
678 int vbc_idx = vbc_str_2_index(stream);
679 return vbc[vbc_idx].dg_switch[id];
682 static int vbc_dg_switch_put(int stream, int id, int enable)
685 int vbc_idx = vbc_str_2_index(stream);
687 pr_info("VBC %s%s DG switch %s\n", vbc_idx == 1 ? "ADC" : "DAC",
688 id == VBC_LEFT ? "L" : "R",
689 enable ? "ON" : "OFF");
692 if (ret == vbc[vbc_idx].dg_switch[id]) {
696 vbc[vbc_idx].dg_switch[id] = ret;
698 vbc_try_dg_set(vbc_idx, id);
700 vbc_dbg("Leaving %s\n", __func__);
706 arch_audio_vbc_switch(AUDIO_TO_ARM_CTRL);
714 MODULE_DESCRIPTION("SPRD ASoC VBC CUP-DAI driver");
715 MODULE_AUTHOR("Ken Kuang <ken.kuang@spreadtrum.com>");
716 MODULE_LICENSE("GPL");