tizen 2.4 release
[profile/mobile/platform/kernel/u-boot-tm1.git] / drivers / sound / dai / vbc.c
1 /*
2  * sound/soc/sprd/dai/vbc/vbc.c
3  *
4  * SPRD SoC CPU-DAI -- SpreadTrum SOC DAI with EQ&ALC and some loop.
5  *
6  * Copyright (C) 2012 SpreadTrum Ltd.
7  *
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.
11  *
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.
16  */
17 #define pr_fmt(fmt) "[audio: vbc ] " fmt
18
19 #include <common.h>
20 #include <errno.h>
21 #include <asm/atomic.h>
22 #include <ubi_uboot.h>
23
24 #include "vbc.h"
25 #include "../sound_common.h"
26
27 #ifdef CONFIG_SPRD_AUDIO_DEBUG
28 #define vbc_dbg pr_info
29 #else
30 #define vbc_dbg(...)
31 #endif
32
33 #define FUN_REG(f) ((unsigned short)(-((f) + 1)))
34
35 #define DEFINE_SPINLOCK(...)
36 #define DEFINE_MUTEX(...)
37
38 #define VBC_DG_VAL_MAX (0x7F)
39
40 struct vbc_fw_header {
41         char magic[VBC_EQ_FIRMWARE_MAGIC_LEN];
42         u32 profile_version;
43         u32 num_profile;
44 };
45
46 struct vbc_eq_profile {
47         char magic[VBC_EQ_FIRMWARE_MAGIC_LEN];
48         char name[VBC_EQ_PROFILE_NAME_MAX];
49         /* TODO */
50         u32 effect_paras[VBC_EFFECT_PARAS_LEN];
51 };
52
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         */
74 };
75
76 struct vbc_equ {
77         struct device *dev;
78         int is_active;
79         int is_loaded;
80         int is_loading;
81         int now_profile;
82         struct vbc_fw_header hdr;
83         struct vbc_eq_profile *data;
84         void (*vbc_eq_apply) (void *data);
85 };
86
87 typedef int (*vbc_dma_set) (int enable);
88 typedef int (*vbc_dg_set) (int enable, int dg);
89 DEFINE_MUTEX(vbc_mutex);
90 struct vbc_priv {
91         vbc_dma_set dma_set[2];
92
93         int (*arch_enable) (int chan);
94         int (*arch_disable) (int chan);
95         int is_open;
96         int is_active;
97         int used_chan_count;
98         int dg_switch[2];
99         int dg_val[2];
100         vbc_dg_set dg_set[2];
101 };
102
103 DEFINE_MUTEX(load_mutex);
104 static struct vbc_equ vbc_eq_setting = { 0 };
105
106 static void vbc_eq_try_apply(void);
107 static struct vbc_priv vbc[2];
108
109 DEFINE_SPINLOCK(vbc_lock);
110 /* local register setting */
111 static int vbc_reg_write(int reg, int val, int mask)
112 {
113         int tmp, ret;
114         spin_lock(&vbc_lock);
115         tmp = __raw_readl(reg);
116         ret = tmp;
117         tmp &= ~(mask);
118         tmp |= val & mask;
119         __raw_writel(tmp, reg);
120         spin_unlock(&vbc_lock);
121         return ret & (mask);
122 }
123
124 static inline int vbc_reg_read(int reg)
125 {
126         int tmp;
127         tmp = __raw_readl(reg);
128         return tmp;
129 }
130
131 static int vbc_set_buffer_size(int ad_buffer_size, int da_buffer_size)
132 {
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);
141         }
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);
147         }
148         vbc_reg_write(VBBUFFSIZE, val,
149                       (VBDABUFFERSIZE_MASK | VBADBUFFERSIZE_MASK));
150         return 0;
151 }
152
153 static inline int vbc_sw_write_buffer(int enable)
154 {
155         /* Software access ping-pong buffer enable when VBENABE bit low */
156         vbc_reg_write(VBDABUFFDTA, ((enable ? 1 : 0) << RAMSW_EN),
157                       (1 << RAMSW_EN));
158         return 0;
159 }
160
161 static inline int vbc_enable_set(int enable)
162 {
163         vbc_reg_write(VBADBUFFDTA, (0 << VBIIS_LRCK), (1 << VBIIS_LRCK));
164         vbc_reg_write(VBDABUFFDTA, ((enable ? 1 : 0) << VBENABLE),
165                       (1 << VBENABLE));
166         return 0;
167 }
168
169 static inline int vbc_ad0_dma_set(int enable)
170 {
171         vbc_reg_write(VBDABUFFDTA, ((enable ? 1 : 0) << VBAD0DMA_EN),
172                       (1 << VBAD0DMA_EN));
173         return 0;
174 }
175
176 static inline int vbc_ad1_dma_set(int enable)
177 {
178         vbc_reg_write(VBDABUFFDTA, ((enable ? 1 : 0) << VBAD1DMA_EN),
179                       (1 << VBAD1DMA_EN));
180         return 0;
181 }
182
183 static inline int vbc_da0_dma_set(int enable)
184 {
185         vbc_reg_write(VBDABUFFDTA, ((enable ? 1 : 0) << VBDA0DMA_EN),
186                       (1 << VBDA0DMA_EN));
187         return 0;
188 }
189
190 static inline int vbc_da1_dma_set(int enable)
191 {
192         vbc_reg_write(VBDABUFFDTA, ((enable ? 1 : 0) << VBDA1DMA_EN),
193                       (1 << VBDA1DMA_EN));
194         return 0;
195 }
196
197 static void vbc_da_buffer_clear(int id)
198 {
199         int i;
200         vbc_reg_write(VBDABUFFDTA, ((id ? 1 : 0) << RAMSW_NUMB),
201                       (1 << RAMSW_NUMB));
202         for (i = 0; i < VBC_FIFO_FRAME_NUM; i++) {
203                 __raw_writel(0, VBDA0);
204                 __raw_writel(0, VBDA1);
205         }
206 }
207
208 static void vbc_da_buffer_clear_all(void)
209 {
210         int i;
211         int ret;
212         int save_enable = 0;
213         ret = arch_audio_vbc_enable();
214         if (ret < 0) {
215                 pr_err("Failed to enable VBC\n");
216         }
217         save_enable |= (ret << 2);
218         for (i = 0; i < 2; i++) {
219                 ret = arch_audio_vbc_da_enable(i);
220                 if (ret < 0) {
221                         pr_err("Failed to enable VBC DA%d\n", i);
222                 }
223                 save_enable |= (ret << i);
224         }
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);
233                 }
234         }
235         save_enable >>= 1;
236         if (save_enable & 0x1) {
237                 arch_audio_vbc_disable();
238         }
239 }
240
241 static inline int vbc_str_2_index(int stream);
242
243 static int vbc_da_arch_enable(int chan)
244 {
245         int ret;
246         ret = arch_audio_vbc_da_enable(chan);
247         if (ret < 0) {
248                 pr_err("VBC da enable error:%i\n", ret);
249                 return ret;
250         } else {
251                 arch_audio_vbc_enable();
252                 vbc[vbc_str_2_index(SNDRV_PCM_STREAM_PLAYBACK)].is_active = 1;
253         }
254         return ret;
255 }
256
257 static int vbc_da_arch_disable(int chan)
258 {
259         int ret;
260         ret = arch_audio_vbc_da_disable(chan);
261         if (ret < 0) {
262                 pr_err("VBC da disable error:%i\n", ret);
263                 return ret;
264         } else {
265                 vbc[vbc_str_2_index(SNDRV_PCM_STREAM_PLAYBACK)].is_active = 0;
266         }
267         return ret;
268 }
269
270 static int vbc_ad_arch_enable(int chan)
271 {
272         int ret;
273         ret = arch_audio_vbc_ad_enable(chan);
274         if (ret < 0) {
275                 pr_err("VBC ad enable error:%i\n", ret);
276                 return ret;
277         } else {
278                 arch_audio_vbc_enable();
279                 vbc[vbc_str_2_index(SNDRV_PCM_STREAM_CAPTURE)].is_active = 1;
280         }
281         return ret;
282 }
283
284 static int vbc_ad_arch_disable(int chan)
285 {
286         int ret;
287         ret = arch_audio_vbc_ad_disable(chan);
288         if (ret < 0) {
289                 pr_err("VBC ad disable error:%i\n", ret);
290                 return ret;
291         } else {
292                 vbc[vbc_str_2_index(SNDRV_PCM_STREAM_CAPTURE)].is_active = 0;
293         }
294         return ret;
295 }
296
297 static inline int vbc_da0_dg_set(int enable, int dg)
298 {
299         if (enable) {
300                 vbc_reg_write(DADGCTL, 0x80 | (0xFF & dg), 0xFF);
301         } else {
302                 vbc_reg_write(DADGCTL, 0, 0x80);
303         }
304         return 0;
305 }
306
307 static inline int vbc_da1_dg_set(int enable, int dg)
308 {
309         if (enable) {
310                 vbc_reg_write(DADGCTL, (0x80 | (0xFF & dg)) << 8, 0xFF00);
311         } else {
312                 vbc_reg_write(DADGCTL, 0, 0x8000);
313         }
314         return 0;
315 }
316
317 static inline int vbc_ad0_dg_set(int enable, int dg)
318 {
319         if (enable) {
320                 vbc_reg_write(ADDGCTL, 0x80 | (0xFF & dg), 0xFF);
321         } else {
322                 vbc_reg_write(ADDGCTL, 0, 0x80);
323         }
324         return 0;
325 }
326
327 static inline int vbc_ad1_dg_set(int enable, int dg)
328 {
329         if (enable) {
330                 vbc_reg_write(ADDGCTL, (0x80 | (0xFF & dg)) << 8, 0xFF00);
331         } else {
332                 vbc_reg_write(ADDGCTL, 0, 0x8000);
333         }
334         return 0;
335 }
336
337 static int vbc_try_dg_set(int vbc_idx, int id)
338 {
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);
342         } else {
343                 vbc[vbc_idx].dg_set[id] (0, dg);
344         }
345         return 0;
346 }
347
348 static struct vbc_priv vbc[2] = {
349         {                       /*PlayBack */
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,
353          .is_active = 0,
354          .dg_switch = {0, 0},
355          .dg_val = {0x18, 0x18},
356          .dg_set = {vbc_da0_dg_set, vbc_da1_dg_set},
357          },
358         {                       /*Capture */
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,
362          .is_active = 0,
363          .dg_switch = {0, 0},
364          .dg_val = {0x18, 0x18},
365          .dg_set = {vbc_ad0_dg_set, vbc_ad1_dg_set},
366          },
367 };
368
369 /* NOTE:
370    this index need use for the [struct vbc_priv] vbc[2] index
371    default MUST return 0.
372  */
373 static inline int vbc_str_2_index(int stream)
374 {
375         if (stream == SNDRV_PCM_STREAM_CAPTURE) {
376                 return 1;
377         }
378         return 0;
379 }
380
381 static inline void vbc_reg_enable(void)
382 {
383         arch_audio_vbc_reg_enable();
384 }
385
386 int vbc_startup(int stream)
387 {
388         int vbc_idx;
389
390         vbc_dbg("Entering %s\n", __func__);
391         vbc_idx = vbc_str_2_index(stream);
392
393         if (vbc[vbc_idx].is_open || vbc[vbc_idx].is_active) {
394                 pr_err("vbc is actived:%d\n", stream);
395         }
396
397         mutex_lock(&vbc_mutex);
398         vbc[vbc_idx].is_open = 1;
399         mutex_unlock(&vbc_mutex);
400
401         vbc_reg_enable();
402
403         if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
404                 vbc_da_buffer_clear_all();
405                 vbc_set_buffer_size(0, VBC_FIFO_FRAME_NUM);
406                 vbc_eq_try_apply();
407         } else {
408                 vbc_set_buffer_size(VBC_FIFO_FRAME_NUM, 0);
409         }
410
411         vbc_try_dg_set(vbc_idx, VBC_LEFT);
412         vbc_try_dg_set(vbc_idx, VBC_RIGHT);
413
414         vbc[vbc_idx].used_chan_count = 2;
415
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]);
420
421         vbc_dbg("Leaving %s\n", __func__);
422         return 0;
423 }
424
425 static inline int vbc_can_close(void)
426 {
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);
429 }
430
431 void vbc_shutdown(int stream)
432 {
433         int vbc_idx;
434         int i;
435
436         vbc_dbg("Entering %s\n", __func__);
437
438         /* vbc da close MUST clear da buffer */
439         if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
440                 vbc_da_buffer_clear_all();
441         }
442
443         vbc_idx = vbc_str_2_index(stream);
444
445         for (i = 0; i < 2; i++) {
446                 vbc[vbc_idx].arch_disable(i);
447                 vbc[vbc_idx].dma_set[i] (0);
448         }
449
450         mutex_lock(&vbc_mutex);
451         vbc[vbc_idx].is_open = 0;
452         if (vbc_can_close()) {
453                 vbc_enable_set(0);
454                 arch_audio_vbc_reset();
455                 arch_audio_vbc_disable();
456                 arch_audio_vbc_reg_disable();
457                 pr_info("close the VBC\n");
458         }
459         mutex_unlock(&vbc_mutex);
460
461         vbc_dbg("Leaving %s\n", __func__);
462 }
463
464 int vbc_trigger(int stream, int enable)
465 {
466         int vbc_idx;
467         int ret = 0;
468         int i;
469
470 #if 0
471         vbc_dbg("Entering %s\n", __func__);
472 #endif
473
474         vbc_idx = vbc_str_2_index(stream);
475
476         if (enable) {
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);
480                 }
481                 vbc_enable_set(1);
482         } else {
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);
486                 }
487
488                 if (vbc_can_close()) {
489                         vbc_enable_set(0);
490                         arch_audio_vbc_disable();
491                 }
492         }
493
494 #if 0
495         vbc_dbg("Leaving %s\n", __func__);
496 #endif
497         return ret;
498 }
499
500 static int vbc_eq_reg_offset(u32 reg)
501 {
502         int i = 0;
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;
507         }
508         BUG_ON(i >= VBC_EFFECT_PARAS_LEN);
509         return i;
510 }
511
512 static inline void vbc_eq_reg_set(u32 reg, void *data)
513 {
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);
518 }
519
520 static inline void vbc_eq_reg_set_range(u32 reg_start, u32 reg_end, void *data)
521 {
522         u32 reg_addr;
523         for (reg_addr = reg_start; reg_addr <= reg_end; reg_addr += 4) {
524                 vbc_eq_reg_set(reg_addr, data);
525         }
526 }
527
528 static void vbc_eq_reg_apply(void *data)
529 {
530         vbc_eq_reg_set_range(DAALCCTL0, DAALCCTL10, data);
531         vbc_eq_reg_set_range(HPCOEF0, HPCOEF42, data);
532
533         vbc_eq_reg_set(DAHPCTL, data);
534         vbc_eq_reg_set(DAPATCHCTL, data);
535
536         vbc_eq_reg_set(STCTL0, data);
537         vbc_eq_reg_set(STCTL1, data);
538
539         vbc_eq_reg_set(ADPATCHCTL, data);
540 }
541
542 static void vbc_eq_profile_apply(void *data)
543 {
544         vbc_eq_reg_apply(data);
545 }
546
547 static void vbc_eq_profile_close(void)
548 {
549         vbc_eq_profile_apply(&vbc_eq_profile_default);
550 }
551
552 static void vbc_eq_try_apply(void)
553 {
554         u32 *data;
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);
565                 }
566                 mutex_unlock(&load_mutex);
567         }
568         vbc_dbg("Leaving %s\n", __func__);
569 }
570
571 static int vbc_eq_profile_get(void)
572 {
573         return vbc_eq_setting.now_profile;
574 }
575
576 static int vbc_eq_profile_put(int select)
577 {
578         int ret = 0;
579
580         pr_info("vbc eq select %d max %d\n", select,
581                 vbc_eq_setting.hdr.num_profile);
582
583         ret = select;
584         if (ret == vbc_eq_setting.now_profile) {
585                 return ret;
586         }
587         if (ret < vbc_eq_setting.hdr.num_profile) {
588                 vbc_eq_setting.now_profile = ret;
589         }
590
591         vbc_eq_try_apply();
592
593         vbc_dbg("Leaving %s\n", __func__);
594         return ret;
595 }
596
597 static int vbc_switch_get(void)
598 {
599         int ret;
600         ret = arch_audio_vbc_switch(AUDIO_NO_CHANGE);
601         if (ret >= 0)
602                     return (ret == AUDIO_TO_DSP_CTRL ? 0 : 1);
603         return ret;
604 }
605
606 static int vbc_switch_put(int is_arm)
607 {
608         int ret;
609         pr_info("VBC switch to %s\n", is_arm ? "ARM" : "DSP");
610
611         ret = is_arm;
612         ret = arch_audio_vbc_switch(ret == 0 ?
613                                     AUDIO_TO_DSP_CTRL : AUDIO_TO_ARM_CTRL);
614
615         vbc_dbg("Leaving %s\n", __func__);
616         return ret;
617 }
618
619 static int vbc_eq_switch_get(void)
620 {
621         return vbc_eq_setting.is_active;
622 }
623
624 static int vbc_eq_switch_put(int is_active)
625 {
626         int ret;
627         pr_info("VBC eq switch %s\n", is_active ? "ON" : "OFF");
628
629         ret = is_active;
630         if (ret == vbc_eq_setting.is_active) {
631                 return ret;
632         }
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;
637                         vbc_eq_try_apply();
638                 } else {
639                         vbc_eq_setting.vbc_eq_apply = 0;
640                         vbc_eq_profile_close();
641                 }
642         }
643
644         vbc_dbg("Leaving %s\n", __func__);
645         return ret;
646 }
647
648 static int vbc_dg_get(int stream, int id)
649 {
650         int vbc_idx = vbc_str_2_index(stream);
651         return vbc[vbc_idx].dg_val[id];
652 }
653
654 static int vbc_dg_put(int stream, int id, int dg)
655 {
656         int ret = 0;
657         int vbc_idx = vbc_str_2_index(stream);
658
659         pr_info("VBC %s%s DG set 0x%02x\n", vbc_idx == 1 ? "ADC" : "DAC",
660                 id == VBC_LEFT ? "L" : "R", dg);
661
662         ret = dg;
663         if (ret == vbc[vbc_idx].dg_val[id]) {
664                 return ret;
665         }
666         if (ret <= VBC_DG_VAL_MAX) {
667                 vbc[vbc_idx].dg_val[id] = ret;
668         }
669
670         vbc_try_dg_set(vbc_idx, id);
671
672         vbc_dbg("Leaving %s\n", __func__);
673         return ret;
674 }
675
676 static int vbc_dg_switch_get(int stream, int id)
677 {
678         int vbc_idx = vbc_str_2_index(stream);
679         return vbc[vbc_idx].dg_switch[id];
680 }
681
682 static int vbc_dg_switch_put(int stream, int id, int enable)
683 {
684         int ret = 0;
685         int vbc_idx = vbc_str_2_index(stream);
686
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");
690
691         ret = enable;
692         if (ret == vbc[vbc_idx].dg_switch[id]) {
693                 return ret;
694         }
695
696         vbc[vbc_idx].dg_switch[id] = ret;
697
698         vbc_try_dg_set(vbc_idx, id);
699
700         vbc_dbg("Leaving %s\n", __func__);
701         return ret;
702 }
703
704 int vbc_init(void)
705 {
706         arch_audio_vbc_switch(AUDIO_TO_ARM_CTRL);
707         return 0;
708 }
709
710 void vbc_exit(void)
711 {
712 }
713
714 MODULE_DESCRIPTION("SPRD ASoC VBC CUP-DAI driver");
715 MODULE_AUTHOR("Ken Kuang <ken.kuang@spreadtrum.com>");
716 MODULE_LICENSE("GPL");