soc-dapm.c 34 KB

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