mfld_machine.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. /*
  2. * mfld_machine.c - ASoc Machine driver for Intel Medfield MID platform
  3. *
  4. * Copyright (C) 2010 Intel Corp
  5. * Author: Vinod Koul <vinod.koul@intel.com>
  6. * Author: Harsha Priya <priya.harsha@intel.com>
  7. * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  8. *
  9. * This program is free software; you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License as published by
  11. * the Free Software Foundation; version 2 of the License.
  12. *
  13. * This program is distributed in the hope that it will be useful, but
  14. * WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License along
  19. * with this program; if not, write to the Free Software Foundation, Inc.,
  20. * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  21. *
  22. * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  23. */
  24. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  25. #include <linux/init.h>
  26. #include <linux/device.h>
  27. #include <linux/slab.h>
  28. #include <linux/io.h>
  29. #include <sound/pcm.h>
  30. #include <sound/pcm_params.h>
  31. #include <sound/soc.h>
  32. #include <sound/jack.h>
  33. #include "../codecs/sn95031.h"
  34. #define MID_MONO 1
  35. #define MID_STEREO 2
  36. #define MID_MAX_CAP 5
  37. #define MFLD_JACK_INSERT 0x04
  38. enum soc_mic_bias_zones {
  39. MFLD_MV_START = 0,
  40. /* mic bias volutage range for Headphones*/
  41. MFLD_MV_HP = 400,
  42. /* mic bias volutage range for American Headset*/
  43. MFLD_MV_AM_HS = 650,
  44. /* mic bias volutage range for Headset*/
  45. MFLD_MV_HS = 2000,
  46. MFLD_MV_UNDEFINED,
  47. };
  48. static unsigned int hs_switch;
  49. static unsigned int lo_dac;
  50. struct mfld_mc_private {
  51. struct platform_device *socdev;
  52. void __iomem *int_base;
  53. struct snd_soc_codec *codec;
  54. u8 interrupt_status;
  55. };
  56. struct snd_soc_jack mfld_jack;
  57. /*Headset jack detection DAPM pins */
  58. static struct snd_soc_jack_pin mfld_jack_pins[] = {
  59. {
  60. .pin = "Headphones",
  61. .mask = SND_JACK_HEADPHONE,
  62. },
  63. {
  64. .pin = "AMIC1",
  65. .mask = SND_JACK_MICROPHONE,
  66. },
  67. };
  68. /* sound card controls */
  69. static const char *headset_switch_text[] = {"Earpiece", "Headset"};
  70. static const char *lo_text[] = {"Vibra", "Headset", "IHF", "None"};
  71. static const struct soc_enum headset_enum =
  72. SOC_ENUM_SINGLE_EXT(2, headset_switch_text);
  73. static const struct soc_enum lo_enum =
  74. SOC_ENUM_SINGLE_EXT(4, lo_text);
  75. static int headset_get_switch(struct snd_kcontrol *kcontrol,
  76. struct snd_ctl_elem_value *ucontrol)
  77. {
  78. ucontrol->value.integer.value[0] = hs_switch;
  79. return 0;
  80. }
  81. static int headset_set_switch(struct snd_kcontrol *kcontrol,
  82. struct snd_ctl_elem_value *ucontrol)
  83. {
  84. struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
  85. if (ucontrol->value.integer.value[0] == hs_switch)
  86. return 0;
  87. if (ucontrol->value.integer.value[0]) {
  88. pr_debug("hs_set HS path\n");
  89. snd_soc_dapm_enable_pin(&codec->dapm, "Headphones");
  90. snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");
  91. } else {
  92. pr_debug("hs_set EP path\n");
  93. snd_soc_dapm_disable_pin(&codec->dapm, "Headphones");
  94. snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT");
  95. }
  96. snd_soc_dapm_sync(&codec->dapm);
  97. hs_switch = ucontrol->value.integer.value[0];
  98. return 0;
  99. }
  100. static void lo_enable_out_pins(struct snd_soc_codec *codec)
  101. {
  102. snd_soc_dapm_enable_pin(&codec->dapm, "IHFOUTL");
  103. snd_soc_dapm_enable_pin(&codec->dapm, "IHFOUTR");
  104. snd_soc_dapm_enable_pin(&codec->dapm, "LINEOUTL");
  105. snd_soc_dapm_enable_pin(&codec->dapm, "LINEOUTR");
  106. snd_soc_dapm_enable_pin(&codec->dapm, "VIB1OUT");
  107. snd_soc_dapm_enable_pin(&codec->dapm, "VIB2OUT");
  108. if (hs_switch) {
  109. snd_soc_dapm_enable_pin(&codec->dapm, "Headphones");
  110. snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");
  111. } else {
  112. snd_soc_dapm_disable_pin(&codec->dapm, "Headphones");
  113. snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT");
  114. }
  115. }
  116. static int lo_get_switch(struct snd_kcontrol *kcontrol,
  117. struct snd_ctl_elem_value *ucontrol)
  118. {
  119. ucontrol->value.integer.value[0] = lo_dac;
  120. return 0;
  121. }
  122. static int lo_set_switch(struct snd_kcontrol *kcontrol,
  123. struct snd_ctl_elem_value *ucontrol)
  124. {
  125. struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
  126. if (ucontrol->value.integer.value[0] == lo_dac)
  127. return 0;
  128. /* we dont want to work with last state of lineout so just enable all
  129. * pins and then disable pins not required
  130. */
  131. lo_enable_out_pins(codec);
  132. switch (ucontrol->value.integer.value[0]) {
  133. case 0:
  134. pr_debug("set vibra path\n");
  135. snd_soc_dapm_disable_pin(&codec->dapm, "VIB1OUT");
  136. snd_soc_dapm_disable_pin(&codec->dapm, "VIB2OUT");
  137. snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0);
  138. break;
  139. case 1:
  140. pr_debug("set hs path\n");
  141. snd_soc_dapm_disable_pin(&codec->dapm, "Headphones");
  142. snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");
  143. snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x22);
  144. break;
  145. case 2:
  146. pr_debug("set spkr path\n");
  147. snd_soc_dapm_disable_pin(&codec->dapm, "IHFOUTL");
  148. snd_soc_dapm_disable_pin(&codec->dapm, "IHFOUTR");
  149. snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x44);
  150. break;
  151. case 3:
  152. pr_debug("set null path\n");
  153. snd_soc_dapm_disable_pin(&codec->dapm, "LINEOUTL");
  154. snd_soc_dapm_disable_pin(&codec->dapm, "LINEOUTR");
  155. snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x66);
  156. break;
  157. }
  158. snd_soc_dapm_sync(&codec->dapm);
  159. lo_dac = ucontrol->value.integer.value[0];
  160. return 0;
  161. }
  162. static const struct snd_kcontrol_new mfld_snd_controls[] = {
  163. SOC_ENUM_EXT("Playback Switch", headset_enum,
  164. headset_get_switch, headset_set_switch),
  165. SOC_ENUM_EXT("Lineout Mux", lo_enum,
  166. lo_get_switch, lo_set_switch),
  167. };
  168. static const struct snd_soc_dapm_widget mfld_widgets[] = {
  169. SND_SOC_DAPM_HP("Headphones", NULL),
  170. SND_SOC_DAPM_MIC("Mic", NULL),
  171. };
  172. static const struct snd_soc_dapm_route mfld_map[] = {
  173. {"Headphones", NULL, "HPOUTR"},
  174. {"Headphones", NULL, "HPOUTL"},
  175. {"Mic", NULL, "AMIC1"},
  176. };
  177. static void mfld_jack_check(unsigned int intr_status)
  178. {
  179. struct mfld_jack_data jack_data;
  180. jack_data.mfld_jack = &mfld_jack;
  181. jack_data.intr_id = intr_status;
  182. sn95031_jack_detection(&jack_data);
  183. /* TODO: add american headset detection post gpiolib support */
  184. }
  185. static int mfld_init(struct snd_soc_pcm_runtime *runtime)
  186. {
  187. struct snd_soc_codec *codec = runtime->codec;
  188. struct snd_soc_dapm_context *dapm = &codec->dapm;
  189. int ret_val;
  190. /* Add jack sense widgets */
  191. snd_soc_dapm_new_controls(dapm, mfld_widgets, ARRAY_SIZE(mfld_widgets));
  192. /* Set up the map */
  193. snd_soc_dapm_add_routes(dapm, mfld_map, ARRAY_SIZE(mfld_map));
  194. /* always connected */
  195. snd_soc_dapm_enable_pin(dapm, "Headphones");
  196. snd_soc_dapm_enable_pin(dapm, "Mic");
  197. snd_soc_dapm_sync(dapm);
  198. ret_val = snd_soc_add_controls(codec, mfld_snd_controls,
  199. ARRAY_SIZE(mfld_snd_controls));
  200. if (ret_val) {
  201. pr_err("soc_add_controls failed %d", ret_val);
  202. return ret_val;
  203. }
  204. /* default is earpiece pin, userspace sets it explcitly */
  205. snd_soc_dapm_disable_pin(dapm, "Headphones");
  206. /* default is lineout NC, userspace sets it explcitly */
  207. snd_soc_dapm_disable_pin(dapm, "LINEOUTL");
  208. snd_soc_dapm_disable_pin(dapm, "LINEOUTR");
  209. lo_dac = 3;
  210. hs_switch = 0;
  211. /* we dont use linein in this so set to NC */
  212. snd_soc_dapm_disable_pin(dapm, "LINEINL");
  213. snd_soc_dapm_disable_pin(dapm, "LINEINR");
  214. snd_soc_dapm_sync(dapm);
  215. /* Headset and button jack detection */
  216. ret_val = snd_soc_jack_new(codec, "Intel(R) MID Audio Jack",
  217. SND_JACK_HEADSET | SND_JACK_BTN_0 |
  218. SND_JACK_BTN_1, &mfld_jack);
  219. if (ret_val) {
  220. pr_err("jack creation failed\n");
  221. return ret_val;
  222. }
  223. ret_val = snd_soc_jack_add_pins(&mfld_jack,
  224. ARRAY_SIZE(mfld_jack_pins), mfld_jack_pins);
  225. if (ret_val) {
  226. pr_err("adding jack pins failed\n");
  227. return ret_val;
  228. }
  229. /* we want to check if anything is inserted at boot,
  230. * so send a fake event to codec and it will read adc
  231. * to find if anything is there or not */
  232. mfld_jack_check(MFLD_JACK_INSERT);
  233. return ret_val;
  234. }
  235. struct snd_soc_dai_link mfld_msic_dailink[] = {
  236. {
  237. .name = "Medfield Headset",
  238. .stream_name = "Headset",
  239. .cpu_dai_name = "Headset-cpu-dai",
  240. .codec_dai_name = "SN95031 Headset",
  241. .codec_name = "sn95031",
  242. .platform_name = "sst-platform",
  243. .init = mfld_init,
  244. },
  245. {
  246. .name = "Medfield Speaker",
  247. .stream_name = "Speaker",
  248. .cpu_dai_name = "Speaker-cpu-dai",
  249. .codec_dai_name = "SN95031 Speaker",
  250. .codec_name = "sn95031",
  251. .platform_name = "sst-platform",
  252. .init = NULL,
  253. },
  254. {
  255. .name = "Medfield Vibra",
  256. .stream_name = "Vibra1",
  257. .cpu_dai_name = "Vibra1-cpu-dai",
  258. .codec_dai_name = "SN95031 Vibra1",
  259. .codec_name = "sn95031",
  260. .platform_name = "sst-platform",
  261. .init = NULL,
  262. },
  263. {
  264. .name = "Medfield Haptics",
  265. .stream_name = "Vibra2",
  266. .cpu_dai_name = "Vibra2-cpu-dai",
  267. .codec_dai_name = "SN95031 Vibra2",
  268. .codec_name = "sn95031",
  269. .platform_name = "sst-platform",
  270. .init = NULL,
  271. },
  272. };
  273. /* SoC card */
  274. static struct snd_soc_card snd_soc_card_mfld = {
  275. .name = "medfield_audio",
  276. .dai_link = mfld_msic_dailink,
  277. .num_links = ARRAY_SIZE(mfld_msic_dailink),
  278. };
  279. static irqreturn_t snd_mfld_jack_intr_handler(int irq, void *dev)
  280. {
  281. struct mfld_mc_private *mc_private = (struct mfld_mc_private *) dev;
  282. memcpy_fromio(&mc_private->interrupt_status,
  283. ((void *)(mc_private->int_base)),
  284. sizeof(u8));
  285. return IRQ_WAKE_THREAD;
  286. }
  287. static irqreturn_t snd_mfld_jack_detection(int irq, void *data)
  288. {
  289. struct mfld_mc_private *mc_drv_ctx = (struct mfld_mc_private *) data;
  290. if (mfld_jack.codec == NULL)
  291. return IRQ_HANDLED;
  292. mfld_jack_check(mc_drv_ctx->interrupt_status);
  293. return IRQ_HANDLED;
  294. }
  295. static int __devinit snd_mfld_mc_probe(struct platform_device *pdev)
  296. {
  297. int ret_val = 0, irq;
  298. struct mfld_mc_private *mc_drv_ctx;
  299. struct resource *irq_mem;
  300. pr_debug("snd_mfld_mc_probe called\n");
  301. /* retrive the irq number */
  302. irq = platform_get_irq(pdev, 0);
  303. /* audio interrupt base of SRAM location where
  304. * interrupts are stored by System FW */
  305. mc_drv_ctx = kzalloc(sizeof(*mc_drv_ctx), GFP_ATOMIC);
  306. if (!mc_drv_ctx) {
  307. pr_err("allocation failed\n");
  308. return -ENOMEM;
  309. }
  310. irq_mem = platform_get_resource_byname(
  311. pdev, IORESOURCE_MEM, "IRQ_BASE");
  312. if (!irq_mem) {
  313. pr_err("no mem resource given\n");
  314. ret_val = -ENODEV;
  315. goto unalloc;
  316. }
  317. mc_drv_ctx->int_base = ioremap_nocache(irq_mem->start,
  318. resource_size(irq_mem));
  319. if (!mc_drv_ctx->int_base) {
  320. pr_err("Mapping of cache failed\n");
  321. ret_val = -ENOMEM;
  322. goto unalloc;
  323. }
  324. /* register for interrupt */
  325. ret_val = request_threaded_irq(irq, snd_mfld_jack_intr_handler,
  326. snd_mfld_jack_detection,
  327. IRQF_SHARED, pdev->dev.driver->name, mc_drv_ctx);
  328. if (ret_val) {
  329. pr_err("cannot register IRQ\n");
  330. goto unalloc;
  331. }
  332. /* create soc device */
  333. mc_drv_ctx->socdev = platform_device_alloc("soc-audio", -1);
  334. if (!mc_drv_ctx->socdev) {
  335. pr_err("soc-audio device allocation failed\n");
  336. ret_val = -ENOMEM;
  337. goto freeirq;
  338. }
  339. platform_set_drvdata(mc_drv_ctx->socdev, &snd_soc_card_mfld);
  340. ret_val = platform_device_add(mc_drv_ctx->socdev);
  341. if (ret_val) {
  342. pr_err("Unable to add soc-audio device, err %d\n", ret_val);
  343. goto unregister;
  344. }
  345. platform_set_drvdata(pdev, mc_drv_ctx);
  346. pr_debug("successfully exited probe\n");
  347. return ret_val;
  348. unregister:
  349. platform_device_put(mc_drv_ctx->socdev);
  350. freeirq:
  351. free_irq(irq, mc_drv_ctx);
  352. unalloc:
  353. kfree(mc_drv_ctx);
  354. return ret_val;
  355. }
  356. static int __devexit snd_mfld_mc_remove(struct platform_device *pdev)
  357. {
  358. struct mfld_mc_private *mc_drv_ctx = platform_get_drvdata(pdev);
  359. pr_debug("snd_mfld_mc_remove called\n");
  360. free_irq(platform_get_irq(pdev, 0), mc_drv_ctx);
  361. platform_device_unregister(mc_drv_ctx->socdev);
  362. kfree(mc_drv_ctx);
  363. platform_set_drvdata(pdev, NULL);
  364. return 0;
  365. }
  366. static struct platform_driver snd_mfld_mc_driver = {
  367. .driver = {
  368. .owner = THIS_MODULE,
  369. .name = "msic_audio",
  370. },
  371. .probe = snd_mfld_mc_probe,
  372. .remove = __devexit_p(snd_mfld_mc_remove),
  373. };
  374. static int __init snd_mfld_driver_init(void)
  375. {
  376. pr_debug("snd_mfld_driver_init called\n");
  377. return platform_driver_register(&snd_mfld_mc_driver);
  378. }
  379. module_init(snd_mfld_driver_init);
  380. static void __exit snd_mfld_driver_exit(void)
  381. {
  382. pr_debug("snd_mfld_driver_exit called\n");
  383. platform_driver_unregister(&snd_mfld_mc_driver);
  384. }
  385. module_exit(snd_mfld_driver_exit);
  386. MODULE_DESCRIPTION("ASoC Intel(R) MID Machine driver");
  387. MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
  388. MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
  389. MODULE_LICENSE("GPL v2");
  390. MODULE_ALIAS("platform:msic-audio");