summaryrefslogtreecommitdiff
path: root/drivers/input/misc/ab8500-accdet.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/misc/ab8500-accdet.c')
-rw-r--r--drivers/input/misc/ab8500-accdet.c1164
1 files changed, 61 insertions, 1103 deletions
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");