]> Pileus Git - ~andy/linux/blob - drivers/extcon/extcon-arizona.c
driver core: move uevent call to driver_register
[~andy/linux] / 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 "Mechanical"
58 #define ARIZONA_CABLE_HEADPHONE  "Headphone"
59 #define ARIZONA_CABLE_HEADSET    "Headset"
60
61 static const char *arizona_cable[] = {
62         ARIZONA_CABLE_MECHANICAL,
63         ARIZONA_CABLE_HEADSET,
64         ARIZONA_CABLE_HEADPHONE,
65         NULL,
66 };
67
68 static const u32 arizona_exclusions[] = {
69         0x6,   /* Headphone and headset */
70         0,
71 };
72
73 static void arizona_extcon_set_mode(struct arizona_extcon_info *info, int mode)
74 {
75         struct arizona *arizona = info->arizona;
76
77         gpio_set_value_cansleep(arizona->pdata.micd_pol_gpio,
78                                 info->micd_modes[mode].gpio);
79         regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
80                            ARIZONA_MICD_BIAS_SRC_MASK,
81                            info->micd_modes[mode].bias);
82         regmap_update_bits(arizona->regmap, ARIZONA_ACCESSORY_DETECT_MODE_1,
83                            ARIZONA_ACCDET_SRC, info->micd_modes[mode].src);
84
85         info->micd_mode = mode;
86
87         dev_dbg(arizona->dev, "Set jack polarity to %d\n", mode);
88 }
89
90 static void arizona_start_mic(struct arizona_extcon_info *info)
91 {
92         struct arizona *arizona = info->arizona;
93         bool change;
94         int ret;
95
96         info->detecting = true;
97         info->mic = false;
98         info->jack_flips = 0;
99
100         /* Microphone detection can't use idle mode */
101         pm_runtime_get(info->dev);
102
103         ret = regulator_enable(info->micvdd);
104         if (ret != 0) {
105                 dev_err(arizona->dev, "Failed to enable MICVDD: %d\n",
106                         ret);
107         }
108
109         if (info->micd_reva) {
110                 regmap_write(arizona->regmap, 0x80, 0x3);
111                 regmap_write(arizona->regmap, 0x294, 0);
112                 regmap_write(arizona->regmap, 0x80, 0x0);
113         }
114
115         regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1,
116                                  ARIZONA_MICD_ENA, ARIZONA_MICD_ENA,
117                                  &change);
118         if (!change) {
119                 regulator_disable(info->micvdd);
120                 pm_runtime_put_autosuspend(info->dev);
121         }
122 }
123
124 static void arizona_stop_mic(struct arizona_extcon_info *info)
125 {
126         struct arizona *arizona = info->arizona;
127         bool change;
128
129         regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1,
130                                  ARIZONA_MICD_ENA, 0,
131                                  &change);
132
133         if (info->micd_reva) {
134                 regmap_write(arizona->regmap, 0x80, 0x3);
135                 regmap_write(arizona->regmap, 0x294, 2);
136                 regmap_write(arizona->regmap, 0x80, 0x0);
137         }
138
139         if (change) {
140                 regulator_disable(info->micvdd);
141                 pm_runtime_put_autosuspend(info->dev);
142         }
143 }
144
145 static irqreturn_t arizona_micdet(int irq, void *data)
146 {
147         struct arizona_extcon_info *info = data;
148         struct arizona *arizona = info->arizona;
149         unsigned int val;
150         int ret;
151
152         mutex_lock(&info->lock);
153
154         ret = regmap_read(arizona->regmap, ARIZONA_MIC_DETECT_3, &val);
155         if (ret != 0) {
156                 dev_err(arizona->dev, "Failed to read MICDET: %d\n", ret);
157                 return IRQ_NONE;
158         }
159
160         dev_dbg(arizona->dev, "MICDET: %x\n", val);
161
162         if (!(val & ARIZONA_MICD_VALID)) {
163                 dev_warn(arizona->dev, "Microphone detection state invalid\n");
164                 mutex_unlock(&info->lock);
165                 return IRQ_NONE;
166         }
167
168         /* Due to jack detect this should never happen */
169         if (!(val & ARIZONA_MICD_STS)) {
170                 dev_warn(arizona->dev, "Detected open circuit\n");
171                 info->detecting = false;
172                 goto handled;
173         }
174
175         /* If we got a high impedence we should have a headset, report it. */
176         if (info->detecting && (val & 0x400)) {
177                 ret = extcon_set_cable_state(&info->edev,
178                                              ARIZONA_CABLE_HEADSET, true);
179
180                 if (ret != 0)
181                         dev_err(arizona->dev, "Headset report failed: %d\n",
182                                 ret);
183
184                 info->mic = true;
185                 info->detecting = false;
186                 goto handled;
187         }
188
189         /* If we detected a lower impedence during initial startup
190          * then we probably have the wrong polarity, flip it.  Don't
191          * do this for the lowest impedences to speed up detection of
192          * plain headphones.  If both polarities report a low
193          * impedence then give up and report headphones.
194          */
195         if (info->detecting && (val & 0x3f8)) {
196                 info->jack_flips++;
197
198                 if (info->jack_flips >= info->micd_num_modes) {
199                         dev_dbg(arizona->dev, "Detected headphone\n");
200                         info->detecting = false;
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         info->edev.mutually_exclusive = arizona_exclusions;
351
352         ret = extcon_dev_register(&info->edev, arizona->dev);
353         if (ret < 0) {
354                 dev_err(arizona->dev, "extcon_dev_regster() failed: %d\n",
355                         ret);
356                 goto err;
357         }
358
359         if (pdata->num_micd_configs) {
360                 info->micd_modes = pdata->micd_configs;
361                 info->micd_num_modes = pdata->num_micd_configs;
362         } else {
363                 info->micd_modes = micd_default_modes;
364                 info->micd_num_modes = ARRAY_SIZE(micd_default_modes);
365         }
366
367         if (arizona->pdata.micd_pol_gpio > 0) {
368                 if (info->micd_modes[0].gpio)
369                         mode = GPIOF_OUT_INIT_HIGH;
370                 else
371                         mode = GPIOF_OUT_INIT_LOW;
372
373                 ret = devm_gpio_request_one(&pdev->dev,
374                                             arizona->pdata.micd_pol_gpio,
375                                             mode,
376                                             "MICD polarity");
377                 if (ret != 0) {
378                         dev_err(arizona->dev, "Failed to request GPIO%d: %d\n",
379                                 arizona->pdata.micd_pol_gpio, ret);
380                         goto err_register;
381                 }
382         }
383
384         arizona_extcon_set_mode(info, 0);
385
386         pm_runtime_enable(&pdev->dev);
387         pm_runtime_idle(&pdev->dev);
388         pm_runtime_get_sync(&pdev->dev);
389
390         ret = arizona_request_irq(arizona, ARIZONA_IRQ_JD_RISE,
391                                   "JACKDET rise", arizona_jackdet, info);
392         if (ret != 0) {
393                 dev_err(&pdev->dev, "Failed to get JACKDET rise IRQ: %d\n",
394                         ret);
395                 goto err_register;
396         }
397
398         ret = arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_RISE, 1);
399         if (ret != 0) {
400                 dev_err(&pdev->dev, "Failed to set JD rise IRQ wake: %d\n",
401                         ret);
402                 goto err_rise;
403         }
404
405         ret = arizona_request_irq(arizona, ARIZONA_IRQ_JD_FALL,
406                                   "JACKDET fall", arizona_jackdet, info);
407         if (ret != 0) {
408                 dev_err(&pdev->dev, "Failed to get JD fall IRQ: %d\n", ret);
409                 goto err_rise_wake;
410         }
411
412         ret = arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_FALL, 1);
413         if (ret != 0) {
414                 dev_err(&pdev->dev, "Failed to set JD fall IRQ wake: %d\n",
415                         ret);
416                 goto err_fall;
417         }
418
419         ret = arizona_request_irq(arizona, ARIZONA_IRQ_MICDET,
420                                   "MICDET", arizona_micdet, info);
421         if (ret != 0) {
422                 dev_err(&pdev->dev, "Failed to get MICDET IRQ: %d\n", ret);
423                 goto err_fall_wake;
424         }
425
426         regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
427                            ARIZONA_MICD_BIAS_STARTTIME_MASK |
428                            ARIZONA_MICD_RATE_MASK,
429                            7 << ARIZONA_MICD_BIAS_STARTTIME_SHIFT |
430                            8 << ARIZONA_MICD_RATE_SHIFT);
431
432         arizona_clk32k_enable(arizona);
433         regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_DEBOUNCE,
434                            ARIZONA_JD1_DB, ARIZONA_JD1_DB);
435         regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,
436                            ARIZONA_JD1_ENA, ARIZONA_JD1_ENA);
437
438         pm_runtime_put(&pdev->dev);
439
440         return 0;
441
442 err_fall_wake:
443         arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_FALL, 0);
444 err_fall:
445         arizona_free_irq(arizona, ARIZONA_IRQ_JD_FALL, info);
446 err_rise_wake:
447         arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_RISE, 0);
448 err_rise:
449         arizona_free_irq(arizona, ARIZONA_IRQ_JD_RISE, info);
450 err_register:
451         pm_runtime_disable(&pdev->dev);
452         extcon_dev_unregister(&info->edev);
453 err:
454         return ret;
455 }
456
457 static int __devexit arizona_extcon_remove(struct platform_device *pdev)
458 {
459         struct arizona_extcon_info *info = platform_get_drvdata(pdev);
460         struct arizona *arizona = info->arizona;
461
462         pm_runtime_disable(&pdev->dev);
463
464         arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_RISE, 0);
465         arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_FALL, 0);
466         arizona_free_irq(arizona, ARIZONA_IRQ_MICDET, info);
467         arizona_free_irq(arizona, ARIZONA_IRQ_JD_RISE, info);
468         arizona_free_irq(arizona, ARIZONA_IRQ_JD_FALL, info);
469         regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,
470                            ARIZONA_JD1_ENA, 0);
471         arizona_clk32k_disable(arizona);
472         extcon_dev_unregister(&info->edev);
473
474         return 0;
475 }
476
477 static struct platform_driver arizona_extcon_driver = {
478         .driver         = {
479                 .name   = "arizona-extcon",
480                 .owner  = THIS_MODULE,
481         },
482         .probe          = arizona_extcon_probe,
483         .remove         = __devexit_p(arizona_extcon_remove),
484 };
485
486 module_platform_driver(arizona_extcon_driver);
487
488 MODULE_DESCRIPTION("Arizona Extcon driver");
489 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
490 MODULE_LICENSE("GPL");
491 MODULE_ALIAS("platform:extcon-arizona");