[media] rc: nuvoton-cir: Add support wakeup via sysfs filter callback
authorAntti Seppälä <a.seppala@gmail.com>
Tue, 31 Mar 2015 17:48:12 +0000 (14:48 -0300)
committerMauro Carvalho Chehab <mchehab@s-opensource.com>
Mon, 30 Jan 2017 15:57:01 +0000 (13:57 -0200)
Nuvoton-cir utilizes the encoding capabilities of rc-core to convert
scancodes from user space to pulse/space format understood by the
underlying hardware.

Converted samples are then written to the wakeup fifo along with other
necessary configuration to enable wake up functionality.

Signed-off-by: Antti Seppälä <a.seppala@gmail.com>
Signed-off-by: James Hogan <james@albanarts.com>
Signed-off-by: Sean Young <sean@mess.org>
Cc: Jarod Wilson <jarod@redhat.com>
Cc: Heiner Kallweit <hkallweit1@gmail.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
drivers/media/rc/nuvoton-cir.c

index 77c0514..a849eed 100644 (file)
@@ -171,6 +171,41 @@ static void nvt_set_ioaddr(struct nvt_dev *nvt, unsigned long *ioaddr)
        }
 }
 
+static void nvt_write_wakeup_codes(struct rc_dev *dev,
+                                  const u8 *wbuf, int count)
+{
+       u8 tolerance, config;
+       struct nvt_dev *nvt = dev->priv;
+       int i;
+
+       /* hardcode the tolerance to 10% */
+       tolerance = DIV_ROUND_UP(count, 10);
+
+       spin_lock(&nvt->lock);
+
+       nvt_clear_cir_wake_fifo(nvt);
+       nvt_cir_wake_reg_write(nvt, count, CIR_WAKE_FIFO_CMP_DEEP);
+       nvt_cir_wake_reg_write(nvt, tolerance, CIR_WAKE_FIFO_CMP_TOL);
+
+       config = nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRCON);
+
+       /* enable writes to wake fifo */
+       nvt_cir_wake_reg_write(nvt, config | CIR_WAKE_IRCON_MODE1,
+                              CIR_WAKE_IRCON);
+
+       if (count)
+               pr_info("Wake samples (%d) =", count);
+       else
+               pr_info("Wake sample fifo cleared");
+
+       for (i = 0; i < count; i++)
+               nvt_cir_wake_reg_write(nvt, wbuf[i], CIR_WAKE_WR_FIFO_DATA);
+
+       nvt_cir_wake_reg_write(nvt, config, CIR_WAKE_IRCON);
+
+       spin_unlock(&nvt->lock);
+}
+
 static ssize_t wakeup_data_show(struct device *dev,
                                struct device_attribute *attr,
                                char *buf)
@@ -209,9 +244,7 @@ static ssize_t wakeup_data_store(struct device *dev,
                                 const char *buf, size_t len)
 {
        struct rc_dev *rc_dev = to_rc_dev(dev);
-       struct nvt_dev *nvt = rc_dev->priv;
-       unsigned long flags;
-       u8 tolerance, config, wake_buf[WAKEUP_MAX_SIZE];
+       u8 wake_buf[WAKEUP_MAX_SIZE];
        char **argv;
        int i, count;
        unsigned int val;
@@ -240,27 +273,7 @@ static ssize_t wakeup_data_store(struct device *dev,
                        wake_buf[i] |= BUF_PULSE_BIT;
        }
 
-       /* hardcode the tolerance to 10% */
-       tolerance = DIV_ROUND_UP(count, 10);
-
-       spin_lock_irqsave(&nvt->lock, flags);
-
-       nvt_clear_cir_wake_fifo(nvt);
-       nvt_cir_wake_reg_write(nvt, count, CIR_WAKE_FIFO_CMP_DEEP);
-       nvt_cir_wake_reg_write(nvt, tolerance, CIR_WAKE_FIFO_CMP_TOL);
-
-       config = nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRCON);
-
-       /* enable writes to wake fifo */
-       nvt_cir_wake_reg_write(nvt, config | CIR_WAKE_IRCON_MODE1,
-                              CIR_WAKE_IRCON);
-
-       for (i = 0; i < count; i++)
-               nvt_cir_wake_reg_write(nvt, wake_buf[i], CIR_WAKE_WR_FIFO_DATA);
-
-       nvt_cir_wake_reg_write(nvt, config, CIR_WAKE_IRCON);
-
-       spin_unlock_irqrestore(&nvt->lock, flags);
+       nvt_write_wakeup_codes(rc_dev, wake_buf, count);
 
        ret = len;
 out:
