blob: 4528f87e9837334d58f1fe97651aa8a60f1e419c [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13
14#include <linux/init.h>
15#include <linux/err.h>
16#include <linux/module.h>
17#include <linux/moduleparam.h>
18#include <linux/platform_device.h>
19#include <linux/bitops.h>
20#include <sound/core.h>
21#include <sound/soc.h>
22#include <sound/soc-dapm.h>
23#include <sound/pcm.h>
24#include <sound/initval.h>
25#include <sound/control.h>
26#include <sound/q6adm.h>
27#include <sound/q6afe.h>
Jayasena Sangaraboinacb1e22f2011-07-18 10:36:57 -070028#include <sound/tlv.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070029#include "msm-pcm-routing.h"
30#include "qdsp6/q6voice.h"
31
32struct audio_mixer_data {
33 u32 port_id; /* AFE port ID for Rx, FE DAI ID for TX */
34 unsigned long dai_sessions; /* Rx: FE DAIs Tx: BE DAI */
35 u32 mixer_type; /* playback or capture */
36};
37
38#define INVALID_SESSION -1
39
40enum {
41 AUDIO_MIXER_PRI_I2S_RX = 0,
42 AUDIO_MIXER_SLIMBUS_0_RX,
43 AUDIO_MIXER_HDMI_RX,
44 AUDIO_MIXER_MM_UL1,
45 AUDIO_MIXER_INT_BT_SCO_RX,
46 AUDIO_MIXER_INT_FM_RX,
47 AUDIO_MIXER_MAX,
48};
49
50enum {
51 AUDIO_PORT_MIXER_SLIM_0_RX = 0,
52 AUDIO_PORT_MIXER_MAX,
53};
54
Sriranjan Srikantama4969dd2011-07-14 00:34:56 -070055static int fm_switch_enable;
Jayasena Sangaraboinacb1e22f2011-07-18 10:36:57 -070056#define INT_FM_RX_VOL_MAX_STEPS 100
57#define INT_FM_RX_VOL_GAIN 2000
58
59static int msm_route_fm_vol_control;
60static const DECLARE_TLV_DB_SCALE(fm_rx_vol_gain, 0,
61 INT_FM_RX_VOL_MAX_STEPS, 0);
Sriranjan Srikantama4969dd2011-07-14 00:34:56 -070062
Asish Bhattacharya0ec76182011-07-29 16:58:11 +053063#define INT_LPA_RX_VOL_MAX_STEPS 0x100
64#define INT_LPA_RX_VOL_GAIN 0x3FFF
65
66static int msm_route_lpa_vol_control;
67static const DECLARE_TLV_DB_SCALE(lpa_rx_vol_gain, 0,
68 INT_LPA_RX_VOL_MAX_STEPS, 0);
69
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070070/* Tx mixer session is stored based on BE DAI ID
71 * Need to map to actual AFE port ID since AFE port
72 * ID can get really large.
73 * The table convert DAI back to AFE port ID
74 */
75static int bedai_port_map[MSM_BACKEND_DAI_MAX] = {
76 PRIMARY_I2S_RX,
77 PRIMARY_I2S_TX,
78 SLIMBUS_0_RX,
79 SLIMBUS_0_TX,
80 HDMI_RX,
81 INT_BT_SCO_RX,
82 INT_BT_SCO_TX,
83 INT_FM_RX,
84 INT_FM_TX,
85};
86
87/* Track ASM playback & capture sessions of DAI */
88static int fe_dai_map[MSM_FRONTEND_DAI_MAX][2] = {
89 /* MULTIMEDIA1 */
90 {INVALID_SESSION, INVALID_SESSION},
91 /* MULTIMEDIA2 */
92 {INVALID_SESSION, INVALID_SESSION},
93 /* MULTIMEDIA3 */
94 {INVALID_SESSION, INVALID_SESSION},
95};
96
97static struct audio_mixer_data audio_mixers[AUDIO_MIXER_MAX] = {
98 /* AUDIO_MIXER_PRI_I2S_RX */
99 {PRIMARY_I2S_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
100 /* AUDIO_MIXER_SLIMBUS_0_RX */
101 {SLIMBUS_0_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
102 /* AUDIO_MIXER_HDMI_RX */
103 {HDMI_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
104 /* AUDIO_MIXER_MM_UL1 */
105 {MSM_FRONTEND_DAI_MULTIMEDIA1, 0, SNDRV_PCM_STREAM_CAPTURE},
106 /* AUDIO_MIXER_INT_BT_SCO_RX */
107 {INT_BT_SCO_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
108 /* AUDIO_MIXER_INT_FM_RX */
109 {INT_FM_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
110};
111static struct voice_mixer_data voice_mixers[VOICE_MIXER_MAX] = {
112 /* VOICE_MIXER_PRI_I2S_RX */
113 {VOICE_PRI_I2S_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
114 /* VOICE_MIXER_SLIMBUS_0_RX */
115 {VOICE_SLIMBUS_0_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
116 /* VOICE_MIXER_PRI_I2S_TX */
117 {VOICE_PRI_I2S_TX, 0, SNDRV_PCM_STREAM_CAPTURE},
118 /* VOICE_MIXER_SLIMBUS_0_TX */
119 {VOICE_SLIMBUS_0_TX, 0, SNDRV_PCM_STREAM_CAPTURE},
120 /* VOICE_MIXER_INT_BT_SCO_RX */
121 {VOICE_INT_BT_SCO_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
122 /* VOICE_MIXER_INT_BT_SCO_TX */
123 {VOICE_INT_BT_SCO_TX, 0, SNDRV_PCM_STREAM_CAPTURE}
124};
125
126/* Reuse audio_mixer_data struct but ignore mixer type field
127 * unless there is use case for RX -> TX
128 */
129static struct audio_mixer_data audio_port_mixers[AUDIO_PORT_MIXER_MAX] = {
130 /* AUDIO_PORT_MIXER_SLIM_0_RX */
131 {SLIMBUS_0_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
132};
133
134void msm_pcm_routing_reg_phy_stream(int fedai_id, int dspst_id, int stream_type)
135{
136 int i, be_id;
137
138 if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) {
139 fe_dai_map[fedai_id][0] = dspst_id;
140 for (i = 0; i < AUDIO_MIXER_MAX; i++) {
141 if ((audio_mixers[i].mixer_type == stream_type) &&
142 (test_bit(fedai_id, &audio_mixers[i].dai_sessions)))
143 /* To do: multiple devices case */
144 adm_route_session(audio_mixers[i].port_id,
145 dspst_id, 1);
146 }
147 } else {
148 fe_dai_map[fedai_id][1] = dspst_id;
149 for (i = 0; i < AUDIO_MIXER_MAX; i++) {
150 if ((audio_mixers[i].mixer_type == stream_type) &&
151 (fedai_id == audio_mixers[i].port_id)) {
152 /* To-do: Handle mixing of inputs */
153 be_id = find_next_bit(
154 &audio_mixers[i].dai_sessions,
155 MSM_BACKEND_DAI_MAX, 0);
156 if (be_id < MSM_BACKEND_DAI_MAX)
157 adm_route_session(bedai_port_map[be_id],
158 dspst_id, 1);
159 else
160 pr_err("%s: no routing\n", __func__);
161 }
162 }
163 }
164}
165
166void msm_pcm_routing_dereg_phy_stream(int fedai_id, int stream_type)
167{
168 int i, be_id;
169
170 if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) {
171 for (i = 0; i < AUDIO_MIXER_MAX; i++) {
172 if ((audio_mixers[i].mixer_type == stream_type) &&
173 (test_bit(fedai_id, &audio_mixers[i].dai_sessions))) {
174 /* To do: multiple devices case */
175 adm_route_session(audio_mixers[i].port_id,
176 fe_dai_map[fedai_id][0], 0);
177 }
178 }
179 fe_dai_map[fedai_id][0] = INVALID_SESSION;
180 } else {
181 for (i = 0; i < AUDIO_MIXER_MAX; i++) {
182 if ((audio_mixers[i].mixer_type == stream_type) &&
183 (fedai_id == audio_mixers[i].port_id)) {
184 /* To-do: Handle mixing of inputs */
185 be_id = find_next_bit(
186 &audio_mixers[i].dai_sessions,
187 MSM_BACKEND_DAI_MAX, 0);
188 if (be_id < MSM_BACKEND_DAI_MAX)
189 adm_route_session(bedai_port_map[be_id],
190 fe_dai_map[fedai_id][1], 0);
191 else
192 pr_err("%s: no routing\n", __func__);
193 }
194 }
195 fe_dai_map[fedai_id][1] = INVALID_SESSION;
196 }
197}
198
199static void msm_pcm_routing_process_audio(u16 reg, u16 val, int set)
200{
201
202 pr_debug("%s: reg %x val %x set %x\n", __func__, reg, val, set);
203
204 if (set)
205 set_bit(val, &audio_mixers[reg].dai_sessions);
206 else
207 clear_bit(val, &audio_mixers[reg].dai_sessions);
208
209 if (audio_mixers[reg].mixer_type == SNDRV_PCM_STREAM_PLAYBACK) {
210 if (fe_dai_map[val][0] != INVALID_SESSION)
211 adm_route_session(audio_mixers[reg].port_id,
212 fe_dai_map[val][0], set);
213 } else {
214 int fe_id = audio_mixers[reg].port_id;
215 if (fe_dai_map[fe_id][1] != INVALID_SESSION)
216 adm_route_session(bedai_port_map[val],
217 fe_dai_map[fe_id][1], set);
218 }
219}
220
221static int msm_routing_get_audio_mixer(struct snd_kcontrol *kcontrol,
222 struct snd_ctl_elem_value *ucontrol)
223{
224 struct soc_mixer_control *mc =
225 (struct soc_mixer_control *)kcontrol->private_value;
226
227 if (test_bit(mc->shift, &audio_mixers[mc->reg].dai_sessions))
228 ucontrol->value.integer.value[0] = 1;
229 else
230 ucontrol->value.integer.value[0] = 0;
231
232 pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
233 ucontrol->value.integer.value[0]);
234
235 return 0;
236}
237
238static int msm_routing_put_audio_mixer(struct snd_kcontrol *kcontrol,
239 struct snd_ctl_elem_value *ucontrol)
240{
Patrick Laiec2b8942011-09-01 11:01:51 -0700241 struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
242 struct snd_soc_dapm_widget *widget = wlist->widgets[0];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700243 struct soc_mixer_control *mc =
244 (struct soc_mixer_control *)kcontrol->private_value;
245
246
247 if (ucontrol->value.integer.value[0]) {
248 msm_pcm_routing_process_audio(mc->reg, mc->shift, 1);
249 snd_soc_dapm_mixer_update_power(widget, kcontrol, 1);
250 } else {
251 msm_pcm_routing_process_audio(mc->reg, mc->shift, 0);
252 snd_soc_dapm_mixer_update_power(widget, kcontrol, 0);
253 }
254
255 return 1;
256}
257
Neema Shetty2c07eb52011-08-21 20:33:52 -0700258static void msm_pcm_routing_process_voice(u16 reg, u16 val, u16 invert, int set)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700259{
260
261 u32 port_map_id;
Neema Shetty2c07eb52011-08-21 20:33:52 -0700262 u16 session_id = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700263
Neema Shetty2c07eb52011-08-21 20:33:52 -0700264 pr_debug("%s: reg %x val %x invert %x set %x\n",
265 __func__, reg, val, invert, set);
266
267 if (invert == MSM_FRONTEND_DAI_CS_VOICE)
268 session_id = voc_get_session_id(VOICE_SESSION_NAME);
269 else
270 session_id = voc_get_session_id(VOIP_SESSION_NAME);
271
272 pr_debug("%s: FE DAI 0x%x session_id 0x%x\n",
273 __func__, invert, session_id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700274
275 port_map_id = voice_mixers[reg].port_id;
276
277 if (set)
278 set_bit(val, &voice_mixers[reg].dai_sessions);
279 else
280 clear_bit(val, &voice_mixers[reg].dai_sessions);
281
282 if (voice_mixers[reg].mixer_type == SNDRV_PCM_STREAM_PLAYBACK) {
Neema Shetty2c07eb52011-08-21 20:33:52 -0700283 voc_set_route_flag(session_id, RX_PATH, set);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700284 if (set) {
Neema Shetty2c07eb52011-08-21 20:33:52 -0700285 voc_set_rxtx_port(session_id,
286 bedai_port_map[port_map_id],
287 DEV_RX);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700288
Neema Shetty2c07eb52011-08-21 20:33:52 -0700289 if (voc_get_route_flag(session_id, RX_PATH) &&
290 voc_get_route_flag(session_id, TX_PATH))
291 voc_enable_cvp(session_id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700292 } else {
Neema Shetty2c07eb52011-08-21 20:33:52 -0700293 voc_disable_cvp(session_id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700294 }
295 } else {
Neema Shetty2c07eb52011-08-21 20:33:52 -0700296 voc_set_route_flag(session_id, TX_PATH, set);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700297 if (set) {
Neema Shetty2c07eb52011-08-21 20:33:52 -0700298 voc_set_rxtx_port(session_id,
299 bedai_port_map[port_map_id],
300 DEV_TX);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700301
Neema Shetty2c07eb52011-08-21 20:33:52 -0700302 if (voc_get_route_flag(session_id, RX_PATH) &&
303 voc_get_route_flag(session_id, TX_PATH))
304 voc_enable_cvp(session_id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700305 } else {
Neema Shetty2c07eb52011-08-21 20:33:52 -0700306 voc_disable_cvp(session_id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700307 }
308 }
309}
310
311static int msm_routing_get_voice_mixer(struct snd_kcontrol *kcontrol,
312 struct snd_ctl_elem_value *ucontrol)
313{
314 struct soc_mixer_control *mc =
315 (struct soc_mixer_control *)kcontrol->private_value;
316
317 if (test_bit(mc->shift, &voice_mixers[mc->reg].dai_sessions))
318 ucontrol->value.integer.value[0] = 1;
319 else
320 ucontrol->value.integer.value[0] = 0;
321
322 pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
323 ucontrol->value.integer.value[0]);
324
325 return 0;
326}
327
328static int msm_routing_put_voice_mixer(struct snd_kcontrol *kcontrol,
329 struct snd_ctl_elem_value *ucontrol)
330{
Patrick Laiec2b8942011-09-01 11:01:51 -0700331 struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
332 struct snd_soc_dapm_widget *widget = wlist->widgets[0];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700333 struct soc_mixer_control *mc =
334 (struct soc_mixer_control *)kcontrol->private_value;
335
336 if (ucontrol->value.integer.value[0]) {
Neema Shetty2c07eb52011-08-21 20:33:52 -0700337 msm_pcm_routing_process_voice(mc->reg, mc->shift,
338 mc->invert, 1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700339 snd_soc_dapm_mixer_update_power(widget, kcontrol, 1);
340 } else {
Neema Shetty2c07eb52011-08-21 20:33:52 -0700341 msm_pcm_routing_process_voice(mc->reg, mc->shift,
342 mc->invert, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700343 snd_soc_dapm_mixer_update_power(widget, kcontrol, 0);
344 }
345
346 return 1;
347}
348
Sriranjan Srikantama4969dd2011-07-14 00:34:56 -0700349static int msm_routing_get_switch_mixer(struct snd_kcontrol *kcontrol,
350 struct snd_ctl_elem_value *ucontrol)
351{
352 ucontrol->value.integer.value[0] = fm_switch_enable;
353 pr_debug("%s: FM Switch enable %ld\n", __func__,
354 ucontrol->value.integer.value[0]);
355 return 0;
356}
357
358static int msm_routing_put_switch_mixer(struct snd_kcontrol *kcontrol,
359 struct snd_ctl_elem_value *ucontrol)
360{
361 struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
362
363 pr_debug("%s: FM Switch enable %ld\n", __func__,
364 ucontrol->value.integer.value[0]);
365 if (ucontrol->value.integer.value[0])
366 snd_soc_dapm_mixer_update_power(widget, kcontrol, 1);
367 else
368 snd_soc_dapm_mixer_update_power(widget, kcontrol, 0);
369 fm_switch_enable = ucontrol->value.integer.value[0];
370 return 1;
371}
372
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700373static int msm_routing_get_port_mixer(struct snd_kcontrol *kcontrol,
374 struct snd_ctl_elem_value *ucontrol)
375{
376 struct soc_mixer_control *mc =
377 (struct soc_mixer_control *)kcontrol->private_value;
378
379 if (test_bit(mc->shift, &audio_port_mixers[mc->reg].dai_sessions))
380 ucontrol->value.integer.value[0] = 1;
381 else
382 ucontrol->value.integer.value[0] = 0;
383
384 pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
385 ucontrol->value.integer.value[0]);
386
387 return 0;
388}
389
390static int msm_routing_put_port_mixer(struct snd_kcontrol *kcontrol,
391 struct snd_ctl_elem_value *ucontrol)
392{
393 struct soc_mixer_control *mc =
394 (struct soc_mixer_control *)kcontrol->private_value;
395
396 pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg,
397 mc->shift, ucontrol->value.integer.value[0]);
398
399 if (ucontrol->value.integer.value[0]) {
400 afe_loopback(1, audio_port_mixers[mc->reg].port_id,
401 bedai_port_map[mc->shift]);
402 set_bit(mc->shift,
403 &audio_port_mixers[mc->reg].dai_sessions);
404 } else {
405 afe_loopback(0, audio_port_mixers[mc->reg].port_id,
406 bedai_port_map[mc->shift]);
407 clear_bit(mc->shift,
408 &audio_port_mixers[mc->reg].dai_sessions);
409 }
410
411 return 1;
412}
413
Jayasena Sangaraboinacb1e22f2011-07-18 10:36:57 -0700414static int msm_routing_get_fm_vol_mixer(struct snd_kcontrol *kcontrol,
415 struct snd_ctl_elem_value *ucontrol)
416{
417 ucontrol->value.integer.value[0] = msm_route_fm_vol_control;
418 return 0;
419}
420
421static int msm_routing_set_fm_vol_mixer(struct snd_kcontrol *kcontrol,
422 struct snd_ctl_elem_value *ucontrol)
423{
424 afe_loopback_gain(INT_FM_TX , ucontrol->value.integer.value[0]);
425
426 msm_route_fm_vol_control = ucontrol->value.integer.value[0];
427
428 return 0;
429}
430
Asish Bhattacharya0ec76182011-07-29 16:58:11 +0530431static int msm_routing_get_lpa_vol_mixer(struct snd_kcontrol *kcontrol,
432 struct snd_ctl_elem_value *ucontrol)
433{
434 ucontrol->value.integer.value[0] = msm_route_lpa_vol_control;
435 return 0;
436}
437
438static int msm_routing_set_lpa_vol_mixer(struct snd_kcontrol *kcontrol,
439 struct snd_ctl_elem_value *ucontrol)
440{
441 if (!lpa_set_volume(ucontrol->value.integer.value[0]))
442 msm_route_lpa_vol_control =
443 ucontrol->value.integer.value[0];
444
445 return 0;
446}
447
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700448static const struct snd_kcontrol_new pri_i2s_rx_mixer_controls[] = {
449 SOC_SINGLE_EXT("MultiMedia1", AUDIO_MIXER_PRI_I2S_RX ,
450 MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
451 msm_routing_put_audio_mixer),
452 SOC_SINGLE_EXT("MultiMedia2", AUDIO_MIXER_PRI_I2S_RX,
453 MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
454 msm_routing_put_audio_mixer),
455 SOC_SINGLE_EXT("MultiMedia3", AUDIO_MIXER_PRI_I2S_RX,
456 MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
457 msm_routing_put_audio_mixer),
458};
459
460static const struct snd_kcontrol_new slimbus_rx_mixer_controls[] = {
461 SOC_SINGLE_EXT("MultiMedia1", AUDIO_MIXER_SLIMBUS_0_RX ,
462 MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
463 msm_routing_put_audio_mixer),
464 SOC_SINGLE_EXT("MultiMedia2", AUDIO_MIXER_SLIMBUS_0_RX,
465 MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
466 msm_routing_put_audio_mixer),
467 SOC_SINGLE_EXT("MultiMedia3", AUDIO_MIXER_SLIMBUS_0_RX,
468 MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
469 msm_routing_put_audio_mixer),
470};
471
472static const struct snd_kcontrol_new hdmi_mixer_controls[] = {
473 SOC_SINGLE_EXT("MultiMedia1", AUDIO_MIXER_HDMI_RX,
474 MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
475 msm_routing_put_audio_mixer),
476 SOC_SINGLE_EXT("MultiMedia2", AUDIO_MIXER_HDMI_RX,
477 MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
478 msm_routing_put_audio_mixer),
479};
480
481static const struct snd_kcontrol_new int_bt_sco_rx_mixer_controls[] = {
482 SOC_SINGLE_EXT("MultiMedia1", AUDIO_MIXER_INT_BT_SCO_RX,
483 MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
484 msm_routing_put_audio_mixer),
485 SOC_SINGLE_EXT("MultiMedia2", AUDIO_MIXER_INT_BT_SCO_RX,
486 MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
487 msm_routing_put_audio_mixer),
488};
489
490static const struct snd_kcontrol_new int_fm_rx_mixer_controls[] = {
491 SOC_SINGLE_EXT("MultiMedia1", AUDIO_MIXER_INT_FM_RX,
492 MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
493 msm_routing_put_audio_mixer),
494 SOC_SINGLE_EXT("MultiMedia2", AUDIO_MIXER_INT_FM_RX,
495 MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
496 msm_routing_put_audio_mixer),
497};
498
499static const struct snd_kcontrol_new mmul1_mixer_controls[] = {
500 SOC_SINGLE_EXT("PRI_TX", AUDIO_MIXER_MM_UL1,
501 MSM_BACKEND_DAI_PRI_I2S_TX, 1, 0, msm_routing_get_audio_mixer,
502 msm_routing_put_audio_mixer),
503 SOC_SINGLE_EXT("SLIM_0_TX", AUDIO_MIXER_MM_UL1,
504 MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_audio_mixer,
505 msm_routing_put_audio_mixer),
506 SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", AUDIO_MIXER_MM_UL1,
507 MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_audio_mixer,
508 msm_routing_put_audio_mixer),
509 SOC_SINGLE_EXT("INTERNAL_FM_TX", AUDIO_MIXER_MM_UL1,
510 MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_audio_mixer,
511 msm_routing_put_audio_mixer),
512};
513
514static const struct snd_kcontrol_new pri_rx_voice_mixer_controls[] = {
515 SOC_SINGLE_EXT("CSVoice", VOICE_MIXER_PRI_I2S_RX,
Neema Shetty2c07eb52011-08-21 20:33:52 -0700516 MSM_FRONTEND_DAI_CS_VOICE, 1, MSM_FRONTEND_DAI_CS_VOICE,
517 msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700518 SOC_SINGLE_EXT("Voip", VOICE_MIXER_PRI_I2S_RX ,
Neema Shetty2c07eb52011-08-21 20:33:52 -0700519 MSM_FRONTEND_DAI_VOIP, 1, MSM_FRONTEND_DAI_VOIP,
520 msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700521};
522
523static const struct snd_kcontrol_new slimbus_rx_voice_mixer_controls[] = {
524 SOC_SINGLE_EXT("CSVoice", VOICE_MIXER_SLIMBUS_0_RX,
Neema Shetty2c07eb52011-08-21 20:33:52 -0700525 MSM_FRONTEND_DAI_CS_VOICE, 1, MSM_FRONTEND_DAI_CS_VOICE,
526 msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700527 SOC_SINGLE_EXT("Voip", VOICE_MIXER_SLIMBUS_0_RX ,
Neema Shetty2c07eb52011-08-21 20:33:52 -0700528 MSM_FRONTEND_DAI_VOIP, 1, MSM_FRONTEND_DAI_VOIP,
529 msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700530};
531
532static const struct snd_kcontrol_new bt_sco_rx_voice_mixer_controls[] = {
533 SOC_SINGLE_EXT("CSVoice", VOICE_MIXER_INT_BT_SCO_RX,
Neema Shetty2c07eb52011-08-21 20:33:52 -0700534 MSM_FRONTEND_DAI_CS_VOICE, 1, MSM_FRONTEND_DAI_CS_VOICE,
535 msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700536 SOC_SINGLE_EXT("Voip", VOICE_MIXER_INT_BT_SCO_RX ,
Neema Shetty2c07eb52011-08-21 20:33:52 -0700537 MSM_FRONTEND_DAI_VOIP, 1, MSM_FRONTEND_DAI_VOIP,
538 msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700539};
540
541static const struct snd_kcontrol_new tx_voice_mixer_controls[] = {
542 SOC_SINGLE_EXT("PRI_TX_Voice", VOICE_MIXER_PRI_I2S_TX,
Neema Shetty2c07eb52011-08-21 20:33:52 -0700543 MSM_BACKEND_DAI_PRI_I2S_TX, 1, MSM_FRONTEND_DAI_CS_VOICE,
544 msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700545 SOC_SINGLE_EXT("SLIM_0_TX_Voice", VOICE_MIXER_SLIMBUS_0_TX,
Neema Shetty2c07eb52011-08-21 20:33:52 -0700546 MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, MSM_FRONTEND_DAI_CS_VOICE,
547 msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700548 SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX_Voice", VOICE_MIXER_INT_BT_SCO_TX,
Neema Shetty2c07eb52011-08-21 20:33:52 -0700549 MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, MSM_FRONTEND_DAI_CS_VOICE,
550 msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700551};
552
553static const struct snd_kcontrol_new tx_voip_mixer_controls[] = {
554 SOC_SINGLE_EXT("PRI_TX_Voip", VOICE_MIXER_PRI_I2S_TX,
Neema Shetty2c07eb52011-08-21 20:33:52 -0700555 MSM_BACKEND_DAI_PRI_I2S_TX, 1, MSM_FRONTEND_DAI_VOIP,
556 msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700557 SOC_SINGLE_EXT("SLIM_0_TX_Voip", VOICE_MIXER_SLIMBUS_0_TX,
Neema Shetty2c07eb52011-08-21 20:33:52 -0700558 MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, MSM_FRONTEND_DAI_VOIP,
559 msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700560 SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX_Voip", VOICE_MIXER_INT_BT_SCO_TX,
Neema Shetty2c07eb52011-08-21 20:33:52 -0700561 MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, MSM_FRONTEND_DAI_VOIP,
562 msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700563};
564
565static const struct snd_kcontrol_new sbus_0_rx_port_mixer_controls[] = {
566 SOC_SINGLE_EXT("INTERNAL_FM_TX", AUDIO_PORT_MIXER_SLIM_0_RX,
567 MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer,
568 msm_routing_put_port_mixer),
569 SOC_SINGLE_EXT("SLIM_0_TX", AUDIO_PORT_MIXER_SLIM_0_RX,
570 MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
571 msm_routing_put_port_mixer),
572};
573
Sriranjan Srikantama4969dd2011-07-14 00:34:56 -0700574static const struct snd_kcontrol_new fm_switch_mixer_controls =
575 SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
576 0, 1, 0, msm_routing_get_switch_mixer,
577 msm_routing_put_switch_mixer);
578
Jayasena Sangaraboinacb1e22f2011-07-18 10:36:57 -0700579static const struct snd_kcontrol_new int_fm_vol_mixer_controls[] = {
580 SOC_SINGLE_EXT_TLV("Internal FM RX Volume", SND_SOC_NOPM, 0,
581 INT_FM_RX_VOL_GAIN, 0, msm_routing_get_fm_vol_mixer,
582 msm_routing_set_fm_vol_mixer, fm_rx_vol_gain),
583};
584
Asish Bhattacharya0ec76182011-07-29 16:58:11 +0530585static const struct snd_kcontrol_new lpa_vol_mixer_controls[] = {
586 SOC_SINGLE_EXT_TLV("LPA RX Volume", SND_SOC_NOPM, 0,
587 INT_LPA_RX_VOL_GAIN, 0, msm_routing_get_lpa_vol_mixer,
588 msm_routing_set_lpa_vol_mixer, lpa_rx_vol_gain),
589};
590
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700591static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = {
592 /* Frontend AIF */
593 /* Widget name equals to Front-End DAI name<Need confirmation>,
594 * Stream name must contains substring of front-end dai name
595 */
596 SND_SOC_DAPM_AIF_IN("MM_DL1", "MultiMedia1 Playback", 0, 0, 0, 0),
597 SND_SOC_DAPM_AIF_IN("MM_DL2", "MultiMedia2 Playback", 0, 0, 0, 0),
598 SND_SOC_DAPM_AIF_IN("MM_DL3", "MultiMedia3 Playback", 0, 0, 0, 0),
599 SND_SOC_DAPM_AIF_IN("VOIP_DL", "VoIP Playback", 0, 0, 0, 0),
600 SND_SOC_DAPM_AIF_OUT("MM_UL1", "MultiMedia1 Capture", 0, 0, 0, 0),
601 SND_SOC_DAPM_AIF_IN("CS-VOICE_DL1", "CS-VOICE Playback", 0, 0, 0, 0),
602 SND_SOC_DAPM_AIF_OUT("CS-VOICE_UL1", "CS-VOICE Capture", 0, 0, 0, 0),
603 SND_SOC_DAPM_AIF_OUT("VOIP_UL", "VoIP Capture", 0, 0, 0, 0),
604 SND_SOC_DAPM_AIF_IN("SLIM0_DL_HL", "SLIMBUS0_HOSTLESS Playback",
605 0, 0, 0, 0),
606 SND_SOC_DAPM_AIF_OUT("SLIM0_UL_HL", "SLIMBUS0_HOSTLESS Capture",
607 0, 0, 0, 0),
608 SND_SOC_DAPM_AIF_IN("INTFM_DL_HL", "INT_FM_HOSTLESS Playback",
609 0, 0, 0, 0),
610 SND_SOC_DAPM_AIF_OUT("INTFM_UL_HL", "INT_FM_HOSTLESS Capture",
611 0, 0, 0, 0),
612 /* Backend AIF */
613 /* Stream name equals to backend dai link stream name
614 */
615 SND_SOC_DAPM_AIF_OUT("PRI_I2S_RX", "Primary I2S Playback", 0, 0, 0, 0),
616 SND_SOC_DAPM_AIF_OUT("SLIMBUS_0_RX", "Slimbus Playback", 0, 0, 0, 0),
617 SND_SOC_DAPM_AIF_OUT("HDMI", "HDMI Playback", 0, 0, 0 , 0),
618 SND_SOC_DAPM_AIF_IN("PRI_I2S_TX", "Primary I2S Capture", 0, 0, 0, 0),
619 SND_SOC_DAPM_AIF_IN("SLIMBUS_0_TX", "Slimbus Capture", 0, 0, 0, 0),
620 SND_SOC_DAPM_AIF_OUT("INT_BT_SCO_RX", "Internal BT-SCO Playback",
621 0, 0, 0 , 0),
622 SND_SOC_DAPM_AIF_IN("INT_BT_SCO_TX", "Internal BT-SCO Capture",
623 0, 0, 0, 0),
624 SND_SOC_DAPM_AIF_OUT("INT_FM_RX", "Internal FM Playback",
625 0, 0, 0 , 0),
626 SND_SOC_DAPM_AIF_IN("INT_FM_TX", "Internal FM Capture",
627 0, 0, 0, 0),
Sriranjan Srikantama4969dd2011-07-14 00:34:56 -0700628 /* Switch Definitions */
629 SND_SOC_DAPM_SWITCH("SBUS_0_RX", SND_SOC_NOPM, 0, 0,
630 &fm_switch_mixer_controls),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700631 /* Mixer definitions */
632 SND_SOC_DAPM_MIXER("PRI_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
633 pri_i2s_rx_mixer_controls, ARRAY_SIZE(pri_i2s_rx_mixer_controls)),
634 SND_SOC_DAPM_MIXER("SLIMBUS_0_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
635 slimbus_rx_mixer_controls, ARRAY_SIZE(slimbus_rx_mixer_controls)),
636 SND_SOC_DAPM_MIXER("HDMI Mixer", SND_SOC_NOPM, 0, 0,
637 hdmi_mixer_controls, ARRAY_SIZE(hdmi_mixer_controls)),
638 SND_SOC_DAPM_MIXER("MultiMedia1 Mixer", SND_SOC_NOPM, 0, 0,
639 mmul1_mixer_controls, ARRAY_SIZE(mmul1_mixer_controls)),
640 /* Voice Mixer */
641 SND_SOC_DAPM_MIXER("PRI_RX_Voice Mixer",
642 SND_SOC_NOPM, 0, 0, pri_rx_voice_mixer_controls,
643 ARRAY_SIZE(pri_rx_voice_mixer_controls)),
644 SND_SOC_DAPM_MIXER("SLIM_0_RX_Voice Mixer",
645 SND_SOC_NOPM, 0, 0,
646 slimbus_rx_voice_mixer_controls,
647 ARRAY_SIZE(slimbus_rx_voice_mixer_controls)),
648 SND_SOC_DAPM_MIXER("INTERNAL_BT_SCO_RX_Voice Mixer",
649 SND_SOC_NOPM, 0, 0,
650 bt_sco_rx_voice_mixer_controls,
651 ARRAY_SIZE(bt_sco_rx_voice_mixer_controls)),
652 SND_SOC_DAPM_MIXER("Voice_Tx Mixer",
653 SND_SOC_NOPM, 0, 0, tx_voice_mixer_controls,
654 ARRAY_SIZE(tx_voice_mixer_controls)),
655 SND_SOC_DAPM_MIXER("Voip_Tx Mixer",
656 SND_SOC_NOPM, 0, 0, tx_voip_mixer_controls,
657 ARRAY_SIZE(tx_voip_mixer_controls)),
658 SND_SOC_DAPM_MIXER("INTERNAL_BT_SCO_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
659 int_bt_sco_rx_mixer_controls, ARRAY_SIZE(int_bt_sco_rx_mixer_controls)),
660 SND_SOC_DAPM_MIXER("INTERNAL_FM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
661 int_fm_rx_mixer_controls, ARRAY_SIZE(int_fm_rx_mixer_controls)),
662 SND_SOC_DAPM_MIXER("SLIMBUS_0_RX Port Mixer",
663 SND_SOC_NOPM, 0, 0, sbus_0_rx_port_mixer_controls,
664 ARRAY_SIZE(sbus_0_rx_port_mixer_controls)),
665};
666
667static const struct snd_soc_dapm_route intercon[] = {
668 {"PRI_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
669 {"PRI_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
670 {"PRI_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
671 {"PRI_I2S_RX", NULL, "PRI_RX Audio Mixer"},
672
673 {"SLIMBUS_0_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
674 {"SLIMBUS_0_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
675 {"SLIMBUS_0_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
676 {"SLIMBUS_0_RX", NULL, "SLIMBUS_0_RX Audio Mixer"},
677
678 {"HDMI Mixer", "MultiMedia1", "MM_DL1"},
679 {"HDMI Mixer", "MultiMedia2", "MM_DL2"},
680 {"HDMI", NULL, "HDMI Mixer"},
681
682 {"MultiMedia1 Mixer", "PRI_TX", "PRI_I2S_TX"},
683 {"MultiMedia1 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
684
685 {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
686 {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
687 {"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX Audio Mixer"},
688
689 {"INTERNAL_FM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
690 {"INTERNAL_FM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
691 {"INT_FM_RX", NULL, "INTERNAL_FM_RX Audio Mixer"},
692
693 {"MultiMedia1 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
694 {"MultiMedia1 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
695 {"MM_UL1", NULL, "MultiMedia1 Mixer"},
696
697 {"PRI_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
698 {"PRI_RX_Voice Mixer", "Voip", "VOIP_DL"},
699 {"PRI_I2S_RX", NULL, "PRI_RX_Voice Mixer"},
700
701 {"SLIM_0_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
702 {"SLIM_0_RX_Voice Mixer", "Voip", "VOIP_DL"},
703 {"SLIMBUS_0_RX", NULL, "SLIM_0_RX_Voice Mixer"},
704
705 {"INTERNAL_BT_SCO_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
706 {"INTERNAL_BT_SCO_RX_Voice Mixer", "Voip", "VOIP_DL"},
707 {"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX_Voice Mixer"},
708
709 {"Voice_Tx Mixer", "PRI_TX_Voice", "PRI_I2S_TX"},
710 {"Voice_Tx Mixer", "SLIM_0_TX_Voice", "SLIMBUS_0_TX"},
711 {"Voice_Tx Mixer", "INTERNAL_BT_SCO_TX_Voice", "INT_BT_SCO_TX"},
712 {"CS-VOICE_UL1", NULL, "Voice_Tx Mixer"},
713 {"Voip_Tx Mixer", "PRI_TX_Voip", "PRI_I2S_TX"},
714 {"Voip_Tx Mixer", "SLIM_0_TX_Voip", "SLIMBUS_0_TX"},
715 {"Voip_Tx Mixer", "INTERNAL_BT_SCO_TX_Voip", "INT_BT_SCO_TX"},
716 {"VOIP_UL", NULL, "Voip_Tx Mixer"},
Sriranjan Srikantama4969dd2011-07-14 00:34:56 -0700717 {"SLIMBUS_0_RX", "Switch", "SLIM0_DL_HL"},
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700718 {"SLIM0_UL_HL", NULL, "SLIMBUS_0_TX"},
719 {"INT_FM_RX", NULL, "INTFM_DL_HL"},
720 {"INTFM_UL_HL", NULL, "INT_FM_TX"},
721 {"SLIMBUS_0_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
722 {"SLIMBUS_0_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
723 {"SLIMBUS_0_RX", NULL, "SLIMBUS_0_RX Port Mixer"},
724};
725
726static struct snd_pcm_ops msm_routing_pcm_ops = {};
727
728static unsigned int msm_routing_read(struct snd_soc_platform *platform,
729 unsigned int reg)
730{
731 dev_dbg(platform->dev, "reg %x\n", reg);
732 return 0;
733}
734
735/* Not used but frame seems to require it */
736static int msm_routing_write(struct snd_soc_platform *platform,
737 unsigned int reg, unsigned int val)
738{
739 dev_dbg(platform->dev, "reg %x val %x\n", reg, val);
740 return 0;
741}
742
743/* Not used but frame seems to require it */
744static int msm_routing_probe(struct snd_soc_platform *platform)
745{
746 snd_soc_dapm_new_controls(&platform->dapm, msm_qdsp6_widgets,
747 ARRAY_SIZE(msm_qdsp6_widgets));
748 snd_soc_dapm_add_routes(&platform->dapm, intercon,
749 ARRAY_SIZE(intercon));
750
751 snd_soc_dapm_new_widgets(&platform->dapm);
752
Jayasena Sangaraboinacb1e22f2011-07-18 10:36:57 -0700753 snd_soc_add_platform_controls(platform,
754 int_fm_vol_mixer_controls,
755 ARRAY_SIZE(int_fm_vol_mixer_controls));
Asish Bhattacharya0ec76182011-07-29 16:58:11 +0530756
757 snd_soc_add_platform_controls(platform,
758 lpa_vol_mixer_controls,
759 ARRAY_SIZE(lpa_vol_mixer_controls));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700760 return 0;
761}
762
763static struct snd_soc_platform_driver msm_soc_routing_platform = {
764 .ops = &msm_routing_pcm_ops,
765 .probe = msm_routing_probe,
766 .read = msm_routing_read,
767 .write = msm_routing_write,
768};
769
770static __devinit int msm_routing_pcm_probe(struct platform_device *pdev)
771{
772 dev_dbg(&pdev->dev, "dev name %s\n", dev_name(&pdev->dev));
773 return snd_soc_register_platform(&pdev->dev,
774 &msm_soc_routing_platform);
775}
776
777static int msm_routing_pcm_remove(struct platform_device *pdev)
778{
779 snd_soc_unregister_platform(&pdev->dev);
780 return 0;
781}
782
783static struct platform_driver msm_routing_pcm_driver = {
784 .driver = {
785 .name = "msm-pcm-routing",
786 .owner = THIS_MODULE,
787 },
788 .probe = msm_routing_pcm_probe,
789 .remove = __devexit_p(msm_routing_pcm_remove),
790};
791
792static int __init msm_soc_routing_platform_init(void)
793{
794 return platform_driver_register(&msm_routing_pcm_driver);
795}
796module_init(msm_soc_routing_platform_init);
797
798static void __exit msm_soc_routing_platform_exit(void)
799{
800 platform_driver_unregister(&msm_routing_pcm_driver);
801}
802module_exit(msm_soc_routing_platform_exit);
803
804MODULE_DESCRIPTION("MSM routing platform driver");
805MODULE_LICENSE("GPL v2");