extcon: arizona: Updates for v3.10
There's a bunch of different things in this series, I can split them out if need be: - Support for configuring the button detection circuit to reflect the accessories supplied with the system. - Improvements in the HPDET based detection scheme. - Additional robustness against more pathological use cases. - A few small standalone fixes. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iQIcBAABAgAGBQJRWr1qAAoJELSic+t+oim9DUwP/3ur4GgIjk4T3Kix364EeWyw CDipdcxw/kEIopXSbFX3nMolWvvA0bEtOVGnu67lTm1K2YQxStn9XnK/SnijZq0x DR7cWygKwWVF+STYZrWW3OX/4Lyb3MCCeKqBTmw8LNUBHIrWgEi7YB96G3aO+top lsHlPNhXYfG65017NmLyGdWCVsCnzRA21nj4ZPJORf9ij+5wNCJwcCsCLvC1kULX ol5apYUoToEUCrkHa5x5vYVEGif3483CwPIteKLUWyHgQ1vIxkNqxAQLxJHdyH+M +tJ+qKciavttUjXhIwZ/ZaOY5NiQo8d/1OQFUb2z1ZmOpj7uS7ICS6nBg2pBRK+h vRY/q+0zZI89o6gggV5HFHmwJtCiYmcLgsX3YL4KH4oXTX/nQvUkkSKzWf3pXCQb NpmcWhRuGsiNr6qq/twdJ+34ddxgesmXwtzozlecZ4Goz/9ooInpT5QYtXWzHFqm vbUrfk+WrXY6jpz5Xo2hDf2N7zmZaBiPbggJk5wymCWscHl4gANjkfHYEoRedUnX VKC21WHxaR22e0KLKo+Aq+eDdOy8BZjCKbOUbhhWe1L5TCQ6QojptLyNJJIuNQ7c NAdu8nQDLUnQBC8HTZrDWlsjo/xIatozvhWyI3NX5Q9VSgkCYnu7fpQ/Q2UYn13p MkxOYhqCsBuqH9cPDvB8 =6SXJ -----END PGP SIGNATURE----- Merge tag 'extcon-arizona-v3.10' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/misc into char-misc-next Mark writes: extcon: arizona: Updates for v3.10 There's a bunch of different things in this series, I can split them out if need be: - Support for configuring the button detection circuit to reflect the accessories supplied with the system. - Improvements in the HPDET based detection scheme. - Additional robustness against more pathological use cases. - A few small standalone fixes.
This commit is contained in:
commit
2638953fcd
4 changed files with 321 additions and 105 deletions
|
@ -33,12 +33,17 @@
|
|||
#include <linux/mfd/arizona/pdata.h>
|
||||
#include <linux/mfd/arizona/registers.h>
|
||||
|
||||
#define ARIZONA_NUM_BUTTONS 6
|
||||
#define ARIZONA_MAX_MICD_RANGE 8
|
||||
|
||||
#define ARIZONA_ACCDET_MODE_MIC 0
|
||||
#define ARIZONA_ACCDET_MODE_HPL 1
|
||||
#define ARIZONA_ACCDET_MODE_HPR 2
|
||||
|
||||
#define ARIZONA_HPDET_MAX 10000
|
||||
|
||||
#define HPDET_DEBOUNCE 500
|
||||
#define DEFAULT_MICD_TIMEOUT 2000
|
||||
|
||||
struct arizona_extcon_info {
|
||||
struct device *dev;
|
||||
struct arizona *arizona;
|
||||
|
@ -46,17 +51,27 @@ struct arizona_extcon_info {
|
|||
struct regulator *micvdd;
|
||||
struct input_dev *input;
|
||||
|
||||
u16 last_jackdet;
|
||||
|
||||
int micd_mode;
|
||||
const struct arizona_micd_config *micd_modes;
|
||||
int micd_num_modes;
|
||||
|
||||
const struct arizona_micd_range *micd_ranges;
|
||||
int num_micd_ranges;
|
||||
|
||||
int micd_timeout;
|
||||
|
||||
bool micd_reva;
|
||||
bool micd_clamp;
|
||||
|
||||
struct delayed_work hpdet_work;
|
||||
struct delayed_work micd_detect_work;
|
||||
struct delayed_work micd_timeout_work;
|
||||
|
||||
bool hpdet_active;
|
||||
bool hpdet_done;
|
||||
bool hpdet_retried;
|
||||
|
||||
int num_hpdet_res;
|
||||
unsigned int hpdet_res[3];
|
||||
|
@ -71,20 +86,25 @@ struct arizona_extcon_info {
|
|||
};
|
||||
|
||||
static const struct arizona_micd_config micd_default_modes[] = {
|
||||
{ 0, 2 << ARIZONA_MICD_BIAS_SRC_SHIFT, 1 },
|
||||
{ ARIZONA_ACCDET_SRC, 1 << ARIZONA_MICD_BIAS_SRC_SHIFT, 0 },
|
||||
{ 0, 2 << ARIZONA_MICD_BIAS_SRC_SHIFT, 1 },
|
||||
};
|
||||
|
||||
static struct {
|
||||
u16 status;
|
||||
int report;
|
||||
} arizona_lvl_to_key[ARIZONA_NUM_BUTTONS] = {
|
||||
{ 0x1, BTN_0 },
|
||||
{ 0x2, BTN_1 },
|
||||
{ 0x4, BTN_2 },
|
||||
{ 0x8, BTN_3 },
|
||||
{ 0x10, BTN_4 },
|
||||
{ 0x20, BTN_5 },
|
||||
static const struct arizona_micd_range micd_default_ranges[] = {
|
||||
{ .max = 11, .key = BTN_0 },
|
||||
{ .max = 28, .key = BTN_1 },
|
||||
{ .max = 54, .key = BTN_2 },
|
||||
{ .max = 100, .key = BTN_3 },
|
||||
{ .max = 186, .key = BTN_4 },
|
||||
{ .max = 430, .key = BTN_5 },
|
||||
};
|
||||
|
||||
static const int arizona_micd_levels[] = {
|
||||
3, 6, 8, 11, 13, 16, 18, 21, 23, 26, 28, 31, 34, 36, 39, 41, 44, 46,
|
||||
49, 52, 54, 57, 60, 62, 65, 67, 70, 73, 75, 78, 81, 83, 89, 94, 100,
|
||||
105, 111, 116, 122, 127, 139, 150, 161, 173, 186, 196, 209, 220, 245,
|
||||
270, 295, 321, 348, 375, 402, 430, 489, 550, 614, 681, 752, 903, 1071,
|
||||
1257,
|
||||
};
|
||||
|
||||
#define ARIZONA_CABLE_MECHANICAL 0
|
||||
|
@ -100,6 +120,8 @@ static const char *arizona_cable[] = {
|
|||
NULL,
|
||||
};
|
||||
|
||||
static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info);
|
||||
|
||||
static void arizona_extcon_do_magic(struct arizona_extcon_info *info,
|
||||
unsigned int magic)
|
||||
{
|
||||
|
@ -153,6 +175,8 @@ static void arizona_extcon_set_mode(struct arizona_extcon_info *info, int mode)
|
|||
{
|
||||
struct arizona *arizona = info->arizona;
|
||||
|
||||
mode %= info->micd_num_modes;
|
||||
|
||||
if (arizona->pdata.micd_pol_gpio > 0)
|
||||
gpio_set_value_cansleep(arizona->pdata.micd_pol_gpio,
|
||||
info->micd_modes[mode].gpio);
|
||||
|
@ -379,7 +403,7 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)
|
|||
/* If we go out of range report top of range */
|
||||
if (val < 100 || val > 0x3fb) {
|
||||
dev_dbg(arizona->dev, "Measurement out of range\n");
|
||||
return 10000;
|
||||
return ARIZONA_HPDET_MAX;
|
||||
}
|
||||
|
||||
dev_dbg(arizona->dev, "HPDET read %d in range %d\n",
|
||||
|
@ -440,7 +464,8 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)
|
|||
return val;
|
||||
}
|
||||
|
||||
static int arizona_hpdet_do_id(struct arizona_extcon_info *info, int *reading)
|
||||
static int arizona_hpdet_do_id(struct arizona_extcon_info *info, int *reading,
|
||||
bool *mic)
|
||||
{
|
||||
struct arizona *arizona = info->arizona;
|
||||
int id_gpio = arizona->pdata.hpdet_id_gpio;
|
||||
|
@ -452,32 +477,8 @@ static int arizona_hpdet_do_id(struct arizona_extcon_info *info, int *reading)
|
|||
if (arizona->pdata.hpdet_acc_id) {
|
||||
info->hpdet_res[info->num_hpdet_res++] = *reading;
|
||||
|
||||
/*
|
||||
* If the impedence is too high don't measure the
|
||||
* second ground.
|
||||
*/
|
||||
if (info->num_hpdet_res == 1 && *reading >= 45) {
|
||||
dev_dbg(arizona->dev, "Skipping ground flip\n");
|
||||
info->hpdet_res[info->num_hpdet_res++] = *reading;
|
||||
}
|
||||
|
||||
if (info->num_hpdet_res == 1) {
|
||||
dev_dbg(arizona->dev, "Flipping ground\n");
|
||||
|
||||
regmap_update_bits(arizona->regmap,
|
||||
ARIZONA_ACCESSORY_DETECT_MODE_1,
|
||||
ARIZONA_ACCDET_SRC,
|
||||
~info->micd_modes[0].src);
|
||||
|
||||
regmap_update_bits(arizona->regmap,
|
||||
ARIZONA_HEADPHONE_DETECT_1,
|
||||
ARIZONA_HP_POLL, ARIZONA_HP_POLL);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/* Only check the mic directly if we didn't already ID it */
|
||||
if (id_gpio && info->num_hpdet_res == 2 &&
|
||||
!((info->hpdet_res[0] > info->hpdet_res[1] * 2))) {
|
||||
if (id_gpio && info->num_hpdet_res == 1) {
|
||||
dev_dbg(arizona->dev, "Measuring mic\n");
|
||||
|
||||
regmap_update_bits(arizona->regmap,
|
||||
|
@ -496,22 +497,28 @@ static int arizona_hpdet_do_id(struct arizona_extcon_info *info, int *reading)
|
|||
}
|
||||
|
||||
/* OK, got both. Now, compare... */
|
||||
dev_dbg(arizona->dev, "HPDET measured %d %d %d\n",
|
||||
info->hpdet_res[0], info->hpdet_res[1],
|
||||
info->hpdet_res[2]);
|
||||
|
||||
dev_dbg(arizona->dev, "HPDET measured %d %d\n",
|
||||
info->hpdet_res[0], info->hpdet_res[1]);
|
||||
|
||||
/* Take the headphone impedance for the main report */
|
||||
*reading = info->hpdet_res[0];
|
||||
|
||||
/* Sometimes we get false readings due to slow insert */
|
||||
if (*reading >= ARIZONA_HPDET_MAX && !info->hpdet_retried) {
|
||||
dev_dbg(arizona->dev, "Retrying high impedance\n");
|
||||
info->num_hpdet_res = 0;
|
||||
info->hpdet_retried = true;
|
||||
arizona_start_hpdet_acc_id(info);
|
||||
pm_runtime_put(info->dev);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/*
|
||||
* Either the two grounds measure differently or we
|
||||
* measure the mic as high impedance.
|
||||
* If we measure the mic as
|
||||
*/
|
||||
if ((info->hpdet_res[0] > info->hpdet_res[1] * 2) ||
|
||||
(id_gpio && info->hpdet_res[2] > 10)) {
|
||||
if (!id_gpio || info->hpdet_res[1] > 50) {
|
||||
dev_dbg(arizona->dev, "Detected mic\n");
|
||||
info->mic = true;
|
||||
*mic = true;
|
||||
info->detecting = true;
|
||||
} else {
|
||||
dev_dbg(arizona->dev, "Detected headphone\n");
|
||||
|
@ -534,6 +541,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
|
|||
int id_gpio = arizona->pdata.hpdet_id_gpio;
|
||||
int report = ARIZONA_CABLE_HEADPHONE;
|
||||
int ret, reading;
|
||||
bool mic = false;
|
||||
|
||||
mutex_lock(&info->lock);
|
||||
|
||||
|
@ -569,7 +577,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
|
|||
ARIZONA_HP_IMPEDANCE_RANGE_MASK | ARIZONA_HP_POLL,
|
||||
0);
|
||||
|
||||
ret = arizona_hpdet_do_id(info, &reading);
|
||||
ret = arizona_hpdet_do_id(info, &reading, &mic);
|
||||
if (ret == -EAGAIN) {
|
||||
goto out;
|
||||
} else if (ret < 0) {
|
||||
|
@ -599,7 +607,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
|
|||
ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
|
||||
|
||||
/* If we have a mic then reenable MICDET */
|
||||
if (info->mic)
|
||||
if (mic || info->mic)
|
||||
arizona_start_mic(info);
|
||||
|
||||
if (info->hpdet_active) {
|
||||
|
@ -674,6 +682,8 @@ static void arizona_identify_headphone(struct arizona_extcon_info *info)
|
|||
static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info)
|
||||
{
|
||||
struct arizona *arizona = info->arizona;
|
||||
int hp_reading = 32;
|
||||
bool mic;
|
||||
int ret;
|
||||
|
||||
dev_dbg(arizona->dev, "Starting identification via HPDET\n");
|
||||
|
@ -683,8 +693,6 @@ static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info)
|
|||
|
||||
info->hpdet_active = true;
|
||||
|
||||
arizona_extcon_pulse_micbias(info);
|
||||
|
||||
arizona_extcon_do_magic(info, 0x4000);
|
||||
|
||||
ret = regmap_update_bits(arizona->regmap,
|
||||
|
@ -697,12 +705,18 @@ static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info)
|
|||
goto err;
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1,
|
||||
ARIZONA_HP_POLL, ARIZONA_HP_POLL);
|
||||
if (ret != 0) {
|
||||
dev_err(arizona->dev, "Can't start HPDETL measurement: %d\n",
|
||||
ret);
|
||||
goto err;
|
||||
if (arizona->pdata.hpdet_acc_id_line) {
|
||||
ret = regmap_update_bits(arizona->regmap,
|
||||
ARIZONA_HEADPHONE_DETECT_1,
|
||||
ARIZONA_HP_POLL, ARIZONA_HP_POLL);
|
||||
if (ret != 0) {
|
||||
dev_err(arizona->dev,
|
||||
"Can't start HPDETL measurement: %d\n",
|
||||
ret);
|
||||
goto err;
|
||||
}
|
||||
} else {
|
||||
arizona_hpdet_do_id(info, &hp_reading, &mic);
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -721,28 +735,58 @@ static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info)
|
|||
info->hpdet_active = false;
|
||||
}
|
||||
|
||||
static irqreturn_t arizona_micdet(int irq, void *data)
|
||||
static void arizona_micd_timeout_work(struct work_struct *work)
|
||||
{
|
||||
struct arizona_extcon_info *info = data;
|
||||
struct arizona *arizona = info->arizona;
|
||||
unsigned int val, lvl;
|
||||
int ret, i;
|
||||
struct arizona_extcon_info *info = container_of(work,
|
||||
struct arizona_extcon_info,
|
||||
micd_timeout_work.work);
|
||||
|
||||
mutex_lock(&info->lock);
|
||||
|
||||
ret = regmap_read(arizona->regmap, ARIZONA_MIC_DETECT_3, &val);
|
||||
if (ret != 0) {
|
||||
dev_err(arizona->dev, "Failed to read MICDET: %d\n", ret);
|
||||
mutex_unlock(&info->lock);
|
||||
return IRQ_NONE;
|
||||
dev_dbg(info->arizona->dev, "MICD timed out, reporting HP\n");
|
||||
arizona_identify_headphone(info);
|
||||
|
||||
info->detecting = false;
|
||||
|
||||
arizona_stop_mic(info);
|
||||
|
||||
mutex_unlock(&info->lock);
|
||||
}
|
||||
|
||||
static void arizona_micd_detect(struct work_struct *work)
|
||||
{
|
||||
struct arizona_extcon_info *info = container_of(work,
|
||||
struct arizona_extcon_info,
|
||||
micd_detect_work.work);
|
||||
struct arizona *arizona = info->arizona;
|
||||
unsigned int val = 0, lvl;
|
||||
int ret, i, key;
|
||||
|
||||
cancel_delayed_work_sync(&info->micd_timeout_work);
|
||||
|
||||
mutex_lock(&info->lock);
|
||||
|
||||
for (i = 0; i < 10 && !(val & 0x7fc); i++) {
|
||||
ret = regmap_read(arizona->regmap, ARIZONA_MIC_DETECT_3, &val);
|
||||
if (ret != 0) {
|
||||
dev_err(arizona->dev, "Failed to read MICDET: %d\n", ret);
|
||||
mutex_unlock(&info->lock);
|
||||
return;
|
||||
}
|
||||
|
||||
dev_dbg(arizona->dev, "MICDET: %x\n", val);
|
||||
|
||||
if (!(val & ARIZONA_MICD_VALID)) {
|
||||
dev_warn(arizona->dev, "Microphone detection state invalid\n");
|
||||
mutex_unlock(&info->lock);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
dev_dbg(arizona->dev, "MICDET: %x\n", val);
|
||||
|
||||
if (!(val & ARIZONA_MICD_VALID)) {
|
||||
dev_warn(arizona->dev, "Microphone detection state invalid\n");
|
||||
if (i == 10 && !(val & 0x7fc)) {
|
||||
dev_err(arizona->dev, "Failed to get valid MICDET value\n");
|
||||
mutex_unlock(&info->lock);
|
||||
return IRQ_NONE;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Due to jack detect this should never happen */
|
||||
|
@ -783,7 +827,7 @@ static irqreturn_t arizona_micdet(int irq, void *data)
|
|||
* impedence then give up and report headphones.
|
||||
*/
|
||||
if (info->detecting && (val & 0x3f8)) {
|
||||
if (info->jack_flips >= info->micd_num_modes) {
|
||||
if (info->jack_flips >= info->micd_num_modes * 10) {
|
||||
dev_dbg(arizona->dev, "Detected HP/line\n");
|
||||
arizona_identify_headphone(info);
|
||||
|
||||
|
@ -813,12 +857,17 @@ static irqreturn_t arizona_micdet(int irq, void *data)
|
|||
lvl = val & ARIZONA_MICD_LVL_MASK;
|
||||
lvl >>= ARIZONA_MICD_LVL_SHIFT;
|
||||
|
||||
for (i = 0; i < ARIZONA_NUM_BUTTONS; i++)
|
||||
if (lvl & arizona_lvl_to_key[i].status)
|
||||
input_report_key(info->input,
|
||||
arizona_lvl_to_key[i].report,
|
||||
1);
|
||||
input_sync(info->input);
|
||||
for (i = 0; i < info->num_micd_ranges; i++)
|
||||
input_report_key(info->input,
|
||||
info->micd_ranges[i].key, 0);
|
||||
|
||||
WARN_ON(!lvl);
|
||||
WARN_ON(ffs(lvl) - 1 >= info->num_micd_ranges);
|
||||
if (lvl && ffs(lvl) - 1 < info->num_micd_ranges) {
|
||||
key = info->micd_ranges[ffs(lvl) - 1].key;
|
||||
input_report_key(info->input, key, 1);
|
||||
input_sync(info->input);
|
||||
}
|
||||
|
||||
} else if (info->detecting) {
|
||||
dev_dbg(arizona->dev, "Headphone detected\n");
|
||||
|
@ -832,16 +881,41 @@ static irqreturn_t arizona_micdet(int irq, void *data)
|
|||
}
|
||||
} else {
|
||||
dev_dbg(arizona->dev, "Mic button released\n");
|
||||
for (i = 0; i < ARIZONA_NUM_BUTTONS; i++)
|
||||
for (i = 0; i < info->num_micd_ranges; i++)
|
||||
input_report_key(info->input,
|
||||
arizona_lvl_to_key[i].report, 0);
|
||||
info->micd_ranges[i].key, 0);
|
||||
input_sync(info->input);
|
||||
arizona_extcon_pulse_micbias(info);
|
||||
}
|
||||
|
||||
handled:
|
||||
if (info->detecting)
|
||||
schedule_delayed_work(&info->micd_timeout_work,
|
||||
msecs_to_jiffies(info->micd_timeout));
|
||||
|
||||
pm_runtime_mark_last_busy(info->dev);
|
||||
mutex_unlock(&info->lock);
|
||||
}
|
||||
|
||||
static irqreturn_t arizona_micdet(int irq, void *data)
|
||||
{
|
||||
struct arizona_extcon_info *info = data;
|
||||
struct arizona *arizona = info->arizona;
|
||||
int debounce = arizona->pdata.micd_detect_debounce;
|
||||
|
||||
cancel_delayed_work_sync(&info->micd_detect_work);
|
||||
cancel_delayed_work_sync(&info->micd_timeout_work);
|
||||
|
||||
mutex_lock(&info->lock);
|
||||
if (!info->detecting)
|
||||
debounce = 0;
|
||||
mutex_unlock(&info->lock);
|
||||
|
||||
if (debounce)
|
||||
schedule_delayed_work(&info->micd_detect_work,
|
||||
msecs_to_jiffies(debounce));
|
||||
else
|
||||
arizona_micd_detect(&info->micd_detect_work.work);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
@ -862,11 +936,13 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
|
|||
struct arizona_extcon_info *info = data;
|
||||
struct arizona *arizona = info->arizona;
|
||||
unsigned int val, present, mask;
|
||||
bool cancelled_hp, cancelled_mic;
|
||||
int ret, i;
|
||||
|
||||
pm_runtime_get_sync(info->dev);
|
||||
cancelled_hp = cancel_delayed_work_sync(&info->hpdet_work);
|
||||
cancelled_mic = cancel_delayed_work_sync(&info->micd_timeout_work);
|
||||
|
||||
cancel_delayed_work_sync(&info->hpdet_work);
|
||||
pm_runtime_get_sync(info->dev);
|
||||
|
||||
mutex_lock(&info->lock);
|
||||
|
||||
|
@ -887,7 +963,22 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
|
|||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
if ((val & mask) == present) {
|
||||
val &= mask;
|
||||
if (val == info->last_jackdet) {
|
||||
dev_dbg(arizona->dev, "Suppressing duplicate JACKDET\n");
|
||||
if (cancelled_hp)
|
||||
schedule_delayed_work(&info->hpdet_work,
|
||||
msecs_to_jiffies(HPDET_DEBOUNCE));
|
||||
|
||||
if (cancelled_mic)
|
||||
schedule_delayed_work(&info->micd_timeout_work,
|
||||
msecs_to_jiffies(info->micd_timeout));
|
||||
|
||||
goto out;
|
||||
}
|
||||
info->last_jackdet = val;
|
||||
|
||||
if (info->last_jackdet == present) {
|
||||
dev_dbg(arizona->dev, "Detected jack\n");
|
||||
ret = extcon_set_cable_state_(&info->edev,
|
||||
ARIZONA_CABLE_MECHANICAL, true);
|
||||
|
@ -904,7 +995,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
|
|||
arizona_start_mic(info);
|
||||
} else {
|
||||
schedule_delayed_work(&info->hpdet_work,
|
||||
msecs_to_jiffies(250));
|
||||
msecs_to_jiffies(HPDET_DEBOUNCE));
|
||||
}
|
||||
|
||||
regmap_update_bits(arizona->regmap,
|
||||
|
@ -920,10 +1011,11 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
|
|||
info->hpdet_res[i] = 0;
|
||||
info->mic = false;
|
||||
info->hpdet_done = false;
|
||||
info->hpdet_retried = false;
|
||||
|
||||
for (i = 0; i < ARIZONA_NUM_BUTTONS; i++)
|
||||
for (i = 0; i < info->num_micd_ranges; i++)
|
||||
input_report_key(info->input,
|
||||
arizona_lvl_to_key[i].report, 0);
|
||||
info->micd_ranges[i].key, 0);
|
||||
input_sync(info->input);
|
||||
|
||||
ret = extcon_update_state(&info->edev, 0xffffffff, 0);
|
||||
|
@ -937,6 +1029,11 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
|
|||
ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB);
|
||||
}
|
||||
|
||||
if (arizona->pdata.micd_timeout)
|
||||
info->micd_timeout = arizona->pdata.micd_timeout;
|
||||
else
|
||||
info->micd_timeout = DEFAULT_MICD_TIMEOUT;
|
||||
|
||||
/* Clear trig_sts to make sure DCVDD is not forced up */
|
||||
regmap_write(arizona->regmap, ARIZONA_AOD_WKUP_AND_TRIG,
|
||||
ARIZONA_MICD_CLAMP_FALL_TRIG_STS |
|
||||
|
@ -944,6 +1041,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
|
|||
ARIZONA_JD1_FALL_TRIG_STS |
|
||||
ARIZONA_JD1_RISE_TRIG_STS);
|
||||
|
||||
out:
|
||||
mutex_unlock(&info->lock);
|
||||
|
||||
pm_runtime_mark_last_busy(info->dev);
|
||||
|
@ -952,13 +1050,34 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* Map a level onto a slot in the register bank */
|
||||
static void arizona_micd_set_level(struct arizona *arizona, int index,
|
||||
unsigned int level)
|
||||
{
|
||||
int reg;
|
||||
unsigned int mask;
|
||||
|
||||
reg = ARIZONA_MIC_DETECT_LEVEL_4 - (index / 2);
|
||||
|
||||
if (!(index % 2)) {
|
||||
mask = 0x3f00;
|
||||
level <<= 8;
|
||||
} else {
|
||||
mask = 0x3f;
|
||||
}
|
||||
|
||||
/* Program the level itself */
|
||||
regmap_update_bits(arizona->regmap, reg, mask, level);
|
||||
}
|
||||
|
||||
static int arizona_extcon_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
|
||||
struct arizona_pdata *pdata;
|
||||
struct arizona_extcon_info *info;
|
||||
unsigned int val;
|
||||
int jack_irq_fall, jack_irq_rise;
|
||||
int ret, mode, i;
|
||||
int ret, mode, i, j;
|
||||
|
||||
if (!arizona->dapm || !arizona->dapm->card)
|
||||
return -EPROBE_DEFER;
|
||||
|
@ -982,7 +1101,10 @@ static int arizona_extcon_probe(struct platform_device *pdev)
|
|||
mutex_init(&info->lock);
|
||||
info->arizona = arizona;
|
||||
info->dev = &pdev->dev;
|
||||
info->last_jackdet = ~(ARIZONA_MICD_CLAMP_STS | ARIZONA_JD1_STS);
|
||||
INIT_DELAYED_WORK(&info->hpdet_work, arizona_hpdet_work);
|
||||
INIT_DELAYED_WORK(&info->micd_detect_work, arizona_micd_detect);
|
||||
INIT_DELAYED_WORK(&info->micd_timeout_work, arizona_micd_timeout_work);
|
||||
platform_set_drvdata(pdev, info);
|
||||
|
||||
switch (arizona->type) {
|
||||
|
@ -1011,6 +1133,17 @@ static int arizona_extcon_probe(struct platform_device *pdev)
|
|||
goto err;
|
||||
}
|
||||
|
||||
info->input = devm_input_allocate_device(&pdev->dev);
|
||||
if (!info->input) {
|
||||
dev_err(arizona->dev, "Can't allocate input dev\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_register;
|
||||
}
|
||||
|
||||
info->input->name = "Headset";
|
||||
info->input->phys = "arizona/extcon";
|
||||
info->input->dev.parent = &pdev->dev;
|
||||
|
||||
if (pdata->num_micd_configs) {
|
||||
info->micd_modes = pdata->micd_configs;
|
||||
info->micd_num_modes = pdata->num_micd_configs;
|
||||
|
@ -1066,15 +1199,79 @@ static int arizona_extcon_probe(struct platform_device *pdev)
|
|||
arizona->pdata.micd_dbtime
|
||||
<< ARIZONA_MICD_DBTIME_SHIFT);
|
||||
|
||||
BUILD_BUG_ON(ARRAY_SIZE(arizona_micd_levels) != 0x40);
|
||||
|
||||
if (arizona->pdata.num_micd_ranges) {
|
||||
info->micd_ranges = pdata->micd_ranges;
|
||||
info->num_micd_ranges = pdata->num_micd_ranges;
|
||||
} else {
|
||||
info->micd_ranges = micd_default_ranges;
|
||||
info->num_micd_ranges = ARRAY_SIZE(micd_default_ranges);
|
||||
}
|
||||
|
||||
if (arizona->pdata.num_micd_ranges > ARIZONA_MAX_MICD_RANGE) {
|
||||
dev_err(arizona->dev, "Too many MICD ranges: %d\n",
|
||||
arizona->pdata.num_micd_ranges);
|
||||
}
|
||||
|
||||
if (info->num_micd_ranges > 1) {
|
||||
for (i = 1; i < info->num_micd_ranges; i++) {
|
||||
if (info->micd_ranges[i - 1].max >
|
||||
info->micd_ranges[i].max) {
|
||||
dev_err(arizona->dev,
|
||||
"MICD ranges must be sorted\n");
|
||||
ret = -EINVAL;
|
||||
goto err_input;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Disable all buttons by default */
|
||||
regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_2,
|
||||
ARIZONA_MICD_LVL_SEL_MASK, 0x81);
|
||||
|
||||
/* Set up all the buttons the user specified */
|
||||
for (i = 0; i < info->num_micd_ranges; i++) {
|
||||
for (j = 0; j < ARRAY_SIZE(arizona_micd_levels); j++)
|
||||
if (arizona_micd_levels[j] >= info->micd_ranges[i].max)
|
||||
break;
|
||||
|
||||
if (j == ARRAY_SIZE(arizona_micd_levels)) {
|
||||
dev_err(arizona->dev, "Unsupported MICD level %d\n",
|
||||
info->micd_ranges[i].max);
|
||||
ret = -EINVAL;
|
||||
goto err_input;
|
||||
}
|
||||
|
||||
dev_dbg(arizona->dev, "%d ohms for MICD threshold %d\n",
|
||||
arizona_micd_levels[j], i);
|
||||
|
||||
arizona_micd_set_level(arizona, i, j);
|
||||
input_set_capability(info->input, EV_KEY,
|
||||
info->micd_ranges[i].key);
|
||||
|
||||
/* Enable reporting of that range */
|
||||
regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_2,
|
||||
1 << i, 1 << i);
|
||||
}
|
||||
|
||||
/* Set all the remaining keys to a maximum */
|
||||
for (; i < ARIZONA_MAX_MICD_RANGE; i++)
|
||||
arizona_micd_set_level(arizona, i, 0x3f);
|
||||
|
||||
/*
|
||||
* If we have a clamp use it, activating in conjunction with
|
||||
* GPIO5 if that is connected for jack detect operation.
|
||||
*/
|
||||
if (info->micd_clamp) {
|
||||
if (arizona->pdata.jd_gpio5) {
|
||||
/* Put the GPIO into input mode */
|
||||
/* Put the GPIO into input mode with optional pull */
|
||||
val = 0xc101;
|
||||
if (arizona->pdata.jd_gpio5_nopull)
|
||||
val &= ~ARIZONA_GPN_PU;
|
||||
|
||||
regmap_write(arizona->regmap, ARIZONA_GPIO5_CTRL,
|
||||
0xc101);
|
||||
val);
|
||||
|
||||
regmap_update_bits(arizona->regmap,
|
||||
ARIZONA_MICD_CLAMP_CONTROL,
|
||||
|
@ -1093,20 +1290,6 @@ static int arizona_extcon_probe(struct platform_device *pdev)
|
|||
|
||||
arizona_extcon_set_mode(info, 0);
|
||||
|
||||
info->input = devm_input_allocate_device(&pdev->dev);
|
||||
if (!info->input) {
|
||||
dev_err(arizona->dev, "Can't allocate input dev\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_register;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARIZONA_NUM_BUTTONS; i++)
|
||||
input_set_capability(info->input, EV_KEY,
|
||||
arizona_lvl_to_key[i].report);
|
||||
info->input->name = "Headset";
|
||||
info->input->phys = "arizona/extcon";
|
||||
info->input->dev.parent = &pdev->dev;
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_idle(&pdev->dev);
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
|
|
|
@ -331,6 +331,10 @@ static const struct reg_default wm5102_reg_default[] = {
|
|||
{ 0x000002A3, 0x1102 }, /* R675 - Mic Detect 1 */
|
||||
{ 0x000002A4, 0x009F }, /* R676 - Mic Detect 2 */
|
||||
{ 0x000002A5, 0x0000 }, /* R677 - Mic Detect 3 */
|
||||
{ 0x000002A6, 0x3737 }, /* R678 - Mic Detect Level 1 */
|
||||
{ 0x000002A7, 0x372C }, /* R679 - Mic Detect Level 2 */
|
||||
{ 0x000002A8, 0x1422 }, /* R680 - Mic Detect Level 3 */
|
||||
{ 0x000002A9, 0x030A }, /* R681 - Mic Detect Level 4 */
|
||||
{ 0x000002C3, 0x0000 }, /* R707 - Mic noise mix control 1 */
|
||||
{ 0x000002CB, 0x0000 }, /* R715 - Isolation control */
|
||||
{ 0x000002D3, 0x0000 }, /* R723 - Jack detect analogue */
|
||||
|
@ -1090,6 +1094,10 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg)
|
|||
case ARIZONA_MIC_DETECT_1:
|
||||
case ARIZONA_MIC_DETECT_2:
|
||||
case ARIZONA_MIC_DETECT_3:
|
||||
case ARIZONA_MIC_DETECT_LEVEL_1:
|
||||
case ARIZONA_MIC_DETECT_LEVEL_2:
|
||||
case ARIZONA_MIC_DETECT_LEVEL_3:
|
||||
case ARIZONA_MIC_DETECT_LEVEL_4:
|
||||
case ARIZONA_MIC_NOISE_MIX_CONTROL_1:
|
||||
case ARIZONA_ISOLATION_CONTROL:
|
||||
case ARIZONA_JACK_DETECT_ANALOGUE:
|
||||
|
|
|
@ -86,6 +86,11 @@ struct arizona_micd_config {
|
|||
bool gpio;
|
||||
};
|
||||
|
||||
struct arizona_micd_range {
|
||||
int max; /** Ohms */
|
||||
int key; /** Key to report to input layer */
|
||||
};
|
||||
|
||||
struct arizona_pdata {
|
||||
int reset; /** GPIO controlling /RESET, if any */
|
||||
int ldoena; /** GPIO controlling LODENA, if any */
|
||||
|
@ -117,12 +122,21 @@ struct arizona_pdata {
|
|||
/** GPIO5 is used for jack detection */
|
||||
bool jd_gpio5;
|
||||
|
||||
/** Internal pull on GPIO5 is disabled when used for jack detection */
|
||||
bool jd_gpio5_nopull;
|
||||
|
||||
/** Use the headphone detect circuit to identify the accessory */
|
||||
bool hpdet_acc_id;
|
||||
|
||||
/** Check for line output with HPDET method */
|
||||
bool hpdet_acc_id_line;
|
||||
|
||||
/** GPIO used for mic isolation with HPDET */
|
||||
int hpdet_id_gpio;
|
||||
|
||||
/** Extra debounce timeout used during initial mic detection (ms) */
|
||||
int micd_detect_debounce;
|
||||
|
||||
/** GPIO for mic detection polarity */
|
||||
int micd_pol_gpio;
|
||||
|
||||
|
@ -135,9 +149,16 @@ struct arizona_pdata {
|
|||
/** Mic detect debounce level */
|
||||
int micd_dbtime;
|
||||
|
||||
/** Mic detect timeout (ms) */
|
||||
int micd_timeout;
|
||||
|
||||
/** Force MICBIAS on for mic detect */
|
||||
bool micd_force_micbias;
|
||||
|
||||
/** Mic detect level parameters */
|
||||
const struct arizona_micd_range *micd_ranges;
|
||||
int num_micd_ranges;
|
||||
|
||||
/** Headset polarity configurations */
|
||||
struct arizona_micd_config *micd_configs;
|
||||
int num_micd_configs;
|
||||
|
|
|
@ -124,6 +124,10 @@
|
|||
#define ARIZONA_MIC_DETECT_1 0x2A3
|
||||
#define ARIZONA_MIC_DETECT_2 0x2A4
|
||||
#define ARIZONA_MIC_DETECT_3 0x2A5
|
||||
#define ARIZONA_MIC_DETECT_LEVEL_1 0x2A6
|
||||
#define ARIZONA_MIC_DETECT_LEVEL_2 0x2A7
|
||||
#define ARIZONA_MIC_DETECT_LEVEL_3 0x2A8
|
||||
#define ARIZONA_MIC_DETECT_LEVEL_4 0x2A9
|
||||
#define ARIZONA_MIC_NOISE_MIX_CONTROL_1 0x2C3
|
||||
#define ARIZONA_ISOLATION_CONTROL 0x2CB
|
||||
#define ARIZONA_JACK_DETECT_ANALOGUE 0x2D3
|
||||
|
|
Loading…
Reference in a new issue