// SPDX-License-Identifier: GPL-2.0-or-later /* * driver for ENE KB3926 B/C/D/E/F CIR (pnp id: ENE0XXX) * * Copyright (C) 2010 Maxim Levitsky <maximlevitsky@gmail.com> * * 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
*/
/* A helper to set/clear a bit in register according to boolean variable */ staticvoid 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);
}
if (hw_revision == 0xFF) {
pr_warn("device seems to be disabled\n");
pr_warn("send a mail to lirc-list@lists.sourceforge.net\n");
pr_warn("please attach output of acpidump and dmidecode\n"); return -ENODEV;
}
if (dev->hw_learning_and_tx_capable)
dev->hw_fan_input = !!(fw_reg2 & ENE_FW2_FAN_INPUT);
pr_notice("Hardware features:\n");
if (dev->hw_learning_and_tx_capable) {
pr_notice("* Supports transmitting & learning mode\n");
pr_notice(" This feature is rare and therefore,\n");
pr_notice(" you are welcome to test it,\n");
pr_notice(" and/or contact the author via:\n");
pr_notice(" lirc-list@lists.sourceforge.net\n");
pr_notice(" or maximlevitsky@gmail.com\n");
pr_notice("* Uses GPIO %s for IR raw input\n",
dev->hw_use_gpio_0a ? "40" : "0A");
if (dev->hw_fan_input)
pr_notice("* Uses unused fan feedback input as source of demodulated IR data\n");
}
if (!dev->hw_fan_input)
pr_notice("* Uses GPIO %s for IR demodulated input\n",
dev->hw_use_gpio_0a ? "0A" : "40");
if (dev->hw_extra_buffer)
pr_notice("* Uses new style input buffer\n"); return 0;
}
/* Restore the pointers to extra buffers - to make module reload work*/ staticvoid ene_rx_restore_hw_buffer(struct ene_device *dev)
{ if (!dev->hw_extra_buffer) return;
/* Gets address of next sample from HW ring buffer */ staticint 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 beyond ring buffer end"); return 0;
}
/* Sense current received carrier */ staticvoid ene_rx_sense_carrier(struct ene_device *dev)
{ 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_CIRCAR_PRD_VALID;
if (!period) return;
dbg("RX: hardware carrier period = %02x", period);
dbg("RX: hardware carrier pulse period = %02x", hperiod);
/* this selects input for CIR engine. Ether GPIO 0A or GPIO40*/ staticvoid ene_rx_select_input(struct ene_device *dev, bool gpio_0a)
{
ene_set_clear_reg_mask(dev, ENE_CIRCFG2, ENE_CIRCFG2_GPIO0A, gpio_0a);
}
/* * this enables alternative input via fan tachometer sensor and bypasses * the hw CIR engine
*/ staticvoid ene_rx_enable_fan_input(struct ene_device *dev, bool enable)
{ if (!dev->hw_fan_input) return;
/* 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;
if (learning_mode) {
WARN_ON(!dev->hw_learning_and_tx_capable);
/* 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;
select_timeout: if (dev->rx_fan_input_inuse) {
dev->rdev->rx_resolution = ENE_FW_SAMPLE_PERIOD_FAN;
/* Fan input doesn't support timeouts, it just ends the
input with a maximum sample */
dev->rdev->min_timeout = dev->rdev->max_timeout =
ENE_FW_SMPL_BUF_FAN_MSK *
ENE_FW_SAMPLE_PERIOD_FAN;
} else {
dev->rdev->rx_resolution = 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->rdev->min_timeout = 127 * sample_period;
dev->rdev->max_timeout = 200000;
}
if (dev->rdev->timeout > dev->rdev->max_timeout)
dev->rdev->timeout = dev->rdev->max_timeout; if (dev->rdev->timeout < dev->rdev->min_timeout)
dev->rdev->timeout = dev->rdev->min_timeout;
}
/* Enable the device for receive */ staticvoid ene_rx_enable_hw(struct ene_device *dev)
{
u8 reg_value;
/* disable hardware IRQ and firmware flag */
ene_clear_reg_mask(dev, ENE_FW1, ENE_FW1_ENABLE | ENE_FW1_IRQ);
ir_raw_event_set_idle(dev->rdev, true);
}
/* Disable the device receiver - wrapper to track the state */ staticvoid ene_rx_disable(struct ene_device *dev)
{
ene_rx_disable_hw(dev);
dev->rx_enabled = false;
}
/* This resets the receiver. Useful to stop stream of spaces at end of * transmission
*/ staticvoid ene_rx_reset(struct ene_device *dev)
{
ene_clear_reg_mask(dev, ENE_CIRCFG, ENE_CIRCFG_RX_EN);
ene_set_reg_mask(dev, ENE_CIRCFG, ENE_CIRCFG_RX_EN);
}
/* Set up the TX carrier frequency and duty cycle */ staticvoid ene_tx_set_carrier(struct ene_device *dev)
{
u8 tx_puls_width; unsignedlong flags;
/* TX one sample - must be called with dev->hw_lock*/ staticvoid ene_tx_sample(struct ene_device *dev)
{
u8 raw_tx;
u32 sample; bool pulse = dev->tx_sample_pulse;
if (!dev->tx_buffer) {
pr_warn("TX: BUG: attempt to transmit NULL buffer\n"); return;
}
/* Grab next TX sample */ if (!dev->tx_sample) {
if (dev->tx_pos == dev->tx_len) { if (!dev->tx_done) {
dbg("TX: no more data to send");
dev->tx_done = true; gotoexit;
} else {
dbg("TX: last sample sent by hardware");
ene_tx_disable(dev);
complete(&dev->tx_complete); return;
}
}
if (irq_status & ENE_IRQ_TX) {
dbg_verbose("TX interrupt"); if (!dev->hw_learning_and_tx_capable) {
dbg("TX interrupt on unsupported device!"); goto unlock;
}
ene_tx_sample(dev);
}
if (!(irq_status & ENE_IRQ_RX)) goto unlock;
dbg_verbose("RX interrupt");
if (dev->hw_learning_and_tx_capable)
ene_rx_sense_carrier(dev);
/* On hardware that don't support extra buffer we need to trust
the interrupt and not track the read pointer */ if (!dev->hw_extra_buffer)
dev->r_pointer = dev->w_pointer == 0 ? ENE_FW_PACKET_SIZE : 0;
while (1) {
reg = ene_rx_get_sample_reg(dev);
dbg_verbose("next sample to read at: %04x", reg); if (!reg) break;
hw_value = ene_read_reg(dev, reg);
if (dev->rx_fan_input_inuse) {
int offset = ENE_FW_SMPL_BUF_FAN - ENE_FW_SAMPLE_BUFFER;
/* read high part of the sample */
hw_value |= ene_read_reg(dev, reg + offset) << 8;
pulse = hw_value & ENE_FW_SMPL_BUF_FAN_PLS;
/* clear space bit, and other unused bits */
hw_value &= ENE_FW_SMPL_BUF_FAN_MSK;
hw_sample = hw_value * ENE_FW_SAMPLE_PERIOD_FAN;
/* Set reasonable default timeout */
dev->rdev->timeout = MS_TO_US(150);
}
/* Upload all hardware settings at once. Used at load and resume time */ staticvoid ene_setup_hw_settings(struct ene_device *dev)
{ if (dev->hw_learning_and_tx_capable) {
ene_tx_set_carrier(dev);
ene_tx_set_transmitters(dev);
}
ene_rx_setup(dev);
}
/* outside interface: called on first open*/ staticint ene_open(struct rc_dev *rdev)
{ struct ene_device *dev = rdev->priv; unsignedlong flags;
/* enable wake on IR (wakes on specific button on original remote) */ staticvoid ene_enable_wake(struct ene_device *dev, bool enable)
{
dbg("wake on IR %s", enable ? "enabled" : "disabled");
ene_set_clear_reg_mask(dev, ENE_FW1, ENE_FW1_WAKE, enable);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.