audio-lnx: Initial change for techpack of audio drivers.
Add snapshot for audio drivers for SDM targets. The code is
migrated from msm-4.9 kernel at the below cutoff -
(74ff856e8d6: "net: ipc_router: Add dynamic enable/disable
wakeup source feature")
This changes are done for new techpack addition
for audio kernel. Migrate all audio kernel drivers
to this techpack.
Change-Id: I33d580af3ba86a5cb777583efc5d4cdaf2882d93
Signed-off-by: Asish Bhattacharya <asishb@codeaurora.org>
diff --git a/sound/soc/codecs/wcdcal-hwdep.c b/sound/soc/codecs/wcdcal-hwdep.c
new file mode 100644
index 0000000..31eae69
--- /dev/null
+++ b/sound/soc/codecs/wcdcal-hwdep.c
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2015, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/platform_device.h>
+#include <linux/slab.h>
+#include <linux/ioctl.h>
+#include <linux/bitops.h>
+#include <sound/hwdep.h>
+#include <sound/msmcal-hwdep.h>
+#include <sound/soc.h>
+#include "wcdcal-hwdep.h"
+
+const int cal_size_info[WCD9XXX_MAX_CAL] = {
+ [WCD9XXX_ANC_CAL] = 16384,
+ [WCD9XXX_MBHC_CAL] = 4096,
+ [WCD9XXX_MAD_CAL] = 4096,
+ [WCD9XXX_VBAT_CAL] = 72,
+};
+
+const char *cal_name_info[WCD9XXX_MAX_CAL] = {
+ [WCD9XXX_ANC_CAL] = "anc",
+ [WCD9XXX_MBHC_CAL] = "mbhc",
+ [WCD9XXX_MAD_CAL] = "mad",
+ [WCD9XXX_VBAT_CAL] = "vbat",
+};
+
+struct firmware_cal *wcdcal_get_fw_cal(struct fw_info *fw_data,
+ enum wcd_cal_type type)
+{
+ if (!fw_data) {
+ pr_err("%s: fw_data is NULL\n", __func__);
+ return NULL;
+ }
+ if (type >= WCD9XXX_MAX_CAL ||
+ type < WCD9XXX_MIN_CAL) {
+ pr_err("%s: wrong cal type sent %d\n", __func__, type);
+ return NULL;
+ }
+ mutex_lock(&fw_data->lock);
+ if (!test_bit(WCDCAL_RECIEVED,
+ &fw_data->wcdcal_state[type])) {
+ pr_err("%s: cal not sent by userspace %d\n",
+ __func__, type);
+ mutex_unlock(&fw_data->lock);
+ return NULL;
+ }
+ mutex_unlock(&fw_data->lock);
+ return fw_data->fw[type];
+}
+EXPORT_SYMBOL(wcdcal_get_fw_cal);
+
+static int wcdcal_hwdep_ioctl_shared(struct snd_hwdep *hw,
+ struct wcdcal_ioctl_buffer fw_user)
+{
+ struct fw_info *fw_data = hw->private_data;
+ struct firmware_cal **fw = fw_data->fw;
+ void *data;
+
+ if (!test_bit(fw_user.cal_type, fw_data->cal_bit)) {
+ pr_err("%s: codec didn't set this %d!!\n",
+ __func__, fw_user.cal_type);
+ return -EFAULT;
+ }
+ if (fw_user.cal_type >= WCD9XXX_MAX_CAL ||
+ fw_user.cal_type < WCD9XXX_MIN_CAL) {
+ pr_err("%s: wrong cal type sent %d\n",
+ __func__, fw_user.cal_type);
+ return -EFAULT;
+ }
+ if (fw_user.size > cal_size_info[fw_user.cal_type] ||
+ fw_user.size <= 0) {
+ pr_err("%s: incorrect firmware size %d for %s\n",
+ __func__, fw_user.size,
+ cal_name_info[fw_user.cal_type]);
+ return -EFAULT;
+ }
+ data = fw[fw_user.cal_type]->data;
+ if (copy_from_user(data, fw_user.buffer, fw_user.size))
+ return -EFAULT;
+ fw[fw_user.cal_type]->size = fw_user.size;
+ mutex_lock(&fw_data->lock);
+ set_bit(WCDCAL_RECIEVED, &fw_data->wcdcal_state[fw_user.cal_type]);
+ mutex_unlock(&fw_data->lock);
+ return 0;
+}
+
+#ifdef CONFIG_COMPAT
+struct wcdcal_ioctl_buffer32 {
+ u32 size;
+ compat_uptr_t buffer;
+ enum wcd_cal_type cal_type;
+};
+
+enum {
+ SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE32 =
+ _IOW('U', 0x1, struct wcdcal_ioctl_buffer32),
+};
+
+static int wcdcal_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct wcdcal_ioctl_buffer __user *argp = (void __user *)arg;
+ struct wcdcal_ioctl_buffer32 fw_user32;
+ struct wcdcal_ioctl_buffer fw_user_compat;
+
+ if (cmd != SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE32) {
+ pr_err("%s: wrong ioctl command sent %u!\n", __func__, cmd);
+ return -ENOIOCTLCMD;
+ }
+ if (copy_from_user(&fw_user32, argp, sizeof(fw_user32))) {
+ pr_err("%s: failed to copy\n", __func__);
+ return -EFAULT;
+ }
+ fw_user_compat.size = fw_user32.size;
+ fw_user_compat.buffer = compat_ptr(fw_user32.buffer);
+ fw_user_compat.cal_type = fw_user32.cal_type;
+ return wcdcal_hwdep_ioctl_shared(hw, fw_user_compat);
+}
+#else
+#define wcdcal_hwdep_ioctl_compat NULL
+#endif
+
+static int wcdcal_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct wcdcal_ioctl_buffer __user *argp = (void __user *)arg;
+ struct wcdcal_ioctl_buffer fw_user;
+
+ if (cmd != SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE) {
+ pr_err("%s: wrong ioctl command sent %d!\n", __func__, cmd);
+ return -ENOIOCTLCMD;
+ }
+ if (copy_from_user(&fw_user, argp, sizeof(fw_user))) {
+ pr_err("%s: failed to copy\n", __func__);
+ return -EFAULT;
+ }
+ return wcdcal_hwdep_ioctl_shared(hw, fw_user);
+}
+
+static int wcdcal_hwdep_release(struct snd_hwdep *hw, struct file *file)
+{
+ struct fw_info *fw_data = hw->private_data;
+
+ mutex_lock(&fw_data->lock);
+ /* clear all the calibrations */
+ memset(fw_data->wcdcal_state, 0,
+ sizeof(fw_data->wcdcal_state));
+ mutex_unlock(&fw_data->lock);
+ return 0;
+}
+
+int wcd_cal_create_hwdep(void *data, int node, struct snd_soc_codec *codec)
+{
+ char hwname[40];
+ struct snd_hwdep *hwdep;
+ struct firmware_cal **fw;
+ struct fw_info *fw_data = data;
+ int err, cal_bit;
+
+ if (!fw_data || !codec) {
+ pr_err("%s: wrong arguments passed\n", __func__);
+ return -EINVAL;
+ }
+
+ fw = fw_data->fw;
+ snprintf(hwname, strlen("Codec %s"), "Codec %s",
+ codec->component.name);
+ err = snd_hwdep_new(codec->component.card->snd_card,
+ hwname, node, &hwdep);
+ if (err < 0) {
+ dev_err(codec->dev, "%s: new hwdep failed %d\n",
+ __func__, err);
+ return err;
+ }
+ snprintf(hwdep->name, strlen("Codec %s"), "Codec %s",
+ codec->component.name);
+ hwdep->iface = SNDRV_HWDEP_IFACE_AUDIO_CODEC;
+ hwdep->private_data = fw_data;
+ hwdep->ops.ioctl_compat = wcdcal_hwdep_ioctl_compat;
+ hwdep->ops.ioctl = wcdcal_hwdep_ioctl;
+ hwdep->ops.release = wcdcal_hwdep_release;
+ mutex_init(&fw_data->lock);
+
+ for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
+ set_bit(WCDCAL_UNINITIALISED,
+ &fw_data->wcdcal_state[cal_bit]);
+ fw[cal_bit] = kzalloc(sizeof *(fw[cal_bit]), GFP_KERNEL);
+ if (!fw[cal_bit]) {
+ dev_err(codec->dev, "%s: no memory for %s cal\n",
+ __func__, cal_name_info[cal_bit]);
+ goto end;
+ }
+ }
+ for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
+ fw[cal_bit]->data = kzalloc(cal_size_info[cal_bit],
+ GFP_KERNEL);
+ if (!fw[cal_bit]->data)
+ goto exit;
+ set_bit(WCDCAL_INITIALISED,
+ &fw_data->wcdcal_state[cal_bit]);
+ }
+ return 0;
+exit:
+ for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
+ kfree(fw[cal_bit]->data);
+ fw[cal_bit]->data = NULL;
+ }
+end:
+ for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
+ kfree(fw[cal_bit]);
+ fw[cal_bit] = NULL;
+ }
+ return -ENOMEM;
+}
+EXPORT_SYMBOL(wcd_cal_create_hwdep);