1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Driver for the remote control of SAA7146 based AV7110 cards
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>
10 #include <linux/kernel.h>
11 #include <media/rc-core.h>
14 #include "av7110_hw.h"
18 #define IR_RC5_EXT 2 /* internal only */
20 /* interrupt handler */
21 void av7110_ir_handler(struct av7110
*av7110
, u32 ircom
)
23 struct rc_dev
*rcdev
= av7110
->ir
.rcdev
;
25 u32 command
, addr
, scancode
;
28 dprintk(4, "ir command = %08x\n", ircom
);
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;
40 case IR_RCMM
: /* RCMM: 32 bits scancode */
41 scancode
= ircom
& ~0x8000;
42 toggle
= ircom
& 0x8000;
43 proto
= RC_PROTO_RCMM32
;
48 * extended RC5: 5 bits device address, 7 bits command
50 * Extended RC5 uses only one start bit. The second
51 * start bit is re-assigned bit 6 of the command bit.
53 command
= ircom
& 0x3f;
54 addr
= (ircom
>> 6) & 0x1f;
55 if (!(ircom
& 0x1000))
57 scancode
= RC_SCANCODE_RC5(addr
, command
);
58 toggle
= ircom
& 0x0800;
62 dprintk(2, "unknown ir config %d\n",
63 av7110
->ir
.ir_config
);
67 rc_keydown(rcdev
, proto
, scancode
, toggle
!= 0);
71 int av7110_set_ir_config(struct av7110
*av7110
)
73 dprintk(4, "ir config = %08x\n", av7110
->ir
.ir_config
);
75 return av7110_fw_cmd(av7110
, COMTYPE_PIDFILTER
, SetIR
, 1,
76 av7110
->ir
.ir_config
);
79 static int change_protocol(struct rc_dev
*rcdev
, u64
*rc_type
)
81 struct av7110
*av7110
= rcdev
->priv
;
84 if (*rc_type
& RC_PROTO_BIT_RCMM32
) {
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
;
92 *rc_type
= RC_PROTO_BIT_RC5
;
97 if (ir_config
== av7110
->ir
.ir_config
)
100 av7110
->ir
.ir_config
= ir_config
;
102 return av7110_set_ir_config(av7110
);
105 int av7110_ir_init(struct av7110
*av7110
)
107 struct rc_dev
*rcdev
;
111 rcdev
= rc_allocate_device(RC_DRIVER_SCANCODE
);
115 pci
= av7110
->dev
->pci
;
117 snprintf(av7110
->ir
.input_phys
, sizeof(av7110
->ir
.input_phys
),
118 "pci-%s/ir0", pci_name(pci
));
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
;
129 rcdev
->input_id
.vendor
= pci
->vendor
;
130 rcdev
->input_id
.product
= pci
->device
;
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
;
139 av7110
->ir
.rcdev
= rcdev
;
140 av7110
->ir
.ir_config
= IR_RC5
;
141 av7110_set_ir_config(av7110
);
143 ret
= rc_register_device(rcdev
);
145 av7110
->ir
.rcdev
= NULL
;
146 rc_free_device(rcdev
);
152 void av7110_ir_exit(struct av7110
*av7110
)
154 rc_unregister_device(av7110
->ir
.rcdev
);
157 //MODULE_AUTHOR("Holger Waechtler <holger@convergence.de>, Oliver Endriss <o.endriss@gmx.de>");
158 //MODULE_LICENSE("GPL");