blob: 6408cefb3562852b40d326d5d2996971770a342a [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;
51 int device;
52};
53
54static struct msm_snd_rpc_ids snd_rpc_ids;
55
56static struct platform_device *msm_audio_snd_device;
57
58static int snd_msm_volume_info(struct snd_kcontrol *kcontrol,
59 struct snd_ctl_elem_info *uinfo)
60{
61 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
62 uinfo->count = 1; /* Volume Param, in dB */
63 uinfo->value.integer.min = MIN_DB;
64 uinfo->value.integer.max = MAX_DB;
65 return 0;
66}
67
68static int snd_msm_volume_get(struct snd_kcontrol *kcontrol,
69 struct snd_ctl_elem_value *ucontrol)
70{
71 spin_lock_irq(&the_locks.mixer_lock);
72 ucontrol->value.integer.value[0] = msm_vol_ctl.volume;
73 spin_unlock_irq(&the_locks.mixer_lock);
74 return 0;
75}
76
77static int snd_msm_volume_put(struct snd_kcontrol *kcontrol,
78 struct snd_ctl_elem_value *ucontrol)
79{
80 int change;
81 int volume;
82
83 volume = ucontrol->value.integer.value[0];
84 spin_lock_irq(&the_locks.mixer_lock);
85 change = (msm_vol_ctl.volume != volume);
86 if (change) {
87 msm_vol_ctl.update = 1;
88 msm_vol_ctl.volume = volume;
89 }
90 spin_unlock_irq(&the_locks.mixer_lock);
91 return change;
92}
93
94static int snd_msm_device_info(struct snd_kcontrol *kcontrol,
95 struct snd_ctl_elem_info *uinfo)
96{
97 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
Manish Dewangan3a260992011-06-24 18:01:34 +053098 uinfo->count = 3; /* Device */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070099
100 /*
101 * The number of devices supported is 26 (0 to 25)
102 */
103 uinfo->value.integer.min = 0;
Asish Bhattacharya6ab87772012-02-03 06:03:32 -0800104 uinfo->value.integer.max = 36;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700105 return 0;
106}
107
108static int snd_msm_device_get(struct snd_kcontrol *kcontrol,
109 struct snd_ctl_elem_value *ucontrol)
110{
111 ucontrol->value.integer.value[0] = (uint32_t)snd_rpc_ids.device;
Asish Bhattacharya6ab87772012-02-03 06:03:32 -0800112 ucontrol->value.integer.value[1] = snd_mute_ear_mute;
113 ucontrol->value.integer.value[2] = snd_mute_mic_mute;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700114 return 0;
115}
116
117int msm_snd_init_rpc_ids(void)
118{
119 snd_rpc_ids.prog = 0x30000002;
120 snd_rpc_ids.vers = 0x00020001;
Manish Dewangan3a260992011-06-24 18:01:34 +0530121 snd_rpc_ids.vers2 = 0x00030001;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700122 /*
123 * The magic number 2 corresponds to the rpc call
124 * index for snd_set_device
125 */
126 snd_rpc_ids.rpc_set_snd_device = 2;
127 return 0;
128}
129
130int msm_snd_rpc_connect(void)
131{
132 if (snd_ep) {
133 printk(KERN_INFO "%s: snd_ep already connected\n", __func__);
134 return 0;
135 }
136
137 /* Initialize rpc ids */
138 if (msm_snd_init_rpc_ids()) {
139 printk(KERN_ERR "%s: snd rpc ids initialization failed\n"
140 , __func__);
141 return -ENODATA;
142 }
143
144 snd_ep = msm_rpc_connect_compatible(snd_rpc_ids.prog,
145 snd_rpc_ids.vers, 0);
146 if (IS_ERR(snd_ep)) {
Manish Dewangan3a260992011-06-24 18:01:34 +0530147 printk(KERN_DEBUG "%s failed (compatible VERS = %ld) \
148 trying again with another API\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700149 __func__, snd_rpc_ids.vers);
Manish Dewangan3a260992011-06-24 18:01:34 +0530150 snd_ep =
151 msm_rpc_connect_compatible(snd_rpc_ids.prog,
152 snd_rpc_ids.vers2, 0);
153 }
154 if (IS_ERR(snd_ep)) {
155 printk(KERN_ERR "%s: failed (compatible VERS = %ld)\n",
156 __func__, snd_rpc_ids.vers2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700157 snd_ep = NULL;
158 return -EAGAIN;
159 }
160 return 0;
161}
162
163int msm_snd_rpc_close(void)
164{
165 int rc = 0;
166
167 if (IS_ERR(snd_ep)) {
168 printk(KERN_ERR "%s: snd handle unavailable, rc = %ld\n",
169 __func__, PTR_ERR(snd_ep));
170 return -EAGAIN;
171 }
172
173 rc = msm_rpc_close(snd_ep);
174 snd_ep = NULL;
175
176 if (rc < 0) {
177 printk(KERN_ERR "%s: close rpc failed! rc = %d\n",
178 __func__, rc);
179 return -EAGAIN;
180 } else
181 printk(KERN_INFO "rpc close success\n");
182
183 return rc;
184}
185
186static int snd_msm_device_put(struct snd_kcontrol *kcontrol,
187 struct snd_ctl_elem_value *ucontrol)
188{
189 int rc = 0;
190 struct snd_start_req {
191 struct rpc_request_hdr hdr;
192 uint32_t rpc_snd_device;
193 uint32_t snd_mute_ear_mute;
194 uint32_t snd_mute_mic_mute;
195 uint32_t callback_ptr;
196 uint32_t client_data;
197 } req;
198
199 snd_rpc_ids.device = (int)ucontrol->value.integer.value[0];
200 req.hdr.type = 0;
201 req.hdr.rpc_vers = 2;
202
203 req.rpc_snd_device = cpu_to_be32(snd_rpc_ids.device);
Manish Dewangan3a260992011-06-24 18:01:34 +0530204 req.snd_mute_ear_mute =
205 cpu_to_be32((int)ucontrol->value.integer.value[1]);
206 req.snd_mute_mic_mute =
207 cpu_to_be32((int)ucontrol->value.integer.value[2]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700208 req.callback_ptr = -1;
209 req.client_data = cpu_to_be32(0);
210
211 req.hdr.prog = snd_rpc_ids.prog;
212 req.hdr.vers = snd_rpc_ids.vers;
213
214 rc = msm_rpc_call(snd_ep, snd_rpc_ids.rpc_set_snd_device ,
215 &req, sizeof(req), 5 * HZ);
216
217 if (rc < 0) {
218 printk(KERN_ERR "%s: snd rpc call failed! rc = %d\n",
219 __func__, rc);
Asish Bhattacharya6ab87772012-02-03 06:03:32 -0800220 } else {
221 printk(KERN_INFO "snd device connected\n");
222 snd_mute_ear_mute = ucontrol->value.integer.value[1];
223 snd_mute_mic_mute = ucontrol->value.integer.value[2];
224 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700225
226 return rc;
227}
228
229/* Supported range -50dB to 18dB */
230static const DECLARE_TLV_DB_LINEAR(db_scale_linear, -5000, 1800);
231
232#define MSM_EXT(xname, xindex, fp_info, fp_get, fp_put, addr) \
233{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
234 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
235 .name = xname, .index = xindex, \
236 .info = fp_info,\
237 .get = fp_get, .put = fp_put, \
238 .private_value = addr, \
239}
240
241#define MSM_EXT_TLV(xname, xindex, fp_info, fp_get, fp_put, addr, tlv_array) \
242{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
243 .access = (SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
244 SNDRV_CTL_ELEM_ACCESS_READWRITE), \
245 .name = xname, .index = xindex, \
246 .info = fp_info,\
247 .get = fp_get, .put = fp_put, .tlv.p = tlv_array, \
248 .private_value = addr, \
249}
250
251static struct snd_kcontrol_new snd_msm_controls[] = {
252 MSM_EXT_TLV("PCM Playback Volume", 0, snd_msm_volume_info, \
253 snd_msm_volume_get, snd_msm_volume_put, 0, db_scale_linear),
254 MSM_EXT("device", 1, snd_msm_device_info, snd_msm_device_get, \
255 snd_msm_device_put, 0),
256};
257
258static int msm_new_mixer(struct snd_soc_codec *codec)
259{
260 unsigned int idx;
261 int err;
262
263 pr_err("msm_soc: ALSA MSM Mixer Setting\n");
264 strcpy(codec->card->snd_card->mixername, "MSM Mixer");
265 for (idx = 0; idx < ARRAY_SIZE(snd_msm_controls); idx++) {
266 err = snd_ctl_add(codec->card->snd_card,
267 snd_ctl_new1(&snd_msm_controls[idx], NULL));
268 if (err < 0)
269 return err;
270 }
271 return 0;
272}
273
274static int msm_soc_dai_init(
275 struct snd_soc_pcm_runtime *rtd)
276{
277 int ret = 0;
278 struct snd_soc_codec *codec = rtd->codec;
279
280 mutex_init(&the_locks.lock);
281 mutex_init(&the_locks.write_lock);
282 mutex_init(&the_locks.read_lock);
283 spin_lock_init(&the_locks.read_dsp_lock);
284 spin_lock_init(&the_locks.write_dsp_lock);
285 spin_lock_init(&the_locks.mixer_lock);
286 init_waitqueue_head(&the_locks.eos_wait);
287 init_waitqueue_head(&the_locks.write_wait);
288 init_waitqueue_head(&the_locks.read_wait);
289 msm_vol_ctl.volume = MSM_PLAYBACK_DEFAULT_VOLUME;
290 msm_vol_ctl.pan = MSM_PLAYBACK_DEFAULT_PAN;
291
292 ret = msm_new_mixer(codec);
293 if (ret < 0) {
294 pr_err("msm_soc: ALSA MSM Mixer Fail\n");
295 }
296
297 return ret;
298}
299
300static struct snd_soc_dai_link msm_dai[] = {
301{
302 .name = "MSM Primary I2S",
303 .stream_name = "DSP 1",
304 .cpu_dai_name = "msm-cpu-dai.0",
305 .platform_name = "msm-dsp-audio.0",
306 .codec_name = "msm-codec-dai.0",
307 .codec_dai_name = "msm-codec-dai",
308 .init = &msm_soc_dai_init,
309},
310};
311
312static struct snd_soc_card snd_soc_card_msm = {
313 .name = "msm-audio",
314 .dai_link = msm_dai,
315 .num_links = ARRAY_SIZE(msm_dai),
316};
317
318static int __init msm_audio_init(void)
319{
320 int ret;
321
322 msm_audio_snd_device = platform_device_alloc("soc-audio", -1);
323 if (!msm_audio_snd_device)
324 return -ENOMEM;
325
326 platform_set_drvdata(msm_audio_snd_device, &snd_soc_card_msm);
327 ret = platform_device_add(msm_audio_snd_device);
328 if (ret) {
329 platform_device_put(msm_audio_snd_device);
330 return ret;
331 }
332
333 ret = msm_snd_rpc_connect();
334
335 return ret;
336}
337
338static void __exit msm_audio_exit(void)
339{
340 msm_snd_rpc_close();
341 platform_device_unregister(msm_audio_snd_device);
342}
343
344module_init(msm_audio_init);
345module_exit(msm_audio_exit);
346
347MODULE_DESCRIPTION("PCM module");
348MODULE_LICENSE("GPL v2");