blob: 3eb9a421903aedada8f6d8e896d33ae23e0c85b9 [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>
Neema Shettyfeea7742011-09-11 12:30:36 -070020#include <linux/mutex.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070021#include <sound/core.h>
22#include <sound/soc.h>
23#include <sound/soc-dapm.h>
24#include <sound/pcm.h>
25#include <sound/initval.h>
26#include <sound/control.h>
27#include <sound/q6adm.h>
28#include <sound/q6afe.h>
Jayasena Sangaraboinacb1e22f2011-07-18 10:36:57 -070029#include <sound/tlv.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070030#include "msm-pcm-routing.h"
31#include "qdsp6/q6voice.h"
32
Neema Shettyfeea7742011-09-11 12:30:36 -070033struct msm_pcm_routing_bdai_data {
34 u16 port_id; /* AFE port ID */
35 u8 active; /* track if this backend is enabled */
36 struct snd_pcm_hw_params *hw_params; /* to get freq and channel mode */
37 unsigned long fe_sessions; /* Front-end sessions */
38 unsigned long port_sessions; /* track Tx BE ports -> Rx BE */
39};
40
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070041#define INVALID_SESSION -1
Patrick Laicf999112011-08-23 11:27:20 -070042#define SESSION_TYPE_RX 0
43#define SESSION_TYPE_TX 1
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070044
Neema Shettyfeea7742011-09-11 12:30:36 -070045static struct mutex routing_lock;
46
Sriranjan Srikantama4969dd2011-07-14 00:34:56 -070047static int fm_switch_enable;
Jayasena Sangaraboinacb1e22f2011-07-18 10:36:57 -070048#define INT_FM_RX_VOL_MAX_STEPS 100
49#define INT_FM_RX_VOL_GAIN 2000
50
51static int msm_route_fm_vol_control;
52static const DECLARE_TLV_DB_SCALE(fm_rx_vol_gain, 0,
53 INT_FM_RX_VOL_MAX_STEPS, 0);
Sriranjan Srikantama4969dd2011-07-14 00:34:56 -070054
Sriranjan Srikantam269de7f2011-09-06 18:24:45 -070055#define INT_LPA_RX_VOL_MAX_STEPS 100
56#define INT_LPA_RX_VOL_GAIN 0x2000
Asish Bhattacharya0ec76182011-07-29 16:58:11 +053057
58static int msm_route_lpa_vol_control;
59static const DECLARE_TLV_DB_SCALE(lpa_rx_vol_gain, 0,
60 INT_LPA_RX_VOL_MAX_STEPS, 0);
61
Neema Shettyfeea7742011-09-11 12:30:36 -070062/* This array is indexed by back-end DAI ID defined in msm-pcm-routing.h
63 * If new back-end is defined, add new back-end DAI ID at the end of enum
64 */
65static struct msm_pcm_routing_bdai_data msm_bedais[MSM_BACKEND_DAI_MAX] = {
66 { PRIMARY_I2S_RX, 0, NULL, 0, 0},
67 { PRIMARY_I2S_TX, 0, NULL, 0, 0},
68 { SLIMBUS_0_RX, 0, NULL, 0, 0},
69 { SLIMBUS_0_TX, 0, NULL, 0, 0},
70 { HDMI_RX, 0, NULL, 0, 0},
71 { INT_BT_SCO_RX, 0, NULL, 0, 0},
72 { INT_BT_SCO_TX, 0, NULL, 0, 0},
73 { INT_FM_RX, 0, NULL, 0, 0},
74 { INT_FM_TX, 0, NULL, 0, 0},
Laxminath Kasam32657ec2011-08-01 19:26:57 +053075 { RT_PROXY_PORT_001_RX, 0, NULL, 0, 0},
76 { RT_PROXY_PORT_001_TX, 0, NULL, 0, 0},
Bhalchandra Gajare0e795c42011-08-15 18:10:30 -070077 { PCM_RX, 0, NULL, 0, 0},
78 { PCM_TX, 0, NULL, 0, 0},
Neema Shettyfeea7742011-09-11 12:30:36 -070079};
80
81
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070082/* Track ASM playback & capture sessions of DAI */
83static int fe_dai_map[MSM_FRONTEND_DAI_MAX][2] = {
84 /* MULTIMEDIA1 */
85 {INVALID_SESSION, INVALID_SESSION},
86 /* MULTIMEDIA2 */
87 {INVALID_SESSION, INVALID_SESSION},
88 /* MULTIMEDIA3 */
89 {INVALID_SESSION, INVALID_SESSION},
90};
91
Patrick Laicf999112011-08-23 11:27:20 -070092static void msm_pcm_routing_build_matrix(int fedai_id, int dspst_id,
93 int path_type)
94{
95 int i, port_type;
96 struct route_payload payload;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070097
Patrick Laicf999112011-08-23 11:27:20 -070098 payload.num_copps = 0;
99 port_type = (path_type == ADM_PATH_PLAYBACK ?
100 MSM_AFE_PORT_TYPE_RX : MSM_AFE_PORT_TYPE_TX);
101
102 for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
103 if ((afe_get_port_type(msm_bedais[i].port_id) ==
104 port_type) &&
105 msm_bedais[i].active && (test_bit(fedai_id,
106 &msm_bedais[i].fe_sessions)))
107 payload.copp_ids[payload.num_copps++] =
108 msm_bedais[i].port_id;
109 }
110
111 if (payload.num_copps)
112 adm_matrix_map(dspst_id, path_type,
113 payload.num_copps, payload.copp_ids, 0);
114}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700115
116void msm_pcm_routing_reg_phy_stream(int fedai_id, int dspst_id, int stream_type)
117{
Patrick Laicf999112011-08-23 11:27:20 -0700118 int i, session_type, path_type, port_type;
119 struct route_payload payload;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700120
121 if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) {
Patrick Laicf999112011-08-23 11:27:20 -0700122 session_type = SESSION_TYPE_RX;
123 path_type = ADM_PATH_PLAYBACK;
124 port_type = MSM_AFE_PORT_TYPE_RX;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700125 } else {
Patrick Laicf999112011-08-23 11:27:20 -0700126 session_type = SESSION_TYPE_TX;
127 path_type = ADM_PATH_LIVE_REC;
128 port_type = MSM_AFE_PORT_TYPE_TX;
129 }
130
131 mutex_lock(&routing_lock);
132
133 payload.num_copps = 0; /* only RX needs to use payload */
134 fe_dai_map[fedai_id][session_type] = dspst_id;
135 for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
136 if ((afe_get_port_type(msm_bedais[i].port_id) ==
137 port_type) && msm_bedais[i].active &&
138 (test_bit(fedai_id,
139 &msm_bedais[i].fe_sessions))) {
140 adm_open(msm_bedais[i].port_id,
141 path_type,
142 params_rate(msm_bedais[i].hw_params),
143 params_channels(msm_bedais[i].hw_params),
144 DEFAULT_COPP_TOPOLOGY);
145 payload.copp_ids[payload.num_copps++] =
146 msm_bedais[i].port_id;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700147 }
148 }
Patrick Laicf999112011-08-23 11:27:20 -0700149 if (payload.num_copps)
150 adm_matrix_map(dspst_id, path_type,
151 payload.num_copps, payload.copp_ids, 0);
152
153 mutex_unlock(&routing_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700154}
155
156void msm_pcm_routing_dereg_phy_stream(int fedai_id, int stream_type)
157{
Patrick Laicf999112011-08-23 11:27:20 -0700158 int i, port_type, session_type;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700159
160 if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) {
Patrick Laicf999112011-08-23 11:27:20 -0700161 port_type = MSM_AFE_PORT_TYPE_RX;
162 session_type = SESSION_TYPE_RX;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700163 } else {
Patrick Laicf999112011-08-23 11:27:20 -0700164 port_type = MSM_AFE_PORT_TYPE_TX;
165 session_type = SESSION_TYPE_TX;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700166 }
Patrick Laicf999112011-08-23 11:27:20 -0700167
168 mutex_lock(&routing_lock);
169
170 for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
171 if ((afe_get_port_type(msm_bedais[i].port_id) ==
172 port_type) && msm_bedais[i].active &&
173 (test_bit(fedai_id,
174 &msm_bedais[i].fe_sessions)))
175 adm_close(msm_bedais[i].port_id);
176 }
177
178 fe_dai_map[fedai_id][session_type] = INVALID_SESSION;
179
180 mutex_unlock(&routing_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700181}
182
183static void msm_pcm_routing_process_audio(u16 reg, u16 val, int set)
184{
Patrick Laicf999112011-08-23 11:27:20 -0700185 int session_type, path_type;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700186
187 pr_debug("%s: reg %x val %x set %x\n", __func__, reg, val, set);
188
Patrick Laicf999112011-08-23 11:27:20 -0700189 if (afe_get_port_type(msm_bedais[reg].port_id) ==
190 MSM_AFE_PORT_TYPE_RX) {
191 session_type = SESSION_TYPE_RX;
192 path_type = ADM_PATH_PLAYBACK;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700193 } else {
Patrick Laicf999112011-08-23 11:27:20 -0700194 session_type = SESSION_TYPE_TX;
195 path_type = ADM_PATH_LIVE_REC;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700196 }
Patrick Laicf999112011-08-23 11:27:20 -0700197
198 mutex_lock(&routing_lock);
199
200 if (set) {
201 set_bit(val, &msm_bedais[reg].fe_sessions);
202 if (msm_bedais[reg].active && fe_dai_map[val][session_type] !=
203 INVALID_SESSION) {
204 adm_open(msm_bedais[reg].port_id, path_type,
205 params_rate(msm_bedais[reg].hw_params),
206 params_channels(msm_bedais[reg].hw_params),
207 DEFAULT_COPP_TOPOLOGY);
208 msm_pcm_routing_build_matrix(val,
209 fe_dai_map[val][session_type], path_type);
210 }
211 } else {
212 clear_bit(val, &msm_bedais[reg].fe_sessions);
213 if (msm_bedais[reg].active && fe_dai_map[val][session_type] !=
214 INVALID_SESSION) {
215 adm_close(msm_bedais[reg].port_id);
216 msm_pcm_routing_build_matrix(val,
217 fe_dai_map[val][session_type], path_type);
218 }
219 }
220
221 mutex_unlock(&routing_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700222}
223
224static int msm_routing_get_audio_mixer(struct snd_kcontrol *kcontrol,
225 struct snd_ctl_elem_value *ucontrol)
226{
227 struct soc_mixer_control *mc =
228 (struct soc_mixer_control *)kcontrol->private_value;
229
Patrick Laicf999112011-08-23 11:27:20 -0700230 if (test_bit(mc->shift, &msm_bedais[mc->reg].fe_sessions))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700231 ucontrol->value.integer.value[0] = 1;
232 else
233 ucontrol->value.integer.value[0] = 0;
234
235 pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
236 ucontrol->value.integer.value[0]);
237
238 return 0;
239}
240
241static int msm_routing_put_audio_mixer(struct snd_kcontrol *kcontrol,
242 struct snd_ctl_elem_value *ucontrol)
243{
Patrick Laiec2b8942011-09-01 11:01:51 -0700244 struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
245 struct snd_soc_dapm_widget *widget = wlist->widgets[0];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700246 struct soc_mixer_control *mc =
247 (struct soc_mixer_control *)kcontrol->private_value;
248
249
250 if (ucontrol->value.integer.value[0]) {
251 msm_pcm_routing_process_audio(mc->reg, mc->shift, 1);
252 snd_soc_dapm_mixer_update_power(widget, kcontrol, 1);
253 } else {
254 msm_pcm_routing_process_audio(mc->reg, mc->shift, 0);
255 snd_soc_dapm_mixer_update_power(widget, kcontrol, 0);
256 }
257
258 return 1;
259}
260
Neema Shettyfeea7742011-09-11 12:30:36 -0700261static void msm_pcm_routing_process_voice(u16 reg, u16 val, int set)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700262{
Neema Shetty2c07eb52011-08-21 20:33:52 -0700263 u16 session_id = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700264
Neema Shettyfeea7742011-09-11 12:30:36 -0700265 pr_debug("%s: reg %x val %x set %x\n", __func__, reg, val, set);
Neema Shetty2c07eb52011-08-21 20:33:52 -0700266
Neema Shettyfeea7742011-09-11 12:30:36 -0700267 if (val == MSM_FRONTEND_DAI_CS_VOICE)
Neema Shetty2c07eb52011-08-21 20:33:52 -0700268 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",
Neema Shettyfeea7742011-09-11 12:30:36 -0700273 __func__, val, session_id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700274
Neema Shettyfeea7742011-09-11 12:30:36 -0700275 mutex_lock(&routing_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700276
277 if (set)
Neema Shettyfeea7742011-09-11 12:30:36 -0700278 set_bit(val, &msm_bedais[reg].fe_sessions);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700279 else
Neema Shettyfeea7742011-09-11 12:30:36 -0700280 clear_bit(val, &msm_bedais[reg].fe_sessions);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700281
Neema Shettyfeea7742011-09-11 12:30:36 -0700282 mutex_unlock(&routing_lock);
283
284 if (afe_get_port_type(msm_bedais[reg].port_id) ==
285 MSM_AFE_PORT_TYPE_RX) {
Neema Shetty2c07eb52011-08-21 20:33:52 -0700286 voc_set_route_flag(session_id, RX_PATH, set);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700287 if (set) {
Neema Shetty2c07eb52011-08-21 20:33:52 -0700288 voc_set_rxtx_port(session_id,
Neema Shettyfeea7742011-09-11 12:30:36 -0700289 msm_bedais[reg].port_id, DEV_RX);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700290
Neema Shetty2c07eb52011-08-21 20:33:52 -0700291 if (voc_get_route_flag(session_id, RX_PATH) &&
292 voc_get_route_flag(session_id, TX_PATH))
293 voc_enable_cvp(session_id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700294 } else {
Neema Shetty2c07eb52011-08-21 20:33:52 -0700295 voc_disable_cvp(session_id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700296 }
297 } else {
Neema Shetty2c07eb52011-08-21 20:33:52 -0700298 voc_set_route_flag(session_id, TX_PATH, set);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700299 if (set) {
Neema Shetty2c07eb52011-08-21 20:33:52 -0700300 voc_set_rxtx_port(session_id,
Neema Shettyfeea7742011-09-11 12:30:36 -0700301 msm_bedais[reg].port_id, DEV_TX);
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
Neema Shettyfeea7742011-09-11 12:30:36 -0700317 mutex_lock(&routing_lock);
318
319 if (test_bit(mc->shift, &msm_bedais[mc->reg].fe_sessions))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700320 ucontrol->value.integer.value[0] = 1;
321 else
322 ucontrol->value.integer.value[0] = 0;
323
Neema Shettyfeea7742011-09-11 12:30:36 -0700324 mutex_unlock(&routing_lock);
325
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700326 pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
327 ucontrol->value.integer.value[0]);
328
329 return 0;
330}
331
332static int msm_routing_put_voice_mixer(struct snd_kcontrol *kcontrol,
333 struct snd_ctl_elem_value *ucontrol)
334{
Patrick Laiec2b8942011-09-01 11:01:51 -0700335 struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
336 struct snd_soc_dapm_widget *widget = wlist->widgets[0];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700337 struct soc_mixer_control *mc =
338 (struct soc_mixer_control *)kcontrol->private_value;
339
340 if (ucontrol->value.integer.value[0]) {
Neema Shettyfeea7742011-09-11 12:30:36 -0700341 msm_pcm_routing_process_voice(mc->reg, mc->shift, 1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700342 snd_soc_dapm_mixer_update_power(widget, kcontrol, 1);
343 } else {
Neema Shettyfeea7742011-09-11 12:30:36 -0700344 msm_pcm_routing_process_voice(mc->reg, mc->shift, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700345 snd_soc_dapm_mixer_update_power(widget, kcontrol, 0);
346 }
347
348 return 1;
349}
350
Sriranjan Srikantama4969dd2011-07-14 00:34:56 -0700351static int msm_routing_get_switch_mixer(struct snd_kcontrol *kcontrol,
352 struct snd_ctl_elem_value *ucontrol)
353{
354 ucontrol->value.integer.value[0] = fm_switch_enable;
355 pr_debug("%s: FM Switch enable %ld\n", __func__,
356 ucontrol->value.integer.value[0]);
357 return 0;
358}
359
360static int msm_routing_put_switch_mixer(struct snd_kcontrol *kcontrol,
361 struct snd_ctl_elem_value *ucontrol)
362{
363 struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
364
365 pr_debug("%s: FM Switch enable %ld\n", __func__,
366 ucontrol->value.integer.value[0]);
367 if (ucontrol->value.integer.value[0])
368 snd_soc_dapm_mixer_update_power(widget, kcontrol, 1);
369 else
370 snd_soc_dapm_mixer_update_power(widget, kcontrol, 0);
371 fm_switch_enable = ucontrol->value.integer.value[0];
372 return 1;
373}
374
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700375static int msm_routing_get_port_mixer(struct snd_kcontrol *kcontrol,
376 struct snd_ctl_elem_value *ucontrol)
377{
378 struct soc_mixer_control *mc =
379 (struct soc_mixer_control *)kcontrol->private_value;
380
Patrick Laicf999112011-08-23 11:27:20 -0700381 if (test_bit(mc->shift, &msm_bedais[mc->reg].port_sessions))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700382 ucontrol->value.integer.value[0] = 1;
383 else
384 ucontrol->value.integer.value[0] = 0;
385
386 pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
387 ucontrol->value.integer.value[0]);
388
389 return 0;
390}
391
392static int msm_routing_put_port_mixer(struct snd_kcontrol *kcontrol,
393 struct snd_ctl_elem_value *ucontrol)
394{
395 struct soc_mixer_control *mc =
396 (struct soc_mixer_control *)kcontrol->private_value;
397
398 pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg,
399 mc->shift, ucontrol->value.integer.value[0]);
400
401 if (ucontrol->value.integer.value[0]) {
Patrick Laicf999112011-08-23 11:27:20 -0700402 afe_loopback(1, msm_bedais[mc->reg].port_id,
403 msm_bedais[mc->shift].port_id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700404 set_bit(mc->shift,
Patrick Laicf999112011-08-23 11:27:20 -0700405 &msm_bedais[mc->reg].port_sessions);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700406 } else {
Patrick Laicf999112011-08-23 11:27:20 -0700407 afe_loopback(0, msm_bedais[mc->reg].port_id,
408 msm_bedais[mc->shift].port_id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700409 clear_bit(mc->shift,
Patrick Laicf999112011-08-23 11:27:20 -0700410 &msm_bedais[mc->reg].port_sessions);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700411 }
412
413 return 1;
414}
415
Jayasena Sangaraboinacb1e22f2011-07-18 10:36:57 -0700416static int msm_routing_get_fm_vol_mixer(struct snd_kcontrol *kcontrol,
417 struct snd_ctl_elem_value *ucontrol)
418{
419 ucontrol->value.integer.value[0] = msm_route_fm_vol_control;
420 return 0;
421}
422
423static int msm_routing_set_fm_vol_mixer(struct snd_kcontrol *kcontrol,
424 struct snd_ctl_elem_value *ucontrol)
425{
426 afe_loopback_gain(INT_FM_TX , ucontrol->value.integer.value[0]);
427
428 msm_route_fm_vol_control = ucontrol->value.integer.value[0];
429
430 return 0;
431}
432
Asish Bhattacharya0ec76182011-07-29 16:58:11 +0530433static int msm_routing_get_lpa_vol_mixer(struct snd_kcontrol *kcontrol,
434 struct snd_ctl_elem_value *ucontrol)
435{
436 ucontrol->value.integer.value[0] = msm_route_lpa_vol_control;
437 return 0;
438}
439
440static int msm_routing_set_lpa_vol_mixer(struct snd_kcontrol *kcontrol,
441 struct snd_ctl_elem_value *ucontrol)
442{
443 if (!lpa_set_volume(ucontrol->value.integer.value[0]))
444 msm_route_lpa_vol_control =
445 ucontrol->value.integer.value[0];
446
447 return 0;
448}
449
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700450static const struct snd_kcontrol_new pri_i2s_rx_mixer_controls[] = {
Patrick Laicf999112011-08-23 11:27:20 -0700451 SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_PRI_I2S_RX ,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700452 MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
453 msm_routing_put_audio_mixer),
Patrick Laicf999112011-08-23 11:27:20 -0700454 SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_PRI_I2S_RX,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700455 MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
456 msm_routing_put_audio_mixer),
Patrick Laicf999112011-08-23 11:27:20 -0700457 SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_PRI_I2S_RX,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700458 MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
459 msm_routing_put_audio_mixer),
460};
461
462static const struct snd_kcontrol_new slimbus_rx_mixer_controls[] = {
Patrick Laicf999112011-08-23 11:27:20 -0700463 SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SLIMBUS_0_RX ,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700464 MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
465 msm_routing_put_audio_mixer),
Patrick Laicf999112011-08-23 11:27:20 -0700466 SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_SLIMBUS_0_RX,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700467 MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
468 msm_routing_put_audio_mixer),
Patrick Laicf999112011-08-23 11:27:20 -0700469 SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_SLIMBUS_0_RX,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700470 MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
471 msm_routing_put_audio_mixer),
472};
473
474static const struct snd_kcontrol_new hdmi_mixer_controls[] = {
Patrick Laicf999112011-08-23 11:27:20 -0700475 SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_HDMI_RX,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700476 MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
477 msm_routing_put_audio_mixer),
Patrick Laicf999112011-08-23 11:27:20 -0700478 SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_HDMI_RX,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700479 MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
480 msm_routing_put_audio_mixer),
Patrick Laicf999112011-08-23 11:27:20 -0700481 SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_HDMI_RX,
Asish Bhattacharyac592eed2011-09-16 17:43:02 +0530482 MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
483 msm_routing_put_audio_mixer),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700484};
485
486static const struct snd_kcontrol_new int_bt_sco_rx_mixer_controls[] = {
Patrick Laicf999112011-08-23 11:27:20 -0700487 SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_INT_BT_SCO_RX,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700488 MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
489 msm_routing_put_audio_mixer),
Patrick Laicf999112011-08-23 11:27:20 -0700490 SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_INT_BT_SCO_RX,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700491 MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
492 msm_routing_put_audio_mixer),
Patrick Laicf999112011-08-23 11:27:20 -0700493 SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_INT_BT_SCO_RX,
Asish Bhattacharyac592eed2011-09-16 17:43:02 +0530494 MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
495 msm_routing_put_audio_mixer),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700496};
497
498static const struct snd_kcontrol_new int_fm_rx_mixer_controls[] = {
Patrick Laicf999112011-08-23 11:27:20 -0700499 SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_INT_FM_RX,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700500 MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
501 msm_routing_put_audio_mixer),
Patrick Laicf999112011-08-23 11:27:20 -0700502 SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_INT_FM_RX,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700503 MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
504 msm_routing_put_audio_mixer),
Patrick Laicf999112011-08-23 11:27:20 -0700505 SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_INT_FM_RX,
Asish Bhattacharyac592eed2011-09-16 17:43:02 +0530506 MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
507 msm_routing_put_audio_mixer),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700508};
509
Laxminath Kasam32657ec2011-08-01 19:26:57 +0530510static const struct snd_kcontrol_new afe_pcm_rx_mixer_controls[] = {
Patrick Laicf999112011-08-23 11:27:20 -0700511 SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_AFE_PCM_RX,
Laxminath Kasam32657ec2011-08-01 19:26:57 +0530512 MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
513 msm_routing_put_audio_mixer),
Patrick Laicf999112011-08-23 11:27:20 -0700514 SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_AFE_PCM_RX,
Laxminath Kasam32657ec2011-08-01 19:26:57 +0530515 MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
516 msm_routing_put_audio_mixer),
Patrick Laicf999112011-08-23 11:27:20 -0700517 SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_AFE_PCM_RX,
Laxminath Kasam32657ec2011-08-01 19:26:57 +0530518 MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
519 msm_routing_put_audio_mixer),
520};
521
Bhalchandra Gajare0e795c42011-08-15 18:10:30 -0700522static const struct snd_kcontrol_new auxpcm_rx_mixer_controls[] = {
Patrick Laicf999112011-08-23 11:27:20 -0700523 SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_AUXPCM_RX,
Bhalchandra Gajare0e795c42011-08-15 18:10:30 -0700524 MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
525 msm_routing_put_audio_mixer),
Patrick Laicf999112011-08-23 11:27:20 -0700526 SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_AUXPCM_RX,
Bhalchandra Gajare0e795c42011-08-15 18:10:30 -0700527 MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
528 msm_routing_put_audio_mixer),
Patrick Laicf999112011-08-23 11:27:20 -0700529 SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_AUXPCM_RX,
Bhalchandra Gajare0e795c42011-08-15 18:10:30 -0700530 MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
531 msm_routing_put_audio_mixer),
532};
Laxminath Kasam32657ec2011-08-01 19:26:57 +0530533
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700534static const struct snd_kcontrol_new mmul1_mixer_controls[] = {
Patrick Laicf999112011-08-23 11:27:20 -0700535 SOC_SINGLE_EXT("PRI_TX", MSM_BACKEND_DAI_PRI_I2S_TX,
536 MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
537 msm_routing_put_audio_mixer),
538 SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX,
539 MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
540 msm_routing_put_audio_mixer),
541 SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_AUXPCM_TX,
542 MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
543 msm_routing_put_audio_mixer),
544 SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_INT_BT_SCO_TX,
545 MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
546 msm_routing_put_audio_mixer),
547 SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX,
548 MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
549 msm_routing_put_audio_mixer),
550 SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX,
551 MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
552 msm_routing_put_audio_mixer),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700553};
554
Jayasena Sangaraboina99fee652011-09-19 07:43:13 -0700555static const struct snd_kcontrol_new mmul2_mixer_controls[] = {
Patrick Laicf999112011-08-23 11:27:20 -0700556 SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX,
557 MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
Jayasena Sangaraboina99fee652011-09-19 07:43:13 -0700558 msm_routing_put_audio_mixer),
559};
560
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700561static const struct snd_kcontrol_new pri_rx_voice_mixer_controls[] = {
Neema Shettyfeea7742011-09-11 12:30:36 -0700562 SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_PRI_I2S_RX,
563 MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
564 msm_routing_put_voice_mixer),
565 SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_PRI_I2S_RX,
566 MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
567 msm_routing_put_voice_mixer),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700568};
569
570static const struct snd_kcontrol_new slimbus_rx_voice_mixer_controls[] = {
Neema Shettyfeea7742011-09-11 12:30:36 -0700571 SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_SLIMBUS_0_RX,
572 MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
573 msm_routing_put_voice_mixer),
574 SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_SLIMBUS_0_RX ,
575 MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
576 msm_routing_put_voice_mixer),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700577};
578
579static const struct snd_kcontrol_new bt_sco_rx_voice_mixer_controls[] = {
Neema Shettyfeea7742011-09-11 12:30:36 -0700580 SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_INT_BT_SCO_RX,
581 MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
582 msm_routing_put_voice_mixer),
583 SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_INT_BT_SCO_RX ,
584 MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
585 msm_routing_put_voice_mixer),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700586};
587
Laxminath Kasam32657ec2011-08-01 19:26:57 +0530588static const struct snd_kcontrol_new afe_pcm_rx_voice_mixer_controls[] = {
589 SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_AFE_PCM_RX,
590 MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
591 msm_routing_put_voice_mixer),
592 SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_AFE_PCM_RX,
593 MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
594 msm_routing_put_voice_mixer),
595};
596
Bhalchandra Gajare0e795c42011-08-15 18:10:30 -0700597static const struct snd_kcontrol_new aux_pcm_rx_voice_mixer_controls[] = {
598 SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_AUXPCM_RX,
599 MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
600 msm_routing_put_voice_mixer),
601 SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_AUXPCM_RX,
602 MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
603 msm_routing_put_voice_mixer),
604};
605
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700606static const struct snd_kcontrol_new tx_voice_mixer_controls[] = {
Neema Shettyfeea7742011-09-11 12:30:36 -0700607 SOC_SINGLE_EXT("PRI_TX_Voice", MSM_BACKEND_DAI_PRI_I2S_TX,
608 MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
609 msm_routing_put_voice_mixer),
610 SOC_SINGLE_EXT("SLIM_0_TX_Voice", MSM_BACKEND_DAI_SLIMBUS_0_TX,
611 MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
612 msm_routing_put_voice_mixer),
613 SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX_Voice",
614 MSM_BACKEND_DAI_INT_BT_SCO_TX, MSM_FRONTEND_DAI_CS_VOICE, 1, 0,
Neema Shetty2c07eb52011-08-21 20:33:52 -0700615 msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
Laxminath Kasam32657ec2011-08-01 19:26:57 +0530616 SOC_SINGLE_EXT("AFE_PCM_TX_Voice", MSM_BACKEND_DAI_AFE_PCM_TX,
617 MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
618 msm_routing_put_voice_mixer),
Bhalchandra Gajare0e795c42011-08-15 18:10:30 -0700619 SOC_SINGLE_EXT("AUX_PCM_TX_Voice", MSM_BACKEND_DAI_AUXPCM_TX,
620 MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
621 msm_routing_put_voice_mixer),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700622};
623
624static const struct snd_kcontrol_new tx_voip_mixer_controls[] = {
Neema Shettyfeea7742011-09-11 12:30:36 -0700625 SOC_SINGLE_EXT("PRI_TX_Voip", MSM_BACKEND_DAI_PRI_I2S_TX,
626 MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
627 msm_routing_put_voice_mixer),
628 SOC_SINGLE_EXT("SLIM_0_TX_Voip", MSM_BACKEND_DAI_SLIMBUS_0_TX,
629 MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
630 msm_routing_put_voice_mixer),
631 SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX_Voip", MSM_BACKEND_DAI_INT_BT_SCO_TX,
632 MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
633 msm_routing_put_voice_mixer),
Laxminath Kasam32657ec2011-08-01 19:26:57 +0530634 SOC_SINGLE_EXT("AFE_PCM_TX_Voip", MSM_BACKEND_DAI_AFE_PCM_TX,
635 MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
636 msm_routing_put_voice_mixer),
Bhalchandra Gajare0e795c42011-08-15 18:10:30 -0700637 SOC_SINGLE_EXT("AUX_PCM_TX_Voip", MSM_BACKEND_DAI_AUXPCM_TX,
638 MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
639 msm_routing_put_voice_mixer),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700640};
641
642static const struct snd_kcontrol_new sbus_0_rx_port_mixer_controls[] = {
Patrick Laicf999112011-08-23 11:27:20 -0700643 SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700644 MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer,
645 msm_routing_put_port_mixer),
Patrick Laicf999112011-08-23 11:27:20 -0700646 SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700647 MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
648 msm_routing_put_port_mixer),
649};
650
Sriranjan Srikantama4969dd2011-07-14 00:34:56 -0700651static const struct snd_kcontrol_new fm_switch_mixer_controls =
652 SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
653 0, 1, 0, msm_routing_get_switch_mixer,
654 msm_routing_put_switch_mixer);
655
Jayasena Sangaraboinacb1e22f2011-07-18 10:36:57 -0700656static const struct snd_kcontrol_new int_fm_vol_mixer_controls[] = {
657 SOC_SINGLE_EXT_TLV("Internal FM RX Volume", SND_SOC_NOPM, 0,
658 INT_FM_RX_VOL_GAIN, 0, msm_routing_get_fm_vol_mixer,
659 msm_routing_set_fm_vol_mixer, fm_rx_vol_gain),
660};
661
Asish Bhattacharya0ec76182011-07-29 16:58:11 +0530662static const struct snd_kcontrol_new lpa_vol_mixer_controls[] = {
663 SOC_SINGLE_EXT_TLV("LPA RX Volume", SND_SOC_NOPM, 0,
664 INT_LPA_RX_VOL_GAIN, 0, msm_routing_get_lpa_vol_mixer,
665 msm_routing_set_lpa_vol_mixer, lpa_rx_vol_gain),
666};
667
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700668static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = {
669 /* Frontend AIF */
670 /* Widget name equals to Front-End DAI name<Need confirmation>,
671 * Stream name must contains substring of front-end dai name
672 */
673 SND_SOC_DAPM_AIF_IN("MM_DL1", "MultiMedia1 Playback", 0, 0, 0, 0),
674 SND_SOC_DAPM_AIF_IN("MM_DL2", "MultiMedia2 Playback", 0, 0, 0, 0),
675 SND_SOC_DAPM_AIF_IN("MM_DL3", "MultiMedia3 Playback", 0, 0, 0, 0),
676 SND_SOC_DAPM_AIF_IN("VOIP_DL", "VoIP Playback", 0, 0, 0, 0),
677 SND_SOC_DAPM_AIF_OUT("MM_UL1", "MultiMedia1 Capture", 0, 0, 0, 0),
Jayasena Sangaraboina99fee652011-09-19 07:43:13 -0700678 SND_SOC_DAPM_AIF_OUT("MM_UL2", "MultiMedia2 Capture", 0, 0, 0, 0),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700679 SND_SOC_DAPM_AIF_IN("CS-VOICE_DL1", "CS-VOICE Playback", 0, 0, 0, 0),
680 SND_SOC_DAPM_AIF_OUT("CS-VOICE_UL1", "CS-VOICE Capture", 0, 0, 0, 0),
681 SND_SOC_DAPM_AIF_OUT("VOIP_UL", "VoIP Capture", 0, 0, 0, 0),
682 SND_SOC_DAPM_AIF_IN("SLIM0_DL_HL", "SLIMBUS0_HOSTLESS Playback",
683 0, 0, 0, 0),
684 SND_SOC_DAPM_AIF_OUT("SLIM0_UL_HL", "SLIMBUS0_HOSTLESS Capture",
685 0, 0, 0, 0),
686 SND_SOC_DAPM_AIF_IN("INTFM_DL_HL", "INT_FM_HOSTLESS Playback",
687 0, 0, 0, 0),
688 SND_SOC_DAPM_AIF_OUT("INTFM_UL_HL", "INT_FM_HOSTLESS Capture",
689 0, 0, 0, 0),
690 /* Backend AIF */
691 /* Stream name equals to backend dai link stream name
692 */
693 SND_SOC_DAPM_AIF_OUT("PRI_I2S_RX", "Primary I2S Playback", 0, 0, 0, 0),
694 SND_SOC_DAPM_AIF_OUT("SLIMBUS_0_RX", "Slimbus Playback", 0, 0, 0, 0),
695 SND_SOC_DAPM_AIF_OUT("HDMI", "HDMI Playback", 0, 0, 0 , 0),
696 SND_SOC_DAPM_AIF_IN("PRI_I2S_TX", "Primary I2S Capture", 0, 0, 0, 0),
697 SND_SOC_DAPM_AIF_IN("SLIMBUS_0_TX", "Slimbus Capture", 0, 0, 0, 0),
698 SND_SOC_DAPM_AIF_OUT("INT_BT_SCO_RX", "Internal BT-SCO Playback",
699 0, 0, 0 , 0),
700 SND_SOC_DAPM_AIF_IN("INT_BT_SCO_TX", "Internal BT-SCO Capture",
701 0, 0, 0, 0),
702 SND_SOC_DAPM_AIF_OUT("INT_FM_RX", "Internal FM Playback",
703 0, 0, 0 , 0),
704 SND_SOC_DAPM_AIF_IN("INT_FM_TX", "Internal FM Capture",
705 0, 0, 0, 0),
Laxminath Kasam32657ec2011-08-01 19:26:57 +0530706 SND_SOC_DAPM_AIF_OUT("PCM_RX", "AFE Playback",
707 0, 0, 0 , 0),
708 SND_SOC_DAPM_AIF_IN("PCM_TX", "AFE Capture",
709 0, 0, 0, 0),
Bhalchandra Gajare0e795c42011-08-15 18:10:30 -0700710 SND_SOC_DAPM_AIF_OUT("AUX_PCM_RX", "AUX PCM Playback", 0, 0, 0, 0),
711 SND_SOC_DAPM_AIF_IN("AUX_PCM_TX", "AUX PCM Capture", 0, 0, 0, 0),
Sriranjan Srikantama4969dd2011-07-14 00:34:56 -0700712 /* Switch Definitions */
713 SND_SOC_DAPM_SWITCH("SBUS_0_RX", SND_SOC_NOPM, 0, 0,
714 &fm_switch_mixer_controls),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700715 /* Mixer definitions */
716 SND_SOC_DAPM_MIXER("PRI_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
717 pri_i2s_rx_mixer_controls, ARRAY_SIZE(pri_i2s_rx_mixer_controls)),
718 SND_SOC_DAPM_MIXER("SLIMBUS_0_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
719 slimbus_rx_mixer_controls, ARRAY_SIZE(slimbus_rx_mixer_controls)),
720 SND_SOC_DAPM_MIXER("HDMI Mixer", SND_SOC_NOPM, 0, 0,
721 hdmi_mixer_controls, ARRAY_SIZE(hdmi_mixer_controls)),
722 SND_SOC_DAPM_MIXER("MultiMedia1 Mixer", SND_SOC_NOPM, 0, 0,
723 mmul1_mixer_controls, ARRAY_SIZE(mmul1_mixer_controls)),
Jayasena Sangaraboina99fee652011-09-19 07:43:13 -0700724 SND_SOC_DAPM_MIXER("MultiMedia2 Mixer", SND_SOC_NOPM, 0, 0,
725 mmul2_mixer_controls, ARRAY_SIZE(mmul2_mixer_controls)),
Bhalchandra Gajare0e795c42011-08-15 18:10:30 -0700726 SND_SOC_DAPM_MIXER("AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
727 auxpcm_rx_mixer_controls, ARRAY_SIZE(auxpcm_rx_mixer_controls)),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700728 /* Voice Mixer */
729 SND_SOC_DAPM_MIXER("PRI_RX_Voice Mixer",
730 SND_SOC_NOPM, 0, 0, pri_rx_voice_mixer_controls,
731 ARRAY_SIZE(pri_rx_voice_mixer_controls)),
732 SND_SOC_DAPM_MIXER("SLIM_0_RX_Voice Mixer",
733 SND_SOC_NOPM, 0, 0,
734 slimbus_rx_voice_mixer_controls,
735 ARRAY_SIZE(slimbus_rx_voice_mixer_controls)),
736 SND_SOC_DAPM_MIXER("INTERNAL_BT_SCO_RX_Voice Mixer",
737 SND_SOC_NOPM, 0, 0,
738 bt_sco_rx_voice_mixer_controls,
739 ARRAY_SIZE(bt_sco_rx_voice_mixer_controls)),
Laxminath Kasam32657ec2011-08-01 19:26:57 +0530740 SND_SOC_DAPM_MIXER("AFE_PCM_RX_Voice Mixer",
741 SND_SOC_NOPM, 0, 0,
742 afe_pcm_rx_voice_mixer_controls,
743 ARRAY_SIZE(afe_pcm_rx_voice_mixer_controls)),
Bhalchandra Gajare0e795c42011-08-15 18:10:30 -0700744 SND_SOC_DAPM_MIXER("AUX_PCM_RX_Voice Mixer",
745 SND_SOC_NOPM, 0, 0,
746 aux_pcm_rx_voice_mixer_controls,
747 ARRAY_SIZE(aux_pcm_rx_voice_mixer_controls)),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700748 SND_SOC_DAPM_MIXER("Voice_Tx Mixer",
749 SND_SOC_NOPM, 0, 0, tx_voice_mixer_controls,
750 ARRAY_SIZE(tx_voice_mixer_controls)),
751 SND_SOC_DAPM_MIXER("Voip_Tx Mixer",
752 SND_SOC_NOPM, 0, 0, tx_voip_mixer_controls,
753 ARRAY_SIZE(tx_voip_mixer_controls)),
754 SND_SOC_DAPM_MIXER("INTERNAL_BT_SCO_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
755 int_bt_sco_rx_mixer_controls, ARRAY_SIZE(int_bt_sco_rx_mixer_controls)),
756 SND_SOC_DAPM_MIXER("INTERNAL_FM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
757 int_fm_rx_mixer_controls, ARRAY_SIZE(int_fm_rx_mixer_controls)),
Laxminath Kasam32657ec2011-08-01 19:26:57 +0530758 SND_SOC_DAPM_MIXER("AFE_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
759 afe_pcm_rx_mixer_controls, ARRAY_SIZE(afe_pcm_rx_mixer_controls)),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700760 SND_SOC_DAPM_MIXER("SLIMBUS_0_RX Port Mixer",
761 SND_SOC_NOPM, 0, 0, sbus_0_rx_port_mixer_controls,
762 ARRAY_SIZE(sbus_0_rx_port_mixer_controls)),
763};
764
765static const struct snd_soc_dapm_route intercon[] = {
766 {"PRI_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
767 {"PRI_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
768 {"PRI_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
769 {"PRI_I2S_RX", NULL, "PRI_RX Audio Mixer"},
770
771 {"SLIMBUS_0_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
772 {"SLIMBUS_0_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
773 {"SLIMBUS_0_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
774 {"SLIMBUS_0_RX", NULL, "SLIMBUS_0_RX Audio Mixer"},
775
776 {"HDMI Mixer", "MultiMedia1", "MM_DL1"},
777 {"HDMI Mixer", "MultiMedia2", "MM_DL2"},
Asish Bhattacharyac592eed2011-09-16 17:43:02 +0530778 {"HDMI Mixer", "MultiMedia3", "MM_DL3"},
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700779 {"HDMI", NULL, "HDMI Mixer"},
780
781 {"MultiMedia1 Mixer", "PRI_TX", "PRI_I2S_TX"},
782 {"MultiMedia1 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
Bhalchandra Gajare0e795c42011-08-15 18:10:30 -0700783 {"MultiMedia1 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700784
785 {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
786 {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
Asish Bhattacharyac592eed2011-09-16 17:43:02 +0530787 {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700788 {"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX Audio Mixer"},
789
790 {"INTERNAL_FM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
791 {"INTERNAL_FM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
Asish Bhattacharyac592eed2011-09-16 17:43:02 +0530792 {"INTERNAL_FM_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700793 {"INT_FM_RX", NULL, "INTERNAL_FM_RX Audio Mixer"},
794
Laxminath Kasam32657ec2011-08-01 19:26:57 +0530795 {"AFE_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
796 {"AFE_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
797 {"AFE_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
798 {"PCM_RX", NULL, "AFE_PCM_RX Audio Mixer"},
799
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700800 {"MultiMedia1 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
801 {"MultiMedia1 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
Laxminath Kasam32657ec2011-08-01 19:26:57 +0530802
803 {"MultiMedia1 Mixer", "AFE_PCM_TX", "PCM_TX"},
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700804 {"MM_UL1", NULL, "MultiMedia1 Mixer"},
Jayasena Sangaraboina99fee652011-09-19 07:43:13 -0700805 {"MultiMedia2 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
806 {"MM_UL2", NULL, "MultiMedia2 Mixer"},
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700807
Bhalchandra Gajare0e795c42011-08-15 18:10:30 -0700808 {"AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
809 {"AUX_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
810 {"AUX_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
811 {"AUX_PCM_RX", NULL, "AUX_PCM_RX Audio Mixer"},
812
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700813 {"PRI_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
814 {"PRI_RX_Voice Mixer", "Voip", "VOIP_DL"},
815 {"PRI_I2S_RX", NULL, "PRI_RX_Voice Mixer"},
816
817 {"SLIM_0_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
818 {"SLIM_0_RX_Voice Mixer", "Voip", "VOIP_DL"},
819 {"SLIMBUS_0_RX", NULL, "SLIM_0_RX_Voice Mixer"},
820
821 {"INTERNAL_BT_SCO_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
822 {"INTERNAL_BT_SCO_RX_Voice Mixer", "Voip", "VOIP_DL"},
823 {"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX_Voice Mixer"},
824
Laxminath Kasam32657ec2011-08-01 19:26:57 +0530825 {"AFE_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
826 {"AFE_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
827 {"PCM_RX", NULL, "AFE_PCM_RX_Voice Mixer"},
828
Bhalchandra Gajare0e795c42011-08-15 18:10:30 -0700829 {"AUX_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
830 {"AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
831 {"AUX_PCM_RX", NULL, "AUX_PCM_RX_Voice Mixer"},
832
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700833 {"Voice_Tx Mixer", "PRI_TX_Voice", "PRI_I2S_TX"},
834 {"Voice_Tx Mixer", "SLIM_0_TX_Voice", "SLIMBUS_0_TX"},
835 {"Voice_Tx Mixer", "INTERNAL_BT_SCO_TX_Voice", "INT_BT_SCO_TX"},
Laxminath Kasam32657ec2011-08-01 19:26:57 +0530836 {"Voice_Tx Mixer", "AFE_PCM_TX_Voice", "PCM_TX"},
Bhalchandra Gajare0e795c42011-08-15 18:10:30 -0700837 {"Voice_Tx Mixer", "AUX_PCM_TX_Voice", "AUX_PCM_TX"},
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700838 {"CS-VOICE_UL1", NULL, "Voice_Tx Mixer"},
839 {"Voip_Tx Mixer", "PRI_TX_Voip", "PRI_I2S_TX"},
840 {"Voip_Tx Mixer", "SLIM_0_TX_Voip", "SLIMBUS_0_TX"},
841 {"Voip_Tx Mixer", "INTERNAL_BT_SCO_TX_Voip", "INT_BT_SCO_TX"},
Laxminath Kasam32657ec2011-08-01 19:26:57 +0530842 {"Voip_Tx Mixer", "AFE_PCM_TX_Voip", "PCM_TX"},
Bhalchandra Gajare0e795c42011-08-15 18:10:30 -0700843 {"Voip_Tx Mixer", "AUX_PCM_TX_Voip", "AUX_PCM_TX"},
844
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700845 {"VOIP_UL", NULL, "Voip_Tx Mixer"},
Sriranjan Srikantama4969dd2011-07-14 00:34:56 -0700846 {"SLIMBUS_0_RX", "Switch", "SLIM0_DL_HL"},
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700847 {"SLIM0_UL_HL", NULL, "SLIMBUS_0_TX"},
848 {"INT_FM_RX", NULL, "INTFM_DL_HL"},
849 {"INTFM_UL_HL", NULL, "INT_FM_TX"},
850 {"SLIMBUS_0_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
851 {"SLIMBUS_0_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
852 {"SLIMBUS_0_RX", NULL, "SLIMBUS_0_RX Port Mixer"},
853};
854
Patrick Laicf999112011-08-23 11:27:20 -0700855static int msm_pcm_routing_hw_params(struct snd_pcm_substream *substream,
856 struct snd_pcm_hw_params *params)
857{
858 struct snd_soc_pcm_runtime *rtd = substream->private_data;
859 unsigned int be_id = rtd->dai_link->be_id;
860
861 mutex_lock(&routing_lock);
862 msm_bedais[be_id].hw_params = params;
863 mutex_unlock(&routing_lock);
864 return 0;
865}
866
867static int msm_pcm_routing_close(struct snd_pcm_substream *substream)
868{
869 struct snd_soc_pcm_runtime *rtd = substream->private_data;
870 unsigned int be_id = rtd->dai_link->be_id;
871 int i, session_type;
872 struct msm_pcm_routing_bdai_data *bedai;
873
874 if (be_id >= MSM_BACKEND_DAI_MAX) {
875 pr_err("%s: unexpected be_id %d\n", __func__, be_id);
876 return -EINVAL;
877 }
878
879 bedai = &msm_bedais[be_id];
880
881 session_type = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
882 0 : 1);
883
884 mutex_lock(&routing_lock);
885
886 for_each_set_bit(i, &bedai->fe_sessions, MSM_FRONTEND_DAI_MAX) {
887 if (fe_dai_map[i][session_type] != INVALID_SESSION)
888 adm_close(bedai->port_id);
889 }
890
891 bedai->active = 0;
892 bedai->hw_params = NULL;
893
894 mutex_unlock(&routing_lock);
895
896 return 0;
897}
898
899static int msm_pcm_routing_prepare(struct snd_pcm_substream *substream)
900{
901 struct snd_soc_pcm_runtime *rtd = substream->private_data;
902 unsigned int be_id = rtd->dai_link->be_id;
903 int i, path_type, session_type;
904 struct msm_pcm_routing_bdai_data *bedai;
905
906 if (be_id >= MSM_BACKEND_DAI_MAX) {
907 pr_err("%s: unexpected be_id %d\n", __func__, be_id);
908 return -EINVAL;
909 }
910
911
912 bedai = &msm_bedais[be_id];
913
914 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
915 path_type = ADM_PATH_PLAYBACK;
916 session_type = SESSION_TYPE_RX;
917 } else {
918 path_type = ADM_PATH_LIVE_REC;
919 session_type = SESSION_TYPE_TX;
920 }
921
922 mutex_lock(&routing_lock);
923
924 if (bedai->active == 1)
925 goto done; /* Ignore prepare if back-end already active */
926
927 /* AFE port is not active at this point. However, still
928 * go ahead setting active flag under the notion that
929 * QDSP6 is able to handle ADM starting before AFE port
930 * is started.
931 */
932 bedai->active = 1;
933
934 for_each_set_bit(i, &bedai->fe_sessions, MSM_FRONTEND_DAI_MAX) {
935 if (fe_dai_map[i][session_type] != INVALID_SESSION) {
936 adm_open(bedai->port_id, path_type,
937 params_rate(bedai->hw_params),
938 params_channels(bedai->hw_params),
939 DEFAULT_COPP_TOPOLOGY);
940 msm_pcm_routing_build_matrix(i,
941 fe_dai_map[i][session_type], path_type);
942 }
943 }
944
945done:
946 mutex_unlock(&routing_lock);
947
948 return 0;
949}
950
951static struct snd_pcm_ops msm_routing_pcm_ops = {
952 .hw_params = msm_pcm_routing_hw_params,
953 .close = msm_pcm_routing_close,
954 .prepare = msm_pcm_routing_prepare,
955};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700956
957static unsigned int msm_routing_read(struct snd_soc_platform *platform,
958 unsigned int reg)
959{
960 dev_dbg(platform->dev, "reg %x\n", reg);
961 return 0;
962}
963
964/* Not used but frame seems to require it */
965static int msm_routing_write(struct snd_soc_platform *platform,
966 unsigned int reg, unsigned int val)
967{
968 dev_dbg(platform->dev, "reg %x val %x\n", reg, val);
969 return 0;
970}
971
972/* Not used but frame seems to require it */
973static int msm_routing_probe(struct snd_soc_platform *platform)
974{
975 snd_soc_dapm_new_controls(&platform->dapm, msm_qdsp6_widgets,
976 ARRAY_SIZE(msm_qdsp6_widgets));
977 snd_soc_dapm_add_routes(&platform->dapm, intercon,
978 ARRAY_SIZE(intercon));
979
980 snd_soc_dapm_new_widgets(&platform->dapm);
981
Jayasena Sangaraboinacb1e22f2011-07-18 10:36:57 -0700982 snd_soc_add_platform_controls(platform,
983 int_fm_vol_mixer_controls,
984 ARRAY_SIZE(int_fm_vol_mixer_controls));
Asish Bhattacharya0ec76182011-07-29 16:58:11 +0530985
986 snd_soc_add_platform_controls(platform,
987 lpa_vol_mixer_controls,
988 ARRAY_SIZE(lpa_vol_mixer_controls));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700989 return 0;
990}
991
992static struct snd_soc_platform_driver msm_soc_routing_platform = {
993 .ops = &msm_routing_pcm_ops,
994 .probe = msm_routing_probe,
995 .read = msm_routing_read,
996 .write = msm_routing_write,
997};
998
999static __devinit int msm_routing_pcm_probe(struct platform_device *pdev)
1000{
1001 dev_dbg(&pdev->dev, "dev name %s\n", dev_name(&pdev->dev));
1002 return snd_soc_register_platform(&pdev->dev,
1003 &msm_soc_routing_platform);
1004}
1005
1006static int msm_routing_pcm_remove(struct platform_device *pdev)
1007{
1008 snd_soc_unregister_platform(&pdev->dev);
1009 return 0;
1010}
1011
1012static struct platform_driver msm_routing_pcm_driver = {
1013 .driver = {
1014 .name = "msm-pcm-routing",
1015 .owner = THIS_MODULE,
1016 },
1017 .probe = msm_routing_pcm_probe,
1018 .remove = __devexit_p(msm_routing_pcm_remove),
1019};
1020
1021static int __init msm_soc_routing_platform_init(void)
1022{
Neema Shettyfeea7742011-09-11 12:30:36 -07001023 mutex_init(&routing_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001024 return platform_driver_register(&msm_routing_pcm_driver);
1025}
1026module_init(msm_soc_routing_platform_init);
1027
1028static void __exit msm_soc_routing_platform_exit(void)
1029{
1030 platform_driver_unregister(&msm_routing_pcm_driver);
1031}
1032module_exit(msm_soc_routing_platform_exit);
1033
1034MODULE_DESCRIPTION("MSM routing platform driver");
1035MODULE_LICENSE("GPL v2");