From ccb4b560d11f6632178dfa17024f026eccbb4fde Mon Sep 17 00:00:00 2001 From: Virupax Sadashivpetimath Date: Thu, 22 Sep 2011 16:39:54 +0530 Subject: input:misc: Add accessory driver Add driver for the accessory detection block of the ab5500 PMIC. The common functions from the 8500 accessory driver are moved to abx500-accdet generic driver. This generic driver uses callbacks registerd from the 8500 and 5500 specific driver to work as a accessory driver for a perticluar platform. ST Ericsson ID: 353610 ST Ericsson FOSS-OUT ID: Trivial ST Ericsson Linux next: NA Change-Id: Ifb06f9c9dd0dc59cbc071198c9e244a422a63af0 Signed-off-by: Virupax Sadashivpetimath --- arch/arm/mach-ux500/board-mop500.c | 4 +- arch/arm/mach-ux500/board-u5500-regulators.c | 11 + arch/arm/mach-ux500/board-u5500.c | 14 + arch/arm/mach-ux500/include/mach/ab8500-accdet.h | 96 -- arch/arm/mach-ux500/include/mach/abx500-accdet.h | 361 +++++++ drivers/input/misc/Kconfig | 7 + drivers/input/misc/Makefile | 3 +- drivers/input/misc/ab5500-accdet.c | 363 +++++++ drivers/input/misc/ab8500-accdet.c | 1164 ++-------------------- drivers/input/misc/abx500-accdet.c | 977 ++++++++++++++++++ drivers/mfd/ab5500-core.c | 67 ++ drivers/regulator/ab5500.c | 213 ++-- include/linux/mfd/ab8500.h | 2 +- include/linux/mfd/abx500/ab5500.h | 2 + include/linux/regulator/ab5500.h | 1 + 15 files changed, 2006 insertions(+), 1279 deletions(-) delete mode 100644 arch/arm/mach-ux500/include/mach/ab8500-accdet.h create mode 100644 arch/arm/mach-ux500/include/mach/abx500-accdet.h create mode 100644 drivers/input/misc/ab5500-accdet.c create mode 100644 drivers/input/misc/abx500-accdet.c diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c index 92677f5aa7f..0060f0399a4 100644 --- a/arch/arm/mach-ux500/board-mop500.c +++ b/arch/arm/mach-ux500/board-mop500.c @@ -48,7 +48,7 @@ #include #include #include -#include +#include #include #include #include @@ -144,7 +144,7 @@ static struct ab8500_gpio_platform_data ab8500_gpio_pdata = { }; #ifdef CONFIG_INPUT_AB8500_ACCDET -static struct ab8500_accdet_platform_data ab8500_accdet_pdata = { +static struct abx500_accdet_platform_data ab8500_accdet_pdata = { .btn_keycode = KEY_MEDIA, .accdet1_dbth = ACCDET1_TH_1200mV | ACCDET1_DB_70ms, .accdet2122_th = ACCDET21_TH_1000mV | ACCDET22_TH_1000mV, diff --git a/arch/arm/mach-ux500/board-u5500-regulators.c b/arch/arm/mach-ux500/board-u5500-regulators.c index d8c72966c82..4721c8f0b04 100644 --- a/arch/arm/mach-ux500/board-u5500-regulators.c +++ b/arch/arm/mach-ux500/board-u5500-regulators.c @@ -48,6 +48,10 @@ static struct regulator_consumer_supply ab5500_ldo_sim_consumers[] = { REGULATOR_SUPPLY("debug", "reg-virt-consumer.5"), }; +static struct regulator_consumer_supply ab5500_bias2_consumers[] = { + REGULATOR_SUPPLY("v-amic", NULL), +}; + static struct regulator_init_data ab5500_regulator_init_data[AB5500_NUM_REGULATORS] = { /* SD Card */ @@ -124,6 +128,13 @@ ab5500_regulator_init_data[AB5500_NUM_REGULATORS] = { .consumer_supplies = ab5500_ldo_sim_consumers, .num_consumer_supplies = ARRAY_SIZE(ab5500_ldo_sim_consumers), }, + [AB5500_BIAS2] = { + .constraints = { + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .consumer_supplies = ab5500_bias2_consumers, + .num_consumer_supplies = ARRAY_SIZE(ab5500_bias2_consumers), + }, }; struct ab5500_regulator_platform_data u5500_ab5500_regulator_data = { diff --git a/arch/arm/mach-ux500/board-u5500.c b/arch/arm/mach-ux500/board-u5500.c index 7cdadd9792a..7a1715ea4aa 100644 --- a/arch/arm/mach-ux500/board-u5500.c +++ b/arch/arm/mach-ux500/board-u5500.c @@ -37,6 +37,7 @@ #include #include #include +#include #include "pins-db5500.h" #include "pins.h" @@ -431,6 +432,15 @@ static struct resource ab5500_resources[] = { }; +#ifdef CONFIG_INPUT_AB5500_ACCDET +static struct abx500_accdet_platform_data ab5500_accdet_pdata = { + .btn_keycode = KEY_MEDIA, + .accdet1_dbth = ACCDET1_TH_300mV | ACCDET1_DB_10ms, + .accdet2122_th = ACCDET21_TH_300mV | ACCDET22_TH_300mV, + .is_detection_inverted = false, + }; +#endif + static struct ab5500_platform_data ab5500_plf_data = { .irq = { .base = IRQ_AB5500_BASE, @@ -438,6 +448,10 @@ static struct ab5500_platform_data ab5500_plf_data = { }, .pm_power_off = true, .regulator = &u5500_ab5500_regulator_data, +#ifdef CONFIG_INPUT_AB5500_ACCDET + .dev_data[AB5500_DEVID_ACCDET] = &ab5500_accdet_pdata, + .dev_data_sz[AB5500_DEVID_ACCDET] = sizeof(ab5500_accdet_pdata), +#endif .dev_data[AB5500_DEVID_LEDS] = &ab5500_hvleds_data, .dev_data_sz[AB5500_DEVID_LEDS] = sizeof(ab5500_hvleds_data), .dev_data[AB5500_DEVID_VIBRATOR] = &ab5500_vibra_data, diff --git a/arch/arm/mach-ux500/include/mach/ab8500-accdet.h b/arch/arm/mach-ux500/include/mach/ab8500-accdet.h deleted file mode 100644 index b1b157e317e..00000000000 --- a/arch/arm/mach-ux500/include/mach/ab8500-accdet.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright ST-Ericsson 2011. - * - * Author: Jarmo K. Kuronen for ST Ericsson. - * Licensed under GPLv2. - */ - -#ifndef _AB8500_ACCDET_H -#define _AB8500_ACCDET_H - -/* -* Debounce times for AccDet1 input -* @0x880 [2:0] -*/ -#define ACCDET1_DB_0ms 0x00 -#define ACCDET1_DB_10ms 0x01 -#define ACCDET1_DB_20ms 0x02 -#define ACCDET1_DB_30ms 0x03 -#define ACCDET1_DB_40ms 0x04 -#define ACCDET1_DB_50ms 0x05 -#define ACCDET1_DB_60ms 0x06 -#define ACCDET1_DB_70ms 0x07 - -/* -* Voltage threshold for AccDet1 input -* @0x880 [6:3] -*/ -#define ACCDET1_TH_1100mV 0x40 -#define ACCDET1_TH_1200mV 0x48 -#define ACCDET1_TH_1300mV 0x50 -#define ACCDET1_TH_1400mV 0x58 -#define ACCDET1_TH_1500mV 0x60 -#define ACCDET1_TH_1600mV 0x68 -#define ACCDET1_TH_1700mV 0x70 -#define ACCDET1_TH_1800mV 0x78 - -/* -* Voltage threshold for AccDet21 input -* @0x881 [3:0] -*/ -#define ACCDET21_TH_300mV 0x00 -#define ACCDET21_TH_400mV 0x01 -#define ACCDET21_TH_500mV 0x02 -#define ACCDET21_TH_600mV 0x03 -#define ACCDET21_TH_700mV 0x04 -#define ACCDET21_TH_800mV 0x05 -#define ACCDET21_TH_900mV 0x06 -#define ACCDET21_TH_1000mV 0x07 -#define ACCDET21_TH_1100mV 0x08 -#define ACCDET21_TH_1200mV 0x09 -#define ACCDET21_TH_1300mV 0x0a -#define ACCDET21_TH_1400mV 0x0b -#define ACCDET21_TH_1500mV 0x0c -#define ACCDET21_TH_1600mV 0x0d -#define ACCDET21_TH_1700mV 0x0e -#define ACCDET21_TH_1800mV 0x0f - -/* -* Voltage threshold for AccDet22 input -* @0x881 [7:4] -*/ -#define ACCDET22_TH_300mV 0x00 -#define ACCDET22_TH_400mV 0x10 -#define ACCDET22_TH_500mV 0x20 -#define ACCDET22_TH_600mV 0x30 -#define ACCDET22_TH_700mV 0x40 -#define ACCDET22_TH_800mV 0x50 -#define ACCDET22_TH_900mV 0x60 -#define ACCDET22_TH_1000mV 0x70 -#define ACCDET22_TH_1100mV 0x80 -#define ACCDET22_TH_1200mV 0x90 -#define ACCDET22_TH_1300mV 0xa0 -#define ACCDET22_TH_1400mV 0xb0 -#define ACCDET22_TH_1500mV 0xc0 -#define ACCDET22_TH_1600mV 0xd0 -#define ACCDET22_TH_1700mV 0xe0 -#define ACCDET22_TH_1800mV 0xf0 - -/** - * struct ab8500_accdet_platform_data - AV Accessory detection specific - * platform data - * @btn_keycode Keycode to be sent when accessory button is pressed. - * @accdet1_dbth Debounce time + voltage threshold for accdet 1 input. - * @accdet2122_th Voltage thresholds for accdet21 and accdet22 inputs. - * @is_detection_inverted Whether the accessory insert/removal, button - * press/release irq's are inverted. - */ -struct ab8500_accdet_platform_data { - int btn_keycode; - u8 accdet1_dbth; - u8 accdet2122_th; - unsigned int video_ctrl_gpio; - bool is_detection_inverted; -}; - -#endif /* _AB8500_ACCDET_H */ diff --git a/arch/arm/mach-ux500/include/mach/abx500-accdet.h b/arch/arm/mach-ux500/include/mach/abx500-accdet.h new file mode 100644 index 00000000000..5698336e4cf --- /dev/null +++ b/arch/arm/mach-ux500/include/mach/abx500-accdet.h @@ -0,0 +1,361 @@ +/* + * Copyright ST-Ericsson 2011. + * + * Author: Jarmo K. Kuronen for ST Ericsson. + * Licensed under GPLv2. + */ + +#ifndef _ABx500_ACCDET_H +#define _ABx500_ACCDET_H + +#include + +/* +* Debounce times for AccDet1 input +* @0x880 [2:0] +*/ +#define ACCDET1_DB_0ms 0x00 +#define ACCDET1_DB_10ms 0x01 +#define ACCDET1_DB_20ms 0x02 +#define ACCDET1_DB_30ms 0x03 +#define ACCDET1_DB_40ms 0x04 +#define ACCDET1_DB_50ms 0x05 +#define ACCDET1_DB_60ms 0x06 +#define ACCDET1_DB_70ms 0x07 + +/* +* Voltage threshold for AccDet1 input +* @0x880 [6:3] +*/ +#define ACCDET1_TH_1100mV 0x40 +#define ACCDET1_TH_1200mV 0x48 +#define ACCDET1_TH_1300mV 0x50 +#define ACCDET1_TH_1400mV 0x58 +#define ACCDET1_TH_1500mV 0x60 +#define ACCDET1_TH_1600mV 0x68 +#define ACCDET1_TH_1700mV 0x70 +#define ACCDET1_TH_1800mV 0x78 + +/* +* Voltage threshold for AccDet21 input +* @0x881 [3:0] +*/ +#define ACCDET21_TH_300mV 0x00 +#define ACCDET21_TH_400mV 0x01 +#define ACCDET21_TH_500mV 0x02 +#define ACCDET21_TH_600mV 0x03 +#define ACCDET21_TH_700mV 0x04 +#define ACCDET21_TH_800mV 0x05 +#define ACCDET21_TH_900mV 0x06 +#define ACCDET21_TH_1000mV 0x07 +#define ACCDET21_TH_1100mV 0x08 +#define ACCDET21_TH_1200mV 0x09 +#define ACCDET21_TH_1300mV 0x0a +#define ACCDET21_TH_1400mV 0x0b +#define ACCDET21_TH_1500mV 0x0c +#define ACCDET21_TH_1600mV 0x0d +#define ACCDET21_TH_1700mV 0x0e +#define ACCDET21_TH_1800mV 0x0f + +/* +* Voltage threshold for AccDet22 input +* @0x881 [7:4] +*/ +#define ACCDET22_TH_300mV 0x00 +#define ACCDET22_TH_400mV 0x10 +#define ACCDET22_TH_500mV 0x20 +#define ACCDET22_TH_600mV 0x30 +#define ACCDET22_TH_700mV 0x40 +#define ACCDET22_TH_800mV 0x50 +#define ACCDET22_TH_900mV 0x60 +#define ACCDET22_TH_1000mV 0x70 +#define ACCDET22_TH_1100mV 0x80 +#define ACCDET22_TH_1200mV 0x90 +#define ACCDET22_TH_1300mV 0xa0 +#define ACCDET22_TH_1400mV 0xb0 +#define ACCDET22_TH_1500mV 0xc0 +#define ACCDET22_TH_1600mV 0xd0 +#define ACCDET22_TH_1700mV 0xe0 +#define ACCDET22_TH_1800mV 0xf0 + +/* +* Voltage threshold for AccDet1 input +* @0x880 [6:3] +*/ +#define ACCDET1_TH_300mV 0x00 +#define ACCDET1_TH_400mV 0x01 +#define ACCDET1_TH_500mV 0x02 +#define ACCDET1_TH_600mV 0x03 +#define ACCDET1_TH_700mV 0x04 +#define ACCDET1_TH_800mV 0x05 +#define ACCDET1_TH_900mV 0x06 +#define ACCDET1_TH_1000mV 0x07 + +#define MAX_DET_COUNT 10 +#define MAX_VOLT_DIFF 30 +#define MIN_MIC_POWER -100 + +/** + * struct abx500_accdet_platform_data - AV Accessory detection specific + * platform data + * @btn_keycode Keycode to be sent when accessory button is pressed. + * @accdet1_dbth Debounce time + voltage threshold for accdet 1 input. + * @accdet2122_th Voltage thresholds for accdet21 and accdet22 inputs. + * @is_detection_inverted Whether the accessory insert/removal, button + * press/release irq's are inverted. + */ +struct abx500_accdet_platform_data { + int btn_keycode; + u8 accdet1_dbth; + u8 accdet2122_th; + unsigned int video_ctrl_gpio; + bool is_detection_inverted; +}; + +/* Enumerations */ + +/** + * @JACK_TYPE_UNSPECIFIED Not known whether any accessories are connected. + * @JACK_TYPE_DISCONNECTED No accessories connected. + * @JACK_TYPE_CONNECTED Accessory is connected but functionality was unable to + * detect the actual type. In this mode, possible button events are reported. + * @JACK_TYPE_HEADPHONE Headphone type of accessory (spkrs only) connected + * @JACK_TYPE_HEADSET Headset type of accessory (mic+spkrs) connected + * @JACK_TYPE_CARKIT Carkit type of accessory connected + * @JACK_TYPE_OPENCABLE Open cable connected + * @JACK_TYPE_CVIDEO CVideo type of accessory connected. + */ +enum accessory_jack_type { + JACK_TYPE_UNSPECIFIED, + JACK_TYPE_DISCONNECTED, + JACK_TYPE_CONNECTED, + JACK_TYPE_HEADPHONE, + JACK_TYPE_HEADSET, + JACK_TYPE_CARKIT, + JACK_TYPE_OPENCABLE, + JACK_TYPE_CVIDEO +}; + +/** + * @BUTTON_UNK Button state not known + * @BUTTON_PRESSED Button "down" + * @BUTTON_RELEASED Button "up" + */ +enum accessory_button_state { + BUTTON_UNK, + BUTTON_PRESSED, + BUTTON_RELEASED +}; + +/** + * @PLUG_IRQ Interrupt gen. when accessory plugged in + * @UNPLUG_IRQ Interrupt gen. when accessory plugged out + * @BUTTON_PRESS_IRQ Interrupt gen. when accessory button pressed. + * @BUTTON_RELEASE_IRQ Interrupt gen. when accessory button released. + */ +enum accessory_irq { + PLUG_IRQ, + UNPLUG_IRQ, + BUTTON_PRESS_IRQ, + BUTTON_RELEASE_IRQ, +}; + +/** + * Enumerates the op. modes of the avcontrol switch + * @AUDIO_IN Audio input is selected + * @VIDEO_OUT Video output is selected + * @NOT_SET The av-switch control signal is disconnected. + */ +enum accessory_avcontrol_dir { + AUDIO_IN, + VIDEO_OUT, + NOT_SET, +}; + +/** + * @REGULATOR_VAUDIO v-audio regulator + * @REGULATOR_VAMIC1 v-amic1 regulator + * @REGULATOR_AVSWITCH Audio/Video select switch regulator + * @REGULATOR_ALL All regulators combined + */ +enum accessory_regulator { + REGULATOR_NONE = 0x0, + REGULATOR_VAUDIO = 0x1, + REGULATOR_VAMIC1 = 0x2, + REGULATOR_AVSWITCH = 0x4, + REGULATOR_ALL = 0xFF +}; + +/* Structures */ + +/** + * Describes an interrupt + * @irq interrupt identifier + * @name name of the irq in platform data + * @isr interrupt service routine + * @register are we currently registered to receive interrupts from this source. + */ +struct accessory_irq_descriptor { + enum accessory_irq irq; + const char *name; + irq_handler_t isr; + int registered; +}; + +/** + * Encapsulates info of single regulator. + * @id regulator identifier + * @name name of the regulator + * @enabled flag indicating whether regu is currently enabled. + * @handle regulator handle + */ +struct accessory_regu_descriptor { + enum accessory_regulator id; + const char *name; + int enabled; + struct regulator *handle; +}; + +/** + * Defines attributes for accessory detection operation. + * @typename type as string + * @type Type of accessory this task tests + * @req_det_count How many times this particular type of accessory + * needs to be detected in sequence in order to accept. Multidetection + * implemented to avoid false detections during plug-in. + * @meas_mv Should ACCDETECT2 input voltage be measured just before + * making the decision or can cached voltage be used instead. + * @minvol minimum voltage (mV) for decision + * @maxvol maximum voltage (mV) for decision + */ +struct accessory_detect_task { + const char *typename; + enum accessory_jack_type type; + int req_det_count; + int meas_mv; + int minvol; + int maxvol; +}; + +/** + * Device data, capsulates all relevant device data structures. + * + * @pdev: pointer to platform device + * @pdata: Platform data + * @gpadc: interface for ADC data + * @irq_work_queue: Work queue for deferred interrupt processing + * @detect_work: work item to perform detection work + * @unplug_irq_work: work item to process unplug event + * @init_work: work item to process initialization work. + * @btn_input_dev: button input device used to report btn presses + * @btn_state: Current state of accessory button + * @jack_type: type of currently connected accessory + * @reported_jack_type: previously reported jack type. + * @jack_type_temp: temporary storage for currently connected accessory + * @jack_det_count: counter how many times in sequence the accessory + * type detection has produced same result. + * @total_jack_det_count: after plug-in irq, how many times detection + * has totally been made in order to detect the accessory type + * @detect_jiffies: Used to save timestamp when detection was made. Timestamp + * used to filter out spurious button presses that might occur during the + * plug-in procedure. + * @accdet1_th_set: flag to indicate whether accdet1 threshold and debounce + * times are configured + * @accdet2_th_set: flag to indicate whether accdet2 thresholds are configured + * @gpio35_dir_set: flag to indicate whether GPIO35 (VIDEOCTRL) direction + * @ab_switch userspace: android switch interface + * has been configured. + * @irq_desc_norm: irq's as specified in the initial versions of ab + * @irq_desc_inverted: irq's inverted as seen in the latest versions of ab + * @no_irqs: Total number of irq's + * @regu_desc: Pointer to the regulator descriptors. + * @no_of_regu_desc: Total nummber of descriptors. + * @config_accdetect2_hw: Callback for configuring accdet2 comparator. + * @config_accdetect1_hw: Callback for configuring accdet1 comparator. + * @detect_plugged_in: Callback to detect type of accessory connected. + * @meas_voltage_stable: Callback to read present accdet voltage. + * @config_hw_test_basic_carkit: Callback to configure hw for carkit + * detect. + * @turn_of_accdet_comparator: Call back to turn off comparators. + * @turn_on_accdet_comparator: Call back to turn ON comparators. + * @accdet_abx500_gpadc_get Call back to get a instance of the + * GPADC convertor. + * @config_hw_test_plug_connected: Call back to configure the hw for + * accessory detection. + * @set_av_switch: Call back to configure the switch for tvout or audioout. + * @startup: Call back to do the Chip specific initialization. + * @get_platform_data: call to get platform specific data. + */ +struct abx500_ad { + struct platform_device *pdev; + struct abx500_accdet_platform_data *pdata; + void *gpadc; + struct workqueue_struct *irq_work_queue; + + struct delayed_work detect_work; + struct delayed_work unplug_irq_work; + struct delayed_work init_work; + + struct input_dev *btn_input_dev; + enum accessory_button_state btn_state; + + enum accessory_jack_type jack_type; + enum accessory_jack_type reported_jack_type; + enum accessory_jack_type jack_type_temp; + + int jack_det_count; + int total_jack_det_count; + + unsigned long detect_jiffies; + + int accdet1_th_set; + int accdet2_th_set; + int gpio35_dir_set; + struct switch_dev ab_switch; + + struct accessory_irq_descriptor *irq_desc_norm; + struct accessory_irq_descriptor *irq_desc_inverted; + int no_irqs; + + struct accessory_regu_descriptor *regu_desc; + int no_of_regu_desc; + + bool tv_out_connected; + + void (*config_accdetect2_hw)(struct abx500_ad *, int); + void (*config_accdetect1_hw)(struct abx500_ad *, int); + int (*detect_plugged_in)(struct abx500_ad *); + int (*meas_voltage_stable)(struct abx500_ad *); + void (*config_hw_test_basic_carkit)(struct abx500_ad *, int); + void (*turn_off_accdet_comparator)(struct platform_device *pdev); + void (*turn_on_accdet_comparator)(struct platform_device *pdev); + void* (*accdet_abx500_gpadc_get)(void); + void (*config_hw_test_plug_connected)(struct abx500_ad *dd, int enable); + void (*set_av_switch)(struct abx500_ad *dd, + enum accessory_avcontrol_dir dir); + int (*startup)(struct abx500_ad *dd); + struct abx500_accdet_platform_data * + (*get_platform_data)(struct platform_device *pdev); +}; + +/* Forward declarations */ +extern irqreturn_t unplug_irq_handler(int irq, void *_userdata); +extern irqreturn_t plug_irq_handler(int irq, void *_userdata); +extern irqreturn_t button_press_irq_handler(int irq, void *_userdata); +extern irqreturn_t button_release_irq_handler(int irq, void *_userdata); +extern void accessory_regulator_enable(struct abx500_ad *dd, + enum accessory_regulator reg); +extern void accessory_regulator_disable(struct abx500_ad *dd, + enum accessory_regulator reg); +extern void report_jack_status(struct abx500_ad *dd); + +#ifdef CONFIG_INPUT_AB5500_ACCDET +extern struct abx500_ad ab5500_accessory_det_callbacks; +#endif + +#ifdef CONFIG_INPUT_AB8500_ACCDET +extern struct abx500_ad ab8500_accessory_det_callbacks; +#endif + +#endif /* _ABx500_ACCDET_H */ + diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index dd4bd663d7c..143a49b70cd 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -29,6 +29,13 @@ config INPUT_AB8500_ACCDET Say Y here to enable AV accessory detection features for ST-Ericsson's AB8500 Mix-Sig PMIC. +config INPUT_AB5500_ACCDET + bool "AB5500 AV Accessory detection" + depends on AB5500_CORE && AB5500_GPADC + help + Say Y here to enable AV accessory detection features for ST-Ericsson's + AB5500 Mix-Sig PMIC. + config INPUT_AB8500_PONKEY tristate "AB5500/AB8500 Pon (PowerOn) Key" depends on AB5500_CORE || AB8500_CORE diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 611fef71587..a645ddf3003 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -5,7 +5,8 @@ # Each configuration option enables a list of files. obj-$(CONFIG_INPUT_88PM860X_ONKEY) += 88pm860x_onkey.o -obj-$(CONFIG_INPUT_AB8500_ACCDET) += ab8500-accdet.o +obj-$(CONFIG_INPUT_AB8500_ACCDET) += abx500-accdet.o ab8500-accdet.o +obj-$(CONFIG_INPUT_AB5500_ACCDET) += abx500-accdet.o ab5500-accdet.o obj-$(CONFIG_INPUT_AB8500_PONKEY) += ab8500-ponkey.o obj-$(CONFIG_INPUT_AD714X) += ad714x.o obj-$(CONFIG_INPUT_AD714X_I2C) += ad714x-i2c.o diff --git a/drivers/input/misc/ab5500-accdet.c b/drivers/input/misc/ab5500-accdet.c new file mode 100644 index 00000000000..9858d93a475 --- /dev/null +++ b/drivers/input/misc/ab5500-accdet.c @@ -0,0 +1,363 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Jarmo K. Kuronen + * for ST-Ericsson. + * + * License terms: GPL V2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * Register definition for accessory detection. + */ +#define AB5500_REGU_CTRL1_SPARE_REG 0x84 +#define AB5500_ACC_DET_DB1_REG 0x20 +#define AB5500_ACC_DET_DB2_REG 0x21 +#define AB5500_ACC_DET_CTRL_REG 0x23 +#define AB5500_VDENC_CTRL0 0x80 + +#define ENABLE_TV_PLUG_DETECT 0x01 + +/* REGISTER: AB8500_ACC_DET_CTRL_REG */ +#define BITS_ACCDETCTRL2_ENA (0x20 | 0x10 | 0x08) +#define BITS_ACCDETCTRL1_ENA (0x02 | 0x01) + +/* REGISTER: AB8500_REGU_CTRL1_SPARE_REG */ +#define BIT_REGUCTRL1SPARE_VAMIC1_GROUND 0x01 + +/* REGISTER: AB8500_IT_SOURCE5_REG */ +#define BIT_ITSOURCE5_ACCDET1 0x02 + +static struct accessory_irq_descriptor ab5500_irq_desc[] = { + { + .irq = PLUG_IRQ, + .name = "acc_detedt1db_falling", + .isr = plug_irq_handler, + }, + { + .irq = UNPLUG_IRQ, + .name = "acc_detedt1db_rising", + .isr = unplug_irq_handler, + }, + { + .irq = BUTTON_PRESS_IRQ, + .name = "acc_detedt21db_falling", + .isr = button_press_irq_handler, + }, + { + .irq = BUTTON_RELEASE_IRQ, + .name = "acc_detedt21db_rising", + .isr = button_release_irq_handler, + }, +}; + +static struct accessory_regu_descriptor ab5500_regu_desc[] = { + { + .id = REGULATOR_VAMIC1, + .name = "v-amic", + }, +}; + + +/* + * configures accdet2 input on/off + */ +static void ab5500_config_accdetect2_hw(struct abx500_ad *dd, int enable) +{ + int ret = 0; + + if (!dd->accdet2_th_set) { + /* Configure accdetect21+22 thresholds */ + ret = abx500_set_register_interruptible(&dd->pdev->dev, + AB5500_BANK_FG_BATTCOM_ACC, + AB5500_ACC_DET_DB2_REG, + dd->pdata->accdet2122_th); + if (ret < 0) { + dev_err(&dd->pdev->dev, + "%s: Failed to write reg (%d).\n", __func__, + ret); + goto out; + } else { + dd->accdet2_th_set = 1; + } + } + + /* Enable/Disable accdetect21 comparators + pullup */ + ret = abx500_mask_and_set_register_interruptible( + &dd->pdev->dev, + AB5500_BANK_FG_BATTCOM_ACC, + AB5500_ACC_DET_CTRL_REG, + BITS_ACCDETCTRL2_ENA, + enable ? BITS_ACCDETCTRL2_ENA : 0); + + if (ret < 0) + dev_err(&dd->pdev->dev, "%s: Failed to update reg (%d).\n", + __func__, ret); +out: + return; +} + +/* + * configures accdet1 input on/off + */ +static void ab5500_config_accdetect1_hw(struct abx500_ad *dd, int enable) +{ + int ret; + + if (!dd->accdet1_th_set) { + ret = abx500_set_register_interruptible(&dd->pdev->dev, + AB5500_BANK_FG_BATTCOM_ACC, + AB5500_ACC_DET_DB1_REG, + dd->pdata->accdet1_dbth); + if (ret < 0) + dev_err(&dd->pdev->dev, + "%s: Failed to write reg (%d).\n", __func__, + ret); + else + dd->accdet1_th_set = 1; + } + + /* enable accdetect1 comparator */ + ret = abx500_mask_and_set_register_interruptible( + &dd->pdev->dev, + AB5500_BANK_FG_BATTCOM_ACC, + AB5500_ACC_DET_CTRL_REG, + BITS_ACCDETCTRL1_ENA, + enable ? BITS_ACCDETCTRL1_ENA : 0); + + if (ret < 0) + dev_err(&dd->pdev->dev, + "%s: Failed to update reg (%d).\n", __func__, ret); +} + +/* + * returns the high level status whether some accessory is connected (1|0). + */ +static int ab5500_detect_plugged_in(struct abx500_ad *dd) +{ + u8 value = 0; + + int status = abx500_get_register_interruptible( + &dd->pdev->dev, + AB5500_BANK_IT, + AB5500_IT_SOURCE3_REG, + &value); + if (status < 0) { + dev_err(&dd->pdev->dev, "%s: reg read failed (%d).\n", + __func__, status); + return 0; + } + + if (dd->pdata->is_detection_inverted) + return value & BIT_ITSOURCE5_ACCDET1 ? 1 : 0; + else + return value & BIT_ITSOURCE5_ACCDET1 ? 0 : 1; +} + +/* + * mic_line_voltage_stable - measures a relative stable voltage from spec. input + */ +static int ab5500_meas_voltage_stable(struct abx500_ad *dd) +{ + int iterations = 2; + int v1, v2, dv; + + v1 = ab5500_gpadc_convert((struct ab5500_gpadc *)dd->gpadc, + ACC_DETECT2); + do { + msleep(1); + --iterations; + v2 = ab5500_gpadc_convert((struct ab5500_gpadc *)dd->gpadc, + ACC_DETECT2); + dv = abs(v2 - v1); + v1 = v2; + } while (iterations > 0 && dv > MAX_VOLT_DIFF); + + return v1; +} + +/* + * configures HW so that it is possible to make decision whether + * accessory is connected or not. + */ +static void ab5500_config_hw_test_plug_connected(struct abx500_ad *dd, + int enable) +{ + dev_dbg(&dd->pdev->dev, "%s:%d\n", __func__, enable); + + /* enable mic BIAS2 */ + if (enable) + accessory_regulator_enable(dd, REGULATOR_VAMIC1); +} + +/* + * configures HW so that carkit/headset detection can be accomplished. + */ +static void ab5500_config_hw_test_basic_carkit(struct abx500_ad *dd, int enable) +{ + /* enable mic BIAS2 */ + if (enable) + accessory_regulator_disable(dd, REGULATOR_VAMIC1); +} + +static u8 acc_det_ctrl_suspend_val; + +static void ab5500_turn_off_accdet_comparator(struct platform_device *pdev) +{ + struct abx500_ad *dd = platform_get_drvdata(pdev); + + /* Turn off AccDetect comparators and pull-up */ + (void) abx500_get_register_interruptible( + &dd->pdev->dev, + AB5500_BANK_FG_BATTCOM_ACC, + AB5500_ACC_DET_CTRL_REG, + &acc_det_ctrl_suspend_val); + (void) abx500_set_register_interruptible( + &dd->pdev->dev, + AB5500_BANK_FG_BATTCOM_ACC, + AB5500_ACC_DET_CTRL_REG, + 0); +} + +static void ab5500_turn_on_accdet_comparator(struct platform_device *pdev) +{ + struct abx500_ad *dd = platform_get_drvdata(pdev); + + /* Turn on AccDetect comparators and pull-up */ + (void) abx500_set_register_interruptible( + &dd->pdev->dev, + AB5500_BANK_FG_BATTCOM_ACC, + AB5500_ACC_DET_CTRL_REG, + acc_det_ctrl_suspend_val); +} + +irqreturn_t plug_tv_connect_irq_handler(int irq, void *_userdata) +{ + struct abx500_ad *dd = _userdata; + + dev_dbg(&dd->pdev->dev, "%s: Enter (irq=%d)\n", __func__, irq); + + dd->tv_out_connected = true; + report_jack_status(dd); + + return IRQ_HANDLED; +} + +irqreturn_t plug_tv_removal_irq_handler(int irq, void *_userdata) +{ + struct abx500_ad *dd = _userdata; + + dev_dbg(&dd->pdev->dev, "%s: Enter (irq=%d)\n", __func__, irq); + + dd->tv_out_connected = false; + report_jack_status(dd); + + return IRQ_HANDLED; +} + +static void *ab5500_accdet_abx500_gpadc_get(void) +{ + return ab5500_gpadc_get("ab5500-adc.0"); +} + +static int ab5500_startup(struct abx500_ad *dd) +{ + int ret; + int irq, irq_removal; + + irq = platform_get_irq_byname(dd->pdev, + "plugTVdet"); + if (irq < 0) { + dev_err(&dd->pdev->dev, "%s: Failed to get irq plugTVdet \n", + __func__); + return irq; + } + + irq_removal = platform_get_irq_byname(dd->pdev, + "plugTVdet_removal"); + if (irq_removal < 0) { + dev_err(&dd->pdev->dev, "%s: Failed to get irq" + "plugTVdet_removal \n", __func__); + return irq_removal; + } + + ret = request_threaded_irq(irq, NULL, + plug_tv_connect_irq_handler, + IRQF_NO_SUSPEND | IRQF_SHARED, + "plugTVdet", + dd); + if (ret != 0) { + dev_err(&dd->pdev->dev, + "%s: Failed to claim irq plugTVdet (%d)\n", + __func__, ret); + return ret; + } + + ret = request_threaded_irq(irq_removal, NULL, + plug_tv_removal_irq_handler, + IRQF_NO_SUSPEND | IRQF_SHARED, + "plugTVdet_removal", + dd); + if (ret != 0) { + dev_err(&dd->pdev->dev, + "%s: Failed to claim irq plugTVdet_removal (%d)\n", + __func__, ret); + goto req_irq_fail; + } + + ret = abx500_set_register_interruptible(&dd->pdev->dev, + AB5500_BANK_VDENC, AB5500_VDENC_CTRL0, + ENABLE_TV_PLUG_DETECT); + if (ret < 0) { + dev_err(&dd->pdev->dev, + "%s: Failed to update reg (%d).\n", __func__, ret); + goto ab_write_fail; + } + + return 0; + +req_irq_fail: +ab_write_fail: + free_irq(irq, dd); + return ret; +} + +struct abx500_accdet_platform_data * + ab5500_get_platform_data(struct platform_device *pdev) +{ + return pdev->dev.platform_data; +} + +struct abx500_ad ab5500_accessory_det_callbacks = { + .irq_desc_norm = ab5500_irq_desc, + .irq_desc_inverted = NULL, + .no_irqs = ARRAY_SIZE(ab5500_irq_desc), + .regu_desc = ab5500_regu_desc, + .no_of_regu_desc = ARRAY_SIZE(ab5500_regu_desc), + .config_accdetect2_hw = ab5500_config_accdetect2_hw, + .config_accdetect1_hw = ab5500_config_accdetect1_hw, + .detect_plugged_in = ab5500_detect_plugged_in, + .meas_voltage_stable = ab5500_meas_voltage_stable, + .config_hw_test_basic_carkit = ab5500_config_hw_test_basic_carkit, + .turn_off_accdet_comparator = ab5500_turn_off_accdet_comparator, + .turn_on_accdet_comparator = ab5500_turn_on_accdet_comparator, + .accdet_abx500_gpadc_get = ab5500_accdet_abx500_gpadc_get, + .config_hw_test_plug_connected = ab5500_config_hw_test_plug_connected, + .startup = ab5500_startup, + .set_av_switch = NULL, + .get_platform_data = ab5500_get_platform_data, +}; + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/misc/ab8500-accdet.c b/drivers/input/misc/ab8500-accdet.c index bc14143d1b6..aa93aa0efc2 100644 --- a/drivers/input/misc/ab8500-accdet.c +++ b/drivers/input/misc/ab8500-accdet.c @@ -11,30 +11,16 @@ * by the Free Software Foundation. */ -#include /* Needed by all modules */ -#include /* Needed for KERN_INFO */ -#include /* Needed for the macros */ -#include #include -#include -#include -#include -#include -#include -#include -#include +#include #include #include +#include #include #include #include -#include -#include -#ifdef CONFIG_SND_SOC_UX500_AB8500 -#include -#else -#define ux500_ab8500_jack_report(i) -#endif +#include +#include #define MAX_DET_COUNT 10 #define MAX_VOLT_DIFF 30 @@ -83,292 +69,9 @@ #define ACCESSORY_OPENCABLE_DET_VOL_MIN 1730 #define ACCESSORY_OPENCABLE_DET_VOL_MAX 2150 -/* Macros */ - -/* - * Conviniency macros to check jack characteristics. - */ -#define jack_supports_mic(type) \ - (type == JACK_TYPE_HEADSET || type == JACK_TYPE_CARKIT) -#define jack_supports_spkr(type) \ - ((type != JACK_TYPE_DISCONNECTED) && (type != JACK_TYPE_CONNECTED)) -#define jack_supports_buttons(type) \ - ((type == JACK_TYPE_HEADSET) ||\ - (type == JACK_TYPE_CARKIT) ||\ - (type == JACK_TYPE_OPENCABLE) ||\ - (type == JACK_TYPE_CONNECTED)) - - -/* Enumerations */ - -/** - * @JACK_TYPE_UNSPECIFIED Not known whether any accessories are connected. - * @JACK_TYPE_DISCONNECTED No accessories connected. - * @JACK_TYPE_CONNECTED Accessory is connected but functionality was unable to - * detect the actual type. In this mode, possible button events are reported. - * @JACK_TYPE_HEADPHONE Headphone type of accessory (spkrs only) connected - * @JACK_TYPE_HEADSET Headset type of accessory (mic+spkrs) connected - * @JACK_TYPE_CARKIT Carkit type of accessory connected - * @JACK_TYPE_OPENCABLE Open cable connected - * @JACK_TYPE_CVIDEO CVideo type of accessory connected. - */ -enum accessory_jack_type { - JACK_TYPE_UNSPECIFIED, - JACK_TYPE_DISCONNECTED, - JACK_TYPE_CONNECTED, - JACK_TYPE_HEADPHONE, - JACK_TYPE_HEADSET, - JACK_TYPE_CARKIT, - JACK_TYPE_OPENCABLE, - JACK_TYPE_CVIDEO -}; - -/** - * @BUTTON_UNK Button state not known - * @BUTTON_PRESSED Button "down" - * @BUTTON_RELEASED Button "up" - */ -enum accessory_button_state { - BUTTON_UNK, - BUTTON_PRESSED, - BUTTON_RELEASED -}; - -/** - * @PLUG_IRQ Interrupt gen. when accessory plugged in - * @UNPLUG_IRQ Interrupt gen. when accessory plugged out - * @BUTTON_PRESS_IRQ Interrupt gen. when accessory button pressed. - * @BUTTON_RELEASE_IRQ Interrupt gen. when accessory button released. - */ -enum accessory_irq { - PLUG_IRQ, - UNPLUG_IRQ, - BUTTON_PRESS_IRQ, - BUTTON_RELEASE_IRQ -}; - -/** - * Enumerates the op. modes of the avcontrol switch - * @AUDIO_IN Audio input is selected - * @VIDEO_OUT Video output is selected - * @NOT_SET The av-switch control signal is disconnected. - */ -enum accessory_avcontrol_dir { - AUDIO_IN, - VIDEO_OUT, - NOT_SET, -}; - -/** - * @REGULATOR_VAUDIO v-audio regulator - * @REGULATOR_VAMIC1 v-amic1 regulator - * @REGULATOR_AVSWITCH Audio/Video select switch regulator - * @REGULATOR_ALL All regulators combined - */ -enum accessory_regulator { - REGULATOR_NONE = 0x0, - REGULATOR_VAUDIO = 0x1, - REGULATOR_VAMIC1 = 0x2, - REGULATOR_AVSWITCH = 0x4, - REGULATOR_ALL = 0xFF -}; - -/* Structures */ - -/** - * Describes an interrupt - * @irq interrupt identifier - * @name name of the irq in platform data - * @isr interrupt service routine - * @register are we currently registered to receive interrupts from this source. - */ -struct accessory_irq_descriptor { - enum accessory_irq irq; - const char *name; - irq_handler_t isr; - int registered; -}; - -/** - * Encapsulates info of single regulator. - * @id regulator identifier - * @name name of the regulator - * @enabled flag indicating whether regu is currently enabled. - * @handle regulator handle - */ -struct accessory_regu_descriptor { - enum accessory_regulator id; - const char *name; - int enabled; - struct regulator *handle; -}; - -/** - * Defines attributes for accessory detection operation. - * @typename type as string - * @type Type of accessory this task tests - * @req_det_count How many times this particular type of accessory - * needs to be detected in sequence in order to accept. Multidetection - * implemented to avoid false detections during plug-in. - * @meas_mv Should ACCDETECT2 input voltage be measured just before - * making the decision or can cached voltage be used instead. - * @minvol minimum voltage (mV) for decision - * @maxvol maximum voltage (mV) for decision - */ -struct accessory_detect_task { - const char *typename; - enum accessory_jack_type type; - int req_det_count; - int meas_mv; - int minvol; - int maxvol; -}; - -/** - * Device data, capsulates all relevant device data structures. - * - * @pdev pointer to platform device - * @pdata Platform data - * @gpadc interface for ADC data - * @irq_work_queue Work queue for deferred interrupt processing - * - * @detect_work work item to perform detection work - * @unplug_irq_work work item to process unplug event - * @init_work work item to process initialization work. - * - * @btn_input_dev button input device used to report btn presses - * @btn_state Current state of accessory button - * - * @jack_type type of currently connected accessory - * @reported_jack_type previously reported jack type. - * @jack_type_temp temporary storage for currently connected accessory - * - * @jack_det_count counter how many times in sequence the accessory - * type detection has produced same result. - * @total_jack_det_count after plug-in irq, how many times detection - * has totally been made in order to detect the accessory type - * - * @detect_jiffies Used to save timestamp when detection was made. Timestamp - * used to filter out spurious button presses that might occur during the - * plug-in procedure. - * - * @accdet1_th_set flag to indicate whether accdet1 threshold and debounce - * times are configured - * @accdet2_th_set flag to indicate whether accdet2 thresholds are configured - * @gpio35_dir_set flag to indicate whether GPIO35 (VIDEOCTRL) direction - * has been configured. - * @ab_switch userspace android switch interface - */ -struct ab8500_ad { - struct platform_device *pdev; - struct ab8500_accdet_platform_data *pdata; - struct ab8500_gpadc *gpadc; - struct workqueue_struct *irq_work_queue; - - struct delayed_work detect_work; - struct delayed_work unplug_irq_work; - struct delayed_work init_work; - - struct input_dev *btn_input_dev; - enum accessory_button_state btn_state; - - enum accessory_jack_type jack_type; - enum accessory_jack_type reported_jack_type; - enum accessory_jack_type jack_type_temp; - - int jack_det_count; - int total_jack_det_count; - - unsigned long detect_jiffies; - - int accdet1_th_set; - int accdet2_th_set; - int gpio35_dir_set; - struct switch_dev ab_switch; -}; - -/* Forward declarations */ - -static void config_accdetect(struct ab8500_ad *dd); - -static void release_irq(struct ab8500_ad *dd, enum accessory_irq irq_id); -static void claim_irq(struct ab8500_ad *dd, enum accessory_irq irq_id); - -static irqreturn_t unplug_irq_handler(int irq, void *_userdata); -static irqreturn_t plug_irq_handler(int irq, void *_userdata); -static irqreturn_t button_press_irq_handler(int irq, void *_userdata); -static irqreturn_t button_release_irq_handler(int irq, void *_userdata); - -static void unplug_irq_handler_work(struct work_struct *work); -static void detect_work(struct work_struct *work); -static void init_work(struct work_struct *work); - -static enum accessory_jack_type detect(struct ab8500_ad *dd, int *required_det); -static void set_av_switch(struct ab8500_ad *dd, - enum accessory_avcontrol_dir dir); - /* Static data initialization */ -static struct accessory_detect_task detect_ops[] = { - { - .type = JACK_TYPE_DISCONNECTED, - .typename = "DISCONNECTED", - .meas_mv = 1, - .req_det_count = 1, - .minvol = ACCESSORY_DET_VOL_DONTCARE, - .maxvol = ACCESSORY_DET_VOL_DONTCARE - }, - { - .type = JACK_TYPE_HEADPHONE, - .typename = "HEADPHONE", - .meas_mv = 1, - .req_det_count = 1, - .minvol = ACCESSORY_HEADPHONE_DET_VOL_MIN, - .maxvol = ACCESSORY_HEADPHONE_DET_VOL_MAX - }, - { - .type = JACK_TYPE_CVIDEO, - .typename = "CVIDEO", - .meas_mv = 0, - .req_det_count = 4, - .minvol = ACCESSORY_CVIDEO_DET_VOL_MIN, - .maxvol = ACCESSORY_CVIDEO_DET_VOL_MAX - }, - { - .type = JACK_TYPE_OPENCABLE, - .typename = "OPENCABLE", - .meas_mv = 0, - .req_det_count = 4, - .minvol = ACCESSORY_OPENCABLE_DET_VOL_MIN, - .maxvol = ACCESSORY_OPENCABLE_DET_VOL_MAX - }, - { - .type = JACK_TYPE_CARKIT, - .typename = "CARKIT", - .meas_mv = 1, - .req_det_count = 1, - .minvol = ACCESSORY_CARKIT_DET_VOL_MIN, - .maxvol = ACCESSORY_CARKIT_DET_VOL_MAX - }, - { - .type = JACK_TYPE_HEADSET, - .typename = "HEADSET", - .meas_mv = 0, - .req_det_count = 2, - .minvol = ACCESSORY_HEADSET_DET_VOL_MIN, - .maxvol = ACCESSORY_HEADSET_DET_VOL_MAX - }, - { - .type = JACK_TYPE_CONNECTED, - .typename = "CONNECTED", - .meas_mv = 0, - .req_det_count = 4, - .minvol = ACCESSORY_DET_VOL_DONTCARE, - .maxvol = ACCESSORY_DET_VOL_DONTCARE - } -}; - -static struct accessory_regu_descriptor regu_desc[3] = { +static struct accessory_regu_descriptor ab8500_regu_desc[3] = { { .id = REGULATOR_VAUDIO, .name = "v-audio", @@ -383,7 +86,7 @@ static struct accessory_regu_descriptor regu_desc[3] = { }, }; -static struct accessory_irq_descriptor irq_desc_norm[] = { +static struct accessory_irq_descriptor ab8500_irq_desc_norm[] = { { .irq = PLUG_IRQ, .name = "ACC_DETECT_1DB_F", @@ -406,7 +109,7 @@ static struct accessory_irq_descriptor irq_desc_norm[] = { }, }; -static struct accessory_irq_descriptor irq_desc_inverted[] = { +static struct accessory_irq_descriptor ab8500_irq_desc_inverted[] = { { .irq = PLUG_IRQ, .name = "ACC_DETECT_1DB_R", @@ -429,102 +132,10 @@ static struct accessory_irq_descriptor irq_desc_inverted[] = { }, }; -static struct accessory_irq_descriptor *irq_desc; - -/* - * textual represenation of the accessory type - */ -static const char *accessory_str(enum accessory_jack_type type) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(detect_ops); i++) - if (type == detect_ops[i].type) - return detect_ops[i].typename; - - return "UNKNOWN?"; -} - -/* - * enables regulator but only if it has not been enabled earlier. - */ -static void accessory_regulator_enable(enum accessory_regulator reg) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(regu_desc); i++) { - if (reg & regu_desc[i].id) { - if (!regu_desc[i].enabled) { - if (!regulator_enable(regu_desc[i].handle)) - regu_desc[i].enabled = 1; - } - } - } -} - -/* - * disables regulator but only if it has been previously enabled. - */ -static void accessory_regulator_disable(enum accessory_regulator reg) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(regu_desc); i++) { - if (reg & regu_desc[i].id) { - if (regu_desc[i].enabled) { - if (!regulator_disable(regu_desc[i].handle)) - regu_desc[i].enabled = 0; - } - } - } -} - -/* - * frees previously retrieved regulators. - */ -static void free_regulators(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(regu_desc); i++) { - if (regu_desc[i].handle) { - regulator_put(regu_desc[i].handle); - regu_desc[i].handle = NULL; - } - } -} - -/* - * gets required regulators. - */ -static int create_regulators(struct ab8500_ad *dd) -{ - int i; - int status = 0; - - for (i = 0; i < ARRAY_SIZE(regu_desc); i++) { - struct regulator *regu = - regulator_get(&dd->pdev->dev, regu_desc[i].name); - if (IS_ERR(regu)) { - status = PTR_ERR(regu); - dev_err(&dd->pdev->dev, - "%s: Failed to get supply '%s' (%d).\n", - __func__, regu_desc[i].name, status); - free_regulators(); - goto out; - } else { - regu_desc[i].handle = regu; - } - } - -out: - return status; -} - /* * configures accdet2 input on/off */ -static void config_accdetect2_hw(struct ab8500_ad *dd, int enable) +static void ab8500_config_accdetect2_hw(struct abx500_ad *dd, int enable) { int ret = 0; @@ -563,7 +174,7 @@ out: /* * configures accdet1 input on/off */ -static void config_accdetect1_hw(struct ab8500_ad *dd, int enable) +static void ab8500_config_accdetect1_hw(struct abx500_ad *dd, int enable) { int ret; @@ -593,87 +204,10 @@ static void config_accdetect1_hw(struct ab8500_ad *dd, int enable) "%s: Failed to update reg (%d).\n", __func__, ret); } -/* - * create input device for button press reporting - */ -static int create_btn_input_dev(struct ab8500_ad *dd) -{ - int err; - - dd->btn_input_dev = input_allocate_device(); - if (!dd->btn_input_dev) { - dev_err(&dd->pdev->dev, "%s: Failed to allocate input dev.\n", - __func__); - err = -ENOMEM; - goto out; - } - - input_set_capability(dd->btn_input_dev, - EV_KEY, - dd->pdata->btn_keycode); - - dd->btn_input_dev->name = BTN_INPUT_DEV_NAME; - dd->btn_input_dev->uniq = BTN_INPUT_UNIQUE_VALUE; - dd->btn_input_dev->dev.parent = &dd->pdev->dev; - - err = input_register_device(dd->btn_input_dev); - if (err) { - dev_err(&dd->pdev->dev, - "%s: register_input_device failed (%d).\n", __func__, - err); - input_free_device(dd->btn_input_dev); - dd->btn_input_dev = NULL; - goto out; - } -out: - return err; -} - -/* - * reports jack status - */ -static void report_jack_status(struct ab8500_ad *dd) -{ - int value = 0; - - /* Never report possible open cable */ - if (dd->jack_type == JACK_TYPE_OPENCABLE) - goto out; - - /* Never report same state twice in a row */ - if (dd->jack_type == dd->reported_jack_type) - goto out; - dd->reported_jack_type = dd->jack_type; - - dev_info(&dd->pdev->dev, "Accessory: %s\n", - accessory_str(dd->jack_type)); - - if (dd->jack_type != JACK_TYPE_DISCONNECTED && - dd->jack_type != JACK_TYPE_UNSPECIFIED) - value |= SND_JACK_MECHANICAL; - if (jack_supports_mic(dd->jack_type)) - value |= SND_JACK_MICROPHONE; - if (jack_supports_spkr(dd->jack_type)) - value |= (SND_JACK_HEADPHONE | SND_JACK_LINEOUT); - if (dd->jack_type == JACK_TYPE_CVIDEO) { - value |= SND_JACK_VIDEOOUT; - set_av_switch(dd, VIDEO_OUT); - } - - if (dd->jack_type == JACK_TYPE_DISCONNECTED) - switch_set_state(&dd->ab_switch, 0); - else - switch_set_state(&dd->ab_switch, 1); - - ux500_ab8500_jack_report(value); - -out: return; -} - /* * returns the high level status whether some accessory is connected (1|0). */ -static int detect_plugged_in(struct ab8500_ad *dd) +static int ab8500_detect_plugged_in(struct abx500_ad *dd) { u8 value = 0; @@ -697,16 +231,18 @@ static int detect_plugged_in(struct ab8500_ad *dd) /* * mic_line_voltage_stable - measures a relative stable voltage from spec. input */ -static int meas_voltage_stable(struct ab8500_ad *dd, u8 input) +static int ab8500_meas_voltage_stable(struct abx500_ad *dd) { int iterations = 2; int v1, v2, dv; - v1 = ab8500_gpadc_convert(dd->gpadc, input); + v1 = ab8500_gpadc_convert((struct ab8500_gpadc *)dd->gpadc, + ACC_DETECT2); do { msleep(1); --iterations; - v2 = ab8500_gpadc_convert(dd->gpadc, input); + v2 = ab8500_gpadc_convert((struct ab8500_gpadc *)dd->gpadc, + ACC_DETECT2); dv = abs(v2 - v1); v1 = v2; } while (iterations > 0 && dv > MAX_VOLT_DIFF); @@ -714,188 +250,12 @@ static int meas_voltage_stable(struct ab8500_ad *dd, u8 input) return v1; } -/* - * worker routine to handle accessory unplug case - */ -static void unplug_irq_handler_work(struct work_struct *work) -{ - struct ab8500_ad *dd = container_of(work, - struct ab8500_ad, unplug_irq_work.work); - - dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__); - - dd->jack_type = dd->jack_type_temp = JACK_TYPE_DISCONNECTED; - dd->jack_det_count = dd->total_jack_det_count = 0; - dd->btn_state = BUTTON_UNK; - config_accdetect(dd); - - accessory_regulator_disable(REGULATOR_ALL); - - report_jack_status(dd); -} - -/* - * interrupt service routine for accessory unplug. - */ -static irqreturn_t unplug_irq_handler(int irq, void *_userdata) -{ - struct ab8500_ad *dd = _userdata; - - dev_dbg(&dd->pdev->dev, "%s: Enter (irq=%d)\n", __func__, irq); - - queue_delayed_work(dd->irq_work_queue, &dd->unplug_irq_work, - msecs_to_jiffies(DEBOUNCE_UNPLUG_EVENT_MS)); - - return IRQ_HANDLED; -} - -/* - * interrupt service routine for accessory plug. - */ -static irqreturn_t plug_irq_handler(int irq, void *_userdata) -{ - struct ab8500_ad *dd = _userdata; - - dev_dbg(&dd->pdev->dev, "%s: Enter (irq=%d)\n", - __func__, irq); - - switch (dd->jack_type) { - case JACK_TYPE_DISCONNECTED: - case JACK_TYPE_UNSPECIFIED: - queue_delayed_work(dd->irq_work_queue, &dd->detect_work, - msecs_to_jiffies(DEBOUNCE_PLUG_EVENT_MS)); - break; - - default: - dev_err(&dd->pdev->dev, "%s: Unexpected plug IRQ\n", __func__); - break; - } - - return IRQ_HANDLED; -} - -/* - * worker routine to perform detection. - */ -static void detect_work(struct work_struct *work) -{ - int req_det_count = 1; - enum accessory_jack_type new_type; - struct ab8500_ad *dd = container_of(work, - struct ab8500_ad, detect_work.work); - - dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__); - - set_av_switch(dd, AUDIO_IN); - - new_type = detect(dd, &req_det_count); - - dd->total_jack_det_count++; - if (dd->jack_type_temp == new_type) { - dd->jack_det_count++; - } else { - dd->jack_det_count = 1; - dd->jack_type_temp = new_type; - } - - if (dd->total_jack_det_count >= MAX_DET_COUNT) { - dev_err(&dd->pdev->dev, - "%s: MAX_DET_COUNT(=%d) reached. Bailing out.\n", - __func__, MAX_DET_COUNT); - queue_delayed_work(dd->irq_work_queue, &dd->unplug_irq_work, - msecs_to_jiffies(DEBOUNCE_UNPLUG_EVENT_MS)); - } else if (dd->jack_det_count >= req_det_count) { - dd->total_jack_det_count = dd->jack_det_count = 0; - dd->jack_type = new_type; - dd->detect_jiffies = jiffies; - report_jack_status(dd); - config_accdetect(dd); - } else { - queue_delayed_work(dd->irq_work_queue, - &dd->detect_work, - msecs_to_jiffies(DEBOUNCE_PLUG_RETEST_MS)); - } -} - -/* - * reports a button event (pressed, released). - */ -static void report_btn_event(struct ab8500_ad *dd, int down) -{ - input_report_key(dd->btn_input_dev, dd->pdata->btn_keycode, down); - input_sync(dd->btn_input_dev); - - dev_dbg(&dd->pdev->dev, "HS-BTN: %s\n", down ? "PRESSED" : "RELEASED"); -} - -/* - * interrupt service routine invoked when hs button is pressed down. - */ -static irqreturn_t button_press_irq_handler(int irq, void *_userdata) -{ - struct ab8500_ad *dd = _userdata; - - unsigned long accept_jiffies = dd->detect_jiffies + - msecs_to_jiffies(1000); - if (time_before(jiffies, accept_jiffies)) { - dev_dbg(&dd->pdev->dev, "%s: Skipped spurious btn press.\n", - __func__); - return IRQ_HANDLED; - } - - dev_dbg(&dd->pdev->dev, "%s: Enter (irq=%d)\n", __func__, irq); - - if (dd->jack_type == JACK_TYPE_OPENCABLE) { - /* Someting got connected to open cable -> detect.. */ - config_accdetect2_hw(dd, 0); - queue_delayed_work(dd->irq_work_queue, &dd->detect_work, - msecs_to_jiffies(DEBOUNCE_PLUG_EVENT_MS)); - return IRQ_HANDLED; - } - - if (dd->btn_state == BUTTON_PRESSED) - return IRQ_HANDLED; - - if (jack_supports_buttons(dd->jack_type)) { - dd->btn_state = BUTTON_PRESSED; - report_btn_event(dd, 1); - } else { - dd->btn_state = BUTTON_UNK; - } - - return IRQ_HANDLED; -} - -/* - * interrupts service routine invoked when hs button is released. - */ -static irqreturn_t button_release_irq_handler(int irq, void *_userdata) -{ - struct ab8500_ad *dd = _userdata; - - dev_dbg(&dd->pdev->dev, "%s: Enter (irq=%d)\n", __func__, irq); - - if (dd->jack_type == JACK_TYPE_OPENCABLE) - return IRQ_HANDLED; - - if (dd->btn_state != BUTTON_PRESSED) - return IRQ_HANDLED; - - if (jack_supports_buttons(dd->jack_type)) { - report_btn_event(dd, 0); - dd->btn_state = BUTTON_RELEASED; - } else { - dd->btn_state = BUTTON_UNK; - } - - return IRQ_HANDLED; -} - /* * configures HW so that it is possible to make decision whether * accessory is connected or not. */ -static void config_hw_test_plug_connected(struct ab8500_ad *dd, int enable) +static void ab8500_config_hw_test_plug_connected(struct abx500_ad *dd, + int enable) { int ret; @@ -910,20 +270,20 @@ static void config_hw_test_plug_connected(struct ab8500_ad *dd, int enable) } if (enable) - accessory_regulator_enable(REGULATOR_VAMIC1); + accessory_regulator_enable(dd, REGULATOR_VAMIC1); } /* * configures HW so that carkit/headset detection can be accomplished. */ -static void config_hw_test_basic_carkit(struct ab8500_ad *dd, int enable) +static void ab8500_config_hw_test_basic_carkit(struct abx500_ad *dd, int enable) { int ret; dev_dbg(&dd->pdev->dev, "%s:%d\n", __func__, enable); if (enable) - accessory_regulator_disable(REGULATOR_VAMIC1); + accessory_regulator_disable(dd, REGULATOR_VAMIC1); /* Un-Ground the VAMic1 output when enabled */ ret = abx500_mask_and_set_register_interruptible( @@ -937,61 +297,10 @@ static void config_hw_test_basic_carkit(struct ab8500_ad *dd, int enable) "%s: Failed to update reg (%d).\n", __func__, ret); } -/* - * checks whether measured voltage is in given range. depending on arguments, - * voltage might be re-measured or previously measured voltage is reused. - */ -static int mic_vol_in_range(struct ab8500_ad *dd, - int lo, int hi, int force_read) -{ - static int mv = MIN_MIC_POWER; - - if (mv == -100 || force_read) - mv = meas_voltage_stable(dd, ACC_DETECT2); - - return (mv >= lo && mv <= hi) ? 1 : 0; -} - -/* - * checks whether the currently connected HW is of given type. - */ -static int detect_hw(struct ab8500_ad *dd, - struct accessory_detect_task *task) -{ - int status; - - switch (task->type) { - case JACK_TYPE_DISCONNECTED: - config_hw_test_plug_connected(dd, 1); - status = !detect_plugged_in(dd); - break; - case JACK_TYPE_CONNECTED: - config_hw_test_plug_connected(dd, 1); - status = detect_plugged_in(dd); - break; - case JACK_TYPE_CARKIT: - config_hw_test_basic_carkit(dd, 1); - /* flow through.. */ - case JACK_TYPE_HEADPHONE: - case JACK_TYPE_CVIDEO: - case JACK_TYPE_HEADSET: - case JACK_TYPE_OPENCABLE: - status = mic_vol_in_range(dd, - task->minvol, - task->maxvol, - task->meas_mv); - break; - default: - status = 0; - } - - return status; -} - /* * sets the av switch direction - audio-in vs video-out */ -static void set_av_switch(struct ab8500_ad *dd, +static void ab8500_set_av_switch(struct abx500_ad *dd, enum accessory_avcontrol_dir dir) { int ret; @@ -1019,345 +328,11 @@ static void set_av_switch(struct ab8500_ad *dd, } } -/* - * Tries to detect the currently attached accessory - */ -static enum accessory_jack_type detect(struct ab8500_ad *dd, - int *req_det_count) -{ - enum accessory_jack_type type = JACK_TYPE_DISCONNECTED; - int i; - - accessory_regulator_enable(REGULATOR_VAUDIO | REGULATOR_AVSWITCH); - - for (i = 0; i < ARRAY_SIZE(detect_ops); ++i) { - if (detect_hw(dd, &detect_ops[i])) { - type = detect_ops[i].type; - *req_det_count = detect_ops[i].req_det_count; - break; - } - } - - config_hw_test_basic_carkit(dd, 0); - config_hw_test_plug_connected(dd, 0); - - if (jack_supports_buttons(type)) - accessory_regulator_enable(REGULATOR_VAMIC1); - else - accessory_regulator_disable(REGULATOR_VAMIC1 | - REGULATOR_AVSWITCH); - - accessory_regulator_disable(REGULATOR_VAUDIO); - - return type; -} - -/* - * registers to specific interrupt - */ -static void claim_irq(struct ab8500_ad *dd, enum accessory_irq irq_id) -{ - int ret; - int irq; - - if (dd->pdata->is_detection_inverted) - irq_desc = irq_desc_inverted; - else - irq_desc = irq_desc_norm; - - if (irq_desc[irq_id].registered) - return; - - irq = platform_get_irq_byname( - dd->pdev, - irq_desc[irq_id].name); - if (irq < 0) { - dev_err(&dd->pdev->dev, - "%s: Failed to get irq %s\n", __func__, - irq_desc[irq_id].name); - return; - } - - ret = request_threaded_irq(irq, - NULL, - irq_desc[irq_id].isr, - IRQF_NO_SUSPEND | IRQF_SHARED, - irq_desc[irq_id].name, - dd); - if (ret != 0) { - dev_err(&dd->pdev->dev, - "%s: Failed to claim irq %s (%d)\n", - __func__, - irq_desc[irq_id].name, - ret); - } else { - irq_desc[irq_id].registered = 1; - dev_dbg(&dd->pdev->dev, "%s: %s\n", - __func__, irq_desc[irq_id].name); - } -} - -/* - * releases specific interrupt - */ -static void release_irq(struct ab8500_ad *dd, enum accessory_irq irq_id) -{ - int irq; - - if (dd->pdata->is_detection_inverted) - irq_desc = irq_desc_inverted; - else - irq_desc = irq_desc_norm; - - if (!irq_desc[irq_id].registered) - return; - - irq = platform_get_irq_byname( - dd->pdev, - irq_desc[irq_id].name); - if (irq < 0) { - dev_err(&dd->pdev->dev, - "%s: Failed to get irq %s (%d)\n", - __func__, - irq_desc[irq_id].name, irq); - } else { - free_irq(irq, dd); - irq_desc[irq_id].registered = 0; - dev_dbg(&dd->pdev->dev, "%s: %s\n", - __func__, irq_desc[irq_id].name); - } -} - -/* - * configures interrupts + detection hardware to meet the requirements - * set by currently attached accessory type. - */ -static void config_accdetect(struct ab8500_ad *dd) -{ - switch (dd->jack_type) { - case JACK_TYPE_UNSPECIFIED: - config_accdetect1_hw(dd, 1); - config_accdetect2_hw(dd, 0); - - release_irq(dd, PLUG_IRQ); - release_irq(dd, UNPLUG_IRQ); - release_irq(dd, BUTTON_PRESS_IRQ); - release_irq(dd, BUTTON_RELEASE_IRQ); - set_av_switch(dd, NOT_SET); - break; - - case JACK_TYPE_DISCONNECTED: - set_av_switch(dd, NOT_SET); - case JACK_TYPE_HEADPHONE: - case JACK_TYPE_CVIDEO: - config_accdetect1_hw(dd, 1); - config_accdetect2_hw(dd, 0); - - claim_irq(dd, PLUG_IRQ); - claim_irq(dd, UNPLUG_IRQ); - release_irq(dd, BUTTON_PRESS_IRQ); - release_irq(dd, BUTTON_RELEASE_IRQ); - break; - - case JACK_TYPE_CONNECTED: - case JACK_TYPE_HEADSET: - case JACK_TYPE_CARKIT: - case JACK_TYPE_OPENCABLE: - config_accdetect1_hw(dd, 1); - config_accdetect2_hw(dd, 1); - - release_irq(dd, PLUG_IRQ); - claim_irq(dd, UNPLUG_IRQ); - claim_irq(dd, BUTTON_PRESS_IRQ); - claim_irq(dd, BUTTON_RELEASE_IRQ); - break; - - default: - dev_err(&dd->pdev->dev, "%s: Unknown type: %d\n", - __func__, dd->jack_type); - } -} - -/* - * Deferred initialization of the work. - */ -static void init_work(struct work_struct *work) -{ - struct ab8500_ad *dd = container_of(work, - struct ab8500_ad, init_work.work); - - dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__); - - dd->jack_type = dd->reported_jack_type = JACK_TYPE_UNSPECIFIED; - config_accdetect(dd); - queue_delayed_work(dd->irq_work_queue, - &dd->detect_work, - msecs_to_jiffies(0)); -} - -/* - * performs platform device initialization - */ -static int ab8500_accessory_init(struct platform_device *pdev) -{ - struct ab8500_ad *dd; - struct ab8500_platform_data *plat; - int ret; - - dev_dbg(&pdev->dev, "Enter: %s\n", __func__); - - dd = kzalloc(sizeof(struct ab8500_ad), GFP_KERNEL); - if (!dd) { - dev_err(&pdev->dev, "%s: Mem. alloc failed\n", __func__); - goto fail_no_mem_for_devdata; - } - - dd->pdev = pdev; - dd->pdata = pdev->dev.platform_data; - plat = dev_get_platdata(pdev->dev.parent); - - if (!plat || !plat->accdet) { - dev_err(&pdev->dev, "%s: Failed to get accdet plat data.\n", - __func__); - goto fail_no_ab8500_dev; - } - dd->pdata = plat->accdet; - - if (dd->pdata->video_ctrl_gpio) { - if (!gpio_is_valid(dd->pdata->video_ctrl_gpio)) { - dev_err(&pdev->dev, - "%s: Video ctrl GPIO invalid (%d).\n", __func__, - dd->pdata->video_ctrl_gpio); - goto fail_video_ctrl_gpio; - } - if (gpio_request(dd->pdata->video_ctrl_gpio, "Video Control")) { - dev_err(&pdev->dev, "%s: Get video ctrl GPIO failed.\n", - __func__); - goto fail_video_ctrl_gpio; - } - } - - if (create_btn_input_dev(dd) < 0) { - dev_err(&pdev->dev, "%s: create_button_input_dev failed.\n", - __func__); - goto fail_no_btn_input_dev; - } - - if (create_regulators(dd) < 0) { - dev_err(&pdev->dev, "%s: failed to create regulators\n", - __func__); - goto fail_no_regulators; - } - dd->btn_state = BUTTON_UNK; - - dd->irq_work_queue = create_singlethread_workqueue("ab8500_accdet_wq"); - if (!dd->irq_work_queue) { - dev_err(&pdev->dev, "%s: Failed to create wq\n", __func__); - goto fail_no_mem_for_wq; - } - dd->gpadc = ab8500_gpadc_get(); - - INIT_DELAYED_WORK(&dd->detect_work, detect_work); - INIT_DELAYED_WORK(&dd->unplug_irq_work, unplug_irq_handler_work); - INIT_DELAYED_WORK(&dd->init_work, init_work); - - /* Deferred init/detect since no use for the info early in boot */ - queue_delayed_work(dd->irq_work_queue, - &dd->init_work, - msecs_to_jiffies(INIT_DELAY_MS)); - - platform_set_drvdata(pdev, dd); - - /* Android switch interface */ - dd->ab_switch.name = "h2w"; - ret = switch_dev_register(&dd->ab_switch); - if (ret < 0) - goto fail_switch; - - return 0; -fail_switch: - destroy_workqueue(dd->irq_work_queue); -fail_no_mem_for_wq: - free_regulators(); -fail_no_regulators: - input_unregister_device(dd->btn_input_dev); -fail_no_btn_input_dev: - gpio_free(dd->pdata->video_ctrl_gpio); -fail_video_ctrl_gpio: -fail_no_ab8500_dev: - kfree(dd); -fail_no_mem_for_devdata: - - return -ENOMEM; -} - -/* - * Performs platform device cleanup - */ -static void ab8500_accessory_cleanup(struct ab8500_ad *dd) -{ - dev_dbg(&dd->pdev->dev, "Enter: %s\n", __func__); - - dd->jack_type = JACK_TYPE_UNSPECIFIED; - config_accdetect(dd); - - gpio_free(dd->pdata->video_ctrl_gpio); - input_unregister_device(dd->btn_input_dev); - free_regulators(); - - cancel_delayed_work(&dd->detect_work); - cancel_delayed_work(&dd->unplug_irq_work); - cancel_delayed_work(&dd->init_work); - flush_workqueue(dd->irq_work_queue); - destroy_workqueue(dd->irq_work_queue); - switch_dev_unregister(&dd->ab_switch); - - kfree(dd); -} - -static int __devinit ab8500_acc_detect_probe(struct platform_device *pdev) -{ - return ab8500_accessory_init(pdev); -} - - -static int __devexit ab8500_acc_detect_remove(struct platform_device *pdev) -{ - ab8500_accessory_cleanup(platform_get_drvdata(pdev)); - platform_set_drvdata(pdev, NULL); - - return 0; -} - -#if defined(CONFIG_PM) static u8 acc_det_ctrl_suspend_val; -static int ab8500_acc_detect_suspend(struct platform_device *pdev, - pm_message_t state) +static void ab8500_turn_off_accdet_comparator(struct platform_device *pdev) { - struct ab8500_ad *dd = platform_get_drvdata(pdev); - int irq_id, irq; - - dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__); - - cancel_delayed_work_sync(&dd->unplug_irq_work); - cancel_delayed_work_sync(&dd->detect_work); - cancel_delayed_work_sync(&dd->init_work); - - if (dd->pdata->is_detection_inverted) - irq_desc = irq_desc_inverted; - else - irq_desc = irq_desc_norm; - - for (irq_id = 0; irq_id < ARRAY_SIZE(irq_desc_norm); irq_id++) { - if (irq_desc[irq_id].registered == 1) { - irq = platform_get_irq_byname( - dd->pdev, - irq_desc[irq_id].name); - - disable_irq(irq); - } - } + struct abx500_ad *dd = platform_get_drvdata(pdev); /* Turn off AccDetect comparators and pull-up */ (void) abx500_get_register_interruptible( @@ -1371,21 +346,11 @@ static int ab8500_acc_detect_suspend(struct platform_device *pdev, AB8500_ACC_DET_CTRL_REG, 0); - if (dd->jack_type == JACK_TYPE_HEADSET) - accessory_regulator_disable(REGULATOR_VAMIC1); - - return 0; } -static int ab8500_acc_detect_resume(struct platform_device *pdev) +static void ab8500_turn_on_accdet_comparator(struct platform_device *pdev) { - struct ab8500_ad *dd = platform_get_drvdata(pdev); - int irq_id, irq; - - dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__); - - if (dd->jack_type == JACK_TYPE_HEADSET) - accessory_regulator_enable(REGULATOR_VAMIC1); + struct abx500_ad *dd = platform_get_drvdata(pdev); /* Turn on AccDetect comparators and pull-up */ (void) abx500_set_register_interruptible( @@ -1394,56 +359,49 @@ static int ab8500_acc_detect_resume(struct platform_device *pdev) AB8500_ACC_DET_CTRL_REG, acc_det_ctrl_suspend_val); - if (dd->pdata->is_detection_inverted) - irq_desc = irq_desc_inverted; - else - irq_desc = irq_desc_norm; - - for (irq_id = 0; irq_id < ARRAY_SIZE(irq_desc_norm); irq_id++) { - if (irq_desc[irq_id].registered == 1) { - irq = platform_get_irq_byname( - dd->pdev, - irq_desc[irq_id].name); - - enable_irq(irq); - - } - } - - /* After resume, reinitialize */ - dd->gpio35_dir_set = dd->accdet1_th_set = dd->accdet2_th_set = 0; - queue_delayed_work(dd->irq_work_queue, &dd->init_work, 0); - - return 0; } -#else -#define ab8500_acc_detect_suspend NULL -#define ab8500_acc_detect_resume NULL -#endif - -static struct platform_driver ab8500_acc_detect_platform_driver = { - .driver = { - .name = "ab8500-acc-det", - .owner = THIS_MODULE, - }, - .probe = ab8500_acc_detect_probe, - .remove = __devexit_p(ab8500_acc_detect_remove), - .suspend = ab8500_acc_detect_suspend, - .resume = ab8500_acc_detect_resume, -}; -static int __init ab8500_acc_detect_init(void) +static void *ab8500_accdet_abx500_gpadc_get(void) { - return platform_driver_register(&ab8500_acc_detect_platform_driver); + return ab8500_gpadc_get(); } -static void __exit ab8500_acc_detect_exit(void) +struct abx500_accdet_platform_data * + ab8500_get_platform_data(struct platform_device *pdev) { - platform_driver_unregister(&ab8500_acc_detect_platform_driver); -} + struct ab8500_platform_data *plat; -module_init(ab8500_acc_detect_init); -module_exit(ab8500_acc_detect_exit); + plat = dev_get_platdata(pdev->dev.parent); + + if (!plat || !plat->accdet) { + dev_err(&pdev->dev, "%s: Failed to get accdet plat data.\n", + __func__); + return ERR_PTR(-ENODEV); + } + + return plat->accdet; +} + +struct abx500_ad ab8500_accessory_det_callbacks = { + .irq_desc_norm = ab8500_irq_desc_norm, + .irq_desc_inverted = ab8500_irq_desc_inverted, + .no_irqs = ARRAY_SIZE(ab8500_irq_desc_norm), + .regu_desc = ab8500_regu_desc, + .no_of_regu_desc = ARRAY_SIZE(ab8500_regu_desc), + .config_accdetect2_hw = ab8500_config_accdetect2_hw, + .config_accdetect1_hw = ab8500_config_accdetect1_hw, + .detect_plugged_in = ab8500_detect_plugged_in, + .meas_voltage_stable = ab8500_meas_voltage_stable, + .config_hw_test_basic_carkit = ab8500_config_hw_test_basic_carkit, + .turn_off_accdet_comparator = ab8500_turn_off_accdet_comparator, + .turn_on_accdet_comparator = ab8500_turn_on_accdet_comparator, + .accdet_abx500_gpadc_get = ab8500_accdet_abx500_gpadc_get, + .config_hw_test_plug_connected = ab8500_config_hw_test_plug_connected, + .startup = NULL, + .set_av_switch = ab8500_set_av_switch, + .tv_out_connected = false, + .get_platform_data = ab8500_get_platform_data, +}; MODULE_DESCRIPTION("AB8500 AV Accessory detection driver"); MODULE_ALIAS("platform:ab8500-acc-det"); diff --git a/drivers/input/misc/abx500-accdet.c b/drivers/input/misc/abx500-accdet.c new file mode 100644 index 00000000000..5e4d30ae1a7 --- /dev/null +++ b/drivers/input/misc/abx500-accdet.c @@ -0,0 +1,977 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Jarmo K. Kuronen + * for ST-Ericsson. + * + * License terms: GPL V2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_SND_SOC_UX500_AB8500 +#include +#else +#define ux500_ab8500_jack_report(i) +#endif + +/* Unique value used to identify Headset button input device */ +#define BTN_INPUT_UNIQUE_VALUE "AB8500HsBtn" +#define BTN_INPUT_DEV_NAME "AB8500 Hs Button" + +#define DEBOUNCE_PLUG_EVENT_MS 100 +#define DEBOUNCE_PLUG_RETEST_MS 25 +#define DEBOUNCE_UNPLUG_EVENT_MS 0 + +/* After being loaded, how fast the first check is to be made */ +#define INIT_DELAY_MS 3000 + +/* Voltage limits (mV) for various types of AV Accessories */ +#define ACCESSORY_DET_VOL_DONTCARE -1 +#define ACCESSORY_HEADPHONE_DET_VOL_MIN 0 +#define ACCESSORY_HEADPHONE_DET_VOL_MAX 40 +#define ACCESSORY_CVIDEO_DET_VOL_MIN 41 +#define ACCESSORY_CVIDEO_DET_VOL_MAX 105 +#define ACCESSORY_CARKIT_DET_VOL_MIN 1100 +#define ACCESSORY_CARKIT_DET_VOL_MAX 1300 +#define ACCESSORY_HEADSET_DET_VOL_MIN 0 +#define ACCESSORY_HEADSET_DET_VOL_MAX 200 +#define ACCESSORY_OPENCABLE_DET_VOL_MIN 1730 +#define ACCESSORY_OPENCABLE_DET_VOL_MAX 2150 + + +/* Macros */ + +/* + * Conviniency macros to check jack characteristics. + */ +#define jack_supports_mic(type) \ + (type == JACK_TYPE_HEADSET || type == JACK_TYPE_CARKIT) +#define jack_supports_spkr(type) \ + ((type != JACK_TYPE_DISCONNECTED) && (type != JACK_TYPE_CONNECTED)) +#define jack_supports_buttons(type) \ + ((type == JACK_TYPE_HEADSET) ||\ + (type == JACK_TYPE_CARKIT) ||\ + (type == JACK_TYPE_OPENCABLE) ||\ + (type == JACK_TYPE_CONNECTED)) + + +/* Forward declarations */ +static void config_accdetect(struct abx500_ad *dd); +static enum accessory_jack_type detect(struct abx500_ad *dd, int *required_det); + +/* Static data initialization */ +static struct accessory_detect_task detect_ops[] = { + { + .type = JACK_TYPE_DISCONNECTED, + .typename = "DISCONNECTED", + .meas_mv = 1, + .req_det_count = 1, + .minvol = ACCESSORY_DET_VOL_DONTCARE, + .maxvol = ACCESSORY_DET_VOL_DONTCARE + }, + { + .type = JACK_TYPE_HEADPHONE, + .typename = "HEADPHONE", + .meas_mv = 1, + .req_det_count = 1, + .minvol = ACCESSORY_HEADPHONE_DET_VOL_MIN, + .maxvol = ACCESSORY_HEADPHONE_DET_VOL_MAX + }, + { + .type = JACK_TYPE_CVIDEO, + .typename = "CVIDEO", + .meas_mv = 0, + .req_det_count = 4, + .minvol = ACCESSORY_CVIDEO_DET_VOL_MIN, + .maxvol = ACCESSORY_CVIDEO_DET_VOL_MAX + }, + { + .type = JACK_TYPE_OPENCABLE, + .typename = "OPENCABLE", + .meas_mv = 0, + .req_det_count = 4, + .minvol = ACCESSORY_OPENCABLE_DET_VOL_MIN, + .maxvol = ACCESSORY_OPENCABLE_DET_VOL_MAX + }, + { + .type = JACK_TYPE_CARKIT, + .typename = "CARKIT", + .meas_mv = 1, + .req_det_count = 1, + .minvol = ACCESSORY_CARKIT_DET_VOL_MIN, + .maxvol = ACCESSORY_CARKIT_DET_VOL_MAX + }, + { + .type = JACK_TYPE_HEADSET, + .typename = "HEADSET", + .meas_mv = 0, + .req_det_count = 2, + .minvol = ACCESSORY_HEADSET_DET_VOL_MIN, + .maxvol = ACCESSORY_HEADSET_DET_VOL_MAX + }, + { + .type = JACK_TYPE_CONNECTED, + .typename = "CONNECTED", + .meas_mv = 0, + .req_det_count = 4, + .minvol = ACCESSORY_DET_VOL_DONTCARE, + .maxvol = ACCESSORY_DET_VOL_DONTCARE + } +}; + +static struct accessory_irq_descriptor *abx500_accdet_irq_desc; + +/* + * textual represenation of the accessory type + */ +static const char *accessory_str(enum accessory_jack_type type) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(detect_ops); i++) + if (type == detect_ops[i].type) + return detect_ops[i].typename; + + return "UNKNOWN?"; +} + +/* + * enables regulator but only if it has not been enabled earlier. + */ +void accessory_regulator_enable(struct abx500_ad *dd, + enum accessory_regulator reg) +{ + int i; + + for (i = 0; i < dd->no_of_regu_desc; i++) { + if (reg & dd->regu_desc[i].id) { + if (!dd->regu_desc[i].enabled) { + if (!regulator_enable(dd->regu_desc[i].handle)) + dd->regu_desc[i].enabled = 1; + } + } + } +} + +/* + * disables regulator but only if it has been previously enabled. + */ +void accessory_regulator_disable(struct abx500_ad *dd, + enum accessory_regulator reg) +{ + int i; + + for (i = 0; i < dd->no_of_regu_desc; i++) { + if (reg & dd->regu_desc[i].id) { + if (dd->regu_desc[i].enabled) { + if (!regulator_disable(dd->regu_desc[i].handle)) + dd->regu_desc[i].enabled = 0; + } + } + } +} + +/* + * frees previously retrieved regulators. + */ +static void free_regulators(struct abx500_ad *dd) +{ + int i; + + for (i = 0; i < dd->no_of_regu_desc; i++) { + if (dd->regu_desc[i].handle) { + regulator_put(dd->regu_desc[i].handle); + dd->regu_desc[i].handle = NULL; + } + } +} + +/* + * gets required regulators. + */ +static int create_regulators(struct abx500_ad *dd) +{ + int i; + int status = 0; + + for (i = 0; i < dd->no_of_regu_desc; i++) { + struct regulator *regu = + regulator_get(&dd->pdev->dev, dd->regu_desc[i].name); + if (IS_ERR(regu)) { + status = PTR_ERR(regu); + dev_err(&dd->pdev->dev, + "%s: Failed to get supply '%s' (%d).\n", + __func__, dd->regu_desc[i].name, status); + free_regulators(dd); + goto out; + } else { + dd->regu_desc[i].handle = regu; + } + } + +out: + return status; +} + +/* + * create input device for button press reporting + */ +static int create_btn_input_dev(struct abx500_ad *dd) +{ + int err; + + dd->btn_input_dev = input_allocate_device(); + if (!dd->btn_input_dev) { + dev_err(&dd->pdev->dev, "%s: Failed to allocate input dev.\n", + __func__); + err = -ENOMEM; + goto out; + } + + input_set_capability(dd->btn_input_dev, + EV_KEY, + dd->pdata->btn_keycode); + + dd->btn_input_dev->name = BTN_INPUT_DEV_NAME; + dd->btn_input_dev->uniq = BTN_INPUT_UNIQUE_VALUE; + dd->btn_input_dev->dev.parent = &dd->pdev->dev; + + err = input_register_device(dd->btn_input_dev); + if (err) { + dev_err(&dd->pdev->dev, + "%s: register_input_device failed (%d).\n", __func__, + err); + input_free_device(dd->btn_input_dev); + dd->btn_input_dev = NULL; + goto out; + } +out: + return err; +} + +/* + * reports jack status + */ +void report_jack_status(struct abx500_ad *dd) +{ + int value = 0; + + if (dd->tv_out_connected == true) + value = SND_JACK_VIDEOOUT; + + /* Never report possible open cable */ + if (dd->jack_type == JACK_TYPE_OPENCABLE) + goto out; + + /* Never report same state twice in a row */ + if (dd->jack_type == dd->reported_jack_type) + goto out; + dd->reported_jack_type = dd->jack_type; + + dev_info(&dd->pdev->dev, "Accessory: %s\n", + accessory_str(dd->jack_type)); + + if (dd->jack_type != JACK_TYPE_DISCONNECTED && + dd->jack_type != JACK_TYPE_UNSPECIFIED) + value |= SND_JACK_MECHANICAL; + if (jack_supports_mic(dd->jack_type)) + value |= SND_JACK_MICROPHONE; + if (jack_supports_spkr(dd->jack_type)) + value |= (SND_JACK_HEADPHONE | SND_JACK_LINEOUT); + if (dd->jack_type == JACK_TYPE_CVIDEO) { + value |= SND_JACK_VIDEOOUT; + if (dd->set_av_switch) + dd->set_av_switch(dd, VIDEO_OUT); + } + + if (dd->jack_type == JACK_TYPE_DISCONNECTED) + switch_set_state(&dd->ab_switch, 0); + else + switch_set_state(&dd->ab_switch, 1); + + ux500_ab8500_jack_report(value); + +out: return; +} + +/* + * worker routine to handle accessory unplug case + */ +void unplug_irq_handler_work(struct work_struct *work) +{ + struct abx500_ad *dd = container_of(work, + struct abx500_ad, unplug_irq_work.work); + + dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__); + + dd->jack_type = dd->jack_type_temp = JACK_TYPE_DISCONNECTED; + dd->jack_det_count = dd->total_jack_det_count = 0; + dd->btn_state = BUTTON_UNK; + config_accdetect(dd); + + accessory_regulator_disable(dd, REGULATOR_ALL); + + report_jack_status(dd); +} + +/* + * interrupt service routine for accessory unplug. + */ +irqreturn_t unplug_irq_handler(int irq, void *_userdata) +{ + struct abx500_ad *dd = _userdata; + + dev_dbg(&dd->pdev->dev, "%s: Enter (irq=%d)\n", __func__, irq); + + queue_delayed_work(dd->irq_work_queue, &dd->unplug_irq_work, + msecs_to_jiffies(DEBOUNCE_UNPLUG_EVENT_MS)); + + return IRQ_HANDLED; +} + +/* + * interrupt service routine for accessory plug. + */ +irqreturn_t plug_irq_handler(int irq, void *_userdata) +{ + struct abx500_ad *dd = _userdata; + + dev_dbg(&dd->pdev->dev, "%s: Enter (irq=%d)\n", + __func__, irq); + + switch (dd->jack_type) { + case JACK_TYPE_DISCONNECTED: + case JACK_TYPE_UNSPECIFIED: + queue_delayed_work(dd->irq_work_queue, &dd->detect_work, + msecs_to_jiffies(DEBOUNCE_PLUG_EVENT_MS)); + break; + + default: + dev_err(&dd->pdev->dev, "%s: Unexpected plug IRQ\n", __func__); + break; + } + + return IRQ_HANDLED; +} + +/* + * worker routine to perform detection. + */ +static void detect_work(struct work_struct *work) +{ + int req_det_count = 1; + enum accessory_jack_type new_type; + struct abx500_ad *dd = container_of(work, + struct abx500_ad, detect_work.work); + + dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__); + + if (dd->set_av_switch) + dd->set_av_switch(dd, AUDIO_IN); + + new_type = detect(dd, &req_det_count); + + dd->total_jack_det_count++; + if (dd->jack_type_temp == new_type) { + dd->jack_det_count++; + } else { + dd->jack_det_count = 1; + dd->jack_type_temp = new_type; + } + + if (dd->total_jack_det_count >= MAX_DET_COUNT) { + dev_err(&dd->pdev->dev, + "%s: MAX_DET_COUNT(=%d) reached. Bailing out.\n", + __func__, MAX_DET_COUNT); + queue_delayed_work(dd->irq_work_queue, &dd->unplug_irq_work, + msecs_to_jiffies(DEBOUNCE_UNPLUG_EVENT_MS)); + } else if (dd->jack_det_count >= req_det_count) { + dd->total_jack_det_count = dd->jack_det_count = 0; + dd->jack_type = new_type; + dd->detect_jiffies = jiffies; + report_jack_status(dd); + config_accdetect(dd); + } else { + queue_delayed_work(dd->irq_work_queue, + &dd->detect_work, + msecs_to_jiffies(DEBOUNCE_PLUG_RETEST_MS)); + } +} + +/* + * reports a button event (pressed, released). + */ +static void report_btn_event(struct abx500_ad *dd, int down) +{ + input_report_key(dd->btn_input_dev, dd->pdata->btn_keycode, down); + input_sync(dd->btn_input_dev); + + dev_dbg(&dd->pdev->dev, "HS-BTN: %s\n", down ? "PRESSED" : "RELEASED"); +} + +/* + * interrupt service routine invoked when hs button is pressed down. + */ +irqreturn_t button_press_irq_handler(int irq, void *_userdata) +{ + struct abx500_ad *dd = _userdata; + + unsigned long accept_jiffies = dd->detect_jiffies + + msecs_to_jiffies(1000); + if (time_before(jiffies, accept_jiffies)) { + dev_dbg(&dd->pdev->dev, "%s: Skipped spurious btn press.\n", + __func__); + return IRQ_HANDLED; + } + + dev_dbg(&dd->pdev->dev, "%s: Enter (irq=%d)\n", __func__, irq); + + if (dd->jack_type == JACK_TYPE_OPENCABLE) { + /* Someting got connected to open cable -> detect.. */ + dd->config_accdetect2_hw(dd, 0); + queue_delayed_work(dd->irq_work_queue, &dd->detect_work, + msecs_to_jiffies(DEBOUNCE_PLUG_EVENT_MS)); + return IRQ_HANDLED; + } + + if (dd->btn_state == BUTTON_PRESSED) + return IRQ_HANDLED; + + if (jack_supports_buttons(dd->jack_type)) { + dd->btn_state = BUTTON_PRESSED; + report_btn_event(dd, 1); + } else { + dd->btn_state = BUTTON_UNK; + } + + return IRQ_HANDLED; +} + +/* + * interrupts service routine invoked when hs button is released. + */ +irqreturn_t button_release_irq_handler(int irq, void *_userdata) +{ + struct abx500_ad *dd = _userdata; + + dev_dbg(&dd->pdev->dev, "%s: Enter (irq=%d)\n", __func__, irq); + + if (dd->jack_type == JACK_TYPE_OPENCABLE) + return IRQ_HANDLED; + + if (dd->btn_state != BUTTON_PRESSED) + return IRQ_HANDLED; + + if (jack_supports_buttons(dd->jack_type)) { + report_btn_event(dd, 0); + dd->btn_state = BUTTON_RELEASED; + } else { + dd->btn_state = BUTTON_UNK; + } + + return IRQ_HANDLED; +} + +/* + * checks whether measured voltage is in given range. depending on arguments, + * voltage might be re-measured or previously measured voltage is reused. + */ +static int mic_vol_in_range(struct abx500_ad *dd, + int lo, int hi, int force_read) +{ + static int mv = MIN_MIC_POWER; + + if (mv == -100 || force_read) + mv = dd->meas_voltage_stable(dd); + + return (mv >= lo && mv <= hi) ? 1 : 0; +} + +/* + * checks whether the currently connected HW is of given type. + */ +static int detect_hw(struct abx500_ad *dd, + struct accessory_detect_task *task) +{ + int status; + + switch (task->type) { + case JACK_TYPE_DISCONNECTED: + dd->config_hw_test_plug_connected(dd, 1); + status = !dd->detect_plugged_in(dd); + break; + case JACK_TYPE_CONNECTED: + dd->config_hw_test_plug_connected(dd, 1); + status = dd->detect_plugged_in(dd); + break; + case JACK_TYPE_CARKIT: + dd->config_hw_test_basic_carkit(dd, 1); + /* flow through.. */ + case JACK_TYPE_HEADPHONE: + case JACK_TYPE_CVIDEO: + case JACK_TYPE_HEADSET: + case JACK_TYPE_OPENCABLE: + status = mic_vol_in_range(dd, + task->minvol, + task->maxvol, + task->meas_mv); + break; + default: + status = 0; + } + + return status; +} + +/* + * Tries to detect the currently attached accessory + */ +static enum accessory_jack_type detect(struct abx500_ad *dd, + int *req_det_count) +{ + enum accessory_jack_type type = JACK_TYPE_DISCONNECTED; + int i; + + accessory_regulator_enable(dd, REGULATOR_VAUDIO | REGULATOR_AVSWITCH); + + for (i = 0; i < ARRAY_SIZE(detect_ops); ++i) { + if (detect_hw(dd, &detect_ops[i])) { + type = detect_ops[i].type; + *req_det_count = detect_ops[i].req_det_count; + break; + } + } + + dd->config_hw_test_basic_carkit(dd, 0); + dd->config_hw_test_plug_connected(dd, 0); + + if (jack_supports_buttons(type)) + accessory_regulator_enable(dd, REGULATOR_VAMIC1); + else + accessory_regulator_disable(dd, REGULATOR_VAMIC1 | + REGULATOR_AVSWITCH); + + accessory_regulator_disable(dd, REGULATOR_VAUDIO); + + return type; +} + +/* + * registers to specific interrupt + */ +static void claim_irq(struct abx500_ad *dd, enum accessory_irq irq_id) +{ + int ret; + int irq; + + if (dd->pdata->is_detection_inverted) + abx500_accdet_irq_desc = dd->irq_desc_inverted; + else + abx500_accdet_irq_desc = dd->irq_desc_norm; + + if (abx500_accdet_irq_desc[irq_id].registered) + return; + + irq = platform_get_irq_byname( + dd->pdev, + abx500_accdet_irq_desc[irq_id].name); + if (irq < 0) { + dev_err(&dd->pdev->dev, + "%s: Failed to get irq %s\n", __func__, + abx500_accdet_irq_desc[irq_id].name); + return; + } + + ret = request_threaded_irq(irq, + NULL, + abx500_accdet_irq_desc[irq_id].isr, + IRQF_NO_SUSPEND | IRQF_SHARED, + abx500_accdet_irq_desc[irq_id].name, + dd); + if (ret != 0) { + dev_err(&dd->pdev->dev, + "%s: Failed to claim irq %s (%d)\n", + __func__, + abx500_accdet_irq_desc[irq_id].name, + ret); + } else { + abx500_accdet_irq_desc[irq_id].registered = 1; + dev_dbg(&dd->pdev->dev, "%s: %s\n", + __func__, abx500_accdet_irq_desc[irq_id].name); + } +} + +/* + * releases specific interrupt + */ +static void release_irq(struct abx500_ad *dd, enum accessory_irq irq_id) +{ + int irq; + + if (dd->pdata->is_detection_inverted) + abx500_accdet_irq_desc = dd->irq_desc_inverted; + else + abx500_accdet_irq_desc = dd->irq_desc_norm; + + if (!abx500_accdet_irq_desc[irq_id].registered) + return; + + irq = platform_get_irq_byname( + dd->pdev, + abx500_accdet_irq_desc[irq_id].name); + if (irq < 0) { + dev_err(&dd->pdev->dev, + "%s: Failed to get irq %s (%d)\n", + __func__, + abx500_accdet_irq_desc[irq_id].name, irq); + } else { + free_irq(irq, dd); + abx500_accdet_irq_desc[irq_id].registered = 0; + dev_dbg(&dd->pdev->dev, "%s: %s\n", + __func__, abx500_accdet_irq_desc[irq_id].name); + } +} + +/* + * configures interrupts + detection hardware to meet the requirements + * set by currently attached accessory type. + */ +static void config_accdetect(struct abx500_ad *dd) +{ + switch (dd->jack_type) { + case JACK_TYPE_UNSPECIFIED: + dd->config_accdetect1_hw(dd, 1); + dd->config_accdetect2_hw(dd, 0); + + release_irq(dd, PLUG_IRQ); + release_irq(dd, UNPLUG_IRQ); + release_irq(dd, BUTTON_PRESS_IRQ); + release_irq(dd, BUTTON_RELEASE_IRQ); + if (dd->set_av_switch) + dd->set_av_switch(dd, NOT_SET); + break; + + case JACK_TYPE_DISCONNECTED: + if (dd->set_av_switch) + dd->set_av_switch(dd, NOT_SET); + case JACK_TYPE_HEADPHONE: + case JACK_TYPE_CVIDEO: + dd->config_accdetect1_hw(dd, 1); + dd->config_accdetect2_hw(dd, 0); + + claim_irq(dd, PLUG_IRQ); + claim_irq(dd, UNPLUG_IRQ); + release_irq(dd, BUTTON_PRESS_IRQ); + release_irq(dd, BUTTON_RELEASE_IRQ); + break; + + case JACK_TYPE_CONNECTED: + case JACK_TYPE_HEADSET: + case JACK_TYPE_CARKIT: + case JACK_TYPE_OPENCABLE: + dd->config_accdetect1_hw(dd, 1); + dd->config_accdetect2_hw(dd, 1); + + release_irq(dd, PLUG_IRQ); + claim_irq(dd, UNPLUG_IRQ); + claim_irq(dd, BUTTON_PRESS_IRQ); + claim_irq(dd, BUTTON_RELEASE_IRQ); + break; + + default: + dev_err(&dd->pdev->dev, "%s: Unknown type: %d\n", + __func__, dd->jack_type); + } +} + +/* + * Deferred initialization of the work. + */ +static void init_work(struct work_struct *work) +{ + struct abx500_ad *dd = container_of(work, + struct abx500_ad, init_work.work); + + dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__); + + dd->jack_type = dd->reported_jack_type = JACK_TYPE_UNSPECIFIED; + config_accdetect(dd); + queue_delayed_work(dd->irq_work_queue, + &dd->detect_work, + msecs_to_jiffies(0)); +} + +/* + * performs platform device initialization + */ +static int abx500_accessory_init(struct platform_device *pdev) +{ + int ret; + struct abx500_ad *dd = (struct abx500_ad *)pdev->id_entry->driver_data; + + dev_dbg(&pdev->dev, "Enter: %s\n", __func__); + + dd->pdev = pdev; + dd->pdata = dd->get_platform_data(pdev); + if (IS_ERR(dd->pdata)) + return PTR_ERR(dd->pdata); + + if (dd->pdata->video_ctrl_gpio) { + ret = gpio_is_valid(dd->pdata->video_ctrl_gpio); + if (!ret) { + dev_err(&pdev->dev, + "%s: Video ctrl GPIO invalid (%d).\n", __func__, + dd->pdata->video_ctrl_gpio); + + return ret; + } + ret = gpio_request(dd->pdata->video_ctrl_gpio, + "Video Control"); + if (ret) { + dev_err(&pdev->dev, "%s: Get video ctrl GPIO" + "failed.\n", __func__); + return ret; + } + } + + (ret = create_btn_input_dev(dd)); + if (ret < 0) { + dev_err(&pdev->dev, "%s: create_button_input_dev failed.\n", + __func__); + goto fail_no_btn_input_dev; + } + + if (dd->startup) { + ret = dd->startup(dd); + if (ret < 0) { + dev_err(&pdev->dev, "Chip specific init failed\n"); + goto fail_no_regulators; + } + } + + ret = create_regulators(dd); + if (ret < 0) { + dev_err(&pdev->dev, "%s: failed to create regulators\n", + __func__); + goto fail_no_regulators; + } + dd->btn_state = BUTTON_UNK; + + dd->irq_work_queue = create_singlethread_workqueue("abx500_accdet_wq"); + if (!dd->irq_work_queue) { + dev_err(&pdev->dev, "%s: Failed to create wq\n", __func__); + ret = -ENOMEM; + goto fail_no_mem_for_wq; + } + + dd->gpadc = dd->accdet_abx500_gpadc_get(); + + INIT_DELAYED_WORK(&dd->detect_work, detect_work); + INIT_DELAYED_WORK(&dd->unplug_irq_work, unplug_irq_handler_work); + INIT_DELAYED_WORK(&dd->init_work, init_work); + + /* Deferred init/detect since no use for the info early in boot */ + queue_delayed_work(dd->irq_work_queue, + &dd->init_work, + msecs_to_jiffies(INIT_DELAY_MS)); + + platform_set_drvdata(pdev, dd); + + /* Android switch interface */ + dd->ab_switch.name = "h2w"; + ret = switch_dev_register(&dd->ab_switch); + if (ret < 0) + goto fail_switch; + + return 0; +fail_switch: + destroy_workqueue(dd->irq_work_queue); +fail_no_mem_for_wq: + free_regulators(dd); +fail_no_regulators: + input_unregister_device(dd->btn_input_dev); +fail_no_btn_input_dev: + if (dd->pdata->video_ctrl_gpio) + gpio_free(dd->pdata->video_ctrl_gpio); + return ret; +} + +/* + * Performs platform device cleanup + */ +static void abx500_accessory_cleanup(struct abx500_ad *dd) +{ + dev_dbg(&dd->pdev->dev, "Enter: %s\n", __func__); + + dd->jack_type = JACK_TYPE_UNSPECIFIED; + config_accdetect(dd); + + gpio_free(dd->pdata->video_ctrl_gpio); + input_unregister_device(dd->btn_input_dev); + free_regulators(dd); + + cancel_delayed_work(&dd->detect_work); + cancel_delayed_work(&dd->unplug_irq_work); + cancel_delayed_work(&dd->init_work); + flush_workqueue(dd->irq_work_queue); + destroy_workqueue(dd->irq_work_queue); + switch_dev_unregister(&dd->ab_switch); + + kfree(dd); +} + +static int __devinit abx500_acc_detect_probe(struct platform_device *pdev) +{ + + return abx500_accessory_init(pdev); +} + +static int __devexit abx500_acc_detect_remove(struct platform_device *pdev) +{ + abx500_accessory_cleanup(platform_get_drvdata(pdev)); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +#if defined(CONFIG_PM) +static int abx500_acc_detect_suspend(struct device *dev) +{ + struct platform_device *pdev = container_of(dev, + struct platform_device, dev); + struct abx500_ad *dd = platform_get_drvdata(pdev); + int irq_id, irq; + + dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__); + + cancel_delayed_work_sync(&dd->unplug_irq_work); + cancel_delayed_work_sync(&dd->detect_work); + cancel_delayed_work_sync(&dd->init_work); + + if (dd->pdata->is_detection_inverted) + abx500_accdet_irq_desc = dd->irq_desc_inverted; + else + abx500_accdet_irq_desc = dd->irq_desc_norm; + + for (irq_id = 0; irq_id < dd->no_irqs; irq_id++) { + if (abx500_accdet_irq_desc[irq_id].registered == 1) { + irq = platform_get_irq_byname( + dd->pdev, + abx500_accdet_irq_desc[irq_id].name); + + disable_irq(irq); + } + } + + dd->turn_off_accdet_comparator(pdev); + + if (dd->jack_type == JACK_TYPE_HEADSET) + accessory_regulator_disable(dd, REGULATOR_VAMIC1); + + return 0; +} + +static int abx500_acc_detect_resume(struct device *dev) +{ + struct platform_device *pdev = container_of(dev, + struct platform_device, dev); + struct abx500_ad *dd = platform_get_drvdata(pdev); + int irq_id, irq; + + dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__); + + if (dd->jack_type == JACK_TYPE_HEADSET) + accessory_regulator_enable(dd, REGULATOR_VAMIC1); + + dd->turn_on_accdet_comparator(pdev); + + if (dd->pdata->is_detection_inverted) + abx500_accdet_irq_desc = dd->irq_desc_inverted; + else + abx500_accdet_irq_desc = dd->irq_desc_norm; + + for (irq_id = 0; irq_id < dd->no_irqs; irq_id++) { + if (abx500_accdet_irq_desc[irq_id].registered == 1) { + irq = platform_get_irq_byname( + dd->pdev, + abx500_accdet_irq_desc[irq_id].name); + + enable_irq(irq); + + } + } + + /* After resume, reinitialize */ + dd->gpio35_dir_set = dd->accdet1_th_set = dd->accdet2_th_set = 0; + queue_delayed_work(dd->irq_work_queue, &dd->init_work, 0); + + return 0; +} +#else +#define abx500_acc_detect_suspend NULL +#define abx500_acc_detect_resume NULL +#endif + +static struct platform_device_id abx500_accdet_ids[] = { +#ifdef CONFIG_INPUT_AB5500_ACCDET + { "ab5500-acc-det", (kernel_ulong_t)&ab5500_accessory_det_callbacks, }, +#endif +#ifdef CONFIG_INPUT_AB8500_ACCDET + { "ab8500-acc-det", (kernel_ulong_t)&ab8500_accessory_det_callbacks, }, +#endif + { }, +}; + +static const struct dev_pm_ops abx_ops = { + .suspend = abx500_acc_detect_suspend, + .resume = abx500_acc_detect_resume, +}; + +static struct platform_driver abx500_acc_detect_platform_driver = { + .driver = { + .name = "abx500-acc-det", + .owner = THIS_MODULE, + .pm = &abx_ops, + }, + .probe = abx500_acc_detect_probe, + .id_table = abx500_accdet_ids, + .remove = __devexit_p(abx500_acc_detect_remove), +}; + +static int __init abx500_acc_detect_init(void) +{ + return platform_driver_register(&abx500_acc_detect_platform_driver); +} + +static void __exit abx500_acc_detect_exit(void) +{ + platform_driver_unregister(&abx500_acc_detect_platform_driver); +} + +module_init(abx500_acc_detect_init); +module_exit(abx500_acc_detect_exit); + +MODULE_DESCRIPTION("ABx500 AV Accessory detection driver"); +MODULE_ALIAS("platform:abx500-acc-det"); +MODULE_AUTHOR("ST-Ericsson"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/ab5500-core.c b/drivers/mfd/ab5500-core.c index 6e1b5396e7a..e687a148f00 100755 --- a/drivers/mfd/ab5500-core.c +++ b/drivers/mfd/ab5500-core.c @@ -805,6 +805,73 @@ static struct mfd_cell ab5500_devs[AB5500_NUM_DEVICES] = { }, }, }, + [AB5500_DEVID_ACCDET] = { + .name = "ab5500-acc-det", + .id = AB5500_DEVID_ACCDET, + .num_resources = 10, + .resources = (struct resource[]) { + { + .name = "acc_detedt22db_rising", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(2, 7), + .end = AB5500_IRQ(2, 7), + }, + { + .name = "acc_detedt21db_falling", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(2, 6), + .end = AB5500_IRQ(2, 6), + }, + { + .name = "acc_detedt21db_rising", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(2, 5), + .end = AB5500_IRQ(2, 5), + }, + { + .name = "acc_detedt3db_falling", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(3, 4), + .end = AB5500_IRQ(3, 4), + }, + { + .name = "acc_detedt3db_rising", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(3, 3), + .end = AB5500_IRQ(3, 3), + }, + { + .name = "acc_detedt1db_falling", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(3, 2), + .end = AB5500_IRQ(3, 2), + }, + { + .name = "acc_detedt1db_rising", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(3, 1), + .end = AB5500_IRQ(3, 1), + }, + { + .name = "acc_detedt22db_falling", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(3, 0), + .end = AB5500_IRQ(3, 0), + }, + { + .name = "plugTVdet", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(13, 7), + .end = AB5500_IRQ(13, 7), + }, + { + .name = "plugTVdet_removal", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(23, 2), + .end = AB5500_IRQ(23, 2), + }, + }, + }, }; static ssize_t show_chip_id(struct device *dev, diff --git a/drivers/regulator/ab5500.c b/drivers/regulator/ab5500.c index 119728433fb..8c2c5ec9974 100644 --- a/drivers/regulator/ab5500.c +++ b/drivers/regulator/ab5500.c @@ -40,6 +40,8 @@ /* In SIM bank */ #define AB5500_SIM_SUP 0x14 +#define AB5500_MBIAS2 0x01 + #define AB5500_LDO_MODE_MASK (0x3 << 4) #define AB5500_LDO_MODE_FULLPOWER (0x3 << 4) #define AB5500_LDO_MODE_PWRCTRL (0x2 << 4) @@ -47,6 +49,10 @@ #define AB5500_LDO_MODE_OFF (0x0 << 4) #define AB5500_LDO_VOLT_MASK 0x07 +#define AB5500_MBIAS2_ENABLE (0x1 << 1) +#define AB5500_MBIAS2_VOLT_MASK (0x1 << 2) +#define AB5500_MBIAS2_MODE_MASK (0x1 << 1) + struct ab5500_regulator { struct regulator_desc desc; const int *voltages; @@ -57,6 +63,10 @@ struct ab5500_regulator { u8 bank; u8 reg; u8 mode; + u8 update_mask; + u8 update_val_idle; + u8 update_val_normal; + u8 voltage_mask; }; struct ab5500_regulators { @@ -80,7 +90,7 @@ static int ab5500_regulator_enable(struct regulator_dev *rdev) int ret; ret = abx500_mask_and_set(ab5500->dev, r->bank, r->reg, - AB5500_LDO_MODE_MASK, r->mode); + r->update_mask, r->mode); if (ret < 0) return ret; @@ -97,7 +107,7 @@ static int ab5500_regulator_disable(struct regulator_dev *rdev) int ret; ret = abx500_mask_and_set(ab5500->dev, r->bank, r->reg, - AB5500_LDO_MODE_MASK, regval); + r->update_mask, regval); if (ret < 0) return ret; @@ -111,7 +121,7 @@ static unsigned int ab5500_regulator_get_mode(struct regulator_dev *rdev) struct ab5500_regulators *ab5500 = rdev_get_drvdata(rdev); struct ab5500_regulator *r = ab5500->regulator[rdev_get_id(rdev)]; - if (r->mode == AB5500_LDO_MODE_LOWPOWER) + if (r->mode == r->update_val_idle) return REGULATOR_MODE_IDLE; return REGULATOR_MODE_NORMAL; @@ -125,10 +135,10 @@ static int ab5500_regulator_set_mode(struct regulator_dev *rdev, switch (mode) { case REGULATOR_MODE_NORMAL: - r->mode = AB5500_LDO_MODE_FULLPOWER; + r->mode = r->update_val_normal; break; case REGULATOR_MODE_IDLE: - r->mode = AB5500_LDO_MODE_LOWPOWER; + r->mode = r->update_val_idle; break; default: return -EINVAL; @@ -155,7 +165,7 @@ static int ab5500_regulator_is_enabled(struct regulator_dev *rdev) return err; } - switch (regval & AB5500_LDO_MODE_MASK) { + switch (regval & r->update_mask) { case AB5500_LDO_MODE_PWRCTRL: case AB5500_LDO_MODE_OFF: r->enabled = false; @@ -216,7 +226,7 @@ static int ab5500_regulator_get_voltage(struct regulator_dev *rdev) return ret; } - regval &= AB5500_LDO_VOLT_MASK; + regval &= r->voltage_mask; if (regval >= r->desc.n_voltages + r->num_holes) return -EINVAL; @@ -279,7 +289,7 @@ static int ab5500_regulator_set_voltage(struct regulator_dev *rdev, *selector = bestindex; return abx500_mask_and_set_register_interruptible(ab5500->dev, - r->bank, r->reg, AB5500_LDO_VOLT_MASK, bestindex); + r->bank, r->reg, r->voltage_mask, bestindex); } @@ -338,98 +348,149 @@ static const int ab5500_ldo_sim_voltages[] = { [0x02] = 2900000, }; +static const int ab5500_bias2_voltages[] = { + [0x00] = 2000000, + [0x01] = 2200000, +}; + static struct ab5500_regulator ab5500_regulators[] = { [AB5500_LDO_L] = { .desc = { - .name = "LDO_L", - .id = AB5500_LDO_L, - .ops = &ab5500_regulator_variable_ops, - .type = REGULATOR_VOLTAGE, - .owner = THIS_MODULE, - .n_voltages = ARRAY_SIZE(ab5500_ldo_lg_voltages) - 2, + .name = "LDO_L", + .id = AB5500_LDO_L, + .ops = &ab5500_regulator_variable_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(ab5500_ldo_lg_voltages) - + 2, }, - .bank = AB5500_BANK_STARTUP, - .reg = AB5500_LDO_L_ST, - .voltages = ab5500_ldo_lg_voltages, - .num_holes = 2, /* 2 register values unused */ - .enable_time = 400, - .mode = AB5500_LDO_MODE_FULLPOWER, + .bank = AB5500_BANK_STARTUP, + .reg = AB5500_LDO_L_ST, + .voltages = ab5500_ldo_lg_voltages, + .num_holes = 2, /* 2 register values unused */ + .enable_time = 400, + .mode = AB5500_LDO_MODE_FULLPOWER, + .update_mask = AB5500_LDO_MODE_MASK, + .update_val_normal = AB5500_LDO_MODE_FULLPOWER, + .update_val_idle = AB5500_LDO_MODE_LOWPOWER, + .voltage_mask = AB5500_LDO_VOLT_MASK, }, [AB5500_LDO_G] = { .desc = { - .name = "LDO_G", - .id = AB5500_LDO_G, - .ops = &ab5500_regulator_variable_ops, - .type = REGULATOR_VOLTAGE, - .owner = THIS_MODULE, - .n_voltages = ARRAY_SIZE(ab5500_ldo_lg_voltages) - 2, + .name = "LDO_G", + .id = AB5500_LDO_G, + .ops = &ab5500_regulator_variable_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(ab5500_ldo_lg_voltages) - + 2, }, - .bank = AB5500_BANK_STARTUP, - .reg = AB5500_LDO_G_ST, - .voltages = ab5500_ldo_lg_voltages, - .num_holes = 2, /* 2 register values unused */ - .enable_time = 400, - .mode = AB5500_LDO_MODE_FULLPOWER, + .bank = AB5500_BANK_STARTUP, + .reg = AB5500_LDO_G_ST, + .voltages = ab5500_ldo_lg_voltages, + .num_holes = 2, /* 2 register values unused */ + .enable_time = 400, + .mode = AB5500_LDO_MODE_FULLPOWER, + .update_mask = AB5500_LDO_MODE_MASK, + .update_val_normal = AB5500_LDO_MODE_FULLPOWER, + .update_val_idle = AB5500_LDO_MODE_LOWPOWER, + .voltage_mask = AB5500_LDO_VOLT_MASK, }, [AB5500_LDO_K] = { .desc = { - .name = "LDO_K", - .id = AB5500_LDO_K, - .ops = &ab5500_regulator_variable_ops, - .type = REGULATOR_VOLTAGE, - .owner = THIS_MODULE, - .n_voltages = ARRAY_SIZE(ab5500_ldo_kh_voltages), + .name = "LDO_K", + .id = AB5500_LDO_K, + .ops = &ab5500_regulator_variable_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(ab5500_ldo_kh_voltages), }, - .bank = AB5500_BANK_STARTUP, - .reg = AB5500_LDO_K_ST, - .voltages = ab5500_ldo_kh_voltages, - .enable_time = 400, - .mode = AB5500_LDO_MODE_FULLPOWER, + .bank = AB5500_BANK_STARTUP, + .reg = AB5500_LDO_K_ST, + .voltages = ab5500_ldo_kh_voltages, + .enable_time = 400, + .mode = AB5500_LDO_MODE_FULLPOWER, + .update_mask = AB5500_LDO_MODE_MASK, + .update_val_normal = AB5500_LDO_MODE_FULLPOWER, + .update_val_idle = AB5500_LDO_MODE_LOWPOWER, + .voltage_mask = AB5500_LDO_VOLT_MASK, }, [AB5500_LDO_H] = { .desc = { - .name = "LDO_H", - .id = AB5500_LDO_H, - .ops = &ab5500_regulator_variable_ops, - .type = REGULATOR_VOLTAGE, - .owner = THIS_MODULE, - .n_voltages = ARRAY_SIZE(ab5500_ldo_kh_voltages), + .name = "LDO_H", + .id = AB5500_LDO_H, + .ops = &ab5500_regulator_variable_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(ab5500_ldo_kh_voltages), }, - .bank = AB5500_BANK_STARTUP, - .reg = AB5500_LDO_H_ST, - .voltages = ab5500_ldo_kh_voltages, - .enable_time = 400, - .mode = AB5500_LDO_MODE_FULLPOWER, + .bank = AB5500_BANK_STARTUP, + .reg = AB5500_LDO_H_ST, + .voltages = ab5500_ldo_kh_voltages, + .enable_time = 400, + .mode = AB5500_LDO_MODE_FULLPOWER, + .update_mask = AB5500_LDO_MODE_MASK, + .update_val_normal = AB5500_LDO_MODE_FULLPOWER, + .update_val_idle = AB5500_LDO_MODE_LOWPOWER, + .voltage_mask = AB5500_LDO_VOLT_MASK, }, [AB5500_LDO_VDIGMIC] = { .desc = { - .name = "LDO_VDIGMIC", - .id = AB5500_LDO_VDIGMIC, - .ops = &ab5500_regulator_fixed_ops, - .type = REGULATOR_VOLTAGE, - .owner = THIS_MODULE, - .n_voltages = ARRAY_SIZE(ab5500_ldo_vdigmic_voltages), + .name = "LDO_VDIGMIC", + .id = AB5500_LDO_VDIGMIC, + .ops = &ab5500_regulator_fixed_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .n_voltages = + ARRAY_SIZE(ab5500_ldo_vdigmic_voltages), }, - .bank = AB5500_BANK_STARTUP, - .reg = AB5500_LDO_VDIGMIC_ST, - .voltages = ab5500_ldo_vdigmic_voltages, - .enable_time = 450, - .mode = AB5500_LDO_MODE_FULLPOWER, + .bank = AB5500_BANK_STARTUP, + .reg = AB5500_LDO_VDIGMIC_ST, + .voltages = ab5500_ldo_vdigmic_voltages, + .enable_time = 450, + .mode = AB5500_LDO_MODE_FULLPOWER, + .update_mask = AB5500_LDO_MODE_MASK, + .update_val_normal = AB5500_LDO_MODE_FULLPOWER, + .update_val_idle = AB5500_LDO_MODE_LOWPOWER, + .voltage_mask = AB5500_LDO_VOLT_MASK, }, [AB5500_LDO_SIM] = { .desc = { - .name = "LDO_SIM", - .id = AB5500_LDO_SIM, - .ops = &ab5500_regulator_variable_ops, - .type = REGULATOR_VOLTAGE, - .owner = THIS_MODULE, - .n_voltages = ARRAY_SIZE(ab5500_ldo_sim_voltages), + .name = "LDO_SIM", + .id = AB5500_LDO_SIM, + .ops = &ab5500_regulator_variable_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(ab5500_ldo_sim_voltages), + }, + .bank = AB5500_BANK_SIM_USBSIM, + .reg = AB5500_SIM_SUP, + .voltages = ab5500_ldo_sim_voltages, + .enable_time = 1000, + .mode = AB5500_LDO_MODE_FULLPOWER, + .update_mask = AB5500_LDO_MODE_MASK, + .update_val_normal = AB5500_LDO_MODE_FULLPOWER, + .update_val_idle = AB5500_LDO_MODE_LOWPOWER, + .voltage_mask = AB5500_LDO_VOLT_MASK, + }, + [AB5500_BIAS2] = { + .desc = { + .name = "BIAS2", + .id = AB5500_BIAS2, + .ops = &ab5500_regulator_variable_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(ab5500_bias2_voltages), }, - .bank = AB5500_BANK_SIM_USBSIM, - .reg = AB5500_SIM_SUP, - .voltages = ab5500_ldo_sim_voltages, - .enable_time = 1000, - .mode = AB5500_LDO_MODE_FULLPOWER, + .bank = AB5500_BANK_AUDIO_HEADSETUSB, + .reg = AB5500_MBIAS2, + .voltages = ab5500_bias2_voltages, + .enable_time = 1000, + .mode = AB5500_MBIAS2_ENABLE, + .update_mask = AB5500_MBIAS2_MODE_MASK, + .update_val_normal = AB5500_MBIAS2_ENABLE, + .update_val_idle = AB5500_MBIAS2_ENABLE, + .voltage_mask = AB5500_MBIAS2_VOLT_MASK, }, }; diff --git a/include/linux/mfd/ab8500.h b/include/linux/mfd/ab8500.h index f1979bbd464..8908dd989f9 100644 --- a/include/linux/mfd/ab8500.h +++ b/include/linux/mfd/ab8500.h @@ -206,7 +206,7 @@ struct ab8500_platform_data { struct ab8500_regulator_reg_init *regulator_reg_init; int num_regulator; struct regulator_init_data *regulator; - struct ab8500_accdet_platform_data *accdet; + struct abx500_accdet_platform_data *accdet; struct ab8500_bm_data *battery; struct ab8500_denc_platform_data *denc; struct ab8500_audio_platform_data *audio; diff --git a/include/linux/mfd/abx500/ab5500.h b/include/linux/mfd/abx500/ab5500.h index 3a35f2d4b52..7c200c483b0 100644 --- a/include/linux/mfd/abx500/ab5500.h +++ b/include/linux/mfd/abx500/ab5500.h @@ -29,6 +29,7 @@ enum ab5500_devid { AB5500_DEVID_CHARGALG, AB5500_DEVID_BTEMP, AB5500_DEVID_TEMPMON, + AB5500_DEVID_ACCDET, AB5500_NUM_DEVICES, }; @@ -157,6 +158,7 @@ struct ab5500_platform_data { bool pm_power_off; struct ab5500_regulator_platform_data *regulator; struct ab5500_usbgpio_platform_data *usb; + struct abx500_accdet_platform_data *accdet; }; struct ab5500_ponkey_platform_data { diff --git a/include/linux/regulator/ab5500.h b/include/linux/regulator/ab5500.h index 26c57078f95..2d85b57cae8 100644 --- a/include/linux/regulator/ab5500.h +++ b/include/linux/regulator/ab5500.h @@ -14,6 +14,7 @@ enum ab5500_regulator_id { AB5500_LDO_L, AB5500_LDO_VDIGMIC, AB5500_LDO_SIM, + AB5500_BIAS2, AB5500_NUM_REGULATORS, }; -- cgit v1.2.3