diff options
author | John Crispin <john@openwrt.org> | 2015-07-17 12:48:39 +0000 |
---|---|---|
committer | John Crispin <john@openwrt.org> | 2015-07-17 12:48:39 +0000 |
commit | 208ab54e3e9516dd9fcf1ce197950044e0bebd90 (patch) | |
tree | 425580d694235f06661dd2d6763925a1f69279c6 /target/linux/brcm2708/patches-4.1/0016-lirc-added-support-for-RaspberryPi-GPIO.patch | |
parent | 03b56c9350057ee77cfe2b8bb8f8d7146917fb79 (diff) | |
download | mtk-20170518-208ab54e3e9516dd9fcf1ce197950044e0bebd90.zip mtk-20170518-208ab54e3e9516dd9fcf1ce197950044e0bebd90.tar.gz mtk-20170518-208ab54e3e9516dd9fcf1ce197950044e0bebd90.tar.bz2 |
brcm2708: add linux 4.1 support
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
SVN-Revision: 46399
Diffstat (limited to 'target/linux/brcm2708/patches-4.1/0016-lirc-added-support-for-RaspberryPi-GPIO.patch')
-rw-r--r-- | target/linux/brcm2708/patches-4.1/0016-lirc-added-support-for-RaspberryPi-GPIO.patch | 848 |
1 files changed, 848 insertions, 0 deletions
diff --git a/target/linux/brcm2708/patches-4.1/0016-lirc-added-support-for-RaspberryPi-GPIO.patch b/target/linux/brcm2708/patches-4.1/0016-lirc-added-support-for-RaspberryPi-GPIO.patch new file mode 100644 index 0000000..6b9e64c --- /dev/null +++ b/target/linux/brcm2708/patches-4.1/0016-lirc-added-support-for-RaspberryPi-GPIO.patch @@ -0,0 +1,848 @@ +From e074d656e4011eae32577e4d20d7ce6dfa15f6e8 Mon Sep 17 00:00:00 2001 +From: Aron Szabo <aron@aron.ws> +Date: Sat, 16 Jun 2012 12:15:55 +0200 +Subject: [PATCH 016/121] lirc: added support for RaspberryPi GPIO + +lirc_rpi: Use read_current_timer to determine transmitter delay. Thanks to jjmz and others +See: https://github.com/raspberrypi/linux/issues/525 + +lirc: Remove restriction on gpio pins that can be used with lirc + +Compute Module, for example could use different pins + +lirc_rpi: Add parameter to specify input pin pull + +Depending on the connected IR circuitry it might be desirable to change the +gpios internal pull from it pull-down default behaviour. Add a module +parameter to allow the user to set it explicitly. + +Signed-off-by: Julian Scheel <julian@jusst.de> + +lirc-rpi: Use the higher-level irq control functions + +This module used to access the irq_chip methods of the +gpio controller directly, rather than going through the +standard enable_irq/irq_set_irq_type functions. This +caused problems on pinctrl-bcm2835 which only implements +the irq_enable/disable methods and not irq_unmask/mask. + +lirc-rpi: Correct the interrupt usage + +1) Correct the use of enable_irq (i.e. don't call it so often) +2) Correct the shutdown sequence. +3) Avoid a bcm2708_gpio driver quirk by setting the irq flags earlier + +lirc-rpi: use getnstimeofday instead of read_current_timer + +read_current_timer isn't guaranteed to return values in +microseconds, and indeed it doesn't on a Pi2. + +Issue: linux#827 + +lirc-rpi: Add device tree support, and a suitable overlay + +The overlay supports DT parameters that match the old module +parameters, except that gpio_in_pull should be set using the +strings "up", "down" or "off". + +lirc-rpi: Also support pinctrl-bcm2835 in non-DT mode +--- + drivers/staging/media/lirc/Kconfig | 6 + + drivers/staging/media/lirc/Makefile | 1 + + drivers/staging/media/lirc/lirc_rpi.c | 765 ++++++++++++++++++++++++++++++++++ + 3 files changed, 772 insertions(+) + create mode 100644 drivers/staging/media/lirc/lirc_rpi.c + +--- a/drivers/staging/media/lirc/Kconfig ++++ b/drivers/staging/media/lirc/Kconfig +@@ -32,6 +32,12 @@ config LIRC_PARALLEL + help + Driver for Homebrew Parallel Port Receivers + ++config LIRC_RPI ++ tristate "Homebrew GPIO Port Receiver/Transmitter for the RaspberryPi" ++ depends on LIRC ++ help ++ Driver for Homebrew GPIO Port Receiver/Transmitter for the RaspberryPi ++ + config LIRC_SASEM + tristate "Sasem USB IR Remote" + depends on LIRC && USB +--- a/drivers/staging/media/lirc/Makefile ++++ b/drivers/staging/media/lirc/Makefile +@@ -6,6 +6,7 @@ + obj-$(CONFIG_LIRC_BT829) += lirc_bt829.o + obj-$(CONFIG_LIRC_IMON) += lirc_imon.o + obj-$(CONFIG_LIRC_PARALLEL) += lirc_parallel.o ++obj-$(CONFIG_LIRC_RPI) += lirc_rpi.o + obj-$(CONFIG_LIRC_SASEM) += lirc_sasem.o + obj-$(CONFIG_LIRC_SERIAL) += lirc_serial.o + obj-$(CONFIG_LIRC_SIR) += lirc_sir.o +--- /dev/null ++++ b/drivers/staging/media/lirc/lirc_rpi.c +@@ -0,0 +1,765 @@ ++/* ++ * lirc_rpi.c ++ * ++ * lirc_rpi - Device driver that records pulse- and pause-lengths ++ * (space-lengths) (just like the lirc_serial driver does) ++ * between GPIO interrupt events on the Raspberry Pi. ++ * Lots of code has been taken from the lirc_serial module, ++ * so I would like say thanks to the authors. ++ * ++ * Copyright (C) 2012 Aron Robert Szabo <aron@reon.hu>, ++ * Michael Bishop <cleverca22@gmail.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/timex.h> ++#include <linux/timekeeping.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 <mach/gpio.h> ++#include <linux/gpio.h> ++#include <linux/of_platform.h> ++ ++#include <linux/platform_data/bcm2708.h> ++ ++#define LIRC_DRIVER_NAME "lirc_rpi" ++#define RBUF_LEN 256 ++#define LIRC_TRANSMITTER_LATENCY 50 ++ ++#ifndef MAX_UDELAY_MS ++#define MAX_UDELAY_US 5000 ++#else ++#define MAX_UDELAY_US (MAX_UDELAY_MS*1000) ++#endif ++ ++#define dprintk(fmt, args...) \ ++ do { \ ++ if (debug) \ ++ printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \ ++ fmt, ## args); \ ++ } while (0) ++ ++/* module parameters */ ++ ++/* set the default GPIO input pin */ ++static int gpio_in_pin = 18; ++/* set the default pull behaviour for input pin */ ++static int gpio_in_pull = BCM2708_PULL_DOWN; ++/* set the default GPIO output pin */ ++static int gpio_out_pin = 17; ++/* enable debugging messages */ ++static bool debug; ++/* -1 = auto, 0 = active high, 1 = active low */ ++static int sense = -1; ++/* use softcarrier by default */ ++static bool softcarrier = 1; ++/* 0 = do not invert output, 1 = invert output */ ++static bool invert = 0; ++ ++struct gpio_chip *gpiochip; ++static int irq_num; ++ ++/* forward declarations */ ++static long send_pulse(unsigned long length); ++static void send_space(long length); ++static void lirc_rpi_exit(void); ++ ++static struct platform_device *lirc_rpi_dev; ++static struct timeval lasttv = { 0, 0 }; ++static struct lirc_buffer rbuf; ++static spinlock_t lock; ++ ++/* 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; ++ ++static void safe_udelay(unsigned long usecs) ++{ ++ while (usecs > MAX_UDELAY_US) { ++ udelay(MAX_UDELAY_US); ++ usecs -= MAX_UDELAY_US; ++ } ++ udelay(usecs); ++} ++ ++static unsigned long read_current_us(void) ++{ ++ struct timespec now; ++ getnstimeofday(&now); ++ return (now.tv_sec * 1000000) + (now.tv_nsec/1000); ++} ++ ++static int init_timing_params(unsigned int new_duty_cycle, ++ unsigned int new_freq) ++{ ++ if (1000 * 1000000L / new_freq * new_duty_cycle / 100 <= ++ LIRC_TRANSMITTER_LATENCY) ++ return -EINVAL; ++ if (1000 * 1000000L / new_freq * (100 - new_duty_cycle) / 100 <= ++ LIRC_TRANSMITTER_LATENCY) ++ return -EINVAL; ++ duty_cycle = new_duty_cycle; ++ freq = new_freq; ++ period = 1000 * 1000000L / freq; ++ pulse_width = period * duty_cycle / 100; ++ space_width = period - pulse_width; ++ dprintk("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; ++ unsigned long actual_us, initial_us, target_us; ++ ++ length *= 1000; ++ ++ actual = 0; target = 0; flag = 0; ++ actual_us = read_current_us(); ++ ++ while (actual < length) { ++ if (flag) { ++ gpiochip->set(gpiochip, gpio_out_pin, invert); ++ target += space_width; ++ } else { ++ gpiochip->set(gpiochip, gpio_out_pin, !invert); ++ target += pulse_width; ++ } ++ initial_us = actual_us; ++ target_us = actual_us + (target - actual) / 1000; ++ /* ++ * Note - we've checked in ioctl that the pulse/space ++ * widths are big enough so that d is > 0 ++ */ ++ if ((int)(target_us - actual_us) > 0) ++ udelay(target_us - actual_us); ++ actual_us = read_current_us(); ++ actual += (actual_us - initial_us) * 1000; ++ flag = !flag; ++ } ++ return (actual-length) / 1000; ++} ++ ++static long send_pulse(unsigned long length) ++{ ++ if (length <= 0) ++ return 0; ++ ++ if (softcarrier) { ++ return send_pulse_softcarrier(length); ++ } else { ++ gpiochip->set(gpiochip, gpio_out_pin, !invert); ++ safe_udelay(length); ++ return 0; ++ } ++} ++ ++static void send_space(long length) ++{ ++ gpiochip->set(gpiochip, gpio_out_pin, invert); ++ if (length <= 0) ++ return; ++ safe_udelay(length); ++} ++ ++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, gpio_in_pin); ++ ++ 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; ++} ++ ++static int is_right_chip(struct gpio_chip *chip, void *data) ++{ ++ dprintk("is_right_chip %s %d\n", chip->label, strcmp(data, chip->label)); ++ ++ if (strcmp(data, chip->label) == 0) ++ return 1; ++ return 0; ++} ++ ++static inline int read_bool_property(const struct device_node *np, ++ const char *propname, ++ bool *out_value) ++{ ++ u32 value = 0; ++ int err = of_property_read_u32(np, propname, &value); ++ if (err == 0) ++ *out_value = (value != 0); ++ return err; ++} ++ ++static void read_pin_settings(struct device_node *node) ++{ ++ u32 pin; ++ int index; ++ ++ for (index = 0; ++ of_property_read_u32_index( ++ node, ++ "brcm,pins", ++ index, ++ &pin) == 0; ++ index++) { ++ u32 function; ++ int err; ++ err = of_property_read_u32_index( ++ node, ++ "brcm,function", ++ index, ++ &function); ++ if (err == 0) { ++ if (function == 1) /* Output */ ++ gpio_out_pin = pin; ++ else if (function == 0) /* Input */ ++ gpio_in_pin = pin; ++ } ++ } ++} ++ ++static int init_port(void) ++{ ++ int i, nlow, nhigh, ret; ++ struct device_node *node; ++ ++ node = lirc_rpi_dev->dev.of_node; ++ ++ gpiochip = gpiochip_find("bcm2708_gpio", is_right_chip); ++ ++ /* ++ * Because of the lack of a setpull function, only support ++ * pinctrl-bcm2835 if using device tree. ++ */ ++ if (!gpiochip && node) ++ gpiochip = gpiochip_find("pinctrl-bcm2835", is_right_chip); ++ ++ if (!gpiochip) { ++ pr_err(LIRC_DRIVER_NAME ": gpio chip not found!\n"); ++ return -ENODEV; ++ } ++ ++ if (node) { ++ struct device_node *pins_node; ++ ++ pins_node = of_parse_phandle(node, "pinctrl-0", 0); ++ if (!pins_node) { ++ printk(KERN_ERR LIRC_DRIVER_NAME ++ ": pinctrl settings not found!\n"); ++ ret = -EINVAL; ++ goto exit_init_port; ++ } ++ ++ read_pin_settings(pins_node); ++ ++ of_property_read_u32(node, "rpi,sense", &sense); ++ ++ read_bool_property(node, "rpi,softcarrier", &softcarrier); ++ ++ read_bool_property(node, "rpi,invert", &invert); ++ ++ read_bool_property(node, "rpi,debug", &debug); ++ ++ } ++ else ++ { ++ if (gpio_in_pin >= BCM2708_NR_GPIOS || ++ gpio_out_pin >= BCM2708_NR_GPIOS) { ++ ret = -EINVAL; ++ printk(KERN_ERR LIRC_DRIVER_NAME ++ ": invalid GPIO pin(s) specified!\n"); ++ goto exit_init_port; ++ } ++ ++ if (gpio_request(gpio_out_pin, LIRC_DRIVER_NAME " ir/out")) { ++ printk(KERN_ALERT LIRC_DRIVER_NAME ++ ": cant claim gpio pin %d\n", gpio_out_pin); ++ ret = -ENODEV; ++ goto exit_init_port; ++ } ++ ++ if (gpio_request(gpio_in_pin, LIRC_DRIVER_NAME " ir/in")) { ++ printk(KERN_ALERT LIRC_DRIVER_NAME ++ ": cant claim gpio pin %d\n", gpio_in_pin); ++ ret = -ENODEV; ++ goto exit_gpio_free_out_pin; ++ } ++ ++ bcm2708_gpio_setpull(gpiochip, gpio_in_pin, gpio_in_pull); ++ gpiochip->direction_input(gpiochip, gpio_in_pin); ++ gpiochip->direction_output(gpiochip, gpio_out_pin, 1); ++ } ++ ++ gpiochip->set(gpiochip, gpio_out_pin, invert); ++ ++ irq_num = gpiochip->to_irq(gpiochip, gpio_in_pin); ++ dprintk("to_irq %d\n", irq_num); ++ ++ /* if pin is high, then this must be an active low receiver. */ ++ if (sense == -1) { ++ /* wait 1/2 sec for the power supply */ ++ msleep(500); ++ ++ /* ++ * 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, gpio_in_pin)) ++ 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 using active %s receiver on GPIO pin %d\n", ++ sense ? "low" : "high", gpio_in_pin); ++ } ++ ++ return 0; ++ ++ exit_gpio_free_out_pin: ++ gpio_free(gpio_out_pin); ++ ++ exit_init_port: ++ return ret; ++} ++ ++// called when the character device is opened ++static int set_use_inc(void *data) ++{ ++ int result; ++ ++ /* initialize timestamp */ ++ do_gettimeofday(&lasttv); ++ ++ result = request_irq(irq_num, ++ (irq_handler_t) irq_handler, ++ IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING, ++ LIRC_DRIVER_NAME, (void*) 0); ++ ++ switch (result) { ++ case -EBUSY: ++ printk(KERN_ERR LIRC_DRIVER_NAME ++ ": IRQ %d is busy\n", ++ irq_num); ++ return -EBUSY; ++ case -EINVAL: ++ printk(KERN_ERR LIRC_DRIVER_NAME ++ ": Bad irq number or handler\n"); ++ return -EINVAL; ++ default: ++ dprintk("Interrupt %d obtained\n", ++ irq_num); ++ break; ++ }; ++ ++ /* initialize pulse/space widths */ ++ init_timing_params(duty_cycle, freq); ++ ++ return 0; ++} ++ ++static void set_use_dec(void *data) ++{ ++ /* GPIO Pin Falling/Rising Edge Detect Disable */ ++ irq_set_irq_type(irq_num, 0); ++ disable_irq(irq_num); ++ ++ free_irq(irq_num, (void *) 0); ++ ++ dprintk(KERN_INFO LIRC_DRIVER_NAME ++ ": freed IRQ %d\n", irq_num); ++} ++ ++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; ++ ++ 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); ++ ++ for (i = 0; i < count; i++) { ++ if (i%2) ++ send_space(wbuf[i] - delta); ++ else ++ delta = send_pulse(wbuf[i]); ++ } ++ gpiochip->set(gpiochip, gpio_out_pin, invert); ++ ++ spin_unlock_irqrestore(&lock, flags); ++ kfree(wbuf); ++ return n; ++} ++ ++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; ++ break; ++ ++ case LIRC_GET_LENGTH: ++ return -ENOSYS; ++ break; ++ ++ case LIRC_SET_SEND_DUTY_CYCLE: ++ dprintk("SET_SEND_DUTY_CYCLE\n"); ++ result = get_user(value, (__u32 *) arg); ++ if (result) ++ return result; ++ if (value <= 0 || value > 100) ++ return -EINVAL; ++ return init_timing_params(value, freq); ++ break; ++ ++ case LIRC_SET_SEND_CARRIER: ++ dprintk("SET_SEND_CARRIER\n"); ++ result = get_user(value, (__u32 *) arg); ++ if (result) ++ return result; ++ if (value > 500000 || value < 20000) ++ return -EINVAL; ++ 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, ++ .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, ++ .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, ++}; ++ ++static const struct of_device_id lirc_rpi_of_match[] = { ++ { .compatible = "rpi,lirc-rpi", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, lirc_rpi_of_match); ++ ++static struct platform_driver lirc_rpi_driver = { ++ .driver = { ++ .name = LIRC_DRIVER_NAME, ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(lirc_rpi_of_match), ++ }, ++}; ++ ++static int __init lirc_rpi_init(void) ++{ ++ struct device_node *node; ++ int result; ++ ++ /* Init read buffer. */ ++ result = lirc_buffer_init(&rbuf, sizeof(int), RBUF_LEN); ++ if (result < 0) ++ return -ENOMEM; ++ ++ result = platform_driver_register(&lirc_rpi_driver); ++ if (result) { ++ printk(KERN_ERR LIRC_DRIVER_NAME ++ ": lirc register returned %d\n", result); ++ goto exit_buffer_free; ++ } ++ ++ node = of_find_compatible_node(NULL, NULL, ++ lirc_rpi_of_match[0].compatible); ++ ++ if (node) { ++ /* DT-enabled */ ++ lirc_rpi_dev = of_find_device_by_node(node); ++ WARN_ON(lirc_rpi_dev->dev.of_node != node); ++ of_node_put(node); ++ } ++ else { ++ lirc_rpi_dev = platform_device_alloc(LIRC_DRIVER_NAME, 0); ++ if (!lirc_rpi_dev) { ++ result = -ENOMEM; ++ goto exit_driver_unregister; ++ } ++ ++ result = platform_device_add(lirc_rpi_dev); ++ if (result) ++ goto exit_device_put; ++ } ++ ++ return 0; ++ ++ exit_device_put: ++ platform_device_put(lirc_rpi_dev); ++ ++ exit_driver_unregister: ++ platform_driver_unregister(&lirc_rpi_driver); ++ ++ exit_buffer_free: ++ lirc_buffer_free(&rbuf); ++ ++ return result; ++} ++ ++static void lirc_rpi_exit(void) ++{ ++ if (!lirc_rpi_dev->dev.of_node) ++ platform_device_unregister(lirc_rpi_dev); ++ platform_driver_unregister(&lirc_rpi_driver); ++ lirc_buffer_free(&rbuf); ++} ++ ++static int __init lirc_rpi_init_module(void) ++{ ++ int result; ++ ++ result = lirc_rpi_init(); ++ if (result) ++ return result; ++ ++ result = init_port(); ++ if (result < 0) ++ goto exit_rpi; ++ ++ driver.features = LIRC_CAN_SET_SEND_DUTY_CYCLE | ++ LIRC_CAN_SET_SEND_CARRIER | ++ LIRC_CAN_SEND_PULSE | ++ LIRC_CAN_REC_MODE2; ++ ++ driver.dev = &lirc_rpi_dev->dev; ++ 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_rpi; ++ } ++ ++ printk(KERN_INFO LIRC_DRIVER_NAME ": driver registered!\n"); ++ ++ return 0; ++ ++ exit_rpi: ++ lirc_rpi_exit(); ++ ++ return result; ++} ++ ++static void __exit lirc_rpi_exit_module(void) ++{ ++ lirc_unregister_driver(driver.minor); ++ ++ gpio_free(gpio_out_pin); ++ gpio_free(gpio_in_pin); ++ ++ lirc_rpi_exit(); ++ ++ printk(KERN_INFO LIRC_DRIVER_NAME ": cleaned up module\n"); ++} ++ ++module_init(lirc_rpi_init_module); ++module_exit(lirc_rpi_exit_module); ++ ++MODULE_DESCRIPTION("Infra-red receiver and blaster driver for Raspberry Pi GPIO."); ++MODULE_AUTHOR("Aron Robert Szabo <aron@reon.hu>"); ++MODULE_AUTHOR("Michael Bishop <cleverca22@gmail.com>"); ++MODULE_LICENSE("GPL"); ++ ++module_param(gpio_out_pin, int, S_IRUGO); ++MODULE_PARM_DESC(gpio_out_pin, "GPIO output/transmitter pin number of the BCM" ++ " processor. (default 17"); ++ ++module_param(gpio_in_pin, int, S_IRUGO); ++MODULE_PARM_DESC(gpio_in_pin, "GPIO input pin number of the BCM processor." ++ " (default 18"); ++ ++module_param(gpio_in_pull, int, S_IRUGO); ++MODULE_PARM_DESC(gpio_in_pull, "GPIO input pin pull configuration." ++ " (0 = off, 1 = up, 2 = down, default down)"); ++ ++module_param(sense, int, S_IRUGO); ++MODULE_PARM_DESC(sense, "Override autodetection of IR receiver circuit" ++ " (0 = active high, 1 = active low )"); ++ ++module_param(softcarrier, bool, S_IRUGO); ++MODULE_PARM_DESC(softcarrier, "Software carrier (0 = off, 1 = on, default on)"); ++ ++module_param(invert, bool, S_IRUGO); ++MODULE_PARM_DESC(invert, "Invert output (0 = off, 1 = on, default off"); ++ ++module_param(debug, bool, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(debug, "Enable debugging messages"); |