[ALSA] soc - Fix power switching support for DAPM_SWITCH widgets
[safe/jmp/linux-2.6] / sound / soc / soc-dapm.c
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  *  Revision history
14  *    12th Aug 2005   Initial version.
15  *    25th Oct 2005   Implemented path power domain.
16  *    18th Dec 2005   Implemented machine and stream level power domain.
17  *
18  *  Features:
19  *    o Changes power status of internal codec blocks depending on the
20  *      dynamic configuration of codec internal audio paths and active
21  *      DAC's/ADC's.
22  *    o Platform power domain - can support external components i.e. amps and
23  *      mic/meadphone insertion events.
24  *    o Automatic Mic Bias support
25  *    o Jack insertion power event initiation - e.g. hp insertion will enable
26  *      sinks, dacs, etc
27  *    o Delayed powerdown of audio susbsystem to reduce pops between a quick
28  *      device reopen.
29  *
30  *  Todo:
31  *    o DAPM power change sequencing - allow for configurable per
32  *      codec sequences.
33  *    o Support for analogue bias optimisation.
34  *    o Support for reduced codec oversampling rates.
35  *    o Support for reduced codec bias currents.
36  */
37
38 #include <linux/module.h>
39 #include <linux/moduleparam.h>
40 #include <linux/init.h>
41 #include <linux/delay.h>
42 #include <linux/pm.h>
43 #include <linux/bitops.h>
44 #include <linux/platform_device.h>
45 #include <linux/jiffies.h>
46 #include <sound/core.h>
47 #include <sound/pcm.h>
48 #include <sound/pcm_params.h>
49 #include <sound/soc-dapm.h>
50 #include <sound/initval.h>
51
52 /* debug */
53 #define DAPM_DEBUG 0
54 #if DAPM_DEBUG
55 #define dump_dapm(codec, action) dbg_dump_dapm(codec, action)
56 #define dbg(format, arg...) printk(format, ## arg)
57 #else
58 #define dump_dapm(codec, action)
59 #define dbg(format, arg...)
60 #endif
61
62 #define POP_DEBUG 0
63 #if POP_DEBUG
64 #define POP_TIME 500 /* 500 msecs - change if pop debug is too fast */
65 #define pop_wait(time) schedule_timeout_uninterruptible(msecs_to_jiffies(time))
66 #define pop_dbg(format, arg...) printk(format, ## arg); pop_wait(POP_TIME)
67 #else
68 #define pop_dbg(format, arg...)
69 #define pop_wait(time)
70 #endif
71
72 /* dapm power sequences - make this per codec in the future */
73 static int dapm_up_seq[] = {
74         snd_soc_dapm_pre, snd_soc_dapm_micbias, snd_soc_dapm_mic,
75         snd_soc_dapm_mux, snd_soc_dapm_dac, snd_soc_dapm_mixer, snd_soc_dapm_pga,
76         snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, snd_soc_dapm_post
77 };
78 static int dapm_down_seq[] = {
79         snd_soc_dapm_pre, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk,
80         snd_soc_dapm_pga, snd_soc_dapm_mixer, snd_soc_dapm_dac, snd_soc_dapm_mic,
81         snd_soc_dapm_micbias, snd_soc_dapm_mux, snd_soc_dapm_post
82 };
83
84 static int dapm_status = 1;
85 module_param(dapm_status, int, 0);
86 MODULE_PARM_DESC(dapm_status, "enable DPM sysfs entries");
87
88 /* create a new dapm widget */
89 static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
90         const struct snd_soc_dapm_widget *_widget)
91 {
92         return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL);
93 }
94
95 /* set up initial codec paths */
96 static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
97         struct snd_soc_dapm_path *p, int i)
98 {
99         switch (w->id) {
100         case snd_soc_dapm_switch:
101         case snd_soc_dapm_mixer: {
102                 int val;
103                 int reg = w->kcontrols[i].private_value & 0xff;
104                 int shift = (w->kcontrols[i].private_value >> 8) & 0x0f;
105                 int mask = (w->kcontrols[i].private_value >> 16) & 0xff;
106                 int invert = (w->kcontrols[i].private_value >> 24) & 0x01;
107
108                 val = snd_soc_read(w->codec, reg);
109                 val = (val >> shift) & mask;
110
111                 if ((invert && !val) || (!invert && val))
112                         p->connect = 1;
113                 else
114                         p->connect = 0;
115         }
116         break;
117         case snd_soc_dapm_mux: {
118                 struct soc_enum *e = (struct soc_enum *)w->kcontrols[i].private_value;
119                 int val, item, bitmask;
120
121                 for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
122                 ;
123                 val = snd_soc_read(w->codec, e->reg);
124                 item = (val >> e->shift_l) & (bitmask - 1);
125
126                 p->connect = 0;
127                 for (i = 0; i < e->mask; i++) {
128                         if (!(strcmp(p->name, e->texts[i])) && item == i)
129                                 p->connect = 1;
130                 }
131         }
132         break;
133         /* does not effect routing - always connected */
134         case snd_soc_dapm_pga:
135         case snd_soc_dapm_output:
136         case snd_soc_dapm_adc:
137         case snd_soc_dapm_input:
138         case snd_soc_dapm_dac:
139         case snd_soc_dapm_micbias:
140         case snd_soc_dapm_vmid:
141                 p->connect = 1;
142         break;
143         /* does effect routing - dynamically connected */
144         case snd_soc_dapm_hp:
145         case snd_soc_dapm_mic:
146         case snd_soc_dapm_spk:
147         case snd_soc_dapm_line:
148         case snd_soc_dapm_pre:
149         case snd_soc_dapm_post:
150                 p->connect = 0;
151         break;
152         }
153 }
154
155 /* connect mux widget to it's interconnecting audio paths */
156 static int dapm_connect_mux(struct snd_soc_codec *codec,
157         struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
158         struct snd_soc_dapm_path *path, const char *control_name,
159         const struct snd_kcontrol_new *kcontrol)
160 {
161         struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
162         int i;
163
164         for (i = 0; i < e->mask; i++) {
165                 if (!(strcmp(control_name, e->texts[i]))) {
166                         list_add(&path->list, &codec->dapm_paths);
167                         list_add(&path->list_sink, &dest->sources);
168                         list_add(&path->list_source, &src->sinks);
169                         path->name = (char*)e->texts[i];
170                         dapm_set_path_status(dest, path, 0);
171                         return 0;
172                 }
173         }
174
175         return -ENODEV;
176 }
177
178 /* connect mixer widget to it's interconnecting audio paths */
179 static int dapm_connect_mixer(struct snd_soc_codec *codec,
180         struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
181         struct snd_soc_dapm_path *path, const char *control_name)
182 {
183         int i;
184
185         /* search for mixer kcontrol */
186         for (i = 0; i < dest->num_kcontrols; i++) {
187                 if (!strcmp(control_name, dest->kcontrols[i].name)) {
188                         list_add(&path->list, &codec->dapm_paths);
189                         list_add(&path->list_sink, &dest->sources);
190                         list_add(&path->list_source, &src->sinks);
191                         path->name = dest->kcontrols[i].name;
192                         dapm_set_path_status(dest, path, i);
193                         return 0;
194                 }
195         }
196         return -ENODEV;
197 }
198
199 /* update dapm codec register bits */
200 static int dapm_update_bits(struct snd_soc_dapm_widget *widget)
201 {
202         int change, power;
203         unsigned short old, new;
204         struct snd_soc_codec *codec = widget->codec;
205
206         /* check for valid widgets */
207         if (widget->reg < 0 || widget->id == snd_soc_dapm_input ||
208                 widget->id == snd_soc_dapm_output ||
209                 widget->id == snd_soc_dapm_hp ||
210                 widget->id == snd_soc_dapm_mic ||
211                 widget->id == snd_soc_dapm_line ||
212                 widget->id == snd_soc_dapm_spk)
213                 return 0;
214
215         power = widget->power;
216         if (widget->invert)
217                 power = (power ? 0:1);
218
219         old = snd_soc_read(codec, widget->reg);
220         new = (old & ~(0x1 << widget->shift)) | (power << widget->shift);
221
222         change = old != new;
223         if (change) {
224                 pop_dbg("pop test %s : %s in %d ms\n", widget->name,
225                         widget->power ? "on" : "off", POP_TIME);
226                 snd_soc_write(codec, widget->reg, new);
227                 pop_wait(POP_TIME);
228         }
229         dbg("reg old %x new %x change %d\n", old, new, change);
230         return change;
231 }
232
233 /* ramps the volume up or down to minimise pops before or after a
234  * DAPM power event */
235 static int dapm_set_pga(struct snd_soc_dapm_widget *widget, int power)
236 {
237         const struct snd_kcontrol_new *k = widget->kcontrols;
238
239         if (widget->muted && !power)
240                 return 0;
241         if (!widget->muted && power)
242                 return 0;
243
244         if (widget->num_kcontrols && k) {
245                 int reg = k->private_value & 0xff;
246                 int shift = (k->private_value >> 8) & 0x0f;
247                 int mask = (k->private_value >> 16) & 0xff;
248                 int invert = (k->private_value >> 24) & 0x01;
249
250                 if (power) {
251                         int i;
252                         /* power up has happended, increase volume to last level */
253                         if (invert) {
254                                 for (i = mask; i > widget->saved_value; i--)
255                                         snd_soc_update_bits(widget->codec, reg, mask, i);
256                         } else {
257                                 for (i = 0; i < widget->saved_value; i++)
258                                         snd_soc_update_bits(widget->codec, reg, mask, i);
259                         }
260                         widget->muted = 0;
261                 } else {
262                         /* power down is about to occur, decrease volume to mute */
263                         int val = snd_soc_read(widget->codec, reg);
264                         int i = widget->saved_value = (val >> shift) & mask;
265                         if (invert) {
266                                 for (; i < mask; i++)
267                                         snd_soc_update_bits(widget->codec, reg, mask, i);
268                         } else {
269                                 for (; i > 0; i--)
270                                         snd_soc_update_bits(widget->codec, reg, mask, i);
271                         }
272                         widget->muted = 1;
273                 }
274         }
275         return 0;
276 }
277
278 /* create new dapm mixer control */
279 static int dapm_new_mixer(struct snd_soc_codec *codec,
280         struct snd_soc_dapm_widget *w)
281 {
282         int i, ret = 0;
283         char name[32];
284         struct snd_soc_dapm_path *path;
285
286         /* add kcontrol */
287         for (i = 0; i < w->num_kcontrols; i++) {
288
289                 /* match name */
290                 list_for_each_entry(path, &w->sources, list_sink) {
291
292                         /* mixer/mux paths name must match control name */
293                         if (path->name != (char*)w->kcontrols[i].name)
294                                 continue;
295
296                         /* add dapm control with long name */
297                         snprintf(name, 32, "%s %s", w->name, w->kcontrols[i].name);
298                         path->long_name = kstrdup (name, GFP_KERNEL);
299                         if (path->long_name == NULL)
300                                 return -ENOMEM;
301
302                         path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w,
303                                 path->long_name);
304                         ret = snd_ctl_add(codec->card, path->kcontrol);
305                         if (ret < 0) {
306                                 printk(KERN_ERR "asoc: failed to add dapm kcontrol %s\n",
307                                                 path->long_name);
308                                 kfree(path->long_name);
309                                 path->long_name = NULL;
310                                 return ret;
311                         }
312                 }
313         }
314         return ret;
315 }
316
317 /* create new dapm mux control */
318 static int dapm_new_mux(struct snd_soc_codec *codec,
319         struct snd_soc_dapm_widget *w)
320 {
321         struct snd_soc_dapm_path *path = NULL;
322         struct snd_kcontrol *kcontrol;
323         int ret = 0;
324
325         if (!w->num_kcontrols) {
326                 printk(KERN_ERR "asoc: mux %s has no controls\n", w->name);
327                 return -EINVAL;
328         }
329
330         kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name);
331         ret = snd_ctl_add(codec->card, kcontrol);
332         if (ret < 0)
333                 goto err;
334
335         list_for_each_entry(path, &w->sources, list_sink)
336                 path->kcontrol = kcontrol;
337
338         return ret;
339
340 err:
341         printk(KERN_ERR "asoc: failed to add kcontrol %s\n", w->name);
342         return ret;
343 }
344
345 /* create new dapm volume control */
346 static int dapm_new_pga(struct snd_soc_codec *codec,
347         struct snd_soc_dapm_widget *w)
348 {
349         struct snd_kcontrol *kcontrol;
350         int ret = 0;
351
352         if (!w->num_kcontrols)
353                 return -EINVAL;
354
355         kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name);
356         ret = snd_ctl_add(codec->card, kcontrol);
357         if (ret < 0) {
358                 printk(KERN_ERR "asoc: failed to add kcontrol %s\n", w->name);
359                 return ret;
360         }
361
362         return ret;
363 }
364
365 /* reset 'walked' bit for each dapm path */
366 static inline void dapm_clear_walk(struct snd_soc_codec *codec)
367 {
368         struct snd_soc_dapm_path *p;
369
370         list_for_each_entry(p, &codec->dapm_paths, list)
371                 p->walked = 0;
372 }
373
374 /*
375  * Recursively check for a completed path to an active or physically connected
376  * output widget. Returns number of complete paths.
377  */
378 static int is_connected_output_ep(struct snd_soc_dapm_widget *widget)
379 {
380         struct snd_soc_dapm_path *path;
381         int con = 0;
382
383         if (widget->id == snd_soc_dapm_adc && widget->active)
384                 return 1;
385
386         if (widget->connected) {
387                 /* connected pin ? */
388                 if (widget->id == snd_soc_dapm_output && !widget->ext)
389                         return 1;
390
391                 /* connected jack or spk ? */
392                 if (widget->id == snd_soc_dapm_hp || widget->id == snd_soc_dapm_spk ||
393                         widget->id == snd_soc_dapm_line)
394                         return 1;
395         }
396
397         list_for_each_entry(path, &widget->sinks, list_source) {
398                 if (path->walked)
399                         continue;
400
401                 if (path->sink && path->connect) {
402                         path->walked = 1;
403                         con += is_connected_output_ep(path->sink);
404                 }
405         }
406
407         return con;
408 }
409
410 /*
411  * Recursively check for a completed path to an active or physically connected
412  * input widget. Returns number of complete paths.
413  */
414 static int is_connected_input_ep(struct snd_soc_dapm_widget *widget)
415 {
416         struct snd_soc_dapm_path *path;
417         int con = 0;
418
419         /* active stream ? */
420         if (widget->id == snd_soc_dapm_dac && widget->active)
421                 return 1;
422
423         if (widget->connected) {
424                 /* connected pin ? */
425                 if (widget->id == snd_soc_dapm_input && !widget->ext)
426                         return 1;
427
428                 /* connected VMID/Bias for lower pops */
429                 if (widget->id == snd_soc_dapm_vmid)
430                         return 1;
431
432                 /* connected jack ? */
433                 if (widget->id == snd_soc_dapm_mic || widget->id == snd_soc_dapm_line)
434                         return 1;
435         }
436
437         list_for_each_entry(path, &widget->sources, list_sink) {
438                 if (path->walked)
439                         continue;
440
441                 if (path->source && path->connect) {
442                         path->walked = 1;
443                         con += is_connected_input_ep(path->source);
444                 }
445         }
446
447         return con;
448 }
449
450 /*
451  * Scan each dapm widget for complete audio path.
452  * A complete path is a route that has valid endpoints i.e.:-
453  *
454  *  o DAC to output pin.
455  *  o Input Pin to ADC.
456  *  o Input pin to Output pin (bypass, sidetone)
457  *  o DAC to ADC (loopback).
458  */
459 static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
460 {
461         struct snd_soc_dapm_widget *w;
462         int in, out, i, c = 1, *seq = NULL, ret = 0, power_change, power;
463
464         /* do we have a sequenced stream event */
465         if (event == SND_SOC_DAPM_STREAM_START) {
466                 c = ARRAY_SIZE(dapm_up_seq);
467                 seq = dapm_up_seq;
468         } else if (event == SND_SOC_DAPM_STREAM_STOP) {
469                 c = ARRAY_SIZE(dapm_down_seq);
470                 seq = dapm_down_seq;
471         }
472
473         for(i = 0; i < c; i++) {
474                 list_for_each_entry(w, &codec->dapm_widgets, list) {
475
476                         /* is widget in stream order */
477                         if (seq && seq[i] && w->id != seq[i])
478                                 continue;
479
480                         /* vmid - no action */
481                         if (w->id == snd_soc_dapm_vmid)
482                                 continue;
483
484                         /* active ADC */
485                         if (w->id == snd_soc_dapm_adc && w->active) {
486                                 in = is_connected_input_ep(w);
487                                 dapm_clear_walk(w->codec);
488                                 w->power = (in != 0) ? 1 : 0;
489                                 dapm_update_bits(w);
490                                 continue;
491                         }
492
493                         /* active DAC */
494                         if (w->id == snd_soc_dapm_dac && w->active) {
495                                 out = is_connected_output_ep(w);
496                                 dapm_clear_walk(w->codec);
497                                 w->power = (out != 0) ? 1 : 0;
498                                 dapm_update_bits(w);
499                                 continue;
500                         }
501
502                         /* programmable gain/attenuation */
503                         if (w->id == snd_soc_dapm_pga) {
504                                 int on;
505                                 in = is_connected_input_ep(w);
506                                 dapm_clear_walk(w->codec);
507                                 out = is_connected_output_ep(w);
508                                 dapm_clear_walk(w->codec);
509                                 w->power = on = (out != 0 && in != 0) ? 1 : 0;
510
511                                 if (!on)
512                                         dapm_set_pga(w, on); /* lower volume to reduce pops */
513                                 dapm_update_bits(w);
514                                 if (on)
515                                         dapm_set_pga(w, on); /* restore volume from zero */
516
517                                 continue;
518                         }
519
520                         /* pre and post event widgets */
521                         if (w->id == snd_soc_dapm_pre) {
522                                 if (!w->event)
523                                         continue;
524
525                                 if (event == SND_SOC_DAPM_STREAM_START) {
526                                         ret = w->event(w, SND_SOC_DAPM_PRE_PMU);
527                                         if (ret < 0)
528                                                 return ret;
529                                 } else if (event == SND_SOC_DAPM_STREAM_STOP) {
530                                         ret = w->event(w, SND_SOC_DAPM_PRE_PMD);
531                                         if (ret < 0)
532                                                 return ret;
533                                 }
534                                 continue;
535                         }
536                         if (w->id == snd_soc_dapm_post) {
537                                 if (!w->event)
538                                         continue;
539
540                                 if (event == SND_SOC_DAPM_STREAM_START) {
541                                         ret = w->event(w, SND_SOC_DAPM_POST_PMU);
542                                         if (ret < 0)
543                                                 return ret;
544                                 } else if (event == SND_SOC_DAPM_STREAM_STOP) {
545                                         ret = w->event(w, SND_SOC_DAPM_POST_PMD);
546                                         if (ret < 0)
547                                                 return ret;
548                                 }
549                                 continue;
550                         }
551
552                         /* all other widgets */
553                         in = is_connected_input_ep(w);
554                         dapm_clear_walk(w->codec);
555                         out = is_connected_output_ep(w);
556                         dapm_clear_walk(w->codec);
557                         power = (out != 0 && in != 0) ? 1 : 0;
558                         power_change = (w->power == power) ? 0: 1;
559                         w->power = power;
560
561                         /* call any power change event handlers */
562                         if (power_change) {
563                                 if (w->event) {
564                                         dbg("power %s event for %s flags %x\n",
565                                                 w->power ? "on" : "off", w->name, w->event_flags);
566                                         if (power) {
567                                                 /* power up event */
568                                                 if (w->event_flags & SND_SOC_DAPM_PRE_PMU) {
569                                                         ret = w->event(w, SND_SOC_DAPM_PRE_PMU);
570                                                         if (ret < 0)
571                                                                 return ret;
572                                                 }
573                                                 dapm_update_bits(w);
574                                                 if (w->event_flags & SND_SOC_DAPM_POST_PMU){
575                                                         ret = w->event(w, SND_SOC_DAPM_POST_PMU);
576                                                         if (ret < 0)
577                                                                 return ret;
578                                                 }
579                                         } else {
580                                                 /* power down event */
581                                                 if (w->event_flags & SND_SOC_DAPM_PRE_PMD) {
582                                                         ret = w->event(w, SND_SOC_DAPM_PRE_PMD);
583                                                         if (ret < 0)
584                                                                 return ret;
585                                                 }
586                                                 dapm_update_bits(w);
587                                                 if (w->event_flags & SND_SOC_DAPM_POST_PMD) {
588                                                         ret = w->event(w, SND_SOC_DAPM_POST_PMD);
589                                                         if (ret < 0)
590                                                                 return ret;
591                                                 }
592                                         }
593                                 } else
594                                         /* no event handler */
595                                         dapm_update_bits(w);
596                         }
597                 }
598         }
599
600         return ret;
601 }
602
603 #if DAPM_DEBUG
604 static void dbg_dump_dapm(struct snd_soc_codec* codec, const char *action)
605 {
606         struct snd_soc_dapm_widget *w;
607         struct snd_soc_dapm_path *p = NULL;
608         int in, out;
609
610         printk("DAPM %s %s\n", codec->name, action);
611
612         list_for_each_entry(w, &codec->dapm_widgets, list) {
613
614                 /* only display widgets that effect routing */
615                 switch (w->id) {
616                 case snd_soc_dapm_pre:
617                 case snd_soc_dapm_post:
618                 case snd_soc_dapm_vmid:
619                         continue;
620                 case snd_soc_dapm_mux:
621                 case snd_soc_dapm_output:
622                 case snd_soc_dapm_input:
623                 case snd_soc_dapm_switch:
624                 case snd_soc_dapm_hp:
625                 case snd_soc_dapm_mic:
626                 case snd_soc_dapm_spk:
627                 case snd_soc_dapm_line:
628                 case snd_soc_dapm_micbias:
629                 case snd_soc_dapm_dac:
630                 case snd_soc_dapm_adc:
631                 case snd_soc_dapm_pga:
632                 case snd_soc_dapm_mixer:
633                         if (w->name) {
634                                 in = is_connected_input_ep(w);
635                                 dapm_clear_walk(w->codec);
636                                 out = is_connected_output_ep(w);
637                                 dapm_clear_walk(w->codec);
638                                 printk("%s: %s  in %d out %d\n", w->name,
639                                         w->power ? "On":"Off",in, out);
640
641                                 list_for_each_entry(p, &w->sources, list_sink) {
642                                         if (p->connect)
643                                                 printk(" in  %s %s\n", p->name ? p->name : "static",
644                                                         p->source->name);
645                                 }
646                                 list_for_each_entry(p, &w->sinks, list_source) {
647                                         if (p->connect)
648                                                 printk(" out %s %s\n", p->name ? p->name : "static",
649                                                         p->sink->name);
650                                 }
651                         }
652                 break;
653                 }
654         }
655 }
656 #endif
657
658 /* test and update the power status of a mux widget */
659 static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
660                                  struct snd_kcontrol *kcontrol, int mask,
661                                  int val, struct soc_enum* e)
662 {
663         struct snd_soc_dapm_path *path;
664         int found = 0;
665
666         if (widget->id != snd_soc_dapm_mux)
667                 return -ENODEV;
668
669         if (!snd_soc_test_bits(widget->codec, e->reg, mask, val))
670                 return 0;
671
672         /* find dapm widget path assoc with kcontrol */
673         list_for_each_entry(path, &widget->codec->dapm_paths, list) {
674                 if (path->kcontrol != kcontrol)
675                         continue;
676
677                 if (!path->name || ! e->texts[val])
678                         continue;
679
680                 found = 1;
681                 /* we now need to match the string in the enum to the path */
682                 if (!(strcmp(path->name, e->texts[val])))
683                         path->connect = 1; /* new connection */
684                 else
685                         path->connect = 0; /* old connection must be powered down */
686         }
687
688         if (found)
689                 dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP);
690
691         return 0;
692 }
693
694 /* test and update the power status of a mixer or switch widget */
695 static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
696                                    struct snd_kcontrol *kcontrol, int reg,
697                                    int val_mask, int val, int invert)
698 {
699         struct snd_soc_dapm_path *path;
700         int found = 0;
701
702         if (widget->id != snd_soc_dapm_mixer &&
703             widget->id != snd_soc_dapm_switch)
704                 return -ENODEV;
705
706         if (!snd_soc_test_bits(widget->codec, reg, val_mask, val))
707                 return 0;
708
709         /* find dapm widget path assoc with kcontrol */
710         list_for_each_entry(path, &widget->codec->dapm_paths, list) {
711                 if (path->kcontrol != kcontrol)
712                         continue;
713
714                 /* found, now check type */
715                 found = 1;
716                 if (val)
717                         /* new connection */
718                         path->connect = invert ? 0:1;
719                 else
720                         /* old connection must be powered down */
721                         path->connect = invert ? 1:0;
722                 break;
723         }
724
725         if (found)
726                 dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP);
727
728         return 0;
729 }
730
731 /* show dapm widget status in sys fs */
732 static ssize_t dapm_widget_show(struct device *dev,
733         struct device_attribute *attr, char *buf)
734 {
735         struct snd_soc_device *devdata = dev_get_drvdata(dev);
736         struct snd_soc_codec *codec = devdata->codec;
737         struct snd_soc_dapm_widget *w;
738         int count = 0;
739         char *state = "not set";
740
741         list_for_each_entry(w, &codec->dapm_widgets, list) {
742
743                 /* only display widgets that burnm power */
744                 switch (w->id) {
745                 case snd_soc_dapm_hp:
746                 case snd_soc_dapm_mic:
747                 case snd_soc_dapm_spk:
748                 case snd_soc_dapm_line:
749                 case snd_soc_dapm_micbias:
750                 case snd_soc_dapm_dac:
751                 case snd_soc_dapm_adc:
752                 case snd_soc_dapm_pga:
753                 case snd_soc_dapm_mixer:
754                         if (w->name)
755                                 count += sprintf(buf + count, "%s: %s\n",
756                                         w->name, w->power ? "On":"Off");
757                 break;
758                 default:
759                 break;
760                 }
761         }
762
763         switch(codec->dapm_state){
764         case SNDRV_CTL_POWER_D0:
765                 state = "D0";
766                 break;
767         case SNDRV_CTL_POWER_D1:
768                 state = "D1";
769                 break;
770         case SNDRV_CTL_POWER_D2:
771                 state = "D2";
772                 break;
773         case SNDRV_CTL_POWER_D3hot:
774                 state = "D3hot";
775                 break;
776         case SNDRV_CTL_POWER_D3cold:
777                 state = "D3cold";
778                 break;
779         }
780         count += sprintf(buf + count, "PM State: %s\n", state);
781
782         return count;
783 }
784
785 static DEVICE_ATTR(dapm_widget, 0444, dapm_widget_show, NULL);
786
787 int 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
797 static 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 */
804 static void dapm_free_widgets(struct snd_soc_codec *codec)
805 {
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  */
830 int snd_soc_dapm_sync_endpoints(struct snd_soc_codec *codec)
831 {
832         return dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP);
833 }
834 EXPORT_SYMBOL_GPL(snd_soc_dapm_sync_endpoints);
835
836 /**
837  * snd_soc_dapm_connect_input - connect dapm widgets
838  * @codec: audio codec
839  * @sink: name of target widget
840  * @control: mixer control name
841  * @source: name of source name
842  *
843  * Connects 2 dapm widgets together via a named audio path. The sink is
844  * the widget receiving the audio signal, whilst the source is the sender
845  * of the audio signal.
846  *
847  * Returns 0 for success else error.
848  */
849 int snd_soc_dapm_connect_input(struct snd_soc_codec *codec, const char *sink,
850         const char * control, const char *source)
851 {
852         struct snd_soc_dapm_path *path;
853         struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w;
854         int ret = 0;
855
856         /* find src and dest widgets */
857         list_for_each_entry(w, &codec->dapm_widgets, list) {
858
859                 if (!wsink && !(strcmp(w->name, sink))) {
860                         wsink = w;
861                         continue;
862                 }
863                 if (!wsource && !(strcmp(w->name, source))) {
864                         wsource = w;
865                 }
866         }
867
868         if (wsource == NULL || wsink == NULL)
869                 return -ENODEV;
870
871         path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL);
872         if (!path)
873                 return -ENOMEM;
874
875         path->source = wsource;
876         path->sink = wsink;
877         INIT_LIST_HEAD(&path->list);
878         INIT_LIST_HEAD(&path->list_source);
879         INIT_LIST_HEAD(&path->list_sink);
880
881         /* check for external widgets */
882         if (wsink->id == snd_soc_dapm_input) {
883                 if (wsource->id == snd_soc_dapm_micbias ||
884                         wsource->id == snd_soc_dapm_mic ||
885                         wsink->id == snd_soc_dapm_line ||
886                         wsink->id == snd_soc_dapm_output)
887                         wsink->ext = 1;
888         }
889         if (wsource->id == snd_soc_dapm_output) {
890                 if (wsink->id == snd_soc_dapm_spk ||
891                         wsink->id == snd_soc_dapm_hp ||
892                         wsink->id == snd_soc_dapm_line ||
893                         wsink->id == snd_soc_dapm_input)
894                         wsource->ext = 1;
895         }
896
897         /* connect static paths */
898         if (control == NULL) {
899                 list_add(&path->list, &codec->dapm_paths);
900                 list_add(&path->list_sink, &wsink->sources);
901                 list_add(&path->list_source, &wsource->sinks);
902                 path->connect = 1;
903                 return 0;
904         }
905
906         /* connect dynamic paths */
907         switch(wsink->id) {
908         case snd_soc_dapm_adc:
909         case snd_soc_dapm_dac:
910         case snd_soc_dapm_pga:
911         case snd_soc_dapm_input:
912         case snd_soc_dapm_output:
913         case snd_soc_dapm_micbias:
914         case snd_soc_dapm_vmid:
915         case snd_soc_dapm_pre:
916         case snd_soc_dapm_post:
917                 list_add(&path->list, &codec->dapm_paths);
918                 list_add(&path->list_sink, &wsink->sources);
919                 list_add(&path->list_source, &wsource->sinks);
920                 path->connect = 1;
921                 return 0;
922         case snd_soc_dapm_mux:
923                 ret = dapm_connect_mux(codec, wsource, wsink, path, control,
924                         &wsink->kcontrols[0]);
925                 if (ret != 0)
926                         goto err;
927                 break;
928         case snd_soc_dapm_switch:
929         case snd_soc_dapm_mixer:
930                 ret = dapm_connect_mixer(codec, wsource, wsink, path, control);
931                 if (ret != 0)
932                         goto err;
933                 break;
934         case snd_soc_dapm_hp:
935         case snd_soc_dapm_mic:
936         case snd_soc_dapm_line:
937         case snd_soc_dapm_spk:
938                 list_add(&path->list, &codec->dapm_paths);
939                 list_add(&path->list_sink, &wsink->sources);
940                 list_add(&path->list_source, &wsource->sinks);
941                 path->connect = 0;
942                 return 0;
943         }
944         return 0;
945
946 err:
947         printk(KERN_WARNING "asoc: no dapm match for %s --> %s --> %s\n", source,
948                 control, sink);
949         kfree(path);
950         return ret;
951 }
952 EXPORT_SYMBOL_GPL(snd_soc_dapm_connect_input);
953
954 /**
955  * snd_soc_dapm_new_widgets - add new dapm widgets
956  * @codec: audio codec
957  *
958  * Checks the codec for any new dapm widgets and creates them if found.
959  *
960  * Returns 0 for success.
961  */
962 int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec)
963 {
964         struct snd_soc_dapm_widget *w;
965
966         mutex_lock(&codec->mutex);
967         list_for_each_entry(w, &codec->dapm_widgets, list)
968         {
969                 if (w->new)
970                         continue;
971
972                 switch(w->id) {
973                 case snd_soc_dapm_switch:
974                 case snd_soc_dapm_mixer:
975                         dapm_new_mixer(codec, w);
976                         break;
977                 case snd_soc_dapm_mux:
978                         dapm_new_mux(codec, w);
979                         break;
980                 case snd_soc_dapm_adc:
981                 case snd_soc_dapm_dac:
982                 case snd_soc_dapm_pga:
983                         dapm_new_pga(codec, w);
984                         break;
985                 case snd_soc_dapm_input:
986                 case snd_soc_dapm_output:
987                 case snd_soc_dapm_micbias:
988                 case snd_soc_dapm_spk:
989                 case snd_soc_dapm_hp:
990                 case snd_soc_dapm_mic:
991                 case snd_soc_dapm_line:
992                 case snd_soc_dapm_vmid:
993                 case snd_soc_dapm_pre:
994                 case snd_soc_dapm_post:
995                         break;
996                 }
997                 w->new = 1;
998         }
999
1000         dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP);
1001         mutex_unlock(&codec->mutex);
1002         return 0;
1003 }
1004 EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets);
1005
1006 /**
1007  * snd_soc_dapm_get_volsw - dapm mixer get callback
1008  * @kcontrol: mixer control
1009  * @uinfo: control element information
1010  *
1011  * Callback to get the value of a dapm mixer control.
1012  *
1013  * Returns 0 for success.
1014  */
1015 int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
1016         struct snd_ctl_elem_value *ucontrol)
1017 {
1018         struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
1019         int reg = kcontrol->private_value & 0xff;
1020         int shift = (kcontrol->private_value >> 8) & 0x0f;
1021         int rshift = (kcontrol->private_value >> 12) & 0x0f;
1022         int max = (kcontrol->private_value >> 16) & 0xff;
1023         int invert = (kcontrol->private_value >> 24) & 0x01;
1024         int mask = (1 << fls(max)) - 1;
1025
1026         /* return the saved value if we are powered down */
1027         if (widget->id == snd_soc_dapm_pga && !widget->power) {
1028                 ucontrol->value.integer.value[0] = widget->saved_value;
1029                 return 0;
1030         }
1031
1032         ucontrol->value.integer.value[0] =
1033                 (snd_soc_read(widget->codec, reg) >> shift) & mask;
1034         if (shift != rshift)
1035                 ucontrol->value.integer.value[1] =
1036                         (snd_soc_read(widget->codec, reg) >> rshift) & mask;
1037         if (invert) {
1038                 ucontrol->value.integer.value[0] =
1039                         max - ucontrol->value.integer.value[0];
1040                 if (shift != rshift)
1041                         ucontrol->value.integer.value[1] =
1042                                 max - ucontrol->value.integer.value[1];
1043         }
1044
1045         return 0;
1046 }
1047 EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw);
1048
1049 /**
1050  * snd_soc_dapm_put_volsw - dapm mixer set callback
1051  * @kcontrol: mixer control
1052  * @uinfo: control element information
1053  *
1054  * Callback to set the value of a dapm mixer control.
1055  *
1056  * Returns 0 for success.
1057  */
1058 int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
1059         struct snd_ctl_elem_value *ucontrol)
1060 {
1061         struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
1062         int reg = kcontrol->private_value & 0xff;
1063         int shift = (kcontrol->private_value >> 8) & 0x0f;
1064         int rshift = (kcontrol->private_value >> 12) & 0x0f;
1065         int max = (kcontrol->private_value >> 16) & 0xff;
1066         int mask = (1 << fls(max)) - 1;
1067         int invert = (kcontrol->private_value >> 24) & 0x01;
1068         unsigned short val, val2, val_mask;
1069         int ret;
1070
1071         val = (ucontrol->value.integer.value[0] & mask);
1072
1073         if (invert)
1074                 val = max - val;
1075         val_mask = mask << shift;
1076         val = val << shift;
1077         if (shift != rshift) {
1078                 val2 = (ucontrol->value.integer.value[1] & mask);
1079                 if (invert)
1080                         val2 = max - val2;
1081                 val_mask |= mask << rshift;
1082                 val |= val2 << rshift;
1083         }
1084
1085         mutex_lock(&widget->codec->mutex);
1086         widget->value = val;
1087
1088         /* save volume value if the widget is powered down */
1089         if (widget->id == snd_soc_dapm_pga && !widget->power) {
1090                 widget->saved_value = val;
1091                 mutex_unlock(&widget->codec->mutex);
1092                 return 1;
1093         }
1094
1095         dapm_mixer_update_power(widget, kcontrol, reg, val_mask, val, invert);
1096         if (widget->event) {
1097                 if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
1098                         ret = widget->event(widget, SND_SOC_DAPM_PRE_REG);
1099                         if (ret < 0)
1100                                 goto out;
1101                 }
1102                 ret = snd_soc_update_bits(widget->codec, reg, val_mask, val);
1103                 if (widget->event_flags & SND_SOC_DAPM_POST_REG)
1104                         ret = widget->event(widget, SND_SOC_DAPM_POST_REG);
1105         } else
1106                 ret = snd_soc_update_bits(widget->codec, reg, val_mask, val);
1107
1108 out:
1109         mutex_unlock(&widget->codec->mutex);
1110         return ret;
1111 }
1112 EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw);
1113
1114 /**
1115  * snd_soc_dapm_get_enum_double - dapm enumerated double mixer get callback
1116  * @kcontrol: mixer control
1117  * @uinfo: control element information
1118  *
1119  * Callback to get the value of a dapm enumerated double mixer control.
1120  *
1121  * Returns 0 for success.
1122  */
1123 int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
1124         struct snd_ctl_elem_value *ucontrol)
1125 {
1126         struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
1127         struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
1128         unsigned short val, bitmask;
1129
1130         for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
1131                 ;
1132         val = snd_soc_read(widget->codec, e->reg);
1133         ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1);
1134         if (e->shift_l != e->shift_r)
1135                 ucontrol->value.enumerated.item[1] =
1136                         (val >> e->shift_r) & (bitmask - 1);
1137
1138         return 0;
1139 }
1140 EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double);
1141
1142 /**
1143  * snd_soc_dapm_put_enum_double - dapm enumerated double mixer set callback
1144  * @kcontrol: mixer control
1145  * @uinfo: control element information
1146  *
1147  * Callback to set the value of a dapm enumerated double mixer control.
1148  *
1149  * Returns 0 for success.
1150  */
1151 int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
1152         struct snd_ctl_elem_value *ucontrol)
1153 {
1154         struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
1155         struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
1156         unsigned short val, mux;
1157         unsigned short mask, bitmask;
1158         int ret = 0;
1159
1160         for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
1161                 ;
1162         if (ucontrol->value.enumerated.item[0] > e->mask - 1)
1163                 return -EINVAL;
1164         mux = ucontrol->value.enumerated.item[0];
1165         val = mux << e->shift_l;
1166         mask = (bitmask - 1) << e->shift_l;
1167         if (e->shift_l != e->shift_r) {
1168                 if (ucontrol->value.enumerated.item[1] > e->mask - 1)
1169                         return -EINVAL;
1170                 val |= ucontrol->value.enumerated.item[1] << e->shift_r;
1171                 mask |= (bitmask - 1) << e->shift_r;
1172         }
1173
1174         mutex_lock(&widget->codec->mutex);
1175         widget->value = val;
1176         dapm_mux_update_power(widget, kcontrol, mask, mux, e);
1177         if (widget->event) {
1178                 if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
1179                         ret = widget->event(widget, SND_SOC_DAPM_PRE_REG);
1180                         if (ret < 0)
1181                                 goto out;
1182                 }
1183                 ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
1184                 if (widget->event_flags & SND_SOC_DAPM_POST_REG)
1185                         ret = widget->event(widget, SND_SOC_DAPM_POST_REG);
1186         } else
1187                 ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
1188
1189 out:
1190         mutex_unlock(&widget->codec->mutex);
1191         return ret;
1192 }
1193 EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);
1194
1195 /**
1196  * snd_soc_dapm_new_control - create new dapm control
1197  * @codec: audio codec
1198  * @widget: widget template
1199  *
1200  * Creates a new dapm control based upon the template.
1201  *
1202  * Returns 0 for success else error.
1203  */
1204 int snd_soc_dapm_new_control(struct snd_soc_codec *codec,
1205         const struct snd_soc_dapm_widget *widget)
1206 {
1207         struct snd_soc_dapm_widget *w;
1208
1209         if ((w = dapm_cnew_widget(widget)) == NULL)
1210                 return -ENOMEM;
1211
1212         w->codec = codec;
1213         INIT_LIST_HEAD(&w->sources);
1214         INIT_LIST_HEAD(&w->sinks);
1215         INIT_LIST_HEAD(&w->list);
1216         list_add(&w->list, &codec->dapm_widgets);
1217
1218         /* machine layer set ups unconnected pins and insertions */
1219         w->connected = 1;
1220         return 0;
1221 }
1222 EXPORT_SYMBOL_GPL(snd_soc_dapm_new_control);
1223
1224 /**
1225  * snd_soc_dapm_stream_event - send a stream event to the dapm core
1226  * @codec: audio codec
1227  * @stream: stream name
1228  * @event: stream event
1229  *
1230  * Sends a stream event to the dapm core. The core then makes any
1231  * necessary widget power changes.
1232  *
1233  * Returns 0 for success else error.
1234  */
1235 int snd_soc_dapm_stream_event(struct snd_soc_codec *codec,
1236         char *stream, int event)
1237 {
1238         struct snd_soc_dapm_widget *w;
1239
1240         if (stream == NULL)
1241                 return 0;
1242
1243         mutex_lock(&codec->mutex);
1244         list_for_each_entry(w, &codec->dapm_widgets, list)
1245         {
1246                 if (!w->sname)
1247                         continue;
1248                 dbg("widget %s\n %s stream %s event %d\n", w->name, w->sname,
1249                         stream, event);
1250                 if (strstr(w->sname, stream)) {
1251                         switch(event) {
1252                         case SND_SOC_DAPM_STREAM_START:
1253                                 w->active = 1;
1254                                 break;
1255                         case SND_SOC_DAPM_STREAM_STOP:
1256                                 w->active = 0;
1257                                 break;
1258                         case SND_SOC_DAPM_STREAM_SUSPEND:
1259                                 if (w->active)
1260                                         w->suspend = 1;
1261                                 w->active = 0;
1262                                 break;
1263                         case SND_SOC_DAPM_STREAM_RESUME:
1264                                 if (w->suspend) {
1265                                         w->active = 1;
1266                                         w->suspend = 0;
1267                                 }
1268                                 break;
1269                         case SND_SOC_DAPM_STREAM_PAUSE_PUSH:
1270                                 break;
1271                         case SND_SOC_DAPM_STREAM_PAUSE_RELEASE:
1272                                 break;
1273                         }
1274                 }
1275         }
1276         mutex_unlock(&codec->mutex);
1277
1278         dapm_power_widgets(codec, event);
1279         dump_dapm(codec, __FUNCTION__);
1280         return 0;
1281 }
1282 EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event);
1283
1284 /**
1285  * snd_soc_dapm_device_event - send a device event to the dapm core
1286  * @socdev: audio device
1287  * @event: device event
1288  *
1289  * Sends a device event to the dapm core. The core then makes any
1290  * necessary machine or codec power changes..
1291  *
1292  * Returns 0 for success else error.
1293  */
1294 int snd_soc_dapm_device_event(struct snd_soc_device *socdev, int event)
1295 {
1296         struct snd_soc_codec *codec = socdev->codec;
1297         struct snd_soc_machine *machine = socdev->machine;
1298
1299         if (machine->dapm_event)
1300                                 machine->dapm_event(machine, event);
1301         if (codec->dapm_event)
1302                                 codec->dapm_event(codec, event);
1303         return 0;
1304 }
1305 EXPORT_SYMBOL_GPL(snd_soc_dapm_device_event);
1306
1307 /**
1308  * snd_soc_dapm_set_endpoint - set audio endpoint status
1309  * @codec: audio codec
1310  * @endpoint: audio signal endpoint (or start point)
1311  * @status: point status
1312  *
1313  * Set audio endpoint status - connected or disconnected.
1314  *
1315  * Returns 0 for success else error.
1316  */
1317 int snd_soc_dapm_set_endpoint(struct snd_soc_codec *codec,
1318         char *endpoint, int status)
1319 {
1320         struct snd_soc_dapm_widget *w;
1321
1322         list_for_each_entry(w, &codec->dapm_widgets, list) {
1323                 if (!strcmp(w->name, endpoint)) {
1324                         w->connected = status;
1325                 }
1326         }
1327
1328         return 0;
1329 }
1330 EXPORT_SYMBOL_GPL(snd_soc_dapm_set_endpoint);
1331
1332 /**
1333  * snd_soc_dapm_free - free dapm resources
1334  * @socdev: SoC device
1335  *
1336  * Free all dapm widgets and resources.
1337  */
1338 void snd_soc_dapm_free(struct snd_soc_device *socdev)
1339 {
1340         struct snd_soc_codec *codec = socdev->codec;
1341
1342         snd_soc_dapm_sys_remove(socdev->dev);
1343         dapm_free_widgets(codec);
1344 }
1345 EXPORT_SYMBOL_GPL(snd_soc_dapm_free);
1346
1347 /* Module information */
1348 MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
1349 MODULE_DESCRIPTION("Dynamic Audio Power Management core for ALSA SoC");
1350 MODULE_LICENSE("GPL");