diff options
author | Adam Hampson <ahampson@sta.samsung.com> | 2011-08-17 10:33:42 -0700 |
---|---|---|
committer | Simon Wilson <simonwilson@google.com> | 2011-10-09 13:40:44 -0700 |
commit | 2cfe35654a2e72bbd0da04647f0acd9bb8b13323 (patch) | |
tree | 26c64e760f9e2524d0c54dfe043565835e323220 | |
parent | ffc2b67857d88246b8ba1f8fdf7cb1c9afa63bd5 (diff) | |
download | kernel_samsung_tuna-2cfe35654a2e72bbd0da04647f0acd9bb8b13323.zip kernel_samsung_tuna-2cfe35654a2e72bbd0da04647f0acd9bb8b13323.tar.gz kernel_samsung_tuna-2cfe35654a2e72bbd0da04647f0acd9bb8b13323.tar.bz2 |
ARM: omap4: tuna: Add support for POGO detection
Change-Id: Iaf0559b73f8a5c6f943350a8dc8350a40e63ab51
Signed-off-by: Adam Hampson <ahampson@sta.samsung.com>
Signed-off-by: Rom Lemarchand <rlemarchand@sta.samsung.com>
-rw-r--r-- | arch/arm/mach-omap2/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/mach-omap2/board-tuna-pogo.c | 282 | ||||
-rw-r--r-- | arch/arm/mach-omap2/board-tuna.c | 4 | ||||
-rw-r--r-- | arch/arm/mach-omap2/board-tuna.h | 1 |
4 files changed, 285 insertions, 3 deletions
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile index 418a592..961a34f 100644 --- a/arch/arm/mach-omap2/Makefile +++ b/arch/arm/mach-omap2/Makefile @@ -267,6 +267,7 @@ obj-$(CONFIG_MACH_TUNA) += board-tuna-wifi.o obj-$(CONFIG_MACH_TUNA) += board-tuna-bluetooth.o \ board-tuna-emif.o obj-$(CONFIG_MACH_TUNA) += board-tuna-connector.o +obj-$(CONFIG_MACH_TUNA) += board-tuna-pogo.o obj-$(CONFIG_MACH_TUNA) += board-tuna-usbhost.o obj-$(CONFIG_MACH_OMAP3517EVM) += board-am3517evm.o \ diff --git a/arch/arm/mach-omap2/board-tuna-pogo.c b/arch/arm/mach-omap2/board-tuna-pogo.c new file mode 100644 index 0000000..b235a33 --- /dev/null +++ b/arch/arm/mach-omap2/board-tuna-pogo.c @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2011 Samsung, Inc. + * + * Author: Adam Hampson <ahampson@sta.samsung.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/delay.h> +#include <linux/switch.h> +#include <linux/wakelock.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/sched.h> +#include <linux/completion.h> + +#include <asm/div64.h> + +#include "mux.h" +#include "control.h" +#include "board-tuna.h" + +#define GPIO_POGO_DATA 121 +#define GPIO_POGO_DET 169 + +/* The below constants are in milliseconds */ +#define POGO_WAKE_PERIOD 100 +#define POGO_ID_PERIOD_TIMEOUT 750 +#define POGO_ID_CARDOCK 100 +#define POGO_ID_DESKDOCK 200 +#define POGO_ENTER_SPDIF_PERIOD 100 +#define POGO_ENTER_SPDIF_WAIT_PERIOD 100 +#define POGO_ID_PERIOD_TOLERANCE 20 + +#define POGO_DOCK_ID_MAX_RETRY 10 + +#define POGO_AUDIO_DISCONNECTED 0 +#define POGO_AUDIO_CONNECTED 2 + +#define POGO_DOCK_UNDOCKED 0 +#define POGO_DOCK_DESK 1 +#define POGO_DOCK_CAR 2 + + +struct tuna_pogo { + struct switch_dev dock_switch; + struct switch_dev audio_switch; + struct wake_lock wake_lock; + struct completion completion; + struct timespec rise_time; + struct timespec fall_time; + int det_irq; + int data_irq; + int dock_type; + bool fall_detected; +}; +static struct tuna_pogo tuna_pogo; + +static void pogo_send_pulse(unsigned int duration_in_ms) +{ + gpio_direction_output(GPIO_POGO_DATA, 1); + msleep(duration_in_ms); + gpio_direction_output(GPIO_POGO_DATA, 0); +} + +static int pogo_read_id_period(struct tuna_pogo *pogo, + unsigned int timeout_in_ms) +{ + struct timespec temp; + int ret; + + pogo->rise_time.tv_sec = 0; + pogo->rise_time.tv_nsec = 0; + pogo->fall_time.tv_sec = 0; + pogo->fall_time.tv_nsec = 0; + pogo->fall_detected = false; + + gpio_direction_input(GPIO_POGO_DATA); + + enable_irq(pogo->data_irq); + + ret = wait_for_completion_timeout(&pogo->completion, + msecs_to_jiffies(timeout_in_ms)); + if (ret <= 0) { + if (!pogo->fall_detected) + pr_debug("No response to wake within timeout\n"); + else + pr_debug("ID period did not conclude within timeout\n"); + disable_irq(pogo->data_irq); + return -1; + } + + temp = timespec_sub(pogo->fall_time, pogo->rise_time); + return temp.tv_nsec / NSEC_PER_MSEC; +} + +static irqreturn_t pogo_det_irq_thread(int irq, void *data) +{ + struct tuna_pogo *pogo = data; + int id_period; + unsigned int retry = 0; + + if (gpio_get_value(GPIO_POGO_DET)) { + wake_lock(&pogo->wake_lock); + + while (gpio_get_value(GPIO_POGO_DET) && + retry++ <= POGO_DOCK_ID_MAX_RETRY) { + + /* Start the detection process by sending a wake pulse + * to the dock. + */ + pogo_send_pulse(POGO_WAKE_PERIOD); + + id_period = pogo_read_id_period(pogo, + POGO_ID_PERIOD_TIMEOUT); + if (id_period == -1) + continue; + + /* The length of the ID period will indicate the type of + * dock that is attached. + */ + if (abs(id_period - POGO_ID_CARDOCK) <= + POGO_ID_PERIOD_TOLERANCE) { + pr_info("POGO Car Dock Detected, ID period" + " %dms\n", + id_period); + pogo->dock_type = POGO_DOCK_CAR; + switch_set_state(&pogo->dock_switch, + POGO_DOCK_CAR); + switch_set_state(&pogo->audio_switch, + POGO_AUDIO_CONNECTED); + break; + } else if (abs(id_period - POGO_ID_DESKDOCK) <= + POGO_ID_PERIOD_TOLERANCE) { + pr_info("POGO Desk Dock Detected, ID period" + " %dms\n", + id_period); + pogo->dock_type = POGO_DOCK_DESK; + switch_set_state(&pogo->dock_switch, + POGO_DOCK_DESK); + switch_set_state(&pogo->audio_switch, + POGO_AUDIO_CONNECTED); + break; + } else { + pr_err("Unknown POGO dock detected, ID period" + " %ums\n", + id_period); + } + } + + if (pogo->dock_type == POGO_DOCK_UNDOCKED) { + wake_unlock(&pogo->wake_lock); + pr_err("Unable to identify pogo dock, giving up\n"); + return IRQ_HANDLED; + } + + /* Instruct the dock to enter SPDIF mode */ + pogo_send_pulse(POGO_ENTER_SPDIF_PERIOD); + + msleep(POGO_ENTER_SPDIF_WAIT_PERIOD); + + omap_mux_set_gpio(OMAP_MUX_MODE2 | OMAP_PIN_OUTPUT, + GPIO_POGO_DATA); + + wake_unlock(&pogo->wake_lock); + } else { + if (pogo->dock_type != POGO_DOCK_UNDOCKED) { + pogo->dock_type = POGO_DOCK_UNDOCKED; + pr_info("POGO Dock Detached\n"); + switch_set_state(&pogo->dock_switch, + POGO_DOCK_UNDOCKED); + switch_set_state(&pogo->audio_switch, + POGO_AUDIO_DISCONNECTED); + } + + omap_mux_set_gpio(OMAP_MUX_MODE3 | OMAP_PIN_INPUT_PULLDOWN, + GPIO_POGO_DATA); + } + + return IRQ_HANDLED; +} + +static irqreturn_t pogo_data_irq(int irq, void *data) +{ + struct tuna_pogo *pogo = data; + + if (gpio_get_value(GPIO_POGO_DATA)) { + ktime_get_ts(&pogo->rise_time); + irq_set_irq_type(pogo->data_irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT); + } else { + ktime_get_ts(&pogo->fall_time); + pogo->fall_detected = true; + irq_set_irq_type(pogo->data_irq, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT); + complete(&pogo->completion); + disable_irq_nosync(pogo->data_irq); + } + return IRQ_HANDLED; +} + +void __init omap4_tuna_pogo_init(void) +{ + struct tuna_pogo *pogo = &tuna_pogo; + unsigned int r; + int ret; + + omap_mux_init_signal("usbb2_hsic_data.gpio_169", + OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE3); + + /* The pullup/pulldown controls in the mux register are not the controls + * that you are looking for. The usbb2_hsic_data signal has a separate + * special control in the CONTROL_USBB_HSIC register. + */ + r = omap4_ctrl_pad_readl(OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_USBB_HSIC); + r &= ~OMAP4_USBB2_HSIC_DATA_WD_MASK; + omap4_ctrl_pad_writel(r, OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_USBB_HSIC); + + ret = gpio_request(GPIO_POGO_DET, "pogo_det"); + if (ret < 0) + pr_err("request for pogo_det gpio failed, err %d\n", ret); + + gpio_direction_input(GPIO_POGO_DET); + + omap_mux_init_signal("abe_dmic_din2.gpio_121", + OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE3); + ret = gpio_request(GPIO_POGO_DATA, "pogo_data"); + if (ret < 0) + pr_err("request for pogo_data gpio failed, err %d\n", ret); + + gpio_direction_output(GPIO_POGO_DATA, 0); + + pogo->dock_switch.name = "dock"; + + /* The POGO dock does not involve USB but we are reusing the existing + * usb audio switch report the availabilty of SPDIF audio through the + * POGO dock. + */ + pogo->audio_switch.name = "usb_audio"; + + switch_dev_register(&pogo->dock_switch); + + switch_dev_register(&pogo->audio_switch); + + wake_lock_init(&pogo->wake_lock, WAKE_LOCK_IDLE, "pogo"); + + init_completion(&pogo->completion); + + pogo->det_irq = gpio_to_irq(GPIO_POGO_DET); + + ret = request_threaded_irq(pogo->det_irq, NULL, + pogo_det_irq_thread, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + "pogo_det", pogo); + if (ret < 0) + pr_err("Unable to register pogo_det interrupt (%d)\n", ret); + + pogo->data_irq = gpio_to_irq(GPIO_POGO_DATA); + + ret = request_irq(pogo->data_irq, pogo_data_irq, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "pogo_data", pogo); + if (ret < 0) + pr_err("Unable to register pogo_data interrupt (%d)\n", ret); + + disable_irq(pogo->data_irq); +} diff --git a/arch/arm/mach-omap2/board-tuna.c b/arch/arm/mach-omap2/board-tuna.c index a0bbaa7..3e0ff9a 100644 --- a/arch/arm/mach-omap2/board-tuna.c +++ b/arch/arm/mach-omap2/board-tuna.c @@ -764,9 +764,6 @@ static void tuna_audio_init(void) omap_mux_init_signal("gpmc_a24.gpio_48", OMAP_PIN_OUTPUT | OMAP_MUX_MODE3); omap_mux_init_signal("kpd_col3.gpio_171", OMAP_PIN_OUTPUT | OMAP_MUX_MODE3); - - /* McASP for S/PDIF out */ - omap_mux_init_signal("abe_dmic_din2.abe_mcasp_axr", OMAP_PIN_OUTPUT); } static struct i2c_board_info __initdata tuna_i2c1_boardinfo[] = { @@ -1304,6 +1301,7 @@ static void __init tuna_init(void) omap4_tuna_jack_init(); omap4_tuna_sensors_init(); omap4_tuna_led_init(); + omap4_tuna_pogo_init(); omap4_tuna_connector_init(); #ifdef CONFIG_OMAP_HSI_DEVICE if (TUNA_TYPE_MAGURO == omap4_tuna_get_type()) diff --git a/arch/arm/mach-omap2/board-tuna.h b/arch/arm/mach-omap2/board-tuna.h index 1326810..5f2ec1c 100644 --- a/arch/arm/mach-omap2/board-tuna.h +++ b/arch/arm/mach-omap2/board-tuna.h @@ -33,6 +33,7 @@ void omap4_tuna_jack_init(void); void omap4_tuna_nfc_init(void); void omap4_tuna_power_init(void); void omap4_tuna_sensors_init(void); +void omap4_tuna_pogo_init(void); int omap4_tuna_connector_init(void); int tuna_wlan_init(void); int omap_hsi_init(void); |