Skip to contentSkip to conversation

HDMI CEC framework

PATCH New Hans Verkuil 1 participant 17 messages Nov 12, 2015

Subsystems (13)
lore
Share & tools
lore
#0 lore raw

v10 cover HDMI CEC framework

Hi all,

The tenth version of this patchset addresses comments I received from Russell King and various bug fixes and enhancements as the result of more testing.

The cec.txt has been updated, but before I can make the final version there are three areas that I want to look at more closely:

1) What to do if a cable is disconnected and reconnected for a source:
should the CEC adapter be disabled and re-enabled? Which means that you need to reconfigure. Or just update the physical address from the EDID (if necessary)? I think the latter, but I need to analyze this more closely.

2) I am not happy with the event mechanism. It's OK for messages, but for other events it is awkward.

3) The status field gives insufficient information and I want to reorganize that for the next version.

The cec-ctl and cec-compliance utilities used to test the CEC framework can be found here:

http://git.linuxtv.org/cgit.cgi/hverkuil/v4l-utils.git/log/?h=cec

Best regards,

Hans

Changes since v9
================
- Updated cec.txt
- Added a promiscuous capability to signal those adapters that can monitor all CEC traffic, not just directed and broadcast messages. I have one adapter that can do this. Added code in the framework to handle such messages correctly.
- The status field is now value and no longer a bitmask.
- Renamed the kernel config from CEC to MEDIA_CEC
- The adap_transmit() callback now has a retries argument.
- Use the new CEC_MAX_MSG_SIZE define instead of hardcoding it as 16
- Add support to wait for a reply after a broadcast message: this was forbidden, but it is a valid use-case.
- Make sure you can't send a message to yourself.
- Waiting for a transmit to succeed would never timeout (and couldn't be interrupted). Fixed.
- The message status was not updated correctly if it was CEC_MSG_FEATURE_ABORTed.
- Fixed a nasty kernel oops when deleting a cec adapter.
- Removed the owner check: the module owner is NULL if it is compiled into the kernel instead of as a module.
- Added separate register/unregister calls: this is safer and actually made it possible to drop the ugly 'cec_ready' v4l2_subdev op. Suggested by Russell, and that was a good idea.
- Added missing support for 32-bit to 64-bit ioctl conversion.
- Move the v4l2_subdev cec ops into a v4l2_subdev_cec_ops struct.

Changes since v8
================
- Addressed the comments Russell King made about how the cec character devices should be allocated/freed.
- Updated the DocBook documentation.

Changes since v7
================

- I thought that the core thread could handle out-of-order messages, but that turned out to be wrong. After careful analysis I realized that I had to rewrite this part in cec.c in order to make it work.
- Added new CEC-specific keys to input.h and use them in the CEC rc keymap. Replaced KEY_PLAY/PAUSE/STOP with KEY_PLAYCD/PAUSECD/STOPCD to clarify that these are media operations and not the Pause key on the keyboard.
- Added CEC_PHYS_ADDR_INVALID (0xffff)
- Added monitor support to monitor CEC traffic
- Replaced CAP_TRANSMIT and CAP_RECEIVE by a single CAP_IO.
- Replaced CAP_CDC by CAP_CDC_HPD since this only applies to the HPD part of the CDC messages.
- Add CAP_IS_SOURCE.
- Add ninputs field to cec_caps to export the number of inputs of the device.
- Drop CEC_LOG_ADDRS_FL_HANDLE_MSGS and the flags field (see next change for more info).
- Add CEC_CLAIM and CEC_RELEASE to explicitly start/stop processing CEC messages. This also implies ownership of the CEC interface, so other filehandles can only receive but not transmit.
- Reworked event handling: report adapter state changes, input changes and if the message receive queue is full.
- cec-funcs.h: added CDC HEC support.
- Renamed G/S_ADAP ioctls to ADAP_G/S: this made it clearer which ioctls deal with the adapter configuration and which deal with CEC messages/events.
- Clarified which CEC messages are passed on to userspace and which aren't. Specifically if CAP_ARC is set, then all ARC messages are handled by the kernel. If CAP_CDC_HPD is set, then all CDC hotplug messages are handled by the kernel. Otherwise these messages are passed on to userspace.

Changes since v6
================
- added cec-funcs.h to provide wrapper functions that fill in the cec_msg struct. This header is needed both by the kernel and by applications.
- fix a missing rc_unregister_device call.
- added CEC support for the adv7842 and cobalt drivers.
- added CEC operand defines. Rename CEC message defines to CEC_MSG_ and operand defines now use CEC_OP_.
- the CEC_VERSION defines are dropped since we now have the CEC_OP_VERSION defines.
- ditto: CEC_PRIM_DEVTYPE_ is now CEC_OP_PRIM_DEVTYPE.
- ditto: CEC_FL_ALL_DEVTYPE_ is now CEC_OP_ALL_DEVTYPE.
- cec-ioc-g-adap-log-addrs.xml: document cec_versions field.
- cec-ioc-g-caps.xml: drop vendor_id and version fields.
- add MAINTAINERS entry.
- add CDC support (not yet fully functional).
- add a second debug level for message debugging.
- fix a nasty kernel Oops in cec_transmit_msg while waiting for transmit completion (adap->tx_queue[idx].func wasn't set to NULL).
- add support for CEC_MSG_REPORT_FEATURES (CEC 2.0 only).
- correctly abort unsupported messages.
- add support for the device power status feature.
- add support for the audio return channel (preliminary).
- add support for the CDC hotplug message (preliminary).
- added osd_name to struct cec_log_addrs.
- reported physical addresses are stored internally.
- fix enabling/disabling the CEC adapter (internal fields weren't cleared correctly).
- zero reserved fields.
- return an error if you try to receive/transmit and the adapter isn't configured.
- when creating the adapter provide the owner module and the parent device.
- add a CEC_VENDOR_ID_NONE define to signal if no vendor ID was set.
- add new capabilities: RC (remote control), ARC (audio return channel) and CDC (Capability Discovery and Control).
- applications that want to handle messages for a logical address need to set the CEC_LOG_ADDRS_FL_HANDLE_MSGS flag. Otherwise the CEC core will be the one handling all messages.
- Each logical address has its own all_device_types value. So this should be an array, not a single value.
- I'm sure I've forgotten some changes...

Changes since v5
================
- drop struct cec_timeval in favour of a __u64 that keeps the timestamp in ns
- remove userspace documentation from Documentation/cec.txt as userspace API is described in the DocBook
- add missing documentation for the passthrough mode to the DocBook
- add information about the number of events that can be queued
- fix misspelling of reply
- fix behaviour of posting an event in cec_received_msg, such that the behaviour is consistent with the documentation

Changes since v4
================
- add sequence numbering to transmitted messages
- add sequence number handling to event hanlding
- add passthrough mode
- change reserved field sizes
- fixed CEC version defines and addec CEC 2.0 commands
- add DocBook documentation

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

Background
==========

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.

Original RFC by Hans Verkuil/Martin Bugge
=========================================
https://www.mail-archive.com/[email protected]/msg28735.html

Hans Verkuil (10):
input.h: add BUS_CEC type
cec: add HDMI CEC framework
cec: add compat32 ioctl support
cec.txt: add CEC framework documentation
DocBook/media: add CEC documentation
v4l2-subdev: add HDMI CEC ops
cec: adv7604: add cec support.
cec: adv7842: add cec support
cec: adv7511: add cec support.
cobalt: 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 protocol handling
cec: s5p-cec: Add s5p-cec driver

 Documentation/DocBook/device-drivers.tmpl          |    3 +
 Documentation/DocBook/media/Makefile               |    2 +
 Documentation/DocBook/media/v4l/biblio.xml         |   10 +
 Documentation/DocBook/media/v4l/cec-api.xml        |   76 +
 Documentation/DocBook/media/v4l/cec-func-close.xml |   59 +
 Documentation/DocBook/media/v4l/cec-func-ioctl.xml |   73 +
 Documentation/DocBook/media/v4l/cec-func-open.xml  |   94 +
 Documentation/DocBook/media/v4l/cec-func-poll.xml  |   89 +
 .../DocBook/media/v4l/cec-ioc-adap-g-caps.xml      |  167 ++
 .../DocBook/media/v4l/cec-ioc-adap-g-log-addrs.xml |  306 +++
 .../DocBook/media/v4l/cec-ioc-adap-g-phys-addr.xml |   80 +
 .../DocBook/media/v4l/cec-ioc-adap-g-state.xml     |   87 +
 .../DocBook/media/v4l/cec-ioc-adap-g-vendor-id.xml |   70 +
 Documentation/DocBook/media/v4l/cec-ioc-claim.xml  |   71 +
 .../DocBook/media/v4l/cec-ioc-dqevent.xml          |  208 +++
 .../DocBook/media/v4l/cec-ioc-g-monitor.xml        |   86 +
 .../DocBook/media/v4l/cec-ioc-g-passthrough.xml    |   81 +
 .../DocBook/media/v4l/cec-ioc-receive.xml          |  190 ++
 Documentation/DocBook/media_api.tmpl               |    6 +-
 Documentation/cec.txt                              |  326 ++++
 .../devicetree/bindings/media/s5p-cec.txt          |   31 +
 MAINTAINERS                                        |   12 +
 arch/arm/boot/dts/exynos4.dtsi                     |   12 +
 arch/arm/boot/dts/exynos4210-pinctrl.dtsi          |    7 +
 arch/arm/boot/dts/exynos4210-universal_c210.dts    |    4 +
 arch/arm/boot/dts/exynos4x12-pinctrl.dtsi          |    7 +
 drivers/media/Kconfig                              |    7 +
 drivers/media/Makefile                             |    2 +
 drivers/media/cec.c                                | 1962 ++++++++++++++++++++
 drivers/media/i2c/adv7511.c                        |  364 +++-
 drivers/media/i2c/adv7604.c                        |  241 ++-
 drivers/media/i2c/adv7842.c                        |  255 ++-
 drivers/media/pci/cobalt/Kconfig                   |    1 +
 drivers/media/pci/cobalt/cobalt-driver.c           |  108 +-
 drivers/media/pci/cobalt/cobalt-driver.h           |    2 +
 drivers/media/pci/cobalt/cobalt-irq.c              |    3 +
 drivers/media/pci/cobalt/cobalt-v4l2.c             |  126 +-
 drivers/media/pci/cobalt/cobalt-v4l2.h             |    2 +
 drivers/media/platform/Kconfig                     |   12 +
 drivers/media/platform/Makefile                    |    1 +
 drivers/media/platform/s5p-cec/Makefile            |    2 +
 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           |  289 +++
 drivers/media/platform/s5p-cec/s5p_cec.h           |   76 +
 drivers/media/rc/keymaps/Makefile                  |    1 +
 drivers/media/rc/keymaps/rc-cec.c                  |  174 ++
 drivers/media/rc/rc-main.c                         |    1 +
 fs/compat_ioctl.c                                  |   19 +
 include/media/adv7511.h                            |    6 +-
 include/media/cec.h                                |  182 ++
 include/media/rc-core.h                            |    1 +
 include/media/rc-map.h                             |    5 +-
 include/media/v4l2-subdev.h                        |   14 +
 include/uapi/linux/Kbuild                          |    2 +
 include/uapi/linux/cec-funcs.h                     | 1771 ++++++++++++++++++
 include/uapi/linux/cec.h                           |  884 +++++++++
 include/uapi/linux/input.h                         |   29 +
 59 files changed, 8979 insertions(+), 61 deletions(-)
 create mode 100644 Documentation/DocBook/media/v4l/cec-api.xml
 create mode 100644 Documentation/DocBook/media/v4l/cec-func-close.xml
 create mode 100644 Documentation/DocBook/media/v4l/cec-func-ioctl.xml
 create mode 100644 Documentation/DocBook/media/v4l/cec-func-open.xml
 create mode 100644 Documentation/DocBook/media/v4l/cec-func-poll.xml
 create mode 100644 Documentation/DocBook/media/v4l/cec-ioc-adap-g-caps.xml
 create mode 100644 Documentation/DocBook/media/v4l/cec-ioc-adap-g-log-addrs.xml
 create mode 100644 Documentation/DocBook/media/v4l/cec-ioc-adap-g-phys-addr.xml
 create mode 100644 Documentation/DocBook/media/v4l/cec-ioc-adap-g-state.xml
 create mode 100644 Documentation/DocBook/media/v4l/cec-ioc-adap-g-vendor-id.xml
 create mode 100644 Documentation/DocBook/media/v4l/cec-ioc-claim.xml
 create mode 100644 Documentation/DocBook/media/v4l/cec-ioc-dqevent.xml
 create mode 100644 Documentation/DocBook/media/v4l/cec-ioc-g-monitor.xml
 create mode 100644 Documentation/DocBook/media/v4l/cec-ioc-g-passthrough.xml
 create mode 100644 Documentation/DocBook/media/v4l/cec-ioc-receive.xml
 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-funcs.h
 create mode 100644 include/uapi/linux/cec.h
Signature · 1 line
2.6.2
#1 lore raw

PATCH v10 1/16 dts: exynos4*: add HDMI CEC pin definition to pinctrl

From: Kamil Debski <[email protected]>

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

---
 arch/arm/boot/dts/exynos4210-pinctrl.dtsi | 7 +++++++
 arch/arm/boot/dts/exynos4x12-pinctrl.dtsi | 7 +++++++
 2 files changed, 14 insertions(+)
arch/arm/boot/dts/exynos4210-pinctrl.dtsi +7 -0
index a7c2128..9331c62 100644
@@ -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 {
arch/arm/boot/dts/exynos4x12-pinctrl.dtsi +7 -0
index bac25c6..856b292 100644
@@ -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_2: pinctrl@03860000 {
Signature · 1 line
2.6.2
#2 lore raw

PATCH v10 2/16 dts: exynos4: add node for the HDMI CEC device

In reply to Hans Verkuil

From: Kamil Debski <[email protected]>

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

---
 arch/arm/boot/dts/exynos4.dtsi | 12 ++++++++++++
 1 file changed, 12 insertions(+)
arch/arm/boot/dts/exynos4.dtsi +12 -0
index 98c0a36..7baff26 100644
@@ -720,6 +720,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>;
Signature · 1 line
2.6.2
#3 lore raw

PATCH v10 3/16 dts: exynos4412-odroid*: enable the HDMI CEC device

In reply to Hans Verkuil

From: Kamil Debski <[email protected]>

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

---
 arch/arm/boot/dts/exynos4210-universal_c210.dts | 4 ++++
 1 file changed, 4 insertions(+)
arch/arm/boot/dts/exynos4210-universal_c210.dts +4 -0
index eb37952..5c4393d 100644
@@ -222,6 +222,10 @@
 		enable-active-high;
 	};
 
+	cec@100B0000 {
+		status = "okay";
+	};
+
 	hdmi_ddc: i2c-ddc {
 		compatible = "i2c-gpio";
 		gpios = <&gpe4 2 0 &gpe4 3 0>;
Signature · 1 line
2.6.2
#4 lore raw

PATCH v10 4/16 input.h: add BUS_CEC type

In reply to Hans Verkuil

From: Hans Verkuil <[email protected]>

Inputs can come in over the HDMI CEC bus, so add a new type for this.

---
 include/uapi/linux/input.h | 1 +
 1 file changed, 1 insertion(+)
include/uapi/linux/input.h +1 -0
index 731417c..a32bff1 100644
@@ -972,6 +972,7 @@ struct input_keymap_entry {
 #define BUS_GSC			0x1A
 #define BUS_ATARI		0x1B
 #define BUS_SPI			0x1C
+#define BUS_CEC			0x1D
 
 /*
  * MT_TOOL types
Signature · 1 line
2.6.2
#5 lore raw

PATCH v10 5/16 HID: add HDMI CEC specific keycodes

In reply to Hans Verkuil

From: Kamil Debski <[email protected]>

Add HDMI CEC specific keycodes to the keycodes definition.

---
 include/uapi/linux/input.h | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)
include/uapi/linux/input.h +28 -0
index a32bff1..5e7019a 100644
@@ -752,6 +752,34 @@ 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_ROOT_MENU			0x26a /* Show Device's Root Menu */
+#define KEY_MEDIA_TOP_MENU		0x26b /* Show Top Menu of the Media (e.g. DVD) */
+#define KEY_NUMERIC_11			0x26c
+#define KEY_NUMERIC_12			0x26d
+/*
+ * Toggle Audio Description: refers to an audio service that helps blind and
+ * visually impaired consumers understand the action in a program. Note: in
+ * some countries this is referred to as "Video Description".
+ */
+#define KEY_AUDIO_DESC			0x26e
+#define KEY_3D_MODE			0x26f
+#define KEY_NEXT_FAVORITE		0x270
+#define KEY_STOP_RECORD			0x271
+#define KEY_PAUSE_RECORD		0x272
+#define KEY_VOD				0x273 /* Video on Demand */
+#define KEY_UNMUTE			0x274
+#define KEY_FASTREVERSE			0x275
+#define KEY_SLOWREVERSE			0x276
+/*
+ * Control a data application associated with the currently viewed channel,
+ * e.g. teletext or data broadcast application (MHEG, MHP, HbbTV, etc.)
+ */
+#define KEY_DATA			0x275
+
 #define BTN_TRIGGER_HAPPY		0x2c0
 #define BTN_TRIGGER_HAPPY1		0x2c0
 #define BTN_TRIGGER_HAPPY2		0x2c1
Signature · 1 line
2.6.2
#6 lore raw

PATCH v10 6/16 rc: Add HDMI CEC protocol handling

In reply to Hans Verkuil

From: Kamil Debski <[email protected]>

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.

---
 drivers/media/rc/keymaps/Makefile |   1 +
 drivers/media/rc/keymaps/rc-cec.c | 174 ++++++++++++++++++++++++++++++++++++++
 drivers/media/rc/rc-main.c        |   1 +
 include/media/rc-core.h           |   1 +
 include/media/rc-map.h            |   5 +-
 5 files changed, 181 insertions(+), 1 deletion(-)
 create mode 100644 drivers/media/rc/keymaps/rc-cec.c
drivers/media/rc/keymaps/Makefile +1 -0
index fbbd3bb..9cffcc6 100644
@@ -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 \
drivers/media/rc/keymaps/rc-cec.c new file +174 -0

174 lines not on this page · raw message

drivers/media/rc/rc-main.c +1 -0
index 3f0f71a..a639ea6 100644
@@ -801,6 +801,7 @@ static struct {
 	{ RC_BIT_SHARP,		"sharp"		},
 	{ RC_BIT_MCE_KBD,	"mce_kbd"	},
 	{ RC_BIT_XMP,		"xmp"		},
+	{ RC_BIT_CEC,		"cec"		},
 };
 
 /**
include/media/rc-core.h +1 -0
index ec921f6..d4d5050 100644
@@ -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,
 };
 
 /**
include/media/rc-map.h +4 -1
index 7c4bbc4..d9f87d5 100644
@@ -31,6 +31,7 @@ enum rc_type {
 	RC_TYPE_RC6_MCE		= 16,	/* MCE (Philips RC6-6A-32 subtype) protocol */
 	RC_TYPE_SHARP		= 17,	/* Sharp protocol */
 	RC_TYPE_XMP		= 18,	/* XMP protocol */
+	RC_TYPE_CEC		= 19,	/* CEC protocol */
 };
 
 #define RC_BIT_NONE		0
@@ -53,6 +54,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_RC5 | RC_BIT_RC5X | RC_BIT_RC5_SZ | \
@@ -61,7 +63,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)
@@ -123,6 +125,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"
Signature · 1 line
2.6.2
#7 lore raw

PATCH v10 7/16 cec: add HDMI CEC framework

In reply to Hans Verkuil

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

[[email protected]: Merged CEC Updates commit by Hans Verkuil]
[[email protected]: Merged Update author commit by Hans Verkuil]

[[email protected]: change kthread handling when setting logical address]
[[email protected]: code cleanup and fixes] [[email protected]: add missing CEC commands to match spec] [[email protected]: add RC framework support] [[email protected]: move and edit documentation] [[email protected]: add vendor id reporting] [[email protected]: add possibility to clear assigned logical addresses]
[[email protected]: documentation fixes, clenaup and expansion] [[email protected]: reorder of API structs and add reserved fields] [[email protected]: fix handling of events and fix 32/64bit timespec problem]
[[email protected]: add cec.h to include/uapi/linux/Kbuild] [[email protected]: add sequence number handling] [[email protected]: add passthrough mode] [[email protected]: fix CEC defines, add missing CEC 2.0 commands] minor additions]

---
 MAINTAINERS                    |   12 +
 drivers/media/Kconfig          |    7 +
 drivers/media/Makefile         |    2 +
 drivers/media/cec.c            | 1962 ++++++++++++++++++++++++++++++++++++++++
 include/media/cec.h            |  182 ++++
 include/uapi/linux/Kbuild      |    2 +
 include/uapi/linux/cec-funcs.h | 1771 ++++++++++++++++++++++++++++++++++++
 include/uapi/linux/cec.h       |  884 ++++++++++++++++++
 8 files changed, 4822 insertions(+)
 create mode 100644 drivers/media/cec.c
 create mode 100644 include/media/cec.h
 create mode 100644 include/uapi/linux/cec-funcs.h
 create mode 100644 include/uapi/linux/cec.h
MAINTAINERS +12 -0
index 42e81f9..23fe4c6 100644
@@ -2599,6 +2599,18 @@ F:	drivers/net/ieee802154/cc2520.c
 F:	include/linux/spi/cc2520.h
 F:	Documentation/devicetree/bindings/net/ieee802154/cc2520.txt
 
+CEC DRIVER
+M:	Hans Verkuil <[email protected]>
+L:	[email protected]
+T:	git git://linuxtv.org/media_tree.git
+W:	http://linuxtv.org
+S:	Supported
+F:	drivers/media/cec.c
+F:	drivers/media/rc/keymaps/rc-cec.c
+F:	include/media/cec.h
+F:	include/uapi/linux/cec.h
+F:	include/uapi/linux/cec-funcs.h
+
 CELL BROADBAND ENGINE ARCHITECTURE
 M:	Arnd Bergmann <[email protected]>
 L:	[email protected]
drivers/media/Kconfig +7 -0
index 3ef3d6c..ff22089 100644
@@ -80,6 +80,13 @@ config MEDIA_RC_SUPPORT
 
 	  Say Y when you have a TV or an IR device.
 
+config MEDIA_CEC
+	tristate "CEC API (EXPERIMENTAL)"
+	depends on MEDIA_RC_SUPPORT
+	select RC_CORE
+	---help---
+	  Enable the CEC API.
+
 #
 # Media controller
 #	Selectable only for webcam/grabbers, as other drivers don't use it
drivers/media/Makefile +2 -0
index e608bbc..58e8554 100644
@@ -2,6 +2,8 @@
 # Makefile for the kernel multimedia device drivers.
 #
 
+obj-$(CONFIG_MEDIA_CEC) += cec.o
+
 media-objs	:= media-device.o media-devnode.o media-entity.o
 
 #
drivers/media/cec.c new file +1962 -0

1,962 lines not on this page · raw message

include/media/cec.h new file +182 -0

182 lines not on this page · raw message

include/uapi/linux/Kbuild +2 -0
index f7b2db4..7194caa 100644
@@ -81,6 +81,8 @@ header-y += capi.h
 header-y += cciss_defs.h
 header-y += cciss_ioctl.h
 header-y += cdrom.h
+header-y += cec.h
+header-y += cec-funcs.h
 header-y += cgroupstats.h
 header-y += chio.h
 header-y += cm4000_cs.h
include/uapi/linux/cec-funcs.h new file +1771 -0

809 lines not on this page · raw message

include/uapi/linux/cec.h new file +884 -0

884 lines not on this page · raw message

The rest of this diff is not shown. View the raw message.

Signature · 1 line
2.6.2

Part of the diff is not shown here. View the raw message.

#8 lore raw

PATCH v10 8/16 cec: add compat32 ioctl support

In reply to Hans Verkuil

From: Hans Verkuil <[email protected]>

The CEC ioctls didn't have compat32 support, so they returned -ENOTTY when used in a 32 bit application on a 64 bit kernel.

Since all the CEC ioctls are 32-bit compatible adding support for this API is trivial.

---
 fs/compat_ioctl.c | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)
fs/compat_ioctl.c +19 -0
index 48851f6..c8651aa 100644
@@ -57,6 +57,7 @@
 #include <linux/i2c-dev.h>
 #include <linux/atalk.h>
 #include <linux/gfp.h>
+#include <linux/cec.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_sock.h>
@@ -1381,6 +1382,24 @@ COMPATIBLE_IOCTL(VIDEO_GET_NAVI)
 COMPATIBLE_IOCTL(VIDEO_SET_ATTRIBUTES)
 COMPATIBLE_IOCTL(VIDEO_GET_SIZE)
 COMPATIBLE_IOCTL(VIDEO_GET_FRAME_RATE)
+/* cec */
+COMPATIBLE_IOCTL(CEC_ADAP_G_CAPS)
+COMPATIBLE_IOCTL(CEC_ADAP_G_LOG_ADDRS)
+COMPATIBLE_IOCTL(CEC_ADAP_S_LOG_ADDRS)
+COMPATIBLE_IOCTL(CEC_ADAP_G_STATE)
+COMPATIBLE_IOCTL(CEC_ADAP_S_STATE)
+COMPATIBLE_IOCTL(CEC_ADAP_G_PHYS_ADDR)
+COMPATIBLE_IOCTL(CEC_ADAP_S_PHYS_ADDR)
+COMPATIBLE_IOCTL(CEC_ADAP_G_VENDOR_ID)
+COMPATIBLE_IOCTL(CEC_ADAP_S_VENDOR_ID)
+COMPATIBLE_IOCTL(CEC_G_MONITOR)
+COMPATIBLE_IOCTL(CEC_S_MONITOR)
+COMPATIBLE_IOCTL(CEC_CLAIM)
+COMPATIBLE_IOCTL(CEC_RELEASE)
+COMPATIBLE_IOCTL(CEC_G_PASSTHROUGH)
+COMPATIBLE_IOCTL(CEC_TRANSMIT)
+COMPATIBLE_IOCTL(CEC_RECEIVE)
+COMPATIBLE_IOCTL(CEC_DQEVENT)
 
 /* joystick */
 COMPATIBLE_IOCTL(JSIOCGVERSION)
Signature · 1 line
2.6.2
#9 lore raw

PATCH v10 9/16 cec.txt: add CEC framework documentation

In reply to Hans Verkuil

Document the new HDMI CEC framework.

[[email protected]: add DocBook documentation by Hans Verkuil, with

---
 Documentation/cec.txt | 326 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 326 insertions(+)
 create mode 100644 Documentation/cec.txt
Documentation/cec.txt new file +326 -0
new file mode 100644
index 0000000..90c2c7f
@@ -0,0 +1,326 @@
+CEC Kernel Support
+==================
+
+The CEC framework provides a unified kernel interface for use with HDMI CEC
+hardware. It is designed to handle a multiple types of hardware (receivers,
+transmitters, USB dongles). The framework also gives the option to decide
+what to do in the kernel driver and what should be handled by userspace
+applications. In addition it integrates the remote control passthrough
+feature into the kernel's remote control framework.
+
+
+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 CEC framework described here is up to date with the CEC 2.0 specification.
+It is documented in the HDMI 1.4 specification with the new 2.0 bits documented
+in the HDMI 2.0 specification. But for most of the features the freely available
+HDMI 1.3a specification is sufficient:
+
+http://www.microprocessor.org/HDMISpecification13a.pdf
+
+
+The Kernel Interface
+====================
+
+CEC Adapter
+-----------
+
+The struct cec_adapter represents the CEC adapter hardware. It is created by
+calling cec_create_adapter() and deleted by calling cec_delete_adapter():
+
+struct cec_adapter *cec_create_adapter(const struct cec_adap_ops *ops,
+		       void *priv, const char *name, u32 caps,
+		       u8 ninputs, struct module *owner, struct device *parent);
+void cec_delete_adapter(struct cec_adapter *adap);
+
+To create an adapter you need to pass the following information:
+
+ops: adapter operations which are called by the CEC framework and that you
+have to implement.
+
+priv: will be stored in adap->priv and can be used by the adapter ops.
+
+name: the name of the CEC adapter
+
+caps: capabilities of the CEC adapter. These capabilities determine the
+	capabilities of the hardware and which parts are to be handled
+	by userspace and which parts are handled by kernelspace. The
+	capabilities are returned by CEC_ADAP_G_CAPS.
+
+ninputs: the number of HDMI inputs of the device. This may be 0. This
+	is returned by CEC_ADAP_G_CAPS.
+
+owner: the module owner.
+
+parent: the parent device.
+
+
+After creating the adapter the driver can modify the following fields
+in struct cec_adapter:
+
+	u8 available_log_addrs;
+
+This determines the number of simultaneous logical addresses the hardware
+can program. Often this is 1, which is also the default.
+
+	u8 pwr_state;
+
+The CEC_MSG_GIVE_DEVICE_POWER_STATUS power state. By default this is
+CEC_OP_POWER_STATUS_ON (0). The driver can change this to signal power
+state transitions.
+
+	u16 phys_addr;
+
+By default this is 0xffff, but drivers can change this. The phys_addr field
+must be set before the CEC adapter is enabled (see the adap_enable op below).
+While the CEC adapter remains enabled it cannot be changed. Drivers never set
+this if CEC_CAP_PHYS_ADDR is set.
+
+	u32 vendor_id;
+
+By default this is CEC_VENDOR_ID_NONE (0xffffffff). It should not be changed
+once the adapter is configured. Drivers never set this if CEC_CAP_VENDOR_ID
+is set.
+
+	u8 cec_version;
+
+The CEC version that the framework should support. By default this is the
+latest version, but it can be changed to an older version, causing attempts
+to use later extensions to fail. Obviously this should be set before the
+CEC adapter is enabled.
+
+To register the /dev/cecX device node and the remote control device (if
+CEC_CAP_RC is set) you call:
+
+int cec_register_adapter(struct cec_adapter *adap);
+
+To unregister the devices call:
+
+void cec_unregister_adapter(struct cec_adapter *adap);
+
+
+Implementing the Low-Level CEC Adapter
+--------------------------------------
+
+The following low-level adapter operations have to be implemented in
+your driver:
+
+struct cec_adap_ops {
+	/* Low-level callbacks */
+	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, u32 timeout_ms, struct cec_msg *msg);
+
+	/* High-level callbacks */
+	...
+};
+
+The three low-level ops deal with various aspects of controlling the CEC adapter
+hardware:
+
+To enable/disable the hardware:
+
+	int (*adap_enable)(struct cec_adapter *adap, bool enable);
+
+This callback enables or disables the CEC hardware. Enabling the CEC hardware
+means powering it up in a state where no logical addresses are claimed. This
+op assumes that the physical address (adap->phys_addr) is valid when enable is
+true and will not change while the CEC adapter remains enabled. The initial
+state of the CEC adapter after calling cec_create_adapter() is disabled.
+
+To program a new logical address:
+
+	int (*adap_log_addr)(struct cec_adapter *adap, u8 logical_addr);
+
+If logical_addr == CEC_LOG_ADDR_INVALID then all programmed logical addresses
+are to be erased. Otherwise the given logical address should be programmed.
+If the maximum number of available logical addresses is exceeded, then it
+should return -ENXIO. Once a logical address is programmed the CEC hardware
+can start receiving messages.
+
+To transmit a new message:
+
+	int (*adap_transmit)(struct cec_adapter *adap, u32 timeout_ms,
+			     u8 retries, struct cec_msg *msg);
+
+This transmits a new message. The timeout_ms argument is the suggested timeout
+in milliseconds to use in the hardware (200 ms for a poll message, 1000 ms for
+other messages). The retries argument is the suggested number of retries for
+the transmit. The low level implementation is responsible for repeating the
+transmit in case of a failure. This is typically something that the hardware
+will do for you. The recommended number of retry attempts are 1 for a poll
+message and 3 for other messages.
+
+Your adapter driver will also have to react to events (typically interrupt
+driven) by calling into the framework in the following situations:
+
+When a transmit finished (successfully or otherwise):
+
+void cec_transmit_done(struct cec_adapter *adap, u32 status);
+
+The status can be one of:
+
+CEC_TX_STATUS_OK: the transmit was successful.
+CEC_TX_STATUS_ARB_LOST: arbitration was lost: another CEC initiator
+unexpectedly takes control of the CEC line and you lost the arbitration.
+#define CEC_TX_STATUS_RETRY_TIMEOUT: could not transmit the message after
+trying multiple times.
+
+Never return CEC_TX_STATUS_FEATURE_ABORT or CEC_TX_STATUS_REPLY_TIMEOUT:
+these are high-level statuses that are returned by the CEC framework and
+not by a CEC adapter driver.
+
+When a CEC message was received:
+
+void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg);
+
+Speaks for itself.
+
+When the number connected inputs changes:
+
+void cec_connected_inputs(struct cec_adapter *adap, u16 connected_inputs);
+
+The connected_inputs argument is a bit mask where bit 0 refers to HDMI input 0,
+etc. Only the first ninputs (see CEC_ADAP_G_CAPS) bits can be 1, the remainder
+are always 0. This function should be called when the HDMI receiver driver
+detects when a new device is connected or disconnected from a given input.
+
+The CEC framework will pass this information on to the user.
+
+Implementing the High-Level CEC Adapter
+---------------------------------------
+
+The low-level operations drive the hardware, the high-level operations are
+CEC protocol driven. The following high-level callbacks are available:
+
+struct cec_adap_ops {
+	/* Low-level callbacks */
+	...
+
+	/* High-level CEC message callback */
+	int (*received)(struct cec_adapter *adap, struct cec_msg *msg);
+
+	/* High-level CDC Hotplug Detect callbacks */
+	u8 (*source_cdc_hpd)(struct cec_adapter *adap, u8 cdc_hpd_state);
+	void (*sink_cdc_hpd)(struct cec_adapter *adap, u8 cdc_hpd_state, u8 cdc_hpd_error);
+
+	/* High-level Audio Return Channel callbacks */
+	int (*sink_initiate_arc)(struct cec_adapter *adap);
+	int (*sink_terminate_arc)(struct cec_adapter *adap);
+	int (*source_arc_initiated)(struct cec_adapter *adap);
+	int (*source_arc_terminated)(struct cec_adapter *adap);
+};
+
+The received() callback allows the driver to optionally handle a newly
+received CEC message
+
+	int (*received)(struct cec_adapter *adap, struct cec_msg *msg);
+
+If the driver wants to process a CEC message, then it can implement this
+callback. If it doesn't want to handle this message, then it should return
+-ENOMSG, otherwise the CEC framework assumes it processed this message and
+it will not no anything with it.
+
+The other callbacks deal with two CEC features: CDC Hotplug Detect and
+Audio Return Channel. Here the framework takes care of handling these
+messages and it calls the callbacks to notify the driver when it needs
+to take action.
+
+CDC Hotplug Support
+-------------------
+
+A source received a hotplug state change message:
+
+	u8 (*source_cdc_hpd)(struct cec_adapter *adap, u8 cdc_hpd_state);
+
+A source received a CEC_MSG_CDC_HPD_SET_STATE message. The framework will
+reply with a CEC_MSG_CDC_HPD_REPORT_STATE message and this callback is used
+to fill in the HPD Error Code Operand of the REPORT_STATE message. In addition,
+the driver can act in this callback on the hotplug state change.
+
+Only implement if CEC_CAP_CDC_HPD is set.
+
+A sink received a hotplug report state message:
+
+	void (*sink_cdc_hpd)(struct cec_adapter *adap, u8 cdc_hpd_state, u8 cdc_hpd_error);
+
+A sink received a CEC_MSG_CDC_HPD_REPORT_STATE message. This callback will
+do anything necessary to implement this hotplug change. The two arguments
+are the HPD Error State and HPD Error Code Operands from the CEC_MSG_CDC_HPD_REPORT_STATE
+message.
+
+
+Audio Return Channel Support
+----------------------------
+
+Called if a CEC_MSG_INITIATE_ARC message is received by an HDMI sink.
+This callback should start sending audio over the audio return channel. If
+successful it should return 0.
+
+	int (*sink_initiate_arc)(struct cec_adapter *adap);
+
+Called if a CEC_MSG_TERMINATE_ARC message is received by an HDMI sink.
+This callback should stop sending audio over the audio return channel. If
+successful it should return 0.
+
+	void (*sink_terminate_arc)(struct cec_adapter *adap);
+
+Called if a CEC_MSG_REPORT_ARC_INITIATED message is received by an
+HDMI source. This callback can be used to enable receiving audio from
+the audio return channel.
+
+	void (*source_arc_initiated)(struct cec_adapter *adap);
+
+Called if a CEC_MSG_REPORT_ARC_TERMINATED message is received by an
+HDMI source. This callback can be used to disable receiving audio from
+the audio return channel.
+
+	void (*source_arc_terminated)(struct cec_adapter *adap);
+
+
+CEC framework functions
+-----------------------
+
+CEC Adapter drivers can call the following CEC framework functions:
+
+int cec_transmit_msg(struct cec_adapter *adap, struct cec_msg *msg,
+		     bool block);
+
+Transmit a CEC message. If block is true, then wait until the message has been
+transmitted, otherwise just queue it and return.
+
+int cec_claim_log_addrs(struct cec_adapter *adap,
+			struct cec_log_addrs *log_addrs, bool block);
+
+Claim the CEC logical addresses. Should never be called if CEC_CAP_LOG_ADDRS
+is set. If block is true, then wait until the logical addresses have been
+claimed, otherwise just queue it and return.
+
+int cec_enable(struct cec_adapter *adap, bool enable);
+
+Enable or disable the CEC adapter. HDMI transmitters will typically disable
+the adapter when the hotplug signal goes down and enable it after it went up
+again and the EDID was read containing the new physical address. Should never
+be called if CEC_CAP_STATE is set.
+
+u8 cec_sink_cdc_hpd(struct cec_adapter *adap, u8 input_port, u8 cdc_hpd_state);
+
+If an HDMI receiver supports hotplug signalling over CDC (CEC_CAP_CDC_HPD is
+set), then the driver should call this function whenever the hotplug state
+changes for an input. This call will send an appropriate CDC message over
+the CEC line. It returns CEC_OP_HPD_ERROR_NONE on success, if the adapter
+is unconfigured it returns CEC_OP_HPD_ERROR_INITIATOR_WRONG_STATE and if
+the cec_transmit fails it returns CEC_OP_HPD_ERROR_OTHER.
+
+void cec_log_status(struct cec_adapter *adap);
+
+This logs the current CEC adapter status in the kernel log. Useful for
+debugging and implementing the V4L2 VIDIOC_LOG_STATUS ioctl.
Signature · 1 line
2.6.2
#10 lore raw

PATCH v10 10/16 DocBook/media: add CEC documentation

In reply to Hans Verkuil

From: Hans Verkuil <[email protected]>

Add DocBook documentation for the CEC API.

[[email protected]: add documentation for passthrough mode]
[[email protected]: minor fixes and change of reserved field sizes]
---
 Documentation/DocBook/device-drivers.tmpl          |   3 +
 Documentation/DocBook/media/Makefile               |   2 +
 Documentation/DocBook/media/v4l/biblio.xml         |  10 +
 Documentation/DocBook/media/v4l/cec-api.xml        |  76 +++++
 Documentation/DocBook/media/v4l/cec-func-close.xml |  59 ++++
 Documentation/DocBook/media/v4l/cec-func-ioctl.xml |  73 +++++
 Documentation/DocBook/media/v4l/cec-func-open.xml  |  94 +++++++
 Documentation/DocBook/media/v4l/cec-func-poll.xml  |  89 ++++++
 .../DocBook/media/v4l/cec-ioc-adap-g-caps.xml      | 167 +++++++++++
 .../DocBook/media/v4l/cec-ioc-adap-g-log-addrs.xml | 306 +++++++++++++++++++++
 .../DocBook/media/v4l/cec-ioc-adap-g-phys-addr.xml |  80 ++++++
 .../DocBook/media/v4l/cec-ioc-adap-g-state.xml     |  87 ++++++
 .../DocBook/media/v4l/cec-ioc-adap-g-vendor-id.xml |  70 +++++
 Documentation/DocBook/media/v4l/cec-ioc-claim.xml  |  71 +++++
 .../DocBook/media/v4l/cec-ioc-dqevent.xml          | 208 ++++++++++++++
 .../DocBook/media/v4l/cec-ioc-g-monitor.xml        |  86 ++++++
 .../DocBook/media/v4l/cec-ioc-g-passthrough.xml    |  81 ++++++
 .../DocBook/media/v4l/cec-ioc-receive.xml          | 190 +++++++++++++
 Documentation/DocBook/media_api.tmpl               |   6 +-
 19 files changed, 1757 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/DocBook/media/v4l/cec-api.xml
 create mode 100644 Documentation/DocBook/media/v4l/cec-func-close.xml
 create mode 100644 Documentation/DocBook/media/v4l/cec-func-ioctl.xml
 create mode 100644 Documentation/DocBook/media/v4l/cec-func-open.xml
 create mode 100644 Documentation/DocBook/media/v4l/cec-func-poll.xml
 create mode 100644 Documentation/DocBook/media/v4l/cec-ioc-adap-g-caps.xml
 create mode 100644 Documentation/DocBook/media/v4l/cec-ioc-adap-g-log-addrs.xml
 create mode 100644 Documentation/DocBook/media/v4l/cec-ioc-adap-g-phys-addr.xml
 create mode 100644 Documentation/DocBook/media/v4l/cec-ioc-adap-g-state.xml
 create mode 100644 Documentation/DocBook/media/v4l/cec-ioc-adap-g-vendor-id.xml
 create mode 100644 Documentation/DocBook/media/v4l/cec-ioc-claim.xml
 create mode 100644 Documentation/DocBook/media/v4l/cec-ioc-dqevent.xml
 create mode 100644 Documentation/DocBook/media/v4l/cec-ioc-g-monitor.xml
 create mode 100644 Documentation/DocBook/media/v4l/cec-ioc-g-passthrough.xml
 create mode 100644 Documentation/DocBook/media/v4l/cec-ioc-receive.xml
Documentation/DocBook/device-drivers.tmpl +3 -0
index 42a2d85..fade76c 100644
@@ -319,6 +319,9 @@ X!Isound/sound_firmware.c
 !Iinclude/media/media-devnode.h
 !Iinclude/media/media-entity.h
     </sect1>
+     <sect1><title>Consumer Electronics Control devices</title>
+!Iinclude/media/cec.h
+     </sect1>
 
   </chapter>
 
Documentation/DocBook/media/Makefile +2 -0
index 08527e7..b97fb71 100644
@@ -64,6 +64,7 @@ IOCTLS = \
 	$(shell perl -ne 'print "$$1 " if /\#define\s+([A-Z][^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/dvb/net.h) \
 	$(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/dvb/video.h) \
 	$(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/media.h) \
+	$(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/cec.h) \
 	$(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/v4l2-subdev.h) \
 
 DEFINES = \
@@ -100,6 +101,7 @@ STRUCTS = \
 	$(shell perl -ne 'print "$$1 " if (/^struct\s+([^\s]+)\s+/ && !/_old/)' $(srctree)/include/uapi/linux/dvb/net.h) \
 	$(shell perl -ne 'print "$$1 " if (/^struct\s+([^\s]+)\s+/)' $(srctree)/include/uapi/linux/dvb/video.h) \
 	$(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/uapi/linux/media.h) \
+	$(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/uapi/linux/cec.h) \
 	$(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/uapi/linux/v4l2-subdev.h) \
 	$(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/uapi/linux/v4l2-mediabus.h)
 
Documentation/DocBook/media/v4l/biblio.xml +10 -0
index 9beb30f..87f1d24 100644
@@ -342,6 +342,16 @@ in the frequency range from 87,5 to 108,0 MHz</title>
       <subtitle>Specification Version 1.4a</subtitle>
     </biblioentry>
 
+    <biblioentry id="hdmi2">
+      <abbrev>HDMI2</abbrev>
+      <authorgroup>
+	<corpauthor>HDMI Licensing LLC
+(<ulink url="http://www.hdmi.org">http://www.hdmi.org</ulink>)</corpauthor>
+      </authorgroup>
+      <title>High-Definition Multimedia Interface</title>
+      <subtitle>Specification Version 2.0</subtitle>
+    </biblioentry>
+
     <biblioentry id="dp">
       <abbrev>DP</abbrev>
       <authorgroup>
Documentation/DocBook/media/v4l/cec-api.xml new file +76 -0

76 lines not on this page · raw message

Documentation/DocBook/media/v4l/cec-func-close.xml new file +59 -0

59 lines not on this page · raw message

Documentation/DocBook/media/v4l/cec-func-ioctl.xml new file +73 -0

73 lines not on this page · raw message

Documentation/DocBook/media/v4l/cec-func-open.xml new file +94 -0

94 lines not on this page · raw message

Documentation/DocBook/media/v4l/cec-func-poll.xml new file +89 -0

89 lines not on this page · raw message

Documentation/DocBook/media/v4l/cec-ioc-adap-g-caps.xml new file +167 -0

167 lines not on this page · raw message

Documentation/DocBook/media/v4l/cec-ioc-adap-g-log-addrs.xml new file +306 -0

306 lines not on this page · raw message

Documentation/DocBook/media/v4l/cec-ioc-adap-g-phys-addr.xml new file +80 -0

80 lines not on this page · raw message

Documentation/DocBook/media/v4l/cec-ioc-adap-g-state.xml new file +87 -0

87 lines not on this page · raw message

Documentation/DocBook/media/v4l/cec-ioc-adap-g-vendor-id.xml new file +70 -0

70 lines not on this page · raw message

Documentation/DocBook/media/v4l/cec-ioc-claim.xml new file +71 -0

71 lines not on this page · raw message

Documentation/DocBook/media/v4l/cec-ioc-dqevent.xml new file +208 -0

208 lines not on this page · raw message

Documentation/DocBook/media/v4l/cec-ioc-g-monitor.xml new file +86 -0

86 lines not on this page · raw message

Documentation/DocBook/media/v4l/cec-ioc-g-passthrough.xml new file +81 -0

81 lines not on this page · raw message

Documentation/DocBook/media/v4l/cec-ioc-receive.xml new file +190 -0

190 lines not on this page · raw message

Documentation/DocBook/media_api.tmpl +5 -1
index 9203703..3f76d41 100644
@@ -75,7 +75,7 @@
 	    </mediaobject>
 	</figure>
 	<para>The media infrastructure API was designed to control such
-	    devices. It is divided into four parts.</para>
+	    devices. It is divided into five parts.</para>
 	<para>The first part covers radio, video capture and output,
 		cameras, analog TV devices and codecs.</para>
 	<para>The second part covers the
@@ -87,6 +87,7 @@
 		<xref linkend="fe-delivery-system-t" />.</para>
 	<para>The third part covers the Remote Controller API.</para>
 	<para>The fourth part covers the Media Controller API.</para>
+	<para>The fifth part covers the CEC (Consumer Electronics Control) API.</para>
 	<para>It should also be noted that a media device may also have audio
 	      components, like mixers, PCM capture, PCM playback, etc, which
 	      are controlled via ALSA API.</para>
@@ -107,6 +108,9 @@
 <part id="media_common">
 &sub-media-controller;
 </part>
+<part id="cec">
+&sub-cec-api;
+</part>
 
 <chapter id="gen_errors">
 &sub-gen-errors;
Signature · 1 line
2.6.2
#11 lore raw

PATCH v10 11/16 v4l2-subdev: add HDMI CEC ops

In reply to Hans Verkuil

From: Hans Verkuil <[email protected]>

Add CEC callbacks to the new v4l2_subdev_cec_ops struct. These are the low-level CEC ops that subdevs that support CEC have to implement.

[[email protected]: Merged changes from CEC Updates commit by Hans Verkuil]
---
 include/media/v4l2-subdev.h | 14 ++++++++++++++
 1 file changed, 14 insertions(+)
include/media/v4l2-subdev.h +14 -0
index b273cf9..e55031e 100644
@@ -42,6 +42,10 @@
 
 #define	V4L2_DEVICE_NOTIFY_EVENT		_IOW('v', 2, struct v4l2_event)
 
+#define V4L2_SUBDEV_CEC_TX_DONE			_IOW('v', 3, u32)
+#define V4L2_SUBDEV_CEC_RX_MSG			_IOW('v', 4, struct cec_msg)
+#define V4L2_SUBDEV_CEC_CONN_INPUTS		_IOW('v', 5, u16)
+
 struct v4l2_device;
 struct v4l2_ctrl_handler;
 struct v4l2_event;
@@ -51,6 +55,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 {
@@ -642,6 +647,14 @@ struct v4l2_subdev_pad_ops {
 			      struct v4l2_mbus_frame_desc *fd);
 };
 
+struct v4l2_subdev_cec_ops {
+	unsigned (*available_log_addrs)(struct v4l2_subdev *sd);
+	int (*enable)(struct v4l2_subdev *sd, bool enable);
+	int (*log_addr)(struct v4l2_subdev *sd, u8 logical_addr);
+	int (*transmit)(struct v4l2_subdev *sd, u32 timeout_ms,
+			u8 retries, struct cec_msg *msg);
+};
+
 struct v4l2_subdev_ops {
 	const struct v4l2_subdev_core_ops	*core;
 	const struct v4l2_subdev_tuner_ops	*tuner;
@@ -651,6 +664,7 @@ struct v4l2_subdev_ops {
 	const struct v4l2_subdev_ir_ops		*ir;
 	const struct v4l2_subdev_sensor_ops	*sensor;
 	const struct v4l2_subdev_pad_ops	*pad;
+	const struct v4l2_subdev_cec_ops	*cec;
 };
 
 /*
Signature · 1 line
2.6.2
#12 lore raw

PATCH v10 12/16 cec: adv7604: add cec support.

In reply to Hans Verkuil

From: Hans Verkuil <[email protected]>

Add CEC support to the adv7604 driver.

[[email protected]: Merged changes from CEC Updates commit by Hans Verkuil]
[[email protected]: add missing methods cec/io_write_and_or]
[[email protected]: change adv7604 to adv76xx in added functions]
[[email protected]: use _clr_set instead of _and_or]
---
 drivers/media/i2c/adv7604.c | 241 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 237 insertions(+), 4 deletions(-)
drivers/media/i2c/adv7604.c +237 -4
index 5631ec0..f039ae6 100644
@@ -40,6 +40,7 @@
 #include <linux/regmap.h>
 
 #include <media/adv7604.h>
+#include <media/cec.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-event.h>
@@ -80,6 +81,8 @@ MODULE_LICENSE("GPL");
 
 #define ADV76XX_OP_SWAP_CB_CR				(1 << 0)
 
+#define ADV76XX_MAX_ADDRS (3)
+
 enum adv76xx_type {
 	ADV7604,
 	ADV7611,
@@ -184,6 +187,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;
@@ -430,7 +437,8 @@ static inline int io_write(struct v4l2_subdev *sd, u8 reg, u8 val)
 	return regmap_write(state->regmap[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_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask,
+				   u8 val)
 {
 	return io_write(sd, reg, (io_read(sd, reg) & ~mask) | val);
 }
@@ -463,6 +471,12 @@ static inline int cec_write(struct v4l2_subdev *sd, u8 reg, u8 val)
 	return regmap_write(state->regmap[ADV76XX_PAGE_CEC], reg, val);
 }
 
+static inline int cec_write_clr_set(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);
@@ -550,8 +564,13 @@ static inline int edid_write_block(struct v4l2_subdev *sd,
 
 static void adv76xx_set_hpd(struct adv76xx_state *state, unsigned int hpd)
 {
+	u16 connected_inputs;
 	unsigned int i;
 
+	connected_inputs = hpd & state->info->read_cable_det(&state->sd);
+	v4l2_subdev_notify(&state->sd, V4L2_SUBDEV_CEC_CONN_INPUTS,
+			   &connected_inputs);
+
 	for (i = 0; i < state->info->num_dv_ports; ++i)
 		gpiod_set_value_cansleep(state->hpd_gpio[i], hpd & BIT(i));
 
@@ -891,9 +910,12 @@ static int adv76xx_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd)
 {
 	struct adv76xx_state *state = to_state(sd);
 	const struct adv76xx_chip_info *info = state->info;
+	u16 cable_det = info->read_cable_det(sd);
+	u16 connected_inputs = state->edid.present & cable_det;
 
-	return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl,
-				info->read_cable_det(sd));
+	v4l2_subdev_notify(sd, V4L2_SUBDEV_CEC_CONN_INPUTS,
+			   &connected_inputs);
+	return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, cable_det);
 }
 
 static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd,
@@ -1914,6 +1936,187 @@ 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)
+{
+	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) {
+		struct cec_msg msg;
+
+		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 unsigned adv76xx_cec_available_log_addrs(struct v4l2_subdev *sd)
+{
+	return ADV76XX_MAX_ADDRS;
+}
+
+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_clr_set(sd, 0x2a, 0x01, 0x01);	/* power up cec */
+		cec_write(sd, 0x2c, 0x01);	/* cec soft reset */
+		cec_write_clr_set(sd, 0x11, 0x01, 0);  /* initially disable tx */
+		/* enabled irqs: */
+		/* tx: ready */
+		/* tx: arbitration lost */
+		/* tx: retry timeout */
+		/* rx: ready */
+		io_write_clr_set(sd, 0x50, 0x0f, 0x0f);
+		cec_write(sd, 0x26, 0x01);            /* enable rx */
+	} else if (state->cec_enabled_adap && !enable) {
+		/* disable cec interrupts */
+		io_write_clr_set(sd, 0x50, 0x0f, 0x00);
+		/* disable address mask 1-3 */
+		cec_write_clr_set(sd, 0x27, 0x70, 0x00);
+		/* power down cec section */
+		cec_write_clr_set(sd, 0x2a, 0x01, 0x00);
+		state->cec_valid_addrs = 0;
+	}
+	state->cec_enabled_adap = enable;
+	adv76xx_s_detect_tx_5v_ctrl(sd);
+	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;
+
+	if (addr == CEC_LOG_ADDR_INVALID) {
+		cec_write_clr_set(sd, 0x27, 0x70, 0);
+		state->cec_valid_addrs = 0;
+		return 0;
+	}
+
+	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_clr_set(sd, 0x27, 0x10, 0x10);
+		/* set address for mask 0 */
+		cec_write_clr_set(sd, 0x28, 0x0f, addr);
+		break;
+	case 1:
+		/* enable address mask 1 */
+		cec_write_clr_set(sd, 0x27, 0x20, 0x20);
+		/* set address for mask 1 */
+		cec_write_clr_set(sd, 0x28, 0xf0, addr << 4);
+		break;
+	case 2:
+		/* enable address mask 2 */
+		cec_write_clr_set(sd, 0x27, 0x40, 0x40);
+		/* set address for mask 1 */
+		cec_write_clr_set(sd, 0x29, 0x0f, addr);
+		break;
+	}
+	return 0;
+}
+
+static int adv76xx_cec_transmit(struct v4l2_subdev *sd, u32 timeout_ms,
+				u8 retries, struct cec_msg *msg)
+{
+	u8 len = msg->len;
+	unsigned i;
+
+	if (len == 1)
+		/* allow for one retry for polling */
+		cec_write_clr_set(sd, 0x12, 0x07, 1);
+	else
+		/* allow for three retries */
+		cec_write_clr_set(sd, 0x12, 0x07, 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);
+	return 0;
+}
+
 static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
 {
 	struct adv76xx_state *state = to_state(sd);
@@ -1959,6 +2162,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) {
@@ -2266,8 +2472,19 @@ static int adv76xx_log_status(struct v4l2_subdev *sd)
 			((edid_enabled & 0x02) ? "Yes" : "No"),
 			((edid_enabled & 0x04) ? "Yes" : "No"),
 			((edid_enabled & 0x08) ? "Yes" : "No"));
-	v4l2_info(sd, "CEC: %s\n", !!(cec_read(sd, 0x2a) & 0x01) ?
+	v4l2_info(sd, "CEC: %s\n", state->cec_enabled_adap ?
 			"enabled" : "disabled");
+	if (state->cec_enabled_adap) {
+		int i;
+
+		for (i = 0; i < ADV76XX_MAX_ADDRS; i++) {
+			bool is_valid = state->cec_valid_addrs & (1 << i);
+
+			if (is_valid)
+				v4l2_info(sd, "CEC Logical Address: 0x%x\n",
+					  state->cec_addr[i]);
+		}
+	}
 
 	v4l2_info(sd, "-----Signal status-----\n");
 	cable_det = info->read_cable_det(sd);
@@ -2402,6 +2619,13 @@ static const struct v4l2_subdev_video_ops adv76xx_video_ops = {
 	.query_dv_timings = adv76xx_query_dv_timings,
 };
 
+static const struct v4l2_subdev_cec_ops adv76xx_cec_ops = {
+	.available_log_addrs = adv76xx_cec_available_log_addrs,
+	.enable = adv76xx_cec_enable,
+	.log_addr = adv76xx_cec_log_addr,
+	.transmit = adv76xx_cec_transmit,
+};
+
 static const struct v4l2_subdev_pad_ops adv76xx_pad_ops = {
 	.enum_mbus_code = adv76xx_enum_mbus_code,
 	.get_fmt = adv76xx_get_format,
@@ -2416,6 +2640,7 @@ static const struct v4l2_subdev_ops adv76xx_ops = {
 	.core = &adv76xx_core_ops,
 	.video = &adv76xx_video_ops,
 	.pad = &adv76xx_pad_ops,
+	.cec = &adv76xx_cec_ops,
 };
 
 /* -------------------------- custom ctrls ---------------------------------- */
@@ -3249,6 +3474,14 @@ static int adv76xx_remove(struct i2c_client *client)
 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
 	struct adv76xx_state *state = to_state(sd);
 
+	/* disable interrupts */
+	io_write(sd, 0x40, 0);
+	io_write(sd, 0x41, 0);
+	io_write(sd, 0x46, 0);
+	io_write(sd, 0x6e, 0);
+	io_write(sd, 0x73, 0);
+	adv76xx_cec_enable(sd, false);
+
 	cancel_delayed_work(&state->delayed_work_enable_hotplug);
 	destroy_workqueue(state->work_queues);
 	v4l2_async_unregister_subdev(sd);
Signature · 1 line
2.6.2
#13 lore raw

PATCH v10 13/16 cec: adv7842: add cec support

In reply to Hans Verkuil

From: Hans Verkuil <[email protected]>

Add CEC support to the adv7842 driver.

---
 drivers/media/i2c/adv7842.c | 255 +++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 238 insertions(+), 17 deletions(-)
drivers/media/i2c/adv7842.c +238 -17
index b7269b8..0f29b33 100644
@@ -39,6 +39,7 @@
 #include <linux/workqueue.h>
 #include <linux/v4l2-dv-timings.h>
 #include <linux/hdmi.h>
+#include <media/cec.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-event.h>
 #include <media/v4l2-ctrls.h>
@@ -79,6 +80,8 @@ MODULE_LICENSE("GPL");
 
 #define ADV7842_OP_SWAP_CB_CR				(1 << 0)
 
+#define ADV7842_MAX_ADDRS (3)
+
 /*
 **********************************************************************
 *
@@ -142,6 +145,10 @@ struct adv7842_state {
 	struct v4l2_ctrl *free_run_color_ctrl_manual;
 	struct v4l2_ctrl *free_run_color_ctrl;
 	struct v4l2_ctrl *rgb_quantization_range_ctrl;
+
+	u8   cec_addr[ADV7842_MAX_ADDRS];
+	u8   cec_valid_addrs;
+	bool cec_enabled_adap;
 };
 
 /* Unsupported timings. This device cannot support 720p30. */
@@ -418,9 +425,9 @@ static inline int cec_write(struct v4l2_subdev *sd, u8 reg, u8 val)
 	return adv_smbus_write_byte_data(state->i2c_cec, reg, val);
 }
 
-static inline int cec_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
+static inline int cec_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
 {
-	return cec_write(sd, reg, (cec_read(sd, reg) & mask) | val);
+	return cec_write(sd, reg, (cec_read(sd, reg) & ~mask) | val);
 }
 
 static inline int infoframe_read(struct v4l2_subdev *sd, u8 reg)
@@ -696,6 +703,18 @@ adv7842_get_dv_timings_cap(struct v4l2_subdev *sd)
 
 /* ----------------------------------------------------------------------- */
 
+static u16 adv7842_read_cable_det(struct v4l2_subdev *sd)
+{
+	u8 reg = io_read(sd, 0x6f);
+	u16 val = 0;
+
+	if (reg & 0x02)
+		val |= 1; /* port A */
+	if (reg & 0x01)
+		val |= 2; /* port B */
+	return val;
+}
+
 static void adv7842_delayed_work_enable_hotplug(struct work_struct *work)
 {
 	struct delayed_work *dwork = to_delayed_work(work);
@@ -703,8 +722,13 @@ static void adv7842_delayed_work_enable_hotplug(struct work_struct *work)
 			struct adv7842_state, delayed_work_enable_hotplug);
 	struct v4l2_subdev *sd = &state->sd;
 	int present = state->hdmi_edid.present;
+	u16 connected_inputs;
 	u8 mask = 0;
 
+	connected_inputs = present & adv7842_read_cable_det(sd);
+	v4l2_subdev_notify(sd, V4L2_SUBDEV_CEC_CONN_INPUTS,
+			   &connected_inputs);
+
 	v4l2_dbg(2, debug, sd, "%s: enable hotplug on ports: 0x%x\n",
 			__func__, present);
 
@@ -983,20 +1007,16 @@ static int adv7842_s_register(struct v4l2_subdev *sd,
 static int adv7842_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd)
 {
 	struct adv7842_state *state = to_state(sd);
-	int prev = v4l2_ctrl_g_ctrl(state->detect_tx_5v_ctrl);
-	u8 reg_io_6f = io_read(sd, 0x6f);
-	int val = 0;
+	u16 cable_det = adv7842_read_cable_det(sd);
+	u16 connected_inputs;
 
-	if (reg_io_6f & 0x02)
-		val |= 1; /* port A */
-	if (reg_io_6f & 0x01)
-		val |= 2; /* port B */
+	v4l2_dbg(1, debug, sd, "%s: 0x%x\n", __func__, cable_det);
 
-	v4l2_dbg(1, debug, sd, "%s: 0x%x -> 0x%x\n", __func__, prev, val);
+	connected_inputs = state->hdmi_edid.present & cable_det;
+	v4l2_subdev_notify(sd, V4L2_SUBDEV_CEC_CONN_INPUTS,
+			   &connected_inputs);
 
-	if (val != prev)
-		return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, val);
-	return 0;
+	return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, cable_det);
 }
 
 static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd,
