1*2874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
23fed7dbeSJames Hogan /*
33fed7dbeSJames Hogan * ImgTec IR Raw Decoder found in PowerDown Controller.
43fed7dbeSJames Hogan *
53fed7dbeSJames Hogan * Copyright 2010-2014 Imagination Technologies Ltd.
63fed7dbeSJames Hogan *
73fed7dbeSJames Hogan * This ties into the input subsystem using the RC-core in raw mode. Raw IR
83fed7dbeSJames Hogan * signal edges are reported and decoded by generic software decoders.
93fed7dbeSJames Hogan */
103fed7dbeSJames Hogan
113fed7dbeSJames Hogan #include <linux/spinlock.h>
123fed7dbeSJames Hogan #include <media/rc-core.h>
133fed7dbeSJames Hogan #include "img-ir.h"
143fed7dbeSJames Hogan
153fed7dbeSJames Hogan #define ECHO_TIMEOUT_MS 150 /* ms between echos */
163fed7dbeSJames Hogan
173fed7dbeSJames Hogan /* must be called with priv->lock held */
img_ir_refresh_raw(struct img_ir_priv * priv,u32 irq_status)183fed7dbeSJames Hogan static void img_ir_refresh_raw(struct img_ir_priv *priv, u32 irq_status)
193fed7dbeSJames Hogan {
203fed7dbeSJames Hogan struct img_ir_priv_raw *raw = &priv->raw;
213fed7dbeSJames Hogan struct rc_dev *rc_dev = priv->raw.rdev;
223fed7dbeSJames Hogan int multiple;
233fed7dbeSJames Hogan u32 ir_status;
243fed7dbeSJames Hogan
253fed7dbeSJames Hogan /* find whether both rise and fall was detected */
263fed7dbeSJames Hogan multiple = ((irq_status & IMG_IR_IRQ_EDGE) == IMG_IR_IRQ_EDGE);
273fed7dbeSJames Hogan /*
283fed7dbeSJames Hogan * If so, we need to see if the level has actually changed.
293fed7dbeSJames Hogan * If it's just noise that we didn't have time to process,
303fed7dbeSJames Hogan * there's no point reporting it.
313fed7dbeSJames Hogan */
323fed7dbeSJames Hogan ir_status = img_ir_read(priv, IMG_IR_STATUS) & IMG_IR_IRRXD;
333fed7dbeSJames Hogan if (multiple && ir_status == raw->last_status)
343fed7dbeSJames Hogan return;
353fed7dbeSJames Hogan raw->last_status = ir_status;
363fed7dbeSJames Hogan
373fed7dbeSJames Hogan /* report the edge to the IR raw decoders */
383fed7dbeSJames Hogan if (ir_status) /* low */
3986fe1ac0SSean Young ir_raw_event_store_edge(rc_dev, false);
403fed7dbeSJames Hogan else /* high */
4186fe1ac0SSean Young ir_raw_event_store_edge(rc_dev, true);
423fed7dbeSJames Hogan ir_raw_event_handle(rc_dev);
433fed7dbeSJames Hogan }
443fed7dbeSJames Hogan
453fed7dbeSJames Hogan /* called with priv->lock held */
img_ir_isr_raw(struct img_ir_priv * priv,u32 irq_status)463fed7dbeSJames Hogan void img_ir_isr_raw(struct img_ir_priv *priv, u32 irq_status)
473fed7dbeSJames Hogan {
483fed7dbeSJames Hogan struct img_ir_priv_raw *raw = &priv->raw;
493fed7dbeSJames Hogan
503fed7dbeSJames Hogan /* check not removing */
513fed7dbeSJames Hogan if (!raw->rdev)
523fed7dbeSJames Hogan return;
533fed7dbeSJames Hogan
543fed7dbeSJames Hogan img_ir_refresh_raw(priv, irq_status);
553fed7dbeSJames Hogan
563fed7dbeSJames Hogan /* start / push back the echo timer */
573fed7dbeSJames Hogan mod_timer(&raw->timer, jiffies + msecs_to_jiffies(ECHO_TIMEOUT_MS));
583fed7dbeSJames Hogan }
593fed7dbeSJames Hogan
603fed7dbeSJames Hogan /*
613fed7dbeSJames Hogan * Echo timer callback function.
623fed7dbeSJames Hogan * The raw decoders expect to get a final sample even if there are no edges, in
633fed7dbeSJames Hogan * order to be assured of the final space. If there are no edges for a certain
643fed7dbeSJames Hogan * time we use this timer to emit a final sample to satisfy them.
653fed7dbeSJames Hogan */
img_ir_echo_timer(struct timer_list * t)66b17ec78aSKees Cook static void img_ir_echo_timer(struct timer_list *t)
673fed7dbeSJames Hogan {
68b17ec78aSKees Cook struct img_ir_priv *priv = from_timer(priv, t, raw.timer);
693fed7dbeSJames Hogan
703fed7dbeSJames Hogan spin_lock_irq(&priv->lock);
713fed7dbeSJames Hogan
723fed7dbeSJames Hogan /* check not removing */
733fed7dbeSJames Hogan if (priv->raw.rdev)
743fed7dbeSJames Hogan /*
753fed7dbeSJames Hogan * It's safe to pass irq_status=0 since it's only used to check
763fed7dbeSJames Hogan * for double edges.
773fed7dbeSJames Hogan */
783fed7dbeSJames Hogan img_ir_refresh_raw(priv, 0);
793fed7dbeSJames Hogan
803fed7dbeSJames Hogan spin_unlock_irq(&priv->lock);
813fed7dbeSJames Hogan }
823fed7dbeSJames Hogan
img_ir_setup_raw(struct img_ir_priv * priv)833fed7dbeSJames Hogan void img_ir_setup_raw(struct img_ir_priv *priv)
843fed7dbeSJames Hogan {
853fed7dbeSJames Hogan u32 irq_en;
863fed7dbeSJames Hogan
873fed7dbeSJames Hogan if (!priv->raw.rdev)
883fed7dbeSJames Hogan return;
893fed7dbeSJames Hogan
903fed7dbeSJames Hogan /* clear and enable edge interrupts */
913fed7dbeSJames Hogan spin_lock_irq(&priv->lock);
923fed7dbeSJames Hogan irq_en = img_ir_read(priv, IMG_IR_IRQ_ENABLE);
933fed7dbeSJames Hogan irq_en |= IMG_IR_IRQ_EDGE;
943fed7dbeSJames Hogan img_ir_write(priv, IMG_IR_IRQ_CLEAR, IMG_IR_IRQ_EDGE);
953fed7dbeSJames Hogan img_ir_write(priv, IMG_IR_IRQ_ENABLE, irq_en);
963fed7dbeSJames Hogan spin_unlock_irq(&priv->lock);
973fed7dbeSJames Hogan }
983fed7dbeSJames Hogan
img_ir_probe_raw(struct img_ir_priv * priv)993fed7dbeSJames Hogan int img_ir_probe_raw(struct img_ir_priv *priv)
1003fed7dbeSJames Hogan {
1013fed7dbeSJames Hogan struct img_ir_priv_raw *raw = &priv->raw;
1023fed7dbeSJames Hogan struct rc_dev *rdev;
1033fed7dbeSJames Hogan int error;
1043fed7dbeSJames Hogan
1053fed7dbeSJames Hogan /* Set up the echo timer */
106b17ec78aSKees Cook timer_setup(&raw->timer, img_ir_echo_timer, 0);
1073fed7dbeSJames Hogan
1083fed7dbeSJames Hogan /* Allocate raw decoder */
1090f7499fdSAndi Shyti raw->rdev = rdev = rc_allocate_device(RC_DRIVER_IR_RAW);
1103fed7dbeSJames Hogan if (!rdev) {
1113fed7dbeSJames Hogan dev_err(priv->dev, "cannot allocate raw input device\n");
1123fed7dbeSJames Hogan return -ENOMEM;
1133fed7dbeSJames Hogan }
1143fed7dbeSJames Hogan rdev->priv = priv;
1153fed7dbeSJames Hogan rdev->map_name = RC_MAP_EMPTY;
116518f4b26SSean Young rdev->device_name = "IMG Infrared Decoder Raw";
1173fed7dbeSJames Hogan
1183fed7dbeSJames Hogan /* Register raw decoder */
1193fed7dbeSJames Hogan error = rc_register_device(rdev);
1203fed7dbeSJames Hogan if (error) {
1213fed7dbeSJames Hogan dev_err(priv->dev, "failed to register raw IR input device\n");
1223fed7dbeSJames Hogan rc_free_device(rdev);
1233fed7dbeSJames Hogan raw->rdev = NULL;
1243fed7dbeSJames Hogan return error;
1253fed7dbeSJames Hogan }
1263fed7dbeSJames Hogan
1273fed7dbeSJames Hogan return 0;
1283fed7dbeSJames Hogan }
1293fed7dbeSJames Hogan
img_ir_remove_raw(struct img_ir_priv * priv)1303fed7dbeSJames Hogan void img_ir_remove_raw(struct img_ir_priv *priv)
1313fed7dbeSJames Hogan {
1323fed7dbeSJames Hogan struct img_ir_priv_raw *raw = &priv->raw;
1333fed7dbeSJames Hogan struct rc_dev *rdev = raw->rdev;
1343fed7dbeSJames Hogan u32 irq_en;
1353fed7dbeSJames Hogan
1363fed7dbeSJames Hogan if (!rdev)
1373fed7dbeSJames Hogan return;
1383fed7dbeSJames Hogan
1393fed7dbeSJames Hogan /* switch off and disable raw (edge) interrupts */
1403fed7dbeSJames Hogan spin_lock_irq(&priv->lock);
1413fed7dbeSJames Hogan raw->rdev = NULL;
1423fed7dbeSJames Hogan irq_en = img_ir_read(priv, IMG_IR_IRQ_ENABLE);
1433fed7dbeSJames Hogan irq_en &= ~IMG_IR_IRQ_EDGE;
1443fed7dbeSJames Hogan img_ir_write(priv, IMG_IR_IRQ_ENABLE, irq_en);
1453fed7dbeSJames Hogan img_ir_write(priv, IMG_IR_IRQ_CLEAR, IMG_IR_IRQ_EDGE);
1463fed7dbeSJames Hogan spin_unlock_irq(&priv->lock);
1473fed7dbeSJames Hogan
1483fed7dbeSJames Hogan rc_unregister_device(rdev);
1493fed7dbeSJames Hogan
1503fed7dbeSJames Hogan del_timer_sync(&raw->timer);
1513fed7dbeSJames Hogan }
152