blob: 07d287f5e53d93749877d129e058bfce58ad0100 [file] [log] [blame]
/*
* drivers/input/misc/ambarella_input_ir.c
*
* Author: Qiao Wang <qwang@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
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/input.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/mach-types.h>
#include <mach/hardware.h>
#include <plat/ir.h>
#include <plat/ambinput.h>
/* ========================================================================= */
#define MAX_IR_BUFFER (512)
#define HW_FIFO_BUFFER (48)
/* ========================================================================= */
struct ambarella_ir_frame_info {
u32 frame_head_size;
u32 frame_data_size;
u32 frame_end_size;
u32 frame_repeat_head_size;
};
struct ambarella_ir_info {
struct input_dev *pinput_dev;
unsigned char __iomem *regbase;
u32 id;
struct resource *mem;
unsigned int irq;
unsigned int gpio_id;
int (*ir_parse)(struct ambarella_ir_info *pirinfo, u32 *uid);
int ir_pread;
int ir_pwrite;
u16 tick_buf[MAX_IR_BUFFER];
struct ambarella_ir_frame_info frame_info;
u32 frame_data_to_received;
u32 last_ir_uid;
u32 last_ir_flag;
struct ambarella_key_table *pkeymap;
struct ambarella_ir_controller *pcontroller_info;
};
/* ========================================================================= */
extern struct ambarella_input_board_info *ambarella_input_get_board_info(void);
/* ========================================================================= */
static u16 ambarella_ir_read_data(
struct ambarella_ir_info *pirinfo, int pointer)
{
BUG_ON(pointer < 0);
BUG_ON(pointer >= MAX_IR_BUFFER);
BUG_ON(pointer == pirinfo->ir_pwrite);
return pirinfo->tick_buf[pointer];
}
static int ambarella_ir_get_tick_size(struct ambarella_ir_info *pirinfo)
{
int size = 0;
if (pirinfo->ir_pread > pirinfo->ir_pwrite)
size = MAX_IR_BUFFER - pirinfo->ir_pread + pirinfo->ir_pwrite;
else
size = pirinfo->ir_pwrite - pirinfo->ir_pread;
return size;
}
void ambarella_ir_inc_read_ptr(struct ambarella_ir_info *pirinfo)
{
BUG_ON(pirinfo->ir_pread == pirinfo->ir_pwrite);
pirinfo->ir_pread++;
if (pirinfo->ir_pread >= MAX_IR_BUFFER)
pirinfo->ir_pread = 0;
}
void ambarella_ir_move_read_ptr(struct ambarella_ir_info *pirinfo, int offset)
{
for (; offset > 0; offset--) {
ambarella_ir_inc_read_ptr(pirinfo);
}
}
/* ========================================================================= */
#include "ambarella_ir_nec.c"
#include "ambarella_ir_sony.c"
#include "ambarella_ir_philips.c"
#include "ambarella_ir_panasonic.c"
/* ========================================================================= */
static int ambarella_input_report_ir(struct ambarella_ir_info *pirinfo, u32 uid)
{
int i;
if (!pirinfo->pkeymap)
return -1;
if ((pirinfo->last_ir_uid == uid) && (pirinfo->last_ir_flag)) {
pirinfo->last_ir_flag--;
return 0;
}
pirinfo->last_ir_uid = uid;
for (i = 0; i < AMBINPUT_TABLE_SIZE; i++) {
if (pirinfo->pkeymap[i].type == AMBINPUT_END)
break;
if ((pirinfo->pkeymap[i].type & AMBINPUT_SOURCE_MASK) !=
AMBINPUT_SOURCE_IR)
continue;
if ((pirinfo->pkeymap[i].type == AMBINPUT_IR_KEY) &&
(pirinfo->pkeymap[i].ir_key.raw_id == uid)) {
input_report_key(pirinfo->pinput_dev,
pirinfo->pkeymap[i].ir_key.key_code, 1);
input_report_key(pirinfo->pinput_dev,
pirinfo->pkeymap[i].ir_key.key_code, 0);
dev_dbg(&pirinfo->pinput_dev->dev, "IR_KEY [%d]\n",
pirinfo->pkeymap[i].ir_key.key_code);
pirinfo->last_ir_flag =
pirinfo->pkeymap[i].ir_key.key_flag;
break;
}
if ((pirinfo->pkeymap[i].type == AMBINPUT_IR_REL) &&
(pirinfo->pkeymap[i].ir_rel.raw_id == uid)) {
if (pirinfo->pkeymap[i].ir_rel.key_code == REL_X) {
input_report_rel(pirinfo->pinput_dev,
REL_X,
pirinfo->pkeymap[i].ir_rel.rel_step);
input_report_rel(pirinfo->pinput_dev,
REL_Y, 0);
input_sync(pirinfo->pinput_dev);
dev_dbg(&pirinfo->pinput_dev->dev,
"IR_REL X[%d]:Y[%d]\n",
pirinfo->pkeymap[i].ir_rel.rel_step, 0);
} else
if (pirinfo->pkeymap[i].ir_rel.key_code == REL_Y) {
input_report_rel(pirinfo->pinput_dev,
REL_X, 0);
input_report_rel(pirinfo->pinput_dev,
REL_Y,
pirinfo->pkeymap[i].ir_rel.rel_step);
input_sync(pirinfo->pinput_dev);
dev_dbg(&pirinfo->pinput_dev->dev,
"IR_REL X[%d]:Y[%d]\n", 0,
pirinfo->pkeymap[i].ir_rel.rel_step);
}
pirinfo->last_ir_flag = 0;
break;
}
if ((pirinfo->pkeymap[i].type == AMBINPUT_IR_ABS) &&
(pirinfo->pkeymap[i].ir_abs.raw_id == uid)) {
input_report_abs(pirinfo->pinput_dev,
ABS_X, pirinfo->pkeymap[i].ir_abs.abs_x);
input_report_abs(pirinfo->pinput_dev,
ABS_Y, pirinfo->pkeymap[i].ir_abs.abs_y);
input_sync(pirinfo->pinput_dev);
dev_dbg(&pirinfo->pinput_dev->dev,
"IR_ABS X[%d]:Y[%d]\n",
pirinfo->pkeymap[i].ir_abs.abs_x,
pirinfo->pkeymap[i].ir_abs.abs_y);
pirinfo->last_ir_flag = 0;
break;
}
if ((pirinfo->pkeymap[i].type == AMBINPUT_IR_SW) &&
(pirinfo->pkeymap[i].ir_sw.raw_id == uid)) {
input_report_switch(pirinfo->pinput_dev,
pirinfo->pkeymap[i].ir_sw.key_code,
pirinfo->pkeymap[i].ir_sw.key_value);
dev_dbg(&pirinfo->pinput_dev->dev, "IR_SW [%d:%d]\n",
pirinfo->pkeymap[i].ir_sw.key_code,
pirinfo->pkeymap[i].ir_sw.key_value);
pirinfo->last_ir_flag = 0;
break;
}
}
return 0;
}
static inline void ambarella_ir_write_data(struct ambarella_ir_info *pirinfo,
u16 val)
{
BUG_ON(pirinfo->ir_pwrite < 0);
BUG_ON(pirinfo->ir_pwrite >= MAX_IR_BUFFER);
pirinfo->tick_buf[pirinfo->ir_pwrite] = val;
pirinfo->ir_pwrite++;
if (pirinfo->ir_pwrite >= MAX_IR_BUFFER)
pirinfo->ir_pwrite = 0;
}
static inline int ambarella_ir_update_buffer(struct ambarella_ir_info *pirinfo)
{
int count;
int size;
count = amba_readl(pirinfo->regbase + IR_STATUS_OFFSET);
dev_dbg(&pirinfo->pinput_dev->dev, "size we got is [%d]\n", count);
for (; count > 0; count--) {
ambarella_ir_write_data(pirinfo,
amba_readl(pirinfo->regbase + IR_DATA_OFFSET));
}
size = ambarella_ir_get_tick_size(pirinfo);
return size;
}
static irqreturn_t ambarella_ir_irq(int irq, void *devid)
{
struct ambarella_ir_info *pirinfo;
int rval;
u32 uid;
u32 edges;
pirinfo = (struct ambarella_ir_info *)devid;
BUG_ON(pirinfo->ir_pread < 0);
BUG_ON(pirinfo->ir_pread >= MAX_IR_BUFFER);
if (amba_readl(pirinfo->regbase + IR_CONTROL_OFFSET) &
IR_CONTROL_FIFO_OV) {
while (amba_readl(pirinfo->regbase + IR_STATUS_OFFSET) > 0) {
amba_readl(pirinfo->regbase + IR_DATA_OFFSET);
}
amba_writel(pirinfo->regbase + IR_CONTROL_OFFSET,
(amba_readl(pirinfo->regbase + IR_CONTROL_OFFSET) |
IR_CONTROL_FIFO_OV));
dev_err(&pirinfo->pinput_dev->dev,
"IR_CONTROL_FIFO_OV overflow\n");
goto ambarella_ir_irq_exit;
}
ambarella_ir_update_buffer(pirinfo);
if(!pirinfo->ir_parse)
goto ambarella_ir_irq_exit;
rval = pirinfo->ir_parse(pirinfo, &uid);
if (rval == 0) {// yes, we find the key
if(pirinfo->pcontroller_info->debug)
printk(KERN_NOTICE "uid = 0x%08x\n", uid);
ambarella_input_report_ir(pirinfo, uid);
}
pirinfo->frame_data_to_received = pirinfo->frame_info.frame_data_size +
pirinfo->frame_info.frame_head_size;
pirinfo->frame_data_to_received -= ambarella_ir_get_tick_size(pirinfo);
if (pirinfo->frame_data_to_received <= HW_FIFO_BUFFER) {
edges = pirinfo->frame_data_to_received;
pirinfo->frame_data_to_received = 0;
} else {// > HW_FIFO_BUFFER
edges = HW_FIFO_BUFFER;
pirinfo->frame_data_to_received -= HW_FIFO_BUFFER;
}
dev_dbg(&pirinfo->pinput_dev->dev,
"line[%d],frame_data_to_received[%d]\n", __LINE__, edges);
amba_clrbitsl(pirinfo->regbase + IR_CONTROL_OFFSET,
IR_CONTROL_INTLEV(0x3F));
amba_setbitsl(pirinfo->regbase + IR_CONTROL_OFFSET,
IR_CONTROL_INTLEV(edges));
ambarella_ir_irq_exit:
amba_writel(pirinfo->regbase + IR_CONTROL_OFFSET,
(amba_readl(pirinfo->regbase + IR_CONTROL_OFFSET) |
IR_CONTROL_LEVINT));
return IRQ_HANDLED;
}
void ambarella_ir_enable(struct ambarella_ir_info *pirinfo)
{
u32 edges;
pirinfo->frame_data_to_received = pirinfo->frame_info.frame_head_size
+ pirinfo->frame_info.frame_data_size;
BUG_ON(pirinfo->frame_data_to_received > MAX_IR_BUFFER);
if (pirinfo->frame_data_to_received > HW_FIFO_BUFFER) {
edges = HW_FIFO_BUFFER;
pirinfo->frame_data_to_received -= HW_FIFO_BUFFER;
} else {
edges = pirinfo->frame_data_to_received;
pirinfo->frame_data_to_received = 0;
}
amba_writel(pirinfo->regbase + IR_CONTROL_OFFSET, IR_CONTROL_RESET);
amba_setbitsl(pirinfo->regbase + IR_CONTROL_OFFSET,
IR_CONTROL_ENB | IR_CONTROL_INTLEV(edges) | IR_CONTROL_INTENB);
enable_irq(pirinfo->irq);
}
void ambarella_ir_disable(struct ambarella_ir_info *pirinfo)
{
disable_irq(pirinfo->irq);
}
void ambarella_ir_set_protocol(struct ambarella_ir_info *pirinfo,
enum ambarella_ir_protocol protocol_id)
{
memset(pirinfo->tick_buf, 0x0, sizeof(pirinfo->tick_buf));
pirinfo->ir_pread = 0;
pirinfo->ir_pwrite = 0;
switch (protocol_id) {
case AMBA_IR_PROTOCOL_NEC:
dev_notice(&pirinfo->pinput_dev->dev,
"Protocol NEC[%d]\n", protocol_id);
pirinfo->ir_parse = ambarella_ir_nec_parse;
ambarella_ir_get_nec_info(&pirinfo->frame_info);
break;
case AMBA_IR_PROTOCOL_PANASONIC:
dev_notice(&pirinfo->pinput_dev->dev,
"Protocol PANASONIC[%d]\n", protocol_id);
pirinfo->ir_parse = ambarella_ir_panasonic_parse;
ambarella_ir_get_panasonic_info(&pirinfo->frame_info);
break;
case AMBA_IR_PROTOCOL_SONY:
dev_notice(&pirinfo->pinput_dev->dev,
"Protocol SONY[%d]\n", protocol_id);
pirinfo->ir_parse = ambarella_ir_sony_parse;
ambarella_ir_get_sony_info(&pirinfo->frame_info);
break;
case AMBA_IR_PROTOCOL_PHILIPS:
dev_notice(&pirinfo->pinput_dev->dev,
"Protocol PHILIPS[%d]\n", protocol_id);
pirinfo->ir_parse = ambarella_ir_philips_parse;
ambarella_ir_get_philips_info(&pirinfo->frame_info);
break;
default:
dev_notice(&pirinfo->pinput_dev->dev,
"Protocol default NEC[%d]\n", protocol_id);
pirinfo->ir_parse = ambarella_ir_nec_parse;
ambarella_ir_get_nec_info(&pirinfo->frame_info);
break;
}
}
void ambarella_ir_init(struct ambarella_ir_info *pirinfo)
{
ambarella_ir_disable(pirinfo);
pirinfo->pcontroller_info->set_pll();
ambarella_gpio_config(pirinfo->gpio_id, GPIO_FUNC_HW);
if (pirinfo->pcontroller_info->protocol >= AMBA_IR_PROTOCOL_END)
pirinfo->pcontroller_info->protocol = AMBA_IR_PROTOCOL_NEC;
ambarella_ir_set_protocol(pirinfo, pirinfo->pcontroller_info->protocol);
ambarella_ir_enable(pirinfo);
}
static int __devinit ambarella_ir_probe(struct platform_device *pdev)
{
int retval;
struct resource *irq;
struct resource *mem;
struct resource *io;
struct resource *ioarea;
struct ambarella_ir_info *pirinfo;
struct ambarella_input_board_info *pboard_info;
pboard_info = ambarella_input_get_board_info();
if (pboard_info == NULL){
dev_err(&pdev->dev, "pboard_info is NULL!\n");
retval = -ENOMEM;
goto ir_errorCode_na;
}
pirinfo = kzalloc(sizeof(struct ambarella_ir_info), GFP_KERNEL);
if (!pirinfo) {
dev_err(&pdev->dev, "Failed to allocate pirinfo!\n");
retval = -ENOMEM;
goto ir_errorCode_na;
}
pirinfo->pcontroller_info =
(struct ambarella_ir_controller *)pdev->dev.platform_data;
if ((pirinfo->pcontroller_info == NULL) ||
(pirinfo->pcontroller_info->set_pll == NULL) ||
(pirinfo->pcontroller_info->get_pll == NULL) ) {
dev_err(&pdev->dev, "Platform data is NULL!\n");
retval = -ENXIO;
goto ir_errorCode_pinfo;
}
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (mem == NULL) {
dev_err(&pdev->dev, "Get mem resource failed!\n");
retval = -ENXIO;
goto ir_errorCode_pinfo;
}
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (irq == NULL) {
dev_err(&pdev->dev, "Get irq resource failed!\n");
retval = -ENXIO;
goto ir_errorCode_pinfo;
}
io = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (io == NULL) {
dev_err(&pdev->dev, "Get GPIO resource failed!\n");
retval = -ENXIO;
goto ir_errorCode_pinfo;
}
ioarea = request_mem_region(mem->start,
(mem->end - mem->start) + 1, pdev->name);
if (ioarea == NULL) {
dev_err(&pdev->dev, "Request ioarea failed!\n");
retval = -EBUSY;
goto ir_errorCode_pinfo;
}
pirinfo->regbase = (unsigned char __iomem *)mem->start;
pirinfo->id = pdev->id;
pirinfo->mem = mem;
pirinfo->irq = irq->start;
pirinfo->gpio_id = io->start;
pirinfo->last_ir_uid = 0;
pirinfo->last_ir_flag = 0;
pirinfo->pinput_dev = pboard_info->pinput_dev;
pirinfo->pkeymap = pboard_info->pkeymap;
platform_set_drvdata(pdev, pirinfo);
retval = request_irq(pirinfo->irq,
ambarella_ir_irq, IRQF_TRIGGER_HIGH,
dev_name(&pdev->dev), pirinfo);
if (retval) {
dev_err(&pdev->dev, "Request IRQ failed!\n");
goto ir_errorCode_free_platform;
}
ambarella_ir_init(pirinfo);
dev_notice(&pdev->dev, "IR Host Controller probed!\n");
goto ir_errorCode_na;
ir_errorCode_free_platform:
platform_set_drvdata(pdev, NULL);
release_mem_region(mem->start, (mem->end - mem->start) + 1);
ir_errorCode_pinfo:
kfree(pirinfo);
ir_errorCode_na:
return retval;
}
static int __devexit ambarella_ir_remove(struct platform_device *pdev)
{
struct ambarella_ir_info *pirinfo;
int retval = 0;
pirinfo = platform_get_drvdata(pdev);
if (pirinfo) {
free_irq(pirinfo->irq, pirinfo);
platform_set_drvdata(pdev, NULL);
release_mem_region(pirinfo->mem->start,
(pirinfo->mem->end - pirinfo->mem->start) + 1);
kfree(pirinfo);
}
dev_notice(&pdev->dev,
"Remove Ambarella Media Processor IR Host Controller.\n");
return retval;
}
#if (defined CONFIG_PM)
static int ambarella_ir_suspend(struct platform_device *pdev,
pm_message_t state)
{
int retval = 0;
struct ambarella_ir_info *pirinfo;
pirinfo = platform_get_drvdata(pdev);
disable_irq(pirinfo->irq);
amba_clrbitsl(pirinfo->regbase + IR_CONTROL_OFFSET, IR_CONTROL_INTENB);
dev_dbg(&pdev->dev, "%s exit with %d @ %d\n",
__func__, retval, state.event);
return retval;
}
static int ambarella_ir_resume(struct platform_device *pdev)
{
int retval = 0;
struct ambarella_ir_info *pirinfo;
pirinfo = platform_get_drvdata(pdev);
ambarella_ir_enable(pirinfo);
dev_dbg(&pdev->dev, "%s exit with %d\n", __func__, retval);
return retval;
}
#endif
static struct platform_driver ambarella_ir_driver = {
.probe = ambarella_ir_probe,
.remove = __devexit_p(ambarella_ir_remove),
#if (defined CONFIG_PM)
.suspend = ambarella_ir_suspend,
.resume = ambarella_ir_resume,
#endif
.driver = {
.name = "ambarella-ir",
.owner = THIS_MODULE,
},
};
int platform_driver_register_ir(void)
{
return platform_driver_register(&ambarella_ir_driver);
}
void platform_driver_unregister_ir(void)
{
platform_driver_unregister(&ambarella_ir_driver);
}