@@ -2157,6 +2177,183 @@ static void adv7842_irq_enable(struct v4l2_subdev *sd, bool enable)
 	}
 }
 
+static void adv7842_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 adv7842_cec_isr(struct v4l2_subdev *sd, bool *handled)
+{
+	u8 cec_irq;
+
+	/* cec controller */
+	cec_irq = io_read(sd, 0x93) & 0x0f;
+	if (!cec_irq)
+		return;
+
+	v4l2_dbg(1, debug, sd, "%s: cec: irq 0x%x\n", __func__, cec_irq);
+	adv7842_cec_tx_raw_status(sd, cec_irq);
+	if (cec_irq & 0x08) {
+		struct cec_msg msg;
+
+		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);
+		}
+	}
+
+	io_write(sd, 0x94, cec_irq);
+
+	if (handled)
+		*handled = true;
+}
+
+static unsigned adv7842_cec_available_log_addrs(struct v4l2_subdev *sd)
+{
+	return ADV7842_MAX_ADDRS;
+}
+
+static int adv7842_cec_enable(struct v4l2_subdev *sd, bool enable)
+{
+	struct adv7842_state *state = to_state(sd);
+
+	if (!state->cec_enabled_adap && enable) {
+		cec_write_clr_set(sd, 0x2a, 0x01, 0x01);	/* power up cec */
+		cec_write(sd, 0x2c, 0x01);	/* cec soft reset */
+		cec_write_clr_set(sd, 0x11, 0x01, 0);  /* initially disable tx */
+		/* enabled irqs: */
+		/* tx: ready */
+		/* tx: arbitration lost */
+		/* tx: retry timeout */
+		/* rx: ready */
+		io_write_clr_set(sd, 0x96, 0x0f, 0x0f);
+		cec_write(sd, 0x26, 0x01);            /* enable rx */
+	} else if (state->cec_enabled_adap && !enable) {
+		/* disable cec interrupts */
+		io_write_clr_set(sd, 0x96, 0x0f, 0x00);
+		/* disable address mask 1-3 */
+		cec_write_clr_set(sd, 0x27, 0x70, 0x00);
+		/* power down cec section */
+		cec_write_clr_set(sd, 0x2a, 0x01, 0x00);
+		state->cec_valid_addrs = 0;
+	}
+	state->cec_enabled_adap = enable;
+	return 0;
+}
+
+static int adv7842_cec_log_addr(struct v4l2_subdev *sd, u8 addr)
+{
+	struct adv7842_state *state = to_state(sd);
+	unsigned i, free_idx = ADV7842_MAX_ADDRS;
+
+	if (!state->cec_enabled_adap)
+		return -EIO;
+
+	if (addr == CEC_LOG_ADDR_INVALID) {
+		cec_write_clr_set(sd, 0x27, 0x70, 0);
+		state->cec_valid_addrs = 0;
+		return 0;
+	}
+
+	for (i = 0; i < ADV7842_MAX_ADDRS; i++) {
+		bool is_valid = state->cec_valid_addrs & (1 << i);
+
+		if (free_idx == ADV7842_MAX_ADDRS && !is_valid)
+			free_idx = i;
+		if (is_valid && state->cec_addr[i] == addr)
+			return 0;
+	}
+	if (i == ADV7842_MAX_ADDRS) {
+		i = free_idx;
+		if (i == ADV7842_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_clr_set(sd, 0x27, 0x10, 0x10);
+		/* set address for mask 0 */
+		cec_write_clr_set(sd, 0x28, 0x0f, addr);
+		break;
+	case 1:
+		/* enable address mask 1 */
+		cec_write_clr_set(sd, 0x27, 0x20, 0x20);
+		/* set address for mask 1 */
+		cec_write_clr_set(sd, 0x28, 0xf0, addr << 4);
+		break;
+	case 2:
+		/* enable address mask 2 */
+		cec_write_clr_set(sd, 0x27, 0x40, 0x40);
+		/* set address for mask 1 */
+		cec_write_clr_set(sd, 0x29, 0x0f, addr);
+		break;
+	}
+	return 0;
+}
+
+static int adv7842_cec_transmit(struct v4l2_subdev *sd, u32 timeout_ms,
+				u8 retries, struct cec_msg *msg)
+{
+	u8 len = msg->len;
+	unsigned i;
+
+	if (len == 1)
+		/* allow for one retry for polling */
+		cec_write_clr_set(sd, 0x12, 0x07, 1);
+	else
+		/* allow for three retries */
+		cec_write_clr_set(sd, 0x12, 0x07, 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);
+	return 0;
+}
+
 static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
 {
 	struct adv7842_state *state = to_state(sd);
@@ -2228,6 +2425,9 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
 			*handled = true;
 	}
 
+	/* cec */
+	adv7842_cec_isr(sd, handled);
+
 	/* tx 5v detect */
 	if (irq_status[2] & 0x3) {
 		v4l2_dbg(1, debug, sd, "%s: irq tx_5v\n", __func__);
@@ -2308,10 +2508,12 @@ static int adv7842_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *e)
 	case ADV7842_EDID_PORT_A:
 	case ADV7842_EDID_PORT_B:
 		memset(&state->hdmi_edid.edid, 0, 256);
-		if (e->blocks)
+		if (e->blocks) {
 			state->hdmi_edid.present |= 0x04 << e->pad;
-		else
+		} else {
 			state->hdmi_edid.present &= ~(0x04 << e->pad);
+			adv7842_s_detect_tx_5v_ctrl(sd);
+		}
 		memcpy(&state->hdmi_edid.edid, e->edid, 128 * e->blocks);
 		err = edid_write_hdmi_segment(sd, e->pad);
 		break;
@@ -2496,8 +2698,19 @@ static int adv7842_cp_log_status(struct v4l2_subdev *sd)
 	v4l2_info(sd, "HPD A %s, B %s\n",
 		  reg_io_0x21 & 0x02 ? "enabled" : "disabled",
 		  reg_io_0x21 & 0x01 ? "enabled" : "disabled");
-	v4l2_info(sd, "CEC %s\n", !!(cec_read(sd, 0x2a) & 0x01) ?
+	v4l2_info(sd, "CEC: %s\n", state->cec_enabled_adap ?
 			"enabled" : "disabled");
+	if (state->cec_enabled_adap) {
+		int i;
+
+		for (i = 0; i < ADV7842_MAX_ADDRS; i++) {
+			bool is_valid = state->cec_valid_addrs & (1 << i);
+
+			if (is_valid)
+				v4l2_info(sd, "CEC Logical Address: 0x%x\n",
+					  state->cec_addr[i]);
+		}
+	}
 
 	v4l2_info(sd, "-----Signal status-----\n");
 	if (state->hdmi_port_a) {
@@ -3047,6 +3260,13 @@ static const struct v4l2_subdev_video_ops adv7842_video_ops = {
 	.query_dv_timings = adv7842_query_dv_timings,
 };
 
+static const struct v4l2_subdev_cec_ops adv7842_cec_ops = {
+	.available_log_addrs = adv7842_cec_available_log_addrs,
+	.enable = adv7842_cec_enable,
+	.log_addr = adv7842_cec_log_addr,
+	.transmit = adv7842_cec_transmit,
+};
+
 static const struct v4l2_subdev_pad_ops adv7842_pad_ops = {
 	.enum_mbus_code = adv7842_enum_mbus_code,
 	.get_fmt = adv7842_get_format,
@@ -3061,6 +3281,7 @@ static const struct v4l2_subdev_ops adv7842_ops = {
 	.core = &adv7842_core_ops,
 	.video = &adv7842_video_ops,
 	.pad = &adv7842_pad_ops,
+	.cec = &adv7842_cec_ops,
 };
 
 /* -------------------------- custom ctrls ---------------------------------- */
@@ -3341,7 +3562,7 @@ static int adv7842_remove(struct i2c_client *client)
 	struct adv7842_state *state = to_state(sd);
 
 	adv7842_irq_enable(sd, false);
-
+	adv7842_cec_enable(sd, false);
 	cancel_delayed_work(&state->delayed_work_enable_hotplug);
 	destroy_workqueue(state->work_queues);
 	v4l2_device_unregister_subdev(sd);
Signature · 1 line
2.6.2
#14 lore raw

PATCH v10 14/16 cec: adv7511: add cec support.

In reply to Hans Verkuil

From: Hans Verkuil <[email protected]>

Add CEC support to the adv7511 driver.

[[email protected]: Merged changes from CEC Updates commit by Hans Verkuil]
---
 drivers/media/i2c/adv7511.c | 364 +++++++++++++++++++++++++++++++++++++++++++-
 include/media/adv7511.h     |   6 +-
 2 files changed, 358 insertions(+), 12 deletions(-)
drivers/media/i2c/adv7511.c +357 -7
index e4900df..dee73a6 100644
@@ -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);
@@ -59,6 +60,8 @@ MODULE_LICENSE("GPL v2");
 #define ADV7511_MIN_PIXELCLOCK 20000000
 #define ADV7511_MAX_PIXELCLOCK 225000000
 
+#define ADV7511_MAX_ADDRS (3)
+
 /*
 **********************************************************************
 *
@@ -90,12 +93,19 @@ struct adv7511_state {
 	struct v4l2_ctrl_handler hdl;
 	int chip_revision;
 	u8 i2c_edid_addr;
-	u8 i2c_cec_addr;
 	u8 i2c_pktmem_addr;
+	u8 i2c_cec_addr;
+
+	struct i2c_client *i2c_cec;
+	u8   cec_addr[ADV7511_MAX_ADDRS];
+	u8   cec_valid_addrs;
+	bool cec_enabled_adap;
+
 	/* Is the adv7511 powered on? */
 	bool power_on;
 	/* Did we receive hotplug and rx-sense signals? */
 	bool have_monitor;
+	bool enabled_irq;
 	/* timings from s_dv_timings */
 	struct v4l2_dv_timings dv_timings;
 	u32 fmt_code;
@@ -225,7 +235,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, u16 len, u8 *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;
@@ -240,6 +250,34 @@ static inline void adv7511_edid_rd(struct v4l2_subdev *sd, u16 len, u8 *buf)
 		v4l2_err(sd, "%s: i2c read error\n", __func__);
 }
 
+static inline int adv7511_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 adv7511_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 adv7511_cec_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask,
+				   u8 val)
+{
+	return adv7511_cec_write(sd, reg, (adv7511_cec_read(sd, reg) & mask) | val);
+}
+
 static int adv7511_pktmem_rd(struct v4l2_subdev *sd, u8 reg)
 {
 	struct adv7511_state *state = get_adv7511_state(sd);
@@ -413,16 +451,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 = adv7511_cec_read(sd, reg->reg & 0xff);
+			break;
+		}
+		/* fall through */
 	default:
 		v4l2_info(sd, "Register %03llx not supported\n", reg->reg);
 		adv7511_inv_register(sd);
@@ -433,10 +483,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) {
+			adv7511_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);
@@ -524,6 +582,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",
@@ -593,7 +652,23 @@ 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);
+
+	v4l2_info(sd, "CEC: %s\n", state->cec_enabled_adap ?
+			"enabled" : "disabled");
+	if (state->cec_enabled_adap) {
+		for (i = 0; i < ADV7511_MAX_ADDRS; i++) {
+			bool is_valid = state->cec_valid_addrs & (1 << i);
+
+			if (is_valid)
+				v4l2_info(sd, "CEC Logical Address: 0x%x\n",
+					  state->cec_addr[i]);
+		}
+	}
 	v4l2_info(sd, "i2c pktmem addr: 0x%x\n", state->i2c_pktmem_addr);
 	return 0;
 }
