input:ir:add remotectl support

This commit is contained in:
huang zhibao
2014-07-22 15:40:52 +08:00
parent be88b7b049
commit d809b68ff7
9 changed files with 602 additions and 695 deletions

View File

@@ -435,6 +435,18 @@
clock-names = "pclk_pwm";
status = "disabled";
};
remotectl: pwm@20050030 {
compatible = "rockchip,remotectl-pwm";
reg = <0x20050030 0x10>;
#pwm-cells = <2>;
pinctrl-names = "default";
pinctrl-0 = <&pwm3_pin>;
clocks = <&clk_gates7 10>;
clock-names = "pclk_pwm";
interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
status = "okay";
};
emmc: rksdmmc@1021c000 {
compatible = "rockchip,rk_mmc", "rockchip,rk3036-sdmmc";

View File

@@ -299,6 +299,8 @@ CONFIG_INPUT_GPIO=y
# CONFIG_COMPASS_DEVICE is not set
# CONFIG_GYROSCOPE_DEVICE is not set
# CONFIG_HALL_DEVICE is not set
CONFIG_ROCKCHIP_REMOTECTL=y
CONFIG_ROCKCHIP_REMOTECTL_PWM=y
# CONFIG_SERIO is not set
# CONFIG_VT is not set
# CONFIG_LEGACY_PTYS is not set

2
drivers/input/Kconfig Normal file → Executable file
View File

@@ -198,6 +198,8 @@ source "drivers/input/touchscreen/Kconfig"
source "drivers/input/misc/Kconfig"
source "drivers/input/sensors/Kconfig"
source "drivers/input/remotectl/Kconfig"
endif
menu "Hardware I/O ports"

1
drivers/input/Makefile Normal file → Executable file
View File

@@ -26,3 +26,4 @@ obj-$(CONFIG_INPUT_MISC) += misc/
obj-$(CONFIG_SENSOR_DEVICE) += sensors/
obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o
obj-$(CONFIG_INPUT_KEYRESET) += keyreset.o
obj-$(CONFIG_ROCKCHIP_REMOTECTL) += remotectl/

View File

@@ -2,7 +2,7 @@
# Touchscreen driver configuration
#
menuconfig ROCKCHIP_REMOTECTL
bool "rkxx remotectl"
bool "rockchip remotectl"
default n
help
Say Y here, will suport rk remotectl.
@@ -11,12 +11,7 @@ menuconfig ROCKCHIP_REMOTECTL
if ROCKCHIP_REMOTECTL
config RK_REMOTECTL
bool "rkxx remoctrl"
config ROCKCHIP_REMOTECTL_PWM
bool "rockchip remoctrl pwm capture"
default n
config RK_IR_WAKEUP
bool "rkxx remoctrl wakeup"
depends on PLAT_RK
default n
endif

View File

@@ -4,4 +4,4 @@
# Each configuration option enables a list of files.
obj-$(CONFIG_RK_REMOTECTL) += rkxx_remotectl.o
obj-$(CONFIG_ROCKCHIP_REMOTECTL_PWM) += rockchip_pwm_remotectl.o

View File

@@ -1,686 +0,0 @@
/*
* Driver for keys on GPIO lines capable of generating interrupts.
*
* Copyright 2005 Phil Blundell
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/workqueue.h>
#include <linux/adc.h>
#include <asm/gpio.h>
#include <mach/remotectl.h>
#include <mach/iomux.h>
#include <linux/wakelock.h>
#include <linux/suspend.h>
#if 1
#define remotectl_dbg(bdata, format, arg...) \
dev_printk(KERN_INFO , &bdata->input->dev , format , ## arg)
#else
#define remotectl_dbg(bdata, format, arg...)
#endif
extern suspend_state_t get_suspend_state(void);
struct rkxx_remotectl_suspend_data{
int suspend_flag;
int cnt;
long scanTime[50];
};
struct rkxx_remote_key_table{
int scanCode;
int keyCode;
};
struct rkxx_remotectl_button {
int usercode;
int nbuttons;
struct rkxx_remote_key_table *key_table;
};
struct rkxx_remotectl_drvdata {
int state;
int nbuttons;
int result;
unsigned long pre_time;
unsigned long cur_time;
long int pre_sec;
long int cur_sec;
long period;
int scanData;
int count;
int keybdNum;
int keycode;
int press;
int pre_press;
struct input_dev *input;
struct timer_list timer;
struct tasklet_struct remote_tasklet;
struct wake_lock remotectl_wake_lock;
struct rkxx_remotectl_suspend_data remotectl_suspend_data;
};
//<2F><><EFBFBD><EFBFBD>ܼ<EFBFBD>ֵ<EFBFBD><D6B5><EFBFBD><EFBFBD>
//193 //photo
//194 //video
//195 //music
//196 //IE
//197 //
//198
//199
//200
//183 //rorate_left
//184 //rorate_right
//185 //zoom out
//186 //zoom in
static struct rkxx_remote_key_table remote_key_table_meiyu_202[] = {
{0xB0, KEY_ENTER},//ok = DPAD CENTER
{0xA2, KEY_BACK},
{0xD0, KEY_UP},
{0x70, KEY_DOWN},
{0x08, KEY_LEFT},
{0x88, KEY_RIGHT}, ////////
{0x42, KEY_HOME}, //home
{0xA8, KEY_VOLUMEUP},
{0x38, KEY_VOLUMEDOWN},
{0xE2, KEY_SEARCH}, //search
{0xB2, KEY_POWER}, //power off
{0xC2, KEY_MUTE}, //mute
{0xC8, KEY_MENU},
//media ctrl
{0x78, 0x190}, //play pause
{0xF8, 0x191}, //pre
{0x02, 0x192}, //next
//pic
{0xB8, 183}, //rorate left
{0x58, 248}, //rorate right
{0x68, 185}, //zoom out
{0x98, 186}, //zoom in
//mouse switch
{0xf0,388},
//display switch
{0x82, 0x175},
};
static struct rkxx_remote_key_table remote_key_table_df[] = {
{0xf8, KEY_REPLY},
{0xc0, KEY_BACK},
{0xf0, KEY_UP},
{0xd8, KEY_DOWN},
{0xd0, KEY_LEFT},
{0xe8,KEY_RIGHT}, ////////
{0x90, KEY_VOLUMEDOWN},
{0x60, KEY_VOLUMEUP},
{0x80, KEY_HOME}, //home
{0xe0, 183}, //rorate left
{0x10, 184}, //rorate right
{0x20, 185}, //zoom out
{0xa0, 186}, //zoom in
{0x70, KEY_MUTE}, //mute
{0x50, KEY_POWER}, //power off
{0x40, KEY_SEARCH}, //search
};
extern suspend_state_t get_suspend_state(void);
static struct rkxx_remotectl_button remotectl_button[] =
{
{
.usercode = 0x206,
.nbuttons = 22,
.key_table = &remote_key_table_meiyu_202[0],
},
{
.usercode = 0x12ee,
.nbuttons = 22,
.key_table = &remote_key_table_meiyu_202[0],
},
{
.usercode = 0x202,
.nbuttons = 22,
.key_table = &remote_key_table_meiyu_202[0],
},
{
.usercode = 0xdf,
.nbuttons = 16,
.key_table = &remote_key_table_df[0],
},
};
static int remotectl_keybdNum_lookup(struct rkxx_remotectl_drvdata *ddata)
{
int i;
for (i = 0; i < sizeof(remotectl_button)/sizeof(struct rkxx_remotectl_button); i++){
if (remotectl_button[i].usercode == (ddata->scanData&0xFFFF)){
ddata->keybdNum = i;
return 1;
}
}
return 0;
}
static int remotectl_keycode_lookup(struct rkxx_remotectl_drvdata *ddata)
{
int i;
unsigned char keyData = ((ddata->scanData >> 8) & 0xff);
for (i = 0; i < remotectl_button[ddata->keybdNum].nbuttons; i++){
if (remotectl_button[ddata->keybdNum].key_table[i].scanCode == keyData){
ddata->keycode = remotectl_button[ddata->keybdNum].key_table[i].keyCode;
return 1;
}
}
return 0;
}
static void remotectl_get_pwr_scanData(struct rkxx_remotectl_drvdata *ddata,int *pwr_data,int loop)
{
int i;
int temp_scanCode;
int temp_pwr_data;
for (i = 0; i < remotectl_button[loop].nbuttons; i++){
if (remotectl_button[loop].key_table[i].keyCode == KEY_POWER){
temp_scanCode = remotectl_button[loop].key_table[i].scanCode;
temp_pwr_data = (temp_scanCode<<8)|((~temp_scanCode)&0xFF);
//printk("pwr data =0x%x\n",temp_pwr_data);
}
}
*pwr_data = temp_pwr_data;
}
static void remotectl_do_something(unsigned long data)
{
struct rkxx_remotectl_drvdata *ddata = (struct rkxx_remotectl_drvdata *)data;
switch (ddata->state)
{
case RMC_IDLE:
{
;
}
break;
case RMC_PRELOAD:
{
mod_timer(&ddata->timer,jiffies + msecs_to_jiffies(130));
//printk("RMC_PRELOAD,period=%d\n",ddata->period);
if ((TIME_PRE_MIN < ddata->period) && (ddata->period < TIME_PRE_MAX)){
ddata->scanData = 0;
ddata->count = 0;
ddata->state = RMC_USERCODE;
}else{
ddata->state = RMC_PRELOAD;
}
ddata->pre_time = ddata->cur_time;
//mod_timer(&ddata->timer,jiffies + msecs_to_jiffies(130));
}
break;
case RMC_USERCODE:
{
ddata->scanData <<= 1;
ddata->count ++;
printk("RMC_USERCODE,period=%d<><64>count=%d\n",ddata->period,ddata->count );
if ((TIME_BIT1_MIN < ddata->period) && (ddata->period < TIME_BIT1_MAX)){
ddata->scanData |= 0x01;
}
if (ddata->count == 0x10){//16 bit user code
printk("u=0x%x\n",((ddata->scanData)&0xFFFF));
if (remotectl_keybdNum_lookup(ddata)){
ddata->state = RMC_GETDATA;
ddata->scanData = 0;
ddata->count = 0;
}else{ //user code error
ddata->state = RMC_PRELOAD;
}
}
}
break;
case RMC_GETDATA:
{
ddata->count ++;
ddata->scanData <<= 1;
if ((TIME_BIT1_MIN < ddata->period) && (ddata->period < TIME_BIT1_MAX)){
ddata->scanData |= 0x01;
}
if (ddata->count == 0x10){
//printk("RMC_GETDATA=%x\n",(ddata->scanData&0xFFFF));
if ((ddata->scanData&0x0ff) == ((~ddata->scanData >> 8)&0x0ff)){
if (remotectl_keycode_lookup(ddata)){
ddata->press = 1;
/*
if (get_suspend_state()==0){
input_event(ddata->input, EV_KEY, ddata->keycode, 1);
input_sync(ddata->input);
}else if ((get_suspend_state())&&(ddata->keycode==KEY_POWER)){
input_event(ddata->input, EV_KEY, KEY_WAKEUP, 1);
input_sync(ddata->input);
}*/
//printk("0\n");
input_event(ddata->input, EV_KEY, ddata->keycode, 1);
input_sync(ddata->input);
//input_event(ddata->input, EV_KEY, ddata->keycode, ddata->press);
//input_sync(ddata->input);
ddata->state = RMC_SEQUENCE;
}else{
ddata->state = RMC_PRELOAD;
}
}else{
ddata->state = RMC_PRELOAD;
}
}
}
break;
case RMC_SEQUENCE:{
//printk( "S=%d\n",ddata->period);
if ((TIME_RPT_MIN < ddata->period) && (ddata->period < TIME_RPT_MAX)){
mod_timer(&ddata->timer,jiffies + msecs_to_jiffies(110));
//printk("1\n");;
}else if ((TIME_SEQ1_MIN < ddata->period) && (ddata->period < TIME_SEQ1_MAX)){
mod_timer(&ddata->timer,jiffies + msecs_to_jiffies(110));
//printk("2\n");
}else if ((TIME_SEQ2_MIN < ddata->period) && (ddata->period < TIME_SEQ2_MAX)){
mod_timer(&ddata->timer,jiffies + msecs_to_jiffies(110));
//printk("3\n");;
}else{
input_event(ddata->input, EV_KEY, ddata->keycode, 0);
input_sync(ddata->input);
ddata->state = RMC_PRELOAD;
ddata->press = 0;
//printk("4\n");
}
}
break;
default:
break;
}
return;
}
#ifdef CONFIG_PM
void remotectl_wakeup(unsigned long _data)
{
struct rkxx_remotectl_drvdata *ddata = (struct rkxx_remotectl_drvdata*)_data;
long *time;
int i;
int power_scanData;
time = ddata->remotectl_suspend_data.scanTime;
if (get_suspend_state()){
ddata->remotectl_suspend_data.suspend_flag = 0;
ddata->count = 0;
ddata->state = RMC_USERCODE;
ddata->scanData = 0;
for (i=0;i<ddata->remotectl_suspend_data.cnt;i++){
if (ddata->count>=32)
break;
if ((TIME_BIT1_MIN < time[i]) && (time[i] < TIME_BIT1_MAX)){
ddata->scanData |= 0x01;
ddata->scanData <<= 1;
ddata->count ++;;
}else if ((TIME_BIT0_MIN < time[i]) && (time[i] < TIME_BIT0_MAX)){
ddata->scanData <<= 1;
ddata->count ++;;
}/*else{
if (ddata->count>16){
break;
}else{
printk(KERN_ERR "ddata->count=0x%x**********************\n",ddata->count);
ddata->count = 0;
ddata->scanData = 0;
}
}*/
}
//printk(KERN_ERR"data=0x%x\n",ddata->scanData);
if (ddata->scanData) //(ddata->scanData>16)
{
ddata->scanData=(ddata->scanData>>1)&0xFFFF;
printk(KERN_ERR"data=0x%x\n",ddata->scanData);
for (i=0;i<sizeof(remotectl_button)/sizeof(struct rkxx_remotectl_button);i++){
remotectl_get_pwr_scanData(ddata,&power_scanData,i);
if ((ddata->scanData == power_scanData)||((ddata->scanData&0x0fff) == (power_scanData&0x0fff))||((ddata->scanData&0x00ff) == (power_scanData&0x00ff))) //modified by zwm 2013.06.19
{
input_event(ddata->input, EV_KEY, KEY_WAKEUP, 1);
input_sync(ddata->input);
input_event(ddata->input, EV_KEY, KEY_WAKEUP, 0);
input_sync(ddata->input);
break;
}
}
}
}
memset(ddata->remotectl_suspend_data.scanTime,0,50*sizeof(long));
ddata->remotectl_suspend_data.cnt= 0;
ddata->state = RMC_PRELOAD;
}
#endif
static void remotectl_timer(unsigned long _data)
{
struct rkxx_remotectl_drvdata *ddata = (struct rkxx_remotectl_drvdata*)_data;
//printk("to\n");
if(ddata->press != ddata->pre_press) {
ddata->pre_press = ddata->press = 0;
input_event(ddata->input, EV_KEY, ddata->keycode, 0);
input_sync(ddata->input);
//printk("5\n");
//if (get_suspend_state()==0){
//input_event(ddata->input, EV_KEY, ddata->keycode, 1);
//input_sync(ddata->input);
//input_event(ddata->input, EV_KEY, ddata->keycode, 0);
//input_sync(ddata->input);
//}else if ((get_suspend_state())&&(ddata->keycode==KEY_POWER)){
//input_event(ddata->input, EV_KEY, KEY_WAKEUP, 1);
//input_sync(ddata->input);
//input_event(ddata->input, EV_KEY, KEY_WAKEUP, 0);
//input_sync(ddata->input);
//}
}
#ifdef CONFIG_PM
remotectl_wakeup(_data);
#endif
ddata->state = RMC_PRELOAD;
}
static irqreturn_t remotectl_isr(int irq, void *dev_id)
{
struct rkxx_remotectl_drvdata *ddata = (struct rkxx_remotectl_drvdata*)dev_id;
struct timeval ts;
ddata->pre_time = ddata->cur_time;
ddata->pre_sec = ddata->cur_sec;
do_gettimeofday(&ts);
ddata->cur_time = ts.tv_usec;
ddata->cur_sec = ts.tv_sec;
if (likely(ddata->cur_sec == ddata->pre_sec)){
ddata->period = ddata->cur_time - ddata->pre_time;
}else{
ddata->period = 1000000 - ddata->pre_time + ddata->cur_time;
}
tasklet_hi_schedule(&ddata->remote_tasklet);
//if ((ddata->state==RMC_PRELOAD)||(ddata->state==RMC_SEQUENCE))
//mod_timer(&ddata->timer,jiffies + msecs_to_jiffies(130));
#ifdef CONFIG_PM
if (ddata->state==RMC_PRELOAD)
wake_lock_timeout(&ddata->remotectl_wake_lock, HZ);
if ((get_suspend_state())&&(ddata->remotectl_suspend_data.cnt<50)) //zwm
ddata->remotectl_suspend_data.scanTime[ddata->remotectl_suspend_data.cnt++] = ddata->period;
#endif
return IRQ_HANDLED;
}
static int __devinit remotectl_probe(struct platform_device *pdev)
{
struct RKxx_remotectl_platform_data *pdata = pdev->dev.platform_data;
struct rkxx_remotectl_drvdata *ddata;
struct input_dev *input;
int i, j;
int irq;
int error = 0;
printk("++++++++remotectl_probe\n");
if(!pdata)
return -EINVAL;
ddata = kzalloc(sizeof(struct rkxx_remotectl_drvdata),GFP_KERNEL);
memset(ddata,0,sizeof(struct rkxx_remotectl_drvdata));
ddata->state = RMC_PRELOAD;
input = input_allocate_device();
if (!ddata || !input) {
error = -ENOMEM;
goto fail0;
}
platform_set_drvdata(pdev, ddata);
input->name = pdev->name;
input->phys = "gpio-keys/input0";
input->dev.parent = &pdev->dev;
input->id.bustype = BUS_HOST;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0100;
/* Enable auto repeat feature of Linux input subsystem */
if (pdata->rep)
__set_bit(EV_REP, input->evbit);
ddata->nbuttons = pdata->nbuttons;
ddata->input = input;
wake_lock_init(&ddata->remotectl_wake_lock, WAKE_LOCK_SUSPEND, "rk29_remote");
if (pdata->set_iomux){
pdata->set_iomux();
}
error = gpio_request(pdata->gpio, "remotectl");
if (error < 0) {
printk("gpio-keys: failed to request GPIO %d,"
" error %d\n", pdata->gpio, error);
//goto fail1;
}
error = gpio_direction_input(pdata->gpio);
if (error < 0) {
pr_err("gpio-keys: failed to configure input"
" direction for GPIO %d, error %d\n",
pdata->gpio, error);
gpio_free(pdata->gpio);
//goto fail1;
}
irq = gpio_to_irq(pdata->gpio);
if (irq < 0) {
error = irq;
pr_err("gpio-keys: Unable to get irq number for GPIO %d, error %d\n",
pdata->gpio, error);
gpio_free(pdata->gpio);
goto fail1;
}
error = request_irq(irq, remotectl_isr, IRQF_TRIGGER_FALLING , "remotectl", ddata);
if (error) {
pr_err("gpio-remotectl: Unable to claim irq %d; error %d\n", irq, error);
gpio_free(pdata->gpio);
goto fail1;
}
setup_timer(&ddata->timer,remotectl_timer, (unsigned long)ddata);
tasklet_init(&ddata->remote_tasklet, remotectl_do_something, (unsigned long)ddata);
for (j=0;j<sizeof(remotectl_button)/sizeof(struct rkxx_remotectl_button);j++){
printk("remotectl probe j=0x%x\n",j);
for (i = 0; i < remotectl_button[j].nbuttons; i++) {
unsigned int type = EV_KEY;
input_set_capability(input, type, remotectl_button[j].key_table[i].keyCode);
}
}
error = input_register_device(input);
if (error) {
pr_err("gpio-keys: Unable to register input device, error: %d\n", error);
goto fail2;
}
input_set_capability(input, EV_KEY, KEY_WAKEUP);
device_init_wakeup(&pdev->dev, 1);
return 0;
fail2:
pr_err("gpio-remotectl input_allocate_device fail\n");
input_free_device(input);
kfree(ddata);
fail1:
pr_err("gpio-remotectl gpio irq request fail\n");
free_irq(gpio_to_irq(pdata->gpio), ddata);
del_timer_sync(&ddata->timer);
tasklet_kill(&ddata->remote_tasklet);
gpio_free(pdata->gpio);
fail0:
pr_err("gpio-remotectl input_register_device fail\n");
platform_set_drvdata(pdev, NULL);
return error;
}
static int __devexit remotectl_remove(struct platform_device *pdev)
{
struct RKxx_remotectl_platform_data *pdata = pdev->dev.platform_data;
struct rkxx_remotectl_drvdata *ddata = platform_get_drvdata(pdev);
struct input_dev *input = ddata->input;
int irq;
device_init_wakeup(&pdev->dev, 0);
irq = gpio_to_irq(pdata->gpio);
free_irq(irq, ddata);
tasklet_kill(&ddata->remote_tasklet);
gpio_free(pdata->gpio);
input_unregister_device(input);
return 0;
}
#ifdef CONFIG_PM
static int remotectl_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct RKxx_remotectl_platform_data *pdata = pdev->dev.platform_data;
struct rkxx_remotectl_drvdata *ddata = platform_get_drvdata(pdev);
//ddata->remotectl_suspend_data.suspend_flag = 1;
ddata->remotectl_suspend_data.cnt = 0;
if (device_may_wakeup(&pdev->dev)) {
if (pdata->wakeup) {
int irq = gpio_to_irq(pdata->gpio);
enable_irq_wake(irq);
}
}
return 0;
}
static int remotectl_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct RKxx_remotectl_platform_data *pdata = pdev->dev.platform_data;
if (device_may_wakeup(&pdev->dev)) {
if (pdata->wakeup) {
int irq = gpio_to_irq(pdata->gpio);
disable_irq_wake(irq);
}
}
return 0;
}
static const struct dev_pm_ops remotectl_pm_ops = {
.suspend = remotectl_suspend,
.resume = remotectl_resume,
};
#endif
static struct platform_driver remotectl_device_driver = {
.probe = remotectl_probe,
.remove = __devexit_p(remotectl_remove),
.driver = {
.name = "rkxx-remotectl",
.owner = THIS_MODULE,
#ifdef CONFIG_PM
.pm = &remotectl_pm_ops,
#endif
},
};
static int remotectl_init(void)
{
printk(KERN_INFO "++++++++remotectl_init\n");
return platform_driver_register(&remotectl_device_driver);
}
static void remotectl_exit(void)
{
platform_driver_unregister(&remotectl_device_driver);
printk(KERN_INFO "++++++++remotectl_init\n");
}
module_init(remotectl_init);
module_exit(remotectl_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("rockchip");
MODULE_DESCRIPTION("Keyboard driver for CPU GPIOs");
MODULE_ALIAS("platform:gpio-keys1");

View File

@@ -0,0 +1,453 @@
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/workqueue.h>
#include <linux/wakelock.h>
#include "rockchip_pwm_remotectl.h"
/*sys/module/rk_pwm_remotectl/parameters,
modify code_print to change the value*/
static int rk_remote_print_code;
module_param_named(code_print, rk_remote_print_code, int, 0644);
#define DBG_CODE(args...) \
do { \
if (rk_remote_print_code) { \
pr_info(args); \
} \
} while (0)
static int rk_remote_pwm_dbg_level;
module_param_named(dbg_level, rk_remote_pwm_dbg_level, int, 0644);
#define DBG(args...) \
do { \
if (rk_remote_pwm_dbg_level) { \
pr_info(args); \
} \
} while (0)
struct rkxx_remote_key_table {
int scancode;
int keycode;
};
struct rkxx_remotectl_button {
int usercode;
int nbuttons;
struct rkxx_remote_key_table *key_table;
};
struct rkxx_remotectl_drvdata {
void __iomem *base;
int state;
int nbuttons;
int result;
int scandata;
int count;
int keynum;
int keycode;
int press;
int pre_press;
int period;
int irq;
int wakeup;
struct input_dev *input;
struct timer_list timer;
struct tasklet_struct remote_tasklet;
struct wake_lock remotectl_wake_lock;
};
static struct rkxx_remote_key_table remote_key_table_meiyu_4040[] = {
{0xf2, KEY_REPLY},
{0xba, KEY_BACK},
{0xf4, KEY_UP},
{0xf1, KEY_DOWN},
{0xef, KEY_LEFT},
{0xee, KEY_RIGHT},
{0xbd, KEY_HOME},
{0xea, KEY_VOLUMEUP},
{0xe3, KEY_VOLUMEDOWN},
{0xe2, KEY_SEARCH},
{0xb2, KEY_POWER},
{0xbc, KEY_MUTE},
{0xec, KEY_MENU},
/*lay pause*/
{0xbf, 0x190},
/*pre*/
{0xe0, 0x191},
/*next*/
{0xe1, 0x192},
/*pic,rorate left*/
{0xe9, 183},
/*rorate right*/
{0xe6, 248},
/*zoom out*/
{0xe8, 185},
/*zoom in*/
{0xe7, 186},
/*mouse switch*/
{0xb8, 388},
/*zoom outdisplay switch*/
{0xbe, 0x175},
};
static struct rkxx_remote_key_table remote_key_table_sunchip_ff00[] = {
{0xf9, KEY_HOME},
{0xbf, KEY_BACK},
{0xfb, KEY_MENU},
{0xaa, KEY_REPLY},
{0xb9, KEY_UP},
{0xe9, KEY_DOWN},
{0xb8, KEY_LEFT},
{0xea, KEY_RIGHT},
{0xeb, KEY_VOLUMEDOWN},
{0xef, KEY_VOLUMEUP},
{0xf7, KEY_MUTE},
{0xe7, KEY_POWER},
{0xfc, KEY_POWER},
{0xa9, KEY_VOLUMEDOWN},
{0xa8, KEY_VOLUMEDOWN},
{0xe0, KEY_VOLUMEDOWN},
{0xa5, KEY_VOLUMEDOWN},
{0xab, 183},
{0xb7, 388},
{0xf8, 184},
{0xaf, 185},
{0xed, KEY_VOLUMEDOWN},
{0xee, 186},
{0xb3, KEY_VOLUMEDOWN},
{0xf1, KEY_VOLUMEDOWN},
{0xf2, KEY_VOLUMEDOWN},
{0xf3, KEY_SEARCH},
{0xb4, KEY_VOLUMEDOWN},
{0xbe, KEY_SEARCH},
};
static struct rkxx_remotectl_button remotectl_button[] = {
{
.usercode = 0xff00,
.nbuttons = 29,
.key_table = &remote_key_table_sunchip_ff00[0],
},
{
.usercode = 0x4040,
.nbuttons = 22,
.key_table = &remote_key_table_meiyu_4040[0],
},
};
static int remotectl_keybd_num_lookup(struct rkxx_remotectl_drvdata *ddata)
{
int i;
int num;
num = sizeof(remotectl_button)/sizeof(struct rkxx_remotectl_button);
for (i = 0; i < num; i++) {
if (remotectl_button[i].usercode == (ddata->scandata&0xFFFF)) {
ddata->keynum = i;
return 1;
}
}
return 0;
}
static int remotectl_keycode_lookup(struct rkxx_remotectl_drvdata *ddata)
{
int i;
unsigned char keydata = (unsigned char)((ddata->scandata >> 8) & 0xff);
for (i = 0; i < remotectl_button[ddata->keynum].nbuttons; i++) {
if (remotectl_button[ddata->keynum].key_table[i].scancode ==
keydata) {
ddata->keycode =
remotectl_button[ddata->keynum].key_table[i].keycode;
return 1;
}
}
return 0;
}
static void rk_pwm_remotectl_do_something(unsigned long data)
{
struct rkxx_remotectl_drvdata *ddata;
ddata = (struct rkxx_remotectl_drvdata *)data;
switch (ddata->state) {
case RMC_IDLE: {
;
break;
}
case RMC_PRELOAD: {
mod_timer(&ddata->timer, jiffies + msecs_to_jiffies(130));
if ((RK_PWM_TIME_PRE_MIN < ddata->period) &&
(ddata->period < RK_PWM_TIME_PRE_MAX)) {
ddata->scandata = 0;
ddata->count = 0;
ddata->state = RMC_USERCODE;
} else {
ddata->state = RMC_PRELOAD;
}
break;
}
case RMC_USERCODE: {
if ((RK_PWM_TIME_BIT1_MIN < ddata->period) &&
(ddata->period < RK_PWM_TIME_BIT1_MAX))
ddata->scandata |= (0x01 << ddata->count);
ddata->count++;
if (ddata->count == 0x10) {
DBG_CODE("USERCODE=0x%x\n", ddata->scandata);
if (remotectl_keybd_num_lookup(ddata)) {
ddata->state = RMC_GETDATA;
ddata->scandata = 0;
ddata->count = 0;
} else {
ddata->state = RMC_PRELOAD;
}
}
}
break;
case RMC_GETDATA: {
if ((RK_PWM_TIME_BIT1_MIN < ddata->period) &&
(ddata->period < RK_PWM_TIME_BIT1_MAX))
ddata->scandata |= (0x01<<ddata->count);
ddata->count++;
if (ddata->count < 0x10)
return;
DBG_CODE("RMC_GETDATA=%x\n", (ddata->scandata>>8));
if ((ddata->scandata&0x0ff) ==
((~ddata->scandata >> 8) & 0x0ff)) {
if (remotectl_keycode_lookup(ddata)) {
ddata->press = 1;
input_event(ddata->input, EV_KEY,
ddata->keycode, 1);
input_sync(ddata->input);
ddata->state = RMC_SEQUENCE;
} else {
ddata->state = RMC_PRELOAD;
}
} else {
ddata->state = RMC_PRELOAD;
}
}
break;
case RMC_SEQUENCE:{
DBG("S=%d\n", ddata->period);
if ((RK_PWM_TIME_RPT_MIN < ddata->period) &&
(ddata->period < RK_PWM_TIME_RPT_MAX)) {
DBG("S1\n");
mod_timer(&ddata->timer, jiffies
+ msecs_to_jiffies(110));
} else if ((RK_PWM_TIME_SEQ1_MIN < ddata->period) &&
(ddata->period < RK_PWM_TIME_SEQ1_MAX)) {
DBG("S2\n");
mod_timer(&ddata->timer, jiffies
+ msecs_to_jiffies(110));
} else if ((RK_PWM_TIME_SEQ2_MIN < ddata->period) &&
(ddata->period < RK_PWM_TIME_SEQ2_MAX)) {
DBG("S3\n");
mod_timer(&ddata->timer, jiffies
+ msecs_to_jiffies(110));
} else {
DBG("S4\n");
input_event(ddata->input, EV_KEY,
ddata->keycode, 0);
input_sync(ddata->input);
ddata->state = RMC_PRELOAD;
ddata->press = 0;
}
}
break;
default:
break;
}
}
static void rk_pwm_remotectl_timer(unsigned long _data)
{
struct rkxx_remotectl_drvdata *ddata;
ddata = (struct rkxx_remotectl_drvdata *)_data;
if (ddata->press != ddata->pre_press) {
ddata->pre_press = 0;
ddata->press = 0;
input_event(ddata->input, EV_KEY, ddata->keycode, 0);
input_sync(ddata->input);
}
ddata->state = RMC_PRELOAD;
}
static irqreturn_t rockchip_pwm_irq(int irq, void *dev_id)
{
struct rkxx_remotectl_drvdata *ddata;
int val;
ddata = (struct rkxx_remotectl_drvdata *)dev_id;
val = readl_relaxed(ddata->base + PWM_REG_INTSTS);
if (val&PWM_CH3_INT) {
if (val & PWM_CH3_POL) {
val = readl_relaxed(ddata->base + PWM_REG_HPR);
ddata->period = val;
tasklet_hi_schedule(&ddata->remote_tasklet);
DBG("hpr=0x%x\n", val);
} else {
val = readl_relaxed(ddata->base + PWM_REG_LPR);
DBG("lpr=0x%x\n", val);
}
writel_relaxed(PWM_CH3_INT, ddata->base + PWM_REG_INTSTS);
if (ddata->state == RMC_PRELOAD)
wake_lock_timeout(&ddata->remotectl_wake_lock, HZ);
return IRQ_HANDLED;
}
return IRQ_NONE;
}
static int rk_pwm_remotectl_hw_init(struct rkxx_remotectl_drvdata *ddata)
{
int val;
val = readl_relaxed(ddata->base + PWM_REG_CTRL);
val = (val & 0xFFFFFFFE) | PWM_DISABLE;
writel_relaxed(val, ddata->base + PWM_REG_CTRL);
val = readl_relaxed(ddata->base + PWM_REG_CTRL);
val = (val & 0xFFFFFFF9) | PWM_MODE_CAPTURE;
writel_relaxed(val, ddata->base + PWM_REG_CTRL);
val = readl_relaxed(ddata->base + PWM_REG_CTRL);
val = (val & 0xFF008DFF) | 0x00646200;
writel_relaxed(val, ddata->base + PWM_REG_CTRL);
val = readl_relaxed(ddata->base + PWM_REG_INT_EN);
val = (val & 0xFFFFFFF7) | PWM_CH3_INT_ENABLE;
writel_relaxed(val, ddata->base + PWM_REG_INT_EN);
val = readl_relaxed(ddata->base + PWM_REG_CTRL);
val = (val & 0xFFFFFFFE) | PWM_ENABLE;
writel_relaxed(val, ddata->base + PWM_REG_CTRL);
return 0;
}
static int rk_pwm_probe(struct platform_device *pdev)
{
struct rkxx_remotectl_drvdata *ddata;
struct resource *r;
struct input_dev *input;
struct clk *clk;
int num;
int irq;
int ret;
int i, j;
DBG(".. rk pwm remotectl v1.1 init\n");
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r) {
dev_err(&pdev->dev, "no memory resources defined\n");
return -ENODEV;
}
ddata = devm_kzalloc(&pdev->dev, sizeof(struct rkxx_remotectl_drvdata),
GFP_KERNEL);
if (!ddata) {
dev_err(&pdev->dev, "failed to allocate memory\n");
return -ENOMEM;
}
ddata->state = RMC_PRELOAD;
ddata->base = devm_ioremap_resource(&pdev->dev, r);
if (IS_ERR(ddata->base))
return PTR_ERR(ddata->base);
clk = devm_clk_get(&pdev->dev, "pclk_pwm");
if (IS_ERR(clk))
return PTR_ERR(clk);
platform_set_drvdata(pdev, ddata);
input = input_allocate_device();
input->name = pdev->name;
input->phys = "gpio-keys/remotectl";
input->dev.parent = &pdev->dev;
input->id.bustype = BUS_HOST;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0100;
ddata->input = input;
ddata->input = input;
wake_lock_init(&ddata->remotectl_wake_lock,
WAKE_LOCK_SUSPEND, "rk29_pwm_remote");
ret = clk_prepare_enable(clk);
if (ret)
return ret;
irq = platform_get_irq(pdev, 0);
if (ret < 0) {
dev_err(&pdev->dev, "cannot find IRQ\n");
return ret;
}
ddata->irq = irq;
ddata->wakeup = 1;
tasklet_init(&ddata->remote_tasklet, rk_pwm_remotectl_do_something,
(unsigned long)ddata);
num = sizeof(remotectl_button)/sizeof(struct rkxx_remotectl_button);
for (j = 0; j < num; j++) {
DBG("remotectl probe j = 0x%x\n", j);
for (i = 0; i < remotectl_button[j].nbuttons; i++) {
unsigned int type = EV_KEY;
input_set_capability(input, type, remotectl_button[j].
key_table[i].keycode);
}
}
ret = input_register_device(input);
if (ret)
pr_err("remotectl: register input device err, ret: %d\n", ret);
input_set_capability(input, EV_KEY, KEY_WAKEUP);
device_init_wakeup(&pdev->dev, 1);
ret = devm_request_irq(&pdev->dev, irq, rockchip_pwm_irq,
0, "rk_pwm_irq", ddata);
if (ret) {
dev_err(&pdev->dev, "cannot claim IRQ %d\n", irq);
return ret;
}
enable_irq_wake(irq);
setup_timer(&ddata->timer, rk_pwm_remotectl_timer,
(unsigned long)ddata);
mod_timer(&ddata->timer, jiffies + msecs_to_jiffies(1000));
rk_pwm_remotectl_hw_init(ddata);
return ret;
}
static int rk_pwm_remove(struct platform_device *pdev)
{
return 0;
}
static const struct of_device_id rk_pwm_of_match[] = {
{ .compatible = "rockchip,remotectl-pwm"},
{ }
};
MODULE_DEVICE_TABLE(of, rk_pwm_of_match);
static struct platform_driver rk_pwm_driver = {
.driver = {
.name = "remotectl-pwm",
.of_match_table = rk_pwm_of_match,
},
.probe = rk_pwm_probe,
.remove = rk_pwm_remove,
};
module_platform_driver(rk_pwm_driver);
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,128 @@
#ifndef __RKXX_PWM_REMOTECTL_H__
#define __RKXX_PWM_REMOTECTL_H__
#include <linux/input.h>
/* PWM0 registers */
#define PWM_REG_CNTR 0x00 /* Counter Register */
#define PWM_REG_HPR 0x04 /* Period Register */
#define PWM_REG_LPR 0x08 /* Duty Cycle Register */
#define PWM_REG_CTRL 0x0c /* Control Register */
#define PWM_REG_INTSTS 0x10 /* Interrupt Status Refister */
#define PWM_REG_INT_EN 0x14 /* Interrupt Enable Refister */
/*REG_CTRL bits definitions*/
#define PWM_ENABLE (1 << 0)
#define PWM_DISABLE (0 << 0)
/*operation mode*/
#define PWM_MODE_ONESHOT (0x00 << 1)
#define PWM_MODE_CONTINUMOUS (0x01 << 1)
#define PWM_MODE_CAPTURE (0x02 << 1)
/*duty cycle output polarity*/
#define PWM_DUTY_POSTIVE (0x01 << 3)
#define PWM_DUTY_NEGATIVE (0x00 << 3)
/*incative state output polarity*/
#define PWM_INACTIVE_POSTIVE (0x01 << 4)
#define PWM_INACTIVE_NEGATIVE (0x00 << 4)
/*clock source select*/
#define PWM_CLK_SCALE (1 << 9)
#define PWM_CLK_NON_SCALE (0 << 9)
#define PWM_CH0_INT (1 << 0)
#define PWM_CH1_INT (1 << 1)
#define PWM_CH2_INT (1 << 2)
#define PWM_CH3_INT (1 << 3)
#define PWM_CH0_POL (1 << 8)
#define PWM_CH1_POL (1 << 9)
#define PWM_CH2_POL (1 << 10)
#define PWM_CH3_POL (1 << 11)
#define PWM_CH0_INT_ENABLE (1 << 0)
#define PWM_CH0_INT_DISABLE (0 << 0)
#define PWM_CH1_INT_ENABLE (1 << 0)
#define PWM_CH1_INT_DISABLE (0 << 1)
#define PWM_CH2_INT_ENABLE (1 << 2)
#define PWM_CH2_INT_DISABLE (0 << 2)
#define PWM_CH3_INT_ENABLE (1 << 3)
#define PWM_CH3_INT_DISABLE (0 << 3)
/*prescale factor*/
#define PWMCR_MIN_PRESCALE 0x00
#define PWMCR_MAX_PRESCALE 0x07
#define PWMDCR_MIN_DUTY 0x0001
#define PWMDCR_MAX_DUTY 0xFFFF
#define PWMPCR_MIN_PERIOD 0x0001
#define PWMPCR_MAX_PERIOD 0xFFFF
#define PWMPCR_MIN_PERIOD 0x0001
#define PWMPCR_MAX_PERIOD 0xFFFF
enum pwm_div {
PWM_DIV1 = (0x0 << 12),
PWM_DIV2 = (0x1 << 12),
PWM_DIV4 = (0x2 << 12),
PWM_DIV8 = (0x3 << 12),
PWM_DIV16 = (0x4 << 12),
PWM_DIV32 = (0x5 << 12),
PWM_DIV64 = (0x6 << 12),
PWM_DIV128 = (0x7 << 12),
};
/********************************************************************
** <20><EFBFBD><EAB6A8> *
********************************************************************/
#define RK_PWM_TIME_PRE_MIN 19 /*4500*/
#define RK_PWM_TIME_PRE_MAX 30 /*5500*/ /*PreLoad 4.5+0.56 = 5.06ms*/
#define RK_PWM_TIME_BIT0_MIN 1 /*Bit0 1.125ms*/
#define RK_PWM_TIME_BIT0_MAX 5
#define RK_PWM_TIME_BIT1_MIN 7 /*Bit1 2.25ms*/
#define RK_PWM_TIME_BIT1_MAX 11
#define RK_PWM_TIME_RPT_MIN 200 /*101000*/
#define RK_PWM_TIME_RPT_MAX 250 /*103000*/ /*Repeat 105-2.81=102.19ms*/ //110-9-2.25-0.56=98.19ms
#define RK_PWM_TIME_SEQ1_MIN 8 /*2650*/
#define RK_PWM_TIME_SEQ1_MAX 12 /*3000*/ /*sequence 2.25+0.56=2.81ms*/ //11.25ms
#define RK_PWM_TIME_SEQ2_MIN 450 /*101000*/
#define RK_PWM_TIME_SEQ2_MAX 500 /*103000*/ /*Repeat 105-2.81=102.19ms*/ //110-9-2.25-0.56=98.19ms
/********************************************************************
** <20><EFBFBD><E1B9B9><EFBFBD><EFBFBD> *
********************************************************************/
typedef enum _RMC_STATE
{
RMC_IDLE,
RMC_PRELOAD,
RMC_USERCODE,
RMC_GETDATA,
RMC_SEQUENCE
}eRMC_STATE;
struct RKxx_remotectl_platform_data {
//struct rkxx_remotectl_button *buttons;
int nbuttons;
int rep;
int timer;
int wakeup;
};
#endif