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