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 */ |
18 | static 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(dev: rc_dev, pulse: false); |
40 | else /* high */ |
41 | ir_raw_event_store_edge(dev: rc_dev, pulse: true); |
42 | ir_raw_event_handle(dev: rc_dev); |
43 | } |
44 | |
45 | /* called with priv->lock held */ |
46 | void 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(timer: &raw->timer, expires: 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 | */ |
66 | static 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(lock: &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, irq_status: 0); |
79 | |
80 | spin_unlock_irq(lock: &priv->lock); |
81 | } |
82 | |
83 | void 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(lock: &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, data: irq_en); |
96 | spin_unlock_irq(lock: &priv->lock); |
97 | } |
98 | |
99 | int 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(dev: rdev); |
120 | if (error) { |
121 | dev_err(priv->dev, "failed to register raw IR input device\n" ); |
122 | rc_free_device(dev: rdev); |
123 | raw->rdev = NULL; |
124 | return error; |
125 | } |
126 | |
127 | return 0; |
128 | } |
129 | |
130 | void 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(lock: &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, data: irq_en); |
145 | img_ir_write(priv, IMG_IR_IRQ_CLEAR, IMG_IR_IRQ_EDGE); |
146 | spin_unlock_irq(lock: &priv->lock); |
147 | |
148 | rc_unregister_device(dev: rdev); |
149 | |
150 | del_timer_sync(timer: &raw->timer); |
151 | } |
152 | |