soc-dapm.c 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326
  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. if (p->connect)
  575. printk(" out %s %s\n", p->name ? p->name : "static",
  576. p->sink->name);
  577. }
  578. }
  579. break;
  580. }
  581. }
  582. }
  583. #endif
  584. /* test and update the power status of a mux widget */
  585. static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
  586. struct snd_kcontrol *kcontrol, int mask,
  587. int val, struct soc_enum* e)
  588. {
  589. struct snd_soc_dapm_path *path;
  590. int found = 0;
  591. if (widget->id != snd_soc_dapm_mux)
  592. return -ENODEV;
  593. if (!snd_soc_test_bits(widget->codec, e->reg, mask, val))
  594. return 0;
  595. /* find dapm widget path assoc with kcontrol */
  596. list_for_each_entry(path, &widget->codec->dapm_paths, list) {
  597. if (path->kcontrol != kcontrol)
  598. continue;
  599. if (!path->name || ! e->texts[val])
  600. continue;
  601. found = 1;
  602. /* we now need to match the string in the enum to the path */
  603. if (!(strcmp(path->name, e->texts[val])))
  604. path->connect = 1; /* new connection */
  605. else
  606. path->connect = 0; /* old connection must be powered down */
  607. }
  608. if (found)
  609. dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP);
  610. return 0;
  611. }
  612. /* test and update the power status of a mixer widget */
  613. static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
  614. struct snd_kcontrol *kcontrol, int reg,
  615. int val_mask, int val, int invert)
  616. {
  617. struct snd_soc_dapm_path *path;
  618. int found = 0;
  619. if (widget->id != snd_soc_dapm_mixer)
  620. return -ENODEV;
  621. if (!snd_soc_test_bits(widget->codec, reg, val_mask, val))
  622. return 0;
  623. /* find dapm widget path assoc with kcontrol */
  624. list_for_each_entry(path, &widget->codec->dapm_paths, list) {
  625. if (path->kcontrol != kcontrol)
  626. continue;
  627. /* found, now check type */
  628. found = 1;
  629. if (val)
  630. /* new connection */
  631. path->connect = invert ? 0:1;
  632. else
  633. /* old connection must be powered down */
  634. path->connect = invert ? 1:0;
  635. break;
  636. }
  637. if (found)
  638. dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP);
  639. return 0;
  640. }
  641. /* show dapm widget status in sys fs */
  642. static ssize_t dapm_widget_show(struct device *dev,
  643. struct device_attribute *attr, char *buf)
  644. {
  645. struct snd_soc_device *devdata = dev_get_drvdata(dev);
  646. struct snd_soc_codec *codec = devdata->codec;
  647. struct snd_soc_dapm_widget *w;
  648. int count = 0;
  649. char *state = "not set";
  650. list_for_each_entry(w, &codec->dapm_widgets, list) {
  651. /* only display widgets that burnm power */
  652. switch (w->id) {
  653. case snd_soc_dapm_hp:
  654. case snd_soc_dapm_mic:
  655. case snd_soc_dapm_spk:
  656. case snd_soc_dapm_line:
  657. case snd_soc_dapm_micbias:
  658. case snd_soc_dapm_dac:
  659. case snd_soc_dapm_adc:
  660. case snd_soc_dapm_pga:
  661. case snd_soc_dapm_mixer:
  662. if (w->name)
  663. count += sprintf(buf + count, "%s: %s\n",
  664. w->name, w->power ? "On":"Off");
  665. break;
  666. default:
  667. break;
  668. }
  669. }
  670. switch(codec->dapm_state){
  671. case SNDRV_CTL_POWER_D0:
  672. state = "D0";
  673. break;
  674. case SNDRV_CTL_POWER_D1:
  675. state = "D1";
  676. break;
  677. case SNDRV_CTL_POWER_D2:
  678. state = "D2";
  679. break;
  680. case SNDRV_CTL_POWER_D3hot:
  681. state = "D3hot";
  682. break;
  683. case SNDRV_CTL_POWER_D3cold:
  684. state = "D3cold";
  685. break;
  686. }
  687. count += sprintf(buf + count, "PM State: %s\n", state);
  688. return count;
  689. }
  690. static DEVICE_ATTR(dapm_widget, 0444, dapm_widget_show, NULL);
  691. int snd_soc_dapm_sys_add(struct device *dev)
  692. {
  693. int ret = 0;
  694. if (dapm_status)
  695. ret = device_create_file(dev, &dev_attr_dapm_widget);
  696. return ret;
  697. }
  698. static void snd_soc_dapm_sys_remove(struct device *dev)
  699. {
  700. if (dapm_status)
  701. device_remove_file(dev, &dev_attr_dapm_widget);
  702. }
  703. /* free all dapm widgets and resources */
  704. static void dapm_free_widgets(struct snd_soc_codec *codec)
  705. {
  706. struct snd_soc_dapm_widget *w, *next_w;
  707. struct snd_soc_dapm_path *p, *next_p;
  708. list_for_each_entry_safe(w, next_w, &codec->dapm_widgets, list) {
  709. list_del(&w->list);
  710. kfree(w);
  711. }
  712. list_for_each_entry_safe(p, next_p, &codec->dapm_paths, list) {
  713. list_del(&p->list);
  714. kfree(p->long_name);
  715. kfree(p);
  716. }
  717. }
  718. /**
  719. * snd_soc_dapm_sync_endpoints - scan and power dapm paths
  720. * @codec: audio codec
  721. *
  722. * Walks all dapm audio paths and powers widgets according to their
  723. * stream or path usage.
  724. *
  725. * Returns 0 for success.
  726. */
  727. int snd_soc_dapm_sync_endpoints(struct snd_soc_codec *codec)
  728. {
  729. return dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP);
  730. }
  731. EXPORT_SYMBOL_GPL(snd_soc_dapm_sync_endpoints);
  732. /**
  733. * snd_soc_dapm_connect_input - connect dapm widgets
  734. * @codec: audio codec
  735. * @sink: name of target widget
  736. * @control: mixer control name
  737. * @source: name of source name
  738. *
  739. * Connects 2 dapm widgets together via a named audio path. The sink is
  740. * the widget receiving the audio signal, whilst the source is the sender
  741. * of the audio signal.
  742. *
  743. * Returns 0 for success else error.
  744. */
  745. int snd_soc_dapm_connect_input(struct snd_soc_codec *codec, const char *sink,
  746. const char * control, const char *source)
  747. {
  748. struct snd_soc_dapm_path *path;
  749. struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w;
  750. int ret = 0;
  751. /* find src and dest widgets */
  752. list_for_each_entry(w, &codec->dapm_widgets, list) {
  753. if (!wsink && !(strcmp(w->name, sink))) {
  754. wsink = w;
  755. continue;
  756. }
  757. if (!wsource && !(strcmp(w->name, source))) {
  758. wsource = w;
  759. }
  760. }
  761. if (wsource == NULL || wsink == NULL)
  762. return -ENODEV;
  763. path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL);
  764. if (!path)
  765. return -ENOMEM;
  766. path->source = wsource;
  767. path->sink = wsink;
  768. INIT_LIST_HEAD(&path->list);
  769. INIT_LIST_HEAD(&path->list_source);
  770. INIT_LIST_HEAD(&path->list_sink);
  771. /* check for external widgets */
  772. if (wsink->id == snd_soc_dapm_input) {
  773. if (wsource->id == snd_soc_dapm_micbias ||
  774. wsource->id == snd_soc_dapm_mic ||
  775. wsink->id == snd_soc_dapm_line)
  776. wsink->ext = 1;
  777. }
  778. if (wsource->id == snd_soc_dapm_output) {
  779. if (wsink->id == snd_soc_dapm_spk ||
  780. wsink->id == snd_soc_dapm_hp ||
  781. wsink->id == snd_soc_dapm_line)
  782. wsource->ext = 1;
  783. }
  784. /* connect static paths */
  785. if (control == NULL) {
  786. list_add(&path->list, &codec->dapm_paths);
  787. list_add(&path->list_sink, &wsink->sources);
  788. list_add(&path->list_source, &wsource->sinks);
  789. path->connect = 1;
  790. return 0;
  791. }
  792. /* connect dynamic paths */
  793. switch(wsink->id) {
  794. case snd_soc_dapm_adc:
  795. case snd_soc_dapm_dac:
  796. case snd_soc_dapm_pga:
  797. case snd_soc_dapm_input:
  798. case snd_soc_dapm_output:
  799. case snd_soc_dapm_micbias:
  800. case snd_soc_dapm_vmid:
  801. case snd_soc_dapm_pre:
  802. case snd_soc_dapm_post:
  803. list_add(&path->list, &codec->dapm_paths);
  804. list_add(&path->list_sink, &wsink->sources);
  805. list_add(&path->list_source, &wsource->sinks);
  806. path->connect = 1;
  807. return 0;
  808. case snd_soc_dapm_mux:
  809. ret = dapm_connect_mux(codec, wsource, wsink, path, control,
  810. &wsink->kcontrols[0]);
  811. if (ret != 0)
  812. goto err;
  813. break;
  814. case snd_soc_dapm_switch:
  815. case snd_soc_dapm_mixer:
  816. ret = dapm_connect_mixer(codec, wsource, wsink, path, control);
  817. if (ret != 0)
  818. goto err;
  819. break;
  820. case snd_soc_dapm_hp:
  821. case snd_soc_dapm_mic:
  822. case snd_soc_dapm_line:
  823. case snd_soc_dapm_spk:
  824. list_add(&path->list, &codec->dapm_paths);
  825. list_add(&path->list_sink, &wsink->sources);
  826. list_add(&path->list_source, &wsource->sinks);
  827. path->connect = 0;
  828. return 0;
  829. }
  830. return 0;
  831. err:
  832. printk(KERN_WARNING "asoc: no dapm match for %s --> %s --> %s\n", source,
  833. control, sink);
  834. kfree(path);
  835. return ret;
  836. }
  837. EXPORT_SYMBOL_GPL(snd_soc_dapm_connect_input);
  838. /**
  839. * snd_soc_dapm_new_widgets - add new dapm widgets
  840. * @codec: audio codec
  841. *
  842. * Checks the codec for any new dapm widgets and creates them if found.
  843. *
  844. * Returns 0 for success.
  845. */
  846. int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec)
  847. {
  848. struct snd_soc_dapm_widget *w;
  849. mutex_lock(&codec->mutex);
  850. list_for_each_entry(w, &codec->dapm_widgets, list)
  851. {
  852. if (w->new)
  853. continue;
  854. switch(w->id) {
  855. case snd_soc_dapm_switch:
  856. case snd_soc_dapm_mixer:
  857. dapm_new_mixer(codec, w);
  858. break;
  859. case snd_soc_dapm_mux:
  860. dapm_new_mux(codec, w);
  861. break;
  862. case snd_soc_dapm_adc:
  863. case snd_soc_dapm_dac:
  864. case snd_soc_dapm_pga:
  865. dapm_new_pga(codec, w);
  866. break;
  867. case snd_soc_dapm_input:
  868. case snd_soc_dapm_output:
  869. case snd_soc_dapm_micbias:
  870. case snd_soc_dapm_spk:
  871. case snd_soc_dapm_hp:
  872. case snd_soc_dapm_mic:
  873. case snd_soc_dapm_line:
  874. case snd_soc_dapm_vmid:
  875. case snd_soc_dapm_pre:
  876. case snd_soc_dapm_post:
  877. break;
  878. }
  879. w->new = 1;
  880. }
  881. dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP);
  882. mutex_unlock(&codec->mutex);
  883. return 0;
  884. }
  885. EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets);
  886. /**
  887. * snd_soc_dapm_get_volsw - dapm mixer get callback
  888. * @kcontrol: mixer control
  889. * @uinfo: control element information
  890. *
  891. * Callback to get the value of a dapm mixer control.
  892. *
  893. * Returns 0 for success.
  894. */
  895. int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
  896. struct snd_ctl_elem_value *ucontrol)
  897. {
  898. struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
  899. int reg = kcontrol->private_value & 0xff;
  900. int shift = (kcontrol->private_value >> 8) & 0x0f;
  901. int rshift = (kcontrol->private_value >> 12) & 0x0f;
  902. int mask = (kcontrol->private_value >> 16) & 0xff;
  903. int invert = (kcontrol->private_value >> 24) & 0x01;
  904. /* return the saved value if we are powered down */
  905. if (widget->id == snd_soc_dapm_pga && !widget->power) {
  906. ucontrol->value.integer.value[0] = widget->saved_value;
  907. return 0;
  908. }
  909. ucontrol->value.integer.value[0] =
  910. (snd_soc_read(widget->codec, reg) >> shift) & mask;
  911. if (shift != rshift)
  912. ucontrol->value.integer.value[1] =
  913. (snd_soc_read(widget->codec, reg) >> rshift) & mask;
  914. if (invert) {
  915. ucontrol->value.integer.value[0] =
  916. mask - ucontrol->value.integer.value[0];
  917. if (shift != rshift)
  918. ucontrol->value.integer.value[1] =
  919. mask - ucontrol->value.integer.value[1];
  920. }
  921. return 0;
  922. }
  923. EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw);
  924. /**
  925. * snd_soc_dapm_put_volsw - dapm mixer set callback
  926. * @kcontrol: mixer control
  927. * @uinfo: control element information
  928. *
  929. * Callback to set the value of a dapm mixer control.
  930. *
  931. * Returns 0 for success.
  932. */
  933. int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
  934. struct snd_ctl_elem_value *ucontrol)
  935. {
  936. struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
  937. int reg = kcontrol->private_value & 0xff;
  938. int shift = (kcontrol->private_value >> 8) & 0x0f;
  939. int rshift = (kcontrol->private_value >> 12) & 0x0f;
  940. int mask = (kcontrol->private_value >> 16) & 0xff;
  941. int invert = (kcontrol->private_value >> 24) & 0x01;
  942. unsigned short val, val2, val_mask;
  943. int ret;
  944. val = (ucontrol->value.integer.value[0] & mask);
  945. if (invert)
  946. val = mask - val;
  947. val_mask = mask << shift;
  948. val = val << shift;
  949. if (shift != rshift) {
  950. val2 = (ucontrol->value.integer.value[1] & mask);
  951. if (invert)
  952. val2 = mask - val2;
  953. val_mask |= mask << rshift;
  954. val |= val2 << rshift;
  955. }
  956. mutex_lock(&widget->codec->mutex);
  957. widget->value = val;
  958. /* save volume value if the widget is powered down */
  959. if (widget->id == snd_soc_dapm_pga && !widget->power) {
  960. widget->saved_value = val;
  961. mutex_unlock(&widget->codec->mutex);
  962. return 1;
  963. }
  964. dapm_mixer_update_power(widget, kcontrol, reg, val_mask, val, invert);
  965. if (widget->event) {
  966. if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
  967. ret = widget->event(widget, SND_SOC_DAPM_PRE_REG);
  968. if (ret < 0)
  969. goto out;
  970. }
  971. ret = snd_soc_update_bits(widget->codec, reg, val_mask, val);
  972. if (widget->event_flags & SND_SOC_DAPM_POST_REG)
  973. ret = widget->event(widget, SND_SOC_DAPM_POST_REG);
  974. } else
  975. ret = snd_soc_update_bits(widget->codec, reg, val_mask, val);
  976. out:
  977. mutex_unlock(&widget->codec->mutex);
  978. return ret;
  979. }
  980. EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw);
  981. /**
  982. * snd_soc_dapm_get_enum_double - dapm enumerated double mixer get callback
  983. * @kcontrol: mixer control
  984. * @uinfo: control element information
  985. *
  986. * Callback to get the value of a dapm enumerated double mixer control.
  987. *
  988. * Returns 0 for success.
  989. */
  990. int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
  991. struct snd_ctl_elem_value *ucontrol)
  992. {
  993. struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
  994. struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
  995. unsigned short val, bitmask;
  996. for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
  997. ;
  998. val = snd_soc_read(widget->codec, e->reg);
  999. ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1);
  1000. if (e->shift_l != e->shift_r)
  1001. ucontrol->value.enumerated.item[1] =
  1002. (val >> e->shift_r) & (bitmask - 1);
  1003. return 0;
  1004. }
  1005. EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double);
  1006. /**
  1007. * snd_soc_dapm_put_enum_double - dapm enumerated double mixer set callback
  1008. * @kcontrol: mixer control
  1009. * @uinfo: control element information
  1010. *
  1011. * Callback to set the value of a dapm enumerated double mixer control.
  1012. *
  1013. * Returns 0 for success.
  1014. */
  1015. int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
  1016. struct snd_ctl_elem_value *ucontrol)
  1017. {
  1018. struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
  1019. struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
  1020. unsigned short val, mux;
  1021. unsigned short mask, bitmask;
  1022. int ret = 0;
  1023. for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
  1024. ;
  1025. if (ucontrol->value.enumerated.item[0] > e->mask - 1)
  1026. return -EINVAL;
  1027. mux = ucontrol->value.enumerated.item[0];
  1028. val = mux << e->shift_l;
  1029. mask = (bitmask - 1) << e->shift_l;
  1030. if (e->shift_l != e->shift_r) {
  1031. if (ucontrol->value.enumerated.item[1] > e->mask - 1)
  1032. return -EINVAL;
  1033. val |= ucontrol->value.enumerated.item[1] << e->shift_r;
  1034. mask |= (bitmask - 1) << e->shift_r;
  1035. }
  1036. mutex_lock(&widget->codec->mutex);
  1037. widget->value = val;
  1038. dapm_mux_update_power(widget, kcontrol, mask, mux, e);
  1039. if (widget->event) {
  1040. if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
  1041. ret = widget->event(widget, SND_SOC_DAPM_PRE_REG);
  1042. if (ret < 0)
  1043. goto out;
  1044. }
  1045. ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
  1046. if (widget->event_flags & SND_SOC_DAPM_POST_REG)
  1047. ret = widget->event(widget, SND_SOC_DAPM_POST_REG);
  1048. } else
  1049. ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
  1050. out:
  1051. mutex_unlock(&widget->codec->mutex);
  1052. return ret;
  1053. }
  1054. EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);
  1055. /**
  1056. * snd_soc_dapm_new_control - create new dapm control
  1057. * @codec: audio codec
  1058. * @widget: widget template
  1059. *
  1060. * Creates a new dapm control based upon the template.
  1061. *
  1062. * Returns 0 for success else error.
  1063. */
  1064. int snd_soc_dapm_new_control(struct snd_soc_codec *codec,
  1065. const struct snd_soc_dapm_widget *widget)
  1066. {
  1067. struct snd_soc_dapm_widget *w;
  1068. if ((w = dapm_cnew_widget(widget)) == NULL)
  1069. return -ENOMEM;
  1070. w->codec = codec;
  1071. INIT_LIST_HEAD(&w->sources);
  1072. INIT_LIST_HEAD(&w->sinks);
  1073. INIT_LIST_HEAD(&w->list);
  1074. list_add(&w->list, &codec->dapm_widgets);
  1075. /* machine layer set ups unconnected pins and insertions */
  1076. w->connected = 1;
  1077. return 0;
  1078. }
  1079. EXPORT_SYMBOL_GPL(snd_soc_dapm_new_control);
  1080. /**
  1081. * snd_soc_dapm_stream_event - send a stream event to the dapm core
  1082. * @codec: audio codec
  1083. * @stream: stream name
  1084. * @event: stream event
  1085. *
  1086. * Sends a stream event to the dapm core. The core then makes any
  1087. * necessary widget power changes.
  1088. *
  1089. * Returns 0 for success else error.
  1090. */
  1091. int snd_soc_dapm_stream_event(struct snd_soc_codec *codec,
  1092. char *stream, int event)
  1093. {
  1094. struct snd_soc_dapm_widget *w;
  1095. mutex_lock(&codec->mutex);
  1096. list_for_each_entry(w, &codec->dapm_widgets, list)
  1097. {
  1098. if (!w->sname)
  1099. continue;
  1100. dbg("widget %s\n %s stream %s event %d\n", w->name, w->sname,
  1101. stream, event);
  1102. if (strstr(w->sname, stream)) {
  1103. switch(event) {
  1104. case SND_SOC_DAPM_STREAM_START:
  1105. w->active = 1;
  1106. break;
  1107. case SND_SOC_DAPM_STREAM_STOP:
  1108. w->active = 0;
  1109. break;
  1110. case SND_SOC_DAPM_STREAM_SUSPEND:
  1111. if (w->active)
  1112. w->suspend = 1;
  1113. w->active = 0;
  1114. break;
  1115. case SND_SOC_DAPM_STREAM_RESUME:
  1116. if (w->suspend) {
  1117. w->active = 1;
  1118. w->suspend = 0;
  1119. }
  1120. break;
  1121. case SND_SOC_DAPM_STREAM_PAUSE_PUSH:
  1122. break;
  1123. case SND_SOC_DAPM_STREAM_PAUSE_RELEASE:
  1124. break;
  1125. }
  1126. }
  1127. }
  1128. mutex_unlock(&codec->mutex);
  1129. dapm_power_widgets(codec, event);
  1130. dump_dapm(codec, __FUNCTION__);
  1131. return 0;
  1132. }
  1133. EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event);
  1134. /**
  1135. * snd_soc_dapm_set_endpoint - set audio endpoint status
  1136. * @codec: audio codec
  1137. * @endpoint: audio signal endpoint (or start point)
  1138. * @status: point status
  1139. *
  1140. * Set audio endpoint status - connected or disconnected.
  1141. *
  1142. * Returns 0 for success else error.
  1143. */
  1144. int snd_soc_dapm_set_endpoint(struct snd_soc_codec *codec,
  1145. char *endpoint, int status)
  1146. {
  1147. struct snd_soc_dapm_widget *w;
  1148. list_for_each_entry(w, &codec->dapm_widgets, list) {
  1149. if (!strcmp(w->name, endpoint)) {
  1150. w->connected = status;
  1151. }
  1152. }
  1153. return 0;
  1154. }
  1155. EXPORT_SYMBOL_GPL(snd_soc_dapm_set_endpoint);
  1156. /**
  1157. * snd_soc_dapm_free - free dapm resources
  1158. * @socdev: SoC device
  1159. *
  1160. * Free all dapm widgets and resources.
  1161. */
  1162. void snd_soc_dapm_free(struct snd_soc_device *socdev)
  1163. {
  1164. struct snd_soc_codec *codec = socdev->codec;
  1165. snd_soc_dapm_sys_remove(socdev->dev);
  1166. dapm_free_widgets(codec);
  1167. }
  1168. EXPORT_SYMBOL_GPL(snd_soc_dapm_free);
  1169. /* Module information */
  1170. MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
  1171. MODULE_DESCRIPTION("Dynamic Audio Power Management core for ALSA SoC");
  1172. MODULE_LICENSE("GPL");