@@ -651,15 +726,139 @@ static int adv7511_s_power(struct v4l2_subdev *sd, int on)
 	return true;
 }
 
+static unsigned adv7511_cec_available_log_addrs(struct v4l2_subdev *sd)
+{
+	return ADV7511_MAX_ADDRS;
+}
+
+static int adv7511_cec_enable(struct v4l2_subdev *sd, bool enable)
+{
+	struct adv7511_state *state = get_adv7511_state(sd);
+
+	if (state->i2c_cec == NULL)
+		return -EIO;
+
+	if (!state->cec_enabled_adap && enable) {
+		/* power up cec section */
+		adv7511_cec_write_and_or(sd, 0x4e, 0xfc, 0x01);
+		/* legacy mode and clear all rx buffers */
+		adv7511_cec_write(sd, 0x4a, 0x07);
+		adv7511_cec_write(sd, 0x4a, 0);
+		adv7511_cec_write_and_or(sd, 0x11, 0xfe, 0); /* initially disable tx */
+		/* enabled irqs: */
+		/* tx: ready */
+		/* tx: arbitration lost */
+		/* tx: retry timeout */
+		/* rx: ready 1 */
+		if (state->enabled_irq)
+			adv7511_wr_and_or(sd, 0x95, 0xc0, 0x39);
+	} else if (state->cec_enabled_adap && !enable) {
+		if (state->enabled_irq)
+			adv7511_wr_and_or(sd, 0x95, 0xc0, 0x00);
+		/* disable address mask 1-3 */
+		adv7511_cec_write_and_or(sd, 0x4b, 0x8f, 0x00);
+		/* power down cec section */
+		adv7511_cec_write_and_or(sd, 0x4e, 0xfc, 0x00);
+		state->cec_valid_addrs = 0;
+	}
+	state->cec_enabled_adap = enable;
+	return 0;
+}
+
+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;
+
+	if (addr == CEC_LOG_ADDR_INVALID) {
+		adv7511_cec_write_and_or(sd, 0x4b, 0x8f, 0);
+		state->cec_valid_addrs = 0;
+		return 0;
+	}
+
+	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 */
+		adv7511_cec_write_and_or(sd, 0x4b, 0xef, 0x10);
+		/* set address for mask 0 */
+		adv7511_cec_write_and_or(sd, 0x4c, 0xf0, addr);
+		break;
+	case 1:
+		/* enable address mask 1 */
+		adv7511_cec_write_and_or(sd, 0x4b, 0xdf, 0x20);
+		/* set address for mask 1 */
+		adv7511_cec_write_and_or(sd, 0x4c, 0x0f, addr << 4);
+		break;
+	case 2:
+		/* enable address mask 2 */
+		adv7511_cec_write_and_or(sd, 0x4b, 0xbf, 0x40);
+		/* set address for mask 1 */
+		adv7511_cec_write_and_or(sd, 0x4d, 0xf0, addr);
+		break;
+	}
+	return 0;
+}
+
+static int adv7511_cec_transmit(struct v4l2_subdev *sd, u32 timeout_ms,
+				u8 retries, 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++)
+		adv7511_cec_write(sd, i, msg->msg[i]);
+
+	/* set length (data + header) */
+	adv7511_cec_write(sd, 0x10, len);
+	/* start transmit, enable tx */
+	adv7511_cec_write(sd, 0x11, 0x01);
+	return 0;
+}
+
 /* Enable interrupts */
 static void adv7511_set_isr(struct v4l2_subdev *sd, bool enable)
 {
+	struct adv7511_state *state = get_adv7511_state(sd);
 	u8 irqs = MASK_ADV7511_HPD_INT | MASK_ADV7511_MSEN_INT;
 	u8 irqs_rd;
 	int retries = 100;
 
 	v4l2_dbg(2, debug, sd, "%s: %s\n", __func__, enable ? "enable" : "disable");
 
+	if (state->enabled_irq == enable)
+		return;
+	state->enabled_irq = enable;
+
 	/* The datasheet says that the EDID ready interrupt should be
 	   disabled if there is no hotplug. */
 	if (!enable)
@@ -667,6 +866,9 @@ static void adv7511_set_isr(struct v4l2_subdev *sd, bool enable)
 	else if (adv7511_have_hotplug(sd))
 		irqs |= MASK_ADV7511_EDID_RDY_INT;
 
+	if (state->cec_enabled_adap)
+		adv7511_wr_and_or(sd, 0x95, 0xc0, enable ? 0x39 : 0x00);
+
 	/*
 	 * This i2c write can fail (approx. 1 in 1000 writes). But it
 	 * is essential that this register is correct, so retry it
@@ -685,24 +887,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 ((adv7511_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)
 {
 	u8 irq_status;
+	u8 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 = adv7511_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] = adv7511_cec_read(sd, i + 0x15);
+
+			adv7511_cec_write(sd, 0x4a, 1); /* toggle to re-enable rx 1 */
+			adv7511_cec_write(sd, 0x4a, 0);
+			v4l2_subdev_notify(sd, V4L2_SUBDEV_CEC_RX_MSG, &msg);
+		}
+	}
+
 	/* enable interrupts */
 	adv7511_set_isr(sd, true);
 
