From 97c129747af52c23a4dd9c6fc8c73ab8be3b9038 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antti=20Sepp=C3=A4l=C3=A4?= Date: Tue, 31 Mar 2015 14:48:12 -0300 Subject: [PATCH] [media] rc: nuvoton-cir: Add support wakeup via sysfs filter callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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ä Signed-off-by: James Hogan Signed-off-by: Sean Young Cc: Jarod Wilson Cc: Heiner Kallweit Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/nuvoton-cir.c | 120 ++++++++++++++++++++++++++------- 1 file changed, 96 insertions(+), 24 deletions(-) diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c index 77c05145c04e..a849eed43d41 100644 --- a/drivers/media/rc/nuvoton-cir.c +++ b/drivers/media/rc/nuvoton-cir.c @@ -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;