card-dra7-evm: Add support for dra7evm sound card
[omap-audio:omap-audio-tool.git] / card-dra7-evm.c
1 /*
2  * card-dra7-evm.c - based on card-omap-abe.c
3  *
4  * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
5  *
6  *
7  *  Redistribution and use in source and binary forms, with or without
8  *  modification, are permitted provided that the following conditions
9  *  are met:
10  *
11  *    Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  *    Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the
17  *    distribution.
18  *
19  *    Neither the name of Texas Instruments Incorporated nor the names of
20  *    its contributors may be used to endorse or promote products derived
21  *    from this software without specific prior written permission.
22  *
23  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26  *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27  *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28  *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29  *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30  *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31  *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32  *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33  *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34  *
35  */
36
37 #include <errno.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <assert.h>
41 #include <stdlib.h>
42 #include <tinyalsa/asoundlib.h>
43
44 #include "module.h"
45 #include "route.h"
46 #include "alsa-control.h"
47 #include "mixer_cache.h"
48
49 #define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
50
51 /*
52  * Supported cards (NULL terminated list)
53  */
54
55 static const char* supported_cards[] = {
56         "VayuEVM",
57         "dra7evm",
58         NULL
59 };
60
61 /*
62  * Defaults
63  */
64
65 static struct audio_tool_mixer_control_info g_mix_defaults[] = {
66         /* Media port */
67         /* Input: Mic */
68         CTRL_BOOL1("Left PGA Mixer Mic3L Switch", 1),
69         CTRL_BOOL1("Right PGA Mixer Mic3R Switch", 1),
70         CTRL_BOOL2("PGA Capture Switch", 1, 1),
71         CTRL_INT2("PGA Capture Volume", 24, 24),
72
73         /* Output: Headphone */
74         CTRL_ENUM1("Left DAC Mux", "DAC_L1"),
75         CTRL_ENUM1("Right DAC Mux", "DAC_R1"),
76         CTRL_BOOL1("Left HP Mixer DACL1 Switch", 1),
77         CTRL_BOOL1("Right HP Mixer DACR1 Switch", 1),
78         CTRL_INT2("HP DAC Playback Volume", 118, 118),
79         CTRL_BOOL2("HP Playback Switch", 1, 1),
80         CTRL_INT2("PCM Playback Volume", 127, 127),
81
82         /* Multichannel port */
83         /* Codec-A Input: Line-In */
84         CTRL_BOOL1("J3A Left PGA Mixer Line1L Switch", 1),
85         CTRL_BOOL1("J3A Right PGA Mixer Line1R Switch", 1),
86         CTRL_BOOL2("J3A PGA Capture Switch", 1, 1),
87         CTRL_INT2("J3A PGA Capture Volume", 0, 0),
88         CTRL_ENUM1("J3A Left Line1L Mux", "differential"),
89         CTRL_ENUM1("J3A Right Line1L Mux", "differential"),
90         CTRL_ENUM1("J3A Left Line1R Mux", "differential"),
91         CTRL_ENUM1("J3A Right Line1R Mux", "differential"),
92
93         /* Codec-B Input: Mic */
94         CTRL_BOOL1("J3B Left PGA Mixer Line1L Switch", 1),
95         CTRL_BOOL1("J3B Right PGA Mixer Line1R Switch", 1),
96         CTRL_BOOL2("J3B PGA Capture Switch", 1, 1),
97         CTRL_INT2("J3B PGA Capture Volume", 0, 0),
98         CTRL_ENUM1("J3B Left Line1L Mux", "differential"),
99         CTRL_ENUM1("J3B Right Line1L Mux", "differential"),
100         CTRL_ENUM1("J3B Left Line1R Mux", "differential"),
101         CTRL_ENUM1("J3B Right Line1R Mux", "differential"),
102
103         /* Codec-A Output: Line-Out */
104         CTRL_ENUM1("J3A Left DAC Mux", "DAC_L1"),
105         CTRL_ENUM1("J3A Right DAC Mux", "DAC_R1"),
106         CTRL_BOOL1("J3A Left Line Mixer DACL1 Switch", 1),
107         CTRL_BOOL1("J3A Right Line Mixer DACR1 Switch", 1),
108         CTRL_INT2("J3A Line DAC Playback Volume", 118, 118),
109         CTRL_BOOL2("J3A Line Playback Switch", 1, 1),
110         CTRL_INT2("J3A PCM Playback Volume", 127, 127),
111
112         /* Codec-B Output: Line-Out */
113         CTRL_ENUM1("J3B Left DAC Mux", "DAC_L1"),
114         CTRL_ENUM1("J3B Right DAC Mux", "DAC_R1"),
115         CTRL_BOOL1("J3B Left Line Mixer DACL1 Switch", 1),
116         CTRL_BOOL1("J3B Right Line Mixer DACR1 Switch", 1),
117         CTRL_INT2("J3B Line DAC Playback Volume", 118, 118),
118         CTRL_BOOL2("J3B Line Playback Switch", 1, 1),
119         CTRL_INT2("J3B PCM Playback Volume", 127, 127),
120
121         /* Codec-C Output: Line-Out */
122         CTRL_ENUM1("J3C Left DAC Mux", "DAC_L1"),
123         CTRL_ENUM1("J3C Right DAC Mux", "DAC_R1"),
124         CTRL_BOOL1("J3C Left Line Mixer DACL1 Switch", 1),
125         CTRL_BOOL1("J3C Right Line Mixer DACR1 Switch", 1),
126         CTRL_INT2("J3C Line DAC Playback Volume", 118, 118),
127         CTRL_BOOL2("J3C Line Playback Switch", 1, 1),
128         CTRL_INT2("J3C PCM Playback Volume", 127, 127),
129 };
130
131 static struct audio_tool_mixer_cache g_mix_cache = {
132         .count = ARRAY_SIZE(g_mix_defaults),
133         .ctrls = g_mix_defaults,
134 };
135
136 static int get_mixer_defaults(struct audio_tool_mixer_cache *cache)
137 {
138         struct audio_tool_mixer_cache *defs = &g_mix_cache;
139         int m, n, i;
140         int ret = 0;
141
142         /* initialize static id's */
143         for (n = 0; n < g_mix_cache.count; ++n)
144                 g_mix_cache.ctrls[n].id = n;
145
146         mixer_cache_reset_touch(defs);
147         mixer_cache_reset_touch(cache);
148
149         for (m = 0; m < cache->count; ++m) {
150                 n = mixer_cache_get_id_by_name(defs, cache->ctrls[m].name);
151                 if (n < 0) {
152                         switch (cache->ctrls[m].type) {
153                         case MIXER_CTL_TYPE_BOOL:
154                         case MIXER_CTL_TYPE_INT:
155                                 for (i = 0; i < cache->ctrls[m].num_values; i++)
156                                         cache->ctrls[m].value.integer[i] = 0;
157                                 break;
158                         case MIXER_CTL_TYPE_ENUM:
159                                 for (i = 0; i < cache->ctrls[m].num_values; i++)
160                                         strcpy(cache->ctrls[m].value.enumerated[i],
161                                                "Off");
162                                 break;
163                         default:
164                                 break;
165                         }
166                 } else {
167                         if (cache->ctrls[m].type != defs->ctrls[n].type) {
168                                 fprintf(stderr, "Warning: type mismatch on %s\n",
169                                         cache->ctrls[m].name);
170                                 ret = 1;
171                                 continue;
172                         }
173
174                         memcpy(&cache->ctrls[m].value, &defs->ctrls[n].value,
175                                sizeof(g_mix_defaults[0].value));
176                         mixer_cache_touch(defs, n);
177                 }
178
179                 mixer_cache_touch(cache, m);
180         }
181
182         ret = mixer_cache_audit_touch(cache, 1) ? 1 : ret;
183         ret = mixer_cache_audit_touch(defs, 1) ? 1 : ret;
184
185         return ret;
186 }
187
188 /*
189  * strictly speaking they are not frontends, but will be used
190  * to identify the CODEC they are associated with
191  */
192 static char* g_frontends[] = {
193         "Media",
194         "JAMR3-A",
195         "JAMR3-B",
196         "JAMR3-C",
197         "J3A", /* alias of "JAMR3-A" */
198         "J3B", /* alias of "JAMR3-B" */
199         "J3C", /* alias of "JAMR3-C" */
200         0,
201 };
202
203 static char* g_capture_backends[] = {
204         "Mic",
205         "LineIn",
206         0,
207 };
208
209 static char* g_playback_backends[] = {
210         "Headphone",
211         "LineOut",
212         0,
213 };
214
215 static int get_fe_be_names(int direction, char ***fes, char ***bes)
216 {
217         *fes = g_frontends;
218
219         if (direction == AUDIO_DIRECTION_PLAYBACK)
220                 *bes = g_playback_backends;
221         else
222                 *bes = g_capture_backends;
223
224         return 0;
225 }
226
227 /*
228  * Route settings
229  */
230
231 static struct route_setting g_line1_se_mix[] = {
232         RS_INT("Left PGA Mixer Line1L Switch", 1),
233         RS_INT("Right PGA Mixer Line1R Switch", 1),
234         RS_INT("PGA Capture Switch", 1),
235         RS_INT("PGA Capture Volume", 0), /* 0dB */
236         RS_ENUM("Left Line1L Mux", "single-ended"),
237         RS_ENUM("Right Line1R Mux", "single-ended"),
238         RS_NULL,
239 };
240
241 static struct route_setting g_line1_diff_mix[] = {
242         RS_INT("Left PGA Mixer Line1L Switch", 1),
243         RS_INT("Right PGA Mixer Line1R Switch", 1),
244         RS_INT("PGA Capture Switch", 1),
245         RS_INT("PGA Capture Volume", 0), /* 0dB */
246         RS_ENUM("Left Line1L Mux", "differential"),
247         RS_ENUM("Right Line1L Mux", "differential"),
248         RS_ENUM("Left Line1R Mux", "differential"),
249         RS_ENUM("Right Line1R Mux", "differential"),
250         RS_NULL,
251 };
252
253 /* Mic1's pins are same than Line1's, same controls but higher gain */
254 static struct route_setting g_mic1_diff_mix[] = {
255         RS_INT("Left PGA Mixer Line1L Switch", 1),
256         RS_INT("Right PGA Mixer Line1R Switch", 1),
257         RS_INT("PGA Capture Switch", 1),
258         RS_INT("PGA Capture Volume", 24), /* 12dB */
259         RS_ENUM("Left Line1L Mux", "differential"),
260         RS_ENUM("Right Line1L Mux", "differential"),
261         RS_ENUM("Left Line1R Mux", "differential"),
262         RS_ENUM("Right Line1R Mux", "differential"),
263         RS_NULL,
264 };
265
266 static struct route_setting g_mic3_mix[] = {
267         RS_INT("Left PGA Mixer Mic3L Switch", 1),
268         RS_INT("Right PGA Mixer Mic3R Switch", 1),
269         RS_INT("PGA Capture Switch", 1),
270         RS_INT("PGA Capture Volume", 24), /* 12dB */
271         RS_NULL,
272 };
273
274 static struct route_setting g_hpout_mix[] = {
275         RS_ENUM("Left DAC Mux", "DAC_L1"),
276         RS_ENUM("Right DAC Mux", "DAC_R1"),
277         RS_INT("Left HP Mixer DACL1 Switch", 1),
278         RS_INT("Right HP Mixer DACR1 Switch", 1),
279         RS_INT("HP DAC Playback Volume", 118), /* 0dB */
280         RS_INT("HP Playback Switch", 1),
281         RS_INT("PCM Playback Volume", 127), /* 0dB */
282         RS_NULL,
283 };
284
285 static struct route_setting g_lineout_mix[] = {
286         RS_ENUM("Left DAC Mux", "DAC_L1"),
287         RS_ENUM("Right DAC Mux", "DAC_R1"),
288         RS_INT("Left Line Mixer DACL1 Switch", 1),
289         RS_INT("Right Line Mixer DACR1 Switch", 1),
290         RS_INT("Line DAC Playback Volume", 118), /* 0dB */
291         RS_INT("Line Playback Switch", 1),
292         RS_INT("PCM Playback Volume", 127), /* 0dB */
293         RS_NULL,
294 };
295
296 static int config_capture(struct mixer *mixer, const char* fe,
297                           const char* be, int enable, int *optional_port)
298 {
299         struct route_setting *route;
300         char *prefix;
301         int is_line_diff;
302         int ret;
303
304         if (!strcmp(fe, "Media")) {
305                 prefix = NULL;
306                 *optional_port = 0;
307
308                 /* Inputs: Mic3, Line1 single-ended */
309                 if (!strcmp(be, "Mic"))
310                         route = g_mic3_mix;
311                 else if (!strcmp(be, "LineIn"))
312                         route = g_line1_se_mix;
313                 else
314                         return -EINVAL;
315
316         } else if (!strcmp(fe, "JAMR3-A") ||
317                    !strcmp(fe, "J3A")) {
318                 prefix = "J3A";
319                 *optional_port = 1;
320
321                 /* Inputs: Mic1 differential, Line1 differential */
322                 if (!strcmp(be, "Mic"))
323                         route = g_mic1_diff_mix;
324                 else if (!strcmp(be, "LineIn"))
325                         route = g_line1_diff_mix;
326                 else
327                         return -EINVAL;
328
329         } else if (!strcmp(fe, "JAMR3-B") ||
330                    !strcmp(fe, "J3B")) {
331                 prefix = "J3B";
332                 *optional_port = 1;
333
334                 /* Inputs: Mic1 differential, Line1 differential */
335                 if (!strcmp(be, "Mic"))
336                         route = g_mic1_diff_mix;
337                 else if (!strcmp(be, "LineIn"))
338                         route = g_line1_diff_mix;
339                 else
340                         return -EINVAL;
341
342         } else if (!strcmp(fe, "JAMR3-C") ||
343                    !strcmp(fe, "J3C")) {
344                 prefix = "J3C";
345                 *optional_port = 1;
346                 is_line_diff = 1;
347
348                 /* Inputs: Mic1 differential, Line1 differential */
349                 if (!strcmp(be, "Mic"))
350                         route = g_mic1_diff_mix;
351                 else if (!strcmp(be, "LineIn"))
352                         route = g_line1_diff_mix;
353                 else
354                         return -EINVAL;
355
356         } else {
357                 return -EINVAL;
358         }
359
360         if (prefix)
361                 ret = set_route_by_array_prefix(mixer, route, enable, prefix);
362         else
363                 ret = set_route_by_array(mixer, route, enable);
364
365         return ret;
366 }
367
368 static int config_playback(struct mixer *mixer, const char* fe,
369                            const char* be, int enable, int *optional_port)
370 {
371         struct route_setting *route;
372         char *prefix;
373         int ret;
374
375         if (!strcmp(fe, "Media")) {
376                 prefix = NULL;
377                 *optional_port = 0;
378         } else if (!strcmp(fe, "JAMR3-A") ||
379                    !strcmp(fe, "J3A")) {
380                 prefix = "J3A";
381                 *optional_port = 1;
382         } else if (!strcmp(fe, "JAMR3-B") ||
383                    !strcmp(fe, "J3B")) {
384                 prefix = "J3B";
385                 *optional_port = 1;
386         } else if (!strcmp(fe, "JAMR3-C") ||
387                    !strcmp(fe, "J3C")) {
388                 prefix = "J3C";
389                 *optional_port = 1;
390         } else {
391                 return -EINVAL;
392         }
393
394         if (!strcmp(be, "Headphone"))
395                 route = g_hpout_mix;
396         else if (!strcmp(be, "LineOut"))
397                 route = g_lineout_mix;
398         else
399                 return -EINVAL;
400
401         if (prefix)
402                 ret = set_route_by_array_prefix(mixer, route, enable, prefix);
403         else
404                 ret = set_route_by_array(mixer, route, enable);
405
406         return ret;
407 }
408
409 static int config(struct mixer *mixer, int direction, const char* fe,
410                   const char* be, int enable, int *optional_port)
411 {
412         if (direction == AUDIO_DIRECTION_PLAYBACK)
413                 return config_playback(mixer, fe, be, enable, optional_port);
414         else
415                 return config_capture(mixer, fe, be, enable, optional_port);
416 }
417
418 static int probe(void)
419 {
420         int card = -ENODEV;
421         const char** cardname;
422
423         for (cardname = supported_cards; *cardname; ++cardname) {
424                 card = ah_card_find_by_name(*cardname);
425                 if (card >= 0)
426                         return 0;
427         }
428
429         return -ENODEV;
430
431 }
432
433 static struct audio_tool_card_module g_dra7_evm_mod = {
434         .type = AUDIO_TOOL_MOD_TYPE_CARD,
435         .name = "",
436         .get_mixer_defaults = get_mixer_defaults,
437         .get_fe_be_names = get_fe_be_names,
438         .config = config,
439         .probe = probe,
440 };
441
442 static void __init init(void)
443 {
444         struct audio_tool_card_module *mod;
445         const char** cardname;
446         int card;
447         int ret;
448
449         for (cardname = supported_cards ; *cardname ; ++cardname) {
450                 card = ah_card_find_by_name(*cardname);
451                 if (card < 0)
452                         continue;
453
454                 mod = malloc(sizeof(struct audio_tool_card_module));
455                 if (!mod) {
456                         fprintf(stderr, "Error: could not allocate memory for module %s\n",
457                                 *cardname);
458                         return;
459                 }
460                 *mod = g_dra7_evm_mod;
461                 mod->name = *cardname;
462
463                 ret = audio_tool_module_register((struct audio_tool_module*)mod);
464                 if (ret) {
465                         fprintf(stderr, "Error: could not register module %s (%s)\n",
466                                 *cardname, strerror(ret));
467                         free(mod);
468                 }
469         }
470 }