@@ -808,6 +1068,13 @@ static const struct v4l2_subdev_video_ops adv7511_video_ops = {
 	.g_dv_timings = adv7511_g_dv_timings,
 };
 
+static const struct v4l2_subdev_cec_ops adv7511_cec_ops = {
+	.available_log_addrs = adv7511_cec_available_log_addrs,
+	.enable = adv7511_cec_enable,
+	.log_addr = adv7511_cec_log_addr,
+	.transmit = adv7511_cec_transmit,
+};
+
 /* ------------------------------ AUDIO OPS ------------------------------ */
 static int adv7511_s_audio_stream(struct v4l2_subdev *sd, int enable)
 {
@@ -1138,6 +1405,7 @@ static const struct v4l2_subdev_ops adv7511_ops = {
 	.pad  = &adv7511_pad_ops,
 	.video = &adv7511_video_ops,
 	.audio = &adv7511_audio_ops,
+	.cec  = &adv7511_cec_ops,
 };
 
 /* ----------------------------------------------------------------------- */
@@ -1193,6 +1461,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__);
 }
@@ -1333,10 +1602,36 @@ 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);
 	u8 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);
@@ -1382,6 +1677,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
@@ -1403,11 +1704,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,
@@ -1419,6 +1723,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 */
+	adv7511_cec_write(sd, 0x50, 0x01);
+	adv7511_cec_write(sd, 0x50, 0x00);
+
+	/* legacy mode */
+	adv7511_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;
+	adv7511_cec_write(sd, 0x4e, ratio << 2);
 }
 
 static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *id)
@@ -1495,26 +1818,47 @@ 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, 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->i2c_pktmem = i2c_new_dummy(client->adapter, state->i2c_pktmem_addr >> 1);
 	if (state->i2c_pktmem == NULL) {
 		v4l2_err(sd, "failed to register pktmem i2c client\n");
 		err = -ENOMEM;
-		goto err_unreg_edid;
+		goto err_unreg_cec;
 	}
 
-	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");
@@ -1534,6 +1878,9 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *
 
 err_unreg_pktmem:
 	i2c_unregister_device(state->i2c_pktmem);
+err_unreg_cec:
+	if (state->i2c_cec)
+		i2c_unregister_device(state->i2c_cec);
 err_unreg_edid:
 	i2c_unregister_device(state->i2c_edid);
 err_entity:
@@ -1555,9 +1902,12 @@ static int adv7511_remove(struct i2c_client *client)
 	v4l2_dbg(1, debug, sd, "%s removed @ 0x%x (%s)\n", client->name,
 		 client->addr << 1, client->adapter->name);
 
+	adv7511_set_isr(sd, false);
 	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);
 	i2c_unregister_device(state->i2c_pktmem);
 	destroy_workqueue(state->work_queue);
 	v4l2_device_unregister_subdev(sd);
include/media/adv7511.h +1 -5
index d83b91d..61c3d71 100644
@@ -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 {
Signature · 1 line
2.6.2
#15 lore raw

PATCH v10 15/16 cec: s5p-cec: Add s5p-cec driver

In reply to Hans Verkuil

From: Kamil Debski <[email protected]>

Add CEC interface driver present in the Samsung Exynos range of SoCs.

The following files were based on work by SangPil Moon:
- exynos_hdmi_cec.h
- exynos_hdmi_cecctl.c

---
 .../devicetree/bindings/media/s5p-cec.txt          |  31 +++
 drivers/media/platform/Kconfig                     |  12 +
 drivers/media/platform/Makefile                    |   1 +
 drivers/media/platform/s5p-cec/Makefile            |   2 +
 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           | 289 +++++++++++++++++++++
 drivers/media/platform/s5p-cec/s5p_cec.h           |  76 ++++++
 9 files changed, 752 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/s5p-cec.txt
 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
Documentation/devicetree/bindings/media/s5p-cec.txt new file +31 -0
new file mode 100644
index 0000000..925ab4d
@@ -0,0 +1,31 @@
+* Samsung HDMI CEC driver
+
+The HDMI CEC module is present is Samsung SoCs and its purpose is to
+handle communication between HDMI connected devices over the CEC bus.
+
+Required properties:
+  - compatible : value should be following
+	"samsung,s5p-cec"
+
+  - reg : Physical base address of the IP registers and length of memory
+	  mapped region.
+
+  - interrupts : HDMI CEC interrupt number to the CPU.
+  - clocks : from common clock binding: handle to HDMI CEC clock.
+  - clock-names : from common clock binding: must contain "hdmicec",
+		  corresponding to entry in the clocks property.
+  - samsung,syscon-phandle - phandle to the PMU system controller
+
+Example:
+
+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 = "okay";
+};
drivers/media/platform/Kconfig +12 -0
index ccbc974..91794a6 100644
@@ -117,6 +117,18 @@ config VIDEO_S3C_CAMIF
 source "drivers/media/platform/soc_camera/Kconfig"
 source "drivers/media/platform/exynos4-is/Kconfig"
 source "drivers/media/platform/s5p-tv/Kconfig"
+
+config VIDEO_SAMSUNG_S5P_CEC
+	tristate "Samsung S5P CEC driver"
+	depends on VIDEO_DEV && VIDEO_V4L2 && (PLAT_S5P || ARCH_EXYNOS || COMPILE_TEST)
+	select MEDIA_CEC
+	default n
+	---help---
+	  This is a driver for Samsung S5P HDMI CEC interface. It uses the
+	  generic CEC framework interface.
+	  CEC bus is present in the HDMI connector and enables communication
+	  between compatible devices.
+
 source "drivers/media/platform/am437x/Kconfig"
 source "drivers/media/platform/xilinx/Kconfig"
 
drivers/media/platform/Makefile +1 -0
index efa0295..957af5f 100644
@@ -27,6 +27,7 @@ obj-$(CONFIG_VIDEO_MEM2MEM_DEINTERLACE)	+= m2m-deinterlace.o
 
 obj-$(CONFIG_VIDEO_S3C_CAMIF) 		+= s3c-camif/
 obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS4_IS) 	+= exynos4-is/
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_CEC)	+= s5p-cec/
 obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG)	+= s5p-jpeg/
 obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC)	+= s5p-mfc/
 obj-$(CONFIG_VIDEO_SAMSUNG_S5P_TV)	+= s5p-tv/
drivers/media/platform/s5p-cec/Makefile new file +2 -0
new file mode 100644
index 0000000..0e2cf45
@@ -0,0 +1,2 @@
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_CEC)	+= s5p-cec.o
+s5p-cec-y += s5p_cec.o exynos_hdmi_cecctrl.o
drivers/media/platform/s5p-cec/exynos_hdmi_cec.h new file +37 -0
new file mode 100644
index 0000000..d008695
@@ -0,0 +1,37 @@
+/* drivers/media/platform/s5p-cec/exynos_hdmi_cec.h
+ *
+ * Copyright (c) 2010, 2014 Samsung Electronics
+ *		http://www.samsung.com/
+ *
+ * Header file for interface of Samsung Exynos hdmi cec hardware
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _EXYNOS_HDMI_CEC_H_
+#define _EXYNOS_HDMI_CEC_H_ __FILE__
+
+#include <linux/regmap.h>
+#include <linux/miscdevice.h>
+#include "s5p_cec.h"
+
+void s5p_cec_set_divider(struct s5p_cec_dev *cec);
+void s5p_cec_enable_rx(struct s5p_cec_dev *cec);
+void s5p_cec_mask_rx_interrupts(struct s5p_cec_dev *cec);
+void s5p_cec_unmask_rx_interrupts(struct s5p_cec_dev *cec);
+void s5p_cec_mask_tx_interrupts(struct s5p_cec_dev *cec);
+void s5p_cec_unmask_tx_interrupts(struct s5p_cec_dev *cec);
+void s5p_cec_reset(struct s5p_cec_dev *cec);
+void s5p_cec_tx_reset(struct s5p_cec_dev *cec);
+void s5p_cec_rx_reset(struct s5p_cec_dev *cec);
+void s5p_cec_threshold(struct s5p_cec_dev *cec);
+void s5p_cec_copy_packet(struct s5p_cec_dev *cec, char *data, size_t count);
+void s5p_cec_set_addr(struct s5p_cec_dev *cec, u32 addr);
+u32 s5p_cec_get_status(struct s5p_cec_dev *cec);
+void s5p_clr_pending_tx(struct s5p_cec_dev *cec);
+void s5p_clr_pending_rx(struct s5p_cec_dev *cec);
+void s5p_cec_get_rx_buf(struct s5p_cec_dev *cec, u32 size, u8 *buffer);
+
+#endif /* _EXYNOS_HDMI_CEC_H_ */
drivers/media/platform/s5p-cec/exynos_hdmi_cecctrl.c new file +208 -0

