blob: f098f723b86b9732eec02f6c296222a4261a62e4 [file] [log] [blame]
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301/*
Bala Kishore Patida9885f2018-05-18 21:42:31 +05302 * Copyright (c) 2015, 2017-2018 The Linux Foundation. All rights reserved.
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05303 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 */
14#include <linux/kernel.h>
15#include <linux/platform_device.h>
16#include <linux/slab.h>
17#include <linux/ioctl.h>
18#include <linux/bitops.h>
19#include <sound/hwdep.h>
20#include <sound/msmcal-hwdep.h>
21#include <sound/soc.h>
22#include "wcdcal-hwdep.h"
23
24const int cal_size_info[WCD9XXX_MAX_CAL] = {
25 [WCD9XXX_ANC_CAL] = 16384,
26 [WCD9XXX_MBHC_CAL] = 4096,
27 [WCD9XXX_MAD_CAL] = 4096,
28 [WCD9XXX_VBAT_CAL] = 72,
Bala Kishore Patida9885f2018-05-18 21:42:31 +053029 [BG_CODEC_MIC_CAL] = 20,
30 [BG_CODEC_SPEAKER_CAL] = 3077,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053031};
32
33const char *cal_name_info[WCD9XXX_MAX_CAL] = {
34 [WCD9XXX_ANC_CAL] = "anc",
35 [WCD9XXX_MBHC_CAL] = "mbhc",
36 [WCD9XXX_MAD_CAL] = "mad",
37 [WCD9XXX_VBAT_CAL] = "vbat",
Bala Kishore Patida9885f2018-05-18 21:42:31 +053038 [BG_CODEC_MIC_CAL] = "bgmic",
39 [BG_CODEC_SPEAKER_CAL] = "bgspk",
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053040};
41
42struct firmware_cal *wcdcal_get_fw_cal(struct fw_info *fw_data,
43 enum wcd_cal_type type)
44{
45 if (!fw_data) {
46 pr_err("%s: fw_data is NULL\n", __func__);
47 return NULL;
48 }
49 if (type >= WCD9XXX_MAX_CAL ||
50 type < WCD9XXX_MIN_CAL) {
51 pr_err("%s: wrong cal type sent %d\n", __func__, type);
52 return NULL;
53 }
54 mutex_lock(&fw_data->lock);
55 if (!test_bit(WCDCAL_RECIEVED,
56 &fw_data->wcdcal_state[type])) {
57 pr_err("%s: cal not sent by userspace %d\n",
58 __func__, type);
59 mutex_unlock(&fw_data->lock);
60 return NULL;
61 }
62 mutex_unlock(&fw_data->lock);
63 return fw_data->fw[type];
64}
65EXPORT_SYMBOL(wcdcal_get_fw_cal);
66
67static int wcdcal_hwdep_ioctl_shared(struct snd_hwdep *hw,
68 struct wcdcal_ioctl_buffer fw_user)
69{
70 struct fw_info *fw_data = hw->private_data;
71 struct firmware_cal **fw = fw_data->fw;
72 void *data;
73
74 if (!test_bit(fw_user.cal_type, fw_data->cal_bit)) {
75 pr_err("%s: codec didn't set this %d!!\n",
76 __func__, fw_user.cal_type);
77 return -EFAULT;
78 }
79 if (fw_user.cal_type >= WCD9XXX_MAX_CAL ||
80 fw_user.cal_type < WCD9XXX_MIN_CAL) {
81 pr_err("%s: wrong cal type sent %d\n",
82 __func__, fw_user.cal_type);
83 return -EFAULT;
84 }
85 if (fw_user.size > cal_size_info[fw_user.cal_type] ||
86 fw_user.size <= 0) {
87 pr_err("%s: incorrect firmware size %d for %s\n",
88 __func__, fw_user.size,
89 cal_name_info[fw_user.cal_type]);
90 return -EFAULT;
91 }
92 data = fw[fw_user.cal_type]->data;
93 if (copy_from_user(data, fw_user.buffer, fw_user.size))
94 return -EFAULT;
95 fw[fw_user.cal_type]->size = fw_user.size;
96 mutex_lock(&fw_data->lock);
97 set_bit(WCDCAL_RECIEVED, &fw_data->wcdcal_state[fw_user.cal_type]);
98 mutex_unlock(&fw_data->lock);
99 return 0;
100}
101
102#ifdef CONFIG_COMPAT
103struct wcdcal_ioctl_buffer32 {
104 u32 size;
105 compat_uptr_t buffer;
106 enum wcd_cal_type cal_type;
107};
108
109enum {
110 SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE32 =
111 _IOW('U', 0x1, struct wcdcal_ioctl_buffer32),
112};
113
114static int wcdcal_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file,
115 unsigned int cmd, unsigned long arg)
116{
117 struct wcdcal_ioctl_buffer __user *argp = (void __user *)arg;
118 struct wcdcal_ioctl_buffer32 fw_user32;
119 struct wcdcal_ioctl_buffer fw_user_compat;
120
121 if (cmd != SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE32) {
122 pr_err("%s: wrong ioctl command sent %u!\n", __func__, cmd);
123 return -ENOIOCTLCMD;
124 }
125 if (copy_from_user(&fw_user32, argp, sizeof(fw_user32))) {
126 pr_err("%s: failed to copy\n", __func__);
127 return -EFAULT;
128 }
129 fw_user_compat.size = fw_user32.size;
130 fw_user_compat.buffer = compat_ptr(fw_user32.buffer);
131 fw_user_compat.cal_type = fw_user32.cal_type;
132 return wcdcal_hwdep_ioctl_shared(hw, fw_user_compat);
133}
134#else
135#define wcdcal_hwdep_ioctl_compat NULL
136#endif
137
138static int wcdcal_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
139 unsigned int cmd, unsigned long arg)
140{
141 struct wcdcal_ioctl_buffer __user *argp = (void __user *)arg;
142 struct wcdcal_ioctl_buffer fw_user;
143
144 if (cmd != SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE) {
145 pr_err("%s: wrong ioctl command sent %d!\n", __func__, cmd);
146 return -ENOIOCTLCMD;
147 }
148 if (copy_from_user(&fw_user, argp, sizeof(fw_user))) {
149 pr_err("%s: failed to copy\n", __func__);
150 return -EFAULT;
151 }
152 return wcdcal_hwdep_ioctl_shared(hw, fw_user);
153}
154
155static int wcdcal_hwdep_release(struct snd_hwdep *hw, struct file *file)
156{
157 struct fw_info *fw_data = hw->private_data;
158
159 mutex_lock(&fw_data->lock);
160 /* clear all the calibrations */
161 memset(fw_data->wcdcal_state, 0,
162 sizeof(fw_data->wcdcal_state));
163 mutex_unlock(&fw_data->lock);
164 return 0;
165}
166
167int wcd_cal_create_hwdep(void *data, int node, struct snd_soc_codec *codec)
168{
169 char hwname[40];
170 struct snd_hwdep *hwdep;
171 struct firmware_cal **fw;
172 struct fw_info *fw_data = data;
173 int err, cal_bit;
174
175 if (!fw_data || !codec) {
176 pr_err("%s: wrong arguments passed\n", __func__);
177 return -EINVAL;
178 }
179
180 fw = fw_data->fw;
181 snprintf(hwname, strlen("Codec %s"), "Codec %s",
182 codec->component.name);
183 err = snd_hwdep_new(codec->component.card->snd_card,
184 hwname, node, &hwdep);
185 if (err < 0) {
186 dev_err(codec->dev, "%s: new hwdep failed %d\n",
187 __func__, err);
188 return err;
189 }
190 snprintf(hwdep->name, strlen("Codec %s"), "Codec %s",
191 codec->component.name);
192 hwdep->iface = SNDRV_HWDEP_IFACE_AUDIO_CODEC;
193 hwdep->private_data = fw_data;
194 hwdep->ops.ioctl_compat = wcdcal_hwdep_ioctl_compat;
195 hwdep->ops.ioctl = wcdcal_hwdep_ioctl;
196 hwdep->ops.release = wcdcal_hwdep_release;
197 mutex_init(&fw_data->lock);
198
199 for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
200 set_bit(WCDCAL_UNINITIALISED,
201 &fw_data->wcdcal_state[cal_bit]);
202 fw[cal_bit] = kzalloc(sizeof *(fw[cal_bit]), GFP_KERNEL);
203 if (!fw[cal_bit]) {
204 dev_err(codec->dev, "%s: no memory for %s cal\n",
205 __func__, cal_name_info[cal_bit]);
206 goto end;
207 }
208 }
209 for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
210 fw[cal_bit]->data = kzalloc(cal_size_info[cal_bit],
211 GFP_KERNEL);
Bala Kishore Patida9885f2018-05-18 21:42:31 +0530212 if (!fw[cal_bit]->data) {
213 dev_err(codec->dev, "%s: no memory for %s cal data\n",
214 __func__, cal_name_info[cal_bit]);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530215 goto exit;
Bala Kishore Patida9885f2018-05-18 21:42:31 +0530216 }
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530217 set_bit(WCDCAL_INITIALISED,
218 &fw_data->wcdcal_state[cal_bit]);
219 }
220 return 0;
221exit:
222 for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
223 kfree(fw[cal_bit]->data);
224 fw[cal_bit]->data = NULL;
225 }
226end:
227 for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
228 kfree(fw[cal_bit]);
229 fw[cal_bit] = NULL;
230 }
231 return -ENOMEM;
232}
233EXPORT_SYMBOL(wcd_cal_create_hwdep);