251 lines
6.5 KiB
C
251 lines
6.5 KiB
C
/* arch/arm/mach-msm/board-htcleo-keypad.c
|
|
*
|
|
* Author: Markinus
|
|
* Author: Parad0X
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include <linux/gpio_event.h>
|
|
#include <linux/input.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/keyreset.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/leds.h>
|
|
#include <linux/workqueue.h>
|
|
#include <linux/gpio.h>
|
|
|
|
#include <asm/mach-types.h>
|
|
|
|
#include "board-htcleo.h"
|
|
|
|
#define HTCLEO_DEFAULT_KEYPAD_BRIGHTNESS 0
|
|
static DEFINE_MUTEX(htcleo_keypad_brightness_lock);
|
|
|
|
struct led_data {
|
|
struct mutex led_data_mutex;
|
|
struct work_struct brightness_work;
|
|
spinlock_t brightness_lock;
|
|
enum led_brightness brightness;
|
|
uint8_t oldval;
|
|
} keypad_led_data;
|
|
|
|
|
|
static unsigned int htcleo_col_gpios[] = {
|
|
HTCLEO_GPIO_KP_MKOUT0,
|
|
HTCLEO_GPIO_KP_MKOUT1,
|
|
HTCLEO_GPIO_KP_MKOUT2
|
|
};
|
|
|
|
static unsigned int htcleo_row_gpios[] = {
|
|
HTCLEO_GPIO_KP_MPIN0,
|
|
HTCLEO_GPIO_KP_MPIN1,
|
|
HTCLEO_GPIO_KP_MPIN2
|
|
};
|
|
|
|
#define KEYMAP_INDEX(col, row) ((col)*ARRAY_SIZE(htcleo_row_gpios) + (row))
|
|
#define KEYMAP_SIZE (ARRAY_SIZE(htcleo_col_gpios) * \
|
|
ARRAY_SIZE(htcleo_row_gpios))
|
|
|
|
/* keypad */
|
|
static const unsigned short htcleo_keymap[KEYMAP_SIZE] = {
|
|
#if defined(CONFIG_HTCLEO_KEYMAP_DPAD)
|
|
[KEYMAP_INDEX(0, 0)] = KEY_LEFT, // Volume Up
|
|
[KEYMAP_INDEX(0, 1)] = KEY_RIGHT, // Volume Down
|
|
[KEYMAP_INDEX(1, 0)] = KEY_DOWN, // Windows Button
|
|
[KEYMAP_INDEX(1, 1)] = KEY_ENTER, // Dial Button
|
|
[KEYMAP_INDEX(1, 2)] = KEY_END, // Hangup Button
|
|
[KEYMAP_INDEX(2, 0)] = KEY_UP, // Back Button
|
|
[KEYMAP_INDEX(2, 1)] = KEY_LEFTALT, // Home Button
|
|
#endif
|
|
#if defined(CONFIG_HTCLEO_KEYMAP_ANDROID)
|
|
[KEYMAP_INDEX(0, 0)] = KEY_VOLUMEUP, // Volume Up
|
|
[KEYMAP_INDEX(0, 1)] = KEY_VOLUMEDOWN, // Volume Down
|
|
[KEYMAP_INDEX(1, 0)] = KEY_MENU, // Windows Button
|
|
[KEYMAP_INDEX(1, 1)] = KEY_SEND, // Dial Button
|
|
[KEYMAP_INDEX(1, 2)] = KEY_END, // Hangup Button
|
|
[KEYMAP_INDEX(2, 0)] = KEY_BACK, // Back Button
|
|
[KEYMAP_INDEX(2, 1)] = KEY_HOME, // Home Button
|
|
#endif
|
|
};
|
|
|
|
static struct gpio_event_matrix_info htcleo_keypad_matrix_info = {
|
|
.info.func = gpio_event_matrix_func,
|
|
.keymap = htcleo_keymap,
|
|
.output_gpios = htcleo_col_gpios,
|
|
.input_gpios = htcleo_row_gpios,
|
|
.noutputs = ARRAY_SIZE(htcleo_col_gpios),
|
|
.ninputs = ARRAY_SIZE(htcleo_row_gpios),
|
|
.settle_time.tv.nsec = 40 * NSEC_PER_USEC,
|
|
.poll_time.tv.nsec = 20 * NSEC_PER_MSEC,
|
|
.debounce_delay.tv.nsec = 5 * NSEC_PER_MSEC,
|
|
.flags = (GPIOKPF_LEVEL_TRIGGERED_IRQ |
|
|
GPIOKPF_REMOVE_PHANTOM_KEYS |
|
|
GPIOKPF_PRINT_UNMAPPED_KEYS),
|
|
};
|
|
|
|
static struct gpio_event_direct_entry htcleo_keypad_key_map[] = {
|
|
{
|
|
.gpio = HTCLEO_GPIO_POWER_KEY,
|
|
.code = KEY_END, // Power key
|
|
},
|
|
};
|
|
|
|
static struct gpio_event_input_info htcleo_keypad_key_info = {
|
|
.info.func = gpio_event_input_func,
|
|
.info.no_suspend = true,
|
|
.flags = 0,
|
|
.type = EV_KEY,
|
|
.debounce_time.tv.nsec = 5 * NSEC_PER_MSEC,
|
|
.keymap = htcleo_keypad_key_map,
|
|
.keymap_size = ARRAY_SIZE(htcleo_keypad_key_map)
|
|
};
|
|
|
|
static struct gpio_event_info *htcleo_input_info[] = {
|
|
&htcleo_keypad_matrix_info.info,
|
|
&htcleo_keypad_key_info.info,
|
|
};
|
|
|
|
static struct gpio_event_platform_data htcleo_input_data = {
|
|
.names = {
|
|
"htcleo-keypad",
|
|
NULL,
|
|
},
|
|
.info = htcleo_input_info,
|
|
.info_count = ARRAY_SIZE(htcleo_input_info),
|
|
};
|
|
|
|
static struct platform_device htcleo_input_device = {
|
|
.name = GPIO_EVENT_DEV_NAME,
|
|
.id = 0,
|
|
.dev = {
|
|
.platform_data = &htcleo_input_data,
|
|
},
|
|
};
|
|
|
|
static struct keyreset_platform_data htcleo_reset_keys_pdata = {
|
|
.keys_down = {
|
|
KEY_END,
|
|
KEY_VOLUMEUP,
|
|
KEY_VOLUMEDOWN,
|
|
0
|
|
},
|
|
};
|
|
|
|
static struct platform_device htcleo_reset_keys_device = {
|
|
.name = KEYRESET_NAME,
|
|
.dev.platform_data = &htcleo_reset_keys_pdata,
|
|
};
|
|
|
|
static void keypad_led_brightness_set_work(struct work_struct *work)
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
enum led_brightness brightness;
|
|
uint8_t value;
|
|
|
|
spin_lock_irqsave(&keypad_led_data.brightness_lock, flags);
|
|
brightness = keypad_led_data.brightness;
|
|
spin_unlock_irqrestore(&keypad_led_data.brightness_lock, flags);
|
|
|
|
value = brightness >= 1 ? 1 : 0;
|
|
|
|
/* avoid a flicker that can occur when writing the same value */
|
|
if (keypad_led_data.oldval == value)
|
|
return;
|
|
keypad_led_data.oldval = value;
|
|
|
|
gpio_set_value(HTCLEO_GPIO_KP_LED, value);
|
|
|
|
}
|
|
|
|
static void keypad_led_brightness_set(struct led_classdev *led_cdev,
|
|
enum led_brightness brightness)
|
|
{
|
|
unsigned long flags;
|
|
mutex_lock(&htcleo_keypad_brightness_lock);
|
|
|
|
pr_debug("Setting %s brightness current %d new %d\n",
|
|
led_cdev->name, led_cdev->brightness, brightness);
|
|
|
|
if (brightness > 255)
|
|
brightness = 255;
|
|
led_cdev->brightness = brightness;
|
|
|
|
spin_lock_irqsave(&keypad_led_data.brightness_lock, flags);
|
|
keypad_led_data.brightness = brightness;
|
|
spin_unlock_irqrestore(&keypad_led_data.brightness_lock, flags);
|
|
|
|
schedule_work(&keypad_led_data.brightness_work);
|
|
mutex_unlock(&htcleo_keypad_brightness_lock);
|
|
}
|
|
|
|
static enum led_brightness keypad_led_brightness_get(struct led_classdev *led_cdev)
|
|
{
|
|
return led_cdev->brightness;
|
|
}
|
|
|
|
static struct led_classdev htcleo_backlight_led =
|
|
{
|
|
.name = "button-backlight",
|
|
.brightness = HTCLEO_DEFAULT_KEYPAD_BRIGHTNESS,
|
|
.brightness_set = keypad_led_brightness_set,
|
|
.brightness_get = keypad_led_brightness_get,
|
|
};
|
|
|
|
static int __init htcleo_init_keypad(void)
|
|
{
|
|
int ret;
|
|
|
|
if (!machine_is_htcleo())
|
|
return 0;
|
|
|
|
ret = platform_device_register(&htcleo_reset_keys_device);
|
|
if (ret != 0) {
|
|
pr_err("%s: register reset key fail\n", __func__);
|
|
goto exit;
|
|
}
|
|
|
|
ret = platform_device_register(&htcleo_input_device);
|
|
if (ret != 0)
|
|
goto exit;
|
|
|
|
ret = gpio_request(HTCLEO_GPIO_KP_LED, "keypad_led");
|
|
if (ret < 0) {
|
|
pr_err("failed on request gpio keypad backlight on\n");
|
|
goto exit;
|
|
}
|
|
|
|
ret = gpio_direction_output(HTCLEO_GPIO_KP_LED, 0);
|
|
if (ret < 0) {
|
|
pr_err("failed on gpio_direction_output keypad backlight on\n");
|
|
goto err_gpio_kpl;
|
|
}
|
|
|
|
keypad_led_data.oldval = 0;
|
|
mutex_init(&keypad_led_data.led_data_mutex);
|
|
INIT_WORK(&keypad_led_data.brightness_work, keypad_led_brightness_set_work);
|
|
spin_lock_init(&keypad_led_data.brightness_lock);
|
|
ret = led_classdev_register(&htcleo_input_device.dev, &htcleo_backlight_led);
|
|
if (ret) {
|
|
goto exit;
|
|
}
|
|
|
|
return 0;
|
|
err_gpio_kpl:
|
|
gpio_free(HTCLEO_GPIO_KP_LED);
|
|
exit:
|
|
return ret;
|
|
|
|
}
|
|
|
|
device_initcall(htcleo_init_keypad);
|