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