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