Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit da7ee60b authored by Antti Seppälä's avatar Antti Seppälä Committed by Mauro Carvalho Chehab
Browse files

[media] rc: nuvoton-cir: Add support for writing wakeup samples 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: default avatarAntti Seppälä <a.seppala@gmail.com>
Signed-off-by: default avatarJames Hogan <james@albanarts.com>
Cc: Jarod Wilson <jarod@redhat.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@osg.samsung.com>
parent 2e4ebde2
Loading
Loading
Loading
Loading
+127 −0
Original line number Diff line number Diff line
@@ -526,6 +526,130 @@ static int nvt_set_tx_carrier(struct rc_dev *dev, u32 carrier)
	return 0;
}

static int nvt_write_wakeup_codes(struct rc_dev *dev,
				  const u8 *wakeup_sample_buf, int count)
{
	int i = 0;
	u8 reg, reg_learn_mode;
	unsigned long flags;
	struct nvt_dev *nvt = dev->priv;

	nvt_dbg_wake("writing wakeup samples");

	reg = nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRCON);
	reg_learn_mode = reg & ~CIR_WAKE_IRCON_MODE0;
	reg_learn_mode |= CIR_WAKE_IRCON_MODE1;

	/* Lock the learn area to prevent racing with wake-isr */
	spin_lock_irqsave(&nvt->nvt_lock, flags);

	/* Enable fifo writes */
	nvt_cir_wake_reg_write(nvt, reg_learn_mode, CIR_WAKE_IRCON);

	/* Clear cir wake rx fifo */
	nvt_clear_cir_wake_fifo(nvt);

	if (count > WAKE_FIFO_LEN) {
		nvt_dbg_wake("HW FIFO too small for all wake samples");
		count = WAKE_FIFO_LEN;
	}

	if (count)
		pr_info("Wake samples (%d) =", count);
	else
		pr_info("Wake sample fifo cleared");

	/* Write wake samples to fifo */
	for (i = 0; i < count; i++) {
		pr_cont(" %02x", wakeup_sample_buf[i]);
		nvt_cir_wake_reg_write(nvt, wakeup_sample_buf[i],
				       CIR_WAKE_WR_FIFO_DATA);
	}
	pr_cont("\n");

	/* Switch cir to wakeup mode and disable fifo writing */
	nvt_cir_wake_reg_write(nvt, reg, CIR_WAKE_IRCON);

	/* Set number of bytes needed for wake */
	nvt_cir_wake_reg_write(nvt, count ? count :
			       CIR_WAKE_FIFO_CMP_BYTES,
			       CIR_WAKE_FIFO_CMP_DEEP);

	spin_unlock_irqrestore(&nvt->nvt_lock, flags);

	return 0;
}

static int nvt_ir_raw_set_wakeup_filter(struct rc_dev *dev,
					struct rc_scancode_filter *sc_filter)
{
	u8 *reg_buf;
	u8 buf_val;
	int i, ret, count;
	unsigned int val;
	struct ir_raw_event *raw;
	bool complete;

	/* Require both mask and data to be set before actually committing */
	if (!sc_filter->mask || !sc_filter->data)
		return 0;

	raw = kmalloc_array(WAKE_FIFO_LEN, sizeof(*raw), GFP_KERNEL);
	if (!raw)
		return -ENOMEM;

	ret = ir_raw_encode_scancode(dev->enabled_wakeup_protocols, sc_filter,
				     raw, WAKE_FIFO_LEN);
	complete = (ret != -ENOBUFS);
	if (!complete)
		ret = WAKE_FIFO_LEN;
	else if (ret < 0)
		goto out_raw;

	reg_buf = kmalloc_array(WAKE_FIFO_LEN, sizeof(*reg_buf), GFP_KERNEL);
	if (!reg_buf) {
		ret = -ENOMEM;
		goto out_raw;
	}

	/* Inspect the ir samples */
	for (i = 0, count = 0; i < ret && count < WAKE_FIFO_LEN; ++i) {
		val = NS_TO_US((raw[i]).duration) / SAMPLE_PERIOD;

		/* Split too large values into several smaller ones */
		while (val > 0 && count < WAKE_FIFO_LEN) {

			/* 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;

			reg_buf[count] = buf_val;
			val -= buf_val;
			if ((raw[i]).pulse)
				reg_buf[count] |= BUF_PULSE_BIT;
			count++;
		}
	}

	ret = nvt_write_wakeup_codes(dev, reg_buf, count);

	kfree(reg_buf);
out_raw:
	kfree(raw);

	return ret;
}

/* Dummy implementation. nuvoton is agnostic to the protocol used */
static int nvt_ir_raw_change_wakeup_protocol(struct rc_dev *dev,
					     u64 *rc_type)
{
	return 0;
}

/*
 * nvt_tx_ir
 *
@@ -1043,11 +1167,14 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
	/* Set up the rc device */
	rdev->priv = nvt;
	rdev->driver_type = RC_DRIVER_IR_RAW;
	rdev->encode_wakeup = true;
	rdev->allowed_protocols = RC_BIT_ALL;
	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->change_wakeup_protocol = nvt_ir_raw_change_wakeup_protocol;
	rdev->input_name = "Nuvoton w836x7hg Infrared Remote Transceiver";
	rdev->input_phys = "nuvoton/cir0";
	rdev->input_id.bustype = BUS_HOST;
+1 −0
Original line number Diff line number Diff line
@@ -63,6 +63,7 @@ static int debug;
 */
#define TX_BUF_LEN 256
#define RX_BUF_LEN 32
#define WAKE_FIFO_LEN 67

struct nvt_dev {
	struct pnp_dev *pdev;
+1 −0
Original line number Diff line number Diff line
@@ -246,6 +246,7 @@ static inline void init_ir_raw_event(struct ir_raw_event *ev)
#define US_TO_NS(usec)		((usec) * 1000)
#define MS_TO_US(msec)		((msec) * 1000)
#define MS_TO_NS(msec)		((msec) * 1000 * 1000)
#define NS_TO_US(nsec)		DIV_ROUND_UP(nsec, 1000L)

void ir_raw_event_handle(struct rc_dev *dev);
int ir_raw_event_store(struct rc_dev *dev, struct ir_raw_event *ev);