18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * ImgTec IR Raw Decoder found in PowerDown Controller.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2010-2014 Imagination Technologies Ltd.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * This ties into the input subsystem using the RC-core in raw mode. Raw IR
88c2ecf20Sopenharmony_ci * signal edges are reported and decoded by generic software decoders.
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
128c2ecf20Sopenharmony_ci#include <media/rc-core.h>
138c2ecf20Sopenharmony_ci#include "img-ir.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#define ECHO_TIMEOUT_MS 150	/* ms between echos */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci/* must be called with priv->lock held */
188c2ecf20Sopenharmony_cistatic void img_ir_refresh_raw(struct img_ir_priv *priv, u32 irq_status)
198c2ecf20Sopenharmony_ci{
208c2ecf20Sopenharmony_ci	struct img_ir_priv_raw *raw = &priv->raw;
218c2ecf20Sopenharmony_ci	struct rc_dev *rc_dev = priv->raw.rdev;
228c2ecf20Sopenharmony_ci	int multiple;
238c2ecf20Sopenharmony_ci	u32 ir_status;
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	/* find whether both rise and fall was detected */
268c2ecf20Sopenharmony_ci	multiple = ((irq_status & IMG_IR_IRQ_EDGE) == IMG_IR_IRQ_EDGE);
278c2ecf20Sopenharmony_ci	/*
288c2ecf20Sopenharmony_ci	 * If so, we need to see if the level has actually changed.
298c2ecf20Sopenharmony_ci	 * If it's just noise that we didn't have time to process,
308c2ecf20Sopenharmony_ci	 * there's no point reporting it.
318c2ecf20Sopenharmony_ci	 */
328c2ecf20Sopenharmony_ci	ir_status = img_ir_read(priv, IMG_IR_STATUS) & IMG_IR_IRRXD;
338c2ecf20Sopenharmony_ci	if (multiple && ir_status == raw->last_status)
348c2ecf20Sopenharmony_ci		return;
358c2ecf20Sopenharmony_ci	raw->last_status = ir_status;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	/* report the edge to the IR raw decoders */
388c2ecf20Sopenharmony_ci	if (ir_status) /* low */
398c2ecf20Sopenharmony_ci		ir_raw_event_store_edge(rc_dev, false);
408c2ecf20Sopenharmony_ci	else /* high */
418c2ecf20Sopenharmony_ci		ir_raw_event_store_edge(rc_dev, true);
428c2ecf20Sopenharmony_ci	ir_raw_event_handle(rc_dev);
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci/* called with priv->lock held */
468c2ecf20Sopenharmony_civoid img_ir_isr_raw(struct img_ir_priv *priv, u32 irq_status)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	struct img_ir_priv_raw *raw = &priv->raw;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	/* check not removing */
518c2ecf20Sopenharmony_ci	if (!raw->rdev)
528c2ecf20Sopenharmony_ci		return;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	img_ir_refresh_raw(priv, irq_status);
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	/* start / push back the echo timer */
578c2ecf20Sopenharmony_ci	mod_timer(&raw->timer, jiffies + msecs_to_jiffies(ECHO_TIMEOUT_MS));
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci/*
618c2ecf20Sopenharmony_ci * Echo timer callback function.
628c2ecf20Sopenharmony_ci * The raw decoders expect to get a final sample even if there are no edges, in
638c2ecf20Sopenharmony_ci * order to be assured of the final space. If there are no edges for a certain
648c2ecf20Sopenharmony_ci * time we use this timer to emit a final sample to satisfy them.
658c2ecf20Sopenharmony_ci */
668c2ecf20Sopenharmony_cistatic void img_ir_echo_timer(struct timer_list *t)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	struct img_ir_priv *priv = from_timer(priv, t, raw.timer);
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	spin_lock_irq(&priv->lock);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	/* check not removing */
738c2ecf20Sopenharmony_ci	if (priv->raw.rdev)
748c2ecf20Sopenharmony_ci		/*
758c2ecf20Sopenharmony_ci		 * It's safe to pass irq_status=0 since it's only used to check
768c2ecf20Sopenharmony_ci		 * for double edges.
778c2ecf20Sopenharmony_ci		 */
788c2ecf20Sopenharmony_ci		img_ir_refresh_raw(priv, 0);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	spin_unlock_irq(&priv->lock);
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_civoid img_ir_setup_raw(struct img_ir_priv *priv)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	u32 irq_en;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	if (!priv->raw.rdev)
888c2ecf20Sopenharmony_ci		return;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	/* clear and enable edge interrupts */
918c2ecf20Sopenharmony_ci	spin_lock_irq(&priv->lock);
928c2ecf20Sopenharmony_ci	irq_en = img_ir_read(priv, IMG_IR_IRQ_ENABLE);
938c2ecf20Sopenharmony_ci	irq_en |= IMG_IR_IRQ_EDGE;
948c2ecf20Sopenharmony_ci	img_ir_write(priv, IMG_IR_IRQ_CLEAR, IMG_IR_IRQ_EDGE);
958c2ecf20Sopenharmony_ci	img_ir_write(priv, IMG_IR_IRQ_ENABLE, irq_en);
968c2ecf20Sopenharmony_ci	spin_unlock_irq(&priv->lock);
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ciint img_ir_probe_raw(struct img_ir_priv *priv)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	struct img_ir_priv_raw *raw = &priv->raw;
1028c2ecf20Sopenharmony_ci	struct rc_dev *rdev;
1038c2ecf20Sopenharmony_ci	int error;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	/* Set up the echo timer */
1068c2ecf20Sopenharmony_ci	timer_setup(&raw->timer, img_ir_echo_timer, 0);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	/* Allocate raw decoder */
1098c2ecf20Sopenharmony_ci	raw->rdev = rdev = rc_allocate_device(RC_DRIVER_IR_RAW);
1108c2ecf20Sopenharmony_ci	if (!rdev) {
1118c2ecf20Sopenharmony_ci		dev_err(priv->dev, "cannot allocate raw input device\n");
1128c2ecf20Sopenharmony_ci		return -ENOMEM;
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci	rdev->priv = priv;
1158c2ecf20Sopenharmony_ci	rdev->map_name = RC_MAP_EMPTY;
1168c2ecf20Sopenharmony_ci	rdev->device_name = "IMG Infrared Decoder Raw";
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	/* Register raw decoder */
1198c2ecf20Sopenharmony_ci	error = rc_register_device(rdev);
1208c2ecf20Sopenharmony_ci	if (error) {
1218c2ecf20Sopenharmony_ci		dev_err(priv->dev, "failed to register raw IR input device\n");
1228c2ecf20Sopenharmony_ci		rc_free_device(rdev);
1238c2ecf20Sopenharmony_ci		raw->rdev = NULL;
1248c2ecf20Sopenharmony_ci		return error;
1258c2ecf20Sopenharmony_ci	}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	return 0;
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_civoid img_ir_remove_raw(struct img_ir_priv *priv)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	struct img_ir_priv_raw *raw = &priv->raw;
1338c2ecf20Sopenharmony_ci	struct rc_dev *rdev = raw->rdev;
1348c2ecf20Sopenharmony_ci	u32 irq_en;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	if (!rdev)
1378c2ecf20Sopenharmony_ci		return;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	/* switch off and disable raw (edge) interrupts */
1408c2ecf20Sopenharmony_ci	spin_lock_irq(&priv->lock);
1418c2ecf20Sopenharmony_ci	raw->rdev = NULL;
1428c2ecf20Sopenharmony_ci	irq_en = img_ir_read(priv, IMG_IR_IRQ_ENABLE);
1438c2ecf20Sopenharmony_ci	irq_en &= ~IMG_IR_IRQ_EDGE;
1448c2ecf20Sopenharmony_ci	img_ir_write(priv, IMG_IR_IRQ_ENABLE, irq_en);
1458c2ecf20Sopenharmony_ci	img_ir_write(priv, IMG_IR_IRQ_CLEAR, IMG_IR_IRQ_EDGE);
1468c2ecf20Sopenharmony_ci	spin_unlock_irq(&priv->lock);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	rc_unregister_device(rdev);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	del_timer_sync(&raw->timer);
1518c2ecf20Sopenharmony_ci}
152