summaryrefslogtreecommitdiff
path: root/sound/soc/sprd/dai/vbc/r1p0/vbc-comm.c
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
committerYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
commit8e4bff4cab0ddac6060645b0715210484d02ff40 (patch)
tree3a5c3024371a04692a3a6d9974d001cdff8ebf84 /sound/soc/sprd/dai/vbc/r1p0/vbc-comm.c
Initial file dump from open source releaseHEADmaster
Diffstat (limited to 'sound/soc/sprd/dai/vbc/r1p0/vbc-comm.c')
-rw-r--r--sound/soc/sprd/dai/vbc/r1p0/vbc-comm.c229
1 files changed, 229 insertions, 0 deletions
diff --git a/sound/soc/sprd/dai/vbc/r1p0/vbc-comm.c b/sound/soc/sprd/dai/vbc/r1p0/vbc-comm.c
new file mode 100644
index 00000000..6123ded2
--- /dev/null
+++ b/sound/soc/sprd/dai/vbc/r1p0/vbc-comm.c
@@ -0,0 +1,229 @@
+/*
+ * sound/soc/sprd/dai/vbc/r1p0/vbc-comm.c
+ *
+ * SPRD SoC VBC -- SpreadTrum SOC DAI for VBC Common function.
+ *
+ * Copyright (C) 2012 SpreadTrum Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include <linux/stat.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+#include <linux/workqueue.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <sound/soc.h>
+
+#include "sprd-asoc-common.h"
+#include "vbc-comm.h"
+
+static DEFINE_SPINLOCK(vbc_lock);
+
+/* vbc local power suppliy and chan on */
+static struct vbc_refcount {
+ atomic_t vbc_power_on; /*vbc reg power enable */
+ atomic_t vbc_on; /*vbc enable */
+ atomic_t chan_on[VBC_IDX_MAX][2];
+} vbc_refcnt;
+
+static const char *sprd_vbc_name[VBC_IDX_MAX] = {
+ "DAC",
+ "ADC",
+};
+
+static inline const char *vbc_get_name(int vbc_idx)
+{
+ return sprd_vbc_name[vbc_idx];
+}
+
+static inline int vbc_reg_read(unsigned int reg)
+{
+ return __raw_readl((void *__iomem)reg);
+}
+
+static inline void vbc_reg_raw_write(unsigned int reg, int val)
+{
+ __raw_writel(val, (void *__iomem)reg);
+}
+
+static int vbc_reg_write(unsigned int reg, int val)
+{
+ spin_lock(&vbc_lock);
+ vbc_reg_raw_write(reg, val);
+ spin_unlock(&vbc_lock);
+ sp_asoc_pr_reg("[0x%04x] W:[0x%08x] R:[0x%08x]\n", reg & 0xFFFF, val,
+ vbc_reg_read(reg));
+ return 0;
+}
+
+/*
+ * Returns 1 for change, 0 for no change, or negative error code.
+ */
+static int vbc_reg_update(unsigned int reg, int val, int mask)
+{
+ int new, old;
+ spin_lock(&vbc_lock);
+ old = vbc_reg_read(reg);
+ new = (old & ~mask) | (val & mask);
+ vbc_reg_raw_write(reg, new);
+ spin_unlock(&vbc_lock);
+ sp_asoc_pr_reg("[0x%04x] U:[0x%08x] R:[0x%08x]\n", reg & 0xFFFF, new,
+ vbc_reg_read(reg));
+ return old != new;
+}
+
+static struct clk *s_vbc_clk = 0;
+static void vbc_clk_set(struct clk *clk)
+{
+ s_vbc_clk = clk;
+}
+
+static struct clk *vbc_clk_get(void)
+{
+ return s_vbc_clk;
+}
+
+static inline void vbc_reg_enable(void)
+{
+ if (s_vbc_clk) {
+ clk_enable(s_vbc_clk);
+ } else {
+ arch_audio_vbc_reg_enable();
+ }
+}
+
+static inline void vbc_reg_disable(void)
+{
+ if (s_vbc_clk) {
+ clk_disable(s_vbc_clk);
+ } else {
+ arch_audio_vbc_reg_disable();
+ }
+}
+
+static int vbc_power(int enable)
+{
+ int i;
+ atomic_t *vbc_power_on = &vbc_refcnt.vbc_power_on;
+ if (enable) {
+ atomic_inc(vbc_power_on);
+ if (atomic_read(vbc_power_on) == 1) {
+ arch_audio_vbc_enable();
+ vbc_reg_enable();
+ for (i = 0; i < SPRD_VBC_PLAYBACK_COUNT; i++) {
+ vbc_da_buffer_clear_all(i);
+ }
+ sp_asoc_pr_dbg("VBC Power On\n");
+ }
+ } else {
+ if (atomic_dec_and_test(vbc_power_on)) {
+ arch_audio_vbc_reset();
+ arch_audio_vbc_disable();
+ vbc_reg_disable();
+ sp_asoc_pr_dbg("VBC Power Off\n");
+ }
+ if (atomic_read(vbc_power_on) < 0) {
+ atomic_set(vbc_power_on, 0);
+ }
+ }
+ sp_asoc_pr_dbg("VBC Power REF: %d", atomic_read(vbc_power_on));
+ return 0;
+}
+
+static inline int vbc_da_enable_raw(int enable, int chan)
+{
+ int ret;
+ if (enable) {
+ ret = arch_audio_vbc_da_enable(chan);
+ } else {
+ ret = arch_audio_vbc_da_disable(chan);
+ }
+ return ret;
+}
+
+static inline int vbc_ad_enable_raw(int enable, int chan)
+{
+ int ret;
+ if (enable) {
+ ret = arch_audio_vbc_ad_enable(chan);
+ } else {
+ ret = arch_audio_vbc_ad_disable(chan);
+ }
+ return ret;
+}
+
+static inline int vbc_enable_set(int enable)
+{
+ vbc_reg_update(VBDABUFFDTA, ((enable ? 1 : 0) << VBENABLE),
+ (1 << VBENABLE));
+ return 0;
+}
+
+typedef int (*vbc_chan_enable_raw) (int enable, int chan);
+static vbc_chan_enable_raw vbc_chan_enable_fun[VBC_IDX_MAX] = {
+ vbc_da_enable_raw,
+ vbc_ad_enable_raw,
+};
+
+static int vbc_chan_enable(int enable, int vbc_idx, int chan)
+{
+ atomic_t *chan_on = &vbc_refcnt.chan_on[vbc_idx][chan];
+ if (enable) {
+ atomic_inc(chan_on);
+ if (atomic_read(chan_on) == 1) {
+ vbc_chan_enable_fun[vbc_idx] (1, chan);
+ sp_asoc_pr_dbg("VBC %s%d On\n", vbc_get_name(vbc_idx),
+ chan);
+ }
+ } else {
+ if (atomic_dec_and_test(chan_on)) {
+ vbc_chan_enable_fun[vbc_idx] (0, chan);
+ sp_asoc_pr_dbg("VBC %s%d Off\n", vbc_get_name(vbc_idx),
+ chan);
+ }
+ if (atomic_read(chan_on) < 0) {
+ atomic_set(chan_on, 0);
+ }
+ }
+ sp_asoc_pr_dbg("%s%d REF: %d", vbc_get_name(vbc_idx), chan,
+ atomic_read(chan_on));
+ return 0;
+}
+
+static int vbc_enable(int enable)
+{
+ atomic_t *vbc_on = &vbc_refcnt.vbc_on;
+ if (enable) {
+ atomic_inc(vbc_on);
+ if (atomic_read(vbc_on) == 1) {
+ vbc_enable_set(1);
+ sp_asoc_pr_dbg("VBC Enable\n");
+ }
+ } else {
+ if (atomic_dec_and_test(vbc_on)) {
+ vbc_enable_set(0);
+ sp_asoc_pr_dbg("VBC Disable");
+ }
+ if (atomic_read(vbc_on) < 0) {
+ atomic_set(vbc_on, 0);
+ }
+ }
+
+ sp_asoc_pr_dbg("VBC EN REF: %d", atomic_read(vbc_on));
+ return 0;
+}