1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Driver for the remote control of SAA7146 based AV7110 cards
4 *
5 * Copyright (C) 1999-2003 Holger Waechtler <holger@convergence.de>
6 * Copyright (C) 2003-2007 Oliver Endriss <o.endriss@gmx.de>
7 * Copyright (C) 2019 Sean Young <sean@mess.org>
8 */
9
10#include <linux/kernel.h>
11#include <media/rc-core.h>
12
13#include "av7110.h"
14#include "av7110_hw.h"
15
16#define IR_RC5		0
17#define IR_RCMM		1
18#define IR_RC5_EXT	2 /* internal only */
19
20/* interrupt handler */
21void av7110_ir_handler(struct av7110 *av7110, u32 ircom)
22{
23	struct rc_dev *rcdev = av7110->ir.rcdev;
24	enum rc_proto proto;
25	u32 command, addr, scancode;
26	u32 toggle;
27
28	dprintk(4, "ir command = %08x\n", ircom);
29
30	if (rcdev) {
31		switch (av7110->ir.ir_config) {
32		case IR_RC5: /* RC5: 5 bits device address, 6 bits command */
33			command = ircom & 0x3f;
34			addr = (ircom >> 6) & 0x1f;
35			scancode = RC_SCANCODE_RC5(addr, command);
36			toggle = ircom & 0x0800;
37			proto = RC_PROTO_RC5;
38			break;
39
40		case IR_RCMM: /* RCMM: 32 bits scancode */
41			scancode = ircom & ~0x8000;
42			toggle = ircom & 0x8000;
43			proto = RC_PROTO_RCMM32;
44			break;
45
46		case IR_RC5_EXT:
47			/*
48			 * extended RC5: 5 bits device address, 7 bits command
49			 *
50			 * Extended RC5 uses only one start bit. The second
51			 * start bit is re-assigned bit 6 of the command bit.
52			 */
53			command = ircom & 0x3f;
54			addr = (ircom >> 6) & 0x1f;
55			if (!(ircom & 0x1000))
56				command |= 0x40;
57			scancode = RC_SCANCODE_RC5(addr, command);
58			toggle = ircom & 0x0800;
59			proto = RC_PROTO_RC5;
60			break;
61		default:
62			dprintk(2, "unknown ir config %d\n",
63				av7110->ir.ir_config);
64			return;
65		}
66
67		rc_keydown(rcdev, proto, scancode, toggle != 0);
68	}
69}
70
71int av7110_set_ir_config(struct av7110 *av7110)
72{
73	dprintk(4, "ir config = %08x\n", av7110->ir.ir_config);
74
75	return av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1,
76			     av7110->ir.ir_config);
77}
78
79static int change_protocol(struct rc_dev *rcdev, u64 *rc_type)
80{
81	struct av7110 *av7110 = rcdev->priv;
82	u32 ir_config;
83
84	if (*rc_type & RC_PROTO_BIT_RCMM32) {
85		ir_config = IR_RCMM;
86		*rc_type = RC_PROTO_BIT_RCMM32;
87	} else if (*rc_type & RC_PROTO_BIT_RC5) {
88		if (FW_VERSION(av7110->arm_app) >= 0x2620)
89			ir_config = IR_RC5_EXT;
90		else
91			ir_config = IR_RC5;
92		*rc_type = RC_PROTO_BIT_RC5;
93	} else {
94		return -EINVAL;
95	}
96
97	if (ir_config == av7110->ir.ir_config)
98		return 0;
99
100	av7110->ir.ir_config = ir_config;
101
102	return av7110_set_ir_config(av7110);
103}
104
105int av7110_ir_init(struct av7110 *av7110)
106{
107	struct rc_dev *rcdev;
108	struct pci_dev *pci;
109	int ret;
110
111	rcdev = rc_allocate_device(RC_DRIVER_SCANCODE);
112	if (!rcdev)
113		return -ENOMEM;
114
115	pci = av7110->dev->pci;
116
117	snprintf(av7110->ir.input_phys, sizeof(av7110->ir.input_phys),
118		 "pci-%s/ir0", pci_name(pci));
119
120	rcdev->device_name = av7110->card_name;
121	rcdev->driver_name = KBUILD_MODNAME;
122	rcdev->input_phys = av7110->ir.input_phys;
123	rcdev->input_id.bustype = BUS_PCI;
124	rcdev->input_id.version = 2;
125	if (pci->subsystem_vendor) {
126		rcdev->input_id.vendor	= pci->subsystem_vendor;
127		rcdev->input_id.product = pci->subsystem_device;
128	} else {
129		rcdev->input_id.vendor	= pci->vendor;
130		rcdev->input_id.product = pci->device;
131	}
132
133	rcdev->dev.parent = &pci->dev;
134	rcdev->allowed_protocols = RC_PROTO_BIT_RC5 | RC_PROTO_BIT_RCMM32;
135	rcdev->change_protocol = change_protocol;
136	rcdev->map_name = RC_MAP_HAUPPAUGE;
137	rcdev->priv = av7110;
138
139	av7110->ir.rcdev = rcdev;
140	av7110->ir.ir_config = IR_RC5;
141	av7110_set_ir_config(av7110);
142
143	ret = rc_register_device(rcdev);
144	if (ret) {
145		av7110->ir.rcdev = NULL;
146		rc_free_device(rcdev);
147	}
148
149	return ret;
150}
151
152void av7110_ir_exit(struct av7110 *av7110)
153{
154	rc_unregister_device(av7110->ir.rcdev);
155}
156
157//MODULE_AUTHOR("Holger Waechtler <holger@convergence.de>, Oliver Endriss <o.endriss@gmx.de>");
158//MODULE_LICENSE("GPL");
159