@@ -657,6 +670,62 @@ static int nvt_set_tx_carrier(struct rc_dev *dev, u32 carrier)
        return 0;
 }
 
+static int nvt_ir_raw_set_wakeup_filter(struct rc_dev *dev,
+                                       struct rc_scancode_filter *sc_filter)
+{
+       u8 buf_val;
+       int i, ret, count;
+       unsigned int val;
+       struct ir_raw_event *raw;
+       u8 wake_buf[WAKEUP_MAX_SIZE];
+       bool complete;
+
+       /* Require mask to be set */
+       if (!sc_filter->mask)
+               return 0;
+
+       raw = kmalloc_array(WAKEUP_MAX_SIZE, sizeof(*raw), GFP_KERNEL);
+       if (!raw)
+               return -ENOMEM;
+
+       ret = ir_raw_encode_scancode(dev->wakeup_protocol, sc_filter->data,
+                                    raw, WAKEUP_MAX_SIZE);
+       complete = (ret != -ENOBUFS);
+       if (!complete)
+               ret = WAKEUP_MAX_SIZE;
+       else if (ret < 0)
+               goto out_raw;
+
+       /* Inspect the ir samples */
+       for (i = 0, count = 0; i < ret && count < WAKEUP_MAX_SIZE; ++i) {
+               /* NS to US */
+               val = DIV_ROUND_UP(raw[i].duration, 1000L) / SAMPLE_PERIOD;
+
+               /* Split too large values into several smaller ones */
+               while (val > 0 && count < WAKEUP_MAX_SIZE) {
+                       /* Skip last value for better comparison tolerance */
+                       if (complete && i == ret - 1 && val < BUF_LEN_MASK)
+                               break;
+
+                       /* Clamp values to BUF_LEN_MASK at most */
+                       buf_val = (val > BUF_LEN_MASK) ? BUF_LEN_MASK : val;
+
+                       wake_buf[count] = buf_val;
+                       val -= buf_val;
+                       if ((raw[i]).pulse)
+                               wake_buf[count] |= BUF_PULSE_BIT;
+                       count++;
+               }
+       }
+
+       nvt_write_wakeup_codes(dev, wake_buf, count);
+       ret = 0;
+out_raw:
+       kfree(raw);
+
+       return ret;
+}
+
 /*
  * nvt_tx_ir
  *
@@ -1058,10 +1127,13 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
        rdev->priv = nvt;
        rdev->driver_type = RC_DRIVER_IR_RAW;
        rdev->allowed_protocols = RC_BIT_ALL_IR_DECODER;
+       rdev->allowed_wakeup_protocols = RC_BIT_ALL_IR_ENCODER;
+       rdev->encode_wakeup = true;
        rdev->open = nvt_open;
        rdev->close = nvt_close;
        rdev->tx_ir = nvt_tx_ir;
        rdev->s_tx_carrier = nvt_set_tx_carrier;
+       rdev->s_wakeup_filter = nvt_ir_raw_set_wakeup_filter;
        rdev->input_name = "Nuvoton w836x7hg Infrared Remote Transceiver";
        rdev->input_phys = "nuvoton/cir0";
        rdev->input_id.bustype = BUS_HOST;