diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 01:02:30 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 01:02:30 +0000 |
commit | 76cb841cb886eef6b3bee341a2266c76578724ad (patch) | |
tree | f5892e5ba6cc11949952a6ce4ecbe6d516d6ce58 /drivers/media/i2c/ir-kbd-i2c.c | |
parent | Initial commit. (diff) | |
download | linux-76cb841cb886eef6b3bee341a2266c76578724ad.tar.xz linux-76cb841cb886eef6b3bee341a2266c76578724ad.zip |
Adding upstream version 4.19.249.upstream/4.19.249
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/media/i2c/ir-kbd-i2c.c')
-rw-r--r-- | drivers/media/i2c/ir-kbd-i2c.c | 968 |
1 files changed, 968 insertions, 0 deletions
diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c new file mode 100644 index 000000000..933ddfcc5 --- /dev/null +++ b/drivers/media/i2c/ir-kbd-i2c.c @@ -0,0 +1,968 @@ +/* + * + * 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> + * + * 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. + * + */ + +#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; + } + + 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); + 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; + ir->polling_interval = 125; + 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"); |