irblaster: add code and dts config for axg
authorZan Peng <zan.peng@amlogic.com>
Mon, 19 Jun 2017 08:31:09 +0000 (16:31 +0800)
committerPan Jianxin <jianxin.pan@amlogic.com>
Tue, 20 Jun 2017 08:18:42 +0000 (16:18 +0800)
PD#146157: irblaster : add code and dts config for axg
1. default dts config of irblater is disabled

Change-Id: I7cc4eece765d28dcac70e222bd2a36df2ba58183
Signed-off-by: Zan Peng <zan.peng@amlogic.com>
MAINTAINERS
arch/arm64/boot/dts/amlogic/axg_s400.dts
arch/arm64/boot/dts/amlogic/mesonaxg.dtsi
arch/arm64/configs/meson64_defconfig
drivers/amlogic/Kconfig
drivers/amlogic/Makefile
drivers/amlogic/irblaster/Kconfig [new file with mode: 0644]
drivers/amlogic/irblaster/Makefile [new file with mode: 0644]
drivers/amlogic/irblaster/irblaster.c [new file with mode: 0644]
drivers/amlogic/irblaster/irblaster.h [new file with mode: 0644]

index 7ea8921..caa3ae9 100644 (file)
@@ -13961,3 +13961,9 @@ AMLOGIC AXG ADD AO CLK
 M:     Yun Cai <yun.cai@amlogic.com>
 F:     drivers/amlogic/clk/axg/axg_ao.c
 
+AMLOGIC Irblaster driver
+M: Zan Peng <zan.peng@amlogic.com>
+F: drivers/amlogic/irblaster/irblaster.c
+F: drivers/amlogic/irblaster/irblaster.h
+F: drivers/amlogic/irblaster/Kconfig
+F: drivers/amlogic/irblaster/Makefile
index 0bff4a9..c2bc8ef 100644 (file)
                pinctrl-0 = <&b_uart_pins>;
        };
 
+       meson-irblaster {
+               compatible = "amlogic, am_irblaster";
+               dev_name = "meson-irblaster";
+               status = "disable";
+               pinctrl-names = "default";
+               pinctrl-0 = <&irblaster_pins>;
+       };
+
        vpu {
                compatible = "amlogic, vpu";
                dev_name = "vpu";
index a0f7a94..aa0b62d 100644 (file)
                };
        };
 
+       irblaster_pins:irblaster_pin {
+               mux {
+                       pins = "GPIOAO_7";
+                       function = "ir_out";
+               };
+       };
+
        ao_uart_pins:ao_uart {
                        mux {
                                pins = "GPIOAO_0",
        };
 
 }; /* end of pinctrl_periphs */
-
index fa40b38..3a85ed5 100644 (file)
@@ -226,6 +226,7 @@ CONFIG_AMLOGIC_ADC_KEYPADS=y
 CONFIG_AMLOGIC_SARADC=y
 CONFIG_AMLOGIC_REMOTE=y
 CONFIG_AMLOGIC_MESON_REMOTE=y
+CONFIG_AMLOGIC_IRBLASTER=y
 CONFIG_AMLOGIC_EFUSE=y
 CONFIG_AMLOGIC_REBOOT=y
 CONFIG_AMLOGIC_GX_REBOOT=y
index b3b05dc..8315bc6 100644 (file)
@@ -100,5 +100,7 @@ source "drivers/amlogic/power/Kconfig"
 
 source "drivers/amlogic/pci/Kconfig"
 
+source "drivers/amlogic/irblaster/Kconfig"
+
 endmenu
 endif
index b045c87..0a05beb 100644 (file)
@@ -83,3 +83,5 @@ obj-$(CONFIG_AMLOGIC_WIFI)      += wifi/
 obj-$(CONFIG_AMLOGIC_POWER)            += power/
 
 obj-$(CONFIG_AMLOGIC_PCIE)      += pci/
