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