build/patch/kernel/sun4i-default/0050-lircgpio_and_raw.patch

1608 lines
46 KiB
Diff
Raw Normal View History

diff --git a/drivers/staging/media/lirc/Kconfig b/drivers/staging/media/lirc/Kconfig
index 526ec0f..8d7971d 100644
--- a/drivers/staging/media/lirc/Kconfig
+++ b/drivers/staging/media/lirc/Kconfig
@@ -69,6 +69,21 @@ config LIRC_TTUSBIR
help
Driver for the Technotrend USB IR Receiver
+config LIRC_GPIO
+ tristate "LIRC GPIO receiver/transmitter"
+ depends on LIRC && GPIOLIB
+ help
+ Driver for GPIOLIB enabled gpio chip. Module setup for and tested
+ on Allwinner A10 Gpio SOC.
+ change/redefine LIRC_GPIO_ID_STRING to the correct gpiochip label
+
+config LIRC_SUNXI_RAW
+ tristate "LIRC for sunxi CIR interface"
+ depends on LIRC
+ help
+ Driver for Allwinner's native CIR interface, passing
+ data (almost) unfiltered to LIRC device
+
config LIRC_ZILOG
tristate "Zilog/Hauppauge IR Transmitter"
depends on LIRC && I2C
diff --git a/drivers/staging/media/lirc/Makefile b/drivers/staging/media/lirc/Makefile
index d76b0fa..65af5e6 100644
--- a/drivers/staging/media/lirc/Makefile
+++ b/drivers/staging/media/lirc/Makefile
@@ -11,4 +11,6 @@ obj-$(CONFIG_LIRC_SASEM) += lirc_sasem.o
obj-$(CONFIG_LIRC_SERIAL) += lirc_serial.o
obj-$(CONFIG_LIRC_SIR) += lirc_sir.o
obj-$(CONFIG_LIRC_TTUSBIR) += lirc_ttusbir.o
+obj-$(CONFIG_LIRC_GPIO) += lirc_gpio.o
+obj-$(CONFIG_LIRC_SUNXI_RAW) += sunxi-lirc.o
obj-$(CONFIG_LIRC_ZILOG) += lirc_zilog.o
diff --git a/drivers/staging/media/lirc/lirc_gpio.c b/drivers/staging/media/lirc/lirc_gpio.c
new file mode 100644
index 0000000..10f5129
--- /dev/null
+++ b/drivers/staging/media/lirc/lirc_gpio.c
@@ -0,0 +1,968 @@
+/*
+ * lirc_gpio.c
+ *
+ * lirc_gpio - Device driver that records pulse- and pause-lengths
+ * (space-lengths) (just like the lirc_serial driver does)
+ * between GPIO interrupt events. Tested on a Cubieboard with Allwinner A10
+ * However, everything relies on the gpiolib.c module, so there is a good
+ * chance it will also run on other platforms.
+ * Lots of code has been taken from the lirc_rpi module, who in turn took a
+ * lot of code from the lirc_serial module,
+ * so I would like say thanks to the authors.
+ *
+ * Copyright (C) 2013 Matthias H<><48>lling <mhoel....@gmail.nospam.com>,
+ *
+ * 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/module.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/spinlock.h>
+#include <media/lirc.h>
+#include <media/lirc_dev.h>
+#include <linux/gpio.h>
+#include <plat/sys_config.h>
+#include <../drivers/gpio/gpio-sunxi.h>
+
+#define LIRC_DRIVER_NAME "lirc_gpio"
+/* this may have to be adapted for different platforms */
+#define LIRC_GPIO_ID_STRING "A1X_GPIO"
+#define RBUF_LEN 256
+#define LIRC_TRANSMITTER_LATENCY 256
+
+#ifndef MAX_UDELAY_MS
+#define MAX_UDELAY_US 5000
+#else
+#define MAX_UDELAY_US (MAX_UDELAY_MS*1000)
+#endif
+
+#define RX_OFFSET_GPIOCHIP gpio_in_pin - gpiochip->base
+#define TX_OFFSET_GPIOCHIP gpio_out_pin - gpiochip->base
+
+#define dprintk(fmt, args...) \
+do { \
+if (debug) \
+printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \
+fmt, ## args); \
+} while (0)
+
+/* module parameters */
+
+/* set the default GPIO input pins, 3 channels */
+static int gpio_in_pin = 0;
+/* -1 = auto, 0 = active high, 1 = active low */
+static int sense = -1;
+static struct timeval lasttv = { 0, 0 };
+static spinlock_t lock;
+
+/* set the default GPIO output pin, 4 channels */
+static int gpio_out_pin = 0;
+/* enable debugging messages */
+static int debug;
+/* use softcarrier by default */
+static int softcarrier = 1;
+/* 0 = do not invert output, 1 = invert output */
+static int invert = 0;
+
+/* is the device open, so interrupt must be changed if pins are changed */
+static int device_open = 0;
+
+struct gpio_chip *gpiochip=NULL;
+struct irq_chip *irqchip=NULL;
+struct irq_data *irqdata=NULL;
+
+/* forward declarations */
+static long send_pulse(unsigned long length);
+static void send_space(long length);
+static void lirc_gpio_exit(void);
+
+static struct platform_device *lirc_gpio_dev;
+static struct lirc_buffer rbuf;
+
+/* initialized/set in init_timing_params() */
+static unsigned int freq = 38000;
+static unsigned int duty_cycle = 50;
+static unsigned long period;
+static unsigned long pulse_width;
+static unsigned long space_width;
+
+
+/* stuff for TX pin */
+
+static void safe_udelay(unsigned long usecs)
+{
+ while (usecs > MAX_UDELAY_US) {
+ udelay(MAX_UDELAY_US);
+ usecs -= MAX_UDELAY_US;
+ }
+ udelay(usecs);
+}
+
+static int init_timing_params(unsigned int new_duty_cycle,
+ unsigned int new_freq)
+{
+ /*
+ * period, pulse/space width are kept with 8 binary places -
+ * IE multiplied by 256.
+ */
+ if (256 * 1000000L / new_freq * new_duty_cycle / 100 <=
+ LIRC_TRANSMITTER_LATENCY)
+ return -EINVAL;
+ if (256 * 1000000L / new_freq * (100 - new_duty_cycle) / 100 <=
+ LIRC_TRANSMITTER_LATENCY)
+ return -EINVAL;
+ duty_cycle = new_duty_cycle;
+ freq = new_freq;
+ period = 256 * 1000000L / freq;
+ pulse_width = period * duty_cycle / 100;
+ space_width = period - pulse_width;
+ /*printk(KERN_INFO "in init_timing_params, freq=%d pulse=%ld, "
+ "space=%ld\n", freq, pulse_width, space_width); */
+ return 0;
+}
+
+static long send_pulse_softcarrier(unsigned long length)
+{
+ int flag;
+ unsigned long actual, target, d;
+
+ length <<= 8;
+
+ actual = 0; target = 0; flag = 0;
+ while (actual < length) {
+ if (flag) {
+ gpiochip->set(gpiochip, TX_OFFSET_GPIOCHIP, invert);
+ target += space_width;
+ } else {
+ gpiochip->set(gpiochip, TX_OFFSET_GPIOCHIP, !invert);
+ target += pulse_width;
+ }
+ d = (target - actual -
+ LIRC_TRANSMITTER_LATENCY + 128) >> 8;
+ /*
+ * Note - we've checked in ioctl that the pulse/space
+ * widths are big enough so that d is > 0
+ */
+ udelay(d);
+ actual += (d << 8) + LIRC_TRANSMITTER_LATENCY;
+ flag = !flag;
+ }
+ return (actual-length) >> 8;
+}
+
+static long send_pulse(unsigned long length)
+{
+ if (length <= 0)
+ return 0;
+
+ if (softcarrier) {
+ return send_pulse_softcarrier(length);
+ } else {
+ gpiochip->set(gpiochip, TX_OFFSET_GPIOCHIP, !invert);
+ safe_udelay(length);
+ return 0;
+ }
+}
+
+
+static void send_space(long length)
+{
+ gpiochip->set(gpiochip, TX_OFFSET_GPIOCHIP, invert);
+ if (length <= 0)
+ return;
+ safe_udelay(length);
+}
+
+/* end of TX stuff */
+
+
+
+/* RX stuff: Handle interrupt and write vals to lirc buffer */
+
+static void rbwrite(int l)
+{
+ if (lirc_buffer_full(&rbuf)) {
+ /* no new signals will be accepted */
+ dprintk("Buffer overrun\n");
+ return;
+ }
+ lirc_buffer_write(&rbuf, (void *)&l);
+}
+
+static void frbwrite(int l)
+{
+ /* simple noise filter */
+ static int pulse, space;
+ static unsigned int ptr;
+
+ if (ptr > 0 && (l & PULSE_BIT)) {
+ pulse += l & PULSE_MASK;
+ if (pulse > 250) {
+ rbwrite(space);
+ rbwrite(pulse | PULSE_BIT);
+ ptr = 0;
+ pulse = 0;
+ }
+ return;
+ }
+ if (!(l & PULSE_BIT)) {
+ if (ptr == 0) {
+ if (l > 20000) {
+ space = l;
+ ptr++;
+ return;
+ }
+ } else {
+ if (l > 20000) {
+ space += pulse;
+ if (space > PULSE_MASK)
+ space = PULSE_MASK;
+ space += l;
+ if (space > PULSE_MASK)
+ space = PULSE_MASK;
+ pulse = 0;
+ return;
+ }
+ rbwrite(space);
+ rbwrite(pulse | PULSE_BIT);
+ ptr = 0;
+ pulse = 0;
+ }
+ }
+ rbwrite(l);
+}
+
+static irqreturn_t irq_handler(int i, void *blah, struct pt_regs *regs)
+{
+ struct timeval tv;
+ long deltv;
+ int data;
+ int signal;
+
+ /* use the GPIO signal level */
+ signal = gpiochip->get(gpiochip, RX_OFFSET_GPIOCHIP);
+
+ /* unmask the irq */
+ irqchip->irq_unmask(irqdata);
+
+ if (sense != -1) {
+ /* get current time */
+ do_gettimeofday(&tv);
+
+ /* calc time since last interrupt in microseconds */
+ deltv = tv.tv_sec-lasttv.tv_sec;
+ if (tv.tv_sec < lasttv.tv_sec ||
+ (tv.tv_sec == lasttv.tv_sec &&
+ tv.tv_usec < lasttv.tv_usec)) {
+ printk(KERN_WARNING LIRC_DRIVER_NAME
+ ": AIEEEE: your clock just jumped backwards\n");
+ printk(KERN_WARNING LIRC_DRIVER_NAME
+ ": %d %d %lx %lx %lx %lx\n", signal, sense,
+ tv.tv_sec, lasttv.tv_sec,
+ tv.tv_usec, lasttv.tv_usec);
+ data = PULSE_MASK;
+ } else if (deltv > 15) {
+ data = PULSE_MASK; /* really long time */
+ if (!(signal^sense)) {
+ /* sanity check */
+ printk(KERN_WARNING LIRC_DRIVER_NAME
+ ": AIEEEE: %d %d %lx %lx %lx %lx\n",
+ signal, sense, tv.tv_sec, lasttv.tv_sec,
+ tv.tv_usec, lasttv.tv_usec);
+ /*
+ * detecting pulse while this
+ * MUST be a space!
+ */
+ sense = sense ? 0 : 1;
+ }
+ } else {
+ data = (int) (deltv*1000000 +
+ (tv.tv_usec - lasttv.tv_usec));
+ }
+ frbwrite(signal^sense ? data : (data|PULSE_BIT));
+ lasttv = tv;
+ wake_up_interruptible(&rbuf.wait_poll);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/* end of rx stuff */
+
+
+/* setup pins, rx, tx, interrupts, active low/high.... */
+
+static void set_sense(void)
+{
+ int i,nlow, nhigh;
+ if (gpio_in_pin==0) {
+ return; // no rx
+ }
+ if (sense == -1) {
+ /* wait 1/10 sec for the power supply */
+ msleep(100);
+
+ /*
+ * probe 9 times every 0.04s, collect "votes" for
+ * active high/low
+ */
+ nlow = 0;
+ nhigh = 0;
+ for (i = 0; i < 9; i++) {
+ if (gpiochip->get(gpiochip, RX_OFFSET_GPIOCHIP))
+ nlow++;
+ else
+ nhigh++;
+ msleep(40);
+ }
+ sense = (nlow >= nhigh ? 1 : 0);
+ printk(KERN_INFO LIRC_DRIVER_NAME
+ ": auto-detected active %s receiver on GPIO pin %d\n",
+ sense ? "low" : "high", gpio_in_pin);
+ } else {
+ printk(KERN_INFO LIRC_DRIVER_NAME
+ ": manually/previously detected using active %s receiver on GPIO pin %d\n",
+ sense ? "low" : "high", gpio_in_pin);
+ }
+
+}
+
+
+
+static int setup_tx(int new_out_pin)
+{
+ int ret;
+ user_gpio_set_t* pinstate;
+ struct sunxi_gpio_chip* sgpio = container_of(gpiochip,struct sunxi_gpio_chip,chip);
+ dprintk("addresses: gpiochip: %lx, sgpio: %lx",(unsigned long int) gpiochip,(unsigned long int) sgpio);
+ if (gpio_out_pin==new_out_pin)
+ return 0; //do not set up, pin not changed
+
+ if (gpio_out_pin!=0) { //we had tx pin setup. Free it so others can use it!
+ dprintk(": trying to free old out pin index %d: %s\n",gpio_out_pin,gpiochip->names[TX_OFFSET_GPIOCHIP]);
+ gpio_free(gpio_out_pin);
+ }
+ gpio_out_pin=new_out_pin;
+ if (gpio_out_pin==0) {
+ return 0; // do not set up, TX disabled
+ }
+ dprintk(": trying to claim new out pin index %d: %s\n",gpio_out_pin,gpiochip->names[TX_OFFSET_GPIOCHIP]);
+ ret = gpio_request(gpio_out_pin,LIRC_DRIVER_NAME "ir/out");
+ if (ret){
+ printk(KERN_ALERT LIRC_DRIVER_NAME
+ ": cant claim gpio pin %d with code %d\n", gpio_out_pin,ret);
+ ret = -ENODEV;
+ goto exit_disable_tx;
+ }
+ gpiochip->direction_output(gpiochip, TX_OFFSET_GPIOCHIP, 1);
+ gpiochip->set(gpiochip, TX_OFFSET_GPIOCHIP, invert);
+ pinstate=kzalloc(sizeof(user_gpio_set_t),GFP_KERNEL);
+
+ //dprintk("pin:address %lx, pin_name: %s, pin handler: %d",(unsigned long int) &(sgpio->data[TX_OFFSET_GPIOCHIP]),
+ // sgpio->data[TX_OFFSET_GPIOCHIP].pin_name,sgpio->data[TX_OFFSET_GPIOCHIP].gpio_handler);
+ ret=gpio_get_one_pin_status(sgpio->data[TX_OFFSET_GPIOCHIP].gpio_handler, pinstate, sgpio->data[TX_OFFSET_GPIOCHIP].pin_name, true);
+ if(pinstate && !ret) {
+ pr_info("Maximum load on '%s' is %d mA\n", gpiochip->names[TX_OFFSET_GPIOCHIP], 10+10*pinstate->drv_level);
+ kfree(pinstate);
+ }
+ else
+ printk(KERN_ALERT LIRC_DRIVER_NAME ": something might have gone wrong, return from pin status query: %d",ret);
+ return 0; // successfully set up
+
+exit_disable_tx:
+ // cannot claim new pin
+ gpio_out_pin = 0; // disable tx
+ return ret;
+}
+
+static int setup_rx(int new_in_pin)
+{
+ int ret,irq;
+ if (gpio_in_pin==new_in_pin)
+ return 0; //do not set up, pin not changed
+
+ if (gpio_in_pin!=0) { //we had rx pin setup. Free it so others can use it!
+ dprintk(": trying to free old in pin index %d: %s\n",gpio_in_pin,gpiochip->names[RX_OFFSET_GPIOCHIP]);
+ gpio_free(gpio_in_pin);
+ irqchip=NULL;
+ irqdata=NULL;
+ }
+ gpio_in_pin=new_in_pin;
+ if (gpio_in_pin==0) {
+ return 0; // do not set up, RX disabled
+ }
+ dprintk(": trying to claim new in pin index %d: %s\n",gpio_in_pin,gpiochip->names[RX_OFFSET_GPIOCHIP]);
+ ret = gpio_request(gpio_in_pin, LIRC_DRIVER_NAME " ir/in");
+ if (ret) {
+ printk(KERN_ALERT LIRC_DRIVER_NAME
+ ": cant claim gpio pin %d with code %d\n", gpio_in_pin,ret);
+ ret = -ENODEV;
+ goto exit_disable_rx;
+ }
+
+ gpiochip->direction_input(gpiochip, RX_OFFSET_GPIOCHIP);
+ /* try to setup interrupt data */
+ irq = gpiochip->to_irq(gpiochip, RX_OFFSET_GPIOCHIP);
+ dprintk("to_irq %d for pin %d\n", irq, gpio_in_pin);
+ irqdata = irq_get_irq_data(irq);
+
+ if (irqdata && irqdata->chip) {
+ irqchip = irqdata->chip;
+ } else {
+ ret = -ENODEV;
+ goto exit_gpio_free_in_pin;
+ }
+
+ set_sense();
+
+ return 0; //successfully set up
+
+exit_gpio_free_in_pin:
+ // interrupt set up failed, so free pin
+ gpio_free(gpio_in_pin);
+exit_disable_rx:
+ // could not claim new pin
+ gpio_in_pin=0; // disable rx
+ return ret;
+
+}
+
+/* end of pin setup */
+
+/* get right gpio chip */
+
+static int is_right_chip(struct gpio_chip *chip, const void *data)
+{
+ if (strcmp(data, chip->label) == 0)
+ return 1;
+ return 0;
+}
+
+
+static int set_gpiochip(void)
+{
+ gpiochip = gpiochip_find(LIRC_GPIO_ID_STRING, is_right_chip);
+
+ if (!gpiochip)
+ return -ENODEV;
+
+ return 0;
+
+}
+
+/* end of find gpio chip */
+
+
+
+/* lirc device stuff */
+
+/* called when the character device is opened
+ timing params initialized and interrupts activated */
+static int set_use_inc(void *data)
+{
+ int result;
+ unsigned long flags;
+
+ init_timing_params(duty_cycle, freq);
+ /* initialize pulse/space widths */
+
+ //initialize timestamp, would not be needed if no RX
+ do_gettimeofday(&lasttv);
+ device_open++;
+
+ if (gpio_in_pin!=0) { // entered if RX used
+ /* try to set all interrupts to same handler, should work */
+ result = request_irq(gpiochip->to_irq(gpiochip, RX_OFFSET_GPIOCHIP),
+ (irq_handler_t) irq_handler, 0,
+ LIRC_DRIVER_NAME, (void*) 0);
+
+ switch (result) {
+ case -EBUSY:
+ printk(KERN_ERR LIRC_DRIVER_NAME
+ ": IRQ %d is busy\n",
+ gpiochip->to_irq(gpiochip, RX_OFFSET_GPIOCHIP));
+ return -EBUSY;
+ case -EINVAL:
+ printk(KERN_ERR LIRC_DRIVER_NAME
+ ": Bad irq number or handler\n");
+ return -EINVAL;
+ default:
+ dprintk("Interrupt %d obtained\n",
+ gpiochip->to_irq(gpiochip, RX_OFFSET_GPIOCHIP));
+ break;
+ };
+ spin_lock_irqsave(&lock, flags);
+
+ /* GPIO Pin Falling/Rising Edge Detect Enable */
+ irqchip->irq_set_type(irqdata,
+ IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING);
+
+ /* unmask the irq for active channel, only */
+ irqchip->irq_unmask(irqdata);
+ spin_unlock_irqrestore(&lock, flags);
+
+ }
+
+ return 0;
+}
+
+/* called when character device is closed */
+static void set_use_dec(void *data)
+{
+ unsigned long flags;
+ device_open--;
+
+ if(!irqchip)
+ return;
+
+ /* GPIO Pin Falling/Rising Edge Detect Disable */
+ spin_lock_irqsave(&lock, flags);
+ irqchip->irq_set_type(irqdata, 0);
+ irqchip->irq_mask(irqdata);
+ spin_unlock_irqrestore(&lock, flags);
+
+ free_irq(gpiochip->to_irq(gpiochip, RX_OFFSET_GPIOCHIP), (void *) 0);
+ device_open--;
+ dprintk("freed IRQ %d\n", gpiochip->to_irq(gpiochip, RX_OFFSET_GPIOCHIP));
+
+}
+
+/* lirc to tx */
+static ssize_t lirc_write(struct file *file, const char *buf,
+ size_t n, loff_t *ppos)
+{
+ int i, count;
+ unsigned long flags;
+
+ long delta = 0;
+ int *wbuf;
+ if (!gpio_out_pin) {
+ return -ENODEV;
+ }
+ count = n / sizeof(int);
+ if (n % sizeof(int) || count % 2 == 0)
+ return -EINVAL;
+ wbuf = memdup_user(buf, n);
+ if (IS_ERR(wbuf))
+ return PTR_ERR(wbuf);
+ spin_lock_irqsave(&lock, flags);
+ dprintk("lirc_write called, offset %d",TX_OFFSET_GPIOCHIP);
+ for (i = 0; i < count; i++) {
+ if (i%2)
+ send_space(wbuf[i] - delta);
+ else
+ delta = send_pulse(wbuf[i]);
+ }
+ gpiochip->set(gpiochip, TX_OFFSET_GPIOCHIP, invert);
+ spin_unlock_irqrestore(&lock, flags);
+ if (count>11) {
+ dprintk("lirc_write sent %d pulses: no10: %d, no11: %d\n",count,wbuf[10],wbuf[11]);
+ }
+ kfree(wbuf);
+ return n;
+}
+
+/* interpret lirc commands */
+static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
+{
+ int result;
+ __u32 value;
+
+ switch (cmd) {
+ case LIRC_GET_SEND_MODE:
+ return -ENOIOCTLCMD;
+ break;
+
+ case LIRC_SET_SEND_MODE:
+ result = get_user(value, (__u32 *) arg);
+ if (result)
+ return result;
+ /* only LIRC_MODE_PULSE supported */
+ if (value != LIRC_MODE_PULSE)
+ return -ENOSYS;
+ dprintk("Sending stuff on lirc");
+ break;
+
+ case LIRC_GET_LENGTH:
+ return -ENOSYS;
+ break;
+
+ case LIRC_SET_SEND_DUTY_CYCLE:
+ result = get_user(value, (__u32 *) arg);
+ if (result)
+ return result;
+ if (value <= 0 || value > 100)
+ return -EINVAL;
+ dprintk("SET_SEND_DUTY_CYCLE to %d \n", value);
+ return init_timing_params(value, freq);
+ break;
+
+ case LIRC_SET_SEND_CARRIER:
+ result = get_user(value, (__u32 *) arg);
+ if (result)
+ return result;
+ if (value > 500000 || value < 20000)
+ return -EINVAL;
+ dprintk("SET_SEND_CARRIER to %d \n",value);
+ return init_timing_params(duty_cycle, value);
+ break;
+
+ default:
+ return lirc_dev_fop_ioctl(filep, cmd, arg);
+ }
+ return 0;
+}
+
+static const struct file_operations lirc_fops = {
+ .owner = THIS_MODULE,
+ .write = lirc_write,
+ .unlocked_ioctl = lirc_ioctl,
+ .read = lirc_dev_fop_read, // this and the rest is default
+ .poll = lirc_dev_fop_poll,
+ .open = lirc_dev_fop_open,
+ .release = lirc_dev_fop_close,
+ .llseek = no_llseek,
+};
+
+static struct lirc_driver driver = {
+ .name = LIRC_DRIVER_NAME,
+ .minor = -1, // assing automatically
+ .code_length = 1,
+ .sample_rate = 0,
+ .data = NULL,
+ .add_to_buf = NULL,
+ .rbuf = &rbuf,
+ .set_use_inc = set_use_inc,
+ .set_use_dec = set_use_dec,
+ .fops = &lirc_fops,
+ .dev = NULL,
+ .owner = THIS_MODULE,
+};
+
+/* end of lirc device/driver stuff */
+
+/* now comes THIS driver, above is lirc */
+static struct platform_driver lirc_gpio_driver = {
+ .driver = {
+ .name = LIRC_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+
+
+/* stuff for sysfs*/
+
+static DEFINE_MUTEX(sysfs_lock);
+
+
+
+static ssize_t lirc_txpin_show(struct class *class, struct class_attribute *attr, char *buf)
+{
+ ssize_t status;
+ mutex_lock(&sysfs_lock);
+ status = sprintf(buf,"%d\n",gpio_out_pin);
+ mutex_unlock(&sysfs_lock);
+ return status;
+}
+
+static ssize_t lirc_txpin_store(struct class *class, struct class_attribute *attr, const char* buf, size_t size)
+{
+ int new_pin;
+ ssize_t status;
+ mutex_lock(&sysfs_lock);
+ sscanf(buf,"%d",&new_pin);
+ status = setup_tx(new_pin) ? : size;
+ mutex_unlock(&sysfs_lock);
+ return status;
+}
+
+static ssize_t lirc_rxpin_show(struct class *class, struct class_attribute *attr, char *buf)
+{
+ ssize_t status;
+ mutex_lock(&sysfs_lock);
+ status = sprintf(buf,"%d\n",gpio_in_pin);
+ mutex_unlock(&sysfs_lock);
+ return status;
+}
+
+static ssize_t lirc_rxpin_store(struct class *class, struct class_attribute *attr, const char* buf, size_t size)
+{
+ int new_pin;
+ ssize_t status;
+ mutex_lock(&sysfs_lock);
+ sscanf(buf,"%d",&new_pin);
+ status = setup_rx(new_pin) ? : size;
+ mutex_unlock(&sysfs_lock);
+ return status;
+}
+
+static ssize_t lirc_softcarrier_show(struct class *class, struct class_attribute *attr, char *buf)
+{
+ ssize_t status;
+ mutex_lock(&sysfs_lock);
+ status = sprintf(buf,"%d\n",softcarrier);
+ mutex_unlock(&sysfs_lock);
+ return status;
+}
+
+static ssize_t lirc_softcarrier_store(struct class *class, struct class_attribute *attr, const char* buf, size_t size)
+{
+ int try_value;
+ ssize_t status=size;
+ mutex_lock(&sysfs_lock);
+ sscanf(buf,"%d",&try_value);
+ if ((try_value==0) || (try_value==1)) {
+ softcarrier=try_value;
+ }
+ else
+ status = -EINVAL;
+ mutex_unlock(&sysfs_lock);
+ return status;
+}
+static ssize_t lirc_invert_show(struct class *class, struct class_attribute *attr, char *buf)
+{
+ ssize_t status;
+ mutex_lock(&sysfs_lock);
+ status = sprintf(buf,"%d\n",invert);
+ mutex_unlock(&sysfs_lock);
+ return status;
+}
+
+static ssize_t lirc_invert_store(struct class *class, struct class_attribute *attr, const char* buf, size_t size)
+{
+ int try_value;
+ ssize_t status=size;
+ mutex_lock(&sysfs_lock);
+ sscanf(buf,"%d",&try_value);
+ if ((try_value==0) || (try_value==1)) {
+ invert=try_value;
+ }
+ else
+ status = -EINVAL;
+ mutex_unlock(&sysfs_lock);
+ return status;
+}
+
+static ssize_t lirc_sense_show(struct class *class, struct class_attribute *attr, char *buf)
+{
+ ssize_t status;
+ mutex_lock(&sysfs_lock);
+ status = sprintf(buf,"%d\n",sense);
+ mutex_unlock(&sysfs_lock);
+ return status;
+}
+
+static ssize_t lirc_sense_store(struct class *class, struct class_attribute *attr, const char* buf, size_t size)
+{
+ int try_value;
+ ssize_t status=size;
+ mutex_lock(&sysfs_lock);
+ sscanf(buf,"%d",&try_value);
+ mutex_unlock(&sysfs_lock);
+ if ((try_value<2) && (try_value>-2)) {
+ sense=try_value;
+ }
+ else
+ return -EINVAL;
+
+ if (gpio_in_pin>0) {
+ set_sense();
+ }
+ return status;
+}
+
+/* I don't think we need another device, so just put it in the class directory
+ * All we need is a way to access some global parameters of this module */
+
+static struct class_attribute lirc_gpio_attrs[] = {
+ __ATTR(tx_gpio_pin, 0644, lirc_txpin_show, lirc_txpin_store),
+ __ATTR(rx_gpio_pin, 0644, lirc_rxpin_show, lirc_rxpin_store),
+ __ATTR(lirc_softcarrier, 0644, lirc_softcarrier_show, lirc_softcarrier_store),
+ __ATTR(lirc_invert, 0644, lirc_invert_show, lirc_invert_store),
+ __ATTR(lirc_sense, 0644, lirc_sense_show, lirc_sense_store),
+ __ATTR_NULL,
+};
+static struct class lirc_gpio_class = {
+ .name = "lirc_gpio",
+ .owner = THIS_MODULE,
+ .class_attrs = lirc_gpio_attrs,
+};
+
+/* end of sysfs stuff */
+
+/* initialize / free THIS driver and device and a lirc buffer*/
+
+static int __init lirc_gpio_init(void)
+{
+ int result;
+
+ /* Init read buffer. */
+ result = lirc_buffer_init(&rbuf, sizeof(int), RBUF_LEN);
+ if (result < 0)
+ return -ENOMEM;
+
+ result = platform_driver_register(&lirc_gpio_driver);
+ if (result) {
+ printk(KERN_ERR LIRC_DRIVER_NAME
+ ": lirc register returned %d\n", result);
+ goto exit_buffer_free;
+ }
+
+ lirc_gpio_dev = platform_device_alloc(LIRC_DRIVER_NAME, 0);
+ if (!lirc_gpio_dev) {
+ result = -ENOMEM;
+ goto exit_driver_unregister;
+ }
+
+ result = platform_device_add(lirc_gpio_dev);
+ if (result)
+ goto exit_device_put;
+
+ return 0;
+
+exit_device_put:
+ platform_device_put(lirc_gpio_dev);
+
+exit_driver_unregister:
+ platform_driver_unregister(&lirc_gpio_driver);
+
+exit_buffer_free:
+ lirc_buffer_free(&rbuf);
+
+ return result;
+}
+
+static void lirc_gpio_exit(void)
+{
+ setup_tx(0); // frees gpio_out_pin if set
+ setup_rx(0); // dito
+ platform_device_unregister(lirc_gpio_dev);
+ platform_driver_unregister(&lirc_gpio_driver);
+ lirc_buffer_free(&rbuf);
+}
+
+/* end of stuff for THIS driver/device registration */
+/* ignorance of unset pins in setup routines tolerate call if nothing is set up */
+
+/* master init */
+
+static int __init lirc_gpio_init_module(void)
+{
+ int result,temp_in_pin,temp_out_pin;
+
+ result = lirc_gpio_init();
+ if (result)
+ return result;
+ // 'driver' is the lirc driver
+ driver.features = LIRC_CAN_SET_SEND_DUTY_CYCLE |
+ LIRC_CAN_SET_SEND_CARRIER |
+ LIRC_CAN_SEND_PULSE |
+ LIRC_CAN_REC_MODE2;
+
+ driver.dev = &lirc_gpio_dev->dev; // link THIS platform device to lirc driver
+ driver.minor = lirc_register_driver(&driver);
+
+ if (driver.minor < 0) {
+ printk(KERN_ERR LIRC_DRIVER_NAME
+ ": device registration failed with %d\n", result);
+ result = -EIO;
+ goto exit_lirc;
+ }
+
+ printk(KERN_INFO LIRC_DRIVER_NAME ": driver registered!\n");
+
+ result = set_gpiochip();
+ if (result < 0)
+ goto exit_lirc;
+ /* some hacking to get pins initialized on first used */
+ /* setup_tx/rx will not do anything if pins would not change */
+ temp_out_pin = gpio_out_pin; gpio_out_pin = 0;
+ result = setup_tx(temp_out_pin);
+ if (result < 0)
+ goto exit_lirc;
+ /* dito for rx */
+ temp_in_pin = gpio_in_pin; gpio_in_pin = 0;
+ result = setup_rx(temp_in_pin);
+ if (result < 0)
+ goto exit_lirc;
+ if (device_open) { // this is unlikely, but well...
+ result = set_use_inc((void*) 0);
+ if (result<0) {
+ goto exit_lirc;
+ }
+ }
+
+ result=class_register(&lirc_gpio_class);
+ if (result) {
+ goto exit_lirc;
+ }
+
+
+ return 0;
+
+exit_lirc:
+ /* failed attempt to setup_tx/rx sets pin to 0. */
+ /* next call with arg 0 will then not do anything -> only one exit routine */
+ lirc_gpio_exit();
+
+ return result;
+}
+
+static void __exit lirc_gpio_exit_module(void)
+{
+
+ lirc_gpio_exit();
+ class_unregister(&lirc_gpio_class);
+
+ lirc_unregister_driver(driver.minor);
+ printk(KERN_INFO LIRC_DRIVER_NAME ": cleaned up module\n");
+}
+
+module_init(lirc_gpio_init_module);
+module_exit(lirc_gpio_exit_module);
+
+MODULE_DESCRIPTION("Infra-red receiver and blaster driver for GPIO-Lib.");
+MODULE_DESCRIPTION("Parameters can be set/changed in /sys/class/lirc_gpio");
+MODULE_DESCRIPTION("If RX Pin is changed, previous value of sense is taken");
+MODULE_DESCRIPTION("You may want to write -1 to lirc_sense to force new auto-detection");
+MODULE_AUTHOR("Matthias Hoelling <mhoel....@gmail.nospam.com");
+MODULE_LICENSE("GPL");
+
+module_param(gpio_out_pin, int, S_IRUGO);
+MODULE_PARM_DESC(gpio_out_pin, "GPIO output/transmitter pin array");
+
+module_param(gpio_in_pin, int, S_IRUGO);
+MODULE_PARM_DESC(gpio_in_pin, "GPIO input/receiver pin array");
+
+module_param(sense, int, S_IRUGO);
+MODULE_PARM_DESC(sense, "Override autodetection of IR receiver circuit");
+
+module_param(softcarrier, int, S_IRUGO);
+MODULE_PARM_DESC(softcarrier, "Software carrier (0 = off, 1 = on, default on)");
+
+module_param(invert, int, S_IRUGO);
+MODULE_PARM_DESC(invert, "Invert output (0 = off, 1 = on, default off");
+
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
diff --git a/drivers/staging/media/lirc/sunxi-lirc.c b/drivers/staging/media/lirc/sunxi-lirc.c
new file mode 100644
index 0000000..b571b7b
--- /dev/null
+++ b/drivers/staging/media/lirc/sunxi-lirc.c
@@ -0,0 +1,590 @@
+/*
+ *
+ * sunxi_lirc.c
+ *
+ * sunxi_lirc - Device driver that uses Allwinner A1X or A20 IR module in CIR mode
+ * for LIRC. Tested on a Cubietruck with Allwinner A20
+ * a lot of code from the sunxi-ir module,
+ * so I would like say thanks to the authors.
+ * Difference to sunxi-ir is that no verification of IR code
+ * against NEC protocol is made, whatsoever
+ * but just passed on to lirc buffer to let lirc do any decoding
+ *
+ * Copyright (C) 2014 Matthias Hoelling <mhoel....@gmail.nospam.com>,
+ *
+ * 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/module.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+#include <asm/irq.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <mach/clock.h>
+#include <media/lirc.h>
+#include <media/lirc_dev.h>
+
+#include <mach/irqs.h>
+#include <mach/system.h>
+#include <mach/hardware.h>
+#include <plat/sys_config.h>
+
+#include <linux/clk.h>
+
+#define LIRC_DRIVER_NAME "sunxi_lirc"
+#define RBUF_LEN 256
+
+static struct platform_device *lirc_sunxi_dev;
+
+static struct clk *apb_ir_clk;
+static struct clk *ir_clk;
+static u32 ir_gpio_hdle;
+
+#define SYS_CLK_CFG_EN
+
+#define SYS_GPIO_CFG_EN
+/* #define DEBUG_IR */
+#define PRINT_SUSPEND_INFO
+
+#define dprintk(fmt, args...) \
+do { \
+if (debug) \
+printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \
+fmt, ## args); \
+} while (0)
+
+/* Registers */
+#define IR_REG(x) (x)
+#define IR0_BASE (0xf1c21800)
+#define IR1_BASE (0xf1c21c00)
+#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)
+
+#ifdef CONFIG_ARCH_SUN5I
+#define IR_FIFO_SIZE (64) /* 64Bytes */
+#else
+#define IR_FIFO_SIZE (16) /* 16Bytes */
+#endif
+/* 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_RAW_BUF_SIZE 128
+#define DRV_VERSION "1.00"
+
+
+
+struct ir_raw_buffer {
+ unsigned int dcnt; /*Packet Count*/
+ unsigned char buf[IR_RAW_BUF_SIZE];
+};
+
+static struct lirc_buffer rbuf;
+
+DEFINE_SPINLOCK(sunxi_lirc_spinlock);
+
+
+
+static struct ir_raw_buffer ir_rawbuf;
+
+static int debug=0;
+
+
+
+static inline void ir_reset_rawbuffer(void)
+{
+ ir_rawbuf.dcnt = 0;
+}
+
+static inline void ir_write_rawbuffer(unsigned char data)
+{
+ if (ir_rawbuf.dcnt < IR_RAW_BUF_SIZE)
+ ir_rawbuf.buf[ir_rawbuf.dcnt++] = data;
+ else
+ printk("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; /* 6 MHz */
+#else
+ unsigned long tmp = 0;
+#endif
+
+#ifdef SYS_CLK_CFG_EN
+ apb_ir_clk = clk_get(NULL, "apb_ir0");
+ if (!apb_ir_clk) {
+ printk("try to get apb_ir0 clock failed\n");
+ return;
+ }
+
+ ir_clk = clk_get(NULL, "ir0");
+ if (!ir_clk) {
+ printk("try to get ir0 clock failed\n");
+ return;
+ }
+ dprintk("trying to set clock via SYS_CLK_CFG_EN, when no error follows -> succeeded\n");
+ if (clk_set_rate(ir_clk, rate))
+ printk("set ir0 clock freq to 6M failed\n");
+
+ if (clk_enable(apb_ir_clk))
+ printk("try to enable apb_ir_clk failed\n");
+
+ if (clk_enable(ir_clk))
+ printk("try to enable apb_ir_clk failed\n");
+
+#else
+ dprintk("setting clock via register manipulation\n");
+ /* Enable APB Clock for IR */
+ /* copied from sunxi-ir.c, but not sure if this will work, registers do not seem conform with data sheet */
+ tmp = readl(CCM_BASE + 0x10);
+ tmp |= 0x1 << 10; /* IR */
+ writel(tmp, CCM_BASE + 0x10);
+
+ /* config Special Clock for IR (24/8=6MHz) */
+ 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
+ clk_put(apb_ir_clk);
+ clk_put(ir_clk);
+#endif
+
+ return;
+}
+static void ir_sys_cfg(void)
+{
+#ifdef SYS_GPIO_CFG_EN
+ ir_gpio_hdle = gpio_request_ex("ir_para", "ir0_rx");
+ if (0 == ir_gpio_hdle)
+ printk("try to request ir_para gpio failed\n");
+#else
+ /* 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;
+}
+
+static void ir_sys_uncfg(void)
+{
+#ifdef SYS_GPIO_CFG_EN
+ gpio_release(ir_gpio_hdle, 2);
+#endif
+ ir_clk_uncfg();
+
+ return;
+}
+
+static void ir_reg_cfg(void)
+{
+ unsigned long tmp = 0;
+ /* Enable CIR Mode */
+ tmp = 0x3 << 4;
+ writel(tmp, IR_BASE + IR_CTRL_REG);
+
+ /* Config IR Sample 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 */
+ 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;
+#ifdef CONFIG_ARCH_SUN5I
+ tmp |= ((IR_FIFO_SIZE >> 2) - 1) << 8; /* Rx FIFO Threshold = FIFOsz/4 */
+#else
+ tmp |= ((IR_FIFO_SIZE >> 1) - 1) << 8; /* Rx FIFO Threshold = FIFOsz/2 */
+#endif
+ 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("ir_setup: ir setup start!!\n");
+
+ ir_reset_rawbuffer();
+ ir_sys_cfg();
+ ir_reg_cfg();
+
+ dprintk("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)
+{
+ unsigned int i;
+ unsigned int lirc_val;
+ dprintk("Buffer length: %d",dcnt);
+ for(i=0;i<dcnt;i++) {
+ lirc_val= ((unsigned int) (buf[i] & 0x80) << PULSE_BIT_SHIFTER) | (SAMPLES_TO_US(buf[i] &0x7f));
+ while((buf[i] & 0x80) == (buf[i+1] & 0x80)) {
+ lirc_val += SAMPLES_TO_US(buf[++i]&0x7f);
+ }
+ /* statistically pulses are one sample period (?) too long, spaces too short */
+ /* would make sense because of bandpass latency, but not sure... */
+ lirc_val += (buf[i] & 0x80) ? (-SAMPLES_TO_US(1)) : SAMPLES_TO_US(1);
+ dprintk("rawbuf: %x, value: %x, level:%d for %d us\n",buf[i],lirc_val,(buf[i]&0x80) ? 1 : 0,lirc_val & PULSE_MASK) ;
+ /* to do, write to lirc buffer */
+ if (lirc_buffer_full(&rbuf)) {
+ /* no new signals will be accepted */
+ dprintk("Buffer overrun\n");
+ return;
+ }
+ lirc_buffer_write(&rbuf,(unsigned char*)&lirc_val);
+ }
+
+
+ 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);
+
+ /* Read Data Every Time Enter this Routine*/
+#ifdef CONFIG_ARCH_SUN5I
+ dcnt = (unsigned int) (ir_get_intsta() >> 8) & 0x3f;
+#else
+ dcnt = (unsigned int) (ir_get_intsta() >> 8) & 0x1f;
+#endif
+
+ /* Read FIFO */
+ for (i = 0; i < dcnt; i++) {
+ if (ir_rawbuffer_full()) {
+ dprintk("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("Buffer written\n");
+ ir_rawbuf.dcnt = 0;
+
+ wake_up_interruptible(&rbuf.wait_poll);
+
+
+ }
+
+ if (intsta & IR_RXINTS_RXOF) {/* FIFO Overflow */
+ /* flush raw buffer */
+ ir_reset_rawbuffer();
+ dprintk("ir_irq_service: Rx FIFO Overflow!!\n");
+ }
+
+ return IRQ_HANDLED;
+}
+
+
+
+/* interpret lirc commands */
+static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
+{
+
+ switch (cmd) {
+ case LIRC_GET_SEND_MODE:
+ return -ENOIOCTLCMD;
+ break;
+
+ /* driver cannot send */
+ case LIRC_SET_SEND_MODE:
+ return -ENOSYS;
+ break;
+
+ case LIRC_GET_LENGTH:
+ return -ENOSYS;
+ break;
+
+ case LIRC_SET_SEND_DUTY_CYCLE:
+ return -ENOSYS;
+ break;
+
+ case LIRC_SET_SEND_CARRIER:
+ return -ENOSYS;
+ break;
+
+ default:
+ return lirc_dev_fop_ioctl(filep, cmd, arg);
+ }
+ return 0;
+}
+
+
+static int set_use_inc(void* data) {
+ return 0;
+}
+
+static void set_use_dec(void* data) {
+
+}
+
+static const struct file_operations lirc_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = lirc_ioctl,
+ .read = lirc_dev_fop_read, // this and the rest is default
+ .write = lirc_dev_fop_write,
+ .poll = lirc_dev_fop_poll,
+ .open = lirc_dev_fop_open,
+ .release = lirc_dev_fop_close,
+ .llseek = no_llseek,
+};
+
+static struct lirc_driver driver = {
+ .name = LIRC_DRIVER_NAME,
+ .minor = -1, // assing automatically
+ .code_length = 1,
+ .sample_rate = 0,
+ .data = NULL,
+ .add_to_buf = NULL,
+ .rbuf = &rbuf,
+ .set_use_inc = set_use_inc,
+ .set_use_dec = set_use_dec,
+ .fops = &lirc_fops,
+ .dev = NULL,
+ .owner = THIS_MODULE,
+};
+
+/* 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)
+{
+ int result;
+ /* Init read buffer. */
+ result = lirc_buffer_init(&rbuf, sizeof(int), RBUF_LEN);
+ if (result < 0)
+ return -ENOMEM;
+
+ result = platform_driver_register(&lirc_sunxi_driver);
+ if (result) {
+ printk(KERN_ERR LIRC_DRIVER_NAME
+ ": lirc register returned %d\n", result);
+ goto exit_buffer_free;
+ }
+
+ lirc_sunxi_dev = platform_device_alloc(LIRC_DRIVER_NAME, 0);
+ if (!lirc_sunxi_dev) {
+ result = -ENOMEM;
+ goto exit_driver_unregister;
+ }
+
+ result = platform_device_add(lirc_sunxi_dev);
+ if (result) {
+ platform_device_put(lirc_sunxi_dev);
+ goto exit_driver_unregister;
+ }
+
+ if (request_irq(IR_IRQNO, ir_irq_service, 0, "RemoteIR",
+ (void*) 0)) {
+ result = -EBUSY;
+ goto exit_device_unregister;
+ }
+
+ ir_setup();
+
+ printk("IR Initial OK\n");
+
+
+
+ // 'driver' is the lirc driver
+ driver.features = LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2;
+
+ driver.dev = &lirc_sunxi_dev->dev; // link THIS platform device to lirc driver
+ driver.minor = lirc_register_driver(&driver);
+
+ if (driver.minor < 0) {
+ printk(KERN_ERR LIRC_DRIVER_NAME
+ ": device registration failed with %d\n", result);
+
+ result = -EIO;
+ goto exit_free_irq;
+ }
+
+ printk(KERN_INFO LIRC_DRIVER_NAME ": driver registered!\n");
+
+ return 0;
+
+
+exit_free_irq:
+ free_irq(IR_IRQNO, (void*) 0);
+
+exit_device_unregister:
+ platform_device_unregister(lirc_sunxi_dev);
+
+exit_driver_unregister:
+ platform_driver_unregister(&lirc_sunxi_driver);
+
+exit_buffer_free:
+ lirc_buffer_free(&rbuf);
+
+ return result;
+}
+
+static void __exit ir_exit(void)
+{
+
+ free_irq(IR_IRQNO, (void*) 0);
+ ir_sys_uncfg();
+ platform_device_unregister(lirc_sunxi_dev);
+
+ platform_driver_unregister(&lirc_sunxi_driver);
+
+ lirc_buffer_free(&rbuf);
+ lirc_unregister_driver(driver.minor);
+ 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("Matthias Hoelling");
+MODULE_LICENSE("GPL");
+
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+