+
+obj-$(CONFIG_AMLOGIC_IRBLASTER) += irblaster/
diff --git a/drivers/amlogic/irblaster/Kconfig b/drivers/amlogic/irblaster/Kconfig
new file mode 100644 (file)
index 0000000..6c35ae8
--- /dev/null
@@ -0,0 +1,11 @@
+#
+# Input core configuration
+#
+config AMLOGIC_IRBLASTER
+       bool "Amlogic irblaster device surport"
+       default n
+       help
+         Say Y here if you want to use the amlogic irblaster.
+
+
+
diff --git a/drivers/amlogic/irblaster/Makefile b/drivers/amlogic/irblaster/Makefile
new file mode 100644 (file)
index 0000000..ae60255
--- /dev/null
@@ -0,0 +1,6 @@
+#
+# Makefile for the remote control drivers
+#
+
+# Each configuration option enables a list of files.
+obj-$(CONFIG_AMLOGIC_IRBLASTER)        += irblaster.o
diff --git a/drivers/amlogic/irblaster/irblaster.c b/drivers/amlogic/irblaster/irblaster.c
new file mode 100644 (file)
index 0000000..9d8485c
--- /dev/null
@@ -0,0 +1,668 @@
+/*
+ * drivers/amlogic/irblaster/irblaster.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/major.h>
+#include <linux/uaccess.h>
+#include <linux/cdev.h>
+#include <linux/amlogic/iomap.h>
+#include <linux/amlogic/cpu_version.h>
+#include "irblaster.h"
+#include <linux/amlogic/gpio-amlogic.h>
+#include <linux/kthread.h>
+#include <linux/kfifo.h>
+
+#include <linux/amlogic/aml_gpio_consumer.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+
+
+#define DEVICE_NAME "meson-irblaster"
+#define DEIVE_COUNT 32
+
+#define PS_SIZE 10
+
+static dev_t amirblaster_id;
+static struct class *irblaster_class;
+static struct device *irblaster_dev;
+static struct cdev amirblaster_device;
+static struct blaster_window *irblaster;
+static DEFINE_MUTEX(irblaster_file_mutex);
+static int debug_enable;
+
+struct irtx_dev {
+       struct device *dev;
+       struct task_struct      *thread;
+#ifdef CONFIG_IRBLASTER_ENABLE_PIN
+       int enable_pin;
+#endif
+};
+
+
+#define IR_TX_EVENT_SIZE 4
+#define IR_TX_BUFFER_SIZE 1024
+
+struct tx_event {
+       struct list_head list;
+       int size;
+       int buffer[IR_TX_BUFFER_SIZE];
+};
+
+DECLARE_KFIFO(fifo, struct tx_event *, IR_TX_EVENT_SIZE);
+static struct irtx_dev *tx_dev;
+
+#define irblaster_dbg(format, arg...)     \
+do {                                        \
+       if (debug_enable)        \
+               pr_info(format, ##arg);         \
+} while (0)
+
+static struct tx_event *event_get(void)
+{
+       struct tx_event *ev = NULL;
+
+       ev = kzalloc(sizeof(struct tx_event), GFP_KERNEL);
+       irblaster_dbg("event_get ev=0x%p\n", ev);
+       return ev;
+}
+
+static void event_put(struct tx_event *ev)
+{
+       irblaster_dbg("event_put ev=0x%p\n", ev);
+       kfree(ev);
+}
+
+static int send_bit(unsigned int hightime, unsigned int lowtime,
+       unsigned int cycle)
+{
+       unsigned int count_delay;
+       uint32_t val;
+       int n = 0;
+       int tb[3] = {
+               1, 10, 100
+       };
+       /*
+        *MODULATOR_TB:
+        *      00:     system clock clk
+        *      01:     mpeg_xtal3_tick
+        *      10:     mpeg_1uS_tick
+        *      11:     mpeg_10uS_tick
+        * lowtime<1024,n=0,timebase=1us
+        * 1024<=lowtime<10240,n=1,timebase=10us
+        * AO_IR_BLASTER_ADDR2
+        * bit12: output level(or modulation enable/disable:1=enable)
+        * bit[11:10]: Timebase :
+        *                      00=1us
+        *                      01=10us
+        *                      10=100us
+        *                      11=Modulator clock
+        * bit[9:0]: Count of timebase units to delay
+        */
+       count_delay = (((hightime + cycle/2) / cycle) - 1) & 0x3ff;
+       val = (0x10000 | (1 << 12)) | (3 << 10) | (count_delay << 0);
+       aml_write_aobus(AO_IR_BLASTER_ADDR2, val);
+
+       /*
+        * lowtime<1024,n=0,timebase=1us
+        * 1024<=lowtime<10240,n=1,timebase=10us
+        * 10240<=lowtime,n=2,timebase=100us
+        */
+       n = lowtime >> 10;
+       if (n > 0 && n < 10)
+               n = 1;
+       else if (n >= 10)
+               n = 2;
+       lowtime = (lowtime + (tb[n] >> 1))/tb[n];
+       count_delay = (lowtime-1) & 0x3ff;
+       val = (0x10000 | (0 << 12)) |
+               (n << 10) | (count_delay << 0);
+       aml_write_aobus(AO_IR_BLASTER_ADDR2, val);
+       return 0;
+}
+
+static void send_all_frame(struct blaster_window *cw)
+{
+       int i, k;
+       int exp = 0x00;
+       unsigned int *pData;
+       unsigned int consumerir_cycle;
+       unsigned int high_ct, low_ct;
+       unsigned long cnt;
+
+       irblaster_dbg("cw->winnum = %d\n", cw->winnum);
+       irblaster_dbg("cw->winarray = ");
+       for (i = 0; i < cw->winnum; i++) {
+               irblaster_dbg("%d,", cw->winarray[i]);
+               if (i % 10 == 9)
+                       irblaster_dbg("\n");
+       }
+       irblaster_dbg("\n");
+       consumerir_cycle = 1000 / (irblaster->consumerir_freqs / 1000);
+
+       /*reset*/
+       aml_write_aobus(AO_RTI_GEN_CTNL_REG0,
+               aml_read_aobus(AO_RTI_GEN_CTNL_REG0) | (1 << 23));
+       udelay(2);
+       aml_write_aobus(AO_RTI_GEN_CTNL_REG0,
+               aml_read_aobus(AO_RTI_GEN_CTNL_REG0) & ~(1 << 23));
+
+       /*
+       *1.disable ir blaster
+       *2.set the modulator_tb = 2'10; mpeg_1uS_tick 1us
+       */
+       aml_write_aobus(AO_IR_BLASTER_ADDR0, (1 << 2) | (2 << 12) | (1<<2));
+       /*
+        * 1. set mod_high_count = 13
+        * 2. set mod_low_count = 13
+        * 3. 60khz 8, 38k-13us, 12
+        */
+       high_ct = consumerir_cycle * irblaster->consumerir_dutycycle/100;
+       low_ct = consumerir_cycle - high_ct;
+       aml_write_aobus(AO_IR_BLASTER_ADDR1,
+               ((high_ct - 1) << 16) | ((low_ct - 1) << 0));
+
+       /* Setting this bit to 1 initializes the output to be high.*/
+       aml_write_aobus(AO_IR_BLASTER_ADDR0,
+               aml_read_aobus(AO_IR_BLASTER_ADDR0) & ~(1 << 2));
+
+       /*enable irblaster*/
+       aml_write_aobus(AO_IR_BLASTER_ADDR0,
+               aml_read_aobus(AO_IR_BLASTER_ADDR0) | (1 << 0));
+       k = cw->winnum;
+#define SEND_BIT_NUM 64
+       exp = cw->winnum / SEND_BIT_NUM;
+       pData = cw->winarray;
+
+       while (exp) {
+               for (i = 0; i < SEND_BIT_NUM/2; i++) {
+                       send_bit(*pData, *(pData+1), consumerir_cycle);
+                       pData += 2;
+               }
+               cnt = jiffies + msecs_to_jiffies(1000);
+               while (!(aml_read_aobus(AO_IR_BLASTER_ADDR0) & (1<<24)) &&
+                       time_is_after_eq_jiffies(cnt))
+                       ;
+               cnt = jiffies + msecs_to_jiffies(1000);
+               while ((aml_read_aobus(AO_IR_BLASTER_ADDR0) & (1<<26)) &&
+                       time_is_after_eq_jiffies(cnt))
+                       ;
+               /*reset*/
+               aml_write_aobus(AO_RTI_GEN_CTNL_REG0,
+                       aml_read_aobus(AO_RTI_GEN_CTNL_REG0) | (1 << 23));
+               udelay(2);
+               /*reset*/
+               aml_write_aobus(AO_RTI_GEN_CTNL_REG0,
+                       aml_read_aobus(AO_RTI_GEN_CTNL_REG0) & ~(1 << 23));
+               exp--;
+       }
+       exp = (cw->winnum % SEND_BIT_NUM) & (~(1));
+       for (i = 0; i < exp; ) {
+               send_bit(*pData, *(pData+1), consumerir_cycle);
+               pData += 2;
+               i += 2;
+       }
+
+       irblaster_dbg("The all frame finished !!\n");
+}
+
+static int ir_tx_thread(void *data)
+{
+#ifdef CONFIG_IRBLASTER_ENABLE_PIN
+       struct irtx_dev *dev = (struct irtx_dev *)data;
+#endif
+       struct tx_event *ev = NULL;
+       int retval;
+       int i;
+       unsigned long cnt;
+
+       while (!kthread_should_stop()) {
+               irblaster_dbg("wakeup ir_tx_thread\n");
+               retval = kfifo_len(&fifo);
+               if (retval <= 0) {
+                       set_current_state(TASK_INTERRUPTIBLE);
+                       if (kthread_should_stop())
+                               set_current_state(TASK_RUNNING);
+                       schedule();
+                       continue;
+               }
+               retval = kfifo_get(&fifo, &ev);
+               irblaster_dbg("retval=%d,ev=%p\n", retval, ev);
+               if (retval) {
+                       irblaster->winnum = ev->size;
+                       for (i = 0; i < irblaster->winnum; i++)
+                               irblaster->winarray[i] = ev->buffer[i];
+                       irblaster_dbg("send_all_frame.size=%d\n", ev->size);
+#ifdef CONFIG_IRBLASTER_ENABLE_PIN
+                       gpio_direction_output(dev->enable_pin, 1);
+#endif
+                       send_all_frame(irblaster);
+                       event_put(ev);
+                       cnt = jiffies + msecs_to_jiffies(1000);
+                       while (!(aml_read_aobus(AO_IR_BLASTER_ADDR0) &
+                               (1<<24)) && time_is_after_eq_jiffies(cnt))
+                               ;
+                       cnt = jiffies + msecs_to_jiffies(1000);
+                       while ((aml_read_aobus(AO_IR_BLASTER_ADDR0) &
+                               (1<<26)) && time_is_after_eq_jiffies(cnt))
+                               ;
+#ifdef CONFIG_IRBLASTER_ENABLE_PIN
+                       gpio_direction_output(dev->enable_pin, 0);
+#endif
+               } else
+                       pr_err("kfifo_get fail\n");
+       }
+
+       return 0;
+}
+
+ /*
+  * Function to set the irblaster Carrier Frequency,
+  * The modulator is typically run between 32khz and 56khz.
+  *
+  * @param[in] pointer to irblaster structure.
+  * @param[in] carrirer freqs value.
+  * \return Reuturns 0 on success else return the error value.
+  */
+int set_consumerir_freqs(struct blaster_window *irblaster, int consumerir_freqs)
+{
+       return  ((irblaster->consumerir_freqs = consumerir_freqs) >= 32000
+                && (irblaster->consumerir_freqs <= 56000)) ? 0 : -1;
+}
+
+ /*
+  * Function to get the irblaster cur Carrier Frequency.
+  *
+  * @param[in] pointer to irblaster structure.
+  * return Reuturns freqs.
+  */
+
+static int  get_consumerir_freqs(struct blaster_window *irblaster)
+{
+       return irblaster->consumerir_freqs;
+}
+
+static int  set_duty_cycle(int duty_cycle)
+{
+       if (duty_cycle > 100 || duty_cycle < 0)
+               return -1;
+       irblaster->consumerir_dutycycle = duty_cycle;
+       return 0;
+}
+
+static int aml_irblaster_open(struct inode *inode, struct file *file)
+{
+       irblaster_dbg("aml_irblaster_open()\n");
+       return 0;
+}
+
+int send(const char *buf, int len)
+{
+       /*alloc memory*/
+       struct tx_event *ev;
+       int i = 0, j = 0, m = 0, ret = 0;
+       int val;
+       char tone[PS_SIZE];
+       struct irtx_dev *irdev = tx_dev;
+
+       ev = event_get();
+       if (!ev) {
+               pr_info("please wait for send\n");
+               return -ENOMEM;
+       }
+       for (i = 0; i < len; i++) {
+               if (buf[i] == 's') {
+                       tone[j] = '\0';
+                       ret = kstrtoint(tone, 10, &val);
+                       pr_info("val is %d", val);
+                       ev->buffer[m] = val * 10;
+                       j = 0;
+                       m++;
+                       if (m >= IR_TX_BUFFER_SIZE)
+                               break;
+                       continue;
+               }
+               tone[j] = buf[i];
+               j++;
+
+               if (j >= PS_SIZE) {
+                       pr_err("send timing value is out of range\n");
+                       return -ENOMEM;
+               }
+       }
+       ev->size = m;
+       /*to send cycle to,*/
+       kfifo_put(&fifo, (const struct tx_event *)ev);
+       /*to wake up ir_tx_thread*/
+       wake_up_process(irdev->thread);
+       return 0;
+}
+
+static long aml_irblaster_ioctl(struct file *filp, unsigned int cmd,
+                               unsigned long args)
+{
+
+       int consumerir_freqs = 0, duty_cycle = 0;
+       s32 r = 0;
+       char sendcode[MAX_PLUSE];
+       void __user *argp = (void __user *)args;
+
+       irblaster_dbg("aml_irblaster_ioctl()  0x%4x\n ", cmd);
+       switch (cmd) {
+       case CONSUMERIR_TRANSMIT:
+               if (copy_from_user(sendcode, (char *)argp,
+                                       strlen((char *)argp)))
+                       return -EFAULT;
+               pr_info("send code is %s\n", sendcode);
+               r = send(sendcode, strlen(argp));
+               break;
+       case GET_CARRIER:
+               pr_info("in get freq\n");
+               consumerir_freqs = get_consumerir_freqs(irblaster);
+               put_user(consumerir_freqs, (int *)argp);
+               return consumerir_freqs;
+       case SET_CARRIER:
+               pr_info("in set freq\n");
+               get_user(consumerir_freqs, (int *)argp);
+               r = set_consumerir_freqs(irblaster, consumerir_freqs);
+               break;
+       case SET_DUTYCYCLE:
+               pr_info("in set duty_cycle\n");
+               if (copy_from_user(&duty_cycle, argp, sizeof(int)))
+                       return -EFAULT;
+               get_user(duty_cycle, (int *)argp);
+               r = set_duty_cycle(duty_cycle);
+               break;
+
+       default:
+               r = -ENOIOCTLCMD;
+               break;
+       }
+
+       return r;
+}
+static int aml_irblaster_release(struct inode *inode, struct file *file)
+{
+       return 0;
+}
+
+static ssize_t show_debug(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       if (debug_enable)
+               sprintf(buf, "debug=enable\n");
+       else
+               sprintf(buf, "debug=disable\n");
+       return strlen(buf);
+}
+
+static ssize_t store_debug(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+       if (!strncmp(buf, "enable", 1)) {
+               debug_enable = 1;
+               pr_info("enable debug\n");
+       } else if (!strncmp(buf, "disable", 1)) {
+               debug_enable = 0;
+               pr_info("disable debug\n");
+       }
+       return strlen(buf);
+}
+
+static ssize_t store_carrier_freq(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+       int ret;
+
+       mutex_lock(&irblaster->lock);
+       ret = kstrtoint(buf, 10, &irblaster->consumerir_freqs);
+       if (ret) {
+               pr_err("IR_OUT: Invalid input for carrier_freq\n");
+               return ret;
+       }
+       irblaster_dbg("carrier_freq is %d\n", irblaster->consumerir_freqs);
+       mutex_unlock(&irblaster->lock);
+       return strlen(buf);
+}
+
+static ssize_t store_duty_cycle(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+       int ret;
+
+       mutex_lock(&irblaster->lock);
+       ret = kstrtoint(buf, 10, &irblaster->consumerir_dutycycle);
+       if (ret) {
+               pr_err("IR_OUT: Invalid input for carrier_freq\n");
+               return ret;
+       }
+       irblaster_dbg("duty_cycle is %d\n", irblaster->consumerir_dutycycle);
+       mutex_unlock(&irblaster->lock);
+       return strlen(buf);
+}
+
+static ssize_t show_log(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       memset(buf, 0, PAGE_SIZE);
+       return strlen(buf);
+}
+
+static ssize_t show_send_value(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       memset(buf, 0, PAGE_SIZE);
+       return strlen(buf);
+}
+
+static ssize_t store_send(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+       /*alloc memory*/
+       struct tx_event *ev;
+       int i = 0, j = 0, m = 0, ret = 0;
+       int val;
+       char tone[PS_SIZE];
+       struct irtx_dev *irdev = dev_get_drvdata(dev);
+
+       ev = event_get();
+       if (!ev) {
+               pr_err("please wait for send\n");
+               return -ENOMEM;
+       }
+       while (buf[i] != '\0') {
+               if (buf[i] == 's') {
+                       tone[j] = '\0';
+                       ret = kstrtoint(tone, 10, &val);
+                       ev->buffer[m] = val * 10;
+                       j = 0;
+                       i++;
+                       m++;
+                       if (m >= IR_TX_BUFFER_SIZE)
+                               break;
+                       continue;
+               }
+               tone[j] = buf[i];
+               i++;
+               j++;
+               if (j >= PS_SIZE) {
+                       pr_err("send timing value is out of range\n");
+                       return -ENOMEM;
+               }
+       }
+       ev->size = m;
+       /*to send cycle to,*/
+       kfifo_put(&fifo, (const struct tx_event *)ev);
+       /*to wake up ir_tx_thread*/
+       wake_up_process(irdev->thread);
+       return count;
+}
+
+static DEVICE_ATTR(debug, 0644, show_debug, store_debug);
+static DEVICE_ATTR(log, 0444, show_log, NULL);
+static DEVICE_ATTR(sendvalue, 0444, show_send_value, NULL);
+static DEVICE_ATTR(send, 0200, NULL, store_send);
+static DEVICE_ATTR(carrier_freq, 0200, NULL, store_carrier_freq);
+static DEVICE_ATTR(duty_cycle, 0200, NULL, store_duty_cycle);
+
+static const struct file_operations aml_irblaster_fops = {
+       .owner          = THIS_MODULE,
+       .open           = aml_irblaster_open,
+       .compat_ioctl = aml_irblaster_ioctl,
+       .unlocked_ioctl = aml_irblaster_ioctl,
+       .release        = aml_irblaster_release,
+};
+
+static int  aml_irblaster_probe(struct platform_device *pdev)
+{
+       int r;
+       struct irtx_dev *dev;
+       struct pinctrl *p;
+
+       pr_info("irblaster probe\n");
+       dev = kzalloc(sizeof(struct irtx_dev), GFP_KERNEL);
+       if (!dev) {
+               pr_info("");
+               return -ENOMEM;
+       }
+
+       irblaster = kzalloc(sizeof(struct blaster_window), GFP_KERNEL);
+       if (irblaster == NULL)
+               return -1;
+
+       irblaster->consumerir_freqs = 38000;
+       irblaster->consumerir_dutycycle = 50;
+
+       if (!pdev->dev.of_node) {
+               pr_info("aml_irblaster: pdev->dev.of_node == NULL!\n");
+               return -1;
+       }
+
+       p = devm_pinctrl_get_select_default(&pdev->dev);
+       if (IS_ERR(p)) {
+               dev_err(&pdev->dev, "pinctrl error, %ld\n", PTR_ERR(p));
+               return -1;
+       }
+
+       r = alloc_chrdev_region(&amirblaster_id, 0, DEIVE_COUNT, DEVICE_NAME);
+       if (r < 0) {
+               pr_err("Can't register major for ir irblaster device\n");
+               return r;
+       }
+       cdev_init(&amirblaster_device, &aml_irblaster_fops);
+       amirblaster_device.owner = THIS_MODULE;
+       cdev_add(&(amirblaster_device), amirblaster_id, DEIVE_COUNT);
+       irblaster_class = class_create(THIS_MODULE, DEVICE_NAME);
+       if (IS_ERR(irblaster_class)) {
+               unregister_chrdev_region(amirblaster_id, DEIVE_COUNT);
+               pr_info("Can't create class for ir irblaster device\n");
+               return -1;
+       }
+       irblaster_dev = device_create(irblaster_class, NULL,
+                                       amirblaster_id, dev,
+                                     "irblaster%d", 1);
+       if (irblaster_dev == NULL) {
+               pr_err("irblaster_dev create error\n");
+               class_destroy(irblaster_class);
+               return -EEXIST;
+       }
+
+       mutex_init(&irblaster->lock);
+       device_create_file(irblaster_dev, &dev_attr_debug);
+       device_create_file(irblaster_dev, &dev_attr_log);
+       device_create_file(irblaster_dev, &dev_attr_sendvalue);
+       device_create_file(irblaster_dev, &dev_attr_send);
+       device_create_file(irblaster_dev, &dev_attr_duty_cycle);
+       device_create_file(irblaster_dev, &dev_attr_carrier_freq);
+       INIT_KFIFO(fifo);
+       dev->thread = kthread_run(ir_tx_thread, dev,
+                "ir-blaster-thread");
+
+#ifdef CONFIG_IRBLASTER_ENABLE_PIN
+       dev->enable_pin = of_get_named_gpio(pdev->dev.of_node, "enable_pin", 0);
+       gpio_request(dev->enable_pin, "ir enable pin");
+       gpio_direction_output(dev->enable_pin, 0);
+#endif
+       tx_dev = dev;
+       return 0;
+}
+
+static int aml_irblaster_remove(struct platform_device *pdev)
+{
+       pr_info("remove IRBLASTER\n");
+       device_remove_file(irblaster_dev, &dev_attr_debug);
+       device_remove_file(irblaster_dev, &dev_attr_log);
+       device_remove_file(irblaster_dev, &dev_attr_sendvalue);
+       device_remove_file(irblaster_dev, &dev_attr_send);
+       device_remove_file(irblaster_dev, &dev_attr_carrier_freq);
+       device_remove_file(irblaster_dev, &dev_attr_duty_cycle);
+       kfree(irblaster);
+       cdev_del(&amirblaster_device);
+       device_destroy(irblaster_class, amirblaster_id);
+       class_destroy(irblaster_class);
+       unregister_chrdev_region(amirblaster_id, DEIVE_COUNT);
+       return 0;
+}
+static const struct of_device_id irblaster_dt_match[] = {
+       {
+               .compatible     = "amlogic, am_irblaster",
+       },
+       {},
+};
+static struct platform_driver aml_irblaster_driver = {
+       .probe          = aml_irblaster_probe,
+       .remove         = aml_irblaster_remove,
+       .suspend        = NULL,
+       .resume         = NULL,
+       .driver = {
+               .name = "meson-irblaster",
+               .owner  = THIS_MODULE,
+               .of_match_table = irblaster_dt_match,
+       },
+};
+
+static int __init aml_irblaster_init(void)
+{
+       pr_info("BLASTER Driver Init\n");
+       if (platform_driver_register(&aml_irblaster_driver)) {
+               irblaster_dbg("failed to register aml_ir_irblaster_driver module\n");
+               return -ENODEV;
+       }
+       return 0;
+}
+
+static void __exit aml_irblaster_exit(void)
+{
+       pr_info("IRBLASTER Driver exit\n");
+       platform_driver_unregister(&aml_irblaster_driver);
+}
+module_init(aml_irblaster_init);
+module_exit(aml_irblaster_exit);
+
+MODULE_AUTHOR("platform-beijing");
+MODULE_DESCRIPTION("Irblaster Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/amlogic/irblaster/irblaster.h b/drivers/amlogic/irblaster/irblaster.h
new file mode 100644 (file)
index 0000000..6d0633d
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * drivers/amlogic/irblaster/irblaster.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#ifndef AM_IRBLASTER_H
+#define AM_IRBLASTER_H
+#define MAX_PLUSE 1024
+
+enum  {        /* Modulation level*/
+       fisrt_low  = 0,
+       fisrt_high = 1
+};
+
+struct blaster_window {
+       unsigned int winnum;
+       unsigned int winarray[MAX_PLUSE];
+       int consumerir_freqs;
+       unsigned int consumerir_dutycycle;
+       struct mutex lock;
+};
+
+#define AO_IR_BLASTER_ADDR0 ((0x00 << 10) | (0x30 << 2))
+#define AO_IR_BLASTER_ADDR1 ((0x00 << 10) | (0x31 << 2))
+#define AO_IR_BLASTER_ADDR2 ((0x00 << 10) | (0x32 << 2))
+#define AO_RTI_PIN_MUX_REG             (0x14)
+#define AO_RTI_GEN_CTNL_REG0   (0x40)
+
+#define CONSUMERIR_TRANSMIT     0x5500
+#define GET_CARRIER         0x5501
+#define SET_CARRIER         0x5502
+#define SET_DUTYCYCLE         0x5503
+#endif