On 12/22/2015 00:18, [email protected] wrote:
Hello, do you still have that patch?
Haven't cleaned it up but you can find the sunxi lirc driver attached.
Just drop it in drivers/input/keyboard/ and add it to the makefile.
Please note that I've only tested it on Mixtile(A31 SoC), other SoCs
seem to have some differences in configuring the IR device.
If it's of any interest I can clean it up and submit it as a proper patch.
Regards
--
You received this message because you are subscribed to the Google Groups
"linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.
/****************************************************************
*
*VERSION 1.0 Inital Version
*note: when swith to real ic, need to define SYS_CLK_CFG_EN & undef FPGA_SIM_CONFIG, vice versa.
*****************************************************************/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/keyboard.h>
#include <linux/ioport.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <mach/clock.h>
#include <linux/gpio.h>
#include <linux/scenelock.h>
#include <linux/power/aw_pm.h>
#include <mach/sys_config.h>
#include <media/lirc.h>
#include <media/lirc_dev.h>
#include <media/rc-core.h>
#define LIRC_DRIVER_NAME "sunxi_lirc"
#define RBUF_LEN 512
static struct platform_device *lirc_sunxi_dev;
#include <mach/irqs.h>
#include <mach/hardware.h>
#include <linux/clk.h>
#include <mach/gpio.h>
#include <mach/ar100.h>
#undef CONFIG_HAS_EARLYSUSPEND
#ifdef CONFIG_HAS_EARLYSUSPEND
#include <linux/earlysuspend.h>
#endif
#if defined(CONFIG_HAS_EARLYSUSPEND) || defined(CONFIG_PM)
#include <linux/pm.h>
#endif
#include "ir-keymap.h"
#define SYS_GPIO_CFG_EN
#define SYS_CLK_CFG_EN
#define SW_INT_IRQNO_IR0 (69)
#ifdef SYS_CLK_CFG_EN
static struct clk *ir_clk;
static struct clk *ir_clk_source;
#endif
#ifdef SYS_GPIO_CFG_EN
static struct gpio_hdle {
script_item_u val;
script_item_value_type_e type;
}ir_gpio_hdle;
#endif
#define CIR_SAMPLE_PERIOD
/* Registers */
#define IR_REG(x) (x)
#define IR0_BASE (0xf1f02000)
#define IR_BASE IR0_BASE
#define IR_IRQNO (SW_INT_IRQNO_IR0)
/* CCM register */
#define CCM_BASE 0xf1c20000
/* PIO register */
#define PI_BASE 0xf1c20800
#define IR_CTRL_REG IR_REG(0x00) /* IR Control */
#define IR_RXCFG_REG IR_REG(0x10) /* Rx Config */
#define IR_RXDAT_REG IR_REG(0x20) /* Rx Data */
#define IR_RXINTE_REG IR_REG(0x2C) /* Rx Interrupt Enable */
#define IR_RXINTS_REG IR_REG(0x30) /* Rx Interrupt Status */
#define IR_SPLCFG_REG IR_REG(0x34) /* IR Sample Config */
//Bit Definition of IR_RXINTS_REG Register
#define IR_RXINTS_RXOF (0x1<<0) /* Rx FIFO Overflow */
#define IR_RXINTS_RXPE (0x1<<1) /* Rx Packet End */
#define IR_RXINTS_RXDA (0x1<<4) /* Rx FIFO Data Available */
/* Helper */
#define PULSE_BIT_SHIFTER (17) /* from 0x80 to PULSE_BIT */
#define SAMPLES_TO_US(us) (( ((unsigned long) us) * 1000000UL)/46875UL)
#define IR_FIFO_SIZE (64) /* 64Bytes */
/* Frequency of Sample Clock = 46875.Hz, Cycle is 21.3us */
#define IR_RXFILT_VAL (8) /* Filter Threshold = 8*21.3 = ~128us < 200us */
#define IR_RXIDLE_VAL (5) /* Idle Threshold = (5+1)*128*21.3 = ~16.4ms > 9ms */
//#define IR_RXFILT_VAL (16) /* Filter Threshold = 8*42.7 = ~341us < 500us */
//#define IR_RXIDLE_VAL (5) /* Idle Threshold = (2+1)*128*42.7 = ~16.4ms > 9ms */
#define IR_ACTIVE_T (99) /* Active Threshold */
#define IR_ACTIVE_T_C (0) /* Active Threshold */
#define IR_ERROR_CODE (0xffffffff)
#define IR_REPEAT_CODE (0x00000000)
#define DRV_VERSION "1.00"
#define REPORT_REPEAT_KEY_VALUE
#ifdef CONFIG_HAS_EARLYSUSPEND
struct sun6i_ir_data {
struct early_suspend early_suspend;
};
#endif
struct ir_raw_buffer {
unsigned long dcnt; /*Packet Count*/
#define IR_RAW_BUF_SIZE 512
unsigned char buf[IR_RAW_BUF_SIZE];
};
static unsigned int ir_cnt = 0;
static unsigned long ir_code=0;
static int timer_used=0;
static struct ir_raw_buffer ir_rawbuf;
static u32 power_key = 0;
extern unsigned int normal_standby_wakesource;
static u32 ir_addr_code = 0x9f00;
DEFINE_SPINLOCK(sunxi_lirc_spinlock);
#ifdef CONFIG_HAS_EARLYSUSPEND
static struct sun6i_ir_data *ir_data;
#else
#ifdef CONFIG_PM
struct dev_pm_domain ir_pm_domain;
#endif
#endif
enum {
DEBUG_INIT = 1U << 0,
DEBUG_INT = 1U << 1,
DEBUG_DATA_INFO = 1U << 2,
DEBUG_SUSPEND = 1U << 3,
};
static u32 debug_mask = 0;
#define dprintk(level_mask, fmt, arg...) if (unlikely(debug_mask & level_mask)) \
printk(KERN_DEBUG fmt , ## arg)
module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
static inline void ir_reset_rawbuffer(void)
{
ir_rawbuf.dcnt = 0;
}
static inline void ir_write_rawbuffer(unsigned char data)
{
dprintk(DEBUG_DATA_INFO, "ir_write_rawbuffer: %02x\n", data);
if (ir_rawbuf.dcnt < IR_RAW_BUF_SIZE)
ir_rawbuf.buf[ir_rawbuf.dcnt++] = data;
else
printk(KERN_DEBUG "ir_write_rawbuffer: IR Rx Buffer Full!!\n");
}
static inline unsigned char ir_read_rawbuffer(void)
{
unsigned char data = 0x00;
if (ir_rawbuf.dcnt > 0)
data = ir_rawbuf.buf[--ir_rawbuf.dcnt];
return data;
}
static inline int ir_rawbuffer_empty(void)
{
return (ir_rawbuf.dcnt == 0);
}
static inline int ir_rawbuffer_full(void)
{
return (ir_rawbuf.dcnt >= IR_RAW_BUF_SIZE);
}
static void ir_clk_cfg(void)
{
#ifdef SYS_CLK_CFG_EN
unsigned long rate = 3000000; /* 3Mhz */
#else
unsigned long tmp = 0;
#endif
#ifdef SYS_CLK_CFG_EN
ir_clk_source = clk_get(NULL, CLK_SYS_HOSC);
if (!ir_clk_source || IS_ERR(ir_clk_source)) {
printk(KERN_DEBUG "try to get ir_clk_source clock failed!\n");
return;
}
rate = clk_get_rate(ir_clk_source);
dprintk(DEBUG_INIT, "%s: get ir_clk_source rate %dHZ\n", __func__, (__u32)rate);
ir_clk = clk_get(NULL, CLK_MOD_R_CIR);
if (!ir_clk || IS_ERR(ir_clk)) {
printk(KERN_DEBUG "try to get ir clock failed!\n");
return;
}
if(clk_set_parent(ir_clk, ir_clk_source))
printk("%s: set ir_clk parent to ir_clk_source failed!\n", __func__);
if (clk_set_rate(ir_clk, 3000000)) {
printk(KERN_DEBUG "set ir clock freq to 3M failed!\n");
}
if (clk_enable(ir_clk)) {
printk(KERN_DEBUG "try to enable ir_clk failed!\n");
}
if (clk_reset(ir_clk, AW_CCU_CLK_NRESET)) {
printk(KERN_DEBUG "try to nreset ir_clk failed!\n");
}
#else
/* Enable APB Clock for IR */
tmp = readl(CCM_BASE + 0x10);
tmp |= 0x1<<10; //IR
writel(tmp, CCM_BASE + 0x10);
/* config Special Clock for IR (24/8=3MHz) */
tmp = readl(CCM_BASE + 0x34);
tmp &= ~(0x3<<8);
tmp |= (0x1<<8); /* Select 24MHz */
tmp |= (0x1<<7); /* Open Clock */
tmp &= ~(0x3f<<0);
tmp |= (7<<0); /* Divisor = 8 */
writel(tmp, CCM_BASE + 0x34);
#endif
return;
}
static void ir_clk_uncfg(void)
{
#ifdef SYS_CLK_CFG_EN
if(NULL == ir_clk || IS_ERR(ir_clk)) {
printk("ir_clk handle is invalid, just return!\n");
return;
} else {
clk_disable(ir_clk);
clk_put(ir_clk);
ir_clk = NULL;
}
if(NULL == ir_clk_source || IS_ERR(ir_clk_source)) {
printk("ir_clk_source handle is invalid, just return!\n");
return;
} else {
clk_put(ir_clk_source);
ir_clk_source = NULL;
}
#else
#endif
return;
}
static void ir_sys_cfg(void)
{
#ifdef SYS_GPIO_CFG_EN
ir_gpio_hdle.type = script_get_item("ir_para", "ir_rx", &(ir_gpio_hdle.val));
if(SCIRPT_ITEM_VALUE_TYPE_PIO != ir_gpio_hdle.type)
printk(KERN_ERR "IR gpio type err! \n");
dprintk(DEBUG_INIT, "value is: gpio %d, mul_sel %d, pull %d, drv_level %d, data %d\n",
ir_gpio_hdle.val.gpio.gpio, ir_gpio_hdle.val.gpio.mul_sel, ir_gpio_hdle.val.gpio.pull,
ir_gpio_hdle.val.gpio.drv_level, ir_gpio_hdle.val.gpio.data);
if(0 != gpio_request(ir_gpio_hdle.val.gpio.gpio, NULL)) {
printk(KERN_ERR "ERROR: IR Gpio_request is failed\n");
}
if (0 != sw_gpio_setall_range(&ir_gpio_hdle.val.gpio, 1)) {
printk(KERN_ERR "IR gpio set err!");
goto end;
}
#else
unsigned long tmp;
/* config IO: PIOB4 to IR_Rx */
tmp = readl(PI_BASE + 0x24); /* PIOB_CFG0_REG */
tmp &= ~(0xf<<16);
tmp |= (0x2<<16);
writel(tmp, PI_BASE + 0x24);
#endif
ir_clk_cfg();
return;
#ifdef SYS_GPIO_CFG_EN
end:
gpio_free(ir_gpio_hdle.val.gpio.gpio);
return;
#endif
}
static void ir_sys_uncfg(void)
{
#ifdef SYS_GPIO_CFG_EN
gpio_free(ir_gpio_hdle.val.gpio.gpio);
#else
#endif
ir_clk_uncfg();
return;
}
static void ir_reg_cfg(void)
{
unsigned long tmp = 0;
/* Enable IR Mode */
tmp = 0x3<<4;
writel(tmp, IR_BASE+IR_CTRL_REG);
/* Config IR Smaple Register */
tmp = 0x0 << 0; /* Fsample = 3MHz/64 =46875Hz (21.3us) */
tmp |= (IR_RXFILT_VAL&0x3f)<<2; /* Set Filter Threshold */
tmp |= (IR_RXIDLE_VAL&0xff)<<8; /* Set Idle Threshold */
tmp |= (IR_ACTIVE_T&0xff)<<16; /* Set Active Threshold */
tmp |= (IR_ACTIVE_T_C&0xff)<<23;
writel(tmp, IR_BASE+IR_SPLCFG_REG);
/* Invert Input Signal */
writel(0x1<<2, IR_BASE+IR_RXCFG_REG);
/* Clear All Rx Interrupt Status */
writel(0xff, IR_BASE+IR_RXINTS_REG);
/* Set Rx Interrupt Enable */
tmp = (0x1<<4)|0x3;
//tmp |= ((IR_FIFO_SIZE>>1)-1)<<8; /* Rx FIFO Threshold = FIFOsz/2; */
tmp |= ((IR_FIFO_SIZE>>2)-1)<<8; /* Rx FIFO Threshold = FIFOsz/4; */
writel(tmp, IR_BASE+IR_RXINTE_REG);
/* Enable IR Module */
tmp = readl(IR_BASE+IR_CTRL_REG);
tmp |= 0x3;
writel(tmp, IR_BASE+IR_CTRL_REG);
return;
}
static void ir_setup(void)
{
dprintk(DEBUG_INIT, "ir_setup: ir setup start!!\n");
ir_code = 0;
timer_used = 0;
ir_reset_rawbuffer();
ir_sys_cfg();
ir_reg_cfg();
dprintk(DEBUG_INIT, "ir_setup: ir setup end!!\n");
return;
}
static inline unsigned char ir_get_data(void)
{
return (unsigned char)(readl(IR_BASE + IR_RXDAT_REG));
}
static inline unsigned long ir_get_intsta(void)
{
return (readl(IR_BASE + IR_RXINTS_REG));
}
static inline void ir_clr_intsta(unsigned long bitmap)
{
unsigned long tmp = readl(IR_BASE + IR_RXINTS_REG);
tmp &= ~0xff;
tmp |= bitmap&0xff;
writel(tmp, IR_BASE + IR_RXINTS_REG);
}
void ir_packet_handler(unsigned char *buf, unsigned int dcnt)
{
DEFINE_IR_RAW_EVENT(rawir);
struct rc_dev *rdev = lirc_sunxi_dev->dev.platform_data;
unsigned int i;
unsigned int lirc_val;
bool pulse;
dprintk(DEBUG_DATA_INFO, "Buffer length: %d",dcnt);
for(i = 0; i < dcnt; i++) {
pulse = (buf[i] & 0x80) != 0;
lirc_val= buf[i] & 0x7f;
#if 0
/* to do, write to lirc buffer */
if (lirc_buffer_full(&rbuf)) {
/* no new signals will be accepted */
dprintk(DEBUG_DATA_INFO, "Buffer overrun\n");
return;
}
#endif
init_ir_raw_event(&rawir);
rawir.pulse = pulse;
rawir.duration = US_TO_NS(SAMPLES_TO_US(lirc_val));
ir_raw_event_store_with_filter(rdev, &rawir);
}
ir_raw_event_handle(rdev);
return;
}
static irqreturn_t ir_irq_service(int irqno, void *dev_id)
{
unsigned int dcnt;
unsigned int i = 0;
unsigned long intsta;
intsta = ir_get_intsta();
ir_clr_intsta(intsta);
dprintk(DEBUG_DATA_INFO, "ir_irq_service: interrupt\n");
/* Read Data Every Time Enter this Routine*/
dcnt = (unsigned int) (ir_get_intsta() >> 8) & 0x3f;
/* Read FIFO */
for (i = 0; i < dcnt; i++) {
if (ir_rawbuffer_full()) {
dprintk(DEBUG_DATA_INFO, "ir_irq_service: raw buffer full\n");
break;
} else {
ir_write_rawbuffer(ir_get_data());
}
}
if (intsta & IR_RXINTS_RXPE) { /* Packet End */
ir_packet_handler(ir_rawbuf.buf, ir_rawbuf.dcnt);
dprintk(DEBUG_DATA_INFO, "Buffer written\n");
ir_rawbuf.dcnt = 0;
}
if (intsta & IR_RXINTS_RXOF) {/* FIFO Overflow */
/* flush raw buffer */
ir_reset_rawbuffer();
dprintk(DEBUG_DATA_INFO, "ir_irq_service: Rx FIFO Overflow!!\n");
}
return IRQ_HANDLED;
}
static void ir_get_powerkey()
{
script_item_u script_val;
script_item_value_type_e type;
type = script_get_item("ir_para", "ir_power_key_code", &script_val);
if (SCIRPT_ITEM_VALUE_TYPE_INT != type) {
printk("######### ir power key code config type err! ######");
return;
}
power_key = script_val.val;
//get ir addr code
type = script_get_item("ir_para", "ir_addr_code", &script_val);
if (SCIRPT_ITEM_VALUE_TYPE_INT != type) {
printk("######### ir ir_addr_code config type err! ######");
return;
}
ir_addr_code = script_val.val;
printk("ir_addr_code = 0x%x power_key=0x%x\n ",ir_addr_code,power_key);
}
/* Í£ÓÃÉ豸 */
#ifdef CONFIG_HAS_EARLYSUSPEND
static void sun6i_ir_early_suspend(struct early_suspend *h)
{
//unsigned long tmp = 0;
//int ret;
//struct sun6i_ir_data *ts = container_of(h, struct sun6i_ir_data, early_suspend);
dprintk(DEBUG_SUSPEND, "enter earlysuspend: sun6i_ir_suspend. \n");
//tmp = readl(IR_BASE+IR_CTRL_REG);
//tmp &= 0xfffffffc;
//writel(tmp, IR_BASE+IR_CTRL_REG);
#ifdef SYS_CLK_CFG_EN
if(NULL == ir_clk || IS_ERR(ir_clk)) {
printk("ir_clk handle is invalid, just return!\n");
return;
} else {
clk_disable(ir_clk);
}
#endif
return ;
}
/* ÖØÐ»½ÐÑ */
static void sun6i_ir_late_resume(struct early_suspend *h)
{
//unsigned long tmp = 0;
//int ret;
//struct sun6i_ir_data *ts = container_of(h, struct sun6i_ir_data, early_suspend);
dprintk(DEBUG_SUSPEND, "enter laterresume: sun6i_ir_resume. \n");
ir_code = 0;
timer_used = 0;
ir_reset_rawbuffer();
ir_clk_cfg();
ir_reg_cfg();
return ;
}
#else
#ifdef CONFIG_PM
static int sun6i_ir_suspend(struct device *dev)
{
dprintk(DEBUG_SUSPEND, "enter earlysuspend: sun6i_ir_suspend. \n");
//tmp = readl(IR_BASE+IR_CTRL_REG);
//tmp &= 0xfffffffc;
//writel(tmp, IR_BASE+IR_CTRL_REG);
#ifdef SYS_CLK_CFG_EN
if(NULL == ir_clk || IS_ERR(ir_clk)) {
printk("ir_clk handle is invalid, just return!\n");
return -1;
} else {
clk_disable(ir_clk);
}
#endif
return 0;
}
/* ÖØÐ»½ÐÑ */
static int sun6i_ir_resume(struct device *dev)
{
unsigned long ir_event = 0;
dprintk(DEBUG_SUSPEND, "enter laterresume: sun6i_ir_resume. \n");
ir_code = 0;
timer_used = 0;
ir_reset_rawbuffer();
ir_clk_cfg();
ir_reg_cfg();
ar100_query_wakeup_source(&ir_event);
dprintk(DEBUG_SUSPEND, "%s: event 0x%lx\n", __func__, ir_event);
return 0;
}
#endif
#endif
static int set_use_inc(void* data) {
return 0;
}
static void set_use_dec(void* data) {
}
/* end of lirc device/driver stuff */
/* now comes THIS driver, above is lirc */
static struct platform_driver lirc_sunxi_driver = {
.driver = {
.name = LIRC_DRIVER_NAME,
.owner = THIS_MODULE,
},
};
static int __init ir_init(void)
{
struct rc_dev *rdev;
int i,ret;
int err = 0;
/* input device for IR remote (and tx) */
rdev = rc_allocate_device();
if (!rdev)
return -ENOMEM;
err = platform_driver_register(&lirc_sunxi_driver);
if (err) {
printk(KERN_ERR LIRC_DRIVER_NAME
": lirc register returned %d\n", err);
goto exit_buffer_free;
}
lirc_sunxi_dev = platform_device_alloc(LIRC_DRIVER_NAME, 0);
if (!lirc_sunxi_dev) {
err = -ENOMEM;
goto exit_driver_unregister;
}
err = platform_device_add(lirc_sunxi_dev);
if (err) {
platform_device_put(lirc_sunxi_dev);
goto exit_driver_unregister;
}
if (request_irq(IR_IRQNO, ir_irq_service, 0, "RemoteIR",
lirc_sunxi_dev)) {
err = -EBUSY;
goto exit_device_unregister;
}
lirc_sunxi_dev->dev.platform_data = rdev;
ir_setup();
#ifdef CONFIG_HAS_EARLYSUSPEND
#else
#ifdef CONFIG_PM
ir_pm_domain.ops.suspend = sun6i_ir_suspend;
ir_pm_domain.ops.resume = sun6i_ir_resume;
lirc_sunxi_dev->dev.pm_domain = &ir_pm_domain;
#endif
#endif
/* Set up the rc device */
rdev->priv = lirc_sunxi_dev;
rdev->driver_type = RC_DRIVER_IR_RAW;
rdev->allowed_protos = RC_TYPE_ALL;
rdev->input_name = LIRC_DRIVER_NAME;
rdev->input_phys = "sunxi/cir0";
rdev->input_id.bustype = BUS_HOST;
rdev->input_id.version = 1;
rdev->dev.parent = &lirc_sunxi_dev->dev;
rdev->driver_name = LIRC_DRIVER_NAME;
rdev->map_name = RC_MAP_RC6_MCE;
rdev->timeout = US_TO_NS(1000);
/* rx resolution is hardwired to 50us atm, 1, 25, 100 also possible */
rdev->rx_resolution = SAMPLES_TO_US(1);
err = rc_register_device(rdev);
if (err)
goto exit_free_irq;
printk(KERN_INFO LIRC_DRIVER_NAME ": driver registered!\n");
printk("IR Initial OK\n");
#ifdef CONFIG_HAS_EARLYSUSPEND
dprintk(DEBUG_INIT, "==register_early_suspend =\n");
ir_data = kzalloc(sizeof(*ir_data), GFP_KERNEL);
if (ir_data == NULL) {
err = -ENOMEM;
goto err_alloc_data_failed;
}
ir_data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
ir_data->early_suspend.suspend = sun6i_ir_early_suspend;
ir_data->early_suspend.resume = sun6i_ir_late_resume;
register_early_suspend(&ir_data->early_suspend);
#endif
dprintk(DEBUG_INIT, "ir_init end\n");
return 0;
#ifdef CONFIG_HAS_EARLYSUSPEND
err_alloc_data_failed:
#endif
exit_free_irq:
free_irq(IR_IRQNO, NULL);
exit_device_unregister:
platform_device_unregister(lirc_sunxi_dev);
exit_driver_unregister:
platform_driver_unregister(&lirc_sunxi_driver);
exit_buffer_free:
rc_free_device(rdev);
return err;
}
static void __exit ir_exit(void)
{
struct rc_dev *rdev = lirc_sunxi_dev->dev.platform_data;
#ifdef CONFIG_HAS_EARLYSUSPEND
unregister_early_suspend(&ir_data->early_suspend);
#endif
free_irq(IR_IRQNO, lirc_sunxi_dev);
ir_sys_uncfg();
rc_free_device(rdev);
platform_device_unregister(lirc_sunxi_dev);
platform_driver_unregister(&lirc_sunxi_driver);
printk(KERN_INFO LIRC_DRIVER_NAME ": cleaned up module\n");
}
module_init(ir_init);
module_exit(ir_exit);
MODULE_DESCRIPTION("Remote IR driver");
MODULE_AUTHOR("DanielWang");
MODULE_LICENSE("GPL");