android_kernel_cmhtcleo/arch/arm/mach-msm/board-htcleo-keypad.c
Markinus a4206db79f htcleo: add keypad driver
Incl. keypad LED
2010-08-28 10:49:07 +02:00

232 lines
5.7 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"
struct led_data {
struct led_classdev ldev;
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] = {
[KEYMAP_INDEX(0, 0)] = KEY_VOLUMEUP,
[KEYMAP_INDEX(0, 1)] = KEY_VOLUMEDOWN,
[KEYMAP_INDEX(1, 0)] = KEY_MENU, // Windows Button
[KEYMAP_INDEX(1, 1)] = KEY_SEND, // Green Button
[KEYMAP_INDEX(1, 2)] = KEY_END, // Red Button
[KEYMAP_INDEX(2, 0)] = KEY_BACK, // Back Button
[KEYMAP_INDEX(2, 1)] = KEY_HOME, // Home Button
};
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 int htcleo_reset_keys_up[] = {
KEY_VOLUMEUP,
0,
};
static struct keyreset_platform_data htcleo_reset_keys_pdata = {
.keys_up = htcleo_reset_keys_up,
.keys_down = {
KEY_MENU,
0
},
};
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;
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);
}
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)
// return ret;
ret = platform_device_register(&htcleo_input_device);
if (ret != 0)
return ret;
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.ldev.name = "button-backlight";
keypad_led_data.ldev.brightness_set = keypad_led_brightness_set;
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, &keypad_led_data.ldev);
if (ret) {
keypad_led_data.ldev.name = NULL;
goto exit;
}
return 0;
err_gpio_kpl:
gpio_free(HTCLEO_GPIO_KP_LED);
exit:
return ret;
}
device_initcall(htcleo_init_keypad);