diff options
Diffstat (limited to 'drivers/media/rc')
-rw-r--r-- | drivers/media/rc/iguanair.c | 15 | ||||
-rw-r--r-- | drivers/media/rc/img-ir/img-ir-core.c | 4 | ||||
-rw-r--r-- | drivers/media/rc/imon.c | 7 | ||||
-rw-r--r-- | drivers/media/rc/imon_raw.c | 43 | ||||
-rw-r--r-- | drivers/media/rc/ir-hix5hd2.c | 4 | ||||
-rw-r--r-- | drivers/media/rc/keymaps/Makefile | 7 | ||||
-rw-r--r-- | drivers/media/rc/keymaps/rc-imon-rsc.c | 7 | ||||
-rw-r--r-- | drivers/media/rc/keymaps/rc-khadas.c | 54 | ||||
-rw-r--r-- | drivers/media/rc/keymaps/rc-odroid.c | 54 | ||||
-rw-r--r-- | drivers/media/rc/keymaps/rc-tanix-tx3mini.c | 77 | ||||
-rw-r--r-- | drivers/media/rc/keymaps/rc-tanix-tx5max.c | 68 | ||||
-rw-r--r-- | drivers/media/rc/keymaps/rc-wetek-hub.c | 53 | ||||
-rw-r--r-- | drivers/media/rc/keymaps/rc-wetek-play2.c | 93 | ||||
-rw-r--r-- | drivers/media/rc/keymaps/rc-x96max.c | 83 | ||||
-rw-r--r-- | drivers/media/rc/mceusb.c | 401 | ||||
-rw-r--r-- | drivers/media/rc/meson-ir.c | 4 | ||||
-rw-r--r-- | drivers/media/rc/mtk-cir.c | 14 | ||||
-rw-r--r-- | drivers/media/rc/sunxi-cir.c | 89 |
18 files changed, 872 insertions, 205 deletions
diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c index ea05e125016a..872d6441e512 100644 --- a/drivers/media/rc/iguanair.c +++ b/drivers/media/rc/iguanair.c @@ -413,6 +413,10 @@ static int iguanair_probe(struct usb_interface *intf, int ret, pipein, pipeout; struct usb_host_interface *idesc; + idesc = intf->altsetting; + if (idesc->desc.bNumEndpoints < 2) + return -ENODEV; + ir = kzalloc(sizeof(*ir), GFP_KERNEL); rc = rc_allocate_device(RC_DRIVER_IR_RAW); if (!ir || !rc) { @@ -427,18 +431,13 @@ static int iguanair_probe(struct usb_interface *intf, ir->urb_in = usb_alloc_urb(0, GFP_KERNEL); ir->urb_out = usb_alloc_urb(0, GFP_KERNEL); - if (!ir->buf_in || !ir->packet || !ir->urb_in || !ir->urb_out) { + if (!ir->buf_in || !ir->packet || !ir->urb_in || !ir->urb_out || + !usb_endpoint_is_int_in(&idesc->endpoint[0].desc) || + !usb_endpoint_is_int_out(&idesc->endpoint[1].desc)) { ret = -ENOMEM; goto out; } - idesc = intf->altsetting; - - if (idesc->desc.bNumEndpoints < 2) { - ret = -ENODEV; - goto out; - } - ir->rc = rc; ir->dev = &intf->dev; ir->udev = udev; diff --git a/drivers/media/rc/img-ir/img-ir-core.c b/drivers/media/rc/img-ir/img-ir-core.c index 7e457f26a595..094aa6a06315 100644 --- a/drivers/media/rc/img-ir/img-ir-core.c +++ b/drivers/media/rc/img-ir/img-ir-core.c @@ -81,10 +81,8 @@ static int img_ir_probe(struct platform_device *pdev) /* Get resources from platform device */ irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "cannot find IRQ resource\n"); + if (irq < 0) return irq; - } /* Private driver data */ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index 7bee72108b0e..37a850421fbb 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -1826,12 +1826,17 @@ static void imon_get_ffdc_type(struct imon_context *ictx) break; /* iMON VFD, MCE IR */ case 0x46: - case 0x7e: case 0x9e: dev_info(ictx->dev, "0xffdc iMON VFD, MCE IR"); detected_display_type = IMON_DISPLAY_TYPE_VFD; allowed_protos = RC_PROTO_BIT_RC6_MCE; break; + /* iMON VFD, iMON or MCE IR */ + case 0x7e: + dev_info(ictx->dev, "0xffdc iMON VFD, iMON or MCE IR"); + detected_display_type = IMON_DISPLAY_TYPE_VFD; + allowed_protos |= RC_PROTO_BIT_RC6_MCE; + break; /* iMON LCD, MCE IR */ case 0x9f: dev_info(ictx->dev, "0xffdc iMON LCD, MCE IR"); diff --git a/drivers/media/rc/imon_raw.c b/drivers/media/rc/imon_raw.c index 25e56c5b13c0..d4aedcf76418 100644 --- a/drivers/media/rc/imon_raw.c +++ b/drivers/media/rc/imon_raw.c @@ -14,7 +14,7 @@ struct imon { struct device *dev; struct urb *ir_urb; struct rc_dev *rcdev; - u8 ir_buf[8] __aligned(__alignof__(u64)); + __be64 ir_buf; char phys[64]; }; @@ -29,14 +29,35 @@ struct imon { static void imon_ir_data(struct imon *imon) { struct ir_raw_event rawir = {}; - u64 d = be64_to_cpup((__be64 *)imon->ir_buf) >> 24; + u64 data = be64_to_cpu(imon->ir_buf); + u8 packet_no = data & 0xff; int offset = 40; int bit; - dev_dbg(imon->dev, "data: %*ph", 8, imon->ir_buf); + if (packet_no == 0xff) + return; + + dev_dbg(imon->dev, "data: %*ph", 8, &imon->ir_buf); + + /* + * Only the first 5 bytes contain IR data. Right shift so we move + * the IR bits to the lower 40 bits. + */ + data >>= 24; do { - bit = fls64(d & (BIT_ULL(offset) - 1)); + /* + * Find highest set bit which is less or equal to offset + * + * offset is the bit above (base 0) where we start looking. + * + * data & (BIT_ULL(offset) - 1) masks off any unwanted bits, + * so we have just bits less than offset. + * + * fls will tell us the highest bit set plus 1 (or 0 if no + * bits are set). + */ + bit = fls64(data & (BIT_ULL(offset) - 1)); if (bit < offset) { dev_dbg(imon->dev, "pulse: %d bits", offset - bit); rawir.pulse = true; @@ -49,7 +70,12 @@ static void imon_ir_data(struct imon *imon) offset = bit; } - bit = fls64(~d & (BIT_ULL(offset) - 1)); + /* + * Find highest clear bit which is less than offset. + * + * Just invert the data and use same trick as above. + */ + bit = fls64(~data & (BIT_ULL(offset) - 1)); dev_dbg(imon->dev, "space: %d bits", offset - bit); rawir.pulse = false; @@ -59,7 +85,7 @@ static void imon_ir_data(struct imon *imon) offset = bit; } while (offset > 0); - if (imon->ir_buf[7] == 0x0a) { + if (packet_no == 0x0a && !imon->rcdev->idle) { ir_raw_event_set_idle(imon->rcdev, true); ir_raw_event_handle(imon->rcdev); } @@ -72,8 +98,7 @@ static void imon_ir_rx(struct urb *urb) switch (urb->status) { case 0: - if (imon->ir_buf[7] != 0xff) - imon_ir_data(imon); + imon_ir_data(imon); break; case -ECONNRESET: case -ENOENT: @@ -129,7 +154,7 @@ static int imon_probe(struct usb_interface *intf, imon->dev = &intf->dev; usb_fill_int_urb(imon->ir_urb, udev, usb_rcvintpipe(udev, ir_ep->bEndpointAddress), - imon->ir_buf, sizeof(imon->ir_buf), + &imon->ir_buf, sizeof(imon->ir_buf), imon_ir_rx, imon, ir_ep->bInterval); rcdev = devm_rc_allocate_device(&intf->dev, RC_DRIVER_IR_RAW); diff --git a/drivers/media/rc/ir-hix5hd2.c b/drivers/media/rc/ir-hix5hd2.c index 85561f6555a2..32ccefeff57d 100644 --- a/drivers/media/rc/ir-hix5hd2.c +++ b/drivers/media/rc/ir-hix5hd2.c @@ -232,10 +232,8 @@ static int hix5hd2_ir_probe(struct platform_device *pdev) return PTR_ERR(priv->base); priv->irq = platform_get_irq(pdev, 0); - if (priv->irq < 0) { - dev_err(dev, "irq can not get\n"); + if (priv->irq < 0) return priv->irq; - } rdev = rc_allocate_device(RC_DRIVER_IR_RAW); if (!rdev) diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index 5b1399af6b3a..a56fc634d2d6 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-it913x-v1.o \ rc-it913x-v2.o \ rc-kaiomy.o \ + rc-khadas.o \ rc-kworld-315u.o \ rc-kworld-pc150u.o \ rc-kworld-plus-tv-analog.o \ @@ -75,6 +76,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-nec-terratec-cinergy-xs.o \ rc-norwood.o \ rc-npgtech.o \ + rc-odroid.o \ rc-pctv-sedna.o \ rc-pinnacle-color.o \ rc-pinnacle-grey.o \ @@ -94,6 +96,8 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-snapstream-firefly.o \ rc-streamzap.o \ rc-tango.o \ + rc-tanix-tx3mini.o \ + rc-tanix-tx5max.o \ rc-tbs-nec.o \ rc-technisat-ts35.o \ rc-technisat-usb2.o \ @@ -113,8 +117,11 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-videomate-m1f.o \ rc-videomate-s350.o \ rc-videomate-tv-pvr.o \ + rc-wetek-hub.o \ + rc-wetek-play2.o \ rc-winfast.o \ rc-winfast-usbii-deluxe.o \ rc-su3000.o \ rc-xbox-dvd.o \ + rc-x96max.o \ rc-zx-irdec.o diff --git a/drivers/media/rc/keymaps/rc-imon-rsc.c b/drivers/media/rc/keymaps/rc-imon-rsc.c index 6f7ee4859682..38787dd0e4a0 100644 --- a/drivers/media/rc/keymaps/rc-imon-rsc.c +++ b/drivers/media/rc/keymaps/rc-imon-rsc.c @@ -7,7 +7,8 @@ // // Note that this remote has a stick which its own IR protocol, -// with 16 directions. This is not supported yet. +// with 16 directions. This is supported by the imon_rsc BPF decoder +// in v4l-utils. // static struct rc_map_table imon_rsc[] = { { 0x801010, KEY_EXIT }, @@ -25,7 +26,7 @@ static struct rc_map_table imon_rsc[] = { { 0x80105c, KEY_NUMERIC_9 }, { 0x801081, KEY_SCREEN }, /* Desktop */ { 0x80105d, KEY_NUMERIC_0 }, - { 0x801082, KEY_MAX }, + { 0x801082, KEY_ZOOM }, /* Maximise */ { 0x801048, KEY_ESC }, { 0x80104b, KEY_MEDIA }, /* Windows key */ { 0x801083, KEY_MENU }, @@ -52,7 +53,7 @@ static struct rc_map_table imon_rsc[] = { { 0x80104e, KEY_STOP }, { 0x801052, KEY_REWIND }, { 0x801053, KEY_FASTFORWARD }, - { 0x801089, KEY_ZOOM } /* full screen */ + { 0x801089, KEY_FULL_SCREEN } /* full screen */ }; static struct rc_map_list imon_rsc_map = { diff --git a/drivers/media/rc/keymaps/rc-khadas.c b/drivers/media/rc/keymaps/rc-khadas.c new file mode 100644 index 000000000000..ce4938444d90 --- /dev/null +++ b/drivers/media/rc/keymaps/rc-khadas.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Copyright (C) 2019 Christian Hewitt <christianshewitt@gmail.com> + +/* + * Keytable for the Khadas VIM/EDGE SBC remote control + */ + +#include <media/rc-map.h> +#include <linux/module.h> + +static struct rc_map_table khadas[] = { + { 0x14, KEY_POWER }, + + { 0x03, KEY_UP }, + { 0x02, KEY_DOWN }, + { 0x0e, KEY_LEFT }, + { 0x1a, KEY_RIGHT }, + { 0x07, KEY_OK }, + + { 0x01, KEY_BACK }, + { 0x5b, KEY_MUTE }, // mouse + { 0x13, KEY_MENU }, + + { 0x58, KEY_VOLUMEDOWN }, + { 0x0b, KEY_VOLUMEUP }, + + { 0x48, KEY_HOME }, +}; + +static struct rc_map_list khadas_map = { + .map = { + .scan = khadas, + .size = ARRAY_SIZE(khadas), + .rc_proto = RC_PROTO_NEC, + .name = RC_MAP_KHADAS, + } +}; + +static int __init init_rc_map_khadas(void) +{ + return rc_map_register(&khadas_map); +} + +static void __exit exit_rc_map_khadas(void) +{ + rc_map_unregister(&khadas_map); +} + +module_init(init_rc_map_khadas) +module_exit(exit_rc_map_khadas) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Christian Hewitt <christianshewitt@gmail.com>"); diff --git a/drivers/media/rc/keymaps/rc-odroid.c b/drivers/media/rc/keymaps/rc-odroid.c new file mode 100644 index 000000000000..c6fbb64b5c41 --- /dev/null +++ b/drivers/media/rc/keymaps/rc-odroid.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Copyright (C) 2019 Christian Hewitt <christianshewitt@gmail.com> + +#include <media/rc-map.h> +#include <linux/module.h> + +// +// Keytable for the HardKernel ODROID remote control +// + +static struct rc_map_table odroid[] = { + { 0xb2dc, KEY_POWER }, + + { 0xb288, KEY_MUTE }, + { 0xb282, KEY_HOME }, + + { 0xb2ca, KEY_UP }, + { 0xb299, KEY_LEFT }, + { 0xb2ce, KEY_OK }, + { 0xb2c1, KEY_RIGHT }, + { 0xb2d2, KEY_DOWN }, + + { 0xb2c5, KEY_MENU }, + { 0xb29a, KEY_BACK }, + + { 0xb281, KEY_VOLUMEDOWN }, + { 0xb280, KEY_VOLUMEUP }, +}; + +static struct rc_map_list odroid_map = { + .map = { + .scan = odroid, + .size = ARRAY_SIZE(odroid), + .rc_proto = RC_PROTO_NEC, + .name = RC_MAP_ODROID, + } +}; + +static int __init init_rc_map_odroid(void) +{ + return rc_map_register(&odroid_map); +} + +static void __exit exit_rc_map_odroid(void) +{ + rc_map_unregister(&odroid_map); +} + +module_init(init_rc_map_odroid) +module_exit(exit_rc_map_odroid) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Christian Hewitt <christianshewitt@gmail.com"); diff --git a/drivers/media/rc/keymaps/rc-tanix-tx3mini.c b/drivers/media/rc/keymaps/rc-tanix-tx3mini.c new file mode 100644 index 000000000000..d486cd69afb2 --- /dev/null +++ b/drivers/media/rc/keymaps/rc-tanix-tx3mini.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright (c) 2018 Christian Hewitt + +#include <media/rc-map.h> +#include <linux/module.h> + +/* + * Keymap for the Tanix TX3 mini STB remote control + */ + +static struct rc_map_table tanix_tx3mini[] = { + { 0x8051, KEY_POWER }, + { 0x804d, KEY_MUTE }, + + { 0x8009, KEY_RED }, + { 0x8011, KEY_GREEN }, + { 0x8054, KEY_YELLOW }, + { 0x804f, KEY_BLUE }, + + { 0x8056, KEY_VOLUMEDOWN }, + { 0x80bd, KEY_PREVIOUS }, + { 0x80bb, KEY_NEXT }, + { 0x804e, KEY_VOLUMEUP }, + + { 0x8053, KEY_HOME }, + { 0x801b, KEY_BACK }, + + { 0x8026, KEY_UP }, + { 0x8028, KEY_DOWN }, + { 0x8025, KEY_LEFT }, + { 0x8027, KEY_RIGHT }, + { 0x800d, KEY_OK }, + + { 0x8049, KEY_MENU }, + { 0x8052, KEY_EPG }, // mouse + + { 0x8031, KEY_1 }, + { 0x8032, KEY_2 }, + { 0x8033, KEY_3 }, + + { 0x8034, KEY_4 }, + { 0x8035, KEY_5 }, + { 0x8036, KEY_6 }, + + { 0x8037, KEY_7 }, + { 0x8038, KEY_8 }, + { 0x8039, KEY_9 }, + + { 0x8058, KEY_SUBTITLE }, // 1/a + { 0x8030, KEY_0 }, + { 0x8044, KEY_DELETE }, +}; + +static struct rc_map_list tanix_tx3mini_map = { + .map = { + .scan = tanix_tx3mini, + .size = ARRAY_SIZE(tanix_tx3mini), + .rc_proto = RC_PROTO_NEC, + .name = RC_MAP_TANIX_TX3MINI, + } +}; + +static int __init init_rc_map_tanix_tx3mini(void) +{ + return rc_map_register(&tanix_tx3mini_map); +} + +static void __exit exit_rc_map_tanix_tx3mini(void) +{ + rc_map_unregister(&tanix_tx3mini_map); +} + +module_init(init_rc_map_tanix_tx3mini) +module_exit(exit_rc_map_tanix_tx3mini) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Christian Hewitt <christianshewitt@gmail.com>"); diff --git a/drivers/media/rc/keymaps/rc-tanix-tx5max.c b/drivers/media/rc/keymaps/rc-tanix-tx5max.c new file mode 100644 index 000000000000..59aaabed80dd --- /dev/null +++ b/drivers/media/rc/keymaps/rc-tanix-tx5max.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright (c) 2018 Christian Hewitt + +#include <media/rc-map.h> +#include <linux/module.h> + +/* + * Keymap for the Tanix TX5 max STB remote control + */ + +static struct rc_map_table tanix_tx5max[] = { + { 0x40404d, KEY_POWER }, + { 0x404043, KEY_MUTE }, + + { 0x404017, KEY_VOLUMEDOWN }, + { 0x404018, KEY_VOLUMEUP }, + + { 0x40400b, KEY_UP }, + { 0x404010, KEY_LEFT }, + { 0x404011, KEY_RIGHT }, + { 0x40400e, KEY_DOWN }, + { 0x40400d, KEY_OK }, + + { 0x40401a, KEY_HOME }, + { 0x404045, KEY_MENU }, + { 0x404042, KEY_BACK }, + + { 0x404001, KEY_1 }, + { 0x404002, KEY_2 }, + { 0x404003, KEY_3 }, + + { 0x404004, KEY_4 }, + { 0x404005, KEY_5 }, + { 0x404006, KEY_6 }, + + { 0x404007, KEY_7 }, + { 0x404008, KEY_8 }, + { 0x404009, KEY_9 }, + + { 0x404047, KEY_SUBTITLE }, // mouse + { 0x404000, KEY_0 }, + { 0x40400c, KEY_DELETE }, +}; + +static struct rc_map_list tanix_tx5max_map = { + .map = { + .scan = tanix_tx5max, + .size = ARRAY_SIZE(tanix_tx5max), + .rc_proto = RC_PROTO_NECX, + .name = RC_MAP_TANIX_TX5MAX, + } +}; + +static int __init init_rc_map_tanix_tx5max(void) +{ + return rc_map_register(&tanix_tx5max_map); +} + +static void __exit exit_rc_map_tanix_tx5max(void) +{ + rc_map_unregister(&tanix_tx5max_map); +} + +module_init(init_rc_map_tanix_tx5max) +module_exit(exit_rc_map_tanix_tx5max) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Christian Hewitt <christianshewitt@gmail.com>"); diff --git a/drivers/media/rc/keymaps/rc-wetek-hub.c b/drivers/media/rc/keymaps/rc-wetek-hub.c new file mode 100644 index 000000000000..b5a21aff45f5 --- /dev/null +++ b/drivers/media/rc/keymaps/rc-wetek-hub.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright (c) 2018 Christian Hewitt + +#include <media/rc-map.h> +#include <linux/module.h> + +/* + * This keymap is used with the WeTek Hub STB. + */ + +static struct rc_map_table wetek_hub[] = { + { 0x77f1, KEY_POWER }, + + { 0x77f2, KEY_HOME }, + { 0x77f3, KEY_MUTE }, // mouse + + { 0x77f4, KEY_UP }, + { 0x77f5, KEY_DOWN }, + { 0x77f6, KEY_LEFT }, + { 0x77f7, KEY_RIGHT }, + { 0x77f8, KEY_OK }, + + { 0x77f9, KEY_BACK }, + { 0x77fa, KEY_MENU }, + + { 0x77fb, KEY_VOLUMEUP }, + { 0x77fc, KEY_VOLUMEDOWN }, +}; + +static struct rc_map_list wetek_hub_map = { + .map = { + .scan = wetek_hub, + .size = ARRAY_SIZE(wetek_hub), + .rc_proto = RC_PROTO_NEC, + .name = RC_MAP_WETEK_HUB, + } +}; + +static int __init init_rc_map_wetek_hub(void) +{ + return rc_map_register(&wetek_hub_map); +} + +static void __exit exit_rc_map_wetek_hub(void) +{ + rc_map_unregister(&wetek_hub_map); +} + +module_init(init_rc_map_wetek_hub) +module_exit(exit_rc_map_wetek_hub) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Christian Hewitt <christianshewitt@gmail.com>"); diff --git a/drivers/media/rc/keymaps/rc-wetek-play2.c b/drivers/media/rc/keymaps/rc-wetek-play2.c new file mode 100644 index 000000000000..bbbb11fa3c11 --- /dev/null +++ b/drivers/media/rc/keymaps/rc-wetek-play2.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Copyright (C) 2019 Christian Hewitt <christianshewitt@gmail.com> + +#include <media/rc-map.h> +#include <linux/module.h> + +// +// Keytable for the WeTek Play 2 STB remote control +// + +static struct rc_map_table wetek_play2[] = { + { 0x5e5f02, KEY_POWER }, + { 0x5e5f46, KEY_SLEEP }, // tv + { 0x5e5f10, KEY_MUTE }, + + { 0x5e5f22, KEY_1 }, + { 0x5e5f23, KEY_2 }, + { 0x5e5f24, KEY_3 }, + + { 0x5e5f25, KEY_4 }, + { 0x5e5f26, KEY_5 }, + { 0x5e5f27, KEY_6 }, + + { 0x5e5f28, KEY_7 }, + { 0x5e5f29, KEY_8 }, + { 0x5e5f30, KEY_9 }, + + { 0x5e5f71, KEY_BACK }, + { 0x5e5f21, KEY_0 }, + { 0x5e5f72, KEY_CAPSLOCK }, + + // outer ring clockwide from top + { 0x5e5f03, KEY_HOME }, + { 0x5e5f61, KEY_BACK }, + { 0x5e5f77, KEY_CONFIG }, // mouse + { 0x5e5f83, KEY_EPG }, + { 0x5e5f84, KEY_SCREEN }, // square + { 0x5e5f48, KEY_MENU }, + + // inner ring + { 0x5e5f50, KEY_UP }, + { 0x5e5f4b, KEY_DOWN }, + { 0x5e5f4c, KEY_LEFT }, + { 0x5e5f4d, KEY_RIGHT }, + { 0x5e5f47, KEY_OK }, + + { 0x5e5f44, KEY_VOLUMEUP }, + { 0x5e5f43, KEY_VOLUMEDOWN }, + { 0x5e5f4f, KEY_FAVORITES }, + { 0x5e5f82, KEY_SUBTITLE }, // txt + { 0x5e5f41, KEY_PAGEUP }, + { 0x5e5f42, KEY_PAGEDOWN }, + + { 0x5e5f73, KEY_RED }, + { 0x5e5f74, KEY_GREEN }, + { 0x5e5f75, KEY_YELLOW }, + { 0x5e5f76, KEY_BLUE }, + + { 0x5e5f67, KEY_PREVIOUSSONG }, + { 0x5e5f79, KEY_REWIND }, + { 0x5e5f80, KEY_FASTFORWARD }, + { 0x5e5f81, KEY_NEXTSONG }, + + { 0x5e5f04, KEY_RECORD }, + { 0x5e5f2c, KEY_PLAYPAUSE }, + { 0x5e5f2b, KEY_STOP }, +}; + +static struct rc_map_list wetek_play2_map = { + .map = { + .scan = wetek_play2, + .size = ARRAY_SIZE(wetek_play2), + .rc_proto = RC_PROTO_NECX, + .name = RC_MAP_WETEK_PLAY2, + } +}; + +static int __init init_rc_map_wetek_play2(void) +{ + return rc_map_register(&wetek_play2_map); +} + +static void __exit exit_rc_map_wetek_play2(void) +{ + rc_map_unregister(&wetek_play2_map); +} + +module_init(init_rc_map_wetek_play2) +module_exit(exit_rc_map_wetek_play2) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Christian Hewitt <christianshewitt@gmail.com"); diff --git a/drivers/media/rc/keymaps/rc-x96max.c b/drivers/media/rc/keymaps/rc-x96max.c new file mode 100644 index 000000000000..0998ec3320e4 --- /dev/null +++ b/drivers/media/rc/keymaps/rc-x96max.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Copyright (C) 2019 Christian Hewitt <christianshewitt@gmail.com> + +#include <media/rc-map.h> +#include <linux/module.h> + +// +// Keytable for the X96-max STB remote control +// + +static struct rc_map_table x96max[] = { + { 0x140, KEY_POWER }, + + // ** TV CONTROL ** + // SET + // AV/TV + // POWER + // VOLUME UP + // VOLUME DOWN + + { 0x118, KEY_VOLUMEUP }, + { 0x110, KEY_VOLUMEDOWN }, + + { 0x143, KEY_MUTE }, // config + + { 0x100, KEY_EPG }, // mouse + { 0x119, KEY_BACK }, + + { 0x116, KEY_UP }, + { 0x151, KEY_LEFT }, + { 0x150, KEY_RIGHT }, + { 0x11a, KEY_DOWN }, + { 0x113, KEY_OK }, + + { 0x111, KEY_HOME }, + { 0x14c, KEY_CONTEXT_MENU }, + + { 0x159, KEY_PREVIOUS }, + { 0x15a, KEY_PLAYPAUSE }, + { 0x158, KEY_NEXT }, + + { 0x147, KEY_MENU }, // @ key + { 0x101, KEY_NUMERIC_0 }, + { 0x142, KEY_BACKSPACE }, + + { 0x14e, KEY_NUMERIC_1 }, + { 0x10d, KEY_NUMERIC_2 }, + { 0x10c, KEY_NUMERIC_3 }, + + { 0x14a, KEY_NUMERIC_4 }, + { 0x109, KEY_NUMERIC_5 }, + { 0x108, KEY_NUMERIC_6 }, + + { 0x146, KEY_NUMERIC_7 }, + { 0x105, KEY_NUMERIC_8 }, + { 0x104, KEY_NUMERIC_9 }, +}; + +static struct rc_map_list x96max_map = { + .map = { + .scan = x96max, + .size = ARRAY_SIZE(x96max), + .rc_proto = RC_PROTO_NEC, + .name = RC_MAP_X96MAX, + } +}; + +static int __init init_rc_map_x96max(void) +{ + return rc_map_register(&x96max_map); +} + +static void __exit exit_rc_map_x96max(void) +{ + rc_map_unregister(&x96max_map); +} + +module_init(init_rc_map_x96max) +module_exit(exit_rc_map_x96max) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Christian Hewitt <christianshewitt@gmail.com"); diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 4d5351ebb940..3fc9829a9233 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -31,21 +31,22 @@ #include <linux/pm_wakeup.h> #include <media/rc-core.h> -#define DRIVER_VERSION "1.94" +#define DRIVER_VERSION "1.95" #define DRIVER_AUTHOR "Jarod Wilson <jarod@redhat.com>" #define DRIVER_DESC "Windows Media Center Ed. eHome Infrared Transceiver " \ "device driver" #define DRIVER_NAME "mceusb" +#define USB_TX_TIMEOUT 1000 /* in milliseconds */ #define USB_CTRL_MSG_SZ 2 /* Size of usb ctrl msg on gen1 hw */ #define MCE_G1_INIT_MSGS 40 /* Init messages on gen1 hw to throw out */ /* MCE constants */ -#define MCE_CMDBUF_SIZE 384 /* MCE Command buffer length */ +#define MCE_IRBUF_SIZE 128 /* TX IR buffer length */ #define MCE_TIME_UNIT 50 /* Approx 50us resolution */ -#define MCE_CODE_LENGTH 5 /* Normal length of packet (with header) */ -#define MCE_PACKET_SIZE 4 /* Normal length of packet (without header) */ -#define MCE_IRDATA_HEADER 0x84 /* Actual header format is 0x80 + num_bytes */ +#define MCE_PACKET_SIZE 31 /* Max length of packet (with header) */ +#define MCE_IRDATA_HEADER (0x80 + MCE_PACKET_SIZE - 1) + /* Actual format is 0x80 + num_bytes */ #define MCE_IRDATA_TRAILER 0x80 /* End of IR data */ #define MCE_MAX_CHANNELS 2 /* Two transmitters, hardware dependent? */ #define MCE_DEFAULT_TX_MASK 0x03 /* Vals: TX1=0x01, TX2=0x02, ALL=0x03 */ @@ -461,6 +462,7 @@ struct mceusb_dev { /* usb */ struct usb_device *usbdev; + struct usb_interface *usbintf; struct urb *urb_in; unsigned int pipe_in; struct usb_endpoint_descriptor *usb_ep_out; @@ -517,6 +519,7 @@ struct mceusb_dev { unsigned long kevent_flags; # define EVENT_TX_HALT 0 # define EVENT_RX_HALT 1 +# define EVENT_RST_PEND 31 }; /* MCE Device Command Strings, generally a port and command pair */ @@ -607,9 +610,9 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, u8 *buf, int buf_len, if (len <= skip) return; - dev_dbg(dev, "%cx data: %*ph (length=%d)", - (out ? 't' : 'r'), - min(len, buf_len - offset), buf + offset, len); + dev_dbg(dev, "%cx data[%d]: %*ph (len=%d sz=%d)", + (out ? 't' : 'r'), offset, + min(len, buf_len - offset), buf + offset, len, buf_len); inout = out ? "Request" : "Got"; @@ -731,6 +734,9 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, u8 *buf, int buf_len, case MCE_RSP_CMD_ILLEGAL: dev_dbg(dev, "Illegal PORT_IR command"); break; + case MCE_RSP_TX_TIMEOUT: + dev_dbg(dev, "IR TX timeout (TX buffer underrun)"); + break; default: dev_dbg(dev, "Unknown command 0x%02x 0x%02x", cmd, subcmd); @@ -745,42 +751,107 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, u8 *buf, int buf_len, dev_dbg(dev, "End of raw IR data"); else if ((cmd != MCE_CMD_PORT_IR) && ((cmd & MCE_PORT_MASK) == MCE_COMMAND_IRDATA)) - dev_dbg(dev, "Raw IR data, %d pulse/space samples", ir->rem); + dev_dbg(dev, "Raw IR data, %d pulse/space samples", + cmd & MCE_PACKET_LENGTH_MASK); #endif } /* * Schedule work that can't be done in interrupt handlers - * (mceusb_dev_recv() and mce_async_callback()) nor tasklets. + * (mceusb_dev_recv() and mce_write_callback()) nor tasklets. * Invokes mceusb_deferred_kevent() for recovering from * error events specified by the kevent bit field. */ static void mceusb_defer_kevent(struct mceusb_dev *ir, int kevent) { set_bit(kevent, &ir->kevent_flags); + + if (test_bit(EVENT_RST_PEND, &ir->kevent_flags)) { + dev_dbg(ir->dev, "kevent %d dropped pending USB Reset Device", + kevent); + return; + } + if (!schedule_work(&ir->kevent)) - dev_err(ir->dev, "kevent %d may have been dropped", kevent); + dev_dbg(ir->dev, "kevent %d already scheduled", kevent); else dev_dbg(ir->dev, "kevent %d scheduled", kevent); } -static void mce_async_callback(struct urb *urb) +static void mce_write_callback(struct urb *urb) { - struct mceusb_dev *ir; - int len; - if (!urb) return; - ir = urb->context; + complete(urb->context); +} + +/* + * Write (TX/send) data to MCE device USB endpoint out. + * Used for IR blaster TX and MCE device commands. + * + * Return: The number of bytes written (> 0) or errno (< 0). + */ +static int mce_write(struct mceusb_dev *ir, u8 *data, int size) +{ + int ret; + struct urb *urb; + struct device *dev = ir->dev; + unsigned char *buf_out; + struct completion tx_done; + unsigned long expire; + unsigned long ret_wait; + + mceusb_dev_printdata(ir, data, size, 0, size, true); + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (unlikely(!urb)) { + dev_err(dev, "Error: mce write couldn't allocate urb"); + return -ENOMEM; + } + + buf_out = kmalloc(size, GFP_KERNEL); + if (!buf_out) { + usb_free_urb(urb); + return -ENOMEM; + } + + init_completion(&tx_done); + + /* outbound data */ + if (usb_endpoint_xfer_int(ir->usb_ep_out)) + usb_fill_int_urb(urb, ir->usbdev, ir->pipe_out, + buf_out, size, mce_write_callback, &tx_done, + ir->usb_ep_out->bInterval); + else + usb_fill_bulk_urb(urb, ir->usbdev, ir->pipe_out, + buf_out, size, mce_write_callback, &tx_done); + memcpy(buf_out, data, size); + + ret = usb_submit_urb(urb, GFP_KERNEL); + if (ret) { + dev_err(dev, "Error: mce write submit urb error = %d", ret); + kfree(buf_out); + usb_free_urb(urb); + return ret; + } + + expire = msecs_to_jiffies(USB_TX_TIMEOUT); + ret_wait = wait_for_completion_timeout(&tx_done, expire); + if (!ret_wait) { + dev_err(dev, "Error: mce write timed out (expire = %lu (%dms))", + expire, USB_TX_TIMEOUT); + usb_kill_urb(urb); + ret = (urb->status == -ENOENT ? -ETIMEDOUT : urb->status); + } else { + ret = urb->status; + } + if (ret >= 0) + ret = urb->actual_length; /* bytes written */ switch (urb->status) { /* success */ case 0: - len = urb->actual_length; - - mceusb_dev_printdata(ir, urb->transfer_buffer, len, - 0, len, true); break; case -ECONNRESET: @@ -790,140 +861,135 @@ static void mce_async_callback(struct urb *urb) break; case -EPIPE: - dev_err(ir->dev, "Error: request urb status = %d (TX HALT)", + dev_err(ir->dev, "Error: mce write urb status = %d (TX HALT)", urb->status); mceusb_defer_kevent(ir, EVENT_TX_HALT); break; default: - dev_err(ir->dev, "Error: request urb status = %d", urb->status); + dev_err(ir->dev, "Error: mce write urb status = %d", + urb->status); break; } - /* the transfer buffer and urb were allocated in mce_request_packet */ - kfree(urb->transfer_buffer); - usb_free_urb(urb); -} - -/* request outgoing (send) usb packet - used to initialize remote */ -static void mce_request_packet(struct mceusb_dev *ir, unsigned char *data, - int size) -{ - int res; - struct urb *async_urb; - struct device *dev = ir->dev; - unsigned char *async_buf; - - async_urb = usb_alloc_urb(0, GFP_KERNEL); - if (unlikely(!async_urb)) { - dev_err(dev, "Error, couldn't allocate urb!"); - return; - } - - async_buf = kmalloc(size, GFP_KERNEL); - if (!async_buf) { - usb_free_urb(async_urb); - return; - } + dev_dbg(dev, "tx done status = %d (wait = %lu, expire = %lu (%dms), urb->actual_length = %d, urb->status = %d)", + ret, ret_wait, expire, USB_TX_TIMEOUT, + urb->actual_length, urb->status); - /* outbound data */ - if (usb_endpoint_xfer_int(ir->usb_ep_out)) - usb_fill_int_urb(async_urb, ir->usbdev, ir->pipe_out, - async_buf, size, mce_async_callback, ir, - ir->usb_ep_out->bInterval); - else - usb_fill_bulk_urb(async_urb, ir->usbdev, ir->pipe_out, - async_buf, size, mce_async_callback, ir); - - memcpy(async_buf, data, size); - - dev_dbg(dev, "send request called (size=%#x)", size); + kfree(buf_out); + usb_free_urb(urb); - res = usb_submit_urb(async_urb, GFP_ATOMIC); - if (res) { - dev_err(dev, "send request FAILED! (res=%d)", res); - kfree(async_buf); - usb_free_urb(async_urb); - return; - } - dev_dbg(dev, "send request complete (res=%d)", res); + return ret; } -static void mce_async_out(struct mceusb_dev *ir, unsigned char *data, int size) +static void mce_command_out(struct mceusb_dev *ir, u8 *data, int size) { int rsize = sizeof(DEVICE_RESUME); if (ir->need_reset) { ir->need_reset = false; - mce_request_packet(ir, DEVICE_RESUME, rsize); + mce_write(ir, DEVICE_RESUME, rsize); msleep(10); } - mce_request_packet(ir, data, size); + mce_write(ir, data, size); msleep(10); } -/* Send data out the IR blaster port(s) */ +/* + * Transmit IR out the MCE device IR blaster port(s). + * + * Convert IR pulse/space sequence from LIRC to MCE format. + * Break up a long IR sequence into multiple parts (MCE IR data packets). + * + * u32 txbuf[] consists of IR pulse, space, ..., and pulse times in usec. + * Pulses and spaces are implicit by their position. + * The first IR sample, txbuf[0], is always a pulse. + * + * u8 irbuf[] consists of multiple IR data packets for the MCE device. + * A packet is 1 u8 MCE_IRDATA_HEADER and up to 30 u8 IR samples. + * An IR sample is 1-bit pulse/space flag with 7-bit time + * in MCE time units (50usec). + * + * Return: The number of IR samples sent (> 0) or errno (< 0). + */ static int mceusb_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned count) { struct mceusb_dev *ir = dev->priv; - int i, length, ret = 0; - int cmdcount = 0; - unsigned char cmdbuf[MCE_CMDBUF_SIZE]; - - /* MCE tx init header */ - cmdbuf[cmdcount++] = MCE_CMD_PORT_IR; - cmdbuf[cmdcount++] = MCE_CMD_SETIRTXPORTS; - cmdbuf[cmdcount++] = ir->tx_mask; + u8 cmdbuf[3] = { MCE_CMD_PORT_IR, MCE_CMD_SETIRTXPORTS, 0x00 }; + u8 irbuf[MCE_IRBUF_SIZE]; + int ircount = 0; + unsigned int irsample; + int i, length, ret; /* Send the set TX ports command */ - mce_async_out(ir, cmdbuf, cmdcount); - cmdcount = 0; - - /* Generate mce packet data */ - for (i = 0; (i < count) && (cmdcount < MCE_CMDBUF_SIZE); i++) { - txbuf[i] = txbuf[i] / MCE_TIME_UNIT; - - do { /* loop to support long pulses/spaces > 127*50us=6.35ms */ - - /* Insert mce packet header every 4th entry */ - if ((cmdcount < MCE_CMDBUF_SIZE) && - (cmdcount % MCE_CODE_LENGTH) == 0) - cmdbuf[cmdcount++] = MCE_IRDATA_HEADER; - - /* Insert mce packet data */ - if (cmdcount < MCE_CMDBUF_SIZE) - cmdbuf[cmdcount++] = - (txbuf[i] < MCE_PULSE_BIT ? - txbuf[i] : MCE_MAX_PULSE_LENGTH) | - (i & 1 ? 0x00 : MCE_PULSE_BIT); - else { - ret = -EINVAL; - goto out; + cmdbuf[2] = ir->tx_mask; + mce_command_out(ir, cmdbuf, sizeof(cmdbuf)); + + /* Generate mce IR data packet */ + for (i = 0; i < count; i++) { + irsample = txbuf[i] / MCE_TIME_UNIT; + + /* loop to support long pulses/spaces > 6350us (127*50us) */ + while (irsample > 0) { + /* Insert IR header every 30th entry */ + if (ircount % MCE_PACKET_SIZE == 0) { + /* Room for IR header and one IR sample? */ + if (ircount >= MCE_IRBUF_SIZE - 1) { + /* Send near full buffer */ + ret = mce_write(ir, irbuf, ircount); + if (ret < 0) + return ret; + ircount = 0; + } + irbuf[ircount++] = MCE_IRDATA_HEADER; } - } while ((txbuf[i] > MCE_MAX_PULSE_LENGTH) && - (txbuf[i] -= MCE_MAX_PULSE_LENGTH)); - } - - /* Check if we have room for the empty packet at the end */ - if (cmdcount >= MCE_CMDBUF_SIZE) { - ret = -EINVAL; - goto out; - } + /* Insert IR sample */ + if (irsample <= MCE_MAX_PULSE_LENGTH) { + irbuf[ircount] = irsample; + irsample = 0; + } else { + irbuf[ircount] = MCE_MAX_PULSE_LENGTH; + irsample -= MCE_MAX_PULSE_LENGTH; + } + /* + * Even i = IR pulse + * Odd i = IR space + */ + irbuf[ircount] |= (i & 1 ? 0 : MCE_PULSE_BIT); + ircount++; + + /* IR buffer full? */ + if (ircount >= MCE_IRBUF_SIZE) { + /* Fix packet length in last header */ + length = ircount % MCE_PACKET_SIZE; + if (length > 0) + irbuf[ircount - length] -= + MCE_PACKET_SIZE - length; + /* Send full buffer */ + ret = mce_write(ir, irbuf, ircount); + if (ret < 0) + return ret; + ircount = 0; + } + } + } /* after for loop, 0 <= ircount < MCE_IRBUF_SIZE */ /* Fix packet length in last header */ - length = cmdcount % MCE_CODE_LENGTH; - cmdbuf[cmdcount - length] -= MCE_CODE_LENGTH - length; + length = ircount % MCE_PACKET_SIZE; + if (length > 0) + irbuf[ircount - length] -= MCE_PACKET_SIZE - length; - /* All mce commands end with an empty packet (0x80) */ - cmdbuf[cmdcount++] = MCE_IRDATA_TRAILER; + /* Append IR trailer (0x80) to final partial (or empty) IR buffer */ + irbuf[ircount++] = MCE_IRDATA_TRAILER; - /* Transmit the command to the mce device */ - mce_async_out(ir, cmdbuf, cmdcount); + /* Send final buffer */ + ret = mce_write(ir, irbuf, ircount); + if (ret < 0) + return ret; -out: - return ret ? ret : count; + return count; } /* Sets active IR outputs -- mce devices typically have two */ @@ -963,7 +1029,7 @@ static int mceusb_set_tx_carrier(struct rc_dev *dev, u32 carrier) cmdbuf[2] = MCE_CMD_SIG_END; cmdbuf[3] = MCE_IRDATA_TRAILER; dev_dbg(ir->dev, "disabling carrier modulation"); - mce_async_out(ir, cmdbuf, sizeof(cmdbuf)); + mce_command_out(ir, cmdbuf, sizeof(cmdbuf)); return 0; } @@ -977,7 +1043,7 @@ static int mceusb_set_tx_carrier(struct rc_dev *dev, u32 carrier) carrier); /* Transmit new carrier to mce device */ - mce_async_out(ir, cmdbuf, sizeof(cmdbuf)); + mce_command_out(ir, cmdbuf, sizeof(cmdbuf)); return 0; } } @@ -1000,10 +1066,10 @@ static int mceusb_set_timeout(struct rc_dev *dev, unsigned int timeout) cmdbuf[2] = units >> 8; cmdbuf[3] = units; - mce_async_out(ir, cmdbuf, sizeof(cmdbuf)); + mce_command_out(ir, cmdbuf, sizeof(cmdbuf)); /* get receiver timeout value */ - mce_async_out(ir, GET_RX_TIMEOUT, sizeof(GET_RX_TIMEOUT)); + mce_command_out(ir, GET_RX_TIMEOUT, sizeof(GET_RX_TIMEOUT)); return 0; } @@ -1028,7 +1094,7 @@ static int mceusb_set_rx_wideband(struct rc_dev *dev, int enable) ir->wideband_rx_enabled = false; cmdbuf[2] = 1; /* port 1 is long range receiver */ } - mce_async_out(ir, cmdbuf, sizeof(cmdbuf)); + mce_command_out(ir, cmdbuf, sizeof(cmdbuf)); /* response from device sets ir->learning_active */ return 0; @@ -1051,7 +1117,7 @@ static int mceusb_set_rx_carrier_report(struct rc_dev *dev, int enable) ir->carrier_report_enabled = true; if (!ir->learning_active) { cmdbuf[2] = 2; /* port 2 is short range receiver */ - mce_async_out(ir, cmdbuf, sizeof(cmdbuf)); + mce_command_out(ir, cmdbuf, sizeof(cmdbuf)); } } else { ir->carrier_report_enabled = false; @@ -1062,7 +1128,7 @@ static int mceusb_set_rx_carrier_report(struct rc_dev *dev, int enable) */ if (ir->learning_active && !ir->wideband_rx_enabled) { cmdbuf[2] = 1; /* port 1 is long range receiver */ - mce_async_out(ir, cmdbuf, sizeof(cmdbuf)); + mce_command_out(ir, cmdbuf, sizeof(cmdbuf)); } } @@ -1141,6 +1207,7 @@ static void mceusb_handle_command(struct mceusb_dev *ir, int index) } break; case MCE_RSP_CMD_ILLEGAL: + case MCE_RSP_TX_TIMEOUT: ir->need_reset = true; break; default: @@ -1279,7 +1346,7 @@ static void mceusb_get_emulator_version(struct mceusb_dev *ir) { /* If we get no reply or an illegal command reply, its ver 1, says MS */ ir->emver = 1; - mce_async_out(ir, GET_EMVER, sizeof(GET_EMVER)); + mce_command_out(ir, GET_EMVER, sizeof(GET_EMVER)); } static void mceusb_gen1_init(struct mceusb_dev *ir) @@ -1325,10 +1392,10 @@ static void mceusb_gen1_init(struct mceusb_dev *ir) dev_dbg(dev, "set handshake - retC = %d", ret); /* device resume */ - mce_async_out(ir, DEVICE_RESUME, sizeof(DEVICE_RESUME)); + mce_command_out(ir, DEVICE_RESUME, sizeof(DEVICE_RESUME)); /* get hw/sw revision? */ - mce_async_out(ir, GET_REVISION, sizeof(GET_REVISION)); + mce_command_out(ir, GET_REVISION, sizeof(GET_REVISION)); kfree(data); } @@ -1336,13 +1403,13 @@ static void mceusb_gen1_init(struct mceusb_dev *ir) static void mceusb_gen2_init(struct mceusb_dev *ir) { /* device resume */ - mce_async_out(ir, DEVICE_RESUME, sizeof(DEVICE_RESUME)); + mce_command_out(ir, DEVICE_RESUME, sizeof(DEVICE_RESUME)); /* get wake version (protocol, key, address) */ - mce_async_out(ir, GET_WAKEVERSION, sizeof(GET_WAKEVERSION)); + mce_command_out(ir, GET_WAKEVERSION, sizeof(GET_WAKEVERSION)); /* unknown what this one actually returns... */ - mce_async_out(ir, GET_UNKNOWN2, sizeof(GET_UNKNOWN2)); + mce_command_out(ir, GET_UNKNOWN2, sizeof(GET_UNKNOWN2)); } static void mceusb_get_parameters(struct mceusb_dev *ir) @@ -1356,24 +1423,24 @@ static void mceusb_get_parameters(struct mceusb_dev *ir) ir->num_rxports = 2; /* get number of tx and rx ports */ - mce_async_out(ir, GET_NUM_PORTS, sizeof(GET_NUM_PORTS)); + mce_command_out(ir, GET_NUM_PORTS, sizeof(GET_NUM_PORTS)); /* get the carrier and frequency */ - mce_async_out(ir, GET_CARRIER_FREQ, sizeof(GET_CARRIER_FREQ)); + mce_command_out(ir, GET_CARRIER_FREQ, sizeof(GET_CARRIER_FREQ)); if (ir->num_txports && !ir->flags.no_tx) /* get the transmitter bitmask */ - mce_async_out(ir, GET_TX_BITMASK, sizeof(GET_TX_BITMASK)); + mce_command_out(ir, GET_TX_BITMASK, sizeof(GET_TX_BITMASK)); /* get receiver timeout value */ - mce_async_out(ir, GET_RX_TIMEOUT, sizeof(GET_RX_TIMEOUT)); + mce_command_out(ir, GET_RX_TIMEOUT, sizeof(GET_RX_TIMEOUT)); /* get receiver sensor setting */ - mce_async_out(ir, GET_RX_SENSOR, sizeof(GET_RX_SENSOR)); + mce_command_out(ir, GET_RX_SENSOR, sizeof(GET_RX_SENSOR)); for (i = 0; i < ir->num_txports; i++) { cmdbuf[2] = i; - mce_async_out(ir, cmdbuf, sizeof(cmdbuf)); + mce_command_out(ir, cmdbuf, sizeof(cmdbuf)); } } @@ -1382,7 +1449,7 @@ static void mceusb_flash_led(struct mceusb_dev *ir) if (ir->emver < 2) return; - mce_async_out(ir, FLASH_LED, sizeof(FLASH_LED)); + mce_command_out(ir, FLASH_LED, sizeof(FLASH_LED)); } /* @@ -1398,28 +1465,59 @@ static void mceusb_deferred_kevent(struct work_struct *work) container_of(work, struct mceusb_dev, kevent); int status; + dev_err(ir->dev, "kevent handler called (flags 0x%lx)", + ir->kevent_flags); + + if (test_bit(EVENT_RST_PEND, &ir->kevent_flags)) { + dev_err(ir->dev, "kevent handler canceled pending USB Reset Device"); + return; + } + if (test_bit(EVENT_RX_HALT, &ir->kevent_flags)) { usb_unlink_urb(ir->urb_in); status = usb_clear_halt(ir->usbdev, ir->pipe_in); + dev_err(ir->dev, "rx clear halt status = %d", status); if (status < 0) { - dev_err(ir->dev, "rx clear halt error %d", - status); + /* + * Unable to clear RX halt/stall. + * Will need to call usb_reset_device(). + */ + dev_err(ir->dev, + "stuck RX HALT state requires USB Reset Device to clear"); + usb_queue_reset_device(ir->usbintf); + set_bit(EVENT_RST_PEND, &ir->kevent_flags); + clear_bit(EVENT_RX_HALT, &ir->kevent_flags); + + /* Cancel all other error events and handlers */ + clear_bit(EVENT_TX_HALT, &ir->kevent_flags); + return; } clear_bit(EVENT_RX_HALT, &ir->kevent_flags); - if (status == 0) { - status = usb_submit_urb(ir->urb_in, GFP_KERNEL); - if (status < 0) { - dev_err(ir->dev, - "rx unhalt submit urb error %d", - status); - } + status = usb_submit_urb(ir->urb_in, GFP_KERNEL); + if (status < 0) { + dev_err(ir->dev, "rx unhalt submit urb error = %d", + status); } } if (test_bit(EVENT_TX_HALT, &ir->kevent_flags)) { status = usb_clear_halt(ir->usbdev, ir->pipe_out); - if (status < 0) - dev_err(ir->dev, "tx clear halt error %d", status); + dev_err(ir->dev, "tx clear halt status = %d", status); + if (status < 0) { + /* + * Unable to clear TX halt/stall. + * Will need to call usb_reset_device(). + */ + dev_err(ir->dev, + "stuck TX HALT state requires USB Reset Device to clear"); + usb_queue_reset_device(ir->usbintf); + set_bit(EVENT_RST_PEND, &ir->kevent_flags); + clear_bit(EVENT_TX_HALT, &ir->kevent_flags); + + /* Cancel all other error events and handlers */ + clear_bit(EVENT_RX_HALT, &ir->kevent_flags); + return; + } clear_bit(EVENT_TX_HALT, &ir->kevent_flags); } } @@ -1581,6 +1679,7 @@ static int mceusb_dev_probe(struct usb_interface *intf, if (!ir->urb_in) goto urb_in_alloc_fail; + ir->usbintf = intf; ir->usbdev = usb_get_dev(dev); ir->dev = &intf->dev; ir->len_in = maxp; @@ -1688,6 +1787,8 @@ static void mceusb_dev_disconnect(struct usb_interface *intf) struct usb_device *dev = interface_to_usbdev(intf); struct mceusb_dev *ir = usb_get_intfdata(intf); + dev_dbg(&intf->dev, "%s called", __func__); + usb_set_intfdata(intf, NULL); if (!ir) diff --git a/drivers/media/rc/meson-ir.c b/drivers/media/rc/meson-ir.c index 72a7bbbf6b1f..51c6dd3406a0 100644 --- a/drivers/media/rc/meson-ir.c +++ b/drivers/media/rc/meson-ir.c @@ -117,10 +117,8 @@ static int meson_ir_probe(struct platform_device *pdev) return PTR_ERR(ir->reg); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "no irq resource\n"); + if (irq < 0) return irq; - } ir->rc = devm_rc_allocate_device(dev, RC_DRIVER_IR_RAW); if (!ir->rc) { diff --git a/drivers/media/rc/mtk-cir.c b/drivers/media/rc/mtk-cir.c index 50fb0aebb8d4..a0c94ab322c7 100644 --- a/drivers/media/rc/mtk-cir.c +++ b/drivers/media/rc/mtk-cir.c @@ -35,6 +35,11 @@ /* Fields containing pulse width data */ #define MTK_WIDTH_MASK (GENMASK(7, 0)) +/* IR threshold */ +#define MTK_IRTHD 0x14 +#define MTK_DG_CNT_MASK (GENMASK(12, 8)) +#define MTK_DG_CNT(x) ((x) << 8) + /* Bit to enable interrupt */ #define MTK_IRINT_EN BIT(0) @@ -340,7 +345,7 @@ static int mtk_ir_probe(struct platform_device *pdev) ir->rc->map_name = map_name ?: RC_MAP_EMPTY; ir->rc->dev.parent = dev; ir->rc->driver_name = MTK_IR_DEV; - ir->rc->allowed_protocols = RC_PROTO_BIT_ALL; + ir->rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; ir->rc->rx_resolution = MTK_IR_SAMPLE; ir->rc->timeout = MTK_MAX_SAMPLES * (MTK_IR_SAMPLE + 1); @@ -353,10 +358,8 @@ static int mtk_ir_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ir); ir->irq = platform_get_irq(pdev, 0); - if (ir->irq < 0) { - dev_err(dev, "no irq resource\n"); + if (ir->irq < 0) return -ENODEV; - } if (clk_prepare_enable(ir->clk)) { dev_err(dev, "try to enable ir_clk failed\n"); @@ -398,6 +401,9 @@ static int mtk_ir_probe(struct platform_device *pdev) mtk_w32_mask(ir, val, ir->data->fields[MTK_HW_PERIOD].mask, ir->data->fields[MTK_HW_PERIOD].reg); + /* Set de-glitch counter */ + mtk_w32_mask(ir, MTK_DG_CNT(1), MTK_DG_CNT_MASK, MTK_IRTHD); + /* Enable IR and PWM */ val = mtk_r32(ir, MTK_CONFIG_HIGH_REG); val |= MTK_OK_COUNT(ir->data->ok_count) | MTK_PWM_EN | MTK_IR_EN; diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c index aa719d0ae6b0..e222b4c98be4 100644 --- a/drivers/media/rc/sunxi-cir.c +++ b/drivers/media/rc/sunxi-cir.c @@ -39,11 +39,11 @@ /* Rx Interrupt Enable */ #define SUNXI_IR_RXINT_REG 0x2C -/* Rx FIFO Overflow */ +/* Rx FIFO Overflow Interrupt Enable */ #define REG_RXINT_ROI_EN BIT(0) -/* Rx Packet End */ +/* Rx Packet End Interrupt Enable */ #define REG_RXINT_RPEI_EN BIT(1) -/* Rx FIFO Data Available */ +/* Rx FIFO Data Available Interrupt Enable */ #define REG_RXINT_RAI_EN BIT(4) /* Rx FIFO available byte level */ @@ -51,6 +51,12 @@ /* Rx Interrupt Status */ #define SUNXI_IR_RXSTA_REG 0x30 +/* Rx FIFO Overflow */ +#define REG_RXSTA_ROI REG_RXINT_ROI_EN +/* Rx Packet End */ +#define REG_RXSTA_RPE REG_RXINT_RPEI_EN +/* Rx FIFO Data Available */ +#define REG_RXSTA_RA REG_RXINT_RAI_EN /* RX FIFO Get Available Counter */ #define REG_RXSTA_GET_AC(val) (((val) >> 8) & (ir->fifo_size * 2 - 1)) /* Clear all interrupt status value */ @@ -72,6 +78,17 @@ /* Time after which device stops sending data in ms */ #define SUNXI_IR_TIMEOUT 120 +/** + * struct sunxi_ir_quirks - Differences between SoC variants. + * + * @has_reset: SoC needs reset deasserted. + * @fifo_size: size of the fifo. + */ +struct sunxi_ir_quirks { + bool has_reset; + int fifo_size; +}; + struct sunxi_ir { spinlock_t ir_lock; struct rc_dev *rc; @@ -99,7 +116,7 @@ static irqreturn_t sunxi_ir_irq(int irqno, void *dev_id) /* clean all pending statuses */ writel(status | REG_RXSTA_CLEARALL, ir->base + SUNXI_IR_RXSTA_REG); - if (status & (REG_RXINT_RAI_EN | REG_RXINT_RPEI_EN)) { + if (status & (REG_RXSTA_RA | REG_RXSTA_RPE)) { /* How many messages in fifo */ rc = REG_RXSTA_GET_AC(status); /* Sanity check */ @@ -115,9 +132,9 @@ static irqreturn_t sunxi_ir_irq(int irqno, void *dev_id) } } - if (status & REG_RXINT_ROI_EN) { + if (status & REG_RXSTA_ROI) { ir_raw_event_reset(ir->rc); - } else if (status & REG_RXINT_RPEI_EN) { + } else if (status & REG_RXSTA_RPE) { ir_raw_event_set_idle(ir->rc, true); ir_raw_event_handle(ir->rc); } @@ -134,6 +151,7 @@ static int sunxi_ir_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *dn = dev->of_node; + const struct sunxi_ir_quirks *quirks; struct resource *res; struct sunxi_ir *ir; u32 b_clk_freq = SUNXI_IR_BASE_CLK; @@ -142,12 +160,15 @@ static int sunxi_ir_probe(struct platform_device *pdev) if (!ir) return -ENOMEM; + quirks = of_device_get_match_data(&pdev->dev); + if (!quirks) { + dev_err(&pdev->dev, "Failed to determine the quirks to use\n"); + return -ENODEV; + } + spin_lock_init(&ir->ir_lock); - if (of_device_is_compatible(dn, "allwinner,sun5i-a13-ir")) - ir->fifo_size = 64; - else - ir->fifo_size = 16; + ir->fifo_size = quirks->fifo_size; /* Clock */ ir->apb_clk = devm_clk_get(dev, "apb"); @@ -164,13 +185,15 @@ static int sunxi_ir_probe(struct platform_device *pdev) /* Base clock frequency (optional) */ of_property_read_u32(dn, "clock-frequency", &b_clk_freq); - /* Reset (optional) */ - ir->rst = devm_reset_control_get_optional_exclusive(dev, NULL); - if (IS_ERR(ir->rst)) - return PTR_ERR(ir->rst); - ret = reset_control_deassert(ir->rst); - if (ret) - return ret; + /* Reset */ + if (quirks->has_reset) { + ir->rst = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(ir->rst)) + return PTR_ERR(ir->rst); + ret = reset_control_deassert(ir->rst); + if (ret) + return ret; + } ret = clk_set_rate(ir->clk, b_clk_freq); if (ret) { @@ -233,7 +256,6 @@ static int sunxi_ir_probe(struct platform_device *pdev) /* IRQ */ ir->irq = platform_get_irq(pdev, 0); if (ir->irq < 0) { - dev_err(dev, "no irq resource\n"); ret = ir->irq; goto exit_free_dev; } @@ -306,10 +328,35 @@ static int sunxi_ir_remove(struct platform_device *pdev) return 0; } +static const struct sunxi_ir_quirks sun4i_a10_ir_quirks = { + .has_reset = false, + .fifo_size = 16, +}; + +static const struct sunxi_ir_quirks sun5i_a13_ir_quirks = { + .has_reset = false, + .fifo_size = 64, +}; + +static const struct sunxi_ir_quirks sun6i_a31_ir_quirks = { + .has_reset = true, + .fifo_size = 64, +}; + static const struct of_device_id sunxi_ir_match[] = { - { .compatible = "allwinner,sun4i-a10-ir", }, - { .compatible = "allwinner,sun5i-a13-ir", }, - {}, + { + .compatible = "allwinner,sun4i-a10-ir", + .data = &sun4i_a10_ir_quirks, + }, + { + .compatible = "allwinner,sun5i-a13-ir", + .data = &sun5i_a13_ir_quirks, + }, + { + .compatible = "allwinner,sun6i-a31-ir", + .data = &sun6i_a31_ir_quirks, + }, + {} }; MODULE_DEVICE_TABLE(of, sunxi_ir_match); |