162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ImgTec IR Raw Decoder found in PowerDown Controller. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2010-2014 Imagination Technologies Ltd. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This ties into the input subsystem using the RC-core in raw mode. Raw IR 862306a36Sopenharmony_ci * signal edges are reported and decoded by generic software decoders. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/spinlock.h> 1262306a36Sopenharmony_ci#include <media/rc-core.h> 1362306a36Sopenharmony_ci#include "img-ir.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define ECHO_TIMEOUT_MS 150 /* ms between echos */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* must be called with priv->lock held */ 1862306a36Sopenharmony_cistatic void img_ir_refresh_raw(struct img_ir_priv *priv, u32 irq_status) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci struct img_ir_priv_raw *raw = &priv->raw; 2162306a36Sopenharmony_ci struct rc_dev *rc_dev = priv->raw.rdev; 2262306a36Sopenharmony_ci int multiple; 2362306a36Sopenharmony_ci u32 ir_status; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci /* find whether both rise and fall was detected */ 2662306a36Sopenharmony_ci multiple = ((irq_status & IMG_IR_IRQ_EDGE) == IMG_IR_IRQ_EDGE); 2762306a36Sopenharmony_ci /* 2862306a36Sopenharmony_ci * If so, we need to see if the level has actually changed. 2962306a36Sopenharmony_ci * If it's just noise that we didn't have time to process, 3062306a36Sopenharmony_ci * there's no point reporting it. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci ir_status = img_ir_read(priv, IMG_IR_STATUS) & IMG_IR_IRRXD; 3362306a36Sopenharmony_ci if (multiple && ir_status == raw->last_status) 3462306a36Sopenharmony_ci return; 3562306a36Sopenharmony_ci raw->last_status = ir_status; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci /* report the edge to the IR raw decoders */ 3862306a36Sopenharmony_ci if (ir_status) /* low */ 3962306a36Sopenharmony_ci ir_raw_event_store_edge(rc_dev, false); 4062306a36Sopenharmony_ci else /* high */ 4162306a36Sopenharmony_ci ir_raw_event_store_edge(rc_dev, true); 4262306a36Sopenharmony_ci ir_raw_event_handle(rc_dev); 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* called with priv->lock held */ 4662306a36Sopenharmony_civoid img_ir_isr_raw(struct img_ir_priv *priv, u32 irq_status) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci struct img_ir_priv_raw *raw = &priv->raw; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci /* check not removing */ 5162306a36Sopenharmony_ci if (!raw->rdev) 5262306a36Sopenharmony_ci return; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci img_ir_refresh_raw(priv, irq_status); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci /* start / push back the echo timer */ 5762306a36Sopenharmony_ci mod_timer(&raw->timer, jiffies + msecs_to_jiffies(ECHO_TIMEOUT_MS)); 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/* 6162306a36Sopenharmony_ci * Echo timer callback function. 6262306a36Sopenharmony_ci * The raw decoders expect to get a final sample even if there are no edges, in 6362306a36Sopenharmony_ci * order to be assured of the final space. If there are no edges for a certain 6462306a36Sopenharmony_ci * time we use this timer to emit a final sample to satisfy them. 6562306a36Sopenharmony_ci */ 6662306a36Sopenharmony_cistatic void img_ir_echo_timer(struct timer_list *t) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci struct img_ir_priv *priv = from_timer(priv, t, raw.timer); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci spin_lock_irq(&priv->lock); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* check not removing */ 7362306a36Sopenharmony_ci if (priv->raw.rdev) 7462306a36Sopenharmony_ci /* 7562306a36Sopenharmony_ci * It's safe to pass irq_status=0 since it's only used to check 7662306a36Sopenharmony_ci * for double edges. 7762306a36Sopenharmony_ci */ 7862306a36Sopenharmony_ci img_ir_refresh_raw(priv, 0); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci spin_unlock_irq(&priv->lock); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_civoid img_ir_setup_raw(struct img_ir_priv *priv) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci u32 irq_en; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (!priv->raw.rdev) 8862306a36Sopenharmony_ci return; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* clear and enable edge interrupts */ 9162306a36Sopenharmony_ci spin_lock_irq(&priv->lock); 9262306a36Sopenharmony_ci irq_en = img_ir_read(priv, IMG_IR_IRQ_ENABLE); 9362306a36Sopenharmony_ci irq_en |= IMG_IR_IRQ_EDGE; 9462306a36Sopenharmony_ci img_ir_write(priv, IMG_IR_IRQ_CLEAR, IMG_IR_IRQ_EDGE); 9562306a36Sopenharmony_ci img_ir_write(priv, IMG_IR_IRQ_ENABLE, irq_en); 9662306a36Sopenharmony_ci spin_unlock_irq(&priv->lock); 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ciint img_ir_probe_raw(struct img_ir_priv *priv) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct img_ir_priv_raw *raw = &priv->raw; 10262306a36Sopenharmony_ci struct rc_dev *rdev; 10362306a36Sopenharmony_ci int error; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci /* Set up the echo timer */ 10662306a36Sopenharmony_ci timer_setup(&raw->timer, img_ir_echo_timer, 0); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* Allocate raw decoder */ 10962306a36Sopenharmony_ci raw->rdev = rdev = rc_allocate_device(RC_DRIVER_IR_RAW); 11062306a36Sopenharmony_ci if (!rdev) { 11162306a36Sopenharmony_ci dev_err(priv->dev, "cannot allocate raw input device\n"); 11262306a36Sopenharmony_ci return -ENOMEM; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci rdev->priv = priv; 11562306a36Sopenharmony_ci rdev->map_name = RC_MAP_EMPTY; 11662306a36Sopenharmony_ci rdev->device_name = "IMG Infrared Decoder Raw"; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci /* Register raw decoder */ 11962306a36Sopenharmony_ci error = rc_register_device(rdev); 12062306a36Sopenharmony_ci if (error) { 12162306a36Sopenharmony_ci dev_err(priv->dev, "failed to register raw IR input device\n"); 12262306a36Sopenharmony_ci rc_free_device(rdev); 12362306a36Sopenharmony_ci raw->rdev = NULL; 12462306a36Sopenharmony_ci return error; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci return 0; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_civoid img_ir_remove_raw(struct img_ir_priv *priv) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct img_ir_priv_raw *raw = &priv->raw; 13362306a36Sopenharmony_ci struct rc_dev *rdev = raw->rdev; 13462306a36Sopenharmony_ci u32 irq_en; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (!rdev) 13762306a36Sopenharmony_ci return; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* switch off and disable raw (edge) interrupts */ 14062306a36Sopenharmony_ci spin_lock_irq(&priv->lock); 14162306a36Sopenharmony_ci raw->rdev = NULL; 14262306a36Sopenharmony_ci irq_en = img_ir_read(priv, IMG_IR_IRQ_ENABLE); 14362306a36Sopenharmony_ci irq_en &= ~IMG_IR_IRQ_EDGE; 14462306a36Sopenharmony_ci img_ir_write(priv, IMG_IR_IRQ_ENABLE, irq_en); 14562306a36Sopenharmony_ci img_ir_write(priv, IMG_IR_IRQ_CLEAR, IMG_IR_IRQ_EDGE); 14662306a36Sopenharmony_ci spin_unlock_irq(&priv->lock); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci rc_unregister_device(rdev); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci del_timer_sync(&raw->timer); 15162306a36Sopenharmony_ci} 152