]> err.no Git - linux-2.6/blob
2536382
[linux-2.6] /
1 /*
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  *
13  *  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
22  *    o Delayed powerdown of audio susbsystem to reduce pops between a quick
23  *      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>
41 #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 */
60 #define pop_wait(time) schedule_timeout_uninterruptible(msecs_to_jiffies(time))
61 #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 */
68 static 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 };
73 static 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
79 static int dapm_status = 1;
80 module_param(dapm_status, int, 0);
81 MODULE_PARM_DESC(dapm_status, "enable DPM sysfs entries");
82
83 /* create a new dapm widget */
84 static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
85         const struct snd_soc_dapm_widget *_widget)
86 {
87         return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL);
88 }
89
90 /* set up initial codec paths */
91 static 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 */
151 static 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 */
174 static 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 */
195 static 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         }
224         dbg("reg %x old %x new %x change %d\n", widget->reg, old, new, change);
225         return change;
226 }
227
228 /* ramps the volume up or down to minimise pops before or after a
229  * DAPM power event */
230 static 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 */
274 static 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 */
313 static 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
335 err:
336         printk(KERN_ERR "asoc: failed to add kcontrol %s\n", w->name);
337         return ret;
338 }
339
340 /* create new dapm volume control */
341 static 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 */
361 static 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  */
373 static 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  */
409 static 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  * Handler for generic register modifier widget.
447  */
448 int dapm_reg_event(struct snd_soc_dapm_widget *w,
449                    struct snd_kcontrol *kcontrol, int event)
450 {
451         unsigned int val;
452
453         if (SND_SOC_DAPM_EVENT_ON(event))
454                 val = w->on_val;
455         else
456                 val = w->off_val;
457
458         snd_soc_update_bits(w->codec, -(w->reg + 1),
459                             w->mask << w->shift, val << w->shift);
460
461         return 0;
462 }
463
464 /*
465  * Scan each dapm widget for complete audio path.
466  * A complete path is a route that has valid endpoints i.e.:-
467  *
468  *  o DAC to output pin.
469  *  o Input Pin to ADC.
470  *  o Input pin to Output pin (bypass, sidetone)
471  *  o DAC to ADC (loopback).
472  */
473 static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
474 {
475         struct snd_soc_dapm_widget *w;
476         int in, out, i, c = 1, *seq = NULL, ret = 0, power_change, power;
477
478         /* do we have a sequenced stream event */
479         if (event == SND_SOC_DAPM_STREAM_START) {
480                 c = ARRAY_SIZE(dapm_up_seq);
481                 seq = dapm_up_seq;
482         } else if (event == SND_SOC_DAPM_STREAM_STOP) {
483                 c = ARRAY_SIZE(dapm_down_seq);
484                 seq = dapm_down_seq;
485         }
486
487         for(i = 0; i < c; i++) {
488                 list_for_each_entry(w, &codec->dapm_widgets, list) {
489
490                         /* is widget in stream order */
491                         if (seq && seq[i] && w->id != seq[i])
492                                 continue;
493
494                         /* vmid - no action */
495                         if (w->id == snd_soc_dapm_vmid)
496                                 continue;
497
498                         /* active ADC */
499                         if (w->id == snd_soc_dapm_adc && w->active) {
500                                 in = is_connected_input_ep(w);
501                                 dapm_clear_walk(w->codec);
502                                 w->power = (in != 0) ? 1 : 0;
503                                 dapm_update_bits(w);
504                                 continue;
505                         }
506
507                         /* active DAC */
508                         if (w->id == snd_soc_dapm_dac && w->active) {
509                                 out = is_connected_output_ep(w);
510                                 dapm_clear_walk(w->codec);
511                                 w->power = (out != 0) ? 1 : 0;
512                                 dapm_update_bits(w);
513                                 continue;
514                         }
515
516                         /* programmable gain/attenuation */
517                         if (w->id == snd_soc_dapm_pga) {
518                                 int on;
519                                 in = is_connected_input_ep(w);
520                                 dapm_clear_walk(w->codec);
521                                 out = is_connected_output_ep(w);
522                                 dapm_clear_walk(w->codec);
523                                 w->power = on = (out != 0 && in != 0) ? 1 : 0;
524
525                                 if (!on)
526                                         dapm_set_pga(w, on); /* lower volume to reduce pops */
527                                 dapm_update_bits(w);
528                                 if (on)
529                                         dapm_set_pga(w, on); /* restore volume from zero */
530
531                                 continue;
532                         }
533
534                         /* pre and post event widgets */
535                         if (w->id == snd_soc_dapm_pre) {
536                                 if (!w->event)
537                                         continue;
538
539                                 if (event == SND_SOC_DAPM_STREAM_START) {
540                                         ret = w->event(w,
541                                                 NULL, SND_SOC_DAPM_PRE_PMU);
542                                         if (ret < 0)
543                                                 return ret;
544                                 } else if (event == SND_SOC_DAPM_STREAM_STOP) {
545                                         ret = w->event(w,
546                                                 NULL, SND_SOC_DAPM_PRE_PMD);
547                                         if (ret < 0)
548                                                 return ret;
549                                 }
550                                 continue;
551                         }
552                         if (w->id == snd_soc_dapm_post) {
553                                 if (!w->event)
554                                         continue;
555
556                                 if (event == SND_SOC_DAPM_STREAM_START) {
557                                         ret = w->event(w,
558                                                 NULL, SND_SOC_DAPM_POST_PMU);
559                                         if (ret < 0)
560                                                 return ret;
561                                 } else if (event == SND_SOC_DAPM_STREAM_STOP) {
562                                         ret = w->event(w,
563                                                 NULL, SND_SOC_DAPM_POST_PMD);
564                                         if (ret < 0)
565                                                 return ret;
566                                 }
567                                 continue;
568                         }
569
570                         /* all other widgets */
571                         in = is_connected_input_ep(w);
572                         dapm_clear_walk(w->codec);
573                         out = is_connected_output_ep(w);
574                         dapm_clear_walk(w->codec);
575                         power = (out != 0 && in != 0) ? 1 : 0;
576                         power_change = (w->power == power) ? 0: 1;
577                         w->power = power;
578
579                         /* call any power change event handlers */
580                         if (power_change) {
581                                 if (w->event) {
582                                         dbg("power %s event for %s flags %x\n",
583                                                 w->power ? "on" : "off", w->name, w->event_flags);
584                                         if (power) {
585                                                 /* power up event */
586                                                 if (w->event_flags & SND_SOC_DAPM_PRE_PMU) {
587                                                         ret = w->event(w,
588                                                                 NULL, SND_SOC_DAPM_PRE_PMU);
589                                                         if (ret < 0)
590                                                                 return ret;
591                                                 }
592                                                 dapm_update_bits(w);
593                                                 if (w->event_flags & SND_SOC_DAPM_POST_PMU){
594                                                         ret = w->event(w,
595                                                                 NULL, SND_SOC_DAPM_POST_PMU);
596                                                         if (ret < 0)
597                                                                 return ret;
598                                                 }
599                                         } else {
600                                                 /* power down event */
601                                                 if (w->event_flags & SND_SOC_DAPM_PRE_PMD) {
602                                                         ret = w->event(w,
603                                                                 NULL, SND_SOC_DAPM_PRE_PMD);
604                                                         if (ret < 0)
605                                                                 return ret;
606                                                 }
607                                                 dapm_update_bits(w);
608                                                 if (w->event_flags & SND_SOC_DAPM_POST_PMD) {
609                                                         ret = w->event(w,
610                                                                 NULL, SND_SOC_DAPM_POST_PMD);
611                                                         if (ret < 0)
612                                                                 return ret;
613                                                 }
614                                         }
615                                 } else
616                                         /* no event handler */
617                                         dapm_update_bits(w);
618                         }
619                 }
620         }
621
622         return ret;
623 }
624
625 #if DAPM_DEBUG
626 static void dbg_dump_dapm(struct snd_soc_codec* codec, const char *action)
627 {
628         struct snd_soc_dapm_widget *w;
629         struct snd_soc_dapm_path *p = NULL;
630         int in, out;
631
632         printk("DAPM %s %s\n", codec->name, action);
633
634         list_for_each_entry(w, &codec->dapm_widgets, list) {
635
636                 /* only display widgets that effect routing */
637                 switch (w->id) {
638                 case snd_soc_dapm_pre:
639                 case snd_soc_dapm_post:
640                 case snd_soc_dapm_vmid:
641                         continue;
642                 case snd_soc_dapm_mux:
643                 case snd_soc_dapm_output:
644                 case snd_soc_dapm_input:
645                 case snd_soc_dapm_switch:
646                 case snd_soc_dapm_hp:
647                 case snd_soc_dapm_mic:
648                 case snd_soc_dapm_spk:
649                 case snd_soc_dapm_line:
650                 case snd_soc_dapm_micbias:
651                 case snd_soc_dapm_dac:
652                 case snd_soc_dapm_adc:
653                 case snd_soc_dapm_pga:
654                 case snd_soc_dapm_mixer:
655                         if (w->name) {
656                                 in = is_connected_input_ep(w);
657                                 dapm_clear_walk(w->codec);
658                                 out = is_connected_output_ep(w);
659                                 dapm_clear_walk(w->codec);
660                                 printk("%s: %s  in %d out %d\n", w->name,
661                                         w->power ? "On":"Off",in, out);
662
663                                 list_for_each_entry(p, &w->sources, list_sink) {
664                                         if (p->connect)
665                                                 printk(" in  %s %s\n", p->name ? p->name : "static",
666                                                         p->source->name);
667                                 }
668                                 list_for_each_entry(p, &w->sinks, list_source) {
669                                         if (p->connect)
670                                                 printk(" out %s %s\n", p->name ? p->name : "static",
671                                                         p->sink->name);
672                                 }
673                         }
674                 break;
675                 }
676         }
677 }
678 #endif
679
680 /* test and update the power status of a mux widget */
681 static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
682                                  struct snd_kcontrol *kcontrol, int mask,
683                                  int val, struct soc_enum* e)
684 {
685         struct snd_soc_dapm_path *path;
686         int found = 0;
687
688         if (widget->id != snd_soc_dapm_mux)
689                 return -ENODEV;
690
691         if (!snd_soc_test_bits(widget->codec, e->reg, mask, val))
692                 return 0;
693
694         /* find dapm widget path assoc with kcontrol */
695         list_for_each_entry(path, &widget->codec->dapm_paths, list) {
696                 if (path->kcontrol != kcontrol)
697                         continue;
698
699                 if (!path->name || ! e->texts[val])
700                         continue;
701
702                 found = 1;
703                 /* we now need to match the string in the enum to the path */
704                 if (!(strcmp(path->name, e->texts[val])))
705                         path->connect = 1; /* new connection */
706                 else
707                         path->connect = 0; /* old connection must be powered down */
708         }
709
710         if (found)
711                 dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP);
712
713         return 0;
714 }
715
716 /* test and update the power status of a mixer or switch widget */
717 static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
718                                    struct snd_kcontrol *kcontrol, int reg,
719                                    int val_mask, int val, int invert)
720 {
721         struct snd_soc_dapm_path *path;
722         int found = 0;
723
724         if (widget->id != snd_soc_dapm_mixer &&
725             widget->id != snd_soc_dapm_switch)
726                 return -ENODEV;
727
728         if (!snd_soc_test_bits(widget->codec, reg, val_mask, val))
729                 return 0;
730
731         /* find dapm widget path assoc with kcontrol */
732         list_for_each_entry(path, &widget->codec->dapm_paths, list) {
733                 if (path->kcontrol != kcontrol)
734                         continue;
735
736                 /* found, now check type */
737                 found = 1;
738                 if (val)
739                         /* new connection */
740                         path->connect = invert ? 0:1;
741                 else
742                         /* old connection must be powered down */
743                         path->connect = invert ? 1:0;
744                 break;
745         }
746
747         if (found)
748                 dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP);
749
750         return 0;
751 }
752
753 /* show dapm widget status in sys fs */
754 static ssize_t dapm_widget_show(struct device *dev,
755         struct device_attribute *attr, char *buf)
756 {
757         struct snd_soc_device *devdata = dev_get_drvdata(dev);
758         struct snd_soc_codec *codec = devdata->codec;
759         struct snd_soc_dapm_widget *w;
760         int count = 0;
761         char *state = "not set";
762
763         list_for_each_entry(w, &codec->dapm_widgets, list) {
764
765                 /* only display widgets that burnm power */
766                 switch (w->id) {
767                 case snd_soc_dapm_hp:
768                 case snd_soc_dapm_mic:
769                 case snd_soc_dapm_spk:
770                 case snd_soc_dapm_line:
771                 case snd_soc_dapm_micbias:
772                 case snd_soc_dapm_dac:
773                 case snd_soc_dapm_adc:
774                 case snd_soc_dapm_pga:
775                 case snd_soc_dapm_mixer:
776                         if (w->name)
777                                 count += sprintf(buf + count, "%s: %s\n",
778                                         w->name, w->power ? "On":"Off");
779                 break;
780                 default:
781                 break;
782                 }
783         }
784
785         switch (codec->bias_level) {
786         case SND_SOC_BIAS_ON:
787                 state = "On";
788                 break;
789         case SND_SOC_BIAS_PREPARE:
790                 state = "Prepare";
791                 break;
792         case SND_SOC_BIAS_STANDBY:
793                 state = "Standby";
794                 break;
795         case SND_SOC_BIAS_OFF:
796                 state = "Off";
797                 break;
798         }
799         count += sprintf(buf + count, "PM State: %s\n", state);
800
801         return count;
802 }
803
804 static DEVICE_ATTR(dapm_widget, 0444, dapm_widget_show, NULL);
805
806 int snd_soc_dapm_sys_add(struct device *dev)
807 {
808         int ret = 0;
809
810         if (dapm_status)
811                 ret = device_create_file(dev, &dev_attr_dapm_widget);
812
813         return ret;
814 }
815
816 static void snd_soc_dapm_sys_remove(struct device *dev)
817 {
818         if (dapm_status)
819                 device_remove_file(dev, &dev_attr_dapm_widget);
820 }
821
822 /* free all dapm widgets and resources */
823 static void dapm_free_widgets(struct snd_soc_codec *codec)
824 {
825         struct snd_soc_dapm_widget *w, *next_w;
826         struct snd_soc_dapm_path *p, *next_p;
827
828         list_for_each_entry_safe(w, next_w, &codec->dapm_widgets, list) {
829                 list_del(&w->list);
830                 kfree(w);
831         }
832
833         list_for_each_entry_safe(p, next_p, &codec->dapm_paths, list) {
834                 list_del(&p->list);
835                 kfree(p->long_name);
836                 kfree(p);
837         }
838 }
839
840 /**
841  * snd_soc_dapm_sync_endpoints - scan and power dapm paths
842  * @codec: audio codec
843  *
844  * Walks all dapm audio paths and powers widgets according to their
845  * stream or path usage.
846  *
847  * Returns 0 for success.
848  */
849 int snd_soc_dapm_sync_endpoints(struct snd_soc_codec *codec)
850 {
851         return dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP);
852 }
853 EXPORT_SYMBOL_GPL(snd_soc_dapm_sync_endpoints);
854
855 static int snd_soc_dapm_add_route(struct snd_soc_codec *codec,
856         const char *sink, const char *control, const char *source)
857 {
858         struct snd_soc_dapm_path *path;
859         struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w;
860         int ret = 0;
861
862         /* find src and dest widgets */
863         list_for_each_entry(w, &codec->dapm_widgets, list) {
864
865                 if (!wsink && !(strcmp(w->name, sink))) {
866                         wsink = w;
867                         continue;
868                 }
869                 if (!wsource && !(strcmp(w->name, source))) {
870                         wsource = w;
871                 }
872         }
873
874         if (wsource == NULL || wsink == NULL)
875                 return -ENODEV;
876
877         path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL);
878         if (!path)
879                 return -ENOMEM;
880
881         path->source = wsource;
882         path->sink = wsink;
883         INIT_LIST_HEAD(&path->list);
884         INIT_LIST_HEAD(&path->list_source);
885         INIT_LIST_HEAD(&path->list_sink);
886
887         /* check for external widgets */
888         if (wsink->id == snd_soc_dapm_input) {
889                 if (wsource->id == snd_soc_dapm_micbias ||
890                         wsource->id == snd_soc_dapm_mic ||
891                         wsink->id == snd_soc_dapm_line ||
892                         wsink->id == snd_soc_dapm_output)
893                         wsink->ext = 1;
894         }
895         if (wsource->id == snd_soc_dapm_output) {
896                 if (wsink->id == snd_soc_dapm_spk ||
897                         wsink->id == snd_soc_dapm_hp ||
898                         wsink->id == snd_soc_dapm_line ||
899                         wsink->id == snd_soc_dapm_input)
900                         wsource->ext = 1;
901         }
902
903         /* connect static paths */
904         if (control == NULL) {
905                 list_add(&path->list, &codec->dapm_paths);
906                 list_add(&path->list_sink, &wsink->sources);
907                 list_add(&path->list_source, &wsource->sinks);
908                 path->connect = 1;
909                 return 0;
910         }
911
912         /* connect dynamic paths */
913         switch(wsink->id) {
914         case snd_soc_dapm_adc:
915         case snd_soc_dapm_dac:
916         case snd_soc_dapm_pga:
917         case snd_soc_dapm_input:
918         case snd_soc_dapm_output:
919         case snd_soc_dapm_micbias:
920         case snd_soc_dapm_vmid:
921         case snd_soc_dapm_pre:
922         case snd_soc_dapm_post:
923                 list_add(&path->list, &codec->dapm_paths);
924                 list_add(&path->list_sink, &wsink->sources);
925                 list_add(&path->list_source, &wsource->sinks);
926                 path->connect = 1;
927                 return 0;
928         case snd_soc_dapm_mux:
929                 ret = dapm_connect_mux(codec, wsource, wsink, path, control,
930                         &wsink->kcontrols[0]);
931                 if (ret != 0)
932                         goto err;
933                 break;
934         case snd_soc_dapm_switch:
935         case snd_soc_dapm_mixer:
936                 ret = dapm_connect_mixer(codec, wsource, wsink, path, control);
937                 if (ret != 0)
938                         goto err;
939                 break;
940         case snd_soc_dapm_hp:
941         case snd_soc_dapm_mic:
942         case snd_soc_dapm_line:
943         case snd_soc_dapm_spk:
944                 list_add(&path->list, &codec->dapm_paths);
945                 list_add(&path->list_sink, &wsink->sources);
946                 list_add(&path->list_source, &wsource->sinks);
947                 path->connect = 0;
948                 return 0;
949         }
950         return 0;
951
952 err:
953         printk(KERN_WARNING "asoc: no dapm match for %s --> %s --> %s\n", source,
954                 control, sink);
955         kfree(path);
956         return ret;
957 }
958
959 /**
960  * snd_soc_dapm_connect_input - connect dapm widgets
961  * @codec: audio codec
962  * @sink: name of target widget
963  * @control: mixer control name
964  * @source: name of source name
965  *
966  * Connects 2 dapm widgets together via a named audio path. The sink is
967  * the widget receiving the audio signal, whilst the source is the sender
968  * of the audio signal.
969  *
970  * This function has been deprecated in favour of snd_soc_dapm_add_routes().
971  *
972  * Returns 0 for success else error.
973  */
974 int snd_soc_dapm_connect_input(struct snd_soc_codec *codec, const char *sink,
975         const char *control, const char *source)
976 {
977         return snd_soc_dapm_add_route(codec, sink, control, source);
978 }
979 EXPORT_SYMBOL_GPL(snd_soc_dapm_connect_input);
980
981 /**
982  * snd_soc_dapm_add_routes - Add routes between DAPM widgets
983  * @codec: codec
984  * @route: audio routes
985  * @num: number of routes
986  *
987  * Connects 2 dapm widgets together via a named audio path. The sink is
988  * the widget receiving the audio signal, whilst the source is the sender
989  * of the audio signal.
990  *
991  * Returns 0 for success else error. On error all resources can be freed
992  * with a call to snd_soc_card_free().
993  */
994 int snd_soc_dapm_add_routes(struct snd_soc_codec *codec,
995                             const struct snd_soc_dapm_route *route, int num)
996 {
997         int i, ret;
998
999         for (i = 0; i < num; i++) {
1000                 ret = snd_soc_dapm_add_route(codec, route->sink,
1001                                              route->control, route->source);
1002                 if (ret < 0) {
1003                         printk(KERN_ERR "Failed to add route %s->%s\n",
1004                                route->source,
1005                                route->sink);
1006                         return ret;
1007                 }
1008                 route++;
1009         }
1010
1011         return 0;
1012 }
1013 EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes);
1014
1015 /**
1016  * snd_soc_dapm_new_widgets - add new dapm widgets
1017  * @codec: audio codec
1018  *
1019  * Checks the codec for any new dapm widgets and creates them if found.
1020  *
1021  * Returns 0 for success.
1022  */
1023 int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec)
1024 {
1025         struct snd_soc_dapm_widget *w;
1026
1027         list_for_each_entry(w, &codec->dapm_widgets, list)
1028         {
1029                 if (w->new)
1030                         continue;
1031
1032                 switch(w->id) {
1033                 case snd_soc_dapm_switch:
1034                 case snd_soc_dapm_mixer:
1035                         dapm_new_mixer(codec, w);
1036                         break;
1037                 case snd_soc_dapm_mux:
1038                         dapm_new_mux(codec, w);
1039                         break;
1040                 case snd_soc_dapm_adc:
1041                 case snd_soc_dapm_dac:
1042                 case snd_soc_dapm_pga:
1043                         dapm_new_pga(codec, w);
1044                         break;
1045                 case snd_soc_dapm_input:
1046                 case snd_soc_dapm_output:
1047                 case snd_soc_dapm_micbias:
1048                 case snd_soc_dapm_spk:
1049                 case snd_soc_dapm_hp:
1050                 case snd_soc_dapm_mic:
1051                 case snd_soc_dapm_line:
1052                 case snd_soc_dapm_vmid:
1053                 case snd_soc_dapm_pre:
1054                 case snd_soc_dapm_post:
1055                         break;
1056                 }
1057                 w->new = 1;
1058         }
1059
1060         dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP);
1061         return 0;
1062 }
1063 EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets);
1064
1065 /**
1066  * snd_soc_dapm_get_volsw - dapm mixer get callback
1067  * @kcontrol: mixer control
1068  * @uinfo: control element information
1069  *
1070  * Callback to get the value of a dapm mixer control.
1071  *
1072  * Returns 0 for success.
1073  */
1074 int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
1075         struct snd_ctl_elem_value *ucontrol)
1076 {
1077         struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
1078         int reg = kcontrol->private_value & 0xff;
1079         int shift = (kcontrol->private_value >> 8) & 0x0f;
1080         int rshift = (kcontrol->private_value >> 12) & 0x0f;
1081         int max = (kcontrol->private_value >> 16) & 0xff;
1082         int invert = (kcontrol->private_value >> 24) & 0x01;
1083         int mask = (1 << fls(max)) - 1;
1084
1085         /* return the saved value if we are powered down */
1086         if (widget->id == snd_soc_dapm_pga && !widget->power) {
1087                 ucontrol->value.integer.value[0] = widget->saved_value;
1088                 return 0;
1089         }
1090
1091         ucontrol->value.integer.value[0] =
1092                 (snd_soc_read(widget->codec, reg) >> shift) & mask;
1093         if (shift != rshift)
1094                 ucontrol->value.integer.value[1] =
1095                         (snd_soc_read(widget->codec, reg) >> rshift) & mask;
1096         if (invert) {
1097                 ucontrol->value.integer.value[0] =
1098                         max - ucontrol->value.integer.value[0];
1099                 if (shift != rshift)
1100                         ucontrol->value.integer.value[1] =
1101                                 max - ucontrol->value.integer.value[1];
1102         }
1103
1104         return 0;
1105 }
1106 EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw);
1107
1108 /**
1109  * snd_soc_dapm_put_volsw - dapm mixer set callback
1110  * @kcontrol: mixer control
1111  * @uinfo: control element information
1112  *
1113  * Callback to set the value of a dapm mixer control.
1114  *
1115  * Returns 0 for success.
1116  */
1117 int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
1118         struct snd_ctl_elem_value *ucontrol)
1119 {
1120         struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
1121         int reg = kcontrol->private_value & 0xff;
1122         int shift = (kcontrol->private_value >> 8) & 0x0f;
1123         int rshift = (kcontrol->private_value >> 12) & 0x0f;
1124         int max = (kcontrol->private_value >> 16) & 0xff;
1125         int mask = (1 << fls(max)) - 1;
1126         int invert = (kcontrol->private_value >> 24) & 0x01;
1127         unsigned short val, val2, val_mask;
1128         int ret;
1129
1130         val = (ucontrol->value.integer.value[0] & mask);
1131
1132         if (invert)
1133                 val = max - val;
1134         val_mask = mask << shift;
1135         val = val << shift;
1136         if (shift != rshift) {
1137                 val2 = (ucontrol->value.integer.value[1] & mask);
1138                 if (invert)
1139                         val2 = max - val2;
1140                 val_mask |= mask << rshift;
1141                 val |= val2 << rshift;
1142         }
1143
1144         mutex_lock(&widget->codec->mutex);
1145         widget->value = val;
1146
1147         /* save volume value if the widget is powered down */
1148         if (widget->id == snd_soc_dapm_pga && !widget->power) {
1149                 widget->saved_value = val;
1150                 mutex_unlock(&widget->codec->mutex);
1151                 return 1;
1152         }
1153
1154         dapm_mixer_update_power(widget, kcontrol, reg, val_mask, val, invert);
1155         if (widget->event) {
1156                 if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
1157                         ret = widget->event(widget, kcontrol,
1158                                                 SND_SOC_DAPM_PRE_REG);
1159                         if (ret < 0) {
1160                                 ret = 1;
1161                                 goto out;
1162                         }
1163                 }
1164                 ret = snd_soc_update_bits(widget->codec, reg, val_mask, val);
1165                 if (widget->event_flags & SND_SOC_DAPM_POST_REG)
1166                         ret = widget->event(widget, kcontrol,
1167                                                 SND_SOC_DAPM_POST_REG);
1168         } else
1169                 ret = snd_soc_update_bits(widget->codec, reg, val_mask, val);
1170
1171 out:
1172         mutex_unlock(&widget->codec->mutex);
1173         return ret;
1174 }
1175 EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw);
1176
1177 /**
1178  * snd_soc_dapm_get_enum_double - dapm enumerated double mixer get callback
1179  * @kcontrol: mixer control
1180  * @uinfo: control element information
1181  *
1182  * Callback to get the value of a dapm enumerated double mixer control.
1183  *
1184  * Returns 0 for success.
1185  */
1186 int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
1187         struct snd_ctl_elem_value *ucontrol)
1188 {
1189         struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
1190         struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
1191         unsigned short val, bitmask;
1192
1193         for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
1194                 ;
1195         val = snd_soc_read(widget->codec, e->reg);
1196         ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1);
1197         if (e->shift_l != e->shift_r)
1198                 ucontrol->value.enumerated.item[1] =
1199                         (val >> e->shift_r) & (bitmask - 1);
1200
1201         return 0;
1202 }
1203 EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double);
1204
1205 /**
1206  * snd_soc_dapm_put_enum_double - dapm enumerated double mixer set callback
1207  * @kcontrol: mixer control
1208  * @uinfo: control element information
1209  *
1210  * Callback to set the value of a dapm enumerated double mixer control.
1211  *
1212  * Returns 0 for success.
1213  */
1214 int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
1215         struct snd_ctl_elem_value *ucontrol)
1216 {
1217         struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
1218         struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
1219         unsigned short val, mux;
1220         unsigned short mask, bitmask;
1221         int ret = 0;
1222
1223         for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
1224                 ;
1225         if (ucontrol->value.enumerated.item[0] > e->mask - 1)
1226                 return -EINVAL;
1227         mux = ucontrol->value.enumerated.item[0];
1228         val = mux << e->shift_l;
1229         mask = (bitmask - 1) << e->shift_l;
1230         if (e->shift_l != e->shift_r) {
1231                 if (ucontrol->value.enumerated.item[1] > e->mask - 1)
1232                         return -EINVAL;
1233                 val |= ucontrol->value.enumerated.item[1] << e->shift_r;
1234                 mask |= (bitmask - 1) << e->shift_r;
1235         }
1236
1237         mutex_lock(&widget->codec->mutex);
1238         widget->value = val;
1239         dapm_mux_update_power(widget, kcontrol, mask, mux, e);
1240         if (widget->event) {
1241                 if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
1242                         ret = widget->event(widget,
1243                                 kcontrol, SND_SOC_DAPM_PRE_REG);
1244                         if (ret < 0)
1245                                 goto out;
1246                 }
1247                 ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
1248                 if (widget->event_flags & SND_SOC_DAPM_POST_REG)
1249                         ret = widget->event(widget,
1250                                 kcontrol, SND_SOC_DAPM_POST_REG);
1251         } else
1252                 ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
1253
1254 out:
1255         mutex_unlock(&widget->codec->mutex);
1256         return ret;
1257 }
1258 EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);
1259
1260 /**
1261  * snd_soc_dapm_new_control - create new dapm control
1262  * @codec: audio codec
1263  * @widget: widget template
1264  *
1265  * Creates a new dapm control based upon the template.
1266  *
1267  * Returns 0 for success else error.
1268  */
1269 int snd_soc_dapm_new_control(struct snd_soc_codec *codec,
1270         const struct snd_soc_dapm_widget *widget)
1271 {
1272         struct snd_soc_dapm_widget *w;
1273
1274         if ((w = dapm_cnew_widget(widget)) == NULL)
1275                 return -ENOMEM;
1276
1277         w->codec = codec;
1278         INIT_LIST_HEAD(&w->sources);
1279         INIT_LIST_HEAD(&w->sinks);
1280         INIT_LIST_HEAD(&w->list);
1281         list_add(&w->list, &codec->dapm_widgets);
1282
1283         /* machine layer set ups unconnected pins and insertions */
1284         w->connected = 1;
1285         return 0;
1286 }
1287 EXPORT_SYMBOL_GPL(snd_soc_dapm_new_control);
1288
1289 /**
1290  * snd_soc_dapm_new_controls - create new dapm controls
1291  * @codec: audio codec
1292  * @widget: widget array
1293  * @num: number of widgets
1294  *
1295  * Creates new DAPM controls based upon the templates.
1296  *
1297  * Returns 0 for success else error.
1298  */
1299 int snd_soc_dapm_new_controls(struct snd_soc_codec *codec,
1300         const struct snd_soc_dapm_widget *widget,
1301         int num)
1302 {
1303         int i, ret;
1304
1305         for (i = 0; i < num; i++) {
1306                 ret = snd_soc_dapm_new_control(codec, widget);
1307                 if (ret < 0)
1308                         return ret;
1309                 widget++;
1310         }
1311         return 0;
1312 }
1313 EXPORT_SYMBOL_GPL(snd_soc_dapm_new_controls);
1314
1315
1316 /**
1317  * snd_soc_dapm_stream_event - send a stream event to the dapm core
1318  * @codec: audio codec
1319  * @stream: stream name
1320  * @event: stream event
1321  *
1322  * Sends a stream event to the dapm core. The core then makes any
1323  * necessary widget power changes.
1324  *
1325  * Returns 0 for success else error.
1326  */
1327 int snd_soc_dapm_stream_event(struct snd_soc_codec *codec,
1328         char *stream, int event)
1329 {
1330         struct snd_soc_dapm_widget *w;
1331
1332         if (stream == NULL)
1333                 return 0;
1334
1335         mutex_lock(&codec->mutex);
1336         list_for_each_entry(w, &codec->dapm_widgets, list)
1337         {
1338                 if (!w->sname)
1339                         continue;
1340                 dbg("widget %s\n %s stream %s event %d\n", w->name, w->sname,
1341                         stream, event);
1342                 if (strstr(w->sname, stream)) {
1343                         switch(event) {
1344                         case SND_SOC_DAPM_STREAM_START:
1345                                 w->active = 1;
1346                                 break;
1347                         case SND_SOC_DAPM_STREAM_STOP:
1348                                 w->active = 0;
1349                                 break;
1350                         case SND_SOC_DAPM_STREAM_SUSPEND:
1351                                 if (w->active)
1352                                         w->suspend = 1;
1353                                 w->active = 0;
1354                                 break;
1355                         case SND_SOC_DAPM_STREAM_RESUME:
1356                                 if (w->suspend) {
1357                                         w->active = 1;
1358                                         w->suspend = 0;
1359                                 }
1360                                 break;
1361                         case SND_SOC_DAPM_STREAM_PAUSE_PUSH:
1362                                 break;
1363                         case SND_SOC_DAPM_STREAM_PAUSE_RELEASE:
1364                                 break;
1365                         }
1366                 }
1367         }
1368         mutex_unlock(&codec->mutex);
1369
1370         dapm_power_widgets(codec, event);
1371         dump_dapm(codec, __func__);
1372         return 0;
1373 }
1374 EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event);
1375
1376 /**
1377  * snd_soc_dapm_set_bias_level - set the bias level for the system
1378  * @socdev: audio device
1379  * @level: level to configure
1380  *
1381  * Configure the bias (power) levels for the SoC audio device.
1382  *
1383  * Returns 0 for success else error.
1384  */
1385 int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev,
1386                                 enum snd_soc_bias_level level)
1387 {
1388         struct snd_soc_codec *codec = socdev->codec;
1389         struct snd_soc_machine *machine = socdev->machine;
1390         int ret = 0;
1391
1392         if (machine->set_bias_level)
1393                 ret = machine->set_bias_level(machine, level);
1394         if (ret == 0 && codec->set_bias_level)
1395                 ret = codec->set_bias_level(codec, level);
1396
1397         return ret;
1398 }
1399
1400 /**
1401  * snd_soc_dapm_set_endpoint - set audio endpoint status
1402  * @codec: audio codec
1403  * @endpoint: audio signal endpoint (or start point)
1404  * @status: point status
1405  *
1406  * Set audio endpoint status - connected or disconnected.
1407  *
1408  * Returns 0 for success else error.
1409  */
1410 int snd_soc_dapm_set_endpoint(struct snd_soc_codec *codec,
1411         char *endpoint, int status)
1412 {
1413         struct snd_soc_dapm_widget *w;
1414
1415         list_for_each_entry(w, &codec->dapm_widgets, list) {
1416                 if (!strcmp(w->name, endpoint)) {
1417                         w->connected = status;
1418                         return 0;
1419                 }
1420         }
1421
1422         return -ENODEV;
1423 }
1424 EXPORT_SYMBOL_GPL(snd_soc_dapm_set_endpoint);
1425
1426 /**
1427  * snd_soc_dapm_get_endpoint_status - get audio endpoint status
1428  * @codec: audio codec
1429  * @endpoint: audio signal endpoint (or start point)
1430  *
1431  * Get audio endpoint status - connected or disconnected.
1432  *
1433  * Returns status
1434  */
1435 int snd_soc_dapm_get_endpoint_status(struct snd_soc_codec *codec,
1436         char *endpoint)
1437 {
1438         struct snd_soc_dapm_widget *w;
1439
1440         list_for_each_entry(w, &codec->dapm_widgets, list) {
1441                 if (!strcmp(w->name, endpoint))
1442                         return w->connected;
1443         }
1444
1445         return 0;
1446 }
1447 EXPORT_SYMBOL_GPL(snd_soc_dapm_get_endpoint_status);
1448
1449 /**
1450  * snd_soc_dapm_free - free dapm resources
1451  * @socdev: SoC device
1452  *
1453  * Free all dapm widgets and resources.
1454  */
1455 void snd_soc_dapm_free(struct snd_soc_device *socdev)
1456 {
1457         struct snd_soc_codec *codec = socdev->codec;
1458
1459         snd_soc_dapm_sys_remove(socdev->dev);
1460         dapm_free_widgets(codec);
1461 }
1462 EXPORT_SYMBOL_GPL(snd_soc_dapm_free);
1463
1464 /* Module information */
1465 MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
1466 MODULE_DESCRIPTION("Dynamic Audio Power Management core for ALSA SoC");
1467 MODULE_LICENSE("GPL");