diff mbox

[06/11] media: rc: img-ir: add NEC decoder module

Message ID 1386947579-26703-7-git-send-email-james.hogan@imgtec.com (mailing list archive)
State New, archived
Headers show

Commit Message

James Hogan Dec. 13, 2013, 3:12 p.m. UTC
Add an img-ir module for decoding the NEC and extended NEC infrared
protocols.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Mauro Carvalho Chehab <m.chehab@samsung.com>
Cc: linux-media@vger.kernel.org
---
 drivers/media/rc/img-ir/Kconfig      |   7 ++
 drivers/media/rc/img-ir/Makefile     |   1 +
 drivers/media/rc/img-ir/img-ir-nec.c | 149 +++++++++++++++++++++++++++++++++++
 3 files changed, 157 insertions(+)
 create mode 100644 drivers/media/rc/img-ir/img-ir-nec.c

Comments

Mauro Carvalho Chehab Dec. 22, 2013, 1:49 p.m. UTC | #1
Em Fri, 13 Dec 2013 15:12:54 +0000
James Hogan <james.hogan@imgtec.com> escreveu:

> Add an img-ir module for decoding the NEC and extended NEC infrared
> protocols.
> 
> Signed-off-by: James Hogan <james.hogan@imgtec.com>
> Cc: Mauro Carvalho Chehab <m.chehab@samsung.com>
> Cc: linux-media@vger.kernel.org
> ---
>  drivers/media/rc/img-ir/Kconfig      |   7 ++
>  drivers/media/rc/img-ir/Makefile     |   1 +
>  drivers/media/rc/img-ir/img-ir-nec.c | 149 +++++++++++++++++++++++++++++++++++
>  3 files changed, 157 insertions(+)
>  create mode 100644 drivers/media/rc/img-ir/img-ir-nec.c
> 
> diff --git a/drivers/media/rc/img-ir/Kconfig b/drivers/media/rc/img-ir/Kconfig
> index 60eaba6a0843..44d00227c6c4 100644
> --- a/drivers/media/rc/img-ir/Kconfig
> +++ b/drivers/media/rc/img-ir/Kconfig
> @@ -24,3 +24,10 @@ config IR_IMG_HW
>  	   signals in hardware. This is more reliable, consumes less processing
>  	   power since only a single interrupt is received for each scancode,
>  	   and allows an IR scancode to be used as a wake event.
> +
> +config IR_IMG_NEC
> +	tristate "NEC protocol support"
> +	depends on IR_IMG && IR_IMG_HW
> +	help
> +	   Say Y or M here to enable support for the NEC and extended NEC
> +	   protocols in the ImgTec infrared decoder block.
> diff --git a/drivers/media/rc/img-ir/Makefile b/drivers/media/rc/img-ir/Makefile
> index 4ef86edec873..f3052878f092 100644
> --- a/drivers/media/rc/img-ir/Makefile
> +++ b/drivers/media/rc/img-ir/Makefile
> @@ -4,3 +4,4 @@ img-ir-$(CONFIG_IR_IMG_HW)	+= img-ir-hw.o
>  img-ir-objs			:= $(img-ir-y)
>  
>  obj-$(CONFIG_IR_IMG)		+= img-ir.o
> +obj-$(CONFIG_IR_IMG_NEC)	+= img-ir-nec.o
> diff --git a/drivers/media/rc/img-ir/img-ir-nec.c b/drivers/media/rc/img-ir/img-ir-nec.c
> new file mode 100644
> index 000000000000..ba376caafaf2
> --- /dev/null
> +++ b/drivers/media/rc/img-ir/img-ir-nec.c
> @@ -0,0 +1,149 @@
> +/*
> + * ImgTec IR Decoder setup for NEC protocol.
> + *
> + * Copyright 2010-2013 Imagination Technologies Ltd.
> + */
> +
> +#include <linux/module.h>
> +
> +#include "img-ir-hw.h"
> +
> +/* Convert NEC data to a scancode */
> +static int img_ir_nec_scancode(int len, u64 raw, u64 protocols)
> +{
> +	unsigned int addr, addr_inv, data, data_inv;
> +	int scancode;
> +	/* a repeat code has no data */
> +	if (!len)
> +		return IMG_IR_REPEATCODE;
> +	if (len != 32)
> +		return IMG_IR_ERR_INVALID;
> +	addr     = (raw >>  0) & 0xff;
> +	addr_inv = (raw >>  8) & 0xff;
> +	data     = (raw >> 16) & 0xff;
> +	data_inv = (raw >> 24) & 0xff;
> +	/* Validate data */
> +	if ((data_inv ^ data) != 0xff)
> +		return IMG_IR_ERR_INVALID;
> +
> +	if ((addr_inv ^ addr) != 0xff) {
> +		/* Extended NEC */
> +		scancode = addr     << 16 |
> +			   addr_inv <<  8 |
> +			   data;
> +	} else {
> +		/* Normal NEC */
> +		scancode = addr << 8 |
> +			   data;
> +	}

There are some types of NEC extended that uses the full 32 bits as
scancodes. Those are used at least on Apple and TiVo remote controllers.

> +	return scancode;
> +}
> +
> +/* Convert NEC scancode to NEC data filter */
> +static int img_ir_nec_filter(const struct img_ir_sc_filter *in,
> +			     struct img_ir_filter *out, u64 protocols)
> +{
> +	unsigned int addr, addr_inv, data, data_inv;
> +	unsigned int addr_m, addr_inv_m, data_m;
> +
> +	data     = in->data & 0xff;
> +	data_m   = in->mask & 0xff;
> +	data_inv = data ^ 0xff;
> +
> +	if (in->data & 0xff000000)
> +		return -EINVAL;
> +
> +	if (in->data & 0x00ff0000) {
> +		/* Extended NEC */
> +		addr       = (in->data >> 16) & 0xff;
> +		addr_m     = (in->mask >> 16) & 0xff;
> +		addr_inv   = (in->data >>  8) & 0xff;
> +		addr_inv_m = (in->mask >>  8) & 0xff;
> +	} else {
> +		/* Normal NEC */
> +		addr       = (in->data >>  8) & 0xff;
> +		addr_m     = (in->mask >>  8) & 0xff;
> +		addr_inv   = addr ^ 0xff;
> +		addr_inv_m = addr_m;
> +	}
> +
> +	out->data = data_inv << 24 |
> +		    data     << 16 |
> +		    addr_inv <<  8 |
> +		    addr;
> +	out->mask = data_m     << 24 |
> +		    data_m     << 16 |
> +		    addr_inv_m <<  8 |
> +		    addr_m;
> +	return 0;
> +}
> +
> +/*
> + * NEC decoder
> + * See also http://www.sbprojects.com/knowledge/ir/nec.php
> + *        http://wiki.altium.com/display/ADOH/NEC+Infrared+Transmission+Protocol
> + */
> +static struct img_ir_decoder img_ir_nec = {
> +	.type = RC_BIT_NEC,
> +	.control = {
> +		.decoden = 1,
> +		.code_type = IMG_IR_CODETYPE_PULSEDIST,
> +	},
> +	/* main timings */
> +	.unit = 562500, /* 562.5 us */
> +	.timings = {
> +		/* leader symbol */
> +		.ldr = {
> +			.pulse = { 16	/* 9ms */ },
> +			.space = { 8	/* 4.5ms */ },
> +		},
> +		/* 0 symbol */
> +		.s00 = {
> +			.pulse = { 1	/* 562.5 us */ },
> +			.space = { 1	/* 562.5 us */ },
> +		},
> +		/* 1 symbol */
> +		.s01 = {
> +			.pulse = { 1	/* 562.5 us */ },
> +			.space = { 3	/* 1687.5 us */ },
> +		},
> +		/* free time */
> +		.ft = {
> +			.minlen = 32,
> +			.maxlen = 32,
> +			.ft_min = 10,	/* 5.625 ms */
> +		},
> +	},
> +	/* repeat codes */
> +	.repeat = 108,			/* 108 ms */
> +	.rtimings = {
> +		/* leader symbol */
> +		.ldr = {
> +			.space = { 4	/* 2.25 ms */ },
> +		},
> +		/* free time */
> +		.ft = {
> +			.minlen = 0,	/* repeat code has no data */
> +			.maxlen = 0,
> +		},
> +	},
> +	/* scancode logic */
> +	.scancode = img_ir_nec_scancode,
> +	.filter = img_ir_nec_filter,
> +};
> +
> +static int __init img_ir_nec_init(void)
> +{
> +	return img_ir_register_decoder(&img_ir_nec);
> +}
> +module_init(img_ir_nec_init);
> +
> +static void __exit img_ir_nec_exit(void)
> +{
> +	img_ir_unregister_decoder(&img_ir_nec);
> +}
> +module_exit(img_ir_nec_exit);
> +
> +MODULE_AUTHOR("Imagination Technologies Ltd.");
> +MODULE_DESCRIPTION("ImgTec IR NEC protocol support");
> +MODULE_LICENSE("GPL");
James Hogan Dec. 23, 2013, 11:30 a.m. UTC | #2
On 22/12/13 13:49, Mauro Carvalho Chehab wrote:
> Em Fri, 13 Dec 2013 15:12:54 +0000
> James Hogan <james.hogan@imgtec.com> escreveu:
>> +/* Convert NEC data to a scancode */
>> +static int img_ir_nec_scancode(int len, u64 raw, u64 protocols)
>> +{
>> +	unsigned int addr, addr_inv, data, data_inv;
>> +	int scancode;
>> +	/* a repeat code has no data */
>> +	if (!len)
>> +		return IMG_IR_REPEATCODE;
>> +	if (len != 32)
>> +		return IMG_IR_ERR_INVALID;
>> +	addr     = (raw >>  0) & 0xff;
>> +	addr_inv = (raw >>  8) & 0xff;
>> +	data     = (raw >> 16) & 0xff;
>> +	data_inv = (raw >> 24) & 0xff;
>> +	/* Validate data */
>> +	if ((data_inv ^ data) != 0xff)
>> +		return IMG_IR_ERR_INVALID;
>> +
>> +	if ((addr_inv ^ addr) != 0xff) {
>> +		/* Extended NEC */
>> +		scancode = addr     << 16 |
>> +			   addr_inv <<  8 |
>> +			   data;
>> +	} else {
>> +		/* Normal NEC */
>> +		scancode = addr << 8 |
>> +			   data;
>> +	}
> 
> There are some types of NEC extended that uses the full 32 bits as
> scancodes. Those are used at least on Apple and TiVo remote controllers.

Ooh, I hadn't spotted that patch. I'll make the necessary changes.

I notice that the scancode produced by the raw NEC decoder is the raw
non-bit-reversed version of the bits received, whereas for normal and
extended NEC the scancode fields are bit reversed. The TiVo keymap
appears to confirm that this is essentially backwards:

NEC:0xAAaaCCcc (AA=address, aa=not A, CC=command, cc=not command)

				bitrev(CCcc):
{ 0xa10c140b, KEY_NUMERIC_1 },	d028
{ 0xa10c940b, KEY_NUMERIC_2 },	d029
{ 0xa10c540b, KEY_NUMERIC_3 },	d02a
{ 0xa10cd40b, KEY_NUMERIC_4 },	d02b
{ 0xa10c340b, KEY_NUMERIC_5 },	d02c
{ 0xa10cb40b, KEY_NUMERIC_6 },	d02d
{ 0xa10c740b, KEY_NUMERIC_7 },	d02e
{ 0xa10cf40b, KEY_NUMERIC_8 },	d02f
{ 0x0085302f, KEY_NUMERIC_8 },
{ 0xa10c0c03, KEY_NUMERIC_9 },	c030
{ 0xa10c8c03, KEY_NUMERIC_0 },	c031

Clearly CC is supposed to be the LSB of the command.

Is it possible to reverse the bits in these scancode encodings (and of
course update the keymaps) or does this constitute a stable ABI that is
now too late to change?

IMO the following encoding would make much better sense for 32bit NEC
scancodes:
bits 31:16 = bitrev(AAaa)
bits 15:0  = bitrev(CCcc)

I.e. just bit reverse each 16bit half.

This puts the LSB of the command field in the LSB of the scancode which
I think is important, and treats the address field as a continuous
16bits (even though the extended NEC scancodes have address bytes
swapped for some reason - although for address it doesn't really
matter). If we assume the high byte of the address (aa) is always
non-zero, then the scancodes can be distinguished.

Cheers
James

--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/media/rc/img-ir/Kconfig b/drivers/media/rc/img-ir/Kconfig
index 60eaba6a0843..44d00227c6c4 100644
--- a/drivers/media/rc/img-ir/Kconfig
+++ b/drivers/media/rc/img-ir/Kconfig
@@ -24,3 +24,10 @@  config IR_IMG_HW
 	   signals in hardware. This is more reliable, consumes less processing
 	   power since only a single interrupt is received for each scancode,
 	   and allows an IR scancode to be used as a wake event.
+
+config IR_IMG_NEC
+	tristate "NEC protocol support"
+	depends on IR_IMG && IR_IMG_HW
+	help
+	   Say Y or M here to enable support for the NEC and extended NEC
+	   protocols in the ImgTec infrared decoder block.
diff --git a/drivers/media/rc/img-ir/Makefile b/drivers/media/rc/img-ir/Makefile
index 4ef86edec873..f3052878f092 100644
--- a/drivers/media/rc/img-ir/Makefile
+++ b/drivers/media/rc/img-ir/Makefile
@@ -4,3 +4,4 @@  img-ir-$(CONFIG_IR_IMG_HW)	+= img-ir-hw.o
 img-ir-objs			:= $(img-ir-y)
 
 obj-$(CONFIG_IR_IMG)		+= img-ir.o
+obj-$(CONFIG_IR_IMG_NEC)	+= img-ir-nec.o
diff --git a/drivers/media/rc/img-ir/img-ir-nec.c b/drivers/media/rc/img-ir/img-ir-nec.c
new file mode 100644
index 000000000000..ba376caafaf2
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-nec.c
@@ -0,0 +1,149 @@ 
+/*
+ * ImgTec IR Decoder setup for NEC protocol.
+ *
+ * Copyright 2010-2013 Imagination Technologies Ltd.
+ */
+
+#include <linux/module.h>
+
+#include "img-ir-hw.h"
+
+/* Convert NEC data to a scancode */
+static int img_ir_nec_scancode(int len, u64 raw, u64 protocols)
+{
+	unsigned int addr, addr_inv, data, data_inv;
+	int scancode;
+	/* a repeat code has no data */
+	if (!len)
+		return IMG_IR_REPEATCODE;
+	if (len != 32)
+		return IMG_IR_ERR_INVALID;
+	addr     = (raw >>  0) & 0xff;
+	addr_inv = (raw >>  8) & 0xff;
+	data     = (raw >> 16) & 0xff;
+	data_inv = (raw >> 24) & 0xff;
+	/* Validate data */
+	if ((data_inv ^ data) != 0xff)
+		return IMG_IR_ERR_INVALID;
+
+	if ((addr_inv ^ addr) != 0xff) {
+		/* Extended NEC */
+		scancode = addr     << 16 |
+			   addr_inv <<  8 |
+			   data;
+	} else {
+		/* Normal NEC */
+		scancode = addr << 8 |
+			   data;
+	}
+	return scancode;
+}
+
+/* Convert NEC scancode to NEC data filter */
+static int img_ir_nec_filter(const struct img_ir_sc_filter *in,
+			     struct img_ir_filter *out, u64 protocols)
+{
+	unsigned int addr, addr_inv, data, data_inv;
+	unsigned int addr_m, addr_inv_m, data_m;
+
+	data     = in->data & 0xff;
+	data_m   = in->mask & 0xff;
+	data_inv = data ^ 0xff;
+
+	if (in->data & 0xff000000)
+		return -EINVAL;
+
+	if (in->data & 0x00ff0000) {
+		/* Extended NEC */
+		addr       = (in->data >> 16) & 0xff;
+		addr_m     = (in->mask >> 16) & 0xff;
+		addr_inv   = (in->data >>  8) & 0xff;
+		addr_inv_m = (in->mask >>  8) & 0xff;
+	} else {
+		/* Normal NEC */
+		addr       = (in->data >>  8) & 0xff;
+		addr_m     = (in->mask >>  8) & 0xff;
+		addr_inv   = addr ^ 0xff;
+		addr_inv_m = addr_m;
+	}
+
+	out->data = data_inv << 24 |
+		    data     << 16 |
+		    addr_inv <<  8 |
+		    addr;
+	out->mask = data_m     << 24 |
+		    data_m     << 16 |
+		    addr_inv_m <<  8 |
+		    addr_m;
+	return 0;
+}
+
+/*
+ * NEC decoder
+ * See also http://www.sbprojects.com/knowledge/ir/nec.php
+ *        http://wiki.altium.com/display/ADOH/NEC+Infrared+Transmission+Protocol
+ */
+static struct img_ir_decoder img_ir_nec = {
+	.type = RC_BIT_NEC,
+	.control = {
+		.decoden = 1,
+		.code_type = IMG_IR_CODETYPE_PULSEDIST,
+	},
+	/* main timings */
+	.unit = 562500, /* 562.5 us */
+	.timings = {
+		/* leader symbol */
+		.ldr = {
+			.pulse = { 16	/* 9ms */ },
+			.space = { 8	/* 4.5ms */ },
+		},
+		/* 0 symbol */
+		.s00 = {
+			.pulse = { 1	/* 562.5 us */ },
+			.space = { 1	/* 562.5 us */ },
+		},
+		/* 1 symbol */
+		.s01 = {
+			.pulse = { 1	/* 562.5 us */ },
+			.space = { 3	/* 1687.5 us */ },
+		},
+		/* free time */
+		.ft = {
+			.minlen = 32,
+			.maxlen = 32,
+			.ft_min = 10,	/* 5.625 ms */
+		},
+	},
+	/* repeat codes */
+	.repeat = 108,			/* 108 ms */
+	.rtimings = {
+		/* leader symbol */
+		.ldr = {
+			.space = { 4	/* 2.25 ms */ },
+		},
+		/* free time */
+		.ft = {
+			.minlen = 0,	/* repeat code has no data */
+			.maxlen = 0,
+		},
+	},
+	/* scancode logic */
+	.scancode = img_ir_nec_scancode,
+	.filter = img_ir_nec_filter,
+};
+
+static int __init img_ir_nec_init(void)
+{
+	return img_ir_register_decoder(&img_ir_nec);
+}
+module_init(img_ir_nec_init);
+
+static void __exit img_ir_nec_exit(void)
+{
+	img_ir_unregister_decoder(&img_ir_nec);
+}
+module_exit(img_ir_nec_exit);
+
+MODULE_AUTHOR("Imagination Technologies Ltd.");
+MODULE_DESCRIPTION("ImgTec IR NEC protocol support");
+MODULE_LICENSE("GPL");