extcon-arizona.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  1. /*
  2. * extcon-arizona.c - Extcon driver Wolfson Arizona devices
  3. *
  4. * Copyright (C) 2012 Wolfson Microelectronics plc
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. */
  16. #include <linux/kernel.h>
  17. #include <linux/module.h>
  18. #include <linux/i2c.h>
  19. #include <linux/slab.h>
  20. #include <linux/interrupt.h>
  21. #include <linux/err.h>
  22. #include <linux/gpio.h>
  23. #include <linux/input.h>
  24. #include <linux/platform_device.h>
  25. #include <linux/pm_runtime.h>
  26. #include <linux/regulator/consumer.h>
  27. #include <linux/extcon.h>
  28. #include <linux/mfd/arizona/core.h>
  29. #include <linux/mfd/arizona/pdata.h>
  30. #include <linux/mfd/arizona/registers.h>
  31. #define ARIZONA_NUM_BUTTONS 6
  32. struct arizona_extcon_info {
  33. struct device *dev;
  34. struct arizona *arizona;
  35. struct mutex lock;
  36. struct regulator *micvdd;
  37. struct input_dev *input;
  38. int micd_mode;
  39. const struct arizona_micd_config *micd_modes;
  40. int micd_num_modes;
  41. bool micd_reva;
  42. bool micd_clamp;
  43. bool mic;
  44. bool detecting;
  45. int jack_flips;
  46. struct extcon_dev edev;
  47. };
  48. static const struct arizona_micd_config micd_default_modes[] = {
  49. { ARIZONA_ACCDET_SRC, 1 << ARIZONA_MICD_BIAS_SRC_SHIFT, 0 },
  50. { 0, 2 << ARIZONA_MICD_BIAS_SRC_SHIFT, 1 },
  51. };
  52. static struct {
  53. u16 status;
  54. int report;
  55. } arizona_lvl_to_key[ARIZONA_NUM_BUTTONS] = {
  56. { 0x1, BTN_0 },
  57. { 0x2, BTN_1 },
  58. { 0x4, BTN_2 },
  59. { 0x8, BTN_3 },
  60. { 0x10, BTN_4 },
  61. { 0x20, BTN_5 },
  62. };
  63. #define ARIZONA_CABLE_MECHANICAL 0
  64. #define ARIZONA_CABLE_MICROPHONE 1
  65. #define ARIZONA_CABLE_HEADPHONE 2
  66. static const char *arizona_cable[] = {
  67. "Mechanical",
  68. "Microphone",
  69. "Headphone",
  70. NULL,
  71. };
  72. static void arizona_extcon_set_mode(struct arizona_extcon_info *info, int mode)
  73. {
  74. struct arizona *arizona = info->arizona;
  75. if (arizona->pdata.micd_pol_gpio > 0)
  76. gpio_set_value_cansleep(arizona->pdata.micd_pol_gpio,
  77. info->micd_modes[mode].gpio);
  78. regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
  79. ARIZONA_MICD_BIAS_SRC_MASK,
  80. info->micd_modes[mode].bias);
  81. regmap_update_bits(arizona->regmap, ARIZONA_ACCESSORY_DETECT_MODE_1,
  82. ARIZONA_ACCDET_SRC, info->micd_modes[mode].src);
  83. info->micd_mode = mode;
  84. dev_dbg(arizona->dev, "Set jack polarity to %d\n", mode);
  85. }
  86. static void arizona_start_mic(struct arizona_extcon_info *info)
  87. {
  88. struct arizona *arizona = info->arizona;
  89. bool change;
  90. int ret;
  91. info->detecting = true;
  92. info->mic = false;
  93. info->jack_flips = 0;
  94. /* Microphone detection can't use idle mode */
  95. pm_runtime_get(info->dev);
  96. ret = regulator_enable(info->micvdd);
  97. if (ret != 0) {
  98. dev_err(arizona->dev, "Failed to enable MICVDD: %d\n",
  99. ret);
  100. }
  101. if (info->micd_reva) {
  102. regmap_write(arizona->regmap, 0x80, 0x3);
  103. regmap_write(arizona->regmap, 0x294, 0);
  104. regmap_write(arizona->regmap, 0x80, 0x0);
  105. }
  106. regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1,
  107. ARIZONA_MICD_ENA, ARIZONA_MICD_ENA,
  108. &change);
  109. if (!change) {
  110. regulator_disable(info->micvdd);
  111. pm_runtime_put_autosuspend(info->dev);
  112. }
  113. }
  114. static void arizona_stop_mic(struct arizona_extcon_info *info)
  115. {
  116. struct arizona *arizona = info->arizona;
  117. bool change;
  118. regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1,
  119. ARIZONA_MICD_ENA, 0,
  120. &change);
  121. if (info->micd_reva) {
  122. regmap_write(arizona->regmap, 0x80, 0x3);
  123. regmap_write(arizona->regmap, 0x294, 2);
  124. regmap_write(arizona->regmap, 0x80, 0x0);
  125. }
  126. if (change) {
  127. regulator_disable(info->micvdd);
  128. pm_runtime_mark_last_busy(info->dev);
  129. pm_runtime_put_autosuspend(info->dev);
  130. }
  131. }
  132. static irqreturn_t arizona_micdet(int irq, void *data)
  133. {
  134. struct arizona_extcon_info *info = data;
  135. struct arizona *arizona = info->arizona;
  136. unsigned int val, lvl;
  137. int ret, i;
  138. mutex_lock(&info->lock);
  139. ret = regmap_read(arizona->regmap, ARIZONA_MIC_DETECT_3, &val);
  140. if (ret != 0) {
  141. dev_err(arizona->dev, "Failed to read MICDET: %d\n", ret);
  142. mutex_unlock(&info->lock);
  143. return IRQ_NONE;
  144. }
  145. dev_dbg(arizona->dev, "MICDET: %x\n", val);
  146. if (!(val & ARIZONA_MICD_VALID)) {
  147. dev_warn(arizona->dev, "Microphone detection state invalid\n");
  148. mutex_unlock(&info->lock);
  149. return IRQ_NONE;
  150. }
  151. /* Due to jack detect this should never happen */
  152. if (!(val & ARIZONA_MICD_STS)) {
  153. dev_warn(arizona->dev, "Detected open circuit\n");
  154. info->detecting = false;
  155. goto handled;
  156. }
  157. /* If we got a high impedence we should have a headset, report it. */
  158. if (info->detecting && (val & 0x400)) {
  159. ret = extcon_update_state(&info->edev,
  160. 1 << ARIZONA_CABLE_MICROPHONE |
  161. 1 << ARIZONA_CABLE_HEADPHONE,
  162. 1 << ARIZONA_CABLE_MICROPHONE |
  163. 1 << ARIZONA_CABLE_HEADPHONE);
  164. if (ret != 0)
  165. dev_err(arizona->dev, "Headset report failed: %d\n",
  166. ret);
  167. info->mic = true;
  168. info->detecting = false;
  169. goto handled;
  170. }
  171. /* If we detected a lower impedence during initial startup
  172. * then we probably have the wrong polarity, flip it. Don't
  173. * do this for the lowest impedences to speed up detection of
  174. * plain headphones. If both polarities report a low
  175. * impedence then give up and report headphones.
  176. */
  177. if (info->detecting && (val & 0x3f8)) {
  178. info->jack_flips++;
  179. if (info->jack_flips >= info->micd_num_modes) {
  180. dev_dbg(arizona->dev, "Detected headphone\n");
  181. info->detecting = false;
  182. arizona_stop_mic(info);
  183. ret = extcon_set_cable_state_(&info->edev,
  184. ARIZONA_CABLE_HEADPHONE,
  185. true);
  186. if (ret != 0)
  187. dev_err(arizona->dev,
  188. "Headphone report failed: %d\n",
  189. ret);
  190. } else {
  191. info->micd_mode++;
  192. if (info->micd_mode == info->micd_num_modes)
  193. info->micd_mode = 0;
  194. arizona_extcon_set_mode(info, info->micd_mode);
  195. info->jack_flips++;
  196. }
  197. goto handled;
  198. }
  199. /*
  200. * If we're still detecting and we detect a short then we've
  201. * got a headphone. Otherwise it's a button press.
  202. */
  203. if (val & 0x3fc) {
  204. if (info->mic) {
  205. dev_dbg(arizona->dev, "Mic button detected\n");
  206. lvl = val & ARIZONA_MICD_LVL_MASK;
  207. lvl >>= ARIZONA_MICD_LVL_SHIFT;
  208. for (i = 0; i < ARIZONA_NUM_BUTTONS; i++)
  209. if (lvl & arizona_lvl_to_key[i].status)
  210. input_report_key(info->input,
  211. arizona_lvl_to_key[i].report,
  212. 1);
  213. input_sync(info->input);
  214. } else if (info->detecting) {
  215. dev_dbg(arizona->dev, "Headphone detected\n");
  216. info->detecting = false;
  217. arizona_stop_mic(info);
  218. ret = extcon_set_cable_state_(&info->edev,
  219. ARIZONA_CABLE_HEADPHONE,
  220. true);
  221. if (ret != 0)
  222. dev_err(arizona->dev,
  223. "Headphone report failed: %d\n",
  224. ret);
  225. } else {
  226. dev_warn(arizona->dev, "Button with no mic: %x\n",
  227. val);
  228. }
  229. } else {
  230. dev_dbg(arizona->dev, "Mic button released\n");
  231. for (i = 0; i < ARIZONA_NUM_BUTTONS; i++)
  232. input_report_key(info->input,
  233. arizona_lvl_to_key[i].report, 0);
  234. input_sync(info->input);
  235. }
  236. handled:
  237. pm_runtime_mark_last_busy(info->dev);
  238. mutex_unlock(&info->lock);
  239. return IRQ_HANDLED;
  240. }
  241. static irqreturn_t arizona_jackdet(int irq, void *data)
  242. {
  243. struct arizona_extcon_info *info = data;
  244. struct arizona *arizona = info->arizona;
  245. unsigned int val;
  246. int ret, i;
  247. pm_runtime_get_sync(info->dev);
  248. mutex_lock(&info->lock);
  249. ret = regmap_read(arizona->regmap, ARIZONA_AOD_IRQ_RAW_STATUS, &val);
  250. if (ret != 0) {
  251. dev_err(arizona->dev, "Failed to read jackdet status: %d\n",
  252. ret);
  253. mutex_unlock(&info->lock);
  254. pm_runtime_put_autosuspend(info->dev);
  255. return IRQ_NONE;
  256. }
  257. if (val & ARIZONA_JD1_STS) {
  258. dev_dbg(arizona->dev, "Detected jack\n");
  259. ret = extcon_set_cable_state_(&info->edev,
  260. ARIZONA_CABLE_MECHANICAL, true);
  261. if (ret != 0)
  262. dev_err(arizona->dev, "Mechanical report failed: %d\n",
  263. ret);
  264. arizona_start_mic(info);
  265. } else {
  266. dev_dbg(arizona->dev, "Detected jack removal\n");
  267. arizona_stop_mic(info);
  268. for (i = 0; i < ARIZONA_NUM_BUTTONS; i++)
  269. input_report_key(info->input,
  270. arizona_lvl_to_key[i].report, 0);
  271. input_sync(info->input);
  272. ret = extcon_update_state(&info->edev, 0xffffffff, 0);
  273. if (ret != 0)
  274. dev_err(arizona->dev, "Removal report failed: %d\n",
  275. ret);
  276. }
  277. mutex_unlock(&info->lock);
  278. pm_runtime_mark_last_busy(info->dev);
  279. pm_runtime_put_autosuspend(info->dev);
  280. return IRQ_HANDLED;
  281. }
  282. static int arizona_extcon_probe(struct platform_device *pdev)
  283. {
  284. struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
  285. struct arizona_pdata *pdata;
  286. struct arizona_extcon_info *info;
  287. int ret, mode, i;
  288. pdata = dev_get_platdata(arizona->dev);
  289. info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
  290. if (!info) {
  291. dev_err(&pdev->dev, "Failed to allocate memory\n");
  292. ret = -ENOMEM;
  293. goto err;
  294. }
  295. info->micvdd = devm_regulator_get(arizona->dev, "MICVDD");
  296. if (IS_ERR(info->micvdd)) {
  297. ret = PTR_ERR(info->micvdd);
  298. dev_err(arizona->dev, "Failed to get MICVDD: %d\n", ret);
  299. goto err;
  300. }
  301. mutex_init(&info->lock);
  302. info->arizona = arizona;
  303. info->dev = &pdev->dev;
  304. info->detecting = true;
  305. platform_set_drvdata(pdev, info);
  306. switch (arizona->type) {
  307. case WM5102:
  308. switch (arizona->rev) {
  309. case 0:
  310. info->micd_reva = true;
  311. break;
  312. default:
  313. info->micd_clamp = true;
  314. break;
  315. }
  316. break;
  317. default:
  318. break;
  319. }
  320. info->edev.name = "Headset Jack";
  321. info->edev.supported_cable = arizona_cable;
  322. ret = extcon_dev_register(&info->edev, arizona->dev);
  323. if (ret < 0) {
  324. dev_err(arizona->dev, "extcon_dev_register() failed: %d\n",
  325. ret);
  326. goto err;
  327. }
  328. if (pdata->num_micd_configs) {
  329. info->micd_modes = pdata->micd_configs;
  330. info->micd_num_modes = pdata->num_micd_configs;
  331. } else {
  332. info->micd_modes = micd_default_modes;
  333. info->micd_num_modes = ARRAY_SIZE(micd_default_modes);
  334. }
  335. if (arizona->pdata.micd_pol_gpio > 0) {
  336. if (info->micd_modes[0].gpio)
  337. mode = GPIOF_OUT_INIT_HIGH;
  338. else
  339. mode = GPIOF_OUT_INIT_LOW;
  340. ret = devm_gpio_request_one(&pdev->dev,
  341. arizona->pdata.micd_pol_gpio,
  342. mode,
  343. "MICD polarity");
  344. if (ret != 0) {
  345. dev_err(arizona->dev, "Failed to request GPIO%d: %d\n",
  346. arizona->pdata.micd_pol_gpio, ret);
  347. goto err_register;
  348. }
  349. }
  350. if (arizona->pdata.micd_bias_start_time)
  351. regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
  352. ARIZONA_MICD_BIAS_STARTTIME_MASK,
  353. arizona->pdata.micd_bias_start_time
  354. << ARIZONA_MICD_BIAS_STARTTIME_SHIFT);
  355. /*
  356. * If we have a clamp use it.
  357. */
  358. if (info->micd_clamp) {
  359. regmap_update_bits(arizona->regmap,
  360. ARIZONA_MICD_CLAMP_CONTROL,
  361. ARIZONA_MICD_CLAMP_MODE_MASK, 4);
  362. regmap_update_bits(arizona->regmap,
  363. ARIZONA_JACK_DETECT_DEBOUNCE,
  364. ARIZONA_MICD_CLAMP_DB,
  365. ARIZONA_MICD_CLAMP_DB);
  366. }
  367. arizona_extcon_set_mode(info, 0);
  368. info->input = devm_input_allocate_device(&pdev->dev);
  369. if (!info->input) {
  370. dev_err(arizona->dev, "Can't allocate input dev\n");
  371. ret = -ENOMEM;
  372. goto err_register;
  373. }
  374. for (i = 0; i < ARIZONA_NUM_BUTTONS; i++)
  375. input_set_capability(info->input, EV_KEY,
  376. arizona_lvl_to_key[i].report);
  377. info->input->name = "Headset";
  378. info->input->phys = "arizona/extcon";
  379. info->input->dev.parent = &pdev->dev;
  380. pm_runtime_enable(&pdev->dev);
  381. pm_runtime_idle(&pdev->dev);
  382. pm_runtime_get_sync(&pdev->dev);
  383. ret = arizona_request_irq(arizona, ARIZONA_IRQ_JD_RISE,
  384. "JACKDET rise", arizona_jackdet, info);
  385. if (ret != 0) {
  386. dev_err(&pdev->dev, "Failed to get JACKDET rise IRQ: %d\n",
  387. ret);
  388. goto err_input;
  389. }
  390. ret = arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_RISE, 1);
  391. if (ret != 0) {
  392. dev_err(&pdev->dev, "Failed to set JD rise IRQ wake: %d\n",
  393. ret);
  394. goto err_rise;
  395. }
  396. ret = arizona_request_irq(arizona, ARIZONA_IRQ_JD_FALL,
  397. "JACKDET fall", arizona_jackdet, info);
  398. if (ret != 0) {
  399. dev_err(&pdev->dev, "Failed to get JD fall IRQ: %d\n", ret);
  400. goto err_rise_wake;
  401. }
  402. ret = arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_FALL, 1);
  403. if (ret != 0) {
  404. dev_err(&pdev->dev, "Failed to set JD fall IRQ wake: %d\n",
  405. ret);
  406. goto err_fall;
  407. }
  408. ret = arizona_request_irq(arizona, ARIZONA_IRQ_MICDET,
  409. "MICDET", arizona_micdet, info);
  410. if (ret != 0) {
  411. dev_err(&pdev->dev, "Failed to get MICDET IRQ: %d\n", ret);
  412. goto err_fall_wake;
  413. }
  414. regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
  415. ARIZONA_MICD_RATE_MASK,
  416. 8 << ARIZONA_MICD_RATE_SHIFT);
  417. arizona_clk32k_enable(arizona);
  418. regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_DEBOUNCE,
  419. ARIZONA_JD1_DB, ARIZONA_JD1_DB);
  420. regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,
  421. ARIZONA_JD1_ENA, ARIZONA_JD1_ENA);
  422. ret = regulator_allow_bypass(info->micvdd, true);
  423. if (ret != 0)
  424. dev_warn(arizona->dev, "Failed to set MICVDD to bypass: %d\n",
  425. ret);
  426. pm_runtime_put(&pdev->dev);
  427. ret = input_register_device(info->input);
  428. if (ret) {
  429. dev_err(&pdev->dev, "Can't register input device: %d\n", ret);
  430. goto err_micdet;
  431. }
  432. return 0;
  433. err_micdet:
  434. arizona_free_irq(arizona, ARIZONA_IRQ_MICDET, info);
  435. err_fall_wake:
  436. arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_FALL, 0);
  437. err_fall:
  438. arizona_free_irq(arizona, ARIZONA_IRQ_JD_FALL, info);
  439. err_rise_wake:
  440. arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_RISE, 0);
  441. err_rise:
  442. arizona_free_irq(arizona, ARIZONA_IRQ_JD_RISE, info);
  443. err_input:
  444. err_register:
  445. pm_runtime_disable(&pdev->dev);
  446. extcon_dev_unregister(&info->edev);
  447. err:
  448. return ret;
  449. }
  450. static int arizona_extcon_remove(struct platform_device *pdev)
  451. {
  452. struct arizona_extcon_info *info = platform_get_drvdata(pdev);
  453. struct arizona *arizona = info->arizona;
  454. pm_runtime_disable(&pdev->dev);
  455. regmap_update_bits(arizona->regmap,
  456. ARIZONA_MICD_CLAMP_CONTROL,
  457. ARIZONA_MICD_CLAMP_MODE_MASK, 0);
  458. arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_RISE, 0);
  459. arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_FALL, 0);
  460. arizona_free_irq(arizona, ARIZONA_IRQ_MICDET, info);
  461. arizona_free_irq(arizona, ARIZONA_IRQ_JD_RISE, info);
  462. arizona_free_irq(arizona, ARIZONA_IRQ_JD_FALL, info);
  463. regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,
  464. ARIZONA_JD1_ENA, 0);
  465. arizona_clk32k_disable(arizona);
  466. extcon_dev_unregister(&info->edev);
  467. return 0;
  468. }
  469. static struct platform_driver arizona_extcon_driver = {
  470. .driver = {
  471. .name = "arizona-extcon",
  472. .owner = THIS_MODULE,
  473. },
  474. .probe = arizona_extcon_probe,
  475. .remove = arizona_extcon_remove,
  476. };
  477. module_platform_driver(arizona_extcon_driver);
  478. MODULE_DESCRIPTION("Arizona Extcon driver");
  479. MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
  480. MODULE_LICENSE("GPL");
  481. MODULE_ALIAS("platform:extcon-arizona");