summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVirupax Sadashivpetimath <virupax.sadashivpetimath@stericsson.com>2011-09-22 16:39:54 +0530
committerVirupax Sadashivpetimath <virupax.sadashivpetimath@stericsson.com>2011-09-22 18:19:56 +0530
commitccb4b560d11f6632178dfa17024f026eccbb4fde (patch)
treec0e349bb9350ba19314c3481cef535947db5d9a0
parentabca64e5d564e8902c831e24348f6215f884d352 (diff)
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 <virupax.sadashivpetimath@stericsson.com>
-rw-r--r--arch/arm/mach-ux500/board-mop500.c4
-rw-r--r--arch/arm/mach-ux500/board-u5500-regulators.c11
-rw-r--r--arch/arm/mach-ux500/board-u5500.c14
-rw-r--r--arch/arm/mach-ux500/include/mach/ab8500-accdet.h96
-rw-r--r--arch/arm/mach-ux500/include/mach/abx500-accdet.h361
-rw-r--r--drivers/input/misc/Kconfig7
-rw-r--r--drivers/input/misc/Makefile3
-rw-r--r--drivers/input/misc/ab5500-accdet.c363
-rw-r--r--drivers/input/misc/ab8500-accdet.c1164
-rw-r--r--drivers/input/misc/abx500-accdet.c977
-rwxr-xr-xdrivers/mfd/ab5500-core.c67
-rw-r--r--drivers/regulator/ab5500.c213
-rw-r--r--include/linux/mfd/ab8500.h2
-rw-r--r--include/linux/mfd/abx500/ab5500.h2
-rw-r--r--include/linux/regulator/ab5500.h1
15 files changed, 2006 insertions, 1279 deletions
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 <mach/setup.h>
#include <mach/devices.h>
#include <mach/sensors1p.h>
-#include <mach/ab8500-accdet.h>
+#include <mach/abx500-accdet.h>
#include <mach/irqs.h>
#include <mach/ste_audio.h>
#include <mach/ste-dma40-db8500.h>
@@ -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 <mach/db5500-keypad.h>
#include <mach/crypto-ux500.h>
#include <mach/usb.h>
+#include <mach/abx500-accdet.h>
#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 <jarmo.kuronen@symbio.com> 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 <jarmo.kuronen@symbio.com> for ST Ericsson.
+ * Licensed under GPLv2.
+ */
+
+#ifndef _ABx500_ACCDET_H
+#define _ABx500_ACCDET_H
+
+#include <linux/switch.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
+
+/*
+* 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 <jarmo.kuronen@symbio.com>
+ * 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 <linux/delay.h>
+#include <linux/mfd/abx500/ab5500.h>
+#include <linux/mfd/abx500/ab5500-gpadc.h>
+#include <linux/mfd/abx500.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <mach/abx500-accdet.h>
+
+/*
+ * 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 <linux/module.h> /* Needed by all modules */
-#include <linux/kernel.h> /* Needed for KERN_INFO */
-#include <linux/init.h> /* Needed for the macros */
-#include <linux/workqueue.h>
#include <linux/delay.h>
-#include <linux/jiffies.h>
-#include <linux/slab.h>
-#include <linux/input.h>
-#include <sound/soc.h>
-#include <sound/jack.h>
-#include <linux/platform_device.h>
-#include <linux/regulator/consumer.h>
+#include <linux/interrupt.h>
#include <linux/mfd/ab8500.h>
#include <linux/mfd/abx500.h>
+#include <linux/platform_device.h>
#include <linux/mfd/ab8500/gpadc.h>
#include <linux/mfd/ab8500/gpio.h>
#include <linux/gpio.h>
-#include <linux/switch.h>
-#include <mach/ab8500-accdet.h>
-#ifdef CONFIG_SND_SOC_UX500_AB8500
-#include <sound/ux500_ab8500.h>
-#else
-#define ux500_ab8500_jack_report(i)
-#endif
+#include <linux/err.h>
+#include <mach/abx500-accdet.h>
#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;
@@ -594,86 +205,9 @@ static void config_accdetect1_hw(struct ab8500_ad *dd, int enable)
}
/*
- * 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);
@@ -715,187 +251,11 @@ static int meas_voltage_stable(struct ab8500_ad *dd, u8 input)
}
/*
- * 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(
@@ -938,60 +298,9 @@ static void config_hw_test_basic_carkit(struct ab8500_ad *dd, int enable)
}
/*
- * 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 <jarmo.kuronen@symbio.com>
+ * 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 <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/workqueue.h>
+#include <linux/irq.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio.h>
+#include <linux/mfd/abx500.h>
+#include <linux/interrupt.h>
+#include <sound/jack.h>
+#include <mach/abx500-accdet.h>
+#ifdef CONFIG_SND_SOC_UX500_AB8500
+#include <sound/ux500_ab8500.h>
+#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,
};