432789a3c312637d0567ed5e1dee2db25ffb030b
[sfrench/cifs-2.6.git] / drivers / media / pci / ttpci / av7110_ir.c
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 */
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: ? bits device address, ? bits command */
41                         command = ircom & 0xff;
42                         addr = (ircom >> 8) & 0x1f;
43                         scancode = ircom;
44                         toggle = ircom & 0x8000;
45                         proto = RC_PROTO_UNKNOWN;
46                         break;
47
48                 case IR_RC5_EXT:
49                         /*
50                          * extended RC5: 5 bits device address, 7 bits command
51                          *
52                          * Extended RC5 uses only one start bit. The second
53                          * start bit is re-assigned bit 6 of the command bit.
54                          */
55                         command = ircom & 0x3f;
56                         addr = (ircom >> 6) & 0x1f;
57                         if (!(ircom & 0x1000))
58                                 command |= 0x40;
59                         scancode = RC_SCANCODE_RC5(addr, command);
60                         toggle = ircom & 0x0800;
61                         proto = RC_PROTO_RC5;
62                         break;
63                 default:
64                         dprintk(2, "unknown ir config %d\n",
65                                 av7110->ir.ir_config);
66                         return;
67                 }
68
69                 rc_keydown(rcdev, proto, scancode, toggle != 0);
70         }
71 }
72
73 int av7110_set_ir_config(struct av7110 *av7110)
74 {
75         dprintk(4, "ir config = %08x\n", av7110->ir.ir_config);
76
77         return av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1,
78                              av7110->ir.ir_config);
79 }
80
81 static int change_protocol(struct rc_dev *rcdev, u64 *rc_type)
82 {
83         struct av7110 *av7110 = rcdev->priv;
84         u32 ir_config;
85
86         if (*rc_type & RC_PROTO_BIT_UNKNOWN) {
87                 ir_config = IR_RCMM;
88                 *rc_type = RC_PROTO_UNKNOWN;
89         } else if (*rc_type & RC_PROTO_BIT_RC5) {
90                 if (FW_VERSION(av7110->arm_app) >= 0x2620)
91                         ir_config = IR_RC5_EXT;
92                 else
93                         ir_config = IR_RC5;
94                 *rc_type = RC_PROTO_BIT_RC5;
95         } else {
96                 return -EINVAL;
97         }
98
99         if (ir_config == av7110->ir.ir_config)
100                 return 0;
101
102         av7110->ir.ir_config = ir_config;
103
104         return av7110_set_ir_config(av7110);
105 }
106
107 int av7110_ir_init(struct av7110 *av7110)
108 {
109         struct rc_dev *rcdev;
110         struct pci_dev *pci;
111         int ret;
112
113         rcdev = rc_allocate_device(RC_DRIVER_SCANCODE);
114         if (!rcdev)
115                 return -ENOMEM;
116
117         pci = av7110->dev->pci;
118
119         snprintf(av7110->ir.input_phys, sizeof(av7110->ir.input_phys),
120                  "pci-%s/ir0", pci_name(pci));
121
122         rcdev->device_name = av7110->card_name;
123         rcdev->driver_name = KBUILD_MODNAME;
124         rcdev->input_phys = av7110->ir.input_phys;
125         rcdev->input_id.bustype = BUS_PCI;
126         rcdev->input_id.version = 2;
127         if (pci->subsystem_vendor) {
128                 rcdev->input_id.vendor  = pci->subsystem_vendor;
129                 rcdev->input_id.product = pci->subsystem_device;
130         } else {
131                 rcdev->input_id.vendor  = pci->vendor;
132                 rcdev->input_id.product = pci->device;
133         }
134
135         rcdev->dev.parent = &pci->dev;
136         rcdev->allowed_protocols = RC_PROTO_BIT_RC5 | RC_PROTO_BIT_UNKNOWN;
137         rcdev->change_protocol = change_protocol;
138         rcdev->map_name = RC_MAP_HAUPPAUGE;
139         rcdev->priv = av7110;
140
141         av7110->ir.rcdev = rcdev;
142         av7110->ir.ir_config = IR_RC5;
143         av7110_set_ir_config(av7110);
144
145         ret = rc_register_device(rcdev);
146         if (ret) {
147                 av7110->ir.rcdev = NULL;
148                 rc_free_device(rcdev);
149         }
150
151         return ret;
152 }
153
154 void av7110_ir_exit(struct av7110 *av7110)
155 {
156         rc_unregister_device(av7110->ir.rcdev);
157 }
158
159 //MODULE_AUTHOR("Holger Waechtler <holger@convergence.de>, Oliver Endriss <o.endriss@gmx.de>");
160 //MODULE_LICENSE("GPL");