blob: f9097e339b24a8a20f3b50aaa2338b6db5c31b7f [file] [log] [blame]
Meng Wang43bbb872018-12-10 12:32:05 +08001// SPDX-License-Identifier: GPL-2.0-only
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05302/*
Meng Wang15c825d2018-09-06 10:49:18 +08003 * Copyright (c) 2015, 2017-2018 The Linux Foundation. All rights reserved.
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05304 *
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05305 */
6#include <linux/kernel.h>
7#include <linux/platform_device.h>
8#include <linux/slab.h>
9#include <linux/ioctl.h>
10#include <linux/bitops.h>
11#include <sound/hwdep.h>
12#include <sound/msmcal-hwdep.h>
13#include <sound/soc.h>
Meng Wang11a25cf2018-10-31 14:11:26 +080014#include <asoc/wcdcal-hwdep.h>
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053015
16const int cal_size_info[WCD9XXX_MAX_CAL] = {
17 [WCD9XXX_ANC_CAL] = 16384,
18 [WCD9XXX_MBHC_CAL] = 4096,
19 [WCD9XXX_MAD_CAL] = 4096,
20 [WCD9XXX_VBAT_CAL] = 72,
21};
22
23const char *cal_name_info[WCD9XXX_MAX_CAL] = {
24 [WCD9XXX_ANC_CAL] = "anc",
25 [WCD9XXX_MBHC_CAL] = "mbhc",
26 [WCD9XXX_MAD_CAL] = "mad",
27 [WCD9XXX_VBAT_CAL] = "vbat",
28};
29
30struct firmware_cal *wcdcal_get_fw_cal(struct fw_info *fw_data,
31 enum wcd_cal_type type)
32{
33 if (!fw_data) {
34 pr_err("%s: fw_data is NULL\n", __func__);
35 return NULL;
36 }
37 if (type >= WCD9XXX_MAX_CAL ||
38 type < WCD9XXX_MIN_CAL) {
39 pr_err("%s: wrong cal type sent %d\n", __func__, type);
40 return NULL;
41 }
42 mutex_lock(&fw_data->lock);
43 if (!test_bit(WCDCAL_RECIEVED,
44 &fw_data->wcdcal_state[type])) {
45 pr_err("%s: cal not sent by userspace %d\n",
46 __func__, type);
47 mutex_unlock(&fw_data->lock);
48 return NULL;
49 }
50 mutex_unlock(&fw_data->lock);
51 return fw_data->fw[type];
52}
53EXPORT_SYMBOL(wcdcal_get_fw_cal);
54
55static int wcdcal_hwdep_ioctl_shared(struct snd_hwdep *hw,
56 struct wcdcal_ioctl_buffer fw_user)
57{
58 struct fw_info *fw_data = hw->private_data;
59 struct firmware_cal **fw = fw_data->fw;
60 void *data;
61
62 if (!test_bit(fw_user.cal_type, fw_data->cal_bit)) {
63 pr_err("%s: codec didn't set this %d!!\n",
64 __func__, fw_user.cal_type);
65 return -EFAULT;
66 }
67 if (fw_user.cal_type >= WCD9XXX_MAX_CAL ||
68 fw_user.cal_type < WCD9XXX_MIN_CAL) {
69 pr_err("%s: wrong cal type sent %d\n",
70 __func__, fw_user.cal_type);
71 return -EFAULT;
72 }
73 if (fw_user.size > cal_size_info[fw_user.cal_type] ||
74 fw_user.size <= 0) {
75 pr_err("%s: incorrect firmware size %d for %s\n",
76 __func__, fw_user.size,
77 cal_name_info[fw_user.cal_type]);
78 return -EFAULT;
79 }
80 data = fw[fw_user.cal_type]->data;
81 if (copy_from_user(data, fw_user.buffer, fw_user.size))
82 return -EFAULT;
83 fw[fw_user.cal_type]->size = fw_user.size;
84 mutex_lock(&fw_data->lock);
85 set_bit(WCDCAL_RECIEVED, &fw_data->wcdcal_state[fw_user.cal_type]);
86 mutex_unlock(&fw_data->lock);
87 return 0;
88}
89
90#ifdef CONFIG_COMPAT
91struct wcdcal_ioctl_buffer32 {
92 u32 size;
93 compat_uptr_t buffer;
94 enum wcd_cal_type cal_type;
95};
96
97enum {
98 SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE32 =
99 _IOW('U', 0x1, struct wcdcal_ioctl_buffer32),
100};
101
102static int wcdcal_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file,
103 unsigned int cmd, unsigned long arg)
104{
105 struct wcdcal_ioctl_buffer __user *argp = (void __user *)arg;
106 struct wcdcal_ioctl_buffer32 fw_user32;
107 struct wcdcal_ioctl_buffer fw_user_compat;
108
109 if (cmd != SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE32) {
110 pr_err("%s: wrong ioctl command sent %u!\n", __func__, cmd);
111 return -ENOIOCTLCMD;
112 }
113 if (copy_from_user(&fw_user32, argp, sizeof(fw_user32))) {
114 pr_err("%s: failed to copy\n", __func__);
115 return -EFAULT;
116 }
117 fw_user_compat.size = fw_user32.size;
118 fw_user_compat.buffer = compat_ptr(fw_user32.buffer);
119 fw_user_compat.cal_type = fw_user32.cal_type;
120 return wcdcal_hwdep_ioctl_shared(hw, fw_user_compat);
121}
122#else
123#define wcdcal_hwdep_ioctl_compat NULL
124#endif
125
126static int wcdcal_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
127 unsigned int cmd, unsigned long arg)
128{
129 struct wcdcal_ioctl_buffer __user *argp = (void __user *)arg;
130 struct wcdcal_ioctl_buffer fw_user;
131
132 if (cmd != SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE) {
133 pr_err("%s: wrong ioctl command sent %d!\n", __func__, cmd);
134 return -ENOIOCTLCMD;
135 }
136 if (copy_from_user(&fw_user, argp, sizeof(fw_user))) {
137 pr_err("%s: failed to copy\n", __func__);
138 return -EFAULT;
139 }
140 return wcdcal_hwdep_ioctl_shared(hw, fw_user);
141}
142
143static int wcdcal_hwdep_release(struct snd_hwdep *hw, struct file *file)
144{
145 struct fw_info *fw_data = hw->private_data;
146
147 mutex_lock(&fw_data->lock);
148 /* clear all the calibrations */
149 memset(fw_data->wcdcal_state, 0,
150 sizeof(fw_data->wcdcal_state));
151 mutex_unlock(&fw_data->lock);
152 return 0;
153}
154
Meng Wang15c825d2018-09-06 10:49:18 +0800155int wcd_cal_create_hwdep(void *data, int node,
156 struct snd_soc_component *component)
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530157{
158 char hwname[40];
159 struct snd_hwdep *hwdep;
160 struct firmware_cal **fw;
161 struct fw_info *fw_data = data;
162 int err, cal_bit;
163
Meng Wang15c825d2018-09-06 10:49:18 +0800164 if (!fw_data || !component) {
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530165 pr_err("%s: wrong arguments passed\n", __func__);
166 return -EINVAL;
167 }
168
169 fw = fw_data->fw;
170 snprintf(hwname, strlen("Codec %s"), "Codec %s",
Meng Wang15c825d2018-09-06 10:49:18 +0800171 component->name);
172 err = snd_hwdep_new(component->card->snd_card,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530173 hwname, node, &hwdep);
174 if (err < 0) {
Meng Wang15c825d2018-09-06 10:49:18 +0800175 dev_err(component->dev, "%s: new hwdep failed %d\n",
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530176 __func__, err);
177 return err;
178 }
179 snprintf(hwdep->name, strlen("Codec %s"), "Codec %s",
Meng Wang15c825d2018-09-06 10:49:18 +0800180 component->name);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530181 hwdep->iface = SNDRV_HWDEP_IFACE_AUDIO_CODEC;
182 hwdep->private_data = fw_data;
183 hwdep->ops.ioctl_compat = wcdcal_hwdep_ioctl_compat;
184 hwdep->ops.ioctl = wcdcal_hwdep_ioctl;
185 hwdep->ops.release = wcdcal_hwdep_release;
186 mutex_init(&fw_data->lock);
187
188 for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
189 set_bit(WCDCAL_UNINITIALISED,
190 &fw_data->wcdcal_state[cal_bit]);
191 fw[cal_bit] = kzalloc(sizeof *(fw[cal_bit]), GFP_KERNEL);
Meng Wang15c825d2018-09-06 10:49:18 +0800192 if (!fw[cal_bit])
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530193 goto end;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530194 }
195 for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
196 fw[cal_bit]->data = kzalloc(cal_size_info[cal_bit],
197 GFP_KERNEL);
198 if (!fw[cal_bit]->data)
199 goto exit;
200 set_bit(WCDCAL_INITIALISED,
201 &fw_data->wcdcal_state[cal_bit]);
202 }
203 return 0;
204exit:
205 for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
206 kfree(fw[cal_bit]->data);
207 fw[cal_bit]->data = NULL;
208 }
209end:
210 for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
211 kfree(fw[cal_bit]);
212 fw[cal_bit] = NULL;
213 }
214 return -ENOMEM;
215}
216EXPORT_SYMBOL(wcd_cal_create_hwdep);