[media] rc: nuvoton-cir: Add support wakeup via sysfs filter callback

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>
This commit is contained in:
Antti Seppälä 2015-03-31 14:48:12 -03:00 committed by Mauro Carvalho Chehab
parent 914535c059
commit 97c129747a
1 changed files with 96 additions and 24 deletions

View 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;