forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
151 lines
3.8 KiB
151 lines
3.8 KiB
// SPDX-License-Identifier: GPL-2.0-or-later |
|
/* |
|
* ImgTec IR Raw Decoder found in PowerDown Controller. |
|
* |
|
* Copyright 2010-2014 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" |
|
|
|
#define ECHO_TIMEOUT_MS 150 /* ms between echos */ |
|
|
|
/* must be called with priv->lock held */ |
|
static void img_ir_refresh_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, false); |
|
else /* high */ |
|
ir_raw_event_store_edge(rc_dev, true); |
|
ir_raw_event_handle(rc_dev); |
|
} |
|
|
|
/* called with priv->lock held */ |
|
void img_ir_isr_raw(struct img_ir_priv *priv, u32 irq_status) |
|
{ |
|
struct img_ir_priv_raw *raw = &priv->raw; |
|
|
|
/* check not removing */ |
|
if (!raw->rdev) |
|
return; |
|
|
|
img_ir_refresh_raw(priv, irq_status); |
|
|
|
/* start / push back the echo timer */ |
|
mod_timer(&raw->timer, jiffies + msecs_to_jiffies(ECHO_TIMEOUT_MS)); |
|
} |
|
|
|
/* |
|
* Echo timer callback function. |
|
* The raw decoders expect to get a final sample even if there are no edges, in |
|
* order to be assured of the final space. If there are no edges for a certain |
|
* time we use this timer to emit a final sample to satisfy them. |
|
*/ |
|
static void img_ir_echo_timer(struct timer_list *t) |
|
{ |
|
struct img_ir_priv *priv = from_timer(priv, t, raw.timer); |
|
|
|
spin_lock_irq(&priv->lock); |
|
|
|
/* check not removing */ |
|
if (priv->raw.rdev) |
|
/* |
|
* It's safe to pass irq_status=0 since it's only used to check |
|
* for double edges. |
|
*/ |
|
img_ir_refresh_raw(priv, 0); |
|
|
|
spin_unlock_irq(&priv->lock); |
|
} |
|
|
|
void img_ir_setup_raw(struct img_ir_priv *priv) |
|
{ |
|
u32 irq_en; |
|
|
|
if (!priv->raw.rdev) |
|
return; |
|
|
|
/* clear and enable edge interrupts */ |
|
spin_lock_irq(&priv->lock); |
|
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_irq(&priv->lock); |
|
} |
|
|
|
int img_ir_probe_raw(struct img_ir_priv *priv) |
|
{ |
|
struct img_ir_priv_raw *raw = &priv->raw; |
|
struct rc_dev *rdev; |
|
int error; |
|
|
|
/* Set up the echo timer */ |
|
timer_setup(&raw->timer, img_ir_echo_timer, 0); |
|
|
|
/* Allocate raw decoder */ |
|
raw->rdev = rdev = rc_allocate_device(RC_DRIVER_IR_RAW); |
|
if (!rdev) { |
|
dev_err(priv->dev, "cannot allocate raw input device\n"); |
|
return -ENOMEM; |
|
} |
|
rdev->priv = priv; |
|
rdev->map_name = RC_MAP_EMPTY; |
|
rdev->device_name = "IMG Infrared Decoder 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; |
|
u32 irq_en; |
|
|
|
if (!rdev) |
|
return; |
|
|
|
/* switch off and disable raw (edge) interrupts */ |
|
spin_lock_irq(&priv->lock); |
|
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_irq(&priv->lock); |
|
|
|
rc_unregister_device(rdev); |
|
|
|
del_timer_sync(&raw->timer); |
|
}
|
|
|