aboutsummaryrefslogtreecommitdiff
path: root/drivers/media/rc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/rc')
-rw-r--r--drivers/media/rc/Kconfig213
-rw-r--r--drivers/media/rc/Makefile13
-rw-r--r--drivers/media/rc/ati_remote.c1007
-rw-r--r--drivers/media/rc/ene_ir.c167
-rw-r--r--drivers/media/rc/ene_ir.h23
-rw-r--r--drivers/media/rc/fintek-cir.c709
-rw-r--r--drivers/media/rc/fintek-cir.h245
-rw-r--r--drivers/media/rc/gpio-ir-recv.c253
-rw-r--r--drivers/media/rc/iguanair.c618
-rw-r--r--drivers/media/rc/img-ir/Kconfig61
-rw-r--r--drivers/media/rc/img-ir/Makefile11
-rw-r--r--drivers/media/rc/img-ir/img-ir-core.c176
-rw-r--r--drivers/media/rc/img-ir/img-ir-hw.c1066
-rw-r--r--drivers/media/rc/img-ir/img-ir-hw.h269
-rw-r--r--drivers/media/rc/img-ir/img-ir-jvc.c81
-rw-r--r--drivers/media/rc/img-ir/img-ir-nec.c151
-rw-r--r--drivers/media/rc/img-ir/img-ir-raw.c151
-rw-r--r--drivers/media/rc/img-ir/img-ir-raw.h60
-rw-r--r--drivers/media/rc/img-ir/img-ir-sanyo.c122
-rw-r--r--drivers/media/rc/img-ir/img-ir-sharp.c99
-rw-r--r--drivers/media/rc/img-ir/img-ir-sony.c145
-rw-r--r--drivers/media/rc/img-ir/img-ir.h166
-rw-r--r--drivers/media/rc/imon.c279
-rw-r--r--drivers/media/rc/ir-jvc-decoder.c5
-rw-r--r--drivers/media/rc/ir-lirc-codec.c72
-rw-r--r--drivers/media/rc/ir-mce_kbd-decoder.c449
-rw-r--r--drivers/media/rc/ir-nec-decoder.c27
-rw-r--r--drivers/media/rc/ir-raw.c49
-rw-r--r--drivers/media/rc/ir-rc5-decoder.c19
-rw-r--r--drivers/media/rc/ir-rc5-sz-decoder.c9
-rw-r--r--drivers/media/rc/ir-rc6-decoder.c76
-rw-r--r--drivers/media/rc/ir-rx51.c485
-rw-r--r--drivers/media/rc/ir-sanyo-decoder.c205
-rw-r--r--drivers/media/rc/ir-sharp-decoder.c200
-rw-r--r--drivers/media/rc/ir-sony-decoder.c20
-rw-r--r--drivers/media/rc/ite-cir.c1729
-rw-r--r--drivers/media/rc/ite-cir.h484
-rw-r--r--drivers/media/rc/keymaps/Makefile22
-rw-r--r--drivers/media/rc/keymaps/rc-adstech-dvb-t-pci.c11
-rw-r--r--drivers/media/rc/keymaps/rc-alink-dtu-m.c1
-rw-r--r--drivers/media/rc/keymaps/rc-anysee.c1
-rw-r--r--drivers/media/rc/keymaps/rc-apac-viewcomp.c5
-rw-r--r--drivers/media/rc/keymaps/rc-asus-pc39.c5
-rw-r--r--drivers/media/rc/keymaps/rc-asus-ps3-100.c91
-rw-r--r--drivers/media/rc/keymaps/rc-ati-tv-wonder-hd-600.c5
-rw-r--r--drivers/media/rc/keymaps/rc-ati-x10.c104
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia-a16d.c5
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia-cardbus.c7
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia-dvbt.c9
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia-m135a.c7
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia-m733a-rm-k6.c5
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia-rm-ks.c3
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia.c5
-rw-r--r--drivers/media/rc/keymaps/rc-avertv-303.c5
-rw-r--r--drivers/media/rc/keymaps/rc-azurewave-ad-tu700.c1
-rw-r--r--drivers/media/rc/keymaps/rc-behold-columbus.c7
-rw-r--r--drivers/media/rc/keymaps/rc-behold.c7
-rw-r--r--drivers/media/rc/keymaps/rc-budget-ci-old.c8
-rw-r--r--drivers/media/rc/keymaps/rc-cinergy-1400.c5
-rw-r--r--drivers/media/rc/keymaps/rc-cinergy.c7
-rw-r--r--drivers/media/rc/keymaps/rc-delock-61959.c83
-rw-r--r--drivers/media/rc/keymaps/rc-dib0700-nec.c7
-rw-r--r--drivers/media/rc/keymaps/rc-dib0700-rc5.c7
-rw-r--r--drivers/media/rc/keymaps/rc-digitalnow-tinytwin.c1
-rw-r--r--drivers/media/rc/keymaps/rc-digittrade.c1
-rw-r--r--drivers/media/rc/keymaps/rc-dm1105-nec.c5
-rw-r--r--drivers/media/rc/keymaps/rc-dntv-live-dvb-t.c7
-rw-r--r--drivers/media/rc/keymaps/rc-dntv-live-dvbt-pro.c5
-rw-r--r--drivers/media/rc/keymaps/rc-em-terratec.c5
-rw-r--r--drivers/media/rc/keymaps/rc-encore-enltv-fm53.c5
-rw-r--r--drivers/media/rc/keymaps/rc-encore-enltv.c9
-rw-r--r--drivers/media/rc/keymaps/rc-encore-enltv2.c7
-rw-r--r--drivers/media/rc/keymaps/rc-evga-indtube.c5
-rw-r--r--drivers/media/rc/keymaps/rc-eztv.c5
-rw-r--r--drivers/media/rc/keymaps/rc-flydvb.c9
-rw-r--r--drivers/media/rc/keymaps/rc-flyvideo.c5
-rw-r--r--drivers/media/rc/keymaps/rc-fusionhdtv-mce.c5
-rw-r--r--drivers/media/rc/keymaps/rc-gadmei-rm008z.c5
-rw-r--r--drivers/media/rc/keymaps/rc-genius-tvgo-a11mce.c5
-rw-r--r--drivers/media/rc/keymaps/rc-gotview7135.c5
-rw-r--r--drivers/media/rc/keymaps/rc-hauppauge-new.c100
-rw-r--r--drivers/media/rc/keymaps/rc-hauppauge.c293
-rw-r--r--drivers/media/rc/keymaps/rc-imon-mce.c5
-rw-r--r--drivers/media/rc/keymaps/rc-imon-pad.c7
-rw-r--r--drivers/media/rc/keymaps/rc-iodata-bctv7e.c5
-rw-r--r--drivers/media/rc/keymaps/rc-it913x-v1.c95
-rw-r--r--drivers/media/rc/keymaps/rc-it913x-v2.c94
-rw-r--r--drivers/media/rc/keymaps/rc-kaiomy.c5
-rw-r--r--drivers/media/rc/keymaps/rc-kworld-315u.c7
-rw-r--r--drivers/media/rc/keymaps/rc-kworld-pc150u.c102
-rw-r--r--drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.c7
-rw-r--r--drivers/media/rc/keymaps/rc-leadtek-y04g0051.c1
-rw-r--r--drivers/media/rc/keymaps/rc-lirc.c1
-rw-r--r--drivers/media/rc/keymaps/rc-lme2510.c99
-rw-r--r--drivers/media/rc/keymaps/rc-manli.c5
-rw-r--r--drivers/media/rc/keymaps/rc-medion-x10-digitainer.c123
-rw-r--r--drivers/media/rc/keymaps/rc-medion-x10-or2x.c108
-rw-r--r--drivers/media/rc/keymaps/rc-medion-x10.c117
-rw-r--r--drivers/media/rc/keymaps/rc-msi-digivox-ii.c37
-rw-r--r--drivers/media/rc/keymaps/rc-msi-digivox-iii.c1
-rw-r--r--drivers/media/rc/keymaps/rc-msi-tvanywhere-plus.c7
-rw-r--r--drivers/media/rc/keymaps/rc-msi-tvanywhere.c7
-rw-r--r--drivers/media/rc/keymaps/rc-nebula.c7
-rw-r--r--drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c59
-rw-r--r--drivers/media/rc/keymaps/rc-norwood.c9
-rw-r--r--drivers/media/rc/keymaps/rc-npgtech.c5
-rw-r--r--drivers/media/rc/keymaps/rc-pctv-sedna.c7
-rw-r--r--drivers/media/rc/keymaps/rc-pinnacle-color.c5
-rw-r--r--drivers/media/rc/keymaps/rc-pinnacle-grey.c5
-rw-r--r--drivers/media/rc/keymaps/rc-pinnacle-pctv-hd.c64
-rw-r--r--drivers/media/rc/keymaps/rc-pixelview-002t.c5
-rw-r--r--drivers/media/rc/keymaps/rc-pixelview-mk12.c7
-rw-r--r--drivers/media/rc/keymaps/rc-pixelview-new.c7
-rw-r--r--drivers/media/rc/keymaps/rc-pixelview.c7
-rw-r--r--drivers/media/rc/keymaps/rc-powercolor-real-angel.c5
-rw-r--r--drivers/media/rc/keymaps/rc-proteus-2309.c5
-rw-r--r--drivers/media/rc/keymaps/rc-purpletv.c5
-rw-r--r--drivers/media/rc/keymaps/rc-pv951.c9
-rw-r--r--drivers/media/rc/keymaps/rc-rc5-hauppauge-new.c141
-rw-r--r--drivers/media/rc/keymaps/rc-rc5-tv.c81
-rw-r--r--drivers/media/rc/keymaps/rc-rc6-mce.c10
-rw-r--r--drivers/media/rc/keymaps/rc-real-audio-220-32-keys.c7
-rw-r--r--drivers/media/rc/keymaps/rc-reddo.c86
-rw-r--r--drivers/media/rc/keymaps/rc-snapstream-firefly.c107
-rw-r--r--drivers/media/rc/keymaps/rc-streamzap.c1
-rw-r--r--drivers/media/rc/keymaps/rc-su3000.c75
-rw-r--r--drivers/media/rc/keymaps/rc-tbs-nec.c5
-rw-r--r--drivers/media/rc/keymaps/rc-technisat-usb2.c94
-rw-r--r--drivers/media/rc/keymaps/rc-terratec-cinergy-xs.c5
-rw-r--r--drivers/media/rc/keymaps/rc-terratec-slim-2.c73
-rw-r--r--drivers/media/rc/keymaps/rc-terratec-slim.c1
-rw-r--r--drivers/media/rc/keymaps/rc-tevii-nec.c5
-rw-r--r--drivers/media/rc/keymaps/rc-tivo.c99
-rw-r--r--drivers/media/rc/keymaps/rc-total-media-in-hand-02.c86
-rw-r--r--drivers/media/rc/keymaps/rc-total-media-in-hand.c1
-rw-r--r--drivers/media/rc/keymaps/rc-trekstor.c1
-rw-r--r--drivers/media/rc/keymaps/rc-tt-1500.c7
-rw-r--r--drivers/media/rc/keymaps/rc-twinhan1027.c1
-rw-r--r--drivers/media/rc/keymaps/rc-videomate-m1f.c25
-rw-r--r--drivers/media/rc/keymaps/rc-videomate-s350.c5
-rw-r--r--drivers/media/rc/keymaps/rc-videomate-tv-pvr.c5
-rw-r--r--drivers/media/rc/keymaps/rc-winfast-usbii-deluxe.c5
-rw-r--r--drivers/media/rc/keymaps/rc-winfast.c27
-rw-r--r--drivers/media/rc/lirc_dev.c70
-rw-r--r--drivers/media/rc/mceusb.c840
-rw-r--r--drivers/media/rc/nuvoton-cir.c384
-rw-r--r--drivers/media/rc/nuvoton-cir.h32
-rw-r--r--drivers/media/rc/rc-core-priv.h72
-rw-r--r--drivers/media/rc/rc-loopback.c34
-rw-r--r--drivers/media/rc/rc-main.c586
-rw-r--r--drivers/media/rc/redrat3.c1095
-rw-r--r--drivers/media/rc/st_rc.c408
-rw-r--r--drivers/media/rc/streamzap.c43
-rw-r--r--drivers/media/rc/ttusbir.c449
-rw-r--r--drivers/media/rc/winbond-cir.c646
155 files changed, 15579 insertions, 1896 deletions
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
index 3785162f928..8fbd377e631 100644
--- a/drivers/media/rc/Kconfig
+++ b/drivers/media/rc/Kconfig
@@ -1,21 +1,20 @@
-menuconfig RC_CORE
- tristate "Remote Controller adapters"
+config RC_CORE
+ tristate
+ depends on MEDIA_RC_SUPPORT
depends on INPUT
- default INPUT
- ---help---
- Enable support for Remote Controllers on Linux. This is
- needed in order to support several video capture adapters.
- Currently, all supported devices use InfraRed.
+ default y
- Enable this option if you have a video capture board even
- if you don't need IR, as otherwise, you may not be able to
- compile the driver for your adapter.
+source "drivers/media/rc/keymaps/Kconfig"
-if RC_CORE
+menuconfig RC_DECODERS
+ bool "Remote controller decoders"
+ depends on RC_CORE
+ default y
+if RC_DECODERS
config LIRC
- tristate
- default y
+ tristate "LIRC interface driver"
+ depends on RC_CORE
---help---
Enable this option to build the Linux Infrared Remote
@@ -24,7 +23,16 @@ config LIRC
LIRC daemon handles protocol decoding for IR reception and
encoding for IR transmitting (aka "blasting").
-source "drivers/media/rc/keymaps/Kconfig"
+config IR_LIRC_CODEC
+ tristate "Enable IR to LIRC bridge"
+ depends on RC_CORE
+ depends on LIRC
+ default y
+
+ ---help---
+ Enable this option to pass raw IR to and from userspace via
+ the LIRC interface.
+
config IR_NEC_DECODER
tristate "Enable IR raw decoder for the NEC protocol"
@@ -69,6 +77,7 @@ config IR_JVC_DECODER
config IR_SONY_DECODER
tristate "Enable IR raw decoder for the Sony protocol"
depends on RC_CORE
+ select BITREVERSE
default y
---help---
@@ -87,15 +96,61 @@ config IR_RC5_SZ_DECODER
uses an IR protocol that is almost standard RC-5, but not quite,
as it uses an additional bit).
-config IR_LIRC_CODEC
- tristate "Enable IR to LIRC bridge"
+config IR_SANYO_DECODER
+ tristate "Enable IR raw decoder for the Sanyo protocol"
depends on RC_CORE
- depends on LIRC
default y
---help---
- Enable this option to pass raw IR to and from userspace via
- the LIRC interface.
+ Enable this option if you have an infrared remote control which
+ uses the Sanyo protocol (Sanyo, Aiwa, Chinon remotes),
+ and you need software decoding support.
+
+config IR_SHARP_DECODER
+ tristate "Enable IR raw decoder for the Sharp protocol"
+ depends on RC_CORE
+ default y
+
+ ---help---
+ Enable this option if you have an infrared remote control which
+ uses the Sharp protocol, and you need software decoding support.
+
+config IR_MCE_KBD_DECODER
+ tristate "Enable IR raw decoder for the MCE keyboard/mouse protocol"
+ depends on RC_CORE
+ select BITREVERSE
+ default y
+
+ ---help---
+ Enable this option if you have a Microsoft Remote Keyboard for
+ Windows Media Center Edition, which you would like to use with
+ a raw IR receiver in your system.
+endif #RC_DECODERS
+
+menuconfig RC_DEVICES
+ bool "Remote Controller devices"
+ depends on RC_CORE
+
+if RC_DEVICES
+
+config RC_ATI_REMOTE
+ tristate "ATI / X10 based USB RF remote controls"
+ depends on USB_ARCH_HAS_HCD
+ depends on RC_CORE
+ select USB
+ help
+ Say Y here if you want to use an X10 based USB remote control.
+ These are RF remotes with USB receivers.
+
+ Such devices include the ATI remote that comes with many of ATI's
+ All-In-Wonder video cards, the X10 "Lola" remote, NVIDIA RF remote,
+ Medion RF remote, and SnapStream FireFly remote.
+
+ This driver provides mouse pointer, left and right mouse buttons,
+ and maps all the other remote buttons to keypress events.
+
+ To compile this driver as a module, choose M here: the module will be
+ called ati_remote.
config IR_ENE
tristate "ENE eHome Receiver/Transceiver (pnp id: ENE0100/ENE02xxx)"
@@ -135,6 +190,31 @@ config IR_MCEUSB
To compile this driver as a module, choose M here: the
module will be called mceusb.
+config IR_ITE_CIR
+ tristate "ITE Tech Inc. IT8712/IT8512 Consumer Infrared Transceiver"
+ depends on PNP
+ depends on RC_CORE
+ ---help---
+ Say Y here to enable support for integrated infrared receivers
+ /transceivers made by ITE Tech Inc. These are found in
+ several ASUS devices, like the ASUS Digimatrix or the ASUS
+ EEEBox 1501U.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ite-cir.
+
+config IR_FINTEK
+ tristate "Fintek Consumer Infrared Transceiver"
+ depends on PNP
+ depends on RC_CORE
+ ---help---
+ Say Y here to enable support for integrated infrared receiver
+ /transciever made by Fintek. This chip is found on assorted
+ Jetway motherboards (and of course, possibly others).
+
+ To compile this driver as a module, choose M here: the
+ module will be called fintek-cir.
+
config IR_NUVOTON
tristate "Nuvoton w836x7hg Consumer Infrared Transceiver"
depends on PNP
@@ -148,6 +228,19 @@ config IR_NUVOTON
To compile this driver as a module, choose M here: the
module will be called nuvoton-cir.
+config IR_REDRAT3
+ tristate "RedRat3 IR Transceiver"
+ depends on USB_ARCH_HAS_HCD
+ depends on RC_CORE
+ select NEW_LEDS
+ select LEDS_CLASS
+ select USB
+ ---help---
+ Say Y here if you want to use a RedRat3 Infrared Transceiver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called redrat3.
+
config IR_STREAMZAP
tristate "Streamzap PC Remote IR Receiver"
depends on USB_ARCH_HAS_HCD
@@ -161,22 +254,63 @@ config IR_STREAMZAP
module will be called streamzap.
config IR_WINBOND_CIR
- tristate "Winbond IR remote control"
- depends on X86 && PNP
+ tristate "Winbond IR remote control"
+ depends on X86 && PNP
depends on RC_CORE
- select NEW_LEDS
- select LEDS_CLASS
- select LEDS_TRIGGERS
- select BITREVERSE
+ select NEW_LEDS
+ select LEDS_CLASS
+ select BITREVERSE
---help---
- Say Y here if you want to use the IR remote functionality found
- in some Winbond SuperI/O chips. Currently only the WPCD376I
- chip is supported (included in some Intel Media series
+ Say Y here if you want to use the IR remote functionality found
+ in some Winbond SuperI/O chips. Currently only the WPCD376I
+ chip is supported (included in some Intel Media series
motherboards).
- To compile this driver as a module, choose M here: the module will
+ To compile this driver as a module, choose M here: the module will
be called winbond_cir.
+config IR_IGUANA
+ tristate "IguanaWorks USB IR Transceiver"
+ depends on USB_ARCH_HAS_HCD
+ depends on RC_CORE
+ select USB
+ ---help---
+ Say Y here if you want to use the IguanaWorks USB IR Transceiver.
+ Both infrared receive and send are supported. If you want to
+ change the ID or the pin config, use the user space driver from
+ IguanaWorks.
+
+ Only firmware 0x0205 and later is supported.
+
+ To compile this driver as a module, choose M here: the module will
+ be called iguanair.
+
+config IR_TTUSBIR
+ tristate "TechnoTrend USB IR Receiver"
+ depends on USB_ARCH_HAS_HCD
+ depends on RC_CORE
+ select USB
+ select NEW_LEDS
+ select LEDS_CLASS
+ ---help---
+ Say Y here if you want to use the TechnoTrend USB IR Receiver. The
+ driver can control the led.
+
+ To compile this driver as a module, choose M here: the module will
+ be called ttusbir.
+
+config IR_RX51
+ tristate "Nokia N900 IR transmitter diode"
+ depends on OMAP_DM_TIMER && ARCH_OMAP2PLUS && LIRC && !ARCH_MULTIPLATFORM
+ ---help---
+ Say Y or M here if you want to enable support for the IR
+ transmitter diode built in the Nokia N900 (RX51) device.
+
+ The driver uses omap DM timers for generating the carrier
+ wave and pulses.
+
+source "drivers/media/rc/img-ir/Kconfig"
+
config RC_LOOPBACK
tristate "Remote Control Loopback Driver"
depends on RC_CORE
@@ -190,4 +324,23 @@ config RC_LOOPBACK
To compile this driver as a module, choose M here: the module will
be called rc_loopback.
-endif #RC_CORE
+config IR_GPIO_CIR
+ tristate "GPIO IR remote control"
+ depends on RC_CORE
+ ---help---
+ Say Y if you want to use GPIO based IR Receiver.
+
+ To compile this driver as a module, choose M here: the module will
+ be called gpio-ir-recv.
+
+config RC_ST
+ tristate "ST remote control receiver"
+ depends on ARCH_STI && RC_CORE
+ help
+ Say Y here if you want support for ST remote control driver
+ which allows both IR and UHF RX.
+ The driver passes raw pulse and space information to the LIRC decoder.
+
+ If you're not sure, select N here.
+
+endif #RC_DEVICES
diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
index 67b4f7fe257..f8b54ff4660 100644
--- a/drivers/media/rc/Makefile
+++ b/drivers/media/rc/Makefile
@@ -10,13 +10,26 @@ obj-$(CONFIG_IR_RC6_DECODER) += ir-rc6-decoder.o
obj-$(CONFIG_IR_JVC_DECODER) += ir-jvc-decoder.o
obj-$(CONFIG_IR_SONY_DECODER) += ir-sony-decoder.o
obj-$(CONFIG_IR_RC5_SZ_DECODER) += ir-rc5-sz-decoder.o
+obj-$(CONFIG_IR_SANYO_DECODER) += ir-sanyo-decoder.o
+obj-$(CONFIG_IR_SHARP_DECODER) += ir-sharp-decoder.o
+obj-$(CONFIG_IR_MCE_KBD_DECODER) += ir-mce_kbd-decoder.o
obj-$(CONFIG_IR_LIRC_CODEC) += ir-lirc-codec.o
# stand-alone IR receivers/transmitters
+obj-$(CONFIG_RC_ATI_REMOTE) += ati_remote.o
obj-$(CONFIG_IR_IMON) += imon.o
+obj-$(CONFIG_IR_ITE_CIR) += ite-cir.o
obj-$(CONFIG_IR_MCEUSB) += mceusb.o
+obj-$(CONFIG_IR_FINTEK) += fintek-cir.o
obj-$(CONFIG_IR_NUVOTON) += nuvoton-cir.o
obj-$(CONFIG_IR_ENE) += ene_ir.o
+obj-$(CONFIG_IR_REDRAT3) += redrat3.o
+obj-$(CONFIG_IR_RX51) += ir-rx51.o
obj-$(CONFIG_IR_STREAMZAP) += streamzap.o
obj-$(CONFIG_IR_WINBOND_CIR) += winbond-cir.o
obj-$(CONFIG_RC_LOOPBACK) += rc-loopback.o
+obj-$(CONFIG_IR_GPIO_CIR) += gpio-ir-recv.o
+obj-$(CONFIG_IR_IGUANA) += iguanair.o
+obj-$(CONFIG_IR_TTUSBIR) += ttusbir.o
+obj-$(CONFIG_RC_ST) += st_rc.o
+obj-$(CONFIG_IR_IMG) += img-ir/
diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c
new file mode 100644
index 00000000000..2df7c551601
--- /dev/null
+++ b/drivers/media/rc/ati_remote.c
@@ -0,0 +1,1007 @@
+/*
+ * USB ATI Remote support
+ *
+ * Copyright (c) 2011, 2012 Anssi Hannula <anssi.hannula@iki.fi>
+ * Version 2.2.0 Copyright (c) 2004 Torrey Hoffman <thoffman@arnor.net>
+ * Version 2.1.1 Copyright (c) 2002 Vladimir Dergachev
+ *
+ * This 2.2.0 version is a rewrite / cleanup of the 2.1.1 driver, including
+ * porting to the 2.6 kernel interfaces, along with other modification
+ * to better match the style of the existing usb/input drivers. However, the
+ * protocol and hardware handling is essentially unchanged from 2.1.1.
+ *
+ * The 2.1.1 driver was derived from the usbati_remote and usbkbd drivers by
+ * Vojtech Pavlik.
+ *
+ * Changes:
+ *
+ * Feb 2004: Torrey Hoffman <thoffman@arnor.net>
+ * Version 2.2.0
+ * Jun 2004: Torrey Hoffman <thoffman@arnor.net>
+ * Version 2.2.1
+ * Added key repeat support contributed by:
+ * Vincent Vanackere <vanackere@lif.univ-mrs.fr>
+ * Added support for the "Lola" remote contributed by:
+ * Seth Cohn <sethcohn@yahoo.com>
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * Hardware & software notes
+ *
+ * These remote controls are distributed by ATI as part of their
+ * "All-In-Wonder" video card packages. The receiver self-identifies as a
+ * "USB Receiver" with manufacturer "X10 Wireless Technology Inc".
+ *
+ * The "Lola" remote is available from X10. See:
+ * http://www.x10.com/products/lola_sg1.htm
+ * The Lola is similar to the ATI remote but has no mouse support, and slightly
+ * different keys.
+ *
+ * It is possible to use multiple receivers and remotes on multiple computers
+ * simultaneously by configuring them to use specific channels.
+ *
+ * The RF protocol used by the remote supports 16 distinct channels, 1 to 16.
+ * Actually, it may even support more, at least in some revisions of the
+ * hardware.
+ *
+ * Each remote can be configured to transmit on one channel as follows:
+ * - Press and hold the "hand icon" button.
+ * - When the red LED starts to blink, let go of the "hand icon" button.
+ * - When it stops blinking, input the channel code as two digits, from 01
+ * to 16, and press the hand icon again.
+ *
+ * The timing can be a little tricky. Try loading the module with debug=1
+ * to have the kernel print out messages about the remote control number
+ * and mask. Note: debugging prints remote numbers as zero-based hexadecimal.
+ *
+ * The driver has a "channel_mask" parameter. This bitmask specifies which
+ * channels will be ignored by the module. To mask out channels, just add
+ * all the 2^channel_number values together.
+ *
+ * For instance, set channel_mask = 2^4 = 16 (binary 10000) to make ati_remote
+ * ignore signals coming from remote controls transmitting on channel 4, but
+ * accept all other channels.
+ *
+ * Or, set channel_mask = 65533, (0xFFFD), and all channels except 1 will be
+ * ignored.
+ *
+ * The default is 0 (respond to all channels). Bit 0 and bits 17-32 of this
+ * parameter are unused.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/usb/input.h>
+#include <linux/wait.h>
+#include <linux/jiffies.h>
+#include <media/rc-core.h>
+
+/*
+ * Module and Version Information, Module Parameters
+ */
+
+#define ATI_REMOTE_VENDOR_ID 0x0bc7
+#define LOLA_REMOTE_PRODUCT_ID 0x0002
+#define LOLA2_REMOTE_PRODUCT_ID 0x0003
+#define ATI_REMOTE_PRODUCT_ID 0x0004
+#define NVIDIA_REMOTE_PRODUCT_ID 0x0005
+#define MEDION_REMOTE_PRODUCT_ID 0x0006
+#define FIREFLY_REMOTE_PRODUCT_ID 0x0008
+
+#define DRIVER_VERSION "2.2.1"
+#define DRIVER_AUTHOR "Torrey Hoffman <thoffman@arnor.net>"
+#define DRIVER_DESC "ATI/X10 RF USB Remote Control"
+
+#define NAME_BUFSIZE 80 /* size of product name, path buffers */
+#define DATA_BUFSIZE 63 /* size of URB data buffers */
+
+/*
+ * Duplicate event filtering time.
+ * Sequential, identical KIND_FILTERED inputs with less than
+ * FILTER_TIME milliseconds between them are considered as repeat
+ * events. The hardware generates 5 events for the first keypress
+ * and we have to take this into account for an accurate repeat
+ * behaviour.
+ */
+#define FILTER_TIME 60 /* msec */
+#define REPEAT_DELAY 500 /* msec */
+
+static unsigned long channel_mask;
+module_param(channel_mask, ulong, 0644);
+MODULE_PARM_DESC(channel_mask, "Bitmask of remote control channels to ignore");
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Enable extra debug messages and information");
+
+static int repeat_filter = FILTER_TIME;
+module_param(repeat_filter, int, 0644);
+MODULE_PARM_DESC(repeat_filter, "Repeat filter time, default = 60 msec");
+
+static int repeat_delay = REPEAT_DELAY;
+module_param(repeat_delay, int, 0644);
+MODULE_PARM_DESC(repeat_delay, "Delay before sending repeats, default = 500 msec");
+
+static bool mouse = true;
+module_param(mouse, bool, 0444);
+MODULE_PARM_DESC(mouse, "Enable mouse device, default = yes");
+
+#define dbginfo(dev, format, arg...) \
+ do { if (debug) dev_info(dev , format , ## arg); } while (0)
+#undef err
+#define err(format, arg...) printk(KERN_ERR format , ## arg)
+
+struct ati_receiver_type {
+ /* either default_keymap or get_default_keymap should be set */
+ const char *default_keymap;
+ const char *(*get_default_keymap)(struct usb_interface *interface);
+};
+
+static const char *get_medion_keymap(struct usb_interface *interface)
+{
+ struct usb_device *udev = interface_to_usbdev(interface);
+
+ /*
+ * There are many different Medion remotes shipped with a receiver
+ * with the same usb id, but the receivers have subtle differences
+ * in the USB descriptors allowing us to detect them.
+ */
+
+ if (udev->manufacturer && udev->product) {
+ if (udev->actconfig->desc.bmAttributes & USB_CONFIG_ATT_WAKEUP) {
+
+ if (!strcmp(udev->manufacturer, "X10 Wireless Technology Inc")
+ && !strcmp(udev->product, "USB Receiver"))
+ return RC_MAP_MEDION_X10_DIGITAINER;
+
+ if (!strcmp(udev->manufacturer, "X10 WTI")
+ && !strcmp(udev->product, "RF receiver"))
+ return RC_MAP_MEDION_X10_OR2X;
+ } else {
+
+ if (!strcmp(udev->manufacturer, "X10 Wireless Technology Inc")
+ && !strcmp(udev->product, "USB Receiver"))
+ return RC_MAP_MEDION_X10;
+ }
+ }
+
+ dev_info(&interface->dev,
+ "Unknown Medion X10 receiver, using default ati_remote Medion keymap\n");
+
+ return RC_MAP_MEDION_X10;
+}
+
+static const struct ati_receiver_type type_ati = {
+ .default_keymap = RC_MAP_ATI_X10
+};
+static const struct ati_receiver_type type_medion = {
+ .get_default_keymap = get_medion_keymap
+};
+static const struct ati_receiver_type type_firefly = {
+ .default_keymap = RC_MAP_SNAPSTREAM_FIREFLY
+};
+
+static struct usb_device_id ati_remote_table[] = {
+ {
+ USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA_REMOTE_PRODUCT_ID),
+ .driver_info = (unsigned long)&type_ati
+ },
+ {
+ USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA2_REMOTE_PRODUCT_ID),
+ .driver_info = (unsigned long)&type_ati
+ },
+ {
+ USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID),
+ .driver_info = (unsigned long)&type_ati
+ },
+ {
+ USB_DEVICE(ATI_REMOTE_VENDOR_ID, NVIDIA_REMOTE_PRODUCT_ID),
+ .driver_info = (unsigned long)&type_ati
+ },
+ {
+ USB_DEVICE(ATI_REMOTE_VENDOR_ID, MEDION_REMOTE_PRODUCT_ID),
+ .driver_info = (unsigned long)&type_medion
+ },
+ {
+ USB_DEVICE(ATI_REMOTE_VENDOR_ID, FIREFLY_REMOTE_PRODUCT_ID),
+ .driver_info = (unsigned long)&type_firefly
+ },
+ {} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, ati_remote_table);
+
+/* Get hi and low bytes of a 16-bits int */
+#define HI(a) ((unsigned char)((a) >> 8))
+#define LO(a) ((unsigned char)((a) & 0xff))
+
+#define SEND_FLAG_IN_PROGRESS 1
+#define SEND_FLAG_COMPLETE 2
+
+/* Device initialization strings */
+static char init1[] = { 0x01, 0x00, 0x20, 0x14 };
+static char init2[] = { 0x01, 0x00, 0x20, 0x14, 0x20, 0x20, 0x20 };
+
+struct ati_remote {
+ struct input_dev *idev;
+ struct rc_dev *rdev;
+ struct usb_device *udev;
+ struct usb_interface *interface;
+
+ struct urb *irq_urb;
+ struct urb *out_urb;
+ struct usb_endpoint_descriptor *endpoint_in;
+ struct usb_endpoint_descriptor *endpoint_out;
+ unsigned char *inbuf;
+ unsigned char *outbuf;
+ dma_addr_t inbuf_dma;
+ dma_addr_t outbuf_dma;
+
+ unsigned char old_data; /* Detect duplicate events */
+ unsigned long old_jiffies;
+ unsigned long acc_jiffies; /* handle acceleration */
+ unsigned long first_jiffies;
+
+ unsigned int repeat_count;
+
+ char rc_name[NAME_BUFSIZE];
+ char rc_phys[NAME_BUFSIZE];
+ char mouse_name[NAME_BUFSIZE];
+ char mouse_phys[NAME_BUFSIZE];
+
+ wait_queue_head_t wait;
+ int send_flags;
+
+ int users; /* 0-2, users are rc and input */
+ struct mutex open_mutex;
+};
+
+/* "Kinds" of messages sent from the hardware to the driver. */
+#define KIND_END 0
+#define KIND_LITERAL 1 /* Simply pass to input system */
+#define KIND_FILTERED 2 /* Add artificial key-up events, drop keyrepeats */
+#define KIND_LU 3 /* Directional keypad diagonals - left up, */
+#define KIND_RU 4 /* right up, */
+#define KIND_LD 5 /* left down, */
+#define KIND_RD 6 /* right down */
+#define KIND_ACCEL 7 /* Directional keypad - left, right, up, down.*/
+
+/* Translation table from hardware messages to input events. */
+static const struct {
+ short kind;
+ unsigned char data;
+ int type;
+ unsigned int code;
+ int value;
+} ati_remote_tbl[] = {
+ /* Directional control pad axes */
+ {KIND_ACCEL, 0x70, EV_REL, REL_X, -1}, /* left */
+ {KIND_ACCEL, 0x71, EV_REL, REL_X, 1}, /* right */
+ {KIND_ACCEL, 0x72, EV_REL, REL_Y, -1}, /* up */
+ {KIND_ACCEL, 0x73, EV_REL, REL_Y, 1}, /* down */
+ /* Directional control pad diagonals */
+ {KIND_LU, 0x74, EV_REL, 0, 0}, /* left up */
+ {KIND_RU, 0x75, EV_REL, 0, 0}, /* right up */
+ {KIND_LD, 0x77, EV_REL, 0, 0}, /* left down */
+ {KIND_RD, 0x76, EV_REL, 0, 0}, /* right down */
+
+ /* "Mouse button" buttons */
+ {KIND_LITERAL, 0x78, EV_KEY, BTN_LEFT, 1}, /* left btn down */
+ {KIND_LITERAL, 0x79, EV_KEY, BTN_LEFT, 0}, /* left btn up */
+ {KIND_LITERAL, 0x7c, EV_KEY, BTN_RIGHT, 1},/* right btn down */
+ {KIND_LITERAL, 0x7d, EV_KEY, BTN_RIGHT, 0},/* right btn up */
+
+ /* Artificial "doubleclick" events are generated by the hardware.
+ * They are mapped to the "side" and "extra" mouse buttons here. */
+ {KIND_FILTERED, 0x7a, EV_KEY, BTN_SIDE, 1}, /* left dblclick */
+ {KIND_FILTERED, 0x7e, EV_KEY, BTN_EXTRA, 1},/* right dblclick */
+
+ /* Non-mouse events are handled by rc-core */
+ {KIND_END, 0x00, EV_MAX + 1, 0, 0}
+};
+
+/*
+ * ati_remote_dump_input
+ */
+static void ati_remote_dump(struct device *dev, unsigned char *data,
+ unsigned int len)
+{
+ if (len == 1) {
+ if (data[0] != (unsigned char)0xff && data[0] != 0x00)
+ dev_warn(dev, "Weird byte 0x%02x\n", data[0]);
+ } else if (len == 4)
+ dev_warn(dev, "Weird key %*ph\n", 4, data);
+ else
+ dev_warn(dev, "Weird data, len=%d %*ph ...\n", len, 6, data);
+}
+
+/*
+ * ati_remote_open
+ */
+static int ati_remote_open(struct ati_remote *ati_remote)
+{
+ int err = 0;
+
+ mutex_lock(&ati_remote->open_mutex);
+
+ if (ati_remote->users++ != 0)
+ goto out; /* one was already active */
+
+ /* On first open, submit the read urb which was set up previously. */
+ ati_remote->irq_urb->dev = ati_remote->udev;
+ if (usb_submit_urb(ati_remote->irq_urb, GFP_KERNEL)) {
+ dev_err(&ati_remote->interface->dev,
+ "%s: usb_submit_urb failed!\n", __func__);
+ err = -EIO;
+ }
+
+out: mutex_unlock(&ati_remote->open_mutex);
+ return err;
+}
+
+/*
+ * ati_remote_close
+ */
+static void ati_remote_close(struct ati_remote *ati_remote)
+{
+ mutex_lock(&ati_remote->open_mutex);
+ if (--ati_remote->users == 0)
+ usb_kill_urb(ati_remote->irq_urb);
+ mutex_unlock(&ati_remote->open_mutex);
+}
+
+static int ati_remote_input_open(struct input_dev *inputdev)
+{
+ struct ati_remote *ati_remote = input_get_drvdata(inputdev);
+ return ati_remote_open(ati_remote);
+}
+
+static void ati_remote_input_close(struct input_dev *inputdev)
+{
+ struct ati_remote *ati_remote = input_get_drvdata(inputdev);
+ ati_remote_close(ati_remote);
+}
+
+static int ati_remote_rc_open(struct rc_dev *rdev)
+{
+ struct ati_remote *ati_remote = rdev->priv;
+ return ati_remote_open(ati_remote);
+}
+
+static void ati_remote_rc_close(struct rc_dev *rdev)
+{
+ struct ati_remote *ati_remote = rdev->priv;
+ ati_remote_close(ati_remote);
+}
+
+/*
+ * ati_remote_irq_out
+ */
+static void ati_remote_irq_out(struct urb *urb)
+{
+ struct ati_remote *ati_remote = urb->context;
+
+ if (urb->status) {
+ dev_dbg(&ati_remote->interface->dev, "%s: status %d\n",
+ __func__, urb->status);
+ return;
+ }
+
+ ati_remote->send_flags |= SEND_FLAG_COMPLETE;
+ wmb();
+ wake_up(&ati_remote->wait);
+}
+
+/*
+ * ati_remote_sendpacket
+ *
+ * Used to send device initialization strings
+ */
+static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd,
+ unsigned char *data)
+{
+ int retval = 0;
+
+ /* Set up out_urb */
+ memcpy(ati_remote->out_urb->transfer_buffer + 1, data, LO(cmd));
+ ((char *) ati_remote->out_urb->transfer_buffer)[0] = HI(cmd);
+
+ ati_remote->out_urb->transfer_buffer_length = LO(cmd) + 1;
+ ati_remote->out_urb->dev = ati_remote->udev;
+ ati_remote->send_flags = SEND_FLAG_IN_PROGRESS;
+
+ retval = usb_submit_urb(ati_remote->out_urb, GFP_ATOMIC);
+ if (retval) {
+ dev_dbg(&ati_remote->interface->dev,
+ "sendpacket: usb_submit_urb failed: %d\n", retval);
+ return retval;
+ }
+
+ wait_event_timeout(ati_remote->wait,
+ ((ati_remote->out_urb->status != -EINPROGRESS) ||
+ (ati_remote->send_flags & SEND_FLAG_COMPLETE)),
+ HZ);
+ usb_kill_urb(ati_remote->out_urb);
+
+ return retval;
+}
+
+/*
+ * ati_remote_compute_accel
+ *
+ * Implements acceleration curve for directional control pad
+ * If elapsed time since last event is > 1/4 second, user "stopped",
+ * so reset acceleration. Otherwise, user is probably holding the control
+ * pad down, so we increase acceleration, ramping up over two seconds to
+ * a maximum speed.
+ */
+static int ati_remote_compute_accel(struct ati_remote *ati_remote)
+{
+ static const char accel[] = { 1, 2, 4, 6, 9, 13, 20 };
+ unsigned long now = jiffies;
+ int acc;
+
+ if (time_after(now, ati_remote->old_jiffies + msecs_to_jiffies(250))) {
+ acc = 1;
+ ati_remote->acc_jiffies = now;
+ }
+ else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(125)))
+ acc = accel[0];
+ else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(250)))
+ acc = accel[1];
+ else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(500)))
+ acc = accel[2];
+ else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(1000)))
+ acc = accel[3];
+ else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(1500)))
+ acc = accel[4];
+ else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(2000)))
+ acc = accel[5];
+ else
+ acc = accel[6];
+
+ return acc;
+}
+
+/*
+ * ati_remote_report_input
+ */
+static void ati_remote_input_report(struct urb *urb)
+{
+ struct ati_remote *ati_remote = urb->context;
+ unsigned char *data= ati_remote->inbuf;
+ struct input_dev *dev = ati_remote->idev;
+ int index = -1;
+ int acc;
+ int remote_num;
+ unsigned char scancode;
+ u32 wheel_keycode = KEY_RESERVED;
+ int i;
+
+ /*
+ * data[0] = 0x14
+ * data[1] = data[2] + data[3] + 0xd5 (a checksum byte)
+ * data[2] = the key code (with toggle bit in MSB with some models)
+ * data[3] = channel << 4 (the low 4 bits must be zero)
+ */
+
+ /* Deal with strange looking inputs */
+ if ( (urb->actual_length != 4) || (data[0] != 0x14) ||
+ ((data[3] & 0x0f) != 0x00) ) {
+ ati_remote_dump(&urb->dev->dev, data, urb->actual_length);
+ return;
+ }
+
+ if (data[1] != ((data[2] + data[3] + 0xd5) & 0xff)) {
+ dbginfo(&ati_remote->interface->dev,
+ "wrong checksum in input: %*ph\n", 4, data);
+ return;
+ }
+
+ /* Mask unwanted remote channels. */
+ /* note: remote_num is 0-based, channel 1 on remote == 0 here */
+ remote_num = (data[3] >> 4) & 0x0f;
+ if (channel_mask & (1 << (remote_num + 1))) {
+ dbginfo(&ati_remote->interface->dev,
+ "Masked input from channel 0x%02x: data %02x,%02x, "
+ "mask= 0x%02lx\n",
+ remote_num, data[1], data[2], channel_mask);
+ return;
+ }
+
+ /*
+ * MSB is a toggle code, though only used by some devices
+ * (e.g. SnapStream Firefly)
+ */
+ scancode = data[2] & 0x7f;
+
+ dbginfo(&ati_remote->interface->dev,
+ "channel 0x%02x; key data %02x, scancode %02x\n",
+ remote_num, data[2], scancode);
+
+ if (scancode >= 0x70) {
+ /*
+ * This is either a mouse or scrollwheel event, depending on
+ * the remote/keymap.
+ * Get the keycode assigned to scancode 0x78/0x70. If it is
+ * set, assume this is a scrollwheel up/down event.
+ */
+ wheel_keycode = rc_g_keycode_from_table(ati_remote->rdev,
+ scancode & 0x78);
+
+ if (wheel_keycode == KEY_RESERVED) {
+ /* scrollwheel was not mapped, assume mouse */
+
+ /* Look up event code index in the mouse translation
+ * table.
+ */
+ for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) {
+ if (scancode == ati_remote_tbl[i].data) {
+ index = i;
+ break;
+ }
+ }
+ }
+ }
+
+ if (index >= 0 && ati_remote_tbl[index].kind == KIND_LITERAL) {
+ input_event(dev, ati_remote_tbl[index].type,
+ ati_remote_tbl[index].code,
+ ati_remote_tbl[index].value);
+ input_sync(dev);
+
+ ati_remote->old_jiffies = jiffies;
+ return;
+ }
+
+ if (index < 0 || ati_remote_tbl[index].kind == KIND_FILTERED) {
+ unsigned long now = jiffies;
+
+ /* Filter duplicate events which happen "too close" together. */
+ if (ati_remote->old_data == data[2] &&
+ time_before(now, ati_remote->old_jiffies +
+ msecs_to_jiffies(repeat_filter))) {
+ ati_remote->repeat_count++;
+ } else {
+ ati_remote->repeat_count = 0;
+ ati_remote->first_jiffies = now;
+ }
+
+ ati_remote->old_data = data[2];
+ ati_remote->old_jiffies = now;
+
+ /* Ensure we skip at least the 4 first duplicate events (generated
+ * by a single keypress), and continue skipping until repeat_delay
+ * msecs have passed
+ */
+ if (ati_remote->repeat_count > 0 &&
+ (ati_remote->repeat_count < 5 ||
+ time_before(now, ati_remote->first_jiffies +
+ msecs_to_jiffies(repeat_delay))))
+ return;
+
+ if (index < 0) {
+ /* Not a mouse event, hand it to rc-core. */
+ int count = 1;
+
+ if (wheel_keycode != KEY_RESERVED) {
+ /*
+ * This is a scrollwheel event, send the
+ * scroll up (0x78) / down (0x70) scancode
+ * repeatedly as many times as indicated by
+ * rest of the scancode.
+ */
+ count = (scancode & 0x07) + 1;
+ scancode &= 0x78;
+ }
+
+ while (count--) {
+ /*
+ * We don't use the rc-core repeat handling yet as
+ * it would cause ghost repeats which would be a
+ * regression for this driver.
+ */
+ rc_keydown_notimeout(ati_remote->rdev, scancode,
+ data[2]);
+ rc_keyup(ati_remote->rdev);
+ }
+ return;
+ }
+
+ input_event(dev, ati_remote_tbl[index].type,
+ ati_remote_tbl[index].code, 1);
+ input_sync(dev);
+ input_event(dev, ati_remote_tbl[index].type,
+ ati_remote_tbl[index].code, 0);
+ input_sync(dev);
+
+ } else {
+
+ /*
+ * Other event kinds are from the directional control pad, and
+ * have an acceleration factor applied to them. Without this
+ * acceleration, the control pad is mostly unusable.
+ */
+ acc = ati_remote_compute_accel(ati_remote);
+
+ switch (ati_remote_tbl[index].kind) {
+ case KIND_ACCEL:
+ input_event(dev, ati_remote_tbl[index].type,
+ ati_remote_tbl[index].code,
+ ati_remote_tbl[index].value * acc);
+ break;
+ case KIND_LU:
+ input_report_rel(dev, REL_X, -acc);
+ input_report_rel(dev, REL_Y, -acc);
+ break;
+ case KIND_RU:
+ input_report_rel(dev, REL_X, acc);
+ input_report_rel(dev, REL_Y, -acc);
+ break;
+ case KIND_LD:
+ input_report_rel(dev, REL_X, -acc);
+ input_report_rel(dev, REL_Y, acc);
+ break;
+ case KIND_RD:
+ input_report_rel(dev, REL_X, acc);
+ input_report_rel(dev, REL_Y, acc);
+ break;
+ default:
+ dev_dbg(&ati_remote->interface->dev,
+ "ati_remote kind=%d\n",
+ ati_remote_tbl[index].kind);
+ }
+ input_sync(dev);
+
+ ati_remote->old_jiffies = jiffies;
+ ati_remote->old_data = data[2];
+ }
+}
+
+/*
+ * ati_remote_irq_in
+ */
+static void ati_remote_irq_in(struct urb *urb)
+{
+ struct ati_remote *ati_remote = urb->context;
+ int retval;
+
+ switch (urb->status) {
+ case 0: /* success */
+ ati_remote_input_report(urb);
+ break;
+ case -ECONNRESET: /* unlink */
+ case -ENOENT:
+ case -ESHUTDOWN:
+ dev_dbg(&ati_remote->interface->dev,
+ "%s: urb error status, unlink?\n",
+ __func__);
+ return;
+ default: /* error */
+ dev_dbg(&ati_remote->interface->dev,
+ "%s: Nonzero urb status %d\n",
+ __func__, urb->status);
+ }
+
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval)
+ dev_err(&ati_remote->interface->dev,
+ "%s: usb_submit_urb()=%d\n",
+ __func__, retval);
+}
+
+/*
+ * ati_remote_alloc_buffers
+ */
+static int ati_remote_alloc_buffers(struct usb_device *udev,
+ struct ati_remote *ati_remote)
+{
+ ati_remote->inbuf = usb_alloc_coherent(udev, DATA_BUFSIZE, GFP_ATOMIC,
+ &ati_remote->inbuf_dma);
+ if (!ati_remote->inbuf)
+ return -1;
+
+ ati_remote->outbuf = usb_alloc_coherent(udev, DATA_BUFSIZE, GFP_ATOMIC,
+ &ati_remote->outbuf_dma);
+ if (!ati_remote->outbuf)
+ return -1;
+
+ ati_remote->irq_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!ati_remote->irq_urb)
+ return -1;
+
+ ati_remote->out_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!ati_remote->out_urb)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * ati_remote_free_buffers
+ */
+static void ati_remote_free_buffers(struct ati_remote *ati_remote)
+{
+ usb_free_urb(ati_remote->irq_urb);
+ usb_free_urb(ati_remote->out_urb);
+
+ usb_free_coherent(ati_remote->udev, DATA_BUFSIZE,
+ ati_remote->inbuf, ati_remote->inbuf_dma);
+
+ usb_free_coherent(ati_remote->udev, DATA_BUFSIZE,
+ ati_remote->outbuf, ati_remote->outbuf_dma);
+}
+
+static void ati_remote_input_init(struct ati_remote *ati_remote)
+{
+ struct input_dev *idev = ati_remote->idev;
+ int i;
+
+ idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+ idev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
+ BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_SIDE) | BIT_MASK(BTN_EXTRA);
+ idev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+ for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++)
+ if (ati_remote_tbl[i].type == EV_KEY)
+ set_bit(ati_remote_tbl[i].code, idev->keybit);
+
+ input_set_drvdata(idev, ati_remote);
+
+ idev->open = ati_remote_input_open;
+ idev->close = ati_remote_input_close;
+
+ idev->name = ati_remote->mouse_name;
+ idev->phys = ati_remote->mouse_phys;
+
+ usb_to_input_id(ati_remote->udev, &idev->id);
+ idev->dev.parent = &ati_remote->interface->dev;
+}
+
+static void ati_remote_rc_init(struct ati_remote *ati_remote)
+{
+ struct rc_dev *rdev = ati_remote->rdev;
+
+ rdev->priv = ati_remote;
+ rdev->driver_type = RC_DRIVER_SCANCODE;
+ rc_set_allowed_protocols(rdev, RC_BIT_OTHER);
+ rdev->driver_name = "ati_remote";
+
+ rdev->open = ati_remote_rc_open;
+ rdev->close = ati_remote_rc_close;
+
+ rdev->input_name = ati_remote->rc_name;
+ rdev->input_phys = ati_remote->rc_phys;
+
+ usb_to_input_id(ati_remote->udev, &rdev->input_id);
+ rdev->dev.parent = &ati_remote->interface->dev;
+}
+
+static int ati_remote_initialize(struct ati_remote *ati_remote)
+{
+ struct usb_device *udev = ati_remote->udev;
+ int pipe, maxp;
+
+ init_waitqueue_head(&ati_remote->wait);
+
+ /* Set up irq_urb */
+ pipe = usb_rcvintpipe(udev, ati_remote->endpoint_in->bEndpointAddress);
+ maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+ maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp;
+
+ usb_fill_int_urb(ati_remote->irq_urb, udev, pipe, ati_remote->inbuf,
+ maxp, ati_remote_irq_in, ati_remote,
+ ati_remote->endpoint_in->bInterval);
+ ati_remote->irq_urb->transfer_dma = ati_remote->inbuf_dma;
+ ati_remote->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ /* Set up out_urb */
+ pipe = usb_sndintpipe(udev, ati_remote->endpoint_out->bEndpointAddress);
+ maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+ maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp;
+
+ usb_fill_int_urb(ati_remote->out_urb, udev, pipe, ati_remote->outbuf,
+ maxp, ati_remote_irq_out, ati_remote,
+ ati_remote->endpoint_out->bInterval);
+ ati_remote->out_urb->transfer_dma = ati_remote->outbuf_dma;
+ ati_remote->out_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ /* send initialization strings */
+ if ((ati_remote_sendpacket(ati_remote, 0x8004, init1)) ||
+ (ati_remote_sendpacket(ati_remote, 0x8007, init2))) {
+ dev_err(&ati_remote->interface->dev,
+ "Initializing ati_remote hardware failed.\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*
+ * ati_remote_probe
+ */
+static int ati_remote_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(interface);
+ struct usb_host_interface *iface_host = interface->cur_altsetting;
+ struct usb_endpoint_descriptor *endpoint_in, *endpoint_out;
+ struct ati_receiver_type *type = (struct ati_receiver_type *)id->driver_info;
+ struct ati_remote *ati_remote;
+ struct input_dev *input_dev;
+ struct rc_dev *rc_dev;
+ int err = -ENOMEM;
+
+ if (iface_host->desc.bNumEndpoints != 2) {
+ err("%s: Unexpected desc.bNumEndpoints\n", __func__);
+ return -ENODEV;
+ }
+
+ endpoint_in = &iface_host->endpoint[0].desc;
+ endpoint_out = &iface_host->endpoint[1].desc;
+
+ if (!usb_endpoint_is_int_in(endpoint_in)) {
+ err("%s: Unexpected endpoint_in\n", __func__);
+ return -ENODEV;
+ }
+ if (le16_to_cpu(endpoint_in->wMaxPacketSize) == 0) {
+ err("%s: endpoint_in message size==0? \n", __func__);
+ return -ENODEV;
+ }
+
+ ati_remote = kzalloc(sizeof (struct ati_remote), GFP_KERNEL);
+ rc_dev = rc_allocate_device();
+ if (!ati_remote || !rc_dev)
+ goto exit_free_dev_rdev;
+
+ /* Allocate URB buffers, URBs */
+ if (ati_remote_alloc_buffers(udev, ati_remote))
+ goto exit_free_buffers;
+
+ ati_remote->endpoint_in = endpoint_in;
+ ati_remote->endpoint_out = endpoint_out;
+ ati_remote->udev = udev;
+ ati_remote->rdev = rc_dev;
+ ati_remote->interface = interface;
+
+ usb_make_path(udev, ati_remote->rc_phys, sizeof(ati_remote->rc_phys));
+ strlcpy(ati_remote->mouse_phys, ati_remote->rc_phys,
+ sizeof(ati_remote->mouse_phys));
+
+ strlcat(ati_remote->rc_phys, "/input0", sizeof(ati_remote->rc_phys));
+ strlcat(ati_remote->mouse_phys, "/input1", sizeof(ati_remote->mouse_phys));
+
+ if (udev->manufacturer)
+ strlcpy(ati_remote->rc_name, udev->manufacturer,
+ sizeof(ati_remote->rc_name));
+
+ if (udev->product)
+ snprintf(ati_remote->rc_name, sizeof(ati_remote->rc_name),
+ "%s %s", ati_remote->rc_name, udev->product);
+
+ if (!strlen(ati_remote->rc_name))
+ snprintf(ati_remote->rc_name, sizeof(ati_remote->rc_name),
+ DRIVER_DESC "(%04x,%04x)",
+ le16_to_cpu(ati_remote->udev->descriptor.idVendor),
+ le16_to_cpu(ati_remote->udev->descriptor.idProduct));
+
+ snprintf(ati_remote->mouse_name, sizeof(ati_remote->mouse_name),
+ "%s mouse", ati_remote->rc_name);
+
+ rc_dev->map_name = RC_MAP_ATI_X10; /* default map */
+
+ /* set default keymap according to receiver model */
+ if (type) {
+ if (type->default_keymap)
+ rc_dev->map_name = type->default_keymap;
+ else if (type->get_default_keymap)
+ rc_dev->map_name = type->get_default_keymap(interface);
+ }
+
+ ati_remote_rc_init(ati_remote);
+ mutex_init(&ati_remote->open_mutex);
+
+ /* Device Hardware Initialization - fills in ati_remote->idev from udev. */
+ err = ati_remote_initialize(ati_remote);
+ if (err)
+ goto exit_kill_urbs;
+
+ /* Set up and register rc device */
+ err = rc_register_device(ati_remote->rdev);
+ if (err)
+ goto exit_kill_urbs;
+
+ /* use our delay for rc_dev */
+ ati_remote->rdev->input_dev->rep[REP_DELAY] = repeat_delay;
+
+ /* Set up and register mouse input device */
+ if (mouse) {
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ err = -ENOMEM;
+ goto exit_unregister_device;
+ }
+
+ ati_remote->idev = input_dev;
+ ati_remote_input_init(ati_remote);
+ err = input_register_device(input_dev);
+
+ if (err)
+ goto exit_free_input_device;
+ }
+
+ usb_set_intfdata(interface, ati_remote);
+ return 0;
+
+ exit_free_input_device:
+ input_free_device(input_dev);
+ exit_unregister_device:
+ rc_unregister_device(rc_dev);
+ rc_dev = NULL;
+ exit_kill_urbs:
+ usb_kill_urb(ati_remote->irq_urb);
+ usb_kill_urb(ati_remote->out_urb);
+ exit_free_buffers:
+ ati_remote_free_buffers(ati_remote);
+ exit_free_dev_rdev:
+ rc_free_device(rc_dev);
+ kfree(ati_remote);
+ return err;
+}
+
+/*
+ * ati_remote_disconnect
+ */
+static void ati_remote_disconnect(struct usb_interface *interface)
+{
+ struct ati_remote *ati_remote;
+
+ ati_remote = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+ if (!ati_remote) {
+ dev_warn(&interface->dev, "%s - null device?\n", __func__);
+ return;
+ }
+
+ usb_kill_urb(ati_remote->irq_urb);
+ usb_kill_urb(ati_remote->out_urb);
+ if (ati_remote->idev)
+ input_unregister_device(ati_remote->idev);
+ rc_unregister_device(ati_remote->rdev);
+ ati_remote_free_buffers(ati_remote);
+ kfree(ati_remote);
+}
+
+/* usb specific object to register with the usb subsystem */
+static struct usb_driver ati_remote_driver = {
+ .name = "ati_remote",
+ .probe = ati_remote_probe,
+ .disconnect = ati_remote_disconnect,
+ .id_table = ati_remote_table,
+};
+
+module_usb_driver(ati_remote_driver);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c
index 1ac49139158..fc9d23f2ed3 100644
--- a/drivers/media/rc/ene_ir.c
+++ b/drivers/media/rc/ene_ir.c
@@ -30,6 +30,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pnp.h>
@@ -118,31 +120,31 @@ static int ene_hw_detect(struct ene_device *dev)
dev->pll_freq == ENE_DEFAULT_PLL_FREQ ? 2 : 4;
if (hw_revision == 0xFF) {
- ene_warn("device seems to be disabled");
- ene_warn("send a mail to lirc-list@lists.sourceforge.net");
- ene_warn("please attach output of acpidump and dmidecode");
+ pr_warn("device seems to be disabled\n");
+ pr_warn("send a mail to lirc-list@lists.sourceforge.net\n");
+ pr_warn("please attach output of acpidump and dmidecode\n");
return -ENODEV;
}
- ene_notice("chip is 0x%02x%02x - kbver = 0x%02x, rev = 0x%02x",
- chip_major, chip_minor, old_ver, hw_revision);
+ pr_notice("chip is 0x%02x%02x - kbver = 0x%02x, rev = 0x%02x\n",
+ chip_major, chip_minor, old_ver, hw_revision);
- ene_notice("PLL freq = %d", dev->pll_freq);
+ pr_notice("PLL freq = %d\n", dev->pll_freq);
if (chip_major == 0x33) {
- ene_warn("chips 0x33xx aren't supported");
+ pr_warn("chips 0x33xx aren't supported\n");
return -ENODEV;
}
if (chip_major == 0x39 && chip_minor == 0x26 && hw_revision == 0xC0) {
dev->hw_revision = ENE_HW_C;
- ene_notice("KB3926C detected");
+ pr_notice("KB3926C detected\n");
} else if (old_ver == 0x24 && hw_revision == 0xC0) {
dev->hw_revision = ENE_HW_B;
- ene_notice("KB3926B detected");
+ pr_notice("KB3926B detected\n");
} else {
dev->hw_revision = ENE_HW_D;
- ene_notice("KB3926D or higher detected");
+ pr_notice("KB3926D or higher detected\n");
}
/* detect features hardware supports */
@@ -152,7 +154,7 @@ static int ene_hw_detect(struct ene_device *dev)
fw_reg1 = ene_read_reg(dev, ENE_FW1);
fw_reg2 = ene_read_reg(dev, ENE_FW2);
- ene_notice("Firmware regs: %02x %02x", fw_reg1, fw_reg2);
+ pr_notice("Firmware regs: %02x %02x\n", fw_reg1, fw_reg2);
dev->hw_use_gpio_0a = !!(fw_reg2 & ENE_FW2_GP0A);
dev->hw_learning_and_tx_capable = !!(fw_reg2 & ENE_FW2_LEARNING);
@@ -161,30 +163,29 @@ static int ene_hw_detect(struct ene_device *dev)
if (dev->hw_learning_and_tx_capable)
dev->hw_fan_input = !!(fw_reg2 & ENE_FW2_FAN_INPUT);
- ene_notice("Hardware features:");
+ pr_notice("Hardware features:\n");
if (dev->hw_learning_and_tx_capable) {
- ene_notice("* Supports transmitting & learning mode");
- ene_notice(" This feature is rare and therefore,");
- ene_notice(" you are welcome to test it,");
- ene_notice(" and/or contact the author via:");
- ene_notice(" lirc-list@lists.sourceforge.net");
- ene_notice(" or maximlevitsky@gmail.com");
+ pr_notice("* Supports transmitting & learning mode\n");
+ pr_notice(" This feature is rare and therefore,\n");
+ pr_notice(" you are welcome to test it,\n");
+ pr_notice(" and/or contact the author via:\n");
+ pr_notice(" lirc-list@lists.sourceforge.net\n");
+ pr_notice(" or maximlevitsky@gmail.com\n");
- ene_notice("* Uses GPIO %s for IR raw input",
- dev->hw_use_gpio_0a ? "40" : "0A");
+ pr_notice("* Uses GPIO %s for IR raw input\n",
+ dev->hw_use_gpio_0a ? "40" : "0A");
if (dev->hw_fan_input)
- ene_notice("* Uses unused fan feedback input as source"
- " of demodulated IR data");
+ pr_notice("* Uses unused fan feedback input as source of demodulated IR data\n");
}
if (!dev->hw_fan_input)
- ene_notice("* Uses GPIO %s for IR demodulated input",
- dev->hw_use_gpio_0a ? "0A" : "40");
+ pr_notice("* Uses GPIO %s for IR demodulated input\n",
+ dev->hw_use_gpio_0a ? "0A" : "40");
if (dev->hw_extra_buffer)
- ene_notice("* Uses new style input buffer");
+ pr_notice("* Uses new style input buffer\n");
return 0;
}
@@ -215,13 +216,13 @@ static void ene_rx_setup_hw_buffer(struct ene_device *dev)
dev->buffer_len = dev->extra_buf1_len + dev->extra_buf2_len + 8;
- ene_notice("Hardware uses 2 extended buffers:");
- ene_notice(" 0x%04x - len : %d", dev->extra_buf1_address,
- dev->extra_buf1_len);
- ene_notice(" 0x%04x - len : %d", dev->extra_buf2_address,
- dev->extra_buf2_len);
+ pr_notice("Hardware uses 2 extended buffers:\n");
+ pr_notice(" 0x%04x - len : %d\n",
+ dev->extra_buf1_address, dev->extra_buf1_len);
+ pr_notice(" 0x%04x - len : %d\n",
+ dev->extra_buf2_address, dev->extra_buf2_len);
- ene_notice("Total buffer len = %d", dev->buffer_len);
+ pr_notice("Total buffer len = %d\n", dev->buffer_len);
if (dev->buffer_len > 64 || dev->buffer_len < 16)
goto error;
@@ -240,7 +241,7 @@ static void ene_rx_setup_hw_buffer(struct ene_device *dev)
ene_set_reg_mask(dev, ENE_FW1, ENE_FW1_EXTRA_BUF_HND);
return;
error:
- ene_warn("Error validating extra buffers, device probably won't work");
+ pr_warn("Error validating extra buffers, device probably won't work\n");
dev->hw_extra_buffer = false;
ene_clear_reg_mask(dev, ENE_FW1, ENE_FW1_EXTRA_BUF_HND);
}
@@ -323,12 +324,12 @@ static int ene_rx_get_sample_reg(struct ene_device *dev)
return dev->extra_buf2_address + r_pointer;
}
- dbg("attempt to read beyong ring bufer end");
+ dbg("attempt to read beyond ring buffer end");
return 0;
}
/* Sense current received carrier */
-void ene_rx_sense_carrier(struct ene_device *dev)
+static void ene_rx_sense_carrier(struct ene_device *dev)
{
DEFINE_IR_RAW_EVENT(ev);
@@ -475,7 +476,7 @@ select_timeout:
}
/* Enable the device for receive */
-static void ene_rx_enable(struct ene_device *dev)
+static void ene_rx_enable_hw(struct ene_device *dev)
{
u8 reg_value;
@@ -503,11 +504,17 @@ static void ene_rx_enable(struct ene_device *dev)
/* enter idle mode */
ir_raw_event_set_idle(dev->rdev, true);
+}
+
+/* Enable the device for receive - wrapper to track the state*/
+static void ene_rx_enable(struct ene_device *dev)
+{
+ ene_rx_enable_hw(dev);
dev->rx_enabled = true;
}
/* Disable the device receiver */
-static void ene_rx_disable(struct ene_device *dev)
+static void ene_rx_disable_hw(struct ene_device *dev)
{
/* disable inputs */
ene_rx_enable_cir_engine(dev, false);
@@ -515,12 +522,17 @@ static void ene_rx_disable(struct ene_device *dev)
/* disable hardware IRQ and firmware flag */
ene_clear_reg_mask(dev, ENE_FW1, ENE_FW1_ENABLE | ENE_FW1_IRQ);
-
ir_raw_event_set_idle(dev->rdev, true);
+}
+
+/* Disable the device receiver - wrapper to track the state */
+static void ene_rx_disable(struct ene_device *dev)
+{
+ ene_rx_disable_hw(dev);
dev->rx_enabled = false;
}
-/* This resets the receiver. Usefull to stop stream of spaces at end of
+/* This resets the receiver. Useful to stop stream of spaces at end of
* transmission
*/
static void ene_rx_reset(struct ene_device *dev)
@@ -588,7 +600,7 @@ static void ene_tx_enable(struct ene_device *dev)
dbg("TX: Transmitter #2 is connected");
if (!(fwreg2 & (ENE_FW2_EMMITER1_CONN | ENE_FW2_EMMITER2_CONN)))
- ene_warn("TX: transmitter cable isn't connected!");
+ pr_warn("TX: transmitter cable isn't connected!\n");
/* disable receive on revc */
if (dev->hw_revision == ENE_HW_C)
@@ -615,7 +627,7 @@ static void ene_tx_sample(struct ene_device *dev)
bool pulse = dev->tx_sample_pulse;
if (!dev->tx_buffer) {
- ene_warn("TX: BUG: attempt to transmit NULL buffer");
+ pr_warn("TX: BUG: attempt to transmit NULL buffer\n");
return;
}
@@ -880,10 +892,13 @@ static int ene_set_tx_mask(struct rc_dev *rdev, u32 tx_mask)
static int ene_set_tx_carrier(struct rc_dev *rdev, u32 carrier)
{
struct ene_device *dev = rdev->priv;
- u32 period = 2000000 / carrier;
+ u32 period;
dbg("TX: attempt to set tx carrier to %d kHz", carrier);
+ if (carrier == 0)
+ return -EINVAL;
+ period = 2000000 / carrier;
if (period && (period > ENE_CIRMOD_PRD_MAX ||
period < ENE_CIRMOD_PRD_MIN)) {
@@ -953,13 +968,13 @@ static void ene_set_idle(struct rc_dev *rdev, bool idle)
}
/* outside interface: transmit */
-static int ene_transmit(struct rc_dev *rdev, int *buf, u32 n)
+static int ene_transmit(struct rc_dev *rdev, unsigned *buf, unsigned n)
{
struct ene_device *dev = rdev->priv;
unsigned long flags;
dev->tx_buffer = buf;
- dev->tx_len = n / sizeof(int);
+ dev->tx_len = n;
dev->tx_pos = 0;
dev->tx_reg = 0;
dev->tx_done = 0;
@@ -999,7 +1014,7 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
dev = kzalloc(sizeof(struct ene_device), GFP_KERNEL);
rdev = rc_allocate_device();
if (!dev || !rdev)
- goto error1;
+ goto exit_free_dev_rdev;
/* validate resources */
error = -ENODEV;
@@ -1010,28 +1025,16 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
if (!pnp_port_valid(pnp_dev, 0) ||
pnp_port_len(pnp_dev, 0) < ENE_IO_SIZE)
- goto error;
+ goto exit_free_dev_rdev;
if (!pnp_irq_valid(pnp_dev, 0))
- goto error;
+ goto exit_free_dev_rdev;
spin_lock_init(&dev->hw_lock);
- /* claim the resources */
- error = -EBUSY;
dev->hw_io = pnp_port_start(pnp_dev, 0);
- if (!request_region(dev->hw_io, ENE_IO_SIZE, ENE_DRIVER_NAME)) {
- dev->hw_io = -1;
- dev->irq = -1;
- goto error;
- }
-
dev->irq = pnp_irq(pnp_dev, 0);
- if (request_irq(dev->irq, ene_isr,
- IRQF_SHARED, ENE_DRIVER_NAME, (void *)dev)) {
- dev->irq = -1;
- goto error;
- }
+
pnp_set_drvdata(pnp_dev, dev);
dev->pnp_dev = pnp_dev;
@@ -1043,20 +1046,20 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
/* detect hardware version and features */
error = ene_hw_detect(dev);
if (error)
- goto error;
+ goto exit_free_dev_rdev;
if (!dev->hw_learning_and_tx_capable && txsim) {
dev->hw_learning_and_tx_capable = true;
setup_timer(&dev->tx_sim_timer, ene_tx_irqsim,
(long unsigned int)dev);
- ene_warn("Simulation of TX activated");
+ pr_warn("Simulation of TX activated\n");
}
if (!dev->hw_learning_and_tx_capable)
learning_mode_force = false;
rdev->driver_type = RC_DRIVER_IR_RAW;
- rdev->allowed_protos = RC_TYPE_ALL;
+ rc_set_allowed_protocols(rdev, RC_BIT_ALL);
rdev->priv = dev;
rdev->open = ene_open;
rdev->close = ene_close;
@@ -1087,16 +1090,28 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
error = rc_register_device(rdev);
if (error < 0)
- goto error;
+ goto exit_free_dev_rdev;
+
+ /* claim the resources */
+ error = -EBUSY;
+ if (!request_region(dev->hw_io, ENE_IO_SIZE, ENE_DRIVER_NAME)) {
+ goto exit_unregister_device;
+ }
+
+ if (request_irq(dev->irq, ene_isr,
+ IRQF_SHARED, ENE_DRIVER_NAME, (void *)dev)) {
+ goto exit_release_hw_io;
+ }
- ene_notice("driver has been succesfully loaded");
+ pr_notice("driver has been successfully loaded\n");
return 0;
-error:
- if (dev && dev->irq >= 0)
- free_irq(dev->irq, dev);
- if (dev && dev->hw_io >= 0)
- release_region(dev->hw_io, ENE_IO_SIZE);
-error1:
+
+exit_release_hw_io:
+ release_region(dev->hw_io, ENE_IO_SIZE);
+exit_unregister_device:
+ rc_unregister_device(rdev);
+ rdev = NULL;
+exit_free_dev_rdev:
rc_free_device(rdev);
kfree(dev);
return error;
@@ -1120,9 +1135,8 @@ static void ene_remove(struct pnp_dev *pnp_dev)
}
/* enable wake on IR (wakes on specific button on original remote) */
-static void ene_enable_wake(struct ene_device *dev, int enable)
+static void ene_enable_wake(struct ene_device *dev, bool enable)
{
- enable = enable && device_may_wakeup(&dev->pnp_dev->dev);
dbg("wake on IR %s", enable ? "enabled" : "disabled");
ene_set_clear_reg_mask(dev, ENE_FW1, ENE_FW1_WAKE, enable);
}
@@ -1131,9 +1145,12 @@ static void ene_enable_wake(struct ene_device *dev, int enable)
static int ene_suspend(struct pnp_dev *pnp_dev, pm_message_t state)
{
struct ene_device *dev = pnp_get_drvdata(pnp_dev);
- ene_enable_wake(dev, true);
+ bool wake = device_may_wakeup(&dev->pnp_dev->dev);
+
+ if (!wake && dev->rx_enabled)
+ ene_rx_disable_hw(dev);
- /* TODO: add support for wake pattern */
+ ene_enable_wake(dev, wake);
return 0;
}
@@ -1170,7 +1187,7 @@ static struct pnp_driver ene_driver = {
.flags = PNP_DRIVER_RES_DO_NOT_CHANGE,
.probe = ene_probe,
- .remove = __devexit_p(ene_remove),
+ .remove = ene_remove,
#ifdef CONFIG_PM
.suspend = ene_suspend,
.resume = ene_resume,
diff --git a/drivers/media/rc/ene_ir.h b/drivers/media/rc/ene_ir.h
index 337a41d4450..a7911e3b9bc 100644
--- a/drivers/media/rc/ene_ir.h
+++ b/drivers/media/rc/ene_ir.h
@@ -182,20 +182,11 @@
#define ENE_HW_C 2 /* 3926C */
#define ENE_HW_D 3 /* 3926D or later */
-#define ene_printk(level, text, ...) \
- printk(level ENE_DRIVER_NAME ": " text "\n", ## __VA_ARGS__)
-
-#define ene_notice(text, ...) ene_printk(KERN_NOTICE, text, ## __VA_ARGS__)
-#define ene_warn(text, ...) ene_printk(KERN_WARNING, text, ## __VA_ARGS__)
-
-
-#define __dbg(level, format, ...) \
- do { \
- if (debug >= level) \
- printk(KERN_DEBUG ENE_DRIVER_NAME \
- ": " format "\n", ## __VA_ARGS__); \
- } while (0)
-
+#define __dbg(level, format, ...) \
+do { \
+ if (debug >= level) \
+ pr_info(format "\n", ## __VA_ARGS__); \
+} while (0)
#define dbg(format, ...) __dbg(1, format, ## __VA_ARGS__)
#define dbg_verbose(format, ...) __dbg(2, format, ## __VA_ARGS__)
@@ -235,8 +226,8 @@ struct ene_device {
bool tx_sample_pulse; /* current sample is pulse */
/* TX buffer */
- int *tx_buffer; /* input samples buffer*/
- int tx_pos; /* position in that bufer */
+ unsigned *tx_buffer; /* input samples buffer*/
+ int tx_pos; /* position in that buffer */
int tx_len; /* current len of tx buffer */
int tx_done; /* done transmitting */
/* one more sample pending*/
diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c
new file mode 100644
index 00000000000..46b66e59438
--- /dev/null
+++ b/drivers/media/rc/fintek-cir.c
@@ -0,0 +1,709 @@
+/*
+ * Driver for Feature Integration Technology Inc. (aka Fintek) LPC CIR
+ *
+ * Copyright (C) 2011 Jarod Wilson <jarod@redhat.com>
+ *
+ * Special thanks to Fintek for providing hardware and spec sheets.
+ * This driver is based upon the nuvoton, ite and ene drivers for
+ * similar hardware.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pnp.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <media/rc-core.h>
+#include <linux/pci_ids.h>
+
+#include "fintek-cir.h"
+
+/* write val to config reg */
+static inline void fintek_cr_write(struct fintek_dev *fintek, u8 val, u8 reg)
+{
+ fit_dbg("%s: reg 0x%02x, val 0x%02x (ip/dp: %02x/%02x)",
+ __func__, reg, val, fintek->cr_ip, fintek->cr_dp);
+ outb(reg, fintek->cr_ip);
+ outb(val, fintek->cr_dp);
+}
+
+/* read val from config reg */
+static inline u8 fintek_cr_read(struct fintek_dev *fintek, u8 reg)
+{
+ u8 val;
+
+ outb(reg, fintek->cr_ip);
+ val = inb(fintek->cr_dp);
+
+ fit_dbg("%s: reg 0x%02x, val 0x%02x (ip/dp: %02x/%02x)",
+ __func__, reg, val, fintek->cr_ip, fintek->cr_dp);
+ return val;
+}
+
+/* update config register bit without changing other bits */
+static inline void fintek_set_reg_bit(struct fintek_dev *fintek, u8 val, u8 reg)
+{
+ u8 tmp = fintek_cr_read(fintek, reg) | val;
+ fintek_cr_write(fintek, tmp, reg);
+}
+
+/* clear config register bit without changing other bits */
+static inline void fintek_clear_reg_bit(struct fintek_dev *fintek, u8 val, u8 reg)
+{
+ u8 tmp = fintek_cr_read(fintek, reg) & ~val;
+ fintek_cr_write(fintek, tmp, reg);
+}
+
+/* enter config mode */
+static inline void fintek_config_mode_enable(struct fintek_dev *fintek)
+{
+ /* Enabling Config Mode explicitly requires writing 2x */
+ outb(CONFIG_REG_ENABLE, fintek->cr_ip);
+ outb(CONFIG_REG_ENABLE, fintek->cr_ip);
+}
+
+/* exit config mode */
+static inline void fintek_config_mode_disable(struct fintek_dev *fintek)
+{
+ outb(CONFIG_REG_DISABLE, fintek->cr_ip);
+}
+
+/*
+ * When you want to address a specific logical device, write its logical
+ * device number to GCR_LOGICAL_DEV_NO
+ */
+static inline void fintek_select_logical_dev(struct fintek_dev *fintek, u8 ldev)
+{
+ fintek_cr_write(fintek, ldev, GCR_LOGICAL_DEV_NO);
+}
+
+/* write val to cir config register */
+static inline void fintek_cir_reg_write(struct fintek_dev *fintek, u8 val, u8 offset)
+{
+ outb(val, fintek->cir_addr + offset);
+}
+
+/* read val from cir config register */
+static u8 fintek_cir_reg_read(struct fintek_dev *fintek, u8 offset)
+{
+ u8 val;
+
+ val = inb(fintek->cir_addr + offset);
+
+ return val;
+}
+
+/* dump current cir register contents */
+static void cir_dump_regs(struct fintek_dev *fintek)
+{
+ fintek_config_mode_enable(fintek);
+ fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
+
+ pr_info("%s: Dump CIR logical device registers:\n", FINTEK_DRIVER_NAME);
+ pr_info(" * CR CIR BASE ADDR: 0x%x\n",
+ (fintek_cr_read(fintek, CIR_CR_BASE_ADDR_HI) << 8) |
+ fintek_cr_read(fintek, CIR_CR_BASE_ADDR_LO));
+ pr_info(" * CR CIR IRQ NUM: 0x%x\n",
+ fintek_cr_read(fintek, CIR_CR_IRQ_SEL));
+
+ fintek_config_mode_disable(fintek);
+
+ pr_info("%s: Dump CIR registers:\n", FINTEK_DRIVER_NAME);
+ pr_info(" * STATUS: 0x%x\n",
+ fintek_cir_reg_read(fintek, CIR_STATUS));
+ pr_info(" * CONTROL: 0x%x\n",
+ fintek_cir_reg_read(fintek, CIR_CONTROL));
+ pr_info(" * RX_DATA: 0x%x\n",
+ fintek_cir_reg_read(fintek, CIR_RX_DATA));
+ pr_info(" * TX_CONTROL: 0x%x\n",
+ fintek_cir_reg_read(fintek, CIR_TX_CONTROL));
+ pr_info(" * TX_DATA: 0x%x\n",
+ fintek_cir_reg_read(fintek, CIR_TX_DATA));
+}
+
+/* detect hardware features */
+static int fintek_hw_detect(struct fintek_dev *fintek)
+{
+ unsigned long flags;
+ u8 chip_major, chip_minor;
+ u8 vendor_major, vendor_minor;
+ u8 portsel, ir_class;
+ u16 vendor, chip;
+ int ret = 0;
+
+ fintek_config_mode_enable(fintek);
+
+ /* Check if we're using config port 0x4e or 0x2e */
+ portsel = fintek_cr_read(fintek, GCR_CONFIG_PORT_SEL);
+ if (portsel == 0xff) {
+ fit_pr(KERN_INFO, "first portsel read was bunk, trying alt");
+ fintek_config_mode_disable(fintek);
+ fintek->cr_ip = CR_INDEX_PORT2;
+ fintek->cr_dp = CR_DATA_PORT2;
+ fintek_config_mode_enable(fintek);
+ portsel = fintek_cr_read(fintek, GCR_CONFIG_PORT_SEL);
+ }
+ fit_dbg("portsel reg: 0x%02x", portsel);
+
+ ir_class = fintek_cir_reg_read(fintek, CIR_CR_CLASS);
+ fit_dbg("ir_class reg: 0x%02x", ir_class);
+
+ switch (ir_class) {
+ case CLASS_RX_2TX:
+ case CLASS_RX_1TX:
+ fintek->hw_tx_capable = true;
+ break;
+ case CLASS_RX_ONLY:
+ default:
+ fintek->hw_tx_capable = false;
+ break;
+ }
+
+ chip_major = fintek_cr_read(fintek, GCR_CHIP_ID_HI);
+ chip_minor = fintek_cr_read(fintek, GCR_CHIP_ID_LO);
+ chip = chip_major << 8 | chip_minor;
+
+ vendor_major = fintek_cr_read(fintek, GCR_VENDOR_ID_HI);
+ vendor_minor = fintek_cr_read(fintek, GCR_VENDOR_ID_LO);
+ vendor = vendor_major << 8 | vendor_minor;
+
+ if (vendor != VENDOR_ID_FINTEK)
+ fit_pr(KERN_WARNING, "Unknown vendor ID: 0x%04x", vendor);
+ else
+ fit_dbg("Read Fintek vendor ID from chip");
+
+ fintek_config_mode_disable(fintek);
+
+ spin_lock_irqsave(&fintek->fintek_lock, flags);
+ fintek->chip_major = chip_major;
+ fintek->chip_minor = chip_minor;
+ fintek->chip_vendor = vendor;
+
+ /*
+ * Newer reviews of this chipset uses port 8 instead of 5
+ */
+ if ((chip != 0x0408) && (chip != 0x0804))
+ fintek->logical_dev_cir = LOGICAL_DEV_CIR_REV2;
+ else
+ fintek->logical_dev_cir = LOGICAL_DEV_CIR_REV1;
+
+ spin_unlock_irqrestore(&fintek->fintek_lock, flags);
+
+ return ret;
+}
+
+static void fintek_cir_ldev_init(struct fintek_dev *fintek)
+{
+ /* Select CIR logical device and enable */
+ fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
+ fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN);
+
+ /* Write allocated CIR address and IRQ information to hardware */
+ fintek_cr_write(fintek, fintek->cir_addr >> 8, CIR_CR_BASE_ADDR_HI);
+ fintek_cr_write(fintek, fintek->cir_addr & 0xff, CIR_CR_BASE_ADDR_LO);
+
+ fintek_cr_write(fintek, fintek->cir_irq, CIR_CR_IRQ_SEL);
+
+ fit_dbg("CIR initialized, base io address: 0x%lx, irq: %d (len: %d)",
+ fintek->cir_addr, fintek->cir_irq, fintek->cir_port_len);
+}
+
+/* enable CIR interrupts */
+static void fintek_enable_cir_irq(struct fintek_dev *fintek)
+{
+ fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_EN, CIR_STATUS);
+}
+
+static void fintek_cir_regs_init(struct fintek_dev *fintek)
+{
+ /* clear any and all stray interrupts */
+ fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS);
+
+ /* and finally, enable interrupts */
+ fintek_enable_cir_irq(fintek);
+}
+
+static void fintek_enable_wake(struct fintek_dev *fintek)
+{
+ fintek_config_mode_enable(fintek);
+ fintek_select_logical_dev(fintek, LOGICAL_DEV_ACPI);
+
+ /* Allow CIR PME's to wake system */
+ fintek_set_reg_bit(fintek, ACPI_WAKE_EN_CIR_BIT, LDEV_ACPI_WAKE_EN_REG);
+ /* Enable CIR PME's */
+ fintek_set_reg_bit(fintek, ACPI_PME_CIR_BIT, LDEV_ACPI_PME_EN_REG);
+ /* Clear CIR PME status register */
+ fintek_set_reg_bit(fintek, ACPI_PME_CIR_BIT, LDEV_ACPI_PME_CLR_REG);
+ /* Save state */
+ fintek_set_reg_bit(fintek, ACPI_STATE_CIR_BIT, LDEV_ACPI_STATE_REG);
+
+ fintek_config_mode_disable(fintek);
+}
+
+static int fintek_cmdsize(u8 cmd, u8 subcmd)
+{
+ int datasize = 0;
+
+ switch (cmd) {
+ case BUF_COMMAND_NULL:
+ if (subcmd == BUF_HW_CMD_HEADER)
+ datasize = 1;
+ break;
+ case BUF_HW_CMD_HEADER:
+ if (subcmd == BUF_CMD_G_REVISION)
+ datasize = 2;
+ break;
+ case BUF_COMMAND_HEADER:
+ switch (subcmd) {
+ case BUF_CMD_S_CARRIER:
+ case BUF_CMD_S_TIMEOUT:
+ case BUF_RSP_PULSE_COUNT:
+ datasize = 2;
+ break;
+ case BUF_CMD_SIG_END:
+ case BUF_CMD_S_TXMASK:
+ case BUF_CMD_S_RXSENSOR:
+ datasize = 1;
+ break;
+ }
+ }
+
+ return datasize;
+}
+
+/* process ir data stored in driver buffer */
+static void fintek_process_rx_ir_data(struct fintek_dev *fintek)
+{
+ DEFINE_IR_RAW_EVENT(rawir);
+ u8 sample;
+ bool event = false;
+ int i;
+
+ for (i = 0; i < fintek->pkts; i++) {
+ sample = fintek->buf[i];
+ switch (fintek->parser_state) {
+ case CMD_HEADER:
+ fintek->cmd = sample;
+ if ((fintek->cmd == BUF_COMMAND_HEADER) ||
+ ((fintek->cmd & BUF_COMMAND_MASK) !=
+ BUF_PULSE_BIT)) {
+ fintek->parser_state = SUBCMD;
+ continue;
+ }
+ fintek->rem = (fintek->cmd & BUF_LEN_MASK);
+ fit_dbg("%s: rem: 0x%02x", __func__, fintek->rem);
+ if (fintek->rem)
+ fintek->parser_state = PARSE_IRDATA;
+ else
+ ir_raw_event_reset(fintek->rdev);
+ break;
+ case SUBCMD:
+ fintek->rem = fintek_cmdsize(fintek->cmd, sample);
+ fintek->parser_state = CMD_DATA;
+ break;
+ case CMD_DATA:
+ fintek->rem--;
+ break;
+ case PARSE_IRDATA:
+ fintek->rem--;
+ init_ir_raw_event(&rawir);
+ rawir.pulse = ((sample & BUF_PULSE_BIT) != 0);
+ rawir.duration = US_TO_NS((sample & BUF_SAMPLE_MASK)
+ * CIR_SAMPLE_PERIOD);
+
+ fit_dbg("Storing %s with duration %d",
+ rawir.pulse ? "pulse" : "space",
+ rawir.duration);
+ if (ir_raw_event_store_with_filter(fintek->rdev,
+ &rawir))
+ event = true;
+ break;
+ }
+
+ if ((fintek->parser_state != CMD_HEADER) && !fintek->rem)
+ fintek->parser_state = CMD_HEADER;
+ }
+
+ fintek->pkts = 0;
+
+ if (event) {
+ fit_dbg("Calling ir_raw_event_handle");
+ ir_raw_event_handle(fintek->rdev);
+ }
+}
+
+/* copy data from hardware rx register into driver buffer */
+static void fintek_get_rx_ir_data(struct fintek_dev *fintek, u8 rx_irqs)
+{
+ unsigned long flags;
+ u8 sample, status;
+
+ spin_lock_irqsave(&fintek->fintek_lock, flags);
+
+ /*
+ * We must read data from CIR_RX_DATA until the hardware IR buffer
+ * is empty and clears the RX_TIMEOUT and/or RX_RECEIVE flags in
+ * the CIR_STATUS register
+ */
+ do {
+ sample = fintek_cir_reg_read(fintek, CIR_RX_DATA);
+ fit_dbg("%s: sample: 0x%02x", __func__, sample);
+
+ fintek->buf[fintek->pkts] = sample;
+ fintek->pkts++;
+
+ status = fintek_cir_reg_read(fintek, CIR_STATUS);
+ if (!(status & CIR_STATUS_IRQ_EN))
+ break;
+ } while (status & rx_irqs);
+
+ fintek_process_rx_ir_data(fintek);
+
+ spin_unlock_irqrestore(&fintek->fintek_lock, flags);
+}
+
+static void fintek_cir_log_irqs(u8 status)
+{
+ fit_pr(KERN_INFO, "IRQ 0x%02x:%s%s%s%s%s", status,
+ status & CIR_STATUS_IRQ_EN ? " IRQEN" : "",
+ status & CIR_STATUS_TX_FINISH ? " TXF" : "",
+ status & CIR_STATUS_TX_UNDERRUN ? " TXU" : "",
+ status & CIR_STATUS_RX_TIMEOUT ? " RXTO" : "",
+ status & CIR_STATUS_RX_RECEIVE ? " RXOK" : "");
+}
+
+/* interrupt service routine for incoming and outgoing CIR data */
+static irqreturn_t fintek_cir_isr(int irq, void *data)
+{
+ struct fintek_dev *fintek = data;
+ u8 status, rx_irqs;
+
+ fit_dbg_verbose("%s firing", __func__);
+
+ fintek_config_mode_enable(fintek);
+ fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
+ fintek_config_mode_disable(fintek);
+
+ /*
+ * Get IR Status register contents. Write 1 to ack/clear
+ *
+ * bit: reg name - description
+ * 3: TX_FINISH - TX is finished
+ * 2: TX_UNDERRUN - TX underrun
+ * 1: RX_TIMEOUT - RX data timeout
+ * 0: RX_RECEIVE - RX data received
+ */
+ status = fintek_cir_reg_read(fintek, CIR_STATUS);
+ if (!(status & CIR_STATUS_IRQ_MASK) || status == 0xff) {
+ fit_dbg_verbose("%s exiting, IRSTS 0x%02x", __func__, status);
+ fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS);
+ return IRQ_RETVAL(IRQ_NONE);
+ }
+
+ if (debug)
+ fintek_cir_log_irqs(status);
+
+ rx_irqs = status & (CIR_STATUS_RX_RECEIVE | CIR_STATUS_RX_TIMEOUT);
+ if (rx_irqs)
+ fintek_get_rx_ir_data(fintek, rx_irqs);
+
+ /* ack/clear all irq flags we've got */
+ fintek_cir_reg_write(fintek, status, CIR_STATUS);
+
+ fit_dbg_verbose("%s done", __func__);
+ return IRQ_RETVAL(IRQ_HANDLED);
+}
+
+static void fintek_enable_cir(struct fintek_dev *fintek)
+{
+ /* set IRQ enabled */
+ fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_EN, CIR_STATUS);
+
+ fintek_config_mode_enable(fintek);
+
+ /* enable the CIR logical device */
+ fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
+ fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN);
+
+ fintek_config_mode_disable(fintek);
+
+ /* clear all pending interrupts */
+ fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS);
+
+ /* enable interrupts */
+ fintek_enable_cir_irq(fintek);
+}
+
+static void fintek_disable_cir(struct fintek_dev *fintek)
+{
+ fintek_config_mode_enable(fintek);
+
+ /* disable the CIR logical device */
+ fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
+ fintek_cr_write(fintek, LOGICAL_DEV_DISABLE, CIR_CR_DEV_EN);
+
+ fintek_config_mode_disable(fintek);
+}
+
+static int fintek_open(struct rc_dev *dev)
+{
+ struct fintek_dev *fintek = dev->priv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fintek->fintek_lock, flags);
+ fintek_enable_cir(fintek);
+ spin_unlock_irqrestore(&fintek->fintek_lock, flags);
+
+ return 0;
+}
+
+static void fintek_close(struct rc_dev *dev)
+{
+ struct fintek_dev *fintek = dev->priv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fintek->fintek_lock, flags);
+ fintek_disable_cir(fintek);
+ spin_unlock_irqrestore(&fintek->fintek_lock, flags);
+}
+
+/* Allocate memory, probe hardware, and initialize everything */
+static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
+{
+ struct fintek_dev *fintek;
+ struct rc_dev *rdev;
+ int ret = -ENOMEM;
+
+ fintek = kzalloc(sizeof(struct fintek_dev), GFP_KERNEL);
+ if (!fintek)
+ return ret;
+
+ /* input device for IR remote (and tx) */
+ rdev = rc_allocate_device();
+ if (!rdev)
+ goto exit_free_dev_rdev;
+
+ ret = -ENODEV;
+ /* validate pnp resources */
+ if (!pnp_port_valid(pdev, 0)) {
+ dev_err(&pdev->dev, "IR PNP Port not valid!\n");
+ goto exit_free_dev_rdev;
+ }
+
+ if (!pnp_irq_valid(pdev, 0)) {
+ dev_err(&pdev->dev, "IR PNP IRQ not valid!\n");
+ goto exit_free_dev_rdev;
+ }
+
+ fintek->cir_addr = pnp_port_start(pdev, 0);
+ fintek->cir_irq = pnp_irq(pdev, 0);
+ fintek->cir_port_len = pnp_port_len(pdev, 0);
+
+ fintek->cr_ip = CR_INDEX_PORT;
+ fintek->cr_dp = CR_DATA_PORT;
+
+ spin_lock_init(&fintek->fintek_lock);
+
+ pnp_set_drvdata(pdev, fintek);
+ fintek->pdev = pdev;
+
+ ret = fintek_hw_detect(fintek);
+ if (ret)
+ goto exit_free_dev_rdev;
+
+ /* Initialize CIR & CIR Wake Logical Devices */
+ fintek_config_mode_enable(fintek);
+ fintek_cir_ldev_init(fintek);
+ fintek_config_mode_disable(fintek);
+
+ /* Initialize CIR & CIR Wake Config Registers */
+ fintek_cir_regs_init(fintek);
+
+ /* Set up the rc device */
+ rdev->priv = fintek;
+ rdev->driver_type = RC_DRIVER_IR_RAW;
+ rc_set_allowed_protocols(rdev, RC_BIT_ALL);
+ rdev->open = fintek_open;
+ rdev->close = fintek_close;
+ rdev->input_name = FINTEK_DESCRIPTION;
+ rdev->input_phys = "fintek/cir0";
+ rdev->input_id.bustype = BUS_HOST;
+ rdev->input_id.vendor = VENDOR_ID_FINTEK;
+ rdev->input_id.product = fintek->chip_major;
+ rdev->input_id.version = fintek->chip_minor;
+ rdev->dev.parent = &pdev->dev;
+ rdev->driver_name = FINTEK_DRIVER_NAME;
+ rdev->map_name = RC_MAP_RC6_MCE;
+ rdev->timeout = US_TO_NS(1000);
+ /* rx resolution is hardwired to 50us atm, 1, 25, 100 also possible */
+ rdev->rx_resolution = US_TO_NS(CIR_SAMPLE_PERIOD);
+
+ fintek->rdev = rdev;
+
+ ret = -EBUSY;
+ /* now claim resources */
+ if (!request_region(fintek->cir_addr,
+ fintek->cir_port_len, FINTEK_DRIVER_NAME))
+ goto exit_free_dev_rdev;
+
+ if (request_irq(fintek->cir_irq, fintek_cir_isr, IRQF_SHARED,
+ FINTEK_DRIVER_NAME, (void *)fintek))
+ goto exit_free_cir_addr;
+
+ ret = rc_register_device(rdev);
+ if (ret)
+ goto exit_free_irq;
+
+ device_init_wakeup(&pdev->dev, true);
+
+ fit_pr(KERN_NOTICE, "driver has been successfully loaded\n");
+ if (debug)
+ cir_dump_regs(fintek);
+
+ return 0;
+
+exit_free_irq:
+ free_irq(fintek->cir_irq, fintek);
+exit_free_cir_addr:
+ release_region(fintek->cir_addr, fintek->cir_port_len);
+exit_free_dev_rdev:
+ rc_free_device(rdev);
+ kfree(fintek);
+
+ return ret;
+}
+
+static void fintek_remove(struct pnp_dev *pdev)
+{
+ struct fintek_dev *fintek = pnp_get_drvdata(pdev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&fintek->fintek_lock, flags);
+ /* disable CIR */
+ fintek_disable_cir(fintek);
+ fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS);
+ /* enable CIR Wake (for IR power-on) */
+ fintek_enable_wake(fintek);
+ spin_unlock_irqrestore(&fintek->fintek_lock, flags);
+
+ /* free resources */
+ free_irq(fintek->cir_irq, fintek);
+ release_region(fintek->cir_addr, fintek->cir_port_len);
+
+ rc_unregister_device(fintek->rdev);
+
+ kfree(fintek);
+}
+
+static int fintek_suspend(struct pnp_dev *pdev, pm_message_t state)
+{
+ struct fintek_dev *fintek = pnp_get_drvdata(pdev);
+ unsigned long flags;
+
+ fit_dbg("%s called", __func__);
+
+ spin_lock_irqsave(&fintek->fintek_lock, flags);
+
+ /* disable all CIR interrupts */
+ fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS);
+
+ spin_unlock_irqrestore(&fintek->fintek_lock, flags);
+
+ fintek_config_mode_enable(fintek);
+
+ /* disable cir logical dev */
+ fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
+ fintek_cr_write(fintek, LOGICAL_DEV_DISABLE, CIR_CR_DEV_EN);
+
+ fintek_config_mode_disable(fintek);
+
+ /* make sure wake is enabled */
+ fintek_enable_wake(fintek);
+
+ return 0;
+}
+
+static int fintek_resume(struct pnp_dev *pdev)
+{
+ int ret = 0;
+ struct fintek_dev *fintek = pnp_get_drvdata(pdev);
+
+ fit_dbg("%s called", __func__);
+
+ /* open interrupt */
+ fintek_enable_cir_irq(fintek);
+
+ /* Enable CIR logical device */
+ fintek_config_mode_enable(fintek);
+ fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
+ fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN);
+
+ fintek_config_mode_disable(fintek);
+
+ fintek_cir_regs_init(fintek);
+
+ return ret;
+}
+
+static void fintek_shutdown(struct pnp_dev *pdev)
+{
+ struct fintek_dev *fintek = pnp_get_drvdata(pdev);
+ fintek_enable_wake(fintek);
+}
+
+static const struct pnp_device_id fintek_ids[] = {
+ { "FIT0002", 0 }, /* CIR */
+ { "", 0 },
+};
+
+static struct pnp_driver fintek_driver = {
+ .name = FINTEK_DRIVER_NAME,
+ .id_table = fintek_ids,
+ .flags = PNP_DRIVER_RES_DO_NOT_CHANGE,
+ .probe = fintek_probe,
+ .remove = fintek_remove,
+ .suspend = fintek_suspend,
+ .resume = fintek_resume,
+ .shutdown = fintek_shutdown,
+};
+
+static int fintek_init(void)
+{
+ return pnp_register_driver(&fintek_driver);
+}
+
+static void fintek_exit(void)
+{
+ pnp_unregister_driver(&fintek_driver);
+}
+
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Enable debugging output");
+
+MODULE_DEVICE_TABLE(pnp, fintek_ids);
+MODULE_DESCRIPTION(FINTEK_DESCRIPTION " driver");
+
+MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
+MODULE_LICENSE("GPL");
+
+module_init(fintek_init);
+module_exit(fintek_exit);
diff --git a/drivers/media/rc/fintek-cir.h b/drivers/media/rc/fintek-cir.h
new file mode 100644
index 00000000000..b698f3d2ced
--- /dev/null
+++ b/drivers/media/rc/fintek-cir.h
@@ -0,0 +1,245 @@
+/*
+ * Driver for Feature Integration Technology Inc. (aka Fintek) LPC CIR
+ *
+ * Copyright (C) 2011 Jarod Wilson <jarod@redhat.com>
+ *
+ * Special thanks to Fintek for providing hardware and spec sheets.
+ * This driver is based upon the nuvoton, ite and ene drivers for
+ * similar hardware.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <linux/spinlock.h>
+#include <linux/ioctl.h>
+
+/* platform driver name to register */
+#define FINTEK_DRIVER_NAME "fintek-cir"
+#define FINTEK_DESCRIPTION "Fintek LPC SuperIO Consumer IR Transceiver"
+#define VENDOR_ID_FINTEK 0x1934
+
+
+/* debugging module parameter */
+static int debug;
+
+#define fit_pr(level, text, ...) \
+ printk(level KBUILD_MODNAME ": " text, ## __VA_ARGS__)
+
+#define fit_dbg(text, ...) \
+ if (debug) \
+ printk(KERN_DEBUG \
+ KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__)
+
+#define fit_dbg_verbose(text, ...) \
+ if (debug > 1) \
+ printk(KERN_DEBUG \
+ KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__)
+
+#define fit_dbg_wake(text, ...) \
+ if (debug > 2) \
+ printk(KERN_DEBUG \
+ KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__)
+
+
+#define TX_BUF_LEN 256
+#define RX_BUF_LEN 32
+
+struct fintek_dev {
+ struct pnp_dev *pdev;
+ struct rc_dev *rdev;
+
+ spinlock_t fintek_lock;
+
+ /* for rx */
+ u8 buf[RX_BUF_LEN];
+ unsigned int pkts;
+
+ struct {
+ spinlock_t lock;
+ u8 buf[TX_BUF_LEN];
+ unsigned int buf_count;
+ unsigned int cur_buf_num;
+ wait_queue_head_t queue;
+ } tx;
+
+ /* Config register index/data port pair */
+ u32 cr_ip;
+ u32 cr_dp;
+
+ /* hardware I/O settings */
+ unsigned long cir_addr;
+ int cir_irq;
+ int cir_port_len;
+
+ /* hardware id */
+ u8 chip_major;
+ u8 chip_minor;
+ u16 chip_vendor;
+ u8 logical_dev_cir;
+
+ /* hardware features */
+ bool hw_learning_capable;
+ bool hw_tx_capable;
+
+ /* rx settings */
+ bool learning_enabled;
+ bool carrier_detect_enabled;
+
+ enum {
+ CMD_HEADER = 0,
+ SUBCMD,
+ CMD_DATA,
+ PARSE_IRDATA,
+ } parser_state;
+
+ u8 cmd, rem;
+
+ /* carrier period = 1 / frequency */
+ u32 carrier;
+};
+
+/* buffer packet constants, largely identical to mceusb.c */
+#define BUF_PULSE_BIT 0x80
+#define BUF_LEN_MASK 0x1f
+#define BUF_SAMPLE_MASK 0x7f
+
+#define BUF_COMMAND_HEADER 0x9f
+#define BUF_COMMAND_MASK 0xe0
+#define BUF_COMMAND_NULL 0x00
+#define BUF_HW_CMD_HEADER 0xff
+#define BUF_CMD_G_REVISION 0x0b
+#define BUF_CMD_S_CARRIER 0x06
+#define BUF_CMD_S_TIMEOUT 0x0c
+#define BUF_CMD_SIG_END 0x01
+#define BUF_CMD_S_TXMASK 0x08
+#define BUF_CMD_S_RXSENSOR 0x14
+#define BUF_RSP_PULSE_COUNT 0x15
+
+#define CIR_SAMPLE_PERIOD 50
+
+/*
+ * Configuration Register:
+ * Index Port
+ * Data Port
+ */
+#define CR_INDEX_PORT 0x2e
+#define CR_DATA_PORT 0x2f
+
+/* Possible alternate values, depends on how the chip is wired */
+#define CR_INDEX_PORT2 0x4e
+#define CR_DATA_PORT2 0x4f
+
+/*
+ * GCR_CONFIG_PORT_SEL bit 4 specifies which Index Port value is
+ * active. 1 = 0x4e, 0 = 0x2e
+ */
+#define PORT_SEL_PORT_4E_EN 0x10
+
+/* Extended Function Mode enable/disable magic values */
+#define CONFIG_REG_ENABLE 0x87
+#define CONFIG_REG_DISABLE 0xaa
+
+/* Chip IDs found in CR_CHIP_ID_{HI,LO} */
+#define CHIP_ID_HIGH_F71809U 0x04
+#define CHIP_ID_LOW_F71809U 0x08
+
+/*
+ * Global control regs we need to care about:
+ * Global Control def.
+ * Register name addr val. */
+#define GCR_SOFTWARE_RESET 0x02 /* 0x00 */
+#define GCR_LOGICAL_DEV_NO 0x07 /* 0x00 */
+#define GCR_CHIP_ID_HI 0x20 /* 0x04 */
+#define GCR_CHIP_ID_LO 0x21 /* 0x08 */
+#define GCR_VENDOR_ID_HI 0x23 /* 0x19 */
+#define GCR_VENDOR_ID_LO 0x24 /* 0x34 */
+#define GCR_CONFIG_PORT_SEL 0x25 /* 0x01 */
+#define GCR_KBMOUSE_WAKEUP 0x27
+
+#define LOGICAL_DEV_DISABLE 0x00
+#define LOGICAL_DEV_ENABLE 0x01
+
+/* Logical device number of the CIR function */
+#define LOGICAL_DEV_CIR_REV1 0x05
+#define LOGICAL_DEV_CIR_REV2 0x08
+
+/* CIR Logical Device (LDN 0x08) config registers */
+#define CIR_CR_COMMAND_INDEX 0x04
+#define CIR_CR_IRCS 0x05 /* Before host writes command to IR, host
+ must set to 1. When host finshes write
+ command to IR, host must clear to 0. */
+#define CIR_CR_COMMAND_DATA 0x06 /* Host read or write comand data */
+#define CIR_CR_CLASS 0x07 /* 0xff = rx-only, 0x66 = rx + 2 tx,
+ 0x33 = rx + 1 tx */
+#define CIR_CR_DEV_EN 0x30 /* bit0 = 1 enables CIR */
+#define CIR_CR_BASE_ADDR_HI 0x60 /* MSB of CIR IO base addr */
+#define CIR_CR_BASE_ADDR_LO 0x61 /* LSB of CIR IO base addr */
+#define CIR_CR_IRQ_SEL 0x70 /* bits3-0 store CIR IRQ */
+#define CIR_CR_PSOUT_STATUS 0xf1
+#define CIR_CR_WAKE_KEY3_ADDR 0xf8
+#define CIR_CR_WAKE_KEY3_CODE 0xf9
+#define CIR_CR_WAKE_KEY3_DC 0xfa
+#define CIR_CR_WAKE_CONTROL 0xfb
+#define CIR_CR_WAKE_KEY12_ADDR 0xfc
+#define CIR_CR_WAKE_KEY4_ADDR 0xfd
+#define CIR_CR_WAKE_KEY5_ADDR 0xfe
+
+#define CLASS_RX_ONLY 0xff
+#define CLASS_RX_2TX 0x66
+#define CLASS_RX_1TX 0x33
+
+/* CIR device registers */
+#define CIR_STATUS 0x00
+#define CIR_RX_DATA 0x01
+#define CIR_TX_CONTROL 0x02
+#define CIR_TX_DATA 0x03
+#define CIR_CONTROL 0x04
+
+/* Bits to enable CIR wake */
+#define LOGICAL_DEV_ACPI 0x01
+#define LDEV_ACPI_WAKE_EN_REG 0xe8
+#define ACPI_WAKE_EN_CIR_BIT 0x04
+
+#define LDEV_ACPI_PME_EN_REG 0xf0
+#define LDEV_ACPI_PME_CLR_REG 0xf1
+#define ACPI_PME_CIR_BIT 0x02
+
+#define LDEV_ACPI_STATE_REG 0xf4
+#define ACPI_STATE_CIR_BIT 0x20
+
+/*
+ * CIR status register (0x00):
+ * 7 - CIR_IRQ_EN (1 = enable CIR IRQ, 0 = disable)
+ * 3 - TX_FINISH (1 when TX finished, write 1 to clear)
+ * 2 - TX_UNDERRUN (1 on TX underrun, write 1 to clear)
+ * 1 - RX_TIMEOUT (1 on RX timeout, write 1 to clear)
+ * 0 - RX_RECEIVE (1 on RX receive, write 1 to clear)
+ */
+#define CIR_STATUS_IRQ_EN 0x80
+#define CIR_STATUS_TX_FINISH 0x08
+#define CIR_STATUS_TX_UNDERRUN 0x04
+#define CIR_STATUS_RX_TIMEOUT 0x02
+#define CIR_STATUS_RX_RECEIVE 0x01
+#define CIR_STATUS_IRQ_MASK 0x0f
+
+/*
+ * CIR TX control register (0x02):
+ * 7 - TX_START (1 to indicate TX start, auto-cleared when done)
+ * 6 - TX_END (1 to indicate TX data written to TX fifo)
+ */
+#define CIR_TX_CONTROL_TX_START 0x80
+#define CIR_TX_CONTROL_TX_END 0x40
+
diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c
new file mode 100644
index 00000000000..29b5f89813b
--- /dev/null
+++ b/drivers/media/rc/gpio-ir-recv.c
@@ -0,0 +1,253 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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 <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <media/rc-core.h>
+#include <media/gpio-ir-recv.h>
+
+#define GPIO_IR_DRIVER_NAME "gpio-rc-recv"
+#define GPIO_IR_DEVICE_NAME "gpio_ir_recv"
+
+struct gpio_rc_dev {
+ struct rc_dev *rcdev;
+ int gpio_nr;
+ bool active_low;
+};
+
+#ifdef CONFIG_OF
+/*
+ * Translate OpenFirmware node properties into platform_data
+ */
+static int gpio_ir_recv_get_devtree_pdata(struct device *dev,
+ struct gpio_ir_recv_platform_data *pdata)
+{
+ struct device_node *np = dev->of_node;
+ enum of_gpio_flags flags;
+ int gpio;
+
+ gpio = of_get_gpio_flags(np, 0, &flags);
+ if (gpio < 0) {
+ if (gpio != -EPROBE_DEFER)
+ dev_err(dev, "Failed to get gpio flags (%d)\n", gpio);
+ return gpio;
+ }
+
+ pdata->gpio_nr = gpio;
+ pdata->active_low = (flags & OF_GPIO_ACTIVE_LOW);
+ /* probe() takes care of map_name == NULL or allowed_protos == 0 */
+ pdata->map_name = of_get_property(np, "linux,rc-map-name", NULL);
+ pdata->allowed_protos = 0;
+
+ return 0;
+}
+
+static struct of_device_id gpio_ir_recv_of_match[] = {
+ { .compatible = "gpio-ir-receiver", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, gpio_ir_recv_of_match);
+
+#else /* !CONFIG_OF */
+
+#define gpio_ir_recv_get_devtree_pdata(dev, pdata) (-ENOSYS)
+
+#endif
+
+static irqreturn_t gpio_ir_recv_irq(int irq, void *dev_id)
+{
+ struct gpio_rc_dev *gpio_dev = dev_id;
+ int gval;
+ int rc = 0;
+ enum raw_event_type type = IR_SPACE;
+
+ gval = gpio_get_value_cansleep(gpio_dev->gpio_nr);
+
+ if (gval < 0)
+ goto err_get_value;
+
+ if (gpio_dev->active_low)
+ gval = !gval;
+
+ if (gval == 1)
+ type = IR_PULSE;
+
+ rc = ir_raw_event_store_edge(gpio_dev->rcdev, type);
+ if (rc < 0)
+ goto err_get_value;
+
+ ir_raw_event_handle(gpio_dev->rcdev);
+
+err_get_value:
+ return IRQ_HANDLED;
+}
+
+static int gpio_ir_recv_probe(struct platform_device *pdev)
+{
+ struct gpio_rc_dev *gpio_dev;
+ struct rc_dev *rcdev;
+ const struct gpio_ir_recv_platform_data *pdata =
+ pdev->dev.platform_data;
+ int rc;
+
+ if (pdev->dev.of_node) {
+ struct gpio_ir_recv_platform_data *dtpdata =
+ devm_kzalloc(&pdev->dev, sizeof(*dtpdata), GFP_KERNEL);
+ if (!dtpdata)
+ return -ENOMEM;
+ rc = gpio_ir_recv_get_devtree_pdata(&pdev->dev, dtpdata);
+ if (rc)
+ return rc;
+ pdata = dtpdata;
+ }
+
+ if (!pdata)
+ return -EINVAL;
+
+ if (pdata->gpio_nr < 0)
+ return -EINVAL;
+
+ gpio_dev = kzalloc(sizeof(struct gpio_rc_dev), GFP_KERNEL);
+ if (!gpio_dev)
+ return -ENOMEM;
+
+ rcdev = rc_allocate_device();
+ if (!rcdev) {
+ rc = -ENOMEM;
+ goto err_allocate_device;
+ }
+
+ rcdev->priv = gpio_dev;
+ rcdev->driver_type = RC_DRIVER_IR_RAW;
+ rcdev->input_name = GPIO_IR_DEVICE_NAME;
+ rcdev->input_phys = GPIO_IR_DEVICE_NAME "/input0";
+ rcdev->input_id.bustype = BUS_HOST;
+ rcdev->input_id.vendor = 0x0001;
+ rcdev->input_id.product = 0x0001;
+ rcdev->input_id.version = 0x0100;
+ rcdev->dev.parent = &pdev->dev;
+ rcdev->driver_name = GPIO_IR_DRIVER_NAME;
+ if (pdata->allowed_protos)
+ rc_set_allowed_protocols(rcdev, pdata->allowed_protos);
+ else
+ rc_set_allowed_protocols(rcdev, RC_BIT_ALL);
+ rcdev->map_name = pdata->map_name ?: RC_MAP_EMPTY;
+
+ gpio_dev->rcdev = rcdev;
+ gpio_dev->gpio_nr = pdata->gpio_nr;
+ gpio_dev->active_low = pdata->active_low;
+
+ rc = gpio_request(pdata->gpio_nr, "gpio-ir-recv");
+ if (rc < 0)
+ goto err_gpio_request;
+ rc = gpio_direction_input(pdata->gpio_nr);
+ if (rc < 0)
+ goto err_gpio_direction_input;
+
+ rc = rc_register_device(rcdev);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "failed to register rc device\n");
+ goto err_register_rc_device;
+ }
+
+ platform_set_drvdata(pdev, gpio_dev);
+
+ rc = request_any_context_irq(gpio_to_irq(pdata->gpio_nr),
+ gpio_ir_recv_irq,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ "gpio-ir-recv-irq", gpio_dev);
+ if (rc < 0)
+ goto err_request_irq;
+
+ return 0;
+
+err_request_irq:
+ rc_unregister_device(rcdev);
+ rcdev = NULL;
+err_register_rc_device:
+err_gpio_direction_input:
+ gpio_free(pdata->gpio_nr);
+err_gpio_request:
+ rc_free_device(rcdev);
+err_allocate_device:
+ kfree(gpio_dev);
+ return rc;
+}
+
+static int gpio_ir_recv_remove(struct platform_device *pdev)
+{
+ struct gpio_rc_dev *gpio_dev = platform_get_drvdata(pdev);
+
+ free_irq(gpio_to_irq(gpio_dev->gpio_nr), gpio_dev);
+ rc_unregister_device(gpio_dev->rcdev);
+ gpio_free(gpio_dev->gpio_nr);
+ kfree(gpio_dev);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int gpio_ir_recv_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct gpio_rc_dev *gpio_dev = platform_get_drvdata(pdev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(gpio_to_irq(gpio_dev->gpio_nr));
+ else
+ disable_irq(gpio_to_irq(gpio_dev->gpio_nr));
+
+ return 0;
+}
+
+static int gpio_ir_recv_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct gpio_rc_dev *gpio_dev = platform_get_drvdata(pdev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(gpio_to_irq(gpio_dev->gpio_nr));
+ else
+ enable_irq(gpio_to_irq(gpio_dev->gpio_nr));
+
+ return 0;
+}
+
+static const struct dev_pm_ops gpio_ir_recv_pm_ops = {
+ .suspend = gpio_ir_recv_suspend,
+ .resume = gpio_ir_recv_resume,
+};
+#endif
+
+static struct platform_driver gpio_ir_recv_driver = {
+ .probe = gpio_ir_recv_probe,
+ .remove = gpio_ir_recv_remove,
+ .driver = {
+ .name = GPIO_IR_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(gpio_ir_recv_of_match),
+#ifdef CONFIG_PM
+ .pm = &gpio_ir_recv_pm_ops,
+#endif
+ },
+};
+module_platform_driver(gpio_ir_recv_driver);
+
+MODULE_DESCRIPTION("GPIO IR Receiver driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c
new file mode 100644
index 00000000000..627ddfd6198
--- /dev/null
+++ b/drivers/media/rc/iguanair.c
@@ -0,0 +1,618 @@
+/*
+ * IguanaWorks USB IR Transceiver support
+ *
+ * Copyright (C) 2012 Sean Young <sean@mess.org>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+#include <media/rc-core.h>
+
+#define DRIVER_NAME "iguanair"
+#define BUF_SIZE 152
+
+struct iguanair {
+ struct rc_dev *rc;
+
+ struct device *dev;
+ struct usb_device *udev;
+
+ uint16_t version;
+ uint8_t bufsize;
+ uint8_t cycle_overhead;
+
+ struct mutex lock;
+
+ /* receiver support */
+ bool receiver_on;
+ dma_addr_t dma_in, dma_out;
+ uint8_t *buf_in;
+ struct urb *urb_in, *urb_out;
+ struct completion completion;
+
+ /* transmit support */
+ bool tx_overflow;
+ uint32_t carrier;
+ struct send_packet *packet;
+
+ char name[64];
+ char phys[64];
+};
+
+#define CMD_NOP 0x00
+#define CMD_GET_VERSION 0x01
+#define CMD_GET_BUFSIZE 0x11
+#define CMD_GET_FEATURES 0x10
+#define CMD_SEND 0x15
+#define CMD_EXECUTE 0x1f
+#define CMD_RX_OVERFLOW 0x31
+#define CMD_TX_OVERFLOW 0x32
+#define CMD_RECEIVER_ON 0x12
+#define CMD_RECEIVER_OFF 0x14
+
+#define DIR_IN 0xdc
+#define DIR_OUT 0xcd
+
+#define MAX_IN_PACKET 8u
+#define MAX_OUT_PACKET (sizeof(struct send_packet) + BUF_SIZE)
+#define TIMEOUT 1000
+#define RX_RESOLUTION 21333
+
+struct packet {
+ uint16_t start;
+ uint8_t direction;
+ uint8_t cmd;
+};
+
+struct send_packet {
+ struct packet header;
+ uint8_t length;
+ uint8_t channels;
+ uint8_t busy7;
+ uint8_t busy4;
+ uint8_t payload[0];
+};
+
+static void process_ir_data(struct iguanair *ir, unsigned len)
+{
+ if (len >= 4 && ir->buf_in[0] == 0 && ir->buf_in[1] == 0) {
+ switch (ir->buf_in[3]) {
+ case CMD_GET_VERSION:
+ if (len == 6) {
+ ir->version = (ir->buf_in[5] << 8) |
+ ir->buf_in[4];
+ complete(&ir->completion);
+ }
+ break;
+ case CMD_GET_BUFSIZE:
+ if (len >= 5) {
+ ir->bufsize = ir->buf_in[4];
+ complete(&ir->completion);
+ }
+ break;
+ case CMD_GET_FEATURES:
+ if (len > 5) {
+ ir->cycle_overhead = ir->buf_in[5];
+ complete(&ir->completion);
+ }
+ break;
+ case CMD_TX_OVERFLOW:
+ ir->tx_overflow = true;
+ case CMD_RECEIVER_OFF:
+ case CMD_RECEIVER_ON:
+ case CMD_SEND:
+ complete(&ir->completion);
+ break;
+ case CMD_RX_OVERFLOW:
+ dev_warn(ir->dev, "receive overflow\n");
+ ir_raw_event_reset(ir->rc);
+ break;
+ default:
+ dev_warn(ir->dev, "control code %02x received\n",
+ ir->buf_in[3]);
+ break;
+ }
+ } else if (len >= 7) {
+ DEFINE_IR_RAW_EVENT(rawir);
+ unsigned i;
+ bool event = false;
+
+ init_ir_raw_event(&rawir);
+
+ for (i = 0; i < 7; i++) {
+ if (ir->buf_in[i] == 0x80) {
+ rawir.pulse = false;
+ rawir.duration = US_TO_NS(21845);
+ } else {
+ rawir.pulse = (ir->buf_in[i] & 0x80) == 0;
+ rawir.duration = ((ir->buf_in[i] & 0x7f) + 1) *
+ RX_RESOLUTION;
+ }
+
+ if (ir_raw_event_store_with_filter(ir->rc, &rawir))
+ event = true;
+ }
+
+ if (event)
+ ir_raw_event_handle(ir->rc);
+ }
+}
+
+static void iguanair_rx(struct urb *urb)
+{
+ struct iguanair *ir;
+ int rc;
+
+ if (!urb)
+ return;
+
+ ir = urb->context;
+ if (!ir) {
+ usb_unlink_urb(urb);
+ return;
+ }
+
+ switch (urb->status) {
+ case 0:
+ process_ir_data(ir, urb->actual_length);
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ usb_unlink_urb(urb);
+ return;
+ case -EPIPE:
+ default:
+ dev_dbg(ir->dev, "Error: urb status = %d\n", urb->status);
+ break;
+ }
+
+ rc = usb_submit_urb(urb, GFP_ATOMIC);
+ if (rc && rc != -ENODEV)
+ dev_warn(ir->dev, "failed to resubmit urb: %d\n", rc);
+}
+
+static void iguanair_irq_out(struct urb *urb)
+{
+ struct iguanair *ir = urb->context;
+
+ if (urb->status)
+ dev_dbg(ir->dev, "Error: out urb status = %d\n", urb->status);
+
+ /* if we sent an nop packet, do not expect a response */
+ if (urb->status == 0 && ir->packet->header.cmd == CMD_NOP)
+ complete(&ir->completion);
+}
+
+static int iguanair_send(struct iguanair *ir, unsigned size)
+{
+ int rc;
+
+ reinit_completion(&ir->completion);
+
+ ir->urb_out->transfer_buffer_length = size;
+ rc = usb_submit_urb(ir->urb_out, GFP_KERNEL);
+ if (rc)
+ return rc;
+
+ if (wait_for_completion_timeout(&ir->completion, TIMEOUT) == 0)
+ return -ETIMEDOUT;
+
+ return rc;
+}
+
+static int iguanair_get_features(struct iguanair *ir)
+{
+ int rc;
+
+ /*
+ * On cold boot, the iguanair initializes on the first packet
+ * received but does not process that packet. Send an empty
+ * packet.
+ */
+ ir->packet->header.start = 0;
+ ir->packet->header.direction = DIR_OUT;
+ ir->packet->header.cmd = CMD_NOP;
+ iguanair_send(ir, sizeof(ir->packet->header));
+
+ ir->packet->header.cmd = CMD_GET_VERSION;
+ rc = iguanair_send(ir, sizeof(ir->packet->header));
+ if (rc) {
+ dev_info(ir->dev, "failed to get version\n");
+ goto out;
+ }
+
+ if (ir->version < 0x205) {
+ dev_err(ir->dev, "firmware 0x%04x is too old\n", ir->version);
+ rc = -ENODEV;
+ goto out;
+ }
+
+ ir->bufsize = 150;
+ ir->cycle_overhead = 65;
+
+ ir->packet->header.cmd = CMD_GET_BUFSIZE;
+
+ rc = iguanair_send(ir, sizeof(ir->packet->header));
+ if (rc) {
+ dev_info(ir->dev, "failed to get buffer size\n");
+ goto out;
+ }
+
+ if (ir->bufsize > BUF_SIZE) {
+ dev_info(ir->dev, "buffer size %u larger than expected\n",
+ ir->bufsize);
+ ir->bufsize = BUF_SIZE;
+ }
+
+ ir->packet->header.cmd = CMD_GET_FEATURES;
+
+ rc = iguanair_send(ir, sizeof(ir->packet->header));
+ if (rc)
+ dev_info(ir->dev, "failed to get features\n");
+out:
+ return rc;
+}
+
+static int iguanair_receiver(struct iguanair *ir, bool enable)
+{
+ ir->packet->header.start = 0;
+ ir->packet->header.direction = DIR_OUT;
+ ir->packet->header.cmd = enable ? CMD_RECEIVER_ON : CMD_RECEIVER_OFF;
+
+ if (enable)
+ ir_raw_event_reset(ir->rc);
+
+ return iguanair_send(ir, sizeof(ir->packet->header));
+}
+
+/*
+ * The iguanair creates the carrier by busy spinning after each half period.
+ * This is counted in CPU cycles, with the CPU running at 24MHz. It is
+ * broken down into 7-cycles and 4-cyles delays, with a preference for
+ * 4-cycle delays, minus the overhead of the loop itself (cycle_overhead).
+ */
+static int iguanair_set_tx_carrier(struct rc_dev *dev, uint32_t carrier)
+{
+ struct iguanair *ir = dev->priv;
+
+ if (carrier < 25000 || carrier > 150000)
+ return -EINVAL;
+
+ mutex_lock(&ir->lock);
+
+ if (carrier != ir->carrier) {
+ uint32_t cycles, fours, sevens;
+
+ ir->carrier = carrier;
+
+ cycles = DIV_ROUND_CLOSEST(24000000, carrier * 2) -
+ ir->cycle_overhead;
+
+ /*
+ * Calculate minimum number of 7 cycles needed so
+ * we are left with a multiple of 4; so we want to have
+ * (sevens * 7) & 3 == cycles & 3
+ */
+ sevens = (4 - cycles) & 3;
+ fours = (cycles - sevens * 7) / 4;
+
+ /*
+ * The firmware interprets these values as a relative offset
+ * for a branch. Immediately following the branches, there
+ * 4 instructions of 7 cycles (2 bytes each) and 110
+ * instructions of 4 cycles (1 byte each). A relative branch
+ * of 0 will execute all of them, branch further for less
+ * cycle burning.
+ */
+ ir->packet->busy7 = (4 - sevens) * 2;
+ ir->packet->busy4 = 110 - fours;
+ }
+
+ mutex_unlock(&ir->lock);
+
+ return carrier;
+}
+
+static int iguanair_set_tx_mask(struct rc_dev *dev, uint32_t mask)
+{
+ struct iguanair *ir = dev->priv;
+
+ if (mask > 15)
+ return 4;
+
+ mutex_lock(&ir->lock);
+ ir->packet->channels = mask << 4;
+ mutex_unlock(&ir->lock);
+
+ return 0;
+}
+
+static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count)
+{
+ struct iguanair *ir = dev->priv;
+ uint8_t space;
+ unsigned i, size, periods, bytes;
+ int rc;
+
+ mutex_lock(&ir->lock);
+
+ /* convert from us to carrier periods */
+ for (i = space = size = 0; i < count; i++) {
+ periods = DIV_ROUND_CLOSEST(txbuf[i] * ir->carrier, 1000000);
+ bytes = DIV_ROUND_UP(periods, 127);
+ if (size + bytes > ir->bufsize) {
+ rc = -EINVAL;
+ goto out;
+ }
+ while (periods) {
+ unsigned p = min(periods, 127u);
+ ir->packet->payload[size++] = p | space;
+ periods -= p;
+ }
+ space ^= 0x80;
+ }
+
+ ir->packet->header.start = 0;
+ ir->packet->header.direction = DIR_OUT;
+ ir->packet->header.cmd = CMD_SEND;
+ ir->packet->length = size;
+
+ ir->tx_overflow = false;
+
+ rc = iguanair_send(ir, sizeof(*ir->packet) + size);
+
+ if (rc == 0 && ir->tx_overflow)
+ rc = -EOVERFLOW;
+
+out:
+ mutex_unlock(&ir->lock);
+
+ return rc ? rc : count;
+}
+
+static int iguanair_open(struct rc_dev *rdev)
+{
+ struct iguanair *ir = rdev->priv;
+ int rc;
+
+ mutex_lock(&ir->lock);
+
+ rc = iguanair_receiver(ir, true);
+ if (rc == 0)
+ ir->receiver_on = true;
+
+ mutex_unlock(&ir->lock);
+
+ return rc;
+}
+
+static void iguanair_close(struct rc_dev *rdev)
+{
+ struct iguanair *ir = rdev->priv;
+ int rc;
+
+ mutex_lock(&ir->lock);
+
+ rc = iguanair_receiver(ir, false);
+ ir->receiver_on = false;
+ if (rc && rc != -ENODEV)
+ dev_warn(ir->dev, "failed to disable receiver: %d\n", rc);
+
+ mutex_unlock(&ir->lock);
+}
+
+static int iguanair_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct iguanair *ir;
+ struct rc_dev *rc;
+ int ret, pipein, pipeout;
+ struct usb_host_interface *idesc;
+
+ ir = kzalloc(sizeof(*ir), GFP_KERNEL);
+ rc = rc_allocate_device();
+ if (!ir || !rc) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ir->buf_in = usb_alloc_coherent(udev, MAX_IN_PACKET, GFP_KERNEL,
+ &ir->dma_in);
+ ir->packet = usb_alloc_coherent(udev, MAX_OUT_PACKET, GFP_KERNEL,
+ &ir->dma_out);
+ 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) {
+ 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;
+ mutex_init(&ir->lock);
+
+ init_completion(&ir->completion);
+ pipeout = usb_sndintpipe(udev,
+ idesc->endpoint[1].desc.bEndpointAddress);
+ usb_fill_int_urb(ir->urb_out, udev, pipeout, ir->packet, MAX_OUT_PACKET,
+ iguanair_irq_out, ir, 1);
+ ir->urb_out->transfer_dma = ir->dma_out;
+ ir->urb_out->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ pipein = usb_rcvintpipe(udev, idesc->endpoint[0].desc.bEndpointAddress);
+ usb_fill_int_urb(ir->urb_in, udev, pipein, ir->buf_in, MAX_IN_PACKET,
+ iguanair_rx, ir, 1);
+ ir->urb_in->transfer_dma = ir->dma_in;
+ ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ ret = usb_submit_urb(ir->urb_in, GFP_KERNEL);
+ if (ret) {
+ dev_warn(&intf->dev, "failed to submit urb: %d\n", ret);
+ goto out;
+ }
+
+ ret = iguanair_get_features(ir);
+ if (ret)
+ goto out2;
+
+ snprintf(ir->name, sizeof(ir->name),
+ "IguanaWorks USB IR Transceiver version 0x%04x", ir->version);
+
+ usb_make_path(ir->udev, ir->phys, sizeof(ir->phys));
+
+ rc->input_name = ir->name;
+ rc->input_phys = ir->phys;
+ usb_to_input_id(ir->udev, &rc->input_id);
+ rc->dev.parent = &intf->dev;
+ rc->driver_type = RC_DRIVER_IR_RAW;
+ rc_set_allowed_protocols(rc, RC_BIT_ALL);
+ rc->priv = ir;
+ rc->open = iguanair_open;
+ rc->close = iguanair_close;
+ rc->s_tx_mask = iguanair_set_tx_mask;
+ rc->s_tx_carrier = iguanair_set_tx_carrier;
+ rc->tx_ir = iguanair_tx;
+ rc->driver_name = DRIVER_NAME;
+ rc->map_name = RC_MAP_RC6_MCE;
+ rc->timeout = MS_TO_NS(100);
+ rc->rx_resolution = RX_RESOLUTION;
+
+ iguanair_set_tx_carrier(rc, 38000);
+ iguanair_set_tx_mask(rc, 0);
+
+ ret = rc_register_device(rc);
+ if (ret < 0) {
+ dev_err(&intf->dev, "failed to register rc device %d", ret);
+ goto out2;
+ }
+
+ usb_set_intfdata(intf, ir);
+
+ return 0;
+out2:
+ usb_kill_urb(ir->urb_in);
+ usb_kill_urb(ir->urb_out);
+out:
+ if (ir) {
+ usb_free_urb(ir->urb_in);
+ usb_free_urb(ir->urb_out);
+ usb_free_coherent(udev, MAX_IN_PACKET, ir->buf_in, ir->dma_in);
+ usb_free_coherent(udev, MAX_OUT_PACKET, ir->packet,
+ ir->dma_out);
+ }
+ rc_free_device(rc);
+ kfree(ir);
+ return ret;
+}
+
+static void iguanair_disconnect(struct usb_interface *intf)
+{
+ struct iguanair *ir = usb_get_intfdata(intf);
+
+ rc_unregister_device(ir->rc);
+ usb_set_intfdata(intf, NULL);
+ usb_kill_urb(ir->urb_in);
+ usb_kill_urb(ir->urb_out);
+ usb_free_urb(ir->urb_in);
+ usb_free_urb(ir->urb_out);
+ usb_free_coherent(ir->udev, MAX_IN_PACKET, ir->buf_in, ir->dma_in);
+ usb_free_coherent(ir->udev, MAX_OUT_PACKET, ir->packet, ir->dma_out);
+ kfree(ir);
+}
+
+static int iguanair_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct iguanair *ir = usb_get_intfdata(intf);
+ int rc = 0;
+
+ mutex_lock(&ir->lock);
+
+ if (ir->receiver_on) {
+ rc = iguanair_receiver(ir, false);
+ if (rc)
+ dev_warn(ir->dev, "failed to disable receiver for suspend\n");
+ }
+
+ usb_kill_urb(ir->urb_in);
+ usb_kill_urb(ir->urb_out);
+
+ mutex_unlock(&ir->lock);
+
+ return rc;
+}
+
+static int iguanair_resume(struct usb_interface *intf)
+{
+ struct iguanair *ir = usb_get_intfdata(intf);
+ int rc = 0;
+
+ mutex_lock(&ir->lock);
+
+ rc = usb_submit_urb(ir->urb_in, GFP_KERNEL);
+ if (rc)
+ dev_warn(&intf->dev, "failed to submit urb: %d\n", rc);
+
+ if (ir->receiver_on) {
+ rc = iguanair_receiver(ir, true);
+ if (rc)
+ dev_warn(ir->dev, "failed to enable receiver after resume\n");
+ }
+
+ mutex_unlock(&ir->lock);
+
+ return rc;
+}
+
+static const struct usb_device_id iguanair_table[] = {
+ { USB_DEVICE(0x1781, 0x0938) },
+ { }
+};
+
+static struct usb_driver iguanair_driver = {
+ .name = DRIVER_NAME,
+ .probe = iguanair_probe,
+ .disconnect = iguanair_disconnect,
+ .suspend = iguanair_suspend,
+ .resume = iguanair_resume,
+ .reset_resume = iguanair_resume,
+ .id_table = iguanair_table,
+ .soft_unbind = 1 /* we want to disable receiver on unbind */
+};
+
+module_usb_driver(iguanair_driver);
+
+MODULE_DESCRIPTION("IguanaWorks USB IR Transceiver");
+MODULE_AUTHOR("Sean Young <sean@mess.org>");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, iguanair_table);
+
diff --git a/drivers/media/rc/img-ir/Kconfig b/drivers/media/rc/img-ir/Kconfig
new file mode 100644
index 00000000000..03ba9fc170f
--- /dev/null
+++ b/drivers/media/rc/img-ir/Kconfig
@@ -0,0 +1,61 @@
+config IR_IMG
+ tristate "ImgTec IR Decoder"
+ depends on RC_CORE
+ select IR_IMG_HW if !IR_IMG_RAW
+ help
+ Say Y or M here if you want to use the ImgTec infrared decoder
+ functionality found in SoCs such as TZ1090.
+
+config IR_IMG_RAW
+ bool "Raw decoder"
+ depends on IR_IMG
+ help
+ Say Y here to enable the raw mode driver which passes raw IR signal
+ changes to the IR raw decoders for software decoding. This is much
+ less reliable (due to lack of timestamps) and consumes more
+ processing power than using hardware decode, but can be useful for
+ testing, debug, and to make more protocols available.
+
+config IR_IMG_HW
+ bool "Hardware decoder"
+ depends on IR_IMG
+ help
+ Say Y here to enable the hardware decode driver which decodes the IR
+ signals in hardware. This is more reliable, consumes less processing
+ power since only a single interrupt is received for each scancode,
+ and allows an IR scancode to be used as a wake event.
+
+config IR_IMG_NEC
+ bool "NEC protocol support"
+ depends on IR_IMG_HW
+ help
+ Say Y here to enable support for the NEC, extended NEC, and 32-bit
+ NEC protocols in the ImgTec infrared decoder block.
+
+config IR_IMG_JVC
+ bool "JVC protocol support"
+ depends on IR_IMG_HW
+ help
+ Say Y here to enable support for the JVC protocol in the ImgTec
+ infrared decoder block.
+
+config IR_IMG_SONY
+ bool "Sony protocol support"
+ depends on IR_IMG_HW
+ help
+ Say Y here to enable support for the Sony protocol in the ImgTec
+ infrared decoder block.
+
+config IR_IMG_SHARP
+ bool "Sharp protocol support"
+ depends on IR_IMG_HW
+ help
+ Say Y here to enable support for the Sharp protocol in the ImgTec
+ infrared decoder block.
+
+config IR_IMG_SANYO
+ bool "Sanyo protocol support"
+ depends on IR_IMG_HW
+ help
+ Say Y here to enable support for the Sanyo protocol (used by Sanyo,
+ Aiwa, Chinon remotes) in the ImgTec infrared decoder block.
diff --git a/drivers/media/rc/img-ir/Makefile b/drivers/media/rc/img-ir/Makefile
new file mode 100644
index 00000000000..92a459d9950
--- /dev/null
+++ b/drivers/media/rc/img-ir/Makefile
@@ -0,0 +1,11 @@
+img-ir-y := img-ir-core.o
+img-ir-$(CONFIG_IR_IMG_RAW) += img-ir-raw.o
+img-ir-$(CONFIG_IR_IMG_HW) += img-ir-hw.o
+img-ir-$(CONFIG_IR_IMG_NEC) += img-ir-nec.o
+img-ir-$(CONFIG_IR_IMG_JVC) += img-ir-jvc.o
+img-ir-$(CONFIG_IR_IMG_SONY) += img-ir-sony.o
+img-ir-$(CONFIG_IR_IMG_SHARP) += img-ir-sharp.o
+img-ir-$(CONFIG_IR_IMG_SANYO) += img-ir-sanyo.o
+img-ir-objs := $(img-ir-y)
+
+obj-$(CONFIG_IR_IMG) += img-ir.o
diff --git a/drivers/media/rc/img-ir/img-ir-core.c b/drivers/media/rc/img-ir/img-ir-core.c
new file mode 100644
index 00000000000..6b7834834fb
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-core.c
@@ -0,0 +1,176 @@
+/*
+ * ImgTec IR Decoder found in PowerDown Controller.
+ *
+ * Copyright 2010-2014 Imagination Technologies Ltd.
+ *
+ * This contains core img-ir code for setting up the driver. The two interfaces
+ * (raw and hardware decode) are handled separately.
+ */
+
+#include <linux/clk.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include "img-ir.h"
+
+static irqreturn_t img_ir_isr(int irq, void *dev_id)
+{
+ struct img_ir_priv *priv = dev_id;
+ u32 irq_status;
+
+ spin_lock(&priv->lock);
+ /* we have to clear irqs before reading */
+ irq_status = img_ir_read(priv, IMG_IR_IRQ_STATUS);
+ img_ir_write(priv, IMG_IR_IRQ_CLEAR, irq_status);
+
+ /* don't handle valid data irqs if we're only interested in matches */
+ irq_status &= img_ir_read(priv, IMG_IR_IRQ_ENABLE);
+
+ /* hand off edge interrupts to raw decode handler */
+ if (irq_status & IMG_IR_IRQ_EDGE && img_ir_raw_enabled(&priv->raw))
+ img_ir_isr_raw(priv, irq_status);
+
+ /* hand off hardware match interrupts to hardware decode handler */
+ if (irq_status & (IMG_IR_IRQ_DATA_MATCH |
+ IMG_IR_IRQ_DATA_VALID |
+ IMG_IR_IRQ_DATA2_VALID) &&
+ img_ir_hw_enabled(&priv->hw))
+ img_ir_isr_hw(priv, irq_status);
+
+ spin_unlock(&priv->lock);
+ return IRQ_HANDLED;
+}
+
+static void img_ir_setup(struct img_ir_priv *priv)
+{
+ /* start off with interrupts disabled */
+ img_ir_write(priv, IMG_IR_IRQ_ENABLE, 0);
+
+ img_ir_setup_raw(priv);
+ img_ir_setup_hw(priv);
+
+ if (!IS_ERR(priv->clk))
+ clk_prepare_enable(priv->clk);
+}
+
+static void img_ir_ident(struct img_ir_priv *priv)
+{
+ u32 core_rev = img_ir_read(priv, IMG_IR_CORE_REV);
+
+ dev_info(priv->dev,
+ "IMG IR Decoder (%d.%d.%d.%d) probed successfully\n",
+ (core_rev & IMG_IR_DESIGNER) >> IMG_IR_DESIGNER_SHIFT,
+ (core_rev & IMG_IR_MAJOR_REV) >> IMG_IR_MAJOR_REV_SHIFT,
+ (core_rev & IMG_IR_MINOR_REV) >> IMG_IR_MINOR_REV_SHIFT,
+ (core_rev & IMG_IR_MAINT_REV) >> IMG_IR_MAINT_REV_SHIFT);
+ dev_info(priv->dev, "Modes:%s%s\n",
+ img_ir_hw_enabled(&priv->hw) ? " hardware" : "",
+ img_ir_raw_enabled(&priv->raw) ? " raw" : "");
+}
+
+static int img_ir_probe(struct platform_device *pdev)
+{
+ struct img_ir_priv *priv;
+ struct resource *res_regs;
+ int irq, error, error2;
+
+ /* Get resources from platform device */
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "cannot find IRQ resource\n");
+ return irq;
+ }
+
+ /* Private driver data */
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&pdev->dev, "cannot allocate device data\n");
+ return -ENOMEM;
+ }
+ platform_set_drvdata(pdev, priv);
+ priv->dev = &pdev->dev;
+ spin_lock_init(&priv->lock);
+
+ /* Ioremap the registers */
+ res_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->reg_base = devm_ioremap_resource(&pdev->dev, res_regs);
+ if (IS_ERR(priv->reg_base))
+ return PTR_ERR(priv->reg_base);
+
+ /* Get core clock */
+ priv->clk = devm_clk_get(&pdev->dev, "core");
+ if (IS_ERR(priv->clk))
+ dev_warn(&pdev->dev, "cannot get core clock resource\n");
+ /*
+ * The driver doesn't need to know about the system ("sys") or power
+ * modulation ("mod") clocks yet
+ */
+
+ /* Set up raw & hw decoder */
+ error = img_ir_probe_raw(priv);
+ error2 = img_ir_probe_hw(priv);
+ if (error && error2)
+ return (error == -ENODEV) ? error2 : error;
+
+ /* Get the IRQ */
+ priv->irq = irq;
+ error = request_irq(priv->irq, img_ir_isr, 0, "img-ir", priv);
+ if (error) {
+ dev_err(&pdev->dev, "cannot register IRQ %u\n",
+ priv->irq);
+ error = -EIO;
+ goto err_irq;
+ }
+
+ img_ir_ident(priv);
+ img_ir_setup(priv);
+
+ return 0;
+
+err_irq:
+ img_ir_remove_hw(priv);
+ img_ir_remove_raw(priv);
+ return error;
+}
+
+static int img_ir_remove(struct platform_device *pdev)
+{
+ struct img_ir_priv *priv = platform_get_drvdata(pdev);
+
+ free_irq(priv->irq, img_ir_isr);
+ img_ir_remove_hw(priv);
+ img_ir_remove_raw(priv);
+
+ if (!IS_ERR(priv->clk))
+ clk_disable_unprepare(priv->clk);
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(img_ir_pmops, img_ir_suspend, img_ir_resume);
+
+static const struct of_device_id img_ir_match[] = {
+ { .compatible = "img,ir-rev1" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, img_ir_match);
+
+static struct platform_driver img_ir_driver = {
+ .driver = {
+ .name = "img-ir",
+ .owner = THIS_MODULE,
+ .of_match_table = img_ir_match,
+ .pm = &img_ir_pmops,
+ },
+ .probe = img_ir_probe,
+ .remove = img_ir_remove,
+};
+
+module_platform_driver(img_ir_driver);
+
+MODULE_AUTHOR("Imagination Technologies Ltd.");
+MODULE_DESCRIPTION("ImgTec IR");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/rc/img-ir/img-ir-hw.c b/drivers/media/rc/img-ir/img-ir-hw.c
new file mode 100644
index 00000000000..0127dd257a5
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-hw.c
@@ -0,0 +1,1066 @@
+/*
+ * ImgTec IR Hardware Decoder found in PowerDown Controller.
+ *
+ * Copyright 2010-2014 Imagination Technologies Ltd.
+ *
+ * This ties into the input subsystem using the RC-core. Protocol support is
+ * provided in separate modules which provide the parameters and scancode
+ * translation functions to set up the hardware decoder and interpret the
+ * resulting input.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <media/rc-core.h>
+#include "img-ir.h"
+
+/* Decoders lock (only modified to preprocess them) */
+static DEFINE_SPINLOCK(img_ir_decoders_lock);
+
+extern struct img_ir_decoder img_ir_nec;
+extern struct img_ir_decoder img_ir_jvc;
+extern struct img_ir_decoder img_ir_sony;
+extern struct img_ir_decoder img_ir_sharp;
+extern struct img_ir_decoder img_ir_sanyo;
+
+static bool img_ir_decoders_preprocessed;
+static struct img_ir_decoder *img_ir_decoders[] = {
+#ifdef CONFIG_IR_IMG_NEC
+ &img_ir_nec,
+#endif
+#ifdef CONFIG_IR_IMG_JVC
+ &img_ir_jvc,
+#endif
+#ifdef CONFIG_IR_IMG_SONY
+ &img_ir_sony,
+#endif
+#ifdef CONFIG_IR_IMG_SHARP
+ &img_ir_sharp,
+#endif
+#ifdef CONFIG_IR_IMG_SANYO
+ &img_ir_sanyo,
+#endif
+ NULL
+};
+
+#define IMG_IR_F_FILTER BIT(RC_FILTER_NORMAL) /* enable filtering */
+#define IMG_IR_F_WAKE BIT(RC_FILTER_WAKEUP) /* enable waking */
+
+/* code type quirks */
+
+#define IMG_IR_QUIRK_CODE_BROKEN 0x1 /* Decode is broken */
+#define IMG_IR_QUIRK_CODE_LEN_INCR 0x2 /* Bit length needs increment */
+
+/* functions for preprocessing timings, ensuring max is set */
+
+static void img_ir_timing_preprocess(struct img_ir_timing_range *range,
+ unsigned int unit)
+{
+ if (range->max < range->min)
+ range->max = range->min;
+ if (unit) {
+ /* multiply by unit and convert to microseconds */
+ range->min = (range->min*unit)/1000;
+ range->max = (range->max*unit + 999)/1000; /* round up */
+ }
+}
+
+static void img_ir_symbol_timing_preprocess(struct img_ir_symbol_timing *timing,
+ unsigned int unit)
+{
+ img_ir_timing_preprocess(&timing->pulse, unit);
+ img_ir_timing_preprocess(&timing->space, unit);
+}
+
+static void img_ir_timings_preprocess(struct img_ir_timings *timings,
+ unsigned int unit)
+{
+ img_ir_symbol_timing_preprocess(&timings->ldr, unit);
+ img_ir_symbol_timing_preprocess(&timings->s00, unit);
+ img_ir_symbol_timing_preprocess(&timings->s01, unit);
+ img_ir_symbol_timing_preprocess(&timings->s10, unit);
+ img_ir_symbol_timing_preprocess(&timings->s11, unit);
+ /* default s10 and s11 to s00 and s01 if no leader */
+ if (unit)
+ /* multiply by unit and convert to microseconds (round up) */
+ timings->ft.ft_min = (timings->ft.ft_min*unit + 999)/1000;
+}
+
+/* functions for filling empty fields with defaults */
+
+static void img_ir_timing_defaults(struct img_ir_timing_range *range,
+ struct img_ir_timing_range *defaults)
+{
+ if (!range->min)
+ range->min = defaults->min;
+ if (!range->max)
+ range->max = defaults->max;
+}
+
+static void img_ir_symbol_timing_defaults(struct img_ir_symbol_timing *timing,
+ struct img_ir_symbol_timing *defaults)
+{
+ img_ir_timing_defaults(&timing->pulse, &defaults->pulse);
+ img_ir_timing_defaults(&timing->space, &defaults->space);
+}
+
+static void img_ir_timings_defaults(struct img_ir_timings *timings,
+ struct img_ir_timings *defaults)
+{
+ img_ir_symbol_timing_defaults(&timings->ldr, &defaults->ldr);
+ img_ir_symbol_timing_defaults(&timings->s00, &defaults->s00);
+ img_ir_symbol_timing_defaults(&timings->s01, &defaults->s01);
+ img_ir_symbol_timing_defaults(&timings->s10, &defaults->s10);
+ img_ir_symbol_timing_defaults(&timings->s11, &defaults->s11);
+ if (!timings->ft.ft_min)
+ timings->ft.ft_min = defaults->ft.ft_min;
+}
+
+/* functions for converting timings to register values */
+
+/**
+ * img_ir_control() - Convert control struct to control register value.
+ * @control: Control data
+ *
+ * Returns: The control register value equivalent of @control.
+ */
+static u32 img_ir_control(const struct img_ir_control *control)
+{
+ u32 ctrl = control->code_type << IMG_IR_CODETYPE_SHIFT;
+ if (control->decoden)
+ ctrl |= IMG_IR_DECODEN;
+ if (control->hdrtog)
+ ctrl |= IMG_IR_HDRTOG;
+ if (control->ldrdec)
+ ctrl |= IMG_IR_LDRDEC;
+ if (control->decodinpol)
+ ctrl |= IMG_IR_DECODINPOL;
+ if (control->bitorien)
+ ctrl |= IMG_IR_BITORIEN;
+ if (control->d1validsel)
+ ctrl |= IMG_IR_D1VALIDSEL;
+ if (control->bitinv)
+ ctrl |= IMG_IR_BITINV;
+ if (control->decodend2)
+ ctrl |= IMG_IR_DECODEND2;
+ if (control->bitoriend2)
+ ctrl |= IMG_IR_BITORIEND2;
+ if (control->bitinvd2)
+ ctrl |= IMG_IR_BITINVD2;
+ return ctrl;
+}
+
+/**
+ * img_ir_timing_range_convert() - Convert microsecond range.
+ * @out: Output timing range in clock cycles with a shift.
+ * @in: Input timing range in microseconds.
+ * @tolerance: Tolerance as a fraction of 128 (roughly percent).
+ * @clock_hz: IR clock rate in Hz.
+ * @shift: Shift of output units.
+ *
+ * Converts min and max from microseconds to IR clock cycles, applies a
+ * tolerance, and shifts for the register, rounding in the right direction.
+ * Note that in and out can safely be the same object.
+ */
+static void img_ir_timing_range_convert(struct img_ir_timing_range *out,
+ const struct img_ir_timing_range *in,
+ unsigned int tolerance,
+ unsigned long clock_hz,
+ unsigned int shift)
+{
+ unsigned int min = in->min;
+ unsigned int max = in->max;
+ /* add a tolerance */
+ min = min - (min*tolerance >> 7);
+ max = max + (max*tolerance >> 7);
+ /* convert from microseconds into clock cycles */
+ min = min*clock_hz / 1000000;
+ max = (max*clock_hz + 999999) / 1000000; /* round up */
+ /* apply shift and copy to output */
+ out->min = min >> shift;
+ out->max = (max + ((1 << shift) - 1)) >> shift; /* round up */
+}
+
+/**
+ * img_ir_symbol_timing() - Convert symbol timing struct to register value.
+ * @timing: Symbol timing data
+ * @tolerance: Timing tolerance where 0-128 represents 0-100%
+ * @clock_hz: Frequency of source clock in Hz
+ * @pd_shift: Shift to apply to symbol period
+ * @w_shift: Shift to apply to symbol width
+ *
+ * Returns: Symbol timing register value based on arguments.
+ */
+static u32 img_ir_symbol_timing(const struct img_ir_symbol_timing *timing,
+ unsigned int tolerance,
+ unsigned long clock_hz,
+ unsigned int pd_shift,
+ unsigned int w_shift)
+{
+ struct img_ir_timing_range hw_pulse, hw_period;
+ /* we calculate period in hw_period, then convert in place */
+ hw_period.min = timing->pulse.min + timing->space.min;
+ hw_period.max = timing->pulse.max + timing->space.max;
+ img_ir_timing_range_convert(&hw_period, &hw_period,
+ tolerance, clock_hz, pd_shift);
+ img_ir_timing_range_convert(&hw_pulse, &timing->pulse,
+ tolerance, clock_hz, w_shift);
+ /* construct register value */
+ return (hw_period.max << IMG_IR_PD_MAX_SHIFT) |
+ (hw_period.min << IMG_IR_PD_MIN_SHIFT) |
+ (hw_pulse.max << IMG_IR_W_MAX_SHIFT) |
+ (hw_pulse.min << IMG_IR_W_MIN_SHIFT);
+}
+
+/**
+ * img_ir_free_timing() - Convert free time timing struct to register value.
+ * @timing: Free symbol timing data
+ * @clock_hz: Source clock frequency in Hz
+ *
+ * Returns: Free symbol timing register value.
+ */
+static u32 img_ir_free_timing(const struct img_ir_free_timing *timing,
+ unsigned long clock_hz)
+{
+ unsigned int minlen, maxlen, ft_min;
+ /* minlen is only 5 bits, and round minlen to multiple of 2 */
+ if (timing->minlen < 30)
+ minlen = timing->minlen & -2;
+ else
+ minlen = 30;
+ /* maxlen has maximum value of 48, and round maxlen to multiple of 2 */
+ if (timing->maxlen < 48)
+ maxlen = (timing->maxlen + 1) & -2;
+ else
+ maxlen = 48;
+ /* convert and shift ft_min, rounding upwards */
+ ft_min = (timing->ft_min*clock_hz + 999999) / 1000000;
+ ft_min = (ft_min + 7) >> 3;
+ /* construct register value */
+ return (maxlen << IMG_IR_MAXLEN_SHIFT) |
+ (minlen << IMG_IR_MINLEN_SHIFT) |
+ (ft_min << IMG_IR_FT_MIN_SHIFT);
+}
+
+/**
+ * img_ir_free_timing_dynamic() - Update free time register value.
+ * @st_ft: Static free time register value from img_ir_free_timing.
+ * @filter: Current filter which may additionally restrict min/max len.
+ *
+ * Returns: Updated free time register value based on the current filter.
+ */
+static u32 img_ir_free_timing_dynamic(u32 st_ft, struct img_ir_filter *filter)
+{
+ unsigned int minlen, maxlen, newminlen, newmaxlen;
+
+ /* round minlen, maxlen to multiple of 2 */
+ newminlen = filter->minlen & -2;
+ newmaxlen = (filter->maxlen + 1) & -2;
+ /* extract min/max len from register */
+ minlen = (st_ft & IMG_IR_MINLEN) >> IMG_IR_MINLEN_SHIFT;
+ maxlen = (st_ft & IMG_IR_MAXLEN) >> IMG_IR_MAXLEN_SHIFT;
+ /* if the new values are more restrictive, update the register value */
+ if (newminlen > minlen) {
+ st_ft &= ~IMG_IR_MINLEN;
+ st_ft |= newminlen << IMG_IR_MINLEN_SHIFT;
+ }
+ if (newmaxlen < maxlen) {
+ st_ft &= ~IMG_IR_MAXLEN;
+ st_ft |= newmaxlen << IMG_IR_MAXLEN_SHIFT;
+ }
+ return st_ft;
+}
+
+/**
+ * img_ir_timings_convert() - Convert timings to register values
+ * @regs: Output timing register values
+ * @timings: Input timing data
+ * @tolerance: Timing tolerance where 0-128 represents 0-100%
+ * @clock_hz: Source clock frequency in Hz
+ */
+static void img_ir_timings_convert(struct img_ir_timing_regvals *regs,
+ const struct img_ir_timings *timings,
+ unsigned int tolerance,
+ unsigned int clock_hz)
+{
+ /* leader symbol timings are divided by 16 */
+ regs->ldr = img_ir_symbol_timing(&timings->ldr, tolerance, clock_hz,
+ 4, 4);
+ /* other symbol timings, pd fields only are divided by 2 */
+ regs->s00 = img_ir_symbol_timing(&timings->s00, tolerance, clock_hz,
+ 1, 0);
+ regs->s01 = img_ir_symbol_timing(&timings->s01, tolerance, clock_hz,
+ 1, 0);
+ regs->s10 = img_ir_symbol_timing(&timings->s10, tolerance, clock_hz,
+ 1, 0);
+ regs->s11 = img_ir_symbol_timing(&timings->s11, tolerance, clock_hz,
+ 1, 0);
+ regs->ft = img_ir_free_timing(&timings->ft, clock_hz);
+}
+
+/**
+ * img_ir_decoder_preprocess() - Preprocess timings in decoder.
+ * @decoder: Decoder to be preprocessed.
+ *
+ * Ensures that the symbol timing ranges are valid with respect to ordering, and
+ * does some fixed conversion on them.
+ */
+static void img_ir_decoder_preprocess(struct img_ir_decoder *decoder)
+{
+ /* default tolerance */
+ if (!decoder->tolerance)
+ decoder->tolerance = 10; /* percent */
+ /* and convert tolerance to fraction out of 128 */
+ decoder->tolerance = decoder->tolerance * 128 / 100;
+
+ /* fill in implicit fields */
+ img_ir_timings_preprocess(&decoder->timings, decoder->unit);
+
+ /* do the same for repeat timings if applicable */
+ if (decoder->repeat) {
+ img_ir_timings_preprocess(&decoder->rtimings, decoder->unit);
+ img_ir_timings_defaults(&decoder->rtimings, &decoder->timings);
+ }
+}
+
+/**
+ * img_ir_decoder_convert() - Generate internal timings in decoder.
+ * @decoder: Decoder to be converted to internal timings.
+ * @timings: Timing register values.
+ * @clock_hz: IR clock rate in Hz.
+ *
+ * Fills out the repeat timings and timing register values for a specific clock
+ * rate.
+ */
+static void img_ir_decoder_convert(const struct img_ir_decoder *decoder,
+ struct img_ir_reg_timings *reg_timings,
+ unsigned int clock_hz)
+{
+ /* calculate control value */
+ reg_timings->ctrl = img_ir_control(&decoder->control);
+
+ /* fill in implicit fields and calculate register values */
+ img_ir_timings_convert(&reg_timings->timings, &decoder->timings,
+ decoder->tolerance, clock_hz);
+
+ /* do the same for repeat timings if applicable */
+ if (decoder->repeat)
+ img_ir_timings_convert(&reg_timings->rtimings,
+ &decoder->rtimings, decoder->tolerance,
+ clock_hz);
+}
+
+/**
+ * img_ir_write_timings() - Write timings to the hardware now
+ * @priv: IR private data
+ * @regs: Timing register values to write
+ * @type: RC filter type (RC_FILTER_*)
+ *
+ * Write timing register values @regs to the hardware, taking into account the
+ * current filter which may impose restrictions on the length of the expected
+ * data.
+ */
+static void img_ir_write_timings(struct img_ir_priv *priv,
+ struct img_ir_timing_regvals *regs,
+ enum rc_filter_type type)
+{
+ struct img_ir_priv_hw *hw = &priv->hw;
+
+ /* filter may be more restrictive to minlen, maxlen */
+ u32 ft = regs->ft;
+ if (hw->flags & BIT(type))
+ ft = img_ir_free_timing_dynamic(regs->ft, &hw->filters[type]);
+ /* write to registers */
+ img_ir_write(priv, IMG_IR_LEAD_SYMB_TIMING, regs->ldr);
+ img_ir_write(priv, IMG_IR_S00_SYMB_TIMING, regs->s00);
+ img_ir_write(priv, IMG_IR_S01_SYMB_TIMING, regs->s01);
+ img_ir_write(priv, IMG_IR_S10_SYMB_TIMING, regs->s10);
+ img_ir_write(priv, IMG_IR_S11_SYMB_TIMING, regs->s11);
+ img_ir_write(priv, IMG_IR_FREE_SYMB_TIMING, ft);
+ dev_dbg(priv->dev, "timings: ldr=%#x, s=[%#x, %#x, %#x, %#x], ft=%#x\n",
+ regs->ldr, regs->s00, regs->s01, regs->s10, regs->s11, ft);
+}
+
+static void img_ir_write_filter(struct img_ir_priv *priv,
+ struct img_ir_filter *filter)
+{
+ if (filter) {
+ dev_dbg(priv->dev, "IR filter=%016llx & %016llx\n",
+ (unsigned long long)filter->data,
+ (unsigned long long)filter->mask);
+ img_ir_write(priv, IMG_IR_IRQ_MSG_DATA_LW, (u32)filter->data);
+ img_ir_write(priv, IMG_IR_IRQ_MSG_DATA_UP, (u32)(filter->data
+ >> 32));
+ img_ir_write(priv, IMG_IR_IRQ_MSG_MASK_LW, (u32)filter->mask);
+ img_ir_write(priv, IMG_IR_IRQ_MSG_MASK_UP, (u32)(filter->mask
+ >> 32));
+ } else {
+ dev_dbg(priv->dev, "IR clearing filter\n");
+ img_ir_write(priv, IMG_IR_IRQ_MSG_MASK_LW, 0);
+ img_ir_write(priv, IMG_IR_IRQ_MSG_MASK_UP, 0);
+ }
+}
+
+/* caller must have lock */
+static void _img_ir_set_filter(struct img_ir_priv *priv,
+ struct img_ir_filter *filter)
+{
+ struct img_ir_priv_hw *hw = &priv->hw;
+ u32 irq_en, irq_on;
+
+ irq_en = img_ir_read(priv, IMG_IR_IRQ_ENABLE);
+ if (filter) {
+ /* Only use the match interrupt */
+ hw->filters[RC_FILTER_NORMAL] = *filter;
+ hw->flags |= IMG_IR_F_FILTER;
+ irq_on = IMG_IR_IRQ_DATA_MATCH;
+ irq_en &= ~(IMG_IR_IRQ_DATA_VALID | IMG_IR_IRQ_DATA2_VALID);
+ } else {
+ /* Only use the valid interrupt */
+ hw->flags &= ~IMG_IR_F_FILTER;
+ irq_en &= ~IMG_IR_IRQ_DATA_MATCH;
+ irq_on = IMG_IR_IRQ_DATA_VALID | IMG_IR_IRQ_DATA2_VALID;
+ }
+ irq_en |= irq_on;
+
+ img_ir_write_filter(priv, filter);
+ /* clear any interrupts we're enabling so we don't handle old ones */
+ img_ir_write(priv, IMG_IR_IRQ_CLEAR, irq_on);
+ img_ir_write(priv, IMG_IR_IRQ_ENABLE, irq_en);
+}
+
+/* caller must have lock */
+static void _img_ir_set_wake_filter(struct img_ir_priv *priv,
+ struct img_ir_filter *filter)
+{
+ struct img_ir_priv_hw *hw = &priv->hw;
+ if (filter) {
+ /* Enable wake, and copy filter for later */
+ hw->filters[RC_FILTER_WAKEUP] = *filter;
+ hw->flags |= IMG_IR_F_WAKE;
+ } else {
+ /* Disable wake */
+ hw->flags &= ~IMG_IR_F_WAKE;
+ }
+}
+
+/* Callback for setting scancode filter */
+static int img_ir_set_filter(struct rc_dev *dev, enum rc_filter_type type,
+ struct rc_scancode_filter *sc_filter)
+{
+ struct img_ir_priv *priv = dev->priv;
+ struct img_ir_priv_hw *hw = &priv->hw;
+ struct img_ir_filter filter, *filter_ptr = &filter;
+ int ret = 0;
+
+ dev_dbg(priv->dev, "IR scancode %sfilter=%08x & %08x\n",
+ type == RC_FILTER_WAKEUP ? "wake " : "",
+ sc_filter->data,
+ sc_filter->mask);
+
+ spin_lock_irq(&priv->lock);
+
+ /* filtering can always be disabled */
+ if (!sc_filter->mask) {
+ filter_ptr = NULL;
+ goto set_unlock;
+ }
+
+ /* current decoder must support scancode filtering */
+ if (!hw->decoder || !hw->decoder->filter) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ /* convert scancode filter to raw filter */
+ filter.minlen = 0;
+ filter.maxlen = ~0;
+ ret = hw->decoder->filter(sc_filter, &filter, hw->enabled_protocols);
+ if (ret)
+ goto unlock;
+ dev_dbg(priv->dev, "IR raw %sfilter=%016llx & %016llx\n",
+ type == RC_FILTER_WAKEUP ? "wake " : "",
+ (unsigned long long)filter.data,
+ (unsigned long long)filter.mask);
+
+set_unlock:
+ /* apply raw filters */
+ switch (type) {
+ case RC_FILTER_NORMAL:
+ _img_ir_set_filter(priv, filter_ptr);
+ break;
+ case RC_FILTER_WAKEUP:
+ _img_ir_set_wake_filter(priv, filter_ptr);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+unlock:
+ spin_unlock_irq(&priv->lock);
+ return ret;
+}
+
+static int img_ir_set_normal_filter(struct rc_dev *dev,
+ struct rc_scancode_filter *sc_filter)
+{
+ return img_ir_set_filter(dev, RC_FILTER_NORMAL, sc_filter);
+}
+
+static int img_ir_set_wakeup_filter(struct rc_dev *dev,
+ struct rc_scancode_filter *sc_filter)
+{
+ return img_ir_set_filter(dev, RC_FILTER_WAKEUP, sc_filter);
+}
+
+/**
+ * img_ir_set_decoder() - Set the current decoder.
+ * @priv: IR private data.
+ * @decoder: Decoder to use with immediate effect.
+ * @proto: Protocol bitmap (or 0 to use decoder->type).
+ */
+static void img_ir_set_decoder(struct img_ir_priv *priv,
+ const struct img_ir_decoder *decoder,
+ u64 proto)
+{
+ struct img_ir_priv_hw *hw = &priv->hw;
+ struct rc_dev *rdev = hw->rdev;
+ u32 ir_status, irq_en;
+ spin_lock_irq(&priv->lock);
+
+ /* switch off and disable interrupts */
+ img_ir_write(priv, IMG_IR_CONTROL, 0);
+ irq_en = img_ir_read(priv, IMG_IR_IRQ_ENABLE);
+ img_ir_write(priv, IMG_IR_IRQ_ENABLE, irq_en & IMG_IR_IRQ_EDGE);
+ img_ir_write(priv, IMG_IR_IRQ_CLEAR, IMG_IR_IRQ_ALL & ~IMG_IR_IRQ_EDGE);
+
+ /* ack any data already detected */
+ ir_status = img_ir_read(priv, IMG_IR_STATUS);
+ if (ir_status & (IMG_IR_RXDVAL | IMG_IR_RXDVALD2)) {
+ ir_status &= ~(IMG_IR_RXDVAL | IMG_IR_RXDVALD2);
+ img_ir_write(priv, IMG_IR_STATUS, ir_status);
+ img_ir_read(priv, IMG_IR_DATA_LW);
+ img_ir_read(priv, IMG_IR_DATA_UP);
+ }
+
+ /* stop the end timer and switch back to normal mode */
+ del_timer_sync(&hw->end_timer);
+ hw->mode = IMG_IR_M_NORMAL;
+
+ /* clear the wakeup scancode filter */
+ rdev->scancode_filters[RC_FILTER_WAKEUP].data = 0;
+ rdev->scancode_filters[RC_FILTER_WAKEUP].mask = 0;
+
+ /* clear raw filters */
+ _img_ir_set_filter(priv, NULL);
+ _img_ir_set_wake_filter(priv, NULL);
+
+ /* clear the enabled protocols */
+ hw->enabled_protocols = 0;
+
+ /* switch decoder */
+ hw->decoder = decoder;
+ if (!decoder)
+ goto unlock;
+
+ /* set the enabled protocols */
+ if (!proto)
+ proto = decoder->type;
+ hw->enabled_protocols = proto;
+
+ /* write the new timings */
+ img_ir_decoder_convert(decoder, &hw->reg_timings, hw->clk_hz);
+ img_ir_write_timings(priv, &hw->reg_timings.timings, RC_FILTER_NORMAL);
+
+ /* set up and enable */
+ img_ir_write(priv, IMG_IR_CONTROL, hw->reg_timings.ctrl);
+
+
+unlock:
+ spin_unlock_irq(&priv->lock);
+}
+
+/**
+ * img_ir_decoder_compatable() - Find whether a decoder will work with a device.
+ * @priv: IR private data.
+ * @dec: Decoder to check.
+ *
+ * Returns: true if @dec is compatible with the device @priv refers to.
+ */
+static bool img_ir_decoder_compatible(struct img_ir_priv *priv,
+ const struct img_ir_decoder *dec)
+{
+ unsigned int ct;
+
+ /* don't accept decoders using code types which aren't supported */
+ ct = dec->control.code_type;
+ if (priv->hw.ct_quirks[ct] & IMG_IR_QUIRK_CODE_BROKEN)
+ return false;
+
+ return true;
+}
+
+/**
+ * img_ir_allowed_protos() - Get allowed protocols from global decoder list.
+ * @priv: IR private data.
+ *
+ * Returns: Mask of protocols supported by the device @priv refers to.
+ */
+static u64 img_ir_allowed_protos(struct img_ir_priv *priv)
+{
+ u64 protos = 0;
+ struct img_ir_decoder **decp;
+
+ for (decp = img_ir_decoders; *decp; ++decp) {
+ const struct img_ir_decoder *dec = *decp;
+ if (img_ir_decoder_compatible(priv, dec))
+ protos |= dec->type;
+ }
+ return protos;
+}
+
+/* Callback for changing protocol using sysfs */
+static int img_ir_change_protocol(struct rc_dev *dev, u64 *ir_type)
+{
+ struct img_ir_priv *priv = dev->priv;
+ struct img_ir_priv_hw *hw = &priv->hw;
+ struct rc_dev *rdev = hw->rdev;
+ struct img_ir_decoder **decp;
+ u64 wakeup_protocols;
+
+ if (!*ir_type) {
+ /* disable all protocols */
+ img_ir_set_decoder(priv, NULL, 0);
+ goto success;
+ }
+ for (decp = img_ir_decoders; *decp; ++decp) {
+ const struct img_ir_decoder *dec = *decp;
+ if (!img_ir_decoder_compatible(priv, dec))
+ continue;
+ if (*ir_type & dec->type) {
+ *ir_type &= dec->type;
+ img_ir_set_decoder(priv, dec, *ir_type);
+ goto success;
+ }
+ }
+ return -EINVAL;
+
+success:
+ /*
+ * Only allow matching wakeup protocols for now, and only if filtering
+ * is supported.
+ */
+ wakeup_protocols = *ir_type;
+ if (!hw->decoder || !hw->decoder->filter)
+ wakeup_protocols = 0;
+ rc_set_allowed_wakeup_protocols(rdev, wakeup_protocols);
+ rc_set_enabled_wakeup_protocols(rdev, wakeup_protocols);
+ return 0;
+}
+
+/* Changes ir-core protocol device attribute */
+static void img_ir_set_protocol(struct img_ir_priv *priv, u64 proto)
+{
+ struct rc_dev *rdev = priv->hw.rdev;
+
+ spin_lock_irq(&rdev->rc_map.lock);
+ rdev->rc_map.rc_type = __ffs64(proto);
+ spin_unlock_irq(&rdev->rc_map.lock);
+
+ mutex_lock(&rdev->lock);
+ rc_set_enabled_protocols(rdev, proto);
+ rc_set_allowed_wakeup_protocols(rdev, proto);
+ rc_set_enabled_wakeup_protocols(rdev, proto);
+ mutex_unlock(&rdev->lock);
+}
+
+/* Set up IR decoders */
+static void img_ir_init_decoders(void)
+{
+ struct img_ir_decoder **decp;
+
+ spin_lock(&img_ir_decoders_lock);
+ if (!img_ir_decoders_preprocessed) {
+ for (decp = img_ir_decoders; *decp; ++decp)
+ img_ir_decoder_preprocess(*decp);
+ img_ir_decoders_preprocessed = true;
+ }
+ spin_unlock(&img_ir_decoders_lock);
+}
+
+#ifdef CONFIG_PM_SLEEP
+/**
+ * img_ir_enable_wake() - Switch to wake mode.
+ * @priv: IR private data.
+ *
+ * Returns: non-zero if the IR can wake the system.
+ */
+static int img_ir_enable_wake(struct img_ir_priv *priv)
+{
+ struct img_ir_priv_hw *hw = &priv->hw;
+ int ret = 0;
+
+ spin_lock_irq(&priv->lock);
+ if (hw->flags & IMG_IR_F_WAKE) {
+ /* interrupt only on a match */
+ hw->suspend_irqen = img_ir_read(priv, IMG_IR_IRQ_ENABLE);
+ img_ir_write(priv, IMG_IR_IRQ_ENABLE, IMG_IR_IRQ_DATA_MATCH);
+ img_ir_write_filter(priv, &hw->filters[RC_FILTER_WAKEUP]);
+ img_ir_write_timings(priv, &hw->reg_timings.timings,
+ RC_FILTER_WAKEUP);
+ hw->mode = IMG_IR_M_WAKE;
+ ret = 1;
+ }
+ spin_unlock_irq(&priv->lock);
+ return ret;
+}
+
+/**
+ * img_ir_disable_wake() - Switch out of wake mode.
+ * @priv: IR private data
+ *
+ * Returns: 1 if the hardware should be allowed to wake from a sleep state.
+ * 0 otherwise.
+ */
+static int img_ir_disable_wake(struct img_ir_priv *priv)
+{
+ struct img_ir_priv_hw *hw = &priv->hw;
+ int ret = 0;
+
+ spin_lock_irq(&priv->lock);
+ if (hw->flags & IMG_IR_F_WAKE) {
+ /* restore normal filtering */
+ if (hw->flags & IMG_IR_F_FILTER) {
+ img_ir_write(priv, IMG_IR_IRQ_ENABLE,
+ (hw->suspend_irqen & IMG_IR_IRQ_EDGE) |
+ IMG_IR_IRQ_DATA_MATCH);
+ img_ir_write_filter(priv,
+ &hw->filters[RC_FILTER_NORMAL]);
+ } else {
+ img_ir_write(priv, IMG_IR_IRQ_ENABLE,
+ (hw->suspend_irqen & IMG_IR_IRQ_EDGE) |
+ IMG_IR_IRQ_DATA_VALID |
+ IMG_IR_IRQ_DATA2_VALID);
+ img_ir_write_filter(priv, NULL);
+ }
+ img_ir_write_timings(priv, &hw->reg_timings.timings,
+ RC_FILTER_NORMAL);
+ hw->mode = IMG_IR_M_NORMAL;
+ ret = 1;
+ }
+ spin_unlock_irq(&priv->lock);
+ return ret;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+/* lock must be held */
+static void img_ir_begin_repeat(struct img_ir_priv *priv)
+{
+ struct img_ir_priv_hw *hw = &priv->hw;
+ if (hw->mode == IMG_IR_M_NORMAL) {
+ /* switch to repeat timings */
+ img_ir_write(priv, IMG_IR_CONTROL, 0);
+ hw->mode = IMG_IR_M_REPEATING;
+ img_ir_write_timings(priv, &hw->reg_timings.rtimings,
+ RC_FILTER_NORMAL);
+ img_ir_write(priv, IMG_IR_CONTROL, hw->reg_timings.ctrl);
+ }
+}
+
+/* lock must be held */
+static void img_ir_end_repeat(struct img_ir_priv *priv)
+{
+ struct img_ir_priv_hw *hw = &priv->hw;
+ if (hw->mode == IMG_IR_M_REPEATING) {
+ /* switch to normal timings */
+ img_ir_write(priv, IMG_IR_CONTROL, 0);
+ hw->mode = IMG_IR_M_NORMAL;
+ img_ir_write_timings(priv, &hw->reg_timings.timings,
+ RC_FILTER_NORMAL);
+ img_ir_write(priv, IMG_IR_CONTROL, hw->reg_timings.ctrl);
+ }
+}
+
+/* lock must be held */
+static void img_ir_handle_data(struct img_ir_priv *priv, u32 len, u64 raw)
+{
+ struct img_ir_priv_hw *hw = &priv->hw;
+ const struct img_ir_decoder *dec = hw->decoder;
+ int ret = IMG_IR_SCANCODE;
+ int scancode;
+ if (dec->scancode)
+ ret = dec->scancode(len, raw, &scancode, hw->enabled_protocols);
+ else if (len >= 32)
+ scancode = (u32)raw;
+ else if (len < 32)
+ scancode = (u32)raw & ((1 << len)-1);
+ dev_dbg(priv->dev, "data (%u bits) = %#llx\n",
+ len, (unsigned long long)raw);
+ if (ret == IMG_IR_SCANCODE) {
+ dev_dbg(priv->dev, "decoded scan code %#x\n", scancode);
+ rc_keydown(hw->rdev, scancode, 0);
+ img_ir_end_repeat(priv);
+ } else if (ret == IMG_IR_REPEATCODE) {
+ if (hw->mode == IMG_IR_M_REPEATING) {
+ dev_dbg(priv->dev, "decoded repeat code\n");
+ rc_repeat(hw->rdev);
+ } else {
+ dev_dbg(priv->dev, "decoded unexpected repeat code, ignoring\n");
+ }
+ } else {
+ dev_dbg(priv->dev, "decode failed (%d)\n", ret);
+ return;
+ }
+
+
+ if (dec->repeat) {
+ unsigned long interval;
+
+ img_ir_begin_repeat(priv);
+
+ /* update timer, but allowing for 1/8th tolerance */
+ interval = dec->repeat + (dec->repeat >> 3);
+ mod_timer(&hw->end_timer,
+ jiffies + msecs_to_jiffies(interval));
+ }
+}
+
+/* timer function to end waiting for repeat. */
+static void img_ir_end_timer(unsigned long arg)
+{
+ struct img_ir_priv *priv = (struct img_ir_priv *)arg;
+
+ spin_lock_irq(&priv->lock);
+ img_ir_end_repeat(priv);
+ spin_unlock_irq(&priv->lock);
+}
+
+#ifdef CONFIG_COMMON_CLK
+static void img_ir_change_frequency(struct img_ir_priv *priv,
+ struct clk_notifier_data *change)
+{
+ struct img_ir_priv_hw *hw = &priv->hw;
+
+ dev_dbg(priv->dev, "clk changed %lu HZ -> %lu HZ\n",
+ change->old_rate, change->new_rate);
+
+ spin_lock_irq(&priv->lock);
+ if (hw->clk_hz == change->new_rate)
+ goto unlock;
+ hw->clk_hz = change->new_rate;
+ /* refresh current timings */
+ if (hw->decoder) {
+ img_ir_decoder_convert(hw->decoder, &hw->reg_timings,
+ hw->clk_hz);
+ switch (hw->mode) {
+ case IMG_IR_M_NORMAL:
+ img_ir_write_timings(priv, &hw->reg_timings.timings,
+ RC_FILTER_NORMAL);
+ break;
+ case IMG_IR_M_REPEATING:
+ img_ir_write_timings(priv, &hw->reg_timings.rtimings,
+ RC_FILTER_NORMAL);
+ break;
+#ifdef CONFIG_PM_SLEEP
+ case IMG_IR_M_WAKE:
+ img_ir_write_timings(priv, &hw->reg_timings.timings,
+ RC_FILTER_WAKEUP);
+ break;
+#endif
+ }
+ }
+unlock:
+ spin_unlock_irq(&priv->lock);
+}
+
+static int img_ir_clk_notify(struct notifier_block *self, unsigned long action,
+ void *data)
+{
+ struct img_ir_priv *priv = container_of(self, struct img_ir_priv,
+ hw.clk_nb);
+ switch (action) {
+ case POST_RATE_CHANGE:
+ img_ir_change_frequency(priv, data);
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_OK;
+}
+#endif /* CONFIG_COMMON_CLK */
+
+/* called with priv->lock held */
+void img_ir_isr_hw(struct img_ir_priv *priv, u32 irq_status)
+{
+ struct img_ir_priv_hw *hw = &priv->hw;
+ u32 ir_status, len, lw, up;
+ unsigned int ct;
+
+ /* use the current decoder */
+ if (!hw->decoder)
+ return;
+
+ ir_status = img_ir_read(priv, IMG_IR_STATUS);
+ if (!(ir_status & (IMG_IR_RXDVAL | IMG_IR_RXDVALD2)))
+ return;
+ ir_status &= ~(IMG_IR_RXDVAL | IMG_IR_RXDVALD2);
+ img_ir_write(priv, IMG_IR_STATUS, ir_status);
+
+ len = (ir_status & IMG_IR_RXDLEN) >> IMG_IR_RXDLEN_SHIFT;
+ /* some versions report wrong length for certain code types */
+ ct = hw->decoder->control.code_type;
+ if (hw->ct_quirks[ct] & IMG_IR_QUIRK_CODE_LEN_INCR)
+ ++len;
+
+ lw = img_ir_read(priv, IMG_IR_DATA_LW);
+ up = img_ir_read(priv, IMG_IR_DATA_UP);
+ img_ir_handle_data(priv, len, (u64)up << 32 | lw);
+}
+
+void img_ir_setup_hw(struct img_ir_priv *priv)
+{
+ struct img_ir_decoder **decp;
+
+ if (!priv->hw.rdev)
+ return;
+
+ /* Use the first available decoder (or disable stuff if NULL) */
+ for (decp = img_ir_decoders; *decp; ++decp) {
+ const struct img_ir_decoder *dec = *decp;
+ if (img_ir_decoder_compatible(priv, dec)) {
+ img_ir_set_protocol(priv, dec->type);
+ img_ir_set_decoder(priv, dec, 0);
+ return;
+ }
+ }
+ img_ir_set_decoder(priv, NULL, 0);
+}
+
+/**
+ * img_ir_probe_hw_caps() - Probe capabilities of the hardware.
+ * @priv: IR private data.
+ */
+static void img_ir_probe_hw_caps(struct img_ir_priv *priv)
+{
+ struct img_ir_priv_hw *hw = &priv->hw;
+ /*
+ * When a version of the block becomes available without these quirks,
+ * they'll have to depend on the core revision.
+ */
+ hw->ct_quirks[IMG_IR_CODETYPE_PULSELEN]
+ |= IMG_IR_QUIRK_CODE_LEN_INCR;
+ hw->ct_quirks[IMG_IR_CODETYPE_BIPHASE]
+ |= IMG_IR_QUIRK_CODE_BROKEN;
+ hw->ct_quirks[IMG_IR_CODETYPE_2BITPULSEPOS]
+ |= IMG_IR_QUIRK_CODE_BROKEN;
+}
+
+int img_ir_probe_hw(struct img_ir_priv *priv)
+{
+ struct img_ir_priv_hw *hw = &priv->hw;
+ struct rc_dev *rdev;
+ int error;
+
+ /* Ensure hardware decoders have been preprocessed */
+ img_ir_init_decoders();
+
+ /* Probe hardware capabilities */
+ img_ir_probe_hw_caps(priv);
+
+ /* Set up the end timer */
+ setup_timer(&hw->end_timer, img_ir_end_timer, (unsigned long)priv);
+
+ /* Register a clock notifier */
+ if (!IS_ERR(priv->clk)) {
+ hw->clk_hz = clk_get_rate(priv->clk);
+#ifdef CONFIG_COMMON_CLK
+ hw->clk_nb.notifier_call = img_ir_clk_notify;
+ error = clk_notifier_register(priv->clk, &hw->clk_nb);
+ if (error)
+ dev_warn(priv->dev,
+ "failed to register clock notifier\n");
+#endif
+ } else {
+ hw->clk_hz = 32768;
+ }
+
+ /* Allocate hardware decoder */
+ hw->rdev = rdev = rc_allocate_device();
+ if (!rdev) {
+ dev_err(priv->dev, "cannot allocate input device\n");
+ error = -ENOMEM;
+ goto err_alloc_rc;
+ }
+ rdev->priv = priv;
+ rdev->map_name = RC_MAP_EMPTY;
+ rc_set_allowed_protocols(rdev, img_ir_allowed_protos(priv));
+ rdev->input_name = "IMG Infrared Decoder";
+ rdev->s_filter = img_ir_set_normal_filter;
+ rdev->s_wakeup_filter = img_ir_set_wakeup_filter;
+
+ /* Register hardware decoder */
+ error = rc_register_device(rdev);
+ if (error) {
+ dev_err(priv->dev, "failed to register IR input device\n");
+ goto err_register_rc;
+ }
+
+ /*
+ * Set this after rc_register_device as no protocols have been
+ * registered yet.
+ */
+ rdev->change_protocol = img_ir_change_protocol;
+
+ device_init_wakeup(priv->dev, 1);
+
+ return 0;
+
+err_register_rc:
+ img_ir_set_decoder(priv, NULL, 0);
+ hw->rdev = NULL;
+ rc_free_device(rdev);
+err_alloc_rc:
+#ifdef CONFIG_COMMON_CLK
+ if (!IS_ERR(priv->clk))
+ clk_notifier_unregister(priv->clk, &hw->clk_nb);
+#endif
+ return error;
+}
+
+void img_ir_remove_hw(struct img_ir_priv *priv)
+{
+ struct img_ir_priv_hw *hw = &priv->hw;
+ struct rc_dev *rdev = hw->rdev;
+ if (!rdev)
+ return;
+ img_ir_set_decoder(priv, NULL, 0);
+ hw->rdev = NULL;
+ rc_unregister_device(rdev);
+#ifdef CONFIG_COMMON_CLK
+ if (!IS_ERR(priv->clk))
+ clk_notifier_unregister(priv->clk, &hw->clk_nb);
+#endif
+}
+
+#ifdef CONFIG_PM_SLEEP
+int img_ir_suspend(struct device *dev)
+{
+ struct img_ir_priv *priv = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev) && img_ir_enable_wake(priv))
+ enable_irq_wake(priv->irq);
+ return 0;
+}
+
+int img_ir_resume(struct device *dev)
+{
+ struct img_ir_priv *priv = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev) && img_ir_disable_wake(priv))
+ disable_irq_wake(priv->irq);
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
diff --git a/drivers/media/rc/img-ir/img-ir-hw.h b/drivers/media/rc/img-ir/img-ir-hw.h
new file mode 100644
index 00000000000..6c9a94a8119
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-hw.h
@@ -0,0 +1,269 @@
+/*
+ * ImgTec IR Hardware Decoder found in PowerDown Controller.
+ *
+ * Copyright 2010-2014 Imagination Technologies Ltd.
+ */
+
+#ifndef _IMG_IR_HW_H_
+#define _IMG_IR_HW_H_
+
+#include <linux/kernel.h>
+#include <media/rc-core.h>
+
+/* constants */
+
+#define IMG_IR_CODETYPE_PULSELEN 0x0 /* Sony */
+#define IMG_IR_CODETYPE_PULSEDIST 0x1 /* NEC, Toshiba, Micom, Sharp */
+#define IMG_IR_CODETYPE_BIPHASE 0x2 /* RC-5/6 */
+#define IMG_IR_CODETYPE_2BITPULSEPOS 0x3 /* RC-MM */
+
+
+/* Timing information */
+
+/**
+ * struct img_ir_control - Decoder control settings
+ * @decoden: Primary decoder enable
+ * @code_type: Decode type (see IMG_IR_CODETYPE_*)
+ * @hdrtog: Detect header toggle symbol after leader symbol
+ * @ldrdec: Don't discard leader if maximum width reached
+ * @decodinpol: Decoder input polarity (1=active high)
+ * @bitorien: Bit orientation (1=MSB first)
+ * @d1validsel: Decoder 2 takes over if it detects valid data
+ * @bitinv: Bit inversion switch (1=don't invert)
+ * @decodend2: Secondary decoder enable (no leader symbol)
+ * @bitoriend2: Bit orientation (1=MSB first)
+ * @bitinvd2: Secondary decoder bit inversion switch (1=don't invert)
+ */
+struct img_ir_control {
+ unsigned decoden:1;
+ unsigned code_type:2;
+ unsigned hdrtog:1;
+ unsigned ldrdec:1;
+ unsigned decodinpol:1;
+ unsigned bitorien:1;
+ unsigned d1validsel:1;
+ unsigned bitinv:1;
+ unsigned decodend2:1;
+ unsigned bitoriend2:1;
+ unsigned bitinvd2:1;
+};
+
+/**
+ * struct img_ir_timing_range - range of timing values
+ * @min: Minimum timing value
+ * @max: Maximum timing value (if < @min, this will be set to @min during
+ * preprocessing step, so it is normally not explicitly initialised
+ * and is taken care of by the tolerance)
+ */
+struct img_ir_timing_range {
+ u16 min;
+ u16 max;
+};
+
+/**
+ * struct img_ir_symbol_timing - timing data for a symbol
+ * @pulse: Timing range for the length of the pulse in this symbol
+ * @space: Timing range for the length of the space in this symbol
+ */
+struct img_ir_symbol_timing {
+ struct img_ir_timing_range pulse;
+ struct img_ir_timing_range space;
+};
+
+/**
+ * struct img_ir_free_timing - timing data for free time symbol
+ * @minlen: Minimum number of bits of data
+ * @maxlen: Maximum number of bits of data
+ * @ft_min: Minimum free time after message
+ */
+struct img_ir_free_timing {
+ /* measured in bits */
+ u8 minlen;
+ u8 maxlen;
+ u16 ft_min;
+};
+
+/**
+ * struct img_ir_timings - Timing values.
+ * @ldr: Leader symbol timing data
+ * @s00: Zero symbol timing data for primary decoder
+ * @s01: One symbol timing data for primary decoder
+ * @s10: Zero symbol timing data for secondary (no leader symbol) decoder
+ * @s11: One symbol timing data for secondary (no leader symbol) decoder
+ * @ft: Free time symbol timing data
+ */
+struct img_ir_timings {
+ struct img_ir_symbol_timing ldr, s00, s01, s10, s11;
+ struct img_ir_free_timing ft;
+};
+
+/**
+ * struct img_ir_filter - Filter IR events.
+ * @data: Data to match.
+ * @mask: Mask of bits to compare.
+ * @minlen: Additional minimum number of bits.
+ * @maxlen: Additional maximum number of bits.
+ */
+struct img_ir_filter {
+ u64 data;
+ u64 mask;
+ u8 minlen;
+ u8 maxlen;
+};
+
+/**
+ * struct img_ir_timing_regvals - Calculated timing register values.
+ * @ldr: Leader symbol timing register value
+ * @s00: Zero symbol timing register value for primary decoder
+ * @s01: One symbol timing register value for primary decoder
+ * @s10: Zero symbol timing register value for secondary decoder
+ * @s11: One symbol timing register value for secondary decoder
+ * @ft: Free time symbol timing register value
+ */
+struct img_ir_timing_regvals {
+ u32 ldr, s00, s01, s10, s11, ft;
+};
+
+#define IMG_IR_SCANCODE 0 /* new scancode */
+#define IMG_IR_REPEATCODE 1 /* repeat the previous code */
+
+/**
+ * struct img_ir_decoder - Decoder settings for an IR protocol.
+ * @type: Protocol types bitmap.
+ * @tolerance: Timing tolerance as a percentage (default 10%).
+ * @unit: Unit of timings in nanoseconds (default 1 us).
+ * @timings: Primary timings
+ * @rtimings: Additional override timings while waiting for repeats.
+ * @repeat: Maximum repeat interval (always in milliseconds).
+ * @control: Control flags.
+ *
+ * @scancode: Pointer to function to convert the IR data into a scancode (it
+ * must be safe to execute in interrupt context).
+ * Returns IMG_IR_SCANCODE to emit new scancode.
+ * Returns IMG_IR_REPEATCODE to repeat previous code.
+ * Returns -errno (e.g. -EINVAL) on error.
+ * @filter: Pointer to function to convert scancode filter to raw hardware
+ * filter. The minlen and maxlen fields will have been initialised
+ * to the maximum range.
+ */
+struct img_ir_decoder {
+ /* core description */
+ u64 type;
+ unsigned int tolerance;
+ unsigned int unit;
+ struct img_ir_timings timings;
+ struct img_ir_timings rtimings;
+ unsigned int repeat;
+ struct img_ir_control control;
+
+ /* scancode logic */
+ int (*scancode)(int len, u64 raw, int *scancode, u64 protocols);
+ int (*filter)(const struct rc_scancode_filter *in,
+ struct img_ir_filter *out, u64 protocols);
+};
+
+/**
+ * struct img_ir_reg_timings - Reg values for decoder timings at clock rate.
+ * @ctrl: Processed control register value.
+ * @timings: Processed primary timings.
+ * @rtimings: Processed repeat timings.
+ */
+struct img_ir_reg_timings {
+ u32 ctrl;
+ struct img_ir_timing_regvals timings;
+ struct img_ir_timing_regvals rtimings;
+};
+
+int img_ir_register_decoder(struct img_ir_decoder *dec);
+void img_ir_unregister_decoder(struct img_ir_decoder *dec);
+
+struct img_ir_priv;
+
+#ifdef CONFIG_IR_IMG_HW
+
+enum img_ir_mode {
+ IMG_IR_M_NORMAL,
+ IMG_IR_M_REPEATING,
+#ifdef CONFIG_PM_SLEEP
+ IMG_IR_M_WAKE,
+#endif
+};
+
+/**
+ * struct img_ir_priv_hw - Private driver data for hardware decoder.
+ * @ct_quirks: Quirk bits for each code type.
+ * @rdev: Remote control device
+ * @clk_nb: Notifier block for clock notify events.
+ * @end_timer: Timer until repeat timeout.
+ * @decoder: Current decoder settings.
+ * @enabled_protocols: Currently enabled protocols.
+ * @clk_hz: Current core clock rate in Hz.
+ * @reg_timings: Timing reg values for decoder at clock rate.
+ * @flags: IMG_IR_F_*.
+ * @filters: HW filters (derived from scancode filters).
+ * @mode: Current decode mode.
+ * @suspend_irqen: Saved IRQ enable mask over suspend.
+ */
+struct img_ir_priv_hw {
+ unsigned int ct_quirks[4];
+ struct rc_dev *rdev;
+ struct notifier_block clk_nb;
+ struct timer_list end_timer;
+ const struct img_ir_decoder *decoder;
+ u64 enabled_protocols;
+ unsigned long clk_hz;
+ struct img_ir_reg_timings reg_timings;
+ unsigned int flags;
+ struct img_ir_filter filters[RC_FILTER_MAX];
+
+ enum img_ir_mode mode;
+ u32 suspend_irqen;
+};
+
+static inline bool img_ir_hw_enabled(struct img_ir_priv_hw *hw)
+{
+ return hw->rdev;
+};
+
+void img_ir_isr_hw(struct img_ir_priv *priv, u32 irq_status);
+void img_ir_setup_hw(struct img_ir_priv *priv);
+int img_ir_probe_hw(struct img_ir_priv *priv);
+void img_ir_remove_hw(struct img_ir_priv *priv);
+
+#ifdef CONFIG_PM_SLEEP
+int img_ir_suspend(struct device *dev);
+int img_ir_resume(struct device *dev);
+#else
+#define img_ir_suspend NULL
+#define img_ir_resume NULL
+#endif
+
+#else
+
+struct img_ir_priv_hw {
+};
+
+static inline bool img_ir_hw_enabled(struct img_ir_priv_hw *hw)
+{
+ return false;
+};
+static inline void img_ir_isr_hw(struct img_ir_priv *priv, u32 irq_status)
+{
+}
+static inline void img_ir_setup_hw(struct img_ir_priv *priv)
+{
+}
+static inline int img_ir_probe_hw(struct img_ir_priv *priv)
+{
+ return -ENODEV;
+}
+static inline void img_ir_remove_hw(struct img_ir_priv *priv)
+{
+}
+
+#define img_ir_suspend NULL
+#define img_ir_resume NULL
+
+#endif /* CONFIG_IR_IMG_HW */
+
+#endif /* _IMG_IR_HW_H_ */
diff --git a/drivers/media/rc/img-ir/img-ir-jvc.c b/drivers/media/rc/img-ir/img-ir-jvc.c
new file mode 100644
index 00000000000..10209d200ef
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-jvc.c
@@ -0,0 +1,81 @@
+/*
+ * ImgTec IR Decoder setup for JVC protocol.
+ *
+ * Copyright 2012-2014 Imagination Technologies Ltd.
+ */
+
+#include "img-ir-hw.h"
+
+/* Convert JVC data to a scancode */
+static int img_ir_jvc_scancode(int len, u64 raw, int *scancode, u64 protocols)
+{
+ unsigned int cust, data;
+
+ if (len != 16)
+ return -EINVAL;
+
+ cust = (raw >> 0) & 0xff;
+ data = (raw >> 8) & 0xff;
+
+ *scancode = cust << 8 | data;
+ return IMG_IR_SCANCODE;
+}
+
+/* Convert JVC scancode to JVC data filter */
+static int img_ir_jvc_filter(const struct rc_scancode_filter *in,
+ struct img_ir_filter *out, u64 protocols)
+{
+ unsigned int cust, data;
+ unsigned int cust_m, data_m;
+
+ cust = (in->data >> 8) & 0xff;
+ cust_m = (in->mask >> 8) & 0xff;
+ data = (in->data >> 0) & 0xff;
+ data_m = (in->mask >> 0) & 0xff;
+
+ out->data = cust | data << 8;
+ out->mask = cust_m | data_m << 8;
+
+ return 0;
+}
+
+/*
+ * JVC decoder
+ * See also http://www.sbprojects.com/knowledge/ir/jvc.php
+ * http://support.jvc.com/consumer/support/documents/RemoteCodes.pdf
+ */
+struct img_ir_decoder img_ir_jvc = {
+ .type = RC_BIT_JVC,
+ .control = {
+ .decoden = 1,
+ .code_type = IMG_IR_CODETYPE_PULSEDIST,
+ },
+ /* main timings */
+ .unit = 527500, /* 527.5 us */
+ .timings = {
+ /* leader symbol */
+ .ldr = {
+ .pulse = { 16 /* 8.44 ms */ },
+ .space = { 8 /* 4.22 ms */ },
+ },
+ /* 0 symbol */
+ .s00 = {
+ .pulse = { 1 /* 527.5 us +-60 us */ },
+ .space = { 1 /* 527.5 us */ },
+ },
+ /* 1 symbol */
+ .s01 = {
+ .pulse = { 1 /* 527.5 us +-60 us */ },
+ .space = { 3 /* 1.5825 ms +-40 us */ },
+ },
+ /* free time */
+ .ft = {
+ .minlen = 16,
+ .maxlen = 16,
+ .ft_min = 10, /* 5.275 ms */
+ },
+ },
+ /* scancode logic */
+ .scancode = img_ir_jvc_scancode,
+ .filter = img_ir_jvc_filter,
+};
diff --git a/drivers/media/rc/img-ir/img-ir-nec.c b/drivers/media/rc/img-ir/img-ir-nec.c
new file mode 100644
index 00000000000..751d9d94526
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-nec.c
@@ -0,0 +1,151 @@
+/*
+ * ImgTec IR Decoder setup for NEC protocol.
+ *
+ * Copyright 2010-2014 Imagination Technologies Ltd.
+ */
+
+#include "img-ir-hw.h"
+#include <linux/bitrev.h>
+
+/* Convert NEC data to a scancode */
+static int img_ir_nec_scancode(int len, u64 raw, int *scancode, u64 protocols)
+{
+ unsigned int addr, addr_inv, data, data_inv;
+ /* a repeat code has no data */
+ if (!len)
+ return IMG_IR_REPEATCODE;
+ if (len != 32)
+ return -EINVAL;
+ /* raw encoding: ddDDaaAA */
+ addr = (raw >> 0) & 0xff;
+ addr_inv = (raw >> 8) & 0xff;
+ data = (raw >> 16) & 0xff;
+ data_inv = (raw >> 24) & 0xff;
+ if ((data_inv ^ data) != 0xff) {
+ /* 32-bit NEC (used by Apple and TiVo remotes) */
+ /* scan encoding: as transmitted, MSBit = first received bit */
+ *scancode = bitrev8(addr) << 24 |
+ bitrev8(addr_inv) << 16 |
+ bitrev8(data) << 8 |
+ bitrev8(data_inv);
+ } else if ((addr_inv ^ addr) != 0xff) {
+ /* Extended NEC */
+ /* scan encoding: AAaaDD */
+ *scancode = addr << 16 |
+ addr_inv << 8 |
+ data;
+ } else {
+ /* Normal NEC */
+ /* scan encoding: AADD */
+ *scancode = addr << 8 |
+ data;
+ }
+ return IMG_IR_SCANCODE;
+}
+
+/* Convert NEC scancode to NEC data filter */
+static int img_ir_nec_filter(const struct rc_scancode_filter *in,
+ struct img_ir_filter *out, u64 protocols)
+{
+ unsigned int addr, addr_inv, data, data_inv;
+ unsigned int addr_m, addr_inv_m, data_m, data_inv_m;
+
+ data = in->data & 0xff;
+ data_m = in->mask & 0xff;
+
+ if ((in->data | in->mask) & 0xff000000) {
+ /* 32-bit NEC (used by Apple and TiVo remotes) */
+ /* scan encoding: as transmitted, MSBit = first received bit */
+ addr = bitrev8(in->data >> 24);
+ addr_m = bitrev8(in->mask >> 24);
+ addr_inv = bitrev8(in->data >> 16);
+ addr_inv_m = bitrev8(in->mask >> 16);
+ data = bitrev8(in->data >> 8);
+ data_m = bitrev8(in->mask >> 8);
+ data_inv = bitrev8(in->data >> 0);
+ data_inv_m = bitrev8(in->mask >> 0);
+ } else if ((in->data | in->mask) & 0x00ff0000) {
+ /* Extended NEC */
+ /* scan encoding AAaaDD */
+ addr = (in->data >> 16) & 0xff;
+ addr_m = (in->mask >> 16) & 0xff;
+ addr_inv = (in->data >> 8) & 0xff;
+ addr_inv_m = (in->mask >> 8) & 0xff;
+ data_inv = data ^ 0xff;
+ data_inv_m = data_m;
+ } else {
+ /* Normal NEC */
+ /* scan encoding: AADD */
+ addr = (in->data >> 8) & 0xff;
+ addr_m = (in->mask >> 8) & 0xff;
+ addr_inv = addr ^ 0xff;
+ addr_inv_m = addr_m;
+ data_inv = data ^ 0xff;
+ data_inv_m = data_m;
+ }
+
+ /* raw encoding: ddDDaaAA */
+ out->data = data_inv << 24 |
+ data << 16 |
+ addr_inv << 8 |
+ addr;
+ out->mask = data_inv_m << 24 |
+ data_m << 16 |
+ addr_inv_m << 8 |
+ addr_m;
+ return 0;
+}
+
+/*
+ * NEC decoder
+ * See also http://www.sbprojects.com/knowledge/ir/nec.php
+ * http://wiki.altium.com/display/ADOH/NEC+Infrared+Transmission+Protocol
+ */
+struct img_ir_decoder img_ir_nec = {
+ .type = RC_BIT_NEC,
+ .control = {
+ .decoden = 1,
+ .code_type = IMG_IR_CODETYPE_PULSEDIST,
+ },
+ /* main timings */
+ .unit = 562500, /* 562.5 us */
+ .timings = {
+ /* leader symbol */
+ .ldr = {
+ .pulse = { 16 /* 9ms */ },
+ .space = { 8 /* 4.5ms */ },
+ },
+ /* 0 symbol */
+ .s00 = {
+ .pulse = { 1 /* 562.5 us */ },
+ .space = { 1 /* 562.5 us */ },
+ },
+ /* 1 symbol */
+ .s01 = {
+ .pulse = { 1 /* 562.5 us */ },
+ .space = { 3 /* 1687.5 us */ },
+ },
+ /* free time */
+ .ft = {
+ .minlen = 32,
+ .maxlen = 32,
+ .ft_min = 10, /* 5.625 ms */
+ },
+ },
+ /* repeat codes */
+ .repeat = 108, /* 108 ms */
+ .rtimings = {
+ /* leader symbol */
+ .ldr = {
+ .space = { 4 /* 2.25 ms */ },
+ },
+ /* free time */
+ .ft = {
+ .minlen = 0, /* repeat code has no data */
+ .maxlen = 0,
+ },
+ },
+ /* scancode logic */
+ .scancode = img_ir_nec_scancode,
+ .filter = img_ir_nec_filter,
+};
diff --git a/drivers/media/rc/img-ir/img-ir-raw.c b/drivers/media/rc/img-ir/img-ir-raw.c
new file mode 100644
index 00000000000..cfb01d9e571
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-raw.c
@@ -0,0 +1,151 @@
+/*
+ * ImgTec IR Raw Decoder found in PowerDown Controller.
+ *
+ * Copyright 2010-2014 Imagination Technologies Ltd.
+ *
+ * This ties into the input subsystem using the RC-core in raw mode. Raw IR
+ * signal edges are reported and decoded by generic software decoders.
+ */
+
+#include <linux/spinlock.h>
+#include <media/rc-core.h>
+#include "img-ir.h"
+
+#define ECHO_TIMEOUT_MS 150 /* ms between echos */
+
+/* must be called with priv->lock held */
+static void img_ir_refresh_raw(struct img_ir_priv *priv, u32 irq_status)
+{
+ struct img_ir_priv_raw *raw = &priv->raw;
+ struct rc_dev *rc_dev = priv->raw.rdev;
+ int multiple;
+ u32 ir_status;
+
+ /* find whether both rise and fall was detected */
+ multiple = ((irq_status & IMG_IR_IRQ_EDGE) == IMG_IR_IRQ_EDGE);
+ /*
+ * If so, we need to see if the level has actually changed.
+ * If it's just noise that we didn't have time to process,
+ * there's no point reporting it.
+ */
+ ir_status = img_ir_read(priv, IMG_IR_STATUS) & IMG_IR_IRRXD;
+ if (multiple && ir_status == raw->last_status)
+ return;
+ raw->last_status = ir_status;
+
+ /* report the edge to the IR raw decoders */
+ if (ir_status) /* low */
+ ir_raw_event_store_edge(rc_dev, IR_SPACE);
+ else /* high */
+ ir_raw_event_store_edge(rc_dev, IR_PULSE);
+ ir_raw_event_handle(rc_dev);
+}
+
+/* called with priv->lock held */
+void img_ir_isr_raw(struct img_ir_priv *priv, u32 irq_status)
+{
+ struct img_ir_priv_raw *raw = &priv->raw;
+
+ /* check not removing */
+ if (!raw->rdev)
+ return;
+
+ img_ir_refresh_raw(priv, irq_status);
+
+ /* start / push back the echo timer */
+ mod_timer(&raw->timer, jiffies + msecs_to_jiffies(ECHO_TIMEOUT_MS));
+}
+
+/*
+ * Echo timer callback function.
+ * The raw decoders expect to get a final sample even if there are no edges, in
+ * order to be assured of the final space. If there are no edges for a certain
+ * time we use this timer to emit a final sample to satisfy them.
+ */
+static void img_ir_echo_timer(unsigned long arg)
+{
+ struct img_ir_priv *priv = (struct img_ir_priv *)arg;
+
+ spin_lock_irq(&priv->lock);
+
+ /* check not removing */
+ if (priv->raw.rdev)
+ /*
+ * It's safe to pass irq_status=0 since it's only used to check
+ * for double edges.
+ */
+ img_ir_refresh_raw(priv, 0);
+
+ spin_unlock_irq(&priv->lock);
+}
+
+void img_ir_setup_raw(struct img_ir_priv *priv)
+{
+ u32 irq_en;
+
+ if (!priv->raw.rdev)
+ return;
+
+ /* clear and enable edge interrupts */
+ spin_lock_irq(&priv->lock);
+ irq_en = img_ir_read(priv, IMG_IR_IRQ_ENABLE);
+ irq_en |= IMG_IR_IRQ_EDGE;
+ img_ir_write(priv, IMG_IR_IRQ_CLEAR, IMG_IR_IRQ_EDGE);
+ img_ir_write(priv, IMG_IR_IRQ_ENABLE, irq_en);
+ spin_unlock_irq(&priv->lock);
+}
+
+int img_ir_probe_raw(struct img_ir_priv *priv)
+{
+ struct img_ir_priv_raw *raw = &priv->raw;
+ struct rc_dev *rdev;
+ int error;
+
+ /* Set up the echo timer */
+ setup_timer(&raw->timer, img_ir_echo_timer, (unsigned long)priv);
+
+ /* Allocate raw decoder */
+ raw->rdev = rdev = rc_allocate_device();
+ if (!rdev) {
+ dev_err(priv->dev, "cannot allocate raw input device\n");
+ return -ENOMEM;
+ }
+ rdev->priv = priv;
+ rdev->map_name = RC_MAP_EMPTY;
+ rdev->input_name = "IMG Infrared Decoder Raw";
+ rdev->driver_type = RC_DRIVER_IR_RAW;
+
+ /* Register raw decoder */
+ error = rc_register_device(rdev);
+ if (error) {
+ dev_err(priv->dev, "failed to register raw IR input device\n");
+ rc_free_device(rdev);
+ raw->rdev = NULL;
+ return error;
+ }
+
+ return 0;
+}
+
+void img_ir_remove_raw(struct img_ir_priv *priv)
+{
+ struct img_ir_priv_raw *raw = &priv->raw;
+ struct rc_dev *rdev = raw->rdev;
+ u32 irq_en;
+
+ if (!rdev)
+ return;
+
+ /* switch off and disable raw (edge) interrupts */
+ spin_lock_irq(&priv->lock);
+ raw->rdev = NULL;
+ irq_en = img_ir_read(priv, IMG_IR_IRQ_ENABLE);
+ irq_en &= ~IMG_IR_IRQ_EDGE;
+ img_ir_write(priv, IMG_IR_IRQ_ENABLE, irq_en);
+ img_ir_write(priv, IMG_IR_IRQ_CLEAR, IMG_IR_IRQ_EDGE);
+ spin_unlock_irq(&priv->lock);
+
+ rc_unregister_device(rdev);
+
+ del_timer_sync(&raw->timer);
+}
diff --git a/drivers/media/rc/img-ir/img-ir-raw.h b/drivers/media/rc/img-ir/img-ir-raw.h
new file mode 100644
index 00000000000..9802ffd51b9
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-raw.h
@@ -0,0 +1,60 @@
+/*
+ * ImgTec IR Raw Decoder found in PowerDown Controller.
+ *
+ * Copyright 2010-2014 Imagination Technologies Ltd.
+ */
+
+#ifndef _IMG_IR_RAW_H_
+#define _IMG_IR_RAW_H_
+
+struct img_ir_priv;
+
+#ifdef CONFIG_IR_IMG_RAW
+
+/**
+ * struct img_ir_priv_raw - Private driver data for raw decoder.
+ * @rdev: Raw remote control device
+ * @timer: Timer to echo samples to keep soft decoders happy.
+ * @last_status: Last raw status bits.
+ */
+struct img_ir_priv_raw {
+ struct rc_dev *rdev;
+ struct timer_list timer;
+ u32 last_status;
+};
+
+static inline bool img_ir_raw_enabled(struct img_ir_priv_raw *raw)
+{
+ return raw->rdev;
+};
+
+void img_ir_isr_raw(struct img_ir_priv *priv, u32 irq_status);
+void img_ir_setup_raw(struct img_ir_priv *priv);
+int img_ir_probe_raw(struct img_ir_priv *priv);
+void img_ir_remove_raw(struct img_ir_priv *priv);
+
+#else
+
+struct img_ir_priv_raw {
+};
+static inline bool img_ir_raw_enabled(struct img_ir_priv_raw *raw)
+{
+ return false;
+};
+static inline void img_ir_isr_raw(struct img_ir_priv *priv, u32 irq_status)
+{
+}
+static inline void img_ir_setup_raw(struct img_ir_priv *priv)
+{
+}
+static inline int img_ir_probe_raw(struct img_ir_priv *priv)
+{
+ return -ENODEV;
+}
+static inline void img_ir_remove_raw(struct img_ir_priv *priv)
+{
+}
+
+#endif /* CONFIG_IR_IMG_RAW */
+
+#endif /* _IMG_IR_RAW_H_ */
diff --git a/drivers/media/rc/img-ir/img-ir-sanyo.c b/drivers/media/rc/img-ir/img-ir-sanyo.c
new file mode 100644
index 00000000000..c2c763e08a4
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-sanyo.c
@@ -0,0 +1,122 @@
+/*
+ * ImgTec IR Decoder setup for Sanyo protocol.
+ *
+ * Copyright 2012-2014 Imagination Technologies Ltd.
+ *
+ * From ir-sanyo-decoder.c:
+ *
+ * This protocol uses the NEC protocol timings. However, data is formatted as:
+ * 13 bits Custom Code
+ * 13 bits NOT(Custom Code)
+ * 8 bits Key data
+ * 8 bits NOT(Key data)
+ *
+ * According with LIRC, this protocol is used on Sanyo, Aiwa and Chinon
+ * Information for this protocol is available at the Sanyo LC7461 datasheet.
+ */
+
+#include "img-ir-hw.h"
+
+/* Convert Sanyo data to a scancode */
+static int img_ir_sanyo_scancode(int len, u64 raw, int *scancode, u64 protocols)
+{
+ unsigned int addr, addr_inv, data, data_inv;
+ /* a repeat code has no data */
+ if (!len)
+ return IMG_IR_REPEATCODE;
+ if (len != 42)
+ return -EINVAL;
+ addr = (raw >> 0) & 0x1fff;
+ addr_inv = (raw >> 13) & 0x1fff;
+ data = (raw >> 26) & 0xff;
+ data_inv = (raw >> 34) & 0xff;
+ /* Validate data */
+ if ((data_inv ^ data) != 0xff)
+ return -EINVAL;
+ /* Validate address */
+ if ((addr_inv ^ addr) != 0x1fff)
+ return -EINVAL;
+
+ /* Normal Sanyo */
+ *scancode = addr << 8 | data;
+ return IMG_IR_SCANCODE;
+}
+
+/* Convert Sanyo scancode to Sanyo data filter */
+static int img_ir_sanyo_filter(const struct rc_scancode_filter *in,
+ struct img_ir_filter *out, u64 protocols)
+{
+ unsigned int addr, addr_inv, data, data_inv;
+ unsigned int addr_m, data_m;
+
+ data = in->data & 0xff;
+ data_m = in->mask & 0xff;
+ data_inv = data ^ 0xff;
+
+ if (in->data & 0xff700000)
+ return -EINVAL;
+
+ addr = (in->data >> 8) & 0x1fff;
+ addr_m = (in->mask >> 8) & 0x1fff;
+ addr_inv = addr ^ 0x1fff;
+
+ out->data = (u64)data_inv << 34 |
+ (u64)data << 26 |
+ addr_inv << 13 |
+ addr;
+ out->mask = (u64)data_m << 34 |
+ (u64)data_m << 26 |
+ addr_m << 13 |
+ addr_m;
+ return 0;
+}
+
+/* Sanyo decoder */
+struct img_ir_decoder img_ir_sanyo = {
+ .type = RC_BIT_SANYO,
+ .control = {
+ .decoden = 1,
+ .code_type = IMG_IR_CODETYPE_PULSEDIST,
+ },
+ /* main timings */
+ .unit = 562500, /* 562.5 us */
+ .timings = {
+ /* leader symbol */
+ .ldr = {
+ .pulse = { 16 /* 9ms */ },
+ .space = { 8 /* 4.5ms */ },
+ },
+ /* 0 symbol */
+ .s00 = {
+ .pulse = { 1 /* 562.5 us */ },
+ .space = { 1 /* 562.5 us */ },
+ },
+ /* 1 symbol */
+ .s01 = {
+ .pulse = { 1 /* 562.5 us */ },
+ .space = { 3 /* 1687.5 us */ },
+ },
+ /* free time */
+ .ft = {
+ .minlen = 42,
+ .maxlen = 42,
+ .ft_min = 10, /* 5.625 ms */
+ },
+ },
+ /* repeat codes */
+ .repeat = 108, /* 108 ms */
+ .rtimings = {
+ /* leader symbol */
+ .ldr = {
+ .space = { 4 /* 2.25 ms */ },
+ },
+ /* free time */
+ .ft = {
+ .minlen = 0, /* repeat code has no data */
+ .maxlen = 0,
+ },
+ },
+ /* scancode logic */
+ .scancode = img_ir_sanyo_scancode,
+ .filter = img_ir_sanyo_filter,
+};
diff --git a/drivers/media/rc/img-ir/img-ir-sharp.c b/drivers/media/rc/img-ir/img-ir-sharp.c
new file mode 100644
index 00000000000..3397cc5a679
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-sharp.c
@@ -0,0 +1,99 @@
+/*
+ * ImgTec IR Decoder setup for Sharp protocol.
+ *
+ * Copyright 2012-2014 Imagination Technologies Ltd.
+ */
+
+#include "img-ir-hw.h"
+
+/* Convert Sharp data to a scancode */
+static int img_ir_sharp_scancode(int len, u64 raw, int *scancode, u64 protocols)
+{
+ unsigned int addr, cmd, exp, chk;
+
+ if (len != 15)
+ return -EINVAL;
+
+ addr = (raw >> 0) & 0x1f;
+ cmd = (raw >> 5) & 0xff;
+ exp = (raw >> 13) & 0x1;
+ chk = (raw >> 14) & 0x1;
+
+ /* validate data */
+ if (!exp)
+ return -EINVAL;
+ if (chk)
+ /* probably the second half of the message */
+ return -EINVAL;
+
+ *scancode = addr << 8 | cmd;
+ return IMG_IR_SCANCODE;
+}
+
+/* Convert Sharp scancode to Sharp data filter */
+static int img_ir_sharp_filter(const struct rc_scancode_filter *in,
+ struct img_ir_filter *out, u64 protocols)
+{
+ unsigned int addr, cmd, exp = 0, chk = 0;
+ unsigned int addr_m, cmd_m, exp_m = 0, chk_m = 0;
+
+ addr = (in->data >> 8) & 0x1f;
+ addr_m = (in->mask >> 8) & 0x1f;
+ cmd = (in->data >> 0) & 0xff;
+ cmd_m = (in->mask >> 0) & 0xff;
+ if (cmd_m) {
+ /* if filtering commands, we can only match the first part */
+ exp = 1;
+ exp_m = 1;
+ chk = 0;
+ chk_m = 1;
+ }
+
+ out->data = addr |
+ cmd << 5 |
+ exp << 13 |
+ chk << 14;
+ out->mask = addr_m |
+ cmd_m << 5 |
+ exp_m << 13 |
+ chk_m << 14;
+
+ return 0;
+}
+
+/*
+ * Sharp decoder
+ * See also http://www.sbprojects.com/knowledge/ir/sharp.php
+ */
+struct img_ir_decoder img_ir_sharp = {
+ .type = RC_BIT_SHARP,
+ .control = {
+ .decoden = 0,
+ .decodend2 = 1,
+ .code_type = IMG_IR_CODETYPE_PULSEDIST,
+ .d1validsel = 1,
+ },
+ /* main timings */
+ .tolerance = 20, /* 20% */
+ .timings = {
+ /* 0 symbol */
+ .s10 = {
+ .pulse = { 320 /* 320 us */ },
+ .space = { 680 /* 1 ms period */ },
+ },
+ /* 1 symbol */
+ .s11 = {
+ .pulse = { 320 /* 320 us */ },
+ .space = { 1680 /* 2 ms period */ },
+ },
+ /* free time */
+ .ft = {
+ .minlen = 15,
+ .maxlen = 15,
+ .ft_min = 5000, /* 5 ms */
+ },
+ },
+ /* scancode logic */
+ .scancode = img_ir_sharp_scancode,
+ .filter = img_ir_sharp_filter,
+};
diff --git a/drivers/media/rc/img-ir/img-ir-sony.c b/drivers/media/rc/img-ir/img-ir-sony.c
new file mode 100644
index 00000000000..993409a51a7
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-sony.c
@@ -0,0 +1,145 @@
+/*
+ * ImgTec IR Decoder setup for Sony (SIRC) protocol.
+ *
+ * Copyright 2012-2014 Imagination Technologies Ltd.
+ */
+
+#include "img-ir-hw.h"
+
+/* Convert Sony data to a scancode */
+static int img_ir_sony_scancode(int len, u64 raw, int *scancode, u64 protocols)
+{
+ unsigned int dev, subdev, func;
+
+ switch (len) {
+ case 12:
+ if (!(protocols & RC_BIT_SONY12))
+ return -EINVAL;
+ func = raw & 0x7f; /* first 7 bits */
+ raw >>= 7;
+ dev = raw & 0x1f; /* next 5 bits */
+ subdev = 0;
+ break;
+ case 15:
+ if (!(protocols & RC_BIT_SONY15))
+ return -EINVAL;
+ func = raw & 0x7f; /* first 7 bits */
+ raw >>= 7;
+ dev = raw & 0xff; /* next 8 bits */
+ subdev = 0;
+ break;
+ case 20:
+ if (!(protocols & RC_BIT_SONY20))
+ return -EINVAL;
+ func = raw & 0x7f; /* first 7 bits */
+ raw >>= 7;
+ dev = raw & 0x1f; /* next 5 bits */
+ raw >>= 5;
+ subdev = raw & 0xff; /* next 8 bits */
+ break;
+ default:
+ return -EINVAL;
+ }
+ *scancode = dev << 16 | subdev << 8 | func;
+ return IMG_IR_SCANCODE;
+}
+
+/* Convert NEC scancode to NEC data filter */
+static int img_ir_sony_filter(const struct rc_scancode_filter *in,
+ struct img_ir_filter *out, u64 protocols)
+{
+ unsigned int dev, subdev, func;
+ unsigned int dev_m, subdev_m, func_m;
+ unsigned int len = 0;
+
+ dev = (in->data >> 16) & 0xff;
+ dev_m = (in->mask >> 16) & 0xff;
+ subdev = (in->data >> 8) & 0xff;
+ subdev_m = (in->mask >> 8) & 0xff;
+ func = (in->data >> 0) & 0x7f;
+ func_m = (in->mask >> 0) & 0x7f;
+
+ if (subdev & subdev_m) {
+ /* can't encode subdev and higher device bits */
+ if (dev & dev_m & 0xe0)
+ return -EINVAL;
+ /* subdevice (extended) bits only in 20 bit encoding */
+ if (!(protocols & RC_BIT_SONY20))
+ return -EINVAL;
+ len = 20;
+ dev_m &= 0x1f;
+ } else if (dev & dev_m & 0xe0) {
+ /* upper device bits only in 15 bit encoding */
+ if (!(protocols & RC_BIT_SONY15))
+ return -EINVAL;
+ len = 15;
+ subdev_m = 0;
+ } else {
+ /*
+ * The hardware mask cannot distinguish high device bits and low
+ * extended bits, so logically AND those bits of the masks
+ * together.
+ */
+ subdev_m &= (dev_m >> 5) | 0xf8;
+ dev_m &= 0x1f;
+ }
+
+ /* ensure there aren't any bits straying between fields */
+ dev &= dev_m;
+ subdev &= subdev_m;
+
+ /* write the hardware filter */
+ out->data = func |
+ dev << 7 |
+ subdev << 15;
+ out->mask = func_m |
+ dev_m << 7 |
+ subdev_m << 15;
+
+ if (len) {
+ out->minlen = len;
+ out->maxlen = len;
+ }
+ return 0;
+}
+
+/*
+ * Sony SIRC decoder
+ * See also http://www.sbprojects.com/knowledge/ir/sirc.php
+ * http://picprojects.org.uk/projects/sirc/sonysirc.pdf
+ */
+struct img_ir_decoder img_ir_sony = {
+ .type = RC_BIT_SONY12 | RC_BIT_SONY15 | RC_BIT_SONY20,
+ .control = {
+ .decoden = 1,
+ .code_type = IMG_IR_CODETYPE_PULSELEN,
+ },
+ /* main timings */
+ .unit = 600000, /* 600 us */
+ .timings = {
+ /* leader symbol */
+ .ldr = {
+ .pulse = { 4 /* 2.4 ms */ },
+ .space = { 1 /* 600 us */ },
+ },
+ /* 0 symbol */
+ .s00 = {
+ .pulse = { 1 /* 600 us */ },
+ .space = { 1 /* 600 us */ },
+ },
+ /* 1 symbol */
+ .s01 = {
+ .pulse = { 2 /* 1.2 ms */ },
+ .space = { 1 /* 600 us */ },
+ },
+ /* free time */
+ .ft = {
+ .minlen = 12,
+ .maxlen = 20,
+ .ft_min = 10, /* 6 ms */
+ },
+ },
+ /* scancode logic */
+ .scancode = img_ir_sony_scancode,
+ .filter = img_ir_sony_filter,
+};
diff --git a/drivers/media/rc/img-ir/img-ir.h b/drivers/media/rc/img-ir/img-ir.h
new file mode 100644
index 00000000000..afb189394af
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir.h
@@ -0,0 +1,166 @@
+/*
+ * ImgTec IR Decoder found in PowerDown Controller.
+ *
+ * Copyright 2010-2014 Imagination Technologies Ltd.
+ */
+
+#ifndef _IMG_IR_H_
+#define _IMG_IR_H_
+
+#include <linux/io.h>
+#include <linux/spinlock.h>
+
+#include "img-ir-raw.h"
+#include "img-ir-hw.h"
+
+/* registers */
+
+/* relative to the start of the IR block of registers */
+#define IMG_IR_CONTROL 0x00
+#define IMG_IR_STATUS 0x04
+#define IMG_IR_DATA_LW 0x08
+#define IMG_IR_DATA_UP 0x0c
+#define IMG_IR_LEAD_SYMB_TIMING 0x10
+#define IMG_IR_S00_SYMB_TIMING 0x14
+#define IMG_IR_S01_SYMB_TIMING 0x18
+#define IMG_IR_S10_SYMB_TIMING 0x1c
+#define IMG_IR_S11_SYMB_TIMING 0x20
+#define IMG_IR_FREE_SYMB_TIMING 0x24
+#define IMG_IR_POW_MOD_PARAMS 0x28
+#define IMG_IR_POW_MOD_ENABLE 0x2c
+#define IMG_IR_IRQ_MSG_DATA_LW 0x30
+#define IMG_IR_IRQ_MSG_DATA_UP 0x34
+#define IMG_IR_IRQ_MSG_MASK_LW 0x38
+#define IMG_IR_IRQ_MSG_MASK_UP 0x3c
+#define IMG_IR_IRQ_ENABLE 0x40
+#define IMG_IR_IRQ_STATUS 0x44
+#define IMG_IR_IRQ_CLEAR 0x48
+#define IMG_IR_IRCORE_ID 0xf0
+#define IMG_IR_CORE_REV 0xf4
+#define IMG_IR_CORE_DES1 0xf8
+#define IMG_IR_CORE_DES2 0xfc
+
+
+/* field masks */
+
+/* IMG_IR_CONTROL */
+#define IMG_IR_DECODEN 0x40000000
+#define IMG_IR_CODETYPE 0x30000000
+#define IMG_IR_CODETYPE_SHIFT 28
+#define IMG_IR_HDRTOG 0x08000000
+#define IMG_IR_LDRDEC 0x04000000
+#define IMG_IR_DECODINPOL 0x02000000 /* active high */
+#define IMG_IR_BITORIEN 0x01000000 /* MSB first */
+#define IMG_IR_D1VALIDSEL 0x00008000
+#define IMG_IR_BITINV 0x00000040 /* don't invert */
+#define IMG_IR_DECODEND2 0x00000010
+#define IMG_IR_BITORIEND2 0x00000002 /* MSB first */
+#define IMG_IR_BITINVD2 0x00000001 /* don't invert */
+
+/* IMG_IR_STATUS */
+#define IMG_IR_RXDVALD2 0x00001000
+#define IMG_IR_IRRXD 0x00000400
+#define IMG_IR_TOGSTATE 0x00000200
+#define IMG_IR_RXDVAL 0x00000040
+#define IMG_IR_RXDLEN 0x0000003f
+#define IMG_IR_RXDLEN_SHIFT 0
+
+/* IMG_IR_LEAD_SYMB_TIMING, IMG_IR_Sxx_SYMB_TIMING */
+#define IMG_IR_PD_MAX 0xff000000
+#define IMG_IR_PD_MAX_SHIFT 24
+#define IMG_IR_PD_MIN 0x00ff0000
+#define IMG_IR_PD_MIN_SHIFT 16
+#define IMG_IR_W_MAX 0x0000ff00
+#define IMG_IR_W_MAX_SHIFT 8
+#define IMG_IR_W_MIN 0x000000ff
+#define IMG_IR_W_MIN_SHIFT 0
+
+/* IMG_IR_FREE_SYMB_TIMING */
+#define IMG_IR_MAXLEN 0x0007e000
+#define IMG_IR_MAXLEN_SHIFT 13
+#define IMG_IR_MINLEN 0x00001f00
+#define IMG_IR_MINLEN_SHIFT 8
+#define IMG_IR_FT_MIN 0x000000ff
+#define IMG_IR_FT_MIN_SHIFT 0
+
+/* IMG_IR_POW_MOD_PARAMS */
+#define IMG_IR_PERIOD_LEN 0x3f000000
+#define IMG_IR_PERIOD_LEN_SHIFT 24
+#define IMG_IR_PERIOD_DUTY 0x003f0000
+#define IMG_IR_PERIOD_DUTY_SHIFT 16
+#define IMG_IR_STABLE_STOP 0x00003f00
+#define IMG_IR_STABLE_STOP_SHIFT 8
+#define IMG_IR_STABLE_START 0x0000003f
+#define IMG_IR_STABLE_START_SHIFT 0
+
+/* IMG_IR_POW_MOD_ENABLE */
+#define IMG_IR_POWER_OUT_EN 0x00000002
+#define IMG_IR_POWER_MOD_EN 0x00000001
+
+/* IMG_IR_IRQ_ENABLE, IMG_IR_IRQ_STATUS, IMG_IR_IRQ_CLEAR */
+#define IMG_IR_IRQ_DEC2_ERR 0x00000080
+#define IMG_IR_IRQ_DEC_ERR 0x00000040
+#define IMG_IR_IRQ_ACT_LEVEL 0x00000020
+#define IMG_IR_IRQ_FALL_EDGE 0x00000010
+#define IMG_IR_IRQ_RISE_EDGE 0x00000008
+#define IMG_IR_IRQ_DATA_MATCH 0x00000004
+#define IMG_IR_IRQ_DATA2_VALID 0x00000002
+#define IMG_IR_IRQ_DATA_VALID 0x00000001
+#define IMG_IR_IRQ_ALL 0x000000ff
+#define IMG_IR_IRQ_EDGE (IMG_IR_IRQ_FALL_EDGE | IMG_IR_IRQ_RISE_EDGE)
+
+/* IMG_IR_CORE_ID */
+#define IMG_IR_CORE_ID 0x00ff0000
+#define IMG_IR_CORE_ID_SHIFT 16
+#define IMG_IR_CORE_CONFIG 0x0000ffff
+#define IMG_IR_CORE_CONFIG_SHIFT 0
+
+/* IMG_IR_CORE_REV */
+#define IMG_IR_DESIGNER 0xff000000
+#define IMG_IR_DESIGNER_SHIFT 24
+#define IMG_IR_MAJOR_REV 0x00ff0000
+#define IMG_IR_MAJOR_REV_SHIFT 16
+#define IMG_IR_MINOR_REV 0x0000ff00
+#define IMG_IR_MINOR_REV_SHIFT 8
+#define IMG_IR_MAINT_REV 0x000000ff
+#define IMG_IR_MAINT_REV_SHIFT 0
+
+struct device;
+struct clk;
+
+/**
+ * struct img_ir_priv - Private driver data.
+ * @dev: Platform device.
+ * @irq: IRQ number.
+ * @clk: Input clock.
+ * @reg_base: Iomem base address of IR register block.
+ * @lock: Protects IR registers and variables in this struct.
+ * @raw: Driver data for raw decoder.
+ * @hw: Driver data for hardware decoder.
+ */
+struct img_ir_priv {
+ struct device *dev;
+ int irq;
+ struct clk *clk;
+ void __iomem *reg_base;
+ spinlock_t lock;
+
+ struct img_ir_priv_raw raw;
+ struct img_ir_priv_hw hw;
+};
+
+/* Hardware access */
+
+static inline void img_ir_write(struct img_ir_priv *priv,
+ unsigned int reg_offs, unsigned int data)
+{
+ iowrite32(data, priv->reg_base + reg_offs);
+}
+
+static inline unsigned int img_ir_read(struct img_ir_priv *priv,
+ unsigned int reg_offs)
+{
+ return ioread32(priv->reg_base + reg_offs);
+}
+
+#endif /* _IMG_IR_H_ */
diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c
index e7dc6b46fdf..6f24e77b148 100644
--- a/drivers/media/rc/imon.c
+++ b/drivers/media/rc/imon.c
@@ -34,6 +34,7 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
+#include <linux/ratelimit.h>
#include <linux/input.h>
#include <linux/usb.h>
@@ -46,7 +47,7 @@
#define MOD_AUTHOR "Jarod Wilson <jarod@wilsonet.com>"
#define MOD_DESC "Driver for SoundGraph iMON MultiMedia IR/Display"
#define MOD_NAME "imon"
-#define MOD_VERSION "0.9.2"
+#define MOD_VERSION "0.9.4"
#define DISPLAY_MINOR_BASE 144
#define DEVICE_NAME "lcd%d"
@@ -111,6 +112,7 @@ struct imon_context {
bool tx_control;
unsigned char usb_rx_buf[8];
unsigned char usb_tx_buf[8];
+ unsigned int send_packet_delay;
struct tx_t {
unsigned char data_buf[35]; /* user data buffer */
@@ -184,6 +186,10 @@ enum {
IMON_KEY_PANEL = 2,
};
+enum {
+ IMON_NEED_20MS_PKT_DELAY = 1
+};
+
/*
* USB Device ID for iMON USB Control Boards
*
@@ -214,7 +220,7 @@ static struct usb_device_id imon_usb_id_table[] = {
/* SoundGraph iMON OEM Touch LCD (IR & 4.3" VGA LCD) */
{ USB_DEVICE(0x15c2, 0x0035) },
/* SoundGraph iMON OEM VFD (IR & VFD) */
- { USB_DEVICE(0x15c2, 0x0036) },
+ { USB_DEVICE(0x15c2, 0x0036), .driver_info = IMON_NEED_20MS_PKT_DELAY },
/* device specifics unknown */
{ USB_DEVICE(0x15c2, 0x0037) },
/* SoundGraph iMON OEM LCD (IR & LCD) */
@@ -277,12 +283,21 @@ static const struct {
u64 hw_code;
u32 keycode;
} imon_panel_key_table[] = {
- { 0x000000000f00ffeell, KEY_PROG1 }, /* Go */
+ { 0x000000000f00ffeell, KEY_MEDIA }, /* Go */
+ { 0x000000001200ffeell, KEY_UP },
+ { 0x000000001300ffeell, KEY_DOWN },
+ { 0x000000001400ffeell, KEY_LEFT },
+ { 0x000000001500ffeell, KEY_RIGHT },
+ { 0x000000001600ffeell, KEY_ENTER },
+ { 0x000000001700ffeell, KEY_ESC },
{ 0x000000001f00ffeell, KEY_AUDIO },
{ 0x000000002000ffeell, KEY_VIDEO },
{ 0x000000002100ffeell, KEY_CAMERA },
{ 0x000000002700ffeell, KEY_DVD },
{ 0x000000002300ffeell, KEY_TV },
+ { 0x000000002b00ffeell, KEY_EXIT },
+ { 0x000000002c00ffeell, KEY_SELECT },
+ { 0x000000002d00ffeell, KEY_MENU },
{ 0x000000000500ffeell, KEY_PREVIOUS },
{ 0x000000000700ffeell, KEY_REWIND },
{ 0x000000000400ffeell, KEY_STOP },
@@ -298,6 +313,14 @@ static const struct {
/* 0xffdc iMON MCE VFD */
{ 0x00010000ffffffeell, KEY_VOLUMEUP },
{ 0x01000000ffffffeell, KEY_VOLUMEDOWN },
+ { 0x00000001ffffffeell, KEY_MUTE },
+ { 0x0000000fffffffeell, KEY_MEDIA },
+ { 0x00000012ffffffeell, KEY_UP },
+ { 0x00000013ffffffeell, KEY_DOWN },
+ { 0x00000014ffffffeell, KEY_LEFT },
+ { 0x00000015ffffffeell, KEY_RIGHT },
+ { 0x00000016ffffffeell, KEY_ENTER },
+ { 0x00000017ffffffeell, KEY_ESC },
/* iMON Knob values */
{ 0x000100ffffffffeell, KEY_VOLUMEUP },
{ 0x010000ffffffffeell, KEY_VOLUMEDOWN },
@@ -434,16 +457,6 @@ static int display_close(struct inode *inode, struct file *file)
} else {
ictx->display_isopen = false;
dev_dbg(ictx->dev, "display port closed\n");
- if (!ictx->dev_present_intf0) {
- /*
- * Device disconnected before close and IR port is not
- * open. If IR port is open, context will be deleted by
- * ir_close.
- */
- mutex_unlock(&ictx->lock);
- free_imon_context(ictx);
- return retval;
- }
}
mutex_unlock(&ictx->lock);
@@ -451,8 +464,9 @@ static int display_close(struct inode *inode, struct file *file)
}
/**
- * Sends a packet to the device -- this function must be called
- * with ictx->lock held.
+ * Sends a packet to the device -- this function must be called with
+ * ictx->lock held, or its unlock/lock sequence while waiting for tx
+ * to complete can/will lead to a deadlock.
*/
static int send_packet(struct imon_context *ictx)
{
@@ -508,30 +522,32 @@ static int send_packet(struct imon_context *ictx)
if (retval) {
ictx->tx.busy = false;
smp_rmb(); /* ensure later readers know we're not busy */
- pr_err("error submitting urb(%d)\n", retval);
+ pr_err_ratelimited("error submitting urb(%d)\n", retval);
} else {
/* Wait for transmission to complete (or abort) */
mutex_unlock(&ictx->lock);
retval = wait_for_completion_interruptible(
&ictx->tx.finished);
- if (retval)
- pr_err("task interrupted\n");
+ if (retval) {
+ usb_kill_urb(ictx->tx_urb);
+ pr_err_ratelimited("task interrupted\n");
+ }
mutex_lock(&ictx->lock);
retval = ictx->tx.status;
if (retval)
- pr_err("packet tx failed (%d)\n", retval);
+ pr_err_ratelimited("packet tx failed (%d)\n", retval);
}
kfree(control_req);
/*
- * Induce a mandatory 5ms delay before returning, as otherwise,
+ * Induce a mandatory delay before returning, as otherwise,
* send_packet can get called so rapidly as to overwhelm the device,
* particularly on faster systems and/or those with quirky usb.
*/
- timeout = msecs_to_jiffies(5);
- set_current_state(TASK_UNINTERRUPTIBLE);
+ timeout = msecs_to_jiffies(ictx->send_packet_delay);
+ set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(timeout);
return retval;
@@ -822,20 +838,20 @@ static ssize_t vfd_write(struct file *file, const char *buf,
ictx = file->private_data;
if (!ictx) {
- pr_err("no context for device\n");
+ pr_err_ratelimited("no context for device\n");
return -ENODEV;
}
mutex_lock(&ictx->lock);
if (!ictx->dev_present_intf0) {
- pr_err("no iMON device present\n");
+ pr_err_ratelimited("no iMON device present\n");
retval = -ENODEV;
goto exit;
}
if (n_bytes <= 0 || n_bytes > 32) {
- pr_err("invalid payload size\n");
+ pr_err_ratelimited("invalid payload size\n");
retval = -EINVAL;
goto exit;
}
@@ -861,7 +877,7 @@ static ssize_t vfd_write(struct file *file, const char *buf,
retval = send_packet(ictx);
if (retval) {
- pr_err("send packet failed for packet #%d\n", seq / 2);
+ pr_err_ratelimited("send packet #%d failed\n", seq / 2);
goto exit;
} else {
seq += 2;
@@ -875,7 +891,7 @@ static ssize_t vfd_write(struct file *file, const char *buf,
ictx->usb_tx_buf[7] = (unsigned char) seq;
retval = send_packet(ictx);
if (retval)
- pr_err("send packet failed for packet #%d\n", seq / 2);
+ pr_err_ratelimited("send packet #%d failed\n", seq / 2);
exit:
mutex_unlock(&ictx->lock);
@@ -904,20 +920,21 @@ static ssize_t lcd_write(struct file *file, const char *buf,
ictx = file->private_data;
if (!ictx) {
- pr_err("no context for device\n");
+ pr_err_ratelimited("no context for device\n");
return -ENODEV;
}
mutex_lock(&ictx->lock);
if (!ictx->display_supported) {
- pr_err("no iMON display present\n");
+ pr_err_ratelimited("no iMON display present\n");
retval = -ENODEV;
goto exit;
}
if (n_bytes != 8) {
- pr_err("invalid payload size: %d (expected 8)\n", (int)n_bytes);
+ pr_err_ratelimited("invalid payload size: %d (expected 8)\n",
+ (int)n_bytes);
retval = -EINVAL;
goto exit;
}
@@ -929,7 +946,7 @@ static ssize_t lcd_write(struct file *file, const char *buf,
retval = send_packet(ictx);
if (retval) {
- pr_err("send packet failed!\n");
+ pr_err_ratelimited("send packet failed!\n");
goto exit;
} else {
dev_dbg(ictx->dev, "%s: write %d bytes to LCD\n",
@@ -982,52 +999,65 @@ static void imon_touch_display_timeout(unsigned long data)
* the iMON remotes, and those used by the Windows MCE remotes (which is
* really just RC-6), but only one or the other at a time, as the signals
* are decoded onboard the receiver.
+ *
+ * This function gets called two different ways, one way is from
+ * rc_register_device, for initial protocol selection/setup, and the other is
+ * via a userspace-initiated protocol change request, either by direct sysfs
+ * prodding or by something like ir-keytable. In the rc_register_device case,
+ * the imon context lock is already held, but when initiated from userspace,
+ * it is not, so we must acquire it prior to calling send_packet, which
+ * requires that the lock is held.
*/
-static int imon_ir_change_protocol(struct rc_dev *rc, u64 rc_type)
+static int imon_ir_change_protocol(struct rc_dev *rc, u64 *rc_type)
{
int retval;
struct imon_context *ictx = rc->priv;
struct device *dev = ictx->dev;
+ bool unlock = false;
unsigned char ir_proto_packet[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86 };
- if (rc_type && !(rc_type & rc->allowed_protos))
+ if (*rc_type && !rc_protocols_allowed(rc, *rc_type))
dev_warn(dev, "Looks like you're trying to use an IR protocol "
"this device does not support\n");
- switch (rc_type) {
- case RC_TYPE_RC6:
+ if (*rc_type & RC_BIT_RC6_MCE) {
dev_dbg(dev, "Configuring IR receiver for MCE protocol\n");
ir_proto_packet[0] = 0x01;
- break;
- case RC_TYPE_UNKNOWN:
- case RC_TYPE_OTHER:
+ *rc_type = RC_BIT_RC6_MCE;
+ } else if (*rc_type & RC_BIT_OTHER) {
dev_dbg(dev, "Configuring IR receiver for iMON protocol\n");
if (!pad_stabilize)
dev_dbg(dev, "PAD stabilize functionality disabled\n");
/* ir_proto_packet[0] = 0x00; // already the default */
- rc_type = RC_TYPE_OTHER;
- break;
- default:
+ *rc_type = RC_BIT_OTHER;
+ } else {
dev_warn(dev, "Unsupported IR protocol specified, overriding "
"to iMON IR protocol\n");
if (!pad_stabilize)
dev_dbg(dev, "PAD stabilize functionality disabled\n");
/* ir_proto_packet[0] = 0x00; // already the default */
- rc_type = RC_TYPE_OTHER;
- break;
+ *rc_type = RC_BIT_OTHER;
}
memcpy(ictx->usb_tx_buf, &ir_proto_packet, sizeof(ir_proto_packet));
+ if (!mutex_is_locked(&ictx->lock)) {
+ unlock = true;
+ mutex_lock(&ictx->lock);
+ }
+
retval = send_packet(ictx);
if (retval)
goto out;
- ictx->rc_type = rc_type;
+ ictx->rc_type = *rc_type;
ictx->pad_mouse = false;
out:
+ if (unlock)
+ mutex_unlock(&ictx->lock);
+
return retval;
}
@@ -1198,7 +1228,7 @@ static u32 imon_panel_key_lookup(u64 code)
static bool imon_mouse_event(struct imon_context *ictx,
unsigned char *buf, int len)
{
- char rel_x = 0x00, rel_y = 0x00;
+ signed char rel_x = 0x00, rel_y = 0x00;
u8 right_shift = 1;
bool mouse_input = true;
int dir = 0;
@@ -1274,7 +1304,7 @@ static void imon_touch_event(struct imon_context *ictx, unsigned char *buf)
static void imon_pad_to_keys(struct imon_context *ictx, unsigned char *buf)
{
int dir = 0;
- char rel_x = 0x00, rel_y = 0x00;
+ signed char rel_x = 0x00, rel_y = 0x00;
u16 timeout, threshold;
u32 scancode = KEY_RESERVED;
unsigned long flags;
@@ -1284,7 +1314,7 @@ static void imon_pad_to_keys(struct imon_context *ictx, unsigned char *buf)
* contain a position coordinate (x,y), with each component ranging
* from -14 to 14. We want to down-sample this to only 4 discrete values
* for up/down/left/right arrow keys. Also, when you get too close to
- * diagonals, it has a tendancy to jump back and forth, so lets try to
+ * diagonals, it has a tendency to jump back and forth, so lets try to
* ignore when they get too close.
*/
if (ictx->product != 0xffdc) {
@@ -1296,7 +1326,7 @@ static void imon_pad_to_keys(struct imon_context *ictx, unsigned char *buf)
rel_x = buf[2];
rel_y = buf[3];
- if (ictx->rc_type == RC_TYPE_OTHER && pad_stabilize) {
+ if (ictx->rc_type == RC_BIT_OTHER && pad_stabilize) {
if ((buf[1] == 0) && ((rel_x != 0) || (rel_y != 0))) {
dir = stabilize((int)rel_x, (int)rel_y,
timeout, threshold);
@@ -1340,7 +1370,7 @@ static void imon_pad_to_keys(struct imon_context *ictx, unsigned char *buf)
* 0x68nnnnB7 to 0x6AnnnnB7, the left mouse button generates
* 0x688301b7 and the right one 0x688481b7. All other keys generate
* 0x2nnnnnnn. Position coordinate is encoded in buf[1] and buf[2] with
- * reversed endianess. Extract direction from buffer, rotate endianess,
+ * reversed endianness. Extract direction from buffer, rotate endianness,
* adjust sign and feed the values into stabilize(). The resulting codes
* will be 0x01008000, 0x01007F00, which match the newer devices.
*/
@@ -1363,7 +1393,7 @@ static void imon_pad_to_keys(struct imon_context *ictx, unsigned char *buf)
buf[0] = 0x01;
buf[1] = buf[4] = buf[5] = buf[6] = buf[7] = 0;
- if (ictx->rc_type == RC_TYPE_OTHER && pad_stabilize) {
+ if (ictx->rc_type == RC_BIT_OTHER && pad_stabilize) {
dir = stabilize((int)rel_x, (int)rel_y,
timeout, threshold);
if (!dir) {
@@ -1465,7 +1495,6 @@ static void imon_incoming_packet(struct imon_context *ictx,
struct device *dev = ictx->dev;
unsigned long flags;
u32 kc;
- bool norelease = false;
int i;
u64 scancode;
int press_type = 0;
@@ -1485,7 +1514,7 @@ static void imon_incoming_packet(struct imon_context *ictx,
kc = imon_panel_key_lookup(scancode);
} else {
scancode = be32_to_cpu(*((u32 *)buf));
- if (ictx->rc_type == RC_TYPE_RC6) {
+ if (ictx->rc_type == RC_BIT_RC6_MCE) {
ktype = IMON_KEY_IMON;
if (buf[0] == 0x80)
ktype = IMON_KEY_MCE;
@@ -1533,7 +1562,6 @@ static void imon_incoming_packet(struct imon_context *ictx,
!(buf[1] & 0x1 || buf[1] >> 2 & 0x1))) {
len = 8;
imon_pad_to_keys(ictx, buf);
- norelease = true;
}
if (debug) {
@@ -1547,11 +1575,6 @@ static void imon_incoming_packet(struct imon_context *ictx,
if (press_type < 0)
goto not_input_data;
- spin_lock_irqsave(&ictx->kc_lock, flags);
- if (ictx->kc == KEY_UNKNOWN)
- goto unknown_key;
- spin_unlock_irqrestore(&ictx->kc_lock, flags);
-
if (ktype != IMON_KEY_PANEL) {
if (press_type == 0)
rc_keyup(ictx->rdev);
@@ -1567,16 +1590,16 @@ static void imon_incoming_packet(struct imon_context *ictx,
/* Only panel type events left to process now */
spin_lock_irqsave(&ictx->kc_lock, flags);
+ do_gettimeofday(&t);
/* KEY_MUTE repeats from knob need to be suppressed */
if (ictx->kc == KEY_MUTE && ictx->kc == ictx->last_keycode) {
- do_gettimeofday(&t);
msec = tv2int(&t, &prev_time);
- prev_time = t;
if (msec < ictx->idev->rep[REP_DELAY]) {
spin_unlock_irqrestore(&ictx->kc_lock, flags);
return;
}
}
+ prev_time = t;
kc = ictx->kc;
spin_unlock_irqrestore(&ictx->kc_lock, flags);
@@ -1588,14 +1611,10 @@ static void imon_incoming_packet(struct imon_context *ictx,
input_report_key(ictx->idev, kc, 0);
input_sync(ictx->idev);
+ spin_lock_irqsave(&ictx->kc_lock, flags);
ictx->last_keycode = kc;
-
- return;
-
-unknown_key:
spin_unlock_irqrestore(&ictx->kc_lock, flags);
- dev_info(dev, "%s: unknown keypress, code 0x%llx\n", __func__,
- (long long)scancode);
+
return;
not_input_data:
@@ -1634,6 +1653,14 @@ static void usb_rx_callback_intf0(struct urb *urb)
if (!ictx)
return;
+ /*
+ * if we get a callback before we're done configuring the hardware, we
+ * can't yet process the data, as there's nowhere to send it, but we
+ * still need to submit a new rx URB to avoid wedging the hardware
+ */
+ if (!ictx->dev_present_intf0)
+ goto out;
+
switch (urb->status) {
case -ENOENT: /* usbcore unlink successful! */
return;
@@ -1651,6 +1678,7 @@ static void usb_rx_callback_intf0(struct urb *urb)
break;
}
+out:
usb_submit_urb(ictx->rx_urb_intf0, GFP_ATOMIC);
}
@@ -1666,6 +1694,14 @@ static void usb_rx_callback_intf1(struct urb *urb)
if (!ictx)
return;
+ /*
+ * if we get a callback before we're done configuring the hardware, we
+ * can't yet process the data, as there's nowhere to send it, but we
+ * still need to submit a new rx URB to avoid wedging the hardware
+ */
+ if (!ictx->dev_present_intf1)
+ goto out;
+
switch (urb->status) {
case -ENOENT: /* usbcore unlink successful! */
return;
@@ -1683,6 +1719,7 @@ static void usb_rx_callback_intf1(struct urb *urb)
break;
}
+out:
usb_submit_urb(ictx->rx_urb_intf1, GFP_ATOMIC);
}
@@ -1699,7 +1736,7 @@ static void imon_get_ffdc_type(struct imon_context *ictx)
{
u8 ffdc_cfg_byte = ictx->usb_rx_buf[6];
u8 detected_display_type = IMON_DISPLAY_TYPE_NONE;
- u64 allowed_protos = RC_TYPE_OTHER;
+ u64 allowed_protos = RC_BIT_OTHER;
switch (ffdc_cfg_byte) {
/* iMON Knob, no display, iMON IR + vol knob */
@@ -1725,21 +1762,26 @@ static void imon_get_ffdc_type(struct imon_context *ictx)
detected_display_type = IMON_DISPLAY_TYPE_VFD;
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_TYPE_RC6;
+ allowed_protos = RC_BIT_RC6_MCE;
break;
/* iMON LCD, MCE IR */
case 0x9f:
dev_info(ictx->dev, "0xffdc iMON LCD, MCE IR");
detected_display_type = IMON_DISPLAY_TYPE_LCD;
- allowed_protos = RC_TYPE_RC6;
+ allowed_protos = RC_BIT_RC6_MCE;
break;
default:
dev_info(ictx->dev, "Unknown 0xffdc device, "
"defaulting to VFD and iMON IR");
detected_display_type = IMON_DISPLAY_TYPE_VFD;
+ /* We don't know which one it is, allow user to set the
+ * RC6 one from userspace if OTHER wasn't correct. */
+ allowed_protos |= RC_BIT_RC6_MCE;
break;
}
@@ -1825,7 +1867,8 @@ static struct rc_dev *imon_init_rdev(struct imon_context *ictx)
rdev->priv = ictx;
rdev->driver_type = RC_DRIVER_SCANCODE;
- rdev->allowed_protos = RC_TYPE_OTHER | RC_TYPE_RC6; /* iMON PAD or MCE */
+ /* iMON PAD or MCE */
+ rc_set_allowed_protocols(rdev, RC_BIT_OTHER | RC_BIT_RC6_MCE);
rdev->change_protocol = imon_ir_change_protocol;
rdev->driver_name = MOD_NAME;
@@ -1838,12 +1881,12 @@ static struct rc_dev *imon_init_rdev(struct imon_context *ictx)
if (ictx->product == 0xffdc) {
imon_get_ffdc_type(ictx);
- rdev->allowed_protos = ictx->rc_type;
+ rc_set_allowed_protocols(rdev, ictx->rc_type);
}
imon_set_display_type(ictx);
- if (ictx->rc_type == RC_TYPE_RC6)
+ if (ictx->rc_type == RC_BIT_RC6_MCE)
rdev->map_name = RC_MAP_IMON_MCE;
else
rdev->map_name = RC_MAP_IMON_PAD;
@@ -1867,10 +1910,8 @@ static struct input_dev *imon_init_idev(struct imon_context *ictx)
int ret, i;
idev = input_allocate_device();
- if (!idev) {
- dev_err(ictx->dev, "input dev allocation failed\n");
+ if (!idev)
goto out;
- }
snprintf(ictx->name_idev, sizeof(ictx->name_idev),
"iMON Panel, Knob and Mouse(%04x:%04x)",
@@ -1918,10 +1959,8 @@ static struct input_dev *imon_init_touch(struct imon_context *ictx)
int ret;
touch = input_allocate_device();
- if (!touch) {
- dev_err(ictx->dev, "touchscreen input dev allocation failed\n");
+ if (!touch)
goto touch_alloc_failed;
- }
snprintf(ictx->name_touch, sizeof(ictx->name_touch),
"iMON USB Touchscreen (%04x:%04x)",
@@ -1955,7 +1994,7 @@ static struct input_dev *imon_init_touch(struct imon_context *ictx)
return touch;
touch_register_failed:
- input_free_device(ictx->touch);
+ input_free_device(touch);
touch_alloc_failed:
return NULL;
@@ -2053,7 +2092,8 @@ static bool imon_find_endpoints(struct imon_context *ictx,
}
-static struct imon_context *imon_init_intf0(struct usb_interface *intf)
+static struct imon_context *imon_init_intf0(struct usb_interface *intf,
+ const struct usb_device_id *id)
{
struct imon_context *ictx;
struct urb *rx_urb;
@@ -2086,7 +2126,6 @@ static struct imon_context *imon_init_intf0(struct usb_interface *intf)
ictx->dev = dev;
ictx->usbdev_intf0 = usb_get_dev(interface_to_usbdev(intf));
- ictx->dev_present_intf0 = true;
ictx->rx_urb_intf0 = rx_urb;
ictx->tx_urb = tx_urb;
ictx->rf_device = false;
@@ -2094,6 +2133,10 @@ static struct imon_context *imon_init_intf0(struct usb_interface *intf)
ictx->vendor = le16_to_cpu(ictx->usbdev_intf0->descriptor.idVendor);
ictx->product = le16_to_cpu(ictx->usbdev_intf0->descriptor.idProduct);
+ /* default send_packet delay is 5ms but some devices need more */
+ ictx->send_packet_delay = id->driver_info & IMON_NEED_20MS_PKT_DELAY ?
+ 20 : 5;
+
ret = -ENODEV;
iface_desc = intf->cur_altsetting;
if (!imon_find_endpoints(ictx, iface_desc)) {
@@ -2125,6 +2168,9 @@ static struct imon_context *imon_init_intf0(struct usb_interface *intf)
goto rdev_setup_failed;
}
+ ictx->dev_present_intf0 = true;
+
+ mutex_unlock(&ictx->lock);
return ictx;
rdev_setup_failed:
@@ -2167,7 +2213,6 @@ static struct imon_context *imon_init_intf1(struct usb_interface *intf,
}
ictx->usbdev_intf1 = usb_get_dev(interface_to_usbdev(intf));
- ictx->dev_present_intf1 = true;
ictx->rx_urb_intf1 = rx_urb;
ret = -ENODEV;
@@ -2196,6 +2241,9 @@ static struct imon_context *imon_init_intf1(struct usb_interface *intf,
goto urb_submit_failed;
}
+ ictx->dev_present_intf1 = true;
+
+ mutex_unlock(&ictx->lock);
return ictx;
urb_submit_failed:
@@ -2206,7 +2254,7 @@ find_endpoint_failed:
mutex_unlock(&ictx->lock);
usb_free_urb(rx_urb);
rx_urb_alloc_failed:
- dev_err(ictx->dev, "unable to initialize intf0, err %d\n", ret);
+ dev_err(ictx->dev, "unable to initialize intf1, err %d\n", ret);
return NULL;
}
@@ -2238,21 +2286,19 @@ static void imon_init_display(struct imon_context *ictx,
/**
* Callback function for USB core API: Probe
*/
-static int __devinit imon_probe(struct usb_interface *interface,
- const struct usb_device_id *id)
+static int imon_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
{
struct usb_device *usbdev = NULL;
struct usb_host_interface *iface_desc = NULL;
struct usb_interface *first_if;
struct device *dev = &interface->dev;
- int ifnum, code_length, sysfs_err;
+ int ifnum, sysfs_err;
int ret = 0;
struct imon_context *ictx = NULL;
struct imon_context *first_if_ctx = NULL;
u16 vendor, product;
- code_length = BUF_CHUNK_SIZE * 8;
-
usbdev = usb_get_dev(interface_to_usbdev(interface));
iface_desc = interface->cur_altsetting;
ifnum = iface_desc->desc.bInterfaceNumber;
@@ -2269,7 +2315,7 @@ static int __devinit imon_probe(struct usb_interface *interface,
first_if_ctx = usb_get_intfdata(first_if);
if (ifnum == 0) {
- ictx = imon_init_intf0(interface);
+ ictx = imon_init_intf0(interface, id);
if (!ictx) {
pr_err("failed to initialize context!\n");
ret = -ENODEV;
@@ -2277,7 +2323,14 @@ static int __devinit imon_probe(struct usb_interface *interface,
}
} else {
- /* this is the secondary interface on the device */
+ /* this is the secondary interface on the device */
+
+ /* fail early if first intf failed to register */
+ if (!first_if_ctx) {
+ ret = -ENODEV;
+ goto fail;
+ }
+
ictx = imon_init_intf1(interface, first_if_ctx);
if (!ictx) {
pr_err("failed to attach to context!\n");
@@ -2290,6 +2343,8 @@ static int __devinit imon_probe(struct usb_interface *interface,
usb_set_intfdata(interface, ictx);
if (ifnum == 0) {
+ mutex_lock(&ictx->lock);
+
if (product == 0xffdc && ictx->rf_device) {
sysfs_err = sysfs_create_group(&interface->dev.kobj,
&imon_rf_attr_group);
@@ -2300,13 +2355,14 @@ static int __devinit imon_probe(struct usb_interface *interface,
if (ictx->display_supported)
imon_init_display(ictx, interface);
+
+ mutex_unlock(&ictx->lock);
}
dev_info(dev, "iMON device (%04x:%04x, intf%d) on "
"usb<%d:%d> initialized\n", vendor, product, ifnum,
usbdev->bus->busnum, usbdev->devnum);
- mutex_unlock(&ictx->lock);
mutex_unlock(&driver_lock);
return 0;
@@ -2321,7 +2377,7 @@ fail:
/**
* Callback function for USB core API: disconnect
*/
-static void __devexit imon_disconnect(struct usb_interface *interface)
+static void imon_disconnect(struct usb_interface *interface)
{
struct imon_context *ictx;
struct device *dev;
@@ -2334,8 +2390,6 @@ static void __devexit imon_disconnect(struct usb_interface *interface)
dev = ictx->dev;
ifnum = interface->cur_altsetting->desc.bInterfaceNumber;
- mutex_lock(&ictx->lock);
-
/*
* sysfs_remove_group is safe to call even if sysfs_create_group
* hasn't been called
@@ -2359,24 +2413,20 @@ static void __devexit imon_disconnect(struct usb_interface *interface)
if (ictx->display_supported) {
if (ictx->display_type == IMON_DISPLAY_TYPE_LCD)
usb_deregister_dev(interface, &imon_lcd_class);
- else
+ else if (ictx->display_type == IMON_DISPLAY_TYPE_VFD)
usb_deregister_dev(interface, &imon_vfd_class);
}
} else {
ictx->dev_present_intf1 = false;
usb_kill_urb(ictx->rx_urb_intf1);
- if (ictx->display_type == IMON_DISPLAY_TYPE_VGA)
+ if (ictx->display_type == IMON_DISPLAY_TYPE_VGA) {
input_unregister_device(ictx->touch);
+ del_timer_sync(&ictx->ttimer);
+ }
}
- if (!ictx->dev_present_intf0 && !ictx->dev_present_intf1) {
- if (ictx->display_type == IMON_DISPLAY_TYPE_VGA)
- del_timer_sync(&ictx->ttimer);
- mutex_unlock(&ictx->lock);
- if (!ictx->display_isopen)
- free_imon_context(ictx);
- } else
- mutex_unlock(&ictx->lock);
+ if (!ictx->dev_present_intf0 && !ictx->dev_present_intf1)
+ free_imon_context(ictx);
mutex_unlock(&driver_lock);
@@ -2427,23 +2477,4 @@ static int imon_resume(struct usb_interface *intf)
return rc;
}
-static int __init imon_init(void)
-{
- int rc;
-
- rc = usb_register(&imon_driver);
- if (rc) {
- pr_err("usb register failed(%d)\n", rc);
- rc = -ENODEV;
- }
-
- return rc;
-}
-
-static void __exit imon_exit(void)
-{
- usb_deregister(&imon_driver);
-}
-
-module_init(imon_init);
-module_exit(imon_exit);
+module_usb_driver(imon_driver);
diff --git a/drivers/media/rc/ir-jvc-decoder.c b/drivers/media/rc/ir-jvc-decoder.c
index 624449afaa6..4ea62a1dcfd 100644
--- a/drivers/media/rc/ir-jvc-decoder.c
+++ b/drivers/media/rc/ir-jvc-decoder.c
@@ -13,6 +13,7 @@
*/
#include <linux/bitrev.h>
+#include <linux/module.h>
#include "rc-core-priv.h"
#define JVC_NBITS 16 /* dev(8) + func(8) */
@@ -46,7 +47,7 @@ static int ir_jvc_decode(struct rc_dev *dev, struct ir_raw_event ev)
{
struct jvc_dec *data = &dev->raw->jvc;
- if (!(dev->raw->enabled_protocols & RC_TYPE_JVC))
+ if (!rc_protocols_enabled(dev, RC_BIT_JVC))
return 0;
if (!is_timing_event(ev)) {
@@ -173,7 +174,7 @@ out:
}
static struct ir_raw_handler jvc_handler = {
- .protocols = RC_TYPE_JVC,
+ .protocols = RC_BIT_JVC,
.decode = ir_jvc_decode,
};
diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c
index 1c5cc65ea1e..d731da6c414 100644
--- a/drivers/media/rc/ir-lirc-codec.c
+++ b/drivers/media/rc/ir-lirc-codec.c
@@ -14,6 +14,7 @@
#include <linux/sched.h>
#include <linux/wait.h>
+#include <linux/module.h>
#include <media/lirc.h>
#include <media/lirc_dev.h>
#include <media/rc-core.h>
@@ -34,7 +35,7 @@ static int ir_lirc_decode(struct rc_dev *dev, struct ir_raw_event ev)
struct lirc_codec *lirc = &dev->raw->lirc;
int sample;
- if (!(dev->raw->enabled_protocols & RC_TYPE_LIRC))
+ if (!rc_protocols_enabled(dev, RC_BIT_LIRC))
return 0;
if (!dev->raw->lirc.drv || !dev->raw->lirc.drv->rbuf)
@@ -98,24 +99,30 @@ static int ir_lirc_decode(struct rc_dev *dev, struct ir_raw_event ev)
return 0;
}
-static ssize_t ir_lirc_transmit_ir(struct file *file, const char *buf,
+static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf,
size_t n, loff_t *ppos)
{
struct lirc_codec *lirc;
struct rc_dev *dev;
- int *txbuf; /* buffer with values to transmit */
- int ret = 0;
+ unsigned int *txbuf; /* buffer with values to transmit */
+ ssize_t ret = -EINVAL;
size_t count;
+ ktime_t start;
+ s64 towait;
+ unsigned int duration = 0; /* signal duration in us */
+ int i;
+
+ start = ktime_get();
lirc = lirc_get_pdata(file);
if (!lirc)
return -EFAULT;
- if (n % sizeof(int))
+ if (n < sizeof(unsigned) || n % sizeof(unsigned))
return -EINVAL;
- count = n / sizeof(int);
- if (count > LIRCBUF_SIZE || count % 2 == 0 || n % sizeof(int) != 0)
+ count = n / sizeof(unsigned);
+ if (count > LIRCBUF_SIZE || count % 2 == 0)
return -EINVAL;
txbuf = memdup_user(buf, n);
@@ -128,8 +135,39 @@ static ssize_t ir_lirc_transmit_ir(struct file *file, const char *buf,
goto out;
}
- if (dev->tx_ir)
- ret = dev->tx_ir(dev, txbuf, (u32)n);
+ if (!dev->tx_ir) {
+ ret = -ENOSYS;
+ goto out;
+ }
+
+ for (i = 0; i < count; i++) {
+ if (txbuf[i] > IR_MAX_DURATION / 1000 - duration || !txbuf[i]) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ duration += txbuf[i];
+ }
+
+ ret = dev->tx_ir(dev, txbuf, count);
+ if (ret < 0)
+ goto out;
+
+ for (duration = i = 0; i < ret; i++)
+ duration += txbuf[i];
+
+ ret *= sizeof(unsigned int);
+
+ /*
+ * The lircd gap calculation expects the write function to
+ * wait for the actual IR signal to be transmitted before
+ * returning.
+ */
+ towait = ktime_us_delta(ktime_add_us(start, duration), ktime_get());
+ if (towait > 0) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(usecs_to_jiffies(towait));
+ }
out:
kfree(txbuf);
@@ -137,10 +175,11 @@ out:
}
static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,
- unsigned long __user arg)
+ unsigned long arg)
{
struct lirc_codec *lirc;
struct rc_dev *dev;
+ u32 __user *argp = (u32 __user *)(arg);
int ret = 0;
__u32 val = 0, tmp;
@@ -153,7 +192,7 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,
return -EFAULT;
if (_IOC_DIR(cmd) & _IOC_WRITE) {
- ret = get_user(val, (__u32 *)arg);
+ ret = get_user(val, argp);
if (ret)
return ret;
}
@@ -173,13 +212,13 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,
/* TX settings */
case LIRC_SET_TRANSMITTER_MASK:
if (!dev->s_tx_mask)
- return -EINVAL;
+ return -ENOSYS;
return dev->s_tx_mask(dev, val);
case LIRC_SET_SEND_CARRIER:
if (!dev->s_tx_carrier)
- return -EINVAL;
+ return -ENOSYS;
return dev->s_tx_carrier(dev, val);
@@ -262,7 +301,7 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,
}
if (_IOC_DIR(cmd) & _IOC_READ)
- ret = put_user(val, (__u32 *)arg);
+ ret = put_user(val, argp);
return ret;
}
@@ -277,7 +316,7 @@ static void ir_lirc_close(void *data)
return;
}
-static struct file_operations lirc_fops = {
+static const struct file_operations lirc_fops = {
.owner = THIS_MODULE,
.write = ir_lirc_transmit_ir,
.unlocked_ioctl = ir_lirc_ioctl,
@@ -345,6 +384,7 @@ static int ir_lirc_register(struct rc_dev *dev)
drv->code_length = sizeof(struct ir_raw_event) * 8;
drv->fops = &lirc_fops;
drv->dev = &dev->dev;
+ drv->rdev = dev;
drv->owner = THIS_MODULE;
drv->minor = lirc_register_driver(drv);
@@ -378,7 +418,7 @@ static int ir_lirc_unregister(struct rc_dev *dev)
}
static struct ir_raw_handler lirc_handler = {
- .protocols = RC_TYPE_LIRC,
+ .protocols = RC_BIT_LIRC,
.decode = ir_lirc_decode,
.raw_register = ir_lirc_register,
.raw_unregister = ir_lirc_unregister,
diff --git a/drivers/media/rc/ir-mce_kbd-decoder.c b/drivers/media/rc/ir-mce_kbd-decoder.c
new file mode 100644
index 00000000000..0c55f794c8c
--- /dev/null
+++ b/drivers/media/rc/ir-mce_kbd-decoder.c
@@ -0,0 +1,449 @@
+/* ir-mce_kbd-decoder.c - A decoder for the RC6-ish keyboard/mouse IR protocol
+ * used by the Microsoft Remote Keyboard for Windows Media Center Edition,
+ * referred to by Microsoft's Windows Media Center remote specification docs
+ * as "an internal protocol called MCIR-2".
+ *
+ * Copyright (C) 2011 by Jarod Wilson <jarod@redhat.com>
+ *
+ * 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 version 2 of the License.
+ *
+ * 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 <linux/module.h>
+
+#include "rc-core-priv.h"
+
+/*
+ * This decoder currently supports:
+ * - MCIR-2 29-bit IR signals used for mouse movement and buttons
+ * - MCIR-2 32-bit IR signals used for standard keyboard keys
+ *
+ * The media keys on the keyboard send RC-6 signals that are inditinguishable
+ * from the keys of the same name on the stock MCE remote, and will be handled
+ * by the standard RC-6 decoder, and be made available to the system via the
+ * input device for the remote, rather than the keyboard/mouse one.
+ */
+
+#define MCIR2_UNIT 333333 /* ns */
+#define MCIR2_HEADER_NBITS 5
+#define MCIR2_MOUSE_NBITS 29
+#define MCIR2_KEYBOARD_NBITS 32
+#define MCIR2_PREFIX_PULSE (8 * MCIR2_UNIT)
+#define MCIR2_PREFIX_SPACE (1 * MCIR2_UNIT)
+#define MCIR2_MAX_LEN (3 * MCIR2_UNIT)
+#define MCIR2_BIT_START (1 * MCIR2_UNIT)
+#define MCIR2_BIT_END (1 * MCIR2_UNIT)
+#define MCIR2_BIT_0 (1 * MCIR2_UNIT)
+#define MCIR2_BIT_SET (2 * MCIR2_UNIT)
+#define MCIR2_MODE_MASK 0xf /* for the header bits */
+#define MCIR2_KEYBOARD_HEADER 0x4
+#define MCIR2_MOUSE_HEADER 0x1
+#define MCIR2_MASK_KEYS_START 0xe0
+
+enum mce_kbd_mode {
+ MCIR2_MODE_KEYBOARD,
+ MCIR2_MODE_MOUSE,
+ MCIR2_MODE_UNKNOWN,
+};
+
+enum mce_kbd_state {
+ STATE_INACTIVE,
+ STATE_HEADER_BIT_START,
+ STATE_HEADER_BIT_END,
+ STATE_BODY_BIT_START,
+ STATE_BODY_BIT_END,
+ STATE_FINISHED,
+};
+
+static unsigned char kbd_keycodes[256] = {
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_A,
+ KEY_B, KEY_C, KEY_D, KEY_E, KEY_F,
+ KEY_G, KEY_H, KEY_I, KEY_J, KEY_K,
+ KEY_L, KEY_M, KEY_N, KEY_O, KEY_P,
+ KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U,
+ KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z,
+ KEY_1, KEY_2, KEY_3, KEY_4, KEY_5,
+ KEY_6, KEY_7, KEY_8, KEY_9, KEY_0,
+ KEY_ENTER, KEY_ESC, KEY_BACKSPACE, KEY_TAB, KEY_SPACE,
+ KEY_MINUS, KEY_EQUAL, KEY_LEFTBRACE, KEY_RIGHTBRACE, KEY_BACKSLASH,
+ KEY_RESERVED, KEY_SEMICOLON, KEY_APOSTROPHE, KEY_GRAVE, KEY_COMMA,
+ KEY_DOT, KEY_SLASH, KEY_CAPSLOCK, KEY_F1, KEY_F2,
+ KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7,
+ KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12,
+ KEY_SYSRQ, KEY_SCROLLLOCK, KEY_PAUSE, KEY_INSERT, KEY_HOME,
+ KEY_PAGEUP, KEY_DELETE, KEY_END, KEY_PAGEDOWN, KEY_RIGHT,
+ KEY_LEFT, KEY_DOWN, KEY_UP, KEY_NUMLOCK, KEY_KPSLASH,
+ KEY_KPASTERISK, KEY_KPMINUS, KEY_KPPLUS, KEY_KPENTER, KEY_KP1,
+ KEY_KP2, KEY_KP3, KEY_KP4, KEY_KP5, KEY_KP6,
+ KEY_KP7, KEY_KP8, KEY_KP9, KEY_KP0, KEY_KPDOT,
+ KEY_102ND, KEY_COMPOSE, KEY_POWER, KEY_KPEQUAL, KEY_F13,
+ KEY_F14, KEY_F15, KEY_F16, KEY_F17, KEY_F18,
+ KEY_F19, KEY_F20, KEY_F21, KEY_F22, KEY_F23,
+ KEY_F24, KEY_OPEN, KEY_HELP, KEY_PROPS, KEY_FRONT,
+ KEY_STOP, KEY_AGAIN, KEY_UNDO, KEY_CUT, KEY_COPY,
+ KEY_PASTE, KEY_FIND, KEY_MUTE, KEY_VOLUMEUP, KEY_VOLUMEDOWN,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_KPCOMMA, KEY_RESERVED,
+ KEY_RO, KEY_KATAKANAHIRAGANA, KEY_YEN, KEY_HENKAN, KEY_MUHENKAN,
+ KEY_KPJPCOMMA, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_HANGUEL,
+ KEY_HANJA, KEY_KATAKANA, KEY_HIRAGANA, KEY_ZENKAKUHANKAKU, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_LEFTCTRL,
+ KEY_LEFTSHIFT, KEY_LEFTALT, KEY_LEFTMETA, KEY_RIGHTCTRL, KEY_RIGHTSHIFT,
+ KEY_RIGHTALT, KEY_RIGHTMETA, KEY_PLAYPAUSE, KEY_STOPCD, KEY_PREVIOUSSONG,
+ KEY_NEXTSONG, KEY_EJECTCD, KEY_VOLUMEUP, KEY_VOLUMEDOWN, KEY_MUTE,
+ KEY_WWW, KEY_BACK, KEY_FORWARD, KEY_STOP, KEY_FIND,
+ KEY_SCROLLUP, KEY_SCROLLDOWN, KEY_EDIT, KEY_SLEEP, KEY_COFFEE,
+ KEY_REFRESH, KEY_CALC, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED
+};
+
+static void mce_kbd_rx_timeout(unsigned long data)
+{
+ struct mce_kbd_dec *mce_kbd = (struct mce_kbd_dec *)data;
+ int i;
+ unsigned char maskcode;
+
+ IR_dprintk(2, "timer callback clearing all keys\n");
+
+ for (i = 0; i < 7; i++) {
+ maskcode = kbd_keycodes[MCIR2_MASK_KEYS_START + i];
+ input_report_key(mce_kbd->idev, maskcode, 0);
+ }
+
+ for (i = 0; i < MCIR2_MASK_KEYS_START; i++)
+ input_report_key(mce_kbd->idev, kbd_keycodes[i], 0);
+}
+
+static enum mce_kbd_mode mce_kbd_mode(struct mce_kbd_dec *data)
+{
+ switch (data->header & MCIR2_MODE_MASK) {
+ case MCIR2_KEYBOARD_HEADER:
+ return MCIR2_MODE_KEYBOARD;
+ case MCIR2_MOUSE_HEADER:
+ return MCIR2_MODE_MOUSE;
+ default:
+ return MCIR2_MODE_UNKNOWN;
+ }
+}
+
+static void ir_mce_kbd_process_keyboard_data(struct input_dev *idev,
+ u32 scancode)
+{
+ u8 keydata = (scancode >> 8) & 0xff;
+ u8 shiftmask = scancode & 0xff;
+ unsigned char keycode, maskcode;
+ int i, keystate;
+
+ IR_dprintk(1, "keyboard: keydata = 0x%02x, shiftmask = 0x%02x\n",
+ keydata, shiftmask);
+
+ for (i = 0; i < 7; i++) {
+ maskcode = kbd_keycodes[MCIR2_MASK_KEYS_START + i];
+ if (shiftmask & (1 << i))
+ keystate = 1;
+ else
+ keystate = 0;
+ input_report_key(idev, maskcode, keystate);
+ }
+
+ if (keydata) {
+ keycode = kbd_keycodes[keydata];
+ input_report_key(idev, keycode, 1);
+ } else {
+ for (i = 0; i < MCIR2_MASK_KEYS_START; i++)
+ input_report_key(idev, kbd_keycodes[i], 0);
+ }
+}
+
+static void ir_mce_kbd_process_mouse_data(struct input_dev *idev, u32 scancode)
+{
+ /* raw mouse coordinates */
+ u8 xdata = (scancode >> 7) & 0x7f;
+ u8 ydata = (scancode >> 14) & 0x7f;
+ int x, y;
+ /* mouse buttons */
+ bool right = scancode & 0x40;
+ bool left = scancode & 0x20;
+
+ if (xdata & 0x40)
+ x = -((~xdata & 0x7f) + 1);
+ else
+ x = xdata;
+
+ if (ydata & 0x40)
+ y = -((~ydata & 0x7f) + 1);
+ else
+ y = ydata;
+
+ IR_dprintk(1, "mouse: x = %d, y = %d, btns = %s%s\n",
+ x, y, left ? "L" : "", right ? "R" : "");
+
+ input_report_rel(idev, REL_X, x);
+ input_report_rel(idev, REL_Y, y);
+
+ input_report_key(idev, BTN_LEFT, left);
+ input_report_key(idev, BTN_RIGHT, right);
+}
+
+/**
+ * ir_mce_kbd_decode() - Decode one mce_kbd pulse or space
+ * @dev: the struct rc_dev descriptor of the device
+ * @ev: the struct ir_raw_event descriptor of the pulse/space
+ *
+ * This function returns -EINVAL if the pulse violates the state machine
+ */
+static int ir_mce_kbd_decode(struct rc_dev *dev, struct ir_raw_event ev)
+{
+ struct mce_kbd_dec *data = &dev->raw->mce_kbd;
+ u32 scancode;
+ unsigned long delay;
+
+ if (!rc_protocols_enabled(dev, RC_BIT_MCE_KBD))
+ return 0;
+
+ if (!is_timing_event(ev)) {
+ if (ev.reset)
+ data->state = STATE_INACTIVE;
+ return 0;
+ }
+
+ if (!geq_margin(ev.duration, MCIR2_UNIT, MCIR2_UNIT / 2))
+ goto out;
+
+again:
+ IR_dprintk(2, "started at state %i (%uus %s)\n",
+ data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+
+ if (!geq_margin(ev.duration, MCIR2_UNIT, MCIR2_UNIT / 2))
+ return 0;
+
+ switch (data->state) {
+
+ case STATE_INACTIVE:
+ if (!ev.pulse)
+ break;
+
+ /* Note: larger margin on first pulse since each MCIR2_UNIT
+ is quite short and some hardware takes some time to
+ adjust to the signal */
+ if (!eq_margin(ev.duration, MCIR2_PREFIX_PULSE, MCIR2_UNIT))
+ break;
+
+ data->state = STATE_HEADER_BIT_START;
+ data->count = 0;
+ data->header = 0;
+ return 0;
+
+ case STATE_HEADER_BIT_START:
+ if (geq_margin(ev.duration, MCIR2_MAX_LEN, MCIR2_UNIT / 2))
+ break;
+
+ data->header <<= 1;
+ if (ev.pulse)
+ data->header |= 1;
+ data->count++;
+ data->state = STATE_HEADER_BIT_END;
+ return 0;
+
+ case STATE_HEADER_BIT_END:
+ if (!is_transition(&ev, &dev->raw->prev_ev))
+ break;
+
+ decrease_duration(&ev, MCIR2_BIT_END);
+
+ if (data->count != MCIR2_HEADER_NBITS) {
+ data->state = STATE_HEADER_BIT_START;
+ goto again;
+ }
+
+ switch (mce_kbd_mode(data)) {
+ case MCIR2_MODE_KEYBOARD:
+ data->wanted_bits = MCIR2_KEYBOARD_NBITS;
+ break;
+ case MCIR2_MODE_MOUSE:
+ data->wanted_bits = MCIR2_MOUSE_NBITS;
+ break;
+ default:
+ IR_dprintk(1, "not keyboard or mouse data\n");
+ goto out;
+ }
+
+ data->count = 0;
+ data->body = 0;
+ data->state = STATE_BODY_BIT_START;
+ goto again;
+
+ case STATE_BODY_BIT_START:
+ if (geq_margin(ev.duration, MCIR2_MAX_LEN, MCIR2_UNIT / 2))
+ break;
+
+ data->body <<= 1;
+ if (ev.pulse)
+ data->body |= 1;
+ data->count++;
+ data->state = STATE_BODY_BIT_END;
+ return 0;
+
+ case STATE_BODY_BIT_END:
+ if (!is_transition(&ev, &dev->raw->prev_ev))
+ break;
+
+ if (data->count == data->wanted_bits)
+ data->state = STATE_FINISHED;
+ else
+ data->state = STATE_BODY_BIT_START;
+
+ decrease_duration(&ev, MCIR2_BIT_END);
+ goto again;
+
+ case STATE_FINISHED:
+ if (ev.pulse)
+ break;
+
+ switch (data->wanted_bits) {
+ case MCIR2_KEYBOARD_NBITS:
+ scancode = data->body & 0xffff;
+ IR_dprintk(1, "keyboard data 0x%08x\n", data->body);
+ if (dev->timeout)
+ delay = usecs_to_jiffies(dev->timeout / 1000);
+ else
+ delay = msecs_to_jiffies(100);
+ mod_timer(&data->rx_timeout, jiffies + delay);
+ /* Pass data to keyboard buffer parser */
+ ir_mce_kbd_process_keyboard_data(data->idev, scancode);
+ break;
+ case MCIR2_MOUSE_NBITS:
+ scancode = data->body & 0x1fffff;
+ IR_dprintk(1, "mouse data 0x%06x\n", scancode);
+ /* Pass data to mouse buffer parser */
+ ir_mce_kbd_process_mouse_data(data->idev, scancode);
+ break;
+ default:
+ IR_dprintk(1, "not keyboard or mouse data\n");
+ goto out;
+ }
+
+ data->state = STATE_INACTIVE;
+ input_sync(data->idev);
+ return 0;
+ }
+
+out:
+ IR_dprintk(1, "failed at state %i (%uus %s)\n",
+ data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+ data->state = STATE_INACTIVE;
+ input_sync(data->idev);
+ return -EINVAL;
+}
+
+static int ir_mce_kbd_register(struct rc_dev *dev)
+{
+ struct mce_kbd_dec *mce_kbd = &dev->raw->mce_kbd;
+ struct input_dev *idev;
+ int i, ret;
+
+ idev = input_allocate_device();
+ if (!idev)
+ return -ENOMEM;
+
+ snprintf(mce_kbd->name, sizeof(mce_kbd->name),
+ "MCE IR Keyboard/Mouse (%s)", dev->driver_name);
+ strlcat(mce_kbd->phys, "/input0", sizeof(mce_kbd->phys));
+
+ idev->name = mce_kbd->name;
+ idev->phys = mce_kbd->phys;
+
+ /* Keyboard bits */
+ set_bit(EV_KEY, idev->evbit);
+ set_bit(EV_REP, idev->evbit);
+ for (i = 0; i < sizeof(kbd_keycodes); i++)
+ set_bit(kbd_keycodes[i], idev->keybit);
+
+ /* Mouse bits */
+ set_bit(EV_REL, idev->evbit);
+ set_bit(REL_X, idev->relbit);
+ set_bit(REL_Y, idev->relbit);
+ set_bit(BTN_LEFT, idev->keybit);
+ set_bit(BTN_RIGHT, idev->keybit);
+
+ /* Report scancodes too */
+ set_bit(EV_MSC, idev->evbit);
+ set_bit(MSC_SCAN, idev->mscbit);
+
+ setup_timer(&mce_kbd->rx_timeout, mce_kbd_rx_timeout,
+ (unsigned long)mce_kbd);
+
+ input_set_drvdata(idev, mce_kbd);
+
+#if 0
+ /* Adding this reference means two input devices are associated with
+ * this rc-core device, which ir-keytable doesn't cope with yet */
+ idev->dev.parent = &dev->dev;
+#endif
+
+ ret = input_register_device(idev);
+ if (ret < 0) {
+ input_free_device(idev);
+ return -EIO;
+ }
+
+ mce_kbd->idev = idev;
+
+ return 0;
+}
+
+static int ir_mce_kbd_unregister(struct rc_dev *dev)
+{
+ struct mce_kbd_dec *mce_kbd = &dev->raw->mce_kbd;
+ struct input_dev *idev = mce_kbd->idev;
+
+ del_timer_sync(&mce_kbd->rx_timeout);
+ input_unregister_device(idev);
+
+ return 0;
+}
+
+static struct ir_raw_handler mce_kbd_handler = {
+ .protocols = RC_BIT_MCE_KBD,
+ .decode = ir_mce_kbd_decode,
+ .raw_register = ir_mce_kbd_register,
+ .raw_unregister = ir_mce_kbd_unregister,
+};
+
+static int __init ir_mce_kbd_decode_init(void)
+{
+ ir_raw_handler_register(&mce_kbd_handler);
+
+ printk(KERN_INFO "IR MCE Keyboard/mouse protocol handler initialized\n");
+ return 0;
+}
+
+static void __exit ir_mce_kbd_decode_exit(void)
+{
+ ir_raw_handler_unregister(&mce_kbd_handler);
+}
+
+module_init(ir_mce_kbd_decode_init);
+module_exit(ir_mce_kbd_decode_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
+MODULE_DESCRIPTION("MCE Keyboard/mouse IR protocol decoder");
diff --git a/drivers/media/rc/ir-nec-decoder.c b/drivers/media/rc/ir-nec-decoder.c
index 7b58b4a1729..35c42e5e270 100644
--- a/drivers/media/rc/ir-nec-decoder.c
+++ b/drivers/media/rc/ir-nec-decoder.c
@@ -1,6 +1,6 @@
/* ir-nec-decoder.c - handle NEC IR Pulse/Space protocol
*
- * Copyright (C) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (C) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -13,6 +13,7 @@
*/
#include <linux/bitrev.h>
+#include <linux/module.h>
#include "rc-core-priv.h"
#define NEC_NBITS 32
@@ -49,8 +50,9 @@ static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev)
struct nec_dec *data = &dev->raw->nec;
u32 scancode;
u8 address, not_address, command, not_command;
+ bool send_32bits = false;
- if (!(dev->raw->enabled_protocols & RC_TYPE_NEC))
+ if (!rc_protocols_enabled(dev, RC_BIT_NEC))
return 0;
if (!is_timing_event(ev)) {
@@ -68,7 +70,7 @@ static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev)
if (!ev.pulse)
break;
- if (eq_margin(ev.duration, NEC_HEADER_PULSE, NEC_UNIT / 2)) {
+ if (eq_margin(ev.duration, NEC_HEADER_PULSE, NEC_UNIT * 2)) {
data->is_nec_x = false;
data->necx_repeat = false;
} else if (eq_margin(ev.duration, NECX_HEADER_PULSE, NEC_UNIT / 2))
@@ -84,7 +86,7 @@ static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev)
if (ev.pulse)
break;
- if (eq_margin(ev.duration, NEC_HEADER_SPACE, NEC_UNIT / 2)) {
+ if (eq_margin(ev.duration, NEC_HEADER_SPACE, NEC_UNIT)) {
data->state = STATE_BIT_PULSE;
return 0;
} else if (eq_margin(ev.duration, NEC_REPEAT_SPACE, NEC_UNIT / 2)) {
@@ -164,10 +166,15 @@ static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev)
if ((command ^ not_command) != 0xff) {
IR_dprintk(1, "NEC checksum error: received 0x%08x\n",
data->bits);
- break;
+ send_32bits = true;
}
- if ((address ^ not_address) != 0xff) {
+ if (send_32bits) {
+ /* NEC transport, but modified protocol, used by at
+ * least Apple and TiVo remotes */
+ scancode = data->bits;
+ IR_dprintk(1, "NEC (modified) scancode 0x%08x\n", scancode);
+ } else if ((address ^ not_address) != 0xff) {
/* Extended NEC */
scancode = address << 16 |
not_address << 8 |
@@ -187,14 +194,14 @@ static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev)
return 0;
}
- IR_dprintk(1, "NEC decode failed at state %d (%uus %s)\n",
- data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+ IR_dprintk(1, "NEC decode failed at count %d state %d (%uus %s)\n",
+ data->count, data->state, TO_US(ev.duration), TO_STR(ev.pulse));
data->state = STATE_INACTIVE;
return -EINVAL;
}
static struct ir_raw_handler nec_handler = {
- .protocols = RC_TYPE_NEC,
+ .protocols = RC_BIT_NEC,
.decode = ir_nec_decode,
};
@@ -215,6 +222,6 @@ module_init(ir_nec_decode_init);
module_exit(ir_nec_decode_exit);
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
MODULE_DESCRIPTION("NEC IR protocol decoder");
diff --git a/drivers/media/rc/ir-raw.c b/drivers/media/rc/ir-raw.c
index 73230ff93b8..763c9d131d0 100644
--- a/drivers/media/rc/ir-raw.c
+++ b/drivers/media/rc/ir-raw.c
@@ -1,6 +1,6 @@
/* ir-raw.c - handle IR pulse/space events
*
- * Copyright (C) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (C) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -12,8 +12,10 @@
* GNU General Public License for more details.
*/
+#include <linux/export.h>
#include <linux/kthread.h>
#include <linux/mutex.h>
+#include <linux/kmod.h>
#include <linux/sched.h>
#include <linux/freezer.h>
#include "rc-core-priv.h"
@@ -29,11 +31,6 @@ static DEFINE_MUTEX(ir_raw_handler_lock);
static LIST_HEAD(ir_raw_handler_list);
static u64 available_protocols;
-#ifdef MODULE
-/* Used to load the decoders */
-static struct work_struct wq_load;
-#endif
-
static int ir_raw_event_thread(void *data)
{
struct ir_raw_event ev;
@@ -44,9 +41,9 @@ static int ir_raw_event_thread(void *data)
while (!kthread_should_stop()) {
spin_lock_irq(&raw->lock);
- retval = kfifo_out(&raw->kfifo, &ev, sizeof(ev));
+ retval = kfifo_len(&raw->kfifo);
- if (!retval) {
+ if (retval < sizeof(ev)) {
set_current_state(TASK_INTERRUPTIBLE);
if (kthread_should_stop())
@@ -57,11 +54,9 @@ static int ir_raw_event_thread(void *data)
continue;
}
+ retval = kfifo_out(&raw->kfifo, &ev, sizeof(ev));
spin_unlock_irq(&raw->lock);
-
- BUG_ON(retval != sizeof(ev));
-
mutex_lock(&ir_raw_handler_lock);
list_for_each_entry(handler, &ir_raw_handler_list, list)
handler->decode(raw->dev, ev);
@@ -112,21 +107,22 @@ int ir_raw_event_store_edge(struct rc_dev *dev, enum raw_event_type type)
{
ktime_t now;
s64 delta; /* ns */
- struct ir_raw_event ev;
+ DEFINE_IR_RAW_EVENT(ev);
int rc = 0;
+ int delay;
if (!dev->raw)
return -EINVAL;
now = ktime_get();
delta = ktime_to_ns(ktime_sub(now, dev->raw->last_event));
+ delay = MS_TO_NS(dev->input_dev->rep[REP_DELAY]);
/* Check for a long duration since last event or if we're
* being called for the first time, note that delta can't
* possibly be negative.
*/
- ev.duration = 0;
- if (delta > IR_MAX_DURATION || !dev->raw->last_type)
+ if (delta > delay || !dev->raw->last_type)
type |= IR_START_EVENT;
else
ev.duration = delta;
@@ -154,9 +150,11 @@ EXPORT_SYMBOL_GPL(ir_raw_event_store_edge);
* @type: the type of the event that has occurred
*
* This routine (which may be called from an interrupt context) works
- * in similiar manner to ir_raw_event_store_edge.
+ * in similar manner to ir_raw_event_store_edge.
* This routine is intended for devices with limited internal buffer
- * It automerges samples of same type, and handles timeouts
+ * It automerges samples of same type, and handles timeouts. Returns non-zero
+ * if the event was added, and zero if the event was ignored due to idle
+ * processing.
*/
int ir_raw_event_store_with_filter(struct rc_dev *dev, struct ir_raw_event *ev)
{
@@ -183,7 +181,7 @@ int ir_raw_event_store_with_filter(struct rc_dev *dev, struct ir_raw_event *ev)
dev->raw->this_ev.duration >= dev->timeout)
ir_raw_event_set_idle(dev, true);
- return 0;
+ return 1;
}
EXPORT_SYMBOL_GPL(ir_raw_event_store_with_filter);
@@ -258,7 +256,7 @@ int ir_raw_event_register(struct rc_dev *dev)
return -ENOMEM;
dev->raw->dev = dev;
- dev->raw->enabled_protocols = ~0;
+ rc_set_enabled_protocols(dev, ~0);
rc = kfifo_alloc(&dev->raw->kfifo,
sizeof(struct ir_raw_event) * MAX_IR_EVENT_SIZE,
GFP_KERNEL);
@@ -344,8 +342,7 @@ void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler)
}
EXPORT_SYMBOL(ir_raw_handler_unregister);
-#ifdef MODULE
-static void init_decoders(struct work_struct *work)
+void ir_raw_init(void)
{
/* Load the decoder modules */
@@ -354,18 +351,12 @@ static void init_decoders(struct work_struct *work)
load_rc6_decode();
load_jvc_decode();
load_sony_decode();
+ load_sanyo_decode();
+ load_sharp_decode();
+ load_mce_kbd_decode();
load_lirc_codec();
/* If needed, we may later add some init code. In this case,
it is needed to change the CONFIG_MODULE test at rc-core.h
*/
}
-#endif
-
-void ir_raw_init(void)
-{
-#ifdef MODULE
- INIT_WORK(&wq_load, init_decoders);
- schedule_work(&wq_load);
-#endif
-}
diff --git a/drivers/media/rc/ir-rc5-decoder.c b/drivers/media/rc/ir-rc5-decoder.c
index ebdba553991..4295d9b250c 100644
--- a/drivers/media/rc/ir-rc5-decoder.c
+++ b/drivers/media/rc/ir-rc5-decoder.c
@@ -1,6 +1,6 @@
/* ir-rc5-decoder.c - handle RC5(x) IR Pulse/Space protocol
*
- * Copyright (C) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (C) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -21,6 +21,7 @@
*/
#include "rc-core-priv.h"
+#include <linux/module.h>
#define RC5_NBITS 14
#define RC5X_NBITS 20
@@ -51,8 +52,8 @@ static int ir_rc5_decode(struct rc_dev *dev, struct ir_raw_event ev)
u8 toggle;
u32 scancode;
- if (!(dev->raw->enabled_protocols & RC_TYPE_RC5))
- return 0;
+ if (!rc_protocols_enabled(dev, RC_BIT_RC5 | RC_BIT_RC5X))
+ return 0;
if (!is_timing_event(ev)) {
if (ev.reset)
@@ -127,6 +128,10 @@ again:
if (data->wanted_bits == RC5X_NBITS) {
/* RC5X */
u8 xdata, command, system;
+ if (!rc_protocols_enabled(dev, RC_BIT_RC5X)) {
+ data->state = STATE_INACTIVE;
+ return 0;
+ }
xdata = (data->bits & 0x0003F) >> 0;
command = (data->bits & 0x00FC0) >> 6;
system = (data->bits & 0x1F000) >> 12;
@@ -140,6 +145,10 @@ again:
} else {
/* RC5 */
u8 command, system;
+ if (!rc_protocols_enabled(dev, RC_BIT_RC5)) {
+ data->state = STATE_INACTIVE;
+ return 0;
+ }
command = (data->bits & 0x0003F) >> 0;
system = (data->bits & 0x007C0) >> 6;
toggle = (data->bits & 0x00800) ? 1 : 0;
@@ -163,7 +172,7 @@ out:
}
static struct ir_raw_handler rc5_handler = {
- .protocols = RC_TYPE_RC5,
+ .protocols = RC_BIT_RC5 | RC_BIT_RC5X,
.decode = ir_rc5_decode,
};
@@ -184,6 +193,6 @@ module_init(ir_rc5_decode_init);
module_exit(ir_rc5_decode_exit);
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
MODULE_DESCRIPTION("RC5(x) IR protocol decoder");
diff --git a/drivers/media/rc/ir-rc5-sz-decoder.c b/drivers/media/rc/ir-rc5-sz-decoder.c
index 90aa8868629..dc18b7434db 100644
--- a/drivers/media/rc/ir-rc5-sz-decoder.c
+++ b/drivers/media/rc/ir-rc5-sz-decoder.c
@@ -1,6 +1,6 @@
/* ir-rc5-sz-decoder.c - handle RC5 Streamzap IR Pulse/Space protocol
*
- * Copyright (C) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (C) 2010 by Mauro Carvalho Chehab
* Copyright (C) 2010 by Jarod Wilson <jarod@redhat.com>
*
* This program is free software; you can redistribute it and/or modify
@@ -21,6 +21,7 @@
*/
#include "rc-core-priv.h"
+#include <linux/module.h>
#define RC5_SZ_NBITS 15
#define RC5_UNIT 888888 /* ns */
@@ -47,8 +48,8 @@ static int ir_rc5_sz_decode(struct rc_dev *dev, struct ir_raw_event ev)
u8 toggle, command, system;
u32 scancode;
- if (!(dev->raw->enabled_protocols & RC_TYPE_RC5_SZ))
- return 0;
+ if (!rc_protocols_enabled(dev, RC_BIT_RC5_SZ))
+ return 0;
if (!is_timing_event(ev)) {
if (ev.reset)
@@ -127,7 +128,7 @@ out:
}
static struct ir_raw_handler rc5_sz_handler = {
- .protocols = RC_TYPE_RC5_SZ,
+ .protocols = RC_BIT_RC5_SZ,
.decode = ir_rc5_sz_decode,
};
diff --git a/drivers/media/rc/ir-rc6-decoder.c b/drivers/media/rc/ir-rc6-decoder.c
index 755dafa3871..cfbd64e3999 100644
--- a/drivers/media/rc/ir-rc6-decoder.c
+++ b/drivers/media/rc/ir-rc6-decoder.c
@@ -13,28 +13,36 @@
*/
#include "rc-core-priv.h"
+#include <linux/module.h>
/*
* This decoder currently supports:
* RC6-0-16 (standard toggle bit in header)
+ * RC6-6A-20 (no toggle bit)
* RC6-6A-24 (no toggle bit)
* RC6-6A-32 (MCE version with toggle bit in body)
*/
-#define RC6_UNIT 444444 /* us */
+#define RC6_UNIT 444444 /* nanosecs */
#define RC6_HEADER_NBITS 4 /* not including toggle bit */
#define RC6_0_NBITS 16
-#define RC6_6A_SMALL_NBITS 24
-#define RC6_6A_LARGE_NBITS 32
+#define RC6_6A_32_NBITS 32
+#define RC6_6A_NBITS 128 /* Variable 8..128 */
#define RC6_PREFIX_PULSE (6 * RC6_UNIT)
#define RC6_PREFIX_SPACE (2 * RC6_UNIT)
#define RC6_BIT_START (1 * RC6_UNIT)
#define RC6_BIT_END (1 * RC6_UNIT)
#define RC6_TOGGLE_START (2 * RC6_UNIT)
#define RC6_TOGGLE_END (2 * RC6_UNIT)
+#define RC6_SUFFIX_SPACE (6 * RC6_UNIT)
#define RC6_MODE_MASK 0x07 /* for the header bits */
#define RC6_STARTBIT_MASK 0x08 /* for the header bits */
#define RC6_6A_MCE_TOGGLE_MASK 0x8000 /* for the body bits */
+#define RC6_6A_LCC_MASK 0xffff0000 /* RC6-6A-32 long customer code mask */
+#define RC6_6A_MCE_CC 0x800f0000 /* MCE customer code */
+#ifndef CHAR_BIT
+#define CHAR_BIT 8 /* Normally in <limits.h> */
+#endif
enum rc6_mode {
RC6_MODE_0,
@@ -81,7 +89,9 @@ static int ir_rc6_decode(struct rc_dev *dev, struct ir_raw_event ev)
u32 scancode;
u8 toggle;
- if (!(dev->raw->enabled_protocols & RC_TYPE_RC6))
+ if (!rc_protocols_enabled(dev, RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 |
+ RC_BIT_RC6_6A_24 | RC_BIT_RC6_6A_32 |
+ RC_BIT_RC6_MCE))
return 0;
if (!is_timing_event(ev)) {
@@ -124,6 +134,7 @@ again:
break;
data->state = STATE_HEADER_BIT_START;
+ data->header = 0;
return 0;
case STATE_HEADER_BIT_START:
@@ -170,20 +181,14 @@ again:
data->state = STATE_BODY_BIT_START;
decrease_duration(&ev, RC6_TOGGLE_END);
data->count = 0;
+ data->body = 0;
switch (rc6_mode(data)) {
case RC6_MODE_0:
data->wanted_bits = RC6_0_NBITS;
break;
case RC6_MODE_6A:
- /* This might look weird, but we basically
- check the value of the first body bit to
- determine the number of bits in mode 6A */
- if ((!ev.pulse && !geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2)) ||
- geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2))
- data->wanted_bits = RC6_6A_LARGE_NBITS;
- else
- data->wanted_bits = RC6_6A_SMALL_NBITS;
+ data->wanted_bits = RC6_6A_NBITS;
break;
default:
IR_dprintk(1, "RC6 unknown mode\n");
@@ -192,15 +197,21 @@ again:
goto again;
case STATE_BODY_BIT_START:
- if (!eq_margin(ev.duration, RC6_BIT_START, RC6_UNIT / 2))
- break;
-
- data->body <<= 1;
- if (ev.pulse)
- data->body |= 1;
- data->count++;
- data->state = STATE_BODY_BIT_END;
- return 0;
+ if (eq_margin(ev.duration, RC6_BIT_START, RC6_UNIT / 2)) {
+ /* Discard LSB's that won't fit in data->body */
+ if (data->count++ < CHAR_BIT * sizeof data->body) {
+ data->body <<= 1;
+ if (ev.pulse)
+ data->body |= 1;
+ }
+ data->state = STATE_BODY_BIT_END;
+ return 0;
+ } else if (RC6_MODE_6A == rc6_mode(data) && !ev.pulse &&
+ geq_margin(ev.duration, RC6_SUFFIX_SPACE, RC6_UNIT / 2)) {
+ data->state = STATE_FINISHED;
+ goto again;
+ }
+ break;
case STATE_BODY_BIT_END:
if (!is_transition(&ev, &dev->raw->prev_ev))
@@ -220,20 +231,27 @@ again:
switch (rc6_mode(data)) {
case RC6_MODE_0:
- scancode = data->body & 0xffff;
+ scancode = data->body;
toggle = data->toggle;
IR_dprintk(1, "RC6(0) scancode 0x%04x (toggle: %u)\n",
scancode, toggle);
break;
case RC6_MODE_6A:
- if (data->wanted_bits == RC6_6A_LARGE_NBITS) {
- toggle = data->body & RC6_6A_MCE_TOGGLE_MASK ? 1 : 0;
- scancode = data->body & ~RC6_6A_MCE_TOGGLE_MASK;
+ if (data->count > CHAR_BIT * sizeof data->body) {
+ IR_dprintk(1, "RC6 too many (%u) data bits\n",
+ data->count);
+ goto out;
+ }
+
+ scancode = data->body;
+ if (data->count == RC6_6A_32_NBITS &&
+ (scancode & RC6_6A_LCC_MASK) == RC6_6A_MCE_CC) {
+ /* MCE RC */
+ toggle = (scancode & RC6_6A_MCE_TOGGLE_MASK) ? 1 : 0;
+ scancode &= ~RC6_6A_MCE_TOGGLE_MASK;
} else {
toggle = 0;
- scancode = data->body & 0xffffff;
}
-
IR_dprintk(1, "RC6(6A) scancode 0x%08x (toggle: %u)\n",
scancode, toggle);
break;
@@ -255,7 +273,9 @@ out:
}
static struct ir_raw_handler rc6_handler = {
- .protocols = RC_TYPE_RC6,
+ .protocols = RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 |
+ RC_BIT_RC6_6A_24 | RC_BIT_RC6_6A_32 |
+ RC_BIT_RC6_MCE,
.decode = ir_rc6_decode,
};
diff --git a/drivers/media/rc/ir-rx51.c b/drivers/media/rc/ir-rx51.c
new file mode 100644
index 00000000000..b1e19a26208
--- /dev/null
+++ b/drivers/media/rc/ir-rx51.c
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Based on lirc_serial.c
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+
+#include <plat/dmtimer.h>
+#include <plat/clock.h>
+
+#include <media/lirc.h>
+#include <media/lirc_dev.h>
+#include <media/ir-rx51.h>
+
+#define LIRC_RX51_DRIVER_FEATURES (LIRC_CAN_SET_SEND_DUTY_CYCLE | \
+ LIRC_CAN_SET_SEND_CARRIER | \
+ LIRC_CAN_SEND_PULSE)
+
+#define DRIVER_NAME "lirc_rx51"
+
+#define WBUF_LEN 256
+
+#define TIMER_MAX_VALUE 0xffffffff
+
+struct lirc_rx51 {
+ struct omap_dm_timer *pwm_timer;
+ struct omap_dm_timer *pulse_timer;
+ struct device *dev;
+ struct lirc_rx51_platform_data *pdata;
+ wait_queue_head_t wqueue;
+
+ unsigned long fclk_khz;
+ unsigned int freq; /* carrier frequency */
+ unsigned int duty_cycle; /* carrier duty cycle */
+ unsigned int irq_num;
+ unsigned int match;
+ int wbuf[WBUF_LEN];
+ int wbuf_index;
+ unsigned long device_is_open;
+ int pwm_timer_num;
+};
+
+static void lirc_rx51_on(struct lirc_rx51 *lirc_rx51)
+{
+ omap_dm_timer_set_pwm(lirc_rx51->pwm_timer, 0, 1,
+ OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE);
+}
+
+static void lirc_rx51_off(struct lirc_rx51 *lirc_rx51)
+{
+ omap_dm_timer_set_pwm(lirc_rx51->pwm_timer, 0, 1,
+ OMAP_TIMER_TRIGGER_NONE);
+}
+
+static int init_timing_params(struct lirc_rx51 *lirc_rx51)
+{
+ u32 load, match;
+
+ load = -(lirc_rx51->fclk_khz * 1000 / lirc_rx51->freq);
+ match = -(lirc_rx51->duty_cycle * -load / 100);
+ omap_dm_timer_set_load(lirc_rx51->pwm_timer, 1, load);
+ omap_dm_timer_set_match(lirc_rx51->pwm_timer, 1, match);
+ omap_dm_timer_write_counter(lirc_rx51->pwm_timer, TIMER_MAX_VALUE - 2);
+ omap_dm_timer_start(lirc_rx51->pwm_timer);
+ omap_dm_timer_set_int_enable(lirc_rx51->pulse_timer, 0);
+ omap_dm_timer_start(lirc_rx51->pulse_timer);
+
+ lirc_rx51->match = 0;
+
+ return 0;
+}
+
+#define tics_after(a, b) ((long)(b) - (long)(a) < 0)
+
+static int pulse_timer_set_timeout(struct lirc_rx51 *lirc_rx51, int usec)
+{
+ int counter;
+
+ BUG_ON(usec < 0);
+
+ if (lirc_rx51->match == 0)
+ counter = omap_dm_timer_read_counter(lirc_rx51->pulse_timer);
+ else
+ counter = lirc_rx51->match;
+
+ counter += (u32)(lirc_rx51->fclk_khz * usec / (1000));
+ omap_dm_timer_set_match(lirc_rx51->pulse_timer, 1, counter);
+ omap_dm_timer_set_int_enable(lirc_rx51->pulse_timer,
+ OMAP_TIMER_INT_MATCH);
+ if (tics_after(omap_dm_timer_read_counter(lirc_rx51->pulse_timer),
+ counter)) {
+ return 1;
+ }
+ return 0;
+}
+
+static irqreturn_t lirc_rx51_interrupt_handler(int irq, void *ptr)
+{
+ unsigned int retval;
+ struct lirc_rx51 *lirc_rx51 = ptr;
+
+ retval = omap_dm_timer_read_status(lirc_rx51->pulse_timer);
+ if (!retval)
+ return IRQ_NONE;
+
+ if (retval & ~OMAP_TIMER_INT_MATCH)
+ dev_err_ratelimited(lirc_rx51->dev,
+ ": Unexpected interrupt source: %x\n", retval);
+
+ omap_dm_timer_write_status(lirc_rx51->pulse_timer,
+ OMAP_TIMER_INT_MATCH |
+ OMAP_TIMER_INT_OVERFLOW |
+ OMAP_TIMER_INT_CAPTURE);
+ if (lirc_rx51->wbuf_index < 0) {
+ dev_err_ratelimited(lirc_rx51->dev,
+ ": BUG wbuf_index has value of %i\n",
+ lirc_rx51->wbuf_index);
+ goto end;
+ }
+
+ /*
+ * If we happen to hit an odd latency spike, loop through the
+ * pulses until we catch up.
+ */
+ do {
+ if (lirc_rx51->wbuf_index >= WBUF_LEN)
+ goto end;
+ if (lirc_rx51->wbuf[lirc_rx51->wbuf_index] == -1)
+ goto end;
+
+ if (lirc_rx51->wbuf_index % 2)
+ lirc_rx51_off(lirc_rx51);
+ else
+ lirc_rx51_on(lirc_rx51);
+
+ retval = pulse_timer_set_timeout(lirc_rx51,
+ lirc_rx51->wbuf[lirc_rx51->wbuf_index]);
+ lirc_rx51->wbuf_index++;
+
+ } while (retval);
+
+ return IRQ_HANDLED;
+end:
+ /* Stop TX here */
+ lirc_rx51_off(lirc_rx51);
+ lirc_rx51->wbuf_index = -1;
+ omap_dm_timer_stop(lirc_rx51->pwm_timer);
+ omap_dm_timer_stop(lirc_rx51->pulse_timer);
+ omap_dm_timer_set_int_enable(lirc_rx51->pulse_timer, 0);
+ wake_up_interruptible(&lirc_rx51->wqueue);
+
+ return IRQ_HANDLED;
+}
+
+static int lirc_rx51_init_port(struct lirc_rx51 *lirc_rx51)
+{
+ struct clk *clk_fclk;
+ int retval, pwm_timer = lirc_rx51->pwm_timer_num;
+
+ lirc_rx51->pwm_timer = omap_dm_timer_request_specific(pwm_timer);
+ if (lirc_rx51->pwm_timer == NULL) {
+ dev_err(lirc_rx51->dev, ": Error requesting GPT%d timer\n",
+ pwm_timer);
+ return -EBUSY;
+ }
+
+ lirc_rx51->pulse_timer = omap_dm_timer_request();
+ if (lirc_rx51->pulse_timer == NULL) {
+ dev_err(lirc_rx51->dev, ": Error requesting pulse timer\n");
+ retval = -EBUSY;
+ goto err1;
+ }
+
+ omap_dm_timer_set_source(lirc_rx51->pwm_timer, OMAP_TIMER_SRC_SYS_CLK);
+ omap_dm_timer_set_source(lirc_rx51->pulse_timer,
+ OMAP_TIMER_SRC_SYS_CLK);
+
+ omap_dm_timer_enable(lirc_rx51->pwm_timer);
+ omap_dm_timer_enable(lirc_rx51->pulse_timer);
+
+ lirc_rx51->irq_num = omap_dm_timer_get_irq(lirc_rx51->pulse_timer);
+ retval = request_irq(lirc_rx51->irq_num, lirc_rx51_interrupt_handler,
+ IRQF_SHARED, "lirc_pulse_timer", lirc_rx51);
+ if (retval) {
+ dev_err(lirc_rx51->dev, ": Failed to request interrupt line\n");
+ goto err2;
+ }
+
+ clk_fclk = omap_dm_timer_get_fclk(lirc_rx51->pwm_timer);
+ lirc_rx51->fclk_khz = clk_fclk->rate / 1000;
+
+ return 0;
+
+err2:
+ omap_dm_timer_free(lirc_rx51->pulse_timer);
+err1:
+ omap_dm_timer_free(lirc_rx51->pwm_timer);
+
+ return retval;
+}
+
+static int lirc_rx51_free_port(struct lirc_rx51 *lirc_rx51)
+{
+ omap_dm_timer_set_int_enable(lirc_rx51->pulse_timer, 0);
+ free_irq(lirc_rx51->irq_num, lirc_rx51);
+ lirc_rx51_off(lirc_rx51);
+ omap_dm_timer_disable(lirc_rx51->pwm_timer);
+ omap_dm_timer_disable(lirc_rx51->pulse_timer);
+ omap_dm_timer_free(lirc_rx51->pwm_timer);
+ omap_dm_timer_free(lirc_rx51->pulse_timer);
+ lirc_rx51->wbuf_index = -1;
+
+ return 0;
+}
+
+static ssize_t lirc_rx51_write(struct file *file, const char *buf,
+ size_t n, loff_t *ppos)
+{
+ int count, i;
+ struct lirc_rx51 *lirc_rx51 = file->private_data;
+
+ if (n % sizeof(int))
+ return -EINVAL;
+
+ count = n / sizeof(int);
+ if ((count > WBUF_LEN) || (count % 2 == 0))
+ return -EINVAL;
+
+ /* Wait any pending transfers to finish */
+ wait_event_interruptible(lirc_rx51->wqueue, lirc_rx51->wbuf_index < 0);
+
+ if (copy_from_user(lirc_rx51->wbuf, buf, n))
+ return -EFAULT;
+
+ /* Sanity check the input pulses */
+ for (i = 0; i < count; i++)
+ if (lirc_rx51->wbuf[i] < 0)
+ return -EINVAL;
+
+ init_timing_params(lirc_rx51);
+ if (count < WBUF_LEN)
+ lirc_rx51->wbuf[count] = -1; /* Insert termination mark */
+
+ /*
+ * Adjust latency requirements so the device doesn't go in too
+ * deep sleep states
+ */
+ lirc_rx51->pdata->set_max_mpu_wakeup_lat(lirc_rx51->dev, 50);
+
+ lirc_rx51_on(lirc_rx51);
+ lirc_rx51->wbuf_index = 1;
+ pulse_timer_set_timeout(lirc_rx51, lirc_rx51->wbuf[0]);
+
+ /*
+ * Don't return back to the userspace until the transfer has
+ * finished
+ */
+ wait_event_interruptible(lirc_rx51->wqueue, lirc_rx51->wbuf_index < 0);
+
+ /* We can sleep again */
+ lirc_rx51->pdata->set_max_mpu_wakeup_lat(lirc_rx51->dev, -1);
+
+ return n;
+}
+
+static long lirc_rx51_ioctl(struct file *filep,
+ unsigned int cmd, unsigned long arg)
+{
+ int result;
+ unsigned long value;
+ unsigned int ivalue;
+ struct lirc_rx51 *lirc_rx51 = filep->private_data;
+
+ switch (cmd) {
+ case LIRC_GET_SEND_MODE:
+ result = put_user(LIRC_MODE_PULSE, (unsigned long *)arg);
+ if (result)
+ return result;
+ break;
+
+ case LIRC_SET_SEND_MODE:
+ result = get_user(value, (unsigned long *)arg);
+ if (result)
+ return result;
+
+ /* only LIRC_MODE_PULSE supported */
+ if (value != LIRC_MODE_PULSE)
+ return -ENOSYS;
+ break;
+
+ case LIRC_GET_REC_MODE:
+ result = put_user(0, (unsigned long *) arg);
+ if (result)
+ return result;
+ break;
+
+ case LIRC_GET_LENGTH:
+ return -ENOSYS;
+ break;
+
+ case LIRC_SET_SEND_DUTY_CYCLE:
+ result = get_user(ivalue, (unsigned int *) arg);
+ if (result)
+ return result;
+
+ if (ivalue <= 0 || ivalue > 100) {
+ dev_err(lirc_rx51->dev, ": invalid duty cycle %d\n",
+ ivalue);
+ return -EINVAL;
+ }
+
+ lirc_rx51->duty_cycle = ivalue;
+ break;
+
+ case LIRC_SET_SEND_CARRIER:
+ result = get_user(ivalue, (unsigned int *) arg);
+ if (result)
+ return result;
+
+ if (ivalue > 500000 || ivalue < 20000) {
+ dev_err(lirc_rx51->dev, ": invalid carrier freq %d\n",
+ ivalue);
+ return -EINVAL;
+ }
+
+ lirc_rx51->freq = ivalue;
+ break;
+
+ case LIRC_GET_FEATURES:
+ result = put_user(LIRC_RX51_DRIVER_FEATURES,
+ (unsigned long *) arg);
+ if (result)
+ return result;
+ break;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ return 0;
+}
+
+static int lirc_rx51_open(struct inode *inode, struct file *file)
+{
+ struct lirc_rx51 *lirc_rx51 = lirc_get_pdata(file);
+ BUG_ON(!lirc_rx51);
+
+ file->private_data = lirc_rx51;
+
+ if (test_and_set_bit(1, &lirc_rx51->device_is_open))
+ return -EBUSY;
+
+ return lirc_rx51_init_port(lirc_rx51);
+}
+
+static int lirc_rx51_release(struct inode *inode, struct file *file)
+{
+ struct lirc_rx51 *lirc_rx51 = file->private_data;
+
+ lirc_rx51_free_port(lirc_rx51);
+
+ clear_bit(1, &lirc_rx51->device_is_open);
+
+ return 0;
+}
+
+static struct lirc_rx51 lirc_rx51 = {
+ .freq = 38000,
+ .duty_cycle = 50,
+ .wbuf_index = -1,
+};
+
+static const struct file_operations lirc_fops = {
+ .owner = THIS_MODULE,
+ .write = lirc_rx51_write,
+ .unlocked_ioctl = lirc_rx51_ioctl,
+ .read = lirc_dev_fop_read,
+ .poll = lirc_dev_fop_poll,
+ .open = lirc_rx51_open,
+ .release = lirc_rx51_release,
+};
+
+static struct lirc_driver lirc_rx51_driver = {
+ .name = DRIVER_NAME,
+ .minor = -1,
+ .code_length = 1,
+ .data = &lirc_rx51,
+ .fops = &lirc_fops,
+ .owner = THIS_MODULE,
+};
+
+#ifdef CONFIG_PM
+
+static int lirc_rx51_suspend(struct platform_device *dev, pm_message_t state)
+{
+ /*
+ * In case the device is still open, do not suspend. Normally
+ * this should not be a problem as lircd only keeps the device
+ * open only for short periods of time. We also don't want to
+ * get involved with race conditions that might happen if we
+ * were in a middle of a transmit. Thus, we defer any suspend
+ * actions until transmit has completed.
+ */
+ if (test_and_set_bit(1, &lirc_rx51.device_is_open))
+ return -EAGAIN;
+
+ clear_bit(1, &lirc_rx51.device_is_open);
+
+ return 0;
+}
+
+static int lirc_rx51_resume(struct platform_device *dev)
+{
+ return 0;
+}
+
+#else
+
+#define lirc_rx51_suspend NULL
+#define lirc_rx51_resume NULL
+
+#endif /* CONFIG_PM */
+
+static int lirc_rx51_probe(struct platform_device *dev)
+{
+ lirc_rx51_driver.features = LIRC_RX51_DRIVER_FEATURES;
+ lirc_rx51.pdata = dev->dev.platform_data;
+ lirc_rx51.pwm_timer_num = lirc_rx51.pdata->pwm_timer;
+ lirc_rx51.dev = &dev->dev;
+ lirc_rx51_driver.dev = &dev->dev;
+ lirc_rx51_driver.minor = lirc_register_driver(&lirc_rx51_driver);
+ init_waitqueue_head(&lirc_rx51.wqueue);
+
+ if (lirc_rx51_driver.minor < 0) {
+ dev_err(lirc_rx51.dev, ": lirc_register_driver failed: %d\n",
+ lirc_rx51_driver.minor);
+ return lirc_rx51_driver.minor;
+ }
+ dev_info(lirc_rx51.dev, "registration ok, minor: %d, pwm: %d\n",
+ lirc_rx51_driver.minor, lirc_rx51.pwm_timer_num);
+
+ return 0;
+}
+
+static int lirc_rx51_remove(struct platform_device *dev)
+{
+ return lirc_unregister_driver(lirc_rx51_driver.minor);
+}
+
+struct platform_driver lirc_rx51_platform_driver = {
+ .probe = lirc_rx51_probe,
+ .remove = lirc_rx51_remove,
+ .suspend = lirc_rx51_suspend,
+ .resume = lirc_rx51_resume,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+module_platform_driver(lirc_rx51_platform_driver);
+
+MODULE_DESCRIPTION("LIRC TX driver for Nokia RX51");
+MODULE_AUTHOR("Nokia Corporation");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/rc/ir-sanyo-decoder.c b/drivers/media/rc/ir-sanyo-decoder.c
new file mode 100644
index 00000000000..eb715f04dc2
--- /dev/null
+++ b/drivers/media/rc/ir-sanyo-decoder.c
@@ -0,0 +1,205 @@
+/* ir-sanyo-decoder.c - handle SANYO IR Pulse/Space protocol
+ *
+ * Copyright (C) 2011 by Mauro Carvalho Chehab
+ *
+ * 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 version 2 of the License.
+ *
+ * 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.
+ *
+ * This protocol uses the NEC protocol timings. However, data is formatted as:
+ * 13 bits Custom Code
+ * 13 bits NOT(Custom Code)
+ * 8 bits Key data
+ * 8 bits NOT(Key data)
+ *
+ * According with LIRC, this protocol is used on Sanyo, Aiwa and Chinon
+ * Information for this protocol is available at the Sanyo LC7461 datasheet.
+ */
+
+#include <linux/module.h>
+#include <linux/bitrev.h>
+#include "rc-core-priv.h"
+
+#define SANYO_NBITS (13+13+8+8)
+#define SANYO_UNIT 562500 /* ns */
+#define SANYO_HEADER_PULSE (16 * SANYO_UNIT)
+#define SANYO_HEADER_SPACE (8 * SANYO_UNIT)
+#define SANYO_BIT_PULSE (1 * SANYO_UNIT)
+#define SANYO_BIT_0_SPACE (1 * SANYO_UNIT)
+#define SANYO_BIT_1_SPACE (3 * SANYO_UNIT)
+#define SANYO_REPEAT_SPACE (150 * SANYO_UNIT)
+#define SANYO_TRAILER_PULSE (1 * SANYO_UNIT)
+#define SANYO_TRAILER_SPACE (10 * SANYO_UNIT) /* in fact, 42 */
+
+enum sanyo_state {
+ STATE_INACTIVE,
+ STATE_HEADER_SPACE,
+ STATE_BIT_PULSE,
+ STATE_BIT_SPACE,
+ STATE_TRAILER_PULSE,
+ STATE_TRAILER_SPACE,
+};
+
+/**
+ * ir_sanyo_decode() - Decode one SANYO pulse or space
+ * @dev: the struct rc_dev descriptor of the device
+ * @duration: the struct ir_raw_event descriptor of the pulse/space
+ *
+ * This function returns -EINVAL if the pulse violates the state machine
+ */
+static int ir_sanyo_decode(struct rc_dev *dev, struct ir_raw_event ev)
+{
+ struct sanyo_dec *data = &dev->raw->sanyo;
+ u32 scancode;
+ u8 address, command, not_command;
+
+ if (!rc_protocols_enabled(dev, RC_BIT_SANYO))
+ return 0;
+
+ if (!is_timing_event(ev)) {
+ if (ev.reset) {
+ IR_dprintk(1, "SANYO event reset received. reset to state 0\n");
+ data->state = STATE_INACTIVE;
+ }
+ return 0;
+ }
+
+ IR_dprintk(2, "SANYO decode started at state %d (%uus %s)\n",
+ data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+
+ switch (data->state) {
+
+ case STATE_INACTIVE:
+ if (!ev.pulse)
+ break;
+
+ if (eq_margin(ev.duration, SANYO_HEADER_PULSE, SANYO_UNIT / 2)) {
+ data->count = 0;
+ data->state = STATE_HEADER_SPACE;
+ return 0;
+ }
+ break;
+
+
+ case STATE_HEADER_SPACE:
+ if (ev.pulse)
+ break;
+
+ if (eq_margin(ev.duration, SANYO_HEADER_SPACE, SANYO_UNIT / 2)) {
+ data->state = STATE_BIT_PULSE;
+ return 0;
+ }
+
+ break;
+
+ case STATE_BIT_PULSE:
+ if (!ev.pulse)
+ break;
+
+ if (!eq_margin(ev.duration, SANYO_BIT_PULSE, SANYO_UNIT / 2))
+ break;
+
+ data->state = STATE_BIT_SPACE;
+ return 0;
+
+ case STATE_BIT_SPACE:
+ if (ev.pulse)
+ break;
+
+ if (!data->count && geq_margin(ev.duration, SANYO_REPEAT_SPACE, SANYO_UNIT / 2)) {
+ if (!dev->keypressed) {
+ IR_dprintk(1, "SANYO discarding last key repeat: event after key up\n");
+ } else {
+ rc_repeat(dev);
+ IR_dprintk(1, "SANYO repeat last key\n");
+ data->state = STATE_INACTIVE;
+ }
+ return 0;
+ }
+
+ data->bits <<= 1;
+ if (eq_margin(ev.duration, SANYO_BIT_1_SPACE, SANYO_UNIT / 2))
+ data->bits |= 1;
+ else if (!eq_margin(ev.duration, SANYO_BIT_0_SPACE, SANYO_UNIT / 2))
+ break;
+ data->count++;
+
+ if (data->count == SANYO_NBITS)
+ data->state = STATE_TRAILER_PULSE;
+ else
+ data->state = STATE_BIT_PULSE;
+
+ return 0;
+
+ case STATE_TRAILER_PULSE:
+ if (!ev.pulse)
+ break;
+
+ if (!eq_margin(ev.duration, SANYO_TRAILER_PULSE, SANYO_UNIT / 2))
+ break;
+
+ data->state = STATE_TRAILER_SPACE;
+ return 0;
+
+ case STATE_TRAILER_SPACE:
+ if (ev.pulse)
+ break;
+
+ if (!geq_margin(ev.duration, SANYO_TRAILER_SPACE, SANYO_UNIT / 2))
+ break;
+
+ address = bitrev16((data->bits >> 29) & 0x1fff) >> 3;
+ /* not_address = bitrev16((data->bits >> 16) & 0x1fff) >> 3; */
+ command = bitrev8((data->bits >> 8) & 0xff);
+ not_command = bitrev8((data->bits >> 0) & 0xff);
+
+ if ((command ^ not_command) != 0xff) {
+ IR_dprintk(1, "SANYO checksum error: received 0x%08Lx\n",
+ data->bits);
+ data->state = STATE_INACTIVE;
+ return 0;
+ }
+
+ scancode = address << 8 | command;
+ IR_dprintk(1, "SANYO scancode: 0x%06x\n", scancode);
+ rc_keydown(dev, scancode, 0);
+ data->state = STATE_INACTIVE;
+ return 0;
+ }
+
+ IR_dprintk(1, "SANYO decode failed at count %d state %d (%uus %s)\n",
+ data->count, data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+ data->state = STATE_INACTIVE;
+ return -EINVAL;
+}
+
+static struct ir_raw_handler sanyo_handler = {
+ .protocols = RC_BIT_SANYO,
+ .decode = ir_sanyo_decode,
+};
+
+static int __init ir_sanyo_decode_init(void)
+{
+ ir_raw_handler_register(&sanyo_handler);
+
+ printk(KERN_INFO "IR SANYO protocol handler initialized\n");
+ return 0;
+}
+
+static void __exit ir_sanyo_decode_exit(void)
+{
+ ir_raw_handler_unregister(&sanyo_handler);
+}
+
+module_init(ir_sanyo_decode_init);
+module_exit(ir_sanyo_decode_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
+MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
+MODULE_DESCRIPTION("SANYO IR protocol decoder");
diff --git a/drivers/media/rc/ir-sharp-decoder.c b/drivers/media/rc/ir-sharp-decoder.c
new file mode 100644
index 00000000000..66d20394cea
--- /dev/null
+++ b/drivers/media/rc/ir-sharp-decoder.c
@@ -0,0 +1,200 @@
+/* ir-sharp-decoder.c - handle Sharp IR Pulse/Space protocol
+ *
+ * Copyright (C) 2013-2014 Imagination Technologies Ltd.
+ *
+ * Based on NEC decoder:
+ * Copyright (C) 2010 by Mauro Carvalho Chehab
+ *
+ * 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 version 2 of the License.
+ *
+ * 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 <linux/bitrev.h>
+#include <linux/module.h>
+#include "rc-core-priv.h"
+
+#define SHARP_NBITS 15
+#define SHARP_UNIT 40000 /* ns */
+#define SHARP_BIT_PULSE (8 * SHARP_UNIT) /* 320us */
+#define SHARP_BIT_0_PERIOD (25 * SHARP_UNIT) /* 1ms (680us space) */
+#define SHARP_BIT_1_PERIOD (50 * SHARP_UNIT) /* 2ms (1680ms space) */
+#define SHARP_ECHO_SPACE (1000 * SHARP_UNIT) /* 40 ms */
+#define SHARP_TRAILER_SPACE (125 * SHARP_UNIT) /* 5 ms (even longer) */
+
+enum sharp_state {
+ STATE_INACTIVE,
+ STATE_BIT_PULSE,
+ STATE_BIT_SPACE,
+ STATE_TRAILER_PULSE,
+ STATE_ECHO_SPACE,
+ STATE_TRAILER_SPACE,
+};
+
+/**
+ * ir_sharp_decode() - Decode one Sharp pulse or space
+ * @dev: the struct rc_dev descriptor of the device
+ * @duration: the struct ir_raw_event descriptor of the pulse/space
+ *
+ * This function returns -EINVAL if the pulse violates the state machine
+ */
+static int ir_sharp_decode(struct rc_dev *dev, struct ir_raw_event ev)
+{
+ struct sharp_dec *data = &dev->raw->sharp;
+ u32 msg, echo, address, command, scancode;
+
+ if (!rc_protocols_enabled(dev, RC_BIT_SHARP))
+ return 0;
+
+ if (!is_timing_event(ev)) {
+ if (ev.reset)
+ data->state = STATE_INACTIVE;
+ return 0;
+ }
+
+ IR_dprintk(2, "Sharp decode started at state %d (%uus %s)\n",
+ data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+
+ switch (data->state) {
+
+ case STATE_INACTIVE:
+ if (!ev.pulse)
+ break;
+
+ if (!eq_margin(ev.duration, SHARP_BIT_PULSE,
+ SHARP_BIT_PULSE / 2))
+ break;
+
+ data->count = 0;
+ data->pulse_len = ev.duration;
+ data->state = STATE_BIT_SPACE;
+ return 0;
+
+ case STATE_BIT_PULSE:
+ if (!ev.pulse)
+ break;
+
+ if (!eq_margin(ev.duration, SHARP_BIT_PULSE,
+ SHARP_BIT_PULSE / 2))
+ break;
+
+ data->pulse_len = ev.duration;
+ data->state = STATE_BIT_SPACE;
+ return 0;
+
+ case STATE_BIT_SPACE:
+ if (ev.pulse)
+ break;
+
+ data->bits <<= 1;
+ if (eq_margin(data->pulse_len + ev.duration, SHARP_BIT_1_PERIOD,
+ SHARP_BIT_PULSE * 2))
+ data->bits |= 1;
+ else if (!eq_margin(data->pulse_len + ev.duration,
+ SHARP_BIT_0_PERIOD, SHARP_BIT_PULSE * 2))
+ break;
+ data->count++;
+
+ if (data->count == SHARP_NBITS ||
+ data->count == SHARP_NBITS * 2)
+ data->state = STATE_TRAILER_PULSE;
+ else
+ data->state = STATE_BIT_PULSE;
+
+ return 0;
+
+ case STATE_TRAILER_PULSE:
+ if (!ev.pulse)
+ break;
+
+ if (!eq_margin(ev.duration, SHARP_BIT_PULSE,
+ SHARP_BIT_PULSE / 2))
+ break;
+
+ if (data->count == SHARP_NBITS) {
+ /* exp,chk bits should be 1,0 */
+ if ((data->bits & 0x3) != 0x2)
+ break;
+ data->state = STATE_ECHO_SPACE;
+ } else {
+ data->state = STATE_TRAILER_SPACE;
+ }
+ return 0;
+
+ case STATE_ECHO_SPACE:
+ if (ev.pulse)
+ break;
+
+ if (!eq_margin(ev.duration, SHARP_ECHO_SPACE,
+ SHARP_ECHO_SPACE / 4))
+ break;
+
+ data->state = STATE_BIT_PULSE;
+
+ return 0;
+
+ case STATE_TRAILER_SPACE:
+ if (ev.pulse)
+ break;
+
+ if (!geq_margin(ev.duration, SHARP_TRAILER_SPACE,
+ SHARP_BIT_PULSE / 2))
+ break;
+
+ /* Validate - command, ext, chk should be inverted in 2nd */
+ msg = (data->bits >> 15) & 0x7fff;
+ echo = data->bits & 0x7fff;
+ if ((msg ^ echo) != 0x3ff) {
+ IR_dprintk(1,
+ "Sharp checksum error: received 0x%04x, 0x%04x\n",
+ msg, echo);
+ break;
+ }
+
+ address = bitrev8((msg >> 7) & 0xf8);
+ command = bitrev8((msg >> 2) & 0xff);
+
+ scancode = address << 8 | command;
+ IR_dprintk(1, "Sharp scancode 0x%04x\n", scancode);
+
+ rc_keydown(dev, scancode, 0);
+ data->state = STATE_INACTIVE;
+ return 0;
+ }
+
+ IR_dprintk(1, "Sharp decode failed at count %d state %d (%uus %s)\n",
+ data->count, data->state, TO_US(ev.duration),
+ TO_STR(ev.pulse));
+ data->state = STATE_INACTIVE;
+ return -EINVAL;
+}
+
+static struct ir_raw_handler sharp_handler = {
+ .protocols = RC_BIT_SHARP,
+ .decode = ir_sharp_decode,
+};
+
+static int __init ir_sharp_decode_init(void)
+{
+ ir_raw_handler_register(&sharp_handler);
+
+ pr_info("IR Sharp protocol handler initialized\n");
+ return 0;
+}
+
+static void __exit ir_sharp_decode_exit(void)
+{
+ ir_raw_handler_unregister(&sharp_handler);
+}
+
+module_init(ir_sharp_decode_init);
+module_exit(ir_sharp_decode_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("James Hogan <james.hogan@imgtec.com>");
+MODULE_DESCRIPTION("Sharp IR protocol decoder");
diff --git a/drivers/media/rc/ir-sony-decoder.c b/drivers/media/rc/ir-sony-decoder.c
index a92de80c48d..599c19a7336 100644
--- a/drivers/media/rc/ir-sony-decoder.c
+++ b/drivers/media/rc/ir-sony-decoder.c
@@ -13,6 +13,7 @@
*/
#include <linux/bitrev.h>
+#include <linux/module.h>
#include "rc-core-priv.h"
#define SONY_UNIT 600000 /* ns */
@@ -44,7 +45,8 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev)
u32 scancode;
u8 device, subdevice, function;
- if (!(dev->raw->enabled_protocols & RC_TYPE_SONY))
+ if (!rc_protocols_enabled(dev, RC_BIT_SONY12 | RC_BIT_SONY15 |
+ RC_BIT_SONY20))
return 0;
if (!is_timing_event(ev)) {
@@ -122,16 +124,28 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev)
switch (data->count) {
case 12:
+ if (!rc_protocols_enabled(dev, RC_BIT_SONY12)) {
+ data->state = STATE_INACTIVE;
+ return 0;
+ }
device = bitrev8((data->bits << 3) & 0xF8);
subdevice = 0;
function = bitrev8((data->bits >> 4) & 0xFE);
break;
case 15:
+ if (!rc_protocols_enabled(dev, RC_BIT_SONY15)) {
+ data->state = STATE_INACTIVE;
+ return 0;
+ }
device = bitrev8((data->bits >> 0) & 0xFF);
subdevice = 0;
- function = bitrev8((data->bits >> 7) & 0xFD);
+ function = bitrev8((data->bits >> 7) & 0xFE);
break;
case 20:
+ if (!rc_protocols_enabled(dev, RC_BIT_SONY20)) {
+ data->state = STATE_INACTIVE;
+ return 0;
+ }
device = bitrev8((data->bits >> 5) & 0xF8);
subdevice = bitrev8((data->bits >> 0) & 0xFF);
function = bitrev8((data->bits >> 12) & 0xFE);
@@ -156,7 +170,7 @@ out:
}
static struct ir_raw_handler sony_handler = {
- .protocols = RC_TYPE_SONY,
+ .protocols = RC_BIT_SONY12 | RC_BIT_SONY15 | RC_BIT_SONY20,
.decode = ir_sony_decode,
};
diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c
new file mode 100644
index 00000000000..ab24cc6d365
--- /dev/null
+++ b/drivers/media/rc/ite-cir.c
@@ -0,0 +1,1729 @@
+/*
+ * Driver for ITE Tech Inc. IT8712F/IT8512 CIR
+ *
+ * Copyright (C) 2010 Juan Jesús García de Soria <skandalfo@gmail.com>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA.
+ *
+ * Inspired by the original lirc_it87 and lirc_ite8709 drivers, on top of the
+ * skeleton provided by the nuvoton-cir driver.
+ *
+ * The lirc_it87 driver was originally written by Hans-Gunter Lutke Uphues
+ * <hg_lu@web.de> in 2001, with enhancements by Christoph Bartelmus
+ * <lirc@bartelmus.de>, Andrew Calkin <r_tay@hotmail.com> and James Edwards
+ * <jimbo-lirc@edwardsclan.net>.
+ *
+ * The lirc_ite8709 driver was written by Grégory Lardière
+ * <spmf2004-lirc@yahoo.fr> in 2008.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pnp.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/bitops.h>
+#include <media/rc-core.h>
+#include <linux/pci_ids.h>
+
+#include "ite-cir.h"
+
+/* module parameters */
+
+/* debug level */
+static int debug;
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Enable debugging output");
+
+/* low limit for RX carrier freq, Hz, 0 for no RX demodulation */
+static int rx_low_carrier_freq;
+module_param(rx_low_carrier_freq, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(rx_low_carrier_freq, "Override low RX carrier frequency, Hz, "
+ "0 for no RX demodulation");
+
+/* high limit for RX carrier freq, Hz, 0 for no RX demodulation */
+static int rx_high_carrier_freq;
+module_param(rx_high_carrier_freq, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(rx_high_carrier_freq, "Override high RX carrier frequency, "
+ "Hz, 0 for no RX demodulation");
+
+/* override tx carrier frequency */
+static int tx_carrier_freq;
+module_param(tx_carrier_freq, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(tx_carrier_freq, "Override TX carrier frequency, Hz");
+
+/* override tx duty cycle */
+static int tx_duty_cycle;
+module_param(tx_duty_cycle, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(tx_duty_cycle, "Override TX duty cycle, 1-100");
+
+/* override default sample period */
+static long sample_period;
+module_param(sample_period, long, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(sample_period, "Override carrier sample period, us");
+
+/* override detected model id */
+static int model_number = -1;
+module_param(model_number, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(model_number, "Use this model number, don't autodetect");
+
+
+/* HW-independent code functions */
+
+/* check whether carrier frequency is high frequency */
+static inline bool ite_is_high_carrier_freq(unsigned int freq)
+{
+ return freq >= ITE_HCF_MIN_CARRIER_FREQ;
+}
+
+/* get the bits required to program the carrier frequency in CFQ bits,
+ * unshifted */
+static u8 ite_get_carrier_freq_bits(unsigned int freq)
+{
+ if (ite_is_high_carrier_freq(freq)) {
+ if (freq < 425000)
+ return ITE_CFQ_400;
+
+ else if (freq < 465000)
+ return ITE_CFQ_450;
+
+ else if (freq < 490000)
+ return ITE_CFQ_480;
+
+ else
+ return ITE_CFQ_500;
+ } else {
+ /* trim to limits */
+ if (freq < ITE_LCF_MIN_CARRIER_FREQ)
+ freq = ITE_LCF_MIN_CARRIER_FREQ;
+ if (freq > ITE_LCF_MAX_CARRIER_FREQ)
+ freq = ITE_LCF_MAX_CARRIER_FREQ;
+
+ /* convert to kHz and subtract the base freq */
+ freq =
+ DIV_ROUND_CLOSEST(freq - ITE_LCF_MIN_CARRIER_FREQ,
+ 1000);
+
+ return (u8) freq;
+ }
+}
+
+/* get the bits required to program the pulse with in TXMPW */
+static u8 ite_get_pulse_width_bits(unsigned int freq, int duty_cycle)
+{
+ unsigned long period_ns, on_ns;
+
+ /* sanitize freq into range */
+ if (freq < ITE_LCF_MIN_CARRIER_FREQ)
+ freq = ITE_LCF_MIN_CARRIER_FREQ;
+ if (freq > ITE_HCF_MAX_CARRIER_FREQ)
+ freq = ITE_HCF_MAX_CARRIER_FREQ;
+
+ period_ns = 1000000000UL / freq;
+ on_ns = period_ns * duty_cycle / 100;
+
+ if (ite_is_high_carrier_freq(freq)) {
+ if (on_ns < 750)
+ return ITE_TXMPW_A;
+
+ else if (on_ns < 850)
+ return ITE_TXMPW_B;
+
+ else if (on_ns < 950)
+ return ITE_TXMPW_C;
+
+ else if (on_ns < 1080)
+ return ITE_TXMPW_D;
+
+ else
+ return ITE_TXMPW_E;
+ } else {
+ if (on_ns < 6500)
+ return ITE_TXMPW_A;
+
+ else if (on_ns < 7850)
+ return ITE_TXMPW_B;
+
+ else if (on_ns < 9650)
+ return ITE_TXMPW_C;
+
+ else if (on_ns < 11950)
+ return ITE_TXMPW_D;
+
+ else
+ return ITE_TXMPW_E;
+ }
+}
+
+/* decode raw bytes as received by the hardware, and push them to the ir-core
+ * layer */
+static void ite_decode_bytes(struct ite_dev *dev, const u8 * data, int
+ length)
+{
+ u32 sample_period;
+ unsigned long *ldata;
+ unsigned int next_one, next_zero, size;
+ DEFINE_IR_RAW_EVENT(ev);
+
+ if (length == 0)
+ return;
+
+ sample_period = dev->params.sample_period;
+ ldata = (unsigned long *)data;
+ size = length << 3;
+ next_one = find_next_bit_le(ldata, size, 0);
+ if (next_one > 0) {
+ ev.pulse = true;
+ ev.duration =
+ ITE_BITS_TO_NS(next_one, sample_period);
+ ir_raw_event_store_with_filter(dev->rdev, &ev);
+ }
+
+ while (next_one < size) {
+ next_zero = find_next_zero_bit_le(ldata, size, next_one + 1);
+ ev.pulse = false;
+ ev.duration = ITE_BITS_TO_NS(next_zero - next_one, sample_period);
+ ir_raw_event_store_with_filter(dev->rdev, &ev);
+
+ if (next_zero < size) {
+ next_one =
+ find_next_bit_le(ldata,
+ size,
+ next_zero + 1);
+ ev.pulse = true;
+ ev.duration =
+ ITE_BITS_TO_NS(next_one - next_zero,
+ sample_period);
+ ir_raw_event_store_with_filter
+ (dev->rdev, &ev);
+ } else
+ next_one = size;
+ }
+
+ ir_raw_event_handle(dev->rdev);
+
+ ite_dbg_verbose("decoded %d bytes.", length);
+}
+
+/* set all the rx/tx carrier parameters; this must be called with the device
+ * spinlock held */
+static void ite_set_carrier_params(struct ite_dev *dev)
+{
+ unsigned int freq, low_freq, high_freq;
+ int allowance;
+ bool use_demodulator;
+ bool for_tx = dev->transmitting;
+
+ ite_dbg("%s called", __func__);
+
+ if (for_tx) {
+ /* we don't need no stinking calculations */
+ freq = dev->params.tx_carrier_freq;
+ allowance = ITE_RXDCR_DEFAULT;
+ use_demodulator = false;
+ } else {
+ low_freq = dev->params.rx_low_carrier_freq;
+ high_freq = dev->params.rx_high_carrier_freq;
+
+ if (low_freq == 0) {
+ /* don't demodulate */
+ freq =
+ ITE_DEFAULT_CARRIER_FREQ;
+ allowance = ITE_RXDCR_DEFAULT;
+ use_demodulator = false;
+ } else {
+ /* calculate the middle freq */
+ freq = (low_freq + high_freq) / 2;
+
+ /* calculate the allowance */
+ allowance =
+ DIV_ROUND_CLOSEST(10000 * (high_freq - low_freq),
+ ITE_RXDCR_PER_10000_STEP
+ * (high_freq + low_freq));
+
+ if (allowance < 1)
+ allowance = 1;
+
+ if (allowance > ITE_RXDCR_MAX)
+ allowance = ITE_RXDCR_MAX;
+ }
+ }
+
+ /* set the carrier parameters in a device-dependent way */
+ dev->params.set_carrier_params(dev, ite_is_high_carrier_freq(freq),
+ use_demodulator, ite_get_carrier_freq_bits(freq), allowance,
+ ite_get_pulse_width_bits(freq, dev->params.tx_duty_cycle));
+}
+
+/* interrupt service routine for incoming and outgoing CIR data */
+static irqreturn_t ite_cir_isr(int irq, void *data)
+{
+ struct ite_dev *dev = data;
+ unsigned long flags;
+ irqreturn_t ret = IRQ_RETVAL(IRQ_NONE);
+ u8 rx_buf[ITE_RX_FIFO_LEN];
+ int rx_bytes;
+ int iflags;
+
+ ite_dbg_verbose("%s firing", __func__);
+
+ /* grab the spinlock */
+ spin_lock_irqsave(&dev->lock, flags);
+
+ /* read the interrupt flags */
+ iflags = dev->params.get_irq_causes(dev);
+
+ /* check for the receive interrupt */
+ if (iflags & (ITE_IRQ_RX_FIFO | ITE_IRQ_RX_FIFO_OVERRUN)) {
+ /* read the FIFO bytes */
+ rx_bytes =
+ dev->params.get_rx_bytes(dev, rx_buf,
+ ITE_RX_FIFO_LEN);
+
+ if (rx_bytes > 0) {
+ /* drop the spinlock, since the ir-core layer
+ * may call us back again through
+ * ite_s_idle() */
+ spin_unlock_irqrestore(&dev->
+ lock,
+ flags);
+
+ /* decode the data we've just received */
+ ite_decode_bytes(dev, rx_buf,
+ rx_bytes);
+
+ /* reacquire the spinlock */
+ spin_lock_irqsave(&dev->lock,
+ flags);
+
+ /* mark the interrupt as serviced */
+ ret = IRQ_RETVAL(IRQ_HANDLED);
+ }
+ } else if (iflags & ITE_IRQ_TX_FIFO) {
+ /* FIFO space available interrupt */
+ ite_dbg_verbose("got interrupt for TX FIFO");
+
+ /* wake any sleeping transmitter */
+ wake_up_interruptible(&dev->tx_queue);
+
+ /* mark the interrupt as serviced */
+ ret = IRQ_RETVAL(IRQ_HANDLED);
+ }
+
+ /* drop the spinlock */
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ ite_dbg_verbose("%s done returning %d", __func__, (int)ret);
+
+ return ret;
+}
+
+/* set the rx carrier freq range, guess it's in Hz... */
+static int ite_set_rx_carrier_range(struct rc_dev *rcdev, u32 carrier_low, u32
+ carrier_high)
+{
+ unsigned long flags;
+ struct ite_dev *dev = rcdev->priv;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->params.rx_low_carrier_freq = carrier_low;
+ dev->params.rx_high_carrier_freq = carrier_high;
+ ite_set_carrier_params(dev);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return 0;
+}
+
+/* set the tx carrier freq, guess it's in Hz... */
+static int ite_set_tx_carrier(struct rc_dev *rcdev, u32 carrier)
+{
+ unsigned long flags;
+ struct ite_dev *dev = rcdev->priv;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->params.tx_carrier_freq = carrier;
+ ite_set_carrier_params(dev);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return 0;
+}
+
+/* set the tx duty cycle by controlling the pulse width */
+static int ite_set_tx_duty_cycle(struct rc_dev *rcdev, u32 duty_cycle)
+{
+ unsigned long flags;
+ struct ite_dev *dev = rcdev->priv;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->params.tx_duty_cycle = duty_cycle;
+ ite_set_carrier_params(dev);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return 0;
+}
+
+/* transmit out IR pulses; what you get here is a batch of alternating
+ * pulse/space/pulse/space lengths that we should write out completely through
+ * the FIFO, blocking on a full FIFO */
+static int ite_tx_ir(struct rc_dev *rcdev, unsigned *txbuf, unsigned n)
+{
+ unsigned long flags;
+ struct ite_dev *dev = rcdev->priv;
+ bool is_pulse = false;
+ int remaining_us, fifo_avail, fifo_remaining, last_idx = 0;
+ int max_rle_us, next_rle_us;
+ int ret = n;
+ u8 last_sent[ITE_TX_FIFO_LEN];
+ u8 val;
+
+ ite_dbg("%s called", __func__);
+
+ /* clear the array just in case */
+ memset(last_sent, 0, ARRAY_SIZE(last_sent));
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ /* let everybody know we're now transmitting */
+ dev->transmitting = true;
+
+ /* and set the carrier values for transmission */
+ ite_set_carrier_params(dev);
+
+ /* calculate how much time we can send in one byte */
+ max_rle_us =
+ (ITE_BAUDRATE_DIVISOR * dev->params.sample_period *
+ ITE_TX_MAX_RLE) / 1000;
+
+ /* disable the receiver */
+ dev->params.disable_rx(dev);
+
+ /* this is where we'll begin filling in the FIFO, until it's full.
+ * then we'll just activate the interrupt, wait for it to wake us up
+ * again, disable it, continue filling the FIFO... until everything
+ * has been pushed out */
+ fifo_avail =
+ ITE_TX_FIFO_LEN - dev->params.get_tx_used_slots(dev);
+
+ while (n > 0 && dev->in_use) {
+ /* transmit the next sample */
+ is_pulse = !is_pulse;
+ remaining_us = *(txbuf++);
+ n--;
+
+ ite_dbg("%s: %ld",
+ ((is_pulse) ? "pulse" : "space"),
+ (long int)
+ remaining_us);
+
+ /* repeat while the pulse is non-zero length */
+ while (remaining_us > 0 && dev->in_use) {
+ if (remaining_us > max_rle_us)
+ next_rle_us = max_rle_us;
+
+ else
+ next_rle_us = remaining_us;
+
+ remaining_us -= next_rle_us;
+
+ /* check what's the length we have to pump out */
+ val = (ITE_TX_MAX_RLE * next_rle_us) / max_rle_us;
+
+ /* put it into the sent buffer */
+ last_sent[last_idx++] = val;
+ last_idx &= (ITE_TX_FIFO_LEN);
+
+ /* encode it for 7 bits */
+ val = (val - 1) & ITE_TX_RLE_MASK;
+
+ /* take into account pulse/space prefix */
+ if (is_pulse)
+ val |= ITE_TX_PULSE;
+
+ else
+ val |= ITE_TX_SPACE;
+
+ /*
+ * if we get to 0 available, read again, just in case
+ * some other slot got freed
+ */
+ if (fifo_avail <= 0)
+ fifo_avail = ITE_TX_FIFO_LEN - dev->params.get_tx_used_slots(dev);
+
+ /* if it's still full */
+ if (fifo_avail <= 0) {
+ /* enable the tx interrupt */
+ dev->params.
+ enable_tx_interrupt(dev);
+
+ /* drop the spinlock */
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ /* wait for the FIFO to empty enough */
+ wait_event_interruptible(dev->tx_queue, (fifo_avail = ITE_TX_FIFO_LEN - dev->params.get_tx_used_slots(dev)) >= 8);
+
+ /* get the spinlock again */
+ spin_lock_irqsave(&dev->lock, flags);
+
+ /* disable the tx interrupt again. */
+ dev->params.
+ disable_tx_interrupt(dev);
+ }
+
+ /* now send the byte through the FIFO */
+ dev->params.put_tx_byte(dev, val);
+ fifo_avail--;
+ }
+ }
+
+ /* wait and don't return until the whole FIFO has been sent out;
+ * otherwise we could configure the RX carrier params instead of the
+ * TX ones while the transmission is still being performed! */
+ fifo_remaining = dev->params.get_tx_used_slots(dev);
+ remaining_us = 0;
+ while (fifo_remaining > 0) {
+ fifo_remaining--;
+ last_idx--;
+ last_idx &= (ITE_TX_FIFO_LEN - 1);
+ remaining_us += last_sent[last_idx];
+ }
+ remaining_us = (remaining_us * max_rle_us) / (ITE_TX_MAX_RLE);
+
+ /* drop the spinlock while we sleep */
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ /* sleep remaining_us microseconds */
+ mdelay(DIV_ROUND_UP(remaining_us, 1000));
+
+ /* reacquire the spinlock */
+ spin_lock_irqsave(&dev->lock, flags);
+
+ /* now we're not transmitting anymore */
+ dev->transmitting = false;
+
+ /* and set the carrier values for reception */
+ ite_set_carrier_params(dev);
+
+ /* reenable the receiver */
+ if (dev->in_use)
+ dev->params.enable_rx(dev);
+
+ /* notify transmission end */
+ wake_up_interruptible(&dev->tx_ended);
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return ret;
+}
+
+/* idle the receiver if needed */
+static void ite_s_idle(struct rc_dev *rcdev, bool enable)
+{
+ unsigned long flags;
+ struct ite_dev *dev = rcdev->priv;
+
+ ite_dbg("%s called", __func__);
+
+ if (enable) {
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->params.idle_rx(dev);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ }
+}
+
+
+/* IT8712F HW-specific functions */
+
+/* retrieve a bitmask of the current causes for a pending interrupt; this may
+ * be composed of ITE_IRQ_TX_FIFO, ITE_IRQ_RX_FIFO and ITE_IRQ_RX_FIFO_OVERRUN
+ * */
+static int it87_get_irq_causes(struct ite_dev *dev)
+{
+ u8 iflags;
+ int ret = 0;
+
+ ite_dbg("%s called", __func__);
+
+ /* read the interrupt flags */
+ iflags = inb(dev->cir_addr + IT87_IIR) & IT87_II;
+
+ switch (iflags) {
+ case IT87_II_RXDS:
+ ret = ITE_IRQ_RX_FIFO;
+ break;
+ case IT87_II_RXFO:
+ ret = ITE_IRQ_RX_FIFO_OVERRUN;
+ break;
+ case IT87_II_TXLDL:
+ ret = ITE_IRQ_TX_FIFO;
+ break;
+ }
+
+ return ret;
+}
+
+/* set the carrier parameters; to be called with the spinlock held */
+static void it87_set_carrier_params(struct ite_dev *dev, bool high_freq,
+ bool use_demodulator,
+ u8 carrier_freq_bits, u8 allowance_bits,
+ u8 pulse_width_bits)
+{
+ u8 val;
+
+ ite_dbg("%s called", __func__);
+
+ /* program the RCR register */
+ val = inb(dev->cir_addr + IT87_RCR)
+ & ~(IT87_HCFS | IT87_RXEND | IT87_RXDCR);
+
+ if (high_freq)
+ val |= IT87_HCFS;
+
+ if (use_demodulator)
+ val |= IT87_RXEND;
+
+ val |= allowance_bits;
+
+ outb(val, dev->cir_addr + IT87_RCR);
+
+ /* program the TCR2 register */
+ outb((carrier_freq_bits << IT87_CFQ_SHIFT) | pulse_width_bits,
+ dev->cir_addr + IT87_TCR2);
+}
+
+/* read up to buf_size bytes from the RX FIFO; to be called with the spinlock
+ * held */
+static int it87_get_rx_bytes(struct ite_dev *dev, u8 * buf, int buf_size)
+{
+ int fifo, read = 0;
+
+ ite_dbg("%s called", __func__);
+
+ /* read how many bytes are still in the FIFO */
+ fifo = inb(dev->cir_addr + IT87_RSR) & IT87_RXFBC;
+
+ while (fifo > 0 && buf_size > 0) {
+ *(buf++) = inb(dev->cir_addr + IT87_DR);
+ fifo--;
+ read++;
+ buf_size--;
+ }
+
+ return read;
+}
+
+/* return how many bytes are still in the FIFO; this will be called
+ * with the device spinlock NOT HELD while waiting for the TX FIFO to get
+ * empty; let's expect this won't be a problem */
+static int it87_get_tx_used_slots(struct ite_dev *dev)
+{
+ ite_dbg("%s called", __func__);
+
+ return inb(dev->cir_addr + IT87_TSR) & IT87_TXFBC;
+}
+
+/* put a byte to the TX fifo; this should be called with the spinlock held */
+static void it87_put_tx_byte(struct ite_dev *dev, u8 value)
+{
+ outb(value, dev->cir_addr + IT87_DR);
+}
+
+/* idle the receiver so that we won't receive samples until another
+ pulse is detected; this must be called with the device spinlock held */
+static void it87_idle_rx(struct ite_dev *dev)
+{
+ ite_dbg("%s called", __func__);
+
+ /* disable streaming by clearing RXACT writing it as 1 */
+ outb(inb(dev->cir_addr + IT87_RCR) | IT87_RXACT,
+ dev->cir_addr + IT87_RCR);
+
+ /* clear the FIFO */
+ outb(inb(dev->cir_addr + IT87_TCR1) | IT87_FIFOCLR,
+ dev->cir_addr + IT87_TCR1);
+}
+
+/* disable the receiver; this must be called with the device spinlock held */
+static void it87_disable_rx(struct ite_dev *dev)
+{
+ ite_dbg("%s called", __func__);
+
+ /* disable the receiver interrupts */
+ outb(inb(dev->cir_addr + IT87_IER) & ~(IT87_RDAIE | IT87_RFOIE),
+ dev->cir_addr + IT87_IER);
+
+ /* disable the receiver */
+ outb(inb(dev->cir_addr + IT87_RCR) & ~IT87_RXEN,
+ dev->cir_addr + IT87_RCR);
+
+ /* clear the FIFO and RXACT (actually RXACT should have been cleared
+ * in the previous outb() call) */
+ it87_idle_rx(dev);
+}
+
+/* enable the receiver; this must be called with the device spinlock held */
+static void it87_enable_rx(struct ite_dev *dev)
+{
+ ite_dbg("%s called", __func__);
+
+ /* enable the receiver by setting RXEN */
+ outb(inb(dev->cir_addr + IT87_RCR) | IT87_RXEN,
+ dev->cir_addr + IT87_RCR);
+
+ /* just prepare it to idle for the next reception */
+ it87_idle_rx(dev);
+
+ /* enable the receiver interrupts and master enable flag */
+ outb(inb(dev->cir_addr + IT87_IER) | IT87_RDAIE | IT87_RFOIE | IT87_IEC,
+ dev->cir_addr + IT87_IER);
+}
+
+/* disable the transmitter interrupt; this must be called with the device
+ * spinlock held */
+static void it87_disable_tx_interrupt(struct ite_dev *dev)
+{
+ ite_dbg("%s called", __func__);
+
+ /* disable the transmitter interrupts */
+ outb(inb(dev->cir_addr + IT87_IER) & ~IT87_TLDLIE,
+ dev->cir_addr + IT87_IER);
+}
+
+/* enable the transmitter interrupt; this must be called with the device
+ * spinlock held */
+static void it87_enable_tx_interrupt(struct ite_dev *dev)
+{
+ ite_dbg("%s called", __func__);
+
+ /* enable the transmitter interrupts and master enable flag */
+ outb(inb(dev->cir_addr + IT87_IER) | IT87_TLDLIE | IT87_IEC,
+ dev->cir_addr + IT87_IER);
+}
+
+/* disable the device; this must be called with the device spinlock held */
+static void it87_disable(struct ite_dev *dev)
+{
+ ite_dbg("%s called", __func__);
+
+ /* clear out all interrupt enable flags */
+ outb(inb(dev->cir_addr + IT87_IER) &
+ ~(IT87_IEC | IT87_RFOIE | IT87_RDAIE | IT87_TLDLIE),
+ dev->cir_addr + IT87_IER);
+
+ /* disable the receiver */
+ it87_disable_rx(dev);
+
+ /* erase the FIFO */
+ outb(IT87_FIFOCLR | inb(dev->cir_addr + IT87_TCR1),
+ dev->cir_addr + IT87_TCR1);
+}
+
+/* initialize the hardware */
+static void it87_init_hardware(struct ite_dev *dev)
+{
+ ite_dbg("%s called", __func__);
+
+ /* enable just the baud rate divisor register,
+ disabling all the interrupts at the same time */
+ outb((inb(dev->cir_addr + IT87_IER) &
+ ~(IT87_IEC | IT87_RFOIE | IT87_RDAIE | IT87_TLDLIE)) | IT87_BR,
+ dev->cir_addr + IT87_IER);
+
+ /* write out the baud rate divisor */
+ outb(ITE_BAUDRATE_DIVISOR & 0xff, dev->cir_addr + IT87_BDLR);
+ outb((ITE_BAUDRATE_DIVISOR >> 8) & 0xff, dev->cir_addr + IT87_BDHR);
+
+ /* disable the baud rate divisor register again */
+ outb(inb(dev->cir_addr + IT87_IER) & ~IT87_BR,
+ dev->cir_addr + IT87_IER);
+
+ /* program the RCR register defaults */
+ outb(ITE_RXDCR_DEFAULT, dev->cir_addr + IT87_RCR);
+
+ /* program the TCR1 register */
+ outb(IT87_TXMPM_DEFAULT | IT87_TXENDF | IT87_TXRLE
+ | IT87_FIFOTL_DEFAULT | IT87_FIFOCLR,
+ dev->cir_addr + IT87_TCR1);
+
+ /* program the carrier parameters */
+ ite_set_carrier_params(dev);
+}
+
+/* IT8512F on ITE8708 HW-specific functions */
+
+/* retrieve a bitmask of the current causes for a pending interrupt; this may
+ * be composed of ITE_IRQ_TX_FIFO, ITE_IRQ_RX_FIFO and ITE_IRQ_RX_FIFO_OVERRUN
+ * */
+static int it8708_get_irq_causes(struct ite_dev *dev)
+{
+ u8 iflags;
+ int ret = 0;
+
+ ite_dbg("%s called", __func__);
+
+ /* read the interrupt flags */
+ iflags = inb(dev->cir_addr + IT8708_C0IIR);
+
+ if (iflags & IT85_TLDLI)
+ ret |= ITE_IRQ_TX_FIFO;
+ if (iflags & IT85_RDAI)
+ ret |= ITE_IRQ_RX_FIFO;
+ if (iflags & IT85_RFOI)
+ ret |= ITE_IRQ_RX_FIFO_OVERRUN;
+
+ return ret;
+}
+
+/* set the carrier parameters; to be called with the spinlock held */
+static void it8708_set_carrier_params(struct ite_dev *dev, bool high_freq,
+ bool use_demodulator,
+ u8 carrier_freq_bits, u8 allowance_bits,
+ u8 pulse_width_bits)
+{
+ u8 val;
+
+ ite_dbg("%s called", __func__);
+
+ /* program the C0CFR register, with HRAE=1 */
+ outb(inb(dev->cir_addr + IT8708_BANKSEL) | IT8708_HRAE,
+ dev->cir_addr + IT8708_BANKSEL);
+
+ val = (inb(dev->cir_addr + IT8708_C0CFR)
+ & ~(IT85_HCFS | IT85_CFQ)) | carrier_freq_bits;
+
+ if (high_freq)
+ val |= IT85_HCFS;
+
+ outb(val, dev->cir_addr + IT8708_C0CFR);
+
+ outb(inb(dev->cir_addr + IT8708_BANKSEL) & ~IT8708_HRAE,
+ dev->cir_addr + IT8708_BANKSEL);
+
+ /* program the C0RCR register */
+ val = inb(dev->cir_addr + IT8708_C0RCR)
+ & ~(IT85_RXEND | IT85_RXDCR);
+
+ if (use_demodulator)
+ val |= IT85_RXEND;
+
+ val |= allowance_bits;
+
+ outb(val, dev->cir_addr + IT8708_C0RCR);
+
+ /* program the C0TCR register */
+ val = inb(dev->cir_addr + IT8708_C0TCR) & ~IT85_TXMPW;
+ val |= pulse_width_bits;
+ outb(val, dev->cir_addr + IT8708_C0TCR);
+}
+
+/* read up to buf_size bytes from the RX FIFO; to be called with the spinlock
+ * held */
+static int it8708_get_rx_bytes(struct ite_dev *dev, u8 * buf, int buf_size)
+{
+ int fifo, read = 0;
+
+ ite_dbg("%s called", __func__);
+
+ /* read how many bytes are still in the FIFO */
+ fifo = inb(dev->cir_addr + IT8708_C0RFSR) & IT85_RXFBC;
+
+ while (fifo > 0 && buf_size > 0) {
+ *(buf++) = inb(dev->cir_addr + IT8708_C0DR);
+ fifo--;
+ read++;
+ buf_size--;
+ }
+
+ return read;
+}
+
+/* return how many bytes are still in the FIFO; this will be called
+ * with the device spinlock NOT HELD while waiting for the TX FIFO to get
+ * empty; let's expect this won't be a problem */
+static int it8708_get_tx_used_slots(struct ite_dev *dev)
+{
+ ite_dbg("%s called", __func__);
+
+ return inb(dev->cir_addr + IT8708_C0TFSR) & IT85_TXFBC;
+}
+
+/* put a byte to the TX fifo; this should be called with the spinlock held */
+static void it8708_put_tx_byte(struct ite_dev *dev, u8 value)
+{
+ outb(value, dev->cir_addr + IT8708_C0DR);
+}
+
+/* idle the receiver so that we won't receive samples until another
+ pulse is detected; this must be called with the device spinlock held */
+static void it8708_idle_rx(struct ite_dev *dev)
+{
+ ite_dbg("%s called", __func__);
+
+ /* disable streaming by clearing RXACT writing it as 1 */
+ outb(inb(dev->cir_addr + IT8708_C0RCR) | IT85_RXACT,
+ dev->cir_addr + IT8708_C0RCR);
+
+ /* clear the FIFO */
+ outb(inb(dev->cir_addr + IT8708_C0MSTCR) | IT85_FIFOCLR,
+ dev->cir_addr + IT8708_C0MSTCR);
+}
+
+/* disable the receiver; this must be called with the device spinlock held */
+static void it8708_disable_rx(struct ite_dev *dev)
+{
+ ite_dbg("%s called", __func__);
+
+ /* disable the receiver interrupts */
+ outb(inb(dev->cir_addr + IT8708_C0IER) &
+ ~(IT85_RDAIE | IT85_RFOIE),
+ dev->cir_addr + IT8708_C0IER);
+
+ /* disable the receiver */
+ outb(inb(dev->cir_addr + IT8708_C0RCR) & ~IT85_RXEN,
+ dev->cir_addr + IT8708_C0RCR);
+
+ /* clear the FIFO and RXACT (actually RXACT should have been cleared
+ * in the previous outb() call) */
+ it8708_idle_rx(dev);
+}
+
+/* enable the receiver; this must be called with the device spinlock held */
+static void it8708_enable_rx(struct ite_dev *dev)
+{
+ ite_dbg("%s called", __func__);
+
+ /* enable the receiver by setting RXEN */
+ outb(inb(dev->cir_addr + IT8708_C0RCR) | IT85_RXEN,
+ dev->cir_addr + IT8708_C0RCR);
+
+ /* just prepare it to idle for the next reception */
+ it8708_idle_rx(dev);
+
+ /* enable the receiver interrupts and master enable flag */
+ outb(inb(dev->cir_addr + IT8708_C0IER)
+ |IT85_RDAIE | IT85_RFOIE | IT85_IEC,
+ dev->cir_addr + IT8708_C0IER);
+}
+
+/* disable the transmitter interrupt; this must be called with the device
+ * spinlock held */
+static void it8708_disable_tx_interrupt(struct ite_dev *dev)
+{
+ ite_dbg("%s called", __func__);
+
+ /* disable the transmitter interrupts */
+ outb(inb(dev->cir_addr + IT8708_C0IER) & ~IT85_TLDLIE,
+ dev->cir_addr + IT8708_C0IER);
+}
+
+/* enable the transmitter interrupt; this must be called with the device
+ * spinlock held */
+static void it8708_enable_tx_interrupt(struct ite_dev *dev)
+{
+ ite_dbg("%s called", __func__);
+
+ /* enable the transmitter interrupts and master enable flag */
+ outb(inb(dev->cir_addr + IT8708_C0IER)
+ |IT85_TLDLIE | IT85_IEC,
+ dev->cir_addr + IT8708_C0IER);
+}
+
+/* disable the device; this must be called with the device spinlock held */
+static void it8708_disable(struct ite_dev *dev)
+{
+ ite_dbg("%s called", __func__);
+
+ /* clear out all interrupt enable flags */
+ outb(inb(dev->cir_addr + IT8708_C0IER) &
+ ~(IT85_IEC | IT85_RFOIE | IT85_RDAIE | IT85_TLDLIE),
+ dev->cir_addr + IT8708_C0IER);
+
+ /* disable the receiver */
+ it8708_disable_rx(dev);
+
+ /* erase the FIFO */
+ outb(IT85_FIFOCLR | inb(dev->cir_addr + IT8708_C0MSTCR),
+ dev->cir_addr + IT8708_C0MSTCR);
+}
+
+/* initialize the hardware */
+static void it8708_init_hardware(struct ite_dev *dev)
+{
+ ite_dbg("%s called", __func__);
+
+ /* disable all the interrupts */
+ outb(inb(dev->cir_addr + IT8708_C0IER) &
+ ~(IT85_IEC | IT85_RFOIE | IT85_RDAIE | IT85_TLDLIE),
+ dev->cir_addr + IT8708_C0IER);
+
+ /* program the baud rate divisor */
+ outb(inb(dev->cir_addr + IT8708_BANKSEL) | IT8708_HRAE,
+ dev->cir_addr + IT8708_BANKSEL);
+
+ outb(ITE_BAUDRATE_DIVISOR & 0xff, dev->cir_addr + IT8708_C0BDLR);
+ outb((ITE_BAUDRATE_DIVISOR >> 8) & 0xff,
+ dev->cir_addr + IT8708_C0BDHR);
+
+ outb(inb(dev->cir_addr + IT8708_BANKSEL) & ~IT8708_HRAE,
+ dev->cir_addr + IT8708_BANKSEL);
+
+ /* program the C0MSTCR register defaults */
+ outb((inb(dev->cir_addr + IT8708_C0MSTCR) &
+ ~(IT85_ILSEL | IT85_ILE | IT85_FIFOTL |
+ IT85_FIFOCLR | IT85_RESET)) |
+ IT85_FIFOTL_DEFAULT,
+ dev->cir_addr + IT8708_C0MSTCR);
+
+ /* program the C0RCR register defaults */
+ outb((inb(dev->cir_addr + IT8708_C0RCR) &
+ ~(IT85_RXEN | IT85_RDWOS | IT85_RXEND |
+ IT85_RXACT | IT85_RXDCR)) |
+ ITE_RXDCR_DEFAULT,
+ dev->cir_addr + IT8708_C0RCR);
+
+ /* program the C0TCR register defaults */
+ outb((inb(dev->cir_addr + IT8708_C0TCR) &
+ ~(IT85_TXMPM | IT85_TXMPW))
+ |IT85_TXRLE | IT85_TXENDF |
+ IT85_TXMPM_DEFAULT | IT85_TXMPW_DEFAULT,
+ dev->cir_addr + IT8708_C0TCR);
+
+ /* program the carrier parameters */
+ ite_set_carrier_params(dev);
+}
+
+/* IT8512F on ITE8709 HW-specific functions */
+
+/* read a byte from the SRAM module */
+static inline u8 it8709_rm(struct ite_dev *dev, int index)
+{
+ outb(index, dev->cir_addr + IT8709_RAM_IDX);
+ return inb(dev->cir_addr + IT8709_RAM_VAL);
+}
+
+/* write a byte to the SRAM module */
+static inline void it8709_wm(struct ite_dev *dev, u8 val, int index)
+{
+ outb(index, dev->cir_addr + IT8709_RAM_IDX);
+ outb(val, dev->cir_addr + IT8709_RAM_VAL);
+}
+
+static void it8709_wait(struct ite_dev *dev)
+{
+ int i = 0;
+ /*
+ * loop until device tells it's ready to continue
+ * iterations count is usually ~750 but can sometimes achieve 13000
+ */
+ for (i = 0; i < 15000; i++) {
+ udelay(2);
+ if (it8709_rm(dev, IT8709_MODE) == IT8709_IDLE)
+ break;
+ }
+}
+
+/* read the value of a CIR register */
+static u8 it8709_rr(struct ite_dev *dev, int index)
+{
+ /* just wait in case the previous access was a write */
+ it8709_wait(dev);
+ it8709_wm(dev, index, IT8709_REG_IDX);
+ it8709_wm(dev, IT8709_READ, IT8709_MODE);
+
+ /* wait for the read data to be available */
+ it8709_wait(dev);
+
+ /* return the read value */
+ return it8709_rm(dev, IT8709_REG_VAL);
+}
+
+/* write the value of a CIR register */
+static void it8709_wr(struct ite_dev *dev, u8 val, int index)
+{
+ /* we wait before writing, and not afterwards, since this allows us to
+ * pipeline the host CPU with the microcontroller */
+ it8709_wait(dev);
+ it8709_wm(dev, val, IT8709_REG_VAL);
+ it8709_wm(dev, index, IT8709_REG_IDX);
+ it8709_wm(dev, IT8709_WRITE, IT8709_MODE);
+}
+
+/* retrieve a bitmask of the current causes for a pending interrupt; this may
+ * be composed of ITE_IRQ_TX_FIFO, ITE_IRQ_RX_FIFO and ITE_IRQ_RX_FIFO_OVERRUN
+ * */
+static int it8709_get_irq_causes(struct ite_dev *dev)
+{
+ u8 iflags;
+ int ret = 0;
+
+ ite_dbg("%s called", __func__);
+
+ /* read the interrupt flags */
+ iflags = it8709_rm(dev, IT8709_IIR);
+
+ if (iflags & IT85_TLDLI)
+ ret |= ITE_IRQ_TX_FIFO;
+ if (iflags & IT85_RDAI)
+ ret |= ITE_IRQ_RX_FIFO;
+ if (iflags & IT85_RFOI)
+ ret |= ITE_IRQ_RX_FIFO_OVERRUN;
+
+ return ret;
+}
+
+/* set the carrier parameters; to be called with the spinlock held */
+static void it8709_set_carrier_params(struct ite_dev *dev, bool high_freq,
+ bool use_demodulator,
+ u8 carrier_freq_bits, u8 allowance_bits,
+ u8 pulse_width_bits)
+{
+ u8 val;
+
+ ite_dbg("%s called", __func__);
+
+ val = (it8709_rr(dev, IT85_C0CFR)
+ &~(IT85_HCFS | IT85_CFQ)) |
+ carrier_freq_bits;
+
+ if (high_freq)
+ val |= IT85_HCFS;
+
+ it8709_wr(dev, val, IT85_C0CFR);
+
+ /* program the C0RCR register */
+ val = it8709_rr(dev, IT85_C0RCR)
+ & ~(IT85_RXEND | IT85_RXDCR);
+
+ if (use_demodulator)
+ val |= IT85_RXEND;
+
+ val |= allowance_bits;
+
+ it8709_wr(dev, val, IT85_C0RCR);
+
+ /* program the C0TCR register */
+ val = it8709_rr(dev, IT85_C0TCR) & ~IT85_TXMPW;
+ val |= pulse_width_bits;
+ it8709_wr(dev, val, IT85_C0TCR);
+}
+
+/* read up to buf_size bytes from the RX FIFO; to be called with the spinlock
+ * held */
+static int it8709_get_rx_bytes(struct ite_dev *dev, u8 * buf, int buf_size)
+{
+ int fifo, read = 0;
+
+ ite_dbg("%s called", __func__);
+
+ /* read how many bytes are still in the FIFO */
+ fifo = it8709_rm(dev, IT8709_RFSR) & IT85_RXFBC;
+
+ while (fifo > 0 && buf_size > 0) {
+ *(buf++) = it8709_rm(dev, IT8709_FIFO + read);
+ fifo--;
+ read++;
+ buf_size--;
+ }
+
+ /* 'clear' the FIFO by setting the writing index to 0; this is
+ * completely bound to be racy, but we can't help it, since it's a
+ * limitation of the protocol */
+ it8709_wm(dev, 0, IT8709_RFSR);
+
+ return read;
+}
+
+/* return how many bytes are still in the FIFO; this will be called
+ * with the device spinlock NOT HELD while waiting for the TX FIFO to get
+ * empty; let's expect this won't be a problem */
+static int it8709_get_tx_used_slots(struct ite_dev *dev)
+{
+ ite_dbg("%s called", __func__);
+
+ return it8709_rr(dev, IT85_C0TFSR) & IT85_TXFBC;
+}
+
+/* put a byte to the TX fifo; this should be called with the spinlock held */
+static void it8709_put_tx_byte(struct ite_dev *dev, u8 value)
+{
+ it8709_wr(dev, value, IT85_C0DR);
+}
+
+/* idle the receiver so that we won't receive samples until another
+ pulse is detected; this must be called with the device spinlock held */
+static void it8709_idle_rx(struct ite_dev *dev)
+{
+ ite_dbg("%s called", __func__);
+
+ /* disable streaming by clearing RXACT writing it as 1 */
+ it8709_wr(dev, it8709_rr(dev, IT85_C0RCR) | IT85_RXACT,
+ IT85_C0RCR);
+
+ /* clear the FIFO */
+ it8709_wr(dev, it8709_rr(dev, IT85_C0MSTCR) | IT85_FIFOCLR,
+ IT85_C0MSTCR);
+}
+
+/* disable the receiver; this must be called with the device spinlock held */
+static void it8709_disable_rx(struct ite_dev *dev)
+{
+ ite_dbg("%s called", __func__);
+
+ /* disable the receiver interrupts */
+ it8709_wr(dev, it8709_rr(dev, IT85_C0IER) &
+ ~(IT85_RDAIE | IT85_RFOIE),
+ IT85_C0IER);
+
+ /* disable the receiver */
+ it8709_wr(dev, it8709_rr(dev, IT85_C0RCR) & ~IT85_RXEN,
+ IT85_C0RCR);
+
+ /* clear the FIFO and RXACT (actually RXACT should have been cleared
+ * in the previous it8709_wr(dev, ) call) */
+ it8709_idle_rx(dev);
+}
+
+/* enable the receiver; this must be called with the device spinlock held */
+static void it8709_enable_rx(struct ite_dev *dev)
+{
+ ite_dbg("%s called", __func__);
+
+ /* enable the receiver by setting RXEN */
+ it8709_wr(dev, it8709_rr(dev, IT85_C0RCR) | IT85_RXEN,
+ IT85_C0RCR);
+
+ /* just prepare it to idle for the next reception */
+ it8709_idle_rx(dev);
+
+ /* enable the receiver interrupts and master enable flag */
+ it8709_wr(dev, it8709_rr(dev, IT85_C0IER)
+ |IT85_RDAIE | IT85_RFOIE | IT85_IEC,
+ IT85_C0IER);
+}
+
+/* disable the transmitter interrupt; this must be called with the device
+ * spinlock held */
+static void it8709_disable_tx_interrupt(struct ite_dev *dev)
+{
+ ite_dbg("%s called", __func__);
+
+ /* disable the transmitter interrupts */
+ it8709_wr(dev, it8709_rr(dev, IT85_C0IER) & ~IT85_TLDLIE,
+ IT85_C0IER);
+}
+
+/* enable the transmitter interrupt; this must be called with the device
+ * spinlock held */
+static void it8709_enable_tx_interrupt(struct ite_dev *dev)
+{
+ ite_dbg("%s called", __func__);
+
+ /* enable the transmitter interrupts and master enable flag */
+ it8709_wr(dev, it8709_rr(dev, IT85_C0IER)
+ |IT85_TLDLIE | IT85_IEC,
+ IT85_C0IER);
+}
+
+/* disable the device; this must be called with the device spinlock held */
+static void it8709_disable(struct ite_dev *dev)
+{
+ ite_dbg("%s called", __func__);
+
+ /* clear out all interrupt enable flags */
+ it8709_wr(dev, it8709_rr(dev, IT85_C0IER) &
+ ~(IT85_IEC | IT85_RFOIE | IT85_RDAIE | IT85_TLDLIE),
+ IT85_C0IER);
+
+ /* disable the receiver */
+ it8709_disable_rx(dev);
+
+ /* erase the FIFO */
+ it8709_wr(dev, IT85_FIFOCLR | it8709_rr(dev, IT85_C0MSTCR),
+ IT85_C0MSTCR);
+}
+
+/* initialize the hardware */
+static void it8709_init_hardware(struct ite_dev *dev)
+{
+ ite_dbg("%s called", __func__);
+
+ /* disable all the interrupts */
+ it8709_wr(dev, it8709_rr(dev, IT85_C0IER) &
+ ~(IT85_IEC | IT85_RFOIE | IT85_RDAIE | IT85_TLDLIE),
+ IT85_C0IER);
+
+ /* program the baud rate divisor */
+ it8709_wr(dev, ITE_BAUDRATE_DIVISOR & 0xff, IT85_C0BDLR);
+ it8709_wr(dev, (ITE_BAUDRATE_DIVISOR >> 8) & 0xff,
+ IT85_C0BDHR);
+
+ /* program the C0MSTCR register defaults */
+ it8709_wr(dev, (it8709_rr(dev, IT85_C0MSTCR) &
+ ~(IT85_ILSEL | IT85_ILE | IT85_FIFOTL
+ | IT85_FIFOCLR | IT85_RESET)) | IT85_FIFOTL_DEFAULT,
+ IT85_C0MSTCR);
+
+ /* program the C0RCR register defaults */
+ it8709_wr(dev, (it8709_rr(dev, IT85_C0RCR) &
+ ~(IT85_RXEN | IT85_RDWOS | IT85_RXEND | IT85_RXACT
+ | IT85_RXDCR)) | ITE_RXDCR_DEFAULT,
+ IT85_C0RCR);
+
+ /* program the C0TCR register defaults */
+ it8709_wr(dev, (it8709_rr(dev, IT85_C0TCR) & ~(IT85_TXMPM | IT85_TXMPW))
+ | IT85_TXRLE | IT85_TXENDF | IT85_TXMPM_DEFAULT
+ | IT85_TXMPW_DEFAULT,
+ IT85_C0TCR);
+
+ /* program the carrier parameters */
+ ite_set_carrier_params(dev);
+}
+
+
+/* generic hardware setup/teardown code */
+
+/* activate the device for use */
+static int ite_open(struct rc_dev *rcdev)
+{
+ struct ite_dev *dev = rcdev->priv;
+ unsigned long flags;
+
+ ite_dbg("%s called", __func__);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->in_use = true;
+
+ /* enable the receiver */
+ dev->params.enable_rx(dev);
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return 0;
+}
+
+/* deactivate the device for use */
+static void ite_close(struct rc_dev *rcdev)
+{
+ struct ite_dev *dev = rcdev->priv;
+ unsigned long flags;
+
+ ite_dbg("%s called", __func__);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->in_use = false;
+
+ /* wait for any transmission to end */
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_event_interruptible(dev->tx_ended, !dev->transmitting);
+ spin_lock_irqsave(&dev->lock, flags);
+
+ dev->params.disable(dev);
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+/* supported models and their parameters */
+static const struct ite_dev_params ite_dev_descs[] = {
+ { /* 0: ITE8704 */
+ .model = "ITE8704 CIR transceiver",
+ .io_region_size = IT87_IOREG_LENGTH,
+ .io_rsrc_no = 0,
+ .hw_tx_capable = true,
+ .sample_period = (u32) (1000000000ULL / 115200),
+ .tx_carrier_freq = 38000,
+ .tx_duty_cycle = 33,
+ .rx_low_carrier_freq = 0,
+ .rx_high_carrier_freq = 0,
+
+ /* operations */
+ .get_irq_causes = it87_get_irq_causes,
+ .enable_rx = it87_enable_rx,
+ .idle_rx = it87_idle_rx,
+ .disable_rx = it87_idle_rx,
+ .get_rx_bytes = it87_get_rx_bytes,
+ .enable_tx_interrupt = it87_enable_tx_interrupt,
+ .disable_tx_interrupt = it87_disable_tx_interrupt,
+ .get_tx_used_slots = it87_get_tx_used_slots,
+ .put_tx_byte = it87_put_tx_byte,
+ .disable = it87_disable,
+ .init_hardware = it87_init_hardware,
+ .set_carrier_params = it87_set_carrier_params,
+ },
+ { /* 1: ITE8713 */
+ .model = "ITE8713 CIR transceiver",
+ .io_region_size = IT87_IOREG_LENGTH,
+ .io_rsrc_no = 0,
+ .hw_tx_capable = true,
+ .sample_period = (u32) (1000000000ULL / 115200),
+ .tx_carrier_freq = 38000,
+ .tx_duty_cycle = 33,
+ .rx_low_carrier_freq = 0,
+ .rx_high_carrier_freq = 0,
+
+ /* operations */
+ .get_irq_causes = it87_get_irq_causes,
+ .enable_rx = it87_enable_rx,
+ .idle_rx = it87_idle_rx,
+ .disable_rx = it87_idle_rx,
+ .get_rx_bytes = it87_get_rx_bytes,
+ .enable_tx_interrupt = it87_enable_tx_interrupt,
+ .disable_tx_interrupt = it87_disable_tx_interrupt,
+ .get_tx_used_slots = it87_get_tx_used_slots,
+ .put_tx_byte = it87_put_tx_byte,
+ .disable = it87_disable,
+ .init_hardware = it87_init_hardware,
+ .set_carrier_params = it87_set_carrier_params,
+ },
+ { /* 2: ITE8708 */
+ .model = "ITE8708 CIR transceiver",
+ .io_region_size = IT8708_IOREG_LENGTH,
+ .io_rsrc_no = 0,
+ .hw_tx_capable = true,
+ .sample_period = (u32) (1000000000ULL / 115200),
+ .tx_carrier_freq = 38000,
+ .tx_duty_cycle = 33,
+ .rx_low_carrier_freq = 0,
+ .rx_high_carrier_freq = 0,
+
+ /* operations */
+ .get_irq_causes = it8708_get_irq_causes,
+ .enable_rx = it8708_enable_rx,
+ .idle_rx = it8708_idle_rx,
+ .disable_rx = it8708_idle_rx,
+ .get_rx_bytes = it8708_get_rx_bytes,
+ .enable_tx_interrupt = it8708_enable_tx_interrupt,
+ .disable_tx_interrupt =
+ it8708_disable_tx_interrupt,
+ .get_tx_used_slots = it8708_get_tx_used_slots,
+ .put_tx_byte = it8708_put_tx_byte,
+ .disable = it8708_disable,
+ .init_hardware = it8708_init_hardware,
+ .set_carrier_params = it8708_set_carrier_params,
+ },
+ { /* 3: ITE8709 */
+ .model = "ITE8709 CIR transceiver",
+ .io_region_size = IT8709_IOREG_LENGTH,
+ .io_rsrc_no = 2,
+ .hw_tx_capable = true,
+ .sample_period = (u32) (1000000000ULL / 115200),
+ .tx_carrier_freq = 38000,
+ .tx_duty_cycle = 33,
+ .rx_low_carrier_freq = 0,
+ .rx_high_carrier_freq = 0,
+
+ /* operations */
+ .get_irq_causes = it8709_get_irq_causes,
+ .enable_rx = it8709_enable_rx,
+ .idle_rx = it8709_idle_rx,
+ .disable_rx = it8709_idle_rx,
+ .get_rx_bytes = it8709_get_rx_bytes,
+ .enable_tx_interrupt = it8709_enable_tx_interrupt,
+ .disable_tx_interrupt =
+ it8709_disable_tx_interrupt,
+ .get_tx_used_slots = it8709_get_tx_used_slots,
+ .put_tx_byte = it8709_put_tx_byte,
+ .disable = it8709_disable,
+ .init_hardware = it8709_init_hardware,
+ .set_carrier_params = it8709_set_carrier_params,
+ },
+};
+
+static const struct pnp_device_id ite_ids[] = {
+ {"ITE8704", 0}, /* Default model */
+ {"ITE8713", 1}, /* CIR found in EEEBox 1501U */
+ {"ITE8708", 2}, /* Bridged IT8512 */
+ {"ITE8709", 3}, /* SRAM-Bridged IT8512 */
+ {"", 0},
+};
+
+/* allocate memory, probe hardware, and initialize everything */
+static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id
+ *dev_id)
+{
+ const struct ite_dev_params *dev_desc = NULL;
+ struct ite_dev *itdev = NULL;
+ struct rc_dev *rdev = NULL;
+ int ret = -ENOMEM;
+ int model_no;
+ int io_rsrc_no;
+
+ ite_dbg("%s called", __func__);
+
+ itdev = kzalloc(sizeof(struct ite_dev), GFP_KERNEL);
+ if (!itdev)
+ return ret;
+
+ /* input device for IR remote (and tx) */
+ rdev = rc_allocate_device();
+ if (!rdev)
+ goto exit_free_dev_rdev;
+ itdev->rdev = rdev;
+
+ ret = -ENODEV;
+
+ /* get the model number */
+ model_no = (int)dev_id->driver_data;
+ ite_pr(KERN_NOTICE, "Auto-detected model: %s\n",
+ ite_dev_descs[model_no].model);
+
+ if (model_number >= 0 && model_number < ARRAY_SIZE(ite_dev_descs)) {
+ model_no = model_number;
+ ite_pr(KERN_NOTICE, "The model has been fixed by a module "
+ "parameter.");
+ }
+
+ ite_pr(KERN_NOTICE, "Using model: %s\n", ite_dev_descs[model_no].model);
+
+ /* get the description for the device */
+ dev_desc = &ite_dev_descs[model_no];
+ io_rsrc_no = dev_desc->io_rsrc_no;
+
+ /* validate pnp resources */
+ if (!pnp_port_valid(pdev, io_rsrc_no) ||
+ pnp_port_len(pdev, io_rsrc_no) != dev_desc->io_region_size) {
+ dev_err(&pdev->dev, "IR PNP Port not valid!\n");
+ goto exit_free_dev_rdev;
+ }
+
+ if (!pnp_irq_valid(pdev, 0)) {
+ dev_err(&pdev->dev, "PNP IRQ not valid!\n");
+ goto exit_free_dev_rdev;
+ }
+
+ /* store resource values */
+ itdev->cir_addr = pnp_port_start(pdev, io_rsrc_no);
+ itdev->cir_irq = pnp_irq(pdev, 0);
+
+ /* initialize spinlocks */
+ spin_lock_init(&itdev->lock);
+
+ /* initialize raw event */
+ init_ir_raw_event(&itdev->rawir);
+
+ /* set driver data into the pnp device */
+ pnp_set_drvdata(pdev, itdev);
+ itdev->pdev = pdev;
+
+ /* initialize waitqueues for transmission */
+ init_waitqueue_head(&itdev->tx_queue);
+ init_waitqueue_head(&itdev->tx_ended);
+
+ /* copy model-specific parameters */
+ itdev->params = *dev_desc;
+
+ /* apply any overrides */
+ if (sample_period > 0)
+ itdev->params.sample_period = sample_period;
+
+ if (tx_carrier_freq > 0)
+ itdev->params.tx_carrier_freq = tx_carrier_freq;
+
+ if (tx_duty_cycle > 0 && tx_duty_cycle <= 100)
+ itdev->params.tx_duty_cycle = tx_duty_cycle;
+
+ if (rx_low_carrier_freq > 0)
+ itdev->params.rx_low_carrier_freq = rx_low_carrier_freq;
+
+ if (rx_high_carrier_freq > 0)
+ itdev->params.rx_high_carrier_freq = rx_high_carrier_freq;
+
+ /* print out parameters */
+ ite_pr(KERN_NOTICE, "TX-capable: %d\n", (int)
+ itdev->params.hw_tx_capable);
+ ite_pr(KERN_NOTICE, "Sample period (ns): %ld\n", (long)
+ itdev->params.sample_period);
+ ite_pr(KERN_NOTICE, "TX carrier frequency (Hz): %d\n", (int)
+ itdev->params.tx_carrier_freq);
+ ite_pr(KERN_NOTICE, "TX duty cycle (%%): %d\n", (int)
+ itdev->params.tx_duty_cycle);
+ ite_pr(KERN_NOTICE, "RX low carrier frequency (Hz): %d\n", (int)
+ itdev->params.rx_low_carrier_freq);
+ ite_pr(KERN_NOTICE, "RX high carrier frequency (Hz): %d\n", (int)
+ itdev->params.rx_high_carrier_freq);
+
+ /* set up hardware initial state */
+ itdev->params.init_hardware(itdev);
+
+ /* set up ir-core props */
+ rdev->priv = itdev;
+ rdev->driver_type = RC_DRIVER_IR_RAW;
+ rc_set_allowed_protocols(rdev, RC_BIT_ALL);
+ rdev->open = ite_open;
+ rdev->close = ite_close;
+ rdev->s_idle = ite_s_idle;
+ rdev->s_rx_carrier_range = ite_set_rx_carrier_range;
+ rdev->min_timeout = ITE_MIN_IDLE_TIMEOUT;
+ rdev->max_timeout = ITE_MAX_IDLE_TIMEOUT;
+ rdev->timeout = ITE_IDLE_TIMEOUT;
+ rdev->rx_resolution = ITE_BAUDRATE_DIVISOR *
+ itdev->params.sample_period;
+ rdev->tx_resolution = ITE_BAUDRATE_DIVISOR *
+ itdev->params.sample_period;
+
+ /* set up transmitter related values if needed */
+ if (itdev->params.hw_tx_capable) {
+ rdev->tx_ir = ite_tx_ir;
+ rdev->s_tx_carrier = ite_set_tx_carrier;
+ rdev->s_tx_duty_cycle = ite_set_tx_duty_cycle;
+ }
+
+ rdev->input_name = dev_desc->model;
+ rdev->input_id.bustype = BUS_HOST;
+ rdev->input_id.vendor = PCI_VENDOR_ID_ITE;
+ rdev->input_id.product = 0;
+ rdev->input_id.version = 0;
+ rdev->driver_name = ITE_DRIVER_NAME;
+ rdev->map_name = RC_MAP_RC6_MCE;
+
+ ret = rc_register_device(rdev);
+ if (ret)
+ goto exit_free_dev_rdev;
+
+ ret = -EBUSY;
+ /* now claim resources */
+ if (!request_region(itdev->cir_addr,
+ dev_desc->io_region_size, ITE_DRIVER_NAME))
+ goto exit_unregister_device;
+
+ if (request_irq(itdev->cir_irq, ite_cir_isr, IRQF_SHARED,
+ ITE_DRIVER_NAME, (void *)itdev))
+ goto exit_release_cir_addr;
+
+ ite_pr(KERN_NOTICE, "driver has been successfully loaded\n");
+
+ return 0;
+
+exit_release_cir_addr:
+ release_region(itdev->cir_addr, itdev->params.io_region_size);
+exit_unregister_device:
+ rc_unregister_device(rdev);
+ rdev = NULL;
+exit_free_dev_rdev:
+ rc_free_device(rdev);
+ kfree(itdev);
+
+ return ret;
+}
+
+static void ite_remove(struct pnp_dev *pdev)
+{
+ struct ite_dev *dev = pnp_get_drvdata(pdev);
+ unsigned long flags;
+
+ ite_dbg("%s called", __func__);
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ /* disable hardware */
+ dev->params.disable(dev);
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ /* free resources */
+ free_irq(dev->cir_irq, dev);
+ release_region(dev->cir_addr, dev->params.io_region_size);
+
+ rc_unregister_device(dev->rdev);
+
+ kfree(dev);
+}
+
+static int ite_suspend(struct pnp_dev *pdev, pm_message_t state)
+{
+ struct ite_dev *dev = pnp_get_drvdata(pdev);
+ unsigned long flags;
+
+ ite_dbg("%s called", __func__);
+
+ /* wait for any transmission to end */
+ wait_event_interruptible(dev->tx_ended, !dev->transmitting);
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ /* disable all interrupts */
+ dev->params.disable(dev);
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return 0;
+}
+
+static int ite_resume(struct pnp_dev *pdev)
+{
+ int ret = 0;
+ struct ite_dev *dev = pnp_get_drvdata(pdev);
+ unsigned long flags;
+
+ ite_dbg("%s called", __func__);
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ /* reinitialize hardware config registers */
+ dev->params.init_hardware(dev);
+ /* enable the receiver */
+ dev->params.enable_rx(dev);
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return ret;
+}
+
+static void ite_shutdown(struct pnp_dev *pdev)
+{
+ struct ite_dev *dev = pnp_get_drvdata(pdev);
+ unsigned long flags;
+
+ ite_dbg("%s called", __func__);
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ /* disable all interrupts */
+ dev->params.disable(dev);
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static struct pnp_driver ite_driver = {
+ .name = ITE_DRIVER_NAME,
+ .id_table = ite_ids,
+ .probe = ite_probe,
+ .remove = ite_remove,
+ .suspend = ite_suspend,
+ .resume = ite_resume,
+ .shutdown = ite_shutdown,
+};
+
+static int ite_init(void)
+{
+ return pnp_register_driver(&ite_driver);
+}
+
+static void ite_exit(void)
+{
+ pnp_unregister_driver(&ite_driver);
+}
+
+MODULE_DEVICE_TABLE(pnp, ite_ids);
+MODULE_DESCRIPTION("ITE Tech Inc. IT8712F/ITE8512F CIR driver");
+
+MODULE_AUTHOR("Juan J. Garcia de Soria <skandalfo@gmail.com>");
+MODULE_LICENSE("GPL");
+
+module_init(ite_init);
+module_exit(ite_exit);
diff --git a/drivers/media/rc/ite-cir.h b/drivers/media/rc/ite-cir.h
new file mode 100644
index 00000000000..aa899a0b975
--- /dev/null
+++ b/drivers/media/rc/ite-cir.h
@@ -0,0 +1,484 @@
+/*
+ * Driver for ITE Tech Inc. IT8712F/IT8512F CIR
+ *
+ * Copyright (C) 2010 Juan Jesús García de Soria <skandalfo@gmail.com>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA.
+ */
+
+/* platform driver name to register */
+#define ITE_DRIVER_NAME "ite-cir"
+
+/* logging macros */
+#define ite_pr(level, text, ...) \
+ printk(level KBUILD_MODNAME ": " text, ## __VA_ARGS__)
+#define ite_dbg(text, ...) do { \
+ if (debug) \
+ printk(KERN_DEBUG \
+ KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__); \
+} while (0)
+
+#define ite_dbg_verbose(text, ...) do {\
+ if (debug > 1) \
+ printk(KERN_DEBUG \
+ KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__); \
+} while (0)
+
+/* FIFO sizes */
+#define ITE_TX_FIFO_LEN 32
+#define ITE_RX_FIFO_LEN 32
+
+/* interrupt types */
+#define ITE_IRQ_TX_FIFO 1
+#define ITE_IRQ_RX_FIFO 2
+#define ITE_IRQ_RX_FIFO_OVERRUN 4
+
+/* forward declaration */
+struct ite_dev;
+
+/* struct for storing the parameters of different recognized devices */
+struct ite_dev_params {
+ /* model of the device */
+ const char *model;
+
+ /* size of the I/O region */
+ int io_region_size;
+
+ /* IR pnp I/O resource number */
+ int io_rsrc_no;
+
+ /* true if the hardware supports transmission */
+ bool hw_tx_capable;
+
+ /* base sampling period, in ns */
+ u32 sample_period;
+
+ /* rx low carrier frequency, in Hz, 0 means no demodulation */
+ unsigned int rx_low_carrier_freq;
+
+ /* tx high carrier frequency, in Hz, 0 means no demodulation */
+ unsigned int rx_high_carrier_freq;
+
+ /* tx carrier frequency, in Hz */
+ unsigned int tx_carrier_freq;
+
+ /* duty cycle, 0-100 */
+ int tx_duty_cycle;
+
+ /* hw-specific operation function pointers; most of these must be
+ * called while holding the spin lock, except for the TX FIFO length
+ * one */
+ /* get pending interrupt causes */
+ int (*get_irq_causes) (struct ite_dev *dev);
+
+ /* enable rx */
+ void (*enable_rx) (struct ite_dev *dev);
+
+ /* make rx enter the idle state; keep listening for a pulse, but stop
+ * streaming space bytes */
+ void (*idle_rx) (struct ite_dev *dev);
+
+ /* disable rx completely */
+ void (*disable_rx) (struct ite_dev *dev);
+
+ /* read bytes from RX FIFO; return read count */
+ int (*get_rx_bytes) (struct ite_dev *dev, u8 *buf, int buf_size);
+
+ /* enable tx FIFO space available interrupt */
+ void (*enable_tx_interrupt) (struct ite_dev *dev);
+
+ /* disable tx FIFO space available interrupt */
+ void (*disable_tx_interrupt) (struct ite_dev *dev);
+
+ /* get number of full TX FIFO slots */
+ int (*get_tx_used_slots) (struct ite_dev *dev);
+
+ /* put a byte to the TX FIFO */
+ void (*put_tx_byte) (struct ite_dev *dev, u8 value);
+
+ /* disable hardware completely */
+ void (*disable) (struct ite_dev *dev);
+
+ /* initialize the hardware */
+ void (*init_hardware) (struct ite_dev *dev);
+
+ /* set the carrier parameters */
+ void (*set_carrier_params) (struct ite_dev *dev, bool high_freq,
+ bool use_demodulator, u8 carrier_freq_bits,
+ u8 allowance_bits, u8 pulse_width_bits);
+};
+
+/* ITE CIR device structure */
+struct ite_dev {
+ struct pnp_dev *pdev;
+ struct rc_dev *rdev;
+ struct ir_raw_event rawir;
+
+ /* sync data */
+ spinlock_t lock;
+ bool in_use, transmitting;
+
+ /* transmit support */
+ int tx_fifo_allowance;
+ wait_queue_head_t tx_queue, tx_ended;
+
+ /* hardware I/O settings */
+ unsigned long cir_addr;
+ int cir_irq;
+
+ /* overridable copy of model parameters */
+ struct ite_dev_params params;
+};
+
+/* common values for all kinds of hardware */
+
+/* baud rate divisor default */
+#define ITE_BAUDRATE_DIVISOR 1
+
+/* low-speed carrier frequency limits (Hz) */
+#define ITE_LCF_MIN_CARRIER_FREQ 27000
+#define ITE_LCF_MAX_CARRIER_FREQ 58000
+
+/* high-speed carrier frequency limits (Hz) */
+#define ITE_HCF_MIN_CARRIER_FREQ 400000
+#define ITE_HCF_MAX_CARRIER_FREQ 500000
+
+/* default carrier freq for when demodulator is off (Hz) */
+#define ITE_DEFAULT_CARRIER_FREQ 38000
+
+/* default idling timeout in ns (0.2 seconds) */
+#define ITE_IDLE_TIMEOUT 200000000UL
+
+/* limit timeout values */
+#define ITE_MIN_IDLE_TIMEOUT 100000000UL
+#define ITE_MAX_IDLE_TIMEOUT 1000000000UL
+
+/* convert bits to us */
+#define ITE_BITS_TO_NS(bits, sample_period) \
+((u32) ((bits) * ITE_BAUDRATE_DIVISOR * sample_period))
+
+/*
+ * n in RDCR produces a tolerance of +/- n * 6.25% around the center
+ * carrier frequency...
+ *
+ * From two limit frequencies, L (low) and H (high), we can get both the
+ * center frequency F = (L + H) / 2 and the variation from the center
+ * frequency A = (H - L) / (H + L). We can use this in order to honor the
+ * s_rx_carrier_range() call in ir-core. We'll suppose that any request
+ * setting L=0 means we must shut down the demodulator.
+ */
+#define ITE_RXDCR_PER_10000_STEP 625
+
+/* high speed carrier freq values */
+#define ITE_CFQ_400 0x03
+#define ITE_CFQ_450 0x08
+#define ITE_CFQ_480 0x0b
+#define ITE_CFQ_500 0x0d
+
+/* values for pulse widths */
+#define ITE_TXMPW_A 0x02
+#define ITE_TXMPW_B 0x03
+#define ITE_TXMPW_C 0x04
+#define ITE_TXMPW_D 0x05
+#define ITE_TXMPW_E 0x06
+
+/* values for demodulator carrier range allowance */
+#define ITE_RXDCR_DEFAULT 0x01 /* default carrier range */
+#define ITE_RXDCR_MAX 0x07 /* default carrier range */
+
+/* DR TX bits */
+#define ITE_TX_PULSE 0x00
+#define ITE_TX_SPACE 0x80
+#define ITE_TX_MAX_RLE 0x80
+#define ITE_TX_RLE_MASK 0x7f
+
+/*
+ * IT8712F
+ *
+ * hardware data obtained from:
+ *
+ * IT8712F
+ * Environment Control – Low Pin Count Input / Output
+ * (EC - LPC I/O)
+ * Preliminary Specification V0. 81
+ */
+
+/* register offsets */
+#define IT87_DR 0x00 /* data register */
+#define IT87_IER 0x01 /* interrupt enable register */
+#define IT87_RCR 0x02 /* receiver control register */
+#define IT87_TCR1 0x03 /* transmitter control register 1 */
+#define IT87_TCR2 0x04 /* transmitter control register 2 */
+#define IT87_TSR 0x05 /* transmitter status register */
+#define IT87_RSR 0x06 /* receiver status register */
+#define IT87_BDLR 0x05 /* baud rate divisor low byte register */
+#define IT87_BDHR 0x06 /* baud rate divisor high byte register */
+#define IT87_IIR 0x07 /* interrupt identification register */
+
+#define IT87_IOREG_LENGTH 0x08 /* length of register file */
+
+/* IER bits */
+#define IT87_TLDLIE 0x01 /* transmitter low data interrupt enable */
+#define IT87_RDAIE 0x02 /* receiver data available interrupt enable */
+#define IT87_RFOIE 0x04 /* receiver FIFO overrun interrupt enable */
+#define IT87_IEC 0x08 /* interrupt enable control */
+#define IT87_BR 0x10 /* baud rate register enable */
+#define IT87_RESET 0x20 /* reset */
+
+/* RCR bits */
+#define IT87_RXDCR 0x07 /* receiver demodulation carrier range mask */
+#define IT87_RXACT 0x08 /* receiver active */
+#define IT87_RXEND 0x10 /* receiver demodulation enable */
+#define IT87_RXEN 0x20 /* receiver enable */
+#define IT87_HCFS 0x40 /* high-speed carrier frequency select */
+#define IT87_RDWOS 0x80 /* receiver data without sync */
+
+/* TCR1 bits */
+#define IT87_TXMPM 0x03 /* transmitter modulation pulse mode mask */
+#define IT87_TXMPM_DEFAULT 0x00 /* modulation pulse mode default */
+#define IT87_TXENDF 0x04 /* transmitter deferral */
+#define IT87_TXRLE 0x08 /* transmitter run length enable */
+#define IT87_FIFOTL 0x30 /* FIFO level threshold mask */
+#define IT87_FIFOTL_DEFAULT 0x20 /* FIFO level threshold default
+ * 0x00 -> 1, 0x10 -> 7, 0x20 -> 17,
+ * 0x30 -> 25 */
+#define IT87_ILE 0x40 /* internal loopback enable */
+#define IT87_FIFOCLR 0x80 /* FIFO clear bit */
+
+/* TCR2 bits */
+#define IT87_TXMPW 0x07 /* transmitter modulation pulse width mask */
+#define IT87_TXMPW_DEFAULT 0x04 /* default modulation pulse width */
+#define IT87_CFQ 0xf8 /* carrier frequency mask */
+#define IT87_CFQ_SHIFT 3 /* carrier frequency bit shift */
+
+/* TSR bits */
+#define IT87_TXFBC 0x3f /* transmitter FIFO byte count mask */
+
+/* RSR bits */
+#define IT87_RXFBC 0x3f /* receiver FIFO byte count mask */
+#define IT87_RXFTO 0x80 /* receiver FIFO time-out */
+
+/* IIR bits */
+#define IT87_IP 0x01 /* interrupt pending */
+#define IT87_II 0x06 /* interrupt identification mask */
+#define IT87_II_NOINT 0x00 /* no interrupt */
+#define IT87_II_TXLDL 0x02 /* transmitter low data level */
+#define IT87_II_RXDS 0x04 /* receiver data stored */
+#define IT87_II_RXFO 0x06 /* receiver FIFO overrun */
+
+/*
+ * IT8512E/F
+ *
+ * Hardware data obtained from:
+ *
+ * IT8512E/F
+ * Embedded Controller
+ * Preliminary Specification V0.4.1
+ *
+ * Note that the CIR registers are not directly available to the host, because
+ * they only are accessible to the integrated microcontroller. Thus, in order
+ * use it, some kind of bridging is required. As the bridging may depend on
+ * the controller firmware in use, we are going to use the PNP ID in order to
+ * determine the strategy and ports available. See after these generic
+ * IT8512E/F register definitions for register definitions for those
+ * strategies.
+ */
+
+/* register offsets */
+#define IT85_C0DR 0x00 /* data register */
+#define IT85_C0MSTCR 0x01 /* master control register */
+#define IT85_C0IER 0x02 /* interrupt enable register */
+#define IT85_C0IIR 0x03 /* interrupt identification register */
+#define IT85_C0CFR 0x04 /* carrier frequency register */
+#define IT85_C0RCR 0x05 /* receiver control register */
+#define IT85_C0TCR 0x06 /* transmitter control register */
+#define IT85_C0SCK 0x07 /* slow clock control register */
+#define IT85_C0BDLR 0x08 /* baud rate divisor low byte register */
+#define IT85_C0BDHR 0x09 /* baud rate divisor high byte register */
+#define IT85_C0TFSR 0x0a /* transmitter FIFO status register */
+#define IT85_C0RFSR 0x0b /* receiver FIFO status register */
+#define IT85_C0WCL 0x0d /* wakeup code length register */
+#define IT85_C0WCR 0x0e /* wakeup code read/write register */
+#define IT85_C0WPS 0x0f /* wakeup power control/status register */
+
+#define IT85_IOREG_LENGTH 0x10 /* length of register file */
+
+/* C0MSTCR bits */
+#define IT85_RESET 0x01 /* reset */
+#define IT85_FIFOCLR 0x02 /* FIFO clear bit */
+#define IT85_FIFOTL 0x0c /* FIFO level threshold mask */
+#define IT85_FIFOTL_DEFAULT 0x08 /* FIFO level threshold default
+ * 0x00 -> 1, 0x04 -> 7, 0x08 -> 17,
+ * 0x0c -> 25 */
+#define IT85_ILE 0x10 /* internal loopback enable */
+#define IT85_ILSEL 0x20 /* internal loopback select */
+
+/* C0IER bits */
+#define IT85_TLDLIE 0x01 /* TX low data level interrupt enable */
+#define IT85_RDAIE 0x02 /* RX data available interrupt enable */
+#define IT85_RFOIE 0x04 /* RX FIFO overrun interrupt enable */
+#define IT85_IEC 0x80 /* interrupt enable function control */
+
+/* C0IIR bits */
+#define IT85_TLDLI 0x01 /* transmitter low data level interrupt */
+#define IT85_RDAI 0x02 /* receiver data available interrupt */
+#define IT85_RFOI 0x04 /* receiver FIFO overrun interrupt */
+#define IT85_NIP 0x80 /* no interrupt pending */
+
+/* C0CFR bits */
+#define IT85_CFQ 0x1f /* carrier frequency mask */
+#define IT85_HCFS 0x20 /* high speed carrier frequency select */
+
+/* C0RCR bits */
+#define IT85_RXDCR 0x07 /* receiver demodulation carrier range mask */
+#define IT85_RXACT 0x08 /* receiver active */
+#define IT85_RXEND 0x10 /* receiver demodulation enable */
+#define IT85_RDWOS 0x20 /* receiver data without sync */
+#define IT85_RXEN 0x80 /* receiver enable */
+
+/* C0TCR bits */
+#define IT85_TXMPW 0x07 /* transmitter modulation pulse width mask */
+#define IT85_TXMPW_DEFAULT 0x04 /* default modulation pulse width */
+#define IT85_TXMPM 0x18 /* transmitter modulation pulse mode mask */
+#define IT85_TXMPM_DEFAULT 0x00 /* modulation pulse mode default */
+#define IT85_TXENDF 0x20 /* transmitter deferral */
+#define IT85_TXRLE 0x40 /* transmitter run length enable */
+
+/* C0SCK bits */
+#define IT85_SCKS 0x01 /* slow clock select */
+#define IT85_TXDCKG 0x02 /* TXD clock gating */
+#define IT85_DLL1P8E 0x04 /* DLL 1.8432M enable */
+#define IT85_DLLTE 0x08 /* DLL test enable */
+#define IT85_BRCM 0x70 /* baud rate count mode */
+#define IT85_DLLOCK 0x80 /* DLL lock */
+
+/* C0TFSR bits */
+#define IT85_TXFBC 0x3f /* transmitter FIFO count mask */
+
+/* C0RFSR bits */
+#define IT85_RXFBC 0x3f /* receiver FIFO count mask */
+#define IT85_RXFTO 0x80 /* receiver FIFO time-out */
+
+/* C0WCL bits */
+#define IT85_WCL 0x3f /* wakeup code length mask */
+
+/* C0WPS bits */
+#define IT85_CIRPOSIE 0x01 /* power on/off status interrupt enable */
+#define IT85_CIRPOIS 0x02 /* power on/off interrupt status */
+#define IT85_CIRPOII 0x04 /* power on/off interrupt identification */
+#define IT85_RCRST 0x10 /* wakeup code reading counter reset bit */
+#define IT85_WCRST 0x20 /* wakeup code writing counter reset bit */
+
+/*
+ * ITE8708
+ *
+ * Hardware data obtained from hacked driver for IT8512 in this forum post:
+ *
+ * http://ubuntuforums.org/showthread.php?t=1028640
+ *
+ * Although there's no official documentation for that driver, analysis would
+ * suggest that it maps the 16 registers of IT8512 onto two 8-register banks,
+ * selectable by a single bank-select bit that's mapped onto both banks. The
+ * IT8512 registers are mapped in a different order, so that the first bank
+ * maps the ones that are used more often, and two registers that share a
+ * reserved high-order bit are placed at the same offset in both banks in
+ * order to reuse the reserved bit as the bank select bit.
+ */
+
+/* register offsets */
+
+/* mapped onto both banks */
+#define IT8708_BANKSEL 0x07 /* bank select register */
+#define IT8708_HRAE 0x80 /* high registers access enable */
+
+/* mapped onto the low bank */
+#define IT8708_C0DR 0x00 /* data register */
+#define IT8708_C0MSTCR 0x01 /* master control register */
+#define IT8708_C0IER 0x02 /* interrupt enable register */
+#define IT8708_C0IIR 0x03 /* interrupt identification register */
+#define IT8708_C0RFSR 0x04 /* receiver FIFO status register */
+#define IT8708_C0RCR 0x05 /* receiver control register */
+#define IT8708_C0TFSR 0x06 /* transmitter FIFO status register */
+#define IT8708_C0TCR 0x07 /* transmitter control register */
+
+/* mapped onto the high bank */
+#define IT8708_C0BDLR 0x01 /* baud rate divisor low byte register */
+#define IT8708_C0BDHR 0x02 /* baud rate divisor high byte register */
+#define IT8708_C0CFR 0x04 /* carrier frequency register */
+
+/* registers whose bank mapping we don't know, since they weren't being used
+ * in the hacked driver... most probably they belong to the high bank too,
+ * since they fit in the holes the other registers leave */
+#define IT8708_C0SCK 0x03 /* slow clock control register */
+#define IT8708_C0WCL 0x05 /* wakeup code length register */
+#define IT8708_C0WCR 0x06 /* wakeup code read/write register */
+#define IT8708_C0WPS 0x07 /* wakeup power control/status register */
+
+#define IT8708_IOREG_LENGTH 0x08 /* length of register file */
+
+/* two more registers that are defined in the hacked driver, but can't be
+ * found in the data sheets; no idea what they are or how they are accessed,
+ * since the hacked driver doesn't seem to use them */
+#define IT8708_CSCRR 0x00
+#define IT8708_CGPINTR 0x01
+
+/* CSCRR bits */
+#define IT8708_CSCRR_SCRB 0x3f
+#define IT8708_CSCRR_PM 0x80
+
+/* CGPINTR bits */
+#define IT8708_CGPINT 0x01
+
+/*
+ * ITE8709
+ *
+ * Hardware interfacing data obtained from the original lirc_ite8709 driver.
+ * Verbatim from its sources:
+ *
+ * The ITE8709 device seems to be the combination of IT8512 superIO chip and
+ * a specific firmware running on the IT8512's embedded micro-controller.
+ * In addition of the embedded micro-controller, the IT8512 chip contains a
+ * CIR module and several other modules. A few modules are directly accessible
+ * by the host CPU, but most of them are only accessible by the
+ * micro-controller. The CIR module is only accessible by the
+ * micro-controller.
+ *
+ * The battery-backed SRAM module is accessible by the host CPU and the
+ * micro-controller. So one of the MC's firmware role is to act as a bridge
+ * between the host CPU and the CIR module. The firmware implements a kind of
+ * communication protocol using the SRAM module as a shared memory. The IT8512
+ * specification is publicly available on ITE's web site, but the
+ * communication protocol is not, so it was reverse-engineered.
+ */
+
+/* register offsets */
+#define IT8709_RAM_IDX 0x00 /* index into the SRAM module bytes */
+#define IT8709_RAM_VAL 0x01 /* read/write data to the indexed byte */
+
+#define IT8709_IOREG_LENGTH 0x02 /* length of register file */
+
+/* register offsets inside the SRAM module */
+#define IT8709_MODE 0x1a /* request/ack byte */
+#define IT8709_REG_IDX 0x1b /* index of the CIR register to access */
+#define IT8709_REG_VAL 0x1c /* value read/to be written */
+#define IT8709_IIR 0x1e /* interrupt identification register */
+#define IT8709_RFSR 0x1f /* receiver FIFO status register */
+#define IT8709_FIFO 0x20 /* start of in RAM RX FIFO copy */
+
+/* MODE values */
+#define IT8709_IDLE 0x00
+#define IT8709_WRITE 0x01
+#define IT8709_READ 0x02
diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile
index 0659e9f5014..0b8c5491901 100644
--- a/drivers/media/rc/keymaps/Makefile
+++ b/drivers/media/rc/keymaps/Makefile
@@ -3,7 +3,9 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
rc-anysee.o \
rc-apac-viewcomp.o \
rc-asus-pc39.o \
+ rc-asus-ps3-100.o \
rc-ati-tv-wonder-hd-600.o \
+ rc-ati-x10.o \
rc-avermedia-a16d.o \
rc-avermedia.o \
rc-avermedia-cardbus.o \
@@ -18,6 +20,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
rc-budget-ci-old.o \
rc-cinergy-1400.o \
rc-cinergy.o \
+ rc-delock-61959.o \
rc-dib0700-nec.o \
rc-dib0700-rc5.o \
rc-digitalnow-tinytwin.o \
@@ -37,17 +40,22 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
rc-gadmei-rm008z.o \
rc-genius-tvgo-a11mce.o \
rc-gotview7135.o \
- rc-hauppauge-new.o \
rc-imon-mce.o \
rc-imon-pad.o \
rc-iodata-bctv7e.o \
+ rc-it913x-v1.o \
+ rc-it913x-v2.o \
rc-kaiomy.o \
rc-kworld-315u.o \
+ rc-kworld-pc150u.o \
rc-kworld-plus-tv-analog.o \
rc-leadtek-y04g0051.o \
rc-lirc.o \
rc-lme2510.o \
rc-manli.o \
+ rc-medion-x10.o \
+ rc-medion-x10-digitainer.o \
+ rc-medion-x10-or2x.o \
rc-msi-digivox-ii.o \
rc-msi-digivox-iii.o \
rc-msi-tvanywhere.o \
@@ -68,16 +76,21 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
rc-proteus-2309.o \
rc-purpletv.o \
rc-pv951.o \
- rc-rc5-hauppauge-new.o \
- rc-rc5-tv.o \
+ rc-hauppauge.o \
rc-rc6-mce.o \
rc-real-audio-220-32-keys.o \
+ rc-reddo.o \
+ rc-snapstream-firefly.o \
rc-streamzap.o \
rc-tbs-nec.o \
+ rc-technisat-usb2.o \
rc-terratec-cinergy-xs.o \
rc-terratec-slim.o \
+ rc-terratec-slim-2.o \
rc-tevii-nec.o \
+ rc-tivo.o \
rc-total-media-in-hand.o \
+ rc-total-media-in-hand-02.o \
rc-trekstor.o \
rc-tt-1500.o \
rc-twinhan1027.o \
@@ -85,4 +98,5 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
rc-videomate-s350.o \
rc-videomate-tv-pvr.o \
rc-winfast.o \
- rc-winfast-usbii-deluxe.o
+ rc-winfast-usbii-deluxe.o \
+ rc-su3000.o
diff --git a/drivers/media/rc/keymaps/rc-adstech-dvb-t-pci.c b/drivers/media/rc/keymaps/rc-adstech-dvb-t-pci.c
index 136d3952ded..01d901fbfc8 100644
--- a/drivers/media/rc/keymaps/rc-adstech-dvb-t-pci.c
+++ b/drivers/media/rc/keymaps/rc-adstech-dvb-t-pci.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* ADS Tech Instant TV DVB-T PCI Remote */
@@ -50,9 +51,9 @@ static struct rc_map_table adstech_dvb_t_pci[] = {
{ 0x13, KEY_TUNER }, /* Live */
{ 0x0a, KEY_A },
{ 0x12, KEY_B },
- { 0x03, KEY_PROG1 }, /* 1 */
- { 0x01, KEY_PROG2 }, /* 2 */
- { 0x00, KEY_PROG3 }, /* 3 */
+ { 0x03, KEY_RED }, /* 1 */
+ { 0x01, KEY_GREEN }, /* 2 */
+ { 0x00, KEY_YELLOW }, /* 3 */
{ 0x06, KEY_DVD },
{ 0x48, KEY_AUX }, /* Photo */
{ 0x40, KEY_VIDEO },
@@ -86,4 +87,4 @@ module_init(init_rc_map_adstech_dvb_t_pci)
module_exit(exit_rc_map_adstech_dvb_t_pci)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-alink-dtu-m.c b/drivers/media/rc/keymaps/rc-alink-dtu-m.c
index fe652e928dc..4e6ade8e616 100644
--- a/drivers/media/rc/keymaps/rc-alink-dtu-m.c
+++ b/drivers/media/rc/keymaps/rc-alink-dtu-m.c
@@ -19,6 +19,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* A-Link DTU(m) slim remote, 6 rows, 3 columns. */
static struct rc_map_table alink_dtu_m[] = {
diff --git a/drivers/media/rc/keymaps/rc-anysee.c b/drivers/media/rc/keymaps/rc-anysee.c
index 884f1b51a8e..c735fe10a39 100644
--- a/drivers/media/rc/keymaps/rc-anysee.c
+++ b/drivers/media/rc/keymaps/rc-anysee.c
@@ -19,6 +19,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table anysee[] = {
{ 0x0800, KEY_0 },
diff --git a/drivers/media/rc/keymaps/rc-apac-viewcomp.c b/drivers/media/rc/keymaps/rc-apac-viewcomp.c
index 7af188209ff..bf9efa007e1 100644
--- a/drivers/media/rc/keymaps/rc-apac-viewcomp.c
+++ b/drivers/media/rc/keymaps/rc-apac-viewcomp.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Attila Kondoros <attila.kondoros@chello.hu> */
@@ -77,4 +78,4 @@ module_init(init_rc_map_apac_viewcomp)
module_exit(exit_rc_map_apac_viewcomp)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-asus-pc39.c b/drivers/media/rc/keymaps/rc-asus-pc39.c
index b2481154491..9e674ba5dd4 100644
--- a/drivers/media/rc/keymaps/rc-asus-pc39.c
+++ b/drivers/media/rc/keymaps/rc-asus-pc39.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/*
* Marc Fargas <telenieko@telenieko.com>
@@ -88,4 +89,4 @@ module_init(init_rc_map_asus_pc39)
module_exit(exit_rc_map_asus_pc39)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-asus-ps3-100.c b/drivers/media/rc/keymaps/rc-asus-ps3-100.c
new file mode 100644
index 00000000000..e45de35f528
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-asus-ps3-100.c
@@ -0,0 +1,91 @@
+/* asus-ps3-100.h - Keytable for asus_ps3_100 Remote Controller
+ *
+ * Copyright (c) 2012 by Mauro Carvalho Chehab
+ *
+ * Based on a previous patch from Remi Schwartz <remi.schwartz@gmail.com>
+ *
+ * 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.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table asus_ps3_100[] = {
+ { 0x081c, KEY_HOME }, /* home */
+ { 0x081e, KEY_TV }, /* tv */
+ { 0x0803, KEY_TEXT }, /* teletext */
+ { 0x0829, KEY_POWER }, /* close */
+
+ { 0x080b, KEY_RED }, /* red */
+ { 0x080d, KEY_YELLOW }, /* yellow */
+ { 0x0806, KEY_BLUE }, /* blue */
+ { 0x0807, KEY_GREEN }, /* green */
+
+ /* Keys 0 to 9 */
+ { 0x082a, KEY_0 },
+ { 0x0816, KEY_1 },
+ { 0x0812, KEY_2 },
+ { 0x0814, KEY_3 },
+ { 0x0836, KEY_4 },
+ { 0x0832, KEY_5 },
+ { 0x0834, KEY_6 },
+ { 0x080e, KEY_7 },
+ { 0x080a, KEY_8 },
+ { 0x080c, KEY_9 },
+
+ { 0x0815, KEY_VOLUMEUP },
+ { 0x0826, KEY_VOLUMEDOWN },
+ { 0x0835, KEY_CHANNELUP }, /* channel / program + */
+ { 0x0824, KEY_CHANNELDOWN }, /* channel / program - */
+
+ { 0x0808, KEY_UP },
+ { 0x0804, KEY_DOWN },
+ { 0x0818, KEY_LEFT },
+ { 0x0810, KEY_RIGHT },
+ { 0x0825, KEY_ENTER }, /* enter */
+
+ { 0x0822, KEY_EXIT }, /* back */
+ { 0x082c, KEY_AB }, /* recall */
+
+ { 0x0820, KEY_AUDIO }, /* TV audio */
+ { 0x0837, KEY_SCREEN }, /* snapshot */
+ { 0x082e, KEY_ZOOM }, /* full screen */
+ { 0x0802, KEY_MUTE }, /* mute */
+
+ { 0x0831, KEY_REWIND }, /* backward << */
+ { 0x0811, KEY_RECORD }, /* recording */
+ { 0x0809, KEY_STOP },
+ { 0x0805, KEY_FASTFORWARD }, /* forward >> */
+ { 0x0821, KEY_PREVIOUS }, /* rew */
+ { 0x081a, KEY_PAUSE }, /* pause */
+ { 0x0839, KEY_PLAY }, /* play */
+ { 0x0819, KEY_NEXT }, /* forward */
+};
+
+static struct rc_map_list asus_ps3_100_map = {
+.map = {
+ .scan = asus_ps3_100,
+ .size = ARRAY_SIZE(asus_ps3_100),
+ .rc_type = RC_TYPE_RC5,
+ .name = RC_MAP_ASUS_PS3_100,
+}
+};
+
+static int __init init_rc_map_asus_ps3_100(void)
+{
+return rc_map_register(&asus_ps3_100_map);
+}
+
+static void __exit exit_rc_map_asus_ps3_100(void)
+{
+rc_map_unregister(&asus_ps3_100_map);
+}
+
+module_init(init_rc_map_asus_ps3_100)
+module_exit(exit_rc_map_asus_ps3_100)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-ati-tv-wonder-hd-600.c b/drivers/media/rc/keymaps/rc-ati-tv-wonder-hd-600.c
index f766b24b015..91392d4cfd6 100644
--- a/drivers/media/rc/keymaps/rc-ati-tv-wonder-hd-600.c
+++ b/drivers/media/rc/keymaps/rc-ati-tv-wonder-hd-600.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* ATI TV Wonder HD 600 USB
Devin Heitmueller <devin.heitmueller@gmail.com>
@@ -66,4 +67,4 @@ module_init(init_rc_map_ati_tv_wonder_hd_600)
module_exit(exit_rc_map_ati_tv_wonder_hd_600)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-ati-x10.c b/drivers/media/rc/keymaps/rc-ati-x10.c
new file mode 100644
index 00000000000..81506440ede
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-ati-x10.c
@@ -0,0 +1,104 @@
+/*
+ * ATI X10 RF remote keytable
+ *
+ * Copyright (C) 2011 Anssi Hannula <anssi.hannula@?ki.fi>
+ *
+ * This file is based on the static generic keytable previously found in
+ * ati_remote.c, which is
+ * Copyright (c) 2004 Torrey Hoffman <thoffman@arnor.net>
+ * Copyright (c) 2002 Vladimir Dergachev
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/module.h>
+#include <media/rc-map.h>
+
+static struct rc_map_table ati_x10[] = {
+ { 0x0d, KEY_1 },
+ { 0x0e, KEY_2 },
+ { 0x0f, KEY_3 },
+ { 0x10, KEY_4 },
+ { 0x11, KEY_5 },
+ { 0x12, KEY_6 },
+ { 0x13, KEY_7 },
+ { 0x14, KEY_8 },
+ { 0x15, KEY_9 },
+ { 0x17, KEY_0 },
+ { 0x00, KEY_A },
+ { 0x01, KEY_B },
+ { 0x19, KEY_C },
+ { 0x1b, KEY_D },
+ { 0x21, KEY_E },
+ { 0x23, KEY_F },
+
+ { 0x18, KEY_KPENTER }, /* "check" */
+ { 0x16, KEY_MENU }, /* "menu" */
+ { 0x02, KEY_POWER }, /* Power */
+ { 0x03, KEY_TV }, /* TV */
+ { 0x04, KEY_DVD }, /* DVD */
+ { 0x05, KEY_WWW }, /* WEB */
+ { 0x06, KEY_BOOKMARKS }, /* "book" */
+ { 0x07, KEY_EDIT }, /* "hand" */
+ { 0x1c, KEY_COFFEE }, /* "timer" */
+ { 0x20, KEY_FRONT }, /* "max" */
+ { 0x1d, KEY_LEFT }, /* left */
+ { 0x1f, KEY_RIGHT }, /* right */
+ { 0x22, KEY_DOWN }, /* down */
+ { 0x1a, KEY_UP }, /* up */
+ { 0x1e, KEY_OK }, /* "OK" */
+ { 0x09, KEY_VOLUMEDOWN }, /* VOL + */
+ { 0x08, KEY_VOLUMEUP }, /* VOL - */
+ { 0x0a, KEY_MUTE }, /* MUTE */
+ { 0x0b, KEY_CHANNELUP }, /* CH + */
+ { 0x0c, KEY_CHANNELDOWN },/* CH - */
+ { 0x27, KEY_RECORD }, /* ( o) red */
+ { 0x25, KEY_PLAY }, /* ( >) */
+ { 0x24, KEY_REWIND }, /* (<<) */
+ { 0x26, KEY_FORWARD }, /* (>>) */
+ { 0x28, KEY_STOP }, /* ([]) */
+ { 0x29, KEY_PAUSE }, /* ('') */
+ { 0x2b, KEY_PREVIOUS }, /* (<-) */
+ { 0x2a, KEY_NEXT }, /* (>+) */
+ { 0x2d, KEY_INFO }, /* PLAYING */
+ { 0x2e, KEY_HOME }, /* TOP */
+ { 0x2f, KEY_END }, /* END */
+ { 0x30, KEY_SELECT }, /* SELECT */
+};
+
+static struct rc_map_list ati_x10_map = {
+ .map = {
+ .scan = ati_x10,
+ .size = ARRAY_SIZE(ati_x10),
+ .rc_type = RC_TYPE_OTHER,
+ .name = RC_MAP_ATI_X10,
+ }
+};
+
+static int __init init_rc_map_ati_x10(void)
+{
+ return rc_map_register(&ati_x10_map);
+}
+
+static void __exit exit_rc_map_ati_x10(void)
+{
+ rc_map_unregister(&ati_x10_map);
+}
+
+module_init(init_rc_map_ati_x10)
+module_exit(exit_rc_map_ati_x10)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Anssi Hannula <anssi.hannula@iki.fi>");
diff --git a/drivers/media/rc/keymaps/rc-avermedia-a16d.c b/drivers/media/rc/keymaps/rc-avermedia-a16d.c
index ec9beeebd41..ff30a71d623 100644
--- a/drivers/media/rc/keymaps/rc-avermedia-a16d.c
+++ b/drivers/media/rc/keymaps/rc-avermedia-a16d.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table avermedia_a16d[] = {
{ 0x20, KEY_LIST},
@@ -72,4 +73,4 @@ module_init(init_rc_map_avermedia_a16d)
module_exit(exit_rc_map_avermedia_a16d)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-avermedia-cardbus.c b/drivers/media/rc/keymaps/rc-avermedia-cardbus.c
index bdf97b74cf9..d7471a6de9b 100644
--- a/drivers/media/rc/keymaps/rc-avermedia-cardbus.c
+++ b/drivers/media/rc/keymaps/rc-avermedia-cardbus.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Oldrich Jedlicka <oldium.pro@seznam.cz> */
@@ -52,7 +53,7 @@ static struct rc_map_table avermedia_cardbus[] = {
{ 0x28, KEY_SELECT }, /* Select */
{ 0x29, KEY_BLUE }, /* Blue/Picture */
{ 0x2a, KEY_BACKSPACE }, /* Back */
- { 0x2b, KEY_MEDIA }, /* PIP (Picture-in-picture) */
+ { 0x2b, KEY_VIDEO }, /* PIP (Picture-in-picture) */
{ 0x2c, KEY_DOWN },
{ 0x2e, KEY_DOT },
{ 0x2f, KEY_TV }, /* Live TV */
@@ -94,4 +95,4 @@ module_init(init_rc_map_avermedia_cardbus)
module_exit(exit_rc_map_avermedia_cardbus)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-avermedia-dvbt.c b/drivers/media/rc/keymaps/rc-avermedia-dvbt.c
index 3ddb41bc075..e2417d6331f 100644
--- a/drivers/media/rc/keymaps/rc-avermedia-dvbt.c
+++ b/drivers/media/rc/keymaps/rc-avermedia-dvbt.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Matt Jesson <dvb@jesson.eclipse.co.uk */
@@ -26,12 +27,12 @@ static struct rc_map_table avermedia_dvbt[] = {
{ 0x16, KEY_8 }, /* '8' / 'down arrow' */
{ 0x36, KEY_9 }, /* '9' */
- { 0x20, KEY_LIST }, /* 'source' */
+ { 0x20, KEY_VIDEO }, /* 'source' */
{ 0x10, KEY_TEXT }, /* 'teletext' */
{ 0x00, KEY_POWER }, /* 'power' */
{ 0x04, KEY_AUDIO }, /* 'audio' */
{ 0x06, KEY_ZOOM }, /* 'full screen' */
- { 0x18, KEY_VIDEO }, /* 'display' */
+ { 0x18, KEY_SWITCHVIDEOMODE }, /* 'display' */
{ 0x38, KEY_SEARCH }, /* 'loop' */
{ 0x08, KEY_INFO }, /* 'preview' */
{ 0x2a, KEY_REWIND }, /* 'backward <<' */
@@ -75,4 +76,4 @@ module_init(init_rc_map_avermedia_dvbt)
module_exit(exit_rc_map_avermedia_dvbt)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-avermedia-m135a.c b/drivers/media/rc/keymaps/rc-avermedia-m135a.c
index 357fea58a46..843598a5f1b 100644
--- a/drivers/media/rc/keymaps/rc-avermedia-m135a.c
+++ b/drivers/media/rc/keymaps/rc-avermedia-m135a.c
@@ -1,6 +1,6 @@
/* avermedia-m135a.c - Keytable for Avermedia M135A Remote Controllers
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
* Copyright (c) 2010 by Herton Ronaldo Krzesinski <herton@mandriva.com.br>
*
* This program is free software; you can redistribute it and/or modify
@@ -10,6 +10,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/*
* Avermedia M135A with RM-JX and RM-K6 remote controls
@@ -108,7 +109,7 @@ static struct rc_map_table avermedia_m135a[] = {
{ 0x0414, KEY_TEXT },
{ 0x0415, KEY_EPG },
{ 0x041a, KEY_TV2 }, /* PIP */
- { 0x041b, KEY_MHP }, /* Snapshot */
+ { 0x041b, KEY_CAMERA }, /* Snapshot */
{ 0x0417, KEY_RECORD },
{ 0x0416, KEY_PLAYPAUSE },
@@ -144,4 +145,4 @@ module_init(init_rc_map_avermedia_m135a)
module_exit(exit_rc_map_avermedia_m135a)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-avermedia-m733a-rm-k6.c b/drivers/media/rc/keymaps/rc-avermedia-m733a-rm-k6.c
index e694e6eac37..b24e7481ac2 100644
--- a/drivers/media/rc/keymaps/rc-avermedia-m733a-rm-k6.c
+++ b/drivers/media/rc/keymaps/rc-avermedia-m733a-rm-k6.c
@@ -9,6 +9,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/*
* Avermedia M733A with IR model RM-K6
@@ -56,7 +57,7 @@ static struct rc_map_table avermedia_m733a_rm_k6[] = {
{ 0x0414, KEY_TEXT },
{ 0x0415, KEY_EPG },
{ 0x041a, KEY_TV2 }, /* PIP */
- { 0x041b, KEY_MHP }, /* Snapshot */
+ { 0x041b, KEY_CAMERA }, /* Snapshot */
{ 0x0417, KEY_RECORD },
{ 0x0416, KEY_PLAYPAUSE },
@@ -92,4 +93,4 @@ module_init(init_rc_map_avermedia_m733a_rm_k6)
module_exit(exit_rc_map_avermedia_m733a_rm_k6)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-avermedia-rm-ks.c b/drivers/media/rc/keymaps/rc-avermedia-rm-ks.c
index f4ca1fff455..8344bcc595b 100644
--- a/drivers/media/rc/keymaps/rc-avermedia-rm-ks.c
+++ b/drivers/media/rc/keymaps/rc-avermedia-rm-ks.c
@@ -19,6 +19,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Initial keytable is from Jose Alberto Reguero <jareguero@telefonica.net>
and Felipe Morales Moreno <felipe.morales.moreno@gmail.com> */
@@ -31,7 +32,7 @@ static struct rc_map_table avermedia_rm_ks[] = {
{ 0x0505, KEY_VOLUMEDOWN },
{ 0x0506, KEY_MUTE },
{ 0x0507, KEY_RIGHT },
- { 0x0508, KEY_PROG1 },
+ { 0x0508, KEY_RED },
{ 0x0509, KEY_1 },
{ 0x050a, KEY_2 },
{ 0x050b, KEY_3 },
diff --git a/drivers/media/rc/keymaps/rc-avermedia.c b/drivers/media/rc/keymaps/rc-avermedia.c
index edfa71506d3..3f68fbecc18 100644
--- a/drivers/media/rc/keymaps/rc-avermedia.c
+++ b/drivers/media/rc/keymaps/rc-avermedia.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Alex Hermann <gaaf@gmx.net> */
@@ -83,4 +84,4 @@ module_init(init_rc_map_avermedia)
module_exit(exit_rc_map_avermedia)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-avertv-303.c b/drivers/media/rc/keymaps/rc-avertv-303.c
index 32e94988dc9..c35bc5b835c 100644
--- a/drivers/media/rc/keymaps/rc-avertv-303.c
+++ b/drivers/media/rc/keymaps/rc-avertv-303.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* AVERTV STUDIO 303 Remote */
@@ -82,4 +83,4 @@ module_init(init_rc_map_avertv_303)
module_exit(exit_rc_map_avertv_303)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-azurewave-ad-tu700.c b/drivers/media/rc/keymaps/rc-azurewave-ad-tu700.c
index c3f6d62ac89..ea7f2d0f31e 100644
--- a/drivers/media/rc/keymaps/rc-azurewave-ad-tu700.c
+++ b/drivers/media/rc/keymaps/rc-azurewave-ad-tu700.c
@@ -19,6 +19,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table azurewave_ad_tu700[] = {
{ 0x0000, KEY_TAB }, /* Tab */
diff --git a/drivers/media/rc/keymaps/rc-behold-columbus.c b/drivers/media/rc/keymaps/rc-behold-columbus.c
index 4b787fa94f0..1fc344e9daa 100644
--- a/drivers/media/rc/keymaps/rc-behold-columbus.c
+++ b/drivers/media/rc/keymaps/rc-behold-columbus.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Beholder Intl. Ltd. 2008
* Dmitry Belimov d.belimov@google.com
@@ -28,7 +29,7 @@ static struct rc_map_table behold_columbus[] = {
* */
{ 0x13, KEY_MUTE },
- { 0x11, KEY_PROPS },
+ { 0x11, KEY_VIDEO },
{ 0x1C, KEY_TUNER }, /* KEY_TV/KEY_RADIO */
{ 0x12, KEY_POWER },
@@ -105,4 +106,4 @@ module_init(init_rc_map_behold_columbus)
module_exit(exit_rc_map_behold_columbus)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-behold.c b/drivers/media/rc/keymaps/rc-behold.c
index 0ee1f149364..d6519f8ac95 100644
--- a/drivers/media/rc/keymaps/rc-behold.c
+++ b/drivers/media/rc/keymaps/rc-behold.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/*
* Igor Kuznetsov <igk72@ya.ru>
@@ -97,7 +98,7 @@ static struct rc_map_table behold[] = {
{ 0x6b861a, KEY_STOP },
{ 0x6b860e, KEY_TEXT },
{ 0x6b861f, KEY_RED }, /*XXX KEY_AUDIO */
- { 0x6b861e, KEY_YELLOW }, /*XXX KEY_SOURCE */
+ { 0x6b861e, KEY_VIDEO },
/* 0x1d 0x13 0x19 *
* SLEEP PREVIEW DVB *
@@ -138,4 +139,4 @@ module_init(init_rc_map_behold)
module_exit(exit_rc_map_behold)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-budget-ci-old.c b/drivers/media/rc/keymaps/rc-budget-ci-old.c
index 97fc3862f60..b196a5f436a 100644
--- a/drivers/media/rc/keymaps/rc-budget-ci-old.c
+++ b/drivers/media/rc/keymaps/rc-budget-ci-old.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,8 +11,10 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
-/* From reading the following remotes:
+/*
+ * From reading the following remotes:
* Zenith Universal 7 / TV Mode 807 / VCR Mode 837
* Hauppauge (from NOVA-CI-s box product)
* This is a "middle of the road" approach, differences are noted
@@ -89,4 +91,4 @@ module_init(init_rc_map_budget_ci_old)
module_exit(exit_rc_map_budget_ci_old)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-cinergy-1400.c b/drivers/media/rc/keymaps/rc-cinergy-1400.c
index 284534b67e7..a099c080bf8 100644
--- a/drivers/media/rc/keymaps/rc-cinergy-1400.c
+++ b/drivers/media/rc/keymaps/rc-cinergy-1400.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Cinergy 1400 DVB-T */
@@ -81,4 +82,4 @@ module_init(init_rc_map_cinergy_1400)
module_exit(exit_rc_map_cinergy_1400)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-cinergy.c b/drivers/media/rc/keymaps/rc-cinergy.c
index 99520ff65b6..b0f4328bdd6 100644
--- a/drivers/media/rc/keymaps/rc-cinergy.c
+++ b/drivers/media/rc/keymaps/rc-cinergy.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table cinergy[] = {
{ 0x00, KEY_0 },
@@ -25,7 +26,7 @@ static struct rc_map_table cinergy[] = {
{ 0x09, KEY_9 },
{ 0x0a, KEY_POWER },
- { 0x0b, KEY_PROG1 }, /* app */
+ { 0x0b, KEY_MEDIA }, /* app */
{ 0x0c, KEY_ZOOM }, /* zoom/fullscreen */
{ 0x0d, KEY_CHANNELUP }, /* channel */
{ 0x0e, KEY_CHANNELDOWN }, /* channel- */
@@ -75,4 +76,4 @@ module_init(init_rc_map_cinergy)
module_exit(exit_rc_map_cinergy)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-delock-61959.c b/drivers/media/rc/keymaps/rc-delock-61959.c
new file mode 100644
index 00000000000..01bed864f09
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-delock-61959.c
@@ -0,0 +1,83 @@
+/* rc-delock-61959.c - Keytable for Delock
+ *
+ * Copyright (c) 2013 by Jakob Haufe <sur5r@sur5r.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.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/*
+ * Keytable for remote provided with Delock 61959
+ */
+static struct rc_map_table delock_61959[] = {
+ { 0x866b16, KEY_POWER2 }, /* Power */
+ { 0x866b0c, KEY_POWER }, /* Shut Down */
+
+ { 0x866b00, KEY_1},
+ { 0x866b01, KEY_2},
+ { 0x866b02, KEY_3},
+ { 0x866b03, KEY_4},
+ { 0x866b04, KEY_5},
+ { 0x866b05, KEY_6},
+ { 0x866b06, KEY_7},
+ { 0x866b07, KEY_8},
+ { 0x866b08, KEY_9},
+ { 0x866b14, KEY_0},
+
+ { 0x866b0a, KEY_ZOOM}, /* Full Screen */
+ { 0x866b10, KEY_CAMERA}, /* Photo */
+ { 0x866b0e, KEY_CHANNEL}, /* circular arrow / Recall */
+ { 0x866b13, KEY_ESC}, /* Back */
+
+ { 0x866b20, KEY_UP},
+ { 0x866b21, KEY_DOWN},
+ { 0x866b42, KEY_LEFT},
+ { 0x866b43, KEY_RIGHT},
+ { 0x866b0b, KEY_OK},
+
+ { 0x866b11, KEY_CHANNELUP},
+ { 0x866b1b, KEY_CHANNELDOWN},
+
+ { 0x866b12, KEY_VOLUMEUP},
+ { 0x866b48, KEY_VOLUMEDOWN},
+ { 0x866b44, KEY_MUTE},
+
+ { 0x866b1a, KEY_RECORD},
+ { 0x866b41, KEY_PLAY},
+ { 0x866b40, KEY_STOP},
+ { 0x866b19, KEY_PAUSE},
+ { 0x866b1c, KEY_FASTFORWARD}, /* >> / FWD */
+ { 0x866b1e, KEY_REWIND}, /* << / REW */
+
+};
+
+static struct rc_map_list delock_61959_map = {
+ .map = {
+ .scan = delock_61959,
+ .size = ARRAY_SIZE(delock_61959),
+ .rc_type = RC_TYPE_NEC,
+ .name = RC_MAP_DELOCK_61959,
+ }
+};
+
+static int __init init_rc_map_delock_61959(void)
+{
+ return rc_map_register(&delock_61959_map);
+}
+
+static void __exit exit_rc_map_delock_61959(void)
+{
+ rc_map_unregister(&delock_61959_map);
+}
+
+module_init(init_rc_map_delock_61959)
+module_exit(exit_rc_map_delock_61959)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jakob Haufe <sur5r@sur5r.net>");
+MODULE_DESCRIPTION("Delock 61959 remote keytable");
diff --git a/drivers/media/rc/keymaps/rc-dib0700-nec.c b/drivers/media/rc/keymaps/rc-dib0700-nec.c
index 7a5f5300caf..a0fa543c9f9 100644
--- a/drivers/media/rc/keymaps/rc-dib0700-nec.c
+++ b/drivers/media/rc/keymaps/rc-dib0700-nec.c
@@ -1,11 +1,11 @@
/* rc-dvb0700-big.c - Keytable for devices in dvb0700
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* TODO: This table is a real mess, as it merges RC codes from several
* devices into a big table. It also has both RC-5 and NEC codes inside.
* It should be broken into small tables, and the protocols should properly
- * be indentificated.
+ * be identificated.
*
* The table were imported from dib0700_devices.c.
*
@@ -16,6 +16,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table dib0700_nec_table[] = {
/* Key codes for the Pixelview SBTVD remote */
@@ -121,4 +122,4 @@ module_init(init_rc_map)
module_exit(exit_rc_map)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-dib0700-rc5.c b/drivers/media/rc/keymaps/rc-dib0700-rc5.c
index 4af12e45dfb..907941145eb 100644
--- a/drivers/media/rc/keymaps/rc-dib0700-rc5.c
+++ b/drivers/media/rc/keymaps/rc-dib0700-rc5.c
@@ -1,11 +1,11 @@
/* rc-dvb0700-big.c - Keytable for devices in dvb0700
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* TODO: This table is a real mess, as it merges RC codes from several
* devices into a big table. It also has both RC-5 and NEC codes inside.
* It should be broken into small tables, and the protocols should properly
- * be indentificated.
+ * be identificated.
*
* The table were imported from dib0700_devices.c.
*
@@ -16,6 +16,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table dib0700_rc5_table[] = {
/* Key codes for the tiny Pinnacle remote*/
@@ -232,4 +233,4 @@ module_init(init_rc_map)
module_exit(exit_rc_map)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-digitalnow-tinytwin.c b/drivers/media/rc/keymaps/rc-digitalnow-tinytwin.c
index f68b450f559..bed78acb919 100644
--- a/drivers/media/rc/keymaps/rc-digitalnow-tinytwin.c
+++ b/drivers/media/rc/keymaps/rc-digitalnow-tinytwin.c
@@ -19,6 +19,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table digitalnow_tinytwin[] = {
{ 0x0000, KEY_MUTE }, /* [symbol speaker] */
diff --git a/drivers/media/rc/keymaps/rc-digittrade.c b/drivers/media/rc/keymaps/rc-digittrade.c
index 21d49871f2a..a3b97a1fe22 100644
--- a/drivers/media/rc/keymaps/rc-digittrade.c
+++ b/drivers/media/rc/keymaps/rc-digittrade.c
@@ -19,6 +19,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Digittrade DVB-T USB Stick remote controller. */
/* Imported from af9015.h.
diff --git a/drivers/media/rc/keymaps/rc-dm1105-nec.c b/drivers/media/rc/keymaps/rc-dm1105-nec.c
index d024fbf88bc..46e7ae414cc 100644
--- a/drivers/media/rc/keymaps/rc-dm1105-nec.c
+++ b/drivers/media/rc/keymaps/rc-dm1105-nec.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* DVBWorld remotes
Igor M. Liplianin <liplianin@me.by>
@@ -73,4 +74,4 @@ module_init(init_rc_map_dm1105_nec)
module_exit(exit_rc_map_dm1105_nec)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-dntv-live-dvb-t.c b/drivers/media/rc/keymaps/rc-dntv-live-dvb-t.c
index 43912bd02a9..d2826b46fea 100644
--- a/drivers/media/rc/keymaps/rc-dntv-live-dvb-t.c
+++ b/drivers/media/rc/keymaps/rc-dntv-live-dvb-t.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* DigitalNow DNTV Live DVB-T Remote */
@@ -32,7 +33,7 @@ static struct rc_map_table dntv_live_dvb_t[] = {
{ 0x0c, KEY_SEARCH }, /* scan */
{ 0x0d, KEY_STOP },
{ 0x0e, KEY_PAUSE },
- { 0x0f, KEY_LIST }, /* source */
+ { 0x0f, KEY_VIDEO }, /* source */
{ 0x10, KEY_MUTE },
{ 0x11, KEY_REWIND }, /* backward << */
@@ -75,4 +76,4 @@ module_init(init_rc_map_dntv_live_dvb_t)
module_exit(exit_rc_map_dntv_live_dvb_t)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-dntv-live-dvbt-pro.c b/drivers/media/rc/keymaps/rc-dntv-live-dvbt-pro.c
index 015e99de06d..0d74769467b 100644
--- a/drivers/media/rc/keymaps/rc-dntv-live-dvbt-pro.c
+++ b/drivers/media/rc/keymaps/rc-dntv-live-dvbt-pro.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* DigitalNow DNTV Live! DVB-T Pro Remote */
@@ -94,4 +95,4 @@ module_init(init_rc_map_dntv_live_dvbt_pro)
module_exit(exit_rc_map_dntv_live_dvbt_pro)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-em-terratec.c b/drivers/media/rc/keymaps/rc-em-terratec.c
index 269d4299da3..7f1e06be175 100644
--- a/drivers/media/rc/keymaps/rc-em-terratec.c
+++ b/drivers/media/rc/keymaps/rc-em-terratec.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table em_terratec[] = {
{ 0x01, KEY_CHANNEL },
@@ -66,4 +67,4 @@ module_init(init_rc_map_em_terratec)
module_exit(exit_rc_map_em_terratec)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-encore-enltv-fm53.c b/drivers/media/rc/keymaps/rc-encore-enltv-fm53.c
index e388698a069..4fc3904daf0 100644
--- a/drivers/media/rc/keymaps/rc-encore-enltv-fm53.c
+++ b/drivers/media/rc/keymaps/rc-encore-enltv-fm53.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Encore ENLTV-FM v5.3
Mauro Carvalho Chehab <mchehab@infradead.org>
@@ -78,4 +79,4 @@ module_init(init_rc_map_encore_enltv_fm53)
module_exit(exit_rc_map_encore_enltv_fm53)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-encore-enltv.c b/drivers/media/rc/keymaps/rc-encore-enltv.c
index afa4e92284e..f1914e23d20 100644
--- a/drivers/media/rc/keymaps/rc-encore-enltv.c
+++ b/drivers/media/rc/keymaps/rc-encore-enltv.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Encore ENLTV-FM - black plastic, white front cover with white glowing buttons
Juan Pablo Sormani <sorman@gmail.com> */
@@ -24,7 +25,7 @@ static struct rc_map_table encore_enltv[] = {
{ 0x1e, KEY_TV },
{ 0x00, KEY_VIDEO },
{ 0x01, KEY_AUDIO }, /* music */
- { 0x02, KEY_MHP }, /* picture */
+ { 0x02, KEY_CAMERA }, /* picture */
{ 0x1f, KEY_1 },
{ 0x03, KEY_2 },
@@ -77,7 +78,7 @@ static struct rc_map_table encore_enltv[] = {
{ 0x50, KEY_SLEEP }, /* shutdown */
{ 0x51, KEY_MODE }, /* stereo > main */
{ 0x52, KEY_SELECT }, /* stereo > sap */
- { 0x53, KEY_PROG1 }, /* teletext */
+ { 0x53, KEY_TEXT }, /* teletext */
{ 0x59, KEY_RED }, /* AP1 */
@@ -109,4 +110,4 @@ module_init(init_rc_map_encore_enltv)
module_exit(exit_rc_map_encore_enltv)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-encore-enltv2.c b/drivers/media/rc/keymaps/rc-encore-enltv2.c
index 7d5b00ed4ff..9c6c55240d1 100644
--- a/drivers/media/rc/keymaps/rc-encore-enltv2.c
+++ b/drivers/media/rc/keymaps/rc-encore-enltv2.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Encore ENLTV2-FM - silver plastic - "Wand Media" written at the botton
Mauro Carvalho Chehab <mchehab@infradead.org> */
@@ -32,7 +33,7 @@ static struct rc_map_table encore_enltv2[] = {
{ 0x64, KEY_LAST }, /* +100 */
{ 0x4e, KEY_AGAIN }, /* Recall */
- { 0x6c, KEY_SWITCHVIDEOMODE }, /* Video Source */
+ { 0x6c, KEY_VIDEO }, /* Video Source */
{ 0x5e, KEY_MENU },
{ 0x56, KEY_SCREEN },
{ 0x7a, KEY_SETUP },
@@ -87,4 +88,4 @@ module_init(init_rc_map_encore_enltv2)
module_exit(exit_rc_map_encore_enltv2)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-evga-indtube.c b/drivers/media/rc/keymaps/rc-evga-indtube.c
index a2bf24f6dfb..2370d2a3deb 100644
--- a/drivers/media/rc/keymaps/rc-evga-indtube.c
+++ b/drivers/media/rc/keymaps/rc-evga-indtube.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* EVGA inDtube
Devin Heitmueller <devin.heitmueller@gmail.com>
@@ -58,4 +59,4 @@ module_init(init_rc_map_evga_indtube)
module_exit(exit_rc_map_evga_indtube)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-eztv.c b/drivers/media/rc/keymaps/rc-eztv.c
index 1e8e5b2d6e3..b5c96ed8437 100644
--- a/drivers/media/rc/keymaps/rc-eztv.c
+++ b/drivers/media/rc/keymaps/rc-eztv.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Alfons Geser <a.geser@cox.net>
* updates from Job D. R. Borges <jobdrb@ig.com.br> */
@@ -93,4 +94,4 @@ module_init(init_rc_map_eztv)
module_exit(exit_rc_map_eztv)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-flydvb.c b/drivers/media/rc/keymaps/rc-flydvb.c
index aea2f4acf7d..25cb89fac03 100644
--- a/drivers/media/rc/keymaps/rc-flydvb.c
+++ b/drivers/media/rc/keymaps/rc-flydvb.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table flydvb[] = {
{ 0x01, KEY_ZOOM }, /* Full Screen */
@@ -37,8 +38,8 @@ static struct rc_map_table flydvb[] = {
{ 0x13, KEY_CHANNELDOWN }, /* CH- */
{ 0x1d, KEY_ENTER }, /* Enter */
- { 0x1a, KEY_MODE }, /* PIP */
- { 0x18, KEY_TUNER }, /* Source */
+ { 0x1a, KEY_TV2 }, /* PIP */
+ { 0x18, KEY_VIDEO }, /* Source */
{ 0x1e, KEY_RECORD }, /* Record/Pause */
{ 0x15, KEY_ANGLE }, /* Swap (no label on key) */
@@ -74,4 +75,4 @@ module_init(init_rc_map_flydvb)
module_exit(exit_rc_map_flydvb)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-flyvideo.c b/drivers/media/rc/keymaps/rc-flyvideo.c
index 5bbe6837175..e71377dd053 100644
--- a/drivers/media/rc/keymaps/rc-flyvideo.c
+++ b/drivers/media/rc/keymaps/rc-flyvideo.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table flyvideo[] = {
{ 0x0f, KEY_0 },
@@ -67,4 +68,4 @@ module_init(init_rc_map_flyvideo)
module_exit(exit_rc_map_flyvideo)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-fusionhdtv-mce.c b/drivers/media/rc/keymaps/rc-fusionhdtv-mce.c
index c80b25c65b5..cf0608dc83d 100644
--- a/drivers/media/rc/keymaps/rc-fusionhdtv-mce.c
+++ b/drivers/media/rc/keymaps/rc-fusionhdtv-mce.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* DViCO FUSION HDTV MCE remote */
@@ -95,4 +96,4 @@ module_init(init_rc_map_fusionhdtv_mce)
module_exit(exit_rc_map_fusionhdtv_mce)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-gadmei-rm008z.c b/drivers/media/rc/keymaps/rc-gadmei-rm008z.c
index 068c9ead98d..03575bdb2ec 100644
--- a/drivers/media/rc/keymaps/rc-gadmei-rm008z.c
+++ b/drivers/media/rc/keymaps/rc-gadmei-rm008z.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* GADMEI UTV330+ RM008Z remote
Shine Liu <shinel@foxmail.com>
@@ -78,4 +79,4 @@ module_init(init_rc_map_gadmei_rm008z)
module_exit(exit_rc_map_gadmei_rm008z)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-genius-tvgo-a11mce.c b/drivers/media/rc/keymaps/rc-genius-tvgo-a11mce.c
index cdbbed46792..b2ab13b0dcb 100644
--- a/drivers/media/rc/keymaps/rc-genius-tvgo-a11mce.c
+++ b/drivers/media/rc/keymaps/rc-genius-tvgo-a11mce.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/*
* Remote control for the Genius TVGO A11MCE
@@ -81,4 +82,4 @@ module_init(init_rc_map_genius_tvgo_a11mce)
module_exit(exit_rc_map_genius_tvgo_a11mce)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-gotview7135.c b/drivers/media/rc/keymaps/rc-gotview7135.c
index a38bdde8c14..229a36ac7f0 100644
--- a/drivers/media/rc/keymaps/rc-gotview7135.c
+++ b/drivers/media/rc/keymaps/rc-gotview7135.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Mike Baikov <mike@baikov.com> */
@@ -76,4 +77,4 @@ module_init(init_rc_map_gotview7135)
module_exit(exit_rc_map_gotview7135)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-hauppauge-new.c b/drivers/media/rc/keymaps/rc-hauppauge-new.c
deleted file mode 100644
index bd11da46e56..00000000000
--- a/drivers/media/rc/keymaps/rc-hauppauge-new.c
+++ /dev/null
@@ -1,100 +0,0 @@
-/* hauppauge-new.h - Keytable for hauppauge_new Remote Controller
- *
- * keymap imported from ir-keymaps.c
- *
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
- *
- * 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.
- */
-
-#include <media/rc-map.h>
-
-/* Hauppauge: the newer, gray remotes (seems there are multiple
- * slightly different versions), shipped with cx88+ivtv cards.
- * almost rc5 coding, but some non-standard keys */
-
-static struct rc_map_table hauppauge_new[] = {
- /* Keys 0 to 9 */
- { 0x00, KEY_0 },
- { 0x01, KEY_1 },
- { 0x02, KEY_2 },
- { 0x03, KEY_3 },
- { 0x04, KEY_4 },
- { 0x05, KEY_5 },
- { 0x06, KEY_6 },
- { 0x07, KEY_7 },
- { 0x08, KEY_8 },
- { 0x09, KEY_9 },
-
- { 0x0a, KEY_TEXT }, /* keypad asterisk as well */
- { 0x0b, KEY_RED }, /* red button */
- { 0x0c, KEY_RADIO },
- { 0x0d, KEY_MENU },
- { 0x0e, KEY_SUBTITLE }, /* also the # key */
- { 0x0f, KEY_MUTE },
- { 0x10, KEY_VOLUMEUP },
- { 0x11, KEY_VOLUMEDOWN },
- { 0x12, KEY_PREVIOUS }, /* previous channel */
- { 0x14, KEY_UP },
- { 0x15, KEY_DOWN },
- { 0x16, KEY_LEFT },
- { 0x17, KEY_RIGHT },
- { 0x18, KEY_VIDEO }, /* Videos */
- { 0x19, KEY_AUDIO }, /* Music */
- /* 0x1a: Pictures - presume this means
- "Multimedia Home Platform" -
- no "PICTURES" key in input.h
- */
- { 0x1a, KEY_MHP },
-
- { 0x1b, KEY_EPG }, /* Guide */
- { 0x1c, KEY_TV },
- { 0x1e, KEY_NEXTSONG }, /* skip >| */
- { 0x1f, KEY_EXIT }, /* back/exit */
- { 0x20, KEY_CHANNELUP }, /* channel / program + */
- { 0x21, KEY_CHANNELDOWN }, /* channel / program - */
- { 0x22, KEY_CHANNEL }, /* source (old black remote) */
- { 0x24, KEY_PREVIOUSSONG }, /* replay |< */
- { 0x25, KEY_ENTER }, /* OK */
- { 0x26, KEY_SLEEP }, /* minimize (old black remote) */
- { 0x29, KEY_BLUE }, /* blue key */
- { 0x2e, KEY_GREEN }, /* green button */
- { 0x30, KEY_PAUSE }, /* pause */
- { 0x32, KEY_REWIND }, /* backward << */
- { 0x34, KEY_FASTFORWARD }, /* forward >> */
- { 0x35, KEY_PLAY },
- { 0x36, KEY_STOP },
- { 0x37, KEY_RECORD }, /* recording */
- { 0x38, KEY_YELLOW }, /* yellow key */
- { 0x3b, KEY_SELECT }, /* top right button */
- { 0x3c, KEY_ZOOM }, /* full */
- { 0x3d, KEY_POWER }, /* system power (green button) */
-};
-
-static struct rc_map_list hauppauge_new_map = {
- .map = {
- .scan = hauppauge_new,
- .size = ARRAY_SIZE(hauppauge_new),
- .rc_type = RC_TYPE_UNKNOWN, /* Legacy IR type */
- .name = RC_MAP_HAUPPAUGE_NEW,
- }
-};
-
-static int __init init_rc_map_hauppauge_new(void)
-{
- return rc_map_register(&hauppauge_new_map);
-}
-
-static void __exit exit_rc_map_hauppauge_new(void)
-{
- rc_map_unregister(&hauppauge_new_map);
-}
-
-module_init(init_rc_map_hauppauge_new)
-module_exit(exit_rc_map_hauppauge_new)
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
diff --git a/drivers/media/rc/keymaps/rc-hauppauge.c b/drivers/media/rc/keymaps/rc-hauppauge.c
new file mode 100644
index 00000000000..36d57f7c532
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-hauppauge.c
@@ -0,0 +1,293 @@
+/* rc-hauppauge.c - Keytable for Hauppauge Remote Controllers
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * This map currently contains the code for four different RCs:
+ * - New Hauppauge Gray;
+ * - Old Hauppauge Gray (with a golden screen for media keys);
+ * - Hauppauge Black;
+ * - DSR-0112 remote bundled with Haupauge MiniStick.
+ *
+ * Copyright (c) 2010-2011 by Mauro Carvalho Chehab
+ *
+ * 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.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/*
+ * Hauppauge:the newer, gray remotes (seems there are multiple
+ * slightly different versions), shipped with cx88+ivtv cards.
+ *
+ * This table contains the complete RC5 code, instead of just the data part
+ */
+
+static struct rc_map_table rc5_hauppauge_new[] = {
+ /*
+ * Remote Controller Hauppauge Gray found on modern devices
+ * Keycodes start with address = 0x1e
+ */
+
+ { 0x1e3b, KEY_SELECT }, /* GO / house symbol */
+ { 0x1e3d, KEY_POWER2 }, /* system power (green button) */
+
+ { 0x1e1c, KEY_TV },
+ { 0x1e18, KEY_VIDEO }, /* Videos */
+ { 0x1e19, KEY_AUDIO }, /* Music */
+ { 0x1e1a, KEY_CAMERA }, /* Pictures */
+
+ { 0x1e1b, KEY_EPG }, /* Guide */
+ { 0x1e0c, KEY_RADIO },
+
+ { 0x1e14, KEY_UP },
+ { 0x1e15, KEY_DOWN },
+ { 0x1e16, KEY_LEFT },
+ { 0x1e17, KEY_RIGHT },
+ { 0x1e25, KEY_OK }, /* OK */
+
+ { 0x1e1f, KEY_EXIT }, /* back/exit */
+ { 0x1e0d, KEY_MENU },
+
+ { 0x1e10, KEY_VOLUMEUP },
+ { 0x1e11, KEY_VOLUMEDOWN },
+
+ { 0x1e12, KEY_PREVIOUS }, /* previous channel */
+ { 0x1e0f, KEY_MUTE },
+
+ { 0x1e20, KEY_CHANNELUP }, /* channel / program + */
+ { 0x1e21, KEY_CHANNELDOWN }, /* channel / program - */
+
+ { 0x1e37, KEY_RECORD }, /* recording */
+ { 0x1e36, KEY_STOP },
+
+ { 0x1e32, KEY_REWIND }, /* backward << */
+ { 0x1e35, KEY_PLAY },
+ { 0x1e34, KEY_FASTFORWARD }, /* forward >> */
+
+ { 0x1e24, KEY_PREVIOUSSONG }, /* replay |< */
+ { 0x1e30, KEY_PAUSE }, /* pause */
+ { 0x1e1e, KEY_NEXTSONG }, /* skip >| */
+
+ { 0x1e01, KEY_1 },
+ { 0x1e02, KEY_2 },
+ { 0x1e03, KEY_3 },
+
+ { 0x1e04, KEY_4 },
+ { 0x1e05, KEY_5 },
+ { 0x1e06, KEY_6 },
+
+ { 0x1e07, KEY_7 },
+ { 0x1e08, KEY_8 },
+ { 0x1e09, KEY_9 },
+
+ { 0x1e0a, KEY_TEXT }, /* keypad asterisk as well */
+ { 0x1e00, KEY_0 },
+ { 0x1e0e, KEY_SUBTITLE }, /* also the Pound key (#) */
+
+ { 0x1e0b, KEY_RED }, /* red button */
+ { 0x1e2e, KEY_GREEN }, /* green button */
+ { 0x1e38, KEY_YELLOW }, /* yellow key */
+ { 0x1e29, KEY_BLUE }, /* blue key */
+
+ /*
+ * Old Remote Controller Hauppauge Gray with a golden screen
+ * Keycodes start with address = 0x1f
+ */
+ { 0x1f3d, KEY_POWER2 }, /* system power (green button) */
+ { 0x1f3b, KEY_SELECT }, /* GO */
+
+ /* Keys 0 to 9 */
+ { 0x1f00, KEY_0 },
+ { 0x1f01, KEY_1 },
+ { 0x1f02, KEY_2 },
+ { 0x1f03, KEY_3 },
+ { 0x1f04, KEY_4 },
+ { 0x1f05, KEY_5 },
+ { 0x1f06, KEY_6 },
+ { 0x1f07, KEY_7 },
+ { 0x1f08, KEY_8 },
+ { 0x1f09, KEY_9 },
+
+ { 0x1f1f, KEY_EXIT }, /* back/exit */
+ { 0x1f0d, KEY_MENU },
+
+ { 0x1f10, KEY_VOLUMEUP },
+ { 0x1f11, KEY_VOLUMEDOWN },
+ { 0x1f20, KEY_CHANNELUP }, /* channel / program + */
+ { 0x1f21, KEY_CHANNELDOWN }, /* channel / program - */
+ { 0x1f25, KEY_ENTER }, /* OK */
+
+ { 0x1f0b, KEY_RED }, /* red button */
+ { 0x1f2e, KEY_GREEN }, /* green button */
+ { 0x1f38, KEY_YELLOW }, /* yellow key */
+ { 0x1f29, KEY_BLUE }, /* blue key */
+
+ { 0x1f0f, KEY_MUTE },
+ { 0x1f0c, KEY_RADIO }, /* There's no indicator on this key */
+ { 0x1f3c, KEY_ZOOM }, /* full */
+
+ { 0x1f32, KEY_REWIND }, /* backward << */
+ { 0x1f35, KEY_PLAY },
+ { 0x1f34, KEY_FASTFORWARD }, /* forward >> */
+
+ { 0x1f37, KEY_RECORD }, /* recording */
+ { 0x1f36, KEY_STOP },
+ { 0x1f30, KEY_PAUSE }, /* pause */
+
+ { 0x1f24, KEY_PREVIOUSSONG }, /* replay |< */
+ { 0x1f1e, KEY_NEXTSONG }, /* skip >| */
+
+ /*
+ * Keycodes for DSR-0112 remote bundled with Haupauge MiniStick
+ * Keycodes start with address = 0x1d
+ */
+ { 0x1d00, KEY_0 },
+ { 0x1d01, KEY_1 },
+ { 0x1d02, KEY_2 },
+ { 0x1d03, KEY_3 },
+ { 0x1d04, KEY_4 },
+ { 0x1d05, KEY_5 },
+ { 0x1d06, KEY_6 },
+ { 0x1d07, KEY_7 },
+ { 0x1d08, KEY_8 },
+ { 0x1d09, KEY_9 },
+ { 0x1d0a, KEY_TEXT },
+ { 0x1d0d, KEY_MENU },
+ { 0x1d0f, KEY_MUTE },
+ { 0x1d10, KEY_VOLUMEUP },
+ { 0x1d11, KEY_VOLUMEDOWN },
+ { 0x1d12, KEY_PREVIOUS }, /* Prev.Ch .. ??? */
+ { 0x1d14, KEY_UP },
+ { 0x1d15, KEY_DOWN },
+ { 0x1d16, KEY_LEFT },
+ { 0x1d17, KEY_RIGHT },
+ { 0x1d1c, KEY_TV },
+ { 0x1d1e, KEY_NEXT }, /* >| */
+ { 0x1d1f, KEY_EXIT },
+ { 0x1d20, KEY_CHANNELUP },
+ { 0x1d21, KEY_CHANNELDOWN },
+ { 0x1d24, KEY_LAST }, /* <| */
+ { 0x1d25, KEY_OK },
+ { 0x1d30, KEY_PAUSE },
+ { 0x1d32, KEY_REWIND },
+ { 0x1d34, KEY_FASTFORWARD },
+ { 0x1d35, KEY_PLAY },
+ { 0x1d36, KEY_STOP },
+ { 0x1d37, KEY_RECORD },
+ { 0x1d3b, KEY_GOTO },
+ { 0x1d3d, KEY_POWER },
+ { 0x1d3f, KEY_HOME },
+
+ /*
+ * Keycodes for PT# R-005 remote bundled with Haupauge HVR-930C
+ * Keycodes start with address = 0x1c
+ */
+ { 0x1c3b, KEY_GOTO },
+ { 0x1c3d, KEY_POWER },
+
+ { 0x1c14, KEY_UP },
+ { 0x1c15, KEY_DOWN },
+ { 0x1c16, KEY_LEFT },
+ { 0x1c17, KEY_RIGHT },
+ { 0x1c25, KEY_OK },
+
+ { 0x1c00, KEY_0 },
+ { 0x1c01, KEY_1 },
+ { 0x1c02, KEY_2 },
+ { 0x1c03, KEY_3 },
+ { 0x1c04, KEY_4 },
+ { 0x1c05, KEY_5 },
+ { 0x1c06, KEY_6 },
+ { 0x1c07, KEY_7 },
+ { 0x1c08, KEY_8 },
+ { 0x1c09, KEY_9 },
+
+ { 0x1c1f, KEY_EXIT }, /* BACK */
+ { 0x1c0d, KEY_MENU },
+ { 0x1c1c, KEY_TV },
+
+ { 0x1c10, KEY_VOLUMEUP },
+ { 0x1c11, KEY_VOLUMEDOWN },
+
+ { 0x1c20, KEY_CHANNELUP },
+ { 0x1c21, KEY_CHANNELDOWN },
+
+ { 0x1c0f, KEY_MUTE },
+ { 0x1c12, KEY_PREVIOUS }, /* Prev */
+
+ { 0x1c36, KEY_STOP },
+ { 0x1c37, KEY_RECORD },
+
+ { 0x1c24, KEY_LAST }, /* <| */
+ { 0x1c1e, KEY_NEXT }, /* >| */
+
+ { 0x1c0a, KEY_TEXT },
+ { 0x1c0e, KEY_SUBTITLE }, /* CC */
+
+ { 0x1c32, KEY_REWIND },
+ { 0x1c30, KEY_PAUSE },
+ { 0x1c35, KEY_PLAY },
+ { 0x1c34, KEY_FASTFORWARD },
+
+ /*
+ * Keycodes for the old Black Remote Controller
+ * This one also uses RC-5 protocol
+ * Keycodes start with address = 0x00
+ */
+ { 0x001f, KEY_TV },
+ { 0x0020, KEY_CHANNELUP },
+ { 0x000c, KEY_RADIO },
+
+ { 0x0011, KEY_VOLUMEDOWN },
+ { 0x002e, KEY_ZOOM }, /* full screen */
+ { 0x0010, KEY_VOLUMEUP },
+
+ { 0x000d, KEY_MUTE },
+ { 0x0021, KEY_CHANNELDOWN },
+ { 0x0022, KEY_VIDEO }, /* source */
+
+ { 0x0001, KEY_1 },
+ { 0x0002, KEY_2 },
+ { 0x0003, KEY_3 },
+
+ { 0x0004, KEY_4 },
+ { 0x0005, KEY_5 },
+ { 0x0006, KEY_6 },
+
+ { 0x0007, KEY_7 },
+ { 0x0008, KEY_8 },
+ { 0x0009, KEY_9 },
+
+ { 0x001e, KEY_RED }, /* Reserved */
+ { 0x0000, KEY_0 },
+ { 0x0026, KEY_SLEEP }, /* Minimize */
+};
+
+static struct rc_map_list rc5_hauppauge_new_map = {
+ .map = {
+ .scan = rc5_hauppauge_new,
+ .size = ARRAY_SIZE(rc5_hauppauge_new),
+ .rc_type = RC_TYPE_RC5,
+ .name = RC_MAP_HAUPPAUGE,
+ }
+};
+
+static int __init init_rc_map_rc5_hauppauge_new(void)
+{
+ return rc_map_register(&rc5_hauppauge_new_map);
+}
+
+static void __exit exit_rc_map_rc5_hauppauge_new(void)
+{
+ rc_map_unregister(&rc5_hauppauge_new_map);
+}
+
+module_init(init_rc_map_rc5_hauppauge_new)
+module_exit(exit_rc_map_rc5_hauppauge_new)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-imon-mce.c b/drivers/media/rc/keymaps/rc-imon-mce.c
index cb67184e015..f0da960560b 100644
--- a/drivers/media/rc/keymaps/rc-imon-mce.c
+++ b/drivers/media/rc/keymaps/rc-imon-mce.c
@@ -10,6 +10,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* mce-mode imon mce remote key table */
static struct rc_map_table imon_mce[] = {
@@ -111,7 +112,7 @@ static struct rc_map_table imon_mce[] = {
{ 0x800ff44d, KEY_TITLE },
{ 0x800ff40c, KEY_POWER },
- { 0x800ff40d, KEY_PROG1 }, /* Windows MCE button */
+ { 0x800ff40d, KEY_MEDIA }, /* Windows MCE button */
};
@@ -120,7 +121,7 @@ static struct rc_map_list imon_mce_map = {
.scan = imon_mce,
.size = ARRAY_SIZE(imon_mce),
/* its RC6, but w/a hardware decoder */
- .rc_type = RC_TYPE_RC6,
+ .rc_type = RC_TYPE_RC6_MCE,
.name = RC_MAP_IMON_MCE,
}
};
diff --git a/drivers/media/rc/keymaps/rc-imon-pad.c b/drivers/media/rc/keymaps/rc-imon-pad.c
index eef46b73ca7..999c6295c70 100644
--- a/drivers/media/rc/keymaps/rc-imon-pad.c
+++ b/drivers/media/rc/keymaps/rc-imon-pad.c
@@ -10,6 +10,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/*
* standard imon remote key table, which isn't really entirely
@@ -87,7 +88,7 @@ static struct rc_map_table imon_pad[] = {
{ 0x2b8515b7, KEY_VIDEO },
{ 0x299195b7, KEY_AUDIO },
- { 0x2ba115b7, KEY_CAMERA },
+ { 0x2ba115b7, KEY_IMAGES },
{ 0x28a515b7, KEY_TV },
{ 0x29a395b7, KEY_DVD },
{ 0x29a295b7, KEY_DVD },
@@ -97,7 +98,7 @@ static struct rc_map_table imon_pad[] = {
{ 0x2ba395b7, KEY_MENU },
{ 0x288515b7, KEY_BOOKMARKS },
- { 0x2ab715b7, KEY_MEDIA }, /* Thumbnail */
+ { 0x2ab715b7, KEY_CAMERA }, /* Thumbnail */
{ 0x298595b7, KEY_SUBTITLE },
{ 0x2b8595b7, KEY_LANGUAGE },
@@ -125,7 +126,7 @@ static struct rc_map_table imon_pad[] = {
{ 0x2b8195b7, KEY_CONTEXT_MENU }, /* Left Menu*/
{ 0x02000065, KEY_COMPOSE }, /* RightMenu */
{ 0x28b715b7, KEY_COMPOSE }, /* RightMenu */
- { 0x2ab195b7, KEY_PROG1 }, /* Go or MultiMon */
+ { 0x2ab195b7, KEY_MEDIA }, /* Go or MultiMon */
{ 0x29b715b7, KEY_DASHBOARD }, /* AppLauncher */
};
diff --git a/drivers/media/rc/keymaps/rc-iodata-bctv7e.c b/drivers/media/rc/keymaps/rc-iodata-bctv7e.c
index 1f59e163f75..9ee154cb0c6 100644
--- a/drivers/media/rc/keymaps/rc-iodata-bctv7e.c
+++ b/drivers/media/rc/keymaps/rc-iodata-bctv7e.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* IO-DATA BCTV7E Remote */
@@ -85,4 +86,4 @@ module_init(init_rc_map_iodata_bctv7e)
module_exit(exit_rc_map_iodata_bctv7e)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-it913x-v1.c b/drivers/media/rc/keymaps/rc-it913x-v1.c
new file mode 100644
index 00000000000..0ac775fd109
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-it913x-v1.c
@@ -0,0 +1,95 @@
+/* ITE Generic remotes Version 1
+ *
+ * Copyright (C) 2012 Malcolm Priestley (tvboxspy@gmail.com)
+ *
+ * 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.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+
+static struct rc_map_table it913x_v1_rc[] = {
+ /* Type 1 */
+ { 0x61d601, KEY_VIDEO }, /* Source */
+ { 0x61d602, KEY_3 },
+ { 0x61d603, KEY_POWER }, /* ShutDown */
+ { 0x61d604, KEY_1 },
+ { 0x61d605, KEY_5 },
+ { 0x61d606, KEY_6 },
+ { 0x61d607, KEY_CHANNELDOWN }, /* CH- */
+ { 0x61d608, KEY_2 },
+ { 0x61d609, KEY_CHANNELUP }, /* CH+ */
+ { 0x61d60a, KEY_9 },
+ { 0x61d60b, KEY_ZOOM }, /* Zoom */
+ { 0x61d60c, KEY_7 },
+ { 0x61d60d, KEY_8 },
+ { 0x61d60e, KEY_VOLUMEUP }, /* Vol+ */
+ { 0x61d60f, KEY_4 },
+ { 0x61d610, KEY_ESC }, /* [back up arrow] */
+ { 0x61d611, KEY_0 },
+ { 0x61d612, KEY_OK }, /* [enter arrow] */
+ { 0x61d613, KEY_VOLUMEDOWN }, /* Vol- */
+ { 0x61d614, KEY_RECORD }, /* Rec */
+ { 0x61d615, KEY_STOP }, /* Stop */
+ { 0x61d616, KEY_PLAY }, /* Play */
+ { 0x61d617, KEY_MUTE }, /* Mute */
+ { 0x61d618, KEY_UP },
+ { 0x61d619, KEY_DOWN },
+ { 0x61d61a, KEY_LEFT },
+ { 0x61d61b, KEY_RIGHT },
+ { 0x61d61c, KEY_RED },
+ { 0x61d61d, KEY_GREEN },
+ { 0x61d61e, KEY_YELLOW },
+ { 0x61d61f, KEY_BLUE },
+ { 0x61d643, KEY_POWER2 }, /* [red power button] */
+ /* Type 2 - 20 buttons */
+ { 0x807f0d, KEY_0 },
+ { 0x807f04, KEY_1 },
+ { 0x807f05, KEY_2 },
+ { 0x807f06, KEY_3 },
+ { 0x807f07, KEY_4 },
+ { 0x807f08, KEY_5 },
+ { 0x807f09, KEY_6 },
+ { 0x807f0a, KEY_7 },
+ { 0x807f1b, KEY_8 },
+ { 0x807f1f, KEY_9 },
+ { 0x807f12, KEY_POWER },
+ { 0x807f01, KEY_MEDIA_REPEAT}, /* Recall */
+ { 0x807f19, KEY_PAUSE }, /* Timeshift */
+ { 0x807f1e, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */
+ { 0x807f03, KEY_VOLUMEDOWN }, /* Volume defined as right hand*/
+ { 0x807f1a, KEY_CHANNELUP },
+ { 0x807f02, KEY_CHANNELDOWN },
+ { 0x807f0c, KEY_ZOOM },
+ { 0x807f00, KEY_RECORD },
+ { 0x807f0e, KEY_STOP },
+};
+
+static struct rc_map_list it913x_v1_map = {
+ .map = {
+ .scan = it913x_v1_rc,
+ .size = ARRAY_SIZE(it913x_v1_rc),
+ .rc_type = RC_TYPE_NEC,
+ .name = RC_MAP_IT913X_V1,
+ }
+};
+
+static int __init init_rc_it913x_v1_map(void)
+{
+ return rc_map_register(&it913x_v1_map);
+}
+
+static void __exit exit_rc_it913x_v1_map(void)
+{
+ rc_map_unregister(&it913x_v1_map);
+}
+
+module_init(init_rc_it913x_v1_map)
+module_exit(exit_rc_it913x_v1_map)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com");
diff --git a/drivers/media/rc/keymaps/rc-it913x-v2.c b/drivers/media/rc/keymaps/rc-it913x-v2.c
new file mode 100644
index 00000000000..bd42a30ec06
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-it913x-v2.c
@@ -0,0 +1,94 @@
+/* ITE Generic remotes Version 2
+ *
+ * Copyright (C) 2012 Malcolm Priestley (tvboxspy@gmail.com)
+ *
+ * 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.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+
+static struct rc_map_table it913x_v2_rc[] = {
+ /* Type 1 */
+ /* 9005 remote */
+ { 0x807f12, KEY_POWER2 }, /* Power (RED POWER BUTTON)*/
+ { 0x807f1a, KEY_VIDEO }, /* Source */
+ { 0x807f1e, KEY_MUTE }, /* Mute */
+ { 0x807f01, KEY_RECORD }, /* Record */
+ { 0x807f02, KEY_CHANNELUP }, /* Channel+ */
+ { 0x807f03, KEY_TIME }, /* TimeShift */
+ { 0x807f04, KEY_VOLUMEUP }, /* Volume- */
+ { 0x807f05, KEY_SCREEN }, /* FullScreen */
+ { 0x807f06, KEY_VOLUMEDOWN }, /* Volume- */
+ { 0x807f07, KEY_0 }, /* 0 */
+ { 0x807f08, KEY_CHANNELDOWN }, /* Channel- */
+ { 0x807f09, KEY_PREVIOUS }, /* Recall */
+ { 0x807f0a, KEY_1 }, /* 1 */
+ { 0x807f1b, KEY_2 }, /* 2 */
+ { 0x807f1f, KEY_3 }, /* 3 */
+ { 0x807f0c, KEY_4 }, /* 4 */
+ { 0x807f0d, KEY_5 }, /* 5 */
+ { 0x807f0e, KEY_6 }, /* 6 */
+ { 0x807f00, KEY_7 }, /* 7 */
+ { 0x807f0f, KEY_8 }, /* 8 */
+ { 0x807f19, KEY_9 }, /* 9 */
+
+ /* Type 2 */
+ /* keys stereo, snapshot unassigned */
+ { 0x866b00, KEY_0 },
+ { 0x866b01, KEY_1 },
+ { 0x866b02, KEY_2 },
+ { 0x866b03, KEY_3 },
+ { 0x866b04, KEY_4 },
+ { 0x866b05, KEY_5 },
+ { 0x866b06, KEY_6 },
+ { 0x866b07, KEY_7 },
+ { 0x866b08, KEY_8 },
+ { 0x866b09, KEY_9 },
+ { 0x866b12, KEY_POWER },
+ { 0x866b13, KEY_MUTE },
+ { 0x866b0a, KEY_PREVIOUS }, /* Recall */
+ { 0x866b1e, KEY_PAUSE },
+ { 0x866b0c, KEY_VOLUMEUP },
+ { 0x866b18, KEY_VOLUMEDOWN },
+ { 0x866b0b, KEY_CHANNELUP },
+ { 0x866b18, KEY_CHANNELDOWN },
+ { 0x866b10, KEY_ZOOM },
+ { 0x866b1d, KEY_RECORD },
+ { 0x866b0e, KEY_STOP },
+ { 0x866b11, KEY_EPG},
+ { 0x866b1a, KEY_FASTFORWARD },
+ { 0x866b0f, KEY_REWIND },
+ { 0x866b1c, KEY_TV },
+ { 0x866b1b, KEY_TEXT },
+
+};
+
+static struct rc_map_list it913x_v2_map = {
+ .map = {
+ .scan = it913x_v2_rc,
+ .size = ARRAY_SIZE(it913x_v2_rc),
+ .rc_type = RC_TYPE_NEC,
+ .name = RC_MAP_IT913X_V2,
+ }
+};
+
+static int __init init_rc_it913x_v2_map(void)
+{
+ return rc_map_register(&it913x_v2_map);
+}
+
+static void __exit exit_rc_it913x_v2_map(void)
+{
+ rc_map_unregister(&it913x_v2_map);
+}
+
+module_init(init_rc_it913x_v2_map)
+module_exit(exit_rc_it913x_v2_map)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com");
diff --git a/drivers/media/rc/keymaps/rc-kaiomy.c b/drivers/media/rc/keymaps/rc-kaiomy.c
index f31dc5c1ad9..60803a732c0 100644
--- a/drivers/media/rc/keymaps/rc-kaiomy.c
+++ b/drivers/media/rc/keymaps/rc-kaiomy.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Kaiomy TVnPC U2
Mauro Carvalho Chehab <mchehab@infradead.org>
@@ -84,4 +85,4 @@ module_init(init_rc_map_kaiomy)
module_exit(exit_rc_map_kaiomy)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-kworld-315u.c b/drivers/media/rc/keymaps/rc-kworld-315u.c
index 3ce6ef79fc3..ba087eed1ed 100644
--- a/drivers/media/rc/keymaps/rc-kworld-315u.c
+++ b/drivers/media/rc/keymaps/rc-kworld-315u.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,13 +11,14 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Kworld 315U
*/
static struct rc_map_table kworld_315u[] = {
{ 0x6143, KEY_POWER },
- { 0x6101, KEY_TUNER }, /* source */
+ { 0x6101, KEY_VIDEO }, /* source */
{ 0x610b, KEY_ZOOM },
{ 0x6103, KEY_POWER2 }, /* shutdown */
@@ -80,4 +81,4 @@ module_init(init_rc_map_kworld_315u)
module_exit(exit_rc_map_kworld_315u)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-kworld-pc150u.c b/drivers/media/rc/keymaps/rc-kworld-pc150u.c
new file mode 100644
index 00000000000..b92e571f4de
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-kworld-pc150u.c
@@ -0,0 +1,102 @@
+/* kworld-pc150u.c - Keytable for kworld_pc150u Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Kyle Strickland
+ * (based on kworld-plus-tv-analog.c by
+ * Mauro Carvalho Chehab)
+ *
+ * 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.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* Kworld PC150-U
+ Kyle Strickland <kyle@kyle.strickland.name>
+ */
+
+static struct rc_map_table kworld_pc150u[] = {
+ { 0x0c, KEY_MEDIA }, /* Kworld key */
+ { 0x16, KEY_EJECTCLOSECD }, /* -> ) */
+ { 0x1d, KEY_POWER2 },
+
+ { 0x00, KEY_1 },
+ { 0x01, KEY_2 },
+ { 0x02, KEY_3 },
+ { 0x03, KEY_4 },
+ { 0x04, KEY_5 },
+ { 0x05, KEY_6 },
+ { 0x06, KEY_7 },
+ { 0x07, KEY_8 },
+ { 0x08, KEY_9 },
+ { 0x0a, KEY_0 },
+
+ { 0x09, KEY_AGAIN },
+ { 0x14, KEY_MUTE },
+
+ { 0x1e, KEY_LAST },
+ { 0x17, KEY_ZOOM },
+ { 0x1f, KEY_HOMEPAGE },
+ { 0x0e, KEY_ESC },
+
+ { 0x20, KEY_UP },
+ { 0x21, KEY_DOWN },
+ { 0x42, KEY_LEFT },
+ { 0x43, KEY_RIGHT },
+ { 0x0b, KEY_ENTER },
+
+ { 0x10, KEY_CHANNELUP },
+ { 0x11, KEY_CHANNELDOWN },
+
+ { 0x13, KEY_VOLUMEUP },
+ { 0x12, KEY_VOLUMEDOWN },
+
+ { 0x19, KEY_TIME}, /* Timeshift */
+ { 0x1a, KEY_STOP},
+ { 0x1b, KEY_RECORD},
+ { 0x4b, KEY_EMAIL},
+
+ { 0x40, KEY_REWIND},
+ { 0x44, KEY_PLAYPAUSE},
+ { 0x41, KEY_FORWARD},
+ { 0x22, KEY_TEXT},
+
+ { 0x15, KEY_AUDIO}, /* ((*)) */
+ { 0x0f, KEY_MODE}, /* display ratio */
+ { 0x1c, KEY_SYSRQ}, /* snapshot */
+ { 0x4a, KEY_SLEEP}, /* sleep timer */
+
+ { 0x48, KEY_SOUND}, /* switch theater mode */
+ { 0x49, KEY_BLUE}, /* A */
+ { 0x18, KEY_RED}, /* B */
+ { 0x23, KEY_GREEN}, /* C */
+};
+
+static struct rc_map_list kworld_pc150u_map = {
+ .map = {
+ .scan = kworld_pc150u,
+ .size = ARRAY_SIZE(kworld_pc150u),
+ .rc_type = RC_TYPE_UNKNOWN, /* Legacy IR type */
+ .name = RC_MAP_KWORLD_PC150U,
+ }
+};
+
+static int __init init_rc_map_kworld_pc150u(void)
+{
+ return rc_map_register(&kworld_pc150u_map);
+}
+
+static void __exit exit_rc_map_kworld_pc150u(void)
+{
+ rc_map_unregister(&kworld_pc150u_map);
+}
+
+module_init(init_rc_map_kworld_pc150u)
+module_exit(exit_rc_map_kworld_pc150u)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Kyle Strickland <kyle@kyle.strickland.name>");
diff --git a/drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.c b/drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.c
index e45f0b8759d..edc868564f9 100644
--- a/drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.c
+++ b/drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,13 +11,14 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Kworld Plus TV Analog Lite PCI IR
Mauro Carvalho Chehab <mchehab@infradead.org>
*/
static struct rc_map_table kworld_plus_tv_analog[] = {
- { 0x0c, KEY_PROG1 }, /* Kworld key */
+ { 0x0c, KEY_MEDIA }, /* Kworld key */
{ 0x16, KEY_CLOSECD }, /* -> ) */
{ 0x1d, KEY_POWER2 },
@@ -96,4 +97,4 @@ module_init(init_rc_map_kworld_plus_tv_analog)
module_exit(exit_rc_map_kworld_plus_tv_analog)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-leadtek-y04g0051.c b/drivers/media/rc/keymaps/rc-leadtek-y04g0051.c
index 8faa54ff16e..03d762d986e 100644
--- a/drivers/media/rc/keymaps/rc-leadtek-y04g0051.c
+++ b/drivers/media/rc/keymaps/rc-leadtek-y04g0051.c
@@ -19,6 +19,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table leadtek_y04g0051[] = {
{ 0x0300, KEY_POWER2 },
diff --git a/drivers/media/rc/keymaps/rc-lirc.c b/drivers/media/rc/keymaps/rc-lirc.c
index e8e23e233c3..fbf08fa6f46 100644
--- a/drivers/media/rc/keymaps/rc-lirc.c
+++ b/drivers/media/rc/keymaps/rc-lirc.c
@@ -10,6 +10,7 @@
*/
#include <media/rc-core.h>
+#include <linux/module.h>
static struct rc_map_table lirc[] = {
{ },
diff --git a/drivers/media/rc/keymaps/rc-lme2510.c b/drivers/media/rc/keymaps/rc-lme2510.c
index 875cd81477c..51f18bb50a3 100644
--- a/drivers/media/rc/keymaps/rc-lme2510.c
+++ b/drivers/media/rc/keymaps/rc-lme2510.c
@@ -10,43 +10,86 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table lme2510_rc[] = {
- { 0xba45, KEY_0 },
- { 0xa05f, KEY_1 },
- { 0xaf50, KEY_2 },
- { 0xa25d, KEY_3 },
- { 0xbe41, KEY_4 },
- { 0xf50a, KEY_5 },
- { 0xbd42, KEY_6 },
- { 0xb847, KEY_7 },
- { 0xb649, KEY_8 },
- { 0xfa05, KEY_9 },
- { 0xbc43, KEY_POWER },
- { 0xb946, KEY_SUBTITLE },
- { 0xf906, KEY_PAUSE },
- { 0xfc03, KEY_MEDIA_REPEAT},
- { 0xfd02, KEY_PAUSE },
- { 0xa15e, KEY_VOLUMEUP },
- { 0xa35c, KEY_VOLUMEDOWN },
- { 0xf609, KEY_CHANNELUP },
- { 0xe51a, KEY_CHANNELDOWN },
- { 0xe11e, KEY_PLAY },
- { 0xe41b, KEY_ZOOM },
- { 0xa659, KEY_MUTE },
- { 0xa55a, KEY_TV },
- { 0xe718, KEY_RECORD },
- { 0xf807, KEY_EPG },
- { 0xfe01, KEY_STOP },
-
+ /* Type 1 - 26 buttons */
+ { 0x10ed45, KEY_0 },
+ { 0x10ed5f, KEY_1 },
+ { 0x10ed50, KEY_2 },
+ { 0x10ed5d, KEY_3 },
+ { 0x10ed41, KEY_4 },
+ { 0x10ed0a, KEY_5 },
+ { 0x10ed42, KEY_6 },
+ { 0x10ed47, KEY_7 },
+ { 0x10ed49, KEY_8 },
+ { 0x10ed05, KEY_9 },
+ { 0x10ed43, KEY_POWER },
+ { 0x10ed46, KEY_SUBTITLE },
+ { 0x10ed06, KEY_PAUSE },
+ { 0x10ed03, KEY_MEDIA_REPEAT},
+ { 0x10ed02, KEY_PAUSE },
+ { 0x10ed5e, KEY_VOLUMEUP },
+ { 0x10ed5c, KEY_VOLUMEDOWN },
+ { 0x10ed09, KEY_CHANNELUP },
+ { 0x10ed1a, KEY_CHANNELDOWN },
+ { 0x10ed1e, KEY_PLAY },
+ { 0x10ed1b, KEY_ZOOM },
+ { 0x10ed59, KEY_MUTE },
+ { 0x10ed5a, KEY_TV },
+ { 0x10ed18, KEY_RECORD },
+ { 0x10ed07, KEY_EPG },
+ { 0x10ed01, KEY_STOP },
+ /* Type 2 - 20 buttons */
+ { 0xbf15, KEY_0 },
+ { 0xbf08, KEY_1 },
+ { 0xbf09, KEY_2 },
+ { 0xbf0a, KEY_3 },
+ { 0xbf0c, KEY_4 },
+ { 0xbf0d, KEY_5 },
+ { 0xbf0e, KEY_6 },
+ { 0xbf10, KEY_7 },
+ { 0xbf11, KEY_8 },
+ { 0xbf12, KEY_9 },
+ { 0xbf00, KEY_POWER },
+ { 0xbf04, KEY_MEDIA_REPEAT}, /* Recall */
+ { 0xbf1a, KEY_PAUSE }, /* Timeshift */
+ { 0xbf02, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */
+ { 0xbf06, KEY_VOLUMEDOWN }, /* Volume defined as right hand*/
+ { 0xbf01, KEY_CHANNELUP },
+ { 0xbf05, KEY_CHANNELDOWN },
+ { 0xbf14, KEY_ZOOM },
+ { 0xbf18, KEY_RECORD },
+ { 0xbf16, KEY_STOP },
+ /* Type 3 - 20 buttons */
+ { 0x1c, KEY_0 },
+ { 0x07, KEY_1 },
+ { 0x15, KEY_2 },
+ { 0x09, KEY_3 },
+ { 0x16, KEY_4 },
+ { 0x19, KEY_5 },
+ { 0x0d, KEY_6 },
+ { 0x0c, KEY_7 },
+ { 0x18, KEY_8 },
+ { 0x5e, KEY_9 },
+ { 0x45, KEY_POWER },
+ { 0x44, KEY_MEDIA_REPEAT}, /* Recall */
+ { 0x4a, KEY_PAUSE }, /* Timeshift */
+ { 0x47, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */
+ { 0x43, KEY_VOLUMEDOWN }, /* Volume defined as right hand*/
+ { 0x46, KEY_CHANNELUP },
+ { 0x40, KEY_CHANNELDOWN },
+ { 0x08, KEY_ZOOM },
+ { 0x42, KEY_RECORD },
+ { 0x5a, KEY_STOP },
};
static struct rc_map_list lme2510_map = {
.map = {
.scan = lme2510_rc,
.size = ARRAY_SIZE(lme2510_rc),
- .rc_type = RC_TYPE_UNKNOWN,
+ .rc_type = RC_TYPE_NEC,
.name = RC_MAP_LME2510,
}
};
diff --git a/drivers/media/rc/keymaps/rc-manli.c b/drivers/media/rc/keymaps/rc-manli.c
index 23b2d04e7a9..92424ef2aaa 100644
--- a/drivers/media/rc/keymaps/rc-manli.c
+++ b/drivers/media/rc/keymaps/rc-manli.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Michael Tokarev <mjt@tls.msk.ru>
keytable is used by MANLI MTV00[0x0c] and BeholdTV 40[13] at
@@ -131,4 +132,4 @@ module_init(init_rc_map_manli)
module_exit(exit_rc_map_manli)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-medion-x10-digitainer.c b/drivers/media/rc/keymaps/rc-medion-x10-digitainer.c
new file mode 100644
index 00000000000..966f9b3c71d
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-medion-x10-digitainer.c
@@ -0,0 +1,123 @@
+/*
+ * Medion X10 RF remote keytable (Digitainer variant)
+ *
+ * Copyright (C) 2012 Anssi Hannula <anssi.hannula@iki.fi>
+ *
+ * This keymap is for a variant that has a distinctive scrollwheel instead of
+ * up/down buttons (tested with P/N 40009936 / 20018268), reportedly
+ * originally shipped with Medion Digitainer but now sold separately simply as
+ * an "X10" remote.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/module.h>
+#include <media/rc-map.h>
+
+static struct rc_map_table medion_x10_digitainer[] = {
+ { 0x02, KEY_POWER },
+
+ { 0x2c, KEY_TV },
+ { 0x2d, KEY_VIDEO },
+ { 0x04, KEY_DVD }, /* CD/DVD */
+ { 0x16, KEY_TEXT }, /* "teletext" icon, i.e. a screen with lines */
+ { 0x06, KEY_AUDIO },
+ { 0x2e, KEY_RADIO },
+ { 0x31, KEY_EPG }, /* a screen with an open book */
+ { 0x05, KEY_IMAGES }, /* Photo */
+ { 0x2f, KEY_INFO },
+
+ { 0x78, KEY_UP }, /* scrollwheel up 1 notch */
+ /* 0x79..0x7f: 2-8 notches, driver repeats 0x78 entry */
+
+ { 0x70, KEY_DOWN }, /* scrollwheel down 1 notch */
+ /* 0x71..0x77: 2-8 notches, driver repeats 0x70 entry */
+
+ { 0x19, KEY_MENU },
+ { 0x1d, KEY_LEFT },
+ { 0x1e, KEY_OK }, /* scrollwheel press */
+ { 0x1f, KEY_RIGHT },
+ { 0x20, KEY_BACK },
+
+ { 0x09, KEY_VOLUMEUP },
+ { 0x08, KEY_VOLUMEDOWN },
+ { 0x00, KEY_MUTE },
+
+ { 0x1b, KEY_SELECT }, /* also has "U" rotated 90 degrees CCW */
+
+ { 0x0b, KEY_CHANNELUP },
+ { 0x0c, KEY_CHANNELDOWN },
+ { 0x1c, KEY_LAST },
+
+ { 0x32, KEY_RED }, /* also Audio */
+ { 0x33, KEY_GREEN }, /* also Subtitle */
+ { 0x34, KEY_YELLOW }, /* also Angle */
+ { 0x35, KEY_BLUE }, /* also Title */
+
+ { 0x28, KEY_STOP },
+ { 0x29, KEY_PAUSE },
+ { 0x25, KEY_PLAY },
+ { 0x21, KEY_PREVIOUS },
+ { 0x18, KEY_CAMERA },
+ { 0x23, KEY_NEXT },
+ { 0x24, KEY_REWIND },
+ { 0x27, KEY_RECORD },
+ { 0x26, KEY_FORWARD },
+
+ { 0x0d, KEY_1 },
+ { 0x0e, KEY_2 },
+ { 0x0f, KEY_3 },
+ { 0x10, KEY_4 },
+ { 0x11, KEY_5 },
+ { 0x12, KEY_6 },
+ { 0x13, KEY_7 },
+ { 0x14, KEY_8 },
+ { 0x15, KEY_9 },
+ { 0x17, KEY_0 },
+
+ /* these do not actually exist on this remote, but these scancodes
+ * exist on all other Medion X10 remotes and adding them here allows
+ * such remotes to be adequately usable with this keymap in case
+ * this keymap is wrongly used with them (which is quite possible as
+ * there are lots of different Medion X10 remotes): */
+ { 0x1a, KEY_UP },
+ { 0x22, KEY_DOWN },
+};
+
+static struct rc_map_list medion_x10_digitainer_map = {
+ .map = {
+ .scan = medion_x10_digitainer,
+ .size = ARRAY_SIZE(medion_x10_digitainer),
+ .rc_type = RC_TYPE_OTHER,
+ .name = RC_MAP_MEDION_X10_DIGITAINER,
+ }
+};
+
+static int __init init_rc_map_medion_x10_digitainer(void)
+{
+ return rc_map_register(&medion_x10_digitainer_map);
+}
+
+static void __exit exit_rc_map_medion_x10_digitainer(void)
+{
+ rc_map_unregister(&medion_x10_digitainer_map);
+}
+
+module_init(init_rc_map_medion_x10_digitainer)
+module_exit(exit_rc_map_medion_x10_digitainer)
+
+MODULE_DESCRIPTION("Medion X10 RF remote keytable (Digitainer variant)");
+MODULE_AUTHOR("Anssi Hannula <anssi.hannula@iki.fi>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/rc/keymaps/rc-medion-x10-or2x.c b/drivers/media/rc/keymaps/rc-medion-x10-or2x.c
new file mode 100644
index 00000000000..b077300ecb5
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-medion-x10-or2x.c
@@ -0,0 +1,108 @@
+/*
+ * Medion X10 OR22/OR24 RF remote keytable
+ *
+ * Copyright (C) 2012 Anssi Hannula <anssi.hannula@iki.fi>
+ *
+ * This keymap is for several Medion X10 remotes that have the Windows MCE
+ * button. This has been tested with a "RF VISTA Remote Control", OR24V,
+ * P/N 20035335, but should work with other variants that have the same
+ * buttons, such as OR22V and OR24E.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/module.h>
+#include <media/rc-map.h>
+
+static struct rc_map_table medion_x10_or2x[] = {
+ { 0x02, KEY_POWER },
+ { 0x16, KEY_TEXT }, /* "T" in a box, for teletext */
+
+ { 0x09, KEY_VOLUMEUP },
+ { 0x08, KEY_VOLUMEDOWN },
+ { 0x00, KEY_MUTE },
+ { 0x0b, KEY_CHANNELUP },
+ { 0x0c, KEY_CHANNELDOWN },
+
+ { 0x32, KEY_RED },
+ { 0x33, KEY_GREEN },
+ { 0x34, KEY_YELLOW },
+ { 0x35, KEY_BLUE },
+
+ { 0x18, KEY_PVR }, /* record symbol inside a tv symbol */
+ { 0x04, KEY_DVD }, /* disc symbol */
+ { 0x31, KEY_EPG }, /* a tv schedule symbol */
+ { 0x1c, KEY_TV }, /* play symbol inside a tv symbol */
+ { 0x20, KEY_BACK },
+ { 0x2f, KEY_INFO },
+
+ { 0x1a, KEY_UP },
+ { 0x22, KEY_DOWN },
+ { 0x1d, KEY_LEFT },
+ { 0x1f, KEY_RIGHT },
+ { 0x1e, KEY_OK },
+
+ { 0x1b, KEY_MEDIA }, /* Windows MCE button */
+
+ { 0x21, KEY_PREVIOUS },
+ { 0x23, KEY_NEXT },
+ { 0x24, KEY_REWIND },
+ { 0x26, KEY_FORWARD },
+ { 0x25, KEY_PLAY },
+ { 0x28, KEY_STOP },
+ { 0x29, KEY_PAUSE },
+ { 0x27, KEY_RECORD },
+
+ { 0x0d, KEY_1 },
+ { 0x0e, KEY_2 },
+ { 0x0f, KEY_3 },
+ { 0x10, KEY_4 },
+ { 0x11, KEY_5 },
+ { 0x12, KEY_6 },
+ { 0x13, KEY_7 },
+ { 0x14, KEY_8 },
+ { 0x15, KEY_9 },
+ { 0x17, KEY_0 },
+ { 0x30, KEY_CLEAR },
+ { 0x36, KEY_ENTER },
+ { 0x37, KEY_NUMERIC_STAR },
+ { 0x38, KEY_NUMERIC_POUND },
+};
+
+static struct rc_map_list medion_x10_or2x_map = {
+ .map = {
+ .scan = medion_x10_or2x,
+ .size = ARRAY_SIZE(medion_x10_or2x),
+ .rc_type = RC_TYPE_OTHER,
+ .name = RC_MAP_MEDION_X10_OR2X,
+ }
+};
+
+static int __init init_rc_map_medion_x10_or2x(void)
+{
+ return rc_map_register(&medion_x10_or2x_map);
+}
+
+static void __exit exit_rc_map_medion_x10_or2x(void)
+{
+ rc_map_unregister(&medion_x10_or2x_map);
+}
+
+module_init(init_rc_map_medion_x10_or2x)
+module_exit(exit_rc_map_medion_x10_or2x)
+
+MODULE_DESCRIPTION("Medion X10 OR22/OR24 RF remote keytable");
+MODULE_AUTHOR("Anssi Hannula <anssi.hannula@iki.fi>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/rc/keymaps/rc-medion-x10.c b/drivers/media/rc/keymaps/rc-medion-x10.c
new file mode 100644
index 00000000000..479cdb89781
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-medion-x10.c
@@ -0,0 +1,117 @@
+/*
+ * Medion X10 RF remote keytable
+ *
+ * Copyright (C) 2011 Anssi Hannula <anssi.hannula@?ki.fi>
+ *
+ * This file is based on a keytable provided by
+ * Jan Losinski <losinski@wh2.tu-dresden.de>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/module.h>
+#include <media/rc-map.h>
+
+static struct rc_map_table medion_x10[] = {
+ { 0x2c, KEY_TV }, /* TV */
+ { 0x2d, KEY_VCR }, /* VCR */
+ { 0x04, KEY_DVD }, /* DVD */
+ { 0x06, KEY_AUDIO }, /* MUSIC */
+
+ { 0x2e, KEY_RADIO }, /* RADIO */
+ { 0x05, KEY_DIRECTORY }, /* PHOTO */
+ { 0x2f, KEY_INFO }, /* TV-PREVIEW */
+ { 0x30, KEY_LIST }, /* CHANNEL-LST */
+
+ { 0x1b, KEY_SETUP }, /* SETUP */
+ { 0x31, KEY_VIDEO }, /* VIDEO DESKTOP */
+
+ { 0x08, KEY_VOLUMEDOWN }, /* VOL - */
+ { 0x09, KEY_VOLUMEUP }, /* VOL + */
+ { 0x0b, KEY_CHANNELUP }, /* CHAN + */
+ { 0x0c, KEY_CHANNELDOWN }, /* CHAN - */
+ { 0x00, KEY_MUTE }, /* MUTE */
+
+ { 0x32, KEY_RED }, /* red */
+ { 0x33, KEY_GREEN }, /* green */
+ { 0x34, KEY_YELLOW }, /* yellow */
+ { 0x35, KEY_BLUE }, /* blue */
+ { 0x16, KEY_TEXT }, /* TXT */
+
+ { 0x0d, KEY_1 },
+ { 0x0e, KEY_2 },
+ { 0x0f, KEY_3 },
+ { 0x10, KEY_4 },
+ { 0x11, KEY_5 },
+ { 0x12, KEY_6 },
+ { 0x13, KEY_7 },
+ { 0x14, KEY_8 },
+ { 0x15, KEY_9 },
+ { 0x17, KEY_0 },
+ { 0x1c, KEY_SEARCH }, /* TV/RAD, CH SRC */
+ { 0x20, KEY_DELETE }, /* DELETE */
+
+ { 0x36, KEY_KEYBOARD }, /* RENAME */
+ { 0x18, KEY_SCREEN }, /* SNAPSHOT */
+
+ { 0x1a, KEY_UP }, /* up */
+ { 0x22, KEY_DOWN }, /* down */
+ { 0x1d, KEY_LEFT }, /* left */
+ { 0x1f, KEY_RIGHT }, /* right */
+ { 0x1e, KEY_OK }, /* OK */
+
+ { 0x37, KEY_SELECT }, /* ACQUIRE IMAGE */
+ { 0x38, KEY_EDIT }, /* EDIT IMAGE */
+
+ { 0x24, KEY_REWIND }, /* rewind (<<) */
+ { 0x25, KEY_PLAY }, /* play ( >) */
+ { 0x26, KEY_FORWARD }, /* forward (>>) */
+ { 0x27, KEY_RECORD }, /* record ( o) */
+ { 0x28, KEY_STOP }, /* stop ([]) */
+ { 0x29, KEY_PAUSE }, /* pause ('') */
+
+ { 0x21, KEY_PREVIOUS }, /* prev */
+ { 0x39, KEY_SWITCHVIDEOMODE }, /* F SCR */
+ { 0x23, KEY_NEXT }, /* next */
+ { 0x19, KEY_MENU }, /* MENU */
+ { 0x3a, KEY_LANGUAGE }, /* AUDIO */
+
+ { 0x02, KEY_POWER }, /* POWER */
+};
+
+static struct rc_map_list medion_x10_map = {
+ .map = {
+ .scan = medion_x10,
+ .size = ARRAY_SIZE(medion_x10),
+ .rc_type = RC_TYPE_OTHER,
+ .name = RC_MAP_MEDION_X10,
+ }
+};
+
+static int __init init_rc_map_medion_x10(void)
+{
+ return rc_map_register(&medion_x10_map);
+}
+
+static void __exit exit_rc_map_medion_x10(void)
+{
+ rc_map_unregister(&medion_x10_map);
+}
+
+module_init(init_rc_map_medion_x10)
+module_exit(exit_rc_map_medion_x10)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Anssi Hannula <anssi.hannula@iki.fi>");
diff --git a/drivers/media/rc/keymaps/rc-msi-digivox-ii.c b/drivers/media/rc/keymaps/rc-msi-digivox-ii.c
index 7b9a01b6e4c..2fa71d0d72d 100644
--- a/drivers/media/rc/keymaps/rc-msi-digivox-ii.c
+++ b/drivers/media/rc/keymaps/rc-msi-digivox-ii.c
@@ -19,26 +19,27 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table msi_digivox_ii[] = {
- { 0x0002, KEY_2 },
- { 0x0003, KEY_UP }, /* up */
- { 0x0004, KEY_3 },
- { 0x0005, KEY_CHANNELDOWN },
- { 0x0008, KEY_5 },
- { 0x0009, KEY_0 },
- { 0x000b, KEY_8 },
- { 0x000d, KEY_DOWN }, /* down */
- { 0x0010, KEY_9 },
- { 0x0011, KEY_7 },
- { 0x0014, KEY_VOLUMEUP },
- { 0x0015, KEY_CHANNELUP },
- { 0x0016, KEY_OK },
- { 0x0017, KEY_POWER2 },
- { 0x001a, KEY_1 },
- { 0x001c, KEY_4 },
- { 0x001d, KEY_6 },
- { 0x001f, KEY_VOLUMEDOWN },
+ { 0x0302, KEY_2 },
+ { 0x0303, KEY_UP }, /* up */
+ { 0x0304, KEY_3 },
+ { 0x0305, KEY_CHANNELDOWN },
+ { 0x0308, KEY_5 },
+ { 0x0309, KEY_0 },
+ { 0x030b, KEY_8 },
+ { 0x030d, KEY_DOWN }, /* down */
+ { 0x0310, KEY_9 },
+ { 0x0311, KEY_7 },
+ { 0x0314, KEY_VOLUMEUP },
+ { 0x0315, KEY_CHANNELUP },
+ { 0x0316, KEY_OK },
+ { 0x0317, KEY_POWER2 },
+ { 0x031a, KEY_1 },
+ { 0x031c, KEY_4 },
+ { 0x031d, KEY_6 },
+ { 0x031f, KEY_VOLUMEDOWN },
};
static struct rc_map_list msi_digivox_ii_map = {
diff --git a/drivers/media/rc/keymaps/rc-msi-digivox-iii.c b/drivers/media/rc/keymaps/rc-msi-digivox-iii.c
index ae9d06b3915..303a0b73175 100644
--- a/drivers/media/rc/keymaps/rc-msi-digivox-iii.c
+++ b/drivers/media/rc/keymaps/rc-msi-digivox-iii.c
@@ -19,6 +19,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* MSI DIGIVOX mini III */
/* Uses NEC extended 0x61d6. */
diff --git a/drivers/media/rc/keymaps/rc-msi-tvanywhere-plus.c b/drivers/media/rc/keymaps/rc-msi-tvanywhere-plus.c
index fa8fd0ab94c..fd7a55c5616 100644
--- a/drivers/media/rc/keymaps/rc-msi-tvanywhere-plus.c
+++ b/drivers/media/rc/keymaps/rc-msi-tvanywhere-plus.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/*
Keycodes for remote on the MSI TV@nywhere Plus. The controller IC on the card
@@ -62,7 +63,7 @@ static struct rc_map_table msi_tvanywhere_plus[] = {
{ 0x13, KEY_AGAIN }, /* Recall */
{ 0x1e, KEY_POWER }, /* Power */
- { 0x07, KEY_TUNER }, /* Source */
+ { 0x07, KEY_VIDEO }, /* Source */
{ 0x1c, KEY_SEARCH }, /* Scan */
{ 0x18, KEY_MUTE }, /* Mute */
@@ -120,4 +121,4 @@ module_init(init_rc_map_msi_tvanywhere_plus)
module_exit(exit_rc_map_msi_tvanywhere_plus)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-msi-tvanywhere.c b/drivers/media/rc/keymaps/rc-msi-tvanywhere.c
index 18b37facb0d..4233a8d4d63 100644
--- a/drivers/media/rc/keymaps/rc-msi-tvanywhere.c
+++ b/drivers/media/rc/keymaps/rc-msi-tvanywhere.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* MSI TV@nywhere MASTER remote */
@@ -29,7 +30,7 @@ static struct rc_map_table msi_tvanywhere[] = {
{ 0x0c, KEY_MUTE },
{ 0x0f, KEY_SCREEN }, /* Full Screen */
- { 0x10, KEY_FN }, /* Funtion */
+ { 0x10, KEY_FN }, /* Function */
{ 0x11, KEY_TIME }, /* Time shift */
{ 0x12, KEY_POWER },
{ 0x13, KEY_MEDIA }, /* MTS */
@@ -66,4 +67,4 @@ module_init(init_rc_map_msi_tvanywhere)
module_exit(exit_rc_map_msi_tvanywhere)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-nebula.c b/drivers/media/rc/keymaps/rc-nebula.c
index 3e6f077eb70..8ec881adb7c 100644
--- a/drivers/media/rc/keymaps/rc-nebula.c
+++ b/drivers/media/rc/keymaps/rc-nebula.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table nebula[] = {
{ 0x00, KEY_0 },
@@ -27,7 +28,7 @@ static struct rc_map_table nebula[] = {
{ 0x0b, KEY_AUX },
{ 0x0c, KEY_DVD },
{ 0x0d, KEY_POWER },
- { 0x0e, KEY_MHP }, /* labelled 'Picture' */
+ { 0x0e, KEY_CAMERA }, /* labelled 'Picture' */
{ 0x0f, KEY_AUDIO },
{ 0x10, KEY_INFO },
{ 0x11, KEY_F13 }, /* 16:9 */
@@ -93,4 +94,4 @@ module_init(init_rc_map_nebula)
module_exit(exit_rc_map_nebula)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c b/drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c
index 26f114c5c0d..292bbad35d2 100644
--- a/drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c
+++ b/drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,12 +11,15 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Terratec Cinergy Hybrid T USB XS FM
- Mauro Carvalho Chehab <mchehab@redhat.com>
+ Mauro Carvalho Chehab
*/
static struct rc_map_table nec_terratec_cinergy_xs[] = {
+
+ /* Terratec Grey IR, with most keys in orange */
{ 0x1441, KEY_HOME},
{ 0x1401, KEY_POWER2},
@@ -77,6 +80,56 @@ static struct rc_map_table nec_terratec_cinergy_xs[] = {
{ 0x144e, KEY_REWIND},
{ 0x144f, KEY_FASTFORWARD},
{ 0x145c, KEY_NEXT},
+
+ /* Terratec Black IR, with most keys in black */
+ { 0x04eb01, KEY_POWER2},
+
+ { 0x04eb02, KEY_1},
+ { 0x04eb03, KEY_2},
+ { 0x04eb04, KEY_3},
+ { 0x04eb05, KEY_4},
+ { 0x04eb06, KEY_5},
+ { 0x04eb07, KEY_6},
+ { 0x04eb08, KEY_7},
+ { 0x04eb09, KEY_8},
+ { 0x04eb0a, KEY_9},
+ { 0x04eb0c, KEY_0},
+
+ { 0x04eb0b, KEY_TEXT}, /* TXT */
+ { 0x04eb0d, KEY_REFRESH}, /* Refresh */
+
+ { 0x04eb0e, KEY_HOME},
+ { 0x04eb0f, KEY_EPG},
+
+ { 0x04eb10, KEY_UP},
+ { 0x04eb11, KEY_LEFT},
+ { 0x04eb12, KEY_OK},
+ { 0x04eb13, KEY_RIGHT},
+ { 0x04eb14, KEY_DOWN},
+
+ { 0x04eb15, KEY_BACKSPACE},
+ { 0x04eb16, KEY_INFO},
+
+ { 0x04eb17, KEY_RED},
+ { 0x04eb18, KEY_GREEN},
+ { 0x04eb19, KEY_YELLOW},
+ { 0x04eb1a, KEY_BLUE},
+
+ { 0x04eb1c, KEY_VOLUMEUP},
+ { 0x04eb1e, KEY_VOLUMEDOWN},
+
+ { 0x04eb1d, KEY_MUTE},
+
+ { 0x04eb1b, KEY_CHANNELUP},
+ { 0x04eb1f, KEY_CHANNELDOWN},
+
+ { 0x04eb40, KEY_RECORD},
+ { 0x04eb4c, KEY_PLAY},
+ { 0x04eb58, KEY_PAUSE},
+
+ { 0x04eb54, KEY_REWIND},
+ { 0x04eb48, KEY_STOP},
+ { 0x04eb5c, KEY_NEXT},
};
static struct rc_map_list nec_terratec_cinergy_xs_map = {
@@ -102,4 +155,4 @@ module_init(init_rc_map_nec_terratec_cinergy_xs)
module_exit(exit_rc_map_nec_terratec_cinergy_xs)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-norwood.c b/drivers/media/rc/keymaps/rc-norwood.c
index 629ee9d8453..ca1b82a2c54 100644
--- a/drivers/media/rc/keymaps/rc-norwood.c
+++ b/drivers/media/rc/keymaps/rc-norwood.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Norwood Micro (non-Pro) TV Tuner
By Peter Naulls <peter@chocky.org>
@@ -29,7 +30,7 @@ static struct rc_map_table norwood[] = {
{ 0x28, KEY_8 },
{ 0x29, KEY_9 },
- { 0x78, KEY_TUNER }, /* Video Source */
+ { 0x78, KEY_VIDEO }, /* Video Source */
{ 0x2c, KEY_EXIT }, /* Open/Close software */
{ 0x2a, KEY_SELECT }, /* 2 Digit Select */
{ 0x69, KEY_AGAIN }, /* Recall */
@@ -49,7 +50,7 @@ static struct rc_map_table norwood[] = {
{ 0x37, KEY_PLAY }, /* Play */
{ 0x36, KEY_PAUSE }, /* Pause */
{ 0x2b, KEY_STOP }, /* Stop */
- { 0x67, KEY_FASTFORWARD }, /* Foward */
+ { 0x67, KEY_FASTFORWARD }, /* Forward */
{ 0x66, KEY_REWIND }, /* Rewind */
{ 0x3e, KEY_SEARCH }, /* Auto Scan */
{ 0x2e, KEY_CAMERA }, /* Capture Video */
@@ -82,4 +83,4 @@ module_init(init_rc_map_norwood)
module_exit(exit_rc_map_norwood)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-npgtech.c b/drivers/media/rc/keymaps/rc-npgtech.c
index 4aa588bf6d6..1fb94602451 100644
--- a/drivers/media/rc/keymaps/rc-npgtech.c
+++ b/drivers/media/rc/keymaps/rc-npgtech.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table npgtech[] = {
{ 0x1d, KEY_SWITCHVIDEOMODE }, /* switch inputs */
@@ -77,4 +78,4 @@ module_init(init_rc_map_npgtech)
module_exit(exit_rc_map_npgtech)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-pctv-sedna.c b/drivers/media/rc/keymaps/rc-pctv-sedna.c
index fa5ae5981eb..5ef01ab3fd5 100644
--- a/drivers/media/rc/keymaps/rc-pctv-sedna.c
+++ b/drivers/media/rc/keymaps/rc-pctv-sedna.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Mapping for the 28 key remote control as seen at
http://www.sednacomputer.com/photo/cardbus-tv.jpg
@@ -36,7 +37,7 @@ static struct rc_map_table pctv_sedna[] = {
{ 0x0e, KEY_STOP },
{ 0x0f, KEY_PREVIOUSSONG },
{ 0x10, KEY_ZOOM },
- { 0x11, KEY_TUNER }, /* Source */
+ { 0x11, KEY_VIDEO }, /* Source */
{ 0x12, KEY_POWER },
{ 0x13, KEY_MUTE },
{ 0x15, KEY_CHANNELDOWN },
@@ -77,4 +78,4 @@ module_init(init_rc_map_pctv_sedna)
module_exit(exit_rc_map_pctv_sedna)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-pinnacle-color.c b/drivers/media/rc/keymaps/rc-pinnacle-color.c
index 23b8c505c6a..a218b471a4c 100644
--- a/drivers/media/rc/keymaps/rc-pinnacle-color.c
+++ b/drivers/media/rc/keymaps/rc-pinnacle-color.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table pinnacle_color[] = {
{ 0x59, KEY_MUTE },
@@ -91,4 +92,4 @@ module_init(init_rc_map_pinnacle_color)
module_exit(exit_rc_map_pinnacle_color)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-pinnacle-grey.c b/drivers/media/rc/keymaps/rc-pinnacle-grey.c
index 6ba8c368d10..4a3f467a47a 100644
--- a/drivers/media/rc/keymaps/rc-pinnacle-grey.c
+++ b/drivers/media/rc/keymaps/rc-pinnacle-grey.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table pinnacle_grey[] = {
{ 0x3a, KEY_0 },
@@ -86,4 +87,4 @@ module_init(init_rc_map_pinnacle_grey)
module_exit(exit_rc_map_pinnacle_grey)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-pinnacle-pctv-hd.c b/drivers/media/rc/keymaps/rc-pinnacle-pctv-hd.c
index bb10ffe086b..e89cc10b68b 100644
--- a/drivers/media/rc/keymaps/rc-pinnacle-pctv-hd.c
+++ b/drivers/media/rc/keymaps/rc-pinnacle-pctv-hd.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,47 +11,45 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Pinnacle PCTV HD 800i mini remote */
static struct rc_map_table pinnacle_pctv_hd[] = {
-
- { 0x0f, KEY_1 },
- { 0x15, KEY_2 },
- { 0x10, KEY_3 },
- { 0x18, KEY_4 },
- { 0x1b, KEY_5 },
- { 0x1e, KEY_6 },
- { 0x11, KEY_7 },
- { 0x21, KEY_8 },
- { 0x12, KEY_9 },
- { 0x27, KEY_0 },
-
- { 0x24, KEY_ZOOM },
- { 0x2a, KEY_SUBTITLE },
-
- { 0x00, KEY_MUTE },
- { 0x01, KEY_ENTER }, /* Pinnacle Logo */
- { 0x39, KEY_POWER },
-
- { 0x03, KEY_VOLUMEUP },
- { 0x09, KEY_VOLUMEDOWN },
- { 0x06, KEY_CHANNELUP },
- { 0x0c, KEY_CHANNELDOWN },
-
- { 0x2d, KEY_REWIND },
- { 0x30, KEY_PLAYPAUSE },
- { 0x33, KEY_FASTFORWARD },
- { 0x3c, KEY_STOP },
- { 0x36, KEY_RECORD },
- { 0x3f, KEY_EPG }, /* Labeled "?" */
+ /* Key codes for the tiny Pinnacle remote*/
+ { 0x0700, KEY_MUTE },
+ { 0x0701, KEY_MENU }, /* Pinnacle logo */
+ { 0x0739, KEY_POWER },
+ { 0x0703, KEY_VOLUMEUP },
+ { 0x0705, KEY_OK },
+ { 0x0709, KEY_VOLUMEDOWN },
+ { 0x0706, KEY_CHANNELUP },
+ { 0x070c, KEY_CHANNELDOWN },
+ { 0x070f, KEY_1 },
+ { 0x0715, KEY_2 },
+ { 0x0710, KEY_3 },
+ { 0x0718, KEY_4 },
+ { 0x071b, KEY_5 },
+ { 0x071e, KEY_6 },
+ { 0x0711, KEY_7 },
+ { 0x0721, KEY_8 },
+ { 0x0712, KEY_9 },
+ { 0x0727, KEY_0 },
+ { 0x0724, KEY_ZOOM }, /* 'Square' key */
+ { 0x072a, KEY_SUBTITLE }, /* 'T' key */
+ { 0x072d, KEY_REWIND },
+ { 0x0730, KEY_PLAYPAUSE },
+ { 0x0733, KEY_FASTFORWARD },
+ { 0x0736, KEY_RECORD },
+ { 0x073c, KEY_STOP },
+ { 0x073f, KEY_HELP }, /* '?' key */
};
static struct rc_map_list pinnacle_pctv_hd_map = {
.map = {
.scan = pinnacle_pctv_hd,
.size = ARRAY_SIZE(pinnacle_pctv_hd),
- .rc_type = RC_TYPE_UNKNOWN, /* Legacy IR type */
+ .rc_type = RC_TYPE_RC5,
.name = RC_MAP_PINNACLE_PCTV_HD,
}
};
@@ -70,4 +68,4 @@ module_init(init_rc_map_pinnacle_pctv_hd)
module_exit(exit_rc_map_pinnacle_pctv_hd)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-pixelview-002t.c b/drivers/media/rc/keymaps/rc-pixelview-002t.c
index e5ab071f635..d967c3816fd 100644
--- a/drivers/media/rc/keymaps/rc-pixelview-002t.c
+++ b/drivers/media/rc/keymaps/rc-pixelview-002t.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/*
* Keytable for 002-T IR remote provided together with Pixelview
@@ -74,4 +75,4 @@ module_init(init_rc_map_pixelview)
module_exit(exit_rc_map_pixelview)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-pixelview-mk12.c b/drivers/media/rc/keymaps/rc-pixelview-mk12.c
index 8d9f664e0a2..224d0efaa6e 100644
--- a/drivers/media/rc/keymaps/rc-pixelview-mk12.c
+++ b/drivers/media/rc/keymaps/rc-pixelview-mk12.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/*
* Keytable for MK-F12 IR remote provided together with Pixelview
@@ -34,7 +35,7 @@ static struct rc_map_table pixelview_mk12[] = {
{ 0x866b13, KEY_AGAIN }, /* loop */
{ 0x866b10, KEY_DIGITS }, /* +100 */
- { 0x866b00, KEY_MEDIA }, /* source */
+ { 0x866b00, KEY_VIDEO }, /* source */
{ 0x866b18, KEY_MUTE }, /* mute */
{ 0x866b19, KEY_CAMERA }, /* snapshot */
{ 0x866b1a, KEY_SEARCH }, /* scan */
@@ -80,4 +81,4 @@ module_init(init_rc_map_pixelview)
module_exit(exit_rc_map_pixelview)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-pixelview-new.c b/drivers/media/rc/keymaps/rc-pixelview-new.c
index 777a70076be..781d788d6b6 100644
--- a/drivers/media/rc/keymaps/rc-pixelview-new.c
+++ b/drivers/media/rc/keymaps/rc-pixelview-new.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/*
Mauro Carvalho Chehab <mchehab@infradead.org>
@@ -33,7 +34,7 @@ static struct rc_map_table pixelview_new[] = {
{ 0x3e, KEY_0 },
{ 0x1c, KEY_AGAIN }, /* LOOP */
- { 0x3f, KEY_MEDIA }, /* Source */
+ { 0x3f, KEY_VIDEO }, /* Source */
{ 0x1f, KEY_LAST }, /* +100 */
{ 0x1b, KEY_MUTE },
@@ -80,4 +81,4 @@ module_init(init_rc_map_pixelview_new)
module_exit(exit_rc_map_pixelview_new)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-pixelview.c b/drivers/media/rc/keymaps/rc-pixelview.c
index 0ec5988916b..39e6feaa35a 100644
--- a/drivers/media/rc/keymaps/rc-pixelview.c
+++ b/drivers/media/rc/keymaps/rc-pixelview.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,11 +11,12 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table pixelview[] = {
{ 0x1e, KEY_POWER }, /* power */
- { 0x07, KEY_MEDIA }, /* source */
+ { 0x07, KEY_VIDEO }, /* source */
{ 0x1c, KEY_SEARCH }, /* scan */
@@ -79,4 +80,4 @@ module_init(init_rc_map_pixelview)
module_exit(exit_rc_map_pixelview)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-powercolor-real-angel.c b/drivers/media/rc/keymaps/rc-powercolor-real-angel.c
index 5f9d546a86c..e96fa3ab9f4 100644
--- a/drivers/media/rc/keymaps/rc-powercolor-real-angel.c
+++ b/drivers/media/rc/keymaps/rc-powercolor-real-angel.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/*
* Remote control for Powercolor Real Angel 330
@@ -78,4 +79,4 @@ module_init(init_rc_map_powercolor_real_angel)
module_exit(exit_rc_map_powercolor_real_angel)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-proteus-2309.c b/drivers/media/rc/keymaps/rc-proteus-2309.c
index 8a3a643879d..eef626ee02d 100644
--- a/drivers/media/rc/keymaps/rc-proteus-2309.c
+++ b/drivers/media/rc/keymaps/rc-proteus-2309.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Michal Majchrowicz <mmajchrowicz@gmail.com> */
@@ -66,4 +67,4 @@ module_init(init_rc_map_proteus_2309)
module_exit(exit_rc_map_proteus_2309)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-purpletv.c b/drivers/media/rc/keymaps/rc-purpletv.c
index ef90296bfd6..cec6fe46682 100644
--- a/drivers/media/rc/keymaps/rc-purpletv.c
+++ b/drivers/media/rc/keymaps/rc-purpletv.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table purpletv[] = {
{ 0x03, KEY_POWER },
@@ -78,4 +79,4 @@ module_init(init_rc_map_purpletv)
module_exit(exit_rc_map_purpletv)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-pv951.c b/drivers/media/rc/keymaps/rc-pv951.c
index 83a418de12c..5ac89ce8c05 100644
--- a/drivers/media/rc/keymaps/rc-pv951.c
+++ b/drivers/media/rc/keymaps/rc-pv951.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Mark Phalan <phalanm@o2.ie> */
@@ -46,10 +47,10 @@ static struct rc_map_table pv951[] = {
{ 0x0c, KEY_SEARCH }, /* AUTOSCAN */
/* Not sure what to do with these ones! */
- { 0x0f, KEY_SELECT }, /* SOURCE */
+ { 0x0f, KEY_VIDEO }, /* SOURCE */
{ 0x0a, KEY_KPPLUS }, /* +100 */
{ 0x14, KEY_EQUAL }, /* SYNC */
- { 0x1c, KEY_MEDIA }, /* PC/TV */
+ { 0x1c, KEY_TV }, /* PC/TV */
};
static struct rc_map_list pv951_map = {
@@ -75,4 +76,4 @@ module_init(init_rc_map_pv951)
module_exit(exit_rc_map_pv951)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-rc5-hauppauge-new.c b/drivers/media/rc/keymaps/rc-rc5-hauppauge-new.c
deleted file mode 100644
index dfc9b15f43a..00000000000
--- a/drivers/media/rc/keymaps/rc-rc5-hauppauge-new.c
+++ /dev/null
@@ -1,141 +0,0 @@
-/* rc5-hauppauge-new.h - Keytable for rc5_hauppauge_new Remote Controller
- *
- * keymap imported from ir-keymaps.c
- *
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
- *
- * 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.
- */
-
-#include <media/rc-map.h>
-
-/*
- * Hauppauge:the newer, gray remotes (seems there are multiple
- * slightly different versions), shipped with cx88+ivtv cards.
- *
- * This table contains the complete RC5 code, instead of just the data part
- */
-
-static struct rc_map_table rc5_hauppauge_new[] = {
- /* Keys 0 to 9 */
- { 0x1e00, KEY_0 },
- { 0x1e01, KEY_1 },
- { 0x1e02, KEY_2 },
- { 0x1e03, KEY_3 },
- { 0x1e04, KEY_4 },
- { 0x1e05, KEY_5 },
- { 0x1e06, KEY_6 },
- { 0x1e07, KEY_7 },
- { 0x1e08, KEY_8 },
- { 0x1e09, KEY_9 },
-
- { 0x1e0a, KEY_TEXT }, /* keypad asterisk as well */
- { 0x1e0b, KEY_RED }, /* red button */
- { 0x1e0c, KEY_RADIO },
- { 0x1e0d, KEY_MENU },
- { 0x1e0e, KEY_SUBTITLE }, /* also the # key */
- { 0x1e0f, KEY_MUTE },
- { 0x1e10, KEY_VOLUMEUP },
- { 0x1e11, KEY_VOLUMEDOWN },
- { 0x1e12, KEY_PREVIOUS }, /* previous channel */
- { 0x1e14, KEY_UP },
- { 0x1e15, KEY_DOWN },
- { 0x1e16, KEY_LEFT },
- { 0x1e17, KEY_RIGHT },
- { 0x1e18, KEY_VIDEO }, /* Videos */
- { 0x1e19, KEY_AUDIO }, /* Music */
- /* 0x1e1a: Pictures - presume this means
- "Multimedia Home Platform" -
- no "PICTURES" key in input.h
- */
- { 0x1e1a, KEY_MHP },
-
- { 0x1e1b, KEY_EPG }, /* Guide */
- { 0x1e1c, KEY_TV },
- { 0x1e1e, KEY_NEXTSONG }, /* skip >| */
- { 0x1e1f, KEY_EXIT }, /* back/exit */
- { 0x1e20, KEY_CHANNELUP }, /* channel / program + */
- { 0x1e21, KEY_CHANNELDOWN }, /* channel / program - */
- { 0x1e22, KEY_CHANNEL }, /* source (old black remote) */
- { 0x1e24, KEY_PREVIOUSSONG }, /* replay |< */
- { 0x1e25, KEY_ENTER }, /* OK */
- { 0x1e26, KEY_SLEEP }, /* minimize (old black remote) */
- { 0x1e29, KEY_BLUE }, /* blue key */
- { 0x1e2e, KEY_GREEN }, /* green button */
- { 0x1e30, KEY_PAUSE }, /* pause */
- { 0x1e32, KEY_REWIND }, /* backward << */
- { 0x1e34, KEY_FASTFORWARD }, /* forward >> */
- { 0x1e35, KEY_PLAY },
- { 0x1e36, KEY_STOP },
- { 0x1e37, KEY_RECORD }, /* recording */
- { 0x1e38, KEY_YELLOW }, /* yellow key */
- { 0x1e3b, KEY_SELECT }, /* top right button */
- { 0x1e3c, KEY_ZOOM }, /* full */
- { 0x1e3d, KEY_POWER }, /* system power (green button) */
-
- /* Keycodes for DSR-0112 remote bundled with Haupauge MiniStick */
- { 0x1d00, KEY_0 },
- { 0x1d01, KEY_1 },
- { 0x1d02, KEY_2 },
- { 0x1d03, KEY_3 },
- { 0x1d04, KEY_4 },
- { 0x1d05, KEY_5 },
- { 0x1d06, KEY_6 },
- { 0x1d07, KEY_7 },
- { 0x1d08, KEY_8 },
- { 0x1d09, KEY_9 },
- { 0x1d0a, KEY_TEXT },
- { 0x1d0d, KEY_MENU },
- { 0x1d0f, KEY_MUTE },
- { 0x1d10, KEY_VOLUMEUP },
- { 0x1d11, KEY_VOLUMEDOWN },
- { 0x1d12, KEY_PREVIOUS }, /* Prev.Ch .. ??? */
- { 0x1d14, KEY_UP },
- { 0x1d15, KEY_DOWN },
- { 0x1d16, KEY_LEFT },
- { 0x1d17, KEY_RIGHT },
- { 0x1d1c, KEY_TV },
- { 0x1d1e, KEY_NEXT }, /* >| */
- { 0x1d1f, KEY_EXIT },
- { 0x1d20, KEY_CHANNELUP },
- { 0x1d21, KEY_CHANNELDOWN },
- { 0x1d24, KEY_LAST }, /* <| */
- { 0x1d25, KEY_OK },
- { 0x1d30, KEY_PAUSE },
- { 0x1d32, KEY_REWIND },
- { 0x1d34, KEY_FASTFORWARD },
- { 0x1d35, KEY_PLAY },
- { 0x1d36, KEY_STOP },
- { 0x1d37, KEY_RECORD },
- { 0x1d3b, KEY_GOTO },
- { 0x1d3d, KEY_POWER },
- { 0x1d3f, KEY_HOME },
-};
-
-static struct rc_map_list rc5_hauppauge_new_map = {
- .map = {
- .scan = rc5_hauppauge_new,
- .size = ARRAY_SIZE(rc5_hauppauge_new),
- .rc_type = RC_TYPE_RC5,
- .name = RC_MAP_RC5_HAUPPAUGE_NEW,
- }
-};
-
-static int __init init_rc_map_rc5_hauppauge_new(void)
-{
- return rc_map_register(&rc5_hauppauge_new_map);
-}
-
-static void __exit exit_rc_map_rc5_hauppauge_new(void)
-{
- rc_map_unregister(&rc5_hauppauge_new_map);
-}
-
-module_init(init_rc_map_rc5_hauppauge_new)
-module_exit(exit_rc_map_rc5_hauppauge_new)
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
diff --git a/drivers/media/rc/keymaps/rc-rc5-tv.c b/drivers/media/rc/keymaps/rc-rc5-tv.c
deleted file mode 100644
index 4fcef9f1f72..00000000000
--- a/drivers/media/rc/keymaps/rc-rc5-tv.c
+++ /dev/null
@@ -1,81 +0,0 @@
-/* rc5-tv.h - Keytable for rc5_tv Remote Controller
- *
- * keymap imported from ir-keymaps.c
- *
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
- *
- * 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.
- */
-
-#include <media/rc-map.h>
-
-/* generic RC5 keytable */
-/* see http://users.pandora.be/nenya/electronics/rc5/codes00.htm */
-/* used by old (black) Hauppauge remotes */
-
-static struct rc_map_table rc5_tv[] = {
- /* Keys 0 to 9 */
- { 0x00, KEY_0 },
- { 0x01, KEY_1 },
- { 0x02, KEY_2 },
- { 0x03, KEY_3 },
- { 0x04, KEY_4 },
- { 0x05, KEY_5 },
- { 0x06, KEY_6 },
- { 0x07, KEY_7 },
- { 0x08, KEY_8 },
- { 0x09, KEY_9 },
-
- { 0x0b, KEY_CHANNEL }, /* channel / program (japan: 11) */
- { 0x0c, KEY_POWER }, /* standby */
- { 0x0d, KEY_MUTE }, /* mute / demute */
- { 0x0f, KEY_TV }, /* display */
- { 0x10, KEY_VOLUMEUP },
- { 0x11, KEY_VOLUMEDOWN },
- { 0x12, KEY_BRIGHTNESSUP },
- { 0x13, KEY_BRIGHTNESSDOWN },
- { 0x1e, KEY_SEARCH }, /* search + */
- { 0x20, KEY_CHANNELUP }, /* channel / program + */
- { 0x21, KEY_CHANNELDOWN }, /* channel / program - */
- { 0x22, KEY_CHANNEL }, /* alt / channel */
- { 0x23, KEY_LANGUAGE }, /* 1st / 2nd language */
- { 0x26, KEY_SLEEP }, /* sleeptimer */
- { 0x2e, KEY_MENU }, /* 2nd controls (USA: menu) */
- { 0x30, KEY_PAUSE },
- { 0x32, KEY_REWIND },
- { 0x33, KEY_GOTO },
- { 0x35, KEY_PLAY },
- { 0x36, KEY_STOP },
- { 0x37, KEY_RECORD }, /* recording */
- { 0x3c, KEY_TEXT }, /* teletext submode (Japan: 12) */
- { 0x3d, KEY_SUSPEND }, /* system standby */
-
-};
-
-static struct rc_map_list rc5_tv_map = {
- .map = {
- .scan = rc5_tv,
- .size = ARRAY_SIZE(rc5_tv),
- .rc_type = RC_TYPE_UNKNOWN, /* Legacy IR type */
- .name = RC_MAP_RC5_TV,
- }
-};
-
-static int __init init_rc_map_rc5_tv(void)
-{
- return rc_map_register(&rc5_tv_map);
-}
-
-static void __exit exit_rc_map_rc5_tv(void)
-{
- rc_map_unregister(&rc5_tv_map);
-}
-
-module_init(init_rc_map_rc5_tv)
-module_exit(exit_rc_map_rc5_tv)
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
diff --git a/drivers/media/rc/keymaps/rc-rc6-mce.c b/drivers/media/rc/keymaps/rc-rc6-mce.c
index 2f5dc0622b9..ef4006fe4de 100644
--- a/drivers/media/rc/keymaps/rc-rc6-mce.c
+++ b/drivers/media/rc/keymaps/rc-rc6-mce.c
@@ -13,6 +13,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table rc6_mce[] = {
@@ -29,8 +30,8 @@ static struct rc_map_table rc6_mce[] = {
{ 0x800f040a, KEY_DELETE },
{ 0x800f040b, KEY_ENTER },
- { 0x800f040c, KEY_POWER }, /* PC Power */
- { 0x800f040d, KEY_PROG1 }, /* Windows MCE button */
+ { 0x800f040c, KEY_SLEEP }, /* Formerly PC Power */
+ { 0x800f040d, KEY_MEDIA }, /* Windows MCE button */
{ 0x800f040e, KEY_MUTE },
{ 0x800f040f, KEY_INFO },
@@ -44,7 +45,6 @@ static struct rc_map_table rc6_mce[] = {
{ 0x800f0416, KEY_PLAY },
{ 0x800f0417, KEY_RECORD },
{ 0x800f0418, KEY_PAUSE },
- { 0x800f046e, KEY_PLAYPAUSE },
{ 0x800f0419, KEY_STOP },
{ 0x800f041a, KEY_NEXT },
{ 0x800f041b, KEY_PREVIOUS },
@@ -87,7 +87,7 @@ static struct rc_map_table rc6_mce[] = {
{ 0x800f0465, KEY_POWER2 }, /* TV Power */
{ 0x800f046e, KEY_PLAYPAUSE },
- { 0x800f046f, KEY_MEDIA }, /* Start media application (NEW) */
+ { 0x800f046f, KEY_PLAYER }, /* Start media application (NEW) */
{ 0x800f0480, KEY_BRIGHTNESSDOWN },
{ 0x800f0481, KEY_PLAYPAUSE },
@@ -97,7 +97,7 @@ static struct rc_map_list rc6_mce_map = {
.map = {
.scan = rc6_mce,
.size = ARRAY_SIZE(rc6_mce),
- .rc_type = RC_TYPE_RC6,
+ .rc_type = RC_TYPE_RC6_MCE,
.name = RC_MAP_RC6_MCE,
}
};
diff --git a/drivers/media/rc/keymaps/rc-real-audio-220-32-keys.c b/drivers/media/rc/keymaps/rc-real-audio-220-32-keys.c
index 2d14598592d..9f778bd091d 100644
--- a/drivers/media/rc/keymaps/rc-real-audio-220-32-keys.c
+++ b/drivers/media/rc/keymaps/rc-real-audio-220-32-keys.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Zogis Real Audio 220 - 32 keys IR */
@@ -35,7 +36,7 @@ static struct rc_map_table real_audio_220_32_keys[] = {
{ 0x15, KEY_CHANNELDOWN},
{ 0x16, KEY_ENTER},
- { 0x11, KEY_LIST}, /* Source */
+ { 0x11, KEY_VIDEO}, /* Source */
{ 0x0d, KEY_AUDIO}, /* stereo */
{ 0x0f, KEY_PREVIOUS}, /* Prev */
@@ -75,4 +76,4 @@ module_init(init_rc_map_real_audio_220_32_keys)
module_exit(exit_rc_map_real_audio_220_32_keys)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-reddo.c b/drivers/media/rc/keymaps/rc-reddo.c
new file mode 100644
index 00000000000..b80b336e928
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-reddo.c
@@ -0,0 +1,86 @@
+/*
+ * MSI DIGIVOX mini III remote controller keytable
+ *
+ * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/*
+ * Derived from MSI DIGIVOX mini III remote (rc-msi-digivox-iii.c)
+ *
+ * Differences between these remotes are:
+ *
+ * 1) scancode 0x61d601 is mapped to different button:
+ * MSI DIGIVOX mini III "Source" = KEY_VIDEO
+ * Reddo "EPG" = KEY_EPG
+ *
+ * 2) Reddo remote has less buttons. Missing buttons are: colored buttons,
+ * navigation buttons and main power button.
+ */
+
+static struct rc_map_table reddo[] = {
+ { 0x61d601, KEY_EPG }, /* EPG */
+ { 0x61d602, KEY_3 },
+ { 0x61d604, KEY_1 },
+ { 0x61d605, KEY_5 },
+ { 0x61d606, KEY_6 },
+ { 0x61d607, KEY_CHANNELDOWN }, /* CH- */
+ { 0x61d608, KEY_2 },
+ { 0x61d609, KEY_CHANNELUP }, /* CH+ */
+ { 0x61d60a, KEY_9 },
+ { 0x61d60b, KEY_ZOOM }, /* Zoom */
+ { 0x61d60c, KEY_7 },
+ { 0x61d60d, KEY_8 },
+ { 0x61d60e, KEY_VOLUMEUP }, /* Vol+ */
+ { 0x61d60f, KEY_4 },
+ { 0x61d610, KEY_ESC }, /* [back up arrow] */
+ { 0x61d611, KEY_0 },
+ { 0x61d612, KEY_OK }, /* [enter arrow] */
+ { 0x61d613, KEY_VOLUMEDOWN }, /* Vol- */
+ { 0x61d614, KEY_RECORD }, /* Rec */
+ { 0x61d615, KEY_STOP }, /* Stop */
+ { 0x61d616, KEY_PLAY }, /* Play */
+ { 0x61d617, KEY_MUTE }, /* Mute */
+ { 0x61d643, KEY_POWER2 }, /* [red power button] */
+};
+
+static struct rc_map_list reddo_map = {
+ .map = {
+ .scan = reddo,
+ .size = ARRAY_SIZE(reddo),
+ .rc_type = RC_TYPE_NEC,
+ .name = RC_MAP_REDDO,
+ }
+};
+
+static int __init init_rc_map_reddo(void)
+{
+ return rc_map_register(&reddo_map);
+}
+
+static void __exit exit_rc_map_reddo(void)
+{
+ rc_map_unregister(&reddo_map);
+}
+
+module_init(init_rc_map_reddo)
+module_exit(exit_rc_map_reddo)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
diff --git a/drivers/media/rc/keymaps/rc-snapstream-firefly.c b/drivers/media/rc/keymaps/rc-snapstream-firefly.c
new file mode 100644
index 00000000000..c7f33ec719b
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-snapstream-firefly.c
@@ -0,0 +1,107 @@
+/*
+ * SnapStream Firefly X10 RF remote keytable
+ *
+ * Copyright (C) 2011 Anssi Hannula <anssi.hannula@?ki.fi>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/module.h>
+#include <media/rc-map.h>
+
+static struct rc_map_table snapstream_firefly[] = {
+ { 0x2c, KEY_ZOOM }, /* Maximize */
+ { 0x02, KEY_CLOSE },
+
+ { 0x0d, KEY_1 },
+ { 0x0e, KEY_2 },
+ { 0x0f, KEY_3 },
+ { 0x10, KEY_4 },
+ { 0x11, KEY_5 },
+ { 0x12, KEY_6 },
+ { 0x13, KEY_7 },
+ { 0x14, KEY_8 },
+ { 0x15, KEY_9 },
+ { 0x17, KEY_0 },
+ { 0x16, KEY_BACK },
+ { 0x18, KEY_KPENTER }, /* ent */
+
+ { 0x09, KEY_VOLUMEUP },
+ { 0x08, KEY_VOLUMEDOWN },
+ { 0x0a, KEY_MUTE },
+ { 0x0b, KEY_CHANNELUP },
+ { 0x0c, KEY_CHANNELDOWN },
+ { 0x00, KEY_VENDOR }, /* firefly */
+
+ { 0x2e, KEY_INFO },
+ { 0x2f, KEY_OPTION },
+
+ { 0x1d, KEY_LEFT },
+ { 0x1f, KEY_RIGHT },
+ { 0x22, KEY_DOWN },
+ { 0x1a, KEY_UP },
+ { 0x1e, KEY_OK },
+
+ { 0x1c, KEY_MENU },
+ { 0x20, KEY_EXIT },
+
+ { 0x27, KEY_RECORD },
+ { 0x25, KEY_PLAY },
+ { 0x28, KEY_STOP },
+ { 0x24, KEY_REWIND },
+ { 0x26, KEY_FORWARD },
+ { 0x29, KEY_PAUSE },
+ { 0x2b, KEY_PREVIOUS },
+ { 0x2a, KEY_NEXT },
+
+ { 0x06, KEY_AUDIO }, /* Music */
+ { 0x05, KEY_IMAGES }, /* Photos */
+ { 0x04, KEY_DVD },
+ { 0x03, KEY_TV },
+ { 0x07, KEY_VIDEO },
+
+ { 0x01, KEY_HELP },
+ { 0x2d, KEY_MODE }, /* Mouse */
+
+ { 0x19, KEY_A },
+ { 0x1b, KEY_B },
+ { 0x21, KEY_C },
+ { 0x23, KEY_D },
+};
+
+static struct rc_map_list snapstream_firefly_map = {
+ .map = {
+ .scan = snapstream_firefly,
+ .size = ARRAY_SIZE(snapstream_firefly),
+ .rc_type = RC_TYPE_OTHER,
+ .name = RC_MAP_SNAPSTREAM_FIREFLY,
+ }
+};
+
+static int __init init_rc_map_snapstream_firefly(void)
+{
+ return rc_map_register(&snapstream_firefly_map);
+}
+
+static void __exit exit_rc_map_snapstream_firefly(void)
+{
+ rc_map_unregister(&snapstream_firefly_map);
+}
+
+module_init(init_rc_map_snapstream_firefly)
+module_exit(exit_rc_map_snapstream_firefly)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Anssi Hannula <anssi.hannula@iki.fi>");
diff --git a/drivers/media/rc/keymaps/rc-streamzap.c b/drivers/media/rc/keymaps/rc-streamzap.c
index 92cc10d2f9c..f9a07578d98 100644
--- a/drivers/media/rc/keymaps/rc-streamzap.c
+++ b/drivers/media/rc/keymaps/rc-streamzap.c
@@ -10,6 +10,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table streamzap[] = {
/*
diff --git a/drivers/media/rc/keymaps/rc-su3000.c b/drivers/media/rc/keymaps/rc-su3000.c
new file mode 100644
index 00000000000..8dbd3e9bc95
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-su3000.c
@@ -0,0 +1,75 @@
+/* rc-su3000.h - Keytable for Geniatech HDStar Remote Controller
+ *
+ * Copyright (c) 2013 by Evgeny Plehov <Evgeny Plehov@ukr.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.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table su3000[] = {
+ { 0x25, KEY_POWER }, /* right-bottom Red */
+ { 0x0a, KEY_MUTE }, /* -/-- */
+ { 0x01, KEY_1 },
+ { 0x02, KEY_2 },
+ { 0x03, KEY_3 },
+ { 0x04, KEY_4 },
+ { 0x05, KEY_5 },
+ { 0x06, KEY_6 },
+ { 0x07, KEY_7 },
+ { 0x08, KEY_8 },
+ { 0x09, KEY_9 },
+ { 0x00, KEY_0 },
+ { 0x20, KEY_UP }, /* CH+ */
+ { 0x21, KEY_DOWN }, /* CH+ */
+ { 0x12, KEY_VOLUMEUP }, /* Brightness Up */
+ { 0x13, KEY_VOLUMEDOWN },/* Brightness Down */
+ { 0x1f, KEY_RECORD },
+ { 0x17, KEY_PLAY },
+ { 0x16, KEY_PAUSE },
+ { 0x0b, KEY_STOP },
+ { 0x27, KEY_FASTFORWARD },/* >> */
+ { 0x26, KEY_REWIND }, /* << */
+ { 0x0d, KEY_OK }, /* Mute */
+ { 0x11, KEY_LEFT }, /* VOL- */
+ { 0x10, KEY_RIGHT }, /* VOL+ */
+ { 0x29, KEY_BACK }, /* button under 9 */
+ { 0x2c, KEY_MENU }, /* TTX */
+ { 0x2b, KEY_EPG }, /* EPG */
+ { 0x1e, KEY_RED }, /* OSD */
+ { 0x0e, KEY_GREEN }, /* Window */
+ { 0x2d, KEY_YELLOW }, /* button under << */
+ { 0x0f, KEY_BLUE }, /* bottom yellow button */
+ { 0x14, KEY_AUDIO }, /* Snapshot */
+ { 0x38, KEY_TV }, /* TV/Radio */
+ { 0x0c, KEY_ESC } /* upper Red button */
+};
+
+static struct rc_map_list su3000_map = {
+ .map = {
+ .scan = su3000,
+ .size = ARRAY_SIZE(su3000),
+ .rc_type = RC_TYPE_RC5,
+ .name = RC_MAP_SU3000,
+ }
+};
+
+static int __init init_rc_map_su3000(void)
+{
+ return rc_map_register(&su3000_map);
+}
+
+static void __exit exit_rc_map_su3000(void)
+{
+ rc_map_unregister(&su3000_map);
+}
+
+module_init(init_rc_map_su3000)
+module_exit(exit_rc_map_su3000)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Evgeny Plehov <Evgeny Plehov@ukr.net>");
diff --git a/drivers/media/rc/keymaps/rc-tbs-nec.c b/drivers/media/rc/keymaps/rc-tbs-nec.c
index 7242ee66f6e..24ce2a25250 100644
--- a/drivers/media/rc/keymaps/rc-tbs-nec.c
+++ b/drivers/media/rc/keymaps/rc-tbs-nec.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table tbs_nec[] = {
{ 0x84, KEY_POWER2}, /* power */
@@ -72,4 +73,4 @@ module_init(init_rc_map_tbs_nec)
module_exit(exit_rc_map_tbs_nec)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-technisat-usb2.c b/drivers/media/rc/keymaps/rc-technisat-usb2.c
new file mode 100644
index 00000000000..f9733bb289d
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-technisat-usb2.c
@@ -0,0 +1,94 @@
+/* rc-technisat-usb2.c - Keytable for SkyStar HD USB
+ *
+ * Copyright (C) 2010 Patrick Boettcher,
+ * Kernel Labs Inc. PO Box 745, St James, NY 11780
+ *
+ * Development was sponsored by Technisat Digital UK Limited, whose
+ * registered office is Witan Gate House 500 - 600 Witan Gate West,
+ * Milton Keynes, MK9 1SH
+ *
+ * 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.
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * THIS PROGRAM IS PROVIDED "AS IS" AND BOTH THE COPYRIGHT HOLDER AND
+ * TECHNISAT DIGITAL UK LTD DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS PROGRAM INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE. NEITHER THE COPYRIGHT HOLDER
+ * NOR TECHNISAT DIGITAL UK LIMITED SHALL BE LIABLE FOR ANY SPECIAL,
+ * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS PROGRAM. See the
+ * GNU General Public License for more details.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table technisat_usb2[] = {
+ {0x0a0c, KEY_POWER},
+ {0x0a01, KEY_1},
+ {0x0a02, KEY_2},
+ {0x0a03, KEY_3},
+ {0x0a0d, KEY_MUTE},
+ {0x0a04, KEY_4},
+ {0x0a05, KEY_5},
+ {0x0a06, KEY_6},
+ {0x0a38, KEY_VIDEO}, /* EXT */
+ {0x0a07, KEY_7},
+ {0x0a08, KEY_8},
+ {0x0a09, KEY_9},
+ {0x0a00, KEY_0},
+ {0x0a4f, KEY_INFO},
+ {0x0a20, KEY_CHANNELUP},
+ {0x0a52, KEY_MENU},
+ {0x0a11, KEY_VOLUMEUP},
+ {0x0a57, KEY_OK},
+ {0x0a10, KEY_VOLUMEDOWN},
+ {0x0a2f, KEY_EPG},
+ {0x0a21, KEY_CHANNELDOWN},
+ {0x0a22, KEY_REFRESH},
+ {0x0a3c, KEY_TEXT},
+ {0x0a76, KEY_ENTER}, /* HOOK */
+ {0x0a0f, KEY_HELP},
+ {0x0a6b, KEY_RED},
+ {0x0a6c, KEY_GREEN},
+ {0x0a6d, KEY_YELLOW},
+ {0x0a6e, KEY_BLUE},
+ {0x0a29, KEY_STOP},
+ {0x0a23, KEY_LANGUAGE},
+ {0x0a53, KEY_TV},
+ {0x0a0a, KEY_PROGRAM},
+};
+
+static struct rc_map_list technisat_usb2_map = {
+ .map = {
+ .scan = technisat_usb2,
+ .size = ARRAY_SIZE(technisat_usb2),
+ .rc_type = RC_TYPE_RC5,
+ .name = RC_MAP_TECHNISAT_USB2,
+ }
+};
+
+static int __init init_rc_map(void)
+{
+ return rc_map_register(&technisat_usb2_map);
+}
+
+static void __exit exit_rc_map(void)
+{
+ rc_map_unregister(&technisat_usb2_map);
+}
+
+module_init(init_rc_map)
+module_exit(exit_rc_map)
+
+MODULE_AUTHOR("Patrick Boettcher <pboettcher@kernellabs.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/rc/keymaps/rc-terratec-cinergy-xs.c b/drivers/media/rc/keymaps/rc-terratec-cinergy-xs.c
index bc38e34b9fd..97eb83ab5a3 100644
--- a/drivers/media/rc/keymaps/rc-terratec-cinergy-xs.c
+++ b/drivers/media/rc/keymaps/rc-terratec-cinergy-xs.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Terratec Cinergy Hybrid T USB XS
Devin Heitmueller <dheitmueller@linuxtv.org>
@@ -89,4 +90,4 @@ module_init(init_rc_map_terratec_cinergy_xs)
module_exit(exit_rc_map_terratec_cinergy_xs)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-terratec-slim-2.c b/drivers/media/rc/keymaps/rc-terratec-slim-2.c
new file mode 100644
index 00000000000..4c149ef712d
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-terratec-slim-2.c
@@ -0,0 +1,73 @@
+/*
+ * TerraTec remote controller keytable
+ *
+ * Copyright (C) 2011 Martin Groszhauser <mgroszhauser@gmail.com>
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/*
+ * TerraTec slim remote, 6 rows, 3 columns.
+ * Keytable from Martin Groszhauser <mgroszhauser@gmail.com>
+ */
+static struct rc_map_table terratec_slim_2[] = {
+ { 0x8001, KEY_MUTE }, /* MUTE */
+ { 0x8002, KEY_VOLUMEDOWN },
+ { 0x8003, KEY_CHANNELDOWN },
+ { 0x8004, KEY_1 },
+ { 0x8005, KEY_2 },
+ { 0x8006, KEY_3 },
+ { 0x8007, KEY_4 },
+ { 0x8008, KEY_5 },
+ { 0x8009, KEY_6 },
+ { 0x800a, KEY_7 },
+ { 0x800c, KEY_ZOOM }, /* [fullscreen] */
+ { 0x800d, KEY_0 },
+ { 0x800e, KEY_AGAIN }, /* [two arrows forming a circle] */
+ { 0x8012, KEY_POWER2 }, /* [red power button] */
+ { 0x801a, KEY_VOLUMEUP },
+ { 0x801b, KEY_8 },
+ { 0x801e, KEY_CHANNELUP },
+ { 0x801f, KEY_9 },
+};
+
+static struct rc_map_list terratec_slim_2_map = {
+ .map = {
+ .scan = terratec_slim_2,
+ .size = ARRAY_SIZE(terratec_slim_2),
+ .rc_type = RC_TYPE_NEC,
+ .name = RC_MAP_TERRATEC_SLIM_2,
+ }
+};
+
+static int __init init_rc_map_terratec_slim_2(void)
+{
+ return rc_map_register(&terratec_slim_2_map);
+}
+
+static void __exit exit_rc_map_terratec_slim_2(void)
+{
+ rc_map_unregister(&terratec_slim_2_map);
+}
+
+module_init(init_rc_map_terratec_slim_2)
+module_exit(exit_rc_map_terratec_slim_2)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
diff --git a/drivers/media/rc/keymaps/rc-terratec-slim.c b/drivers/media/rc/keymaps/rc-terratec-slim.c
index 1abafa5fd30..3d8a19cdb5a 100644
--- a/drivers/media/rc/keymaps/rc-terratec-slim.c
+++ b/drivers/media/rc/keymaps/rc-terratec-slim.c
@@ -19,6 +19,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* TerraTec slim remote, 7 rows, 4 columns. */
/* Uses NEC extended 0x02bd. */
diff --git a/drivers/media/rc/keymaps/rc-tevii-nec.c b/drivers/media/rc/keymaps/rc-tevii-nec.c
index ef5ba3f3273..38e0c087559 100644
--- a/drivers/media/rc/keymaps/rc-tevii-nec.c
+++ b/drivers/media/rc/keymaps/rc-tevii-nec.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table tevii_nec[] = {
{ 0x0a, KEY_POWER2},
@@ -85,4 +86,4 @@ module_init(init_rc_map_tevii_nec)
module_exit(exit_rc_map_tevii_nec)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-tivo.c b/drivers/media/rc/keymaps/rc-tivo.c
new file mode 100644
index 00000000000..454e0629569
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-tivo.c
@@ -0,0 +1,99 @@
+/* rc-tivo.c - Keytable for TiVo remotes
+ *
+ * Copyright (c) 2011 by Jarod Wilson <jarod@redhat.com>
+ *
+ * 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.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/*
+ * Initial mapping is for the TiVo remote included in the Nero LiquidTV bundle,
+ * which also ships with a TiVo-branded IR transceiver, supported by the mceusb
+ * driver. Note that the remote uses an NEC-ish protocol, but instead of having
+ * a command/not_command pair, it has a vendor ID of 0xa10c, but some keys, the
+ * NEC extended checksums do pass, so the table presently has the intended
+ * values and the checksum-passed versions for those keys.
+ */
+static struct rc_map_table tivo[] = {
+ { 0xa10c900f, KEY_MEDIA }, /* TiVo Button */
+ { 0xa10c0807, KEY_POWER2 }, /* TV Power */
+ { 0xa10c8807, KEY_TV }, /* Live TV/Swap */
+ { 0xa10c2c03, KEY_VIDEO_NEXT }, /* TV Input */
+ { 0xa10cc807, KEY_INFO },
+ { 0xa10cfa05, KEY_CYCLEWINDOWS }, /* Window */
+ { 0x0085305f, KEY_CYCLEWINDOWS },
+ { 0xa10c6c03, KEY_EPG }, /* Guide */
+
+ { 0xa10c2807, KEY_UP },
+ { 0xa10c6807, KEY_DOWN },
+ { 0xa10ce807, KEY_LEFT },
+ { 0xa10ca807, KEY_RIGHT },
+
+ { 0xa10c1807, KEY_SCROLLDOWN }, /* Red Thumbs Down */
+ { 0xa10c9807, KEY_SELECT },
+ { 0xa10c5807, KEY_SCROLLUP }, /* Green Thumbs Up */
+
+ { 0xa10c3807, KEY_VOLUMEUP },
+ { 0xa10cb807, KEY_VOLUMEDOWN },
+ { 0xa10cd807, KEY_MUTE },
+ { 0xa10c040b, KEY_RECORD },
+ { 0xa10c7807, KEY_CHANNELUP },
+ { 0xa10cf807, KEY_CHANNELDOWN },
+ { 0x0085301f, KEY_CHANNELDOWN },
+
+ { 0xa10c840b, KEY_PLAY },
+ { 0xa10cc40b, KEY_PAUSE },
+ { 0xa10ca40b, KEY_SLOW },
+ { 0xa10c440b, KEY_REWIND },
+ { 0xa10c240b, KEY_FASTFORWARD },
+ { 0xa10c640b, KEY_PREVIOUS },
+ { 0xa10ce40b, KEY_NEXT }, /* ->| */
+
+ { 0xa10c220d, KEY_ZOOM }, /* Aspect */
+ { 0xa10c120d, KEY_STOP },
+ { 0xa10c520d, KEY_DVD }, /* DVD Menu */
+
+ { 0xa10c140b, KEY_NUMERIC_1 },
+ { 0xa10c940b, KEY_NUMERIC_2 },
+ { 0xa10c540b, KEY_NUMERIC_3 },
+ { 0xa10cd40b, KEY_NUMERIC_4 },
+ { 0xa10c340b, KEY_NUMERIC_5 },
+ { 0xa10cb40b, KEY_NUMERIC_6 },
+ { 0xa10c740b, KEY_NUMERIC_7 },
+ { 0xa10cf40b, KEY_NUMERIC_8 },
+ { 0x0085302f, KEY_NUMERIC_8 },
+ { 0xa10c0c03, KEY_NUMERIC_9 },
+ { 0xa10c8c03, KEY_NUMERIC_0 },
+ { 0xa10ccc03, KEY_ENTER },
+ { 0xa10c4c03, KEY_CLEAR },
+};
+
+static struct rc_map_list tivo_map = {
+ .map = {
+ .scan = tivo,
+ .size = ARRAY_SIZE(tivo),
+ .rc_type = RC_TYPE_NEC,
+ .name = RC_MAP_TIVO,
+ }
+};
+
+static int __init init_rc_map_tivo(void)
+{
+ return rc_map_register(&tivo_map);
+}
+
+static void __exit exit_rc_map_tivo(void)
+{
+ rc_map_unregister(&tivo_map);
+}
+
+module_init(init_rc_map_tivo)
+module_exit(exit_rc_map_tivo)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
diff --git a/drivers/media/rc/keymaps/rc-total-media-in-hand-02.c b/drivers/media/rc/keymaps/rc-total-media-in-hand-02.c
new file mode 100644
index 00000000000..47270f72ebf
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-total-media-in-hand-02.c
@@ -0,0 +1,86 @@
+/*
+ * Total Media In Hand_02 remote controller keytable for Mygica X8507
+ *
+ * Copyright (C) 2012 Alfredo J. Delaiti <alfredodelaiti@netscape.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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+
+static struct rc_map_table total_media_in_hand_02[] = {
+ { 0x0000, KEY_0 },
+ { 0x0001, KEY_1 },
+ { 0x0002, KEY_2 },
+ { 0x0003, KEY_3 },
+ { 0x0004, KEY_4 },
+ { 0x0005, KEY_5 },
+ { 0x0006, KEY_6 },
+ { 0x0007, KEY_7 },
+ { 0x0008, KEY_8 },
+ { 0x0009, KEY_9 },
+ { 0x000a, KEY_MUTE },
+ { 0x000b, KEY_STOP }, /* Stop */
+ { 0x000c, KEY_POWER2 }, /* Turn on/off application */
+ { 0x000d, KEY_OK }, /* OK */
+ { 0x000e, KEY_CAMERA }, /* Snapshot */
+ { 0x000f, KEY_ZOOM }, /* Full Screen/Restore */
+ { 0x0010, KEY_RIGHT }, /* Right arrow */
+ { 0x0011, KEY_LEFT }, /* Left arrow */
+ { 0x0012, KEY_CHANNELUP },
+ { 0x0013, KEY_CHANNELDOWN },
+ { 0x0014, KEY_SHUFFLE },
+ { 0x0016, KEY_PAUSE },
+ { 0x0017, KEY_PLAY }, /* Play */
+ { 0x001e, KEY_TIME }, /* Time Shift */
+ { 0x001f, KEY_RECORD },
+ { 0x0020, KEY_UP },
+ { 0x0021, KEY_DOWN },
+ { 0x0025, KEY_POWER }, /* Turn off computer */
+ { 0x0026, KEY_REWIND }, /* FR << */
+ { 0x0027, KEY_FASTFORWARD }, /* FF >> */
+ { 0x0029, KEY_ESC },
+ { 0x002b, KEY_VOLUMEUP },
+ { 0x002c, KEY_VOLUMEDOWN },
+ { 0x002d, KEY_CHANNEL }, /* CH Surfing */
+ { 0x0038, KEY_VIDEO }, /* TV/AV/S-Video/YPbPr */
+};
+
+static struct rc_map_list total_media_in_hand_02_map = {
+ .map = {
+ .scan = total_media_in_hand_02,
+ .size = ARRAY_SIZE(total_media_in_hand_02),
+ .rc_type = RC_TYPE_RC5,
+ .name = RC_MAP_TOTAL_MEDIA_IN_HAND_02,
+ }
+};
+
+static int __init init_rc_map_total_media_in_hand_02(void)
+{
+ return rc_map_register(&total_media_in_hand_02_map);
+}
+
+static void __exit exit_rc_map_total_media_in_hand_02(void)
+{
+ rc_map_unregister(&total_media_in_hand_02_map);
+}
+
+module_init(init_rc_map_total_media_in_hand_02)
+module_exit(exit_rc_map_total_media_in_hand_02)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR(" Alfredo J. Delaiti <alfredodelaiti@netscape.net>");
diff --git a/drivers/media/rc/keymaps/rc-total-media-in-hand.c b/drivers/media/rc/keymaps/rc-total-media-in-hand.c
index 20ac4e19fb3..5b9f9ec1368 100644
--- a/drivers/media/rc/keymaps/rc-total-media-in-hand.c
+++ b/drivers/media/rc/keymaps/rc-total-media-in-hand.c
@@ -19,6 +19,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Uses NEC extended 0x02bd */
static struct rc_map_table total_media_in_hand[] = {
diff --git a/drivers/media/rc/keymaps/rc-trekstor.c b/drivers/media/rc/keymaps/rc-trekstor.c
index f8190ead2e3..f9a2e0fabb9 100644
--- a/drivers/media/rc/keymaps/rc-trekstor.c
+++ b/drivers/media/rc/keymaps/rc-trekstor.c
@@ -19,6 +19,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* TrekStor DVB-T USB Stick remote controller. */
/* Imported from af9015.h.
diff --git a/drivers/media/rc/keymaps/rc-tt-1500.c b/drivers/media/rc/keymaps/rc-tt-1500.c
index 295f3738e30..c766d3b2b6b 100644
--- a/drivers/media/rc/keymaps/rc-tt-1500.c
+++ b/drivers/media/rc/keymaps/rc-tt-1500.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* for the Technotrend 1500 bundled remotes (grey and black): */
@@ -60,7 +61,7 @@ static struct rc_map_list tt_1500_map = {
.map = {
.scan = tt_1500,
.size = ARRAY_SIZE(tt_1500),
- .rc_type = RC_TYPE_UNKNOWN, /* Legacy IR type */
+ .rc_type = RC_TYPE_RC5,
.name = RC_MAP_TT_1500,
}
};
@@ -79,4 +80,4 @@ module_init(init_rc_map_tt_1500)
module_exit(exit_rc_map_tt_1500)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-twinhan1027.c b/drivers/media/rc/keymaps/rc-twinhan1027.c
index 8bf8df64b08..509299b90c9 100644
--- a/drivers/media/rc/keymaps/rc-twinhan1027.c
+++ b/drivers/media/rc/keymaps/rc-twinhan1027.c
@@ -1,4 +1,5 @@
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table twinhan_vp1027[] = {
{ 0x16, KEY_POWER2 },
diff --git a/drivers/media/rc/keymaps/rc-videomate-m1f.c b/drivers/media/rc/keymaps/rc-videomate-m1f.c
index 4994d405c0a..23ee05e5394 100644
--- a/drivers/media/rc/keymaps/rc-videomate-m1f.c
+++ b/drivers/media/rc/keymaps/rc-videomate-m1f.c
@@ -1,4 +1,4 @@
-/* videomate-m1f.h - Keytable for videomate_m1f Remote Controller
+/* videomate-k100.h - Keytable for videomate_k100 Remote Controller
*
* keymap imported from ir-keymaps.c
*
@@ -11,8 +11,9 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
-static struct rc_map_table videomate_m1f[] = {
+static struct rc_map_table videomate_k100[] = {
{ 0x01, KEY_POWER },
{ 0x31, KEY_TUNER },
{ 0x33, KEY_VIDEO },
@@ -66,27 +67,27 @@ static struct rc_map_table videomate_m1f[] = {
{ 0x18, KEY_TEXT },
};
-static struct rc_map_list videomate_m1f_map = {
+static struct rc_map_list videomate_k100_map = {
.map = {
- .scan = videomate_m1f,
- .size = ARRAY_SIZE(videomate_m1f),
+ .scan = videomate_k100,
+ .size = ARRAY_SIZE(videomate_k100),
.rc_type = RC_TYPE_UNKNOWN, /* Legacy IR type */
- .name = RC_MAP_VIDEOMATE_M1F,
+ .name = RC_MAP_VIDEOMATE_K100,
}
};
-static int __init init_rc_map_videomate_m1f(void)
+static int __init init_rc_map_videomate_k100(void)
{
- return rc_map_register(&videomate_m1f_map);
+ return rc_map_register(&videomate_k100_map);
}
-static void __exit exit_rc_map_videomate_m1f(void)
+static void __exit exit_rc_map_videomate_k100(void)
{
- rc_map_unregister(&videomate_m1f_map);
+ rc_map_unregister(&videomate_k100_map);
}
-module_init(init_rc_map_videomate_m1f)
-module_exit(exit_rc_map_videomate_m1f)
+module_init(init_rc_map_videomate_k100)
+module_exit(exit_rc_map_videomate_k100)
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Pavel Osnova <pvosnova@gmail.com>");
diff --git a/drivers/media/rc/keymaps/rc-videomate-s350.c b/drivers/media/rc/keymaps/rc-videomate-s350.c
index 9e474a6024e..8a354775a2d 100644
--- a/drivers/media/rc/keymaps/rc-videomate-s350.c
+++ b/drivers/media/rc/keymaps/rc-videomate-s350.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table videomate_s350[] = {
{ 0x00, KEY_TV},
@@ -82,4 +83,4 @@ module_init(init_rc_map_videomate_s350)
module_exit(exit_rc_map_videomate_s350)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-videomate-tv-pvr.c b/drivers/media/rc/keymaps/rc-videomate-tv-pvr.c
index 5f2a46e1f8f..eb0cda7766c 100644
--- a/drivers/media/rc/keymaps/rc-videomate-tv-pvr.c
+++ b/drivers/media/rc/keymaps/rc-videomate-tv-pvr.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
static struct rc_map_table videomate_tv_pvr[] = {
{ 0x14, KEY_MUTE },
@@ -84,4 +85,4 @@ module_init(init_rc_map_videomate_tv_pvr)
module_exit(exit_rc_map_videomate_tv_pvr)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-winfast-usbii-deluxe.c b/drivers/media/rc/keymaps/rc-winfast-usbii-deluxe.c
index bd8d021f40a..c1dd598e828 100644
--- a/drivers/media/rc/keymaps/rc-winfast-usbii-deluxe.c
+++ b/drivers/media/rc/keymaps/rc-winfast-usbii-deluxe.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Leadtek Winfast TV USB II Deluxe remote
Magnus Alm <magnus.alm@gmail.com>
@@ -79,4 +80,4 @@ module_init(init_rc_map_winfast_usbii_deluxe)
module_exit(exit_rc_map_winfast_usbii_deluxe)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-winfast.c b/drivers/media/rc/keymaps/rc-winfast.c
index 2747db43b70..8a779da1e97 100644
--- a/drivers/media/rc/keymaps/rc-winfast.c
+++ b/drivers/media/rc/keymaps/rc-winfast.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -11,6 +11,7 @@
*/
#include <media/rc-map.h>
+#include <linux/module.h>
/* Table for Leadtek Winfast Remote Controls - used by both bttv and cx88 */
@@ -27,15 +28,15 @@ static struct rc_map_table winfast[] = {
{ 0x0e, KEY_8 },
{ 0x0f, KEY_9 },
- { 0x00, KEY_POWER },
+ { 0x00, KEY_POWER2 },
{ 0x1b, KEY_AUDIO }, /* Audio Source */
{ 0x02, KEY_TUNER }, /* TV/FM, not on Y0400052 */
{ 0x1e, KEY_VIDEO }, /* Video Source */
{ 0x16, KEY_INFO }, /* Display information */
- { 0x04, KEY_VOLUMEUP },
- { 0x08, KEY_VOLUMEDOWN },
- { 0x0c, KEY_CHANNELUP },
- { 0x10, KEY_CHANNELDOWN },
+ { 0x04, KEY_RIGHT },
+ { 0x08, KEY_LEFT },
+ { 0x0c, KEY_UP },
+ { 0x10, KEY_DOWN },
{ 0x03, KEY_ZOOM }, /* fullscreen */
{ 0x1f, KEY_TEXT }, /* closed caption/teletext */
{ 0x20, KEY_SLEEP },
@@ -47,7 +48,7 @@ static struct rc_map_table winfast[] = {
{ 0x2e, KEY_BLUE },
{ 0x18, KEY_KPPLUS }, /* fine tune + , not on Y040052 */
{ 0x19, KEY_KPMINUS }, /* fine tune - , not on Y040052 */
- { 0x2a, KEY_MEDIA }, /* PIP (Picture in picture */
+ { 0x2a, KEY_TV2 }, /* PIP (Picture in picture */
{ 0x21, KEY_DOT },
{ 0x13, KEY_ENTER },
{ 0x11, KEY_LAST }, /* Recall (last channel */
@@ -57,7 +58,7 @@ static struct rc_map_table winfast[] = {
{ 0x25, KEY_TIME }, /* Time Shifting */
{ 0x26, KEY_STOP },
{ 0x27, KEY_RECORD },
- { 0x28, KEY_SAVE }, /* Screenshot */
+ { 0x28, KEY_CAMERA }, /* Screenshot */
{ 0x2f, KEY_MENU },
{ 0x30, KEY_CANCEL },
{ 0x31, KEY_CHANNEL }, /* Channel Surf */
@@ -70,10 +71,10 @@ static struct rc_map_table winfast[] = {
{ 0x38, KEY_DVD },
{ 0x1a, KEY_MODE}, /* change to MCE mode on Y04G0051 */
- { 0x3e, KEY_F21 }, /* MCE +VOL, on Y04G0033 */
- { 0x3a, KEY_F22 }, /* MCE -VOL, on Y04G0033 */
- { 0x3b, KEY_F23 }, /* MCE +CH, on Y04G0033 */
- { 0x3f, KEY_F24 } /* MCE -CH, on Y04G0033 */
+ { 0x3e, KEY_VOLUMEUP }, /* MCE +VOL, on Y04G0033 */
+ { 0x3a, KEY_VOLUMEDOWN }, /* MCE -VOL, on Y04G0033 */
+ { 0x3b, KEY_CHANNELUP }, /* MCE +CH, on Y04G0033 */
+ { 0x3f, KEY_CHANNELDOWN } /* MCE -CH, on Y04G0033 */
};
static struct rc_map_list winfast_map = {
@@ -99,4 +100,4 @@ module_init(init_rc_map_winfast)
module_exit(exit_rc_map_winfast)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c
index fd237ab120b..dc5cbffcd5a 100644
--- a/drivers/media/rc/lirc_dev.c
+++ b/drivers/media/rc/lirc_dev.c
@@ -35,10 +35,11 @@
#include <linux/device.h>
#include <linux/cdev.h>
+#include <media/rc-core.h>
#include <media/lirc.h>
#include <media/lirc_dev.h>
-static int debug;
+static bool debug;
#define IRCTL_DEV_NAME "BaseRemoteCtl"
#define NOPLUG -1
@@ -55,6 +56,8 @@ struct irctl {
struct lirc_buffer *buf;
unsigned int chunk_size;
+ struct cdev *cdev;
+
struct task_struct *task;
long jiffies_to_wait;
};
@@ -62,7 +65,6 @@ struct irctl {
static DEFINE_MUTEX(lirc_dev_lock);
static struct irctl *irctls[MAX_IRCTL_DEVICES];
-static struct cdev cdevs[MAX_IRCTL_DEVICES];
/* Only used for sysfs but defined to void otherwise */
static struct class *lirc_class;
@@ -151,7 +153,7 @@ static int lirc_thread(void *irctl)
}
-static struct file_operations lirc_dev_fops = {
+static const struct file_operations lirc_dev_fops = {
.owner = THIS_MODULE,
.read = lirc_dev_fop_read,
.write = lirc_dev_fop_write,
@@ -167,9 +169,13 @@ static struct file_operations lirc_dev_fops = {
static int lirc_cdev_add(struct irctl *ir)
{
- int retval;
+ int retval = -ENOMEM;
struct lirc_driver *d = &ir->d;
- struct cdev *cdev = &cdevs[d->minor];
+ struct cdev *cdev;
+
+ cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
+ if (!cdev)
+ goto err_out;
if (d->fops) {
cdev_init(cdev, d->fops);
@@ -180,12 +186,20 @@ static int lirc_cdev_add(struct irctl *ir)
}
retval = kobject_set_name(&cdev->kobj, "lirc%d", d->minor);
if (retval)
- return retval;
+ goto err_out;
retval = cdev_add(cdev, MKDEV(MAJOR(lirc_base_dev), d->minor), 1);
- if (retval)
+ if (retval) {
kobject_put(&cdev->kobj);
+ goto err_out;
+ }
+ ir->cdev = cdev;
+
+ return 0;
+
+err_out:
+ kfree(cdev);
return retval;
}
@@ -214,7 +228,7 @@ int lirc_register_driver(struct lirc_driver *d)
if (MAX_IRCTL_DEVICES <= d->minor) {
dev_err(d->dev, "lirc_dev: lirc_register_driver: "
"\"minor\" must be between 0 and %d (%d)!\n",
- MAX_IRCTL_DEVICES-1, d->minor);
+ MAX_IRCTL_DEVICES - 1, d->minor);
err = -EBADRQC;
goto out;
}
@@ -369,7 +383,7 @@ int lirc_unregister_driver(int minor)
if (minor < 0 || minor >= MAX_IRCTL_DEVICES) {
printk(KERN_ERR "lirc_dev: %s: minor (%d) must be between "
- "0 and %d!\n", __func__, minor, MAX_IRCTL_DEVICES-1);
+ "0 and %d!\n", __func__, minor, MAX_IRCTL_DEVICES - 1);
return -EBADRQC;
}
@@ -380,7 +394,7 @@ int lirc_unregister_driver(int minor)
return -ENOENT;
}
- cdev = &cdevs[minor];
+ cdev = ir->cdev;
mutex_lock(&lirc_dev_lock);
@@ -410,6 +424,7 @@ int lirc_unregister_driver(int minor)
} else {
lirc_irctl_cleanup(ir);
cdev_del(cdev);
+ kfree(cdev);
kfree(ir);
irctls[minor] = NULL;
}
@@ -453,7 +468,13 @@ int lirc_dev_fop_open(struct inode *inode, struct file *file)
goto error;
}
- cdev = &cdevs[iminor(inode)];
+ if (ir->d.rdev) {
+ retval = rc_open(ir->d.rdev);
+ if (retval)
+ goto error;
+ }
+
+ cdev = ir->cdev;
if (try_module_get(cdev->owner)) {
ir->open++;
retval = ir->d.set_use_inc(ir->d.data);
@@ -484,17 +505,22 @@ EXPORT_SYMBOL(lirc_dev_fop_open);
int lirc_dev_fop_close(struct inode *inode, struct file *file)
{
struct irctl *ir = irctls[iminor(inode)];
- struct cdev *cdev = &cdevs[iminor(inode)];
+ struct cdev *cdev;
if (!ir) {
printk(KERN_ERR "%s: called with invalid irctl\n", __func__);
return -EINVAL;
}
+ cdev = ir->cdev;
+
dev_dbg(ir->d.dev, LOGHEAD "close called\n", ir->d.name, ir->d.minor);
WARN_ON(mutex_lock_killable(&lirc_dev_lock));
+ if (ir->d.rdev)
+ rc_close(ir->d.rdev);
+
ir->open--;
if (ir->attached) {
ir->d.set_use_dec(ir->d.data);
@@ -503,6 +529,7 @@ int lirc_dev_fop_close(struct inode *inode, struct file *file)
lirc_irctl_cleanup(ir);
cdev_del(cdev);
irctls[ir->d.minor] = NULL;
+ kfree(cdev);
kfree(ir);
}
@@ -514,7 +541,7 @@ EXPORT_SYMBOL(lirc_dev_fop_close);
unsigned int lirc_dev_fop_poll(struct file *file, poll_table *wait)
{
- struct irctl *ir = irctls[iminor(file->f_dentry->d_inode)];
+ struct irctl *ir = irctls[iminor(file_inode(file))];
unsigned int ret;
if (!ir) {
@@ -548,7 +575,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
__u32 mode;
int result = 0;
- struct irctl *ir = irctls[iminor(file->f_dentry->d_inode)];
+ struct irctl *ir = irctls[iminor(file_inode(file))];
if (!ir) {
printk(KERN_ERR "lirc_dev: %s: no irctl found!\n", __func__);
@@ -633,7 +660,7 @@ ssize_t lirc_dev_fop_read(struct file *file,
size_t length,
loff_t *ppos)
{
- struct irctl *ir = irctls[iminor(file->f_dentry->d_inode)];
+ struct irctl *ir = irctls[iminor(file_inode(file))];
unsigned char *buf;
int ret = 0, written = 0;
DECLARE_WAITQUEUE(wait, current);
@@ -735,16 +762,7 @@ EXPORT_SYMBOL(lirc_dev_fop_read);
void *lirc_get_pdata(struct file *file)
{
- void *data = NULL;
-
- if (file && file->f_dentry && file->f_dentry->d_inode &&
- file->f_dentry->d_inode->i_rdev) {
- struct irctl *ir;
- ir = irctls[iminor(file->f_dentry->d_inode)];
- data = ir->d.data;
- }
-
- return data;
+ return irctls[iminor(file_inode(file))]->d.data;
}
EXPORT_SYMBOL(lirc_get_pdata);
@@ -752,7 +770,7 @@ EXPORT_SYMBOL(lirc_get_pdata);
ssize_t lirc_dev_fop_write(struct file *file, const char __user *buffer,
size_t length, loff_t *ppos)
{
- struct irctl *ir = irctls[iminor(file->f_dentry->d_inode)];
+ struct irctl *ir = irctls[iminor(file_inode(file))];
if (!ir) {
printk(KERN_ERR "%s: called with invalid irctl\n", __func__);
diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c
index 6df0a498064..d5c1df3c9db 100644
--- a/drivers/media/rc/mceusb.c
+++ b/drivers/media/rc/mceusb.c
@@ -1,7 +1,7 @@
/*
* Driver for USB Windows Media Center Ed. eHome Infrared Transceivers
*
- * Copyright (c) 2010 by Jarod Wilson <jarod@redhat.com>
+ * Copyright (c) 2010-2011, Jarod Wilson <jarod@redhat.com>
*
* Based on the original lirc_mceusb and lirc_mceusb2 drivers, by Dan
* Conti, Martin Blatter and Daniel Melander, the latter of which was
@@ -15,6 +15,11 @@
* Jon Smirl, which included enhancements and simplifications to the
* incoming IR buffer parsing routines.
*
+ * Updated in July of 2011 with the aid of Microsoft's official
+ * remote/transceiver requirements and specification document, found at
+ * download.microsoft.com, title
+ * Windows-Media-Center-RC-IR-Collection-Green-Button-Specification-03-08-2011-V2.pdf
+ *
*
* 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
@@ -37,10 +42,11 @@
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/usb/input.h>
+#include <linux/pm_wakeup.h>
#include <media/rc-core.h>
-#define DRIVER_VERSION "1.91"
-#define DRIVER_AUTHOR "Jarod Wilson <jarod@wilsonet.com>"
+#define DRIVER_VERSION "1.92"
+#define DRIVER_AUTHOR "Jarod Wilson <jarod@redhat.com>"
#define DRIVER_DESC "Windows Media Center Ed. eHome Infrared Transceiver " \
"device driver"
#define DRIVER_NAME "mceusb"
@@ -56,57 +62,96 @@
#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_IRDATA_TRAILER 0x80 /* End of IR data */
-#define MCE_TX_HEADER_LENGTH 3 /* # of bytes in the initializing tx header */
#define MCE_MAX_CHANNELS 2 /* Two transmitters, hardware dependent? */
#define MCE_DEFAULT_TX_MASK 0x03 /* Vals: TX1=0x01, TX2=0x02, ALL=0x03 */
#define MCE_PULSE_BIT 0x80 /* Pulse bit, MSB set == PULSE else SPACE */
#define MCE_PULSE_MASK 0x7f /* Pulse mask */
#define MCE_MAX_PULSE_LENGTH 0x7f /* Longest transmittable pulse symbol */
-#define MCE_HW_CMD_HEADER 0xff /* MCE hardware command header */
-#define MCE_COMMAND_HEADER 0x9f /* MCE command header */
-#define MCE_COMMAND_MASK 0xe0 /* Mask out command bits */
-#define MCE_COMMAND_NULL 0x00 /* These show up various places... */
-/* if buf[i] & MCE_COMMAND_MASK == 0x80 and buf[i] != MCE_COMMAND_HEADER,
- * then we're looking at a raw IR data sample */
-#define MCE_COMMAND_IRDATA 0x80
-#define MCE_PACKET_LENGTH_MASK 0x1f /* Packet length mask */
-
-/* Sub-commands, which follow MCE_COMMAND_HEADER or MCE_HW_CMD_HEADER */
+/*
+ * The interface between the host and the IR hardware is command-response
+ * based. All commands and responses have a consistent format, where a lead
+ * byte always identifies the type of data following it. The lead byte has
+ * a port value in the 3 highest bits and a length value in the 5 lowest
+ * bits.
+ *
+ * The length field is overloaded, with a value of 11111 indicating that the
+ * following byte is a command or response code, and the length of the entire
+ * message is determined by the code. If the length field is not 11111, then
+ * it specifies the number of bytes of port data that follow.
+ */
+#define MCE_CMD 0x1f
+#define MCE_PORT_IR 0x4 /* (0x4 << 5) | MCE_CMD = 0x9f */
+#define MCE_PORT_SYS 0x7 /* (0x7 << 5) | MCE_CMD = 0xff */
+#define MCE_PORT_SER 0x6 /* 0xc0 thru 0xdf flush & 0x1f bytes */
+#define MCE_PORT_MASK 0xe0 /* Mask out command bits */
+
+/* Command port headers */
+#define MCE_CMD_PORT_IR 0x9f /* IR-related cmd/rsp */
+#define MCE_CMD_PORT_SYS 0xff /* System (non-IR) device cmd/rsp */
+
+/* Commands that set device state (2-4 bytes in length) */
+#define MCE_CMD_RESET 0xfe /* Reset device, 2 bytes */
+#define MCE_CMD_RESUME 0xaa /* Resume device after error, 2 bytes */
+#define MCE_CMD_SETIRCFS 0x06 /* Set tx carrier, 4 bytes */
+#define MCE_CMD_SETIRTIMEOUT 0x0c /* Set timeout, 4 bytes */
+#define MCE_CMD_SETIRTXPORTS 0x08 /* Set tx ports, 3 bytes */
+#define MCE_CMD_SETIRRXPORTEN 0x14 /* Set rx ports, 3 bytes */
+#define MCE_CMD_FLASHLED 0x23 /* Flash receiver LED, 2 bytes */
+
+/* Commands that query device state (all 2 bytes, unless noted) */
+#define MCE_CMD_GETIRCFS 0x07 /* Get carrier */
+#define MCE_CMD_GETIRTIMEOUT 0x0d /* Get timeout */
+#define MCE_CMD_GETIRTXPORTS 0x13 /* Get tx ports */
+#define MCE_CMD_GETIRRXPORTEN 0x15 /* Get rx ports */
+#define MCE_CMD_GETPORTSTATUS 0x11 /* Get tx port status, 3 bytes */
+#define MCE_CMD_GETIRNUMPORTS 0x16 /* Get number of ports */
+#define MCE_CMD_GETWAKESOURCE 0x17 /* Get wake source */
+#define MCE_CMD_GETEMVER 0x22 /* Get emulator interface version */
+#define MCE_CMD_GETDEVDETAILS 0x21 /* Get device details (em ver2 only) */
+#define MCE_CMD_GETWAKESUPPORT 0x20 /* Get wake details (em ver2 only) */
+#define MCE_CMD_GETWAKEVERSION 0x18 /* Get wake pattern (em ver2 only) */
+
+/* Misc commands */
+#define MCE_CMD_NOP 0xff /* No operation */
+
+/* Responses to commands (non-error cases) */
+#define MCE_RSP_EQIRCFS 0x06 /* tx carrier, 4 bytes */
+#define MCE_RSP_EQIRTIMEOUT 0x0c /* rx timeout, 4 bytes */
+#define MCE_RSP_GETWAKESOURCE 0x17 /* wake source, 3 bytes */
+#define MCE_RSP_EQIRTXPORTS 0x08 /* tx port mask, 3 bytes */
+#define MCE_RSP_EQIRRXPORTEN 0x14 /* rx port mask, 3 bytes */
+#define MCE_RSP_GETPORTSTATUS 0x11 /* tx port status, 7 bytes */
+#define MCE_RSP_EQIRRXCFCNT 0x15 /* rx carrier count, 4 bytes */
+#define MCE_RSP_EQIRNUMPORTS 0x16 /* number of ports, 4 bytes */
+#define MCE_RSP_EQWAKESUPPORT 0x20 /* wake capabilities, 3 bytes */
+#define MCE_RSP_EQWAKEVERSION 0x18 /* wake pattern details, 6 bytes */
+#define MCE_RSP_EQDEVDETAILS 0x21 /* device capabilities, 3 bytes */
+#define MCE_RSP_EQEMVER 0x22 /* emulator interface ver, 3 bytes */
+#define MCE_RSP_FLASHLED 0x23 /* success flashing LED, 2 bytes */
+
+/* Responses to error cases, must send MCE_CMD_RESUME to clear them */
+#define MCE_RSP_CMD_ILLEGAL 0xfe /* illegal command for port, 2 bytes */
+#define MCE_RSP_TX_TIMEOUT 0x81 /* tx timed out, 2 bytes */
+
+/* Misc commands/responses not defined in the MCE remote/transceiver spec */
#define MCE_CMD_SIG_END 0x01 /* End of signal */
#define MCE_CMD_PING 0x03 /* Ping device */
#define MCE_CMD_UNKNOWN 0x04 /* Unknown */
#define MCE_CMD_UNKNOWN2 0x05 /* Unknown */
-#define MCE_CMD_S_CARRIER 0x06 /* Set TX carrier frequency */
-#define MCE_CMD_G_CARRIER 0x07 /* Get TX carrier frequency */
-#define MCE_CMD_S_TXMASK 0x08 /* Set TX port bitmask */
#define MCE_CMD_UNKNOWN3 0x09 /* Unknown */
#define MCE_CMD_UNKNOWN4 0x0a /* Unknown */
#define MCE_CMD_G_REVISION 0x0b /* Get hw/sw revision */
-#define MCE_CMD_S_TIMEOUT 0x0c /* Set RX timeout value */
-#define MCE_CMD_G_TIMEOUT 0x0d /* Get RX timeout value */
#define MCE_CMD_UNKNOWN5 0x0e /* Unknown */
#define MCE_CMD_UNKNOWN6 0x0f /* Unknown */
-#define MCE_CMD_G_RXPORTSTS 0x11 /* Get RX port status */
-#define MCE_CMD_G_TXMASK 0x13 /* Set TX port bitmask */
-#define MCE_CMD_S_RXSENSOR 0x14 /* Set RX sensor (std/learning) */
-#define MCE_CMD_G_RXSENSOR 0x15 /* Get RX sensor (std/learning) */
-#define MCE_RSP_PULSE_COUNT 0x15 /* RX pulse count (only if learning) */
-#define MCE_CMD_TX_PORTS 0x16 /* Get number of TX ports */
-#define MCE_CMD_G_WAKESRC 0x17 /* Get wake source */
-#define MCE_CMD_UNKNOWN7 0x18 /* Unknown */
#define MCE_CMD_UNKNOWN8 0x19 /* Unknown */
#define MCE_CMD_UNKNOWN9 0x1b /* Unknown */
-#define MCE_CMD_DEVICE_RESET 0xaa /* Reset the hardware */
-#define MCE_RSP_CMD_INVALID 0xfe /* Invalid command issued */
+#define MCE_CMD_NULL 0x00 /* These show up various places... */
-
-/* module parameters */
-#ifdef CONFIG_USB_DEBUG
-static int debug = 1;
-#else
-static int debug;
-#endif
+/* if buf[i] & MCE_PORT_MASK == 0x80 and buf[i] != MCE_CMD_PORT_IR,
+ * then we're looking at a raw IR data sample */
+#define MCE_COMMAND_IRDATA 0x80
+#define MCE_PACKET_LENGTH_MASK 0x1f /* Packet length mask */
/* general constants */
#define SEND_FLAG_IN_PROGRESS 1
@@ -140,6 +185,8 @@ static int debug;
#define VENDOR_REALTEK 0x0bda
#define VENDOR_TIVO 0x105a
#define VENDOR_CONEXANT 0x0572
+#define VENDOR_TWISTEDMELON 0x2596
+#define VENDOR_HAUPPAUGE 0x2040
enum mceusb_model_type {
MCE_GEN2 = 0, /* Most boards */
@@ -148,6 +195,10 @@ enum mceusb_model_type {
MCE_GEN2_TX_INV,
POLARIS_EVK,
CX_HYBRID_TV,
+ MULTIFUNCTION,
+ TIVO_KIT,
+ MCE_GEN2_NO_TX,
+ HAUPPAUGE_CX_HYBRID_TV,
};
struct mceusb_model {
@@ -155,9 +206,10 @@ struct mceusb_model {
u32 mce_gen2:1;
u32 mce_gen3:1;
u32 tx_mask_normal:1;
- u32 is_polaris:1;
u32 no_tx:1;
+ int ir_intfnum;
+
const char *rc_map; /* Allow specify a per-board map */
const char *name; /* per-board name */
};
@@ -170,6 +222,10 @@ static const struct mceusb_model mceusb_model[] = {
[MCE_GEN2] = {
.mce_gen2 = 1,
},
+ [MCE_GEN2_NO_TX] = {
+ .mce_gen2 = 1,
+ .no_tx = 1,
+ },
[MCE_GEN2_TX_INV] = {
.mce_gen2 = 1,
.tx_mask_normal = 1,
@@ -179,20 +235,31 @@ static const struct mceusb_model mceusb_model[] = {
.tx_mask_normal = 1,
},
[POLARIS_EVK] = {
- .is_polaris = 1,
/*
* In fact, the EVK is shipped without
* remotes, but we should have something handy,
* to allow testing it
*/
- .rc_map = RC_MAP_RC5_HAUPPAUGE_NEW,
+ .rc_map = RC_MAP_HAUPPAUGE,
.name = "Conexant Hybrid TV (cx231xx) MCE IR",
},
[CX_HYBRID_TV] = {
- .is_polaris = 1,
.no_tx = 1, /* tx isn't wired up at all */
.name = "Conexant Hybrid TV (cx231xx) MCE IR",
},
+ [HAUPPAUGE_CX_HYBRID_TV] = {
+ .rc_map = RC_MAP_HAUPPAUGE,
+ .no_tx = 1, /* eeprom says it has no tx */
+ .name = "Conexant Hybrid TV (cx231xx) MCE IR no TX",
+ },
+ [MULTIFUNCTION] = {
+ .mce_gen2 = 1,
+ .ir_intfnum = 2,
+ },
+ [TIVO_KIT] = {
+ .mce_gen2 = 1,
+ .rc_map = RC_MAP_TIVO,
+ },
};
static struct usb_device_id mceusb_dev_table[] = {
@@ -216,8 +283,12 @@ static struct usb_device_id mceusb_dev_table[] = {
{ USB_DEVICE(VENDOR_PHILIPS, 0x206c) },
/* Philips/Spinel plus IR transceiver for ASUS */
{ USB_DEVICE(VENDOR_PHILIPS, 0x2088) },
- /* Realtek MCE IR Receiver */
- { USB_DEVICE(VENDOR_REALTEK, 0x0161) },
+ /* Philips IR transceiver (Dell branded) */
+ { USB_DEVICE(VENDOR_PHILIPS, 0x2093),
+ .driver_info = MCE_GEN2_TX_INV },
+ /* Realtek MCE IR Receiver and card reader */
+ { USB_DEVICE(VENDOR_REALTEK, 0x0161),
+ .driver_info = MULTIFUNCTION },
/* SMK/Toshiba G83C0004D410 */
{ USB_DEVICE(VENDOR_SMK, 0x031d),
.driver_info = MCE_GEN2_TX_INV },
@@ -229,6 +300,9 @@ static struct usb_device_id mceusb_dev_table[] = {
.driver_info = MCE_GEN2_TX_INV },
/* SMK eHome Infrared Transceiver */
{ USB_DEVICE(VENDOR_SMK, 0x0338) },
+ /* SMK/I-O Data GV-MC7/RCKIT Receiver */
+ { USB_DEVICE(VENDOR_SMK, 0x0353),
+ .driver_info = MCE_GEN2_NO_TX },
/* Tatung eHome Infrared Transceiver */
{ USB_DEVICE(VENDOR_TATUNG, 0x9150) },
/* Shuttle eHome Infrared Transceiver */
@@ -256,7 +330,7 @@ static struct usb_device_id mceusb_dev_table[] = {
.driver_info = MCE_GEN2_TX_INV },
/* Topseed eHome Infrared Transceiver */
{ USB_DEVICE(VENDOR_TOPSEED, 0x0011),
- .driver_info = MCE_GEN2_TX_INV },
+ .driver_info = MCE_GEN3 },
/* Ricavision internal Infrared Transceiver */
{ USB_DEVICE(VENDOR_RICAVISION, 0x0010) },
/* Itron ione Libra Q-11 */
@@ -272,7 +346,8 @@ static struct usb_device_id mceusb_dev_table[] = {
/* Formosa21 / eHome Infrared Receiver */
{ USB_DEVICE(VENDOR_FORMOSA, 0xe016) },
/* Formosa aim / Trust MCE Infrared Receiver */
- { USB_DEVICE(VENDOR_FORMOSA, 0xe017) },
+ { USB_DEVICE(VENDOR_FORMOSA, 0xe017),
+ .driver_info = MCE_GEN2_NO_TX },
/* Formosa Industrial Computing / Beanbag Emulation Device */
{ USB_DEVICE(VENDOR_FORMOSA, 0xe018) },
/* Formosa21 / eHome Infrared Receiver */
@@ -281,8 +356,11 @@ static struct usb_device_id mceusb_dev_table[] = {
{ USB_DEVICE(VENDOR_FORMOSA, 0xe03c) },
/* Formosa Industrial Computing */
{ USB_DEVICE(VENDOR_FORMOSA, 0xe03e) },
+ /* Formosa Industrial Computing */
+ { USB_DEVICE(VENDOR_FORMOSA, 0xe042) },
/* Fintek eHome Infrared Transceiver (HP branded) */
- { USB_DEVICE(VENDOR_FINTEK, 0x5168) },
+ { USB_DEVICE(VENDOR_FINTEK, 0x5168),
+ .driver_info = MCE_GEN2_TX_INV },
/* Fintek eHome Infrared Transceiver */
{ USB_DEVICE(VENDOR_FINTEK, 0x0602) },
/* Fintek eHome Infrared Transceiver (in the AOpen MP45) */
@@ -301,13 +379,23 @@ static struct usb_device_id mceusb_dev_table[] = {
/* Northstar Systems, Inc. eHome Infrared Transceiver */
{ USB_DEVICE(VENDOR_NORTHSTAR, 0xe004) },
/* TiVo PC IR Receiver */
- { USB_DEVICE(VENDOR_TIVO, 0x2000) },
+ { USB_DEVICE(VENDOR_TIVO, 0x2000),
+ .driver_info = TIVO_KIT },
/* Conexant Hybrid TV "Shelby" Polaris SDK */
{ USB_DEVICE(VENDOR_CONEXANT, 0x58a1),
.driver_info = POLARIS_EVK },
/* Conexant Hybrid TV RDU253S Polaris */
{ USB_DEVICE(VENDOR_CONEXANT, 0x58a5),
.driver_info = CX_HYBRID_TV },
+ /* Twisted Melon Inc. - Manta Mini Receiver */
+ { USB_DEVICE(VENDOR_TWISTEDMELON, 0x8008) },
+ /* Twisted Melon Inc. - Manta Pico Receiver */
+ { USB_DEVICE(VENDOR_TWISTEDMELON, 0x8016) },
+ /* Twisted Melon Inc. - Manta Transceiver */
+ { USB_DEVICE(VENDOR_TWISTEDMELON, 0x8042) },
+ /* Hauppauge WINTV-HVR-HVR 930C-HD - based on cx231xx */
+ { USB_DEVICE(VENDOR_HAUPPAUGE, 0xb130),
+ .driver_info = HAUPPAUGE_CX_HYBRID_TV },
/* Terminating entry */
{ }
};
@@ -327,14 +415,12 @@ struct mceusb_dev {
/* usb */
struct usb_device *usbdev;
struct urb *urb_in;
- struct usb_endpoint_descriptor *usb_ep_in;
struct usb_endpoint_descriptor *usb_ep_out;
/* buffers and dma */
unsigned char *buf_in;
unsigned int len_in;
dma_addr_t dma_in;
- dma_addr_t dma_out;
enum {
CMD_HEADER = 0,
@@ -360,76 +446,78 @@ struct mceusb_dev {
char name[128];
char phys[64];
enum mceusb_model_type model;
+
+ bool need_reset; /* flag to issue a device resume cmd */
+ u8 emver; /* emulator interface version */
+ u8 num_txports; /* number of transmit ports */
+ u8 num_rxports; /* number of receive sensors */
+ u8 txports_cabled; /* bitmask of transmitters with cable */
+ u8 rxports_active; /* bitmask of active receive sensors */
};
-/*
- * MCE Device Command Strings
- * Device command responses vary from device to device...
- * - DEVICE_RESET resets the hardware to its default state
- * - GET_REVISION fetches the hardware/software revision, common
- * replies are ff 0b 45 ff 1b 08 and ff 0b 50 ff 1b 42
- * - GET_CARRIER_FREQ gets the carrier mode and frequency of the
- * device, with replies in the form of 9f 06 MM FF, where MM is 0-3,
- * meaning clk of 10000000, 2500000, 625000 or 156250, and FF is
- * ((clk / frequency) - 1)
- * - GET_RX_TIMEOUT fetches the receiver timeout in units of 50us,
- * response in the form of 9f 0c msb lsb
- * - GET_TX_BITMASK fetches the transmitter bitmask, replies in
- * the form of 9f 08 bm, where bm is the bitmask
- * - GET_RX_SENSOR fetches the RX sensor setting -- long-range
- * general use one or short-range learning one, in the form of
- * 9f 14 ss, where ss is either 01 for long-range or 02 for short
- * - SET_CARRIER_FREQ sets a new carrier mode and frequency
- * - SET_TX_BITMASK sets the transmitter bitmask
- * - SET_RX_TIMEOUT sets the receiver timeout
- * - SET_RX_SENSOR sets which receiver sensor to use
- */
-static char DEVICE_RESET[] = {MCE_COMMAND_NULL, MCE_HW_CMD_HEADER,
- MCE_CMD_DEVICE_RESET};
-static char GET_REVISION[] = {MCE_HW_CMD_HEADER, MCE_CMD_G_REVISION};
-static char GET_UNKNOWN[] = {MCE_HW_CMD_HEADER, MCE_CMD_UNKNOWN7};
-static char GET_UNKNOWN2[] = {MCE_COMMAND_HEADER, MCE_CMD_UNKNOWN2};
-static char GET_CARRIER_FREQ[] = {MCE_COMMAND_HEADER, MCE_CMD_G_CARRIER};
-static char GET_RX_TIMEOUT[] = {MCE_COMMAND_HEADER, MCE_CMD_G_TIMEOUT};
-static char GET_TX_BITMASK[] = {MCE_COMMAND_HEADER, MCE_CMD_G_TXMASK};
-static char GET_RX_SENSOR[] = {MCE_COMMAND_HEADER, MCE_CMD_G_RXSENSOR};
+/* MCE Device Command Strings, generally a port and command pair */
+static char DEVICE_RESUME[] = {MCE_CMD_NULL, MCE_CMD_PORT_SYS,
+ MCE_CMD_RESUME};
+static char GET_REVISION[] = {MCE_CMD_PORT_SYS, MCE_CMD_G_REVISION};
+static char GET_EMVER[] = {MCE_CMD_PORT_SYS, MCE_CMD_GETEMVER};
+static char GET_WAKEVERSION[] = {MCE_CMD_PORT_SYS, MCE_CMD_GETWAKEVERSION};
+static char FLASH_LED[] = {MCE_CMD_PORT_SYS, MCE_CMD_FLASHLED};
+static char GET_UNKNOWN2[] = {MCE_CMD_PORT_IR, MCE_CMD_UNKNOWN2};
+static char GET_CARRIER_FREQ[] = {MCE_CMD_PORT_IR, MCE_CMD_GETIRCFS};
+static char GET_RX_TIMEOUT[] = {MCE_CMD_PORT_IR, MCE_CMD_GETIRTIMEOUT};
+static char GET_NUM_PORTS[] = {MCE_CMD_PORT_IR, MCE_CMD_GETIRNUMPORTS};
+static char GET_TX_BITMASK[] = {MCE_CMD_PORT_IR, MCE_CMD_GETIRTXPORTS};
+static char GET_RX_SENSOR[] = {MCE_CMD_PORT_IR, MCE_CMD_GETIRRXPORTEN};
/* sub in desired values in lower byte or bytes for full command */
/* FIXME: make use of these for transmit.
-static char SET_CARRIER_FREQ[] = {MCE_COMMAND_HEADER,
- MCE_CMD_S_CARRIER, 0x00, 0x00};
-static char SET_TX_BITMASK[] = {MCE_COMMAND_HEADER, MCE_CMD_S_TXMASK, 0x00};
-static char SET_RX_TIMEOUT[] = {MCE_COMMAND_HEADER,
- MCE_CMD_S_TIMEOUT, 0x00, 0x00};
-static char SET_RX_SENSOR[] = {MCE_COMMAND_HEADER,
- MCE_CMD_S_RXSENSOR, 0x00};
+static char SET_CARRIER_FREQ[] = {MCE_CMD_PORT_IR,
+ MCE_CMD_SETIRCFS, 0x00, 0x00};
+static char SET_TX_BITMASK[] = {MCE_CMD_PORT_IR, MCE_CMD_SETIRTXPORTS, 0x00};
+static char SET_RX_TIMEOUT[] = {MCE_CMD_PORT_IR,
+ MCE_CMD_SETIRTIMEOUT, 0x00, 0x00};
+static char SET_RX_SENSOR[] = {MCE_CMD_PORT_IR,
+ MCE_RSP_EQIRRXPORTEN, 0x00};
*/
-static int mceusb_cmdsize(u8 cmd, u8 subcmd)
+static int mceusb_cmd_datasize(u8 cmd, u8 subcmd)
{
int datasize = 0;
switch (cmd) {
- case MCE_COMMAND_NULL:
- if (subcmd == MCE_HW_CMD_HEADER)
+ case MCE_CMD_NULL:
+ if (subcmd == MCE_CMD_PORT_SYS)
datasize = 1;
break;
- case MCE_HW_CMD_HEADER:
+ case MCE_CMD_PORT_SYS:
switch (subcmd) {
+ case MCE_RSP_GETPORTSTATUS:
+ datasize = 5;
+ break;
+ case MCE_RSP_EQWAKEVERSION:
+ datasize = 4;
+ break;
case MCE_CMD_G_REVISION:
datasize = 2;
break;
+ case MCE_RSP_EQWAKESUPPORT:
+ case MCE_RSP_GETWAKESOURCE:
+ case MCE_RSP_EQDEVDETAILS:
+ case MCE_RSP_EQEMVER:
+ datasize = 1;
+ break;
}
- case MCE_COMMAND_HEADER:
+ case MCE_CMD_PORT_IR:
switch (subcmd) {
case MCE_CMD_UNKNOWN:
- case MCE_CMD_S_CARRIER:
- case MCE_CMD_S_TIMEOUT:
- case MCE_RSP_PULSE_COUNT:
+ case MCE_RSP_EQIRCFS:
+ case MCE_RSP_EQIRTIMEOUT:
+ case MCE_RSP_EQIRRXCFCNT:
+ case MCE_RSP_EQIRNUMPORTS:
datasize = 2;
break;
case MCE_CMD_SIG_END:
- case MCE_CMD_S_TXMASK:
- case MCE_CMD_S_RXSENSOR:
+ case MCE_RSP_EQIRTXPORTS:
+ case MCE_RSP_EQIRRXPORTEN:
datasize = 1;
break;
}
@@ -440,14 +528,12 @@ static int mceusb_cmdsize(u8 cmd, u8 subcmd)
static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
int offset, int len, bool out)
{
- char codes[USB_BUFLEN * 3 + 1];
- char inout[9];
- u8 cmd, subcmd, data1, data2;
+#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG)
+ char *inout;
+ u8 cmd, subcmd, data1, data2, data3, data4;
struct device *dev = ir->dev;
- int i, start, skip = 0;
-
- if (!debug)
- return;
+ int start, skip = 0;
+ u32 carrier, period;
/* skip meaningless 0xb1 0x60 header bytes on orig receiver */
if (ir->flags.microsoft_gen1 && !out && !offset)
@@ -456,110 +542,136 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
if (len <= skip)
return;
- for (i = 0; i < len && i < USB_BUFLEN; i++)
- snprintf(codes + i * 3, 4, "%02x ", buf[i + offset] & 0xff);
-
- dev_info(dev, "%sx data: %s(length=%d)\n",
- (out ? "t" : "r"), codes, len);
+ dev_dbg(dev, "%cx data: %*ph (length=%d)",
+ (out ? 't' : 'r'), min(len, USB_BUFLEN), buf, len);
- if (out)
- strcpy(inout, "Request\0");
- else
- strcpy(inout, "Got\0");
+ inout = out ? "Request" : "Got";
start = offset + skip;
cmd = buf[start] & 0xff;
subcmd = buf[start + 1] & 0xff;
data1 = buf[start + 2] & 0xff;
data2 = buf[start + 3] & 0xff;
+ data3 = buf[start + 4] & 0xff;
+ data4 = buf[start + 5] & 0xff;
switch (cmd) {
- case MCE_COMMAND_NULL:
- if ((subcmd == MCE_HW_CMD_HEADER) &&
- (data1 == MCE_CMD_DEVICE_RESET))
- dev_info(dev, "Device reset requested\n");
+ case MCE_CMD_NULL:
+ if (subcmd == MCE_CMD_NULL)
+ break;
+ if ((subcmd == MCE_CMD_PORT_SYS) &&
+ (data1 == MCE_CMD_RESUME))
+ dev_dbg(dev, "Device resume requested");
else
- dev_info(dev, "Unknown command 0x%02x 0x%02x\n",
+ dev_dbg(dev, "Unknown command 0x%02x 0x%02x",
cmd, subcmd);
break;
- case MCE_HW_CMD_HEADER:
+ case MCE_CMD_PORT_SYS:
switch (subcmd) {
+ case MCE_RSP_EQEMVER:
+ if (!out)
+ dev_dbg(dev, "Emulator interface version %x",
+ data1);
+ break;
case MCE_CMD_G_REVISION:
if (len == 2)
- dev_info(dev, "Get hw/sw rev?\n");
+ dev_dbg(dev, "Get hw/sw rev?");
else
- dev_info(dev, "hw/sw rev 0x%02x 0x%02x "
- "0x%02x 0x%02x\n", data1, data2,
+ dev_dbg(dev, "hw/sw rev 0x%02x 0x%02x 0x%02x 0x%02x",
+ data1, data2,
buf[start + 4], buf[start + 5]);
break;
- case MCE_CMD_DEVICE_RESET:
- dev_info(dev, "Device reset requested\n");
+ case MCE_CMD_RESUME:
+ dev_dbg(dev, "Device resume requested");
+ break;
+ case MCE_RSP_CMD_ILLEGAL:
+ dev_dbg(dev, "Illegal PORT_SYS command");
break;
- case MCE_RSP_CMD_INVALID:
- dev_info(dev, "Previous command not supported\n");
+ case MCE_RSP_EQWAKEVERSION:
+ if (!out)
+ dev_dbg(dev, "Wake version, proto: 0x%02x, "
+ "payload: 0x%02x, address: 0x%02x, "
+ "version: 0x%02x",
+ data1, data2, data3, data4);
+ break;
+ case MCE_RSP_GETPORTSTATUS:
+ if (!out)
+ /* We use data1 + 1 here, to match hw labels */
+ dev_dbg(dev, "TX port %d: blaster is%s connected",
+ data1 + 1, data4 ? " not" : "");
+ break;
+ case MCE_CMD_FLASHLED:
+ dev_dbg(dev, "Attempting to flash LED");
break;
- case MCE_CMD_UNKNOWN7:
- case MCE_CMD_UNKNOWN9:
default:
- dev_info(dev, "Unknown command 0x%02x 0x%02x\n",
+ dev_dbg(dev, "Unknown command 0x%02x 0x%02x",
cmd, subcmd);
break;
}
break;
- case MCE_COMMAND_HEADER:
+ case MCE_CMD_PORT_IR:
switch (subcmd) {
case MCE_CMD_SIG_END:
- dev_info(dev, "End of signal\n");
+ dev_dbg(dev, "End of signal");
break;
case MCE_CMD_PING:
- dev_info(dev, "Ping\n");
+ dev_dbg(dev, "Ping");
break;
case MCE_CMD_UNKNOWN:
- dev_info(dev, "Resp to 9f 05 of 0x%02x 0x%02x\n",
+ dev_dbg(dev, "Resp to 9f 05 of 0x%02x 0x%02x",
data1, data2);
break;
- case MCE_CMD_S_CARRIER:
- dev_info(dev, "%s carrier mode and freq of "
- "0x%02x 0x%02x\n", inout, data1, data2);
+ case MCE_RSP_EQIRCFS:
+ period = DIV_ROUND_CLOSEST(
+ (1U << data1 * 2) * (data2 + 1), 10);
+ if (!period)
+ break;
+ carrier = (1000 * 1000) / period;
+ dev_dbg(dev, "%s carrier of %u Hz (period %uus)",
+ inout, carrier, period);
break;
- case MCE_CMD_G_CARRIER:
- dev_info(dev, "Get carrier mode and freq\n");
+ case MCE_CMD_GETIRCFS:
+ dev_dbg(dev, "Get carrier mode and freq");
break;
- case MCE_CMD_S_TXMASK:
- dev_info(dev, "%s transmit blaster mask of 0x%02x\n",
+ case MCE_RSP_EQIRTXPORTS:
+ dev_dbg(dev, "%s transmit blaster mask of 0x%02x",
inout, data1);
break;
- case MCE_CMD_S_TIMEOUT:
- /* value is in units of 50us, so x*50/100 or x/2 ms */
- dev_info(dev, "%s receive timeout of %d ms\n",
- inout, ((data1 << 8) | data2) / 2);
+ case MCE_RSP_EQIRTIMEOUT:
+ /* value is in units of 50us, so x*50/1000 ms */
+ period = ((data1 << 8) | data2) * MCE_TIME_UNIT / 1000;
+ dev_dbg(dev, "%s receive timeout of %d ms",
+ inout, period);
break;
- case MCE_CMD_G_TIMEOUT:
- dev_info(dev, "Get receive timeout\n");
+ case MCE_CMD_GETIRTIMEOUT:
+ dev_dbg(dev, "Get receive timeout");
break;
- case MCE_CMD_G_TXMASK:
- dev_info(dev, "Get transmit blaster mask\n");
+ case MCE_CMD_GETIRTXPORTS:
+ dev_dbg(dev, "Get transmit blaster mask");
break;
- case MCE_CMD_S_RXSENSOR:
- dev_info(dev, "%s %s-range receive sensor in use\n",
+ case MCE_RSP_EQIRRXPORTEN:
+ dev_dbg(dev, "%s %s-range receive sensor in use",
inout, data1 == 0x02 ? "short" : "long");
break;
- case MCE_CMD_G_RXSENSOR:
- /* aka MCE_RSP_PULSE_COUNT */
+ case MCE_CMD_GETIRRXPORTEN:
+ /* aka MCE_RSP_EQIRRXCFCNT */
if (out)
- dev_info(dev, "Get receive sensor\n");
+ dev_dbg(dev, "Get receive sensor");
else if (ir->learning_enabled)
- dev_info(dev, "RX pulse count: %d\n",
+ dev_dbg(dev, "RX pulse count: %d",
((data1 << 8) | data2));
break;
- case MCE_RSP_CMD_INVALID:
- dev_info(dev, "Error! Hardware is likely wedged...\n");
+ case MCE_RSP_EQIRNUMPORTS:
+ if (out)
+ break;
+ dev_dbg(dev, "Num TX ports: %x, num RX ports: %x",
+ data1, data2);
+ break;
+ case MCE_RSP_CMD_ILLEGAL:
+ dev_dbg(dev, "Illegal PORT_IR command");
break;
- case MCE_CMD_UNKNOWN2:
- case MCE_CMD_UNKNOWN3:
- case MCE_CMD_UNKNOWN5:
default:
- dev_info(dev, "Unknown command 0x%02x 0x%02x\n",
+ dev_dbg(dev, "Unknown command 0x%02x 0x%02x",
cmd, subcmd);
break;
}
@@ -569,13 +681,14 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
}
if (cmd == MCE_IRDATA_TRAILER)
- dev_info(dev, "End of raw IR data\n");
- else if ((cmd != MCE_COMMAND_HEADER) &&
- ((cmd & MCE_COMMAND_MASK) == MCE_COMMAND_IRDATA))
- dev_info(dev, "Raw IR data, %d pulse/space samples\n", ir->rem);
+ 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);
+#endif
}
-static void mce_async_callback(struct urb *urb, struct pt_regs *regs)
+static void mce_async_callback(struct urb *urb)
{
struct mceusb_dev *ir;
int len;
@@ -584,23 +697,37 @@ static void mce_async_callback(struct urb *urb, struct pt_regs *regs)
return;
ir = urb->context;
- if (ir) {
- len = urb->actual_length;
- dev_dbg(ir->dev, "callback called (status=%d len=%d)\n",
- urb->status, len);
+ switch (urb->status) {
+ /* success */
+ case 0:
+ len = urb->actual_length;
mceusb_dev_printdata(ir, urb->transfer_buffer, 0, len, true);
+ break;
+
+ case -ECONNRESET:
+ case -ENOENT:
+ case -EILSEQ:
+ case -ESHUTDOWN:
+ break;
+
+ case -EPIPE:
+ default:
+ dev_err(ir->dev, "Error: request 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 incoming or send outgoing usb packet - used to initialize remote */
-static void mce_request_packet(struct mceusb_dev *ir,
- struct usb_endpoint_descriptor *ep,
- unsigned char *data, int size, int urb_type)
+static void mce_request_packet(struct mceusb_dev *ir, unsigned char *data,
+ int size, int urb_type)
{
- int res;
+ int res, pipe;
struct urb *async_urb;
struct device *dev = ir->dev;
unsigned char *async_buf;
@@ -620,10 +747,19 @@ static void mce_request_packet(struct mceusb_dev *ir,
}
/* outbound data */
- usb_fill_int_urb(async_urb, ir->usbdev,
- usb_sndintpipe(ir->usbdev, ep->bEndpointAddress),
- async_buf, size, (usb_complete_t)mce_async_callback,
- ir, ep->bInterval);
+ if (usb_endpoint_xfer_int(ir->usb_ep_out)) {
+ pipe = usb_sndintpipe(ir->usbdev,
+ ir->usb_ep_out->bEndpointAddress);
+ usb_fill_int_urb(async_urb, ir->usbdev, pipe, async_buf,
+ size, mce_async_callback, ir,
+ ir->usb_ep_out->bInterval);
+ } else {
+ pipe = usb_sndbulkpipe(ir->usbdev,
+ ir->usb_ep_out->bEndpointAddress);
+ usb_fill_bulk_urb(async_urb, ir->usbdev, pipe,
+ async_buf, size, mce_async_callback,
+ ir);
+ }
memcpy(async_buf, data, size);
} else if (urb_type == MCEUSB_RX) {
@@ -636,63 +772,64 @@ static void mce_request_packet(struct mceusb_dev *ir,
return;
}
- dev_dbg(dev, "receive request called (size=%#x)\n", size);
+ dev_dbg(dev, "receive request called (size=%#x)", size);
async_urb->transfer_buffer_length = size;
async_urb->dev = ir->usbdev;
res = usb_submit_urb(async_urb, GFP_ATOMIC);
if (res) {
- dev_dbg(dev, "receive request FAILED! (res=%d)\n", res);
+ dev_err(dev, "receive request FAILED! (res=%d)", res);
return;
}
- dev_dbg(dev, "receive request complete (res=%d)\n", res);
+ dev_dbg(dev, "receive request complete (res=%d)", res);
}
static void mce_async_out(struct mceusb_dev *ir, unsigned char *data, int size)
{
- mce_request_packet(ir, ir->usb_ep_out, data, size, MCEUSB_TX);
+ int rsize = sizeof(DEVICE_RESUME);
+
+ if (ir->need_reset) {
+ ir->need_reset = false;
+ mce_request_packet(ir, DEVICE_RESUME, rsize, MCEUSB_TX);
+ msleep(10);
+ }
+
+ mce_request_packet(ir, data, size, MCEUSB_TX);
+ msleep(10);
}
-static void mce_sync_in(struct mceusb_dev *ir, unsigned char *data, int size)
+static void mce_flush_rx_buffer(struct mceusb_dev *ir, int size)
{
- mce_request_packet(ir, ir->usb_ep_in, data, size, MCEUSB_RX);
+ mce_request_packet(ir, NULL, size, MCEUSB_RX);
}
/* Send data out the IR blaster port(s) */
-static int mceusb_tx_ir(struct rc_dev *dev, int *txbuf, u32 n)
+static int mceusb_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned count)
{
struct mceusb_dev *ir = dev->priv;
- int i, ret = 0;
- int count, cmdcount = 0;
- unsigned char *cmdbuf; /* MCE command buffer */
- long signal_duration = 0; /* Singnal length in us */
- struct timeval start_time, end_time;
-
- do_gettimeofday(&start_time);
-
- count = n / sizeof(int);
-
- cmdbuf = kzalloc(sizeof(int) * MCE_CMDBUF_SIZE, GFP_KERNEL);
- if (!cmdbuf)
- return -ENOMEM;
+ int i, length, ret = 0;
+ int cmdcount = 0;
+ unsigned char cmdbuf[MCE_CMDBUF_SIZE];
/* MCE tx init header */
- cmdbuf[cmdcount++] = MCE_COMMAND_HEADER;
- cmdbuf[cmdcount++] = MCE_CMD_S_TXMASK;
+ cmdbuf[cmdcount++] = MCE_CMD_PORT_IR;
+ cmdbuf[cmdcount++] = MCE_CMD_SETIRTXPORTS;
cmdbuf[cmdcount++] = ir->tx_mask;
+ /* 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++) {
- signal_duration += txbuf[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_TX_HEADER_LENGTH) %
- MCE_CODE_LENGTH == 0)
+ (cmdcount % MCE_CODE_LENGTH) == 0)
cmdbuf[cmdcount++] = MCE_IRDATA_HEADER;
/* Insert mce packet data */
@@ -710,39 +847,24 @@ static int mceusb_tx_ir(struct rc_dev *dev, int *txbuf, u32 n)
(txbuf[i] -= MCE_MAX_PULSE_LENGTH));
}
- /* Fix packet length in last header */
- cmdbuf[cmdcount - (cmdcount - MCE_TX_HEADER_LENGTH) % MCE_CODE_LENGTH] =
- MCE_COMMAND_IRDATA + (cmdcount - MCE_TX_HEADER_LENGTH) %
- MCE_CODE_LENGTH - 1;
-
/* Check if we have room for the empty packet at the end */
if (cmdcount >= MCE_CMDBUF_SIZE) {
ret = -EINVAL;
goto out;
}
+ /* Fix packet length in last header */
+ length = cmdcount % MCE_CODE_LENGTH;
+ cmdbuf[cmdcount - length] -= MCE_CODE_LENGTH - length;
+
/* All mce commands end with an empty packet (0x80) */
cmdbuf[cmdcount++] = MCE_IRDATA_TRAILER;
/* Transmit the command to the mce device */
mce_async_out(ir, cmdbuf, cmdcount);
- /*
- * The lircd gap calculation expects the write function to
- * wait the time it takes for the ircommand to be sent before
- * it returns.
- */
- do_gettimeofday(&end_time);
- signal_duration -= (end_time.tv_usec - start_time.tv_usec) +
- (end_time.tv_sec - start_time.tv_sec) * 1000000;
-
- /* delay with the closest number of ticks */
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(usecs_to_jiffies(signal_duration));
-
out:
- kfree(cmdbuf);
- return ret ? ret : n;
+ return ret ? ret : count;
}
/* Sets active IR outputs -- mce devices typically have two */
@@ -765,8 +887,8 @@ static int mceusb_set_tx_carrier(struct rc_dev *dev, u32 carrier)
struct mceusb_dev *ir = dev->priv;
int clk = 10000000;
int prescaler = 0, divisor = 0;
- unsigned char cmdbuf[4] = { MCE_COMMAND_HEADER,
- MCE_CMD_S_CARRIER, 0x00, 0x00 };
+ unsigned char cmdbuf[4] = { MCE_CMD_PORT_IR,
+ MCE_CMD_SETIRCFS, 0x00, 0x00 };
/* Carrier has changed */
if (ir->carrier != carrier) {
@@ -775,8 +897,7 @@ static int mceusb_set_tx_carrier(struct rc_dev *dev, u32 carrier)
ir->carrier = carrier;
cmdbuf[2] = MCE_CMD_SIG_END;
cmdbuf[3] = MCE_IRDATA_TRAILER;
- dev_dbg(ir->dev, "%s: disabling carrier "
- "modulation\n", __func__);
+ dev_dbg(ir->dev, "disabling carrier modulation");
mce_async_out(ir, cmdbuf, sizeof(cmdbuf));
return carrier;
}
@@ -787,8 +908,8 @@ static int mceusb_set_tx_carrier(struct rc_dev *dev, u32 carrier)
ir->carrier = carrier;
cmdbuf[2] = prescaler;
cmdbuf[3] = divisor;
- dev_dbg(ir->dev, "%s: requesting %u HZ "
- "carrier\n", __func__, carrier);
+ dev_dbg(ir->dev, "requesting %u HZ carrier",
+ carrier);
/* Transmit new carrier to mce device */
mce_async_out(ir, cmdbuf, sizeof(cmdbuf));
@@ -814,17 +935,34 @@ static void mceusb_handle_command(struct mceusb_dev *ir, int index)
u8 lo = ir->buf_in[index + 2] & 0xff;
switch (ir->buf_in[index]) {
+ /* the one and only 5-byte return value command */
+ case MCE_RSP_GETPORTSTATUS:
+ if ((ir->buf_in[index + 4] & 0xff) == 0x00)
+ ir->txports_cabled |= 1 << hi;
+ break;
+
/* 2-byte return value commands */
- case MCE_CMD_S_TIMEOUT:
- ir->rc->timeout = US_TO_NS((hi << 8 | lo) / 2);
+ case MCE_RSP_EQIRTIMEOUT:
+ ir->rc->timeout = US_TO_NS((hi << 8 | lo) * MCE_TIME_UNIT);
+ break;
+ case MCE_RSP_EQIRNUMPORTS:
+ ir->num_txports = hi;
+ ir->num_rxports = lo;
break;
/* 1-byte return value commands */
- case MCE_CMD_S_TXMASK:
+ case MCE_RSP_EQEMVER:
+ ir->emver = hi;
+ break;
+ case MCE_RSP_EQIRTXPORTS:
ir->tx_mask = hi;
break;
- case MCE_CMD_S_RXSENSOR:
- ir->learning_enabled = (hi == 0x02);
+ case MCE_RSP_EQIRRXPORTEN:
+ ir->learning_enabled = ((hi & 0x02) == 0x02);
+ ir->rxports_active = hi;
+ break;
+ case MCE_RSP_CMD_ILLEGAL:
+ ir->need_reset = true;
break;
default:
break;
@@ -834,6 +972,7 @@ static void mceusb_handle_command(struct mceusb_dev *ir, int index)
static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len)
{
DEFINE_IR_RAW_EVENT(rawir);
+ bool event = false;
int i = 0;
/* skip meaningless 0xb1 0x60 header bytes on orig receiver */
@@ -847,7 +986,7 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len)
for (; i < buf_len; i++) {
switch (ir->parser_state) {
case SUBCMD:
- ir->rem = mceusb_cmdsize(ir->cmd, ir->buf_in[i]);
+ ir->rem = mceusb_cmd_datasize(ir->cmd, ir->buf_in[i]);
mceusb_dev_printdata(ir, ir->buf_in, i - 1,
ir->rem + 2, false);
mceusb_handle_command(ir, i);
@@ -860,11 +999,12 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len)
rawir.duration = (ir->buf_in[i] & MCE_PULSE_MASK)
* US_TO_NS(MCE_TIME_UNIT);
- dev_dbg(ir->dev, "Storing %s with duration %d\n",
+ dev_dbg(ir->dev, "Storing %s with duration %d",
rawir.pulse ? "pulse" : "space",
rawir.duration);
- ir_raw_event_store_with_filter(ir->rc, &rawir);
+ if (ir_raw_event_store_with_filter(ir->rc, &rawir))
+ event = true;
break;
case CMD_DATA:
ir->rem--;
@@ -873,8 +1013,8 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len)
/* decode mce packets of the form (84),AA,BB,CC,DD */
/* IR data packets can span USB messages - rem */
ir->cmd = ir->buf_in[i];
- if ((ir->cmd == MCE_COMMAND_HEADER) ||
- ((ir->cmd & MCE_COMMAND_MASK) !=
+ if ((ir->cmd == MCE_CMD_PORT_IR) ||
+ ((ir->cmd & MCE_PORT_MASK) !=
MCE_COMMAND_IRDATA)) {
ir->parser_state = SUBCMD;
continue;
@@ -892,11 +1032,13 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len)
if (ir->parser_state != CMD_HEADER && !ir->rem)
ir->parser_state = CMD_HEADER;
}
- dev_dbg(ir->dev, "processed IR data, calling ir_raw_event_handle\n");
- ir_raw_event_handle(ir->rc);
+ if (event) {
+ dev_dbg(ir->dev, "processed IR data");
+ ir_raw_event_handle(ir->rc);
+ }
}
-static void mceusb_dev_recv(struct urb *urb, struct pt_regs *regs)
+static void mceusb_dev_recv(struct urb *urb)
{
struct mceusb_dev *ir;
int buf_len;
@@ -926,29 +1068,36 @@ static void mceusb_dev_recv(struct urb *urb, struct pt_regs *regs)
case -ECONNRESET:
case -ENOENT:
+ case -EILSEQ:
case -ESHUTDOWN:
usb_unlink_urb(urb);
return;
case -EPIPE:
default:
- dev_dbg(ir->dev, "Error: urb status = %d\n", urb->status);
+ dev_err(ir->dev, "Error: urb status = %d", urb->status);
break;
}
usb_submit_urb(urb, GFP_ATOMIC);
}
+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));
+}
+
static void mceusb_gen1_init(struct mceusb_dev *ir)
{
int ret;
- int maxp = ir->len_in;
struct device *dev = ir->dev;
char *data;
data = kzalloc(USB_CTRL_MSG_SZ, GFP_KERNEL);
if (!data) {
- dev_err(dev, "%s: memory allocation failed!\n", __func__);
+ dev_err(dev, "%s: memory allocation failed!", __func__);
return;
}
@@ -959,80 +1108,88 @@ static void mceusb_gen1_init(struct mceusb_dev *ir)
ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
USB_REQ_SET_ADDRESS, USB_TYPE_VENDOR, 0, 0,
data, USB_CTRL_MSG_SZ, HZ * 3);
- dev_dbg(dev, "%s - ret = %d\n", __func__, ret);
- dev_dbg(dev, "%s - data[0] = %d, data[1] = %d\n",
- __func__, data[0], data[1]);
+ dev_dbg(dev, "set address - ret = %d", ret);
+ dev_dbg(dev, "set address - data[0] = %d, data[1] = %d",
+ data[0], data[1]);
/* set feature: bit rate 38400 bps */
ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0),
USB_REQ_SET_FEATURE, USB_TYPE_VENDOR,
0xc04e, 0x0000, NULL, 0, HZ * 3);
- dev_dbg(dev, "%s - ret = %d\n", __func__, ret);
+ dev_dbg(dev, "set feature - ret = %d", ret);
/* bRequest 4: set char length to 8 bits */
ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0),
4, USB_TYPE_VENDOR,
0x0808, 0x0000, NULL, 0, HZ * 3);
- dev_dbg(dev, "%s - retB = %d\n", __func__, ret);
+ dev_dbg(dev, "set char length - retB = %d", ret);
/* bRequest 2: set handshaking to use DTR/DSR */
ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0),
2, USB_TYPE_VENDOR,
0x0000, 0x0100, NULL, 0, HZ * 3);
- dev_dbg(dev, "%s - retC = %d\n", __func__, ret);
+ dev_dbg(dev, "set handshake - retC = %d", ret);
- /* device reset */
- mce_async_out(ir, DEVICE_RESET, sizeof(DEVICE_RESET));
- mce_sync_in(ir, NULL, maxp);
+ /* device resume */
+ mce_async_out(ir, DEVICE_RESUME, sizeof(DEVICE_RESUME));
/* get hw/sw revision? */
mce_async_out(ir, GET_REVISION, sizeof(GET_REVISION));
- mce_sync_in(ir, NULL, maxp);
kfree(data);
-};
+}
static void mceusb_gen2_init(struct mceusb_dev *ir)
{
- int maxp = ir->len_in;
+ /* device resume */
+ mce_async_out(ir, DEVICE_RESUME, sizeof(DEVICE_RESUME));
- /* device reset */
- mce_async_out(ir, DEVICE_RESET, sizeof(DEVICE_RESET));
- mce_sync_in(ir, NULL, maxp);
+ /* get wake version (protocol, key, address) */
+ mce_async_out(ir, GET_WAKEVERSION, sizeof(GET_WAKEVERSION));
- /* get hw/sw revision? */
- mce_async_out(ir, GET_REVISION, sizeof(GET_REVISION));
- mce_sync_in(ir, NULL, maxp);
-
- /* unknown what the next two actually return... */
- mce_async_out(ir, GET_UNKNOWN, sizeof(GET_UNKNOWN));
- mce_sync_in(ir, NULL, maxp);
+ /* unknown what this one actually returns... */
mce_async_out(ir, GET_UNKNOWN2, sizeof(GET_UNKNOWN2));
- mce_sync_in(ir, NULL, maxp);
}
static void mceusb_get_parameters(struct mceusb_dev *ir)
{
- int maxp = ir->len_in;
+ int i;
+ unsigned char cmdbuf[3] = { MCE_CMD_PORT_SYS,
+ MCE_CMD_GETPORTSTATUS, 0x00 };
+
+ /* defaults, if the hardware doesn't support querying */
+ ir->num_txports = 2;
+ ir->num_rxports = 2;
+
+ /* get number of tx and rx ports */
+ mce_async_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_sync_in(ir, NULL, maxp);
- if (!ir->flags.no_tx) {
+ if (ir->num_txports && !ir->flags.no_tx)
/* get the transmitter bitmask */
mce_async_out(ir, GET_TX_BITMASK, sizeof(GET_TX_BITMASK));
- mce_sync_in(ir, NULL, maxp);
- }
/* get receiver timeout value */
mce_async_out(ir, GET_RX_TIMEOUT, sizeof(GET_RX_TIMEOUT));
- mce_sync_in(ir, NULL, maxp);
/* get receiver sensor setting */
mce_async_out(ir, GET_RX_SENSOR, sizeof(GET_RX_SENSOR));
- mce_sync_in(ir, NULL, maxp);
+
+ for (i = 0; i < ir->num_txports; i++) {
+ cmdbuf[2] = i;
+ mce_async_out(ir, cmdbuf, sizeof(cmdbuf));
+ }
+}
+
+static void mceusb_flash_led(struct mceusb_dev *ir)
+{
+ if (ir->emver < 2)
+ return;
+
+ mce_async_out(ir, FLASH_LED, sizeof(FLASH_LED));
}
static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir)
@@ -1043,7 +1200,7 @@ static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir)
rc = rc_allocate_device();
if (!rc) {
- dev_err(dev, "remote dev allocation failed\n");
+ dev_err(dev, "remote dev allocation failed");
goto out;
}
@@ -1062,8 +1219,8 @@ static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir)
rc->dev.parent = dev;
rc->priv = ir;
rc->driver_type = RC_DRIVER_IR_RAW;
- rc->allowed_protos = RC_TYPE_ALL;
- rc->timeout = US_TO_NS(1000);
+ rc_set_allowed_protocols(rc, RC_BIT_ALL);
+ rc->timeout = MS_TO_NS(100);
if (!ir->flags.no_tx) {
rc->s_tx_mask = mceusb_set_tx_mask;
rc->s_tx_carrier = mceusb_set_tx_carrier;
@@ -1075,7 +1232,7 @@ static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir)
ret = rc_register_device(rc);
if (ret < 0) {
- dev_err(dev, "remote dev registration failed\n");
+ dev_err(dev, "remote dev registration failed");
goto out;
}
@@ -1086,8 +1243,8 @@ out:
return NULL;
}
-static int __devinit mceusb_dev_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
+static int mceusb_dev_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf);
struct usb_host_interface *idesc;
@@ -1101,63 +1258,56 @@ static int __devinit mceusb_dev_probe(struct usb_interface *intf,
bool is_gen3;
bool is_microsoft_gen1;
bool tx_mask_normal;
- bool is_polaris;
+ int ir_intfnum;
- dev_dbg(&intf->dev, "%s called\n", __func__);
+ dev_dbg(&intf->dev, "%s called", __func__);
idesc = intf->cur_altsetting;
is_gen3 = mceusb_model[model].mce_gen3;
is_microsoft_gen1 = mceusb_model[model].mce_gen1;
tx_mask_normal = mceusb_model[model].tx_mask_normal;
- is_polaris = mceusb_model[model].is_polaris;
+ ir_intfnum = mceusb_model[model].ir_intfnum;
- if (is_polaris) {
- /* Interface 0 is IR */
- if (idesc->desc.bInterfaceNumber)
- return -ENODEV;
- }
+ /* There are multi-function devices with non-IR interfaces */
+ if (idesc->desc.bInterfaceNumber != ir_intfnum)
+ return -ENODEV;
/* step through the endpoints to find first bulk in and out endpoint */
for (i = 0; i < idesc->desc.bNumEndpoints; ++i) {
ep = &idesc->endpoint[i].desc;
- if ((ep_in == NULL)
- && ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
- == USB_DIR_IN)
- && (((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- == USB_ENDPOINT_XFER_BULK)
- || ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- == USB_ENDPOINT_XFER_INT))) {
-
- ep_in = ep;
- ep_in->bmAttributes = USB_ENDPOINT_XFER_INT;
- ep_in->bInterval = 1;
- dev_dbg(&intf->dev, "acceptable inbound endpoint "
- "found\n");
+ if (ep_in == NULL) {
+ if (usb_endpoint_is_bulk_in(ep)) {
+ ep_in = ep;
+ dev_dbg(&intf->dev, "acceptable bulk inbound endpoint found\n");
+ } else if (usb_endpoint_is_int_in(ep)) {
+ ep_in = ep;
+ ep_in->bInterval = 1;
+ dev_dbg(&intf->dev, "acceptable interrupt inbound endpoint found\n");
+ }
}
- if ((ep_out == NULL)
- && ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
- == USB_DIR_OUT)
- && (((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- == USB_ENDPOINT_XFER_BULK)
- || ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- == USB_ENDPOINT_XFER_INT))) {
-
- ep_out = ep;
- ep_out->bmAttributes = USB_ENDPOINT_XFER_INT;
- ep_out->bInterval = 1;
- dev_dbg(&intf->dev, "acceptable outbound endpoint "
- "found\n");
+ if (ep_out == NULL) {
+ if (usb_endpoint_is_bulk_out(ep)) {
+ ep_out = ep;
+ dev_dbg(&intf->dev, "acceptable bulk outbound endpoint found\n");
+ } else if (usb_endpoint_is_int_out(ep)) {
+ ep_out = ep;
+ ep_out->bInterval = 1;
+ dev_dbg(&intf->dev, "acceptable interrupt outbound endpoint found\n");
+ }
}
}
if (ep_in == NULL) {
- dev_dbg(&intf->dev, "inbound and/or endpoint not found\n");
+ dev_dbg(&intf->dev, "inbound and/or endpoint not found");
return -ENODEV;
}
- pipe = usb_rcvintpipe(dev, ep_in->bEndpointAddress);
+ if (usb_endpoint_xfer_int(ep_in))
+ pipe = usb_rcvintpipe(dev, ep_in->bEndpointAddress);
+ else
+ pipe = usb_rcvbulkpipe(dev, ep_in->bEndpointAddress);
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
ir = kzalloc(sizeof(struct mceusb_dev), GFP_KERNEL);
@@ -1181,7 +1331,6 @@ static int __devinit mceusb_dev_probe(struct usb_interface *intf,
ir->model = model;
/* Saving usb interface data for use by the transmitter routine */
- ir->usb_ep_in = ep_in;
ir->usb_ep_out = ep_out;
if (dev->descriptor.iManufacturer
@@ -1198,16 +1347,19 @@ static int __devinit mceusb_dev_probe(struct usb_interface *intf,
if (!ir->rc)
goto rc_dev_fail;
- /* flush buffers on the device */
- mce_sync_in(ir, NULL, maxp);
- mce_sync_in(ir, NULL, maxp);
-
/* wire up inbound data handler */
- usb_fill_int_urb(ir->urb_in, dev, pipe, ir->buf_in,
- maxp, (usb_complete_t) mceusb_dev_recv, ir, ep_in->bInterval);
+ usb_fill_int_urb(ir->urb_in, dev, pipe, ir->buf_in, maxp,
+ mceusb_dev_recv, ir, ep_in->bInterval);
ir->urb_in->transfer_dma = ir->dma_in;
ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ /* flush buffers on the device */
+ dev_dbg(&intf->dev, "Flushing receive buffers\n");
+ mce_flush_rx_buffer(ir, maxp);
+
+ /* figure out which firmware/emulator version this hardware has */
+ mceusb_get_emulator_version(ir);
+
/* initialize device */
if (ir->flags.microsoft_gen1)
mceusb_gen1_init(ir);
@@ -1216,13 +1368,22 @@ static int __devinit mceusb_dev_probe(struct usb_interface *intf,
mceusb_get_parameters(ir);
+ mceusb_flash_led(ir);
+
if (!ir->flags.no_tx)
mceusb_set_tx_mask(ir->rc, MCE_DEFAULT_TX_MASK);
usb_set_intfdata(intf, ir);
- dev_info(&intf->dev, "Registered %s on usb%d:%d\n", name,
- dev->bus->busnum, dev->devnum);
+ /* enable wake via this device */
+ device_set_wakeup_capable(ir->dev, true);
+ device_set_wakeup_enable(ir->dev, true);
+
+ dev_info(&intf->dev, "Registered %s with mce emulator interface version %x",
+ name, ir->emver);
+ dev_info(&intf->dev, "%x tx ports (0x%x cabled) and %x rx sensors (0x%x active)",
+ ir->num_txports, ir->txports_cabled,
+ ir->num_rxports, ir->rxports_active);
return 0;
@@ -1234,13 +1395,13 @@ urb_in_alloc_fail:
buf_in_alloc_fail:
kfree(ir);
mem_alloc_fail:
- dev_err(&intf->dev, "%s: device setup failed!\n", __func__);
+ dev_err(&intf->dev, "%s: device setup failed!", __func__);
return -ENOMEM;
}
-static void __devexit mceusb_dev_disconnect(struct usb_interface *intf)
+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);
@@ -1262,7 +1423,7 @@ static void __devexit mceusb_dev_disconnect(struct usb_interface *intf)
static int mceusb_dev_suspend(struct usb_interface *intf, pm_message_t message)
{
struct mceusb_dev *ir = usb_get_intfdata(intf);
- dev_info(ir->dev, "suspend\n");
+ dev_info(ir->dev, "suspend");
usb_kill_urb(ir->urb_in);
return 0;
}
@@ -1270,7 +1431,7 @@ static int mceusb_dev_suspend(struct usb_interface *intf, pm_message_t message)
static int mceusb_dev_resume(struct usb_interface *intf)
{
struct mceusb_dev *ir = usb_get_intfdata(intf);
- dev_info(ir->dev, "resume\n");
+ dev_info(ir->dev, "resume");
if (usb_submit_urb(ir->urb_in, GFP_ATOMIC))
return -EIO;
return 0;
@@ -1286,30 +1447,9 @@ static struct usb_driver mceusb_dev_driver = {
.id_table = mceusb_dev_table
};
-static int __init mceusb_dev_init(void)
-{
- int ret;
-
- ret = usb_register(&mceusb_dev_driver);
- if (ret < 0)
- printk(KERN_ERR DRIVER_NAME
- ": usb register failed, result = %d\n", ret);
-
- return ret;
-}
-
-static void __exit mceusb_dev_exit(void)
-{
- usb_deregister(&mceusb_dev_driver);
-}
-
-module_init(mceusb_dev_init);
-module_exit(mceusb_dev_exit);
+module_usb_driver(mceusb_dev_driver);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(usb, mceusb_dev_table);
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c
index 273d9d67479..d244e1a83f4 100644
--- a/drivers/media/rc/nuvoton-cir.c
+++ b/drivers/media/rc/nuvoton-cir.c
@@ -25,6 +25,8 @@
* USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pnp.h>
@@ -37,8 +39,6 @@
#include "nuvoton-cir.h"
-static char *chip_id = "w836x7hg";
-
/* write val to config reg */
static inline void nvt_cr_write(struct nvt_dev *nvt, u8 val, u8 reg)
{
@@ -125,43 +125,40 @@ static u8 nvt_cir_wake_reg_read(struct nvt_dev *nvt, u8 offset)
return val;
}
-#define pr_reg(text, ...) \
- printk(KERN_INFO KBUILD_MODNAME ": " text, ## __VA_ARGS__)
-
/* dump current cir register contents */
static void cir_dump_regs(struct nvt_dev *nvt)
{
nvt_efm_enable(nvt);
nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR);
- pr_reg("%s: Dump CIR logical device registers:\n", NVT_DRIVER_NAME);
- pr_reg(" * CR CIR ACTIVE : 0x%x\n",
- nvt_cr_read(nvt, CR_LOGICAL_DEV_EN));
- pr_reg(" * CR CIR BASE ADDR: 0x%x\n",
- (nvt_cr_read(nvt, CR_CIR_BASE_ADDR_HI) << 8) |
+ pr_info("%s: Dump CIR logical device registers:\n", NVT_DRIVER_NAME);
+ pr_info(" * CR CIR ACTIVE : 0x%x\n",
+ nvt_cr_read(nvt, CR_LOGICAL_DEV_EN));
+ pr_info(" * CR CIR BASE ADDR: 0x%x\n",
+ (nvt_cr_read(nvt, CR_CIR_BASE_ADDR_HI) << 8) |
nvt_cr_read(nvt, CR_CIR_BASE_ADDR_LO));
- pr_reg(" * CR CIR IRQ NUM: 0x%x\n",
- nvt_cr_read(nvt, CR_CIR_IRQ_RSRC));
+ pr_info(" * CR CIR IRQ NUM: 0x%x\n",
+ nvt_cr_read(nvt, CR_CIR_IRQ_RSRC));
nvt_efm_disable(nvt);
- pr_reg("%s: Dump CIR registers:\n", NVT_DRIVER_NAME);
- pr_reg(" * IRCON: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IRCON));
- pr_reg(" * IRSTS: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IRSTS));
- pr_reg(" * IREN: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IREN));
- pr_reg(" * RXFCONT: 0x%x\n", nvt_cir_reg_read(nvt, CIR_RXFCONT));
- pr_reg(" * CP: 0x%x\n", nvt_cir_reg_read(nvt, CIR_CP));
- pr_reg(" * CC: 0x%x\n", nvt_cir_reg_read(nvt, CIR_CC));
- pr_reg(" * SLCH: 0x%x\n", nvt_cir_reg_read(nvt, CIR_SLCH));
- pr_reg(" * SLCL: 0x%x\n", nvt_cir_reg_read(nvt, CIR_SLCL));
- pr_reg(" * FIFOCON: 0x%x\n", nvt_cir_reg_read(nvt, CIR_FIFOCON));
- pr_reg(" * IRFIFOSTS: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IRFIFOSTS));
- pr_reg(" * SRXFIFO: 0x%x\n", nvt_cir_reg_read(nvt, CIR_SRXFIFO));
- pr_reg(" * TXFCONT: 0x%x\n", nvt_cir_reg_read(nvt, CIR_TXFCONT));
- pr_reg(" * STXFIFO: 0x%x\n", nvt_cir_reg_read(nvt, CIR_STXFIFO));
- pr_reg(" * FCCH: 0x%x\n", nvt_cir_reg_read(nvt, CIR_FCCH));
- pr_reg(" * FCCL: 0x%x\n", nvt_cir_reg_read(nvt, CIR_FCCL));
- pr_reg(" * IRFSM: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IRFSM));
+ pr_info("%s: Dump CIR registers:\n", NVT_DRIVER_NAME);
+ pr_info(" * IRCON: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IRCON));
+ pr_info(" * IRSTS: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IRSTS));
+ pr_info(" * IREN: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IREN));
+ pr_info(" * RXFCONT: 0x%x\n", nvt_cir_reg_read(nvt, CIR_RXFCONT));
+ pr_info(" * CP: 0x%x\n", nvt_cir_reg_read(nvt, CIR_CP));
+ pr_info(" * CC: 0x%x\n", nvt_cir_reg_read(nvt, CIR_CC));
+ pr_info(" * SLCH: 0x%x\n", nvt_cir_reg_read(nvt, CIR_SLCH));
+ pr_info(" * SLCL: 0x%x\n", nvt_cir_reg_read(nvt, CIR_SLCL));
+ pr_info(" * FIFOCON: 0x%x\n", nvt_cir_reg_read(nvt, CIR_FIFOCON));
+ pr_info(" * IRFIFOSTS: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IRFIFOSTS));
+ pr_info(" * SRXFIFO: 0x%x\n", nvt_cir_reg_read(nvt, CIR_SRXFIFO));
+ pr_info(" * TXFCONT: 0x%x\n", nvt_cir_reg_read(nvt, CIR_TXFCONT));
+ pr_info(" * STXFIFO: 0x%x\n", nvt_cir_reg_read(nvt, CIR_STXFIFO));
+ pr_info(" * FCCH: 0x%x\n", nvt_cir_reg_read(nvt, CIR_FCCH));
+ pr_info(" * FCCL: 0x%x\n", nvt_cir_reg_read(nvt, CIR_FCCL));
+ pr_info(" * IRFSM: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IRFSM));
}
/* dump current cir wake register contents */
@@ -172,59 +169,59 @@ static void cir_wake_dump_regs(struct nvt_dev *nvt)
nvt_efm_enable(nvt);
nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR_WAKE);
- pr_reg("%s: Dump CIR WAKE logical device registers:\n",
- NVT_DRIVER_NAME);
- pr_reg(" * CR CIR WAKE ACTIVE : 0x%x\n",
- nvt_cr_read(nvt, CR_LOGICAL_DEV_EN));
- pr_reg(" * CR CIR WAKE BASE ADDR: 0x%x\n",
- (nvt_cr_read(nvt, CR_CIR_BASE_ADDR_HI) << 8) |
+ pr_info("%s: Dump CIR WAKE logical device registers:\n",
+ NVT_DRIVER_NAME);
+ pr_info(" * CR CIR WAKE ACTIVE : 0x%x\n",
+ nvt_cr_read(nvt, CR_LOGICAL_DEV_EN));
+ pr_info(" * CR CIR WAKE BASE ADDR: 0x%x\n",
+ (nvt_cr_read(nvt, CR_CIR_BASE_ADDR_HI) << 8) |
nvt_cr_read(nvt, CR_CIR_BASE_ADDR_LO));
- pr_reg(" * CR CIR WAKE IRQ NUM: 0x%x\n",
- nvt_cr_read(nvt, CR_CIR_IRQ_RSRC));
+ pr_info(" * CR CIR WAKE IRQ NUM: 0x%x\n",
+ nvt_cr_read(nvt, CR_CIR_IRQ_RSRC));
nvt_efm_disable(nvt);
- pr_reg("%s: Dump CIR WAKE registers\n", NVT_DRIVER_NAME);
- pr_reg(" * IRCON: 0x%x\n",
- nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRCON));
- pr_reg(" * IRSTS: 0x%x\n",
- nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRSTS));
- pr_reg(" * IREN: 0x%x\n",
- nvt_cir_wake_reg_read(nvt, CIR_WAKE_IREN));
- pr_reg(" * FIFO CMP DEEP: 0x%x\n",
- nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_CMP_DEEP));
- pr_reg(" * FIFO CMP TOL: 0x%x\n",
- nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_CMP_TOL));
- pr_reg(" * FIFO COUNT: 0x%x\n",
- nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_COUNT));
- pr_reg(" * SLCH: 0x%x\n",
- nvt_cir_wake_reg_read(nvt, CIR_WAKE_SLCH));
- pr_reg(" * SLCL: 0x%x\n",
- nvt_cir_wake_reg_read(nvt, CIR_WAKE_SLCL));
- pr_reg(" * FIFOCON: 0x%x\n",
- nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFOCON));
- pr_reg(" * SRXFSTS: 0x%x\n",
- nvt_cir_wake_reg_read(nvt, CIR_WAKE_SRXFSTS));
- pr_reg(" * SAMPLE RX FIFO: 0x%x\n",
- nvt_cir_wake_reg_read(nvt, CIR_WAKE_SAMPLE_RX_FIFO));
- pr_reg(" * WR FIFO DATA: 0x%x\n",
- nvt_cir_wake_reg_read(nvt, CIR_WAKE_WR_FIFO_DATA));
- pr_reg(" * RD FIFO ONLY: 0x%x\n",
- nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY));
- pr_reg(" * RD FIFO ONLY IDX: 0x%x\n",
- nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY_IDX));
- pr_reg(" * FIFO IGNORE: 0x%x\n",
- nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_IGNORE));
- pr_reg(" * IRFSM: 0x%x\n",
- nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRFSM));
+ pr_info("%s: Dump CIR WAKE registers\n", NVT_DRIVER_NAME);
+ pr_info(" * IRCON: 0x%x\n",
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRCON));
+ pr_info(" * IRSTS: 0x%x\n",
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRSTS));
+ pr_info(" * IREN: 0x%x\n",
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_IREN));
+ pr_info(" * FIFO CMP DEEP: 0x%x\n",
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_CMP_DEEP));
+ pr_info(" * FIFO CMP TOL: 0x%x\n",
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_CMP_TOL));
+ pr_info(" * FIFO COUNT: 0x%x\n",
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_COUNT));
+ pr_info(" * SLCH: 0x%x\n",
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_SLCH));
+ pr_info(" * SLCL: 0x%x\n",
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_SLCL));
+ pr_info(" * FIFOCON: 0x%x\n",
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFOCON));
+ pr_info(" * SRXFSTS: 0x%x\n",
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_SRXFSTS));
+ pr_info(" * SAMPLE RX FIFO: 0x%x\n",
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_SAMPLE_RX_FIFO));
+ pr_info(" * WR FIFO DATA: 0x%x\n",
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_WR_FIFO_DATA));
+ pr_info(" * RD FIFO ONLY: 0x%x\n",
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY));
+ pr_info(" * RD FIFO ONLY IDX: 0x%x\n",
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY_IDX));
+ pr_info(" * FIFO IGNORE: 0x%x\n",
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_IGNORE));
+ pr_info(" * IRFSM: 0x%x\n",
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRFSM));
fifo_len = nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_COUNT);
- pr_reg("%s: Dump CIR WAKE FIFO (len %d)\n", NVT_DRIVER_NAME, fifo_len);
- pr_reg("* Contents = ");
+ pr_info("%s: Dump CIR WAKE FIFO (len %d)\n", NVT_DRIVER_NAME, fifo_len);
+ pr_info("* Contents =");
for (i = 0; i < fifo_len; i++)
- printk(KERN_CONT "%02x ",
- nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY));
- printk(KERN_CONT "\n");
+ pr_cont(" %02x",
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY));
+ pr_cont("\n");
}
/* detect hardware features */
@@ -233,6 +230,8 @@ static int nvt_hw_detect(struct nvt_dev *nvt)
unsigned long flags;
u8 chip_major, chip_minor;
int ret = 0;
+ char chip_id[12];
+ bool chip_unknown = false;
nvt_efm_enable(nvt);
@@ -246,15 +245,39 @@ static int nvt_hw_detect(struct nvt_dev *nvt)
}
chip_minor = nvt_cr_read(nvt, CR_CHIP_ID_LO);
- nvt_dbg("%s: chip id: 0x%02x 0x%02x", chip_id, chip_major, chip_minor);
- if (chip_major != CHIP_ID_HIGH ||
- (chip_minor != CHIP_ID_LOW && chip_minor != CHIP_ID_LOW2)) {
- nvt_pr(KERN_ERR, "%s: unsupported chip, id: 0x%02x 0x%02x",
- chip_id, chip_major, chip_minor);
- ret = -ENODEV;
+ /* these are the known working chip revisions... */
+ switch (chip_major) {
+ case CHIP_ID_HIGH_667:
+ strcpy(chip_id, "w83667hg\0");
+ if (chip_minor != CHIP_ID_LOW_667)
+ chip_unknown = true;
+ break;
+ case CHIP_ID_HIGH_677B:
+ strcpy(chip_id, "w83677hg\0");
+ if (chip_minor != CHIP_ID_LOW_677B2 &&
+ chip_minor != CHIP_ID_LOW_677B3)
+ chip_unknown = true;
+ break;
+ case CHIP_ID_HIGH_677C:
+ strcpy(chip_id, "w83677hg-c\0");
+ if (chip_minor != CHIP_ID_LOW_677C)
+ chip_unknown = true;
+ break;
+ default:
+ strcpy(chip_id, "w836x7hg\0");
+ chip_unknown = true;
+ break;
}
+ /* warn, but still let the driver load, if we don't know this chip */
+ if (chip_unknown)
+ nvt_pr(KERN_WARNING, "%s: unknown chip, id: 0x%02x 0x%02x, "
+ "it may not work...", chip_id, chip_major, chip_minor);
+ else
+ nvt_dbg("%s: chip id: 0x%02x 0x%02x",
+ chip_id, chip_major, chip_minor);
+
nvt_efm_disable(nvt);
spin_lock_irqsave(&nvt->nvt_lock, flags);
@@ -267,13 +290,23 @@ static int nvt_hw_detect(struct nvt_dev *nvt)
static void nvt_cir_ldev_init(struct nvt_dev *nvt)
{
- u8 val;
+ u8 val, psreg, psmask, psval;
+
+ if (nvt->chip_major == CHIP_ID_HIGH_667) {
+ psreg = CR_MULTIFUNC_PIN_SEL;
+ psmask = MULTIFUNC_PIN_SEL_MASK;
+ psval = MULTIFUNC_ENABLE_CIR | MULTIFUNC_ENABLE_CIRWB;
+ } else {
+ psreg = CR_OUTPUT_PIN_SEL;
+ psmask = OUTPUT_PIN_SEL_MASK;
+ psval = OUTPUT_ENABLE_CIR | OUTPUT_ENABLE_CIRWB;
+ }
- /* output pin selection (Pin95=CIRRX, Pin96=CIRTX1, WB enabled */
- val = nvt_cr_read(nvt, CR_OUTPUT_PIN_SEL);
- val &= OUTPUT_PIN_SEL_MASK;
- val |= (OUTPUT_ENABLE_CIR | OUTPUT_ENABLE_CIRWB);
- nvt_cr_write(nvt, val, CR_OUTPUT_PIN_SEL);
+ /* output pin selection: enable CIR, with WB sensor enabled */
+ val = nvt_cr_read(nvt, psreg);
+ val &= psmask;
+ val |= psval;
+ nvt_cr_write(nvt, val, psreg);
/* Select CIR logical device and enable */
nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR);
@@ -297,9 +330,6 @@ static void nvt_cir_wake_ldev_init(struct nvt_dev *nvt)
/* Enable CIR Wake via PSOUT# (Pin60) */
nvt_set_reg_bit(nvt, CIR_WAKE_ENABLE_BIT, CR_ACPI_CIR_WAKE);
- /* enable cir interrupt of mouse/keyboard IRQ event */
- nvt_set_reg_bit(nvt, CIR_INTR_MOUSE_IRQ_BIT, CR_ACPI_IRQ_EVENTS);
-
/* enable pme interrupt of cir wakeup event */
nvt_set_reg_bit(nvt, PME_INTR_CIR_PASS_BIT, CR_ACPI_IRQ_EVENTS2);
@@ -385,8 +415,9 @@ static void nvt_cir_regs_init(struct nvt_dev *nvt)
static void nvt_cir_wake_regs_init(struct nvt_dev *nvt)
{
- /* set number of bytes needed for wake key comparison (default 67) */
- nvt_cir_wake_reg_write(nvt, CIR_WAKE_FIFO_LEN, CIR_WAKE_FIFO_CMP_DEEP);
+ /* set number of bytes needed for wake from s3 (default 65) */
+ nvt_cir_wake_reg_write(nvt, CIR_WAKE_FIFO_CMP_BYTES,
+ CIR_WAKE_FIFO_CMP_DEEP);
/* set tolerance/variance allowed per byte during wake compare */
nvt_cir_wake_reg_write(nvt, CIR_WAKE_CMP_TOLERANCE,
@@ -422,7 +453,6 @@ static void nvt_enable_wake(struct nvt_dev *nvt)
nvt_select_logical_dev(nvt, LOGICAL_DEV_ACPI);
nvt_set_reg_bit(nvt, CIR_WAKE_ENABLE_BIT, CR_ACPI_CIR_WAKE);
- nvt_set_reg_bit(nvt, CIR_INTR_MOUSE_IRQ_BIT, CR_ACPI_IRQ_EVENTS);
nvt_set_reg_bit(nvt, PME_INTR_CIR_PASS_BIT, CR_ACPI_IRQ_EVENTS2);
nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR_WAKE);
@@ -438,6 +468,7 @@ static void nvt_enable_wake(struct nvt_dev *nvt)
nvt_cir_wake_reg_write(nvt, 0, CIR_WAKE_IREN);
}
+#if 0 /* Currently unused */
/* rx carrier detect only works in learning mode, must be called w/nvt_lock */
static u32 nvt_rx_carrier_detect(struct nvt_dev *nvt)
{
@@ -470,7 +501,7 @@ static u32 nvt_rx_carrier_detect(struct nvt_dev *nvt)
return carrier;
}
-
+#endif
/*
* set carrier frequency
*
@@ -483,6 +514,9 @@ static int nvt_set_tx_carrier(struct rc_dev *dev, u32 carrier)
struct nvt_dev *nvt = dev->priv;
u16 val;
+ if (carrier == 0)
+ return -EINVAL;
+
nvt_cir_reg_write(nvt, 1, CIR_CP);
val = 3000000 / (carrier) - 1;
nvt_cir_reg_write(nvt, val & 0xff, CIR_CC);
@@ -511,24 +545,18 @@ static int nvt_set_tx_carrier(struct rc_dev *dev, u32 carrier)
* number may larger than TXFCONT (0xff). So in interrupt_handler, it has to
* set TXFCONT as 0xff, until buf_count less than 0xff.
*/
-static int nvt_tx_ir(struct rc_dev *dev, int *txbuf, u32 n)
+static int nvt_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned n)
{
struct nvt_dev *nvt = dev->priv;
unsigned long flags;
- size_t cur_count;
unsigned int i;
u8 iren;
int ret;
spin_lock_irqsave(&nvt->tx.lock, flags);
- if (n >= TX_BUF_LEN) {
- nvt->tx.buf_count = cur_count = TX_BUF_LEN;
- ret = TX_BUF_LEN;
- } else {
- nvt->tx.buf_count = cur_count = n;
- ret = n;
- }
+ ret = min((unsigned)(TX_BUF_LEN / sizeof(unsigned)), n);
+ nvt->tx.buf_count = (ret * sizeof(unsigned));
memcpy(nvt->tx.buf, txbuf, nvt->tx.buf_count);
@@ -589,8 +617,6 @@ static void nvt_dump_rx_buf(struct nvt_dev *nvt)
static void nvt_process_rx_ir_data(struct nvt_dev *nvt)
{
DEFINE_IR_RAW_EVENT(rawir);
- unsigned int count;
- u32 carrier;
u8 sample;
int i;
@@ -599,68 +625,38 @@ static void nvt_process_rx_ir_data(struct nvt_dev *nvt)
if (debug)
nvt_dump_rx_buf(nvt);
- if (nvt->carrier_detect_enabled)
- carrier = nvt_rx_carrier_detect(nvt);
-
- count = nvt->pkts;
- nvt_dbg_verbose("Processing buffer of len %d", count);
+ nvt_dbg_verbose("Processing buffer of len %d", nvt->pkts);
init_ir_raw_event(&rawir);
- for (i = 0; i < count; i++) {
- nvt->pkts--;
+ for (i = 0; i < nvt->pkts; i++) {
sample = nvt->buf[i];
rawir.pulse = ((sample & BUF_PULSE_BIT) != 0);
rawir.duration = US_TO_NS((sample & BUF_LEN_MASK)
* SAMPLE_PERIOD);
- if ((sample & BUF_LEN_MASK) == BUF_LEN_MASK) {
- if (nvt->rawir.pulse == rawir.pulse)
- nvt->rawir.duration += rawir.duration;
- else {
- nvt->rawir.duration = rawir.duration;
- nvt->rawir.pulse = rawir.pulse;
- }
- continue;
- }
-
- rawir.duration += nvt->rawir.duration;
-
- init_ir_raw_event(&nvt->rawir);
- nvt->rawir.duration = 0;
- nvt->rawir.pulse = rawir.pulse;
+ nvt_dbg("Storing %s with duration %d",
+ rawir.pulse ? "pulse" : "space", rawir.duration);
- if (sample == BUF_PULSE_BIT)
- rawir.pulse = false;
-
- if (rawir.duration) {
- nvt_dbg("Storing %s with duration %d",
- rawir.pulse ? "pulse" : "space",
- rawir.duration);
-
- ir_raw_event_store(nvt->rdev, &rawir);
- }
+ ir_raw_event_store_with_filter(nvt->rdev, &rawir);
/*
* BUF_PULSE_BIT indicates end of IR data, BUF_REPEAT_BYTE
* indicates end of IR signal, but new data incoming. In both
* cases, it means we're ready to call ir_raw_event_handle
*/
- if ((sample == BUF_PULSE_BIT) && nvt->pkts) {
+ if ((sample == BUF_PULSE_BIT) && (i + 1 < nvt->pkts)) {
nvt_dbg("Calling ir_raw_event_handle (signal end)\n");
ir_raw_event_handle(nvt->rdev);
}
}
+ nvt->pkts = 0;
+
nvt_dbg("Calling ir_raw_event_handle (buffer empty)\n");
ir_raw_event_handle(nvt->rdev);
- if (nvt->pkts) {
- nvt_dbg("Odd, pkts should be 0 now... (its %u)", nvt->pkts);
- nvt->pkts = 0;
- }
-
nvt_dbg_verbose("%s done", __func__);
}
@@ -956,7 +952,6 @@ static int nvt_open(struct rc_dev *dev)
unsigned long flags;
spin_lock_irqsave(&nvt->nvt_lock, flags);
- nvt->in_use = true;
nvt_enable_cir(nvt);
spin_unlock_irqrestore(&nvt->nvt_lock, flags);
@@ -969,7 +964,6 @@ static void nvt_close(struct rc_dev *dev)
unsigned long flags;
spin_lock_irqsave(&nvt->nvt_lock, flags);
- nvt->in_use = false;
nvt_disable_cir(nvt);
spin_unlock_irqrestore(&nvt->nvt_lock, flags);
}
@@ -988,25 +982,31 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
/* input device for IR remote (and tx) */
rdev = rc_allocate_device();
if (!rdev)
- goto failure;
+ goto exit_free_dev_rdev;
ret = -ENODEV;
+ /* activate pnp device */
+ if (pnp_activate_dev(pdev) < 0) {
+ dev_err(&pdev->dev, "Could not activate PNP device!\n");
+ goto exit_free_dev_rdev;
+ }
+
/* validate pnp resources */
if (!pnp_port_valid(pdev, 0) ||
pnp_port_len(pdev, 0) < CIR_IOREG_LENGTH) {
dev_err(&pdev->dev, "IR PNP Port not valid!\n");
- goto failure;
+ goto exit_free_dev_rdev;
}
if (!pnp_irq_valid(pdev, 0)) {
dev_err(&pdev->dev, "PNP IRQ not valid!\n");
- goto failure;
+ goto exit_free_dev_rdev;
}
if (!pnp_port_valid(pdev, 1) ||
pnp_port_len(pdev, 1) < CIR_IOREG_LENGTH) {
dev_err(&pdev->dev, "Wake PNP Port not valid!\n");
- goto failure;
+ goto exit_free_dev_rdev;
}
nvt->cir_addr = pnp_port_start(pdev, 0);
@@ -1021,25 +1021,6 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
spin_lock_init(&nvt->nvt_lock);
spin_lock_init(&nvt->tx.lock);
- init_ir_raw_event(&nvt->rawir);
-
- ret = -EBUSY;
- /* now claim resources */
- if (!request_region(nvt->cir_addr,
- CIR_IOREG_LENGTH, NVT_DRIVER_NAME))
- goto failure;
-
- if (request_irq(nvt->cir_irq, nvt_cir_isr, IRQF_SHARED,
- NVT_DRIVER_NAME, (void *)nvt))
- goto failure;
-
- if (!request_region(nvt->cir_wake_addr,
- CIR_IOREG_LENGTH, NVT_DRIVER_NAME))
- goto failure;
-
- if (request_irq(nvt->cir_wake_irq, nvt_cir_wake_isr, IRQF_SHARED,
- NVT_DRIVER_NAME, (void *)nvt))
- goto failure;
pnp_set_drvdata(pdev, nvt);
nvt->pdev = pdev;
@@ -1048,7 +1029,7 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
ret = nvt_hw_detect(nvt);
if (ret)
- goto failure;
+ goto exit_free_dev_rdev;
/* Initialize CIR & CIR Wake Logical Devices */
nvt_efm_enable(nvt);
@@ -1063,35 +1044,55 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
/* Set up the rc device */
rdev->priv = nvt;
rdev->driver_type = RC_DRIVER_IR_RAW;
- rdev->allowed_protos = RC_TYPE_ALL;
+ rc_set_allowed_protocols(rdev, RC_BIT_ALL);
rdev->open = nvt_open;
rdev->close = nvt_close;
rdev->tx_ir = nvt_tx_ir;
rdev->s_tx_carrier = nvt_set_tx_carrier;
rdev->input_name = "Nuvoton w836x7hg Infrared Remote Transceiver";
+ rdev->input_phys = "nuvoton/cir0";
rdev->input_id.bustype = BUS_HOST;
rdev->input_id.vendor = PCI_VENDOR_ID_WINBOND2;
rdev->input_id.product = nvt->chip_major;
rdev->input_id.version = nvt->chip_minor;
+ rdev->dev.parent = &pdev->dev;
rdev->driver_name = NVT_DRIVER_NAME;
rdev->map_name = RC_MAP_RC6_MCE;
+ rdev->timeout = MS_TO_NS(100);
+ /* rx resolution is hardwired to 50us atm, 1, 25, 100 also possible */
+ rdev->rx_resolution = US_TO_NS(CIR_SAMPLE_PERIOD);
#if 0
rdev->min_timeout = XYZ;
rdev->max_timeout = XYZ;
- rdev->timeout = XYZ;
- /* rx resolution is hardwired to 50us atm, 1, 25, 100 also possible */
- rdev->rx_resolution = XYZ;
/* tx bits */
rdev->tx_resolution = XYZ;
#endif
+ nvt->rdev = rdev;
ret = rc_register_device(rdev);
if (ret)
- goto failure;
+ goto exit_free_dev_rdev;
+
+ ret = -EBUSY;
+ /* now claim resources */
+ if (!request_region(nvt->cir_addr,
+ CIR_IOREG_LENGTH, NVT_DRIVER_NAME))
+ goto exit_unregister_device;
+
+ if (request_irq(nvt->cir_irq, nvt_cir_isr, IRQF_SHARED,
+ NVT_DRIVER_NAME, (void *)nvt))
+ goto exit_release_cir_addr;
+
+ if (!request_region(nvt->cir_wake_addr,
+ CIR_IOREG_LENGTH, NVT_DRIVER_NAME))
+ goto exit_free_irq;
+
+ if (request_irq(nvt->cir_wake_irq, nvt_cir_wake_isr, IRQF_SHARED,
+ NVT_DRIVER_NAME, (void *)nvt))
+ goto exit_release_cir_wake_addr;
+
+ device_init_wakeup(&pdev->dev, true);
- device_set_wakeup_capable(&pdev->dev, 1);
- device_set_wakeup_enable(&pdev->dev, 1);
- nvt->rdev = rdev;
nvt_pr(KERN_NOTICE, "driver has been successfully loaded\n");
if (debug) {
cir_dump_regs(nvt);
@@ -1100,24 +1101,23 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
return 0;
-failure:
- if (nvt->cir_irq)
- free_irq(nvt->cir_irq, nvt);
- if (nvt->cir_addr)
- release_region(nvt->cir_addr, CIR_IOREG_LENGTH);
-
- if (nvt->cir_wake_irq)
- free_irq(nvt->cir_wake_irq, nvt);
- if (nvt->cir_wake_addr)
- release_region(nvt->cir_wake_addr, CIR_IOREG_LENGTH);
-
+exit_release_cir_wake_addr:
+ release_region(nvt->cir_wake_addr, CIR_IOREG_LENGTH);
+exit_free_irq:
+ free_irq(nvt->cir_irq, nvt);
+exit_release_cir_addr:
+ release_region(nvt->cir_addr, CIR_IOREG_LENGTH);
+exit_unregister_device:
+ rc_unregister_device(rdev);
+ rdev = NULL;
+exit_free_dev_rdev:
rc_free_device(rdev);
kfree(nvt);
return ret;
}
-static void __devexit nvt_remove(struct pnp_dev *pdev)
+static void nvt_remove(struct pnp_dev *pdev)
{
struct nvt_dev *nvt = pnp_get_drvdata(pdev);
unsigned long flags;
@@ -1215,18 +1215,18 @@ static struct pnp_driver nvt_driver = {
.id_table = nvt_ids,
.flags = PNP_DRIVER_RES_DO_NOT_CHANGE,
.probe = nvt_probe,
- .remove = __devexit_p(nvt_remove),
+ .remove = nvt_remove,
.suspend = nvt_suspend,
.resume = nvt_resume,
.shutdown = nvt_shutdown,
};
-int nvt_init(void)
+static int nvt_init(void)
{
return pnp_register_driver(&nvt_driver);
}
-void nvt_exit(void)
+static void nvt_exit(void)
{
pnp_unregister_driver(&nvt_driver);
}
diff --git a/drivers/media/rc/nuvoton-cir.h b/drivers/media/rc/nuvoton-cir.h
index 1df82351cb0..e1cf23c3875 100644
--- a/drivers/media/rc/nuvoton-cir.h
+++ b/drivers/media/rc/nuvoton-cir.h
@@ -67,10 +67,8 @@ static int debug;
struct nvt_dev {
struct pnp_dev *pdev;
struct rc_dev *rdev;
- struct ir_raw_event rawir;
spinlock_t nvt_lock;
- bool in_use;
/* for rx */
u8 buf[RX_BUF_LEN];
@@ -86,8 +84,8 @@ struct nvt_dev {
} tx;
/* EFER Config register index/data pair */
- u8 cr_efir;
- u8 cr_efdr;
+ u32 cr_efir;
+ u32 cr_efdr;
/* hardware I/O settings */
unsigned long cir_addr;
@@ -105,7 +103,6 @@ struct nvt_dev {
/* rx settings */
bool learning_enabled;
- bool carrier_detect_enabled;
/* track cir wake state */
u8 wake_state;
@@ -305,8 +302,11 @@ struct nvt_dev {
#define CIR_WAKE_IRFIFOSTS_RX_EMPTY 0x20
#define CIR_WAKE_IRFIFOSTS_RX_FULL 0x10
-/* CIR Wake FIFO buffer is 67 bytes long */
-#define CIR_WAKE_FIFO_LEN 67
+/*
+ * The CIR Wake FIFO buffer is 67 bytes long, but the stock remote wakes
+ * the system comparing only 65 bytes (fails with this set to 67)
+ */
+#define CIR_WAKE_FIFO_CMP_BYTES 65
/* CIR Wake byte comparison tolerance */
#define CIR_WAKE_CMP_TOLERANCE 5
@@ -327,9 +327,13 @@ struct nvt_dev {
#define EFER_EFM_DISABLE 0xaa
/* Chip IDs found in CR_CHIP_ID_{HI,LO} */
-#define CHIP_ID_HIGH 0xb4
-#define CHIP_ID_LOW 0x72
-#define CHIP_ID_LOW2 0x73
+#define CHIP_ID_HIGH_667 0xa5
+#define CHIP_ID_HIGH_677B 0xb4
+#define CHIP_ID_HIGH_677C 0xc3
+#define CHIP_ID_LOW_667 0x13
+#define CHIP_ID_LOW_677B2 0x72
+#define CHIP_ID_LOW_677B3 0x73
+#define CHIP_ID_LOW_677C 0x33
/* Config regs we need to care about */
#define CR_SOFTWARE_RESET 0x02
@@ -338,6 +342,7 @@ struct nvt_dev {
#define CR_CHIP_ID_LO 0x21
#define CR_DEV_POWER_DOWN 0x22 /* bit 2 is CIR power, default power on */
#define CR_OUTPUT_PIN_SEL 0x27
+#define CR_MULTIFUNC_PIN_SEL 0x2c
#define CR_LOGICAL_DEV_EN 0x30 /* valid for all logical devices */
/* next three regs valid for both the CIR and CIR_WAKE logical devices */
#define CR_CIR_BASE_ADDR_HI 0x60
@@ -358,13 +363,18 @@ struct nvt_dev {
#define LOGICAL_DEV_ENABLE 0x01
#define CIR_WAKE_ENABLE_BIT 0x08
-#define CIR_INTR_MOUSE_IRQ_BIT 0x80
#define PME_INTR_CIR_PASS_BIT 0x08
+/* w83677hg CIR pin config */
#define OUTPUT_PIN_SEL_MASK 0xbc
#define OUTPUT_ENABLE_CIR 0x01 /* Pin95=CIRRX, Pin96=CIRTX1 */
#define OUTPUT_ENABLE_CIRWB 0x40 /* enable wide-band sensor */
+/* w83667hg CIR pin config */
+#define MULTIFUNC_PIN_SEL_MASK 0x1f
+#define MULTIFUNC_ENABLE_CIR 0x80 /* Pin75=CIRRX, Pin76=CIRTX1 */
+#define MULTIFUNC_ENABLE_CIRWB 0x20 /* enable wide-band sensor */
+
/* MCE CIR signal length, related on sample period */
/* MCE CIR controller signal length: about 43ms
diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h
index 873b3878975..da536c93c97 100644
--- a/drivers/media/rc/rc-core-priv.h
+++ b/drivers/media/rc/rc-core-priv.h
@@ -1,7 +1,7 @@
/*
* Remote Controller core raw events header
*
- * Copyright (C) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (C) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -35,11 +35,10 @@ struct ir_raw_event_ctrl {
struct list_head list; /* to keep track of raw clients */
struct task_struct *thread;
spinlock_t lock;
- struct kfifo kfifo; /* fifo for the pulse/space durations */
+ struct kfifo_rec_ptr_1 kfifo; /* fifo for the pulse/space durations */
ktime_t last_event; /* when last event occurred */
enum raw_event_type last_type; /* last event type */
struct rc_dev *dev; /* pointer to the parent rc_dev */
- u64 enabled_protocols; /* enabled raw protocol decoders */
/* raw decoder state follows */
struct ir_raw_event prev_ev;
@@ -84,6 +83,28 @@ struct ir_raw_event_ctrl {
unsigned count;
unsigned wanted_bits;
} rc5_sz;
+ struct sanyo_dec {
+ int state;
+ unsigned count;
+ u64 bits;
+ } sanyo;
+ struct sharp_dec {
+ int state;
+ unsigned count;
+ u32 bits;
+ unsigned int pulse_len;
+ } sharp;
+ struct mce_kbd_dec {
+ struct input_dev *idev;
+ struct timer_list rx_timeout;
+ char name[64];
+ char phys[64];
+ int state;
+ u8 header;
+ u32 body;
+ unsigned count;
+ unsigned wanted_bits;
+ } mce_kbd;
struct lirc_codec {
struct rc_dev *dev;
struct lirc_driver *drv;
@@ -149,44 +170,65 @@ void ir_raw_init(void);
/* from ir-nec-decoder.c */
#ifdef CONFIG_IR_NEC_DECODER_MODULE
-#define load_nec_decode() request_module("ir-nec-decoder")
+#define load_nec_decode() request_module_nowait("ir-nec-decoder")
#else
-#define load_nec_decode() 0
+static inline void load_nec_decode(void) { }
#endif
/* from ir-rc5-decoder.c */
#ifdef CONFIG_IR_RC5_DECODER_MODULE
-#define load_rc5_decode() request_module("ir-rc5-decoder")
+#define load_rc5_decode() request_module_nowait("ir-rc5-decoder")
#else
-#define load_rc5_decode() 0
+static inline void load_rc5_decode(void) { }
#endif
/* from ir-rc6-decoder.c */
#ifdef CONFIG_IR_RC6_DECODER_MODULE
-#define load_rc6_decode() request_module("ir-rc6-decoder")
+#define load_rc6_decode() request_module_nowait("ir-rc6-decoder")
#else
-#define load_rc6_decode() 0
+static inline void load_rc6_decode(void) { }
#endif
/* from ir-jvc-decoder.c */
#ifdef CONFIG_IR_JVC_DECODER_MODULE
-#define load_jvc_decode() request_module("ir-jvc-decoder")
+#define load_jvc_decode() request_module_nowait("ir-jvc-decoder")
#else
-#define load_jvc_decode() 0
+static inline void load_jvc_decode(void) { }
#endif
/* from ir-sony-decoder.c */
#ifdef CONFIG_IR_SONY_DECODER_MODULE
-#define load_sony_decode() request_module("ir-sony-decoder")
+#define load_sony_decode() request_module_nowait("ir-sony-decoder")
+#else
+static inline void load_sony_decode(void) { }
+#endif
+
+/* from ir-sanyo-decoder.c */
+#ifdef CONFIG_IR_SANYO_DECODER_MODULE
+#define load_sanyo_decode() request_module_nowait("ir-sanyo-decoder")
+#else
+static inline void load_sanyo_decode(void) { }
+#endif
+
+/* from ir-sharp-decoder.c */
+#ifdef CONFIG_IR_SHARP_DECODER_MODULE
+#define load_sharp_decode() request_module_nowait("ir-sharp-decoder")
+#else
+static inline void load_sharp_decode(void) { }
+#endif
+
+/* from ir-mce_kbd-decoder.c */
+#ifdef CONFIG_IR_MCE_KBD_DECODER_MODULE
+#define load_mce_kbd_decode() request_module_nowait("ir-mce_kbd-decoder")
#else
-#define load_sony_decode() 0
+static inline void load_mce_kbd_decode(void) { }
#endif
/* from ir-lirc-codec.c */
#ifdef CONFIG_IR_LIRC_CODEC_MODULE
-#define load_lirc_codec() request_module("ir-lirc-codec")
+#define load_lirc_codec() request_module_nowait("ir-lirc-codec")
#else
-#define load_lirc_codec() 0
+static inline void load_lirc_codec(void) { }
#endif
diff --git a/drivers/media/rc/rc-loopback.c b/drivers/media/rc/rc-loopback.c
index 49cee61d79c..0a88e0cf964 100644
--- a/drivers/media/rc/rc-loopback.c
+++ b/drivers/media/rc/rc-loopback.c
@@ -101,29 +101,13 @@ static int loop_set_rx_carrier_range(struct rc_dev *dev, u32 min, u32 max)
return 0;
}
-static int loop_tx_ir(struct rc_dev *dev, int *txbuf, u32 n)
+static int loop_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned count)
{
struct loopback_dev *lodev = dev->priv;
u32 rxmask;
- unsigned count;
- unsigned total_duration = 0;
unsigned i;
DEFINE_IR_RAW_EVENT(rawir);
- if (n == 0 || n % sizeof(int)) {
- dprintk("invalid tx buffer size\n");
- return -EINVAL;
- }
-
- count = n / sizeof(int);
- for (i = 0; i < count; i++)
- total_duration += abs(txbuf[i]);
-
- if (total_duration == 0) {
- dprintk("invalid tx data, total duration zero\n");
- return -EINVAL;
- }
-
if (lodev->txcarrier < lodev->rxcarriermin ||
lodev->txcarrier > lodev->rxcarriermax) {
dprintk("ignoring tx, carrier out of range\n");
@@ -142,17 +126,20 @@ static int loop_tx_ir(struct rc_dev *dev, int *txbuf, u32 n)
for (i = 0; i < count; i++) {
rawir.pulse = i % 2 ? false : true;
- rawir.duration = abs(txbuf[i]) * 1000;
+ rawir.duration = txbuf[i] * 1000;
if (rawir.duration)
ir_raw_event_store_with_filter(dev, &rawir);
}
+
+ /* Fake a silence long enough to cause us to go idle */
+ rawir.pulse = false;
+ rawir.duration = dev->timeout;
+ ir_raw_event_store_with_filter(dev, &rawir);
+
ir_raw_event_handle(dev);
out:
- /* Lirc expects this function to take as long as the total duration */
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(usecs_to_jiffies(total_duration));
- return n;
+ return count;
}
static void loop_set_idle(struct rc_dev *dev, bool enable)
@@ -208,7 +195,7 @@ static int __init loop_init(void)
rc->map_name = RC_MAP_EMPTY;
rc->priv = &loopdev;
rc->driver_type = RC_DRIVER_IR_RAW;
- rc->allowed_protos = RC_TYPE_ALL;
+ rc_set_allowed_protocols(rc, RC_BIT_ALL);
rc->timeout = 100 * 1000 * 1000; /* 100 ms */
rc->min_timeout = 1;
rc->max_timeout = UINT_MAX;
@@ -222,7 +209,6 @@ static int __init loop_init(void)
rc->s_idle = loop_set_idle;
rc->s_learning_mode = loop_set_learning_mode;
rc->s_carrier_report = loop_set_carrier_report;
- rc->priv = &loopdev;
loopdev.txmask = RXMASK_REGULAR;
loopdev.txcarrier = 36000;
diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
index 512a2f4ada0..970b93d6f39 100644
--- a/drivers/media/rc/rc-main.c
+++ b/drivers/media/rc/rc-main.c
@@ -1,6 +1,6 @@
/* rc-main.c - Remote Controller core module
*
- * Copyright (C) 2009-2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (C) 2009-2010 by Mauro Carvalho Chehab
*
* 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
@@ -16,10 +16,16 @@
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <linux/input.h>
+#include <linux/leds.h>
#include <linux/slab.h>
#include <linux/device.h>
+#include <linux/module.h>
#include "rc-core-priv.h"
+/* Bitmap to store allocated device numbers from 0 to IRRCV_NUM_DEVICES - 1 */
+#define IRRCV_NUM_DEVICES 256
+static DECLARE_BITMAP(ir_core_dev_number, IRRCV_NUM_DEVICES);
+
/* Sizes are in bytes, 256 bytes allows for 32 entries on x64 */
#define IR_TAB_MIN_SIZE 256
#define IR_TAB_MAX_SIZE 8192
@@ -30,6 +36,7 @@
/* Used to keep track of known keymaps */
static LIST_HEAD(rc_map_list);
static DEFINE_SPINLOCK(rc_map_lock);
+static struct led_trigger *led_feedback;
static struct rc_map_list *seek_rc_map(const char *name)
{
@@ -55,7 +62,7 @@ struct rc_map *rc_map_get(const char *name)
map = seek_rc_map(name);
#ifdef MODULE
if (!map) {
- int rc = request_module(name);
+ int rc = request_module("%s", name);
if (rc < 0) {
printk(KERN_ERR "Couldn't load IR keymap %s\n", name);
return NULL;
@@ -255,7 +262,7 @@ static unsigned int ir_update_mapping(struct rc_dev *dev,
* @rc_map: scancode table to be searched
* @scancode: the desired scancode
* @resize: controls whether we allowed to resize the table to
- * accomodate not yet present scancodes
+ * accommodate not yet present scancodes
* @return: index of the mapping containing scancode in question
* or -1U in case of failure.
*
@@ -522,18 +529,21 @@ EXPORT_SYMBOL_GPL(rc_g_keycode_from_table);
/**
* ir_do_keyup() - internal function to signal the release of a keypress
* @dev: the struct rc_dev descriptor of the device
+ * @sync: whether or not to call input_sync
*
* This function is used internally to release a keypress, it must be
* called with keylock held.
*/
-static void ir_do_keyup(struct rc_dev *dev)
+static void ir_do_keyup(struct rc_dev *dev, bool sync)
{
if (!dev->keypressed)
return;
IR_dprintk(1, "keyup key 0x%04x\n", dev->last_keycode);
input_report_key(dev->input_dev, dev->last_keycode, 0);
- input_sync(dev->input_dev);
+ led_trigger_event(led_feedback, LED_OFF);
+ if (sync)
+ input_sync(dev->input_dev);
dev->keypressed = false;
}
@@ -549,7 +559,7 @@ void rc_keyup(struct rc_dev *dev)
unsigned long flags;
spin_lock_irqsave(&dev->keylock, flags);
- ir_do_keyup(dev);
+ ir_do_keyup(dev, true);
spin_unlock_irqrestore(&dev->keylock, flags);
}
EXPORT_SYMBOL_GPL(rc_keyup);
@@ -578,7 +588,7 @@ static void ir_timer_keyup(unsigned long cookie)
*/
spin_lock_irqsave(&dev->keylock, flags);
if (time_is_before_eq_jiffies(dev->keyup_jiffies))
- ir_do_keyup(dev);
+ ir_do_keyup(dev, true);
spin_unlock_irqrestore(&dev->keylock, flags);
}
@@ -597,6 +607,7 @@ void rc_repeat(struct rc_dev *dev)
spin_lock_irqsave(&dev->keylock, flags);
input_event(dev->input_dev, EV_MSC, MSC_SCAN, dev->last_scancode);
+ input_sync(dev->input_dev);
if (!dev->keypressed)
goto out;
@@ -622,29 +633,30 @@ EXPORT_SYMBOL_GPL(rc_repeat);
static void ir_do_keydown(struct rc_dev *dev, int scancode,
u32 keycode, u8 toggle)
{
- input_event(dev->input_dev, EV_MSC, MSC_SCAN, scancode);
+ bool new_event = (!dev->keypressed ||
+ dev->last_scancode != scancode ||
+ dev->last_toggle != toggle);
- /* Repeat event? */
- if (dev->keypressed &&
- dev->last_scancode == scancode &&
- dev->last_toggle == toggle)
- return;
+ if (new_event && dev->keypressed)
+ ir_do_keyup(dev, false);
- /* Release old keypress */
- ir_do_keyup(dev);
+ input_event(dev->input_dev, EV_MSC, MSC_SCAN, scancode);
- dev->last_scancode = scancode;
- dev->last_toggle = toggle;
- dev->last_keycode = keycode;
+ if (new_event && keycode != KEY_RESERVED) {
+ /* Register a keypress */
+ dev->keypressed = true;
+ dev->last_scancode = scancode;
+ dev->last_toggle = toggle;
+ dev->last_keycode = keycode;
- if (keycode == KEY_RESERVED)
- return;
+ IR_dprintk(1, "%s: key down event, "
+ "key 0x%04x, scancode 0x%04x\n",
+ dev->input_name, keycode, scancode);
+ input_report_key(dev->input_dev, keycode, 1);
+
+ led_trigger_event(led_feedback, LED_FULL);
+ }
- /* Register a keypress */
- dev->keypressed = true;
- IR_dprintk(1, "%s: key down event, key 0x%04x, scancode 0x%04x\n",
- dev->input_name, keycode, scancode);
- input_report_key(dev->input_dev, dev->last_keycode, 1);
input_sync(dev->input_dev);
}
@@ -696,62 +708,138 @@ void rc_keydown_notimeout(struct rc_dev *dev, int scancode, u8 toggle)
}
EXPORT_SYMBOL_GPL(rc_keydown_notimeout);
+int rc_open(struct rc_dev *rdev)
+{
+ int rval = 0;
+
+ if (!rdev)
+ return -EINVAL;
+
+ mutex_lock(&rdev->lock);
+ if (!rdev->users++ && rdev->open != NULL)
+ rval = rdev->open(rdev);
+
+ if (rval)
+ rdev->users--;
+
+ mutex_unlock(&rdev->lock);
+
+ return rval;
+}
+EXPORT_SYMBOL_GPL(rc_open);
+
static int ir_open(struct input_dev *idev)
{
struct rc_dev *rdev = input_get_drvdata(idev);
- return rdev->open(rdev);
+ return rc_open(rdev);
}
+void rc_close(struct rc_dev *rdev)
+{
+ if (rdev) {
+ mutex_lock(&rdev->lock);
+
+ if (!--rdev->users && rdev->close != NULL)
+ rdev->close(rdev);
+
+ mutex_unlock(&rdev->lock);
+ }
+}
+EXPORT_SYMBOL_GPL(rc_close);
+
static void ir_close(struct input_dev *idev)
{
struct rc_dev *rdev = input_get_drvdata(idev);
-
- rdev->close(rdev);
+ rc_close(rdev);
}
/* class for /sys/class/rc */
-static char *ir_devnode(struct device *dev, mode_t *mode)
+static char *rc_devnode(struct device *dev, umode_t *mode)
{
return kasprintf(GFP_KERNEL, "rc/%s", dev_name(dev));
}
-static struct class ir_input_class = {
+static struct class rc_class = {
.name = "rc",
- .devnode = ir_devnode,
+ .devnode = rc_devnode,
};
+/*
+ * These are the protocol textual descriptions that are
+ * used by the sysfs protocols file. Note that the order
+ * of the entries is relevant.
+ */
static struct {
u64 type;
char *name;
} proto_names[] = {
- { RC_TYPE_UNKNOWN, "unknown" },
- { RC_TYPE_RC5, "rc-5" },
- { RC_TYPE_NEC, "nec" },
- { RC_TYPE_RC6, "rc-6" },
- { RC_TYPE_JVC, "jvc" },
- { RC_TYPE_SONY, "sony" },
- { RC_TYPE_RC5_SZ, "rc-5-sz" },
- { RC_TYPE_LIRC, "lirc" },
+ { RC_BIT_NONE, "none" },
+ { RC_BIT_OTHER, "other" },
+ { RC_BIT_UNKNOWN, "unknown" },
+ { RC_BIT_RC5 |
+ RC_BIT_RC5X, "rc-5" },
+ { RC_BIT_NEC, "nec" },
+ { RC_BIT_RC6_0 |
+ RC_BIT_RC6_6A_20 |
+ RC_BIT_RC6_6A_24 |
+ RC_BIT_RC6_6A_32 |
+ RC_BIT_RC6_MCE, "rc-6" },
+ { RC_BIT_JVC, "jvc" },
+ { RC_BIT_SONY12 |
+ RC_BIT_SONY15 |
+ RC_BIT_SONY20, "sony" },
+ { RC_BIT_RC5_SZ, "rc-5-sz" },
+ { RC_BIT_SANYO, "sanyo" },
+ { RC_BIT_SHARP, "sharp" },
+ { RC_BIT_MCE_KBD, "mce_kbd" },
+ { RC_BIT_LIRC, "lirc" },
+};
+
+/**
+ * struct rc_filter_attribute - Device attribute relating to a filter type.
+ * @attr: Device attribute.
+ * @type: Filter type.
+ * @mask: false for filter value, true for filter mask.
+ */
+struct rc_filter_attribute {
+ struct device_attribute attr;
+ enum rc_filter_type type;
+ bool mask;
};
+#define to_rc_filter_attr(a) container_of(a, struct rc_filter_attribute, attr)
-#define PROTO_NONE "none"
+#define RC_PROTO_ATTR(_name, _mode, _show, _store, _type) \
+ struct rc_filter_attribute dev_attr_##_name = { \
+ .attr = __ATTR(_name, _mode, _show, _store), \
+ .type = (_type), \
+ }
+#define RC_FILTER_ATTR(_name, _mode, _show, _store, _type, _mask) \
+ struct rc_filter_attribute dev_attr_##_name = { \
+ .attr = __ATTR(_name, _mode, _show, _store), \
+ .type = (_type), \
+ .mask = (_mask), \
+ }
/**
- * show_protocols() - shows the current IR protocol(s)
+ * show_protocols() - shows the current/wakeup IR protocol(s)
* @device: the device descriptor
* @mattr: the device attribute struct (unused)
* @buf: a pointer to the output buffer
*
* This routine is a callback routine for input read the IR protocol type(s).
- * it is trigged by reading /sys/class/rc/rc?/protocols.
+ * it is trigged by reading /sys/class/rc/rc?/[wakeup_]protocols.
* It returns the protocol names of supported protocols.
* Enabled protocols are printed in brackets.
+ *
+ * dev->lock is taken to guard against races between device
+ * registration, store_protocols and show_protocols.
*/
static ssize_t show_protocols(struct device *device,
struct device_attribute *mattr, char *buf)
{
struct rc_dev *dev = to_rc_dev(device);
+ struct rc_filter_attribute *fattr = to_rc_filter_attr(mattr);
u64 allowed, enabled;
char *tmp = buf;
int i;
@@ -760,12 +848,17 @@ static ssize_t show_protocols(struct device *device,
if (!dev)
return -EINVAL;
- if (dev->driver_type == RC_DRIVER_SCANCODE) {
- enabled = dev->rc_map.rc_type;
- allowed = dev->allowed_protos;
- } else {
- enabled = dev->raw->enabled_protocols;
+ mutex_lock(&dev->lock);
+
+ enabled = dev->enabled_protocols[fattr->type];
+ if (dev->driver_type == RC_DRIVER_SCANCODE ||
+ fattr->type == RC_FILTER_WAKEUP)
+ allowed = dev->allowed_protocols[fattr->type];
+ else if (dev->raw)
allowed = ir_raw_get_allowed_protocols();
+ else {
+ mutex_unlock(&dev->lock);
+ return -ENODEV;
}
IR_dprintk(1, "allowed - 0x%llx, enabled - 0x%llx\n",
@@ -777,29 +870,38 @@ static ssize_t show_protocols(struct device *device,
tmp += sprintf(tmp, "[%s] ", proto_names[i].name);
else if (allowed & proto_names[i].type)
tmp += sprintf(tmp, "%s ", proto_names[i].name);
+
+ if (allowed & proto_names[i].type)
+ allowed &= ~proto_names[i].type;
}
if (tmp != buf)
tmp--;
*tmp = '\n';
+
+ mutex_unlock(&dev->lock);
+
return tmp + 1 - buf;
}
/**
- * store_protocols() - changes the current IR protocol(s)
+ * store_protocols() - changes the current/wakeup IR protocol(s)
* @device: the device descriptor
* @mattr: the device attribute struct (unused)
* @buf: a pointer to the input buffer
* @len: length of the input buffer
*
* This routine is for changing the IR protocol type.
- * It is trigged by writing to /sys/class/rc/rc?/protocols.
+ * It is trigged by writing to /sys/class/rc/rc?/[wakeup_]protocols.
* Writing "+proto" will add a protocol to the list of enabled protocols.
* Writing "-proto" will remove a protocol from the list of enabled protocols.
* Writing "proto" will enable only "proto".
* Writing "none" will disable all protocols.
* Returns -EINVAL if an invalid protocol combination or unknown protocol name
* is used, otherwise @len.
+ *
+ * dev->lock is taken to guard against races between device
+ * registration, store_protocols and show_protocols.
*/
static ssize_t store_protocols(struct device *device,
struct device_attribute *mattr,
@@ -807,25 +909,30 @@ static ssize_t store_protocols(struct device *device,
size_t len)
{
struct rc_dev *dev = to_rc_dev(device);
+ struct rc_filter_attribute *fattr = to_rc_filter_attr(mattr);
bool enable, disable;
const char *tmp;
- u64 type;
+ u64 old_type, type;
u64 mask;
int rc, i, count = 0;
- unsigned long flags;
+ ssize_t ret;
+ int (*change_protocol)(struct rc_dev *dev, u64 *rc_type);
+ int (*set_filter)(struct rc_dev *dev, struct rc_scancode_filter *filter);
+ struct rc_scancode_filter local_filter, *filter;
/* Device is being removed */
if (!dev)
return -EINVAL;
- if (dev->driver_type == RC_DRIVER_SCANCODE)
- type = dev->rc_map.rc_type;
- else if (dev->raw)
- type = dev->raw->enabled_protocols;
- else {
+ mutex_lock(&dev->lock);
+
+ if (dev->driver_type != RC_DRIVER_SCANCODE && !dev->raw) {
IR_dprintk(1, "Protocol switching not supported\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
+ old_type = dev->enabled_protocols[fattr->type];
+ type = old_type;
while ((tmp = strsep((char **) &data, " \n")) != NULL) {
if (!*tmp)
@@ -844,25 +951,21 @@ static ssize_t store_protocols(struct device *device,
disable = false;
}
- if (!enable && !disable && !strncasecmp(tmp, PROTO_NONE, sizeof(PROTO_NONE))) {
- tmp += sizeof(PROTO_NONE);
- mask = 0;
- count++;
- } else {
- for (i = 0; i < ARRAY_SIZE(proto_names); i++) {
- if (!strncasecmp(tmp, proto_names[i].name, strlen(proto_names[i].name))) {
- tmp += strlen(proto_names[i].name);
- mask = proto_names[i].type;
- break;
- }
- }
- if (i == ARRAY_SIZE(proto_names)) {
- IR_dprintk(1, "Unknown protocol: '%s'\n", tmp);
- return -EINVAL;
+ for (i = 0; i < ARRAY_SIZE(proto_names); i++) {
+ if (!strcasecmp(tmp, proto_names[i].name)) {
+ mask = proto_names[i].type;
+ break;
}
- count++;
}
+ if (i == ARRAY_SIZE(proto_names)) {
+ IR_dprintk(1, "Unknown protocol: '%s'\n", tmp);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ count++;
+
if (enable)
type |= mask;
else if (disable)
@@ -873,38 +976,179 @@ static ssize_t store_protocols(struct device *device,
if (!count) {
IR_dprintk(1, "Protocol not specified\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
- if (dev->change_protocol) {
- rc = dev->change_protocol(dev, type);
+ change_protocol = (fattr->type == RC_FILTER_NORMAL)
+ ? dev->change_protocol : dev->change_wakeup_protocol;
+ if (change_protocol) {
+ rc = change_protocol(dev, &type);
if (rc < 0) {
IR_dprintk(1, "Error setting protocols to 0x%llx\n",
(long long)type);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
}
- if (dev->driver_type == RC_DRIVER_SCANCODE) {
- spin_lock_irqsave(&dev->rc_map.lock, flags);
- dev->rc_map.rc_type = type;
- spin_unlock_irqrestore(&dev->rc_map.lock, flags);
- } else {
- dev->raw->enabled_protocols = type;
- }
-
+ dev->enabled_protocols[fattr->type] = type;
IR_dprintk(1, "Current protocol(s): 0x%llx\n",
(long long)type);
- return len;
+ /*
+ * If the protocol is changed the filter needs updating.
+ * Try setting the same filter with the new protocol (if any).
+ * Fall back to clearing the filter.
+ */
+ filter = &dev->scancode_filters[fattr->type];
+ set_filter = (fattr->type == RC_FILTER_NORMAL)
+ ? dev->s_filter : dev->s_wakeup_filter;
+
+ if (set_filter && old_type != type && filter->mask) {
+ local_filter = *filter;
+ if (!type) {
+ /* no protocol => clear filter */
+ ret = -1;
+ } else {
+ /* hardware filtering => try setting, otherwise clear */
+ ret = set_filter(dev, &local_filter);
+ }
+ if (ret < 0) {
+ /* clear the filter */
+ local_filter.data = 0;
+ local_filter.mask = 0;
+ set_filter(dev, &local_filter);
+ }
+
+ /* commit the new filter */
+ *filter = local_filter;
+ }
+
+ ret = len;
+
+out:
+ mutex_unlock(&dev->lock);
+ return ret;
}
-static void rc_dev_release(struct device *device)
+/**
+ * show_filter() - shows the current scancode filter value or mask
+ * @device: the device descriptor
+ * @attr: the device attribute struct
+ * @buf: a pointer to the output buffer
+ *
+ * This routine is a callback routine to read a scancode filter value or mask.
+ * It is trigged by reading /sys/class/rc/rc?/[wakeup_]filter[_mask].
+ * It prints the current scancode filter value or mask of the appropriate filter
+ * type in hexadecimal into @buf and returns the size of the buffer.
+ *
+ * Bits of the filter value corresponding to set bits in the filter mask are
+ * compared against input scancodes and non-matching scancodes are discarded.
+ *
+ * dev->lock is taken to guard against races between device registration,
+ * store_filter and show_filter.
+ */
+static ssize_t show_filter(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
{
struct rc_dev *dev = to_rc_dev(device);
+ struct rc_filter_attribute *fattr = to_rc_filter_attr(attr);
+ u32 val;
- kfree(dev);
- module_put(THIS_MODULE);
+ /* Device is being removed */
+ if (!dev)
+ return -EINVAL;
+
+ mutex_lock(&dev->lock);
+ if ((fattr->type == RC_FILTER_NORMAL && !dev->s_filter) ||
+ (fattr->type == RC_FILTER_WAKEUP && !dev->s_wakeup_filter))
+ val = 0;
+ else if (fattr->mask)
+ val = dev->scancode_filters[fattr->type].mask;
+ else
+ val = dev->scancode_filters[fattr->type].data;
+ mutex_unlock(&dev->lock);
+
+ return sprintf(buf, "%#x\n", val);
+}
+
+/**
+ * store_filter() - changes the scancode filter value
+ * @device: the device descriptor
+ * @attr: the device attribute struct
+ * @buf: a pointer to the input buffer
+ * @len: length of the input buffer
+ *
+ * This routine is for changing a scancode filter value or mask.
+ * It is trigged by writing to /sys/class/rc/rc?/[wakeup_]filter[_mask].
+ * Returns -EINVAL if an invalid filter value for the current protocol was
+ * specified or if scancode filtering is not supported by the driver, otherwise
+ * returns @len.
+ *
+ * Bits of the filter value corresponding to set bits in the filter mask are
+ * compared against input scancodes and non-matching scancodes are discarded.
+ *
+ * dev->lock is taken to guard against races between device registration,
+ * store_filter and show_filter.
+ */
+static ssize_t store_filter(struct device *device,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct rc_dev *dev = to_rc_dev(device);
+ struct rc_filter_attribute *fattr = to_rc_filter_attr(attr);
+ struct rc_scancode_filter local_filter, *filter;
+ int ret;
+ unsigned long val;
+ int (*set_filter)(struct rc_dev *dev, struct rc_scancode_filter *filter);
+
+ /* Device is being removed */
+ if (!dev)
+ return -EINVAL;
+
+ ret = kstrtoul(buf, 0, &val);
+ if (ret < 0)
+ return ret;
+
+ /* Can the scancode filter be set? */
+ set_filter = (fattr->type == RC_FILTER_NORMAL) ? dev->s_filter :
+ dev->s_wakeup_filter;
+ if (!set_filter)
+ return -EINVAL;
+
+ mutex_lock(&dev->lock);
+
+ /* Tell the driver about the new filter */
+ filter = &dev->scancode_filters[fattr->type];
+ local_filter = *filter;
+ if (fattr->mask)
+ local_filter.mask = val;
+ else
+ local_filter.data = val;
+
+ if (!dev->enabled_protocols[fattr->type] && local_filter.mask) {
+ /* refuse to set a filter unless a protocol is enabled */
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ ret = set_filter(dev, &local_filter);
+ if (ret < 0)
+ goto unlock;
+
+ /* Success, commit the new filter */
+ *filter = local_filter;
+
+unlock:
+ mutex_unlock(&dev->lock);
+ return (ret < 0) ? ret : count;
+}
+
+static void rc_dev_release(struct device *device)
+{
}
#define ADD_HOTPLUG_VAR(fmt, val...) \
@@ -918,6 +1162,9 @@ static int rc_dev_uevent(struct device *device, struct kobj_uevent_env *env)
{
struct rc_dev *dev = to_rc_dev(device);
+ if (!dev || !dev->input_dev)
+ return -ENODEV;
+
if (dev->rc_map.name)
ADD_HOTPLUG_VAR("NAME=%s", dev->rc_map.name);
if (dev->driver_name)
@@ -929,25 +1176,58 @@ static int rc_dev_uevent(struct device *device, struct kobj_uevent_env *env)
/*
* Static device attribute struct with the sysfs attributes for IR's
*/
-static DEVICE_ATTR(protocols, S_IRUGO | S_IWUSR,
- show_protocols, store_protocols);
+static RC_PROTO_ATTR(protocols, S_IRUGO | S_IWUSR,
+ show_protocols, store_protocols, RC_FILTER_NORMAL);
+static RC_PROTO_ATTR(wakeup_protocols, S_IRUGO | S_IWUSR,
+ show_protocols, store_protocols, RC_FILTER_WAKEUP);
+static RC_FILTER_ATTR(filter, S_IRUGO|S_IWUSR,
+ show_filter, store_filter, RC_FILTER_NORMAL, false);
+static RC_FILTER_ATTR(filter_mask, S_IRUGO|S_IWUSR,
+ show_filter, store_filter, RC_FILTER_NORMAL, true);
+static RC_FILTER_ATTR(wakeup_filter, S_IRUGO|S_IWUSR,
+ show_filter, store_filter, RC_FILTER_WAKEUP, false);
+static RC_FILTER_ATTR(wakeup_filter_mask, S_IRUGO|S_IWUSR,
+ show_filter, store_filter, RC_FILTER_WAKEUP, true);
+
+static struct attribute *rc_dev_protocol_attrs[] = {
+ &dev_attr_protocols.attr.attr,
+ NULL,
+};
+
+static struct attribute_group rc_dev_protocol_attr_grp = {
+ .attrs = rc_dev_protocol_attrs,
+};
-static struct attribute *rc_dev_attrs[] = {
- &dev_attr_protocols.attr,
+static struct attribute *rc_dev_wakeup_protocol_attrs[] = {
+ &dev_attr_wakeup_protocols.attr.attr,
NULL,
};
-static struct attribute_group rc_dev_attr_grp = {
- .attrs = rc_dev_attrs,
+static struct attribute_group rc_dev_wakeup_protocol_attr_grp = {
+ .attrs = rc_dev_wakeup_protocol_attrs,
};
-static const struct attribute_group *rc_dev_attr_groups[] = {
- &rc_dev_attr_grp,
- NULL
+static struct attribute *rc_dev_filter_attrs[] = {
+ &dev_attr_filter.attr.attr,
+ &dev_attr_filter_mask.attr.attr,
+ NULL,
+};
+
+static struct attribute_group rc_dev_filter_attr_grp = {
+ .attrs = rc_dev_filter_attrs,
+};
+
+static struct attribute *rc_dev_wakeup_filter_attrs[] = {
+ &dev_attr_wakeup_filter.attr.attr,
+ &dev_attr_wakeup_filter_mask.attr.attr,
+ NULL,
+};
+
+static struct attribute_group rc_dev_wakeup_filter_attr_grp = {
+ .attrs = rc_dev_wakeup_filter_attrs,
};
static struct device_type rc_dev_type = {
- .groups = rc_dev_attr_groups,
.release = rc_dev_release,
.uevent = rc_dev_uevent,
};
@@ -966,16 +1246,17 @@ struct rc_dev *rc_allocate_device(void)
return NULL;
}
- dev->input_dev->getkeycode_new = ir_getkeycode;
- dev->input_dev->setkeycode_new = ir_setkeycode;
+ dev->input_dev->getkeycode = ir_getkeycode;
+ dev->input_dev->setkeycode = ir_setkeycode;
input_set_drvdata(dev->input_dev, dev);
spin_lock_init(&dev->rc_map.lock);
spin_lock_init(&dev->keylock);
+ mutex_init(&dev->lock);
setup_timer(&dev->timer_keyup, ir_timer_keyup, (unsigned long)dev);
dev->dev.type = &rc_dev_type;
- dev->dev.class = &ir_input_class;
+ dev->dev.class = &rc_class;
device_initialize(&dev->dev);
__module_get(THIS_MODULE);
@@ -985,19 +1266,25 @@ EXPORT_SYMBOL_GPL(rc_allocate_device);
void rc_free_device(struct rc_dev *dev)
{
- if (dev) {
+ if (!dev)
+ return;
+
+ if (dev->input_dev)
input_free_device(dev->input_dev);
- put_device(&dev->dev);
- }
+
+ put_device(&dev->dev);
+
+ kfree(dev);
+ module_put(THIS_MODULE);
}
EXPORT_SYMBOL_GPL(rc_free_device);
int rc_register_device(struct rc_dev *dev)
{
- static atomic_t devno = ATOMIC_INIT(0);
+ static bool raw_init = false; /* raw decoders loaded? */
struct rc_map *rc_map;
const char *path;
- int rc;
+ int rc, devno, attr = 0;
if (!dev || !dev->map_name)
return -EINVAL;
@@ -1017,12 +1304,38 @@ int rc_register_device(struct rc_dev *dev)
if (dev->close)
dev->input_dev->close = ir_close;
- dev->devno = (unsigned long)(atomic_inc_return(&devno) - 1);
+ do {
+ devno = find_first_zero_bit(ir_core_dev_number,
+ IRRCV_NUM_DEVICES);
+ /* No free device slots */
+ if (devno >= IRRCV_NUM_DEVICES)
+ return -ENOMEM;
+ } while (test_and_set_bit(devno, ir_core_dev_number));
+
+ dev->dev.groups = dev->sysfs_groups;
+ dev->sysfs_groups[attr++] = &rc_dev_protocol_attr_grp;
+ if (dev->s_filter)
+ dev->sysfs_groups[attr++] = &rc_dev_filter_attr_grp;
+ if (dev->s_wakeup_filter)
+ dev->sysfs_groups[attr++] = &rc_dev_wakeup_filter_attr_grp;
+ if (dev->change_wakeup_protocol)
+ dev->sysfs_groups[attr++] = &rc_dev_wakeup_protocol_attr_grp;
+ dev->sysfs_groups[attr++] = NULL;
+
+ /*
+ * Take the lock here, as the device sysfs node will appear
+ * when device_add() is called, which may trigger an ir-keytable udev
+ * rule, which will in turn call show_protocols and access
+ * dev->enabled_protocols before it has been initialized.
+ */
+ mutex_lock(&dev->lock);
+
+ dev->devno = devno;
dev_set_name(&dev->dev, "rc%ld", dev->devno);
dev_set_drvdata(&dev->dev, dev);
rc = device_add(&dev->dev);
if (rc)
- return rc;
+ goto out_unlock;
rc = ir_setkeytable(dev, rc_map);
if (rc)
@@ -1032,18 +1345,32 @@ int rc_register_device(struct rc_dev *dev)
memcpy(&dev->input_dev->id, &dev->input_id, sizeof(dev->input_id));
dev->input_dev->phys = dev->input_phys;
dev->input_dev->name = dev->input_name;
+
+ /* input_register_device can call ir_open, so unlock mutex here */
+ mutex_unlock(&dev->lock);
+
rc = input_register_device(dev->input_dev);
+
+ mutex_lock(&dev->lock);
+
if (rc)
goto out_table;
/*
- * Default delay of 250ms is too short for some protocols, expecially
+ * Default delay of 250ms is too short for some protocols, especially
* since the timeout is currently set to 250ms. Increase it to 500ms,
* to avoid wrong repetition of the keycodes. Note that this must be
* set after the call to input_register_device().
*/
dev->input_dev->rep[REP_DELAY] = 500;
+ /*
+ * As a repeat event on protocols like RC-5 and NEC take as long as
+ * 110/114ms, using 33ms as a repeat period is not the right thing
+ * to do.
+ */
+ dev->input_dev->rep[REP_PERIOD] = 125;
+
path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
printk(KERN_INFO "%s: %s as %s\n",
dev_name(&dev->dev),
@@ -1052,17 +1379,27 @@ int rc_register_device(struct rc_dev *dev)
kfree(path);
if (dev->driver_type == RC_DRIVER_IR_RAW) {
+ /* Load raw decoders, if they aren't already */
+ if (!raw_init) {
+ IR_dprintk(1, "Loading raw decoders\n");
+ ir_raw_init();
+ raw_init = true;
+ }
rc = ir_raw_event_register(dev);
if (rc < 0)
goto out_input;
}
if (dev->change_protocol) {
- rc = dev->change_protocol(dev, rc_map->rc_type);
+ u64 rc_type = (1 << rc_map->rc_type);
+ rc = dev->change_protocol(dev, &rc_type);
if (rc < 0)
goto out_raw;
+ dev->enabled_protocols[RC_FILTER_NORMAL] = rc_type;
}
+ mutex_unlock(&dev->lock);
+
IR_dprintk(1, "Registered rc%ld (driver: %s, remote: %s, mode %s)\n",
dev->devno,
dev->driver_name ? dev->driver_name : "unknown",
@@ -1081,6 +1418,9 @@ out_table:
ir_free_table(&dev->rc_map);
out_dev:
device_del(&dev->dev);
+out_unlock:
+ mutex_unlock(&dev->lock);
+ clear_bit(dev->devno, ir_core_dev_number);
return rc;
}
EXPORT_SYMBOL_GPL(rc_register_device);
@@ -1092,17 +1432,23 @@ void rc_unregister_device(struct rc_dev *dev)
del_timer_sync(&dev->timer_keyup);
+ clear_bit(dev->devno, ir_core_dev_number);
+
if (dev->driver_type == RC_DRIVER_IR_RAW)
ir_raw_event_unregister(dev);
+ /* Freeing the table should also call the stop callback */
+ ir_free_table(&dev->rc_map);
+ IR_dprintk(1, "Freed keycode table\n");
+
input_unregister_device(dev->input_dev);
dev->input_dev = NULL;
- ir_free_table(&dev->rc_map);
- IR_dprintk(1, "Freed keycode table\n");
+ device_del(&dev->dev);
- device_unregister(&dev->dev);
+ rc_free_device(dev);
}
+
EXPORT_SYMBOL_GPL(rc_unregister_device);
/*
@@ -1111,14 +1457,13 @@ EXPORT_SYMBOL_GPL(rc_unregister_device);
static int __init rc_core_init(void)
{
- int rc = class_register(&ir_input_class);
+ int rc = class_register(&rc_class);
if (rc) {
printk(KERN_ERR "rc_core: unable to register rc class\n");
return rc;
}
- /* Initialize/load the decoders/keymap code that will be used */
- ir_raw_init();
+ led_trigger_register_simple("rc-feedback", &led_feedback);
rc_map_register(&empty_map);
return 0;
@@ -1126,16 +1471,17 @@ static int __init rc_core_init(void)
static void __exit rc_core_exit(void)
{
- class_unregister(&ir_input_class);
+ class_unregister(&rc_class);
+ led_trigger_unregister_simple(led_feedback);
rc_map_unregister(&empty_map);
}
-module_init(rc_core_init);
+subsys_initcall(rc_core_init);
module_exit(rc_core_exit);
int rc_core_debug; /* ir_debug level (0,1,2) */
EXPORT_SYMBOL_GPL(rc_core_debug);
module_param_named(debug, rc_core_debug, int, 0644);
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c
new file mode 100644
index 00000000000..79abbc8d960
--- /dev/null
+++ b/drivers/media/rc/redrat3.c
@@ -0,0 +1,1095 @@
+/*
+ * USB RedRat3 IR Transceiver rc-core driver
+ *
+ * Copyright (c) 2011 by Jarod Wilson <jarod@redhat.com>
+ * based heavily on the work of Stephen Cox, with additional
+ * help from RedRat Ltd.
+ *
+ * This driver began life based an an old version of the first-generation
+ * lirc_mceusb driver from the lirc 0.7.2 distribution. It was then
+ * significantly rewritten by Stephen Cox with the aid of RedRat Ltd's
+ * Chris Dodge.
+ *
+ * The driver was then ported to rc-core and significantly rewritten again,
+ * by Jarod, using the in-kernel mceusb driver as a guide, after an initial
+ * port effort was started by Stephen.
+ *
+ * TODO LIST:
+ * - fix lirc not showing repeats properly
+ * --
+ *
+ * The RedRat3 is a USB transceiver with both send & receive,
+ * with 2 separate sensors available for receive to enable
+ * both good long range reception for general use, and good
+ * short range reception when required for learning a signal.
+ *
+ * http://www.redrat.co.uk/
+ *
+ * It uses its own little protocol to communicate, the required
+ * parts of which are embedded within this driver.
+ * --
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <asm/unaligned.h>
+#include <linux/device.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <media/rc-core.h>
+
+/* Driver Information */
+#define DRIVER_AUTHOR "Jarod Wilson <jarod@redhat.com>"
+#define DRIVER_AUTHOR2 "The Dweller, Stephen Cox"
+#define DRIVER_DESC "RedRat3 USB IR Transceiver Driver"
+#define DRIVER_NAME "redrat3"
+
+/* bulk data transfer types */
+#define RR3_ERROR 0x01
+#define RR3_MOD_SIGNAL_IN 0x20
+#define RR3_MOD_SIGNAL_OUT 0x21
+
+/* Get the RR firmware version */
+#define RR3_FW_VERSION 0xb1
+#define RR3_FW_VERSION_LEN 64
+/* Send encoded signal bulk-sent earlier*/
+#define RR3_TX_SEND_SIGNAL 0xb3
+#define RR3_SET_IR_PARAM 0xb7
+#define RR3_GET_IR_PARAM 0xb8
+/* Blink the red LED on the device */
+#define RR3_BLINK_LED 0xb9
+/* Read serial number of device */
+#define RR3_READ_SER_NO 0xba
+#define RR3_SER_NO_LEN 4
+/* Start capture with the RC receiver */
+#define RR3_RC_DET_ENABLE 0xbb
+/* Stop capture with the RC receiver */
+#define RR3_RC_DET_DISABLE 0xbc
+/* Return the status of RC detector capture */
+#define RR3_RC_DET_STATUS 0xbd
+/* Reset redrat */
+#define RR3_RESET 0xa0
+
+/* Max number of lengths in the signal. */
+#define RR3_IR_IO_MAX_LENGTHS 0x01
+/* Periods to measure mod. freq. */
+#define RR3_IR_IO_PERIODS_MF 0x02
+/* Size of memory for main signal data */
+#define RR3_IR_IO_SIG_MEM_SIZE 0x03
+/* Delta value when measuring lengths */
+#define RR3_IR_IO_LENGTH_FUZZ 0x04
+/* Timeout for end of signal detection */
+#define RR3_IR_IO_SIG_TIMEOUT 0x05
+/* Minimum value for pause recognition. */
+#define RR3_IR_IO_MIN_PAUSE 0x06
+
+/* Clock freq. of EZ-USB chip */
+#define RR3_CLK 24000000
+/* Clock periods per timer count */
+#define RR3_CLK_PER_COUNT 12
+/* (RR3_CLK / RR3_CLK_PER_COUNT) */
+#define RR3_CLK_CONV_FACTOR 2000000
+/* USB bulk-in IR data endpoint address */
+#define RR3_BULK_IN_EP_ADDR 0x82
+
+/* Size of the fixed-length portion of the signal */
+#define RR3_DRIVER_MAXLENS 128
+#define RR3_MAX_SIG_SIZE 512
+#define RR3_TIME_UNIT 50
+#define RR3_END_OF_SIGNAL 0x7f
+#define RR3_TX_TRAILER_LEN 2
+#define RR3_RX_MIN_TIMEOUT 5
+#define RR3_RX_MAX_TIMEOUT 2000
+
+/* The 8051's CPUCS Register address */
+#define RR3_CPUCS_REG_ADDR 0x7f92
+
+#define USB_RR3USB_VENDOR_ID 0x112a
+#define USB_RR3USB_PRODUCT_ID 0x0001
+#define USB_RR3IIUSB_PRODUCT_ID 0x0005
+
+struct redrat3_header {
+ __be16 length;
+ __be16 transfer_type;
+} __packed;
+
+/* sending and receiving irdata */
+struct redrat3_irdata {
+ struct redrat3_header header;
+ __be32 pause;
+ __be16 mod_freq_count;
+ __be16 num_periods;
+ __u8 max_lengths;
+ __u8 no_lengths;
+ __be16 max_sig_size;
+ __be16 sig_size;
+ __u8 no_repeats;
+ __be16 lens[RR3_DRIVER_MAXLENS]; /* not aligned */
+ __u8 sigdata[RR3_MAX_SIG_SIZE];
+} __packed;
+
+/* firmware errors */
+struct redrat3_error {
+ struct redrat3_header header;
+ __be16 fw_error;
+} __packed;
+
+/* table of devices that work with this driver */
+static struct usb_device_id redrat3_dev_table[] = {
+ /* Original version of the RedRat3 */
+ {USB_DEVICE(USB_RR3USB_VENDOR_ID, USB_RR3USB_PRODUCT_ID)},
+ /* Second Version/release of the RedRat3 - RetRat3-II */
+ {USB_DEVICE(USB_RR3USB_VENDOR_ID, USB_RR3IIUSB_PRODUCT_ID)},
+ {} /* Terminating entry */
+};
+
+/* Structure to hold all of our device specific stuff */
+struct redrat3_dev {
+ /* core device bits */
+ struct rc_dev *rc;
+ struct device *dev;
+
+ /* led control */
+ struct led_classdev led;
+ atomic_t flash;
+ struct usb_ctrlrequest flash_control;
+ struct urb *flash_urb;
+ u8 flash_in_buf;
+
+ /* save off the usb device pointer */
+ struct usb_device *udev;
+
+ /* the receive endpoint */
+ struct usb_endpoint_descriptor *ep_in;
+ /* the buffer to receive data */
+ void *bulk_in_buf;
+ /* urb used to read ir data */
+ struct urb *read_urb;
+
+ /* the send endpoint */
+ struct usb_endpoint_descriptor *ep_out;
+
+ /* usb dma */
+ dma_addr_t dma_in;
+
+ /* rx signal timeout timer */
+ struct timer_list rx_timeout;
+ u32 hw_timeout;
+
+ /* Is the device currently transmitting?*/
+ bool transmitting;
+
+ /* store for current packet */
+ struct redrat3_irdata irdata;
+ u16 bytes_read;
+
+ u32 carrier;
+
+ char name[64];
+ char phys[64];
+};
+
+/*
+ * redrat3_issue_async
+ *
+ * Issues an async read to the ir data in port..
+ * sets the callback to be redrat3_handle_async
+ */
+static void redrat3_issue_async(struct redrat3_dev *rr3)
+{
+ int res;
+
+ res = usb_submit_urb(rr3->read_urb, GFP_ATOMIC);
+ if (res)
+ dev_dbg(rr3->dev,
+ "%s: receive request FAILED! (res %d, len %d)\n",
+ __func__, res, rr3->read_urb->transfer_buffer_length);
+}
+
+static void redrat3_dump_fw_error(struct redrat3_dev *rr3, int code)
+{
+ if (!rr3->transmitting && (code != 0x40))
+ dev_info(rr3->dev, "fw error code 0x%02x: ", code);
+
+ switch (code) {
+ case 0x00:
+ pr_cont("No Error\n");
+ break;
+
+ /* Codes 0x20 through 0x2f are IR Firmware Errors */
+ case 0x20:
+ pr_cont("Initial signal pulse not long enough "
+ "to measure carrier frequency\n");
+ break;
+ case 0x21:
+ pr_cont("Not enough length values allocated for signal\n");
+ break;
+ case 0x22:
+ pr_cont("Not enough memory allocated for signal data\n");
+ break;
+ case 0x23:
+ pr_cont("Too many signal repeats\n");
+ break;
+ case 0x28:
+ pr_cont("Insufficient memory available for IR signal "
+ "data memory allocation\n");
+ break;
+ case 0x29:
+ pr_cont("Insufficient memory available "
+ "for IrDa signal data memory allocation\n");
+ break;
+
+ /* Codes 0x30 through 0x3f are USB Firmware Errors */
+ case 0x30:
+ pr_cont("Insufficient memory available for bulk "
+ "transfer structure\n");
+ break;
+
+ /*
+ * Other error codes... These are primarily errors that can occur in
+ * the control messages sent to the redrat
+ */
+ case 0x40:
+ if (!rr3->transmitting)
+ pr_cont("Signal capture has been terminated\n");
+ break;
+ case 0x41:
+ pr_cont("Attempt to set/get and unknown signal I/O "
+ "algorithm parameter\n");
+ break;
+ case 0x42:
+ pr_cont("Signal capture already started\n");
+ break;
+
+ default:
+ pr_cont("Unknown Error\n");
+ break;
+ }
+}
+
+static u32 redrat3_val_to_mod_freq(struct redrat3_irdata *irdata)
+{
+ u32 mod_freq = 0;
+ u16 mod_freq_count = be16_to_cpu(irdata->mod_freq_count);
+
+ if (mod_freq_count != 0)
+ mod_freq = (RR3_CLK * be16_to_cpu(irdata->num_periods)) /
+ (mod_freq_count * RR3_CLK_PER_COUNT);
+
+ return mod_freq;
+}
+
+/* this function scales down the figures for the same result... */
+static u32 redrat3_len_to_us(u32 length)
+{
+ u32 biglen = length * 1000;
+ u32 divisor = (RR3_CLK_CONV_FACTOR) / 1000;
+ u32 result = (u32) (biglen / divisor);
+
+ /* don't allow zero lengths to go back, breaks lirc */
+ return result ? result : 1;
+}
+
+/*
+ * convert us back into redrat3 lengths
+ *
+ * length * 1000 length * 1000000
+ * ------------- = ---------------- = micro
+ * rr3clk / 1000 rr3clk
+
+ * 6 * 2 4 * 3 micro * rr3clk micro * rr3clk / 1000
+ * ----- = 4 ----- = 6 -------------- = len ---------------------
+ * 3 2 1000000 1000
+ */
+static u32 redrat3_us_to_len(u32 microsec)
+{
+ u32 result;
+ u32 divisor;
+
+ microsec &= IR_MAX_DURATION;
+ divisor = (RR3_CLK_CONV_FACTOR / 1000);
+ result = (u32)(microsec * divisor) / 1000;
+
+ /* don't allow zero lengths to go back, breaks lirc */
+ return result ? result : 1;
+}
+
+/* timer callback to send reset event */
+static void redrat3_rx_timeout(unsigned long data)
+{
+ struct redrat3_dev *rr3 = (struct redrat3_dev *)data;
+
+ dev_dbg(rr3->dev, "calling ir_raw_event_reset\n");
+ ir_raw_event_reset(rr3->rc);
+}
+
+static void redrat3_process_ir_data(struct redrat3_dev *rr3)
+{
+ DEFINE_IR_RAW_EVENT(rawir);
+ struct device *dev;
+ unsigned i, trailer = 0;
+ unsigned sig_size, single_len, offset, val;
+ unsigned long delay;
+ u32 mod_freq;
+
+ if (!rr3) {
+ pr_err("%s called with no context!\n", __func__);
+ return;
+ }
+
+ dev = rr3->dev;
+
+ /* Make sure we reset the IR kfifo after a bit of inactivity */
+ delay = usecs_to_jiffies(rr3->hw_timeout);
+ mod_timer(&rr3->rx_timeout, jiffies + delay);
+
+ mod_freq = redrat3_val_to_mod_freq(&rr3->irdata);
+ dev_dbg(dev, "Got mod_freq of %u\n", mod_freq);
+
+ /* process each rr3 encoded byte into an int */
+ sig_size = be16_to_cpu(rr3->irdata.sig_size);
+ for (i = 0; i < sig_size; i++) {
+ offset = rr3->irdata.sigdata[i];
+ val = get_unaligned_be16(&rr3->irdata.lens[offset]);
+ single_len = redrat3_len_to_us(val);
+
+ /* we should always get pulse/space/pulse/space samples */
+ if (i % 2)
+ rawir.pulse = false;
+ else
+ rawir.pulse = true;
+
+ rawir.duration = US_TO_NS(single_len);
+ /* Save initial pulse length to fudge trailer */
+ if (i == 0)
+ trailer = rawir.duration;
+ /* cap the value to IR_MAX_DURATION */
+ rawir.duration &= IR_MAX_DURATION;
+
+ dev_dbg(dev, "storing %s with duration %d (i: %d)\n",
+ rawir.pulse ? "pulse" : "space", rawir.duration, i);
+ ir_raw_event_store_with_filter(rr3->rc, &rawir);
+ }
+
+ /* add a trailing space, if need be */
+ if (i % 2) {
+ rawir.pulse = false;
+ /* this duration is made up, and may not be ideal... */
+ if (trailer < US_TO_NS(1000))
+ rawir.duration = US_TO_NS(2800);
+ else
+ rawir.duration = trailer;
+ dev_dbg(dev, "storing trailing space with duration %d\n",
+ rawir.duration);
+ ir_raw_event_store_with_filter(rr3->rc, &rawir);
+ }
+
+ dev_dbg(dev, "calling ir_raw_event_handle\n");
+ ir_raw_event_handle(rr3->rc);
+}
+
+/* Util fn to send rr3 cmds */
+static u8 redrat3_send_cmd(int cmd, struct redrat3_dev *rr3)
+{
+ struct usb_device *udev;
+ u8 *data;
+ int res;
+
+ data = kzalloc(sizeof(u8), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ udev = rr3->udev;
+ res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), cmd,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0x0000, 0x0000, data, sizeof(u8), HZ * 10);
+
+ if (res < 0) {
+ dev_err(rr3->dev, "%s: Error sending rr3 cmd res %d, data %d",
+ __func__, res, *data);
+ res = -EIO;
+ } else
+ res = data[0];
+
+ kfree(data);
+
+ return res;
+}
+
+/* Enables the long range detector and starts async receive */
+static int redrat3_enable_detector(struct redrat3_dev *rr3)
+{
+ struct device *dev = rr3->dev;
+ u8 ret;
+
+ ret = redrat3_send_cmd(RR3_RC_DET_ENABLE, rr3);
+ if (ret != 0)
+ dev_dbg(dev, "%s: unexpected ret of %d\n",
+ __func__, ret);
+
+ ret = redrat3_send_cmd(RR3_RC_DET_STATUS, rr3);
+ if (ret != 1) {
+ dev_err(dev, "%s: detector status: %d, should be 1\n",
+ __func__, ret);
+ return -EIO;
+ }
+
+ redrat3_issue_async(rr3);
+
+ return 0;
+}
+
+static inline void redrat3_delete(struct redrat3_dev *rr3,
+ struct usb_device *udev)
+{
+ usb_kill_urb(rr3->read_urb);
+ usb_kill_urb(rr3->flash_urb);
+ usb_free_urb(rr3->read_urb);
+ usb_free_urb(rr3->flash_urb);
+ usb_free_coherent(udev, le16_to_cpu(rr3->ep_in->wMaxPacketSize),
+ rr3->bulk_in_buf, rr3->dma_in);
+
+ kfree(rr3);
+}
+
+static u32 redrat3_get_timeout(struct redrat3_dev *rr3)
+{
+ __be32 *tmp;
+ u32 timeout = MS_TO_US(150); /* a sane default, if things go haywire */
+ int len, ret, pipe;
+
+ len = sizeof(*tmp);
+ tmp = kzalloc(len, GFP_KERNEL);
+ if (!tmp) {
+ dev_warn(rr3->dev, "Memory allocation faillure\n");
+ return timeout;
+ }
+
+ pipe = usb_rcvctrlpipe(rr3->udev, 0);
+ ret = usb_control_msg(rr3->udev, pipe, RR3_GET_IR_PARAM,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ RR3_IR_IO_SIG_TIMEOUT, 0, tmp, len, HZ * 5);
+ if (ret != len)
+ dev_warn(rr3->dev, "Failed to read timeout from hardware\n");
+ else {
+ timeout = redrat3_len_to_us(be32_to_cpup(tmp));
+
+ dev_dbg(rr3->dev, "Got timeout of %d ms\n", timeout / 1000);
+ }
+
+ kfree(tmp);
+
+ return timeout;
+}
+
+static void redrat3_reset(struct redrat3_dev *rr3)
+{
+ struct usb_device *udev = rr3->udev;
+ struct device *dev = rr3->dev;
+ int rc, rxpipe, txpipe;
+ u8 *val;
+ int len = sizeof(u8);
+
+ rxpipe = usb_rcvctrlpipe(udev, 0);
+ txpipe = usb_sndctrlpipe(udev, 0);
+
+ val = kmalloc(len, GFP_KERNEL);
+ if (!val) {
+ dev_err(dev, "Memory allocation failure\n");
+ return;
+ }
+
+ *val = 0x01;
+ rc = usb_control_msg(udev, rxpipe, RR3_RESET,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ RR3_CPUCS_REG_ADDR, 0, val, len, HZ * 25);
+ dev_dbg(dev, "reset returned 0x%02x\n", rc);
+
+ *val = 5;
+ rc = usb_control_msg(udev, txpipe, RR3_SET_IR_PARAM,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+ RR3_IR_IO_LENGTH_FUZZ, 0, val, len, HZ * 25);
+ dev_dbg(dev, "set ir parm len fuzz %d rc 0x%02x\n", *val, rc);
+
+ *val = RR3_DRIVER_MAXLENS;
+ rc = usb_control_msg(udev, txpipe, RR3_SET_IR_PARAM,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+ RR3_IR_IO_MAX_LENGTHS, 0, val, len, HZ * 25);
+ dev_dbg(dev, "set ir parm max lens %d rc 0x%02x\n", *val, rc);
+
+ kfree(val);
+}
+
+static void redrat3_get_firmware_rev(struct redrat3_dev *rr3)
+{
+ int rc = 0;
+ char *buffer;
+
+ buffer = kzalloc(sizeof(char) * (RR3_FW_VERSION_LEN + 1), GFP_KERNEL);
+ if (!buffer) {
+ dev_err(rr3->dev, "Memory allocation failure\n");
+ return;
+ }
+
+ rc = usb_control_msg(rr3->udev, usb_rcvctrlpipe(rr3->udev, 0),
+ RR3_FW_VERSION,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0, 0, buffer, RR3_FW_VERSION_LEN, HZ * 5);
+
+ if (rc >= 0)
+ dev_info(rr3->dev, "Firmware rev: %s", buffer);
+ else
+ dev_err(rr3->dev, "Problem fetching firmware ID\n");
+
+ kfree(buffer);
+}
+
+static void redrat3_read_packet_start(struct redrat3_dev *rr3, unsigned len)
+{
+ struct redrat3_header *header = rr3->bulk_in_buf;
+ unsigned pktlen, pkttype;
+
+ /* grab the Length and type of transfer */
+ pktlen = be16_to_cpu(header->length);
+ pkttype = be16_to_cpu(header->transfer_type);
+
+ if (pktlen > sizeof(rr3->irdata)) {
+ dev_warn(rr3->dev, "packet length %u too large\n", pktlen);
+ return;
+ }
+
+ switch (pkttype) {
+ case RR3_ERROR:
+ if (len >= sizeof(struct redrat3_error)) {
+ struct redrat3_error *error = rr3->bulk_in_buf;
+ unsigned fw_error = be16_to_cpu(error->fw_error);
+ redrat3_dump_fw_error(rr3, fw_error);
+ }
+ break;
+
+ case RR3_MOD_SIGNAL_IN:
+ memcpy(&rr3->irdata, rr3->bulk_in_buf, len);
+ rr3->bytes_read = len;
+ dev_dbg(rr3->dev, "bytes_read %d, pktlen %d\n",
+ rr3->bytes_read, pktlen);
+ break;
+
+ default:
+ dev_dbg(rr3->dev, "ignoring packet with type 0x%02x, len of %d, 0x%02x\n",
+ pkttype, len, pktlen);
+ break;
+ }
+}
+
+static void redrat3_read_packet_continue(struct redrat3_dev *rr3, unsigned len)
+{
+ void *irdata = &rr3->irdata;
+
+ if (len + rr3->bytes_read > sizeof(rr3->irdata)) {
+ dev_warn(rr3->dev, "too much data for packet\n");
+ rr3->bytes_read = 0;
+ return;
+ }
+
+ memcpy(irdata + rr3->bytes_read, rr3->bulk_in_buf, len);
+
+ rr3->bytes_read += len;
+ dev_dbg(rr3->dev, "bytes_read %d, pktlen %d\n", rr3->bytes_read,
+ be16_to_cpu(rr3->irdata.header.length));
+}
+
+/* gather IR data from incoming urb, process it when we have enough */
+static int redrat3_get_ir_data(struct redrat3_dev *rr3, unsigned len)
+{
+ struct device *dev = rr3->dev;
+ unsigned pkttype;
+ int ret = 0;
+
+ if (rr3->bytes_read == 0 && len >= sizeof(struct redrat3_header)) {
+ redrat3_read_packet_start(rr3, len);
+ } else if (rr3->bytes_read != 0) {
+ redrat3_read_packet_continue(rr3, len);
+ } else if (rr3->bytes_read == 0) {
+ dev_err(dev, "error: no packet data read\n");
+ ret = -ENODATA;
+ goto out;
+ }
+
+ if (rr3->bytes_read < be16_to_cpu(rr3->irdata.header.length) +
+ sizeof(struct redrat3_header))
+ /* we're still accumulating data */
+ return 0;
+
+ /* if we get here, we've got IR data to decode */
+ pkttype = be16_to_cpu(rr3->irdata.header.transfer_type);
+ if (pkttype == RR3_MOD_SIGNAL_IN)
+ redrat3_process_ir_data(rr3);
+ else
+ dev_dbg(dev, "discarding non-signal data packet (type 0x%02x)\n",
+ pkttype);
+
+out:
+ rr3->bytes_read = 0;
+ return ret;
+}
+
+/* callback function from USB when async USB request has completed */
+static void redrat3_handle_async(struct urb *urb)
+{
+ struct redrat3_dev *rr3;
+ int ret;
+
+ if (!urb)
+ return;
+
+ rr3 = urb->context;
+ if (!rr3) {
+ pr_err("%s called with invalid context!\n", __func__);
+ usb_unlink_urb(urb);
+ return;
+ }
+
+ switch (urb->status) {
+ case 0:
+ ret = redrat3_get_ir_data(rr3, urb->actual_length);
+ if (!ret) {
+ /* no error, prepare to read more */
+ redrat3_issue_async(rr3);
+ }
+ break;
+
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ usb_unlink_urb(urb);
+ return;
+
+ case -EPIPE:
+ default:
+ dev_warn(rr3->dev, "Error: urb status = %d\n", urb->status);
+ rr3->bytes_read = 0;
+ break;
+ }
+}
+
+static u16 mod_freq_to_val(unsigned int mod_freq)
+{
+ int mult = 6000000;
+
+ /* Clk used in mod. freq. generation is CLK24/4. */
+ return 65536 - (mult / mod_freq);
+}
+
+static int redrat3_set_tx_carrier(struct rc_dev *rcdev, u32 carrier)
+{
+ struct redrat3_dev *rr3 = rcdev->priv;
+ struct device *dev = rr3->dev;
+
+ dev_dbg(dev, "Setting modulation frequency to %u", carrier);
+ if (carrier == 0)
+ return -EINVAL;
+
+ rr3->carrier = carrier;
+
+ return carrier;
+}
+
+static int redrat3_transmit_ir(struct rc_dev *rcdev, unsigned *txbuf,
+ unsigned count)
+{
+ struct redrat3_dev *rr3 = rcdev->priv;
+ struct device *dev = rr3->dev;
+ struct redrat3_irdata *irdata = NULL;
+ int ret, ret_len;
+ int lencheck, cur_sample_len, pipe;
+ int *sample_lens = NULL;
+ u8 curlencheck = 0;
+ unsigned i, sendbuf_len;
+
+ if (rr3->transmitting) {
+ dev_warn(dev, "%s: transmitter already in use\n", __func__);
+ return -EAGAIN;
+ }
+
+ if (count > RR3_MAX_SIG_SIZE - RR3_TX_TRAILER_LEN)
+ return -EINVAL;
+
+ /* rr3 will disable rc detector on transmit */
+ rr3->transmitting = true;
+
+ sample_lens = kzalloc(sizeof(int) * RR3_DRIVER_MAXLENS, GFP_KERNEL);
+ if (!sample_lens) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ irdata = kzalloc(sizeof(*irdata), GFP_KERNEL);
+ if (!irdata) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < count; i++) {
+ cur_sample_len = redrat3_us_to_len(txbuf[i]);
+ if (cur_sample_len > 0xffff) {
+ dev_warn(dev, "transmit period of %uus truncated to %uus\n",
+ txbuf[i], redrat3_len_to_us(0xffff));
+ cur_sample_len = 0xffff;
+ }
+ for (lencheck = 0; lencheck < curlencheck; lencheck++) {
+ if (sample_lens[lencheck] == cur_sample_len)
+ break;
+ }
+ if (lencheck == curlencheck) {
+ dev_dbg(dev, "txbuf[%d]=%u, pos %d, enc %u\n",
+ i, txbuf[i], curlencheck, cur_sample_len);
+ if (curlencheck < RR3_DRIVER_MAXLENS) {
+ /* now convert the value to a proper
+ * rr3 value.. */
+ sample_lens[curlencheck] = cur_sample_len;
+ put_unaligned_be16(cur_sample_len,
+ &irdata->lens[curlencheck]);
+ curlencheck++;
+ } else {
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+ irdata->sigdata[i] = lencheck;
+ }
+
+ irdata->sigdata[count] = RR3_END_OF_SIGNAL;
+ irdata->sigdata[count + 1] = RR3_END_OF_SIGNAL;
+
+ sendbuf_len = offsetof(struct redrat3_irdata,
+ sigdata[count + RR3_TX_TRAILER_LEN]);
+ /* fill in our packet header */
+ irdata->header.length = cpu_to_be16(sendbuf_len -
+ sizeof(struct redrat3_header));
+ irdata->header.transfer_type = cpu_to_be16(RR3_MOD_SIGNAL_OUT);
+ irdata->pause = cpu_to_be32(redrat3_len_to_us(100));
+ irdata->mod_freq_count = cpu_to_be16(mod_freq_to_val(rr3->carrier));
+ irdata->no_lengths = curlencheck;
+ irdata->sig_size = cpu_to_be16(count + RR3_TX_TRAILER_LEN);
+
+ pipe = usb_sndbulkpipe(rr3->udev, rr3->ep_out->bEndpointAddress);
+ ret = usb_bulk_msg(rr3->udev, pipe, irdata,
+ sendbuf_len, &ret_len, 10 * HZ);
+ dev_dbg(dev, "sent %d bytes, (ret %d)\n", ret_len, ret);
+
+ /* now tell the hardware to transmit what we sent it */
+ pipe = usb_rcvctrlpipe(rr3->udev, 0);
+ ret = usb_control_msg(rr3->udev, pipe, RR3_TX_SEND_SIGNAL,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0, 0, irdata, 2, HZ * 10);
+
+ if (ret < 0)
+ dev_err(dev, "Error: control msg send failed, rc %d\n", ret);
+ else
+ ret = count;
+
+out:
+ kfree(sample_lens);
+ kfree(irdata);
+
+ rr3->transmitting = false;
+ /* rr3 re-enables rc detector because it was enabled before */
+
+ return ret;
+}
+
+static void redrat3_brightness_set(struct led_classdev *led_dev, enum
+ led_brightness brightness)
+{
+ struct redrat3_dev *rr3 = container_of(led_dev, struct redrat3_dev,
+ led);
+
+ if (brightness != LED_OFF && atomic_cmpxchg(&rr3->flash, 0, 1) == 0) {
+ int ret = usb_submit_urb(rr3->flash_urb, GFP_ATOMIC);
+ if (ret != 0) {
+ dev_dbg(rr3->dev, "%s: unexpected ret of %d\n",
+ __func__, ret);
+ atomic_set(&rr3->flash, 0);
+ }
+ }
+}
+
+static void redrat3_led_complete(struct urb *urb)
+{
+ struct redrat3_dev *rr3 = urb->context;
+
+ switch (urb->status) {
+ case 0:
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ usb_unlink_urb(urb);
+ return;
+ case -EPIPE:
+ default:
+ dev_dbg(rr3->dev, "Error: urb status = %d\n", urb->status);
+ break;
+ }
+
+ rr3->led.brightness = LED_OFF;
+ atomic_dec(&rr3->flash);
+}
+
+static struct rc_dev *redrat3_init_rc_dev(struct redrat3_dev *rr3)
+{
+ struct device *dev = rr3->dev;
+ struct rc_dev *rc;
+ int ret = -ENODEV;
+ u16 prod = le16_to_cpu(rr3->udev->descriptor.idProduct);
+
+ rc = rc_allocate_device();
+ if (!rc) {
+ dev_err(dev, "remote input dev allocation failed\n");
+ goto out;
+ }
+
+ snprintf(rr3->name, sizeof(rr3->name), "RedRat3%s "
+ "Infrared Remote Transceiver (%04x:%04x)",
+ prod == USB_RR3IIUSB_PRODUCT_ID ? "-II" : "",
+ le16_to_cpu(rr3->udev->descriptor.idVendor), prod);
+
+ usb_make_path(rr3->udev, rr3->phys, sizeof(rr3->phys));
+
+ rc->input_name = rr3->name;
+ rc->input_phys = rr3->phys;
+ usb_to_input_id(rr3->udev, &rc->input_id);
+ rc->dev.parent = dev;
+ rc->priv = rr3;
+ rc->driver_type = RC_DRIVER_IR_RAW;
+ rc_set_allowed_protocols(rc, RC_BIT_ALL);
+ rc->timeout = US_TO_NS(2750);
+ rc->tx_ir = redrat3_transmit_ir;
+ rc->s_tx_carrier = redrat3_set_tx_carrier;
+ rc->driver_name = DRIVER_NAME;
+ rc->rx_resolution = US_TO_NS(2);
+ rc->map_name = RC_MAP_HAUPPAUGE;
+
+ ret = rc_register_device(rc);
+ if (ret < 0) {
+ dev_err(dev, "remote dev registration failed\n");
+ goto out;
+ }
+
+ return rc;
+
+out:
+ rc_free_device(rc);
+ return NULL;
+}
+
+static int redrat3_dev_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct device *dev = &intf->dev;
+ struct usb_host_interface *uhi;
+ struct redrat3_dev *rr3;
+ struct usb_endpoint_descriptor *ep;
+ struct usb_endpoint_descriptor *ep_in = NULL;
+ struct usb_endpoint_descriptor *ep_out = NULL;
+ u8 addr, attrs;
+ int pipe, i;
+ int retval = -ENOMEM;
+
+ uhi = intf->cur_altsetting;
+
+ /* find our bulk-in and bulk-out endpoints */
+ for (i = 0; i < uhi->desc.bNumEndpoints; ++i) {
+ ep = &uhi->endpoint[i].desc;
+ addr = ep->bEndpointAddress;
+ attrs = ep->bmAttributes;
+
+ if ((ep_in == NULL) &&
+ ((addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) &&
+ ((attrs & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_BULK)) {
+ dev_dbg(dev, "found bulk-in endpoint at 0x%02x\n",
+ ep->bEndpointAddress);
+ /* data comes in on 0x82, 0x81 is for other data... */
+ if (ep->bEndpointAddress == RR3_BULK_IN_EP_ADDR)
+ ep_in = ep;
+ }
+
+ if ((ep_out == NULL) &&
+ ((addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) &&
+ ((attrs & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_BULK)) {
+ dev_dbg(dev, "found bulk-out endpoint at 0x%02x\n",
+ ep->bEndpointAddress);
+ ep_out = ep;
+ }
+ }
+
+ if (!ep_in || !ep_out) {
+ dev_err(dev, "Couldn't find both in and out endpoints\n");
+ retval = -ENODEV;
+ goto no_endpoints;
+ }
+
+ /* allocate memory for our device state and initialize it */
+ rr3 = kzalloc(sizeof(*rr3), GFP_KERNEL);
+ if (rr3 == NULL) {
+ dev_err(dev, "Memory allocation failure\n");
+ goto no_endpoints;
+ }
+
+ rr3->dev = &intf->dev;
+
+ /* set up bulk-in endpoint */
+ rr3->read_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!rr3->read_urb) {
+ dev_err(dev, "Read urb allocation failure\n");
+ goto error;
+ }
+
+ rr3->ep_in = ep_in;
+ rr3->bulk_in_buf = usb_alloc_coherent(udev,
+ le16_to_cpu(ep_in->wMaxPacketSize), GFP_ATOMIC, &rr3->dma_in);
+ if (!rr3->bulk_in_buf) {
+ dev_err(dev, "Read buffer allocation failure\n");
+ goto error;
+ }
+
+ pipe = usb_rcvbulkpipe(udev, ep_in->bEndpointAddress);
+ usb_fill_bulk_urb(rr3->read_urb, udev, pipe, rr3->bulk_in_buf,
+ le16_to_cpu(ep_in->wMaxPacketSize), redrat3_handle_async, rr3);
+
+ rr3->ep_out = ep_out;
+ rr3->udev = udev;
+
+ redrat3_reset(rr3);
+ redrat3_get_firmware_rev(rr3);
+
+ /* might be all we need to do? */
+ retval = redrat3_enable_detector(rr3);
+ if (retval < 0)
+ goto error;
+
+ /* store current hardware timeout, in us, will use for kfifo resets */
+ rr3->hw_timeout = redrat3_get_timeout(rr3);
+
+ /* default.. will get overridden by any sends with a freq defined */
+ rr3->carrier = 38000;
+
+ /* led control */
+ rr3->led.name = "redrat3:red:feedback";
+ rr3->led.default_trigger = "rc-feedback";
+ rr3->led.brightness_set = redrat3_brightness_set;
+ retval = led_classdev_register(&intf->dev, &rr3->led);
+ if (retval)
+ goto error;
+
+ atomic_set(&rr3->flash, 0);
+ rr3->flash_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!rr3->flash_urb) {
+ retval = -ENOMEM;
+ goto led_free_error;
+ }
+
+ /* setup packet is 'c0 b9 0000 0000 0001' */
+ rr3->flash_control.bRequestType = 0xc0;
+ rr3->flash_control.bRequest = RR3_BLINK_LED;
+ rr3->flash_control.wLength = cpu_to_le16(1);
+
+ usb_fill_control_urb(rr3->flash_urb, udev, usb_rcvctrlpipe(udev, 0),
+ (unsigned char *)&rr3->flash_control,
+ &rr3->flash_in_buf, sizeof(rr3->flash_in_buf),
+ redrat3_led_complete, rr3);
+
+ rr3->rc = redrat3_init_rc_dev(rr3);
+ if (!rr3->rc) {
+ retval = -ENOMEM;
+ goto led_free_error;
+ }
+ setup_timer(&rr3->rx_timeout, redrat3_rx_timeout, (unsigned long)rr3);
+
+ /* we can register the device now, as it is ready */
+ usb_set_intfdata(intf, rr3);
+
+ return 0;
+
+led_free_error:
+ led_classdev_unregister(&rr3->led);
+error:
+ redrat3_delete(rr3, rr3->udev);
+
+no_endpoints:
+ dev_err(dev, "%s: retval = %x", __func__, retval);
+
+ return retval;
+}
+
+static void redrat3_dev_disconnect(struct usb_interface *intf)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct redrat3_dev *rr3 = usb_get_intfdata(intf);
+
+ if (!rr3)
+ return;
+
+ usb_set_intfdata(intf, NULL);
+ rc_unregister_device(rr3->rc);
+ led_classdev_unregister(&rr3->led);
+ del_timer_sync(&rr3->rx_timeout);
+ redrat3_delete(rr3, udev);
+}
+
+static int redrat3_dev_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct redrat3_dev *rr3 = usb_get_intfdata(intf);
+
+ led_classdev_suspend(&rr3->led);
+ usb_kill_urb(rr3->read_urb);
+ usb_kill_urb(rr3->flash_urb);
+ return 0;
+}
+
+static int redrat3_dev_resume(struct usb_interface *intf)
+{
+ struct redrat3_dev *rr3 = usb_get_intfdata(intf);
+
+ if (usb_submit_urb(rr3->read_urb, GFP_ATOMIC))
+ return -EIO;
+ led_classdev_resume(&rr3->led);
+ return 0;
+}
+
+static struct usb_driver redrat3_dev_driver = {
+ .name = DRIVER_NAME,
+ .probe = redrat3_dev_probe,
+ .disconnect = redrat3_dev_disconnect,
+ .suspend = redrat3_dev_suspend,
+ .resume = redrat3_dev_resume,
+ .reset_resume = redrat3_dev_resume,
+ .id_table = redrat3_dev_table
+};
+
+module_usb_driver(redrat3_dev_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_AUTHOR(DRIVER_AUTHOR2);
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, redrat3_dev_table);
diff --git a/drivers/media/rc/st_rc.c b/drivers/media/rc/st_rc.c
new file mode 100644
index 00000000000..22e4c1f28ab
--- /dev/null
+++ b/drivers/media/rc/st_rc.c
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) 2013 STMicroelectronics Limited
+ * Author: Srinivas Kandagatla <srinivas.kandagatla@st.com>
+ *
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <media/rc-core.h>
+#include <linux/pinctrl/consumer.h>
+
+struct st_rc_device {
+ struct device *dev;
+ int irq;
+ int irq_wake;
+ struct clk *sys_clock;
+ void *base; /* Register base address */
+ void *rx_base;/* RX Register base address */
+ struct rc_dev *rdev;
+ bool overclocking;
+ int sample_mult;
+ int sample_div;
+ bool rxuhfmode;
+ struct reset_control *rstc;
+};
+
+/* Registers */
+#define IRB_SAMPLE_RATE_COMM 0x64 /* sample freq divisor*/
+#define IRB_CLOCK_SEL 0x70 /* clock select */
+#define IRB_CLOCK_SEL_STATUS 0x74 /* clock status */
+/* IRB IR/UHF receiver registers */
+#define IRB_RX_ON 0x40 /* pulse time capture */
+#define IRB_RX_SYS 0X44 /* sym period capture */
+#define IRB_RX_INT_EN 0x48 /* IRQ enable (R/W) */
+#define IRB_RX_INT_STATUS 0x4c /* IRQ status (R/W) */
+#define IRB_RX_EN 0x50 /* Receive enable */
+#define IRB_MAX_SYM_PERIOD 0x54 /* max sym value */
+#define IRB_RX_INT_CLEAR 0x58 /* overrun status */
+#define IRB_RX_STATUS 0x6c /* receive status */
+#define IRB_RX_NOISE_SUPPR 0x5c /* noise suppression */
+#define IRB_RX_POLARITY_INV 0x68 /* polarity inverter */
+
+/**
+ * IRQ set: Enable full FIFO 1 -> bit 3;
+ * Enable overrun IRQ 1 -> bit 2;
+ * Enable last symbol IRQ 1 -> bit 1:
+ * Enable RX interrupt 1 -> bit 0;
+ */
+#define IRB_RX_INTS 0x0f
+#define IRB_RX_OVERRUN_INT 0x04
+ /* maximum symbol period (microsecs),timeout to detect end of symbol train */
+#define MAX_SYMB_TIME 0x5000
+#define IRB_SAMPLE_FREQ 10000000
+#define IRB_FIFO_NOT_EMPTY 0xff00
+#define IRB_OVERFLOW 0x4
+#define IRB_TIMEOUT 0xffff
+#define IR_ST_NAME "st-rc"
+
+static void st_rc_send_lirc_timeout(struct rc_dev *rdev)
+{
+ DEFINE_IR_RAW_EVENT(ev);
+ ev.timeout = true;
+ ir_raw_event_store(rdev, &ev);
+}
+
+/**
+ * RX graphical example to better understand the difference between ST IR block
+ * output and standard definition used by LIRC (and most of the world!)
+ *
+ * mark mark
+ * |-IRB_RX_ON-| |-IRB_RX_ON-|
+ * ___ ___ ___ ___ ___ ___ _
+ * | | | | | | | | | | | | |
+ * | | | | | | space 0 | | | | | | space 1 |
+ * _____| |__| |__| |____________________________| |__| |__| |_____________|
+ *
+ * |--------------- IRB_RX_SYS -------------|------ IRB_RX_SYS -------|
+ *
+ * |------------- encoding bit 0 -----------|---- encoding bit 1 -----|
+ *
+ * ST hardware returns mark (IRB_RX_ON) and total symbol time (IRB_RX_SYS), so
+ * convert to standard mark/space we have to calculate space=(IRB_RX_SYS-mark)
+ * The mark time represents the amount of time the carrier (usually 36-40kHz)
+ * is detected.The above examples shows Pulse Width Modulation encoding where
+ * bit 0 is represented by space>mark.
+ */
+
+static irqreturn_t st_rc_rx_interrupt(int irq, void *data)
+{
+ unsigned int symbol, mark = 0;
+ struct st_rc_device *dev = data;
+ int last_symbol = 0;
+ u32 status;
+ DEFINE_IR_RAW_EVENT(ev);
+
+ if (dev->irq_wake)
+ pm_wakeup_event(dev->dev, 0);
+
+ status = readl(dev->rx_base + IRB_RX_STATUS);
+
+ while (status & (IRB_FIFO_NOT_EMPTY | IRB_OVERFLOW)) {
+ u32 int_status = readl(dev->rx_base + IRB_RX_INT_STATUS);
+ if (unlikely(int_status & IRB_RX_OVERRUN_INT)) {
+ /* discard the entire collection in case of errors! */
+ ir_raw_event_reset(dev->rdev);
+ dev_info(dev->dev, "IR RX overrun\n");
+ writel(IRB_RX_OVERRUN_INT,
+ dev->rx_base + IRB_RX_INT_CLEAR);
+ continue;
+ }
+
+ symbol = readl(dev->rx_base + IRB_RX_SYS);
+ mark = readl(dev->rx_base + IRB_RX_ON);
+
+ if (symbol == IRB_TIMEOUT)
+ last_symbol = 1;
+
+ /* Ignore any noise */
+ if ((mark > 2) && (symbol > 1)) {
+ symbol -= mark;
+ if (dev->overclocking) { /* adjustments to timings */
+ symbol *= dev->sample_mult;
+ symbol /= dev->sample_div;
+ mark *= dev->sample_mult;
+ mark /= dev->sample_div;
+ }
+
+ ev.duration = US_TO_NS(mark);
+ ev.pulse = true;
+ ir_raw_event_store(dev->rdev, &ev);
+
+ if (!last_symbol) {
+ ev.duration = US_TO_NS(symbol);
+ ev.pulse = false;
+ ir_raw_event_store(dev->rdev, &ev);
+ } else {
+ st_rc_send_lirc_timeout(dev->rdev);
+ }
+
+ }
+ last_symbol = 0;
+ status = readl(dev->rx_base + IRB_RX_STATUS);
+ }
+
+ writel(IRB_RX_INTS, dev->rx_base + IRB_RX_INT_CLEAR);
+
+ /* Empty software fifo */
+ ir_raw_event_handle(dev->rdev);
+ return IRQ_HANDLED;
+}
+
+static void st_rc_hardware_init(struct st_rc_device *dev)
+{
+ int baseclock, freqdiff;
+ unsigned int rx_max_symbol_per = MAX_SYMB_TIME;
+ unsigned int rx_sampling_freq_div;
+
+ /* Enable the IP */
+ if (dev->rstc)
+ reset_control_deassert(dev->rstc);
+
+ clk_prepare_enable(dev->sys_clock);
+ baseclock = clk_get_rate(dev->sys_clock);
+
+ /* IRB input pins are inverted internally from high to low. */
+ writel(1, dev->rx_base + IRB_RX_POLARITY_INV);
+
+ rx_sampling_freq_div = baseclock / IRB_SAMPLE_FREQ;
+ writel(rx_sampling_freq_div, dev->base + IRB_SAMPLE_RATE_COMM);
+
+ freqdiff = baseclock - (rx_sampling_freq_div * IRB_SAMPLE_FREQ);
+ if (freqdiff) { /* over clocking, workout the adjustment factors */
+ dev->overclocking = true;
+ dev->sample_mult = 1000;
+ dev->sample_div = baseclock / (10000 * rx_sampling_freq_div);
+ rx_max_symbol_per = (rx_max_symbol_per * 1000)/dev->sample_div;
+ }
+
+ writel(rx_max_symbol_per, dev->rx_base + IRB_MAX_SYM_PERIOD);
+}
+
+static int st_rc_remove(struct platform_device *pdev)
+{
+ struct st_rc_device *rc_dev = platform_get_drvdata(pdev);
+ clk_disable_unprepare(rc_dev->sys_clock);
+ rc_unregister_device(rc_dev->rdev);
+ return 0;
+}
+
+static int st_rc_open(struct rc_dev *rdev)
+{
+ struct st_rc_device *dev = rdev->priv;
+ unsigned long flags;
+ local_irq_save(flags);
+ /* enable interrupts and receiver */
+ writel(IRB_RX_INTS, dev->rx_base + IRB_RX_INT_EN);
+ writel(0x01, dev->rx_base + IRB_RX_EN);
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+static void st_rc_close(struct rc_dev *rdev)
+{
+ struct st_rc_device *dev = rdev->priv;
+ /* disable interrupts and receiver */
+ writel(0x00, dev->rx_base + IRB_RX_EN);
+ writel(0x00, dev->rx_base + IRB_RX_INT_EN);
+}
+
+static int st_rc_probe(struct platform_device *pdev)
+{
+ int ret = -EINVAL;
+ struct rc_dev *rdev;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ struct st_rc_device *rc_dev;
+ struct device_node *np = pdev->dev.of_node;
+ const char *rx_mode;
+
+ rc_dev = devm_kzalloc(dev, sizeof(struct st_rc_device), GFP_KERNEL);
+
+ if (!rc_dev)
+ return -ENOMEM;
+
+ rdev = rc_allocate_device();
+
+ if (!rdev)
+ return -ENOMEM;
+
+ if (np && !of_property_read_string(np, "rx-mode", &rx_mode)) {
+
+ if (!strcmp(rx_mode, "uhf")) {
+ rc_dev->rxuhfmode = true;
+ } else if (!strcmp(rx_mode, "infrared")) {
+ rc_dev->rxuhfmode = false;
+ } else {
+ dev_err(dev, "Unsupported rx mode [%s]\n", rx_mode);
+ goto err;
+ }
+
+ } else {
+ goto err;
+ }
+
+ rc_dev->sys_clock = devm_clk_get(dev, NULL);
+ if (IS_ERR(rc_dev->sys_clock)) {
+ dev_err(dev, "System clock not found\n");
+ ret = PTR_ERR(rc_dev->sys_clock);
+ goto err;
+ }
+
+ rc_dev->irq = platform_get_irq(pdev, 0);
+ if (rc_dev->irq < 0) {
+ ret = rc_dev->irq;
+ goto err;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ rc_dev->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(rc_dev->base)) {
+ ret = PTR_ERR(rc_dev->base);
+ goto err;
+ }
+
+ if (rc_dev->rxuhfmode)
+ rc_dev->rx_base = rc_dev->base + 0x40;
+ else
+ rc_dev->rx_base = rc_dev->base;
+
+
+ rc_dev->rstc = reset_control_get(dev, NULL);
+ if (IS_ERR(rc_dev->rstc))
+ rc_dev->rstc = NULL;
+
+ rc_dev->dev = dev;
+ platform_set_drvdata(pdev, rc_dev);
+ st_rc_hardware_init(rc_dev);
+
+ rdev->driver_type = RC_DRIVER_IR_RAW;
+ rc_set_allowed_protocols(rdev, RC_BIT_ALL);
+ /* rx sampling rate is 10Mhz */
+ rdev->rx_resolution = 100;
+ rdev->timeout = US_TO_NS(MAX_SYMB_TIME);
+ rdev->priv = rc_dev;
+ rdev->open = st_rc_open;
+ rdev->close = st_rc_close;
+ rdev->driver_name = IR_ST_NAME;
+ rdev->map_name = RC_MAP_LIRC;
+ rdev->input_name = "ST Remote Control Receiver";
+
+ /* enable wake via this device */
+ device_set_wakeup_capable(dev, true);
+ device_set_wakeup_enable(dev, true);
+
+ ret = rc_register_device(rdev);
+ if (ret < 0)
+ goto clkerr;
+
+ rc_dev->rdev = rdev;
+ if (devm_request_irq(dev, rc_dev->irq, st_rc_rx_interrupt,
+ IRQF_NO_SUSPEND, IR_ST_NAME, rc_dev) < 0) {
+ dev_err(dev, "IRQ %d register failed\n", rc_dev->irq);
+ ret = -EINVAL;
+ goto rcerr;
+ }
+
+ /**
+ * for LIRC_MODE_MODE2 or LIRC_MODE_PULSE or LIRC_MODE_RAW
+ * lircd expects a long space first before a signal train to sync.
+ */
+ st_rc_send_lirc_timeout(rdev);
+
+ dev_info(dev, "setup in %s mode\n", rc_dev->rxuhfmode ? "UHF" : "IR");
+
+ return ret;
+rcerr:
+ rc_unregister_device(rdev);
+ rdev = NULL;
+clkerr:
+ clk_disable_unprepare(rc_dev->sys_clock);
+err:
+ rc_free_device(rdev);
+ dev_err(dev, "Unable to register device (%d)\n", ret);
+ return ret;
+}
+
+#ifdef CONFIG_PM
+static int st_rc_suspend(struct device *dev)
+{
+ struct st_rc_device *rc_dev = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev)) {
+ if (!enable_irq_wake(rc_dev->irq))
+ rc_dev->irq_wake = 1;
+ else
+ return -EINVAL;
+ } else {
+ pinctrl_pm_select_sleep_state(dev);
+ writel(0x00, rc_dev->rx_base + IRB_RX_EN);
+ writel(0x00, rc_dev->rx_base + IRB_RX_INT_EN);
+ clk_disable_unprepare(rc_dev->sys_clock);
+ if (rc_dev->rstc)
+ reset_control_assert(rc_dev->rstc);
+ }
+
+ return 0;
+}
+
+static int st_rc_resume(struct device *dev)
+{
+ struct st_rc_device *rc_dev = dev_get_drvdata(dev);
+ struct rc_dev *rdev = rc_dev->rdev;
+
+ if (rc_dev->irq_wake) {
+ disable_irq_wake(rc_dev->irq);
+ rc_dev->irq_wake = 0;
+ } else {
+ pinctrl_pm_select_default_state(dev);
+ st_rc_hardware_init(rc_dev);
+ if (rdev->users) {
+ writel(IRB_RX_INTS, rc_dev->rx_base + IRB_RX_INT_EN);
+ writel(0x01, rc_dev->rx_base + IRB_RX_EN);
+ }
+ }
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(st_rc_pm_ops, st_rc_suspend, st_rc_resume);
+#endif
+
+#ifdef CONFIG_OF
+static struct of_device_id st_rc_match[] = {
+ { .compatible = "st,comms-irb", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, st_rc_match);
+#endif
+
+static struct platform_driver st_rc_driver = {
+ .driver = {
+ .name = IR_ST_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(st_rc_match),
+#ifdef CONFIG_PM
+ .pm = &st_rc_pm_ops,
+#endif
+ },
+ .probe = st_rc_probe,
+ .remove = st_rc_remove,
+};
+
+module_platform_driver(st_rc_driver);
+
+MODULE_DESCRIPTION("RC Transceiver driver for STMicroelectronics platforms");
+MODULE_AUTHOR("STMicroelectronics (R&D) Ltd");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/rc/streamzap.c b/drivers/media/rc/streamzap.c
index e435d94c077..bd5e4ff9e0b 100644
--- a/drivers/media/rc/streamzap.c
+++ b/drivers/media/rc/streamzap.c
@@ -42,12 +42,6 @@
#define DRIVER_NAME "streamzap"
#define DRIVER_DESC "Streamzap Remote Control driver"
-#ifdef CONFIG_USB_DEBUG
-static int debug = 1;
-#else
-static int debug;
-#endif
-
#define USB_STREAMZAP_VENDOR_ID 0x0e9c
#define USB_STREAMZAP_PRODUCT_ID 0x0000
@@ -322,7 +316,7 @@ static struct rc_dev *streamzap_init_rc_dev(struct streamzap_ir *sz)
rdev->dev.parent = dev;
rdev->priv = sz;
rdev->driver_type = RC_DRIVER_IR_RAW;
- rdev->allowed_protos = RC_TYPE_ALL;
+ rc_set_allowed_protocols(rdev, RC_BIT_ALL);
rdev->driver_name = DRIVER_NAME;
rdev->map_name = RC_MAP_STREAMZAP;
@@ -346,8 +340,8 @@ out:
* On any failure the return value is the ERROR
* On success return 0
*/
-static int __devinit streamzap_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
+static int streamzap_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
{
struct usb_device *usbdev = interface_to_usbdev(intf);
struct usb_host_interface *iface_host;
@@ -523,37 +517,8 @@ static int streamzap_resume(struct usb_interface *intf)
return 0;
}
-/**
- * streamzap_init
- */
-static int __init streamzap_init(void)
-{
- int ret;
-
- /* register this driver with the USB subsystem */
- ret = usb_register(&streamzap_driver);
- if (ret < 0)
- printk(KERN_ERR DRIVER_NAME ": usb register failed, "
- "result = %d\n", ret);
-
- return ret;
-}
-
-/**
- * streamzap_exit
- */
-static void __exit streamzap_exit(void)
-{
- usb_deregister(&streamzap_driver);
-}
-
-
-module_init(streamzap_init);
-module_exit(streamzap_exit);
+module_usb_driver(streamzap_driver);
MODULE_AUTHOR("Jarod Wilson <jarod@wilsonet.com>");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Enable debugging messages");
diff --git a/drivers/media/rc/ttusbir.c b/drivers/media/rc/ttusbir.c
new file mode 100644
index 00000000000..c5be38e2a2f
--- /dev/null
+++ b/drivers/media/rc/ttusbir.c
@@ -0,0 +1,449 @@
+/*
+ * TechnoTrend USB IR Receiver
+ *
+ * Copyright (C) 2012 Sean Young <sean@mess.org>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <linux/slab.h>
+#include <linux/leds.h>
+#include <media/rc-core.h>
+
+#define DRIVER_NAME "ttusbir"
+#define DRIVER_DESC "TechnoTrend USB IR Receiver"
+/*
+ * The Windows driver uses 8 URBS, the original lirc drivers has a
+ * configurable amount (2 default, 4 max). This device generates about 125
+ * messages per second (!), whether IR is idle or not.
+ */
+#define NUM_URBS 4
+#define NS_PER_BYTE 62500
+#define NS_PER_BIT (NS_PER_BYTE/8)
+
+struct ttusbir {
+ struct rc_dev *rc;
+ struct device *dev;
+ struct usb_device *udev;
+
+ struct urb *urb[NUM_URBS];
+
+ struct led_classdev led;
+ struct urb *bulk_urb;
+ uint8_t bulk_buffer[5];
+ int bulk_out_endp, iso_in_endp;
+ bool led_on, is_led_on;
+ atomic_t led_complete;
+
+ char phys[64];
+};
+
+static enum led_brightness ttusbir_brightness_get(struct led_classdev *led_dev)
+{
+ struct ttusbir *tt = container_of(led_dev, struct ttusbir, led);
+
+ return tt->led_on ? LED_FULL : LED_OFF;
+}
+
+static void ttusbir_set_led(struct ttusbir *tt)
+{
+ int ret;
+
+ smp_mb();
+
+ if (tt->led_on != tt->is_led_on && tt->udev &&
+ atomic_add_unless(&tt->led_complete, 1, 1)) {
+ tt->bulk_buffer[4] = tt->is_led_on = tt->led_on;
+ ret = usb_submit_urb(tt->bulk_urb, GFP_ATOMIC);
+ if (ret) {
+ dev_warn(tt->dev, "failed to submit bulk urb: %d\n",
+ ret);
+ atomic_dec(&tt->led_complete);
+ }
+ }
+}
+
+static void ttusbir_brightness_set(struct led_classdev *led_dev, enum
+ led_brightness brightness)
+{
+ struct ttusbir *tt = container_of(led_dev, struct ttusbir, led);
+
+ tt->led_on = brightness != LED_OFF;
+
+ ttusbir_set_led(tt);
+}
+
+/*
+ * The urb cannot be reused until the urb completes
+ */
+static void ttusbir_bulk_complete(struct urb *urb)
+{
+ struct ttusbir *tt = urb->context;
+
+ atomic_dec(&tt->led_complete);
+
+ switch (urb->status) {
+ case 0:
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ usb_unlink_urb(urb);
+ return;
+ case -EPIPE:
+ default:
+ dev_dbg(tt->dev, "Error: urb status = %d\n", urb->status);
+ break;
+ }
+
+ ttusbir_set_led(tt);
+}
+
+/*
+ * The data is one bit per sample, a set bit signifying silence and samples
+ * being MSB first. Bit 0 can contain garbage so take it to be whatever
+ * bit 1 is, so we don't have unexpected edges.
+ */
+static void ttusbir_process_ir_data(struct ttusbir *tt, uint8_t *buf)
+{
+ struct ir_raw_event rawir;
+ unsigned i, v, b;
+ bool event = false;
+
+ init_ir_raw_event(&rawir);
+
+ for (i = 0; i < 128; i++) {
+ v = buf[i] & 0xfe;
+ switch (v) {
+ case 0xfe:
+ rawir.pulse = false;
+ rawir.duration = NS_PER_BYTE;
+ if (ir_raw_event_store_with_filter(tt->rc, &rawir))
+ event = true;
+ break;
+ case 0:
+ rawir.pulse = true;
+ rawir.duration = NS_PER_BYTE;
+ if (ir_raw_event_store_with_filter(tt->rc, &rawir))
+ event = true;
+ break;
+ default:
+ /* one edge per byte */
+ if (v & 2) {
+ b = ffz(v | 1);
+ rawir.pulse = true;
+ } else {
+ b = ffs(v) - 1;
+ rawir.pulse = false;
+ }
+
+ rawir.duration = NS_PER_BIT * (8 - b);
+ if (ir_raw_event_store_with_filter(tt->rc, &rawir))
+ event = true;
+
+ rawir.pulse = !rawir.pulse;
+ rawir.duration = NS_PER_BIT * b;
+ if (ir_raw_event_store_with_filter(tt->rc, &rawir))
+ event = true;
+ break;
+ }
+ }
+
+ /* don't wakeup when there's nothing to do */
+ if (event)
+ ir_raw_event_handle(tt->rc);
+}
+
+static void ttusbir_urb_complete(struct urb *urb)
+{
+ struct ttusbir *tt = urb->context;
+ int rc;
+
+ switch (urb->status) {
+ case 0:
+ ttusbir_process_ir_data(tt, urb->transfer_buffer);
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ usb_unlink_urb(urb);
+ return;
+ case -EPIPE:
+ default:
+ dev_dbg(tt->dev, "Error: urb status = %d\n", urb->status);
+ break;
+ }
+
+ rc = usb_submit_urb(urb, GFP_ATOMIC);
+ if (rc && rc != -ENODEV)
+ dev_warn(tt->dev, "failed to resubmit urb: %d\n", rc);
+}
+
+static int ttusbir_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct ttusbir *tt;
+ struct usb_interface_descriptor *idesc;
+ struct usb_endpoint_descriptor *desc;
+ struct rc_dev *rc;
+ int i, j, ret;
+ int altsetting = -1;
+
+ tt = kzalloc(sizeof(*tt), GFP_KERNEL);
+ rc = rc_allocate_device();
+ if (!tt || !rc) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* find the correct alt setting */
+ for (i = 0; i < intf->num_altsetting && altsetting == -1; i++) {
+ int max_packet, bulk_out_endp = -1, iso_in_endp = -1;
+
+ idesc = &intf->altsetting[i].desc;
+
+ for (j = 0; j < idesc->bNumEndpoints; j++) {
+ desc = &intf->altsetting[i].endpoint[j].desc;
+ max_packet = le16_to_cpu(desc->wMaxPacketSize);
+ if (usb_endpoint_dir_in(desc) &&
+ usb_endpoint_xfer_isoc(desc) &&
+ max_packet == 0x10)
+ iso_in_endp = j;
+ else if (usb_endpoint_dir_out(desc) &&
+ usb_endpoint_xfer_bulk(desc) &&
+ max_packet == 0x20)
+ bulk_out_endp = j;
+
+ if (bulk_out_endp != -1 && iso_in_endp != -1) {
+ tt->bulk_out_endp = bulk_out_endp;
+ tt->iso_in_endp = iso_in_endp;
+ altsetting = i;
+ break;
+ }
+ }
+ }
+
+ if (altsetting == -1) {
+ dev_err(&intf->dev, "cannot find expected altsetting\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ tt->dev = &intf->dev;
+ tt->udev = interface_to_usbdev(intf);
+ tt->rc = rc;
+
+ ret = usb_set_interface(tt->udev, 0, altsetting);
+ if (ret)
+ goto out;
+
+ for (i = 0; i < NUM_URBS; i++) {
+ struct urb *urb = usb_alloc_urb(8, GFP_KERNEL);
+ void *buffer;
+
+ if (!urb) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ urb->dev = tt->udev;
+ urb->context = tt;
+ urb->pipe = usb_rcvisocpipe(tt->udev, tt->iso_in_endp);
+ urb->interval = 1;
+ buffer = usb_alloc_coherent(tt->udev, 128, GFP_KERNEL,
+ &urb->transfer_dma);
+ if (!buffer) {
+ usb_free_urb(urb);
+ ret = -ENOMEM;
+ goto out;
+ }
+ urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP | URB_ISO_ASAP;
+ urb->transfer_buffer = buffer;
+ urb->complete = ttusbir_urb_complete;
+ urb->number_of_packets = 8;
+ urb->transfer_buffer_length = 128;
+
+ for (j = 0; j < 8; j++) {
+ urb->iso_frame_desc[j].offset = j * 16;
+ urb->iso_frame_desc[j].length = 16;
+ }
+
+ tt->urb[i] = urb;
+ }
+
+ tt->bulk_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!tt->bulk_urb) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ tt->bulk_buffer[0] = 0xaa;
+ tt->bulk_buffer[1] = 0x01;
+ tt->bulk_buffer[2] = 0x05;
+ tt->bulk_buffer[3] = 0x01;
+
+ usb_fill_bulk_urb(tt->bulk_urb, tt->udev, usb_sndbulkpipe(tt->udev,
+ tt->bulk_out_endp), tt->bulk_buffer, sizeof(tt->bulk_buffer),
+ ttusbir_bulk_complete, tt);
+
+ tt->led.name = "ttusbir:green:power";
+ tt->led.default_trigger = "rc-feedback";
+ tt->led.brightness_set = ttusbir_brightness_set;
+ tt->led.brightness_get = ttusbir_brightness_get;
+ tt->is_led_on = tt->led_on = true;
+ atomic_set(&tt->led_complete, 0);
+ ret = led_classdev_register(&intf->dev, &tt->led);
+ if (ret)
+ goto out;
+
+ usb_make_path(tt->udev, tt->phys, sizeof(tt->phys));
+
+ rc->input_name = DRIVER_DESC;
+ rc->input_phys = tt->phys;
+ usb_to_input_id(tt->udev, &rc->input_id);
+ rc->dev.parent = &intf->dev;
+ rc->driver_type = RC_DRIVER_IR_RAW;
+ rc_set_allowed_protocols(rc, RC_BIT_ALL);
+ rc->priv = tt;
+ rc->driver_name = DRIVER_NAME;
+ rc->map_name = RC_MAP_TT_1500;
+ rc->timeout = MS_TO_NS(100);
+ /*
+ * The precision is NS_PER_BIT, but since every 8th bit can be
+ * overwritten with garbage the accuracy is at best 2 * NS_PER_BIT.
+ */
+ rc->rx_resolution = NS_PER_BIT;
+
+ ret = rc_register_device(rc);
+ if (ret) {
+ dev_err(&intf->dev, "failed to register rc device %d\n", ret);
+ goto out2;
+ }
+
+ usb_set_intfdata(intf, tt);
+
+ for (i = 0; i < NUM_URBS; i++) {
+ ret = usb_submit_urb(tt->urb[i], GFP_KERNEL);
+ if (ret) {
+ dev_err(tt->dev, "failed to submit urb %d\n", ret);
+ goto out3;
+ }
+ }
+
+ return 0;
+out3:
+ rc_unregister_device(rc);
+ rc = NULL;
+out2:
+ led_classdev_unregister(&tt->led);
+out:
+ if (tt) {
+ for (i = 0; i < NUM_URBS && tt->urb[i]; i++) {
+ struct urb *urb = tt->urb[i];
+
+ usb_kill_urb(urb);
+ usb_free_coherent(tt->udev, 128, urb->transfer_buffer,
+ urb->transfer_dma);
+ usb_free_urb(urb);
+ }
+ usb_kill_urb(tt->bulk_urb);
+ usb_free_urb(tt->bulk_urb);
+ kfree(tt);
+ }
+ rc_free_device(rc);
+
+ return ret;
+}
+
+static void ttusbir_disconnect(struct usb_interface *intf)
+{
+ struct ttusbir *tt = usb_get_intfdata(intf);
+ struct usb_device *udev = tt->udev;
+ int i;
+
+ tt->udev = NULL;
+
+ rc_unregister_device(tt->rc);
+ led_classdev_unregister(&tt->led);
+ for (i = 0; i < NUM_URBS; i++) {
+ usb_kill_urb(tt->urb[i]);
+ usb_free_coherent(udev, 128, tt->urb[i]->transfer_buffer,
+ tt->urb[i]->transfer_dma);
+ usb_free_urb(tt->urb[i]);
+ }
+ usb_kill_urb(tt->bulk_urb);
+ usb_free_urb(tt->bulk_urb);
+ usb_set_intfdata(intf, NULL);
+ kfree(tt);
+}
+
+static int ttusbir_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct ttusbir *tt = usb_get_intfdata(intf);
+ int i;
+
+ for (i = 0; i < NUM_URBS; i++)
+ usb_kill_urb(tt->urb[i]);
+
+ led_classdev_suspend(&tt->led);
+ usb_kill_urb(tt->bulk_urb);
+
+ return 0;
+}
+
+static int ttusbir_resume(struct usb_interface *intf)
+{
+ struct ttusbir *tt = usb_get_intfdata(intf);
+ int i, rc;
+
+ tt->is_led_on = true;
+ led_classdev_resume(&tt->led);
+
+ for (i = 0; i < NUM_URBS; i++) {
+ rc = usb_submit_urb(tt->urb[i], GFP_KERNEL);
+ if (rc) {
+ dev_warn(tt->dev, "failed to submit urb: %d\n", rc);
+ break;
+ }
+ }
+
+ return rc;
+}
+
+static const struct usb_device_id ttusbir_table[] = {
+ { USB_DEVICE(0x0b48, 0x2003) },
+ { }
+};
+
+static struct usb_driver ttusbir_driver = {
+ .name = DRIVER_NAME,
+ .id_table = ttusbir_table,
+ .probe = ttusbir_probe,
+ .suspend = ttusbir_suspend,
+ .resume = ttusbir_resume,
+ .reset_resume = ttusbir_resume,
+ .disconnect = ttusbir_disconnect,
+};
+
+module_usb_driver(ttusbir_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Sean Young <sean@mess.org>");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, ttusbir_table);
+
diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c
index 186de552200..a8b981f5ce2 100644
--- a/drivers/media/rc/winbond-cir.c
+++ b/drivers/media/rc/winbond-cir.c
@@ -6,8 +6,9 @@
* could probably support others (Winbond WEC102X, NatSemi, etc)
* with minor modifications.
*
- * Original Author: David Härdeman <david@hardeman.nu>
- * Copyright (C) 2009 - 2010 David Härdeman <david@hardeman.nu>
+ * Original Author: David Härdeman <david@hardeman.nu>
+ * Copyright (C) 2012 Sean Young <sean@mess.org>
+ * Copyright (C) 2009 - 2011 David Härdeman <david@hardeman.nu>
*
* Dedicated to my daughter Matilda, without whose loving attention this
* driver would have been finished in half the time and with a fraction
@@ -19,11 +20,10 @@
* o DSDT dumps
*
* Supported features:
- * o Wake-On-CIR functionality
- *
- * To do:
- * o Learning
+ * o IR Receive
* o IR Transmit
+ * o Wake-On-CIR functionality
+ * o Carrier detection
*
* 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
@@ -40,6 +40,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/pnp.h>
#include <linux/interrupt.h>
@@ -50,6 +52,8 @@
#include <linux/io.h>
#include <linux/bitrev.h>
#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
#include <media/rc-core.h>
#define DRVNAME "winbond-cir"
@@ -118,14 +122,24 @@
#define WBCIR_IRQ_NONE 0x00
/* RX data bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */
#define WBCIR_IRQ_RX 0x01
+/* TX data low bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */
+#define WBCIR_IRQ_TX_LOW 0x02
/* Over/Under-flow bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */
#define WBCIR_IRQ_ERR 0x04
+/* TX data empty bit for WBCEIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */
+#define WBCIR_IRQ_TX_EMPTY 0x20
/* Led enable/disable bit for WBCIR_REG_ECEIR_CTS */
#define WBCIR_LED_ENABLE 0x80
/* RX data available bit for WBCIR_REG_SP3_LSR */
#define WBCIR_RX_AVAIL 0x01
+/* RX data overrun error bit for WBCIR_REG_SP3_LSR */
+#define WBCIR_RX_OVERRUN 0x02
+/* TX End-Of-Transmission bit for WBCIR_REG_SP3_ASCR */
+#define WBCIR_TX_EOT 0x04
/* RX disable bit for WBCIR_REG_SP3_ASCR */
#define WBCIR_RX_DISABLE 0x20
+/* TX data underrun error bit for WBCIR_REG_SP3_ASCR */
+#define WBCIR_TX_UNDERRUN 0x40
/* Extended mode enable bit for WBCIR_REG_SP3_EXCR1 */
#define WBCIR_EXT_ENABLE 0x01
/* Select compare register in WBCIR_REG_WCEIR_INDEX (bits 5 & 6) */
@@ -134,6 +148,14 @@
#define WBCIR_REGSEL_MASK 0x20
/* Starting address of selected register in WBCIR_REG_WCEIR_INDEX */
#define WBCIR_REG_ADDR0 0x00
+/* Enable carrier counter */
+#define WBCIR_CNTR_EN 0x01
+/* Reset carrier counter */
+#define WBCIR_CNTR_R 0x02
+/* Invert TX */
+#define WBCIR_IRTX_INV 0x04
+/* Receiver oversampling */
+#define WBCIR_RX_T_OV 0x40
/* Valid banks for the SP3 UART */
enum wbcir_bank {
@@ -154,6 +176,20 @@ enum wbcir_protocol {
IR_PROTOCOL_RC6 = 0x2,
};
+/* Possible states for IR reception */
+enum wbcir_rxstate {
+ WBCIR_RXSTATE_INACTIVE = 0,
+ WBCIR_RXSTATE_ACTIVE,
+ WBCIR_RXSTATE_ERROR
+};
+
+/* Possible states for IR transmission */
+enum wbcir_txstate {
+ WBCIR_TXSTATE_INACTIVE = 0,
+ WBCIR_TXSTATE_ACTIVE,
+ WBCIR_TXSTATE_ERROR
+};
+
/* Misc */
#define WBCIR_NAME "Winbond CIR"
#define WBCIR_ID_FAMILY 0xF1 /* Family ID for the WPCD376I */
@@ -166,22 +202,27 @@ enum wbcir_protocol {
/* Per-device data */
struct wbcir_data {
spinlock_t spinlock;
+ struct rc_dev *dev;
+ struct led_classdev led;
unsigned long wbase; /* Wake-Up Baseaddr */
unsigned long ebase; /* Enhanced Func. Baseaddr */
unsigned long sbase; /* Serial Port Baseaddr */
unsigned int irq; /* Serial Port IRQ */
-
- struct rc_dev *dev;
-
- struct led_trigger *rxtrigger;
- struct led_trigger *txtrigger;
- struct led_classdev led;
-
- /* RX irdata state */
- bool irdata_active;
- bool irdata_error;
- struct ir_raw_event ev;
+ u8 irqmask;
+
+ /* RX state */
+ enum wbcir_rxstate rxstate;
+ int carrier_report_enabled;
+ u32 pulse_duration;
+
+ /* TX state */
+ enum wbcir_txstate txstate;
+ u32 txlen;
+ u32 txoff;
+ u32 *txbuf;
+ u8 txmask;
+ u32 txcarrier;
};
static enum wbcir_protocol protocol = IR_PROTOCOL_RC6;
@@ -189,10 +230,14 @@ module_param(protocol, uint, 0444);
MODULE_PARM_DESC(protocol, "IR protocol to use for the power-on command "
"(0 = RC5, 1 = NEC, 2 = RC6A, default)");
-static int invert; /* default = 0 */
+static bool invert; /* default = 0 */
module_param(invert, bool, 0444);
MODULE_PARM_DESC(invert, "Invert the signal from the IR receiver");
+static bool txandrx; /* default = 0 */
+module_param(txandrx, bool, 0444);
+MODULE_PARM_DESC(txandrx, "Allow simultaneous TX and RX");
+
static unsigned int wake_sc = 0x800F040C;
module_param(wake_sc, uint, 0644);
MODULE_PARM_DESC(wake_sc, "Scancode of the power-on IR command");
@@ -228,6 +273,17 @@ wbcir_select_bank(struct wbcir_data *data, enum wbcir_bank bank)
outb(bank, data->sbase + WBCIR_REG_SP3_BSR);
}
+static inline void
+wbcir_set_irqmask(struct wbcir_data *data, u8 irqmask)
+{
+ if (data->irqmask == irqmask)
+ return;
+
+ wbcir_select_bank(data, WBCIR_BANK_0);
+ outb(irqmask, data->sbase + WBCIR_REG_SP3_IER);
+ data->irqmask = irqmask;
+}
+
static enum led_brightness
wbcir_led_brightness_get(struct led_classdev *led_cdev)
{
@@ -279,97 +335,353 @@ wbcir_to_rc6cells(u8 val)
*
*****************************************************************************/
+static void
+wbcir_carrier_report(struct wbcir_data *data)
+{
+ unsigned counter = inb(data->ebase + WBCIR_REG_ECEIR_CNT_LO) |
+ inb(data->ebase + WBCIR_REG_ECEIR_CNT_HI) << 8;
+
+ if (counter > 0 && counter < 0xffff) {
+ DEFINE_IR_RAW_EVENT(ev);
+
+ ev.carrier_report = 1;
+ ev.carrier = DIV_ROUND_CLOSEST(counter * 1000000u,
+ data->pulse_duration);
+
+ ir_raw_event_store(data->dev, &ev);
+ }
+
+ /* reset and restart the counter */
+ data->pulse_duration = 0;
+ wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CCTL, WBCIR_CNTR_R,
+ WBCIR_CNTR_EN | WBCIR_CNTR_R);
+ wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CCTL, WBCIR_CNTR_EN,
+ WBCIR_CNTR_EN | WBCIR_CNTR_R);
+}
+
+static void
+wbcir_idle_rx(struct rc_dev *dev, bool idle)
+{
+ struct wbcir_data *data = dev->priv;
+
+ if (!idle && data->rxstate == WBCIR_RXSTATE_INACTIVE)
+ data->rxstate = WBCIR_RXSTATE_ACTIVE;
+
+ if (idle && data->rxstate != WBCIR_RXSTATE_INACTIVE) {
+ data->rxstate = WBCIR_RXSTATE_INACTIVE;
+
+ if (data->carrier_report_enabled)
+ wbcir_carrier_report(data);
+
+ /* Tell hardware to go idle by setting RXINACTIVE */
+ outb(WBCIR_RX_DISABLE, data->sbase + WBCIR_REG_SP3_ASCR);
+ }
+}
+
+static void
+wbcir_irq_rx(struct wbcir_data *data, struct pnp_dev *device)
+{
+ u8 irdata;
+ DEFINE_IR_RAW_EVENT(rawir);
+ unsigned duration;
+
+ /* Since RXHDLEV is set, at least 8 bytes are in the FIFO */
+ while (inb(data->sbase + WBCIR_REG_SP3_LSR) & WBCIR_RX_AVAIL) {
+ irdata = inb(data->sbase + WBCIR_REG_SP3_RXDATA);
+ if (data->rxstate == WBCIR_RXSTATE_ERROR)
+ continue;
+
+ duration = ((irdata & 0x7F) + 1) *
+ (data->carrier_report_enabled ? 2 : 10);
+ rawir.pulse = irdata & 0x80 ? false : true;
+ rawir.duration = US_TO_NS(duration);
+
+ if (rawir.pulse)
+ data->pulse_duration += duration;
+
+ ir_raw_event_store_with_filter(data->dev, &rawir);
+ }
+
+ ir_raw_event_handle(data->dev);
+}
+
+static void
+wbcir_irq_tx(struct wbcir_data *data)
+{
+ unsigned int space;
+ unsigned int used;
+ u8 bytes[16];
+ u8 byte;
+
+ if (!data->txbuf)
+ return;
+
+ switch (data->txstate) {
+ case WBCIR_TXSTATE_INACTIVE:
+ /* TX FIFO empty */
+ space = 16;
+ break;
+ case WBCIR_TXSTATE_ACTIVE:
+ /* TX FIFO low (3 bytes or less) */
+ space = 13;
+ break;
+ case WBCIR_TXSTATE_ERROR:
+ space = 0;
+ break;
+ default:
+ return;
+ }
+
+ /*
+ * TX data is run-length coded in bytes: YXXXXXXX
+ * Y = space (1) or pulse (0)
+ * X = duration, encoded as (X + 1) * 10us (i.e 10 to 1280 us)
+ */
+ for (used = 0; used < space && data->txoff != data->txlen; used++) {
+ if (data->txbuf[data->txoff] == 0) {
+ data->txoff++;
+ continue;
+ }
+ byte = min((u32)0x80, data->txbuf[data->txoff]);
+ data->txbuf[data->txoff] -= byte;
+ byte--;
+ byte |= (data->txoff % 2 ? 0x80 : 0x00); /* pulse/space */
+ bytes[used] = byte;
+ }
+
+ while (data->txbuf[data->txoff] == 0 && data->txoff != data->txlen)
+ data->txoff++;
+
+ if (used == 0) {
+ /* Finished */
+ if (data->txstate == WBCIR_TXSTATE_ERROR)
+ /* Clear TX underrun bit */
+ outb(WBCIR_TX_UNDERRUN, data->sbase + WBCIR_REG_SP3_ASCR);
+ wbcir_set_irqmask(data, WBCIR_IRQ_RX | WBCIR_IRQ_ERR);
+ kfree(data->txbuf);
+ data->txbuf = NULL;
+ data->txstate = WBCIR_TXSTATE_INACTIVE;
+ } else if (data->txoff == data->txlen) {
+ /* At the end of transmission, tell the hw before last byte */
+ outsb(data->sbase + WBCIR_REG_SP3_TXDATA, bytes, used - 1);
+ outb(WBCIR_TX_EOT, data->sbase + WBCIR_REG_SP3_ASCR);
+ outb(bytes[used - 1], data->sbase + WBCIR_REG_SP3_TXDATA);
+ wbcir_set_irqmask(data, WBCIR_IRQ_RX | WBCIR_IRQ_ERR |
+ WBCIR_IRQ_TX_EMPTY);
+ } else {
+ /* More data to follow... */
+ outsb(data->sbase + WBCIR_REG_SP3_RXDATA, bytes, used);
+ if (data->txstate == WBCIR_TXSTATE_INACTIVE) {
+ wbcir_set_irqmask(data, WBCIR_IRQ_RX | WBCIR_IRQ_ERR |
+ WBCIR_IRQ_TX_LOW);
+ data->txstate = WBCIR_TXSTATE_ACTIVE;
+ }
+ }
+}
+
static irqreturn_t
wbcir_irq_handler(int irqno, void *cookie)
{
struct pnp_dev *device = cookie;
struct wbcir_data *data = pnp_get_drvdata(device);
unsigned long flags;
- u8 irdata[8];
- u8 disable = true;
u8 status;
- int i;
spin_lock_irqsave(&data->spinlock, flags);
-
wbcir_select_bank(data, WBCIR_BANK_0);
-
status = inb(data->sbase + WBCIR_REG_SP3_EIR);
+ status &= data->irqmask;
- if (!(status & (WBCIR_IRQ_RX | WBCIR_IRQ_ERR))) {
+ if (!status) {
spin_unlock_irqrestore(&data->spinlock, flags);
return IRQ_NONE;
}
- /* Check for e.g. buffer overflow */
if (status & WBCIR_IRQ_ERR) {
- data->irdata_error = true;
- ir_raw_event_reset(data->dev);
+ /* RX overflow? (read clears bit) */
+ if (inb(data->sbase + WBCIR_REG_SP3_LSR) & WBCIR_RX_OVERRUN) {
+ data->rxstate = WBCIR_RXSTATE_ERROR;
+ ir_raw_event_reset(data->dev);
+ }
+
+ /* TX underflow? */
+ if (inb(data->sbase + WBCIR_REG_SP3_ASCR) & WBCIR_TX_UNDERRUN)
+ data->txstate = WBCIR_TXSTATE_ERROR;
}
- if (!(status & WBCIR_IRQ_RX))
- goto out;
+ if (status & WBCIR_IRQ_RX)
+ wbcir_irq_rx(data, device);
+
+ if (status & (WBCIR_IRQ_TX_LOW | WBCIR_IRQ_TX_EMPTY))
+ wbcir_irq_tx(data);
+
+ spin_unlock_irqrestore(&data->spinlock, flags);
+ return IRQ_HANDLED;
+}
+
+/*****************************************************************************
+ *
+ * RC-CORE INTERFACE FUNCTIONS
+ *
+ *****************************************************************************/
+
+static int
+wbcir_set_carrier_report(struct rc_dev *dev, int enable)
+{
+ struct wbcir_data *data = dev->priv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&data->spinlock, flags);
- if (!data->irdata_active) {
- data->irdata_active = true;
- led_trigger_event(data->rxtrigger, LED_FULL);
+ if (data->carrier_report_enabled == enable) {
+ spin_unlock_irqrestore(&data->spinlock, flags);
+ return 0;
}
- /* Since RXHDLEV is set, at least 8 bytes are in the FIFO */
- insb(data->sbase + WBCIR_REG_SP3_RXDATA, &irdata[0], 8);
+ data->pulse_duration = 0;
+ wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CCTL, WBCIR_CNTR_R,
+ WBCIR_CNTR_EN | WBCIR_CNTR_R);
- for (i = 0; i < 8; i++) {
- u8 pulse;
- u32 duration;
+ if (enable && data->dev->idle)
+ wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CCTL,
+ WBCIR_CNTR_EN, WBCIR_CNTR_EN | WBCIR_CNTR_R);
- if (irdata[i] != 0xFF && irdata[i] != 0x00)
- disable = false;
+ /* Set a higher sampling resolution if carrier reports are enabled */
+ wbcir_select_bank(data, WBCIR_BANK_2);
+ data->dev->rx_resolution = US_TO_NS(enable ? 2 : 10);
+ outb(enable ? 0x03 : 0x0f, data->sbase + WBCIR_REG_SP3_BGDL);
+ outb(0x00, data->sbase + WBCIR_REG_SP3_BGDH);
- if (data->irdata_error)
- continue;
+ /* Enable oversampling if carrier reports are enabled */
+ wbcir_select_bank(data, WBCIR_BANK_7);
+ wbcir_set_bits(data->sbase + WBCIR_REG_SP3_RCCFG,
+ enable ? WBCIR_RX_T_OV : 0, WBCIR_RX_T_OV);
- pulse = irdata[i] & 0x80 ? false : true;
- duration = (irdata[i] & 0x7F) * 10000; /* ns */
+ data->carrier_report_enabled = enable;
+ spin_unlock_irqrestore(&data->spinlock, flags);
- if (data->ev.pulse != pulse) {
- if (data->ev.duration != 0) {
- ir_raw_event_store(data->dev, &data->ev);
- data->ev.duration = 0;
- }
+ return 0;
+}
- data->ev.pulse = pulse;
- }
+static int
+wbcir_txcarrier(struct rc_dev *dev, u32 carrier)
+{
+ struct wbcir_data *data = dev->priv;
+ unsigned long flags;
+ u8 val;
+ u32 freq;
+
+ freq = DIV_ROUND_CLOSEST(carrier, 1000);
+ if (freq < 30 || freq > 60)
+ return -EINVAL;
+
+ switch (freq) {
+ case 58:
+ case 59:
+ case 60:
+ val = freq - 58;
+ freq *= 1000;
+ break;
+ case 57:
+ val = freq - 27;
+ freq = 56900;
+ break;
+ default:
+ val = freq - 27;
+ freq *= 1000;
+ break;
+ }
- data->ev.duration += duration;
+ spin_lock_irqsave(&data->spinlock, flags);
+ if (data->txstate != WBCIR_TXSTATE_INACTIVE) {
+ spin_unlock_irqrestore(&data->spinlock, flags);
+ return -EBUSY;
}
- if (disable) {
- if (data->ev.duration != 0 && !data->irdata_error) {
- ir_raw_event_store(data->dev, &data->ev);
- data->ev.duration = 0;
- }
+ if (data->txcarrier != freq) {
+ wbcir_select_bank(data, WBCIR_BANK_7);
+ wbcir_set_bits(data->sbase + WBCIR_REG_SP3_IRTXMC, val, 0x1F);
+ data->txcarrier = freq;
+ }
- /* Set RXINACTIVE */
- outb(WBCIR_RX_DISABLE, data->sbase + WBCIR_REG_SP3_ASCR);
+ spin_unlock_irqrestore(&data->spinlock, flags);
+ return 0;
+}
- /* Drain the FIFO */
- while (inb(data->sbase + WBCIR_REG_SP3_LSR) & WBCIR_RX_AVAIL)
- inb(data->sbase + WBCIR_REG_SP3_RXDATA);
+static int
+wbcir_txmask(struct rc_dev *dev, u32 mask)
+{
+ struct wbcir_data *data = dev->priv;
+ unsigned long flags;
+ u8 val;
- ir_raw_event_reset(data->dev);
- data->irdata_error = false;
- data->irdata_active = false;
- led_trigger_event(data->rxtrigger, LED_OFF);
+ /* Four outputs, only one output can be enabled at a time */
+ switch (mask) {
+ case 0x1:
+ val = 0x0;
+ break;
+ case 0x2:
+ val = 0x1;
+ break;
+ case 0x4:
+ val = 0x2;
+ break;
+ case 0x8:
+ val = 0x3;
+ break;
+ default:
+ return -EINVAL;
}
- ir_raw_event_handle(data->dev);
+ spin_lock_irqsave(&data->spinlock, flags);
+ if (data->txstate != WBCIR_TXSTATE_INACTIVE) {
+ spin_unlock_irqrestore(&data->spinlock, flags);
+ return -EBUSY;
+ }
+
+ if (data->txmask != mask) {
+ wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CTS, val, 0x0c);
+ data->txmask = mask;
+ }
-out:
spin_unlock_irqrestore(&data->spinlock, flags);
- return IRQ_HANDLED;
+ return 0;
}
+static int
+wbcir_tx(struct rc_dev *dev, unsigned *b, unsigned count)
+{
+ struct wbcir_data *data = dev->priv;
+ unsigned *buf;
+ unsigned i;
+ unsigned long flags;
+
+ buf = kmalloc(count * sizeof(*b), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ /* Convert values to multiples of 10us */
+ for (i = 0; i < count; i++)
+ buf[i] = DIV_ROUND_CLOSEST(b[i], 10);
+ /* Not sure if this is possible, but better safe than sorry */
+ spin_lock_irqsave(&data->spinlock, flags);
+ if (data->txstate != WBCIR_TXSTATE_INACTIVE) {
+ spin_unlock_irqrestore(&data->spinlock, flags);
+ kfree(buf);
+ return -EBUSY;
+ }
+
+ /* Fill the TX fifo once, the irq handler will do the rest */
+ data->txbuf = buf;
+ data->txlen = count;
+ data->txoff = 0;
+ wbcir_irq_tx(data);
+
+ /* We're done */
+ spin_unlock_irqrestore(&data->spinlock, flags);
+ return count;
+}
/*****************************************************************************
*
@@ -382,7 +694,7 @@ wbcir_shutdown(struct pnp_dev *device)
{
struct device *dev = &device->dev;
struct wbcir_data *data = pnp_get_drvdata(device);
- int do_wake = 1;
+ bool do_wake = true;
u8 match[11];
u8 mask[11];
u8 rc6_csl = 0;
@@ -392,14 +704,14 @@ wbcir_shutdown(struct pnp_dev *device)
memset(mask, 0, sizeof(mask));
if (wake_sc == INVALID_SCANCODE || !device_may_wakeup(dev)) {
- do_wake = 0;
+ do_wake = false;
goto finish;
}
switch (protocol) {
case IR_PROTOCOL_RC5:
if (wake_sc > 0xFFF) {
- do_wake = 0;
+ do_wake = false;
dev_err(dev, "RC5 - Invalid wake scancode\n");
break;
}
@@ -418,7 +730,7 @@ wbcir_shutdown(struct pnp_dev *device)
case IR_PROTOCOL_NEC:
if (wake_sc > 0xFFFFFF) {
- do_wake = 0;
+ do_wake = false;
dev_err(dev, "NEC - Invalid wake scancode\n");
break;
}
@@ -440,7 +752,7 @@ wbcir_shutdown(struct pnp_dev *device)
if (wake_rc6mode == 0) {
if (wake_sc > 0xFFFF) {
- do_wake = 0;
+ do_wake = false;
dev_err(dev, "RC6 - Invalid wake scancode\n");
break;
}
@@ -496,7 +808,7 @@ wbcir_shutdown(struct pnp_dev *device)
} else if (wake_sc <= 0x007FFFFF) {
rc6_csl = 60;
} else {
- do_wake = 0;
+ do_wake = false;
dev_err(dev, "RC6 - Invalid wake scancode\n");
break;
}
@@ -508,14 +820,14 @@ wbcir_shutdown(struct pnp_dev *device)
mask[i++] = 0x0F;
} else {
- do_wake = 0;
+ do_wake = false;
dev_err(dev, "RC6 - Invalid wake mode\n");
}
break;
default:
- do_wake = 0;
+ do_wake = false;
break;
}
@@ -551,26 +863,21 @@ finish:
wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x00, 0x01);
}
- /* Disable interrupts */
- wbcir_select_bank(data, WBCIR_BANK_0);
- outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER);
-
- /* Disable LED */
- data->irdata_active = false;
- led_trigger_event(data->rxtrigger, LED_OFF);
-
/*
* ACPI will set the HW disable bit for SP3 which means that the
* output signals are left in an undefined state which may cause
* spurious interrupts which we need to ignore until the hardware
* is reinitialized.
*/
+ wbcir_set_irqmask(data, WBCIR_IRQ_NONE);
disable_irq(data->irq);
}
static int
wbcir_suspend(struct pnp_dev *device, pm_message_t state)
{
+ struct wbcir_data *data = pnp_get_drvdata(device);
+ led_classdev_suspend(&data->led);
wbcir_shutdown(device);
return 0;
}
@@ -581,8 +888,7 @@ wbcir_init_hw(struct wbcir_data *data)
u8 tmp;
/* Disable interrupts */
- wbcir_select_bank(data, WBCIR_BANK_0);
- outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER);
+ wbcir_set_irqmask(data, WBCIR_IRQ_NONE);
/* Set PROT_SEL, RX_INV, Clear CEIR_EN (needed for the led) */
tmp = protocol << 4;
@@ -601,15 +907,16 @@ wbcir_init_hw(struct wbcir_data *data)
/* Set IRTX_INV */
if (invert)
- outb(0x04, data->ebase + WBCIR_REG_ECEIR_CCTL);
+ outb(WBCIR_IRTX_INV, data->ebase + WBCIR_REG_ECEIR_CCTL);
else
outb(0x00, data->ebase + WBCIR_REG_ECEIR_CCTL);
/*
- * Clear IR LED, set SP3 clock to 24Mhz
+ * Clear IR LED, set SP3 clock to 24Mhz, set TX mask to IRTX1,
* set SP3_IRRX_SW to binary 01, helpfully not documented
*/
outb(0x10, data->ebase + WBCIR_REG_ECEIR_CTS);
+ data->txmask = 0x1;
/* Enable extended mode */
wbcir_select_bank(data, WBCIR_BANK_2);
@@ -629,18 +936,8 @@ wbcir_init_hw(struct wbcir_data *data)
/* prescaler 1.0, tx/rx fifo lvl 16 */
outb(0x30, data->sbase + WBCIR_REG_SP3_EXCR2);
- /* Set baud divisor to generate one byte per bit/cell */
- switch (protocol) {
- case IR_PROTOCOL_RC5:
- outb(0xA7, data->sbase + WBCIR_REG_SP3_BGDL);
- break;
- case IR_PROTOCOL_RC6:
- outb(0x53, data->sbase + WBCIR_REG_SP3_BGDL);
- break;
- case IR_PROTOCOL_NEC:
- outb(0x69, data->sbase + WBCIR_REG_SP3_BGDL);
- break;
- }
+ /* Set baud divisor to sample every 10 us */
+ outb(0x0f, data->sbase + WBCIR_REG_SP3_BGDL);
outb(0x00, data->sbase + WBCIR_REG_SP3_BGDH);
/* Set CEIR mode */
@@ -649,26 +946,29 @@ wbcir_init_hw(struct wbcir_data *data)
inb(data->sbase + WBCIR_REG_SP3_LSR); /* Clear LSR */
inb(data->sbase + WBCIR_REG_SP3_MSR); /* Clear MSR */
- /* Disable RX demod, run-length encoding/decoding, set freq span */
+ /* Disable RX demod, enable run-length enc/dec, set freq span */
wbcir_select_bank(data, WBCIR_BANK_7);
- outb(0x10, data->sbase + WBCIR_REG_SP3_RCCFG);
+ outb(0x90, data->sbase + WBCIR_REG_SP3_RCCFG);
/* Disable timer */
wbcir_select_bank(data, WBCIR_BANK_4);
outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR1);
- /* Enable MSR interrupt, Clear AUX_IRX */
+ /* Disable MSR interrupt, clear AUX_IRX, mask RX during TX? */
wbcir_select_bank(data, WBCIR_BANK_5);
- outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR2);
+ outb(txandrx ? 0x03 : 0x02, data->sbase + WBCIR_REG_SP3_IRCR2);
/* Disable CRC */
wbcir_select_bank(data, WBCIR_BANK_6);
outb(0x20, data->sbase + WBCIR_REG_SP3_IRCR3);
- /* Set RX/TX (de)modulation freq, not really used */
+ /* Set RX demodulation freq, not really used */
wbcir_select_bank(data, WBCIR_BANK_7);
outb(0xF2, data->sbase + WBCIR_REG_SP3_IRRXDC);
+
+ /* Set TX modulation, 36kHz, 7us pulse width */
outb(0x69, data->sbase + WBCIR_REG_SP3_IRTXMC);
+ data->txcarrier = 36000;
/* Set invert and pin direction */
if (invert)
@@ -683,16 +983,20 @@ wbcir_init_hw(struct wbcir_data *data)
/* Clear AUX status bits */
outb(0xE0, data->sbase + WBCIR_REG_SP3_ASCR);
- /* Clear IR decoding state */
- data->irdata_active = false;
- led_trigger_event(data->rxtrigger, LED_OFF);
- data->irdata_error = false;
- data->ev.duration = 0;
+ /* Clear RX state */
+ data->rxstate = WBCIR_RXSTATE_INACTIVE;
ir_raw_event_reset(data->dev);
- ir_raw_event_handle(data->dev);
+ ir_raw_event_set_idle(data->dev, true);
+
+ /* Clear TX state */
+ if (data->txstate == WBCIR_TXSTATE_ACTIVE) {
+ kfree(data->txbuf);
+ data->txbuf = NULL;
+ data->txstate = WBCIR_TXSTATE_INACTIVE;
+ }
/* Enable interrupts */
- outb(WBCIR_IRQ_RX | WBCIR_IRQ_ERR, data->sbase + WBCIR_REG_SP3_IER);
+ wbcir_set_irqmask(data, WBCIR_IRQ_RX | WBCIR_IRQ_ERR);
}
static int
@@ -702,11 +1006,12 @@ wbcir_resume(struct pnp_dev *device)
wbcir_init_hw(data);
enable_irq(data->irq);
+ led_classdev_resume(&data->led);
return 0;
}
-static int __devinit
+static int
wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id)
{
struct device *dev = &device->dev;
@@ -745,11 +1050,49 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id)
"(w: 0x%lX, e: 0x%lX, s: 0x%lX, i: %u)\n",
data->wbase, data->ebase, data->sbase, data->irq);
+ data->led.name = "cir::activity";
+ data->led.default_trigger = "rc-feedback";
+ data->led.brightness_set = wbcir_led_brightness_set;
+ data->led.brightness_get = wbcir_led_brightness_get;
+ err = led_classdev_register(&device->dev, &data->led);
+ if (err)
+ goto exit_free_data;
+
+ data->dev = rc_allocate_device();
+ if (!data->dev) {
+ err = -ENOMEM;
+ goto exit_unregister_led;
+ }
+
+ data->dev->driver_type = RC_DRIVER_IR_RAW;
+ data->dev->driver_name = DRVNAME;
+ data->dev->input_name = WBCIR_NAME;
+ data->dev->input_phys = "wbcir/cir0";
+ data->dev->input_id.bustype = BUS_HOST;
+ data->dev->input_id.vendor = PCI_VENDOR_ID_WINBOND;
+ data->dev->input_id.product = WBCIR_ID_FAMILY;
+ data->dev->input_id.version = WBCIR_ID_CHIP;
+ data->dev->map_name = RC_MAP_RC6_MCE;
+ data->dev->s_idle = wbcir_idle_rx;
+ data->dev->s_carrier_report = wbcir_set_carrier_report;
+ data->dev->s_tx_mask = wbcir_txmask;
+ data->dev->s_tx_carrier = wbcir_txcarrier;
+ data->dev->tx_ir = wbcir_tx;
+ data->dev->priv = data;
+ data->dev->dev.parent = &device->dev;
+ data->dev->timeout = MS_TO_NS(100);
+ data->dev->rx_resolution = US_TO_NS(2);
+ rc_set_allowed_protocols(data->dev, RC_BIT_ALL);
+
+ err = rc_register_device(data->dev);
+ if (err)
+ goto exit_free_rc;
+
if (!request_region(data->wbase, WAKEUP_IOMEM_LEN, DRVNAME)) {
dev_err(dev, "Region 0x%lx-0x%lx already in use!\n",
data->wbase, data->wbase + WAKEUP_IOMEM_LEN - 1);
err = -EBUSY;
- goto exit_free_data;
+ goto exit_unregister_device;
}
if (!request_region(data->ebase, EHFUNC_IOMEM_LEN, DRVNAME)) {
@@ -767,75 +1110,32 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id)
}
err = request_irq(data->irq, wbcir_irq_handler,
- IRQF_DISABLED, DRVNAME, device);
+ 0, DRVNAME, device);
if (err) {
dev_err(dev, "Failed to claim IRQ %u\n", data->irq);
err = -EBUSY;
goto exit_release_sbase;
}
- led_trigger_register_simple("cir-tx", &data->txtrigger);
- if (!data->txtrigger) {
- err = -ENOMEM;
- goto exit_free_irq;
- }
-
- led_trigger_register_simple("cir-rx", &data->rxtrigger);
- if (!data->rxtrigger) {
- err = -ENOMEM;
- goto exit_unregister_txtrigger;
- }
-
- data->led.name = "cir::activity";
- data->led.default_trigger = "cir-rx";
- data->led.brightness_set = wbcir_led_brightness_set;
- data->led.brightness_get = wbcir_led_brightness_get;
- err = led_classdev_register(&device->dev, &data->led);
- if (err)
- goto exit_unregister_rxtrigger;
-
- data->dev = rc_allocate_device();
- if (!data->dev) {
- err = -ENOMEM;
- goto exit_unregister_led;
- }
-
- data->dev->driver_name = WBCIR_NAME;
- data->dev->input_name = WBCIR_NAME;
- data->dev->input_phys = "wbcir/cir0";
- data->dev->input_id.bustype = BUS_HOST;
- data->dev->input_id.vendor = PCI_VENDOR_ID_WINBOND;
- data->dev->input_id.product = WBCIR_ID_FAMILY;
- data->dev->input_id.version = WBCIR_ID_CHIP;
- data->dev->priv = data;
- data->dev->dev.parent = &device->dev;
-
- err = rc_register_device(data->dev);
- if (err)
- goto exit_free_rc;
-
device_init_wakeup(&device->dev, 1);
wbcir_init_hw(data);
return 0;
-exit_free_rc:
- rc_free_device(data->dev);
-exit_unregister_led:
- led_classdev_unregister(&data->led);
-exit_unregister_rxtrigger:
- led_trigger_unregister_simple(data->rxtrigger);
-exit_unregister_txtrigger:
- led_trigger_unregister_simple(data->txtrigger);
-exit_free_irq:
- free_irq(data->irq, device);
exit_release_sbase:
release_region(data->sbase, SP_IOMEM_LEN);
exit_release_ebase:
release_region(data->ebase, EHFUNC_IOMEM_LEN);
exit_release_wbase:
release_region(data->wbase, WAKEUP_IOMEM_LEN);
+exit_unregister_device:
+ rc_unregister_device(data->dev);
+ data->dev = NULL;
+exit_free_rc:
+ rc_free_device(data->dev);
+exit_unregister_led:
+ led_classdev_unregister(&data->led);
exit_free_data:
kfree(data);
pnp_set_drvdata(device, NULL);
@@ -843,15 +1143,13 @@ exit:
return err;
}
-static void __devexit
+static void
wbcir_remove(struct pnp_dev *device)
{
struct wbcir_data *data = pnp_get_drvdata(device);
/* Disable interrupts */
- wbcir_select_bank(data, WBCIR_BANK_0);
- outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER);
-
+ wbcir_set_irqmask(data, WBCIR_IRQ_NONE);
free_irq(data->irq, device);
/* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */
@@ -865,8 +1163,6 @@ wbcir_remove(struct pnp_dev *device)
rc_unregister_device(data->dev);
- led_trigger_unregister_simple(data->rxtrigger);
- led_trigger_unregister_simple(data->txtrigger);
led_classdev_unregister(&data->led);
/* This is ok since &data->led isn't actually used */
@@ -891,7 +1187,7 @@ static struct pnp_driver wbcir_driver = {
.name = WBCIR_NAME,
.id_table = wbcir_ids,
.probe = wbcir_probe,
- .remove = __devexit_p(wbcir_remove),
+ .remove = wbcir_remove,
.suspend = wbcir_suspend,
.resume = wbcir_resume,
.shutdown = wbcir_shutdown
@@ -908,12 +1204,12 @@ wbcir_init(void)
case IR_PROTOCOL_RC6:
break;
default:
- printk(KERN_ERR DRVNAME ": Invalid power-on protocol\n");
+ pr_err("Invalid power-on protocol\n");
}
ret = pnp_register_driver(&wbcir_driver);
if (ret)
- printk(KERN_ERR DRVNAME ": Unable to register driver\n");
+ pr_err("Unable to register driver\n");
return ret;
}
@@ -927,6 +1223,6 @@ wbcir_exit(void)
module_init(wbcir_init);
module_exit(wbcir_exit);
-MODULE_AUTHOR("David Härdeman <david@hardeman.nu>");
+MODULE_AUTHOR("David Härdeman <david@hardeman.nu>");
MODULE_DESCRIPTION("Winbond SuperI/O Consumer IR Driver");
MODULE_LICENSE("GPL");