mfld_machine.c 12 KB

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