2
0
mirror of https://github.com/edk2-porting/linux-next.git synced 2025-01-27 08:05:27 +08:00
linux-next/drivers/media/pci/ttpci/av7110_ir.c
Thomas Gleixner a0c7056fda treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 113
Based on 1 normalized pattern(s):

  this program is free software you can redistribute it and or modify
  it under the terms of the gnu general public license as published by
  the free software foundation either version 2 of the license or at
  your option any later version this program is distributed in the
  hope that it will be useful but without any warranty without even
  the implied warranty of merchantability or fitness for a particular
  purpose see the gnu general public license for more details to
  obtain the license point your browser to http www gnu org copyleft
  gpl html

extracted by the scancode license scanner the SPDX license identifier

  GPL-2.0-or-later

has been chosen to replace the boilerplate/reference in 26 file(s).

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Kate Stewart <kstewart@linuxfoundation.org>
Reviewed-by: Richard Fontana <rfontana@redhat.com>
Reviewed-by: Allison Randal <allison@lohutok.net>
Cc: linux-spdx@vger.kernel.org
Link: https://lkml.kernel.org/r/20190523091650.572604764@linutronix.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2019-05-24 17:39:01 +02:00

384 lines
9.8 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>
*/
#include <linux/types.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/kernel.h>
#include <linux/bitops.h>
#include "av7110.h"
#include "av7110_hw.h"
#define AV_CNT 4
#define IR_RC5 0
#define IR_RCMM 1
#define IR_RC5_EXT 2 /* internal only */
#define IR_ALL 0xffffffff
#define UP_TIMEOUT (HZ*7/25)
/* Note: enable ir debugging by or'ing debug with 16 */
static int ir_protocol[AV_CNT] = { IR_RCMM, IR_RCMM, IR_RCMM, IR_RCMM};
module_param_array(ir_protocol, int, NULL, 0644);
MODULE_PARM_DESC(ir_protocol, "Infrared protocol: 0 RC5, 1 RCMM (default)");
static int ir_inversion[AV_CNT];
module_param_array(ir_inversion, int, NULL, 0644);
MODULE_PARM_DESC(ir_inversion, "Inversion of infrared signal: 0 not inverted (default), 1 inverted");
static uint ir_device_mask[AV_CNT] = { IR_ALL, IR_ALL, IR_ALL, IR_ALL };
module_param_array(ir_device_mask, uint, NULL, 0644);
MODULE_PARM_DESC(ir_device_mask, "Bitmask of infrared devices: bit 0..31 = device 0..31 (default: all)");
static int av_cnt;
static struct av7110 *av_list[AV_CNT];
static u16 default_key_map [256] = {
KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7,
KEY_8, KEY_9, KEY_BACK, 0, KEY_POWER, KEY_MUTE, 0, KEY_INFO,
KEY_VOLUMEUP, KEY_VOLUMEDOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
KEY_CHANNELUP, KEY_CHANNELDOWN, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, KEY_TEXT, 0, 0, KEY_TV, 0, 0, 0, 0, 0, KEY_SETUP, 0, 0,
0, 0, 0, KEY_SUBTITLE, 0, 0, KEY_LANGUAGE, 0,
KEY_RADIO, 0, 0, 0, 0, KEY_EXIT, 0, 0,
KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_OK, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_RED, KEY_GREEN, KEY_YELLOW,
KEY_BLUE, 0, 0, 0, 0, 0, 0, 0, KEY_MENU, KEY_LIST, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, KEY_UP, KEY_UP, KEY_DOWN, KEY_DOWN,
0, 0, 0, 0, KEY_EPG, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_VCR
};
/* key-up timer */
static void av7110_emit_keyup(struct timer_list *t)
{
struct infrared *ir = from_timer(ir, t, keyup_timer);
if (!ir || !ir->keypressed)
return;
input_report_key(ir->input_dev, ir->last_key, 0);
input_sync(ir->input_dev);
ir->keypressed = false;
}
/* tasklet */
static void av7110_emit_key(unsigned long parm)
{
struct infrared *ir = (struct infrared *) parm;
u32 ircom = ir->ir_command;
u8 data;
u8 addr;
u16 toggle;
u16 keycode;
/* extract device address and data */
switch (ir->protocol) {
case IR_RC5: /* RC5: 5 bits device address, 6 bits data */
data = ircom & 0x3f;
addr = (ircom >> 6) & 0x1f;
toggle = ircom & 0x0800;
break;
case IR_RCMM: /* RCMM: ? bits device address, ? bits data */
data = ircom & 0xff;
addr = (ircom >> 8) & 0x1f;
toggle = ircom & 0x8000;
break;
case IR_RC5_EXT: /* extended RC5: 5 bits device address, 7 bits data */
data = ircom & 0x3f;
addr = (ircom >> 6) & 0x1f;
/* invert 7th data bit for backward compatibility with RC5 keymaps */
if (!(ircom & 0x1000))
data |= 0x40;
toggle = ircom & 0x0800;
break;
default:
printk("%s invalid protocol %x\n", __func__, ir->protocol);
return;
}
input_event(ir->input_dev, EV_MSC, MSC_RAW, (addr << 16) | data);
input_event(ir->input_dev, EV_MSC, MSC_SCAN, data);
keycode = ir->key_map[data];
dprintk(16, "%s: code %08x -> addr %i data 0x%02x -> keycode %i\n",
__func__, ircom, addr, data, keycode);
/* check device address */
if (!(ir->device_mask & (1 << addr)))
return;
if (!keycode) {
printk ("%s: code %08x -> addr %i data 0x%02x -> unknown key!\n",
__func__, ircom, addr, data);
return;
}
if (ir->keypressed &&
(ir->last_key != keycode || toggle != ir->last_toggle))
input_event(ir->input_dev, EV_KEY, ir->last_key, 0);
input_event(ir->input_dev, EV_KEY, keycode, 1);
input_sync(ir->input_dev);
ir->keypressed = true;
ir->last_key = keycode;
ir->last_toggle = toggle;
mod_timer(&ir->keyup_timer, jiffies + UP_TIMEOUT);
}
/* register with input layer */
static void input_register_keys(struct infrared *ir)
{
int i;
set_bit(EV_KEY, ir->input_dev->evbit);
set_bit(EV_REP, ir->input_dev->evbit);
set_bit(EV_MSC, ir->input_dev->evbit);
set_bit(MSC_RAW, ir->input_dev->mscbit);
set_bit(MSC_SCAN, ir->input_dev->mscbit);
memset(ir->input_dev->keybit, 0, sizeof(ir->input_dev->keybit));
for (i = 0; i < ARRAY_SIZE(ir->key_map); i++) {
if (ir->key_map[i] > KEY_MAX)
ir->key_map[i] = 0;
else if (ir->key_map[i] > KEY_RESERVED)
set_bit(ir->key_map[i], ir->input_dev->keybit);
}
ir->input_dev->keycode = ir->key_map;
ir->input_dev->keycodesize = sizeof(ir->key_map[0]);
ir->input_dev->keycodemax = ARRAY_SIZE(ir->key_map);
}
/* check for configuration changes */
int av7110_check_ir_config(struct av7110 *av7110, int force)
{
int i;
int modified = force;
int ret = -ENODEV;
for (i = 0; i < av_cnt; i++)
if (av7110 == av_list[i])
break;
if (i < av_cnt && av7110) {
if ((av7110->ir.protocol & 1) != ir_protocol[i] ||
av7110->ir.inversion != ir_inversion[i])
modified = true;
if (modified) {
/* protocol */
if (ir_protocol[i]) {
ir_protocol[i] = 1;
av7110->ir.protocol = IR_RCMM;
av7110->ir.ir_config = 0x0001;
} else if (FW_VERSION(av7110->arm_app) >= 0x2620) {
av7110->ir.protocol = IR_RC5_EXT;
av7110->ir.ir_config = 0x0002;
} else {
av7110->ir.protocol = IR_RC5;
av7110->ir.ir_config = 0x0000;
}
/* inversion */
if (ir_inversion[i]) {
ir_inversion[i] = 1;
av7110->ir.ir_config |= 0x8000;
}
av7110->ir.inversion = ir_inversion[i];
/* update ARM */
ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1,
av7110->ir.ir_config);
} else
ret = 0;
/* address */
if (av7110->ir.device_mask != ir_device_mask[i])
av7110->ir.device_mask = ir_device_mask[i];
}
return ret;
}
/* /proc/av7110_ir interface */
static ssize_t av7110_ir_proc_write(struct file *file, const char __user *buffer,
size_t count, loff_t *pos)
{
char *page;
u32 ir_config;
int size = sizeof ir_config + sizeof av_list[0]->ir.key_map;
int i;
if (count < size)
return -EINVAL;
page = vmalloc(size);
if (!page)
return -ENOMEM;
if (copy_from_user(page, buffer, size)) {
vfree(page);
return -EFAULT;
}
memcpy(&ir_config, page, sizeof ir_config);
for (i = 0; i < av_cnt; i++) {
/* keymap */
memcpy(av_list[i]->ir.key_map, page + sizeof ir_config,
sizeof(av_list[i]->ir.key_map));
/* protocol, inversion, address */
ir_protocol[i] = ir_config & 0x0001;
ir_inversion[i] = ir_config & 0x8000 ? 1 : 0;
if (ir_config & 0x4000)
ir_device_mask[i] = 1 << ((ir_config >> 16) & 0x1f);
else
ir_device_mask[i] = IR_ALL;
/* update configuration */
av7110_check_ir_config(av_list[i], false);
input_register_keys(&av_list[i]->ir);
}
vfree(page);
return count;
}
static const struct file_operations av7110_ir_proc_fops = {
.owner = THIS_MODULE,
.write = av7110_ir_proc_write,
.llseek = noop_llseek,
};
/* interrupt handler */
static void ir_handler(struct av7110 *av7110, u32 ircom)
{
dprintk(4, "ir command = %08x\n", ircom);
av7110->ir.ir_command = ircom;
tasklet_schedule(&av7110->ir.ir_tasklet);
}
int av7110_ir_init(struct av7110 *av7110)
{
struct input_dev *input_dev;
static struct proc_dir_entry *e;
int err;
if (av_cnt >= ARRAY_SIZE(av_list))
return -ENOSPC;
av_list[av_cnt++] = av7110;
av7110_check_ir_config(av7110, true);
timer_setup(&av7110->ir.keyup_timer, av7110_emit_keyup, 0);
input_dev = input_allocate_device();
if (!input_dev)
return -ENOMEM;
av7110->ir.input_dev = input_dev;
snprintf(av7110->ir.input_phys, sizeof(av7110->ir.input_phys),
"pci-%s/ir0", pci_name(av7110->dev->pci));
input_dev->name = "DVB on-card IR receiver";
input_dev->phys = av7110->ir.input_phys;
input_dev->id.bustype = BUS_PCI;
input_dev->id.version = 2;
if (av7110->dev->pci->subsystem_vendor) {
input_dev->id.vendor = av7110->dev->pci->subsystem_vendor;
input_dev->id.product = av7110->dev->pci->subsystem_device;
} else {
input_dev->id.vendor = av7110->dev->pci->vendor;
input_dev->id.product = av7110->dev->pci->device;
}
input_dev->dev.parent = &av7110->dev->pci->dev;
/* initial keymap */
memcpy(av7110->ir.key_map, default_key_map, sizeof av7110->ir.key_map);
input_register_keys(&av7110->ir);
err = input_register_device(input_dev);
if (err) {
input_free_device(input_dev);
return err;
}
/*
* Input core's default autorepeat is 33 cps with 250 msec
* delay, let's adjust to numbers more suitable for remote
* control.
*/
input_enable_softrepeat(input_dev, 250, 125);
if (av_cnt == 1) {
e = proc_create("av7110_ir", S_IWUSR, NULL, &av7110_ir_proc_fops);
if (e)
proc_set_size(e, 4 + 256 * sizeof(u16));
}
tasklet_init(&av7110->ir.ir_tasklet, av7110_emit_key, (unsigned long) &av7110->ir);
av7110->ir.ir_handler = ir_handler;
return 0;
}
void av7110_ir_exit(struct av7110 *av7110)
{
int i;
if (av_cnt == 0)
return;
del_timer_sync(&av7110->ir.keyup_timer);
av7110->ir.ir_handler = NULL;
tasklet_kill(&av7110->ir.ir_tasklet);
for (i = 0; i < av_cnt; i++)
if (av_list[i] == av7110) {
av_list[i] = av_list[av_cnt-1];
av_list[av_cnt-1] = NULL;
break;
}
if (av_cnt == 1)
remove_proc_entry("av7110_ir", NULL);
input_unregister_device(av7110->ir.input_dev);
av_cnt--;
}
//MODULE_AUTHOR("Holger Waechtler <holger@convergence.de>, Oliver Endriss <o.endriss@gmx.de>");
//MODULE_LICENSE("GPL");