* [PATCH 00/11] media: rc: ImgTec IR decoder driver
@ 2013-12-13 15:12 James Hogan
2013-12-13 15:12 ` [PATCH 01/11] dt: binding: add binding for ImgTec IR block James Hogan
` (10 more replies)
0 siblings, 11 replies; 21+ messages in thread
From: James Hogan @ 2013-12-13 15:12 UTC (permalink / raw)
To: Mauro Carvalho Chehab, linux-media
Cc: James Hogan, Grant Likely, Rob Herring, devicetree
Add driver for the ImgTec Infrared decoder block. Two separate rc input
devices are exposed depending on kernel configuration. One uses the
hardware decoder which is set up with timings for a specific protocol
and supports mask/value filtering and wake events. The other uses raw
edge interrupts and the generic software protocol decoders to allow
multiple protocols to be supported, including those not supported by the
hardware decoder.
The hardware decoder timing values, raw data to scan code conversion
function and scan code filter to raw data filter conversion function are
provided as separate modules for each protocol which the main driver can
use. The scan code filter value and mask (and the same again for wake
from sleep) are specified via sysfs files in /sys/class/rc/rcX/.
Cc: Mauro Carvalho Chehab <m.chehab@samsung.com>
Cc: linux-media@vger.kernel.org
Cc: Grant Likely <grant.likely@linaro.org>
Cc: Rob Herring <rob.herring@calxeda.com>
Cc: devicetree@vger.kernel.org
James Hogan (11):
dt: binding: add binding for ImgTec IR block
media: rc: img-ir: add base driver
media: rc: img-ir: add raw driver
media: rc: img-ir: add hardware decoder driver
media: rc: img-ir: add to build
media: rc: img-ir: add NEC decoder module
media: rc: img-ir: add JVC decoder module
media: rc: img-ir: add Sony decoder module
media: rc: add Sharp infrared protocol
media: rc: img-ir: add Sharp decoder module
media: rc: img-ir: add Sanyo decoder module
Documentation/devicetree/bindings/media/img-ir.txt | 20 +
drivers/media/rc/Kconfig | 2 +
drivers/media/rc/Makefile | 1 +
drivers/media/rc/img-ir/Kconfig | 61 +
drivers/media/rc/img-ir/Makefile | 11 +
drivers/media/rc/img-ir/img-ir-core.c | 172 +++
drivers/media/rc/img-ir/img-ir-hw.c | 1277 ++++++++++++++++++++
drivers/media/rc/img-ir/img-ir-hw.h | 284 +++++
drivers/media/rc/img-ir/img-ir-jvc.c | 109 ++
drivers/media/rc/img-ir/img-ir-nec.c | 149 +++
drivers/media/rc/img-ir/img-ir-raw.c | 107 ++
drivers/media/rc/img-ir/img-ir-raw.h | 58 +
drivers/media/rc/img-ir/img-ir-sanyo.c | 139 +++
drivers/media/rc/img-ir/img-ir-sharp.c | 115 ++
drivers/media/rc/img-ir/img-ir-sony.c | 163 +++
drivers/media/rc/img-ir/img-ir.h | 170 +++
drivers/media/rc/rc-main.c | 1 +
include/media/rc-map.h | 4 +-
18 files changed, 2842 insertions(+), 1 deletion(-)
create mode 100644 Documentation/devicetree/bindings/media/img-ir.txt
create mode 100644 drivers/media/rc/img-ir/Kconfig
create mode 100644 drivers/media/rc/img-ir/Makefile
create mode 100644 drivers/media/rc/img-ir/img-ir-core.c
create mode 100644 drivers/media/rc/img-ir/img-ir-hw.c
create mode 100644 drivers/media/rc/img-ir/img-ir-hw.h
create mode 100644 drivers/media/rc/img-ir/img-ir-jvc.c
create mode 100644 drivers/media/rc/img-ir/img-ir-nec.c
create mode 100644 drivers/media/rc/img-ir/img-ir-raw.c
create mode 100644 drivers/media/rc/img-ir/img-ir-raw.h
create mode 100644 drivers/media/rc/img-ir/img-ir-sanyo.c
create mode 100644 drivers/media/rc/img-ir/img-ir-sharp.c
create mode 100644 drivers/media/rc/img-ir/img-ir-sony.c
create mode 100644 drivers/media/rc/img-ir/img-ir.h
--
1.8.1.2
^ permalink raw reply [flat|nested] 21+ messages in thread* [PATCH 01/11] dt: binding: add binding for ImgTec IR block 2013-12-13 15:12 [PATCH 00/11] media: rc: ImgTec IR decoder driver James Hogan @ 2013-12-13 15:12 ` James Hogan 2013-12-22 10:56 ` Mauro Carvalho Chehab 2013-12-22 12:48 ` Tomasz Figa 2013-12-13 15:12 ` [PATCH 02/11] media: rc: img-ir: add base driver James Hogan ` (9 subsequent siblings) 10 siblings, 2 replies; 21+ messages in thread From: James Hogan @ 2013-12-13 15:12 UTC (permalink / raw) To: Mauro Carvalho Chehab, linux-media Cc: James Hogan, Rob Herring, Pawel Moll, Mark Rutland, Stephen Warren, Ian Campbell, devicetree, Rob Landley, linux-doc Add device tree binding for ImgTec Consumer Infrared block. Signed-off-by: James Hogan <james.hogan@imgtec.com> Cc: Mauro Carvalho Chehab <m.chehab@samsung.com> Cc: linux-media@vger.kernel.org Cc: Rob Herring <rob.herring@calxeda.com> Cc: Pawel Moll <pawel.moll@arm.com> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Stephen Warren <swarren@wwwdotorg.org> Cc: Ian Campbell <ijc+devicetree@hellion.org.uk> Cc: devicetree@vger.kernel.org Cc: Rob Landley <rob@landley.net> Cc: linux-doc@vger.kernel.org --- Documentation/devicetree/bindings/media/img-ir.txt | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/img-ir.txt diff --git a/Documentation/devicetree/bindings/media/img-ir.txt b/Documentation/devicetree/bindings/media/img-ir.txt new file mode 100644 index 000000000000..6f623b094ea6 --- /dev/null +++ b/Documentation/devicetree/bindings/media/img-ir.txt @@ -0,0 +1,20 @@ +* ImgTec Infrared (IR) decoder + +Required properties: +- compatible: Should be "img,ir" +- reg: Physical base address of the controller and length of + memory mapped region. +- interrupts: The interrupt specifier to the cpu. + +Optional properties: +- clocks: Clock specifier for base clock. + Defaults to 32.768KHz if not specified. + +Example: + + ir@02006200 { + compatible = "img,ir"; + reg = <0x02006200 0x100>; + interrupts = <29 4>; + clocks = <&clk_32khz>; + }; -- 1.8.1.2 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH 01/11] dt: binding: add binding for ImgTec IR block 2013-12-13 15:12 ` [PATCH 01/11] dt: binding: add binding for ImgTec IR block James Hogan @ 2013-12-22 10:56 ` Mauro Carvalho Chehab 2013-12-22 12:48 ` Tomasz Figa 1 sibling, 0 replies; 21+ messages in thread From: Mauro Carvalho Chehab @ 2013-12-22 10:56 UTC (permalink / raw) To: James Hogan, devicetree Cc: linux-media, Rob Herring, Pawel Moll, Mark Rutland, Stephen Warren, Ian Campbell, Rob Landley, linux-doc Em Fri, 13 Dec 2013 15:12:49 +0000 James Hogan <james.hogan@imgtec.com> escreveu: > Add device tree binding for ImgTec Consumer Infrared block. > > Signed-off-by: James Hogan <james.hogan@imgtec.com> > Cc: Mauro Carvalho Chehab <m.chehab@samsung.com> > Cc: linux-media@vger.kernel.org > Cc: Rob Herring <rob.herring@calxeda.com> > Cc: Pawel Moll <pawel.moll@arm.com> > Cc: Mark Rutland <mark.rutland@arm.com> > Cc: Stephen Warren <swarren@wwwdotorg.org> > Cc: Ian Campbell <ijc+devicetree@hellion.org.uk> > Cc: devicetree@vger.kernel.org > Cc: Rob Landley <rob@landley.net> > Cc: linux-doc@vger.kernel.org It looks ok for me, but we should wait for a DT maintainer ack for this one. Regards, Mauro > --- > Documentation/devicetree/bindings/media/img-ir.txt | 20 ++++++++++++++++++++ > 1 file changed, 20 insertions(+) > create mode 100644 Documentation/devicetree/bindings/media/img-ir.txt > > diff --git a/Documentation/devicetree/bindings/media/img-ir.txt b/Documentation/devicetree/bindings/media/img-ir.txt > new file mode 100644 > index 000000000000..6f623b094ea6 > --- /dev/null > +++ b/Documentation/devicetree/bindings/media/img-ir.txt > @@ -0,0 +1,20 @@ > +* ImgTec Infrared (IR) decoder > + > +Required properties: > +- compatible: Should be "img,ir" > +- reg: Physical base address of the controller and length of > + memory mapped region. > +- interrupts: The interrupt specifier to the cpu. > + > +Optional properties: > +- clocks: Clock specifier for base clock. > + Defaults to 32.768KHz if not specified. > + > +Example: > + > + ir@02006200 { > + compatible = "img,ir"; > + reg = <0x02006200 0x100>; > + interrupts = <29 4>; > + clocks = <&clk_32khz>; > + }; -- Cheers, Mauro ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 01/11] dt: binding: add binding for ImgTec IR block 2013-12-13 15:12 ` [PATCH 01/11] dt: binding: add binding for ImgTec IR block James Hogan 2013-12-22 10:56 ` Mauro Carvalho Chehab @ 2013-12-22 12:48 ` Tomasz Figa 2013-12-23 10:41 ` James Hogan 1 sibling, 1 reply; 21+ messages in thread From: Tomasz Figa @ 2013-12-22 12:48 UTC (permalink / raw) To: James Hogan Cc: Mauro Carvalho Chehab, linux-media, Rob Herring, Pawel Moll, Mark Rutland, Stephen Warren, Ian Campbell, devicetree, Rob Landley, linux-doc Hi James, On Friday 13 of December 2013 15:12:49 James Hogan wrote: > Add device tree binding for ImgTec Consumer Infrared block. > > Signed-off-by: James Hogan <james.hogan@imgtec.com> > Cc: Mauro Carvalho Chehab <m.chehab@samsung.com> > Cc: linux-media@vger.kernel.org > Cc: Rob Herring <rob.herring@calxeda.com> > Cc: Pawel Moll <pawel.moll@arm.com> > Cc: Mark Rutland <mark.rutland@arm.com> > Cc: Stephen Warren <swarren@wwwdotorg.org> > Cc: Ian Campbell <ijc+devicetree@hellion.org.uk> > Cc: devicetree@vger.kernel.org > Cc: Rob Landley <rob@landley.net> > Cc: linux-doc@vger.kernel.org > --- > Documentation/devicetree/bindings/media/img-ir.txt | 20 ++++++++++++++++++++ > 1 file changed, 20 insertions(+) > create mode 100644 Documentation/devicetree/bindings/media/img-ir.txt > > diff --git a/Documentation/devicetree/bindings/media/img-ir.txt b/Documentation/devicetree/bindings/media/img-ir.txt > new file mode 100644 > index 000000000000..6f623b094ea6 > --- /dev/null > +++ b/Documentation/devicetree/bindings/media/img-ir.txt > @@ -0,0 +1,20 @@ > +* ImgTec Infrared (IR) decoder > + > +Required properties: > +- compatible: Should be "img,ir" This compatible string isn't really very specific. Is there some IP revision string that could be added, to account for possible design changes that may require binding change? > +- reg: Physical base address of the controller and length of > + memory mapped region. > +- interrupts: The interrupt specifier to the cpu. > + > +Optional properties: > +- clocks: Clock specifier for base clock. > + Defaults to 32.768KHz if not specified. To make the binding less fragile and allow interoperability with non-DT platforms it may be better to provide also clock-names property (so you can use clk_get(); that's a Linux implementation detail, though, but to make our lives easier IMHO they should be sometimes considered too). Best regards, Tomasz ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 01/11] dt: binding: add binding for ImgTec IR block 2013-12-22 12:48 ` Tomasz Figa @ 2013-12-23 10:41 ` James Hogan 0 siblings, 0 replies; 21+ messages in thread From: James Hogan @ 2013-12-23 10:41 UTC (permalink / raw) To: Tomasz Figa Cc: Mauro Carvalho Chehab, linux-media, Rob Herring, Pawel Moll, Mark Rutland, Stephen Warren, Ian Campbell, devicetree, Rob Landley, linux-doc On 22/12/13 12:48, Tomasz Figa wrote: >> diff --git a/Documentation/devicetree/bindings/media/img-ir.txt b/Documentation/devicetree/bindings/media/img-ir.txt >> new file mode 100644 >> index 000000000000..6f623b094ea6 >> --- /dev/null >> +++ b/Documentation/devicetree/bindings/media/img-ir.txt >> @@ -0,0 +1,20 @@ >> +* ImgTec Infrared (IR) decoder >> + >> +Required properties: >> +- compatible: Should be "img,ir" > > This compatible string isn't really very specific. Is there some IP > revision string that could be added, to account for possible design > changes that may require binding change? Yes, agreed. I'll try and find a more unambiguous name for the IP block. >> +- reg: Physical base address of the controller and length of >> + memory mapped region. >> +- interrupts: The interrupt specifier to the cpu. >> + >> +Optional properties: >> +- clocks: Clock specifier for base clock. >> + Defaults to 32.768KHz if not specified. > > To make the binding less fragile and allow interoperability with non-DT > platforms it may be better to provide also clock-names property (so you > can use clk_get(); that's a Linux implementation detail, though, but to > make our lives easier IMHO they should be sometimes considered too). Good idea. Looking at the hardware manual it actually describes 3 clock inputs, and although only one is needed by the driver it makes sense for the DT binding to be able to describe them all. I'll probably go with these clock-names values: "core": Core clock (32.867kHz) "sys": System side (fast) clock "mod": Power modulation clock Cheers James ^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 02/11] media: rc: img-ir: add base driver 2013-12-13 15:12 [PATCH 00/11] media: rc: ImgTec IR decoder driver James Hogan 2013-12-13 15:12 ` [PATCH 01/11] dt: binding: add binding for ImgTec IR block James Hogan @ 2013-12-13 15:12 ` James Hogan 2013-12-13 15:12 ` [PATCH 03/11] media: rc: img-ir: add raw driver James Hogan ` (8 subsequent siblings) 10 siblings, 0 replies; 21+ messages in thread From: James Hogan @ 2013-12-13 15:12 UTC (permalink / raw) To: Mauro Carvalho Chehab, linux-media Cc: James Hogan, Grant Likely, Rob Herring, devicetree Add base driver for the ImgTec Infrared decoder block. The driver is split into separate components for raw (software) decode and hardware decoder which are in following commits. Signed-off-by: James Hogan <james.hogan@imgtec.com> Cc: Mauro Carvalho Chehab <m.chehab@samsung.com> Cc: linux-media@vger.kernel.org Cc: Grant Likely <grant.likely@linaro.org> Cc: Rob Herring <rob.herring@calxeda.com> Cc: devicetree@vger.kernel.org --- drivers/media/rc/img-ir/img-ir-core.c | 172 ++++++++++++++++++++++++++++++++++ drivers/media/rc/img-ir/img-ir.h | 170 +++++++++++++++++++++++++++++++++ 2 files changed, 342 insertions(+) create mode 100644 drivers/media/rc/img-ir/img-ir-core.c create mode 100644 drivers/media/rc/img-ir/img-ir.h diff --git a/drivers/media/rc/img-ir/img-ir-core.c b/drivers/media/rc/img-ir/img-ir-core.c new file mode 100644 index 000000000000..a577217aa739 --- /dev/null +++ b/drivers/media/rc/img-ir/img-ir-core.c @@ -0,0 +1,172 @@ +/* + * ImgTec IR Decoder found in PowerDown Controller. + * + * Copyright 2010-2013 Imagination Technologies Ltd. + * + * This contains core img-ir code for setting up the driver. The two interfaces + * (raw and hardware decode) are handled separately. + */ + +#include <linux/clk.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include "img-ir.h" + +static irqreturn_t img_ir_isr(int irq, void *dev_id) +{ + struct img_ir_priv *priv = dev_id; + u32 irq_status; + + spin_lock(&priv->lock); + /* we have to clear irqs before reading */ + irq_status = img_ir_read(priv, IMG_IR_IRQ_STATUS); + img_ir_write(priv, IMG_IR_IRQ_CLEAR, irq_status); + + /* don't handle valid data irqs if we're only interested in matches */ + irq_status &= img_ir_read(priv, IMG_IR_IRQ_ENABLE); + + /* hand off edge interrupts to raw decode handler */ + if (irq_status & IMG_IR_IRQ_EDGE && img_ir_raw_enabled(&priv->raw)) + img_ir_isr_raw(priv, irq_status); + + /* hand off hardware match interrupts to hardware decode handler */ + if (irq_status & (IMG_IR_IRQ_DATA_MATCH | + IMG_IR_IRQ_DATA_VALID | + IMG_IR_IRQ_DATA2_VALID) && + img_ir_hw_enabled(&priv->hw)) + img_ir_isr_hw(priv, irq_status); + + spin_unlock(&priv->lock); + return IRQ_HANDLED; +} + +static void img_ir_setup(struct img_ir_priv *priv) +{ + /* start off with interrupts disabled */ + img_ir_write(priv, IMG_IR_IRQ_ENABLE, 0); + + img_ir_setup_raw(priv); + img_ir_setup_hw(priv); + + if (!IS_ERR(priv->clk)) + clk_prepare_enable(priv->clk); +} + +static void img_ir_ident(struct img_ir_priv *priv) +{ + u32 core_rev = img_ir_read(priv, IMG_IR_CORE_REV); + + dev_info(priv->dev, + "IMG IR Decoder (%d.%d.%d.%d) probed successfully\n", + (core_rev & IMG_IR_DESIGNER) >> IMG_IR_DESIGNER_SHIFT, + (core_rev & IMG_IR_MAJOR_REV) >> IMG_IR_MAJOR_REV_SHIFT, + (core_rev & IMG_IR_MINOR_REV) >> IMG_IR_MINOR_REV_SHIFT, + (core_rev & IMG_IR_MAINT_REV) >> IMG_IR_MAINT_REV_SHIFT); + dev_info(priv->dev, "Modes:%s%s\n", + img_ir_hw_enabled(&priv->hw) ? " hardware" : "", + img_ir_raw_enabled(&priv->raw) ? " raw" : ""); +} + +static int img_ir_probe(struct platform_device *pdev) +{ + struct img_ir_priv *priv; + struct resource *res_regs; + int irq, error, error2; + + /* Get resources from platform device */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "cannot find IRQ resource\n"); + return irq; + } + + /* Private driver data */ + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(&pdev->dev, "cannot allocate device data\n"); + return -ENOMEM; + } + platform_set_drvdata(pdev, priv); + priv->dev = &pdev->dev; + spin_lock_init(&priv->lock); + + /* Ioremap the registers */ + res_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->reg_base = devm_ioremap_resource(&pdev->dev, res_regs); + if (IS_ERR(priv->reg_base)) + return PTR_ERR(priv->reg_base); + + /* Get clock */ + priv->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(priv->clk)) + dev_warn(&pdev->dev, "cannot get clock resource\n"); + + /* Set up raw & hw decoder */ + error = img_ir_probe_raw(priv); + error2 = img_ir_probe_hw(priv); + if (error && error2) + return (error == -ENODEV) ? error2 : error; + + /* Get the IRQ */ + priv->irq = irq; + error = request_irq(priv->irq, img_ir_isr, 0, "img-ir", priv); + if (error) { + dev_err(&pdev->dev, "cannot register IRQ %u\n", + priv->irq); + error = -EIO; + goto err_irq; + } + + img_ir_ident(priv); + img_ir_setup(priv); + + return 0; + +err_irq: + img_ir_remove_hw(priv); + img_ir_remove_raw(priv); + return error; +} + +static int img_ir_remove(struct platform_device *pdev) +{ + struct img_ir_priv *priv = platform_get_drvdata(pdev); + + free_irq(priv->irq, img_ir_isr); + img_ir_remove_hw(priv); + img_ir_remove_raw(priv); + + if (!IS_ERR(priv->clk)) + clk_disable_unprepare(priv->clk); + return 0; +} + +static SIMPLE_DEV_PM_OPS(img_ir_pmops, img_ir_suspend, img_ir_resume); + +static const struct of_device_id img_ir_match[] = { + { .compatible = "img,ir" }, + {} +}; +MODULE_DEVICE_TABLE(of, img_ir_match); + +static struct platform_driver img_ir_driver = { + .driver = { + .name = "img-ir", + .owner = THIS_MODULE, + .of_match_table = img_ir_match, + .pm = &img_ir_pmops, + }, + .probe = img_ir_probe, + .remove = img_ir_remove, +}; + +module_platform_driver(img_ir_driver); + +MODULE_AUTHOR("Imagination Technologies Ltd."); +MODULE_DESCRIPTION("ImgTec IR"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/rc/img-ir/img-ir.h b/drivers/media/rc/img-ir/img-ir.h new file mode 100644 index 000000000000..950cf90573c8 --- /dev/null +++ b/drivers/media/rc/img-ir/img-ir.h @@ -0,0 +1,170 @@ +/* + * ImgTec IR Decoder found in PowerDown Controller. + * + * Copyright 2010-2013 Imagination Technologies Ltd. + */ + +#ifndef _IMG_IR_H_ +#define _IMG_IR_H_ + +#include <linux/spinlock.h> + +#include "img-ir-raw.h" +#include "img-ir-hw.h" + +/* registers */ + +/* relative to the start of the IR block of registers */ +#define IMG_IR_CONTROL 0x00 +#define IMG_IR_STATUS 0x04 +#define IMG_IR_DATA_LW 0x08 +#define IMG_IR_DATA_UP 0x0c +#define IMG_IR_LEAD_SYMB_TIMING 0x10 +#define IMG_IR_S00_SYMB_TIMING 0x14 +#define IMG_IR_S01_SYMB_TIMING 0x18 +#define IMG_IR_S10_SYMB_TIMING 0x1c +#define IMG_IR_S11_SYMB_TIMING 0x20 +#define IMG_IR_FREE_SYMB_TIMING 0x24 +#define IMG_IR_POW_MOD_PARAMS 0x28 +#define IMG_IR_POW_MOD_ENABLE 0x2c +#define IMG_IR_IRQ_MSG_DATA_LW 0x30 +#define IMG_IR_IRQ_MSG_DATA_UP 0x34 +#define IMG_IR_IRQ_MSG_MASK_LW 0x38 +#define IMG_IR_IRQ_MSG_MASK_UP 0x3c +#define IMG_IR_IRQ_ENABLE 0x40 +#define IMG_IR_IRQ_STATUS 0x44 +#define IMG_IR_IRQ_CLEAR 0x48 +#define IMG_IR_IRCORE_ID 0xf0 +#define IMG_IR_CORE_REV 0xf4 +#define IMG_IR_CORE_DES1 0xf8 +#define IMG_IR_CORE_DES2 0xfc + + +/* field masks */ + +/* IMG_IR_CONTROL */ +#define IMG_IR_DECODEN 0x40000000 +#define IMG_IR_CODETYPE 0x30000000 +#define IMG_IR_CODETYPE_SHIFT 28 +#define IMG_IR_HDRTOG 0x08000000 +#define IMG_IR_LDRDEC 0x04000000 +#define IMG_IR_DECODINPOL 0x02000000 /* active high */ +#define IMG_IR_BITORIEN 0x01000000 /* MSB first */ +#define IMG_IR_D1VALIDSEL 0x00008000 +#define IMG_IR_BITINV 0x00000040 /* don't invert */ +#define IMG_IR_DECODEND2 0x00000010 +#define IMG_IR_BITORIEND2 0x00000002 /* MSB first */ +#define IMG_IR_BITINVD2 0x00000001 /* don't invert */ + +/* IMG_IR_STATUS */ +#define IMG_IR_RXDVALD2 0x00001000 +#define IMG_IR_IRRXD 0x00000400 +#define IMG_IR_TOGSTATE 0x00000200 +#define IMG_IR_RXDVAL 0x00000040 +#define IMG_IR_RXDLEN 0x0000003f +#define IMG_IR_RXDLEN_SHIFT 0 + +/* IMG_IR_LEAD_SYMB_TIMING, IMG_IR_Sxx_SYMB_TIMING */ +#define IMG_IR_PD_MAX 0xff000000 +#define IMG_IR_PD_MAX_SHIFT 24 +#define IMG_IR_PD_MIN 0x00ff0000 +#define IMG_IR_PD_MIN_SHIFT 16 +#define IMG_IR_W_MAX 0x0000ff00 +#define IMG_IR_W_MAX_SHIFT 8 +#define IMG_IR_W_MIN 0x000000ff +#define IMG_IR_W_MIN_SHIFT 0 + +/* IMG_IR_FREE_SYMB_TIMING */ +#define IMG_IR_MAXLEN 0x0007e000 +#define IMG_IR_MAXLEN_SHIFT 13 +#define IMG_IR_MINLEN 0x00001f00 +#define IMG_IR_MINLEN_SHIFT 8 +#define IMG_IR_FT_MIN 0x000000ff +#define IMG_IR_FT_MIN_SHIFT 0 + +/* IMG_IR_POW_MOD_PARAMS */ +#define IMG_IR_PERIOD_LEN 0x3f000000 +#define IMG_IR_PERIOD_LEN_SHIFT 24 +#define IMG_IR_PERIOD_DUTY 0x003f0000 +#define IMG_IR_PERIOD_DUTY_SHIFT 16 +#define IMG_IR_STABLE_STOP 0x00003f00 +#define IMG_IR_STABLE_STOP_SHIFT 8 +#define IMG_IR_STABLE_START 0x0000003f +#define IMG_IR_STABLE_START_SHIFT 0 + +/* IMG_IR_POW_MOD_ENABLE */ +#define IMG_IR_POWER_OUT_EN 0x00000002 +#define IMG_IR_POWER_MOD_EN 0x00000001 + +/* IMG_IR_IRQ_ENABLE, IMG_IR_IRQ_STATUS, IMG_IR_IRQ_CLEAR */ +#define IMG_IR_IRQ_DEC2_ERR 0x00000080 +#define IMG_IR_IRQ_DEC_ERR 0x00000040 +#define IMG_IR_IRQ_ACT_LEVEL 0x00000020 +#define IMG_IR_IRQ_FALL_EDGE 0x00000010 +#define IMG_IR_IRQ_RISE_EDGE 0x00000008 +#define IMG_IR_IRQ_DATA_MATCH 0x00000004 +#define IMG_IR_IRQ_DATA2_VALID 0x00000002 +#define IMG_IR_IRQ_DATA_VALID 0x00000001 +#define IMG_IR_IRQ_ALL 0x000000ff +#define IMG_IR_IRQ_EDGE (IMG_IR_IRQ_FALL_EDGE | IMG_IR_IRQ_RISE_EDGE) + +/* IMG_IR_CORE_ID */ +#define IMG_IR_CORE_ID 0x00ff0000 +#define IMG_IR_CORE_ID_SHIFT 16 +#define IMG_IR_CORE_CONFIG 0x0000ffff +#define IMG_IR_CORE_CONFIG_SHIFT 0 + +/* IMG_IR_CORE_REV */ +#define IMG_IR_DESIGNER 0xff000000 +#define IMG_IR_DESIGNER_SHIFT 24 +#define IMG_IR_MAJOR_REV 0x00ff0000 +#define IMG_IR_MAJOR_REV_SHIFT 16 +#define IMG_IR_MINOR_REV 0x0000ff00 +#define IMG_IR_MINOR_REV_SHIFT 8 +#define IMG_IR_MAINT_REV 0x000000ff +#define IMG_IR_MAINT_REV_SHIFT 0 + +struct device; +struct clk; + +/** + * struct img_ir_priv - Private driver data. + * @next: Next IR device's private driver data (to form a linked + * list). + * @dev: Platform device. + * @irq: IRQ number. + * @clk: Input clock. + * @reg_base: Iomem base address of IR register block. + * @lock: Protects IR registers and variables in this struct. + * @raw: Driver data for raw decoder. + * @hw: Driver data for hardware decoder. + */ +struct img_ir_priv { + /* this priv sits in a global list protected by img_ir_decoders_lock */ + struct img_ir_priv *next; + + struct device *dev; + int irq; + struct clk *clk; + void __iomem *reg_base; + spinlock_t lock; + + struct img_ir_priv_raw raw; + struct img_ir_priv_hw hw; +}; + +/* Hardware access */ + +static inline void img_ir_write(struct img_ir_priv *priv, + unsigned int reg_offs, unsigned int data) +{ + iowrite32(data, priv->reg_base + reg_offs); +} + +static inline unsigned int img_ir_read(struct img_ir_priv *priv, + unsigned int reg_offs) +{ + return ioread32(priv->reg_base + reg_offs); +} + +#endif /* _IMG_IR_H_ */ -- 1.8.1.2 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 03/11] media: rc: img-ir: add raw driver 2013-12-13 15:12 [PATCH 00/11] media: rc: ImgTec IR decoder driver James Hogan 2013-12-13 15:12 ` [PATCH 01/11] dt: binding: add binding for ImgTec IR block James Hogan 2013-12-13 15:12 ` [PATCH 02/11] media: rc: img-ir: add base driver James Hogan @ 2013-12-13 15:12 ` James Hogan 2013-12-13 15:12 ` [PATCH 04/11] media: rc: img-ir: add hardware decoder driver James Hogan ` (7 subsequent siblings) 10 siblings, 0 replies; 21+ messages in thread From: James Hogan @ 2013-12-13 15:12 UTC (permalink / raw) To: Mauro Carvalho Chehab, linux-media; +Cc: James Hogan Add raw IR remote control input driver for the ImgTec Infrared decoder block raw edge interrupts. Generic software protocol decoders are used to allow multiple protocols to be supported at a time, including those not supported by the hardware decoder. 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/img-ir-raw.c | 107 +++++++++++++++++++++++++++++++++++ drivers/media/rc/img-ir/img-ir-raw.h | 58 +++++++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 drivers/media/rc/img-ir/img-ir-raw.c create mode 100644 drivers/media/rc/img-ir/img-ir-raw.h diff --git a/drivers/media/rc/img-ir/img-ir-raw.c b/drivers/media/rc/img-ir/img-ir-raw.c new file mode 100644 index 000000000000..7e90300d6daf --- /dev/null +++ b/drivers/media/rc/img-ir/img-ir-raw.c @@ -0,0 +1,107 @@ +/* + * ImgTec IR Raw Decoder found in PowerDown Controller. + * + * Copyright 2010-2013 Imagination Technologies Ltd. + * + * This ties into the input subsystem using the RC-core in raw mode. Raw IR + * signal edges are reported and decoded by generic software decoders. + */ + +#include <linux/spinlock.h> +#include <media/rc-core.h> +#include "img-ir.h" + +void img_ir_isr_raw(struct img_ir_priv *priv, u32 irq_status) +{ + struct img_ir_priv_raw *raw = &priv->raw; + struct rc_dev *rc_dev = priv->raw.rdev; + int multiple; + u32 ir_status; + + /* find whether both rise and fall was detected */ + multiple = ((irq_status & IMG_IR_IRQ_EDGE) == IMG_IR_IRQ_EDGE); + /* + * If so, we need to see if the level has actually changed. + * If it's just noise that we didn't have time to process, + * there's no point reporting it. + */ + ir_status = img_ir_read(priv, IMG_IR_STATUS) & IMG_IR_IRRXD; + if (multiple && ir_status == raw->last_status) + return; + raw->last_status = ir_status; + + /* report the edge to the IR raw decoders */ + if (ir_status) /* low */ + ir_raw_event_store_edge(rc_dev, IR_SPACE); + else /* high */ + ir_raw_event_store_edge(rc_dev, IR_PULSE); + ir_raw_event_handle(rc_dev); +} + +void img_ir_setup_raw(struct img_ir_priv *priv) +{ + u32 irq_en; + unsigned long flags; + + if (!priv->raw.rdev) + return; + + /* clear and enable edge interrupts */ + spin_lock_irqsave(&priv->lock, flags); + irq_en = img_ir_read(priv, IMG_IR_IRQ_ENABLE); + irq_en |= IMG_IR_IRQ_EDGE; + img_ir_write(priv, IMG_IR_IRQ_CLEAR, IMG_IR_IRQ_EDGE); + img_ir_write(priv, IMG_IR_IRQ_ENABLE, irq_en); + spin_unlock_irqrestore(&priv->lock, flags); +} + +int img_ir_probe_raw(struct img_ir_priv *priv) +{ + struct img_ir_priv_raw *raw = &priv->raw; + struct rc_dev *rdev; + int error; + + /* Allocate raw decoder */ + raw->rdev = rdev = rc_allocate_device(); + if (!rdev) { + dev_err(priv->dev, "cannot allocate raw input device\n"); + return -ENOMEM; + } + rdev->priv = priv; + rdev->map_name = RC_MAP_EMPTY; + rdev->input_name = "IMG Infrared Decoder Raw"; + rdev->driver_type = RC_DRIVER_IR_RAW; + + /* Register raw decoder */ + error = rc_register_device(rdev); + if (error) { + dev_err(priv->dev, "failed to register raw IR input device\n"); + rc_free_device(rdev); + raw->rdev = NULL; + return error; + } + + return 0; +} + +void img_ir_remove_raw(struct img_ir_priv *priv) +{ + struct img_ir_priv_raw *raw = &priv->raw; + struct rc_dev *rdev = raw->rdev; + unsigned long flags; + u32 irq_en; + + if (!rdev) + return; + + /* switch off and disable raw (edge) interrupts */ + spin_lock_irqsave(&priv->lock, flags); + raw->rdev = NULL; + irq_en = img_ir_read(priv, IMG_IR_IRQ_ENABLE); + irq_en &= ~IMG_IR_IRQ_EDGE; + img_ir_write(priv, IMG_IR_IRQ_ENABLE, irq_en); + img_ir_write(priv, IMG_IR_IRQ_CLEAR, IMG_IR_IRQ_EDGE); + spin_unlock_irqrestore(&priv->lock, flags); + + rc_unregister_device(rdev); +} diff --git a/drivers/media/rc/img-ir/img-ir-raw.h b/drivers/media/rc/img-ir/img-ir-raw.h new file mode 100644 index 000000000000..d302a65e19dc --- /dev/null +++ b/drivers/media/rc/img-ir/img-ir-raw.h @@ -0,0 +1,58 @@ +/* + * ImgTec IR Raw Decoder found in PowerDown Controller. + * + * Copyright 2010-2013 Imagination Technologies Ltd. + */ + +#ifndef _IMG_IR_RAW_H_ +#define _IMG_IR_RAW_H_ + +struct img_ir_priv; + +#ifdef CONFIG_IR_IMG_RAW + +/** + * struct img_ir_priv_raw - Private driver data for raw decoder. + * @rdev: Raw remote control device + * @last_status: Last raw status bits. + */ +struct img_ir_priv_raw { + struct rc_dev *rdev; + u32 last_status; +}; + +static inline bool img_ir_raw_enabled(struct img_ir_priv_raw *raw) +{ + return raw->rdev; +}; + +void img_ir_isr_raw(struct img_ir_priv *priv, u32 irq_status); +void img_ir_setup_raw(struct img_ir_priv *priv); +int img_ir_probe_raw(struct img_ir_priv *priv); +void img_ir_remove_raw(struct img_ir_priv *priv); + +#else + +struct img_ir_priv_raw { +}; +static inline bool img_ir_raw_enabled(struct img_ir_priv_raw *raw) +{ + return false; +}; +static inline void img_ir_isr_raw(struct img_ir_priv *priv, u32 irq_status) +{ +} +static inline void img_ir_setup_raw(struct img_ir_priv *priv) +{ +} +static inline int img_ir_probe_raw(struct img_ir_priv *priv) +{ + return -ENODEV; +} +static inline void img_ir_remove_raw(struct img_ir_priv *priv) +{ +} + +#endif /* CONFIG_IR_IMG_RAW */ + +#endif /* _IMG_IR_RAW_H_ */ -- 1.8.1.2 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 04/11] media: rc: img-ir: add hardware decoder driver 2013-12-13 15:12 [PATCH 00/11] media: rc: ImgTec IR decoder driver James Hogan ` (2 preceding siblings ...) 2013-12-13 15:12 ` [PATCH 03/11] media: rc: img-ir: add raw driver James Hogan @ 2013-12-13 15:12 ` James Hogan 2013-12-22 13:40 ` Mauro Carvalho Chehab 2013-12-13 15:12 ` [PATCH 05/11] media: rc: img-ir: add to build James Hogan ` (6 subsequent siblings) 10 siblings, 1 reply; 21+ messages in thread From: James Hogan @ 2013-12-13 15:12 UTC (permalink / raw) To: Mauro Carvalho Chehab, linux-media; +Cc: James Hogan Add remote control input driver for the ImgTec Infrared block hardware decoder, which is set up with timings for a specific protocol and supports mask/value filtering and wake events. The hardware decoder timing values, raw data to scan code conversion function and scan code filter to raw data filter conversion function are provided as separate modules for each protocol which this part of the driver can use. The scan code filter value and mask (and the same again for wake from sleep) are specified via sysfs files in /sys/class/rc/rcX/. 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/img-ir-hw.c | 1277 +++++++++++++++++++++++++++++++++++ drivers/media/rc/img-ir/img-ir-hw.h | 284 ++++++++ 2 files changed, 1561 insertions(+) create mode 100644 drivers/media/rc/img-ir/img-ir-hw.c create mode 100644 drivers/media/rc/img-ir/img-ir-hw.h diff --git a/drivers/media/rc/img-ir/img-ir-hw.c b/drivers/media/rc/img-ir/img-ir-hw.c new file mode 100644 index 000000000000..917d9a076a8c --- /dev/null +++ b/drivers/media/rc/img-ir/img-ir-hw.c @@ -0,0 +1,1277 @@ +/* + * ImgTec IR Hardware Decoder found in PowerDown Controller. + * + * Copyright 2010-2013 Imagination Technologies Ltd. + * + * This ties into the input subsystem using the RC-core. Protocol support is + * provided in separate modules which provide the parameters and scancode + * translation functions to set up the hardware decoder and interpret the + * resulting input. + */ + +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/timer.h> +#include <media/rc-core.h> +#include "img-ir.h" + +/* Decoder list */ +static DEFINE_SPINLOCK(img_ir_decoders_lock); +static struct img_ir_decoder *img_ir_decoders; +static struct img_ir_priv *img_ir_privs; + +#define IMG_IR_F_FILTER 0x00000001 /* enable filtering */ +#define IMG_IR_F_WAKE 0x00000002 /* enable waking */ + +/* code type quirks */ + +#define IMG_IR_QUIRK_CODE_BROKEN 0x1 /* Decode is broken */ +#define IMG_IR_QUIRK_CODE_LEN_INCR 0x2 /* Bit length needs increment */ + +/* functions for preprocessing timings, ensuring max is set */ + +static void img_ir_timing_preprocess(struct img_ir_timing_range *range, + unsigned int unit) +{ + if (range->max < range->min) + range->max = range->min; + if (unit) { + /* multiply by unit and convert to microseconds */ + range->min = (range->min*unit)/1000; + range->max = (range->max*unit + 999)/1000; /* round up */ + } +} + +static void img_ir_symbol_timing_preprocess(struct img_ir_symbol_timing *timing, + unsigned int unit) +{ + img_ir_timing_preprocess(&timing->pulse, unit); + img_ir_timing_preprocess(&timing->space, unit); +} + +static void img_ir_timings_preprocess(struct img_ir_timings *timings, + unsigned int unit) +{ + img_ir_symbol_timing_preprocess(&timings->ldr, unit); + img_ir_symbol_timing_preprocess(&timings->s00, unit); + img_ir_symbol_timing_preprocess(&timings->s01, unit); + img_ir_symbol_timing_preprocess(&timings->s10, unit); + img_ir_symbol_timing_preprocess(&timings->s11, unit); + /* default s10 and s11 to s00 and s01 if no leader */ + if (unit) + /* multiply by unit and convert to microseconds (round up) */ + timings->ft.ft_min = (timings->ft.ft_min*unit + 999)/1000; +} + +/* functions for filling empty fields with defaults */ + +static void img_ir_timing_defaults(struct img_ir_timing_range *range, + struct img_ir_timing_range *defaults) +{ + if (!range->min) + range->min = defaults->min; + if (!range->max) + range->max = defaults->max; +} + +static void img_ir_symbol_timing_defaults(struct img_ir_symbol_timing *timing, + struct img_ir_symbol_timing *defaults) +{ + img_ir_timing_defaults(&timing->pulse, &defaults->pulse); + img_ir_timing_defaults(&timing->space, &defaults->space); +} + +static void img_ir_timings_defaults(struct img_ir_timings *timings, + struct img_ir_timings *defaults) +{ + img_ir_symbol_timing_defaults(&timings->ldr, &defaults->ldr); + img_ir_symbol_timing_defaults(&timings->s00, &defaults->s00); + img_ir_symbol_timing_defaults(&timings->s01, &defaults->s01); + img_ir_symbol_timing_defaults(&timings->s10, &defaults->s10); + img_ir_symbol_timing_defaults(&timings->s11, &defaults->s11); + if (!timings->ft.ft_min) + timings->ft.ft_min = defaults->ft.ft_min; +} + +/* functions for converting timings to register values */ + +/** + * img_ir_control() - Convert control struct to control register value. + * @control: Control data + * + * Returns: The control register value equivalent of @control. + */ +static u32 img_ir_control(struct img_ir_control *control) +{ + u32 ctrl = control->code_type << IMG_IR_CODETYPE_SHIFT; + if (control->decoden) + ctrl |= IMG_IR_DECODEN; + if (control->hdrtog) + ctrl |= IMG_IR_HDRTOG; + if (control->ldrdec) + ctrl |= IMG_IR_LDRDEC; + if (control->decodinpol) + ctrl |= IMG_IR_DECODINPOL; + if (control->bitorien) + ctrl |= IMG_IR_BITORIEN; + if (control->d1validsel) + ctrl |= IMG_IR_D1VALIDSEL; + if (control->bitinv) + ctrl |= IMG_IR_BITINV; + if (control->decodend2) + ctrl |= IMG_IR_DECODEND2; + if (control->bitoriend2) + ctrl |= IMG_IR_BITORIEND2; + if (control->bitinvd2) + ctrl |= IMG_IR_BITINVD2; + return ctrl; +} + +/** + * img_ir_timing_range_convert() - Convert microsecond range. + * @out: Output timing range in clock cycles with a shift. + * @in: Input timing range in microseconds. + * @tolerance: Tolerance as a fraction of 128 (roughly percent). + * @clock_hz: IR clock rate in Hz. + * @shift: Shift of output units. + * + * Converts min and max from microseconds to IR clock cycles, applies a + * tolerance, and shifts for the register, rounding in the right direction. + * Note that in and out can safely be the same object. + */ +static void img_ir_timing_range_convert(struct img_ir_timing_range *out, + const struct img_ir_timing_range *in, + unsigned int tolerance, + unsigned long clock_hz, + unsigned int shift) +{ + unsigned int min = in->min; + unsigned int max = in->max; + /* add a tolerance */ + min = min - (min*tolerance >> 7); + max = max + (max*tolerance >> 7); + /* convert from microseconds into clock cycles */ + min = min*clock_hz / 1000000; + max = (max*clock_hz + 999999) / 1000000; /* round up */ + /* apply shift and copy to output */ + out->min = min >> shift; + out->max = (max + ((1 << shift) - 1)) >> shift; /* round up */ +} + +/** + * img_ir_symbol_timing() - Convert symbol timing struct to register value. + * @timing: Symbol timing data + * @tolerance: Timing tolerance where 0-128 represents 0-100% + * @clock_hz: Frequency of source clock in Hz + * @pd_shift: Shift to apply to symbol period + * @w_shift: Shift to apply to symbol width + * + * Returns: Symbol timing register value based on arguments. + */ +static u32 img_ir_symbol_timing(const struct img_ir_symbol_timing *timing, + unsigned int tolerance, + unsigned long clock_hz, + unsigned int pd_shift, + unsigned int w_shift) +{ + struct img_ir_timing_range hw_pulse, hw_period; + /* we calculate period in hw_period, then convert in place */ + hw_period.min = timing->pulse.min + timing->space.min; + hw_period.max = timing->pulse.max + timing->space.max; + img_ir_timing_range_convert(&hw_period, &hw_period, + tolerance, clock_hz, pd_shift); + img_ir_timing_range_convert(&hw_pulse, &timing->pulse, + tolerance, clock_hz, w_shift); + /* construct register value */ + return (hw_period.max << IMG_IR_PD_MAX_SHIFT) | + (hw_period.min << IMG_IR_PD_MIN_SHIFT) | + (hw_pulse.max << IMG_IR_W_MAX_SHIFT) | + (hw_pulse.min << IMG_IR_W_MIN_SHIFT); +} + +/** + * img_ir_free_timing() - Convert free time timing struct to register value. + * @timing: Free symbol timing data + * @clock_hz: Source clock frequency in Hz + * + * Returns: Free symbol timing register value. + */ +static u32 img_ir_free_timing(const struct img_ir_free_timing *timing, + unsigned long clock_hz) +{ + unsigned int minlen, maxlen, ft_min; + /* minlen is only 5 bits, and round minlen to multiple of 2 */ + if (timing->minlen < 30) + minlen = timing->minlen & -2; + else + minlen = 30; + /* maxlen has maximum value of 48, and round maxlen to multiple of 2 */ + if (timing->maxlen < 48) + maxlen = (timing->maxlen + 1) & -2; + else + maxlen = 48; + /* convert and shift ft_min, rounding upwards */ + ft_min = (timing->ft_min*clock_hz + 999999) / 1000000; + ft_min = (ft_min + 7) >> 3; + /* construct register value */ + return (timing->maxlen << IMG_IR_MAXLEN_SHIFT) | + (timing->minlen << IMG_IR_MINLEN_SHIFT) | + (ft_min << IMG_IR_FT_MIN_SHIFT); +} + +/** + * img_ir_free_timing_dynamic() - Update free time register value. + * @st_ft: Static free time register value from img_ir_free_timing. + * @filter: Current filter which may additionally restrict min/max len. + * + * Returns: Updated free time register value based on the current filter. + */ +static u32 img_ir_free_timing_dynamic(u32 st_ft, struct img_ir_filter *filter) +{ + unsigned int minlen, maxlen, newminlen, newmaxlen; + + /* round minlen, maxlen to multiple of 2 */ + newminlen = filter->minlen & -2; + newmaxlen = (filter->maxlen + 1) & -2; + /* extract min/max len from register */ + minlen = (st_ft & IMG_IR_MINLEN) >> IMG_IR_MINLEN_SHIFT; + maxlen = (st_ft & IMG_IR_MAXLEN) >> IMG_IR_MAXLEN_SHIFT; + /* if the new values are more restrictive, update the register value */ + if (newminlen > minlen) { + st_ft &= ~IMG_IR_MINLEN; + st_ft |= newminlen << IMG_IR_MINLEN_SHIFT; + } + if (newmaxlen < maxlen) { + st_ft &= ~IMG_IR_MAXLEN; + st_ft |= newmaxlen << IMG_IR_MAXLEN_SHIFT; + } + return st_ft; +} + +/** + * img_ir_timings_convert() - Convert timings to register values + * @regs: Output timing register values + * @timings: Input timing data + * @tolerance: Timing tolerance where 0-128 represents 0-100% + * @clock_hz: Source clock frequency in Hz + */ +static void img_ir_timings_convert(struct img_ir_timing_regvals *regs, + const struct img_ir_timings *timings, + unsigned int tolerance, + unsigned int clock_hz) +{ + /* leader symbol timings are divided by 16 */ + regs->ldr = img_ir_symbol_timing(&timings->ldr, tolerance, clock_hz, + 4, 4); + /* other symbol timings, pd fields only are divided by 2 */ + regs->s00 = img_ir_symbol_timing(&timings->s00, tolerance, clock_hz, + 1, 0); + regs->s01 = img_ir_symbol_timing(&timings->s01, tolerance, clock_hz, + 1, 0); + regs->s10 = img_ir_symbol_timing(&timings->s10, tolerance, clock_hz, + 1, 0); + regs->s11 = img_ir_symbol_timing(&timings->s11, tolerance, clock_hz, + 1, 0); + regs->ft = img_ir_free_timing(&timings->ft, clock_hz); +} + +/** + * img_ir_decoder_preprocess() - Preprocess timings in decoder. + * @decoder: Decoder to be preprocessed. + * + * Ensures that the symbol timing ranges are valid with respect to ordering, and + * does some fixed conversion on them. + */ +static void img_ir_decoder_preprocess(struct img_ir_decoder *decoder) +{ + /* fill in implicit fields */ + img_ir_timings_preprocess(&decoder->timings, decoder->unit); + + /* do the same for repeat timings if applicable */ + if (decoder->repeat) { + img_ir_timings_preprocess(&decoder->rtimings, decoder->unit); + img_ir_timings_defaults(&decoder->rtimings, &decoder->timings); + } + + /* calculate control value */ + decoder->reg_ctrl = img_ir_control(&decoder->control); +} + +/** + * img_ir_decoder_convert() - Generate internal timings in decoder. + * @decoder: Decoder to be converted to internal timings. + * @tolerance: Tolerance as percent. + * @clock_hz: IR clock rate in Hz. + * + * Fills out the repeat timings and timing register values for a specific + * tolerance and clock rate. + */ +static void img_ir_decoder_convert(struct img_ir_decoder *decoder, + unsigned int tolerance, + unsigned int clock_hz) +{ + tolerance = tolerance * 128 / 100; + + /* record clock rate in case timings need recalculating */ + decoder->clk_hz = clock_hz; + + /* fill in implicit fields and calculate register values */ + img_ir_timings_convert(&decoder->reg_timings, &decoder->timings, + tolerance, clock_hz); + + /* do the same for repeat timings if applicable */ + if (decoder->repeat) + img_ir_timings_convert(&decoder->reg_rtimings, + &decoder->rtimings, tolerance, clock_hz); +} + +/** + * img_ir_decoder_check_timings() - Check if decoder timings need updating. + * @priv: IR private data. + */ +static void img_ir_check_timings(struct img_ir_priv *priv) +{ + struct img_ir_priv_hw *hw = &priv->hw; + struct img_ir_decoder *dec = hw->decoder; + if (dec->clk_hz != hw->clk_hz) { + dev_dbg(priv->dev, "converting tolerance=%d%%, clk=%lu\n", + 10, hw->clk_hz); + img_ir_decoder_convert(dec, 10, hw->clk_hz); + } +} + +/** + * img_ir_write_timings() - Write timings to the hardware now + * @priv: IR private data + * @regs: Timing register values to write + * @filter: Current filter data or NULL + * + * Write timing register values @regs to the hardware, taking into account the + * current filter pointed to by @filter which may impose restrictions on the + * length of the expected data. + */ +static void img_ir_write_timings(struct img_ir_priv *priv, + struct img_ir_timing_regvals *regs, + struct img_ir_filter *filter) +{ + /* filter may be more restrictive to minlen, maxlen */ + u32 ft = regs->ft; + if (filter) + ft = img_ir_free_timing_dynamic(regs->ft, filter); + /* write to registers */ + img_ir_write(priv, IMG_IR_LEAD_SYMB_TIMING, regs->ldr); + img_ir_write(priv, IMG_IR_S00_SYMB_TIMING, regs->s00); + img_ir_write(priv, IMG_IR_S01_SYMB_TIMING, regs->s01); + img_ir_write(priv, IMG_IR_S10_SYMB_TIMING, regs->s10); + img_ir_write(priv, IMG_IR_S11_SYMB_TIMING, regs->s11); + img_ir_write(priv, IMG_IR_FREE_SYMB_TIMING, ft); + dev_dbg(priv->dev, "timings: ldr=%#x, s=[%#x, %#x, %#x, %#x], ft=%#x\n", + regs->ldr, regs->s00, regs->s01, regs->s10, regs->s11, ft); +} + +/** + * img_ir_write_timings_normal() - Write normal timings to the hardware now + * @priv: IR private data + * @regs: Normal timing register values to write + * + * Write the normal (non-wake) timing register values @regs to the hardware, + * taking into account the current filter. + */ +static void img_ir_write_timings_normal(struct img_ir_priv *priv, + struct img_ir_timing_regvals *regs) +{ + struct img_ir_priv_hw *hw = &priv->hw; + struct img_ir_filter *filter = NULL; + if (hw->flags & IMG_IR_F_FILTER) + filter = &hw->filter; + img_ir_write_timings(priv, regs, filter); +} + +#ifdef CONFIG_PM_SLEEP +/** + * img_ir_write_timings_wake() - Write wake timings to the hardware now + * @priv: IR private data + * @regs: Wake timing register values to write + * + * Write the wake timing register values @regs to the hardware, taking into + * account the current wake filter. + */ +static void img_ir_write_timings_wake(struct img_ir_priv *priv, + struct img_ir_timing_regvals *regs) +{ + struct img_ir_priv_hw *hw = &priv->hw; + struct img_ir_filter *filter = NULL; + if (hw->flags & IMG_IR_F_WAKE) + filter = &hw->wake_filter; + img_ir_write_timings(priv, regs, filter); +} +#endif + +static void img_ir_write_filter(struct img_ir_priv *priv, + struct img_ir_filter *filter) +{ + if (filter) { + dev_dbg(priv->dev, "IR filter=%016llx & %016llx\n", + (unsigned long long)filter->data, + (unsigned long long)filter->mask); + img_ir_write(priv, IMG_IR_IRQ_MSG_DATA_LW, (u32)filter->data); + img_ir_write(priv, IMG_IR_IRQ_MSG_DATA_UP, (u32)(filter->data + >> 32)); + img_ir_write(priv, IMG_IR_IRQ_MSG_MASK_LW, (u32)filter->mask); + img_ir_write(priv, IMG_IR_IRQ_MSG_MASK_UP, (u32)(filter->mask + >> 32)); + } else { + dev_dbg(priv->dev, "IR clearing filter\n"); + img_ir_write(priv, IMG_IR_IRQ_MSG_MASK_LW, 0); + img_ir_write(priv, IMG_IR_IRQ_MSG_MASK_UP, 0); + } +} + +/* caller must have lock */ +static void _img_ir_set_filter(struct img_ir_priv *priv, + struct img_ir_filter *filter) +{ + struct img_ir_priv_hw *hw = &priv->hw; + u32 irq_en, irq_on; + + irq_en = img_ir_read(priv, IMG_IR_IRQ_ENABLE); + if (filter) { + /* Only use the match interrupt */ + hw->filter = *filter; + hw->flags |= IMG_IR_F_FILTER; + irq_on = IMG_IR_IRQ_DATA_MATCH; + irq_en &= ~(IMG_IR_IRQ_DATA_VALID | IMG_IR_IRQ_DATA2_VALID); + } else { + /* Only use the valid interrupt */ + hw->flags &= ~IMG_IR_F_FILTER; + irq_en &= ~IMG_IR_IRQ_DATA_MATCH; + irq_on = IMG_IR_IRQ_DATA_VALID | IMG_IR_IRQ_DATA2_VALID; + } + irq_en |= irq_on; + + img_ir_write_filter(priv, filter); + /* clear any interrupts we're enabling so we don't handle old ones */ + img_ir_write(priv, IMG_IR_IRQ_CLEAR, irq_on); + img_ir_write(priv, IMG_IR_IRQ_ENABLE, irq_en); +} + +/* caller must have lock */ +static void _img_ir_set_wake_filter(struct img_ir_priv *priv, + struct img_ir_filter *filter) +{ + struct img_ir_priv_hw *hw = &priv->hw; + if (filter) { + /* Enable wake, and copy filter for later */ + hw->wake_filter = *filter; + hw->flags |= IMG_IR_F_WAKE; + } else { + /* Disable wake */ + hw->flags &= ~IMG_IR_F_WAKE; + } +} + +/* caller must have lock */ +static void _img_ir_update_filters(struct img_ir_priv *priv) +{ + struct img_ir_priv_hw *hw = &priv->hw; + struct img_ir_filter filter; + int ret1 = -1, ret2 = -1; + + /* clear raw filters */ + _img_ir_set_filter(priv, NULL); + _img_ir_set_wake_filter(priv, NULL); + + /* convert scancode filters to raw filters and try to set them */ + if (hw->decoder && hw->decoder->filter) { + if (hw->sc_filter.mask) { + filter.minlen = 0; + filter.maxlen = ~0; + dev_dbg(priv->dev, "IR scancode filter=%08x & %08x\n", + hw->sc_filter.data, + hw->sc_filter.mask); + ret1 = hw->decoder->filter(&hw->sc_filter, &filter, + hw->enabled_protocols); + if (!ret1) { + dev_dbg(priv->dev, "IR raw filter=%016llx & %016llx\n", + (unsigned long long)filter.data, + (unsigned long long)filter.mask); + _img_ir_set_filter(priv, &filter); + } + } + if (hw->sc_wake_filter.mask) { + filter.minlen = 0; + filter.maxlen = ~0; + dev_dbg(priv->dev, "IR scancode wake filter=%08x & %08x\n", + hw->sc_wake_filter.data, + hw->sc_wake_filter.mask); + ret2 = hw->decoder->filter(&hw->sc_wake_filter, + &filter, + hw->enabled_protocols); + if (!ret2) { + dev_dbg(priv->dev, "IR raw wake filter=%016llx & %016llx\n", + (unsigned long long)filter.data, + (unsigned long long)filter.mask); + _img_ir_set_wake_filter(priv, &filter); + } + } + } + + /* + * if either of the filters couldn't get set, clear the corresponding + * scancode filter mask. + */ + if (ret1) + hw->sc_filter.mask = 0; + if (ret2) + hw->sc_wake_filter.mask = 0; +} + +static void img_ir_update_filters(struct img_ir_priv *priv) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + _img_ir_update_filters(priv); + spin_unlock_irqrestore(&priv->lock, flags); +} + +/** + * img_ir_set_decoder() - Set the current decoder. + * @priv: IR private data. + * @decoder: Decoder to use with immediate effect. + * @proto: Protocol bitmap (or 0 to use decoder->type). + */ +static void img_ir_set_decoder(struct img_ir_priv *priv, + struct img_ir_decoder *decoder, + u64 proto) +{ + struct img_ir_priv_hw *hw = &priv->hw; + unsigned long flags; + u32 ir_status, irq_en; + spin_lock_irqsave(&priv->lock, flags); + + /* switch off and disable interrupts */ + img_ir_write(priv, IMG_IR_CONTROL, 0); + irq_en = img_ir_read(priv, IMG_IR_IRQ_ENABLE); + img_ir_write(priv, IMG_IR_IRQ_ENABLE, irq_en & IMG_IR_IRQ_EDGE); + img_ir_write(priv, IMG_IR_IRQ_CLEAR, IMG_IR_IRQ_ALL & ~IMG_IR_IRQ_EDGE); + + /* ack any data already detected */ + ir_status = img_ir_read(priv, IMG_IR_STATUS); + if (ir_status & (IMG_IR_RXDVAL | IMG_IR_RXDVALD2)) { + ir_status &= ~(IMG_IR_RXDVAL | IMG_IR_RXDVALD2); + img_ir_write(priv, IMG_IR_STATUS, ir_status); + img_ir_read(priv, IMG_IR_DATA_LW); + img_ir_read(priv, IMG_IR_DATA_UP); + } + + /* clear the scancode filters */ + hw->sc_filter.data = 0; + hw->sc_filter.mask = 0; + hw->sc_wake_filter.data = 0; + hw->sc_wake_filter.mask = 0; + + /* update (clear) the raw filters */ + _img_ir_update_filters(priv); + + /* clear the enabled protocols */ + hw->enabled_protocols = 0; + + /* switch decoder */ + hw->decoder = decoder; + if (!decoder) + goto unlock; + + hw->mode = IMG_IR_M_NORMAL; + + /* set the enabled protocols */ + if (!proto) + proto = decoder->type; + hw->enabled_protocols = proto; + + /* write the new timings */ + img_ir_check_timings(priv); + img_ir_write_timings_normal(priv, &decoder->reg_timings); + + /* set up and enable */ + img_ir_write(priv, IMG_IR_CONTROL, decoder->reg_ctrl); + + +unlock: + spin_unlock_irqrestore(&priv->lock, flags); +} + +/** + * img_ir_decoder_compatable() - Find whether a decoder will work with a device. + * @priv: IR private data. + * @dec: Decoder to check. + * + * Returns: true if @dec is compatible with the device @priv refers to. + */ +static bool img_ir_decoder_compatible(struct img_ir_priv *priv, + struct img_ir_decoder *dec) +{ + unsigned int ct; + + /* don't accept decoders using code types which aren't supported */ + ct = dec->control.code_type; + if (priv->hw.ct_quirks[ct] & IMG_IR_QUIRK_CODE_BROKEN) + return false; + + return true; +} + +/** + * img_ir_allowed_protos() - Get allowed protocols from global decoder list. + * @priv: IR private data. + * + * img_ir_decoders_lock must be locked by caller. + * + * Returns: Mask of protocols supported by the device @priv refers to. + */ +static unsigned long img_ir_allowed_protos(struct img_ir_priv *priv) +{ + unsigned long protos = 0; + struct img_ir_decoder *dec; + + for (dec = img_ir_decoders; dec; dec = dec->next) + if (img_ir_decoder_compatible(priv, dec)) + protos |= dec->type; + return protos; +} + +/** + * img_ir_update_allowed_protos() - Update devices with allowed protocols. + * + * img_ir_decoders_lock must be locked by caller. + */ +static void img_ir_update_allowed_protos(void) +{ + struct img_ir_priv *priv; + + for (priv = img_ir_privs; priv; priv = priv->next) + priv->hw.rdev->allowed_protos = img_ir_allowed_protos(priv); +} + +/* Callback for changing protocol using sysfs */ +static int img_ir_change_protocol(struct rc_dev *data, u64 *ir_type) +{ + struct img_ir_priv *priv; + struct img_ir_decoder *dec; + int ret = -EINVAL; + priv = data->priv; + + spin_lock(&img_ir_decoders_lock); + for (dec = img_ir_decoders; dec; dec = dec->next) { + if (!img_ir_decoder_compatible(priv, dec)) + continue; + if (*ir_type & dec->type) { + *ir_type &= dec->type; + img_ir_set_decoder(priv, dec, *ir_type); + ret = 0; + break; + } + } + spin_unlock(&img_ir_decoders_lock); + return ret; +} + +/* Changes ir-core protocol device attribute */ +static void img_ir_set_protocol(struct img_ir_priv *priv, u64 proto) +{ + struct rc_dev *ir_dev = priv->hw.rdev; + + unsigned long flags; + + spin_lock_irqsave(&ir_dev->rc_map.lock, flags); + ir_dev->rc_map.rc_type = proto; + spin_unlock_irqrestore(&ir_dev->rc_map.lock, flags); +} + +/* Register an ir decoder */ +int img_ir_register_decoder(struct img_ir_decoder *dec) +{ + struct img_ir_priv *priv; + + /* first preprocess decoder timings */ + img_ir_decoder_preprocess(dec); + + spin_lock(&img_ir_decoders_lock); + /* add to list */ + dec->next = img_ir_decoders; + img_ir_decoders = dec; + img_ir_update_allowed_protos(); + /* if it's the first decoder, start using it */ + for (priv = img_ir_privs; priv; priv = priv->next) { + if (!priv->hw.decoder && img_ir_decoder_compatible(priv, dec)) { + img_ir_set_protocol(priv, dec->type); + img_ir_set_decoder(priv, dec, 0); + } + } + spin_unlock(&img_ir_decoders_lock); + return 0; +} +EXPORT_SYMBOL_GPL(img_ir_register_decoder); + +/* Unregister an ir decoder */ +void img_ir_unregister_decoder(struct img_ir_decoder *dec) +{ + struct img_ir_priv *priv; + + spin_lock(&img_ir_decoders_lock); + /* If the decoder is in use, stop it now */ + for (priv = img_ir_privs; priv; priv = priv->next) + if (priv->hw.decoder == dec) { + img_ir_set_protocol(priv, 0); + img_ir_set_decoder(priv, NULL, 0); + } + /* Remove from list of decoders */ + if (img_ir_decoders == dec) { + img_ir_decoders = dec->next; + } else { + struct img_ir_decoder *cur; + for (cur = img_ir_decoders; cur; cur = cur->next) + if (dec == cur->next) { + cur->next = dec->next; + dec->next = NULL; + break; + } + } + img_ir_update_allowed_protos(); + spin_unlock(&img_ir_decoders_lock); +} +EXPORT_SYMBOL_GPL(img_ir_unregister_decoder); + +static void img_ir_register_device(struct img_ir_priv *priv) +{ + spin_lock(&img_ir_decoders_lock); + priv->next = img_ir_privs; + img_ir_privs = priv; + priv->hw.rdev->allowed_protos = img_ir_allowed_protos(priv); + spin_unlock(&img_ir_decoders_lock); +} + +static void img_ir_unregister_device(struct img_ir_priv *priv) +{ + struct img_ir_priv *cur; + + spin_lock(&img_ir_decoders_lock); + if (img_ir_privs == priv) + img_ir_privs = priv->next; + else + for (cur = img_ir_privs; cur; cur = cur->next) + if (cur->next == priv) { + cur->next = priv->next; + break; + } + img_ir_set_decoder(priv, NULL, 0); + spin_unlock(&img_ir_decoders_lock); +} + +#ifdef CONFIG_PM_SLEEP +/** + * img_ir_enable_wake() - Switch to wake mode. + * @priv: IR private data. + * + * Returns: non-zero if the IR can wake the system. + */ +static int img_ir_enable_wake(struct img_ir_priv *priv) +{ + struct img_ir_priv_hw *hw = &priv->hw; + int ret = 0; + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + if (hw->flags & IMG_IR_F_WAKE) { + /* interrupt only on a match */ + hw->suspend_irqen = img_ir_read(priv, IMG_IR_IRQ_ENABLE); + img_ir_write(priv, IMG_IR_IRQ_ENABLE, IMG_IR_IRQ_DATA_MATCH); + img_ir_write_filter(priv, &hw->wake_filter); + img_ir_write_timings_wake(priv, &hw->decoder->reg_timings); + hw->mode = IMG_IR_M_WAKE; + ret = 1; + } + spin_unlock_irqrestore(&priv->lock, flags); + return ret; +} + +/** + * img_ir_disable_wake() - Switch out of wake mode. + * @priv: IR private data + * + * Returns: 1 if the hardware should be allowed to wake from a sleep state. + * 0 otherwise. + */ +static int img_ir_disable_wake(struct img_ir_priv *priv) +{ + struct img_ir_priv_hw *hw = &priv->hw; + int ret = 0; + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + if (hw->flags & IMG_IR_F_WAKE) { + /* restore normal filtering */ + if (hw->flags & IMG_IR_F_FILTER) { + img_ir_write(priv, IMG_IR_IRQ_ENABLE, + (hw->suspend_irqen & IMG_IR_IRQ_EDGE) | + IMG_IR_IRQ_DATA_MATCH); + img_ir_write_filter(priv, &hw->filter); + } else { + img_ir_write(priv, IMG_IR_IRQ_ENABLE, + (hw->suspend_irqen & IMG_IR_IRQ_EDGE) | + IMG_IR_IRQ_DATA_VALID | + IMG_IR_IRQ_DATA2_VALID); + img_ir_write_filter(priv, NULL); + } + img_ir_write_timings_normal(priv, &hw->decoder->reg_timings); + hw->mode = IMG_IR_M_NORMAL; + ret = 1; + } + spin_unlock_irqrestore(&priv->lock, flags); + return ret; +} +#endif /* CONFIG_PM_SLEEP */ + +/* lock must be held */ +static void img_ir_begin_repeat(struct img_ir_priv *priv) +{ + struct img_ir_priv_hw *hw = &priv->hw; + if (hw->mode == IMG_IR_M_NORMAL) { + struct img_ir_decoder *dec = hw->decoder; + + /* switch to repeat timings */ + img_ir_write(priv, IMG_IR_CONTROL, 0); + hw->mode = IMG_IR_M_REPEATING; + img_ir_write_timings_normal(priv, &dec->reg_rtimings); + img_ir_write(priv, IMG_IR_CONTROL, dec->reg_ctrl); + } +} + +/* lock must be held */ +static void img_ir_end_repeat(struct img_ir_priv *priv) +{ + struct img_ir_priv_hw *hw = &priv->hw; + if (hw->mode == IMG_IR_M_REPEATING) { + struct img_ir_decoder *dec = hw->decoder; + + /* switch to normal timings */ + img_ir_write(priv, IMG_IR_CONTROL, 0); + hw->mode = IMG_IR_M_NORMAL; + img_ir_write_timings_normal(priv, &dec->reg_timings); + img_ir_write(priv, IMG_IR_CONTROL, dec->reg_ctrl); + } +} + +/* lock must be held */ +static void img_ir_handle_data(struct img_ir_priv *priv, u32 len, u64 raw) +{ + struct img_ir_priv_hw *hw = &priv->hw; + struct img_ir_decoder *dec = hw->decoder; + int scancode = IMG_IR_ERR_INVALID; + if (dec->scancode) + scancode = dec->scancode(len, raw, hw->enabled_protocols); + else if (len >= 32) + scancode = (u32)raw; + else if (len < 32) + scancode = (u32)raw & ((1 << len)-1); + dev_dbg(priv->dev, "data (%u bits) = %#llx\n", + len, (unsigned long long)raw); + if (scancode >= 0) { + dev_dbg(priv->dev, "decoded scan code %#x\n", scancode); + rc_keydown(hw->rdev, scancode, 0); + img_ir_end_repeat(priv); + } else if (scancode == IMG_IR_REPEATCODE) { + if (hw->mode == IMG_IR_M_REPEATING) { + dev_dbg(priv->dev, "decoded repeat code\n"); + rc_repeat(hw->rdev); + } else { + dev_dbg(priv->dev, "decoded unexpected repeat code, ignoring\n"); + } + } else { + return; + } + + + if (dec->repeat) { + unsigned long interval; + + img_ir_begin_repeat(priv); + + /* update timer, but allowing for 1/8th tolerance */ + interval = dec->repeat + (dec->repeat >> 3); + mod_timer(&hw->end_timer, + jiffies + msecs_to_jiffies(interval)); + } +} + +/* timer function to end waiting for repeat. */ +static void img_ir_end_timer(unsigned long arg) +{ + unsigned long flags; + struct img_ir_priv *priv = (struct img_ir_priv *)arg; + + spin_lock_irqsave(&priv->lock, flags); + img_ir_end_repeat(priv); + spin_unlock_irqrestore(&priv->lock, flags); +} + +/* Kernel interface */ + +static ssize_t img_ir_filter_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rc_dev *rdev = container_of(dev, struct rc_dev, dev); + struct img_ir_priv *priv = rdev->priv; + + return sprintf(buf, "%#x\n", priv->hw.sc_filter.data); +} +static ssize_t img_ir_filter_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rc_dev *rdev = container_of(dev, struct rc_dev, dev); + struct img_ir_priv *priv = rdev->priv; + int ret; + unsigned long val; + + ret = kstrtoul(buf, 0, &val); + if (ret < 0) + return ret; + priv->hw.sc_filter.data = val; + img_ir_update_filters(priv); + return count; +} +static DEVICE_ATTR(filter, S_IRUGO|S_IWUSR, + img_ir_filter_show, + img_ir_filter_store); + +static ssize_t img_ir_filter_mask_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rc_dev *rdev = container_of(dev, struct rc_dev, dev); + struct img_ir_priv *priv = rdev->priv; + + return sprintf(buf, "%#x\n", priv->hw.sc_filter.mask); +} +static ssize_t img_ir_filter_mask_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rc_dev *rdev = container_of(dev, struct rc_dev, dev); + struct img_ir_priv *priv = rdev->priv; + int ret; + unsigned long val; + + ret = kstrtoul(buf, 0, &val); + if (ret < 0) + return ret; + priv->hw.sc_filter.mask = val; + img_ir_update_filters(priv); + return count; +} +static DEVICE_ATTR(filter_mask, S_IRUGO|S_IWUSR, + img_ir_filter_mask_show, + img_ir_filter_mask_store); + +static ssize_t img_ir_wakeup_filter_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rc_dev *rdev = container_of(dev, struct rc_dev, dev); + struct img_ir_priv *priv = rdev->priv; + + return sprintf(buf, "%#x\n", priv->hw.sc_wake_filter.data); +} +static ssize_t img_ir_wakeup_filter_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rc_dev *rdev = container_of(dev, struct rc_dev, dev); + struct img_ir_priv *priv = rdev->priv; + int ret; + unsigned long val; + + ret = kstrtoul(buf, 0, &val); + if (ret < 0) + return ret; + priv->hw.sc_wake_filter.data = val; + img_ir_update_filters(priv); + return count; +} +static DEVICE_ATTR(wakeup_filter, S_IRUGO|S_IWUSR, + img_ir_wakeup_filter_show, + img_ir_wakeup_filter_store); + +static ssize_t img_ir_wakeup_filter_mask_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rc_dev *rdev = container_of(dev, struct rc_dev, dev); + struct img_ir_priv *priv = rdev->priv; + + return sprintf(buf, "%#x\n", priv->hw.sc_wake_filter.mask); +} +static ssize_t img_ir_wakeup_filter_mask_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rc_dev *rdev = container_of(dev, struct rc_dev, dev); + struct img_ir_priv *priv = rdev->priv; + int ret; + unsigned long val; + + ret = kstrtoul(buf, 0, &val); + if (ret < 0) + return ret; + priv->hw.sc_wake_filter.mask = val; + img_ir_update_filters(priv); + return count; +} +static DEVICE_ATTR(wakeup_filter_mask, S_IRUGO|S_IWUSR, + img_ir_wakeup_filter_mask_show, + img_ir_wakeup_filter_mask_store); + +static void img_ir_attr_create(struct device *dev) +{ + device_create_file(dev, &dev_attr_filter); + device_create_file(dev, &dev_attr_filter_mask); + device_create_file(dev, &dev_attr_wakeup_filter); + device_create_file(dev, &dev_attr_wakeup_filter_mask); +} + +static void img_ir_attr_remove(struct device *dev) +{ + device_remove_file(dev, &dev_attr_filter); + device_remove_file(dev, &dev_attr_filter_mask); + device_remove_file(dev, &dev_attr_wakeup_filter); + device_remove_file(dev, &dev_attr_wakeup_filter_mask); +} + +#ifdef CONFIG_COMMON_CLK +static void img_ir_change_frequency(struct img_ir_priv *priv, + struct clk_notifier_data *change) +{ + struct img_ir_priv_hw *hw = &priv->hw; + struct img_ir_decoder *dec; + unsigned long flags; + + dev_dbg(priv->dev, "clk changed %lu HZ -> %lu HZ\n", + change->old_rate, change->new_rate); + + spin_lock_irqsave(&priv->lock, flags); + dec = hw->decoder; + hw->clk_hz = change->new_rate; + /* refresh current timings */ + if (hw->decoder) { + img_ir_check_timings(priv); + switch (hw->mode) { + case IMG_IR_M_NORMAL: + img_ir_write_timings_normal(priv, &dec->reg_timings); + break; + case IMG_IR_M_REPEATING: + img_ir_write_timings_normal(priv, &dec->reg_rtimings); + break; +#ifdef CONFIG_PM_SLEEP + case IMG_IR_M_WAKE: + img_ir_write_timings_wake(priv, &dec->reg_timings); + break; +#endif + } + } + spin_unlock_irqrestore(&priv->lock, flags); +} + +static int img_ir_clk_notify(struct notifier_block *self, unsigned long action, + void *data) +{ + struct img_ir_priv *priv = container_of(self, struct img_ir_priv, + hw.clk_nb); + switch (action) { + case POST_RATE_CHANGE: + img_ir_change_frequency(priv, data); + break; + default: + break; + } + return NOTIFY_OK; +} +#endif /* CONFIG_COMMON_CLK */ + +void img_ir_isr_hw(struct img_ir_priv *priv, u32 irq_status) +{ + struct img_ir_priv_hw *hw = &priv->hw; + u32 ir_status, len, lw, up; + unsigned int ct; + + /* use the current decoder */ + if (!hw->decoder) + return; + + ir_status = img_ir_read(priv, IMG_IR_STATUS); + if (!(ir_status & (IMG_IR_RXDVAL | IMG_IR_RXDVALD2))) + return; + ir_status &= ~(IMG_IR_RXDVAL | IMG_IR_RXDVALD2); + img_ir_write(priv, IMG_IR_STATUS, ir_status); + + len = (ir_status & IMG_IR_RXDLEN) >> IMG_IR_RXDLEN_SHIFT; + /* some versions report wrong length for certain code types */ + ct = hw->decoder->control.code_type; + if (hw->ct_quirks[ct] & IMG_IR_QUIRK_CODE_LEN_INCR) + ++len; + + lw = img_ir_read(priv, IMG_IR_DATA_LW); + up = img_ir_read(priv, IMG_IR_DATA_UP); + img_ir_handle_data(priv, len, (u64)up << 32 | lw); +} + +void img_ir_setup_hw(struct img_ir_priv *priv) +{ + struct img_ir_decoder *dec; + + if (!priv->hw.rdev) + return; + + spin_lock(&img_ir_decoders_lock); + /* Use the first available decoder (or disable stuff if NULL) */ + for (dec = img_ir_decoders; dec; dec = dec->next) { + if (img_ir_decoder_compatible(priv, dec)) { + img_ir_set_protocol(priv, dec->type); + img_ir_set_decoder(priv, dec, 0); + goto unlock; + } + } + img_ir_set_decoder(priv, NULL, 0); +unlock: + spin_unlock(&img_ir_decoders_lock); +} + +/** + * img_ir_probe_hw_caps() - Probe capabilities of the hardware. + * @priv: IR private data. + */ +static void img_ir_probe_hw_caps(struct img_ir_priv *priv) +{ + struct img_ir_priv_hw *hw = &priv->hw; + /* + * When a version of the block becomes available without these quirks, + * they'll have to depend on the core revision. + */ + hw->ct_quirks[IMG_IR_CODETYPE_PULSELEN] + |= IMG_IR_QUIRK_CODE_LEN_INCR; + hw->ct_quirks[IMG_IR_CODETYPE_BIPHASE] + |= IMG_IR_QUIRK_CODE_BROKEN; + hw->ct_quirks[IMG_IR_CODETYPE_2BITPULSEPOS] + |= IMG_IR_QUIRK_CODE_BROKEN; +} + +int img_ir_probe_hw(struct img_ir_priv *priv) +{ + struct img_ir_priv_hw *hw = &priv->hw; + struct rc_dev *rdev; + int error; + + /* Probe hardware capabilities */ + img_ir_probe_hw_caps(priv); + + /* Set up the end timer */ + init_timer(&hw->end_timer); + hw->end_timer.function = img_ir_end_timer; + hw->end_timer.data = (unsigned long)priv; + + /* Register a clock notifier */ + if (!IS_ERR(priv->clk)) { + hw->clk_hz = clk_get_rate(priv->clk); +#ifdef CONFIG_COMMON_CLK + hw->clk_nb.notifier_call = img_ir_clk_notify; + error = clk_notifier_register(priv->clk, &hw->clk_nb); + if (error) + dev_warn(priv->dev, + "failed to register clock notifier\n"); +#endif + } else { + hw->clk_hz = 32768; + } + + /* Allocate hardware decoder */ + hw->rdev = rdev = rc_allocate_device(); + if (!rdev) { + dev_err(priv->dev, "cannot allocate input device\n"); + error = -ENOMEM; + goto err_alloc_rc; + } + rdev->priv = priv; + rdev->map_name = RC_MAP_EMPTY; + rdev->input_name = "IMG Infrared Decoder"; + /* img_ir_register_device sets rdev->allowed_protos. */ + img_ir_register_device(priv); + + /* Register hardware decoder */ + error = rc_register_device(rdev); + if (error) { + dev_err(priv->dev, "failed to register IR input device\n"); + goto err_register_rc; + } + + /* + * Set this after rc_register_device as no protocols have been + * registered yet. + */ + rdev->change_protocol = img_ir_change_protocol; + + device_init_wakeup(priv->dev, 1); + + /* Create custom sysfs attributes */ + img_ir_attr_create(&rdev->dev); + + return 0; + +err_register_rc: + img_ir_unregister_device(priv); + hw->rdev = NULL; + rc_free_device(rdev); +err_alloc_rc: +#ifdef CONFIG_COMMON_CLK + if (!IS_ERR(priv->clk)) + clk_notifier_unregister(priv->clk, &hw->clk_nb); +#endif + return error; +} + +void img_ir_remove_hw(struct img_ir_priv *priv) +{ + struct img_ir_priv_hw *hw = &priv->hw; + struct rc_dev *rdev = hw->rdev; + if (!rdev) + return; + img_ir_attr_remove(&rdev->dev); + img_ir_unregister_device(priv); + hw->rdev = NULL; + rc_unregister_device(rdev); +#ifdef CONFIG_COMMON_CLK + if (!IS_ERR(priv->clk)) + clk_notifier_unregister(priv->clk, &hw->clk_nb); +#endif +} + +#ifdef CONFIG_PM_SLEEP +int img_ir_suspend(struct device *dev) +{ + struct img_ir_priv *priv = dev_get_drvdata(dev); + + if (device_may_wakeup(dev) && img_ir_enable_wake(priv)) + enable_irq_wake(priv->irq); + return 0; +} + +int img_ir_resume(struct device *dev) +{ + struct img_ir_priv *priv = dev_get_drvdata(dev); + + if (device_may_wakeup(dev) && img_ir_disable_wake(priv)) + disable_irq_wake(priv->irq); + return 0; +} +#endif /* CONFIG_PM_SLEEP */ diff --git a/drivers/media/rc/img-ir/img-ir-hw.h b/drivers/media/rc/img-ir/img-ir-hw.h new file mode 100644 index 000000000000..0fb8e48fda3a --- /dev/null +++ b/drivers/media/rc/img-ir/img-ir-hw.h @@ -0,0 +1,284 @@ +/* + * ImgTec IR Hardware Decoder found in PowerDown Controller. + * + * Copyright 2010-2013 Imagination Technologies Ltd. + */ + +#ifndef _IMG_IR_HW_H_ +#define _IMG_IR_HW_H_ + +#include <linux/kernel.h> +#include <media/rc-core.h> + +/* constants */ + +#define IMG_IR_CODETYPE_PULSELEN 0x0 /* Sony */ +#define IMG_IR_CODETYPE_PULSEDIST 0x1 /* NEC, Toshiba, Micom, Sharp */ +#define IMG_IR_CODETYPE_BIPHASE 0x2 /* RC-5/6 */ +#define IMG_IR_CODETYPE_2BITPULSEPOS 0x3 /* RC-MM */ + + +/* Timing information */ + +/** + * struct img_ir_control - Decoder control settings + * @decoden: Primary decoder enable + * @code_type: Decode type (see IMG_IR_CODETYPE_*) + * @hdrtog: Detect header toggle symbol after leader symbol + * @ldrdec: Don't discard leader if maximum width reached + * @decodinpol: Decoder input polarity (1=active high) + * @bitorien: Bit orientation (1=MSB first) + * @d1validsel: Decoder 2 takes over if it detects valid data + * @bitinv: Bit inversion switch (1=don't invert) + * @decodend2: Secondary decoder enable (no leader symbol) + * @bitoriend2: Bit orientation (1=MSB first) + * @bitinvd2: Secondary decoder bit inversion switch (1=don't invert) + */ +struct img_ir_control { + unsigned decoden:1; + unsigned code_type:2; + unsigned hdrtog:1; + unsigned ldrdec:1; + unsigned decodinpol:1; + unsigned bitorien:1; + unsigned d1validsel:1; + unsigned bitinv:1; + unsigned decodend2:1; + unsigned bitoriend2:1; + unsigned bitinvd2:1; +}; + +/** + * struct img_ir_timing_range - range of timing values + * @min: Minimum timing value + * @max: Maximum timing value (if < @min, this will be set to @min during + * preprocessing step, so it is normally not explicitly initialised + * and is taken care of by the tolerance) + */ +struct img_ir_timing_range { + u16 min; + u16 max; +}; + +/** + * struct img_ir_symbol_timing - timing data for a symbol + * @pulse: Timing range for the length of the pulse in this symbol + * @space: Timing range for the length of the space in this symbol + */ +struct img_ir_symbol_timing { + struct img_ir_timing_range pulse; + struct img_ir_timing_range space; +}; + +/** + * struct img_ir_free_timing - timing data for free time symbol + * @minlen: Minimum number of bits of data + * @maxlen: Maximum number of bits of data + * @ft_min: Minimum free time after message + */ +struct img_ir_free_timing { + /* measured in bits */ + u8 minlen; + u8 maxlen; + u16 ft_min; +}; + +/** + * struct img_ir_timings - Timing values. + * @ldr: Leader symbol timing data + * @s00: Zero symbol timing data for primary decoder + * @s01: One symbol timing data for primary decoder + * @s10: Zero symbol timing data for secondary (no leader symbol) decoder + * @s11: One symbol timing data for secondary (no leader symbol) decoder + * @ft: Free time symbol timing data + */ +struct img_ir_timings { + struct img_ir_symbol_timing ldr, s00, s01, s10, s11; + struct img_ir_free_timing ft; +}; + +/** + * struct img_ir_sc_filter - Filter scan codes. + * @data: Data to match. + * @mask: Mask of bits to compare. + */ +struct img_ir_sc_filter { + unsigned int data; + unsigned int mask; +}; + +/** + * struct img_ir_filter - Filter IR events. + * @data: Data to match. + * @mask: Mask of bits to compare. + * @minlen: Additional minimum number of bits. + * @maxlen: Additional maximum number of bits. + */ +struct img_ir_filter { + u64 data; + u64 mask; + u8 minlen; + u8 maxlen; +}; + +/** + * struct img_ir_timing_regvals - Calculated timing register values. + * @ldr: Leader symbol timing register value + * @s00: Zero symbol timing register value for primary decoder + * @s01: One symbol timing register value for primary decoder + * @s10: Zero symbol timing register value for secondary decoder + * @s11: One symbol timing register value for secondary decoder + * @ft: Free time symbol timing register value + */ +struct img_ir_timing_regvals { + u32 ldr, s00, s01, s10, s11, ft; +}; + +#define IMG_IR_REPEATCODE (-1) /* repeat the previous code */ +#define IMG_IR_ERR_INVALID (-2) /* not a valid code */ + +/** + * struct img_ir_decoder - Decoder settings for an IR protocol. + * @type: Protocol types bitmap. + * @unit: Unit of timings in nanoseconds (default 1 us). + * @timings: Primary timings + * @rtimings: Additional override timings while waiting for repeats. + * @repeat: Maximum repeat interval (always in milliseconds). + * @control: Control flags. + * + * @scancode: Pointer to function to convert the IR data into a + * scancode (it must be safe to execute in interrupt + * context). + * Returns IMG_IR_REPEATCODE to repeat previous code. + * Returns IMG_IR_ERR_* on error. + * @filter: Pointer to function to convert scancode filter to raw + * hardware filter. The minlen and maxlen fields will have + * been initialised to the maximum range. + * + * @reg_ctrl: Processed control register value. + * @clk_hz: Assumed clock rate in Hz for processed timings. + * @reg_timings: Processed primary timings. + * @reg_rtimings: Processed repeat timings. + * @next: Next IR decoder (to form a linked list). + */ +struct img_ir_decoder { + /* core description */ + u64 type; + unsigned int unit; + struct img_ir_timings timings; + struct img_ir_timings rtimings; + unsigned int repeat; + struct img_ir_control control; + + /* scancode logic */ + int (*scancode)(int len, u64 raw, u64 protocols); + int (*filter)(const struct img_ir_sc_filter *in, + struct img_ir_filter *out, u64 protocols); + + /* for internal use only */ + u32 reg_ctrl; + unsigned long clk_hz; + struct img_ir_timing_regvals reg_timings; + struct img_ir_timing_regvals reg_rtimings; + struct img_ir_decoder *next; +}; + +int img_ir_register_decoder(struct img_ir_decoder *dec); +void img_ir_unregister_decoder(struct img_ir_decoder *dec); + +struct img_ir_priv; + +#ifdef CONFIG_IR_IMG_HW + +enum img_ir_mode { + IMG_IR_M_NORMAL, + IMG_IR_M_REPEATING, +#ifdef CONFIG_PM_SLEEP + IMG_IR_M_WAKE, +#endif +}; + +/** + * struct img_ir_priv_hw - Private driver data for hardware decoder. + * @ct_quirks: Quirk bits for each code type. + * @rdev: Remote control device + * @clk_nb: Notifier block for clock notify events. + * @end_timer: Timer until repeat timeout. + * @decoder: Current decoder settings. + * @enabled_protocols: Currently enabled protocols. + * @clk_hz: Current clock rate in Hz. + * @flags: IMG_IR_F_*. + * @filter: HW filter for normal events (derived from sc_filter). + * @wake_filter: HW filter for wake event (derived from sc_wake_filter). + * @sc_filter: Current scancode filter. + * @sc_wake_filter: Current scancode filter for wake events. + * @mode: Current decode mode. + * @suspend_irqen: Saved IRQ enable mask over suspend. + */ +struct img_ir_priv_hw { + unsigned int ct_quirks[4]; + struct rc_dev *rdev; + struct notifier_block clk_nb; + struct timer_list end_timer; + struct img_ir_decoder *decoder; + u64 enabled_protocols; + unsigned long clk_hz; + unsigned int flags; + struct img_ir_filter filter; + struct img_ir_filter wake_filter; + + /* filters in terms of scancodes */ + struct img_ir_sc_filter sc_filter; + struct img_ir_sc_filter sc_wake_filter; + + enum img_ir_mode mode; + u32 suspend_irqen; +}; + +static inline bool img_ir_hw_enabled(struct img_ir_priv_hw *hw) +{ + return hw->rdev; +}; + +void img_ir_isr_hw(struct img_ir_priv *priv, u32 irq_status); +void img_ir_setup_hw(struct img_ir_priv *priv); +int img_ir_probe_hw(struct img_ir_priv *priv); +void img_ir_remove_hw(struct img_ir_priv *priv); + +#ifdef CONFIG_PM_SLEEP +int img_ir_suspend(struct device *dev); +int img_ir_resume(struct device *dev); +#else +#define img_ir_suspend NULL +#define img_ir_resume NULL +#endif + +#else + +struct img_ir_priv_hw { +}; + +static inline bool img_ir_hw_enabled(struct img_ir_priv_hw *hw) +{ + return false; +}; +static inline void img_ir_isr_hw(struct img_ir_priv *priv, u32 irq_status) +{ +} +static inline void img_ir_setup_hw(struct img_ir_priv *priv) +{ +} +static inline int img_ir_probe_hw(struct img_ir_priv *priv) +{ + return -ENODEV; +} +static inline void img_ir_remove_hw(struct img_ir_priv *priv) +{ +} + +#define img_ir_suspend NULL +#define img_ir_resume NULL + +#endif /* CONFIG_IR_IMG_HW */ + +#endif /* _IMG_IR_HW_H_ */ -- 1.8.1.2 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH 04/11] media: rc: img-ir: add hardware decoder driver 2013-12-13 15:12 ` [PATCH 04/11] media: rc: img-ir: add hardware decoder driver James Hogan @ 2013-12-22 13:40 ` Mauro Carvalho Chehab 2013-12-23 13:17 ` James Hogan 0 siblings, 1 reply; 21+ messages in thread From: Mauro Carvalho Chehab @ 2013-12-22 13:40 UTC (permalink / raw) To: James Hogan; +Cc: linux-media Hi James, Em Fri, 13 Dec 2013 15:12:52 +0000 James Hogan <james.hogan@imgtec.com> escreveu: > Add remote control input driver for the ImgTec Infrared block hardware > decoder, which is set up with timings for a specific protocol and > supports mask/value filtering and wake events. > > The hardware decoder timing values, raw data to scan code conversion > function and scan code filter to raw data filter conversion function are > provided as separate modules for each protocol which this part of the > driver can use. The scan code filter value and mask (and the same again > for wake from sleep) are specified via sysfs files in > /sys/class/rc/rcX/. We should discuss a little more about those new sysfs controls. There are two separate questions here: 1) are those new sysfs controls really device specific? If not, then it makes sense to add support for it at rc-core. I suspect that a wakeup scancode is something that should be part of the RC core, as other devices may have it too. Also, the RC core currently supports a scancode mask. Not sure if it is the same concept as the one used on your hardware. Could you please explain it better? 2) Where those new sysfs nodes will be documented. With regards to (2), we currently lack a chapter at the Linux Media Documentation/DocBook or at Documentation/ABI/ for the existing sysfs interface, but let's not increase the gap, please. I'll see if I can find some time to write such documentation, probably at Documentation/ABI/testing. So, if we come to the conclusion that those interfaces should be part of the rc core, we'll need them to be documented at Documentation/ABI/testing too. Otherwise, if we decide to add some of those as private API, please add a README for this device, under Documentation/remote-controllers. The patch itself (and patches 1-3) look OK to me. I'll be reviewing the other patches on separate emails. > > 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/img-ir-hw.c | 1277 +++++++++++++++++++++++++++++++++++ > drivers/media/rc/img-ir/img-ir-hw.h | 284 ++++++++ > 2 files changed, 1561 insertions(+) > create mode 100644 drivers/media/rc/img-ir/img-ir-hw.c > create mode 100644 drivers/media/rc/img-ir/img-ir-hw.h > > diff --git a/drivers/media/rc/img-ir/img-ir-hw.c b/drivers/media/rc/img-ir/img-ir-hw.c > new file mode 100644 > index 000000000000..917d9a076a8c > --- /dev/null > +++ b/drivers/media/rc/img-ir/img-ir-hw.c > @@ -0,0 +1,1277 @@ > +/* > + * ImgTec IR Hardware Decoder found in PowerDown Controller. > + * > + * Copyright 2010-2013 Imagination Technologies Ltd. > + * > + * This ties into the input subsystem using the RC-core. Protocol support is > + * provided in separate modules which provide the parameters and scancode > + * translation functions to set up the hardware decoder and interpret the > + * resulting input. > + */ > + > +#include <linux/clk.h> > +#include <linux/interrupt.h> > +#include <linux/spinlock.h> > +#include <linux/timer.h> > +#include <media/rc-core.h> > +#include "img-ir.h" > + > +/* Decoder list */ > +static DEFINE_SPINLOCK(img_ir_decoders_lock); > +static struct img_ir_decoder *img_ir_decoders; > +static struct img_ir_priv *img_ir_privs; > + > +#define IMG_IR_F_FILTER 0x00000001 /* enable filtering */ > +#define IMG_IR_F_WAKE 0x00000002 /* enable waking */ > + > +/* code type quirks */ > + > +#define IMG_IR_QUIRK_CODE_BROKEN 0x1 /* Decode is broken */ > +#define IMG_IR_QUIRK_CODE_LEN_INCR 0x2 /* Bit length needs increment */ > + > +/* functions for preprocessing timings, ensuring max is set */ > + > +static void img_ir_timing_preprocess(struct img_ir_timing_range *range, > + unsigned int unit) > +{ > + if (range->max < range->min) > + range->max = range->min; > + if (unit) { > + /* multiply by unit and convert to microseconds */ > + range->min = (range->min*unit)/1000; > + range->max = (range->max*unit + 999)/1000; /* round up */ > + } > +} > + > +static void img_ir_symbol_timing_preprocess(struct img_ir_symbol_timing *timing, > + unsigned int unit) > +{ > + img_ir_timing_preprocess(&timing->pulse, unit); > + img_ir_timing_preprocess(&timing->space, unit); > +} > + > +static void img_ir_timings_preprocess(struct img_ir_timings *timings, > + unsigned int unit) > +{ > + img_ir_symbol_timing_preprocess(&timings->ldr, unit); > + img_ir_symbol_timing_preprocess(&timings->s00, unit); > + img_ir_symbol_timing_preprocess(&timings->s01, unit); > + img_ir_symbol_timing_preprocess(&timings->s10, unit); > + img_ir_symbol_timing_preprocess(&timings->s11, unit); > + /* default s10 and s11 to s00 and s01 if no leader */ > + if (unit) > + /* multiply by unit and convert to microseconds (round up) */ > + timings->ft.ft_min = (timings->ft.ft_min*unit + 999)/1000; > +} > + > +/* functions for filling empty fields with defaults */ > + > +static void img_ir_timing_defaults(struct img_ir_timing_range *range, > + struct img_ir_timing_range *defaults) > +{ > + if (!range->min) > + range->min = defaults->min; > + if (!range->max) > + range->max = defaults->max; > +} > + > +static void img_ir_symbol_timing_defaults(struct img_ir_symbol_timing *timing, > + struct img_ir_symbol_timing *defaults) > +{ > + img_ir_timing_defaults(&timing->pulse, &defaults->pulse); > + img_ir_timing_defaults(&timing->space, &defaults->space); > +} > + > +static void img_ir_timings_defaults(struct img_ir_timings *timings, > + struct img_ir_timings *defaults) > +{ > + img_ir_symbol_timing_defaults(&timings->ldr, &defaults->ldr); > + img_ir_symbol_timing_defaults(&timings->s00, &defaults->s00); > + img_ir_symbol_timing_defaults(&timings->s01, &defaults->s01); > + img_ir_symbol_timing_defaults(&timings->s10, &defaults->s10); > + img_ir_symbol_timing_defaults(&timings->s11, &defaults->s11); > + if (!timings->ft.ft_min) > + timings->ft.ft_min = defaults->ft.ft_min; > +} > + > +/* functions for converting timings to register values */ > + > +/** > + * img_ir_control() - Convert control struct to control register value. > + * @control: Control data > + * > + * Returns: The control register value equivalent of @control. > + */ > +static u32 img_ir_control(struct img_ir_control *control) > +{ > + u32 ctrl = control->code_type << IMG_IR_CODETYPE_SHIFT; > + if (control->decoden) > + ctrl |= IMG_IR_DECODEN; > + if (control->hdrtog) > + ctrl |= IMG_IR_HDRTOG; > + if (control->ldrdec) > + ctrl |= IMG_IR_LDRDEC; > + if (control->decodinpol) > + ctrl |= IMG_IR_DECODINPOL; > + if (control->bitorien) > + ctrl |= IMG_IR_BITORIEN; > + if (control->d1validsel) > + ctrl |= IMG_IR_D1VALIDSEL; > + if (control->bitinv) > + ctrl |= IMG_IR_BITINV; > + if (control->decodend2) > + ctrl |= IMG_IR_DECODEND2; > + if (control->bitoriend2) > + ctrl |= IMG_IR_BITORIEND2; > + if (control->bitinvd2) > + ctrl |= IMG_IR_BITINVD2; > + return ctrl; > +} > + > +/** > + * img_ir_timing_range_convert() - Convert microsecond range. > + * @out: Output timing range in clock cycles with a shift. > + * @in: Input timing range in microseconds. > + * @tolerance: Tolerance as a fraction of 128 (roughly percent). > + * @clock_hz: IR clock rate in Hz. > + * @shift: Shift of output units. > + * > + * Converts min and max from microseconds to IR clock cycles, applies a > + * tolerance, and shifts for the register, rounding in the right direction. > + * Note that in and out can safely be the same object. > + */ > +static void img_ir_timing_range_convert(struct img_ir_timing_range *out, > + const struct img_ir_timing_range *in, > + unsigned int tolerance, > + unsigned long clock_hz, > + unsigned int shift) > +{ > + unsigned int min = in->min; > + unsigned int max = in->max; > + /* add a tolerance */ > + min = min - (min*tolerance >> 7); > + max = max + (max*tolerance >> 7); > + /* convert from microseconds into clock cycles */ > + min = min*clock_hz / 1000000; > + max = (max*clock_hz + 999999) / 1000000; /* round up */ > + /* apply shift and copy to output */ > + out->min = min >> shift; > + out->max = (max + ((1 << shift) - 1)) >> shift; /* round up */ > +} > + > +/** > + * img_ir_symbol_timing() - Convert symbol timing struct to register value. > + * @timing: Symbol timing data > + * @tolerance: Timing tolerance where 0-128 represents 0-100% > + * @clock_hz: Frequency of source clock in Hz > + * @pd_shift: Shift to apply to symbol period > + * @w_shift: Shift to apply to symbol width > + * > + * Returns: Symbol timing register value based on arguments. > + */ > +static u32 img_ir_symbol_timing(const struct img_ir_symbol_timing *timing, > + unsigned int tolerance, > + unsigned long clock_hz, > + unsigned int pd_shift, > + unsigned int w_shift) > +{ > + struct img_ir_timing_range hw_pulse, hw_period; > + /* we calculate period in hw_period, then convert in place */ > + hw_period.min = timing->pulse.min + timing->space.min; > + hw_period.max = timing->pulse.max + timing->space.max; > + img_ir_timing_range_convert(&hw_period, &hw_period, > + tolerance, clock_hz, pd_shift); > + img_ir_timing_range_convert(&hw_pulse, &timing->pulse, > + tolerance, clock_hz, w_shift); > + /* construct register value */ > + return (hw_period.max << IMG_IR_PD_MAX_SHIFT) | > + (hw_period.min << IMG_IR_PD_MIN_SHIFT) | > + (hw_pulse.max << IMG_IR_W_MAX_SHIFT) | > + (hw_pulse.min << IMG_IR_W_MIN_SHIFT); > +} > + > +/** > + * img_ir_free_timing() - Convert free time timing struct to register value. > + * @timing: Free symbol timing data > + * @clock_hz: Source clock frequency in Hz > + * > + * Returns: Free symbol timing register value. > + */ > +static u32 img_ir_free_timing(const struct img_ir_free_timing *timing, > + unsigned long clock_hz) > +{ > + unsigned int minlen, maxlen, ft_min; > + /* minlen is only 5 bits, and round minlen to multiple of 2 */ > + if (timing->minlen < 30) > + minlen = timing->minlen & -2; > + else > + minlen = 30; > + /* maxlen has maximum value of 48, and round maxlen to multiple of 2 */ > + if (timing->maxlen < 48) > + maxlen = (timing->maxlen + 1) & -2; > + else > + maxlen = 48; > + /* convert and shift ft_min, rounding upwards */ > + ft_min = (timing->ft_min*clock_hz + 999999) / 1000000; > + ft_min = (ft_min + 7) >> 3; > + /* construct register value */ > + return (timing->maxlen << IMG_IR_MAXLEN_SHIFT) | > + (timing->minlen << IMG_IR_MINLEN_SHIFT) | > + (ft_min << IMG_IR_FT_MIN_SHIFT); > +} > + > +/** > + * img_ir_free_timing_dynamic() - Update free time register value. > + * @st_ft: Static free time register value from img_ir_free_timing. > + * @filter: Current filter which may additionally restrict min/max len. > + * > + * Returns: Updated free time register value based on the current filter. > + */ > +static u32 img_ir_free_timing_dynamic(u32 st_ft, struct img_ir_filter *filter) > +{ > + unsigned int minlen, maxlen, newminlen, newmaxlen; > + > + /* round minlen, maxlen to multiple of 2 */ > + newminlen = filter->minlen & -2; > + newmaxlen = (filter->maxlen + 1) & -2; > + /* extract min/max len from register */ > + minlen = (st_ft & IMG_IR_MINLEN) >> IMG_IR_MINLEN_SHIFT; > + maxlen = (st_ft & IMG_IR_MAXLEN) >> IMG_IR_MAXLEN_SHIFT; > + /* if the new values are more restrictive, update the register value */ > + if (newminlen > minlen) { > + st_ft &= ~IMG_IR_MINLEN; > + st_ft |= newminlen << IMG_IR_MINLEN_SHIFT; > + } > + if (newmaxlen < maxlen) { > + st_ft &= ~IMG_IR_MAXLEN; > + st_ft |= newmaxlen << IMG_IR_MAXLEN_SHIFT; > + } > + return st_ft; > +} > + > +/** > + * img_ir_timings_convert() - Convert timings to register values > + * @regs: Output timing register values > + * @timings: Input timing data > + * @tolerance: Timing tolerance where 0-128 represents 0-100% > + * @clock_hz: Source clock frequency in Hz > + */ > +static void img_ir_timings_convert(struct img_ir_timing_regvals *regs, > + const struct img_ir_timings *timings, > + unsigned int tolerance, > + unsigned int clock_hz) > +{ > + /* leader symbol timings are divided by 16 */ > + regs->ldr = img_ir_symbol_timing(&timings->ldr, tolerance, clock_hz, > + 4, 4); > + /* other symbol timings, pd fields only are divided by 2 */ > + regs->s00 = img_ir_symbol_timing(&timings->s00, tolerance, clock_hz, > + 1, 0); > + regs->s01 = img_ir_symbol_timing(&timings->s01, tolerance, clock_hz, > + 1, 0); > + regs->s10 = img_ir_symbol_timing(&timings->s10, tolerance, clock_hz, > + 1, 0); > + regs->s11 = img_ir_symbol_timing(&timings->s11, tolerance, clock_hz, > + 1, 0); > + regs->ft = img_ir_free_timing(&timings->ft, clock_hz); > +} > + > +/** > + * img_ir_decoder_preprocess() - Preprocess timings in decoder. > + * @decoder: Decoder to be preprocessed. > + * > + * Ensures that the symbol timing ranges are valid with respect to ordering, and > + * does some fixed conversion on them. > + */ > +static void img_ir_decoder_preprocess(struct img_ir_decoder *decoder) > +{ > + /* fill in implicit fields */ > + img_ir_timings_preprocess(&decoder->timings, decoder->unit); > + > + /* do the same for repeat timings if applicable */ > + if (decoder->repeat) { > + img_ir_timings_preprocess(&decoder->rtimings, decoder->unit); > + img_ir_timings_defaults(&decoder->rtimings, &decoder->timings); > + } > + > + /* calculate control value */ > + decoder->reg_ctrl = img_ir_control(&decoder->control); > +} > + > +/** > + * img_ir_decoder_convert() - Generate internal timings in decoder. > + * @decoder: Decoder to be converted to internal timings. > + * @tolerance: Tolerance as percent. > + * @clock_hz: IR clock rate in Hz. > + * > + * Fills out the repeat timings and timing register values for a specific > + * tolerance and clock rate. > + */ > +static void img_ir_decoder_convert(struct img_ir_decoder *decoder, > + unsigned int tolerance, > + unsigned int clock_hz) > +{ > + tolerance = tolerance * 128 / 100; > + > + /* record clock rate in case timings need recalculating */ > + decoder->clk_hz = clock_hz; > + > + /* fill in implicit fields and calculate register values */ > + img_ir_timings_convert(&decoder->reg_timings, &decoder->timings, > + tolerance, clock_hz); > + > + /* do the same for repeat timings if applicable */ > + if (decoder->repeat) > + img_ir_timings_convert(&decoder->reg_rtimings, > + &decoder->rtimings, tolerance, clock_hz); > +} > + > +/** > + * img_ir_decoder_check_timings() - Check if decoder timings need updating. > + * @priv: IR private data. > + */ > +static void img_ir_check_timings(struct img_ir_priv *priv) > +{ > + struct img_ir_priv_hw *hw = &priv->hw; > + struct img_ir_decoder *dec = hw->decoder; > + if (dec->clk_hz != hw->clk_hz) { > + dev_dbg(priv->dev, "converting tolerance=%d%%, clk=%lu\n", > + 10, hw->clk_hz); > + img_ir_decoder_convert(dec, 10, hw->clk_hz); > + } > +} > + > +/** > + * img_ir_write_timings() - Write timings to the hardware now > + * @priv: IR private data > + * @regs: Timing register values to write > + * @filter: Current filter data or NULL > + * > + * Write timing register values @regs to the hardware, taking into account the > + * current filter pointed to by @filter which may impose restrictions on the > + * length of the expected data. > + */ > +static void img_ir_write_timings(struct img_ir_priv *priv, > + struct img_ir_timing_regvals *regs, > + struct img_ir_filter *filter) > +{ > + /* filter may be more restrictive to minlen, maxlen */ > + u32 ft = regs->ft; > + if (filter) > + ft = img_ir_free_timing_dynamic(regs->ft, filter); > + /* write to registers */ > + img_ir_write(priv, IMG_IR_LEAD_SYMB_TIMING, regs->ldr); > + img_ir_write(priv, IMG_IR_S00_SYMB_TIMING, regs->s00); > + img_ir_write(priv, IMG_IR_S01_SYMB_TIMING, regs->s01); > + img_ir_write(priv, IMG_IR_S10_SYMB_TIMING, regs->s10); > + img_ir_write(priv, IMG_IR_S11_SYMB_TIMING, regs->s11); > + img_ir_write(priv, IMG_IR_FREE_SYMB_TIMING, ft); > + dev_dbg(priv->dev, "timings: ldr=%#x, s=[%#x, %#x, %#x, %#x], ft=%#x\n", > + regs->ldr, regs->s00, regs->s01, regs->s10, regs->s11, ft); > +} > + > +/** > + * img_ir_write_timings_normal() - Write normal timings to the hardware now > + * @priv: IR private data > + * @regs: Normal timing register values to write > + * > + * Write the normal (non-wake) timing register values @regs to the hardware, > + * taking into account the current filter. > + */ > +static void img_ir_write_timings_normal(struct img_ir_priv *priv, > + struct img_ir_timing_regvals *regs) > +{ > + struct img_ir_priv_hw *hw = &priv->hw; > + struct img_ir_filter *filter = NULL; > + if (hw->flags & IMG_IR_F_FILTER) > + filter = &hw->filter; > + img_ir_write_timings(priv, regs, filter); > +} > + > +#ifdef CONFIG_PM_SLEEP > +/** > + * img_ir_write_timings_wake() - Write wake timings to the hardware now > + * @priv: IR private data > + * @regs: Wake timing register values to write > + * > + * Write the wake timing register values @regs to the hardware, taking into > + * account the current wake filter. > + */ > +static void img_ir_write_timings_wake(struct img_ir_priv *priv, > + struct img_ir_timing_regvals *regs) > +{ > + struct img_ir_priv_hw *hw = &priv->hw; > + struct img_ir_filter *filter = NULL; > + if (hw->flags & IMG_IR_F_WAKE) > + filter = &hw->wake_filter; > + img_ir_write_timings(priv, regs, filter); > +} > +#endif > + > +static void img_ir_write_filter(struct img_ir_priv *priv, > + struct img_ir_filter *filter) > +{ > + if (filter) { > + dev_dbg(priv->dev, "IR filter=%016llx & %016llx\n", > + (unsigned long long)filter->data, > + (unsigned long long)filter->mask); > + img_ir_write(priv, IMG_IR_IRQ_MSG_DATA_LW, (u32)filter->data); > + img_ir_write(priv, IMG_IR_IRQ_MSG_DATA_UP, (u32)(filter->data > + >> 32)); > + img_ir_write(priv, IMG_IR_IRQ_MSG_MASK_LW, (u32)filter->mask); > + img_ir_write(priv, IMG_IR_IRQ_MSG_MASK_UP, (u32)(filter->mask > + >> 32)); > + } else { > + dev_dbg(priv->dev, "IR clearing filter\n"); > + img_ir_write(priv, IMG_IR_IRQ_MSG_MASK_LW, 0); > + img_ir_write(priv, IMG_IR_IRQ_MSG_MASK_UP, 0); > + } > +} > + > +/* caller must have lock */ > +static void _img_ir_set_filter(struct img_ir_priv *priv, > + struct img_ir_filter *filter) > +{ > + struct img_ir_priv_hw *hw = &priv->hw; > + u32 irq_en, irq_on; > + > + irq_en = img_ir_read(priv, IMG_IR_IRQ_ENABLE); > + if (filter) { > + /* Only use the match interrupt */ > + hw->filter = *filter; > + hw->flags |= IMG_IR_F_FILTER; > + irq_on = IMG_IR_IRQ_DATA_MATCH; > + irq_en &= ~(IMG_IR_IRQ_DATA_VALID | IMG_IR_IRQ_DATA2_VALID); > + } else { > + /* Only use the valid interrupt */ > + hw->flags &= ~IMG_IR_F_FILTER; > + irq_en &= ~IMG_IR_IRQ_DATA_MATCH; > + irq_on = IMG_IR_IRQ_DATA_VALID | IMG_IR_IRQ_DATA2_VALID; > + } > + irq_en |= irq_on; > + > + img_ir_write_filter(priv, filter); > + /* clear any interrupts we're enabling so we don't handle old ones */ > + img_ir_write(priv, IMG_IR_IRQ_CLEAR, irq_on); > + img_ir_write(priv, IMG_IR_IRQ_ENABLE, irq_en); > +} > + > +/* caller must have lock */ > +static void _img_ir_set_wake_filter(struct img_ir_priv *priv, > + struct img_ir_filter *filter) > +{ > + struct img_ir_priv_hw *hw = &priv->hw; > + if (filter) { > + /* Enable wake, and copy filter for later */ > + hw->wake_filter = *filter; > + hw->flags |= IMG_IR_F_WAKE; > + } else { > + /* Disable wake */ > + hw->flags &= ~IMG_IR_F_WAKE; > + } > +} > + > +/* caller must have lock */ > +static void _img_ir_update_filters(struct img_ir_priv *priv) > +{ > + struct img_ir_priv_hw *hw = &priv->hw; > + struct img_ir_filter filter; > + int ret1 = -1, ret2 = -1; > + > + /* clear raw filters */ > + _img_ir_set_filter(priv, NULL); > + _img_ir_set_wake_filter(priv, NULL); > + > + /* convert scancode filters to raw filters and try to set them */ > + if (hw->decoder && hw->decoder->filter) { > + if (hw->sc_filter.mask) { > + filter.minlen = 0; > + filter.maxlen = ~0; > + dev_dbg(priv->dev, "IR scancode filter=%08x & %08x\n", > + hw->sc_filter.data, > + hw->sc_filter.mask); > + ret1 = hw->decoder->filter(&hw->sc_filter, &filter, > + hw->enabled_protocols); > + if (!ret1) { > + dev_dbg(priv->dev, "IR raw filter=%016llx & %016llx\n", > + (unsigned long long)filter.data, > + (unsigned long long)filter.mask); > + _img_ir_set_filter(priv, &filter); > + } > + } > + if (hw->sc_wake_filter.mask) { > + filter.minlen = 0; > + filter.maxlen = ~0; > + dev_dbg(priv->dev, "IR scancode wake filter=%08x & %08x\n", > + hw->sc_wake_filter.data, > + hw->sc_wake_filter.mask); > + ret2 = hw->decoder->filter(&hw->sc_wake_filter, > + &filter, > + hw->enabled_protocols); > + if (!ret2) { > + dev_dbg(priv->dev, "IR raw wake filter=%016llx & %016llx\n", > + (unsigned long long)filter.data, > + (unsigned long long)filter.mask); > + _img_ir_set_wake_filter(priv, &filter); > + } > + } > + } > + > + /* > + * if either of the filters couldn't get set, clear the corresponding > + * scancode filter mask. > + */ > + if (ret1) > + hw->sc_filter.mask = 0; > + if (ret2) > + hw->sc_wake_filter.mask = 0; > +} > + > +static void img_ir_update_filters(struct img_ir_priv *priv) > +{ > + unsigned long flags; > + > + spin_lock_irqsave(&priv->lock, flags); > + _img_ir_update_filters(priv); > + spin_unlock_irqrestore(&priv->lock, flags); > +} > + > +/** > + * img_ir_set_decoder() - Set the current decoder. > + * @priv: IR private data. > + * @decoder: Decoder to use with immediate effect. > + * @proto: Protocol bitmap (or 0 to use decoder->type). > + */ > +static void img_ir_set_decoder(struct img_ir_priv *priv, > + struct img_ir_decoder *decoder, > + u64 proto) > +{ > + struct img_ir_priv_hw *hw = &priv->hw; > + unsigned long flags; > + u32 ir_status, irq_en; > + spin_lock_irqsave(&priv->lock, flags); > + > + /* switch off and disable interrupts */ > + img_ir_write(priv, IMG_IR_CONTROL, 0); > + irq_en = img_ir_read(priv, IMG_IR_IRQ_ENABLE); > + img_ir_write(priv, IMG_IR_IRQ_ENABLE, irq_en & IMG_IR_IRQ_EDGE); > + img_ir_write(priv, IMG_IR_IRQ_CLEAR, IMG_IR_IRQ_ALL & ~IMG_IR_IRQ_EDGE); > + > + /* ack any data already detected */ > + ir_status = img_ir_read(priv, IMG_IR_STATUS); > + if (ir_status & (IMG_IR_RXDVAL | IMG_IR_RXDVALD2)) { > + ir_status &= ~(IMG_IR_RXDVAL | IMG_IR_RXDVALD2); > + img_ir_write(priv, IMG_IR_STATUS, ir_status); > + img_ir_read(priv, IMG_IR_DATA_LW); > + img_ir_read(priv, IMG_IR_DATA_UP); > + } > + > + /* clear the scancode filters */ > + hw->sc_filter.data = 0; > + hw->sc_filter.mask = 0; > + hw->sc_wake_filter.data = 0; > + hw->sc_wake_filter.mask = 0; > + > + /* update (clear) the raw filters */ > + _img_ir_update_filters(priv); > + > + /* clear the enabled protocols */ > + hw->enabled_protocols = 0; > + > + /* switch decoder */ > + hw->decoder = decoder; > + if (!decoder) > + goto unlock; > + > + hw->mode = IMG_IR_M_NORMAL; > + > + /* set the enabled protocols */ > + if (!proto) > + proto = decoder->type; > + hw->enabled_protocols = proto; > + > + /* write the new timings */ > + img_ir_check_timings(priv); > + img_ir_write_timings_normal(priv, &decoder->reg_timings); > + > + /* set up and enable */ > + img_ir_write(priv, IMG_IR_CONTROL, decoder->reg_ctrl); > + > + > +unlock: > + spin_unlock_irqrestore(&priv->lock, flags); > +} > + > +/** > + * img_ir_decoder_compatable() - Find whether a decoder will work with a device. > + * @priv: IR private data. > + * @dec: Decoder to check. > + * > + * Returns: true if @dec is compatible with the device @priv refers to. > + */ > +static bool img_ir_decoder_compatible(struct img_ir_priv *priv, > + struct img_ir_decoder *dec) > +{ > + unsigned int ct; > + > + /* don't accept decoders using code types which aren't supported */ > + ct = dec->control.code_type; > + if (priv->hw.ct_quirks[ct] & IMG_IR_QUIRK_CODE_BROKEN) > + return false; > + > + return true; > +} > + > +/** > + * img_ir_allowed_protos() - Get allowed protocols from global decoder list. > + * @priv: IR private data. > + * > + * img_ir_decoders_lock must be locked by caller. > + * > + * Returns: Mask of protocols supported by the device @priv refers to. > + */ > +static unsigned long img_ir_allowed_protos(struct img_ir_priv *priv) > +{ > + unsigned long protos = 0; > + struct img_ir_decoder *dec; > + > + for (dec = img_ir_decoders; dec; dec = dec->next) > + if (img_ir_decoder_compatible(priv, dec)) > + protos |= dec->type; > + return protos; > +} > + > +/** > + * img_ir_update_allowed_protos() - Update devices with allowed protocols. > + * > + * img_ir_decoders_lock must be locked by caller. > + */ > +static void img_ir_update_allowed_protos(void) > +{ > + struct img_ir_priv *priv; > + > + for (priv = img_ir_privs; priv; priv = priv->next) > + priv->hw.rdev->allowed_protos = img_ir_allowed_protos(priv); > +} > + > +/* Callback for changing protocol using sysfs */ > +static int img_ir_change_protocol(struct rc_dev *data, u64 *ir_type) > +{ > + struct img_ir_priv *priv; > + struct img_ir_decoder *dec; > + int ret = -EINVAL; > + priv = data->priv; > + > + spin_lock(&img_ir_decoders_lock); > + for (dec = img_ir_decoders; dec; dec = dec->next) { > + if (!img_ir_decoder_compatible(priv, dec)) > + continue; > + if (*ir_type & dec->type) { > + *ir_type &= dec->type; > + img_ir_set_decoder(priv, dec, *ir_type); > + ret = 0; > + break; > + } > + } > + spin_unlock(&img_ir_decoders_lock); > + return ret; > +} > + > +/* Changes ir-core protocol device attribute */ > +static void img_ir_set_protocol(struct img_ir_priv *priv, u64 proto) > +{ > + struct rc_dev *ir_dev = priv->hw.rdev; > + > + unsigned long flags; > + > + spin_lock_irqsave(&ir_dev->rc_map.lock, flags); > + ir_dev->rc_map.rc_type = proto; > + spin_unlock_irqrestore(&ir_dev->rc_map.lock, flags); > +} > + > +/* Register an ir decoder */ > +int img_ir_register_decoder(struct img_ir_decoder *dec) > +{ > + struct img_ir_priv *priv; > + > + /* first preprocess decoder timings */ > + img_ir_decoder_preprocess(dec); > + > + spin_lock(&img_ir_decoders_lock); > + /* add to list */ > + dec->next = img_ir_decoders; > + img_ir_decoders = dec; > + img_ir_update_allowed_protos(); > + /* if it's the first decoder, start using it */ > + for (priv = img_ir_privs; priv; priv = priv->next) { > + if (!priv->hw.decoder && img_ir_decoder_compatible(priv, dec)) { > + img_ir_set_protocol(priv, dec->type); > + img_ir_set_decoder(priv, dec, 0); > + } > + } > + spin_unlock(&img_ir_decoders_lock); > + return 0; > +} > +EXPORT_SYMBOL_GPL(img_ir_register_decoder); > + > +/* Unregister an ir decoder */ > +void img_ir_unregister_decoder(struct img_ir_decoder *dec) > +{ > + struct img_ir_priv *priv; > + > + spin_lock(&img_ir_decoders_lock); > + /* If the decoder is in use, stop it now */ > + for (priv = img_ir_privs; priv; priv = priv->next) > + if (priv->hw.decoder == dec) { > + img_ir_set_protocol(priv, 0); > + img_ir_set_decoder(priv, NULL, 0); > + } > + /* Remove from list of decoders */ > + if (img_ir_decoders == dec) { > + img_ir_decoders = dec->next; > + } else { > + struct img_ir_decoder *cur; > + for (cur = img_ir_decoders; cur; cur = cur->next) > + if (dec == cur->next) { > + cur->next = dec->next; > + dec->next = NULL; > + break; > + } > + } > + img_ir_update_allowed_protos(); > + spin_unlock(&img_ir_decoders_lock); > +} > +EXPORT_SYMBOL_GPL(img_ir_unregister_decoder); > + > +static void img_ir_register_device(struct img_ir_priv *priv) > +{ > + spin_lock(&img_ir_decoders_lock); > + priv->next = img_ir_privs; > + img_ir_privs = priv; > + priv->hw.rdev->allowed_protos = img_ir_allowed_protos(priv); > + spin_unlock(&img_ir_decoders_lock); > +} > + > +static void img_ir_unregister_device(struct img_ir_priv *priv) > +{ > + struct img_ir_priv *cur; > + > + spin_lock(&img_ir_decoders_lock); > + if (img_ir_privs == priv) > + img_ir_privs = priv->next; > + else > + for (cur = img_ir_privs; cur; cur = cur->next) > + if (cur->next == priv) { > + cur->next = priv->next; > + break; > + } > + img_ir_set_decoder(priv, NULL, 0); > + spin_unlock(&img_ir_decoders_lock); > +} > + > +#ifdef CONFIG_PM_SLEEP > +/** > + * img_ir_enable_wake() - Switch to wake mode. > + * @priv: IR private data. > + * > + * Returns: non-zero if the IR can wake the system. > + */ > +static int img_ir_enable_wake(struct img_ir_priv *priv) > +{ > + struct img_ir_priv_hw *hw = &priv->hw; > + int ret = 0; > + unsigned long flags; > + > + spin_lock_irqsave(&priv->lock, flags); > + if (hw->flags & IMG_IR_F_WAKE) { > + /* interrupt only on a match */ > + hw->suspend_irqen = img_ir_read(priv, IMG_IR_IRQ_ENABLE); > + img_ir_write(priv, IMG_IR_IRQ_ENABLE, IMG_IR_IRQ_DATA_MATCH); > + img_ir_write_filter(priv, &hw->wake_filter); > + img_ir_write_timings_wake(priv, &hw->decoder->reg_timings); > + hw->mode = IMG_IR_M_WAKE; > + ret = 1; > + } > + spin_unlock_irqrestore(&priv->lock, flags); > + return ret; > +} > + > +/** > + * img_ir_disable_wake() - Switch out of wake mode. > + * @priv: IR private data > + * > + * Returns: 1 if the hardware should be allowed to wake from a sleep state. > + * 0 otherwise. > + */ > +static int img_ir_disable_wake(struct img_ir_priv *priv) > +{ > + struct img_ir_priv_hw *hw = &priv->hw; > + int ret = 0; > + unsigned long flags; > + > + spin_lock_irqsave(&priv->lock, flags); > + if (hw->flags & IMG_IR_F_WAKE) { > + /* restore normal filtering */ > + if (hw->flags & IMG_IR_F_FILTER) { > + img_ir_write(priv, IMG_IR_IRQ_ENABLE, > + (hw->suspend_irqen & IMG_IR_IRQ_EDGE) | > + IMG_IR_IRQ_DATA_MATCH); > + img_ir_write_filter(priv, &hw->filter); > + } else { > + img_ir_write(priv, IMG_IR_IRQ_ENABLE, > + (hw->suspend_irqen & IMG_IR_IRQ_EDGE) | > + IMG_IR_IRQ_DATA_VALID | > + IMG_IR_IRQ_DATA2_VALID); > + img_ir_write_filter(priv, NULL); > + } > + img_ir_write_timings_normal(priv, &hw->decoder->reg_timings); > + hw->mode = IMG_IR_M_NORMAL; > + ret = 1; > + } > + spin_unlock_irqrestore(&priv->lock, flags); > + return ret; > +} > +#endif /* CONFIG_PM_SLEEP */ > + > +/* lock must be held */ > +static void img_ir_begin_repeat(struct img_ir_priv *priv) > +{ > + struct img_ir_priv_hw *hw = &priv->hw; > + if (hw->mode == IMG_IR_M_NORMAL) { > + struct img_ir_decoder *dec = hw->decoder; > + > + /* switch to repeat timings */ > + img_ir_write(priv, IMG_IR_CONTROL, 0); > + hw->mode = IMG_IR_M_REPEATING; > + img_ir_write_timings_normal(priv, &dec->reg_rtimings); > + img_ir_write(priv, IMG_IR_CONTROL, dec->reg_ctrl); > + } > +} > + > +/* lock must be held */ > +static void img_ir_end_repeat(struct img_ir_priv *priv) > +{ > + struct img_ir_priv_hw *hw = &priv->hw; > + if (hw->mode == IMG_IR_M_REPEATING) { > + struct img_ir_decoder *dec = hw->decoder; > + > + /* switch to normal timings */ > + img_ir_write(priv, IMG_IR_CONTROL, 0); > + hw->mode = IMG_IR_M_NORMAL; > + img_ir_write_timings_normal(priv, &dec->reg_timings); > + img_ir_write(priv, IMG_IR_CONTROL, dec->reg_ctrl); > + } > +} > + > +/* lock must be held */ > +static void img_ir_handle_data(struct img_ir_priv *priv, u32 len, u64 raw) > +{ > + struct img_ir_priv_hw *hw = &priv->hw; > + struct img_ir_decoder *dec = hw->decoder; > + int scancode = IMG_IR_ERR_INVALID; > + if (dec->scancode) > + scancode = dec->scancode(len, raw, hw->enabled_protocols); > + else if (len >= 32) > + scancode = (u32)raw; > + else if (len < 32) > + scancode = (u32)raw & ((1 << len)-1); > + dev_dbg(priv->dev, "data (%u bits) = %#llx\n", > + len, (unsigned long long)raw); > + if (scancode >= 0) { > + dev_dbg(priv->dev, "decoded scan code %#x\n", scancode); > + rc_keydown(hw->rdev, scancode, 0); > + img_ir_end_repeat(priv); > + } else if (scancode == IMG_IR_REPEATCODE) { > + if (hw->mode == IMG_IR_M_REPEATING) { > + dev_dbg(priv->dev, "decoded repeat code\n"); > + rc_repeat(hw->rdev); > + } else { > + dev_dbg(priv->dev, "decoded unexpected repeat code, ignoring\n"); > + } > + } else { > + return; > + } > + > + > + if (dec->repeat) { > + unsigned long interval; > + > + img_ir_begin_repeat(priv); > + > + /* update timer, but allowing for 1/8th tolerance */ > + interval = dec->repeat + (dec->repeat >> 3); > + mod_timer(&hw->end_timer, > + jiffies + msecs_to_jiffies(interval)); > + } > +} > + > +/* timer function to end waiting for repeat. */ > +static void img_ir_end_timer(unsigned long arg) > +{ > + unsigned long flags; > + struct img_ir_priv *priv = (struct img_ir_priv *)arg; > + > + spin_lock_irqsave(&priv->lock, flags); > + img_ir_end_repeat(priv); > + spin_unlock_irqrestore(&priv->lock, flags); > +} > + > +/* Kernel interface */ > + > +static ssize_t img_ir_filter_show(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct rc_dev *rdev = container_of(dev, struct rc_dev, dev); > + struct img_ir_priv *priv = rdev->priv; > + > + return sprintf(buf, "%#x\n", priv->hw.sc_filter.data); > +} > +static ssize_t img_ir_filter_store(struct device *dev, > + struct device_attribute *attr, > + const char *buf, size_t count) > +{ > + struct rc_dev *rdev = container_of(dev, struct rc_dev, dev); > + struct img_ir_priv *priv = rdev->priv; > + int ret; > + unsigned long val; > + > + ret = kstrtoul(buf, 0, &val); > + if (ret < 0) > + return ret; > + priv->hw.sc_filter.data = val; > + img_ir_update_filters(priv); > + return count; > +} > +static DEVICE_ATTR(filter, S_IRUGO|S_IWUSR, > + img_ir_filter_show, > + img_ir_filter_store); > + > +static ssize_t img_ir_filter_mask_show(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct rc_dev *rdev = container_of(dev, struct rc_dev, dev); > + struct img_ir_priv *priv = rdev->priv; > + > + return sprintf(buf, "%#x\n", priv->hw.sc_filter.mask); > +} > +static ssize_t img_ir_filter_mask_store(struct device *dev, > + struct device_attribute *attr, > + const char *buf, size_t count) > +{ > + struct rc_dev *rdev = container_of(dev, struct rc_dev, dev); > + struct img_ir_priv *priv = rdev->priv; > + int ret; > + unsigned long val; > + > + ret = kstrtoul(buf, 0, &val); > + if (ret < 0) > + return ret; > + priv->hw.sc_filter.mask = val; > + img_ir_update_filters(priv); > + return count; > +} > +static DEVICE_ATTR(filter_mask, S_IRUGO|S_IWUSR, > + img_ir_filter_mask_show, > + img_ir_filter_mask_store); > + > +static ssize_t img_ir_wakeup_filter_show(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct rc_dev *rdev = container_of(dev, struct rc_dev, dev); > + struct img_ir_priv *priv = rdev->priv; > + > + return sprintf(buf, "%#x\n", priv->hw.sc_wake_filter.data); > +} > +static ssize_t img_ir_wakeup_filter_store(struct device *dev, > + struct device_attribute *attr, > + const char *buf, size_t count) > +{ > + struct rc_dev *rdev = container_of(dev, struct rc_dev, dev); > + struct img_ir_priv *priv = rdev->priv; > + int ret; > + unsigned long val; > + > + ret = kstrtoul(buf, 0, &val); > + if (ret < 0) > + return ret; > + priv->hw.sc_wake_filter.data = val; > + img_ir_update_filters(priv); > + return count; > +} > +static DEVICE_ATTR(wakeup_filter, S_IRUGO|S_IWUSR, > + img_ir_wakeup_filter_show, > + img_ir_wakeup_filter_store); > + > +static ssize_t img_ir_wakeup_filter_mask_show(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct rc_dev *rdev = container_of(dev, struct rc_dev, dev); > + struct img_ir_priv *priv = rdev->priv; > + > + return sprintf(buf, "%#x\n", priv->hw.sc_wake_filter.mask); > +} > +static ssize_t img_ir_wakeup_filter_mask_store(struct device *dev, > + struct device_attribute *attr, > + const char *buf, size_t count) > +{ > + struct rc_dev *rdev = container_of(dev, struct rc_dev, dev); > + struct img_ir_priv *priv = rdev->priv; > + int ret; > + unsigned long val; > + > + ret = kstrtoul(buf, 0, &val); > + if (ret < 0) > + return ret; > + priv->hw.sc_wake_filter.mask = val; > + img_ir_update_filters(priv); > + return count; > +} > +static DEVICE_ATTR(wakeup_filter_mask, S_IRUGO|S_IWUSR, > + img_ir_wakeup_filter_mask_show, > + img_ir_wakeup_filter_mask_store); > + > +static void img_ir_attr_create(struct device *dev) > +{ > + device_create_file(dev, &dev_attr_filter); > + device_create_file(dev, &dev_attr_filter_mask); > + device_create_file(dev, &dev_attr_wakeup_filter); > + device_create_file(dev, &dev_attr_wakeup_filter_mask); > +} > + > +static void img_ir_attr_remove(struct device *dev) > +{ > + device_remove_file(dev, &dev_attr_filter); > + device_remove_file(dev, &dev_attr_filter_mask); > + device_remove_file(dev, &dev_attr_wakeup_filter); > + device_remove_file(dev, &dev_attr_wakeup_filter_mask); > +} > + > +#ifdef CONFIG_COMMON_CLK > +static void img_ir_change_frequency(struct img_ir_priv *priv, > + struct clk_notifier_data *change) > +{ > + struct img_ir_priv_hw *hw = &priv->hw; > + struct img_ir_decoder *dec; > + unsigned long flags; > + > + dev_dbg(priv->dev, "clk changed %lu HZ -> %lu HZ\n", > + change->old_rate, change->new_rate); > + > + spin_lock_irqsave(&priv->lock, flags); > + dec = hw->decoder; > + hw->clk_hz = change->new_rate; > + /* refresh current timings */ > + if (hw->decoder) { > + img_ir_check_timings(priv); > + switch (hw->mode) { > + case IMG_IR_M_NORMAL: > + img_ir_write_timings_normal(priv, &dec->reg_timings); > + break; > + case IMG_IR_M_REPEATING: > + img_ir_write_timings_normal(priv, &dec->reg_rtimings); > + break; > +#ifdef CONFIG_PM_SLEEP > + case IMG_IR_M_WAKE: > + img_ir_write_timings_wake(priv, &dec->reg_timings); > + break; > +#endif > + } > + } > + spin_unlock_irqrestore(&priv->lock, flags); > +} > + > +static int img_ir_clk_notify(struct notifier_block *self, unsigned long action, > + void *data) > +{ > + struct img_ir_priv *priv = container_of(self, struct img_ir_priv, > + hw.clk_nb); > + switch (action) { > + case POST_RATE_CHANGE: > + img_ir_change_frequency(priv, data); > + break; > + default: > + break; > + } > + return NOTIFY_OK; > +} > +#endif /* CONFIG_COMMON_CLK */ > + > +void img_ir_isr_hw(struct img_ir_priv *priv, u32 irq_status) > +{ > + struct img_ir_priv_hw *hw = &priv->hw; > + u32 ir_status, len, lw, up; > + unsigned int ct; > + > + /* use the current decoder */ > + if (!hw->decoder) > + return; > + > + ir_status = img_ir_read(priv, IMG_IR_STATUS); > + if (!(ir_status & (IMG_IR_RXDVAL | IMG_IR_RXDVALD2))) > + return; > + ir_status &= ~(IMG_IR_RXDVAL | IMG_IR_RXDVALD2); > + img_ir_write(priv, IMG_IR_STATUS, ir_status); > + > + len = (ir_status & IMG_IR_RXDLEN) >> IMG_IR_RXDLEN_SHIFT; > + /* some versions report wrong length for certain code types */ > + ct = hw->decoder->control.code_type; > + if (hw->ct_quirks[ct] & IMG_IR_QUIRK_CODE_LEN_INCR) > + ++len; > + > + lw = img_ir_read(priv, IMG_IR_DATA_LW); > + up = img_ir_read(priv, IMG_IR_DATA_UP); > + img_ir_handle_data(priv, len, (u64)up << 32 | lw); > +} > + > +void img_ir_setup_hw(struct img_ir_priv *priv) > +{ > + struct img_ir_decoder *dec; > + > + if (!priv->hw.rdev) > + return; > + > + spin_lock(&img_ir_decoders_lock); > + /* Use the first available decoder (or disable stuff if NULL) */ > + for (dec = img_ir_decoders; dec; dec = dec->next) { > + if (img_ir_decoder_compatible(priv, dec)) { > + img_ir_set_protocol(priv, dec->type); > + img_ir_set_decoder(priv, dec, 0); > + goto unlock; > + } > + } > + img_ir_set_decoder(priv, NULL, 0); > +unlock: > + spin_unlock(&img_ir_decoders_lock); > +} > + > +/** > + * img_ir_probe_hw_caps() - Probe capabilities of the hardware. > + * @priv: IR private data. > + */ > +static void img_ir_probe_hw_caps(struct img_ir_priv *priv) > +{ > + struct img_ir_priv_hw *hw = &priv->hw; > + /* > + * When a version of the block becomes available without these quirks, > + * they'll have to depend on the core revision. > + */ > + hw->ct_quirks[IMG_IR_CODETYPE_PULSELEN] > + |= IMG_IR_QUIRK_CODE_LEN_INCR; > + hw->ct_quirks[IMG_IR_CODETYPE_BIPHASE] > + |= IMG_IR_QUIRK_CODE_BROKEN; > + hw->ct_quirks[IMG_IR_CODETYPE_2BITPULSEPOS] > + |= IMG_IR_QUIRK_CODE_BROKEN; > +} > + > +int img_ir_probe_hw(struct img_ir_priv *priv) > +{ > + struct img_ir_priv_hw *hw = &priv->hw; > + struct rc_dev *rdev; > + int error; > + > + /* Probe hardware capabilities */ > + img_ir_probe_hw_caps(priv); > + > + /* Set up the end timer */ > + init_timer(&hw->end_timer); > + hw->end_timer.function = img_ir_end_timer; > + hw->end_timer.data = (unsigned long)priv; > + > + /* Register a clock notifier */ > + if (!IS_ERR(priv->clk)) { > + hw->clk_hz = clk_get_rate(priv->clk); > +#ifdef CONFIG_COMMON_CLK > + hw->clk_nb.notifier_call = img_ir_clk_notify; > + error = clk_notifier_register(priv->clk, &hw->clk_nb); > + if (error) > + dev_warn(priv->dev, > + "failed to register clock notifier\n"); > +#endif > + } else { > + hw->clk_hz = 32768; > + } > + > + /* Allocate hardware decoder */ > + hw->rdev = rdev = rc_allocate_device(); > + if (!rdev) { > + dev_err(priv->dev, "cannot allocate input device\n"); > + error = -ENOMEM; > + goto err_alloc_rc; > + } > + rdev->priv = priv; > + rdev->map_name = RC_MAP_EMPTY; > + rdev->input_name = "IMG Infrared Decoder"; > + /* img_ir_register_device sets rdev->allowed_protos. */ > + img_ir_register_device(priv); > + > + /* Register hardware decoder */ > + error = rc_register_device(rdev); > + if (error) { > + dev_err(priv->dev, "failed to register IR input device\n"); > + goto err_register_rc; > + } > + > + /* > + * Set this after rc_register_device as no protocols have been > + * registered yet. > + */ > + rdev->change_protocol = img_ir_change_protocol; > + > + device_init_wakeup(priv->dev, 1); > + > + /* Create custom sysfs attributes */ > + img_ir_attr_create(&rdev->dev); > + > + return 0; > + > +err_register_rc: > + img_ir_unregister_device(priv); > + hw->rdev = NULL; > + rc_free_device(rdev); > +err_alloc_rc: > +#ifdef CONFIG_COMMON_CLK > + if (!IS_ERR(priv->clk)) > + clk_notifier_unregister(priv->clk, &hw->clk_nb); > +#endif > + return error; > +} > + > +void img_ir_remove_hw(struct img_ir_priv *priv) > +{ > + struct img_ir_priv_hw *hw = &priv->hw; > + struct rc_dev *rdev = hw->rdev; > + if (!rdev) > + return; > + img_ir_attr_remove(&rdev->dev); > + img_ir_unregister_device(priv); > + hw->rdev = NULL; > + rc_unregister_device(rdev); > +#ifdef CONFIG_COMMON_CLK > + if (!IS_ERR(priv->clk)) > + clk_notifier_unregister(priv->clk, &hw->clk_nb); > +#endif > +} > + > +#ifdef CONFIG_PM_SLEEP > +int img_ir_suspend(struct device *dev) > +{ > + struct img_ir_priv *priv = dev_get_drvdata(dev); > + > + if (device_may_wakeup(dev) && img_ir_enable_wake(priv)) > + enable_irq_wake(priv->irq); > + return 0; > +} > + > +int img_ir_resume(struct device *dev) > +{ > + struct img_ir_priv *priv = dev_get_drvdata(dev); > + > + if (device_may_wakeup(dev) && img_ir_disable_wake(priv)) > + disable_irq_wake(priv->irq); > + return 0; > +} > +#endif /* CONFIG_PM_SLEEP */ > diff --git a/drivers/media/rc/img-ir/img-ir-hw.h b/drivers/media/rc/img-ir/img-ir-hw.h > new file mode 100644 > index 000000000000..0fb8e48fda3a > --- /dev/null > +++ b/drivers/media/rc/img-ir/img-ir-hw.h > @@ -0,0 +1,284 @@ > +/* > + * ImgTec IR Hardware Decoder found in PowerDown Controller. > + * > + * Copyright 2010-2013 Imagination Technologies Ltd. > + */ > + > +#ifndef _IMG_IR_HW_H_ > +#define _IMG_IR_HW_H_ > + > +#include <linux/kernel.h> > +#include <media/rc-core.h> > + > +/* constants */ > + > +#define IMG_IR_CODETYPE_PULSELEN 0x0 /* Sony */ > +#define IMG_IR_CODETYPE_PULSEDIST 0x1 /* NEC, Toshiba, Micom, Sharp */ > +#define IMG_IR_CODETYPE_BIPHASE 0x2 /* RC-5/6 */ > +#define IMG_IR_CODETYPE_2BITPULSEPOS 0x3 /* RC-MM */ > + > + > +/* Timing information */ > + > +/** > + * struct img_ir_control - Decoder control settings > + * @decoden: Primary decoder enable > + * @code_type: Decode type (see IMG_IR_CODETYPE_*) > + * @hdrtog: Detect header toggle symbol after leader symbol > + * @ldrdec: Don't discard leader if maximum width reached > + * @decodinpol: Decoder input polarity (1=active high) > + * @bitorien: Bit orientation (1=MSB first) > + * @d1validsel: Decoder 2 takes over if it detects valid data > + * @bitinv: Bit inversion switch (1=don't invert) > + * @decodend2: Secondary decoder enable (no leader symbol) > + * @bitoriend2: Bit orientation (1=MSB first) > + * @bitinvd2: Secondary decoder bit inversion switch (1=don't invert) > + */ > +struct img_ir_control { > + unsigned decoden:1; > + unsigned code_type:2; > + unsigned hdrtog:1; > + unsigned ldrdec:1; > + unsigned decodinpol:1; > + unsigned bitorien:1; > + unsigned d1validsel:1; > + unsigned bitinv:1; > + unsigned decodend2:1; > + unsigned bitoriend2:1; > + unsigned bitinvd2:1; > +}; > + > +/** > + * struct img_ir_timing_range - range of timing values > + * @min: Minimum timing value > + * @max: Maximum timing value (if < @min, this will be set to @min during > + * preprocessing step, so it is normally not explicitly initialised > + * and is taken care of by the tolerance) > + */ > +struct img_ir_timing_range { > + u16 min; > + u16 max; > +}; > + > +/** > + * struct img_ir_symbol_timing - timing data for a symbol > + * @pulse: Timing range for the length of the pulse in this symbol > + * @space: Timing range for the length of the space in this symbol > + */ > +struct img_ir_symbol_timing { > + struct img_ir_timing_range pulse; > + struct img_ir_timing_range space; > +}; > + > +/** > + * struct img_ir_free_timing - timing data for free time symbol > + * @minlen: Minimum number of bits of data > + * @maxlen: Maximum number of bits of data > + * @ft_min: Minimum free time after message > + */ > +struct img_ir_free_timing { > + /* measured in bits */ > + u8 minlen; > + u8 maxlen; > + u16 ft_min; > +}; > + > +/** > + * struct img_ir_timings - Timing values. > + * @ldr: Leader symbol timing data > + * @s00: Zero symbol timing data for primary decoder > + * @s01: One symbol timing data for primary decoder > + * @s10: Zero symbol timing data for secondary (no leader symbol) decoder > + * @s11: One symbol timing data for secondary (no leader symbol) decoder > + * @ft: Free time symbol timing data > + */ > +struct img_ir_timings { > + struct img_ir_symbol_timing ldr, s00, s01, s10, s11; > + struct img_ir_free_timing ft; > +}; > + > +/** > + * struct img_ir_sc_filter - Filter scan codes. > + * @data: Data to match. > + * @mask: Mask of bits to compare. > + */ > +struct img_ir_sc_filter { > + unsigned int data; > + unsigned int mask; > +}; > + > +/** > + * struct img_ir_filter - Filter IR events. > + * @data: Data to match. > + * @mask: Mask of bits to compare. > + * @minlen: Additional minimum number of bits. > + * @maxlen: Additional maximum number of bits. > + */ > +struct img_ir_filter { > + u64 data; > + u64 mask; > + u8 minlen; > + u8 maxlen; > +}; > + > +/** > + * struct img_ir_timing_regvals - Calculated timing register values. > + * @ldr: Leader symbol timing register value > + * @s00: Zero symbol timing register value for primary decoder > + * @s01: One symbol timing register value for primary decoder > + * @s10: Zero symbol timing register value for secondary decoder > + * @s11: One symbol timing register value for secondary decoder > + * @ft: Free time symbol timing register value > + */ > +struct img_ir_timing_regvals { > + u32 ldr, s00, s01, s10, s11, ft; > +}; > + > +#define IMG_IR_REPEATCODE (-1) /* repeat the previous code */ > +#define IMG_IR_ERR_INVALID (-2) /* not a valid code */ > + > +/** > + * struct img_ir_decoder - Decoder settings for an IR protocol. > + * @type: Protocol types bitmap. > + * @unit: Unit of timings in nanoseconds (default 1 us). > + * @timings: Primary timings > + * @rtimings: Additional override timings while waiting for repeats. > + * @repeat: Maximum repeat interval (always in milliseconds). > + * @control: Control flags. > + * > + * @scancode: Pointer to function to convert the IR data into a > + * scancode (it must be safe to execute in interrupt > + * context). > + * Returns IMG_IR_REPEATCODE to repeat previous code. > + * Returns IMG_IR_ERR_* on error. > + * @filter: Pointer to function to convert scancode filter to raw > + * hardware filter. The minlen and maxlen fields will have > + * been initialised to the maximum range. > + * > + * @reg_ctrl: Processed control register value. > + * @clk_hz: Assumed clock rate in Hz for processed timings. > + * @reg_timings: Processed primary timings. > + * @reg_rtimings: Processed repeat timings. > + * @next: Next IR decoder (to form a linked list). > + */ > +struct img_ir_decoder { > + /* core description */ > + u64 type; > + unsigned int unit; > + struct img_ir_timings timings; > + struct img_ir_timings rtimings; > + unsigned int repeat; > + struct img_ir_control control; > + > + /* scancode logic */ > + int (*scancode)(int len, u64 raw, u64 protocols); > + int (*filter)(const struct img_ir_sc_filter *in, > + struct img_ir_filter *out, u64 protocols); > + > + /* for internal use only */ > + u32 reg_ctrl; > + unsigned long clk_hz; > + struct img_ir_timing_regvals reg_timings; > + struct img_ir_timing_regvals reg_rtimings; > + struct img_ir_decoder *next; > +}; > + > +int img_ir_register_decoder(struct img_ir_decoder *dec); > +void img_ir_unregister_decoder(struct img_ir_decoder *dec); > + > +struct img_ir_priv; > + > +#ifdef CONFIG_IR_IMG_HW > + > +enum img_ir_mode { > + IMG_IR_M_NORMAL, > + IMG_IR_M_REPEATING, > +#ifdef CONFIG_PM_SLEEP > + IMG_IR_M_WAKE, > +#endif > +}; > + > +/** > + * struct img_ir_priv_hw - Private driver data for hardware decoder. > + * @ct_quirks: Quirk bits for each code type. > + * @rdev: Remote control device > + * @clk_nb: Notifier block for clock notify events. > + * @end_timer: Timer until repeat timeout. > + * @decoder: Current decoder settings. > + * @enabled_protocols: Currently enabled protocols. > + * @clk_hz: Current clock rate in Hz. > + * @flags: IMG_IR_F_*. > + * @filter: HW filter for normal events (derived from sc_filter). > + * @wake_filter: HW filter for wake event (derived from sc_wake_filter). > + * @sc_filter: Current scancode filter. > + * @sc_wake_filter: Current scancode filter for wake events. > + * @mode: Current decode mode. > + * @suspend_irqen: Saved IRQ enable mask over suspend. > + */ > +struct img_ir_priv_hw { > + unsigned int ct_quirks[4]; > + struct rc_dev *rdev; > + struct notifier_block clk_nb; > + struct timer_list end_timer; > + struct img_ir_decoder *decoder; > + u64 enabled_protocols; > + unsigned long clk_hz; > + unsigned int flags; > + struct img_ir_filter filter; > + struct img_ir_filter wake_filter; > + > + /* filters in terms of scancodes */ > + struct img_ir_sc_filter sc_filter; > + struct img_ir_sc_filter sc_wake_filter; > + > + enum img_ir_mode mode; > + u32 suspend_irqen; > +}; > + > +static inline bool img_ir_hw_enabled(struct img_ir_priv_hw *hw) > +{ > + return hw->rdev; > +}; > + > +void img_ir_isr_hw(struct img_ir_priv *priv, u32 irq_status); > +void img_ir_setup_hw(struct img_ir_priv *priv); > +int img_ir_probe_hw(struct img_ir_priv *priv); > +void img_ir_remove_hw(struct img_ir_priv *priv); > + > +#ifdef CONFIG_PM_SLEEP > +int img_ir_suspend(struct device *dev); > +int img_ir_resume(struct device *dev); > +#else > +#define img_ir_suspend NULL > +#define img_ir_resume NULL > +#endif > + > +#else > + > +struct img_ir_priv_hw { > +}; > + > +static inline bool img_ir_hw_enabled(struct img_ir_priv_hw *hw) > +{ > + return false; > +}; > +static inline void img_ir_isr_hw(struct img_ir_priv *priv, u32 irq_status) > +{ > +} > +static inline void img_ir_setup_hw(struct img_ir_priv *priv) > +{ > +} > +static inline int img_ir_probe_hw(struct img_ir_priv *priv) > +{ > + return -ENODEV; > +} > +static inline void img_ir_remove_hw(struct img_ir_priv *priv) > +{ > +} > + > +#define img_ir_suspend NULL > +#define img_ir_resume NULL > + > +#endif /* CONFIG_IR_IMG_HW */ > + > +#endif /* _IMG_IR_HW_H_ */ -- Cheers, Mauro ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 04/11] media: rc: img-ir: add hardware decoder driver 2013-12-22 13:40 ` Mauro Carvalho Chehab @ 2013-12-23 13:17 ` James Hogan 0 siblings, 0 replies; 21+ messages in thread From: James Hogan @ 2013-12-23 13:17 UTC (permalink / raw) To: Mauro Carvalho Chehab; +Cc: linux-media Hi Mauro, On 22/12/13 13:40, Mauro Carvalho Chehab wrote: > Em Fri, 13 Dec 2013 15:12:52 +0000 > James Hogan <james.hogan@imgtec.com> escreveu: > >> Add remote control input driver for the ImgTec Infrared block hardware >> decoder, which is set up with timings for a specific protocol and >> supports mask/value filtering and wake events. >> >> The hardware decoder timing values, raw data to scan code conversion >> function and scan code filter to raw data filter conversion function are >> provided as separate modules for each protocol which this part of the >> driver can use. The scan code filter value and mask (and the same again >> for wake from sleep) are specified via sysfs files in >> /sys/class/rc/rcX/. > > We should discuss a little more about those new sysfs controls. Yes I thought so. For reference there was some mention of this in an old thread ([RFC] What are the goals for the architecture of an in-kernel IR system), but I didn't go into any details back then: http://thread.gmane.org/gmane.linux.kernel.input/9747 (search for my name) > There are two separate questions here: > > 1) are those new sysfs controls really device specific? If not, then it > makes sense to add support for it at rc-core. I would say no, although at least wakeup filters is dependent on hardware support. I've attempted to make them fairly generic (i.e. they deal with scancodes rather than raw data, so the filter could certainly be done in software) but they do implicitly assume only a single protocol being enabled at a time (in fact the values reset if the protocol is changed). I suppose it could just be expected that matches on any enabled protocol would be reported. > I suspect that a wakeup scancode is something that should be part of the > RC core, as other devices may have it too. > > Also, the RC core currently supports a scancode mask. Not sure if it is > the same concept as the one used on your hardware. Could you please > explain it better? Okay. The hardware provides a data valid interrupt (indicating that data was received which conforms to the programmed timings), and a data match interrupt (indicating that the valid data matches a certain value - with a mask of bits which are compared). By default the data valid interrupt is used to trigger input events, and wake-on-interrupt is disabled. After ... echo 0x1fc00 > filter echo 0xffff00 > filter_mask ... the data match interrupt is used to trigger input events, and the value and mask are transformed into raw IR data value and mask and programmed into HW. In this case only extended-NEC IR codes (NEC is current protocol, extended because the filter value has 0xff0000 bits set) with an address field of 0x01fc will trigger input events, but the command code (in scancode bits 0x0000ff) isn't matched so any button on the remote triggers input events. As well as reducing the irrelevant input events, this prevents the driver switching to repeat code timings until the timeout is hit for codes that aren't actually interesting anyway. The wakeup_* files behave identically, except they apply only during suspend and a match triggers a wakeup. So after the following ... echo 0x1fc00 > wakeup_filter echo 0xffffff > wakeup_filter_mask ... when suspend takes place the wakeup specific value/mask is programmed, and the wake only occurs if the whole address and command code matches, i.e. only the power button of this specific remote triggers a wake. Note that wakeup_filter[_mask] currently does not have to be a subset of filter[_mask]. > 2) Where those new sysfs nodes will be documented. > > With regards to (2), we currently lack a chapter at the Linux Media > Documentation/DocBook or at Documentation/ABI/ for the existing sysfs > interface, but let's not increase the gap, please. > I'll see if I can find some time to write such documentation, probably > at Documentation/ABI/testing. > > So, if we come to the conclusion that those interfaces should be part > of the rc core, we'll need them to be documented at > Documentation/ABI/testing too. > > Otherwise, if we decide to add some of those as private API, please > add a README for this device, under Documentation/remote-controllers. Okay. > The patch itself (and patches 1-3) look OK to me. I'll be reviewing > the other patches on separate emails. Thanks very much for taking the time to review it. Cheers James ^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 05/11] media: rc: img-ir: add to build 2013-12-13 15:12 [PATCH 00/11] media: rc: ImgTec IR decoder driver James Hogan ` (3 preceding siblings ...) 2013-12-13 15:12 ` [PATCH 04/11] media: rc: img-ir: add hardware decoder driver James Hogan @ 2013-12-13 15:12 ` James Hogan 2013-12-13 15:12 ` [PATCH 06/11] media: rc: img-ir: add NEC decoder module James Hogan ` (5 subsequent siblings) 10 siblings, 0 replies; 21+ messages in thread From: James Hogan @ 2013-12-13 15:12 UTC (permalink / raw) To: Mauro Carvalho Chehab, linux-media; +Cc: James Hogan Add ImgTec IR decoder driver to the build system. 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/Kconfig | 2 ++ drivers/media/rc/Makefile | 1 + drivers/media/rc/img-ir/Kconfig | 26 ++++++++++++++++++++++++++ drivers/media/rc/img-ir/Makefile | 6 ++++++ 4 files changed, 35 insertions(+) create mode 100644 drivers/media/rc/img-ir/Kconfig create mode 100644 drivers/media/rc/img-ir/Makefile diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index 904f11367c29..43b71813862e 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -300,6 +300,8 @@ config IR_RX51 The driver uses omap DM timers for generating the carrier wave and pulses. +source "drivers/media/rc/img-ir/Kconfig" + config RC_LOOPBACK tristate "Remote Control Loopback Driver" depends on RC_CORE diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile index f4eb32c0a455..cbe3d44f7fab 100644 --- a/drivers/media/rc/Makefile +++ b/drivers/media/rc/Makefile @@ -31,3 +31,4 @@ obj-$(CONFIG_IR_GPIO_CIR) += gpio-ir-recv.o obj-$(CONFIG_IR_IGUANA) += iguanair.o obj-$(CONFIG_IR_TTUSBIR) += ttusbir.o obj-$(CONFIG_RC_ST) += st_rc.o +obj-$(CONFIG_IR_IMG) += img-ir/ diff --git a/drivers/media/rc/img-ir/Kconfig b/drivers/media/rc/img-ir/Kconfig new file mode 100644 index 000000000000..60eaba6a0843 --- /dev/null +++ b/drivers/media/rc/img-ir/Kconfig @@ -0,0 +1,26 @@ +config IR_IMG + tristate "ImgTec IR Decoder" + depends on RC_CORE + select IR_IMG_HW if !IR_IMG_RAW + help + Say Y or M here if you want to use the ImgTec infrared decoder + functionality found in SoCs such as TZ1090. + +config IR_IMG_RAW + bool "Raw decoder" + depends on IR_IMG + help + Say Y here to enable the raw mode driver which passes raw IR signal + changes to the IR raw decoders for software decoding. This is much + less reliable (due to lack of timestamps) and consumes more + processing power than using hardware decode, but can be useful for + testing, debug, and to make more protocols available. + +config IR_IMG_HW + bool "Hardware decoder" + depends on IR_IMG + help + Say Y here to enable the hardware decode driver which decodes the IR + 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. diff --git a/drivers/media/rc/img-ir/Makefile b/drivers/media/rc/img-ir/Makefile new file mode 100644 index 000000000000..4ef86edec873 --- /dev/null +++ b/drivers/media/rc/img-ir/Makefile @@ -0,0 +1,6 @@ +img-ir-y := img-ir-core.o +img-ir-$(CONFIG_IR_IMG_RAW) += img-ir-raw.o +img-ir-$(CONFIG_IR_IMG_HW) += img-ir-hw.o +img-ir-objs := $(img-ir-y) + +obj-$(CONFIG_IR_IMG) += img-ir.o -- 1.8.1.2 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 06/11] media: rc: img-ir: add NEC decoder module 2013-12-13 15:12 [PATCH 00/11] media: rc: ImgTec IR decoder driver James Hogan ` (4 preceding siblings ...) 2013-12-13 15:12 ` [PATCH 05/11] media: rc: img-ir: add to build James Hogan @ 2013-12-13 15:12 ` James Hogan 2013-12-22 13:49 ` Mauro Carvalho Chehab 2013-12-13 15:12 ` [PATCH 07/11] media: rc: img-ir: add JVC " James Hogan ` (4 subsequent siblings) 10 siblings, 1 reply; 21+ messages in thread From: James Hogan @ 2013-12-13 15:12 UTC (permalink / raw) To: Mauro Carvalho Chehab, linux-media; +Cc: James Hogan 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; + } + 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"); -- 1.8.1.2 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH 06/11] media: rc: img-ir: add NEC decoder module 2013-12-13 15:12 ` [PATCH 06/11] media: rc: img-ir: add NEC decoder module James Hogan @ 2013-12-22 13:49 ` Mauro Carvalho Chehab 2013-12-23 11:30 ` James Hogan 0 siblings, 1 reply; 21+ messages in thread From: Mauro Carvalho Chehab @ 2013-12-22 13:49 UTC (permalink / raw) To: James Hogan; +Cc: linux-media 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"); -- Cheers, Mauro ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 06/11] media: rc: img-ir: add NEC decoder module 2013-12-22 13:49 ` Mauro Carvalho Chehab @ 2013-12-23 11:30 ` James Hogan 0 siblings, 0 replies; 21+ messages in thread From: James Hogan @ 2013-12-23 11:30 UTC (permalink / raw) To: Mauro Carvalho Chehab; +Cc: linux-media, Jarod Wilson 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 ^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 07/11] media: rc: img-ir: add JVC decoder module 2013-12-13 15:12 [PATCH 00/11] media: rc: ImgTec IR decoder driver James Hogan ` (5 preceding siblings ...) 2013-12-13 15:12 ` [PATCH 06/11] media: rc: img-ir: add NEC decoder module James Hogan @ 2013-12-13 15:12 ` James Hogan 2013-12-13 15:12 ` [PATCH 08/11] media: rc: img-ir: add Sony " James Hogan ` (3 subsequent siblings) 10 siblings, 0 replies; 21+ messages in thread From: James Hogan @ 2013-12-13 15:12 UTC (permalink / raw) To: Mauro Carvalho Chehab, linux-media; +Cc: James Hogan Add an img-ir module for decoding the JVC infrared protocol. 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-jvc.c | 109 +++++++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+) create mode 100644 drivers/media/rc/img-ir/img-ir-jvc.c diff --git a/drivers/media/rc/img-ir/Kconfig b/drivers/media/rc/img-ir/Kconfig index 44d00227c6c4..b7774a30509f 100644 --- a/drivers/media/rc/img-ir/Kconfig +++ b/drivers/media/rc/img-ir/Kconfig @@ -31,3 +31,10 @@ config IR_IMG_NEC help Say Y or M here to enable support for the NEC and extended NEC protocols in the ImgTec infrared decoder block. + +config IR_IMG_JVC + tristate "JVC protocol support" + depends on IR_IMG && IR_IMG_HW + help + Say Y or M here to enable support for the JVC protocol in the ImgTec + infrared decoder block. diff --git a/drivers/media/rc/img-ir/Makefile b/drivers/media/rc/img-ir/Makefile index f3052878f092..1d9643801f02 100644 --- a/drivers/media/rc/img-ir/Makefile +++ b/drivers/media/rc/img-ir/Makefile @@ -5,3 +5,4 @@ img-ir-objs := $(img-ir-y) obj-$(CONFIG_IR_IMG) += img-ir.o obj-$(CONFIG_IR_IMG_NEC) += img-ir-nec.o +obj-$(CONFIG_IR_IMG_JVC) += img-ir-jvc.o diff --git a/drivers/media/rc/img-ir/img-ir-jvc.c b/drivers/media/rc/img-ir/img-ir-jvc.c new file mode 100644 index 000000000000..a6f383afd2b3 --- /dev/null +++ b/drivers/media/rc/img-ir/img-ir-jvc.c @@ -0,0 +1,109 @@ +/* + * ImgTec IR Decoder setup for JVC protocol. + * + * Copyright 2012-2013 Imagination Technologies Ltd. + */ + +#include <linux/module.h> + +#include "img-ir-hw.h" + +/* Convert JVC data to a scancode */ +static int img_ir_jvc_scancode(int len, u64 raw, u64 protocols) +{ + unsigned int cust, data; + + if (len != 16) + return IMG_IR_ERR_INVALID; + + cust = (raw >> 0) & 0xff; + data = (raw >> 8) & 0xff; + + return cust << 8 | data; +} + +/* Convert JVC scancode to JVC data filter */ +static int img_ir_jvc_filter(const struct img_ir_sc_filter *in, + struct img_ir_filter *out, u64 protocols) +{ + unsigned int cust, data; + unsigned int cust_m, data_m; + + cust = (in->data >> 8) & 0xff; + cust_m = (in->mask >> 8) & 0xff; + data = (in->data >> 0) & 0xff; + data_m = (in->mask >> 0) & 0xff; + + out->data = cust | data << 8; + out->mask = cust_m | data_m << 8; + + return 0; +} + +/* + * JVC decoder + * See also http://www.sbprojects.com/knowledge/ir/jvc.php + * http://support.jvc.com/consumer/support/documents/RemoteCodes.pdf + */ +static struct img_ir_decoder img_ir_jvc = { + .type = RC_BIT_JVC, + .control = { + .decoden = 1, + .code_type = IMG_IR_CODETYPE_PULSEDIST, + .decodend2 = 1, + }, + /* main timings */ + .unit = 527500, /* 527.5 us */ + .timings = { + /* leader symbol */ + .ldr = { + .pulse = { 16 /* 8.44 ms */ }, + .space = { 8 /* 4.22 ms */ }, + }, + /* 0 symbol */ + .s00 = { + .pulse = { 1 /* 527.5 us +-60 us */ }, + .space = { 1 /* 527.5 us */ }, + }, + /* 1 symbol */ + .s01 = { + .pulse = { 1 /* 527.5 us +-60 us */ }, + .space = { 3 /* 1.5825 ms +-40 us */ }, + }, + /* 0 symbol (no leader) */ + .s00 = { + .pulse = { 1 /* 527.5 us +-60 us */ }, + .space = { 1 /* 527.5 us */ }, + }, + /* 1 symbol (no leader) */ + .s01 = { + .pulse = { 1 /* 527.5 us +-60 us */ }, + .space = { 3 /* 1.5825 ms +-40 us */ }, + }, + /* free time */ + .ft = { + .minlen = 16, + .maxlen = 16, + .ft_min = 10, /* 5.275 ms */ + }, + }, + /* scancode logic */ + .scancode = img_ir_jvc_scancode, + .filter = img_ir_jvc_filter, +}; + +static int __init img_ir_jvc_init(void) +{ + return img_ir_register_decoder(&img_ir_jvc); +} +module_init(img_ir_jvc_init); + +static void __exit img_ir_jvc_exit(void) +{ + img_ir_unregister_decoder(&img_ir_jvc); +} +module_exit(img_ir_jvc_exit); + +MODULE_AUTHOR("Imagination Technologies Ltd."); +MODULE_DESCRIPTION("ImgTec IR JVC protocol support"); +MODULE_LICENSE("GPL"); -- 1.8.1.2 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 08/11] media: rc: img-ir: add Sony decoder module 2013-12-13 15:12 [PATCH 00/11] media: rc: ImgTec IR decoder driver James Hogan ` (6 preceding siblings ...) 2013-12-13 15:12 ` [PATCH 07/11] media: rc: img-ir: add JVC " James Hogan @ 2013-12-13 15:12 ` James Hogan 2013-12-13 15:12 ` [PATCH 09/11] media: rc: add Sharp infrared protocol James Hogan ` (2 subsequent siblings) 10 siblings, 0 replies; 21+ messages in thread From: James Hogan @ 2013-12-13 15:12 UTC (permalink / raw) To: Mauro Carvalho Chehab, linux-media; +Cc: James Hogan Add an img-ir module for decoding the Sony infrared protocol. 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-sony.c | 163 ++++++++++++++++++++++++++++++++++ 3 files changed, 171 insertions(+) create mode 100644 drivers/media/rc/img-ir/img-ir-sony.c diff --git a/drivers/media/rc/img-ir/Kconfig b/drivers/media/rc/img-ir/Kconfig index b7774a30509f..38505188df0e 100644 --- a/drivers/media/rc/img-ir/Kconfig +++ b/drivers/media/rc/img-ir/Kconfig @@ -38,3 +38,10 @@ config IR_IMG_JVC help Say Y or M here to enable support for the JVC protocol in the ImgTec infrared decoder block. + +config IR_IMG_SONY + tristate "Sony protocol support" + depends on IR_IMG && IR_IMG_HW + help + Say Y or M here to enable support for the Sony protocol in the ImgTec + infrared decoder block. diff --git a/drivers/media/rc/img-ir/Makefile b/drivers/media/rc/img-ir/Makefile index 1d9643801f02..f3e7cc4f32e4 100644 --- a/drivers/media/rc/img-ir/Makefile +++ b/drivers/media/rc/img-ir/Makefile @@ -6,3 +6,4 @@ img-ir-objs := $(img-ir-y) obj-$(CONFIG_IR_IMG) += img-ir.o obj-$(CONFIG_IR_IMG_NEC) += img-ir-nec.o obj-$(CONFIG_IR_IMG_JVC) += img-ir-jvc.o +obj-$(CONFIG_IR_IMG_SONY) += img-ir-sony.o diff --git a/drivers/media/rc/img-ir/img-ir-sony.c b/drivers/media/rc/img-ir/img-ir-sony.c new file mode 100644 index 000000000000..964ab242d384 --- /dev/null +++ b/drivers/media/rc/img-ir/img-ir-sony.c @@ -0,0 +1,163 @@ +/* + * ImgTec IR Decoder setup for Sony (SIRC) protocol. + * + * Copyright 2012-2013 Imagination Technologies Ltd. + */ + +#include <linux/module.h> + +#include "img-ir-hw.h" + +/* Convert Sony data to a scancode */ +static int img_ir_sony_scancode(int len, u64 raw, u64 protocols) +{ + unsigned int dev, subdev, func; + + switch (len) { + case 12: + if (!(protocols & RC_BIT_SONY12)) + goto invalid; + func = raw & 0x7f; /* first 7 bits */ + raw >>= 7; + dev = raw & 0x1f; /* next 5 bits */ + subdev = 0; + break; + case 15: + if (!(protocols & RC_BIT_SONY15)) + goto invalid; + func = raw & 0x7f; /* first 7 bits */ + raw >>= 7; + dev = raw & 0xff; /* next 8 bits */ + subdev = 0; + break; + case 20: + if (!(protocols & RC_BIT_SONY20)) + goto invalid; + func = raw & 0x7f; /* first 7 bits */ + raw >>= 7; + dev = raw & 0x1f; /* next 5 bits */ + raw >>= 5; + subdev = raw & 0xff; /* next 8 bits */ + break; + default: +invalid: + return IMG_IR_ERR_INVALID; + } + return dev << 16 | subdev << 8 | func; +} + +/* Convert NEC scancode to NEC data filter */ +static int img_ir_sony_filter(const struct img_ir_sc_filter *in, + struct img_ir_filter *out, u64 protocols) +{ + unsigned int dev, subdev, func; + unsigned int dev_m, subdev_m, func_m; + unsigned int len = 0; + + dev = (in->data >> 16) & 0xff; + dev_m = (in->mask >> 16) & 0xff; + subdev = (in->data >> 8) & 0xff; + subdev_m = (in->mask >> 8) & 0xff; + func = (in->data >> 0) & 0x7f; + func_m = (in->mask >> 0) & 0x7f; + + if (subdev & subdev_m) { + /* can't encode subdev and higher device bits */ + if (dev & dev_m & 0xe0) + return -EINVAL; + /* subdevice (extended) bits only in 20 bit encoding */ + if (!(protocols & RC_BIT_SONY20)) + return -EINVAL; + len = 20; + dev_m &= 0x1f; + } else if (dev & dev_m & 0xe0) { + /* upper device bits only in 15 bit encoding */ + if (!(protocols & RC_BIT_SONY15)) + return -EINVAL; + len = 15; + subdev_m = 0; + } else { + /* + * The hardware mask cannot distinguish high device bits and low + * extended bits, so logically AND those bits of the masks + * together. + */ + subdev_m &= (dev_m >> 5) | 0xf8; + dev_m &= 0x1f; + } + + /* ensure there aren't any bits straying between fields */ + dev &= dev_m; + subdev &= subdev_m; + + /* write the hardware filter */ + out->data = func | + dev << 7 | + subdev << 15; + out->mask = func_m | + dev_m << 7 | + subdev_m << 15; + + if (len) { + out->minlen = len; + out->maxlen = len; + } + return 0; +} + +/* + * Sony SIRC decoder + * See also http://www.sbprojects.com/knowledge/ir/sirc.php + * http://picprojects.org.uk/projects/sirc/sonysirc.pdf + */ +static struct img_ir_decoder img_ir_sony = { + .type = RC_BIT_SONY12 | RC_BIT_SONY15 | RC_BIT_SONY20, + .control = { + .decoden = 1, + .code_type = IMG_IR_CODETYPE_PULSELEN, + }, + /* main timings */ + .unit = 600000, /* 600 us */ + .timings = { + /* leader symbol */ + .ldr = { + .pulse = { 4 /* 2.4 ms */ }, + .space = { 1 /* 600 us */ }, + }, + /* 0 symbol */ + .s00 = { + .pulse = { 1 /* 600 us */ }, + .space = { 1 /* 600 us */ }, + }, + /* 1 symbol */ + .s01 = { + .pulse = { 2 /* 1.2 ms */ }, + .space = { 1 /* 600 us */ }, + }, + /* free time */ + .ft = { + .minlen = 12, + .maxlen = 20, + .ft_min = 10, /* 6 ms */ + }, + }, + /* scancode logic */ + .scancode = img_ir_sony_scancode, + .filter = img_ir_sony_filter, +}; + +static int __init img_ir_sony_init(void) +{ + return img_ir_register_decoder(&img_ir_sony); +} +module_init(img_ir_sony_init); + +static void __exit img_ir_sony_exit(void) +{ + img_ir_unregister_decoder(&img_ir_sony); +} +module_exit(img_ir_sony_exit); + +MODULE_AUTHOR("Imagination Technologies Ltd."); +MODULE_DESCRIPTION("ImgTec IR Sony SIRC protocol support"); +MODULE_LICENSE("GPL"); -- 1.8.1.2 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 09/11] media: rc: add Sharp infrared protocol 2013-12-13 15:12 [PATCH 00/11] media: rc: ImgTec IR decoder driver James Hogan ` (7 preceding siblings ...) 2013-12-13 15:12 ` [PATCH 08/11] media: rc: img-ir: add Sony " James Hogan @ 2013-12-13 15:12 ` James Hogan 2013-12-13 15:12 ` [PATCH 10/11] media: rc: img-ir: add Sharp decoder module James Hogan 2013-12-13 15:12 ` [PATCH 11/11] media: rc: img-ir: add Sanyo " James Hogan 10 siblings, 0 replies; 21+ messages in thread From: James Hogan @ 2013-12-13 15:12 UTC (permalink / raw) To: Mauro Carvalho Chehab, linux-media; +Cc: James Hogan Add Sharp infrared protocol constants RC_TYPE_SHARP and RC_BIT_SHARP. 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/rc-main.c | 1 + include/media/rc-map.h | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 46da365c9c84..a1b90ef45910 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -786,6 +786,7 @@ static struct { RC_BIT_SONY20, "sony" }, { RC_BIT_RC5_SZ, "rc-5-sz" }, { RC_BIT_SANYO, "sanyo" }, + { RC_BIT_SHARP, "sharp" }, { RC_BIT_MCE_KBD, "mce_kbd" }, { RC_BIT_LIRC, "lirc" }, }; diff --git a/include/media/rc-map.h b/include/media/rc-map.h index a20ed97d7d8a..b3224edf1b46 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -30,6 +30,7 @@ enum rc_type { RC_TYPE_RC6_6A_24 = 15, /* Philips RC6-6A-24 protocol */ RC_TYPE_RC6_6A_32 = 16, /* Philips RC6-6A-32 protocol */ RC_TYPE_RC6_MCE = 17, /* MCE (Philips RC6-6A-32 subtype) protocol */ + RC_TYPE_SHARP = 18, /* Sharp protocol */ }; #define RC_BIT_NONE 0 @@ -51,6 +52,7 @@ enum rc_type { #define RC_BIT_RC6_6A_24 (1 << RC_TYPE_RC6_6A_24) #define RC_BIT_RC6_6A_32 (1 << RC_TYPE_RC6_6A_32) #define RC_BIT_RC6_MCE (1 << RC_TYPE_RC6_MCE) +#define RC_BIT_SHARP (1 << RC_TYPE_SHARP) #define RC_BIT_ALL (RC_BIT_UNKNOWN | RC_BIT_OTHER | RC_BIT_LIRC | \ RC_BIT_RC5 | RC_BIT_RC5X | RC_BIT_RC5_SZ | \ @@ -58,7 +60,7 @@ enum rc_type { RC_BIT_SONY12 | RC_BIT_SONY15 | RC_BIT_SONY20 | \ RC_BIT_NEC | RC_BIT_SANYO | RC_BIT_MCE_KBD | \ RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 | RC_BIT_RC6_6A_24 | \ - RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE) + RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE | RC_BIT_SHARP) struct rc_map_table { u32 scancode; -- 1.8.1.2 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 10/11] media: rc: img-ir: add Sharp decoder module 2013-12-13 15:12 [PATCH 00/11] media: rc: ImgTec IR decoder driver James Hogan ` (8 preceding siblings ...) 2013-12-13 15:12 ` [PATCH 09/11] media: rc: add Sharp infrared protocol James Hogan @ 2013-12-13 15:12 ` James Hogan 2013-12-22 14:01 ` Mauro Carvalho Chehab 2013-12-13 15:12 ` [PATCH 11/11] media: rc: img-ir: add Sanyo " James Hogan 10 siblings, 1 reply; 21+ messages in thread From: James Hogan @ 2013-12-13 15:12 UTC (permalink / raw) To: Mauro Carvalho Chehab, linux-media; +Cc: James Hogan Add an img-ir module for decoding the Sharp infrared protocol. 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-sharp.c | 115 +++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+) create mode 100644 drivers/media/rc/img-ir/img-ir-sharp.c diff --git a/drivers/media/rc/img-ir/Kconfig b/drivers/media/rc/img-ir/Kconfig index 38505188df0e..24e0966a3220 100644 --- a/drivers/media/rc/img-ir/Kconfig +++ b/drivers/media/rc/img-ir/Kconfig @@ -45,3 +45,10 @@ config IR_IMG_SONY help Say Y or M here to enable support for the Sony protocol in the ImgTec infrared decoder block. + +config IR_IMG_SHARP + tristate "Sharp protocol support" + depends on IR_IMG && IR_IMG_HW + help + Say Y or M here to enable support for the Sharp protocol in the + ImgTec infrared decoder block. diff --git a/drivers/media/rc/img-ir/Makefile b/drivers/media/rc/img-ir/Makefile index f3e7cc4f32e4..3c3ab4f1a9f1 100644 --- a/drivers/media/rc/img-ir/Makefile +++ b/drivers/media/rc/img-ir/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_IR_IMG) += img-ir.o obj-$(CONFIG_IR_IMG_NEC) += img-ir-nec.o obj-$(CONFIG_IR_IMG_JVC) += img-ir-jvc.o obj-$(CONFIG_IR_IMG_SONY) += img-ir-sony.o +obj-$(CONFIG_IR_IMG_SHARP) += img-ir-sharp.o diff --git a/drivers/media/rc/img-ir/img-ir-sharp.c b/drivers/media/rc/img-ir/img-ir-sharp.c new file mode 100644 index 000000000000..4d70abc088b4 --- /dev/null +++ b/drivers/media/rc/img-ir/img-ir-sharp.c @@ -0,0 +1,115 @@ +/* + * ImgTec IR Decoder setup for Sharp protocol. + * + * Copyright 2012-2013 Imagination Technologies Ltd. + */ + +#include <linux/module.h> + +#include "img-ir-hw.h" + +/* Convert Sharp data to a scancode */ +static int img_ir_sharp_scancode(int len, u64 raw, u64 protocols) +{ + unsigned int addr, cmd, exp, chk; + + if (len != 15) + return IMG_IR_ERR_INVALID; + + addr = (raw >> 0) & 0x1f; + cmd = (raw >> 5) & 0xff; + exp = (raw >> 13) & 0x1; + chk = (raw >> 14) & 0x1; + + /* validate data */ + if (!exp) + return IMG_IR_ERR_INVALID; + if (chk) + /* probably the second half of the message */ + return IMG_IR_ERR_INVALID; + + return addr << 8 | cmd; +} + +/* Convert Sharp scancode to Sharp data filter */ +static int img_ir_sharp_filter(const struct img_ir_sc_filter *in, + struct img_ir_filter *out, u64 protocols) +{ + unsigned int addr, cmd, exp = 0, chk = 0; + unsigned int addr_m, cmd_m, exp_m = 0, chk_m = 0; + + addr = (in->data >> 8) & 0x1f; + addr_m = (in->mask >> 8) & 0x1f; + cmd = (in->data >> 0) & 0xff; + cmd_m = (in->mask >> 0) & 0xff; + if (cmd_m) { + /* if filtering commands, we can only match the first part */ + exp = 1; + exp_m = 1; + chk = 0; + chk_m = 1; + } + + out->data = addr | + cmd << 5 | + exp << 13 | + chk << 14; + out->mask = addr_m | + cmd_m << 5 | + exp_m << 13 | + chk_m << 14; + + return 0; +} + +/* + * Sharp decoder + * See also http://www.sbprojects.com/knowledge/ir/sharp.php + */ +static struct img_ir_decoder img_ir_sharp = { + .type = RC_BIT_SHARP, + .control = { + .decoden = 0, + .decodend2 = 1, + .code_type = IMG_IR_CODETYPE_PULSEDIST, + .d1validsel = 1, + }, + /* main timings */ + .timings = { + /* 0 symbol */ + .s10 = { + .pulse = { 320 /* 320 us */ }, + .space = { 680 /* 1 ms period */ }, + }, + /* 1 symbol */ + .s11 = { + .pulse = { 320 /* 230 us */ }, + .space = { 1680 /* 2 ms period */ }, + }, + /* free time */ + .ft = { + .minlen = 15, + .maxlen = 15, + .ft_min = 5000, /* 5 ms */ + }, + }, + /* scancode logic */ + .scancode = img_ir_sharp_scancode, + .filter = img_ir_sharp_filter, +}; + +static int __init img_ir_sharp_init(void) +{ + return img_ir_register_decoder(&img_ir_sharp); +} +module_init(img_ir_sharp_init); + +static void __exit img_ir_sharp_exit(void) +{ + img_ir_unregister_decoder(&img_ir_sharp); +} +module_exit(img_ir_sharp_exit); + +MODULE_AUTHOR("Imagination Technologies Ltd."); +MODULE_DESCRIPTION("ImgTec IR Sharp protocol support"); +MODULE_LICENSE("GPL"); -- 1.8.1.2 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH 10/11] media: rc: img-ir: add Sharp decoder module 2013-12-13 15:12 ` [PATCH 10/11] media: rc: img-ir: add Sharp decoder module James Hogan @ 2013-12-22 14:01 ` Mauro Carvalho Chehab 2013-12-23 12:04 ` James Hogan 0 siblings, 1 reply; 21+ messages in thread From: Mauro Carvalho Chehab @ 2013-12-22 14:01 UTC (permalink / raw) To: James Hogan; +Cc: linux-media Em Fri, 13 Dec 2013 15:12:58 +0000 James Hogan <james.hogan@imgtec.com> escreveu: > Add an img-ir module for decoding the Sharp infrared protocol. Patches 5 and 7-11 look OK to me. While not required for patchset acceptance, it would be great if you could also add an IR raw decoder for this protocol, specially if you can also test it. > > 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-sharp.c | 115 +++++++++++++++++++++++++++++++++ > 3 files changed, 123 insertions(+) > create mode 100644 drivers/media/rc/img-ir/img-ir-sharp.c > > diff --git a/drivers/media/rc/img-ir/Kconfig b/drivers/media/rc/img-ir/Kconfig > index 38505188df0e..24e0966a3220 100644 > --- a/drivers/media/rc/img-ir/Kconfig > +++ b/drivers/media/rc/img-ir/Kconfig > @@ -45,3 +45,10 @@ config IR_IMG_SONY > help > Say Y or M here to enable support for the Sony protocol in the ImgTec > infrared decoder block. > + > +config IR_IMG_SHARP > + tristate "Sharp protocol support" > + depends on IR_IMG && IR_IMG_HW > + help > + Say Y or M here to enable support for the Sharp protocol in the > + ImgTec infrared decoder block. > diff --git a/drivers/media/rc/img-ir/Makefile b/drivers/media/rc/img-ir/Makefile > index f3e7cc4f32e4..3c3ab4f1a9f1 100644 > --- a/drivers/media/rc/img-ir/Makefile > +++ b/drivers/media/rc/img-ir/Makefile > @@ -7,3 +7,4 @@ obj-$(CONFIG_IR_IMG) += img-ir.o > obj-$(CONFIG_IR_IMG_NEC) += img-ir-nec.o > obj-$(CONFIG_IR_IMG_JVC) += img-ir-jvc.o > obj-$(CONFIG_IR_IMG_SONY) += img-ir-sony.o > +obj-$(CONFIG_IR_IMG_SHARP) += img-ir-sharp.o > diff --git a/drivers/media/rc/img-ir/img-ir-sharp.c b/drivers/media/rc/img-ir/img-ir-sharp.c > new file mode 100644 > index 000000000000..4d70abc088b4 > --- /dev/null > +++ b/drivers/media/rc/img-ir/img-ir-sharp.c > @@ -0,0 +1,115 @@ > +/* > + * ImgTec IR Decoder setup for Sharp protocol. > + * > + * Copyright 2012-2013 Imagination Technologies Ltd. > + */ > + > +#include <linux/module.h> > + > +#include "img-ir-hw.h" > + > +/* Convert Sharp data to a scancode */ > +static int img_ir_sharp_scancode(int len, u64 raw, u64 protocols) > +{ > + unsigned int addr, cmd, exp, chk; > + > + if (len != 15) > + return IMG_IR_ERR_INVALID; > + > + addr = (raw >> 0) & 0x1f; > + cmd = (raw >> 5) & 0xff; > + exp = (raw >> 13) & 0x1; > + chk = (raw >> 14) & 0x1; > + > + /* validate data */ > + if (!exp) > + return IMG_IR_ERR_INVALID; > + if (chk) > + /* probably the second half of the message */ > + return IMG_IR_ERR_INVALID; > + > + return addr << 8 | cmd; > +} > + > +/* Convert Sharp scancode to Sharp data filter */ > +static int img_ir_sharp_filter(const struct img_ir_sc_filter *in, > + struct img_ir_filter *out, u64 protocols) > +{ > + unsigned int addr, cmd, exp = 0, chk = 0; > + unsigned int addr_m, cmd_m, exp_m = 0, chk_m = 0; > + > + addr = (in->data >> 8) & 0x1f; > + addr_m = (in->mask >> 8) & 0x1f; > + cmd = (in->data >> 0) & 0xff; > + cmd_m = (in->mask >> 0) & 0xff; > + if (cmd_m) { > + /* if filtering commands, we can only match the first part */ > + exp = 1; > + exp_m = 1; > + chk = 0; > + chk_m = 1; > + } > + > + out->data = addr | > + cmd << 5 | > + exp << 13 | > + chk << 14; > + out->mask = addr_m | > + cmd_m << 5 | > + exp_m << 13 | > + chk_m << 14; > + > + return 0; > +} > + > +/* > + * Sharp decoder > + * See also http://www.sbprojects.com/knowledge/ir/sharp.php > + */ > +static struct img_ir_decoder img_ir_sharp = { > + .type = RC_BIT_SHARP, > + .control = { > + .decoden = 0, > + .decodend2 = 1, > + .code_type = IMG_IR_CODETYPE_PULSEDIST, > + .d1validsel = 1, > + }, > + /* main timings */ > + .timings = { > + /* 0 symbol */ > + .s10 = { > + .pulse = { 320 /* 320 us */ }, > + .space = { 680 /* 1 ms period */ }, > + }, > + /* 1 symbol */ > + .s11 = { > + .pulse = { 320 /* 230 us */ }, > + .space = { 1680 /* 2 ms period */ }, > + }, > + /* free time */ > + .ft = { > + .minlen = 15, > + .maxlen = 15, > + .ft_min = 5000, /* 5 ms */ > + }, > + }, > + /* scancode logic */ > + .scancode = img_ir_sharp_scancode, > + .filter = img_ir_sharp_filter, > +}; > + > +static int __init img_ir_sharp_init(void) > +{ > + return img_ir_register_decoder(&img_ir_sharp); > +} > +module_init(img_ir_sharp_init); > + > +static void __exit img_ir_sharp_exit(void) > +{ > + img_ir_unregister_decoder(&img_ir_sharp); > +} > +module_exit(img_ir_sharp_exit); > + > +MODULE_AUTHOR("Imagination Technologies Ltd."); > +MODULE_DESCRIPTION("ImgTec IR Sharp protocol support"); > +MODULE_LICENSE("GPL"); -- Cheers, Mauro ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 10/11] media: rc: img-ir: add Sharp decoder module 2013-12-22 14:01 ` Mauro Carvalho Chehab @ 2013-12-23 12:04 ` James Hogan 0 siblings, 0 replies; 21+ messages in thread From: James Hogan @ 2013-12-23 12:04 UTC (permalink / raw) To: Mauro Carvalho Chehab; +Cc: linux-media On 22/12/13 14:01, Mauro Carvalho Chehab wrote: > Em Fri, 13 Dec 2013 15:12:58 +0000 > James Hogan <james.hogan@imgtec.com> escreveu: > >> Add an img-ir module for decoding the Sharp infrared protocol. > > Patches 5 and 7-11 look OK to me. Thanks very much for reviewing. > > While not required for patchset acceptance, it would be great if you could > also add an IR raw decoder for this protocol, specially if you can also > test it. Yes, this had occurred to me too, but I haven't got around to it yet. I've found several codes my universal remote control can use which appear to be the sharp protocol so I am able to test it a bit, but don't have any original sharp remotes to hand. Cheers James ^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 11/11] media: rc: img-ir: add Sanyo decoder module 2013-12-13 15:12 [PATCH 00/11] media: rc: ImgTec IR decoder driver James Hogan ` (9 preceding siblings ...) 2013-12-13 15:12 ` [PATCH 10/11] media: rc: img-ir: add Sharp decoder module James Hogan @ 2013-12-13 15:12 ` James Hogan 10 siblings, 0 replies; 21+ messages in thread From: James Hogan @ 2013-12-13 15:12 UTC (permalink / raw) To: Mauro Carvalho Chehab, linux-media; +Cc: James Hogan Add an img-ir module for decoding the Sanyo infrared protocol. 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-sanyo.c | 139 +++++++++++++++++++++++++++++++++ 3 files changed, 147 insertions(+) create mode 100644 drivers/media/rc/img-ir/img-ir-sanyo.c diff --git a/drivers/media/rc/img-ir/Kconfig b/drivers/media/rc/img-ir/Kconfig index 24e0966a3220..8c035b7c34e8 100644 --- a/drivers/media/rc/img-ir/Kconfig +++ b/drivers/media/rc/img-ir/Kconfig @@ -52,3 +52,10 @@ config IR_IMG_SHARP help Say Y or M here to enable support for the Sharp protocol in the ImgTec infrared decoder block. + +config IR_IMG_SANYO + tristate "Sanyo protocol support" + depends on IR_IMG && IR_IMG_HW + help + Say Y or M here to enable support for the Sanyo protocol (used by + Sanyo, Aiwa, Chinon remotes) in the ImgTec infrared decoder block. diff --git a/drivers/media/rc/img-ir/Makefile b/drivers/media/rc/img-ir/Makefile index 3c3ab4f1a9f1..4f1e4305870d 100644 --- a/drivers/media/rc/img-ir/Makefile +++ b/drivers/media/rc/img-ir/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_IR_IMG_NEC) += img-ir-nec.o obj-$(CONFIG_IR_IMG_JVC) += img-ir-jvc.o obj-$(CONFIG_IR_IMG_SONY) += img-ir-sony.o obj-$(CONFIG_IR_IMG_SHARP) += img-ir-sharp.o +obj-$(CONFIG_IR_IMG_SANYO) += img-ir-sanyo.o diff --git a/drivers/media/rc/img-ir/img-ir-sanyo.c b/drivers/media/rc/img-ir/img-ir-sanyo.c new file mode 100644 index 000000000000..bfd44b4fd468 --- /dev/null +++ b/drivers/media/rc/img-ir/img-ir-sanyo.c @@ -0,0 +1,139 @@ +/* + * ImgTec IR Decoder setup for Sanyo protocol. + * + * Copyright 2012-2013 Imagination Technologies Ltd. + * + * From ir-sanyo-decoder.c: + * + * This protocol uses the NEC protocol timings. However, data is formatted as: + * 13 bits Custom Code + * 13 bits NOT(Custom Code) + * 8 bits Key data + * 8 bits NOT(Key data) + * + * According with LIRC, this protocol is used on Sanyo, Aiwa and Chinon + * Information for this protocol is available at the Sanyo LC7461 datasheet. + */ + +#include <linux/module.h> + +#include "img-ir-hw.h" + +/* Convert Sanyo data to a scancode */ +static int img_ir_sanyo_scancode(int len, u64 raw, u64 protocols) +{ + unsigned int addr, addr_inv, data, data_inv; + /* a repeat code has no data */ + if (!len) + return IMG_IR_REPEATCODE; + if (len != 42) + return IMG_IR_ERR_INVALID; + addr = (raw >> 0) & 0x1fff; + addr_inv = (raw >> 13) & 0x1fff; + data = (raw >> 26) & 0xff; + data_inv = (raw >> 34) & 0xff; + /* Validate data */ + if ((data_inv ^ data) != 0xff) + return IMG_IR_ERR_INVALID; + /* Validate address */ + if ((addr_inv ^ addr) != 0x1fff) + return IMG_IR_ERR_INVALID; + + /* Normal Sanyo */ + return addr << 8 | data; +} + +/* Convert Sanyo scancode to Sanyo data filter */ +static int img_ir_sanyo_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, data_m; + + data = in->data & 0xff; + data_m = in->mask & 0xff; + data_inv = data ^ 0xff; + + if (in->data & 0xff700000) + return -EINVAL; + + addr = (in->data >> 8) & 0x1fff; + addr_m = (in->mask >> 8) & 0x1fff; + addr_inv = addr ^ 0x1fff; + + out->data = (u64)data_inv << 34 | + (u64)data << 26 | + addr_inv << 13 | + addr; + out->mask = (u64)data_m << 34 | + (u64)data_m << 26 | + addr_m << 13 | + addr_m; + return 0; +} + +/* Sanyo decoder */ +static struct img_ir_decoder img_ir_sanyo = { + .type = RC_BIT_SANYO, + .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 = 42, + .maxlen = 42, + .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_sanyo_scancode, + .filter = img_ir_sanyo_filter, +}; + +static int __init img_ir_sanyo_init(void) +{ + return img_ir_register_decoder(&img_ir_sanyo); +} +module_init(img_ir_sanyo_init); + +static void __exit img_ir_sanyo_exit(void) +{ + img_ir_unregister_decoder(&img_ir_sanyo); +} +module_exit(img_ir_sanyo_exit); + +MODULE_AUTHOR("Imagination Technologies Ltd."); +MODULE_DESCRIPTION("ImgTec IR Sanyo protocol support"); +MODULE_LICENSE("GPL"); -- 1.8.1.2 ^ permalink raw reply related [flat|nested] 21+ messages in thread
end of thread, other threads:[~2013-12-23 13:18 UTC | newest] Thread overview: 21+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2013-12-13 15:12 [PATCH 00/11] media: rc: ImgTec IR decoder driver James Hogan 2013-12-13 15:12 ` [PATCH 01/11] dt: binding: add binding for ImgTec IR block James Hogan 2013-12-22 10:56 ` Mauro Carvalho Chehab 2013-12-22 12:48 ` Tomasz Figa 2013-12-23 10:41 ` James Hogan 2013-12-13 15:12 ` [PATCH 02/11] media: rc: img-ir: add base driver James Hogan 2013-12-13 15:12 ` [PATCH 03/11] media: rc: img-ir: add raw driver James Hogan 2013-12-13 15:12 ` [PATCH 04/11] media: rc: img-ir: add hardware decoder driver James Hogan 2013-12-22 13:40 ` Mauro Carvalho Chehab 2013-12-23 13:17 ` James Hogan 2013-12-13 15:12 ` [PATCH 05/11] media: rc: img-ir: add to build James Hogan 2013-12-13 15:12 ` [PATCH 06/11] media: rc: img-ir: add NEC decoder module James Hogan 2013-12-22 13:49 ` Mauro Carvalho Chehab 2013-12-23 11:30 ` James Hogan 2013-12-13 15:12 ` [PATCH 07/11] media: rc: img-ir: add JVC " James Hogan 2013-12-13 15:12 ` [PATCH 08/11] media: rc: img-ir: add Sony " James Hogan 2013-12-13 15:12 ` [PATCH 09/11] media: rc: add Sharp infrared protocol James Hogan 2013-12-13 15:12 ` [PATCH 10/11] media: rc: img-ir: add Sharp decoder module James Hogan 2013-12-22 14:01 ` Mauro Carvalho Chehab 2013-12-23 12:04 ` James Hogan 2013-12-13 15:12 ` [PATCH 11/11] media: rc: img-ir: add Sanyo " James Hogan
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox