linux-media.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4 00/10] HDMI CEC framework
@ 2015-04-23 13:03 Kamil Debski
  2015-04-23 13:03 ` [PATCH v4 01/10] dts: exynos4*: add HDMI CEC pin definition to pinctrl Kamil Debski
                   ` (8 more replies)
  0 siblings, 9 replies; 27+ messages in thread
From: Kamil Debski @ 2015-04-23 13:03 UTC (permalink / raw)
  To: dri-devel, linux-media
  Cc: m.szyprowski, k.debski, mchehab, hverkuil, kyungmin.park, thomas,
	sean, dmitry.torokhov, linux-input, linux-samsung-soc

Hi,

This is the fourth version of the HDMI CEC framework. I would like to thank
for all the comments and suggestions to the previous versions of this patch.
I believe that the code has matured enough to be tagged as PATCH and not RFC
as in previous version.

This patchset is base on the linux-next tree. I believe that there will be
some comments to it and there will be some things to fix, hence I am sending
this version now. The next version with appropriate fixes will be based on the
next RC (which I guess will be released soon).

The promiscuous mode included in the previous version caused some discussion and
I decided to drop it. In my opinion it can be useful for debugging, but on the
other hand I believe it can be easily added at a later time, if appropriate.

Best wishes,
Kamil Debski

Changes since v3
================
- remove the promiscuous mode
- rewrite the devicetree patches
- fixes, expansion and partial rewrite of the documentation
- reorder of API structures and addition of reserved fields
- use own struct to report time (32/64 bit safe)
- fix of handling events
- add cec.h to include/uapi/linux/Kbuild
- fixes in the adv76xx driver (add missing methods, change adv7604 to adv76xx)
- cleanup of debug messages in s5p-cec driver
- remove non necessary claiming of a gpio in the s5p-cec driver
- cleanup headers of the s5p-cec driver

Changes since v2
===============-
- added promiscuous mode
- added new key codes to the input framework
- add vendor ID reporting
- add the possibility to clear assigned logical addresses
- cleanup of the rc cec map

Changes since v1
================
- documentation edited and moved to the Documentation folder
- added key up/down message handling
- add missing CEC commands to the cec.h file

Original cover letter
=====================

Hi,

The work on a common CEC framework was started over three years ago by Hans
Verkuil. Unfortunately the work has stalled. As I have received the task of
creating a driver for the CEC interface module present on the Exynos range of
SoCs, I got in touch with Hans. He replied that the work stalled due to his
lack of time.

The driver was done in the most part and there were only minor fixes that needed
to be implemented. I would like to bring back the discussion on a common CEC
interface framework.

There are a few things that were still left as TODO, I think they might need
some discussion - for instance the way how the remote controls should be
handled.

Best wishes,
Kamil Debski

Original RFC by Hans Verkuil/Martin Bugge
=========================================
https://www.mail-archive.com/linux-media@vger.kernel.org/msg28735.html

Hans Verkuil (4):
  cec: add HDMI CEC framework
  v4l2-subdev: add HDMI CEC ops
  cec: adv7604: add cec support.
  cec: adv7511: add cec support.

Kamil Debski (6):
  dts: exynos4*: add HDMI CEC pin definition to pinctrl
  dts: exynos4: add node for the HDMI CEC device
  dts: exynos4412-odroid*: enable the HDMI CEC device
  HID: add HDMI CEC specific keycodes
  rc: Add HDMI CEC protoctol handling
  cec: s5p-cec: Add s5p-cec driver

 Documentation/cec.txt                              |  396 +++++++
 .../devicetree/bindings/media/s5p-cec.txt          |   33 +
 arch/arm/boot/dts/exynos4.dtsi                     |   12 +
 arch/arm/boot/dts/exynos4210-pinctrl.dtsi          |    7 +
 arch/arm/boot/dts/exynos4412-odroid-common.dtsi    |    4 +
 arch/arm/boot/dts/exynos4x12-pinctrl.dtsi          |    7 +
 drivers/media/Kconfig                              |    6 +
 drivers/media/Makefile                             |    2 +
 drivers/media/cec.c                                | 1161 ++++++++++++++++++++
 drivers/media/i2c/adv7511.c                        |  347 +++++-
 drivers/media/i2c/adv7604.c                        |  207 +++-
 drivers/media/platform/Kconfig                     |   10 +
 drivers/media/platform/Makefile                    |    1 +
 drivers/media/platform/s5p-cec/Makefile            |    4 +
 drivers/media/platform/s5p-cec/exynos_hdmi_cec.h   |   37 +
 .../media/platform/s5p-cec/exynos_hdmi_cecctrl.c   |  208 ++++
 drivers/media/platform/s5p-cec/regs-cec.h          |   96 ++
 drivers/media/platform/s5p-cec/s5p_cec.c           |  283 +++++
 drivers/media/platform/s5p-cec/s5p_cec.h           |   76 ++
 drivers/media/rc/keymaps/Makefile                  |    1 +
 drivers/media/rc/keymaps/rc-cec.c                  |  144 +++
 drivers/media/rc/rc-main.c                         |    1 +
 include/media/adv7511.h                            |    6 +-
 include/media/cec.h                                |  140 +++
 include/media/rc-core.h                            |    1 +
 include/media/rc-map.h                             |    5 +-
 include/media/v4l2-subdev.h                        |    8 +
 include/uapi/linux/Kbuild                          |    1 +
 include/uapi/linux/cec.h                           |  303 +++++
 include/uapi/linux/input.h                         |   12 +
 30 files changed, 3507 insertions(+), 12 deletions(-)
 create mode 100644 Documentation/cec.txt
 create mode 100644 Documentation/devicetree/bindings/media/s5p-cec.txt
 create mode 100644 drivers/media/cec.c
 create mode 100644 drivers/media/platform/s5p-cec/Makefile
 create mode 100644 drivers/media/platform/s5p-cec/exynos_hdmi_cec.h
 create mode 100644 drivers/media/platform/s5p-cec/exynos_hdmi_cecctrl.c
 create mode 100644 drivers/media/platform/s5p-cec/regs-cec.h
 create mode 100644 drivers/media/platform/s5p-cec/s5p_cec.c
 create mode 100644 drivers/media/platform/s5p-cec/s5p_cec.h
 create mode 100644 drivers/media/rc/keymaps/rc-cec.c
 create mode 100644 include/media/cec.h
 create mode 100644 include/uapi/linux/cec.h

-- 
1.7.9.5


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v4 01/10] dts: exynos4*: add HDMI CEC pin definition to pinctrl
  2015-04-23 13:03 [PATCH v4 00/10] HDMI CEC framework Kamil Debski
@ 2015-04-23 13:03 ` Kamil Debski
  2015-04-23 13:03 ` [PATCH v4 02/10] dts: exynos4: add node for the HDMI CEC device Kamil Debski
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 27+ messages in thread
From: Kamil Debski @ 2015-04-23 13:03 UTC (permalink / raw)
  To: dri-devel, linux-media
  Cc: m.szyprowski, k.debski, mchehab, hverkuil, kyungmin.park, thomas,
	sean, dmitry.torokhov, linux-input, linux-samsung-soc

Add pinctrl nodes for the HDMI CEC device to the Exynos4210 and
Exynos4x12 SoCs. These are required by the HDMI CEC device.

Signed-off-by: Kamil Debski <k.debski@samsung.com>
---
 arch/arm/boot/dts/exynos4210-pinctrl.dtsi |    7 +++++++
 arch/arm/boot/dts/exynos4x12-pinctrl.dtsi |    7 +++++++
 2 files changed, 14 insertions(+)

diff --git a/arch/arm/boot/dts/exynos4210-pinctrl.dtsi b/arch/arm/boot/dts/exynos4210-pinctrl.dtsi
index a7c2128..9331c62 100644
--- a/arch/arm/boot/dts/exynos4210-pinctrl.dtsi
+++ b/arch/arm/boot/dts/exynos4210-pinctrl.dtsi
@@ -820,6 +820,13 @@
 			samsung,pin-pud = <1>;
 			samsung,pin-drv = <0>;
 		};
+
+		hdmi_cec: hdmi-cec {
+			samsung,pins = "gpx3-6";
+			samsung,pin-function = <3>;
+			samsung,pin-pud = <0>;
+			samsung,pin-drv = <0>;
+		};
 	};
 
 	pinctrl@03860000 {
diff --git a/arch/arm/boot/dts/exynos4x12-pinctrl.dtsi b/arch/arm/boot/dts/exynos4x12-pinctrl.dtsi
index c141931..875464e 100644
--- a/arch/arm/boot/dts/exynos4x12-pinctrl.dtsi
+++ b/arch/arm/boot/dts/exynos4x12-pinctrl.dtsi
@@ -885,6 +885,13 @@
 			samsung,pin-pud = <0>;
 			samsung,pin-drv = <0>;
 		};
+
+		hdmi_cec: hdmi-cec {
+			samsung,pins = "gpx3-6";
+			samsung,pin-function = <3>;
+			samsung,pin-pud = <0>;
+			samsung,pin-drv = <0>;
+		};
 	};
 
 	pinctrl@03860000 {
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v4 02/10] dts: exynos4: add node for the HDMI CEC device
  2015-04-23 13:03 [PATCH v4 00/10] HDMI CEC framework Kamil Debski
  2015-04-23 13:03 ` [PATCH v4 01/10] dts: exynos4*: add HDMI CEC pin definition to pinctrl Kamil Debski
@ 2015-04-23 13:03 ` Kamil Debski
  2015-04-23 13:03 ` [PATCH v4 03/10] dts: exynos4412-odroid*: enable " Kamil Debski
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 27+ messages in thread
From: Kamil Debski @ 2015-04-23 13:03 UTC (permalink / raw)
  To: dri-devel, linux-media
  Cc: m.szyprowski, k.debski, mchehab, hverkuil, kyungmin.park, thomas,
	sean, dmitry.torokhov, linux-input, linux-samsung-soc

This patch adds HDMI CEC node specific to the Exynos4210/4x12 SoC series.

Signed-off-by: Kamil Debski <k.debski@samsung.com>
---
 arch/arm/boot/dts/exynos4.dtsi |   12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi
index e20cdc2..8776db9 100644
--- a/arch/arm/boot/dts/exynos4.dtsi
+++ b/arch/arm/boot/dts/exynos4.dtsi
@@ -704,6 +704,18 @@
 		status = "disabled";
 	};
 
+	hdmicec: cec@100B0000 {
+		compatible = "samsung,s5p-cec";
+		reg = <0x100B0000 0x200>;
+		interrupts = <0 114 0>;
+		clocks = <&clock CLK_HDMI_CEC>;
+		clock-names = "hdmicec";
+		samsung,syscon-phandle = <&pmu_system_controller>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&hdmi_cec>;
+		status = "disabled";
+	};
+
 	mixer: mixer@12C10000 {
 		compatible = "samsung,exynos4210-mixer";
 		interrupts = <0 91 0>;
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v4 03/10] dts: exynos4412-odroid*: enable the HDMI CEC device
  2015-04-23 13:03 [PATCH v4 00/10] HDMI CEC framework Kamil Debski
  2015-04-23 13:03 ` [PATCH v4 01/10] dts: exynos4*: add HDMI CEC pin definition to pinctrl Kamil Debski
  2015-04-23 13:03 ` [PATCH v4 02/10] dts: exynos4: add node for the HDMI CEC device Kamil Debski
@ 2015-04-23 13:03 ` Kamil Debski
  2015-04-23 13:03 ` [PATCH v4 04/10] HID: add HDMI CEC specific keycodes Kamil Debski
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 27+ messages in thread
From: Kamil Debski @ 2015-04-23 13:03 UTC (permalink / raw)
  To: dri-devel, linux-media
  Cc: m.szyprowski, k.debski, mchehab, hverkuil, kyungmin.park, thomas,
	sean, dmitry.torokhov, linux-input, linux-samsung-soc

Add a dts node entry and enable the HDMI CEC device present in the Exynos4
family of SoCs.

Signed-off-by: Kamil Debski <k.debski@samsung.com>
---
 arch/arm/boot/dts/exynos4412-odroid-common.dtsi |    4 ++++
 1 file changed, 4 insertions(+)

diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi
index 8de12af..e50862d 100644
--- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi
+++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi
@@ -469,6 +469,10 @@
 		status = "okay";
 	};
 
+	cec@100B0000 {
+		status = "okay";
+	};
+
 	hdmi_ddc: i2c@13880000 {
 		status = "okay";
 		pinctrl-names = "default";
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v4 04/10] HID: add HDMI CEC specific keycodes
  2015-04-23 13:03 [PATCH v4 00/10] HDMI CEC framework Kamil Debski
                   ` (2 preceding siblings ...)
  2015-04-23 13:03 ` [PATCH v4 03/10] dts: exynos4412-odroid*: enable " Kamil Debski
@ 2015-04-23 13:03 ` Kamil Debski
  2015-04-23 13:03 ` [PATCH v4 05/10] rc: Add HDMI CEC protoctol handling Kamil Debski
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 27+ messages in thread
From: Kamil Debski @ 2015-04-23 13:03 UTC (permalink / raw)
  To: dri-devel, linux-media
  Cc: m.szyprowski, k.debski, mchehab, hverkuil, kyungmin.park, thomas,
	sean, dmitry.torokhov, linux-input, linux-samsung-soc

Add HDMI CEC specific keycodes to the keycodes definition.

Signed-off-by: Kamil Debski <k.debski@samsung.com>
---
 include/uapi/linux/input.h |   12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h
index 731417c..7430a3f 100644
--- a/include/uapi/linux/input.h
+++ b/include/uapi/linux/input.h
@@ -752,6 +752,18 @@ struct input_keymap_entry {
 #define KEY_KBDINPUTASSIST_ACCEPT		0x264
 #define KEY_KBDINPUTASSIST_CANCEL		0x265
 
+#define KEY_RIGHT_UP			0x266
+#define KEY_RIGHT_DOWN			0x267
+#define KEY_LEFT_UP			0x268
+#define KEY_LEFT_DOWN			0x269
+
+#define KEY_NEXT_FAVORITE		0x270
+#define KEY_STOP_RECORD			0x271
+#define KEY_PAUSE_RECORD		0x272
+#define KEY_VOD				0x273
+#define KEY_UNMUTE			0x274
+#define KEY_DVB				0x275
+
 #define BTN_TRIGGER_HAPPY		0x2c0
 #define BTN_TRIGGER_HAPPY1		0x2c0
 #define BTN_TRIGGER_HAPPY2		0x2c1
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v4 05/10] rc: Add HDMI CEC protoctol handling
  2015-04-23 13:03 [PATCH v4 00/10] HDMI CEC framework Kamil Debski
                   ` (3 preceding siblings ...)
  2015-04-23 13:03 ` [PATCH v4 04/10] HID: add HDMI CEC specific keycodes Kamil Debski
@ 2015-04-23 13:03 ` Kamil Debski
  2015-04-23 13:03 ` [PATCH v4 06/10] cec: add HDMI CEC framework Kamil Debski
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 27+ messages in thread
From: Kamil Debski @ 2015-04-23 13:03 UTC (permalink / raw)
  To: dri-devel, linux-media
  Cc: m.szyprowski, k.debski, mchehab, hverkuil, kyungmin.park, thomas,
	sean, dmitry.torokhov, linux-input, linux-samsung-soc

Add handling of remote control events coming from the HDMI CEC bus.
This patch includes a new keymap that maps values found in the CEC
messages to the keys pressed and released. Also, a new protocol has
been added to the core.

Signed-off-by: Kamil Debski <k.debski@samsung.com>
---
 drivers/media/rc/keymaps/Makefile |    1 +
 drivers/media/rc/keymaps/rc-cec.c |  144 +++++++++++++++++++++++++++++++++++++
 drivers/media/rc/rc-main.c        |    1 +
 include/media/rc-core.h           |    1 +
 include/media/rc-map.h            |    5 +-
 5 files changed, 151 insertions(+), 1 deletion(-)
 create mode 100644 drivers/media/rc/keymaps/rc-cec.c

diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile
index abf6079..56f10d6 100644
--- a/drivers/media/rc/keymaps/Makefile
+++ b/drivers/media/rc/keymaps/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
 			rc-behold.o \
 			rc-behold-columbus.o \
 			rc-budget-ci-old.o \
+			rc-cec.o \
 			rc-cinergy-1400.o \
 			rc-cinergy.o \
 			rc-delock-61959.o \
diff --git a/drivers/media/rc/keymaps/rc-cec.c b/drivers/media/rc/keymaps/rc-cec.c
new file mode 100644
index 0000000..cc5b318
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-cec.c
@@ -0,0 +1,144 @@
+/* Keytable for the CEC remote control
+ *
+ * Copyright (c) 2015 by Kamil Debski
+ *
+ * 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>
+
+/* CEC Spec "High-Definition Multimedia Interface Specification" can be obtained
+ * here: http://xtreamerdev.googlecode.com/files/CEC_Specs.pdf
+ * The list of control codes is listed in Table 27: User Control Codes p. 95 */
+
+static struct rc_map_table cec[] = {
+	{ 0x00, KEY_OK },
+	{ 0x01, KEY_UP },
+	{ 0x02, KEY_DOWN },
+	{ 0x03, KEY_LEFT },
+	{ 0x04, KEY_RIGHT },
+	{ 0x05, KEY_RIGHT_UP },
+	{ 0x06, KEY_RIGHT_DOWN },
+	{ 0x07, KEY_LEFT_UP },
+	{ 0x08, KEY_LEFT_DOWN },
+	{ 0x09, KEY_CONTEXT_MENU }, /* CEC Spec: Root Menu - see Note 2 */
+	/* Note 2: This is the initial display that a device shows. It is
+	 * device-dependent and can be, for example, a contents menu, setup
+	 * menu, favorite menu or other menu. The actual menu displayed
+	 * may also depend on the device’s current state. */
+	{ 0x0a, KEY_SETUP },
+	{ 0x0b, KEY_MENU }, /* CEC Spec: Contents Menu */
+	{ 0x0c, KEY_FAVORITES }, /* CEC Spec: Favorite Menu */
+	{ 0x0d, KEY_EXIT },
+	/* 0x0e-0x1f: Reserved */
+	/* 0x20-0x29: Keys 0 to 9 */
+	{ 0x20, KEY_NUMERIC_0 },
+	{ 0x21, KEY_NUMERIC_1 },
+	{ 0x22, KEY_NUMERIC_2 },
+	{ 0x23, KEY_NUMERIC_3 },
+	{ 0x24, KEY_NUMERIC_4 },
+	{ 0x25, KEY_NUMERIC_5 },
+	{ 0x26, KEY_NUMERIC_6 },
+	{ 0x27, KEY_NUMERIC_7 },
+	{ 0x28, KEY_NUMERIC_8 },
+	{ 0x29, KEY_NUMERIC_9 },
+	{ 0x2a, KEY_DOT },
+	{ 0x2b, KEY_ENTER },
+	{ 0x2c, KEY_CLEAR },
+	/* 0x2d-0x2e: Reserved */
+	{ 0x2f, KEY_NEXT_FAVORITE }, /* CEC Spec: Next Favorite */
+	{ 0x30, KEY_CHANNELUP },
+	{ 0x31, KEY_CHANNELDOWN },
+	{ 0x32, KEY_PREVIOUS }, /* CEC Spec: Previous Channel */
+	{ 0x33, KEY_SOUND }, /* CEC Spec: Sound Select */
+	{ 0x34, KEY_VIDEO }, /* 0x34: CEC Spec: Input Select */
+	{ 0x35, KEY_INFO }, /* CEC Spec: Display Information */
+	{ 0x36, KEY_HELP },
+	{ 0x37, KEY_PAGEUP },
+	{ 0x38, KEY_PAGEDOWN },
+	/* 0x39-0x3f: Reserved */
+	{ 0x40, KEY_POWER },
+	{ 0x41, KEY_VOLUMEUP },
+	{ 0x42, KEY_VOLUMEDOWN },
+	{ 0x43, KEY_MUTE },
+	{ 0x44, KEY_PLAY },
+	{ 0x45, KEY_STOP },
+	{ 0x46, KEY_PAUSE },
+	{ 0x47, KEY_RECORD },
+	{ 0x48, KEY_REWIND },
+	{ 0x49, KEY_FASTFORWARD },
+	{ 0x4a, KEY_EJECTCD }, /* CEC Spec: Eject */
+	{ 0x4b, KEY_FORWARD },
+	{ 0x4c, KEY_BACK },
+	{ 0x4d, KEY_STOP_RECORD }, /* CEC Spec: Stop-Record */
+	{ 0x4e, KEY_PAUSE_RECORD }, /* CEC Spec: Pause-Record */
+	/* 0x4f: Reserved */
+	{ 0x50, KEY_ANGLE },
+	{ 0x51, KEY_TV2 },
+	{ 0x52, KEY_VOD }, /* CEC Spec: Video on Demand */
+	{ 0x53, KEY_EPG },
+	{ 0x54, KEY_TIME }, /* CEC Spec: Timer */
+	{ 0x55, KEY_CONFIG },
+	/* 0x56-0x5f: Reserved */
+	{ 0x60, KEY_PLAY }, /* CEC Spec: Play Function */
+	{ 0x6024, KEY_PLAY },
+	{ 0x6020, KEY_PAUSE },
+	{ 0x61, KEY_PLAYPAUSE }, /* CEC Spec: Pause-Play Function */
+	{ 0x62, KEY_RECORD }, /* Spec: Record Function */
+	{ 0x63, KEY_PAUSE }, /* CEC Spec: Pause-Record Function */
+	{ 0x64, KEY_STOP }, /* CEC Spec: Stop Function */
+	{ 0x65, KEY_MUTE }, /* CEC Spec: Mute Function */
+	{ 0x66, KEY_UNMUTE }, /* CEC Spec: Restore the volume */
+	/* The following codes are hard to implement at this moment, as they
+	 * carry an additional additional argument. Most likely changes to RC
+	 * framework are necessary.
+	 * For now they are interpreted by the CEC framework as non keycodes
+	 * and are passed as messages enabling user application to parse them.
+	 * */
+	/* 0x67: CEC Spec: Tune Function */
+	/* 0x68: CEC Spec: Seleect Media Function */
+	/* 0x69: CEC Spec: Select A/V Input Function */
+	/* 0x6a: CEC Spec: Select Audio Input Function */
+	{ 0x6b, KEY_POWER }, /* CEC Spec: Power Toggle Function */
+	{ 0x6c, KEY_SLEEP }, /* CEC Spec: Power Off Function */
+	{ 0x6d, KEY_WAKEUP }, /* CEC Spec: Power On Function */
+	/* 0x6e-0x70: Reserved */
+	{ 0x71, KEY_BLUE }, /* CEC Spec: F1 (Blue) */
+	{ 0x72, KEY_RED }, /* CEC Spec: F2 (Red) */
+	{ 0x73, KEY_GREEN }, /* CEC Spec: F3 (Green) */
+	{ 0x74, KEY_YELLOW }, /* CEC Spec: F4 (Yellow) */
+	{ 0x75, KEY_F5 },
+	{ 0x76, KEY_DVB }, /* CEC Spec: Data - see Note 3 */
+	/* Note 3: This is used, for example, to enter or leave a digital TV
+	 * data broadcast application. */
+	/* 0x77-0xff: Reserved */
+};
+
+static struct rc_map_list cec_map = {
+	.map = {
+		.scan		= cec,
+		.size		= ARRAY_SIZE(cec),
+		.rc_type	= RC_TYPE_CEC,
+		.name		= RC_MAP_CEC,
+	}
+};
+
+static int __init init_rc_map_cec(void)
+{
+	return rc_map_register(&cec_map);
+}
+
+static void __exit exit_rc_map_cec(void)
+{
+	rc_map_unregister(&cec_map);
+}
+
+module_init(init_rc_map_cec);
+module_exit(exit_rc_map_cec);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Kamil Debski");
diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
index f8c5e47..37d1ce0 100644
--- a/drivers/media/rc/rc-main.c
+++ b/drivers/media/rc/rc-main.c
@@ -801,6 +801,7 @@ static struct {
 	{ RC_BIT_MCE_KBD,	"mce_kbd"	},
 	{ RC_BIT_LIRC,		"lirc"		},
 	{ RC_BIT_XMP,		"xmp"		},
+	{ RC_BIT_CEC,		"cec"		},
 };
 
 /**
diff --git a/include/media/rc-core.h b/include/media/rc-core.h
index 2c7fbca..7c9d15d 100644
--- a/include/media/rc-core.h
+++ b/include/media/rc-core.h
@@ -32,6 +32,7 @@ do {								\
 enum rc_driver_type {
 	RC_DRIVER_SCANCODE = 0,	/* Driver or hardware generates a scancode */
 	RC_DRIVER_IR_RAW,	/* Needs a Infra-Red pulse/space decoder */
+	RC_DRIVER_CEC,
 };
 
 /**
diff --git a/include/media/rc-map.h b/include/media/rc-map.h
index e7a1514..2058a89 100644
--- a/include/media/rc-map.h
+++ b/include/media/rc-map.h
@@ -32,6 +32,7 @@ enum rc_type {
 	RC_TYPE_RC6_MCE		= 17,	/* MCE (Philips RC6-6A-32 subtype) protocol */
 	RC_TYPE_SHARP		= 18,	/* Sharp protocol */
 	RC_TYPE_XMP		= 19,	/* XMP protocol */
+	RC_TYPE_CEC		= 20,	/* CEC protocol */
 };
 
 #define RC_BIT_NONE		0
@@ -55,6 +56,7 @@ enum rc_type {
 #define RC_BIT_RC6_MCE		(1 << RC_TYPE_RC6_MCE)
 #define RC_BIT_SHARP		(1 << RC_TYPE_SHARP)
 #define RC_BIT_XMP		(1 << RC_TYPE_XMP)
+#define RC_BIT_CEC		(1 << RC_TYPE_CEC)
 
 #define RC_BIT_ALL	(RC_BIT_UNKNOWN | RC_BIT_OTHER | RC_BIT_LIRC | \
 			 RC_BIT_RC5 | RC_BIT_RC5X | RC_BIT_RC5_SZ | \
@@ -63,7 +65,7 @@ enum rc_type {
 			 RC_BIT_NEC | RC_BIT_SANYO | RC_BIT_MCE_KBD | \
 			 RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 | RC_BIT_RC6_6A_24 | \
 			 RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE | RC_BIT_SHARP | \
-			 RC_BIT_XMP)
+			 RC_BIT_XMP | RC_BIT_CEC)
 
 
 #define RC_SCANCODE_UNKNOWN(x)			(x)
@@ -125,6 +127,7 @@ void rc_map_init(void);
 #define RC_MAP_BEHOLD_COLUMBUS           "rc-behold-columbus"
 #define RC_MAP_BEHOLD                    "rc-behold"
 #define RC_MAP_BUDGET_CI_OLD             "rc-budget-ci-old"
+#define RC_MAP_CEC                       "rc-cec"
 #define RC_MAP_CINERGY_1400              "rc-cinergy-1400"
 #define RC_MAP_CINERGY                   "rc-cinergy"
 #define RC_MAP_DELOCK_61959              "rc-delock-61959"
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v4 06/10] cec: add HDMI CEC framework
  2015-04-23 13:03 [PATCH v4 00/10] HDMI CEC framework Kamil Debski
                   ` (4 preceding siblings ...)
  2015-04-23 13:03 ` [PATCH v4 05/10] rc: Add HDMI CEC protoctol handling Kamil Debski
@ 2015-04-23 13:03 ` Kamil Debski
  2015-04-23 14:18   ` Hans Verkuil
                     ` (6 more replies)
  2015-04-23 13:03 ` [PATCH v4 07/10] v4l2-subdev: add HDMI CEC ops Kamil Debski
                   ` (2 subsequent siblings)
  8 siblings, 7 replies; 27+ messages in thread
From: Kamil Debski @ 2015-04-23 13:03 UTC (permalink / raw)
  To: dri-devel, linux-media
  Cc: m.szyprowski, k.debski, mchehab, hverkuil, kyungmin.park, thomas,
	sean, dmitry.torokhov, linux-input, linux-samsung-soc,
	Hans Verkuil

From: Hans Verkuil <hansverk@cisco.com>

The added HDMI CEC framework provides a generic kernel interface for
HDMI CEC devices.

Signed-off-by: Hans Verkuil <hansverk@cisco.com>
[k.debski@samsung.com: Merged CEC Updates commit by Hans Verkuil]
[k.debski@samsung.com: Merged Update author commit by Hans Verkuil]
[k.debski@samsung.com: change kthread handling when setting logical
address]
[k.debski@samsung.com: code cleanup and fixes]
[k.debski@samsung.com: add missing CEC commands to match spec]
[k.debski@samsung.com: add RC framework support]
[k.debski@samsung.com: move and edit documentation]
[k.debski@samsung.com: add vendor id reporting]
[k.debski@samsung.com: add possibility to clear assigned logical
addresses]
[k.debski@samsung.com: documentation fixes, clenaup and expansion]
[k.debski@samsung.com: reorder of API structs and add reserved fields]
[k.debski@samsung.com: fix handling of events and fix 32/64bit timespec
problem]
[k.debski@samsung.com: add cec.h to include/uapi/linux/Kbuild]
Signed-off-by: Kamil Debski <k.debski@samsung.com>
---
 Documentation/cec.txt     |  396 ++++++++++++++++
 drivers/media/Kconfig     |    6 +
 drivers/media/Makefile    |    2 +
 drivers/media/cec.c       | 1161 +++++++++++++++++++++++++++++++++++++++++++++
 include/media/cec.h       |  140 ++++++
 include/uapi/linux/Kbuild |    1 +
 include/uapi/linux/cec.h  |  303 ++++++++++++
 7 files changed, 2009 insertions(+)
 create mode 100644 Documentation/cec.txt
 create mode 100644 drivers/media/cec.c
 create mode 100644 include/media/cec.h
 create mode 100644 include/uapi/linux/cec.h

diff --git a/Documentation/cec.txt b/Documentation/cec.txt
new file mode 100644
index 0000000..2b6c08a
--- /dev/null
+++ b/Documentation/cec.txt
@@ -0,0 +1,396 @@
+CEC Kernel Support
+==================
+
+The CEC framework provides a unified kernel interface for use with HDMI CEC
+hardware. It is designed to handle a multiple variants of hardware. Adding to
+the flexibility of the framework it enables to set which parts of the CEC
+protocol processing is handled by the hardware, by the driver and by the
+userspace application.
+
+
+The CEC Protocol
+----------------
+
+The CEC protocol enables consumer electronic devices to communicate with each
+other through the HDMI connection. The protocol uses logical addresses in the
+communication. The logical address is strictly connected with the functionality
+provided by the device. The TV acting as the communication hub is always
+assigned address 0. The physical address is determined by the physical
+connection between devices.
+
+The protocol enables control of compatible devices with a single remote.
+Synchronous power on/standby, instant playback with changing the content source
+on the TV.
+
+The Kernel Interface
+====================
+
+CEC Adapter
+-----------
+
+#define CEC_LOG_ADDR_INVALID 0xff
+
+/* The maximum number of logical addresses one device can be assigned to.
+ * The CEC 2.0 spec allows for only 2 logical addresses at the moment. The
+ * Analog Devices CEC hardware supports 3. So let's go wild and go for 4. */
+#define CEC_MAX_LOG_ADDRS 4
+
+/* The "Primary Device Type" */
+#define CEC_PRIM_DEVTYPE_TV		0
+#define CEC_PRIM_DEVTYPE_RECORD		1
+#define CEC_PRIM_DEVTYPE_TUNER		3
+#define CEC_PRIM_DEVTYPE_PLAYBACK	4
+#define CEC_PRIM_DEVTYPE_AUDIOSYSTEM	5
+#define CEC_PRIM_DEVTYPE_SWITCH		6
+#define CEC_PRIM_DEVTYPE_VIDEOPROC	7
+
+/* The "All Device Types" flags (CEC 2.0) */
+#define CEC_FL_ALL_DEVTYPE_TV		(1 << 7)
+#define CEC_FL_ALL_DEVTYPE_RECORD	(1 << 6)
+#define CEC_FL_ALL_DEVTYPE_TUNER	(1 << 5)
+#define CEC_FL_ALL_DEVTYPE_PLAYBACK	(1 << 4)
+#define CEC_FL_ALL_DEVTYPE_AUDIOSYSTEM	(1 << 3)
+#define CEC_FL_ALL_DEVTYPE_SWITCH	(1 << 2)
+/* And if you wondering what happened to VIDEOPROC devices: those should
+ * be mapped to a SWITCH. */
+
+/* The logical address types that the CEC device wants to claim */
+#define CEC_LOG_ADDR_TYPE_TV		0
+#define CEC_LOG_ADDR_TYPE_RECORD	1
+#define CEC_LOG_ADDR_TYPE_TUNER		2
+#define CEC_LOG_ADDR_TYPE_PLAYBACK	3
+#define CEC_LOG_ADDR_TYPE_AUDIOSYSTEM	4
+#define CEC_LOG_ADDR_TYPE_SPECIFIC	5
+#define CEC_LOG_ADDR_TYPE_UNREGISTERED	6
+/* Switches should use UNREGISTERED.
+ * Video processors should use SPECIFIC. */
+
+/* The CEC version */
+#define CEC_VERSION_1_4B		5
+#define CEC_VERSION_2_0			6
+
+struct cec_adapter {
+	/* internal fields removed */
+
+	u16 phys_addr;
+	u32 capabilities;
+	u8 version;
+	u8 num_log_addrs;
+	u8 prim_device[CEC_MAX_LOG_ADDRS];
+	u8 log_addr_type[CEC_MAX_LOG_ADDRS];
+	u8 log_addr[CEC_MAX_LOG_ADDRS];
+
+	int (*adap_enable)(struct cec_adapter *adap, bool enable);
+	int (*adap_log_addr)(struct cec_adapter *adap, u8 logical_addr);
+	int (*adap_transmit)(struct cec_adapter *adap, struct cec_msg *msg);
+	void (*adap_transmit_timed_out)(struct cec_adapter *adap);
+
+	void (*claimed_log_addr)(struct cec_adapter *adap, u8 idx);
+	int (*received)(struct cec_adapter *adap, struct cec_msg *msg);
+};
+
+int cec_create_adapter(struct cec_adapter *adap, u32 caps);
+void cec_delete_adapter(struct cec_adapter *adap);
+int cec_transmit_msg(struct cec_adapter *adap, struct cec_data *data, bool block);
+
+/* Called by the adapter */
+void cec_transmit_done(struct cec_adapter *adap, u32 status);
+void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg);
+
+int cec_receive_msg(struct cec_adapter *adap, struct cec_msg *msg, bool block);
+int cec_claim_log_addrs(struct cec_adapter *adap, struct cec_log_addrs *log_addrs, bool block);
+
+The device type defines are defined by the CEC standard.
+
+The cec_adapter structure represents the adapter. It has a number of
+operations that have to be implemented in the driver: adap_enable() enables
+or disables the physical adapter, adap_log_addr() tells the driver which
+logical address should be configured. This may be called multiple times
+to configure multiple logical addresses. Calling adap_enable(false) or
+adap_log_addr(CEC_LOG_ADDR_INVALID) will clear all configured logical
+addresses.
+
+The adap_transmit op will setup the hardware to send out the given CEC message.
+This will return without waiting for the transmission to finish. The
+adap_transmit_timed_out() function is called when the current transmission timed
+out and the hardware needs to be informed of this (the hardware should go back
+from transmitter to receiver mode).
+
+The adapter driver will also call into the adapter: it should call
+cec_transmit_done() when a cec transfer was finalized and cec_received_msg()
+when a new message was received.
+
+When a message is received the received() op is called.
+
+The driver has to call cec_create_adapter to initialize the structure. If
+the 'caps' argument is non-zero, then it will also create a /dev/cecX
+device node to allow userspace to interact with the CEC device. Userspace
+can request those capabilities with the CEC_G_CAPS ioctl.
+
+In order for a CEC adapter to be configured it needs a physical address.
+This is normally assigned by the driver. It is either 0.0.0.0 for a TV (aka
+video receiver) or it is derived from the EDID that the source received
+from the sink. This is normally set by the driver before enabling the CEC
+adapter, or it is set from userspace in the case of CEC USB dongles (although
+embedded systems might also want to set this manually).
+
+After enabling the CEC adapter it has to be configured.
+
+The userspace has to inform the CEC adapter of which type of device it requests
+the adapter to identify itself. After this information is set by userspace, the
+CEC framework will attempt to to find and claim a logical addresses matching the
+requested device type. If none are found, then it will fall back to logical
+address Unregistered (15). To clear the logical addresses list from the list the
+userspace application should set the num_log_addrs field of struct cec_log_addr
+to 0.
+
+The type of device is set from the userspace with the CEC_S_ADAP_LOG_ADDRS. In
+addition, claiming logical addresses can be initiated from the kernel side by
+calling the cec_claim_log_addrs function.
+
+Before the addresses are claimed it is possible to send and receive messages.
+Sending all messages is possible as it is up to the userspace to the source
+and destination addresses in the message payload. However, only broadcast
+messages can be received until a regular logical address is claimed.
+
+When a CEC message is received the CEC framework will take care of the CEC
+core messages CEC_OP_GET_CEC_VERSION, CEC_OP_GIVE_PHYS_ADDR and CEC_OP_ABORT.
+Then it will call the received() op (if set), and finally it will queue it
+for handling by userspace if create_devnode was true, or send back
+FEATURE_ABORT if create_devnode was false.
+
+Drivers can also use the cec_transmit_msg() call to transmit a message. This
+can either be fire-and-forget (the CEC framework will queue up messages in a
+transmit queue), or a blocking wait until there is either an error or a
+reply to the message.
+
+
+The Userspace API
+=================
+
+ioctl API
+---------
+
+- CEC_G_CAPS ioctl
+
+Read the CEC adapter capabilities: the number of logical addresses
+that the adapter can configure and what can be controlled from userspace.
+
+#define CEC_G_CAPS			_IOR('a', 0, struct cec_caps)
+
+The cec_caps struct is following:
+
+struct cec_caps {
+	__u32 available_log_addrs;
+	__u32 capabilities;
+	__u32 vendor_id;
+	__u8  version;
+	__u8  reserved[11];
+};
+
+The following capabilities are defined:
+
+/* Userspace has to configure the adapter state (enable/disable) */
+#define CEC_CAP_STATE		(1 << 0)
+/* Userspace has to configure the physical address */
+#define CEC_CAP_PHYS_ADDR	(1 << 1)
+/* Userspace has to configure the logical addresses */
+#define CEC_CAP_LOG_ADDRS	(1 << 2)
+/* Userspace can transmit messages */
+#define CEC_CAP_TRANSMIT	(1 << 3)
+/* Userspace can receive messages */
+#define CEC_CAP_RECEIVE		(1 << 4)
+/* Userspace has to configure the vendor id */
+#define CEC_CAP_VENDOR_ID	(1 << 5)
+/* The hardware has the possibility to work in the promiscuous mode */
+#define CEC_CAP_PROMISCUOUS	(1 << 6)
+
+- CEC_TRANSMIT and CEC_RECEIVE ioctls
+
+These ioctls are used to send and receive messages over the CEC bus.
+
+#define CEC_TRANSMIT		_IOWR('a', 1, struct cec_msg)
+#define CEC_RECEIVE		_IOWR('a', 2, struct cec_msg)
+
+The struct cec_msg is the main message struct:
+
+struct cec_msg {
+	__u32 len;
+	__u32 status;
+	__u32 timeout;
+	/* timeout (in ms) is used to timeout CEC_RECEIVE.
+	   Set to 0 if you want to wait forever. */
+	struct cec_time ts;
+	__u8  msg[16];
+	__u8  reply;
+	/* If non-zero, then wait for a reply with this opcode.
+	   If there was an error when sending the msg or FeatureAbort
+	   was returned, then reply is set to 0.
+	   If reply is non-zero upon return, then len/msg are set to
+	   the received message.
+	   If reply is zero upon return and status has the
+	   CEC_TX_STATUS_FEATURE_ABORT bit set, then len/msg are set to the
+	   received feature abort message.  If reply is zero upon return and
+	   status has the CEC_TX_STATUS_REPLY_TIMEOUT bit set, then no reply
+	   was seen at all.  This field is ignored with CEC_RECEIVE.
+	   If reply is non-zero for CEC_TRANSMIT and the message is a broadcast,
+	   then -EINVAL is returned.
+	   if reply is non-zero, then timeout is set to 1000 (the required
+	   maximum response time).
+	 */
+	__u8 reserved[31];
+};
+
+The struct contains 16 bytes for the message, the length of the message, a
+status value in case of errors. Optionally you can request the CEC framework to
+wait after transmitting the message until the 'reply' message is returned (or
+Feature Abort). This is done asynchronously, i.e. it does not require that the
+reply comes right after the transmit, but other messages in between are allowed.
+
+The ts field of the struct cec_msg represents a timestamp. The timestamp struct
+is following:
+
+struct cec_time {
+	__u64 sec;
+	__u64 nsec;
+};
+
+With CEC_TRANSMIT you can transmit a message, either blocking or
+non-blocking. With CEC_RECEIVE you can dequeue a pending received
+message from the internal queue or wait for a message to arrive
+(if called in blocking mode).
+
+- CEC_G_ADAP_LOG_ADDRS and CEC_S_ADAP_LOG_ADDRS
+
+These ioctl are used to configure the logical addresses of the CEC adapter.
+
+#define CEC_G_ADAP_LOG_ADDRS	_IOR('a', 3, struct cec_log_addrs)
+#define CEC_S_ADAP_LOG_ADDRS	_IOWR('a', 4, struct cec_log_addrs)
+
+The struct cec_log_addrs is following:
+
+struct cec_log_addrs {
+	__u8 cec_version;
+	__u8 num_log_addrs;
+	__u8 primary_device_type[CEC_MAX_LOG_ADDRS];
+	__u8 log_addr_type[CEC_MAX_LOG_ADDRS];
+	__u8 log_addr[CEC_MAX_LOG_ADDRS];
+
+	/* CEC 2.0 */
+	__u8 all_device_types;
+	__u8 features[CEC_MAX_LOG_ADDRS][12];
+
+	__u8 reserved[9];
+};
+
+The cec_version determines which CEC version should be used.
+
+/* The CEC version */
+#define CEC_VERSION_1_4B		5
+#define CEC_VERSION_2_0			6
+
+It will try to claim num_log_addrs devices. The log_addr_type array has
+the logical address type that needs to be claimed for that device, and
+the log_addr array will receive the actual logical address that was
+claimed for that device or 0xff if no address could be claimed.
+
+The primary_device_type contains the primary device for each logical
+address.
+
+For CEC 2.0 devices fill in the all_device_types parameter to use with the
+Report Features command, and fill in the 'features' which contains the
+remaining parameters (RC Profile and Device Features) to use in Report
+Features.
+
+An error is returned if the adapter is disabled or if there
+is no physical address assigned or if the cec_version is unknown.
+
+If no logical address of one or more of the given types could be claimed,
+then log_addr will be set to CEC_LOG_ADDR_INVALID.
+
+If no logical address could be claimed at all, then num_log_addrs will
+be set to 1, log_addr_type[0] to UNREGISTERED and log_addr[0] to 0xf.
+
+The S_ADAP_LOG_ADDRS ioctl is not available unless CEC_CAP_LOG_ADDRS
+is set.
+
+- CEC_G_ADAP_STATE and CEC_S_ADAP_STATE ioctls
+
+Enable/disable the adapter. The S_ADAP_STATE ioctl is not available
+unless CEC_CAP_STATE is set.
+
+#define CEC_G_ADAP_STATE	_IOR('a', 5, __u32)
+#define CEC_S_ADAP_STATE	_IOW('a', 6, __u32)
+
+State CEC_STATE_DISABLED means the adapter is disabled, CEC_STATE_ENABLED
+stands for adapter enabled.
+
+/* The CEC state */
+#define CEC_STATE_DISABLED		0
+#define CEC_STATE_ENABLED		1
+
+- CEC_G_ADAP_PHYS_ADDR and CEC_S_ADAP_PHYS_ADDR ioctls
+
+phys_addr is either 0 (if this is the CEC root device) or a valid physical
+address obtained from the EDID of the sink as read by this CEC device (if this
+is a source device) or a physical address obtained and modified from
+the EDID of the sink and used for a sink CEC device.  If nothing is connected,
+then phys_addr is 0xffff.  See HDMI 1.4b, section 8.7 (Physical Address).
+
+#define CEC_G_ADAP_PHYS_ADDR	_IOR('a', 7, __u16)
+#define CEC_S_ADAP_PHYS_ADDR	_IOW('a', 8, __u16)
+
+The S_ADAP_PHYS_ADDR ioctl is not available unless CEC_CAP_PHYS_ADDR
+is set.
+
+- CEC_G_EVENT ioctl
+
+This ioctl is used to read a pending event. It takes a struct cec_event
+that is filled with appropriate data.
+
+The struct cec_event is following:
+
+struct cec_event {
+	struct cec_time ts;
+	__u32 event;
+	__u32 reserved[4];
+};
+
+- CEC_G_VENDOR_ID and CEC_S_VENDOR_ID ioctls
+
+These calls are used to read or set the vendor ID of the adapter.
+
+#define CEC_G_VENDOR_ID		_IOR('a', 9, __u32)
+#define CEC_S_VENDOR_ID		_IOW('a', 10, __u32)
+
+Vendor ID is a 24 bit identifier obtained from the IEEE Registration
+Authority Committee.
+
+The CEC_S_ADAP_VENDOR_ID ioctl is not available unless CEC_CAP_VENDOR_ID
+is set.
+
+Events
+------
+
+The CEC framework provides a way for the userspace to be informed about
+a number of event that can occur in the hardware.
+
+The userspace is informed about a new event with the POLLPRI event of the
+poll function.
+
+The list of events is following:
+
+/* Event that occurs when a cable is connected */
+#define CEC_EVENT_CONNECT	1
+/* Event that occurs when all logical addresses were claimed */
+#define CEC_EVENT_READY		2
+/* Event that is sent when the cable is disconnected */
+#define CEC_EVENT_DISCONNECT	3
+
+The events can be read with the CEC_G_EVENT ioctl.
+
+Remote control handling
+-----------------------
+
+The CEC framework handles the key up/down messages of remote control and
+provides the key events via the RC framework.
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
index 3ef0f90..262e9ad 100644
--- a/drivers/media/Kconfig
+++ b/drivers/media/Kconfig
@@ -15,6 +15,12 @@ if MEDIA_SUPPORT
 
 comment "Multimedia core support"
 
+config CEC
+	tristate "CEC API (EXPERIMENTAL)"
+	select RC_CORE
+	---help---
+	  Enable the CEC API.
+
 #
 # Multimedia support - automatically enable V4L2 and DVB core
 #
diff --git a/drivers/media/Makefile b/drivers/media/Makefile
index e608bbc..db66014 100644
--- a/drivers/media/Makefile
+++ b/drivers/media/Makefile
@@ -2,6 +2,8 @@
 # Makefile for the kernel multimedia device drivers.
 #
 
+obj-$(CONFIG_CEC) += cec.o
+
 media-objs	:= media-device.o media-devnode.o media-entity.o
 
 #
diff --git a/drivers/media/cec.c b/drivers/media/cec.c
new file mode 100644
index 0000000..bf5cc07
--- /dev/null
+++ b/drivers/media/cec.c
@@ -0,0 +1,1161 @@
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/ktime.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <media/cec.h>
+
+#define CEC_NUM_DEVICES	256
+#define CEC_NAME	"cec"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "debug level (0-1)");
+
+struct cec_transmit_notifier {
+	struct completion c;
+	struct cec_data *data;
+};
+
+#define dprintk(fmt, arg...)						\
+	do {								\
+		if (debug)						\
+			pr_info("cec-%s: " fmt, adap->name, ## arg);	\
+	} while (0)
+
+static dev_t cec_dev_t;
+
+/* Active devices */
+static DEFINE_MUTEX(cec_devnode_lock);
+static DECLARE_BITMAP(cec_devnode_nums, CEC_NUM_DEVICES);
+
+/* dev to cec_devnode */
+#define to_cec_devnode(cd) container_of(cd, struct cec_devnode, dev)
+
+static inline struct cec_devnode *cec_devnode_data(struct file *filp)
+{
+	return filp->private_data;
+}
+
+static int cec_log_addr2idx(const struct cec_adapter *adap, u8 log_addr)
+{
+	int i;
+
+	for (i = 0; i < adap->num_log_addrs; i++)
+		if (adap->log_addr[i] == log_addr)
+			return i;
+	return -1;
+}
+
+static unsigned cec_log_addr2dev(const struct cec_adapter *adap, u8 log_addr)
+{
+	int i = cec_log_addr2idx(adap, log_addr);
+
+	return adap->prim_device[i < 0 ? 0 : i];
+}
+
+/* Called when the last user of the cec device exits. */
+static void cec_devnode_release(struct device *cd)
+{
+	struct cec_devnode *cecdev = to_cec_devnode(cd);
+
+	mutex_lock(&cec_devnode_lock);
+
+	/* Delete the cdev on this minor as well */
+	cdev_del(&cecdev->cdev);
+
+	/* Mark device node number as free */
+	clear_bit(cecdev->minor, cec_devnode_nums);
+
+	mutex_unlock(&cec_devnode_lock);
+
+	/* Release cec_devnode and perform other cleanups as needed. */
+	if (cecdev->release)
+		cecdev->release(cecdev);
+}
+
+static struct bus_type cec_bus_type = {
+	.name = CEC_NAME,
+};
+
+static bool cec_sleep(struct cec_adapter *adap, int timeout)
+{
+	bool timed_out = false;
+
+	DECLARE_WAITQUEUE(wait, current);
+
+	add_wait_queue(&adap->kthread_waitq, &wait);
+	if (!kthread_should_stop()) {
+		if (timeout < 0) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule();
+		} else {
+			timed_out = !schedule_timeout_interruptible
+				(msecs_to_jiffies(timeout));
+		}
+	}
+
+	remove_wait_queue(&adap->kthread_waitq, &wait);
+	return timed_out;
+}
+
+/*
+ * Main CEC state machine
+ *
+ * In the IDLE state the CEC adapter is ready to receive or transmit messages.
+ * If it is woken up it will check if a new message is queued, and if so it
+ * will be transmitted and the state will go to TRANSMITTING.
+ *
+ * When the transmit is marked as done the state machine will check if it
+ * should wait for a reply. If not, it will call the notifier and go back
+ * to the IDLE state. Else it will switch to the WAIT state and wait for a
+ * reply. When the reply arrives it will call the notifier and go back
+ * to IDLE state.
+ *
+ * For the transmit and the wait-for-reply states a timeout is used of
+ * 1 second as per the standard.
+ */
+static int cec_thread_func(void *data)
+{
+	struct cec_adapter *adap = data;
+	int timeout = -1;
+
+	for (;;) {
+		bool timed_out = cec_sleep(adap, timeout);
+
+		if (kthread_should_stop())
+			break;
+		timeout = -1;
+		mutex_lock(&adap->lock);
+		dprintk("state %d timedout: %d tx: %d@%d\n", adap->state,
+			timed_out, adap->tx_qcount, adap->tx_qstart);
+		if (adap->state == CEC_ADAP_STATE_TRANSMITTING && timed_out)
+			adap->adap_transmit_timed_out(adap);
+
+		if (adap->state == CEC_ADAP_STATE_WAIT ||
+		    adap->state == CEC_ADAP_STATE_TRANSMITTING) {
+			struct cec_data *data = adap->tx_queue +
+						adap->tx_qstart;
+
+			if (adap->state == CEC_ADAP_STATE_TRANSMITTING &&
+			    data->msg.reply && !timed_out &&
+			    data->msg.status == CEC_TX_STATUS_OK) {
+				adap->state = CEC_ADAP_STATE_WAIT;
+				timeout = 1000;
+			} else {
+				if (timed_out) {
+					data->msg.reply = 0;
+					if (adap->state ==
+					    CEC_ADAP_STATE_TRANSMITTING)
+						data->msg.status =
+						    CEC_TX_STATUS_RETRY_TIMEOUT;
+					else
+						data->msg.status =
+						    CEC_TX_STATUS_REPLY_TIMEOUT;
+				}
+				adap->state = CEC_ADAP_STATE_IDLE;
+				if (data->func) {
+					mutex_unlock(&adap->lock);
+					data->func(adap, data, data->priv);
+					mutex_lock(&adap->lock);
+				}
+				adap->tx_qstart = (adap->tx_qstart + 1) %
+						  CEC_TX_QUEUE_SZ;
+				adap->tx_qcount--;
+				wake_up_interruptible(&adap->waitq);
+			}
+		}
+		if (adap->state == CEC_ADAP_STATE_IDLE && adap->tx_qcount) {
+			adap->state = CEC_ADAP_STATE_TRANSMITTING;
+			timeout = adap->tx_queue[adap->tx_qstart].msg.len == 1 ?
+				  200 : 1000;
+			adap->adap_transmit(adap,
+					  &adap->tx_queue[adap->tx_qstart].msg);
+			mutex_unlock(&adap->lock);
+			continue;
+		}
+		mutex_unlock(&adap->lock);
+	}
+	return 0;
+}
+
+static int cec_transmit_notify(struct cec_adapter *adap, struct cec_data *data,
+		void *priv)
+{
+	struct cec_transmit_notifier *n = priv;
+
+	*(n->data) = *data;
+	complete(&n->c);
+	return 0;
+}
+
+int cec_transmit_msg(struct cec_adapter *adap, struct cec_data *data,
+		     bool block)
+{
+	struct cec_transmit_notifier notifier;
+	struct cec_msg *msg = &data->msg;
+	int res = 0;
+	unsigned idx;
+
+	if (msg->len == 0 || msg->len > 16)
+		return -EINVAL;
+	if (msg->reply && (msg->len == 1 || cec_msg_is_broadcast(msg)))
+		return -EINVAL;
+	if (msg->len > 1 && !cec_msg_is_broadcast(msg) &&
+	    cec_msg_initiator(msg) == cec_msg_destination(msg))
+		return -EINVAL;
+	if (cec_msg_initiator(msg) != 0xf &&
+	    cec_log_addr2idx(adap, cec_msg_initiator(msg)) < 0)
+		return -EINVAL;
+
+	if (msg->len == 1)
+		dprintk("cec_transmit_msg: 0x%02x%s\n",
+				msg->msg[0], !block ? " nb" : "");
+	else if (msg->reply)
+		dprintk("cec_transmit_msg: 0x%02x 0x%02x (wait for 0x%02x)%s\n",
+				msg->msg[0], msg->msg[1],
+				msg->reply, !block ? " nb" : "");
+	else
+		dprintk("cec_transmit_msg: 0x%02x 0x%02x%s\n",
+				msg->msg[0], msg->msg[1],
+				!block ? " nb" : "");
+
+	msg->status = 0;
+	memset(&msg->ts, 0, sizeof(msg->ts));
+	if (msg->reply)
+		msg->timeout = 1000;
+	if (block) {
+		init_completion(&notifier.c);
+		notifier.data = data;
+		data->func = cec_transmit_notify;
+		data->priv = &notifier;
+	} else {
+		data->func = NULL;
+		data->priv = NULL;
+	}
+	mutex_lock(&adap->lock);
+	idx = (adap->tx_qstart + adap->tx_qcount) % CEC_TX_QUEUE_SZ;
+	if (adap->tx_qcount == CEC_TX_QUEUE_SZ) {
+		res = -EBUSY;
+	} else {
+		adap->tx_queue[idx] = *data;
+		adap->tx_qcount++;
+		if (adap->state == CEC_ADAP_STATE_IDLE)
+			wake_up_interruptible(&adap->kthread_waitq);
+	}
+	mutex_unlock(&adap->lock);
+	if (res || !block)
+		return res;
+	wait_for_completion_interruptible(&notifier.c);
+	return res;
+}
+EXPORT_SYMBOL_GPL(cec_transmit_msg);
+
+void cec_transmit_done(struct cec_adapter *adap, u32 status)
+{
+	struct cec_msg *msg;
+	struct timespec64 ts;
+
+	dprintk("cec_transmit_done\n");
+	mutex_lock(&adap->lock);
+	if (adap->state == CEC_ADAP_STATE_TRANSMITTING) {
+		msg = &adap->tx_queue[adap->tx_qstart].msg;
+		msg->status = status;
+		if (status)
+			msg->reply = 0;
+		ktime_get_ts64(&ts);
+		msg->ts.sec = ts.tv_sec;
+		msg->ts.nsec = ts.tv_nsec;
+		wake_up_interruptible(&adap->kthread_waitq);
+	}
+	mutex_unlock(&adap->lock);
+}
+EXPORT_SYMBOL_GPL(cec_transmit_done);
+
+static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg)
+{
+	bool is_broadcast = cec_msg_is_broadcast(msg);
+	u8 dest_laddr = cec_msg_destination(msg);
+	u8 devtype = cec_log_addr2dev(adap, dest_laddr);
+	bool is_directed = cec_log_addr2idx(adap, dest_laddr) >= 0;
+	struct cec_data tx_data;
+	int res = 0;
+	unsigned idx;
+
+	if (msg->len <= 1)
+		return 0;
+	if (!is_directed && !is_broadcast)
+		return 0;	/* Not for us */
+
+	tx_data.msg.msg[0] = (msg->msg[0] << 4) | (msg->msg[0] >> 4);
+	tx_data.msg.reply = 0;
+
+	if (adap->received) {
+		res = adap->received(adap, msg);
+		if (res != -ENOMSG)
+			return 0;
+		res = 0;
+	}
+
+	switch (msg->msg[1]) {
+	case CEC_OP_GET_CEC_VERSION:
+		if (is_broadcast)
+			return 0;
+		tx_data.msg.len = 3;
+		tx_data.msg.msg[1] = CEC_OP_CEC_VERSION;
+		tx_data.msg.msg[2] = adap->version;
+		return cec_transmit_msg(adap, &tx_data, false);
+
+	case CEC_OP_GIVE_PHYSICAL_ADDR:
+		if (!is_directed)
+			return 0;
+		/* Do nothing for CEC switches using addr 15 */
+		if (devtype == CEC_PRIM_DEVTYPE_SWITCH && dest_laddr == 15)
+			return 0;
+		tx_data.msg.len = 5;
+		tx_data.msg.msg[1] = CEC_OP_REPORT_PHYSICAL_ADDR;
+		tx_data.msg.msg[2] = adap->phys_addr >> 8;
+		tx_data.msg.msg[3] = adap->phys_addr & 0xff;
+		tx_data.msg.msg[4] = devtype;
+		return cec_transmit_msg(adap, &tx_data, false);
+
+	case CEC_OP_ABORT:
+		/* Do nothing for CEC switches */
+		if (devtype == CEC_PRIM_DEVTYPE_SWITCH)
+			return 0;
+		tx_data.msg.len = 4;
+		tx_data.msg.msg[1] = CEC_OP_FEATURE_ABORT;
+		tx_data.msg.msg[2] = msg->msg[1];
+		tx_data.msg.msg[3] = 4;	/* Refused */
+		return cec_transmit_msg(adap, &tx_data, false);
+
+	case CEC_OP_USER_CONTROL_PRESSED:
+		switch (msg->msg[2]) {
+		/* Play function, this message can have variable length
+		 * depending on the specific play function that is used.
+		 */
+		case 0x60:
+			if (msg->len == 3)
+				rc_keydown(adap->rc, RC_TYPE_CEC,
+					   msg->msg[2] << 8 | msg->msg[3], 0);
+			else
+				rc_keydown(adap->rc, RC_TYPE_CEC, msg->msg[2],
+					   0);
+			break;
+		/* Other function messages that are not handled.
+		 * Currently the RC framework does not allow to supply an
+		 * additional parameter to a keypress. These "keys" contain
+		 * other information such as channel number, an input number
+		 * etc.
+		 * For the time being these messages are not processed by the
+		 * framework and are simply forwarded to the user space.
+		 */
+		case 0x67: case 0x68: case 0x69: case 0x6a:
+			break;
+		default:
+			rc_keydown(adap->rc, RC_TYPE_CEC, msg->msg[2], 0);
+		}
+		break;
+	case CEC_OP_USER_CONTROL_RELEASED:
+		rc_keyup(adap->rc);
+		return 0;
+	}
+
+	if ((adap->capabilities & CEC_CAP_RECEIVE) == 0)
+		return 0;
+	mutex_lock(&adap->lock);
+	idx = (adap->rx_qstart + adap->rx_qcount) % CEC_RX_QUEUE_SZ;
+	if (adap->rx_qcount == CEC_RX_QUEUE_SZ) {
+		res = -EBUSY;
+	} else {
+		adap->rx_queue[idx] = *msg;
+		adap->rx_qcount++;
+		wake_up_interruptible(&adap->waitq);
+	}
+	mutex_unlock(&adap->lock);
+	return res;
+}
+
+int cec_receive_msg(struct cec_adapter *adap, struct cec_msg *msg, bool block)
+{
+	int res;
+
+	do {
+		mutex_lock(&adap->lock);
+		if (adap->rx_qcount) {
+			*msg = adap->rx_queue[adap->rx_qstart];
+			adap->rx_qstart = (adap->rx_qstart + 1) %
+					  CEC_RX_QUEUE_SZ;
+			adap->rx_qcount--;
+			res = 0;
+		} else {
+			res = -EAGAIN;
+		}
+		mutex_unlock(&adap->lock);
+		if (!block || !res)
+			break;
+		if (msg->timeout) {
+			res = wait_event_interruptible_timeout(adap->waitq,
+				adap->rx_qcount,
+				msecs_to_jiffies(msg->timeout));
+			if (res == 0)
+				res = -ETIMEDOUT;
+			else if (res > 0)
+				res = 0;
+		} else {
+			res = wait_event_interruptible(adap->waitq,
+				adap->rx_qcount);
+		}
+	} while (!res);
+	return res;
+}
+EXPORT_SYMBOL_GPL(cec_receive_msg);
+
+void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg)
+{
+	struct timespec64 ts;
+	bool is_reply = false;
+
+	mutex_lock(&adap->lock);
+	ktime_get_ts64(&ts);
+	msg->ts.sec = ts.tv_sec;
+	msg->ts.nsec = ts.tv_nsec;
+	dprintk("cec_received_msg: %02x %02x\n", msg->msg[0], msg->msg[1]);
+	if (!cec_msg_is_broadcast(msg) && msg->len > 1 &&
+	    adap->state == CEC_ADAP_STATE_WAIT) {
+		struct cec_msg *dst = &adap->tx_queue[adap->tx_qstart].msg;
+
+		if (msg->msg[1] == dst->reply ||
+		    msg->msg[1] == CEC_OP_FEATURE_ABORT) {
+			*dst = *msg;
+			is_reply = true;
+			if (msg->msg[1] == CEC_OP_FEATURE_ABORT) {
+				dst->reply = 0;
+				dst->status = CEC_TX_STATUS_FEATURE_ABORT;
+			}
+			wake_up_interruptible(&adap->kthread_waitq);
+		}
+	}
+	mutex_unlock(&adap->lock);
+	if (!is_reply)
+		adap->recv_notifier(adap, msg);
+}
+EXPORT_SYMBOL_GPL(cec_received_msg);
+
+void cec_post_event(struct cec_adapter *adap, u32 event)
+{
+	struct timespec64 ts;
+	unsigned idx;
+
+	mutex_lock(&adap->lock);
+	if (adap->ev_qcount == CEC_EV_QUEUE_SZ) {
+		/* Drop oldest event */
+		adap->ev_qstart = (adap->ev_qstart + 1) % CEC_EV_QUEUE_SZ;
+		adap->ev_qcount--;
+	}
+
+	idx = (adap->ev_qstart + adap->ev_qcount) % CEC_EV_QUEUE_SZ;
+
+	adap->ev_queue[idx].event = event;
+	ktime_get_ts64(&ts);
+	adap->ev_queue[idx].ts.sec = ts.tv_sec;
+	adap->ev_queue[idx].ts.nsec = ts.tv_nsec;
+
+	adap->ev_qcount++;
+	mutex_unlock(&adap->lock);
+}
+EXPORT_SYMBOL_GPL(cec_post_event);
+
+static int cec_report_phys_addr(struct cec_adapter *adap, unsigned logical_addr)
+{
+	struct cec_data data;
+
+	/* Report Physical Address */
+	data.msg.len = 5;
+	data.msg.msg[0] = (logical_addr << 4) | 0x0f;
+	data.msg.msg[1] = CEC_OP_REPORT_PHYSICAL_ADDR;
+	data.msg.msg[2] = adap->phys_addr >> 8;
+	data.msg.msg[3] = adap->phys_addr & 0xff;
+	data.msg.msg[4] = cec_log_addr2dev(adap, logical_addr);
+	data.msg.reply = 0;
+	dprintk("config: la %d pa %x.%x.%x.%x\n",
+			logical_addr, cec_phys_addr_exp(adap->phys_addr));
+	return cec_transmit_msg(adap, &data, true);
+}
+
+int cec_enable(struct cec_adapter *adap, bool enable)
+{
+	int ret;
+
+	mutex_lock(&adap->lock);
+	ret = adap->adap_enable(adap, enable);
+	if (ret) {
+		mutex_unlock(&adap->lock);
+		return ret;
+	}
+	if (!enable) {
+		adap->state = CEC_ADAP_STATE_DISABLED;
+		adap->tx_qcount = 0;
+		adap->rx_qcount = 0;
+		adap->ev_qcount = 0;
+		adap->num_log_addrs = 0;
+	} else {
+		adap->state = CEC_ADAP_STATE_UNCONF;
+	}
+	mutex_unlock(&adap->lock);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cec_enable);
+
+struct cec_log_addrs_int {
+	struct cec_adapter *adap;
+	struct cec_log_addrs log_addrs;
+	struct completion c;
+	bool free_on_exit;
+	int err;
+};
+
+static int cec_config_log_addrs(struct cec_adapter *adap,
+				struct cec_log_addrs *log_addrs)
+{
+	static const u8 tv_log_addrs[] = {
+		0, CEC_LOG_ADDR_INVALID
+	};
+	static const u8 record_log_addrs[] = {
+		1, 2, 9, 12, 13, CEC_LOG_ADDR_INVALID
+	};
+	static const u8 tuner_log_addrs[] = {
+		3, 6, 7, 10, 12, 13, CEC_LOG_ADDR_INVALID
+	};
+	static const u8 playback_log_addrs[] = {
+		4, 8, 11, 12, 13, CEC_LOG_ADDR_INVALID
+	};
+	static const u8 audiosystem_log_addrs[] = {
+		5, 12, 13, CEC_LOG_ADDR_INVALID
+	};
+	static const u8 specific_use_log_addrs[] = {
+		14, 12, 13, CEC_LOG_ADDR_INVALID
+	};
+	static const u8 unregistered_log_addrs[] = {
+		CEC_LOG_ADDR_INVALID
+	};
+	static const u8 *type2addrs[7] = {
+		[CEC_LOG_ADDR_TYPE_TV] = tv_log_addrs,
+		[CEC_LOG_ADDR_TYPE_RECORD] = record_log_addrs,
+		[CEC_LOG_ADDR_TYPE_TUNER] = tuner_log_addrs,
+		[CEC_LOG_ADDR_TYPE_PLAYBACK] = playback_log_addrs,
+		[CEC_LOG_ADDR_TYPE_AUDIOSYSTEM] = audiosystem_log_addrs,
+		[CEC_LOG_ADDR_TYPE_SPECIFIC] = specific_use_log_addrs,
+		[CEC_LOG_ADDR_TYPE_UNREGISTERED] = unregistered_log_addrs,
+	};
+	struct cec_data data;
+	u32 claimed_addrs = 0;
+	int i, j;
+	int err;
+
+	if (adap->phys_addr) {
+		/* The TV functionality can only map to physical address 0.
+		   For any other address, try the Specific functionality
+		   instead as per the spec. */
+		for (i = 0; i < log_addrs->num_log_addrs; i++)
+			if (log_addrs->log_addr_type[i] == CEC_LOG_ADDR_TYPE_TV)
+				log_addrs->log_addr_type[i] =
+						CEC_LOG_ADDR_TYPE_SPECIFIC;
+	}
+
+	memcpy(adap->prim_device, log_addrs->primary_device_type,
+			log_addrs->num_log_addrs);
+	dprintk("physical address: %x.%x.%x.%x, claim %d logical addresses\n",
+			cec_phys_addr_exp(adap->phys_addr),
+			log_addrs->num_log_addrs);
+	adap->num_log_addrs = 0;
+	adap->state = CEC_ADAP_STATE_IDLE;
+
+	/* TODO: remember last used logical addr type to achieve
+	   faster logical address polling by trying that one first.
+	 */
+	for (i = 0; i < log_addrs->num_log_addrs; i++) {
+		const u8 *la_list = type2addrs[log_addrs->log_addr_type[i]];
+
+		if (kthread_should_stop())
+			return -EINTR;
+
+		for (j = 0; la_list[j] != CEC_LOG_ADDR_INVALID; j++) {
+			u8 log_addr = la_list[j];
+
+			if (claimed_addrs & (1 << log_addr))
+				continue;
+
+			/* Send polling message */
+			data.msg.len = 1;
+			data.msg.msg[0] = 0xf0 | log_addr;
+			data.msg.reply = 0;
+			err = cec_transmit_msg(adap, &data, true);
+			if (err)
+				return err;
+			if (data.msg.status == CEC_TX_STATUS_RETRY_TIMEOUT) {
+				/* Message not acknowledged, so this logical
+				   address is free to use. */
+				claimed_addrs |= 1 << log_addr;
+				adap->log_addr[adap->num_log_addrs++] =
+								log_addr;
+				log_addrs->log_addr[i] = log_addr;
+				err = adap->adap_log_addr(adap, log_addr);
+				dprintk("claim addr %d (%d)\n", log_addr,
+							adap->prim_device[i]);
+				if (err)
+					return err;
+				cec_report_phys_addr(adap, log_addr);
+				if (adap->claimed_log_addr)
+					adap->claimed_log_addr(adap, i);
+				break;
+			}
+		}
+	}
+	if (adap->num_log_addrs == 0) {
+		if (log_addrs->num_log_addrs > 1)
+			dprintk("could not claim last %d addresses\n",
+				log_addrs->num_log_addrs - 1);
+		adap->log_addr[adap->num_log_addrs++] = 15;
+		log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_UNREGISTERED;
+		log_addrs->log_addr[0] = 15;
+		log_addrs->num_log_addrs = 1;
+		err = adap->adap_log_addr(adap, 15);
+		dprintk("claim addr %d (%d)\n", 15, adap->prim_device[0]);
+		if (err)
+			return err;
+		cec_report_phys_addr(adap, 15);
+		if (adap->claimed_log_addr)
+			adap->claimed_log_addr(adap, 0);
+	}
+	return 0;
+}
+
+static int cec_config_thread_func(void *arg)
+{
+	struct cec_log_addrs_int *cla_int = arg;
+	int err;
+
+	cla_int->err = err = cec_config_log_addrs(cla_int->adap,
+						  &cla_int->log_addrs);
+	cla_int->adap->kthread_config = NULL;
+	if (cla_int->free_on_exit)
+		kfree(cla_int);
+	else
+		complete(&cla_int->c);
+	return err;
+}
+
+int cec_claim_log_addrs(struct cec_adapter *adap,
+			struct cec_log_addrs *log_addrs, bool block)
+{
+	struct cec_log_addrs_int *cla_int;
+	int i;
+
+	if (adap->state == CEC_ADAP_STATE_DISABLED)
+		return -EINVAL;
+
+	if (log_addrs->num_log_addrs > CEC_MAX_LOG_ADDRS)
+		return -EINVAL;
+	if (log_addrs->num_log_addrs == 0) {
+		adap->num_log_addrs = 0;
+		adap->state = CEC_ADAP_STATE_IDLE;
+		return 0;
+	}
+	if (log_addrs->cec_version != CEC_VERSION_1_4B &&
+	    log_addrs->cec_version != CEC_VERSION_2_0)
+		return -EINVAL;
+	if (log_addrs->num_log_addrs > 1)
+		for (i = 0; i < log_addrs->num_log_addrs; i++)
+			if (log_addrs->log_addr_type[i] ==
+					CEC_LOG_ADDR_TYPE_UNREGISTERED)
+				return -EINVAL;
+	for (i = 0; i < log_addrs->num_log_addrs; i++) {
+		if (log_addrs->primary_device_type[i] >
+						CEC_PRIM_DEVTYPE_VIDEOPROC)
+			return -EINVAL;
+		if (log_addrs->primary_device_type[i] == 2)
+			return -EINVAL;
+		if (log_addrs->log_addr_type[i] >
+						CEC_LOG_ADDR_TYPE_UNREGISTERED)
+			return -EINVAL;
+	}
+
+	/* For phys addr 0xffff only the Unregistered functionality is
+	   allowed. */
+	if (adap->phys_addr == 0xffff &&
+	    (log_addrs->num_log_addrs > 1 ||
+	     log_addrs->log_addr_type[0] != CEC_LOG_ADDR_TYPE_UNREGISTERED))
+		return -EINVAL;
+
+	cla_int = kzalloc(sizeof(*cla_int), GFP_KERNEL);
+	if (cla_int == NULL)
+		return -ENOMEM;
+	init_completion(&cla_int->c);
+	cla_int->free_on_exit = !block;
+	cla_int->adap = adap;
+	cla_int->log_addrs = *log_addrs;
+	adap->kthread_config = kthread_run(cec_config_thread_func, cla_int,
+							"cec_log_addrs");
+	if (block) {
+		wait_for_completion(&cla_int->c);
+		*log_addrs = cla_int->log_addrs;
+		kfree(cla_int);
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cec_claim_log_addrs);
+
+static unsigned int cec_poll(struct file *filp,
+			       struct poll_table_struct *poll)
+{
+	struct cec_devnode *cecdev = cec_devnode_data(filp);
+	struct cec_adapter *adap = to_cec_adapter(cecdev);
+	unsigned res = 0;
+
+	if (!cec_devnode_is_registered(cecdev))
+		return POLLERR | POLLHUP;
+	mutex_lock(&adap->lock);
+	if (adap->tx_qcount < CEC_TX_QUEUE_SZ)
+		res |= POLLOUT | POLLWRNORM;
+	if (adap->rx_qcount)
+		res |= POLLIN | POLLRDNORM;
+	if (adap->ev_qcount)
+		res |= POLLPRI;
+	poll_wait(filp, &adap->waitq, poll);
+	mutex_unlock(&adap->lock);
+	return res;
+}
+
+static long cec_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	struct cec_devnode *cecdev = cec_devnode_data(filp);
+	struct cec_adapter *adap = to_cec_adapter(cecdev);
+	void __user *parg = (void __user *)arg;
+	int err;
+
+	if (!cec_devnode_is_registered(cecdev))
+		return -EIO;
+
+	switch (cmd) {
+	case CEC_G_CAPS: {
+		struct cec_caps caps;
+
+		caps.available_log_addrs = 3;
+		caps.capabilities = adap->capabilities;
+		caps.version = adap->version;
+		caps.vendor_id = adap->vendor_id;
+		if (copy_to_user(parg, &caps, sizeof(caps)))
+			return -EFAULT;
+		break;
+	}
+
+	case CEC_TRANSMIT: {
+		struct cec_data data;
+
+		if (!(adap->capabilities & CEC_CAP_TRANSMIT))
+			return -ENOTTY;
+		if (copy_from_user(&data.msg, parg, sizeof(data.msg)))
+			return -EFAULT;
+		err = cec_transmit_msg(adap, &data,
+						!(filp->f_flags & O_NONBLOCK));
+		if (err)
+			return err;
+		if (copy_to_user(parg, &data.msg, sizeof(data.msg)))
+			return -EFAULT;
+		break;
+	}
+
+	case CEC_RECEIVE: {
+		struct cec_data data;
+
+		if (!(adap->capabilities & CEC_CAP_RECEIVE))
+			return -ENOTTY;
+		if (copy_from_user(&data.msg, parg, sizeof(data.msg)))
+			return -EFAULT;
+		err = cec_receive_msg(adap, &data.msg,
+						!(filp->f_flags & O_NONBLOCK));
+		if (err)
+			return err;
+		if (copy_to_user(parg, &data.msg, sizeof(data.msg)))
+			return -EFAULT;
+		break;
+	}
+
+	case CEC_G_EVENT: {
+		struct cec_event ev;
+
+		mutex_lock(&adap->lock);
+		err = -EAGAIN;
+		if (adap->ev_qcount) {
+			err = 0;
+			ev = adap->ev_queue[adap->ev_qstart];
+			adap->ev_qstart = (adap->ev_qstart + 1) %
+								CEC_EV_QUEUE_SZ;
+			adap->ev_qcount--;
+		}
+		mutex_unlock(&adap->lock);
+		if (err)
+			return err;
+		if (copy_to_user((void __user *)arg, &ev, sizeof(ev)))
+			return -EFAULT;
+		break;
+	}
+
+	case CEC_G_ADAP_STATE: {
+		u32 state = adap->state != CEC_ADAP_STATE_DISABLED;
+
+		if (copy_to_user(parg, &state, sizeof(state)))
+			return -EFAULT;
+		break;
+	}
+
+	case CEC_S_ADAP_STATE: {
+		u32 state;
+
+		if (!(adap->capabilities & CEC_CAP_STATE))
+			return -ENOTTY;
+		if (copy_from_user(&state, parg, sizeof(state)))
+			return -EFAULT;
+		if (!state && adap->state == CEC_ADAP_STATE_DISABLED)
+			return 0;
+		if (state && adap->state != CEC_ADAP_STATE_DISABLED)
+			return 0;
+		cec_enable(adap, !!state);
+		break;
+	}
+
+	case CEC_G_ADAP_PHYS_ADDR:
+		if (copy_to_user(parg, &adap->phys_addr,
+						sizeof(adap->phys_addr)))
+			return -EFAULT;
+		break;
+
+	case CEC_S_ADAP_PHYS_ADDR: {
+		u16 phys_addr;
+
+		if (!(adap->capabilities & CEC_CAP_PHYS_ADDR))
+			return -ENOTTY;
+		if (copy_from_user(&phys_addr, parg, sizeof(phys_addr)))
+			return -EFAULT;
+		adap->phys_addr = phys_addr;
+		break;
+	}
+
+	case CEC_G_ADAP_LOG_ADDRS: {
+		struct cec_log_addrs log_addrs;
+
+		log_addrs.cec_version = adap->version;
+		log_addrs.num_log_addrs = adap->num_log_addrs;
+		memcpy(log_addrs.primary_device_type, adap->prim_device,
+							CEC_MAX_LOG_ADDRS);
+		memcpy(log_addrs.log_addr_type, adap->log_addr_type,
+							CEC_MAX_LOG_ADDRS);
+		memcpy(log_addrs.log_addr, adap->log_addr,
+							CEC_MAX_LOG_ADDRS);
+
+		if (copy_to_user(parg, &log_addrs, sizeof(log_addrs)))
+			return -EFAULT;
+		break;
+	}
+
+	case CEC_S_ADAP_LOG_ADDRS: {
+		struct cec_log_addrs log_addrs;
+
+		if (!(adap->capabilities & CEC_CAP_LOG_ADDRS))
+			return -ENOTTY;
+		if (copy_from_user(&log_addrs, parg, sizeof(log_addrs)))
+			return -EFAULT;
+		err = cec_claim_log_addrs(adap, &log_addrs, true);
+		if (err)
+			return err;
+
+		if (copy_to_user(parg, &log_addrs, sizeof(log_addrs)))
+			return -EFAULT;
+		break;
+	}
+
+	case CEC_G_VENDOR_ID:
+		if (copy_to_user(parg, &adap->vendor_id,
+						sizeof(adap->vendor_id)))
+			return -EFAULT;
+		break;
+
+	case CEC_S_VENDOR_ID: {
+		u32 vendor_id;
+
+		if (!(adap->capabilities & CEC_CAP_VENDOR_ID))
+			return -ENOTTY;
+		if (copy_from_user(&vendor_id, parg, sizeof(vendor_id)))
+			return -EFAULT;
+		/* Vendori ID is a 24 bit number, so check if the value is
+		 * within the correct range. */
+		if ((vendor_id & 0xff000000) != 0)
+			return -EINVAL;
+		adap->vendor_id = vendor_id;
+		break;
+	}
+
+	default:
+		return -ENOTTY;
+	}
+	return 0;
+}
+
+/* Override for the open function */
+static int cec_open(struct inode *inode, struct file *filp)
+{
+	struct cec_devnode *cecdev;
+
+	/* Check if the cec device is available. This needs to be done with
+	 * the cec_devnode_lock held to prevent an open/unregister race:
+	 * without the lock, the device could be unregistered and freed between
+	 * the cec_devnode_is_registered() and get_device() calls, leading to
+	 * a crash.
+	 */
+	mutex_lock(&cec_devnode_lock);
+	cecdev = container_of(inode->i_cdev, struct cec_devnode, cdev);
+	/* return ENXIO if the cec device has been removed
+	   already or if it is not registered anymore. */
+	if (!cec_devnode_is_registered(cecdev)) {
+		mutex_unlock(&cec_devnode_lock);
+		return -ENXIO;
+	}
+	/* and increase the device refcount */
+	get_device(&cecdev->dev);
+	mutex_unlock(&cec_devnode_lock);
+
+	filp->private_data = cecdev;
+
+	return 0;
+}
+
+/* Override for the release function */
+static int cec_release(struct inode *inode, struct file *filp)
+{
+	struct cec_devnode *cecdev = cec_devnode_data(filp);
+	int ret = 0;
+
+	/* decrease the refcount unconditionally since the release()
+	   return value is ignored. */
+	put_device(&cecdev->dev);
+	filp->private_data = NULL;
+	return ret;
+}
+
+static const struct file_operations cec_devnode_fops = {
+	.owner = THIS_MODULE,
+	.open = cec_open,
+	.unlocked_ioctl = cec_ioctl,
+	.release = cec_release,
+	.poll = cec_poll,
+	.llseek = no_llseek,
+};
+
+/**
+ * cec_devnode_register - register a cec device node
+ * @cecdev: cec device node structure we want to register
+ *
+ * The registration code assigns minor numbers and registers the new device node
+ * with the kernel. An error is returned if no free minor number can be found,
+ * or if the registration of the device node fails.
+ *
+ * Zero is returned on success.
+ *
+ * Note that if the cec_devnode_register call fails, the release() callback of
+ * the cec_devnode structure is *not* called, so the caller is responsible for
+ * freeing any data.
+ */
+static int __must_check cec_devnode_register(struct cec_devnode *cecdev,
+		struct module *owner)
+{
+	int minor;
+	int ret;
+
+	/* Part 1: Find a free minor number */
+	mutex_lock(&cec_devnode_lock);
+	minor = find_next_zero_bit(cec_devnode_nums, CEC_NUM_DEVICES, 0);
+	if (minor == CEC_NUM_DEVICES) {
+		mutex_unlock(&cec_devnode_lock);
+		pr_err("could not get a free minor\n");
+		return -ENFILE;
+	}
+
+	set_bit(minor, cec_devnode_nums);
+	mutex_unlock(&cec_devnode_lock);
+
+	cecdev->minor = minor;
+
+	/* Part 2: Initialize and register the character device */
+	cdev_init(&cecdev->cdev, &cec_devnode_fops);
+	cecdev->cdev.owner = owner;
+
+	ret = cdev_add(&cecdev->cdev, MKDEV(MAJOR(cec_dev_t), cecdev->minor),
+									1);
+	if (ret < 0) {
+		pr_err("%s: cdev_add failed\n", __func__);
+		goto error;
+	}
+
+	/* Part 3: Register the cec device */
+	cecdev->dev.bus = &cec_bus_type;
+	cecdev->dev.devt = MKDEV(MAJOR(cec_dev_t), cecdev->minor);
+	cecdev->dev.release = cec_devnode_release;
+	if (cecdev->parent)
+		cecdev->dev.parent = cecdev->parent;
+	dev_set_name(&cecdev->dev, "cec%d", cecdev->minor);
+	ret = device_register(&cecdev->dev);
+	if (ret < 0) {
+		pr_err("%s: device_register failed\n", __func__);
+		goto error;
+	}
+
+	/* Part 4: Activate this minor. The char device can now be used. */
+	set_bit(CEC_FLAG_REGISTERED, &cecdev->flags);
+
+	return 0;
+
+error:
+	cdev_del(&cecdev->cdev);
+	clear_bit(cecdev->minor, cec_devnode_nums);
+	return ret;
+}
+
+/**
+ * cec_devnode_unregister - unregister a cec device node
+ * @cecdev: the device node to unregister
+ *
+ * This unregisters the passed device. Future open calls will be met with
+ * errors.
+ *
+ * This function can safely be called if the device node has never been
+ * registered or has already been unregistered.
+ */
+static void cec_devnode_unregister(struct cec_devnode *cecdev)
+{
+	/* Check if cecdev was ever registered at all */
+	if (!cec_devnode_is_registered(cecdev))
+		return;
+
+	mutex_lock(&cec_devnode_lock);
+	clear_bit(CEC_FLAG_REGISTERED, &cecdev->flags);
+	mutex_unlock(&cec_devnode_lock);
+	device_unregister(&cecdev->dev);
+}
+
+int cec_create_adapter(struct cec_adapter *adap, const char *name, u32 caps)
+{
+	int res = 0;
+
+	adap->state = CEC_ADAP_STATE_DISABLED;
+	adap->name = name;
+	adap->phys_addr = 0xffff;
+	adap->capabilities = caps;
+	adap->version = CEC_VERSION_1_4B;
+	mutex_init(&adap->lock);
+	adap->kthread = kthread_run(cec_thread_func, adap, name);
+	init_waitqueue_head(&adap->kthread_waitq);
+	init_waitqueue_head(&adap->waitq);
+	if (IS_ERR(adap->kthread)) {
+		pr_err("cec-%s: kernel_thread() failed\n", name);
+		return PTR_ERR(adap->kthread);
+	}
+	if (caps) {
+		res = cec_devnode_register(&adap->devnode, adap->owner);
+		if (res)
+			kthread_stop(adap->kthread);
+	}
+	adap->recv_notifier = cec_receive_notify;
+
+	/* Prepare the RC input device */
+	adap->rc = rc_allocate_device();
+	if (!adap->rc) {
+		pr_err("cec-%s: failed to allocate memory for rc_dev\n", name);
+		cec_devnode_unregister(&adap->devnode);
+		kthread_stop(adap->kthread);
+		return -ENOMEM;
+	}
+
+	snprintf(adap->input_name, sizeof(adap->input_name), "RC for %s", name);
+	snprintf(adap->input_phys, sizeof(adap->input_phys), "%s/input0", name);
+	strncpy(adap->input_drv, name, sizeof(adap->input_drv));
+
+	adap->rc->input_name = adap->input_name;
+	adap->rc->input_phys = adap->input_phys;
+	adap->rc->dev.parent = &adap->devnode.dev;
+	adap->rc->driver_name = adap->input_drv;
+	adap->rc->driver_type = RC_DRIVER_CEC;
+	adap->rc->allowed_protocols = RC_BIT_CEC;
+	adap->rc->priv = adap;
+	adap->rc->map_name = RC_MAP_CEC;
+	adap->rc->timeout = MS_TO_NS(100);
+
+	res = rc_register_device(adap->rc);
+
+	if (res) {
+		pr_err("cec-%s: failed to prepare input device\n", name);
+		cec_devnode_unregister(&adap->devnode);
+		rc_free_device(adap->rc);
+		kthread_stop(adap->kthread);
+	}
+
+	return res;
+}
+EXPORT_SYMBOL_GPL(cec_create_adapter);
+
+void cec_delete_adapter(struct cec_adapter *adap)
+{
+	if (adap->kthread == NULL)
+		return;
+	kthread_stop(adap->kthread);
+	if (adap->kthread_config)
+		kthread_stop(adap->kthread_config);
+	adap->state = CEC_ADAP_STATE_DISABLED;
+	if (cec_devnode_is_registered(&adap->devnode))
+		cec_devnode_unregister(&adap->devnode);
+}
+EXPORT_SYMBOL_GPL(cec_delete_adapter);
+
+/*
+ *	Initialise cec for linux
+ */
+static int __init cec_devnode_init(void)
+{
+	int ret;
+
+	pr_info("Linux cec interface: v0.10\n");
+	ret = alloc_chrdev_region(&cec_dev_t, 0, CEC_NUM_DEVICES,
+				  CEC_NAME);
+	if (ret < 0) {
+		pr_warn("cec: unable to allocate major\n");
+		return ret;
+	}
+
+	ret = bus_register(&cec_bus_type);
+	if (ret < 0) {
+		unregister_chrdev_region(cec_dev_t, CEC_NUM_DEVICES);
+		pr_warn("cec: bus_register failed\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static void __exit cec_devnode_exit(void)
+{
+	bus_unregister(&cec_bus_type);
+	unregister_chrdev_region(cec_dev_t, CEC_NUM_DEVICES);
+}
+
+subsys_initcall(cec_devnode_init);
+module_exit(cec_devnode_exit)
+
+MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>");
+MODULE_DESCRIPTION("Device node registration for cec drivers");
+MODULE_LICENSE("GPL");
diff --git a/include/media/cec.h b/include/media/cec.h
new file mode 100644
index 0000000..df3b9e93
--- /dev/null
+++ b/include/media/cec.h
@@ -0,0 +1,140 @@
+#ifndef _CEC_DEVNODE_H
+#define _CEC_DEVNODE_H
+
+#include <linux/poll.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/cdev.h>
+#include <linux/kthread.h>
+#include <linux/cec.h>
+#include <media/rc-core.h>
+
+#define cec_phys_addr_exp(pa) \
+	((pa) >> 12), ((pa) >> 8) & 0xf, ((pa) >> 4) & 0xf, (pa) & 0xf
+
+/*
+ * Flag to mark the cec_devnode struct as registered. Drivers must not touch
+ * this flag directly, it will be set and cleared by cec_devnode_register and
+ * cec_devnode_unregister.
+ */
+#define CEC_FLAG_REGISTERED	0
+
+/**
+ * struct cec_devnode - cec device node
+ * @parent:	parent device
+ * @minor:	device node minor number
+ * @flags:	flags, combination of the CEC_FLAG_* constants
+ *
+ * This structure represents a cec-related device node.
+ *
+ * The @parent is a physical device. It must be set by core or device drivers
+ * before registering the node.
+ */
+struct cec_devnode {
+	/* sysfs */
+	struct device dev;		/* cec device */
+	struct cdev cdev;		/* character device */
+	struct device *parent;		/* device parent */
+
+	/* device info */
+	int minor;
+	unsigned long flags;		/* Use bitops to access flags */
+
+	/* callbacks */
+	void (*release)(struct cec_devnode *cecdev);
+};
+
+static inline int cec_devnode_is_registered(struct cec_devnode *cecdev)
+{
+	return test_bit(CEC_FLAG_REGISTERED, &cecdev->flags);
+}
+
+struct cec_adapter;
+struct cec_data;
+
+typedef int (*cec_notify)(struct cec_adapter *adap, struct cec_data *data,
+			  void *priv);
+typedef int (*cec_recv_notify)(struct cec_adapter *adap, struct cec_msg *msg);
+
+struct cec_data {
+	struct cec_msg msg;
+	cec_notify func;
+	void *priv;
+};
+
+/* Unconfigured state */
+#define CEC_ADAP_STATE_DISABLED		0
+#define CEC_ADAP_STATE_UNCONF		1
+#define CEC_ADAP_STATE_IDLE		2
+#define CEC_ADAP_STATE_TRANSMITTING	3
+#define CEC_ADAP_STATE_WAIT		4
+#define CEC_ADAP_STATE_RECEIVED		5
+
+#define CEC_TX_QUEUE_SZ	(4)
+#define CEC_RX_QUEUE_SZ	(4)
+#define CEC_EV_QUEUE_SZ	(16)
+
+struct cec_adapter {
+	struct module *owner;
+	const char *name;
+	struct cec_devnode devnode;
+	struct mutex lock;
+	struct rc_dev *rc;
+
+	struct cec_data tx_queue[CEC_TX_QUEUE_SZ];
+	u8 tx_qstart, tx_qcount;
+
+	struct cec_msg rx_queue[CEC_RX_QUEUE_SZ];
+	u8 rx_qstart, rx_qcount;
+
+	struct cec_event ev_queue[CEC_EV_QUEUE_SZ];
+	u8 ev_qstart, ev_qcount;
+
+	cec_recv_notify recv_notifier;
+	struct task_struct *kthread_config;
+
+	struct task_struct *kthread;
+	wait_queue_head_t kthread_waitq;
+	wait_queue_head_t waitq;
+
+	u8 state;
+	u32 capabilities;
+	u16 phys_addr;
+	u32 vendor_id;
+	u8 version;
+	u8 num_log_addrs;
+	u8 prim_device[CEC_MAX_LOG_ADDRS];
+	u8 log_addr_type[CEC_MAX_LOG_ADDRS];
+	u8 log_addr[CEC_MAX_LOG_ADDRS];
+	u8 promiscuous;
+
+	char input_name[32];
+	char input_phys[32];
+	char input_drv[32];
+
+	int (*adap_enable)(struct cec_adapter *adap, bool enable);
+	int (*adap_log_addr)(struct cec_adapter *adap, u8 logical_addr);
+	int (*adap_transmit)(struct cec_adapter *adap, struct cec_msg *msg);
+	void (*adap_transmit_timed_out)(struct cec_adapter *adap);
+
+	void (*claimed_log_addr)(struct cec_adapter *adap, u8 idx);
+	int (*received)(struct cec_adapter *adap, struct cec_msg *msg);
+};
+
+#define to_cec_adapter(node) container_of(node, struct cec_adapter, devnode)
+
+int cec_create_adapter(struct cec_adapter *adap, const char *name, u32 caps);
+void cec_delete_adapter(struct cec_adapter *adap);
+int cec_transmit_msg(struct cec_adapter *adap, struct cec_data *data,
+		     bool block);
+int cec_receive_msg(struct cec_adapter *adap, struct cec_msg *msg, bool block);
+void cec_post_event(struct cec_adapter *adap, u32 event);
+int cec_claim_log_addrs(struct cec_adapter *adap,
+			struct cec_log_addrs *log_addrs, bool block);
+int cec_enable(struct cec_adapter *adap, bool enable);
+
+/* Called by the adapter */
+void cec_transmit_done(struct cec_adapter *adap, u32 status);
+void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg);
+
+#endif /* _CEC_DEVNODE_H */
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index 4842a98..5854cfd 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -81,6 +81,7 @@ header-y += capi.h
 header-y += cciss_defs.h
 header-y += cciss_ioctl.h
 header-y += cdrom.h
+header-y += cec.h
 header-y += cgroupstats.h
 header-y += chio.h
 header-y += cm4000_cs.h
diff --git a/include/uapi/linux/cec.h b/include/uapi/linux/cec.h
new file mode 100644
index 0000000..bb6d66c
--- /dev/null
+++ b/include/uapi/linux/cec.h
@@ -0,0 +1,303 @@
+#ifndef _CEC_H
+#define _CEC_H
+
+#include <linux/types.h>
+
+struct cec_time {
+	__u64 sec;
+	__u64 nsec;
+};
+
+struct cec_msg {
+	struct cec_time ts;
+	__u32 len;
+	__u32 status;
+	__u32 timeout;
+	/* timeout (in ms) is used to timeout CEC_RECEIVE.
+	   Set to 0 if you want to wait forever. */
+	__u8  msg[16];
+	__u8  reply;
+	/* If non-zero, then wait for a reply with this opcode.
+	   If there was an error when sending the msg or FeatureAbort
+	   was returned, then reply is set to 0.
+	   If reply is non-zero upon return, then len/msg are set to
+	   the received message.
+	   If reply is zero upon return and status has the
+	   CEC_TX_STATUS_FEATURE_ABORT bit set, then len/msg are set to the
+	   received feature abort message.
+	   If reply is zero upon return and status has the
+	   CEC_TX_STATUS_REPLY_TIMEOUT
+	   bit set, then no reply was seen at all.
+	   This field is ignored with CEC_RECEIVE.
+	   If reply is non-zero for CEC_TRANSMIT and the message is a broadcast,
+	   then -EINVAL is returned.
+	   if reply is non-zero, then timeout is set to 1000 (the required
+	   maximum response time).
+	 */
+	__u8 reserved[31];
+};
+
+static inline __u8 cec_msg_initiator(const struct cec_msg *msg)
+{
+	return msg->msg[0] >> 4;
+}
+
+static inline __u8 cec_msg_destination(const struct cec_msg *msg)
+{
+	return msg->msg[0] & 0xf;
+}
+
+static inline bool cec_msg_is_broadcast(const struct cec_msg *msg)
+{
+	return (msg->msg[0] & 0xf) == 0xf;
+}
+
+/* cec status field */
+#define CEC_TX_STATUS_OK            (0)
+#define CEC_TX_STATUS_ARB_LOST      (1 << 0)
+#define CEC_TX_STATUS_RETRY_TIMEOUT (1 << 1)
+#define CEC_TX_STATUS_FEATURE_ABORT (1 << 2)
+#define CEC_TX_STATUS_REPLY_TIMEOUT (1 << 3)
+#define CEC_RX_STATUS_READY         (0)
+
+#define CEC_LOG_ADDR_INVALID 0xff
+
+/* The maximum number of logical addresses one device can be assigned to.
+ * The CEC 2.0 spec allows for only 2 logical addresses at the moment. The
+ * Analog Devices CEC hardware supports 3. So let's go wild and go for 4. */
+#define CEC_MAX_LOG_ADDRS 4
+
+/* The "Primary Device Type" */
+#define CEC_PRIM_DEVTYPE_TV		0
+#define CEC_PRIM_DEVTYPE_RECORD		1
+#define CEC_PRIM_DEVTYPE_TUNER		3
+#define CEC_PRIM_DEVTYPE_PLAYBACK	4
+#define CEC_PRIM_DEVTYPE_AUDIOSYSTEM	5
+#define CEC_PRIM_DEVTYPE_SWITCH		6
+#define CEC_PRIM_DEVTYPE_VIDEOPROC	7
+
+/* The "All Device Types" flags (CEC 2.0) */
+#define CEC_FL_ALL_DEVTYPE_TV		(1 << 7)
+#define CEC_FL_ALL_DEVTYPE_RECORD	(1 << 6)
+#define CEC_FL_ALL_DEVTYPE_TUNER	(1 << 5)
+#define CEC_FL_ALL_DEVTYPE_PLAYBACK	(1 << 4)
+#define CEC_FL_ALL_DEVTYPE_AUDIOSYSTEM	(1 << 3)
+#define CEC_FL_ALL_DEVTYPE_SWITCH	(1 << 2)
+/* And if you wondering what happened to VIDEOPROC devices: those should
+ * be mapped to a SWITCH. */
+
+/* The logical address types that the CEC device wants to claim */
+#define CEC_LOG_ADDR_TYPE_TV		0
+#define CEC_LOG_ADDR_TYPE_RECORD	1
+#define CEC_LOG_ADDR_TYPE_TUNER		2
+#define CEC_LOG_ADDR_TYPE_PLAYBACK	3
+#define CEC_LOG_ADDR_TYPE_AUDIOSYSTEM	4
+#define CEC_LOG_ADDR_TYPE_SPECIFIC	5
+#define CEC_LOG_ADDR_TYPE_UNREGISTERED	6
+/* Switches should use UNREGISTERED.
+ * Video processors should use SPECIFIC. */
+
+/* The CEC version */
+#define CEC_VERSION_1_4B		5
+#define CEC_VERSION_2_0			6
+
+struct cec_event {
+	struct cec_time ts;
+	__u32 event;
+	__u8 reserved[4];
+};
+
+/* The CEC state */
+#define CEC_STATE_DISABLED		0
+#define CEC_STATE_ENABLED		1
+
+/* Userspace has to configure the adapter state (enable/disable) */
+#define CEC_CAP_STATE		(1 << 0)
+/* Userspace has to configure the physical address */
+#define CEC_CAP_PHYS_ADDR	(1 << 1)
+/* Userspace has to configure the logical addresses */
+#define CEC_CAP_LOG_ADDRS	(1 << 2)
+/* Userspace can transmit messages */
+#define CEC_CAP_TRANSMIT	(1 << 3)
+/* Userspace can receive messages */
+#define CEC_CAP_RECEIVE		(1 << 4)
+/* Userspace has to configure the vendor id */
+#define CEC_CAP_VENDOR_ID	(1 << 5)
+/* The hardware has the possibility to work in the promiscuous mode */
+#define CEC_CAP_PROMISCUOUS	(1 << 6)
+
+struct cec_caps {
+	__u32 available_log_addrs;
+	__u32 capabilities;
+	__u32 vendor_id;
+	__u8  version;
+	__u8  reserved[11];
+};
+
+struct cec_log_addrs {
+	__u8 cec_version;
+	__u8 num_log_addrs;
+	__u8 primary_device_type[CEC_MAX_LOG_ADDRS];
+	__u8 log_addr_type[CEC_MAX_LOG_ADDRS];
+	__u8 log_addr[CEC_MAX_LOG_ADDRS];
+
+	/* CEC 2.0 */
+	__u8 all_device_types;
+	__u8 features[CEC_MAX_LOG_ADDRS][12];
+
+	__u8 reserved[9];
+};
+
+/* Commands */
+
+/* One Touch Play Feature */
+#define CEC_OP_ACTIVE_SOURCE			0x82
+#define CEC_OP_IMAGE_VIEW_ON			0x04
+#define CEC_OP_TEXT_VIEW_ON			0x0d
+
+/* Routing Control Feature */
+#define CEC_OP_ACTIVE_SOURCE			0x82
+#define CEC_OP_INACTIVE_SOURCE			0x9d
+#define CEC_OP_REQUEST_ACTIVE_SOURCE		0x85
+#define CEC_OP_ROUTING_CHANGE			0x80
+#define CEC_OP_ROUTING_INFORMATION		0x81
+#define CEC_OP_SET_STREAM_PATH			0x86
+
+/* Standby Feature */
+#define CEC_OP_STANDBY				0x36
+
+/* One Touch Record Feature */
+#define CEC_OP_RECORD_OFF			0x0b
+#define CEC_OP_RECORD_ON			0x09
+#define CEC_OP_RECORD_STATUS			0x0a
+#define CEC_OP_RECORD_TV_SCREEN			0x0f
+
+/* Timer Programming Feature */
+#define CEC_OP_CLEAR_ANALOGUE_TIMER		0x33
+#define CEC_OP_CLEAR_DIGITAL_TIMER		0x99
+#define CEC_OP_CLEAR_EXT_TIMER			0xa1
+#define CEC_OP_SET_ANALOGUE_TIMER		0x34
+#define CEC_OP_SET_DIGITAL_TIMER		0x97
+#define CEC_OP_SET_EXT_TIMER			0xa2
+#define CEC_OP_SET_EXT_PROGRAM_TIMER		0x67
+#define CEC_OP_TIMER_CLEARED_STATUS		0x43
+#define CEC_OP_TIMER_STATUS			0x35
+
+/* System Information Feature */
+#define CEC_OP_CEC_VERSION			0x9e
+#define CEC_OP_GET_CEC_VERSION			0x9f
+#define CEC_OP_GIVE_PHYSICAL_ADDR		0x83
+#define CEC_OP_GET_MENU_LANGUAGE		0x91
+#define CEC_OP_REPORT_PHYSICAL_ADDR		0x84
+#define CEC_OP_SET_MENU_LANGUAGE		0x32
+
+/* Deck Control Feature */
+#define CEC_OP_DECK_CONTROL			0x42
+#define CEC_OP_DECK_STATUS			0x1b
+#define CEC_OP_GIVE_DECK_STATUS			0x1a
+#define CEC_OP_PLAY				0x41
+
+/* Tuner Control Feature */
+#define CEC_OP_GIVE_TUNER_DEVICE_STATUS		0x08
+#define CEC_OP_SELECT_ANALOGUE_SERVICE		0x92
+#define CEC_OP_SELECT_DIGITAL_SERVICE		0x93
+#define CEC_OP_TUNER_DEVICE_STATUS		0x07
+#define CEC_OP_TUNER_STEP_DECREMENT		0x06
+#define CEC_OP_TUNER_STEP_INCREMENT		0x05
+
+/* Vendor Specific Commands Feature */
+#define CEC_OP_CEC_VERSION			0x9e
+#define CEC_OP_DEVICE_VENDOR_ID			0x87
+#define CEC_OP_GET_CEC_VERSION			0x9f
+#define CEC_OP_GIVE_DEVICE_VENDOR_ID		0x8c
+#define CEC_OP_VENDOR_COMMAND			0x89
+#define CEC_OP_VENDOR_COMMAND_WITH_ID		0xa0
+#define CEC_OP_VENDOR_REMOTE_BUTTON_DOWN	0x8a
+#define CEC_OP_VENDOR_REMOTE_BUTTON_UP		0x8b
+
+/* OSD Display Feature */
+#define CEC_OP_SET_OSD_STRING			0x64
+
+/* Device OSD Transfer Feature */
+#define CEC_OP_GIVE_OSD_NAME			0x46
+#define CEC_OP_SET_OSD_NAME			0x47
+
+/* Device Menu Control Feature */
+#define CEC_OP_MENU_REQUEST			0x8d
+#define CEC_OP_MENU_STATUS			0x8e
+#define CEC_OP_USER_CONTROL_PRESSED		0x44
+#define CEC_OP_USER_CONTROL_RELEASED		0x45
+
+/* Power Status Feature */
+#define CEC_OP_GIVE_DEVICE_POWER_STATUS		0x8f
+#define CEC_OP_REPORT_POWER_STATUS		0x90
+#define CEC_OP_FEATURE_ABORT			0x00
+#define CEC_OP_ABORT				0xff
+
+/* System Audio Control Feature */
+#define CEC_OP_GIVE_AUDIO_STATUS		0x71
+#define CEC_OP_GIVE_SYSTEM_AUDIO_MODE_STATUS	0x7d
+#define CEC_OP_REPORT_AUDIO_STATUS		0x7a
+#define CEC_OP_SET_SYSTEM_AUDIO_MODE		0x72
+#define CEC_OP_SYSTEM_AUDIO_MODE_REQUEST	0x70
+#define CEC_OP_SYSTEM_AUDIO_MODE_STATUS		0x7e
+
+/* Audio Rate Control Feature */
+#define CEC_OP_SET_AUDIO_RATE			0x9a
+
+/* Events */
+/* Event that occurs when a cable is connected */
+#define CEC_EVENT_CONNECT	1
+/* Event that occurs when all logical addresses were claimed */
+#define CEC_EVENT_READY		2
+/* Event that is sent when the cable is disconnected */
+#define CEC_EVENT_DISCONNECT	3
+
+/* ioctls */
+
+/* issue a CEC command */
+#define CEC_G_CAPS		_IOWR('a', 0, struct cec_caps)
+#define CEC_TRANSMIT		_IOWR('a', 1, struct cec_msg)
+#define CEC_RECEIVE		_IOWR('a', 2, struct cec_msg)
+
+/*
+   Configure the CEC adapter. It sets the device type and which
+   logical types it will try to claim. It will return which
+   logical addresses it could actually claim.
+   An error is returned if the adapter is disabled or if there
+   is no physical address assigned.
+ */
+
+#define CEC_G_ADAP_LOG_ADDRS	_IOR('a', 3, struct cec_log_addrs)
+#define CEC_S_ADAP_LOG_ADDRS	_IOWR('a', 4, struct cec_log_addrs)
+
+/*
+   Enable/disable the adapter. The Set state ioctl may not
+   be available if that is handled internally.
+ */
+#define CEC_G_ADAP_STATE	_IOR('a', 5, __u32)
+#define CEC_S_ADAP_STATE	_IOW('a', 6, __u32)
+
+/*
+   phys_addr is either 0 (if this is the CEC root device)
+   or a valid physical address obtained from the sink's EDID
+   as read by this CEC device (if this is a source device)
+   or a physical address obtained and modified from a sink
+   EDID and used for a sink CEC device.
+   If nothing is connected, then phys_addr is 0xffff.
+   See HDMI 1.4b, section 8.7 (Physical Address).
+
+   The Set ioctl may not be available if that is handled
+   internally.
+ */
+#define CEC_G_ADAP_PHYS_ADDR	_IOR('a', 7, __u16)
+#define CEC_S_ADAP_PHYS_ADDR	_IOW('a', 8, __u16)
+
+#define CEC_G_EVENT		_IOWR('a', 9, struct cec_event)
+/*
+   Read and set the vendor ID of the CEC adapter.
+ */
+#define CEC_G_VENDOR_ID		_IOR('a', 9, __u32)
+#define CEC_S_VENDOR_ID		_IOW('a', 10, __u32)
+
+#endif
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v4 07/10] v4l2-subdev: add HDMI CEC ops
  2015-04-23 13:03 [PATCH v4 00/10] HDMI CEC framework Kamil Debski
                   ` (5 preceding siblings ...)
  2015-04-23 13:03 ` [PATCH v4 06/10] cec: add HDMI CEC framework Kamil Debski
@ 2015-04-23 13:03 ` Kamil Debski
  2015-04-23 13:03 ` [PATCH v4 08/10] cec: adv7604: add cec support Kamil Debski
  2015-04-23 13:03 ` [PATCH v4 09/10] cec: adv7511: " Kamil Debski
  8 siblings, 0 replies; 27+ messages in thread
From: Kamil Debski @ 2015-04-23 13:03 UTC (permalink / raw)
  To: dri-devel, linux-media
  Cc: m.szyprowski, k.debski, mchehab, hverkuil, kyungmin.park, thomas,
	sean, dmitry.torokhov, linux-input, linux-samsung-soc,
	Hans Verkuil

From: Hans Verkuil <hansverk@cisco.com>

Add callbacks to the v4l2_subdev_video_ops.

Signed-off-by: Hans Verkuil <hansverk@cisco.com>
[k.debski@samsung.com: Merged changes from CEC Updates commit by Hans Verkuil]
Signed-off-by: Kamil Debski <k.debski@samsung.com>
---
 include/media/v4l2-subdev.h |    8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
index 2f0a345..9323e10 100644
--- a/include/media/v4l2-subdev.h
+++ b/include/media/v4l2-subdev.h
@@ -40,6 +40,9 @@
 #define V4L2_SUBDEV_IR_TX_NOTIFY		_IOW('v', 1, u32)
 #define V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ	0x00000001
 
+#define V4L2_SUBDEV_CEC_TX_DONE			_IOW('v', 2, u32)
+#define V4L2_SUBDEV_CEC_RX_MSG			_IOW('v', 3, struct cec_msg)
+
 struct v4l2_device;
 struct v4l2_ctrl_handler;
 struct v4l2_event_subscription;
@@ -48,6 +51,7 @@ struct v4l2_subdev;
 struct v4l2_subdev_fh;
 struct tuner_setup;
 struct v4l2_mbus_frame_desc;
+struct cec_msg;
 
 /* decode_vbi_line */
 struct v4l2_decode_vbi_line {
@@ -352,6 +356,10 @@ struct v4l2_subdev_video_ops {
 			     const struct v4l2_mbus_config *cfg);
 	int (*s_rx_buffer)(struct v4l2_subdev *sd, void *buf,
 			   unsigned int *size);
+	int (*cec_enable)(struct v4l2_subdev *sd, bool enable);
+	int (*cec_log_addr)(struct v4l2_subdev *sd, u8 logical_addr);
+	int (*cec_transmit)(struct v4l2_subdev *sd, struct cec_msg *msg);
+	void (*cec_transmit_timed_out)(struct v4l2_subdev *sd);
 };
 
 /*
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v4 08/10] cec: adv7604: add cec support.
  2015-04-23 13:03 [PATCH v4 00/10] HDMI CEC framework Kamil Debski
                   ` (6 preceding siblings ...)
  2015-04-23 13:03 ` [PATCH v4 07/10] v4l2-subdev: add HDMI CEC ops Kamil Debski
@ 2015-04-23 13:03 ` Kamil Debski
  2015-04-23 13:03 ` [PATCH v4 09/10] cec: adv7511: " Kamil Debski
  8 siblings, 0 replies; 27+ messages in thread
From: Kamil Debski @ 2015-04-23 13:03 UTC (permalink / raw)
  To: dri-devel, linux-media
  Cc: m.szyprowski, k.debski, mchehab, hverkuil, kyungmin.park, thomas,
	sean, dmitry.torokhov, linux-input, linux-samsung-soc,
	Hans Verkuil

From: Hans Verkuil <hansverk@cisco.com>

Add CEC support to the adv7604 driver.

Signed-off-by: Hans Verkuil <hansverk@cisco.com>
[k.debski@samsung.com: Merged changes from CEC Updates commit by Hans Verkuil]
[k.debski@samsung.com: add missing methods cec/io_write_and_or]
[k.debski@samsung.com: change adv7604 to adv76xx in added functions]
Signed-off-by: Kamil Debski <k.debski@samsung.com>
---
 drivers/media/i2c/adv7604.c |  207 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 206 insertions(+), 1 deletion(-)

diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
index 60ffcf0..4921276 100644
--- a/drivers/media/i2c/adv7604.c
+++ b/drivers/media/i2c/adv7604.c
@@ -38,6 +38,7 @@
 #include <linux/workqueue.h>
 
 #include <media/adv7604.h>
+#include <media/cec.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-dv-timings.h>
@@ -77,6 +78,8 @@ MODULE_LICENSE("GPL");
 
 #define ADV76XX_OP_SWAP_CB_CR				(1 << 0)
 
+#define ADV76XX_MAX_ADDRS (3)
+
 enum adv76xx_type {
 	ADV7604,
 	ADV7611,
@@ -159,6 +162,10 @@ struct adv76xx_state {
 	u16 spa_port_a[2];
 	struct v4l2_fract aspect_ratio;
 	u32 rgb_quantization_range;
+	u8   cec_addr[ADV76XX_MAX_ADDRS];
+	u8   cec_valid_addrs;
+	bool cec_enabled_adap;
+
 	struct workqueue_struct *work_queues;
 	struct delayed_work delayed_work_enable_hotplug;
 	bool restart_stdi_once;
@@ -424,7 +431,15 @@ static inline int io_write(struct v4l2_subdev *sd, u8 reg, u8 val)
 	return adv_smbus_write_byte_data(state, ADV76XX_PAGE_IO, reg, val);
 }
 
-static inline int io_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
+static inline int io_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask,
+				  u8 val)
+{
+	return io_write(sd, reg, (io_read(sd, reg) & mask) | val);
+}
+
+
+static inline int io_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask,
+				   u8 val)
 {
 	return io_write(sd, reg, (io_read(sd, reg) & ~mask) | val);
 }
@@ -457,6 +472,12 @@ static inline int cec_write(struct v4l2_subdev *sd, u8 reg, u8 val)
 	return adv_smbus_write_byte_data(state, ADV76XX_PAGE_CEC, reg, val);
 }
 
+static inline int cec_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask,
+				   u8 val)
+{
+	return cec_write(sd, reg, (cec_read(sd, reg) & mask) | val);
+}
+
 static inline int infoframe_read(struct v4l2_subdev *sd, u8 reg)
 {
 	struct adv76xx_state *state = to_state(sd);
@@ -1865,6 +1886,183 @@ static int adv76xx_set_format(struct v4l2_subdev *sd,
 	return 0;
 }
 
+static void adv76xx_cec_tx_raw_status(struct v4l2_subdev *sd, u8 tx_raw_status)
+{
+	if ((cec_read(sd, 0x11) & 0x01) == 0) {
+		v4l2_dbg(1, debug, sd, "%s: tx raw: tx disabled\n", __func__);
+		return;
+	}
+
+	if (tx_raw_status & 0x02) {
+		v4l2_dbg(1, debug, sd, "%s: tx raw: arbitration lost\n",
+			 __func__);
+		v4l2_subdev_notify(sd, V4L2_SUBDEV_CEC_TX_DONE,
+				   (void *)CEC_TX_STATUS_ARB_LOST);
+		return;
+	}
+	if (tx_raw_status & 0x04) {
+		v4l2_dbg(1, debug, sd, "%s: tx raw: retry failed\n", __func__);
+		v4l2_subdev_notify(sd, V4L2_SUBDEV_CEC_TX_DONE,
+				   (void *)CEC_TX_STATUS_RETRY_TIMEOUT);
+		return;
+	}
+	if (tx_raw_status & 0x01) {
+		v4l2_dbg(1, debug, sd, "%s: tx raw: ready ok\n", __func__);
+		v4l2_subdev_notify(sd, V4L2_SUBDEV_CEC_TX_DONE,
+				   (void *)CEC_TX_STATUS_OK);
+		return;
+	}
+}
+
+static void adv76xx_cec_isr(struct v4l2_subdev *sd, bool *handled)
+{
+	struct cec_msg msg;
+	u8 cec_irq;
+
+	/* cec controller */
+	cec_irq = io_read(sd, 0x4d) & 0x0f;
+	if (!cec_irq)
+		return;
+
+	v4l2_dbg(1, debug, sd, "%s: cec: irq 0x%x\n", __func__, cec_irq);
+	adv76xx_cec_tx_raw_status(sd, cec_irq);
+	if (cec_irq & 0x08) {
+		msg.len = cec_read(sd, 0x25) & 0x1f;
+		if (msg.len > 16)
+			msg.len = 16;
+
+		if (msg.len) {
+			u8 i;
+
+			for (i = 0; i < msg.len; i++)
+				msg.msg[i] = cec_read(sd, i + 0x15);
+			cec_write(sd, 0x26, 0x01); /* re-enable rx */
+			v4l2_subdev_notify(sd, V4L2_SUBDEV_CEC_RX_MSG, &msg);
+		}
+	}
+
+	/* note: the bit order is swapped between 0x4d and 0x4e */
+	cec_irq = ((cec_irq & 0x08) >> 3) | ((cec_irq & 0x04) >> 1) |
+		  ((cec_irq & 0x02) << 1) | ((cec_irq & 0x01) << 3);
+	io_write(sd, 0x4e, cec_irq);
+
+	if (handled)
+		*handled = true;
+}
+
+static int adv76xx_cec_enable(struct v4l2_subdev *sd, bool enable)
+{
+	struct adv76xx_state *state = to_state(sd);
+
+	if (!state->cec_enabled_adap && enable) {
+		cec_write_and_or(sd, 0x2a, 0xfe, 0x01);	/* power up cec */
+		cec_write(sd, 0x2c, 0x01);	/* cec soft reset */
+		cec_write_and_or(sd, 0x11, 0xfe, 0);  /* initially disable tx */
+		/* enabled irqs: */
+		/* tx: ready */
+		/* tx: arbitration lost */
+		/* tx: retry timeout */
+		/* rx: ready */
+		io_write_and_or(sd, 0x50, 0xf0, 0x0f);
+		cec_write(sd, 0x26, 0x01);            /* enable rx */
+	} else if (state->cec_enabled_adap && !enable) {
+		/* disable cec interrupts */
+		io_write_and_or(sd, 0x50, 0xf0, 0x00);
+		/* disable address mask 1-3 */
+		cec_write_and_or(sd, 0x27, 0x8f, 0x70);
+		/* power down cec section */
+		cec_write_and_or(sd, 0x2a, 0xfe, 0x00);
+		state->cec_valid_addrs = 0;
+	}
+	state->cec_enabled_adap = enable;
+	return 0;
+}
+
+static int adv76xx_cec_log_addr(struct v4l2_subdev *sd, u8 addr)
+{
+	struct adv76xx_state *state = to_state(sd);
+	unsigned i, free_idx = ADV76XX_MAX_ADDRS;
+
+	if (!state->cec_enabled_adap)
+		return -EIO;
+
+	for (i = 0; i < ADV76XX_MAX_ADDRS; i++) {
+		bool is_valid = state->cec_valid_addrs & (1 << i);
+
+		if (free_idx == ADV76XX_MAX_ADDRS && !is_valid)
+			free_idx = i;
+		if (is_valid && state->cec_addr[i] == addr)
+			return 0;
+	}
+	if (i == ADV76XX_MAX_ADDRS) {
+		i = free_idx;
+		if (i == ADV76XX_MAX_ADDRS)
+			return -ENXIO;
+	}
+	state->cec_addr[i] = addr;
+	state->cec_valid_addrs |= 1 << i;
+
+	switch (i) {
+	case 0:
+		/* enable address mask 0 */
+		cec_write_and_or(sd, 0x27, 0xef, 0x10);
+		/* set address for mask 0 */
+		cec_write_and_or(sd, 0x28, 0xf0, addr);
+		break;
+	case 1:
+		/* enable address mask 1 */
+		cec_write_and_or(sd, 0x27, 0xdf, 0x20);
+		/* set address for mask 1 */
+		cec_write_and_or(sd, 0x28, 0x0f, addr << 4);
+		break;
+	case 2:
+		/* enable address mask 2 */
+		cec_write_and_or(sd, 0x27, 0xbf, 0x40);
+		/* set address for mask 1 */
+		cec_write_and_or(sd, 0x29, 0xf0, addr);
+		break;
+	}
+	return 0;
+}
+
+static int adv76xx_cec_transmit(struct v4l2_subdev *sd, struct cec_msg *msg)
+{
+	u8 len = msg->len;
+	unsigned i;
+
+	if (len == 1)
+		/* allow for one retry for polling */
+		cec_write_and_or(sd, 0x12, 0xf8, 1);
+	else
+		/* allow for three retries */
+		cec_write_and_or(sd, 0x12, 0xf8, 3);
+
+	if (len > 16) {
+		v4l2_err(sd, "%s: len exceeded 16 (%d)\n", __func__, len);
+		return -EINVAL;
+	}
+
+	/* write data */
+	for (i = 0; i < len; i++)
+		cec_write(sd, i, msg->msg[i]);
+
+	/* set length (data + header) */
+	cec_write(sd, 0x10, len);
+	/* start transmit, enable tx */
+	cec_write(sd, 0x11, 0x01);
+	/* For some reason sometimes the
+	 * transmit won't start.
+	 * Doing it twice seems to help ?
+	*/
+	cec_write(sd, 0x11, 0x01);
+	return 0;
+}
+
+static void adv76xx_cec_transmit_timed_out(struct v4l2_subdev *sd)
+{
+	cec_write_and_or(sd, 0x11, 0xfe, 0);  /* disable tx */
+}
+
 static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
 {
 	struct adv76xx_state *state = to_state(sd);
@@ -1910,6 +2108,9 @@ static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
 			*handled = true;
 	}
 
+	/* cec */
+	adv76xx_cec_isr(sd, handled);
+
 	/* tx 5v detect */
 	tx_5v = io_read(sd, 0x70) & info->cable_det_mask;
 	if (tx_5v) {
@@ -2304,6 +2505,10 @@ static const struct v4l2_subdev_video_ops adv76xx_video_ops = {
 	.s_dv_timings = adv76xx_s_dv_timings,
 	.g_dv_timings = adv76xx_g_dv_timings,
 	.query_dv_timings = adv76xx_query_dv_timings,
+	.cec_enable = adv76xx_cec_enable,
+	.cec_log_addr = adv76xx_cec_log_addr,
+	.cec_transmit = adv76xx_cec_transmit,
+	.cec_transmit_timed_out = adv76xx_cec_transmit_timed_out,
 };
 
 static const struct v4l2_subdev_pad_ops adv76xx_pad_ops = {
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v4 09/10] cec: adv7511: add cec support.
  2015-04-23 13:03 [PATCH v4 00/10] HDMI CEC framework Kamil Debski
                   ` (7 preceding siblings ...)
  2015-04-23 13:03 ` [PATCH v4 08/10] cec: adv7604: add cec support Kamil Debski
@ 2015-04-23 13:03 ` Kamil Debski
  8 siblings, 0 replies; 27+ messages in thread
From: Kamil Debski @ 2015-04-23 13:03 UTC (permalink / raw)
  To: dri-devel, linux-media
  Cc: m.szyprowski, k.debski, mchehab, hverkuil, kyungmin.park, thomas,
	sean, dmitry.torokhov, linux-input, linux-samsung-soc,
	Hans Verkuil

From: Hans Verkuil <hansverk@cisco.com>

Add CEC support to the adv7511 driver.

Signed-off-by: Hans Verkuil <hansverk@cisco.com>
[k.debski@samsung.com: Merged changes from CEC Updates commit by Hans Verkuil]
Signed-off-by: Kamil Debski <k.debski@samsung.com>
---
 drivers/media/i2c/adv7511.c |  347 ++++++++++++++++++++++++++++++++++++++++++-
 include/media/adv7511.h     |    6 +-
 2 files changed, 343 insertions(+), 10 deletions(-)

diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c
index 12d9320..d56e110 100644
--- a/drivers/media/i2c/adv7511.c
+++ b/drivers/media/i2c/adv7511.c
@@ -33,6 +33,7 @@
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-dv-timings.h>
 #include <media/adv7511.h>
+#include <media/cec.h>
 
 static int debug;
 module_param(debug, int, 0644);
@@ -91,6 +92,12 @@ struct adv7511_state {
 	int chip_revision;
 	uint8_t i2c_edid_addr;
 	uint8_t i2c_cec_addr;
+
+	struct i2c_client *i2c_cec;
+	u8   cec_addr[3];
+	u8   cec_valid_addrs;
+	bool cec_enabled_adap;
+
 	/* Is the adv7511 powered on? */
 	bool power_on;
 	/* Did we receive hotplug and rx-sense signals? */
@@ -222,7 +229,7 @@ static int adv_smbus_read_i2c_block_data(struct i2c_client *client,
 	return ret;
 }
 
-static inline void adv7511_edid_rd(struct v4l2_subdev *sd, uint16_t len, uint8_t *buf)
+static void adv7511_edid_rd(struct v4l2_subdev *sd, uint16_t len, uint8_t *buf)
 {
 	struct adv7511_state *state = get_adv7511_state(sd);
 	int i;
@@ -237,6 +244,34 @@ static inline void adv7511_edid_rd(struct v4l2_subdev *sd, uint16_t len, uint8_t
 		v4l2_err(sd, "%s: i2c read error\n", __func__);
 }
 
+static inline int cec_read(struct v4l2_subdev *sd, u8 reg)
+{
+	struct adv7511_state *state = get_adv7511_state(sd);
+
+	return i2c_smbus_read_byte_data(state->i2c_cec, reg);
+}
+
+static int cec_write(struct v4l2_subdev *sd, u8 reg, u8 val)
+{
+	struct adv7511_state *state = get_adv7511_state(sd);
+	int ret;
+	int i;
+
+	for (i = 0; i < 3; i++) {
+		ret = i2c_smbus_write_byte_data(state->i2c_cec, reg, val);
+		if (ret == 0)
+			return 0;
+	}
+	v4l2_err(sd, "%s: I2C Write Problem\n", __func__);
+	return ret;
+}
+
+static inline int cec_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask,
+				   u8 val)
+{
+	return cec_write(sd, reg, (cec_read(sd, reg) & mask) | val);
+}
+
 static inline bool adv7511_have_hotplug(struct v4l2_subdev *sd)
 {
 	return adv7511_rd(sd, 0x42) & MASK_ADV7511_HPD_DETECT;
@@ -381,16 +416,28 @@ static const struct v4l2_ctrl_ops adv7511_ctrl_ops = {
 #ifdef CONFIG_VIDEO_ADV_DEBUG
 static void adv7511_inv_register(struct v4l2_subdev *sd)
 {
+	struct adv7511_state *state = get_adv7511_state(sd);
+
 	v4l2_info(sd, "0x000-0x0ff: Main Map\n");
+	if (state->i2c_cec)
+		v4l2_info(sd, "0x100-0x1ff: CEC Map\n");
 }
 
 static int adv7511_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
 {
+	struct adv7511_state *state = get_adv7511_state(sd);
+
 	reg->size = 1;
 	switch (reg->reg >> 8) {
 	case 0:
 		reg->val = adv7511_rd(sd, reg->reg & 0xff);
 		break;
+	case 1:
+		if (state->i2c_cec) {
+			reg->val = cec_read(sd, reg->reg & 0xff);
+			break;
+		}
+		/* fall through */
 	default:
 		v4l2_info(sd, "Register %03llx not supported\n", reg->reg);
 		adv7511_inv_register(sd);
@@ -401,10 +448,18 @@ static int adv7511_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *
 
 static int adv7511_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)
 {
+	struct adv7511_state *state = get_adv7511_state(sd);
+
 	switch (reg->reg >> 8) {
 	case 0:
 		adv7511_wr(sd, reg->reg & 0xff, reg->val & 0xff);
 		break;
+	case 1:
+		if (state->i2c_cec) {
+			cec_write(sd, reg->reg & 0xff, reg->val & 0xff);
+			break;
+		}
+		/* fall through */
 	default:
 		v4l2_info(sd, "Register %03llx not supported\n", reg->reg);
 		adv7511_inv_register(sd);
@@ -418,6 +473,7 @@ static int adv7511_log_status(struct v4l2_subdev *sd)
 {
 	struct adv7511_state *state = get_adv7511_state(sd);
 	struct adv7511_state_edid *edid = &state->edid;
+	int i;
 
 	static const char * const states[] = {
 		"in reset",
@@ -486,7 +542,21 @@ static int adv7511_log_status(struct v4l2_subdev *sd)
 	else
 		v4l2_info(sd, "no timings set\n");
 	v4l2_info(sd, "i2c edid addr: 0x%x\n", state->i2c_edid_addr);
+
+	if (state->i2c_cec == NULL)
+		return 0;
+
 	v4l2_info(sd, "i2c cec addr: 0x%x\n", state->i2c_cec_addr);
+
+	if (cec_read(sd, 0x4e) & 0x01) {
+		v4l2_info(sd, "cec: enabled\n");
+		for (i = 0; i < 3; i++)
+			if (state->cec_valid_addrs & (1 << i))
+				v4l2_info(sd, "cec device %d: addr %d\n", i,
+					  state->cec_addr[i]);
+	} else {
+		v4l2_info(sd, "cec: disabled\n");
+	}
 	return 0;
 }
 
@@ -542,15 +612,136 @@ static int adv7511_s_power(struct v4l2_subdev *sd, int on)
 	return true;
 }
 
+static int adv7511_cec_enable(struct v4l2_subdev *sd, bool enable)
+{
+	struct adv7511_state *state = get_adv7511_state(sd);
+
+	if (!state->cec_enabled_adap && enable) {
+		/* power up cec section */
+		cec_write_and_or(sd, 0x4e, 0xfc, 0x01);
+		/* legacy mode and clear all rx buffers */
+		cec_write(sd, 0x4a, 0x07);
+		cec_write(sd, 0x4a, 0);
+		cec_write_and_or(sd, 0x11, 0xfe, 0); /* initially disable tx */
+		/* enabled irqs: */
+		/* tx: ready */
+		/* tx: arbitration lost */
+		/* tx: retry timeout */
+		/* rx: ready 1 */
+		adv7511_wr_and_or(sd, 0x95, 0xc0, 0x39);
+	} else if (state->cec_enabled_adap && !enable) {
+		adv7511_wr_and_or(sd, 0x95, 0xc0, 0x00);
+		/* disable address mask 1-3 */
+		cec_write_and_or(sd, 0x4b, 0x8f, 0x00);
+		/* power down cec section */
+		cec_write_and_or(sd, 0x4e, 0xfc, 0x00);
+		state->cec_valid_addrs = 0;
+	}
+	state->cec_enabled_adap = enable;
+	return 0;
+}
+
+#define ADV7511_MAX_ADDRS 3
+
+static int adv7511_cec_log_addr(struct v4l2_subdev *sd, u8 addr)
+{
+	struct adv7511_state *state = get_adv7511_state(sd);
+	unsigned i, free_idx = ADV7511_MAX_ADDRS;
+
+	if (!state->cec_enabled_adap)
+		return -EIO;
+
+	for (i = 0; i < ADV7511_MAX_ADDRS; i++) {
+		bool is_valid = state->cec_valid_addrs & (1 << i);
+
+		if (free_idx == ADV7511_MAX_ADDRS && !is_valid)
+			free_idx = i;
+		if (is_valid && state->cec_addr[i] == addr)
+			return 0;
+	}
+	if (i == ADV7511_MAX_ADDRS) {
+		i = free_idx;
+		if (i == ADV7511_MAX_ADDRS)
+			return -ENXIO;
+	}
+	state->cec_addr[i] = addr;
+	state->cec_valid_addrs |= 1 << i;
+
+	switch (i) {
+	case 0:
+		/* enable address mask 0 */
+		cec_write_and_or(sd, 0x4b, 0xef, 0x10);
+		/* set address for mask 0 */
+		cec_write_and_or(sd, 0x4c, 0xf0, addr);
+		break;
+	case 1:
+		/* enable address mask 1 */
+		cec_write_and_or(sd, 0x4b, 0xdf, 0x20);
+		/* set address for mask 1 */
+		cec_write_and_or(sd, 0x4c, 0x0f, addr << 4);
+		break;
+	case 2:
+		/* enable address mask 2 */
+		cec_write_and_or(sd, 0x4b, 0xbf, 0x40);
+		/* set address for mask 1 */
+		cec_write_and_or(sd, 0x4d, 0xf0, addr);
+		break;
+	}
+	return 0;
+}
+
+static int adv7511_cec_transmit(struct v4l2_subdev *sd, struct cec_msg *msg)
+{
+	u8 len = msg->len;
+	unsigned i;
+
+	v4l2_dbg(1, debug, sd, "%s: len %d\n", __func__, len);
+
+	if (len > 16) {
+		v4l2_err(sd, "%s: len exceeded 16 (%d)\n", __func__, len);
+		return -EINVAL;
+	}
+
+	/* blocking, clear cec tx irq status */
+	adv7511_wr_and_or(sd, 0x97, 0xc7, 0x38);
+
+	/* write data */
+	for (i = 0; i < len; i++)
+		cec_write(sd, i, msg->msg[i]);
+
+	/* set length (data + header) */
+	cec_write(sd, 0x10, len);
+	/* start transmit, enable tx */
+	cec_write(sd, 0x11, 0x01);
+	return 0;
+}
+
+static void adv7511_cec_transmit_timed_out(struct v4l2_subdev *sd)
+{
+	cec_write_and_or(sd, 0x11, 0xfe, 0); /* disable tx */
+}
+
 /* Enable interrupts */
 static void adv7511_set_isr(struct v4l2_subdev *sd, bool enable)
 {
+	struct adv7511_state *state = get_adv7511_state(sd);
 	uint8_t irqs = MASK_ADV7511_HPD_INT | MASK_ADV7511_MSEN_INT;
 	uint8_t irqs_rd;
 	int retries = 100;
 
 	v4l2_dbg(2, debug, sd, "%s: %s\n", __func__, enable ? "enable" : "disable");
 
+	if (state->i2c_cec) {
+		/*
+		 * Enabled CEC irqs:
+		 * tx: ready
+		 * tx: arbitration lost
+		 * tx: retry timeout
+		 * rx: ready 1
+		 */
+		adv7511_wr_and_or(sd, 0x95, 0xc0, enable ? 0x39 : 0);
+	}
+
 	/* The datasheet says that the EDID ready interrupt should be
 	   disabled if there is no hotplug. */
 	if (!enable)
@@ -576,24 +767,82 @@ static void adv7511_set_isr(struct v4l2_subdev *sd, bool enable)
 	v4l2_err(sd, "Could not set interrupts: hw failure?\n");
 }
 
+static void adv_cec_tx_raw_status(struct v4l2_subdev *sd, u8 tx_raw_status)
+{
+	if ((cec_read(sd, 0x11) & 0x01) == 0) {
+		v4l2_dbg(1, debug, sd, "%s: tx raw: tx disabled\n", __func__);
+		return;
+	}
+
+	if (tx_raw_status & 0x10) {
+		v4l2_dbg(1, debug, sd,
+			 "%s: tx raw: arbitration lost\n", __func__);
+		v4l2_subdev_notify(sd, V4L2_SUBDEV_CEC_TX_DONE,
+				   (void *)CEC_TX_STATUS_ARB_LOST);
+		return;
+	}
+	if (tx_raw_status & 0x08) {
+		v4l2_dbg(1, debug, sd, "%s: tx raw: retry failed\n", __func__);
+		v4l2_subdev_notify(sd, V4L2_SUBDEV_CEC_TX_DONE,
+				   (void *)CEC_TX_STATUS_RETRY_TIMEOUT);
+		return;
+	}
+	if (tx_raw_status & 0x20) {
+		v4l2_dbg(1, debug, sd, "%s: tx raw: ready ok\n", __func__);
+		v4l2_subdev_notify(sd, V4L2_SUBDEV_CEC_TX_DONE,
+				   (void *)CEC_TX_STATUS_OK);
+		return;
+	}
+}
+
 /* Interrupt handler */
 static int adv7511_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
 {
 	uint8_t irq_status;
+	uint8_t cec_irq;
 
 	/* disable interrupts to prevent a race condition */
 	adv7511_set_isr(sd, false);
 	irq_status = adv7511_rd(sd, 0x96);
+	cec_irq = adv7511_rd(sd, 0x97);
 	/* clear detected interrupts */
 	adv7511_wr(sd, 0x96, irq_status);
+	adv7511_wr(sd, 0x97, cec_irq);
 
-	v4l2_dbg(1, debug, sd, "%s: irq 0x%x\n", __func__, irq_status);
+	v4l2_dbg(1, debug, sd, "%s: irq 0x%x, cec-irq 0x%x\n", __func__,
+		 irq_status, cec_irq);
 
 	if (irq_status & (MASK_ADV7511_HPD_INT | MASK_ADV7511_MSEN_INT))
 		adv7511_check_monitor_present_status(sd);
 	if (irq_status & MASK_ADV7511_EDID_RDY_INT)
 		adv7511_check_edid_status(sd);
 
+	if (cec_irq & 0x38)
+		adv_cec_tx_raw_status(sd, cec_irq);
+
+	if (cec_irq & 1) {
+		struct cec_msg msg;
+
+		msg.len = cec_read(sd, 0x25) & 0x1f;
+
+		v4l2_dbg(1, debug, sd, "%s: cec msg len %d\n", __func__,
+			 msg.len);
+
+		if (msg.len > 16)
+			msg.len = 16;
+
+		if (msg.len) {
+			u8 i;
+
+			for (i = 0; i < msg.len; i++)
+				msg.msg[i] = cec_read(sd, i + 0x15);
+
+			cec_write(sd, 0x4a, 1); /* toggle to re-enable rx 1 */
+			cec_write(sd, 0x4a, 0);
+			v4l2_subdev_notify(sd, V4L2_SUBDEV_CEC_RX_MSG, &msg);
+		}
+	}
+
 	/* enable interrupts */
 	adv7511_set_isr(sd, true);
 
@@ -697,6 +946,10 @@ static const struct v4l2_subdev_video_ops adv7511_video_ops = {
 	.s_stream = adv7511_s_stream,
 	.s_dv_timings = adv7511_s_dv_timings,
 	.g_dv_timings = adv7511_g_dv_timings,
+	.cec_enable = adv7511_cec_enable,
+	.cec_log_addr = adv7511_cec_log_addr,
+	.cec_transmit = adv7511_cec_transmit,
+	.cec_transmit_timed_out = adv7511_cec_transmit_timed_out,
 };
 
 /* ------------------------------ AUDIO OPS ------------------------------ */
@@ -1080,6 +1333,7 @@ static void adv7511_edid_handler(struct work_struct *work)
 	/* We failed to read the EDID, so send an event for this. */
 	ed.present = false;
 	ed.segment = adv7511_rd(sd, 0xc4);
+	ed.phys_addr = 0xffff;
 	v4l2_subdev_notify(sd, ADV7511_EDID_DETECT, (void *)&ed);
 	v4l2_dbg(1, debug, sd, "%s: no edid found\n", __func__);
 }
@@ -1220,10 +1474,39 @@ static bool edid_verify_header(struct v4l2_subdev *sd, u32 segment)
 	return !memcmp(data, hdmi_header, sizeof(hdmi_header));
 }
 
+static int get_edid_spa_location(const u8 *edid)
+{
+	u8 d;
+
+	if ((edid[0x7e] != 1) ||
+			(edid[0x80] != 0x02) ||
+			(edid[0x81] != 0x03)) {
+		return -1;
+	}
+
+	/* search Vendor Specific Data Block (tag 3) */
+	d = edid[0x82] & 0x7f;
+	if (d > 4) {
+		int i = 0x84;
+		int end = 0x80 + d;
+
+		do {
+			u8 tag = edid[i] >> 5;
+			u8 len = edid[i] & 0x1f;
+
+			if ((tag == 3) && (len >= 5))
+				return i + 4;
+			i += len + 1;
+		} while (i < end);
+	}
+	return -1;
+}
+
 static bool adv7511_check_edid_status(struct v4l2_subdev *sd)
 {
 	struct adv7511_state *state = get_adv7511_state(sd);
 	uint8_t edidRdy = adv7511_rd(sd, 0xc5);
+	int offset;
 
 	v4l2_dbg(1, debug, sd, "%s: edid ready (retries: %d)\n",
 			 __func__, EDID_MAX_RETRIES - state->edid.read_retries);
@@ -1269,6 +1552,12 @@ static bool adv7511_check_edid_status(struct v4l2_subdev *sd)
 
 		v4l2_dbg(1, debug, sd, "%s: edid complete with %d segment(s)\n", __func__, state->edid.segments);
 		state->edid.complete = true;
+		offset = get_edid_spa_location(state->edid.data);
+		if (offset > 0)
+			ed.phys_addr = (state->edid.data[offset] << 8) |
+					state->edid.data[offset + 1];
+		else
+			ed.phys_addr = 0xffff;
 
 		/* report when we have all segments
 		   but report only for segment 0
@@ -1290,11 +1579,14 @@ static void adv7511_init_setup(struct v4l2_subdev *sd)
 {
 	struct adv7511_state *state = get_adv7511_state(sd);
 	struct adv7511_state_edid *edid = &state->edid;
+	u32 cec_clk = state->pdata.cec_clk;
+	u8 ratio;
 
 	v4l2_dbg(1, debug, sd, "%s\n", __func__);
 
 	/* clear all interrupts */
 	adv7511_wr(sd, 0x96, 0xff);
+	adv7511_wr(sd, 0x97, 0xff);
 	/*
 	 * Stop HPD from resetting a lot of registers.
 	 * It might leave the chip in a partly un-initialized state,
@@ -1306,6 +1598,25 @@ static void adv7511_init_setup(struct v4l2_subdev *sd)
 	adv7511_set_isr(sd, false);
 	adv7511_s_stream(sd, false);
 	adv7511_s_audio_stream(sd, false);
+
+	if (state->i2c_cec == NULL)
+		return;
+
+	v4l2_dbg(1, debug, sd, "%s: cec_clk %d\n", __func__, cec_clk);
+
+	/* cec soft reset */
+	cec_write(sd, 0x50, 0x01);
+	cec_write(sd, 0x50, 0x00);
+
+	/* legacy mode */
+	cec_write(sd, 0x4a, 0x00);
+
+	if (cec_clk % 750000 != 0)
+		v4l2_err(sd, "%s: cec_clk %d, not multiple of 750 Khz\n",
+			 __func__, cec_clk);
+
+	ratio = (cec_clk / 750000) - 1;
+	cec_write(sd, 0x4e, ratio << 2);
 }
 
 static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *id)
@@ -1381,19 +1692,40 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *
 	chip_id[0] = adv7511_rd(sd, 0xf5);
 	chip_id[1] = adv7511_rd(sd, 0xf6);
 	if (chip_id[0] != 0x75 || chip_id[1] != 0x11) {
-		v4l2_err(sd, "chip_id != 0x7511, read 0x%02x%02x\n", chip_id[0], chip_id[1]);
+		v4l2_err(sd, "chip_id != 0x7511, read 0x%02x%02x\n", chip_id[0],
+			 chip_id[1]);
 		err = -EIO;
 		goto err_entity;
 	}
 
-	state->i2c_edid = i2c_new_dummy(client->adapter, state->i2c_edid_addr >> 1);
+	state->i2c_edid = i2c_new_dummy(client->adapter,
+					state->i2c_edid_addr >> 1);
 	if (state->i2c_edid == NULL) {
 		v4l2_err(sd, "failed to register edid i2c client\n");
 		err = -ENOMEM;
 		goto err_entity;
 	}
 
-	adv7511_wr(sd, 0xe2, 0x01); /* power down cec section */
+	adv7511_wr(sd, 0xe1, state->i2c_cec_addr);
+	if (state->pdata.cec_clk < 3000000 ||
+	    state->pdata.cec_clk > 100000000) {
+		v4l2_err(sd, "%s: cec_clk %u outside range, disabling cec\n",
+				__func__, state->pdata.cec_clk);
+		state->pdata.cec_clk = 0;
+	}
+
+	if (state->pdata.cec_clk) {
+		state->i2c_cec = i2c_new_dummy(client->adapter,
+					       (state->i2c_cec_addr>>1));
+		if (state->i2c_cec == NULL) {
+			v4l2_err(sd, "failed to register cec i2c client\n");
+			goto err_unreg_edid;
+		}
+		adv7511_wr(sd, 0xe2, 0x00); /* power up cec section */
+	} else {
+		adv7511_wr(sd, 0xe2, 0x01); /* power down cec section */
+	}
+
 	state->work_queue = create_singlethread_workqueue(sd->name);
 	if (state->work_queue == NULL) {
 		v4l2_err(sd, "could not create workqueue\n");
@@ -1412,6 +1744,9 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *
 	return 0;
 
 err_unreg_cec:
+	if (state->i2c_cec)
+		i2c_unregister_device(state->i2c_cec);
+err_unreg_edid:
 	i2c_unregister_device(state->i2c_edid);
 err_entity:
 	media_entity_cleanup(&sd->entity);
@@ -1435,6 +1770,8 @@ static int adv7511_remove(struct i2c_client *client)
 	adv7511_init_setup(sd);
 	cancel_delayed_work(&state->edid_handler);
 	i2c_unregister_device(state->i2c_edid);
+	if (state->i2c_cec)
+		i2c_unregister_device(state->i2c_cec);
 	destroy_workqueue(state->work_queue);
 	v4l2_device_unregister_subdev(sd);
 	media_entity_cleanup(&sd->entity);
diff --git a/include/media/adv7511.h b/include/media/adv7511.h
index bb78bed..c971b52 100644
--- a/include/media/adv7511.h
+++ b/include/media/adv7511.h
@@ -32,11 +32,7 @@ struct adv7511_monitor_detect {
 struct adv7511_edid_detect {
 	int present;
 	int segment;
-};
-
-struct adv7511_cec_arg {
-	void *arg;
-	u32 f_flags;
+	uint16_t phys_addr;
 };
 
 struct adv7511_platform_data {
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PATCH v4 06/10] cec: add HDMI CEC framework
  2015-04-23 13:03 ` [PATCH v4 06/10] cec: add HDMI CEC framework Kamil Debski
@ 2015-04-23 14:18   ` Hans Verkuil
  2015-04-24 10:03   ` Lars Op den Kamp
                     ` (5 subsequent siblings)
  6 siblings, 0 replies; 27+ messages in thread
From: Hans Verkuil @ 2015-04-23 14:18 UTC (permalink / raw)
  To: Kamil Debski, dri-devel, linux-media
  Cc: m.szyprowski, mchehab, kyungmin.park, thomas, sean,
	dmitry.torokhov, linux-input, linux-samsung-soc, Hans Verkuil

On 04/23/2015 03:03 PM, Kamil Debski wrote:
> From: Hans Verkuil <hansverk@cisco.com>
> 
> The added HDMI CEC framework provides a generic kernel interface for
> HDMI CEC devices.
> 
> Signed-off-by: Hans Verkuil <hansverk@cisco.com>
> [k.debski@samsung.com: Merged CEC Updates commit by Hans Verkuil]
> [k.debski@samsung.com: Merged Update author commit by Hans Verkuil]
> [k.debski@samsung.com: change kthread handling when setting logical
> address]
> [k.debski@samsung.com: code cleanup and fixes]
> [k.debski@samsung.com: add missing CEC commands to match spec]
> [k.debski@samsung.com: add RC framework support]
> [k.debski@samsung.com: move and edit documentation]
> [k.debski@samsung.com: add vendor id reporting]
> [k.debski@samsung.com: add possibility to clear assigned logical
> addresses]
> [k.debski@samsung.com: documentation fixes, clenaup and expansion]
> [k.debski@samsung.com: reorder of API structs and add reserved fields]
> [k.debski@samsung.com: fix handling of events and fix 32/64bit timespec
> problem]
> [k.debski@samsung.com: add cec.h to include/uapi/linux/Kbuild]
> Signed-off-by: Kamil Debski <k.debski@samsung.com>
> ---
>  Documentation/cec.txt     |  396 ++++++++++++++++
>  drivers/media/Kconfig     |    6 +
>  drivers/media/Makefile    |    2 +
>  drivers/media/cec.c       | 1161 +++++++++++++++++++++++++++++++++++++++++++++
>  include/media/cec.h       |  140 ++++++
>  include/uapi/linux/Kbuild |    1 +
>  include/uapi/linux/cec.h  |  303 ++++++++++++
>  7 files changed, 2009 insertions(+)
>  create mode 100644 Documentation/cec.txt
>  create mode 100644 drivers/media/cec.c
>  create mode 100644 include/media/cec.h
>  create mode 100644 include/uapi/linux/cec.h
> 
> diff --git a/include/uapi/linux/cec.h b/include/uapi/linux/cec.h
> new file mode 100644
> index 0000000..bb6d66c
> --- /dev/null
> +++ b/include/uapi/linux/cec.h
> @@ -0,0 +1,303 @@
> +
> +/* Userspace has to configure the adapter state (enable/disable) */
> +#define CEC_CAP_STATE		(1 << 0)
> +/* Userspace has to configure the physical address */
> +#define CEC_CAP_PHYS_ADDR	(1 << 1)
> +/* Userspace has to configure the logical addresses */
> +#define CEC_CAP_LOG_ADDRS	(1 << 2)
> +/* Userspace can transmit messages */
> +#define CEC_CAP_TRANSMIT	(1 << 3)
> +/* Userspace can receive messages */
> +#define CEC_CAP_RECEIVE		(1 << 4)
> +/* Userspace has to configure the vendor id */
> +#define CEC_CAP_VENDOR_ID	(1 << 5)
> +/* The hardware has the possibility to work in the promiscuous mode */
> +#define CEC_CAP_PROMISCUOUS	(1 << 6)

Since promiscuous support has been dropped, this capability needs to be
dropped as well.

> +
> +struct cec_caps {
> +	__u32 available_log_addrs;
> +	__u32 capabilities;
> +	__u32 vendor_id;
> +	__u8  version;
> +	__u8  reserved[11];

I'd increase this to 31.

> +};
> +
> +struct cec_log_addrs {
> +	__u8 cec_version;
> +	__u8 num_log_addrs;
> +	__u8 primary_device_type[CEC_MAX_LOG_ADDRS];
> +	__u8 log_addr_type[CEC_MAX_LOG_ADDRS];
> +	__u8 log_addr[CEC_MAX_LOG_ADDRS];
> +
> +	/* CEC 2.0 */
> +	__u8 all_device_types;
> +	__u8 features[CEC_MAX_LOG_ADDRS][12];
> +
> +	__u8 reserved[9];

I'd increase this to 65 or so.

Regards,

	Hans


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v4 06/10] cec: add HDMI CEC framework
  2015-04-23 13:03 ` [PATCH v4 06/10] cec: add HDMI CEC framework Kamil Debski
  2015-04-23 14:18   ` Hans Verkuil
@ 2015-04-24 10:03   ` Lars Op den Kamp
  2015-04-27  8:09     ` Kamil Debski
  2015-04-27  7:40   ` [PATCH v4 06/10] cec: add HDMI CEC framework: y2038 question Hans Verkuil
                     ` (4 subsequent siblings)
  6 siblings, 1 reply; 27+ messages in thread
From: Lars Op den Kamp @ 2015-04-24 10:03 UTC (permalink / raw)
  To: Kamil Debski, dri-devel, linux-media
  Cc: m.szyprowski, mchehab, hverkuil, kyungmin.park, thomas, sean,
	dmitry.torokhov, linux-input, linux-samsung-soc, Hans Verkuil

Hi Kamil, Hans,

I'm the main developer of libCEC 
(https://github.com/Pulse-Eight/libcec). Sorry for the late time to jump 
in here, but I wasn't signed up to this mailing list and someone pointed 
me to this discussion.

Unfortunately this approach will not work with half the TVs that are out 
there. I'll explain why:

* because of how some (common) brands implemented CEC in their TVs, this 
implementation will not work, as the TV will just reject it. In libCEC, 
we've created work arounds for brands like this. Without these work 
arounds, your in-kernel implementation will be very vendor specific. 
e.g. this implementation will work for Samsung's TVs, but not for the 
TVs made by another big TV brand. All commands, including CEC_OP_ABORT, 
should be passed to userspace to make it work with all brands.

* it should be made possible to not have the kernel send any CEC 
message, try to process any received CEC message, or ack to any logical 
address at all, to allow libraries like libCEC to fully handle all CEC 
traffic. Some brands only enable routing of some CEC keys when a 
specific device type is used. libCEC will allocate a logical address of 
the correct type for the brand that's used. If another address is first 
allocated by the kernel, and the TV communicates with it to find out 
it's name and things like that, and libCEC allocates another address a 
bit later when initialised, then you'll end up with multiple entries in 
the device list on the TV, and only one of them will work.

* CEC is *very* vendor specific. The main reason is, in my opinion, the 
use of the word "should" instead of "shall" in the spec. It's addressed 
in the new version, but it'll take years before all the non 2.x devices 
are gone. What works for vendor A will simply not work for vendor B. 
libCEC aims to address this, in a library that can be used on all major 
platforms and by all major programming languages. You could duplicate 
the work done there in the kernel to make make the implementation work 
with all brands, but I think that this does simply not belong in the 
kernel when it can be handled in userspace perfectly.

So I suggest that you limit the in-kernel implementation to handling raw 
traffic only, to have it do this (and nothing more):
* allocate one or more logical addresses, and ack CEC traffic sent to 
those logical addresses
* receive CEC traffic and forward it to userspace (traffic sent to all 
addresses is preferred, not just traffic sent to the logical address 
used by the device running this code)
* transmit CEC traffic initiated by userspace

thanks,

Lars Op den Kamp


On 23-04-15 15:03, Kamil Debski wrote:
> From: Hans Verkuil <hansverk@cisco.com>
>
> The added HDMI CEC framework provides a generic kernel interface for
> HDMI CEC devices.
>
> Signed-off-by: Hans Verkuil <hansverk@cisco.com>
> [k.debski@samsung.com: Merged CEC Updates commit by Hans Verkuil]
> [k.debski@samsung.com: Merged Update author commit by Hans Verkuil]
> [k.debski@samsung.com: change kthread handling when setting logical
> address]
> [k.debski@samsung.com: code cleanup and fixes]
> [k.debski@samsung.com: add missing CEC commands to match spec]
> [k.debski@samsung.com: add RC framework support]
> [k.debski@samsung.com: move and edit documentation]
> [k.debski@samsung.com: add vendor id reporting]
> [k.debski@samsung.com: add possibility to clear assigned logical
> addresses]
> [k.debski@samsung.com: documentation fixes, clenaup and expansion]
> [k.debski@samsung.com: reorder of API structs and add reserved fields]
> [k.debski@samsung.com: fix handling of events and fix 32/64bit timespec
> problem]
> [k.debski@samsung.com: add cec.h to include/uapi/linux/Kbuild]
> Signed-off-by: Kamil Debski <k.debski@samsung.com>
> ---
>   Documentation/cec.txt     |  396 ++++++++++++++++
>   drivers/media/Kconfig     |    6 +
>   drivers/media/Makefile    |    2 +
>   drivers/media/cec.c       | 1161 +++++++++++++++++++++++++++++++++++++++++++++
>   include/media/cec.h       |  140 ++++++
>   include/uapi/linux/Kbuild |    1 +
>   include/uapi/linux/cec.h  |  303 ++++++++++++
>   7 files changed, 2009 insertions(+)
>   create mode 100644 Documentation/cec.txt
>   create mode 100644 drivers/media/cec.c
>   create mode 100644 include/media/cec.h
>   create mode 100644 include/uapi/linux/cec.h
>
> diff --git a/Documentation/cec.txt b/Documentation/cec.txt
> new file mode 100644
> index 0000000..2b6c08a
> --- /dev/null
> +++ b/Documentation/cec.txt
> @@ -0,0 +1,396 @@
> +CEC Kernel Support
> +==================
> +
> +The CEC framework provides a unified kernel interface for use with HDMI CEC
> +hardware. It is designed to handle a multiple variants of hardware. Adding to
> +the flexibility of the framework it enables to set which parts of the CEC
> +protocol processing is handled by the hardware, by the driver and by the
> +userspace application.
> +
> +
> +The CEC Protocol
> +----------------
> +
> +The CEC protocol enables consumer electronic devices to communicate with each
> +other through the HDMI connection. The protocol uses logical addresses in the
> +communication. The logical address is strictly connected with the functionality
> +provided by the device. The TV acting as the communication hub is always
> +assigned address 0. The physical address is determined by the physical
> +connection between devices.
> +
> +The protocol enables control of compatible devices with a single remote.
> +Synchronous power on/standby, instant playback with changing the content source
> +on the TV.
> +
> +The Kernel Interface
> +====================
> +
> +CEC Adapter
> +-----------
> +
> +#define CEC_LOG_ADDR_INVALID 0xff
> +
> +/* The maximum number of logical addresses one device can be assigned to.
> + * The CEC 2.0 spec allows for only 2 logical addresses at the moment. The
> + * Analog Devices CEC hardware supports 3. So let's go wild and go for 4. */
> +#define CEC_MAX_LOG_ADDRS 4
> +
> +/* The "Primary Device Type" */
> +#define CEC_PRIM_DEVTYPE_TV		0
> +#define CEC_PRIM_DEVTYPE_RECORD		1
> +#define CEC_PRIM_DEVTYPE_TUNER		3
> +#define CEC_PRIM_DEVTYPE_PLAYBACK	4
> +#define CEC_PRIM_DEVTYPE_AUDIOSYSTEM	5
> +#define CEC_PRIM_DEVTYPE_SWITCH		6
> +#define CEC_PRIM_DEVTYPE_VIDEOPROC	7
> +
> +/* The "All Device Types" flags (CEC 2.0) */
> +#define CEC_FL_ALL_DEVTYPE_TV		(1 << 7)
> +#define CEC_FL_ALL_DEVTYPE_RECORD	(1 << 6)
> +#define CEC_FL_ALL_DEVTYPE_TUNER	(1 << 5)
> +#define CEC_FL_ALL_DEVTYPE_PLAYBACK	(1 << 4)
> +#define CEC_FL_ALL_DEVTYPE_AUDIOSYSTEM	(1 << 3)
> +#define CEC_FL_ALL_DEVTYPE_SWITCH	(1 << 2)
> +/* And if you wondering what happened to VIDEOPROC devices: those should
> + * be mapped to a SWITCH. */
> +
> +/* The logical address types that the CEC device wants to claim */
> +#define CEC_LOG_ADDR_TYPE_TV		0
> +#define CEC_LOG_ADDR_TYPE_RECORD	1
> +#define CEC_LOG_ADDR_TYPE_TUNER		2
> +#define CEC_LOG_ADDR_TYPE_PLAYBACK	3
> +#define CEC_LOG_ADDR_TYPE_AUDIOSYSTEM	4
> +#define CEC_LOG_ADDR_TYPE_SPECIFIC	5
> +#define CEC_LOG_ADDR_TYPE_UNREGISTERED	6
> +/* Switches should use UNREGISTERED.
> + * Video processors should use SPECIFIC. */
> +
> +/* The CEC version */
> +#define CEC_VERSION_1_4B		5
> +#define CEC_VERSION_2_0			6
> +
> +struct cec_adapter {
> +	/* internal fields removed */
> +
> +	u16 phys_addr;
> +	u32 capabilities;
> +	u8 version;
> +	u8 num_log_addrs;
> +	u8 prim_device[CEC_MAX_LOG_ADDRS];
> +	u8 log_addr_type[CEC_MAX_LOG_ADDRS];
> +	u8 log_addr[CEC_MAX_LOG_ADDRS];
> +
> +	int (*adap_enable)(struct cec_adapter *adap, bool enable);
> +	int (*adap_log_addr)(struct cec_adapter *adap, u8 logical_addr);
> +	int (*adap_transmit)(struct cec_adapter *adap, struct cec_msg *msg);
> +	void (*adap_transmit_timed_out)(struct cec_adapter *adap);
> +
> +	void (*claimed_log_addr)(struct cec_adapter *adap, u8 idx);
> +	int (*received)(struct cec_adapter *adap, struct cec_msg *msg);
> +};
> +
> +int cec_create_adapter(struct cec_adapter *adap, u32 caps);
> +void cec_delete_adapter(struct cec_adapter *adap);
> +int cec_transmit_msg(struct cec_adapter *adap, struct cec_data *data, bool block);
> +
> +/* Called by the adapter */
> +void cec_transmit_done(struct cec_adapter *adap, u32 status);
> +void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg);
> +
> +int cec_receive_msg(struct cec_adapter *adap, struct cec_msg *msg, bool block);
> +int cec_claim_log_addrs(struct cec_adapter *adap, struct cec_log_addrs *log_addrs, bool block);
> +
> +The device type defines are defined by the CEC standard.
> +
> +The cec_adapter structure represents the adapter. It has a number of
> +operations that have to be implemented in the driver: adap_enable() enables
> +or disables the physical adapter, adap_log_addr() tells the driver which
> +logical address should be configured. This may be called multiple times
> +to configure multiple logical addresses. Calling adap_enable(false) or
> +adap_log_addr(CEC_LOG_ADDR_INVALID) will clear all configured logical
> +addresses.
> +
> +The adap_transmit op will setup the hardware to send out the given CEC message.
> +This will return without waiting for the transmission to finish. The
> +adap_transmit_timed_out() function is called when the current transmission timed
> +out and the hardware needs to be informed of this (the hardware should go back
> +from transmitter to receiver mode).
> +
> +The adapter driver will also call into the adapter: it should call
> +cec_transmit_done() when a cec transfer was finalized and cec_received_msg()
> +when a new message was received.
> +
> +When a message is received the received() op is called.
> +
> +The driver has to call cec_create_adapter to initialize the structure. If
> +the 'caps' argument is non-zero, then it will also create a /dev/cecX
> +device node to allow userspace to interact with the CEC device. Userspace
> +can request those capabilities with the CEC_G_CAPS ioctl.
> +
> +In order for a CEC adapter to be configured it needs a physical address.
> +This is normally assigned by the driver. It is either 0.0.0.0 for a TV (aka
> +video receiver) or it is derived from the EDID that the source received
> +from the sink. This is normally set by the driver before enabling the CEC
> +adapter, or it is set from userspace in the case of CEC USB dongles (although
> +embedded systems might also want to set this manually).
> +
> +After enabling the CEC adapter it has to be configured.
> +
> +The userspace has to inform the CEC adapter of which type of device it requests
> +the adapter to identify itself. After this information is set by userspace, the
> +CEC framework will attempt to to find and claim a logical addresses matching the
> +requested device type. If none are found, then it will fall back to logical
> +address Unregistered (15). To clear the logical addresses list from the list the
> +userspace application should set the num_log_addrs field of struct cec_log_addr
> +to 0.
> +
> +The type of device is set from the userspace with the CEC_S_ADAP_LOG_ADDRS. In
> +addition, claiming logical addresses can be initiated from the kernel side by
> +calling the cec_claim_log_addrs function.
> +
> +Before the addresses are claimed it is possible to send and receive messages.
> +Sending all messages is possible as it is up to the userspace to the source
> +and destination addresses in the message payload. However, only broadcast
> +messages can be received until a regular logical address is claimed.
> +
> +When a CEC message is received the CEC framework will take care of the CEC
> +core messages CEC_OP_GET_CEC_VERSION, CEC_OP_GIVE_PHYS_ADDR and CEC_OP_ABORT.
> +Then it will call the received() op (if set), and finally it will queue it
> +for handling by userspace if create_devnode was true, or send back
> +FEATURE_ABORT if create_devnode was false.
> +
> +Drivers can also use the cec_transmit_msg() call to transmit a message. This
> +can either be fire-and-forget (the CEC framework will queue up messages in a
> +transmit queue), or a blocking wait until there is either an error or a
> +reply to the message.
> +
> +
> +The Userspace API
> +=================
> +
> +ioctl API
> +---------
> +
> +- CEC_G_CAPS ioctl
> +
> +Read the CEC adapter capabilities: the number of logical addresses
> +that the adapter can configure and what can be controlled from userspace.
> +
> +#define CEC_G_CAPS			_IOR('a', 0, struct cec_caps)
> +
> +The cec_caps struct is following:
> +
> +struct cec_caps {
> +	__u32 available_log_addrs;
> +	__u32 capabilities;
> +	__u32 vendor_id;
> +	__u8  version;
> +	__u8  reserved[11];
> +};
> +
> +The following capabilities are defined:
> +
> +/* Userspace has to configure the adapter state (enable/disable) */
> +#define CEC_CAP_STATE		(1 << 0)
> +/* Userspace has to configure the physical address */
> +#define CEC_CAP_PHYS_ADDR	(1 << 1)
> +/* Userspace has to configure the logical addresses */
> +#define CEC_CAP_LOG_ADDRS	(1 << 2)
> +/* Userspace can transmit messages */
> +#define CEC_CAP_TRANSMIT	(1 << 3)
> +/* Userspace can receive messages */
> +#define CEC_CAP_RECEIVE		(1 << 4)
> +/* Userspace has to configure the vendor id */
> +#define CEC_CAP_VENDOR_ID	(1 << 5)
> +/* The hardware has the possibility to work in the promiscuous mode */
> +#define CEC_CAP_PROMISCUOUS	(1 << 6)
> +
> +- CEC_TRANSMIT and CEC_RECEIVE ioctls
> +
> +These ioctls are used to send and receive messages over the CEC bus.
> +
> +#define CEC_TRANSMIT		_IOWR('a', 1, struct cec_msg)
> +#define CEC_RECEIVE		_IOWR('a', 2, struct cec_msg)
> +
> +The struct cec_msg is the main message struct:
> +
> +struct cec_msg {
> +	__u32 len;
> +	__u32 status;
> +	__u32 timeout;
> +	/* timeout (in ms) is used to timeout CEC_RECEIVE.
> +	   Set to 0 if you want to wait forever. */
> +	struct cec_time ts;
> +	__u8  msg[16];
> +	__u8  reply;
> +	/* If non-zero, then wait for a reply with this opcode.
> +	   If there was an error when sending the msg or FeatureAbort
> +	   was returned, then reply is set to 0.
> +	   If reply is non-zero upon return, then len/msg are set to
> +	   the received message.
> +	   If reply is zero upon return and status has the
> +	   CEC_TX_STATUS_FEATURE_ABORT bit set, then len/msg are set to the
> +	   received feature abort message.  If reply is zero upon return and
> +	   status has the CEC_TX_STATUS_REPLY_TIMEOUT bit set, then no reply
> +	   was seen at all.  This field is ignored with CEC_RECEIVE.
> +	   If reply is non-zero for CEC_TRANSMIT and the message is a broadcast,
> +	   then -EINVAL is returned.
> +	   if reply is non-zero, then timeout is set to 1000 (the required
> +	   maximum response time).
> +	 */
> +	__u8 reserved[31];
> +};
> +
> +The struct contains 16 bytes for the message, the length of the message, a
> +status value in case of errors. Optionally you can request the CEC framework to
> +wait after transmitting the message until the 'reply' message is returned (or
> +Feature Abort). This is done asynchronously, i.e. it does not require that the
> +reply comes right after the transmit, but other messages in between are allowed.
> +
> +The ts field of the struct cec_msg represents a timestamp. The timestamp struct
> +is following:
> +
> +struct cec_time {
> +	__u64 sec;
> +	__u64 nsec;
> +};
> +
> +With CEC_TRANSMIT you can transmit a message, either blocking or
> +non-blocking. With CEC_RECEIVE you can dequeue a pending received
> +message from the internal queue or wait for a message to arrive
> +(if called in blocking mode).
> +
> +- CEC_G_ADAP_LOG_ADDRS and CEC_S_ADAP_LOG_ADDRS
> +
> +These ioctl are used to configure the logical addresses of the CEC adapter.
> +
> +#define CEC_G_ADAP_LOG_ADDRS	_IOR('a', 3, struct cec_log_addrs)
> +#define CEC_S_ADAP_LOG_ADDRS	_IOWR('a', 4, struct cec_log_addrs)
> +
> +The struct cec_log_addrs is following:
> +
> +struct cec_log_addrs {
> +	__u8 cec_version;
> +	__u8 num_log_addrs;
> +	__u8 primary_device_type[CEC_MAX_LOG_ADDRS];
> +	__u8 log_addr_type[CEC_MAX_LOG_ADDRS];
> +	__u8 log_addr[CEC_MAX_LOG_ADDRS];
> +
> +	/* CEC 2.0 */
> +	__u8 all_device_types;
> +	__u8 features[CEC_MAX_LOG_ADDRS][12];
> +
> +	__u8 reserved[9];
> +};
> +
> +The cec_version determines which CEC version should be used.
> +
> +/* The CEC version */
> +#define CEC_VERSION_1_4B		5
> +#define CEC_VERSION_2_0			6
> +
> +It will try to claim num_log_addrs devices. The log_addr_type array has
> +the logical address type that needs to be claimed for that device, and
> +the log_addr array will receive the actual logical address that was
> +claimed for that device or 0xff if no address could be claimed.
> +
> +The primary_device_type contains the primary device for each logical
> +address.
> +
> +For CEC 2.0 devices fill in the all_device_types parameter to use with the
> +Report Features command, and fill in the 'features' which contains the
> +remaining parameters (RC Profile and Device Features) to use in Report
> +Features.
> +
> +An error is returned if the adapter is disabled or if there
> +is no physical address assigned or if the cec_version is unknown.
> +
> +If no logical address of one or more of the given types could be claimed,
> +then log_addr will be set to CEC_LOG_ADDR_INVALID.
> +
> +If no logical address could be claimed at all, then num_log_addrs will
> +be set to 1, log_addr_type[0] to UNREGISTERED and log_addr[0] to 0xf.
> +
> +The S_ADAP_LOG_ADDRS ioctl is not available unless CEC_CAP_LOG_ADDRS
> +is set.
> +
> +- CEC_G_ADAP_STATE and CEC_S_ADAP_STATE ioctls
> +
> +Enable/disable the adapter. The S_ADAP_STATE ioctl is not available
> +unless CEC_CAP_STATE is set.
> +
> +#define CEC_G_ADAP_STATE	_IOR('a', 5, __u32)
> +#define CEC_S_ADAP_STATE	_IOW('a', 6, __u32)
> +
> +State CEC_STATE_DISABLED means the adapter is disabled, CEC_STATE_ENABLED
> +stands for adapter enabled.
> +
> +/* The CEC state */
> +#define CEC_STATE_DISABLED		0
> +#define CEC_STATE_ENABLED		1
> +
> +- CEC_G_ADAP_PHYS_ADDR and CEC_S_ADAP_PHYS_ADDR ioctls
> +
> +phys_addr is either 0 (if this is the CEC root device) or a valid physical
> +address obtained from the EDID of the sink as read by this CEC device (if this
> +is a source device) or a physical address obtained and modified from
> +the EDID of the sink and used for a sink CEC device.  If nothing is connected,
> +then phys_addr is 0xffff.  See HDMI 1.4b, section 8.7 (Physical Address).
> +
> +#define CEC_G_ADAP_PHYS_ADDR	_IOR('a', 7, __u16)
> +#define CEC_S_ADAP_PHYS_ADDR	_IOW('a', 8, __u16)
> +
> +The S_ADAP_PHYS_ADDR ioctl is not available unless CEC_CAP_PHYS_ADDR
> +is set.
> +
> +- CEC_G_EVENT ioctl
> +
> +This ioctl is used to read a pending event. It takes a struct cec_event
> +that is filled with appropriate data.
> +
> +The struct cec_event is following:
> +
> +struct cec_event {
> +	struct cec_time ts;
> +	__u32 event;
> +	__u32 reserved[4];
> +};
> +
> +- CEC_G_VENDOR_ID and CEC_S_VENDOR_ID ioctls
> +
> +These calls are used to read or set the vendor ID of the adapter.
> +
> +#define CEC_G_VENDOR_ID		_IOR('a', 9, __u32)
> +#define CEC_S_VENDOR_ID		_IOW('a', 10, __u32)
> +
> +Vendor ID is a 24 bit identifier obtained from the IEEE Registration
> +Authority Committee.
> +
> +The CEC_S_ADAP_VENDOR_ID ioctl is not available unless CEC_CAP_VENDOR_ID
> +is set.
> +
> +Events
> +------
> +
> +The CEC framework provides a way for the userspace to be informed about
> +a number of event that can occur in the hardware.
> +
> +The userspace is informed about a new event with the POLLPRI event of the
> +poll function.
> +
> +The list of events is following:
> +
> +/* Event that occurs when a cable is connected */
> +#define CEC_EVENT_CONNECT	1
> +/* Event that occurs when all logical addresses were claimed */
> +#define CEC_EVENT_READY		2
> +/* Event that is sent when the cable is disconnected */
> +#define CEC_EVENT_DISCONNECT	3
> +
> +The events can be read with the CEC_G_EVENT ioctl.
> +
> +Remote control handling
> +-----------------------
> +
> +The CEC framework handles the key up/down messages of remote control and
> +provides the key events via the RC framework.
> diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
> index 3ef0f90..262e9ad 100644
> --- a/drivers/media/Kconfig
> +++ b/drivers/media/Kconfig
> @@ -15,6 +15,12 @@ if MEDIA_SUPPORT
>
>   comment "Multimedia core support"
>
> +config CEC
> +	tristate "CEC API (EXPERIMENTAL)"
> +	select RC_CORE
> +	---help---
> +	  Enable the CEC API.
> +
>   #
>   # Multimedia support - automatically enable V4L2 and DVB core
>   #
> diff --git a/drivers/media/Makefile b/drivers/media/Makefile
> index e608bbc..db66014 100644
> --- a/drivers/media/Makefile
> +++ b/drivers/media/Makefile
> @@ -2,6 +2,8 @@
>   # Makefile for the kernel multimedia device drivers.
>   #
>
> +obj-$(CONFIG_CEC) += cec.o
> +
>   media-objs	:= media-device.o media-devnode.o media-entity.o
>
>   #
> diff --git a/drivers/media/cec.c b/drivers/media/cec.c
> new file mode 100644
> index 0000000..bf5cc07
> --- /dev/null
> +++ b/drivers/media/cec.c
> @@ -0,0 +1,1161 @@
> +#include <linux/errno.h>
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/kmod.h>
> +#include <linux/ktime.h>
> +#include <linux/slab.h>
> +#include <linux/mm.h>
> +#include <linux/string.h>
> +#include <linux/types.h>
> +#include <linux/uaccess.h>
> +#include <media/cec.h>
> +
> +#define CEC_NUM_DEVICES	256
> +#define CEC_NAME	"cec"
> +
> +static int debug;
> +module_param(debug, int, 0644);
> +MODULE_PARM_DESC(debug, "debug level (0-1)");
> +
> +struct cec_transmit_notifier {
> +	struct completion c;
> +	struct cec_data *data;
> +};
> +
> +#define dprintk(fmt, arg...)						\
> +	do {								\
> +		if (debug)						\
> +			pr_info("cec-%s: " fmt, adap->name, ## arg);	\
> +	} while (0)
> +
> +static dev_t cec_dev_t;
> +
> +/* Active devices */
> +static DEFINE_MUTEX(cec_devnode_lock);
> +static DECLARE_BITMAP(cec_devnode_nums, CEC_NUM_DEVICES);
> +
> +/* dev to cec_devnode */
> +#define to_cec_devnode(cd) container_of(cd, struct cec_devnode, dev)
> +
> +static inline struct cec_devnode *cec_devnode_data(struct file *filp)
> +{
> +	return filp->private_data;
> +}
> +
> +static int cec_log_addr2idx(const struct cec_adapter *adap, u8 log_addr)
> +{
> +	int i;
> +
> +	for (i = 0; i < adap->num_log_addrs; i++)
> +		if (adap->log_addr[i] == log_addr)
> +			return i;
> +	return -1;
> +}
> +
> +static unsigned cec_log_addr2dev(const struct cec_adapter *adap, u8 log_addr)
> +{
> +	int i = cec_log_addr2idx(adap, log_addr);
> +
> +	return adap->prim_device[i < 0 ? 0 : i];
> +}
> +
> +/* Called when the last user of the cec device exits. */
> +static void cec_devnode_release(struct device *cd)
> +{
> +	struct cec_devnode *cecdev = to_cec_devnode(cd);
> +
> +	mutex_lock(&cec_devnode_lock);
> +
> +	/* Delete the cdev on this minor as well */
> +	cdev_del(&cecdev->cdev);
> +
> +	/* Mark device node number as free */
> +	clear_bit(cecdev->minor, cec_devnode_nums);
> +
> +	mutex_unlock(&cec_devnode_lock);
> +
> +	/* Release cec_devnode and perform other cleanups as needed. */
> +	if (cecdev->release)
> +		cecdev->release(cecdev);
> +}
> +
> +static struct bus_type cec_bus_type = {
> +	.name = CEC_NAME,
> +};
> +
> +static bool cec_sleep(struct cec_adapter *adap, int timeout)
> +{
> +	bool timed_out = false;
> +
> +	DECLARE_WAITQUEUE(wait, current);
> +
> +	add_wait_queue(&adap->kthread_waitq, &wait);
> +	if (!kthread_should_stop()) {
> +		if (timeout < 0) {
> +			set_current_state(TASK_INTERRUPTIBLE);
> +			schedule();
> +		} else {
> +			timed_out = !schedule_timeout_interruptible
> +				(msecs_to_jiffies(timeout));
> +		}
> +	}
> +
> +	remove_wait_queue(&adap->kthread_waitq, &wait);
> +	return timed_out;
> +}
> +
> +/*
> + * Main CEC state machine
> + *
> + * In the IDLE state the CEC adapter is ready to receive or transmit messages.
> + * If it is woken up it will check if a new message is queued, and if so it
> + * will be transmitted and the state will go to TRANSMITTING.
> + *
> + * When the transmit is marked as done the state machine will check if it
> + * should wait for a reply. If not, it will call the notifier and go back
> + * to the IDLE state. Else it will switch to the WAIT state and wait for a
> + * reply. When the reply arrives it will call the notifier and go back
> + * to IDLE state.
> + *
> + * For the transmit and the wait-for-reply states a timeout is used of
> + * 1 second as per the standard.
> + */
> +static int cec_thread_func(void *data)
> +{
> +	struct cec_adapter *adap = data;
> +	int timeout = -1;
> +
> +	for (;;) {
> +		bool timed_out = cec_sleep(adap, timeout);
> +
> +		if (kthread_should_stop())
> +			break;
> +		timeout = -1;
> +		mutex_lock(&adap->lock);
> +		dprintk("state %d timedout: %d tx: %d@%d\n", adap->state,
> +			timed_out, adap->tx_qcount, adap->tx_qstart);
> +		if (adap->state == CEC_ADAP_STATE_TRANSMITTING && timed_out)
> +			adap->adap_transmit_timed_out(adap);
> +
> +		if (adap->state == CEC_ADAP_STATE_WAIT ||
> +		    adap->state == CEC_ADAP_STATE_TRANSMITTING) {
> +			struct cec_data *data = adap->tx_queue +
> +						adap->tx_qstart;
> +
> +			if (adap->state == CEC_ADAP_STATE_TRANSMITTING &&
> +			    data->msg.reply && !timed_out &&
> +			    data->msg.status == CEC_TX_STATUS_OK) {
> +				adap->state = CEC_ADAP_STATE_WAIT;
> +				timeout = 1000;
> +			} else {
> +				if (timed_out) {
> +					data->msg.reply = 0;
> +					if (adap->state ==
> +					    CEC_ADAP_STATE_TRANSMITTING)
> +						data->msg.status =
> +						    CEC_TX_STATUS_RETRY_TIMEOUT;
> +					else
> +						data->msg.status =
> +						    CEC_TX_STATUS_REPLY_TIMEOUT;
> +				}
> +				adap->state = CEC_ADAP_STATE_IDLE;
> +				if (data->func) {
> +					mutex_unlock(&adap->lock);
> +					data->func(adap, data, data->priv);
> +					mutex_lock(&adap->lock);
> +				}
> +				adap->tx_qstart = (adap->tx_qstart + 1) %
> +						  CEC_TX_QUEUE_SZ;
> +				adap->tx_qcount--;
> +				wake_up_interruptible(&adap->waitq);
> +			}
> +		}
> +		if (adap->state == CEC_ADAP_STATE_IDLE && adap->tx_qcount) {
> +			adap->state = CEC_ADAP_STATE_TRANSMITTING;
> +			timeout = adap->tx_queue[adap->tx_qstart].msg.len == 1 ?
> +				  200 : 1000;
> +			adap->adap_transmit(adap,
> +					  &adap->tx_queue[adap->tx_qstart].msg);
> +			mutex_unlock(&adap->lock);
> +			continue;
> +		}
> +		mutex_unlock(&adap->lock);
> +	}
> +	return 0;
> +}
> +
> +static int cec_transmit_notify(struct cec_adapter *adap, struct cec_data *data,
> +		void *priv)
> +{
> +	struct cec_transmit_notifier *n = priv;
> +
> +	*(n->data) = *data;
> +	complete(&n->c);
> +	return 0;
> +}
> +
> +int cec_transmit_msg(struct cec_adapter *adap, struct cec_data *data,
> +		     bool block)
> +{
> +	struct cec_transmit_notifier notifier;
> +	struct cec_msg *msg = &data->msg;
> +	int res = 0;
> +	unsigned idx;
> +
> +	if (msg->len == 0 || msg->len > 16)
> +		return -EINVAL;
> +	if (msg->reply && (msg->len == 1 || cec_msg_is_broadcast(msg)))
> +		return -EINVAL;
> +	if (msg->len > 1 && !cec_msg_is_broadcast(msg) &&
> +	    cec_msg_initiator(msg) == cec_msg_destination(msg))
> +		return -EINVAL;
> +	if (cec_msg_initiator(msg) != 0xf &&
> +	    cec_log_addr2idx(adap, cec_msg_initiator(msg)) < 0)
> +		return -EINVAL;
> +
> +	if (msg->len == 1)
> +		dprintk("cec_transmit_msg: 0x%02x%s\n",
> +				msg->msg[0], !block ? " nb" : "");
> +	else if (msg->reply)
> +		dprintk("cec_transmit_msg: 0x%02x 0x%02x (wait for 0x%02x)%s\n",
> +				msg->msg[0], msg->msg[1],
> +				msg->reply, !block ? " nb" : "");
> +	else
> +		dprintk("cec_transmit_msg: 0x%02x 0x%02x%s\n",
> +				msg->msg[0], msg->msg[1],
> +				!block ? " nb" : "");
> +
> +	msg->status = 0;
> +	memset(&msg->ts, 0, sizeof(msg->ts));
> +	if (msg->reply)
> +		msg->timeout = 1000;
> +	if (block) {
> +		init_completion(&notifier.c);
> +		notifier.data = data;
> +		data->func = cec_transmit_notify;
> +		data->priv = &notifier;
> +	} else {
> +		data->func = NULL;
> +		data->priv = NULL;
> +	}
> +	mutex_lock(&adap->lock);
> +	idx = (adap->tx_qstart + adap->tx_qcount) % CEC_TX_QUEUE_SZ;
> +	if (adap->tx_qcount == CEC_TX_QUEUE_SZ) {
> +		res = -EBUSY;
> +	} else {
> +		adap->tx_queue[idx] = *data;
> +		adap->tx_qcount++;
> +		if (adap->state == CEC_ADAP_STATE_IDLE)
> +			wake_up_interruptible(&adap->kthread_waitq);
> +	}
> +	mutex_unlock(&adap->lock);
> +	if (res || !block)
> +		return res;
> +	wait_for_completion_interruptible(&notifier.c);
> +	return res;
> +}
> +EXPORT_SYMBOL_GPL(cec_transmit_msg);
> +
> +void cec_transmit_done(struct cec_adapter *adap, u32 status)
> +{
> +	struct cec_msg *msg;
> +	struct timespec64 ts;
> +
> +	dprintk("cec_transmit_done\n");
> +	mutex_lock(&adap->lock);
> +	if (adap->state == CEC_ADAP_STATE_TRANSMITTING) {
> +		msg = &adap->tx_queue[adap->tx_qstart].msg;
> +		msg->status = status;
> +		if (status)
> +			msg->reply = 0;
> +		ktime_get_ts64(&ts);
> +		msg->ts.sec = ts.tv_sec;
> +		msg->ts.nsec = ts.tv_nsec;
> +		wake_up_interruptible(&adap->kthread_waitq);
> +	}
> +	mutex_unlock(&adap->lock);
> +}
> +EXPORT_SYMBOL_GPL(cec_transmit_done);
> +
> +static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg)
> +{
> +	bool is_broadcast = cec_msg_is_broadcast(msg);
> +	u8 dest_laddr = cec_msg_destination(msg);
> +	u8 devtype = cec_log_addr2dev(adap, dest_laddr);
> +	bool is_directed = cec_log_addr2idx(adap, dest_laddr) >= 0;
> +	struct cec_data tx_data;
> +	int res = 0;
> +	unsigned idx;
> +
> +	if (msg->len <= 1)
> +		return 0;
> +	if (!is_directed && !is_broadcast)
> +		return 0;	/* Not for us */
> +
> +	tx_data.msg.msg[0] = (msg->msg[0] << 4) | (msg->msg[0] >> 4);
> +	tx_data.msg.reply = 0;
> +
> +	if (adap->received) {
> +		res = adap->received(adap, msg);
> +		if (res != -ENOMSG)
> +			return 0;
> +		res = 0;
> +	}
> +
> +	switch (msg->msg[1]) {
> +	case CEC_OP_GET_CEC_VERSION:
> +		if (is_broadcast)
> +			return 0;
> +		tx_data.msg.len = 3;
> +		tx_data.msg.msg[1] = CEC_OP_CEC_VERSION;
> +		tx_data.msg.msg[2] = adap->version;
> +		return cec_transmit_msg(adap, &tx_data, false);
> +
> +	case CEC_OP_GIVE_PHYSICAL_ADDR:
> +		if (!is_directed)
> +			return 0;
> +		/* Do nothing for CEC switches using addr 15 */
> +		if (devtype == CEC_PRIM_DEVTYPE_SWITCH && dest_laddr == 15)
> +			return 0;
> +		tx_data.msg.len = 5;
> +		tx_data.msg.msg[1] = CEC_OP_REPORT_PHYSICAL_ADDR;
> +		tx_data.msg.msg[2] = adap->phys_addr >> 8;
> +		tx_data.msg.msg[3] = adap->phys_addr & 0xff;
> +		tx_data.msg.msg[4] = devtype;
> +		return cec_transmit_msg(adap, &tx_data, false);
> +
> +	case CEC_OP_ABORT:
> +		/* Do nothing for CEC switches */
> +		if (devtype == CEC_PRIM_DEVTYPE_SWITCH)
> +			return 0;
> +		tx_data.msg.len = 4;
> +		tx_data.msg.msg[1] = CEC_OP_FEATURE_ABORT;
> +		tx_data.msg.msg[2] = msg->msg[1];
> +		tx_data.msg.msg[3] = 4;	/* Refused */
> +		return cec_transmit_msg(adap, &tx_data, false);
> +
> +	case CEC_OP_USER_CONTROL_PRESSED:
> +		switch (msg->msg[2]) {
> +		/* Play function, this message can have variable length
> +		 * depending on the specific play function that is used.
> +		 */
> +		case 0x60:
> +			if (msg->len == 3)
> +				rc_keydown(adap->rc, RC_TYPE_CEC,
> +					   msg->msg[2] << 8 | msg->msg[3], 0);
> +			else
> +				rc_keydown(adap->rc, RC_TYPE_CEC, msg->msg[2],
> +					   0);
> +			break;
> +		/* Other function messages that are not handled.
> +		 * Currently the RC framework does not allow to supply an
> +		 * additional parameter to a keypress. These "keys" contain
> +		 * other information such as channel number, an input number
> +		 * etc.
> +		 * For the time being these messages are not processed by the
> +		 * framework and are simply forwarded to the user space.
> +		 */
> +		case 0x67: case 0x68: case 0x69: case 0x6a:
> +			break;
> +		default:
> +			rc_keydown(adap->rc, RC_TYPE_CEC, msg->msg[2], 0);
> +		}
> +		break;
> +	case CEC_OP_USER_CONTROL_RELEASED:
> +		rc_keyup(adap->rc);
> +		return 0;
> +	}
> +
> +	if ((adap->capabilities & CEC_CAP_RECEIVE) == 0)
> +		return 0;
> +	mutex_lock(&adap->lock);
> +	idx = (adap->rx_qstart + adap->rx_qcount) % CEC_RX_QUEUE_SZ;
> +	if (adap->rx_qcount == CEC_RX_QUEUE_SZ) {
> +		res = -EBUSY;
> +	} else {
> +		adap->rx_queue[idx] = *msg;
> +		adap->rx_qcount++;
> +		wake_up_interruptible(&adap->waitq);
> +	}
> +	mutex_unlock(&adap->lock);
> +	return res;
> +}
> +
> +int cec_receive_msg(struct cec_adapter *adap, struct cec_msg *msg, bool block)
> +{
> +	int res;
> +
> +	do {
> +		mutex_lock(&adap->lock);
> +		if (adap->rx_qcount) {
> +			*msg = adap->rx_queue[adap->rx_qstart];
> +			adap->rx_qstart = (adap->rx_qstart + 1) %
> +					  CEC_RX_QUEUE_SZ;
> +			adap->rx_qcount--;
> +			res = 0;
> +		} else {
> +			res = -EAGAIN;
> +		}
> +		mutex_unlock(&adap->lock);
> +		if (!block || !res)
> +			break;
> +		if (msg->timeout) {
> +			res = wait_event_interruptible_timeout(adap->waitq,
> +				adap->rx_qcount,
> +				msecs_to_jiffies(msg->timeout));
> +			if (res == 0)
> +				res = -ETIMEDOUT;
> +			else if (res > 0)
> +				res = 0;
> +		} else {
> +			res = wait_event_interruptible(adap->waitq,
> +				adap->rx_qcount);
> +		}
> +	} while (!res);
> +	return res;
> +}
> +EXPORT_SYMBOL_GPL(cec_receive_msg);
> +
> +void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg)
> +{
> +	struct timespec64 ts;
> +	bool is_reply = false;
> +
> +	mutex_lock(&adap->lock);
> +	ktime_get_ts64(&ts);
> +	msg->ts.sec = ts.tv_sec;
> +	msg->ts.nsec = ts.tv_nsec;
> +	dprintk("cec_received_msg: %02x %02x\n", msg->msg[0], msg->msg[1]);
> +	if (!cec_msg_is_broadcast(msg) && msg->len > 1 &&
> +	    adap->state == CEC_ADAP_STATE_WAIT) {
> +		struct cec_msg *dst = &adap->tx_queue[adap->tx_qstart].msg;
> +
> +		if (msg->msg[1] == dst->reply ||
> +		    msg->msg[1] == CEC_OP_FEATURE_ABORT) {
> +			*dst = *msg;
> +			is_reply = true;
> +			if (msg->msg[1] == CEC_OP_FEATURE_ABORT) {
> +				dst->reply = 0;
> +				dst->status = CEC_TX_STATUS_FEATURE_ABORT;
> +			}
> +			wake_up_interruptible(&adap->kthread_waitq);
> +		}
> +	}
> +	mutex_unlock(&adap->lock);
> +	if (!is_reply)
> +		adap->recv_notifier(adap, msg);
> +}
> +EXPORT_SYMBOL_GPL(cec_received_msg);
> +
> +void cec_post_event(struct cec_adapter *adap, u32 event)
> +{
> +	struct timespec64 ts;
> +	unsigned idx;
> +
> +	mutex_lock(&adap->lock);
> +	if (adap->ev_qcount == CEC_EV_QUEUE_SZ) {
> +		/* Drop oldest event */
> +		adap->ev_qstart = (adap->ev_qstart + 1) % CEC_EV_QUEUE_SZ;
> +		adap->ev_qcount--;
> +	}
> +
> +	idx = (adap->ev_qstart + adap->ev_qcount) % CEC_EV_QUEUE_SZ;
> +
> +	adap->ev_queue[idx].event = event;
> +	ktime_get_ts64(&ts);
> +	adap->ev_queue[idx].ts.sec = ts.tv_sec;
> +	adap->ev_queue[idx].ts.nsec = ts.tv_nsec;
> +
> +	adap->ev_qcount++;
> +	mutex_unlock(&adap->lock);
> +}
> +EXPORT_SYMBOL_GPL(cec_post_event);
> +
> +static int cec_report_phys_addr(struct cec_adapter *adap, unsigned logical_addr)
> +{
> +	struct cec_data data;
> +
> +	/* Report Physical Address */
> +	data.msg.len = 5;
> +	data.msg.msg[0] = (logical_addr << 4) | 0x0f;
> +	data.msg.msg[1] = CEC_OP_REPORT_PHYSICAL_ADDR;
> +	data.msg.msg[2] = adap->phys_addr >> 8;
> +	data.msg.msg[3] = adap->phys_addr & 0xff;
> +	data.msg.msg[4] = cec_log_addr2dev(adap, logical_addr);
> +	data.msg.reply = 0;
> +	dprintk("config: la %d pa %x.%x.%x.%x\n",
> +			logical_addr, cec_phys_addr_exp(adap->phys_addr));
> +	return cec_transmit_msg(adap, &data, true);
> +}
> +
> +int cec_enable(struct cec_adapter *adap, bool enable)
> +{
> +	int ret;
> +
> +	mutex_lock(&adap->lock);
> +	ret = adap->adap_enable(adap, enable);
> +	if (ret) {
> +		mutex_unlock(&adap->lock);
> +		return ret;
> +	}
> +	if (!enable) {
> +		adap->state = CEC_ADAP_STATE_DISABLED;
> +		adap->tx_qcount = 0;
> +		adap->rx_qcount = 0;
> +		adap->ev_qcount = 0;
> +		adap->num_log_addrs = 0;
> +	} else {
> +		adap->state = CEC_ADAP_STATE_UNCONF;
> +	}
> +	mutex_unlock(&adap->lock);
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(cec_enable);
> +
> +struct cec_log_addrs_int {
> +	struct cec_adapter *adap;
> +	struct cec_log_addrs log_addrs;
> +	struct completion c;
> +	bool free_on_exit;
> +	int err;
> +};
> +
> +static int cec_config_log_addrs(struct cec_adapter *adap,
> +				struct cec_log_addrs *log_addrs)
> +{
> +	static const u8 tv_log_addrs[] = {
> +		0, CEC_LOG_ADDR_INVALID
> +	};
> +	static const u8 record_log_addrs[] = {
> +		1, 2, 9, 12, 13, CEC_LOG_ADDR_INVALID
> +	};
> +	static const u8 tuner_log_addrs[] = {
> +		3, 6, 7, 10, 12, 13, CEC_LOG_ADDR_INVALID
> +	};
> +	static const u8 playback_log_addrs[] = {
> +		4, 8, 11, 12, 13, CEC_LOG_ADDR_INVALID
> +	};
> +	static const u8 audiosystem_log_addrs[] = {
> +		5, 12, 13, CEC_LOG_ADDR_INVALID
> +	};
> +	static const u8 specific_use_log_addrs[] = {
> +		14, 12, 13, CEC_LOG_ADDR_INVALID
> +	};
> +	static const u8 unregistered_log_addrs[] = {
> +		CEC_LOG_ADDR_INVALID
> +	};
> +	static const u8 *type2addrs[7] = {
> +		[CEC_LOG_ADDR_TYPE_TV] = tv_log_addrs,
> +		[CEC_LOG_ADDR_TYPE_RECORD] = record_log_addrs,
> +		[CEC_LOG_ADDR_TYPE_TUNER] = tuner_log_addrs,
> +		[CEC_LOG_ADDR_TYPE_PLAYBACK] = playback_log_addrs,
> +		[CEC_LOG_ADDR_TYPE_AUDIOSYSTEM] = audiosystem_log_addrs,
> +		[CEC_LOG_ADDR_TYPE_SPECIFIC] = specific_use_log_addrs,
> +		[CEC_LOG_ADDR_TYPE_UNREGISTERED] = unregistered_log_addrs,
> +	};
> +	struct cec_data data;
> +	u32 claimed_addrs = 0;
> +	int i, j;
> +	int err;
> +
> +	if (adap->phys_addr) {
> +		/* The TV functionality can only map to physical address 0.
> +		   For any other address, try the Specific functionality
> +		   instead as per the spec. */
> +		for (i = 0; i < log_addrs->num_log_addrs; i++)
> +			if (log_addrs->log_addr_type[i] == CEC_LOG_ADDR_TYPE_TV)
> +				log_addrs->log_addr_type[i] =
> +						CEC_LOG_ADDR_TYPE_SPECIFIC;
> +	}
> +
> +	memcpy(adap->prim_device, log_addrs->primary_device_type,
> +			log_addrs->num_log_addrs);
> +	dprintk("physical address: %x.%x.%x.%x, claim %d logical addresses\n",
> +			cec_phys_addr_exp(adap->phys_addr),
> +			log_addrs->num_log_addrs);
> +	adap->num_log_addrs = 0;
> +	adap->state = CEC_ADAP_STATE_IDLE;
> +
> +	/* TODO: remember last used logical addr type to achieve
> +	   faster logical address polling by trying that one first.
> +	 */
> +	for (i = 0; i < log_addrs->num_log_addrs; i++) {
> +		const u8 *la_list = type2addrs[log_addrs->log_addr_type[i]];
> +
> +		if (kthread_should_stop())
> +			return -EINTR;
> +
> +		for (j = 0; la_list[j] != CEC_LOG_ADDR_INVALID; j++) {
> +			u8 log_addr = la_list[j];
> +
> +			if (claimed_addrs & (1 << log_addr))
> +				continue;
> +
> +			/* Send polling message */
> +			data.msg.len = 1;
> +			data.msg.msg[0] = 0xf0 | log_addr;
> +			data.msg.reply = 0;
> +			err = cec_transmit_msg(adap, &data, true);
> +			if (err)
> +				return err;
> +			if (data.msg.status == CEC_TX_STATUS_RETRY_TIMEOUT) {
> +				/* Message not acknowledged, so this logical
> +				   address is free to use. */
> +				claimed_addrs |= 1 << log_addr;
> +				adap->log_addr[adap->num_log_addrs++] =
> +								log_addr;
> +				log_addrs->log_addr[i] = log_addr;
> +				err = adap->adap_log_addr(adap, log_addr);
> +				dprintk("claim addr %d (%d)\n", log_addr,
> +							adap->prim_device[i]);
> +				if (err)
> +					return err;
> +				cec_report_phys_addr(adap, log_addr);
> +				if (adap->claimed_log_addr)
> +					adap->claimed_log_addr(adap, i);
> +				break;
> +			}
> +		}
> +	}
> +	if (adap->num_log_addrs == 0) {
> +		if (log_addrs->num_log_addrs > 1)
> +			dprintk("could not claim last %d addresses\n",
> +				log_addrs->num_log_addrs - 1);
> +		adap->log_addr[adap->num_log_addrs++] = 15;
> +		log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_UNREGISTERED;
> +		log_addrs->log_addr[0] = 15;
> +		log_addrs->num_log_addrs = 1;
> +		err = adap->adap_log_addr(adap, 15);
> +		dprintk("claim addr %d (%d)\n", 15, adap->prim_device[0]);
> +		if (err)
> +			return err;
> +		cec_report_phys_addr(adap, 15);
> +		if (adap->claimed_log_addr)
> +			adap->claimed_log_addr(adap, 0);
> +	}
> +	return 0;
> +}
> +
> +static int cec_config_thread_func(void *arg)
> +{
> +	struct cec_log_addrs_int *cla_int = arg;
> +	int err;
> +
> +	cla_int->err = err = cec_config_log_addrs(cla_int->adap,
> +						  &cla_int->log_addrs);
> +	cla_int->adap->kthread_config = NULL;
> +	if (cla_int->free_on_exit)
> +		kfree(cla_int);
> +	else
> +		complete(&cla_int->c);
> +	return err;
> +}
> +
> +int cec_claim_log_addrs(struct cec_adapter *adap,
> +			struct cec_log_addrs *log_addrs, bool block)
> +{
> +	struct cec_log_addrs_int *cla_int;
> +	int i;
> +
> +	if (adap->state == CEC_ADAP_STATE_DISABLED)
> +		return -EINVAL;
> +
> +	if (log_addrs->num_log_addrs > CEC_MAX_LOG_ADDRS)
> +		return -EINVAL;
> +	if (log_addrs->num_log_addrs == 0) {
> +		adap->num_log_addrs = 0;
> +		adap->state = CEC_ADAP_STATE_IDLE;
> +		return 0;
> +	}
> +	if (log_addrs->cec_version != CEC_VERSION_1_4B &&
> +	    log_addrs->cec_version != CEC_VERSION_2_0)
> +		return -EINVAL;
> +	if (log_addrs->num_log_addrs > 1)
> +		for (i = 0; i < log_addrs->num_log_addrs; i++)
> +			if (log_addrs->log_addr_type[i] ==
> +					CEC_LOG_ADDR_TYPE_UNREGISTERED)
> +				return -EINVAL;
> +	for (i = 0; i < log_addrs->num_log_addrs; i++) {
> +		if (log_addrs->primary_device_type[i] >
> +						CEC_PRIM_DEVTYPE_VIDEOPROC)
> +			return -EINVAL;
> +		if (log_addrs->primary_device_type[i] == 2)
> +			return -EINVAL;
> +		if (log_addrs->log_addr_type[i] >
> +						CEC_LOG_ADDR_TYPE_UNREGISTERED)
> +			return -EINVAL;
> +	}
> +
> +	/* For phys addr 0xffff only the Unregistered functionality is
> +	   allowed. */
> +	if (adap->phys_addr == 0xffff &&
> +	    (log_addrs->num_log_addrs > 1 ||
> +	     log_addrs->log_addr_type[0] != CEC_LOG_ADDR_TYPE_UNREGISTERED))
> +		return -EINVAL;
> +
> +	cla_int = kzalloc(sizeof(*cla_int), GFP_KERNEL);
> +	if (cla_int == NULL)
> +		return -ENOMEM;
> +	init_completion(&cla_int->c);
> +	cla_int->free_on_exit = !block;
> +	cla_int->adap = adap;
> +	cla_int->log_addrs = *log_addrs;
> +	adap->kthread_config = kthread_run(cec_config_thread_func, cla_int,
> +							"cec_log_addrs");
> +	if (block) {
> +		wait_for_completion(&cla_int->c);
> +		*log_addrs = cla_int->log_addrs;
> +		kfree(cla_int);
> +	}
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(cec_claim_log_addrs);
> +
> +static unsigned int cec_poll(struct file *filp,
> +			       struct poll_table_struct *poll)
> +{
> +	struct cec_devnode *cecdev = cec_devnode_data(filp);
> +	struct cec_adapter *adap = to_cec_adapter(cecdev);
> +	unsigned res = 0;
> +
> +	if (!cec_devnode_is_registered(cecdev))
> +		return POLLERR | POLLHUP;
> +	mutex_lock(&adap->lock);
> +	if (adap->tx_qcount < CEC_TX_QUEUE_SZ)
> +		res |= POLLOUT | POLLWRNORM;
> +	if (adap->rx_qcount)
> +		res |= POLLIN | POLLRDNORM;
> +	if (adap->ev_qcount)
> +		res |= POLLPRI;
> +	poll_wait(filp, &adap->waitq, poll);
> +	mutex_unlock(&adap->lock);
> +	return res;
> +}
> +
> +static long cec_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> +{
> +	struct cec_devnode *cecdev = cec_devnode_data(filp);
> +	struct cec_adapter *adap = to_cec_adapter(cecdev);
> +	void __user *parg = (void __user *)arg;
> +	int err;
> +
> +	if (!cec_devnode_is_registered(cecdev))
> +		return -EIO;
> +
> +	switch (cmd) {
> +	case CEC_G_CAPS: {
> +		struct cec_caps caps;
> +
> +		caps.available_log_addrs = 3;
> +		caps.capabilities = adap->capabilities;
> +		caps.version = adap->version;
> +		caps.vendor_id = adap->vendor_id;
> +		if (copy_to_user(parg, &caps, sizeof(caps)))
> +			return -EFAULT;
> +		break;
> +	}
> +
> +	case CEC_TRANSMIT: {
> +		struct cec_data data;
> +
> +		if (!(adap->capabilities & CEC_CAP_TRANSMIT))
> +			return -ENOTTY;
> +		if (copy_from_user(&data.msg, parg, sizeof(data.msg)))
> +			return -EFAULT;
> +		err = cec_transmit_msg(adap, &data,
> +						!(filp->f_flags & O_NONBLOCK));
> +		if (err)
> +			return err;
> +		if (copy_to_user(parg, &data.msg, sizeof(data.msg)))
> +			return -EFAULT;
> +		break;
> +	}
> +
> +	case CEC_RECEIVE: {
> +		struct cec_data data;
> +
> +		if (!(adap->capabilities & CEC_CAP_RECEIVE))
> +			return -ENOTTY;
> +		if (copy_from_user(&data.msg, parg, sizeof(data.msg)))
> +			return -EFAULT;
> +		err = cec_receive_msg(adap, &data.msg,
> +						!(filp->f_flags & O_NONBLOCK));
> +		if (err)
> +			return err;
> +		if (copy_to_user(parg, &data.msg, sizeof(data.msg)))
> +			return -EFAULT;
> +		break;
> +	}
> +
> +	case CEC_G_EVENT: {
> +		struct cec_event ev;
> +
> +		mutex_lock(&adap->lock);
> +		err = -EAGAIN;
> +		if (adap->ev_qcount) {
> +			err = 0;
> +			ev = adap->ev_queue[adap->ev_qstart];
> +			adap->ev_qstart = (adap->ev_qstart + 1) %
> +								CEC_EV_QUEUE_SZ;
> +			adap->ev_qcount--;
> +		}
> +		mutex_unlock(&adap->lock);
> +		if (err)
> +			return err;
> +		if (copy_to_user((void __user *)arg, &ev, sizeof(ev)))
> +			return -EFAULT;
> +		break;
> +	}
> +
> +	case CEC_G_ADAP_STATE: {
> +		u32 state = adap->state != CEC_ADAP_STATE_DISABLED;
> +
> +		if (copy_to_user(parg, &state, sizeof(state)))
> +			return -EFAULT;
> +		break;
> +	}
> +
> +	case CEC_S_ADAP_STATE: {
> +		u32 state;
> +
> +		if (!(adap->capabilities & CEC_CAP_STATE))
> +			return -ENOTTY;
> +		if (copy_from_user(&state, parg, sizeof(state)))
> +			return -EFAULT;
> +		if (!state && adap->state == CEC_ADAP_STATE_DISABLED)
> +			return 0;
> +		if (state && adap->state != CEC_ADAP_STATE_DISABLED)
> +			return 0;
> +		cec_enable(adap, !!state);
> +		break;
> +	}
> +
> +	case CEC_G_ADAP_PHYS_ADDR:
> +		if (copy_to_user(parg, &adap->phys_addr,
> +						sizeof(adap->phys_addr)))
> +			return -EFAULT;
> +		break;
> +
> +	case CEC_S_ADAP_PHYS_ADDR: {
> +		u16 phys_addr;
> +
> +		if (!(adap->capabilities & CEC_CAP_PHYS_ADDR))
> +			return -ENOTTY;
> +		if (copy_from_user(&phys_addr, parg, sizeof(phys_addr)))
> +			return -EFAULT;
> +		adap->phys_addr = phys_addr;
> +		break;
> +	}
> +
> +	case CEC_G_ADAP_LOG_ADDRS: {
> +		struct cec_log_addrs log_addrs;
> +
> +		log_addrs.cec_version = adap->version;
> +		log_addrs.num_log_addrs = adap->num_log_addrs;
> +		memcpy(log_addrs.primary_device_type, adap->prim_device,
> +							CEC_MAX_LOG_ADDRS);
> +		memcpy(log_addrs.log_addr_type, adap->log_addr_type,
> +							CEC_MAX_LOG_ADDRS);
> +		memcpy(log_addrs.log_addr, adap->log_addr,
> +							CEC_MAX_LOG_ADDRS);
> +
> +		if (copy_to_user(parg, &log_addrs, sizeof(log_addrs)))
> +			return -EFAULT;
> +		break;
> +	}
> +
> +	case CEC_S_ADAP_LOG_ADDRS: {
> +		struct cec_log_addrs log_addrs;
> +
> +		if (!(adap->capabilities & CEC_CAP_LOG_ADDRS))
> +			return -ENOTTY;
> +		if (copy_from_user(&log_addrs, parg, sizeof(log_addrs)))
> +			return -EFAULT;
> +		err = cec_claim_log_addrs(adap, &log_addrs, true);
> +		if (err)
> +			return err;
> +
> +		if (copy_to_user(parg, &log_addrs, sizeof(log_addrs)))
> +			return -EFAULT;
> +		break;
> +	}
> +
> +	case CEC_G_VENDOR_ID:
> +		if (copy_to_user(parg, &adap->vendor_id,
> +						sizeof(adap->vendor_id)))
> +			return -EFAULT;
> +		break;
> +
> +	case CEC_S_VENDOR_ID: {
> +		u32 vendor_id;
> +
> +		if (!(adap->capabilities & CEC_CAP_VENDOR_ID))
> +			return -ENOTTY;
> +		if (copy_from_user(&vendor_id, parg, sizeof(vendor_id)))
> +			return -EFAULT;
> +		/* Vendori ID is a 24 bit number, so check if the value is
> +		 * within the correct range. */
> +		if ((vendor_id & 0xff000000) != 0)
> +			return -EINVAL;
> +		adap->vendor_id = vendor_id;
> +		break;
> +	}
> +
> +	default:
> +		return -ENOTTY;
> +	}
> +	return 0;
> +}
> +
> +/* Override for the open function */
> +static int cec_open(struct inode *inode, struct file *filp)
> +{
> +	struct cec_devnode *cecdev;
> +
> +	/* Check if the cec device is available. This needs to be done with
> +	 * the cec_devnode_lock held to prevent an open/unregister race:
> +	 * without the lock, the device could be unregistered and freed between
> +	 * the cec_devnode_is_registered() and get_device() calls, leading to
> +	 * a crash.
> +	 */
> +	mutex_lock(&cec_devnode_lock);
> +	cecdev = container_of(inode->i_cdev, struct cec_devnode, cdev);
> +	/* return ENXIO if the cec device has been removed
> +	   already or if it is not registered anymore. */
> +	if (!cec_devnode_is_registered(cecdev)) {
> +		mutex_unlock(&cec_devnode_lock);
> +		return -ENXIO;
> +	}
> +	/* and increase the device refcount */
> +	get_device(&cecdev->dev);
> +	mutex_unlock(&cec_devnode_lock);
> +
> +	filp->private_data = cecdev;
> +
> +	return 0;
> +}
> +
> +/* Override for the release function */
> +static int cec_release(struct inode *inode, struct file *filp)
> +{
> +	struct cec_devnode *cecdev = cec_devnode_data(filp);
> +	int ret = 0;
> +
> +	/* decrease the refcount unconditionally since the release()
> +	   return value is ignored. */
> +	put_device(&cecdev->dev);
> +	filp->private_data = NULL;
> +	return ret;
> +}
> +
> +static const struct file_operations cec_devnode_fops = {
> +	.owner = THIS_MODULE,
> +	.open = cec_open,
> +	.unlocked_ioctl = cec_ioctl,
> +	.release = cec_release,
> +	.poll = cec_poll,
> +	.llseek = no_llseek,
> +};
> +
> +/**
> + * cec_devnode_register - register a cec device node
> + * @cecdev: cec device node structure we want to register
> + *
> + * The registration code assigns minor numbers and registers the new device node
> + * with the kernel. An error is returned if no free minor number can be found,
> + * or if the registration of the device node fails.
> + *
> + * Zero is returned on success.
> + *
> + * Note that if the cec_devnode_register call fails, the release() callback of
> + * the cec_devnode structure is *not* called, so the caller is responsible for
> + * freeing any data.
> + */
> +static int __must_check cec_devnode_register(struct cec_devnode *cecdev,
> +		struct module *owner)
> +{
> +	int minor;
> +	int ret;
> +
> +	/* Part 1: Find a free minor number */
> +	mutex_lock(&cec_devnode_lock);
> +	minor = find_next_zero_bit(cec_devnode_nums, CEC_NUM_DEVICES, 0);
> +	if (minor == CEC_NUM_DEVICES) {
> +		mutex_unlock(&cec_devnode_lock);
> +		pr_err("could not get a free minor\n");
> +		return -ENFILE;
> +	}
> +
> +	set_bit(minor, cec_devnode_nums);
> +	mutex_unlock(&cec_devnode_lock);
> +
> +	cecdev->minor = minor;
> +
> +	/* Part 2: Initialize and register the character device */
> +	cdev_init(&cecdev->cdev, &cec_devnode_fops);
> +	cecdev->cdev.owner = owner;
> +
> +	ret = cdev_add(&cecdev->cdev, MKDEV(MAJOR(cec_dev_t), cecdev->minor),
> +									1);
> +	if (ret < 0) {
> +		pr_err("%s: cdev_add failed\n", __func__);
> +		goto error;
> +	}
> +
> +	/* Part 3: Register the cec device */
> +	cecdev->dev.bus = &cec_bus_type;
> +	cecdev->dev.devt = MKDEV(MAJOR(cec_dev_t), cecdev->minor);
> +	cecdev->dev.release = cec_devnode_release;
> +	if (cecdev->parent)
> +		cecdev->dev.parent = cecdev->parent;
> +	dev_set_name(&cecdev->dev, "cec%d", cecdev->minor);
> +	ret = device_register(&cecdev->dev);
> +	if (ret < 0) {
> +		pr_err("%s: device_register failed\n", __func__);
> +		goto error;
> +	}
> +
> +	/* Part 4: Activate this minor. The char device can now be used. */
> +	set_bit(CEC_FLAG_REGISTERED, &cecdev->flags);
> +
> +	return 0;
> +
> +error:
> +	cdev_del(&cecdev->cdev);
> +	clear_bit(cecdev->minor, cec_devnode_nums);
> +	return ret;
> +}
> +
> +/**
> + * cec_devnode_unregister - unregister a cec device node
> + * @cecdev: the device node to unregister
> + *
> + * This unregisters the passed device. Future open calls will be met with
> + * errors.
> + *
> + * This function can safely be called if the device node has never been
> + * registered or has already been unregistered.
> + */
> +static void cec_devnode_unregister(struct cec_devnode *cecdev)
> +{
> +	/* Check if cecdev was ever registered at all */
> +	if (!cec_devnode_is_registered(cecdev))
> +		return;
> +
> +	mutex_lock(&cec_devnode_lock);
> +	clear_bit(CEC_FLAG_REGISTERED, &cecdev->flags);
> +	mutex_unlock(&cec_devnode_lock);
> +	device_unregister(&cecdev->dev);
> +}
> +
> +int cec_create_adapter(struct cec_adapter *adap, const char *name, u32 caps)
> +{
> +	int res = 0;
> +
> +	adap->state = CEC_ADAP_STATE_DISABLED;
> +	adap->name = name;
> +	adap->phys_addr = 0xffff;
> +	adap->capabilities = caps;
> +	adap->version = CEC_VERSION_1_4B;
> +	mutex_init(&adap->lock);
> +	adap->kthread = kthread_run(cec_thread_func, adap, name);
> +	init_waitqueue_head(&adap->kthread_waitq);
> +	init_waitqueue_head(&adap->waitq);
> +	if (IS_ERR(adap->kthread)) {
> +		pr_err("cec-%s: kernel_thread() failed\n", name);
> +		return PTR_ERR(adap->kthread);
> +	}
> +	if (caps) {
> +		res = cec_devnode_register(&adap->devnode, adap->owner);
> +		if (res)
> +			kthread_stop(adap->kthread);
> +	}
> +	adap->recv_notifier = cec_receive_notify;
> +
> +	/* Prepare the RC input device */
> +	adap->rc = rc_allocate_device();
> +	if (!adap->rc) {
> +		pr_err("cec-%s: failed to allocate memory for rc_dev\n", name);
> +		cec_devnode_unregister(&adap->devnode);
> +		kthread_stop(adap->kthread);
> +		return -ENOMEM;
> +	}
> +
> +	snprintf(adap->input_name, sizeof(adap->input_name), "RC for %s", name);
> +	snprintf(adap->input_phys, sizeof(adap->input_phys), "%s/input0", name);
> +	strncpy(adap->input_drv, name, sizeof(adap->input_drv));
> +
> +	adap->rc->input_name = adap->input_name;
> +	adap->rc->input_phys = adap->input_phys;
> +	adap->rc->dev.parent = &adap->devnode.dev;
> +	adap->rc->driver_name = adap->input_drv;
> +	adap->rc->driver_type = RC_DRIVER_CEC;
> +	adap->rc->allowed_protocols = RC_BIT_CEC;
> +	adap->rc->priv = adap;
> +	adap->rc->map_name = RC_MAP_CEC;
> +	adap->rc->timeout = MS_TO_NS(100);
> +
> +	res = rc_register_device(adap->rc);
> +
> +	if (res) {
> +		pr_err("cec-%s: failed to prepare input device\n", name);
> +		cec_devnode_unregister(&adap->devnode);
> +		rc_free_device(adap->rc);
> +		kthread_stop(adap->kthread);
> +	}
> +
> +	return res;
> +}
> +EXPORT_SYMBOL_GPL(cec_create_adapter);
> +
> +void cec_delete_adapter(struct cec_adapter *adap)
> +{
> +	if (adap->kthread == NULL)
> +		return;
> +	kthread_stop(adap->kthread);
> +	if (adap->kthread_config)
> +		kthread_stop(adap->kthread_config);
> +	adap->state = CEC_ADAP_STATE_DISABLED;
> +	if (cec_devnode_is_registered(&adap->devnode))
> +		cec_devnode_unregister(&adap->devnode);
> +}
> +EXPORT_SYMBOL_GPL(cec_delete_adapter);
> +
> +/*
> + *	Initialise cec for linux
> + */
> +static int __init cec_devnode_init(void)
> +{
> +	int ret;
> +
> +	pr_info("Linux cec interface: v0.10\n");
> +	ret = alloc_chrdev_region(&cec_dev_t, 0, CEC_NUM_DEVICES,
> +				  CEC_NAME);
> +	if (ret < 0) {
> +		pr_warn("cec: unable to allocate major\n");
> +		return ret;
> +	}
> +
> +	ret = bus_register(&cec_bus_type);
> +	if (ret < 0) {
> +		unregister_chrdev_region(cec_dev_t, CEC_NUM_DEVICES);
> +		pr_warn("cec: bus_register failed\n");
> +		return -EIO;
> +	}
> +
> +	return 0;
> +}
> +
> +static void __exit cec_devnode_exit(void)
> +{
> +	bus_unregister(&cec_bus_type);
> +	unregister_chrdev_region(cec_dev_t, CEC_NUM_DEVICES);
> +}
> +
> +subsys_initcall(cec_devnode_init);
> +module_exit(cec_devnode_exit)
> +
> +MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>");
> +MODULE_DESCRIPTION("Device node registration for cec drivers");
> +MODULE_LICENSE("GPL");
> diff --git a/include/media/cec.h b/include/media/cec.h
> new file mode 100644
> index 0000000..df3b9e93
> --- /dev/null
> +++ b/include/media/cec.h
> @@ -0,0 +1,140 @@
> +#ifndef _CEC_DEVNODE_H
> +#define _CEC_DEVNODE_H
> +
> +#include <linux/poll.h>
> +#include <linux/fs.h>
> +#include <linux/device.h>
> +#include <linux/cdev.h>
> +#include <linux/kthread.h>
> +#include <linux/cec.h>
> +#include <media/rc-core.h>
> +
> +#define cec_phys_addr_exp(pa) \
> +	((pa) >> 12), ((pa) >> 8) & 0xf, ((pa) >> 4) & 0xf, (pa) & 0xf
> +
> +/*
> + * Flag to mark the cec_devnode struct as registered. Drivers must not touch
> + * this flag directly, it will be set and cleared by cec_devnode_register and
> + * cec_devnode_unregister.
> + */
> +#define CEC_FLAG_REGISTERED	0
> +
> +/**
> + * struct cec_devnode - cec device node
> + * @parent:	parent device
> + * @minor:	device node minor number
> + * @flags:	flags, combination of the CEC_FLAG_* constants
> + *
> + * This structure represents a cec-related device node.
> + *
> + * The @parent is a physical device. It must be set by core or device drivers
> + * before registering the node.
> + */
> +struct cec_devnode {
> +	/* sysfs */
> +	struct device dev;		/* cec device */
> +	struct cdev cdev;		/* character device */
> +	struct device *parent;		/* device parent */
> +
> +	/* device info */
> +	int minor;
> +	unsigned long flags;		/* Use bitops to access flags */
> +
> +	/* callbacks */
> +	void (*release)(struct cec_devnode *cecdev);
> +};
> +
> +static inline int cec_devnode_is_registered(struct cec_devnode *cecdev)
> +{
> +	return test_bit(CEC_FLAG_REGISTERED, &cecdev->flags);
> +}
> +
> +struct cec_adapter;
> +struct cec_data;
> +
> +typedef int (*cec_notify)(struct cec_adapter *adap, struct cec_data *data,
> +			  void *priv);
> +typedef int (*cec_recv_notify)(struct cec_adapter *adap, struct cec_msg *msg);
> +
> +struct cec_data {
> +	struct cec_msg msg;
> +	cec_notify func;
> +	void *priv;
> +};
> +
> +/* Unconfigured state */
> +#define CEC_ADAP_STATE_DISABLED		0
> +#define CEC_ADAP_STATE_UNCONF		1
> +#define CEC_ADAP_STATE_IDLE		2
> +#define CEC_ADAP_STATE_TRANSMITTING	3
> +#define CEC_ADAP_STATE_WAIT		4
> +#define CEC_ADAP_STATE_RECEIVED		5
> +
> +#define CEC_TX_QUEUE_SZ	(4)
> +#define CEC_RX_QUEUE_SZ	(4)
> +#define CEC_EV_QUEUE_SZ	(16)
> +
> +struct cec_adapter {
> +	struct module *owner;
> +	const char *name;
> +	struct cec_devnode devnode;
> +	struct mutex lock;
> +	struct rc_dev *rc;
> +
> +	struct cec_data tx_queue[CEC_TX_QUEUE_SZ];
> +	u8 tx_qstart, tx_qcount;
> +
> +	struct cec_msg rx_queue[CEC_RX_QUEUE_SZ];
> +	u8 rx_qstart, rx_qcount;
> +
> +	struct cec_event ev_queue[CEC_EV_QUEUE_SZ];
> +	u8 ev_qstart, ev_qcount;
> +
> +	cec_recv_notify recv_notifier;
> +	struct task_struct *kthread_config;
> +
> +	struct task_struct *kthread;
> +	wait_queue_head_t kthread_waitq;
> +	wait_queue_head_t waitq;
> +
> +	u8 state;
> +	u32 capabilities;
> +	u16 phys_addr;
> +	u32 vendor_id;
> +	u8 version;
> +	u8 num_log_addrs;
> +	u8 prim_device[CEC_MAX_LOG_ADDRS];
> +	u8 log_addr_type[CEC_MAX_LOG_ADDRS];
> +	u8 log_addr[CEC_MAX_LOG_ADDRS];
> +	u8 promiscuous;
> +
> +	char input_name[32];
> +	char input_phys[32];
> +	char input_drv[32];
> +
> +	int (*adap_enable)(struct cec_adapter *adap, bool enable);
> +	int (*adap_log_addr)(struct cec_adapter *adap, u8 logical_addr);
> +	int (*adap_transmit)(struct cec_adapter *adap, struct cec_msg *msg);
> +	void (*adap_transmit_timed_out)(struct cec_adapter *adap);
> +
> +	void (*claimed_log_addr)(struct cec_adapter *adap, u8 idx);
> +	int (*received)(struct cec_adapter *adap, struct cec_msg *msg);
> +};
> +
> +#define to_cec_adapter(node) container_of(node, struct cec_adapter, devnode)
> +
> +int cec_create_adapter(struct cec_adapter *adap, const char *name, u32 caps);
> +void cec_delete_adapter(struct cec_adapter *adap);
> +int cec_transmit_msg(struct cec_adapter *adap, struct cec_data *data,
> +		     bool block);
> +int cec_receive_msg(struct cec_adapter *adap, struct cec_msg *msg, bool block);
> +void cec_post_event(struct cec_adapter *adap, u32 event);
> +int cec_claim_log_addrs(struct cec_adapter *adap,
> +			struct cec_log_addrs *log_addrs, bool block);
> +int cec_enable(struct cec_adapter *adap, bool enable);
> +
> +/* Called by the adapter */
> +void cec_transmit_done(struct cec_adapter *adap, u32 status);
> +void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg);
> +
> +#endif /* _CEC_DEVNODE_H */
> diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
> index 4842a98..5854cfd 100644
> --- a/include/uapi/linux/Kbuild
> +++ b/include/uapi/linux/Kbuild
> @@ -81,6 +81,7 @@ header-y += capi.h
>   header-y += cciss_defs.h
>   header-y += cciss_ioctl.h
>   header-y += cdrom.h
> +header-y += cec.h
>   header-y += cgroupstats.h
>   header-y += chio.h
>   header-y += cm4000_cs.h
> diff --git a/include/uapi/linux/cec.h b/include/uapi/linux/cec.h
> new file mode 100644
> index 0000000..bb6d66c
> --- /dev/null
> +++ b/include/uapi/linux/cec.h
> @@ -0,0 +1,303 @@
> +#ifndef _CEC_H
> +#define _CEC_H
> +
> +#include <linux/types.h>
> +
> +struct cec_time {
> +	__u64 sec;
> +	__u64 nsec;
> +};
> +
> +struct cec_msg {
> +	struct cec_time ts;
> +	__u32 len;
> +	__u32 status;
> +	__u32 timeout;
> +	/* timeout (in ms) is used to timeout CEC_RECEIVE.
> +	   Set to 0 if you want to wait forever. */
> +	__u8  msg[16];
> +	__u8  reply;
> +	/* If non-zero, then wait for a reply with this opcode.
> +	   If there was an error when sending the msg or FeatureAbort
> +	   was returned, then reply is set to 0.
> +	   If reply is non-zero upon return, then len/msg are set to
> +	   the received message.
> +	   If reply is zero upon return and status has the
> +	   CEC_TX_STATUS_FEATURE_ABORT bit set, then len/msg are set to the
> +	   received feature abort message.
> +	   If reply is zero upon return and status has the
> +	   CEC_TX_STATUS_REPLY_TIMEOUT
> +	   bit set, then no reply was seen at all.
> +	   This field is ignored with CEC_RECEIVE.
> +	   If reply is non-zero for CEC_TRANSMIT and the message is a broadcast,
> +	   then -EINVAL is returned.
> +	   if reply is non-zero, then timeout is set to 1000 (the required
> +	   maximum response time).
> +	 */
> +	__u8 reserved[31];
> +};
> +
> +static inline __u8 cec_msg_initiator(const struct cec_msg *msg)
> +{
> +	return msg->msg[0] >> 4;
> +}
> +
> +static inline __u8 cec_msg_destination(const struct cec_msg *msg)
> +{
> +	return msg->msg[0] & 0xf;
> +}
> +
> +static inline bool cec_msg_is_broadcast(const struct cec_msg *msg)
> +{
> +	return (msg->msg[0] & 0xf) == 0xf;
> +}
> +
> +/* cec status field */
> +#define CEC_TX_STATUS_OK            (0)
> +#define CEC_TX_STATUS_ARB_LOST      (1 << 0)
> +#define CEC_TX_STATUS_RETRY_TIMEOUT (1 << 1)
> +#define CEC_TX_STATUS_FEATURE_ABORT (1 << 2)
> +#define CEC_TX_STATUS_REPLY_TIMEOUT (1 << 3)
> +#define CEC_RX_STATUS_READY         (0)
> +
> +#define CEC_LOG_ADDR_INVALID 0xff
> +
> +/* The maximum number of logical addresses one device can be assigned to.
> + * The CEC 2.0 spec allows for only 2 logical addresses at the moment. The
> + * Analog Devices CEC hardware supports 3. So let's go wild and go for 4. */
> +#define CEC_MAX_LOG_ADDRS 4
> +
> +/* The "Primary Device Type" */
> +#define CEC_PRIM_DEVTYPE_TV		0
> +#define CEC_PRIM_DEVTYPE_RECORD		1
> +#define CEC_PRIM_DEVTYPE_TUNER		3
> +#define CEC_PRIM_DEVTYPE_PLAYBACK	4
> +#define CEC_PRIM_DEVTYPE_AUDIOSYSTEM	5
> +#define CEC_PRIM_DEVTYPE_SWITCH		6
> +#define CEC_PRIM_DEVTYPE_VIDEOPROC	7
> +
> +/* The "All Device Types" flags (CEC 2.0) */
> +#define CEC_FL_ALL_DEVTYPE_TV		(1 << 7)
> +#define CEC_FL_ALL_DEVTYPE_RECORD	(1 << 6)
> +#define CEC_FL_ALL_DEVTYPE_TUNER	(1 << 5)
> +#define CEC_FL_ALL_DEVTYPE_PLAYBACK	(1 << 4)
> +#define CEC_FL_ALL_DEVTYPE_AUDIOSYSTEM	(1 << 3)
> +#define CEC_FL_ALL_DEVTYPE_SWITCH	(1 << 2)
> +/* And if you wondering what happened to VIDEOPROC devices: those should
> + * be mapped to a SWITCH. */
> +
> +/* The logical address types that the CEC device wants to claim */
> +#define CEC_LOG_ADDR_TYPE_TV		0
> +#define CEC_LOG_ADDR_TYPE_RECORD	1
> +#define CEC_LOG_ADDR_TYPE_TUNER		2
> +#define CEC_LOG_ADDR_TYPE_PLAYBACK	3
> +#define CEC_LOG_ADDR_TYPE_AUDIOSYSTEM	4
> +#define CEC_LOG_ADDR_TYPE_SPECIFIC	5
> +#define CEC_LOG_ADDR_TYPE_UNREGISTERED	6
> +/* Switches should use UNREGISTERED.
> + * Video processors should use SPECIFIC. */
> +
> +/* The CEC version */
> +#define CEC_VERSION_1_4B		5
> +#define CEC_VERSION_2_0			6
> +
> +struct cec_event {
> +	struct cec_time ts;
> +	__u32 event;
> +	__u8 reserved[4];
> +};
> +
> +/* The CEC state */
> +#define CEC_STATE_DISABLED		0
> +#define CEC_STATE_ENABLED		1
> +
> +/* Userspace has to configure the adapter state (enable/disable) */
> +#define CEC_CAP_STATE		(1 << 0)
> +/* Userspace has to configure the physical address */
> +#define CEC_CAP_PHYS_ADDR	(1 << 1)
> +/* Userspace has to configure the logical addresses */
> +#define CEC_CAP_LOG_ADDRS	(1 << 2)
> +/* Userspace can transmit messages */
> +#define CEC_CAP_TRANSMIT	(1 << 3)
> +/* Userspace can receive messages */
> +#define CEC_CAP_RECEIVE		(1 << 4)
> +/* Userspace has to configure the vendor id */
> +#define CEC_CAP_VENDOR_ID	(1 << 5)
> +/* The hardware has the possibility to work in the promiscuous mode */
> +#define CEC_CAP_PROMISCUOUS	(1 << 6)
> +
> +struct cec_caps {
> +	__u32 available_log_addrs;
> +	__u32 capabilities;
> +	__u32 vendor_id;
> +	__u8  version;
> +	__u8  reserved[11];
> +};
> +
> +struct cec_log_addrs {
> +	__u8 cec_version;
> +	__u8 num_log_addrs;
> +	__u8 primary_device_type[CEC_MAX_LOG_ADDRS];
> +	__u8 log_addr_type[CEC_MAX_LOG_ADDRS];
> +	__u8 log_addr[CEC_MAX_LOG_ADDRS];
> +
> +	/* CEC 2.0 */
> +	__u8 all_device_types;
> +	__u8 features[CEC_MAX_LOG_ADDRS][12];
> +
> +	__u8 reserved[9];
> +};
> +
> +/* Commands */
> +
> +/* One Touch Play Feature */
> +#define CEC_OP_ACTIVE_SOURCE			0x82
> +#define CEC_OP_IMAGE_VIEW_ON			0x04
> +#define CEC_OP_TEXT_VIEW_ON			0x0d
> +
> +/* Routing Control Feature */
> +#define CEC_OP_ACTIVE_SOURCE			0x82
> +#define CEC_OP_INACTIVE_SOURCE			0x9d
> +#define CEC_OP_REQUEST_ACTIVE_SOURCE		0x85
> +#define CEC_OP_ROUTING_CHANGE			0x80
> +#define CEC_OP_ROUTING_INFORMATION		0x81
> +#define CEC_OP_SET_STREAM_PATH			0x86
> +
> +/* Standby Feature */
> +#define CEC_OP_STANDBY				0x36
> +
> +/* One Touch Record Feature */
> +#define CEC_OP_RECORD_OFF			0x0b
> +#define CEC_OP_RECORD_ON			0x09
> +#define CEC_OP_RECORD_STATUS			0x0a
> +#define CEC_OP_RECORD_TV_SCREEN			0x0f
> +
> +/* Timer Programming Feature */
> +#define CEC_OP_CLEAR_ANALOGUE_TIMER		0x33
> +#define CEC_OP_CLEAR_DIGITAL_TIMER		0x99
> +#define CEC_OP_CLEAR_EXT_TIMER			0xa1
> +#define CEC_OP_SET_ANALOGUE_TIMER		0x34
> +#define CEC_OP_SET_DIGITAL_TIMER		0x97
> +#define CEC_OP_SET_EXT_TIMER			0xa2
> +#define CEC_OP_SET_EXT_PROGRAM_TIMER		0x67
> +#define CEC_OP_TIMER_CLEARED_STATUS		0x43
> +#define CEC_OP_TIMER_STATUS			0x35
> +
> +/* System Information Feature */
> +#define CEC_OP_CEC_VERSION			0x9e
> +#define CEC_OP_GET_CEC_VERSION			0x9f
> +#define CEC_OP_GIVE_PHYSICAL_ADDR		0x83
> +#define CEC_OP_GET_MENU_LANGUAGE		0x91
> +#define CEC_OP_REPORT_PHYSICAL_ADDR		0x84
> +#define CEC_OP_SET_MENU_LANGUAGE		0x32
> +
> +/* Deck Control Feature */
> +#define CEC_OP_DECK_CONTROL			0x42
> +#define CEC_OP_DECK_STATUS			0x1b
> +#define CEC_OP_GIVE_DECK_STATUS			0x1a
> +#define CEC_OP_PLAY				0x41
> +
> +/* Tuner Control Feature */
> +#define CEC_OP_GIVE_TUNER_DEVICE_STATUS		0x08
> +#define CEC_OP_SELECT_ANALOGUE_SERVICE		0x92
> +#define CEC_OP_SELECT_DIGITAL_SERVICE		0x93
> +#define CEC_OP_TUNER_DEVICE_STATUS		0x07
> +#define CEC_OP_TUNER_STEP_DECREMENT		0x06
> +#define CEC_OP_TUNER_STEP_INCREMENT		0x05
> +
> +/* Vendor Specific Commands Feature */
> +#define CEC_OP_CEC_VERSION			0x9e
> +#define CEC_OP_DEVICE_VENDOR_ID			0x87
> +#define CEC_OP_GET_CEC_VERSION			0x9f
> +#define CEC_OP_GIVE_DEVICE_VENDOR_ID		0x8c
> +#define CEC_OP_VENDOR_COMMAND			0x89
> +#define CEC_OP_VENDOR_COMMAND_WITH_ID		0xa0
> +#define CEC_OP_VENDOR_REMOTE_BUTTON_DOWN	0x8a
> +#define CEC_OP_VENDOR_REMOTE_BUTTON_UP		0x8b
> +
> +/* OSD Display Feature */
> +#define CEC_OP_SET_OSD_STRING			0x64
> +
> +/* Device OSD Transfer Feature */
> +#define CEC_OP_GIVE_OSD_NAME			0x46
> +#define CEC_OP_SET_OSD_NAME			0x47
> +
> +/* Device Menu Control Feature */
> +#define CEC_OP_MENU_REQUEST			0x8d
> +#define CEC_OP_MENU_STATUS			0x8e
> +#define CEC_OP_USER_CONTROL_PRESSED		0x44
> +#define CEC_OP_USER_CONTROL_RELEASED		0x45
> +
> +/* Power Status Feature */
> +#define CEC_OP_GIVE_DEVICE_POWER_STATUS		0x8f
> +#define CEC_OP_REPORT_POWER_STATUS		0x90
> +#define CEC_OP_FEATURE_ABORT			0x00
> +#define CEC_OP_ABORT				0xff
> +
> +/* System Audio Control Feature */
> +#define CEC_OP_GIVE_AUDIO_STATUS		0x71
> +#define CEC_OP_GIVE_SYSTEM_AUDIO_MODE_STATUS	0x7d
> +#define CEC_OP_REPORT_AUDIO_STATUS		0x7a
> +#define CEC_OP_SET_SYSTEM_AUDIO_MODE		0x72
> +#define CEC_OP_SYSTEM_AUDIO_MODE_REQUEST	0x70
> +#define CEC_OP_SYSTEM_AUDIO_MODE_STATUS		0x7e
> +
> +/* Audio Rate Control Feature */
> +#define CEC_OP_SET_AUDIO_RATE			0x9a
> +
> +/* Events */
> +/* Event that occurs when a cable is connected */
> +#define CEC_EVENT_CONNECT	1
> +/* Event that occurs when all logical addresses were claimed */
> +#define CEC_EVENT_READY		2
> +/* Event that is sent when the cable is disconnected */
> +#define CEC_EVENT_DISCONNECT	3
> +
> +/* ioctls */
> +
> +/* issue a CEC command */
> +#define CEC_G_CAPS		_IOWR('a', 0, struct cec_caps)
> +#define CEC_TRANSMIT		_IOWR('a', 1, struct cec_msg)
> +#define CEC_RECEIVE		_IOWR('a', 2, struct cec_msg)
> +
> +/*
> +   Configure the CEC adapter. It sets the device type and which
> +   logical types it will try to claim. It will return which
> +   logical addresses it could actually claim.
> +   An error is returned if the adapter is disabled or if there
> +   is no physical address assigned.
> + */
> +
> +#define CEC_G_ADAP_LOG_ADDRS	_IOR('a', 3, struct cec_log_addrs)
> +#define CEC_S_ADAP_LOG_ADDRS	_IOWR('a', 4, struct cec_log_addrs)
> +
> +/*
> +   Enable/disable the adapter. The Set state ioctl may not
> +   be available if that is handled internally.
> + */
> +#define CEC_G_ADAP_STATE	_IOR('a', 5, __u32)
> +#define CEC_S_ADAP_STATE	_IOW('a', 6, __u32)
> +
> +/*
> +   phys_addr is either 0 (if this is the CEC root device)
> +   or a valid physical address obtained from the sink's EDID
> +   as read by this CEC device (if this is a source device)
> +   or a physical address obtained and modified from a sink
> +   EDID and used for a sink CEC device.
> +   If nothing is connected, then phys_addr is 0xffff.
> +   See HDMI 1.4b, section 8.7 (Physical Address).
> +
> +   The Set ioctl may not be available if that is handled
> +   internally.
> + */
> +#define CEC_G_ADAP_PHYS_ADDR	_IOR('a', 7, __u16)
> +#define CEC_S_ADAP_PHYS_ADDR	_IOW('a', 8, __u16)
> +
> +#define CEC_G_EVENT		_IOWR('a', 9, struct cec_event)
> +/*
> +   Read and set the vendor ID of the CEC adapter.
> + */
> +#define CEC_G_VENDOR_ID		_IOR('a', 9, __u32)
> +#define CEC_S_VENDOR_ID		_IOW('a', 10, __u32)
> +
> +#endif
>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v4 06/10] cec: add HDMI CEC framework: y2038 question
  2015-04-23 13:03 ` [PATCH v4 06/10] cec: add HDMI CEC framework Kamil Debski
  2015-04-23 14:18   ` Hans Verkuil
  2015-04-24 10:03   ` Lars Op den Kamp
@ 2015-04-27  7:40   ` Hans Verkuil
  2015-05-04  7:42     ` [Y2038] " Hans Verkuil
  2015-04-27  9:13   ` [PATCH v4 06/10] cec: add HDMI CEC framework Hans Verkuil
                     ` (3 subsequent siblings)
  6 siblings, 1 reply; 27+ messages in thread
From: Hans Verkuil @ 2015-04-27  7:40 UTC (permalink / raw)
  To: y2038
  Cc: Kamil Debski, dri-devel, linux-media, m.szyprowski, mchehab,
	kyungmin.park, thomas, sean, dmitry.torokhov, linux-input,
	linux-samsung-soc

Added the y2038 mailinglist since I would like to get their input for
this API.

Y2038 experts, can you take a look at my comments in the code below?

Thanks!

On 04/23/2015 03:03 PM, Kamil Debski wrote:
> From: Hans Verkuil <hansverk@cisco.com>
> 
> The added HDMI CEC framework provides a generic kernel interface for
> HDMI CEC devices.
> 
> Signed-off-by: Hans Verkuil <hansverk@cisco.com>
> [k.debski@samsung.com: Merged CEC Updates commit by Hans Verkuil]
> [k.debski@samsung.com: Merged Update author commit by Hans Verkuil]
> [k.debski@samsung.com: change kthread handling when setting logical
> address]
> [k.debski@samsung.com: code cleanup and fixes]
> [k.debski@samsung.com: add missing CEC commands to match spec]
> [k.debski@samsung.com: add RC framework support]
> [k.debski@samsung.com: move and edit documentation]
> [k.debski@samsung.com: add vendor id reporting]
> [k.debski@samsung.com: add possibility to clear assigned logical
> addresses]
> [k.debski@samsung.com: documentation fixes, clenaup and expansion]
> [k.debski@samsung.com: reorder of API structs and add reserved fields]
> [k.debski@samsung.com: fix handling of events and fix 32/64bit timespec
> problem]
> [k.debski@samsung.com: add cec.h to include/uapi/linux/Kbuild]
> Signed-off-by: Kamil Debski <k.debski@samsung.com>
> ---
>  Documentation/cec.txt     |  396 ++++++++++++++++
>  drivers/media/Kconfig     |    6 +
>  drivers/media/Makefile    |    2 +
>  drivers/media/cec.c       | 1161 +++++++++++++++++++++++++++++++++++++++++++++
>  include/media/cec.h       |  140 ++++++
>  include/uapi/linux/Kbuild |    1 +
>  include/uapi/linux/cec.h  |  303 ++++++++++++
>  7 files changed, 2009 insertions(+)
>  create mode 100644 Documentation/cec.txt
>  create mode 100644 drivers/media/cec.c
>  create mode 100644 include/media/cec.h
>  create mode 100644 include/uapi/linux/cec.h
> 

<snip>

> diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
> index 4842a98..5854cfd 100644
> --- a/include/uapi/linux/Kbuild
> +++ b/include/uapi/linux/Kbuild
> @@ -81,6 +81,7 @@ header-y += capi.h
>  header-y += cciss_defs.h
>  header-y += cciss_ioctl.h
>  header-y += cdrom.h
> +header-y += cec.h
>  header-y += cgroupstats.h
>  header-y += chio.h
>  header-y += cm4000_cs.h
> diff --git a/include/uapi/linux/cec.h b/include/uapi/linux/cec.h
> new file mode 100644
> index 0000000..bb6d66c
> --- /dev/null
> +++ b/include/uapi/linux/cec.h
> @@ -0,0 +1,303 @@
> +#ifndef _CEC_H
> +#define _CEC_H
> +
> +#include <linux/types.h>
> +
> +struct cec_time {
> +	__u64 sec;
> +	__u64 nsec;
> +};

I don't like having to introduce a new struct for time here. Basically we are
only doing this because there is still no public struct timespec64.

When will that struct become available for use in a public API? If it is 4.2,
then we can start using it. If it will take longer, then what alternative can
we use to prevent having to change the API later?

One alternative might be to drop it for now and just reserve space in the
structs to add it later.

Input from the y2038 experts will be welcome!

Regards,

	Hans

> +
> +struct cec_msg {
> +	struct cec_time ts;
> +	__u32 len;
> +	__u32 status;
> +	__u32 timeout;
> +	/* timeout (in ms) is used to timeout CEC_RECEIVE.
> +	   Set to 0 if you want to wait forever. */
> +	__u8  msg[16];
> +	__u8  reply;
> +	/* If non-zero, then wait for a reply with this opcode.
> +	   If there was an error when sending the msg or FeatureAbort
> +	   was returned, then reply is set to 0.
> +	   If reply is non-zero upon return, then len/msg are set to
> +	   the received message.
> +	   If reply is zero upon return and status has the
> +	   CEC_TX_STATUS_FEATURE_ABORT bit set, then len/msg are set to the
> +	   received feature abort message.
> +	   If reply is zero upon return and status has the
> +	   CEC_TX_STATUS_REPLY_TIMEOUT
> +	   bit set, then no reply was seen at all.
> +	   This field is ignored with CEC_RECEIVE.
> +	   If reply is non-zero for CEC_TRANSMIT and the message is a broadcast,
> +	   then -EINVAL is returned.
> +	   if reply is non-zero, then timeout is set to 1000 (the required
> +	   maximum response time).
> +	 */
> +	__u8 reserved[31];
> +};
> +
> +static inline __u8 cec_msg_initiator(const struct cec_msg *msg)
> +{
> +	return msg->msg[0] >> 4;
> +}
> +
> +static inline __u8 cec_msg_destination(const struct cec_msg *msg)
> +{
> +	return msg->msg[0] & 0xf;
> +}
> +
> +static inline bool cec_msg_is_broadcast(const struct cec_msg *msg)
> +{
> +	return (msg->msg[0] & 0xf) == 0xf;
> +}
> +
> +/* cec status field */
> +#define CEC_TX_STATUS_OK            (0)
> +#define CEC_TX_STATUS_ARB_LOST      (1 << 0)
> +#define CEC_TX_STATUS_RETRY_TIMEOUT (1 << 1)
> +#define CEC_TX_STATUS_FEATURE_ABORT (1 << 2)
> +#define CEC_TX_STATUS_REPLY_TIMEOUT (1 << 3)
> +#define CEC_RX_STATUS_READY         (0)
> +
> +#define CEC_LOG_ADDR_INVALID 0xff
> +
> +/* The maximum number of logical addresses one device can be assigned to.
> + * The CEC 2.0 spec allows for only 2 logical addresses at the moment. The
> + * Analog Devices CEC hardware supports 3. So let's go wild and go for 4. */
> +#define CEC_MAX_LOG_ADDRS 4
> +
> +/* The "Primary Device Type" */
> +#define CEC_PRIM_DEVTYPE_TV		0
> +#define CEC_PRIM_DEVTYPE_RECORD		1
> +#define CEC_PRIM_DEVTYPE_TUNER		3
> +#define CEC_PRIM_DEVTYPE_PLAYBACK	4
> +#define CEC_PRIM_DEVTYPE_AUDIOSYSTEM	5
> +#define CEC_PRIM_DEVTYPE_SWITCH		6
> +#define CEC_PRIM_DEVTYPE_VIDEOPROC	7
> +
> +/* The "All Device Types" flags (CEC 2.0) */
> +#define CEC_FL_ALL_DEVTYPE_TV		(1 << 7)
> +#define CEC_FL_ALL_DEVTYPE_RECORD	(1 << 6)
> +#define CEC_FL_ALL_DEVTYPE_TUNER	(1 << 5)
> +#define CEC_FL_ALL_DEVTYPE_PLAYBACK	(1 << 4)
> +#define CEC_FL_ALL_DEVTYPE_AUDIOSYSTEM	(1 << 3)
> +#define CEC_FL_ALL_DEVTYPE_SWITCH	(1 << 2)
> +/* And if you wondering what happened to VIDEOPROC devices: those should
> + * be mapped to a SWITCH. */
> +
> +/* The logical address types that the CEC device wants to claim */
> +#define CEC_LOG_ADDR_TYPE_TV		0
> +#define CEC_LOG_ADDR_TYPE_RECORD	1
> +#define CEC_LOG_ADDR_TYPE_TUNER		2
> +#define CEC_LOG_ADDR_TYPE_PLAYBACK	3
> +#define CEC_LOG_ADDR_TYPE_AUDIOSYSTEM	4
> +#define CEC_LOG_ADDR_TYPE_SPECIFIC	5
> +#define CEC_LOG_ADDR_TYPE_UNREGISTERED	6
> +/* Switches should use UNREGISTERED.
> + * Video processors should use SPECIFIC. */
> +
> +/* The CEC version */
> +#define CEC_VERSION_1_4B		5
> +#define CEC_VERSION_2_0			6
> +
> +struct cec_event {
> +	struct cec_time ts;
> +	__u32 event;
> +	__u8 reserved[4];
> +};
> +
> +/* The CEC state */
> +#define CEC_STATE_DISABLED		0
> +#define CEC_STATE_ENABLED		1
> +
> +/* Userspace has to configure the adapter state (enable/disable) */
> +#define CEC_CAP_STATE		(1 << 0)
> +/* Userspace has to configure the physical address */
> +#define CEC_CAP_PHYS_ADDR	(1 << 1)
> +/* Userspace has to configure the logical addresses */
> +#define CEC_CAP_LOG_ADDRS	(1 << 2)
> +/* Userspace can transmit messages */
> +#define CEC_CAP_TRANSMIT	(1 << 3)
> +/* Userspace can receive messages */
> +#define CEC_CAP_RECEIVE		(1 << 4)
> +/* Userspace has to configure the vendor id */
> +#define CEC_CAP_VENDOR_ID	(1 << 5)
> +/* The hardware has the possibility to work in the promiscuous mode */
> +#define CEC_CAP_PROMISCUOUS	(1 << 6)
> +
> +struct cec_caps {
> +	__u32 available_log_addrs;
> +	__u32 capabilities;
> +	__u32 vendor_id;
> +	__u8  version;
> +	__u8  reserved[11];
> +};
> +
> +struct cec_log_addrs {
> +	__u8 cec_version;
> +	__u8 num_log_addrs;
> +	__u8 primary_device_type[CEC_MAX_LOG_ADDRS];
> +	__u8 log_addr_type[CEC_MAX_LOG_ADDRS];
> +	__u8 log_addr[CEC_MAX_LOG_ADDRS];
> +
> +	/* CEC 2.0 */
> +	__u8 all_device_types;
> +	__u8 features[CEC_MAX_LOG_ADDRS][12];
> +
> +	__u8 reserved[9];
> +};
> +
> +/* Commands */
> +
> +/* One Touch Play Feature */
> +#define CEC_OP_ACTIVE_SOURCE			0x82
> +#define CEC_OP_IMAGE_VIEW_ON			0x04
> +#define CEC_OP_TEXT_VIEW_ON			0x0d
> +
> +/* Routing Control Feature */
> +#define CEC_OP_ACTIVE_SOURCE			0x82
> +#define CEC_OP_INACTIVE_SOURCE			0x9d
> +#define CEC_OP_REQUEST_ACTIVE_SOURCE		0x85
> +#define CEC_OP_ROUTING_CHANGE			0x80
> +#define CEC_OP_ROUTING_INFORMATION		0x81
> +#define CEC_OP_SET_STREAM_PATH			0x86
> +
> +/* Standby Feature */
> +#define CEC_OP_STANDBY				0x36
> +
> +/* One Touch Record Feature */
> +#define CEC_OP_RECORD_OFF			0x0b
> +#define CEC_OP_RECORD_ON			0x09
> +#define CEC_OP_RECORD_STATUS			0x0a
> +#define CEC_OP_RECORD_TV_SCREEN			0x0f
> +
> +/* Timer Programming Feature */
> +#define CEC_OP_CLEAR_ANALOGUE_TIMER		0x33
> +#define CEC_OP_CLEAR_DIGITAL_TIMER		0x99
> +#define CEC_OP_CLEAR_EXT_TIMER			0xa1
> +#define CEC_OP_SET_ANALOGUE_TIMER		0x34
> +#define CEC_OP_SET_DIGITAL_TIMER		0x97
> +#define CEC_OP_SET_EXT_TIMER			0xa2
> +#define CEC_OP_SET_EXT_PROGRAM_TIMER		0x67
> +#define CEC_OP_TIMER_CLEARED_STATUS		0x43
> +#define CEC_OP_TIMER_STATUS			0x35
> +
> +/* System Information Feature */
> +#define CEC_OP_CEC_VERSION			0x9e
> +#define CEC_OP_GET_CEC_VERSION			0x9f
> +#define CEC_OP_GIVE_PHYSICAL_ADDR		0x83
> +#define CEC_OP_GET_MENU_LANGUAGE		0x91
> +#define CEC_OP_REPORT_PHYSICAL_ADDR		0x84
> +#define CEC_OP_SET_MENU_LANGUAGE		0x32
> +
> +/* Deck Control Feature */
> +#define CEC_OP_DECK_CONTROL			0x42
> +#define CEC_OP_DECK_STATUS			0x1b
> +#define CEC_OP_GIVE_DECK_STATUS			0x1a
> +#define CEC_OP_PLAY				0x41
> +
> +/* Tuner Control Feature */
> +#define CEC_OP_GIVE_TUNER_DEVICE_STATUS		0x08
> +#define CEC_OP_SELECT_ANALOGUE_SERVICE		0x92
> +#define CEC_OP_SELECT_DIGITAL_SERVICE		0x93
> +#define CEC_OP_TUNER_DEVICE_STATUS		0x07
> +#define CEC_OP_TUNER_STEP_DECREMENT		0x06
> +#define CEC_OP_TUNER_STEP_INCREMENT		0x05
> +
> +/* Vendor Specific Commands Feature */
> +#define CEC_OP_CEC_VERSION			0x9e
> +#define CEC_OP_DEVICE_VENDOR_ID			0x87
> +#define CEC_OP_GET_CEC_VERSION			0x9f
> +#define CEC_OP_GIVE_DEVICE_VENDOR_ID		0x8c
> +#define CEC_OP_VENDOR_COMMAND			0x89
> +#define CEC_OP_VENDOR_COMMAND_WITH_ID		0xa0
> +#define CEC_OP_VENDOR_REMOTE_BUTTON_DOWN	0x8a
> +#define CEC_OP_VENDOR_REMOTE_BUTTON_UP		0x8b
> +
> +/* OSD Display Feature */
> +#define CEC_OP_SET_OSD_STRING			0x64
> +
> +/* Device OSD Transfer Feature */
> +#define CEC_OP_GIVE_OSD_NAME			0x46
> +#define CEC_OP_SET_OSD_NAME			0x47
> +
> +/* Device Menu Control Feature */
> +#define CEC_OP_MENU_REQUEST			0x8d
> +#define CEC_OP_MENU_STATUS			0x8e
> +#define CEC_OP_USER_CONTROL_PRESSED		0x44
> +#define CEC_OP_USER_CONTROL_RELEASED		0x45
> +
> +/* Power Status Feature */
> +#define CEC_OP_GIVE_DEVICE_POWER_STATUS		0x8f
> +#define CEC_OP_REPORT_POWER_STATUS		0x90
> +#define CEC_OP_FEATURE_ABORT			0x00
> +#define CEC_OP_ABORT				0xff
> +
> +/* System Audio Control Feature */
> +#define CEC_OP_GIVE_AUDIO_STATUS		0x71
> +#define CEC_OP_GIVE_SYSTEM_AUDIO_MODE_STATUS	0x7d
> +#define CEC_OP_REPORT_AUDIO_STATUS		0x7a
> +#define CEC_OP_SET_SYSTEM_AUDIO_MODE		0x72
> +#define CEC_OP_SYSTEM_AUDIO_MODE_REQUEST	0x70
> +#define CEC_OP_SYSTEM_AUDIO_MODE_STATUS		0x7e
> +
> +/* Audio Rate Control Feature */
> +#define CEC_OP_SET_AUDIO_RATE			0x9a
> +
> +/* Events */
> +/* Event that occurs when a cable is connected */
> +#define CEC_EVENT_CONNECT	1
> +/* Event that occurs when all logical addresses were claimed */
> +#define CEC_EVENT_READY		2
> +/* Event that is sent when the cable is disconnected */
> +#define CEC_EVENT_DISCONNECT	3
> +
> +/* ioctls */
> +
> +/* issue a CEC command */
> +#define CEC_G_CAPS		_IOWR('a', 0, struct cec_caps)
> +#define CEC_TRANSMIT		_IOWR('a', 1, struct cec_msg)
> +#define CEC_RECEIVE		_IOWR('a', 2, struct cec_msg)
> +
> +/*
> +   Configure the CEC adapter. It sets the device type and which
> +   logical types it will try to claim. It will return which
> +   logical addresses it could actually claim.
> +   An error is returned if the adapter is disabled or if there
> +   is no physical address assigned.
> + */
> +
> +#define CEC_G_ADAP_LOG_ADDRS	_IOR('a', 3, struct cec_log_addrs)
> +#define CEC_S_ADAP_LOG_ADDRS	_IOWR('a', 4, struct cec_log_addrs)
> +
> +/*
> +   Enable/disable the adapter. The Set state ioctl may not
> +   be available if that is handled internally.
> + */
> +#define CEC_G_ADAP_STATE	_IOR('a', 5, __u32)
> +#define CEC_S_ADAP_STATE	_IOW('a', 6, __u32)
> +
> +/*
> +   phys_addr is either 0 (if this is the CEC root device)
> +   or a valid physical address obtained from the sink's EDID
> +   as read by this CEC device (if this is a source device)
> +   or a physical address obtained and modified from a sink
> +   EDID and used for a sink CEC device.
> +   If nothing is connected, then phys_addr is 0xffff.
> +   See HDMI 1.4b, section 8.7 (Physical Address).
> +
> +   The Set ioctl may not be available if that is handled
> +   internally.
> + */
> +#define CEC_G_ADAP_PHYS_ADDR	_IOR('a', 7, __u16)
> +#define CEC_S_ADAP_PHYS_ADDR	_IOW('a', 8, __u16)
> +
> +#define CEC_G_EVENT		_IOWR('a', 9, struct cec_event)
> +/*
> +   Read and set the vendor ID of the CEC adapter.
> + */
> +#define CEC_G_VENDOR_ID		_IOR('a', 9, __u32)
> +#define CEC_S_VENDOR_ID		_IOW('a', 10, __u32)
> +
> +#endif
> 


^ permalink raw reply	[flat|nested] 27+ messages in thread

* RE: [PATCH v4 06/10] cec: add HDMI CEC framework
  2015-04-24 10:03   ` Lars Op den Kamp
@ 2015-04-27  8:09     ` Kamil Debski
  2015-04-27  8:19       ` Hans Verkuil
  0 siblings, 1 reply; 27+ messages in thread
From: Kamil Debski @ 2015-04-27  8:09 UTC (permalink / raw)
  To: 'Lars Op den Kamp', dri-devel, linux-media
  Cc: m.szyprowski, mchehab, hverkuil, kyungmin.park, thomas, sean,
	dmitry.torokhov, linux-input, linux-samsung-soc,
	'Hans Verkuil'

Hi Lars, 

Thank you for your comments.

From: linux-media-owner@vger.kernel.org [mailto:linux-media-
owner@vger.kernel.org] On Behalf Of Lars Op den Kamp
Sent: Friday, April 24, 2015 12:04 PM
 
> Hi Kamil, Hans,
> 
> I'm the main developer of libCEC
> (https://github.com/Pulse-Eight/libcec). Sorry for the late time to
> jump
> in here, but I wasn't signed up to this mailing list and someone
> pointed
> me to this discussion.
> 
> Unfortunately this approach will not work with half the TVs that are
> out
> there. I'll explain why:
> 
> * because of how some (common) brands implemented CEC in their TVs,
> this
> implementation will not work, as the TV will just reject it. In libCEC,
> we've created work arounds for brands like this. Without these work
> arounds, your in-kernel implementation will be very vendor specific.
> e.g. this implementation will work for Samsung's TVs, but not for the
> TVs made by another big TV brand. All commands, including CEC_OP_ABORT,
> should be passed to userspace to make it work with all brands.
>
> * it should be made possible to not have the kernel send any CEC
> message, try to process any received CEC message, or ack to any logical
> address at all, to allow libraries like libCEC to fully handle all CEC
> traffic. Some brands only enable routing of some CEC keys when a
> specific device type is used. libCEC will allocate a logical address of
> the correct type for the brand that's used. If another address is first
> allocated by the kernel, and the TV communicates with it to find out
> it's name and things like that, and libCEC allocates another address a
> bit later when initialised, then you'll end up with multiple entries in
> the device list on the TV, and only one of them will work.

Adding a special mode in the CEC framework that disables parsing and
processing seems like a good idea for me. This way libCEC could be
completely
in charge of how the communication is handled. 

I discussed this with Hans and he is for this solution. This way there would
be two modes:
- One with handling of CEC messages enabled in the kernel, in idea behind
  this is to have processing adhere to the CEC spec as closely as possible.
  It should work with equipment that also follows the spec and has little
  vendor specific quirks.
- Second, the passthrough mode, in which the handling of CEC messages would
  be left to userspace application. Kernel would not be sending or
  receiving messages, unless specifically told to do so. Below you mentioned
  that allocating logical addresses and sending ACKs could be done in
kernel.

  The way I see it is following: If allocation of a logical address is made
  then ACKs will be handled by the framework. If no allocation is made then
  the userspace can still send and receive messages. However no filtering is
  done based on the logical address - all received messages are sent to the
  userspace.

> 
> * CEC is *very* vendor specific. The main reason is, in my opinion, the
> use of the word "should" instead of "shall" in the spec. It's addressed
> in the new version, but it'll take years before all the non 2.x devices
> are gone. What works for vendor A will simply not work for vendor B.
> libCEC aims to address this, in a library that can be used on all major
> platforms and by all major programming languages. You could duplicate
> the work done there in the kernel to make make the implementation work
> with all brands, but I think that this does simply not belong in the
> kernel when it can be handled in userspace perfectly.

CEC being very vendor specific is a huge problem. I agree with you that
there is no need to duplicate the effort to mitigate all the vendor quirks.
Especially that a working implementation (libCEC) is already done.

> So I suggest that you limit the in-kernel implementation to handling
> raw
> traffic only, to have it do this (and nothing more):
> * allocate one or more logical addresses, and ack CEC traffic sent to
> those logical addresses
> * receive CEC traffic and forward it to userspace (traffic sent to all
> addresses is preferred, not just traffic sent to the logical address
> used by the device running this code)
> * transmit CEC traffic initiated by userspace

As mentioned above, I propose a "passthorugh" mode in which handling of
CEC messages by the kernel CEC framework will be very limited. I think
that the three functions listed above should be enough.

Any comments on this solution?

> 
> thanks,
> 
> Lars Op den Kamp

Best wishes,
-- 
Kamil Debski
Samsung R&D Institute Poland

> 
> 
> On 23-04-15 15:03, Kamil Debski wrote:
> > From: Hans Verkuil <hansverk@cisco.com>
> >
> > The added HDMI CEC framework provides a generic kernel interface for
> > HDMI CEC devices.
> >
> > Signed-off-by: Hans Verkuil <hansverk@cisco.com>
> > [k.debski@samsung.com: Merged CEC Updates commit by Hans Verkuil]
> > [k.debski@samsung.com: Merged Update author commit by Hans Verkuil]
> > [k.debski@samsung.com: change kthread handling when setting logical
> > address]
> > [k.debski@samsung.com: code cleanup and fixes]
> > [k.debski@samsung.com: add missing CEC commands to match spec]
> > [k.debski@samsung.com: add RC framework support]
> > [k.debski@samsung.com: move and edit documentation]
> > [k.debski@samsung.com: add vendor id reporting]
> > [k.debski@samsung.com: add possibility to clear assigned logical
> > addresses]
> > [k.debski@samsung.com: documentation fixes, clenaup and expansion]
> > [k.debski@samsung.com: reorder of API structs and add reserved fields]
> > [k.debski@samsung.com: fix handling of events and fix 32/64bit
> timespec
> > problem]
> > [k.debski@samsung.com: add cec.h to include/uapi/linux/Kbuild]
> > Signed-off-by: Kamil Debski <k.debski@samsung.com>
> > ---
> >   Documentation/cec.txt     |  396 ++++++++++++++++
> >   drivers/media/Kconfig     |    6 +
> >   drivers/media/Makefile    |    2 +
> >   drivers/media/cec.c       | 1161
> +++++++++++++++++++++++++++++++++++++++++++++
> >   include/media/cec.h       |  140 ++++++
> >   include/uapi/linux/Kbuild |    1 +
> >   include/uapi/linux/cec.h  |  303 ++++++++++++
> >   7 files changed, 2009 insertions(+)
> >   create mode 100644 Documentation/cec.txt
> >   create mode 100644 drivers/media/cec.c
> >   create mode 100644 include/media/cec.h
> >   create mode 100644 include/uapi/linux/cec.h
> >
> > diff --git a/Documentation/cec.txt b/Documentation/cec.txt
> > new file mode 100644
> > index 0000000..2b6c08a
> > --- /dev/null
> > +++ b/Documentation/cec.txt
> > @@ -0,0 +1,396 @@
> > +CEC Kernel Support
> > +==================
> > +
> > +The CEC framework provides a unified kernel interface for use with
> HDMI CEC
> > +hardware. It is designed to handle a multiple variants of hardware.
> Adding to
> > +the flexibility of the framework it enables to set which parts of
> the CEC
> > +protocol processing is handled by the hardware, by the driver and by
> the
> > +userspace application.
> > +
> > +
> > +The CEC Protocol
> > +----------------
> > +
> > +The CEC protocol enables consumer electronic devices to communicate
> with each
> > +other through the HDMI connection. The protocol uses logical
> addresses in the
> > +communication. The logical address is strictly connected with the
> functionality
> > +provided by the device. The TV acting as the communication hub is
> always
> > +assigned address 0. The physical address is determined by the
> physical
> > +connection between devices.
> > +
> > +The protocol enables control of compatible devices with a single
> remote.
> > +Synchronous power on/standby, instant playback with changing the
> content source
> > +on the TV.
> > +
> > +The Kernel Interface
> > +====================
> > +
> > +CEC Adapter
> > +-----------
> > +
> > +#define CEC_LOG_ADDR_INVALID 0xff
> > +
> > +/* The maximum number of logical addresses one device can be
> assigned to.
> > + * The CEC 2.0 spec allows for only 2 logical addresses at the
> moment. The
> > + * Analog Devices CEC hardware supports 3. So let's go wild and go
> for 4. */
> > +#define CEC_MAX_LOG_ADDRS 4
> > +
> > +/* The "Primary Device Type" */
> > +#define CEC_PRIM_DEVTYPE_TV		0
> > +#define CEC_PRIM_DEVTYPE_RECORD		1
> > +#define CEC_PRIM_DEVTYPE_TUNER		3
> > +#define CEC_PRIM_DEVTYPE_PLAYBACK	4
> > +#define CEC_PRIM_DEVTYPE_AUDIOSYSTEM	5
> > +#define CEC_PRIM_DEVTYPE_SWITCH		6
> > +#define CEC_PRIM_DEVTYPE_VIDEOPROC	7
> > +
> > +/* The "All Device Types" flags (CEC 2.0) */
> > +#define CEC_FL_ALL_DEVTYPE_TV		(1 << 7)
> > +#define CEC_FL_ALL_DEVTYPE_RECORD	(1 << 6)
> > +#define CEC_FL_ALL_DEVTYPE_TUNER	(1 << 5)
> > +#define CEC_FL_ALL_DEVTYPE_PLAYBACK	(1 << 4)
> > +#define CEC_FL_ALL_DEVTYPE_AUDIOSYSTEM	(1 << 3)
> > +#define CEC_FL_ALL_DEVTYPE_SWITCH	(1 << 2)
> > +/* And if you wondering what happened to VIDEOPROC devices: those
> should
> > + * be mapped to a SWITCH. */
> > +
> > +/* The logical address types that the CEC device wants to claim */
> > +#define CEC_LOG_ADDR_TYPE_TV		0
> > +#define CEC_LOG_ADDR_TYPE_RECORD	1
> > +#define CEC_LOG_ADDR_TYPE_TUNER		2
> > +#define CEC_LOG_ADDR_TYPE_PLAYBACK	3
> > +#define CEC_LOG_ADDR_TYPE_AUDIOSYSTEM	4
> > +#define CEC_LOG_ADDR_TYPE_SPECIFIC	5
> > +#define CEC_LOG_ADDR_TYPE_UNREGISTERED	6
> > +/* Switches should use UNREGISTERED.
> > + * Video processors should use SPECIFIC. */
> > +
> > +/* The CEC version */
> > +#define CEC_VERSION_1_4B		5
> > +#define CEC_VERSION_2_0			6
> > +
> > +struct cec_adapter {
> > +	/* internal fields removed */
> > +
> > +	u16 phys_addr;
> > +	u32 capabilities;
> > +	u8 version;
> > +	u8 num_log_addrs;
> > +	u8 prim_device[CEC_MAX_LOG_ADDRS];
> > +	u8 log_addr_type[CEC_MAX_LOG_ADDRS];
> > +	u8 log_addr[CEC_MAX_LOG_ADDRS];
> > +
> > +	int (*adap_enable)(struct cec_adapter *adap, bool enable);
> > +	int (*adap_log_addr)(struct cec_adapter *adap, u8 logical_addr);
> > +	int (*adap_transmit)(struct cec_adapter *adap, struct cec_msg
> *msg);
> > +	void (*adap_transmit_timed_out)(struct cec_adapter *adap);
> > +
> > +	void (*claimed_log_addr)(struct cec_adapter *adap, u8 idx);
> > +	int (*received)(struct cec_adapter *adap, struct cec_msg *msg);
> > +};
> > +
> > +int cec_create_adapter(struct cec_adapter *adap, u32 caps);
> > +void cec_delete_adapter(struct cec_adapter *adap);
> > +int cec_transmit_msg(struct cec_adapter *adap, struct cec_data *data,
> bool block);
> > +
> > +/* Called by the adapter */
> > +void cec_transmit_done(struct cec_adapter *adap, u32 status);
> > +void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg);
> > +
> > +int cec_receive_msg(struct cec_adapter *adap, struct cec_msg *msg,
> bool block);
> > +int cec_claim_log_addrs(struct cec_adapter *adap, struct
> cec_log_addrs *log_addrs, bool block);
> > +
> > +The device type defines are defined by the CEC standard.
> > +
> > +The cec_adapter structure represents the adapter. It has a number of
> > +operations that have to be implemented in the driver: adap_enable()
> enables
> > +or disables the physical adapter, adap_log_addr() tells the driver
> which
> > +logical address should be configured. This may be called multiple
> times
> > +to configure multiple logical addresses. Calling adap_enable(false)
> or
> > +adap_log_addr(CEC_LOG_ADDR_INVALID) will clear all configured
> logical
> > +addresses.
> > +
> > +The adap_transmit op will setup the hardware to send out the given
> CEC message.
> > +This will return without waiting for the transmission to finish. The
> > +adap_transmit_timed_out() function is called when the current
> transmission timed
> > +out and the hardware needs to be informed of this (the hardware
> should go back
> > +from transmitter to receiver mode).
> > +
> > +The adapter driver will also call into the adapter: it should call
> > +cec_transmit_done() when a cec transfer was finalized and
> cec_received_msg()
> > +when a new message was received.
> > +
> > +When a message is received the received() op is called.
> > +
> > +The driver has to call cec_create_adapter to initialize the
> structure. If
> > +the 'caps' argument is non-zero, then it will also create a
> /dev/cecX
> > +device node to allow userspace to interact with the CEC device.
> Userspace
> > +can request those capabilities with the CEC_G_CAPS ioctl.
> > +
> > +In order for a CEC adapter to be configured it needs a physical
> address.
> > +This is normally assigned by the driver. It is either 0.0.0.0 for a
> TV (aka
> > +video receiver) or it is derived from the EDID that the source
> received
> > +from the sink. This is normally set by the driver before enabling
> the CEC
> > +adapter, or it is set from userspace in the case of CEC USB dongles
> (although
> > +embedded systems might also want to set this manually).
> > +
> > +After enabling the CEC adapter it has to be configured.
> > +
> > +The userspace has to inform the CEC adapter of which type of device
> it requests
> > +the adapter to identify itself. After this information is set by
> userspace, the
> > +CEC framework will attempt to to find and claim a logical addresses
> matching the
> > +requested device type. If none are found, then it will fall back to
> logical
> > +address Unregistered (15). To clear the logical addresses list from
> the list the
> > +userspace application should set the num_log_addrs field of struct
> cec_log_addr
> > +to 0.
> > +
> > +The type of device is set from the userspace with the
> CEC_S_ADAP_LOG_ADDRS. In
> > +addition, claiming logical addresses can be initiated from the
> kernel side by
> > +calling the cec_claim_log_addrs function.
> > +
> > +Before the addresses are claimed it is possible to send and receive
> messages.
> > +Sending all messages is possible as it is up to the userspace to the
> source
> > +and destination addresses in the message payload. However, only
> broadcast
> > +messages can be received until a regular logical address is claimed.
> > +
> > +When a CEC message is received the CEC framework will take care of
> the CEC
> > +core messages CEC_OP_GET_CEC_VERSION, CEC_OP_GIVE_PHYS_ADDR and
> CEC_OP_ABORT.
> > +Then it will call the received() op (if set), and finally it will
> queue it
> > +for handling by userspace if create_devnode was true, or send back
> > +FEATURE_ABORT if create_devnode was false.
> > +
> > +Drivers can also use the cec_transmit_msg() call to transmit a
> message. This
> > +can either be fire-and-forget (the CEC framework will queue up
> messages in a
> > +transmit queue), or a blocking wait until there is either an error
> or a
> > +reply to the message.
> > +
> > +
> > +The Userspace API
> > +=================
> > +
> > +ioctl API
> > +---------
> > +
> > +- CEC_G_CAPS ioctl
> > +
> > +Read the CEC adapter capabilities: the number of logical addresses
> > +that the adapter can configure and what can be controlled from
> userspace.
> > +
> > +#define CEC_G_CAPS			_IOR('a', 0, struct cec_caps)
> > +
> > +The cec_caps struct is following:
> > +
> > +struct cec_caps {
> > +	__u32 available_log_addrs;
> > +	__u32 capabilities;
> > +	__u32 vendor_id;
> > +	__u8  version;
> > +	__u8  reserved[11];
> > +};
> > +
> > +The following capabilities are defined:
> > +
> > +/* Userspace has to configure the adapter state (enable/disable) */
> > +#define CEC_CAP_STATE		(1 << 0)
> > +/* Userspace has to configure the physical address */
> > +#define CEC_CAP_PHYS_ADDR	(1 << 1)
> > +/* Userspace has to configure the logical addresses */
> > +#define CEC_CAP_LOG_ADDRS	(1 << 2)
> > +/* Userspace can transmit messages */
> > +#define CEC_CAP_TRANSMIT	(1 << 3)
> > +/* Userspace can receive messages */
> > +#define CEC_CAP_RECEIVE		(1 << 4)
> > +/* Userspace has to configure the vendor id */
> > +#define CEC_CAP_VENDOR_ID	(1 << 5)
> > +/* The hardware has the possibility to work in the promiscuous mode
> */
> > +#define CEC_CAP_PROMISCUOUS	(1 << 6)
> > +
> > +- CEC_TRANSMIT and CEC_RECEIVE ioctls
> > +
> > +These ioctls are used to send and receive messages over the CEC bus.
> > +
> > +#define CEC_TRANSMIT		_IOWR('a', 1, struct cec_msg)
> > +#define CEC_RECEIVE		_IOWR('a', 2, struct cec_msg)
> > +
> > +The struct cec_msg is the main message struct:
> > +
> > +struct cec_msg {
> > +	__u32 len;
> > +	__u32 status;
> > +	__u32 timeout;
> > +	/* timeout (in ms) is used to timeout CEC_RECEIVE.
> > +	   Set to 0 if you want to wait forever. */
> > +	struct cec_time ts;
> > +	__u8  msg[16];
> > +	__u8  reply;
> > +	/* If non-zero, then wait for a reply with this opcode.
> > +	   If there was an error when sending the msg or FeatureAbort
> > +	   was returned, then reply is set to 0.
> > +	   If reply is non-zero upon return, then len/msg are set to
> > +	   the received message.
> > +	   If reply is zero upon return and status has the
> > +	   CEC_TX_STATUS_FEATURE_ABORT bit set, then len/msg are set to
> the
> > +	   received feature abort message.  If reply is zero upon return
> and
> > +	   status has the CEC_TX_STATUS_REPLY_TIMEOUT bit set, then no
> reply
> > +	   was seen at all.  This field is ignored with CEC_RECEIVE.
> > +	   If reply is non-zero for CEC_TRANSMIT and the message is a
> broadcast,
> > +	   then -EINVAL is returned.
> > +	   if reply is non-zero, then timeout is set to 1000 (the
> required
> > +	   maximum response time).
> > +	 */
> > +	__u8 reserved[31];
> > +};
> > +
> > +The struct contains 16 bytes for the message, the length of the
> message, a
> > +status value in case of errors. Optionally you can request the CEC
> framework to
> > +wait after transmitting the message until the 'reply' message is
> returned (or
> > +Feature Abort). This is done asynchronously, i.e. it does not
> require that the
> > +reply comes right after the transmit, but other messages in between
> are allowed.
> > +
> > +The ts field of the struct cec_msg represents a timestamp. The
> timestamp struct
> > +is following:
> > +
> > +struct cec_time {
> > +	__u64 sec;
> > +	__u64 nsec;
> > +};
> > +
> > +With CEC_TRANSMIT you can transmit a message, either blocking or
> > +non-blocking. With CEC_RECEIVE you can dequeue a pending received
> > +message from the internal queue or wait for a message to arrive
> > +(if called in blocking mode).
> > +
> > +- CEC_G_ADAP_LOG_ADDRS and CEC_S_ADAP_LOG_ADDRS
> > +
> > +These ioctl are used to configure the logical addresses of the CEC
> adapter.
> > +
> > +#define CEC_G_ADAP_LOG_ADDRS	_IOR('a', 3, struct cec_log_addrs)
> > +#define CEC_S_ADAP_LOG_ADDRS	_IOWR('a', 4, struct cec_log_addrs)
> > +
> > +The struct cec_log_addrs is following:
> > +
> > +struct cec_log_addrs {
> > +	__u8 cec_version;
> > +	__u8 num_log_addrs;
> > +	__u8 primary_device_type[CEC_MAX_LOG_ADDRS];
> > +	__u8 log_addr_type[CEC_MAX_LOG_ADDRS];
> > +	__u8 log_addr[CEC_MAX_LOG_ADDRS];
> > +
> > +	/* CEC 2.0 */
> > +	__u8 all_device_types;
> > +	__u8 features[CEC_MAX_LOG_ADDRS][12];
> > +
> > +	__u8 reserved[9];
> > +};
> > +
> > +The cec_version determines which CEC version should be used.
> > +
> > +/* The CEC version */
> > +#define CEC_VERSION_1_4B		5
> > +#define CEC_VERSION_2_0			6
> > +
> > +It will try to claim num_log_addrs devices. The log_addr_type array
> has
> > +the logical address type that needs to be claimed for that device,
> and
> > +the log_addr array will receive the actual logical address that was
> > +claimed for that device or 0xff if no address could be claimed.
> > +
> > +The primary_device_type contains the primary device for each logical
> > +address.
> > +
> > +For CEC 2.0 devices fill in the all_device_types parameter to use
> with the
> > +Report Features command, and fill in the 'features' which contains
> the
> > +remaining parameters (RC Profile and Device Features) to use in
> Report
> > +Features.
> > +
> > +An error is returned if the adapter is disabled or if there
> > +is no physical address assigned or if the cec_version is unknown.
> > +
> > +If no logical address of one or more of the given types could be
> claimed,
> > +then log_addr will be set to CEC_LOG_ADDR_INVALID.
> > +
> > +If no logical address could be claimed at all, then num_log_addrs
> will
> > +be set to 1, log_addr_type[0] to UNREGISTERED and log_addr[0] to 0xf.
> > +
> > +The S_ADAP_LOG_ADDRS ioctl is not available unless CEC_CAP_LOG_ADDRS
> > +is set.
> > +
> > +- CEC_G_ADAP_STATE and CEC_S_ADAP_STATE ioctls
> > +
> > +Enable/disable the adapter. The S_ADAP_STATE ioctl is not available
> > +unless CEC_CAP_STATE is set.
> > +
> > +#define CEC_G_ADAP_STATE	_IOR('a', 5, __u32)
> > +#define CEC_S_ADAP_STATE	_IOW('a', 6, __u32)
> > +
> > +State CEC_STATE_DISABLED means the adapter is disabled,
> CEC_STATE_ENABLED
> > +stands for adapter enabled.
> > +
> > +/* The CEC state */
> > +#define CEC_STATE_DISABLED		0
> > +#define CEC_STATE_ENABLED		1
> > +
> > +- CEC_G_ADAP_PHYS_ADDR and CEC_S_ADAP_PHYS_ADDR ioctls
> > +
> > +phys_addr is either 0 (if this is the CEC root device) or a valid
> physical
> > +address obtained from the EDID of the sink as read by this CEC
> device (if this
> > +is a source device) or a physical address obtained and modified from
> > +the EDID of the sink and used for a sink CEC device.  If nothing is
> connected,
> > +then phys_addr is 0xffff.  See HDMI 1.4b, section 8.7 (Physical
> Address).
> > +
> > +#define CEC_G_ADAP_PHYS_ADDR	_IOR('a', 7, __u16)
> > +#define CEC_S_ADAP_PHYS_ADDR	_IOW('a', 8, __u16)
> > +
> > +The S_ADAP_PHYS_ADDR ioctl is not available unless CEC_CAP_PHYS_ADDR
> > +is set.
> > +
> > +- CEC_G_EVENT ioctl
> > +
> > +This ioctl is used to read a pending event. It takes a struct
> cec_event
> > +that is filled with appropriate data.
> > +
> > +The struct cec_event is following:
> > +
> > +struct cec_event {
> > +	struct cec_time ts;
> > +	__u32 event;
> > +	__u32 reserved[4];
> > +};
> > +
> > +- CEC_G_VENDOR_ID and CEC_S_VENDOR_ID ioctls
> > +
> > +These calls are used to read or set the vendor ID of the adapter.
> > +
> > +#define CEC_G_VENDOR_ID		_IOR('a', 9, __u32)
> > +#define CEC_S_VENDOR_ID		_IOW('a', 10, __u32)
> > +
> > +Vendor ID is a 24 bit identifier obtained from the IEEE Registration
> > +Authority Committee.
> > +
> > +The CEC_S_ADAP_VENDOR_ID ioctl is not available unless
> CEC_CAP_VENDOR_ID
> > +is set.
> > +
> > +Events
> > +------
> > +
> > +The CEC framework provides a way for the userspace to be informed
> about
> > +a number of event that can occur in the hardware.
> > +
> > +The userspace is informed about a new event with the POLLPRI event
> of the
> > +poll function.
> > +
> > +The list of events is following:
> > +
> > +/* Event that occurs when a cable is connected */
> > +#define CEC_EVENT_CONNECT	1
> > +/* Event that occurs when all logical addresses were claimed */
> > +#define CEC_EVENT_READY		2
> > +/* Event that is sent when the cable is disconnected */
> > +#define CEC_EVENT_DISCONNECT	3
> > +
> > +The events can be read with the CEC_G_EVENT ioctl.
> > +
> > +Remote control handling
> > +-----------------------
> > +
> > +The CEC framework handles the key up/down messages of remote control
> and
> > +provides the key events via the RC framework.
> > diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
> > index 3ef0f90..262e9ad 100644
> > --- a/drivers/media/Kconfig
> > +++ b/drivers/media/Kconfig
> > @@ -15,6 +15,12 @@ if MEDIA_SUPPORT
> >
> >   comment "Multimedia core support"
> >
> > +config CEC
> > +	tristate "CEC API (EXPERIMENTAL)"
> > +	select RC_CORE
> > +	---help---
> > +	  Enable the CEC API.
> > +
> >   #
> >   # Multimedia support - automatically enable V4L2 and DVB core
> >   #
> > diff --git a/drivers/media/Makefile b/drivers/media/Makefile
> > index e608bbc..db66014 100644
> > --- a/drivers/media/Makefile
> > +++ b/drivers/media/Makefile
> > @@ -2,6 +2,8 @@
> >   # Makefile for the kernel multimedia device drivers.
> >   #
> >
> > +obj-$(CONFIG_CEC) += cec.o
> > +
> >   media-objs	:= media-device.o media-devnode.o media-entity.o
> >
> >   #
> > diff --git a/drivers/media/cec.c b/drivers/media/cec.c
> > new file mode 100644
> > index 0000000..bf5cc07
> > --- /dev/null
> > +++ b/drivers/media/cec.c
> > @@ -0,0 +1,1161 @@
> > +#include <linux/errno.h>
> > +#include <linux/init.h>
> > +#include <linux/module.h>
> > +#include <linux/kernel.h>
> > +#include <linux/kmod.h>
> > +#include <linux/ktime.h>
> > +#include <linux/slab.h>
> > +#include <linux/mm.h>
> > +#include <linux/string.h>
> > +#include <linux/types.h>
> > +#include <linux/uaccess.h>
> > +#include <media/cec.h>
> > +
> > +#define CEC_NUM_DEVICES	256
> > +#define CEC_NAME	"cec"
> > +
> > +static int debug;
> > +module_param(debug, int, 0644);
> > +MODULE_PARM_DESC(debug, "debug level (0-1)");
> > +
> > +struct cec_transmit_notifier {
> > +	struct completion c;
> > +	struct cec_data *data;
> > +};
> > +
> > +#define dprintk(fmt, arg...)
\
> > +	do {								\
> > +		if (debug)						\
> > +			pr_info("cec-%s: " fmt, adap->name, ## arg);	\
> > +	} while (0)
> > +
> > +static dev_t cec_dev_t;
> > +
> > +/* Active devices */
> > +static DEFINE_MUTEX(cec_devnode_lock);
> > +static DECLARE_BITMAP(cec_devnode_nums, CEC_NUM_DEVICES);
> > +
> > +/* dev to cec_devnode */
> > +#define to_cec_devnode(cd) container_of(cd, struct cec_devnode, dev)
> > +
> > +static inline struct cec_devnode *cec_devnode_data(struct file *filp)
> > +{
> > +	return filp->private_data;
> > +}
> > +
> > +static int cec_log_addr2idx(const struct cec_adapter *adap, u8
> log_addr)
> > +{
> > +	int i;
> > +
> > +	for (i = 0; i < adap->num_log_addrs; i++)
> > +		if (adap->log_addr[i] == log_addr)
> > +			return i;
> > +	return -1;
> > +}
> > +
> > +static unsigned cec_log_addr2dev(const struct cec_adapter *adap, u8
> log_addr)
> > +{
> > +	int i = cec_log_addr2idx(adap, log_addr);
> > +
> > +	return adap->prim_device[i < 0 ? 0 : i];
> > +}
> > +
> > +/* Called when the last user of the cec device exits. */
> > +static void cec_devnode_release(struct device *cd)
> > +{
> > +	struct cec_devnode *cecdev = to_cec_devnode(cd);
> > +
> > +	mutex_lock(&cec_devnode_lock);
> > +
> > +	/* Delete the cdev on this minor as well */
> > +	cdev_del(&cecdev->cdev);
> > +
> > +	/* Mark device node number as free */
> > +	clear_bit(cecdev->minor, cec_devnode_nums);
> > +
> > +	mutex_unlock(&cec_devnode_lock);
> > +
> > +	/* Release cec_devnode and perform other cleanups as needed. */
> > +	if (cecdev->release)
> > +		cecdev->release(cecdev);
> > +}
> > +
> > +static struct bus_type cec_bus_type = {
> > +	.name = CEC_NAME,
> > +};
> > +
> > +static bool cec_sleep(struct cec_adapter *adap, int timeout)
> > +{
> > +	bool timed_out = false;
> > +
> > +	DECLARE_WAITQUEUE(wait, current);
> > +
> > +	add_wait_queue(&adap->kthread_waitq, &wait);
> > +	if (!kthread_should_stop()) {
> > +		if (timeout < 0) {
> > +			set_current_state(TASK_INTERRUPTIBLE);
> > +			schedule();
> > +		} else {
> > +			timed_out = !schedule_timeout_interruptible
> > +				(msecs_to_jiffies(timeout));
> > +		}
> > +	}
> > +
> > +	remove_wait_queue(&adap->kthread_waitq, &wait);
> > +	return timed_out;
> > +}
> > +
> > +/*
> > + * Main CEC state machine
> > + *
> > + * In the IDLE state the CEC adapter is ready to receive or transmit
> messages.
> > + * If it is woken up it will check if a new message is queued, and
> if so it
> > + * will be transmitted and the state will go to TRANSMITTING.
> > + *
> > + * When the transmit is marked as done the state machine will check
> if it
> > + * should wait for a reply. If not, it will call the notifier and go
> back
> > + * to the IDLE state. Else it will switch to the WAIT state and wait
> for a
> > + * reply. When the reply arrives it will call the notifier and go
> back
> > + * to IDLE state.
> > + *
> > + * For the transmit and the wait-for-reply states a timeout is used
> of
> > + * 1 second as per the standard.
> > + */
> > +static int cec_thread_func(void *data)
> > +{
> > +	struct cec_adapter *adap = data;
> > +	int timeout = -1;
> > +
> > +	for (;;) {
> > +		bool timed_out = cec_sleep(adap, timeout);
> > +
> > +		if (kthread_should_stop())
> > +			break;
> > +		timeout = -1;
> > +		mutex_lock(&adap->lock);
> > +		dprintk("state %d timedout: %d tx: %d@%d\n", adap->state,
> > +			timed_out, adap->tx_qcount, adap->tx_qstart);
> > +		if (adap->state == CEC_ADAP_STATE_TRANSMITTING && timed_out)
> > +			adap->adap_transmit_timed_out(adap);
> > +
> > +		if (adap->state == CEC_ADAP_STATE_WAIT ||
> > +		    adap->state == CEC_ADAP_STATE_TRANSMITTING) {
> > +			struct cec_data *data = adap->tx_queue +
> > +						adap->tx_qstart;
> > +
> > +			if (adap->state == CEC_ADAP_STATE_TRANSMITTING &&
> > +			    data->msg.reply && !timed_out &&
> > +			    data->msg.status == CEC_TX_STATUS_OK) {
> > +				adap->state = CEC_ADAP_STATE_WAIT;
> > +				timeout = 1000;
> > +			} else {
> > +				if (timed_out) {
> > +					data->msg.reply = 0;
> > +					if (adap->state ==
> > +					    CEC_ADAP_STATE_TRANSMITTING)
> > +						data->msg.status =
> > +
CEC_TX_STATUS_RETRY_TIMEOUT;
> > +					else
> > +						data->msg.status =
> > +
CEC_TX_STATUS_REPLY_TIMEOUT;
> > +				}
> > +				adap->state = CEC_ADAP_STATE_IDLE;
> > +				if (data->func) {
> > +					mutex_unlock(&adap->lock);
> > +					data->func(adap, data, data->priv);
> > +					mutex_lock(&adap->lock);
> > +				}
> > +				adap->tx_qstart = (adap->tx_qstart + 1) %
> > +						  CEC_TX_QUEUE_SZ;
> > +				adap->tx_qcount--;
> > +				wake_up_interruptible(&adap->waitq);
> > +			}
> > +		}
> > +		if (adap->state == CEC_ADAP_STATE_IDLE && adap->tx_qcount)
> {
> > +			adap->state = CEC_ADAP_STATE_TRANSMITTING;
> > +			timeout = adap->tx_queue[adap->tx_qstart].msg.len ==
> 1 ?
> > +				  200 : 1000;
> > +			adap->adap_transmit(adap,
> > +
&adap->tx_queue[adap->tx_qstart].msg);
> > +			mutex_unlock(&adap->lock);
> > +			continue;
> > +		}
> > +		mutex_unlock(&adap->lock);
> > +	}
> > +	return 0;
> > +}
> > +
> > +static int cec_transmit_notify(struct cec_adapter *adap, struct
> cec_data *data,
> > +		void *priv)
> > +{
> > +	struct cec_transmit_notifier *n = priv;
> > +
> > +	*(n->data) = *data;
> > +	complete(&n->c);
> > +	return 0;
> > +}
> > +
> > +int cec_transmit_msg(struct cec_adapter *adap, struct cec_data *data,
> > +		     bool block)
> > +{
> > +	struct cec_transmit_notifier notifier;
> > +	struct cec_msg *msg = &data->msg;
> > +	int res = 0;
> > +	unsigned idx;
> > +
> > +	if (msg->len == 0 || msg->len > 16)
> > +		return -EINVAL;
> > +	if (msg->reply && (msg->len == 1 || cec_msg_is_broadcast(msg)))
> > +		return -EINVAL;
> > +	if (msg->len > 1 && !cec_msg_is_broadcast(msg) &&
> > +	    cec_msg_initiator(msg) == cec_msg_destination(msg))
> > +		return -EINVAL;
> > +	if (cec_msg_initiator(msg) != 0xf &&
> > +	    cec_log_addr2idx(adap, cec_msg_initiator(msg)) < 0)
> > +		return -EINVAL;
> > +
> > +	if (msg->len == 1)
> > +		dprintk("cec_transmit_msg: 0x%02x%s\n",
> > +				msg->msg[0], !block ? " nb" : "");
> > +	else if (msg->reply)
> > +		dprintk("cec_transmit_msg: 0x%02x 0x%02x (wait for
> 0x%02x)%s\n",
> > +				msg->msg[0], msg->msg[1],
> > +				msg->reply, !block ? " nb" : "");
> > +	else
> > +		dprintk("cec_transmit_msg: 0x%02x 0x%02x%s\n",
> > +				msg->msg[0], msg->msg[1],
> > +				!block ? " nb" : "");
> > +
> > +	msg->status = 0;
> > +	memset(&msg->ts, 0, sizeof(msg->ts));
> > +	if (msg->reply)
> > +		msg->timeout = 1000;
> > +	if (block) {
> > +		init_completion(&notifier.c);
> > +		notifier.data = data;
> > +		data->func = cec_transmit_notify;
> > +		data->priv = &notifier;
> > +	} else {
> > +		data->func = NULL;
> > +		data->priv = NULL;
> > +	}
> > +	mutex_lock(&adap->lock);
> > +	idx = (adap->tx_qstart + adap->tx_qcount) % CEC_TX_QUEUE_SZ;
> > +	if (adap->tx_qcount == CEC_TX_QUEUE_SZ) {
> > +		res = -EBUSY;
> > +	} else {
> > +		adap->tx_queue[idx] = *data;
> > +		adap->tx_qcount++;
> > +		if (adap->state == CEC_ADAP_STATE_IDLE)
> > +			wake_up_interruptible(&adap->kthread_waitq);
> > +	}
> > +	mutex_unlock(&adap->lock);
> > +	if (res || !block)
> > +		return res;
> > +	wait_for_completion_interruptible(&notifier.c);
> > +	return res;
> > +}
> > +EXPORT_SYMBOL_GPL(cec_transmit_msg);
> > +
> > +void cec_transmit_done(struct cec_adapter *adap, u32 status)
> > +{
> > +	struct cec_msg *msg;
> > +	struct timespec64 ts;
> > +
> > +	dprintk("cec_transmit_done\n");
> > +	mutex_lock(&adap->lock);
> > +	if (adap->state == CEC_ADAP_STATE_TRANSMITTING) {
> > +		msg = &adap->tx_queue[adap->tx_qstart].msg;
> > +		msg->status = status;
> > +		if (status)
> > +			msg->reply = 0;
> > +		ktime_get_ts64(&ts);
> > +		msg->ts.sec = ts.tv_sec;
> > +		msg->ts.nsec = ts.tv_nsec;
> > +		wake_up_interruptible(&adap->kthread_waitq);
> > +	}
> > +	mutex_unlock(&adap->lock);
> > +}
> > +EXPORT_SYMBOL_GPL(cec_transmit_done);
> > +
> > +static int cec_receive_notify(struct cec_adapter *adap, struct
> cec_msg *msg)
> > +{
> > +	bool is_broadcast = cec_msg_is_broadcast(msg);
> > +	u8 dest_laddr = cec_msg_destination(msg);
> > +	u8 devtype = cec_log_addr2dev(adap, dest_laddr);
> > +	bool is_directed = cec_log_addr2idx(adap, dest_laddr) >= 0;
> > +	struct cec_data tx_data;
> > +	int res = 0;
> > +	unsigned idx;
> > +
> > +	if (msg->len <= 1)
> > +		return 0;
> > +	if (!is_directed && !is_broadcast)
> > +		return 0;	/* Not for us */
> > +
> > +	tx_data.msg.msg[0] = (msg->msg[0] << 4) | (msg->msg[0] >> 4);
> > +	tx_data.msg.reply = 0;
> > +
> > +	if (adap->received) {
> > +		res = adap->received(adap, msg);
> > +		if (res != -ENOMSG)
> > +			return 0;
> > +		res = 0;
> > +	}
> > +
> > +	switch (msg->msg[1]) {
> > +	case CEC_OP_GET_CEC_VERSION:
> > +		if (is_broadcast)
> > +			return 0;
> > +		tx_data.msg.len = 3;
> > +		tx_data.msg.msg[1] = CEC_OP_CEC_VERSION;
> > +		tx_data.msg.msg[2] = adap->version;
> > +		return cec_transmit_msg(adap, &tx_data, false);
> > +
> > +	case CEC_OP_GIVE_PHYSICAL_ADDR:
> > +		if (!is_directed)
> > +			return 0;
> > +		/* Do nothing for CEC switches using addr 15 */
> > +		if (devtype == CEC_PRIM_DEVTYPE_SWITCH && dest_laddr == 15)
> > +			return 0;
> > +		tx_data.msg.len = 5;
> > +		tx_data.msg.msg[1] = CEC_OP_REPORT_PHYSICAL_ADDR;
> > +		tx_data.msg.msg[2] = adap->phys_addr >> 8;
> > +		tx_data.msg.msg[3] = adap->phys_addr & 0xff;
> > +		tx_data.msg.msg[4] = devtype;
> > +		return cec_transmit_msg(adap, &tx_data, false);
> > +
> > +	case CEC_OP_ABORT:
> > +		/* Do nothing for CEC switches */
> > +		if (devtype == CEC_PRIM_DEVTYPE_SWITCH)
> > +			return 0;
> > +		tx_data.msg.len = 4;
> > +		tx_data.msg.msg[1] = CEC_OP_FEATURE_ABORT;
> > +		tx_data.msg.msg[2] = msg->msg[1];
> > +		tx_data.msg.msg[3] = 4;	/* Refused */
> > +		return cec_transmit_msg(adap, &tx_data, false);
> > +
> > +	case CEC_OP_USER_CONTROL_PRESSED:
> > +		switch (msg->msg[2]) {
> > +		/* Play function, this message can have variable length
> > +		 * depending on the specific play function that is used.
> > +		 */
> > +		case 0x60:
> > +			if (msg->len == 3)
> > +				rc_keydown(adap->rc, RC_TYPE_CEC,
> > +					   msg->msg[2] << 8 | msg->msg[3],
0);
> > +			else
> > +				rc_keydown(adap->rc, RC_TYPE_CEC,
msg->msg[2],
> > +					   0);
> > +			break;
> > +		/* Other function messages that are not handled.
> > +		 * Currently the RC framework does not allow to supply an
> > +		 * additional parameter to a keypress. These "keys" contain
> > +		 * other information such as channel number, an input
> number
> > +		 * etc.
> > +		 * For the time being these messages are not processed by
> the
> > +		 * framework and are simply forwarded to the user space.
> > +		 */
> > +		case 0x67: case 0x68: case 0x69: case 0x6a:
> > +			break;
> > +		default:
> > +			rc_keydown(adap->rc, RC_TYPE_CEC, msg->msg[2], 0);
> > +		}
> > +		break;
> > +	case CEC_OP_USER_CONTROL_RELEASED:
> > +		rc_keyup(adap->rc);
> > +		return 0;
> > +	}
> > +
> > +	if ((adap->capabilities & CEC_CAP_RECEIVE) == 0)
> > +		return 0;
> > +	mutex_lock(&adap->lock);
> > +	idx = (adap->rx_qstart + adap->rx_qcount) % CEC_RX_QUEUE_SZ;
> > +	if (adap->rx_qcount == CEC_RX_QUEUE_SZ) {
> > +		res = -EBUSY;
> > +	} else {
> > +		adap->rx_queue[idx] = *msg;
> > +		adap->rx_qcount++;
> > +		wake_up_interruptible(&adap->waitq);
> > +	}
> > +	mutex_unlock(&adap->lock);
> > +	return res;
> > +}
> > +
> > +int cec_receive_msg(struct cec_adapter *adap, struct cec_msg *msg,
> bool block)
> > +{
> > +	int res;
> > +
> > +	do {
> > +		mutex_lock(&adap->lock);
> > +		if (adap->rx_qcount) {
> > +			*msg = adap->rx_queue[adap->rx_qstart];
> > +			adap->rx_qstart = (adap->rx_qstart + 1) %
> > +					  CEC_RX_QUEUE_SZ;
> > +			adap->rx_qcount--;
> > +			res = 0;
> > +		} else {
> > +			res = -EAGAIN;
> > +		}
> > +		mutex_unlock(&adap->lock);
> > +		if (!block || !res)
> > +			break;
> > +		if (msg->timeout) {
> > +			res = wait_event_interruptible_timeout(adap->waitq,
> > +				adap->rx_qcount,
> > +				msecs_to_jiffies(msg->timeout));
> > +			if (res == 0)
> > +				res = -ETIMEDOUT;
> > +			else if (res > 0)
> > +				res = 0;
> > +		} else {
> > +			res = wait_event_interruptible(adap->waitq,
> > +				adap->rx_qcount);
> > +		}
> > +	} while (!res);
> > +	return res;
> > +}
> > +EXPORT_SYMBOL_GPL(cec_receive_msg);
> > +
> > +void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg)
> > +{
> > +	struct timespec64 ts;
> > +	bool is_reply = false;
> > +
> > +	mutex_lock(&adap->lock);
> > +	ktime_get_ts64(&ts);
> > +	msg->ts.sec = ts.tv_sec;
> > +	msg->ts.nsec = ts.tv_nsec;
> > +	dprintk("cec_received_msg: %02x %02x\n", msg->msg[0], msg-
> >msg[1]);
> > +	if (!cec_msg_is_broadcast(msg) && msg->len > 1 &&
> > +	    adap->state == CEC_ADAP_STATE_WAIT) {
> > +		struct cec_msg *dst = &adap->tx_queue[adap->tx_qstart].msg;
> > +
> > +		if (msg->msg[1] == dst->reply ||
> > +		    msg->msg[1] == CEC_OP_FEATURE_ABORT) {
> > +			*dst = *msg;
> > +			is_reply = true;
> > +			if (msg->msg[1] == CEC_OP_FEATURE_ABORT) {
> > +				dst->reply = 0;
> > +				dst->status = CEC_TX_STATUS_FEATURE_ABORT;
> > +			}
> > +			wake_up_interruptible(&adap->kthread_waitq);
> > +		}
> > +	}
> > +	mutex_unlock(&adap->lock);
> > +	if (!is_reply)
> > +		adap->recv_notifier(adap, msg);
> > +}
> > +EXPORT_SYMBOL_GPL(cec_received_msg);
> > +
> > +void cec_post_event(struct cec_adapter *adap, u32 event)
> > +{
> > +	struct timespec64 ts;
> > +	unsigned idx;
> > +
> > +	mutex_lock(&adap->lock);
> > +	if (adap->ev_qcount == CEC_EV_QUEUE_SZ) {
> > +		/* Drop oldest event */
> > +		adap->ev_qstart = (adap->ev_qstart + 1) % CEC_EV_QUEUE_SZ;
> > +		adap->ev_qcount--;
> > +	}
> > +
> > +	idx = (adap->ev_qstart + adap->ev_qcount) % CEC_EV_QUEUE_SZ;
> > +
> > +	adap->ev_queue[idx].event = event;
> > +	ktime_get_ts64(&ts);
> > +	adap->ev_queue[idx].ts.sec = ts.tv_sec;
> > +	adap->ev_queue[idx].ts.nsec = ts.tv_nsec;
> > +
> > +	adap->ev_qcount++;
> > +	mutex_unlock(&adap->lock);
> > +}
> > +EXPORT_SYMBOL_GPL(cec_post_event);
> > +
> > +static int cec_report_phys_addr(struct cec_adapter *adap, unsigned
> logical_addr)
> > +{
> > +	struct cec_data data;
> > +
> > +	/* Report Physical Address */
> > +	data.msg.len = 5;
> > +	data.msg.msg[0] = (logical_addr << 4) | 0x0f;
> > +	data.msg.msg[1] = CEC_OP_REPORT_PHYSICAL_ADDR;
> > +	data.msg.msg[2] = adap->phys_addr >> 8;
> > +	data.msg.msg[3] = adap->phys_addr & 0xff;
> > +	data.msg.msg[4] = cec_log_addr2dev(adap, logical_addr);
> > +	data.msg.reply = 0;
> > +	dprintk("config: la %d pa %x.%x.%x.%x\n",
> > +			logical_addr, cec_phys_addr_exp(adap->phys_addr));
> > +	return cec_transmit_msg(adap, &data, true);
> > +}
> > +
> > +int cec_enable(struct cec_adapter *adap, bool enable)
> > +{
> > +	int ret;
> > +
> > +	mutex_lock(&adap->lock);
> > +	ret = adap->adap_enable(adap, enable);
> > +	if (ret) {
> > +		mutex_unlock(&adap->lock);
> > +		return ret;
> > +	}
> > +	if (!enable) {
> > +		adap->state = CEC_ADAP_STATE_DISABLED;
> > +		adap->tx_qcount = 0;
> > +		adap->rx_qcount = 0;
> > +		adap->ev_qcount = 0;
> > +		adap->num_log_addrs = 0;
> > +	} else {
> > +		adap->state = CEC_ADAP_STATE_UNCONF;
> > +	}
> > +	mutex_unlock(&adap->lock);
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(cec_enable);
> > +
> > +struct cec_log_addrs_int {
> > +	struct cec_adapter *adap;
> > +	struct cec_log_addrs log_addrs;
> > +	struct completion c;
> > +	bool free_on_exit;
> > +	int err;
> > +};
> > +
> > +static int cec_config_log_addrs(struct cec_adapter *adap,
> > +				struct cec_log_addrs *log_addrs)
> > +{
> > +	static const u8 tv_log_addrs[] = {
> > +		0, CEC_LOG_ADDR_INVALID
> > +	};
> > +	static const u8 record_log_addrs[] = {
> > +		1, 2, 9, 12, 13, CEC_LOG_ADDR_INVALID
> > +	};
> > +	static const u8 tuner_log_addrs[] = {
> > +		3, 6, 7, 10, 12, 13, CEC_LOG_ADDR_INVALID
> > +	};
> > +	static const u8 playback_log_addrs[] = {
> > +		4, 8, 11, 12, 13, CEC_LOG_ADDR_INVALID
> > +	};
> > +	static const u8 audiosystem_log_addrs[] = {
> > +		5, 12, 13, CEC_LOG_ADDR_INVALID
> > +	};
> > +	static const u8 specific_use_log_addrs[] = {
> > +		14, 12, 13, CEC_LOG_ADDR_INVALID
> > +	};
> > +	static const u8 unregistered_log_addrs[] = {
> > +		CEC_LOG_ADDR_INVALID
> > +	};
> > +	static const u8 *type2addrs[7] = {
> > +		[CEC_LOG_ADDR_TYPE_TV] = tv_log_addrs,
> > +		[CEC_LOG_ADDR_TYPE_RECORD] = record_log_addrs,
> > +		[CEC_LOG_ADDR_TYPE_TUNER] = tuner_log_addrs,
> > +		[CEC_LOG_ADDR_TYPE_PLAYBACK] = playback_log_addrs,
> > +		[CEC_LOG_ADDR_TYPE_AUDIOSYSTEM] = audiosystem_log_addrs,
> > +		[CEC_LOG_ADDR_TYPE_SPECIFIC] = specific_use_log_addrs,
> > +		[CEC_LOG_ADDR_TYPE_UNREGISTERED] = unregistered_log_addrs,
> > +	};
> > +	struct cec_data data;
> > +	u32 claimed_addrs = 0;
> > +	int i, j;
> > +	int err;
> > +
> > +	if (adap->phys_addr) {
> > +		/* The TV functionality can only map to physical address 0.
> > +		   For any other address, try the Specific functionality
> > +		   instead as per the spec. */
> > +		for (i = 0; i < log_addrs->num_log_addrs; i++)
> > +			if (log_addrs->log_addr_type[i] ==
> CEC_LOG_ADDR_TYPE_TV)
> > +				log_addrs->log_addr_type[i] =
> > +						CEC_LOG_ADDR_TYPE_SPECIFIC;
> > +	}
> > +
> > +	memcpy(adap->prim_device, log_addrs->primary_device_type,
> > +			log_addrs->num_log_addrs);
> > +	dprintk("physical address: %x.%x.%x.%x, claim %d logical
> addresses\n",
> > +			cec_phys_addr_exp(adap->phys_addr),
> > +			log_addrs->num_log_addrs);
> > +	adap->num_log_addrs = 0;
> > +	adap->state = CEC_ADAP_STATE_IDLE;
> > +
> > +	/* TODO: remember last used logical addr type to achieve
> > +	   faster logical address polling by trying that one first.
> > +	 */
> > +	for (i = 0; i < log_addrs->num_log_addrs; i++) {
> > +		const u8 *la_list = type2addrs[log_addrs->log_addr_type[i]];
> > +
> > +		if (kthread_should_stop())
> > +			return -EINTR;
> > +
> > +		for (j = 0; la_list[j] != CEC_LOG_ADDR_INVALID; j++) {
> > +			u8 log_addr = la_list[j];
> > +
> > +			if (claimed_addrs & (1 << log_addr))
> > +				continue;
> > +
> > +			/* Send polling message */
> > +			data.msg.len = 1;
> > +			data.msg.msg[0] = 0xf0 | log_addr;
> > +			data.msg.reply = 0;
> > +			err = cec_transmit_msg(adap, &data, true);
> > +			if (err)
> > +				return err;
> > +			if (data.msg.status == CEC_TX_STATUS_RETRY_TIMEOUT)
{
> > +				/* Message not acknowledged, so this logical
> > +				   address is free to use. */
> > +				claimed_addrs |= 1 << log_addr;
> > +				adap->log_addr[adap->num_log_addrs++] =
> > +								log_addr;
> > +				log_addrs->log_addr[i] = log_addr;
> > +				err = adap->adap_log_addr(adap, log_addr);
> > +				dprintk("claim addr %d (%d)\n", log_addr,
> > +
adap->prim_device[i]);
> > +				if (err)
> > +					return err;
> > +				cec_report_phys_addr(adap, log_addr);
> > +				if (adap->claimed_log_addr)
> > +					adap->claimed_log_addr(adap, i);
> > +				break;
> > +			}
> > +		}
> > +	}
> > +	if (adap->num_log_addrs == 0) {
> > +		if (log_addrs->num_log_addrs > 1)
> > +			dprintk("could not claim last %d addresses\n",
> > +				log_addrs->num_log_addrs - 1);
> > +		adap->log_addr[adap->num_log_addrs++] = 15;
> > +		log_addrs->log_addr_type[0] =
> CEC_LOG_ADDR_TYPE_UNREGISTERED;
> > +		log_addrs->log_addr[0] = 15;
> > +		log_addrs->num_log_addrs = 1;
> > +		err = adap->adap_log_addr(adap, 15);
> > +		dprintk("claim addr %d (%d)\n", 15, adap->prim_device[0]);
> > +		if (err)
> > +			return err;
> > +		cec_report_phys_addr(adap, 15);
> > +		if (adap->claimed_log_addr)
> > +			adap->claimed_log_addr(adap, 0);
> > +	}
> > +	return 0;
> > +}
> > +
> > +static int cec_config_thread_func(void *arg)
> > +{
> > +	struct cec_log_addrs_int *cla_int = arg;
> > +	int err;
> > +
> > +	cla_int->err = err = cec_config_log_addrs(cla_int->adap,
> > +						  &cla_int->log_addrs);
> > +	cla_int->adap->kthread_config = NULL;
> > +	if (cla_int->free_on_exit)
> > +		kfree(cla_int);
> > +	else
> > +		complete(&cla_int->c);
> > +	return err;
> > +}
> > +
> > +int cec_claim_log_addrs(struct cec_adapter *adap,
> > +			struct cec_log_addrs *log_addrs, bool block)
> > +{
> > +	struct cec_log_addrs_int *cla_int;
> > +	int i;
> > +
> > +	if (adap->state == CEC_ADAP_STATE_DISABLED)
> > +		return -EINVAL;
> > +
> > +	if (log_addrs->num_log_addrs > CEC_MAX_LOG_ADDRS)
> > +		return -EINVAL;
> > +	if (log_addrs->num_log_addrs == 0) {
> > +		adap->num_log_addrs = 0;
> > +		adap->state = CEC_ADAP_STATE_IDLE;
> > +		return 0;
> > +	}
> > +	if (log_addrs->cec_version != CEC_VERSION_1_4B &&
> > +	    log_addrs->cec_version != CEC_VERSION_2_0)
> > +		return -EINVAL;
> > +	if (log_addrs->num_log_addrs > 1)
> > +		for (i = 0; i < log_addrs->num_log_addrs; i++)
> > +			if (log_addrs->log_addr_type[i] ==
> > +					CEC_LOG_ADDR_TYPE_UNREGISTERED)
> > +				return -EINVAL;
> > +	for (i = 0; i < log_addrs->num_log_addrs; i++) {
> > +		if (log_addrs->primary_device_type[i] >
> > +						CEC_PRIM_DEVTYPE_VIDEOPROC)
> > +			return -EINVAL;
> > +		if (log_addrs->primary_device_type[i] == 2)
> > +			return -EINVAL;
> > +		if (log_addrs->log_addr_type[i] >
> > +
CEC_LOG_ADDR_TYPE_UNREGISTERED)
> > +			return -EINVAL;
> > +	}
> > +
> > +	/* For phys addr 0xffff only the Unregistered functionality is
> > +	   allowed. */
> > +	if (adap->phys_addr == 0xffff &&
> > +	    (log_addrs->num_log_addrs > 1 ||
> > +	     log_addrs->log_addr_type[0] !=
> CEC_LOG_ADDR_TYPE_UNREGISTERED))
> > +		return -EINVAL;
> > +
> > +	cla_int = kzalloc(sizeof(*cla_int), GFP_KERNEL);
> > +	if (cla_int == NULL)
> > +		return -ENOMEM;
> > +	init_completion(&cla_int->c);
> > +	cla_int->free_on_exit = !block;
> > +	cla_int->adap = adap;
> > +	cla_int->log_addrs = *log_addrs;
> > +	adap->kthread_config = kthread_run(cec_config_thread_func,
> cla_int,
> > +							"cec_log_addrs");
> > +	if (block) {
> > +		wait_for_completion(&cla_int->c);
> > +		*log_addrs = cla_int->log_addrs;
> > +		kfree(cla_int);
> > +	}
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(cec_claim_log_addrs);
> > +
> > +static unsigned int cec_poll(struct file *filp,
> > +			       struct poll_table_struct *poll)
> > +{
> > +	struct cec_devnode *cecdev = cec_devnode_data(filp);
> > +	struct cec_adapter *adap = to_cec_adapter(cecdev);
> > +	unsigned res = 0;
> > +
> > +	if (!cec_devnode_is_registered(cecdev))
> > +		return POLLERR | POLLHUP;
> > +	mutex_lock(&adap->lock);
> > +	if (adap->tx_qcount < CEC_TX_QUEUE_SZ)
> > +		res |= POLLOUT | POLLWRNORM;
> > +	if (adap->rx_qcount)
> > +		res |= POLLIN | POLLRDNORM;
> > +	if (adap->ev_qcount)
> > +		res |= POLLPRI;
> > +	poll_wait(filp, &adap->waitq, poll);
> > +	mutex_unlock(&adap->lock);
> > +	return res;
> > +}
> > +
> > +static long cec_ioctl(struct file *filp, unsigned int cmd, unsigned
> long arg)
> > +{
> > +	struct cec_devnode *cecdev = cec_devnode_data(filp);
> > +	struct cec_adapter *adap = to_cec_adapter(cecdev);
> > +	void __user *parg = (void __user *)arg;
> > +	int err;
> > +
> > +	if (!cec_devnode_is_registered(cecdev))
> > +		return -EIO;
> > +
> > +	switch (cmd) {
> > +	case CEC_G_CAPS: {
> > +		struct cec_caps caps;
> > +
> > +		caps.available_log_addrs = 3;
> > +		caps.capabilities = adap->capabilities;
> > +		caps.version = adap->version;
> > +		caps.vendor_id = adap->vendor_id;
> > +		if (copy_to_user(parg, &caps, sizeof(caps)))
> > +			return -EFAULT;
> > +		break;
> > +	}
> > +
> > +	case CEC_TRANSMIT: {
> > +		struct cec_data data;
> > +
> > +		if (!(adap->capabilities & CEC_CAP_TRANSMIT))
> > +			return -ENOTTY;
> > +		if (copy_from_user(&data.msg, parg, sizeof(data.msg)))
> > +			return -EFAULT;
> > +		err = cec_transmit_msg(adap, &data,
> > +						!(filp->f_flags &
O_NONBLOCK));
> > +		if (err)
> > +			return err;
> > +		if (copy_to_user(parg, &data.msg, sizeof(data.msg)))
> > +			return -EFAULT;
> > +		break;
> > +	}
> > +
> > +	case CEC_RECEIVE: {
> > +		struct cec_data data;
> > +
> > +		if (!(adap->capabilities & CEC_CAP_RECEIVE))
> > +			return -ENOTTY;
> > +		if (copy_from_user(&data.msg, parg, sizeof(data.msg)))
> > +			return -EFAULT;
> > +		err = cec_receive_msg(adap, &data.msg,
> > +						!(filp->f_flags &
O_NONBLOCK));
> > +		if (err)
> > +			return err;
> > +		if (copy_to_user(parg, &data.msg, sizeof(data.msg)))
> > +			return -EFAULT;
> > +		break;
> > +	}
> > +
> > +	case CEC_G_EVENT: {
> > +		struct cec_event ev;
> > +
> > +		mutex_lock(&adap->lock);
> > +		err = -EAGAIN;
> > +		if (adap->ev_qcount) {
> > +			err = 0;
> > +			ev = adap->ev_queue[adap->ev_qstart];
> > +			adap->ev_qstart = (adap->ev_qstart + 1) %
> > +
CEC_EV_QUEUE_SZ;
> > +			adap->ev_qcount--;
> > +		}
> > +		mutex_unlock(&adap->lock);
> > +		if (err)
> > +			return err;
> > +		if (copy_to_user((void __user *)arg, &ev, sizeof(ev)))
> > +			return -EFAULT;
> > +		break;
> > +	}
> > +
> > +	case CEC_G_ADAP_STATE: {
> > +		u32 state = adap->state != CEC_ADAP_STATE_DISABLED;
> > +
> > +		if (copy_to_user(parg, &state, sizeof(state)))
> > +			return -EFAULT;
> > +		break;
> > +	}
> > +
> > +	case CEC_S_ADAP_STATE: {
> > +		u32 state;
> > +
> > +		if (!(adap->capabilities & CEC_CAP_STATE))
> > +			return -ENOTTY;
> > +		if (copy_from_user(&state, parg, sizeof(state)))
> > +			return -EFAULT;
> > +		if (!state && adap->state == CEC_ADAP_STATE_DISABLED)
> > +			return 0;
> > +		if (state && adap->state != CEC_ADAP_STATE_DISABLED)
> > +			return 0;
> > +		cec_enable(adap, !!state);
> > +		break;
> > +	}
> > +
> > +	case CEC_G_ADAP_PHYS_ADDR:
> > +		if (copy_to_user(parg, &adap->phys_addr,
> > +						sizeof(adap->phys_addr)))
> > +			return -EFAULT;
> > +		break;
> > +
> > +	case CEC_S_ADAP_PHYS_ADDR: {
> > +		u16 phys_addr;
> > +
> > +		if (!(adap->capabilities & CEC_CAP_PHYS_ADDR))
> > +			return -ENOTTY;
> > +		if (copy_from_user(&phys_addr, parg, sizeof(phys_addr)))
> > +			return -EFAULT;
> > +		adap->phys_addr = phys_addr;
> > +		break;
> > +	}
> > +
> > +	case CEC_G_ADAP_LOG_ADDRS: {
> > +		struct cec_log_addrs log_addrs;
> > +
> > +		log_addrs.cec_version = adap->version;
> > +		log_addrs.num_log_addrs = adap->num_log_addrs;
> > +		memcpy(log_addrs.primary_device_type, adap->prim_device,
> > +							CEC_MAX_LOG_ADDRS);
> > +		memcpy(log_addrs.log_addr_type, adap->log_addr_type,
> > +							CEC_MAX_LOG_ADDRS);
> > +		memcpy(log_addrs.log_addr, adap->log_addr,
> > +							CEC_MAX_LOG_ADDRS);
> > +
> > +		if (copy_to_user(parg, &log_addrs, sizeof(log_addrs)))
> > +			return -EFAULT;
> > +		break;
> > +	}
> > +
> > +	case CEC_S_ADAP_LOG_ADDRS: {
> > +		struct cec_log_addrs log_addrs;
> > +
> > +		if (!(adap->capabilities & CEC_CAP_LOG_ADDRS))
> > +			return -ENOTTY;
> > +		if (copy_from_user(&log_addrs, parg, sizeof(log_addrs)))
> > +			return -EFAULT;
> > +		err = cec_claim_log_addrs(adap, &log_addrs, true);
> > +		if (err)
> > +			return err;
> > +
> > +		if (copy_to_user(parg, &log_addrs, sizeof(log_addrs)))
> > +			return -EFAULT;
> > +		break;
> > +	}
> > +
> > +	case CEC_G_VENDOR_ID:
> > +		if (copy_to_user(parg, &adap->vendor_id,
> > +						sizeof(adap->vendor_id)))
> > +			return -EFAULT;
> > +		break;
> > +
> > +	case CEC_S_VENDOR_ID: {
> > +		u32 vendor_id;
> > +
> > +		if (!(adap->capabilities & CEC_CAP_VENDOR_ID))
> > +			return -ENOTTY;
> > +		if (copy_from_user(&vendor_id, parg, sizeof(vendor_id)))
> > +			return -EFAULT;
> > +		/* Vendori ID is a 24 bit number, so check if the value is
> > +		 * within the correct range. */
> > +		if ((vendor_id & 0xff000000) != 0)
> > +			return -EINVAL;
> > +		adap->vendor_id = vendor_id;
> > +		break;
> > +	}
> > +
> > +	default:
> > +		return -ENOTTY;
> > +	}
> > +	return 0;
> > +}
> > +
> > +/* Override for the open function */
> > +static int cec_open(struct inode *inode, struct file *filp)
> > +{
> > +	struct cec_devnode *cecdev;
> > +
> > +	/* Check if the cec device is available. This needs to be done
> with
> > +	 * the cec_devnode_lock held to prevent an open/unregister race:
> > +	 * without the lock, the device could be unregistered and freed
> between
> > +	 * the cec_devnode_is_registered() and get_device() calls,
> leading to
> > +	 * a crash.
> > +	 */
> > +	mutex_lock(&cec_devnode_lock);
> > +	cecdev = container_of(inode->i_cdev, struct cec_devnode, cdev);
> > +	/* return ENXIO if the cec device has been removed
> > +	   already or if it is not registered anymore. */
> > +	if (!cec_devnode_is_registered(cecdev)) {
> > +		mutex_unlock(&cec_devnode_lock);
> > +		return -ENXIO;
> > +	}
> > +	/* and increase the device refcount */
> > +	get_device(&cecdev->dev);
> > +	mutex_unlock(&cec_devnode_lock);
> > +
> > +	filp->private_data = cecdev;
> > +
> > +	return 0;
> > +}
> > +
> > +/* Override for the release function */
> > +static int cec_release(struct inode *inode, struct file *filp)
> > +{
> > +	struct cec_devnode *cecdev = cec_devnode_data(filp);
> > +	int ret = 0;
> > +
> > +	/* decrease the refcount unconditionally since the release()
> > +	   return value is ignored. */
> > +	put_device(&cecdev->dev);
> > +	filp->private_data = NULL;
> > +	return ret;
> > +}
> > +
> > +static const struct file_operations cec_devnode_fops = {
> > +	.owner = THIS_MODULE,
> > +	.open = cec_open,
> > +	.unlocked_ioctl = cec_ioctl,
> > +	.release = cec_release,
> > +	.poll = cec_poll,
> > +	.llseek = no_llseek,
> > +};
> > +
> > +/**
> > + * cec_devnode_register - register a cec device node
> > + * @cecdev: cec device node structure we want to register
> > + *
> > + * The registration code assigns minor numbers and registers the new
> device node
> > + * with the kernel. An error is returned if no free minor number can
> be found,
> > + * or if the registration of the device node fails.
> > + *
> > + * Zero is returned on success.
> > + *
> > + * Note that if the cec_devnode_register call fails, the release()
> callback of
> > + * the cec_devnode structure is *not* called, so the caller is
> responsible for
> > + * freeing any data.
> > + */
> > +static int __must_check cec_devnode_register(struct cec_devnode
> *cecdev,
> > +		struct module *owner)
> > +{
> > +	int minor;
> > +	int ret;
> > +
> > +	/* Part 1: Find a free minor number */
> > +	mutex_lock(&cec_devnode_lock);
> > +	minor = find_next_zero_bit(cec_devnode_nums, CEC_NUM_DEVICES, 0);
> > +	if (minor == CEC_NUM_DEVICES) {
> > +		mutex_unlock(&cec_devnode_lock);
> > +		pr_err("could not get a free minor\n");
> > +		return -ENFILE;
> > +	}
> > +
> > +	set_bit(minor, cec_devnode_nums);
> > +	mutex_unlock(&cec_devnode_lock);
> > +
> > +	cecdev->minor = minor;
> > +
> > +	/* Part 2: Initialize and register the character device */
> > +	cdev_init(&cecdev->cdev, &cec_devnode_fops);
> > +	cecdev->cdev.owner = owner;
> > +
> > +	ret = cdev_add(&cecdev->cdev, MKDEV(MAJOR(cec_dev_t), cecdev-
> >minor),
> > +									1);
> > +	if (ret < 0) {
> > +		pr_err("%s: cdev_add failed\n", __func__);
> > +		goto error;
> > +	}
> > +
> > +	/* Part 3: Register the cec device */
> > +	cecdev->dev.bus = &cec_bus_type;
> > +	cecdev->dev.devt = MKDEV(MAJOR(cec_dev_t), cecdev->minor);
> > +	cecdev->dev.release = cec_devnode_release;
> > +	if (cecdev->parent)
> > +		cecdev->dev.parent = cecdev->parent;
> > +	dev_set_name(&cecdev->dev, "cec%d", cecdev->minor);
> > +	ret = device_register(&cecdev->dev);
> > +	if (ret < 0) {
> > +		pr_err("%s: device_register failed\n", __func__);
> > +		goto error;
> > +	}
> > +
> > +	/* Part 4: Activate this minor. The char device can now be used.
> */
> > +	set_bit(CEC_FLAG_REGISTERED, &cecdev->flags);
> > +
> > +	return 0;
> > +
> > +error:
> > +	cdev_del(&cecdev->cdev);
> > +	clear_bit(cecdev->minor, cec_devnode_nums);
> > +	return ret;
> > +}
> > +
> > +/**
> > + * cec_devnode_unregister - unregister a cec device node
> > + * @cecdev: the device node to unregister
> > + *
> > + * This unregisters the passed device. Future open calls will be met
> with
> > + * errors.
> > + *
> > + * This function can safely be called if the device node has never
> been
> > + * registered or has already been unregistered.
> > + */
> > +static void cec_devnode_unregister(struct cec_devnode *cecdev)
> > +{
> > +	/* Check if cecdev was ever registered at all */
> > +	if (!cec_devnode_is_registered(cecdev))
> > +		return;
> > +
> > +	mutex_lock(&cec_devnode_lock);
> > +	clear_bit(CEC_FLAG_REGISTERED, &cecdev->flags);
> > +	mutex_unlock(&cec_devnode_lock);
> > +	device_unregister(&cecdev->dev);
> > +}
> > +
> > +int cec_create_adapter(struct cec_adapter *adap, const char *name,
> u32 caps)
> > +{
> > +	int res = 0;
> > +
> > +	adap->state = CEC_ADAP_STATE_DISABLED;
> > +	adap->name = name;
> > +	adap->phys_addr = 0xffff;
> > +	adap->capabilities = caps;
> > +	adap->version = CEC_VERSION_1_4B;
> > +	mutex_init(&adap->lock);
> > +	adap->kthread = kthread_run(cec_thread_func, adap, name);
> > +	init_waitqueue_head(&adap->kthread_waitq);
> > +	init_waitqueue_head(&adap->waitq);
> > +	if (IS_ERR(adap->kthread)) {
> > +		pr_err("cec-%s: kernel_thread() failed\n", name);
> > +		return PTR_ERR(adap->kthread);
> > +	}
> > +	if (caps) {
> > +		res = cec_devnode_register(&adap->devnode, adap->owner);
> > +		if (res)
> > +			kthread_stop(adap->kthread);
> > +	}
> > +	adap->recv_notifier = cec_receive_notify;
> > +
> > +	/* Prepare the RC input device */
> > +	adap->rc = rc_allocate_device();
> > +	if (!adap->rc) {
> > +		pr_err("cec-%s: failed to allocate memory for rc_dev\n",
> name);
> > +		cec_devnode_unregister(&adap->devnode);
> > +		kthread_stop(adap->kthread);
> > +		return -ENOMEM;
> > +	}
> > +
> > +	snprintf(adap->input_name, sizeof(adap->input_name), "RC for %s",
> name);
> > +	snprintf(adap->input_phys, sizeof(adap->input_phys), "%s/input0",
> name);
> > +	strncpy(adap->input_drv, name, sizeof(adap->input_drv));
> > +
> > +	adap->rc->input_name = adap->input_name;
> > +	adap->rc->input_phys = adap->input_phys;
> > +	adap->rc->dev.parent = &adap->devnode.dev;
> > +	adap->rc->driver_name = adap->input_drv;
> > +	adap->rc->driver_type = RC_DRIVER_CEC;
> > +	adap->rc->allowed_protocols = RC_BIT_CEC;
> > +	adap->rc->priv = adap;
> > +	adap->rc->map_name = RC_MAP_CEC;
> > +	adap->rc->timeout = MS_TO_NS(100);
> > +
> > +	res = rc_register_device(adap->rc);
> > +
> > +	if (res) {
> > +		pr_err("cec-%s: failed to prepare input device\n", name);
> > +		cec_devnode_unregister(&adap->devnode);
> > +		rc_free_device(adap->rc);
> > +		kthread_stop(adap->kthread);
> > +	}
> > +
> > +	return res;
> > +}
> > +EXPORT_SYMBOL_GPL(cec_create_adapter);
> > +
> > +void cec_delete_adapter(struct cec_adapter *adap)
> > +{
> > +	if (adap->kthread == NULL)
> > +		return;
> > +	kthread_stop(adap->kthread);
> > +	if (adap->kthread_config)
> > +		kthread_stop(adap->kthread_config);
> > +	adap->state = CEC_ADAP_STATE_DISABLED;
> > +	if (cec_devnode_is_registered(&adap->devnode))
> > +		cec_devnode_unregister(&adap->devnode);
> > +}
> > +EXPORT_SYMBOL_GPL(cec_delete_adapter);
> > +
> > +/*
> > + *	Initialise cec for linux
> > + */
> > +static int __init cec_devnode_init(void)
> > +{
> > +	int ret;
> > +
> > +	pr_info("Linux cec interface: v0.10\n");
> > +	ret = alloc_chrdev_region(&cec_dev_t, 0, CEC_NUM_DEVICES,
> > +				  CEC_NAME);
> > +	if (ret < 0) {
> > +		pr_warn("cec: unable to allocate major\n");
> > +		return ret;
> > +	}
> > +
> > +	ret = bus_register(&cec_bus_type);
> > +	if (ret < 0) {
> > +		unregister_chrdev_region(cec_dev_t, CEC_NUM_DEVICES);
> > +		pr_warn("cec: bus_register failed\n");
> > +		return -EIO;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static void __exit cec_devnode_exit(void)
> > +{
> > +	bus_unregister(&cec_bus_type);
> > +	unregister_chrdev_region(cec_dev_t, CEC_NUM_DEVICES);
> > +}
> > +
> > +subsys_initcall(cec_devnode_init);
> > +module_exit(cec_devnode_exit)
> > +
> > +MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>");
> > +MODULE_DESCRIPTION("Device node registration for cec drivers");
> > +MODULE_LICENSE("GPL");
> > diff --git a/include/media/cec.h b/include/media/cec.h
> > new file mode 100644
> > index 0000000..df3b9e93
> > --- /dev/null
> > +++ b/include/media/cec.h
> > @@ -0,0 +1,140 @@
> > +#ifndef _CEC_DEVNODE_H
> > +#define _CEC_DEVNODE_H
> > +
> > +#include <linux/poll.h>
> > +#include <linux/fs.h>
> > +#include <linux/device.h>
> > +#include <linux/cdev.h>
> > +#include <linux/kthread.h>
> > +#include <linux/cec.h>
> > +#include <media/rc-core.h>
> > +
> > +#define cec_phys_addr_exp(pa) \
> > +	((pa) >> 12), ((pa) >> 8) & 0xf, ((pa) >> 4) & 0xf, (pa) & 0xf
> > +
> > +/*
> > + * Flag to mark the cec_devnode struct as registered. Drivers must
> not touch
> > + * this flag directly, it will be set and cleared by
> cec_devnode_register and
> > + * cec_devnode_unregister.
> > + */
> > +#define CEC_FLAG_REGISTERED	0
> > +
> > +/**
> > + * struct cec_devnode - cec device node
> > + * @parent:	parent device
> > + * @minor:	device node minor number
> > + * @flags:	flags, combination of the CEC_FLAG_* constants
> > + *
> > + * This structure represents a cec-related device node.
> > + *
> > + * The @parent is a physical device. It must be set by core or
> device drivers
> > + * before registering the node.
> > + */
> > +struct cec_devnode {
> > +	/* sysfs */
> > +	struct device dev;		/* cec device */
> > +	struct cdev cdev;		/* character device */
> > +	struct device *parent;		/* device parent */
> > +
> > +	/* device info */
> > +	int minor;
> > +	unsigned long flags;		/* Use bitops to access flags */
> > +
> > +	/* callbacks */
> > +	void (*release)(struct cec_devnode *cecdev);
> > +};
> > +
> > +static inline int cec_devnode_is_registered(struct cec_devnode
> *cecdev)
> > +{
> > +	return test_bit(CEC_FLAG_REGISTERED, &cecdev->flags);
> > +}
> > +
> > +struct cec_adapter;
> > +struct cec_data;
> > +
> > +typedef int (*cec_notify)(struct cec_adapter *adap, struct cec_data
> *data,
> > +			  void *priv);
> > +typedef int (*cec_recv_notify)(struct cec_adapter *adap, struct
> cec_msg *msg);
> > +
> > +struct cec_data {
> > +	struct cec_msg msg;
> > +	cec_notify func;
> > +	void *priv;
> > +};
> > +
> > +/* Unconfigured state */
> > +#define CEC_ADAP_STATE_DISABLED		0
> > +#define CEC_ADAP_STATE_UNCONF		1
> > +#define CEC_ADAP_STATE_IDLE		2
> > +#define CEC_ADAP_STATE_TRANSMITTING	3
> > +#define CEC_ADAP_STATE_WAIT		4
> > +#define CEC_ADAP_STATE_RECEIVED		5
> > +
> > +#define CEC_TX_QUEUE_SZ	(4)
> > +#define CEC_RX_QUEUE_SZ	(4)
> > +#define CEC_EV_QUEUE_SZ	(16)
> > +
> > +struct cec_adapter {
> > +	struct module *owner;
> > +	const char *name;
> > +	struct cec_devnode devnode;
> > +	struct mutex lock;
> > +	struct rc_dev *rc;
> > +
> > +	struct cec_data tx_queue[CEC_TX_QUEUE_SZ];
> > +	u8 tx_qstart, tx_qcount;
> > +
> > +	struct cec_msg rx_queue[CEC_RX_QUEUE_SZ];
> > +	u8 rx_qstart, rx_qcount;
> > +
> > +	struct cec_event ev_queue[CEC_EV_QUEUE_SZ];
> > +	u8 ev_qstart, ev_qcount;
> > +
> > +	cec_recv_notify recv_notifier;
> > +	struct task_struct *kthread_config;
> > +
> > +	struct task_struct *kthread;
> > +	wait_queue_head_t kthread_waitq;
> > +	wait_queue_head_t waitq;
> > +
> > +	u8 state;
> > +	u32 capabilities;
> > +	u16 phys_addr;
> > +	u32 vendor_id;
> > +	u8 version;
> > +	u8 num_log_addrs;
> > +	u8 prim_device[CEC_MAX_LOG_ADDRS];
> > +	u8 log_addr_type[CEC_MAX_LOG_ADDRS];
> > +	u8 log_addr[CEC_MAX_LOG_ADDRS];
> > +	u8 promiscuous;
> > +
> > +	char input_name[32];
> > +	char input_phys[32];
> > +	char input_drv[32];
> > +
> > +	int (*adap_enable)(struct cec_adapter *adap, bool enable);
> > +	int (*adap_log_addr)(struct cec_adapter *adap, u8 logical_addr);
> > +	int (*adap_transmit)(struct cec_adapter *adap, struct cec_msg
> *msg);
> > +	void (*adap_transmit_timed_out)(struct cec_adapter *adap);
> > +
> > +	void (*claimed_log_addr)(struct cec_adapter *adap, u8 idx);
> > +	int (*received)(struct cec_adapter *adap, struct cec_msg *msg);
> > +};
> > +
> > +#define to_cec_adapter(node) container_of(node, struct cec_adapter,
> devnode)
> > +
> > +int cec_create_adapter(struct cec_adapter *adap, const char *name,
> u32 caps);
> > +void cec_delete_adapter(struct cec_adapter *adap);
> > +int cec_transmit_msg(struct cec_adapter *adap, struct cec_data *data,
> > +		     bool block);
> > +int cec_receive_msg(struct cec_adapter *adap, struct cec_msg *msg,
> bool block);
> > +void cec_post_event(struct cec_adapter *adap, u32 event);
> > +int cec_claim_log_addrs(struct cec_adapter *adap,
> > +			struct cec_log_addrs *log_addrs, bool block);
> > +int cec_enable(struct cec_adapter *adap, bool enable);
> > +
> > +/* Called by the adapter */
> > +void cec_transmit_done(struct cec_adapter *adap, u32 status);
> > +void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg);
> > +
> > +#endif /* _CEC_DEVNODE_H */
> > diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
> > index 4842a98..5854cfd 100644
> > --- a/include/uapi/linux/Kbuild
> > +++ b/include/uapi/linux/Kbuild
> > @@ -81,6 +81,7 @@ header-y += capi.h
> >   header-y += cciss_defs.h
> >   header-y += cciss_ioctl.h
> >   header-y += cdrom.h
> > +header-y += cec.h
> >   header-y += cgroupstats.h
> >   header-y += chio.h
> >   header-y += cm4000_cs.h
> > diff --git a/include/uapi/linux/cec.h b/include/uapi/linux/cec.h
> > new file mode 100644
> > index 0000000..bb6d66c
> > --- /dev/null
> > +++ b/include/uapi/linux/cec.h
> > @@ -0,0 +1,303 @@
> > +#ifndef _CEC_H
> > +#define _CEC_H
> > +
> > +#include <linux/types.h>
> > +
> > +struct cec_time {
> > +	__u64 sec;
> > +	__u64 nsec;
> > +};
> > +
> > +struct cec_msg {
> > +	struct cec_time ts;
> > +	__u32 len;
> > +	__u32 status;
> > +	__u32 timeout;
> > +	/* timeout (in ms) is used to timeout CEC_RECEIVE.
> > +	   Set to 0 if you want to wait forever. */
> > +	__u8  msg[16];
> > +	__u8  reply;
> > +	/* If non-zero, then wait for a reply with this opcode.
> > +	   If there was an error when sending the msg or FeatureAbort
> > +	   was returned, then reply is set to 0.
> > +	   If reply is non-zero upon return, then len/msg are set to
> > +	   the received message.
> > +	   If reply is zero upon return and status has the
> > +	   CEC_TX_STATUS_FEATURE_ABORT bit set, then len/msg are set to
> the
> > +	   received feature abort message.
> > +	   If reply is zero upon return and status has the
> > +	   CEC_TX_STATUS_REPLY_TIMEOUT
> > +	   bit set, then no reply was seen at all.
> > +	   This field is ignored with CEC_RECEIVE.
> > +	   If reply is non-zero for CEC_TRANSMIT and the message is a
> broadcast,
> > +	   then -EINVAL is returned.
> > +	   if reply is non-zero, then timeout is set to 1000 (the
> required
> > +	   maximum response time).
> > +	 */
> > +	__u8 reserved[31];
> > +};
> > +
> > +static inline __u8 cec_msg_initiator(const struct cec_msg *msg)
> > +{
> > +	return msg->msg[0] >> 4;
> > +}
> > +
> > +static inline __u8 cec_msg_destination(const struct cec_msg *msg)
> > +{
> > +	return msg->msg[0] & 0xf;
> > +}
> > +
> > +static inline bool cec_msg_is_broadcast(const struct cec_msg *msg)
> > +{
> > +	return (msg->msg[0] & 0xf) == 0xf;
> > +}
> > +
> > +/* cec status field */
> > +#define CEC_TX_STATUS_OK            (0)
> > +#define CEC_TX_STATUS_ARB_LOST      (1 << 0)
> > +#define CEC_TX_STATUS_RETRY_TIMEOUT (1 << 1)
> > +#define CEC_TX_STATUS_FEATURE_ABORT (1 << 2)
> > +#define CEC_TX_STATUS_REPLY_TIMEOUT (1 << 3)
> > +#define CEC_RX_STATUS_READY         (0)
> > +
> > +#define CEC_LOG_ADDR_INVALID 0xff
> > +
> > +/* The maximum number of logical addresses one device can be
> assigned to.
> > + * The CEC 2.0 spec allows for only 2 logical addresses at the
> moment. The
> > + * Analog Devices CEC hardware supports 3. So let's go wild and go
> for 4. */
> > +#define CEC_MAX_LOG_ADDRS 4
> > +
> > +/* The "Primary Device Type" */
> > +#define CEC_PRIM_DEVTYPE_TV		0
> > +#define CEC_PRIM_DEVTYPE_RECORD		1
> > +#define CEC_PRIM_DEVTYPE_TUNER		3
> > +#define CEC_PRIM_DEVTYPE_PLAYBACK	4
> > +#define CEC_PRIM_DEVTYPE_AUDIOSYSTEM	5
> > +#define CEC_PRIM_DEVTYPE_SWITCH		6
> > +#define CEC_PRIM_DEVTYPE_VIDEOPROC	7
> > +
> > +/* The "All Device Types" flags (CEC 2.0) */
> > +#define CEC_FL_ALL_DEVTYPE_TV		(1 << 7)
> > +#define CEC_FL_ALL_DEVTYPE_RECORD	(1 << 6)
> > +#define CEC_FL_ALL_DEVTYPE_TUNER	(1 << 5)
> > +#define CEC_FL_ALL_DEVTYPE_PLAYBACK	(1 << 4)
> > +#define CEC_FL_ALL_DEVTYPE_AUDIOSYSTEM	(1 << 3)
> > +#define CEC_FL_ALL_DEVTYPE_SWITCH	(1 << 2)
> > +/* And if you wondering what happened to VIDEOPROC devices: those
> should
> > + * be mapped to a SWITCH. */
> > +
> > +/* The logical address types that the CEC device wants to claim */
> > +#define CEC_LOG_ADDR_TYPE_TV		0
> > +#define CEC_LOG_ADDR_TYPE_RECORD	1
> > +#define CEC_LOG_ADDR_TYPE_TUNER		2
> > +#define CEC_LOG_ADDR_TYPE_PLAYBACK	3
> > +#define CEC_LOG_ADDR_TYPE_AUDIOSYSTEM	4
> > +#define CEC_LOG_ADDR_TYPE_SPECIFIC	5
> > +#define CEC_LOG_ADDR_TYPE_UNREGISTERED	6
> > +/* Switches should use UNREGISTERED.
> > + * Video processors should use SPECIFIC. */
> > +
> > +/* The CEC version */
> > +#define CEC_VERSION_1_4B		5
> > +#define CEC_VERSION_2_0			6
> > +
> > +struct cec_event {
> > +	struct cec_time ts;
> > +	__u32 event;
> > +	__u8 reserved[4];
> > +};
> > +
> > +/* The CEC state */
> > +#define CEC_STATE_DISABLED		0
> > +#define CEC_STATE_ENABLED		1
> > +
> > +/* Userspace has to configure the adapter state (enable/disable) */
> > +#define CEC_CAP_STATE		(1 << 0)
> > +/* Userspace has to configure the physical address */
> > +#define CEC_CAP_PHYS_ADDR	(1 << 1)
> > +/* Userspace has to configure the logical addresses */
> > +#define CEC_CAP_LOG_ADDRS	(1 << 2)
> > +/* Userspace can transmit messages */
> > +#define CEC_CAP_TRANSMIT	(1 << 3)
> > +/* Userspace can receive messages */
> > +#define CEC_CAP_RECEIVE		(1 << 4)
> > +/* Userspace has to configure the vendor id */
> > +#define CEC_CAP_VENDOR_ID	(1 << 5)
> > +/* The hardware has the possibility to work in the promiscuous mode
> */
> > +#define CEC_CAP_PROMISCUOUS	(1 << 6)
> > +
> > +struct cec_caps {
> > +	__u32 available_log_addrs;
> > +	__u32 capabilities;
> > +	__u32 vendor_id;
> > +	__u8  version;
> > +	__u8  reserved[11];
> > +};
> > +
> > +struct cec_log_addrs {
> > +	__u8 cec_version;
> > +	__u8 num_log_addrs;
> > +	__u8 primary_device_type[CEC_MAX_LOG_ADDRS];
> > +	__u8 log_addr_type[CEC_MAX_LOG_ADDRS];
> > +	__u8 log_addr[CEC_MAX_LOG_ADDRS];
> > +
> > +	/* CEC 2.0 */
> > +	__u8 all_device_types;
> > +	__u8 features[CEC_MAX_LOG_ADDRS][12];
> > +
> > +	__u8 reserved[9];
> > +};
> > +
> > +/* Commands */
> > +
> > +/* One Touch Play Feature */
> > +#define CEC_OP_ACTIVE_SOURCE			0x82
> > +#define CEC_OP_IMAGE_VIEW_ON			0x04
> > +#define CEC_OP_TEXT_VIEW_ON			0x0d
> > +
> > +/* Routing Control Feature */
> > +#define CEC_OP_ACTIVE_SOURCE			0x82
> > +#define CEC_OP_INACTIVE_SOURCE			0x9d
> > +#define CEC_OP_REQUEST_ACTIVE_SOURCE		0x85
> > +#define CEC_OP_ROUTING_CHANGE			0x80
> > +#define CEC_OP_ROUTING_INFORMATION		0x81
> > +#define CEC_OP_SET_STREAM_PATH			0x86
> > +
> > +/* Standby Feature */
> > +#define CEC_OP_STANDBY				0x36
> > +
> > +/* One Touch Record Feature */
> > +#define CEC_OP_RECORD_OFF			0x0b
> > +#define CEC_OP_RECORD_ON			0x09
> > +#define CEC_OP_RECORD_STATUS			0x0a
> > +#define CEC_OP_RECORD_TV_SCREEN			0x0f
> > +
> > +/* Timer Programming Feature */
> > +#define CEC_OP_CLEAR_ANALOGUE_TIMER		0x33
> > +#define CEC_OP_CLEAR_DIGITAL_TIMER		0x99
> > +#define CEC_OP_CLEAR_EXT_TIMER			0xa1
> > +#define CEC_OP_SET_ANALOGUE_TIMER		0x34
> > +#define CEC_OP_SET_DIGITAL_TIMER		0x97
> > +#define CEC_OP_SET_EXT_TIMER			0xa2
> > +#define CEC_OP_SET_EXT_PROGRAM_TIMER		0x67
> > +#define CEC_OP_TIMER_CLEARED_STATUS		0x43
> > +#define CEC_OP_TIMER_STATUS			0x35
> > +
> > +/* System Information Feature */
> > +#define CEC_OP_CEC_VERSION			0x9e
> > +#define CEC_OP_GET_CEC_VERSION			0x9f
> > +#define CEC_OP_GIVE_PHYSICAL_ADDR		0x83
> > +#define CEC_OP_GET_MENU_LANGUAGE		0x91
> > +#define CEC_OP_REPORT_PHYSICAL_ADDR		0x84
> > +#define CEC_OP_SET_MENU_LANGUAGE		0x32
> > +
> > +/* Deck Control Feature */
> > +#define CEC_OP_DECK_CONTROL			0x42
> > +#define CEC_OP_DECK_STATUS			0x1b
> > +#define CEC_OP_GIVE_DECK_STATUS			0x1a
> > +#define CEC_OP_PLAY				0x41
> > +
> > +/* Tuner Control Feature */
> > +#define CEC_OP_GIVE_TUNER_DEVICE_STATUS		0x08
> > +#define CEC_OP_SELECT_ANALOGUE_SERVICE		0x92
> > +#define CEC_OP_SELECT_DIGITAL_SERVICE		0x93
> > +#define CEC_OP_TUNER_DEVICE_STATUS		0x07
> > +#define CEC_OP_TUNER_STEP_DECREMENT		0x06
> > +#define CEC_OP_TUNER_STEP_INCREMENT		0x05
> > +
> > +/* Vendor Specific Commands Feature */
> > +#define CEC_OP_CEC_VERSION			0x9e
> > +#define CEC_OP_DEVICE_VENDOR_ID			0x87
> > +#define CEC_OP_GET_CEC_VERSION			0x9f
> > +#define CEC_OP_GIVE_DEVICE_VENDOR_ID		0x8c
> > +#define CEC_OP_VENDOR_COMMAND			0x89
> > +#define CEC_OP_VENDOR_COMMAND_WITH_ID		0xa0
> > +#define CEC_OP_VENDOR_REMOTE_BUTTON_DOWN	0x8a
> > +#define CEC_OP_VENDOR_REMOTE_BUTTON_UP		0x8b
> > +
> > +/* OSD Display Feature */
> > +#define CEC_OP_SET_OSD_STRING			0x64
> > +
> > +/* Device OSD Transfer Feature */
> > +#define CEC_OP_GIVE_OSD_NAME			0x46
> > +#define CEC_OP_SET_OSD_NAME			0x47
> > +
> > +/* Device Menu Control Feature */
> > +#define CEC_OP_MENU_REQUEST			0x8d
> > +#define CEC_OP_MENU_STATUS			0x8e
> > +#define CEC_OP_USER_CONTROL_PRESSED		0x44
> > +#define CEC_OP_USER_CONTROL_RELEASED		0x45
> > +
> > +/* Power Status Feature */
> > +#define CEC_OP_GIVE_DEVICE_POWER_STATUS		0x8f
> > +#define CEC_OP_REPORT_POWER_STATUS		0x90
> > +#define CEC_OP_FEATURE_ABORT			0x00
> > +#define CEC_OP_ABORT				0xff
> > +
> > +/* System Audio Control Feature */
> > +#define CEC_OP_GIVE_AUDIO_STATUS		0x71
> > +#define CEC_OP_GIVE_SYSTEM_AUDIO_MODE_STATUS	0x7d
> > +#define CEC_OP_REPORT_AUDIO_STATUS		0x7a
> > +#define CEC_OP_SET_SYSTEM_AUDIO_MODE		0x72
> > +#define CEC_OP_SYSTEM_AUDIO_MODE_REQUEST	0x70
> > +#define CEC_OP_SYSTEM_AUDIO_MODE_STATUS		0x7e
> > +
> > +/* Audio Rate Control Feature */
> > +#define CEC_OP_SET_AUDIO_RATE			0x9a
> > +
> > +/* Events */
> > +/* Event that occurs when a cable is connected */
> > +#define CEC_EVENT_CONNECT	1
> > +/* Event that occurs when all logical addresses were claimed */
> > +#define CEC_EVENT_READY		2
> > +/* Event that is sent when the cable is disconnected */
> > +#define CEC_EVENT_DISCONNECT	3
> > +
> > +/* ioctls */
> > +
> > +/* issue a CEC command */
> > +#define CEC_G_CAPS		_IOWR('a', 0, struct cec_caps)
> > +#define CEC_TRANSMIT		_IOWR('a', 1, struct cec_msg)
> > +#define CEC_RECEIVE		_IOWR('a', 2, struct cec_msg)
> > +
> > +/*
> > +   Configure the CEC adapter. It sets the device type and which
> > +   logical types it will try to claim. It will return which
> > +   logical addresses it could actually claim.
> > +   An error is returned if the adapter is disabled or if there
> > +   is no physical address assigned.
> > + */
> > +
> > +#define CEC_G_ADAP_LOG_ADDRS	_IOR('a', 3, struct cec_log_addrs)
> > +#define CEC_S_ADAP_LOG_ADDRS	_IOWR('a', 4, struct cec_log_addrs)
> > +
> > +/*
> > +   Enable/disable the adapter. The Set state ioctl may not
> > +   be available if that is handled internally.
> > + */
> > +#define CEC_G_ADAP_STATE	_IOR('a', 5, __u32)
> > +#define CEC_S_ADAP_STATE	_IOW('a', 6, __u32)
> > +
> > +/*
> > +   phys_addr is either 0 (if this is the CEC root device)
> > +   or a valid physical address obtained from the sink's EDID
> > +   as read by this CEC device (if this is a source device)
> > +   or a physical address obtained and modified from a sink
> > +   EDID and used for a sink CEC device.
> > +   If nothing is connected, then phys_addr is 0xffff.
> > +   See HDMI 1.4b, section 8.7 (Physical Address).
> > +
> > +   The Set ioctl may not be available if that is handled
> > +   internally.
> > + */
> > +#define CEC_G_ADAP_PHYS_ADDR	_IOR('a', 7, __u16)
> > +#define CEC_S_ADAP_PHYS_ADDR	_IOW('a', 8, __u16)
> > +
> > +#define CEC_G_EVENT		_IOWR('a', 9, struct cec_event)
> > +/*
> > +   Read and set the vendor ID of the CEC adapter.
> > + */
> > +#define CEC_G_VENDOR_ID		_IOR('a', 9, __u32)
> > +#define CEC_S_VENDOR_ID		_IOW('a', 10, __u32)
> > +
> > +#endif
> >
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media"
> in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html



^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v4 06/10] cec: add HDMI CEC framework
  2015-04-27  8:09     ` Kamil Debski
@ 2015-04-27  8:19       ` Hans Verkuil
  0 siblings, 0 replies; 27+ messages in thread
From: Hans Verkuil @ 2015-04-27  8:19 UTC (permalink / raw)
  To: Kamil Debski, 'Lars Op den Kamp', dri-devel, linux-media
  Cc: m.szyprowski, mchehab, kyungmin.park, thomas, sean,
	dmitry.torokhov, linux-input, linux-samsung-soc,
	'Hans Verkuil'

Hi Lars,

My thanks as well for your comments.

I'd like to add some background information as well as to why we move
the core CEC support into the kernel: the main reason for doing this
is to support the HEAC part of the CEC protocol. Specifically the ARC
support and handling the hotplug detect CEC/HEAC messages. This has to
be handled in the kernel and cannot be left to userspace. While the
current framework does not yet handle these messages support for this
will appear, probably later this year since I will have to work on ARC.

Out of curiosity: have you ever seen CEC adapters that implement the
ethernet part of HEAC? My understanding is that nobody uses that part
since wifi is the standard these days. But perhaps you know of examples
where it was actually implemented.

Regards,

	Hans

On 04/27/2015 10:09 AM, Kamil Debski wrote:
> Hi Lars, 
> 
> Thank you for your comments.
> 
> From: linux-media-owner@vger.kernel.org [mailto:linux-media-
> owner@vger.kernel.org] On Behalf Of Lars Op den Kamp
> Sent: Friday, April 24, 2015 12:04 PM
>  
>> Hi Kamil, Hans,
>>
>> I'm the main developer of libCEC
>> (https://github.com/Pulse-Eight/libcec). Sorry for the late time to
>> jump
>> in here, but I wasn't signed up to this mailing list and someone
>> pointed
>> me to this discussion.
>>
>> Unfortunately this approach will not work with half the TVs that are
>> out
>> there. I'll explain why:
>>
>> * because of how some (common) brands implemented CEC in their TVs,
>> this
>> implementation will not work, as the TV will just reject it. In libCEC,
>> we've created work arounds for brands like this. Without these work
>> arounds, your in-kernel implementation will be very vendor specific.
>> e.g. this implementation will work for Samsung's TVs, but not for the
>> TVs made by another big TV brand. All commands, including CEC_OP_ABORT,
>> should be passed to userspace to make it work with all brands.
>>
>> * it should be made possible to not have the kernel send any CEC
>> message, try to process any received CEC message, or ack to any logical
>> address at all, to allow libraries like libCEC to fully handle all CEC
>> traffic. Some brands only enable routing of some CEC keys when a
>> specific device type is used. libCEC will allocate a logical address of
>> the correct type for the brand that's used. If another address is first
>> allocated by the kernel, and the TV communicates with it to find out
>> it's name and things like that, and libCEC allocates another address a
>> bit later when initialised, then you'll end up with multiple entries in
>> the device list on the TV, and only one of them will work.
> 
> Adding a special mode in the CEC framework that disables parsing and
> processing seems like a good idea for me. This way libCEC could be
> completely
> in charge of how the communication is handled. 
> 
> I discussed this with Hans and he is for this solution. This way there would
> be two modes:
> - One with handling of CEC messages enabled in the kernel, in idea behind
>   this is to have processing adhere to the CEC spec as closely as possible.
>   It should work with equipment that also follows the spec and has little
>   vendor specific quirks.
> - Second, the passthrough mode, in which the handling of CEC messages would
>   be left to userspace application. Kernel would not be sending or
>   receiving messages, unless specifically told to do so. Below you mentioned
>   that allocating logical addresses and sending ACKs could be done in
> kernel.
> 
>   The way I see it is following: If allocation of a logical address is made
>   then ACKs will be handled by the framework. If no allocation is made then
>   the userspace can still send and receive messages. However no filtering is
>   done based on the logical address - all received messages are sent to the
>   userspace.
> 
>>
>> * CEC is *very* vendor specific. The main reason is, in my opinion, the
>> use of the word "should" instead of "shall" in the spec. It's addressed
>> in the new version, but it'll take years before all the non 2.x devices
>> are gone. What works for vendor A will simply not work for vendor B.
>> libCEC aims to address this, in a library that can be used on all major
>> platforms and by all major programming languages. You could duplicate
>> the work done there in the kernel to make make the implementation work
>> with all brands, but I think that this does simply not belong in the
>> kernel when it can be handled in userspace perfectly.
> 
> CEC being very vendor specific is a huge problem. I agree with you that
> there is no need to duplicate the effort to mitigate all the vendor quirks.
> Especially that a working implementation (libCEC) is already done.
> 
>> So I suggest that you limit the in-kernel implementation to handling
>> raw
>> traffic only, to have it do this (and nothing more):
>> * allocate one or more logical addresses, and ack CEC traffic sent to
>> those logical addresses
>> * receive CEC traffic and forward it to userspace (traffic sent to all
>> addresses is preferred, not just traffic sent to the logical address
>> used by the device running this code)
>> * transmit CEC traffic initiated by userspace
> 
> As mentioned above, I propose a "passthorugh" mode in which handling of
> CEC messages by the kernel CEC framework will be very limited. I think
> that the three functions listed above should be enough.
> 
> Any comments on this solution?
> 
>>
>> thanks,
>>
>> Lars Op den Kamp
> 
> Best wishes,
> 


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v4 06/10] cec: add HDMI CEC framework
  2015-04-23 13:03 ` [PATCH v4 06/10] cec: add HDMI CEC framework Kamil Debski
                     ` (2 preceding siblings ...)
  2015-04-27  7:40   ` [PATCH v4 06/10] cec: add HDMI CEC framework: y2038 question Hans Verkuil
@ 2015-04-27  9:13   ` Hans Verkuil
  2015-04-27  9:22   ` Hans Verkuil
                     ` (2 subsequent siblings)
  6 siblings, 0 replies; 27+ messages in thread
From: Hans Verkuil @ 2015-04-27  9:13 UTC (permalink / raw)
  To: Kamil Debski, dri-devel, linux-media
  Cc: m.szyprowski, mchehab, kyungmin.park, thomas, sean,
	dmitry.torokhov, linux-input, linux-samsung-soc, Hans Verkuil

Hi Kamil,

I've added some missing HDMI 2.0 commands:

On 04/23/2015 03:03 PM, Kamil Debski wrote:
> From: Hans Verkuil <hansverk@cisco.com>
> 
> The added HDMI CEC framework provides a generic kernel interface for
> HDMI CEC devices.
> 
> Signed-off-by: Hans Verkuil <hansverk@cisco.com>
> [k.debski@samsung.com: Merged CEC Updates commit by Hans Verkuil]
> [k.debski@samsung.com: Merged Update author commit by Hans Verkuil]
> [k.debski@samsung.com: change kthread handling when setting logical
> address]
> [k.debski@samsung.com: code cleanup and fixes]
> [k.debski@samsung.com: add missing CEC commands to match spec]
> [k.debski@samsung.com: add RC framework support]
> [k.debski@samsung.com: move and edit documentation]
> [k.debski@samsung.com: add vendor id reporting]
> [k.debski@samsung.com: add possibility to clear assigned logical
> addresses]
> [k.debski@samsung.com: documentation fixes, clenaup and expansion]
> [k.debski@samsung.com: reorder of API structs and add reserved fields]
> [k.debski@samsung.com: fix handling of events and fix 32/64bit timespec
> problem]
> [k.debski@samsung.com: add cec.h to include/uapi/linux/Kbuild]
> Signed-off-by: Kamil Debski <k.debski@samsung.com>
> ---
>  Documentation/cec.txt     |  396 ++++++++++++++++
>  drivers/media/Kconfig     |    6 +
>  drivers/media/Makefile    |    2 +
>  drivers/media/cec.c       | 1161 +++++++++++++++++++++++++++++++++++++++++++++
>  include/media/cec.h       |  140 ++++++
>  include/uapi/linux/Kbuild |    1 +
>  include/uapi/linux/cec.h  |  303 ++++++++++++
>  7 files changed, 2009 insertions(+)
>  create mode 100644 Documentation/cec.txt
>  create mode 100644 drivers/media/cec.c
>  create mode 100644 include/media/cec.h
>  create mode 100644 include/uapi/linux/cec.h
> 
> diff --git a/include/uapi/linux/cec.h b/include/uapi/linux/cec.h
> new file mode 100644
> index 0000000..bb6d66c
> --- /dev/null
> +++ b/include/uapi/linux/cec.h
> @@ -0,0 +1,303 @@
> +#ifndef _CEC_H
> +#define _CEC_H
> +
> +#include <linux/types.h>
> +
> +struct cec_time {
> +	__u64 sec;
> +	__u64 nsec;
> +};
> +
> +struct cec_msg {
> +	struct cec_time ts;
> +	__u32 len;
> +	__u32 status;
> +	__u32 timeout;
> +	/* timeout (in ms) is used to timeout CEC_RECEIVE.
> +	   Set to 0 if you want to wait forever. */
> +	__u8  msg[16];
> +	__u8  reply;
> +	/* If non-zero, then wait for a reply with this opcode.
> +	   If there was an error when sending the msg or FeatureAbort
> +	   was returned, then reply is set to 0.
> +	   If reply is non-zero upon return, then len/msg are set to
> +	   the received message.
> +	   If reply is zero upon return and status has the
> +	   CEC_TX_STATUS_FEATURE_ABORT bit set, then len/msg are set to the
> +	   received feature abort message.
> +	   If reply is zero upon return and status has the
> +	   CEC_TX_STATUS_REPLY_TIMEOUT
> +	   bit set, then no reply was seen at all.
> +	   This field is ignored with CEC_RECEIVE.
> +	   If reply is non-zero for CEC_TRANSMIT and the message is a broadcast,
> +	   then -EINVAL is returned.
> +	   if reply is non-zero, then timeout is set to 1000 (the required
> +	   maximum response time).
> +	 */
> +	__u8 reserved[31];
> +};
> +
> +static inline __u8 cec_msg_initiator(const struct cec_msg *msg)
> +{
> +	return msg->msg[0] >> 4;
> +}
> +
> +static inline __u8 cec_msg_destination(const struct cec_msg *msg)
> +{
> +	return msg->msg[0] & 0xf;
> +}
> +
> +static inline bool cec_msg_is_broadcast(const struct cec_msg *msg)
> +{
> +	return (msg->msg[0] & 0xf) == 0xf;
> +}
> +
> +/* cec status field */
> +#define CEC_TX_STATUS_OK            (0)
> +#define CEC_TX_STATUS_ARB_LOST      (1 << 0)
> +#define CEC_TX_STATUS_RETRY_TIMEOUT (1 << 1)
> +#define CEC_TX_STATUS_FEATURE_ABORT (1 << 2)
> +#define CEC_TX_STATUS_REPLY_TIMEOUT (1 << 3)
> +#define CEC_RX_STATUS_READY         (0)
> +
> +#define CEC_LOG_ADDR_INVALID 0xff
> +
> +/* The maximum number of logical addresses one device can be assigned to.
> + * The CEC 2.0 spec allows for only 2 logical addresses at the moment. The
> + * Analog Devices CEC hardware supports 3. So let's go wild and go for 4. */
> +#define CEC_MAX_LOG_ADDRS 4
> +
> +/* The "Primary Device Type" */
> +#define CEC_PRIM_DEVTYPE_TV		0
> +#define CEC_PRIM_DEVTYPE_RECORD		1
> +#define CEC_PRIM_DEVTYPE_TUNER		3
> +#define CEC_PRIM_DEVTYPE_PLAYBACK	4
> +#define CEC_PRIM_DEVTYPE_AUDIOSYSTEM	5
> +#define CEC_PRIM_DEVTYPE_SWITCH		6
> +#define CEC_PRIM_DEVTYPE_VIDEOPROC	7
> +
> +/* The "All Device Types" flags (CEC 2.0) */
> +#define CEC_FL_ALL_DEVTYPE_TV		(1 << 7)
> +#define CEC_FL_ALL_DEVTYPE_RECORD	(1 << 6)
> +#define CEC_FL_ALL_DEVTYPE_TUNER	(1 << 5)
> +#define CEC_FL_ALL_DEVTYPE_PLAYBACK	(1 << 4)
> +#define CEC_FL_ALL_DEVTYPE_AUDIOSYSTEM	(1 << 3)
> +#define CEC_FL_ALL_DEVTYPE_SWITCH	(1 << 2)
> +/* And if you wondering what happened to VIDEOPROC devices: those should
> + * be mapped to a SWITCH. */
> +
> +/* The logical address types that the CEC device wants to claim */
> +#define CEC_LOG_ADDR_TYPE_TV		0
> +#define CEC_LOG_ADDR_TYPE_RECORD	1
> +#define CEC_LOG_ADDR_TYPE_TUNER		2
> +#define CEC_LOG_ADDR_TYPE_PLAYBACK	3
> +#define CEC_LOG_ADDR_TYPE_AUDIOSYSTEM	4
> +#define CEC_LOG_ADDR_TYPE_SPECIFIC	5
> +#define CEC_LOG_ADDR_TYPE_UNREGISTERED	6
> +/* Switches should use UNREGISTERED.
> + * Video processors should use SPECIFIC. */
> +
> +/* The CEC version */
> +#define CEC_VERSION_1_4B		5
> +#define CEC_VERSION_2_0			6
> +
> +struct cec_event {
> +	struct cec_time ts;
> +	__u32 event;
> +	__u8 reserved[4];
> +};
> +
> +/* The CEC state */
> +#define CEC_STATE_DISABLED		0
> +#define CEC_STATE_ENABLED		1
> +
> +/* Userspace has to configure the adapter state (enable/disable) */
> +#define CEC_CAP_STATE		(1 << 0)
> +/* Userspace has to configure the physical address */
> +#define CEC_CAP_PHYS_ADDR	(1 << 1)
> +/* Userspace has to configure the logical addresses */
> +#define CEC_CAP_LOG_ADDRS	(1 << 2)
> +/* Userspace can transmit messages */
> +#define CEC_CAP_TRANSMIT	(1 << 3)
> +/* Userspace can receive messages */
> +#define CEC_CAP_RECEIVE		(1 << 4)
> +/* Userspace has to configure the vendor id */
> +#define CEC_CAP_VENDOR_ID	(1 << 5)
> +/* The hardware has the possibility to work in the promiscuous mode */
> +#define CEC_CAP_PROMISCUOUS	(1 << 6)
> +
> +struct cec_caps {
> +	__u32 available_log_addrs;
> +	__u32 capabilities;
> +	__u32 vendor_id;
> +	__u8  version;
> +	__u8  reserved[11];
> +};
> +
> +struct cec_log_addrs {
> +	__u8 cec_version;
> +	__u8 num_log_addrs;
> +	__u8 primary_device_type[CEC_MAX_LOG_ADDRS];
> +	__u8 log_addr_type[CEC_MAX_LOG_ADDRS];
> +	__u8 log_addr[CEC_MAX_LOG_ADDRS];
> +
> +	/* CEC 2.0 */
> +	__u8 all_device_types;
> +	__u8 features[CEC_MAX_LOG_ADDRS][12];
> +
> +	__u8 reserved[9];
> +};
> +
> +/* Commands */
> +
> +/* One Touch Play Feature */
> +#define CEC_OP_ACTIVE_SOURCE			0x82
> +#define CEC_OP_IMAGE_VIEW_ON			0x04
> +#define CEC_OP_TEXT_VIEW_ON			0x0d
> +
> +/* Routing Control Feature */
> +#define CEC_OP_ACTIVE_SOURCE			0x82
> +#define CEC_OP_INACTIVE_SOURCE			0x9d
> +#define CEC_OP_REQUEST_ACTIVE_SOURCE		0x85
> +#define CEC_OP_ROUTING_CHANGE			0x80
> +#define CEC_OP_ROUTING_INFORMATION		0x81
> +#define CEC_OP_SET_STREAM_PATH			0x86
> +
> +/* Standby Feature */
> +#define CEC_OP_STANDBY				0x36
> +
> +/* One Touch Record Feature */
> +#define CEC_OP_RECORD_OFF			0x0b
> +#define CEC_OP_RECORD_ON			0x09
> +#define CEC_OP_RECORD_STATUS			0x0a
> +#define CEC_OP_RECORD_TV_SCREEN			0x0f
> +
> +/* Timer Programming Feature */
> +#define CEC_OP_CLEAR_ANALOGUE_TIMER		0x33
> +#define CEC_OP_CLEAR_DIGITAL_TIMER		0x99
> +#define CEC_OP_CLEAR_EXT_TIMER			0xa1
> +#define CEC_OP_SET_ANALOGUE_TIMER		0x34
> +#define CEC_OP_SET_DIGITAL_TIMER		0x97
> +#define CEC_OP_SET_EXT_TIMER			0xa2
> +#define CEC_OP_SET_EXT_PROGRAM_TIMER		0x67
> +#define CEC_OP_TIMER_CLEARED_STATUS		0x43
> +#define CEC_OP_TIMER_STATUS			0x35
> +
> +/* System Information Feature */
> +#define CEC_OP_CEC_VERSION			0x9e
> +#define CEC_OP_GET_CEC_VERSION			0x9f
> +#define CEC_OP_GIVE_PHYSICAL_ADDR		0x83
> +#define CEC_OP_GET_MENU_LANGUAGE		0x91
> +#define CEC_OP_REPORT_PHYSICAL_ADDR		0x84
> +#define CEC_OP_SET_MENU_LANGUAGE		0x32

#define CEC_OP_REPORT_FEATURES			0xa6	/* HDMI 2.0 */
#define CEC_OP_GIVE_FEATURES			0xa5	/* HDMI 2.0 */

> +
> +/* Deck Control Feature */
> +#define CEC_OP_DECK_CONTROL			0x42
> +#define CEC_OP_DECK_STATUS			0x1b
> +#define CEC_OP_GIVE_DECK_STATUS			0x1a
> +#define CEC_OP_PLAY				0x41
> +
> +/* Tuner Control Feature */
> +#define CEC_OP_GIVE_TUNER_DEVICE_STATUS		0x08
> +#define CEC_OP_SELECT_ANALOGUE_SERVICE		0x92
> +#define CEC_OP_SELECT_DIGITAL_SERVICE		0x93
> +#define CEC_OP_TUNER_DEVICE_STATUS		0x07
> +#define CEC_OP_TUNER_STEP_DECREMENT		0x06
> +#define CEC_OP_TUNER_STEP_INCREMENT		0x05
> +
> +/* Vendor Specific Commands Feature */
> +#define CEC_OP_CEC_VERSION			0x9e
> +#define CEC_OP_DEVICE_VENDOR_ID			0x87
> +#define CEC_OP_GET_CEC_VERSION			0x9f
> +#define CEC_OP_GIVE_DEVICE_VENDOR_ID		0x8c
> +#define CEC_OP_VENDOR_COMMAND			0x89
> +#define CEC_OP_VENDOR_COMMAND_WITH_ID		0xa0
> +#define CEC_OP_VENDOR_REMOTE_BUTTON_DOWN	0x8a
> +#define CEC_OP_VENDOR_REMOTE_BUTTON_UP		0x8b
> +
> +/* OSD Display Feature */
> +#define CEC_OP_SET_OSD_STRING			0x64
> +
> +/* Device OSD Transfer Feature */
> +#define CEC_OP_GIVE_OSD_NAME			0x46
> +#define CEC_OP_SET_OSD_NAME			0x47
> +
> +/* Device Menu Control Feature */
> +#define CEC_OP_MENU_REQUEST			0x8d
> +#define CEC_OP_MENU_STATUS			0x8e
> +#define CEC_OP_USER_CONTROL_PRESSED		0x44
> +#define CEC_OP_USER_CONTROL_RELEASED		0x45
> +
> +/* Power Status Feature */
> +#define CEC_OP_GIVE_DEVICE_POWER_STATUS		0x8f
> +#define CEC_OP_REPORT_POWER_STATUS		0x90
> +#define CEC_OP_FEATURE_ABORT			0x00
> +#define CEC_OP_ABORT				0xff
> +
> +/* System Audio Control Feature */
> +#define CEC_OP_GIVE_AUDIO_STATUS		0x71
> +#define CEC_OP_GIVE_SYSTEM_AUDIO_MODE_STATUS	0x7d
> +#define CEC_OP_REPORT_AUDIO_STATUS		0x7a
> +#define CEC_OP_SET_SYSTEM_AUDIO_MODE		0x72
> +#define CEC_OP_SYSTEM_AUDIO_MODE_REQUEST	0x70
> +#define CEC_OP_SYSTEM_AUDIO_MODE_STATUS		0x7e
> +
> +/* Audio Rate Control Feature */
> +#define CEC_OP_SET_AUDIO_RATE			0x9a
> +

/* Audio Return Channel Control Feature */

#define CEC_OP_INITIATE_ARC			0xc0
#define CEC_OP_REPORT_ARC_INITIATED		0xc1
#define CEC_OP_REPORT_ARC_TERMINATED		0xc2
#define CEC_OP_REQUEST_ARC_INITIATION		0xc3
#define CEC_OP_REQUEST_ARC_TERMINATION		0xc4
#define CEC_OP_TERMINATE_ARC			0xc5

/* Dynamic Audio Lipsync Feature, HDMI 2.0 */

#define CEC_OP_REQUEST_CURRENT_LATENCY		0xa7
#define CEC_OP_REPORT_CURRENT_LATENCY		0xa8

/* Capability Discovery and Control Feature */

#define CEC_OP_CDC_MESSAGE			0xf8

Regards,

	Hans

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v4 06/10] cec: add HDMI CEC framework
  2015-04-23 13:03 ` [PATCH v4 06/10] cec: add HDMI CEC framework Kamil Debski
                     ` (3 preceding siblings ...)
  2015-04-27  9:13   ` [PATCH v4 06/10] cec: add HDMI CEC framework Hans Verkuil
@ 2015-04-27  9:22   ` Hans Verkuil
  2015-04-27  9:30     ` Hans Verkuil
  2015-04-27 11:45     ` Hans Verkuil
  2015-04-27 10:04   ` Hans Verkuil
  2015-04-27 10:25   ` Hans Verkuil
  6 siblings, 2 replies; 27+ messages in thread
From: Hans Verkuil @ 2015-04-27  9:22 UTC (permalink / raw)
  To: Kamil Debski, dri-devel, linux-media
  Cc: m.szyprowski, mchehab, kyungmin.park, thomas, sean,
	dmitry.torokhov, linux-input, linux-samsung-soc, Hans Verkuil

Hi Kamil,

Sorry for all the replies, but I'm writing the DocBook documentation, so
whenever I find something missing I'll just reply to this patch.
> +/* The CEC version */

Add support for version 1.3a here:

#define CEC_VERSION_1_3A		4

> +#define CEC_VERSION_1_4B		5
> +#define CEC_VERSION_2_0			6

Regards,

	Hans

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v4 06/10] cec: add HDMI CEC framework
  2015-04-27  9:22   ` Hans Verkuil
@ 2015-04-27  9:30     ` Hans Verkuil
  2015-04-27 11:45     ` Hans Verkuil
  1 sibling, 0 replies; 27+ messages in thread
From: Hans Verkuil @ 2015-04-27  9:30 UTC (permalink / raw)
  To: Kamil Debski, dri-devel, linux-media
  Cc: m.szyprowski, mchehab, kyungmin.park, thomas, sean,
	dmitry.torokhov, linux-input, linux-samsung-soc, Hans Verkuil

On 04/27/2015 11:22 AM, Hans Verkuil wrote:
> Hi Kamil,
> 
> Sorry for all the replies, but I'm writing the DocBook documentation, so
> whenever I find something missing I'll just reply to this patch.
>> +/* The CEC version */
> 
> Add support for version 1.3a here:
> 
> #define CEC_VERSION_1_3A		4

To clarify: the core CEC framework will not support 1.3A, but of course
other devices can report it, so we do need the define.

Regards,

	Hans

> 
>> +#define CEC_VERSION_1_4B		5
>> +#define CEC_VERSION_2_0			6
> 
> Regards,
> 
> 	Hans
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v4 06/10] cec: add HDMI CEC framework
  2015-04-23 13:03 ` [PATCH v4 06/10] cec: add HDMI CEC framework Kamil Debski
                     ` (4 preceding siblings ...)
  2015-04-27  9:22   ` Hans Verkuil
@ 2015-04-27 10:04   ` Hans Verkuil
  2015-04-27 10:25   ` Hans Verkuil
  6 siblings, 0 replies; 27+ messages in thread
From: Hans Verkuil @ 2015-04-27 10:04 UTC (permalink / raw)
  To: Kamil Debski, dri-devel, linux-media
  Cc: m.szyprowski, mchehab, kyungmin.park, thomas, sean,
	dmitry.torokhov, linux-input, linux-samsung-soc, Hans Verkuil

On 04/23/2015 03:03 PM, Kamil Debski wrote:
> From: Hans Verkuil <hansverk@cisco.com>
> 
> The added HDMI CEC framework provides a generic kernel interface for
> HDMI CEC devices.
> 
> Signed-off-by: Hans Verkuil <hansverk@cisco.com>
> [k.debski@samsung.com: Merged CEC Updates commit by Hans Verkuil]
> [k.debski@samsung.com: Merged Update author commit by Hans Verkuil]
> [k.debski@samsung.com: change kthread handling when setting logical
> address]
> [k.debski@samsung.com: code cleanup and fixes]
> [k.debski@samsung.com: add missing CEC commands to match spec]
> [k.debski@samsung.com: add RC framework support]
> [k.debski@samsung.com: move and edit documentation]
> [k.debski@samsung.com: add vendor id reporting]
> [k.debski@samsung.com: add possibility to clear assigned logical
> addresses]
> [k.debski@samsung.com: documentation fixes, clenaup and expansion]
> [k.debski@samsung.com: reorder of API structs and add reserved fields]
> [k.debski@samsung.com: fix handling of events and fix 32/64bit timespec
> problem]
> [k.debski@samsung.com: add cec.h to include/uapi/linux/Kbuild]
> Signed-off-by: Kamil Debski <k.debski@samsung.com>
> ---
>  Documentation/cec.txt     |  396 ++++++++++++++++
>  drivers/media/Kconfig     |    6 +
>  drivers/media/Makefile    |    2 +
>  drivers/media/cec.c       | 1161 +++++++++++++++++++++++++++++++++++++++++++++
>  include/media/cec.h       |  140 ++++++
>  include/uapi/linux/Kbuild |    1 +
>  include/uapi/linux/cec.h  |  303 ++++++++++++
>  7 files changed, 2009 insertions(+)
>  create mode 100644 Documentation/cec.txt
>  create mode 100644 drivers/media/cec.c
>  create mode 100644 include/media/cec.h
>  create mode 100644 include/uapi/linux/cec.h
> 
> diff --git a/Documentation/cec.txt b/Documentation/cec.txt
> new file mode 100644
> index 0000000..2b6c08a
> --- /dev/null
> +++ b/Documentation/cec.txt
> @@ -0,0 +1,396 @@
> +- CEC_G_ADAP_LOG_ADDRS and CEC_S_ADAP_LOG_ADDRS
> +
> +These ioctl are used to configure the logical addresses of the CEC adapter.
> +
> +#define CEC_G_ADAP_LOG_ADDRS	_IOR('a', 3, struct cec_log_addrs)
> +#define CEC_S_ADAP_LOG_ADDRS	_IOWR('a', 4, struct cec_log_addrs)
> +
> +The struct cec_log_addrs is following:
> +
> +struct cec_log_addrs {
> +	__u8 cec_version;
> +	__u8 num_log_addrs;
> +	__u8 primary_device_type[CEC_MAX_LOG_ADDRS];
> +	__u8 log_addr_type[CEC_MAX_LOG_ADDRS];
> +	__u8 log_addr[CEC_MAX_LOG_ADDRS];
> +
> +	/* CEC 2.0 */
> +	__u8 all_device_types;
> +	__u8 features[CEC_MAX_LOG_ADDRS][12];
> +
> +	__u8 reserved[9];
> +};
> +
> +The cec_version determines which CEC version should be used.
> +
> +/* The CEC version */
> +#define CEC_VERSION_1_4B		5
> +#define CEC_VERSION_2_0			6
> +
> +It will try to claim num_log_addrs devices. The log_addr_type array has
> +the logical address type that needs to be claimed for that device, and
> +the log_addr array will receive the actual logical address that was
> +claimed for that device or 0xff if no address could be claimed.
> +
> +The primary_device_type contains the primary device for each logical
> +address.
> +
> +For CEC 2.0 devices fill in the all_device_types parameter to use with the
> +Report Features command, and fill in the 'features' which contains the
> +remaining parameters (RC Profile and Device Features) to use in Report
> +Features.
> +
> +An error is returned if the adapter is disabled or if there
> +is no physical address assigned or if the cec_version is unknown.
> +
> +If no logical address of one or more of the given types could be claimed,
> +then log_addr will be set to CEC_LOG_ADDR_INVALID.

This does not appear to be the case looking at the cec_config_log_addrs function.
I don't see it being set to INVALID if it couldn't be claimed. I think that is
missing in the cec_config_log_addrs function.

Regards,

	Hans


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v4 06/10] cec: add HDMI CEC framework
  2015-04-23 13:03 ` [PATCH v4 06/10] cec: add HDMI CEC framework Kamil Debski
                     ` (5 preceding siblings ...)
  2015-04-27 10:04   ` Hans Verkuil
@ 2015-04-27 10:25   ` Hans Verkuil
  2015-04-27 11:27     ` Hans Verkuil
  6 siblings, 1 reply; 27+ messages in thread
From: Hans Verkuil @ 2015-04-27 10:25 UTC (permalink / raw)
  To: Kamil Debski, dri-devel, linux-media
  Cc: m.szyprowski, mchehab, kyungmin.park, thomas, sean,
	dmitry.torokhov, linux-input, linux-samsung-soc, Hans Verkuil

On 04/23/2015 03:03 PM, Kamil Debski wrote:
> From: Hans Verkuil <hansverk@cisco.com>
> 
> The added HDMI CEC framework provides a generic kernel interface for
> HDMI CEC devices.
> 
> Signed-off-by: Hans Verkuil <hansverk@cisco.com>
> [k.debski@samsung.com: Merged CEC Updates commit by Hans Verkuil]
> [k.debski@samsung.com: Merged Update author commit by Hans Verkuil]
> [k.debski@samsung.com: change kthread handling when setting logical
> address]
> [k.debski@samsung.com: code cleanup and fixes]
> [k.debski@samsung.com: add missing CEC commands to match spec]
> [k.debski@samsung.com: add RC framework support]
> [k.debski@samsung.com: move and edit documentation]
> [k.debski@samsung.com: add vendor id reporting]
> [k.debski@samsung.com: add possibility to clear assigned logical
> addresses]
> [k.debski@samsung.com: documentation fixes, clenaup and expansion]
> [k.debski@samsung.com: reorder of API structs and add reserved fields]
> [k.debski@samsung.com: fix handling of events and fix 32/64bit timespec
> problem]
> [k.debski@samsung.com: add cec.h to include/uapi/linux/Kbuild]
> Signed-off-by: Kamil Debski <k.debski@samsung.com>
> ---
>  Documentation/cec.txt     |  396 ++++++++++++++++
>  drivers/media/Kconfig     |    6 +
>  drivers/media/Makefile    |    2 +
>  drivers/media/cec.c       | 1161 +++++++++++++++++++++++++++++++++++++++++++++
>  include/media/cec.h       |  140 ++++++
>  include/uapi/linux/Kbuild |    1 +
>  include/uapi/linux/cec.h  |  303 ++++++++++++
>  7 files changed, 2009 insertions(+)
>  create mode 100644 Documentation/cec.txt
>  create mode 100644 drivers/media/cec.c
>  create mode 100644 include/media/cec.h
>  create mode 100644 include/uapi/linux/cec.h
> 

> +	case CEC_S_ADAP_LOG_ADDRS: {
> +		struct cec_log_addrs log_addrs;
> +
> +		if (!(adap->capabilities & CEC_CAP_LOG_ADDRS))
> +			return -ENOTTY;
> +		if (copy_from_user(&log_addrs, parg, sizeof(log_addrs)))
> +			return -EFAULT;
> +		err = cec_claim_log_addrs(adap, &log_addrs, true);

Currently CEC_S_ADAP_LOG_ADDRS is always blocking, but since we have CEC_EVENT_READY
I think it makes sense to just return in non-blocking mode and have cec_claim_log_addrs
generate CEC_EVENT_READY when done. Userspace can then call G_ADAP_LOG_ADDRS to discover
the result.

What do you think?

Regards,

	Hans

> +		if (err)
> +			return err;
> +
> +		if (copy_to_user(parg, &log_addrs, sizeof(log_addrs)))
> +			return -EFAULT;
> +		break;
> +	}


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v4 06/10] cec: add HDMI CEC framework
  2015-04-27 10:25   ` Hans Verkuil
@ 2015-04-27 11:27     ` Hans Verkuil
  2015-04-27 12:18       ` Kamil Debski
  0 siblings, 1 reply; 27+ messages in thread
From: Hans Verkuil @ 2015-04-27 11:27 UTC (permalink / raw)
  To: Kamil Debski, dri-devel, linux-media
  Cc: m.szyprowski, mchehab, kyungmin.park, thomas, sean,
	dmitry.torokhov, linux-input, linux-samsung-soc, Hans Verkuil

On 04/27/2015 12:25 PM, Hans Verkuil wrote:
> On 04/23/2015 03:03 PM, Kamil Debski wrote:
>> From: Hans Verkuil <hansverk@cisco.com>
>>
>> The added HDMI CEC framework provides a generic kernel interface for
>> HDMI CEC devices.
>>
>> Signed-off-by: Hans Verkuil <hansverk@cisco.com>
>> [k.debski@samsung.com: Merged CEC Updates commit by Hans Verkuil]
>> [k.debski@samsung.com: Merged Update author commit by Hans Verkuil]
>> [k.debski@samsung.com: change kthread handling when setting logical
>> address]
>> [k.debski@samsung.com: code cleanup and fixes]
>> [k.debski@samsung.com: add missing CEC commands to match spec]
>> [k.debski@samsung.com: add RC framework support]
>> [k.debski@samsung.com: move and edit documentation]
>> [k.debski@samsung.com: add vendor id reporting]
>> [k.debski@samsung.com: add possibility to clear assigned logical
>> addresses]
>> [k.debski@samsung.com: documentation fixes, clenaup and expansion]
>> [k.debski@samsung.com: reorder of API structs and add reserved fields]
>> [k.debski@samsung.com: fix handling of events and fix 32/64bit timespec
>> problem]
>> [k.debski@samsung.com: add cec.h to include/uapi/linux/Kbuild]
>> Signed-off-by: Kamil Debski <k.debski@samsung.com>
>> ---
>>  Documentation/cec.txt     |  396 ++++++++++++++++
>>  drivers/media/Kconfig     |    6 +
>>  drivers/media/Makefile    |    2 +
>>  drivers/media/cec.c       | 1161 +++++++++++++++++++++++++++++++++++++++++++++
>>  include/media/cec.h       |  140 ++++++
>>  include/uapi/linux/Kbuild |    1 +
>>  include/uapi/linux/cec.h  |  303 ++++++++++++
>>  7 files changed, 2009 insertions(+)
>>  create mode 100644 Documentation/cec.txt
>>  create mode 100644 drivers/media/cec.c
>>  create mode 100644 include/media/cec.h
>>  create mode 100644 include/uapi/linux/cec.h
>>
> 
>> +	case CEC_S_ADAP_LOG_ADDRS: {
>> +		struct cec_log_addrs log_addrs;
>> +
>> +		if (!(adap->capabilities & CEC_CAP_LOG_ADDRS))
>> +			return -ENOTTY;
>> +		if (copy_from_user(&log_addrs, parg, sizeof(log_addrs)))
>> +			return -EFAULT;
>> +		err = cec_claim_log_addrs(adap, &log_addrs, true);
> 
> Currently CEC_S_ADAP_LOG_ADDRS is always blocking, but since we have CEC_EVENT_READY
> I think it makes sense to just return in non-blocking mode and have cec_claim_log_addrs
> generate CEC_EVENT_READY when done. Userspace can then call G_ADAP_LOG_ADDRS to discover
> the result.
> 
> What do you think?
> 

On a related topic: non-blocking behavior for CEC_RECEIVE is well defined, but for
CEC_TRANSMIT it isn't. If reply == 0, then we need a way to inform userspace that
the transmit finished (with a possible non-zero status code). An event would be
suitable for that, but we would need a way to associate a transmit message with
the event.

One possibility might be to have the CEC framework assign a sequence number to
a transmit message which is returned by CEC_TRANSMIT and used in the event.

If reply != 0, then I think the received message should be queued up in the
receive queue, but with a non-zero reply field and with the sequence number of the
transmit message it is a reply of.

Regards,

	Hans

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v4 06/10] cec: add HDMI CEC framework
  2015-04-27  9:22   ` Hans Verkuil
  2015-04-27  9:30     ` Hans Verkuil
@ 2015-04-27 11:45     ` Hans Verkuil
  1 sibling, 0 replies; 27+ messages in thread
From: Hans Verkuil @ 2015-04-27 11:45 UTC (permalink / raw)
  To: Kamil Debski, dri-devel, linux-media
  Cc: m.szyprowski, mchehab, kyungmin.park, thomas, sean,
	dmitry.torokhov, linux-input, linux-samsung-soc, Hans Verkuil

On 04/27/2015 11:22 AM, Hans Verkuil wrote:
> Hi Kamil,
> 
> Sorry for all the replies, but I'm writing the DocBook documentation, so
> whenever I find something missing I'll just reply to this patch.
>> +/* The CEC version */
> 
> Add support for version 1.3a here:
> 
> #define CEC_VERSION_1_3A		4
> 
>> +#define CEC_VERSION_1_4B		5

Hmm, reading up on this I see that '5' is used for HDMI 1.4, 1.4a or 1.4b.

I think it should be renamed to VERSION_1_4 (i.e. drop the 'B').

Regards,

	Hans

>> +#define CEC_VERSION_2_0			6
> 
> Regards,
> 
> 	Hans
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 


^ permalink raw reply	[flat|nested] 27+ messages in thread

* RE: [PATCH v4 06/10] cec: add HDMI CEC framework
  2015-04-27 11:27     ` Hans Verkuil
@ 2015-04-27 12:18       ` Kamil Debski
  0 siblings, 0 replies; 27+ messages in thread
From: Kamil Debski @ 2015-04-27 12:18 UTC (permalink / raw)
  To: 'Hans Verkuil', dri-devel, linux-media
  Cc: m.szyprowski, mchehab, kyungmin.park, thomas, sean,
	dmitry.torokhov, linux-input, linux-samsung-soc,
	'Hans Verkuil'

Hi Hans,

Thank you so much for all today's comments. I will consider them when
preparing the next version.

From: linux-media-owner@vger.kernel.org [mailto:linux-media-
owner@vger.kernel.org] On Behalf Of Hans Verkuil
Sent: Monday, April 27, 2015 1:27 PM
> 
> On 04/27/2015 12:25 PM, Hans Verkuil wrote:
> > On 04/23/2015 03:03 PM, Kamil Debski wrote:
> >> From: Hans Verkuil <hansverk@cisco.com>
> >>
> >> The added HDMI CEC framework provides a generic kernel interface for
> >> HDMI CEC devices.
> >>
> >> Signed-off-by: Hans Verkuil <hansverk@cisco.com>
> >> [k.debski@samsung.com: Merged CEC Updates commit by Hans Verkuil]
> >> [k.debski@samsung.com: Merged Update author commit by Hans Verkuil]
> >> [k.debski@samsung.com: change kthread handling when setting logical
> >> address]
> >> [k.debski@samsung.com: code cleanup and fixes]
> >> [k.debski@samsung.com: add missing CEC commands to match spec]
> >> [k.debski@samsung.com: add RC framework support]
> >> [k.debski@samsung.com: move and edit documentation]
> >> [k.debski@samsung.com: add vendor id reporting]
> >> [k.debski@samsung.com: add possibility to clear assigned logical
> >> addresses]
> >> [k.debski@samsung.com: documentation fixes, clenaup and expansion]
> >> [k.debski@samsung.com: reorder of API structs and add reserved
> >> fields]
> >> [k.debski@samsung.com: fix handling of events and fix 32/64bit
> >> timespec problem]
> >> [k.debski@samsung.com: add cec.h to include/uapi/linux/Kbuild]
> >> Signed-off-by: Kamil Debski <k.debski@samsung.com>
> >> ---
> >>  Documentation/cec.txt     |  396 ++++++++++++++++
> >>  drivers/media/Kconfig     |    6 +
> >>  drivers/media/Makefile    |    2 +
> >>  drivers/media/cec.c       | 1161
> +++++++++++++++++++++++++++++++++++++++++++++
> >>  include/media/cec.h       |  140 ++++++
> >>  include/uapi/linux/Kbuild |    1 +
> >>  include/uapi/linux/cec.h  |  303 ++++++++++++
> >>  7 files changed, 2009 insertions(+)
> >>  create mode 100644 Documentation/cec.txt  create mode 100644
> >> drivers/media/cec.c  create mode 100644 include/media/cec.h  create
> >> mode 100644 include/uapi/linux/cec.h
> >>
> >
> >> +	case CEC_S_ADAP_LOG_ADDRS: {
> >> +		struct cec_log_addrs log_addrs;
> >> +
> >> +		if (!(adap->capabilities & CEC_CAP_LOG_ADDRS))
> >> +			return -ENOTTY;
> >> +		if (copy_from_user(&log_addrs, parg, sizeof(log_addrs)))
> >> +			return -EFAULT;
> >> +		err = cec_claim_log_addrs(adap, &log_addrs, true);
> >
> > Currently CEC_S_ADAP_LOG_ADDRS is always blocking, but since we have
> > CEC_EVENT_READY I think it makes sense to just return in non-blocking
> > mode and have cec_claim_log_addrs generate CEC_EVENT_READY when done.
> > Userspace can then call G_ADAP_LOG_ADDRS to discover the result.
> >
> > What do you think?
> >

I am looking into this now. If I see this correctly this involves:
- adding cec_post_event(cla_int->adap, CEC_EVENT_READY); to
cec_config_thread_func
- adding O_NONBLOCK check in CEC_S_ADAP_LOG_ADDRS 
Right?
 
> On a related topic: non-blocking behavior for CEC_RECEIVE is well
> defined, but for CEC_TRA NSMIT it isn't. If reply == 0, then we need a
> way to inform userspace that the transmit finished (with a possible
> non-zero status code). An event would be suitable for that, but we
> would need a way to associate a transmit message with the event.
> 
> One possibility might be to have the CEC framework assign a sequence
> number to a transmit message which is returned by CEC_TRANSMIT and used
> in the event.
> 
> If reply != 0, then I think the received message should be queued up in
> the receive queue, but with a non-zero reply field and with the
> sequence number of the transmit message it is a reply of.

A sequence number is a good solution, I believe. To recap:
- a sequence number should be set by the framework and returned in the
CEC_TRANSMIT ioctl
- a new event should be added CEC_EVENT_TX_DONE and it should be posted on
each transmission
  finish 
- event struct has to include a sequence field as well
Is this ok?

> 
> Regards,
> 
> 	Hans

Best wishes,
-- 
Kamil Debski
Samsung R&D Institute Poland


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [Y2038] [PATCH v4 06/10] cec: add HDMI CEC framework: y2038 question
  2015-04-27  7:40   ` [PATCH v4 06/10] cec: add HDMI CEC framework: y2038 question Hans Verkuil
@ 2015-05-04  7:42     ` Hans Verkuil
  2015-05-04 10:14       ` Arnd Bergmann
  0 siblings, 1 reply; 27+ messages in thread
From: Hans Verkuil @ 2015-05-04  7:42 UTC (permalink / raw)
  To: y2038
  Cc: Kamil Debski, linux-samsung-soc, Arnd Bergmann, mchehab,
	dmitry.torokhov, dri-devel, kyungmin.park, thomas, linux-input,
	linux-media, m.szyprowski

Ping! (Added Arnd to the CC list)

On 04/27/2015 09:40 AM, Hans Verkuil wrote:
> Added the y2038 mailinglist since I would like to get their input for
> this API.
> 
> Y2038 experts, can you take a look at my comment in the code below?
> 
> Thanks!

Arnd, I just saw your patch series adding struct __kernel_timespec to
uapi/linux/time.h. I get the feeling that it might take a few kernel
cycles before we have a timespec64 available in userspace. Based on that
I think this CEC API should drop the timestamps for now and wait until
timespec64 becomes available before adding it.

The timestamps are a nice-to-have, but not critical. So adding it later
shouldn't be a problem. What is your opinion?

	Hans

> 
> On 04/23/2015 03:03 PM, Kamil Debski wrote:
>> From: Hans Verkuil <hansverk@cisco.com>
>>
>> The added HDMI CEC framework provides a generic kernel interface for
>> HDMI CEC devices.
>>
>> Signed-off-by: Hans Verkuil <hansverk@cisco.com>
>> [k.debski@samsung.com: Merged CEC Updates commit by Hans Verkuil]
>> [k.debski@samsung.com: Merged Update author commit by Hans Verkuil]
>> [k.debski@samsung.com: change kthread handling when setting logical
>> address]
>> [k.debski@samsung.com: code cleanup and fixes]
>> [k.debski@samsung.com: add missing CEC commands to match spec]
>> [k.debski@samsung.com: add RC framework support]
>> [k.debski@samsung.com: move and edit documentation]
>> [k.debski@samsung.com: add vendor id reporting]
>> [k.debski@samsung.com: add possibility to clear assigned logical
>> addresses]
>> [k.debski@samsung.com: documentation fixes, clenaup and expansion]
>> [k.debski@samsung.com: reorder of API structs and add reserved fields]
>> [k.debski@samsung.com: fix handling of events and fix 32/64bit timespec
>> problem]
>> [k.debski@samsung.com: add cec.h to include/uapi/linux/Kbuild]
>> Signed-off-by: Kamil Debski <k.debski@samsung.com>
>> ---
>>  Documentation/cec.txt     |  396 ++++++++++++++++
>>  drivers/media/Kconfig     |    6 +
>>  drivers/media/Makefile    |    2 +
>>  drivers/media/cec.c       | 1161 +++++++++++++++++++++++++++++++++++++++++++++
>>  include/media/cec.h       |  140 ++++++
>>  include/uapi/linux/Kbuild |    1 +
>>  include/uapi/linux/cec.h  |  303 ++++++++++++
>>  7 files changed, 2009 insertions(+)
>>  create mode 100644 Documentation/cec.txt
>>  create mode 100644 drivers/media/cec.c
>>  create mode 100644 include/media/cec.h
>>  create mode 100644 include/uapi/linux/cec.h
>>
> 
> <snip>
> 
>> diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
>> index 4842a98..5854cfd 100644
>> --- a/include/uapi/linux/Kbuild
>> +++ b/include/uapi/linux/Kbuild
>> @@ -81,6 +81,7 @@ header-y += capi.h
>>  header-y += cciss_defs.h
>>  header-y += cciss_ioctl.h
>>  header-y += cdrom.h
>> +header-y += cec.h
>>  header-y += cgroupstats.h
>>  header-y += chio.h
>>  header-y += cm4000_cs.h
>> diff --git a/include/uapi/linux/cec.h b/include/uapi/linux/cec.h
>> new file mode 100644
>> index 0000000..bb6d66c
>> --- /dev/null
>> +++ b/include/uapi/linux/cec.h
>> @@ -0,0 +1,303 @@
>> +#ifndef _CEC_H
>> +#define _CEC_H
>> +
>> +#include <linux/types.h>
>> +
>> +struct cec_time {
>> +	__u64 sec;
>> +	__u64 nsec;
>> +};
> 
> I don't like having to introduce a new struct for time here. Basically we are
> only doing this because there is still no public struct timespec64.
> 
> When will that struct become available for use in a public API? If it is 4.2,
> then we can start using it. If it will take longer, then what alternative can
> we use to prevent having to change the API later?
> 
> One alternative might be to drop it for now and just reserve space in the
> structs to add it later.
> 
> Input from the y2038 experts will be welcome!
> 
> Regards,
> 
> 	Hans
> 
>> +
>> +struct cec_msg {
>> +	struct cec_time ts;
>> +	__u32 len;
>> +	__u32 status;
>> +	__u32 timeout;
>> +	/* timeout (in ms) is used to timeout CEC_RECEIVE.
>> +	   Set to 0 if you want to wait forever. */
>> +	__u8  msg[16];
>> +	__u8  reply;
>> +	/* If non-zero, then wait for a reply with this opcode.
>> +	   If there was an error when sending the msg or FeatureAbort
>> +	   was returned, then reply is set to 0.
>> +	   If reply is non-zero upon return, then len/msg are set to
>> +	   the received message.
>> +	   If reply is zero upon return and status has the
>> +	   CEC_TX_STATUS_FEATURE_ABORT bit set, then len/msg are set to the
>> +	   received feature abort message.
>> +	   If reply is zero upon return and status has the
>> +	   CEC_TX_STATUS_REPLY_TIMEOUT
>> +	   bit set, then no reply was seen at all.
>> +	   This field is ignored with CEC_RECEIVE.
>> +	   If reply is non-zero for CEC_TRANSMIT and the message is a broadcast,
>> +	   then -EINVAL is returned.
>> +	   if reply is non-zero, then timeout is set to 1000 (the required
>> +	   maximum response time).
>> +	 */
>> +	__u8 reserved[31];
>> +};
>> +
>> +static inline __u8 cec_msg_initiator(const struct cec_msg *msg)
>> +{
>> +	return msg->msg[0] >> 4;
>> +}
>> +
>> +static inline __u8 cec_msg_destination(const struct cec_msg *msg)
>> +{
>> +	return msg->msg[0] & 0xf;
>> +}
>> +
>> +static inline bool cec_msg_is_broadcast(const struct cec_msg *msg)
>> +{
>> +	return (msg->msg[0] & 0xf) == 0xf;
>> +}
>> +
>> +/* cec status field */
>> +#define CEC_TX_STATUS_OK            (0)
>> +#define CEC_TX_STATUS_ARB_LOST      (1 << 0)
>> +#define CEC_TX_STATUS_RETRY_TIMEOUT (1 << 1)
>> +#define CEC_TX_STATUS_FEATURE_ABORT (1 << 2)
>> +#define CEC_TX_STATUS_REPLY_TIMEOUT (1 << 3)
>> +#define CEC_RX_STATUS_READY         (0)
>> +
>> +#define CEC_LOG_ADDR_INVALID 0xff
>> +
>> +/* The maximum number of logical addresses one device can be assigned to.
>> + * The CEC 2.0 spec allows for only 2 logical addresses at the moment. The
>> + * Analog Devices CEC hardware supports 3. So let's go wild and go for 4. */
>> +#define CEC_MAX_LOG_ADDRS 4
>> +
>> +/* The "Primary Device Type" */
>> +#define CEC_PRIM_DEVTYPE_TV		0
>> +#define CEC_PRIM_DEVTYPE_RECORD		1
>> +#define CEC_PRIM_DEVTYPE_TUNER		3
>> +#define CEC_PRIM_DEVTYPE_PLAYBACK	4
>> +#define CEC_PRIM_DEVTYPE_AUDIOSYSTEM	5
>> +#define CEC_PRIM_DEVTYPE_SWITCH		6
>> +#define CEC_PRIM_DEVTYPE_VIDEOPROC	7
>> +
>> +/* The "All Device Types" flags (CEC 2.0) */
>> +#define CEC_FL_ALL_DEVTYPE_TV		(1 << 7)
>> +#define CEC_FL_ALL_DEVTYPE_RECORD	(1 << 6)
>> +#define CEC_FL_ALL_DEVTYPE_TUNER	(1 << 5)
>> +#define CEC_FL_ALL_DEVTYPE_PLAYBACK	(1 << 4)
>> +#define CEC_FL_ALL_DEVTYPE_AUDIOSYSTEM	(1 << 3)
>> +#define CEC_FL_ALL_DEVTYPE_SWITCH	(1 << 2)
>> +/* And if you wondering what happened to VIDEOPROC devices: those should
>> + * be mapped to a SWITCH. */
>> +
>> +/* The logical address types that the CEC device wants to claim */
>> +#define CEC_LOG_ADDR_TYPE_TV		0
>> +#define CEC_LOG_ADDR_TYPE_RECORD	1
>> +#define CEC_LOG_ADDR_TYPE_TUNER		2
>> +#define CEC_LOG_ADDR_TYPE_PLAYBACK	3
>> +#define CEC_LOG_ADDR_TYPE_AUDIOSYSTEM	4
>> +#define CEC_LOG_ADDR_TYPE_SPECIFIC	5
>> +#define CEC_LOG_ADDR_TYPE_UNREGISTERED	6
>> +/* Switches should use UNREGISTERED.
>> + * Video processors should use SPECIFIC. */
>> +
>> +/* The CEC version */
>> +#define CEC_VERSION_1_4B		5
>> +#define CEC_VERSION_2_0			6
>> +
>> +struct cec_event {
>> +	struct cec_time ts;
>> +	__u32 event;
>> +	__u8 reserved[4];
>> +};
>> +
>> +/* The CEC state */
>> +#define CEC_STATE_DISABLED		0
>> +#define CEC_STATE_ENABLED		1
>> +
>> +/* Userspace has to configure the adapter state (enable/disable) */
>> +#define CEC_CAP_STATE		(1 << 0)
>> +/* Userspace has to configure the physical address */
>> +#define CEC_CAP_PHYS_ADDR	(1 << 1)
>> +/* Userspace has to configure the logical addresses */
>> +#define CEC_CAP_LOG_ADDRS	(1 << 2)
>> +/* Userspace can transmit messages */
>> +#define CEC_CAP_TRANSMIT	(1 << 3)
>> +/* Userspace can receive messages */
>> +#define CEC_CAP_RECEIVE		(1 << 4)
>> +/* Userspace has to configure the vendor id */
>> +#define CEC_CAP_VENDOR_ID	(1 << 5)
>> +/* The hardware has the possibility to work in the promiscuous mode */
>> +#define CEC_CAP_PROMISCUOUS	(1 << 6)
>> +
>> +struct cec_caps {
>> +	__u32 available_log_addrs;
>> +	__u32 capabilities;
>> +	__u32 vendor_id;
>> +	__u8  version;
>> +	__u8  reserved[11];
>> +};
>> +
>> +struct cec_log_addrs {
>> +	__u8 cec_version;
>> +	__u8 num_log_addrs;
>> +	__u8 primary_device_type[CEC_MAX_LOG_ADDRS];
>> +	__u8 log_addr_type[CEC_MAX_LOG_ADDRS];
>> +	__u8 log_addr[CEC_MAX_LOG_ADDRS];
>> +
>> +	/* CEC 2.0 */
>> +	__u8 all_device_types;
>> +	__u8 features[CEC_MAX_LOG_ADDRS][12];
>> +
>> +	__u8 reserved[9];
>> +};
>> +
>> +/* Commands */
>> +
>> +/* One Touch Play Feature */
>> +#define CEC_OP_ACTIVE_SOURCE			0x82
>> +#define CEC_OP_IMAGE_VIEW_ON			0x04
>> +#define CEC_OP_TEXT_VIEW_ON			0x0d
>> +
>> +/* Routing Control Feature */
>> +#define CEC_OP_ACTIVE_SOURCE			0x82
>> +#define CEC_OP_INACTIVE_SOURCE			0x9d
>> +#define CEC_OP_REQUEST_ACTIVE_SOURCE		0x85
>> +#define CEC_OP_ROUTING_CHANGE			0x80
>> +#define CEC_OP_ROUTING_INFORMATION		0x81
>> +#define CEC_OP_SET_STREAM_PATH			0x86
>> +
>> +/* Standby Feature */
>> +#define CEC_OP_STANDBY				0x36
>> +
>> +/* One Touch Record Feature */
>> +#define CEC_OP_RECORD_OFF			0x0b
>> +#define CEC_OP_RECORD_ON			0x09
>> +#define CEC_OP_RECORD_STATUS			0x0a
>> +#define CEC_OP_RECORD_TV_SCREEN			0x0f
>> +
>> +/* Timer Programming Feature */
>> +#define CEC_OP_CLEAR_ANALOGUE_TIMER		0x33
>> +#define CEC_OP_CLEAR_DIGITAL_TIMER		0x99
>> +#define CEC_OP_CLEAR_EXT_TIMER			0xa1
>> +#define CEC_OP_SET_ANALOGUE_TIMER		0x34
>> +#define CEC_OP_SET_DIGITAL_TIMER		0x97
>> +#define CEC_OP_SET_EXT_TIMER			0xa2
>> +#define CEC_OP_SET_EXT_PROGRAM_TIMER		0x67
>> +#define CEC_OP_TIMER_CLEARED_STATUS		0x43
>> +#define CEC_OP_TIMER_STATUS			0x35
>> +
>> +/* System Information Feature */
>> +#define CEC_OP_CEC_VERSION			0x9e
>> +#define CEC_OP_GET_CEC_VERSION			0x9f
>> +#define CEC_OP_GIVE_PHYSICAL_ADDR		0x83
>> +#define CEC_OP_GET_MENU_LANGUAGE		0x91
>> +#define CEC_OP_REPORT_PHYSICAL_ADDR		0x84
>> +#define CEC_OP_SET_MENU_LANGUAGE		0x32
>> +
>> +/* Deck Control Feature */
>> +#define CEC_OP_DECK_CONTROL			0x42
>> +#define CEC_OP_DECK_STATUS			0x1b
>> +#define CEC_OP_GIVE_DECK_STATUS			0x1a
>> +#define CEC_OP_PLAY				0x41
>> +
>> +/* Tuner Control Feature */
>> +#define CEC_OP_GIVE_TUNER_DEVICE_STATUS		0x08
>> +#define CEC_OP_SELECT_ANALOGUE_SERVICE		0x92
>> +#define CEC_OP_SELECT_DIGITAL_SERVICE		0x93
>> +#define CEC_OP_TUNER_DEVICE_STATUS		0x07
>> +#define CEC_OP_TUNER_STEP_DECREMENT		0x06
>> +#define CEC_OP_TUNER_STEP_INCREMENT		0x05
>> +
>> +/* Vendor Specific Commands Feature */
>> +#define CEC_OP_CEC_VERSION			0x9e
>> +#define CEC_OP_DEVICE_VENDOR_ID			0x87
>> +#define CEC_OP_GET_CEC_VERSION			0x9f
>> +#define CEC_OP_GIVE_DEVICE_VENDOR_ID		0x8c
>> +#define CEC_OP_VENDOR_COMMAND			0x89
>> +#define CEC_OP_VENDOR_COMMAND_WITH_ID		0xa0
>> +#define CEC_OP_VENDOR_REMOTE_BUTTON_DOWN	0x8a
>> +#define CEC_OP_VENDOR_REMOTE_BUTTON_UP		0x8b
>> +
>> +/* OSD Display Feature */
>> +#define CEC_OP_SET_OSD_STRING			0x64
>> +
>> +/* Device OSD Transfer Feature */
>> +#define CEC_OP_GIVE_OSD_NAME			0x46
>> +#define CEC_OP_SET_OSD_NAME			0x47
>> +
>> +/* Device Menu Control Feature */
>> +#define CEC_OP_MENU_REQUEST			0x8d
>> +#define CEC_OP_MENU_STATUS			0x8e
>> +#define CEC_OP_USER_CONTROL_PRESSED		0x44
>> +#define CEC_OP_USER_CONTROL_RELEASED		0x45
>> +
>> +/* Power Status Feature */
>> +#define CEC_OP_GIVE_DEVICE_POWER_STATUS		0x8f
>> +#define CEC_OP_REPORT_POWER_STATUS		0x90
>> +#define CEC_OP_FEATURE_ABORT			0x00
>> +#define CEC_OP_ABORT				0xff
>> +
>> +/* System Audio Control Feature */
>> +#define CEC_OP_GIVE_AUDIO_STATUS		0x71
>> +#define CEC_OP_GIVE_SYSTEM_AUDIO_MODE_STATUS	0x7d
>> +#define CEC_OP_REPORT_AUDIO_STATUS		0x7a
>> +#define CEC_OP_SET_SYSTEM_AUDIO_MODE		0x72
>> +#define CEC_OP_SYSTEM_AUDIO_MODE_REQUEST	0x70
>> +#define CEC_OP_SYSTEM_AUDIO_MODE_STATUS		0x7e
>> +
>> +/* Audio Rate Control Feature */
>> +#define CEC_OP_SET_AUDIO_RATE			0x9a
>> +
>> +/* Events */
>> +/* Event that occurs when a cable is connected */
>> +#define CEC_EVENT_CONNECT	1
>> +/* Event that occurs when all logical addresses were claimed */
>> +#define CEC_EVENT_READY		2
>> +/* Event that is sent when the cable is disconnected */
>> +#define CEC_EVENT_DISCONNECT	3
>> +
>> +/* ioctls */
>> +
>> +/* issue a CEC command */
>> +#define CEC_G_CAPS		_IOWR('a', 0, struct cec_caps)
>> +#define CEC_TRANSMIT		_IOWR('a', 1, struct cec_msg)
>> +#define CEC_RECEIVE		_IOWR('a', 2, struct cec_msg)
>> +
>> +/*
>> +   Configure the CEC adapter. It sets the device type and which
>> +   logical types it will try to claim. It will return which
>> +   logical addresses it could actually claim.
>> +   An error is returned if the adapter is disabled or if there
>> +   is no physical address assigned.
>> + */
>> +
>> +#define CEC_G_ADAP_LOG_ADDRS	_IOR('a', 3, struct cec_log_addrs)
>> +#define CEC_S_ADAP_LOG_ADDRS	_IOWR('a', 4, struct cec_log_addrs)
>> +
>> +/*
>> +   Enable/disable the adapter. The Set state ioctl may not
>> +   be available if that is handled internally.
>> + */
>> +#define CEC_G_ADAP_STATE	_IOR('a', 5, __u32)
>> +#define CEC_S_ADAP_STATE	_IOW('a', 6, __u32)
>> +
>> +/*
>> +   phys_addr is either 0 (if this is the CEC root device)
>> +   or a valid physical address obtained from the sink's EDID
>> +   as read by this CEC device (if this is a source device)
>> +   or a physical address obtained and modified from a sink
>> +   EDID and used for a sink CEC device.
>> +   If nothing is connected, then phys_addr is 0xffff.
>> +   See HDMI 1.4b, section 8.7 (Physical Address).
>> +
>> +   The Set ioctl may not be available if that is handled
>> +   internally.
>> + */
>> +#define CEC_G_ADAP_PHYS_ADDR	_IOR('a', 7, __u16)
>> +#define CEC_S_ADAP_PHYS_ADDR	_IOW('a', 8, __u16)
>> +
>> +#define CEC_G_EVENT		_IOWR('a', 9, struct cec_event)
>> +/*
>> +   Read and set the vendor ID of the CEC adapter.
>> + */
>> +#define CEC_G_VENDOR_ID		_IOR('a', 9, __u32)
>> +#define CEC_S_VENDOR_ID		_IOW('a', 10, __u32)
>> +
>> +#endif
>>
> 
> _______________________________________________
> Y2038 mailing list
> Y2038@lists.linaro.org
> https://lists.linaro.org/mailman/listinfo/y2038
> 


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [Y2038] [PATCH v4 06/10] cec: add HDMI CEC framework: y2038 question
  2015-05-04  7:42     ` [Y2038] " Hans Verkuil
@ 2015-05-04 10:14       ` Arnd Bergmann
  2015-05-06 15:58         ` Hans Verkuil
  0 siblings, 1 reply; 27+ messages in thread
From: Arnd Bergmann @ 2015-05-04 10:14 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: y2038, Kamil Debski, linux-samsung-soc, mchehab, dmitry.torokhov,
	dri-devel, kyungmin.park, thomas, linux-input, linux-media,
	m.szyprowski

On Monday 04 May 2015 09:42:36 Hans Verkuil wrote:
> Ping! (Added Arnd to the CC list)

Hi Hans,

sorry I missed this the first time

> On 04/27/2015 09:40 AM, Hans Verkuil wrote:
> > Added the y2038 mailinglist since I would like to get their input for
> > this API.
> > 
> > Y2038 experts, can you take a look at my comment in the code below?
> > 
> > Thanks!
> 
> Arnd, I just saw your patch series adding struct __kernel_timespec to
> uapi/linux/time.h. I get the feeling that it might take a few kernel
> cycles before we have a timespec64 available in userspace. Based on that
> I think this CEC API should drop the timestamps for now and wait until
> timespec64 becomes available before adding it.
> 
> The timestamps are a nice-to-have, but not critical. So adding it later
> shouldn't be a problem. What is your opinion?

It will take a little while for the patches to make it in, I would guess
4.3 at the earliest. Using your own struct works just as well and would
be less ambiguous.

However, for timestamps, I would recommend not using timespec anyway.
Instead, just use a single 64-bit nanosecond value from ktime_get_ns()
(or ktime_get_boot_ns() if you need a time that keeps ticking across
suspend). This is more efficient to get and simpler to use as long
as you don't need to convert from nanosecond to timespec.

	Arnd

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [Y2038] [PATCH v4 06/10] cec: add HDMI CEC framework: y2038 question
  2015-05-04 10:14       ` Arnd Bergmann
@ 2015-05-06 15:58         ` Hans Verkuil
  2015-05-06 16:17           ` Arnd Bergmann
  0 siblings, 1 reply; 27+ messages in thread
From: Hans Verkuil @ 2015-05-06 15:58 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: y2038, Kamil Debski, linux-samsung-soc, mchehab, dmitry.torokhov,
	dri-devel, kyungmin.park, thomas, linux-input, linux-media,
	m.szyprowski

Hi Arnd,

On 05/04/2015 12:14 PM, Arnd Bergmann wrote:
> On Monday 04 May 2015 09:42:36 Hans Verkuil wrote:
>> Ping! (Added Arnd to the CC list)
> 
> Hi Hans,
> 
> sorry I missed this the first time
> 
>> On 04/27/2015 09:40 AM, Hans Verkuil wrote:
>>> Added the y2038 mailinglist since I would like to get their input for
>>> this API.
>>>
>>> Y2038 experts, can you take a look at my comment in the code below?
>>>
>>> Thanks!
>>
>> Arnd, I just saw your patch series adding struct __kernel_timespec to
>> uapi/linux/time.h. I get the feeling that it might take a few kernel
>> cycles before we have a timespec64 available in userspace. Based on that
>> I think this CEC API should drop the timestamps for now and wait until
>> timespec64 becomes available before adding it.
>>
>> The timestamps are a nice-to-have, but not critical. So adding it later
>> shouldn't be a problem. What is your opinion?
> 
> It will take a little while for the patches to make it in, I would guess
> 4.3 at the earliest. Using your own struct works just as well and would
> be less ambiguous.
> 
> However, for timestamps, I would recommend not using timespec anyway.
> Instead, just use a single 64-bit nanosecond value from ktime_get_ns()
> (or ktime_get_boot_ns() if you need a time that keeps ticking across
> suspend). This is more efficient to get and simpler to use as long
> as you don't need to convert from nanosecond to timespec.

Possibly stupid follow-up question:

is ktime_get_ns() just a different representation as ktime_get_ts64()?

Or is there some offset between the two? They seem to be identical based
on a quick test, but I'd like to be certain that that's always the case.

Users need to be able to relate this timestamp to a struct timespec as
returned by V4L2 (and others).

Thanks!

	Hans

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [Y2038] [PATCH v4 06/10] cec: add HDMI CEC framework: y2038 question
  2015-05-06 15:58         ` Hans Verkuil
@ 2015-05-06 16:17           ` Arnd Bergmann
  0 siblings, 0 replies; 27+ messages in thread
From: Arnd Bergmann @ 2015-05-06 16:17 UTC (permalink / raw)
  To: y2038
  Cc: Hans Verkuil, Kamil Debski, linux-samsung-soc, mchehab,
	dmitry.torokhov, dri-devel, kyungmin.park, thomas, linux-input,
	m.szyprowski, linux-media

On Wednesday 06 May 2015 17:58:01 Hans Verkuil wrote:
> 
> On 05/04/2015 12:14 PM, Arnd Bergmann wrote:
> > On Monday 04 May 2015 09:42:36 Hans Verkuil wrote:
> >> Ping! (Added Arnd to the CC list)
> > 
> > Hi Hans,
> > 
> > sorry I missed this the first time
> > 
> >> On 04/27/2015 09:40 AM, Hans Verkuil wrote:
> >>> Added the y2038 mailinglist since I would like to get their input for
> >>> this API.
> >>>
> >>> Y2038 experts, can you take a look at my comment in the code below?
> >>>
> >>> Thanks!
> >>
> >> Arnd, I just saw your patch series adding struct __kernel_timespec to
> >> uapi/linux/time.h. I get the feeling that it might take a few kernel
> >> cycles before we have a timespec64 available in userspace. Based on that
> >> I think this CEC API should drop the timestamps for now and wait until
> >> timespec64 becomes available before adding it.
> >>
> >> The timestamps are a nice-to-have, but not critical. So adding it later
> >> shouldn't be a problem. What is your opinion?
> > 
> > It will take a little while for the patches to make it in, I would guess
> > 4.3 at the earliest. Using your own struct works just as well and would
> > be less ambiguous.
> > 
> > However, for timestamps, I would recommend not using timespec anyway.
> > Instead, just use a single 64-bit nanosecond value from ktime_get_ns()
> > (or ktime_get_boot_ns() if you need a time that keeps ticking across
> > suspend). This is more efficient to get and simpler to use as long
> > as you don't need to convert from nanosecond to timespec.
> 
> Possibly stupid follow-up question:
> 
> is ktime_get_ns() just a different representation as ktime_get_ts64()?

Yes.

> Or is there some offset between the two? They seem to be identical based
> on a quick test, but I'd like to be certain that that's always the case.
>
> Users need to be able to relate this timestamp to a struct timespec as
> returned by V4L2 (and others).

* ktime_get_ns() uses the same timebase as ktime_get_ts64().
* ktime_get_boot_ns() uses the same timebase as ktime_get_boottime() or
  getboottime64(), which differs from the first after suspend
* ktime_get_real_ns() uses the same time as gettimeofday() in user space,
  which is always different from the other two.

	Arnd

^ permalink raw reply	[flat|nested] 27+ messages in thread

end of thread, other threads:[~2015-05-06 16:17 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-04-23 13:03 [PATCH v4 00/10] HDMI CEC framework Kamil Debski
2015-04-23 13:03 ` [PATCH v4 01/10] dts: exynos4*: add HDMI CEC pin definition to pinctrl Kamil Debski
2015-04-23 13:03 ` [PATCH v4 02/10] dts: exynos4: add node for the HDMI CEC device Kamil Debski
2015-04-23 13:03 ` [PATCH v4 03/10] dts: exynos4412-odroid*: enable " Kamil Debski
2015-04-23 13:03 ` [PATCH v4 04/10] HID: add HDMI CEC specific keycodes Kamil Debski
2015-04-23 13:03 ` [PATCH v4 05/10] rc: Add HDMI CEC protoctol handling Kamil Debski
2015-04-23 13:03 ` [PATCH v4 06/10] cec: add HDMI CEC framework Kamil Debski
2015-04-23 14:18   ` Hans Verkuil
2015-04-24 10:03   ` Lars Op den Kamp
2015-04-27  8:09     ` Kamil Debski
2015-04-27  8:19       ` Hans Verkuil
2015-04-27  7:40   ` [PATCH v4 06/10] cec: add HDMI CEC framework: y2038 question Hans Verkuil
2015-05-04  7:42     ` [Y2038] " Hans Verkuil
2015-05-04 10:14       ` Arnd Bergmann
2015-05-06 15:58         ` Hans Verkuil
2015-05-06 16:17           ` Arnd Bergmann
2015-04-27  9:13   ` [PATCH v4 06/10] cec: add HDMI CEC framework Hans Verkuil
2015-04-27  9:22   ` Hans Verkuil
2015-04-27  9:30     ` Hans Verkuil
2015-04-27 11:45     ` Hans Verkuil
2015-04-27 10:04   ` Hans Verkuil
2015-04-27 10:25   ` Hans Verkuil
2015-04-27 11:27     ` Hans Verkuil
2015-04-27 12:18       ` Kamil Debski
2015-04-23 13:03 ` [PATCH v4 07/10] v4l2-subdev: add HDMI CEC ops Kamil Debski
2015-04-23 13:03 ` [PATCH v4 08/10] cec: adv7604: add cec support Kamil Debski
2015-04-23 13:03 ` [PATCH v4 09/10] cec: adv7511: " Kamil Debski

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).