1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * ImgTec IR Raw Decoder found in PowerDown Controller.
4 *
5 * Copyright 2010-2014 Imagination Technologies Ltd.
6 *
7 * This ties into the input subsystem using the RC-core in raw mode. Raw IR
8 * signal edges are reported and decoded by generic software decoders.
9 */
10
11#include <linux/spinlock.h>
12#include <media/rc-core.h>
13#include "img-ir.h"
14
15#define ECHO_TIMEOUT_MS 150	/* ms between echos */
16
17/* must be called with priv->lock held */
18static void img_ir_refresh_raw(struct img_ir_priv *priv, u32 irq_status)
19{
20	struct img_ir_priv_raw *raw = &priv->raw;
21	struct rc_dev *rc_dev = priv->raw.rdev;
22	int multiple;
23	u32 ir_status;
24
25	/* find whether both rise and fall was detected */
26	multiple = ((irq_status & IMG_IR_IRQ_EDGE) == IMG_IR_IRQ_EDGE);
27	/*
28	 * If so, we need to see if the level has actually changed.
29	 * If it's just noise that we didn't have time to process,
30	 * there's no point reporting it.
31	 */
32	ir_status = img_ir_read(priv, IMG_IR_STATUS) & IMG_IR_IRRXD;
33	if (multiple && ir_status == raw->last_status)
34		return;
35	raw->last_status = ir_status;
36
37	/* report the edge to the IR raw decoders */
38	if (ir_status) /* low */
39		ir_raw_event_store_edge(rc_dev, false);
40	else /* high */
41		ir_raw_event_store_edge(rc_dev, true);
42	ir_raw_event_handle(rc_dev);
43}
44
45/* called with priv->lock held */
46void img_ir_isr_raw(struct img_ir_priv *priv, u32 irq_status)
47{
48	struct img_ir_priv_raw *raw = &priv->raw;
49
50	/* check not removing */
51	if (!raw->rdev)
52		return;
53
54	img_ir_refresh_raw(priv, irq_status);
55
56	/* start / push back the echo timer */
57	mod_timer(&raw->timer, jiffies + msecs_to_jiffies(ECHO_TIMEOUT_MS));
58}
59
60/*
61 * Echo timer callback function.
62 * The raw decoders expect to get a final sample even if there are no edges, in
63 * order to be assured of the final space. If there are no edges for a certain
64 * time we use this timer to emit a final sample to satisfy them.
65 */
66static void img_ir_echo_timer(struct timer_list *t)
67{
68	struct img_ir_priv *priv = from_timer(priv, t, raw.timer);
69
70	spin_lock_irq(&priv->lock);
71
72	/* check not removing */
73	if (priv->raw.rdev)
74		/*
75		 * It's safe to pass irq_status=0 since it's only used to check
76		 * for double edges.
77		 */
78		img_ir_refresh_raw(priv, 0);
79
80	spin_unlock_irq(&priv->lock);
81}
82
83void img_ir_setup_raw(struct img_ir_priv *priv)
84{
85	u32 irq_en;
86
87	if (!priv->raw.rdev)
88		return;
89
90	/* clear and enable edge interrupts */
91	spin_lock_irq(&priv->lock);
92	irq_en = img_ir_read(priv, IMG_IR_IRQ_ENABLE);
93	irq_en |= IMG_IR_IRQ_EDGE;
94	img_ir_write(priv, IMG_IR_IRQ_CLEAR, IMG_IR_IRQ_EDGE);
95	img_ir_write(priv, IMG_IR_IRQ_ENABLE, irq_en);
96	spin_unlock_irq(&priv->lock);
97}
98
99int img_ir_probe_raw(struct img_ir_priv *priv)
100{
101	struct img_ir_priv_raw *raw = &priv->raw;
102	struct rc_dev *rdev;
103	int error;
104
105	/* Set up the echo timer */
106	timer_setup(&raw->timer, img_ir_echo_timer, 0);
107
108	/* Allocate raw decoder */
109	raw->rdev = rdev = rc_allocate_device(RC_DRIVER_IR_RAW);
110	if (!rdev) {
111		dev_err(priv->dev, "cannot allocate raw input device\n");
112		return -ENOMEM;
113	}
114	rdev->priv = priv;
115	rdev->map_name = RC_MAP_EMPTY;
116	rdev->device_name = "IMG Infrared Decoder Raw";
117
118	/* Register raw decoder */
119	error = rc_register_device(rdev);
120	if (error) {
121		dev_err(priv->dev, "failed to register raw IR input device\n");
122		rc_free_device(rdev);
123		raw->rdev = NULL;
124		return error;
125	}
126
127	return 0;
128}
129
130void img_ir_remove_raw(struct img_ir_priv *priv)
131{
132	struct img_ir_priv_raw *raw = &priv->raw;
133	struct rc_dev *rdev = raw->rdev;
134	u32 irq_en;
135
136	if (!rdev)
137		return;
138
139	/* switch off and disable raw (edge) interrupts */
140	spin_lock_irq(&priv->lock);
141	raw->rdev = NULL;
142	irq_en = img_ir_read(priv, IMG_IR_IRQ_ENABLE);
143	irq_en &= ~IMG_IR_IRQ_EDGE;
144	img_ir_write(priv, IMG_IR_IRQ_ENABLE, irq_en);
145	img_ir_write(priv, IMG_IR_IRQ_CLEAR, IMG_IR_IRQ_EDGE);
146	spin_unlock_irq(&priv->lock);
147
148	rc_unregister_device(rdev);
149
150	del_timer_sync(&raw->timer);
151}
152