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 */
av7110_ir_handler(struct av7110 * av7110,u32 ircom)21 void 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", av7110->ir.ir_config);
63 return;
64 }
65
66 rc_keydown(rcdev, proto, scancode, toggle != 0);
67 }
68 }
69
av7110_set_ir_config(struct av7110 * av7110)70 int av7110_set_ir_config(struct av7110 *av7110)
71 {
72 dprintk(4, "ir config = %08x\n", av7110->ir.ir_config);
73
74 return av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1,
75 av7110->ir.ir_config);
76 }
77
change_protocol(struct rc_dev * rcdev,u64 * rc_type)78 static int change_protocol(struct rc_dev *rcdev, u64 *rc_type)
79 {
80 struct av7110 *av7110 = rcdev->priv;
81 u32 ir_config;
82
83 if (*rc_type & RC_PROTO_BIT_RCMM32) {
84 ir_config = IR_RCMM;
85 *rc_type = RC_PROTO_BIT_RCMM32;
86 } else if (*rc_type & RC_PROTO_BIT_RC5) {
87 if (FW_VERSION(av7110->arm_app) >= 0x2620)
88 ir_config = IR_RC5_EXT;
89 else
90 ir_config = IR_RC5;
91 *rc_type = RC_PROTO_BIT_RC5;
92 } else {
93 return -EINVAL;
94 }
95
96 if (ir_config == av7110->ir.ir_config)
97 return 0;
98
99 av7110->ir.ir_config = ir_config;
100
101 return av7110_set_ir_config(av7110);
102 }
103
av7110_ir_init(struct av7110 * av7110)104 int av7110_ir_init(struct av7110 *av7110)
105 {
106 struct rc_dev *rcdev;
107 struct pci_dev *pci;
108 int ret;
109
110 rcdev = rc_allocate_device(RC_DRIVER_SCANCODE);
111 if (!rcdev)
112 return -ENOMEM;
113
114 pci = av7110->dev->pci;
115
116 snprintf(av7110->ir.input_phys, sizeof(av7110->ir.input_phys),
117 "pci-%s/ir0", pci_name(pci));
118
119 rcdev->device_name = av7110->card_name;
120 rcdev->driver_name = KBUILD_MODNAME;
121 rcdev->input_phys = av7110->ir.input_phys;
122 rcdev->input_id.bustype = BUS_PCI;
123 rcdev->input_id.version = 2;
124 if (pci->subsystem_vendor) {
125 rcdev->input_id.vendor = pci->subsystem_vendor;
126 rcdev->input_id.product = pci->subsystem_device;
127 } else {
128 rcdev->input_id.vendor = pci->vendor;
129 rcdev->input_id.product = pci->device;
130 }
131
132 rcdev->dev.parent = &pci->dev;
133 rcdev->allowed_protocols = RC_PROTO_BIT_RC5 | RC_PROTO_BIT_RCMM32;
134 rcdev->change_protocol = change_protocol;
135 rcdev->map_name = RC_MAP_HAUPPAUGE;
136 rcdev->priv = av7110;
137
138 av7110->ir.rcdev = rcdev;
139 av7110->ir.ir_config = IR_RC5;
140 av7110_set_ir_config(av7110);
141
142 ret = rc_register_device(rcdev);
143 if (ret) {
144 av7110->ir.rcdev = NULL;
145 rc_free_device(rcdev);
146 }
147
148 return ret;
149 }
150
av7110_ir_exit(struct av7110 * av7110)151 void av7110_ir_exit(struct av7110 *av7110)
152 {
153 rc_unregister_device(av7110->ir.rcdev);
154 }
155
156 //MODULE_AUTHOR("Holger Waechtler <holger@convergence.de>, Oliver Endriss <o.endriss@gmx.de>");
157 //MODULE_LICENSE("GPL");
158