1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | // handle au0828 IR remotes via linux kernel input layer. |
3 | // |
4 | // Copyright (c) 2014 Mauro Carvalho Chehab <mchehab@samsung.com> |
5 | // Copyright (c) 2014 Samsung Electronics Co., Ltd. |
6 | // |
7 | // Based on em28xx-input.c. |
8 | |
9 | #include "au0828.h" |
10 | |
11 | #include <linux/module.h> |
12 | #include <linux/init.h> |
13 | #include <linux/delay.h> |
14 | #include <linux/interrupt.h> |
15 | #include <linux/usb.h> |
16 | #include <linux/slab.h> |
17 | #include <media/rc-core.h> |
18 | |
19 | static int disable_ir; |
20 | module_param(disable_ir, int, 0444); |
21 | MODULE_PARM_DESC(disable_ir, "disable infrared remote support" ); |
22 | |
23 | struct au0828_rc { |
24 | struct au0828_dev *dev; |
25 | struct rc_dev *rc; |
26 | char name[32]; |
27 | char phys[32]; |
28 | |
29 | /* poll decoder */ |
30 | int polling; |
31 | struct delayed_work work; |
32 | |
33 | /* i2c slave address of external device (if used) */ |
34 | u16 i2c_dev_addr; |
35 | |
36 | int (*get_key_i2c)(struct au0828_rc *ir); |
37 | }; |
38 | |
39 | /* |
40 | * AU8522 has a builtin IR receiver. Add functions to get IR from it |
41 | */ |
42 | |
43 | static int au8522_rc_write(struct au0828_rc *ir, u16 reg, u8 data) |
44 | { |
45 | int rc; |
46 | char buf[] = { (reg >> 8) | 0x80, reg & 0xff, data }; |
47 | struct i2c_msg msg = { .addr = ir->i2c_dev_addr, .flags = 0, |
48 | .buf = buf, .len = sizeof(buf) }; |
49 | |
50 | rc = i2c_transfer(adap: ir->dev->i2c_client.adapter, msgs: &msg, num: 1); |
51 | |
52 | if (rc < 0) |
53 | return rc; |
54 | |
55 | return (rc == 1) ? 0 : -EIO; |
56 | } |
57 | |
58 | static int au8522_rc_read(struct au0828_rc *ir, u16 reg, int val, |
59 | char *buf, int size) |
60 | { |
61 | int rc; |
62 | char obuf[3]; |
63 | struct i2c_msg msg[2] = { { .addr = ir->i2c_dev_addr, .flags = 0, |
64 | .buf = obuf, .len = 2 }, |
65 | { .addr = ir->i2c_dev_addr, .flags = I2C_M_RD, |
66 | .buf = buf, .len = size } }; |
67 | |
68 | obuf[0] = 0x40 | reg >> 8; |
69 | obuf[1] = reg & 0xff; |
70 | if (val >= 0) { |
71 | obuf[2] = val; |
72 | msg[0].len++; |
73 | } |
74 | |
75 | rc = i2c_transfer(adap: ir->dev->i2c_client.adapter, msgs: msg, num: 2); |
76 | |
77 | if (rc < 0) |
78 | return rc; |
79 | |
80 | return (rc == 2) ? 0 : -EIO; |
81 | } |
82 | |
83 | static int au8522_rc_andor(struct au0828_rc *ir, u16 reg, u8 mask, u8 value) |
84 | { |
85 | int rc; |
86 | char buf, oldbuf; |
87 | |
88 | rc = au8522_rc_read(ir, reg, val: -1, buf: &buf, size: 1); |
89 | if (rc < 0) |
90 | return rc; |
91 | |
92 | oldbuf = buf; |
93 | buf = (buf & ~mask) | (value & mask); |
94 | |
95 | /* Nothing to do, just return */ |
96 | if (buf == oldbuf) |
97 | return 0; |
98 | |
99 | return au8522_rc_write(ir, reg, data: buf); |
100 | } |
101 | |
102 | #define au8522_rc_set(ir, reg, bit) au8522_rc_andor(ir, (reg), (bit), (bit)) |
103 | #define au8522_rc_clear(ir, reg, bit) au8522_rc_andor(ir, (reg), (bit), 0) |
104 | |
105 | /* Remote Controller time units */ |
106 | |
107 | #define AU8522_UNIT 200 /* us */ |
108 | #define NEC_START_SPACE (4500 / AU8522_UNIT) |
109 | #define NEC_START_PULSE (563 * 16) |
110 | #define RC5_START_SPACE (4 * AU8522_UNIT) |
111 | #define RC5_START_PULSE 889 |
112 | |
113 | static int au0828_get_key_au8522(struct au0828_rc *ir) |
114 | { |
115 | unsigned char buf[40]; |
116 | struct ir_raw_event rawir = {}; |
117 | int i, j, rc; |
118 | int prv_bit, bit, width; |
119 | bool first = true; |
120 | |
121 | /* do nothing if device is disconnected */ |
122 | if (test_bit(DEV_DISCONNECTED, &ir->dev->dev_state)) |
123 | return 0; |
124 | |
125 | /* Check IR int */ |
126 | rc = au8522_rc_read(ir, reg: 0xe1, val: -1, buf, size: 1); |
127 | if (rc < 0 || !(buf[0] & (1 << 4))) { |
128 | /* Be sure that IR is enabled */ |
129 | au8522_rc_set(ir, 0xe0, 1 << 4); |
130 | return 0; |
131 | } |
132 | |
133 | /* Something arrived. Get the data */ |
134 | rc = au8522_rc_read(ir, reg: 0xe3, val: 0x11, buf, size: sizeof(buf)); |
135 | |
136 | |
137 | if (rc < 0) |
138 | return rc; |
139 | |
140 | /* Disable IR */ |
141 | au8522_rc_clear(ir, 0xe0, 1 << 4); |
142 | |
143 | /* Enable IR */ |
144 | au8522_rc_set(ir, 0xe0, 1 << 4); |
145 | |
146 | dprintk(16, "RC data received: %*ph\n" , 40, buf); |
147 | |
148 | prv_bit = (buf[0] >> 7) & 0x01; |
149 | width = 0; |
150 | for (i = 0; i < sizeof(buf); i++) { |
151 | for (j = 7; j >= 0; j--) { |
152 | bit = (buf[i] >> j) & 0x01; |
153 | if (bit == prv_bit) { |
154 | width++; |
155 | continue; |
156 | } |
157 | |
158 | /* |
159 | * Fix an au8522 bug: the first pulse event |
160 | * is lost. So, we need to fake it, based on the |
161 | * protocol. That means that not all raw decoders |
162 | * will work, as we need to add a hack for each |
163 | * protocol, based on the first space. |
164 | * So, we only support RC5 and NEC. |
165 | */ |
166 | |
167 | if (first) { |
168 | first = false; |
169 | |
170 | rawir.pulse = true; |
171 | if (width > NEC_START_SPACE - 2 && |
172 | width < NEC_START_SPACE + 2) { |
173 | /* NEC protocol */ |
174 | rawir.duration = NEC_START_PULSE; |
175 | dprintk(16, "Storing NEC start %s with duration %d" , |
176 | rawir.pulse ? "pulse" : "space" , |
177 | rawir.duration); |
178 | } else { |
179 | /* RC5 protocol */ |
180 | rawir.duration = RC5_START_PULSE; |
181 | dprintk(16, "Storing RC5 start %s with duration %d" , |
182 | rawir.pulse ? "pulse" : "space" , |
183 | rawir.duration); |
184 | } |
185 | ir_raw_event_store(dev: ir->rc, ev: &rawir); |
186 | } |
187 | |
188 | rawir.pulse = prv_bit ? false : true; |
189 | rawir.duration = AU8522_UNIT * width; |
190 | dprintk(16, "Storing %s with duration %d" , |
191 | rawir.pulse ? "pulse" : "space" , |
192 | rawir.duration); |
193 | ir_raw_event_store(dev: ir->rc, ev: &rawir); |
194 | |
195 | width = 1; |
196 | prv_bit = bit; |
197 | } |
198 | } |
199 | |
200 | rawir.pulse = prv_bit ? false : true; |
201 | rawir.duration = AU8522_UNIT * width; |
202 | dprintk(16, "Storing end %s with duration %d" , |
203 | rawir.pulse ? "pulse" : "space" , |
204 | rawir.duration); |
205 | ir_raw_event_store(dev: ir->rc, ev: &rawir); |
206 | |
207 | ir_raw_event_handle(dev: ir->rc); |
208 | |
209 | return 1; |
210 | } |
211 | |
212 | /* |
213 | * Generic IR code |
214 | */ |
215 | |
216 | static void au0828_rc_work(struct work_struct *work) |
217 | { |
218 | struct au0828_rc *ir = container_of(work, struct au0828_rc, work.work); |
219 | int rc; |
220 | |
221 | rc = ir->get_key_i2c(ir); |
222 | if (rc < 0) |
223 | pr_info("Error while getting RC scancode\n" ); |
224 | |
225 | schedule_delayed_work(dwork: &ir->work, delay: msecs_to_jiffies(m: ir->polling)); |
226 | } |
227 | |
228 | static int au0828_rc_start(struct rc_dev *rc) |
229 | { |
230 | struct au0828_rc *ir = rc->priv; |
231 | |
232 | INIT_DELAYED_WORK(&ir->work, au0828_rc_work); |
233 | |
234 | /* Enable IR */ |
235 | au8522_rc_set(ir, 0xe0, 1 << 4); |
236 | |
237 | schedule_delayed_work(dwork: &ir->work, delay: msecs_to_jiffies(m: ir->polling)); |
238 | |
239 | return 0; |
240 | } |
241 | |
242 | static void au0828_rc_stop(struct rc_dev *rc) |
243 | { |
244 | struct au0828_rc *ir = rc->priv; |
245 | |
246 | cancel_delayed_work_sync(dwork: &ir->work); |
247 | |
248 | /* do nothing if device is disconnected */ |
249 | if (!test_bit(DEV_DISCONNECTED, &ir->dev->dev_state)) { |
250 | /* Disable IR */ |
251 | au8522_rc_clear(ir, 0xe0, 1 << 4); |
252 | } |
253 | } |
254 | |
255 | static int au0828_probe_i2c_ir(struct au0828_dev *dev) |
256 | { |
257 | int i = 0; |
258 | static const unsigned short addr_list[] = { |
259 | 0x47, I2C_CLIENT_END |
260 | }; |
261 | |
262 | while (addr_list[i] != I2C_CLIENT_END) { |
263 | if (i2c_probe_func_quick_read(adap: dev->i2c_client.adapter, |
264 | addr: addr_list[i]) == 1) |
265 | return addr_list[i]; |
266 | i++; |
267 | } |
268 | |
269 | return -ENODEV; |
270 | } |
271 | |
272 | int au0828_rc_register(struct au0828_dev *dev) |
273 | { |
274 | struct au0828_rc *ir; |
275 | struct rc_dev *rc; |
276 | int err = -ENOMEM; |
277 | u16 i2c_rc_dev_addr = 0; |
278 | |
279 | if (!dev->board.has_ir_i2c || disable_ir) |
280 | return 0; |
281 | |
282 | i2c_rc_dev_addr = au0828_probe_i2c_ir(dev); |
283 | if (!i2c_rc_dev_addr) |
284 | return -ENODEV; |
285 | |
286 | ir = kzalloc(size: sizeof(*ir), GFP_KERNEL); |
287 | rc = rc_allocate_device(RC_DRIVER_IR_RAW); |
288 | if (!ir || !rc) |
289 | goto error; |
290 | |
291 | /* record handles to ourself */ |
292 | ir->dev = dev; |
293 | dev->ir = ir; |
294 | ir->rc = rc; |
295 | |
296 | rc->priv = ir; |
297 | rc->open = au0828_rc_start; |
298 | rc->close = au0828_rc_stop; |
299 | |
300 | if (dev->board.has_ir_i2c) { /* external i2c device */ |
301 | switch (dev->boardnr) { |
302 | case AU0828_BOARD_HAUPPAUGE_HVR950Q: |
303 | rc->map_name = RC_MAP_HAUPPAUGE; |
304 | ir->get_key_i2c = au0828_get_key_au8522; |
305 | break; |
306 | default: |
307 | err = -ENODEV; |
308 | goto error; |
309 | } |
310 | |
311 | ir->i2c_dev_addr = i2c_rc_dev_addr; |
312 | } |
313 | |
314 | /* This is how often we ask the chip for IR information */ |
315 | ir->polling = 100; /* ms */ |
316 | |
317 | /* init input device */ |
318 | snprintf(buf: ir->name, size: sizeof(ir->name), fmt: "au0828 IR (%s)" , |
319 | dev->board.name); |
320 | |
321 | usb_make_path(dev: dev->usbdev, buf: ir->phys, size: sizeof(ir->phys)); |
322 | strlcat(p: ir->phys, q: "/input0" , avail: sizeof(ir->phys)); |
323 | |
324 | rc->device_name = ir->name; |
325 | rc->input_phys = ir->phys; |
326 | rc->input_id.bustype = BUS_USB; |
327 | rc->input_id.version = 1; |
328 | rc->input_id.vendor = le16_to_cpu(dev->usbdev->descriptor.idVendor); |
329 | rc->input_id.product = le16_to_cpu(dev->usbdev->descriptor.idProduct); |
330 | rc->dev.parent = &dev->usbdev->dev; |
331 | rc->driver_name = "au0828-input" ; |
332 | rc->allowed_protocols = RC_PROTO_BIT_NEC | RC_PROTO_BIT_NECX | |
333 | RC_PROTO_BIT_NEC32 | RC_PROTO_BIT_RC5; |
334 | |
335 | /* all done */ |
336 | err = rc_register_device(dev: rc); |
337 | if (err) |
338 | goto error; |
339 | |
340 | pr_info("Remote controller %s initialized\n" , ir->name); |
341 | |
342 | return 0; |
343 | |
344 | error: |
345 | dev->ir = NULL; |
346 | rc_free_device(dev: rc); |
347 | kfree(objp: ir); |
348 | return err; |
349 | } |
350 | |
351 | void au0828_rc_unregister(struct au0828_dev *dev) |
352 | { |
353 | struct au0828_rc *ir = dev->ir; |
354 | |
355 | /* skip detach on non attached boards */ |
356 | if (!ir) |
357 | return; |
358 | |
359 | rc_unregister_device(dev: ir->rc); |
360 | |
361 | /* done */ |
362 | kfree(objp: ir); |
363 | dev->ir = NULL; |
364 | } |
365 | |
366 | int au0828_rc_suspend(struct au0828_dev *dev) |
367 | { |
368 | struct au0828_rc *ir = dev->ir; |
369 | |
370 | if (!ir) |
371 | return 0; |
372 | |
373 | pr_info("Stopping RC\n" ); |
374 | |
375 | cancel_delayed_work_sync(dwork: &ir->work); |
376 | |
377 | /* Disable IR */ |
378 | au8522_rc_clear(ir, 0xe0, 1 << 4); |
379 | |
380 | return 0; |
381 | } |
382 | |
383 | int au0828_rc_resume(struct au0828_dev *dev) |
384 | { |
385 | struct au0828_rc *ir = dev->ir; |
386 | |
387 | if (!ir) |
388 | return 0; |
389 | |
390 | pr_info("Restarting RC\n" ); |
391 | |
392 | /* Enable IR */ |
393 | au8522_rc_set(ir, 0xe0, 1 << 4); |
394 | |
395 | schedule_delayed_work(dwork: &ir->work, delay: msecs_to_jiffies(m: ir->polling)); |
396 | |
397 | return 0; |
398 | } |
399 | |