mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-28 01:26:29 +07:00
3ef9dff4ae
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 extracted by the scancode license scanner the SPDX license identifier GPL-2.0-or-later has been chosen to replace the boilerplate/reference in 1 file(s). Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Allison Randal <allison@lohutok.net> Reviewed-by: Richard Fontana <rfontana@redhat.com> Reviewed-by: Kate Stewart <kstewart@linuxfoundation.org> Cc: linux-spdx@vger.kernel.org Link: https://lkml.kernel.org/r/20190524100843.971812365@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
958 lines
23 KiB
C
958 lines
23 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
*
|
|
* keyboard input driver for i2c IR remote controls
|
|
*
|
|
* Copyright (c) 2000-2003 Gerd Knorr <kraxel@bytesex.org>
|
|
* modified for PixelView (BT878P+W/FM) by
|
|
* Michal Kochanowicz <mkochano@pld.org.pl>
|
|
* Christoph Bartelmus <lirc@bartelmus.de>
|
|
* modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by
|
|
* Ulrich Mueller <ulrich.mueller42@web.de>
|
|
* modified for em2820 based USB TV tuners by
|
|
* Markus Rechberger <mrechberger@gmail.com>
|
|
* modified for DViCO Fusion HDTV 5 RT GOLD by
|
|
* Chaogui Zhang <czhang1974@gmail.com>
|
|
* modified for MSI TV@nywhere Plus by
|
|
* Henry Wong <henry@stuffedcow.net>
|
|
* Mark Schultz <n9xmj@yahoo.com>
|
|
* Brian Rogers <brian_rogers@comcast.net>
|
|
* modified for AVerMedia Cardbus by
|
|
* Oldrich Jedlicka <oldium.pro@seznam.cz>
|
|
* Zilog Transmitter portions/ideas were derived from GPLv2+ sources:
|
|
* - drivers/char/pctv_zilogir.[ch] from Hauppauge Broadway product
|
|
* Copyright 2011 Hauppauge Computer works
|
|
* - drivers/staging/media/lirc/lirc_zilog.c
|
|
* Copyright (c) 2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
|
|
* Michal Kochanowicz <mkochano@pld.org.pl>
|
|
* Christoph Bartelmus <lirc@bartelmus.de>
|
|
* Ulrich Mueller <ulrich.mueller42@web.de>
|
|
* Stefan Jahn <stefan@lkcc.org>
|
|
* Jerome Brock <jbrock@users.sourceforge.net>
|
|
* Thomas Reitmayr (treitmayr@yahoo.com)
|
|
* Mark Weaver <mark@npsl.co.uk>
|
|
* Jarod Wilson <jarod@redhat.com>
|
|
* Copyright (C) 2011 Andy Walls <awalls@md.metrocast.net>
|
|
*/
|
|
|
|
#include <asm/unaligned.h>
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/string.h>
|
|
#include <linux/timer.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/workqueue.h>
|
|
|
|
#include <media/rc-core.h>
|
|
#include <media/i2c/ir-kbd-i2c.h>
|
|
|
|
#define FLAG_TX 1
|
|
#define FLAG_HDPVR 2
|
|
|
|
static bool enable_hdpvr;
|
|
module_param(enable_hdpvr, bool, 0644);
|
|
|
|
static int get_key_haup_common(struct IR_i2c *ir, enum rc_proto *protocol,
|
|
u32 *scancode, u8 *ptoggle, int size)
|
|
{
|
|
unsigned char buf[6];
|
|
int start, range, toggle, dev, code, ircode, vendor;
|
|
|
|
/* poll IR chip */
|
|
if (size != i2c_master_recv(ir->c, buf, size))
|
|
return -EIO;
|
|
|
|
if (buf[0] & 0x80) {
|
|
int offset = (size == 6) ? 3 : 0;
|
|
|
|
/* split rc5 data block ... */
|
|
start = (buf[offset] >> 7) & 1;
|
|
range = (buf[offset] >> 6) & 1;
|
|
toggle = (buf[offset] >> 5) & 1;
|
|
dev = buf[offset] & 0x1f;
|
|
code = (buf[offset+1] >> 2) & 0x3f;
|
|
|
|
/* rc5 has two start bits
|
|
* the first bit must be one
|
|
* the second bit defines the command range:
|
|
* 1 = 0-63, 0 = 64 - 127
|
|
*/
|
|
if (!start)
|
|
/* no key pressed */
|
|
return 0;
|
|
|
|
/* filter out invalid key presses */
|
|
ircode = (start << 12) | (toggle << 11) | (dev << 6) | code;
|
|
if ((ircode & 0x1fff) == 0x1fff)
|
|
return 0;
|
|
|
|
if (!range)
|
|
code += 64;
|
|
|
|
dev_dbg(&ir->rc->dev,
|
|
"ir hauppauge (rc5): s%d r%d t%d dev=%d code=%d\n",
|
|
start, range, toggle, dev, code);
|
|
|
|
*protocol = RC_PROTO_RC5;
|
|
*scancode = RC_SCANCODE_RC5(dev, code);
|
|
*ptoggle = toggle;
|
|
|
|
return 1;
|
|
} else if (size == 6 && (buf[0] & 0x40)) {
|
|
code = buf[4];
|
|
dev = buf[3];
|
|
vendor = get_unaligned_be16(buf + 1);
|
|
|
|
if (vendor == 0x800f) {
|
|
*ptoggle = (dev & 0x80) != 0;
|
|
*protocol = RC_PROTO_RC6_MCE;
|
|
dev &= 0x7f;
|
|
dev_dbg(&ir->rc->dev,
|
|
"ir hauppauge (rc6-mce): t%d vendor=%d dev=%d code=%d\n",
|
|
*ptoggle, vendor, dev, code);
|
|
} else {
|
|
*ptoggle = 0;
|
|
*protocol = RC_PROTO_RC6_6A_32;
|
|
dev_dbg(&ir->rc->dev,
|
|
"ir hauppauge (rc6-6a-32): vendor=%d dev=%d code=%d\n",
|
|
vendor, dev, code);
|
|
}
|
|
|
|
*scancode = RC_SCANCODE_RC6_6A(vendor, dev, code);
|
|
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int get_key_haup(struct IR_i2c *ir, enum rc_proto *protocol,
|
|
u32 *scancode, u8 *toggle)
|
|
{
|
|
return get_key_haup_common(ir, protocol, scancode, toggle, 3);
|
|
}
|
|
|
|
static int get_key_haup_xvr(struct IR_i2c *ir, enum rc_proto *protocol,
|
|
u32 *scancode, u8 *toggle)
|
|
{
|
|
int ret;
|
|
unsigned char buf[1] = { 0 };
|
|
|
|
/*
|
|
* This is the same apparent "are you ready?" poll command observed
|
|
* watching Windows driver traffic and implemented in lirc_zilog. With
|
|
* this added, we get far saner remote behavior with z8 chips on usb
|
|
* connected devices, even with the default polling interval of 100ms.
|
|
*/
|
|
ret = i2c_master_send(ir->c, buf, 1);
|
|
if (ret != 1)
|
|
return (ret < 0) ? ret : -EINVAL;
|
|
|
|
return get_key_haup_common(ir, protocol, scancode, toggle, 6);
|
|
}
|
|
|
|
static int get_key_pixelview(struct IR_i2c *ir, enum rc_proto *protocol,
|
|
u32 *scancode, u8 *toggle)
|
|
{
|
|
int rc;
|
|
unsigned char b;
|
|
|
|
/* poll IR chip */
|
|
rc = i2c_master_recv(ir->c, &b, 1);
|
|
if (rc != 1) {
|
|
dev_dbg(&ir->rc->dev, "read error\n");
|
|
if (rc < 0)
|
|
return rc;
|
|
return -EIO;
|
|
}
|
|
|
|
*protocol = RC_PROTO_OTHER;
|
|
*scancode = b;
|
|
*toggle = 0;
|
|
return 1;
|
|
}
|
|
|
|
static int get_key_fusionhdtv(struct IR_i2c *ir, enum rc_proto *protocol,
|
|
u32 *scancode, u8 *toggle)
|
|
{
|
|
int rc;
|
|
unsigned char buf[4];
|
|
|
|
/* poll IR chip */
|
|
rc = i2c_master_recv(ir->c, buf, 4);
|
|
if (rc != 4) {
|
|
dev_dbg(&ir->rc->dev, "read error\n");
|
|
if (rc < 0)
|
|
return rc;
|
|
return -EIO;
|
|
}
|
|
|
|
if (buf[0] != 0 || buf[1] != 0 || buf[2] != 0 || buf[3] != 0)
|
|
dev_dbg(&ir->rc->dev, "%s: %*ph\n", __func__, 4, buf);
|
|
|
|
/* no key pressed or signal from other ir remote */
|
|
if(buf[0] != 0x1 || buf[1] != 0xfe)
|
|
return 0;
|
|
|
|
*protocol = RC_PROTO_UNKNOWN;
|
|
*scancode = buf[2];
|
|
*toggle = 0;
|
|
return 1;
|
|
}
|
|
|
|
static int get_key_knc1(struct IR_i2c *ir, enum rc_proto *protocol,
|
|
u32 *scancode, u8 *toggle)
|
|
{
|
|
int rc;
|
|
unsigned char b;
|
|
|
|
/* poll IR chip */
|
|
rc = i2c_master_recv(ir->c, &b, 1);
|
|
if (rc != 1) {
|
|
dev_dbg(&ir->rc->dev, "read error\n");
|
|
if (rc < 0)
|
|
return rc;
|
|
return -EIO;
|
|
}
|
|
|
|
/* it seems that 0xFE indicates that a button is still hold
|
|
down, while 0xff indicates that no button is hold
|
|
down. 0xfe sequences are sometimes interrupted by 0xFF */
|
|
|
|
dev_dbg(&ir->rc->dev, "key %02x\n", b);
|
|
|
|
if (b == 0xff)
|
|
return 0;
|
|
|
|
if (b == 0xfe)
|
|
/* keep old data */
|
|
return 1;
|
|
|
|
*protocol = RC_PROTO_UNKNOWN;
|
|
*scancode = b;
|
|
*toggle = 0;
|
|
return 1;
|
|
}
|
|
|
|
static int get_key_avermedia_cardbus(struct IR_i2c *ir, enum rc_proto *protocol,
|
|
u32 *scancode, u8 *toggle)
|
|
{
|
|
unsigned char subaddr, key, keygroup;
|
|
struct i2c_msg msg[] = { { .addr = ir->c->addr, .flags = 0,
|
|
.buf = &subaddr, .len = 1},
|
|
{ .addr = ir->c->addr, .flags = I2C_M_RD,
|
|
.buf = &key, .len = 1} };
|
|
subaddr = 0x0d;
|
|
if (2 != i2c_transfer(ir->c->adapter, msg, 2)) {
|
|
dev_dbg(&ir->rc->dev, "read error\n");
|
|
return -EIO;
|
|
}
|
|
|
|
if (key == 0xff)
|
|
return 0;
|
|
|
|
subaddr = 0x0b;
|
|
msg[1].buf = &keygroup;
|
|
if (2 != i2c_transfer(ir->c->adapter, msg, 2)) {
|
|
dev_dbg(&ir->rc->dev, "read error\n");
|
|
return -EIO;
|
|
}
|
|
|
|
if (keygroup == 0xff)
|
|
return 0;
|
|
|
|
dev_dbg(&ir->rc->dev, "read key 0x%02x/0x%02x\n", key, keygroup);
|
|
if (keygroup < 2 || keygroup > 4) {
|
|
dev_warn(&ir->rc->dev, "warning: invalid key group 0x%02x for key 0x%02x\n",
|
|
keygroup, key);
|
|
}
|
|
key |= (keygroup & 1) << 6;
|
|
|
|
*protocol = RC_PROTO_UNKNOWN;
|
|
*scancode = key;
|
|
if (ir->c->addr == 0x41) /* AVerMedia EM78P153 */
|
|
*scancode |= keygroup << 8;
|
|
*toggle = 0;
|
|
return 1;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------- */
|
|
|
|
static int ir_key_poll(struct IR_i2c *ir)
|
|
{
|
|
enum rc_proto protocol;
|
|
u32 scancode;
|
|
u8 toggle;
|
|
int rc;
|
|
|
|
dev_dbg(&ir->rc->dev, "%s\n", __func__);
|
|
rc = ir->get_key(ir, &protocol, &scancode, &toggle);
|
|
if (rc < 0) {
|
|
dev_warn(&ir->rc->dev, "error %d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
if (rc) {
|
|
dev_dbg(&ir->rc->dev, "%s: proto = 0x%04x, scancode = 0x%08x\n",
|
|
__func__, protocol, scancode);
|
|
rc_keydown(ir->rc, protocol, scancode, toggle);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void ir_work(struct work_struct *work)
|
|
{
|
|
int rc;
|
|
struct IR_i2c *ir = container_of(work, struct IR_i2c, work.work);
|
|
|
|
/*
|
|
* If the transmit code is holding the lock, skip polling for
|
|
* IR, we'll get it to it next time round
|
|
*/
|
|
if (mutex_trylock(&ir->lock)) {
|
|
rc = ir_key_poll(ir);
|
|
mutex_unlock(&ir->lock);
|
|
if (rc == -ENODEV) {
|
|
rc_unregister_device(ir->rc);
|
|
ir->rc = NULL;
|
|
return;
|
|
}
|
|
}
|
|
|
|
schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling_interval));
|
|
}
|
|
|
|
static int ir_open(struct rc_dev *dev)
|
|
{
|
|
struct IR_i2c *ir = dev->priv;
|
|
|
|
schedule_delayed_work(&ir->work, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void ir_close(struct rc_dev *dev)
|
|
{
|
|
struct IR_i2c *ir = dev->priv;
|
|
|
|
cancel_delayed_work_sync(&ir->work);
|
|
}
|
|
|
|
/* Zilog Transmit Interface */
|
|
#define XTAL_FREQ 18432000
|
|
|
|
#define ZILOG_SEND 0x80
|
|
#define ZILOG_UIR_END 0x40
|
|
#define ZILOG_INIT_END 0x20
|
|
#define ZILOG_LIR_END 0x10
|
|
|
|
#define ZILOG_STATUS_OK 0x80
|
|
#define ZILOG_STATUS_TX 0x40
|
|
#define ZILOG_STATUS_SET 0x20
|
|
|
|
/*
|
|
* As you can see here, very few different lengths of pulse and space
|
|
* can be encoded. This means that the hardware does not work well with
|
|
* recorded IR. It's best to work with generated IR, like from ir-ctl or
|
|
* the in-kernel encoders.
|
|
*/
|
|
struct code_block {
|
|
u8 length;
|
|
u16 pulse[7]; /* not aligned */
|
|
u8 carrier_pulse;
|
|
u8 carrier_space;
|
|
u16 space[8]; /* not aligned */
|
|
u8 codes[61];
|
|
u8 csum[2];
|
|
} __packed;
|
|
|
|
static int send_data_block(struct IR_i2c *ir, int cmd,
|
|
struct code_block *code_block)
|
|
{
|
|
int i, j, ret;
|
|
u8 buf[5], *p;
|
|
|
|
p = &code_block->length;
|
|
for (i = 0; p < code_block->csum; i++)
|
|
code_block->csum[i & 1] ^= *p++;
|
|
|
|
p = &code_block->length;
|
|
|
|
for (i = 0; i < sizeof(*code_block);) {
|
|
int tosend = sizeof(*code_block) - i;
|
|
|
|
if (tosend > 4)
|
|
tosend = 4;
|
|
buf[0] = i + 1;
|
|
for (j = 0; j < tosend; ++j)
|
|
buf[1 + j] = p[i + j];
|
|
dev_dbg(&ir->rc->dev, "%*ph", tosend + 1, buf);
|
|
ret = i2c_master_send(ir->tx_c, buf, tosend + 1);
|
|
if (ret != tosend + 1) {
|
|
dev_dbg(&ir->rc->dev,
|
|
"i2c_master_send failed with %d\n", ret);
|
|
return ret < 0 ? ret : -EIO;
|
|
}
|
|
i += tosend;
|
|
}
|
|
|
|
buf[0] = 0;
|
|
buf[1] = cmd;
|
|
ret = i2c_master_send(ir->tx_c, buf, 2);
|
|
if (ret != 2) {
|
|
dev_err(&ir->rc->dev, "i2c_master_send failed with %d\n", ret);
|
|
return ret < 0 ? ret : -EIO;
|
|
}
|
|
|
|
usleep_range(2000, 5000);
|
|
|
|
ret = i2c_master_send(ir->tx_c, buf, 1);
|
|
if (ret != 1) {
|
|
dev_err(&ir->rc->dev, "i2c_master_send failed with %d\n", ret);
|
|
return ret < 0 ? ret : -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int zilog_init(struct IR_i2c *ir)
|
|
{
|
|
struct code_block code_block = { .length = sizeof(code_block) };
|
|
u8 buf[4];
|
|
int ret;
|
|
|
|
put_unaligned_be16(0x1000, &code_block.pulse[3]);
|
|
|
|
ret = send_data_block(ir, ZILOG_INIT_END, &code_block);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = i2c_master_recv(ir->tx_c, buf, 4);
|
|
if (ret != 4) {
|
|
dev_err(&ir->c->dev, "failed to retrieve firmware version: %d\n",
|
|
ret);
|
|
return ret < 0 ? ret : -EIO;
|
|
}
|
|
|
|
dev_info(&ir->c->dev, "Zilog/Hauppauge IR blaster firmware version %d.%d.%d\n",
|
|
buf[1], buf[2], buf[3]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* If the last slot for pulse is the same as the current slot for pulse,
|
|
* then use slot no 7.
|
|
*/
|
|
static void copy_codes(u8 *dst, u8 *src, unsigned int count)
|
|
{
|
|
u8 c, last = 0xff;
|
|
|
|
while (count--) {
|
|
c = *src++;
|
|
if ((c & 0xf0) == last) {
|
|
*dst++ = 0x70 | (c & 0xf);
|
|
} else {
|
|
*dst++ = c;
|
|
last = c & 0xf0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* When looking for repeats, we don't care about the trailing space. This
|
|
* is set to the shortest possible anyway.
|
|
*/
|
|
static int cmp_no_trail(u8 *a, u8 *b, unsigned int count)
|
|
{
|
|
while (--count) {
|
|
if (*a++ != *b++)
|
|
return 1;
|
|
}
|
|
|
|
return (*a & 0xf0) - (*b & 0xf0);
|
|
}
|
|
|
|
static int find_slot(u16 *array, unsigned int size, u16 val)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < size; i++) {
|
|
if (get_unaligned_be16(&array[i]) == val) {
|
|
return i;
|
|
} else if (!array[i]) {
|
|
put_unaligned_be16(val, &array[i]);
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int zilog_ir_format(struct rc_dev *rcdev, unsigned int *txbuf,
|
|
unsigned int count, struct code_block *code_block)
|
|
{
|
|
struct IR_i2c *ir = rcdev->priv;
|
|
int rep, i, l, p = 0, s, c = 0;
|
|
bool repeating;
|
|
u8 codes[174];
|
|
|
|
code_block->carrier_pulse = DIV_ROUND_CLOSEST(
|
|
ir->duty_cycle * XTAL_FREQ / 1000, ir->carrier);
|
|
code_block->carrier_space = DIV_ROUND_CLOSEST(
|
|
(100 - ir->duty_cycle) * XTAL_FREQ / 1000, ir->carrier);
|
|
|
|
for (i = 0; i < count; i++) {
|
|
if (c >= ARRAY_SIZE(codes) - 1) {
|
|
dev_warn(&rcdev->dev, "IR too long, cannot transmit\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*
|
|
* Lengths more than 142220us cannot be encoded; also
|
|
* this checks for multiply overflow
|
|
*/
|
|
if (txbuf[i] > 142220)
|
|
return -EINVAL;
|
|
|
|
l = DIV_ROUND_CLOSEST((XTAL_FREQ / 1000) * txbuf[i], 40000);
|
|
|
|
if (i & 1) {
|
|
s = find_slot(code_block->space,
|
|
ARRAY_SIZE(code_block->space), l);
|
|
if (s == -1) {
|
|
dev_warn(&rcdev->dev, "Too many different lengths spaces, cannot transmit");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* We have a pulse and space */
|
|
codes[c++] = (p << 4) | s;
|
|
} else {
|
|
p = find_slot(code_block->pulse,
|
|
ARRAY_SIZE(code_block->pulse), l);
|
|
if (p == -1) {
|
|
dev_warn(&rcdev->dev, "Too many different lengths pulses, cannot transmit");
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* We have to encode the trailing pulse. Find the shortest space */
|
|
s = 0;
|
|
for (i = 1; i < ARRAY_SIZE(code_block->space); i++) {
|
|
u16 d = get_unaligned_be16(&code_block->space[i]);
|
|
|
|
if (get_unaligned_be16(&code_block->space[s]) > d)
|
|
s = i;
|
|
}
|
|
|
|
codes[c++] = (p << 4) | s;
|
|
|
|
dev_dbg(&rcdev->dev, "generated %d codes\n", c);
|
|
|
|
/*
|
|
* Are the last N codes (so pulse + space) repeating 3 times?
|
|
* if so we can shorten the codes list and use code 0xc0 to repeat
|
|
* them.
|
|
*/
|
|
repeating = false;
|
|
|
|
for (rep = c / 3; rep >= 1; rep--) {
|
|
if (!memcmp(&codes[c - rep * 3], &codes[c - rep * 2], rep) &&
|
|
!cmp_no_trail(&codes[c - rep], &codes[c - rep * 2], rep)) {
|
|
repeating = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (repeating) {
|
|
/* first copy any leading non-repeating */
|
|
int leading = c - rep * 3;
|
|
|
|
if (leading >= ARRAY_SIZE(code_block->codes) - 3 - rep) {
|
|
dev_warn(&rcdev->dev, "IR too long, cannot transmit\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
dev_dbg(&rcdev->dev, "found trailing %d repeat\n", rep);
|
|
copy_codes(code_block->codes, codes, leading);
|
|
code_block->codes[leading] = 0x82;
|
|
copy_codes(code_block->codes + leading + 1, codes + leading,
|
|
rep);
|
|
c = leading + 1 + rep;
|
|
code_block->codes[c++] = 0xc0;
|
|
} else {
|
|
if (c >= ARRAY_SIZE(code_block->codes) - 3) {
|
|
dev_warn(&rcdev->dev, "IR too long, cannot transmit\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
dev_dbg(&rcdev->dev, "found no trailing repeat\n");
|
|
code_block->codes[0] = 0x82;
|
|
copy_codes(code_block->codes + 1, codes, c);
|
|
c++;
|
|
code_block->codes[c++] = 0xc4;
|
|
}
|
|
|
|
while (c < ARRAY_SIZE(code_block->codes))
|
|
code_block->codes[c++] = 0x83;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int zilog_tx(struct rc_dev *rcdev, unsigned int *txbuf,
|
|
unsigned int count)
|
|
{
|
|
struct IR_i2c *ir = rcdev->priv;
|
|
struct code_block code_block = { .length = sizeof(code_block) };
|
|
u8 buf[2];
|
|
int ret, i;
|
|
|
|
ret = zilog_ir_format(rcdev, txbuf, count, &code_block);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = mutex_lock_interruptible(&ir->lock);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = send_data_block(ir, ZILOG_UIR_END, &code_block);
|
|
if (ret)
|
|
goto out_unlock;
|
|
|
|
ret = i2c_master_recv(ir->tx_c, buf, 1);
|
|
if (ret != 1) {
|
|
dev_err(&ir->rc->dev, "i2c_master_recv failed with %d\n", ret);
|
|
goto out_unlock;
|
|
}
|
|
|
|
dev_dbg(&ir->rc->dev, "code set status: %02x\n", buf[0]);
|
|
|
|
if (buf[0] != (ZILOG_STATUS_OK | ZILOG_STATUS_SET)) {
|
|
dev_err(&ir->rc->dev, "unexpected IR TX response %02x\n",
|
|
buf[0]);
|
|
ret = -EIO;
|
|
goto out_unlock;
|
|
}
|
|
|
|
buf[0] = 0x00;
|
|
buf[1] = ZILOG_SEND;
|
|
|
|
ret = i2c_master_send(ir->tx_c, buf, 2);
|
|
if (ret != 2) {
|
|
dev_err(&ir->rc->dev, "i2c_master_send failed with %d\n", ret);
|
|
if (ret >= 0)
|
|
ret = -EIO;
|
|
goto out_unlock;
|
|
}
|
|
|
|
dev_dbg(&ir->rc->dev, "send command sent\n");
|
|
|
|
/*
|
|
* This bit NAKs until the device is ready, so we retry it
|
|
* sleeping a bit each time. This seems to be what the windows
|
|
* driver does, approximately.
|
|
* Try for up to 1s.
|
|
*/
|
|
for (i = 0; i < 20; ++i) {
|
|
set_current_state(TASK_UNINTERRUPTIBLE);
|
|
schedule_timeout(msecs_to_jiffies(50));
|
|
ret = i2c_master_send(ir->tx_c, buf, 1);
|
|
if (ret == 1)
|
|
break;
|
|
dev_dbg(&ir->rc->dev,
|
|
"NAK expected: i2c_master_send failed with %d (try %d)\n",
|
|
ret, i + 1);
|
|
}
|
|
|
|
if (ret != 1) {
|
|
dev_err(&ir->rc->dev,
|
|
"IR TX chip never got ready: last i2c_master_send failed with %d\n",
|
|
ret);
|
|
if (ret >= 0)
|
|
ret = -EIO;
|
|
goto out_unlock;
|
|
}
|
|
|
|
i = i2c_master_recv(ir->tx_c, buf, 1);
|
|
if (i != 1) {
|
|
dev_err(&ir->rc->dev, "i2c_master_recv failed with %d\n", ret);
|
|
ret = -EIO;
|
|
goto out_unlock;
|
|
} else if (buf[0] != ZILOG_STATUS_OK) {
|
|
dev_err(&ir->rc->dev, "unexpected IR TX response #2: %02x\n",
|
|
buf[0]);
|
|
ret = -EIO;
|
|
goto out_unlock;
|
|
}
|
|
dev_dbg(&ir->rc->dev, "transmit complete\n");
|
|
|
|
/* Oh good, it worked */
|
|
ret = count;
|
|
out_unlock:
|
|
mutex_unlock(&ir->lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int zilog_tx_carrier(struct rc_dev *dev, u32 carrier)
|
|
{
|
|
struct IR_i2c *ir = dev->priv;
|
|
|
|
if (carrier > 500000 || carrier < 20000)
|
|
return -EINVAL;
|
|
|
|
ir->carrier = carrier;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int zilog_tx_duty_cycle(struct rc_dev *dev, u32 duty_cycle)
|
|
{
|
|
struct IR_i2c *ir = dev->priv;
|
|
|
|
ir->duty_cycle = duty_cycle;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
|
{
|
|
char *ir_codes = NULL;
|
|
const char *name = NULL;
|
|
u64 rc_proto = RC_PROTO_BIT_UNKNOWN;
|
|
struct IR_i2c *ir;
|
|
struct rc_dev *rc = NULL;
|
|
struct i2c_adapter *adap = client->adapter;
|
|
unsigned short addr = client->addr;
|
|
bool probe_tx = (id->driver_data & FLAG_TX) != 0;
|
|
int err;
|
|
|
|
if ((id->driver_data & FLAG_HDPVR) && !enable_hdpvr) {
|
|
dev_err(&client->dev, "IR for HDPVR is known to cause problems during recording, use enable_hdpvr modparam to enable\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
ir = devm_kzalloc(&client->dev, sizeof(*ir), GFP_KERNEL);
|
|
if (!ir)
|
|
return -ENOMEM;
|
|
|
|
ir->c = client;
|
|
ir->polling_interval = DEFAULT_POLLING_INTERVAL;
|
|
i2c_set_clientdata(client, ir);
|
|
|
|
switch(addr) {
|
|
case 0x64:
|
|
name = "Pixelview";
|
|
ir->get_key = get_key_pixelview;
|
|
rc_proto = RC_PROTO_BIT_OTHER;
|
|
ir_codes = RC_MAP_EMPTY;
|
|
break;
|
|
case 0x18:
|
|
case 0x1f:
|
|
case 0x1a:
|
|
name = "Hauppauge";
|
|
ir->get_key = get_key_haup;
|
|
rc_proto = RC_PROTO_BIT_RC5;
|
|
ir_codes = RC_MAP_HAUPPAUGE;
|
|
break;
|
|
case 0x30:
|
|
name = "KNC One";
|
|
ir->get_key = get_key_knc1;
|
|
rc_proto = RC_PROTO_BIT_OTHER;
|
|
ir_codes = RC_MAP_EMPTY;
|
|
break;
|
|
case 0x6b:
|
|
name = "FusionHDTV";
|
|
ir->get_key = get_key_fusionhdtv;
|
|
rc_proto = RC_PROTO_BIT_UNKNOWN;
|
|
ir_codes = RC_MAP_FUSIONHDTV_MCE;
|
|
break;
|
|
case 0x40:
|
|
name = "AVerMedia Cardbus remote";
|
|
ir->get_key = get_key_avermedia_cardbus;
|
|
rc_proto = RC_PROTO_BIT_OTHER;
|
|
ir_codes = RC_MAP_AVERMEDIA_CARDBUS;
|
|
break;
|
|
case 0x41:
|
|
name = "AVerMedia EM78P153";
|
|
ir->get_key = get_key_avermedia_cardbus;
|
|
rc_proto = RC_PROTO_BIT_OTHER;
|
|
/* RM-KV remote, seems to be same as RM-K6 */
|
|
ir_codes = RC_MAP_AVERMEDIA_M733A_RM_K6;
|
|
break;
|
|
case 0x71:
|
|
name = "Hauppauge/Zilog Z8";
|
|
ir->get_key = get_key_haup_xvr;
|
|
rc_proto = RC_PROTO_BIT_RC5 | RC_PROTO_BIT_RC6_MCE |
|
|
RC_PROTO_BIT_RC6_6A_32;
|
|
ir_codes = RC_MAP_HAUPPAUGE;
|
|
probe_tx = true;
|
|
break;
|
|
}
|
|
|
|
/* Let the caller override settings */
|
|
if (client->dev.platform_data) {
|
|
const struct IR_i2c_init_data *init_data =
|
|
client->dev.platform_data;
|
|
|
|
ir_codes = init_data->ir_codes;
|
|
rc = init_data->rc_dev;
|
|
|
|
name = init_data->name;
|
|
if (init_data->type)
|
|
rc_proto = init_data->type;
|
|
|
|
if (init_data->polling_interval)
|
|
ir->polling_interval = init_data->polling_interval;
|
|
|
|
switch (init_data->internal_get_key_func) {
|
|
case IR_KBD_GET_KEY_CUSTOM:
|
|
/* The bridge driver provided us its own function */
|
|
ir->get_key = init_data->get_key;
|
|
break;
|
|
case IR_KBD_GET_KEY_PIXELVIEW:
|
|
ir->get_key = get_key_pixelview;
|
|
break;
|
|
case IR_KBD_GET_KEY_HAUP:
|
|
ir->get_key = get_key_haup;
|
|
break;
|
|
case IR_KBD_GET_KEY_KNC1:
|
|
ir->get_key = get_key_knc1;
|
|
break;
|
|
case IR_KBD_GET_KEY_FUSIONHDTV:
|
|
ir->get_key = get_key_fusionhdtv;
|
|
break;
|
|
case IR_KBD_GET_KEY_HAUP_XVR:
|
|
ir->get_key = get_key_haup_xvr;
|
|
break;
|
|
case IR_KBD_GET_KEY_AVERMEDIA_CARDBUS:
|
|
ir->get_key = get_key_avermedia_cardbus;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!rc) {
|
|
/*
|
|
* If platform_data doesn't specify rc_dev, initialize it
|
|
* internally
|
|
*/
|
|
rc = rc_allocate_device(RC_DRIVER_SCANCODE);
|
|
if (!rc)
|
|
return -ENOMEM;
|
|
}
|
|
ir->rc = rc;
|
|
|
|
/* Make sure we are all setup before going on */
|
|
if (!name || !ir->get_key || !rc_proto || !ir_codes) {
|
|
dev_warn(&client->dev, "Unsupported device at address 0x%02x\n",
|
|
addr);
|
|
err = -ENODEV;
|
|
goto err_out_free;
|
|
}
|
|
|
|
ir->ir_codes = ir_codes;
|
|
|
|
snprintf(ir->phys, sizeof(ir->phys), "%s/%s", dev_name(&adap->dev),
|
|
dev_name(&client->dev));
|
|
|
|
/*
|
|
* Initialize input_dev fields
|
|
* It doesn't make sense to allow overriding them via platform_data
|
|
*/
|
|
rc->input_id.bustype = BUS_I2C;
|
|
rc->input_phys = ir->phys;
|
|
rc->device_name = name;
|
|
rc->dev.parent = &client->dev;
|
|
rc->priv = ir;
|
|
rc->open = ir_open;
|
|
rc->close = ir_close;
|
|
|
|
/*
|
|
* Initialize the other fields of rc_dev
|
|
*/
|
|
rc->map_name = ir->ir_codes;
|
|
rc->allowed_protocols = rc_proto;
|
|
if (!rc->driver_name)
|
|
rc->driver_name = KBUILD_MODNAME;
|
|
|
|
mutex_init(&ir->lock);
|
|
|
|
INIT_DELAYED_WORK(&ir->work, ir_work);
|
|
|
|
if (probe_tx) {
|
|
ir->tx_c = i2c_new_dummy(client->adapter, 0x70);
|
|
if (!ir->tx_c) {
|
|
dev_err(&client->dev, "failed to setup tx i2c address");
|
|
} else if (!zilog_init(ir)) {
|
|
ir->carrier = 38000;
|
|
ir->duty_cycle = 40;
|
|
rc->tx_ir = zilog_tx;
|
|
rc->s_tx_carrier = zilog_tx_carrier;
|
|
rc->s_tx_duty_cycle = zilog_tx_duty_cycle;
|
|
}
|
|
}
|
|
|
|
err = rc_register_device(rc);
|
|
if (err)
|
|
goto err_out_free;
|
|
|
|
return 0;
|
|
|
|
err_out_free:
|
|
if (ir->tx_c)
|
|
i2c_unregister_device(ir->tx_c);
|
|
|
|
/* Only frees rc if it were allocated internally */
|
|
rc_free_device(rc);
|
|
return err;
|
|
}
|
|
|
|
static int ir_remove(struct i2c_client *client)
|
|
{
|
|
struct IR_i2c *ir = i2c_get_clientdata(client);
|
|
|
|
/* kill outstanding polls */
|
|
cancel_delayed_work_sync(&ir->work);
|
|
|
|
if (ir->tx_c)
|
|
i2c_unregister_device(ir->tx_c);
|
|
|
|
/* unregister device */
|
|
rc_unregister_device(ir->rc);
|
|
|
|
/* free memory */
|
|
return 0;
|
|
}
|
|
|
|
static const struct i2c_device_id ir_kbd_id[] = {
|
|
/* Generic entry for any IR receiver */
|
|
{ "ir_video", 0 },
|
|
/* IR device specific entries should be added here */
|
|
{ "ir_z8f0811_haup", FLAG_TX },
|
|
{ "ir_z8f0811_hdpvr", FLAG_TX | FLAG_HDPVR },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(i2c, ir_kbd_id);
|
|
|
|
static struct i2c_driver ir_kbd_driver = {
|
|
.driver = {
|
|
.name = "ir-kbd-i2c",
|
|
},
|
|
.probe = ir_probe,
|
|
.remove = ir_remove,
|
|
.id_table = ir_kbd_id,
|
|
};
|
|
|
|
module_i2c_driver(ir_kbd_driver);
|
|
|
|
/* ----------------------------------------------------------------------- */
|
|
|
|
MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, Ulrich Mueller");
|
|
MODULE_DESCRIPTION("input driver for i2c IR remote controls");
|
|
MODULE_LICENSE("GPL");
|