// SPDX-License-Identifier: GPL-2.0-or-later /* * Driver for the remote control of SAA7146 based AV7110 cards * * Copyright (C) 1999-2003 Holger Waechtler * Copyright (C) 2003-2007 Oliver Endriss * Copyright (C) 2019 Sean Young */ #include #include #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: ? bits device address, ? bits command */ command = ircom & 0xff; addr = (ircom >> 8) & 0x1f; scancode = ircom; toggle = ircom & 0x8000; proto = RC_PROTO_UNKNOWN; 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_UNKNOWN) { ir_config = IR_RCMM; *rc_type = RC_PROTO_UNKNOWN; } 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_UNKNOWN; 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 , Oliver Endriss "); //MODULE_LICENSE("GPL");