159 lines
3.7 KiB
C
159 lines
3.7 KiB
C
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||
|
/*
|
||
|
* Driver for the remote control of SAA7146 based AV7110 cards
|
||
|
*
|
||
|
* Copyright (C) 1999-2003 Holger Waechtler <holger@convergence.de>
|
||
|
* Copyright (C) 2003-2007 Oliver Endriss <o.endriss@gmx.de>
|
||
|
* Copyright (C) 2019 Sean Young <sean@mess.org>
|
||
|
*/
|
||
|
|
||
|
#include <linux/kernel.h>
|
||
|
#include <media/rc-core.h>
|
||
|
|
||
|
#include "av7110.h"
|
||
|
#include "av7110_hw.h"
|
||
|
|
||
|
#define IR_RC5 0
|
||
|
#define IR_RCMM 1
|
||
|
#define IR_RC5_EXT 2 /* internal only */
|
||
|
|
||
|
/* interrupt handler */
|
||
|
void av7110_ir_handler(struct av7110 *av7110, u32 ircom)
|
||
|
{
|
||
|
struct rc_dev *rcdev = av7110->ir.rcdev;
|
||
|
enum rc_proto proto;
|
||
|
u32 command, addr, scancode;
|
||
|
u32 toggle;
|
||
|
|
||
|
dprintk(4, "ir command = %08x\n", ircom);
|
||
|
|
||
|
if (rcdev) {
|
||
|
switch (av7110->ir.ir_config) {
|
||
|
case IR_RC5: /* RC5: 5 bits device address, 6 bits command */
|
||
|
command = ircom & 0x3f;
|
||
|
addr = (ircom >> 6) & 0x1f;
|
||
|
scancode = RC_SCANCODE_RC5(addr, command);
|
||
|
toggle = ircom & 0x0800;
|
||
|
proto = RC_PROTO_RC5;
|
||
|
break;
|
||
|
|
||
|
case IR_RCMM: /* RCMM: 32 bits scancode */
|
||
|
scancode = ircom & ~0x8000;
|
||
|
toggle = ircom & 0x8000;
|
||
|
proto = RC_PROTO_RCMM32;
|
||
|
break;
|
||
|
|
||
|
case IR_RC5_EXT:
|
||
|
/*
|
||
|
* extended RC5: 5 bits device address, 7 bits command
|
||
|
*
|
||
|
* Extended RC5 uses only one start bit. The second
|
||
|
* start bit is re-assigned bit 6 of the command bit.
|
||
|
*/
|
||
|
command = ircom & 0x3f;
|
||
|
addr = (ircom >> 6) & 0x1f;
|
||
|
if (!(ircom & 0x1000))
|
||
|
command |= 0x40;
|
||
|
scancode = RC_SCANCODE_RC5(addr, command);
|
||
|
toggle = ircom & 0x0800;
|
||
|
proto = RC_PROTO_RC5;
|
||
|
break;
|
||
|
default:
|
||
|
dprintk(2, "unknown ir config %d\n",
|
||
|
av7110->ir.ir_config);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
rc_keydown(rcdev, proto, scancode, toggle != 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int av7110_set_ir_config(struct av7110 *av7110)
|
||
|
{
|
||
|
dprintk(4, "ir config = %08x\n", av7110->ir.ir_config);
|
||
|
|
||
|
return av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1,
|
||
|
av7110->ir.ir_config);
|
||
|
}
|
||
|
|
||
|
static int change_protocol(struct rc_dev *rcdev, u64 *rc_type)
|
||
|
{
|
||
|
struct av7110 *av7110 = rcdev->priv;
|
||
|
u32 ir_config;
|
||
|
|
||
|
if (*rc_type & RC_PROTO_BIT_RCMM32) {
|
||
|
ir_config = IR_RCMM;
|
||
|
*rc_type = RC_PROTO_BIT_RCMM32;
|
||
|
} else if (*rc_type & RC_PROTO_BIT_RC5) {
|
||
|
if (FW_VERSION(av7110->arm_app) >= 0x2620)
|
||
|
ir_config = IR_RC5_EXT;
|
||
|
else
|
||
|
ir_config = IR_RC5;
|
||
|
*rc_type = RC_PROTO_BIT_RC5;
|
||
|
} else {
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
if (ir_config == av7110->ir.ir_config)
|
||
|
return 0;
|
||
|
|
||
|
av7110->ir.ir_config = ir_config;
|
||
|
|
||
|
return av7110_set_ir_config(av7110);
|
||
|
}
|
||
|
|
||
|
int av7110_ir_init(struct av7110 *av7110)
|
||
|
{
|
||
|
struct rc_dev *rcdev;
|
||
|
struct pci_dev *pci;
|
||
|
int ret;
|
||
|
|
||
|
rcdev = rc_allocate_device(RC_DRIVER_SCANCODE);
|
||
|
if (!rcdev)
|
||
|
return -ENOMEM;
|
||
|
|
||
|
pci = av7110->dev->pci;
|
||
|
|
||
|
snprintf(av7110->ir.input_phys, sizeof(av7110->ir.input_phys),
|
||
|
"pci-%s/ir0", pci_name(pci));
|
||
|
|
||
|
rcdev->device_name = av7110->card_name;
|
||
|
rcdev->driver_name = KBUILD_MODNAME;
|
||
|
rcdev->input_phys = av7110->ir.input_phys;
|
||
|
rcdev->input_id.bustype = BUS_PCI;
|
||
|
rcdev->input_id.version = 2;
|
||
|
if (pci->subsystem_vendor) {
|
||
|
rcdev->input_id.vendor = pci->subsystem_vendor;
|
||
|
rcdev->input_id.product = pci->subsystem_device;
|
||
|
} else {
|
||
|
rcdev->input_id.vendor = pci->vendor;
|
||
|
rcdev->input_id.product = pci->device;
|
||
|
}
|
||
|
|
||
|
rcdev->dev.parent = &pci->dev;
|
||
|
rcdev->allowed_protocols = RC_PROTO_BIT_RC5 | RC_PROTO_BIT_RCMM32;
|
||
|
rcdev->change_protocol = change_protocol;
|
||
|
rcdev->map_name = RC_MAP_HAUPPAUGE;
|
||
|
rcdev->priv = av7110;
|
||
|
|
||
|
av7110->ir.rcdev = rcdev;
|
||
|
av7110->ir.ir_config = IR_RC5;
|
||
|
av7110_set_ir_config(av7110);
|
||
|
|
||
|
ret = rc_register_device(rcdev);
|
||
|
if (ret) {
|
||
|
av7110->ir.rcdev = NULL;
|
||
|
rc_free_device(rcdev);
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
void av7110_ir_exit(struct av7110 *av7110)
|
||
|
{
|
||
|
rc_unregister_device(av7110->ir.rcdev);
|
||
|
}
|
||
|
|
||
|
//MODULE_AUTHOR("Holger Waechtler <holger@convergence.de>, Oliver Endriss <o.endriss@gmx.de>");
|
||
|
//MODULE_LICENSE("GPL");
|