linux/drivers/media/pci/ttpci/av7110_ir.c
<<
>>
Prefs
   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 */
  21void 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
  73int 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
  81static 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
 107int 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
 154void 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");
 161