diff options
Diffstat (limited to 'drivers/extcon')
| -rw-r--r-- | drivers/extcon/Kconfig | 8 | ||||
| -rw-r--r-- | drivers/extcon/Makefile | 1 | ||||
| -rw-r--r-- | drivers/extcon/extcon-arizona.c | 491 |
3 files changed, 500 insertions, 0 deletions
diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index 29c5cf852efc..bb385ac629a8 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig | |||
| @@ -29,4 +29,12 @@ config EXTCON_MAX8997 | |||
| 29 | Maxim MAX8997 PMIC. The MAX8997 MUIC is a USB port accessory | 29 | Maxim MAX8997 PMIC. The MAX8997 MUIC is a USB port accessory |
| 30 | detector and switch. | 30 | detector and switch. |
| 31 | 31 | ||
| 32 | config EXTCON_ARIZONA | ||
| 33 | tristate "Wolfson Arizona EXTCON support" | ||
| 34 | depends on MFD_ARIZONA | ||
| 35 | help | ||
| 36 | Say Y here to enable support for external accessory detection | ||
| 37 | with Wolfson Arizona devices. These are audio CODECs with | ||
| 38 | advanced audio accessory detection support. | ||
| 39 | |||
| 32 | endif # MULTISTATE_SWITCH | 40 | endif # MULTISTATE_SWITCH |
diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index 86020bdb6da0..e932caaa311c 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile | |||
| @@ -5,3 +5,4 @@ | |||
| 5 | obj-$(CONFIG_EXTCON) += extcon_class.o | 5 | obj-$(CONFIG_EXTCON) += extcon_class.o |
| 6 | obj-$(CONFIG_EXTCON_GPIO) += extcon_gpio.o | 6 | obj-$(CONFIG_EXTCON_GPIO) += extcon_gpio.o |
| 7 | obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o | 7 | obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o |
| 8 | obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o | ||
diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c new file mode 100644 index 000000000000..b068bc9defe1 --- /dev/null +++ b/drivers/extcon/extcon-arizona.c | |||
| @@ -0,0 +1,491 @@ | |||
| 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 | |||
