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