blob: fef521dcead3bd9886aabe91467cd684b7885049 [file] [log] [blame]
/*
* drivers/input/misc/ambarella_input.c
*
* Author: Anthony Ginger <hfjiang@ambarella.com>
* Copyright (C) 2004-2009, Ambarella, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* adc update: Qiao Wang 2009/10/28
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/mach-types.h>
#include <mach/hardware.h>
#include <plat/ambinput.h>
#include <mach/board.h>
#include <linux/firmware.h>
/* ========================================================================= */
#define AMBVI_BUFFER_SIZE (32)
#define AMBVI_NAME "ambvi"
/* ========================================================================= */
extern int platform_driver_register_ir(void);
extern void platform_driver_unregister_ir(void);
extern int platform_driver_register_adc(void);
extern void platform_driver_unregister_adc(void);
/* ========================================================================= */
static int abx_active_pressure = 0;
/* ========================================================================= */
static DEFINE_MUTEX(pboard_info_lock);
static struct ambarella_input_board_info *pboard_info = NULL;
static char *ambarella_keymap = NULL;
MODULE_PARM_DESC(ambarella_keymap, "Ambarella Input's key map");
module_param(ambarella_keymap, charp, 0644);
struct ambarella_input_board_info *ambarella_input_get_board_info(void)
{
mutex_lock(&pboard_info_lock);
mutex_unlock(&pboard_info_lock);
return pboard_info;
}
/* ========================================================================= */
irqreturn_t ambarella_gpio_irq(int irq, void *devid)
{
int i;
int gpio_id;
int level;
struct ambarella_input_board_info *pbinfo;
pbinfo = (struct ambarella_input_board_info *)devid;
gpio_id = irq_to_gpio(irq);
if (gpio_id < 0)
goto ambarella_gpio_irq_exit;
level = ambarella_gpio_get(gpio_id);
if (!pbinfo->pkeymap)
goto ambarella_gpio_irq_exit;
for (i = 0; i < AMBINPUT_TABLE_SIZE; i++) {
if (pbinfo->pkeymap[i].type == AMBINPUT_END)
break;
if ((pbinfo->pkeymap[i].type & AMBINPUT_SOURCE_MASK) !=
AMBINPUT_SOURCE_GPIO)
continue;
if (pbinfo->pkeymap[i].gpio_key.id != gpio_id)
continue;
if (pbinfo->pkeymap[i].type == AMBINPUT_GPIO_KEY) {
input_report_key(pbinfo->pinput_dev,
pbinfo->pkeymap[i].gpio_key.key_code,
(level == pbinfo->pkeymap[i].gpio_key.active_val) ? 1 : 0);
input_sync(pbinfo->pinput_dev);
dev_dbg(&pbinfo->pinput_dev->dev,
"GPIO %d is @ %d:%d\n",
pbinfo->pkeymap[i].gpio_key.key_code,
gpio_id, level);
break;
}
if (pbinfo->pkeymap[i].type == AMBINPUT_GPIO_SW) {
input_report_switch(pbinfo->pinput_dev,
pbinfo->pkeymap[i].gpio_sw.key_code,
(level == pbinfo->pkeymap[i].gpio_key.active_val) ? 1 : 0);
dev_dbg(&pbinfo->pinput_dev->dev,
"GPIO %d is @ %d:%d\n",
pbinfo->pkeymap[i].gpio_sw.key_code,
gpio_id, level);
break;
}
if (pbinfo->pkeymap[i].type == AMBINPUT_GPIO_REL) {
if (pbinfo->pkeymap[i].gpio_rel.key_code == REL_X) {
input_report_rel(pbinfo->pinput_dev,
REL_X,
pbinfo->pkeymap[i].gpio_rel.rel_step);
input_report_rel(pbinfo->pinput_dev,
REL_Y, 0);
input_sync(pbinfo->pinput_dev);
dev_dbg(&pbinfo->pinput_dev->dev,
"report REL_X %d @ %d:%d\n",
pbinfo->pkeymap[i].gpio_rel.rel_step,
gpio_id, level);
} else
if (pbinfo->pkeymap[i].gpio_rel.key_code == REL_Y) {
input_report_rel(pbinfo->pinput_dev,
REL_X, 0);
input_report_rel(pbinfo->pinput_dev,
REL_Y,
pbinfo->pkeymap[i].gpio_rel.rel_step);
input_sync(pbinfo->pinput_dev);
dev_dbg(&pbinfo->pinput_dev->dev,
"report REL_Y %d @ %d:%d\n",
pbinfo->pkeymap[i].gpio_rel.rel_step,
gpio_id, level);
}
break;
}
if (pbinfo->pkeymap[i].type == AMBINPUT_GPIO_ABS) {
input_report_abs(pbinfo->pinput_dev,
ABS_X, pbinfo->pkeymap[i].gpio_abs.abs_x);
input_report_abs(pbinfo->pinput_dev,
ABS_Y, pbinfo->pkeymap[i].gpio_abs.abs_y);
input_sync(pbinfo->pinput_dev);
dev_dbg(&pbinfo->pinput_dev->dev,
"report ABS %d:%d @ %d:%d\n",
pbinfo->pkeymap[i].gpio_abs.abs_x,
pbinfo->pkeymap[i].gpio_abs.abs_y,
gpio_id, level);
break;
}
}
ambarella_gpio_irq_exit:
return IRQ_HANDLED;
}
int ambarella_vi_proc_write(struct file *file,
const char __user *buffer, unsigned long count, void *data)
{
struct ambarella_input_board_info *pbinfo = data;
u32 key_num;
char cmd_buffer[AMBVI_BUFFER_SIZE];
char key_buffer[AMBVI_BUFFER_SIZE];
u32 value1;
u32 value2;
memset(key_buffer, 0, AMBVI_BUFFER_SIZE);
if (count < AMBVI_BUFFER_SIZE) {
if (copy_from_user(cmd_buffer, buffer, count))
return -EINVAL;
key_num = sscanf(cmd_buffer, "%s %d:%d",
key_buffer, &value1, &value2);
if (key_num != 3) {
dev_err(&pbinfo->pdev->dev,
"Get %d data[%s %d:%d]\n",
key_num, key_buffer, value1, value2);
return -EINVAL;
}
if (memcmp(key_buffer, "key", 3) == 0) {
input_report_key(pbinfo->pinput_dev, value1, value2);
}else
if (memcmp(key_buffer, "rel", 3) == 0) {
input_report_rel(pbinfo->pinput_dev, REL_X, value1);
input_report_rel(pbinfo->pinput_dev, REL_Y, value2);
input_sync(pbinfo->pinput_dev);
}else
if (memcmp(key_buffer, "abs", 3) == 0) {
input_report_abs(pbinfo->pinput_dev, ABS_X, value1);
input_report_abs(pbinfo->pinput_dev, ABS_Y, value2);
input_sync(pbinfo->pinput_dev);
}else
if (memcmp(key_buffer, "ton", 3) == 0) {
input_report_key(pbinfo->pinput_dev, BTN_TOUCH, 1);
input_report_abs(pbinfo->pinput_dev, ABS_X, value1);
input_report_abs(pbinfo->pinput_dev, ABS_Y, value2);
if (abx_active_pressure == 0)
abx_active_pressure =
pbinfo->abx_max_pressure / 2;
input_report_abs(pbinfo->pinput_dev, ABS_PRESSURE,
abx_active_pressure);
input_sync(pbinfo->pinput_dev);
}else
if (memcmp(key_buffer, "tof", 3) == 0) {
input_report_key(pbinfo->pinput_dev, BTN_TOUCH, 0);
input_report_abs(pbinfo->pinput_dev, ABS_PRESSURE, 0);
input_sync(pbinfo->pinput_dev);
}else
if (memcmp(key_buffer, "pre", 3) == 0) {
abx_active_pressure = value1;
input_report_abs(pbinfo->pinput_dev,
ABS_PRESSURE, value1);
input_sync(pbinfo->pinput_dev);
}else
if (memcmp(key_buffer, "wid", 3) == 0) {
input_report_abs(pbinfo->pinput_dev,
ABS_TOOL_WIDTH, value1);
input_sync(pbinfo->pinput_dev);
}else
if (memcmp(key_buffer, "swt", 3) == 0) {
input_report_switch(pbinfo->pinput_dev, value1, value2);
input_sync(pbinfo->pinput_dev);
}
}
return count;
}
static int __devinit ambarella_setup_keymap(
struct ambarella_input_board_info *pbinfo)
{
int retval = 0;
int i, j;
int vi_enabled = 0;
int vi_key_set = 0;
int vi_sw_set = 0;
const struct firmware *ext_key_map;
if (ambarella_keymap != NULL) {
retval = request_firmware(&ext_key_map,
ambarella_keymap, &pbinfo->pinput_dev->dev);
if (retval) {
printk("Can't load firmware, use default %d\n",retval);
}
else
{
printk("Keymap Load: %s, size = %d\n",
ambarella_keymap, ext_key_map->size);
memset(pbinfo->pkeymap, AMBINPUT_END,
sizeof(pbinfo->pkeymap));
memcpy(pbinfo->pkeymap,
ext_key_map->data, ext_key_map->size);
}
}
if (!pbinfo->pkeymap) {
retval = -EINVAL;
goto ambarella_setup_keymap_exit;
}
for (i = 0; i < AMBINPUT_TABLE_SIZE; i++) {
if (pbinfo->pkeymap[i].type == AMBINPUT_END)
break;
switch (pbinfo->pkeymap[i].type & AMBINPUT_SOURCE_MASK) {
case AMBINPUT_SOURCE_IR:
break;
case AMBINPUT_SOURCE_ADC:
break;
case AMBINPUT_SOURCE_GPIO:
ambarella_gpio_config(pbinfo->pkeymap[i].gpio_key.id,
GPIO_FUNC_SW_INPUT);
retval = request_irq(
GPIO_INT_VEC(pbinfo->pkeymap[i].gpio_key.id),
ambarella_gpio_irq,
pbinfo->pkeymap[i].gpio_key.irq_mode,
pbinfo->pinput_dev->name, pbinfo);
if (retval)
dev_err(&pbinfo->pdev->dev,
"Request GPIO%d IRQ failed!\n",
pbinfo->pkeymap[i].gpio_key.id);
if ((retval == 0) &&
(pbinfo->pkeymap[i].gpio_key.can_wakeup)) {
retval = set_irq_wake(GPIO_INT_VEC(
pbinfo->pkeymap[i].gpio_key.id), 1);
if (retval)
dev_err(&pbinfo->pdev->dev,
"set_irq_wake %d failed!\n",
pbinfo->pkeymap[i].gpio_key.id);
}
retval = 0; //Continue with error...
break;
case AMBINPUT_SOURCE_VI:
vi_enabled = 1;
break;
default:
dev_warn(&pbinfo->pdev->dev,
"Unknown AMBINPUT_SOURCE %d\n",
(pbinfo->pkeymap[i].type &
AMBINPUT_SOURCE_MASK));
break;
}
switch (pbinfo->pkeymap[i].type & AMBINPUT_TYPE_MASK) {
case AMBINPUT_TYPE_KEY:
set_bit(EV_KEY, pbinfo->pinput_dev->evbit);
set_bit(pbinfo->pkeymap[i].ir_key.key_code,
pbinfo->pinput_dev->keybit);
if (vi_enabled && !vi_key_set) {
for (j = 0; j < KEY_CNT; j++) {
set_bit(j, pbinfo->pinput_dev->keybit);
}
vi_key_set = 1;
}
break;
case AMBINPUT_TYPE_REL:
set_bit(EV_KEY, pbinfo->pinput_dev->evbit);
set_bit(EV_REL, pbinfo->pinput_dev->evbit);
set_bit(BTN_LEFT, pbinfo->pinput_dev->keybit);
set_bit(BTN_RIGHT, pbinfo->pinput_dev->keybit);
set_bit(REL_X, pbinfo->pinput_dev->relbit);
set_bit(REL_Y, pbinfo->pinput_dev->relbit);
break;
case AMBINPUT_TYPE_ABS:
set_bit(EV_KEY, pbinfo->pinput_dev->evbit);
set_bit(EV_ABS, pbinfo->pinput_dev->evbit);
set_bit(BTN_LEFT, pbinfo->pinput_dev->keybit);
set_bit(BTN_RIGHT, pbinfo->pinput_dev->keybit);
set_bit(BTN_TOUCH, pbinfo->pinput_dev->keybit);
set_bit(ABS_X, pbinfo->pinput_dev->absbit);
set_bit(ABS_Y, pbinfo->pinput_dev->absbit);
set_bit(ABS_PRESSURE, pbinfo->pinput_dev->absbit);
set_bit(ABS_TOOL_WIDTH, pbinfo->pinput_dev->absbit);
input_set_abs_params(pbinfo->pinput_dev, ABS_X,
0, pbinfo->abx_max_x, 0, 0);
input_set_abs_params(pbinfo->pinput_dev, ABS_Y,
0, pbinfo->abx_max_y, 0, 0);
input_set_abs_params(pbinfo->pinput_dev, ABS_PRESSURE,
0, pbinfo->abx_max_pressure, 0, 0);
input_set_abs_params(pbinfo->pinput_dev, ABS_TOOL_WIDTH,
0, pbinfo->abx_max_width, 0, 0);
break;
case AMBINPUT_TYPE_SW:
set_bit(EV_SW, pbinfo->pinput_dev->evbit);
set_bit(pbinfo->pkeymap[i].ir_key.key_code,
pbinfo->pinput_dev->swbit);
if (vi_enabled && !vi_sw_set) {
for (j = 0; j < SW_CNT; j++) {
set_bit(j, pbinfo->pinput_dev->swbit);
}
vi_sw_set = 1;
}
break;
default:
dev_warn(&pbinfo->pdev->dev,
"Unknown AMBINPUT_TYPE %d\n",
(pbinfo->pkeymap[i].type & AMBINPUT_TYPE_MASK));
break;
}
}
ambarella_setup_keymap_exit:
return retval;
}
static void __devexit ambarella_free_keymap(
struct ambarella_input_board_info *pbinfo)
{
int i;
if (!pbinfo->pkeymap)
return;
for (i = 0; i < AMBINPUT_TABLE_SIZE; i++) {
if (pbinfo->pkeymap[i].type == AMBINPUT_END)
break;
switch (pbinfo->pkeymap[i].type & AMBINPUT_SOURCE_MASK) {
case AMBINPUT_SOURCE_IR:
break;
case AMBINPUT_SOURCE_ADC:
break;
case AMBINPUT_SOURCE_VI:
break;
case AMBINPUT_SOURCE_GPIO:
if (pbinfo->pkeymap[i].gpio_key.can_wakeup)
set_irq_wake(GPIO_INT_VEC(
pbinfo->pkeymap[i].gpio_key.id), 0);
free_irq(GPIO_INT_VEC(
pbinfo->pkeymap[i].gpio_key.id), pbinfo);
break;
default:
dev_warn(&pbinfo->pdev->dev,
"Unknown AMBINPUT_SOURCE %d\n",
(pbinfo->pkeymap[i].type &
AMBINPUT_SOURCE_MASK));
break;
}
}
}
static int __devinit ambarella_input_probe(struct platform_device *pdev)
{
int retval;
struct proc_dir_entry *input_file;
struct input_dev *pinput_dev;
struct ambarella_input_board_info *pbinfo;
pbinfo =
(struct ambarella_input_board_info *)pdev->dev.platform_data;
if (pbinfo == NULL) {
dev_err(&pdev->dev, "Need board info!\n");
retval = -EPERM;
goto ambarella_input_probe_exit;
}
pinput_dev = input_allocate_device();
if (!pinput_dev) {
dev_err(&pdev->dev, "input_allocate_device fail!\n");
retval = -ENOMEM;
goto ambarella_input_probe_exit;
}
pinput_dev->name = "AmbInput";
pinput_dev->phys = "ambarella/input0";
pinput_dev->id.bustype = BUS_HOST;
retval = input_register_device(pinput_dev);
if (retval) {
dev_err(&pdev->dev, "Register input_dev failed!\n");
goto ambarella_input_probe_free_input_dev;
}
pbinfo->pdev = pdev;
pbinfo->pinput_dev = pinput_dev;
input_file = create_proc_entry(AMBVI_NAME,
S_IRUGO | S_IWUSR, get_ambarella_proc_dir());
if (input_file == NULL) {
dev_err(&pdev->dev, "Register %s failed!\n",
dev_name(&pinput_dev->dev));
retval = -ENOMEM;
goto ambarella_input_probe_unregister_device;
} else {
input_file->write_proc = ambarella_vi_proc_write;
input_file->data = pbinfo;
}
retval = ambarella_setup_keymap(pbinfo);
if (retval)
goto ambarella_input_probe_unregister_device;
pboard_info = pbinfo;
mutex_unlock(&pboard_info_lock);
dev_notice(&pdev->dev, "AmbInput probed!\n");
goto ambarella_input_probe_exit;
ambarella_input_probe_unregister_device:
input_unregister_device(pinput_dev);
ambarella_input_probe_free_input_dev:
input_free_device(pinput_dev);
pbinfo->pdev = NULL;
pbinfo->pinput_dev = NULL;
ambarella_input_probe_exit:
return retval;
}
static int __devexit ambarella_input_remove(struct platform_device *pdev)
{
int retval = 0;
struct ambarella_input_board_info *pbinfo;
pbinfo =
(struct ambarella_input_board_info *)pdev->dev.platform_data;
if (pbinfo != NULL) {
ambarella_free_keymap(pbinfo);
remove_proc_entry(AMBVI_NAME, get_ambarella_proc_dir());
input_unregister_device(pbinfo->pinput_dev);
input_free_device(pbinfo->pinput_dev);
pbinfo->pdev = NULL;
pbinfo->pinput_dev = NULL;
}
dev_notice(&pdev->dev, "AmbInput Removed!\n" );
return retval;
}
#ifdef CONFIG_PM
static int ambarella_input_suspend(struct platform_device *pdev,
pm_message_t state)
{
int errorCode = 0;
dev_dbg(&pdev->dev, "%s exit with %d @ %d\n",
__func__, errorCode, state.event);
return errorCode;
}
static int ambarella_input_resume(struct platform_device *pdev)
{
int errorCode = 0;
dev_dbg(&pdev->dev, "%s exit with %d\n", __func__, errorCode);
#ifdef CONFIG_PMIC_AMBARELLA_TPS6586X
if(ambarella_board_generic.board_rev == 'D'){
struct ambarella_input_board_info *pbinfo;
pbinfo = (struct ambarella_input_board_info *)pdev->dev.platform_data;
input_report_key(pbinfo->pinput_dev, KEY_POWER, 1);
input_sync(pbinfo->pinput_dev);
input_report_key(pbinfo->pinput_dev, KEY_POWER, 0);
input_sync(pbinfo->pinput_dev);
printk("report power key\n");
}
#endif
return errorCode;
}
#endif
static struct platform_driver ambarella_input_driver = {
.probe = ambarella_input_probe,
.remove = __devexit_p(ambarella_input_remove),
#ifdef CONFIG_PM
.suspend = ambarella_input_suspend,
.resume = ambarella_input_resume,
#endif
.driver = {
.name = "ambarella-input",
.owner = THIS_MODULE,
},
};
static int __init ambarella_input_init(void)
{
int retval = 0,i=0;
mutex_lock(&pboard_info_lock);
retval = platform_driver_register(&ambarella_input_driver);
if (retval)
printk(KERN_ERR "Register ambarella_input_driver failed %d!\n",
retval);
#ifdef CONFIG_INPUT_AMBARELLA_IR
for (i = 0; i < AMBINPUT_TABLE_SIZE; i++) {
if (pboard_info->pkeymap[i].type == AMBINPUT_END)
break;
if (pboard_info->pkeymap[i].type == AMBINPUT_IR_KEY){
if (platform_driver_register_ir())
printk(KERN_ERR "Register ambarella_ir_driver failed!\n");
break;
}
}
#endif
#ifdef CONFIG_INPUT_AMBARELLA_ADC
for (i = 0; i < AMBINPUT_TABLE_SIZE; i++) {
if (pboard_info->pkeymap[i].type == AMBINPUT_END)
break;
if (pboard_info->pkeymap[i].type == AMBINPUT_ADC_KEY){
if (platform_driver_register_adc())
printk(KERN_ERR "Register ambarella_adc_driver failed!\n");
break;
}
}
#endif
return retval;
}
static void __exit ambarella_input_exit(void)
{
#ifdef CONFIG_INPUT_AMBARELLA_ADC
platform_driver_unregister_adc();
#endif
#ifdef CONFIG_INPUT_AMBARELLA_IR
platform_driver_unregister_ir();
#endif
platform_driver_unregister(&ambarella_input_driver);
}
module_init(ambarella_input_init);
module_exit(ambarella_input_exit);
MODULE_DESCRIPTION("Ambarella Media Processor Generic Input Driver");
MODULE_AUTHOR("Anthony Ginger, <hfjiang@ambarella.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("ambinput");