// 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 #include #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); }