ir: add ir learning function [1/1]
authorQianggui Song <qianggui.song@amlogic.com>
Fri, 18 Jan 2019 03:26:47 +0000 (11:26 +0800)
committerTao Zeng <tao.zeng@amlogic.com>
Wed, 24 Apr 2019 06:44:20 +0000 (23:44 -0700)
PD#SWPL-4130

Problem:
No ir learning function in current source code

Solution:
Provide two ways to use ir learning function
1. sysfs
/*start learning*/
echo 1 > /sys/class/remote/amremote/ir_learnning
/*get data*/
cat /sys/class/remote/amremote/learned_pulse
/*stop learning*/
echo 0 > /sys/class/remote/amremote/ir_learnning
2. ioctl
/*start/stop learnning 1/0*/
REMOTE_IOC_SET_IR_LEARING
/*get raw data*/
REMOTE_IOC_GET_RAW_DATA

Verify:
U200 and W400

Change-Id: Ibb03838402d9baa6e910b3162ffcc63b57048890
Signed-off-by: Qianggui Song <qianggui.song@amlogic.com>
drivers/amlogic/input/remote/rc_common.h
drivers/amlogic/input/remote/remote_cdev.c
drivers/amlogic/input/remote/remote_core.h
drivers/amlogic/input/remote/remote_meson.c
drivers/amlogic/input/remote/remote_meson.h
drivers/amlogic/input/remote/remote_regmap.c
drivers/amlogic/input/remote/sysfs.c

