mfld_machine.c 12 KB

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