From 154f757fd315270e42bd17f4a68d84bd070e5758 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Tue, 27 Nov 2012 09:40:32 +0900 Subject: extcon: max77693: Remove duplicate code by making function This patch make max77693-muic_get_cable_type() function to remove duplicate code because almost internal function need to read adc/adc1k/adclow/chg_type value of MUIC register. Also, this patch add description of internal function move field constant of muic device from extcon-max77693 driver to max77693 header file because of it is needed for masking some interrupt through platform data. Signed-off-by: Chanwoo Choi Signed-off-by: Myungjoo Ham --- include/linux/mfd/max77693-private.h | 86 ++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) (limited to 'include/linux/mfd') diff --git a/include/linux/mfd/max77693-private.h b/include/linux/mfd/max77693-private.h index 1eeae5c07915..5b18ecde69b5 100644 --- a/include/linux/mfd/max77693-private.h +++ b/include/linux/mfd/max77693-private.h @@ -106,6 +106,92 @@ enum max77693_muic_reg { MAX77693_MUIC_REG_END, }; +/* MAX77693 MUIC - STATUS1~3 Register */ +#define STATUS1_ADC_SHIFT (0) +#define STATUS1_ADCLOW_SHIFT (5) +#define STATUS1_ADCERR_SHIFT (6) +#define STATUS1_ADC1K_SHIFT (7) +#define STATUS1_ADC_MASK (0x1f << STATUS1_ADC_SHIFT) +#define STATUS1_ADCLOW_MASK (0x1 << STATUS1_ADCLOW_SHIFT) +#define STATUS1_ADCERR_MASK (0x1 << STATUS1_ADCERR_SHIFT) +#define STATUS1_ADC1K_MASK (0x1 << STATUS1_ADC1K_SHIFT) + +#define STATUS2_CHGTYP_SHIFT (0) +#define STATUS2_CHGDETRUN_SHIFT (3) +#define STATUS2_DCDTMR_SHIFT (4) +#define STATUS2_DXOVP_SHIFT (5) +#define STATUS2_VBVOLT_SHIFT (6) +#define STATUS2_VIDRM_SHIFT (7) +#define STATUS2_CHGTYP_MASK (0x7 << STATUS2_CHGTYP_SHIFT) +#define STATUS2_CHGDETRUN_MASK (0x1 << STATUS2_CHGDETRUN_SHIFT) +#define STATUS2_DCDTMR_MASK (0x1 << STATUS2_DCDTMR_SHIFT) +#define STATUS2_DXOVP_MASK (0x1 << STATUS2_DXOVP_SHIFT) +#define STATUS2_VBVOLT_MASK (0x1 << STATUS2_VBVOLT_SHIFT) +#define STATUS2_VIDRM_MASK (0x1 << STATUS2_VIDRM_SHIFT) + +#define STATUS3_OVP_SHIFT (2) +#define STATUS3_OVP_MASK (0x1 << STATUS3_OVP_SHIFT) + +/* MAX77693 CDETCTRL1~2 register */ +#define CDETCTRL1_CHGDETEN_SHIFT (0) +#define CDETCTRL1_CHGTYPMAN_SHIFT (1) +#define CDETCTRL1_DCDEN_SHIFT (2) +#define CDETCTRL1_DCD2SCT_SHIFT (3) +#define CDETCTRL1_CDDELAY_SHIFT (4) +#define CDETCTRL1_DCDCPL_SHIFT (5) +#define CDETCTRL1_CDPDET_SHIFT (7) +#define CDETCTRL1_CHGDETEN_MASK (0x1 << CDETCTRL1_CHGDETEN_SHIFT) +#define CDETCTRL1_CHGTYPMAN_MASK (0x1 << CDETCTRL1_CHGTYPMAN_SHIFT) +#define CDETCTRL1_DCDEN_MASK (0x1 << CDETCTRL1_DCDEN_SHIFT) +#define CDETCTRL1_DCD2SCT_MASK (0x1 << CDETCTRL1_DCD2SCT_SHIFT) +#define CDETCTRL1_CDDELAY_MASK (0x1 << CDETCTRL1_CDDELAY_SHIFT) +#define CDETCTRL1_DCDCPL_MASK (0x1 << CDETCTRL1_DCDCPL_SHIFT) +#define CDETCTRL1_CDPDET_MASK (0x1 << CDETCTRL1_CDPDET_SHIFT) + +#define CDETCTRL2_VIDRMEN_SHIFT (1) +#define CDETCTRL2_DXOVPEN_SHIFT (3) +#define CDETCTRL2_VIDRMEN_MASK (0x1 << CDETCTRL2_VIDRMEN_SHIFT) +#define CDETCTRL2_DXOVPEN_MASK (0x1 << CDETCTRL2_DXOVPEN_SHIFT) + +/* MAX77693 MUIC - CONTROL1~3 register */ +#define COMN1SW_SHIFT (0) +#define COMP2SW_SHIFT (3) +#define COMN1SW_MASK (0x7 << COMN1SW_SHIFT) +#define COMP2SW_MASK (0x7 << COMP2SW_SHIFT) +#define COMP_SW_MASK (COMP2SW_MASK | COMN1SW_MASK) +#define CONTROL1_SW_USB ((1 << COMP2SW_SHIFT) \ + | (1 << COMN1SW_SHIFT)) +#define CONTROL1_SW_AUDIO ((2 << COMP2SW_SHIFT) \ + | (2 << COMN1SW_SHIFT)) +#define CONTROL1_SW_UART ((3 << COMP2SW_SHIFT) \ + | (3 << COMN1SW_SHIFT)) +#define CONTROL1_SW_OPEN ((0 << COMP2SW_SHIFT) \ + | (0 << COMN1SW_SHIFT)) + +#define CONTROL2_LOWPWR_SHIFT (0) +#define CONTROL2_ADCEN_SHIFT (1) +#define CONTROL2_CPEN_SHIFT (2) +#define CONTROL2_SFOUTASRT_SHIFT (3) +#define CONTROL2_SFOUTORD_SHIFT (4) +#define CONTROL2_ACCDET_SHIFT (5) +#define CONTROL2_USBCPINT_SHIFT (6) +#define CONTROL2_RCPS_SHIFT (7) +#define CONTROL2_LOWPWR_MASK (0x1 << CONTROL2_LOWPWR_SHIFT) +#define CONTROL2_ADCEN_MASK (0x1 << CONTROL2_ADCEN_SHIFT) +#define CONTROL2_CPEN_MASK (0x1 << CONTROL2_CPEN_SHIFT) +#define CONTROL2_SFOUTASRT_MASK (0x1 << CONTROL2_SFOUTASRT_SHIFT) +#define CONTROL2_SFOUTORD_MASK (0x1 << CONTROL2_SFOUTORD_SHIFT) +#define CONTROL2_ACCDET_MASK (0x1 << CONTROL2_ACCDET_SHIFT) +#define CONTROL2_USBCPINT_MASK (0x1 << CONTROL2_USBCPINT_SHIFT) +#define CONTROL2_RCPS_MASK (0x1 << CONTROL2_RCPS_SHIFT) + +#define CONTROL3_JIGSET_SHIFT (0) +#define CONTROL3_BTLDSET_SHIFT (2) +#define CONTROL3_ADCDBSET_SHIFT (4) +#define CONTROL3_JIGSET_MASK (0x3 << CONTROL3_JIGSET_SHIFT) +#define CONTROL3_BTLDSET_MASK (0x3 << CONTROL3_BTLDSET_SHIFT) +#define CONTROL3_ADCDBSET_MASK (0x3 << CONTROL3_ADCDBSET_SHIFT) + /* Slave addr = 0x90: Haptic */ enum max77693_haptic_reg { MAX77693_HAPTIC_REG_STATUS = 0x00, -- cgit From 297620fd1e14edf5fefa1736f873b9228336eee1 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Wed, 26 Dec 2012 13:10:11 +0900 Subject: extcon: max77693: Check the state/type of cable after boot completed This patch check the state/type of cable after completing the initialization of platform and notify platform of cable state/type through extcon. If extcon provider driver notify the state/type of cable before completing platform boot, this uevent is unused and ignored. Signed-off-by: Chanwoo Choi Signed-off-by: Myungjoo Ham --- drivers/extcon/extcon-max77693.c | 37 ++++++++++++++++++++++++++++++++++--- include/linux/mfd/max77693.h | 2 ++ 2 files changed, 36 insertions(+), 3 deletions(-) (limited to 'include/linux/mfd') diff --git a/drivers/extcon/extcon-max77693.c b/drivers/extcon/extcon-max77693.c index 07ea96bfd0cb..10f41f3d5be4 100644 --- a/drivers/extcon/extcon-max77693.c +++ b/drivers/extcon/extcon-max77693.c @@ -30,6 +30,7 @@ #include #define DEV_NAME "max77693-muic" +#define DELAY_MS_DEFAULT 20000 /* unit: millisecond */ enum max77693_muic_adc_debounce_time { ADC_DEBOUNCE_TIME_5MS = 0, @@ -52,6 +53,14 @@ struct max77693_muic_info { struct work_struct irq_work; struct mutex mutex; + /* + * Use delayed workqueue to detect cable state and then + * notify cable state to notifiee/platform through uevent. + * After completing the booting of platform, the extcon provider + * driver should notify cable state to upper layer. + */ + struct delayed_work wq_detcable; + /* Button of dock device */ struct input_dev *dock; }; @@ -912,13 +921,23 @@ static int max77693_muic_detect_accessory(struct max77693_muic_info *info) return ret; } +static void max77693_muic_detect_cable_wq(struct work_struct *work) +{ + struct max77693_muic_info *info = container_of(to_delayed_work(work), + struct max77693_muic_info, wq_detcable); + + max77693_muic_detect_accessory(info); +} + static int max77693_muic_probe(struct platform_device *pdev) { struct max77693_dev *max77693 = dev_get_drvdata(pdev->dev.parent); struct max77693_platform_data *pdata = dev_get_platdata(max77693->dev); struct max77693_muic_platform_data *muic_pdata = pdata->muic_data; struct max77693_muic_info *info; - int ret, i; + int delay_jiffies; + int ret; + int i; u8 id; info = devm_kzalloc(&pdev->dev, sizeof(struct max77693_muic_info), @@ -1051,8 +1070,20 @@ static int max77693_muic_probe(struct platform_device *pdev) /* Set ADC debounce time */ max77693_muic_set_debounce_time(info, ADC_DEBOUNCE_TIME_25MS); - /* Detect accessory on boot */ - max77693_muic_detect_accessory(info); + /* + * Detect accessory after completing the initialization of platform + * + * - Use delayed workqueue to detect cable state and then + * notify cable state to notifiee/platform through uevent. + * After completing the booting of platform, the extcon provider + * driver should notify cable state to upper layer. + */ + INIT_DELAYED_WORK(&info->wq_detcable, max77693_muic_detect_cable_wq); + if (muic_pdata->detcable_delay_ms) + delay_jiffies = msecs_to_jiffies(muic_pdata->detcable_delay_ms); + else + delay_jiffies = msecs_to_jiffies(DELAY_MS_DEFAULT); + schedule_delayed_work(&info->wq_detcable, delay_jiffies); return ret; diff --git a/include/linux/mfd/max77693.h b/include/linux/mfd/max77693.h index fe03b2d35d4f..9aa9c203f28b 100644 --- a/include/linux/mfd/max77693.h +++ b/include/linux/mfd/max77693.h @@ -38,6 +38,8 @@ struct max77693_reg_data { struct max77693_muic_platform_data { struct max77693_reg_data *init_data; int num_init_data; + + int detcable_delay_ms; }; struct max77693_platform_data { -- cgit From 2b75799f5ae9f31ea9778003591fd295d3bc3159 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Thu, 6 Dec 2012 21:27:56 +0900 Subject: extcon: max77693: Set default uart/usb path by using platform data This patch determine default uart/usb path by using platform data. The MAX77693 MUIC device can possibliy set USB/UART/AUDIO/USB_AUX /UART_AUX to internal h/w path of MUIC device. So, drvier should determine default uart/usb path. Signed-off-by: Chanwoo Choi Signed-off-by: Myungjoo Ham --- drivers/extcon/extcon-max77693.c | 24 ++++++++++++++++++++++++ include/linux/mfd/max77693.h | 7 +++++++ 2 files changed, 31 insertions(+) (limited to 'include/linux/mfd') diff --git a/drivers/extcon/extcon-max77693.c b/drivers/extcon/extcon-max77693.c index 7b7f1a2a0846..abab068adc35 100644 --- a/drivers/extcon/extcon-max77693.c +++ b/drivers/extcon/extcon-max77693.c @@ -63,6 +63,13 @@ struct max77693_muic_info { /* Button of dock device */ struct input_dev *dock; + + /* + * Default usb/uart path whether UART/USB or AUX_UART/AUX_USB + * h/w path of COMP2/COMN1 on CONTROL1 register. + */ + int path_usb; + int path_uart; }; enum max77693_muic_cable_group { @@ -1058,6 +1065,23 @@ static int max77693_muic_probe(struct platform_device *pdev) = muic_pdata->init_data[i].data; } + /* + * Default usb/uart path whether UART/USB or AUX_UART/AUX_USB + * h/w path of COMP2/COMN1 on CONTROL1 register. + */ + if (muic_pdata->path_uart) + info->path_uart = muic_pdata->path_uart; + else + info->path_uart = CONTROL1_SW_UART; + + if (muic_pdata->path_usb) + info->path_usb = muic_pdata->path_usb; + else + info->path_usb = CONTROL1_SW_USB; + + /* Set initial path for UART */ + max77693_muic_set_path(info, info->path_uart, true); + /* Check revision number of MUIC device*/ ret = max77693_read_reg(info->max77693->regmap_muic, MAX77693_MUIC_REG_ID, &id); diff --git a/include/linux/mfd/max77693.h b/include/linux/mfd/max77693.h index 9aa9c203f28b..3109a6c5c948 100644 --- a/include/linux/mfd/max77693.h +++ b/include/linux/mfd/max77693.h @@ -40,6 +40,13 @@ struct max77693_muic_platform_data { int num_init_data; int detcable_delay_ms; + + /* + * Default usb/uart path whether UART/USB or AUX_UART/AUX_USB + * h/w path of COMP2/COMN1 on CONTROL1 register. + */ + int path_usb; + int path_uart; }; struct max77693_platform_data { -- cgit From b17e54625cff1b06aec9df505b46c9bcdcd4823d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 11 Jan 2013 08:55:24 +0900 Subject: extcon: arizona: Allow configuration of MICBIAS rise time Allow configuration of the rise time for MICBIAS via platform data, the delay required depends on things like the external component selection. Signed-off-by: Mark Brown Signed-off-by: Chanwoo Choi Acked-by: MyungJoo Ham --- drivers/extcon/extcon-arizona.c | 6 ++++++ include/linux/mfd/arizona/pdata.h | 3 +++ 2 files changed, 9 insertions(+) (limited to 'include/linux/mfd') diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index 9e1d104803e7..635b7078ce5e 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -417,6 +417,12 @@ static int arizona_extcon_probe(struct platform_device *pdev) } } + if (arizona->pdata.micd_bias_start_time) + regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1, + ARIZONA_MICD_BIAS_STARTTIME_MASK, + arizona->pdata.micd_bias_start_time + << ARIZONA_MICD_BIAS_STARTTIME_SHIFT); + arizona_extcon_set_mode(info, 0); info->input = devm_input_allocate_device(&pdev->dev); diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h index 8b1d1daaae16..5b0508853290 100644 --- a/include/linux/mfd/arizona/pdata.h +++ b/include/linux/mfd/arizona/pdata.h @@ -99,6 +99,9 @@ struct arizona_pdata { /** GPIO for mic detection polarity */ int micd_pol_gpio; + /** Mic detect ramp rate */ + int micd_bias_start_time; + /** Headset polarity configurations */ struct arizona_micd_config *micd_configs; int num_micd_configs; -- cgit From dab63eb25ced7539a51b8f4218f7b6b56d34df22 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 11 Jan 2013 08:55:36 +0900 Subject: extcon: arizona: Use microphone clamp function if available Newer Arizona devices include a microphone clamp function which is tied to jack detect. Activate this feature when present in order to ensure best performance of the subsystem. Signed-off-by: Mark Brown Signed-off-by: Chanwoo Choi Signed-off-by: Myungjoo Ham --- drivers/extcon/extcon-arizona.c | 19 +++++++++++++++++++ include/linux/mfd/arizona/core.h | 4 +++- include/linux/mfd/arizona/registers.h | 28 ++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) (limited to 'include/linux/mfd') diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index 635b7078ce5e..3ef3bee7d1e6 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -45,6 +45,7 @@ struct arizona_extcon_info { int micd_num_modes; bool micd_reva; + bool micd_clamp; bool mic; bool detecting; @@ -375,6 +376,7 @@ static int arizona_extcon_probe(struct platform_device *pdev) info->micd_reva = true; break; default: + info->micd_clamp = true; break; } break; @@ -423,6 +425,19 @@ static int arizona_extcon_probe(struct platform_device *pdev) arizona->pdata.micd_bias_start_time << ARIZONA_MICD_BIAS_STARTTIME_SHIFT); + /* + * If we have a clamp use it. + */ + if (info->micd_clamp) { + regmap_update_bits(arizona->regmap, + ARIZONA_MICD_CLAMP_CONTROL, + ARIZONA_MICD_CLAMP_MODE_MASK, 4); + regmap_update_bits(arizona->regmap, + ARIZONA_JACK_DETECT_DEBOUNCE, + ARIZONA_MICD_CLAMP_DB, + ARIZONA_MICD_CLAMP_DB); + } + arizona_extcon_set_mode(info, 0); info->input = devm_input_allocate_device(&pdev->dev); @@ -529,6 +544,10 @@ static int arizona_extcon_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); + regmap_update_bits(arizona->regmap, + ARIZONA_MICD_CLAMP_CONTROL, + ARIZONA_MICD_CLAMP_MODE_MASK, 0); + arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_RISE, 0); arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_FALL, 0); arizona_free_irq(arizona, ARIZONA_IRQ_MICDET, info); diff --git a/include/linux/mfd/arizona/core.h b/include/linux/mfd/arizona/core.h index a580363a7d29..a710255528d7 100644 --- a/include/linux/mfd/arizona/core.h +++ b/include/linux/mfd/arizona/core.h @@ -75,8 +75,10 @@ enum arizona_type { #define ARIZONA_IRQ_DCS_HP_DONE 47 #define ARIZONA_IRQ_FLL2_CLOCK_OK 48 #define ARIZONA_IRQ_FLL1_CLOCK_OK 49 +#define ARIZONA_IRQ_MICD_CLAMP_RISE 50 +#define ARIZONA_IRQ_MICD_CLAMP_FALL 51 -#define ARIZONA_NUM_IRQ 50 +#define ARIZONA_NUM_IRQ 52 struct snd_soc_dapm_context; diff --git a/include/linux/mfd/arizona/registers.h b/include/linux/mfd/arizona/registers.h index 1f6fe31a4d5c..f3211f06cec6 100644 --- a/include/linux/mfd/arizona/registers.h +++ b/include/linux/mfd/arizona/registers.h @@ -119,6 +119,7 @@ #define ARIZONA_ACCESSORY_DETECT_MODE_1 0x293 #define ARIZONA_HEADPHONE_DETECT_1 0x29B #define ARIZONA_HEADPHONE_DETECT_2 0x29C +#define ARIZONA_MICD_CLAMP_CONTROL 0x2A2 #define ARIZONA_MIC_DETECT_1 0x2A3 #define ARIZONA_MIC_DETECT_2 0x2A4 #define ARIZONA_MIC_DETECT_3 0x2A5 @@ -2069,6 +2070,13 @@ #define ARIZONA_HP_LVL_SHIFT 0 /* HP_LVL - [6:0] */ #define ARIZONA_HP_LVL_WIDTH 7 /* HP_LVL - [6:0] */ +/* + * R674 (0x2A2) - MICD clamp control + */ +#define ARIZONA_MICD_CLAMP_MODE_MASK 0x000F /* MICD_CLAMP_MODE - [3:0] */ +#define ARIZONA_MICD_CLAMP_MODE_SHIFT 0 /* MICD_CLAMP_MODE - [3:0] */ +#define ARIZONA_MICD_CLAMP_MODE_WIDTH 4 /* MICD_CLAMP_MODE - [3:0] */ + /* * R675 (0x2A3) - Mic Detect 1 */ @@ -5267,6 +5275,12 @@ /* * R3409 (0xD51) - AOD IRQ1 */ +#define ARIZONA_MICD_CLAMP_FALL_EINT1 0x0080 /* MICD_CLAMP_FALL_EINT1 */ +#define ARIZONA_MICD_CLAMP_FALL_EINT1_MASK 0x0080 /* MICD_CLAMP_FALL_EINT1 */ +#define ARIZONA_MICD_CLAMP_FALL_EINT1_SHIFT 7 /* MICD_CLAMP_FALL_EINT1 */ +#define ARIZONA_MICD_CLAMP_RISE_EINT1 0x0040 /* MICD_CLAMP_RISE_EINT1 */ +#define ARIZONA_MICD_CLAMP_RISE_EINT1_MASK 0x0040 /* MICD_CLAMP_RISE_EINT1 */ +#define ARIZONA_MICD_CLAMP_RISE_EINT1_SHIFT 6 /* MICD_CLAMP_RISE_EINT1 */ #define ARIZONA_GP5_FALL_EINT1 0x0020 /* GP5_FALL_EINT1 */ #define ARIZONA_GP5_FALL_EINT1_MASK 0x0020 /* GP5_FALL_EINT1 */ #define ARIZONA_GP5_FALL_EINT1_SHIFT 5 /* GP5_FALL_EINT1 */ @@ -5295,6 +5309,12 @@ /* * R3410 (0xD52) - AOD IRQ2 */ +#define ARIZONA_MICD_CLAMP_FALL_EINT2 0x0080 /* MICD_CLAMP_FALL_EINT2 */ +#define ARIZONA_MICD_CLAMP_FALL_EINT2_MASK 0x0080 /* MICD_CLAMP_FALL_EINT2 */ +#define ARIZONA_MICD_CLAMP_FALL_EINT2_SHIFT 7 /* MICD_CLAMP_FALL_EINT2 */ +#define ARIZONA_MICD_CLAMP_RISE_EINT2 0x0040 /* MICD_CLAMP_RISE_EINT2 */ +#define ARIZONA_MICD_CLAMP_RISE_EINT2_MASK 0x0040 /* MICD_CLAMP_RISE_EINT2 */ +#define ARIZONA_MICD_CLAMP_RISE_EINT2_SHIFT 6 /* MICD_CLAMP_RISE_EINT2 */ #define ARIZONA_GP5_FALL_EINT2 0x0020 /* GP5_FALL_EINT2 */ #define ARIZONA_GP5_FALL_EINT2_MASK 0x0020 /* GP5_FALL_EINT2 */ #define ARIZONA_GP5_FALL_EINT2_SHIFT 5 /* GP5_FALL_EINT2 */ @@ -5379,6 +5399,10 @@ /* * R3413 (0xD55) - AOD IRQ Raw Status */ +#define ARIZONA_MICD_CLAMP_STS 0x0008 /* MICD_CLAMP_STS */ +#define ARIZONA_MICD_CLAMP_STS_MASK 0x0008 /* MICD_CLAMP_STS */ +#define ARIZONA_MICD_CLAMP_STS_SHIFT 3 /* MICD_CLAMP_STS */ +#define ARIZONA_MICD_CLAMP_STS_WIDTH 1 /* MICD_CLAMP_STS */ #define ARIZONA_GP5_STS 0x0004 /* GP5_STS */ #define ARIZONA_GP5_STS_MASK 0x0004 /* GP5_STS */ #define ARIZONA_GP5_STS_SHIFT 2 /* GP5_STS */ @@ -5395,6 +5419,10 @@ /* * R3414 (0xD56) - Jack detect debounce */ +#define ARIZONA_MICD_CLAMP_DB 0x0008 /* MICD_CLAMP_DB */ +#define ARIZONA_MICD_CLAMP_DB_MASK 0x0008 /* MICD_CLAMP_DB */ +#define ARIZONA_MICD_CLAMP_DB_SHIFT 3 /* MICD_CLAMP_DB */ +#define ARIZONA_MICD_CLAMP_DB_WIDTH 1 /* MICD_CLAMP_DB */ #define ARIZONA_JD2_DB 0x0002 /* JD2_DB */ #define ARIZONA_JD2_DB_MASK 0x0002 /* JD2_DB */ #define ARIZONA_JD2_DB_SHIFT 1 /* JD2_DB */ -- cgit From 92a49871b378b1e523a95da569cd38efdd06eee5 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 11 Jan 2013 08:55:39 +0900 Subject: extcon: arizona: Support use of GPIO5 as an input to jack detection Some system designs provide an input on GPIO5 which in conjunction with the jack detection feature indicates the presence of an accessory. Support such systems, using the microphone clamp feature to minimise wakeups of the processor. Signed-off-by: Mark Brown Signed-off-by: Chanwoo Choi Signed-off-by: Myungjoo Ham --- drivers/extcon/extcon-arizona.c | 76 +++++++++++++++++++++++++++++---------- include/linux/mfd/arizona/pdata.h | 3 ++ 2 files changed, 61 insertions(+), 18 deletions(-) (limited to 'include/linux/mfd') diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index 3ef3bee7d1e6..b5190a811601 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -290,13 +290,21 @@ static irqreturn_t arizona_jackdet(int irq, void *data) { struct arizona_extcon_info *info = data; struct arizona *arizona = info->arizona; - unsigned int val; + unsigned int val, present, mask; int ret, i; pm_runtime_get_sync(info->dev); mutex_lock(&info->lock); + if (arizona->pdata.jd_gpio5) { + mask = ARIZONA_MICD_CLAMP_STS; + present = 0; + } else { + mask = ARIZONA_JD1_STS; + present = ARIZONA_JD1_STS; + } + ret = regmap_read(arizona->regmap, ARIZONA_AOD_IRQ_RAW_STATUS, &val); if (ret != 0) { dev_err(arizona->dev, "Failed to read jackdet status: %d\n", @@ -306,7 +314,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data) return IRQ_NONE; } - if (val & ARIZONA_JD1_STS) { + if ((val & mask) == present) { dev_dbg(arizona->dev, "Detected jack\n"); ret = extcon_set_cable_state_(&info->edev, ARIZONA_CABLE_MECHANICAL, true); @@ -321,6 +329,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data) arizona_stop_mic(info); + for (i = 0; i < ARIZONA_NUM_BUTTONS; i++) input_report_key(info->input, arizona_lvl_to_key[i].report, 0); @@ -345,6 +354,7 @@ 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; + int jack_irq_fall, jack_irq_rise; int ret, mode, i; pdata = dev_get_platdata(arizona->dev); @@ -426,12 +436,24 @@ static int arizona_extcon_probe(struct platform_device *pdev) << ARIZONA_MICD_BIAS_STARTTIME_SHIFT); /* - * If we have a clamp use it. + * If we have a clamp use it, activating in conjunction with + * GPIO5 if that is connected for jack detect operation. */ if (info->micd_clamp) { - regmap_update_bits(arizona->regmap, - ARIZONA_MICD_CLAMP_CONTROL, - ARIZONA_MICD_CLAMP_MODE_MASK, 4); + if (arizona->pdata.jd_gpio5) { + /* Put the GPIO into input mode */ + regmap_write(arizona->regmap, ARIZONA_GPIO5_CTRL, + 0xc101); + + regmap_update_bits(arizona->regmap, + ARIZONA_MICD_CLAMP_CONTROL, + ARIZONA_MICD_CLAMP_MODE_MASK, 0x9); + } else { + regmap_update_bits(arizona->regmap, + ARIZONA_MICD_CLAMP_CONTROL, + ARIZONA_MICD_CLAMP_MODE_MASK, 0x4); + } + regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_DEBOUNCE, ARIZONA_MICD_CLAMP_DB, @@ -458,7 +480,15 @@ static int arizona_extcon_probe(struct platform_device *pdev) pm_runtime_idle(&pdev->dev); pm_runtime_get_sync(&pdev->dev); - ret = arizona_request_irq(arizona, ARIZONA_IRQ_JD_RISE, + if (arizona->pdata.jd_gpio5) { + jack_irq_rise = ARIZONA_IRQ_MICD_CLAMP_RISE; + jack_irq_fall = ARIZONA_IRQ_MICD_CLAMP_FALL; + } else { + jack_irq_rise = ARIZONA_IRQ_JD_RISE; + jack_irq_fall = ARIZONA_IRQ_JD_FALL; + } + + ret = arizona_request_irq(arizona, jack_irq_rise, "JACKDET rise", arizona_jackdet, info); if (ret != 0) { dev_err(&pdev->dev, "Failed to get JACKDET rise IRQ: %d\n", @@ -466,21 +496,21 @@ static int arizona_extcon_probe(struct platform_device *pdev) goto err_input; } - ret = arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_RISE, 1); + ret = arizona_set_irq_wake(arizona, jack_irq_rise, 1); if (ret != 0) { dev_err(&pdev->dev, "Failed to set JD rise IRQ wake: %d\n", ret); goto err_rise; } - ret = arizona_request_irq(arizona, ARIZONA_IRQ_JD_FALL, + ret = arizona_request_irq(arizona, jack_irq_fall, "JACKDET fall", arizona_jackdet, info); if (ret != 0) { dev_err(&pdev->dev, "Failed to get JD fall IRQ: %d\n", ret); goto err_rise_wake; } - ret = arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_FALL, 1); + ret = arizona_set_irq_wake(arizona, jack_irq_fall, 1); if (ret != 0) { dev_err(&pdev->dev, "Failed to set JD fall IRQ wake: %d\n", ret); @@ -522,13 +552,13 @@ static int arizona_extcon_probe(struct platform_device *pdev) err_micdet: arizona_free_irq(arizona, ARIZONA_IRQ_MICDET, info); err_fall_wake: - arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_FALL, 0); + arizona_set_irq_wake(arizona, jack_irq_fall, 0); err_fall: - arizona_free_irq(arizona, ARIZONA_IRQ_JD_FALL, info); + arizona_free_irq(arizona, jack_irq_fall, info); err_rise_wake: - arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_RISE, 0); + arizona_set_irq_wake(arizona, jack_irq_rise, 0); err_rise: - arizona_free_irq(arizona, ARIZONA_IRQ_JD_RISE, info); + arizona_free_irq(arizona, jack_irq_rise, info); err_input: err_register: pm_runtime_disable(&pdev->dev); @@ -541,6 +571,7 @@ static int arizona_extcon_remove(struct platform_device *pdev) { struct arizona_extcon_info *info = platform_get_drvdata(pdev); struct arizona *arizona = info->arizona; + int jack_irq_rise, jack_irq_fall; pm_runtime_disable(&pdev->dev); @@ -548,11 +579,20 @@ static int arizona_extcon_remove(struct platform_device *pdev) ARIZONA_MICD_CLAMP_CONTROL, ARIZONA_MICD_CLAMP_MODE_MASK, 0); - arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_RISE, 0); - arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_FALL, 0); + if (arizona->pdata.jd_gpio5) { + jack_irq_rise = ARIZONA_IRQ_MICD_CLAMP_RISE; + jack_irq_fall = ARIZONA_IRQ_MICD_CLAMP_FALL; + } else { + jack_irq_rise = ARIZONA_IRQ_JD_RISE; + jack_irq_fall = ARIZONA_IRQ_JD_FALL; + } + + arizona_set_irq_wake(arizona, jack_irq_rise, 0); + arizona_set_irq_wake(arizona, jack_irq_fall, 0); + arizona_free_irq(arizona, ARIZONA_IRQ_HPDET, info); arizona_free_irq(arizona, ARIZONA_IRQ_MICDET, info); - arizona_free_irq(arizona, ARIZONA_IRQ_JD_RISE, info); - arizona_free_irq(arizona, ARIZONA_IRQ_JD_FALL, info); + arizona_free_irq(arizona, jack_irq_rise, info); + arizona_free_irq(arizona, jack_irq_fall, info); regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE, ARIZONA_JD1_ENA, 0); arizona_clk32k_disable(arizona); diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h index 5b0508853290..0fc26a480be3 100644 --- a/include/linux/mfd/arizona/pdata.h +++ b/include/linux/mfd/arizona/pdata.h @@ -96,6 +96,9 @@ struct arizona_pdata { /** Pin state for GPIO pins */ int gpio_defaults[ARIZONA_MAX_GPIO]; + /** GPIO5 is used for jack detection */ + bool jd_gpio5; + /** GPIO for mic detection polarity */ int micd_pol_gpio; -- cgit From 4f340333822de79b3439bddccdbf3846f9794e42 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 11 Jan 2013 08:55:43 +0900 Subject: extcon: arizona: Enable basic headphone identification Use the headphone detection to identify if the accessory is a headphone or line load. There are two different revisions of the IP with different register layouts, support both. Signed-off-by: Mark Brown Signed-off-by: Chanwoo Choi Signed-off-by: Myungjoo Ham --- drivers/extcon/extcon-arizona.c | 341 +++++++++++++++++++++++++++++++--- drivers/mfd/wm5102-tables.c | 3 + include/linux/mfd/arizona/registers.h | 12 ++ 3 files changed, 335 insertions(+), 21 deletions(-) (limited to 'include/linux/mfd') diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index b5190a811601..cc258a8842ef 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -31,8 +31,14 @@ #include #include +#define ARIZONA_DEFAULT_HP 32 + #define ARIZONA_NUM_BUTTONS 6 +#define ARIZONA_ACCDET_MODE_MIC 0 +#define ARIZONA_ACCDET_MODE_HPL 1 +#define ARIZONA_ACCDET_MODE_HPR 2 + struct arizona_extcon_info { struct device *dev; struct arizona *arizona; @@ -47,10 +53,14 @@ struct arizona_extcon_info { bool micd_reva; bool micd_clamp; + bool hpdet_active; + bool mic; bool detecting; int jack_flips; + int hpdet_ip; + struct extcon_dev edev; }; @@ -74,11 +84,13 @@ static struct { #define ARIZONA_CABLE_MECHANICAL 0 #define ARIZONA_CABLE_MICROPHONE 1 #define ARIZONA_CABLE_HEADPHONE 2 +#define ARIZONA_CABLE_LINEOUT 3 static const char *arizona_cable[] = { "Mechanical", "Microphone", "Headphone", + "Line-out", NULL, }; @@ -100,6 +112,290 @@ static void arizona_extcon_set_mode(struct arizona_extcon_info *info, int mode) dev_dbg(arizona->dev, "Set jack polarity to %d\n", mode); } +static struct { + unsigned int factor_a; + unsigned int factor_b; +} arizona_hpdet_b_ranges[] = { + { 5528, 362464 }, + { 11084, 6186851 }, + { 11065, 65460395 }, +}; + +static struct { + int min; + int max; +} arizona_hpdet_c_ranges[] = { + { 0, 30 }, + { 8, 100 }, + { 100, 1000 }, + { 1000, 10000 }, +}; + +static int arizona_hpdet_read(struct arizona_extcon_info *info) +{ + struct arizona *arizona = info->arizona; + unsigned int val, range; + int ret; + + ret = regmap_read(arizona->regmap, ARIZONA_HEADPHONE_DETECT_2, &val); + if (ret != 0) { + dev_err(arizona->dev, "Failed to read HPDET status: %d\n", + ret); + return ret; + } + + switch (info->hpdet_ip) { + case 0: + if (!(val & ARIZONA_HP_DONE)) { + dev_err(arizona->dev, "HPDET did not complete: %x\n", + val); + val = ARIZONA_DEFAULT_HP; + } + + val &= ARIZONA_HP_LVL_MASK; + break; + + case 1: + if (!(val & ARIZONA_HP_DONE_B)) { + dev_err(arizona->dev, "HPDET did not complete: %x\n", + val); + return ARIZONA_DEFAULT_HP; + } + + ret = regmap_read(arizona->regmap, ARIZONA_HP_DACVAL, &val); + if (ret != 0) { + dev_err(arizona->dev, "Failed to read HP value: %d\n", + ret); + return ARIZONA_DEFAULT_HP; + } + + regmap_read(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1, + &range); + range = (range & ARIZONA_HP_IMPEDANCE_RANGE_MASK) + >> ARIZONA_HP_IMPEDANCE_RANGE_SHIFT; + + if (range < ARRAY_SIZE(arizona_hpdet_b_ranges) - 1 && + (val < 100 || val > 0x3fb)) { + range++; + dev_dbg(arizona->dev, "Moving to HPDET range %d\n", + range); + regmap_update_bits(arizona->regmap, + ARIZONA_HEADPHONE_DETECT_1, + ARIZONA_HP_IMPEDANCE_RANGE_MASK, + range << + ARIZONA_HP_IMPEDANCE_RANGE_SHIFT); + return -EAGAIN; + } + + /* 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; + } + + dev_dbg(arizona->dev, "HPDET read %d in range %d\n", + val, range); + + val = arizona_hpdet_b_ranges[range].factor_b + / ((val * 100) - + arizona_hpdet_b_ranges[range].factor_a); + break; + + default: + dev_warn(arizona->dev, "Unknown HPDET IP revision %d\n", + info->hpdet_ip); + case 2: + if (!(val & ARIZONA_HP_DONE_B)) { + dev_err(arizona->dev, "HPDET did not complete: %x\n", + val); + return ARIZONA_DEFAULT_HP; + } + + val &= ARIZONA_HP_LVL_B_MASK; + + regmap_read(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1, + &range); + range = (range & ARIZONA_HP_IMPEDANCE_RANGE_MASK) + >> ARIZONA_HP_IMPEDANCE_RANGE_SHIFT; + + /* Skip up or down a range? */ + if (range && (val < arizona_hpdet_c_ranges[range].min)) { + range--; + dev_dbg(arizona->dev, "Moving to HPDET range %d-%d\n", + arizona_hpdet_c_ranges[range].min, + arizona_hpdet_c_ranges[range].max); + regmap_update_bits(arizona->regmap, + ARIZONA_HEADPHONE_DETECT_1, + ARIZONA_HP_IMPEDANCE_RANGE_MASK, + range << + ARIZONA_HP_IMPEDANCE_RANGE_SHIFT); + return -EAGAIN; + } + + if (range < ARRAY_SIZE(arizona_hpdet_c_ranges) - 1 && + (val >= arizona_hpdet_c_ranges[range].max)) { + range++; + dev_dbg(arizona->dev, "Moving to HPDET range %d-%d\n", + arizona_hpdet_c_ranges[range].min, + arizona_hpdet_c_ranges[range].max); + regmap_update_bits(arizona->regmap, + ARIZONA_HEADPHONE_DETECT_1, + ARIZONA_HP_IMPEDANCE_RANGE_MASK, + range << + ARIZONA_HP_IMPEDANCE_RANGE_SHIFT); + return -EAGAIN; + } + } + + dev_dbg(arizona->dev, "HP impedance %d ohms\n", val); + return val; +} + +static irqreturn_t arizona_hpdet_irq(int irq, void *data) +{ + struct arizona_extcon_info *info = data; + struct arizona *arizona = info->arizona; + int report = ARIZONA_CABLE_HEADPHONE; + int ret; + + mutex_lock(&info->lock); + + /* If we got a spurious IRQ for some reason then ignore it */ + if (!info->hpdet_active) { + dev_warn(arizona->dev, "Spurious HPDET IRQ\n"); + mutex_unlock(&info->lock); + return IRQ_NONE; + } + + /* If the cable was removed while measuring ignore the result */ + ret = extcon_get_cable_state_(&info->edev, ARIZONA_CABLE_MECHANICAL); + if (ret < 0) { + dev_err(arizona->dev, "Failed to check cable state: %d\n", + ret); + goto out; + } else if (!ret) { + dev_dbg(arizona->dev, "Ignoring HPDET for removed cable\n"); + goto done; + } + + ret = arizona_hpdet_read(info); + if (ret == -EAGAIN) { + goto out; + } else if (ret < 0) { + goto done; + } + + /* Reset back to starting range */ + regmap_update_bits(arizona->regmap, + ARIZONA_HEADPHONE_DETECT_1, + ARIZONA_HP_IMPEDANCE_RANGE_MASK, 0); + + /* Report high impedence cables as line outputs */ + if (ret >= 5000) + report = ARIZONA_CABLE_LINEOUT; + else + report = ARIZONA_CABLE_HEADPHONE; + + ret = extcon_set_cable_state_(&info->edev, report, true); + if (ret != 0) + dev_err(arizona->dev, "Failed to report HP/line: %d\n", + ret); + + ret = regmap_update_bits(arizona->regmap, 0x225, 0x4000, 0); + if (ret != 0) + dev_warn(arizona->dev, "Failed to undo magic: %d\n", ret); + + ret = regmap_update_bits(arizona->regmap, 0x226, 0x4000, 0); + if (ret != 0) + dev_warn(arizona->dev, "Failed to undo magic: %d\n", ret); + +done: + regmap_update_bits(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1, + ARIZONA_HP_POLL, 0); + + /* Revert back to MICDET mode */ + regmap_update_bits(arizona->regmap, + ARIZONA_ACCESSORY_DETECT_MODE_1, + ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC); + + /* If we have a mic then reenable MICDET */ + if (info->mic) + arizona_start_mic(info); + + if (info->hpdet_active) { + pm_runtime_put_autosuspend(info->dev); + info->hpdet_active = false; + } + +out: + mutex_unlock(&info->lock); + + return IRQ_HANDLED; +} + +static void arizona_identify_headphone(struct arizona_extcon_info *info) +{ + struct arizona *arizona = info->arizona; + int ret; + + dev_dbg(arizona->dev, "Starting HPDET\n"); + + /* Make sure we keep the device enabled during the measurement */ + pm_runtime_get(info->dev); + + info->hpdet_active = true; + + if (info->mic) + arizona_stop_mic(info); + + ret = regmap_update_bits(arizona->regmap, 0x225, 0x4000, 0x4000); + if (ret != 0) + dev_warn(arizona->dev, "Failed to do magic: %d\n", ret); + + ret = regmap_update_bits(arizona->regmap, 0x226, 0x4000, 0x4000); + if (ret != 0) + dev_warn(arizona->dev, "Failed to do magic: %d\n", ret); + + ret = regmap_update_bits(arizona->regmap, + ARIZONA_ACCESSORY_DETECT_MODE_1, + ARIZONA_ACCDET_MODE_MASK, + ARIZONA_ACCDET_MODE_HPL); + if (ret != 0) { + dev_err(arizona->dev, "Failed to set HPDETL mode: %d\n", ret); + 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; + } + + return; + +err: + regmap_update_bits(arizona->regmap, ARIZONA_ACCESSORY_DETECT_MODE_1, + ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC); + + /* Just report headphone */ + ret = extcon_update_state(&info->edev, + 1 << ARIZONA_CABLE_HEADPHONE, + 1 << ARIZONA_CABLE_HEADPHONE); + if (ret != 0) + dev_err(arizona->dev, "Failed to report headphone: %d\n", ret); + + if (info->mic) + arizona_start_mic(info); + + info->hpdet_active = false; +} + } + + info->hpdet_active = false; +} + static void arizona_start_mic(struct arizona_extcon_info *info) { struct arizona *arizona = info->arizona; @@ -125,6 +421,10 @@ static void arizona_start_mic(struct arizona_extcon_info *info) regmap_write(arizona->regmap, 0x80, 0x0); } + regmap_update_bits(arizona->regmap, + ARIZONA_ACCESSORY_DETECT_MODE_1, + ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC); + regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1, ARIZONA_MICD_ENA, ARIZONA_MICD_ENA, &change); @@ -189,11 +489,11 @@ static irqreturn_t arizona_micdet(int irq, void *data) /* If we got a high impedence we should have a headset, report it. */ if (info->detecting && (val & 0x400)) { + arizona_identify_headphone(info); + ret = extcon_update_state(&info->edev, - 1 << ARIZONA_CABLE_MICROPHONE | - 1 << ARIZONA_CABLE_HEADPHONE, - 1 << ARIZONA_CABLE_MICROPHONE | - 1 << ARIZONA_CABLE_HEADPHONE); + 1 << ARIZONA_CABLE_MICROPHONE, + 1 << ARIZONA_CABLE_MICROPHONE); if (ret != 0) dev_err(arizona->dev, "Headset report failed: %d\n", @@ -214,17 +514,12 @@ static irqreturn_t arizona_micdet(int irq, void *data) info->jack_flips++; if (info->jack_flips >= info->micd_num_modes) { - dev_dbg(arizona->dev, "Detected headphone\n"); + dev_dbg(arizona->dev, "Detected HP/line\n"); + arizona_identify_headphone(info); + info->detecting = false; - arizona_stop_mic(info); - ret = extcon_set_cable_state_(&info->edev, - ARIZONA_CABLE_HEADPHONE, - true); - if (ret != 0) - dev_err(arizona->dev, - "Headphone report failed: %d\n", - ret); + arizona_stop_mic(info); } else { info->micd_mode++; if (info->micd_mode == info->micd_num_modes) @@ -260,13 +555,7 @@ static irqreturn_t arizona_micdet(int irq, void *data) info->detecting = false; arizona_stop_mic(info); - ret = extcon_set_cable_state_(&info->edev, - ARIZONA_CABLE_HEADPHONE, - true); - if (ret != 0) - dev_err(arizona->dev, - "Headphone report failed: %d\n", - ret); + arizona_identify_headphone(info); } else { dev_warn(arizona->dev, "Button with no mic: %x\n", val); @@ -387,6 +676,7 @@ static int arizona_extcon_probe(struct platform_device *pdev) break; default: info->micd_clamp = true; + info->hpdet_ip = 1; break; } break; @@ -524,6 +814,13 @@ static int arizona_extcon_probe(struct platform_device *pdev) goto err_fall_wake; } + ret = arizona_request_irq(arizona, ARIZONA_IRQ_HPDET, + "HPDET", arizona_hpdet_irq, info); + if (ret != 0) { + dev_err(&pdev->dev, "Failed to get HPDET IRQ: %d\n", ret); + goto err_micdet; + } + regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1, ARIZONA_MICD_RATE_MASK, 8 << ARIZONA_MICD_RATE_SHIFT); @@ -544,11 +841,13 @@ static int arizona_extcon_probe(struct platform_device *pdev) ret = input_register_device(info->input); if (ret) { dev_err(&pdev->dev, "Can't register input device: %d\n", ret); - goto err_micdet; + goto err_hpdet; } return 0; +err_hpdet: + arizona_free_irq(arizona, ARIZONA_IRQ_HPDET, info); err_micdet: arizona_free_irq(arizona, ARIZONA_IRQ_MICDET, info); err_fall_wake: diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index 088872ab6338..311cc3657b0b 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -1106,6 +1106,8 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg) case ARIZONA_ACCESSORY_DETECT_MODE_1: case ARIZONA_HEADPHONE_DETECT_1: case ARIZONA_HEADPHONE_DETECT_2: + case ARIZONA_HP_DACVAL: + case ARIZONA_MICD_CLAMP_CONTROL: case ARIZONA_MIC_DETECT_1: case ARIZONA_MIC_DETECT_2: case ARIZONA_MIC_DETECT_3: @@ -1875,6 +1877,7 @@ static bool wm5102_volatile_register(struct device *dev, unsigned int reg) case ARIZONA_DSP1_STATUS_2: case ARIZONA_DSP1_STATUS_3: case ARIZONA_HEADPHONE_DETECT_2: + case ARIZONA_HP_DACVAL: case ARIZONA_MIC_DETECT_3: return true; default: diff --git a/include/linux/mfd/arizona/registers.h b/include/linux/mfd/arizona/registers.h index f3211f06cec6..e9150533e701 100644 --- a/include/linux/mfd/arizona/registers.h +++ b/include/linux/mfd/arizona/registers.h @@ -119,6 +119,7 @@ #define ARIZONA_ACCESSORY_DETECT_MODE_1 0x293 #define ARIZONA_HEADPHONE_DETECT_1 0x29B #define ARIZONA_HEADPHONE_DETECT_2 0x29C +#define ARIZONA_HP_DACVAL 0x29F #define ARIZONA_MICD_CLAMP_CONTROL 0x2A2 #define ARIZONA_MIC_DETECT_1 0x2A3 #define ARIZONA_MIC_DETECT_2 0x2A4 @@ -2036,6 +2037,9 @@ /* * R667 (0x29B) - Headphone Detect 1 */ +#define ARIZONA_HP_IMPEDANCE_RANGE_MASK 0x0600 /* HP_IMPEDANCE_RANGE - [10:9] */ +#define ARIZONA_HP_IMPEDANCE_RANGE_SHIFT 9 /* HP_IMPEDANCE_RANGE - [10:9] */ +#define ARIZONA_HP_IMPEDANCE_RANGE_WIDTH 2 /* HP_IMPEDANCE_RANGE - [10:9] */ #define ARIZONA_HP_STEP_SIZE 0x0100 /* HP_STEP_SIZE */ #define ARIZONA_HP_STEP_SIZE_MASK 0x0100 /* HP_STEP_SIZE */ #define ARIZONA_HP_STEP_SIZE_SHIFT 8 /* HP_STEP_SIZE */ @@ -2070,6 +2074,14 @@ #define ARIZONA_HP_LVL_SHIFT 0 /* HP_LVL - [6:0] */ #define ARIZONA_HP_LVL_WIDTH 7 /* HP_LVL - [6:0] */ +#define ARIZONA_HP_DONE_B 0x8000 /* HP_DONE */ +#define ARIZONA_HP_DONE_B_MASK 0x8000 /* HP_DONE */ +#define ARIZONA_HP_DONE_B_SHIFT 15 /* HP_DONE */ +#define ARIZONA_HP_DONE_B_WIDTH 1 /* HP_DONE */ +#define ARIZONA_HP_LVL_B_MASK 0x7FFF /* HP_LVL - [14:0] */ +#define ARIZONA_HP_LVL_B_SHIFT 0 /* HP_LVL - [14:0] */ +#define ARIZONA_HP_LVL_B_WIDTH 15 /* HP_LVL - [14:0] */ + /* * R674 (0x2A2) - MICD clamp control */ -- cgit From dd235eea4ed75b1599dd9a53bb618fe5befeb731 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 11 Jan 2013 08:55:51 +0900 Subject: extcon: arizona: Support HPDET based accessory identification The accessory detection functionality in Arizona devices is flexible and supports several system designs in addition to the default one implemented by the existing driver. One such design uses the HPDET feature to determine what kind of accessory is present by comparing measurements taken with the two headphone grounds available on the device, implement that if selected by platform data. Signed-off-by: Mark Brown Signed-off-by: Chanwoo Choi Signed-off-by: Myungjoo Ham --- drivers/extcon/extcon-arizona.c | 159 +++++++++++++++++++++++++++++++++++--- include/linux/mfd/arizona/pdata.h | 3 + 2 files changed, 150 insertions(+), 12 deletions(-) (limited to 'include/linux/mfd') diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index dab4584a4ad5..ba9525ee4706 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -55,6 +55,9 @@ struct arizona_extcon_info { bool hpdet_active; + int num_hpdet_res; + unsigned int hpdet_res[2]; + bool mic; bool detecting; int jack_flips; @@ -65,8 +68,8 @@ struct arizona_extcon_info { }; static const struct arizona_micd_config micd_default_modes[] = { - { ARIZONA_ACCDET_SRC, 1 << ARIZONA_MICD_BIAS_SRC_SHIFT, 0 }, { 0, 2 << ARIZONA_MICD_BIAS_SRC_SHIFT, 1 }, + { ARIZONA_ACCDET_SRC, 1 << ARIZONA_MICD_BIAS_SRC_SHIFT, 0 }, }; static struct { @@ -118,10 +121,6 @@ static void arizona_start_mic(struct arizona_extcon_info *info) bool change; int ret; - info->detecting = true; - info->mic = false; - info->jack_flips = 0; - /* Microphone detection can't use idle mode */ pm_runtime_get(info->dev); @@ -311,12 +310,80 @@ 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) +{ + struct arizona *arizona = info->arizona; + int ret; + + /* + * If we're using HPDET for accessory identification we need + * to take multiple measurements, step through them in sequence. + */ + 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, 0); + regmap_update_bits(arizona->regmap, + ARIZONA_HEADPHONE_DETECT_1, + ARIZONA_HP_POLL, ARIZONA_HP_POLL); + return -EAGAIN; + } + + /* OK, got both. Now, compare... */ + dev_dbg(arizona->dev, "HPDET measured %d %d\n", + info->hpdet_res[0], info->hpdet_res[1]); + + if (info->hpdet_res[0] > info->hpdet_res[1] * 2) { + dev_dbg(arizona->dev, "Detected mic\n"); + info->mic = true; + ret = extcon_set_cable_state_(&info->edev, + ARIZONA_CABLE_MICROPHONE, + true); + if (ret != 0) { + dev_err(arizona->dev, + "Failed to report mic: %d\n", ret); + } + + /* Take the headphone impedance for the main report */ + *reading = info->hpdet_res[1]; + } else { + dev_dbg(arizona->dev, "Detected headphone\n"); + } + + /* Make sure everything is reset back to the real polarity */ + regmap_update_bits(arizona->regmap, + ARIZONA_ACCESSORY_DETECT_MODE_1, + ARIZONA_ACCDET_SRC, + info->micd_modes[0].src); + } + + return 0; +} + static irqreturn_t arizona_hpdet_irq(int irq, void *data) { struct arizona_extcon_info *info = data; struct arizona *arizona = info->arizona; int report = ARIZONA_CABLE_HEADPHONE; - int ret; + int ret, reading; mutex_lock(&info->lock); @@ -344,14 +411,23 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data) } else if (ret < 0) { goto done; } + reading = ret; /* Reset back to starting range */ regmap_update_bits(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1, - ARIZONA_HP_IMPEDANCE_RANGE_MASK, 0); + ARIZONA_HP_IMPEDANCE_RANGE_MASK | ARIZONA_HP_POLL, + 0); + + ret = arizona_hpdet_do_id(info, &reading); + if (ret == -EAGAIN) { + goto out; + } else if (ret < 0) { + goto done; + } /* Report high impedence cables as line outputs */ - if (ret >= 5000) + if (reading >= 5000) report = ARIZONA_CABLE_LINEOUT; else report = ARIZONA_CABLE_HEADPHONE; @@ -370,8 +446,6 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data) dev_warn(arizona->dev, "Failed to undo magic: %d\n", ret); done: - regmap_update_bits(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1, - ARIZONA_HP_POLL, 0); /* Revert back to MICDET mode */ regmap_update_bits(arizona->regmap, @@ -451,8 +525,58 @@ err: info->hpdet_active = false; } + +static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info) +{ + struct arizona *arizona = info->arizona; + int ret; + + dev_dbg(arizona->dev, "Starting identification via HPDET\n"); + + /* Make sure we keep the device enabled during the measurement */ + pm_runtime_get(info->dev); + + info->hpdet_active = true; + + ret = regmap_update_bits(arizona->regmap, 0x225, 0x4000, 0x4000); + if (ret != 0) + dev_warn(arizona->dev, "Failed to do magic: %d\n", ret); + + ret = regmap_update_bits(arizona->regmap, 0x226, 0x4000, 0x4000); + if (ret != 0) + dev_warn(arizona->dev, "Failed to do magic: %d\n", ret); + + ret = regmap_update_bits(arizona->regmap, + ARIZONA_ACCESSORY_DETECT_MODE_1, + ARIZONA_ACCDET_SRC | ARIZONA_ACCDET_MODE_MASK, + info->micd_modes[0].src | + ARIZONA_ACCDET_MODE_HPL); + if (ret != 0) { + dev_err(arizona->dev, "Failed to set HPDETL mode: %d\n", ret); + 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; + } + + return; + +err: + regmap_update_bits(arizona->regmap, ARIZONA_ACCESSORY_DETECT_MODE_1, + ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC); + + /* Just report headphone */ + ret = extcon_update_state(&info->edev, + 1 << ARIZONA_CABLE_HEADPHONE, + 1 << ARIZONA_CABLE_HEADPHONE); + if (ret != 0) + dev_err(arizona->dev, "Failed to report headphone: %d\n", ret); + info->hpdet_active = false; } @@ -612,12 +736,24 @@ static irqreturn_t arizona_jackdet(int irq, void *data) dev_err(arizona->dev, "Mechanical report failed: %d\n", ret); - arizona_start_mic(info); + if (!arizona->pdata.hpdet_acc_id) { + info->detecting = true; + info->mic = false; + info->jack_flips = 0; + + arizona_start_mic(info); + } else { + arizona_start_hpdet_acc_id(info); + } } else { dev_dbg(arizona->dev, "Detected jack removal\n"); arizona_stop_mic(info); + info->num_hpdet_res = 0; + for (i = 0; i < ARRAY_SIZE(info->hpdet_res); i++) + info->hpdet_res[i] = 0; + info->mic = false; for (i = 0; i < ARIZONA_NUM_BUTTONS; i++) input_report_key(info->input, @@ -665,7 +801,6 @@ static int arizona_extcon_probe(struct platform_device *pdev) mutex_init(&info->lock); info->arizona = arizona; info->dev = &pdev->dev; - info->detecting = true; platform_set_drvdata(pdev, info); switch (arizona->type) { diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h index 0fc26a480be3..7c0878778357 100644 --- a/include/linux/mfd/arizona/pdata.h +++ b/include/linux/mfd/arizona/pdata.h @@ -99,6 +99,9 @@ struct arizona_pdata { /** GPIO5 is used for jack detection */ bool jd_gpio5; + /** Use the headphone detect circuit to identify the accessory */ + bool hpdet_acc_id; + /** GPIO for mic detection polarity */ int micd_pol_gpio; -- cgit From 1eda6aa7ce101b59dfd91abef2c8b0e51e96e199 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 11 Jan 2013 08:55:54 +0900 Subject: extcon: arizona: Support direct microphone measurement via HPDET With some GPIO control it is possible to detect microphones in a wider range of configurations by directly measuring the microphone impedance when the HPDET method cannot distinguish between the behaviour of the two grounds. Allow a GPIO to be provided in platform data and use it to implement this behaviour. Signed-off-by: Mark Brown Signed-off-by: Chanwoo Choi Signed-off-by: Myungjoo Ham --- drivers/extcon/extcon-arizona.c | 50 +++++++++++++++++++++++++++++++++++---- include/linux/mfd/arizona/pdata.h | 3 +++ 2 files changed, 48 insertions(+), 5 deletions(-) (limited to 'include/linux/mfd') diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index ba9525ee4706..de141f72c8e5 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -56,7 +56,7 @@ struct arizona_extcon_info { bool hpdet_active; int num_hpdet_res; - unsigned int hpdet_res[2]; + unsigned int hpdet_res[3]; bool mic; bool detecting; @@ -313,6 +313,7 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info) static int arizona_hpdet_do_id(struct arizona_extcon_info *info, int *reading) { struct arizona *arizona = info->arizona; + int id_gpio = arizona->pdata.hpdet_id_gpio; int ret; /* @@ -338,9 +339,27 @@ static int arizona_hpdet_do_id(struct arizona_extcon_info *info, int *reading) 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, 0); + 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))) { + dev_dbg(arizona->dev, "Measuring mic\n"); + + regmap_update_bits(arizona->regmap, + ARIZONA_ACCESSORY_DETECT_MODE_1, + ARIZONA_ACCDET_MODE_MASK | + ARIZONA_ACCDET_SRC, + ARIZONA_ACCDET_MODE_HPR | + info->micd_modes[0].src); + + gpio_set_value_cansleep(id_gpio, 1); + regmap_update_bits(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1, ARIZONA_HP_POLL, ARIZONA_HP_POLL); @@ -348,10 +367,16 @@ 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\n", - info->hpdet_res[0], info->hpdet_res[1]); + dev_dbg(arizona->dev, "HPDET measured %d %d %d\n", + info->hpdet_res[0], info->hpdet_res[1], + info->hpdet_res[2]); - if (info->hpdet_res[0] > info->hpdet_res[1] * 2) { + /* + * Either the two grounds measure differently or we + * measure the mic as high impedance. + */ + if ((info->hpdet_res[0] > info->hpdet_res[1] * 2) || + (id_gpio && info->hpdet_res[2] > 10)) { dev_dbg(arizona->dev, "Detected mic\n"); info->mic = true; ret = extcon_set_cable_state_(&info->edev, @@ -382,6 +407,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data) { struct arizona_extcon_info *info = data; struct arizona *arizona = info->arizona; + int id_gpio = arizona->pdata.hpdet_id_gpio; int report = ARIZONA_CABLE_HEADPHONE; int ret, reading; @@ -446,6 +472,8 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data) dev_warn(arizona->dev, "Failed to undo magic: %d\n", ret); done: + if (id_gpio) + gpio_set_value_cansleep(id_gpio, 0); /* Revert back to MICDET mode */ regmap_update_bits(arizona->regmap, @@ -854,6 +882,18 @@ static int arizona_extcon_probe(struct platform_device *pdev) } } + if (arizona->pdata.hpdet_id_gpio > 0) { + ret = devm_gpio_request_one(&pdev->dev, + arizona->pdata.hpdet_id_gpio, + GPIOF_OUT_INIT_LOW, + "HPDET"); + if (ret != 0) { + dev_err(arizona->dev, "Failed to request GPIO%d: %d\n", + arizona->pdata.hpdet_id_gpio, ret); + goto err_register; + } + } + if (arizona->pdata.micd_bias_start_time) regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1, ARIZONA_MICD_BIAS_STARTTIME_MASK, diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h index 7c0878778357..bcbe4fda87cb 100644 --- a/include/linux/mfd/arizona/pdata.h +++ b/include/linux/mfd/arizona/pdata.h @@ -102,6 +102,9 @@ struct arizona_pdata { /** Use the headphone detect circuit to identify the accessory */ bool hpdet_acc_id; + /** GPIO used for mic isolation with HPDET */ + int hpdet_id_gpio; + /** GPIO for mic detection polarity */ int micd_pol_gpio; -- cgit From 689557d3c7045320958d5bc4141088f7b4dff7ba Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 11 Jan 2013 08:55:57 +0900 Subject: mfd: wm5102: Add microphone clamp control registers Signed-off-by: Mark Brown Signed-off-by: Chanwoo Choi Signed-off-by: Myungjoo Ham --- drivers/mfd/wm5102-tables.c | 7 +++++++ include/linux/mfd/arizona/registers.h | 8 ++++++++ 2 files changed, 15 insertions(+) (limited to 'include/linux/mfd') diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index 311cc3657b0b..a396a3ae2045 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -84,6 +84,12 @@ int wm5102_patch(struct arizona *arizona) } static const struct regmap_irq wm5102_aod_irqs[ARIZONA_NUM_IRQ] = { + [ARIZONA_IRQ_MICD_CLAMP_FALL] = { + .mask = ARIZONA_MICD_CLAMP_FALL_EINT1 + }, + [ARIZONA_IRQ_MICD_CLAMP_RISE] = { + .mask = ARIZONA_MICD_CLAMP_RISE_EINT1 + }, [ARIZONA_IRQ_GP5_FALL] = { .mask = ARIZONA_GP5_FALL_EINT1 }, [ARIZONA_IRQ_GP5_RISE] = { .mask = ARIZONA_GP5_RISE_EINT1 }, [ARIZONA_IRQ_JD_FALL] = { .mask = ARIZONA_JD1_FALL_EINT1 }, @@ -312,6 +318,7 @@ static const struct reg_default wm5102_reg_default[] = { { 0x0000021A, 0x01A6 }, /* R538 - Mic Bias Ctrl 3 */ { 0x00000293, 0x0000 }, /* R659 - Accessory Detect Mode 1 */ { 0x0000029B, 0x0020 }, /* R667 - Headphone Detect 1 */ + { 0x000002A2, 0x0000 }, /* R674 - Micd clamp control */ { 0x000002A3, 0x1102 }, /* R675 - Mic Detect 1 */ { 0x000002A4, 0x009F }, /* R676 - Mic Detect 2 */ { 0x000002A5, 0x0000 }, /* R677 - Mic Detect 3 */ diff --git a/include/linux/mfd/arizona/registers.h b/include/linux/mfd/arizona/registers.h index e9150533e701..79e9dd4073d8 100644 --- a/include/linux/mfd/arizona/registers.h +++ b/include/linux/mfd/arizona/registers.h @@ -1196,6 +1196,14 @@ /* * R64 (0x40) - Wake control */ +#define ARIZONA_WKUP_MICD_CLAMP_FALL 0x0080 /* WKUP_MICD_CLAMP_FALL */ +#define ARIZONA_WKUP_MICD_CLAMP_FALL_MASK 0x0080 /* WKUP_MICD_CLAMP_FALL */ +#define ARIZONA_WKUP_MICD_CLAMP_FALL_SHIFT 7 /* WKUP_MICD_CLAMP_FALL */ +#define ARIZONA_WKUP_MICD_CLAMP_FALL_WIDTH 1 /* WKUP_MICD_CLAMP_FALL */ +#define ARIZONA_WKUP_MICD_CLAMP_RISE 0x0040 /* WKUP_MICD_CLAMP_RISE */ +#define ARIZONA_WKUP_MICD_CLAMP_RISE_MASK 0x0040 /* WKUP_MICD_CLAMP_RISE */ +#define ARIZONA_WKUP_MICD_CLAMP_RISE_SHIFT 6 /* WKUP_MICD_CLAMP_RISE */ +#define ARIZONA_WKUP_MICD_CLAMP_RISE_WIDTH 1 /* WKUP_MICD_CLAMP_RISE */ #define ARIZONA_WKUP_GP5_FALL 0x0020 /* WKUP_GP5_FALL */ #define ARIZONA_WKUP_GP5_FALL_MASK 0x0020 /* WKUP_GP5_FALL */ #define ARIZONA_WKUP_GP5_FALL_SHIFT 5 /* WKUP_GP5_FALL */ -- cgit From 2e033db5ddf299de2ae568919d78b0258a5a6423 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 21 Jan 2013 17:36:33 +0900 Subject: extcon: arizona: Support additional configuration of microphone detection Allow systems to tune detection rate and debounce suitably for their mechanical parameters. Signed-off-by: Mark Brown --- drivers/extcon/extcon-arizona.c | 12 ++++++++++++ include/linux/mfd/arizona/pdata.h | 6 ++++++ 2 files changed, 18 insertions(+) (limited to 'include/linux/mfd') diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index ab8b9c7359fb..d7e1047ad68e 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -907,6 +907,18 @@ static int arizona_extcon_probe(struct platform_device *pdev) arizona->pdata.micd_bias_start_time << ARIZONA_MICD_BIAS_STARTTIME_SHIFT); + if (arizona->pdata.micd_rate) + regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1, + ARIZONA_MICD_RATE_MASK, + arizona->pdata.micd_rate + << ARIZONA_MICD_RATE_SHIFT); + + if (arizona->pdata.micd_dbtime) + regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1, + ARIZONA_MICD_DBTIME_MASK, + arizona->pdata.micd_dbtime + << ARIZONA_MICD_DBTIME_SHIFT); + /* * If we have a clamp use it, activating in conjunction with * GPIO5 if that is connected for jack detect operation. diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h index bcbe4fda87cb..2f5f08e10b79 100644 --- a/include/linux/mfd/arizona/pdata.h +++ b/include/linux/mfd/arizona/pdata.h @@ -111,6 +111,12 @@ struct arizona_pdata { /** Mic detect ramp rate */ int micd_bias_start_time; + /** Mic detect sample rate */ + int micd_rate; + + /** Mic detect debounce level */ + int micd_dbtime; + /** Headset polarity configurations */ struct arizona_micd_config *micd_configs; int num_micd_configs; -- cgit From bbbd46e3d7fcdf1c8362bf1c83bcc08a93676cc9 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 10 Jan 2013 19:38:43 +0000 Subject: extcon: arizona: Use regulated mode for microphone supply when detecting When starting microphone detection some headsets should be exposed to the fully regulated microphone bias in order to ensure that they behave in an optimal fashion. Signed-off-by: Mark Brown --- drivers/extcon/Kconfig | 2 +- drivers/extcon/extcon-arizona.c | 93 +++++++++++++++++++++++++++++++++++++++ include/linux/mfd/arizona/pdata.h | 3 ++ 3 files changed, 97 insertions(+), 1 deletion(-) (limited to 'include/linux/mfd') diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index 07122a9ef36e..9377050e5335 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -47,7 +47,7 @@ config EXTCON_MAX8997 config EXTCON_ARIZONA tristate "Wolfson Arizona EXTCON support" - depends on MFD_ARIZONA && INPUT + depends on MFD_ARIZONA && INPUT && SND_SOC help Say Y here to enable support for external accessory detection with Wolfson Arizona devices. These are audio CODECs with diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index d7e1047ad68e..aa724314677a 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -27,6 +27,8 @@ #include #include +#include + #include #include #include @@ -113,6 +115,52 @@ static void arizona_extcon_set_mode(struct arizona_extcon_info *info, int mode) dev_dbg(arizona->dev, "Set jack polarity to %d\n", mode); } +static const char *arizona_extcon_get_micbias(struct arizona_extcon_info *info) +{ + switch (info->micd_modes[0].bias >> ARIZONA_MICD_BIAS_SRC_SHIFT) { + case 1: + return "MICBIAS1"; + case 2: + return "MICBIAS2"; + case 3: + return "MICBIAS3"; + default: + return "MICVDD"; + } +} + +static void arizona_extcon_pulse_micbias(struct arizona_extcon_info *info) +{ + struct arizona *arizona = info->arizona; + const char *widget = arizona_extcon_get_micbias(info); + struct snd_soc_dapm_context *dapm = arizona->dapm; + int ret; + + mutex_lock(&dapm->card->dapm_mutex); + + ret = snd_soc_dapm_force_enable_pin(dapm, widget); + if (ret != 0) + dev_warn(arizona->dev, "Failed to enable %s: %d\n", + widget, ret); + + mutex_unlock(&dapm->card->dapm_mutex); + + snd_soc_dapm_sync(dapm); + + if (!arizona->pdata.micd_force_micbias) { + mutex_lock(&dapm->card->dapm_mutex); + + ret = snd_soc_dapm_disable_pin(arizona->dapm, widget); + if (ret != 0) + dev_warn(arizona->dev, "Failed to disable %s: %d\n", + widget, ret); + + mutex_unlock(&dapm->card->dapm_mutex); + + snd_soc_dapm_sync(dapm); + } +} + static void arizona_start_mic(struct arizona_extcon_info *info) { struct arizona *arizona = info->arizona; @@ -122,6 +170,15 @@ static void arizona_start_mic(struct arizona_extcon_info *info) /* Microphone detection can't use idle mode */ pm_runtime_get(info->dev); + if (info->detecting) { + ret = regulator_allow_bypass(info->micvdd, false); + if (ret != 0) { + dev_err(arizona->dev, + "Failed to regulate MICVDD: %d\n", + ret); + } + } + ret = regulator_enable(info->micvdd); if (ret != 0) { dev_err(arizona->dev, "Failed to enable MICVDD: %d\n", @@ -138,6 +195,8 @@ static void arizona_start_mic(struct arizona_extcon_info *info) ARIZONA_ACCESSORY_DETECT_MODE_1, ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC); + arizona_extcon_pulse_micbias(info); + regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1, ARIZONA_MICD_ENA, ARIZONA_MICD_ENA, &change); @@ -150,18 +209,39 @@ static void arizona_start_mic(struct arizona_extcon_info *info) static void arizona_stop_mic(struct arizona_extcon_info *info) { struct arizona *arizona = info->arizona; + const char *widget = arizona_extcon_get_micbias(info); + struct snd_soc_dapm_context *dapm = arizona->dapm; bool change; + int ret; regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1, ARIZONA_MICD_ENA, 0, &change); + mutex_lock(&dapm->card->dapm_mutex); + + ret = snd_soc_dapm_disable_pin(dapm, widget); + if (ret != 0) + dev_warn(arizona->dev, + "Failed to disable %s: %d\n", + widget, ret); + + mutex_unlock(&dapm->card->dapm_mutex); + + snd_soc_dapm_sync(dapm); + if (info->micd_reva) { regmap_write(arizona->regmap, 0x80, 0x3); regmap_write(arizona->regmap, 0x294, 2); regmap_write(arizona->regmap, 0x80, 0x0); } + ret = regulator_allow_bypass(info->micvdd, true); + if (ret != 0) { + dev_err(arizona->dev, "Failed to bypass MICVDD: %d\n", + ret); + } + if (change) { regulator_disable(info->micvdd); pm_runtime_mark_last_busy(info->dev); @@ -564,6 +644,8 @@ static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info) info->hpdet_active = true; + arizona_extcon_pulse_micbias(info); + ret = regmap_update_bits(arizona->regmap, 0x225, 0x4000, 0x4000); if (ret != 0) dev_warn(arizona->dev, "Failed to do magic: %d\n", ret); @@ -649,6 +731,13 @@ static irqreturn_t arizona_micdet(int irq, void *data) dev_err(arizona->dev, "Headset report failed: %d\n", ret); + /* Don't need to regulate for button detection */ + ret = regulator_allow_bypass(info->micvdd, false); + if (ret != 0) { + dev_err(arizona->dev, "Failed to bypass MICVDD: %d\n", + ret); + } + info->mic = true; info->detecting = false; goto handled; @@ -716,6 +805,7 @@ static irqreturn_t arizona_micdet(int irq, void *data) input_report_key(info->input, arizona_lvl_to_key[i].report, 0); input_sync(info->input); + arizona_extcon_pulse_micbias(info); } handled: @@ -817,6 +907,9 @@ static int arizona_extcon_probe(struct platform_device *pdev) int jack_irq_fall, jack_irq_rise; int ret, mode, i; + if (!arizona->dapm || !arizona->dapm->card) + return -EPROBE_DEFER; + pdata = dev_get_platdata(arizona->dev); info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h index 2f5f08e10b79..f8241753415c 100644 --- a/include/linux/mfd/arizona/pdata.h +++ b/include/linux/mfd/arizona/pdata.h @@ -117,6 +117,9 @@ struct arizona_pdata { /** Mic detect debounce level */ int micd_dbtime; + /** Force MICBIAS on for mic detect */ + bool micd_force_micbias; + /** Headset polarity configurations */ struct arizona_micd_config *micd_configs; int num_micd_configs; -- cgit From 5d9ab708200fefc3ec6e4454c65584d14ce716b0 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 5 Feb 2013 10:13:38 +0000 Subject: extcon: arizona: Clear _trig_sts bits after jack detection It is important to clear the wake trigger status bits otherwise DCVDD will be held high independent of the state of the LDOENA line. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/extcon/extcon-arizona.c | 7 +++++++ include/linux/mfd/arizona/registers.h | 8 ++++++++ 2 files changed, 15 insertions(+) (limited to 'include/linux/mfd') diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index cfd206c4797c..aeaf217a05ee 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -939,6 +939,13 @@ static irqreturn_t arizona_jackdet(int irq, void *data) ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB); } + /* 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 | + ARIZONA_MICD_CLAMP_RISE_TRIG_STS | + ARIZONA_JD1_FALL_TRIG_STS | + ARIZONA_JD1_RISE_TRIG_STS); + mutex_unlock(&info->lock); pm_runtime_mark_last_busy(info->dev); diff --git a/include/linux/mfd/arizona/registers.h b/include/linux/mfd/arizona/registers.h index 79e9dd4073d8..188d89abd963 100644 --- a/include/linux/mfd/arizona/registers.h +++ b/include/linux/mfd/arizona/registers.h @@ -5267,6 +5267,14 @@ /* * R3408 (0xD50) - AOD wkup and trig */ +#define ARIZONA_MICD_CLAMP_FALL_TRIG_STS 0x0080 /* MICD_CLAMP_FALL_TRIG_STS */ +#define ARIZONA_MICD_CLAMP_FALL_TRIG_STS_MASK 0x0080 /* MICD_CLAMP_FALL_TRIG_STS */ +#define ARIZONA_MICD_CLAMP_FALL_TRIG_STS_SHIFT 7 /* MICD_CLAMP_FALL_TRIG_STS */ +#define ARIZONA_MICD_CLAMP_FALL_TRIG_STS_WIDTH 1 /* MICD_CLAMP_FALL_TRIG_STS */ +#define ARIZONA_MICD_CLAMP_RISE_TRIG_STS 0x0040 /* MICD_CLAMP_RISE_TRIG_STS */ +#define ARIZONA_MICD_CLAMP_RISE_TRIG_STS_MASK 0x0040 /* MICD_CLAMP_RISE_TRIG_STS */ +#define ARIZONA_MICD_CLAMP_RISE_TRIG_STS_SHIFT 6 /* MICD_CLAMP_RISE_TRIG_STS */ +#define ARIZONA_MICD_CLAMP_RISE_TRIG_STS_WIDTH 1 /* MICD_CLAMP_RISE_TRIG_STS */ #define ARIZONA_GP5_FALL_TRIG_STS 0x0020 /* GP5_FALL_TRIG_STS */ #define ARIZONA_GP5_FALL_TRIG_STS_MASK 0x0020 /* GP5_FALL_TRIG_STS */ #define ARIZONA_GP5_FALL_TRIG_STS_SHIFT 5 /* GP5_FALL_TRIG_STS */ -- cgit From e3e5bc02d2365d3e09164fd9a559011399ca2a4f Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Tue, 12 Feb 2013 20:44:19 +0900 Subject: extcon: max8997: Move defined constant to header file This patch move defined constants to header file(max77693-private.h) because of mask/unmask selectively interrupt of MUIC device according to attribute of H/W board. Signed-off-by: Chanwoo Choi Signed-off-by: Myungjoo Ham --- drivers/extcon/extcon-max8997.c | 92 +++++++++++++------------------------ include/linux/mfd/max8997-private.h | 49 ++++++++++++++++++++ 2 files changed, 80 insertions(+), 61 deletions(-) (limited to 'include/linux/mfd') diff --git a/drivers/extcon/extcon-max8997.c b/drivers/extcon/extcon-max8997.c index d16090ddd65a..0fb1d48b0741 100644 --- a/drivers/extcon/extcon-max8997.c +++ b/drivers/extcon/extcon-max8997.c @@ -30,51 +30,6 @@ #define DEV_NAME "max8997-muic" -/* MAX8997-MUIC STATUS1 register */ -#define STATUS1_ADC_SHIFT 0 -#define STATUS1_ADCLOW_SHIFT 5 -#define STATUS1_ADCERR_SHIFT 6 -#define STATUS1_ADC_MASK (0x1f << STATUS1_ADC_SHIFT) -#define STATUS1_ADCLOW_MASK (0x1 << STATUS1_ADCLOW_SHIFT) -#define STATUS1_ADCERR_MASK (0x1 << STATUS1_ADCERR_SHIFT) - -/* MAX8997-MUIC STATUS2 register */ -#define STATUS2_CHGTYP_SHIFT 0 -#define STATUS2_CHGDETRUN_SHIFT 3 -#define STATUS2_DCDTMR_SHIFT 4 -#define STATUS2_DBCHG_SHIFT 5 -#define STATUS2_VBVOLT_SHIFT 6 -#define STATUS2_CHGTYP_MASK (0x7 << STATUS2_CHGTYP_SHIFT) -#define STATUS2_CHGDETRUN_MASK (0x1 << STATUS2_CHGDETRUN_SHIFT) -#define STATUS2_DCDTMR_MASK (0x1 << STATUS2_DCDTMR_SHIFT) -#define STATUS2_DBCHG_MASK (0x1 << STATUS2_DBCHG_SHIFT) -#define STATUS2_VBVOLT_MASK (0x1 << STATUS2_VBVOLT_SHIFT) - -/* MAX8997-MUIC STATUS3 register */ -#define STATUS3_OVP_SHIFT 2 -#define STATUS3_OVP_MASK (0x1 << STATUS3_OVP_SHIFT) - -/* MAX8997-MUIC CONTROL1 register */ -#define COMN1SW_SHIFT 0 -#define COMP2SW_SHIFT 3 -#define COMN1SW_MASK (0x7 << COMN1SW_SHIFT) -#define COMP2SW_MASK (0x7 << COMP2SW_SHIFT) -#define SW_MASK (COMP2SW_MASK | COMN1SW_MASK) - -#define MAX8997_SW_USB ((1 << COMP2SW_SHIFT) | (1 << COMN1SW_SHIFT)) -#define MAX8997_SW_AUDIO ((2 << COMP2SW_SHIFT) | (2 << COMN1SW_SHIFT)) -#define MAX8997_SW_UART ((3 << COMP2SW_SHIFT) | (3 << COMN1SW_SHIFT)) -#define MAX8997_SW_OPEN ((0 << COMP2SW_SHIFT) | (0 << COMN1SW_SHIFT)) - -#define MAX8997_ADC_GROUND 0x00 -#define MAX8997_ADC_MHL 0x01 -#define MAX8997_ADC_JIG_USB_1 0x18 -#define MAX8997_ADC_JIG_USB_2 0x19 -#define MAX8997_ADC_DESKDOCK 0x1a -#define MAX8997_ADC_JIG_UART 0x1c -#define MAX8997_ADC_CARDOCK 0x1d -#define MAX8997_ADC_OPEN 0x1f - struct max8997_muic_irq { unsigned int irq; const char *name; @@ -109,17 +64,32 @@ struct max8997_muic_info { struct extcon_dev *edev; }; +enum { + EXTCON_CABLE_USB = 0, + EXTCON_CABLE_USB_HOST, + EXTCON_CABLE_TA, + EXTCON_CABLE_FAST_CHARGER, + EXTCON_CABLE_SLOW_CHARGER, + EXTCON_CABLE_CHARGE_DOWNSTREAM, + EXTCON_CABLE_MHL, + EXTCON_CABLE_DOCK_DESK, + EXTCON_CABLE_DOCK_CARD, + EXTCON_CABLE_JIG, + + _EXTCON_CABLE_NUM, +}; + static const char *max8997_extcon_cable[] = { - [0] = "USB", - [1] = "USB-Host", - [2] = "TA", - [3] = "Fast-charger", - [4] = "Slow-charger", - [5] = "Charge-downstream", - [6] = "MHL", - [7] = "Dock-desk", - [8] = "Dock-card", - [9] = "JIG", + [EXTCON_CABLE_USB] = "USB", + [EXTCON_CABLE_USB_HOST] = "USB-Host", + [EXTCON_CABLE_TA] = "TA", + [EXTCON_CABLE_FAST_CHARGER] = "Fast-charger", + [EXTCON_CABLE_SLOW_CHARGER] = "Slow-charger", + [EXTCON_CABLE_CHARGE_DOWNSTREAM] = "Charge-downstream", + [EXTCON_CABLE_MHL] = "MHL", + [EXTCON_CABLE_DOCK_DESK] = "Dock-Desk", + [EXTCON_CABLE_DOCK_CARD] = "Dock-Card", + [EXTCON_CABLE_JIG] = "JIG", NULL, }; @@ -132,8 +102,8 @@ static int max8997_muic_handle_usb(struct max8997_muic_info *info, if (usb_type == MAX8997_USB_HOST) { /* switch to USB */ ret = max8997_update_reg(info->muic, MAX8997_MUIC_REG_CONTROL1, - attached ? MAX8997_SW_USB : MAX8997_SW_OPEN, - SW_MASK); + attached ? CONTROL1_SW_USB : CONTROL1_SW_OPEN, + CONTROL1_SW_MASK); if (ret) { dev_err(info->dev, "failed to update muic register\n"); goto out; @@ -163,8 +133,8 @@ static int max8997_muic_handle_dock(struct max8997_muic_info *info, /* switch to AUDIO */ ret = max8997_update_reg(info->muic, MAX8997_MUIC_REG_CONTROL1, - attached ? MAX8997_SW_AUDIO : MAX8997_SW_OPEN, - SW_MASK); + attached ? CONTROL1_SW_AUDIO : CONTROL1_SW_OPEN, + CONTROL1_SW_MASK); if (ret) { dev_err(info->dev, "failed to update muic register\n"); goto out; @@ -192,8 +162,8 @@ static int max8997_muic_handle_jig_uart(struct max8997_muic_info *info, /* switch to UART */ ret = max8997_update_reg(info->muic, MAX8997_MUIC_REG_CONTROL1, - attached ? MAX8997_SW_UART : MAX8997_SW_OPEN, - SW_MASK); + attached ? CONTROL1_SW_UART : CONTROL1_SW_OPEN, + CONTROL1_SW_MASK); if (ret) { dev_err(info->dev, "failed to update muic register\n"); goto out; diff --git a/include/linux/mfd/max8997-private.h b/include/linux/mfd/max8997-private.h index 6ae21bf47d64..acf42e960320 100644 --- a/include/linux/mfd/max8997-private.h +++ b/include/linux/mfd/max8997-private.h @@ -194,6 +194,55 @@ enum max8997_muic_reg { MAX8997_MUIC_REG_END = 0xf, }; +/* MAX8997-MUIC STATUS1 register */ +#define STATUS1_ADC_SHIFT 0 +#define STATUS1_ADCLOW_SHIFT 5 +#define STATUS1_ADCERR_SHIFT 6 +#define STATUS1_ADC_MASK (0x1f << STATUS1_ADC_SHIFT) +#define STATUS1_ADCLOW_MASK (0x1 << STATUS1_ADCLOW_SHIFT) +#define STATUS1_ADCERR_MASK (0x1 << STATUS1_ADCERR_SHIFT) + +/* MAX8997-MUIC STATUS2 register */ +#define STATUS2_CHGTYP_SHIFT 0 +#define STATUS2_CHGDETRUN_SHIFT 3 +#define STATUS2_DCDTMR_SHIFT 4 +#define STATUS2_DBCHG_SHIFT 5 +#define STATUS2_VBVOLT_SHIFT 6 +#define STATUS2_CHGTYP_MASK (0x7 << STATUS2_CHGTYP_SHIFT) +#define STATUS2_CHGDETRUN_MASK (0x1 << STATUS2_CHGDETRUN_SHIFT) +#define STATUS2_DCDTMR_MASK (0x1 << STATUS2_DCDTMR_SHIFT) +#define STATUS2_DBCHG_MASK (0x1 << STATUS2_DBCHG_SHIFT) +#define STATUS2_VBVOLT_MASK (0x1 << STATUS2_VBVOLT_SHIFT) + +/* MAX8997-MUIC STATUS3 register */ +#define STATUS3_OVP_SHIFT 2 +#define STATUS3_OVP_MASK (0x1 << STATUS3_OVP_SHIFT) + +/* MAX8997-MUIC CONTROL1 register */ +#define COMN1SW_SHIFT 0 +#define COMP2SW_SHIFT 3 +#define COMN1SW_MASK (0x7 << COMN1SW_SHIFT) +#define COMP2SW_MASK (0x7 << COMP2SW_SHIFT) +#define CONTROL1_SW_MASK (COMP2SW_MASK | COMN1SW_MASK) + +#define CONTROL1_SW_USB ((1 << COMP2SW_SHIFT) \ + | (1 << COMN1SW_SHIFT)) +#define CONTROL1_SW_AUDIO ((2 << COMP2SW_SHIFT) \ + | (2 << COMN1SW_SHIFT)) +#define CONTROL1_SW_UART ((3 << COMP2SW_SHIFT) \ + | (3 << COMN1SW_SHIFT)) +#define CONTROL1_SW_OPEN ((0 << COMP2SW_SHIFT) \ + | (0 << COMN1SW_SHIFT)) + +#define MAX8997_ADC_GROUND 0x00 +#define MAX8997_ADC_MHL 0x01 +#define MAX8997_ADC_JIG_USB_1 0x18 +#define MAX8997_ADC_JIG_USB_2 0x19 +#define MAX8997_ADC_DESKDOCK 0x1a +#define MAX8997_ADC_JIG_UART 0x1c +#define MAX8997_ADC_CARDOCK 0x1d +#define MAX8997_ADC_OPEN 0x1f + enum max8997_haptic_reg { MAX8997_HAPTIC_REG_GENERAL = 0x00, MAX8997_HAPTIC_REG_CONF1 = 0x01, -- cgit From 07c70503a420d48402b3859e2c1c4c847a130a8b Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Wed, 13 Feb 2013 08:42:37 +0900 Subject: extcon: max8997: Remove duplicate code related to set H/W line path Signed-off-by: Chanwoo Choi Signed-off-by: Myungjoo Ham --- drivers/extcon/extcon-max8997.c | 62 ++++++++++++++++++++++++++++++------- include/linux/mfd/max8997-private.h | 19 +++++++++++- 2 files changed, 69 insertions(+), 12 deletions(-) (limited to 'include/linux/mfd') diff --git a/drivers/extcon/extcon-max8997.c b/drivers/extcon/extcon-max8997.c index 0fb1d48b0741..8739b50c2b36 100644 --- a/drivers/extcon/extcon-max8997.c +++ b/drivers/extcon/extcon-max8997.c @@ -94,16 +94,61 @@ static const char *max8997_extcon_cable[] = { NULL, }; +/* + * max8997_muic_set_path - Set hardware line according to attached cable + * @info: the instance including private data of max8997 MUIC + * @value: the path according to attached cable + * @attached: the state of cable (true:attached, false:detached) + * + * The max8997 MUIC device share outside H/W line among a varity of cables, + * so this function set internal path of H/W line according to the type of + * attached cable. + */ +static int max8997_muic_set_path(struct max8997_muic_info *info, + u8 val, bool attached) +{ + int ret = 0; + u8 ctrl1, ctrl2 = 0; + + if (attached) + ctrl1 = val; + else + ctrl1 = CONTROL1_SW_OPEN; + + ret = max8997_update_reg(info->muic, + MAX8997_MUIC_REG_CONTROL1, ctrl1, COMP_SW_MASK); + if (ret < 0) { + dev_err(info->dev, "failed to update MUIC register\n"); + return -EAGAIN; + } + + if (attached) + ctrl2 |= CONTROL2_CPEN_MASK; /* LowPwr=0, CPEn=1 */ + else + ctrl2 |= CONTROL2_LOWPWR_MASK; /* LowPwr=1, CPEn=0 */ + + ret = max8997_update_reg(info->muic, + MAX8997_MUIC_REG_CONTROL2, ctrl2, + CONTROL2_LOWPWR_MASK | CONTROL2_CPEN_MASK); + if (ret < 0) { + dev_err(info->dev, "failed to update MUIC register\n"); + return -EAGAIN; + } + + dev_info(info->dev, + "CONTROL1 : 0x%02x, CONTROL2 : 0x%02x, state : %s\n", + ctrl1, ctrl2, attached ? "attached" : "detached"); + + return 0; +} + static int max8997_muic_handle_usb(struct max8997_muic_info *info, enum max8997_muic_usb_type usb_type, bool attached) { int ret = 0; if (usb_type == MAX8997_USB_HOST) { - /* switch to USB */ - ret = max8997_update_reg(info->muic, MAX8997_MUIC_REG_CONTROL1, - attached ? CONTROL1_SW_USB : CONTROL1_SW_OPEN, - CONTROL1_SW_MASK); + ret = max8997_muic_set_path(info, CONTROL1_SW_USB, attached); if (ret) { dev_err(info->dev, "failed to update muic register\n"); goto out; @@ -131,10 +176,7 @@ static int max8997_muic_handle_dock(struct max8997_muic_info *info, { int ret = 0; - /* switch to AUDIO */ - ret = max8997_update_reg(info->muic, MAX8997_MUIC_REG_CONTROL1, - attached ? CONTROL1_SW_AUDIO : CONTROL1_SW_OPEN, - CONTROL1_SW_MASK); + ret = max8997_muic_set_path(info, CONTROL1_SW_AUDIO, attached); if (ret) { dev_err(info->dev, "failed to update muic register\n"); goto out; @@ -161,9 +203,7 @@ static int max8997_muic_handle_jig_uart(struct max8997_muic_info *info, int ret = 0; /* switch to UART */ - ret = max8997_update_reg(info->muic, MAX8997_MUIC_REG_CONTROL1, - attached ? CONTROL1_SW_UART : CONTROL1_SW_OPEN, - CONTROL1_SW_MASK); + ret = max8997_muic_set_path(info, CONTROL1_SW_UART, attached); if (ret) { dev_err(info->dev, "failed to update muic register\n"); goto out; diff --git a/include/linux/mfd/max8997-private.h b/include/linux/mfd/max8997-private.h index acf42e960320..010173a92274 100644 --- a/include/linux/mfd/max8997-private.h +++ b/include/linux/mfd/max8997-private.h @@ -223,7 +223,7 @@ enum max8997_muic_reg { #define COMP2SW_SHIFT 3 #define COMN1SW_MASK (0x7 << COMN1SW_SHIFT) #define COMP2SW_MASK (0x7 << COMP2SW_SHIFT) -#define CONTROL1_SW_MASK (COMP2SW_MASK | COMN1SW_MASK) +#define COMP_SW_MASK (COMP2SW_MASK | COMN1SW_MASK) #define CONTROL1_SW_USB ((1 << COMP2SW_SHIFT) \ | (1 << COMN1SW_SHIFT)) @@ -234,6 +234,23 @@ enum max8997_muic_reg { #define CONTROL1_SW_OPEN ((0 << COMP2SW_SHIFT) \ | (0 << COMN1SW_SHIFT)) +#define CONTROL2_LOWPWR_SHIFT (0) +#define CONTROL2_ADCEN_SHIFT (1) +#define CONTROL2_CPEN_SHIFT (2) +#define CONTROL2_SFOUTASRT_SHIFT (3) +#define CONTROL2_SFOUTORD_SHIFT (4) +#define CONTROL2_ACCDET_SHIFT (5) +#define CONTROL2_USBCPINT_SHIFT (6) +#define CONTROL2_RCPS_SHIFT (7) +#define CONTROL2_LOWPWR_MASK (0x1 << CONTROL2_LOWPWR_SHIFT) +#define CONTROL2_ADCEN_MASK (0x1 << CONTROL2_ADCEN_SHIFT) +#define CONTROL2_CPEN_MASK (0x1 << CONTROL2_CPEN_SHIFT) +#define CONTROL2_SFOUTASRT_MASK (0x1 << CONTROL2_SFOUTASRT_SHIFT) +#define CONTROL2_SFOUTORD_MASK (0x1 << CONTROL2_SFOUTORD_SHIFT) +#define CONTROL2_ACCDET_MASK (0x1 << CONTROL2_ACCDET_SHIFT) +#define CONTROL2_USBCPINT_MASK (0x1 << CONTROL2_USBCPINT_SHIFT) +#define CONTROL2_RCPS_MASK (0x1 << CONTROL2_RCPS_SHIFT) + #define MAX8997_ADC_GROUND 0x00 #define MAX8997_ADC_MHL 0x01 #define MAX8997_ADC_JIG_USB_1 0x18 -- cgit From 027fcd50500fd87847891d5c2f341c1f002de3e8 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Wed, 13 Feb 2013 08:50:00 +0900 Subject: extcon: max8997: Set default of ADC debounce time during initialization This patch set default of ADC Debounce Time(25ms) during probe step. Also, can possible change ADC Debounce Time according to H/W situation by using max8997_set_adc_debounce_time() Signed-off-by: Chanwoo Choi Signed-off-by: Myungjoo Ham --- drivers/extcon/extcon-max8997.c | 42 +++++++++++++++++++++++++++++++++++++ include/linux/mfd/max8997-private.h | 7 +++++++ 2 files changed, 49 insertions(+) (limited to 'include/linux/mfd') diff --git a/drivers/extcon/extcon-max8997.c b/drivers/extcon/extcon-max8997.c index 8739b50c2b36..3206daaf8e08 100644 --- a/drivers/extcon/extcon-max8997.c +++ b/drivers/extcon/extcon-max8997.c @@ -30,6 +30,13 @@ #define DEV_NAME "max8997-muic" +enum max8997_muic_adc_debounce_time { + ADC_DEBOUNCE_TIME_0_5MS = 0, /* 0.5ms */ + ADC_DEBOUNCE_TIME_10MS, /* 10ms */ + ADC_DEBOUNCE_TIME_25MS, /* 25ms */ + ADC_DEBOUNCE_TIME_38_62MS, /* 38.62ms */ +}; + struct max8997_muic_irq { unsigned int irq; const char *name; @@ -94,6 +101,38 @@ static const char *max8997_extcon_cable[] = { NULL, }; +/* + * max8997_muic_set_debounce_time - Set the debounce time of ADC + * @info: the instance including private data of max8997 MUIC + * @time: the debounce time of ADC + */ +static int max8997_muic_set_debounce_time(struct max8997_muic_info *info, + enum max8997_muic_adc_debounce_time time) +{ + int ret; + + switch (time) { + case ADC_DEBOUNCE_TIME_0_5MS: + case ADC_DEBOUNCE_TIME_10MS: + case ADC_DEBOUNCE_TIME_25MS: + case ADC_DEBOUNCE_TIME_38_62MS: + ret = max8997_update_reg(info->muic, + MAX8997_MUIC_REG_CONTROL3, + time << CONTROL3_ADCDBSET_SHIFT, + CONTROL3_ADCDBSET_MASK); + if (ret) { + dev_err(info->dev, "failed to set ADC debounce time\n"); + return -EAGAIN; + } + break; + default: + dev_err(info->dev, "invalid ADC debounce time\n"); + return -EINVAL; + } + + return 0; +}; + /* * max8997_muic_set_path - Set hardware line according to attached cable * @info: the instance including private data of max8997 MUIC @@ -507,6 +546,9 @@ static int max8997_muic_probe(struct platform_device *pdev) } } + /* Set ADC debounce time */ + max8997_muic_set_debounce_time(info, ADC_DEBOUNCE_TIME_25MS); + /* Initial device detection */ max8997_muic_detect_dev(info); diff --git a/include/linux/mfd/max8997-private.h b/include/linux/mfd/max8997-private.h index 010173a92274..cd37a92ccba9 100644 --- a/include/linux/mfd/max8997-private.h +++ b/include/linux/mfd/max8997-private.h @@ -251,6 +251,13 @@ enum max8997_muic_reg { #define CONTROL2_USBCPINT_MASK (0x1 << CONTROL2_USBCPINT_SHIFT) #define CONTROL2_RCPS_MASK (0x1 << CONTROL2_RCPS_SHIFT) +#define CONTROL3_JIGSET_SHIFT (0) +#define CONTROL3_BTLDSET_SHIFT (2) +#define CONTROL3_ADCDBSET_SHIFT (4) +#define CONTROL3_JIGSET_MASK (0x3 << CONTROL3_JIGSET_SHIFT) +#define CONTROL3_BTLDSET_MASK (0x3 << CONTROL3_BTLDSET_SHIFT) +#define CONTROL3_ADCDBSET_MASK (0x3 << CONTROL3_ADCDBSET_SHIFT) + #define MAX8997_ADC_GROUND 0x00 #define MAX8997_ADC_MHL 0x01 #define MAX8997_ADC_JIG_USB_1 0x18 -- cgit From f73f6232af9131f7b6fc6e377267e4a441727eb3 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Wed, 13 Feb 2013 12:05:42 +0900 Subject: extcon: max8997: Consolidate duplicate code for checking ADC/CHG cable type This patch make max8997_muic_get_cable_type() function to remove duplicate code for checking ADC/Charger cable type because almost internal function need to read adc/chg_type value of MUIC register. Also, remove *_detach() function, extcon-max8997 driver treat attach/detach operation of cable in max8997_*_handler() function. Lastly, this patch move defined constant in header file(include/ linux/mfd/max8997.h, max8997-private.h) because defined constant is only used in the 'extcon-max8997.c'. Signed-off-by: Chanwoo Choi Signed-off-by: Myungjoo Ham --- drivers/extcon/extcon-max8997.c | 492 +++++++++++++++++++++++------------- include/linux/mfd/max8997-private.h | 9 - include/linux/mfd/max8997.h | 15 -- 3 files changed, 323 insertions(+), 193 deletions(-) (limited to 'include/linux/mfd') diff --git a/drivers/extcon/extcon-max8997.c b/drivers/extcon/extcon-max8997.c index 3206daaf8e08..35338a090c06 100644 --- a/drivers/extcon/extcon-max8997.c +++ b/drivers/extcon/extcon-max8997.c @@ -44,31 +44,89 @@ struct max8997_muic_irq { }; static struct max8997_muic_irq muic_irqs[] = { - { MAX8997_MUICIRQ_ADCError, "muic-ADC_error" }, - { MAX8997_MUICIRQ_ADCLow, "muic-ADC_low" }, - { MAX8997_MUICIRQ_ADC, "muic-ADC" }, - { MAX8997_MUICIRQ_VBVolt, "muic-VB_voltage" }, - { MAX8997_MUICIRQ_DBChg, "muic-DB_charger" }, - { MAX8997_MUICIRQ_DCDTmr, "muic-DCD_timer" }, - { MAX8997_MUICIRQ_ChgDetRun, "muic-CDR_status" }, - { MAX8997_MUICIRQ_ChgTyp, "muic-charger_type" }, - { MAX8997_MUICIRQ_OVP, "muic-over_voltage" }, + { MAX8997_MUICIRQ_ADCError, "muic-ADCERROR" }, + { MAX8997_MUICIRQ_ADCLow, "muic-ADCLOW" }, + { MAX8997_MUICIRQ_ADC, "muic-ADC" }, + { MAX8997_MUICIRQ_VBVolt, "muic-VBVOLT" }, + { MAX8997_MUICIRQ_DBChg, "muic-DBCHG" }, + { MAX8997_MUICIRQ_DCDTmr, "muic-DCDTMR" }, + { MAX8997_MUICIRQ_ChgDetRun, "muic-CHGDETRUN" }, + { MAX8997_MUICIRQ_ChgTyp, "muic-CHGTYP" }, + { MAX8997_MUICIRQ_OVP, "muic-OVP" }, +}; + +/* Define supported cable type */ +enum max8997_muic_acc_type { + MAX8997_MUIC_ADC_GROUND = 0x0, + MAX8997_MUIC_ADC_MHL, /* MHL*/ + MAX8997_MUIC_ADC_REMOTE_S1_BUTTON, + MAX8997_MUIC_ADC_REMOTE_S2_BUTTON, + MAX8997_MUIC_ADC_REMOTE_S3_BUTTON, + MAX8997_MUIC_ADC_REMOTE_S4_BUTTON, + MAX8997_MUIC_ADC_REMOTE_S5_BUTTON, + MAX8997_MUIC_ADC_REMOTE_S6_BUTTON, + MAX8997_MUIC_ADC_REMOTE_S7_BUTTON, + MAX8997_MUIC_ADC_REMOTE_S8_BUTTON, + MAX8997_MUIC_ADC_REMOTE_S9_BUTTON, + MAX8997_MUIC_ADC_REMOTE_S10_BUTTON, + MAX8997_MUIC_ADC_REMOTE_S11_BUTTON, + MAX8997_MUIC_ADC_REMOTE_S12_BUTTON, + MAX8997_MUIC_ADC_RESERVED_ACC_1, + MAX8997_MUIC_ADC_RESERVED_ACC_2, + MAX8997_MUIC_ADC_RESERVED_ACC_3, + MAX8997_MUIC_ADC_RESERVED_ACC_4, + MAX8997_MUIC_ADC_RESERVED_ACC_5, + MAX8997_MUIC_ADC_CEA936_AUDIO, + MAX8997_MUIC_ADC_PHONE_POWERED_DEV, + MAX8997_MUIC_ADC_TTY_CONVERTER, + MAX8997_MUIC_ADC_UART_CABLE, + MAX8997_MUIC_ADC_CEA936A_TYPE1_CHG, + MAX8997_MUIC_ADC_FACTORY_MODE_USB_OFF, /* JIG-USB-OFF */ + MAX8997_MUIC_ADC_FACTORY_MODE_USB_ON, /* JIG-USB-ON */ + MAX8997_MUIC_ADC_AV_CABLE_NOLOAD, /* DESKDOCK */ + MAX8997_MUIC_ADC_CEA936A_TYPE2_CHG, + MAX8997_MUIC_ADC_FACTORY_MODE_UART_OFF, /* JIG-UART */ + MAX8997_MUIC_ADC_FACTORY_MODE_UART_ON, /* CARDOCK */ + MAX8997_MUIC_ADC_AUDIO_MODE_REMOTE, + MAX8997_MUIC_ADC_OPEN, /* OPEN */ +}; + +enum max8997_muic_cable_group { + MAX8997_CABLE_GROUP_ADC = 0, + MAX8997_CABLE_GROUP_ADC_GND, + MAX8997_CABLE_GROUP_CHG, + MAX8997_CABLE_GROUP_VBVOLT, +}; + +enum max8997_muic_usb_type { + MAX8997_USB_HOST, + MAX8997_USB_DEVICE, +}; + +enum max8997_muic_charger_type { + MAX8997_CHARGER_TYPE_NONE = 0, + MAX8997_CHARGER_TYPE_USB, + MAX8997_CHARGER_TYPE_DOWNSTREAM_PORT, + MAX8997_CHARGER_TYPE_DEDICATED_CHG, + MAX8997_CHARGER_TYPE_500MA, + MAX8997_CHARGER_TYPE_1A, + MAX8997_CHARGER_TYPE_DEAD_BATTERY = 7, }; struct max8997_muic_info { struct device *dev; struct i2c_client *muic; - struct max8997_muic_platform_data *muic_pdata; + struct extcon_dev *edev; + int prev_cable_type; + int prev_chg_type; + u8 status[2]; int irq; struct work_struct irq_work; - - enum max8997_muic_charger_type pre_charger_type; - int pre_adc; - struct mutex mutex; - struct extcon_dev *edev; + struct max8997_muic_platform_data *muic_pdata; + enum max8997_muic_charger_type pre_charger_type; }; enum { @@ -181,6 +239,83 @@ static int max8997_muic_set_path(struct max8997_muic_info *info, return 0; } +/* + * max8997_muic_get_cable_type - Return cable type and check cable state + * @info: the instance including private data of max8997 MUIC + * @group: the path according to attached cable + * @attached: store cable state and return + * + * This function check the cable state either attached or detached, + * and then divide precise type of cable according to cable group. + * - MAX8997_CABLE_GROUP_ADC + * - MAX8997_CABLE_GROUP_CHG + */ +static int max8997_muic_get_cable_type(struct max8997_muic_info *info, + enum max8997_muic_cable_group group, bool *attached) +{ + int cable_type = 0; + int adc; + int chg_type; + + switch (group) { + case MAX8997_CABLE_GROUP_ADC: + /* + * Read ADC value to check cable type and decide cable state + * according to cable type + */ + adc = info->status[0] & STATUS1_ADC_MASK; + adc >>= STATUS1_ADC_SHIFT; + + /* + * Check current cable state/cable type and store cable type + * (info->prev_cable_type) for handling cable when cable is + * detached. + */ + if (adc == MAX8997_MUIC_ADC_OPEN) { + *attached = false; + + cable_type = info->prev_cable_type; + info->prev_cable_type = MAX8997_MUIC_ADC_OPEN; + } else { + *attached = true; + + cable_type = info->prev_cable_type = adc; + } + break; + case MAX8997_CABLE_GROUP_CHG: + /* + * Read charger type to check cable type and decide cable state + * according to type of charger cable. + */ + chg_type = info->status[1] & STATUS2_CHGTYP_MASK; + chg_type >>= STATUS2_CHGTYP_SHIFT; + + if (chg_type == MAX8997_CHARGER_TYPE_NONE) { + *attached = false; + + cable_type = info->prev_chg_type; + info->prev_chg_type = MAX8997_CHARGER_TYPE_NONE; + } else { + *attached = true; + + /* + * Check current cable state/cable type and store cable + * type(info->prev_chg_type) for handling cable when + * charger cable is detached. + */ + cable_type = info->prev_chg_type = chg_type; + } + + break; + default: + dev_err(info->dev, "Unknown cable group (%d)\n", group); + cable_type = -EINVAL; + break; + } + + return cable_type; +} + static int max8997_muic_handle_usb(struct max8997_muic_info *info, enum max8997_muic_usb_type usb_type, bool attached) { @@ -188,9 +323,9 @@ static int max8997_muic_handle_usb(struct max8997_muic_info *info, if (usb_type == MAX8997_USB_HOST) { ret = max8997_muic_set_path(info, CONTROL1_SW_USB, attached); - if (ret) { + if (ret < 0) { dev_err(info->dev, "failed to update muic register\n"); - goto out; + return ret; } } @@ -202,38 +337,39 @@ static int max8997_muic_handle_usb(struct max8997_muic_info *info, extcon_set_cable_state(info->edev, "USB", attached); break; default: - ret = -EINVAL; - break; + dev_err(info->dev, "failed to detect %s usb cable\n", + attached ? "attached" : "detached"); + return -EINVAL; } -out: - return ret; + return 0; } static int max8997_muic_handle_dock(struct max8997_muic_info *info, - int adc, bool attached) + int cable_type, bool attached) { int ret = 0; ret = max8997_muic_set_path(info, CONTROL1_SW_AUDIO, attached); if (ret) { dev_err(info->dev, "failed to update muic register\n"); - goto out; + return ret; } - switch (adc) { - case MAX8997_ADC_DESKDOCK: + switch (cable_type) { + case MAX8997_MUIC_ADC_AV_CABLE_NOLOAD: extcon_set_cable_state(info->edev, "Dock-desk", attached); break; - case MAX8997_ADC_CARDOCK: + case MAX8997_MUIC_ADC_FACTORY_MODE_UART_ON: extcon_set_cable_state(info->edev, "Dock-card", attached); break; default: - ret = -EINVAL; - break; + dev_err(info->dev, "failed to detect %s dock device\n", + attached ? "attached" : "detached"); + return -EINVAL; } -out: - return ret; + + return 0; } static int max8997_muic_handle_jig_uart(struct max8997_muic_info *info, @@ -245,193 +381,185 @@ static int max8997_muic_handle_jig_uart(struct max8997_muic_info *info, ret = max8997_muic_set_path(info, CONTROL1_SW_UART, attached); if (ret) { dev_err(info->dev, "failed to update muic register\n"); - goto out; + return -EINVAL; } extcon_set_cable_state(info->edev, "JIG", attached); -out: - return ret; -} -static int max8997_muic_handle_adc_detach(struct max8997_muic_info *info) -{ - int ret = 0; - - switch (info->pre_adc) { - case MAX8997_ADC_GROUND: - ret = max8997_muic_handle_usb(info, MAX8997_USB_HOST, false); - break; - case MAX8997_ADC_MHL: - extcon_set_cable_state(info->edev, "MHL", false); - break; - case MAX8997_ADC_JIG_USB_1: - case MAX8997_ADC_JIG_USB_2: - ret = max8997_muic_handle_usb(info, MAX8997_USB_DEVICE, false); - break; - case MAX8997_ADC_DESKDOCK: - case MAX8997_ADC_CARDOCK: - ret = max8997_muic_handle_dock(info, info->pre_adc, false); - break; - case MAX8997_ADC_JIG_UART: - ret = max8997_muic_handle_jig_uart(info, false); - break; - default: - break; - } - - return ret; + return 0; } -static int max8997_muic_handle_adc(struct max8997_muic_info *info, int adc) +static int max8997_muic_adc_handler(struct max8997_muic_info *info) { + int cable_type; + bool attached; int ret = 0; - switch (adc) { - case MAX8997_ADC_GROUND: - ret = max8997_muic_handle_usb(info, MAX8997_USB_HOST, true); - break; - case MAX8997_ADC_MHL: - extcon_set_cable_state(info->edev, "MHL", true); - break; - case MAX8997_ADC_JIG_USB_1: - case MAX8997_ADC_JIG_USB_2: - ret = max8997_muic_handle_usb(info, MAX8997_USB_DEVICE, true); - break; - case MAX8997_ADC_DESKDOCK: - case MAX8997_ADC_CARDOCK: - ret = max8997_muic_handle_dock(info, adc, true); - break; - case MAX8997_ADC_JIG_UART: - ret = max8997_muic_handle_jig_uart(info, true); - break; - case MAX8997_ADC_OPEN: - ret = max8997_muic_handle_adc_detach(info); - break; - default: - ret = -EINVAL; - goto out; - } - - info->pre_adc = adc; -out: - return ret; -} - -static int max8997_muic_handle_charger_type_detach( - struct max8997_muic_info *info) -{ - switch (info->pre_charger_type) { - case MAX8997_CHARGER_TYPE_USB: - extcon_set_cable_state(info->edev, "USB", false); - break; - case MAX8997_CHARGER_TYPE_DOWNSTREAM_PORT: - extcon_set_cable_state(info->edev, "Charge-downstream", false); - break; - case MAX8997_CHARGER_TYPE_DEDICATED_CHG: - extcon_set_cable_state(info->edev, "TA", false); - break; - case MAX8997_CHARGER_TYPE_500MA: - extcon_set_cable_state(info->edev, "Slow-charger", false); - break; - case MAX8997_CHARGER_TYPE_1A: - extcon_set_cable_state(info->edev, "Fast-charger", false); - break; + /* Check cable state which is either detached or attached */ + cable_type = max8997_muic_get_cable_type(info, + MAX8997_CABLE_GROUP_ADC, &attached); + + switch (cable_type) { + case MAX8997_MUIC_ADC_GROUND: + ret = max8997_muic_handle_usb(info, MAX8997_USB_HOST, attached); + if (ret < 0) + return ret; + break; + case MAX8997_MUIC_ADC_MHL: + extcon_set_cable_state(info->edev, "MHL", attached); + break; + case MAX8997_MUIC_ADC_FACTORY_MODE_USB_OFF: + case MAX8997_MUIC_ADC_FACTORY_MODE_USB_ON: + ret = max8997_muic_handle_usb(info, MAX8997_USB_DEVICE, attached); + if (ret < 0) + return ret; + break; + case MAX8997_MUIC_ADC_AV_CABLE_NOLOAD: + case MAX8997_MUIC_ADC_FACTORY_MODE_UART_ON: + ret = max8997_muic_handle_dock(info, cable_type, attached); + if (ret < 0) + return ret; + break; + case MAX8997_MUIC_ADC_FACTORY_MODE_UART_OFF: + ret = max8997_muic_handle_jig_uart(info, attached); + break; + case MAX8997_MUIC_ADC_REMOTE_S1_BUTTON: + case MAX8997_MUIC_ADC_REMOTE_S2_BUTTON: + case MAX8997_MUIC_ADC_REMOTE_S3_BUTTON: + case MAX8997_MUIC_ADC_REMOTE_S4_BUTTON: + case MAX8997_MUIC_ADC_REMOTE_S5_BUTTON: + case MAX8997_MUIC_ADC_REMOTE_S6_BUTTON: + case MAX8997_MUIC_ADC_REMOTE_S7_BUTTON: + case MAX8997_MUIC_ADC_REMOTE_S8_BUTTON: + case MAX8997_MUIC_ADC_REMOTE_S9_BUTTON: + case MAX8997_MUIC_ADC_REMOTE_S10_BUTTON: + case MAX8997_MUIC_ADC_REMOTE_S11_BUTTON: + case MAX8997_MUIC_ADC_REMOTE_S12_BUTTON: + case MAX8997_MUIC_ADC_RESERVED_ACC_1: + case MAX8997_MUIC_ADC_RESERVED_ACC_2: + case MAX8997_MUIC_ADC_RESERVED_ACC_3: + case MAX8997_MUIC_ADC_RESERVED_ACC_4: + case MAX8997_MUIC_ADC_RESERVED_ACC_5: + case MAX8997_MUIC_ADC_CEA936_AUDIO: + case MAX8997_MUIC_ADC_PHONE_POWERED_DEV: + case MAX8997_MUIC_ADC_TTY_CONVERTER: + case MAX8997_MUIC_ADC_UART_CABLE: + case MAX8997_MUIC_ADC_CEA936A_TYPE1_CHG: + case MAX8997_MUIC_ADC_CEA936A_TYPE2_CHG: + case MAX8997_MUIC_ADC_AUDIO_MODE_REMOTE: + /* + * This cable isn't used in general case if it is specially + * needed to detect additional cable, should implement + * proper operation when this cable is attached/detached. + */ + dev_info(info->dev, + "cable is %s but it isn't used (type:0x%x)\n", + attached ? "attached" : "detached", cable_type); + return -EAGAIN; default: + dev_err(info->dev, + "failed to detect %s unknown cable (type:0x%x)\n", + attached ? "attached" : "detached", cable_type); return -EINVAL; } return 0; } -static int max8997_muic_handle_charger_type(struct max8997_muic_info *info, - enum max8997_muic_charger_type charger_type) +static int max8997_muic_chg_handler(struct max8997_muic_info *info) { - u8 adc; - int ret; + int chg_type; + bool attached; + int adc; - ret = max8997_read_reg(info->muic, MAX8997_MUIC_REG_STATUS1, &adc); - if (ret) { - dev_err(info->dev, "failed to read muic register\n"); - goto out; - } + chg_type = max8997_muic_get_cable_type(info, + MAX8997_CABLE_GROUP_CHG, &attached); - switch (charger_type) { + switch (chg_type) { case MAX8997_CHARGER_TYPE_NONE: - ret = max8997_muic_handle_charger_type_detach(info); break; case MAX8997_CHARGER_TYPE_USB: - if ((adc & STATUS1_ADC_MASK) == MAX8997_ADC_OPEN) { + adc = info->status[0] & STATUS1_ADC_MASK; + adc >>= STATUS1_ADC_SHIFT; + + if ((adc & STATUS1_ADC_MASK) == MAX8997_MUIC_ADC_OPEN) { max8997_muic_handle_usb(info, - MAX8997_USB_DEVICE, true); + MAX8997_USB_DEVICE, attached); } break; case MAX8997_CHARGER_TYPE_DOWNSTREAM_PORT: - extcon_set_cable_state(info->edev, "Charge-downstream", true); + extcon_set_cable_state(info->edev, "Charge-downstream", attached); break; case MAX8997_CHARGER_TYPE_DEDICATED_CHG: - extcon_set_cable_state(info->edev, "TA", true); + extcon_set_cable_state(info->edev, "TA", attached); break; case MAX8997_CHARGER_TYPE_500MA: - extcon_set_cable_state(info->edev, "Slow-charger", true); + extcon_set_cable_state(info->edev, "Slow-charger", attached); break; case MAX8997_CHARGER_TYPE_1A: - extcon_set_cable_state(info->edev, "Fast-charger", true); + extcon_set_cable_state(info->edev, "Fast-charger", attached); break; default: - ret = -EINVAL; - goto out; + dev_err(info->dev, + "failed to detect %s unknown chg cable (type:0x%x)\n", + attached ? "attached" : "detached", chg_type); + return -EINVAL; } - info->pre_charger_type = charger_type; -out: - return ret; + return 0; } static void max8997_muic_irq_work(struct work_struct *work) { struct max8997_muic_info *info = container_of(work, struct max8997_muic_info, irq_work); - u8 status[2]; - u8 adc, chg_type; int irq_type = 0; int i, ret; + if (!info->edev) + return; + mutex_lock(&info->mutex); + for (i = 0 ; i < ARRAY_SIZE(muic_irqs) ; i++) + if (info->irq == muic_irqs[i].virq) + irq_type = muic_irqs[i].irq; + ret = max8997_bulk_read(info->muic, MAX8997_MUIC_REG_STATUS1, - 2, status); + 2, info->status); if (ret) { dev_err(info->dev, "failed to read muic register\n"); mutex_unlock(&info->mutex); return; } - dev_dbg(info->dev, "%s: STATUS1:0x%x, 2:0x%x\n", __func__, - status[0], status[1]); - - for (i = 0 ; i < ARRAY_SIZE(muic_irqs) ; i++) - if (info->irq == muic_irqs[i].virq) - irq_type = muic_irqs[i].irq; - switch (irq_type) { + case MAX8997_MUICIRQ_ADCError: + case MAX8997_MUICIRQ_ADCLow: case MAX8997_MUICIRQ_ADC: - adc = status[0] & STATUS1_ADC_MASK; - adc >>= STATUS1_ADC_SHIFT; - - max8997_muic_handle_adc(info, adc); + /* Handle all of cable except for charger cable */ + ret = max8997_muic_adc_handler(info); break; + case MAX8997_MUICIRQ_VBVolt: + case MAX8997_MUICIRQ_DBChg: + case MAX8997_MUICIRQ_DCDTmr: + case MAX8997_MUICIRQ_ChgDetRun: case MAX8997_MUICIRQ_ChgTyp: - chg_type = status[1] & STATUS2_CHGTYP_MASK; - chg_type >>= STATUS2_CHGTYP_SHIFT; - - max8997_muic_handle_charger_type(info, chg_type); + /* Handle charger cable */ + ret = max8997_muic_chg_handler(info); + break; + case MAX8997_MUICIRQ_OVP: break; default: dev_info(info->dev, "misc interrupt: irq %d occurred\n", irq_type); - break; + mutex_unlock(&info->mutex); + return; } + if (ret < 0) + dev_err(info->dev, "failed to handle MUIC interrupt\n"); + mutex_unlock(&info->mutex); return; @@ -449,29 +577,49 @@ static irqreturn_t max8997_muic_irq_handler(int irq, void *data) return IRQ_HANDLED; } -static void max8997_muic_detect_dev(struct max8997_muic_info *info) +static int max8997_muic_detect_dev(struct max8997_muic_info *info) { - int ret; - u8 status[2], adc, chg_type; + int ret = 0; + int adc; + int chg_type; + bool attached; - ret = max8997_bulk_read(info->muic, MAX8997_MUIC_REG_STATUS1, - 2, status); + mutex_lock(&info->mutex); + + /* Read STATUSx register to detect accessory */ + ret = max8997_bulk_read(info->muic, + MAX8997_MUIC_REG_STATUS1, 2, info->status); if (ret) { - dev_err(info->dev, "failed to read muic register\n"); - return; + dev_err(info->dev, "failed to read MUIC register\n"); + mutex_unlock(&info->mutex); + return -EINVAL; } - dev_info(info->dev, "STATUS1:0x%x, STATUS2:0x%x\n", - status[0], status[1]); + adc = max8997_muic_get_cable_type(info, MAX8997_CABLE_GROUP_ADC, + &attached); + if (attached && adc != MAX8997_MUIC_ADC_OPEN) { + ret = max8997_muic_adc_handler(info); + if (ret < 0) { + dev_err(info->dev, "Cannot detect ADC cable\n"); + mutex_unlock(&info->mutex); + return ret; + } + } - adc = status[0] & STATUS1_ADC_MASK; - adc >>= STATUS1_ADC_SHIFT; + chg_type = max8997_muic_get_cable_type(info, MAX8997_CABLE_GROUP_CHG, + &attached); + if (attached && chg_type != MAX8997_CHARGER_TYPE_NONE) { + ret = max8997_muic_chg_handler(info); + if (ret < 0) { + dev_err(info->dev, "Cannot detect charger cable\n"); + mutex_unlock(&info->mutex); + return ret; + } + } - chg_type = status[1] & STATUS2_CHGTYP_MASK; - chg_type >>= STATUS2_CHGTYP_SHIFT; + mutex_unlock(&info->mutex); - max8997_muic_handle_adc(info, adc); - max8997_muic_handle_charger_type(info, chg_type); + return 0; } static int max8997_muic_probe(struct platform_device *pdev) @@ -550,10 +698,16 @@ static int max8997_muic_probe(struct platform_device *pdev) max8997_muic_set_debounce_time(info, ADC_DEBOUNCE_TIME_25MS); /* Initial device detection */ - max8997_muic_detect_dev(info); + ret = max8997_muic_detect_dev(info); + if (ret < 0) { + dev_err(&pdev->dev, "failed to detect cable type\n"); + goto err_extcon; + } return ret; +err_extcon: + extcon_dev_unregister(info->edev); err_irq: while (--i >= 0) free_irq(muic_irqs[i].virq, info); diff --git a/include/linux/mfd/max8997-private.h b/include/linux/mfd/max8997-private.h index cd37a92ccba9..fb465dfbb59e 100644 --- a/include/linux/mfd/max8997-private.h +++ b/include/linux/mfd/max8997-private.h @@ -258,15 +258,6 @@ enum max8997_muic_reg { #define CONTROL3_BTLDSET_MASK (0x3 << CONTROL3_BTLDSET_SHIFT) #define CONTROL3_ADCDBSET_MASK (0x3 << CONTROL3_ADCDBSET_SHIFT) -#define MAX8997_ADC_GROUND 0x00 -#define MAX8997_ADC_MHL 0x01 -#define MAX8997_ADC_JIG_USB_1 0x18 -#define MAX8997_ADC_JIG_USB_2 0x19 -#define MAX8997_ADC_DESKDOCK 0x1a -#define MAX8997_ADC_JIG_UART 0x1c -#define MAX8997_ADC_CARDOCK 0x1d -#define MAX8997_ADC_OPEN 0x1f - enum max8997_haptic_reg { MAX8997_HAPTIC_REG_GENERAL = 0x00, MAX8997_HAPTIC_REG_CONF1 = 0x01, diff --git a/include/linux/mfd/max8997.h b/include/linux/mfd/max8997.h index 1d4a4fe6ac33..65f8d6a3a608 100644 --- a/include/linux/mfd/max8997.h +++ b/include/linux/mfd/max8997.h @@ -78,21 +78,6 @@ struct max8997_regulator_data { struct device_node *reg_node; }; -enum max8997_muic_usb_type { - MAX8997_USB_HOST, - MAX8997_USB_DEVICE, -}; - -enum max8997_muic_charger_type { - MAX8997_CHARGER_TYPE_NONE = 0, - MAX8997_CHARGER_TYPE_USB, - MAX8997_CHARGER_TYPE_DOWNSTREAM_PORT, - MAX8997_CHARGER_TYPE_DEDICATED_CHG, - MAX8997_CHARGER_TYPE_500MA, - MAX8997_CHARGER_TYPE_1A, - MAX8997_CHARGER_TYPE_DEAD_BATTERY = 7, -}; - struct max8997_muic_reg_data { u8 addr; u8 data; -- cgit From 685dc9a7dbfede28cc4a0fe4e65804194ec04fa5 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Wed, 13 Feb 2013 15:04:15 +0900 Subject: extcon: max8997: Set default UART/USB path on probe This patch set default H/W line path according to platfomr data. The MAX8997 MUIC device can possibly set UART/USB or UART_AUX /USB_AUX to internal H/W line path of MUIC device. Namely, only one H/W line is used for two operation. For example, if H/W line path of MAX8997 device set UART/USB, micro usb cable is connected to AP(Application Processor) and if H/W line path set UART_AUX/USB_AUX, micro usb cable is connected to CP(Coprocessor). Signed-off-by: Chanwoo Choi Signed-off-by: Myungjoo Ham --- drivers/extcon/extcon-max8997.c | 28 ++++++++++++++++++++++++++-- include/linux/mfd/max8997.h | 7 +++++++ 2 files changed, 33 insertions(+), 2 deletions(-) (limited to 'include/linux/mfd') diff --git a/drivers/extcon/extcon-max8997.c b/drivers/extcon/extcon-max8997.c index 35338a090c06..349f65f6a4a4 100644 --- a/drivers/extcon/extcon-max8997.c +++ b/drivers/extcon/extcon-max8997.c @@ -127,6 +127,13 @@ struct max8997_muic_info { struct max8997_muic_platform_data *muic_pdata; enum max8997_muic_charger_type pre_charger_type; + + /* + * Default usb/uart path whether UART/USB or AUX_UART/AUX_USB + * h/w path of COMP2/COMN1 on CONTROL1 register. + */ + int path_usb; + int path_uart; }; enum { @@ -322,7 +329,7 @@ static int max8997_muic_handle_usb(struct max8997_muic_info *info, int ret = 0; if (usb_type == MAX8997_USB_HOST) { - ret = max8997_muic_set_path(info, CONTROL1_SW_USB, attached); + ret = max8997_muic_set_path(info, info->path_usb, attached); if (ret < 0) { dev_err(info->dev, "failed to update muic register\n"); return ret; @@ -378,7 +385,7 @@ static int max8997_muic_handle_jig_uart(struct max8997_muic_info *info, int ret = 0; /* switch to UART */ - ret = max8997_muic_set_path(info, CONTROL1_SW_UART, attached); + ret = max8997_muic_set_path(info, info->path_uart, attached); if (ret) { dev_err(info->dev, "failed to update muic register\n"); return -EINVAL; @@ -694,6 +701,23 @@ static int max8997_muic_probe(struct platform_device *pdev) } } + /* + * Default usb/uart path whether UART/USB or AUX_UART/AUX_USB + * h/w path of COMP2/COMN1 on CONTROL1 register. + */ + if (pdata->muic_pdata->path_uart) + info->path_uart = pdata->muic_pdata->path_uart; + else + info->path_uart = CONTROL1_SW_UART; + + if (pdata->muic_pdata->path_usb) + info->path_usb = pdata->muic_pdata->path_usb; + else + info->path_usb = CONTROL1_SW_USB; + + /* Set initial path for UART */ + max8997_muic_set_path(info, info->path_uart, true); + /* Set ADC debounce time */ max8997_muic_set_debounce_time(info, ADC_DEBOUNCE_TIME_25MS); diff --git a/include/linux/mfd/max8997.h b/include/linux/mfd/max8997.h index 65f8d6a3a608..2d2d67b6a393 100644 --- a/include/linux/mfd/max8997.h +++ b/include/linux/mfd/max8997.h @@ -92,6 +92,13 @@ struct max8997_muic_reg_data { struct max8997_muic_platform_data { struct max8997_muic_reg_data *init_data; int num_init_data; + + /* + * Default usb/uart path whether UART/USB or AUX_UART/AUX_USB + * h/w path of COMP2/COMN1 on CONTROL1 register. + */ + int path_usb; + int path_uart; }; enum max8997_haptic_motor_type { -- cgit From af5eb1a13273447c5708cd5425696f3b6f79dd9b Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Wed, 13 Feb 2013 15:10:00 +0900 Subject: extcon: max8997: Use workqueue to check cable state after completing boot of platform This patch use delayed workqueue to check cable state after a certain time. If extcon-max8997 driver check cable state during booting of platform, this couldn't send the correct notification of cable state to extcon consumer. Alwasys, this driver should check cable state after the completion of platform initialization Signed-off-by: Chanwoo Choi Signed-off-by: Myungjoo Ham --- drivers/extcon/extcon-max8997.c | 45 ++++++++++++++++++++++++++++++++--------- include/linux/mfd/max8997.h | 3 +++ 2 files changed, 39 insertions(+), 9 deletions(-) (limited to 'include/linux/mfd') diff --git a/drivers/extcon/extcon-max8997.c b/drivers/extcon/extcon-max8997.c index 349f65f6a4a4..e636d950ad6c 100644 --- a/drivers/extcon/extcon-max8997.c +++ b/drivers/extcon/extcon-max8997.c @@ -29,6 +29,7 @@ #include #define DEV_NAME "max8997-muic" +#define DELAY_MS_DEFAULT 20000 /* unit: millisecond */ enum max8997_muic_adc_debounce_time { ADC_DEBOUNCE_TIME_0_5MS = 0, /* 0.5ms */ @@ -128,6 +129,14 @@ struct max8997_muic_info { struct max8997_muic_platform_data *muic_pdata; enum max8997_muic_charger_type pre_charger_type; + /* + * Use delayed workqueue to detect cable state and then + * notify cable state to notifiee/platform through uevent. + * After completing the booting of platform, the extcon provider + * driver should notify cable state to upper layer. + */ + struct delayed_work wq_detcable; + /* * Default usb/uart path whether UART/USB or AUX_UART/AUX_USB * h/w path of COMP2/COMN1 on CONTROL1 register. @@ -629,11 +638,23 @@ static int max8997_muic_detect_dev(struct max8997_muic_info *info) return 0; } +static void max8997_muic_detect_cable_wq(struct work_struct *work) +{ + struct max8997_muic_info *info = container_of(to_delayed_work(work), + struct max8997_muic_info, wq_detcable); + int ret; + + ret = max8997_muic_detect_dev(info); + if (ret < 0) + pr_err("failed to detect cable type\n"); +} + static int max8997_muic_probe(struct platform_device *pdev) { struct max8997_dev *max8997 = dev_get_drvdata(pdev->dev.parent); struct max8997_platform_data *pdata = dev_get_platdata(max8997->dev); struct max8997_muic_info *info; + int delay_jiffies; int ret, i; info = devm_kzalloc(&pdev->dev, sizeof(struct max8997_muic_info), @@ -721,17 +742,23 @@ static int max8997_muic_probe(struct platform_device *pdev) /* Set ADC debounce time */ max8997_muic_set_debounce_time(info, ADC_DEBOUNCE_TIME_25MS); - /* Initial device detection */ - ret = max8997_muic_detect_dev(info); - if (ret < 0) { - dev_err(&pdev->dev, "failed to detect cable type\n"); - goto err_extcon; - } + /* + * Detect accessory after completing the initialization of platform + * + * - Use delayed workqueue to detect cable state and then + * notify cable state to notifiee/platform through uevent. + * After completing the booting of platform, the extcon provider + * driver should notify cable state to upper layer. + */ + INIT_DELAYED_WORK(&info->wq_detcable, max8997_muic_detect_cable_wq); + if (pdata->muic_pdata->detcable_delay_ms) + delay_jiffies = msecs_to_jiffies(pdata->muic_pdata->detcable_delay_ms); + else + delay_jiffies = msecs_to_jiffies(DELAY_MS_DEFAULT); + schedule_delayed_work(&info->wq_detcable, delay_jiffies); - return ret; + return 0; -err_extcon: - extcon_dev_unregister(info->edev); err_irq: while (--i >= 0) free_irq(muic_irqs[i].virq, info); diff --git a/include/linux/mfd/max8997.h b/include/linux/mfd/max8997.h index 2d2d67b6a393..cf815577bd68 100644 --- a/include/linux/mfd/max8997.h +++ b/include/linux/mfd/max8997.h @@ -93,6 +93,9 @@ struct max8997_muic_platform_data { struct max8997_muic_reg_data *init_data; int num_init_data; + /* Check cable state after certain delay */ + int detcable_delay_ms; + /* * Default usb/uart path whether UART/USB or AUX_UART/AUX_USB * h/w path of COMP2/COMN1 on CONTROL1 register. -- cgit