summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilippe Langlais <philippe.langlais@linaro.org>2011-05-04 14:47:35 +0200
committerUlf Hansson <ulf.hansson@stericsson.com>2011-09-19 15:14:54 +0200
commitfe756415aec67aa0980f1367ae8bd4989b7bce49 (patch)
treeb20d3be2d7f30cedd1d2447dfaed65c190e053f1
parentf4d79c68d460600e5ad1ab5a4833e2768e2c2017 (diff)
Input: Add AB8500 AV Acc. Detection as platform drv
Adds AV Accessory detection driver functionality as a platform driver. Driver allows detection of basic AV-Accessory connected to 3.5mm AV-Connector. Supported accessories include headphone, headset, video and carkit. Driver controls the audio-in/video-out switch based on the detected accessory type and notifies about button presses from basic headsets (No ECI Support). Accessory status and button presses are reported as standard input events, if AB8500 ASoC Machine driver is configured. For accessory properties reporting, ALSA JACK definitions (SND_JACK_*) are used to report the features supported by the attached accessory. Button presses are reported with KEY_MEDIA keycode. Depends on: http://gerrit.lud.stericsson.com/gerrit/16910 for GPIO framework update. ST-Ericsson ID: ER 275366 Signed-off-by: Rahul Venkatram <rahul.venkatram@stericsson.com> Change-Id: Ie1c68120fc718710ecac2d3dafe4f3e7b1a53ac1 Signed-off-by: Rahul Venkatram <rahul.venkatram@stericsson.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/16256 Reviewed-by: Mattias WALLIN <mattias.wallin@stericsson.com> Conflicts: arch/arm/mach-ux500/board-mop500-regulators.c arch/arm/mach-ux500/board-mop500.c sound/soc/ux500/ux500_ab8500.c
-rw-r--r--arch/arm/mach-ux500/board-mop500-regulators.c3
-rw-r--r--arch/arm/mach-ux500/board-mop500.c13
-rw-r--r--arch/arm/mach-ux500/include/mach/ab8500-accdet.h93
-rw-r--r--drivers/input/misc/Kconfig7
-rw-r--r--drivers/input/misc/Makefile1
-rw-r--r--drivers/input/misc/ab8500-accdet.c1328
-rw-r--r--drivers/mfd/ab8500-core.c85
-rw-r--r--drivers/misc/audio_io_dev/ste_audio_io_core.c4
-rw-r--r--include/linux/mfd/ab8500.h3
-rw-r--r--include/sound/ux500_ab8500.h (renamed from sound/soc/ux500/ux500_ab8500.h)2
-rw-r--r--sound/soc/ux500/Makefile2
-rw-r--r--sound/soc/ux500/u8500.c2
-rw-r--r--sound/soc/ux500/ux500_ab8500.c34
-rw-r--r--sound/soc/ux500/ux500_ab8500_accessory.c1336
-rw-r--r--sound/soc/ux500/ux500_ab8500_accessory.h22
15 files changed, 1522 insertions, 1413 deletions
diff --git a/arch/arm/mach-ux500/board-mop500-regulators.c b/arch/arm/mach-ux500/board-mop500-regulators.c
index 689ea39010d..28eaae0d9e6 100644
--- a/arch/arm/mach-ux500/board-mop500-regulators.c
+++ b/arch/arm/mach-ux500/board-mop500-regulators.c
@@ -67,7 +67,8 @@ static struct regulator_consumer_supply ab8500_vaux2_consumers[] = {
/* On-board eMMC power */
REGULATOR_SUPPLY("vmmc", "sdi4"),
/* AB8500 audio codec */
- REGULATOR_SUPPLY("vcc-N2158", "ab8500-codec.0"),
+ REGULATOR_SUPPLY("vcc-avswitch", "ab8500-codec.0"),
+ REGULATOR_SUPPLY("vcc-avswitch", "ab8500-acc-det.0"),
REGULATOR_SUPPLY_DEBUG("aux2", "reg-virt-consumer.1")
};
diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c
index 1e2a44dd9a8..524bfa7cf5a 100644
--- a/arch/arm/mach-ux500/board-mop500.c
+++ b/arch/arm/mach-ux500/board-mop500.c
@@ -46,6 +46,7 @@
#include <mach/hardware.h>
#include <mach/setup.h>
#include <mach/devices.h>
+#include <mach/ab8500-accdet.h>
#include <mach/irqs.h>
#include <mach/ste_audio.h>
#include <mach/ste-dma40-db8500.h>
@@ -111,6 +112,15 @@ static struct ab8500_gpio_platform_data ab8500_gpio_pdata = {
0x7A, 0x00, 0x00},
};
+#ifdef CONFIG_INPUT_AB8500_ACCDET
+static struct ab8500_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,
+ .video_ctrl_gpio = MOP500_AB8500_GPIO(35),
+};
+#endif
+
static struct gpio_keys_button snowball_key_array[] = {
{
.gpio = 32,
@@ -218,6 +228,9 @@ static struct ab8500_platform_data ab8500_platdata = {
.fg = &ab8500_fg_plat_data,
.chargalg = &ab8500_chargalg_plat_data,
.gpio = &ab8500_gpio_pdata,
+#ifdef CONFIG_INPUT_AB8500_ACCDET
+ .accdet = &ab8500_accdet_pdata,
+#endif
};
static struct resource ab8500_resources[] = {
diff --git a/arch/arm/mach-ux500/include/mach/ab8500-accdet.h b/arch/arm/mach-ux500/include/mach/ab8500-accdet.h
new file mode 100644
index 00000000000..5742d7b797a
--- /dev/null
+++ b/arch/arm/mach-ux500/include/mach/ab8500-accdet.h
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+struct ab8500_accdet_platform_data {
+ int btn_keycode;
+ u8 accdet1_dbth;
+ u8 accdet2122_th;
+ unsigned int video_ctrl_gpio;
+};
+
+#endif /* _AB8500_ACCDET_H */
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 45dc6aa62ba..0901e01ab07 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -22,6 +22,13 @@ config INPUT_88PM860X_ONKEY
To compile this driver as a module, choose M here: the module
will be called 88pm860x_onkey.
+config INPUT_AB8500_ACCDET
+ bool "AB8500 AV Accessory detection"
+ depends on AB8500_CORE && AB8500_GPADC && AB8500_GPIO
+ help
+ Say Y here to enable AV accessory detection features for ST-Ericsson's
+ AB8500 Mix-Sig PMIC.
+
config INPUT_AB8500_PONKEY
tristate "AB8500 Pon (PowerOn) Key"
depends on AB8500_CORE
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 38efb2cb182..c77334ac711 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -5,6 +5,7 @@
# 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_PONKEY) += ab8500-ponkey.o
obj-$(CONFIG_INPUT_AD714X) += ad714x.o
obj-$(CONFIG_INPUT_AD714X_I2C) += ad714x-i2c.o
diff --git a/drivers/input/misc/ab8500-accdet.c b/drivers/input/misc/ab8500-accdet.c
new file mode 100644
index 00000000000..8057d0e881a
--- /dev/null
+++ b/drivers/input/misc/ab8500-accdet.c
@@ -0,0 +1,1328 @@
+/*
+ * 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> /* 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/mfd/ab8500.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/ab8500/gpadc.h>
+#include <linux/mfd/ab8500/gpio.h>
+#include <linux/gpio.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
+
+#define MAX_DET_COUNT 10
+#define MAX_VOLT_DIFF 30
+#define MIN_MIC_POWER -100
+
+/* 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
+
+/*
+ * Register definition for accessory detection.
+ */
+#define AB8500_REGU_CTRL1_SPARE_REG 0x84
+#define AB8500_ACC_DET_DB1_REG 0x80
+#define AB8500_ACC_DET_DB2_REG 0x81
+#define AB8500_ACC_DET_CTRL_REG 0x82
+#define AB8500_IT_SOURCE5_REG 0x04
+
+/* 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 0x04
+
+/* 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))
+
+
+/* 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
+ */
+enum accessory_avcontrol_dir {
+ AUDIO_IN,
+ VIDEO_OUT,
+};
+
+/**
+ * @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.
+ */
+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;
+};
+
+/* 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] = {
+ {
+ .id = REGULATOR_VAUDIO,
+ .name = "v-audio",
+ },
+ {
+ .id = REGULATOR_VAMIC1,
+ .name = "v-amic1",
+ },
+ {
+ .id = REGULATOR_AVSWITCH,
+ .name = "vcc-avswitch",
+ },
+};
+
+static struct accessory_irq_descriptor irq_desc[] = {
+ {
+ .irq = PLUG_IRQ,
+ .name = "ACC_DETECT_1DB_F",
+ .isr = plug_irq_handler,
+ },
+ {
+ .irq = UNPLUG_IRQ,
+ .name = "ACC_DETECT_1DB_R",
+ .isr = unplug_irq_handler,
+ },
+ {
+ .irq = BUTTON_PRESS_IRQ,
+ .name = "ACC_DETECT_22DB_F",
+ .isr = button_press_irq_handler,
+ },
+ {
+ .irq = BUTTON_RELEASE_IRQ,
+ .name = "ACC_DETECT_22DB_R",
+ .isr = button_release_irq_handler,
+ },
+};
+
+/*
+ * 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)
+{
+ int ret = 0;
+
+ if (!dd->accdet2_th_set) {
+ /* Configure accdetect21+22 thresholds */
+ ret = abx500_set_register_interruptible(&dd->pdev->dev,
+ AB8500_ECI_AV_ACC,
+ AB8500_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,
+ AB8500_ECI_AV_ACC,
+ AB8500_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
+ */
+void config_accdetect1_hw(struct ab8500_ad *dd, int enable)
+{
+ int ret;
+
+ if (!dd->accdet1_th_set) {
+ ret = abx500_set_register_interruptible(&dd->pdev->dev,
+ AB8500_ECI_AV_ACC,
+ AB8500_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,
+ AB8500_ECI_AV_ACC,
+ AB8500_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);
+}
+
+/*
+ * 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
+ */
+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);
+ }
+
+ 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)
+{
+ u8 value = 0;
+ int status = abx500_get_register_interruptible(
+ &dd->pdev->dev,
+ AB8500_INTERRUPT,
+ AB8500_IT_SOURCE5_REG,
+ &value);
+ if (status < 0) {
+ dev_err(&dd->pdev->dev, "%s: reg read failed (%d).\n",
+ __func__, status);
+ return 0;
+ }
+
+ return value & BIT_ITSOURCE5_ACCDET1 ? 0 : 1;
+}
+
+/*
+ * mic_line_voltage_stable - measures a relative stable voltage from spec. input
+ */
+static int meas_voltage_stable(struct ab8500_ad *dd, u8 input)
+{
+ int iterations = 2;
+ int v1, v2, dv;
+
+ v1 = ab8500_gpadc_convert(dd->gpadc, input);
+ do {
+ msleep(1);
+ --iterations;
+ v2 = ab8500_gpadc_convert(dd->gpadc, input);
+ dv = abs(v2 - v1);
+ v1 = v2;
+ } while (iterations > 0 && dv > MAX_VOLT_DIFF);
+
+ return v1;
+}
+
+/*
+ * worker routine to handle accessory unplug case
+ */
+static void unplug_irq_handler_work(struct work_struct *work)
+{
+ struct ab8500_ad *dd = container_of(work,
+ struct ab8500_ad, unplug_irq_work.work);
+
+ dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__);
+
+ dd->jack_type = dd->jack_type_temp = JACK_TYPE_DISCONNECTED;
+ dd->jack_det_count = dd->total_jack_det_count = 0;
+ dd->btn_state = BUTTON_UNK;
+ config_accdetect(dd);
+
+ set_av_switch(dd, AUDIO_IN);
+
+ 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__);
+
+ 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)
+{
+ int ret;
+
+ dev_dbg(&dd->pdev->dev, "%s:%d\n", __func__, enable);
+
+ ret = ab8500_config_pull_up_or_down(dd->pdev,
+ dd->pdata->video_ctrl_gpio, !enable);
+ if (ret < 0) {
+ dev_err(&dd->pdev->dev,
+ "%s: Failed to update reg (%d).\n", __func__, ret);
+ return;
+ }
+
+ if (enable)
+ accessory_regulator_enable(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)
+{
+ int ret;
+
+ dev_dbg(&dd->pdev->dev, "%s:%d\n", __func__, enable);
+
+ if (enable)
+ accessory_regulator_disable(REGULATOR_VAMIC1);
+
+ /* Un-Ground the VAMic1 output when enabled */
+ ret = abx500_mask_and_set_register_interruptible(
+ &dd->pdev->dev,
+ AB8500_REGU_CTRL1,
+ AB8500_REGU_CTRL1_SPARE_REG,
+ BIT_REGUCTRL1SPARE_VAMIC1_GROUND,
+ enable ? BIT_REGUCTRL1SPARE_VAMIC1_GROUND : 0);
+ if (ret < 0)
+ dev_err(&dd->pdev->dev,
+ "%s: Failed to update reg (%d).\n", __func__, ret);
+}
+
+/*
+ * checks whether measured voltage is in given range. depending on arguments,
+ * voltage might be re-measured or previously measured voltage is reused.
+ */
+static int mic_vol_in_range(struct ab8500_ad *dd,
+ int lo, int hi, int force_read)
+{
+ static int mv = MIN_MIC_POWER;
+
+ if (mv == -100 || force_read)
+ mv = meas_voltage_stable(dd, ACC_DETECT2);
+
+ return (mv >= lo && mv <= hi) ? 1 : 0;
+}
+
+/*
+ * checks whether the currently connected HW is of given type.
+ */
+static int detect_hw(struct ab8500_ad *dd,
+ struct accessory_detect_task *task)
+{
+ int status;
+
+ switch (task->type) {
+ case JACK_TYPE_DISCONNECTED:
+ config_hw_test_plug_connected(dd, 1);
+ status = !detect_plugged_in(dd);
+ break;
+ case JACK_TYPE_CONNECTED:
+ config_hw_test_plug_connected(dd, 1);
+ status = detect_plugged_in(dd);
+ break;
+ case JACK_TYPE_CARKIT:
+ config_hw_test_basic_carkit(dd, 1);
+ /* flow through.. */
+ case JACK_TYPE_HEADPHONE:
+ case JACK_TYPE_CVIDEO:
+ case JACK_TYPE_HEADSET:
+ case JACK_TYPE_OPENCABLE:
+ status = mic_vol_in_range(dd,
+ task->minvol,
+ task->maxvol,
+ task->meas_mv);
+ break;
+ default:
+ status = 0;
+ }
+
+ return status;
+}
+
+/*
+ * sets the av switch direction - audio-in vs video-out
+ */
+static void set_av_switch(struct ab8500_ad *dd,
+ enum accessory_avcontrol_dir dir)
+{
+ int ret;
+
+ dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__);
+ if (!dd->gpio35_dir_set) {
+ ret = gpio_direction_output(dd->pdata->video_ctrl_gpio,
+ dir == AUDIO_IN ? 1 : 0);
+ if (ret < 0) {
+ dev_err(&dd->pdev->dev,
+ "%s: Output video ctrl signal failed (%d).\n",
+ __func__, ret);
+ } else {
+ dd->gpio35_dir_set = 1;
+ dev_dbg(&dd->pdev->dev, "AV-SWITCH: %s\n",
+ dir == AUDIO_IN ? "AUDIO_IN" : "VIDEO_OUT");
+ }
+ } else {
+ gpio_set_value(dd->pdata->video_ctrl_gpio,
+ dir == AUDIO_IN ? 1 : 0);
+ }
+}
+
+/*
+ * 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 (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 (!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);
+ break;
+
+ case JACK_TYPE_DISCONNECTED:
+ 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__);
+
+ set_av_switch(dd, AUDIO_IN);
+ 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
+ */
+int ab8500_accessory_init(struct platform_device *pdev)
+{
+ struct ab8500_ad *dd;
+ struct ab8500_platform_data *plat;
+
+ 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);
+
+ return 0;
+
+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
+ */
+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);
+
+ 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 int ab8500_acc_detect_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct ab8500_ad *dd = platform_get_drvdata(pdev);
+
+ 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);
+
+ return 0;
+}
+
+static int ab8500_acc_detect_resume(struct platform_device *pdev)
+{
+ struct ab8500_ad *dd = platform_get_drvdata(pdev);
+
+ dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__);
+
+ /* 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)
+{
+ return platform_driver_register(&ab8500_acc_detect_platform_driver);
+}
+
+static void __exit ab8500_acc_detect_exit(void)
+{
+ platform_driver_unregister(&ab8500_acc_detect_platform_driver);
+}
+
+module_init(ab8500_acc_detect_init);
+module_exit(ab8500_acc_detect_exit);
+
+MODULE_DESCRIPTION("AB8500 AV Accessory detection driver");
+MODULE_ALIAS("platform:ab8500-acc-det");
+MODULE_AUTHOR("ST-Ericsson");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
index b14441c4694..3c67d0fadea 100644
--- a/drivers/mfd/ab8500-core.c
+++ b/drivers/mfd/ab8500-core.c
@@ -415,6 +415,45 @@ static struct resource ab8500_poweronkey_db_resources[] = {
},
};
+static struct resource ab8500_av_acc_detect_resources[] = {
+ {
+ .name = "ACC_DETECT_1DB_F",
+ .start = AB8500_INT_ACC_DETECT_1DB_F,
+ .end = AB8500_INT_ACC_DETECT_1DB_F,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "ACC_DETECT_1DB_R",
+ .start = AB8500_INT_ACC_DETECT_1DB_R,
+ .end = AB8500_INT_ACC_DETECT_1DB_R,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "ACC_DETECT_21DB_F",
+ .start = AB8500_INT_ACC_DETECT_21DB_F,
+ .end = AB8500_INT_ACC_DETECT_21DB_F,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "ACC_DETECT_21DB_R",
+ .start = AB8500_INT_ACC_DETECT_21DB_R,
+ .end = AB8500_INT_ACC_DETECT_21DB_R,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "ACC_DETECT_22DB_F",
+ .start = AB8500_INT_ACC_DETECT_22DB_F,
+ .end = AB8500_INT_ACC_DETECT_22DB_F,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "ACC_DETECT_22DB_R",
+ .start = AB8500_INT_ACC_DETECT_22DB_R,
+ .end = AB8500_INT_ACC_DETECT_22DB_R,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
static struct resource ab8500_charger_resources[] = {
{
.name = "MAIN_CH_UNPLUG_DET",
@@ -639,45 +678,6 @@ static struct resource ab8500_temp_resources[] = {
},
};
-static struct resource ab8500_codec_resources[] = {
- {
- .name = "ACC_DETECT_1DB_F",
- .start = AB8500_INT_ACC_DETECT_1DB_F,
- .end = AB8500_INT_ACC_DETECT_1DB_F,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "ACC_DETECT_1DB_R",
- .start = AB8500_INT_ACC_DETECT_1DB_R,
- .end = AB8500_INT_ACC_DETECT_1DB_R,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "ACC_DETECT_22DB_F",
- .start = AB8500_INT_ACC_DETECT_22DB_F,
- .end = AB8500_INT_ACC_DETECT_22DB_F,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "ACC_DETECT_22DB_R",
- .start = AB8500_INT_ACC_DETECT_22DB_R,
- .end = AB8500_INT_ACC_DETECT_22DB_R,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "ACC_DETECT_21DB_F",
- .start = AB8500_INT_ACC_DETECT_21DB_F,
- .end = AB8500_INT_ACC_DETECT_21DB_F,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "ACC_DETECT_21DB_R",
- .start = AB8500_INT_ACC_DETECT_21DB_R,
- .end = AB8500_INT_ACC_DETECT_21DB_R,
- .flags = IORESOURCE_IRQ,
- }
-};
-
static struct mfd_cell ab8500_devs[] = {
#ifdef CONFIG_DEBUG_FS
{
@@ -731,9 +731,12 @@ static struct mfd_cell ab8500_devs[] = {
.resources = ab8500_chargalg_resources,
},
{
+ .name = "ab8500-acc-det",
+ .num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources),
+ .resources = ab8500_av_acc_detect_resources,
+ },
+ {
.name = "ab8500-codec",
- .num_resources = ARRAY_SIZE(ab8500_codec_resources),
- .resources = ab8500_codec_resources,
},
{
.name = "ab8500-usb",
diff --git a/drivers/misc/audio_io_dev/ste_audio_io_core.c b/drivers/misc/audio_io_dev/ste_audio_io_core.c
index 129cd041e24..15d0273fc94 100644
--- a/drivers/misc/audio_io_dev/ste_audio_io_core.c
+++ b/drivers/misc/audio_io_dev/ste_audio_io_core.c
@@ -90,11 +90,11 @@ int ste_audio_io_core_api_init_data(struct platform_device *pdev)
goto free_regulator_vdmic_vamic1_vamic2;
}
regulator_avsource = regulator_get(ptr_audio_codec_cnxt->dev,
- "vcc-N2158");
+ "vcc-avswitch");
if (IS_ERR(regulator_avsource)) {
status = PTR_ERR(regulator_avsource);
dev_err(ptr_audio_codec_cnxt->dev,
- "Register error for vcc-N2158=%d", status);
+ "Register error for vcc-avswitch=%d", status);
goto free_regulator_vdmic_vamic1_vamic2_vaudio;
}
diff --git a/include/linux/mfd/ab8500.h b/include/linux/mfd/ab8500.h
index fb287fcbf50..1e2dd01d685 100644
--- a/include/linux/mfd/ab8500.h
+++ b/include/linux/mfd/ab8500.h
@@ -178,6 +178,7 @@ struct ab8500 {
struct regulator_reg_init;
struct regulator_init_data;
+struct ab8500_accdet_platform_data;
struct ab8500_denc_platform_data;
struct ab8500_audio_platform_data;
struct ab8500_gpio_platform_data;
@@ -190,6 +191,7 @@ struct ab8500_gpio_platform_data;
* @regulator_reg_init: regulator init registers
* @num_regulator: number of regulators
* @regulator: machine-specific constraints for regulators
+ * @accdet: machine-specific Accessory detection data
* @battery: machine-specific battery management data
* @charger: machine-specific charger data
* @btemp: machine-specific battery temp data
@@ -202,6 +204,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 ab8500_bm_data *battery;
struct ab8500_denc_platform_data *denc;
struct ab8500_audio_platform_data *audio;
diff --git a/sound/soc/ux500/ux500_ab8500.h b/include/sound/ux500_ab8500.h
index 43cd73e2e29..ff114d82d9e 100644
--- a/sound/soc/ux500/ux500_ab8500.h
+++ b/include/sound/ux500_ab8500.h
@@ -23,4 +23,6 @@ void ux500_ab8500_soc_machine_drv_cleanup(void);
int enable_regulator(const char *name);
void disable_regulator(const char *name);
+extern void ux500_ab8500_jack_report(int);
+
#endif
diff --git a/sound/soc/ux500/Makefile b/sound/soc/ux500/Makefile
index 30799a030de..333f5f27ef0 100644
--- a/sound/soc/ux500/Makefile
+++ b/sound/soc/ux500/Makefile
@@ -21,7 +21,7 @@ endif
ifdef CONFIG_SND_SOC_UX500_AB8500
snd-soc-ux500-ab8500-objs := ux500_ab8500.o
-obj-$(CONFIG_SND_SOC_UX500_AB8500) += ux500_ab8500_accessory.o ux500_ab8500.o
+obj-$(CONFIG_SND_SOC_UX500_AB8500) += ux500_ab8500.o
endif
ifdef CONFIG_SND_SOC_UX500_AV8100
diff --git a/sound/soc/ux500/u8500.c b/sound/soc/ux500/u8500.c
index ff1a6655504..4172dcb1eb1 100644
--- a/sound/soc/ux500/u8500.c
+++ b/sound/soc/ux500/u8500.c
@@ -25,7 +25,7 @@
#endif
#ifdef CONFIG_SND_SOC_UX500_AB8500
-#include "ux500_ab8500.h"
+#include <sound/ux500_ab8500.h>
#endif
#ifdef CONFIG_SND_SOC_UX500_AV8100
diff --git a/sound/soc/ux500/ux500_ab8500.c b/sound/soc/ux500/ux500_ab8500.c
index d94c2c13059..6cb36b43904 100644
--- a/sound/soc/ux500/ux500_ab8500.c
+++ b/sound/soc/ux500/ux500_ab8500.c
@@ -20,13 +20,13 @@
#include <sound/soc.h>
#include <linux/regulator/consumer.h>
#include <sound/pcm.h>
+#include <sound/jack.h>
#include <sound/pcm_params.h>
#include <sound/soc-dapm.h>
#include <mach/hardware.h>
#include "ux500_pcm.h"
#include "ux500_msp_dai.h"
#include "../codecs/ab8500.h"
-#include "ux500_ab8500_accessory.h"
#define AB8500_DAIFMT_TDM_MASTER \
(SND_SOC_DAIFMT_DSP_B | \
@@ -42,6 +42,8 @@
#define DEF_TX_SLOTS TX_SLOT_STEREO
#define DEF_RX_SLOTS RX_SLOT_MONO
+static struct snd_soc_jack jack;
+
/* Slot configuration */
static unsigned int tx_slots = DEF_TX_SLOTS;
static unsigned int rx_slots = DEF_RX_SLOTS;
@@ -247,6 +249,26 @@ static const struct snd_soc_dapm_route dapm_routes[] = {
{"DMIC6", NULL, "v-dmic"},
};
+static int create_jack(struct snd_soc_codec *codec)
+{
+ return snd_soc_jack_new(codec,
+ "AB8500 Hs Status",
+ SND_JACK_HEADPHONE |
+ SND_JACK_MICROPHONE |
+ SND_JACK_HEADSET |
+ SND_JACK_LINEOUT |
+ SND_JACK_MECHANICAL |
+ SND_JACK_VIDEOOUT,
+ &jack);
+}
+
+void ux500_ab8500_jack_report(int value)
+{
+ if (jack.jack)
+ snd_soc_jack_report(&jack, value, 0xFF);
+}
+EXPORT_SYMBOL_GPL(ux500_ab8500_jack_report);
+
int ux500_ab8500_machine_codec_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_codec *codec = rtd->codec;
@@ -261,14 +283,11 @@ int ux500_ab8500_machine_codec_init(struct snd_soc_pcm_runtime *rtd)
return status;
}
- /*
- status = ab8500_accessory_init(rtd->codec);
+ status = create_jack(codec);
if (status < 0) {
- pr_err("%s: Failed to initialize accessories (%d).\n",
- __func__, status);
+ pr_err("%s: Failed to create Jack (%d).\n", __func__, status);
return status;
}
- */
snd_soc_dapm_new_controls(codec, dapm_widgets,
ARRAY_SIZE(dapm_widgets));
@@ -283,9 +302,6 @@ void ux500_ab8500_soc_machine_drv_cleanup(void)
{
pr_info("%s: Enter.\n", __func__);
- /*
- ab8500_accessory_cleanup();
- */
regulator_bulk_free(ARRAY_SIZE(ab8500_regus), ab8500_regus);
}
diff --git a/sound/soc/ux500/ux500_ab8500_accessory.c b/sound/soc/ux500/ux500_ab8500_accessory.c
deleted file mode 100644
index 4401f51d766..00000000000
--- a/sound/soc/ux500/ux500_ab8500_accessory.c
+++ /dev/null
@@ -1,1336 +0,0 @@
-/*
- * Copyright (C) ST-Ericsson SA 2010
- *
- * Author: Jarmo K. Kuronen <jarmo.kuronen@symbio.com>
- * for ST-Ericsson.
- *
- * License terms:
- *
- * 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> /* Needed by all modules */
-#include <linux/kernel.h> /* Needed for KERN_INFO */
-#include <linux/init.h> /* Needed for the macros */
-#include <linux/timer.h>
-#include <linux/workqueue.h>
-#include <linux/delay.h>
-#include <linux/proc_fs.h>
-#include <linux/mutex.h>
-#include <linux/input.h>
-#include <linux/platform_device.h>
-#include <linux/regulator/consumer.h>
-#include <sound/soc.h>
-#include <sound/jack.h>
-#include <linux/mfd/ab8500.h>
-#include <linux/mfd/abx500.h>
-#include <linux/mfd/ab8500/ab8500-gpadc.h>
-
-/* Local definitions -----------------------------------------------*/
-#define ARG_USED(x) ((void)x)
-
-/* How many times, in a row, same device type is to be evaluate in order
- to accept it. Also limits for configuration validation. */
-#define REQUIRED_DET_MIN 2
-#define REQUIRED_DET_MAX 10
-#define REQUIRED_DET_DEF 4
-
-/* Unique value used to identify Headset button input device */
-#define BTN_INPUT_UNIQUE_VALUE "AB8500HsBtn"
-#define BTN_INPUT_DEV_NAME "Headset button"
-
-/* Timeout (ms) after jack type is checked after plug-in irq is received */
-#define DEBOUNCE_PLUG_EVENT_MS 100
-/* Timeout (ms) for subsequent plug checks used to make sure connected device
- is really detected properly */
-#define DEBOUNCE_PLUG_RETEST_MS 50
-/* Timeout after jack disconnect status is checked after plug-out det.*/
-#define DEBOUNCE_UNPLUG_EVENT_MS 250
-
-/*
-* Register definition for accessory detection.
-*/
-#define AB8500_REGU_CTRL1_SPARE_REG 0x0384
-#define AB8500_ECI_CTRL_REG 0x0800
-#define AB8500_ECI_HOOKLEVEL_REG 0x0801
-#define AB8500_ACC_DET_DB1_REG 0x0880
-#define AB8500_ACC_DET_DB2_REG 0x0881
-#define AB8500_ACC_DET_CTRL_REG 0x0882
-#define AB8500_IT_SOURCE5_REG 0x0E04
-#define AB8500_GPIO_PUD5_REG 0x1034
-#define AB8500_GPIO_DIR5_REG 0x1014
-
-/* REGISTER: AB8500_ACC_DET_CTRL_REG */
-#define BIT_ACCDETCTRL_22_ENA 0x20
-#define BIT_ACCDETCTRL_21_ENA 0x10
-#define BIT_ACCDETCTRL_2PU_ENA 0x08
-#define BIT_ACCDETCTRL_2PUS_ENA 0x02
-#define BIT_ACCDETCTRL_1_ENA 0x01
-
-/* REGISTER: AB8500_GPIO_DIR5_REG */
-#define BIT_GPIO35_DIR_OUT 0x04
-
-/* REGISTER: AB8500_REGU_CTRL1_SPARE_REG */
-#define BIT_REGUCTRL1SPARE_VAMIC1_GROUND 0x01
-
-/* REGISTER: AB8500_IT_SOURCE5_REG */
-#define BIT_ITSOURCE5_ACCDET1 0x04
-
-/* REGISTER: AB8500_ACC_DET_DB1_REG
-*
-* Accdetect1 debounce time limits, in milliseconds
-*/
-#define ACCDETECT1_DB_MIN 0
-#define ACCDETECT1_DB_MAX 70
-#define ACCDETECT1_DB_DEF 60
-#define MASK_ACCDETECT1_DB 0x07
-
-/* REGISTER: AB8500_ACC_DET_DB2_REG
-* Accdetect1 threshold voltage limits, in millivolts */
-#define ACCDETECT1_TH_MIN 300
-#define ACCDETECT1_TH_MAX 1800
-#define ACCDETECT1_TH_DEF 1800
-#define MASK_ACCDETECT1_TH 0x78
-/* Accdetect21 threshold voltage limits, in millivolts */
-#define ACCDETECT21_TH_MIN 300
-#define ACCDETECT21_TH_MAX 1800
-#define ACCDETECT21_TH_DEF 1000
-#define MASK_ACCDETECT21_TH 0x0F
-/* Accdetect22 threshold voltage limits, in millivolts */
-#define ACCDETECT22_TH_MIN 300
-#define ACCDETECT22_TH_MAX 1800
-#define ACCDETECT22_TH_DEF 1000
-#define MASK_ACCDETECT22_TH 0xF0
-
-/* After being loaded, how fast the first check is to be made */
-#define INIT_DELAY_MS 5000
-
-/* Name of the workqueue thread */
-#define WORKQUEUE_NAME "ab8500_av_wq"
-
-/* 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_DISCONNECTED) && (type != JACK_CONNECTED))
-#define jack_supports_buttons(type) \
- ((type == JACK_TYPE_HEADSET) ||\
- (type == JACK_TYPE_CARKIT) ||\
- (type == JACK_TYPE_OPENCABLE))
-
-/* Enumerations -----------------------------------------------------*/
-
-/* Possible states of a "standard" accessory button.. */
-enum accessory_button_state {
- /* Uninitialized */
- ACCESSORY_BUTTON_UNSPECIFIED,
- /* Button is currently pressed down */
- ACCESSORY_BUTTON_PRESSED,
- /* Button is not currently pressed down */
- ACCESSORY_BUTTON_RELEASED
-};
-
-/* Two separate accessory detection inputs, one to detect
-* plugin/plugout events and other to detect button events
-* while plugged in
-*/
-enum accessory_detect_channel {
- ACCESSORY_DETECT_CHANNEL_1 = 1,
- ACCESSORY_DETECT_CHANNEL_2 = 2,
- ACCESSORY_DETECT_CHANNEL_ALL = 3
-};
-
-/* Regulators used in accessory detection */
-enum accessory_regulator {
- ACCESSORY_REGULATOR_VAUDIO = 1,
- ACCESSORY_REGULATOR_VAMIC1 = 2,
- ACCESSORY_REGULATOR_ALL = 3
-};
-
-/* State of the jack and possible type */
-enum accessory_jack_state {
- JACK_UNSPECIFIED,
- JACK_DISCONNECTED,
- JACK_CONNECTED,
- JACK_TYPE_HEADPHONE,
- JACK_TYPE_HEADSET,
- JACK_TYPE_CARKIT,
- JACK_TYPE_OPENCABLE,
- JACK_TYPE_CVIDEO,
- JACK_TYPE_ECI
-};
-
-/* Accessory detect operations enumerated */
-enum accessory_op {
- ACCESSORY_TEST_DISCONNECTED,
- ACCESSORY_TEST_CONNECTED,
- ACCESSORY_TEST_HEADPHONE,
- ACCESSORY_TEST_ECI,
- ACCESSORY_TEST_CVIDEO,
- ACCESSORY_TEST_OPENCABLE,
- ACCESSORY_TEST_CARKIT,
- ACCESSORY_TEST_HEADSET
-};
-
-/*
-* @E_PLUG_IRQ
-* @E_UNPLUG_IRQ
-* @E_BUTTON_PRESS_IRQ
-* @E_BUTTON_RELEASE_IRQ
-*/
-enum accessory_irq {
- E_PLUG_IRQ,
- E_UNPLUG_IRQ,
- E_BUTTON_PRESS_IRQ,
- E_BUTTON_RELEASE_IRQ
-};
-
-/*
-* @irq Interrupt enumeration
-* @name Name of the interrupt as defined by the core driver.
-* @handler Interrupt handler
-* @registered flag indicating whether this particular interrupt
-* is already registered or not.
-*/
-struct accessory_irq_descriptor {
- enum accessory_irq irq;
- const char *name;
- irq_handler_t handler;
- int registered;
-};
-
-/*
-* Maps a detect operation to accessory state
-* @operation
-* @jack_state
-* @meas_mv
-* @minvol
-* @maxvol
-*/
-struct accessory_op_to_jack_state {
- /* Operation to be performed */
- enum accessory_op operation;
- /* If operation evals to true -> state is set to mentioned */
- enum accessory_jack_state jack_state;
- /* Whether mic voltage should be remeasured during this step,
- if not set, the previously measured cached value is to be used
- when making the decision */
- int meas_mv;
- /* Voltage limits to make the decision */
- int minvol;
- int maxvol;
-};
-
-/*
-* Device data, capsulates all relevant device data structures.
-*/
-struct devdata {
-
- struct ab8500_gpadc *gpadc;
-
- struct platform_device *codec_p_dev;
-
- /* Codec device for register access etc. */
- struct device *codec_dev;
-
- struct snd_soc_jack jack;
-
- /* Worker thread for accessory detection purposes */
- struct workqueue_struct *irq_work_queue;
-
- /* Input device for button events */
- struct input_dev *btn_input_dev;
-
- /* Current plug status */
- enum accessory_jack_state jack_type;
-
- /* Indeed, we are checking the jack status x times in a row before
- trusting the results.. due the bouncing that occurs during plugin */
- int jack_det_count;
- enum accessory_jack_state jack_type_temp;
-
- /* Current state of the accessory button if any */
- enum accessory_button_state btn_state;
-
- /* threshold value for accdetect1 */
- u8 accdetect1th;
- /* debounce value for accdetect1 */
- u8 accdetect1db;
- /* threshold value for accdetect21 */
- u8 accdetect21th;
- /* threshold value for accdetect22 */
- u8 accdetect22th;
- /* How many detections requred in a row to accept */
- int required_det;
- /* Vamic1 regulator */
- struct regulator *vamic1_reg;
- /* Is vamic1 regulator currently held or not */
- int vamic1_reg_enabled;
- /* VAudio regulator */
- struct regulator *vaudio_reg;
- /* Is vaudio regulator currently held or not */
- int vaudio_reg_enabled;
-};
-
-/* Forward declarations -------------------------------------------------*/
-
-static irqreturn_t unplug_irq_handler(int irq, void *_userdata);
-static irqreturn_t plug_irq_handler(int irq, void *_userdata);
-
-static void config_accdetect(enum accessory_jack_state state);
-static void release_irq(enum accessory_irq irq_id);
-static void claim_irq(enum accessory_irq irq_id);
-
-static void unplug_irq_handler_work(struct work_struct *work);
-static void plug_irq_handler_work(struct work_struct *work);
-static void deferred_init_handler_work(struct work_struct *work);
-static enum accessory_jack_state detect(void);
-static u8 ab8500_reg_read(u8 bank, u32 reg);
-
-/* Local variables ----------------------------------------------------------*/
-DECLARE_DELAYED_WORK(plug_irq_work, plug_irq_handler_work);
-DECLARE_DELAYED_WORK(unplug_irq_work, unplug_irq_handler_work);
-DECLARE_DELAYED_WORK(deferred_init_work, deferred_init_handler_work);
-
-/* Device data - dynamically allocated during the init. */
-static struct devdata *devdata;
-
-/* Note, order of these detections actually count -> changing
-the order might actually cause inproper detection results.
-*/
-static struct accessory_op_to_jack_state detect_ops[] = {
- /* Check is the PLUG connected */
- {
- ACCESSORY_TEST_DISCONNECTED,
- JACK_DISCONNECTED,
- 1,
- ACCESSORY_DET_VOL_DONTCARE,
- ACCESSORY_DET_VOL_DONTCARE
- },
- /* Check is the type HEADPHONE ( no mic ) */
- {
- ACCESSORY_TEST_HEADPHONE,
- JACK_TYPE_HEADPHONE,
- 1,
- ACCESSORY_HEADPHONE_DET_VOL_MIN,
- ACCESSORY_HEADPHONE_DET_VOL_MAX
- },
- /* Check with ECI communication whether device is present or not */
- {
- ACCESSORY_TEST_ECI,
- JACK_TYPE_ECI,
- 0,
- ACCESSORY_DET_VOL_DONTCARE,
- ACCESSORY_DET_VOL_DONTCARE
- },
- /* Check is the VIDEOCABLE connected */
- {
- ACCESSORY_TEST_CVIDEO,
- JACK_TYPE_CVIDEO,
- 0,
- ACCESSORY_CVIDEO_DET_VOL_MIN,
- ACCESSORY_CVIDEO_DET_VOL_MAX
- },
- /* Check is the OPEN CABLE CONNECTED */
- {
- ACCESSORY_TEST_OPENCABLE,
- JACK_TYPE_OPENCABLE,
- 0,
- ACCESSORY_OPENCABLE_DET_VOL_MIN,
- ACCESSORY_OPENCABLE_DET_VOL_MAX
- },
- /* Check is the CARKIT connected */
- {
- ACCESSORY_TEST_CARKIT,
- JACK_TYPE_CARKIT,
- 1,
- ACCESSORY_CARKIT_DET_VOL_MIN,
- ACCESSORY_CARKIT_DET_VOL_MAX
- },
- /* Check is HEADSET connected */
- {
- ACCESSORY_TEST_HEADSET,
- JACK_TYPE_HEADSET,
- 0,
- ACCESSORY_HEADSET_DET_VOL_MIN,
- ACCESSORY_HEADSET_DET_VOL_MAX
- },
- /* Last but not least, check is some unsupported device connected */
- {
- ACCESSORY_TEST_CONNECTED,
- JACK_CONNECTED,
- 0,
- ACCESSORY_DET_VOL_DONTCARE,
- ACCESSORY_DET_VOL_DONTCARE
- }
-};
-
-#ifdef DEBUG
-#define dump_reg(txt_pre, txt_post, bank, reg)\
-{\
- u8 val = ab8500_reg_read(bank, reg);\
- printk(KERN_INFO " R(%s%s) = %02x\n", txt_pre, txt_post, val);\
-}
-
-void dump_regs(const char *txt)
-{
-#if 0
- dump_reg("383", " ACCESSORY", 3, 0x383);
- dump_reg("880", " ACCESSORY", 8, 0x880);
- dump_reg("881", " ACCESSORY", 8, 0x881);
- dump_reg("882", " ACCESSORY", 8, 0x882);
- dump_reg("e44", " ACCESSORY", 14, 0xE44);
-#endif
-}
-#endif
-
-static const char *accessory_str(enum accessory_jack_state status)
-{
- const char *ret;
-
- switch (status) {
- case JACK_DISCONNECTED:
- ret = "DISCONNECTED";
- break;
- case JACK_CONNECTED:
- ret = "CONNECTED";
- break;
- case JACK_TYPE_HEADPHONE:
- ret = "HEADPHONE";
- break;
- case JACK_TYPE_HEADSET:
- ret = "HEADSET";
- break;
- case JACK_TYPE_CARKIT:
- ret = "CARKIT";
- break;
- case JACK_TYPE_OPENCABLE:
- ret = "OPENCABLE";
- break;
- case JACK_TYPE_CVIDEO:
- ret = "CVIDEO";
- break;
- case JACK_TYPE_ECI:
- ret = "ECI";
- break;
- default:
- ret = "ERROR";
- }
-
- return ret;
-}
-
-/*
-* Enables specific accessory detection regulator intelligently so
-* that reference counts are taken into account.
-*/
-static void accessory_regulator_enable(enum accessory_regulator reg)
-{
- if (reg & ACCESSORY_REGULATOR_VAUDIO) {
- if (!devdata->vaudio_reg_enabled) {
- if (!regulator_enable(devdata->vaudio_reg))
- devdata->vaudio_reg_enabled = 1;
- }
- }
-
- if (reg & ACCESSORY_REGULATOR_VAMIC1) {
- if (!devdata->vamic1_reg_enabled) {
- if (!regulator_enable(devdata->vamic1_reg))
- devdata->vamic1_reg_enabled = 1;
- }
- }
-}
-
-/*
-* Disables specific accessory detection related regulator intelligently so
-* that reference counts are taken into account.
-*/
-static void accessory_regulator_disable(enum accessory_regulator reg)
-{
- if (reg & ACCESSORY_REGULATOR_VAUDIO) {
- if (devdata->vaudio_reg_enabled) {
- if (!regulator_disable(devdata->vaudio_reg))
- devdata->vaudio_reg_enabled = 0;
- }
- }
-
- if (reg & ACCESSORY_REGULATOR_VAMIC1) {
- if (devdata->vamic1_reg_enabled) {
- if (!regulator_disable(devdata->vamic1_reg))
- devdata->vamic1_reg_enabled = 0;
- }
- }
-}
-static u8 ab8500_reg_read(u8 bank, u32 adr);
-
-static int ab8500_reg_write(u8 bank, u32 adr, u8 data)
-{
- int status = abx500_set_register_interruptible(
- devdata->codec_dev,
- bank,
- adr & 0xFF,
- data);
-
- if (status < 0)
- pr_err("%s: %x failed: %d\n", __func__, adr, status);
-
- return status;
-}
-
-/* Generic AB8500 register reading
- */
-static u8 ab8500_reg_read(u8 bank, u32 adr)
-{
- u8 value = 0;
- int status = abx500_get_register_interruptible(
- devdata->codec_dev,
- bank,
- adr & 0xFF,
- &value);
- if (status < 0)
- pr_err("%s: %x failed: %d\n", __func__, adr, status);
-
- return value;
-}
-
-/*
-* ab8500_set_bits - reads value, applies bits and writes back
-*/
-static void ab8500_set_bits(u8 bank, u32 reg, u8 bits)
-{
- u8 value = ab8500_reg_read(bank, reg);
-
- /* Check do we actually need to set any bits */
- if ((value & bits) == bits)
- return;
- value |= bits;
- ab8500_reg_write(bank, reg, value);
-}
-
-/*
-* Clears the certain bits ( mask given ) from given register in given bank
-*/
-static void ab8500_clr_bits(u8 bank, u32 reg, u8 bits)
-{
- u8 value = ab8500_reg_read(bank, reg);
-
- /* Check do we actually need to clear any bits */
- if ((value & bits) == 0)
- return;
- value &= ~bits;
- ab8500_reg_write(bank, reg, value);
-}
-
-/*
-* Configures HW so that accessory detection input 2 is effective
-*/
-static void config_accdetect2_hw(int enable)
-{
- if (enable) {
- ab8500_reg_write(AB8500_ECI_AV_ACC,
- AB8500_ACC_DET_DB2_REG,
- devdata->accdetect21th |
- devdata->accdetect22th);
-
- ab8500_set_bits(AB8500_ECI_AV_ACC,
- AB8500_ACC_DET_CTRL_REG,
- BIT_ACCDETCTRL_21_ENA |
- BIT_ACCDETCTRL_2PU_ENA);
-
- accessory_regulator_enable(ACCESSORY_REGULATOR_ALL);
-
- ab8500_set_bits(AB8500_ECI_AV_ACC,
- AB8500_ECI_CTRL_REG,
- 0x3a);
- /* @TODO: Check clearing this later on. */
- ab8500_reg_write(AB8500_ECI_AV_ACC,
- AB8500_ECI_HOOKLEVEL_REG,
- 0x1f);
- } else {
- ab8500_reg_write(AB8500_ECI_AV_ACC,
- AB8500_ACC_DET_DB2_REG,
- devdata->accdetect21th |
- devdata->accdetect22th);
-
- ab8500_clr_bits(AB8500_ECI_AV_ACC,
- AB8500_ACC_DET_CTRL_REG,
- BIT_ACCDETCTRL_21_ENA |
- BIT_ACCDETCTRL_2PU_ENA);
-
- accessory_regulator_disable(ACCESSORY_REGULATOR_ALL);
-
- /* Set ECI bits accordingly.. NOTE: reg 800+801 are UNDOC. */
- ab8500_clr_bits(AB8500_ECI_AV_ACC,
- AB8500_ECI_CTRL_REG,
- 0x3a);
- }
-}
-
-/*
-* config_accdetect1 - configures accessory detection 1 hardware in order
-* to be able to recognized plug-in/plug-out events.
-*/
-void config_accdetect1_hw(int enable)
-{
- if (enable) {
- ab8500_reg_write(AB8500_ECI_AV_ACC,
- AB8500_ACC_DET_DB1_REG,
- devdata->accdetect1th | devdata->accdetect1db);
-
- /* enable accdetect1 comparator */
- ab8500_set_bits(AB8500_ECI_AV_ACC,
- AB8500_ACC_DET_CTRL_REG,
- BIT_ACCDETCTRL_1_ENA |
- BIT_ACCDETCTRL_2PUS_ENA);
- } else {
- /* Disable accdetect1 comparator */
- ab8500_clr_bits(AB8500_ECI_AV_ACC,
- AB8500_ACC_DET_CTRL_REG,
- BIT_ACCDETCTRL_1_ENA |
- BIT_ACCDETCTRL_2PUS_ENA);
- }
-}
-
-/*
-* Configures hardware so that it is possible to receive
-* "standard" button presses from headset/carkit accessories.
-*/
-static void start_wait_button_events(void)
-{
- int stat;
-
- if (devdata && !devdata->btn_input_dev) {
- devdata->btn_input_dev = input_allocate_device();
- if (!devdata->btn_input_dev) {
- pr_err("%s: Failed to allocate input.\n", __func__);
- goto out;
- }
- input_set_capability(devdata->btn_input_dev,
- EV_KEY,
- KEY_PHONE);
-
- devdata->btn_input_dev->name = BTN_INPUT_DEV_NAME;
- devdata->btn_input_dev->uniq = BTN_INPUT_UNIQUE_VALUE;
-
- stat = input_register_device(devdata->btn_input_dev);
- if (stat) {
- pr_err("%s: register_input_device: %d\n",
- __func__,
- stat);
- input_free_device(devdata->btn_input_dev);
- devdata->btn_input_dev = NULL;
- }
- }
-out:
- return;
-}
-
-/*
-* stop_wait_button_events - stops waiting for button events.
-*/
-static void stop_wait_button_events(void)
-{
- if (devdata && devdata->btn_input_dev) {
- input_unregister_device(devdata->btn_input_dev);
- devdata->btn_input_dev = NULL;
- }
-}
-
-int init_jackdev(struct snd_soc_codec *codec)
-{
- int status;
- pr_info("%s: Enter\n", __func__);
-
- status = snd_soc_jack_new(codec,
- "Headset status",
- SND_JACK_HEADPHONE |
- SND_JACK_MICROPHONE |
- SND_JACK_HEADSET |
- SND_JACK_LINEOUT |
- SND_JACK_MECHANICAL |
- SND_JACK_VIDEOOUT,
- &devdata->jack);
-
- pr_info("%s: snd_soc_jack_new = %d\n", __func__, status);
-
- return status;
-}
-
-void update_jack_status(enum accessory_jack_state jack_status)
-{
- /* We can use "full" mask as we maintain the jack state by ourselves */
- const int mask = 0xFF;
- int value = 0;
-
- if (!devdata || !devdata->jack.jack)
- return;
-
- if (jack_status != JACK_DISCONNECTED && jack_status != JACK_UNSPECIFIED)
- value |= SND_JACK_MECHANICAL;
- if (jack_supports_mic(jack_status))
- value |= SND_JACK_MICROPHONE;
- if (jack_supports_spkr(jack_status))
- value |= (SND_JACK_HEADPHONE | SND_JACK_LINEOUT);
- if (jack_status == JACK_TYPE_CVIDEO)
- value |= SND_JACK_VIDEOOUT;
-
- snd_soc_jack_report(&devdata->jack, value, mask);
-}
-
-/*
-* Returns 1 if accessories are plugged in, 0 if not.
-*/
-static int detect_plugged_in(void)
-{
- u8 val = ab8500_reg_read(AB8500_INTERRUPT, AB8500_IT_SOURCE5_REG);
-
- if (val & BIT_ITSOURCE5_ACCDET1)
- return 0;
- return 1;
-}
-
-/*
-* mic_line_voltage_stable - measures a relative stable voltage (dV/dT)
-* from mic1 input.
-*/
-static int meas_voltage_stable(u8 input)
-{
- int iterations = 3;
- const int sleepms = 20;
- const int dmax = 30;
- int v1, v2, dv;
-
- v1 = ab8500_gpadc_convert(devdata->gpadc, input);
- do {
- msleep(sleepms);
- --iterations;
- v2 = ab8500_gpadc_convert(devdata->gpadc, input);
- dv = abs(v2 - v1);
- v1 = v2;
- } while (iterations > 0 && dv > dmax);
-
- return v1;
-}
-
-/*
-* unplug_irq_handler_work - worked routine for unplug interrupt.
-* run in context of the worker thread.
-*/
-static void unplug_irq_handler_work(struct work_struct *work)
-{
- enum accessory_jack_state type;
-
- pr_info("%s: Enter\n", __func__);
-
- type = detect();
-
- if (type == JACK_DISCONNECTED) {
- /* If disconnected > do reset the state accordingly */
- config_accdetect(type);
- devdata->btn_state = ACCESSORY_BUTTON_UNSPECIFIED;
- stop_wait_button_events();
- }
-
- devdata->jack_type = type;
- devdata->jack_det_count = 0;
-
- /* Tell the userland what has just happened */
- update_jack_status(devdata->jack_type);
-
- pr_info("Accessory: %s\n", accessory_str(devdata->jack_type));
-}
-
-/*
-* unplug_irq_handler - interrupt handler for unplug event.
-*/
-static irqreturn_t unplug_irq_handler(int irq, void *_userdata)
-{
- pr_info("%s: Enter\n", __func__);
-
- ARG_USED(irq);
- ARG_USED(_userdata);
- queue_delayed_work(devdata->irq_work_queue,
- &unplug_irq_work,
- msecs_to_jiffies(DEBOUNCE_UNPLUG_EVENT_MS));
-
- return IRQ_HANDLED;
-}
-
-/*
-* plug_irq_handler - Interrupt handler routing for plugging in the cable.
-*/
-static irqreturn_t plug_irq_handler(int irq, void *_userdata)
-{
- pr_info("%s: Enter\n", __func__);
-
- ARG_USED(irq);
- ARG_USED(_userdata);
- queue_delayed_work(devdata->irq_work_queue,
- &plug_irq_work,
- msecs_to_jiffies(DEBOUNCE_PLUG_EVENT_MS));
-
- return IRQ_HANDLED;
-}
-
-/*
-* plug_irq_handler_work - work processing for plug irq. Run in context
-* of the worker thread.
-*/
-static void plug_irq_handler_work(struct work_struct *work)
-{
- enum accessory_jack_state type;
-
- pr_info("%s: Enter\n", __func__);
-
- type = detect();
-
- if (devdata->jack_type_temp == type) {
- devdata->jack_det_count++;
- } else {
- devdata->jack_det_count = 1;
- devdata->jack_type_temp = type;
- }
-
- if (devdata->jack_det_count < devdata->required_det) {
- /* Perform the detection again until we are certain.. */
- /* @TODO: Should we have some kind of "total" limit in order
- not to go into loop in case different type is
- detected all the time. Not very likely though.*/
- queue_delayed_work(devdata->irq_work_queue,
- &plug_irq_work,
- msecs_to_jiffies(DEBOUNCE_PLUG_RETEST_MS));
- } else {
- pr_info("Accessory: %s\n", accessory_str(type));
-
- if (jack_supports_buttons(type) && type != JACK_TYPE_OPENCABLE)
- start_wait_button_events();
-
- /* Report status only if we were disconnected previously, also
- if opencable is detected, it is not reported sep.
- just yet */
- if (devdata->jack_type == JACK_DISCONNECTED) {
- devdata->jack_type = type;
- if (devdata->jack_type != JACK_TYPE_OPENCABLE)
- update_jack_status(devdata->jack_type);
- }
- config_accdetect(devdata->jack_type);
- }
-}
-
-static void report_btn_event(int pressed)
-{
- if (devdata->btn_input_dev) {
- input_report_key(devdata->btn_input_dev, KEY_PHONE, pressed);
- input_sync(devdata->btn_input_dev);
- pr_info("HS-BUTTON: %s\n", pressed ? "PRESSED" : "RELEASED");
- }
-}
-
-/*
-* button_press_irq_handler - irq handler when headset button press is detected.
-*/
-static irqreturn_t button_press_irq_handler(int irq, void *_userdata)
-{
- pr_info("%s: Enter\n", __func__);
- ARG_USED(_userdata);
-
- if (devdata->jack_type == JACK_TYPE_OPENCABLE) {
- /* Simulate the scenario where plug would have
- been connected... */
- plug_irq_handler(irq, _userdata);
- return IRQ_HANDLED;
- } else if (devdata->btn_state == ACCESSORY_BUTTON_RELEASED &&
- jack_supports_buttons(devdata->jack_type)) {
- report_btn_event(1);
- }
-
- /* Update button state */
- if (devdata->jack_type == JACK_DISCONNECTED)
- devdata->btn_state = ACCESSORY_BUTTON_UNSPECIFIED;
- else
- devdata->btn_state = ACCESSORY_BUTTON_PRESSED;
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t button_release_irq_handler(int irq, void *_userdata)
-{
- pr_info("%s: Enter\n", __func__);
-
- ARG_USED(_userdata);
- ARG_USED(irq);
-
- /* Handle button presses only of headset and/or carkit */
- if (devdata->btn_state == ACCESSORY_BUTTON_PRESSED &&
- (devdata->jack_type == JACK_TYPE_HEADSET ||
- devdata->jack_type == JACK_TYPE_CARKIT)) {
- report_btn_event(0);
- }
- if (devdata->jack_type == JACK_DISCONNECTED)
- devdata->btn_state = ACCESSORY_BUTTON_UNSPECIFIED;
- else
- devdata->btn_state = ACCESSORY_BUTTON_RELEASED;
-
- return IRQ_HANDLED;
-}
-
-/*
-* config_hw_plug_connected - configures the hardware after plug
-* insertion has been detected. That is necessary in order to be
-* able to detect what type of plug-has been inserted.
-*/
-static void config_hw_test_plug_connected(int enable)
-{
- if (enable) {
- ab8500_set_bits(AB8500_MISC, AB8500_GPIO_PUD5_REG, 0x04);
- accessory_regulator_enable(ACCESSORY_REGULATOR_VAMIC1);
- } else {
- ab8500_clr_bits(AB8500_MISC, AB8500_GPIO_PUD5_REG, 0x04);
- accessory_regulator_disable(ACCESSORY_REGULATOR_VAMIC1);
- }
-}
-
-/*
-* config_hw_basic_carkit - configures AB8500 so that carkit detection can
-* be made
-*/
-static void config_hw_test_basic_carkit(int enable)
-{
- if (enable) {
- /* Disable mic bias that is mandatory for further detections*/
- accessory_regulator_disable(ACCESSORY_REGULATOR_VAMIC1);
-
- /* Ground the VAMic1 output when disabled ->
- zero input provided */
- ab8500_set_bits(AB8500_REGU_CTRL1,
- AB8500_REGU_CTRL1_SPARE_REG,
- BIT_REGUCTRL1SPARE_VAMIC1_GROUND);
- } else {
- /* NOTE: Here we do not re-enable the VAMic1 Regulator. */
-
- /* Un-Ground the VAMic1 output when enabled */
- ab8500_clr_bits(AB8500_REGU_CTRL1,
- AB8500_REGU_CTRL1_SPARE_REG,
- BIT_REGUCTRL1SPARE_VAMIC1_GROUND);
- }
-}
-
-/*
-* mic_vol_in_range - measures somewhat stable mic1 voltage and returns it.
-* Uses cached value if not explicitly requested not to do so ( force_read ).
-*/
-static int mic_vol_in_range(int lo, int hi, int force_read)
-{
- static int mv = -100;
-
- if (mv == -100 || force_read)
- mv = meas_voltage_stable(ACC_DETECT2);
-
-#ifdef DEBUG_VOLTAGE
- pr_info("mic: %dmV (l=%dmV h=%dmV)\n", mv, lo, hi);
-#endif
- return (mv >= lo && mv <= hi) ? 1 : 0;
-}
-
-/*
-* detect_hw - tries to detect specific type of connected hardware
-*/
-static int detect_hw(struct accessory_op_to_jack_state *op)
-{
- int status;
-
- switch (op->operation) {
- case ACCESSORY_TEST_DISCONNECTED:
- config_hw_test_plug_connected(1);
- status = !detect_plugged_in();
- break;
- case ACCESSORY_TEST_CONNECTED:
- config_hw_test_plug_connected(1);
- status = detect_plugged_in();
- break;
- case ACCESSORY_TEST_ECI:
- /* @TODO: Integrate ECI support here */
- status = 0;
- break;
- case ACCESSORY_TEST_HEADPHONE:
- case ACCESSORY_TEST_CVIDEO:
- case ACCESSORY_TEST_CARKIT:
- case ACCESSORY_TEST_HEADSET:
- case ACCESSORY_TEST_OPENCABLE:
- /* Only do the config when testing carkit, does not harm for
- others but is unncessary anyway. */
- if (op->operation == ACCESSORY_TEST_CARKIT)
- config_hw_test_basic_carkit(1);
- status = mic_vol_in_range(op->minvol, op->maxvol, op->meas_mv);
- break;
- default:
- status = 0;
- }
-
- return status;
-}
-
-/*
-* detect - detects the type of the currently connected accessory if any.
-*/
-static enum accessory_jack_state detect()
-{
- enum accessory_jack_state status = JACK_DISCONNECTED;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(detect_ops); ++i) {
- if (detect_hw(&detect_ops[i])) {
- status = detect_ops[i].jack_state;
- break;
- }
- }
-
- config_hw_test_basic_carkit(0);
- config_hw_test_plug_connected(0);
-
- return status;
-}
-
-static struct accessory_irq_descriptor irq_descriptors[] = {
- {E_PLUG_IRQ, "ACC_DETECT_1DB_F",
- plug_irq_handler, 0},
- {E_UNPLUG_IRQ, "ACC_DETECT_1DB_R",
- unplug_irq_handler, 0},
- {E_BUTTON_PRESS_IRQ, "ACC_DETECT_22DB_F",
- button_press_irq_handler, 0},
- {E_BUTTON_RELEASE_IRQ, "ACC_DETECT_22DB_R",
- button_release_irq_handler, 0},
-};
-
-/*
-* Claims access to specific IRQ.
-*/
-static void claim_irq(enum accessory_irq irq_id)
-{
- int ret;
- int irq;
-
- if (irq_descriptors[irq_id].registered)
- return;
-
- irq = platform_get_irq_byname(
- devdata->codec_p_dev,
- irq_descriptors[irq_id].name);
- if (irq < 0) {
- pr_err("%s: failed to get irq nbr %s\n", __func__,
- irq_descriptors[irq_id].name);
- return;
- }
-
- ret = request_threaded_irq(irq,
- NULL,
- irq_descriptors[irq_id].handler,
- 0,
- irq_descriptors[irq_id].name,
- devdata);
- if (ret != 0) {
- pr_err("%s: Failed to claim irq %s (%d)\n",
- __func__,
- irq_descriptors[irq_id].name,
- ret);
- } else {
- irq_descriptors[irq_id].registered = 1;
- pr_info("%s: EnableIRQ %s\n",
- __func__, irq_descriptors[irq_id].name);
- }
-}
-
-/*
-* Reclaims access to specific IRQ.
-*/
-static void release_irq(enum accessory_irq irq_id)
-{
- int irq;
-
- if (!irq_descriptors[irq_id].registered)
- return;
-
- irq = platform_get_irq_byname(
- devdata->codec_p_dev,
- irq_descriptors[irq_id].name);
- if (irq < 0) {
- pr_err("%s: failed to get irq %s\n", __func__,
- irq_descriptors[irq_id].name);
- } else {
- free_irq(irq, devdata);
- irq_descriptors[irq_id].registered = 0;
- pr_info("%s: DisableIRQ %s\n",
- __func__, irq_descriptors[irq_id].name);
- }
-}
-
-static void config_accdetect_hw(enum accessory_jack_state state)
-{
- switch (state) {
- case JACK_UNSPECIFIED:
- config_accdetect1_hw(1);
- config_accdetect2_hw(0);
- break;
-
- case JACK_DISCONNECTED:
- case JACK_CONNECTED:
- case JACK_TYPE_HEADPHONE:
- case JACK_TYPE_CVIDEO:
- case JACK_TYPE_ECI:
- /* Plug In/Out detection possible */
- config_accdetect1_hw(1);
- config_accdetect2_hw(0);
- break;
-
- case JACK_TYPE_HEADSET:
- case JACK_TYPE_CARKIT:
- case JACK_TYPE_OPENCABLE:
- /* Plug Out + Button Press/Release possible */
- config_accdetect1_hw(1);
- config_accdetect2_hw(1);
- break;
-
- default:
- pr_err("%s: Unspecified state: %d\n", __func__, state);
- }
-}
-
-static void config_accdetect_irq(enum accessory_jack_state state)
-{
- switch (state) {
- case JACK_UNSPECIFIED:
- release_irq(E_PLUG_IRQ);
- release_irq(E_UNPLUG_IRQ);
- release_irq(E_BUTTON_PRESS_IRQ);
- release_irq(E_BUTTON_RELEASE_IRQ);
- break;
-
- case JACK_DISCONNECTED:
- release_irq(E_BUTTON_PRESS_IRQ);
- release_irq(E_BUTTON_RELEASE_IRQ);
- claim_irq(E_UNPLUG_IRQ);
- claim_irq(E_PLUG_IRQ);
- break;
-
- case JACK_CONNECTED:
- case JACK_TYPE_HEADPHONE:
- case JACK_TYPE_CVIDEO:
- case JACK_TYPE_ECI:
- release_irq(E_BUTTON_PRESS_IRQ);
- release_irq(E_BUTTON_RELEASE_IRQ);
- claim_irq(E_PLUG_IRQ);
- claim_irq(E_UNPLUG_IRQ);
- break;
-
- case JACK_TYPE_HEADSET:
- case JACK_TYPE_CARKIT:
- case JACK_TYPE_OPENCABLE:
- release_irq(E_PLUG_IRQ);
- claim_irq(E_UNPLUG_IRQ);
- claim_irq(E_BUTTON_PRESS_IRQ);
- claim_irq(E_BUTTON_RELEASE_IRQ);
- break;
-
- default:
- pr_err("%s: Unspecified state: %d\n", __func__, state);
- }
-}
-
-static void config_accdetect(enum accessory_jack_state state)
-{
- config_accdetect_hw(state);
- config_accdetect_irq(state);
-}
-
-static int ab8500_accessory_configure(int accdetect1db,
- int accdetect1th,
- int accdetect21th,
- int accdetect22th,
- int detections)
-{
- if (!devdata)
- return -1;
-
- if (accdetect1db >= ACCDETECT1_DB_MIN &&
- accdetect1db <= ACCDETECT1_DB_MAX) {
- devdata->accdetect1db =
- (accdetect1db / 10) & MASK_ACCDETECT1_DB;
- pr_info("%s: AccDetect1DB: %dmV > %02x\n", __func__,
- accdetect1db, devdata->accdetect1db);
- }
- if (accdetect1th >= ACCDETECT1_TH_MIN &&
- accdetect1th <= ACCDETECT1_TH_MAX) {
- devdata->accdetect1th =
- ((accdetect1th / 100 - 3) << 3) & MASK_ACCDETECT1_TH;
- pr_info("%s: AccDetect1TH: %dmV > %02x\n", __func__,
- accdetect1th, devdata->accdetect1th);
- }
- if (accdetect21th >= ACCDETECT21_TH_MIN &&
- accdetect21th <= ACCDETECT21_TH_MAX) {
- devdata->accdetect21th =
- (accdetect21th / 100 - 3) & MASK_ACCDETECT21_TH;
- pr_info("%s: AccDetect21TH: %dmV > %02x\n", __func__,
- accdetect21th, devdata->accdetect21th);
- }
- if (accdetect22th >= ACCDETECT22_TH_MIN &&
- accdetect22th <= ACCDETECT22_TH_MAX) {
- devdata->accdetect22th =
- ((accdetect22th / 100 - 3) << 4) & MASK_ACCDETECT22_TH;
- pr_info("%s: AccDetect22TH: %dmV > %02x\n", __func__,
- accdetect22th, devdata->accdetect22th);
- }
- if (detections >= REQUIRED_DET_MIN && detections <= REQUIRED_DET_MAX) {
- devdata->required_det = detections;
- pr_info("%s: detections_required: %d\n", __func__, detections);
- }
-
- return 0;
-}
-
-/*
-* Deferred initialization of the work.
-*/
-static void deferred_init_handler_work(struct work_struct *work)
-{
- pr_info("%s: Enter\n", __func__);
-
- ab8500_set_bits(AB8500_MISC, AB8500_GPIO_DIR5_REG, BIT_GPIO35_DIR_OUT);
- config_accdetect(JACK_UNSPECIFIED);
- plug_irq_handler_work(work);
-}
-
-static int init_platform_devices(struct snd_soc_codec *codec)
-{
- devdata->codec_dev = codec->dev;
- devdata->codec_p_dev = to_platform_device(codec->dev);
-
- return 0;
-}
-
-int ab8500_accessory_init(struct snd_soc_codec *codec)
-{
- pr_info("Enter: %s\n", __func__);
-
- if (devdata)
- return 0;
-
- if (!codec)
- goto fail_invalid_args;
-
- devdata = kzalloc(sizeof(struct devdata), GFP_KERNEL);
- if (!devdata) {
- pr_err("%s: Memory allocation failed\n", __func__);
- goto fail_no_mem_for_devdata;
- }
-
- devdata->gpadc = ab8500_gpadc_get();
-
- /* Get vamic1 regulator */
- devdata->vamic1_reg = regulator_get(NULL, "v-amic1");
- if (IS_ERR(devdata->vamic1_reg)) {
- pr_err("%s: Failed to allocate regulator vamic1\n", __func__);
- devdata->vamic1_reg = NULL;
- goto fail_no_vamic1_regulator;
- }
-
- /* Get vaudio regulator */
- devdata->vaudio_reg = regulator_get(NULL, "v-audio");
- if (IS_ERR(devdata->vaudio_reg)) {
- pr_err("%s: Failed to allocate regulator vaudio\n", __func__);
- devdata->vaudio_reg = NULL;
- goto fail_no_vaudio_regulator;
- }
-
- /* Configure some default values that will always work somehow */
- ab8500_accessory_configure(
- ACCDETECT1_DB_DEF,
- ACCDETECT1_TH_DEF,
- ACCDETECT21_TH_DEF,
- ACCDETECT22_TH_DEF,
- REQUIRED_DET_DEF);
-
- if (init_jackdev(codec) < 0) {
- pr_err("%s: Failed to init jack input device\n", __func__);
- goto fail_no_jackdev;
- }
-
- if (init_platform_devices(codec) < 0) {
- pr_err("%s: platform dev failed\n", __func__);
- goto fail_no_platform;
- }
-
- devdata->btn_state = ACCESSORY_BUTTON_UNSPECIFIED;
- devdata->jack_type_temp = devdata->jack_type = JACK_DISCONNECTED;
- devdata->irq_work_queue = create_singlethread_workqueue(WORKQUEUE_NAME);
- if (!devdata->irq_work_queue) {
- pr_err("%s: Failed to create work queue\n", __func__);
- goto fail_no_mem_for_wq;
- }
-
- /* Perform deferred initialization. */
- queue_delayed_work(devdata->irq_work_queue,
- &deferred_init_work,
- msecs_to_jiffies(INIT_DELAY_MS));
-
- return 0;
-
-fail_no_mem_for_wq:
-fail_no_platform:
-fail_no_jackdev:
- regulator_put(devdata->vaudio_reg);
-fail_no_vaudio_regulator:
- regulator_put(devdata->vamic1_reg);
-fail_no_vamic1_regulator:
- kfree(devdata);
- devdata = NULL;
-fail_no_mem_for_devdata:
-fail_invalid_args:
- return -ENOMEM;
-}
-
-void ab8500_accessory_cleanup(void)
-{
- pr_info("Enter: %s\n", __func__);
-
- if (!devdata)
- return;
-
- config_accdetect(JACK_UNSPECIFIED);
-
- stop_wait_button_events();
-
- cancel_delayed_work(&plug_irq_work);
- cancel_delayed_work(&unplug_irq_work);
- cancel_delayed_work(&deferred_init_work);
-
- if (devdata->vamic1_reg)
- regulator_put(devdata->vamic1_reg);
- if (devdata->vaudio_reg)
- regulator_put(devdata->vaudio_reg);
- if (devdata->irq_work_queue) {
- flush_workqueue(devdata->irq_work_queue);
- destroy_workqueue(devdata->irq_work_queue);
- }
-
- kfree(devdata);
- devdata = NULL;
-}
diff --git a/sound/soc/ux500/ux500_ab8500_accessory.h b/sound/soc/ux500/ux500_ab8500_accessory.h
deleted file mode 100644
index e019cd840aa..00000000000
--- a/sound/soc/ux500/ux500_ab8500_accessory.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) ST-Ericsson SA 2010
- *
- * Author: Jarmo K. Kuronen <jarmo.kuronen@symbio.com>
- * for ST-Ericsson.
- *
- * License terms:
- *
- * 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.
- */
-
-#ifndef UX500_AB8500_ACCESSORY_H__
-#define UX500_AB8500_ACCESSORY_H__
-
-struct snd_soc_codec;
-
-extern int ab8500_accessory_init(struct snd_soc_codec *codec);
-extern void ab8500_accessory_cleanup(void);
-
-#endif