| // 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"); |