index 3420355..fdc6b8f 100644 (file)
@@ -86,5 +86,10 @@ struct ir_sw_decode_para {
 #define REMOTE_IOC_SET_KEY_MAPPING_TAB   _IOW('I', 4, __u32)
 #define REMOTE_IOC_SET_SW_DECODE_PARA    _IOW('I', 5, __u32)
 #define REMOTE_IOC_GET_DATA_VERSION      _IOR('I', 121, __u32)
+#define REMOTE_IOC_SET_IR_LEARNING       _IOW('I', 6, __u32)
+#define REMOTE_IOC_GET_IR_LEARNING       _IOR('I', 7, __u32)
+#define REMOTE_IOC_GET_RAW_DATA          _IOR('I', 8, __u32)
+#define REMOTE_IOC_GET_SUM_CNT0          _IOR('I', 9, __u32)
+#define REMOTE_IOC_GET_SUM_CNT1          _IOR('I', 10, __u32)
 
 #endif
index 60d1be2..ea996e3 100644 (file)
@@ -48,6 +48,7 @@ static long remote_ioctl(struct file *file, unsigned int cmd,
        void __user *parg = (void __user *)arg;
        unsigned long flags;
        u32 value;
+       u8 val;
        int retval = 0;
 
        if (!parg) {
@@ -126,6 +127,93 @@ static long remote_ioctl(struct file *file, unsigned int cmd,
                }
                chip->r_dev->max_frame_time = sw_data.max_frame_time;
                break;
+       case REMOTE_IOC_GET_IR_LEARNING:
+               if (copy_to_user(parg, &chip->r_dev->ir_learning_on,
+                                sizeof(u8))) {
+                       retval = -EFAULT;
+                       goto err;
+               }
+               break;
+
+       case REMOTE_IOC_SET_IR_LEARNING:
+               /*reset demudolation and carrier detect*/
+               if (chip->r_dev->demod_enable)
+                       demod_reset(chip);
+
+               if (copy_from_user(&val, parg, sizeof(u8))) {
+                       retval = -EFAULT;
+                       goto err;
+               }
+
+               chip->r_dev->ir_learning_on = !!val;
+               if (!!val) {
+                       if (remote_pulses_malloc(chip) < 0) {
+                               retval = -ENOMEM;
+                               goto err;
+                       }
+                       chip->set_register_config(chip, REMOTE_TYPE_RAW_NEC);
+                       /*backup protocol*/
+                       chip->r_dev->protocol = chip->protocol;
+                       chip->protocol = REMOTE_TYPE_RAW_NEC;
+               } else {
+                       chip->protocol = chip->r_dev->protocol;
+                       chip->set_register_config(chip, chip->protocol);
+                       remote_pulses_free(chip);
+                       chip->r_dev->ir_learning_done = false;
+               }
+               break;
+
+       case REMOTE_IOC_GET_RAW_DATA:
+               if (copy_to_user(parg, chip->r_dev->pulses,
+                       sizeof(struct pulse_group) +
+                       chip->r_dev->max_learned_pulse *
+                       sizeof(unsigned int))) {
+                       retval = -EFAULT;
+                       goto err;
+               }
+               /*clear to prepear next frame*/
+               memset(chip->r_dev->pulses, 0, sizeof(struct pulse_group) +
+                       chip->r_dev->max_learned_pulse *
+                       sizeof(unsigned int));
+
+               if (chip->r_dev->demod_enable)
+                       demod_reset(chip);
+
+               /*finish reading data ,enable state machine */
+               remote_reg_update_bits(chip, MULTI_IR_ID, REG_REG1, BIT(15),
+                                      BIT(15));
+
+               chip->r_dev->ir_learning_done = false;
+
+               break;
+
+       case REMOTE_IOC_GET_SUM_CNT0:
+               if (chip->r_dev->demod_enable) {
+                       remote_reg_read(chip, MULTI_IR_ID, REG_DEMOD_SUM_CNT0,
+                                       &value);
+                       if (copy_to_user(parg, &value, sizeof(u32))) {
+                               retval = -EFAULT;
+                               goto err;
+                       }
+               } else {
+                       retval = -EFAULT;
+                       goto err;
+               }
+               break;
+
+       case REMOTE_IOC_GET_SUM_CNT1:
+               if (chip->r_dev->demod_enable) {
+                       remote_reg_read(chip, MULTI_IR_ID, REG_DEMOD_SUM_CNT1,
+                                       &value);
+                       if (copy_to_user(parg, &value, sizeof(u32))) {
+                               retval = -EFAULT;
+                               goto err;
+                       }
+               } else {
+                       retval = -EFAULT;
+                       goto err;
+               }
+               break;
 
        default:
                retval = -ENOTTY;
index 6b2d623..b0f7251 100644 (file)
@@ -58,7 +58,11 @@ enum raw_event_type {
        RAW_STOP_EVENT   = (1 << 3),
 };
 
-
+struct pulse_group {
+       int len;
+       /*bit 0-30 durations, bit31: level*/
+       unsigned int pulse[0];
+};
 
 struct remote_raw_handle;
 struct remote_dev {
@@ -75,6 +79,19 @@ struct remote_dev {
        unsigned long delay_off;
        int led_blink;
 
+       /*for ir learnning feature*/
+#define MAX_LEARNED_PULSE      256
+       /*ir learnning switch*/
+       u8 ir_learning_on;
+       u8 ir_learning_done;
+       u8 demod_enable;
+       u8 use_fifo;
+       u8 auto_report;
+       int max_learned_pulse;
+       int protocol;
+       struct timer_list learning_done;
+       struct pulse_group *pulses;
+
        struct timer_list timer_keyup;
        unsigned long keyup_jiffies;
        unsigned long keyup_delay;
index 6a7e416..ea1f0ca 100644 (file)
 #include <linux/pm_wakeirq.h>
 
 static void amlremote_tasklet(unsigned long data);
-
+static void learning_done_workqueue(struct work_struct *work);
+static void get_fifo_data_work(struct work_struct *work);
 
 DECLARE_TASKLET_DISABLED(tasklet, amlremote_tasklet, 0);
 
+static void learning_done_workqueue(struct work_struct *work)
+{
+       struct delayed_work *w = container_of(work, struct delayed_work, work);
+       struct remote_chip *chip =
+               container_of(w, struct remote_chip, ir_workqueue);
+       char *envp[2] = { "LEARN_DONE", NULL};
+
+       kobject_uevent_env(&chip->dev->kobj, KOBJ_CHANGE, envp);
+}
+
+int remote_pulses_malloc(struct remote_chip *chip)
+{
+       struct remote_dev *r_dev = chip->r_dev;
+       int len = r_dev->max_learned_pulse;
+       int ret = 0;
+
+       if (r_dev->pulses) {
+               dev_info(chip->dev, "ir learning pulse already exists\n");
+               return -EEXIST;
+       }
+
+       r_dev->pulses = kzalloc(sizeof(struct pulse_group) +
+                       len * sizeof(unsigned int), GFP_KERNEL);
+
+       if (!r_dev->pulses) {
+               dev_err(chip->dev, "ir learning pulse alloc err\n");
+               ret = -ENOMEM;
+       }
+
+       return ret;
+}
+
+void remote_pulses_free(struct remote_chip *chip)
+{
+       struct remote_dev *r_dev = chip->r_dev;
+
+       kfree(r_dev->pulses);
+       r_dev->pulses = NULL;
+}
+
 int remote_reg_read(struct remote_chip *chip, unsigned char id,
        unsigned int reg, unsigned int *val)
 {
@@ -73,6 +114,19 @@ int remote_reg_write(struct remote_chip *chip, unsigned char id,
        return 0;
 }
 
+int remote_reg_update_bits(struct remote_chip *chip, unsigned char id,
+       unsigned int reg, unsigned int mask, unsigned int val)
+{
+       int orig = 0;
+
+       remote_reg_read(chip, id, reg, &orig);
+       orig &= ~mask;
+       orig |= val & mask;
+       remote_reg_write(chip, id, reg, orig);
+
+       return 0;
+}
+
 int ir_scancode_sort(struct ir_map_tab *ir_map)
 {
        bool is_sorted;
@@ -300,9 +354,64 @@ static void amlremote_tasklet(unsigned long data)
 
 }
 
+static void get_fifo_data_work(struct work_struct *work)
+{
+       struct remote_chip *chip =
+               container_of(work, struct remote_chip, fifo_work);
+       struct remote_dev *r_dev = chip->r_dev;
+       int val = 0;
+       int is_fifo_pending = 0;
+       int is_fifo_timeout = 0;
+       int is_fifo_empty = 0;
+       u32 duration = 0;
+
+       remote_reg_read(chip, MULTI_IR_ID, REG_FIFO, &val);
+       is_fifo_pending = (val >> 30) & 0x01;
+       is_fifo_timeout = (val >> 29) & 0x01;
+       is_fifo_empty = (val >> 27) & 0x01;
+
+       /*disable  interrupt*/
+       remote_reg_update_bits(chip, MULTI_IR_ID, REG_FIFO, GENMASK(22, 23), 0);
+
+       if (is_fifo_pending || is_fifo_timeout) {
+               /*clear state*/
+               remote_reg_update_bits(chip, MULTI_IR_ID, REG_FIFO,
+                                      GENMASK(29, 30), GENMASK(29, 30));
+
+               for (; !is_fifo_empty; ) {
+
+                       remote_reg_read(chip, MULTI_IR_ID, REG_WITH, &val);
+                       val = val & GENMASK(12, 0);
+
+                       duration = val;
+                       r_dev->pulses->pulse[r_dev->pulses->len] = duration;
+
+                       if (r_dev->pulses->len % 2 == 1)
+                               r_dev->pulses->pulse[r_dev->pulses->len]
+                                       |= BIT(31);
+
+                       r_dev->pulses->len++;
+
+                       remote_reg_read(chip, MULTI_IR_ID, REG_FIFO, &val);
+                       is_fifo_empty = (val >> 27) & 0x01;
+
+               }
+       }
+
+       /*enable interrupt*/
+       remote_reg_update_bits(chip, MULTI_IR_ID, REG_FIFO, GENMASK(22, 23),
+                              GENMASK(22, 23));
+
+       if (r_dev->auto_report)
+               mod_timer(&r_dev->learning_done,
+                         jiffies + msecs_to_jiffies(50));
+}
+
 static irqreturn_t ir_interrupt(int irq, void *dev_id)
 {
        struct remote_chip *rc = (struct remote_chip *)dev_id;
+       struct remote_dev *r_dev = rc->r_dev;
+       struct pulse_group *pgs;
        int contr_status = 0;
        int val = 0;
        u32 duration;
@@ -312,12 +421,40 @@ static irqreturn_t ir_interrupt(int irq, void *dev_id)
 
        remote_reg_read(rc, MULTI_IR_ID, REG_REG1, &val);
        val = (val & 0x1FFF0000) >> 16;
-       sprintf(buf, "d:%d\n", val);
+       sprintf(buf, "duration:%d\n", val);
        debug_log_printk(rc->r_dev, buf);
        /**
         *software decode multiple protocols by using Time Measurement of
         *multif-format IR controller
         */
+
+       if (r_dev->ir_learning_on && !r_dev->ir_learning_done) {
+               pgs = r_dev->pulses;
+               if (pgs->len >= r_dev->max_learned_pulse) {
+                       remote_reg_update_bits(rc, MULTI_IR_ID, REG_REG1,
+                                              BIT(15), 0);
+                       return IRQ_HANDLED;
+               }
+               if (!r_dev->use_fifo) {
+                       if (r_dev->auto_report)
+                               mod_timer(&r_dev->learning_done,
+                                         jiffies + msecs_to_jiffies(50));
+                       /*get pulse durations unit:10us*/
+                       pgs->pulse[pgs->len] = val & GENMASK(30, 0);
+                       /*get pulse level*/
+                       remote_reg_read(rc, MULTI_IR_ID, REG_STATUS, &val);
+                       val = !!((val >> 8) & 0x01);
+                       pgs->pulse[pgs->len] &= ~BIT(31);
+                       if (val)
+                               pgs->pulse[pgs->len] |= BIT(31);
+
+                       r_dev->pulses->len++;
+               } else {
+                       schedule_work(&rc->fifo_work);
+               }
+               return IRQ_HANDLED;
+       }
+
        if (MULTI_IR_SOFTWARE_DECODE(rc->protocol)) {
                rc->ir_work = MULTI_IR_ID;
                duration = val*10*1000;
@@ -505,6 +642,18 @@ static int ir_get_devtree_pdata(struct platform_device *pdev)
        }
        dev_info(chip->dev, "led_blink_frq  = %ld\n", chip->r_dev->delay_on);
 
+       ret = of_property_read_bool(pdev->dev.of_node, "demod_enable");
+       if (ret)
+               chip->r_dev->demod_enable = 1;
+
+       ret = of_property_read_bool(pdev->dev.of_node, "use_fifo");
+       if (ret)
+               chip->r_dev->use_fifo = 1;
+
+       ret = of_property_read_bool(pdev->dev.of_node, "auto_report");
+       if (ret)
+               chip->r_dev->auto_report = 1;
+
        p = devm_pinctrl_get_select_default(&pdev->dev);
        if (IS_ERR(p)) {
                dev_err(chip->dev, "pinctrl error, %ld\n", PTR_ERR(p));
@@ -551,6 +700,47 @@ static int ir_get_devtree_pdata(struct platform_device *pdev)
        return 0;
 }
 
+void demod_init(struct remote_chip *chip)
+{
+       int val;
+       unsigned int  mask;
+
+       mask = GENMASK(29, 16) | BIT(31);
+       val = BIT(31) | (0x1FF << 16);
+
+       remote_reg_update_bits(chip, MULTI_IR_ID, REG_DEMOD_CNTL1, mask, val);
+}
+
+void demod_reset(struct remote_chip *chip)
+{
+       unsigned int mask;
+
+       mask = BIT(30);
+       remote_reg_update_bits(chip, MULTI_IR_ID, REG_DEMOD_CNTL0, mask, mask);
+}
+
+static void ir_learning_done(unsigned long cookie)
+{
+
+       struct remote_dev *dev = (struct remote_dev *)cookie;
+       struct remote_chip *chip = (struct remote_chip *) dev->platform_data;
+       unsigned long flags;
+
+       if (dev->pulses->len < 3) {
+               dev->pulses->len = 0;
+               return;
+       }
+
+       spin_lock_irqsave(&chip->slock, flags);
+       dev->ir_learning_done = true;
+       spin_unlock_irqrestore(&chip->slock, flags);
+
+       /*data recive done*/
+       remote_reg_update_bits(chip, MULTI_IR_ID, REG_REG1, BIT(15), 0);
+       schedule_delayed_work(&chip->ir_workqueue, msecs_to_jiffies(100));
+
+}
+
 static int ir_hardware_init(struct platform_device *pdev)
 {
        int ret;
@@ -643,6 +833,7 @@ static int remote_probe(struct platform_device *pdev)
        chip->r_dev->set_custom_code = set_custom_code;
        chip->r_dev->is_valid_custom = is_valid_custom;
        chip->r_dev->is_next_repeat  = is_next_repeat;
+       chip->r_dev->max_learned_pulse = MAX_LEARNED_PULSE;
        chip->set_register_config = ir_register_default_config;
        platform_set_drvdata(pdev, chip);
 
@@ -666,6 +857,11 @@ static int remote_probe(struct platform_device *pdev)
 
        led_trigger_register_simple("rc_feedback", &dev->led_feedback);
 
+       setup_timer(&dev->learning_done, ir_learning_done, (unsigned long)dev);
+       if (dev->demod_enable)
+               demod_init(chip);
+       INIT_DELAYED_WORK(&chip->ir_workqueue, learning_done_workqueue);
+       INIT_WORK(&chip->fifo_work, get_fifo_data_work);
        return 0;
 
 error_register_remote:
@@ -745,6 +941,12 @@ static int remote_suspend(struct device *dev)
 {
        struct remote_chip *chip = dev_get_drvdata(dev);
 
+       if (chip->r_dev->ir_learning_on) {
+               cancel_work_sync(&chip->fifo_work);
+               del_timer_sync(&chip->r_dev->learning_done);
+               cancel_delayed_work_sync(&chip->ir_workqueue);
+       }
+
        if (is_pm_freeze_mode())
                return 0;
 
index 60437d1..aa7538a 100644 (file)
 
 #define IR_DATA_IS_VALID(data) (data & 0x8)
 #define IR_CONTROLLER_BUSY(x) ((x >> 7) & 0x1)
-
 #define CURSOR_MOVE_ACCELERATE {0, 2, 2, 4, 4, 6, 8, 10, 12, 14, 16, 18}
+/*bit 31: enable fifo mode
+ * bit 21-23: time out/level trigger interrupt
+ * bit 13-20: trigger interrupt when receive specified numbers of pulse
+ * bit 0-12: trigger interrupt when time out
+ */
+#define FIFO_REG_VAL ((1 << 31) | (7 << 21) | (80 << 13) | (5000 << 0))
 
 enum IR_CONTR_NUMBER {
        MULTI_IR_ID = 0,
@@ -104,6 +109,8 @@ struct remote_chip {
        struct remote_range reg_duration;
        char *dev_name;
        int protocol;
+       struct delayed_work ir_workqueue;
+       struct work_struct fifo_work;
 
        dev_t chr_devno;
        struct class  *chr_class;
@@ -167,27 +174,36 @@ enum {
 };
 
 enum remote_reg {
-       REG_LDR_ACTIVE = 0x00 << 2,
-       REG_LDR_IDLE   = 0x01 << 2,
-       REG_LDR_REPEAT = 0x02 << 2,
-       REG_BIT_0      = 0x03 << 2,
-       REG_REG0       = 0x04 << 2,
-       REG_FRAME      = 0x05 << 2,
-       REG_STATUS     = 0x06 << 2,
-       REG_REG1       = 0x07 << 2,
-       REG_REG2       = 0x08 << 2,
-       REG_DURATN2    = 0x09 << 2,
-       REG_DURATN3    = 0x0a << 2,
-       REG_FRAME1     = 0x0b << 2,
-       REG_STATUS1    = 0x0c << 2,
-       REG_STATUS2    = 0x0d << 2,
-       REG_REG3       = 0x0e << 2,
-       REG_FRAME_RSV0 = 0x0f << 2,
-       REG_FRAME_RSV1 = 0x10 << 2,
-       REG_FILTE      = 0x11 << 2,
-       REG_IRQ_CTL    = 0x12 << 2,
-       REG_WIDTH_NEW  = 0x14 << 2,
-       REG_REPEAT_DET = 0x15 << 2
+       REG_LDR_ACTIVE          = 0x00 << 2,
+       REG_LDR_IDLE            = 0x01 << 2,
+       REG_LDR_REPEAT          = 0x02 << 2,
+       REG_BIT_0               = 0x03 << 2,
+       REG_REG0                = 0x04 << 2,
+       REG_FRAME               = 0x05 << 2,
+       REG_STATUS              = 0x06 << 2,
+       REG_REG1                = 0x07 << 2,
+       REG_REG2                = 0x08 << 2,
+       REG_DURATN2             = 0x09 << 2,
+       REG_DURATN3             = 0x0a << 2,
+       REG_FRAME1              = 0x0b << 2,
+       REG_STATUS1             = 0x0c << 2,
+       REG_STATUS2             = 0x0d << 2,
+       REG_REG3                = 0x0e << 2,
+       REG_FRAME_RSV0          = 0x0f << 2,
+       REG_FRAME_RSV1          = 0x10 << 2,
+       REG_IRQ_CTL             = 0x12 << 2,
+       REG_FIFO                = 0x13 << 2,
+       REG_WITH                = 0x14 << 2,
+       REG_REPEAT_DET          = 0x15 << 2,
+       REG_DEMOD_CNTL0         = 0x20 << 2,
+       REG_DEMOD_CNTL1         = 0x21 << 2,
+       REG_DEMOD_IIR_THD       = 0x22 << 2,
+       REG_DEMOD_THD0          = 0x23 << 2,
+       REG_DEMOD_THD1          = 0x24 << 2,
+       REG_DEMOD_SUM_CNT0      = 0x25 << 2,
+       REG_DEMOD_SUM_CNT1      = 0x26 << 2,
+       REG_DEMOD_CNT0          = 0x27 << 2,
+       REG_DEMOD_CNT1          = 0x28 << 2
 };
 
 int ir_register_default_config(struct remote_chip *chip, int type);
@@ -199,8 +215,14 @@ int remote_reg_read(struct remote_chip *chip, unsigned char id,
        unsigned int reg, unsigned int *val);
 int remote_reg_write(struct remote_chip *chip, unsigned char id,
        unsigned int reg, unsigned int val);
+int remote_reg_update_bits(struct remote_chip *chip, unsigned char id,
+       unsigned int reg, unsigned int mask, unsigned int val);
 int ir_scancode_sort(struct ir_map_tab *ir_map);
 struct ir_map_tab_list *seek_map_tab(struct remote_chip *chip, int custom_code);
+const struct aml_remote_reg_proto **ir_get_proto_reg(void);
 void ir_tab_free(struct ir_map_tab_list *ir_map_list);
-
+int remote_pulses_malloc(struct remote_chip *chip);
+void remote_pulses_free(struct remote_chip *chip);
+void demod_reset(struct remote_chip *chip);
+void demod_init(struct remote_chip *chip);
 #endif
index 93c9697..f1b506c 100644 (file)
@@ -88,7 +88,7 @@ static struct remote_reg_map regs_default_nec_sw[] = {
        { REG_LDR_IDLE,     0},
        { REG_LDR_REPEAT,   0},
        { REG_BIT_0,        0},
-       { REG_REG0,        ((3 << 28) | (0xFA0 << 12) | (9))},
+       { REG_REG0,        ((7 << 28) | (0xFA0 << 12) | (9))},
        { REG_STATUS,       0},
        { REG_REG1,         0x8574},
        { REG_REG2,         0x02},
@@ -569,6 +569,11 @@ const struct aml_remote_reg_proto *remote_reg_proto[] = {
        NULL
 };
 
+const struct aml_remote_reg_proto **ir_get_proto_reg(void)
+{
+       return remote_reg_proto;
+}
+
 static int ir_contr_init(struct remote_chip *chip, int type, unsigned char id)
 {
        const struct aml_remote_reg_proto **reg_proto = remote_reg_proto;
@@ -623,6 +628,11 @@ static int ir_contr_init(struct remote_chip *chip, int type, unsigned char id)
        chip->ir_contr[id].get_custom_code   = (*reg_proto)->get_custom_code;
        chip->ir_contr[id].set_custom_code   = (*reg_proto)->set_custom_code;
 
+       if (chip->r_dev->ir_learning_on && chip->r_dev->use_fifo)
+               remote_reg_write(chip, id, REG_FIFO, FIFO_REG_VAL);
+       else
+               remote_reg_write(chip, id, REG_FIFO, 0);
+
        return 0;
 }
 
index b7df3e5..5bd084c 100644 (file)
@@ -43,16 +43,29 @@ static ssize_t protocol_show(struct device *dev,
        struct device_attribute *attr, char *buf)
 {
        struct remote_chip *chip = dev_get_drvdata(dev);
+       const struct aml_remote_reg_proto **supported_proto =
+                                                       ir_get_proto_reg();
+       int len;
 
        if (ENABLE_LEGACY_IR(chip->protocol))
-               return sprintf(buf, "protocol=%s&%s (0x%x)\n",
+               len =  sprintf(buf, "current protocol = %s&%s (0x%x)\n",
                        chip->ir_contr[LEGACY_IR_ID].proto_name,
                        chip->ir_contr[MULTI_IR_ID].proto_name,
                        chip->protocol);
+       else
+               len =  sprintf(buf, "currnet protocol = %s (0x%x)\n",
+                              chip->ir_contr[MULTI_IR_ID].proto_name,
+                              chip->protocol);
+
+       len += sprintf(buf + len, "supported protocol:\n");
+
+       for ( ; (*supported_proto) != NULL ; ) {
+               len += sprintf(buf + len, "%s (0x%x)\n",
+               ((*supported_proto)->name), ((*supported_proto)->protocol));
+               supported_proto++;
+       }
 
-       return sprintf(buf, "protocol=%s (0x%x)\n",
-               chip->ir_contr[MULTI_IR_ID].proto_name,
-               chip->protocol);
+       return len;
 }
 
 static ssize_t protocol_store(struct device *dev,
@@ -308,6 +321,169 @@ static ssize_t led_frq_store(struct device *dev,
        return count;
 }
 
+static ssize_t ir_learning_show(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+
+       struct remote_chip *chip = dev_get_drvdata(dev);
+       struct remote_dev  *r_dev = chip->r_dev;
+
+       return sprintf(buf, "%d\n", r_dev->ir_learning_on);
+}
+
+static ssize_t ir_learning_store(struct device *dev,
+                                struct device_attribute *attr,
+                                const char *buf, size_t count)
+{
+       int ret = 0;
+       int val = 0;
+
+       struct remote_chip *chip = dev_get_drvdata(dev);
+       struct remote_dev  *r_dev = chip->r_dev;
+
+       ret = kstrtoint(buf, 0, &val);
+       if (ret != 0)
+               return -EINVAL;
+       if (r_dev->ir_learning_on == val)
+               return count;
+
+       disable_irq(chip->irqno);
+       mutex_lock(&chip->file_lock);
+       r_dev->ir_learning_on = !!val;
+       if (!!val) {
+               if (r_dev->demod_enable)
+                       demod_reset(chip);
+
+               if (remote_pulses_malloc(chip) < 0) {
+                       mutex_unlock(&chip->file_lock);
+                       enable_irq(chip->irqno);
+                       return -ENOMEM;
+               }
+               chip->set_register_config(chip, REMOTE_TYPE_RAW_NEC);
+               r_dev->protocol = chip->protocol;/*backup protocol*/
+               chip->protocol = REMOTE_TYPE_RAW_NEC;
+       } else {
+               chip->protocol = r_dev->protocol;
+               chip->set_register_config(chip, chip->protocol);
+               remote_pulses_free(chip);
+               chip->r_dev->ir_learning_done = false;
+       }
+       mutex_unlock(&chip->file_lock);
+       enable_irq(chip->irqno);
+       return count;
+}
+
+static ssize_t learned_pulse_show(struct device *dev,
+                                 struct device_attribute *attr, char *buf)
+{
+       int len = 0;
+       int i = 0;
+
+       struct remote_chip *chip = dev_get_drvdata(dev);
+       struct remote_dev  *r_dev = chip->r_dev;
+
+       if (!r_dev->pulses)
+               return len;
+
+       disable_irq(chip->irqno);
+       mutex_lock(&chip->file_lock);
+       for (i = 0; i < r_dev->pulses->len; i++)
+               len += sprintf(buf + len, "%lds",
+                              r_dev->pulses->pulse[i] & GENMASK(30, 0));
+
+       len += sprintf(buf + len, "\n");
+
+       remote_reg_update_bits(chip, MULTI_IR_ID, REG_REG1, BIT(15), BIT(15));
+
+       r_dev->ir_learning_done = false;
+
+       mutex_unlock(&chip->file_lock);
+       enable_irq(chip->irqno);
+
+       return len;
+}
+
+static ssize_t learned_pulse_store(struct device *dev,
+                                  struct device_attribute *attr,
+                                  const char *buf, size_t count)
+{
+       struct remote_chip *chip = dev_get_drvdata(dev);
+       struct remote_dev  *r_dev = chip->r_dev;
+
+       if (!r_dev->pulses)
+               return count;
+
+       disable_irq(chip->irqno);
+       mutex_lock(&chip->file_lock);
+       if (buf[0] == 'c') {
+               memset(r_dev->pulses, 0, sizeof(struct pulse_group) +
+                      r_dev->max_learned_pulse * sizeof(u32));
+               remote_reg_update_bits(chip, MULTI_IR_ID, REG_REG1, BIT(15),
+                                      BIT(15));
+
+               r_dev->ir_learning_done = false;
+       }
+       mutex_unlock(&chip->file_lock);
+       enable_irq(chip->irqno);
+       return count;
+}
+
+static ssize_t sum_cnt0_show(struct device *dev,
+                            struct device_attribute *attr, char *buf)
+{
+       int val = 0;
+       struct remote_chip *chip = dev_get_drvdata(dev);
+
+       remote_reg_read(chip, MULTI_IR_ID, REG_DEMOD_SUM_CNT0, &val);
+
+       return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t sum_cnt1_show(struct device *dev,
+                            struct device_attribute *attr, char *buf)
+{
+
+       int val = 0;
+       struct remote_chip *chip = dev_get_drvdata(dev);
+
+       remote_reg_read(chip, MULTI_IR_ID, REG_DEMOD_SUM_CNT1, &val);
+
+       return sprintf(buf, "%d\n", val);
+
+}
+
+static ssize_t use_fifo_show(struct device *dev,
+                            struct device_attribute *attr, char *buf)
+{
+       struct remote_chip *chip = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d\n", chip->r_dev->use_fifo);
+}
+
+static ssize_t use_fifo_store(struct device *dev, struct device_attribute *attr,
+                            const char *buf, size_t count)
+{
+       int val = 0;
+       int len = 0;
+       struct remote_chip *chip = dev_get_drvdata(dev);
+
+       len = kstrtoint(buf, 0, &val);
+
+       if (len != 0) {
+               dev_err(chip->dev, "input parameter error\n");
+               return -EINVAL;
+       }
+
+       chip->r_dev->use_fifo = val;
+
+       return count;
+}
+
+DEVICE_ATTR_RW(use_fifo);
+DEVICE_ATTR_RO(sum_cnt0);
+DEVICE_ATTR_RO(sum_cnt1);
+DEVICE_ATTR_RW(learned_pulse);
+DEVICE_ATTR_RW(ir_learning);
 DEVICE_ATTR_RW(led_frq);
 DEVICE_ATTR_RW(led_blink);
 DEVICE_ATTR_RW(repeat_enable);
@@ -326,6 +502,11 @@ static struct attribute *remote_attrs[] = {
        &dev_attr_debug_log.attr,
        &dev_attr_led_blink.attr,
        &dev_attr_led_frq.attr,
+       &dev_attr_ir_learning.attr,
+       &dev_attr_learned_pulse.attr,
+       &dev_attr_sum_cnt0.attr,
+       &dev_attr_sum_cnt1.attr,
+       &dev_attr_use_fifo.attr,
        NULL,
 };