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