| /* |
| * 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 = devm_kzalloc(tx_dev->dev, |
| 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); |
| devm_kfree(tx_dev->dev, 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"); |
| event_put(ev); |
| 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; |
| void __user *argp = (void __user *)args; |
| |
| sendcode = kzalloc(MAX_PLUSE, GFP_KERNEL); |
| if (!sendcode) |
| return -ENOMEM; |
| |
| irblaster_dbg("aml_irblaster_ioctl() 0x%4x\n ", cmd); |
| switch (cmd) { |
| case CONSUMERIR_TRANSMIT: |
| if (copy_from_user(sendcode, (char *)argp, |
| strlen((char *)argp))) { |
| kfree(sendcode); |
| 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); |
| kfree(sendcode); |
| 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))) { |
| kfree(sendcode); |
| return -EFAULT; |
| } |
| get_user(duty_cycle, (int *)argp); |
| r = set_duty_cycle(duty_cycle); |
| break; |
| |
| default: |
| r = -ENOIOCTLCMD; |
| break; |
| } |
| |
| kfree(sendcode); |
| 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"); |
| event_put(ev); |
| 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 = 0; |
| struct irtx_dev *dev; |
| struct pinctrl *p; |
| |
| pr_info("irblaster probe\n"); |
| dev = devm_kzalloc(&pdev->dev, |
| sizeof(struct irtx_dev), GFP_KERNEL); |
| if (!dev) { |
| pr_info("kzalloc failed\n"); |
| return -ENOMEM; |
| } |
| |
| irblaster = devm_kzalloc(&pdev->dev, |
| 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; |
| r = cdev_add(&(amirblaster_device), amirblaster_id, DEIVE_COUNT); |
| if (r) { |
| pr_err("failed to add cdev\n"); |
| return r; |
| } |
| 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"); |