blob: 728f3ac2f304227f803380084d997d1a642ce450 [file] [log] [blame]
Richard Purdie2b97eab2006-10-06 18:32:18 +02001/*
2 * soc-dapm.c -- ALSA SoC Dynamic Audio Power Management
3 *
4 * Copyright 2005 Wolfson Microelectronics PLC.
5 * Author: Liam Girdwood
6 * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version.
12 *
Richard Purdie2b97eab2006-10-06 18:32:18 +020013 * Features:
14 * o Changes power status of internal codec blocks depending on the
15 * dynamic configuration of codec internal audio paths and active
16 * DAC's/ADC's.
17 * o Platform power domain - can support external components i.e. amps and
18 * mic/meadphone insertion events.
19 * o Automatic Mic Bias support
20 * o Jack insertion power event initiation - e.g. hp insertion will enable
21 * sinks, dacs, etc
Robert P. J. Day3a4fa0a2007-10-19 23:10:43 +020022 * o Delayed powerdown of audio susbsystem to reduce pops between a quick
Richard Purdie2b97eab2006-10-06 18:32:18 +020023 * device reopen.
24 *
25 * Todo:
26 * o DAPM power change sequencing - allow for configurable per
27 * codec sequences.
28 * o Support for analogue bias optimisation.
29 * o Support for reduced codec oversampling rates.
30 * o Support for reduced codec bias currents.
31 */
32
33#include <linux/module.h>
34#include <linux/moduleparam.h>
35#include <linux/init.h>
36#include <linux/delay.h>
37#include <linux/pm.h>
38#include <linux/bitops.h>
39#include <linux/platform_device.h>
40#include <linux/jiffies.h>
Richard Purdie2b97eab2006-10-06 18:32:18 +020041#include <sound/core.h>
42#include <sound/pcm.h>
43#include <sound/pcm_params.h>
44#include <sound/soc-dapm.h>
45#include <sound/initval.h>
46
47/* debug */
48#define DAPM_DEBUG 0
49#if DAPM_DEBUG
50#define dump_dapm(codec, action) dbg_dump_dapm(codec, action)
51#define dbg(format, arg...) printk(format, ## arg)
52#else
53#define dump_dapm(codec, action)
54#define dbg(format, arg...)
55#endif
56
57#define POP_DEBUG 0
58#if POP_DEBUG
59#define POP_TIME 500 /* 500 msecs - change if pop debug is too fast */
Rene Hermand86d0192007-09-18 18:10:49 +020060#define pop_wait(time) schedule_timeout_uninterruptible(msecs_to_jiffies(time))
Richard Purdie2b97eab2006-10-06 18:32:18 +020061#define pop_dbg(format, arg...) printk(format, ## arg); pop_wait(POP_TIME)
62#else
63#define pop_dbg(format, arg...)
64#define pop_wait(time)
65#endif
66
67/* dapm power sequences - make this per codec in the future */
68static int dapm_up_seq[] = {
69 snd_soc_dapm_pre, snd_soc_dapm_micbias, snd_soc_dapm_mic,
70 snd_soc_dapm_mux, snd_soc_dapm_dac, snd_soc_dapm_mixer, snd_soc_dapm_pga,
71 snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, snd_soc_dapm_post
72};
73static int dapm_down_seq[] = {
74 snd_soc_dapm_pre, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk,
75 snd_soc_dapm_pga, snd_soc_dapm_mixer, snd_soc_dapm_dac, snd_soc_dapm_mic,
76 snd_soc_dapm_micbias, snd_soc_dapm_mux, snd_soc_dapm_post
77};
78
79static int dapm_status = 1;
80module_param(dapm_status, int, 0);
81MODULE_PARM_DESC(dapm_status, "enable DPM sysfs entries");
82
83/* create a new dapm widget */
Takashi Iwai88cb4292007-02-05 14:56:20 +010084static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
Richard Purdie2b97eab2006-10-06 18:32:18 +020085 const struct snd_soc_dapm_widget *_widget)
86{
Takashi Iwai88cb4292007-02-05 14:56:20 +010087 return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL);
Richard Purdie2b97eab2006-10-06 18:32:18 +020088}
89
90/* set up initial codec paths */
91static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
92 struct snd_soc_dapm_path *p, int i)
93{
94 switch (w->id) {
95 case snd_soc_dapm_switch:
96 case snd_soc_dapm_mixer: {
97 int val;
98 int reg = w->kcontrols[i].private_value & 0xff;
99 int shift = (w->kcontrols[i].private_value >> 8) & 0x0f;
100 int mask = (w->kcontrols[i].private_value >> 16) & 0xff;
101 int invert = (w->kcontrols[i].private_value >> 24) & 0x01;
102
103 val = snd_soc_read(w->codec, reg);
104 val = (val >> shift) & mask;
105
106 if ((invert && !val) || (!invert && val))
107 p->connect = 1;
108 else
109 p->connect = 0;
110 }
111 break;
112 case snd_soc_dapm_mux: {
113 struct soc_enum *e = (struct soc_enum *)w->kcontrols[i].private_value;
114 int val, item, bitmask;
115
116 for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
117 ;
118 val = snd_soc_read(w->codec, e->reg);
119 item = (val >> e->shift_l) & (bitmask - 1);
120
121 p->connect = 0;
122 for (i = 0; i < e->mask; i++) {
123 if (!(strcmp(p->name, e->texts[i])) && item == i)
124 p->connect = 1;
125 }
126 }
127 break;
128 /* does not effect routing - always connected */
129 case snd_soc_dapm_pga:
130 case snd_soc_dapm_output:
131 case snd_soc_dapm_adc:
132 case snd_soc_dapm_input:
133 case snd_soc_dapm_dac:
134 case snd_soc_dapm_micbias:
135 case snd_soc_dapm_vmid:
136 p->connect = 1;
137 break;
138 /* does effect routing - dynamically connected */
139 case snd_soc_dapm_hp:
140 case snd_soc_dapm_mic:
141 case snd_soc_dapm_spk:
142 case snd_soc_dapm_line:
143 case snd_soc_dapm_pre:
144 case snd_soc_dapm_post:
145 p->connect = 0;
146 break;
147 }
148}
149
150/* connect mux widget to it's interconnecting audio paths */
151static int dapm_connect_mux(struct snd_soc_codec *codec,
152 struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
153 struct snd_soc_dapm_path *path, const char *control_name,
154 const struct snd_kcontrol_new *kcontrol)
155{
156 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
157 int i;
158
159 for (i = 0; i < e->mask; i++) {
160 if (!(strcmp(control_name, e->texts[i]))) {
161 list_add(&path->list, &codec->dapm_paths);
162 list_add(&path->list_sink, &dest->sources);
163 list_add(&path->list_source, &src->sinks);
164 path->name = (char*)e->texts[i];
165 dapm_set_path_status(dest, path, 0);
166 return 0;
167 }
168 }
169
170 return -ENODEV;
171}
172
173/* connect mixer widget to it's interconnecting audio paths */
174static int dapm_connect_mixer(struct snd_soc_codec *codec,
175 struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
176 struct snd_soc_dapm_path *path, const char *control_name)
177{
178 int i;
179
180 /* search for mixer kcontrol */
181 for (i = 0; i < dest->num_kcontrols; i++) {
182 if (!strcmp(control_name, dest->kcontrols[i].name)) {
183 list_add(&path->list, &codec->dapm_paths);
184 list_add(&path->list_sink, &dest->sources);
185 list_add(&path->list_source, &src->sinks);
186 path->name = dest->kcontrols[i].name;
187 dapm_set_path_status(dest, path, i);
188 return 0;
189 }
190 }
191 return -ENODEV;
192}
193
194/* update dapm codec register bits */
195static int dapm_update_bits(struct snd_soc_dapm_widget *widget)
196{
197 int change, power;
198 unsigned short old, new;
199 struct snd_soc_codec *codec = widget->codec;
200
201 /* check for valid widgets */
202 if (widget->reg < 0 || widget->id == snd_soc_dapm_input ||
203 widget->id == snd_soc_dapm_output ||
204 widget->id == snd_soc_dapm_hp ||
205 widget->id == snd_soc_dapm_mic ||
206 widget->id == snd_soc_dapm_line ||
207 widget->id == snd_soc_dapm_spk)
208 return 0;
209
210 power = widget->power;
211 if (widget->invert)
212 power = (power ? 0:1);
213
214 old = snd_soc_read(codec, widget->reg);
215 new = (old & ~(0x1 << widget->shift)) | (power << widget->shift);
216
217 change = old != new;
218 if (change) {
219 pop_dbg("pop test %s : %s in %d ms\n", widget->name,
220 widget->power ? "on" : "off", POP_TIME);
221 snd_soc_write(codec, widget->reg, new);
222 pop_wait(POP_TIME);
223 }
Mark Brown32f48762008-04-14 12:59:27 +0200224 dbg("reg %x old %x new %x change %d\n", widget->reg, old, new, change);
Richard Purdie2b97eab2006-10-06 18:32:18 +0200225 return change;
226}
227
228/* ramps the volume up or down to minimise pops before or after a
229 * DAPM power event */
230static int dapm_set_pga(struct snd_soc_dapm_widget *widget, int power)
231{
232 const struct snd_kcontrol_new *k = widget->kcontrols;
233
234 if (widget->muted && !power)
235 return 0;
236 if (!widget->muted && power)
237 return 0;
238
239 if (widget->num_kcontrols && k) {
240 int reg = k->private_value & 0xff;
241 int shift = (k->private_value >> 8) & 0x0f;
242 int mask = (k->private_value >> 16) & 0xff;
243 int invert = (k->private_value >> 24) & 0x01;
244
245 if (power) {
246 int i;
247 /* power up has happended, increase volume to last level */
248 if (invert) {
249 for (i = mask; i > widget->saved_value; i--)
250 snd_soc_update_bits(widget->codec, reg, mask, i);
251 } else {
252 for (i = 0; i < widget->saved_value; i++)
253 snd_soc_update_bits(widget->codec, reg, mask, i);
254 }
255 widget->muted = 0;
256 } else {
257 /* power down is about to occur, decrease volume to mute */
258 int val = snd_soc_read(widget->codec, reg);
259 int i = widget->saved_value = (val >> shift) & mask;
260 if (invert) {
261 for (; i < mask; i++)
262 snd_soc_update_bits(widget->codec, reg, mask, i);
263 } else {
264 for (; i > 0; i--)
265 snd_soc_update_bits(widget->codec, reg, mask, i);
266 }
267 widget->muted = 1;
268 }
269 }
270 return 0;
271}
272
273/* create new dapm mixer control */
274static int dapm_new_mixer(struct snd_soc_codec *codec,
275 struct snd_soc_dapm_widget *w)
276{
277 int i, ret = 0;
278 char name[32];
279 struct snd_soc_dapm_path *path;
280
281 /* add kcontrol */
282 for (i = 0; i < w->num_kcontrols; i++) {
283
284 /* match name */
285 list_for_each_entry(path, &w->sources, list_sink) {
286
287 /* mixer/mux paths name must match control name */
288 if (path->name != (char*)w->kcontrols[i].name)
289 continue;
290
291 /* add dapm control with long name */
292 snprintf(name, 32, "%s %s", w->name, w->kcontrols[i].name);
293 path->long_name = kstrdup (name, GFP_KERNEL);
294 if (path->long_name == NULL)
295 return -ENOMEM;
296
297 path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w,
298 path->long_name);
299 ret = snd_ctl_add(codec->card, path->kcontrol);
300 if (ret < 0) {
301 printk(KERN_ERR "asoc: failed to add dapm kcontrol %s\n",
302 path->long_name);
303 kfree(path->long_name);
304 path->long_name = NULL;
305 return ret;
306 }
307 }
308 }
309 return ret;
310}
311
312/* create new dapm mux control */
313static int dapm_new_mux(struct snd_soc_codec *codec,
314 struct snd_soc_dapm_widget *w)
315{
316 struct snd_soc_dapm_path *path = NULL;
317 struct snd_kcontrol *kcontrol;
318 int ret = 0;
319
320 if (!w->num_kcontrols) {
321 printk(KERN_ERR "asoc: mux %s has no controls\n", w->name);
322 return -EINVAL;
323 }
324
325 kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name);
326 ret = snd_ctl_add(codec->card, kcontrol);
327 if (ret < 0)
328 goto err;
329
330 list_for_each_entry(path, &w->sources, list_sink)
331 path->kcontrol = kcontrol;
332
333 return ret;
334
335err:
336 printk(KERN_ERR "asoc: failed to add kcontrol %s\n", w->name);
337 return ret;
338}
339
340/* create new dapm volume control */
341static int dapm_new_pga(struct snd_soc_codec *codec,
342 struct snd_soc_dapm_widget *w)
343{
344 struct snd_kcontrol *kcontrol;
345 int ret = 0;
346
347 if (!w->num_kcontrols)
348 return -EINVAL;
349
350 kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name);
351 ret = snd_ctl_add(codec->card, kcontrol);
352 if (ret < 0) {
353 printk(KERN_ERR "asoc: failed to add kcontrol %s\n", w->name);
354 return ret;
355 }
356
357 return ret;
358}
359
360/* reset 'walked' bit for each dapm path */
361static inline void dapm_clear_walk(struct snd_soc_codec *codec)
362{
363 struct snd_soc_dapm_path *p;
364
365 list_for_each_entry(p, &codec->dapm_paths, list)
366 p->walked = 0;
367}
368
369/*
370 * Recursively check for a completed path to an active or physically connected
371 * output widget. Returns number of complete paths.
372 */
373static int is_connected_output_ep(struct snd_soc_dapm_widget *widget)
374{
375 struct snd_soc_dapm_path *path;
376 int con = 0;
377
378 if (widget->id == snd_soc_dapm_adc && widget->active)
379 return 1;
380
381 if (widget->connected) {
382 /* connected pin ? */
383 if (widget->id == snd_soc_dapm_output && !widget->ext)
384 return 1;
385
386 /* connected jack or spk ? */
387 if (widget->id == snd_soc_dapm_hp || widget->id == snd_soc_dapm_spk ||
388 widget->id == snd_soc_dapm_line)
389 return 1;
390 }
391
392 list_for_each_entry(path, &widget->sinks, list_source) {
393 if (path->walked)
394 continue;
395
396 if (path->sink && path->connect) {
397 path->walked = 1;
398 con += is_connected_output_ep(path->sink);
399 }
400 }
401
402 return con;
403}
404
405/*
406 * Recursively check for a completed path to an active or physically connected
407 * input widget. Returns number of complete paths.
408 */
409static int is_connected_input_ep(struct snd_soc_dapm_widget *widget)
410{
411 struct snd_soc_dapm_path *path;
412 int con = 0;
413
414 /* active stream ? */
415 if (widget->id == snd_soc_dapm_dac && widget->active)
416 return 1;
417
418 if (widget->connected) {
419 /* connected pin ? */
420 if (widget->id == snd_soc_dapm_input && !widget->ext)
421 return 1;
422
423 /* connected VMID/Bias for lower pops */
424 if (widget->id == snd_soc_dapm_vmid)
425 return 1;
426
427 /* connected jack ? */
428 if (widget->id == snd_soc_dapm_mic || widget->id == snd_soc_dapm_line)
429 return 1;
430 }
431
432 list_for_each_entry(path, &widget->sources, list_sink) {
433 if (path->walked)
434 continue;
435
436 if (path->source && path->connect) {
437 path->walked = 1;
438 con += is_connected_input_ep(path->source);
439 }
440 }
441
442 return con;
443}
444
445/*
446 * Scan each dapm widget for complete audio path.
447 * A complete path is a route that has valid endpoints i.e.:-
448 *
449 * o DAC to output pin.
450 * o Input Pin to ADC.
451 * o Input pin to Output pin (bypass, sidetone)
452 * o DAC to ADC (loopback).
453 */
Adrian Bunkd9c96cf2006-11-28 12:10:09 +0100454static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
Richard Purdie2b97eab2006-10-06 18:32:18 +0200455{
456 struct snd_soc_dapm_widget *w;
457 int in, out, i, c = 1, *seq = NULL, ret = 0, power_change, power;
458
459 /* do we have a sequenced stream event */
460 if (event == SND_SOC_DAPM_STREAM_START) {
461 c = ARRAY_SIZE(dapm_up_seq);
462 seq = dapm_up_seq;
463 } else if (event == SND_SOC_DAPM_STREAM_STOP) {
464 c = ARRAY_SIZE(dapm_down_seq);
465 seq = dapm_down_seq;
466 }
467
468 for(i = 0; i < c; i++) {
469 list_for_each_entry(w, &codec->dapm_widgets, list) {
470
471 /* is widget in stream order */
472 if (seq && seq[i] && w->id != seq[i])
473 continue;
474
475 /* vmid - no action */
476 if (w->id == snd_soc_dapm_vmid)
477 continue;
478
479 /* active ADC */
480 if (w->id == snd_soc_dapm_adc && w->active) {
481 in = is_connected_input_ep(w);
482 dapm_clear_walk(w->codec);
483 w->power = (in != 0) ? 1 : 0;
484 dapm_update_bits(w);
485 continue;
486 }
487
488 /* active DAC */
489 if (w->id == snd_soc_dapm_dac && w->active) {
490 out = is_connected_output_ep(w);
491 dapm_clear_walk(w->codec);
492 w->power = (out != 0) ? 1 : 0;
493 dapm_update_bits(w);
494 continue;
495 }
496
497 /* programmable gain/attenuation */
498 if (w->id == snd_soc_dapm_pga) {
499 int on;
500 in = is_connected_input_ep(w);
501 dapm_clear_walk(w->codec);
502 out = is_connected_output_ep(w);
503 dapm_clear_walk(w->codec);
504 w->power = on = (out != 0 && in != 0) ? 1 : 0;
505
506 if (!on)
507 dapm_set_pga(w, on); /* lower volume to reduce pops */
508 dapm_update_bits(w);
509 if (on)
510 dapm_set_pga(w, on); /* restore volume from zero */
511
512 continue;
513 }
514
515 /* pre and post event widgets */
516 if (w->id == snd_soc_dapm_pre) {
517 if (!w->event)
518 continue;
519
520 if (event == SND_SOC_DAPM_STREAM_START) {
Laim Girdwood9af6d952008-01-10 14:41:02 +0100521 ret = w->event(w,
522 NULL, SND_SOC_DAPM_PRE_PMU);
Richard Purdie2b97eab2006-10-06 18:32:18 +0200523 if (ret < 0)
524 return ret;
525 } else if (event == SND_SOC_DAPM_STREAM_STOP) {
Laim Girdwood9af6d952008-01-10 14:41:02 +0100526 ret = w->event(w,
527 NULL, SND_SOC_DAPM_PRE_PMD);
Richard Purdie2b97eab2006-10-06 18:32:18 +0200528 if (ret < 0)
529 return ret;
530 }
531 continue;
532 }
533 if (w->id == snd_soc_dapm_post) {
534 if (!w->event)
535 continue;
536
537 if (event == SND_SOC_DAPM_STREAM_START) {
Laim Girdwood9af6d952008-01-10 14:41:02 +0100538 ret = w->event(w,
539 NULL, SND_SOC_DAPM_POST_PMU);
Richard Purdie2b97eab2006-10-06 18:32:18 +0200540 if (ret < 0)
541 return ret;
542 } else if (event == SND_SOC_DAPM_STREAM_STOP) {
Laim Girdwood9af6d952008-01-10 14:41:02 +0100543 ret = w->event(w,
544 NULL, SND_SOC_DAPM_POST_PMD);
Richard Purdie2b97eab2006-10-06 18:32:18 +0200545 if (ret < 0)
546 return ret;
547 }
548 continue;
549 }
550
551 /* all other widgets */
552 in = is_connected_input_ep(w);
553 dapm_clear_walk(w->codec);
554 out = is_connected_output_ep(w);
555 dapm_clear_walk(w->codec);
556 power = (out != 0 && in != 0) ? 1 : 0;
557 power_change = (w->power == power) ? 0: 1;
558 w->power = power;
559
560 /* call any power change event handlers */
561 if (power_change) {
562 if (w->event) {
563 dbg("power %s event for %s flags %x\n",
564 w->power ? "on" : "off", w->name, w->event_flags);
565 if (power) {
566 /* power up event */
567 if (w->event_flags & SND_SOC_DAPM_PRE_PMU) {
Laim Girdwood9af6d952008-01-10 14:41:02 +0100568 ret = w->event(w,
569 NULL, SND_SOC_DAPM_PRE_PMU);
Richard Purdie2b97eab2006-10-06 18:32:18 +0200570 if (ret < 0)
571 return ret;
572 }
573 dapm_update_bits(w);
574 if (w->event_flags & SND_SOC_DAPM_POST_PMU){
Laim Girdwood9af6d952008-01-10 14:41:02 +0100575 ret = w->event(w,
576 NULL, SND_SOC_DAPM_POST_PMU);
Richard Purdie2b97eab2006-10-06 18:32:18 +0200577 if (ret < 0)
578 return ret;
579 }
580 } else {
581 /* power down event */
582 if (w->event_flags & SND_SOC_DAPM_PRE_PMD) {
Laim Girdwood9af6d952008-01-10 14:41:02 +0100583 ret = w->event(w,
584 NULL, SND_SOC_DAPM_PRE_PMD);
Richard Purdie2b97eab2006-10-06 18:32:18 +0200585 if (ret < 0)
586 return ret;
587 }
588 dapm_update_bits(w);
589 if (w->event_flags & SND_SOC_DAPM_POST_PMD) {
Laim Girdwood9af6d952008-01-10 14:41:02 +0100590 ret = w->event(w,
591 NULL, SND_SOC_DAPM_POST_PMD);
Richard Purdie2b97eab2006-10-06 18:32:18 +0200592 if (ret < 0)
593 return ret;
594 }
595 }
596 } else
597 /* no event handler */
598 dapm_update_bits(w);
599 }
600 }
601 }
602
603 return ret;
604}
605
606#if DAPM_DEBUG
607static void dbg_dump_dapm(struct snd_soc_codec* codec, const char *action)
608{
609 struct snd_soc_dapm_widget *w;
610 struct snd_soc_dapm_path *p = NULL;
611 int in, out;
612
613 printk("DAPM %s %s\n", codec->name, action);
614
615 list_for_each_entry(w, &codec->dapm_widgets, list) {
616
617 /* only display widgets that effect routing */
618 switch (w->id) {
619 case snd_soc_dapm_pre:
620 case snd_soc_dapm_post:
621 case snd_soc_dapm_vmid:
622 continue;
623 case snd_soc_dapm_mux:
624 case snd_soc_dapm_output:
625 case snd_soc_dapm_input:
626 case snd_soc_dapm_switch:
627 case snd_soc_dapm_hp:
628 case snd_soc_dapm_mic:
629 case snd_soc_dapm_spk:
630 case snd_soc_dapm_line:
631 case snd_soc_dapm_micbias:
632 case snd_soc_dapm_dac:
633 case snd_soc_dapm_adc:
634 case snd_soc_dapm_pga:
635 case snd_soc_dapm_mixer:
636 if (w->name) {
637 in = is_connected_input_ep(w);
638 dapm_clear_walk(w->codec);
639 out = is_connected_output_ep(w);
640 dapm_clear_walk(w->codec);
641 printk("%s: %s in %d out %d\n", w->name,
642 w->power ? "On":"Off",in, out);
643
644 list_for_each_entry(p, &w->sources, list_sink) {
645 if (p->connect)
646 printk(" in %s %s\n", p->name ? p->name : "static",
647 p->source->name);
648 }
649 list_for_each_entry(p, &w->sinks, list_source) {
Richard Purdie2b97eab2006-10-06 18:32:18 +0200650 if (p->connect)
651 printk(" out %s %s\n", p->name ? p->name : "static",
652 p->sink->name);
653 }
654 }
655 break;
656 }
657 }
658}
659#endif
660
661/* test and update the power status of a mux widget */
Adrian Bunkd9c96cf2006-11-28 12:10:09 +0100662static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
663 struct snd_kcontrol *kcontrol, int mask,
664 int val, struct soc_enum* e)
Richard Purdie2b97eab2006-10-06 18:32:18 +0200665{
666 struct snd_soc_dapm_path *path;
667 int found = 0;
668
669 if (widget->id != snd_soc_dapm_mux)
670 return -ENODEV;
671
672 if (!snd_soc_test_bits(widget->codec, e->reg, mask, val))
673 return 0;
674
675 /* find dapm widget path assoc with kcontrol */
676 list_for_each_entry(path, &widget->codec->dapm_paths, list) {
677 if (path->kcontrol != kcontrol)
678 continue;
679
680 if (!path->name || ! e->texts[val])
681 continue;
682
683 found = 1;
684 /* we now need to match the string in the enum to the path */
685 if (!(strcmp(path->name, e->texts[val])))
686 path->connect = 1; /* new connection */
687 else
688 path->connect = 0; /* old connection must be powered down */
689 }
690
691 if (found)
692 dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP);
693
694 return 0;
695}
Richard Purdie2b97eab2006-10-06 18:32:18 +0200696
Milan plzik1b075e32008-01-10 14:39:46 +0100697/* test and update the power status of a mixer or switch widget */
Adrian Bunkd9c96cf2006-11-28 12:10:09 +0100698static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
699 struct snd_kcontrol *kcontrol, int reg,
700 int val_mask, int val, int invert)
Richard Purdie2b97eab2006-10-06 18:32:18 +0200701{
702 struct snd_soc_dapm_path *path;
703 int found = 0;
704
Milan plzik1b075e32008-01-10 14:39:46 +0100705 if (widget->id != snd_soc_dapm_mixer &&
706 widget->id != snd_soc_dapm_switch)
Richard Purdie2b97eab2006-10-06 18:32:18 +0200707 return -ENODEV;
708
709 if (!snd_soc_test_bits(widget->codec, reg, val_mask, val))
710 return 0;
711
712 /* find dapm widget path assoc with kcontrol */
713 list_for_each_entry(path, &widget->codec->dapm_paths, list) {
714 if (path->kcontrol != kcontrol)
715 continue;
716
717 /* found, now check type */
718 found = 1;
719 if (val)
720 /* new connection */
721 path->connect = invert ? 0:1;
722 else
723 /* old connection must be powered down */
724 path->connect = invert ? 1:0;
725 break;
726 }
727
728 if (found)
729 dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP);
730
731 return 0;
732}
Richard Purdie2b97eab2006-10-06 18:32:18 +0200733
734/* show dapm widget status in sys fs */
735static ssize_t dapm_widget_show(struct device *dev,
736 struct device_attribute *attr, char *buf)
737{
738 struct snd_soc_device *devdata = dev_get_drvdata(dev);
739 struct snd_soc_codec *codec = devdata->codec;
740 struct snd_soc_dapm_widget *w;
741 int count = 0;
742 char *state = "not set";
743
744 list_for_each_entry(w, &codec->dapm_widgets, list) {
745
746 /* only display widgets that burnm power */
747 switch (w->id) {
748 case snd_soc_dapm_hp:
749 case snd_soc_dapm_mic:
750 case snd_soc_dapm_spk:
751 case snd_soc_dapm_line:
752 case snd_soc_dapm_micbias:
753 case snd_soc_dapm_dac:
754 case snd_soc_dapm_adc:
755 case snd_soc_dapm_pga:
756 case snd_soc_dapm_mixer:
757 if (w->name)
758 count += sprintf(buf + count, "%s: %s\n",
759 w->name, w->power ? "On":"Off");
760 break;
761 default:
762 break;
763 }
764 }
765
Mark Brown0be98982008-05-19 12:31:28 +0200766 switch (codec->bias_level) {
767 case SND_SOC_BIAS_ON:
768 state = "On";
Richard Purdie2b97eab2006-10-06 18:32:18 +0200769 break;
Mark Brown0be98982008-05-19 12:31:28 +0200770 case SND_SOC_BIAS_PREPARE:
771 state = "Prepare";
Richard Purdie2b97eab2006-10-06 18:32:18 +0200772 break;
Mark Brown0be98982008-05-19 12:31:28 +0200773 case SND_SOC_BIAS_STANDBY:
774 state = "Standby";
Richard Purdie2b97eab2006-10-06 18:32:18 +0200775 break;
Mark Brown0be98982008-05-19 12:31:28 +0200776 case SND_SOC_BIAS_OFF:
777 state = "Off";
Richard Purdie2b97eab2006-10-06 18:32:18 +0200778 break;
779 }
780 count += sprintf(buf + count, "PM State: %s\n", state);
781
782 return count;
783}
784
785static DEVICE_ATTR(dapm_widget, 0444, dapm_widget_show, NULL);
786
787int snd_soc_dapm_sys_add(struct device *dev)
788{
789 int ret = 0;
790
791 if (dapm_status)
792 ret = device_create_file(dev, &dev_attr_dapm_widget);
793
794 return ret;
795}
796
797static void snd_soc_dapm_sys_remove(struct device *dev)
798{
799 if (dapm_status)
800 device_remove_file(dev, &dev_attr_dapm_widget);
801}
802
803/* free all dapm widgets and resources */
Adrian Bunkd9c96cf2006-11-28 12:10:09 +0100804static void dapm_free_widgets(struct snd_soc_codec *codec)
Richard Purdie2b97eab2006-10-06 18:32:18 +0200805{
806 struct snd_soc_dapm_widget *w, *next_w;
807 struct snd_soc_dapm_path *p, *next_p;
808
809 list_for_each_entry_safe(w, next_w, &codec->dapm_widgets, list) {
810 list_del(&w->list);
811 kfree(w);
812 }
813
814 list_for_each_entry_safe(p, next_p, &codec->dapm_paths, list) {
815 list_del(&p->list);
816 kfree(p->long_name);
817 kfree(p);
818 }
819}
820
821/**
822 * snd_soc_dapm_sync_endpoints - scan and power dapm paths
823 * @codec: audio codec
824 *
825 * Walks all dapm audio paths and powers widgets according to their
826 * stream or path usage.
827 *
828 * Returns 0 for success.
829 */
830int snd_soc_dapm_sync_endpoints(struct snd_soc_codec *codec)
831{
832 return dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP);
833}
834EXPORT_SYMBOL_GPL(snd_soc_dapm_sync_endpoints);
835
Mark Brown105f1c22008-05-13 14:52:19 +0200836static int snd_soc_dapm_add_route(struct snd_soc_codec *codec,
837 const char *sink, const char *control, const char *source)
Richard Purdie2b97eab2006-10-06 18:32:18 +0200838{
839 struct snd_soc_dapm_path *path;
840 struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w;
841 int ret = 0;
842
843 /* find src and dest widgets */
844 list_for_each_entry(w, &codec->dapm_widgets, list) {
845
846 if (!wsink && !(strcmp(w->name, sink))) {
847 wsink = w;
848 continue;
849 }
850 if (!wsource && !(strcmp(w->name, source))) {
851 wsource = w;
852 }
853 }
854
855 if (wsource == NULL || wsink == NULL)
856 return -ENODEV;
857
858 path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL);
859 if (!path)
860 return -ENOMEM;
861
862 path->source = wsource;
863 path->sink = wsink;
864 INIT_LIST_HEAD(&path->list);
865 INIT_LIST_HEAD(&path->list_source);
866 INIT_LIST_HEAD(&path->list_sink);
867
868 /* check for external widgets */
869 if (wsink->id == snd_soc_dapm_input) {
870 if (wsource->id == snd_soc_dapm_micbias ||
871 wsource->id == snd_soc_dapm_mic ||
Seth Forshee1e392212007-04-16 15:36:42 +0200872 wsink->id == snd_soc_dapm_line ||
873 wsink->id == snd_soc_dapm_output)
Richard Purdie2b97eab2006-10-06 18:32:18 +0200874 wsink->ext = 1;
875 }
876 if (wsource->id == snd_soc_dapm_output) {
877 if (wsink->id == snd_soc_dapm_spk ||
878 wsink->id == snd_soc_dapm_hp ||
Seth Forshee1e392212007-04-16 15:36:42 +0200879 wsink->id == snd_soc_dapm_line ||
880 wsink->id == snd_soc_dapm_input)
Richard Purdie2b97eab2006-10-06 18:32:18 +0200881 wsource->ext = 1;
882 }
883
884 /* connect static paths */
885 if (control == NULL) {
886 list_add(&path->list, &codec->dapm_paths);
887 list_add(&path->list_sink, &wsink->sources);
888 list_add(&path->list_source, &wsource->sinks);
889 path->connect = 1;
890 return 0;
891 }
892
893 /* connect dynamic paths */
894 switch(wsink->id) {
895 case snd_soc_dapm_adc:
896 case snd_soc_dapm_dac:
897 case snd_soc_dapm_pga:
898 case snd_soc_dapm_input:
899 case snd_soc_dapm_output:
900 case snd_soc_dapm_micbias:
901 case snd_soc_dapm_vmid:
902 case snd_soc_dapm_pre:
903 case snd_soc_dapm_post:
904 list_add(&path->list, &codec->dapm_paths);
905 list_add(&path->list_sink, &wsink->sources);
906 list_add(&path->list_source, &wsource->sinks);
907 path->connect = 1;
908 return 0;
909 case snd_soc_dapm_mux:
910 ret = dapm_connect_mux(codec, wsource, wsink, path, control,
911 &wsink->kcontrols[0]);
912 if (ret != 0)
913 goto err;
914 break;
915 case snd_soc_dapm_switch:
916 case snd_soc_dapm_mixer:
917 ret = dapm_connect_mixer(codec, wsource, wsink, path, control);
918 if (ret != 0)
919 goto err;
920 break;
921 case snd_soc_dapm_hp:
922 case snd_soc_dapm_mic:
923 case snd_soc_dapm_line:
924 case snd_soc_dapm_spk:
925 list_add(&path->list, &codec->dapm_paths);
926 list_add(&path->list_sink, &wsink->sources);
927 list_add(&path->list_source, &wsource->sinks);
928 path->connect = 0;
929 return 0;
930 }
931 return 0;
932
933err:
934 printk(KERN_WARNING "asoc: no dapm match for %s --> %s --> %s\n", source,
935 control, sink);
936 kfree(path);
937 return ret;
938}
Mark Brown105f1c22008-05-13 14:52:19 +0200939
940/**
941 * snd_soc_dapm_connect_input - connect dapm widgets
942 * @codec: audio codec
943 * @sink: name of target widget
944 * @control: mixer control name
945 * @source: name of source name
946 *
947 * Connects 2 dapm widgets together via a named audio path. The sink is
948 * the widget receiving the audio signal, whilst the source is the sender
949 * of the audio signal.
950 *
951 * This function has been deprecated in favour of snd_soc_dapm_add_routes().
952 *
953 * Returns 0 for success else error.
954 */
955int snd_soc_dapm_connect_input(struct snd_soc_codec *codec, const char *sink,
956 const char *control, const char *source)
957{
958 return snd_soc_dapm_add_route(codec, sink, control, source);
959}
Richard Purdie2b97eab2006-10-06 18:32:18 +0200960EXPORT_SYMBOL_GPL(snd_soc_dapm_connect_input);
961
962/**
Mark Brown105f1c22008-05-13 14:52:19 +0200963 * snd_soc_dapm_add_routes - Add routes between DAPM widgets
964 * @codec: codec
965 * @route: audio routes
966 * @num: number of routes
967 *
968 * Connects 2 dapm widgets together via a named audio path. The sink is
969 * the widget receiving the audio signal, whilst the source is the sender
970 * of the audio signal.
971 *
972 * Returns 0 for success else error. On error all resources can be freed
973 * with a call to snd_soc_card_free().
974 */
975int snd_soc_dapm_add_routes(struct snd_soc_codec *codec,
976 const struct snd_soc_dapm_route *route, int num)
977{
978 int i, ret;
979
980 for (i = 0; i < num; i++) {
981 ret = snd_soc_dapm_add_route(codec, route->sink,
982 route->control, route->source);
983 if (ret < 0) {
984 printk(KERN_ERR "Failed to add route %s->%s\n",
985 route->source,
986 route->sink);
987 return ret;
988 }
989 route++;
990 }
991
992 return 0;
993}
994EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes);
995
996/**
Richard Purdie2b97eab2006-10-06 18:32:18 +0200997 * snd_soc_dapm_new_widgets - add new dapm widgets
998 * @codec: audio codec
999 *
1000 * Checks the codec for any new dapm widgets and creates them if found.
1001 *
1002 * Returns 0 for success.
1003 */
1004int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec)
1005{
1006 struct snd_soc_dapm_widget *w;
1007
Richard Purdie2b97eab2006-10-06 18:32:18 +02001008 list_for_each_entry(w, &codec->dapm_widgets, list)
1009 {
1010 if (w->new)
1011 continue;
1012
1013 switch(w->id) {
1014 case snd_soc_dapm_switch:
1015 case snd_soc_dapm_mixer:
1016 dapm_new_mixer(codec, w);
1017 break;
1018 case snd_soc_dapm_mux:
1019 dapm_new_mux(codec, w);
1020 break;
1021 case snd_soc_dapm_adc:
1022 case snd_soc_dapm_dac:
1023 case snd_soc_dapm_pga:
1024 dapm_new_pga(codec, w);
1025 break;
1026 case snd_soc_dapm_input:
1027 case snd_soc_dapm_output:
1028 case snd_soc_dapm_micbias:
1029 case snd_soc_dapm_spk:
1030 case snd_soc_dapm_hp:
1031 case snd_soc_dapm_mic:
1032 case snd_soc_dapm_line:
1033 case snd_soc_dapm_vmid:
1034 case snd_soc_dapm_pre:
1035 case snd_soc_dapm_post:
1036 break;
1037 }
1038 w->new = 1;
1039 }
1040
1041 dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP);
Richard Purdie2b97eab2006-10-06 18:32:18 +02001042 return 0;
1043}
1044EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets);
1045
1046/**
1047 * snd_soc_dapm_get_volsw - dapm mixer get callback
1048 * @kcontrol: mixer control
1049 * @uinfo: control element information
1050 *
1051 * Callback to get the value of a dapm mixer control.
1052 *
1053 * Returns 0 for success.
1054 */
1055int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
1056 struct snd_ctl_elem_value *ucontrol)
1057{
1058 struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
1059 int reg = kcontrol->private_value & 0xff;
1060 int shift = (kcontrol->private_value >> 8) & 0x0f;
1061 int rshift = (kcontrol->private_value >> 12) & 0x0f;
Philipp Zabela7a4ac82008-01-10 14:37:42 +01001062 int max = (kcontrol->private_value >> 16) & 0xff;
Richard Purdie2b97eab2006-10-06 18:32:18 +02001063 int invert = (kcontrol->private_value >> 24) & 0x01;
Philipp Zabela7a4ac82008-01-10 14:37:42 +01001064 int mask = (1 << fls(max)) - 1;
Richard Purdie2b97eab2006-10-06 18:32:18 +02001065
1066 /* return the saved value if we are powered down */
1067 if (widget->id == snd_soc_dapm_pga && !widget->power) {
1068 ucontrol->value.integer.value[0] = widget->saved_value;
1069 return 0;
1070 }
1071
1072 ucontrol->value.integer.value[0] =
1073 (snd_soc_read(widget->codec, reg) >> shift) & mask;
1074 if (shift != rshift)
1075 ucontrol->value.integer.value[1] =
1076 (snd_soc_read(widget->codec, reg) >> rshift) & mask;
1077 if (invert) {
1078 ucontrol->value.integer.value[0] =
Philipp Zabela7a4ac82008-01-10 14:37:42 +01001079 max - ucontrol->value.integer.value[0];
Richard Purdie2b97eab2006-10-06 18:32:18 +02001080 if (shift != rshift)
1081 ucontrol->value.integer.value[1] =
Philipp Zabela7a4ac82008-01-10 14:37:42 +01001082 max - ucontrol->value.integer.value[1];
Richard Purdie2b97eab2006-10-06 18:32:18 +02001083 }
1084
1085 return 0;
1086}
1087EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw);
1088
1089/**
1090 * snd_soc_dapm_put_volsw - dapm mixer set callback
1091 * @kcontrol: mixer control
1092 * @uinfo: control element information
1093 *
1094 * Callback to set the value of a dapm mixer control.
1095 *
1096 * Returns 0 for success.
1097 */
1098int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
1099 struct snd_ctl_elem_value *ucontrol)
1100{
1101 struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
1102 int reg = kcontrol->private_value & 0xff;
1103 int shift = (kcontrol->private_value >> 8) & 0x0f;
1104 int rshift = (kcontrol->private_value >> 12) & 0x0f;
Philipp Zabela7a4ac82008-01-10 14:37:42 +01001105 int max = (kcontrol->private_value >> 16) & 0xff;
1106 int mask = (1 << fls(max)) - 1;
Richard Purdie2b97eab2006-10-06 18:32:18 +02001107 int invert = (kcontrol->private_value >> 24) & 0x01;
1108 unsigned short val, val2, val_mask;
1109 int ret;
1110
1111 val = (ucontrol->value.integer.value[0] & mask);
1112
1113 if (invert)
Philipp Zabela7a4ac82008-01-10 14:37:42 +01001114 val = max - val;
Richard Purdie2b97eab2006-10-06 18:32:18 +02001115 val_mask = mask << shift;
1116 val = val << shift;
1117 if (shift != rshift) {
1118 val2 = (ucontrol->value.integer.value[1] & mask);
1119 if (invert)
Philipp Zabela7a4ac82008-01-10 14:37:42 +01001120 val2 = max - val2;
Richard Purdie2b97eab2006-10-06 18:32:18 +02001121 val_mask |= mask << rshift;
1122 val |= val2 << rshift;
1123 }
1124
1125 mutex_lock(&widget->codec->mutex);
1126 widget->value = val;
1127
1128 /* save volume value if the widget is powered down */
1129 if (widget->id == snd_soc_dapm_pga && !widget->power) {
1130 widget->saved_value = val;
1131 mutex_unlock(&widget->codec->mutex);
1132 return 1;
1133 }
1134
1135 dapm_mixer_update_power(widget, kcontrol, reg, val_mask, val, invert);
1136 if (widget->event) {
1137 if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
Laim Girdwood9af6d952008-01-10 14:41:02 +01001138 ret = widget->event(widget, kcontrol,
1139 SND_SOC_DAPM_PRE_REG);
1140 if (ret < 0) {
1141 ret = 1;
Richard Purdie2b97eab2006-10-06 18:32:18 +02001142 goto out;
Laim Girdwood9af6d952008-01-10 14:41:02 +01001143 }
Richard Purdie2b97eab2006-10-06 18:32:18 +02001144 }
1145 ret = snd_soc_update_bits(widget->codec, reg, val_mask, val);
1146 if (widget->event_flags & SND_SOC_DAPM_POST_REG)
Laim Girdwood9af6d952008-01-10 14:41:02 +01001147 ret = widget->event(widget, kcontrol,
1148 SND_SOC_DAPM_POST_REG);
Richard Purdie2b97eab2006-10-06 18:32:18 +02001149 } else
1150 ret = snd_soc_update_bits(widget->codec, reg, val_mask, val);
1151
1152out:
1153 mutex_unlock(&widget->codec->mutex);
1154 return ret;
1155}
1156EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw);
1157
1158/**
1159 * snd_soc_dapm_get_enum_double - dapm enumerated double mixer get callback
1160 * @kcontrol: mixer control
1161 * @uinfo: control element information
1162 *
1163 * Callback to get the value of a dapm enumerated double mixer control.
1164 *
1165 * Returns 0 for success.
1166 */
1167int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
1168 struct snd_ctl_elem_value *ucontrol)
1169{
1170 struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
1171 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
1172 unsigned short val, bitmask;
1173
1174 for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
1175 ;
1176 val = snd_soc_read(widget->codec, e->reg);
1177 ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1);
1178 if (e->shift_l != e->shift_r)
1179 ucontrol->value.enumerated.item[1] =
1180 (val >> e->shift_r) & (bitmask - 1);
1181
1182 return 0;
1183}
1184EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double);
1185
1186/**
1187 * snd_soc_dapm_put_enum_double - dapm enumerated double mixer set callback
1188 * @kcontrol: mixer control
1189 * @uinfo: control element information
1190 *
1191 * Callback to set the value of a dapm enumerated double mixer control.
1192 *
1193 * Returns 0 for success.
1194 */
1195int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
1196 struct snd_ctl_elem_value *ucontrol)
1197{
1198 struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
1199 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
1200 unsigned short val, mux;
1201 unsigned short mask, bitmask;
1202 int ret = 0;
1203
1204 for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
1205 ;
1206 if (ucontrol->value.enumerated.item[0] > e->mask - 1)
1207 return -EINVAL;
1208 mux = ucontrol->value.enumerated.item[0];
1209 val = mux << e->shift_l;
1210 mask = (bitmask - 1) << e->shift_l;
1211 if (e->shift_l != e->shift_r) {
1212 if (ucontrol->value.enumerated.item[1] > e->mask - 1)
1213 return -EINVAL;
1214 val |= ucontrol->value.enumerated.item[1] << e->shift_r;
1215 mask |= (bitmask - 1) << e->shift_r;
1216 }
1217
1218 mutex_lock(&widget->codec->mutex);
1219 widget->value = val;
1220 dapm_mux_update_power(widget, kcontrol, mask, mux, e);
1221 if (widget->event) {
1222 if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
Laim Girdwood9af6d952008-01-10 14:41:02 +01001223 ret = widget->event(widget,
1224 kcontrol, SND_SOC_DAPM_PRE_REG);
Richard Purdie2b97eab2006-10-06 18:32:18 +02001225 if (ret < 0)
1226 goto out;
1227 }
1228 ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
1229 if (widget->event_flags & SND_SOC_DAPM_POST_REG)
Laim Girdwood9af6d952008-01-10 14:41:02 +01001230 ret = widget->event(widget,
1231 kcontrol, SND_SOC_DAPM_POST_REG);
Richard Purdie2b97eab2006-10-06 18:32:18 +02001232 } else
1233 ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
1234
1235out:
1236 mutex_unlock(&widget->codec->mutex);
1237 return ret;
1238}
1239EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);
1240
1241/**
1242 * snd_soc_dapm_new_control - create new dapm control
1243 * @codec: audio codec
1244 * @widget: widget template
1245 *
1246 * Creates a new dapm control based upon the template.
1247 *
1248 * Returns 0 for success else error.
1249 */
1250int snd_soc_dapm_new_control(struct snd_soc_codec *codec,
1251 const struct snd_soc_dapm_widget *widget)
1252{
1253 struct snd_soc_dapm_widget *w;
1254
1255 if ((w = dapm_cnew_widget(widget)) == NULL)
1256 return -ENOMEM;
1257
1258 w->codec = codec;
1259 INIT_LIST_HEAD(&w->sources);
1260 INIT_LIST_HEAD(&w->sinks);
1261 INIT_LIST_HEAD(&w->list);
1262 list_add(&w->list, &codec->dapm_widgets);
1263
1264 /* machine layer set ups unconnected pins and insertions */
1265 w->connected = 1;
1266 return 0;
1267}
1268EXPORT_SYMBOL_GPL(snd_soc_dapm_new_control);
1269
1270/**
Mark Brown4ba13272008-05-13 14:51:19 +02001271 * snd_soc_dapm_new_controls - create new dapm controls
1272 * @codec: audio codec
1273 * @widget: widget array
1274 * @num: number of widgets
1275 *
1276 * Creates new DAPM controls based upon the templates.
1277 *
1278 * Returns 0 for success else error.
1279 */
1280int snd_soc_dapm_new_controls(struct snd_soc_codec *codec,
1281 const struct snd_soc_dapm_widget *widget,
1282 int num)
1283{
1284 int i, ret;
1285
1286 for (i = 0; i < num; i++) {
1287 ret = snd_soc_dapm_new_control(codec, widget);
1288 if (ret < 0)
1289 return ret;
1290 widget++;
1291 }
1292 return 0;
1293}
1294EXPORT_SYMBOL_GPL(snd_soc_dapm_new_controls);
1295
1296
1297/**
Richard Purdie2b97eab2006-10-06 18:32:18 +02001298 * snd_soc_dapm_stream_event - send a stream event to the dapm core
1299 * @codec: audio codec
1300 * @stream: stream name
1301 * @event: stream event
1302 *
1303 * Sends a stream event to the dapm core. The core then makes any
1304 * necessary widget power changes.
1305 *
1306 * Returns 0 for success else error.
1307 */
1308int snd_soc_dapm_stream_event(struct snd_soc_codec *codec,
1309 char *stream, int event)
1310{
1311 struct snd_soc_dapm_widget *w;
1312
Seth Forshee11da21a2007-02-02 17:14:19 +01001313 if (stream == NULL)
1314 return 0;
1315
Richard Purdie2b97eab2006-10-06 18:32:18 +02001316 mutex_lock(&codec->mutex);
1317 list_for_each_entry(w, &codec->dapm_widgets, list)
1318 {
1319 if (!w->sname)
1320 continue;
1321 dbg("widget %s\n %s stream %s event %d\n", w->name, w->sname,
1322 stream, event);
1323 if (strstr(w->sname, stream)) {
1324 switch(event) {
1325 case SND_SOC_DAPM_STREAM_START:
1326 w->active = 1;
1327 break;
1328 case SND_SOC_DAPM_STREAM_STOP:
1329 w->active = 0;
1330 break;
1331 case SND_SOC_DAPM_STREAM_SUSPEND:
1332 if (w->active)
1333 w->suspend = 1;
1334 w->active = 0;
1335 break;
1336 case SND_SOC_DAPM_STREAM_RESUME:
1337 if (w->suspend) {
1338 w->active = 1;
1339 w->suspend = 0;
1340 }
1341 break;
1342 case SND_SOC_DAPM_STREAM_PAUSE_PUSH:
1343 break;
1344 case SND_SOC_DAPM_STREAM_PAUSE_RELEASE:
1345 break;
1346 }
1347 }
1348 }
1349 mutex_unlock(&codec->mutex);
1350
1351 dapm_power_widgets(codec, event);
Harvey Harrison9bf8e7d2008-03-03 15:32:18 -08001352 dump_dapm(codec, __func__);
Richard Purdie2b97eab2006-10-06 18:32:18 +02001353 return 0;
1354}
1355EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event);
1356
1357/**
Mark Brown0be98982008-05-19 12:31:28 +02001358 * snd_soc_dapm_set_bias_level - set the bias level for the system
Liam Girdwood0b4d2212008-01-10 14:36:20 +01001359 * @socdev: audio device
Mark Brown0be98982008-05-19 12:31:28 +02001360 * @level: level to configure
Liam Girdwood0b4d2212008-01-10 14:36:20 +01001361 *
Mark Brown0be98982008-05-19 12:31:28 +02001362 * Configure the bias (power) levels for the SoC audio device.
Liam Girdwood0b4d2212008-01-10 14:36:20 +01001363 *
1364 * Returns 0 for success else error.
1365 */
Mark Brown0be98982008-05-19 12:31:28 +02001366int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev,
1367 enum snd_soc_bias_level level)
Liam Girdwood0b4d2212008-01-10 14:36:20 +01001368{
1369 struct snd_soc_codec *codec = socdev->codec;
1370 struct snd_soc_machine *machine = socdev->machine;
Mark Brown0be98982008-05-19 12:31:28 +02001371 int ret = 0;
Liam Girdwood0b4d2212008-01-10 14:36:20 +01001372
Mark Brown0be98982008-05-19 12:31:28 +02001373 if (machine->set_bias_level)
1374 ret = machine->set_bias_level(machine, level);
1375 if (ret == 0 && codec->set_bias_level)
1376 ret = codec->set_bias_level(codec, level);
1377
1378 return ret;
Liam Girdwood0b4d2212008-01-10 14:36:20 +01001379}
Liam Girdwood0b4d2212008-01-10 14:36:20 +01001380
1381/**
Richard Purdie2b97eab2006-10-06 18:32:18 +02001382 * snd_soc_dapm_set_endpoint - set audio endpoint status
1383 * @codec: audio codec
1384 * @endpoint: audio signal endpoint (or start point)
1385 * @status: point status
1386 *
1387 * Set audio endpoint status - connected or disconnected.
1388 *
1389 * Returns 0 for success else error.
1390 */
1391int snd_soc_dapm_set_endpoint(struct snd_soc_codec *codec,
1392 char *endpoint, int status)
1393{
1394 struct snd_soc_dapm_widget *w;
1395
1396 list_for_each_entry(w, &codec->dapm_widgets, list) {
1397 if (!strcmp(w->name, endpoint)) {
1398 w->connected = status;
Mark Brown964a788e2008-02-26 13:16:08 +01001399 return 0;
Richard Purdie2b97eab2006-10-06 18:32:18 +02001400 }
1401 }
1402
Mark Brown964a788e2008-02-26 13:16:08 +01001403 return -ENODEV;
Richard Purdie2b97eab2006-10-06 18:32:18 +02001404}
1405EXPORT_SYMBOL_GPL(snd_soc_dapm_set_endpoint);
1406
1407/**
Graeme Gregoryeeec12b2008-04-30 19:27:40 +02001408 * snd_soc_dapm_get_endpoint_status - get audio endpoint status
1409 * @codec: audio codec
1410 * @endpoint: audio signal endpoint (or start point)
1411 *
1412 * Get audio endpoint status - connected or disconnected.
1413 *
1414 * Returns status
1415 */
1416int snd_soc_dapm_get_endpoint_status(struct snd_soc_codec *codec,
1417 char *endpoint)
1418{
1419 struct snd_soc_dapm_widget *w;
1420
1421 list_for_each_entry(w, &codec->dapm_widgets, list) {
1422 if (!strcmp(w->name, endpoint))
1423 return w->connected;
1424 }
1425
1426 return 0;
1427}
Stephen Rothwell650f6b12008-05-01 10:49:18 +02001428EXPORT_SYMBOL_GPL(snd_soc_dapm_get_endpoint_status);
Graeme Gregoryeeec12b2008-04-30 19:27:40 +02001429
1430/**
Richard Purdie2b97eab2006-10-06 18:32:18 +02001431 * snd_soc_dapm_free - free dapm resources
1432 * @socdev: SoC device
1433 *
1434 * Free all dapm widgets and resources.
1435 */
1436void snd_soc_dapm_free(struct snd_soc_device *socdev)
1437{
1438 struct snd_soc_codec *codec = socdev->codec;
1439
1440 snd_soc_dapm_sys_remove(socdev->dev);
1441 dapm_free_widgets(codec);
1442}
1443EXPORT_SYMBOL_GPL(snd_soc_dapm_free);
1444
1445/* Module information */
1446MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
1447MODULE_DESCRIPTION("Dynamic Audio Power Management core for ALSA SoC");
1448MODULE_LICENSE("GPL");