blob: 2a73fd675c3436899062371cbe74290cf6d24baa [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* linux/sound/soc/msm/msm7201.c
2 *
Asish Bhattacharya6ab87772012-02-03 06:03:32 -08003 * Copyright (c) 2008-2009, 2011, 2012 Code Aurora Forum. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07004 *
5 * All source code in this file is licensed under the following license except
6 * where indicated.
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License version 2 as published
10 * by the Free Software Foundation.
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.
15 *
16 * See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you can find it at http://www.fsf.org.
19 */
20
21#include <linux/init.h>
22#include <linux/err.h>
23#include <linux/module.h>
24#include <linux/moduleparam.h>
25#include <linux/time.h>
26#include <linux/wait.h>
27#include <linux/platform_device.h>
28#include <sound/core.h>
29#include <sound/soc.h>
30#include <sound/soc-dapm.h>
31#include <sound/pcm.h>
32#include <sound/tlv.h>
33#include <sound/initval.h>
34#include <sound/control.h>
35#include <asm/dma.h>
36#include <linux/dma-mapping.h>
37
38#include "msm-pcm.h"
39#include <asm/mach-types.h>
40#include <mach/msm_rpcrouter.h>
41
42static struct msm_rpc_endpoint *snd_ep;
Asish Bhattacharya6ab87772012-02-03 06:03:32 -080043static uint32_t snd_mute_ear_mute;
44static uint32_t snd_mute_mic_mute;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070045
46struct msm_snd_rpc_ids {
47 unsigned long prog;
48 unsigned long vers;
Manish Dewangan3a260992011-06-24 18:01:34 +053049 unsigned long vers2;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070050 unsigned long rpc_set_snd_device;
Asish Bhattacharya53996cc2012-05-03 17:16:57 +053051 unsigned long rpc_set_device_vol;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070052 int device;
53};
54
55static struct msm_snd_rpc_ids snd_rpc_ids;
56
57static struct platform_device *msm_audio_snd_device;
58
59static int snd_msm_volume_info(struct snd_kcontrol *kcontrol,
60 struct snd_ctl_elem_info *uinfo)
61{
62 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
63 uinfo->count = 1; /* Volume Param, in dB */
64 uinfo->value.integer.min = MIN_DB;
65 uinfo->value.integer.max = MAX_DB;
66 return 0;
67}
68
69static int snd_msm_volume_get(struct snd_kcontrol *kcontrol,
70 struct snd_ctl_elem_value *ucontrol)
71{
72 spin_lock_irq(&the_locks.mixer_lock);
73 ucontrol->value.integer.value[0] = msm_vol_ctl.volume;
74 spin_unlock_irq(&the_locks.mixer_lock);
75 return 0;
76}
77
78static int snd_msm_volume_put(struct snd_kcontrol *kcontrol,
79 struct snd_ctl_elem_value *ucontrol)
80{
81 int change;
82 int volume;
83
84 volume = ucontrol->value.integer.value[0];
85 spin_lock_irq(&the_locks.mixer_lock);
86 change = (msm_vol_ctl.volume != volume);
87 if (change) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070088 msm_vol_ctl.volume = volume;
Phani Kumar Allada798b2352012-04-10 14:53:27 +053089 msm_audio_volume_update(PCMPLAYBACK_DECODERID,
90 msm_vol_ctl.volume, msm_vol_ctl.pan);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070091 }
92 spin_unlock_irq(&the_locks.mixer_lock);
Phani Kumar Allada798b2352012-04-10 14:53:27 +053093 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070094}
95
96static int snd_msm_device_info(struct snd_kcontrol *kcontrol,
97 struct snd_ctl_elem_info *uinfo)
98{
99 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
Manish Dewangan3a260992011-06-24 18:01:34 +0530100 uinfo->count = 3; /* Device */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700101
102 /*
103 * The number of devices supported is 26 (0 to 25)
104 */
105 uinfo->value.integer.min = 0;
Asish Bhattacharya6ab87772012-02-03 06:03:32 -0800106 uinfo->value.integer.max = 36;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700107 return 0;
108}
109
110static int snd_msm_device_get(struct snd_kcontrol *kcontrol,
111 struct snd_ctl_elem_value *ucontrol)
112{
113 ucontrol->value.integer.value[0] = (uint32_t)snd_rpc_ids.device;
Asish Bhattacharya6ab87772012-02-03 06:03:32 -0800114 ucontrol->value.integer.value[1] = snd_mute_ear_mute;
115 ucontrol->value.integer.value[2] = snd_mute_mic_mute;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700116 return 0;
117}
118
119int msm_snd_init_rpc_ids(void)
120{
121 snd_rpc_ids.prog = 0x30000002;
122 snd_rpc_ids.vers = 0x00020001;
Manish Dewangan3a260992011-06-24 18:01:34 +0530123 snd_rpc_ids.vers2 = 0x00030001;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700124 /*
125 * The magic number 2 corresponds to the rpc call
126 * index for snd_set_device
127 */
128 snd_rpc_ids.rpc_set_snd_device = 2;
Asish Bhattacharya53996cc2012-05-03 17:16:57 +0530129 snd_rpc_ids.rpc_set_device_vol = 3;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700130 return 0;
131}
132
133int msm_snd_rpc_connect(void)
134{
135 if (snd_ep) {
136 printk(KERN_INFO "%s: snd_ep already connected\n", __func__);
137 return 0;
138 }
139
140 /* Initialize rpc ids */
141 if (msm_snd_init_rpc_ids()) {
142 printk(KERN_ERR "%s: snd rpc ids initialization failed\n"
143 , __func__);
144 return -ENODATA;
145 }
146
147 snd_ep = msm_rpc_connect_compatible(snd_rpc_ids.prog,
148 snd_rpc_ids.vers, 0);
149 if (IS_ERR(snd_ep)) {
Manish Dewangan3a260992011-06-24 18:01:34 +0530150 printk(KERN_DEBUG "%s failed (compatible VERS = %ld) \
151 trying again with another API\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700152 __func__, snd_rpc_ids.vers);
Manish Dewangan3a260992011-06-24 18:01:34 +0530153 snd_ep =
154 msm_rpc_connect_compatible(snd_rpc_ids.prog,
155 snd_rpc_ids.vers2, 0);
156 }
157 if (IS_ERR(snd_ep)) {
158 printk(KERN_ERR "%s: failed (compatible VERS = %ld)\n",
159 __func__, snd_rpc_ids.vers2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700160 snd_ep = NULL;
161 return -EAGAIN;
162 }
163 return 0;
164}
165
166int msm_snd_rpc_close(void)
167{
168 int rc = 0;
169
170 if (IS_ERR(snd_ep)) {
171 printk(KERN_ERR "%s: snd handle unavailable, rc = %ld\n",
172 __func__, PTR_ERR(snd_ep));
173 return -EAGAIN;
174 }
175
176 rc = msm_rpc_close(snd_ep);
177 snd_ep = NULL;
178
179 if (rc < 0) {
180 printk(KERN_ERR "%s: close rpc failed! rc = %d\n",
181 __func__, rc);
182 return -EAGAIN;
183 } else
184 printk(KERN_INFO "rpc close success\n");
185
186 return rc;
187}
188
189static int snd_msm_device_put(struct snd_kcontrol *kcontrol,
190 struct snd_ctl_elem_value *ucontrol)
191{
192 int rc = 0;
193 struct snd_start_req {
194 struct rpc_request_hdr hdr;
195 uint32_t rpc_snd_device;
196 uint32_t snd_mute_ear_mute;
197 uint32_t snd_mute_mic_mute;
198 uint32_t callback_ptr;
199 uint32_t client_data;
200 } req;
201
202 snd_rpc_ids.device = (int)ucontrol->value.integer.value[0];
Phani Kumar Allada070817d2012-03-22 12:03:16 +0530203
204 if (ucontrol->value.integer.value[1] > 1)
205 ucontrol->value.integer.value[1] = 1;
206 if (ucontrol->value.integer.value[2] > 1)
207 ucontrol->value.integer.value[2] = 1;
208
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700209 req.hdr.type = 0;
210 req.hdr.rpc_vers = 2;
211
212 req.rpc_snd_device = cpu_to_be32(snd_rpc_ids.device);
Manish Dewangan3a260992011-06-24 18:01:34 +0530213 req.snd_mute_ear_mute =
214 cpu_to_be32((int)ucontrol->value.integer.value[1]);
215 req.snd_mute_mic_mute =
216 cpu_to_be32((int)ucontrol->value.integer.value[2]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700217 req.callback_ptr = -1;
218 req.client_data = cpu_to_be32(0);
219
220 req.hdr.prog = snd_rpc_ids.prog;
221 req.hdr.vers = snd_rpc_ids.vers;
222
223 rc = msm_rpc_call(snd_ep, snd_rpc_ids.rpc_set_snd_device ,
224 &req, sizeof(req), 5 * HZ);
225
226 if (rc < 0) {
227 printk(KERN_ERR "%s: snd rpc call failed! rc = %d\n",
228 __func__, rc);
Asish Bhattacharya6ab87772012-02-03 06:03:32 -0800229 } else {
230 printk(KERN_INFO "snd device connected\n");
231 snd_mute_ear_mute = ucontrol->value.integer.value[1];
232 snd_mute_mic_mute = ucontrol->value.integer.value[2];
Phani Kumar Allada070817d2012-03-22 12:03:16 +0530233 printk(KERN_ERR "%s: snd_mute_ear_mute =%d, snd_mute_mic_mute = %d\n",
234 __func__, snd_mute_ear_mute, snd_mute_mic_mute);
Asish Bhattacharya6ab87772012-02-03 06:03:32 -0800235 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700236
237 return rc;
238}
239
Asish Bhattacharya53996cc2012-05-03 17:16:57 +0530240static int snd_msm_device_vol_info(struct snd_kcontrol *kcontrol,
241 struct snd_ctl_elem_info *uinfo)
242{
243 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
244 uinfo->count = 2; /* Device/Volume */
245
246 /*
247 * The number of devices supported is 37 (0 to 36)
248 */
249 uinfo->value.integer.min = 0;
250 uinfo->value.integer.max = 36;
251 return 0;
252}
253
254static int snd_msm_device_vol_put(struct snd_kcontrol *kcontrol,
255 struct snd_ctl_elem_value *ucontrol)
256{
257 int rc = 0;
258 struct snd_vol_req {
259 struct rpc_request_hdr hdr;
260 uint32_t device;
261 uint32_t method;
262 uint32_t volume;
263 uint32_t cb_func;
264 uint32_t client_data;
265 } req;
266
267 snd_rpc_ids.device = (int)ucontrol->value.integer.value[0];
268
269 if ((ucontrol->value.integer.value[1] < 0) ||
270 (ucontrol->value.integer.value[1] > 6)) {
271 pr_err("Device volume should be in range of 1 to 6\n");
272 return -EINVAL;
273 }
274 if ((ucontrol->value.integer.value[0] > 36) ||
275 (ucontrol->value.integer.value[0] < 0)) {
276 pr_err("Device range supported is 0 to 36\n");
277 return -EINVAL;
278 }
279
280 req.device = cpu_to_be32((int)ucontrol->value.integer.value[0]);
281 req.method = cpu_to_be32(0);
282 req.volume = cpu_to_be32((int)ucontrol->value.integer.value[1]);
283 req.cb_func = -1;
284 req.client_data = cpu_to_be32(0);
285
286 rc = msm_rpc_call(snd_ep, snd_rpc_ids.rpc_set_device_vol ,
287 &req, sizeof(req), 5 * HZ);
288
289 if (rc < 0) {
290 printk(KERN_ERR "%s: snd rpc call failed! rc = %d\n",
291 __func__, rc);
292 } else {
293 printk(KERN_ERR "%s: device [%d] volume set to [%d]\n",
294 __func__, (int)ucontrol->value.integer.value[0],
295 (int)ucontrol->value.integer.value[1]);
296 }
297
298 return rc;
299}
300
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700301/* Supported range -50dB to 18dB */
302static const DECLARE_TLV_DB_LINEAR(db_scale_linear, -5000, 1800);
303
304#define MSM_EXT(xname, xindex, fp_info, fp_get, fp_put, addr) \
305{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
306 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
307 .name = xname, .index = xindex, \
308 .info = fp_info,\
309 .get = fp_get, .put = fp_put, \
310 .private_value = addr, \
311}
312
313#define MSM_EXT_TLV(xname, xindex, fp_info, fp_get, fp_put, addr, tlv_array) \
314{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
315 .access = (SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
316 SNDRV_CTL_ELEM_ACCESS_READWRITE), \
317 .name = xname, .index = xindex, \
318 .info = fp_info,\
319 .get = fp_get, .put = fp_put, .tlv.p = tlv_array, \
320 .private_value = addr, \
321}
322
323static struct snd_kcontrol_new snd_msm_controls[] = {
324 MSM_EXT_TLV("PCM Playback Volume", 0, snd_msm_volume_info, \
325 snd_msm_volume_get, snd_msm_volume_put, 0, db_scale_linear),
Phani Kumar Allada070817d2012-03-22 12:03:16 +0530326 MSM_EXT("device", 0, snd_msm_device_info, snd_msm_device_get, \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700327 snd_msm_device_put, 0),
Asish Bhattacharya53996cc2012-05-03 17:16:57 +0530328 MSM_EXT("Device Volume", 0, snd_msm_device_vol_info, NULL, \
329 snd_msm_device_vol_put, 0),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700330};
331
332static int msm_new_mixer(struct snd_soc_codec *codec)
333{
334 unsigned int idx;
335 int err;
336
337 pr_err("msm_soc: ALSA MSM Mixer Setting\n");
338 strcpy(codec->card->snd_card->mixername, "MSM Mixer");
339 for (idx = 0; idx < ARRAY_SIZE(snd_msm_controls); idx++) {
340 err = snd_ctl_add(codec->card->snd_card,
341 snd_ctl_new1(&snd_msm_controls[idx], NULL));
342 if (err < 0)
343 return err;
344 }
345 return 0;
346}
347
348static int msm_soc_dai_init(
349 struct snd_soc_pcm_runtime *rtd)
350{
351 int ret = 0;
352 struct snd_soc_codec *codec = rtd->codec;
353
354 mutex_init(&the_locks.lock);
355 mutex_init(&the_locks.write_lock);
356 mutex_init(&the_locks.read_lock);
357 spin_lock_init(&the_locks.read_dsp_lock);
358 spin_lock_init(&the_locks.write_dsp_lock);
359 spin_lock_init(&the_locks.mixer_lock);
360 init_waitqueue_head(&the_locks.eos_wait);
361 init_waitqueue_head(&the_locks.write_wait);
362 init_waitqueue_head(&the_locks.read_wait);
363 msm_vol_ctl.volume = MSM_PLAYBACK_DEFAULT_VOLUME;
364 msm_vol_ctl.pan = MSM_PLAYBACK_DEFAULT_PAN;
365
366 ret = msm_new_mixer(codec);
367 if (ret < 0) {
368 pr_err("msm_soc: ALSA MSM Mixer Fail\n");
369 }
370
371 return ret;
372}
373
374static struct snd_soc_dai_link msm_dai[] = {
375{
376 .name = "MSM Primary I2S",
377 .stream_name = "DSP 1",
378 .cpu_dai_name = "msm-cpu-dai.0",
379 .platform_name = "msm-dsp-audio.0",
380 .codec_name = "msm-codec-dai.0",
381 .codec_dai_name = "msm-codec-dai",
382 .init = &msm_soc_dai_init,
383},
384};
385
386static struct snd_soc_card snd_soc_card_msm = {
387 .name = "msm-audio",
388 .dai_link = msm_dai,
389 .num_links = ARRAY_SIZE(msm_dai),
390};
391
392static int __init msm_audio_init(void)
393{
394 int ret;
395
396 msm_audio_snd_device = platform_device_alloc("soc-audio", -1);
397 if (!msm_audio_snd_device)
398 return -ENOMEM;
399
400 platform_set_drvdata(msm_audio_snd_device, &snd_soc_card_msm);
401 ret = platform_device_add(msm_audio_snd_device);
402 if (ret) {
403 platform_device_put(msm_audio_snd_device);
404 return ret;
405 }
406
407 ret = msm_snd_rpc_connect();
Phani Kumar Allada070817d2012-03-22 12:03:16 +0530408 snd_mute_ear_mute = 0;
409 snd_mute_mic_mute = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700410
411 return ret;
412}
413
414static void __exit msm_audio_exit(void)
415{
416 msm_snd_rpc_close();
417 platform_device_unregister(msm_audio_snd_device);
418}
419
420module_init(msm_audio_init);
421module_exit(msm_audio_exit);
422
423MODULE_DESCRIPTION("PCM module");
424MODULE_LICENSE("GPL v2");