msm8660-apq-wm8903.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726
  1. /* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
  2. *
  3. * This program is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License version 2 and
  5. * only version 2 as published by the Free Software Foundation.
  6. *
  7. * This program is distributed in the hope that it will be useful,
  8. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. * GNU General Public License for more details.
  11. *
  12. */
  13. #include <linux/clk.h>
  14. #include <linux/err.h>
  15. #include <linux/gpio.h>
  16. #include <linux/mfd/pmic8058.h>
  17. #include <linux/mfd/pmic8901.h>
  18. #include <linux/platform_device.h>
  19. #include <linux/regulator/consumer.h>
  20. #include <linux/delay.h>
  21. #include <mach/mpp.h>
  22. #include <sound/core.h>
  23. #include <sound/soc.h>
  24. #include <sound/soc-dapm.h>
  25. #include <sound/soc-dsp.h>
  26. #include <sound/pcm.h>
  27. #include <asm/mach-types.h>
  28. #include "msm-pcm-routing.h"
  29. #include "../codecs/wm8903.h"
  30. #define MSM_GPIO_CLASS_D0_EN 80
  31. #define MSM_GPIO_CLASS_D1_EN 81
  32. #define MSM_CDC_MIC_I2S_MCLK 108
  33. static int msm8660_spk_func;
  34. static int msm8660_headset_func;
  35. static int msm8660_headphone_func;
  36. static struct clk *mic_bit_clk;
  37. static struct clk *spkr_osr_clk;
  38. static struct clk *spkr_bit_clk;
  39. static struct clk *wm8903_mclk;
  40. static int rx_hw_param_status;
  41. static int tx_hw_param_status;
  42. /* Platform specific logic */
  43. enum {
  44. GET_ERR,
  45. SET_ERR,
  46. ENABLE_ERR,
  47. NONE
  48. };
  49. enum {
  50. FUNC_OFF,
  51. FUNC_ON,
  52. };
  53. static struct wm8903_vdd {
  54. struct regulator *reg_id;
  55. const char *name;
  56. u32 voltage;
  57. } wm8903_vdds[] = {
  58. { NULL, "8058_l16", 1800000 },
  59. { NULL, "8058_l0", 1200000 },
  60. { NULL, "8058_s3", 1800000 },
  61. };
  62. static void classd_amp_pwr(int enable)
  63. {
  64. int rc;
  65. pr_debug("%s, enable = %d\n", __func__, enable);
  66. if (enable) {
  67. /* currently external PA isn't used for LINEOUTL */
  68. rc = gpio_request(MSM_GPIO_CLASS_D0_EN, "CLASSD0_EN");
  69. if (rc) {
  70. pr_err("%s: spkr PA gpio %d request failed\n",
  71. __func__, MSM_GPIO_CLASS_D0_EN);
  72. return;
  73. }
  74. gpio_direction_output(MSM_GPIO_CLASS_D0_EN, 1);
  75. gpio_set_value_cansleep(MSM_GPIO_CLASS_D0_EN, 1);
  76. rc = gpio_request(MSM_GPIO_CLASS_D1_EN, "CLASSD1_EN");
  77. if (rc) {
  78. pr_err("%s: spkr PA gpio %d request failed\n",
  79. __func__, MSM_GPIO_CLASS_D1_EN);
  80. return;
  81. }
  82. gpio_direction_output(MSM_GPIO_CLASS_D1_EN, 1);
  83. gpio_set_value_cansleep(MSM_GPIO_CLASS_D1_EN, 1);
  84. } else {
  85. gpio_set_value_cansleep(MSM_GPIO_CLASS_D0_EN, 0);
  86. gpio_free(MSM_GPIO_CLASS_D0_EN);
  87. gpio_set_value_cansleep(MSM_GPIO_CLASS_D1_EN, 0);
  88. gpio_free(MSM_GPIO_CLASS_D1_EN);
  89. }
  90. }
  91. static void extern_poweramp_on(void)
  92. {
  93. pr_debug("%s: enable stereo spkr amp\n", __func__);
  94. classd_amp_pwr(1);
  95. }
  96. static void extern_poweramp_off(void)
  97. {
  98. pr_debug("%s: disable stereo spkr amp\n", __func__);
  99. classd_amp_pwr(0);
  100. }
  101. static int msm8660_wm8903_powerup(void)
  102. {
  103. int rc = 0, index, stage = NONE;
  104. struct wm8903_vdd *vdd = NULL;
  105. for (index = 0; index < ARRAY_SIZE(wm8903_vdds); index++) {
  106. vdd = &wm8903_vdds[index];
  107. vdd->reg_id = regulator_get(NULL, vdd->name);
  108. if (IS_ERR(vdd->reg_id)) {
  109. pr_err("%s: Unable to get %s\n", __func__, vdd->name);
  110. stage = GET_ERR;
  111. rc = -ENODEV;
  112. break;
  113. }
  114. rc = regulator_set_voltage(vdd->reg_id,
  115. vdd->voltage, vdd->voltage);
  116. if (rc) {
  117. pr_err("%s: unable to set %s voltage to %dV\n",
  118. __func__, vdd->name, vdd->voltage);
  119. stage = SET_ERR;
  120. break;
  121. }
  122. rc = regulator_enable(vdd->reg_id);
  123. if (rc) {
  124. pr_err("%s:failed to enable %s\n", __func__, vdd->name);
  125. stage = ENABLE_ERR;
  126. break;
  127. }
  128. }
  129. if (index != ARRAY_SIZE(wm8903_vdds)) {
  130. if (stage != GET_ERR) {
  131. vdd = &wm8903_vdds[index];
  132. regulator_put(vdd->reg_id);
  133. vdd->reg_id = NULL;
  134. }
  135. while (index--) {
  136. vdd = &wm8903_vdds[index];
  137. regulator_disable(vdd->reg_id);
  138. regulator_put(vdd->reg_id);
  139. vdd->reg_id = NULL;
  140. }
  141. }
  142. return rc;
  143. }
  144. static void msm8660_wm8903_powerdown(void)
  145. {
  146. int index = ARRAY_SIZE(wm8903_vdds);
  147. struct wm8903_vdd *vdd = NULL;
  148. while (index--) {
  149. vdd = &wm8903_vdds[index];
  150. if (vdd->reg_id) {
  151. regulator_disable(vdd->reg_id);
  152. regulator_put(vdd->reg_id);
  153. }
  154. }
  155. }
  156. static int msm8660_wm8903_enable_mclk(int enable)
  157. {
  158. int ret = 0;
  159. if (enable) {
  160. ret = gpio_request(MSM_CDC_MIC_I2S_MCLK, "I2S_Clock");
  161. if (ret != 0) {
  162. pr_err("%s: failed to request GPIO\n", __func__);
  163. return ret;
  164. }
  165. wm8903_mclk = clk_get_sys(NULL, "i2s_mic_osr_clk");
  166. if (IS_ERR(wm8903_mclk)) {
  167. pr_err("Failed to get i2s_mic_osr_clk\n");
  168. gpio_free(MSM_CDC_MIC_I2S_MCLK);
  169. return IS_ERR(wm8903_mclk);
  170. }
  171. /* Master clock OSR 256 */
  172. clk_set_rate(wm8903_mclk, 48000 * 256);
  173. ret = clk_prepare_enable(wm8903_mclk);
  174. if (ret != 0) {
  175. pr_err("Unable to enable i2s_mic_osr_clk\n");
  176. gpio_free(MSM_CDC_MIC_I2S_MCLK);
  177. clk_put(wm8903_mclk);
  178. return ret;
  179. }
  180. } else {
  181. if (wm8903_mclk) {
  182. clk_disable_unprepare(wm8903_mclk);
  183. clk_put(wm8903_mclk);
  184. gpio_free(MSM_CDC_MIC_I2S_MCLK);
  185. wm8903_mclk = NULL;
  186. }
  187. }
  188. return ret;
  189. }
  190. static int msm8660_wm8903_prepare(void)
  191. {
  192. int ret = 0;
  193. ret = msm8660_wm8903_powerup();
  194. if (ret) {
  195. pr_err("Unable to powerup wm8903\n");
  196. return ret;
  197. }
  198. ret = msm8660_wm8903_enable_mclk(1);
  199. if (ret) {
  200. pr_err("Unable to enable mclk to wm8903\n");
  201. return ret;
  202. }
  203. return ret;
  204. }
  205. static void msm8660_wm8903_unprepare(void)
  206. {
  207. msm8660_wm8903_powerdown();
  208. msm8660_wm8903_enable_mclk(0);
  209. }
  210. static int msm8660_i2s_hw_params(struct snd_pcm_substream *substream,
  211. struct snd_pcm_hw_params *params)
  212. {
  213. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  214. struct snd_soc_dai *codec_dai = rtd->codec_dai;
  215. struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
  216. int rate = params_rate(params), ret = 0;
  217. pr_debug("Enter %s rate = %d\n", __func__, rate);
  218. if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
  219. if (rx_hw_param_status)
  220. return 0;
  221. /* wm8903 run @ LRC*256 */
  222. ret = snd_soc_dai_set_sysclk(codec_dai, 0, rate * 256,
  223. SND_SOC_CLOCK_IN);
  224. snd_soc_dai_digital_mute(codec_dai, 0);
  225. if (ret < 0) {
  226. pr_err("can't set rx codec clk configuration\n");
  227. return ret;
  228. }
  229. clk_set_rate(wm8903_mclk, rate * 256);
  230. /* set as slave mode CPU */
  231. clk_set_rate(spkr_bit_clk, 0);
  232. ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM);
  233. rx_hw_param_status++;
  234. } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
  235. if (tx_hw_param_status)
  236. return 0;
  237. clk_set_rate(wm8903_mclk, rate * 256);
  238. ret = snd_soc_dai_set_sysclk(codec_dai, 0, rate * 256,
  239. SND_SOC_CLOCK_IN);
  240. if (ret < 0) {
  241. pr_err("can't set tx codec clk configuration\n");
  242. return ret;
  243. }
  244. clk_set_rate(mic_bit_clk, 0);
  245. ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM);
  246. tx_hw_param_status++;
  247. }
  248. return 0;
  249. }
  250. static int msm8660_i2s_startup(struct snd_pcm_substream *substream)
  251. {
  252. int ret = 0;
  253. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  254. struct snd_soc_dai *codec_dai = rtd->codec_dai;
  255. struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
  256. pr_debug("Enter %s\n", __func__);
  257. /* ON Dragonboard, I2S between wm8903 and CPU is shared by
  258. * CODEC_SPEAKER and CODEC_MIC therefore CPU only can operate
  259. * as input SLAVE mode.
  260. */
  261. if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
  262. /* config WM8903 in Mater mode */
  263. ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_CBM_CFM |
  264. SND_SOC_DAIFMT_I2S);
  265. if (ret != 0) {
  266. pr_err("codec_dai set_fmt error\n");
  267. return ret;
  268. }
  269. /* config CPU in SLAVE mode */
  270. ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM);
  271. if (ret != 0) {
  272. pr_err("cpu_dai set_fmt error\n");
  273. return ret;
  274. }
  275. spkr_osr_clk = clk_get_sys(NULL, "i2s_spkr_osr_clk");
  276. if (IS_ERR(spkr_osr_clk)) {
  277. pr_err("Failed to get i2s_spkr_osr_clk\n");
  278. return PTR_ERR(spkr_osr_clk);
  279. }
  280. clk_set_rate(spkr_osr_clk, 48000 * 256);
  281. ret = clk_prepare_enable(spkr_osr_clk);
  282. if (ret != 0) {
  283. pr_err("Unable to enable i2s_spkr_osr_clk\n");
  284. clk_put(spkr_osr_clk);
  285. return ret;
  286. }
  287. spkr_bit_clk = clk_get_sys(NULL, "i2s_spkr_bit_clk");
  288. if (IS_ERR(spkr_bit_clk)) {
  289. pr_err("Failed to get i2s_spkr_bit_clk\n");
  290. clk_disable_unprepare(spkr_osr_clk);
  291. clk_put(spkr_osr_clk);
  292. return PTR_ERR(spkr_bit_clk);
  293. }
  294. clk_set_rate(spkr_bit_clk, 0);
  295. ret = clk_prepare_enable(spkr_bit_clk);
  296. if (ret != 0) {
  297. pr_err("Unable to enable i2s_spkr_bit_clk\n");
  298. clk_disable_unprepare(spkr_osr_clk);
  299. clk_put(spkr_osr_clk);
  300. clk_put(spkr_bit_clk);
  301. return ret;
  302. }
  303. } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
  304. /* config WM8903 in Mater mode */
  305. ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_CBM_CFM |
  306. SND_SOC_DAIFMT_I2S);
  307. if (ret != 0) {
  308. pr_err("codec_dai set_fmt error\n");
  309. return ret;
  310. }
  311. /* config CPU in SLAVE mode */
  312. ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM);
  313. if (ret != 0) {
  314. pr_err("codec_dai set_fmt error\n");
  315. return ret;
  316. }
  317. mic_bit_clk = clk_get_sys(NULL, "i2s_mic_bit_clk");
  318. if (IS_ERR(mic_bit_clk)) {
  319. pr_err("Failed to get i2s_mic_bit_clk\n");
  320. return PTR_ERR(mic_bit_clk);
  321. }
  322. clk_set_rate(mic_bit_clk, 0);
  323. ret = clk_prepare_enable(mic_bit_clk);
  324. if (ret != 0) {
  325. pr_err("Unable to enable i2s_mic_bit_clk\n");
  326. clk_put(mic_bit_clk);
  327. return ret;
  328. }
  329. }
  330. return ret;
  331. }
  332. static void msm8660_i2s_shutdown(struct snd_pcm_substream *substream)
  333. {
  334. pr_debug("Enter %s\n", __func__);
  335. if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
  336. substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
  337. tx_hw_param_status = 0;
  338. rx_hw_param_status = 0;
  339. if (spkr_bit_clk) {
  340. clk_disable_unprepare(spkr_bit_clk);
  341. clk_put(spkr_bit_clk);
  342. spkr_bit_clk = NULL;
  343. }
  344. if (spkr_osr_clk) {
  345. clk_disable_unprepare(spkr_osr_clk);
  346. clk_put(spkr_osr_clk);
  347. spkr_osr_clk = NULL;
  348. }
  349. if (mic_bit_clk) {
  350. clk_disable_unprepare(mic_bit_clk);
  351. clk_put(mic_bit_clk);
  352. mic_bit_clk = NULL;
  353. }
  354. }
  355. }
  356. static void msm8660_ext_control(struct snd_soc_codec *codec)
  357. {
  358. /* set the enpoints to their new connetion states */
  359. if (msm8660_spk_func == FUNC_ON)
  360. snd_soc_dapm_enable_pin(&codec->dapm, "Ext Spk");
  361. else
  362. snd_soc_dapm_disable_pin(&codec->dapm, "Ext Spk");
  363. /* set the enpoints to their new connetion states */
  364. if (msm8660_headset_func == FUNC_ON)
  365. snd_soc_dapm_enable_pin(&codec->dapm, "Headset Jack");
  366. else
  367. snd_soc_dapm_disable_pin(&codec->dapm, "Headset Jack");
  368. /* set the enpoints to their new connetion states */
  369. if (msm8660_headphone_func == FUNC_ON)
  370. snd_soc_dapm_enable_pin(&codec->dapm, "Headphone Jack");
  371. else
  372. snd_soc_dapm_disable_pin(&codec->dapm, "Headphone Jack");
  373. /* signal a DAPM event */
  374. snd_soc_dapm_sync(&codec->dapm);
  375. }
  376. static int msm8660_get_spk(struct snd_kcontrol *kcontrol,
  377. struct snd_ctl_elem_value *ucontrol)
  378. {
  379. ucontrol->value.integer.value[0] = msm8660_spk_func;
  380. return 0;
  381. }
  382. static int msm8660_set_spk(struct snd_kcontrol *kcontrol,
  383. struct snd_ctl_elem_value *ucontrol)
  384. {
  385. struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
  386. pr_debug("%s()\n", __func__);
  387. if (msm8660_spk_func == ucontrol->value.integer.value[0])
  388. return 0;
  389. msm8660_spk_func = ucontrol->value.integer.value[0];
  390. msm8660_ext_control(codec);
  391. return 1;
  392. }
  393. static int msm8660_get_hs(struct snd_kcontrol *kcontrol,
  394. struct snd_ctl_elem_value *ucontrol)
  395. {
  396. ucontrol->value.integer.value[0] = msm8660_headset_func;
  397. return 0;
  398. }
  399. static int msm8660_set_hs(struct snd_kcontrol *kcontrol,
  400. struct snd_ctl_elem_value *ucontrol)
  401. {
  402. struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
  403. pr_debug("%s()\n", __func__);
  404. if (msm8660_headset_func == ucontrol->value.integer.value[0])
  405. return 0;
  406. msm8660_headset_func = ucontrol->value.integer.value[0];
  407. msm8660_ext_control(codec);
  408. return 1;
  409. }
  410. static int msm8660_get_hph(struct snd_kcontrol *kcontrol,
  411. struct snd_ctl_elem_value *ucontrol)
  412. {
  413. ucontrol->value.integer.value[0] = msm8660_headphone_func;
  414. return 0;
  415. }
  416. static int msm8660_set_hph(struct snd_kcontrol *kcontrol,
  417. struct snd_ctl_elem_value *ucontrol)
  418. {
  419. struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
  420. pr_debug("%s()\n", __func__);
  421. if (msm8660_headphone_func == ucontrol->value.integer.value[0])
  422. return 0;
  423. msm8660_headphone_func = ucontrol->value.integer.value[0];
  424. msm8660_ext_control(codec);
  425. return 1;
  426. }
  427. static int msm8660_spkramp_event(struct snd_soc_dapm_widget *w,
  428. struct snd_kcontrol *k, int event)
  429. {
  430. if (SND_SOC_DAPM_EVENT_ON(event))
  431. extern_poweramp_on();
  432. else
  433. extern_poweramp_off();
  434. return 0;
  435. }
  436. static struct snd_soc_ops machine_ops = {
  437. .startup = msm8660_i2s_startup,
  438. .shutdown = msm8660_i2s_shutdown,
  439. .hw_params = msm8660_i2s_hw_params,
  440. };
  441. static const struct snd_soc_dapm_widget msm8660_dapm_widgets[] = {
  442. SND_SOC_DAPM_SPK("Ext Spk", msm8660_spkramp_event),
  443. SND_SOC_DAPM_MIC("Headset Jack", NULL),
  444. SND_SOC_DAPM_MIC("Headphone Jack", NULL),
  445. /* to fix a bug in wm8903.c, where audio doesn't function
  446. * after suspend/resume
  447. */
  448. SND_SOC_DAPM_SUPPLY("CLK_SYS_ENA", WM8903_CLOCK_RATES_2, 2, 0, NULL, 0),
  449. };
  450. static const struct snd_soc_dapm_route audio_map[] = {
  451. /* Match with wm8903 codec line out pin */
  452. {"Ext Spk", NULL, "LINEOUTL"},
  453. {"Ext Spk", NULL, "LINEOUTR"},
  454. /* Headset connects to IN3L with Bias */
  455. {"IN3L", NULL, "Mic Bias"},
  456. {"Mic Bias", NULL, "Headset Jack"},
  457. /* Headphone connects to IN3R with Bias */
  458. {"IN3R", NULL, "Mic Bias"},
  459. {"Mic Bias", NULL, "Headphone Jack"},
  460. {"ADCL", NULL, "CLK_SYS_ENA"},
  461. {"ADCR", NULL, "CLK_SYS_ENA"},
  462. {"DACL", NULL, "CLK_SYS_ENA"},
  463. {"DACR", NULL, "CLK_SYS_ENA"},
  464. };
  465. static const char *cmn_status[] = {"Off", "On"};
  466. static const struct soc_enum msm8660_enum[] = {
  467. SOC_ENUM_SINGLE_EXT(2, cmn_status),
  468. };
  469. static const struct snd_kcontrol_new wm8903_msm8660_controls[] = {
  470. SOC_ENUM_EXT("Speaker Function", msm8660_enum[0], msm8660_get_spk,
  471. msm8660_set_spk),
  472. SOC_ENUM_EXT("Headset Function", msm8660_enum[0], msm8660_get_hs,
  473. msm8660_set_hs),
  474. SOC_ENUM_EXT("Headphone Function", msm8660_enum[0], msm8660_get_hph,
  475. msm8660_set_hph),
  476. };
  477. static int msm8660_audrx_init(struct snd_soc_pcm_runtime *rtd)
  478. {
  479. struct snd_soc_codec *codec = rtd->codec;
  480. int err;
  481. snd_soc_dapm_disable_pin(&codec->dapm, "Ext Spk");
  482. snd_soc_dapm_enable_pin(&codec->dapm, "CLK_SYS_ENA");
  483. err = snd_soc_add_controls(codec, wm8903_msm8660_controls,
  484. ARRAY_SIZE(wm8903_msm8660_controls));
  485. if (err < 0)
  486. return err;
  487. snd_soc_dapm_new_controls(&codec->dapm, msm8660_dapm_widgets,
  488. ARRAY_SIZE(msm8660_dapm_widgets));
  489. snd_soc_dapm_add_routes(&codec->dapm, audio_map, ARRAY_SIZE(audio_map));
  490. snd_soc_dapm_sync(&codec->dapm);
  491. return 0;
  492. }
  493. static int pri_i2s_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
  494. struct snd_pcm_hw_params *params)
  495. {
  496. struct snd_interval *rate = hw_param_interval(params,
  497. SNDRV_PCM_HW_PARAM_RATE);
  498. rate->min = rate->max = 48000;
  499. return 0;
  500. }
  501. /*
  502. * LPA Needs only RX BE DAI links.
  503. * Hence define seperate BE list for lpa
  504. */
  505. static const char *lpa_mm_be[] = {
  506. LPASS_BE_PRI_I2S_RX,
  507. };
  508. static struct snd_soc_dsp_link lpa_fe_media = {
  509. .supported_be = lpa_mm_be,
  510. .num_be = ARRAY_SIZE(lpa_mm_be),
  511. .fe_playback_channels = 2,
  512. .fe_capture_channels = 1,
  513. .trigger = {
  514. SND_SOC_DSP_TRIGGER_POST,
  515. SND_SOC_DSP_TRIGGER_POST
  516. },
  517. };
  518. static const char *mm1_be[] = {
  519. LPASS_BE_PRI_I2S_RX,
  520. LPASS_BE_PRI_I2S_TX,
  521. LPASS_BE_HDMI,
  522. };
  523. static struct snd_soc_dsp_link fe_media = {
  524. .supported_be = mm1_be,
  525. .num_be = ARRAY_SIZE(mm1_be),
  526. .fe_playback_channels = 2,
  527. .fe_capture_channels = 1,
  528. .trigger = {
  529. SND_SOC_DSP_TRIGGER_POST, SND_SOC_DSP_TRIGGER_POST},
  530. };
  531. /* Digital audio interface glue - connects codec <---> CPU */
  532. static struct snd_soc_dai_link msm8660_dai[] = {
  533. /* FrontEnd DAI Links */
  534. {
  535. .name = "MSM8660 Media",
  536. .stream_name = "MultiMedia",
  537. .cpu_dai_name = "MultiMedia1",
  538. .platform_name = "msm-pcm-dsp",
  539. .dynamic = 1,
  540. .dsp_link = &fe_media,
  541. .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
  542. },
  543. {
  544. .name = "MSM8660 Media2",
  545. .stream_name = "MultiMedia2",
  546. .cpu_dai_name = "MultiMedia2",
  547. .platform_name = "msm-pcm-dsp",
  548. .dynamic = 1,
  549. .dsp_link = &fe_media,
  550. .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2,
  551. },
  552. /* Backend DAI Links */
  553. {
  554. .name = LPASS_BE_PRI_I2S_RX,
  555. .stream_name = "Primary I2S Playback",
  556. .cpu_dai_name = "msm-dai-q6.0",
  557. .platform_name = "msm-pcm-routing",
  558. .codec_name = "wm8903-codec.3-001a",
  559. .codec_dai_name = "wm8903-hifi",
  560. .no_pcm = 1,
  561. .be_hw_params_fixup = pri_i2s_be_hw_params_fixup,
  562. .ops = &machine_ops,
  563. .init = &msm8660_audrx_init,
  564. .be_id = MSM_BACKEND_DAI_PRI_I2S_RX
  565. },
  566. {
  567. .name = LPASS_BE_PRI_I2S_TX,
  568. .stream_name = "Primary I2S Capture",
  569. .cpu_dai_name = "msm-dai-q6.1",
  570. .platform_name = "msm-pcm-routing",
  571. .codec_name = "wm8903-codec.3-001a",
  572. .codec_dai_name = "wm8903-hifi",
  573. .no_pcm = 1,
  574. .ops = &machine_ops,
  575. .be_hw_params_fixup = pri_i2s_be_hw_params_fixup,
  576. .be_id = MSM_BACKEND_DAI_PRI_I2S_TX
  577. },
  578. /* LPA frontend DAI link*/
  579. {
  580. .name = "MSM8660 LPA",
  581. .stream_name = "LPA",
  582. .cpu_dai_name = "MultiMedia3",
  583. .platform_name = "msm-pcm-lpa",
  584. .dynamic = 1,
  585. .dsp_link = &lpa_fe_media,
  586. .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3,
  587. },
  588. /* HDMI backend DAI link */
  589. {
  590. .name = LPASS_BE_HDMI,
  591. .stream_name = "HDMI Playback",
  592. .cpu_dai_name = "msm-dai-q6.8",
  593. .platform_name = "msm-pcm-routing",
  594. .codec_name = "msm-stub-codec.1",
  595. .codec_dai_name = "msm-stub-rx",
  596. .no_codec = 1,
  597. .no_pcm = 1,
  598. .be_hw_params_fixup = pri_i2s_be_hw_params_fixup,
  599. .be_id = MSM_BACKEND_DAI_HDMI_RX
  600. },
  601. };
  602. struct snd_soc_card snd_soc_card_msm8660 = {
  603. .name = "msm8660-snd-card",
  604. .dai_link = msm8660_dai,
  605. .num_links = ARRAY_SIZE(msm8660_dai),
  606. };
  607. static struct platform_device *msm_snd_device;
  608. static int __init msm_audio_init(void)
  609. {
  610. int ret = 0;
  611. if (machine_is_msm8x60_dragon()) {
  612. /* wm8903 audio codec needs to power up and mclk existing
  613. before it's probed */
  614. ret = msm8660_wm8903_prepare();
  615. if (ret) {
  616. pr_err("failed to prepare wm8903 audio codec\n");
  617. return ret;
  618. }
  619. msm_snd_device = platform_device_alloc("soc-audio", 0);
  620. if (!msm_snd_device) {
  621. pr_err("Platform device allocation failed\n");
  622. msm8660_wm8903_unprepare();
  623. return -ENOMEM;
  624. }
  625. platform_set_drvdata(msm_snd_device, &snd_soc_card_msm8660);
  626. ret = platform_device_add(msm_snd_device);
  627. if (ret) {
  628. platform_device_put(msm_snd_device);
  629. msm8660_wm8903_unprepare();
  630. return ret;
  631. }
  632. }
  633. return ret;
  634. }
  635. module_init(msm_audio_init);
  636. static void __exit msm_audio_exit(void)
  637. {
  638. msm8660_wm8903_unprepare();
  639. platform_device_unregister(msm_snd_device);
  640. }
  641. module_exit(msm_audio_exit);
  642. MODULE_DESCRIPTION("ALSA SoC MSM8660");
  643. MODULE_LICENSE("GPL v2");