diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-28 09:35:11 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-28 09:35:11 -0700 |
commit | 0851668fdd97e526b2a41f794b785c204dd3d3e0 (patch) | |
tree | 4ef7c20a8be8393006c6fe9627eb29dd30877d61 /drivers/media/IR | |
parent | 00ebb6382b8d9c7c15b5f8ad230670d8161d38dd (diff) | |
parent | 7655e594945289b418af39f6669fea4666a7b520 (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')
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_ |