208 lines not on this page · raw message

drivers/media/platform/s5p-cec/regs-cec.h new file +96 -0

96 lines not on this page · raw message

drivers/media/platform/s5p-cec/s5p_cec.c new file +289 -0

289 lines not on this page · raw message

drivers/media/platform/s5p-cec/s5p_cec.h new file +76 -0

76 lines not on this page · raw message

Signature · 1 line
2.6.2
#16 lore raw

PATCH v10 16/16 cobalt: add cec support

In reply to Hans Verkuil

From: Hans Verkuil <[email protected]>

Add CEC support to the cobalt driver.

---
 drivers/media/pci/cobalt/Kconfig         |   1 +
 drivers/media/pci/cobalt/cobalt-driver.c | 108 +++++++++++++++++++++-----
 drivers/media/pci/cobalt/cobalt-driver.h |   2 +
 drivers/media/pci/cobalt/cobalt-irq.c    |   3 +
 drivers/media/pci/cobalt/cobalt-v4l2.c   | 126 ++++++++++++++++++++++++++++---
 drivers/media/pci/cobalt/cobalt-v4l2.h   |   2 +
 6 files changed, 216 insertions(+), 26 deletions(-)
drivers/media/pci/cobalt/Kconfig +1 -0
index a01f0cc..9125747 100644
@@ -4,6 +4,7 @@ config VIDEO_COBALT
 	depends on PCI_MSI && MTD_COMPLEX_MAPPINGS
 	depends on GPIOLIB || COMPILE_TEST
 	depends on SND
+	select MEDIA_CEC
 	select I2C_ALGOBIT
 	select VIDEO_ADV7604
 	select VIDEO_ADV7511
drivers/media/pci/cobalt/cobalt-driver.c +91 -17

222 lines not on this page · raw message

drivers/media/pci/cobalt/cobalt-driver.h +2 -0
index b2f08e4..66211f8 100644
@@ -31,6 +31,7 @@
 #include <linux/workqueue.h>
 #include <linux/mutex.h>
 
+#include <media/cec.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-ioctl.h>
 #include <media/v4l2-device.h>
@@ -223,6 +224,7 @@ struct cobalt_stream {
 	struct list_head bufs;
 	struct i2c_adapter *i2c_adap;
 	struct v4l2_subdev *sd;
+	struct cec_adapter *cec_adap;
 	struct mutex lock;
 	spinlock_t irqlock;
 	struct v4l2_dv_timings timings;
drivers/media/pci/cobalt/cobalt-irq.c +3 -0
index 3de26d0..384a791 100644
@@ -234,6 +234,9 @@ void cobalt_irq_log_status(struct cobalt *cobalt)
 	u32 mask;
 	int i;
 
+	cobalt_info("irq: edge=%08x mask=%08x\n",
+		    cobalt_read_bar1(cobalt, COBALT_SYS_STAT_EDGE),
+		    cobalt_read_bar1(cobalt, COBALT_SYS_STAT_MASK));
 	cobalt_info("irq: adv1=%u adv2=%u advout=%u none=%u full=%u\n",
 		    cobalt->irq_adv1, cobalt->irq_adv2, cobalt->irq_advout,
 		    cobalt->irq_none, cobalt->irq_full_fifo);
drivers/media/pci/cobalt/cobalt-v4l2.c +117 -9

181 lines not on this page · raw message

drivers/media/pci/cobalt/cobalt-v4l2.h +2 -0
index 62be553..5be36cc 100644
@@ -18,5 +18,7 @@
  *  SOFTWARE.
  */
 
+extern const struct cec_adap_ops cobalt_cec_adap_ops;
+
 int cobalt_nodes_register(struct cobalt *cobalt);
 void cobalt_nodes_unregister(struct cobalt *cobalt);
Signature · 1 line
2.6.2