aboutsummaryrefslogtreecommitdiff
path: root/drivers/media/IR
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2010-10-28 09:35:11 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2010-10-28 09:35:11 -0700
commit0851668fdd97e526b2a41f794b785c204dd3d3e0 (patch)
tree4ef7c20a8be8393006c6fe9627eb29dd30877d61 /drivers/media/IR
parent00ebb6382b8d9c7c15b5f8ad230670d8161d38dd (diff)
parent7655e594945289b418af39f6669fea4666a7b520 (diff)
Merge branch 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6
* 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6: (505 commits) [media] af9015: Fix max I2C message size when used with tda18271 [media] IR: initialize ir_raw_event in few more drivers [media] Guard a divide in v4l1 compat layer [media] imon: fix nomouse modprobe option [media] imon: remove redundant change_protocol call [media] imon: fix my egregious brown paper bag w/rdev/idev split [media] cafe_ccic: Configure ov7670 correctly [media] ov7670: allow configuration of image size, clock speed, and I/O method [media] af9015: support for DigitalNow TinyTwin v3 [1f4d:9016] [media] af9015: map DigitalNow TinyTwin v2 remote [media] DigitalNow TinyTwin remote controller [media] af9015: RC fixes and improvements videodev2.h.xml: Update to reflect the latest changes at videodev2.h [media] v4l: document new Bayer and monochrome pixel formats [media] DocBook/v4l: Add missing formats used on gspca cpia1 and sn9c2028 [media] firedtv: add parameter to fake ca_system_ids in CA_INFO [media] tm6000: fix a macro coding style issue tm6000: Remove some ugly debug code [media] Nova-S-Plus audio line input [media] [RFC,1/1] V4L2: Use new CAP bits in existing RDS capable drivers ...
Diffstat (limited to 'drivers/media/IR')
-rw-r--r--drivers/media/IR/Kconfig39
-rw-r--r--drivers/media/IR/Makefile2
-rw-r--r--drivers/media/IR/ene_ir.c1046
-rw-r--r--drivers/media/IR/ene_ir.h275
-rw-r--r--drivers/media/IR/imon.c690
-rw-r--r--drivers/media/IR/ir-core-priv.h22
-rw-r--r--drivers/media/IR/ir-jvc-decoder.c5
-rw-r--r--drivers/media/IR/ir-keytable.c7
-rw-r--r--drivers/media/IR/ir-lirc-codec.c135
-rw-r--r--drivers/media/IR/ir-nec-decoder.c5
-rw-r--r--drivers/media/IR/ir-raw-event.c81
-rw-r--r--drivers/media/IR/ir-rc5-decoder.c5
-rw-r--r--drivers/media/IR/ir-rc5-sz-decoder.c154
-rw-r--r--drivers/media/IR/ir-rc6-decoder.c5
-rw-r--r--drivers/media/IR/ir-sony-decoder.c5
-rw-r--r--drivers/media/IR/ir-sysfs.c37
-rw-r--r--drivers/media/IR/keymaps/Makefile16
-rw-r--r--drivers/media/IR/keymaps/rc-alink-dtu-m.c68
-rw-r--r--drivers/media/IR/keymaps/rc-anysee.c93
-rw-r--r--drivers/media/IR/keymaps/rc-asus-pc39.c80
-rw-r--r--drivers/media/IR/keymaps/rc-avermedia-rm-ks.c79
-rw-r--r--drivers/media/IR/keymaps/rc-azurewave-ad-tu700.c102
-rw-r--r--drivers/media/IR/keymaps/rc-digitalnow-tinytwin.c98
-rw-r--r--drivers/media/IR/keymaps/rc-digittrade.c82
-rw-r--r--drivers/media/IR/keymaps/rc-leadtek-y04g0051.c99
-rw-r--r--drivers/media/IR/keymaps/rc-lme2510.c68
-rw-r--r--drivers/media/IR/keymaps/rc-msi-digivox-ii.c67
-rw-r--r--drivers/media/IR/keymaps/rc-msi-digivox-iii.c85
-rw-r--r--drivers/media/IR/keymaps/rc-rc5-streamzap.c81
-rw-r--r--drivers/media/IR/keymaps/rc-rc6-mce.c88
-rw-r--r--drivers/media/IR/keymaps/rc-streamzap.c82
-rw-r--r--drivers/media/IR/keymaps/rc-terratec-slim.c79
-rw-r--r--drivers/media/IR/keymaps/rc-total-media-in-hand.c85
-rw-r--r--drivers/media/IR/keymaps/rc-trekstor.c80
-rw-r--r--drivers/media/IR/keymaps/rc-twinhan1027.c87
-rw-r--r--drivers/media/IR/lirc_dev.c133
-rw-r--r--drivers/media/IR/mceusb.c475
-rw-r--r--drivers/media/IR/nuvoton-cir.c1246
-rw-r--r--drivers/media/IR/nuvoton-cir.h408
-rw-r--r--drivers/media/IR/streamzap.c376
40 files changed, 5066 insertions, 1604 deletions
diff --git a/drivers/media/IR/Kconfig b/drivers/media/IR/Kconfig
index 490c57cc4cf..aa4163eb7a8 100644
--- a/drivers/media/IR/Kconfig
+++ b/drivers/media/IR/Kconfig
@@ -79,6 +79,18 @@ config IR_SONY_DECODER
Enable this option if you have an infrared remote control which
uses the Sony protocol, and you need software decoding support.
+config IR_RC5_SZ_DECODER
+ tristate "Enable IR raw decoder for the RC-5 (streamzap) protocol"
+ depends on IR_CORE
+ select BITREVERSE
+ default y
+
+ ---help---
+ Enable this option if you have IR with RC-5 (streamzap) protocol,
+ and if the IR is decoded in software. (The Streamzap PC Remote
+ uses an IR protocol that is almost standard RC-5, but not quite,
+ as it uses an additional bit).
+
config IR_LIRC_CODEC
tristate "Enable IR to LIRC bridge"
depends on IR_CORE
@@ -89,6 +101,20 @@ config IR_LIRC_CODEC
Enable this option to pass raw IR to and from userspace via
the LIRC interface.
+config IR_ENE
+ tristate "ENE eHome Receiver/Transceiver (pnp id: ENE0100/ENE02xxx)"
+ depends on PNP
+ depends on IR_CORE
+ ---help---
+ Say Y here to enable support for integrated infrared receiver
+ /transceiver made by ENE.
+
+ You can see if you have it by looking at lspnp output.
+ Output should include ENE0100 ENE0200 or something similar.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ene_ir.
+
config IR_IMON
tristate "SoundGraph iMON Receiver and Display"
depends on USB_ARCH_HAS_HCD
@@ -113,19 +139,18 @@ config IR_MCEUSB
To compile this driver as a module, choose M here: the
module will be called mceusb.
-config IR_ENE
- tristate "ENE eHome Receiver/Transciever (pnp id: ENE0100/ENE02xxx)"
+config IR_NUVOTON
+ tristate "Nuvoton w836x7hg Consumer Infrared Transceiver"
depends on PNP
depends on IR_CORE
---help---
Say Y here to enable support for integrated infrared receiver
- /transciever made by ENE.
-
- You can see if you have it by looking at lspnp output.
- Output should include ENE0100 ENE0200 or something similiar.
+ /transciever made by Nuvoton (formerly Winbond). This chip is
+ found in the ASRock ION 330HT, as well as assorted Intel
+ DP55-series motherboards (and of course, possibly others).
To compile this driver as a module, choose M here: the
- module will be called ene_ir.
+ module will be called nuvoton-cir.
config IR_STREAMZAP
tristate "Streamzap PC Remote IR Receiver"
diff --git a/drivers/media/IR/Makefile b/drivers/media/IR/Makefile
index 53676838fe9..f9574adab82 100644
--- a/drivers/media/IR/Makefile
+++ b/drivers/media/IR/Makefile
@@ -11,10 +11,12 @@ obj-$(CONFIG_IR_RC5_DECODER) += ir-rc5-decoder.o
obj-$(CONFIG_IR_RC6_DECODER) += ir-rc6-decoder.o
obj-$(CONFIG_IR_JVC_DECODER) += ir-jvc-decoder.o
obj-$(CONFIG_IR_SONY_DECODER) += ir-sony-decoder.o
+obj-$(CONFIG_IR_RC5_SZ_DECODER) += ir-rc5-sz-decoder.o
obj-$(CONFIG_IR_LIRC_CODEC) += ir-lirc-codec.o
# stand-alone IR receivers/transmitters
obj-$(CONFIG_IR_IMON) += imon.o
obj-$(CONFIG_IR_MCEUSB) += mceusb.o
+obj-$(CONFIG_IR_NUVOTON) += nuvoton-cir.o
obj-$(CONFIG_IR_ENE) += ene_ir.o
obj-$(CONFIG_IR_STREAMZAP) += streamzap.o
diff --git a/drivers/media/IR/ene_ir.c b/drivers/media/IR/ene_ir.c
index 5447750f5e3..7637babcd26 100644
--- a/drivers/media/IR/ene_ir.c
+++ b/drivers/media/IR/ene_ir.c
@@ -1,5 +1,5 @@
/*
- * driver for ENE KB3926 B/C/D CIR (pnp id: ENE0XXX)
+ * driver for ENE KB3926 B/C/D/E/F CIR (pnp id: ENE0XXX)
*
* Copyright (C) 2010 Maxim Levitsky <maximlevitsky@gmail.com>
*
@@ -17,6 +17,17 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
+ *
+ * Special thanks to:
+ * Sami R. <maesesami@gmail.com> for lot of help in debugging and therefore
+ * bringing to life support for transmission & learning mode.
+ *
+ * Charlie Andrews <charliethepilot@googlemail.com> for lots of help in
+ * bringing up the support of new firmware buffer that is popular
+ * on latest notebooks
+ *
+ * ENE for partial device documentation
+ *
*/
#include <linux/kernel.h>
@@ -31,51 +42,59 @@
#include <media/ir-common.h>
#include "ene_ir.h"
-
-static int sample_period = -1;
-static int enable_idle = 1;
-static int input = 1;
+static int sample_period;
+static bool learning_mode_force;
static int debug;
-static int txsim;
+static bool txsim;
-static int ene_irq_status(struct ene_device *dev);
+static void ene_set_reg_addr(struct ene_device *dev, u16 reg)
+{
+ outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
+ outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
+}
/* read a hardware register */
-static u8 ene_hw_read_reg(struct ene_device *dev, u16 reg)
+static u8 ene_read_reg(struct ene_device *dev, u16 reg)
{
u8 retval;
- outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
- outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
+ ene_set_reg_addr(dev, reg);
retval = inb(dev->hw_io + ENE_IO);
-
- ene_dbg_verbose("reg %04x == %02x", reg, retval);
+ dbg_regs("reg %04x == %02x", reg, retval);
return retval;
}
/* write a hardware register */
-static void ene_hw_write_reg(struct ene_device *dev, u16 reg, u8 value)
+static void ene_write_reg(struct ene_device *dev, u16 reg, u8 value)
{
- outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
- outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
+ dbg_regs("reg %04x <- %02x", reg, value);
+ ene_set_reg_addr(dev, reg);
outb(value, dev->hw_io + ENE_IO);
-
- ene_dbg_verbose("reg %04x <- %02x", reg, value);
}
-/* change specific bits in hardware register */
-static void ene_hw_write_reg_mask(struct ene_device *dev,
- u16 reg, u8 value, u8 mask)
+/* Set bits in hardware register */
+static void ene_set_reg_mask(struct ene_device *dev, u16 reg, u8 mask)
{
- u8 regvalue;
-
- outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
- outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
+ dbg_regs("reg %04x |= %02x", reg, mask);
+ ene_set_reg_addr(dev, reg);
+ outb(inb(dev->hw_io + ENE_IO) | mask, dev->hw_io + ENE_IO);
+}
- regvalue = inb(dev->hw_io + ENE_IO) & ~mask;
- regvalue |= (value & mask);
- outb(regvalue, dev->hw_io + ENE_IO);
+/* Clear bits in hardware register */
+static void ene_clear_reg_mask(struct ene_device *dev, u16 reg, u8 mask)
+{
+ dbg_regs("reg %04x &= ~%02x ", reg, mask);
+ ene_set_reg_addr(dev, reg);
+ outb(inb(dev->hw_io + ENE_IO) & ~mask, dev->hw_io + ENE_IO);
+}
- ene_dbg_verbose("reg %04x <- %02x (mask=%02x)", reg, value, mask);
+/* A helper to set/clear a bit in register according to boolean variable */
+static void ene_set_clear_reg_mask(struct ene_device *dev, u16 reg, u8 mask,
+ bool set)
+{
+ if (set)
+ ene_set_reg_mask(dev, reg, mask);
+ else
+ ene_clear_reg_mask(dev, reg, mask);
}
/* detect hardware features */
@@ -83,194 +102,378 @@ static int ene_hw_detect(struct ene_device *dev)
{
u8 chip_major, chip_minor;
u8 hw_revision, old_ver;
- u8 tmp;
- u8 fw_capabilities;
- int pll_freq;
+ u8 fw_reg2, fw_reg1;
- tmp = ene_hw_read_reg(dev, ENE_HW_UNK);
- ene_hw_write_reg(dev, ENE_HW_UNK, tmp & ~ENE_HW_UNK_CLR);
+ ene_clear_reg_mask(dev, ENE_ECSTS, ENE_ECSTS_RSRVD);
+ chip_major = ene_read_reg(dev, ENE_ECVER_MAJOR);
+ chip_minor = ene_read_reg(dev, ENE_ECVER_MINOR);
+ ene_set_reg_mask(dev, ENE_ECSTS, ENE_ECSTS_RSRVD);
- chip_major = ene_hw_read_reg(dev, ENE_HW_VER_MAJOR);
- chip_minor = ene_hw_read_reg(dev, ENE_HW_VER_MINOR);
+ hw_revision = ene_read_reg(dev, ENE_ECHV);
+ old_ver = ene_read_reg(dev, ENE_HW_VER_OLD);
- ene_hw_write_reg(dev, ENE_HW_UNK, tmp);
- hw_revision = ene_hw_read_reg(dev, ENE_HW_VERSION);
- old_ver = ene_hw_read_reg(dev, ENE_HW_VER_OLD);
+ dev->pll_freq = (ene_read_reg(dev, ENE_PLLFRH) << 4) +
+ (ene_read_reg(dev, ENE_PLLFRL) >> 4);
- pll_freq = (ene_hw_read_reg(dev, ENE_PLLFRH) << 4) +
- (ene_hw_read_reg(dev, ENE_PLLFRL) >> 4);
-
- if (pll_freq != 1000)
- dev->rx_period_adjust = 4;
- else
- dev->rx_period_adjust = 2;
-
-
- ene_printk(KERN_NOTICE, "PLL freq = %d\n", pll_freq);
+ if (sample_period != ENE_DEFAULT_SAMPLE_PERIOD)
+ dev->rx_period_adjust =
+ dev->pll_freq == ENE_DEFAULT_PLL_FREQ ? 2 : 4;
if (hw_revision == 0xFF) {
-
- ene_printk(KERN_WARNING, "device seems to be disabled\n");
- ene_printk(KERN_WARNING,
- "send a mail to lirc-list@lists.sourceforge.net\n");
- ene_printk(KERN_WARNING, "please attach output of acpidump\n");
+ ene_warn("device seems to be disabled");
+ ene_warn("send a mail to lirc-list@lists.sourceforge.net");
+ ene_warn("please attach output of acpidump and dmidecode");
return -ENODEV;
}
+ ene_notice("chip is 0x%02x%02x - kbver = 0x%02x, rev = 0x%02x",
+ chip_major, chip_minor, old_ver, hw_revision);
+
+ ene_notice("PLL freq = %d", dev->pll_freq);
+
if (chip_major == 0x33) {
- ene_printk(KERN_WARNING, "chips 0x33xx aren't supported\n");
+ ene_warn("chips 0x33xx aren't supported");
return -ENODEV;
}
if (chip_major == 0x39 && chip_minor == 0x26 && hw_revision == 0xC0) {
dev->hw_revision = ENE_HW_C;
+ ene_notice("KB3926C detected");
} else if (old_ver == 0x24 && hw_revision == 0xC0) {
dev->hw_revision = ENE_HW_B;
- ene_printk(KERN_NOTICE, "KB3926B detected\n");
+ ene_notice("KB3926B detected");
} else {
dev->hw_revision = ENE_HW_D;
- ene_printk(KERN_WARNING,
- "unknown ENE chip detected, assuming KB3926D\n");
- ene_printk(KERN_WARNING,
- "driver support might be not complete");
-
+ ene_notice("KB3926D or higher detected");
}
- ene_printk(KERN_DEBUG,
- "chip is 0x%02x%02x - kbver = 0x%02x, rev = 0x%02x\n",
- chip_major, chip_minor, old_ver, hw_revision);
-
/* detect features hardware supports */
if (dev->hw_revision < ENE_HW_C)
return 0;
- fw_capabilities = ene_hw_read_reg(dev, ENE_FW2);
- ene_dbg("Firmware capabilities: %02x", fw_capabilities);
+ fw_reg1 = ene_read_reg(dev, ENE_FW1);
+ fw_reg2 = ene_read_reg(dev, ENE_FW2);
+
+ ene_notice("Firmware regs: %02x %02x", fw_reg1, fw_reg2);
- dev->hw_gpio40_learning = fw_capabilities & ENE_FW2_GP40_AS_LEARN;
- dev->hw_learning_and_tx_capable = fw_capabilities & ENE_FW2_LEARNING;
+ dev->hw_use_gpio_0a = !!(fw_reg2 & ENE_FW2_GP0A);
+ dev->hw_learning_and_tx_capable = !!(fw_reg2 & ENE_FW2_LEARNING);
+ dev->hw_extra_buffer = !!(fw_reg1 & ENE_FW1_HAS_EXTRA_BUF);
- dev->hw_fan_as_normal_input = dev->hw_learning_and_tx_capable &&
- (fw_capabilities & ENE_FW2_FAN_AS_NRML_IN);
+ if (dev->hw_learning_and_tx_capable)
+ dev->hw_fan_input = !!(fw_reg2 & ENE_FW2_FAN_INPUT);
- ene_printk(KERN_NOTICE, "hardware features:\n");
- ene_printk(KERN_NOTICE,
- "learning and transmit %s, gpio40_learn %s, fan_in %s\n",
- dev->hw_learning_and_tx_capable ? "on" : "off",
- dev->hw_gpio40_learning ? "on" : "off",
- dev->hw_fan_as_normal_input ? "on" : "off");
+ ene_notice("Hardware features:");
if (dev->hw_learning_and_tx_capable) {
- ene_printk(KERN_WARNING,
- "Device supports transmitting, but that support is\n");
- ene_printk(KERN_WARNING,
- "lightly tested. Please test it and mail\n");
- ene_printk(KERN_WARNING,
- "lirc-list@lists.sourceforge.net\n");
+ ene_notice("* Supports transmitting & learning mode");
+ ene_notice(" This feature is rare and therefore,");
+ ene_notice(" you are welcome to test it,");
+ ene_notice(" and/or contact the author via:");
+ ene_notice(" lirc-list@lists.sourceforge.net");
+ ene_notice(" or maximlevitsky@gmail.com");
+
+ ene_notice("* Uses GPIO %s for IR raw input",
+ dev->hw_use_gpio_0a ? "40" : "0A");
+
+ if (dev->hw_fan_input)
+ ene_notice("* Uses unused fan feedback input as source"
+ " of demodulated IR data");
}
+
+ if (!dev->hw_fan_input)
+ ene_notice("* Uses GPIO %s for IR demodulated input",
+ dev->hw_use_gpio_0a ? "0A" : "40");
+
+ if (dev->hw_extra_buffer)
+ ene_notice("* Uses new style input buffer");
return 0;
}
-/* this enables/disables IR input via gpio40*/
-static void ene_enable_gpio40_receive(struct ene_device *dev, int enable)
+/* Read properities of hw sample buffer */
+static void ene_rx_setup_hw_buffer(struct ene_device *dev)
{
- ene_hw_write_reg_mask(dev, ENE_CIR_CONF2, enable ?
- 0 : ENE_CIR_CONF2_GPIO40DIS,
- ENE_CIR_CONF2_GPIO40DIS);
+ u16 tmp;
+
+ ene_rx_read_hw_pointer(dev);
+ dev->r_pointer = dev->w_pointer;
+
+ if (!dev->hw_extra_buffer) {
+ dev->buffer_len = ENE_FW_PACKET_SIZE * 2;
+ return;
+ }
+
+ tmp = ene_read_reg(dev, ENE_FW_SAMPLE_BUFFER);
+ tmp |= ene_read_reg(dev, ENE_FW_SAMPLE_BUFFER+1) << 8;
+ dev->extra_buf1_address = tmp;
+
+ dev->extra_buf1_len = ene_read_reg(dev, ENE_FW_SAMPLE_BUFFER + 2);
+
+ tmp = ene_read_reg(dev, ENE_FW_SAMPLE_BUFFER + 3);
+ tmp |= ene_read_reg(dev, ENE_FW_SAMPLE_BUFFER + 4) << 8;
+ dev->extra_buf2_address = tmp;
+
+ dev->extra_buf2_len = ene_read_reg(dev, ENE_FW_SAMPLE_BUFFER + 5);
+
+ dev->buffer_len = dev->extra_buf1_len + dev->extra_buf2_len + 8;
+
+ ene_notice("Hardware uses 2 extended buffers:");
+ ene_notice(" 0x%04x - len : %d", dev->extra_buf1_address,
+ dev->extra_buf1_len);
+ ene_notice(" 0x%04x - len : %d", dev->extra_buf2_address,
+ dev->extra_buf2_len);
+
+ ene_notice("Total buffer len = %d", dev->buffer_len);
+
+ if (dev->buffer_len > 64 || dev->buffer_len < 16)
+ goto error;
+
+ if (dev->extra_buf1_address > 0xFBFC ||
+ dev->extra_buf1_address < 0xEC00)
+ goto error;
+
+ if (dev->extra_buf2_address > 0xFBFC ||
+ dev->extra_buf2_address < 0xEC00)
+ goto error;
+
+ if (dev->r_pointer > dev->buffer_len)
+ goto error;
+
+ ene_set_reg_mask(dev, ENE_FW1, ENE_FW1_EXTRA_BUF_HND);
+ return;
+error:
+ ene_warn("Error validating extra buffers, device probably won't work");
+ dev->hw_extra_buffer = false;
+ ene_clear_reg_mask(dev, ENE_FW1, ENE_FW1_EXTRA_BUF_HND);
}
-/* this enables/disables IR via standard input */
-static void ene_enable_normal_receive(struct ene_device *dev, int enable)
+
+/* Restore the pointers to extra buffers - to make module reload work*/
+static void ene_rx_restore_hw_buffer(struct ene_device *dev)
{
- ene_hw_write_reg(dev, ENE_CIR_CONF1, enable ? ENE_CIR_CONF1_RX_ON : 0);
+ if (!dev->hw_extra_buffer)
+ return;
+
+ ene_write_reg(dev, ENE_FW_SAMPLE_BUFFER + 0,
+ dev->extra_buf1_address & 0xFF);
+ ene_write_reg(dev, ENE_FW_SAMPLE_BUFFER + 1,
+ dev->extra_buf1_address >> 8);
+ ene_write_reg(dev, ENE_FW_SAMPLE_BUFFER + 2, dev->extra_buf1_len);
+
+ ene_write_reg(dev, ENE_FW_SAMPLE_BUFFER + 3,
+ dev->extra_buf2_address & 0xFF);
+ ene_write_reg(dev, ENE_FW_SAMPLE_BUFFER + 4,
+ dev->extra_buf2_address >> 8);
+ ene_write_reg(dev, ENE_FW_SAMPLE_BUFFER + 5,
+ dev->extra_buf2_len);
+ ene_clear_reg_mask(dev, ENE_FW1, ENE_FW1_EXTRA_BUF_HND);
}
-/* this enables/disables IR input via unused fan tachtometer input */
-static void ene_enable_fan_receive(struct ene_device *dev, int enable)
+/* Read hardware write pointer */
+static void ene_rx_read_hw_pointer(struct ene_device *dev)
{
- if (!enable)
- ene_hw_write_reg(dev, ENE_FAN_AS_IN1, 0);
- else {
- ene_hw_write_reg(dev, ENE_FAN_AS_IN1, ENE_FAN_AS_IN1_EN);
- ene_hw_write_reg(dev, ENE_FAN_AS_IN2, ENE_FAN_AS_IN2_EN);
- }
- dev->rx_fan_input_inuse = enable;
+ if (dev->hw_extra_buffer)
+ dev->w_pointer = ene_read_reg(dev, ENE_FW_RX_POINTER);
+ else
+ dev->w_pointer = ene_read_reg(dev, ENE_FW2)
+ & ENE_FW2_BUF_WPTR ? 0 : ENE_FW_PACKET_SIZE;
+
+ dbg_verbose("RB: HW write pointer: %02x, driver read pointer: %02x",
+ dev->w_pointer, dev->r_pointer);
}
+/* Gets address of next sample from HW ring buffer */
+static int ene_rx_get_sample_reg(struct ene_device *dev)
+{
+ int r_pointer;
+
+ if (dev->r_pointer == dev->w_pointer) {
+ dbg_verbose("RB: hit end, try update w_pointer");
+ ene_rx_read_hw_pointer(dev);
+ }
+
+ if (dev->r_pointer == dev->w_pointer) {
+ dbg_verbose("RB: end of data at %d", dev->r_pointer);
+ return 0;
+ }
+
+ dbg_verbose("RB: reading at offset %d", dev->r_pointer);
+ r_pointer = dev->r_pointer;
+
+ dev->r_pointer++;
+ if (dev->r_pointer == dev->buffer_len)
+ dev->r_pointer = 0;
+
+ dbg_verbose("RB: next read will be from offset %d", dev->r_pointer);
+
+ if (r_pointer < 8) {
+ dbg_verbose("RB: read at main buffer at %d", r_pointer);
+ return ENE_FW_SAMPLE_BUFFER + r_pointer;
+ }
+
+ r_pointer -= 8;
+
+ if (r_pointer < dev->extra_buf1_len) {
+ dbg_verbose("RB: read at 1st extra buffer at %d", r_pointer);
+ return dev->extra_buf1_address + r_pointer;
+ }
+
+ r_pointer -= dev->extra_buf1_len;
+
+ if (r_pointer < dev->extra_buf2_len) {
+ dbg_verbose("RB: read at 2nd extra buffer at %d", r_pointer);
+ return dev->extra_buf2_address + r_pointer;
+ }
+
+ dbg("attempt to read beyong ring bufer end");
+ return 0;
+}
/* Sense current received carrier */
-static int ene_rx_sense_carrier(struct ene_device *dev)
+void ene_rx_sense_carrier(struct ene_device *dev)
{
- int period = ene_hw_read_reg(dev, ENE_RX_CARRIER);
- int carrier;
- ene_dbg("RX: hardware carrier period = %02x", period);
+ DEFINE_IR_RAW_EVENT(ev);
- if (!(period & ENE_RX_CARRIER_VALID))
- return 0;
+ int carrier, duty_cycle;
+ int period = ene_read_reg(dev, ENE_CIRCAR_PRD);
+ int hperiod = ene_read_reg(dev, ENE_CIRCAR_HPRD);
+
+ if (!(period & ENE_CIRCAR_PRD_VALID))
+ return;
- period &= ~ENE_RX_CARRIER_VALID;
+ period &= ~ENE_CIRCAR_PRD_VALID;
if (!period)
- return 0;
+ return;
+
+ dbg("RX: hardware carrier period = %02x", period);
+ dbg("RX: hardware carrier pulse period = %02x", hperiod);
carrier = 2000000 / period;
- ene_dbg("RX: sensed carrier = %d Hz", carrier);
- return carrier;
+ duty_cycle = (hperiod * 100) / period;
+ dbg("RX: sensed carrier = %d Hz, duty cycle %d%%",
+ carrier, duty_cycle);
+ if (dev->carrier_detect_enabled) {
+ ev.carrier_report = true;
+ ev.carrier = carrier;
+ ev.duty_cycle = duty_cycle;
+ ir_raw_event_store(dev->idev, &ev);
+ }
}
-/* determine which input to use*/
-static void ene_rx_set_inputs(struct ene_device *dev)
+/* this enables/disables the CIR RX engine */
+static void ene_rx_enable_cir_engine(struct ene_device *dev, bool enable)
{
- int learning_mode = dev->learning_enabled;
-
- ene_dbg("RX: setup receiver, learning mode = %d", learning_mode);
+ ene_set_clear_reg_mask(dev, ENE_CIRCFG,
+ ENE_CIRCFG_RX_EN | ENE_CIRCFG_RX_IRQ, enable);
+}
- ene_enable_normal_receive(dev, 1);
+/* this selects input for CIR engine. Ether GPIO 0A or GPIO40*/
+static void ene_rx_select_input(struct ene_device *dev, bool gpio_0a)
+{
+ ene_set_clear_reg_mask(dev, ENE_CIRCFG2, ENE_CIRCFG2_GPIO0A, gpio_0a);
+}
- /* old hardware doesn't support learning mode for sure */
- if (dev->hw_revision <= ENE_HW_B)
+/*
+ * this enables alternative input via fan tachometer sensor and bypasses
+ * the hw CIR engine
+ */
+static void ene_rx_enable_fan_input(struct ene_device *dev, bool enable)
+{
+ if (!dev->hw_fan_input)
return;
- /* receiver not learning capable, still set gpio40 correctly */
- if (!dev->hw_learning_and_tx_capable) {
- ene_enable_gpio40_receive(dev, !dev->hw_gpio40_learning);
- return;
+ if (!enable)
+ ene_write_reg(dev, ENE_FAN_AS_IN1, 0);
+ else {
+ ene_write_reg(dev, ENE_FAN_AS_IN1, ENE_FAN_AS_IN1_EN);
+ ene_write_reg(dev, ENE_FAN_AS_IN2, ENE_FAN_AS_IN2_EN);
}
+}
+
+/* setup the receiver for RX*/
+static void ene_rx_setup(struct ene_device *dev)
+{
+ bool learning_mode = dev->learning_mode_enabled ||
+ dev->carrier_detect_enabled;
+ int sample_period_adjust = 0;
+
+ dbg("RX: setup receiver, learning mode = %d", learning_mode);
+
+
+ /* This selects RLC input and clears CFG2 settings */
+ ene_write_reg(dev, ENE_CIRCFG2, 0x00);
+
+ /* set sample period*/
+ if (sample_period == ENE_DEFAULT_SAMPLE_PERIOD)
+ sample_period_adjust =
+ dev->pll_freq == ENE_DEFAULT_PLL_FREQ ? 1 : 2;
+
+ ene_write_reg(dev, ENE_CIRRLC_CFG,
+ (sample_period + sample_period_adjust) |
+ ENE_CIRRLC_CFG_OVERFLOW);
+ /* revB doesn't support inputs */
+ if (dev->hw_revision < ENE_HW_C)
+ goto select_timeout;
- /* enable learning mode */
if (learning_mode) {
- ene_enable_gpio40_receive(dev, dev->hw_gpio40_learning);
- /* fan input is not used for learning */
- if (dev->hw_fan_as_normal_input)
- ene_enable_fan_receive(dev, 0);
+ WARN_ON(!dev->hw_learning_and_tx_capable);
- /* disable learning mode */
- } else {
- if (dev->hw_fan_as_normal_input) {
- ene_enable_fan_receive(dev, 1);
- ene_enable_normal_receive(dev, 0);
- } else
- ene_enable_gpio40_receive(dev,
- !dev->hw_gpio40_learning);
- }
+ /* Enable the opposite of the normal input
+ That means that if GPIO40 is normally used, use GPIO0A
+ and vice versa.
+ This input will carry non demodulated
+ signal, and we will tell the hw to demodulate it itself */
+ ene_rx_select_input(dev, !dev->hw_use_gpio_0a);
+ dev->rx_fan_input_inuse = false;
- /* set few additional settings for this mode */
- ene_hw_write_reg_mask(dev, ENE_CIR_CONF1, learning_mode ?
- ENE_CIR_CONF1_LEARN1 : 0, ENE_CIR_CONF1_LEARN1);
+ /* Enable carrier demodulation */
+ ene_set_reg_mask(dev, ENE_CIRCFG, ENE_CIRCFG_CARR_DEMOD);
- ene_hw_write_reg_mask(dev, ENE_CIR_CONF2, learning_mode ?
- ENE_CIR_CONF2_LEARN2 : 0, ENE_CIR_CONF2_LEARN2);
+ /* Enable carrier detection */
+ ene_write_reg(dev, ENE_CIRCAR_PULS, 0x63);
+ ene_set_clear_reg_mask(dev, ENE_CIRCFG2, ENE_CIRCFG2_CARR_DETECT,
+ dev->carrier_detect_enabled || debug);
+ } else {
+ if (dev->hw_fan_input)
+ dev->rx_fan_input_inuse = true;
+ else
+ ene_rx_select_input(dev, dev->hw_use_gpio_0a);
+
+ /* Disable carrier detection & demodulation */
+ ene_clear_reg_mask(dev, ENE_CIRCFG, ENE_CIRCFG_CARR_DEMOD);
+ ene_clear_reg_mask(dev, ENE_CIRCFG2, ENE_CIRCFG2_CARR_DETECT);
+ }
+select_timeout:
if (dev->rx_fan_input_inuse) {
- dev->props->rx_resolution = ENE_SAMPLE_PERIOD_FAN * 1000;
+ dev->props->rx_resolution = MS_TO_NS(ENE_FW_SAMPLE_PERIOD_FAN);
- dev->props->timeout =
- ENE_FAN_VALUE_MASK * ENE_SAMPLE_PERIOD_FAN * 1000;
+ /* Fan input doesn't support timeouts, it just ends the
+ input with a maximum sample */
+ dev->props->min_timeout = dev->props->max_timeout =
+ MS_TO_NS(ENE_FW_SMPL_BUF_FAN_MSK *
+ ENE_FW_SAMPLE_PERIOD_FAN);
} else {
- dev->props->rx_resolution = sample_period * 1000;
- dev->props->timeout = ENE_MAXGAP * 1000;
+ dev->props->rx_resolution = MS_TO_NS(sample_period);
+
+ /* Theoreticly timeout is unlimited, but we cap it
+ * because it was seen that on one device, it
+ * would stop sending spaces after around 250 msec.
+ * Besides, this is close to 2^32 anyway and timeout is u32.
+ */
+ dev->props->min_timeout = MS_TO_NS(127 * sample_period);
+ dev->props->max_timeout = MS_TO_NS(200000);
}
+
+ if (dev->hw_learning_and_tx_capable)
+ dev->props->tx_resolution = MS_TO_NS(sample_period);
+
+ if (dev->props->timeout > dev->props->max_timeout)
+ dev->props->timeout = dev->props->max_timeout;
+ if (dev->props->timeout < dev->props->min_timeout)
+ dev->props->timeout = dev->props->min_timeout;
}
/* Enable the device for receive */
@@ -278,145 +481,157 @@ static void ene_rx_enable(struct ene_device *dev)
{
u8 reg_value;
+ /* Enable system interrupt */
if (dev->hw_revision < ENE_HW_C) {
- ene_hw_