All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCHv16 00/13] HDMI CEC framework
@ 2016-04-29 13:52 Hans Verkuil
  2016-04-29 13:52 ` [PATCHv16 01/13] input.h: add BUS_CEC type Hans Verkuil
                   ` (12 more replies)
  0 siblings, 13 replies; 35+ messages in thread
From: Hans Verkuil @ 2016-04-29 13:52 UTC (permalink / raw)
  To: linux-media; +Cc: dri-devel, linux-samsung-soc, linux-input, lars, linux

From: Hans Verkuil <hans.verkuil@cisco.com>

Hi all,

The sixteenth version of this patchset does some final cleanup and
I'll post a pull request on the linux-media list for this patch series.

At first I didn't want to post a v16 at all, but there were just a bit
too many changes, even though each change itself was trivial.

While the pull request will be for 4.7 I think it is more likely that
it will end up in 4.8 given how late we are in the 4.7 cycle.

I dropped the RFC patches for the pulse8 and ARC/CDC support since those
aren't ready for mainlining. Same for the omap4 support patches I posted
today.

See the changelog below for more details.

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 v15
=================

- Properly document cec-edid.h
- Fix various sparse/smatch warnings
- Fixed several DocBook issues
- Fixed incorrect return values in the adv drivers that caused an
  otherwise harmless WARN_ON.

Changes since v14
=================
- Dropped the Samsung dts patches, these will go in via the samsung tree.
- CEC_LOG_STATUS is replaced by a debugfs status file.
- Dropped the reserved fields in the API, use versioning instead. Two
  flags field were added instead.
- Extended cec_caps with a version field.
- Dropped CEC_CAP_IS_SOURCE as it is not needed in practice.
- The adv drivers now create the CEC adapter themselves instead of leaving
  that to the bridge. It greatly simplifies the code and it was really a
  left-over from the early days of the framework.
- The CEC implementation of the adv drivers is now configured through
  a separate config option. CEC is an optional HDMI feature and you may not
  want to compile this in if your hardware hasn't hooked up the CEC pin.
- The EDID CEC helper functions have been split off into their own cec-edid
  module. You likely need them even if MEDIA_CEC is not set.
- Added missing copyright statements.
- Added RFC code for the pulse-eight USB CEC adapter.

Changes since v13
=================
- Removed CEC_CAP_STATE and _VENDOR_ID
- Removed CEC_ADAP_G/S_STATE and CEC_ADAP_G/S_VENDOR_ID
- Add vendor_id to struct cec_log_addrs
- CEC_EVENT_STATE_CHANGE now reports the physical address, the logical
  address mask and the logical address type mask.
- Add the logical address mask and the logical address type mask to
  struct cec_log_addrs.
- Dropped cec_enable, instead enable/disable the adapter based on the
  physical address.
- Once a valid physical address is available and userspace or driver
  specified the requested logical address configuration the framework
  will automatically claim the needed logical addresses. It will retry
  the last used addresses first, as per the recommendation in the CEC
  specification.
- Dropped CEC power status handling: it's not clear if the kernel should
  handle that, and if it has to, whether this is the correct way.
- Dropped CEC_EVENT_PHYS_ADDR_CHANGE, this is now handled by
  CEC_EVENT_STATE_CHANGE.
- Add helper functions for manipulating physical addresses.
- Updated documentation.
- Dropped ninputs field in struct cec_caps.
- Dropped CEC_EVENT_INPUTS_CHANGE, was never used and it is unclear if it
  is needed.

Changes since v12
=================
- Added driver and name fields to the cec_caps struct. Without this it was
  very difficult to tell CEC adapters apart in a human readable manner.
- Renamed CEC_CAP_IO to CEC_CAP_TRANSMIT, which better describes this
  capability. 
- Added the CEC_ADAP_LOG_STATUS to log the current status of the CEC adapter.
  Very useful when debugging.
- Added comments to the cec.c source.
- Added a timeout when waiting for a transmit to finish. Without the timeout
  the state machine could lock up when dealing with broken CEC adapter drivers
  or just unlucky situations.
- Commenting the code also found a number of bugs which have been fixed.
- Trying to poll yourself is now handled by the framework since different
  hardware would give different results. The framework will just NACK it.
- Disabling the CEC adapter doesn't clear the physical address anymore.
  This turned out to be quite painful for applications.
- Merged the CLAIM/RELEASE, G/S_MONITOR and G_PASSTHROUGH ioctls into two
  G/S_MODE ioctls. This allows you to tell the driver which initiator and
  follower modes you want, and greatly simplifies how these modes are
  handled.

Changes since v11
=================
- Add a new CEC_EVENT_PHYS_ADDR_CHANGED to report when the physical address
  of the CEC adapter changes. Since the physical address is obtained from the
  EDID the application has to be informed when the kernel (or userspace for
  that matter) sets that.
- Add a new internal cec_set_phys_addr() function to change the physical
  address and post the event.
- Modified the retry-transmit mechanism: the framework will do the retry if
  the low-level driver returns a TX_STATUS error, but does not set the
  CEC_TX_STATUS_MAX_RETRIES status bit. In that case the framework assumes
  that there is no hardware retry and it will retry itself. This used to be
  through the 'retries' pointer argument (set to 0 if there is no retry support)
  but that doesn't work for hardware that does retry for some error conditions
  but not for others.
- Added the missing CEC_TX_STATUS_LOW_DRIVE bit and low_drive_cnt error counter.
  I missed that potential CEC bus error condition.
- By passing a NULL pointer to cec_claim_log_addrs() drivers can clear the
  logical addresses.
- When a source loses the HPD the source has to clear the logical addresses
  and update its physical address. Implement this in the cobalt driver.
- Improve tx_status handling in the various low-level drivers.

Changes since v10
=================
- Split the msg.status field into tx_status and rx_status.
- Add counters for various CEC protocol errors that can occur during
  transmit.
- Add tx_status bits for the ARB_LOST and NACK status and add a generic
  CEC_TX_STATUS_ERROR bit.
- Add retry support to the CEC framework for hardware that does not have
  retry support.
- Improve event handling: on open() generate events that report the initial
  CEC adapter status and the number of connected inputs. Also ensure that
  you never loose important events (intermediate events may be lost, but
  never the last event). The basic mechanism has been taken from the V4L2
  event implementation which works very well.
- CEC emulation support was added to vivid which makes it much easier to
  test CEC applications without having hardware present.
- Improve handling of timings and timeouts in the cec framework.
- Improve locking.
- Fixed buggy rc cleanup.
- All CEC messages are now fully supported in cec-funcs.h (specifically
  handling of UI Commands and short audio descriptors).

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/linux-media@vger.kernel.org/msg28735.html


Hans Verkuil (10):
  input.h: add BUS_CEC type
  cec: add HDMI CEC framework
  cec/TODO: add TODO file so we know why this is still in staging
  cec: add compat32 ioctl support
  cec.txt: add CEC framework documentation
  DocBook/media: add CEC documentation
  cec: adv7604: add cec support.
  cec: adv7842: add cec support
  cec: adv7511: add cec support.
  vivid: add CEC emulation

Kamil Debski (3):
  HID: add HDMI CEC specific keycodes
  rc: Add HDMI CEC protocol handling
  cec: s5p-cec: Add s5p-cec driver

 Documentation/DocBook/device-drivers.tmpl          |    4 +
 Documentation/DocBook/media/Makefile               |    2 +
 Documentation/DocBook/media/v4l/biblio.xml         |   10 +
 Documentation/DocBook/media/v4l/cec-api.xml        |   72 +
 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      |  140 ++
 .../DocBook/media/v4l/cec-ioc-adap-g-log-addrs.xml |  324 +++
 .../DocBook/media/v4l/cec-ioc-adap-g-phys-addr.xml |   82 +
 .../DocBook/media/v4l/cec-ioc-dqevent.xml          |  190 ++
 Documentation/DocBook/media/v4l/cec-ioc-g-mode.xml |  245 ++
 .../DocBook/media/v4l/cec-ioc-receive.xml          |  260 ++
 Documentation/DocBook/media_api.tmpl               |    6 +-
 Documentation/cec.txt                              |  267 +++
 .../devicetree/bindings/media/s5p-cec.txt          |   31 +
 Documentation/video4linux/vivid.txt                |   36 +-
 MAINTAINERS                                        |   23 +
 drivers/media/Kconfig                              |    3 +
 drivers/media/Makefile                             |    2 +
 drivers/media/cec-edid.c                           |  139 ++
 drivers/media/i2c/Kconfig                          |   27 +
 drivers/media/i2c/adv7511.c                        |  401 +++-
 drivers/media/i2c/adv7604.c                        |  332 ++-
 drivers/media/i2c/adv7842.c                        |  368 ++-
 drivers/media/platform/Kconfig                     |   11 +
 drivers/media/platform/Makefile                    |    1 +
 drivers/media/platform/s5p-cec/Makefile            |    2 +
 drivers/media/platform/s5p-cec/exynos_hdmi_cec.h   |   38 +
 .../media/platform/s5p-cec/exynos_hdmi_cecctrl.c   |  209 ++
 drivers/media/platform/s5p-cec/regs-cec.h          |   96 +
 drivers/media/platform/s5p-cec/s5p_cec.c           |  295 +++
 drivers/media/platform/s5p-cec/s5p_cec.h           |   76 +
 drivers/media/platform/vivid/Kconfig               |    9 +
 drivers/media/platform/vivid/Makefile              |    4 +
 drivers/media/platform/vivid/vivid-cec.c           |  254 ++
 drivers/media/platform/vivid/vivid-cec.h           |   33 +
 drivers/media/platform/vivid/vivid-core.c          |  119 +-
 drivers/media/platform/vivid/vivid-core.h          |   27 +
 drivers/media/platform/vivid/vivid-kthread-cap.c   |   11 +
 drivers/media/platform/vivid/vivid-vid-cap.c       |   23 +-
 drivers/media/platform/vivid/vivid-vid-common.c    |    7 +
 drivers/media/rc/keymaps/Makefile                  |    1 +
 drivers/media/rc/keymaps/rc-cec.c                  |  174 ++
 drivers/media/rc/rc-main.c                         |    1 +
 drivers/staging/media/Kconfig                      |    2 +
 drivers/staging/media/Makefile                     |    1 +
 drivers/staging/media/cec/Kconfig                  |    8 +
 drivers/staging/media/cec/Makefile                 |    1 +
 drivers/staging/media/cec/TODO                     |   13 +
 drivers/staging/media/cec/cec.c                    | 2481 ++++++++++++++++++++
 fs/compat_ioctl.c                                  |   12 +
 include/linux/cec-funcs.h                          | 1871 +++++++++++++++
 include/linux/cec.h                                |  985 ++++++++
 include/media/cec-edid.h                           |  103 +
 include/media/cec.h                                |  236 ++
 include/media/i2c/adv7511.h                        |    6 +-
 include/media/rc-map.h                             |    5 +-
 include/uapi/linux/input-event-codes.h             |   30 +
 include/uapi/linux/input.h                         |    1 +
 61 files changed, 10296 insertions(+), 129 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-dqevent.xml
 create mode 100644 Documentation/DocBook/media/v4l/cec-ioc-g-mode.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-edid.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/platform/vivid/vivid-cec.c
 create mode 100644 drivers/media/platform/vivid/vivid-cec.h
 create mode 100644 drivers/media/rc/keymaps/rc-cec.c
 create mode 100644 drivers/staging/media/cec/Kconfig
 create mode 100644 drivers/staging/media/cec/Makefile
 create mode 100644 drivers/staging/media/cec/TODO
 create mode 100644 drivers/staging/media/cec/cec.c
 create mode 100644 include/linux/cec-funcs.h
 create mode 100644 include/linux/cec.h
 create mode 100644 include/media/cec-edid.h
 create mode 100644 include/media/cec.h

-- 
2.8.1


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

* [PATCHv16 01/13] input.h: add BUS_CEC type
  2016-04-29 13:52 [PATCHv16 00/13] HDMI CEC framework Hans Verkuil
@ 2016-04-29 13:52 ` Hans Verkuil
  2016-04-29 13:52 ` [PATCHv16 02/13] HID: add HDMI CEC specific keycodes Hans Verkuil
                   ` (11 subsequent siblings)
  12 siblings, 0 replies; 35+ messages in thread
From: Hans Verkuil @ 2016-04-29 13:52 UTC (permalink / raw)
  To: linux-media
  Cc: dri-devel, linux-samsung-soc, linux-input, lars, linux, Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

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

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Acked-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
 include/uapi/linux/input.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h
index 0111384..c514941 100644
--- a/include/uapi/linux/input.h
+++ b/include/uapi/linux/input.h
@@ -247,6 +247,7 @@ struct input_mask {
 #define BUS_ATARI		0x1B
 #define BUS_SPI			0x1C
 #define BUS_RMI			0x1D
+#define BUS_CEC			0x1E
 
 /*
  * MT_TOOL types
-- 
2.8.1


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

* [PATCHv16 02/13] HID: add HDMI CEC specific keycodes
  2016-04-29 13:52 [PATCHv16 00/13] HDMI CEC framework Hans Verkuil
  2016-04-29 13:52 ` [PATCHv16 01/13] input.h: add BUS_CEC type Hans Verkuil
@ 2016-04-29 13:52 ` Hans Verkuil
  2016-04-29 13:52 ` [PATCHv16 03/13] rc: Add HDMI CEC protocol handling Hans Verkuil
                   ` (10 subsequent siblings)
  12 siblings, 0 replies; 35+ messages in thread
From: Hans Verkuil @ 2016-04-29 13:52 UTC (permalink / raw)
  To: linux-media
  Cc: dri-devel, linux-samsung-soc, linux-input, lars, linux,
	Kamil Debski, Hans Verkuil

From: Kamil Debski <kamil@wypas.org>

Add HDMI CEC specific keycodes to the keycodes definition.

Signed-off-by: Kamil Debski <kamil@wypas.org>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Acked-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
 include/uapi/linux/input-event-codes.h | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/include/uapi/linux/input-event-codes.h b/include/uapi/linux/input-event-codes.h
index 87cf351..02b7b3a 100644
--- a/include/uapi/linux/input-event-codes.h
+++ b/include/uapi/linux/input-event-codes.h
@@ -611,6 +611,36 @@
 #define KEY_KBDINPUTASSIST_ACCEPT		0x264
 #define KEY_KBDINPUTASSIST_CANCEL		0x265
 
+/* Diagonal movement keys */
+#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
-- 
2.8.1


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

* [PATCHv16 03/13] rc: Add HDMI CEC protocol handling
  2016-04-29 13:52 [PATCHv16 00/13] HDMI CEC framework Hans Verkuil
  2016-04-29 13:52 ` [PATCHv16 01/13] input.h: add BUS_CEC type Hans Verkuil
  2016-04-29 13:52 ` [PATCHv16 02/13] HID: add HDMI CEC specific keycodes Hans Verkuil
@ 2016-04-29 13:52 ` Hans Verkuil
  2016-04-29 13:52 ` [PATCHv16 04/13] cec: add HDMI CEC framework Hans Verkuil
                   ` (9 subsequent siblings)
  12 siblings, 0 replies; 35+ messages in thread
From: Hans Verkuil @ 2016-04-29 13:52 UTC (permalink / raw)
  To: linux-media
  Cc: dri-devel, linux-samsung-soc, linux-input, lars, linux,
	Kamil Debski, Hans Verkuil

From: Kamil Debski <kamil@wypas.org>

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

Signed-off-by: Kamil Debski <kamil@wypas.org>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/rc/keymaps/Makefile |   1 +
 drivers/media/rc/keymaps/rc-cec.c | 174 ++++++++++++++++++++++++++++++++++++++
 drivers/media/rc/rc-main.c        |   1 +
 include/media/rc-map.h            |   5 +-
 4 files changed, 180 insertions(+), 1 deletion(-)
 create mode 100644 drivers/media/rc/keymaps/rc-cec.c

diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile
index fbbd3bb..9cffcc6 100644
--- a/drivers/media/rc/keymaps/Makefile
+++ b/drivers/media/rc/keymaps/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
 			rc-behold.o \
 			rc-behold-columbus.o \
 			rc-budget-ci-old.o \
+			rc-cec.o \
 			rc-cinergy-1400.o \
 			rc-cinergy.o \
 			rc-delock-61959.o \
diff --git a/drivers/media/rc/keymaps/rc-cec.c b/drivers/media/rc/keymaps/rc-cec.c
new file mode 100644
index 0000000..193cdca
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-cec.c
@@ -0,0 +1,174 @@
+/* Keytable for the CEC remote control
+ *
+ * Copyright (c) 2015 by Kamil Debski
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* CEC Spec "High-Definition Multimedia Interface Specification" can be obtained
+ * here: http://xtreamerdev.googlecode.com/files/CEC_Specs.pdf
+ * The list of control codes is listed in Table 27: User Control Codes p. 95 */
+
+static struct rc_map_table cec[] = {
+	{ 0x00, KEY_OK },
+	{ 0x01, KEY_UP },
+	{ 0x02, KEY_DOWN },
+	{ 0x03, KEY_LEFT },
+	{ 0x04, KEY_RIGHT },
+	{ 0x05, KEY_RIGHT_UP },
+	{ 0x06, KEY_RIGHT_DOWN },
+	{ 0x07, KEY_LEFT_UP },
+	{ 0x08, KEY_LEFT_DOWN },
+	{ 0x09, KEY_ROOT_MENU }, /* CEC Spec: Device Root Menu - see Note 2 */
+	/* Note 2: This is the initial display that a device shows. It is
+	 * device-dependent and can be, for example, a contents menu, setup
+	 * menu, favorite menu or other menu. The actual menu displayed
+	 * may also depend on the device's current state. */
+	{ 0x0a, KEY_SETUP },
+	{ 0x0b, KEY_MENU }, /* CEC Spec: Contents Menu */
+	{ 0x0c, KEY_FAVORITES }, /* CEC Spec: Favorite Menu */
+	{ 0x0d, KEY_EXIT },
+	/* 0x0e-0x0f: Reserved */
+	{ 0x10, KEY_MEDIA_TOP_MENU },
+	{ 0x11, KEY_CONTEXT_MENU },
+	/* 0x12-0x1c: Reserved */
+	{ 0x1d, KEY_DIGITS }, /* CEC Spec: select/toggle a Number Entry Mode */
+	{ 0x1e, KEY_NUMERIC_11 },
+	{ 0x1f, KEY_NUMERIC_12 },
+	/* 0x20-0x29: Keys 0 to 9 */
+	{ 0x20, KEY_NUMERIC_0 },
+	{ 0x21, KEY_NUMERIC_1 },
+	{ 0x22, KEY_NUMERIC_2 },
+	{ 0x23, KEY_NUMERIC_3 },
+	{ 0x24, KEY_NUMERIC_4 },
+	{ 0x25, KEY_NUMERIC_5 },
+	{ 0x26, KEY_NUMERIC_6 },
+	{ 0x27, KEY_NUMERIC_7 },
+	{ 0x28, KEY_NUMERIC_8 },
+	{ 0x29, KEY_NUMERIC_9 },
+	{ 0x2a, KEY_DOT },
+	{ 0x2b, KEY_ENTER },
+	{ 0x2c, KEY_CLEAR },
+	/* 0x2d-0x2e: Reserved */
+	{ 0x2f, KEY_NEXT_FAVORITE }, /* CEC Spec: Next Favorite */
+	{ 0x30, KEY_CHANNELUP },
+	{ 0x31, KEY_CHANNELDOWN },
+	{ 0x32, KEY_PREVIOUS }, /* CEC Spec: Previous Channel */
+	{ 0x33, KEY_SOUND }, /* CEC Spec: Sound Select */
+	{ 0x34, KEY_VIDEO }, /* 0x34: CEC Spec: Input Select */
+	{ 0x35, KEY_INFO }, /* CEC Spec: Display Information */
+	{ 0x36, KEY_HELP },
+	{ 0x37, KEY_PAGEUP },
+	{ 0x38, KEY_PAGEDOWN },
+	/* 0x39-0x3f: Reserved */
+	{ 0x40, KEY_POWER },
+	{ 0x41, KEY_VOLUMEUP },
+	{ 0x42, KEY_VOLUMEDOWN },
+	{ 0x43, KEY_MUTE },
+	{ 0x44, KEY_PLAYCD },
+	{ 0x45, KEY_STOPCD },
+	{ 0x46, KEY_PAUSECD },
+	{ 0x47, KEY_RECORD },
+	{ 0x48, KEY_REWIND },
+	{ 0x49, KEY_FASTFORWARD },
+	{ 0x4a, KEY_EJECTCD }, /* CEC Spec: Eject */
+	{ 0x4b, KEY_FORWARD },
+	{ 0x4c, KEY_BACK },
+	{ 0x4d, KEY_STOP_RECORD }, /* CEC Spec: Stop-Record */
+	{ 0x4e, KEY_PAUSE_RECORD }, /* CEC Spec: Pause-Record */
+	/* 0x4f: Reserved */
+	{ 0x50, KEY_ANGLE },
+	{ 0x51, KEY_TV2 },
+	{ 0x52, KEY_VOD }, /* CEC Spec: Video on Demand */
+	{ 0x53, KEY_EPG },
+	{ 0x54, KEY_TIME }, /* CEC Spec: Timer */
+	{ 0x55, KEY_CONFIG },
+	/* The following codes are hard to implement at this moment, as they
+	 * carry an additional additional argument. Most likely changes to RC
+	 * framework are necessary.
+	 * For now they are interpreted by the CEC framework as non keycodes
+	 * and are passed as messages enabling user application to parse them.
+	 * */
+	/* 0x56: CEC Spec: Select Broadcast Type */
+	/* 0x57: CEC Spec: Select Sound presentation */
+	{ 0x58, KEY_AUDIO_DESC }, /* CEC 2.0 and up */
+	{ 0x59, KEY_WWW }, /* CEC 2.0 and up */
+	{ 0x5a, KEY_3D_MODE }, /* CEC 2.0 and up */
+	/* 0x5b-0x5f: Reserved */
+	{ 0x60, KEY_PLAYCD }, /* CEC Spec: Play Function */
+	{ 0x6005, KEY_FASTFORWARD },
+	{ 0x6006, KEY_FASTFORWARD },
+	{ 0x6007, KEY_FASTFORWARD },
+	{ 0x6015, KEY_SLOW },
+	{ 0x6016, KEY_SLOW },
+	{ 0x6017, KEY_SLOW },
+	{ 0x6009, KEY_FASTREVERSE },
+	{ 0x600a, KEY_FASTREVERSE },
+	{ 0x600b, KEY_FASTREVERSE },
+	{ 0x6019, KEY_SLOWREVERSE },
+	{ 0x601a, KEY_SLOWREVERSE },
+	{ 0x601b, KEY_SLOWREVERSE },
+	{ 0x6020, KEY_REWIND },
+	{ 0x6024, KEY_PLAYCD },
+	{ 0x6025, KEY_PAUSECD },
+	{ 0x61, KEY_PLAYPAUSE }, /* CEC Spec: Pause-Play Function */
+	{ 0x62, KEY_RECORD }, /* Spec: Record Function */
+	{ 0x63, KEY_PAUSE_RECORD }, /* CEC Spec: Pause-Record Function */
+	{ 0x64, KEY_STOPCD }, /* CEC Spec: Stop Function */
+	{ 0x65, KEY_MUTE }, /* CEC Spec: Mute Function */
+	{ 0x66, KEY_UNMUTE }, /* CEC Spec: Restore the volume */
+	/* The following codes are hard to implement at this moment, as they
+	 * carry an additional additional argument. Most likely changes to RC
+	 * framework are necessary.
+	 * For now they are interpreted by the CEC framework as non keycodes
+	 * and are passed as messages enabling user application to parse them.
+	 * */
+	/* 0x67: CEC Spec: Tune Function */
+	/* 0x68: CEC Spec: Seleect Media Function */
+	/* 0x69: CEC Spec: Select A/V Input Function */
+	/* 0x6a: CEC Spec: Select Audio Input Function */
+	{ 0x6b, KEY_POWER }, /* CEC Spec: Power Toggle Function */
+	{ 0x6c, KEY_SLEEP }, /* CEC Spec: Power Off Function */
+	{ 0x6d, KEY_WAKEUP }, /* CEC Spec: Power On Function */
+	/* 0x6e-0x70: Reserved */
+	{ 0x71, KEY_BLUE }, /* CEC Spec: F1 (Blue) */
+	{ 0x72, KEY_RED }, /* CEC Spec: F2 (Red) */
+	{ 0x73, KEY_GREEN }, /* CEC Spec: F3 (Green) */
+	{ 0x74, KEY_YELLOW }, /* CEC Spec: F4 (Yellow) */
+	{ 0x75, KEY_F5 },
+	{ 0x76, KEY_DATA }, /* CEC Spec: Data - see Note 3 */
+	/* Note 3: This is used, for example, to enter or leave a digital TV
+	 * data broadcast application. */
+	/* 0x77-0xff: Reserved */
+};
+
+static struct rc_map_list cec_map = {
+	.map = {
+		.scan		= cec,
+		.size		= ARRAY_SIZE(cec),
+		.rc_type	= RC_TYPE_CEC,
+		.name		= RC_MAP_CEC,
+	}
+};
+
+static int __init init_rc_map_cec(void)
+{
+	return rc_map_register(&cec_map);
+}
+
+static void __exit exit_rc_map_cec(void)
+{
+	rc_map_unregister(&cec_map);
+}
+
+module_init(init_rc_map_cec);
+module_exit(exit_rc_map_cec);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Kamil Debski");
diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
index 4e9bbe7..85c364a 100644
--- a/drivers/media/rc/rc-main.c
+++ b/drivers/media/rc/rc-main.c
@@ -804,6 +804,7 @@ static const struct {
 	{ RC_BIT_SHARP,		"sharp",	"ir-sharp-decoder"	},
 	{ RC_BIT_MCE_KBD,	"mce_kbd",	"ir-mce_kbd-decoder"	},
 	{ RC_BIT_XMP,		"xmp",		"ir-xmp-decoder"	},
+	{ RC_BIT_CEC,		"cec",		NULL			},
 };
 
 /**
diff --git a/include/media/rc-map.h b/include/media/rc-map.h
index 7844e98..6e6557d 100644
--- a/include/media/rc-map.h
+++ b/include/media/rc-map.h
@@ -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		0ULL
@@ -53,6 +54,7 @@ enum rc_type {
 #define RC_BIT_RC6_MCE		(1ULL << RC_TYPE_RC6_MCE)
 #define RC_BIT_SHARP		(1ULL << RC_TYPE_SHARP)
 #define RC_BIT_XMP		(1ULL << RC_TYPE_XMP)
+#define RC_BIT_CEC		(1ULL << 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"
-- 
2.8.1


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

* [PATCHv16 04/13] cec: add HDMI CEC framework
  2016-04-29 13:52 [PATCHv16 00/13] HDMI CEC framework Hans Verkuil
                   ` (2 preceding siblings ...)
  2016-04-29 13:52 ` [PATCHv16 03/13] rc: Add HDMI CEC protocol handling Hans Verkuil
@ 2016-04-29 13:52 ` Hans Verkuil
  2016-06-16 16:00   ` Mauro Carvalho Chehab
  2016-04-29 13:52   ` Hans Verkuil
                   ` (8 subsequent siblings)
  12 siblings, 1 reply; 35+ messages in thread
From: Hans Verkuil @ 2016-04-29 13:52 UTC (permalink / raw)
  To: linux-media
  Cc: dri-devel, linux-samsung-soc, linux-input, lars, linux,
	Hans Verkuil, Kamil Debski

From: Hans Verkuil <hansverk@cisco.com>

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

Besides the cec module itself it also adds a cec-edid module that
contains helper functions to find and manipulate the CEC physical
address inside an EDID. Even if the CEC support itself is disabled,
drivers will still need these functions.

Note that the CEC framework is added to staging/media and that the
cec.h and cec-funcs.h headers are not exported yet. While the kABI
is mature, I would prefer to allow the uABI some more time before
it is mainlined in case it needs more tweaks.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
[k.debski@samsung.com: Merged CEC Updates commit by Hans Verkuil]
[k.debski@samsung.com: Merged Update author commit by Hans Verkuil]
[k.debski@samsung.com: change kthread handling when setting logical
address]
[k.debski@samsung.com: code cleanup and fixes]
[k.debski@samsung.com: add missing CEC commands to match spec]
[k.debski@samsung.com: add RC framework support]
[k.debski@samsung.com: move and edit documentation]
[k.debski@samsung.com: add vendor id reporting]
[k.debski@samsung.com: add possibility to clear assigned logical
addresses]
[k.debski@samsung.com: documentation fixes, clenaup and expansion]
[k.debski@samsung.com: reorder of API structs and add reserved fields]
[k.debski@samsung.com: fix handling of events and fix 32/64bit timespec
problem]
[k.debski@samsung.com: add sequence number handling]
[k.debski@samsung.com: add passthrough mode]
[k.debski@samsung.com: fix CEC defines, add missing CEC 2.0 commands]
minor additions]
Signed-off-by: Kamil Debski <kamil@wypas.org>
---
 MAINTAINERS                        |   16 +
 drivers/media/Kconfig              |    3 +
 drivers/media/Makefile             |    2 +
 drivers/media/cec-edid.c           |  139 ++
 drivers/staging/media/Kconfig      |    2 +
 drivers/staging/media/Makefile     |    1 +
 drivers/staging/media/cec/Kconfig  |    8 +
 drivers/staging/media/cec/Makefile |    1 +
 drivers/staging/media/cec/cec.c    | 2481 ++++++++++++++++++++++++++++++++++++
 include/linux/cec-funcs.h          | 1871 +++++++++++++++++++++++++++
 include/linux/cec.h                |  985 ++++++++++++++
 include/media/cec-edid.h           |  103 ++
 include/media/cec.h                |  236 ++++
 13 files changed, 5848 insertions(+)
 create mode 100644 drivers/media/cec-edid.c
 create mode 100644 drivers/staging/media/cec/Kconfig
 create mode 100644 drivers/staging/media/cec/Makefile
 create mode 100644 drivers/staging/media/cec/cec.c
 create mode 100644 include/linux/cec-funcs.h
 create mode 100644 include/linux/cec.h
 create mode 100644 include/media/cec-edid.h
 create mode 100644 include/media/cec.h

diff --git a/MAINTAINERS b/MAINTAINERS
index bfcb7ea..83bd865 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2760,6 +2760,22 @@ 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 <hans.verkuil@cisco.com>
+L:	linux-media@vger.kernel.org
+T:	git git://linuxtv.org/media_tree.git
+W:	http://linuxtv.org
+S:	Supported
+F:	Documentation/cec.txt
+F:	Documentation/DocBook/media/v4l/cec*
+F:	drivers/staging/media/cec/cec.c
+F:	drivers/media/cec-edid.c
+F:	drivers/media/rc/keymaps/rc-cec.c
+F:	include/media/cec.h
+F:	include/media/cec-edid.h
+F:	include/linux/cec.h
+F:	include/linux/cec-funcs.h
+
 CELL BROADBAND ENGINE ARCHITECTURE
 M:	Arnd Bergmann <arnd@arndb.de>
 L:	linuxppc-dev@lists.ozlabs.org
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
index a8518fb..052dcf7 100644
--- a/drivers/media/Kconfig
+++ b/drivers/media/Kconfig
@@ -80,6 +80,9 @@ config MEDIA_RC_SUPPORT
 
 	  Say Y when you have a TV or an IR device.
 
+config MEDIA_CEC_EDID
+	tristate
+
 #
 # Media controller
 #	Selectable only for webcam/grabbers, as other drivers don't use it
diff --git a/drivers/media/Makefile b/drivers/media/Makefile
index e608bbc..b56f013 100644
--- a/drivers/media/Makefile
+++ b/drivers/media/Makefile
@@ -2,6 +2,8 @@
 # Makefile for the kernel multimedia device drivers.
 #
 
+obj-$(CONFIG_MEDIA_CEC_EDID) += cec-edid.o
+
 media-objs	:= media-device.o media-devnode.o media-entity.o
 
 #
diff --git a/drivers/media/cec-edid.c b/drivers/media/cec-edid.c
new file mode 100644
index 0000000..50202d8
--- /dev/null
+++ b/drivers/media/cec-edid.c
@@ -0,0 +1,139 @@
+/*
+ * cec-edid - HDMI Consumer Electronics Control EDID & CEC helper functions
+ *
+ * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/cec.h>
+#include <media/cec-edid.h>
+
+static unsigned cec_get_edid_spa_location(const u8 *edid, unsigned size)
+{
+	u8 d;
+
+	if (size < 256)
+		return 0;
+
+	if (edid[0x7e] != 1 || edid[0x80] != 0x02 || edid[0x81] != 0x03)
+		return 0;
+
+	/* 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 0;
+}
+
+u16 cec_get_edid_phys_addr(const u8 *edid, unsigned size, unsigned *offset)
+{
+	unsigned loc = cec_get_edid_spa_location(edid, size);
+
+	if (offset)
+		*offset = loc;
+	if (loc == 0)
+		return CEC_PHYS_ADDR_INVALID;
+	return (edid[loc] << 8) | edid[loc + 1];
+}
+EXPORT_SYMBOL_GPL(cec_get_edid_phys_addr);
+
+void cec_set_edid_phys_addr(u8 *edid, unsigned size, u16 phys_addr)
+{
+	unsigned loc = cec_get_edid_spa_location(edid, size);
+	u8 sum = 0;
+	unsigned i;
+
+	if (loc == 0)
+		return;
+	edid[loc] = phys_addr >> 8;
+	edid[loc + 1] = phys_addr & 0xff;
+	loc &= ~0x7f;
+
+	/* update the checksum */
+	for (i = loc; i < loc + 127; i++)
+		sum += edid[i];
+	edid[i] = 256 - sum;
+}
+EXPORT_SYMBOL_GPL(cec_set_edid_phys_addr);
+
+u16 cec_phys_addr_for_input(u16 phys_addr, u8 input)
+{
+	/* Check if input is sane */
+	if (WARN_ON(input == 0 || input > 0xf))
+		return CEC_PHYS_ADDR_INVALID;
+
+	if (phys_addr == 0)
+		return input << 12;
+
+	if ((phys_addr & 0x0fff) == 0)
+		return phys_addr | (input << 8);
+
+	if ((phys_addr & 0x00ff) == 0)
+		return phys_addr | (input << 4);
+
+	if ((phys_addr & 0x000f) == 0)
+		return phys_addr | input;
+
+	/*
+	 * All nibbles are used so no valid physical addresses can be assigned
+	 * to the input.
+	 */
+	return CEC_PHYS_ADDR_INVALID;
+}
+EXPORT_SYMBOL_GPL(cec_phys_addr_for_input);
+
+int cec_phys_addr_validate(u16 phys_addr, u16 *parent, u16 *port)
+{
+	int i;
+
+	if (parent)
+		*parent = phys_addr;
+	if (port)
+		*port = 0;
+	if (phys_addr == CEC_PHYS_ADDR_INVALID)
+		return 0;
+	for (i = 0; i < 16; i += 4)
+		if (phys_addr & (0xf << i))
+			break;
+	if (i == 16)
+		return 0;
+	if (parent)
+		*parent = phys_addr & (0xfff0 << i);
+	if (port)
+		*port = (phys_addr >> i) & 0xf;
+	for (i += 4; i < 16; i += 4)
+		if ((phys_addr & (0xf << i)) == 0)
+			return -EINVAL;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cec_phys_addr_validate);
+
+MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>");
+MODULE_DESCRIPTION("CEC EDID helper functions");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig
index de7e9f5..71554d9 100644
--- a/drivers/staging/media/Kconfig
+++ b/drivers/staging/media/Kconfig
@@ -21,6 +21,8 @@ if STAGING_MEDIA
 # Please keep them in alphabetic order
 source "drivers/staging/media/bcm2048/Kconfig"
 
+source "drivers/staging/media/cec/Kconfig"
+
 source "drivers/staging/media/cxd2099/Kconfig"
 
 source "drivers/staging/media/davinci_vpfe/Kconfig"
diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile
index 60a35b3..1d6a828 100644
--- a/drivers/staging/media/Makefile
+++ b/drivers/staging/media/Makefile
@@ -1,4 +1,5 @@
 obj-$(CONFIG_I2C_BCM2048)	+= bcm2048/
+obj-$(CONFIG_MEDIA_CEC)		+= cec/
 obj-$(CONFIG_DVB_CXD2099)	+= cxd2099/
 obj-$(CONFIG_LIRC_STAGING)	+= lirc/
 obj-$(CONFIG_VIDEO_DM365_VPFE)	+= davinci_vpfe/
diff --git a/drivers/staging/media/cec/Kconfig b/drivers/staging/media/cec/Kconfig
new file mode 100644
index 0000000..3297a54
--- /dev/null
+++ b/drivers/staging/media/cec/Kconfig
@@ -0,0 +1,8 @@
+config MEDIA_CEC
+	tristate "CEC API (EXPERIMENTAL)"
+	select MEDIA_CEC_EDID
+	---help---
+	  Enable the CEC API.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cec.
diff --git a/drivers/staging/media/cec/Makefile b/drivers/staging/media/cec/Makefile
new file mode 100644
index 0000000..7a7532e
--- /dev/null
+++ b/drivers/staging/media/cec/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_MEDIA_CEC) += cec.o
diff --git a/drivers/staging/media/cec/cec.c b/drivers/staging/media/cec/cec.c
new file mode 100644
index 0000000..3c5f084
--- /dev/null
+++ b/drivers/staging/media/cec/cec.c
@@ -0,0 +1,2481 @@
+/*
+ * cec - HDMI Consumer Electronics Control framework
+ *
+ * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/ktime.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/version.h>
+#include <media/cec-edid.h>
+#include <media/cec.h>
+
+#define CEC_NUM_DEVICES	256
+#define CEC_NAME	"cec"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "debug level (0-2)");
+
+/*
+ * 400 ms is the time it takes for one 16 byte message to be
+ * transferred and 5 is the maximum number of retries. Add
+ * another 100 ms as a margin. So if the transmit doesn't
+ * finish before that time something is really wrong and we
+ * have to time out.
+ *
+ * This is a sign that something it really wrong and a warning
+ * will be issued.
+ */
+#define CEC_XFER_TIMEOUT_MS (5 * 400 + 100)
+
+#define dprintk(lvl, fmt, arg...)					\
+	do {								\
+		if (lvl <= debug)					\
+			pr_info("cec-%s: " fmt, adap->name, ## arg);	\
+	} while (0)
+
+#define call_op(adap, op, arg...) \
+	(adap->ops->op ? adap->ops->op(adap, ## arg) : 0)
+
+#define call_void_op(adap, op, arg...)			\
+	do {						\
+		if (adap->ops->op)			\
+			adap->ops->op(adap, ## arg);	\
+	} while (0)
+
+static dev_t cec_dev_t;
+
+/* Active devices */
+static DEFINE_MUTEX(cec_devnode_lock);
+static DECLARE_BITMAP(cec_devnode_nums, CEC_NUM_DEVICES);
+
+static struct dentry *top_cec_dir;
+
+/* dev to cec_devnode */
+#define to_cec_devnode(cd) container_of(cd, struct cec_devnode, dev)
+
+/* devnode to cec_adapter */
+#define to_cec_adapter(node) container_of(node, struct cec_adapter, devnode)
+
+static inline struct cec_devnode *cec_devnode_data(struct file *filp)
+{
+	struct cec_fh *fh = filp->private_data;
+
+	return &fh->adap->devnode;
+}
+
+static int cec_log_addr2idx(const struct cec_adapter *adap, u8 log_addr)
+{
+	int i;
+
+	for (i = 0; i < adap->log_addrs.num_log_addrs; i++)
+		if (adap->log_addrs.log_addr[i] == log_addr)
+			return i;
+	return -1;
+}
+
+static unsigned cec_log_addr2dev(const struct cec_adapter *adap, u8 log_addr)
+{
+	int i = cec_log_addr2idx(adap, log_addr);
+
+	return adap->log_addrs.primary_device_type[i < 0 ? 0 : i];
+}
+
+/* Initialize the event queues for the filehandle. */
+static int cec_queue_event_init(struct cec_fh *fh)
+{
+	/* This has the size of the event queue for each event type. */
+	static const unsigned queue_sizes[CEC_NUM_EVENTS] = {
+		2,	/* CEC_EVENT_STATE_CHANGE */
+		1,	/* CEC_EVENT_LOST_MSGS */
+	};
+	unsigned i;
+
+	for (i = 0; i < CEC_NUM_EVENTS; i++) {
+		fh->evqueue[i].events = kcalloc(queue_sizes[i],
+				sizeof(struct cec_event), GFP_KERNEL);
+		if (fh->evqueue[i].events == NULL) {
+			while (i--) {
+				kfree(fh->evqueue[i].events);
+				fh->evqueue[i].events = NULL;
+				fh->evqueue[i].elems = 0;
+			}
+			return -ENOMEM;
+		}
+		fh->evqueue[i].elems = queue_sizes[i];
+	}
+	return 0;
+}
+
+static void cec_queue_event_free(struct cec_fh *fh)
+{
+	unsigned i;
+
+	for (i = 0; i < CEC_NUM_EVENTS; i++)
+		kfree(fh->evqueue[i].events);
+}
+
+/*
+ * Queue a new event for this filehandle. If ts == 0, then set it
+ * to the current time.
+ */
+static void cec_queue_event_fh(struct cec_fh *fh,
+			       const struct cec_event *new_ev, u64 ts)
+{
+	struct cec_event_queue *evq = &fh->evqueue[new_ev->event - 1];
+	struct cec_event *ev;
+
+	if (ts == 0)
+		ts = ktime_get_ns();
+
+	mutex_lock(&fh->lock);
+	ev = evq->events + evq->num_events;
+	/* Overwrite the last event if there is no more room for the new event */
+	if (evq->num_events == evq->elems) {
+		ev--;
+	} else {
+		evq->num_events++;
+		fh->events++;
+	}
+	*ev = *new_ev;
+	ev->ts = ts;
+	mutex_unlock(&fh->lock);
+	wake_up_interruptible(&fh->wait);
+}
+
+/* Queue a new event for all open filehandles. */
+static void cec_queue_event(struct cec_adapter *adap,
+			    const struct cec_event *ev)
+{
+	u64 ts = ktime_get_ns();
+	struct cec_fh *fh;
+
+	mutex_lock(&adap->devnode.fhs_lock);
+	list_for_each_entry(fh, &adap->devnode.fhs, list)
+		cec_queue_event_fh(fh, ev, ts);
+	mutex_unlock(&adap->devnode.fhs_lock);
+}
+
+/*
+ * Queue a new message for this filehandle. If there is no more room
+ * in the queue, then send the LOST_MSGS event instead.
+ */
+static void cec_queue_msg_fh(struct cec_fh *fh, const struct cec_msg *msg)
+{
+	struct cec_event ev_lost_msg = {
+		.event = CEC_EVENT_LOST_MSGS,
+	};
+	struct cec_msg_entry *entry;
+
+	mutex_lock(&fh->lock);
+	if (fh->queued_msgs == CEC_MAX_MSG_QUEUE_SZ)
+		goto lost_msgs;
+	entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+	if (entry == NULL)
+		goto lost_msgs;
+
+	entry->msg = *msg;
+	list_add(&entry->list, &fh->msgs);
+	fh->queued_msgs++;
+	mutex_unlock(&fh->lock);
+	wake_up_interruptible(&fh->wait);
+	return;
+
+lost_msgs:
+	ev_lost_msg.lost_msgs.lost_msgs = ++fh->lost_msgs;
+	mutex_unlock(&fh->lock);
+	cec_queue_event_fh(fh, &ev_lost_msg, 0);
+}
+
+/*
+ * Queue the message for those filehandles that are in monitor mode.
+ * If valid_la is true (this message is for us or was sent by us),
+ * then pass it on to any monitoring filehandle. If this message
+ * isn't for us or from us, then only give it to filehandles that
+ * are in MONITOR_ALL mode.
+ *
+ * This can only happen if the CEC_CAP_MONITOR_ALL capability is
+ * set and the CEC adapter was placed in 'monitor all' mode.
+ */
+static void cec_queue_msg_monitor(struct cec_adapter *adap,
+				  const struct cec_msg *msg,
+				  bool valid_la)
+{
+	struct cec_fh *fh;
+	u32 monitor_mode = valid_la ? CEC_MODE_MONITOR :
+				      CEC_MODE_MONITOR_ALL;
+
+	mutex_lock(&adap->devnode.fhs_lock);
+	list_for_each_entry(fh, &adap->devnode.fhs, list) {
+		if (fh->mode_follower >= monitor_mode)
+			cec_queue_msg_fh(fh, msg);
+	}
+	mutex_unlock(&adap->devnode.fhs_lock);
+}
+
+/*
+ * Queue the message for follower filehandles.
+ */
+static void cec_queue_msg_followers(struct cec_adapter *adap,
+				    const struct cec_msg *msg)
+{
+	struct cec_fh *fh;
+
+	mutex_lock(&adap->devnode.fhs_lock);
+	list_for_each_entry(fh, &adap->devnode.fhs, list) {
+		if (fh->mode_follower == CEC_MODE_FOLLOWER)
+			cec_queue_msg_fh(fh, msg);
+	}
+	mutex_unlock(&adap->devnode.fhs_lock);
+}
+
+/* Notify userspace of an adapter state change. */
+static void cec_post_state_event(struct cec_adapter *adap)
+{
+	struct cec_event ev = {
+		.event = CEC_EVENT_STATE_CHANGE,
+	};
+
+	ev.state_change.phys_addr = adap->phys_addr;
+	ev.state_change.log_addr_mask = adap->log_addrs.log_addr_mask;
+	cec_queue_event(adap, &ev);
+}
+
+/*
+ * A CEC transmit (and a possible wait for reply) completed.
+ * If this was in blocking mode, then complete it, otherwise
+ * queue the message for userspace to dequeue later.
+ *
+ * This function is called with adap->lock held.
+ */
+static void cec_data_completed(struct cec_data *data)
+{
+	/*
+	 * Delete this transmit from the filehandle's xfer_list since
+	 * we're done with it.
+	 *
+	 * Note that if the filehandle is closed before this transmit
+	 * finished, then the release() function will set data->fh to NULL.
+	 * Without that we would be referring to a closed filehandle.
+	 */
+	if (data->fh)
+		list_del(&data->xfer_list);
+
+	if (data->blocking) {
+		/*
+		 * Someone is blocking so mark the message as completed
+		 * and call complete.
+		 */
+		data->completed = true;
+		complete(&data->c);
+	} else {
+		/*
+		 * No blocking, so just queue the message if needed and
+		 * free the memory.
+		 */
+		if (data->fh)
+			cec_queue_msg_fh(data->fh, &data->msg);
+		kfree(data);
+	}
+}
+
+/*
+ * A pending CEC transmit needs to be cancelled, either because the CEC
+ * adapter is disabled or the transmit takes an impossibly long time to
+ * finish.
+ *
+ * This function is called with adap->lock held.
+ */
+static void cec_data_cancel(struct cec_data *data)
+{
+	/*
+	 * It's either the current transmit, or it is a pending
+	 * transmit. Take the appropriate action to clear it.
+	 */
+	if (data->adap->transmitting == data)
+		data->adap->transmitting = NULL;
+	else
+		list_del_init(&data->list);
+
+	/* Mark it as an error */
+	data->msg.ts = ktime_get_ns();
+	data->msg.tx_status = CEC_TX_STATUS_ERROR |
+			      CEC_TX_STATUS_MAX_RETRIES;
+	data->attempts = 0;
+	data->msg.tx_error_cnt = 1;
+	data->msg.reply = 0;
+	/* Queue transmitted message for monitoring purposes */
+	cec_queue_msg_monitor(data->adap, &data->msg, 1);
+
+	cec_data_completed(data);
+}
+
+/*
+ * Main CEC state machine
+ *
+ * Wait until the thread should be stopped, or we are not transmitting and
+ * a new transmit message is queued up, in which case we start transmitting
+ * that message. When the adapter finished transmitting the message it will
+ * call cec_transmit_done().
+ *
+ * If the adapter is disabled, then remove all queued messages instead.
+ *
+ * If the current transmit times out, then cancel that transmit.
+ */
+static int cec_thread_func(void *_adap)
+{
+	struct cec_adapter *adap = _adap;
+
+	for (;;) {
+		unsigned signal_free_time;
+		struct cec_data *data;
+		bool timeout = false;
+		u8 attempts;
+
+		if (adap->transmitting) {
+			int err;
+
+			/*
+			 * We are transmitting a message, so add a timeout
+			 * to prevent the state machine to get stuck waiting
+			 * for this message to finalize and add a check to
+			 * see if the adapter is disabled in which case the
+			 * transmit should be canceled.
+			 */
+			err = wait_event_interruptible_timeout(adap->kthread_waitq,
+				kthread_should_stop() ||
+				adap->phys_addr == CEC_PHYS_ADDR_INVALID ||
+				(!adap->transmitting &&
+				 !list_empty(&adap->transmit_queue)),
+				msecs_to_jiffies(CEC_XFER_TIMEOUT_MS));
+			timeout = err == 0;
+		} else {
+			/* Otherwise we just wait for something to happen. */
+			wait_event_interruptible(adap->kthread_waitq,
+				kthread_should_stop() ||
+				(!adap->transmitting &&
+				 !list_empty(&adap->transmit_queue)));
+		}
+
+		mutex_lock(&adap->lock);
+
+		if (adap->phys_addr == CEC_PHYS_ADDR_INVALID ||
+		    kthread_should_stop()) {
+			/*
+			 * If the adapter is disabled, or we're asked to stop,
+			 * then cancel any pending transmits.
+			 */
+			while (!list_empty(&adap->transmit_queue)) {
+				data = list_first_entry(&adap->transmit_queue,
+							struct cec_data, list);
+				cec_data_cancel(data);
+			}
+			while (!list_empty(&adap->wait_queue)) {
+				data = list_first_entry(&adap->wait_queue,
+							struct cec_data, list);
+				cec_data_cancel(data);
+			}
+			if (adap->transmitting)
+				cec_data_cancel(adap->transmitting);
+			goto unlock;
+		}
+
+		if (adap->transmitting && timeout) {
+			/*
+			 * If we timeout, then log that. This really shouldn't
+			 * happen and is an indication of a faulty CEC adapter
+			 * driver, or the CEC bus is in some weird state.
+			 */
+			dprintk(0, "message %*ph timed out!\n",
+				adap->transmitting->msg.len,
+				adap->transmitting->msg.msg);
+			/* Just give up on this. */
+			cec_data_cancel(adap->transmitting);
+			goto unlock;
+		}
+
+		/*
+		 * If we are still transmitting, or there is nothing new to
+		 * transmit, then just continue waiting.
+		 */
+		if (adap->transmitting || list_empty(&adap->transmit_queue))
+			goto unlock;
+
+		/* Get a new message to transmit */
+		data = list_first_entry(&adap->transmit_queue,
+					struct cec_data, list);
+		list_del_init(&data->list);
+		/* Make this the current transmitting message */
+		adap->transmitting = data;
+
+		/*
+		 * Suggested number of attempts as per the CEC 2.0 spec:
+		 * 4 attempts is the default, except for 'secondary poll
+		 * messages', i.e. poll messages not sent during the adapter
+		 * configuration phase when it allocates logical addresses.
+		 */
+		if (data->msg.len == 1 && adap->is_configured)
+			attempts = 2;
+		else
+			attempts = 4;
+
+		/* Set the suggested signal free time */
+		if (data->attempts) {
+			/* should be >= 3 data bit periods for a retry */
+			signal_free_time = CEC_SIGNAL_FREE_TIME_RETRY;
+		} else if (data->new_initiator) {
+			/* should be >= 5 data bit periods for new initiator */
+			signal_free_time = CEC_SIGNAL_FREE_TIME_NEW_INITIATOR;
+		} else {
+			/*
+			 * should be >= 7 data bit periods for sending another
+			 * frame immediately after another.
+			 */
+			signal_free_time = CEC_SIGNAL_FREE_TIME_NEXT_XFER;
+		}
+		if (data->attempts == 0)
+			data->attempts = attempts;
+
+		/* Tell the adapter to transmit, cancel on error */
+		if (adap->ops->adap_transmit(adap, data->attempts,
+					     signal_free_time, &data->msg))
+			cec_data_cancel(data);
+
+unlock:
+		mutex_unlock(&adap->lock);
+
+		if (kthread_should_stop())
+			break;
+	}
+	return 0;
+}
+
+/*
+ * Called by the CEC adapter if a transmit finished.
+ */
+void cec_transmit_done(struct cec_adapter *adap, u8 status, u8 arb_lost_cnt,
+		       u8 nack_cnt, u8 low_drive_cnt, u8 error_cnt)
+{
+	struct cec_data *data;
+	struct cec_msg *msg;
+
+	dprintk(2, "cec_transmit_done %02x\n", status);
+	mutex_lock(&adap->lock);
+	data = adap->transmitting;
+	if (WARN_ON(data == NULL)) {
+		/* This is weird and should not happen. Ignore this transmit */
+		dprintk(0, "cec_transmit_done without an ongoing transmit!\n");
+		goto unlock;
+	}
+
+	msg = &data->msg;
+
+	/* Drivers must fill in the status! */
+	WARN_ON(status == 0);
+	msg->ts = ktime_get_ns();
+	msg->tx_status |= status;
+	msg->tx_arb_lost_cnt += arb_lost_cnt;
+	msg->tx_nack_cnt += nack_cnt;
+	msg->tx_low_drive_cnt += low_drive_cnt;
+	msg->tx_error_cnt += error_cnt;
+
+	/* Mark that we're done with this transmit */
+	adap->transmitting = NULL;
+
+	/*
+	 * If there are still retry attempts left and there was an error and
+	 * the hardware didn't signal that it retried itself (by setting
+	 * CEC_TX_STATUS_MAX_RETRIES), then we will retry ourselves.
+	 */
+	if (data->attempts > 1 &&
+	    !(status & (CEC_TX_STATUS_MAX_RETRIES | CEC_TX_STATUS_OK))) {
+		/* Retry this message */
+		data->attempts--;
+		/* Add the message in front of the transmit queue */
+		list_add(&data->list, &adap->transmit_queue);
+		goto wake_thread;
+	}
+
+	data->attempts = 0;
+
+	/* Always set CEC_TX_STATUS_MAX_RETRIES on error */
+	if (!(status & CEC_TX_STATUS_OK))
+		msg->tx_status |= CEC_TX_STATUS_MAX_RETRIES;
+
+	/* Queue transmitted message for monitoring purposes */
+	cec_queue_msg_monitor(adap, msg, 1);
+
+	/*
+	 * Clear reply on error of if the adapter is no longer
+	 * configured. It makes no sense to wait for a reply in
+	 * this case.
+	 */
+	if (!(status & CEC_TX_STATUS_OK) || !adap->is_configured)
+		msg->reply = 0;
+
+	if (msg->timeout) {
+		/*
+		 * Queue the message into the wait queue if we want to wait
+		 * for a reply.
+		 */
+		list_add_tail(&data->list, &adap->wait_queue);
+		schedule_delayed_work(&data->work,
+				      msecs_to_jiffies(msg->timeout));
+	} else {
+		/* Otherwise we're done */
+		cec_data_completed(data);
+	}
+
+wake_thread:
+	/*
+	 * Wake up the main thread to see if another message is ready
+	 * for transmitting or to retry the current message.
+	 */
+	wake_up_interruptible(&adap->kthread_waitq);
+unlock:
+	mutex_unlock(&adap->lock);
+}
+EXPORT_SYMBOL_GPL(cec_transmit_done);
+
+/*
+ * Called when waiting for a reply times out.
+ */
+static void cec_wait_timeout(struct work_struct *work)
+{
+	struct cec_data *data = container_of(work, struct cec_data, work.work);
+	struct cec_adapter *adap = data->adap;
+
+	mutex_lock(&adap->lock);
+	/*
+	 * Sanity check in case the timeout and the arrival of the message
+	 * happened at the same time.
+	 */
+	if (list_empty(&data->list))
+		goto unlock;
+
+	/* Mark the message as timed out */
+	list_del_init(&data->list);
+	data->msg.ts = ktime_get_ns();
+	data->msg.rx_status = CEC_RX_STATUS_TIMEOUT;
+	cec_data_completed(data);
+unlock:
+	mutex_unlock(&adap->lock);
+}
+
+/*
+ * Transmit a message. The fh argument may be NULL if the transmit is not
+ * associated with a specific filehandle.
+ *
+ * This function is called with adap->lock held.
+ */
+static int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg,
+			       struct cec_fh *fh, bool block)
+{
+	struct cec_data *data;
+	u8 last_initiator = 0xff;
+	unsigned timeout;
+	int res = 0;
+
+	if (msg->reply && msg->timeout == 0) {
+		/* Make sure the timeout isn't 0. */
+		msg->timeout = 1000;
+	}
+
+	/* Sanity checks */
+	if (msg->len == 0 || msg->len > CEC_MAX_MSG_SIZE) {
+		dprintk(1, "cec_transmit_msg: invalid length %d\n", msg->len);
+		return -EINVAL;
+	}
+	if (msg->timeout && msg->len == 1) {
+		dprintk(1, "cec_transmit_msg: can't reply for poll msg\n");
+		return -EINVAL;
+	}
+	if (msg->len == 1) {
+		if (cec_msg_initiator(msg) != 0xf ||
+		    cec_msg_destination(msg) == 0xf) {
+			dprintk(1, "cec_transmit_msg: invalid poll message\n");
+			return -EINVAL;
+		}
+		if (cec_has_log_addr(adap, cec_msg_destination(msg))) {
+			/*
+			 * If the destination is a logical address our adapter
+			 * has already claimed, then just NACK this.
+			 * It depends on the hardware what it will do with a
+			 * POLL to itself (some OK this), so it is just as
+			 * easy to handle it here so the behavior will be
+			 * consistent.
+			 */
+			msg->tx_status = CEC_TX_STATUS_NACK |
+					 CEC_TX_STATUS_MAX_RETRIES;
+			msg->tx_nack_cnt = 1;
+			return 0;
+		}
+	}
+	if (msg->len > 1 && !cec_msg_is_broadcast(msg) &&
+	    cec_has_log_addr(adap, cec_msg_destination(msg))) {
+		dprintk(1, "cec_transmit_msg: destination is the adapter itself\n");
+		return -EINVAL;
+	}
+	if (cec_msg_initiator(msg) != 0xf &&
+	    !cec_has_log_addr(adap, cec_msg_initiator(msg))) {
+		dprintk(1, "cec_transmit_msg: initiator has unknown logical address %d\n",
+			cec_msg_initiator(msg));
+		return -EINVAL;
+	}
+	if (!adap->is_configured && !adap->is_configuring)
+		return -ENONET;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (data == NULL)
+		return -ENOMEM;
+
+	if (msg->len > 1 && msg->msg[1] == CEC_MSG_CDC_MESSAGE) {
+		msg->msg[2] = adap->phys_addr >> 8;
+		msg->msg[3] = adap->phys_addr & 0xff;
+	}
+
+	if (msg->timeout)
+		dprintk(2, "cec_transmit_msg: %*ph (wait for 0x%02x%s)\n",
+			msg->len, msg->msg, msg->reply, !block ? ", nb" : "");
+	else
+		dprintk(2, "cec_transmit_msg: %*ph%s\n",
+			msg->len, msg->msg, !block ? " (nb)" : "");
+
+	msg->rx_status = msg->tx_status = 0;
+	msg->tx_arb_lost_cnt = msg->tx_nack_cnt = 0;
+	msg->tx_low_drive_cnt = msg->tx_error_cnt = 0;
+	data->msg = *msg;
+	data->fh = fh;
+	data->adap = adap;
+	data->blocking = block;
+
+	/*
+	 * Determine if this message follows a message from the same
+	 * initiator. Needed to determine the free signal time later on.
+	 */
+	if (msg->len > 1) {
+		if (!(list_empty(&adap->transmit_queue))) {
+			const struct cec_data *last;
+
+			last = list_last_entry(&adap->transmit_queue,
+					       const struct cec_data, list);
+			last_initiator = cec_msg_initiator(&last->msg);
+		} else if (adap->transmitting) {
+			last_initiator =
+				cec_msg_initiator(&adap->transmitting->msg);
+		}
+	}
+	data->new_initiator = last_initiator != cec_msg_initiator(msg);
+	init_completion(&data->c);
+	INIT_DELAYED_WORK(&data->work, cec_wait_timeout);
+
+	data->msg.sequence = adap->sequence++;
+	if (fh)
+		list_add_tail(&data->xfer_list, &fh->xfer_list);
+	list_add_tail(&data->list, &adap->transmit_queue);
+	if (adap->transmitting == NULL)
+		wake_up_interruptible(&adap->kthread_waitq);
+
+	/* All done if we don't need to block waiting for completion */
+	if (!block)
+		return 0;
+
+	/*
+	 * If we don't get a completion before this time something is really
+	 * wrong and we time out.
+	 */
+	timeout = CEC_XFER_TIMEOUT_MS;
+	/* Add the requested timeout if we have to wait for a reply as well */
+	if (msg->timeout)
+		timeout += msg->timeout;
+
+	/*
+	 * Release the lock and wait, retake the lock afterwards.
+	 */
+	mutex_unlock(&adap->lock);
+	res = wait_for_completion_killable_timeout(&data->c,
+			 msecs_to_jiffies(timeout));
+	mutex_lock(&adap->lock);
+
+	if (data->completed) {
+		/* The transmit completed (possibly with an error) */
+		*msg = data->msg;
+		kfree(data);
+		return 0;
+	}
+	/*
+	 * The wait for completion timed out or was interrupted, so mark this
+	 * as non-blocking and disconnect from the filehandle since it is
+	 * still 'in flight'. When it finally completes it will just drop the
+	 * result silently.
+	 */
+	data->blocking = false;
+	if (data->fh)
+		list_del(&data->xfer_list);
+	data->fh = NULL;
+
+	if (res == 0) { /* timed out */
+		/* Check if the reply or the transmit failed */
+		if (msg->timeout && (msg->tx_status & CEC_TX_STATUS_OK))
+			msg->rx_status = CEC_RX_STATUS_TIMEOUT;
+		else
+			msg->tx_status = CEC_TX_STATUS_MAX_RETRIES;
+	}
+	return res > 0 ? 0 : res;
+}
+
+/* Helper function to be used by drivers and this framework. */
+int cec_transmit_msg(struct cec_adapter *adap, struct cec_msg *msg,
+		     bool block)
+{
+	int ret;
+
+	mutex_lock(&adap->lock);
+	ret = cec_transmit_msg_fh(adap, msg, NULL, block);
+	mutex_unlock(&adap->lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(cec_transmit_msg);
+
+/*
+ * I don't like forward references but without this the low-level
+ * cec_received_msg() function would come after a bunch of high-level
+ * CEC protocol handling functions. That was very confusing.
+ */
+static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg,
+			      bool is_reply);
+
+/* Called by the CEC adapter if a message is received */
+void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg)
+{
+	struct cec_data *data;
+	u8 msg_init = cec_msg_initiator(msg);
+	u8 msg_dest = cec_msg_destination(msg);
+	bool is_reply = false;
+	bool valid_la = true;
+
+	mutex_lock(&adap->lock);
+	msg->ts = ktime_get_ns();
+	msg->rx_status = CEC_RX_STATUS_OK;
+	msg->tx_status = 0;
+	msg->sequence = msg->reply = msg->timeout = 0;
+	msg->flags = 0;
+
+	dprintk(2, "cec_received_msg: %*ph\n", msg->len, msg->msg);
+
+	/* Check if this message was for us (directed or broadcast). */
+	if (!cec_msg_is_broadcast(msg))
+		valid_la = cec_has_log_addr(adap, msg_dest);
+
+	/* It's a valid message and not a poll or CDC message */
+	if (valid_la && msg->len > 1 && msg->msg[1] != CEC_MSG_CDC_MESSAGE) {
+		u8 cmd = msg->msg[1];
+		bool abort = cmd == CEC_MSG_FEATURE_ABORT;
+
+		/* The aborted command is in msg[2] */
+		if (abort)
+			cmd = msg->msg[2];
+
+		/*
+		 * Walk over all transmitted messages that are waiting for a
+		 * reply.
+		 */
+		list_for_each_entry(data, &adap->wait_queue, list) {
+			struct cec_msg *dst = &data->msg;
+			u8 dst_reply;
+
+			/* Does the command match? */
+			if ((abort && cmd != dst->msg[1]) ||
+			    (!abort && cmd != dst->reply))
+				continue;
+
+			/* Does the addressing match? */
+			if (msg_init != cec_msg_destination(dst) &&
+			    !cec_msg_is_broadcast(dst))
+				continue;
+
+			/* We got a reply */
+			msg->sequence = dst->sequence;
+			dst_reply = dst->reply;
+			*dst = *msg;
+			dst->reply = dst_reply;
+			if (abort) {
+				dst->reply = 0;
+				dst->rx_status |= CEC_RX_STATUS_FEATURE_ABORT;
+			}
+			/* Remove it from the wait_queue */
+			list_del_init(&data->list);
+
+			/* Cancel the pending timeout work */
+			if (!cancel_delayed_work(&data->work)) {
+				mutex_unlock(&adap->lock);
+				flush_scheduled_work();
+				mutex_lock(&adap->lock);
+			}
+			/*
+			 * Mark this as a reply, provided someone is still
+			 * waiting for the answer.
+			 */
+			if (data->fh)
+				is_reply = true;
+			cec_data_completed(data);
+			break;
+		}
+	}
+	mutex_unlock(&adap->lock);
+
+	/* Pass the message on to any monitoring filehandles */
+	cec_queue_msg_monitor(adap, msg, valid_la);
+
+	/* We're done if it is not for us or a poll message */
+	if (!valid_la || msg->len <= 1)
+		return;
+
+	/*
+	 * Process the message on the protocol level. If is_reply is true,
+	 * then cec_receive_notify() won't pass on the reply to the listener(s)
+	 * since that was already done by cec_data_completed() above.
+	 */
+	cec_receive_notify(adap, msg, is_reply);
+}
+EXPORT_SYMBOL_GPL(cec_received_msg);
+
+
+/* High-level core CEC message handling */
+
+/* Transmit the Report Features message */
+static int cec_report_features(struct cec_adapter *adap, unsigned la_idx)
+{
+	struct cec_msg msg = { };
+	const struct cec_log_addrs *las = &adap->log_addrs;
+	const u8 *features = las->features[la_idx];
+	bool op_is_dev_features = false;
+	unsigned idx;
+
+	/* This is 2.0 and up only */
+	if (adap->log_addrs.cec_version < CEC_OP_CEC_VERSION_2_0)
+		return 0;
+
+	/* Report Features */
+	msg.msg[0] = (las->log_addr[la_idx] << 4) | 0x0f;
+	msg.len = 4;
+	msg.msg[1] = CEC_MSG_REPORT_FEATURES;
+	msg.msg[2] = adap->log_addrs.cec_version;
+	msg.msg[3] = las->all_device_types[la_idx];
+
+	/* Write RC Profiles first, then Device Features */
+	for (idx = 0; idx < sizeof(las->features[0]); idx++) {
+		msg.msg[msg.len++] = features[idx];
+		if ((features[idx] & CEC_OP_FEAT_EXT) == 0) {
+			if (op_is_dev_features)
+				break;
+			op_is_dev_features = true;
+		}
+	}
+	return cec_transmit_msg(adap, &msg, false);
+}
+
+/* Transmit the Report Physical Address message */
+static int cec_report_phys_addr(struct cec_adapter *adap, unsigned la_idx)
+{
+	const struct cec_log_addrs *las = &adap->log_addrs;
+	struct cec_msg msg = { };
+
+	/* Report Physical Address */
+	msg.msg[0] = (las->log_addr[la_idx] << 4) | 0x0f;
+	cec_msg_report_physical_addr(&msg, adap->phys_addr,
+				     las->primary_device_type[la_idx]);
+	dprintk(2, "config: la %d pa %x.%x.%x.%x\n",
+			las->log_addr[la_idx],
+			cec_phys_addr_exp(adap->phys_addr));
+	return cec_transmit_msg(adap, &msg, false);
+}
+
+/* Transmit the Feature Abort message */
+static int cec_feature_abort_reason(struct cec_adapter *adap,
+				    struct cec_msg *msg, u8 reason)
+{
+	struct cec_msg tx_msg = { };
+
+	/*
+	 * Don't reply with CEC_MSG_FEATURE_ABORT to a CEC_MSG_FEATURE_ABORT
+	 * message!
+	 */
+	if (msg->msg[1] == CEC_MSG_FEATURE_ABORT)
+		return 0;
+	cec_msg_set_reply_to(&tx_msg, msg);
+	cec_msg_feature_abort(&tx_msg, msg->msg[1], reason);
+	return cec_transmit_msg(adap, &tx_msg, false);
+}
+
+static int cec_feature_abort(struct cec_adapter *adap, struct cec_msg *msg)
+{
+	return cec_feature_abort_reason(adap, msg,
+					CEC_OP_ABORT_UNRECOGNIZED_OP);
+}
+
+static int cec_feature_refused(struct cec_adapter *adap, struct cec_msg *msg)
+{
+	return cec_feature_abort_reason(adap, msg,
+					CEC_OP_ABORT_REFUSED);
+}
+
+/*
+ * Called when a CEC message is received. This function will do any
+ * necessary core processing. The is_reply bool is true if this message
+ * is a reply to an earlier transmit.
+ *
+ * The message is either a broadcast message or a valid directed message.
+ */
+static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg,
+			      bool is_reply)
+{
+	bool is_broadcast = cec_msg_is_broadcast(msg);
+	u8 dest_laddr = cec_msg_destination(msg);
+	u8 init_laddr = cec_msg_initiator(msg);
+	u8 devtype = cec_log_addr2dev(adap, dest_laddr);
+	int la_idx = cec_log_addr2idx(adap, dest_laddr);
+	bool is_directed = la_idx >= 0;
+	bool from_unregistered = init_laddr == 0xf;
+	struct cec_msg tx_cec_msg = { };
+
+	dprintk(1, "cec_receive_notify: %*ph\n", msg->len, msg->msg);
+
+	if (adap->ops->received) {
+		/* Allow drivers to process the message first */
+		if (adap->ops->received(adap, msg) != -ENOMSG)
+			return 0;
+	}
+
+	/*
+	 * REPORT_PHYSICAL_ADDR, CEC_MSG_USER_CONTROL_PRESSED and
+	 * CEC_MSG_USER_CONTROL_RELEASED messages always have to be
+	 * handled by the CEC core, even if the passthrough mode is on.
+	 * The others are just ignored if passthrough mode is on.
+	 */
+	switch (msg->msg[1]) {
+	case CEC_MSG_GET_CEC_VERSION:
+	case CEC_MSG_GIVE_DEVICE_VENDOR_ID:
+	case CEC_MSG_ABORT:
+	case CEC_MSG_GIVE_DEVICE_POWER_STATUS:
+	case CEC_MSG_GIVE_PHYSICAL_ADDR:
+	case CEC_MSG_GIVE_OSD_NAME:
+	case CEC_MSG_GIVE_FEATURES:
+		/*
+		 * Skip processing these messages if the passthrough mode
+		 * is on.
+		 */
+		if (adap->passthrough)
+			goto skip_processing;
+		/* Ignore if addressing is wrong */
+		if (is_broadcast || from_unregistered)
+			return 0;
+		break;
+
+	case CEC_MSG_USER_CONTROL_PRESSED:
+	case CEC_MSG_USER_CONTROL_RELEASED:
+		/* Wrong addressing mode: don't process */
+		if (is_broadcast || from_unregistered)
+			goto skip_processing;
+		break;
+
+	case CEC_MSG_REPORT_PHYSICAL_ADDR:
+		/*
+		 * This message is always processed, regardless of the
+		 * passthrough setting.
+		 *
+		 * Exception: don't process if wrong addressing mode.
+		 */
+		if (!is_broadcast)
+			goto skip_processing;
+		break;
+
+	default:
+		break;
+	}
+
+	cec_msg_set_reply_to(&tx_cec_msg, msg);
+
+	switch (msg->msg[1]) {
+	/* The following messages are processed but still passed through */
+	case CEC_MSG_REPORT_PHYSICAL_ADDR:
+		adap->phys_addrs[init_laddr] =
+			(msg->msg[2] << 8) | msg->msg[3];
+		dprintk(1, "Reported physical address %04x for logical address %d\n",
+			adap->phys_addrs[init_laddr], init_laddr);
+		break;
+
+	case CEC_MSG_USER_CONTROL_PRESSED:
+		if (!(adap->capabilities & CEC_CAP_RC))
+			break;
+
+#if IS_ENABLED(CONFIG_RC_CORE)
+		switch (msg->msg[2]) {
+		/*
+		 * Play function, this message can have variable length
+		 * depending on the specific play function that is used.
+		 */
+		case 0x60:
+			if (msg->len == 2)
+				rc_keydown(adap->rc, RC_TYPE_CEC,
+					   msg->msg[2], 0);
+			else
+				rc_keydown(adap->rc, RC_TYPE_CEC,
+					   msg->msg[2] << 8 | msg->msg[3], 0);
+			break;
+		/*
+		 * Other function messages that are not handled.
+		 * Currently the RC framework does not allow to supply an
+		 * additional parameter to a keypress. These "keys" contain
+		 * other information such as channel number, an input number
+		 * etc.
+		 * For the time being these messages are not processed by the
+		 * framework and are simply forwarded to the user space.
+		 */
+		case 0x56: case 0x57:
+		case 0x67: case 0x68: case 0x69: case 0x6a:
+			break;
+		default:
+			rc_keydown(adap->rc, RC_TYPE_CEC, msg->msg[2], 0);
+			break;
+		}
+#endif
+		break;
+
+	case CEC_MSG_USER_CONTROL_RELEASED:
+		if (!(adap->capabilities & CEC_CAP_RC))
+			break;
+#if IS_ENABLED(CONFIG_RC_CORE)
+		rc_keyup(adap->rc);
+#endif
+		break;
+
+	/*
+	 * The remaining messages are only processed if the passthrough mode
+	 * is off.
+	 */
+	case CEC_MSG_GET_CEC_VERSION:
+		cec_msg_cec_version(&tx_cec_msg, adap->log_addrs.cec_version);
+		return cec_transmit_msg(adap, &tx_cec_msg, false);
+
+	case CEC_MSG_GIVE_PHYSICAL_ADDR:
+		/* Do nothing for CEC switches using addr 15 */
+		if (devtype == CEC_OP_PRIM_DEVTYPE_SWITCH && dest_laddr == 15)
+			return 0;
+		cec_msg_report_physical_addr(&tx_cec_msg, adap->phys_addr, devtype);
+		return cec_transmit_msg(adap, &tx_cec_msg, false);
+
+	case CEC_MSG_GIVE_DEVICE_VENDOR_ID:
+		if (adap->log_addrs.vendor_id == CEC_VENDOR_ID_NONE)
+			return cec_feature_abort(adap, msg);
+		cec_msg_device_vendor_id(&tx_cec_msg, adap->log_addrs.vendor_id);
+		return cec_transmit_msg(adap, &tx_cec_msg, false);
+
+	case CEC_MSG_ABORT:
+		/* Do nothing for CEC switches */
+		if (devtype == CEC_OP_PRIM_DEVTYPE_SWITCH)
+			return 0;
+		return cec_feature_refused(adap, msg);
+
+	case CEC_MSG_GIVE_OSD_NAME: {
+		if (adap->log_addrs.osd_name[0] == 0)
+			return cec_feature_abort(adap, msg);
+		cec_msg_set_osd_name(&tx_cec_msg, adap->log_addrs.osd_name);
+		return cec_transmit_msg(adap, &tx_cec_msg, false);
+	}
+
+	case CEC_MSG_GIVE_FEATURES:
+		if (adap->log_addrs.cec_version >= CEC_OP_CEC_VERSION_2_0)
+			return cec_report_features(adap, la_idx);
+		return 0;
+
+	default:
+		/*
+		 * Unprocessed messages are aborted if userspace isn't doing
+		 * any processing either.
+		 */
+		if (is_directed && !is_reply && !adap->follower_cnt &&
+		    !adap->cec_follower && msg->msg[1] != CEC_MSG_FEATURE_ABORT)
+			return cec_feature_abort(adap, msg);
+		break;
+	}
+
+skip_processing:
+	/* If this was not a reply, then we're done */
+	if (is_reply)
+		return 0;
+
+	/*
+	 * Send to the exclusive follower if there is one, otherwise send
+	 * to all followerd.
+	 */
+	if (adap->cec_follower)
+		cec_queue_msg_fh(adap->cec_follower, msg);
+	else
+		cec_queue_msg_followers(adap, msg);
+	return 0;
+}
+
+/*
+ * Attempt to claim a specific logical address.
+ *
+ * This function is called with adap->lock held.
+ */
+static int cec_config_log_addr(struct cec_adapter *adap,
+			       unsigned int idx,
+			       unsigned int log_addr)
+{
+	struct cec_log_addrs *las = &adap->log_addrs;
+	struct cec_msg msg = { };
+	int err;
+
+	if (cec_has_log_addr(adap, log_addr))
+		return 0;
+
+	/* Send poll message */
+	msg.len = 1;
+	msg.msg[0] = 0xf0 | log_addr;
+	err = cec_transmit_msg_fh(adap, &msg, NULL, true);
+
+	/*
+	 * While trying to poll the physical address was reset
+	 * and the adapter was unconfigured, so bail out.
+	 */
+	if (!adap->is_configuring)
+		return -EINTR;
+
+	if (err)
+		return err;
+
+	if (msg.tx_status & CEC_TX_STATUS_OK)
+		return 0;
+
+	/*
+	 * Message not acknowledged, so this logical
+	 * address is free to use.
+	 */
+	err = adap->ops->adap_log_addr(adap, log_addr);
+	if (err)
+		return err;
+
+	las->log_addr[idx] = log_addr;
+	las->log_addr_mask |= 1 << log_addr;
+	adap->phys_addrs[log_addr] = adap->phys_addr;
+
+	dprintk(2, "claimed addr %d (%d)\n", log_addr,
+		las->primary_device_type[idx]);
+	return 1;
+}
+
+/*
+ * Unconfigure the adapter: clear all logical addresses and send
+ * the state changed event.
+ *
+ * This function is called with adap->lock held.
+ */
+static void cec_adap_unconfigure(struct cec_adapter *adap)
+{
+	WARN_ON(adap->ops->adap_log_addr(adap, CEC_LOG_ADDR_INVALID));
+	adap->log_addrs.log_addr_mask = 0;
+	adap->is_configuring = false;
+	adap->is_configured = false;
+	memset(adap->phys_addrs, 0xff, sizeof(adap->phys_addrs));
+	wake_up_interruptible(&adap->kthread_waitq);
+	cec_post_state_event(adap);
+}
+
+/*
+ * Attempt to claim the required logical addresses.
+ */
+static int cec_config_thread_func(void *arg)
+{
+	/* The various LAs for each type of device */
+	static const u8 tv_log_addrs[] = {
+		CEC_LOG_ADDR_TV, CEC_LOG_ADDR_SPECIFIC,
+		CEC_LOG_ADDR_INVALID
+	};
+	static const u8 record_log_addrs[] = {
+		CEC_LOG_ADDR_RECORD_1, CEC_LOG_ADDR_RECORD_2,
+		CEC_LOG_ADDR_RECORD_3,
+		CEC_LOG_ADDR_BACKUP_1, CEC_LOG_ADDR_BACKUP_2,
+		CEC_LOG_ADDR_INVALID
+	};
+	static const u8 tuner_log_addrs[] = {
+		CEC_LOG_ADDR_TUNER_1, CEC_LOG_ADDR_TUNER_2,
+		CEC_LOG_ADDR_TUNER_3, CEC_LOG_ADDR_TUNER_4,
+		CEC_LOG_ADDR_BACKUP_1, CEC_LOG_ADDR_BACKUP_2,
+		CEC_LOG_ADDR_INVALID
+	};
+	static const u8 playback_log_addrs[] = {
+		CEC_LOG_ADDR_PLAYBACK_1, CEC_LOG_ADDR_PLAYBACK_2,
+		CEC_LOG_ADDR_PLAYBACK_3,
+		CEC_LOG_ADDR_BACKUP_1, CEC_LOG_ADDR_BACKUP_2,
+		CEC_LOG_ADDR_INVALID
+	};
+	static const u8 audiosystem_log_addrs[] = {
+		CEC_LOG_ADDR_AUDIOSYSTEM,
+		CEC_LOG_ADDR_INVALID
+	};
+	static const u8 specific_use_log_addrs[] = {
+		CEC_LOG_ADDR_SPECIFIC,
+		CEC_LOG_ADDR_BACKUP_1, CEC_LOG_ADDR_BACKUP_2,
+		CEC_LOG_ADDR_INVALID
+	};
+	static const u8 *type2addrs[6] = {
+		[CEC_LOG_ADDR_TYPE_TV] = tv_log_addrs,
+		[CEC_LOG_ADDR_TYPE_RECORD] = record_log_addrs,
+		[CEC_LOG_ADDR_TYPE_TUNER] = tuner_log_addrs,
+		[CEC_LOG_ADDR_TYPE_PLAYBACK] = playback_log_addrs,
+		[CEC_LOG_ADDR_TYPE_AUDIOSYSTEM] = audiosystem_log_addrs,
+		[CEC_LOG_ADDR_TYPE_SPECIFIC] = specific_use_log_addrs,
+	};
+	static const u16 type2mask[] = {
+		[CEC_LOG_ADDR_TYPE_TV] = CEC_LOG_ADDR_MASK_TV,
+		[CEC_LOG_ADDR_TYPE_RECORD] = CEC_LOG_ADDR_MASK_RECORD,
+		[CEC_LOG_ADDR_TYPE_TUNER] = CEC_LOG_ADDR_MASK_TUNER,
+		[CEC_LOG_ADDR_TYPE_PLAYBACK] = CEC_LOG_ADDR_MASK_PLAYBACK,
+		[CEC_LOG_ADDR_TYPE_AUDIOSYSTEM] = CEC_LOG_ADDR_MASK_AUDIOSYSTEM,
+		[CEC_LOG_ADDR_TYPE_SPECIFIC] = CEC_LOG_ADDR_MASK_SPECIFIC,
+	};
+	struct cec_adapter *adap = arg;
+	struct cec_log_addrs *las = &adap->log_addrs;
+	int err;
+	int i, j;
+
+	mutex_lock(&adap->lock);
+	dprintk(1, "physical address: %x.%x.%x.%x, claim %d logical addresses\n",
+			cec_phys_addr_exp(adap->phys_addr),
+			las->num_log_addrs);
+	las->log_addr_mask = 0;
+
+	if (las->log_addr_type[0] == CEC_LOG_ADDR_TYPE_UNREGISTERED)
+		goto configured;
+
+	for (i = 0; i < las->num_log_addrs; i++) {
+		unsigned int type = las->log_addr_type[i];
+		const u8 *la_list;
+		u8 last_la;
+
+		/*
+		 * The TV functionality can only map to physical address 0.
+		 * For any other address, try the Specific functionality
+		 * instead as per the spec.
+		 */
+		if (adap->phys_addr && type == CEC_LOG_ADDR_TYPE_TV)
+			type = CEC_LOG_ADDR_TYPE_SPECIFIC;
+
+		la_list = type2addrs[type];
+		last_la = las->log_addr[i];
+		las->log_addr[i] = CEC_LOG_ADDR_INVALID;
+		if (last_la == CEC_LOG_ADDR_INVALID ||
+		    last_la == CEC_LOG_ADDR_UNREGISTERED ||
+		    !(last_la & type2mask[type]))
+			last_la = la_list[0];
+
+		err = cec_config_log_addr(adap, i, last_la);
+		if (err > 0) /* Reused last LA */
+			continue;
+
+		if (err < 0)
+			goto unconfigure;
+
+		for (j = 0; la_list[j] != CEC_LOG_ADDR_INVALID; j++) {
+			/* Tried this one already, skip it */
+			if (la_list[j] == last_la)
+				continue;
+			/* The backup addresses are CEC 2.0 specific */
+			if ((la_list[j] == CEC_LOG_ADDR_BACKUP_1 ||
+			     la_list[j] == CEC_LOG_ADDR_BACKUP_2) &&
+			    las->cec_version < CEC_OP_CEC_VERSION_2_0)
+				continue;
+
+			err = cec_config_log_addr(adap, i, la_list[j]);
+			if (err == 0) /* LA is in use */
+				continue;
+			if (err < 0)
+				goto unconfigure;
+			/* Done, claimed an LA */
+			break;
+		}
+
+		if (la_list[j] == CEC_LOG_ADDR_INVALID)
+			dprintk(1, "could not claim LA %d\n", i);
+	}
+
+configured:
+	if (adap->log_addrs.log_addr_mask == 0) {
+		/* Fall back to unregistered */
+		las->log_addr[0] = CEC_LOG_ADDR_UNREGISTERED;
+		las->log_addr_mask = 1 << las->log_addr[0];
+	}
+	adap->is_configured = true;
+	adap->is_configuring = false;
+	cec_post_state_event(adap);
+	mutex_unlock(&adap->lock);
+
+	for (i = 0; i < las->num_log_addrs; i++) {
+		if (las->log_addr[i] == CEC_LOG_ADDR_INVALID)
+			continue;
+
+		/*
+		 * Report Features must come first according
+		 * to CEC 2.0
+		 */
+		if (las->log_addr[i] != CEC_LOG_ADDR_UNREGISTERED)
+			cec_report_features(adap, i);
+		cec_report_phys_addr(adap, i);
+	}
+	mutex_lock(&adap->lock);
+	adap->kthread_config = NULL;
+	mutex_unlock(&adap->lock);
+	complete(&adap->config_completion);
+	return 0;
+
+unconfigure:
+	for (i = 0; i < las->num_log_addrs; i++)
+		las->log_addr[i] = CEC_LOG_ADDR_INVALID;
+	cec_adap_unconfigure(adap);
+	adap->kthread_config = NULL;
+	mutex_unlock(&adap->lock);
+	complete(&adap->config_completion);
+	return 0;
+}
+
+/*
+ * Called from either __cec_s_phys_addr or __cec_s_log_addrs to claim the
+ * logical addresses.
+ *
+ * This function is called with adap->lock held.
+ */
+static void cec_claim_log_addrs(struct cec_adapter *adap, bool block)
+{
+	if (WARN_ON(adap->is_configuring || adap->is_configured))
+		return;
+
+	init_completion(&adap->config_completion);
+
+	/* Ready to kick off the thread */
+	adap->is_configuring = true;
+	adap->kthread_config = kthread_run(cec_config_thread_func, adap,
+					   "ceccfg-%s", adap->name);
+	if (IS_ERR(adap->kthread_config)) {
+		adap->kthread_config = NULL;
+	} else if (block) {
+		mutex_unlock(&adap->lock);
+		wait_for_completion(&adap->config_completion);
+		mutex_lock(&adap->lock);
+	}
+}
+
+/* Set a new physical address and send an event notifying userspace of this.
+ *
+ * This function is called with adap->lock held.
+ */
+static void __cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block)
+{
+	if (phys_addr == adap->phys_addr)
+		return;
+
+	if (phys_addr == CEC_PHYS_ADDR_INVALID ||
+	    adap->phys_addr != CEC_PHYS_ADDR_INVALID) {
+		adap->phys_addr = CEC_PHYS_ADDR_INVALID;
+		cec_post_state_event(adap);
+		cec_adap_unconfigure(adap);
+		/* Disabling monitor all mode should always succeed */
+		if (adap->monitor_all_cnt)
+			WARN_ON(call_op(adap, adap_monitor_all_enable, false));
+		WARN_ON(adap->ops->adap_enable(adap, false));
+		if (phys_addr == CEC_PHYS_ADDR_INVALID)
+			return;
+	}
+
+	if (adap->ops->adap_enable(adap, true))
+		return;
+
+	if (adap->monitor_all_cnt &&
+	    call_op(adap, adap_monitor_all_enable, true)) {
+		WARN_ON(adap->ops->adap_enable(adap, false));
+		return;
+	}
+	adap->phys_addr = phys_addr;
+	cec_post_state_event(adap);
+	if (adap->log_addrs.num_log_addrs)
+		cec_claim_log_addrs(adap, block);
+}
+
+void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block)
+{
+	if (IS_ERR_OR_NULL(adap))
+		return;
+
+	if (WARN_ON(adap->capabilities & CEC_CAP_PHYS_ADDR))
+		return;
+	mutex_lock(&adap->lock);
+	__cec_s_phys_addr(adap, phys_addr, block);
+	mutex_unlock(&adap->lock);
+}
+EXPORT_SYMBOL_GPL(cec_s_phys_addr);
+
+/*
+ * Called from either the ioctl or a driver to set the logical addresses.
+ *
+ * This function is called with adap->lock held.
+ */
+static int __cec_s_log_addrs(struct cec_adapter *adap,
+			     struct cec_log_addrs *log_addrs, bool block)
+{
+	u16 type_mask = 0;
+	int i;
+
+	if (log_addrs == NULL || log_addrs->num_log_addrs == 0) {
+		adap->log_addrs.num_log_addrs = 0;
+		cec_adap_unconfigure(adap);
+		return 0;
+	}
+
+	/* Ensure the osd name is 0-terminated */
+	log_addrs->osd_name[sizeof(log_addrs->osd_name) - 1] = '\0';
+
+	/* Sanity checks */
+	if (log_addrs->num_log_addrs > adap->available_log_addrs) {
+		dprintk(1, "num_log_addrs > %d\n", adap->available_log_addrs);
+		return -EINVAL;
+	}
+
+	/*
+	 * Vendor ID is a 24 bit number, so check if the value is
+	 * within the correct range.
+	 */
+	if (log_addrs->vendor_id != CEC_VENDOR_ID_NONE &&
+	    (log_addrs->vendor_id & 0xff000000) != 0)
+		return -EINVAL;
+
+	if (log_addrs->cec_version != CEC_OP_CEC_VERSION_1_4 &&
+	    log_addrs->cec_version != CEC_OP_CEC_VERSION_2_0)
+		return -EINVAL;
+
+	if (log_addrs->num_log_addrs > 1)
+		for (i = 0; i < log_addrs->num_log_addrs; i++)
+			if (log_addrs->log_addr_type[i] ==
+					CEC_LOG_ADDR_TYPE_UNREGISTERED) {
+				dprintk(1, "num_log_addrs > 1 can't be combined with unregistered LA\n");
+				return -EINVAL;
+			}
+
+	if (log_addrs->cec_version < CEC_OP_CEC_VERSION_2_0) {
+		memset(log_addrs->all_device_types, 0,
+		       sizeof(log_addrs->all_device_types));
+		memset(log_addrs->features, 0, sizeof(log_addrs->features));
+	}
+
+	for (i = 0; i < log_addrs->num_log_addrs; i++) {
+		u8 *features = log_addrs->features[i];
+		bool op_is_dev_features = false;
+
+		log_addrs->log_addr[i] = CEC_LOG_ADDR_INVALID;
+		if (type_mask & (1 << log_addrs->log_addr_type[i])) {
+			dprintk(1, "duplicate logical address type\n");
+			return -EINVAL;
+		}
+		type_mask |= 1 << log_addrs->log_addr_type[i];
+		if ((type_mask & (1 << CEC_LOG_ADDR_TYPE_RECORD)) &&
+		    (type_mask & (1 << CEC_LOG_ADDR_TYPE_PLAYBACK))) {
+			/* Record already contains the playback functionality */
+			dprintk(1, "invalid record + playback combination\n");
+			return -EINVAL;
+		}
+		if (log_addrs->primary_device_type[i] >
+					CEC_OP_PRIM_DEVTYPE_PROCESSOR) {
+			dprintk(1, "unknown primary device type\n");
+			return -EINVAL;
+		}
+		if (log_addrs->primary_device_type[i] == 2) {
+			dprintk(1, "invalid primary device type\n");
+			return -EINVAL;
+		}
+		if (log_addrs->log_addr_type[i] > CEC_LOG_ADDR_TYPE_UNREGISTERED) {
+			dprintk(1, "unknown logical address type\n");
+			return -EINVAL;
+		}
+		if (log_addrs->cec_version < CEC_OP_CEC_VERSION_2_0)
+			continue;
+
+		for (i = 0; i < sizeof(log_addrs->features[0]); i++) {
+			if ((features[i] & 0x80) == 0) {
+				if (op_is_dev_features)
+					break;
+				op_is_dev_features = true;
+			}
+		}
+		if (!op_is_dev_features || i == sizeof(log_addrs->features[0])) {
+			dprintk(1, "malformed features\n");
+			return -EINVAL;
+		}
+	}
+
+	if (log_addrs->cec_version >= CEC_OP_CEC_VERSION_2_0) {
+		if (log_addrs->num_log_addrs > 2) {
+			dprintk(1, "CEC 2.0 allows no more than 2 logical addresses\n");
+			return -EINVAL;
+		}
+		if (log_addrs->num_log_addrs == 2) {
+			if (!(type_mask & ((1 << CEC_LOG_ADDR_TYPE_AUDIOSYSTEM) |
+					   (1 << CEC_LOG_ADDR_TYPE_TV)))) {
+				dprintk(1, "Two LAs is only allowed for audiosystem and TV\n");
+				return -EINVAL;
+			}
+			if (!(type_mask & ((1 << CEC_LOG_ADDR_TYPE_PLAYBACK) |
+					   (1 << CEC_LOG_ADDR_TYPE_RECORD)))) {
+				dprintk(1, "An audiosystem/TV can only be combined with record or playback\n");
+				return -EINVAL;
+			}
+		}
+	}
+
+	log_addrs->log_addr_mask = adap->log_addrs.log_addr_mask;
+	adap->log_addrs = *log_addrs;
+	if (adap->phys_addr != CEC_PHYS_ADDR_INVALID)
+		cec_claim_log_addrs(adap, block);
+	return 0;
+}
+
+int cec_s_log_addrs(struct cec_adapter *adap,
+		    struct cec_log_addrs *log_addrs, bool block)
+{
+	int err;
+
+	if (WARN_ON(adap->capabilities & CEC_CAP_LOG_ADDRS))
+		return -EINVAL;
+	mutex_lock(&adap->lock);
+	err = __cec_s_log_addrs(adap, log_addrs, block);
+	mutex_unlock(&adap->lock);
+	return err;
+}
+EXPORT_SYMBOL_GPL(cec_s_log_addrs);
+
+/*
+ * Log the current state of the CEC adapter.
+ * Very useful for debugging.
+ */
+static int cec_status(struct seq_file *file, void *priv)
+{
+	struct cec_adapter *adap = dev_get_drvdata(file->private);
+	struct cec_data *data;
+
+	mutex_lock(&adap->lock);
+	seq_printf(file, "configured: %d\n", adap->is_configured);
+	seq_printf(file, "configuring: %d\n", adap->is_configuring);
+	seq_printf(file, "phys_addr: %x.%x.%x.%x\n",
+		cec_phys_addr_exp(adap->phys_addr));
+	seq_printf(file, "number of LAs: %d\n", adap->log_addrs.num_log_addrs);
+	seq_printf(file, "LA mask: 0x%04x\n", adap->log_addrs.log_addr_mask);
+	if (adap->cec_follower)
+		seq_printf(file, "has CEC follower%s\n",
+			adap->passthrough ? " (in passthrough mode)" : "");
+	if (adap->cec_initiator)
+		seq_puts(file, "has CEC initiator\n");
+	if (adap->monitor_all_cnt)
+		seq_printf(file, "file handles in Monitor All mode: %u\n",
+			adap->monitor_all_cnt);
+	data = adap->transmitting;
+	if (data)
+		seq_printf(file, "transmitting message: %*ph (reply: %02x)\n",
+			data->msg.len, data->msg.msg, data->msg.reply);
+	list_for_each_entry(data, &adap->transmit_queue, list) {
+		seq_printf(file, "queued tx message: %*ph (reply: %02x)\n",
+			data->msg.len, data->msg.msg, data->msg.reply);
+	}
+	list_for_each_entry(data, &adap->wait_queue, list) {
+		seq_printf(file, "message waiting for reply: %*ph (reply: %02x)\n",
+			data->msg.len, data->msg.msg, data->msg.reply);
+	}
+
+	call_void_op(adap, adap_status, file);
+	mutex_unlock(&adap->lock);
+	return 0;
+}
+
+
+/* CEC file operations */
+
+static unsigned cec_poll(struct file *filp,
+			 struct poll_table_struct *poll)
+{
+	struct cec_devnode *devnode = cec_devnode_data(filp);
+	struct cec_fh *fh = filp->private_data;
+	struct cec_adapter *adap = fh->adap;
+	unsigned res = 0;
+
+	if (!devnode->registered)
+		return POLLERR | POLLHUP;
+	mutex_lock(&adap->lock);
+	if (adap->is_configured)
+		res |= POLLOUT | POLLWRNORM;
+	if (fh->queued_msgs)
+		res |= POLLIN | POLLRDNORM;
+	if (fh->events)
+		res |= POLLPRI;
+	poll_wait(filp, &fh->wait, poll);
+	mutex_unlock(&adap->lock);
+	return res;
+}
+
+/*
+ * Helper functions to keep track of the 'monitor all' use count.
+ *
+ * These functions are called with adap->lock held.
+ */
+static int cec_monitor_all_cnt_inc(struct cec_adapter *adap)
+{
+	int ret = 0;
+
+	if (adap->monitor_all_cnt == 0)
+		ret = call_op(adap, adap_monitor_all_enable, 1);
+	if (ret == 0)
+		adap->monitor_all_cnt++;
+	return ret;
+}
+
+static void cec_monitor_all_cnt_dec(struct cec_adapter *adap)
+{
+	adap->monitor_all_cnt--;
+	if (adap->monitor_all_cnt == 0)
+		WARN_ON(call_op(adap, adap_monitor_all_enable, 0));
+}
+
+/* Called by CEC_RECEIVE: wait for a message to arrive */
+static int cec_receive_msg(struct cec_fh *fh, struct cec_msg *msg, bool block)
+{
+	int res;
+
+	do {
+		mutex_lock(&fh->lock);
+		/* Are there received messages queued up? */
+		if (fh->queued_msgs) {
+			/* Yes, return the first one */
+			struct cec_msg_entry *entry =
+				list_first_entry(&fh->msgs,
+						 struct cec_msg_entry, list);
+
+			list_del(&entry->list);
+			*msg = entry->msg;
+			kfree(entry);
+			fh->queued_msgs--;
+			res = 0;
+		} else {
+			/* No, return EAGAIN in non-blocking mode or wait */
+			res = -EAGAIN;
+		}
+		mutex_unlock(&fh->lock);
+		/* Return when in non-blocking mode or if we have a message */
+		if (!block || !res)
+			break;
+
+		if (msg->timeout) {
+			/* The user specified a timeout */
+			res = wait_event_interruptible_timeout(fh->wait,
+				fh->queued_msgs,
+				msecs_to_jiffies(msg->timeout));
+			if (res == 0)
+				res = -ETIMEDOUT;
+			else if (res > 0)
+				res = 0;
+		} else {
+			/* Wait indefinitely */
+			res = wait_event_interruptible(fh->wait,
+				fh->queued_msgs);
+		}
+		/* Exit on error, otherwise loop to get the new message */
+	} while (!res);
+	return res;
+}
+
+static bool cec_is_busy(const struct cec_adapter *adap,
+			const struct cec_fh *fh)
+{
+	bool valid_initiator = adap->cec_initiator && adap->cec_initiator == fh;
+	bool valid_follower = adap->cec_follower && adap->cec_follower == fh;
+
+	/*
+	 * Exclusive initiators and followers can always access the CEC adapter
+	 */
+	if (valid_initiator || valid_follower)
+		return false;
+	/*
+	 * All others can only access the CEC adapter if there is no
+	 * exclusive initiator and they are in INITIATOR mode.
+	 */
+	return adap->cec_initiator != NULL ||
+	       fh->mode_initiator == CEC_MODE_NO_INITIATOR;
+}
+
+static long cec_ioctl(struct file *filp, unsigned cmd, unsigned long arg)
+{
+	struct cec_devnode *devnode = cec_devnode_data(filp);
+	struct cec_fh *fh = filp->private_data;
+	struct cec_adapter *adap = fh->adap;
+	bool block = !(filp->f_flags & O_NONBLOCK);
+	void __user *parg = (void __user *)arg;
+	int err = 0;
+
+	if (!devnode->registered)
+		return -EIO;
+
+	switch (cmd) {
+	case CEC_ADAP_G_CAPS: {
+		struct cec_caps caps = {};
+
+		strlcpy(caps.driver, adap->devnode.parent->driver->name,
+			sizeof(caps.driver));
+		strlcpy(caps.name, adap->name, sizeof(caps.name));
+		caps.available_log_addrs = adap->available_log_addrs;
+		caps.capabilities = adap->capabilities;
+		caps.version = LINUX_VERSION_CODE;
+		if (copy_to_user(parg, &caps, sizeof(caps)))
+			return -EFAULT;
+		break;
+	}
+
+	case CEC_TRANSMIT: {
+		struct cec_msg msg = {};
+
+		if (!(adap->capabilities & CEC_CAP_TRANSMIT))
+			return -ENOTTY;
+		if (copy_from_user(&msg, parg, sizeof(msg)))
+			return -EFAULT;
+		mutex_lock(&adap->lock);
+		if (!adap->is_configured) {
+			err = -ENONET;
+		} else if (cec_is_busy(adap, fh)) {
+			err = -EBUSY;
+		} else {
+			if (block || !msg.reply)
+				fh = NULL;
+			err = cec_transmit_msg_fh(adap, &msg, fh, block);
+		}
+		mutex_unlock(&adap->lock);
+		if (err)
+			return err;
+		if (copy_to_user(parg, &msg, sizeof(msg)))
+			return -EFAULT;
+		break;
+	}
+
+	case CEC_RECEIVE: {
+		struct cec_msg msg = {};
+
+		if (copy_from_user(&msg, parg, sizeof(msg)))
+			return -EFAULT;
+		mutex_lock(&adap->lock);
+		if (!adap->is_configured)
+			err = -ENONET;
+		mutex_unlock(&adap->lock);
+		if (err)
+			return err;
+
+		err = cec_receive_msg(fh, &msg, block);
+		if (err)
+			return err;
+		if (copy_to_user(parg, &msg, sizeof(msg)))
+			return -EFAULT;
+		break;
+	}
+
+	case CEC_DQEVENT: {
+		struct cec_event_queue *evq = NULL;
+		struct cec_event *ev = NULL;
+		u64 ts = ~0ULL;
+		unsigned i;
+
+		mutex_lock(&fh->lock);
+		/* Find the oldest event */
+		for (i = 0; i < CEC_NUM_EVENTS; i++) {
+			struct cec_event_queue *q = fh->evqueue + i;
+
+			if (q->num_events && q->events->ts <= ts) {
+				evq = q;
+				ev = q->events;
+				ts = ev->ts;
+			}
+		}
+		err = -EAGAIN;
+		if (ev) {
+			if (copy_to_user(parg, ev, sizeof(*ev))) {
+				err = -EFAULT;
+			} else {
+				unsigned j;
+
+				evq->num_events--;
+				fh->events--;
+				/*
+				 * Reset lost message counter after returning
+				 * this event.
+				 */
+				if (ev->event == CEC_EVENT_LOST_MSGS)
+					fh->lost_msgs = 0;
+				for (j = 0; j < evq->num_events; j++)
+					evq->events[j] = evq->events[j + 1];
+				err = 0;
+			}
+		}
+		mutex_unlock(&fh->lock);
+		return err;
+	}
+
+	case CEC_ADAP_G_PHYS_ADDR: {
+		u16 phys_addr;
+
+		mutex_lock(&adap->lock);
+		phys_addr = adap->phys_addr;
+		if (copy_to_user(parg, &phys_addr, sizeof(adap->phys_addr)))
+			err = -EFAULT;
+		mutex_unlock(&adap->lock);
+		break;
+	}
+
+	case CEC_ADAP_S_PHYS_ADDR: {
+		u16 phys_addr;
+
+		if (!(adap->capabilities & CEC_CAP_PHYS_ADDR))
+			return -ENOTTY;
+		if (copy_from_user(&phys_addr, parg, sizeof(phys_addr)))
+			return -EFAULT;
+
+		err = cec_phys_addr_validate(phys_addr, NULL, NULL);
+		if (err)
+			return err;
+		mutex_lock(&adap->lock);
+		if (cec_is_busy(adap, fh))
+			err = -EBUSY;
+		else
+			__cec_s_phys_addr(adap, phys_addr, block);
+		mutex_unlock(&adap->lock);
+		break;
+	}
+
+	case CEC_ADAP_G_LOG_ADDRS: {
+		struct cec_log_addrs log_addrs;
+
+		mutex_lock(&adap->lock);
+		log_addrs = adap->log_addrs;
+		if (!adap->is_configured)
+			memset(log_addrs.log_addr, CEC_LOG_ADDR_INVALID,
+			       sizeof(log_addrs.log_addr));
+		mutex_unlock(&adap->lock);
+
+		if (copy_to_user(parg, &log_addrs, sizeof(log_addrs)))
+			return -EFAULT;
+		break;
+	}
+
+	case CEC_ADAP_S_LOG_ADDRS: {
+		struct cec_log_addrs log_addrs;
+
+		if (!(adap->capabilities & CEC_CAP_LOG_ADDRS))
+			return -ENOTTY;
+		if (copy_from_user(&log_addrs, parg, sizeof(log_addrs)))
+			return -EFAULT;
+		log_addrs.flags = 0;
+		mutex_lock(&adap->lock);
+		if (adap->is_configuring)
+			err = -EBUSY;
+		else if (log_addrs.num_log_addrs && adap->is_configured)
+			err = -EBUSY;
+		else if (cec_is_busy(adap, fh))
+			err = -EBUSY;
+		else
+			err = __cec_s_log_addrs(adap, &log_addrs, block);
+		if (!err)
+			log_addrs = adap->log_addrs;
+		mutex_unlock(&adap->lock);
+		if (!err && copy_to_user(parg, &log_addrs, sizeof(log_addrs)))
+			return -EFAULT;
+		break;
+	}
+
+	case CEC_G_MODE: {
+		u32 mode = fh->mode_initiator | fh->mode_follower;
+
+		if (copy_to_user(parg, &mode, sizeof(mode)))
+			return -EFAULT;
+		break;
+	}
+
+	case CEC_S_MODE: {
+		u32 mode;
+		u8 mode_initiator;
+		u8 mode_follower;
+
+		if (copy_from_user(&mode, parg, sizeof(mode)))
+			return -EFAULT;
+		if (mode & ~(CEC_MODE_INITIATOR_MSK | CEC_MODE_FOLLOWER_MSK))
+			return -EINVAL;
+
+		mode_initiator = mode & CEC_MODE_INITIATOR_MSK;
+		mode_follower = mode & CEC_MODE_FOLLOWER_MSK;
+
+		if (mode_initiator > CEC_MODE_EXCL_INITIATOR ||
+		    mode_follower > CEC_MODE_MONITOR_ALL)
+			return -EINVAL;
+
+		if (mode_follower == CEC_MODE_MONITOR_ALL &&
+		    !(adap->capabilities & CEC_CAP_MONITOR_ALL))
+			return -EINVAL;
+
+		/* Follower modes should always be able to send CEC messages */
+		if ((mode_initiator == CEC_MODE_NO_INITIATOR ||
+		    !(adap->capabilities & CEC_CAP_TRANSMIT)) &&
+		    mode_follower >= CEC_MODE_FOLLOWER &&
+		    mode_follower <= CEC_MODE_EXCL_FOLLOWER_PASSTHRU)
+			return -EINVAL;
+
+		/* Monitor modes require CEC_MODE_NO_INITIATOR */
+		if (mode_initiator && mode_follower >= CEC_MODE_MONITOR)
+			return -EINVAL;
+
+		mutex_lock(&adap->lock);
+		/*
+		 * You can't become exclusive follower if someone else already
+		 * has that job.
+		 */
+		if ((mode_follower == CEC_MODE_EXCL_FOLLOWER ||
+		     mode_follower == CEC_MODE_EXCL_FOLLOWER_PASSTHRU) &&
+		    adap->cec_follower && adap->cec_follower != fh)
+			err = -EBUSY;
+		/*
+		 * You can't become exclusive initiator if someone else already
+		 * has that job.
+		 */
+		if (mode_initiator == CEC_MODE_EXCL_INITIATOR &&
+		    adap->cec_initiator && adap->cec_initiator != fh)
+			err = -EBUSY;
+
+		if (!err) {
+			bool old_mon_all = fh->mode_follower == CEC_MODE_MONITOR_ALL;
+			bool new_mon_all = mode_follower == CEC_MODE_MONITOR_ALL;
+
+			if (old_mon_all != new_mon_all) {
+				if (new_mon_all)
+					err = cec_monitor_all_cnt_inc(adap);
+				else
+					cec_monitor_all_cnt_dec(adap);
+			}
+		}
+
+		if (err) {
+			mutex_unlock(&adap->lock);
+			break;
+		}
+
+		if (fh->mode_follower == CEC_MODE_FOLLOWER)
+			adap->follower_cnt--;
+		if (mode_follower == CEC_MODE_FOLLOWER)
+			adap->follower_cnt++;
+		if (mode_follower == CEC_MODE_EXCL_FOLLOWER ||
+		    mode_follower == CEC_MODE_EXCL_FOLLOWER_PASSTHRU) {
+			adap->passthrough =
+				mode_follower == CEC_MODE_EXCL_FOLLOWER_PASSTHRU;
+			adap->cec_follower = fh;
+		} else if (adap->cec_follower == fh) {
+			adap->passthrough = false;
+			adap->cec_follower = NULL;
+		}
+		if (mode_initiator == CEC_MODE_EXCL_INITIATOR)
+			adap->cec_initiator = fh;
+		else if (adap->cec_initiator == fh)
+			adap->cec_initiator = NULL;
+		fh->mode_initiator = mode_initiator;
+		fh->mode_follower = mode_follower;
+		mutex_unlock(&adap->lock);
+		break;
+	}
+
+	default:
+		return -ENOTTY;
+	}
+	return err;
+}
+
+static int cec_open(struct inode *inode, struct file *filp)
+{
+	struct cec_devnode *devnode =
+		container_of(inode->i_cdev, struct cec_devnode, cdev);
+	struct cec_adapter *adap = to_cec_adapter(devnode);
+	struct cec_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+	/*
+	 * Initial events that are automatically sent when the cec device is
+	 * opened.
+	 */
+	struct cec_event ev_state = {
+		.event = CEC_EVENT_STATE_CHANGE,
+		.flags = CEC_EVENT_FL_INITIAL_STATE,
+	};
+	int ret;
+
+	if (fh == NULL)
+		return -ENOMEM;
+
+	ret = cec_queue_event_init(fh);
+
+	if (ret) {
+		kfree(fh);
+		return ret;
+	}
+
+	INIT_LIST_HEAD(&fh->msgs);
+	INIT_LIST_HEAD(&fh->xfer_list);
+	mutex_init(&fh->lock);
+	init_waitqueue_head(&fh->wait);
+
+	fh->mode_initiator = CEC_MODE_INITIATOR;
+	fh->adap = adap;
+
+	/*
+	 * Check if the cec device is available. This needs to be done with
+	 * the cec_devnode_lock held to prevent an open/unregister race:
+	 * without the lock, the device could be unregistered and freed between
+	 * the devnode->registered check and get_device() calls, leading to
+	 * a crash.
+	 */
+	mutex_lock(&cec_devnode_lock);
+	/*
+	 * return ENXIO if the cec device has been removed
+	 * already or if it is not registered anymore.
+	 */
+	if (!devnode->registered) {
+		mutex_unlock(&cec_devnode_lock);
+		cec_queue_event_free(fh);
+		kfree(fh);
+		return -ENXIO;
+	}
+	/* and increase the device refcount */
+	get_device(&devnode->dev);
+	mutex_unlock(&cec_devnode_lock);
+
+	filp->private_data = fh;
+
+	mutex_lock(&devnode->fhs_lock);
+	/* Queue up initial state events */
+	ev_state.state_change.phys_addr = adap->phys_addr;
+	ev_state.state_change.log_addr_mask = adap->log_addrs.log_addr_mask;
+	cec_queue_event_fh(fh, &ev_state, 0);
+
+	list_add(&fh->list, &devnode->fhs);
+	mutex_unlock(&devnode->fhs_lock);
+
+	return 0;
+}
+
+/* Override for the release function */
+static int cec_release(struct inode *inode, struct file *filp)
+{
+	struct cec_devnode *devnode = cec_devnode_data(filp);
+	struct cec_adapter *adap = to_cec_adapter(devnode);
+	struct cec_fh *fh = filp->private_data;
+
+	mutex_lock(&adap->lock);
+	if (adap->cec_initiator == fh)
+		adap->cec_initiator = NULL;
+	if (adap->cec_follower == fh) {
+		adap->cec_follower = NULL;
+		adap->passthrough = false;
+	}
+	if (fh->mode_follower == CEC_MODE_FOLLOWER)
+		adap->follower_cnt--;
+	if (fh->mode_follower == CEC_MODE_MONITOR_ALL)
+		cec_monitor_all_cnt_dec(adap);
+	mutex_unlock(&adap->lock);
+
+	mutex_lock(&devnode->fhs_lock);
+	list_del(&fh->list);
+	mutex_unlock(&devnode->fhs_lock);
+
+	/* Unhook pending transmits from this filehandle. */
+	mutex_lock(&adap->lock);
+	while (!list_empty(&fh->xfer_list)) {
+		struct cec_data *data =
+			list_first_entry(&fh->xfer_list, struct cec_data, xfer_list);
+
+		data->blocking = false;
+		data->fh = NULL;
+		list_del(&data->xfer_list);
+	}
+	mutex_unlock(&adap->lock);
+	while (!list_empty(&fh->msgs)) {
+		struct cec_msg_entry *entry =
+			list_first_entry(&fh->msgs, struct cec_msg_entry, list);
+
+		list_del(&entry->list);
+		kfree(entry);
+	}
+	cec_queue_event_free(fh);
+	kfree(fh);
+
+	/*
+	 * decrease the refcount unconditionally since the release()
+	 * return value is ignored.
+	 */
+	put_device(&devnode->dev);
+	filp->private_data = NULL;
+	return 0;
+}
+
+static const struct file_operations cec_devnode_fops = {
+	.owner = THIS_MODULE,
+	.open = cec_open,
+	.unlocked_ioctl = cec_ioctl,
+	.release = cec_release,
+	.poll = cec_poll,
+	.llseek = no_llseek,
+};
+
+/* Called when the last user of the cec device exits. */
+static void cec_devnode_release(struct device *cd)
+{
+	struct cec_devnode *devnode = to_cec_devnode(cd);
+
+	mutex_lock(&cec_devnode_lock);
+
+	/* Mark device node number as free */
+	clear_bit(devnode->minor, cec_devnode_nums);
+
+	mutex_unlock(&cec_devnode_lock);
+	cec_delete_adapter(to_cec_adapter(devnode));
+}
+
+static struct bus_type cec_bus_type = {
+	.name = CEC_NAME,
+};
+
+/**
+ * cec_devnode_register - register a cec device node
+ * @devnode: cec device node structure we want to register
+ *
+ * The registration code assigns minor numbers and registers the new device node
+ * with the kernel. An error is returned if no free minor number can be found,
+ * or if the registration of the device node fails.
+ *
+ * Zero is returned on success.
+ *
+ * Note that if the cec_devnode_register call fails, the release() callback of
+ * the cec_devnode structure is *not* called, so the caller is responsible for
+ * freeing any data.
+ */
+static int __must_check cec_devnode_register(struct cec_devnode *devnode,
+		struct module *owner)
+{
+	int minor;
+	int ret;
+
+	/* Initialization */
+	INIT_LIST_HEAD(&devnode->fhs);
+	mutex_init(&devnode->fhs_lock);
+
+	/* Part 1: Find a free minor number */
+	mutex_lock(&cec_devnode_lock);
+	minor = find_next_zero_bit(cec_devnode_nums, CEC_NUM_DEVICES, 0);
+	if (minor == CEC_NUM_DEVICES) {
+		mutex_unlock(&cec_devnode_lock);
+		pr_err("could not get a free minor\n");
+		return -ENFILE;
+	}
+
+	set_bit(minor, cec_devnode_nums);
+	mutex_unlock(&cec_devnode_lock);
+
+	devnode->minor = minor;
+	devnode->dev.bus = &cec_bus_type;
+	devnode->dev.devt = MKDEV(MAJOR(cec_dev_t), minor);
+	devnode->dev.release = cec_devnode_release;
+	devnode->dev.parent = devnode->parent;
+	dev_set_name(&devnode->dev, "cec%d", devnode->minor);
+	device_initialize(&devnode->dev);
+
+	/* Part 2: Initialize and register the character device */
+	cdev_init(&devnode->cdev, &cec_devnode_fops);
+	devnode->cdev.kobj.parent = &devnode->dev.kobj;
+	devnode->cdev.owner = owner;
+
+	ret = cdev_add(&devnode->cdev, devnode->dev.devt, 1);
+	if (ret < 0) {
+		pr_err("%s: cdev_add failed\n", __func__);
+		goto clr_bit;
+	}
+
+	ret = device_add(&devnode->dev);
+	if (ret)
+		goto cdev_del;
+
+	devnode->registered = true;
+	return 0;
+
+cdev_del:
+	cdev_del(&devnode->cdev);
+clr_bit:
+	clear_bit(devnode->minor, cec_devnode_nums);
+	put_device(&devnode->dev);
+	return ret;
+}
+
+/**
+ * cec_devnode_unregister - unregister a cec device node
+ * @devnode: the device node to unregister
+ *
+ * This unregisters the passed device. Future open calls will be met with
+ * errors.
+ *
+ * This function can safely be called if the device node has never been
+ * registered or has already been unregistered.
+ */
+static void cec_devnode_unregister(struct cec_devnode *devnode)
+{
+	struct cec_fh *fh;
+
+	/* Check if devnode was never registered or already unregistered */
+	if (!devnode->registered || devnode->unregistered)
+		return;
+
+	mutex_lock(&devnode->fhs_lock);
+	list_for_each_entry(fh, &devnode->fhs, list)
+		wake_up_interruptible(&fh->wait);
+	mutex_unlock(&devnode->fhs_lock);
+
+	devnode->registered = false;
+	devnode->unregistered = true;
+	device_del(&devnode->dev);
+	cdev_del(&devnode->cdev);
+	put_device(&devnode->dev);
+}
+
+struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
+	       void *priv, const char *name, u32 caps, u8 available_las,
+	       struct device *parent)
+{
+	struct cec_adapter *adap;
+	int res;
+
+	if (WARN_ON(!parent))
+		return ERR_PTR(-EINVAL);
+	if (WARN_ON(!caps))
+		return ERR_PTR(-EINVAL);
+	if (WARN_ON(!ops))
+		return ERR_PTR(-EINVAL);
+	if (WARN_ON(!available_las || available_las > CEC_MAX_LOG_ADDRS))
+		return ERR_PTR(-EINVAL);
+	adap = kzalloc(sizeof(*adap), GFP_KERNEL);
+	if (adap == NULL)
+		return ERR_PTR(-ENOMEM);
+	adap->owner = parent->driver->owner;
+	adap->devnode.parent = parent;
+	strlcpy(adap->name, name, sizeof(adap->name));
+	adap->phys_addr = CEC_PHYS_ADDR_INVALID;
+	adap->log_addrs.cec_version = CEC_OP_CEC_VERSION_2_0;
+	adap->log_addrs.vendor_id = CEC_VENDOR_ID_NONE;
+	adap->capabilities = caps;
+	adap->available_log_addrs = available_las;
+	adap->sequence = 0;
+	adap->ops = ops;
+	adap->priv = priv;
+	memset(adap->phys_addrs, 0xff, sizeof(adap->phys_addrs));
+	mutex_init(&adap->lock);
+	INIT_LIST_HEAD(&adap->transmit_queue);
+	INIT_LIST_HEAD(&adap->wait_queue);
+	init_waitqueue_head(&adap->kthread_waitq);
+
+	adap->kthread = kthread_run(cec_thread_func, adap, "cec-%s", name);
+	if (IS_ERR(adap->kthread)) {
+		pr_err("cec-%s: kernel_thread() failed\n", name);
+		res = PTR_ERR(adap->kthread);
+		kfree(adap);
+		return ERR_PTR(res);
+	}
+
+	if (!(caps & CEC_CAP_RC))
+		return adap;
+
+#if IS_ENABLED(CONFIG_RC_CORE)
+	/* Prepare the RC input device */
+	adap->rc = rc_allocate_device();
+	if (!adap->rc) {
+		pr_err("cec-%s: failed to allocate memory for rc_dev\n",
+		       name);
+		kthread_stop(adap->kthread);
+		kfree(adap);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	snprintf(adap->input_name, sizeof(adap->input_name),
+		 "RC for %s", name);
+	snprintf(adap->input_phys, sizeof(adap->input_phys),
+		 "%s/input0", name);
+
+	adap->rc->input_name = adap->input_name;
+	adap->rc->input_phys = adap->input_phys;
+	adap->rc->input_id.bustype = BUS_CEC;
+	adap->rc->input_id.vendor = 0;
+	adap->rc->input_id.product = 0;
+	adap->rc->input_id.version = 1;
+	adap->rc->dev.parent = parent;
+	adap->rc->driver_type = RC_DRIVER_SCANCODE;
+	adap->rc->allowed_protocols = RC_BIT_CEC;
+	adap->rc->priv = adap;
+	adap->rc->map_name = RC_MAP_CEC;
+	adap->rc->timeout = MS_TO_NS(100);
+#else
+	adap->capabilities &= ~CEC_CAP_RC;
+#endif
+	return adap;
+}
+EXPORT_SYMBOL_GPL(cec_allocate_adapter);
+
+int cec_register_adapter(struct cec_adapter *adap)
+{
+	int res;
+
+	if (IS_ERR_OR_NULL(adap))
+		return 0;
+
+#if IS_ENABLED(CONFIG_RC_CORE)
+	if (adap->capabilities & CEC_CAP_RC) {
+		res = rc_register_device(adap->rc);
+
+		if (res) {
+			pr_err("cec-%s: failed to prepare input device\n",
+			       adap->name);
+			rc_free_device(adap->rc);
+			adap->rc = NULL;
+			return res;
+		}
+	}
+#endif
+
+	res = cec_devnode_register(&adap->devnode, adap->owner);
+#if IS_ENABLED(CONFIG_RC_CORE)
+	if (res) {
+		/* Note: rc_unregister also calls rc_free */
+		rc_unregister_device(adap->rc);
+		adap->rc = NULL;
+		return res;
+	}
+#endif
+
+	dev_set_drvdata(&adap->devnode.dev, adap);
+	if (top_cec_dir == NULL)
+		return 0;
+
+	adap->cec_dir = debugfs_create_dir(dev_name(&adap->devnode.dev), top_cec_dir);
+	if (IS_ERR_OR_NULL(adap->cec_dir)) {
+		pr_warn("cec-%s: Failed to create debugfs dir\n", adap->name);
+		return 0;
+	}
+	adap->status_file = debugfs_create_devm_seqfile(&adap->devnode.dev,
+		"status", adap->cec_dir, cec_status);
+	if (IS_ERR_OR_NULL(adap->status_file)) {
+		pr_warn("cec-%s: Failed to create status file\n", adap->name);
+		debugfs_remove_recursive(adap->cec_dir);
+		adap->cec_dir = NULL;
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cec_register_adapter);
+
+void cec_unregister_adapter(struct cec_adapter *adap)
+{
+	if (IS_ERR_OR_NULL(adap))
+		return;
+
+#if IS_ENABLED(CONFIG_RC_CORE)
+	/* Note: rc_unregister also calls rc_free */
+	rc_unregister_device(adap->rc);
+	adap->rc = NULL;
+#endif
+	debugfs_remove_recursive(adap->cec_dir);
+	cec_devnode_unregister(&adap->devnode);
+}
+EXPORT_SYMBOL_GPL(cec_unregister_adapter);
+
+void cec_delete_adapter(struct cec_adapter *adap)
+{
+	if (IS_ERR_OR_NULL(adap))
+		return;
+	mutex_lock(&adap->lock);
+	__cec_s_phys_addr(adap, CEC_PHYS_ADDR_INVALID, false);
+	mutex_unlock(&adap->lock);
+	kthread_stop(adap->kthread);
+	if (adap->kthread_config)
+		kthread_stop(adap->kthread_config);
+#if IS_ENABLED(CONFIG_RC_CORE)
+	if (adap->rc)
+		rc_free_device(adap->rc);
+#endif
+	kfree(adap);
+}
+EXPORT_SYMBOL_GPL(cec_delete_adapter);
+
+/*
+ *	Initialise cec for linux
+ */
+static int __init cec_devnode_init(void)
+{
+	int ret;
+
+	pr_info("Linux cec interface: v0.10\n");
+	ret = alloc_chrdev_region(&cec_dev_t, 0, CEC_NUM_DEVICES,
+				  CEC_NAME);
+	if (ret < 0) {
+		pr_warn("cec: unable to allocate major\n");
+		return ret;
+	}
+
+	top_cec_dir = debugfs_create_dir("cec", NULL);
+	if (IS_ERR_OR_NULL(top_cec_dir)) {
+		pr_warn("cec: Failed to create debugfs cec dir\n");
+		top_cec_dir = NULL;
+	}
+
+	ret = bus_register(&cec_bus_type);
+	if (ret < 0) {
+		unregister_chrdev_region(cec_dev_t, CEC_NUM_DEVICES);
+		pr_warn("cec: bus_register failed\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static void __exit cec_devnode_exit(void)
+{
+	debugfs_remove_recursive(top_cec_dir);
+	bus_unregister(&cec_bus_type);
+	unregister_chrdev_region(cec_dev_t, CEC_NUM_DEVICES);
+}
+
+subsys_initcall(cec_devnode_init);
+module_exit(cec_devnode_exit)
+
+MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>");
+MODULE_DESCRIPTION("Device node registration for cec drivers");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/cec-funcs.h b/include/linux/cec-funcs.h
new file mode 100644
index 0000000..25c37bb
--- /dev/null
+++ b/include/linux/cec-funcs.h
@@ -0,0 +1,1871 @@
+/*
+ * cec - HDMI Consumer Electronics Control message functions
+ *
+ * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _CEC_UAPI_FUNCS_H
+#define _CEC_UAPI_FUNCS_H
+
+#include <linux/cec.h>
+
+/* One Touch Play Feature */
+static inline void cec_msg_active_source(struct cec_msg *msg, __u16 phys_addr)
+{
+	msg->len = 4;
+	msg->msg[0] |= 0xf; /* broadcast */
+	msg->msg[1] = CEC_MSG_ACTIVE_SOURCE;
+	msg->msg[2] = phys_addr >> 8;
+	msg->msg[3] = phys_addr & 0xff;
+}
+
+static inline void cec_ops_active_source(const struct cec_msg *msg,
+					 __u16 *phys_addr)
+{
+	*phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+}
+
+static inline void cec_msg_image_view_on(struct cec_msg *msg)
+{
+	msg->len = 2;
+	msg->msg[1] = CEC_MSG_IMAGE_VIEW_ON;
+}
+
+static inline void cec_msg_text_view_on(struct cec_msg *msg)
+{
+	msg->len = 2;
+	msg->msg[1] = CEC_MSG_TEXT_VIEW_ON;
+}
+
+
+/* Routing Control Feature */
+static inline void cec_msg_inactive_source(struct cec_msg *msg,
+					   __u16 phys_addr)
+{
+	msg->len = 4;
+	msg->msg[1] = CEC_MSG_INACTIVE_SOURCE;
+	msg->msg[2] = phys_addr >> 8;
+	msg->msg[3] = phys_addr & 0xff;
+}
+
+static inline void cec_ops_inactive_source(const struct cec_msg *msg,
+					   __u16 *phys_addr)
+{
+	*phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+}
+
+static inline void cec_msg_request_active_source(struct cec_msg *msg,
+						 bool reply)
+{
+	msg->len = 2;
+	msg->msg[0] |= 0xf; /* broadcast */
+	msg->msg[1] = CEC_MSG_REQUEST_ACTIVE_SOURCE;
+	msg->reply = reply ? CEC_MSG_ACTIVE_SOURCE : 0;
+}
+
+static inline void cec_msg_routing_information(struct cec_msg *msg,
+					       __u16 phys_addr)
+{
+	msg->len = 4;
+	msg->msg[0] |= 0xf; /* broadcast */
+	msg->msg[1] = CEC_MSG_ROUTING_INFORMATION;
+	msg->msg[2] = phys_addr >> 8;
+	msg->msg[3] = phys_addr & 0xff;
+}
+
+static inline void cec_ops_routing_information(const struct cec_msg *msg,
+					       __u16 *phys_addr)
+{
+	*phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+}
+
+static inline void cec_msg_routing_change(struct cec_msg *msg,
+					  bool reply,
+					  __u16 orig_phys_addr,
+					  __u16 new_phys_addr)
+{
+	msg->len = 6;
+	msg->msg[0] |= 0xf; /* broadcast */
+	msg->msg[1] = CEC_MSG_ROUTING_CHANGE;
+	msg->msg[2] = orig_phys_addr >> 8;
+	msg->msg[3] = orig_phys_addr & 0xff;
+	msg->msg[4] = new_phys_addr >> 8;
+	msg->msg[5] = new_phys_addr & 0xff;
+	msg->reply = reply ? CEC_MSG_ROUTING_INFORMATION : 0;
+}
+
+static inline void cec_ops_routing_change(const struct cec_msg *msg,
+					  __u16 *orig_phys_addr,
+					  __u16 *new_phys_addr)
+{
+	*orig_phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+	*new_phys_addr = (msg->msg[4] << 8) | msg->msg[5];
+}
+
+static inline void cec_msg_set_stream_path(struct cec_msg *msg, __u16 phys_addr)
+{
+	msg->len = 4;
+	msg->msg[0] |= 0xf; /* broadcast */
+	msg->msg[1] = CEC_MSG_SET_STREAM_PATH;
+	msg->msg[2] = phys_addr >> 8;
+	msg->msg[3] = phys_addr & 0xff;
+}
+
+static inline void cec_ops_set_stream_path(const struct cec_msg *msg,
+					   __u16 *phys_addr)
+{
+	*phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+}
+
+
+/* Standby Feature */
+static inline void cec_msg_standby(struct cec_msg *msg)
+{
+	msg->len = 2;
+	msg->msg[1] = CEC_MSG_STANDBY;
+}
+
+
+/* One Touch Record Feature */
+static inline void cec_msg_record_off(struct cec_msg *msg)
+{
+	msg->len = 2;
+	msg->msg[1] = CEC_MSG_RECORD_OFF;
+}
+
+struct cec_op_arib_data {
+	__u16 transport_id;
+	__u16 service_id;
+	__u16 orig_network_id;
+};
+
+struct cec_op_atsc_data {
+	__u16 transport_id;
+	__u16 program_number;
+};
+
+struct cec_op_dvb_data {
+	__u16 transport_id;
+	__u16 service_id;
+	__u16 orig_network_id;
+};
+
+struct cec_op_channel_data {
+	__u8 channel_number_fmt;
+	__u16 major;
+	__u16 minor;
+};
+
+struct cec_op_digital_service_id {
+	__u8 service_id_method;
+	__u8 dig_bcast_system;
+	union {
+		struct cec_op_arib_data arib;
+		struct cec_op_atsc_data atsc;
+		struct cec_op_dvb_data dvb;
+		struct cec_op_channel_data channel;
+	};
+};
+
+struct cec_op_record_src {
+	__u8 type;
+	union {
+		struct cec_op_digital_service_id digital;
+		struct {
+			__u8 ana_bcast_type;
+			__u16 ana_freq;
+			__u8 bcast_system;
+		} analog;
+		struct {
+			__u8 plug;
+		} ext_plug;
+		struct {
+			__u16 phys_addr;
+		} ext_phys_addr;
+	};
+};
+
+static inline void cec_set_digital_service_id(__u8 *msg,
+	      const struct cec_op_digital_service_id *digital)
+{
+	*msg++ = (digital->service_id_method << 7) | digital->dig_bcast_system;
+	if (digital->service_id_method == CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL) {
+		*msg++ = (digital->channel.channel_number_fmt << 2) |
+			 (digital->channel.major >> 8);
+		*msg++ = digital->channel.major && 0xff;
+		*msg++ = digital->channel.minor >> 8;
+		*msg++ = digital->channel.minor & 0xff;
+		*msg++ = 0;
+		*msg++ = 0;
+		return;
+	}
+	switch (digital->dig_bcast_system) {
+	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_GEN:
+	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_CABLE:
+	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_SAT:
+	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_T:
+		*msg++ = digital->atsc.transport_id >> 8;
+		*msg++ = digital->atsc.transport_id & 0xff;
+		*msg++ = digital->atsc.program_number >> 8;
+		*msg++ = digital->atsc.program_number & 0xff;
+		*msg++ = 0;
+		*msg++ = 0;
+		break;
+	default:
+		*msg++ = digital->dvb.transport_id >> 8;
+		*msg++ = digital->dvb.transport_id & 0xff;
+		*msg++ = digital->dvb.service_id >> 8;
+		*msg++ = digital->dvb.service_id & 0xff;
+		*msg++ = digital->dvb.orig_network_id >> 8;
+		*msg++ = digital->dvb.orig_network_id & 0xff;
+		break;
+	}
+}
+
+static inline void cec_get_digital_service_id(const __u8 *msg,
+	      struct cec_op_digital_service_id *digital)
+{
+	digital->service_id_method = msg[0] >> 7;
+	digital->dig_bcast_system = msg[0] & 0x7f;
+	if (digital->service_id_method == CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL) {
+		digital->channel.channel_number_fmt = msg[1] >> 2;
+		digital->channel.major = ((msg[1] & 3) << 6) | msg[2];
+		digital->channel.minor = (msg[3] << 8) | msg[4];
+		return;
+	}
+	digital->dvb.transport_id = (msg[1] << 8) | msg[2];
+	digital->dvb.service_id = (msg[3] << 8) | msg[4];
+	digital->dvb.orig_network_id = (msg[5] << 8) | msg[6];
+}
+
+static inline void cec_msg_record_on_own(struct cec_msg *msg)
+{
+	msg->len = 3;
+	msg->msg[1] = CEC_MSG_RECORD_ON;
+	msg->msg[2] = CEC_OP_RECORD_SRC_OWN;
+}
+
+static inline void cec_msg_record_on_digital(struct cec_msg *msg,
+			     const struct cec_op_digital_service_id *digital)
+{
+	msg->len = 10;
+	msg->msg[1] = CEC_MSG_RECORD_ON;
+	msg->msg[2] = CEC_OP_RECORD_SRC_DIGITAL;
+	cec_set_digital_service_id(msg->msg + 3, digital);
+}
+
+static inline void cec_msg_record_on_analog(struct cec_msg *msg,
+					    __u8 ana_bcast_type,
+					    __u16 ana_freq,
+					    __u8 bcast_system)
+{
+	msg->len = 7;
+	msg->msg[1] = CEC_MSG_RECORD_ON;
+	msg->msg[2] = CEC_OP_RECORD_SRC_ANALOG;
+	msg->msg[3] = ana_bcast_type;
+	msg->msg[4] = ana_freq >> 8;
+	msg->msg[5] = ana_freq & 0xff;
+	msg->msg[6] = bcast_system;
+}
+
+static inline void cec_msg_record_on_plug(struct cec_msg *msg,
+					  __u8 plug)
+{
+	msg->len = 4;
+	msg->msg[1] = CEC_MSG_RECORD_ON;
+	msg->msg[2] = CEC_OP_RECORD_SRC_EXT_PLUG;
+	msg->msg[3] = plug;
+}
+
+static inline void cec_msg_record_on_phys_addr(struct cec_msg *msg,
+					       __u16 phys_addr)
+{
+	msg->len = 5;
+	msg->msg[1] = CEC_MSG_RECORD_ON;
+	msg->msg[2] = CEC_OP_RECORD_SRC_EXT_PHYS_ADDR;
+	msg->msg[3] = phys_addr >> 8;
+	msg->msg[4] = phys_addr & 0xff;
+}
+
+static inline void cec_msg_record_on(struct cec_msg *msg,
+				     const struct cec_op_record_src *rec_src)
+{
+	switch (rec_src->type) {
+	case CEC_OP_RECORD_SRC_OWN:
+		cec_msg_record_on_own(msg);
+		break;
+	case CEC_OP_RECORD_SRC_DIGITAL:
+		cec_msg_record_on_digital(msg, &rec_src->digital);
+		break;
+	case CEC_OP_RECORD_SRC_ANALOG:
+		cec_msg_record_on_analog(msg,
+					 rec_src->analog.ana_bcast_type,
+					 rec_src->analog.ana_freq,
+					 rec_src->analog.bcast_system);
+		break;
+	case CEC_OP_RECORD_SRC_EXT_PLUG:
+		cec_msg_record_on_plug(msg, rec_src->ext_plug.plug);
+		break;
+	case CEC_OP_RECORD_SRC_EXT_PHYS_ADDR:
+		cec_msg_record_on_phys_addr(msg,
+					    rec_src->ext_phys_addr.phys_addr);
+		break;
+	}
+}
+
+static inline void cec_ops_record_on(const struct cec_msg *msg,
+				     struct cec_op_record_src *rec_src)
+{
+	rec_src->type = msg->msg[2];
+	switch (rec_src->type) {
+	case CEC_OP_RECORD_SRC_OWN:
+		break;
+	case CEC_OP_RECORD_SRC_DIGITAL:
+		cec_get_digital_service_id(msg->msg + 3, &rec_src->digital);
+		break;
+	case CEC_OP_RECORD_SRC_ANALOG:
+		rec_src->analog.ana_bcast_type = msg->msg[3];
+		rec_src->analog.ana_freq =
+			(msg->msg[4] << 8) | msg->msg[5];
+		rec_src->analog.bcast_system = msg->msg[6];
+		break;
+	case CEC_OP_RECORD_SRC_EXT_PLUG:
+		rec_src->ext_plug.plug = msg->msg[3];
+		break;
+	case CEC_OP_RECORD_SRC_EXT_PHYS_ADDR:
+		rec_src->ext_phys_addr.phys_addr =
+			(msg->msg[3] << 8) | msg->msg[4];
+		break;
+	}
+}
+
+static inline void cec_msg_record_status(struct cec_msg *msg, __u8 rec_status)
+{
+	msg->len = 3;
+	msg->msg[1] = CEC_MSG_RECORD_STATUS;
+	msg->msg[2] = rec_status;
+}
+
+static inline void cec_ops_record_status(const struct cec_msg *msg,
+					 __u8 *rec_status)
+{
+	*rec_status = msg->msg[2];
+}
+
+static inline void cec_msg_record_tv_screen(struct cec_msg *msg,
+					    bool reply)
+{
+	msg->len = 2;
+	msg->msg[1] = CEC_MSG_RECORD_TV_SCREEN;
+	msg->reply = reply ? CEC_MSG_RECORD_ON : 0;
+}
+
+
+/* Timer Programming Feature */
+static inline void cec_msg_timer_status(struct cec_msg *msg,
+					__u8 timer_overlap_warning,
+					__u8 media_info,
+					__u8 prog_info,
+					__u8 prog_error,
+					__u8 duration_hr,
+					__u8 duration_min)
+{
+	msg->len = 3;
+	msg->msg[1] = CEC_MSG_TIMER_STATUS;
+	msg->msg[2] = (timer_overlap_warning << 7) |
+		(media_info << 5) |
+		(prog_info ? 0x10 : 0) |
+		(prog_info ? prog_info : prog_error);
+	if (prog_info == CEC_OP_PROG_INFO_NOT_ENOUGH_SPACE ||
+	    prog_info == CEC_OP_PROG_INFO_MIGHT_NOT_BE_ENOUGH_SPACE ||
+	    prog_error == CEC_OP_PROG_ERROR_DUPLICATE) {
+		msg->len += 2;
+		msg->msg[3] = ((duration_hr / 10) << 4) | (duration_hr % 10);
+		msg->msg[4] = ((duration_min / 10) << 4) | (duration_min % 10);
+	}
+}
+
+static inline void cec_ops_timer_status(struct cec_msg *msg,
+					__u8 *timer_overlap_warning,
+					__u8 *media_info,
+					__u8 *prog_info,
+					__u8 *prog_error,
+					__u8 *duration_hr,
+					__u8 *duration_min)
+{
+	*timer_overlap_warning = msg->msg[2] >> 7;
+	*media_info = (msg->msg[2] >> 5) & 3;
+	if (msg->msg[2] & 0x10) {
+		*prog_info = msg->msg[2] & 0xf;
+		*prog_error = 0;
+	} else {
+		*prog_info = 0;
+		*prog_error = msg->msg[2] & 0xf;
+	}
+	if (*prog_info == CEC_OP_PROG_INFO_NOT_ENOUGH_SPACE ||
+	    *prog_info == CEC_OP_PROG_INFO_MIGHT_NOT_BE_ENOUGH_SPACE ||
+	    *prog_error == CEC_OP_PROG_ERROR_DUPLICATE) {
+		*duration_hr = (msg->msg[3] >> 4) * 10 + (msg->msg[3] & 0xf);
+		*duration_min = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf);
+	} else {
+		*duration_hr = *duration_min = 0;
+	}
+}
+
+static inline void cec_msg_timer_cleared_status(struct cec_msg *msg,
+						__u8 timer_cleared_status)
+{
+	msg->len = 3;
+	msg->msg[1] = CEC_MSG_TIMER_CLEARED_STATUS;
+	msg->msg[2] = timer_cleared_status;
+}
+
+static inline void cec_ops_timer_cleared_status(struct cec_msg *msg,
+						__u8 *timer_cleared_status)
+{
+	*timer_cleared_status = msg->msg[2];
+}
+
+static inline void cec_msg_clear_analogue_timer(struct cec_msg *msg,
+						bool reply,
+						__u8 day,
+						__u8 month,
+						__u8 start_hr,
+						__u8 start_min,
+						__u8 duration_hr,
+						__u8 duration_min,
+						__u8 recording_seq,
+						__u8 ana_bcast_type,
+						__u16 ana_freq,
+						__u8 bcast_system)
+{
+	msg->len = 13;
+	msg->msg[1] = CEC_MSG_CLEAR_ANALOGUE_TIMER;
+	msg->msg[2] = day;
+	msg->msg[3] = month;
+	/* Hours and minutes are in BCD format */
+	msg->msg[4] = ((start_hr / 10) << 4) | (start_hr % 10);
+	msg->msg[5] = ((start_min / 10) << 4) | (start_min % 10);
+	msg->msg[6] = ((duration_hr / 10) << 4) | (duration_hr % 10);
+	msg->msg[7] = ((duration_min / 10) << 4) | (duration_min % 10);
+	msg->msg[8] = recording_seq;
+	msg->msg[9] = ana_bcast_type;
+	msg->msg[10] = ana_freq >> 8;
+	msg->msg[11] = ana_freq & 0xff;
+	msg->msg[12] = bcast_system;
+	msg->reply = reply ? CEC_MSG_TIMER_CLEARED_STATUS : 0;
+}
+
+static inline void cec_ops_clear_analogue_timer(struct cec_msg *msg,
+						__u8 *day,
+						__u8 *month,
+						__u8 *start_hr,
+						__u8 *start_min,
+						__u8 *duration_hr,
+						__u8 *duration_min,
+						__u8 *recording_seq,
+						__u8 *ana_bcast_type,
+						__u16 *ana_freq,
+						__u8 *bcast_system)
+{
+	*day = msg->msg[2];
+	*month = msg->msg[3];
+	/* Hours and minutes are in BCD format */
+	*start_hr = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf);
+	*start_min = (msg->msg[5] >> 4) * 10 + (msg->msg[5] & 0xf);
+	*duration_hr = (msg->msg[6] >> 4) * 10 + (msg->msg[6] & 0xf);
+	*duration_min = (msg->msg[7] >> 4) * 10 + (msg->msg[7] & 0xf);
+	*recording_seq = msg->msg[8];
+	*ana_bcast_type = msg->msg[9];
+	*ana_freq = (msg->msg[10] << 8) | msg->msg[11];
+	*bcast_system = msg->msg[12];
+}
+
+static inline void cec_msg_clear_digital_timer(struct cec_msg *msg,
+					       bool reply,
+					       __u8 day,
+					       __u8 month,
+					       __u8 start_hr,
+					       __u8 start_min,
+					       __u8 duration_hr,
+					       __u8 duration_min,
+					       __u8 recording_seq,
+					       const struct cec_op_digital_service_id *digital)
+{
+	msg->len = 16;
+	msg->reply = reply ? CEC_MSG_TIMER_CLEARED_STATUS : 0;
+	msg->msg[1] = CEC_MSG_CLEAR_DIGITAL_TIMER;
+	msg->msg[2] = day;
+	msg->msg[3] = month;
+	/* Hours and minutes are in BCD format */
+	msg->msg[4] = ((start_hr / 10) << 4) | (start_hr % 10);
+	msg->msg[5] = ((start_min / 10) << 4) | (start_min % 10);
+	msg->msg[6] = ((duration_hr / 10) << 4) | (duration_hr % 10);
+	msg->msg[7] = ((duration_min / 10) << 4) | (duration_min % 10);
+	msg->msg[8] = recording_seq;
+	cec_set_digital_service_id(msg->msg + 9, digital);
+}
+
+static inline void cec_ops_clear_digital_timer(struct cec_msg *msg,
+					       __u8 *day,
+					       __u8 *month,
+					       __u8 *start_hr,
+					       __u8 *start_min,
+					       __u8 *duration_hr,
+					       __u8 *duration_min,
+					       __u8 *recording_seq,
+					       struct cec_op_digital_service_id *digital)
+{
+	*day = msg->msg[2];
+	*month = msg->msg[3];
+	/* Hours and minutes are in BCD format */
+	*start_hr = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf);
+	*start_min = (msg->msg[5] >> 4) * 10 + (msg->msg[5] & 0xf);
+	*duration_hr = (msg->msg[6] >> 4) * 10 + (msg->msg[6] & 0xf);
+	*duration_min = (msg->msg[7] >> 4) * 10 + (msg->msg[7] & 0xf);
+	*recording_seq = msg->msg[8];
+	cec_get_digital_service_id(msg->msg + 9, digital);
+}
+
+static inline void cec_msg_clear_ext_timer(struct cec_msg *msg,
+					   bool reply,
+					   __u8 day,
+					   __u8 month,
+					   __u8 start_hr,
+					   __u8 start_min,
+					   __u8 duration_hr,
+					   __u8 duration_min,
+					   __u8 recording_seq,
+					   __u8 ext_src_spec,
+					   __u8 plug,
+					   __u16 phys_addr)
+{
+	msg->len = 13;
+	msg->msg[1] = CEC_MSG_CLEAR_EXT_TIMER;
+	msg->msg[2] = day;
+	msg->msg[3] = month;
+	/* Hours and minutes are in BCD format */
+	msg->msg[4] = ((start_hr / 10) << 4) | (start_hr % 10);
+	msg->msg[5] = ((start_min / 10) << 4) | (start_min % 10);
+	msg->msg[6] = ((duration_hr / 10) << 4) | (duration_hr % 10);
+	msg->msg[7] = ((duration_min / 10) << 4) | (duration_min % 10);
+	msg->msg[8] = recording_seq;
+	msg->msg[9] = ext_src_spec;
+	msg->msg[10] = plug;
+	msg->msg[11] = phys_addr >> 8;
+	msg->msg[12] = phys_addr & 0xff;
+	msg->reply = reply ? CEC_MSG_TIMER_CLEARED_STATUS : 0;
+}
+
+static inline void cec_ops_clear_ext_timer(struct cec_msg *msg,
+					   __u8 *day,
+					   __u8 *month,
+					   __u8 *start_hr,
+					   __u8 *start_min,
+					   __u8 *duration_hr,
+					   __u8 *duration_min,
+					   __u8 *recording_seq,
+					   __u8 *ext_src_spec,
+					   __u8 *plug,
+					   __u16 *phys_addr)
+{
+	*day = msg->msg[2];
+	*month = msg->msg[3];
+	/* Hours and minutes are in BCD format */
+	*start_hr = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf);
+	*start_min = (msg->msg[5] >> 4) * 10 + (msg->msg[5] & 0xf);
+	*duration_hr = (msg->msg[6] >> 4) * 10 + (msg->msg[6] & 0xf);
+	*duration_min = (msg->msg[7] >> 4) * 10 + (msg->msg[7] & 0xf);
+	*recording_seq = msg->msg[8];
+	*ext_src_spec = msg->msg[9];
+	*plug = msg->msg[10];
+	*phys_addr = (msg->msg[11] << 8) | msg->msg[12];
+}
+
+static inline void cec_msg_set_analogue_timer(struct cec_msg *msg,
+					      bool reply,
+					      __u8 day,
+					      __u8 month,
+					      __u8 start_hr,
+					      __u8 start_min,
+					      __u8 duration_hr,
+					      __u8 duration_min,
+					      __u8 recording_seq,
+					      __u8 ana_bcast_type,
+					      __u16 ana_freq,
+					      __u8 bcast_system)
+{
+	msg->len = 13;
+	msg->msg[1] = CEC_MSG_SET_ANALOGUE_TIMER;
+	msg->msg[2] = day;
+	msg->msg[3] = month;
+	/* Hours and minutes are in BCD format */
+	msg->msg[4] = ((start_hr / 10) << 4) | (start_hr % 10);
+	msg->msg[5] = ((start_min / 10) << 4) | (start_min % 10);
+	msg->msg[6] = ((duration_hr / 10) << 4) | (duration_hr % 10);
+	msg->msg[7] = ((duration_min / 10) << 4) | (duration_min % 10);
+	msg->msg[8] = recording_seq;
+	msg->msg[9] = ana_bcast_type;
+	msg->msg[10] = ana_freq >> 8;
+	msg->msg[11] = ana_freq & 0xff;
+	msg->msg[12] = bcast_system;
+	msg->reply = reply ? CEC_MSG_TIMER_STATUS : 0;
+}
+
+static inline void cec_ops_set_analogue_timer(struct cec_msg *msg,
+					      __u8 *day,
+					      __u8 *month,
+					      __u8 *start_hr,
+					      __u8 *start_min,
+					      __u8 *duration_hr,
+					      __u8 *duration_min,
+					      __u8 *recording_seq,
+					      __u8 *ana_bcast_type,
+					      __u16 *ana_freq,
+					      __u8 *bcast_system)
+{
+	*day = msg->msg[2];
+	*month = msg->msg[3];
+	/* Hours and minutes are in BCD format */
+	*start_hr = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf);
+	*start_min = (msg->msg[5] >> 4) * 10 + (msg->msg[5] & 0xf);
+	*duration_hr = (msg->msg[6] >> 4) * 10 + (msg->msg[6] & 0xf);
+	*duration_min = (msg->msg[7] >> 4) * 10 + (msg->msg[7] & 0xf);
+	*recording_seq = msg->msg[8];
+	*ana_bcast_type = msg->msg[9];
+	*ana_freq = (msg->msg[10] << 8) | msg->msg[11];
+	*bcast_system = msg->msg[12];
+}
+
+static inline void cec_msg_set_digital_timer(struct cec_msg *msg,
+					     bool reply,
+					     __u8 day,
+					     __u8 month,
+					     __u8 start_hr,
+					     __u8 start_min,
+					     __u8 duration_hr,
+					     __u8 duration_min,
+					     __u8 recording_seq,
+					     const struct cec_op_digital_service_id *digital)
+{
+	msg->len = 16;
+	msg->reply = reply ? CEC_MSG_TIMER_STATUS : 0;
+	msg->msg[1] = CEC_MSG_SET_DIGITAL_TIMER;
+	msg->msg[2] = day;
+	msg->msg[3] = month;
+	/* Hours and minutes are in BCD format */
+	msg->msg[4] = ((start_hr / 10) << 4) | (start_hr % 10);
+	msg->msg[5] = ((start_min / 10) << 4) | (start_min % 10);
+	msg->msg[6] = ((duration_hr / 10) << 4) | (duration_hr % 10);
+	msg->msg[7] = ((duration_min / 10) << 4) | (duration_min % 10);
+	msg->msg[8] = recording_seq;
+	cec_set_digital_service_id(msg->msg + 9, digital);
+}
+
+static inline void cec_ops_set_digital_timer(struct cec_msg *msg,
+					     __u8 *day,
+					     __u8 *month,
+					     __u8 *start_hr,
+					     __u8 *start_min,
+					     __u8 *duration_hr,
+					     __u8 *duration_min,
+					     __u8 *recording_seq,
+					     struct cec_op_digital_service_id *digital)
+{
+	*day = msg->msg[2];
+	*month = msg->msg[3];
+	/* Hours and minutes are in BCD format */
+	*start_hr = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf);
+	*start_min = (msg->msg[5] >> 4) * 10 + (msg->msg[5] & 0xf);
+	*duration_hr = (msg->msg[6] >> 4) * 10 + (msg->msg[6] & 0xf);
+	*duration_min = (msg->msg[7] >> 4) * 10 + (msg->msg[7] & 0xf);
+	*recording_seq = msg->msg[8];
+	cec_get_digital_service_id(msg->msg + 9, digital);
+}
+
+static inline void cec_msg_set_ext_timer(struct cec_msg *msg,
+					 bool reply,
+					 __u8 day,
+					 __u8 month,
+					 __u8 start_hr,
+					 __u8 start_min,
+					 __u8 duration_hr,
+					 __u8 duration_min,
+					 __u8 recording_seq,
+					 __u8 ext_src_spec,
+					 __u8 plug,
+					 __u16 phys_addr)
+{
+	msg->len = 13;
+	msg->msg[1] = CEC_MSG_SET_EXT_TIMER;
+	msg->msg[2] = day;
+	msg->msg[3] = month;
+	/* Hours and minutes are in BCD format */
+	msg->msg[4] = ((start_hr / 10) << 4) | (start_hr % 10);
+	msg->msg[5] = ((start_min / 10) << 4) | (start_min % 10);
+	msg->msg[6] = ((duration_hr / 10) << 4) | (duration_hr % 10);
+	msg->msg[7] = ((duration_min / 10) << 4) | (duration_min % 10);
+	msg->msg[8] = recording_seq;
+	msg->msg[9] = ext_src_spec;
+	msg->msg[10] = plug;
+	msg->msg[11] = phys_addr >> 8;
+	msg->msg[12] = phys_addr & 0xff;
+	msg->reply = reply ? CEC_MSG_TIMER_STATUS : 0;
+}
+
+static inline void cec_ops_set_ext_timer(struct cec_msg *msg,
+					 __u8 *day,
+					 __u8 *month,
+					 __u8 *start_hr,
+					 __u8 *start_min,
+					 __u8 *duration_hr,
+					 __u8 *duration_min,
+					 __u8 *recording_seq,
+					 __u8 *ext_src_spec,
+					 __u8 *plug,
+					 __u16 *phys_addr)
+{
+	*day = msg->msg[2];
+	*month = msg->msg[3];
+	/* Hours and minutes are in BCD format */
+	*start_hr = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf);
+	*start_min = (msg->msg[5] >> 4) * 10 + (msg->msg[5] & 0xf);
+	*duration_hr = (msg->msg[6] >> 4) * 10 + (msg->msg[6] & 0xf);
+	*duration_min = (msg->msg[7] >> 4) * 10 + (msg->msg[7] & 0xf);
+	*recording_seq = msg->msg[8];
+	*ext_src_spec = msg->msg[9];
+	*plug = msg->msg[10];
+	*phys_addr = (msg->msg[11] << 8) | msg->msg[12];
+}
+
+static inline void cec_msg_set_timer_program_title(struct cec_msg *msg,
+						   const char *prog_title)
+{
+	unsigned len = strlen(prog_title);
+
+	if (len > 14)
+		len = 14;
+	msg->len = 2 + len;
+	msg->msg[1] = CEC_MSG_SET_TIMER_PROGRAM_TITLE;
+	memcpy(msg->msg + 2, prog_title, len);
+}
+
+static inline void cec_ops_set_timer_program_title(const struct cec_msg *msg,
+						   char *prog_title)
+{
+	unsigned len = msg->len - 2;
+
+	if (len > 14)
+		len = 14;
+	memcpy(prog_title, msg->msg + 2, len);
+	prog_title[len] = '\0';
+}
+
+/* System Information Feature */
+static inline void cec_msg_cec_version(struct cec_msg *msg, __u8 cec_version)
+{
+	msg->len = 3;
+	msg->msg[1] = CEC_MSG_CEC_VERSION;
+	msg->msg[2] = cec_version;
+}
+
+static inline void cec_ops_cec_version(const struct cec_msg *msg,
+				       __u8 *cec_version)
+{
+	*cec_version = msg->msg[2];
+}
+
+static inline void cec_msg_get_cec_version(struct cec_msg *msg,
+					   bool reply)
+{
+	msg->len = 2;
+	msg->msg[1] = CEC_MSG_GET_CEC_VERSION;
+	msg->reply = reply ? CEC_MSG_CEC_VERSION : 0;
+}
+
+static inline void cec_msg_report_physical_addr(struct cec_msg *msg,
+						__u16 phys_addr, __u8 prim_devtype)
+{
+	msg->len = 5;
+	msg->msg[0] |= 0xf; /* broadcast */
+	msg->msg[1] = CEC_MSG_REPORT_PHYSICAL_ADDR;
+	msg->msg[2] = phys_addr >> 8;
+	msg->msg[3] = phys_addr & 0xff;
+	msg->msg[4] = prim_devtype;
+}
+
+static inline void cec_ops_report_physical_addr(const struct cec_msg *msg,
+						__u16 *phys_addr, __u8 *prim_devtype)
+{
+	*phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+	*prim_devtype = msg->msg[4];
+}
+
+static inline void cec_msg_give_physical_addr(struct cec_msg *msg,
+					      bool reply)
+{
+	msg->len = 2;
+	msg->msg[1] = CEC_MSG_GIVE_PHYSICAL_ADDR;
+	msg->reply = reply ? CEC_MSG_REPORT_PHYSICAL_ADDR : 0;
+}
+
+static inline void cec_msg_set_menu_language(struct cec_msg *msg,
+					     const char *language)
+{
+	msg->len = 5;
+	msg->msg[0] |= 0xf; /* broadcast */
+	msg->msg[1] = CEC_MSG_SET_MENU_LANGUAGE;
+	memcpy(msg->msg + 2, language, 3);
+}
+
+static inline void cec_ops_set_menu_language(struct cec_msg *msg,
+					     char *language)
+{
+	memcpy(language, msg->msg + 2, 3);
+}
+
+static inline void cec_msg_get_menu_language(struct cec_msg *msg,
+					     bool reply)
+{
+	msg->len = 2;
+	msg->msg[1] = CEC_MSG_GET_MENU_LANGUAGE;
+	msg->reply = reply ? CEC_MSG_SET_MENU_LANGUAGE : 0;
+}
+
+/*
+ * Assumes a single RC Profile byte and a single Device Features byte,
+ * i.e. no extended features are supported by this helper function.
+ *
+ * As of CEC 2.0 no extended features are defined, should those be added
+ * in the future, then this function needs to be adapted or a new function
+ * should be added.
+ */
+static inline void cec_msg_report_features(struct cec_msg *msg,
+					   __u8 cec_version, __u8 all_device_types,
+					   __u8 rc_profile, __u8 dev_features)
+{
+	msg->len = 6;
+	msg->msg[0] |= 0xf; /* broadcast */
+	msg->msg[1] = CEC_MSG_REPORT_FEATURES;
+	msg->msg[2] = cec_version;
+	msg->msg[3] = all_device_types;
+	msg->msg[4] = rc_profile;
+	msg->msg[5] = dev_features;
+}
+
+static inline void cec_ops_report_features(const struct cec_msg *msg,
+					   __u8 *cec_version, __u8 *all_device_types,
+					   const __u8 **rc_profile, const __u8 **dev_features)
+{
+	const __u8 *p = &msg->msg[4];
+
+	*cec_version = msg->msg[2];
+	*all_device_types = msg->msg[3];
+	*rc_profile = p;
+	while (p < &msg->msg[14] && (*p & CEC_OP_FEAT_EXT))
+		p++;
+	if (!(*p & CEC_OP_FEAT_EXT)) {
+		*dev_features = p + 1;
+		while (p < &msg->msg[15] && (*p & CEC_OP_FEAT_EXT))
+			p++;
+	}
+	if (*p & CEC_OP_FEAT_EXT)
+		*rc_profile = *dev_features = NULL;
+}
+
+static inline void cec_msg_give_features(struct cec_msg *msg,
+					 bool reply)
+{
+	msg->len = 2;
+	msg->msg[1] = CEC_MSG_GIVE_FEATURES;
+	msg->reply = reply ? CEC_MSG_REPORT_FEATURES : 0;
+}
+
+/* Deck Control Feature */
+static inline void cec_msg_deck_control(struct cec_msg *msg,
+					__u8 deck_control_mode)
+{
+	msg->len = 3;
+	msg->msg[1] = CEC_MSG_DECK_CONTROL;
+	msg->msg[2] = deck_control_mode;
+}
+
+static inline void cec_ops_deck_control(struct cec_msg *msg,
+					__u8 *deck_control_mode)
+{
+	*deck_control_mode = msg->msg[2];
+}
+
+static inline void cec_msg_deck_status(struct cec_msg *msg,
+				       __u8 deck_info)
+{
+	msg->len = 3;
+	msg->msg[1] = CEC_MSG_DECK_STATUS;
+	msg->msg[2] = deck_info;
+}
+
+static inline void cec_ops_deck_status(struct cec_msg *msg,
+				       __u8 *deck_info)
+{
+	*deck_info = msg->msg[2];
+}
+
+static inline void cec_msg_give_deck_status(struct cec_msg *msg,
+					    bool reply,
+					    __u8 status_req)
+{
+	msg->len = 3;
+	msg->msg[1] = CEC_MSG_GIVE_DECK_STATUS;
+	msg->msg[2] = status_req;
+	msg->reply = reply ? CEC_MSG_DECK_STATUS : 0;
+}
+
+static inline void cec_ops_give_deck_status(struct cec_msg *msg,
+					    __u8 *status_req)
+{
+	*status_req = msg->msg[2];
+}
+
+static inline void cec_msg_play(struct cec_msg *msg,
+				__u8 play_mode)
+{
+	msg->len = 3;
+	msg->msg[1] = CEC_MSG_PLAY;
+	msg->msg[2] = play_mode;
+}
+
+static inline void cec_ops_play(struct cec_msg *msg,
+				__u8 *play_mode)
+{
+	*play_mode = msg->msg[2];
+}
+
+
+/* Tuner Control Feature */
+struct cec_op_tuner_device_info {
+	__u8 rec_flag;
+	__u8 tuner_display_info;
+	bool is_analog;
+	union {
+		struct cec_op_digital_service_id digital;
+		struct {
+			__u8 ana_bcast_type;
+			__u16 ana_freq;
+			__u8 bcast_system;
+		} analog;
+	};
+};
+
+static inline void cec_msg_tuner_device_status_analog(struct cec_msg *msg,
+						      __u8 rec_flag,
+						      __u8 tuner_display_info,
+						      __u8 ana_bcast_type,
+						      __u16 ana_freq,
+						      __u8 bcast_system)
+{
+	msg->len = 7;
+	msg->msg[1] = CEC_MSG_TUNER_DEVICE_STATUS;
+	msg->msg[2] = (rec_flag << 7) | tuner_display_info;
+	msg->msg[3] = ana_bcast_type;
+	msg->msg[4] = ana_freq >> 8;
+	msg->msg[5] = ana_freq & 0xff;
+	msg->msg[6] = bcast_system;
+}
+
+static inline void cec_msg_tuner_device_status_digital(struct cec_msg *msg,
+		   __u8 rec_flag, __u8 tuner_display_info,
+		   const struct cec_op_digital_service_id *digital)
+{
+	msg->len = 10;
+	msg->msg[1] = CEC_MSG_TUNER_DEVICE_STATUS;
+	msg->msg[2] = (rec_flag << 7) | tuner_display_info;
+	cec_set_digital_service_id(msg->msg + 3, digital);
+}
+
+static inline void cec_msg_tuner_device_status(struct cec_msg *msg,
+			const struct cec_op_tuner_device_info *tuner_dev_info)
+{
+	if (tuner_dev_info->is_analog)
+		cec_msg_tuner_device_status_analog(msg,
+			tuner_dev_info->rec_flag,
+			tuner_dev_info->tuner_display_info,
+			tuner_dev_info->analog.ana_bcast_type,
+			tuner_dev_info->analog.ana_freq,
+			tuner_dev_info->analog.bcast_system);
+	else
+		cec_msg_tuner_device_status_digital(msg,
+			tuner_dev_info->rec_flag,
+			tuner_dev_info->tuner_display_info,
+			&tuner_dev_info->digital);
+}
+
+static inline void cec_ops_tuner_device_status(struct cec_msg *msg,
+					       struct cec_op_tuner_device_info *tuner_dev_info)
+{
+	tuner_dev_info->is_analog = msg->len < 10;
+	tuner_dev_info->rec_flag = msg->msg[2] >> 7;
+	tuner_dev_info->tuner_display_info = msg->msg[2] & 0x7f;
+	if (tuner_dev_info->is_analog) {
+		tuner_dev_info->analog.ana_bcast_type = msg->msg[3];
+		tuner_dev_info->analog.ana_freq = (msg->msg[4] << 8) | msg->msg[5];
+		tuner_dev_info->analog.bcast_system = msg->msg[6];
+		return;
+	}
+	cec_get_digital_service_id(msg->msg + 3, &tuner_dev_info->digital);
+}
+
+static inline void cec_msg_give_tuner_device_status(struct cec_msg *msg,
+						    bool reply,
+						    __u8 status_req)
+{
+	msg->len = 3;
+	msg->msg[1] = CEC_MSG_GIVE_TUNER_DEVICE_STATUS;
+	msg->msg[2] = status_req;
+	msg->reply = reply ? CEC_MSG_TUNER_DEVICE_STATUS : 0;
+}
+
+static inline void cec_ops_give_tuner_device_status(struct cec_msg *msg,
+						    __u8 *status_req)
+{
+	*status_req = msg->msg[2];
+}
+
+static inline void cec_msg_select_analogue_service(struct cec_msg *msg,
+						   __u8 ana_bcast_type,
+						   __u16 ana_freq,
+						   __u8 bcast_system)
+{
+	msg->len = 6;
+	msg->msg[1] = CEC_MSG_SELECT_ANALOGUE_SERVICE;
+	msg->msg[2] = ana_bcast_type;
+	msg->msg[3] = ana_freq >> 8;
+	msg->msg[4] = ana_freq & 0xff;
+	msg->msg[5] = bcast_system;
+}
+
+static inline void cec_ops_select_analogue_service(struct cec_msg *msg,
+						   __u8 *ana_bcast_type,
+						   __u16 *ana_freq,
+						   __u8 *bcast_system)
+{
+	*ana_bcast_type = msg->msg[2];
+	*ana_freq = (msg->msg[3] << 8) | msg->msg[4];
+	*bcast_system = msg->msg[5];
+}
+
+static inline void cec_msg_select_digital_service(struct cec_msg *msg,
+						  const struct cec_op_digital_service_id *digital)
+{
+	msg->len = 9;
+	msg->msg[1] = CEC_MSG_SELECT_DIGITAL_SERVICE;
+	cec_set_digital_service_id(msg->msg + 2, digital);
+}
+
+static inline void cec_ops_select_digital_service(struct cec_msg *msg,
+						  struct cec_op_digital_service_id *digital)
+{
+	cec_get_digital_service_id(msg->msg + 2, digital);
+}
+
+static inline void cec_msg_tuner_step_decrement(struct cec_msg *msg)
+{
+	msg->len = 2;
+	msg->msg[1] = CEC_MSG_TUNER_STEP_DECREMENT;
+}
+
+static inline void cec_msg_tuner_step_increment(struct cec_msg *msg)
+{
+	msg->len = 2;
+	msg->msg[1] = CEC_MSG_TUNER_STEP_INCREMENT;
+}
+
+
+/* Vendor Specific Commands Feature */
+static inline void cec_msg_device_vendor_id(struct cec_msg *msg, __u32 vendor_id)
+{
+	msg->len = 5;
+	msg->msg[0] |= 0xf; /* broadcast */
+	msg->msg[1] = CEC_MSG_DEVICE_VENDOR_ID;
+	msg->msg[2] = vendor_id >> 16;
+	msg->msg[3] = (vendor_id >> 8) & 0xff;
+	msg->msg[4] = vendor_id & 0xff;
+}
+
+static inline void cec_ops_device_vendor_id(const struct cec_msg *msg,
+					    __u32 *vendor_id)
+{
+	*vendor_id = (msg->msg[2] << 16) | (msg->msg[3] << 8) | msg->msg[4];
+}
+
+static inline void cec_msg_give_device_vendor_id(struct cec_msg *msg,
+						 bool reply)
+{
+	msg->len = 2;
+	msg->msg[1] = CEC_MSG_GIVE_DEVICE_VENDOR_ID;
+	msg->reply = reply ? CEC_MSG_DEVICE_VENDOR_ID : 0;
+}
+
+static inline void cec_msg_vendor_remote_button_up(struct cec_msg *msg)
+{
+	msg->len = 2;
+	msg->msg[1] = CEC_MSG_VENDOR_REMOTE_BUTTON_UP;
+}
+
+
+/* OSD Display Feature */
+static inline void cec_msg_set_osd_string(struct cec_msg *msg,
+					  __u8 disp_ctl,
+					  const char *osd)
+{
+	unsigned len = strlen(osd);
+
+	if (len > 13)
+		len = 13;
+	msg->len = 3 + len;
+	msg->msg[1] = CEC_MSG_SET_OSD_STRING;
+	msg->msg[2] = disp_ctl;
+	memcpy(msg->msg + 3, osd, len);
+}
+
+static inline void cec_ops_set_osd_string(const struct cec_msg *msg,
+					  __u8 *disp_ctl,
+					  char *osd)
+{
+	unsigned len = msg->len - 3;
+
+	*disp_ctl = msg->msg[2];
+	if (len > 13)
+		len = 13;
+	memcpy(osd, msg->msg + 3, len);
+	osd[len] = '\0';
+}
+
+
+/* Device OSD Transfer Feature */
+static inline void cec_msg_set_osd_name(struct cec_msg *msg, const char *name)
+{
+	unsigned len = strlen(name);
+
+	if (len > 14)
+		len = 14;
+	msg->len = 2 + len;
+	msg->msg[1] = CEC_MSG_SET_OSD_NAME;
+	memcpy(msg->msg + 2, name, len);
+}
+
+static inline void cec_ops_set_osd_name(const struct cec_msg *msg,
+					char *name)
+{
+	unsigned len = msg->len - 2;
+
+	if (len > 14)
+		len = 14;
+	memcpy(name, msg->msg + 2, len);
+	name[len] = '\0';
+}
+
+static inline void cec_msg_give_osd_name(struct cec_msg *msg,
+					 bool reply)
+{
+	msg->len = 2;
+	msg->msg[1] = CEC_MSG_GIVE_OSD_NAME;
+	msg->reply = reply ? CEC_MSG_SET_OSD_NAME : 0;
+}
+
+
+/* Device Menu Control Feature */
+static inline void cec_msg_menu_status(struct cec_msg *msg,
+				       __u8 menu_state)
+{
+	msg->len = 3;
+	msg->msg[1] = CEC_MSG_MENU_STATUS;
+	msg->msg[2] = menu_state;
+}
+
+static inline void cec_ops_menu_status(struct cec_msg *msg,
+				       __u8 *menu_state)
+{
+	*menu_state = msg->msg[2];
+}
+
+static inline void cec_msg_menu_request(struct cec_msg *msg,
+					bool reply,
+					__u8 menu_req)
+{
+	msg->len = 3;
+	msg->msg[1] = CEC_MSG_MENU_REQUEST;
+	msg->msg[2] = menu_req;
+	msg->reply = reply ? CEC_MSG_MENU_STATUS : 0;
+}
+
+static inline void cec_ops_menu_request(struct cec_msg *msg,
+					__u8 *menu_req)
+{
+	*menu_req = msg->msg[2];
+}
+
+struct cec_op_ui_command {
+	__u8 ui_cmd;
+	bool has_opt_arg;
+	union {
+		struct cec_op_channel_data channel_identifier;
+		__u8 ui_broadcast_type;
+		__u8 ui_sound_presentation_control;
+		__u8 play_mode;
+		__u8 ui_function_media;
+		__u8 ui_function_select_av_input;
+		__u8 ui_function_select_audio_input;
+	};
+};
+
+static inline void cec_msg_user_control_pressed(struct cec_msg *msg,
+						const struct cec_op_ui_command *ui_cmd)
+{
+	msg->len = 3;
+	msg->msg[1] = CEC_MSG_USER_CONTROL_PRESSED;
+	msg->msg[2] = ui_cmd->ui_cmd;
+	if (!ui_cmd->has_opt_arg)
+		return;
+	switch (ui_cmd->ui_cmd) {
+	case 0x56:
+	case 0x57:
+	case 0x60:
+	case 0x68:
+	case 0x69:
+	case 0x6a:
+		/* The optional operand is one byte for all these ui commands */
+		msg->len++;
+		msg->msg[3] = ui_cmd->play_mode;
+		break;
+	case 0x67:
+		msg->len += 4;
+		msg->msg[3] = (ui_cmd->channel_identifier.channel_number_fmt << 2) |
+			      (ui_cmd->channel_identifier.major >> 8);
+		msg->msg[4] = ui_cmd->channel_identifier.major && 0xff;
+		msg->msg[5] = ui_cmd->channel_identifier.minor >> 8;
+		msg->msg[6] = ui_cmd->channel_identifier.minor & 0xff;
+		break;
+	}
+}
+
+static inline void cec_ops_user_control_pressed(struct cec_msg *msg,
+						struct cec_op_ui_command *ui_cmd)
+{
+	ui_cmd->ui_cmd = msg->msg[2];
+	ui_cmd->has_opt_arg = false;
+	if (msg->len == 3)
+		return;
+	switch (ui_cmd->ui_cmd) {
+	case 0x56:
+	case 0x57:
+	case 0x60:
+	case 0x68:
+	case 0x69:
+	case 0x6a:
+		/* The optional operand is one byte for all these ui commands */
+		ui_cmd->play_mode = msg->msg[3];
+		ui_cmd->has_opt_arg = true;
+		break;
+	case 0x67:
+		if (msg->len < 7)
+			break;
+		ui_cmd->has_opt_arg = true;
+		ui_cmd->channel_identifier.channel_number_fmt = msg->msg[3] >> 2;
+		ui_cmd->channel_identifier.major = ((msg->msg[3] & 3) << 6) | msg->msg[4];
+		ui_cmd->channel_identifier.minor = (msg->msg[5] << 8) | msg->msg[6];
+		break;
+	}
+}
+
+static inline void cec_msg_user_control_released(struct cec_msg *msg)
+{
+	msg->len = 2;
+	msg->msg[1] = CEC_MSG_USER_CONTROL_RELEASED;
+}
+
+/* Remote Control Passthrough Feature */
+
+/* Power Status Feature */
+static inline void cec_msg_report_power_status(struct cec_msg *msg,
+					       __u8 pwr_state)
+{
+	msg->len = 3;
+	msg->msg[1] = CEC_MSG_REPORT_POWER_STATUS;
+	msg->msg[2] = pwr_state;
+}
+
+static inline void cec_ops_report_power_status(const struct cec_msg *msg,
+					       __u8 *pwr_state)
+{
+	*pwr_state = msg->msg[2];
+}
+
+static inline void cec_msg_give_device_power_status(struct cec_msg *msg,
+						    bool reply)
+{
+	msg->len = 2;
+	msg->msg[1] = CEC_MSG_GIVE_DEVICE_POWER_STATUS;
+	msg->reply = reply ? CEC_MSG_REPORT_POWER_STATUS : 0;
+}
+
+/* General Protocol Messages */
+static inline void cec_msg_feature_abort(struct cec_msg *msg,
+					 __u8 abort_msg, __u8 reason)
+{
+	msg->len = 4;
+	msg->msg[1] = CEC_MSG_FEATURE_ABORT;
+	msg->msg[2] = abort_msg;
+	msg->msg[3] = reason;
+}
+
+static inline void cec_ops_feature_abort(const struct cec_msg *msg,
+					 __u8 *abort_msg, __u8 *reason)
+{
+	*abort_msg = msg->msg[2];
+	*reason = msg->msg[3];
+}
+
+/* This changes the current message into a feature abort message */
+static inline void cec_msg_reply_feature_abort(struct cec_msg *msg, __u8 reason)
+{
+	cec_msg_set_reply_to(msg, msg);
+	msg->len = 4;
+	msg->msg[2] = msg->msg[1];
+	msg->msg[3] = reason;
+	msg->msg[1] = CEC_MSG_FEATURE_ABORT;
+}
+
+static inline void cec_msg_abort(struct cec_msg *msg)
+{
+	msg->len = 2;
+	msg->msg[1] = CEC_MSG_ABORT;
+}
+
+
+/* System Audio Control Feature */
+static inline void cec_msg_report_audio_status(struct cec_msg *msg,
+					       __u8 aud_mute_status,
+					       __u8 aud_vol_status)
+{
+	msg->len = 3;
+	msg->msg[1] = CEC_MSG_REPORT_AUDIO_STATUS;
+	msg->msg[2] = (aud_mute_status << 7) | (aud_vol_status & 0x7f);
+}
+
+static inline void cec_ops_report_audio_status(const struct cec_msg *msg,
+					       __u8 *aud_mute_status,
+					       __u8 *aud_vol_status)
+{
+	*aud_mute_status = msg->msg[2] >> 7;
+	*aud_vol_status = msg->msg[2] & 0x7f;
+}
+
+static inline void cec_msg_give_audio_status(struct cec_msg *msg,
+					     bool reply)
+{
+	msg->len = 2;
+	msg->msg[1] = CEC_MSG_GIVE_AUDIO_STATUS;
+	msg->reply = reply ? CEC_MSG_REPORT_AUDIO_STATUS : 0;
+}
+
+static inline void cec_msg_set_system_audio_mode(struct cec_msg *msg,
+						 __u8 sys_aud_status)
+{
+	msg->len = 3;
+	msg->msg[1] = CEC_MSG_SET_SYSTEM_AUDIO_MODE;
+	msg->msg[2] = sys_aud_status;
+}
+
+static inline void cec_ops_set_system_audio_mode(const struct cec_msg *msg,
+						 __u8 *sys_aud_status)
+{
+	*sys_aud_status = msg->msg[2];
+}
+
+static inline void cec_msg_system_audio_mode_request(struct cec_msg *msg,
+						     bool reply,
+						     __u16 phys_addr)
+{
+	msg->len = phys_addr == 0xffff ? 2 : 4;
+	msg->msg[1] = CEC_MSG_SYSTEM_AUDIO_MODE_REQUEST;
+	msg->msg[2] = phys_addr >> 8;
+	msg->msg[3] = phys_addr & 0xff;
+	msg->reply = reply ? CEC_MSG_SET_SYSTEM_AUDIO_MODE : 0;
+
+}
+
+static inline void cec_ops_system_audio_mode_request(const struct cec_msg *msg,
+						     __u16 *phys_addr)
+{
+	if (msg->len < 4)
+		*phys_addr = 0xffff;
+	else
+		*phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+}
+
+static inline void cec_msg_system_audio_mode_status(struct cec_msg *msg,
+						    __u8 sys_aud_status)
+{
+	msg->len = 3;
+	msg->msg[1] = CEC_MSG_SYSTEM_AUDIO_MODE_STATUS;
+	msg->msg[2] = sys_aud_status;
+}
+
+static inline void cec_ops_system_audio_mode_status(const struct cec_msg *msg,
+						    __u8 *sys_aud_status)
+{
+	*sys_aud_status = msg->msg[2];
+}
+
+static inline void cec_msg_give_system_audio_mode_status(struct cec_msg *msg,
+							 bool reply)
+{
+	msg->len = 2;
+	msg->msg[1] = CEC_MSG_GIVE_SYSTEM_AUDIO_MODE_STATUS;
+	msg->reply = reply ? CEC_MSG_SYSTEM_AUDIO_MODE_STATUS : 0;
+}
+
+static inline void cec_msg_report_short_audio_descriptor(struct cec_msg *msg,
+							 __u8 num_descriptors,
+							 const __u32 *descriptors)
+{
+	unsigned i;
+
+	if (num_descriptors > 4)
+		num_descriptors = 4;
+	msg->len = 2 + num_descriptors * 3;
+	msg->msg[1] = CEC_MSG_REPORT_SHORT_AUDIO_DESCRIPTOR;
+	for (i = 0; i < num_descriptors; i++) {
+		msg->msg[2 + i * 3] = (descriptors[i] >> 16) & 0xff;
+		msg->msg[3 + i * 3] = (descriptors[i] >> 8) & 0xff;
+		msg->msg[4 + i * 3] = descriptors[i] & 0xff;
+	}
+}
+
+static inline void cec_ops_report_short_audio_descriptor(const struct cec_msg *msg,
+							 __u8 *num_descriptors,
+							 __u32 *descriptors)
+{
+	unsigned i;
+
+	*num_descriptors = (msg->len - 2) / 3;
+	if (*num_descriptors > 4)
+		*num_descriptors = 4;
+	for (i = 0; i < *num_descriptors; i++)
+		descriptors[i] = (msg->msg[2 + i * 3] << 16) |
+			(msg->msg[3 + i * 3] << 8) |
+			msg->msg[4 + i * 3];
+}
+
+static inline void cec_msg_request_short_audio_descriptor(struct cec_msg *msg,
+							  __u8 num_descriptors,
+							  const __u8 *audio_format_id,
+							  const __u8 *audio_format_code)
+{
+	unsigned i;
+
+	if (num_descriptors > 4)
+		num_descriptors = 4;
+	msg->len = 2 + num_descriptors;
+	msg->msg[1] = CEC_MSG_REQUEST_SHORT_AUDIO_DESCRIPTOR;
+	for (i = 0; i < num_descriptors; i++)
+		msg->msg[2 + i] = (audio_format_id[i] << 6) | (audio_format_code[i] & 0x3f);
+}
+
+static inline void cec_ops_request_short_audio_descriptor(const struct cec_msg *msg,
+							  __u8 *num_descriptors,
+							  __u8 *audio_format_id,
+							  __u8 *audio_format_code)
+{
+	unsigned i;
+
+	*num_descriptors = msg->len - 2;
+	if (*num_descriptors > 4)
+		*num_descriptors = 4;
+	for (i = 0; i < *num_descriptors; i++) {
+		audio_format_id[i] = msg->msg[2 + i] >> 6;
+		audio_format_code[i] = msg->msg[2 + i] & 0x3f;
+	}
+}
+
+
+/* Audio Rate Control Feature */
+static inline void cec_msg_set_audio_rate(struct cec_msg *msg,
+					  __u8 audio_rate)
+{
+	msg->len = 3;
+	msg->msg[1] = CEC_MSG_SET_AUDIO_RATE;
+	msg->msg[2] = audio_rate;
+}
+
+static inline void cec_ops_set_audio_rate(const struct cec_msg *msg,
+					  __u8 *audio_rate)
+{
+	*audio_rate = msg->msg[2];
+}
+
+
+/* Audio Return Channel Control Feature */
+static inline void cec_msg_report_arc_initiated(struct cec_msg *msg)
+{
+	msg->len = 2;
+	msg->msg[1] = CEC_MSG_REPORT_ARC_INITIATED;
+}
+
+static inline void cec_msg_initiate_arc(struct cec_msg *msg,
+					bool reply)
+{
+	msg->len = 2;
+	msg->msg[1] = CEC_MSG_INITIATE_ARC;
+	msg->reply = reply ? CEC_MSG_REPORT_ARC_INITIATED : 0;
+}
+
+static inline void cec_msg_request_arc_initiation(struct cec_msg *msg,
+						  bool reply)
+{
+	msg->len = 2;
+	msg->msg[1] = CEC_MSG_REQUEST_ARC_INITIATION;
+	msg->reply = reply ? CEC_MSG_INITIATE_ARC : 0;
+}
+
+static inline void cec_msg_report_arc_terminated(struct cec_msg *msg)
+{
+	msg->len = 2;
+	msg->msg[1] = CEC_MSG_REPORT_ARC_TERMINATED;
+}
+
+static inline void cec_msg_terminate_arc(struct cec_msg *msg,
+					 bool reply)
+{
+	msg->len = 2;
+	msg->msg[1] = CEC_MSG_TERMINATE_ARC;
+	msg->reply = reply ? CEC_MSG_REPORT_ARC_TERMINATED : 0;
+}
+
+static inline void cec_msg_request_arc_termination(struct cec_msg *msg,
+						   bool reply)
+{
+	msg->len = 2;
+	msg->msg[1] = CEC_MSG_REQUEST_ARC_TERMINATION;
+	msg->reply = reply ? CEC_MSG_TERMINATE_ARC : 0;
+}
+
+
+/* Dynamic Audio Lipsync Feature */
+/* Only for CEC 2.0 and up */
+static inline void cec_msg_report_current_latency(struct cec_msg *msg,
+						  __u16 phys_addr,
+						  __u8 video_latency,
+						  __u8 low_latency_mode,
+						  __u8 audio_out_compensated,
+						  __u8 audio_out_delay)
+{
+	msg->len = 7;
+	msg->msg[1] = CEC_MSG_REPORT_CURRENT_LATENCY;
+	msg->msg[2] = phys_addr >> 8;
+	msg->msg[3] = phys_addr & 0xff;
+	msg->msg[4] = video_latency;
+	msg->msg[5] = (low_latency_mode << 2) | audio_out_compensated;
+	msg->msg[6] = audio_out_delay;
+}
+
+static inline void cec_ops_report_current_latency(const struct cec_msg *msg,
+						  __u16 *phys_addr,
+						  __u8 *video_latency,
+						  __u8 *low_latency_mode,
+						  __u8 *audio_out_compensated,
+						  __u8 *audio_out_delay)
+{
+	*phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+	*video_latency = msg->msg[4];
+	*low_latency_mode = (msg->msg[5] >> 2) & 1;
+	*audio_out_compensated = msg->msg[5] & 3;
+	*audio_out_delay = msg->msg[6];
+}
+
+static inline void cec_msg_request_current_latency(struct cec_msg *msg,
+						   bool reply,
+						   __u16 phys_addr)
+{
+	msg->len = 4;
+	msg->msg[1] = CEC_MSG_REQUEST_CURRENT_LATENCY;
+	msg->msg[2] = phys_addr >> 8;
+	msg->msg[3] = phys_addr & 0xff;
+	msg->reply = reply ? CEC_MSG_REPORT_CURRENT_LATENCY : 0;
+}
+
+static inline void cec_ops_request_current_latency(const struct cec_msg *msg,
+						   __u16 *phys_addr)
+{
+	*phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+}
+
+
+/* Capability Discovery and Control Feature */
+static inline void cec_msg_cdc_hec_inquire_state(struct cec_msg *msg,
+						 __u16 phys_addr1,
+						 __u16 phys_addr2)
+{
+	msg->len = 9;
+	msg->msg[0] |= 0xf; /* broadcast */
+	msg->msg[1] = CEC_MSG_CDC_MESSAGE;
+	/* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */
+	msg->msg[4] = CEC_MSG_CDC_HEC_INQUIRE_STATE;
+	msg->msg[5] = phys_addr1 >> 8;
+	msg->msg[6] = phys_addr1 & 0xff;
+	msg->msg[7] = phys_addr2 >> 8;
+	msg->msg[8] = phys_addr2 & 0xff;
+}
+
+static inline void cec_ops_cdc_hec_inquire_state(const struct cec_msg *msg,
+						 __u16 *phys_addr,
+						 __u16 *phys_addr1,
+						 __u16 *phys_addr2)
+{
+	*phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+	*phys_addr1 = (msg->msg[5] << 8) | msg->msg[6];
+	*phys_addr2 = (msg->msg[7] << 8) | msg->msg[8];
+}
+
+static inline void cec_msg_cdc_hec_report_state(struct cec_msg *msg,
+						__u16 target_phys_addr,
+						__u8 hec_func_state,
+						__u8 host_func_state,
+						__u8 enc_func_state,
+						__u8 cdc_errcode,
+						__u8 has_field,
+						__u16 hec_field)
+{
+	msg->len = has_field ? 10 : 8;
+	msg->msg[0] |= 0xf; /* broadcast */
+	msg->msg[1] = CEC_MSG_CDC_MESSAGE;
+	/* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */
+	msg->msg[4] = CEC_MSG_CDC_HEC_REPORT_STATE;
+	msg->msg[5] = target_phys_addr >> 8;
+	msg->msg[6] = target_phys_addr & 0xff;
+	msg->msg[7] = (hec_func_state << 6) |
+		      (host_func_state << 4) |
+		      (enc_func_state << 2) |
+		      cdc_errcode;
+	if (has_field) {
+		msg->msg[8] = hec_field >> 8;
+		msg->msg[9] = hec_field & 0xff;
+	}
+}
+
+static inline void cec_ops_cdc_hec_report_state(const struct cec_msg *msg,
+						__u16 *phys_addr,
+						__u16 *target_phys_addr,
+						__u8 *hec_func_state,
+						__u8 *host_func_state,
+						__u8 *enc_func_state,
+						__u8 *cdc_errcode,
+						__u8 *has_field,
+						__u16 *hec_field)
+{
+	*phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+	*target_phys_addr = (msg->msg[5] << 8) | msg->msg[6];
+	*hec_func_state = msg->msg[7] >> 6;
+	*host_func_state = (msg->msg[7] >> 4) & 3;
+	*enc_func_state = (msg->msg[7] >> 4) & 3;
+	*cdc_errcode = msg->msg[7] & 3;
+	*has_field = msg->len >= 10;
+	*hec_field = *has_field ? ((msg->msg[8] << 8) | msg->msg[9]) : 0;
+}
+
+static inline void cec_msg_cdc_hec_set_state(struct cec_msg *msg,
+					     __u16 phys_addr1,
+					     __u16 phys_addr2,
+					     __u8 hec_set_state,
+					     __u16 phys_addr3,
+					     __u16 phys_addr4,
+					     __u16 phys_addr5)
+{
+	msg->len = 10;
+	msg->msg[0] |= 0xf; /* broadcast */
+	msg->msg[1] = CEC_MSG_CDC_MESSAGE;
+	/* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */
+	msg->msg[4] = CEC_MSG_CDC_HEC_INQUIRE_STATE;
+	msg->msg[5] = phys_addr1 >> 8;
+	msg->msg[6] = phys_addr1 & 0xff;
+	msg->msg[7] = phys_addr2 >> 8;
+	msg->msg[8] = phys_addr2 & 0xff;
+	msg->msg[9] = hec_set_state;
+	if (phys_addr3 != CEC_PHYS_ADDR_INVALID) {
+		msg->msg[msg->len++] = phys_addr3 >> 8;
+		msg->msg[msg->len++] = phys_addr3 & 0xff;
+		if (phys_addr4 != CEC_PHYS_ADDR_INVALID) {
+			msg->msg[msg->len++] = phys_addr4 >> 8;
+			msg->msg[msg->len++] = phys_addr4 & 0xff;
+			if (phys_addr5 != CEC_PHYS_ADDR_INVALID) {
+				msg->msg[msg->len++] = phys_addr5 >> 8;
+				msg->msg[msg->len++] = phys_addr5 & 0xff;
+			}
+		}
+	}
+}
+
+static inline void cec_ops_cdc_hec_set_state(const struct cec_msg *msg,
+					     __u16 *phys_addr,
+					     __u16 *phys_addr1,
+					     __u16 *phys_addr2,
+					     __u8 *hec_set_state,
+					     __u16 *phys_addr3,
+					     __u16 *phys_addr4,
+					     __u16 *phys_addr5)
+{
+	*phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+	*phys_addr1 = (msg->msg[5] << 8) | msg->msg[6];
+	*phys_addr2 = (msg->msg[7] << 8) | msg->msg[8];
+	*hec_set_state = msg->msg[9];
+	*phys_addr3 = *phys_addr4 = *phys_addr5 = CEC_PHYS_ADDR_INVALID;
+	if (msg->len >= 12)
+		*phys_addr3 = (msg->msg[10] << 8) | msg->msg[11];
+	if (msg->len >= 14)
+		*phys_addr4 = (msg->msg[12] << 8) | msg->msg[13];
+	if (msg->len >= 16)
+		*phys_addr5 = (msg->msg[14] << 8) | msg->msg[15];
+}
+
+static inline void cec_msg_cdc_hec_set_state_adjacent(struct cec_msg *msg,
+						      __u16 phys_addr1,
+						      __u8 hec_set_state)
+{
+	msg->len = 8;
+	msg->msg[0] |= 0xf; /* broadcast */
+	msg->msg[1] = CEC_MSG_CDC_MESSAGE;
+	/* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */
+	msg->msg[4] = CEC_MSG_CDC_HEC_SET_STATE_ADJACENT;
+	msg->msg[5] = phys_addr1 >> 8;
+	msg->msg[6] = phys_addr1 & 0xff;
+	msg->msg[7] = hec_set_state;
+}
+
+static inline void cec_ops_cdc_hec_set_state_adjacent(const struct cec_msg *msg,
+						      __u16 *phys_addr,
+						      __u16 *phys_addr1,
+						      __u8 *hec_set_state)
+{
+	*phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+	*phys_addr1 = (msg->msg[5] << 8) | msg->msg[6];
+	*hec_set_state = msg->msg[7];
+}
+
+static inline void cec_msg_cdc_hec_request_deactivation(struct cec_msg *msg,
+							__u16 phys_addr1,
+							__u16 phys_addr2,
+							__u16 phys_addr3)
+{
+	msg->len = 11;
+	msg->msg[0] |= 0xf; /* broadcast */
+	msg->msg[1] = CEC_MSG_CDC_MESSAGE;
+	/* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */
+	msg->msg[4] = CEC_MSG_CDC_HEC_REQUEST_DEACTIVATION;
+	msg->msg[5] = phys_addr1 >> 8;
+	msg->msg[6] = phys_addr1 & 0xff;
+	msg->msg[7] = phys_addr2 >> 8;
+	msg->msg[8] = phys_addr2 & 0xff;
+	msg->msg[9] = phys_addr3 >> 8;
+	msg->msg[10] = phys_addr3 & 0xff;
+}
+
+static inline void cec_ops_cdc_hec_request_deactivation(const struct cec_msg *msg,
+							__u16 *phys_addr,
+							__u16 *phys_addr1,
+							__u16 *phys_addr2,
+							__u16 *phys_addr3)
+{
+	*phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+	*phys_addr1 = (msg->msg[5] << 8) | msg->msg[6];
+	*phys_addr2 = (msg->msg[7] << 8) | msg->msg[8];
+	*phys_addr3 = (msg->msg[9] << 8) | msg->msg[10];
+}
+
+static inline void cec_msg_cdc_hec_notify_alive(struct cec_msg *msg)
+{
+	msg->len = 5;
+	msg->msg[0] |= 0xf; /* broadcast */
+	msg->msg[1] = CEC_MSG_CDC_MESSAGE;
+	/* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */
+	msg->msg[4] = CEC_MSG_CDC_HEC_NOTIFY_ALIVE;
+}
+
+static inline void cec_ops_cdc_hec_notify_alive(const struct cec_msg *msg,
+						__u16 *phys_addr)
+{
+	*phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+}
+
+static inline void cec_msg_cdc_hec_discover(struct cec_msg *msg)
+{
+	msg->len = 5;
+	msg->msg[0] |= 0xf; /* broadcast */
+	msg->msg[1] = CEC_MSG_CDC_MESSAGE;
+	/* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */
+	msg->msg[4] = CEC_MSG_CDC_HEC_DISCOVER;
+}
+
+static inline void cec_ops_cdc_hec_discover(const struct cec_msg *msg,
+					    __u16 *phys_addr)
+{
+	*phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+}
+
+static inline void cec_msg_cdc_hpd_set_state(struct cec_msg *msg,
+					     __u8 input_port,
+					     __u8 hpd_state)
+{
+	msg->len = 6;
+	msg->msg[0] |= 0xf; /* broadcast */
+	msg->msg[1] = CEC_MSG_CDC_MESSAGE;
+	/* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */
+	msg->msg[4] = CEC_MSG_CDC_HPD_SET_STATE;
+	msg->msg[5] = (input_port << 4) | hpd_state;
+}
+
+static inline void cec_ops_cdc_hpd_set_state(const struct cec_msg *msg,
+					    __u16 *phys_addr,
+					    __u8 *input_port,
+					    __u8 *hpd_state)
+{
+	*phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+	*input_port = msg->msg[5] >> 4;
+	*hpd_state = msg->msg[5] & 0xf;
+}
+
+static inline void cec_msg_cdc_hpd_report_state(struct cec_msg *msg,
+						__u8 hpd_state,
+						__u8 hpd_error)
+{
+	msg->len = 6;
+	msg->msg[0] |= 0xf; /* broadcast */
+	msg->msg[1] = CEC_MSG_CDC_MESSAGE;
+	/* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */
+	msg->msg[4] = CEC_MSG_CDC_HPD_REPORT_STATE;
+	msg->msg[5] = (hpd_state << 4) | hpd_error;
+}
+
+static inline void cec_ops_cdc_hpd_report_state(const struct cec_msg *msg,
+						__u16 *phys_addr,
+						__u8 *hpd_state,
+						__u8 *hpd_error)
+{
+	*phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+	*hpd_state = msg->msg[5] >> 4;
+	*hpd_error = msg->msg[5] & 0xf;
+}
+
+#endif
diff --git a/include/linux/cec.h b/include/linux/cec.h
new file mode 100644
index 0000000..521b07cb
--- /dev/null
+++ b/include/linux/cec.h
@@ -0,0 +1,985 @@
+/*
+ * cec - HDMI Consumer Electronics Control public header
+ *
+ * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _CEC_UAPI_H
+#define _CEC_UAPI_H
+
+#include <linux/types.h>
+
+#define CEC_MAX_MSG_SIZE	16
+
+/**
+ * struct cec_msg - CEC message structure.
+ * @ts:		Timestamp in nanoseconds using CLOCK_MONOTONIC. Set by the
+ *		driver. It is set when the message transmission has finished
+ *		and it is set when a message was received.
+ * @len:	Length in bytes of the message.
+ * @timeout:	The timeout (in ms) that is used to timeout CEC_RECEIVE.
+ *		Set to 0 if you want to wait forever. This timeout can also be
+ *		used with CEC_TRANSMIT as the timeout for waiting for a reply.
+ *		If 0, then it will use a 1 second timeout instead of waiting
+ *		forever as is done with CEC_RECEIVE.
+ * @sequence:	The framework assigns a sequence number to messages that are
+ *		sent. This can be used to track replies to previously sent
+ *		messages.
+ * @flags:	Set to 0.
+ * @rx_status:	The message receive status bits. Set by the driver.
+ * @tx_status:	The message transmit status bits. Set by the driver.
+ * @msg:	The message payload.
+ * @reply:	This field is ignored with CEC_RECEIVE and is only used by
+ *		CEC_TRANSMIT. If non-zero, then wait for a reply with this
+ *		opcode. Set to CEC_MSG_FEATURE_ABORT if you want to wait for
+ *		a possible ABORT reply. If there was an error when sending the
+ *		msg or FeatureAbort was returned, then reply is set to 0.
+ *		If reply is non-zero upon return, then len/msg are set to
+ *		the received message.
+ *		If reply is zero upon return and status has the
+ *		CEC_TX_STATUS_FEATURE_ABORT bit set, then len/msg are set to
+ *		the received feature abort message.
+ *		If reply is zero upon return and status has the
+ *		CEC_TX_STATUS_MAX_RETRIES bit set, then no reply was seen at
+ *		all. If reply is non-zero for CEC_TRANSMIT and the message is a
+ *		broadcast, then -EINVAL is returned.
+ *		if reply is non-zero, then timeout is set to 1000 (the required
+ *		maximum response time).
+ * @tx_arb_lost_cnt: The number of 'Arbitration Lost' events. Set by the driver.
+ * @tx_nack_cnt: The number of 'Not Acknowledged' events. Set by the driver.
+ * @tx_low_drive_cnt: The number of 'Low Drive Detected' events. Set by the driver.
+ * @tx_error_cnt: The number of 'Error' events. Set by the driver.
+ */
+struct cec_msg {
+	__u64 ts;
+	__u32 len;
+	__u32 timeout;
+	__u32 sequence;
+	__u32 flags;
+	__u8 rx_status;
+	__u8 tx_status;
+	__u8 msg[CEC_MAX_MSG_SIZE];
+	__u8 reply;
+	__u8 tx_arb_lost_cnt;
+	__u8 tx_nack_cnt;
+	__u8 tx_low_drive_cnt;
+	__u8 tx_error_cnt;
+};
+
+/**
+ * cec_msg_initiator - return the initiator's logical address.
+ * @msg:	the message structure
+ */
+static inline __u8 cec_msg_initiator(const struct cec_msg *msg)
+{
+	return msg->msg[0] >> 4;
+}
+
+/**
+ * cec_msg_destination - return the destination's logical address.
+ * @msg:	the message structure
+ */
+static inline __u8 cec_msg_destination(const struct cec_msg *msg)
+{
+	return msg->msg[0] & 0xf;
+}
+
+/**
+ * cec_msg_opcode - return the opcode of the message, -1 for poll
+ * @msg:	the message structure
+ */
+static inline int cec_msg_opcode(const struct cec_msg *msg)
+{
+	return msg->len > 1 ? msg->msg[1] : -1;
+}
+
+/**
+ * cec_msg_is_broadcast - return true if this is a broadcast message.
+ * @msg:	the message structure
+ */
+static inline bool cec_msg_is_broadcast(const struct cec_msg *msg)
+{
+	return (msg->msg[0] & 0xf) == 0xf;
+}
+
+/**
+ * cec_msg_init - initialize the message structure.
+ * @msg:	the message structure
+ * @initiator:	the logical address of the initiator
+ * @destination:the logical address of the destination (0xf for broadcast)
+ *
+ * The whole structure is zeroed, the len field is set to 1 (i.e. a poll
+ * message) and the initiator and destination are filled in.
+ */
+static inline void cec_msg_init(struct cec_msg *msg,
+				__u8 initiator, __u8 destination)
+{
+	memset(msg, 0, sizeof(*msg));
+	msg->msg[0] = (initiator << 4) | destination;
+	msg->len = 1;
+}
+
+/**
+ * cec_msg_set_reply_to - fill in destination/initiator in a reply message.
+ * @msg:	the message structure for the reply
+ * @orig:	the original message structure
+ *
+ * Set the msg destination to the orig initiator and the msg initiator to the
+ * orig destination. Note that msg and orig may be the same pointer, in which
+ * case the change is done in place.
+ */
+static inline void cec_msg_set_reply_to(struct cec_msg *msg, struct cec_msg *orig)
+{
+	/* The destination becomes the initiator and vice versa */
+	msg->msg[0] = (cec_msg_destination(orig) << 4) | cec_msg_initiator(orig);
+	msg->reply = msg->timeout = 0;
+}
+
+/* cec status field */
+#define CEC_TX_STATUS_OK		(1 << 0)
+#define CEC_TX_STATUS_ARB_LOST		(1 << 1)
+#define CEC_TX_STATUS_NACK		(1 << 2)
+#define CEC_TX_STATUS_LOW_DRIVE		(1 << 3)
+#define CEC_TX_STATUS_ERROR		(1 << 4)
+#define CEC_TX_STATUS_MAX_RETRIES	(1 << 5)
+
+#define CEC_RX_STATUS_OK		(1 << 0)
+#define CEC_RX_STATUS_TIMEOUT		(1 << 1)
+#define CEC_RX_STATUS_FEATURE_ABORT	(1 << 2)
+
+static inline bool cec_msg_status_is_ok(const struct cec_msg *msg)
+{
+	if (msg->tx_status && !(msg->tx_status & CEC_TX_STATUS_OK))
+		return false;
+	if (msg->rx_status && !(msg->rx_status & CEC_RX_STATUS_OK))
+		return false;
+	if (!msg->tx_status && !msg->rx_status)
+		return false;
+	return !(msg->rx_status & CEC_RX_STATUS_FEATURE_ABORT);
+}
+
+#define CEC_LOG_ADDR_INVALID		0xff
+#define CEC_PHYS_ADDR_INVALID		0xffff
+
+/*
+ * The maximum number of logical addresses one device can be assigned to.
+ * The CEC 2.0 spec allows for only 2 logical addresses at the moment. The
+ * Analog Devices CEC hardware supports 3. So let's go wild and go for 4.
+ */
+#define CEC_MAX_LOG_ADDRS 4
+
+/* The logical addresses defined by CEC 2.0 */
+#define CEC_LOG_ADDR_TV			0
+#define CEC_LOG_ADDR_RECORD_1		1
+#define CEC_LOG_ADDR_RECORD_2		2
+#define CEC_LOG_ADDR_TUNER_1		3
+#define CEC_LOG_ADDR_PLAYBACK_1		4
+#define CEC_LOG_ADDR_AUDIOSYSTEM	5
+#define CEC_LOG_ADDR_TUNER_2		6
+#define CEC_LOG_ADDR_TUNER_3		7
+#define CEC_LOG_ADDR_PLAYBACK_2		8
+#define CEC_LOG_ADDR_RECORD_3		9
+#define CEC_LOG_ADDR_TUNER_4		10
+#define CEC_LOG_ADDR_PLAYBACK_3		11
+#define CEC_LOG_ADDR_BACKUP_1		12
+#define CEC_LOG_ADDR_BACKUP_2		13
+#define CEC_LOG_ADDR_SPECIFIC		14
+#define CEC_LOG_ADDR_UNREGISTERED	15 /* as initiator address */
+#define CEC_LOG_ADDR_BROADCAST		15 /* ad destination address */
+
+/* The logical address types that the CEC device wants to claim */
+#define CEC_LOG_ADDR_TYPE_TV		0
+#define CEC_LOG_ADDR_TYPE_RECORD	1
+#define CEC_LOG_ADDR_TYPE_TUNER		2
+#define CEC_LOG_ADDR_TYPE_PLAYBACK	3
+#define CEC_LOG_ADDR_TYPE_AUDIOSYSTEM	4
+#define CEC_LOG_ADDR_TYPE_SPECIFIC	5
+#define CEC_LOG_ADDR_TYPE_UNREGISTERED	6
+/*
+ * Switches should use UNREGISTERED.
+ * Processors should use SPECIFIC.
+ */
+
+#define CEC_LOG_ADDR_MASK_TV		(1 << CEC_LOG_ADDR_TV)
+#define CEC_LOG_ADDR_MASK_RECORD	((1 << CEC_LOG_ADDR_RECORD_1) | \
+					 (1 << CEC_LOG_ADDR_RECORD_2) | \
+					 (1 << CEC_LOG_ADDR_RECORD_3))
+#define CEC_LOG_ADDR_MASK_TUNER		((1 << CEC_LOG_ADDR_TUNER_1) | \
+					 (1 << CEC_LOG_ADDR_TUNER_2) | \
+					 (1 << CEC_LOG_ADDR_TUNER_3) | \
+					 (1 << CEC_LOG_ADDR_TUNER_4))
+#define CEC_LOG_ADDR_MASK_PLAYBACK	((1 << CEC_LOG_ADDR_PLAYBACK_1) | \
+					 (1 << CEC_LOG_ADDR_PLAYBACK_2) | \
+					 (1 << CEC_LOG_ADDR_PLAYBACK_3))
+#define CEC_LOG_ADDR_MASK_AUDIOSYSTEM	(1 << CEC_LOG_ADDR_AUDIOSYSTEM)
+#define CEC_LOG_ADDR_MASK_BACKUP	((1 << CEC_LOG_ADDR_BACKUP_1) | \
+					 (1 << CEC_LOG_ADDR_BACKUP_2))
+#define CEC_LOG_ADDR_MASK_SPECIFIC	(1 << CEC_LOG_ADDR_SPECIFIC)
+#define CEC_LOG_ADDR_MASK_UNREGISTERED	(1 << CEC_LOG_ADDR_UNREGISTERED)
+
+static inline bool cec_has_tv(__u16 log_addr_mask)
+{
+	return log_addr_mask & CEC_LOG_ADDR_MASK_TV;
+}
+
+static inline bool cec_has_record(__u16 log_addr_mask)
+{
+	return log_addr_mask & CEC_LOG_ADDR_MASK_RECORD;
+}
+
+static inline bool cec_has_tuner(__u16 log_addr_mask)
+{
+	return log_addr_mask & CEC_LOG_ADDR_MASK_TUNER;
+}
+
+static inline bool cec_has_playback(__u16 log_addr_mask)
+{
+	return log_addr_mask & CEC_LOG_ADDR_MASK_PLAYBACK;
+}
+
+static inline bool cec_has_audiosystem(__u16 log_addr_mask)
+{
+	return log_addr_mask & CEC_LOG_ADDR_MASK_AUDIOSYSTEM;
+}
+
+static inline bool cec_has_backup(__u16 log_addr_mask)
+{
+	return log_addr_mask & CEC_LOG_ADDR_MASK_BACKUP;
+}
+
+static inline bool cec_has_specific(__u16 log_addr_mask)
+{
+	return log_addr_mask & CEC_LOG_ADDR_MASK_SPECIFIC;
+}
+
+static inline bool cec_is_unregistered(__u16 log_addr_mask)
+{
+	return log_addr_mask & CEC_LOG_ADDR_MASK_UNREGISTERED;
+}
+
+static inline bool cec_is_unconfigured(__u16 log_addr_mask)
+{
+	return log_addr_mask == 0;
+}
+
+/*
+ * Use this if there is no vendor ID (CEC_G_VENDOR_ID) or if the vendor ID
+ * should be disabled (CEC_S_VENDOR_ID)
+ */
+#define CEC_VENDOR_ID_NONE		0xffffffff
+
+/* The message handling modes */
+/* Modes for initiator */
+#define CEC_MODE_NO_INITIATOR		(0x0 << 0)
+#define CEC_MODE_INITIATOR		(0x1 << 0)
+#define CEC_MODE_EXCL_INITIATOR		(0x2 << 0)
+#define CEC_MODE_INITIATOR_MSK		0x0f
+
+/* Modes for follower */
+#define CEC_MODE_NO_FOLLOWER		(0x0 << 4)
+#define CEC_MODE_FOLLOWER		(0x1 << 4)
+#define CEC_MODE_EXCL_FOLLOWER		(0x2 << 4)
+#define CEC_MODE_EXCL_FOLLOWER_PASSTHRU	(0x3 << 4)
+#define CEC_MODE_MONITOR		(0xe << 4)
+#define CEC_MODE_MONITOR_ALL		(0xf << 4)
+#define CEC_MODE_FOLLOWER_MSK		0xf0
+
+/* Userspace has to configure the physical address */
+#define CEC_CAP_PHYS_ADDR	(1 << 0)
+/* Userspace has to configure the logical addresses */
+#define CEC_CAP_LOG_ADDRS	(1 << 1)
+/* Userspace can transmit messages (and thus become follower as well) */
+#define CEC_CAP_TRANSMIT	(1 << 2)
+/*
+ * Passthrough all messages instead of processing them.
+ */
+#define CEC_CAP_PASSTHROUGH	(1 << 3)
+/* Supports remote control */
+#define CEC_CAP_RC		(1 << 4)
+/* Hardware can monitor all messages, not just directed and broadcast. */
+#define CEC_CAP_MONITOR_ALL	(1 << 5)
+
+/**
+ * struct cec_caps - CEC capabilities structure.
+ * @driver: name of the CEC device driver.
+ * @name: name of the CEC device. @driver + @name must be unique.
+ * @available_log_addrs: number of available logical addresses.
+ * @capabilities: capabilities of the CEC adapter.
+ * @version: version of the CEC adapter framework.
+ * @reserved:	Reserved fields, both driver and application must zero this array.
+ */
+struct cec_caps {
+	char driver[32];
+	char name[32];
+	__u32 available_log_addrs;
+	__u32 capabilities;
+	__u32 version;
+};
+
+/**
+ * struct cec_log_addrs - CEC logical addresses structure.
+ * @log_addr: the claimed logical addresses. Set by the driver.
+ * @log_addr_mask: current logical address mask. Set by the driver.
+ * @cec_version: the CEC version that the adapter should implement. Set by the
+ *	caller.
+ * @num_log_addrs: how many logical addresses should be claimed. Set by the
+ *	caller.
+ * @vendor_id: the vendor ID of the device. Set by the caller.
+ * @flags: set to 0.
+ * @osd_name: the OSD name of the device. Set by the caller.
+ * @primary_device_type: the primary device type for each logical address.
+ *	Set by the caller.
+ * @log_addr_type: the logical address types. Set by the caller.
+ * @all_device_types: CEC 2.0: all device types represented by the logical address.
+ *	Set by the caller.
+ * @features:	CEC 2.0: The logical address features. Set by the caller.
+ */
+struct cec_log_addrs {
+	__u8 log_addr[CEC_MAX_LOG_ADDRS];
+	__u16 log_addr_mask;
+	__u8 cec_version;
+	__u8 num_log_addrs;
+	__u32 vendor_id;
+	__u32 flags;
+	char osd_name[15];
+	__u8 primary_device_type[CEC_MAX_LOG_ADDRS];
+	__u8 log_addr_type[CEC_MAX_LOG_ADDRS];
+
+	/* CEC 2.0 */
+	__u8 all_device_types[CEC_MAX_LOG_ADDRS];
+	__u8 features[CEC_MAX_LOG_ADDRS][12];
+};
+
+/* Events */
+
+/* Event that occurs when the adapter state changes */
+#define CEC_EVENT_STATE_CHANGE		1
+/*
+ * This event is sent when messages are lost because the application
+ * didn't empty the message queue in time
+ */
+#define CEC_EVENT_LOST_MSGS		2
+
+#define CEC_EVENT_FL_INITIAL_STATE	(1 << 0)
+
+/**
+ * struct cec_event_state_change - used when the CEC adapter changes state.
+ * @phys_addr: the current physical address
+ * @log_addr_mask: the current logical address mask
+ */
+struct cec_event_state_change {
+	__u16 phys_addr;
+	__u16 log_addr_mask;
+};
+
+/**
+ * struct cec_event_lost_msgs - tells you how many messages were lost due.
+ * @lost_msgs: how many messages were lost.
+ */
+struct cec_event_lost_msgs {
+	__u32 lost_msgs;
+};
+
+/**
+ * struct cec_event - CEC event structure
+ * @ts: the timestamp of when the event was sent.
+ * @event: the event.
+ * array.
+ * @state_change: the event payload for CEC_EVENT_STATE_CHANGE.
+ * @lost_msgs: the event payload for CEC_EVENT_LOST_MSGS.
+ * @raw: array to pad the union.
+ */
+struct cec_event {
+	__u64 ts;
+	__u32 event;
+	__u32 flags;
+	union {
+		struct cec_event_state_change state_change;
+		struct cec_event_lost_msgs lost_msgs;
+		__u32 raw[16];
+	};
+};
+
+/* ioctls */
+
+/* Adapter capabilities */
+#define CEC_ADAP_G_CAPS		_IOWR('a',  0, struct cec_caps)
+
+/*
+ * phys_addr is either 0 (if this is the CEC root device)
+ * or a valid physical address obtained from the sink's EDID
+ * as read by this CEC device (if this is a source device)
+ * or a physical address obtained and modified from a sink
+ * EDID and used for a sink CEC device.
+ * If nothing is connected, then phys_addr is 0xffff.
+ * See HDMI 1.4b, section 8.7 (Physical Address).
+ *
+ * The CEC_ADAP_S_PHYS_ADDR ioctl may not be available if that is handled
+ * internally.
+ */
+#define CEC_ADAP_G_PHYS_ADDR	_IOR ('a',  1, __u16)
+#define CEC_ADAP_S_PHYS_ADDR	_IOW ('a',  2, __u16)
+
+/*
+ * Configure the CEC adapter. It sets the device type and which
+ * logical types it will try to claim. It will return which
+ * logical addresses it could actually claim.
+ * An error is returned if the adapter is disabled or if there
+ * is no physical address assigned.
+ */
+
+#define CEC_ADAP_G_LOG_ADDRS	_IOR ('a',  3, struct cec_log_addrs)
+#define CEC_ADAP_S_LOG_ADDRS	_IOWR('a',  4, struct cec_log_addrs)
+
+/* Transmit/receive a CEC command */
+#define CEC_TRANSMIT		_IOWR('a',  5, struct cec_msg)
+#define CEC_RECEIVE		_IOWR('a',  6, struct cec_msg)
+
+/* Dequeue CEC events */
+#define CEC_DQEVENT		_IOWR('a',  7, struct cec_event)
+
+/*
+ * Get and set the message handling mode for this filehandle.
+ */
+#define CEC_G_MODE		_IOR ('a',  8, __u32)
+#define CEC_S_MODE		_IOW ('a',  9, __u32)
+
+/*
+ * The remainder of this header defines all CEC messages and operands.
+ * The format matters since it the cec-ctl utility parses it to generate
+ * code for implementing all these messages.
+ *
+ * Comments ending with 'Feature' group messages for each feature.
+ * If messages are part of multiple features, then the "Has also"
+ * comment is used to list the previously defined messages that are
+ * supported by the feature.
+ *
+ * Before operands are defined a comment is added that gives the
+ * name of the operand and in brackets the variable name of the
+ * corresponding argument in the cec-funcs.h function.
+ */
+
+/* Messages */
+
+/* One Touch Play Feature */
+#define CEC_MSG_ACTIVE_SOURCE				0x82
+#define CEC_MSG_IMAGE_VIEW_ON				0x04
+#define CEC_MSG_TEXT_VIEW_ON				0x0d
+
+
+/* Routing Control Feature */
+
+/*
+ * Has also:
+ *	CEC_MSG_ACTIVE_SOURCE
+ */
+
+#define CEC_MSG_INACTIVE_SOURCE				0x9d
+#define CEC_MSG_REQUEST_ACTIVE_SOURCE			0x85
+#define CEC_MSG_ROUTING_CHANGE				0x80
+#define CEC_MSG_ROUTING_INFORMATION			0x81
+#define CEC_MSG_SET_STREAM_PATH				0x86
+
+
+/* Standby Feature */
+#define CEC_MSG_STANDBY					0x36
+
+
+/* One Touch Record Feature */
+#define CEC_MSG_RECORD_OFF				0x0b
+#define CEC_MSG_RECORD_ON				0x09
+/* Record Source Type Operand (rec_src_type) */
+#define CEC_OP_RECORD_SRC_OWN				1
+#define CEC_OP_RECORD_SRC_DIGITAL			2
+#define CEC_OP_RECORD_SRC_ANALOG			3
+#define CEC_OP_RECORD_SRC_EXT_PLUG			4
+#define CEC_OP_RECORD_SRC_EXT_PHYS_ADDR			5
+/* Service Identification Method Operand (service_id_method) */
+#define CEC_OP_SERVICE_ID_METHOD_BY_DIG_ID		0
+#define CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL		1
+/* Digital Service Broadcast System Operand (dig_bcast_system) */
+#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_GEN	0x00
+#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_GEN	0x01
+#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_GEN		0x02
+#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_BS		0x08
+#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_CS		0x09
+#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_T		0x0a
+#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_CABLE	0x10
+#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_SAT	0x11
+#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_T		0x12
+#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_C		0x18
+#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S		0x19
+#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S2		0x1a
+#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_T		0x1b
+/* Analogue Broadcast Type Operand (ana_bcast_type) */
+#define CEC_OP_ANA_BCAST_TYPE_CABLE			0
+#define CEC_OP_ANA_BCAST_TYPE_SATELLITE			1
+#define CEC_OP_ANA_BCAST_TYPE_TERRESTRIAL		2
+/* Broadcast System Operand (bcast_system) */
+#define CEC_OP_BCAST_SYSTEM_PAL_BG			0x00
+#define CEC_OP_BCAST_SYSTEM_SECAM_LQ			0x01 /* SECAM L' */
+#define CEC_OP_BCAST_SYSTEM_PAL_M			0x02
+#define CEC_OP_BCAST_SYSTEM_NTSC_M			0x03
+#define CEC_OP_BCAST_SYSTEM_PAL_I			0x04
+#define CEC_OP_BCAST_SYSTEM_SECAM_DK			0x05
+#define CEC_OP_BCAST_SYSTEM_SECAM_BG			0x06
+#define CEC_OP_BCAST_SYSTEM_SECAM_L			0x07
+#define CEC_OP_BCAST_SYSTEM_PAL_DK			0x08
+#define CEC_OP_BCAST_SYSTEM_OTHER			0x1f
+/* Channel Number Format Operand (channel_number_fmt) */
+#define CEC_OP_CHANNEL_NUMBER_FMT_1_PART		0x01
+#define CEC_OP_CHANNEL_NUMBER_FMT_2_PART		0x02
+
+#define CEC_MSG_RECORD_STATUS				0x0a
+/* Record Status Operand (rec_status) */
+#define CEC_OP_RECORD_STATUS_CUR_SRC			0x01
+#define CEC_OP_RECORD_STATUS_DIG_SERVICE		0x02
+#define CEC_OP_RECORD_STATUS_ANA_SERVICE		0x03
+#define CEC_OP_RECORD_STATUS_EXT_INPUT			0x04
+#define CEC_OP_RECORD_STATUS_NO_DIG_SERVICE		0x05
+#define CEC_OP_RECORD_STATUS_NO_ANA_SERVICE		0x06
+#define CEC_OP_RECORD_STATUS_NO_SERVICE			0x07
+#define CEC_OP_RECORD_STATUS_INVALID_EXT_PLUG		0x09
+#define CEC_OP_RECORD_STATUS_INVALID_EXT_PHYS_ADDR	0x0a
+#define CEC_OP_RECORD_STATUS_UNSUP_CA			0x0b
+#define CEC_OP_RECORD_STATUS_NO_CA_ENTITLEMENTS		0x0c
+#define CEC_OP_RECORD_STATUS_CANT_COPY_SRC		0x0d
+#define CEC_OP_RECORD_STATUS_NO_MORE_COPIES		0x0e
+#define CEC_OP_RECORD_STATUS_NO_MEDIA			0x10
+#define CEC_OP_RECORD_STATUS_PLAYING			0x11
+#define CEC_OP_RECORD_STATUS_ALREADY_RECORDING		0x12
+#define CEC_OP_RECORD_STATUS_MEDIA_PROT			0x13
+#define CEC_OP_RECORD_STATUS_NO_SIGNAL			0x14
+#define CEC_OP_RECORD_STATUS_MEDIA_PROBLEM		0x15
+#define CEC_OP_RECORD_STATUS_NO_SPACE			0x16
+#define CEC_OP_RECORD_STATUS_PARENTAL_LOCK		0x17
+#define CEC_OP_RECORD_STATUS_TERMINATED_OK		0x1a
+#define CEC_OP_RECORD_STATUS_ALREADY_TERM		0x1b
+#define CEC_OP_RECORD_STATUS_OTHER			0x1f
+
+#define CEC_MSG_RECORD_TV_SCREEN			0x0f
+
+
+/* Timer Programming Feature */
+#define CEC_MSG_CLEAR_ANALOGUE_TIMER			0x33
+/* Recording Sequence Operand (recording_seq) */
+#define CEC_OP_REC_SEQ_SUNDAY				0x01
+#define CEC_OP_REC_SEQ_MONDAY				0x02
+#define CEC_OP_REC_SEQ_TUESDAY				0x04
+#define CEC_OP_REC_SEQ_WEDNESDAY			0x08
+#define CEC_OP_REC_SEQ_THURSDAY				0x10
+#define CEC_OP_REC_SEQ_FRIDAY				0x20
+#define CEC_OP_REC_SEQ_SATERDAY				0x40
+#define CEC_OP_REC_SEQ_ONCE_ONLY			0x00
+
+#define CEC_MSG_CLEAR_DIGITAL_TIMER			0x99
+
+#define CEC_MSG_CLEAR_EXT_TIMER				0xa1
+/* External Source Specifier Operand (ext_src_spec) */
+#define CEC_OP_EXT_SRC_PLUG				0x04
+#define CEC_OP_EXT_SRC_PHYS_ADDR			0x05
+
+#define CEC_MSG_SET_ANALOGUE_TIMER			0x34
+#define CEC_MSG_SET_DIGITAL_TIMER			0x97
+#define CEC_MSG_SET_EXT_TIMER				0xa2
+
+#define CEC_MSG_SET_TIMER_PROGRAM_TITLE			0x67
+#define CEC_MSG_TIMER_CLEARED_STATUS			0x43
+/* Timer Cleared Status Data Operand (timer_cleared_status) */
+#define CEC_OP_TIMER_CLR_STAT_RECORDING			0x00
+#define CEC_OP_TIMER_CLR_STAT_NO_MATCHING		0x01
+#define CEC_OP_TIMER_CLR_STAT_NO_INFO			0x02
+#define CEC_OP_TIMER_CLR_STAT_CLEARED			0x80
+
+#define CEC_MSG_TIMER_STATUS				0x35
+/* Timer Overlap Warning Operand (timer_overlap_warning) */
+#define CEC_OP_TIMER_OVERLAP_WARNING_NO_OVERLAP		0
+#define CEC_OP_TIMER_OVERLAP_WARNING_OVERLAP		1
+/* Media Info Operand (media_info) */
+#define CEC_OP_MEDIA_INFO_UNPROT_MEDIA			0
+#define CEC_OP_MEDIA_INFO_PROT_MEDIA			1
+#define CEC_OP_MEDIA_INFO_NO_MEDIA			2
+/* Programmed Indicator Operand (prog_indicator) */
+#define CEC_OP_PROG_IND_NOT_PROGRAMMED			0
+#define CEC_OP_PROG_IND_PROGRAMMED			1
+/* Programmed Info Operand (prog_info) */
+#define CEC_OP_PROG_INFO_ENOUGH_SPACE			0x08
+#define CEC_OP_PROG_INFO_NOT_ENOUGH_SPACE		0x09
+#define CEC_OP_PROG_INFO_MIGHT_NOT_BE_ENOUGH_SPACE	0x0b
+#define CEC_OP_PROG_INFO_NONE_AVAILABLE			0x0a
+/* Not Programmed Error Info Operand (prog_error) */
+#define CEC_OP_PROG_ERROR_NO_FREE_TIMER			0x01
+#define CEC_OP_PROG_ERROR_DATE_OUT_OF_RANGE		0x02
+#define CEC_OP_PROG_ERROR_REC_SEQ_ERROR			0x03
+#define CEC_OP_PROG_ERROR_INV_EXT_PLUG			0x04
+#define CEC_OP_PROG_ERROR_INV_EXT_PHYS_ADDR		0x05
+#define CEC_OP_PROG_ERROR_CA_UNSUPP			0x06
+#define CEC_OP_PROG_ERROR_INSUF_CA_ENTITLEMENTS		0x07
+#define CEC_OP_PROG_ERROR_RESOLUTION_UNSUPP		0x08
+#define CEC_OP_PROG_ERROR_PARENTAL_LOCK			0x09
+#define CEC_OP_PROG_ERROR_CLOCK_FAILURE			0x0a
+#define CEC_OP_PROG_ERROR_DUPLICATE			0x0e
+
+
+/* System Information Feature */
+#define CEC_MSG_CEC_VERSION				0x9e
+/* CEC Version Operand (cec_version) */
+#define CEC_OP_CEC_VERSION_1_3A				4
+#define CEC_OP_CEC_VERSION_1_4				5
+#define CEC_OP_CEC_VERSION_2_0				6
+
+#define CEC_MSG_GET_CEC_VERSION				0x9f
+#define CEC_MSG_GIVE_PHYSICAL_ADDR			0x83
+#define CEC_MSG_GET_MENU_LANGUAGE			0x91
+#define CEC_MSG_REPORT_PHYSICAL_ADDR			0x84
+/* Primary Device Type Operand (prim_devtype) */
+#define CEC_OP_PRIM_DEVTYPE_TV				0
+#define CEC_OP_PRIM_DEVTYPE_RECORD			1
+#define CEC_OP_PRIM_DEVTYPE_TUNER			3
+#define CEC_OP_PRIM_DEVTYPE_PLAYBACK			4
+#define CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM			5
+#define CEC_OP_PRIM_DEVTYPE_SWITCH			6
+#define CEC_OP_PRIM_DEVTYPE_PROCESSOR			7
+
+#define CEC_MSG_SET_MENU_LANGUAGE			0x32
+#define CEC_MSG_REPORT_FEATURES				0xa6	/* HDMI 2.0 */
+/* All Device Types Operand (all_device_types) */
+#define CEC_OP_ALL_DEVTYPE_TV				0x80
+#define CEC_OP_ALL_DEVTYPE_RECORD			0x40
+#define CEC_OP_ALL_DEVTYPE_TUNER			0x20
+#define CEC_OP_ALL_DEVTYPE_PLAYBACK			0x10
+#define CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM			0x08
+#define CEC_OP_ALL_DEVTYPE_SWITCH			0x04
+/*
+ * And if you wondering what happened to PROCESSOR devices: those should
+ * be mapped to a SWITCH.
+ */
+
+/* Valid for RC Profile and Device Feature operands */
+#define CEC_OP_FEAT_EXT					0x80	/* Extension bit */
+/* RC Profile Operand (rc_profile) */
+#define CEC_OP_FEAT_RC_TV_PROFILE_NONE			0x00
+#define CEC_OP_FEAT_RC_TV_PROFILE_1			0x02
+#define CEC_OP_FEAT_RC_TV_PROFILE_2			0x06
+#define CEC_OP_FEAT_RC_TV_PROFILE_3			0x0a
+#define CEC_OP_FEAT_RC_TV_PROFILE_4			0x0e
+#define CEC_OP_FEAT_RC_SRC_HAS_DEV_ROOT_MENU		0x50
+#define CEC_OP_FEAT_RC_SRC_HAS_DEV_SETUP_MENU		0x48
+#define CEC_OP_FEAT_RC_SRC_HAS_CONTENTS_MENU		0x44
+#define CEC_OP_FEAT_RC_SRC_HAS_MEDIA_TOP_MENU		0x42
+#define CEC_OP_FEAT_RC_SRC_HAS_MEDIA_CONTEXT_MENU	0x41
+/* Device Feature Operand (dev_features) */
+#define CEC_OP_FEAT_DEV_HAS_RECORD_TV_SCREEN		0x40
+#define CEC_OP_FEAT_DEV_HAS_SET_OSD_STRING		0x20
+#define CEC_OP_FEAT_DEV_HAS_DECK_CONTROL		0x10
+#define CEC_OP_FEAT_DEV_HAS_SET_AUDIO_RATE		0x08
+#define CEC_OP_FEAT_DEV_SINK_HAS_ARC_TX			0x04
+#define CEC_OP_FEAT_DEV_SOURCE_HAS_ARC_RX		0x02
+
+#define CEC_MSG_GIVE_FEATURES				0xa5	/* HDMI 2.0 */
+
+
+/* Deck Control Feature */
+#define CEC_MSG_DECK_CONTROL				0x42
+/* Deck Control Mode Operand (deck_control_mode) */
+#define CEC_OP_DECK_CTL_MODE_SKIP_FWD			1
+#define CEC_OP_DECK_CTL_MODE_SKIP_REV			2
+#define CEC_OP_DECK_CTL_MODE_STOP			3
+#define CEC_OP_DECK_CTL_MODE_EJECT			4
+
+#define CEC_MSG_DECK_STATUS				0x1b
+/* Deck Info Operand (deck_info) */
+#define CEC_OP_DECK_INFO_PLAY				0x11
+#define CEC_OP_DECK_INFO_RECORD				0x12
+#define CEC_OP_DECK_INFO_PLAY_REV			0x13
+#define CEC_OP_DECK_INFO_STILL				0x14
+#define CEC_OP_DECK_INFO_SLOW				0x15
+#define CEC_OP_DECK_INFO_SLOW_REV			0x16
+#define CEC_OP_DECK_INFO_FAST_FWD			0x17
+#define CEC_OP_DECK_INFO_FAST_REV			0x18
+#define CEC_OP_DECK_INFO_NO_MEDIA			0x19
+#define CEC_OP_DECK_INFO_STOP				0x1a
+#define CEC_OP_DECK_INFO_SKIP_FWD			0x1b
+#define CEC_OP_DECK_INFO_SKIP_REV			0x1c
+#define CEC_OP_DECK_INFO_INDEX_SEARCH_FWD		0x1d
+#define CEC_OP_DECK_INFO_INDEX_SEARCH_REV		0x1e
+#define CEC_OP_DECK_INFO_OTHER				0x1f
+
+#define CEC_MSG_GIVE_DECK_STATUS			0x1a
+/* Status Request Operand (status_req) */
+#define CEC_OP_STATUS_REQ_ON				1
+#define CEC_OP_STATUS_REQ_OFF				2
+#define CEC_OP_STATUS_REQ_ONCE				3
+
+#define CEC_MSG_PLAY					0x41
+/* Play Mode Operand (play_mode) */
+#define CEC_OP_PLAY_MODE_PLAY_FWD			0x24
+#define CEC_OP_PLAY_MODE_PLAY_REV			0x20
+#define CEC_OP_PLAY_MODE_PLAY_STILL			0x25
+#define CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MIN		0x05
+#define CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MED		0x06
+#define CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MAX		0x07
+#define CEC_OP_PLAY_MODE_PLAY_FAST_REV_MIN		0x09
+#define CEC_OP_PLAY_MODE_PLAY_FAST_REV_MED		0x0a
+#define CEC_OP_PLAY_MODE_PLAY_FAST_REV_MAX		0x0b
+#define CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MIN		0x15
+#define CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MED		0x16
+#define CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MAX		0x17
+#define CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MIN		0x19
+#define CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MED		0x1a
+#define CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MAX		0x1b
+
+
+/* Tuner Control Feature */
+#define CEC_MSG_GIVE_TUNER_DEVICE_STATUS		0x08
+#define CEC_MSG_SELECT_ANALOGUE_SERVICE			0x92
+#define CEC_MSG_SELECT_DIGITAL_SERVICE			0x93
+#define CEC_MSG_TUNER_DEVICE_STATUS			0x07
+/* Recording Flag Operand (rec_flag) */
+#define CEC_OP_REC_FLAG_USED				0
+#define CEC_OP_REC_FLAG_NOT_USED			1
+/* Tuner Display Info Operand (tuner_display_info) */
+#define CEC_OP_TUNER_DISPLAY_INFO_DIGITAL		0
+#define CEC_OP_TUNER_DISPLAY_INFO_NONE			1
+#define CEC_OP_TUNER_DISPLAY_INFO_ANALOGUE		2
+
+#define CEC_MSG_TUNER_STEP_DECREMENT			0x06
+#define CEC_MSG_TUNER_STEP_INCREMENT			0x05
+
+
+/* Vendor Specific Commands Feature */
+
+/*
+ * Has also:
+ *	CEC_MSG_CEC_VERSION
+ *	CEC_MSG_GET_CEC_VERSION
+ */
+#define CEC_MSG_DEVICE_VENDOR_ID			0x87
+#define CEC_MSG_GIVE_DEVICE_VENDOR_ID			0x8c
+#define CEC_MSG_VENDOR_COMMAND				0x89
+#define CEC_MSG_VENDOR_COMMAND_WITH_ID			0xa0
+#define CEC_MSG_VENDOR_REMOTE_BUTTON_DOWN		0x8a
+#define CEC_MSG_VENDOR_REMOTE_BUTTON_UP			0x8b
+
+
+/* OSD Display Feature */
+#define CEC_MSG_SET_OSD_STRING				0x64
+/* Display Control Operand (disp_ctl) */
+#define CEC_OP_DISP_CTL_DEFAULT				0x00
+#define CEC_OP_DISP_CTL_UNTIL_CLEARED			0x40
+#define CEC_OP_DISP_CTL_CLEAR				0x80
+
+
+/* Device OSD Transfer Feature */
+#define CEC_MSG_GIVE_OSD_NAME				0x46
+#define CEC_MSG_SET_OSD_NAME				0x47
+
+
+/* Device Menu Control Feature */
+#define CEC_MSG_MENU_REQUEST				0x8d
+/* Menu Request Type Operand (menu_req) */
+#define CEC_OP_MENU_REQUEST_ACTIVATE			0x00
+#define CEC_OP_MENU_REQUEST_DEACTIVATE			0x01
+#define CEC_OP_MENU_REQUEST_QUERY			0x02
+
+#define CEC_MSG_MENU_STATUS				0x8e
+/* Menu State Operand (menu_state) */
+#define CEC_OP_MENU_STATE_ACTIVATED			0x00
+#define CEC_OP_MENU_STATE_DEACTIVATED			0x01
+
+#define CEC_MSG_USER_CONTROL_PRESSED			0x44
+/* UI Broadcast Type Operand (ui_bcast_type) */
+#define CEC_OP_UI_BCAST_TYPE_TOGGLE_ALL			0x00
+#define CEC_OP_UI_BCAST_TYPE_TOGGLE_DIG_ANA		0x01
+#define CEC_OP_UI_BCAST_TYPE_ANALOGUE			0x10
+#define CEC_OP_UI_BCAST_TYPE_ANALOGUE_T			0x20
+#define CEC_OP_UI_BCAST_TYPE_ANALOGUE_CABLE		0x30
+#define CEC_OP_UI_BCAST_TYPE_ANALOGUE_SAT		0x40
+#define CEC_OP_UI_BCAST_TYPE_DIGITAL			0x50
+#define CEC_OP_UI_BCAST_TYPE_DIGITAL_T			0x60
+#define CEC_OP_UI_BCAST_TYPE_DIGITAL_CABLE		0x70
+#define CEC_OP_UI_BCAST_TYPE_DIGITAL_SAT		0x80
+#define CEC_OP_UI_BCAST_TYPE_DIGITAL_COM_SAT		0x90
+#define CEC_OP_UI_BCAST_TYPE_DIGITAL_COM_SAT2		0x91
+#define CEC_OP_UI_BCAST_TYPE_IP				0xa0
+/* UI Sound Presentation Control Operand (ui_snd_pres_ctl) */
+#define CEC_OP_UI_SND_PRES_CTL_DUAL_MONO		0x10
+#define CEC_OP_UI_SND_PRES_CTL_KARAOKE			0x20
+#define CEC_OP_UI_SND_PRES_CTL_DOWNMIX			0x80
+#define CEC_OP_UI_SND_PRES_CTL_REVERB			0x90
+#define CEC_OP_UI_SND_PRES_CTL_EQUALIZER		0xa0
+#define CEC_OP_UI_SND_PRES_CTL_BASS_UP			0xb1
+#define CEC_OP_UI_SND_PRES_CTL_BASS_NEUTRAL		0xb2
+#define CEC_OP_UI_SND_PRES_CTL_BASS_DOWN		0xb3
+#define CEC_OP_UI_SND_PRES_CTL_TREBLE_UP		0xc1
+#define CEC_OP_UI_SND_PRES_CTL_TREBLE_NEUTRAL		0xc2
+#define CEC_OP_UI_SND_PRES_CTL_TREBLE_DOWN		0xc3
+
+#define CEC_MSG_USER_CONTROL_RELEASED			0x45
+
+
+/* Remote Control Passthrough Feature */
+
+/*
+ * Has also:
+ *	CEC_MSG_USER_CONTROL_PRESSED
+ *	CEC_MSG_USER_CONTROL_RELEASED
+ */
+
+
+/* Power Status Feature */
+#define CEC_MSG_GIVE_DEVICE_POWER_STATUS		0x8f
+#define CEC_MSG_REPORT_POWER_STATUS			0x90
+/* Power Status Operand (pwr_state) */
+#define CEC_OP_POWER_STATUS_ON				0
+#define CEC_OP_POWER_STATUS_STANDBY			1
+#define CEC_OP_POWER_STATUS_TO_ON			2
+#define CEC_OP_POWER_STATUS_TO_STANDBY			3
+
+
+/* General Protocol Messages */
+#define CEC_MSG_FEATURE_ABORT				0x00
+/* Abort Reason Operand (reason) */
+#define CEC_OP_ABORT_UNRECOGNIZED_OP			0
+#define CEC_OP_ABORT_INCORRECT_MODE			1
+#define CEC_OP_ABORT_NO_SOURCE				2
+#define CEC_OP_ABORT_INVALID_OP				3
+#define CEC_OP_ABORT_REFUSED				4
+#define CEC_OP_ABORT_UNDETERMINED			5
+
+#define CEC_MSG_ABORT					0xff
+
+
+/* System Audio Control Feature */
+
+/*
+ * Has also:
+ *	CEC_MSG_USER_CONTROL_PRESSED
+ *	CEC_MSG_USER_CONTROL_RELEASED
+ */
+#define CEC_MSG_GIVE_AUDIO_STATUS			0x71
+#define CEC_MSG_GIVE_SYSTEM_AUDIO_MODE_STATUS		0x7d
+#define CEC_MSG_REPORT_AUDIO_STATUS			0x7a
+/* Audio Mute Status Operand (aud_mute_status) */
+#define CEC_OP_AUD_MUTE_STATUS_OFF			0
+#define CEC_OP_AUD_MUTE_STATUS_ON			1
+
+#define CEC_MSG_REPORT_SHORT_AUDIO_DESCRIPTOR		0xa3
+#define CEC_MSG_REQUEST_SHORT_AUDIO_DESCRIPTOR		0xa4
+#define CEC_MSG_SET_SYSTEM_AUDIO_MODE			0x72
+/* System Audio Status Operand (sys_aud_status) */
+#define CEC_OP_SYS_AUD_STATUS_OFF			0
+#define CEC_OP_SYS_AUD_STATUS_ON			1
+
+#define CEC_MSG_SYSTEM_AUDIO_MODE_REQUEST		0x70
+#define CEC_MSG_SYSTEM_AUDIO_MODE_STATUS		0x7e
+/* Audio Format ID Operand (audio_format_id) */
+#define CEC_OP_AUD_FMT_ID_CEA861			0
+#define CEC_OP_AUD_FMT_ID_CEA861_CXT			1
+
+
+/* Audio Rate Control Feature */
+#define CEC_MSG_SET_AUDIO_RATE				0x9a
+/* Audio Rate Operand (audio_rate) */
+#define CEC_OP_AUD_RATE_OFF				0
+#define CEC_OP_AUD_RATE_WIDE_STD			1
+#define CEC_OP_AUD_RATE_WIDE_FAST			2
+#define CEC_OP_AUD_RATE_WIDE_SLOW			3
+#define CEC_OP_AUD_RATE_NARROW_STD			4
+#define CEC_OP_AUD_RATE_NARROW_FAST			5
+#define CEC_OP_AUD_RATE_NARROW_SLOW			6
+
+
+/* Audio Return Channel Control Feature */
+#define CEC_MSG_INITIATE_ARC				0xc0
+#define CEC_MSG_REPORT_ARC_INITIATED			0xc1
+#define CEC_MSG_REPORT_ARC_TERMINATED			0xc2
+#define CEC_MSG_REQUEST_ARC_INITIATION			0xc3
+#define CEC_MSG_REQUEST_ARC_TERMINATION			0xc4
+#define CEC_MSG_TERMINATE_ARC				0xc5
+
+
+/* Dynamic Audio Lipsync Feature */
+/* Only for CEC 2.0 and up */
+#define CEC_MSG_REQUEST_CURRENT_LATENCY			0xa7
+#define CEC_MSG_REPORT_CURRENT_LATENCY			0xa8
+/* Low Latency Mode Operand (low_latency_mode) */
+#define CEC_OP_LOW_LATENCY_MODE_OFF			0
+#define CEC_OP_LOW_LATENCY_MODE_ON			1
+/* Audio Output Compensated Operand (audio_out_compensated) */
+#define CEC_OP_AUD_OUT_COMPENSATED_NA			0
+#define CEC_OP_AUD_OUT_COMPENSATED_DELAY		1
+#define CEC_OP_AUD_OUT_COMPENSATED_NO_DELAY		2
+#define CEC_OP_AUD_OUT_COMPENSATED_PARTIAL_DELAY	3
+
+
+/* Capability Discovery and Control Feature */
+#define CEC_MSG_CDC_MESSAGE				0xf8
+/* Ethernet-over-HDMI: nobody ever does this... */
+#define CEC_MSG_CDC_HEC_INQUIRE_STATE			0x00
+#define CEC_MSG_CDC_HEC_REPORT_STATE			0x01
+/* HEC Functionality State Operand (hec_func_state) */
+#define CEC_OP_HEC_FUNC_STATE_NOT_SUPPORTED		0
+#define CEC_OP_HEC_FUNC_STATE_INACTIVE			1
+#define CEC_OP_HEC_FUNC_STATE_ACTIVE			2
+#define CEC_OP_HEC_FUNC_STATE_ACTIVATION_FIELD		3
+/* Host Functionality State Operand (host_func_state) */
+#define CEC_OP_HOST_FUNC_STATE_NOT_SUPPORTED		0
+#define CEC_OP_HOST_FUNC_STATE_INACTIVE			1
+#define CEC_OP_HOST_FUNC_STATE_ACTIVE			2
+/* ENC Functionality State Operand (enc_func_state) */
+#define CEC_OP_ENC_FUNC_STATE_EXT_CON_NOT_SUPPORTED	0
+#define CEC_OP_ENC_FUNC_STATE_EXT_CON_INACTIVE		1
+#define CEC_OP_ENC_FUNC_STATE_EXT_CON_ACTIVE		2
+/* CDC Error Code Operand (cdc_errcode) */
+#define CEC_OP_CDC_ERROR_CODE_NONE			0
+#define CEC_OP_CDC_ERROR_CODE_CAP_UNSUPPORTED		1
+#define CEC_OP_CDC_ERROR_CODE_WRONG_STATE		2
+#define CEC_OP_CDC_ERROR_CODE_OTHER			3
+/* HEC Support Operand (hec_support) */
+#define CEC_OP_HEC_SUPPORT_NO				0
+#define CEC_OP_HEC_SUPPORT_YES				1
+/* HEC Activation Operand (hec_activation) */
+#define CEC_OP_HEC_ACTIVATION_ON			0
+#define CEC_OP_HEC_ACTIVATION_OFF			1
+
+#define CEC_MSG_CDC_HEC_SET_STATE_ADJACENT		0x02
+#define CEC_MSG_CDC_HEC_SET_STATE			0x03
+/* HEC Set State Operand (hec_set_state) */
+#define CEC_OP_HEC_SET_STATE_DEACTIVATE			0
+#define CEC_OP_HEC_SET_STATE_ACTIVATE			1
+
+#define CEC_MSG_CDC_HEC_REQUEST_DEACTIVATION		0x04
+#define CEC_MSG_CDC_HEC_NOTIFY_ALIVE			0x05
+#define CEC_MSG_CDC_HEC_DISCOVER			0x06
+/* Hotplug Detect messages */
+#define CEC_MSG_CDC_HPD_SET_STATE			0x10
+/* HPD State Operand (hpd_state) */
+#define CEC_OP_HPD_STATE_CP_EDID_DISABLE		0
+#define CEC_OP_HPD_STATE_CP_EDID_ENABLE			1
+#define CEC_OP_HPD_STATE_CP_EDID_DISABLE_ENABLE		2
+#define CEC_OP_HPD_STATE_EDID_DISABLE			3
+#define CEC_OP_HPD_STATE_EDID_ENABLE			4
+#define CEC_OP_HPD_STATE_EDID_DISABLE_ENABLE		5
+#define CEC_MSG_CDC_HPD_REPORT_STATE			0x11
+/* HPD Error Code Operand (hpd_error) */
+#define CEC_OP_HPD_ERROR_NONE				0
+#define CEC_OP_HPD_ERROR_INITIATOR_NOT_CAPABLE		1
+#define CEC_OP_HPD_ERROR_INITIATOR_WRONG_STATE		2
+#define CEC_OP_HPD_ERROR_OTHER				3
+#define CEC_OP_HPD_ERROR_NONE_NO_VIDEO			4
+
+#endif
diff --git a/include/media/cec-edid.h b/include/media/cec-edid.h
new file mode 100644
index 0000000..d6e39ca
--- /dev/null
+++ b/include/media/cec-edid.h
@@ -0,0 +1,103 @@
+/*
+ * cec-edid - HDMI Consumer Electronics Control & EDID helpers
+ *
+ * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _MEDIA_CEC_EDID_H
+#define _MEDIA_CEC_EDID_H
+
+#include <linux/types.h>
+#include <linux/cec.h>
+
+#define cec_phys_addr_exp(pa) \
+	((pa) >> 12), ((pa) >> 8) & 0xf, ((pa) >> 4) & 0xf, (pa) & 0xf
+
+/**
+ * cec_get_edid_phys_addr() - find and return the physical address
+ *
+ * @edid:	pointer to the EDID data
+ * @size:	size in bytes of the EDID data
+ * @offset:	If not %NULL then the location of the physical address
+ *		bytes in the EDID will be returned here. This is set to 0
+ *		if there is no physical address found.
+ *
+ * Return: the physical address or CEC_PHYS_ADDR_INVALID if there is none.
+ */
+u16 cec_get_edid_phys_addr(const u8 *edid, unsigned size, unsigned *offset);
+
+/**
+ * cec_set_edid_phys_addr() - find and set the physical address
+ *
+ * @edid:	pointer to the EDID data
+ * @size:	size in bytes of the EDID data
+ * @phys_addr:	the new physical address
+ *
+ * This function finds the location of the physical address in the EDID
+ * and fills in the given physical address and updates the checksum
+ * at the end of the EDID block. It does nothing if the EDID doesn't
+ * contain a physical address.
+ */
+void cec_set_edid_phys_addr(u8 *edid, unsigned size, u16 phys_addr);
+
+/**
+ * cec_phys_addr_for_input() - calculate the PA for an input
+ *
+ * @phys_addr:	the physical address of the parent
+ * @input:	the number of the input port, must be between 1 and 15
+ *
+ * This function calculates a new physical address based on the input
+ * port number. For example:
+ *
+ * PA = 0.0.0.0 and input = 2 becomes 2.0.0.0
+ *
+ * PA = 3.0.0.0 and input = 1 becomes 3.1.0.0
+ *
+ * PA = 3.2.1.0 and input = 5 becomes 3.2.1.5
+ *
+ * PA = 3.2.1.3 and input = 5 becomes f.f.f.f since it maxed out the depth.
+ *
+ * Return: the new physical address or CEC_PHYS_ADDR_INVALID.
+ */
+u16 cec_phys_addr_for_input(u16 phys_addr, u8 input);
+
+/**
+ * cec_phys_addr_validate() - validate a physical address from an EDID
+ *
+ * @phys_addr:	the physical address to validate
+ * @parent:	if not %NULL, then this is filled with the parents PA.
+ * @port:	if not %NULL, then this is filled with the input port.
+ *
+ * This validates a physical address as read from an EDID. If the
+ * PA is invalid (such as 1.0.1.0 since '0' is only allowed at the end),
+ * then it will return -EINVAL.
+ *
+ * The parent PA is passed into %parent and the input port is passed into
+ * %port. For example:
+ *
+ * PA = 0.0.0.0: has parent 0.0.0.0 and input port 0.
+ *
+ * PA = 1.0.0.0: has parent 0.0.0.0 and input port 1.
+ *
+ * PA = 3.2.0.0: has parent 3.0.0.0 and input port 2.
+ *
+ * PA = f.f.f.f: has parent f.f.f.f and input port 0.
+ *
+ * Return: 0 if the PA is valid, -EINVAL if not.
+ */
+int cec_phys_addr_validate(u16 phys_addr, u16 *parent, u16 *port);
+
+#endif /* _MEDIA_CEC_EDID_H */
diff --git a/include/media/cec.h b/include/media/cec.h
new file mode 100644
index 0000000..2dcd838
--- /dev/null
+++ b/include/media/cec.h
@@ -0,0 +1,236 @@
+/*
+ * cec - HDMI Consumer Electronics Control support header
+ *
+ * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _MEDIA_CEC_H
+#define _MEDIA_CEC_H
+
+#include <linux/poll.h>
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/cdev.h>
+#include <linux/kthread.h>
+#include <linux/timer.h>
+#include <linux/cec-funcs.h>
+#include <media/rc-core.h>
+#include <media/cec-edid.h>
+
+/**
+ * struct cec_devnode - cec device node
+ * @dev:	cec device
+ * @cdev:	cec character device
+ * @parent:	parent device
+ * @minor:	device node minor number
+ * @registered:	the device was correctly registered
+ * @unregistered: the device was unregistered
+ * @fhs_lock:	lock to control access to the filehandle list
+ * @fhs:	the list of open filehandles (cec_fh)
+ *
+ * This structure represents a cec-related device node.
+ *
+ * The @parent is a physical device. It must be set by core or device drivers
+ * before registering the node.
+ */
+struct cec_devnode {
+	/* sysfs */
+	struct device dev;
+	struct cdev cdev;
+	struct device *parent;
+
+	/* device info */
+	int minor;
+	bool registered;
+	bool unregistered;
+	struct mutex fhs_lock;
+	struct list_head fhs;
+};
+
+struct cec_adapter;
+struct cec_data;
+
+struct cec_data {
+	struct list_head list;
+	struct list_head xfer_list;
+	struct cec_adapter *adap;
+	struct cec_msg msg;
+	struct cec_fh *fh;
+	struct delayed_work work;
+	struct completion c;
+	u8 attempts;
+	bool new_initiator;
+	bool blocking;
+	bool completed;
+};
+
+struct cec_msg_entry {
+	struct list_head	list;
+	struct cec_msg		msg;
+};
+
+#define CEC_NUM_EVENTS		CEC_EVENT_LOST_MSGS
+
+struct cec_event_queue {
+	unsigned		elems;
+	unsigned		num_events;
+	struct cec_event	*events;
+};
+
+struct cec_fh {
+	struct list_head	list;
+	struct list_head	xfer_list;
+	struct cec_adapter	*adap;
+	u8			mode_initiator;
+	u8			mode_follower;
+
+	/* Events */
+	wait_queue_head_t	wait;
+	unsigned		events;
+	struct cec_event_queue	evqueue[CEC_NUM_EVENTS];
+	struct mutex		lock;
+	struct list_head	msgs; /* queued messages */
+	unsigned		queued_msgs;
+	unsigned		lost_msgs;
+};
+
+#define CEC_SIGNAL_FREE_TIME_RETRY		3
+#define CEC_SIGNAL_FREE_TIME_NEW_INITIATOR	5
+#define CEC_SIGNAL_FREE_TIME_NEXT_XFER		7
+
+/* The nominal data bit period is 2.4 ms */
+#define CEC_FREE_TIME_TO_USEC(ft)		((ft) * 2400)
+
+struct cec_adap_ops {
+	/* Low-level callbacks */
+	int (*adap_enable)(struct cec_adapter *adap, bool enable);
+	int (*adap_monitor_all_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, u8 attempts,
+			     u32 signal_free_time, struct cec_msg *msg);
+	void (*adap_status)(struct cec_adapter *adap, struct seq_file *file);
+
+	/* High-level CEC message callback */
+	int (*received)(struct cec_adapter *adap, struct cec_msg *msg);
+};
+
+/*
+ * The minimum message length you can receive (excepting poll messages) is 2.
+ * With a transfer rate of at most 36 bytes per second this makes 18 messages
+ * per second worst case.
+ *
+ * We queue at most 10 seconds worth of messages.
+ */
+#define CEC_MAX_MSG_QUEUE_SZ		(18 * 10)
+
+struct cec_adapter {
+	struct module *owner;
+	char name[32];
+	struct cec_devnode devnode;
+	struct mutex lock;
+	struct rc_dev *rc;
+
+	struct list_head transmit_queue;
+	struct list_head wait_queue;
+	struct cec_data *transmitting;
+
+	struct task_struct *kthread_config;
+	struct completion config_completion;
+
+	struct task_struct *kthread;
+	wait_queue_head_t kthread_waitq;
+	wait_queue_head_t waitq;
+
+	const struct cec_adap_ops *ops;
+	void *priv;
+	u32 capabilities;
+	u8 available_log_addrs;
+
+	u16 phys_addr;
+	bool is_configuring;
+	bool is_configured;
+	u32 monitor_all_cnt;
+	u32 follower_cnt;
+	struct cec_fh *cec_follower;
+	struct cec_fh *cec_initiator;
+	bool passthrough;
+	struct cec_log_addrs log_addrs;
+
+	struct dentry *cec_dir;
+	struct dentry *status_file;
+
+	u16 phys_addrs[15];
+	u32 sequence;
+
+	char input_name[32];
+	char input_phys[32];
+	char input_drv[32];
+};
+
+static inline bool cec_has_log_addr(const struct cec_adapter *adap, u8 log_addr)
+{
+	return adap->log_addrs.log_addr_mask & (1 << log_addr);
+}
+
+static inline bool cec_is_sink(const struct cec_adapter *adap)
+{
+	return adap->phys_addr == 0;
+}
+
+#if IS_ENABLED(CONFIG_MEDIA_CEC)
+struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
+		void *priv, const char *name, u32 caps, u8 available_las,
+		struct device *parent);
+int cec_register_adapter(struct cec_adapter *adap);
+void cec_unregister_adapter(struct cec_adapter *adap);
+void cec_delete_adapter(struct cec_adapter *adap);
+
+int cec_s_log_addrs(struct cec_adapter *adap, struct cec_log_addrs *log_addrs,
+		    bool block);
+void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr,
+		     bool block);
+int cec_transmit_msg(struct cec_adapter *adap, struct cec_msg *msg,
+		     bool block);
+
+/* Called by the adapter */
+void cec_transmit_done(struct cec_adapter *adap, u8 status, u8 arb_lost_cnt,
+		       u8 nack_cnt, u8 low_drive_cnt, u8 error_cnt);
+void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg);
+
+#else
+
+static inline int cec_register_adapter(struct cec_adapter *adap)
+{
+	return 0;
+}
+
+static inline void cec_unregister_adapter(struct cec_adapter *adap)
+{
+}
+
+static inline void cec_delete_adapter(struct cec_adapter *adap)
+{
+}
+
+static inline void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr,
+				   bool block)
+{
+}
+
+#endif
+
+#endif /* _MEDIA_CEC_H */
-- 
2.8.1


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

* [PATCHv16 05/13] cec/TODO: add TODO file so we know why this is still in staging
  2016-04-29 13:52 [PATCHv16 00/13] HDMI CEC framework Hans Verkuil
@ 2016-04-29 13:52   ` Hans Verkuil
  2016-04-29 13:52 ` [PATCHv16 02/13] HID: add HDMI CEC specific keycodes Hans Verkuil
                     ` (11 subsequent siblings)
  12 siblings, 0 replies; 35+ messages in thread
From: Hans Verkuil @ 2016-04-29 13:52 UTC (permalink / raw)
  To: linux-media
  Cc: dri-devel, linux-samsung-soc, linux-input, lars, linux, Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

Explain why cec.c is still in staging.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/staging/media/cec/TODO | 13 +++++++++++++
 1 file changed, 13 insertions(+)
 create mode 100644 drivers/staging/media/cec/TODO

diff --git a/drivers/staging/media/cec/TODO b/drivers/staging/media/cec/TODO
new file mode 100644
index 0000000..c0751ef
--- /dev/null
+++ b/drivers/staging/media/cec/TODO
@@ -0,0 +1,13 @@
+The reason why cec.c is still in staging is that I would like
+to have a bit more confidence in the uABI. The kABI is fine,
+no problem there, but I would like to let the public API mature
+a bit.
+
+Once I'm confident that I didn't miss anything then the cec.c source
+can move to drivers/media and the linux/cec.h and linux/cec-funcs.h
+headers can move to uapi/linux and added to uapi/linux/Kbuild to make
+them public.
+
+Hopefully this will happen later in 2016.
+
+Hans Verkuil <hans.verkuil@cisco.com>
-- 
2.8.1


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

* [PATCHv16 05/13] cec/TODO: add TODO file so we know why this is still in staging
@ 2016-04-29 13:52   ` Hans Verkuil
  0 siblings, 0 replies; 35+ messages in thread
From: Hans Verkuil @ 2016-04-29 13:52 UTC (permalink / raw)
  To: linux-media
  Cc: linux-samsung-soc, linux, dri-devel, lars, Hans Verkuil, linux-input

From: Hans Verkuil <hans.verkuil@cisco.com>

Explain why cec.c is still in staging.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/staging/media/cec/TODO | 13 +++++++++++++
 1 file changed, 13 insertions(+)
 create mode 100644 drivers/staging/media/cec/TODO

diff --git a/drivers/staging/media/cec/TODO b/drivers/staging/media/cec/TODO
new file mode 100644
index 0000000..c0751ef
--- /dev/null
+++ b/drivers/staging/media/cec/TODO
@@ -0,0 +1,13 @@
+The reason why cec.c is still in staging is that I would like
+to have a bit more confidence in the uABI. The kABI is fine,
+no problem there, but I would like to let the public API mature
+a bit.
+
+Once I'm confident that I didn't miss anything then the cec.c source
+can move to drivers/media and the linux/cec.h and linux/cec-funcs.h
+headers can move to uapi/linux and added to uapi/linux/Kbuild to make
+them public.
+
+Hopefully this will happen later in 2016.
+
+Hans Verkuil <hans.verkuil@cisco.com>
-- 
2.8.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCHv16 06/13] cec: add compat32 ioctl support
  2016-04-29 13:52 [PATCHv16 00/13] HDMI CEC framework Hans Verkuil
                   ` (4 preceding siblings ...)
  2016-04-29 13:52   ` Hans Verkuil
@ 2016-04-29 13:52 ` Hans Verkuil
  2016-04-29 13:52   ` Hans Verkuil
                   ` (6 subsequent siblings)
  12 siblings, 0 replies; 35+ messages in thread
From: Hans Verkuil @ 2016-04-29 13:52 UTC (permalink / raw)
  To: linux-media
  Cc: dri-devel, linux-samsung-soc, linux-input, lars, linux, Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

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.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 fs/compat_ioctl.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c
index bd01b92..c1e9f29 100644
--- a/fs/compat_ioctl.c
+++ b/fs/compat_ioctl.c
@@ -57,6 +57,7 @@
 #include <linux/i2c-dev.h>
 #include <linux/atalk.h>
 #include <linux/gfp.h>
+#include <linux/cec.h>
 
 #include "internal.h"
 
@@ -1377,6 +1378,17 @@ 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_PHYS_ADDR)
+COMPATIBLE_IOCTL(CEC_ADAP_S_PHYS_ADDR)
+COMPATIBLE_IOCTL(CEC_G_MODE)
+COMPATIBLE_IOCTL(CEC_S_MODE)
+COMPATIBLE_IOCTL(CEC_TRANSMIT)
+COMPATIBLE_IOCTL(CEC_RECEIVE)
+COMPATIBLE_IOCTL(CEC_DQEVENT)
 
 /* joystick */
 COMPATIBLE_IOCTL(JSIOCGVERSION)
-- 
2.8.1


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

* [PATCHv16 07/13] cec.txt: add CEC framework documentation
  2016-04-29 13:52 [PATCHv16 00/13] HDMI CEC framework Hans Verkuil
@ 2016-04-29 13:52   ` Hans Verkuil
  2016-04-29 13:52 ` [PATCHv16 02/13] HID: add HDMI CEC specific keycodes Hans Verkuil
                     ` (11 subsequent siblings)
  12 siblings, 0 replies; 35+ messages in thread
From: Hans Verkuil @ 2016-04-29 13:52 UTC (permalink / raw)
  To: linux-media
  Cc: dri-devel, linux-samsung-soc, linux-input, lars, linux,
	Hans Verkuil, Kamil Debski, Hans Verkuil

From: Hans Verkuil <hansverk@cisco.com>

Document the new HDMI CEC framework.

Signed-off-by: Hans Verkuil <hansverk@cisco.com>
[k.debski@samsung.com: add DocBook documentation by Hans Verkuil, with
Signed-off-by: Kamil Debski <kamil@wypas.org>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 Documentation/cec.txt | 267 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 267 insertions(+)
 create mode 100644 Documentation/cec.txt

diff --git a/Documentation/cec.txt b/Documentation/cec.txt
new file mode 100644
index 0000000..75155fe
--- /dev/null
+++ b/Documentation/cec.txt
@@ -0,0 +1,267 @@
+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_allocate_adapter() and deleted by calling cec_delete_adapter():
+
+struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
+	       void *priv, const char *name, u32 caps, u8 available_las,
+	       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. Note: this name will be copied.
+
+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.
+
+available_las: the number of simultaneous logical addresses that this
+	adapter can handle. Must be 1 <= available_las <= CEC_MAX_LOG_ADDRS.
+
+parent: the parent device.
+
+
+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);
+
+Note: if cec_register_adapter() fails, then call cec_delete_adapter() to
+clean up. But if cec_register_adapter() succeeded, then only call
+cec_unregister_adapter() to clean up, never cec_delete_adapter(). The
+unregister function will delete the adapter automatically once the last user
+of that /dev/cecX device has closed its file handle.
+
+
+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_monitor_all_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, u8 attempts,
+			     u32 signal_free_time, struct cec_msg *msg);
+	void (*adap_log_status)(struct cec_adapter *adap);
+
+	/* 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_allocate_adapter() is disabled.
+
+Note that adap_enable must return 0 if enable is false.
+
+
+To enable/disable the 'monitor all' mode:
+
+	int (*adap_monitor_all_enable)(struct cec_adapter *adap, bool enable);
+
+If enabled, then the adapter should be put in a mode to also monitor messages
+that not for us. Not all hardware supports this and this function is only
+called if the CEC_CAP_MONITOR_ALL capability is set. This callback is optional
+(some hardware may always be in 'monitor all' mode).
+
+Note that adap_monitor_all_enable must return 0 if enable is false.
+
+
+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 receive directed messages to that address.
+
+Note that adap_log_addr must return 0 if logical_addr is CEC_LOG_ADDR_INVALID.
+
+
+To transmit a new message:
+
+	int (*adap_transmit)(struct cec_adapter *adap, u8 attempts,
+			     u32 signal_free_time, struct cec_msg *msg);
+
+This transmits a new message. The attempts argument is the suggested number of
+attempts for the transmit.
+
+The signal_free_time is the number of data bit periods that the adapter should
+wait when the line is free before attempting to send a message. This value
+depends on whether this transmit is a retry, a message from a new initiator or
+a new message for the same initiator. Most hardware will handle this
+automatically, but in some cases this information is needed.
+
+The CEC_FREE_TIME_TO_USEC macro can be used to convert signal_free_time to
+microseconds (one data bit period is 2.4 ms).
+
+
+To log the current CEC hardware status:
+
+	void (*adap_status)(struct cec_adapter *adap, struct seq_file *file);
+
+This optional callback can be used to show the status of the CEC hardware.
+The status is available through debugfs: cat /sys/kernel/debug/cec/cecX/status
+
+
+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, u8 status, u8 arb_lost_cnt,
+		       u8 nack_cnt, u8 low_drive_cnt, u8 error_cnt);
+
+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
+took control of the CEC line and you lost the arbitration.
+CEC_TX_STATUS_NACK: the message was nacked (for a directed message) or
+acked (for a broadcast message). A retransmission is needed.
+CEC_TX_STATUS_LOW_DRIVE: low drive was detected on the CEC bus. This
+indicates that a follower detected an error on the bus and requested a
+retransmission.
+CEC_TX_STATUS_ERROR: some unspecified error occurred: this can be one of
+the previous two if the hardware cannot differentiate or something else
+entirely.
+CEC_TX_STATUS_MAX_RETRIES: could not transmit the message after
+trying multiple times. Should only be set by the driver if it has hardware
+support for retrying messages. If set, then the framework assumes that it
+doesn't have to make another attempt to transmit the message since the
+hardware did that already.
+
+The *_cnt arguments are the number of error conditions that were seen.
+This may be 0 if no information is available. Drivers that do not support
+hardware retry can just set the counter corresponding to the transmit error
+to 1, if the hardware does support retry then either set these counters to
+0 if the hardware provides no feedback of which errors occurred and how many
+times, or fill in the correct values as reported by the hardware.
+
+When a CEC message was received:
+
+void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg);
+
+Speaks for itself.
+
+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);
+};
+
+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.
+
+
+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.
+
+void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block);
+
+Change the physical address. This function will set adap->phys_addr and
+send an event if it has changed. If cec_s_log_addrs() has been called and
+the physical address has become valid, then the CEC framework will start
+claiming the logical addresses. If block is true, then this function won't
+return until this process has finished.
+
+When the physical address is set to a valid value the CEC adapter will
+be enabled (see the adap_enable op). When it is set to CEC_PHYS_ADDR_INVALID,
+then the CEC adapter will be disabled. If you change a valid physical address
+to another valid physical address, then this function will first set the
+address to CEC_PHYS_ADDR_INVALID before enabling the new physical address.
+
+int cec_s_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. To unconfigure all logical
+addresses call this function with log_addrs set to NULL or with
+log_addrs->num_log_addrs set to 0. The block argument is ignored when
+unconfiguring. This function will just return if the physical address is
+invalid. Once the physical address becomes valid, then the framework will
+attempt to claim these logical addresses.
-- 
2.8.1


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

* [PATCHv16 07/13] cec.txt: add CEC framework documentation
@ 2016-04-29 13:52   ` Hans Verkuil
  0 siblings, 0 replies; 35+ messages in thread
From: Hans Verkuil @ 2016-04-29 13:52 UTC (permalink / raw)
  To: linux-media
  Cc: linux-samsung-soc, linux, Kamil Debski, dri-devel, Hans Verkuil,
	lars, Hans Verkuil, linux-input

From: Hans Verkuil <hansverk@cisco.com>

Document the new HDMI CEC framework.

Signed-off-by: Hans Verkuil <hansverk@cisco.com>
[k.debski@samsung.com: add DocBook documentation by Hans Verkuil, with
Signed-off-by: Kamil Debski <kamil@wypas.org>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 Documentation/cec.txt | 267 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 267 insertions(+)
 create mode 100644 Documentation/cec.txt

diff --git a/Documentation/cec.txt b/Documentation/cec.txt
new file mode 100644
index 0000000..75155fe
--- /dev/null
+++ b/Documentation/cec.txt
@@ -0,0 +1,267 @@
+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_allocate_adapter() and deleted by calling cec_delete_adapter():
+
+struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
+	       void *priv, const char *name, u32 caps, u8 available_las,
+	       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. Note: this name will be copied.
+
+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.
+
+available_las: the number of simultaneous logical addresses that this
+	adapter can handle. Must be 1 <= available_las <= CEC_MAX_LOG_ADDRS.
+
+parent: the parent device.
+
+
+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);
+
+Note: if cec_register_adapter() fails, then call cec_delete_adapter() to
+clean up. But if cec_register_adapter() succeeded, then only call
+cec_unregister_adapter() to clean up, never cec_delete_adapter(). The
+unregister function will delete the adapter automatically once the last user
+of that /dev/cecX device has closed its file handle.
+
+
+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_monitor_all_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, u8 attempts,
+			     u32 signal_free_time, struct cec_msg *msg);
+	void (*adap_log_status)(struct cec_adapter *adap);
+
+	/* 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_allocate_adapter() is disabled.
+
+Note that adap_enable must return 0 if enable is false.
+
+
+To enable/disable the 'monitor all' mode:
+
+	int (*adap_monitor_all_enable)(struct cec_adapter *adap, bool enable);
+
+If enabled, then the adapter should be put in a mode to also monitor messages
+that not for us. Not all hardware supports this and this function is only
+called if the CEC_CAP_MONITOR_ALL capability is set. This callback is optional
+(some hardware may always be in 'monitor all' mode).
+
+Note that adap_monitor_all_enable must return 0 if enable is false.
+
+
+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 receive directed messages to that address.
+
+Note that adap_log_addr must return 0 if logical_addr is CEC_LOG_ADDR_INVALID.
+
+
+To transmit a new message:
+
+	int (*adap_transmit)(struct cec_adapter *adap, u8 attempts,
+			     u32 signal_free_time, struct cec_msg *msg);
+
+This transmits a new message. The attempts argument is the suggested number of
+attempts for the transmit.
+
+The signal_free_time is the number of data bit periods that the adapter should
+wait when the line is free before attempting to send a message. This value
+depends on whether this transmit is a retry, a message from a new initiator or
+a new message for the same initiator. Most hardware will handle this
+automatically, but in some cases this information is needed.
+
+The CEC_FREE_TIME_TO_USEC macro can be used to convert signal_free_time to
+microseconds (one data bit period is 2.4 ms).
+
+
+To log the current CEC hardware status:
+
+	void (*adap_status)(struct cec_adapter *adap, struct seq_file *file);
+
+This optional callback can be used to show the status of the CEC hardware.
+The status is available through debugfs: cat /sys/kernel/debug/cec/cecX/status
+
+
+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, u8 status, u8 arb_lost_cnt,
+		       u8 nack_cnt, u8 low_drive_cnt, u8 error_cnt);
+
+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
+took control of the CEC line and you lost the arbitration.
+CEC_TX_STATUS_NACK: the message was nacked (for a directed message) or
+acked (for a broadcast message). A retransmission is needed.
+CEC_TX_STATUS_LOW_DRIVE: low drive was detected on the CEC bus. This
+indicates that a follower detected an error on the bus and requested a
+retransmission.
+CEC_TX_STATUS_ERROR: some unspecified error occurred: this can be one of
+the previous two if the hardware cannot differentiate or something else
+entirely.
+CEC_TX_STATUS_MAX_RETRIES: could not transmit the message after
+trying multiple times. Should only be set by the driver if it has hardware
+support for retrying messages. If set, then the framework assumes that it
+doesn't have to make another attempt to transmit the message since the
+hardware did that already.
+
+The *_cnt arguments are the number of error conditions that were seen.
+This may be 0 if no information is available. Drivers that do not support
+hardware retry can just set the counter corresponding to the transmit error
+to 1, if the hardware does support retry then either set these counters to
+0 if the hardware provides no feedback of which errors occurred and how many
+times, or fill in the correct values as reported by the hardware.
+
+When a CEC message was received:
+
+void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg);
+
+Speaks for itself.
+
+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);
+};
+
+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.
+
+
+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.
+
+void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block);
+
+Change the physical address. This function will set adap->phys_addr and
+send an event if it has changed. If cec_s_log_addrs() has been called and
+the physical address has become valid, then the CEC framework will start
+claiming the logical addresses. If block is true, then this function won't
+return until this process has finished.
+
+When the physical address is set to a valid value the CEC adapter will
+be enabled (see the adap_enable op). When it is set to CEC_PHYS_ADDR_INVALID,
+then the CEC adapter will be disabled. If you change a valid physical address
+to another valid physical address, then this function will first set the
+address to CEC_PHYS_ADDR_INVALID before enabling the new physical address.
+
+int cec_s_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. To unconfigure all logical
+addresses call this function with log_addrs set to NULL or with
+log_addrs->num_log_addrs set to 0. The block argument is ignored when
+unconfiguring. This function will just return if the physical address is
+invalid. Once the physical address becomes valid, then the framework will
+attempt to claim these logical addresses.
-- 
2.8.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCHv16 08/13] DocBook/media: add CEC documentation
  2016-04-29 13:52 [PATCHv16 00/13] HDMI CEC framework Hans Verkuil
                   ` (6 preceding siblings ...)
  2016-04-29 13:52   ` Hans Verkuil
@ 2016-04-29 13:52 ` Hans Verkuil
  2016-06-16 21:09   ` Mauro Carvalho Chehab
  2016-04-29 13:52 ` [PATCHv16 09/13] cec: adv7604: add cec support Hans Verkuil
                   ` (4 subsequent siblings)
  12 siblings, 1 reply; 35+ messages in thread
From: Hans Verkuil @ 2016-04-29 13:52 UTC (permalink / raw)
  To: linux-media
  Cc: dri-devel, linux-samsung-soc, linux-input, lars, linux,
	Hans Verkuil, Kamil Debski, Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

Add DocBook documentation for the CEC API.

Signed-off-by: Hans Verkuil <hansverk@cisco.com>
[k.debski@samsung.com: add documentation for passthrough mode]
[k.debski@samsung.com: minor fixes and change of reserved field sizes]
Signed-off-by: Kamil Debski <kamil@wypas.org>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 Documentation/DocBook/device-drivers.tmpl          |   4 +
 Documentation/DocBook/media/Makefile               |   2 +
 Documentation/DocBook/media/v4l/biblio.xml         |  10 +
 Documentation/DocBook/media/v4l/cec-api.xml        |  72 +++++
 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      | 140 +++++++++
 .../DocBook/media/v4l/cec-ioc-adap-g-log-addrs.xml | 324 +++++++++++++++++++++
 .../DocBook/media/v4l/cec-ioc-adap-g-phys-addr.xml |  82 ++++++
 .../DocBook/media/v4l/cec-ioc-dqevent.xml          | 190 ++++++++++++
 Documentation/DocBook/media/v4l/cec-ioc-g-mode.xml | 245 ++++++++++++++++
 .../DocBook/media/v4l/cec-ioc-receive.xml          | 260 +++++++++++++++++
 Documentation/DocBook/media_api.tmpl               |   6 +-
 15 files changed, 1649 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-dqevent.xml
 create mode 100644 Documentation/DocBook/media/v4l/cec-ioc-g-mode.xml
 create mode 100644 Documentation/DocBook/media/v4l/cec-ioc-receive.xml

diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl
index 893b2ca..31258bf 100644
--- a/Documentation/DocBook/device-drivers.tmpl
+++ b/Documentation/DocBook/device-drivers.tmpl
@@ -270,6 +270,10 @@ 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
+!Iinclude/media/cec-edid.h
+     </sect1>
 
   </chapter>
 
diff --git a/Documentation/DocBook/media/Makefile b/Documentation/DocBook/media/Makefile
index 2840ff4..fdc1386 100644
--- a/Documentation/DocBook/media/Makefile
+++ b/Documentation/DocBook/media/Makefile
@@ -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/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/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)
 
diff --git a/Documentation/DocBook/media/v4l/biblio.xml b/Documentation/DocBook/media/v4l/biblio.xml
index 9beb30f..87f1d24 100644
--- a/Documentation/DocBook/media/v4l/biblio.xml
+++ b/Documentation/DocBook/media/v4l/biblio.xml
@@ -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>
diff --git a/Documentation/DocBook/media/v4l/cec-api.xml b/Documentation/DocBook/media/v4l/cec-api.xml
new file mode 100644
index 0000000..caa04c0
--- /dev/null
+++ b/Documentation/DocBook/media/v4l/cec-api.xml
@@ -0,0 +1,72 @@
+<partinfo>
+  <authorgroup>
+    <author>
+      <firstname>Hans</firstname>
+      <surname>Verkuil</surname>
+      <affiliation><address><email>hans.verkuil@cisco.com</email></address></affiliation>
+      <contrib>Initial version.</contrib>
+    </author>
+  </authorgroup>
+  <copyright>
+    <year>2016</year>
+    <holder>Hans Verkuil</holder>
+  </copyright>
+
+  <revhistory>
+    <!-- Put document revisions here, newest first. -->
+    <revision>
+      <revnumber>1.0.0</revnumber>
+      <date>2016-03-17</date>
+      <authorinitials>hv</authorinitials>
+      <revremark>Initial revision</revremark>
+    </revision>
+  </revhistory>
+</partinfo>
+
+<title>CEC API</title>
+
+<chapter id="cec-api">
+  <title>CEC: Consumer Electronics Control</title>
+
+  <section id="cec-intro">
+    <title>Introduction</title>
+    <para>HDMI connectors provide a single pin for use by the Consumer Electronics
+    Control protocol. This protocol allows different devices connected by an HDMI cable
+    to communicate. The protocol for CEC version 1.4 is defined in supplements 1 (CEC)
+    and 2 (HEAC or HDMI Ethernet and Audio Return Channel) of the HDMI 1.4a
+    (<xref linkend="hdmi" />) specification and the extensions added to CEC version 2.0
+    are defined in chapter 11 of the HDMI 2.0 (<xref linkend="hdmi2" />) specification.
+    </para>
+
+    <para>The bitrate is very slow (effectively no more than 36 bytes per second) and
+    is based on the ancient AV.link protocol used in old SCART connectors. The protocol
+    closely resembles a crazy Rube Goldberg contraption and is an unholy mix of low and
+    high level messages. Some messages, especially those part of the HEAC protocol layered
+    on top of CEC, need to be handled by the kernel, others can be handled either by the
+    kernel or by userspace.</para>
+
+    <para>In addition, CEC can be implemented in HDMI receivers, transmitters and in USB
+    devices that have an HDMI input and an HDMI output and that control just the CEC pin.</para>
+
+    <para>Drivers that support CEC and that allow (or require) userspace to handle CEC
+    messages and/or configure the CEC adapter will create a CEC device node (/dev/cecX)
+    to give userspace access to the CEC adapter. The &CEC-ADAP-G-CAPS; ioctl will tell userspace
+    what it is allowed to do.</para>
+  </section>
+</chapter>
+
+<appendix id="cec-user-func">
+  <title>Function Reference</title>
+  <!-- Keep this alphabetically sorted. -->
+  &sub-cec-func-open;
+  &sub-cec-func-close;
+  &sub-cec-func-ioctl;
+  &sub-cec-func-poll;
+  <!-- All ioctls go here. -->
+  &sub-cec-ioc-adap-g-caps;
+  &sub-cec-ioc-adap-g-log-addrs;
+  &sub-cec-ioc-adap-g-phys-addr;
+  &sub-cec-ioc-dqevent;
+  &sub-cec-ioc-g-mode;
+  &sub-cec-ioc-receive;
+</appendix>
diff --git a/Documentation/DocBook/media/v4l/cec-func-close.xml b/Documentation/DocBook/media/v4l/cec-func-close.xml
new file mode 100644
index 0000000..c3978af
--- /dev/null
+++ b/Documentation/DocBook/media/v4l/cec-func-close.xml
@@ -0,0 +1,59 @@
+<refentry id="cec-func-close">
+  <refmeta>
+    <refentrytitle>cec close()</refentrytitle>
+    &manvol;
+  </refmeta>
+
+  <refnamediv>
+    <refname>cec-close</refname>
+    <refpurpose>Close a cec device</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;unistd.h&gt;</funcsynopsisinfo>
+      <funcprototype>
+	<funcdef>int <function>close</function></funcdef>
+	<paramdef>int <parameter>fd</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Arguments</title>
+
+    <variablelist>
+      <varlistentry>
+	<term><parameter>fd</parameter></term>
+	<listitem>
+	  <para>&fd;</para>
+	</listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>Closes the cec device. Resources associated with the file descriptor
+    are freed. The device configuration remain unchanged.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para><function>close</function> returns 0 on success. On error, -1 is
+    returned, and <varname>errno</varname> is set appropriately. Possible error
+    codes are:</para>
+
+    <variablelist>
+      <varlistentry>
+	<term><errorcode>EBADF</errorcode></term>
+	<listitem>
+	  <para><parameter>fd</parameter> is not a valid open file descriptor.
+	  </para>
+	</listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+</refentry>
diff --git a/Documentation/DocBook/media/v4l/cec-func-ioctl.xml b/Documentation/DocBook/media/v4l/cec-func-ioctl.xml
new file mode 100644
index 0000000..0480eeb
--- /dev/null
+++ b/Documentation/DocBook/media/v4l/cec-func-ioctl.xml
@@ -0,0 +1,73 @@
+<refentry id="cec-func-ioctl">
+  <refmeta>
+    <refentrytitle>cec ioctl()</refentrytitle>
+    &manvol;
+  </refmeta>
+
+  <refnamediv>
+    <refname>cec-ioctl</refname>
+    <refpurpose>Control a cec device</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;sys/ioctl.h&gt;</funcsynopsisinfo>
+      <funcprototype>
+	<funcdef>int <function>ioctl</function></funcdef>
+	<paramdef>int <parameter>fd</parameter></paramdef>
+	<paramdef>int <parameter>request</parameter></paramdef>
+	<paramdef>void *<parameter>argp</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Arguments</title>
+
+    <variablelist>
+      <varlistentry>
+	<term><parameter>fd</parameter></term>
+	<listitem>
+	  <para>&fd;</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><parameter>request</parameter></term>
+	<listitem>
+	  <para>CEC ioctl request code as defined in the cec.h header file,
+	  for example CEC_ADAP_G_CAPS.</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><parameter>argp</parameter></term>
+	<listitem>
+	  <para>Pointer to a request-specific structure.</para>
+	</listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Description</title>
+    <para>The <function>ioctl()</function> function manipulates cec device
+    parameters. The argument <parameter>fd</parameter> must be an open file
+    descriptor.</para>
+    <para>The ioctl <parameter>request</parameter> code specifies the cec
+    function to be called. It has encoded in it whether the argument is an
+    input, output or read/write parameter, and the size of the argument
+    <parameter>argp</parameter> in bytes.</para>
+    <para>Macros and structures definitions specifying cec ioctl requests and
+    their parameters are located in the cec.h header file. All cec ioctl
+    requests, their respective function and parameters are specified in
+    <xref linkend="cec-user-func" />.</para>
+  </refsect1>
+
+  <refsect1>
+    &return-value;
+
+    <para>Request-specific error codes are listed in the
+    individual requests descriptions.</para>
+    <para>When an ioctl that takes an output or read/write parameter fails,
+    the parameter remains unmodified.</para>
+  </refsect1>
+</refentry>
diff --git a/Documentation/DocBook/media/v4l/cec-func-open.xml b/Documentation/DocBook/media/v4l/cec-func-open.xml
new file mode 100644
index 0000000..d814847
--- /dev/null
+++ b/Documentation/DocBook/media/v4l/cec-func-open.xml
@@ -0,0 +1,94 @@
+<refentry id="cec-func-open">
+  <refmeta>
+    <refentrytitle>cec open()</refentrytitle>
+    &manvol;
+  </refmeta>
+
+  <refnamediv>
+    <refname>cec-open</refname>
+    <refpurpose>Open a cec device</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;fcntl.h&gt;</funcsynopsisinfo>
+      <funcprototype>
+	<funcdef>int <function>open</function></funcdef>
+	<paramdef>const char *<parameter>device_name</parameter></paramdef>
+	<paramdef>int <parameter>flags</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Arguments</title>
+
+    <variablelist>
+      <varlistentry>
+	<term><parameter>device_name</parameter></term>
+	<listitem>
+	  <para>Device to be opened.</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><parameter>flags</parameter></term>
+	<listitem>
+	  <para>Open flags. Access mode must be either <constant>O_RDONLY</constant>
+	  or <constant>O_RDWR</constant>. Other flags have no effect.</para>
+	</listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+  <refsect1>
+    <title>Description</title>
+    <para>To open a cec device applications call <function>open()</function>
+    with the desired device name. The function has no side effects; the device
+    configuration remain unchanged.</para>
+    <para>When the device is opened in read-only mode, attempts to modify its
+    configuration will result in an error, and <varname>errno</varname> will be
+    set to <errorcode>EBADF</errorcode>.</para>
+  </refsect1>
+  <refsect1>
+    <title>Return Value</title>
+
+    <para><function>open</function> returns the new file descriptor on success.
+    On error, -1 is returned, and <varname>errno</varname> is set appropriately.
+    Possible error codes are:</para>
+
+    <variablelist>
+      <varlistentry>
+	<term><errorcode>EACCES</errorcode></term>
+	<listitem>
+	  <para>The requested access to the file is not allowed.</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><errorcode>EMFILE</errorcode></term>
+	<listitem>
+	  <para>The  process  already  has  the  maximum number of files open.
+	  </para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><errorcode>ENFILE</errorcode></term>
+	<listitem>
+	  <para>The system limit on the total number of open files has been
+	  reached.</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><errorcode>ENOMEM</errorcode></term>
+	<listitem>
+	  <para>Insufficient kernel memory was available.</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><errorcode>ENXIO</errorcode></term>
+	<listitem>
+	  <para>No device corresponding to this device special file exists.
+	  </para>
+	</listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+</refentry>
diff --git a/Documentation/DocBook/media/v4l/cec-func-poll.xml b/Documentation/DocBook/media/v4l/cec-func-poll.xml
new file mode 100644
index 0000000..6853817
--- /dev/null
+++ b/Documentation/DocBook/media/v4l/cec-func-poll.xml
@@ -0,0 +1,89 @@
+<refentry id="cec-func-poll">
+  <refmeta>
+    <refentrytitle>cec poll()</refentrytitle>
+    &manvol;
+  </refmeta>
+
+  <refnamediv>
+    <refname>cec-poll</refname>
+    <refpurpose>Wait for some event on a file descriptor</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;sys/poll.h&gt;</funcsynopsisinfo>
+      <funcprototype>
+	<funcdef>int <function>poll</function></funcdef>
+	<paramdef>struct pollfd *<parameter>ufds</parameter></paramdef>
+	<paramdef>unsigned int <parameter>nfds</parameter></paramdef>
+	<paramdef>int <parameter>timeout</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>With the <function>poll()</function> function applications
+can wait for CEC events.</para>
+
+    <para>On success <function>poll()</function> returns the number of
+file descriptors that have been selected (that is, file descriptors
+for which the <structfield>revents</structfield> field of the
+respective <structname>pollfd</structname> structure is non-zero).
+CEC devices set the <constant>POLLIN</constant> and
+<constant>POLLRDNORM</constant> flags in the
+<structfield>revents</structfield> field if there are messages in the
+receive queue. If the transmit queue has room for new messages, the
+<constant>POLLOUT</constant> and <constant>POLLWRNORM</constant>
+flags are set. If there are events in the event queue, then the
+<constant>POLLPRI</constant> flag is set.
+When the function timed out it returns a value of zero, on
+failure it returns <returnvalue>-1</returnvalue> and the
+<varname>errno</varname> variable is set appropriately.
+</para>
+
+    <para>For more details see the
+<function>poll()</function> manual page.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, <function>poll()</function> returns the number
+structures which have non-zero <structfield>revents</structfield>
+fields, or zero if the call timed out. On error
+<returnvalue>-1</returnvalue> is returned, and the
+<varname>errno</varname> variable is set appropriately:</para>
+
+    <variablelist>
+      <varlistentry>
+	<term><errorcode>EBADF</errorcode></term>
+	<listitem>
+	  <para>One or more of the <parameter>ufds</parameter> members
+specify an invalid file descriptor.</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><errorcode>EFAULT</errorcode></term>
+	<listitem>
+	  <para><parameter>ufds</parameter> references an inaccessible
+memory area.</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><errorcode>EINTR</errorcode></term>
+	<listitem>
+	  <para>The call was interrupted by a signal.</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><errorcode>EINVAL</errorcode></term>
+	<listitem>
+	  <para>The <parameter>nfds</parameter> argument is greater
+than <constant>OPEN_MAX</constant>.</para>
+	</listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+</refentry>
diff --git a/Documentation/DocBook/media/v4l/cec-ioc-adap-g-caps.xml b/Documentation/DocBook/media/v4l/cec-ioc-adap-g-caps.xml
new file mode 100644
index 0000000..b99ed22
--- /dev/null
+++ b/Documentation/DocBook/media/v4l/cec-ioc-adap-g-caps.xml
@@ -0,0 +1,140 @@
+<refentry id="cec-ioc-adap-g-caps">
+  <refmeta>
+    <refentrytitle>ioctl CEC_ADAP_G_CAPS</refentrytitle>
+    &manvol;
+  </refmeta>
+
+  <refnamediv>
+    <refname>CEC_ADAP_G_CAPS</refname>
+    <refpurpose>Query device capabilities</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcprototype>
+	<funcdef>int <function>ioctl</function></funcdef>
+	<paramdef>int <parameter>fd</parameter></paramdef>
+	<paramdef>int <parameter>request</parameter></paramdef>
+	<paramdef>struct cec_caps *<parameter>argp</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Arguments</title>
+
+    <variablelist>
+      <varlistentry>
+	<term><parameter>fd</parameter></term>
+	<listitem>
+	  <para>File descriptor returned by
+	  <link linkend='cec-func-open'><function>open()</function></link>.</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><parameter>request</parameter></term>
+	<listitem>
+	  <para>CEC_ADAP_G_CAPS</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><parameter>argp</parameter></term>
+	<listitem>
+	  <para></para>
+	</listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>All cec devices must support the <constant>CEC_ADAP_G_CAPS</constant>
+    ioctl. To query device information, applications call the ioctl with a
+    pointer to a &cec-caps;. The driver fills the structure and returns
+    the information to the application.
+    The ioctl never fails.</para>
+
+    <table pgwide="1" frame="none" id="cec-caps">
+      <title>struct <structname>cec_caps</structname></title>
+      <tgroup cols="3">
+	&cs-str;
+	<tbody valign="top">
+	  <row>
+	    <entry>char</entry>
+	    <entry><structfield>driver[32]</structfield></entry>
+	    <entry>The name of the cec adapter driver.</entry>
+	  </row>
+	  <row>
+	    <entry>char</entry>
+	    <entry><structfield>name[32]</structfield></entry>
+	    <entry>The name of this CEC adapter. The combination <structfield>driver</structfield>
+	    and <structfield>name</structfield> must be unique.</entry>
+	  </row>
+	  <row>
+	    <entry>__u32</entry>
+	    <entry><structfield>capabilities</structfield></entry>
+	    <entry>The capabilities of the CEC adapter, see <xref
+		linkend="cec-capabilities" />.</entry>
+	  </row>
+	  <row>
+	    <entry>__u32</entry>
+	    <entry><structfield>version</structfield></entry>
+	    <entry>CEC Framework API version, formatted with the
+	    <constant>KERNEL_VERSION()</constant> macro.</entry>
+	  </row>
+	</tbody>
+      </tgroup>
+    </table>
+
+    <table pgwide="1" frame="none" id="cec-capabilities">
+      <title>CEC Capabilities Flags</title>
+      <tgroup cols="3">
+	&cs-def;
+	<tbody valign="top">
+	  <row>
+	    <entry><constant>CEC_CAP_PHYS_ADDR</constant></entry>
+	    <entry>0x00000001</entry>
+	    <entry>Userspace has to configure the physical address by
+	    calling &CEC-ADAP-S-PHYS-ADDR;.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_CAP_LOG_ADDRS</constant></entry>
+	    <entry>0x00000002</entry>
+	    <entry>Userspace has to configure the logical addresses by
+	    calling &CEC-ADAP-S-LOG-ADDRS;.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_CAP_TRANSMIT</constant></entry>
+	    <entry>0x00000004</entry>
+	    <entry>Userspace can transmit CEC messages by calling &CEC-TRANSMIT;. This
+	    implies that userspace can be a follower as well, since being able to
+	    transmit messages is a prerequisite of becoming a follower.
+	    </entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_CAP_PASSTHROUGH</constant></entry>
+	    <entry>0x00000008</entry>
+	    <entry>Userspace can use the passthrough mode by
+	    calling &CEC-S-MODE;.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_CAP_RC</constant></entry>
+	    <entry>0x00000010</entry>
+	    <entry>This adapter supports the remote control protocol.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_CAP_MONITOR_ALL</constant></entry>
+	    <entry>0x00000020</entry>
+	    <entry>The CEC hardware can monitor all messages, not just directed and
+	    broadcast messages.</entry>
+	  </row>
+	</tbody>
+      </tgroup>
+    </table>
+  </refsect1>
+
+  <refsect1>
+    &return-value;
+  </refsect1>
+</refentry>
diff --git a/Documentation/DocBook/media/v4l/cec-ioc-adap-g-log-addrs.xml b/Documentation/DocBook/media/v4l/cec-ioc-adap-g-log-addrs.xml
new file mode 100644
index 0000000..01bc5ab
--- /dev/null
+++ b/Documentation/DocBook/media/v4l/cec-ioc-adap-g-log-addrs.xml
@@ -0,0 +1,324 @@
+<refentry id="cec-ioc-adap-g-log-addrs">
+  <refmeta>
+    <refentrytitle>ioctl CEC_ADAP_G_LOG_ADDRS, CEC_ADAP_S_LOG_ADDRS</refentrytitle>
+    &manvol;
+  </refmeta>
+
+  <refnamediv>
+    <refname>CEC_ADAP_G_LOG_ADDRS</refname>
+    <refname>CEC_ADAP_S_LOG_ADDRS</refname>
+    <refpurpose>Get or set the logical addresses</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcprototype>
+	<funcdef>int <function>ioctl</function></funcdef>
+	<paramdef>int <parameter>fd</parameter></paramdef>
+	<paramdef>int <parameter>request</parameter></paramdef>
+	<paramdef>struct cec_log_addrs *<parameter>argp</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Arguments</title>
+
+    <variablelist>
+      <varlistentry>
+	<term><parameter>fd</parameter></term>
+	<listitem>
+	  <para>File descriptor returned by
+	  <link linkend='cec-func-open'><function>open()</function></link>.</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><parameter>request</parameter></term>
+	<listitem>
+	  <para>CEC_ADAP_G_LOG_ADDRS, CEC_ADAP_S_LOG_ADDRS</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><parameter>argp</parameter></term>
+	<listitem>
+	  <para></para>
+	</listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>To query the current CEC logical addresses applications call the
+<constant>CEC_ADAP_G_LOG_ADDRS</constant> ioctl with a pointer to a
+<structname>cec_log_addrs</structname> structure where the drivers stores the
+logical addresses.</para>
+
+    <para>To set new logical addresses applications fill in struct <structname>cec_log_addrs</structname>
+and call the <constant>CEC_ADAP_S_LOG_ADDRS</constant> ioctl with a pointer to this struct.
+The <constant>CEC_ADAP_S_LOG_ADDRS</constant> ioctl is only available if
+<constant>CEC_CAP_LOG_ADDRS</constant> is set. This ioctl will block until all
+requested logical addresses have been claimed. <constant>CEC_ADAP_S_LOG_ADDRS</constant>
+can only be called by a file handle in initiator mode (see &CEC-S-MODE;).</para>
+
+    <table pgwide="1" frame="none" id="cec-log-addrs">
+      <title>struct <structname>cec_log_addrs</structname></title>
+      <tgroup cols="3">
+	&cs-str;
+	<tbody valign="top">
+	  <row>
+	    <entry>__u8</entry>
+	    <entry><structfield>log_addr</structfield>[CEC_MAX_LOG_ADDRS]</entry>
+	    <entry>The actual logical addresses that were claimed. This is set by the
+	    driver. If no logical address could be claimed, then it is set to
+	    <constant>CEC_LOG_ADDR_INVALID</constant>. If this adapter is Unregistered,
+	    then <structfield>log_addr[0]</structfield> is set to 0xf and all others to
+	    <constant>CEC_LOG_ADDR_INVALID</constant>.</entry>
+	  </row>
+	  <row>
+	    <entry>__u16</entry>
+	    <entry><structfield>log_addr_mask</structfield></entry>
+	    <entry>The bitmask of all logical addresses this adapter has claimed.
+	    If this adapter is Unregistered then <structfield>log_addr_mask</structfield>
+	    sets bit 15, if this adapter is not configured at all, then
+	    <structfield>log_addr_mask</structfield> is set to 0. Set by the driver.</entry>
+	  </row>
+	  <row>
+	    <entry>__u8</entry>
+	    <entry><structfield>cec_version</structfield></entry>
+	    <entry>The CEC version that this adapter shall use. See
+	    <xref linkend="cec-versions" />.
+	    Used to implement the <constant>CEC_MSG_CEC_VERSION</constant> and
+	    <constant>CEC_MSG_REPORT_FEATURES</constant> messages. Note that
+	    <constant>CEC_OP_CEC_VERSION_1_3A</constant> is not allowed
+	    by the CEC framework.
+	    </entry>
+	  </row>
+	  <row>
+	    <entry>__u8</entry>
+	    <entry><structfield>num_log_addrs</structfield></entry>
+	    <entry>Number of logical addresses to set up. Must be &le;
+	    <structfield>available_log_addrs</structfield> as returned by
+	    &CEC-ADAP-G-CAPS;. All arrays in this structure are only filled up to
+	    index <structfield>available_log_addrs</structfield>-1. The remaining
+	    array elements will be ignored. Note that the CEC 2.0 standard allows
+	    for a maximum of 2 logical addresses, although some hardware has support
+	    for more. <constant>CEC_MAX_LOG_ADDRS</constant> is 4. The driver will
+	    return the actual number of logical addresses it could claim, which may
+	    be less than what was requested. If this field is set to 0, then the
+	    CEC adapter shall clear all claimed logical addresses and all other
+	    fields will be ignored.</entry>
+	  </row>
+	  <row>
+	    <entry>__u32</entry>
+	    <entry><structfield>vendor_id</structfield></entry>
+	    <entry>The vendor ID is a 24-bit number that identifies the specific
+	    vendor or entity. Based on this ID vendor specific commands may be
+	    defined. If you do not want a vendor ID then set it to
+	    <constant>CEC_VENDOR_ID_NONE</constant>.</entry>
+	  </row>
+	  <row>
+	    <entry>__u32</entry>
+	    <entry><structfield>flags</structfield></entry>
+	    <entry>Flags. No flags are defined yet, so set this to 0.</entry>
+	  </row>
+	  <row>
+	    <entry>char</entry>
+	    <entry><structfield>osd_name</structfield>[15]</entry>
+	    <entry>The On-Screen Display name as is returned by the
+	    <constant>CEC_MSG_SET_OSD_NAME</constant> message.</entry>
+	  </row>
+	  <row>
+	    <entry>__u8</entry>
+	    <entry><structfield>primary_device_type</structfield>[CEC_MAX_LOG_ADDRS]</entry>
+	    <entry>Primary device type for each logical address. See
+	    <xref linkend="cec-prim-dev-types" /> for possible types.</entry>
+	  </row>
+	  <row>
+	    <entry>__u8</entry>
+	    <entry><structfield>log_addr_type</structfield>[CEC_MAX_LOG_ADDRS]</entry>
+	    <entry>Logical address types. See <xref linkend="cec-log-addr-types" /> for
+	    possible types. The driver will update this with the actual logical address
+	    type that it claimed (e.g. it may have to fallback to
+	    <constant>CEC_LOG_ADDR_TYPE_UNREGISTERED</constant>).</entry>
+	  </row>
+	  <row>
+	    <entry>__u8</entry>
+	    <entry><structfield>all_device_types</structfield>[CEC_MAX_LOG_ADDRS]</entry>
+	    <entry>CEC 2.0 specific: all device types. See <xref linkend="cec-all-dev-types-flags" />.
+	    Used to implement the <constant>CEC_MSG_REPORT_FEATURES</constant> message.
+	    This field is ignored if <structfield>cec_version</structfield> &lt;
+	    <constant>CEC_OP_CEC_VERSION_2_0</constant>.</entry>
+	  </row>
+	  <row>
+	    <entry>__u8</entry>
+	    <entry><structfield>features</structfield>[CEC_MAX_LOG_ADDRS][12]</entry>
+	    <entry>Features for each logical address. Used to implement the
+	    <constant>CEC_MSG_REPORT_FEATURES</constant> message. The 12 bytes include
+	    both the RC Profile and the Device Features.
+	    This field is ignored if <structfield>cec_version</structfield> &lt;
+	    <constant>CEC_OP_CEC_VERSION_2_0</constant>.</entry>
+	  </row>
+	</tbody>
+      </tgroup>
+    </table>
+
+    <table pgwide="1" frame="none" id="cec-versions">
+      <title>CEC Versions</title>
+      <tgroup cols="3">
+	&cs-def;
+	<tbody valign="top">
+	  <row>
+	    <entry><constant>CEC_OP_CEC_VERSION_1_3A</constant></entry>
+	    <entry>4</entry>
+	    <entry>CEC version according to the HDMI 1.3a standard.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_OP_CEC_VERSION_1_4B</constant></entry>
+	    <entry>5</entry>
+	    <entry>CEC version according to the HDMI 1.4b standard.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_OP_CEC_VERSION_2_0</constant></entry>
+	    <entry>6</entry>
+	    <entry>CEC version according to the HDMI 2.0 standard.</entry>
+	  </row>
+	</tbody>
+      </tgroup>
+    </table>
+
+    <table pgwide="1" frame="none" id="cec-prim-dev-types">
+      <title>CEC Primary Device Types</title>
+      <tgroup cols="3">
+	&cs-def;
+	<tbody valign="top">
+	  <row>
+	    <entry><constant>CEC_OP_PRIM_DEVTYPE_TV</constant></entry>
+	    <entry>0</entry>
+	    <entry>Use for a TV.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_OP_PRIM_DEVTYPE_RECORD</constant></entry>
+	    <entry>1</entry>
+	    <entry>Use for a recording device.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_OP_PRIM_DEVTYPE_TUNER</constant></entry>
+	    <entry>3</entry>
+	    <entry>Use for a device with a tuner.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_OP_PRIM_DEVTYPE_PLAYBACK</constant></entry>
+	    <entry>4</entry>
+	    <entry>Use for a playback device.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM</constant></entry>
+	    <entry>5</entry>
+	    <entry>Use for an audio system (e.g. an audio/video receiver).</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_OP_PRIM_DEVTYPE_SWITCH</constant></entry>
+	    <entry>6</entry>
+	    <entry>Use for a CEC switch.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_OP_PRIM_DEVTYPE_VIDEOPROC</constant></entry>
+	    <entry>7</entry>
+	    <entry>Use for a video processor device.</entry>
+	  </row>
+	</tbody>
+      </tgroup>
+    </table>
+
+    <table pgwide="1" frame="none" id="cec-log-addr-types">
+      <title>CEC Logical Address Types</title>
+      <tgroup cols="3">
+	&cs-def;
+	<tbody valign="top">
+	  <row>
+	    <entry><constant>CEC_LOG_ADDR_TYPE_TV</constant></entry>
+	    <entry>0</entry>
+	    <entry>Use for a TV.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_LOG_ADDR_TYPE_RECORD</constant></entry>
+	    <entry>1</entry>
+	    <entry>Use for a recording device.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_LOG_ADDR_TYPE_TUNER</constant></entry>
+	    <entry>2</entry>
+	    <entry>Use for a tuner device.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_LOG_ADDR_TYPE_PLAYBACK</constant></entry>
+	    <entry>3</entry>
+	    <entry>Use for a playback device.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_LOG_ADDR_TYPE_AUDIOSYSTEM</constant></entry>
+	    <entry>4</entry>
+	    <entry>Use for an audio system device.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_LOG_ADDR_TYPE_SPECIFIC</constant></entry>
+	    <entry>5</entry>
+	    <entry>Use for a second TV or for a video processor device.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_LOG_ADDR_TYPE_UNREGISTERED</constant></entry>
+	    <entry>6</entry>
+	    <entry>Use this if you just want to remain unregistered.
+	    Used for pure CEC switches or CDC-only devices (CDC:
+	    Capability Discovery and Control).</entry>
+	  </row>
+	</tbody>
+      </tgroup>
+    </table>
+
+    <table pgwide="1" frame="none" id="cec-all-dev-types-flags">
+      <title>CEC All Device Types Flags</title>
+      <tgroup cols="3">
+	&cs-def;
+	<tbody valign="top">
+	  <row>
+	    <entry><constant>CEC_OP_ALL_DEVTYPE_TV</constant></entry>
+	    <entry>0x80</entry>
+	    <entry>This supports the TV type.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_OP_ALL_DEVTYPE_RECORD</constant></entry>
+	    <entry>0x40</entry>
+	    <entry>This supports the Recording type.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_OP_ALL_DEVTYPE_TUNER</constant></entry>
+	    <entry>0x20</entry>
+	    <entry>This supports the Tuner type.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_OP_ALL_DEVTYPE_PLAYBACK</constant></entry>
+	    <entry>0x10</entry>
+	    <entry>This supports the Playback type.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM</constant></entry>
+	    <entry>0x08</entry>
+	    <entry>This supports the Audio System type.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_OP_ALL_DEVTYPE_SWITCH</constant></entry>
+	    <entry>0x04</entry>
+	    <entry>This supports the CEC Switch or Video Processing type.</entry>
+	  </row>
+	</tbody>
+      </tgroup>
+    </table>
+  </refsect1>
+
+  <refsect1>
+    &return-value;
+  </refsect1>
+</refentry>
diff --git a/Documentation/DocBook/media/v4l/cec-ioc-adap-g-phys-addr.xml b/Documentation/DocBook/media/v4l/cec-ioc-adap-g-phys-addr.xml
new file mode 100644
index 0000000..7a08269
--- /dev/null
+++ b/Documentation/DocBook/media/v4l/cec-ioc-adap-g-phys-addr.xml
@@ -0,0 +1,82 @@
+<refentry id="cec-ioc-adap-g-phys-addr">
+  <refmeta>
+    <refentrytitle>ioctl CEC_ADAP_G_PHYS_ADDR, CEC_ADAP_S_PHYS_ADDR</refentrytitle>
+    &manvol;
+  </refmeta>
+
+  <refnamediv>
+    <refname>CEC_ADAP_G_PHYS_ADDR</refname>
+    <refname>CEC_ADAP_S_PHYS_ADDR</refname>
+    <refpurpose>Get or set the physical address</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcprototype>
+	<funcdef>int <function>ioctl</function></funcdef>
+	<paramdef>int <parameter>fd</parameter></paramdef>
+	<paramdef>int <parameter>request</parameter></paramdef>
+	<paramdef>__u16 *<parameter>argp</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Arguments</title>
+
+    <variablelist>
+      <varlistentry>
+	<term><parameter>fd</parameter></term>
+	<listitem>
+	  <para>File descriptor returned by
+	  <link linkend='cec-func-open'><function>open()</function></link>.</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><parameter>request</parameter></term>
+	<listitem>
+	  <para>CEC_ADAP_G_PHYS_ADDR, CEC_ADAP_S_PHYS_ADDR</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><parameter>argp</parameter></term>
+	<listitem>
+	  <para></para>
+	</listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>To query the current physical address applications call the
+<constant>CEC_ADAP_G_PHYS_ADDR</constant> ioctl with a pointer to an __u16
+where the driver stores the physical address.</para>
+
+    <para>To set a new physical address applications store the physical address in
+an __u16 and call the <constant>CEC_ADAP_S_PHYS_ADDR</constant> ioctl with a
+pointer to this integer. <constant>CEC_ADAP_S_PHYS_ADDR</constant> is only
+available if <constant>CEC_CAP_PHYS_ADDR</constant> is set. It is not allowed
+to change a valid physical address to another valid physical address: you must
+select <constant>CEC_PHYS_ADDR_INVALID</constant> (f.f.f.f) first.
+<constant>CEC_ADAP_S_PHYS_ADDR</constant>
+can only be called by a file handle in initiator mode (see &CEC-S-MODE;).</para>
+
+    <para>The physical address is a 16-bit number where each group of 4 bits
+represent a digit of the physical address a.b.c.d where the most significant
+4 bits represent 'a'. The CEC root device (usually the TV) has address 0.0.0.0.
+Every device that is hooked up to an input of the TV has address a.0.0.0 (where
+'a' is &ge; 1), devices hooked up to those in turn have addresses a.b.0.0, etc.
+So a topology of up to 5 devices deep is supported. The physical address a
+device shall use is stored in the EDID of the sink.</para>
+
+<para>For example, the EDID for each HDMI input of the TV will have a different
+physical address of the form a.0.0.0 that the sources will read out and use as
+their physical address.</para>
+  </refsect1>
+
+  <refsect1>
+    &return-value;
+  </refsect1>
+</refentry>
diff --git a/Documentation/DocBook/media/v4l/cec-ioc-dqevent.xml b/Documentation/DocBook/media/v4l/cec-ioc-dqevent.xml
new file mode 100644
index 0000000..6a9960f
--- /dev/null
+++ b/Documentation/DocBook/media/v4l/cec-ioc-dqevent.xml
@@ -0,0 +1,190 @@
+<refentry id="cec-ioc-g-event">
+  <refmeta>
+    <refentrytitle>ioctl CEC_DQEVENT</refentrytitle>
+    &manvol;
+  </refmeta>
+
+  <refnamediv>
+    <refname>CEC_DQEVENT</refname>
+    <refpurpose>Dequeue a CEC event</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcprototype>
+	<funcdef>int <function>ioctl</function></funcdef>
+	<paramdef>int <parameter>fd</parameter></paramdef>
+	<paramdef>int <parameter>request</parameter></paramdef>
+	<paramdef>struct cec_event *<parameter>argp</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Arguments</title>
+
+    <variablelist>
+      <varlistentry>
+	<term><parameter>fd</parameter></term>
+	<listitem>
+	  <para>File descriptor returned by
+	  <link linkend='cec-func-open'><function>open()</function></link>.</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><parameter>request</parameter></term>
+	<listitem>
+	  <para>CEC_DQEVENT</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><parameter>argp</parameter></term>
+	<listitem>
+	  <para></para>
+	</listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>CEC devices can send asynchronous events. These can be retrieved by calling
+    the <constant>CEC_DQEVENT</constant> ioctl. If the file descriptor is in non-blocking
+    mode and no event is pending, then it will return -1 and set errno to the &EAGAIN;.</para>
+
+    <para>The internal event queues are per-filehandle and per-event type. If there is
+    no more room in a queue then the last event is overwritten with the new one. This
+    means that intermediate results can be thrown away but that the latest event is always
+    available. This also mean that is it possible to read two successive events that have
+    the same value (e.g. two CEC_EVENT_STATE_CHANGE events with the same state). In that
+    case the intermediate state changes were lost but it is guaranteed that the state
+    did change in between the two events.</para>
+
+    <table pgwide="1" frame="none" id="cec-event-state-change">
+      <title>struct <structname>cec_event_state_change</structname></title>
+      <tgroup cols="3">
+	&cs-str;
+	<tbody valign="top">
+	  <row>
+	    <entry>__u16</entry>
+	    <entry><structfield>phys_addr</structfield></entry>
+	    <entry>The current physical address.</entry>
+	  </row>
+	  <row>
+	    <entry>__u16</entry>
+	    <entry><structfield>log_addr_mask</structfield></entry>
+	    <entry>The current set of claimed logical addresses.</entry>
+	  </row>
+	</tbody>
+      </tgroup>
+    </table>
+
+    <table pgwide="1" frame="none" id="cec-event-lost-msgs">
+      <title>struct <structname>cec_event_lost_msgs</structname></title>
+      <tgroup cols="3">
+	&cs-str;
+	<tbody valign="top">
+	  <row>
+	    <entry>__u32</entry>
+	    <entry><structfield>lost_msgs</structfield></entry>
+	    <entry>Set to the number of lost messages since the filehandle
+	    was opened or since the last time this event was dequeued for
+	    this filehandle.</entry>
+	  </row>
+	</tbody>
+      </tgroup>
+    </table>
+
+    <table pgwide="1" frame="none" id="cec-event">
+      <title>struct <structname>cec_event</structname></title>
+      <tgroup cols="4">
+	&cs-str;
+	<tbody valign="top">
+	  <row>
+	    <entry>__u64</entry>
+	    <entry><structfield>ts</structfield></entry>
+	    <entry>Timestamp of the event in ns.</entry>
+	    <entry></entry>
+	  </row>
+	  <row>
+	    <entry>__u32</entry>
+	    <entry><structfield>event</structfield></entry>
+	    <entry>The event, see <xref linkend="cec-events" />.</entry>
+	    <entry></entry>
+	  </row>
+	  <row>
+	    <entry>__u32</entry>
+	    <entry><structfield>flags</structfield></entry>
+	    <entry>Event flags, see <xref linkend="cec-event-flags" />.</entry>
+	    <entry></entry>
+	  </row>
+	  <row>
+	    <entry>union</entry>
+	    <entry>(anonymous)</entry>
+	    <entry></entry>
+	    <entry></entry>
+	  </row>
+	  <row>
+	    <entry></entry>
+	    <entry>struct cec_event_state_change</entry>
+	    <entry><structfield>state_change</structfield></entry>
+	    <entry>The new adapter state as sent by the <constant>CEC_EVENT_STATE_CHANGE</constant>
+	    event.</entry>
+	  </row>
+	  <row>
+	    <entry></entry>
+	    <entry>struct cec_event_lost_msgs</entry>
+	    <entry><structfield>lost_msgs</structfield></entry>
+	    <entry>The number of lost messages as sent by the <constant>CEC_EVENT_LOST_MSGS</constant>
+	    event.</entry>
+	  </row>
+	</tbody>
+      </tgroup>
+    </table>
+
+    <table pgwide="1" frame="none" id="cec-events">
+      <title>CEC Events</title>
+      <tgroup cols="3">
+	&cs-def;
+	<tbody valign="top">
+	  <row>
+	    <entry><constant>CEC_EVENT_STATE_CHANGE</constant></entry>
+	    <entry>1</entry>
+	    <entry>Generated when the CEC Adapter's state changes. When open() is
+	    called an initial event will be generated for that filehandle with the
+	    CEC Adapter's state at that time.
+	    </entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_EVENT_LOST_MSGS</constant></entry>
+	    <entry>2</entry>
+	    <entry>Generated if one or more CEC messages were lost because the
+	    application didn't dequeue CEC messages fast enough.</entry>
+	  </row>
+	</tbody>
+      </tgroup>
+    </table>
+
+    <table pgwide="1" frame="none" id="cec-event-flags">
+      <title>CEC Event Flags</title>
+      <tgroup cols="3">
+	&cs-def;
+	<tbody valign="top">
+	  <row>
+	    <entry><constant>CEC_EVENT_FL_INITIAL_VALUE</constant></entry>
+	    <entry>1</entry>
+	    <entry>Set for the initial events that are generated when the device is
+	    opened. See the table above for which events do this. This allows
+	    applications to learn the initial state of the CEC adapter at open()
+	    time.</entry>
+	  </row>
+	</tbody>
+      </tgroup>
+    </table>
+  </refsect1>
+
+  <refsect1>
+    &return-value;
+  </refsect1>
+</refentry>
diff --git a/Documentation/DocBook/media/v4l/cec-ioc-g-mode.xml b/Documentation/DocBook/media/v4l/cec-ioc-g-mode.xml
new file mode 100644
index 0000000..0cb1941
--- /dev/null
+++ b/Documentation/DocBook/media/v4l/cec-ioc-g-mode.xml
@@ -0,0 +1,245 @@
+<refentry id="cec-ioc-g-mode">
+  <refmeta>
+    <refentrytitle>ioctl CEC_G_MODE, CEC_S_MODE</refentrytitle>
+    &manvol;
+  </refmeta>
+
+  <refnamediv>
+    <refname>CEC_G_MODE</refname>
+    <refname>CEC_S_MODE</refname>
+    <refpurpose>Get or set exclusive use of the CEC adapter</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcprototype>
+	<funcdef>int <function>ioctl</function></funcdef>
+	<paramdef>int <parameter>fd</parameter></paramdef>
+	<paramdef>int <parameter>request</parameter></paramdef>
+	<paramdef>__u32 *<parameter>argp</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Arguments</title>
+
+    <variablelist>
+      <varlistentry>
+	<term><parameter>fd</parameter></term>
+	<listitem>
+	  <para>File descriptor returned by
+	  <link linkend='cec-func-open'><function>open()</function></link>.</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><parameter>request</parameter></term>
+	<listitem>
+	  <para>CEC_G_MODE, CEC_S_MODE</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><parameter>argp</parameter></term>
+	<listitem>
+	  <para></para>
+	</listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>By default any filehandle can use &CEC-TRANSMIT; and &CEC-RECEIVE;, but
+in order to prevent applications from stepping on each others toes you want to get
+exclusive access to the CEC adapter. This ioctl allows you to try and become the
+exclusive initiator and/or follower. The initiator is the filehandle that is used
+to initiate messages, i.e. it commands other CEC devices. The follower is the filehandle
+that receives messages sent to our CEC adapter and processes them. The same filehandle
+can be both initiator and follower, or this role can be taken by two different
+filehandles.</para>
+
+    <para>When a CEC message is received, then the CEC framework will decide how
+it will be processed. If the message is a reply to an earlier transmitted message,
+then the reply is sent back to the filehandle that is waiting for it. In addition
+the CEC framework will process it.</para>
+
+    <para>If the message is not a reply, then the CEC framework will process it
+first. If there is no follower, then the message is just discarded and a feature
+abort is sent back to the initiator if the framework couldn't process it. If there
+is a follower, then the message is passed on to the follower who will use
+&CEC-RECEIVE; to dequeue the new message. The framework expects the follower to
+make the right decisions.</para>
+
+    <para>The CEC framework will process core messages unless requested otherwise
+by the follower. The follower can enable the passthrough mode. In that case the
+CEC framework will pass on most core messages without processing them. In that
+case the follower will have to implement those messages. There are some messages
+that the core will always process, regardless of the passthrough mode.</para>
+
+    <para>If there is no initiator, then any CEC filehandle can use &CEC-TRANSMIT;.
+If there is an exclusive initiator then only that initiator can call &CEC-TRANSMIT;.
+The follower can of course always call &CEC-TRANSMIT;.</para>
+
+    <para>Available initiator modes are:</para>
+
+    <table pgwide="1" frame="none" id="cec-mode-initiator">
+      <title>Initiator Modes</title>
+      <tgroup cols="3">
+	&cs-def;
+	<tbody valign="top">
+	  <row>
+	    <entry><constant>CEC_MODE_NO_INITIATOR</constant></entry>
+	    <entry>0x0</entry>
+	    <entry>This is not an initiator, i.e. it cannot transmit CEC messages
+	    or make any other changes to the CEC adapter.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_MODE_INITIATOR</constant></entry>
+	    <entry>0x1</entry>
+	    <entry>This is an initiator (the default when the device is opened) and it
+	    can transmit CEC messages and make changes to the CEC adapter, unless there
+	    is an exclusive initiator.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_MODE_EXCL_INITIATOR</constant></entry>
+	    <entry>0x2</entry>
+	    <entry>This is an exclusive initiator and this file descriptor is the only one
+	    that can transmit CEC messages and make changes to the CEC adapter. If someone
+	    else is already the exclusive initiator then an attempt to become one will return
+	    the &EBUSY; error.</entry>
+	  </row>
+	</tbody>
+      </tgroup>
+    </table>
+
+    <para>Available follower modes are:</para>
+
+    <table pgwide="1" frame="none" id="cec-mode-follower">
+      <title>Follower Modes</title>
+      <tgroup cols="3">
+	&cs-def;
+	<tbody valign="top">
+	  <row>
+	    <entry><constant>CEC_MODE_NO_FOLLOWER</constant></entry>
+	    <entry>0x00</entry>
+	    <entry>This is not a follower (the default when the device is opened).</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_MODE_FOLLOWER</constant></entry>
+	    <entry>0x10</entry>
+	    <entry>This is a follower and it will receive CEC messages unless there is
+	    an exclusive follower. You cannot become a follower if <constant>CEC_CAP_TRANSMIT</constant>
+	    is not set or if <constant>CEC_MODE_NO_INITIATOR</constant> was specified,
+	    &EINVAL; is returned in that case.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_MODE_EXCL_FOLLOWER</constant></entry>
+	    <entry>0x20</entry>
+	    <entry>This is an exclusive follower and only this file descriptor will receive
+	    CEC messages for processing. If someone else is already the exclusive follower
+	    then an attempt to become one will return the &EBUSY; error. You cannot become
+	    a follower if <constant>CEC_CAP_TRANSMIT</constant> is not set or if
+	    <constant>CEC_MODE_NO_INITIATOR</constant> was specified, &EINVAL; is returned
+	    in that case.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_MODE_EXCL_FOLLOWER_PASSTHRU</constant></entry>
+	    <entry>0x30</entry>
+	    <entry>This is an exclusive follower and only this file descriptor will receive
+	    CEC messages for processing. In addition it will put the CEC device into
+	    passthrough mode, allowing the exclusive follower to handle most core messages
+	    instead of relying on the CEC framework for that. If someone else is already the
+	    exclusive follower then an attempt to become one will return the &EBUSY; error.
+	    You cannot become a follower if <constant>CEC_CAP_TRANSMIT</constant>
+            is not set or if <constant>CEC_MODE_NO_INITIATOR</constant> was specified,
+            &EINVAL; is returned in that case.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_MODE_MONITOR</constant></entry>
+	    <entry>0xe0</entry>
+	    <entry>Put the file descriptor into monitor mode. Can only be used in combination
+	    with <constant>CEC_MODE_NO_INITIATOR</constant>, otherwise &EINVAL; will be
+	    returned. In monitor mode all messages this CEC device transmits and all messages
+	    it receives (both broadcast messages and directed messages for one its logical
+	    addresses) will be reported. This is very useful for debugging.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_MODE_MONITOR_ALL</constant></entry>
+	    <entry>0xf0</entry>
+	    <entry>Put the file descriptor into 'monitor all' mode. Can only be used in combination
+            with <constant>CEC_MODE_NO_INITIATOR</constant>, otherwise &EINVAL; will be
+            returned. In 'monitor all' mode all messages this CEC device transmits and all messages
+            it receives, including directed messages for other CEC devices will be reported. This
+	    is very useful for debugging, but not all devices support this. This mode requires that
+	    the <constant>CEC_CAP_MONITOR_ALL</constant> capability is set, and depending on the
+	    hardware, you may have to be root to select this mode.</entry>
+	  </row>
+	</tbody>
+      </tgroup>
+    </table>
+
+    <para>Core message processing details:</para>
+
+    <table pgwide="1" frame="none" id="cec-core-processing">
+      <title>Core Message Processing</title>
+      <tgroup cols="2">
+	&cs-def;
+	<tbody valign="top">
+	  <row>
+	    <entry><constant>CEC_MSG_GET_CEC_VERSION</constant></entry>
+	    <entry>When in passthrough mode this message has to be handled by userspace,
+	    otherwise the core will return the CEC version that was set with &CEC-ADAP-S-LOG-ADDRS;.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_MSG_GIVE_DEVICE_VENDOR_ID</constant></entry>
+	    <entry>When in passthrough mode this message has to be handled by userspace,
+	    otherwise the core will return the vendor ID that was set with &CEC-ADAP-S-LOG-ADDRS;.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_MSG_ABORT</constant></entry>
+	    <entry>When in passthrough mode this message has to be handled by userspace,
+	    otherwise the core will return a feature refused message as per the specification.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_MSG_GIVE_PHYSICAL_ADDR</constant></entry>
+	    <entry>When in passthrough mode this message has to be handled by userspace,
+	    otherwise the core will report the current physical address.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_MSG_GIVE_OSD_NAME</constant></entry>
+	    <entry>When in passthrough mode this message has to be handled by userspace,
+	    otherwise the core will report the current OSD name as was set with
+	    &CEC-ADAP-S-LOG-ADDRS;.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_MSG_GIVE_FEATURES</constant></entry>
+	    <entry>When in passthrough mode this message has to be handled by userspace,
+	    otherwise the core will report the current features as was set with
+	    &CEC-ADAP-S-LOG-ADDRS; or the message is ignore if the CEC version was
+	    older than 2.0.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_MSG_USER_CONTROL_PRESSED</constant></entry>
+	    <entry>If <constant>CEC_CAP_RC</constant> is set, then generate a remote control
+	    key press. This message is always passed on to userspace.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_MSG_USER_CONTROL_RELEASED</constant></entry>
+	    <entry>If <constant>CEC_CAP_RC</constant> is set, then generate a remote control
+	    key release. This message is always passed on to userspace.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_MSG_REPORT_PHYSICAL_ADDR</constant></entry>
+	    <entry>The CEC framework will make note of the reported physical address
+	    and then just pass the message on to userspace.</entry>
+	  </row>
+	</tbody>
+      </tgroup>
+    </table>
+  </refsect1>
+
+  <refsect1>
+    &return-value;
+  </refsect1>
+</refentry>
diff --git a/Documentation/DocBook/media/v4l/cec-ioc-receive.xml b/Documentation/DocBook/media/v4l/cec-ioc-receive.xml
new file mode 100644
index 0000000..66bf82f
--- /dev/null
+++ b/Documentation/DocBook/media/v4l/cec-ioc-receive.xml
@@ -0,0 +1,260 @@
+<refentry id="cec-ioc-receive">
+  <refmeta>
+    <refentrytitle>ioctl CEC_RECEIVE, CEC_TRANSMIT</refentrytitle>
+    &manvol;
+  </refmeta>
+
+  <refnamediv>
+    <refname>CEC_RECEIVE</refname>
+    <refname>CEC_TRANSMIT</refname>
+    <refpurpose>Receive or transmit a CEC message</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcprototype>
+	<funcdef>int <function>ioctl</function></funcdef>
+	<paramdef>int <parameter>fd</parameter></paramdef>
+	<paramdef>int <parameter>request</parameter></paramdef>
+	<paramdef>struct cec_msg *<parameter>argp</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Arguments</title>
+
+    <variablelist>
+      <varlistentry>
+	<term><parameter>fd</parameter></term>
+	<listitem>
+	  <para>File descriptor returned by
+	  <link linkend='cec-func-open'><function>open()</function></link>.</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><parameter>request</parameter></term>
+	<listitem>
+	  <para>CEC_RECEIVE, CEC_TRANSMIT</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><parameter>argp</parameter></term>
+	<listitem>
+	  <para></para>
+	</listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>To receive a CEC message the application has to fill in the
+    <structname>cec_msg</structname> structure and pass it to the
+    <constant>CEC_RECEIVE</constant> ioctl. <constant>CEC_RECEIVE</constant> is
+    only available if <constant>CEC_CAP_RECEIVE</constant> is set. If the
+    file descriptor is in non-blocking mode and there are no received
+    messages pending, then it will return -1 and set errno to the &EAGAIN;.
+    If the file descriptor is in blocking mode and <structfield>timeout</structfield>
+    is non-zero and no message arrived within <structfield>timeout</structfield>
+    milliseconds, then it will return -1 and set errno to the &ETIMEDOUT;.</para>
+
+    <para>To send a CEC message the application has to fill in the
+    <structname>cec_msg</structname> structure and pass it to the
+    <constant>CEC_TRANSMIT</constant> ioctl. <constant>CEC_TRANSMIT</constant> is
+    only available if <constant>CEC_CAP_TRANSMIT</constant> is set.
+    If there is no more room in the transmit queue, then it will return
+    -1 and set errno to the &EBUSY;.</para>
+
+    <table pgwide="1" frame="none" id="cec-msg">
+      <title>struct <structname>cec_msg</structname></title>
+      <tgroup cols="3">
+	&cs-str;
+	<tbody valign="top">
+	  <row>
+	    <entry>__u64</entry>
+	    <entry><structfield>ts</structfield></entry>
+	    <entry>Timestamp of when the message was transmitted in ns in the case
+	    of <constant>CEC_TRANSMIT</constant> with <structfield>reply</structfield>
+	    set to 0, or the timestamp of the received message in all other cases.</entry>
+	  </row>
+	  <row>
+	    <entry>__u32</entry>
+	    <entry><structfield>len</structfield></entry>
+	    <entry>The length of the message. For <constant>CEC_TRANSMIT</constant> this
+	    is filled in by the application. The driver will fill this in for
+	    <constant>CEC_RECEIVE</constant> and for <constant>CEC_TRANSMIT</constant>
+	    it will be filled in with the length of the reply message if
+	    <structfield>reply</structfield> was set.</entry>
+	  </row>
+	  <row>
+	    <entry>__u32</entry>
+	    <entry><structfield>timeout</structfield></entry>
+	    <entry>The timeout in milliseconds. This is the time we wait for a message to
+	    be received. If it is set to 0, then we wait indefinitely.
+	    It is ignored by <constant>CEC_TRANSMIT</constant>.</entry>
+	  </row>
+	  <row>
+	    <entry>__u32</entry>
+	    <entry><structfield>sequence</structfield></entry>
+	    <entry>The sequence number is automatically assigned by the CEC
+	    framework for all transmitted messages. It can be later used by the
+	    framework to generate an event if a reply for a message was
+	    requested and the message was transmitted in a non-blocking mode.
+	    </entry>
+	  </row>
+	  <row>
+	    <entry>__u32</entry>
+	    <entry><structfield>flags</structfield></entry>
+	    <entry>Flags. No flags are defined yet, so set this to 0.</entry>
+	  </row>
+	  <row>
+	    <entry>__u8</entry>
+	    <entry><structfield>rx_status</structfield></entry>
+	    <entry>The status bits of the received message. See <xref linkend="cec-rx-status" />
+	    for the possible status values. It is 0 if this message was transmitted, not
+	    received, unless this is the reply to a transmitted message. In that case both
+	    <structfield>rx_status</structfield> and <structfield>tx_status</structfield>
+	    are set.</entry>
+	  </row>
+	  <row>
+	    <entry>__u8</entry>
+	    <entry><structfield>tx_status</structfield></entry>
+	    <entry>The status bits of the transmitted message. See <xref linkend="cec-tx-status" />
+	    for the possible status values. It is 0 if this messages was received, not
+	    transmitted.</entry>
+	  </row>
+	  <row>
+	    <entry>__u8</entry>
+	    <entry><structfield>msg</structfield>[16]</entry>
+	    <entry>The message payload. For <constant>CEC_TRANSMIT</constant> this
+	    is filled in by the application. The driver will fill this in for
+	    <constant>CEC_RECEIVE</constant> and for <constant>CEC_TRANSMIT</constant>
+	    it will be filled in with the payload of the reply message if
+	    <structfield>reply</structfield> was set.</entry>
+	  </row>
+	  <row>
+	    <entry>__u8</entry>
+	    <entry><structfield>reply</structfield></entry>
+	    <entry>Wait until this message is replied. If <structfield>reply</structfield>
+	    is 0, then don't wait for a reply but return after transmitting the
+	    message. If there was an error as indicated by a non-zero <structfield>status</structfield>
+	    field, then <structfield>reply</structfield> is set to 0 by the driver.
+	    Ignored by <constant>CEC_RECEIVE</constant>.</entry>
+	  </row>
+	  <row>
+	    <entry>__u8</entry>
+	    <entry><structfield>tx_arb_lost_cnt</structfield></entry>
+	    <entry>A counter of the number of transmit attempts that resulted in the
+	    Arbitration Lost error. This is only set if the hardware supports this, otherwise
+	    it is always 0. This counter is only valid if the <constant>CEC_TX_STATUS_ARB_LOST</constant>
+	    status bit is set.</entry>
+	  </row>
+	  <row>
+	    <entry>__u8</entry>
+	    <entry><structfield>tx_nack_cnt</structfield></entry>
+	    <entry>A counter of the number of transmit attempts that resulted in the
+	    Not Acknowledged error. This is only set if the hardware supports this, otherwise
+	    it is always 0. This counter is only valid if the <constant>CEC_TX_STATUS_NACK</constant>
+            status bit is set.</entry>
+	  </row>
+	  <row>
+	    <entry>__u8</entry>
+	    <entry><structfield>tx_low_drive_cnt</structfield></entry>
+	    <entry>A counter of the number of transmit attempts that resulted in the
+	    Arbitration Lost error. This is only set if the hardware supports this, otherwise
+	    it is always 0. This counter is only valid if the <constant>CEC_TX_STATUS_LOW_DRIVE</constant>
+            status bit is set.</entry>
+	  </row>
+	  <row>
+	    <entry>__u8</entry>
+	    <entry><structfield>tx_error_cnt</structfield></entry>
+	    <entry>A counter of the number of transmit errors other than Arbitration Lost
+	    or Not Acknowledged. This is only set if the hardware supports this, otherwise
+	    it is always 0. This counter is only valid if the <constant>CEC_TX_STATUS_ERROR</constant>
+	    status bit is set.</entry>
+	  </row>
+	</tbody>
+      </tgroup>
+    </table>
+
+    <table pgwide="1" frame="none" id="cec-tx-status">
+      <title>CEC Transmit Status</title>
+      <tgroup cols="3">
+	&cs-def;
+	<tbody valign="top">
+	  <row>
+	    <entry><constant>CEC_TX_STATUS_OK</constant></entry>
+	    <entry>0x01</entry>
+	    <entry>The message was transmitted successfully. This is mutually exclusive with
+	    <constant>CEC_TX_STATUS_MAX_RETRIES</constant>. Other bits can still be set if
+	    earlier attempts met with failure before the transmit was eventually successful.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_TX_STATUS_ARB_LOST</constant></entry>
+	    <entry>0x02</entry>
+	    <entry>CEC line arbitration was lost.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_TX_STATUS_NACK</constant></entry>
+	    <entry>0x04</entry>
+	    <entry>Message was not acknowledged.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_TX_STATUS_LOW_DRIVE</constant></entry>
+	    <entry>0x08</entry>
+	    <entry>Low drive was detected on the CEC bus. This indicates that a follower
+	    detected an error on the bus and requests a retransmission.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_TX_STATUS_ERROR</constant></entry>
+	    <entry>0x10</entry>
+	    <entry>Some error occurred. This is used for any errors that do not
+	    fit the previous two, either because the hardware could not tell
+	    which error occurred, or because the hardware tested for other conditions
+	    besides those two.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_TX_STATUS_MAX_RETRIES</constant></entry>
+	    <entry>0x20</entry>
+	    <entry>The transmit failed after one or more retries. This status bit is mutually
+	    exclusive with <constant>CEC_TX_STATUS_OK</constant>. Other bits can still be set
+	    to explain which failures were seen.</entry>
+	  </row>
+	</tbody>
+      </tgroup>
+    </table>
+
+    <table pgwide="1" frame="none" id="cec-rx-status">
+      <title>CEC Receive Status</title>
+      <tgroup cols="3">
+	&cs-def;
+	<tbody valign="top">
+	  <row>
+	    <entry><constant>CEC_RX_STATUS_OK</constant></entry>
+	    <entry>0x01</entry>
+	    <entry>The message was received successfully.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_RX_STATUS_TIMEOUT</constant></entry>
+	    <entry>0x02</entry>
+	    <entry>The reply to an earlier transmitted message timed out.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>CEC_RX_STATUS_FEATURE_ABORT</constant></entry>
+	    <entry>0x04</entry>
+	    <entry>The message was received successfully but the reply was
+	    <constant>CEC_MSG_FEATURE_ABORT</constant>. This status is only
+	    set if this message was the reply to an earlier transmitted
+	    message.</entry>
+	  </row>
+	</tbody>
+      </tgroup>
+    </table>
+  </refsect1>
+
+  <refsect1>
+    &return-value;
+  </refsect1>
+</refentry>
diff --git a/Documentation/DocBook/media_api.tmpl b/Documentation/DocBook/media_api.tmpl
index 7b77e0f..a2765d8 100644
--- a/Documentation/DocBook/media_api.tmpl
+++ b/Documentation/DocBook/media_api.tmpl
@@ -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;
-- 
2.8.1


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

* [PATCHv16 09/13] cec: adv7604: add cec support.
  2016-04-29 13:52 [PATCHv16 00/13] HDMI CEC framework Hans Verkuil
                   ` (7 preceding siblings ...)
  2016-04-29 13:52 ` [PATCHv16 08/13] DocBook/media: add CEC documentation Hans Verkuil
@ 2016-04-29 13:52 ` Hans Verkuil
  2016-06-16 21:17   ` Mauro Carvalho Chehab
  2016-04-29 13:52 ` [PATCHv16 10/13] cec: adv7842: " Hans Verkuil
                   ` (3 subsequent siblings)
  12 siblings, 1 reply; 35+ messages in thread
From: Hans Verkuil @ 2016-04-29 13:52 UTC (permalink / raw)
  To: linux-media
  Cc: dri-devel, linux-samsung-soc, linux-input, lars, linux, Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

Add CEC support to the adv7604 driver.

Signed-off-by: Hans Verkuil <hansverk@cisco.com>
[k.debski@samsung.com: Merged changes from CEC Updates commit by Hans Verkuil]
[k.debski@samsung.com: add missing methods cec/io_write_and_or]
[k.debski@samsung.com: change adv7604 to adv76xx in added functions]
[hansverk@cisco.com: use _clr_set instead of _and_or]
---
 drivers/media/i2c/Kconfig   |   9 ++
 drivers/media/i2c/adv7604.c | 332 +++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 305 insertions(+), 36 deletions(-)

diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 993dc50..cba1fc7 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -209,6 +209,7 @@ config VIDEO_ADV7604
 	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
 	depends on GPIOLIB || COMPILE_TEST
 	select HDMI
+	select MEDIA_CEC_EDID
 	---help---
 	  Support for the Analog Devices ADV7604 video decoder.
 
@@ -218,6 +219,14 @@ config VIDEO_ADV7604
 	  To compile this driver as a module, choose M here: the
 	  module will be called adv7604.
 
+config VIDEO_ADV7604_CEC
+	bool "Enable Analog Devices ADV7604 CEC support"
+	depends on VIDEO_ADV7604 && MEDIA_CEC
+	default y
+	---help---
+	  When selected the adv7604 will support the optional
+	  HDMI CEC feature.
+
 config VIDEO_ADV7842
 	tristate "Analog Devices ADV7842 decoder"
 	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
index beb2841..f462585 100644
--- a/drivers/media/i2c/adv7604.c
+++ b/drivers/media/i2c/adv7604.c
@@ -40,6 +40,7 @@
 #include <linux/regmap.h>
 
 #include <media/i2c/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,12 @@ struct adv76xx_state {
 	u16 spa_port_a[2];
 	struct v4l2_fract aspect_ratio;
 	u32 rgb_quantization_range;
+
+	struct cec_adapter *cec_adap;
+	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;
@@ -381,7 +390,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);
 }
@@ -414,6 +424,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);
@@ -872,9 +888,9 @@ 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);
 
-	return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl,
-				info->read_cable_det(sd));
+	return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, cable_det);
 }
 
 static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd,
@@ -1900,6 +1916,210 @@ static int adv76xx_set_format(struct v4l2_subdev *sd,
 	return 0;
 }
 
+#if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC)
+static void adv76xx_cec_tx_raw_status(struct v4l2_subdev *sd, u8 tx_raw_status)
+{
+	struct adv76xx_state *state = to_state(sd);
+
+	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__);
+		cec_transmit_done(state->cec_adap, CEC_TX_STATUS_ARB_LOST,
+				  1, 0, 0, 0);
+	}
+	if (tx_raw_status & 0x04) {
+		u8 status;
+		u8 nack_cnt;
+		u8 low_drive_cnt;
+
+		v4l2_dbg(1, debug, sd, "%s: tx raw: retry failed\n", __func__);
+		/*
+		 * We set this status bit since this hardware performs
+		 * retransmissions.
+		 */
+		status = CEC_TX_STATUS_MAX_RETRIES;
+		nack_cnt = cec_read(sd, 0x14) & 0xf;
+		if (nack_cnt)
+			status |= CEC_TX_STATUS_NACK;
+		low_drive_cnt = cec_read(sd, 0x14) >> 4;
+		if (low_drive_cnt)
+			status |= CEC_TX_STATUS_LOW_DRIVE;
+		cec_transmit_done(state->cec_adap, status,
+				  0, nack_cnt, low_drive_cnt, 0);
+		return;
+	}
+	if (tx_raw_status & 0x01) {
+		v4l2_dbg(1, debug, sd, "%s: tx raw: ready ok\n", __func__);
+		cec_transmit_done(state->cec_adap, CEC_TX_STATUS_OK, 0, 0, 0, 0);
+		return;
+	}
+}
+
+static void adv76xx_cec_isr(struct v4l2_subdev *sd, bool *handled)
+{
+	struct adv76xx_state *state = to_state(sd);
+	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 */
+			cec_received_msg(state->cec_adap, &msg);
+		}
+	}
+
+	/* note: the bit order is swapped between 0x4d and 0x4e */
+	cec_irq = ((cec_irq & 0x08) >> 3) | ((cec_irq & 0x04) >> 1) |
+		  ((cec_irq & 0x02) << 1) | ((cec_irq & 0x01) << 3);
+	io_write(sd, 0x4e, cec_irq);
+
+	if (handled)
+		*handled = true;
+}
+
+static int adv76xx_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+	struct adv76xx_state *state = adap->priv;
+	struct v4l2_subdev *sd = &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_adap_log_addr(struct cec_adapter *adap, u8 addr)
+{
+	struct adv76xx_state *state = adap->priv;
+	struct v4l2_subdev *sd = &state->sd;
+	unsigned i, free_idx = ADV76XX_MAX_ADDRS;
+
+	if (!state->cec_enabled_adap)
+		return addr == CEC_LOG_ADDR_INVALID ? 0 : -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_adap_transmit(struct cec_adapter *adap, u8 attempts,
+				     u32 signal_free_time, struct cec_msg *msg)
+{
+	struct adv76xx_state *state = adap->priv;
+	struct v4l2_subdev *sd = &state->sd;
+	u8 len = msg->len;
+	unsigned i;
+
+	/*
+	 * The number of retries is the number of attempts - 1, but retry
+	 * at least once. It's not clear if a value of 0 is allowed, so
+	 * let's do at least one retry.
+	 */
+	cec_write_clr_set(sd, 0x12, 0x70, max(1, attempts - 1) << 4);
+
+	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 const struct cec_adap_ops adv76xx_cec_adap_ops = {
+	.adap_enable = adv76xx_cec_adap_enable,
+	.adap_log_addr = adv76xx_cec_adap_log_addr,
+	.adap_transmit = adv76xx_cec_adap_transmit,
+};
+#endif
+
 static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
 {
 	struct adv76xx_state *state = to_state(sd);
@@ -1945,6 +2165,11 @@ static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
 			*handled = true;
 	}
 
+#if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC)
+	/* cec */
+	adv76xx_cec_isr(sd, handled);
+#endif
+
 	/* tx 5v detect */
 	tx_5v = irq_reg_0x70 & info->cable_det_mask;
 	if (tx_5v) {
@@ -1994,39 +2219,12 @@ static int adv76xx_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
 	return 0;
 }
 
-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 int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
 {
 	struct adv76xx_state *state = to_state(sd);
 	const struct adv76xx_chip_info *info = state->info;
-	int spa_loc;
+	unsigned spa_loc;
+	u16 pa;
 	int err;
 	int i;
 
@@ -2057,6 +2255,10 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
 		edid->blocks = 2;
 		return -E2BIG;
 	}
+	pa = cec_get_edid_phys_addr(edid->edid, edid->blocks * 128, &spa_loc);
+	err = cec_phys_addr_validate(pa, &pa, NULL);
+	if (err)
+		return err;
 
 	v4l2_dbg(2, debug, sd, "%s: write EDID pad %d, edid.present = 0x%x\n",
 			__func__, edid->pad, state->edid.present);
@@ -2066,9 +2268,12 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
 	adv76xx_set_hpd(state, 0);
 	rep_write_clr_set(sd, info->edid_enable_reg, 0x0f, 0x00);
 
-	spa_loc = get_edid_spa_location(edid->edid);
-	if (spa_loc < 0)
-		spa_loc = 0xc0; /* Default value [REF_02, p. 116] */
+	/*
+	 * Return an error if no location of the source physical address
+	 * was found.
+	 */
+	if (spa_loc == 0)
+		return -EINVAL;
 
 	switch (edid->pad) {
 	case ADV76XX_PAD_HDMI_PORT_A:
@@ -2128,6 +2333,7 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
 		v4l2_err(sd, "error enabling edid (0x%x)\n", state->edid.present);
 		return -EIO;
 	}
+	cec_s_phys_addr(state->cec_adap, pa, false);
 
 	/* enable hotplug after 100 ms */
 	queue_delayed_work(state->work_queues,
@@ -2252,8 +2458,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);
@@ -2363,6 +2580,24 @@ static int adv76xx_subscribe_event(struct v4l2_subdev *sd,
 	}
 }
 
+static int adv76xx_registered(struct v4l2_subdev *sd)
+{
+	struct adv76xx_state *state = to_state(sd);
+	int err;
+
+	err = cec_register_adapter(state->cec_adap);
+	if (err)
+		cec_delete_adapter(state->cec_adap);
+	return err;
+}
+
+static void adv76xx_unregistered(struct v4l2_subdev *sd)
+{
+	struct adv76xx_state *state = to_state(sd);
+
+	cec_unregister_adapter(state->cec_adap);
+}
+
 /* ----------------------------------------------------------------------- */
 
 static const struct v4l2_ctrl_ops adv76xx_ctrl_ops = {
@@ -2406,6 +2641,11 @@ static const struct v4l2_subdev_ops adv76xx_ops = {
 	.pad = &adv76xx_pad_ops,
 };
 
+static const struct v4l2_subdev_internal_ops adv76xx_int_ops = {
+	.registered = adv76xx_registered,
+	.unregistered = adv76xx_unregistered,
+};
+
 /* -------------------------- custom ctrls ---------------------------------- */
 
 static const struct v4l2_ctrl_config adv7604_ctrl_analog_sampling_phase = {
@@ -3069,6 +3309,7 @@ static int adv76xx_probe(struct i2c_client *client,
 		id->name, i2c_adapter_id(client->adapter),
 		client->addr);
 	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
+	sd->internal_ops = &adv76xx_int_ops;
 
 	/* Configure IO Regmap region */
 	err = configure_regmap(state, ADV76XX_PAGE_IO);
@@ -3212,6 +3453,18 @@ static int adv76xx_probe(struct i2c_client *client,
 	err = adv76xx_core_init(sd);
 	if (err)
 		goto err_entity;
+
+#if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC)
+	state->cec_adap = cec_allocate_adapter(&adv76xx_cec_adap_ops,
+		state, dev_name(&client->dev),
+		CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS |
+		CEC_CAP_PASSTHROUGH | CEC_CAP_RC, ADV76XX_MAX_ADDRS,
+		&client->dev);
+	err = PTR_ERR_OR_ZERO(state->cec_adap);
+	if (err)
+		goto err_entity;
+#endif
+
 	v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name,
 			client->addr << 1, client->adapter->name);
 
@@ -3240,6 +3493,13 @@ 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);
+
 	cancel_delayed_work(&state->delayed_work_enable_hotplug);
 	destroy_workqueue(state->work_queues);
 	v4l2_async_unregister_subdev(sd);
-- 
2.8.1


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

* [PATCHv16 10/13] cec: adv7842: add cec support
  2016-04-29 13:52 [PATCHv16 00/13] HDMI CEC framework Hans Verkuil
                   ` (8 preceding siblings ...)
  2016-04-29 13:52 ` [PATCHv16 09/13] cec: adv7604: add cec support Hans Verkuil
@ 2016-04-29 13:52 ` Hans Verkuil
  2016-06-16 21:22   ` Mauro Carvalho Chehab
  2016-04-29 13:52 ` [PATCHv16 11/13] cec: adv7511: " Hans Verkuil
                   ` (2 subsequent siblings)
  12 siblings, 1 reply; 35+ messages in thread
From: Hans Verkuil @ 2016-04-29 13:52 UTC (permalink / raw)
  To: linux-media
  Cc: dri-devel, linux-samsung-soc, linux-input, lars, linux, Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

Add CEC support to the adv7842 driver.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/i2c/Kconfig   |   9 ++
 drivers/media/i2c/adv7842.c | 368 ++++++++++++++++++++++++++++++++++++--------
 2 files changed, 314 insertions(+), 63 deletions(-)

diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index cba1fc7..5168454 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -231,6 +231,7 @@ config VIDEO_ADV7842
 	tristate "Analog Devices ADV7842 decoder"
 	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
 	select HDMI
+	select MEDIA_CEC_EDID
 	---help---
 	  Support for the Analog Devices ADV7842 video decoder.
 
@@ -240,6 +241,14 @@ config VIDEO_ADV7842
 	  To compile this driver as a module, choose M here: the
 	  module will be called adv7842.
 
+config VIDEO_ADV7842_CEC
+	bool "Enable Analog Devices ADV7842 CEC support"
+	depends on VIDEO_ADV7842 && MEDIA_CEC
+	default y
+	---help---
+	  When selected the adv7842 will support the optional
+	  HDMI CEC feature.
+
 config VIDEO_BT819
 	tristate "BT819A VideoStream decoder"
 	depends on VIDEO_V4L2 && I2C
diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c
index ecaacb0..297ba7a 100644
--- a/drivers/media/i2c/adv7842.c
+++ b/drivers/media/i2c/adv7842.c
@@ -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,11 @@ 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;
+
+	struct cec_adapter *cec_adap;
+	u8   cec_addr[ADV7842_MAX_ADDRS];
+	u8   cec_valid_addrs;
+	bool cec_enabled_adap;
 };
 
 /* Unsupported timings. This device cannot support 720p30. */
@@ -418,9 +426,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 +704,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);
@@ -762,50 +782,18 @@ static int edid_write_vga_segment(struct v4l2_subdev *sd)
 	return 0;
 }
 
-static int edid_spa_location(const u8 *edid)
-{
-	u8 d;
-
-	/*
-	 * TODO, improve and update for other CEA extensions
-	 * currently only for 1 segment (256 bytes),
-	 * i.e. 1 extension block and CEA revision 3.
-	 */
-	if ((edid[0x7e] != 1) ||
-	    (edid[0x80] != 0x02) ||
-	    (edid[0x81] != 0x03)) {
-		return -EINVAL;
-	}
-	/*
-	 * 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 -EINVAL;
-}
-
 static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct adv7842_state *state = to_state(sd);
-	const u8 *val = state->hdmi_edid.edid;
-	int spa_loc = edid_spa_location(val);
+	const u8 *edid = state->hdmi_edid.edid;
+	int spa_loc;
+	u16 pa;
 	int err = 0;
 	int i;
 
-	v4l2_dbg(2, debug, sd, "%s: write EDID on port %c (spa at 0x%x)\n",
-			__func__, (port == ADV7842_EDID_PORT_A) ? 'A' : 'B', spa_loc);
+	v4l2_dbg(2, debug, sd, "%s: write EDID on port %c\n",
+			__func__, (port == ADV7842_EDID_PORT_A) ? 'A' : 'B');
 
 	/* HPA disable on port A and B */
 	io_write_and_or(sd, 0x20, 0xcf, 0x00);
@@ -816,24 +804,33 @@ static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port)
 	if (!state->hdmi_edid.present)
 		return 0;
 
+	pa = cec_get_edid_phys_addr(edid, 256, &spa_loc);
+	err = cec_phys_addr_validate(pa, &pa, NULL);
+	if (err)
+		return err;
+
+	/*
+	 * Return an error if no location of the source physical address
+	 * was found.
+	 */
+	if (spa_loc == 0)
+		return -EINVAL;
+
 	/* edid segment pointer '0' for HDMI ports */
 	rep_write_and_or(sd, 0x77, 0xef, 0x00);
 
 	for (i = 0; !err && i < 256; i += I2C_SMBUS_BLOCK_MAX)
 		err = adv_smbus_write_i2c_block_data(state->i2c_edid, i,
-						     I2C_SMBUS_BLOCK_MAX, val + i);
+						     I2C_SMBUS_BLOCK_MAX, edid + i);
 	if (err)
 		return err;
 
-	if (spa_loc < 0)
-		spa_loc = 0xc0; /* Default value [REF_02, p. 199] */
-
 	if (port == ADV7842_EDID_PORT_A) {
-		rep_write(sd, 0x72, val[spa_loc]);
-		rep_write(sd, 0x73, val[spa_loc + 1]);
+		rep_write(sd, 0x72, edid[spa_loc]);
+		rep_write(sd, 0x73, edid[spa_loc + 1]);
 	} else {
-		rep_write(sd, 0x74, val[spa_loc]);
-		rep_write(sd, 0x75, val[spa_loc + 1]);
+		rep_write(sd, 0x74, edid[spa_loc]);
+		rep_write(sd, 0x75, edid[spa_loc + 1]);
 	}
 	rep_write(sd, 0x76, spa_loc & 0xff);
 	rep_write_and_or(sd, 0x77, 0xbf, (spa_loc >> 2) & 0x40);
@@ -853,6 +850,7 @@ static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port)
 				(port == ADV7842_EDID_PORT_A) ? 'A' : 'B');
 		return -EIO;
 	}
+	cec_s_phys_addr(state->cec_adap, pa, false);
 
 	/* enable hotplug after 200 ms */
 	queue_delayed_work(state->work_queues,
@@ -983,20 +981,11 @@ 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;
-
-	if (reg_io_6f & 0x02)
-		val |= 1; /* port A */
-	if (reg_io_6f & 0x01)
-		val |= 2; /* port B */
+	u16 cable_det = adv7842_read_cable_det(sd);
 
-	v4l2_dbg(1, debug, sd, "%s: 0x%x -> 0x%x\n", __func__, prev, val);
+	v4l2_dbg(1, debug, sd, "%s: 0x%x\n", __func__, cable_det);
 
-	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,
@@ -2170,6 +2159,207 @@ static void adv7842_irq_enable(struct v4l2_subdev *sd, bool enable)
 	}
 }
 
+#if IS_ENABLED(CONFIG_VIDEO_ADV7842_CEC)
+static void adv7842_cec_tx_raw_status(struct v4l2_subdev *sd, u8 tx_raw_status)
+{
+	struct adv7842_state *state = to_state(sd);
+
+	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__);
+		cec_transmit_done(state->cec_adap, CEC_TX_STATUS_ARB_LOST,
+				  1, 0, 0, 0);
+		return;
+	}
+	if (tx_raw_status & 0x04) {
+		u8 status;
+		u8 nack_cnt;
+		u8 low_drive_cnt;
+
+		v4l2_dbg(1, debug, sd, "%s: tx raw: retry failed\n", __func__);
+		/*
+		 * We set this status bit since this hardware performs
+		 * retransmissions.
+		 */
+		status = CEC_TX_STATUS_MAX_RETRIES;
+		nack_cnt = cec_read(sd, 0x14) & 0xf;
+		if (nack_cnt)
+			status |= CEC_TX_STATUS_NACK;
+		low_drive_cnt = cec_read(sd, 0x14) >> 4;
+		if (low_drive_cnt)
+			status |= CEC_TX_STATUS_LOW_DRIVE;
+		cec_transmit_done(state->cec_adap, status,
+				  0, nack_cnt, low_drive_cnt, 0);
+		return;
+	}
+	if (tx_raw_status & 0x01) {
+		v4l2_dbg(1, debug, sd, "%s: tx raw: ready ok\n", __func__);
+		cec_transmit_done(state->cec_adap, CEC_TX_STATUS_OK, 0, 0, 0, 0);
+		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 adv7842_state *state = to_state(sd);
+		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 */
+			cec_received_msg(state->cec_adap, &msg);
+		}
+	}
+
+	io_write(sd, 0x94, cec_irq);
+
+	if (handled)
+		*handled = true;
+}
+
+static int adv7842_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+	struct adv7842_state *state = adap->priv;
+	struct v4l2_subdev *sd = &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_adap_log_addr(struct cec_adapter *adap, u8 addr)
+{
+	struct adv7842_state *state = adap->priv;
+	struct v4l2_subdev *sd = &state->sd;
+	unsigned i, free_idx = ADV7842_MAX_ADDRS;
+
+	if (!state->cec_enabled_adap)
+		return addr == CEC_LOG_ADDR_INVALID ? 0 : -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_adap_transmit(struct cec_adapter *adap, u8 attempts,
+				     u32 signal_free_time, struct cec_msg *msg)
+{
+	struct adv7842_state *state = adap->priv;
+	struct v4l2_subdev *sd = &state->sd;
+	u8 len = msg->len;
+	unsigned i;
+
+	/*
+	 * The number of retries is the number of attempts - 1, but retry
+	 * at least once. It's not clear if a value of 0 is allowed, so
+	 * let's do at least one retry.
+	 */
+	cec_write_clr_set(sd, 0x12, 0x70, max(1, attempts - 1) << 4);
+
+	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 const struct cec_adap_ops adv7842_cec_adap_ops = {
+	.adap_enable = adv7842_cec_adap_enable,
+	.adap_log_addr = adv7842_cec_adap_log_addr,
+	.adap_transmit = adv7842_cec_adap_transmit,
+};
+#endif
+
 static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
 {
 	struct adv7842_state *state = to_state(sd);
@@ -2241,6 +2431,11 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
 			*handled = true;
 	}
 
+#if IS_ENABLED(CONFIG_VIDEO_ADV7842_CEC)
+	/* cec */
+	adv7842_cec_isr(sd, handled);
+#endif
+
 	/* tx 5v detect */
 	if (irq_status[2] & 0x3) {
 		v4l2_dbg(1, debug, sd, "%s: irq tx_5v\n", __func__);
@@ -2321,10 +2516,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;
@@ -2509,8 +2706,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) {
@@ -3031,6 +3239,24 @@ static int adv7842_subscribe_event(struct v4l2_subdev *sd,
 	}
 }
 
+static int adv7842_registered(struct v4l2_subdev *sd)
+{
+	struct adv7842_state *state = to_state(sd);
+	int err;
+
+	err = cec_register_adapter(state->cec_adap);
+	if (err)
+		cec_delete_adapter(state->cec_adap);
+	return err;
+}
+
+static void adv7842_unregistered(struct v4l2_subdev *sd)
+{
+	struct adv7842_state *state = to_state(sd);
+
+	cec_unregister_adapter(state->cec_adap);
+}
+
 /* ----------------------------------------------------------------------- */
 
 static const struct v4l2_ctrl_ops adv7842_ctrl_ops = {
@@ -3077,6 +3303,11 @@ static const struct v4l2_subdev_ops adv7842_ops = {
 	.pad = &adv7842_pad_ops,
 };
 
+static const struct v4l2_subdev_internal_ops adv7842_int_ops = {
+	.registered = adv7842_registered,
+	.unregistered = adv7842_unregistered,
+};
+
 /* -------------------------- custom ctrls ---------------------------------- */
 
 static const struct v4l2_ctrl_config adv7842_ctrl_analog_sampling_phase = {
@@ -3241,6 +3472,7 @@ static int adv7842_probe(struct i2c_client *client,
 	sd = &state->sd;
 	v4l2_i2c_subdev_init(sd, client, &adv7842_ops);
 	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
+	sd->internal_ops = &adv7842_int_ops;
 	state->mode = pdata->mode;
 
 	state->hdmi_port_a = pdata->input == ADV7842_SELECT_HDMI_PORT_A;
@@ -3331,6 +3563,17 @@ static int adv7842_probe(struct i2c_client *client,
 	if (err)
 		goto err_entity;
 
+#if IS_ENABLED(CONFIG_VIDEO_ADV7842_CEC)
+	state->cec_adap = cec_allocate_adapter(&adv7842_cec_adap_ops,
+		state, dev_name(&client->dev),
+		CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS |
+		CEC_CAP_PASSTHROUGH | CEC_CAP_RC, ADV7842_MAX_ADDRS,
+		&client->dev);
+	err = PTR_ERR_OR_ZERO(state->cec_adap);
+	if (err)
+		goto err_entity;
+#endif
+
 	v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name,
 		  client->addr << 1, client->adapter->name);
 	return 0;
@@ -3355,7 +3598,6 @@ static int adv7842_remove(struct i2c_client *client)
 	struct adv7842_state *state = to_state(sd);
 
 	adv7842_irq_enable(sd, false);
-
 	cancel_delayed_work(&state->delayed_work_enable_hotplug);
 	destroy_workqueue(state->work_queues);
 	v4l2_device_unregister_subdev(sd);
-- 
2.8.1


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

* [PATCHv16 11/13] cec: adv7511: add cec support.
  2016-04-29 13:52 [PATCHv16 00/13] HDMI CEC framework Hans Verkuil
                   ` (9 preceding siblings ...)
  2016-04-29 13:52 ` [PATCHv16 10/13] cec: adv7842: " Hans Verkuil
@ 2016-04-29 13:52 ` Hans Verkuil
  2016-04-29 13:52 ` [PATCHv16 12/13] cec: s5p-cec: Add s5p-cec driver Hans Verkuil
  2016-04-29 13:52 ` [PATCHv16 13/13] vivid: add CEC emulation Hans Verkuil
  12 siblings, 0 replies; 35+ messages in thread
From: Hans Verkuil @ 2016-04-29 13:52 UTC (permalink / raw)
  To: linux-media
  Cc: dri-devel, linux-samsung-soc, linux-input, lars, linux,
	Hans Verkuil, Kamil Debski, Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

Add CEC support to the adv7511 driver.

Signed-off-by: Hans Verkuil <hansverk@cisco.com>
[k.debski@samsung.com: Merged changes from CEC Updates commit by Hans Verkuil]
Signed-off-by: Kamil Debski <kamil@wypas.org>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/i2c/Kconfig   |   9 +
 drivers/media/i2c/adv7511.c | 401 +++++++++++++++++++++++++++++++++++++++++++-
 include/media/i2c/adv7511.h |   6 +-
 3 files changed, 403 insertions(+), 13 deletions(-)

diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 5168454..6c2acb6 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -465,6 +465,7 @@ config VIDEO_ADV7511
 	tristate "Analog Devices ADV7511 encoder"
 	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
 	select HDMI
+	select MEDIA_CEC_EDID
 	---help---
 	  Support for the Analog Devices ADV7511 video encoder.
 
@@ -473,6 +474,14 @@ config VIDEO_ADV7511
 	  To compile this driver as a module, choose M here: the
 	  module will be called adv7511.
 
+config VIDEO_ADV7511_CEC
+	bool "Enable Analog Devices ADV7511 CEC support"
+	depends on VIDEO_ADV7511 && MEDIA_CEC
+	default y
+	---help---
+	  When selected the adv7511 will support the optional
+	  HDMI CEC feature.
+
 config VIDEO_AD9389B
 	tristate "Analog Devices AD9389B encoder"
 	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c
index 39271c3..002117b 100644
--- a/drivers/media/i2c/adv7511.c
+++ b/drivers/media/i2c/adv7511.c
@@ -33,6 +33,7 @@
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-dv-timings.h>
 #include <media/i2c/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,20 @@ 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;
+	struct cec_adapter *cec_adap;
+	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;
@@ -227,7 +238,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;
@@ -242,6 +253,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);
@@ -425,16 +464,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);
@@ -445,10 +496,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);
@@ -536,6 +595,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",
@@ -605,7 +665,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;
 }
@@ -663,15 +739,197 @@ static int adv7511_s_power(struct v4l2_subdev *sd, int on)
 	return true;
 }
 
+#if IS_ENABLED(CONFIG_VIDEO_ADV7511_CEC)
+static int adv7511_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+	struct adv7511_state *state = adap->priv;
+	struct v4l2_subdev *sd = &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_adap_log_addr(struct cec_adapter *adap, u8 addr)
+{
+	struct adv7511_state *state = adap->priv;
+	struct v4l2_subdev *sd = &state->sd;
+	unsigned i, free_idx = ADV7511_MAX_ADDRS;
+
+	if (!state->cec_enabled_adap)
+		return addr == CEC_LOG_ADDR_INVALID ? 0 : -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_adap_transmit(struct cec_adapter *adap, u8 attempts,
+				     u32 signal_free_time, struct cec_msg *msg)
+{
+	struct adv7511_state *state = adap->priv;
+	struct v4l2_subdev *sd = &state->sd;
+	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;
+	}
+
+	/*
+	 * The number of retries is the number of attempts - 1, but retry
+	 * at least once. It's not clear if a value of 0 is allowed, so
+	 * let's do at least one retry.
+	 */
+	adv7511_cec_write_and_or(sd, 0x12, ~0x70, max(1, attempts - 1) << 4);
+
+	/* 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;
+}
+
+static void adv_cec_tx_raw_status(struct v4l2_subdev *sd, u8 tx_raw_status)
+{
+	struct adv7511_state *state = get_adv7511_state(sd);
+
+	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__);
+		cec_transmit_done(state->cec_adap, CEC_TX_STATUS_ARB_LOST,
+				  1, 0, 0, 0);
+		return;
+	}
+	if (tx_raw_status & 0x08) {
+		u8 status;
+		u8 nack_cnt;
+		u8 low_drive_cnt;
+
+		v4l2_dbg(1, debug, sd, "%s: tx raw: retry failed\n", __func__);
+		/*
+		 * We set this status bit since this hardware performs
+		 * retransmissions.
+		 */
+		status = CEC_TX_STATUS_MAX_RETRIES;
+		nack_cnt = adv7511_cec_read(sd, 0x14) & 0xf;
+		if (nack_cnt)
+			status |= CEC_TX_STATUS_NACK;
+		low_drive_cnt = adv7511_cec_read(sd, 0x14) >> 4;
+		if (low_drive_cnt)
+			status |= CEC_TX_STATUS_LOW_DRIVE;
+		cec_transmit_done(state->cec_adap, status,
+				  0, nack_cnt, low_drive_cnt, 0);
+		return;
+	}
+	if (tx_raw_status & 0x20) {
+		v4l2_dbg(1, debug, sd, "%s: tx raw: ready ok\n", __func__);
+		cec_transmit_done(state->cec_adap, CEC_TX_STATUS_OK, 0, 0, 0, 0);
+		return;
+	}
+}
+
+static const struct cec_adap_ops adv7511_cec_adap_ops = {
+	.adap_enable = adv7511_cec_adap_enable,
+	.adap_log_addr = adv7511_cec_adap_log_addr,
+	.adap_transmit = adv7511_cec_adap_transmit,
+};
+#endif
+
 /* 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)
@@ -679,6 +937,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
@@ -701,20 +962,53 @@ static void adv7511_set_isr(struct v4l2_subdev *sd, bool enable)
 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 IS_ENABLED(CONFIG_VIDEO_ADV7511_CEC)
+	if (cec_irq & 0x38)
+		adv_cec_tx_raw_status(sd, cec_irq);
+
+	if (cec_irq & 1) {
+		struct adv7511_state *state = get_adv7511_state(sd);
+		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);
+			cec_received_msg(state->cec_adap, &msg);
+		}
+	}
+#endif
+
 	/* enable interrupts */
 	adv7511_set_isr(sd, true);
 
@@ -1183,6 +1477,8 @@ static void adv7511_notify_no_edid(struct v4l2_subdev *sd)
 	/* We failed to read the EDID, so send an event for this. */
 	ed.present = false;
 	ed.segment = adv7511_rd(sd, 0xc4);
+	ed.phys_addr = CEC_PHYS_ADDR_INVALID;
+	cec_s_phys_addr(state->cec_adap, ed.phys_addr, false);
 	v4l2_subdev_notify(sd, ADV7511_EDID_DETECT, (void *)&ed);
 	v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, 0x0);
 }
@@ -1406,13 +1702,16 @@ 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;
-
+		ed.phys_addr = cec_get_edid_phys_addr(state->edid.data,
+						      state->edid.segments * 256,
+						      NULL);
 		/* report when we have all segments
 		   but report only for segment 0
 		 */
 		ed.present = true;
 		ed.segment = 0;
 		state->edid_detect_counter++;
+		cec_s_phys_addr(state->cec_adap, ed.phys_addr, false);
 		v4l2_subdev_notify(sd, ADV7511_EDID_DETECT, (void *)&ed);
 		return ed.present;
 	}
@@ -1420,17 +1719,43 @@ static bool adv7511_check_edid_status(struct v4l2_subdev *sd)
 	return false;
 }
 
+static int adv7511_registered(struct v4l2_subdev *sd)
+{
+	struct adv7511_state *state = get_adv7511_state(sd);
+	int err;
+
+	err = cec_register_adapter(state->cec_adap);
+	if (err)
+		cec_delete_adapter(state->cec_adap);
+	return err;
+}
+
+static void adv7511_unregistered(struct v4l2_subdev *sd)
+{
+	struct adv7511_state *state = get_adv7511_state(sd);
+
+	cec_unregister_adapter(state->cec_adap);
+}
+
+static const struct v4l2_subdev_internal_ops adv7511_int_ops = {
+	.registered = adv7511_registered,
+	.unregistered = adv7511_unregistered,
+};
+
 /* ----------------------------------------------------------------------- */
 /* Setup ADV7511 */
 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,
@@ -1442,6 +1767,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)
@@ -1476,6 +1820,7 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *
 			 client->addr << 1);
 
 	v4l2_i2c_subdev_init(sd, client, &adv7511_ops);
+	sd->internal_ops = &adv7511_int_ops;
 
 	hdl = &state->hdl;
 	v4l2_ctrl_handler_init(hdl, 10);
@@ -1516,26 +1861,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");
@@ -1546,6 +1912,19 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *
 	INIT_DELAYED_WORK(&state->edid_handler, adv7511_edid_handler);
 
 	adv7511_init_setup(sd);
+
+#if IS_ENABLED(CONFIG_VIDEO_ADV7511_CEC)
+	state->cec_adap = cec_allocate_adapter(&adv7511_cec_adap_ops,
+		state, dev_name(&client->dev), CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS |
+		CEC_CAP_PASSTHROUGH | CEC_CAP_RC,
+		ADV7511_MAX_ADDRS, &client->dev);
+	err = PTR_ERR_OR_ZERO(state->cec_adap);
+	if (err) {
+		destroy_workqueue(state->work_queue);
+		goto err_unreg_pktmem;
+	}
+#endif
+
 	adv7511_set_isr(sd, true);
 	adv7511_check_monitor_present_status(sd);
 
@@ -1555,6 +1934,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:
@@ -1576,9 +1958,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);
diff --git a/include/media/i2c/adv7511.h b/include/media/i2c/adv7511.h
index d83b91d..61c3d71 100644
--- a/include/media/i2c/adv7511.h
+++ b/include/media/i2c/adv7511.h
@@ -32,11 +32,7 @@ struct adv7511_monitor_detect {
 struct adv7511_edid_detect {
 	int present;
 	int segment;
-};
-
-struct adv7511_cec_arg {
-	void *arg;
-	u32 f_flags;
+	uint16_t phys_addr;
 };
 
 struct adv7511_platform_data {
-- 
2.8.1


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

* [PATCHv16 12/13] cec: s5p-cec: Add s5p-cec driver
  2016-04-29 13:52 [PATCHv16 00/13] HDMI CEC framework Hans Verkuil
                   ` (10 preceding siblings ...)
  2016-04-29 13:52 ` [PATCHv16 11/13] cec: adv7511: " Hans Verkuil
@ 2016-04-29 13:52 ` Hans Verkuil
  2016-04-29 13:52 ` [PATCHv16 13/13] vivid: add CEC emulation Hans Verkuil
  12 siblings, 0 replies; 35+ messages in thread
From: Hans Verkuil @ 2016-04-29 13:52 UTC (permalink / raw)
  To: linux-media
  Cc: dri-devel, linux-samsung-soc, linux-input, lars, linux,
	Kamil Debski, Hans Verkuil

From: Kamil Debski <kamil@wypas.org>

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

Signed-off-by: Kamil Debski <kamil@wypas.org>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 .../devicetree/bindings/media/s5p-cec.txt          |  31 +++
 MAINTAINERS                                        |   7 +
 drivers/media/platform/Kconfig                     |  11 +
 drivers/media/platform/Makefile                    |   1 +
 drivers/media/platform/s5p-cec/Makefile            |   2 +
 drivers/media/platform/s5p-cec/exynos_hdmi_cec.h   |  38 +++
 .../media/platform/s5p-cec/exynos_hdmi_cecctrl.c   | 209 +++++++++++++++
 drivers/media/platform/s5p-cec/regs-cec.h          |  96 +++++++
 drivers/media/platform/s5p-cec/s5p_cec.c           | 295 +++++++++++++++++++++
 drivers/media/platform/s5p-cec/s5p_cec.h           |  76 ++++++
 10 files changed, 766 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

diff --git a/Documentation/devicetree/bindings/media/s5p-cec.txt b/Documentation/devicetree/bindings/media/s5p-cec.txt
new file mode 100644
index 0000000..925ab4d
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/s5p-cec.txt
@@ -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";
+};
diff --git a/MAINTAINERS b/MAINTAINERS
index 83bd865..0e43b30 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1581,6 +1581,13 @@ L:	linux-media@vger.kernel.org
 S:	Maintained
 F:	drivers/media/platform/s5p-tv/
 
+ARM/SAMSUNG S5P SERIES HDMI CEC SUBSYSTEM SUPPORT
+M:	Kyungmin Park <kyungmin.park@samsung.com>
+L:	linux-arm-kernel@lists.infradead.org
+L:	linux-media@vger.kernel.org
+S:	Maintained
+F:	drivers/media/platform/s5p-cec/
+
 ARM/SAMSUNG S5P SERIES JPEG CODEC SUPPORT
 M:	Andrzej Pietrasiewicz <andrzej.p@samsung.com>
 M:	Jacek Anaszewski <j.anaszewski@samsung.com>
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 84e041c..c3945c3 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -108,6 +108,17 @@ 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 && MEDIA_CEC && (PLAT_S5P || ARCH_EXYNOS || COMPILE_TEST)
+	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"
 
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index bbb7bd1..f598c80 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -28,6 +28,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/
diff --git a/drivers/media/platform/s5p-cec/Makefile b/drivers/media/platform/s5p-cec/Makefile
new file mode 100644
index 0000000..0e2cf45
--- /dev/null
+++ b/drivers/media/platform/s5p-cec/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_CEC)	+= s5p-cec.o
+s5p-cec-y += s5p_cec.o exynos_hdmi_cecctrl.o
diff --git a/drivers/media/platform/s5p-cec/exynos_hdmi_cec.h b/drivers/media/platform/s5p-cec/exynos_hdmi_cec.h
new file mode 100644
index 0000000..3e4fc7b
--- /dev/null
+++ b/drivers/media/platform/s5p-cec/exynos_hdmi_cec.h
@@ -0,0 +1,38 @@
+/* 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, u8 retries);
+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_ */
diff --git a/drivers/media/platform/s5p-cec/exynos_hdmi_cecctrl.c b/drivers/media/platform/s5p-cec/exynos_hdmi_cecctrl.c
new file mode 100644
index 0000000..ce95e0f
--- /dev/null
+++ b/drivers/media/platform/s5p-cec/exynos_hdmi_cecctrl.c
@@ -0,0 +1,209 @@
+/* drivers/media/platform/s5p-cec/exynos_hdmi_cecctrl.c
+ *
+ * Copyright (c) 2009, 2014 Samsung Electronics
+ *		http://www.samsung.com/
+ *
+ * cec ftn file for Samsung TVOUT driver
+ *
+ * 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.
+ */
+
+#include <linux/io.h>
+#include <linux/device.h>
+
+#include "exynos_hdmi_cec.h"
+#include "regs-cec.h"
+
+#define S5P_HDMI_FIN			24000000
+#define CEC_DIV_RATIO			320000
+
+#define CEC_MESSAGE_BROADCAST_MASK	0x0F
+#define CEC_MESSAGE_BROADCAST		0x0F
+#define CEC_FILTER_THRESHOLD		0x15
+
+void s5p_cec_set_divider(struct s5p_cec_dev *cec)
+{
+	u32 div_ratio, div_val;
+	unsigned int reg;
+
+	div_ratio  = S5P_HDMI_FIN / CEC_DIV_RATIO - 1;
+
+	if (regmap_read(cec->pmu, EXYNOS_HDMI_PHY_CONTROL, &reg)) {
+		dev_err(cec->dev, "failed to read phy control\n");
+		return;
+	}
+
+	reg = (reg & ~(0x3FF << 16)) | (div_ratio << 16);
+
+	if (regmap_write(cec->pmu, EXYNOS_HDMI_PHY_CONTROL, reg)) {
+		dev_err(cec->dev, "failed to write phy control\n");
+		return;
+	}
+
+	div_val = CEC_DIV_RATIO * 0.00005 - 1;
+
+	writeb(0x0, cec->reg + S5P_CEC_DIVISOR_3);
+	writeb(0x0, cec->reg + S5P_CEC_DIVISOR_2);
+	writeb(0x0, cec->reg + S5P_CEC_DIVISOR_1);
+	writeb(div_val, cec->reg + S5P_CEC_DIVISOR_0);
+}
+
+void s5p_cec_enable_rx(struct s5p_cec_dev *cec)
+{
+	u8 reg;
+
+	reg = readb(cec->reg + S5P_CEC_RX_CTRL);
+	reg |= S5P_CEC_RX_CTRL_ENABLE;
+	writeb(reg, cec->reg + S5P_CEC_RX_CTRL);
+}
+
+void s5p_cec_mask_rx_interrupts(struct s5p_cec_dev *cec)
+{
+	u8 reg;
+
+	reg = readb(cec->reg + S5P_CEC_IRQ_MASK);
+	reg |= S5P_CEC_IRQ_RX_DONE;
+	reg |= S5P_CEC_IRQ_RX_ERROR;
+	writeb(reg, cec->reg + S5P_CEC_IRQ_MASK);
+}
+
+void s5p_cec_unmask_rx_interrupts(struct s5p_cec_dev *cec)
+{
+	u8 reg;
+
+	reg = readb(cec->reg + S5P_CEC_IRQ_MASK);
+	reg &= ~S5P_CEC_IRQ_RX_DONE;
+	reg &= ~S5P_CEC_IRQ_RX_ERROR;
+	writeb(reg, cec->reg + S5P_CEC_IRQ_MASK);
+}
+
+void s5p_cec_mask_tx_interrupts(struct s5p_cec_dev *cec)
+{
+	u8 reg;
+
+	reg = readb(cec->reg + S5P_CEC_IRQ_MASK);
+	reg |= S5P_CEC_IRQ_TX_DONE;
+	reg |= S5P_CEC_IRQ_TX_ERROR;
+	writeb(reg, cec->reg + S5P_CEC_IRQ_MASK);
+
+}
+
+void s5p_cec_unmask_tx_interrupts(struct s5p_cec_dev *cec)
+{
+	u8 reg;
+
+	reg = readb(cec->reg + S5P_CEC_IRQ_MASK);
+	reg &= ~S5P_CEC_IRQ_TX_DONE;
+	reg &= ~S5P_CEC_IRQ_TX_ERROR;
+	writeb(reg, cec->reg + S5P_CEC_IRQ_MASK);
+}
+
+void s5p_cec_reset(struct s5p_cec_dev *cec)
+{
+	u8 reg;
+
+	writeb(S5P_CEC_RX_CTRL_RESET, cec->reg + S5P_CEC_RX_CTRL);
+	writeb(S5P_CEC_TX_CTRL_RESET, cec->reg + S5P_CEC_TX_CTRL);
+
+	reg = readb(cec->reg + 0xc4);
+	reg &= ~0x1;
+	writeb(reg, cec->reg + 0xc4);
+}
+
+void s5p_cec_tx_reset(struct s5p_cec_dev *cec)
+{
+	writeb(S5P_CEC_TX_CTRL_RESET, cec->reg + S5P_CEC_TX_CTRL);
+}
+
+void s5p_cec_rx_reset(struct s5p_cec_dev *cec)
+{
+	u8 reg;
+
+	writeb(S5P_CEC_RX_CTRL_RESET, cec->reg + S5P_CEC_RX_CTRL);
+
+	reg = readb(cec->reg + 0xc4);
+	reg &= ~0x1;
+	writeb(reg, cec->reg + 0xc4);
+}
+
+void s5p_cec_threshold(struct s5p_cec_dev *cec)
+{
+	writeb(CEC_FILTER_THRESHOLD, cec->reg + S5P_CEC_RX_FILTER_TH);
+	writeb(0, cec->reg + S5P_CEC_RX_FILTER_CTRL);
+}
+
+void s5p_cec_copy_packet(struct s5p_cec_dev *cec, char *data,
+			 size_t count, u8 retries)
+{
+	int i = 0;
+	u8 reg;
+
+	while (i < count) {
+		writeb(data[i], cec->reg + (S5P_CEC_TX_BUFF0 + (i * 4)));
+		i++;
+	}
+
+	writeb(count, cec->reg + S5P_CEC_TX_BYTES);
+	reg = readb(cec->reg + S5P_CEC_TX_CTRL);
+	reg |= S5P_CEC_TX_CTRL_START;
+	reg &= ~0x70;
+	reg |= retries << 4;
+
+	if ((data[0] & CEC_MESSAGE_BROADCAST_MASK) == CEC_MESSAGE_BROADCAST) {
+		dev_dbg(cec->dev, "Broadcast");
+		reg |= S5P_CEC_TX_CTRL_BCAST;
+	} else {
+		dev_dbg(cec->dev, "No Broadcast");
+		reg &= ~S5P_CEC_TX_CTRL_BCAST;
+	}
+
+	writeb(reg, cec->reg + S5P_CEC_TX_CTRL);
+	dev_dbg(cec->dev, "cec-tx: cec count (%zu): %*ph", count,
+		(int)count, data);
+}
+
+void s5p_cec_set_addr(struct s5p_cec_dev *cec, u32 addr)
+{
+	writeb(addr & 0x0F, cec->reg + S5P_CEC_LOGIC_ADDR);
+}
+
+u32 s5p_cec_get_status(struct s5p_cec_dev *cec)
+{
+	u32 status = 0;
+
+	status = readb(cec->reg + S5P_CEC_STATUS_0);
+	status |= readb(cec->reg + S5P_CEC_STATUS_1) << 8;
+	status |= readb(cec->reg + S5P_CEC_STATUS_2) << 16;
+	status |= readb(cec->reg + S5P_CEC_STATUS_3) << 24;
+
+	dev_dbg(cec->dev, "status = 0x%x!\n", status);
+
+	return status;
+}
+
+void s5p_clr_pending_tx(struct s5p_cec_dev *cec)
+{
+	writeb(S5P_CEC_IRQ_TX_DONE | S5P_CEC_IRQ_TX_ERROR,
+					cec->reg + S5P_CEC_IRQ_CLEAR);
+}
+
+void s5p_clr_pending_rx(struct s5p_cec_dev *cec)
+{
+	writeb(S5P_CEC_IRQ_RX_DONE | S5P_CEC_IRQ_RX_ERROR,
+					cec->reg + S5P_CEC_IRQ_CLEAR);
+}
+
+void s5p_cec_get_rx_buf(struct s5p_cec_dev *cec, u32 size, u8 *buffer)
+{
+	u32 i = 0;
+	char debug[40];
+
+	while (i < size) {
+		buffer[i] = readb(cec->reg + S5P_CEC_RX_BUFF0 + (i * 4));
+		sprintf(debug + i * 2, "%02x ", buffer[i]);
+		i++;
+	}
+	dev_dbg(cec->dev, "cec-rx: cec size(%d): %s", size, debug);
+}
diff --git a/drivers/media/platform/s5p-cec/regs-cec.h b/drivers/media/platform/s5p-cec/regs-cec.h
new file mode 100644
index 0000000..b2e7e12
--- /dev/null
+++ b/drivers/media/platform/s5p-cec/regs-cec.h
@@ -0,0 +1,96 @@
+/* drivers/media/platform/s5p-cec/regs-cec.h
+ *
+ * Copyright (c) 2010 Samsung Electronics
+ *		http://www.samsung.com/
+ *
+ *  register header file for Samsung TVOUT driver
+ *
+ * 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_REGS__H
+#define __EXYNOS_REGS__H
+
+/*
+ * Register part
+ */
+#define S5P_CEC_STATUS_0			(0x0000)
+#define S5P_CEC_STATUS_1			(0x0004)
+#define S5P_CEC_STATUS_2			(0x0008)
+#define S5P_CEC_STATUS_3			(0x000C)
+#define S5P_CEC_IRQ_MASK			(0x0010)
+#define S5P_CEC_IRQ_CLEAR			(0x0014)
+#define S5P_CEC_LOGIC_ADDR			(0x0020)
+#define S5P_CEC_DIVISOR_0			(0x0030)
+#define S5P_CEC_DIVISOR_1			(0x0034)
+#define S5P_CEC_DIVISOR_2			(0x0038)
+#define S5P_CEC_DIVISOR_3			(0x003C)
+
+#define S5P_CEC_TX_CTRL				(0x0040)
+#define S5P_CEC_TX_BYTES			(0x0044)
+#define S5P_CEC_TX_STAT0			(0x0060)
+#define S5P_CEC_TX_STAT1			(0x0064)
+#define S5P_CEC_TX_BUFF0			(0x0080)
+#define S5P_CEC_TX_BUFF1			(0x0084)
+#define S5P_CEC_TX_BUFF2			(0x0088)
+#define S5P_CEC_TX_BUFF3			(0x008C)
+#define S5P_CEC_TX_BUFF4			(0x0090)
+#define S5P_CEC_TX_BUFF5			(0x0094)
+#define S5P_CEC_TX_BUFF6			(0x0098)
+#define S5P_CEC_TX_BUFF7			(0x009C)
+#define S5P_CEC_TX_BUFF8			(0x00A0)
+#define S5P_CEC_TX_BUFF9			(0x00A4)
+#define S5P_CEC_TX_BUFF10			(0x00A8)
+#define S5P_CEC_TX_BUFF11			(0x00AC)
+#define S5P_CEC_TX_BUFF12			(0x00B0)
+#define S5P_CEC_TX_BUFF13			(0x00B4)
+#define S5P_CEC_TX_BUFF14			(0x00B8)
+#define S5P_CEC_TX_BUFF15			(0x00BC)
+
+#define S5P_CEC_RX_CTRL				(0x00C0)
+#define S5P_CEC_RX_STAT0			(0x00E0)
+#define S5P_CEC_RX_STAT1			(0x00E4)
+#define S5P_CEC_RX_BUFF0			(0x0100)
+#define S5P_CEC_RX_BUFF1			(0x0104)
+#define S5P_CEC_RX_BUFF2			(0x0108)
+#define S5P_CEC_RX_BUFF3			(0x010C)
+#define S5P_CEC_RX_BUFF4			(0x0110)
+#define S5P_CEC_RX_BUFF5			(0x0114)
+#define S5P_CEC_RX_BUFF6			(0x0118)
+#define S5P_CEC_RX_BUFF7			(0x011C)
+#define S5P_CEC_RX_BUFF8			(0x0120)
+#define S5P_CEC_RX_BUFF9			(0x0124)
+#define S5P_CEC_RX_BUFF10			(0x0128)
+#define S5P_CEC_RX_BUFF11			(0x012C)
+#define S5P_CEC_RX_BUFF12			(0x0130)
+#define S5P_CEC_RX_BUFF13			(0x0134)
+#define S5P_CEC_RX_BUFF14			(0x0138)
+#define S5P_CEC_RX_BUFF15			(0x013C)
+
+#define S5P_CEC_RX_FILTER_CTRL			(0x0180)
+#define S5P_CEC_RX_FILTER_TH			(0x0184)
+
+/*
+ * Bit definition part
+ */
+#define S5P_CEC_IRQ_TX_DONE			(1<<0)
+#define S5P_CEC_IRQ_TX_ERROR			(1<<1)
+#define S5P_CEC_IRQ_RX_DONE			(1<<4)
+#define S5P_CEC_IRQ_RX_ERROR			(1<<5)
+
+#define S5P_CEC_TX_CTRL_START			(1<<0)
+#define S5P_CEC_TX_CTRL_BCAST			(1<<1)
+#define S5P_CEC_TX_CTRL_RETRY			(0x04<<4)
+#define S5P_CEC_TX_CTRL_RESET			(1<<7)
+
+#define S5P_CEC_RX_CTRL_ENABLE			(1<<0)
+#define S5P_CEC_RX_CTRL_RESET			(1<<7)
+
+#define S5P_CEC_LOGIC_ADDR_MASK			(0xF)
+
+/* PMU Registers for PHY */
+#define EXYNOS_HDMI_PHY_CONTROL			0x700
+
+#endif	/* __EXYNOS_REGS__H	*/
diff --git a/drivers/media/platform/s5p-cec/s5p_cec.c b/drivers/media/platform/s5p-cec/s5p_cec.c
new file mode 100644
index 0000000..ee42beb
--- /dev/null
+++ b/drivers/media/platform/s5p-cec/s5p_cec.c
@@ -0,0 +1,295 @@
+/* drivers/media/platform/s5p-cec/s5p_cec.c
+ *
+ * Samsung S5P CEC driver
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This driver is based on the "cec interface driver for exynos soc" by
+ * SangPil Moon.
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/timer.h>
+#include <linux/version.h>
+#include <linux/workqueue.h>
+#include <media/cec.h>
+
+#include "exynos_hdmi_cec.h"
+#include "regs-cec.h"
+#include "s5p_cec.h"
+
+#define CEC_NAME	"s5p-cec"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "debug level (0-2)");
+
+static int s5p_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+	struct s5p_cec_dev *cec = adap->priv;
+	int ret;
+
+	if (enable) {
+		ret = pm_runtime_get_sync(cec->dev);
+
+		s5p_cec_reset(cec);
+
+		s5p_cec_set_divider(cec);
+		s5p_cec_threshold(cec);
+
+		s5p_cec_unmask_tx_interrupts(cec);
+		s5p_cec_unmask_rx_interrupts(cec);
+		s5p_cec_enable_rx(cec);
+	} else {
+		s5p_cec_mask_tx_interrupts(cec);
+		s5p_cec_mask_rx_interrupts(cec);
+		pm_runtime_disable(cec->dev);
+	}
+
+	return 0;
+}
+
+static int s5p_cec_adap_log_addr(struct cec_adapter *adap, u8 addr)
+{
+	struct s5p_cec_dev *cec = adap->priv;
+
+	s5p_cec_set_addr(cec, addr);
+	return 0;
+}
+
+static int s5p_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
+				 u32 signal_free_time, struct cec_msg *msg)
+{
+	struct s5p_cec_dev *cec = adap->priv;
+
+	/*
+	 * Unclear if 0 retries are allowed by the hardware, so have 1 as
+	 * the minimum.
+	 */
+	s5p_cec_copy_packet(cec, msg->msg, msg->len, max(1, attempts - 1));
+	return 0;
+}
+
+static irqreturn_t s5p_cec_irq_handler(int irq, void *priv)
+{
+	struct s5p_cec_dev *cec = priv;
+	u32 status = 0;
+
+	status = s5p_cec_get_status(cec);
+
+	dev_dbg(cec->dev, "irq received\n");
+
+	if (status & CEC_STATUS_TX_DONE) {
+		if (status & CEC_STATUS_TX_ERROR) {
+			dev_dbg(cec->dev, "CEC_STATUS_TX_ERROR set\n");
+			cec->tx = STATE_ERROR;
+		} else {
+			dev_dbg(cec->dev, "CEC_STATUS_TX_DONE\n");
+			cec->tx = STATE_DONE;
+		}
+		s5p_clr_pending_tx(cec);
+	}
+
+	if (status & CEC_STATUS_RX_DONE) {
+		if (status & CEC_STATUS_RX_ERROR) {
+			dev_dbg(cec->dev, "CEC_STATUS_RX_ERROR set\n");
+			s5p_cec_rx_reset(cec);
+			s5p_cec_enable_rx(cec);
+		} else {
+			dev_dbg(cec->dev, "CEC_STATUS_RX_DONE set\n");
+			if (cec->rx != STATE_IDLE)
+				dev_dbg(cec->dev, "Buffer overrun (worker did not process previous message)\n");
+			cec->rx = STATE_BUSY;
+			cec->msg.len = status >> 24;
+			cec->msg.rx_status = CEC_RX_STATUS_OK;
+			s5p_cec_get_rx_buf(cec, cec->msg.len,
+					cec->msg.msg);
+			cec->rx = STATE_DONE;
+			s5p_cec_enable_rx(cec);
+		}
+		/* Clear interrupt pending bit */
+		s5p_clr_pending_rx(cec);
+	}
+	return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t s5p_cec_irq_handler_thread(int irq, void *priv)
+{
+	struct s5p_cec_dev *cec = priv;
+
+	dev_dbg(cec->dev, "irq processing thread\n");
+	switch (cec->tx) {
+	case STATE_DONE:
+		cec_transmit_done(cec->adap, CEC_TX_STATUS_OK, 0, 0, 0, 0);
+		cec->tx = STATE_IDLE;
+		break;
+	case STATE_ERROR:
+		cec_transmit_done(cec->adap,
+			CEC_TX_STATUS_MAX_RETRIES | CEC_TX_STATUS_ERROR,
+			0, 0, 0, 1);
+		cec->tx = STATE_IDLE;
+		break;
+	case STATE_BUSY:
+		dev_err(cec->dev, "state set to busy, this should not occur here\n");
+		break;
+	default:
+		break;
+	}
+
+	switch (cec->rx) {
+	case STATE_DONE:
+		cec_received_msg(cec->adap, &cec->msg);
+		cec->rx = STATE_IDLE;
+		break;
+	default:
+		break;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static const struct cec_adap_ops s5p_cec_adap_ops = {
+	.adap_enable = s5p_cec_adap_enable,
+	.adap_log_addr = s5p_cec_adap_log_addr,
+	.adap_transmit = s5p_cec_adap_transmit,
+};
+
+static int s5p_cec_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	struct s5p_cec_dev *cec;
+	int ret;
+
+	cec = devm_kzalloc(&pdev->dev, sizeof(*cec), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	cec->dev = dev;
+
+	cec->irq = platform_get_irq(pdev, 0);
+	if (IS_ERR_VALUE(cec->irq))
+		return cec->irq;
+
+	ret = devm_request_threaded_irq(dev, cec->irq, s5p_cec_irq_handler,
+		s5p_cec_irq_handler_thread, 0, pdev->name, cec);
+	if (IS_ERR_VALUE(ret))
+		return ret;
+
+	cec->clk = devm_clk_get(dev, "hdmicec");
+	if (IS_ERR(cec->clk))
+		return PTR_ERR(cec->clk);
+
+	cec->pmu = syscon_regmap_lookup_by_phandle(dev->of_node,
+						 "samsung,syscon-phandle");
+	if (IS_ERR(cec->pmu))
+		return -EPROBE_DEFER;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	cec->reg = devm_ioremap_resource(dev, res);
+	if (IS_ERR(cec->reg))
+		return PTR_ERR(cec->reg);
+
+	cec->adap = cec_allocate_adapter(&s5p_cec_adap_ops, cec,
+		CEC_NAME,
+		CEC_CAP_PHYS_ADDR | CEC_CAP_LOG_ADDRS | CEC_CAP_TRANSMIT |
+		CEC_CAP_PASSTHROUGH | CEC_CAP_RC,
+		1, &pdev->dev);
+	ret = PTR_ERR_OR_ZERO(cec->adap);
+	if (ret)
+		return ret;
+	ret = cec_register_adapter(cec->adap);
+	if (ret) {
+		cec_delete_adapter(cec->adap);
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, cec);
+	pm_runtime_enable(dev);
+
+	dev_dbg(dev, "successfuly probed\n");
+	return 0;
+}
+
+static int s5p_cec_remove(struct platform_device *pdev)
+{
+	struct s5p_cec_dev *cec = platform_get_drvdata(pdev);
+
+	cec_unregister_adapter(cec->adap);
+	pm_runtime_disable(&pdev->dev);
+	return 0;
+}
+
+static int s5p_cec_runtime_suspend(struct device *dev)
+{
+	struct s5p_cec_dev *cec = dev_get_drvdata(dev);
+
+	clk_disable_unprepare(cec->clk);
+	return 0;
+}
+
+static int s5p_cec_runtime_resume(struct device *dev)
+{
+	struct s5p_cec_dev *cec = dev_get_drvdata(dev);
+	int ret;
+
+	ret = clk_prepare_enable(cec->clk);
+	if (ret < 0)
+		return ret;
+	return 0;
+}
+
+static int s5p_cec_suspend(struct device *dev)
+{
+	if (pm_runtime_suspended(dev))
+		return 0;
+	return s5p_cec_runtime_suspend(dev);
+}
+
+static int s5p_cec_resume(struct device *dev)
+{
+	if (pm_runtime_suspended(dev))
+		return 0;
+	return s5p_cec_runtime_resume(dev);
+}
+
+static const struct dev_pm_ops s5p_cec_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(s5p_cec_suspend, s5p_cec_resume)
+	SET_RUNTIME_PM_OPS(s5p_cec_runtime_suspend, s5p_cec_runtime_resume,
+			   NULL)
+};
+
+static const struct of_device_id s5p_cec_match[] = {
+	{
+		.compatible	= "samsung,s5p-cec",
+	},
+	{},
+};
+
+static struct platform_driver s5p_cec_pdrv = {
+	.probe	= s5p_cec_probe,
+	.remove	= s5p_cec_remove,
+	.driver	= {
+		.name		= CEC_NAME,
+		.of_match_table	= s5p_cec_match,
+		.pm		= &s5p_cec_pm_ops,
+	},
+};
+
+module_platform_driver(s5p_cec_pdrv);
+
+MODULE_AUTHOR("Kamil Debski <kamil@wypas.org>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Samsung S5P CEC driver");
diff --git a/drivers/media/platform/s5p-cec/s5p_cec.h b/drivers/media/platform/s5p-cec/s5p_cec.h
new file mode 100644
index 0000000..03732c1
--- /dev/null
+++ b/drivers/media/platform/s5p-cec/s5p_cec.h
@@ -0,0 +1,76 @@
+/* drivers/media/platform/s5p-cec/s5p_cec.h
+ *
+ * Samsung S5P HDMI CEC driver
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef _S5P_CEC_H_
+#define _S5P_CEC_H_ __FILE__
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/timer.h>
+#include <linux/version.h>
+#include <linux/workqueue.h>
+#include <media/cec.h>
+
+#include "exynos_hdmi_cec.h"
+#include "regs-cec.h"
+#include "s5p_cec.h"
+
+#define CEC_NAME	"s5p-cec"
+
+#define CEC_STATUS_TX_RUNNING		(1 << 0)
+#define CEC_STATUS_TX_TRANSFERRING	(1 << 1)
+#define CEC_STATUS_TX_DONE		(1 << 2)
+#define CEC_STATUS_TX_ERROR		(1 << 3)
+#define CEC_STATUS_TX_BYTES		(0xFF << 8)
+#define CEC_STATUS_RX_RUNNING		(1 << 16)
+#define CEC_STATUS_RX_RECEIVING		(1 << 17)
+#define CEC_STATUS_RX_DONE		(1 << 18)
+#define CEC_STATUS_RX_ERROR		(1 << 19)
+#define CEC_STATUS_RX_BCAST		(1 << 20)
+#define CEC_STATUS_RX_BYTES		(0xFF << 24)
+
+#define CEC_WORKER_TX_DONE		(1 << 0)
+#define CEC_WORKER_RX_MSG		(1 << 1)
+
+/* CEC Rx buffer size */
+#define CEC_RX_BUFF_SIZE		16
+/* CEC Tx buffer size */
+#define CEC_TX_BUFF_SIZE		16
+
+enum cec_state {
+	STATE_IDLE,
+	STATE_BUSY,
+	STATE_DONE,
+	STATE_ERROR
+};
+
+struct s5p_cec_dev {
+	struct cec_adapter	*adap;
+	struct clk		*clk;
+	struct device		*dev;
+	struct mutex		lock;
+	struct regmap           *pmu;
+	int			irq;
+	void __iomem		*reg;
+
+	enum cec_state		rx;
+	enum cec_state		tx;
+	struct cec_msg		msg;
+};
+
+#endif /* _S5P_CEC_H_ */
-- 
2.8.1


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

* [PATCHv16 13/13] vivid: add CEC emulation
  2016-04-29 13:52 [PATCHv16 00/13] HDMI CEC framework Hans Verkuil
                   ` (11 preceding siblings ...)
  2016-04-29 13:52 ` [PATCHv16 12/13] cec: s5p-cec: Add s5p-cec driver Hans Verkuil
@ 2016-04-29 13:52 ` Hans Verkuil
  12 siblings, 0 replies; 35+ messages in thread
From: Hans Verkuil @ 2016-04-29 13:52 UTC (permalink / raw)
  To: linux-media
  Cc: dri-devel, linux-samsung-soc, linux-input, lars, linux, Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

The vivid driver has been extended to provide CEC adapters for the HDMI
input and HDMI outputs in order to test CEC applications.

This CEC emulation is faithful to the CEC timings (i.e., it all at a
snail's pace).

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 Documentation/video4linux/vivid.txt              |  36 +++-
 drivers/media/platform/vivid/Kconfig             |   9 +
 drivers/media/platform/vivid/Makefile            |   4 +
 drivers/media/platform/vivid/vivid-cec.c         | 254 +++++++++++++++++++++++
 drivers/media/platform/vivid/vivid-cec.h         |  33 +++
 drivers/media/platform/vivid/vivid-core.c        | 119 ++++++++++-
 drivers/media/platform/vivid/vivid-core.h        |  27 +++
 drivers/media/platform/vivid/vivid-kthread-cap.c |  11 +
 drivers/media/platform/vivid/vivid-vid-cap.c     |  23 +-
 drivers/media/platform/vivid/vivid-vid-common.c  |   7 +
 10 files changed, 508 insertions(+), 15 deletions(-)
 create mode 100644 drivers/media/platform/vivid/vivid-cec.c
 create mode 100644 drivers/media/platform/vivid/vivid-cec.h

diff --git a/Documentation/video4linux/vivid.txt b/Documentation/video4linux/vivid.txt
index 8da5d2a..1b26519 100644
--- a/Documentation/video4linux/vivid.txt
+++ b/Documentation/video4linux/vivid.txt
@@ -74,7 +74,8 @@ Section 11: Cropping, Composing, Scaling
 Section 12: Formats
 Section 13: Capture Overlay
 Section 14: Output Overlay
-Section 15: Some Future Improvements
+Section 15: CEC (Consumer Electronics Control)
+Section 16: Some Future Improvements
 
 
 Section 1: Configuring the driver
@@ -364,7 +365,11 @@ For HDMI inputs it is possible to set the EDID. By default a simple EDID
 is provided. You can only set the EDID for HDMI inputs. Internally, however,
 the EDID is shared between all HDMI inputs.
 
-No interpretation is done of the EDID data.
+No interpretation is done of the EDID data with the exception of the
+physical address. See the CEC section for more details.
+
+There is a maximum of 15 HDMI inputs (if there are more, then they will be
+reduced to 15) since that's the limitation of the EDID physical address.
 
 
 Section 3: Video Output
@@ -409,6 +414,9 @@ standard, and for all others a 1:1 pixel aspect ratio is returned.
 
 An HDMI output has a valid EDID which can be obtained through VIDIOC_G_EDID.
 
+There is a maximum of 15 HDMI outputs (if there are more, then they will be
+reduced to 15) since that's the limitation of the EDID physical address. See
+also the CEC section for more details.
 
 Section 4: VBI Capture
 ----------------------
@@ -1108,7 +1116,26 @@ capabilities will slow down the video loop considerably as a lot of checks have
 to be done per pixel.
 
 
-Section 15: Some Future Improvements
+Section 15: CEC (Consumer Electronics Control)
+----------------------------------------------
+
+If there are HDMI inputs then a CEC adapter will be created that has
+the same number of input ports. This is the equivalent of e.g. a TV that
+has that number of inputs. Each HDMI output will also create a
+CEC adapter that is hooked up to the corresponding input port, or (if there
+are more outputs than inputs) is not hooked up at all. In other words,
+this is the equivalent of hooking up each output device to an input port of
+the TV. Any remaining output devices remain unconnected.
+
+The EDID that each output reads reports a unique CEC physical address that is
+based on the physical address of the EDID of the input. So if the EDID of the
+receiver has physical address A.B.0.0, then each output will see an EDID
+containing physical address A.B.C.0 where C is 1 to the number of inputs. If
+there are more outputs than inputs then the remaining outputs have a CEC adapter
+that is disabled and reports an invalid physical address.
+
+
+Section 16: Some Future Improvements
 ------------------------------------
 
 Just as a reminder and in no particular order:
@@ -1121,8 +1148,6 @@ Just as a reminder and in no particular order:
 - Fix sequence/field numbering when looping of video with alternate fields
 - Add support for V4L2_CID_BG_COLOR for video outputs
 - Add ARGB888 overlay support: better testing of the alpha channel
-- Add custom DV timings support
-- Add support for V4L2_DV_FL_REDUCED_FPS
 - Improve pixel aspect support in the tpg code by passing a real v4l2_fract
 - Use per-queue locks and/or per-device locks to improve throughput
 - Add support to loop from a specific output to a specific input across
@@ -1133,3 +1158,4 @@ Just as a reminder and in no particular order:
 - Make a thread for the RDS generation, that would help in particular for the
   "Controls" RDS Rx I/O Mode as the read-only RDS controls could be updated
   in real-time.
+- Changing the EDID should cause hotplug detect emulation to happen.
diff --git a/drivers/media/platform/vivid/Kconfig b/drivers/media/platform/vivid/Kconfig
index f535f57..20c5eea 100644
--- a/drivers/media/platform/vivid/Kconfig
+++ b/drivers/media/platform/vivid/Kconfig
@@ -6,6 +6,7 @@ config VIDEO_VIVID
 	select FB_CFB_FILLRECT
 	select FB_CFB_COPYAREA
 	select FB_CFB_IMAGEBLIT
+	select MEDIA_CEC_EDID
 	select VIDEOBUF2_VMALLOC
 	select VIDEO_V4L2_TPG
 	default n
@@ -22,6 +23,14 @@ config VIDEO_VIVID
 	  Say Y here if you want to test video apps or debug V4L devices.
 	  When in doubt, say N.
 
+config VIDEO_VIVID_CEC
+	bool "Enable CEC emulation support"
+	depends on VIDEO_VIVID && MEDIA_CEC
+	default y
+	---help---
+	  When selected the vivid module will emulate the optional
+	  HDMI CEC feature.
+
 config VIDEO_VIVID_MAX_DEVS
 	int "Maximum number of devices"
 	depends on VIDEO_VIVID
diff --git a/drivers/media/platform/vivid/Makefile b/drivers/media/platform/vivid/Makefile
index 633c8a1b..2973881 100644
--- a/drivers/media/platform/vivid/Makefile
+++ b/drivers/media/platform/vivid/Makefile
@@ -3,4 +3,8 @@ vivid-objs := vivid-core.o vivid-ctrls.o vivid-vid-common.o vivid-vbi-gen.o \
 		vivid-radio-rx.o vivid-radio-tx.o vivid-radio-common.o \
 		vivid-rds-gen.o vivid-sdr-cap.o vivid-vbi-cap.o vivid-vbi-out.o \
 		vivid-osd.o
+ifeq ($(CONFIG_VIDEO_VIVID_CEC),y)
+  vivid-objs += vivid-cec.o
+endif
+
 obj-$(CONFIG_VIDEO_VIVID) += vivid.o
diff --git a/drivers/media/platform/vivid/vivid-cec.c b/drivers/media/platform/vivid/vivid-cec.c
new file mode 100644
index 0000000..2ad7f06
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-cec.c
@@ -0,0 +1,254 @@
+/*
+ * vivid-core.c - A Virtual Video Test Driver, core initialization
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <media/cec.h>
+
+#include "vivid-core.h"
+#include "vivid-cec.h"
+
+void vivid_cec_bus_free_work(struct vivid_dev *dev)
+{
+	spin_lock(&dev->cec_slock);
+	while (!list_empty(&dev->cec_work_list)) {
+		struct vivid_cec_work *cw =
+			list_first_entry(&dev->cec_work_list,
+					 struct vivid_cec_work, list);
+
+		spin_unlock(&dev->cec_slock);
+		cancel_delayed_work_sync(&cw->work);
+		spin_lock(&dev->cec_slock);
+		list_del(&cw->list);
+		cec_transmit_done(cw->adap, CEC_TX_STATUS_LOW_DRIVE, 0, 0, 1, 0);
+		kfree(cw);
+	}
+	spin_unlock(&dev->cec_slock);
+}
+
+static struct cec_adapter *vivid_cec_find_dest_adap(struct vivid_dev *dev,
+						    struct cec_adapter *adap,
+						    u8 dest)
+{
+	unsigned i;
+
+	if (dest >= 0xf)
+		return NULL;
+
+	if (adap != dev->cec_rx_adap && dev->cec_rx_adap &&
+	    dev->cec_rx_adap->is_configured &&
+	    cec_has_log_addr(dev->cec_rx_adap, dest))
+		return dev->cec_rx_adap;
+
+	for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++) {
+		if (adap == dev->cec_tx_adap[i])
+			continue;
+		if (!dev->cec_tx_adap[i]->is_configured)
+			continue;
+		if (cec_has_log_addr(dev->cec_tx_adap[i], dest))
+			return dev->cec_tx_adap[i];
+	}
+	return NULL;
+}
+
+static void vivid_cec_xfer_done_worker(struct work_struct *work)
+{
+	struct vivid_cec_work *cw =
+		container_of(work, struct vivid_cec_work, work.work);
+	struct vivid_dev *dev = cw->dev;
+	struct cec_adapter *adap = cw->adap;
+	bool is_poll = cw->msg.len == 1;
+	u8 dest = cec_msg_destination(&cw->msg);
+	struct cec_adapter *dest_adap = NULL;
+	bool valid_dest;
+	unsigned i;
+
+	valid_dest = cec_msg_is_broadcast(&cw->msg);
+	if (!valid_dest) {
+		dest_adap = vivid_cec_find_dest_adap(dev, adap, dest);
+		if (dest_adap)
+			valid_dest = true;
+	}
+	cw->tx_status = valid_dest ? CEC_TX_STATUS_OK : CEC_TX_STATUS_NACK;
+	spin_lock(&dev->cec_slock);
+	dev->cec_xfer_time_jiffies = 0;
+	dev->cec_xfer_start_jiffies = 0;
+	list_del(&cw->list);
+	spin_unlock(&dev->cec_slock);
+	cec_transmit_done(cw->adap, cw->tx_status, 0, valid_dest ? 0 : 1, 0, 0);
+
+	if (!is_poll && dest_adap) {
+		/* Directed message */
+		cec_received_msg(dest_adap, &cw->msg);
+	} else if (!is_poll && valid_dest) {
+		/* Broadcast message */
+		if (adap != dev->cec_rx_adap && dev->cec_rx_adap->log_addrs.log_addr_mask)
+			cec_received_msg(dev->cec_rx_adap, &cw->msg);
+		for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++) {
+			if (adap == dev->cec_tx_adap[i] ||
+			    !dev->cec_tx_adap[i]->log_addrs.log_addr_mask)
+				continue;
+			cec_received_msg(dev->cec_tx_adap[i], &cw->msg);
+		}
+	}
+	kfree(cw);
+}
+
+static void vivid_cec_xfer_try_worker(struct work_struct *work)
+{
+	struct vivid_cec_work *cw =
+		container_of(work, struct vivid_cec_work, work.work);
+	struct vivid_dev *dev = cw->dev;
+
+	spin_lock(&dev->cec_slock);
+	if (dev->cec_xfer_time_jiffies) {
+		list_del(&cw->list);
+		spin_unlock(&dev->cec_slock);
+		cec_transmit_done(cw->adap, CEC_TX_STATUS_ARB_LOST, 1, 0, 0, 0);
+		kfree(cw);
+	} else {
+		INIT_DELAYED_WORK(&cw->work, vivid_cec_xfer_done_worker);
+		dev->cec_xfer_start_jiffies = jiffies;
+		dev->cec_xfer_time_jiffies = usecs_to_jiffies(cw->usecs);
+		spin_unlock(&dev->cec_slock);
+		schedule_delayed_work(&cw->work, dev->cec_xfer_time_jiffies);
+	}
+}
+
+static int vivid_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+	return 0;
+}
+
+static int vivid_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
+{
+	return 0;
+}
+
+/*
+ * One data bit takes 2400 us, each byte needs 10 bits so that's 24000 us
+ * per byte.
+ */
+#define USECS_PER_BYTE 24000
+
+static int vivid_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
+				   u32 signal_free_time, struct cec_msg *msg)
+{
+	struct vivid_dev *dev = adap->priv;
+	struct vivid_cec_work *cw = kzalloc(sizeof(*cw), GFP_KERNEL);
+	long delta_jiffies = 0;
+
+	if (cw == NULL)
+		return -ENOMEM;
+	cw->dev = dev;
+	cw->adap = adap;
+	cw->usecs = CEC_FREE_TIME_TO_USEC(signal_free_time) +
+		    msg->len * USECS_PER_BYTE;
+	cw->msg = *msg;
+
+	spin_lock(&dev->cec_slock);
+	list_add(&cw->list, &dev->cec_work_list);
+	if (dev->cec_xfer_time_jiffies == 0) {
+		INIT_DELAYED_WORK(&cw->work, vivid_cec_xfer_done_worker);
+		dev->cec_xfer_start_jiffies = jiffies;
+		dev->cec_xfer_time_jiffies = usecs_to_jiffies(cw->usecs);
+		delta_jiffies = dev->cec_xfer_time_jiffies;
+	} else {
+		INIT_DELAYED_WORK(&cw->work, vivid_cec_xfer_try_worker);
+		delta_jiffies = dev->cec_xfer_start_jiffies +
+			dev->cec_xfer_time_jiffies - jiffies;
+	}
+	spin_unlock(&dev->cec_slock);
+	schedule_delayed_work(&cw->work, delta_jiffies < 0 ? 0 : delta_jiffies);
+	return 0;
+}
+
+static int vivid_received(struct cec_adapter *adap, struct cec_msg *msg)
+{
+	struct vivid_dev *dev = adap->priv;
+	struct cec_msg reply;
+	u8 dest = cec_msg_destination(msg);
+	u16 pa;
+	u8 disp_ctl;
+	char osd[14];
+
+	if (cec_msg_is_broadcast(msg))
+		dest = adap->log_addrs.log_addr[0];
+	cec_msg_init(&reply, dest, cec_msg_initiator(msg));
+
+	switch (cec_msg_opcode(msg)) {
+	case CEC_MSG_SET_STREAM_PATH:
+		if (cec_is_sink(adap))
+			return -ENOMSG;
+		cec_ops_set_stream_path(msg, &pa);
+		if (pa != adap->phys_addr)
+			return -ENOMSG;
+		cec_msg_active_source(&reply, adap->phys_addr);
+		cec_transmit_msg(adap, &reply, false);
+		break;
+	case CEC_MSG_SET_OSD_STRING:
+		if (!cec_is_sink(adap))
+			return -ENOMSG;
+		cec_ops_set_osd_string(msg, &disp_ctl, osd);
+		switch (disp_ctl) {
+		case CEC_OP_DISP_CTL_DEFAULT:
+			strcpy(dev->osd, osd);
+			dev->osd_jiffies = jiffies;
+			break;
+		case CEC_OP_DISP_CTL_UNTIL_CLEARED:
+			strcpy(dev->osd, osd);
+			dev->osd_jiffies = 0;
+			break;
+		case CEC_OP_DISP_CTL_CLEAR:
+			dev->osd[0] = 0;
+			dev->osd_jiffies = 0;
+			break;
+		default:
+			cec_msg_feature_abort(&reply, cec_msg_opcode(msg),
+					      CEC_OP_ABORT_INVALID_OP);
+			cec_transmit_msg(adap, &reply, false);
+			break;
+		}
+		break;
+	default:
+		return -ENOMSG;
+	}
+	return 0;
+}
+
+static const struct cec_adap_ops vivid_cec_adap_ops = {
+	.adap_enable = vivid_cec_adap_enable,
+	.adap_log_addr = vivid_cec_adap_log_addr,
+	.adap_transmit = vivid_cec_adap_transmit,
+	.received = vivid_received,
+};
+
+struct cec_adapter *vivid_cec_alloc_adap(struct vivid_dev *dev,
+					 unsigned idx,
+					 struct device *parent,
+					 bool is_source)
+{
+	char name[sizeof(dev->vid_out_dev.name) + 2];
+	u32 caps = CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS |
+		CEC_CAP_PASSTHROUGH | CEC_CAP_RC;
+
+	snprintf(name, sizeof(name), "%s%d",
+		 is_source ? dev->vid_out_dev.name : dev->vid_cap_dev.name,
+		 idx);
+	return cec_allocate_adapter(&vivid_cec_adap_ops, dev,
+		name, caps, 1, parent);
+}
diff --git a/drivers/media/platform/vivid/vivid-cec.h b/drivers/media/platform/vivid/vivid-cec.h
new file mode 100644
index 0000000..4ef21d2
--- /dev/null
+++ b/drivers/media/platform/vivid/vivid-cec.h
@@ -0,0 +1,33 @@
+/*
+ * vivid-cec.h - A Virtual Video Test Driver, cec emulation
+ *
+ * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifdef CONFIG_VIDEO_VIVID_CEC
+struct cec_adapter *vivid_cec_alloc_adap(struct vivid_dev *dev,
+					 unsigned idx,
+					 struct device *parent,
+					 bool is_source);
+void vivid_cec_bus_free_work(struct vivid_dev *dev);
+
+#else
+
+static inline void vivid_cec_bus_free_work(struct vivid_dev *dev)
+{
+}
+
+#endif
diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c
index c14da84..7fef81c 100644
--- a/drivers/media/platform/vivid/vivid-core.c
+++ b/drivers/media/platform/vivid/vivid-core.c
@@ -46,6 +46,7 @@
 #include "vivid-vbi-cap.h"
 #include "vivid-vbi-out.h"
 #include "vivid-osd.h"
+#include "vivid-cec.h"
 #include "vivid-ctrls.h"
 
 #define VIVID_MODULE_NAME "vivid"
@@ -684,6 +685,11 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
 		dev->input_name_counter[i] = in_type_counter[dev->input_type[i]]++;
 	}
 	dev->has_audio_inputs = in_type_counter[TV] && in_type_counter[SVID];
+	if (in_type_counter[HDMI] == 16) {
+		/* The CEC physical address only allows for max 15 inputs */
+		in_type_counter[HDMI]--;
+		dev->num_inputs--;
+	}
 
 	/* how many outputs do we have and of what type? */
 	dev->num_outputs = num_outputs[inst];
@@ -696,6 +702,15 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
 		dev->output_name_counter[i] = out_type_counter[dev->output_type[i]]++;
 	}
 	dev->has_audio_outputs = out_type_counter[SVID];
+	if (out_type_counter[HDMI] == 16) {
+		/*
+		 * The CEC physical address only allows for max 15 inputs,
+		 * so outputs are also limited to 15 to allow for easy
+		 * CEC output to input mapping.
+		 */
+		out_type_counter[HDMI]--;
+		dev->num_outputs--;
+	}
 
 	/* do we create a video capture device? */
 	dev->has_vid_cap = node_type & 0x0001;
@@ -1010,6 +1025,17 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
 	INIT_LIST_HEAD(&dev->vbi_out_active);
 	INIT_LIST_HEAD(&dev->sdr_cap_active);
 
+	INIT_LIST_HEAD(&dev->cec_work_list);
+	spin_lock_init(&dev->cec_slock);
+	/*
+	 * Same as create_singlethread_workqueue, but now I can use the
+	 * string formatting of alloc_ordered_workqueue.
+	 */
+	dev->cec_workqueue =
+		alloc_ordered_workqueue("vivid-%03d-cec", WQ_MEM_RECLAIM, inst);
+	if (!dev->cec_workqueue)
+		goto unreg_dev;
+
 	/* start creating the vb2 queues */
 	if (dev->has_vid_cap) {
 		/* initialize vid_cap queue */
@@ -1117,7 +1143,8 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
 	/* finally start creating the device nodes */
 	if (dev->has_vid_cap) {
 		vfd = &dev->vid_cap_dev;
-		strlcpy(vfd->name, "vivid-vid-cap", sizeof(vfd->name));
+		snprintf(vfd->name, sizeof(vfd->name),
+			 "vivid-%03d-vid-cap", inst);
 		vfd->fops = &vivid_fops;
 		vfd->ioctl_ops = &vivid_ioctl_ops;
 		vfd->device_caps = dev->vid_cap_caps;
@@ -1133,6 +1160,27 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
 		vfd->lock = &dev->mutex;
 		video_set_drvdata(vfd, dev);
 
+#ifdef CONFIG_VIDEO_VIVID_CEC
+		if (in_type_counter[HDMI]) {
+			struct cec_adapter *adap;
+
+			adap = vivid_cec_alloc_adap(dev, 0, &pdev->dev, false);
+			ret = PTR_ERR_OR_ZERO(adap);
+			if (ret < 0)
+				goto unreg_dev;
+			dev->cec_rx_adap = adap;
+			ret = cec_register_adapter(adap);
+			if (ret < 0) {
+				cec_delete_adapter(adap);
+				dev->cec_rx_adap = NULL;
+				goto unreg_dev;
+			}
+			cec_s_phys_addr(adap, 0, false);
+			v4l2_info(&dev->v4l2_dev, "CEC adapter %s registered for HDMI input %d\n",
+				  dev_name(&adap->devnode.dev), i);
+		}
+#endif
+
 		ret = video_register_device(vfd, VFL_TYPE_GRABBER, vid_cap_nr[inst]);
 		if (ret < 0)
 			goto unreg_dev;
@@ -1141,8 +1189,13 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
 	}
 
 	if (dev->has_vid_out) {
+#ifdef CONFIG_VIDEO_VIVID_CEC
+		unsigned bus_cnt = 0;
+#endif
+
 		vfd = &dev->vid_out_dev;
-		strlcpy(vfd->name, "vivid-vid-out", sizeof(vfd->name));
+		snprintf(vfd->name, sizeof(vfd->name),
+			 "vivid-%03d-vid-out", inst);
 		vfd->vfl_dir = VFL_DIR_TX;
 		vfd->fops = &vivid_fops;
 		vfd->ioctl_ops = &vivid_ioctl_ops;
@@ -1159,6 +1212,36 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
 		vfd->lock = &dev->mutex;
 		video_set_drvdata(vfd, dev);
 
+#ifdef CONFIG_VIDEO_VIVID_CEC
+		for (i = 0; i < dev->num_outputs; i++) {
+			struct cec_adapter *adap;
+
+			if (dev->output_type[i] != HDMI)
+				continue;
+			dev->cec_output2bus_map[i] = bus_cnt;
+			adap = vivid_cec_alloc_adap(dev, bus_cnt,
+						     &pdev->dev, true);
+			ret = PTR_ERR_OR_ZERO(adap);
+			if (ret < 0)
+				goto unreg_dev;
+			dev->cec_tx_adap[bus_cnt] = adap;
+			ret = cec_register_adapter(adap);
+			if (ret < 0) {
+				cec_delete_adapter(adap);
+				dev->cec_tx_adap[bus_cnt] = NULL;
+				goto unreg_dev;
+			}
+			bus_cnt++;
+			if (bus_cnt <= in_type_counter[HDMI]) {
+				cec_s_phys_addr(adap, bus_cnt << 12, false);
+			} else {
+				cec_s_phys_addr(adap, 0x1000, false);
+			}
+			v4l2_info(&dev->v4l2_dev, "CEC adapter %s registered for HDMI output %d\n",
+				  dev_name(&adap->devnode.dev), i);
+		}
+#endif
+
 		ret = video_register_device(vfd, VFL_TYPE_GRABBER, vid_out_nr[inst]);
 		if (ret < 0)
 			goto unreg_dev;
@@ -1168,7 +1251,8 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
 
 	if (dev->has_vbi_cap) {
 		vfd = &dev->vbi_cap_dev;
-		strlcpy(vfd->name, "vivid-vbi-cap", sizeof(vfd->name));
+		snprintf(vfd->name, sizeof(vfd->name),
+			 "vivid-%03d-vbi-cap", inst);
 		vfd->fops = &vivid_fops;
 		vfd->ioctl_ops = &vivid_ioctl_ops;
 		vfd->device_caps = dev->vbi_cap_caps;
@@ -1191,7 +1275,8 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
 
 	if (dev->has_vbi_out) {
 		vfd = &dev->vbi_out_dev;
-		strlcpy(vfd->name, "vivid-vbi-out", sizeof(vfd->name));
+		snprintf(vfd->name, sizeof(vfd->name),
+			 "vivid-%03d-vbi-out", inst);
 		vfd->vfl_dir = VFL_DIR_TX;
 		vfd->fops = &vivid_fops;
 		vfd->ioctl_ops = &vivid_ioctl_ops;
@@ -1215,7 +1300,8 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
 
 	if (dev->has_sdr_cap) {
 		vfd = &dev->sdr_cap_dev;
-		strlcpy(vfd->name, "vivid-sdr-cap", sizeof(vfd->name));
+		snprintf(vfd->name, sizeof(vfd->name),
+			 "vivid-%03d-sdr-cap", inst);
 		vfd->fops = &vivid_fops;
 		vfd->ioctl_ops = &vivid_ioctl_ops;
 		vfd->device_caps = dev->sdr_cap_caps;
@@ -1234,7 +1320,8 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
 
 	if (dev->has_radio_rx) {
 		vfd = &dev->radio_rx_dev;
-		strlcpy(vfd->name, "vivid-rad-rx", sizeof(vfd->name));
+		snprintf(vfd->name, sizeof(vfd->name),
+			 "vivid-%03d-rad-rx", inst);
 		vfd->fops = &vivid_radio_fops;
 		vfd->ioctl_ops = &vivid_ioctl_ops;
 		vfd->device_caps = dev->radio_rx_caps;
@@ -1252,7 +1339,8 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
 
 	if (dev->has_radio_tx) {
 		vfd = &dev->radio_tx_dev;
-		strlcpy(vfd->name, "vivid-rad-tx", sizeof(vfd->name));
+		snprintf(vfd->name, sizeof(vfd->name),
+			 "vivid-%03d-rad-tx", inst);
 		vfd->vfl_dir = VFL_DIR_TX;
 		vfd->fops = &vivid_radio_fops;
 		vfd->ioctl_ops = &vivid_ioctl_ops;
@@ -1282,6 +1370,13 @@ unreg_dev:
 	video_unregister_device(&dev->vbi_cap_dev);
 	video_unregister_device(&dev->vid_out_dev);
 	video_unregister_device(&dev->vid_cap_dev);
+	cec_unregister_adapter(dev->cec_rx_adap);
+	for (i = 0; i < MAX_OUTPUTS; i++)
+		cec_unregister_adapter(dev->cec_tx_adap[i]);
+	if (dev->cec_workqueue) {
+		vivid_cec_bus_free_work(dev);
+		destroy_workqueue(dev->cec_workqueue);
+	}
 free_dev:
 	v4l2_device_put(&dev->v4l2_dev);
 	return ret;
@@ -1331,8 +1426,7 @@ static int vivid_probe(struct platform_device *pdev)
 static int vivid_remove(struct platform_device *pdev)
 {
 	struct vivid_dev *dev;
-	unsigned i;
-
+	unsigned i, j;
 
 	for (i = 0; i < n_devs; i++) {
 		dev = vivid_devs[i];
@@ -1380,6 +1474,13 @@ static int vivid_remove(struct platform_device *pdev)
 			unregister_framebuffer(&dev->fb_info);
 			vivid_fb_release_buffers(dev);
 		}
+		cec_unregister_adapter(dev->cec_rx_adap);
+		for (j = 0; j < MAX_OUTPUTS; j++)
+			cec_unregister_adapter(dev->cec_tx_adap[j]);
+		if (dev->cec_workqueue) {
+			vivid_cec_bus_free_work(dev);
+			destroy_workqueue(dev->cec_workqueue);
+		}
 		v4l2_device_put(&dev->v4l2_dev);
 		vivid_devs[i] = NULL;
 	}
diff --git a/drivers/media/platform/vivid/vivid-core.h b/drivers/media/platform/vivid/vivid-core.h
index 776783b..9f4258b 100644
--- a/drivers/media/platform/vivid/vivid-core.h
+++ b/drivers/media/platform/vivid/vivid-core.h
@@ -21,6 +21,8 @@
 #define _VIVID_CORE_H_
 
 #include <linux/fb.h>
+#include <linux/workqueue.h>
+#include <media/cec.h>
 #include <media/videobuf2-v4l2.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-dev.h>
@@ -132,6 +134,17 @@ enum vivid_colorspace {
 #define VIVID_INVALID_SIGNAL(mode) \
 	((mode) == NO_SIGNAL || (mode) == NO_LOCK || (mode) == OUT_OF_RANGE)
 
+struct vivid_cec_work {
+	struct list_head	list;
+	struct delayed_work	work;
+	struct cec_adapter	*adap;
+	struct vivid_dev	*dev;
+	unsigned		usecs;
+	unsigned		timeout_ms;
+	u8			tx_status;
+	struct cec_msg		msg;
+};
+
 struct vivid_dev {
 	unsigned			inst;
 	struct v4l2_device		v4l2_dev;
@@ -497,6 +510,20 @@ struct vivid_dev {
 	/* Shared between radio receiver and transmitter */
 	bool				radio_rds_loop;
 	struct timespec			radio_rds_init_ts;
+
+	/* CEC */
+	struct cec_adapter		*cec_rx_adap;
+	struct cec_adapter		*cec_tx_adap[MAX_OUTPUTS];
+	struct workqueue_struct		*cec_workqueue;
+	spinlock_t			cec_slock;
+	struct list_head		cec_work_list;
+	unsigned			cec_xfer_time_jiffies;
+	unsigned long			cec_xfer_start_jiffies;
+	u8				cec_output2bus_map[MAX_OUTPUTS];
+
+	/* CEC OSD String */
+	char				osd[14];
+	unsigned long			osd_jiffies;
 };
 
 static inline bool vivid_is_webcam(const struct vivid_dev *dev)
diff --git a/drivers/media/platform/vivid/vivid-kthread-cap.c b/drivers/media/platform/vivid/vivid-kthread-cap.c
index 3b8c101..efba955 100644
--- a/drivers/media/platform/vivid/vivid-kthread-cap.c
+++ b/drivers/media/platform/vivid/vivid-kthread-cap.c
@@ -552,6 +552,17 @@ static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf)
 			snprintf(str, sizeof(str), " button pressed!");
 			tpg_gen_text(tpg, basep, line++ * line_height, 16, str);
 		}
+		if (dev->osd[0]) {
+			if (vivid_is_hdmi_cap(dev)) {
+				snprintf(str, sizeof(str), " OSD \"%s\"", dev->osd);
+				tpg_gen_text(tpg, basep, line++ * line_height, 16, str);
+			}
+			if (dev->osd_jiffies &&
+			    time_is_before_jiffies(dev->osd_jiffies + 5 * HZ)) {
+				dev->osd[0] = 0;
+				dev->osd_jiffies = 0;
+			}
+		}
 	}
 
 	/*
diff --git a/drivers/media/platform/vivid/vivid-vid-cap.c b/drivers/media/platform/vivid/vivid-vid-cap.c
index 4f730f3..232c2e3 100644
--- a/drivers/media/platform/vivid/vivid-vid-cap.c
+++ b/drivers/media/platform/vivid/vivid-vid-cap.c
@@ -1701,6 +1701,9 @@ int vidioc_s_edid(struct file *file, void *_fh,
 			 struct v4l2_edid *edid)
 {
 	struct vivid_dev *dev = video_drvdata(file);
+	u16 phys_addr;
+	unsigned i;
+	int ret;
 
 	memset(edid->reserved, 0, sizeof(edid->reserved));
 	if (edid->pad >= dev->num_inputs)
@@ -1709,14 +1712,32 @@ int vidioc_s_edid(struct file *file, void *_fh,
 		return -EINVAL;
 	if (edid->blocks == 0) {
 		dev->edid_blocks = 0;
-		return 0;
+		phys_addr = CEC_PHYS_ADDR_INVALID;
+		goto set_phys_addr;
 	}
 	if (edid->blocks > dev->edid_max_blocks) {
 		edid->blocks = dev->edid_max_blocks;
 		return -E2BIG;
 	}
+	phys_addr = cec_get_edid_phys_addr(edid->edid, edid->blocks * 128, NULL);
+	ret = cec_phys_addr_validate(phys_addr, &phys_addr, NULL);
+	if (ret)
+		return ret;
+
+	if (vb2_is_busy(&dev->vb_vid_cap_q))
+		return -EBUSY;
+
 	dev->edid_blocks = edid->blocks;
 	memcpy(dev->edid, edid->edid, edid->blocks * 128);
+
+set_phys_addr:
+	/* TODO: a proper hotplug detect cycle should be emulated here */
+	cec_s_phys_addr(dev->cec_rx_adap, phys_addr, false);
+
+	for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++)
+		cec_s_phys_addr(dev->cec_tx_adap[i],
+				cec_phys_addr_for_input(phys_addr, i + 1),
+				false);
 	return 0;
 }
 
diff --git a/drivers/media/platform/vivid/vivid-vid-common.c b/drivers/media/platform/vivid/vivid-vid-common.c
index 39ea228..b0eb14a 100644
--- a/drivers/media/platform/vivid/vivid-vid-common.c
+++ b/drivers/media/platform/vivid/vivid-vid-common.c
@@ -811,6 +811,7 @@ int vidioc_g_edid(struct file *file, void *_fh,
 {
 	struct vivid_dev *dev = video_drvdata(file);
 	struct video_device *vdev = video_devdata(file);
+	struct cec_adapter *adap;
 
 	memset(edid->reserved, 0, sizeof(edid->reserved));
 	if (vdev->vfl_dir == VFL_DIR_RX) {
@@ -818,11 +819,16 @@ int vidioc_g_edid(struct file *file, void *_fh,
 			return -EINVAL;
 		if (dev->input_type[edid->pad] != HDMI)
 			return -EINVAL;
+		adap = dev->cec_rx_adap;
 	} else {
+		unsigned bus_idx;
+
 		if (edid->pad >= dev->num_outputs)
 			return -EINVAL;
 		if (dev->output_type[edid->pad] != HDMI)
 			return -EINVAL;
+		bus_idx = dev->cec_output2bus_map[edid->pad];
+		adap = dev->cec_tx_adap[bus_idx];
 	}
 	if (edid->start_block == 0 && edid->blocks == 0) {
 		edid->blocks = dev->edid_blocks;
@@ -835,5 +841,6 @@ int vidioc_g_edid(struct file *file, void *_fh,
 	if (edid->start_block + edid->blocks > dev->edid_blocks)
 		edid->blocks = dev->edid_blocks - edid->start_block;
 	memcpy(edid->edid, dev->edid, edid->blocks * 128);
+	cec_set_edid_phys_addr(edid->edid, edid->blocks * 128, adap->phys_addr);
 	return 0;
 }
-- 
2.8.1


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

* Re: [PATCHv16 04/13] cec: add HDMI CEC framework
  2016-04-29 13:52 ` [PATCHv16 04/13] cec: add HDMI CEC framework Hans Verkuil
@ 2016-06-16 16:00   ` Mauro Carvalho Chehab
  2016-06-25 12:27     ` Hans Verkuil
  0 siblings, 1 reply; 35+ messages in thread
From: Mauro Carvalho Chehab @ 2016-06-16 16:00 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: linux-media, dri-devel, linux-samsung-soc, linux-input, lars,
	linux, Hans Verkuil, Kamil Debski

Em Fri, 29 Apr 2016 15:52:19 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hansverk@cisco.com>
> 
> The added HDMI CEC framework provides a generic kernel interface for
> HDMI CEC devices.
> 
> Besides the cec module itself it also adds a cec-edid module that
> contains helper functions to find and manipulate the CEC physical
> address inside an EDID. Even if the CEC support itself is disabled,
> drivers will still need these functions.
> 
> Note that the CEC framework is added to staging/media and that the
> cec.h and cec-funcs.h headers are not exported yet. While the kABI
> is mature, I would prefer to allow the uABI some more time before
> it is mainlined in case it needs more tweaks.

As pointed via IRC, it sounds like checkpatch were never used
on this patch series. Just this one got more than 100 errors/warnings:

total: 6 errors, 107 warnings, 5895 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
      mechanically convert to the typical style using --fix or --fix-inplace.

Please fix, except if you have a good reason why not follow the
CodingStyle.

> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> [k.debski@samsung.com: Merged CEC Updates commit by Hans Verkuil]
> [k.debski@samsung.com: Merged Update author commit by Hans Verkuil]
> [k.debski@samsung.com: change kthread handling when setting logical
> address]
> [k.debski@samsung.com: code cleanup and fixes]
> [k.debski@samsung.com: add missing CEC commands to match spec]
> [k.debski@samsung.com: add RC framework support]
> [k.debski@samsung.com: move and edit documentation]
> [k.debski@samsung.com: add vendor id reporting]
> [k.debski@samsung.com: add possibility to clear assigned logical
> addresses]
> [k.debski@samsung.com: documentation fixes, clenaup and expansion]
> [k.debski@samsung.com: reorder of API structs and add reserved fields]
> [k.debski@samsung.com: fix handling of events and fix 32/64bit timespec
> problem]
> [k.debski@samsung.com: add sequence number handling]
> [k.debski@samsung.com: add passthrough mode]
> [k.debski@samsung.com: fix CEC defines, add missing CEC 2.0 commands]
> minor additions]
> Signed-off-by: Kamil Debski <kamil@wypas.org>
> ---
>  MAINTAINERS                        |   16 +
>  drivers/media/Kconfig              |    3 +
>  drivers/media/Makefile             |    2 +
>  drivers/media/cec-edid.c           |  139 ++
>  drivers/staging/media/Kconfig      |    2 +
>  drivers/staging/media/Makefile     |    1 +
>  drivers/staging/media/cec/Kconfig  |    8 +
>  drivers/staging/media/cec/Makefile |    1 +
>  drivers/staging/media/cec/cec.c    | 2481 ++++++++++++++++++++++++++++++++++++
>  include/linux/cec-funcs.h          | 1871 +++++++++++++++++++++++++++
>  include/linux/cec.h                |  985 ++++++++++++++
>  include/media/cec-edid.h           |  103 ++
>  include/media/cec.h                |  236 ++++
>  13 files changed, 5848 insertions(+)
>  create mode 100644 drivers/media/cec-edid.c
>  create mode 100644 drivers/staging/media/cec/Kconfig
>  create mode 100644 drivers/staging/media/cec/Makefile
>  create mode 100644 drivers/staging/media/cec/cec.c
>  create mode 100644 include/linux/cec-funcs.h
>  create mode 100644 include/linux/cec.h
>  create mode 100644 include/media/cec-edid.h
>  create mode 100644 include/media/cec.h
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index bfcb7ea..83bd865 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -2760,6 +2760,22 @@ 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 <hans.verkuil@cisco.com>
> +L:	linux-media@vger.kernel.org
> +T:	git git://linuxtv.org/media_tree.git
> +W:	http://linuxtv.org
> +S:	Supported

> +F:	Documentation/cec.txt
> +F:	Documentation/DocBook/media/v4l/cec*

Not sure about the above. We're in the process of changing the
documentation engines at the Kernel, and the docs location will
likely change soon enough.

> +F:	drivers/staging/media/cec/cec.c
> +F:	drivers/media/cec-edid.c
> +F:	drivers/media/rc/keymaps/rc-cec.c
> +F:	include/media/cec.h
> +F:	include/media/cec-edid.h
> +F:	include/linux/cec.h
> +F:	include/linux/cec-funcs.h
> +
>  CELL BROADBAND ENGINE ARCHITECTURE
>  M:	Arnd Bergmann <arnd@arndb.de>
>  L:	linuxppc-dev@lists.ozlabs.org
> diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
> index a8518fb..052dcf7 100644
> --- a/drivers/media/Kconfig
> +++ b/drivers/media/Kconfig
> @@ -80,6 +80,9 @@ config MEDIA_RC_SUPPORT
>  
>  	  Say Y when you have a TV or an IR device.
>  
> +config MEDIA_CEC_EDID
> +	tristate
> +
>  #
>  # Media controller
>  #	Selectable only for webcam/grabbers, as other drivers don't use it
> diff --git a/drivers/media/Makefile b/drivers/media/Makefile
> index e608bbc..b56f013 100644
> --- a/drivers/media/Makefile
> +++ b/drivers/media/Makefile
> @@ -2,6 +2,8 @@
>  # Makefile for the kernel multimedia device drivers.
>  #
>  
> +obj-$(CONFIG_MEDIA_CEC_EDID) += cec-edid.o
> +
>  media-objs	:= media-device.o media-devnode.o media-entity.o
>  
>  #
> diff --git a/drivers/media/cec-edid.c b/drivers/media/cec-edid.c
> new file mode 100644
> index 0000000..50202d8
> --- /dev/null
> +++ b/drivers/media/cec-edid.c
> @@ -0,0 +1,139 @@
> +/*
> + * cec-edid - HDMI Consumer Electronics Control EDID & CEC helper functions
> + *
> + * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
> + *
> + * This program is free software; you may redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 2 of the License.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
> + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
> + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
> + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> + * SOFTWARE.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/types.h>
> +#include <linux/cec.h>
> +#include <media/cec-edid.h>
> +
> +static unsigned cec_get_edid_spa_location(const u8 *edid, unsigned size)
> +{
> +	u8 d;
> +
> +	if (size < 256)
> +		return 0;
> +
> +	if (edid[0x7e] != 1 || edid[0x80] != 0x02 || edid[0x81] != 0x03)
> +		return 0;

Hmm... looking at E-EDID spec:
	http://read.pudn.com/downloads110/ebook/456020/E-EDID%20Standard.pdf

you're expecting that EDID will have just *one* extension? If so, please
state so, adding a FIXME to warn about future needs to change the code
to support bigger EDID data. Also, in such case, the EDID size should
always be 256.

I would also check the checksum, to be sure that the vendor specific
data block is not pointing to some invalid location.

> +
> +	/* 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);
> +	}

It looked weird that check for size at the beginning, since the size
is not being used in the routine itself. Ok, after looking at the
code 3 or 4 times, I understood that you're implicitly using the
size at the "end" variable, as you're doing a loop between
	'd', with ranges from 0 to 0x7f
	'end', with ranges from 0x85 to 0xff.
	'i', ranging from 0x84 to 0xfe.
	return value, ranging from 0x88 to 0x102 (i + 4)

As the code seems to rely on having just one extension, allowing to go
past 0xff seems a mistake.

I guess the loop should be checking if it won't return a value
past of the EDID array.

> +	return 0;
> +}
> +
> +u16 cec_get_edid_phys_addr(const u8 *edid, unsigned size, unsigned *offset)
> +{
> +	unsigned loc = cec_get_edid_spa_location(edid, size);
> +
> +	if (offset)
> +		*offset = loc;
> +	if (loc == 0)
> +		return CEC_PHYS_ADDR_INVALID;
> +	return (edid[loc] << 8) | edid[loc + 1];

Yeah, if I'm not mistaken, you may be reading past the EDID
array here, if loc > 0xfe, and size = 256.

> +}
> +EXPORT_SYMBOL_GPL(cec_get_edid_phys_addr);
> +
> +void cec_set_edid_phys_addr(u8 *edid, unsigned size, u16 phys_addr)
> +{
> +	unsigned loc = cec_get_edid_spa_location(edid, size);
> +	u8 sum = 0;
> +	unsigned i;
> +
> +	if (loc == 0)
> +		return;
> +	edid[loc] = phys_addr >> 8;
> +	edid[loc + 1] = phys_addr & 0xff;
> +	loc &= ~0x7f;
> +
> +	/* update the checksum */
> +	for (i = loc; i < loc + 127; i++)
> +		sum += edid[i];
> +	edid[i] = 256 - sum;

Here, you can also go past the EDID array, as you're not checking the
size inside the for loop.

> +}
> +EXPORT_SYMBOL_GPL(cec_set_edid_phys_addr);
> +
> +u16 cec_phys_addr_for_input(u16 phys_addr, u8 input)
> +{
> +	/* Check if input is sane */
> +	if (WARN_ON(input == 0 || input > 0xf))
> +		return CEC_PHYS_ADDR_INVALID;
> +
> +	if (phys_addr == 0)
> +		return input << 12;
> +
> +	if ((phys_addr & 0x0fff) == 0)
> +		return phys_addr | (input << 8);
> +
> +	if ((phys_addr & 0x00ff) == 0)
> +		return phys_addr | (input << 4);
> +
> +	if ((phys_addr & 0x000f) == 0)
> +		return phys_addr | input;
> +
> +	/*
> +	 * All nibbles are used so no valid physical addresses can be assigned
> +	 * to the input.
> +	 */
> +	return CEC_PHYS_ADDR_INVALID;
> +}
> +EXPORT_SYMBOL_GPL(cec_phys_addr_for_input);
> +
> +int cec_phys_addr_validate(u16 phys_addr, u16 *parent, u16 *port)
> +{
> +	int i;
> +
> +	if (parent)
> +		*parent = phys_addr;
> +	if (port)
> +		*port = 0;
> +	if (phys_addr == CEC_PHYS_ADDR_INVALID)
> +		return 0;
> +	for (i = 0; i < 16; i += 4)
> +		if (phys_addr & (0xf << i))
> +			break;
> +	if (i == 16)
> +		return 0;
> +	if (parent)
> +		*parent = phys_addr & (0xfff0 << i);
> +	if (port)
> +		*port = (phys_addr >> i) & 0xf;
> +	for (i += 4; i < 16; i += 4)
> +		if ((phys_addr & (0xf << i)) == 0)
> +			return -EINVAL;

I guess you should be checking the size of the arrays here too.

> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(cec_phys_addr_validate);
> +
> +MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>");
> +MODULE_DESCRIPTION("CEC EDID helper functions");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig
> index de7e9f5..71554d9 100644
> --- a/drivers/staging/media/Kconfig
> +++ b/drivers/staging/media/Kconfig
> @@ -21,6 +21,8 @@ if STAGING_MEDIA
>  # Please keep them in alphabetic order
>  source "drivers/staging/media/bcm2048/Kconfig"
>  
> +source "drivers/staging/media/cec/Kconfig"
> +
>  source "drivers/staging/media/cxd2099/Kconfig"
>  
>  source "drivers/staging/media/davinci_vpfe/Kconfig"
> diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile
> index 60a35b3..1d6a828 100644
> --- a/drivers/staging/media/Makefile
> +++ b/drivers/staging/media/Makefile
> @@ -1,4 +1,5 @@
>  obj-$(CONFIG_I2C_BCM2048)	+= bcm2048/
> +obj-$(CONFIG_MEDIA_CEC)		+= cec/
>  obj-$(CONFIG_DVB_CXD2099)	+= cxd2099/
>  obj-$(CONFIG_LIRC_STAGING)	+= lirc/
>  obj-$(CONFIG_VIDEO_DM365_VPFE)	+= davinci_vpfe/
> diff --git a/drivers/staging/media/cec/Kconfig b/drivers/staging/media/cec/Kconfig
> new file mode 100644
> index 0000000..3297a54
> --- /dev/null
> +++ b/drivers/staging/media/cec/Kconfig
> @@ -0,0 +1,8 @@
> +config MEDIA_CEC
> +	tristate "CEC API (EXPERIMENTAL)"
> +	select MEDIA_CEC_EDID
> +	---help---
> +	  Enable the CEC API.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called cec.
> diff --git a/drivers/staging/media/cec/Makefile b/drivers/staging/media/cec/Makefile
> new file mode 100644
> index 0000000..7a7532e
> --- /dev/null
> +++ b/drivers/staging/media/cec/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_MEDIA_CEC) += cec.o
> diff --git a/drivers/staging/media/cec/cec.c b/drivers/staging/media/cec/cec.c
> new file mode 100644
> index 0000000..3c5f084
> --- /dev/null
> +++ b/drivers/staging/media/cec/cec.c
> @@ -0,0 +1,2481 @@
> +/*
> + * cec - HDMI Consumer Electronics Control framework
> + *
> + * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
> + *
> + * This program is free software; you may redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 2 of the License.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
> + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
> + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
> + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> + * SOFTWARE.
> + */
> +
> +#include <linux/errno.h>
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/kmod.h>
> +#include <linux/ktime.h>
> +#include <linux/slab.h>
> +#include <linux/mm.h>
> +#include <linux/string.h>
> +#include <linux/types.h>
> +#include <linux/uaccess.h>
> +#include <linux/version.h>
> +#include <media/cec-edid.h>
> +#include <media/cec.h>
> +
> +#define CEC_NUM_DEVICES	256
> +#define CEC_NAME	"cec"
> +
> +static int debug;
> +module_param(debug, int, 0644);
> +MODULE_PARM_DESC(debug, "debug level (0-2)");
> +
> +/*
> + * 400 ms is the time it takes for one 16 byte message to be
> + * transferred and 5 is the maximum number of retries. Add
> + * another 100 ms as a margin. So if the transmit doesn't
> + * finish before that time something is really wrong and we
> + * have to time out.
> + *
> + * This is a sign that something it really wrong and a warning
> + * will be issued.
> + */
> +#define CEC_XFER_TIMEOUT_MS (5 * 400 + 100)
> +
> +#define dprintk(lvl, fmt, arg...)					\
> +	do {								\
> +		if (lvl <= debug)					\
> +			pr_info("cec-%s: " fmt, adap->name, ## arg);	\
> +	} while (0)
> +
> +#define call_op(adap, op, arg...) \
> +	(adap->ops->op ? adap->ops->op(adap, ## arg) : 0)
> +
> +#define call_void_op(adap, op, arg...)			\
> +	do {						\
> +		if (adap->ops->op)			\
> +			adap->ops->op(adap, ## arg);	\
> +	} while (0)
> +
> +static dev_t cec_dev_t;
> +
> +/* Active devices */
> +static DEFINE_MUTEX(cec_devnode_lock);
> +static DECLARE_BITMAP(cec_devnode_nums, CEC_NUM_DEVICES);
> +
> +static struct dentry *top_cec_dir;
> +
> +/* dev to cec_devnode */
> +#define to_cec_devnode(cd) container_of(cd, struct cec_devnode, dev)
> +
> +/* devnode to cec_adapter */
> +#define to_cec_adapter(node) container_of(node, struct cec_adapter, devnode)
> +
> +static inline struct cec_devnode *cec_devnode_data(struct file *filp)
> +{
> +	struct cec_fh *fh = filp->private_data;
> +
> +	return &fh->adap->devnode;
> +}
> +
> +static int cec_log_addr2idx(const struct cec_adapter *adap, u8 log_addr)
> +{
> +	int i;
> +
> +	for (i = 0; i < adap->log_addrs.num_log_addrs; i++)
> +		if (adap->log_addrs.log_addr[i] == log_addr)
> +			return i;
> +	return -1;
> +}
> +
> +static unsigned cec_log_addr2dev(const struct cec_adapter *adap, u8 log_addr)
> +{
> +	int i = cec_log_addr2idx(adap, log_addr);
> +
> +	return adap->log_addrs.primary_device_type[i < 0 ? 0 : i];
> +}
> +
> +/* Initialize the event queues for the filehandle. */
> +static int cec_queue_event_init(struct cec_fh *fh)
> +{
> +	/* This has the size of the event queue for each event type. */
> +	static const unsigned queue_sizes[CEC_NUM_EVENTS] = {
> +		2,	/* CEC_EVENT_STATE_CHANGE */
> +		1,	/* CEC_EVENT_LOST_MSGS */
> +	};

Why didn't you use, instead, a c99 designated initializer at the array?

> +	unsigned i;
> +
> +	for (i = 0; i < CEC_NUM_EVENTS; i++) {
> +		fh->evqueue[i].events = kcalloc(queue_sizes[i],
> +				sizeof(struct cec_event), GFP_KERNEL);
> +		if (fh->evqueue[i].events == NULL) {
> +			while (i--) {
> +				kfree(fh->evqueue[i].events);
> +				fh->evqueue[i].events = NULL;
> +				fh->evqueue[i].elems = 0;
> +			}
> +			return -ENOMEM;
> +		}
> +		fh->evqueue[i].elems = queue_sizes[i];
> +	}
> +	return 0;
> +}
> +
> +static void cec_queue_event_free(struct cec_fh *fh)
> +{
> +	unsigned i;
> +
> +	for (i = 0; i < CEC_NUM_EVENTS; i++)
> +		kfree(fh->evqueue[i].events);
> +}
> +
> +/*
> + * Queue a new event for this filehandle. If ts == 0, then set it
> + * to the current time.
> + */
> +static void cec_queue_event_fh(struct cec_fh *fh,
> +			       const struct cec_event *new_ev, u64 ts)
> +{
> +	struct cec_event_queue *evq = &fh->evqueue[new_ev->event - 1];

Shouldn't it be checking if event > 0 before instantiating event-1?

> +	struct cec_event *ev;
> +
> +	if (ts == 0)
> +		ts = ktime_get_ns();
> +
> +	mutex_lock(&fh->lock);
> +	ev = evq->events + evq->num_events;
> +	/* Overwrite the last event if there is no more room for the new event */
> +	if (evq->num_events == evq->elems) {
> +		ev--;

Wouldn't be better to make it a circular buffer and override the
oldest queued event? 

Also, the loss of an event should be signaled to the users, for them
to know that a problem happened.

> +	} else {
> +		evq->num_events++;
> +		fh->events++;
> +	}
> +	*ev = *new_ev;
> +	ev->ts = ts;
> +	mutex_unlock(&fh->lock);
> +	wake_up_interruptible(&fh->wait);
> +}
> +
> +/* Queue a new event for all open filehandles. */
> +static void cec_queue_event(struct cec_adapter *adap,
> +			    const struct cec_event *ev)
> +{
> +	u64 ts = ktime_get_ns();
> +	struct cec_fh *fh;
> +
> +	mutex_lock(&adap->devnode.fhs_lock);
> +	list_for_each_entry(fh, &adap->devnode.fhs, list)
> +		cec_queue_event_fh(fh, ev, ts);
> +	mutex_unlock(&adap->devnode.fhs_lock);
> +}
> +
> +/*
> + * Queue a new message for this filehandle. If there is no more room
> + * in the queue, then send the LOST_MSGS event instead.
> + */
> +static void cec_queue_msg_fh(struct cec_fh *fh, const struct cec_msg *msg)
> +{
> +	struct cec_event ev_lost_msg = {
> +		.event = CEC_EVENT_LOST_MSGS,
> +	};
> +	struct cec_msg_entry *entry;
> +
> +	mutex_lock(&fh->lock);
> +	if (fh->queued_msgs == CEC_MAX_MSG_QUEUE_SZ)
> +		goto lost_msgs;
> +	entry = kmalloc(sizeof(*entry), GFP_KERNEL);
> +	if (entry == NULL)
> +		goto lost_msgs;
> +
> +	entry->msg = *msg;
> +	list_add(&entry->list, &fh->msgs);
> +	fh->queued_msgs++;
> +	mutex_unlock(&fh->lock);
> +	wake_up_interruptible(&fh->wait);
> +	return;
> +
> +lost_msgs:
> +	ev_lost_msg.lost_msgs.lost_msgs = ++fh->lost_msgs;
> +	mutex_unlock(&fh->lock);
> +	cec_queue_event_fh(fh, &ev_lost_msg, 0);
> +}
> +
> +/*
> + * Queue the message for those filehandles that are in monitor mode.
> + * If valid_la is true (this message is for us or was sent by us),
> + * then pass it on to any monitoring filehandle. If this message
> + * isn't for us or from us, then only give it to filehandles that
> + * are in MONITOR_ALL mode.
> + *
> + * This can only happen if the CEC_CAP_MONITOR_ALL capability is
> + * set and the CEC adapter was placed in 'monitor all' mode.
> + */
> +static void cec_queue_msg_monitor(struct cec_adapter *adap,
> +				  const struct cec_msg *msg,
> +				  bool valid_la)
> +{
> +	struct cec_fh *fh;
> +	u32 monitor_mode = valid_la ? CEC_MODE_MONITOR :
> +				      CEC_MODE_MONITOR_ALL;
> +
> +	mutex_lock(&adap->devnode.fhs_lock);
> +	list_for_each_entry(fh, &adap->devnode.fhs, list) {
> +		if (fh->mode_follower >= monitor_mode)
> +			cec_queue_msg_fh(fh, msg);
> +	}
> +	mutex_unlock(&adap->devnode.fhs_lock);
> +}
> +
> +/*
> + * Queue the message for follower filehandles.
> + */
> +static void cec_queue_msg_followers(struct cec_adapter *adap,
> +				    const struct cec_msg *msg)
> +{
> +	struct cec_fh *fh;
> +
> +	mutex_lock(&adap->devnode.fhs_lock);
> +	list_for_each_entry(fh, &adap->devnode.fhs, list) {
> +		if (fh->mode_follower == CEC_MODE_FOLLOWER)
> +			cec_queue_msg_fh(fh, msg);
> +	}
> +	mutex_unlock(&adap->devnode.fhs_lock);
> +}
> +
> +/* Notify userspace of an adapter state change. */
> +static void cec_post_state_event(struct cec_adapter *adap)
> +{
> +	struct cec_event ev = {
> +		.event = CEC_EVENT_STATE_CHANGE,
> +	};
> +
> +	ev.state_change.phys_addr = adap->phys_addr;
> +	ev.state_change.log_addr_mask = adap->log_addrs.log_addr_mask;
> +	cec_queue_event(adap, &ev);
> +}
> +
> +/*
> + * A CEC transmit (and a possible wait for reply) completed.
> + * If this was in blocking mode, then complete it, otherwise
> + * queue the message for userspace to dequeue later.
> + *
> + * This function is called with adap->lock held.
> + */
> +static void cec_data_completed(struct cec_data *data)
> +{
> +	/*
> +	 * Delete this transmit from the filehandle's xfer_list since
> +	 * we're done with it.
> +	 *
> +	 * Note that if the filehandle is closed before this transmit
> +	 * finished, then the release() function will set data->fh to NULL.
> +	 * Without that we would be referring to a closed filehandle.
> +	 */
> +	if (data->fh)
> +		list_del(&data->xfer_list);
> +
> +	if (data->blocking) {
> +		/*
> +		 * Someone is blocking so mark the message as completed
> +		 * and call complete.
> +		 */
> +		data->completed = true;
> +		complete(&data->c);
> +	} else {
> +		/*
> +		 * No blocking, so just queue the message if needed and
> +		 * free the memory.
> +		 */
> +		if (data->fh)
> +			cec_queue_msg_fh(data->fh, &data->msg);
> +		kfree(data);
> +	}
> +}
> +
> +/*
> + * A pending CEC transmit needs to be cancelled, either because the CEC
> + * adapter is disabled or the transmit takes an impossibly long time to
> + * finish.
> + *
> + * This function is called with adap->lock held.
> + */
> +static void cec_data_cancel(struct cec_data *data)
> +{
> +	/*
> +	 * It's either the current transmit, or it is a pending
> +	 * transmit. Take the appropriate action to clear it.
> +	 */
> +	if (data->adap->transmitting == data)
> +		data->adap->transmitting = NULL;
> +	else
> +		list_del_init(&data->list);
> +
> +	/* Mark it as an error */
> +	data->msg.ts = ktime_get_ns();
> +	data->msg.tx_status = CEC_TX_STATUS_ERROR |
> +			      CEC_TX_STATUS_MAX_RETRIES;
> +	data->attempts = 0;
> +	data->msg.tx_error_cnt = 1;
> +	data->msg.reply = 0;
> +	/* Queue transmitted message for monitoring purposes */
> +	cec_queue_msg_monitor(data->adap, &data->msg, 1);
> +
> +	cec_data_completed(data);
> +}
> +
> +/*
> + * Main CEC state machine
> + *
> + * Wait until the thread should be stopped, or we are not transmitting and
> + * a new transmit message is queued up, in which case we start transmitting
> + * that message. When the adapter finished transmitting the message it will
> + * call cec_transmit_done().
> + *
> + * If the adapter is disabled, then remove all queued messages instead.
> + *
> + * If the current transmit times out, then cancel that transmit.
> + */
> +static int cec_thread_func(void *_adap)
> +{
> +	struct cec_adapter *adap = _adap;
> +
> +	for (;;) {
> +		unsigned signal_free_time;
> +		struct cec_data *data;
> +		bool timeout = false;
> +		u8 attempts;
> +
> +		if (adap->transmitting) {
> +			int err;
> +
> +			/*
> +			 * We are transmitting a message, so add a timeout
> +			 * to prevent the state machine to get stuck waiting
> +			 * for this message to finalize and add a check to
> +			 * see if the adapter is disabled in which case the
> +			 * transmit should be canceled.
> +			 */
> +			err = wait_event_interruptible_timeout(adap->kthread_waitq,
> +				kthread_should_stop() ||
> +				adap->phys_addr == CEC_PHYS_ADDR_INVALID ||
> +				(!adap->transmitting &&
> +				 !list_empty(&adap->transmit_queue)),
> +				msecs_to_jiffies(CEC_XFER_TIMEOUT_MS));
> +			timeout = err == 0;
> +		} else {
> +			/* Otherwise we just wait for something to happen. */
> +			wait_event_interruptible(adap->kthread_waitq,
> +				kthread_should_stop() ||
> +				(!adap->transmitting &&
> +				 !list_empty(&adap->transmit_queue)));
> +		}
> +
> +		mutex_lock(&adap->lock);
> +
> +		if (adap->phys_addr == CEC_PHYS_ADDR_INVALID ||
> +		    kthread_should_stop()) {
> +			/*
> +			 * If the adapter is disabled, or we're asked to stop,
> +			 * then cancel any pending transmits.
> +			 */
> +			while (!list_empty(&adap->transmit_queue)) {
> +				data = list_first_entry(&adap->transmit_queue,
> +							struct cec_data, list);
> +				cec_data_cancel(data);
> +			}
> +			while (!list_empty(&adap->wait_queue)) {
> +				data = list_first_entry(&adap->wait_queue,
> +							struct cec_data, list);
> +				cec_data_cancel(data);
> +			}
> +			if (adap->transmitting)
> +				cec_data_cancel(adap->transmitting);
> +			goto unlock;
> +		}
> +
> +		if (adap->transmitting && timeout) {
> +			/*
> +			 * If we timeout, then log that. This really shouldn't
> +			 * happen and is an indication of a faulty CEC adapter
> +			 * driver, or the CEC bus is in some weird state.
> +			 */
> +			dprintk(0, "message %*ph timed out!\n",
> +				adap->transmitting->msg.len,
> +				adap->transmitting->msg.msg);
> +			/* Just give up on this. */
> +			cec_data_cancel(adap->transmitting);
> +			goto unlock;
> +		}
> +
> +		/*
> +		 * If we are still transmitting, or there is nothing new to
> +		 * transmit, then just continue waiting.
> +		 */
> +		if (adap->transmitting || list_empty(&adap->transmit_queue))
> +			goto unlock;
> +
> +		/* Get a new message to transmit */
> +		data = list_first_entry(&adap->transmit_queue,
> +					struct cec_data, list);
> +		list_del_init(&data->list);
> +		/* Make this the current transmitting message */
> +		adap->transmitting = data;
> +
> +		/*
> +		 * Suggested number of attempts as per the CEC 2.0 spec:
> +		 * 4 attempts is the default, except for 'secondary poll
> +		 * messages', i.e. poll messages not sent during the adapter
> +		 * configuration phase when it allocates logical addresses.
> +		 */
> +		if (data->msg.len == 1 && adap->is_configured)
> +			attempts = 2;
> +		else
> +			attempts = 4;
> +
> +		/* Set the suggested signal free time */
> +		if (data->attempts) {
> +			/* should be >= 3 data bit periods for a retry */
> +			signal_free_time = CEC_SIGNAL_FREE_TIME_RETRY;
> +		} else if (data->new_initiator) {
> +			/* should be >= 5 data bit periods for new initiator */
> +			signal_free_time = CEC_SIGNAL_FREE_TIME_NEW_INITIATOR;
> +		} else {
> +			/*
> +			 * should be >= 7 data bit periods for sending another
> +			 * frame immediately after another.
> +			 */
> +			signal_free_time = CEC_SIGNAL_FREE_TIME_NEXT_XFER;
> +		}
> +		if (data->attempts == 0)
> +			data->attempts = attempts;
> +
> +		/* Tell the adapter to transmit, cancel on error */
> +		if (adap->ops->adap_transmit(adap, data->attempts,
> +					     signal_free_time, &data->msg))
> +			cec_data_cancel(data);
> +
> +unlock:
> +		mutex_unlock(&adap->lock);
> +
> +		if (kthread_should_stop())
> +			break;
> +	}
> +	return 0;
> +}
> +
> +/*
> + * Called by the CEC adapter if a transmit finished.
> + */
> +void cec_transmit_done(struct cec_adapter *adap, u8 status, u8 arb_lost_cnt,
> +		       u8 nack_cnt, u8 low_drive_cnt, u8 error_cnt)
> +{
> +	struct cec_data *data;
> +	struct cec_msg *msg;
> +
> +	dprintk(2, "cec_transmit_done %02x\n", status);
> +	mutex_lock(&adap->lock);
> +	data = adap->transmitting;
> +	if (WARN_ON(data == NULL)) {
> +		/* This is weird and should not happen. Ignore this transmit */
> +		dprintk(0, "cec_transmit_done without an ongoing transmit!\n");
> +		goto unlock;
> +	}
> +
> +	msg = &data->msg;
> +
> +	/* Drivers must fill in the status! */
> +	WARN_ON(status == 0);
> +	msg->ts = ktime_get_ns();
> +	msg->tx_status |= status;
> +	msg->tx_arb_lost_cnt += arb_lost_cnt;
> +	msg->tx_nack_cnt += nack_cnt;
> +	msg->tx_low_drive_cnt += low_drive_cnt;
> +	msg->tx_error_cnt += error_cnt;
> +
> +	/* Mark that we're done with this transmit */
> +	adap->transmitting = NULL;
> +
> +	/*
> +	 * If there are still retry attempts left and there was an error and
> +	 * the hardware didn't signal that it retried itself (by setting
> +	 * CEC_TX_STATUS_MAX_RETRIES), then we will retry ourselves.
> +	 */
> +	if (data->attempts > 1 &&
> +	    !(status & (CEC_TX_STATUS_MAX_RETRIES | CEC_TX_STATUS_OK))) {
> +		/* Retry this message */
> +		data->attempts--;
> +		/* Add the message in front of the transmit queue */
> +		list_add(&data->list, &adap->transmit_queue);
> +		goto wake_thread;
> +	}
> +
> +	data->attempts = 0;
> +
> +	/* Always set CEC_TX_STATUS_MAX_RETRIES on error */
> +	if (!(status & CEC_TX_STATUS_OK))
> +		msg->tx_status |= CEC_TX_STATUS_MAX_RETRIES;
> +
> +	/* Queue transmitted message for monitoring purposes */
> +	cec_queue_msg_monitor(adap, msg, 1);
> +
> +	/*
> +	 * Clear reply on error of if the adapter is no longer
> +	 * configured. It makes no sense to wait for a reply in
> +	 * this case.
> +	 */
> +	if (!(status & CEC_TX_STATUS_OK) || !adap->is_configured)
> +		msg->reply = 0;
> +
> +	if (msg->timeout) {
> +		/*
> +		 * Queue the message into the wait queue if we want to wait
> +		 * for a reply.
> +		 */
> +		list_add_tail(&data->list, &adap->wait_queue);
> +		schedule_delayed_work(&data->work,
> +				      msecs_to_jiffies(msg->timeout));
> +	} else {
> +		/* Otherwise we're done */
> +		cec_data_completed(data);
> +	}
> +
> +wake_thread:
> +	/*
> +	 * Wake up the main thread to see if another message is ready
> +	 * for transmitting or to retry the current message.
> +	 */
> +	wake_up_interruptible(&adap->kthread_waitq);
> +unlock:
> +	mutex_unlock(&adap->lock);
> +}
> +EXPORT_SYMBOL_GPL(cec_transmit_done);
> +
> +/*
> + * Called when waiting for a reply times out.
> + */
> +static void cec_wait_timeout(struct work_struct *work)
> +{
> +	struct cec_data *data = container_of(work, struct cec_data, work.work);
> +	struct cec_adapter *adap = data->adap;
> +
> +	mutex_lock(&adap->lock);
> +	/*
> +	 * Sanity check in case the timeout and the arrival of the message
> +	 * happened at the same time.
> +	 */
> +	if (list_empty(&data->list))
> +		goto unlock;
> +
> +	/* Mark the message as timed out */
> +	list_del_init(&data->list);
> +	data->msg.ts = ktime_get_ns();
> +	data->msg.rx_status = CEC_RX_STATUS_TIMEOUT;
> +	cec_data_completed(data);
> +unlock:
> +	mutex_unlock(&adap->lock);
> +}
> +
> +/*
> + * Transmit a message. The fh argument may be NULL if the transmit is not
> + * associated with a specific filehandle.
> + *
> + * This function is called with adap->lock held.
> + */
> +static int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg,
> +			       struct cec_fh *fh, bool block)
> +{
> +	struct cec_data *data;
> +	u8 last_initiator = 0xff;
> +	unsigned timeout;
> +	int res = 0;
> +
> +	if (msg->reply && msg->timeout == 0) {
> +		/* Make sure the timeout isn't 0. */
> +		msg->timeout = 1000;
> +	}
> +
> +	/* Sanity checks */
> +	if (msg->len == 0 || msg->len > CEC_MAX_MSG_SIZE) {
> +		dprintk(1, "cec_transmit_msg: invalid length %d\n", msg->len);
> +		return -EINVAL;
> +	}
> +	if (msg->timeout && msg->len == 1) {
> +		dprintk(1, "cec_transmit_msg: can't reply for poll msg\n");
> +		return -EINVAL;
> +	}
> +	if (msg->len == 1) {
> +		if (cec_msg_initiator(msg) != 0xf ||
> +		    cec_msg_destination(msg) == 0xf) {
> +			dprintk(1, "cec_transmit_msg: invalid poll message\n");
> +			return -EINVAL;
> +		}
> +		if (cec_has_log_addr(adap, cec_msg_destination(msg))) {
> +			/*
> +			 * If the destination is a logical address our adapter
> +			 * has already claimed, then just NACK this.
> +			 * It depends on the hardware what it will do with a
> +			 * POLL to itself (some OK this), so it is just as
> +			 * easy to handle it here so the behavior will be
> +			 * consistent.
> +			 */
> +			msg->tx_status = CEC_TX_STATUS_NACK |
> +					 CEC_TX_STATUS_MAX_RETRIES;
> +			msg->tx_nack_cnt = 1;
> +			return 0;
> +		}
> +	}
> +	if (msg->len > 1 && !cec_msg_is_broadcast(msg) &&
> +	    cec_has_log_addr(adap, cec_msg_destination(msg))) {
> +		dprintk(1, "cec_transmit_msg: destination is the adapter itself\n");
> +		return -EINVAL;
> +	}
> +	if (cec_msg_initiator(msg) != 0xf &&
> +	    !cec_has_log_addr(adap, cec_msg_initiator(msg))) {
> +		dprintk(1, "cec_transmit_msg: initiator has unknown logical address %d\n",
> +			cec_msg_initiator(msg));
> +		return -EINVAL;
> +	}
> +	if (!adap->is_configured && !adap->is_configuring)
> +		return -ENONET;
> +
> +	data = kzalloc(sizeof(*data), GFP_KERNEL);
> +	if (data == NULL)
> +		return -ENOMEM;
> +
> +	if (msg->len > 1 && msg->msg[1] == CEC_MSG_CDC_MESSAGE) {
> +		msg->msg[2] = adap->phys_addr >> 8;
> +		msg->msg[3] = adap->phys_addr & 0xff;
> +	}
> +
> +	if (msg->timeout)
> +		dprintk(2, "cec_transmit_msg: %*ph (wait for 0x%02x%s)\n",
> +			msg->len, msg->msg, msg->reply, !block ? ", nb" : "");
> +	else
> +		dprintk(2, "cec_transmit_msg: %*ph%s\n",
> +			msg->len, msg->msg, !block ? " (nb)" : "");
> +
> +	msg->rx_status = msg->tx_status = 0;
> +	msg->tx_arb_lost_cnt = msg->tx_nack_cnt = 0;
> +	msg->tx_low_drive_cnt = msg->tx_error_cnt = 0;
> +	data->msg = *msg;
> +	data->fh = fh;
> +	data->adap = adap;
> +	data->blocking = block;
> +
> +	/*
> +	 * Determine if this message follows a message from the same
> +	 * initiator. Needed to determine the free signal time later on.
> +	 */
> +	if (msg->len > 1) {
> +		if (!(list_empty(&adap->transmit_queue))) {
> +			const struct cec_data *last;
> +
> +			last = list_last_entry(&adap->transmit_queue,
> +					       const struct cec_data, list);
> +			last_initiator = cec_msg_initiator(&last->msg);
> +		} else if (adap->transmitting) {
> +			last_initiator =
> +				cec_msg_initiator(&adap->transmitting->msg);
> +		}
> +	}
> +	data->new_initiator = last_initiator != cec_msg_initiator(msg);
> +	init_completion(&data->c);
> +	INIT_DELAYED_WORK(&data->work, cec_wait_timeout);
> +
> +	data->msg.sequence = adap->sequence++;
> +	if (fh)
> +		list_add_tail(&data->xfer_list, &fh->xfer_list);
> +	list_add_tail(&data->list, &adap->transmit_queue);
> +	if (adap->transmitting == NULL)
> +		wake_up_interruptible(&adap->kthread_waitq);
> +
> +	/* All done if we don't need to block waiting for completion */
> +	if (!block)
> +		return 0;
> +
> +	/*
> +	 * If we don't get a completion before this time something is really
> +	 * wrong and we time out.
> +	 */
> +	timeout = CEC_XFER_TIMEOUT_MS;
> +	/* Add the requested timeout if we have to wait for a reply as well */
> +	if (msg->timeout)
> +		timeout += msg->timeout;
> +
> +	/*
> +	 * Release the lock and wait, retake the lock afterwards.
> +	 */
> +	mutex_unlock(&adap->lock);
> +	res = wait_for_completion_killable_timeout(&data->c,
> +			 msecs_to_jiffies(timeout));
> +	mutex_lock(&adap->lock);
> +
> +	if (data->completed) {
> +		/* The transmit completed (possibly with an error) */
> +		*msg = data->msg;
> +		kfree(data);
> +		return 0;
> +	}
> +	/*
> +	 * The wait for completion timed out or was interrupted, so mark this
> +	 * as non-blocking and disconnect from the filehandle since it is
> +	 * still 'in flight'. When it finally completes it will just drop the
> +	 * result silently.
> +	 */
> +	data->blocking = false;
> +	if (data->fh)
> +		list_del(&data->xfer_list);
> +	data->fh = NULL;
> +
> +	if (res == 0) { /* timed out */
> +		/* Check if the reply or the transmit failed */
> +		if (msg->timeout && (msg->tx_status & CEC_TX_STATUS_OK))
> +			msg->rx_status = CEC_RX_STATUS_TIMEOUT;
> +		else
> +			msg->tx_status = CEC_TX_STATUS_MAX_RETRIES;
> +	}
> +	return res > 0 ? 0 : res;
> +}
> +
> +/* Helper function to be used by drivers and this framework. */
> +int cec_transmit_msg(struct cec_adapter *adap, struct cec_msg *msg,
> +		     bool block)
> +{
> +	int ret;
> +
> +	mutex_lock(&adap->lock);
> +	ret = cec_transmit_msg_fh(adap, msg, NULL, block);
> +	mutex_unlock(&adap->lock);
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(cec_transmit_msg);
> +
> +/*
> + * I don't like forward references but without this the low-level
> + * cec_received_msg() function would come after a bunch of high-level
> + * CEC protocol handling functions. That was very confusing.
> + */
> +static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg,
> +			      bool is_reply);
> +
> +/* Called by the CEC adapter if a message is received */
> +void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg)
> +{
> +	struct cec_data *data;
> +	u8 msg_init = cec_msg_initiator(msg);
> +	u8 msg_dest = cec_msg_destination(msg);
> +	bool is_reply = false;
> +	bool valid_la = true;
> +
> +	mutex_lock(&adap->lock);
> +	msg->ts = ktime_get_ns();
> +	msg->rx_status = CEC_RX_STATUS_OK;
> +	msg->tx_status = 0;
> +	msg->sequence = msg->reply = msg->timeout = 0;
> +	msg->flags = 0;
> +
> +	dprintk(2, "cec_received_msg: %*ph\n", msg->len, msg->msg);
> +
> +	/* Check if this message was for us (directed or broadcast). */
> +	if (!cec_msg_is_broadcast(msg))
> +		valid_la = cec_has_log_addr(adap, msg_dest);
> +
> +	/* It's a valid message and not a poll or CDC message */
> +	if (valid_la && msg->len > 1 && msg->msg[1] != CEC_MSG_CDC_MESSAGE) {
> +		u8 cmd = msg->msg[1];
> +		bool abort = cmd == CEC_MSG_FEATURE_ABORT;
> +
> +		/* The aborted command is in msg[2] */
> +		if (abort)
> +			cmd = msg->msg[2];
> +
> +		/*
> +		 * Walk over all transmitted messages that are waiting for a
> +		 * reply.
> +		 */
> +		list_for_each_entry(data, &adap->wait_queue, list) {
> +			struct cec_msg *dst = &data->msg;
> +			u8 dst_reply;
> +
> +			/* Does the command match? */
> +			if ((abort && cmd != dst->msg[1]) ||
> +			    (!abort && cmd != dst->reply))
> +				continue;
> +
> +			/* Does the addressing match? */
> +			if (msg_init != cec_msg_destination(dst) &&
> +			    !cec_msg_is_broadcast(dst))
> +				continue;
> +
> +			/* We got a reply */
> +			msg->sequence = dst->sequence;
> +			dst_reply = dst->reply;
> +			*dst = *msg;
> +			dst->reply = dst_reply;
> +			if (abort) {
> +				dst->reply = 0;
> +				dst->rx_status |= CEC_RX_STATUS_FEATURE_ABORT;
> +			}
> +			/* Remove it from the wait_queue */
> +			list_del_init(&data->list);
> +
> +			/* Cancel the pending timeout work */
> +			if (!cancel_delayed_work(&data->work)) {
> +				mutex_unlock(&adap->lock);
> +				flush_scheduled_work();
> +				mutex_lock(&adap->lock);
> +			}
> +			/*
> +			 * Mark this as a reply, provided someone is still
> +			 * waiting for the answer.
> +			 */
> +			if (data->fh)
> +				is_reply = true;
> +			cec_data_completed(data);
> +			break;
> +		}
> +	}
> +	mutex_unlock(&adap->lock);
> +
> +	/* Pass the message on to any monitoring filehandles */
> +	cec_queue_msg_monitor(adap, msg, valid_la);
> +
> +	/* We're done if it is not for us or a poll message */
> +	if (!valid_la || msg->len <= 1)
> +		return;
> +
> +	/*
> +	 * Process the message on the protocol level. If is_reply is true,
> +	 * then cec_receive_notify() won't pass on the reply to the listener(s)
> +	 * since that was already done by cec_data_completed() above.
> +	 */
> +	cec_receive_notify(adap, msg, is_reply);
> +}
> +EXPORT_SYMBOL_GPL(cec_received_msg);
> +
> +
> +/* High-level core CEC message handling */
> +
> +/* Transmit the Report Features message */
> +static int cec_report_features(struct cec_adapter *adap, unsigned la_idx)
> +{
> +	struct cec_msg msg = { };
> +	const struct cec_log_addrs *las = &adap->log_addrs;
> +	const u8 *features = las->features[la_idx];
> +	bool op_is_dev_features = false;
> +	unsigned idx;
> +
> +	/* This is 2.0 and up only */
> +	if (adap->log_addrs.cec_version < CEC_OP_CEC_VERSION_2_0)
> +		return 0;
> +
> +	/* Report Features */
> +	msg.msg[0] = (las->log_addr[la_idx] << 4) | 0x0f;
> +	msg.len = 4;
> +	msg.msg[1] = CEC_MSG_REPORT_FEATURES;
> +	msg.msg[2] = adap->log_addrs.cec_version;
> +	msg.msg[3] = las->all_device_types[la_idx];
> +
> +	/* Write RC Profiles first, then Device Features */
> +	for (idx = 0; idx < sizeof(las->features[0]); idx++) {

shouldn't it be, instead, ARRAY_SIZE()?

> +		msg.msg[msg.len++] = features[idx];
> +		if ((features[idx] & CEC_OP_FEAT_EXT) == 0) {
> +			if (op_is_dev_features)
> +				break;
> +			op_is_dev_features = true;
> +		}
> +	}
> +	return cec_transmit_msg(adap, &msg, false);
> +}
> +
> +/* Transmit the Report Physical Address message */
> +static int cec_report_phys_addr(struct cec_adapter *adap, unsigned la_idx)
> +{
> +	const struct cec_log_addrs *las = &adap->log_addrs;
> +	struct cec_msg msg = { };
> +
> +	/* Report Physical Address */
> +	msg.msg[0] = (las->log_addr[la_idx] << 4) | 0x0f;
> +	cec_msg_report_physical_addr(&msg, adap->phys_addr,
> +				     las->primary_device_type[la_idx]);
> +	dprintk(2, "config: la %d pa %x.%x.%x.%x\n",
> +			las->log_addr[la_idx],
> +			cec_phys_addr_exp(adap->phys_addr));

Am I missing something or are there some parameters missing? you're
printing 5 parameters, but I'm only seeing two arguments after the
format string.

> +	return cec_transmit_msg(adap, &msg, false);
> +}
> +
> +/* Transmit the Feature Abort message */
> +static int cec_feature_abort_reason(struct cec_adapter *adap,
> +				    struct cec_msg *msg, u8 reason)
> +{
> +	struct cec_msg tx_msg = { };
> +
> +	/*
> +	 * Don't reply with CEC_MSG_FEATURE_ABORT to a CEC_MSG_FEATURE_ABORT
> +	 * message!
> +	 */
> +	if (msg->msg[1] == CEC_MSG_FEATURE_ABORT)
> +		return 0;
> +	cec_msg_set_reply_to(&tx_msg, msg);
> +	cec_msg_feature_abort(&tx_msg, msg->msg[1], reason);
> +	return cec_transmit_msg(adap, &tx_msg, false);
> +}
> +
> +static int cec_feature_abort(struct cec_adapter *adap, struct cec_msg *msg)
> +{
> +	return cec_feature_abort_reason(adap, msg,
> +					CEC_OP_ABORT_UNRECOGNIZED_OP);
> +}
> +
> +static int cec_feature_refused(struct cec_adapter *adap, struct cec_msg *msg)
> +{
> +	return cec_feature_abort_reason(adap, msg,
> +					CEC_OP_ABORT_REFUSED);
> +}
> +
> +/*
> + * Called when a CEC message is received. This function will do any
> + * necessary core processing. The is_reply bool is true if this message
> + * is a reply to an earlier transmit.
> + *
> + * The message is either a broadcast message or a valid directed message.
> + */
> +static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg,
> +			      bool is_reply)
> +{
> +	bool is_broadcast = cec_msg_is_broadcast(msg);
> +	u8 dest_laddr = cec_msg_destination(msg);
> +	u8 init_laddr = cec_msg_initiator(msg);
> +	u8 devtype = cec_log_addr2dev(adap, dest_laddr);
> +	int la_idx = cec_log_addr2idx(adap, dest_laddr);
> +	bool is_directed = la_idx >= 0;
> +	bool from_unregistered = init_laddr == 0xf;
> +	struct cec_msg tx_cec_msg = { };
> +
> +	dprintk(1, "cec_receive_notify: %*ph\n", msg->len, msg->msg);
> +
> +	if (adap->ops->received) {
> +		/* Allow drivers to process the message first */
> +		if (adap->ops->received(adap, msg) != -ENOMSG)
> +			return 0;
> +	}
> +
> +	/*
> +	 * REPORT_PHYSICAL_ADDR, CEC_MSG_USER_CONTROL_PRESSED and
> +	 * CEC_MSG_USER_CONTROL_RELEASED messages always have to be
> +	 * handled by the CEC core, even if the passthrough mode is on.
> +	 * The others are just ignored if passthrough mode is on.
> +	 */
> +	switch (msg->msg[1]) {
> +	case CEC_MSG_GET_CEC_VERSION:
> +	case CEC_MSG_GIVE_DEVICE_VENDOR_ID:
> +	case CEC_MSG_ABORT:
> +	case CEC_MSG_GIVE_DEVICE_POWER_STATUS:
> +	case CEC_MSG_GIVE_PHYSICAL_ADDR:
> +	case CEC_MSG_GIVE_OSD_NAME:
> +	case CEC_MSG_GIVE_FEATURES:
> +		/*
> +		 * Skip processing these messages if the passthrough mode
> +		 * is on.
> +		 */
> +		if (adap->passthrough)
> +			goto skip_processing;
> +		/* Ignore if addressing is wrong */
> +		if (is_broadcast || from_unregistered)
> +			return 0;
> +		break;
> +
> +	case CEC_MSG_USER_CONTROL_PRESSED:
> +	case CEC_MSG_USER_CONTROL_RELEASED:
> +		/* Wrong addressing mode: don't process */
> +		if (is_broadcast || from_unregistered)
> +			goto skip_processing;
> +		break;
> +
> +	case CEC_MSG_REPORT_PHYSICAL_ADDR:
> +		/*
> +		 * This message is always processed, regardless of the
> +		 * passthrough setting.
> +		 *
> +		 * Exception: don't process if wrong addressing mode.
> +		 */
> +		if (!is_broadcast)
> +			goto skip_processing;
> +		break;
> +
> +	default:
> +		break;
> +	}
> +
> +	cec_msg_set_reply_to(&tx_cec_msg, msg);
> +
> +	switch (msg->msg[1]) {
> +	/* The following messages are processed but still passed through */
> +	case CEC_MSG_REPORT_PHYSICAL_ADDR:
> +		adap->phys_addrs[init_laddr] =
> +			(msg->msg[2] << 8) | msg->msg[3];
> +		dprintk(1, "Reported physical address %04x for logical address %d\n",
> +			adap->phys_addrs[init_laddr], init_laddr);
> +		break;
> +
> +	case CEC_MSG_USER_CONTROL_PRESSED:
> +		if (!(adap->capabilities & CEC_CAP_RC))
> +			break;
> +
> +#if IS_ENABLED(CONFIG_RC_CORE)
> +		switch (msg->msg[2]) {
> +		/*
> +		 * Play function, this message can have variable length
> +		 * depending on the specific play function that is used.
> +		 */
> +		case 0x60:
> +			if (msg->len == 2)
> +				rc_keydown(adap->rc, RC_TYPE_CEC,
> +					   msg->msg[2], 0);
> +			else
> +				rc_keydown(adap->rc, RC_TYPE_CEC,
> +					   msg->msg[2] << 8 | msg->msg[3], 0);
> +			break;
> +		/*
> +		 * Other function messages that are not handled.
> +		 * Currently the RC framework does not allow to supply an
> +		 * additional parameter to a keypress. These "keys" contain
> +		 * other information such as channel number, an input number
> +		 * etc.
> +		 * For the time being these messages are not processed by the
> +		 * framework and are simply forwarded to the user space.
> +		 */
> +		case 0x56: case 0x57:
> +		case 0x67: case 0x68: case 0x69: case 0x6a:
> +			break;
> +		default:
> +			rc_keydown(adap->rc, RC_TYPE_CEC, msg->msg[2], 0);
> +			break;
> +		}
> +#endif
> +		break;
> +
> +	case CEC_MSG_USER_CONTROL_RELEASED:
> +		if (!(adap->capabilities & CEC_CAP_RC))
> +			break;
> +#if IS_ENABLED(CONFIG_RC_CORE)
> +		rc_keyup(adap->rc);
> +#endif
> +		break;
> +
> +	/*
> +	 * The remaining messages are only processed if the passthrough mode
> +	 * is off.
> +	 */
> +	case CEC_MSG_GET_CEC_VERSION:
> +		cec_msg_cec_version(&tx_cec_msg, adap->log_addrs.cec_version);
> +		return cec_transmit_msg(adap, &tx_cec_msg, false);
> +
> +	case CEC_MSG_GIVE_PHYSICAL_ADDR:
> +		/* Do nothing for CEC switches using addr 15 */
> +		if (devtype == CEC_OP_PRIM_DEVTYPE_SWITCH && dest_laddr == 15)
> +			return 0;
> +		cec_msg_report_physical_addr(&tx_cec_msg, adap->phys_addr, devtype);
> +		return cec_transmit_msg(adap, &tx_cec_msg, false);
> +
> +	case CEC_MSG_GIVE_DEVICE_VENDOR_ID:
> +		if (adap->log_addrs.vendor_id == CEC_VENDOR_ID_NONE)
> +			return cec_feature_abort(adap, msg);
> +		cec_msg_device_vendor_id(&tx_cec_msg, adap->log_addrs.vendor_id);
> +		return cec_transmit_msg(adap, &tx_cec_msg, false);
> +
> +	case CEC_MSG_ABORT:
> +		/* Do nothing for CEC switches */
> +		if (devtype == CEC_OP_PRIM_DEVTYPE_SWITCH)
> +			return 0;
> +		return cec_feature_refused(adap, msg);
> +
> +	case CEC_MSG_GIVE_OSD_NAME: {
> +		if (adap->log_addrs.osd_name[0] == 0)
> +			return cec_feature_abort(adap, msg);
> +		cec_msg_set_osd_name(&tx_cec_msg, adap->log_addrs.osd_name);
> +		return cec_transmit_msg(adap, &tx_cec_msg, false);
> +	}
> +
> +	case CEC_MSG_GIVE_FEATURES:
> +		if (adap->log_addrs.cec_version >= CEC_OP_CEC_VERSION_2_0)
> +			return cec_report_features(adap, la_idx);
> +		return 0;
> +
> +	default:
> +		/*
> +		 * Unprocessed messages are aborted if userspace isn't doing
> +		 * any processing either.
> +		 */
> +		if (is_directed && !is_reply && !adap->follower_cnt &&
> +		    !adap->cec_follower && msg->msg[1] != CEC_MSG_FEATURE_ABORT)
> +			return cec_feature_abort(adap, msg);
> +		break;
> +	}
> +
> +skip_processing:
> +	/* If this was not a reply, then we're done */
> +	if (is_reply)
> +		return 0;
> +
> +	/*
> +	 * Send to the exclusive follower if there is one, otherwise send
> +	 * to all followerd.
> +	 */
> +	if (adap->cec_follower)
> +		cec_queue_msg_fh(adap->cec_follower, msg);
> +	else
> +		cec_queue_msg_followers(adap, msg);
> +	return 0;
> +}
> +
> +/*
> + * Attempt to claim a specific logical address.
> + *
> + * This function is called with adap->lock held.
> + */
> +static int cec_config_log_addr(struct cec_adapter *adap,
> +			       unsigned int idx,
> +			       unsigned int log_addr)
> +{
> +	struct cec_log_addrs *las = &adap->log_addrs;
> +	struct cec_msg msg = { };
> +	int err;
> +
> +	if (cec_has_log_addr(adap, log_addr))
> +		return 0;
> +
> +	/* Send poll message */
> +	msg.len = 1;
> +	msg.msg[0] = 0xf0 | log_addr;
> +	err = cec_transmit_msg_fh(adap, &msg, NULL, true);
> +
> +	/*
> +	 * While trying to poll the physical address was reset
> +	 * and the adapter was unconfigured, so bail out.
> +	 */
> +	if (!adap->is_configuring)
> +		return -EINTR;
> +
> +	if (err)
> +		return err;
> +
> +	if (msg.tx_status & CEC_TX_STATUS_OK)
> +		return 0;
> +
> +	/*
> +	 * Message not acknowledged, so this logical
> +	 * address is free to use.
> +	 */
> +	err = adap->ops->adap_log_addr(adap, log_addr);
> +	if (err)
> +		return err;
> +
> +	las->log_addr[idx] = log_addr;
> +	las->log_addr_mask |= 1 << log_addr;
> +	adap->phys_addrs[log_addr] = adap->phys_addr;
> +
> +	dprintk(2, "claimed addr %d (%d)\n", log_addr,
> +		las->primary_device_type[idx]);
> +	return 1;
> +}
> +
> +/*
> + * Unconfigure the adapter: clear all logical addresses and send
> + * the state changed event.
> + *
> + * This function is called with adap->lock held.
> + */
> +static void cec_adap_unconfigure(struct cec_adapter *adap)
> +{
> +	WARN_ON(adap->ops->adap_log_addr(adap, CEC_LOG_ADDR_INVALID));
> +	adap->log_addrs.log_addr_mask = 0;
> +	adap->is_configuring = false;
> +	adap->is_configured = false;
> +	memset(adap->phys_addrs, 0xff, sizeof(adap->phys_addrs));
> +	wake_up_interruptible(&adap->kthread_waitq);
> +	cec_post_state_event(adap);
> +}
> +
> +/*
> + * Attempt to claim the required logical addresses.
> + */
> +static int cec_config_thread_func(void *arg)
> +{
> +	/* The various LAs for each type of device */
> +	static const u8 tv_log_addrs[] = {
> +		CEC_LOG_ADDR_TV, CEC_LOG_ADDR_SPECIFIC,
> +		CEC_LOG_ADDR_INVALID
> +	};
> +	static const u8 record_log_addrs[] = {
> +		CEC_LOG_ADDR_RECORD_1, CEC_LOG_ADDR_RECORD_2,
> +		CEC_LOG_ADDR_RECORD_3,
> +		CEC_LOG_ADDR_BACKUP_1, CEC_LOG_ADDR_BACKUP_2,
> +		CEC_LOG_ADDR_INVALID
> +	};
> +	static const u8 tuner_log_addrs[] = {
> +		CEC_LOG_ADDR_TUNER_1, CEC_LOG_ADDR_TUNER_2,
> +		CEC_LOG_ADDR_TUNER_3, CEC_LOG_ADDR_TUNER_4,
> +		CEC_LOG_ADDR_BACKUP_1, CEC_LOG_ADDR_BACKUP_2,
> +		CEC_LOG_ADDR_INVALID
> +	};
> +	static const u8 playback_log_addrs[] = {
> +		CEC_LOG_ADDR_PLAYBACK_1, CEC_LOG_ADDR_PLAYBACK_2,
> +		CEC_LOG_ADDR_PLAYBACK_3,
> +		CEC_LOG_ADDR_BACKUP_1, CEC_LOG_ADDR_BACKUP_2,
> +		CEC_LOG_ADDR_INVALID
> +	};
> +	static const u8 audiosystem_log_addrs[] = {
> +		CEC_LOG_ADDR_AUDIOSYSTEM,
> +		CEC_LOG_ADDR_INVALID
> +	};
> +	static const u8 specific_use_log_addrs[] = {
> +		CEC_LOG_ADDR_SPECIFIC,
> +		CEC_LOG_ADDR_BACKUP_1, CEC_LOG_ADDR_BACKUP_2,
> +		CEC_LOG_ADDR_INVALID
> +	};
> +	static const u8 *type2addrs[6] = {
> +		[CEC_LOG_ADDR_TYPE_TV] = tv_log_addrs,
> +		[CEC_LOG_ADDR_TYPE_RECORD] = record_log_addrs,
> +		[CEC_LOG_ADDR_TYPE_TUNER] = tuner_log_addrs,
> +		[CEC_LOG_ADDR_TYPE_PLAYBACK] = playback_log_addrs,
> +		[CEC_LOG_ADDR_TYPE_AUDIOSYSTEM] = audiosystem_log_addrs,
> +		[CEC_LOG_ADDR_TYPE_SPECIFIC] = specific_use_log_addrs,
> +	};
> +	static const u16 type2mask[] = {
> +		[CEC_LOG_ADDR_TYPE_TV] = CEC_LOG_ADDR_MASK_TV,
> +		[CEC_LOG_ADDR_TYPE_RECORD] = CEC_LOG_ADDR_MASK_RECORD,
> +		[CEC_LOG_ADDR_TYPE_TUNER] = CEC_LOG_ADDR_MASK_TUNER,
> +		[CEC_LOG_ADDR_TYPE_PLAYBACK] = CEC_LOG_ADDR_MASK_PLAYBACK,
> +		[CEC_LOG_ADDR_TYPE_AUDIOSYSTEM] = CEC_LOG_ADDR_MASK_AUDIOSYSTEM,
> +		[CEC_LOG_ADDR_TYPE_SPECIFIC] = CEC_LOG_ADDR_MASK_SPECIFIC,
> +	};
> +	struct cec_adapter *adap = arg;
> +	struct cec_log_addrs *las = &adap->log_addrs;
> +	int err;
> +	int i, j;
> +
> +	mutex_lock(&adap->lock);
> +	dprintk(1, "physical address: %x.%x.%x.%x, claim %d logical addresses\n",
> +			cec_phys_addr_exp(adap->phys_addr),
> +			las->num_log_addrs);

Again, number of arguments doesn't seem to match.

> +	las->log_addr_mask = 0;
> +
> +	if (las->log_addr_type[0] == CEC_LOG_ADDR_TYPE_UNREGISTERED)
> +		goto configured;
> +
> +	for (i = 0; i < las->num_log_addrs; i++) {
> +		unsigned int type = las->log_addr_type[i];
> +		const u8 *la_list;
> +		u8 last_la;
> +
> +		/*
> +		 * The TV functionality can only map to physical address 0.
> +		 * For any other address, try the Specific functionality
> +		 * instead as per the spec.
> +		 */
> +		if (adap->phys_addr && type == CEC_LOG_ADDR_TYPE_TV)
> +			type = CEC_LOG_ADDR_TYPE_SPECIFIC;
> +
> +		la_list = type2addrs[type];
> +		last_la = las->log_addr[i];
> +		las->log_addr[i] = CEC_LOG_ADDR_INVALID;
> +		if (last_la == CEC_LOG_ADDR_INVALID ||
> +		    last_la == CEC_LOG_ADDR_UNREGISTERED ||
> +		    !(last_la & type2mask[type]))
> +			last_la = la_list[0];
> +
> +		err = cec_config_log_addr(adap, i, last_la);
> +		if (err > 0) /* Reused last LA */
> +			continue;
> +
> +		if (err < 0)
> +			goto unconfigure;
> +
> +		for (j = 0; la_list[j] != CEC_LOG_ADDR_INVALID; j++) {
> +			/* Tried this one already, skip it */
> +			if (la_list[j] == last_la)
> +				continue;
> +			/* The backup addresses are CEC 2.0 specific */
> +			if ((la_list[j] == CEC_LOG_ADDR_BACKUP_1 ||
> +			     la_list[j] == CEC_LOG_ADDR_BACKUP_2) &&
> +			    las->cec_version < CEC_OP_CEC_VERSION_2_0)
> +				continue;
> +
> +			err = cec_config_log_addr(adap, i, la_list[j]);
> +			if (err == 0) /* LA is in use */
> +				continue;
> +			if (err < 0)
> +				goto unconfigure;
> +			/* Done, claimed an LA */
> +			break;
> +		}
> +
> +		if (la_list[j] == CEC_LOG_ADDR_INVALID)
> +			dprintk(1, "could not claim LA %d\n", i);
> +	}
> +
> +configured:
> +	if (adap->log_addrs.log_addr_mask == 0) {
> +		/* Fall back to unregistered */
> +		las->log_addr[0] = CEC_LOG_ADDR_UNREGISTERED;
> +		las->log_addr_mask = 1 << las->log_addr[0];
> +	}
> +	adap->is_configured = true;
> +	adap->is_configuring = false;
> +	cec_post_state_event(adap);
> +	mutex_unlock(&adap->lock);
> +
> +	for (i = 0; i < las->num_log_addrs; i++) {
> +		if (las->log_addr[i] == CEC_LOG_ADDR_INVALID)
> +			continue;
> +
> +		/*
> +		 * Report Features must come first according
> +		 * to CEC 2.0
> +		 */
> +		if (las->log_addr[i] != CEC_LOG_ADDR_UNREGISTERED)
> +			cec_report_features(adap, i);
> +		cec_report_phys_addr(adap, i);
> +	}
> +	mutex_lock(&adap->lock);
> +	adap->kthread_config = NULL;
> +	mutex_unlock(&adap->lock);
> +	complete(&adap->config_completion);
> +	return 0;
> +
> +unconfigure:
> +	for (i = 0; i < las->num_log_addrs; i++)
> +		las->log_addr[i] = CEC_LOG_ADDR_INVALID;
> +	cec_adap_unconfigure(adap);
> +	adap->kthread_config = NULL;
> +	mutex_unlock(&adap->lock);
> +	complete(&adap->config_completion);
> +	return 0;
> +}
> +
> +/*
> + * Called from either __cec_s_phys_addr or __cec_s_log_addrs to claim the
> + * logical addresses.
> + *
> + * This function is called with adap->lock held.
> + */
> +static void cec_claim_log_addrs(struct cec_adapter *adap, bool block)
> +{
> +	if (WARN_ON(adap->is_configuring || adap->is_configured))
> +		return;
> +
> +	init_completion(&adap->config_completion);
> +
> +	/* Ready to kick off the thread */
> +	adap->is_configuring = true;
> +	adap->kthread_config = kthread_run(cec_config_thread_func, adap,
> +					   "ceccfg-%s", adap->name);
> +	if (IS_ERR(adap->kthread_config)) {
> +		adap->kthread_config = NULL;
> +	} else if (block) {
> +		mutex_unlock(&adap->lock);
> +		wait_for_completion(&adap->config_completion);
> +		mutex_lock(&adap->lock);
> +	}
> +}
> +
> +/* Set a new physical address and send an event notifying userspace of this.
> + *
> + * This function is called with adap->lock held.
> + */
> +static void __cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block)
> +{
> +	if (phys_addr == adap->phys_addr)
> +		return;
> +
> +	if (phys_addr == CEC_PHYS_ADDR_INVALID ||
> +	    adap->phys_addr != CEC_PHYS_ADDR_INVALID) {
> +		adap->phys_addr = CEC_PHYS_ADDR_INVALID;
> +		cec_post_state_event(adap);
> +		cec_adap_unconfigure(adap);
> +		/* Disabling monitor all mode should always succeed */
> +		if (adap->monitor_all_cnt)
> +			WARN_ON(call_op(adap, adap_monitor_all_enable, false));
> +		WARN_ON(adap->ops->adap_enable(adap, false));
> +		if (phys_addr == CEC_PHYS_ADDR_INVALID)
> +			return;
> +	}
> +
> +	if (adap->ops->adap_enable(adap, true))
> +		return;
> +
> +	if (adap->monitor_all_cnt &&
> +	    call_op(adap, adap_monitor_all_enable, true)) {
> +		WARN_ON(adap->ops->adap_enable(adap, false));
> +		return;
> +	}
> +	adap->phys_addr = phys_addr;
> +	cec_post_state_event(adap);
> +	if (adap->log_addrs.num_log_addrs)
> +		cec_claim_log_addrs(adap, block);
> +}
> +
> +void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block)
> +{
> +	if (IS_ERR_OR_NULL(adap))
> +		return;
> +
> +	if (WARN_ON(adap->capabilities & CEC_CAP_PHYS_ADDR))
> +		return;
> +	mutex_lock(&adap->lock);
> +	__cec_s_phys_addr(adap, phys_addr, block);
> +	mutex_unlock(&adap->lock);
> +}
> +EXPORT_SYMBOL_GPL(cec_s_phys_addr);
> +
> +/*
> + * Called from either the ioctl or a driver to set the logical addresses.
> + *
> + * This function is called with adap->lock held.
> + */
> +static int __cec_s_log_addrs(struct cec_adapter *adap,
> +			     struct cec_log_addrs *log_addrs, bool block)
> +{
> +	u16 type_mask = 0;
> +	int i;
> +
> +	if (log_addrs == NULL || log_addrs->num_log_addrs == 0) {
> +		adap->log_addrs.num_log_addrs = 0;
> +		cec_adap_unconfigure(adap);
> +		return 0;
> +	}
> +
> +	/* Ensure the osd name is 0-terminated */
> +	log_addrs->osd_name[sizeof(log_addrs->osd_name) - 1] = '\0';
> +
> +	/* Sanity checks */
> +	if (log_addrs->num_log_addrs > adap->available_log_addrs) {
> +		dprintk(1, "num_log_addrs > %d\n", adap->available_log_addrs);
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * Vendor ID is a 24 bit number, so check if the value is
> +	 * within the correct range.
> +	 */
> +	if (log_addrs->vendor_id != CEC_VENDOR_ID_NONE &&
> +	    (log_addrs->vendor_id & 0xff000000) != 0)
> +		return -EINVAL;
> +
> +	if (log_addrs->cec_version != CEC_OP_CEC_VERSION_1_4 &&
> +	    log_addrs->cec_version != CEC_OP_CEC_VERSION_2_0)
> +		return -EINVAL;
> +
> +	if (log_addrs->num_log_addrs > 1)
> +		for (i = 0; i < log_addrs->num_log_addrs; i++)
> +			if (log_addrs->log_addr_type[i] ==
> +					CEC_LOG_ADDR_TYPE_UNREGISTERED) {
> +				dprintk(1, "num_log_addrs > 1 can't be combined with unregistered LA\n");
> +				return -EINVAL;
> +			}
> +
> +	if (log_addrs->cec_version < CEC_OP_CEC_VERSION_2_0) {
> +		memset(log_addrs->all_device_types, 0,
> +		       sizeof(log_addrs->all_device_types));
> +		memset(log_addrs->features, 0, sizeof(log_addrs->features));
> +	}
> +
> +	for (i = 0; i < log_addrs->num_log_addrs; i++) {
> +		u8 *features = log_addrs->features[i];
> +		bool op_is_dev_features = false;
> +
> +		log_addrs->log_addr[i] = CEC_LOG_ADDR_INVALID;
> +		if (type_mask & (1 << log_addrs->log_addr_type[i])) {
> +			dprintk(1, "duplicate logical address type\n");
> +			return -EINVAL;
> +		}
> +		type_mask |= 1 << log_addrs->log_addr_type[i];
> +		if ((type_mask & (1 << CEC_LOG_ADDR_TYPE_RECORD)) &&
> +		    (type_mask & (1 << CEC_LOG_ADDR_TYPE_PLAYBACK))) {
> +			/* Record already contains the playback functionality */
> +			dprintk(1, "invalid record + playback combination\n");
> +			return -EINVAL;
> +		}
> +		if (log_addrs->primary_device_type[i] >
> +					CEC_OP_PRIM_DEVTYPE_PROCESSOR) {
> +			dprintk(1, "unknown primary device type\n");
> +			return -EINVAL;
> +		}
> +		if (log_addrs->primary_device_type[i] == 2) {
> +			dprintk(1, "invalid primary device type\n");
> +			return -EINVAL;
> +		}
> +		if (log_addrs->log_addr_type[i] > CEC_LOG_ADDR_TYPE_UNREGISTERED) {
> +			dprintk(1, "unknown logical address type\n");
> +			return -EINVAL;
> +		}
> +		if (log_addrs->cec_version < CEC_OP_CEC_VERSION_2_0)
> +			continue;
> +
> +		for (i = 0; i < sizeof(log_addrs->features[0]); i++) {

ARRAY_SIZE() - and on other occurrences of it...

> +			if ((features[i] & 0x80) == 0) {
> +				if (op_is_dev_features)
> +					break;
> +				op_is_dev_features = true;
> +			}
> +		}
> +		if (!op_is_dev_features || i == sizeof(log_addrs->features[0])) {
> +			dprintk(1, "malformed features\n");
> +			return -EINVAL;
> +		}
> +	}
> +
> +	if (log_addrs->cec_version >= CEC_OP_CEC_VERSION_2_0) {
> +		if (log_addrs->num_log_addrs > 2) {
> +			dprintk(1, "CEC 2.0 allows no more than 2 logical addresses\n");
> +			return -EINVAL;
> +		}
> +		if (log_addrs->num_log_addrs == 2) {
> +			if (!(type_mask & ((1 << CEC_LOG_ADDR_TYPE_AUDIOSYSTEM) |
> +					   (1 << CEC_LOG_ADDR_TYPE_TV)))) {
> +				dprintk(1, "Two LAs is only allowed for audiosystem and TV\n");
> +				return -EINVAL;
> +			}
> +			if (!(type_mask & ((1 << CEC_LOG_ADDR_TYPE_PLAYBACK) |
> +					   (1 << CEC_LOG_ADDR_TYPE_RECORD)))) {
> +				dprintk(1, "An audiosystem/TV can only be combined with record or playback\n");
> +				return -EINVAL;
> +			}
> +		}
> +	}
> +
> +	log_addrs->log_addr_mask = adap->log_addrs.log_addr_mask;
> +	adap->log_addrs = *log_addrs;
> +	if (adap->phys_addr != CEC_PHYS_ADDR_INVALID)
> +		cec_claim_log_addrs(adap, block);
> +	return 0;
> +}
> +
> +int cec_s_log_addrs(struct cec_adapter *adap,
> +		    struct cec_log_addrs *log_addrs, bool block)
> +{
> +	int err;
> +
> +	if (WARN_ON(adap->capabilities & CEC_CAP_LOG_ADDRS))
> +		return -EINVAL;
> +	mutex_lock(&adap->lock);
> +	err = __cec_s_log_addrs(adap, log_addrs, block);
> +	mutex_unlock(&adap->lock);
> +	return err;
> +}
> +EXPORT_SYMBOL_GPL(cec_s_log_addrs);
> +
> +/*
> + * Log the current state of the CEC adapter.
> + * Very useful for debugging.
> + */
> +static int cec_status(struct seq_file *file, void *priv)

As I briefly commented on IRC, the amount of stuff on this
single patch/file is huge, making hard to review.

I would split it into at least 3 or 4 parts:
	- low-level code;
	- high-level code;
	- userspace interface;
	- debugfs stuff.

And put each part on a separate patch. That would easy review
the code, as it is easy to lose the track on the logic
due to its complexity.


> +{
> +	struct cec_adapter *adap = dev_get_drvdata(file->private);
> +	struct cec_data *data;
> +
> +	mutex_lock(&adap->lock);
> +	seq_printf(file, "configured: %d\n", adap->is_configured);
> +	seq_printf(file, "configuring: %d\n", adap->is_configuring);
> +	seq_printf(file, "phys_addr: %x.%x.%x.%x\n",
> +		cec_phys_addr_exp(adap->phys_addr));
> +	seq_printf(file, "number of LAs: %d\n", adap->log_addrs.num_log_addrs);
> +	seq_printf(file, "LA mask: 0x%04x\n", adap->log_addrs.log_addr_mask);
> +	if (adap->cec_follower)
> +		seq_printf(file, "has CEC follower%s\n",
> +			adap->passthrough ? " (in passthrough mode)" : "");
> +	if (adap->cec_initiator)
> +		seq_puts(file, "has CEC initiator\n");
> +	if (adap->monitor_all_cnt)
> +		seq_printf(file, "file handles in Monitor All mode: %u\n",
> +			adap->monitor_all_cnt);
> +	data = adap->transmitting;
> +	if (data)
> +		seq_printf(file, "transmitting message: %*ph (reply: %02x)\n",
> +			data->msg.len, data->msg.msg, data->msg.reply);
> +	list_for_each_entry(data, &adap->transmit_queue, list) {
> +		seq_printf(file, "queued tx message: %*ph (reply: %02x)\n",
> +			data->msg.len, data->msg.msg, data->msg.reply);
> +	}
> +	list_for_each_entry(data, &adap->wait_queue, list) {
> +		seq_printf(file, "message waiting for reply: %*ph (reply: %02x)\n",
> +			data->msg.len, data->msg.msg, data->msg.reply);
> +	}
> +
> +	call_void_op(adap, adap_status, file);
> +	mutex_unlock(&adap->lock);
> +	return 0;
> +}
> +
> +
> +/* CEC file operations */
> +
> +static unsigned cec_poll(struct file *filp,
> +			 struct poll_table_struct *poll)
> +{
> +	struct cec_devnode *devnode = cec_devnode_data(filp);
> +	struct cec_fh *fh = filp->private_data;
> +	struct cec_adapter *adap = fh->adap;
> +	unsigned res = 0;
> +
> +	if (!devnode->registered)
> +		return POLLERR | POLLHUP;
> +	mutex_lock(&adap->lock);
> +	if (adap->is_configured)
> +		res |= POLLOUT | POLLWRNORM;
> +	if (fh->queued_msgs)
> +		res |= POLLIN | POLLRDNORM;
> +	if (fh->events)
> +		res |= POLLPRI;
> +	poll_wait(filp, &fh->wait, poll);
> +	mutex_unlock(&adap->lock);
> +	return res;
> +}
> +
> +/*
> + * Helper functions to keep track of the 'monitor all' use count.
> + *
> + * These functions are called with adap->lock held.
> + */
> +static int cec_monitor_all_cnt_inc(struct cec_adapter *adap)
> +{
> +	int ret = 0;
> +
> +	if (adap->monitor_all_cnt == 0)
> +		ret = call_op(adap, adap_monitor_all_enable, 1);
> +	if (ret == 0)
> +		adap->monitor_all_cnt++;
> +	return ret;
> +}
> +
> +static void cec_monitor_all_cnt_dec(struct cec_adapter *adap)
> +{
> +	adap->monitor_all_cnt--;
> +	if (adap->monitor_all_cnt == 0)
> +		WARN_ON(call_op(adap, adap_monitor_all_enable, 0));
> +}
> +
> +/* Called by CEC_RECEIVE: wait for a message to arrive */
> +static int cec_receive_msg(struct cec_fh *fh, struct cec_msg *msg, bool block)
> +{
> +	int res;
> +
> +	do {
> +		mutex_lock(&fh->lock);
> +		/* Are there received messages queued up? */
> +		if (fh->queued_msgs) {
> +			/* Yes, return the first one */
> +			struct cec_msg_entry *entry =
> +				list_first_entry(&fh->msgs,
> +						 struct cec_msg_entry, list);
> +
> +			list_del(&entry->list);
> +			*msg = entry->msg;
> +			kfree(entry);
> +			fh->queued_msgs--;
> +			res = 0;
> +		} else {
> +			/* No, return EAGAIN in non-blocking mode or wait */
> +			res = -EAGAIN;

IMHO, it will be better if you revert the if logic:

	mutex_lock()
	if (!block || !fh->queued_msgs) {
		mutex_unlock();
		return -EAGAIN;
	}

	// the code that handles queues message

	That eliminates one indentation and reduce the code complexity.

> +		}
> +		mutex_unlock(&fh->lock);
> +		/* Return when in non-blocking mode or if we have a message */
> +		if (!block || !res)
> +			break;
> +
> +		if (msg->timeout) {
> +			/* The user specified a timeout */
> +			res = wait_event_interruptible_timeout(fh->wait,
> +				fh->queued_msgs,
> +				msecs_to_jiffies(msg->timeout));
> +			if (res == 0)
> +				res = -ETIMEDOUT;
> +			else if (res > 0)
> +				res = 0;
> +		} else {
> +			/* Wait indefinitely */
> +			res = wait_event_interruptible(fh->wait,
> +				fh->queued_msgs);
> +		}
> +		/* Exit on error, otherwise loop to get the new message */
> +	} while (!res);
> +	return res;
> +}
> +
> +static bool cec_is_busy(const struct cec_adapter *adap,
> +			const struct cec_fh *fh)
> +{
> +	bool valid_initiator = adap->cec_initiator && adap->cec_initiator == fh;
> +	bool valid_follower = adap->cec_follower && adap->cec_follower == fh;
> +
> +	/*
> +	 * Exclusive initiators and followers can always access the CEC adapter
> +	 */
> +	if (valid_initiator || valid_follower)
> +		return false;
> +	/*
> +	 * All others can only access the CEC adapter if there is no
> +	 * exclusive initiator and they are in INITIATOR mode.
> +	 */
> +	return adap->cec_initiator != NULL ||
> +	       fh->mode_initiator == CEC_MODE_NO_INITIATOR;
> +}
> +
> +static long cec_ioctl(struct file *filp, unsigned cmd, unsigned long arg)
> +{
> +	struct cec_devnode *devnode = cec_devnode_data(filp);
> +	struct cec_fh *fh = filp->private_data;
> +	struct cec_adapter *adap = fh->adap;
> +	bool block = !(filp->f_flags & O_NONBLOCK);
> +	void __user *parg = (void __user *)arg;
> +	int err = 0;
> +
> +	if (!devnode->registered)
> +		return -EIO;
> +
> +	switch (cmd) {
> +	case CEC_ADAP_G_CAPS: {

I don't like very much handling the ioctl code directly inside a switch.
That makes the code complex. It is usually better to have one function
per each ioctl and use the switch (or some other structure) to call the
function for each ioctl.

> +		struct cec_caps caps = {};
> +
> +		strlcpy(caps.driver, adap->devnode.parent->driver->name,
> +			sizeof(caps.driver));
> +		strlcpy(caps.name, adap->name, sizeof(caps.name));
> +		caps.available_log_addrs = adap->available_log_addrs;
> +		caps.capabilities = adap->capabilities;
> +		caps.version = LINUX_VERSION_CODE;
> +		if (copy_to_user(parg, &caps, sizeof(caps)))
> +			return -EFAULT;

Shouldn't it return an error here if !adap->is_configured?

> +		break;
> +	}
> +
> +	case CEC_TRANSMIT: {
> +		struct cec_msg msg = {};
> +
> +		if (!(adap->capabilities & CEC_CAP_TRANSMIT))
> +			return -ENOTTY;
> +		if (copy_from_user(&msg, parg, sizeof(msg)))
> +			return -EFAULT;
> +		mutex_lock(&adap->lock);
> +		if (!adap->is_configured) {
> +			err = -ENONET;
> +		} else if (cec_is_busy(adap, fh)) {
> +			err = -EBUSY;

Better to use gotos here, instead of all those nested ifs for the
error conditions, or to just do:
	mutex_unlock()
	return errorcode;

I don't like very much to copy_from_user() if it won't use
the data copied. So, maybe we could first test everything
and only then copy the data. The side effect is that it would
copy with the mutex locked. Not sure about the impacts, if any.

Of course, if we change it here, we'll need to do the same
changes for all ioctls.


> +		} else {
> +			if (block || !msg.reply)
> +				fh = NULL;
> +			err = cec_transmit_msg_fh(adap, &msg, fh, block);
> +		}
> +		mutex_unlock(&adap->lock);
> +		if (err)
> +			return err;
> +		if (copy_to_user(parg, &msg, sizeof(msg)))
> +			return -EFAULT;
> +		break;
> +	}
> +
> +	case CEC_RECEIVE: {
> +		struct cec_msg msg = {};
> +
> +		if (copy_from_user(&msg, parg, sizeof(msg)))
> +			return -EFAULT;
> +		mutex_lock(&adap->lock);
> +		if (!adap->is_configured)
> +			err = -ENONET;
> +		mutex_unlock(&adap->lock);
> +		if (err)
> +			return err;
> +
> +		err = cec_receive_msg(fh, &msg, block);
> +		if (err)
> +			return err;
> +		if (copy_to_user(parg, &msg, sizeof(msg)))
> +			return -EFAULT;
> +		break;
> +	}
> +
> +	case CEC_DQEVENT: {
> +		struct cec_event_queue *evq = NULL;
> +		struct cec_event *ev = NULL;
> +		u64 ts = ~0ULL;
> +		unsigned i;
> +
> +		mutex_lock(&fh->lock);
> +		/* Find the oldest event */
> +		for (i = 0; i < CEC_NUM_EVENTS; i++) {
> +			struct cec_event_queue *q = fh->evqueue + i;
> +
> +			if (q->num_events && q->events->ts <= ts) {
> +				evq = q;
> +				ev = q->events;
> +				ts = ev->ts;
> +			}
> +		}
> +		err = -EAGAIN;
> +		if (ev) {
> +			if (copy_to_user(parg, ev, sizeof(*ev))) {
> +				err = -EFAULT;
> +			} else {
> +				unsigned j;
> +
> +				evq->num_events--;
> +				fh->events--;
> +				/*
> +				 * Reset lost message counter after returning
> +				 * this event.
> +				 */
> +				if (ev->event == CEC_EVENT_LOST_MSGS)
> +					fh->lost_msgs = 0;
> +				for (j = 0; j < evq->num_events; j++)
> +					evq->events[j] = evq->events[j + 1];
> +				err = 0;

IMHO, it would be better to mass-pass all events to userspace
and just empty the queue here. As mentioned before, I would also
flag the user if there were event overflows and some events got
discarded.

> +			}
> +		}
> +		mutex_unlock(&fh->lock);
> +		return err;
> +	}
> +
> +	case CEC_ADAP_G_PHYS_ADDR: {
> +		u16 phys_addr;
> +
> +		mutex_lock(&adap->lock);
> +		phys_addr = adap->phys_addr;
> +		if (copy_to_user(parg, &phys_addr, sizeof(adap->phys_addr)))
> +			err = -EFAULT;
> +		mutex_unlock(&adap->lock);
> +		break;
> +	}
> +
> +	case CEC_ADAP_S_PHYS_ADDR: {
> +		u16 phys_addr;
> +
> +		if (!(adap->capabilities & CEC_CAP_PHYS_ADDR))
> +			return -ENOTTY;
> +		if (copy_from_user(&phys_addr, parg, sizeof(phys_addr)))
> +			return -EFAULT;
> +
> +		err = cec_phys_addr_validate(phys_addr, NULL, NULL);
> +		if (err)
> +			return err;
> +		mutex_lock(&adap->lock);
> +		if (cec_is_busy(adap, fh))
> +			err = -EBUSY;
> +		else
> +			__cec_s_phys_addr(adap, phys_addr, block);
> +		mutex_unlock(&adap->lock);
> +		break;
> +	}
> +
> +	case CEC_ADAP_G_LOG_ADDRS: {
> +		struct cec_log_addrs log_addrs;
> +
> +		mutex_lock(&adap->lock);
> +		log_addrs = adap->log_addrs;
> +		if (!adap->is_configured)
> +			memset(log_addrs.log_addr, CEC_LOG_ADDR_INVALID,
> +			       sizeof(log_addrs.log_addr));
> +		mutex_unlock(&adap->lock);
> +
> +		if (copy_to_user(parg, &log_addrs, sizeof(log_addrs)))
> +			return -EFAULT;
> +		break;
> +	}
> +
> +	case CEC_ADAP_S_LOG_ADDRS: {
> +		struct cec_log_addrs log_addrs;
> +
> +		if (!(adap->capabilities & CEC_CAP_LOG_ADDRS))
> +			return -ENOTTY;
> +		if (copy_from_user(&log_addrs, parg, sizeof(log_addrs)))
> +			return -EFAULT;
> +		log_addrs.flags = 0;
> +		mutex_lock(&adap->lock);
> +		if (adap->is_configuring)
> +			err = -EBUSY;
> +		else if (log_addrs.num_log_addrs && adap->is_configured)
> +			err = -EBUSY;
> +		else if (cec_is_busy(adap, fh))
> +			err = -EBUSY;
> +		else
> +			err = __cec_s_log_addrs(adap, &log_addrs, block);
> +		if (!err)
> +			log_addrs = adap->log_addrs;
> +		mutex_unlock(&adap->lock);
> +		if (!err && copy_to_user(parg, &log_addrs, sizeof(log_addrs)))
> +			return -EFAULT;
> +		break;
> +	}
> +
> +	case CEC_G_MODE: {
> +		u32 mode = fh->mode_initiator | fh->mode_follower;
> +
> +		if (copy_to_user(parg, &mode, sizeof(mode)))
> +			return -EFAULT;
> +		break;
> +	}
> +
> +	case CEC_S_MODE: {
> +		u32 mode;
> +		u8 mode_initiator;
> +		u8 mode_follower;
> +
> +		if (copy_from_user(&mode, parg, sizeof(mode)))
> +			return -EFAULT;
> +		if (mode & ~(CEC_MODE_INITIATOR_MSK | CEC_MODE_FOLLOWER_MSK))
> +			return -EINVAL;
> +
> +		mode_initiator = mode & CEC_MODE_INITIATOR_MSK;
> +		mode_follower = mode & CEC_MODE_FOLLOWER_MSK;
> +
> +		if (mode_initiator > CEC_MODE_EXCL_INITIATOR ||
> +		    mode_follower > CEC_MODE_MONITOR_ALL)
> +			return -EINVAL;
> +
> +		if (mode_follower == CEC_MODE_MONITOR_ALL &&
> +		    !(adap->capabilities & CEC_CAP_MONITOR_ALL))
> +			return -EINVAL;
> +
> +		/* Follower modes should always be able to send CEC messages */
> +		if ((mode_initiator == CEC_MODE_NO_INITIATOR ||
> +		    !(adap->capabilities & CEC_CAP_TRANSMIT)) &&
> +		    mode_follower >= CEC_MODE_FOLLOWER &&
> +		    mode_follower <= CEC_MODE_EXCL_FOLLOWER_PASSTHRU)
> +			return -EINVAL;
> +
> +		/* Monitor modes require CEC_MODE_NO_INITIATOR */
> +		if (mode_initiator && mode_follower >= CEC_MODE_MONITOR)
> +			return -EINVAL;
> +
> +		mutex_lock(&adap->lock);
> +		/*
> +		 * You can't become exclusive follower if someone else already
> +		 * has that job.
> +		 */
> +		if ((mode_follower == CEC_MODE_EXCL_FOLLOWER ||
> +		     mode_follower == CEC_MODE_EXCL_FOLLOWER_PASSTHRU) &&
> +		    adap->cec_follower && adap->cec_follower != fh)
> +			err = -EBUSY;
> +		/*
> +		 * You can't become exclusive initiator if someone else already
> +		 * has that job.
> +		 */
> +		if (mode_initiator == CEC_MODE_EXCL_INITIATOR &&
> +		    adap->cec_initiator && adap->cec_initiator != fh)
> +			err = -EBUSY;
> +
> +		if (!err) {
> +			bool old_mon_all = fh->mode_follower == CEC_MODE_MONITOR_ALL;
> +			bool new_mon_all = mode_follower == CEC_MODE_MONITOR_ALL;
> +
> +			if (old_mon_all != new_mon_all) {
> +				if (new_mon_all)
> +					err = cec_monitor_all_cnt_inc(adap);
> +				else
> +					cec_monitor_all_cnt_dec(adap);
> +			}
> +		}
> +
> +		if (err) {
> +			mutex_unlock(&adap->lock);
> +			break;
> +		}
> +
> +		if (fh->mode_follower == CEC_MODE_FOLLOWER)
> +			adap->follower_cnt--;
> +		if (mode_follower == CEC_MODE_FOLLOWER)
> +			adap->follower_cnt++;
> +		if (mode_follower == CEC_MODE_EXCL_FOLLOWER ||
> +		    mode_follower == CEC_MODE_EXCL_FOLLOWER_PASSTHRU) {
> +			adap->passthrough =
> +				mode_follower == CEC_MODE_EXCL_FOLLOWER_PASSTHRU;
> +			adap->cec_follower = fh;
> +		} else if (adap->cec_follower == fh) {
> +			adap->passthrough = false;
> +			adap->cec_follower = NULL;
> +		}
> +		if (mode_initiator == CEC_MODE_EXCL_INITIATOR)
> +			adap->cec_initiator = fh;
> +		else if (adap->cec_initiator == fh)
> +			adap->cec_initiator = NULL;
> +		fh->mode_initiator = mode_initiator;
> +		fh->mode_follower = mode_follower;
> +		mutex_unlock(&adap->lock);
> +		break;
> +	}
> +
> +	default:
> +		return -ENOTTY;
> +	}
> +	return err;
> +}
> +
> +static int cec_open(struct inode *inode, struct file *filp)
> +{
> +	struct cec_devnode *devnode =
> +		container_of(inode->i_cdev, struct cec_devnode, cdev);
> +	struct cec_adapter *adap = to_cec_adapter(devnode);
> +	struct cec_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL);
> +	/*
> +	 * Initial events that are automatically sent when the cec device is
> +	 * opened.
> +	 */
> +	struct cec_event ev_state = {
> +		.event = CEC_EVENT_STATE_CHANGE,
> +		.flags = CEC_EVENT_FL_INITIAL_STATE,
> +	};
> +	int ret;
> +
> +	if (fh == NULL)
> +		return -ENOMEM;
> +
> +	ret = cec_queue_event_init(fh);
> +
> +	if (ret) {
> +		kfree(fh);
> +		return ret;
> +	}
> +
> +	INIT_LIST_HEAD(&fh->msgs);
> +	INIT_LIST_HEAD(&fh->xfer_list);
> +	mutex_init(&fh->lock);
> +	init_waitqueue_head(&fh->wait);
> +
> +	fh->mode_initiator = CEC_MODE_INITIATOR;
> +	fh->adap = adap;
> +
> +	/*
> +	 * Check if the cec device is available. This needs to be done with
> +	 * the cec_devnode_lock held to prevent an open/unregister race:
> +	 * without the lock, the device could be unregistered and freed between
> +	 * the devnode->registered check and get_device() calls, leading to
> +	 * a crash.
> +	 */
> +	mutex_lock(&cec_devnode_lock);
> +	/*
> +	 * return ENXIO if the cec device has been removed
> +	 * already or if it is not registered anymore.
> +	 */
> +	if (!devnode->registered) {
> +		mutex_unlock(&cec_devnode_lock);
> +		cec_queue_event_free(fh);
> +		kfree(fh);
> +		return -ENXIO;
> +	}
> +	/* and increase the device refcount */
> +	get_device(&devnode->dev);
> +	mutex_unlock(&cec_devnode_lock);
> +
> +	filp->private_data = fh;
> +
> +	mutex_lock(&devnode->fhs_lock);
> +	/* Queue up initial state events */
> +	ev_state.state_change.phys_addr = adap->phys_addr;
> +	ev_state.state_change.log_addr_mask = adap->log_addrs.log_addr_mask;
> +	cec_queue_event_fh(fh, &ev_state, 0);
> +
> +	list_add(&fh->list, &devnode->fhs);
> +	mutex_unlock(&devnode->fhs_lock);
> +
> +	return 0;
> +}
> +
> +/* Override for the release function */
> +static int cec_release(struct inode *inode, struct file *filp)
> +{
> +	struct cec_devnode *devnode = cec_devnode_data(filp);
> +	struct cec_adapter *adap = to_cec_adapter(devnode);
> +	struct cec_fh *fh = filp->private_data;
> +
> +	mutex_lock(&adap->lock);
> +	if (adap->cec_initiator == fh)
> +		adap->cec_initiator = NULL;
> +	if (adap->cec_follower == fh) {
> +		adap->cec_follower = NULL;
> +		adap->passthrough = false;
> +	}
> +	if (fh->mode_follower == CEC_MODE_FOLLOWER)
> +		adap->follower_cnt--;
> +	if (fh->mode_follower == CEC_MODE_MONITOR_ALL)
> +		cec_monitor_all_cnt_dec(adap);
> +	mutex_unlock(&adap->lock);
> +
> +	mutex_lock(&devnode->fhs_lock);
> +	list_del(&fh->list);
> +	mutex_unlock(&devnode->fhs_lock);
> +
> +	/* Unhook pending transmits from this filehandle. */
> +	mutex_lock(&adap->lock);
> +	while (!list_empty(&fh->xfer_list)) {
> +		struct cec_data *data =
> +			list_first_entry(&fh->xfer_list, struct cec_data, xfer_list);
> +
> +		data->blocking = false;
> +		data->fh = NULL;
> +		list_del(&data->xfer_list);
> +	}
> +	mutex_unlock(&adap->lock);
> +	while (!list_empty(&fh->msgs)) {
> +		struct cec_msg_entry *entry =
> +			list_first_entry(&fh->msgs, struct cec_msg_entry, list);
> +
> +		list_del(&entry->list);
> +		kfree(entry);
> +	}
> +	cec_queue_event_free(fh);
> +	kfree(fh);
> +
> +	/*
> +	 * decrease the refcount unconditionally since the release()
> +	 * return value is ignored.
> +	 */
> +	put_device(&devnode->dev);
> +	filp->private_data = NULL;
> +	return 0;
> +}
> +
> +static const struct file_operations cec_devnode_fops = {
> +	.owner = THIS_MODULE,
> +	.open = cec_open,
> +	.unlocked_ioctl = cec_ioctl,
> +	.release = cec_release,
> +	.poll = cec_poll,
> +	.llseek = no_llseek,
> +};
> +
> +/* Called when the last user of the cec device exits. */
> +static void cec_devnode_release(struct device *cd)
> +{
> +	struct cec_devnode *devnode = to_cec_devnode(cd);
> +
> +	mutex_lock(&cec_devnode_lock);
> +
> +	/* Mark device node number as free */
> +	clear_bit(devnode->minor, cec_devnode_nums);
> +
> +	mutex_unlock(&cec_devnode_lock);
> +	cec_delete_adapter(to_cec_adapter(devnode));
> +}
> +
> +static struct bus_type cec_bus_type = {
> +	.name = CEC_NAME,
> +};
> +
> +/**
> + * cec_devnode_register - register a cec device node
> + * @devnode: cec device node structure we want to register
> + *
> + * The registration code assigns minor numbers and registers the new device node
> + * with the kernel. An error is returned if no free minor number can be found,
> + * or if the registration of the device node fails.
> + *
> + * Zero is returned on success.
> + *
> + * Note that if the cec_devnode_register call fails, the release() callback of
> + * the cec_devnode structure is *not* called, so the caller is responsible for
> + * freeing any data.
> + */

This is a matter of taste, but I very much prefer to have the
comments at the header files, and for all functions exported
with EXPORT_SYMBOL_GPL(). I don't mind if you're willing to
document non-exported functions like the one below, provided that
all exported ones are documented, and the CEC header added to
device-driver.tmpl.

> +static int __must_check cec_devnode_register(struct cec_devnode *devnode,
> +		struct module *owner)
> +{
> +	int minor;
> +	int ret;
> +
> +	/* Initialization */
> +	INIT_LIST_HEAD(&devnode->fhs);
> +	mutex_init(&devnode->fhs_lock);
> +
> +	/* Part 1: Find a free minor number */
> +	mutex_lock(&cec_devnode_lock);
> +	minor = find_next_zero_bit(cec_devnode_nums, CEC_NUM_DEVICES, 0);
> +	if (minor == CEC_NUM_DEVICES) {
> +		mutex_unlock(&cec_devnode_lock);
> +		pr_err("could not get a free minor\n");
> +		return -ENFILE;
> +	}
> +
> +	set_bit(minor, cec_devnode_nums);
> +	mutex_unlock(&cec_devnode_lock);
> +
> +	devnode->minor = minor;
> +	devnode->dev.bus = &cec_bus_type;
> +	devnode->dev.devt = MKDEV(MAJOR(cec_dev_t), minor);
> +	devnode->dev.release = cec_devnode_release;
> +	devnode->dev.parent = devnode->parent;
> +	dev_set_name(&devnode->dev, "cec%d", devnode->minor);
> +	device_initialize(&devnode->dev);
> +
> +	/* Part 2: Initialize and register the character device */
> +	cdev_init(&devnode->cdev, &cec_devnode_fops);
> +	devnode->cdev.kobj.parent = &devnode->dev.kobj;
> +	devnode->cdev.owner = owner;
> +
> +	ret = cdev_add(&devnode->cdev, devnode->dev.devt, 1);
> +	if (ret < 0) {
> +		pr_err("%s: cdev_add failed\n", __func__);
> +		goto clr_bit;
> +	}
> +
> +	ret = device_add(&devnode->dev);
> +	if (ret)
> +		goto cdev_del;
> +
> +	devnode->registered = true;
> +	return 0;
> +
> +cdev_del:
> +	cdev_del(&devnode->cdev);
> +clr_bit:
> +	clear_bit(devnode->minor, cec_devnode_nums);
> +	put_device(&devnode->dev);
> +	return ret;
> +}

Hmm... not sure how you're freeing the memory allocated
for cec_devnode. Drivers can't free it directly, as a file
descriptor may be opened. I would expect to se a kref()
or some similar mechanism to control its lifecycle.


> +
> +/**
> + * cec_devnode_unregister - unregister a cec device node
> + * @devnode: the device node to unregister
> + *
> + * This unregisters the passed device. Future open calls will be met with
> + * errors.
> + *
> + * This function can safely be called if the device node has never been
> + * registered or has already been unregistered.
> + */
> +static void cec_devnode_unregister(struct cec_devnode *devnode)
> +{
> +	struct cec_fh *fh;
> +
> +	/* Check if devnode was never registered or already unregistered */
> +	if (!devnode->registered || devnode->unregistered)
> +		return;
> +
> +	mutex_lock(&devnode->fhs_lock);
> +	list_for_each_entry(fh, &devnode->fhs, list)
> +		wake_up_interruptible(&fh->wait);
> +	mutex_unlock(&devnode->fhs_lock);
> +
> +	devnode->registered = false;
> +	devnode->unregistered = true;
> +	device_del(&devnode->dev);
> +	cdev_del(&devnode->cdev);
> +	put_device(&devnode->dev);
> +}
> +
> +struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
> +	       void *priv, const char *name, u32 caps, u8 available_las,
> +	       struct device *parent)
> +{
> +	struct cec_adapter *adap;
> +	int res;
> +
> +	if (WARN_ON(!parent))
> +		return ERR_PTR(-EINVAL);
> +	if (WARN_ON(!caps))
> +		return ERR_PTR(-EINVAL);
> +	if (WARN_ON(!ops))
> +		return ERR_PTR(-EINVAL);
> +	if (WARN_ON(!available_las || available_las > CEC_MAX_LOG_ADDRS))
> +		return ERR_PTR(-EINVAL);
> +	adap = kzalloc(sizeof(*adap), GFP_KERNEL);
> +	if (adap == NULL)
> +		return ERR_PTR(-ENOMEM);
> +	adap->owner = parent->driver->owner;
> +	adap->devnode.parent = parent;
> +	strlcpy(adap->name, name, sizeof(adap->name));
> +	adap->phys_addr = CEC_PHYS_ADDR_INVALID;
> +	adap->log_addrs.cec_version = CEC_OP_CEC_VERSION_2_0;
> +	adap->log_addrs.vendor_id = CEC_VENDOR_ID_NONE;
> +	adap->capabilities = caps;
> +	adap->available_log_addrs = available_las;
> +	adap->sequence = 0;
> +	adap->ops = ops;
> +	adap->priv = priv;
> +	memset(adap->phys_addrs, 0xff, sizeof(adap->phys_addrs));
> +	mutex_init(&adap->lock);
> +	INIT_LIST_HEAD(&adap->transmit_queue);
> +	INIT_LIST_HEAD(&adap->wait_queue);
> +	init_waitqueue_head(&adap->kthread_waitq);
> +
> +	adap->kthread = kthread_run(cec_thread_func, adap, "cec-%s", name);
> +	if (IS_ERR(adap->kthread)) {
> +		pr_err("cec-%s: kernel_thread() failed\n", name);
> +		res = PTR_ERR(adap->kthread);
> +		kfree(adap);
> +		return ERR_PTR(res);
> +	}
> +
> +	if (!(caps & CEC_CAP_RC))
> +		return adap;
> +
> +#if IS_ENABLED(CONFIG_RC_CORE)
> +	/* Prepare the RC input device */
> +	adap->rc = rc_allocate_device();
> +	if (!adap->rc) {
> +		pr_err("cec-%s: failed to allocate memory for rc_dev\n",
> +		       name);
> +		kthread_stop(adap->kthread);
> +		kfree(adap);
> +		return ERR_PTR(-ENOMEM);
> +	}
> +
> +	snprintf(adap->input_name, sizeof(adap->input_name),
> +		 "RC for %s", name);
> +	snprintf(adap->input_phys, sizeof(adap->input_phys),
> +		 "%s/input0", name);
> +
> +	adap->rc->input_name = adap->input_name;
> +	adap->rc->input_phys = adap->input_phys;
> +	adap->rc->input_id.bustype = BUS_CEC;
> +	adap->rc->input_id.vendor = 0;
> +	adap->rc->input_id.product = 0;
> +	adap->rc->input_id.version = 1;
> +	adap->rc->dev.parent = parent;
> +	adap->rc->driver_type = RC_DRIVER_SCANCODE;
> +	adap->rc->allowed_protocols = RC_BIT_CEC;
> +	adap->rc->priv = adap;
> +	adap->rc->map_name = RC_MAP_CEC;
> +	adap->rc->timeout = MS_TO_NS(100);
> +#else
> +	adap->capabilities &= ~CEC_CAP_RC;
> +#endif
> +	return adap;
> +}
> +EXPORT_SYMBOL_GPL(cec_allocate_adapter);
> +
> +int cec_register_adapter(struct cec_adapter *adap)
> +{
> +	int res;
> +
> +	if (IS_ERR_OR_NULL(adap))
> +		return 0;
> +
> +#if IS_ENABLED(CONFIG_RC_CORE)
> +	if (adap->capabilities & CEC_CAP_RC) {
> +		res = rc_register_device(adap->rc);
> +
> +		if (res) {
> +			pr_err("cec-%s: failed to prepare input device\n",
> +			       adap->name);
> +			rc_free_device(adap->rc);
> +			adap->rc = NULL;
> +			return res;
> +		}
> +	}
> +#endif
> +
> +	res = cec_devnode_register(&adap->devnode, adap->owner);
> +#if IS_ENABLED(CONFIG_RC_CORE)
> +	if (res) {
> +		/* Note: rc_unregister also calls rc_free */
> +		rc_unregister_device(adap->rc);
> +		adap->rc = NULL;
> +		return res;
> +	}
> +#endif
> +
> +	dev_set_drvdata(&adap->devnode.dev, adap);
> +	if (top_cec_dir == NULL)
> +		return 0;
> +
> +	adap->cec_dir = debugfs_create_dir(dev_name(&adap->devnode.dev), top_cec_dir);
> +	if (IS_ERR_OR_NULL(adap->cec_dir)) {
> +		pr_warn("cec-%s: Failed to create debugfs dir\n", adap->name);
> +		return 0;
> +	}
> +	adap->status_file = debugfs_create_devm_seqfile(&adap->devnode.dev,
> +		"status", adap->cec_dir, cec_status);
> +	if (IS_ERR_OR_NULL(adap->status_file)) {
> +		pr_warn("cec-%s: Failed to create status file\n", adap->name);
> +		debugfs_remove_recursive(adap->cec_dir);
> +		adap->cec_dir = NULL;
> +	}

Hmm... shouldn't the above check if CONFIG_DEBUG_FS? Also, IMHO,
this should be an optional feature, as people may not want it on
production systems.

> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(cec_register_adapter);
> +
> +void cec_unregister_adapter(struct cec_adapter *adap)
> +{
> +	if (IS_ERR_OR_NULL(adap))
> +		return;
> +
> +#if IS_ENABLED(CONFIG_RC_CORE)
> +	/* Note: rc_unregister also calls rc_free */
> +	rc_unregister_device(adap->rc);
> +	adap->rc = NULL;
> +#endif
> +	debugfs_remove_recursive(adap->cec_dir);
> +	cec_devnode_unregister(&adap->devnode);
> +}
> +EXPORT_SYMBOL_GPL(cec_unregister_adapter);
> +
> +void cec_delete_adapter(struct cec_adapter *adap)
> +{
> +	if (IS_ERR_OR_NULL(adap))
> +		return;
> +	mutex_lock(&adap->lock);
> +	__cec_s_phys_addr(adap, CEC_PHYS_ADDR_INVALID, false);
> +	mutex_unlock(&adap->lock);
> +	kthread_stop(adap->kthread);
> +	if (adap->kthread_config)
> +		kthread_stop(adap->kthread_config);
> +#if IS_ENABLED(CONFIG_RC_CORE)
> +	if (adap->rc)
> +		rc_free_device(adap->rc);
> +#endif
> +	kfree(adap);
> +}
> +EXPORT_SYMBOL_GPL(cec_delete_adapter);
> +
> +/*
> + *	Initialise cec for linux
> + */
> +static int __init cec_devnode_init(void)
> +{
> +	int ret;
> +
> +	pr_info("Linux cec interface: v0.10\n");
> +	ret = alloc_chrdev_region(&cec_dev_t, 0, CEC_NUM_DEVICES,
> +				  CEC_NAME);
> +	if (ret < 0) {
> +		pr_warn("cec: unable to allocate major\n");
> +		return ret;
> +	}
> +
> +	top_cec_dir = debugfs_create_dir("cec", NULL);
> +	if (IS_ERR_OR_NULL(top_cec_dir)) {
> +		pr_warn("cec: Failed to create debugfs cec dir\n");
> +		top_cec_dir = NULL;
> +	}
> +
> +	ret = bus_register(&cec_bus_type);
> +	if (ret < 0) {
> +		unregister_chrdev_region(cec_dev_t, CEC_NUM_DEVICES);
> +		pr_warn("cec: bus_register failed\n");
> +		return -EIO;
> +	}
> +
> +	return 0;
> +}
> +
> +static void __exit cec_devnode_exit(void)
> +{
> +	debugfs_remove_recursive(top_cec_dir);
> +	bus_unregister(&cec_bus_type);
> +	unregister_chrdev_region(cec_dev_t, CEC_NUM_DEVICES);
> +}
> +
> +subsys_initcall(cec_devnode_init);
> +module_exit(cec_devnode_exit)
> +
> +MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>");
> +MODULE_DESCRIPTION("Device node registration for cec drivers");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/cec-funcs.h b/include/linux/cec-funcs.h
> new file mode 100644
> index 0000000..25c37bb
> --- /dev/null
> +++ b/include/linux/cec-funcs.h
> @@ -0,0 +1,1871 @@
> +/*
> + * cec - HDMI Consumer Electronics Control message functions
> + *
> + * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
> + *
> + * This program is free software; you may redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 2 of the License.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
> + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
> + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
> + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> + * SOFTWARE.
> + */
> +
> +#ifndef _CEC_UAPI_FUNCS_H
> +#define _CEC_UAPI_FUNCS_H
> +
> +#include <linux/cec.h>
> +
> +/* One Touch Play Feature */
> +static inline void cec_msg_active_source(struct cec_msg *msg, __u16 phys_addr)
> +{
> +	msg->len = 4;
> +	msg->msg[0] |= 0xf; /* broadcast */
> +	msg->msg[1] = CEC_MSG_ACTIVE_SOURCE;
> +	msg->msg[2] = phys_addr >> 8;
> +	msg->msg[3] = phys_addr & 0xff;
> +}
> +
> +static inline void cec_ops_active_source(const struct cec_msg *msg,
> +					 __u16 *phys_addr)
> +{
> +	*phys_addr = (msg->msg[2] << 8) | msg->msg[3];
> +}
> +
> +static inline void cec_msg_image_view_on(struct cec_msg *msg)
> +{
> +	msg->len = 2;
> +	msg->msg[1] = CEC_MSG_IMAGE_VIEW_ON;
> +}
> +
> +static inline void cec_msg_text_view_on(struct cec_msg *msg)
> +{
> +	msg->len = 2;
> +	msg->msg[1] = CEC_MSG_TEXT_VIEW_ON;
> +}
> +
> +
> +/* Routing Control Feature */
> +static inline void cec_msg_inactive_source(struct cec_msg *msg,
> +					   __u16 phys_addr)
> +{
> +	msg->len = 4;
> +	msg->msg[1] = CEC_MSG_INACTIVE_SOURCE;
> +	msg->msg[2] = phys_addr >> 8;
> +	msg->msg[3] = phys_addr & 0xff;
> +}
> +
> +static inline void cec_ops_inactive_source(const struct cec_msg *msg,
> +					   __u16 *phys_addr)
> +{
> +	*phys_addr = (msg->msg[2] << 8) | msg->msg[3];
> +}
> +
> +static inline void cec_msg_request_active_source(struct cec_msg *msg,
> +						 bool reply)
> +{
> +	msg->len = 2;
> +	msg->msg[0] |= 0xf; /* broadcast */
> +	msg->msg[1] = CEC_MSG_REQUEST_ACTIVE_SOURCE;
> +	msg->reply = reply ? CEC_MSG_ACTIVE_SOURCE : 0;
> +}
> +
> +static inline void cec_msg_routing_information(struct cec_msg *msg,
> +					       __u16 phys_addr)
> +{
> +	msg->len = 4;
> +	msg->msg[0] |= 0xf; /* broadcast */
> +	msg->msg[1] = CEC_MSG_ROUTING_INFORMATION;
> +	msg->msg[2] = phys_addr >> 8;
> +	msg->msg[3] = phys_addr & 0xff;
> +}
> +
> +static inline void cec_ops_routing_information(const struct cec_msg *msg,
> +					       __u16 *phys_addr)
> +{
> +	*phys_addr = (msg->msg[2] << 8) | msg->msg[3];
> +}
> +
> +static inline void cec_msg_routing_change(struct cec_msg *msg,
> +					  bool reply,
> +					  __u16 orig_phys_addr,
> +					  __u16 new_phys_addr)
> +{
> +	msg->len = 6;
> +	msg->msg[0] |= 0xf; /* broadcast */
> +	msg->msg[1] = CEC_MSG_ROUTING_CHANGE;
> +	msg->msg[2] = orig_phys_addr >> 8;
> +	msg->msg[3] = orig_phys_addr & 0xff;
> +	msg->msg[4] = new_phys_addr >> 8;
> +	msg->msg[5] = new_phys_addr & 0xff;
> +	msg->reply = reply ? CEC_MSG_ROUTING_INFORMATION : 0;
> +}
> +
> +static inline void cec_ops_routing_change(const struct cec_msg *msg,
> +					  __u16 *orig_phys_addr,
> +					  __u16 *new_phys_addr)
> +{
> +	*orig_phys_addr = (msg->msg[2] << 8) | msg->msg[3];
> +	*new_phys_addr = (msg->msg[4] << 8) | msg->msg[5];
> +}
> +
> +static inline void cec_msg_set_stream_path(struct cec_msg *msg, __u16 phys_addr)
> +{
> +	msg->len = 4;
> +	msg->msg[0] |= 0xf; /* broadcast */
> +	msg->msg[1] = CEC_MSG_SET_STREAM_PATH;
> +	msg->msg[2] = phys_addr >> 8;
> +	msg->msg[3] = phys_addr & 0xff;
> +}
> +
> +static inline void cec_ops_set_stream_path(const struct cec_msg *msg,
> +					   __u16 *phys_addr)
> +{
> +	*phys_addr = (msg->msg[2] << 8) | msg->msg[3];
> +}
> +
> +
> +/* Standby Feature */
> +static inline void cec_msg_standby(struct cec_msg *msg)
> +{
> +	msg->len = 2;
> +	msg->msg[1] = CEC_MSG_STANDBY;
> +}
> +
> +
> +/* One Touch Record Feature */
> +static inline void cec_msg_record_off(struct cec_msg *msg)
> +{
> +	msg->len = 2;
> +	msg->msg[1] = CEC_MSG_RECORD_OFF;
> +}
> +
> +struct cec_op_arib_data {
> +	__u16 transport_id;
> +	__u16 service_id;
> +	__u16 orig_network_id;
> +};
> +
> +struct cec_op_atsc_data {
> +	__u16 transport_id;
> +	__u16 program_number;
> +};
> +
> +struct cec_op_dvb_data {
> +	__u16 transport_id;
> +	__u16 service_id;
> +	__u16 orig_network_id;
> +};
> +
> +struct cec_op_channel_data {
> +	__u8 channel_number_fmt;
> +	__u16 major;
> +	__u16 minor;
> +};
> +
> +struct cec_op_digital_service_id {
> +	__u8 service_id_method;
> +	__u8 dig_bcast_system;
> +	union {
> +		struct cec_op_arib_data arib;
> +		struct cec_op_atsc_data atsc;
> +		struct cec_op_dvb_data dvb;
> +		struct cec_op_channel_data channel;
> +	};
> +};
> +
> +struct cec_op_record_src {
> +	__u8 type;
> +	union {
> +		struct cec_op_digital_service_id digital;
> +		struct {
> +			__u8 ana_bcast_type;
> +			__u16 ana_freq;
> +			__u8 bcast_system;
> +		} analog;
> +		struct {
> +			__u8 plug;
> +		} ext_plug;
> +		struct {
> +			__u16 phys_addr;
> +		} ext_phys_addr;
> +	};
> +};

Shouldn't the above be using __le or __be annotations (and the code
below using the endiannes functions to read/write the data)?

> +
> +static inline void cec_set_digital_service_id(__u8 *msg,
> +	      const struct cec_op_digital_service_id *digital)
> +{
> +	*msg++ = (digital->service_id_method << 7) | digital->dig_bcast_system;
> +	if (digital->service_id_method == CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL) {
> +		*msg++ = (digital->channel.channel_number_fmt << 2) |
> +			 (digital->channel.major >> 8);
> +		*msg++ = digital->channel.major && 0xff;
> +		*msg++ = digital->channel.minor >> 8;
> +		*msg++ = digital->channel.minor & 0xff;
> +		*msg++ = 0;
> +		*msg++ = 0;
> +		return;
> +	}
> +	switch (digital->dig_bcast_system) {
> +	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_GEN:
> +	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_CABLE:
> +	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_SAT:
> +	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_T:
> +		*msg++ = digital->atsc.transport_id >> 8;
> +		*msg++ = digital->atsc.transport_id & 0xff;
> +		*msg++ = digital->atsc.program_number >> 8;
> +		*msg++ = digital->atsc.program_number & 0xff;
> +		*msg++ = 0;
> +		*msg++ = 0;
> +		break;
> +	default:
> +		*msg++ = digital->dvb.transport_id >> 8;
> +		*msg++ = digital->dvb.transport_id & 0xff;
> +		*msg++ = digital->dvb.service_id >> 8;
> +		*msg++ = digital->dvb.service_id & 0xff;
> +		*msg++ = digital->dvb.orig_network_id >> 8;
> +		*msg++ = digital->dvb.orig_network_id & 0xff;
> +		break;
> +	}
> +}
> +
> +static inline void cec_get_digital_service_id(const __u8 *msg,
> +	      struct cec_op_digital_service_id *digital)
> +{
> +	digital->service_id_method = msg[0] >> 7;
> +	digital->dig_bcast_system = msg[0] & 0x7f;
> +	if (digital->service_id_method == CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL) {
> +		digital->channel.channel_number_fmt = msg[1] >> 2;
> +		digital->channel.major = ((msg[1] & 3) << 6) | msg[2];
> +		digital->channel.minor = (msg[3] << 8) | msg[4];
> +		return;
> +	}
> +	digital->dvb.transport_id = (msg[1] << 8) | msg[2];
> +	digital->dvb.service_id = (msg[3] << 8) | msg[4];
> +	digital->dvb.orig_network_id = (msg[5] << 8) | msg[6];
> +}
> +
> +static inline void cec_msg_record_on_own(struct cec_msg *msg)
> +{
> +	msg->len = 3;
> +	msg->msg[1] = CEC_MSG_RECORD_ON;
> +	msg->msg[2] = CEC_OP_RECORD_SRC_OWN;
> +}
> +
> +static inline void cec_msg_record_on_digital(struct cec_msg *msg,
> +			     const struct cec_op_digital_service_id *digital)
> +{
> +	msg->len = 10;
> +	msg->msg[1] = CEC_MSG_RECORD_ON;
> +	msg->msg[2] = CEC_OP_RECORD_SRC_DIGITAL;
> +	cec_set_digital_service_id(msg->msg + 3, digital);
> +}
> +
> +static inline void cec_msg_record_on_analog(struct cec_msg *msg,
> +					    __u8 ana_bcast_type,
> +					    __u16 ana_freq,
> +					    __u8 bcast_system)
> +{
> +	msg->len = 7;
> +	msg->msg[1] = CEC_MSG_RECORD_ON;
> +	msg->msg[2] = CEC_OP_RECORD_SRC_ANALOG;
> +	msg->msg[3] = ana_bcast_type;
> +	msg->msg[4] = ana_freq >> 8;
> +	msg->msg[5] = ana_freq & 0xff;
> +	msg->msg[6] = bcast_system;
> +}
> +
> +static inline void cec_msg_record_on_plug(struct cec_msg *msg,
> +					  __u8 plug)
> +{
> +	msg->len = 4;
> +	msg->msg[1] = CEC_MSG_RECORD_ON;
> +	msg->msg[2] = CEC_OP_RECORD_SRC_EXT_PLUG;
> +	msg->msg[3] = plug;
> +}
> +
> +static inline void cec_msg_record_on_phys_addr(struct cec_msg *msg,
> +					       __u16 phys_addr)
> +{
> +	msg->len = 5;
> +	msg->msg[1] = CEC_MSG_RECORD_ON;
> +	msg->msg[2] = CEC_OP_RECORD_SRC_EXT_PHYS_ADDR;
> +	msg->msg[3] = phys_addr >> 8;
> +	msg->msg[4] = phys_addr & 0xff;
> +}
> +
> +static inline void cec_msg_record_on(struct cec_msg *msg,
> +				     const struct cec_op_record_src *rec_src)
> +{
> +	switch (rec_src->type) {
> +	case CEC_OP_RECORD_SRC_OWN:
> +		cec_msg_record_on_own(msg);
> +		break;
> +	case CEC_OP_RECORD_SRC_DIGITAL:
> +		cec_msg_record_on_digital(msg, &rec_src->digital);
> +		break;
> +	case CEC_OP_RECORD_SRC_ANALOG:
> +		cec_msg_record_on_analog(msg,
> +					 rec_src->analog.ana_bcast_type,
> +					 rec_src->analog.ana_freq,
> +					 rec_src->analog.bcast_system);
> +		break;
> +	case CEC_OP_RECORD_SRC_EXT_PLUG:
> +		cec_msg_record_on_plug(msg, rec_src->ext_plug.plug);
> +		break;
> +	case CEC_OP_RECORD_SRC_EXT_PHYS_ADDR:
> +		cec_msg_record_on_phys_addr(msg,
> +					    rec_src->ext_phys_addr.phys_addr);
> +		break;
> +	}
> +}
> +
> +static inline void cec_ops_record_on(const struct cec_msg *msg,
> +				     struct cec_op_record_src *rec_src)
> +{
> +	rec_src->type = msg->msg[2];
> +	switch (rec_src->type) {
> +	case CEC_OP_RECORD_SRC_OWN:
> +		break;
> +	case CEC_OP_RECORD_SRC_DIGITAL:
> +		cec_get_digital_service_id(msg->msg + 3, &rec_src->digital);
> +		break;
> +	case CEC_OP_RECORD_SRC_ANALOG:
> +		rec_src->analog.ana_bcast_type = msg->msg[3];
> +		rec_src->analog.ana_freq =
> +			(msg->msg[4] << 8) | msg->msg[5];
> +		rec_src->analog.bcast_system = msg->msg[6];
> +		break;
> +	case CEC_OP_RECORD_SRC_EXT_PLUG:
> +		rec_src->ext_plug.plug = msg->msg[3];
> +		break;
> +	case CEC_OP_RECORD_SRC_EXT_PHYS_ADDR:
> +		rec_src->ext_phys_addr.phys_addr =
> +			(msg->msg[3] << 8) | msg->msg[4];
> +		break;
> +	}
> +}
> +
> +static inline void cec_msg_record_status(struct cec_msg *msg, __u8 rec_status)
> +{
> +	msg->len = 3;
> +	msg->msg[1] = CEC_MSG_RECORD_STATUS;
> +	msg->msg[2] = rec_status;
> +}
> +
> +static inline void cec_ops_record_status(const struct cec_msg *msg,
> +					 __u8 *rec_status)
> +{
> +	*rec_status = msg->msg[2];
> +}
> +
> +static inline void cec_msg_record_tv_screen(struct cec_msg *msg,
> +					    bool reply)
> +{
> +	msg->len = 2;
> +	msg->msg[1] = CEC_MSG_RECORD_TV_SCREEN;
> +	msg->reply = reply ? CEC_MSG_RECORD_ON : 0;
> +}
> +
> +
> +/* Timer Programming Feature */
> +static inline void cec_msg_timer_status(struct cec_msg *msg,
> +					__u8 timer_overlap_warning,
> +					__u8 media_info,
> +					__u8 prog_info,
> +					__u8 prog_error,
> +					__u8 duration_hr,
> +					__u8 duration_min)
> +{
> +	msg->len = 3;
> +	msg->msg[1] = CEC_MSG_TIMER_STATUS;
> +	msg->msg[2] = (timer_overlap_warning << 7) |
> +		(media_info << 5) |
> +		(prog_info ? 0x10 : 0) |
> +		(prog_info ? prog_info : prog_error);
> +	if (prog_info == CEC_OP_PROG_INFO_NOT_ENOUGH_SPACE ||
> +	    prog_info == CEC_OP_PROG_INFO_MIGHT_NOT_BE_ENOUGH_SPACE ||
> +	    prog_error == CEC_OP_PROG_ERROR_DUPLICATE) {
> +		msg->len += 2;
> +		msg->msg[3] = ((duration_hr / 10) << 4) | (duration_hr % 10);
> +		msg->msg[4] = ((duration_min / 10) << 4) | (duration_min % 10);
> +	}
> +}
> +
> +static inline void cec_ops_timer_status(struct cec_msg *msg,
> +					__u8 *timer_overlap_warning,
> +					__u8 *media_info,
> +					__u8 *prog_info,
> +					__u8 *prog_error,
> +					__u8 *duration_hr,
> +					__u8 *duration_min)
> +{
> +	*timer_overlap_warning = msg->msg[2] >> 7;
> +	*media_info = (msg->msg[2] >> 5) & 3;
> +	if (msg->msg[2] & 0x10) {
> +		*prog_info = msg->msg[2] & 0xf;
> +		*prog_error = 0;
> +	} else {
> +		*prog_info = 0;
> +		*prog_error = msg->msg[2] & 0xf;
> +	}
> +	if (*prog_info == CEC_OP_PROG_INFO_NOT_ENOUGH_SPACE ||
> +	    *prog_info == CEC_OP_PROG_INFO_MIGHT_NOT_BE_ENOUGH_SPACE ||
> +	    *prog_error == CEC_OP_PROG_ERROR_DUPLICATE) {
> +		*duration_hr = (msg->msg[3] >> 4) * 10 + (msg->msg[3] & 0xf);
> +		*duration_min = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf);
> +	} else {
> +		*duration_hr = *duration_min = 0;
> +	}
> +}
> +
> +static inline void cec_msg_timer_cleared_status(struct cec_msg *msg,
> +						__u8 timer_cleared_status)
> +{
> +	msg->len = 3;
> +	msg->msg[1] = CEC_MSG_TIMER_CLEARED_STATUS;
> +	msg->msg[2] = timer_cleared_status;
> +}
> +
> +static inline void cec_ops_timer_cleared_status(struct cec_msg *msg,
> +						__u8 *timer_cleared_status)
> +{
> +	*timer_cleared_status = msg->msg[2];
> +}
> +
> +static inline void cec_msg_clear_analogue_timer(struct cec_msg *msg,
> +						bool reply,
> +						__u8 day,
> +						__u8 month,
> +						__u8 start_hr,
> +						__u8 start_min,
> +						__u8 duration_hr,
> +						__u8 duration_min,
> +						__u8 recording_seq,
> +						__u8 ana_bcast_type,
> +						__u16 ana_freq,
> +						__u8 bcast_system)
> +{
> +	msg->len = 13;
> +	msg->msg[1] = CEC_MSG_CLEAR_ANALOGUE_TIMER;
> +	msg->msg[2] = day;
> +	msg->msg[3] = month;
> +	/* Hours and minutes are in BCD format */
> +	msg->msg[4] = ((start_hr / 10) << 4) | (start_hr % 10);
> +	msg->msg[5] = ((start_min / 10) << 4) | (start_min % 10);
> +	msg->msg[6] = ((duration_hr / 10) << 4) | (duration_hr % 10);
> +	msg->msg[7] = ((duration_min / 10) << 4) | (duration_min % 10);
> +	msg->msg[8] = recording_seq;
> +	msg->msg[9] = ana_bcast_type;
> +	msg->msg[10] = ana_freq >> 8;
> +	msg->msg[11] = ana_freq & 0xff;
> +	msg->msg[12] = bcast_system;
> +	msg->reply = reply ? CEC_MSG_TIMER_CLEARED_STATUS : 0;
> +}
> +
> +static inline void cec_ops_clear_analogue_timer(struct cec_msg *msg,
> +						__u8 *day,
> +						__u8 *month,
> +						__u8 *start_hr,
> +						__u8 *start_min,
> +						__u8 *duration_hr,
> +						__u8 *duration_min,
> +						__u8 *recording_seq,
> +						__u8 *ana_bcast_type,
> +						__u16 *ana_freq,
> +						__u8 *bcast_system)
> +{
> +	*day = msg->msg[2];
> +	*month = msg->msg[3];
> +	/* Hours and minutes are in BCD format */
> +	*start_hr = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf);
> +	*start_min = (msg->msg[5] >> 4) * 10 + (msg->msg[5] & 0xf);
> +	*duration_hr = (msg->msg[6] >> 4) * 10 + (msg->msg[6] & 0xf);
> +	*duration_min = (msg->msg[7] >> 4) * 10 + (msg->msg[7] & 0xf);
> +	*recording_seq = msg->msg[8];
> +	*ana_bcast_type = msg->msg[9];
> +	*ana_freq = (msg->msg[10] << 8) | msg->msg[11];
> +	*bcast_system = msg->msg[12];
> +}
> +
> +static inline void cec_msg_clear_digital_timer(struct cec_msg *msg,
> +					       bool reply,
> +					       __u8 day,
> +					       __u8 month,
> +					       __u8 start_hr,
> +					       __u8 start_min,
> +					       __u8 duration_hr,
> +					       __u8 duration_min,
> +					       __u8 recording_seq,
> +					       const struct cec_op_digital_service_id *digital)
> +{
> +	msg->len = 16;
> +	msg->reply = reply ? CEC_MSG_TIMER_CLEARED_STATUS : 0;
> +	msg->msg[1] = CEC_MSG_CLEAR_DIGITAL_TIMER;
> +	msg->msg[2] = day;
> +	msg->msg[3] = month;
> +	/* Hours and minutes are in BCD format */
> +	msg->msg[4] = ((start_hr / 10) << 4) | (start_hr % 10);
> +	msg->msg[5] = ((start_min / 10) << 4) | (start_min % 10);
> +	msg->msg[6] = ((duration_hr / 10) << 4) | (duration_hr % 10);
> +	msg->msg[7] = ((duration_min / 10) << 4) | (duration_min % 10);
> +	msg->msg[8] = recording_seq;
> +	cec_set_digital_service_id(msg->msg + 9, digital);
> +}
> +
> +static inline void cec_ops_clear_digital_timer(struct cec_msg *msg,
> +					       __u8 *day,
> +					       __u8 *month,
> +					       __u8 *start_hr,
> +					       __u8 *start_min,
> +					       __u8 *duration_hr,
> +					       __u8 *duration_min,
> +					       __u8 *recording_seq,
> +					       struct cec_op_digital_service_id *digital)
> +{
> +	*day = msg->msg[2];
> +	*month = msg->msg[3];
> +	/* Hours and minutes are in BCD format */
> +	*start_hr = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf);
> +	*start_min = (msg->msg[5] >> 4) * 10 + (msg->msg[5] & 0xf);
> +	*duration_hr = (msg->msg[6] >> 4) * 10 + (msg->msg[6] & 0xf);
> +	*duration_min = (msg->msg[7] >> 4) * 10 + (msg->msg[7] & 0xf);
> +	*recording_seq = msg->msg[8];
> +	cec_get_digital_service_id(msg->msg + 9, digital);
> +}
> +
> +static inline void cec_msg_clear_ext_timer(struct cec_msg *msg,
> +					   bool reply,
> +					   __u8 day,
> +					   __u8 month,
> +					   __u8 start_hr,
> +					   __u8 start_min,
> +					   __u8 duration_hr,
> +					   __u8 duration_min,
> +					   __u8 recording_seq,
> +					   __u8 ext_src_spec,
> +					   __u8 plug,
> +					   __u16 phys_addr)
> +{
> +	msg->len = 13;
> +	msg->msg[1] = CEC_MSG_CLEAR_EXT_TIMER;
> +	msg->msg[2] = day;
> +	msg->msg[3] = month;
> +	/* Hours and minutes are in BCD format */
> +	msg->msg[4] = ((start_hr / 10) << 4) | (start_hr % 10);
> +	msg->msg[5] = ((start_min / 10) << 4) | (start_min % 10);
> +	msg->msg[6] = ((duration_hr / 10) << 4) | (duration_hr % 10);
> +	msg->msg[7] = ((duration_min / 10) << 4) | (duration_min % 10);
> +	msg->msg[8] = recording_seq;
> +	msg->msg[9] = ext_src_spec;
> +	msg->msg[10] = plug;
> +	msg->msg[11] = phys_addr >> 8;
> +	msg->msg[12] = phys_addr & 0xff;
> +	msg->reply = reply ? CEC_MSG_TIMER_CLEARED_STATUS : 0;
> +}
> +
> +static inline void cec_ops_clear_ext_timer(struct cec_msg *msg,
> +					   __u8 *day,
> +					   __u8 *month,
> +					   __u8 *start_hr,
> +					   __u8 *start_min,
> +					   __u8 *duration_hr,
> +					   __u8 *duration_min,
> +					   __u8 *recording_seq,
> +					   __u8 *ext_src_spec,
> +					   __u8 *plug,
> +					   __u16 *phys_addr)
> +{
> +	*day = msg->msg[2];
> +	*month = msg->msg[3];
> +	/* Hours and minutes are in BCD format */
> +	*start_hr = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf);
> +	*start_min = (msg->msg[5] >> 4) * 10 + (msg->msg[5] & 0xf);
> +	*duration_hr = (msg->msg[6] >> 4) * 10 + (msg->msg[6] & 0xf);
> +	*duration_min = (msg->msg[7] >> 4) * 10 + (msg->msg[7] & 0xf);
> +	*recording_seq = msg->msg[8];
> +	*ext_src_spec = msg->msg[9];
> +	*plug = msg->msg[10];
> +	*phys_addr = (msg->msg[11] << 8) | msg->msg[12];
> +}
> +
> +static inline void cec_msg_set_analogue_timer(struct cec_msg *msg,
> +					      bool reply,
> +					      __u8 day,
> +					      __u8 month,
> +					      __u8 start_hr,
> +					      __u8 start_min,
> +					      __u8 duration_hr,
> +					      __u8 duration_min,
> +					      __u8 recording_seq,
> +					      __u8 ana_bcast_type,
> +					      __u16 ana_freq,
> +					      __u8 bcast_system)
> +{
> +	msg->len = 13;
> +	msg->msg[1] = CEC_MSG_SET_ANALOGUE_TIMER;
> +	msg->msg[2] = day;
> +	msg->msg[3] = month;
> +	/* Hours and minutes are in BCD format */
> +	msg->msg[4] = ((start_hr / 10) << 4) | (start_hr % 10);
> +	msg->msg[5] = ((start_min / 10) << 4) | (start_min % 10);
> +	msg->msg[6] = ((duration_hr / 10) << 4) | (duration_hr % 10);
> +	msg->msg[7] = ((duration_min / 10) << 4) | (duration_min % 10);
> +	msg->msg[8] = recording_seq;
> +	msg->msg[9] = ana_bcast_type;
> +	msg->msg[10] = ana_freq >> 8;
> +	msg->msg[11] = ana_freq & 0xff;
> +	msg->msg[12] = bcast_system;
> +	msg->reply = reply ? CEC_MSG_TIMER_STATUS : 0;
> +}
> +
> +static inline void cec_ops_set_analogue_timer(struct cec_msg *msg,
> +					      __u8 *day,
> +					      __u8 *month,
> +					      __u8 *start_hr,
> +					      __u8 *start_min,
> +					      __u8 *duration_hr,
> +					      __u8 *duration_min,
> +					      __u8 *recording_seq,
> +					      __u8 *ana_bcast_type,
> +					      __u16 *ana_freq,
> +					      __u8 *bcast_system)
> +{
> +	*day = msg->msg[2];
> +	*month = msg->msg[3];
> +	/* Hours and minutes are in BCD format */
> +	*start_hr = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf);
> +	*start_min = (msg->msg[5] >> 4) * 10 + (msg->msg[5] & 0xf);
> +	*duration_hr = (msg->msg[6] >> 4) * 10 + (msg->msg[6] & 0xf);
> +	*duration_min = (msg->msg[7] >> 4) * 10 + (msg->msg[7] & 0xf);
> +	*recording_seq = msg->msg[8];
> +	*ana_bcast_type = msg->msg[9];
> +	*ana_freq = (msg->msg[10] << 8) | msg->msg[11];
> +	*bcast_system = msg->msg[12];
> +}
> +
> +static inline void cec_msg_set_digital_timer(struct cec_msg *msg,
> +					     bool reply,
> +					     __u8 day,
> +					     __u8 month,
> +					     __u8 start_hr,
> +					     __u8 start_min,
> +					     __u8 duration_hr,
> +					     __u8 duration_min,
> +					     __u8 recording_seq,
> +					     const struct cec_op_digital_service_id *digital)
> +{
> +	msg->len = 16;
> +	msg->reply = reply ? CEC_MSG_TIMER_STATUS : 0;
> +	msg->msg[1] = CEC_MSG_SET_DIGITAL_TIMER;
> +	msg->msg[2] = day;
> +	msg->msg[3] = month;
> +	/* Hours and minutes are in BCD format */
> +	msg->msg[4] = ((start_hr / 10) << 4) | (start_hr % 10);
> +	msg->msg[5] = ((start_min / 10) << 4) | (start_min % 10);
> +	msg->msg[6] = ((duration_hr / 10) << 4) | (duration_hr % 10);
> +	msg->msg[7] = ((duration_min / 10) << 4) | (duration_min % 10);
> +	msg->msg[8] = recording_seq;
> +	cec_set_digital_service_id(msg->msg + 9, digital);
> +}
> +
> +static inline void cec_ops_set_digital_timer(struct cec_msg *msg,
> +					     __u8 *day,
> +					     __u8 *month,
> +					     __u8 *start_hr,
> +					     __u8 *start_min,
> +					     __u8 *duration_hr,
> +					     __u8 *duration_min,
> +					     __u8 *recording_seq,
> +					     struct cec_op_digital_service_id *digital)
> +{
> +	*day = msg->msg[2];
> +	*month = msg->msg[3];
> +	/* Hours and minutes are in BCD format */
> +	*start_hr = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf);
> +	*start_min = (msg->msg[5] >> 4) * 10 + (msg->msg[5] & 0xf);
> +	*duration_hr = (msg->msg[6] >> 4) * 10 + (msg->msg[6] & 0xf);
> +	*duration_min = (msg->msg[7] >> 4) * 10 + (msg->msg[7] & 0xf);
> +	*recording_seq = msg->msg[8];
> +	cec_get_digital_service_id(msg->msg + 9, digital);
> +}
> +
> +static inline void cec_msg_set_ext_timer(struct cec_msg *msg,
> +					 bool reply,
> +					 __u8 day,
> +					 __u8 month,
> +					 __u8 start_hr,
> +					 __u8 start_min,
> +					 __u8 duration_hr,
> +					 __u8 duration_min,
> +					 __u8 recording_seq,
> +					 __u8 ext_src_spec,
> +					 __u8 plug,
> +					 __u16 phys_addr)
> +{
> +	msg->len = 13;
> +	msg->msg[1] = CEC_MSG_SET_EXT_TIMER;
> +	msg->msg[2] = day;
> +	msg->msg[3] = month;
> +	/* Hours and minutes are in BCD format */
> +	msg->msg[4] = ((start_hr / 10) << 4) | (start_hr % 10);
> +	msg->msg[5] = ((start_min / 10) << 4) | (start_min % 10);
> +	msg->msg[6] = ((duration_hr / 10) << 4) | (duration_hr % 10);
> +	msg->msg[7] = ((duration_min / 10) << 4) | (duration_min % 10);
> +	msg->msg[8] = recording_seq;
> +	msg->msg[9] = ext_src_spec;
> +	msg->msg[10] = plug;
> +	msg->msg[11] = phys_addr >> 8;
> +	msg->msg[12] = phys_addr & 0xff;
> +	msg->reply = reply ? CEC_MSG_TIMER_STATUS : 0;
> +}
> +
> +static inline void cec_ops_set_ext_timer(struct cec_msg *msg,
> +					 __u8 *day,
> +					 __u8 *month,
> +					 __u8 *start_hr,
> +					 __u8 *start_min,
> +					 __u8 *duration_hr,
> +					 __u8 *duration_min,
> +					 __u8 *recording_seq,
> +					 __u8 *ext_src_spec,
> +					 __u8 *plug,
> +					 __u16 *phys_addr)
> +{
> +	*day = msg->msg[2];
> +	*month = msg->msg[3];
> +	/* Hours and minutes are in BCD format */
> +	*start_hr = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf);
> +	*start_min = (msg->msg[5] >> 4) * 10 + (msg->msg[5] & 0xf);
> +	*duration_hr = (msg->msg[6] >> 4) * 10 + (msg->msg[6] & 0xf);
> +	*duration_min = (msg->msg[7] >> 4) * 10 + (msg->msg[7] & 0xf);
> +	*recording_seq = msg->msg[8];
> +	*ext_src_spec = msg->msg[9];
> +	*plug = msg->msg[10];
> +	*phys_addr = (msg->msg[11] << 8) | msg->msg[12];
> +}
> +
> +static inline void cec_msg_set_timer_program_title(struct cec_msg *msg,
> +						   const char *prog_title)
> +{
> +	unsigned len = strlen(prog_title);
> +
> +	if (len > 14)
> +		len = 14;
> +	msg->len = 2 + len;
> +	msg->msg[1] = CEC_MSG_SET_TIMER_PROGRAM_TITLE;
> +	memcpy(msg->msg + 2, prog_title, len);
> +}
> +
> +static inline void cec_ops_set_timer_program_title(const struct cec_msg *msg,
> +						   char *prog_title)
> +{
> +	unsigned len = msg->len - 2;
> +
> +	if (len > 14)
> +		len = 14;
> +	memcpy(prog_title, msg->msg + 2, len);
> +	prog_title[len] = '\0';
> +}
> +
> +/* System Information Feature */
> +static inline void cec_msg_cec_version(struct cec_msg *msg, __u8 cec_version)
> +{
> +	msg->len = 3;
> +	msg->msg[1] = CEC_MSG_CEC_VERSION;
> +	msg->msg[2] = cec_version;
> +}
> +
> +static inline void cec_ops_cec_version(const struct cec_msg *msg,
> +				       __u8 *cec_version)
> +{
> +	*cec_version = msg->msg[2];
> +}
> +
> +static inline void cec_msg_get_cec_version(struct cec_msg *msg,
> +					   bool reply)
> +{
> +	msg->len = 2;
> +	msg->msg[1] = CEC_MSG_GET_CEC_VERSION;
> +	msg->reply = reply ? CEC_MSG_CEC_VERSION : 0;
> +}
> +
> +static inline void cec_msg_report_physical_addr(struct cec_msg *msg,
> +						__u16 phys_addr, __u8 prim_devtype)
> +{
> +	msg->len = 5;
> +	msg->msg[0] |= 0xf; /* broadcast */
> +	msg->msg[1] = CEC_MSG_REPORT_PHYSICAL_ADDR;
> +	msg->msg[2] = phys_addr >> 8;
> +	msg->msg[3] = phys_addr & 0xff;
> +	msg->msg[4] = prim_devtype;
> +}
> +
> +static inline void cec_ops_report_physical_addr(const struct cec_msg *msg,
> +						__u16 *phys_addr, __u8 *prim_devtype)
> +{
> +	*phys_addr = (msg->msg[2] << 8) | msg->msg[3];
> +	*prim_devtype = msg->msg[4];
> +}
> +
> +static inline void cec_msg_give_physical_addr(struct cec_msg *msg,
> +					      bool reply)
> +{
> +	msg->len = 2;
> +	msg->msg[1] = CEC_MSG_GIVE_PHYSICAL_ADDR;
> +	msg->reply = reply ? CEC_MSG_REPORT_PHYSICAL_ADDR : 0;
> +}
> +
> +static inline void cec_msg_set_menu_language(struct cec_msg *msg,
> +					     const char *language)
> +{
> +	msg->len = 5;
> +	msg->msg[0] |= 0xf; /* broadcast */
> +	msg->msg[1] = CEC_MSG_SET_MENU_LANGUAGE;
> +	memcpy(msg->msg + 2, language, 3);
> +}
> +
> +static inline void cec_ops_set_menu_language(struct cec_msg *msg,
> +					     char *language)
> +{
> +	memcpy(language, msg->msg + 2, 3);
> +}
> +
> +static inline void cec_msg_get_menu_language(struct cec_msg *msg,
> +					     bool reply)
> +{
> +	msg->len = 2;
> +	msg->msg[1] = CEC_MSG_GET_MENU_LANGUAGE;
> +	msg->reply = reply ? CEC_MSG_SET_MENU_LANGUAGE : 0;
> +}
> +
> +/*
> + * Assumes a single RC Profile byte and a single Device Features byte,
> + * i.e. no extended features are supported by this helper function.
> + *
> + * As of CEC 2.0 no extended features are defined, should those be added
> + * in the future, then this function needs to be adapted or a new function
> + * should be added.
> + */
> +static inline void cec_msg_report_features(struct cec_msg *msg,
> +					   __u8 cec_version, __u8 all_device_types,
> +					   __u8 rc_profile, __u8 dev_features)
> +{
> +	msg->len = 6;
> +	msg->msg[0] |= 0xf; /* broadcast */
> +	msg->msg[1] = CEC_MSG_REPORT_FEATURES;
> +	msg->msg[2] = cec_version;
> +	msg->msg[3] = all_device_types;
> +	msg->msg[4] = rc_profile;
> +	msg->msg[5] = dev_features;
> +}
> +
> +static inline void cec_ops_report_features(const struct cec_msg *msg,
> +					   __u8 *cec_version, __u8 *all_device_types,
> +					   const __u8 **rc_profile, const __u8 **dev_features)
> +{
> +	const __u8 *p = &msg->msg[4];
> +
> +	*cec_version = msg->msg[2];
> +	*all_device_types = msg->msg[3];
> +	*rc_profile = p;
> +	while (p < &msg->msg[14] && (*p & CEC_OP_FEAT_EXT))
> +		p++;
> +	if (!(*p & CEC_OP_FEAT_EXT)) {
> +		*dev_features = p + 1;
> +		while (p < &msg->msg[15] && (*p & CEC_OP_FEAT_EXT))
> +			p++;
> +	}
> +	if (*p & CEC_OP_FEAT_EXT)
> +		*rc_profile = *dev_features = NULL;
> +}
> +
> +static inline void cec_msg_give_features(struct cec_msg *msg,
> +					 bool reply)
> +{
> +	msg->len = 2;
> +	msg->msg[1] = CEC_MSG_GIVE_FEATURES;
> +	msg->reply = reply ? CEC_MSG_REPORT_FEATURES : 0;
> +}
> +
> +/* Deck Control Feature */
> +static inline void cec_msg_deck_control(struct cec_msg *msg,
> +					__u8 deck_control_mode)
> +{
> +	msg->len = 3;
> +	msg->msg[1] = CEC_MSG_DECK_CONTROL;
> +	msg->msg[2] = deck_control_mode;
> +}
> +
> +static inline void cec_ops_deck_control(struct cec_msg *msg,
> +					__u8 *deck_control_mode)
> +{
> +	*deck_control_mode = msg->msg[2];
> +}
> +
> +static inline void cec_msg_deck_status(struct cec_msg *msg,
> +				       __u8 deck_info)
> +{
> +	msg->len = 3;
> +	msg->msg[1] = CEC_MSG_DECK_STATUS;
> +	msg->msg[2] = deck_info;
> +}
> +
> +static inline void cec_ops_deck_status(struct cec_msg *msg,
> +				       __u8 *deck_info)
> +{
> +	*deck_info = msg->msg[2];
> +}
> +
> +static inline void cec_msg_give_deck_status(struct cec_msg *msg,
> +					    bool reply,
> +					    __u8 status_req)
> +{
> +	msg->len = 3;
> +	msg->msg[1] = CEC_MSG_GIVE_DECK_STATUS;
> +	msg->msg[2] = status_req;
> +	msg->reply = reply ? CEC_MSG_DECK_STATUS : 0;
> +}
> +
> +static inline void cec_ops_give_deck_status(struct cec_msg *msg,
> +					    __u8 *status_req)
> +{
> +	*status_req = msg->msg[2];
> +}
> +
> +static inline void cec_msg_play(struct cec_msg *msg,
> +				__u8 play_mode)
> +{
> +	msg->len = 3;
> +	msg->msg[1] = CEC_MSG_PLAY;
> +	msg->msg[2] = play_mode;
> +}
> +
> +static inline void cec_ops_play(struct cec_msg *msg,
> +				__u8 *play_mode)
> +{
> +	*play_mode = msg->msg[2];
> +}
> +
> +
> +/* Tuner Control Feature */
> +struct cec_op_tuner_device_info {
> +	__u8 rec_flag;
> +	__u8 tuner_display_info;
> +	bool is_analog;
> +	union {
> +		struct cec_op_digital_service_id digital;
> +		struct {
> +			__u8 ana_bcast_type;
> +			__u16 ana_freq;
> +			__u8 bcast_system;
> +		} analog;
> +	};
> +};
> +
> +static inline void cec_msg_tuner_device_status_analog(struct cec_msg *msg,
> +						      __u8 rec_flag,
> +						      __u8 tuner_display_info,
> +						      __u8 ana_bcast_type,
> +						      __u16 ana_freq,
> +						      __u8 bcast_system)
> +{
> +	msg->len = 7;
> +	msg->msg[1] = CEC_MSG_TUNER_DEVICE_STATUS;
> +	msg->msg[2] = (rec_flag << 7) | tuner_display_info;
> +	msg->msg[3] = ana_bcast_type;
> +	msg->msg[4] = ana_freq >> 8;
> +	msg->msg[5] = ana_freq & 0xff;
> +	msg->msg[6] = bcast_system;
> +}
> +
> +static inline void cec_msg_tuner_device_status_digital(struct cec_msg *msg,
> +		   __u8 rec_flag, __u8 tuner_display_info,
> +		   const struct cec_op_digital_service_id *digital)
> +{
> +	msg->len = 10;
> +	msg->msg[1] = CEC_MSG_TUNER_DEVICE_STATUS;
> +	msg->msg[2] = (rec_flag << 7) | tuner_display_info;
> +	cec_set_digital_service_id(msg->msg + 3, digital);
> +}
> +
> +static inline void cec_msg_tuner_device_status(struct cec_msg *msg,
> +			const struct cec_op_tuner_device_info *tuner_dev_info)
> +{
> +	if (tuner_dev_info->is_analog)
> +		cec_msg_tuner_device_status_analog(msg,
> +			tuner_dev_info->rec_flag,
> +			tuner_dev_info->tuner_display_info,
> +			tuner_dev_info->analog.ana_bcast_type,
> +			tuner_dev_info->analog.ana_freq,
> +			tuner_dev_info->analog.bcast_system);
> +	else
> +		cec_msg_tuner_device_status_digital(msg,
> +			tuner_dev_info->rec_flag,
> +			tuner_dev_info->tuner_display_info,
> +			&tuner_dev_info->digital);
> +}
> +
> +static inline void cec_ops_tuner_device_status(struct cec_msg *msg,
> +					       struct cec_op_tuner_device_info *tuner_dev_info)
> +{
> +	tuner_dev_info->is_analog = msg->len < 10;
> +	tuner_dev_info->rec_flag = msg->msg[2] >> 7;
> +	tuner_dev_info->tuner_display_info = msg->msg[2] & 0x7f;
> +	if (tuner_dev_info->is_analog) {
> +		tuner_dev_info->analog.ana_bcast_type = msg->msg[3];
> +		tuner_dev_info->analog.ana_freq = (msg->msg[4] << 8) | msg->msg[5];
> +		tuner_dev_info->analog.bcast_system = msg->msg[6];
> +		return;
> +	}
> +	cec_get_digital_service_id(msg->msg + 3, &tuner_dev_info->digital);
> +}
> +
> +static inline void cec_msg_give_tuner_device_status(struct cec_msg *msg,
> +						    bool reply,
> +						    __u8 status_req)
> +{
> +	msg->len = 3;
> +	msg->msg[1] = CEC_MSG_GIVE_TUNER_DEVICE_STATUS;
> +	msg->msg[2] = status_req;
> +	msg->reply = reply ? CEC_MSG_TUNER_DEVICE_STATUS : 0;
> +}
> +
> +static inline void cec_ops_give_tuner_device_status(struct cec_msg *msg,
> +						    __u8 *status_req)
> +{
> +	*status_req = msg->msg[2];
> +}
> +
> +static inline void cec_msg_select_analogue_service(struct cec_msg *msg,
> +						   __u8 ana_bcast_type,
> +						   __u16 ana_freq,
> +						   __u8 bcast_system)
> +{
> +	msg->len = 6;
> +	msg->msg[1] = CEC_MSG_SELECT_ANALOGUE_SERVICE;
> +	msg->msg[2] = ana_bcast_type;
> +	msg->msg[3] = ana_freq >> 8;
> +	msg->msg[4] = ana_freq & 0xff;
> +	msg->msg[5] = bcast_system;
> +}
> +
> +static inline void cec_ops_select_analogue_service(struct cec_msg *msg,
> +						   __u8 *ana_bcast_type,
> +						   __u16 *ana_freq,
> +						   __u8 *bcast_system)
> +{
> +	*ana_bcast_type = msg->msg[2];
> +	*ana_freq = (msg->msg[3] << 8) | msg->msg[4];
> +	*bcast_system = msg->msg[5];
> +}
> +
> +static inline void cec_msg_select_digital_service(struct cec_msg *msg,
> +						  const struct cec_op_digital_service_id *digital)
> +{
> +	msg->len = 9;
> +	msg->msg[1] = CEC_MSG_SELECT_DIGITAL_SERVICE;
> +	cec_set_digital_service_id(msg->msg + 2, digital);
> +}
> +
> +static inline void cec_ops_select_digital_service(struct cec_msg *msg,
> +						  struct cec_op_digital_service_id *digital)
> +{
> +	cec_get_digital_service_id(msg->msg + 2, digital);
> +}
> +
> +static inline void cec_msg_tuner_step_decrement(struct cec_msg *msg)
> +{
> +	msg->len = 2;
> +	msg->msg[1] = CEC_MSG_TUNER_STEP_DECREMENT;
> +}
> +
> +static inline void cec_msg_tuner_step_increment(struct cec_msg *msg)
> +{
> +	msg->len = 2;
> +	msg->msg[1] = CEC_MSG_TUNER_STEP_INCREMENT;
> +}
> +
> +
> +/* Vendor Specific Commands Feature */
> +static inline void cec_msg_device_vendor_id(struct cec_msg *msg, __u32 vendor_id)
> +{
> +	msg->len = 5;
> +	msg->msg[0] |= 0xf; /* broadcast */
> +	msg->msg[1] = CEC_MSG_DEVICE_VENDOR_ID;
> +	msg->msg[2] = vendor_id >> 16;
> +	msg->msg[3] = (vendor_id >> 8) & 0xff;
> +	msg->msg[4] = vendor_id & 0xff;
> +}
> +
> +static inline void cec_ops_device_vendor_id(const struct cec_msg *msg,
> +					    __u32 *vendor_id)
> +{
> +	*vendor_id = (msg->msg[2] << 16) | (msg->msg[3] << 8) | msg->msg[4];
> +}
> +
> +static inline void cec_msg_give_device_vendor_id(struct cec_msg *msg,
> +						 bool reply)
> +{
> +	msg->len = 2;
> +	msg->msg[1] = CEC_MSG_GIVE_DEVICE_VENDOR_ID;
> +	msg->reply = reply ? CEC_MSG_DEVICE_VENDOR_ID : 0;
> +}
> +
> +static inline void cec_msg_vendor_remote_button_up(struct cec_msg *msg)
> +{
> +	msg->len = 2;
> +	msg->msg[1] = CEC_MSG_VENDOR_REMOTE_BUTTON_UP;
> +}
> +
> +
> +/* OSD Display Feature */
> +static inline void cec_msg_set_osd_string(struct cec_msg *msg,
> +					  __u8 disp_ctl,
> +					  const char *osd)
> +{
> +	unsigned len = strlen(osd);
> +
> +	if (len > 13)
> +		len = 13;
> +	msg->len = 3 + len;
> +	msg->msg[1] = CEC_MSG_SET_OSD_STRING;
> +	msg->msg[2] = disp_ctl;
> +	memcpy(msg->msg + 3, osd, len);
> +}
> +
> +static inline void cec_ops_set_osd_string(const struct cec_msg *msg,
> +					  __u8 *disp_ctl,
> +					  char *osd)
> +{
> +	unsigned len = msg->len - 3;
> +
> +	*disp_ctl = msg->msg[2];
> +	if (len > 13)
> +		len = 13;
> +	memcpy(osd, msg->msg + 3, len);
> +	osd[len] = '\0';
> +}
> +
> +
> +/* Device OSD Transfer Feature */
> +static inline void cec_msg_set_osd_name(struct cec_msg *msg, const char *name)
> +{
> +	unsigned len = strlen(name);
> +
> +	if (len > 14)
> +		len = 14;
> +	msg->len = 2 + len;
> +	msg->msg[1] = CEC_MSG_SET_OSD_NAME;
> +	memcpy(msg->msg + 2, name, len);
> +}
> +
> +static inline void cec_ops_set_osd_name(const struct cec_msg *msg,
> +					char *name)
> +{
> +	unsigned len = msg->len - 2;
> +
> +	if (len > 14)
> +		len = 14;
> +	memcpy(name, msg->msg + 2, len);
> +	name[len] = '\0';
> +}
> +
> +static inline void cec_msg_give_osd_name(struct cec_msg *msg,
> +					 bool reply)
> +{
> +	msg->len = 2;
> +	msg->msg[1] = CEC_MSG_GIVE_OSD_NAME;
> +	msg->reply = reply ? CEC_MSG_SET_OSD_NAME : 0;
> +}
> +
> +
> +/* Device Menu Control Feature */
> +static inline void cec_msg_menu_status(struct cec_msg *msg,
> +				       __u8 menu_state)
> +{
> +	msg->len = 3;
> +	msg->msg[1] = CEC_MSG_MENU_STATUS;
> +	msg->msg[2] = menu_state;
> +}
> +
> +static inline void cec_ops_menu_status(struct cec_msg *msg,
> +				       __u8 *menu_state)
> +{
> +	*menu_state = msg->msg[2];
> +}
> +
> +static inline void cec_msg_menu_request(struct cec_msg *msg,
> +					bool reply,
> +					__u8 menu_req)
> +{
> +	msg->len = 3;
> +	msg->msg[1] = CEC_MSG_MENU_REQUEST;
> +	msg->msg[2] = menu_req;
> +	msg->reply = reply ? CEC_MSG_MENU_STATUS : 0;
> +}
> +
> +static inline void cec_ops_menu_request(struct cec_msg *msg,
> +					__u8 *menu_req)
> +{
> +	*menu_req = msg->msg[2];
> +}
> +
> +struct cec_op_ui_command {
> +	__u8 ui_cmd;
> +	bool has_opt_arg;
> +	union {
> +		struct cec_op_channel_data channel_identifier;
> +		__u8 ui_broadcast_type;
> +		__u8 ui_sound_presentation_control;
> +		__u8 play_mode;
> +		__u8 ui_function_media;
> +		__u8 ui_function_select_av_input;
> +		__u8 ui_function_select_audio_input;
> +	};
> +};
> +
> +static inline void cec_msg_user_control_pressed(struct cec_msg *msg,
> +						const struct cec_op_ui_command *ui_cmd)
> +{
> +	msg->len = 3;
> +	msg->msg[1] = CEC_MSG_USER_CONTROL_PRESSED;
> +	msg->msg[2] = ui_cmd->ui_cmd;
> +	if (!ui_cmd->has_opt_arg)
> +		return;
> +	switch (ui_cmd->ui_cmd) {
> +	case 0x56:
> +	case 0x57:
> +	case 0x60:
> +	case 0x68:
> +	case 0x69:
> +	case 0x6a:
> +		/* The optional operand is one byte for all these ui commands */
> +		msg->len++;
> +		msg->msg[3] = ui_cmd->play_mode;
> +		break;
> +	case 0x67:
> +		msg->len += 4;
> +		msg->msg[3] = (ui_cmd->channel_identifier.channel_number_fmt << 2) |
> +			      (ui_cmd->channel_identifier.major >> 8);
> +		msg->msg[4] = ui_cmd->channel_identifier.major && 0xff;
> +		msg->msg[5] = ui_cmd->channel_identifier.minor >> 8;
> +		msg->msg[6] = ui_cmd->channel_identifier.minor & 0xff;
> +		break;
> +	}
> +}
> +
> +static inline void cec_ops_user_control_pressed(struct cec_msg *msg,
> +						struct cec_op_ui_command *ui_cmd)
> +{
> +	ui_cmd->ui_cmd = msg->msg[2];
> +	ui_cmd->has_opt_arg = false;
> +	if (msg->len == 3)
> +		return;
> +	switch (ui_cmd->ui_cmd) {
> +	case 0x56:
> +	case 0x57:
> +	case 0x60:
> +	case 0x68:
> +	case 0x69:
> +	case 0x6a:
> +		/* The optional operand is one byte for all these ui commands */
> +		ui_cmd->play_mode = msg->msg[3];
> +		ui_cmd->has_opt_arg = true;
> +		break;
> +	case 0x67:
> +		if (msg->len < 7)
> +			break;
> +		ui_cmd->has_opt_arg = true;
> +		ui_cmd->channel_identifier.channel_number_fmt = msg->msg[3] >> 2;
> +		ui_cmd->channel_identifier.major = ((msg->msg[3] & 3) << 6) | msg->msg[4];
> +		ui_cmd->channel_identifier.minor = (msg->msg[5] << 8) | msg->msg[6];
> +		break;
> +	}
> +}
> +
> +static inline void cec_msg_user_control_released(struct cec_msg *msg)
> +{
> +	msg->len = 2;
> +	msg->msg[1] = CEC_MSG_USER_CONTROL_RELEASED;
> +}
> +
> +/* Remote Control Passthrough Feature */
> +
> +/* Power Status Feature */
> +static inline void cec_msg_report_power_status(struct cec_msg *msg,
> +					       __u8 pwr_state)
> +{
> +	msg->len = 3;
> +	msg->msg[1] = CEC_MSG_REPORT_POWER_STATUS;
> +	msg->msg[2] = pwr_state;
> +}
> +
> +static inline void cec_ops_report_power_status(const struct cec_msg *msg,
> +					       __u8 *pwr_state)
> +{
> +	*pwr_state = msg->msg[2];
> +}
> +
> +static inline void cec_msg_give_device_power_status(struct cec_msg *msg,
> +						    bool reply)
> +{
> +	msg->len = 2;
> +	msg->msg[1] = CEC_MSG_GIVE_DEVICE_POWER_STATUS;
> +	msg->reply = reply ? CEC_MSG_REPORT_POWER_STATUS : 0;
> +}
> +
> +/* General Protocol Messages */
> +static inline void cec_msg_feature_abort(struct cec_msg *msg,
> +					 __u8 abort_msg, __u8 reason)
> +{
> +	msg->len = 4;
> +	msg->msg[1] = CEC_MSG_FEATURE_ABORT;
> +	msg->msg[2] = abort_msg;
> +	msg->msg[3] = reason;
> +}
> +
> +static inline void cec_ops_feature_abort(const struct cec_msg *msg,
> +					 __u8 *abort_msg, __u8 *reason)
> +{
> +	*abort_msg = msg->msg[2];
> +	*reason = msg->msg[3];
> +}
> +
> +/* This changes the current message into a feature abort message */
> +static inline void cec_msg_reply_feature_abort(struct cec_msg *msg, __u8 reason)
> +{
> +	cec_msg_set_reply_to(msg, msg);
> +	msg->len = 4;
> +	msg->msg[2] = msg->msg[1];
> +	msg->msg[3] = reason;
> +	msg->msg[1] = CEC_MSG_FEATURE_ABORT;
> +}
> +
> +static inline void cec_msg_abort(struct cec_msg *msg)
> +{
> +	msg->len = 2;
> +	msg->msg[1] = CEC_MSG_ABORT;
> +}
> +
> +
> +/* System Audio Control Feature */
> +static inline void cec_msg_report_audio_status(struct cec_msg *msg,
> +					       __u8 aud_mute_status,
> +					       __u8 aud_vol_status)
> +{
> +	msg->len = 3;
> +	msg->msg[1] = CEC_MSG_REPORT_AUDIO_STATUS;
> +	msg->msg[2] = (aud_mute_status << 7) | (aud_vol_status & 0x7f);
> +}
> +
> +static inline void cec_ops_report_audio_status(const struct cec_msg *msg,
> +					       __u8 *aud_mute_status,
> +					       __u8 *aud_vol_status)
> +{
> +	*aud_mute_status = msg->msg[2] >> 7;
> +	*aud_vol_status = msg->msg[2] & 0x7f;
> +}
> +
> +static inline void cec_msg_give_audio_status(struct cec_msg *msg,
> +					     bool reply)
> +{
> +	msg->len = 2;
> +	msg->msg[1] = CEC_MSG_GIVE_AUDIO_STATUS;
> +	msg->reply = reply ? CEC_MSG_REPORT_AUDIO_STATUS : 0;
> +}
> +
> +static inline void cec_msg_set_system_audio_mode(struct cec_msg *msg,
> +						 __u8 sys_aud_status)
> +{
> +	msg->len = 3;
> +	msg->msg[1] = CEC_MSG_SET_SYSTEM_AUDIO_MODE;
> +	msg->msg[2] = sys_aud_status;
> +}
> +
> +static inline void cec_ops_set_system_audio_mode(const struct cec_msg *msg,
> +						 __u8 *sys_aud_status)
> +{
> +	*sys_aud_status = msg->msg[2];
> +}
> +
> +static inline void cec_msg_system_audio_mode_request(struct cec_msg *msg,
> +						     bool reply,
> +						     __u16 phys_addr)
> +{
> +	msg->len = phys_addr == 0xffff ? 2 : 4;
> +	msg->msg[1] = CEC_MSG_SYSTEM_AUDIO_MODE_REQUEST;
> +	msg->msg[2] = phys_addr >> 8;
> +	msg->msg[3] = phys_addr & 0xff;
> +	msg->reply = reply ? CEC_MSG_SET_SYSTEM_AUDIO_MODE : 0;
> +
> +}
> +
> +static inline void cec_ops_system_audio_mode_request(const struct cec_msg *msg,
> +						     __u16 *phys_addr)
> +{
> +	if (msg->len < 4)
> +		*phys_addr = 0xffff;
> +	else
> +		*phys_addr = (msg->msg[2] << 8) | msg->msg[3];
> +}
> +
> +static inline void cec_msg_system_audio_mode_status(struct cec_msg *msg,
> +						    __u8 sys_aud_status)
> +{
> +	msg->len = 3;
> +	msg->msg[1] = CEC_MSG_SYSTEM_AUDIO_MODE_STATUS;
> +	msg->msg[2] = sys_aud_status;
> +}
> +
> +static inline void cec_ops_system_audio_mode_status(const struct cec_msg *msg,
> +						    __u8 *sys_aud_status)
> +{
> +	*sys_aud_status = msg->msg[2];
> +}
> +
> +static inline void cec_msg_give_system_audio_mode_status(struct cec_msg *msg,
> +							 bool reply)
> +{
> +	msg->len = 2;
> +	msg->msg[1] = CEC_MSG_GIVE_SYSTEM_AUDIO_MODE_STATUS;
> +	msg->reply = reply ? CEC_MSG_SYSTEM_AUDIO_MODE_STATUS : 0;
> +}
> +
> +static inline void cec_msg_report_short_audio_descriptor(struct cec_msg *msg,
> +							 __u8 num_descriptors,
> +							 const __u32 *descriptors)
> +{
> +	unsigned i;
> +
> +	if (num_descriptors > 4)
> +		num_descriptors = 4;
> +	msg->len = 2 + num_descriptors * 3;
> +	msg->msg[1] = CEC_MSG_REPORT_SHORT_AUDIO_DESCRIPTOR;
> +	for (i = 0; i < num_descriptors; i++) {
> +		msg->msg[2 + i * 3] = (descriptors[i] >> 16) & 0xff;
> +		msg->msg[3 + i * 3] = (descriptors[i] >> 8) & 0xff;
> +		msg->msg[4 + i * 3] = descriptors[i] & 0xff;
> +	}
> +}
> +
> +static inline void cec_ops_report_short_audio_descriptor(const struct cec_msg *msg,
> +							 __u8 *num_descriptors,
> +							 __u32 *descriptors)
> +{
> +	unsigned i;
> +
> +	*num_descriptors = (msg->len - 2) / 3;
> +	if (*num_descriptors > 4)
> +		*num_descriptors = 4;
> +	for (i = 0; i < *num_descriptors; i++)
> +		descriptors[i] = (msg->msg[2 + i * 3] << 16) |
> +			(msg->msg[3 + i * 3] << 8) |
> +			msg->msg[4 + i * 3];
> +}
> +
> +static inline void cec_msg_request_short_audio_descriptor(struct cec_msg *msg,
> +							  __u8 num_descriptors,
> +							  const __u8 *audio_format_id,
> +							  const __u8 *audio_format_code)
> +{
> +	unsigned i;
> +
> +	if (num_descriptors > 4)
> +		num_descriptors = 4;
> +	msg->len = 2 + num_descriptors;
> +	msg->msg[1] = CEC_MSG_REQUEST_SHORT_AUDIO_DESCRIPTOR;
> +	for (i = 0; i < num_descriptors; i++)
> +		msg->msg[2 + i] = (audio_format_id[i] << 6) | (audio_format_code[i] & 0x3f);
> +}
> +
> +static inline void cec_ops_request_short_audio_descriptor(const struct cec_msg *msg,
> +							  __u8 *num_descriptors,
> +							  __u8 *audio_format_id,
> +							  __u8 *audio_format_code)
> +{
> +	unsigned i;
> +
> +	*num_descriptors = msg->len - 2;
> +	if (*num_descriptors > 4)
> +		*num_descriptors = 4;
> +	for (i = 0; i < *num_descriptors; i++) {
> +		audio_format_id[i] = msg->msg[2 + i] >> 6;
> +		audio_format_code[i] = msg->msg[2 + i] & 0x3f;
> +	}
> +}
> +
> +
> +/* Audio Rate Control Feature */
> +static inline void cec_msg_set_audio_rate(struct cec_msg *msg,
> +					  __u8 audio_rate)
> +{
> +	msg->len = 3;
> +	msg->msg[1] = CEC_MSG_SET_AUDIO_RATE;
> +	msg->msg[2] = audio_rate;
> +}
> +
> +static inline void cec_ops_set_audio_rate(const struct cec_msg *msg,
> +					  __u8 *audio_rate)
> +{
> +	*audio_rate = msg->msg[2];
> +}
> +
> +
> +/* Audio Return Channel Control Feature */
> +static inline void cec_msg_report_arc_initiated(struct cec_msg *msg)
> +{
> +	msg->len = 2;
> +	msg->msg[1] = CEC_MSG_REPORT_ARC_INITIATED;
> +}
> +
> +static inline void cec_msg_initiate_arc(struct cec_msg *msg,
> +					bool reply)
> +{
> +	msg->len = 2;
> +	msg->msg[1] = CEC_MSG_INITIATE_ARC;
> +	msg->reply = reply ? CEC_MSG_REPORT_ARC_INITIATED : 0;
> +}
> +
> +static inline void cec_msg_request_arc_initiation(struct cec_msg *msg,
> +						  bool reply)
> +{
> +	msg->len = 2;
> +	msg->msg[1] = CEC_MSG_REQUEST_ARC_INITIATION;
> +	msg->reply = reply ? CEC_MSG_INITIATE_ARC : 0;
> +}
> +
> +static inline void cec_msg_report_arc_terminated(struct cec_msg *msg)
> +{
> +	msg->len = 2;
> +	msg->msg[1] = CEC_MSG_REPORT_ARC_TERMINATED;
> +}
> +
> +static inline void cec_msg_terminate_arc(struct cec_msg *msg,
> +					 bool reply)
> +{
> +	msg->len = 2;
> +	msg->msg[1] = CEC_MSG_TERMINATE_ARC;
> +	msg->reply = reply ? CEC_MSG_REPORT_ARC_TERMINATED : 0;
> +}
> +
> +static inline void cec_msg_request_arc_termination(struct cec_msg *msg,
> +						   bool reply)
> +{
> +	msg->len = 2;
> +	msg->msg[1] = CEC_MSG_REQUEST_ARC_TERMINATION;
> +	msg->reply = reply ? CEC_MSG_TERMINATE_ARC : 0;
> +}
> +
> +
> +/* Dynamic Audio Lipsync Feature */
> +/* Only for CEC 2.0 and up */
> +static inline void cec_msg_report_current_latency(struct cec_msg *msg,
> +						  __u16 phys_addr,
> +						  __u8 video_latency,
> +						  __u8 low_latency_mode,
> +						  __u8 audio_out_compensated,
> +						  __u8 audio_out_delay)
> +{
> +	msg->len = 7;
> +	msg->msg[1] = CEC_MSG_REPORT_CURRENT_LATENCY;
> +	msg->msg[2] = phys_addr >> 8;
> +	msg->msg[3] = phys_addr & 0xff;
> +	msg->msg[4] = video_latency;
> +	msg->msg[5] = (low_latency_mode << 2) | audio_out_compensated;
> +	msg->msg[6] = audio_out_delay;
> +}
> +
> +static inline void cec_ops_report_current_latency(const struct cec_msg *msg,
> +						  __u16 *phys_addr,
> +						  __u8 *video_latency,
> +						  __u8 *low_latency_mode,
> +						  __u8 *audio_out_compensated,
> +						  __u8 *audio_out_delay)
> +{
> +	*phys_addr = (msg->msg[2] << 8) | msg->msg[3];
> +	*video_latency = msg->msg[4];
> +	*low_latency_mode = (msg->msg[5] >> 2) & 1;
> +	*audio_out_compensated = msg->msg[5] & 3;
> +	*audio_out_delay = msg->msg[6];
> +}
> +
> +static inline void cec_msg_request_current_latency(struct cec_msg *msg,
> +						   bool reply,
> +						   __u16 phys_addr)
> +{
> +	msg->len = 4;
> +	msg->msg[1] = CEC_MSG_REQUEST_CURRENT_LATENCY;
> +	msg->msg[2] = phys_addr >> 8;
> +	msg->msg[3] = phys_addr & 0xff;
> +	msg->reply = reply ? CEC_MSG_REPORT_CURRENT_LATENCY : 0;
> +}
> +
> +static inline void cec_ops_request_current_latency(const struct cec_msg *msg,
> +						   __u16 *phys_addr)
> +{
> +	*phys_addr = (msg->msg[2] << 8) | msg->msg[3];
> +}
> +
> +
> +/* Capability Discovery and Control Feature */
> +static inline void cec_msg_cdc_hec_inquire_state(struct cec_msg *msg,
> +						 __u16 phys_addr1,
> +						 __u16 phys_addr2)
> +{
> +	msg->len = 9;
> +	msg->msg[0] |= 0xf; /* broadcast */
> +	msg->msg[1] = CEC_MSG_CDC_MESSAGE;
> +	/* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */
> +	msg->msg[4] = CEC_MSG_CDC_HEC_INQUIRE_STATE;
> +	msg->msg[5] = phys_addr1 >> 8;
> +	msg->msg[6] = phys_addr1 & 0xff;
> +	msg->msg[7] = phys_addr2 >> 8;
> +	msg->msg[8] = phys_addr2 & 0xff;
> +}
> +
> +static inline void cec_ops_cdc_hec_inquire_state(const struct cec_msg *msg,
> +						 __u16 *phys_addr,
> +						 __u16 *phys_addr1,
> +						 __u16 *phys_addr2)
> +{
> +	*phys_addr = (msg->msg[2] << 8) | msg->msg[3];
> +	*phys_addr1 = (msg->msg[5] << 8) | msg->msg[6];
> +	*phys_addr2 = (msg->msg[7] << 8) | msg->msg[8];
> +}
> +
> +static inline void cec_msg_cdc_hec_report_state(struct cec_msg *msg,
> +						__u16 target_phys_addr,
> +						__u8 hec_func_state,
> +						__u8 host_func_state,
> +						__u8 enc_func_state,
> +						__u8 cdc_errcode,
> +						__u8 has_field,
> +						__u16 hec_field)
> +{
> +	msg->len = has_field ? 10 : 8;
> +	msg->msg[0] |= 0xf; /* broadcast */
> +	msg->msg[1] = CEC_MSG_CDC_MESSAGE;
> +	/* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */
> +	msg->msg[4] = CEC_MSG_CDC_HEC_REPORT_STATE;
> +	msg->msg[5] = target_phys_addr >> 8;
> +	msg->msg[6] = target_phys_addr & 0xff;
> +	msg->msg[7] = (hec_func_state << 6) |
> +		      (host_func_state << 4) |
> +		      (enc_func_state << 2) |
> +		      cdc_errcode;
> +	if (has_field) {
> +		msg->msg[8] = hec_field >> 8;
> +		msg->msg[9] = hec_field & 0xff;
> +	}
> +}
> +
> +static inline void cec_ops_cdc_hec_report_state(const struct cec_msg *msg,
> +						__u16 *phys_addr,
> +						__u16 *target_phys_addr,
> +						__u8 *hec_func_state,
> +						__u8 *host_func_state,
> +						__u8 *enc_func_state,
> +						__u8 *cdc_errcode,
> +						__u8 *has_field,
> +						__u16 *hec_field)
> +{
> +	*phys_addr = (msg->msg[2] << 8) | msg->msg[3];
> +	*target_phys_addr = (msg->msg[5] << 8) | msg->msg[6];
> +	*hec_func_state = msg->msg[7] >> 6;
> +	*host_func_state = (msg->msg[7] >> 4) & 3;
> +	*enc_func_state = (msg->msg[7] >> 4) & 3;
> +	*cdc_errcode = msg->msg[7] & 3;
> +	*has_field = msg->len >= 10;
> +	*hec_field = *has_field ? ((msg->msg[8] << 8) | msg->msg[9]) : 0;
> +}
> +
> +static inline void cec_msg_cdc_hec_set_state(struct cec_msg *msg,
> +					     __u16 phys_addr1,
> +					     __u16 phys_addr2,
> +					     __u8 hec_set_state,
> +					     __u16 phys_addr3,
> +					     __u16 phys_addr4,
> +					     __u16 phys_addr5)
> +{
> +	msg->len = 10;
> +	msg->msg[0] |= 0xf; /* broadcast */
> +	msg->msg[1] = CEC_MSG_CDC_MESSAGE;
> +	/* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */
> +	msg->msg[4] = CEC_MSG_CDC_HEC_INQUIRE_STATE;
> +	msg->msg[5] = phys_addr1 >> 8;
> +	msg->msg[6] = phys_addr1 & 0xff;
> +	msg->msg[7] = phys_addr2 >> 8;
> +	msg->msg[8] = phys_addr2 & 0xff;
> +	msg->msg[9] = hec_set_state;
> +	if (phys_addr3 != CEC_PHYS_ADDR_INVALID) {
> +		msg->msg[msg->len++] = phys_addr3 >> 8;
> +		msg->msg[msg->len++] = phys_addr3 & 0xff;
> +		if (phys_addr4 != CEC_PHYS_ADDR_INVALID) {
> +			msg->msg[msg->len++] = phys_addr4 >> 8;
> +			msg->msg[msg->len++] = phys_addr4 & 0xff;
> +			if (phys_addr5 != CEC_PHYS_ADDR_INVALID) {
> +				msg->msg[msg->len++] = phys_addr5 >> 8;
> +				msg->msg[msg->len++] = phys_addr5 & 0xff;
> +			}
> +		}
> +	}
> +}
> +
> +static inline void cec_ops_cdc_hec_set_state(const struct cec_msg *msg,
> +					     __u16 *phys_addr,
> +					     __u16 *phys_addr1,
> +					     __u16 *phys_addr2,
> +					     __u8 *hec_set_state,
> +					     __u16 *phys_addr3,
> +					     __u16 *phys_addr4,
> +					     __u16 *phys_addr5)
> +{
> +	*phys_addr = (msg->msg[2] << 8) | msg->msg[3];
> +	*phys_addr1 = (msg->msg[5] << 8) | msg->msg[6];
> +	*phys_addr2 = (msg->msg[7] << 8) | msg->msg[8];
> +	*hec_set_state = msg->msg[9];
> +	*phys_addr3 = *phys_addr4 = *phys_addr5 = CEC_PHYS_ADDR_INVALID;
> +	if (msg->len >= 12)
> +		*phys_addr3 = (msg->msg[10] << 8) | msg->msg[11];
> +	if (msg->len >= 14)
> +		*phys_addr4 = (msg->msg[12] << 8) | msg->msg[13];
> +	if (msg->len >= 16)
> +		*phys_addr5 = (msg->msg[14] << 8) | msg->msg[15];
> +}
> +
> +static inline void cec_msg_cdc_hec_set_state_adjacent(struct cec_msg *msg,
> +						      __u16 phys_addr1,
> +						      __u8 hec_set_state)
> +{
> +	msg->len = 8;
> +	msg->msg[0] |= 0xf; /* broadcast */
> +	msg->msg[1] = CEC_MSG_CDC_MESSAGE;
> +	/* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */
> +	msg->msg[4] = CEC_MSG_CDC_HEC_SET_STATE_ADJACENT;
> +	msg->msg[5] = phys_addr1 >> 8;
> +	msg->msg[6] = phys_addr1 & 0xff;
> +	msg->msg[7] = hec_set_state;
> +}
> +
> +static inline void cec_ops_cdc_hec_set_state_adjacent(const struct cec_msg *msg,
> +						      __u16 *phys_addr,
> +						      __u16 *phys_addr1,
> +						      __u8 *hec_set_state)
> +{
> +	*phys_addr = (msg->msg[2] << 8) | msg->msg[3];
> +	*phys_addr1 = (msg->msg[5] << 8) | msg->msg[6];
> +	*hec_set_state = msg->msg[7];
> +}
> +
> +static inline void cec_msg_cdc_hec_request_deactivation(struct cec_msg *msg,
> +							__u16 phys_addr1,
> +							__u16 phys_addr2,
> +							__u16 phys_addr3)
> +{
> +	msg->len = 11;
> +	msg->msg[0] |= 0xf; /* broadcast */
> +	msg->msg[1] = CEC_MSG_CDC_MESSAGE;
> +	/* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */
> +	msg->msg[4] = CEC_MSG_CDC_HEC_REQUEST_DEACTIVATION;
> +	msg->msg[5] = phys_addr1 >> 8;
> +	msg->msg[6] = phys_addr1 & 0xff;
> +	msg->msg[7] = phys_addr2 >> 8;
> +	msg->msg[8] = phys_addr2 & 0xff;
> +	msg->msg[9] = phys_addr3 >> 8;
> +	msg->msg[10] = phys_addr3 & 0xff;
> +}
> +
> +static inline void cec_ops_cdc_hec_request_deactivation(const struct cec_msg *msg,
> +							__u16 *phys_addr,
> +							__u16 *phys_addr1,
> +							__u16 *phys_addr2,
> +							__u16 *phys_addr3)
> +{
> +	*phys_addr = (msg->msg[2] << 8) | msg->msg[3];
> +	*phys_addr1 = (msg->msg[5] << 8) | msg->msg[6];
> +	*phys_addr2 = (msg->msg[7] << 8) | msg->msg[8];
> +	*phys_addr3 = (msg->msg[9] << 8) | msg->msg[10];
> +}
> +
> +static inline void cec_msg_cdc_hec_notify_alive(struct cec_msg *msg)
> +{
> +	msg->len = 5;
> +	msg->msg[0] |= 0xf; /* broadcast */
> +	msg->msg[1] = CEC_MSG_CDC_MESSAGE;
> +	/* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */
> +	msg->msg[4] = CEC_MSG_CDC_HEC_NOTIFY_ALIVE;
> +}
> +
> +static inline void cec_ops_cdc_hec_notify_alive(const struct cec_msg *msg,
> +						__u16 *phys_addr)
> +{
> +	*phys_addr = (msg->msg[2] << 8) | msg->msg[3];
> +}
> +
> +static inline void cec_msg_cdc_hec_discover(struct cec_msg *msg)
> +{
> +	msg->len = 5;
> +	msg->msg[0] |= 0xf; /* broadcast */
> +	msg->msg[1] = CEC_MSG_CDC_MESSAGE;
> +	/* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */
> +	msg->msg[4] = CEC_MSG_CDC_HEC_DISCOVER;
> +}
> +
> +static inline void cec_ops_cdc_hec_discover(const struct cec_msg *msg,
> +					    __u16 *phys_addr)
> +{
> +	*phys_addr = (msg->msg[2] << 8) | msg->msg[3];
> +}
> +
> +static inline void cec_msg_cdc_hpd_set_state(struct cec_msg *msg,
> +					     __u8 input_port,
> +					     __u8 hpd_state)
> +{
> +	msg->len = 6;
> +	msg->msg[0] |= 0xf; /* broadcast */
> +	msg->msg[1] = CEC_MSG_CDC_MESSAGE;
> +	/* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */
> +	msg->msg[4] = CEC_MSG_CDC_HPD_SET_STATE;
> +	msg->msg[5] = (input_port << 4) | hpd_state;
> +}
> +
> +static inline void cec_ops_cdc_hpd_set_state(const struct cec_msg *msg,
> +					    __u16 *phys_addr,
> +					    __u8 *input_port,
> +					    __u8 *hpd_state)
> +{
> +	*phys_addr = (msg->msg[2] << 8) | msg->msg[3];
> +	*input_port = msg->msg[5] >> 4;
> +	*hpd_state = msg->msg[5] & 0xf;
> +}
> +
> +static inline void cec_msg_cdc_hpd_report_state(struct cec_msg *msg,
> +						__u8 hpd_state,
> +						__u8 hpd_error)
> +{
> +	msg->len = 6;
> +	msg->msg[0] |= 0xf; /* broadcast */
> +	msg->msg[1] = CEC_MSG_CDC_MESSAGE;
> +	/* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */
> +	msg->msg[4] = CEC_MSG_CDC_HPD_REPORT_STATE;
> +	msg->msg[5] = (hpd_state << 4) | hpd_error;
> +}
> +
> +static inline void cec_ops_cdc_hpd_report_state(const struct cec_msg *msg,
> +						__u16 *phys_addr,
> +						__u8 *hpd_state,
> +						__u8 *hpd_error)
> +{
> +	*phys_addr = (msg->msg[2] << 8) | msg->msg[3];
> +	*hpd_state = msg->msg[5] >> 4;
> +	*hpd_error = msg->msg[5] & 0xf;
> +}
> +
> +#endif
> diff --git a/include/linux/cec.h b/include/linux/cec.h
> new file mode 100644
> index 0000000..521b07cb
> --- /dev/null
> +++ b/include/linux/cec.h
> @@ -0,0 +1,985 @@
> +/*
> + * cec - HDMI Consumer Electronics Control public header
> + *
> + * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
> + *
> + * This program is free software; you may redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 2 of the License.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
> + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
> + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
> + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> + * SOFTWARE.
> + */
> +
> +#ifndef _CEC_UAPI_H
> +#define _CEC_UAPI_H

Is this UAPI? If so, why it is at include/linux, instead of
include/uapi/linux?

> +
> +#include <linux/types.h>
> +
> +#define CEC_MAX_MSG_SIZE	16
> +
> +/**
> + * struct cec_msg - CEC message structure.
> + * @ts:		Timestamp in nanoseconds using CLOCK_MONOTONIC. Set by the
> + *		driver. It is set when the message transmission has finished
> + *		and it is set when a message was received.
> + * @len:	Length in bytes of the message.
> + * @timeout:	The timeout (in ms) that is used to timeout CEC_RECEIVE.
> + *		Set to 0 if you want to wait forever. This timeout can also be
> + *		used with CEC_TRANSMIT as the timeout for waiting for a reply.
> + *		If 0, then it will use a 1 second timeout instead of waiting
> + *		forever as is done with CEC_RECEIVE.
> + * @sequence:	The framework assigns a sequence number to messages that are
> + *		sent. This can be used to track replies to previously sent
> + *		messages.
> + * @flags:	Set to 0.
> + * @rx_status:	The message receive status bits. Set by the driver.
> + * @tx_status:	The message transmit status bits. Set by the driver.
> + * @msg:	The message payload.
> + * @reply:	This field is ignored with CEC_RECEIVE and is only used by
> + *		CEC_TRANSMIT. If non-zero, then wait for a reply with this
> + *		opcode. Set to CEC_MSG_FEATURE_ABORT if you want to wait for
> + *		a possible ABORT reply. If there was an error when sending the
> + *		msg or FeatureAbort was returned, then reply is set to 0.
> + *		If reply is non-zero upon return, then len/msg are set to
> + *		the received message.
> + *		If reply is zero upon return and status has the
> + *		CEC_TX_STATUS_FEATURE_ABORT bit set, then len/msg are set to
> + *		the received feature abort message.
> + *		If reply is zero upon return and status has the
> + *		CEC_TX_STATUS_MAX_RETRIES bit set, then no reply was seen at
> + *		all. If reply is non-zero for CEC_TRANSMIT and the message is a
> + *		broadcast, then -EINVAL is returned.
> + *		if reply is non-zero, then timeout is set to 1000 (the required
> + *		maximum response time).
> + * @tx_arb_lost_cnt: The number of 'Arbitration Lost' events. Set by the driver.
> + * @tx_nack_cnt: The number of 'Not Acknowledged' events. Set by the driver.
> + * @tx_low_drive_cnt: The number of 'Low Drive Detected' events. Set by the driver.
> + * @tx_error_cnt: The number of 'Error' events. Set by the driver.
> + */
> +struct cec_msg {
> +	__u64 ts;
> +	__u32 len;
> +	__u32 timeout;
> +	__u32 sequence;
> +	__u32 flags;
> +	__u8 rx_status;
> +	__u8 tx_status;
> +	__u8 msg[CEC_MAX_MSG_SIZE];
> +	__u8 reply;
> +	__u8 tx_arb_lost_cnt;
> +	__u8 tx_nack_cnt;
> +	__u8 tx_low_drive_cnt;
> +	__u8 tx_error_cnt;
> +};
> +
> +/**
> + * cec_msg_initiator - return the initiator's logical address.
> + * @msg:	the message structure
> + */
> +static inline __u8 cec_msg_initiator(const struct cec_msg *msg)
> +{
> +	return msg->msg[0] >> 4;
> +}
> +
> +/**
> + * cec_msg_destination - return the destination's logical address.
> + * @msg:	the message structure
> + */
> +static inline __u8 cec_msg_destination(const struct cec_msg *msg)
> +{
> +	return msg->msg[0] & 0xf;
> +}
> +
> +/**
> + * cec_msg_opcode - return the opcode of the message, -1 for poll
> + * @msg:	the message structure
> + */
> +static inline int cec_msg_opcode(const struct cec_msg *msg)
> +{
> +	return msg->len > 1 ? msg->msg[1] : -1;
> +}
> +
> +/**
> + * cec_msg_is_broadcast - return true if this is a broadcast message.
> + * @msg:	the message structure
> + */
> +static inline bool cec_msg_is_broadcast(const struct cec_msg *msg)
> +{
> +	return (msg->msg[0] & 0xf) == 0xf;
> +}
> +
> +/**
> + * cec_msg_init - initialize the message structure.
> + * @msg:	the message structure
> + * @initiator:	the logical address of the initiator
> + * @destination:the logical address of the destination (0xf for broadcast)
> + *
> + * The whole structure is zeroed, the len field is set to 1 (i.e. a poll
> + * message) and the initiator and destination are filled in.
> + */
> +static inline void cec_msg_init(struct cec_msg *msg,
> +				__u8 initiator, __u8 destination)
> +{
> +	memset(msg, 0, sizeof(*msg));
> +	msg->msg[0] = (initiator << 4) | destination;
> +	msg->len = 1;
> +}
> +
> +/**
> + * cec_msg_set_reply_to - fill in destination/initiator in a reply message.
> + * @msg:	the message structure for the reply
> + * @orig:	the original message structure
> + *
> + * Set the msg destination to the orig initiator and the msg initiator to the
> + * orig destination. Note that msg and orig may be the same pointer, in which
> + * case the change is done in place.
> + */
> +static inline void cec_msg_set_reply_to(struct cec_msg *msg, struct cec_msg *orig)
> +{
> +	/* The destination becomes the initiator and vice versa */
> +	msg->msg[0] = (cec_msg_destination(orig) << 4) | cec_msg_initiator(orig);
> +	msg->reply = msg->timeout = 0;
> +}
> +
> +/* cec status field */
> +#define CEC_TX_STATUS_OK		(1 << 0)
> +#define CEC_TX_STATUS_ARB_LOST		(1 << 1)
> +#define CEC_TX_STATUS_NACK		(1 << 2)
> +#define CEC_TX_STATUS_LOW_DRIVE		(1 << 3)
> +#define CEC_TX_STATUS_ERROR		(1 << 4)
> +#define CEC_TX_STATUS_MAX_RETRIES	(1 << 5)
> +
> +#define CEC_RX_STATUS_OK		(1 << 0)
> +#define CEC_RX_STATUS_TIMEOUT		(1 << 1)
> +#define CEC_RX_STATUS_FEATURE_ABORT	(1 << 2)
> +
> +static inline bool cec_msg_status_is_ok(const struct cec_msg *msg)
> +{
> +	if (msg->tx_status && !(msg->tx_status & CEC_TX_STATUS_OK))
> +		return false;
> +	if (msg->rx_status && !(msg->rx_status & CEC_RX_STATUS_OK))
> +		return false;
> +	if (!msg->tx_status && !msg->rx_status)
> +		return false;
> +	return !(msg->rx_status & CEC_RX_STATUS_FEATURE_ABORT);
> +}
> +
> +#define CEC_LOG_ADDR_INVALID		0xff
> +#define CEC_PHYS_ADDR_INVALID		0xffff
> +
> +/*
> + * The maximum number of logical addresses one device can be assigned to.
> + * The CEC 2.0 spec allows for only 2 logical addresses at the moment. The
> + * Analog Devices CEC hardware supports 3. So let's go wild and go for 4.
> + */
> +#define CEC_MAX_LOG_ADDRS 4
> +
> +/* The logical addresses defined by CEC 2.0 */
> +#define CEC_LOG_ADDR_TV			0
> +#define CEC_LOG_ADDR_RECORD_1		1
> +#define CEC_LOG_ADDR_RECORD_2		2
> +#define CEC_LOG_ADDR_TUNER_1		3
> +#define CEC_LOG_ADDR_PLAYBACK_1		4
> +#define CEC_LOG_ADDR_AUDIOSYSTEM	5
> +#define CEC_LOG_ADDR_TUNER_2		6
> +#define CEC_LOG_ADDR_TUNER_3		7
> +#define CEC_LOG_ADDR_PLAYBACK_2		8
> +#define CEC_LOG_ADDR_RECORD_3		9
> +#define CEC_LOG_ADDR_TUNER_4		10
> +#define CEC_LOG_ADDR_PLAYBACK_3		11
> +#define CEC_LOG_ADDR_BACKUP_1		12
> +#define CEC_LOG_ADDR_BACKUP_2		13
> +#define CEC_LOG_ADDR_SPECIFIC		14
> +#define CEC_LOG_ADDR_UNREGISTERED	15 /* as initiator address */
> +#define CEC_LOG_ADDR_BROADCAST		15 /* ad destination address */
> +
> +/* The logical address types that the CEC device wants to claim */
> +#define CEC_LOG_ADDR_TYPE_TV		0
> +#define CEC_LOG_ADDR_TYPE_RECORD	1
> +#define CEC_LOG_ADDR_TYPE_TUNER		2
> +#define CEC_LOG_ADDR_TYPE_PLAYBACK	3
> +#define CEC_LOG_ADDR_TYPE_AUDIOSYSTEM	4
> +#define CEC_LOG_ADDR_TYPE_SPECIFIC	5
> +#define CEC_LOG_ADDR_TYPE_UNREGISTERED	6
> +/*
> + * Switches should use UNREGISTERED.
> + * Processors should use SPECIFIC.
> + */
> +
> +#define CEC_LOG_ADDR_MASK_TV		(1 << CEC_LOG_ADDR_TV)
> +#define CEC_LOG_ADDR_MASK_RECORD	((1 << CEC_LOG_ADDR_RECORD_1) | \
> +					 (1 << CEC_LOG_ADDR_RECORD_2) | \
> +					 (1 << CEC_LOG_ADDR_RECORD_3))
> +#define CEC_LOG_ADDR_MASK_TUNER		((1 << CEC_LOG_ADDR_TUNER_1) | \
> +					 (1 << CEC_LOG_ADDR_TUNER_2) | \
> +					 (1 << CEC_LOG_ADDR_TUNER_3) | \
> +					 (1 << CEC_LOG_ADDR_TUNER_4))
> +#define CEC_LOG_ADDR_MASK_PLAYBACK	((1 << CEC_LOG_ADDR_PLAYBACK_1) | \
> +					 (1 << CEC_LOG_ADDR_PLAYBACK_2) | \
> +					 (1 << CEC_LOG_ADDR_PLAYBACK_3))
> +#define CEC_LOG_ADDR_MASK_AUDIOSYSTEM	(1 << CEC_LOG_ADDR_AUDIOSYSTEM)
> +#define CEC_LOG_ADDR_MASK_BACKUP	((1 << CEC_LOG_ADDR_BACKUP_1) | \
> +					 (1 << CEC_LOG_ADDR_BACKUP_2))
> +#define CEC_LOG_ADDR_MASK_SPECIFIC	(1 << CEC_LOG_ADDR_SPECIFIC)
> +#define CEC_LOG_ADDR_MASK_UNREGISTERED	(1 << CEC_LOG_ADDR_UNREGISTERED)
> +
> +static inline bool cec_has_tv(__u16 log_addr_mask)
> +{
> +	return log_addr_mask & CEC_LOG_ADDR_MASK_TV;
> +}
> +
> +static inline bool cec_has_record(__u16 log_addr_mask)
> +{
> +	return log_addr_mask & CEC_LOG_ADDR_MASK_RECORD;
> +}
> +
> +static inline bool cec_has_tuner(__u16 log_addr_mask)
> +{
> +	return log_addr_mask & CEC_LOG_ADDR_MASK_TUNER;
> +}
> +
> +static inline bool cec_has_playback(__u16 log_addr_mask)
> +{
> +	return log_addr_mask & CEC_LOG_ADDR_MASK_PLAYBACK;
> +}
> +
> +static inline bool cec_has_audiosystem(__u16 log_addr_mask)
> +{
> +	return log_addr_mask & CEC_LOG_ADDR_MASK_AUDIOSYSTEM;
> +}
> +
> +static inline bool cec_has_backup(__u16 log_addr_mask)
> +{
> +	return log_addr_mask & CEC_LOG_ADDR_MASK_BACKUP;
> +}
> +
> +static inline bool cec_has_specific(__u16 log_addr_mask)
> +{
> +	return log_addr_mask & CEC_LOG_ADDR_MASK_SPECIFIC;
> +}
> +
> +static inline bool cec_is_unregistered(__u16 log_addr_mask)
> +{
> +	return log_addr_mask & CEC_LOG_ADDR_MASK_UNREGISTERED;
> +}
> +
> +static inline bool cec_is_unconfigured(__u16 log_addr_mask)
> +{
> +	return log_addr_mask == 0;
> +}
> +
> +/*
> + * Use this if there is no vendor ID (CEC_G_VENDOR_ID) or if the vendor ID
> + * should be disabled (CEC_S_VENDOR_ID)
> + */
> +#define CEC_VENDOR_ID_NONE		0xffffffff
> +
> +/* The message handling modes */
> +/* Modes for initiator */
> +#define CEC_MODE_NO_INITIATOR		(0x0 << 0)
> +#define CEC_MODE_INITIATOR		(0x1 << 0)
> +#define CEC_MODE_EXCL_INITIATOR		(0x2 << 0)
> +#define CEC_MODE_INITIATOR_MSK		0x0f
> +
> +/* Modes for follower */
> +#define CEC_MODE_NO_FOLLOWER		(0x0 << 4)
> +#define CEC_MODE_FOLLOWER		(0x1 << 4)
> +#define CEC_MODE_EXCL_FOLLOWER		(0x2 << 4)
> +#define CEC_MODE_EXCL_FOLLOWER_PASSTHRU	(0x3 << 4)
> +#define CEC_MODE_MONITOR		(0xe << 4)
> +#define CEC_MODE_MONITOR_ALL		(0xf << 4)
> +#define CEC_MODE_FOLLOWER_MSK		0xf0
> +
> +/* Userspace has to configure the physical address */
> +#define CEC_CAP_PHYS_ADDR	(1 << 0)
> +/* Userspace has to configure the logical addresses */
> +#define CEC_CAP_LOG_ADDRS	(1 << 1)
> +/* Userspace can transmit messages (and thus become follower as well) */
> +#define CEC_CAP_TRANSMIT	(1 << 2)
> +/*
> + * Passthrough all messages instead of processing them.
> + */
> +#define CEC_CAP_PASSTHROUGH	(1 << 3)
> +/* Supports remote control */
> +#define CEC_CAP_RC		(1 << 4)
> +/* Hardware can monitor all messages, not just directed and broadcast. */
> +#define CEC_CAP_MONITOR_ALL	(1 << 5)
> +
> +/**
> + * struct cec_caps - CEC capabilities structure.
> + * @driver: name of the CEC device driver.
> + * @name: name of the CEC device. @driver + @name must be unique.
> + * @available_log_addrs: number of available logical addresses.
> + * @capabilities: capabilities of the CEC adapter.
> + * @version: version of the CEC adapter framework.
> + * @reserved:	Reserved fields, both driver and application must zero this array.
> + */
> +struct cec_caps {
> +	char driver[32];
> +	char name[32];
> +	__u32 available_log_addrs;
> +	__u32 capabilities;
> +	__u32 version;
> +};
> +
> +/**
> + * struct cec_log_addrs - CEC logical addresses structure.
> + * @log_addr: the claimed logical addresses. Set by the driver.
> + * @log_addr_mask: current logical address mask. Set by the driver.
> + * @cec_version: the CEC version that the adapter should implement. Set by the
> + *	caller.
> + * @num_log_addrs: how many logical addresses should be claimed. Set by the
> + *	caller.
> + * @vendor_id: the vendor ID of the device. Set by the caller.
> + * @flags: set to 0.
> + * @osd_name: the OSD name of the device. Set by the caller.
> + * @primary_device_type: the primary device type for each logical address.
> + *	Set by the caller.
> + * @log_addr_type: the logical address types. Set by the caller.
> + * @all_device_types: CEC 2.0: all device types represented by the logical address.
> + *	Set by the caller.
> + * @features:	CEC 2.0: The logical address features. Set by the caller.
> + */
> +struct cec_log_addrs {
> +	__u8 log_addr[CEC_MAX_LOG_ADDRS];
> +	__u16 log_addr_mask;
> +	__u8 cec_version;
> +	__u8 num_log_addrs;
> +	__u32 vendor_id;
> +	__u32 flags;
> +	char osd_name[15];
> +	__u8 primary_device_type[CEC_MAX_LOG_ADDRS];
> +	__u8 log_addr_type[CEC_MAX_LOG_ADDRS];
> +
> +	/* CEC 2.0 */
> +	__u8 all_device_types[CEC_MAX_LOG_ADDRS];
> +	__u8 features[CEC_MAX_LOG_ADDRS][12];
> +};
> +
> +/* Events */
> +
> +/* Event that occurs when the adapter state changes */
> +#define CEC_EVENT_STATE_CHANGE		1

Are there any reason why not start with zero?

> +/*
> + * This event is sent when messages are lost because the application
> + * didn't empty the message queue in time
> + */
> +#define CEC_EVENT_LOST_MSGS		2
> +
> +#define CEC_EVENT_FL_INITIAL_STATE	(1 << 0)
> +
> +/**
> + * struct cec_event_state_change - used when the CEC adapter changes state.
> + * @phys_addr: the current physical address
> + * @log_addr_mask: the current logical address mask
> + */
> +struct cec_event_state_change {
> +	__u16 phys_addr;
> +	__u16 log_addr_mask;
> +};
> +
> +/**
> + * struct cec_event_lost_msgs - tells you how many messages were lost due.
> + * @lost_msgs: how many messages were lost.
> + */
> +struct cec_event_lost_msgs {
> +	__u32 lost_msgs;
> +};
> +
> +/**
> + * struct cec_event - CEC event structure
> + * @ts: the timestamp of when the event was sent.
> + * @event: the event.
> + * array.
> + * @state_change: the event payload for CEC_EVENT_STATE_CHANGE.
> + * @lost_msgs: the event payload for CEC_EVENT_LOST_MSGS.
> + * @raw: array to pad the union.
> + */
> +struct cec_event {
> +	__u64 ts;
> +	__u32 event;
> +	__u32 flags;
> +	union {
> +		struct cec_event_state_change state_change;
> +		struct cec_event_lost_msgs lost_msgs;
> +		__u32 raw[16];
> +	};
> +};
> +
> +/* ioctls */
> +
> +/* Adapter capabilities */
> +#define CEC_ADAP_G_CAPS		_IOWR('a',  0, struct cec_caps)
> +
> +/*
> + * phys_addr is either 0 (if this is the CEC root device)
> + * or a valid physical address obtained from the sink's EDID
> + * as read by this CEC device (if this is a source device)
> + * or a physical address obtained and modified from a sink
> + * EDID and used for a sink CEC device.
> + * If nothing is connected, then phys_addr is 0xffff.
> + * See HDMI 1.4b, section 8.7 (Physical Address).
> + *
> + * The CEC_ADAP_S_PHYS_ADDR ioctl may not be available if that is handled
> + * internally.
> + */
> +#define CEC_ADAP_G_PHYS_ADDR	_IOR ('a',  1, __u16)
> +#define CEC_ADAP_S_PHYS_ADDR	_IOW ('a',  2, __u16)
> +
> +/*
> + * Configure the CEC adapter. It sets the device type and which
> + * logical types it will try to claim. It will return which
> + * logical addresses it could actually claim.
> + * An error is returned if the adapter is disabled or if there
> + * is no physical address assigned.
> + */
> +
> +#define CEC_ADAP_G_LOG_ADDRS	_IOR ('a',  3, struct cec_log_addrs)
> +#define CEC_ADAP_S_LOG_ADDRS	_IOWR('a',  4, struct cec_log_addrs)
> +
> +/* Transmit/receive a CEC command */
> +#define CEC_TRANSMIT		_IOWR('a',  5, struct cec_msg)
> +#define CEC_RECEIVE		_IOWR('a',  6, struct cec_msg)
> +
> +/* Dequeue CEC events */
> +#define CEC_DQEVENT		_IOWR('a',  7, struct cec_event)
> +
> +/*
> + * Get and set the message handling mode for this filehandle.
> + */
> +#define CEC_G_MODE		_IOR ('a',  8, __u32)
> +#define CEC_S_MODE		_IOW ('a',  9, __u32)
> +
> +/*
> + * The remainder of this header defines all CEC messages and operands.
> + * The format matters since it the cec-ctl utility parses it to generate
> + * code for implementing all these messages.
> + *
> + * Comments ending with 'Feature' group messages for each feature.
> + * If messages are part of multiple features, then the "Has also"
> + * comment is used to list the previously defined messages that are
> + * supported by the feature.
> + *
> + * Before operands are defined a comment is added that gives the
> + * name of the operand and in brackets the variable name of the
> + * corresponding argument in the cec-funcs.h function.
> + */
> +
> +/* Messages */
> +
> +/* One Touch Play Feature */
> +#define CEC_MSG_ACTIVE_SOURCE				0x82
> +#define CEC_MSG_IMAGE_VIEW_ON				0x04
> +#define CEC_MSG_TEXT_VIEW_ON				0x0d
> +
> +
> +/* Routing Control Feature */
> +
> +/*
> + * Has also:
> + *	CEC_MSG_ACTIVE_SOURCE
> + */
> +
> +#define CEC_MSG_INACTIVE_SOURCE				0x9d
> +#define CEC_MSG_REQUEST_ACTIVE_SOURCE			0x85
> +#define CEC_MSG_ROUTING_CHANGE				0x80
> +#define CEC_MSG_ROUTING_INFORMATION			0x81
> +#define CEC_MSG_SET_STREAM_PATH				0x86
> +
> +
> +/* Standby Feature */
> +#define CEC_MSG_STANDBY					0x36
> +
> +
> +/* One Touch Record Feature */
> +#define CEC_MSG_RECORD_OFF				0x0b
> +#define CEC_MSG_RECORD_ON				0x09
> +/* Record Source Type Operand (rec_src_type) */
> +#define CEC_OP_RECORD_SRC_OWN				1
> +#define CEC_OP_RECORD_SRC_DIGITAL			2
> +#define CEC_OP_RECORD_SRC_ANALOG			3
> +#define CEC_OP_RECORD_SRC_EXT_PLUG			4
> +#define CEC_OP_RECORD_SRC_EXT_PHYS_ADDR			5
> +/* Service Identification Method Operand (service_id_method) */
> +#define CEC_OP_SERVICE_ID_METHOD_BY_DIG_ID		0
> +#define CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL		1
> +/* Digital Service Broadcast System Operand (dig_bcast_system) */
> +#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_GEN	0x00
> +#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_GEN	0x01
> +#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_GEN		0x02
> +#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_BS		0x08
> +#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_CS		0x09
> +#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_T		0x0a
> +#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_CABLE	0x10
> +#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_SAT	0x11
> +#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_T		0x12
> +#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_C		0x18
> +#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S		0x19
> +#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S2		0x1a
> +#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_T		0x1b
> +/* Analogue Broadcast Type Operand (ana_bcast_type) */
> +#define CEC_OP_ANA_BCAST_TYPE_CABLE			0
> +#define CEC_OP_ANA_BCAST_TYPE_SATELLITE			1
> +#define CEC_OP_ANA_BCAST_TYPE_TERRESTRIAL		2
> +/* Broadcast System Operand (bcast_system) */
> +#define CEC_OP_BCAST_SYSTEM_PAL_BG			0x00
> +#define CEC_OP_BCAST_SYSTEM_SECAM_LQ			0x01 /* SECAM L' */
> +#define CEC_OP_BCAST_SYSTEM_PAL_M			0x02
> +#define CEC_OP_BCAST_SYSTEM_NTSC_M			0x03
> +#define CEC_OP_BCAST_SYSTEM_PAL_I			0x04
> +#define CEC_OP_BCAST_SYSTEM_SECAM_DK			0x05
> +#define CEC_OP_BCAST_SYSTEM_SECAM_BG			0x06
> +#define CEC_OP_BCAST_SYSTEM_SECAM_L			0x07
> +#define CEC_OP_BCAST_SYSTEM_PAL_DK			0x08
> +#define CEC_OP_BCAST_SYSTEM_OTHER			0x1f
> +/* Channel Number Format Operand (channel_number_fmt) */
> +#define CEC_OP_CHANNEL_NUMBER_FMT_1_PART		0x01
> +#define CEC_OP_CHANNEL_NUMBER_FMT_2_PART		0x02
> +
> +#define CEC_MSG_RECORD_STATUS				0x0a
> +/* Record Status Operand (rec_status) */
> +#define CEC_OP_RECORD_STATUS_CUR_SRC			0x01
> +#define CEC_OP_RECORD_STATUS_DIG_SERVICE		0x02
> +#define CEC_OP_RECORD_STATUS_ANA_SERVICE		0x03
> +#define CEC_OP_RECORD_STATUS_EXT_INPUT			0x04
> +#define CEC_OP_RECORD_STATUS_NO_DIG_SERVICE		0x05
> +#define CEC_OP_RECORD_STATUS_NO_ANA_SERVICE		0x06
> +#define CEC_OP_RECORD_STATUS_NO_SERVICE			0x07
> +#define CEC_OP_RECORD_STATUS_INVALID_EXT_PLUG		0x09
> +#define CEC_OP_RECORD_STATUS_INVALID_EXT_PHYS_ADDR	0x0a
> +#define CEC_OP_RECORD_STATUS_UNSUP_CA			0x0b
> +#define CEC_OP_RECORD_STATUS_NO_CA_ENTITLEMENTS		0x0c
> +#define CEC_OP_RECORD_STATUS_CANT_COPY_SRC		0x0d
> +#define CEC_OP_RECORD_STATUS_NO_MORE_COPIES		0x0e
> +#define CEC_OP_RECORD_STATUS_NO_MEDIA			0x10
> +#define CEC_OP_RECORD_STATUS_PLAYING			0x11
> +#define CEC_OP_RECORD_STATUS_ALREADY_RECORDING		0x12
> +#define CEC_OP_RECORD_STATUS_MEDIA_PROT			0x13
> +#define CEC_OP_RECORD_STATUS_NO_SIGNAL			0x14
> +#define CEC_OP_RECORD_STATUS_MEDIA_PROBLEM		0x15
> +#define CEC_OP_RECORD_STATUS_NO_SPACE			0x16
> +#define CEC_OP_RECORD_STATUS_PARENTAL_LOCK		0x17
> +#define CEC_OP_RECORD_STATUS_TERMINATED_OK		0x1a
> +#define CEC_OP_RECORD_STATUS_ALREADY_TERM		0x1b
> +#define CEC_OP_RECORD_STATUS_OTHER			0x1f
> +
> +#define CEC_MSG_RECORD_TV_SCREEN			0x0f
> +
> +
> +/* Timer Programming Feature */
> +#define CEC_MSG_CLEAR_ANALOGUE_TIMER			0x33
> +/* Recording Sequence Operand (recording_seq) */
> +#define CEC_OP_REC_SEQ_SUNDAY				0x01
> +#define CEC_OP_REC_SEQ_MONDAY				0x02
> +#define CEC_OP_REC_SEQ_TUESDAY				0x04
> +#define CEC_OP_REC_SEQ_WEDNESDAY			0x08
> +#define CEC_OP_REC_SEQ_THURSDAY				0x10
> +#define CEC_OP_REC_SEQ_FRIDAY				0x20
> +#define CEC_OP_REC_SEQ_SATERDAY				0x40
> +#define CEC_OP_REC_SEQ_ONCE_ONLY			0x00
> +
> +#define CEC_MSG_CLEAR_DIGITAL_TIMER			0x99
> +
> +#define CEC_MSG_CLEAR_EXT_TIMER				0xa1
> +/* External Source Specifier Operand (ext_src_spec) */
> +#define CEC_OP_EXT_SRC_PLUG				0x04
> +#define CEC_OP_EXT_SRC_PHYS_ADDR			0x05
> +
> +#define CEC_MSG_SET_ANALOGUE_TIMER			0x34
> +#define CEC_MSG_SET_DIGITAL_TIMER			0x97
> +#define CEC_MSG_SET_EXT_TIMER				0xa2
> +
> +#define CEC_MSG_SET_TIMER_PROGRAM_TITLE			0x67
> +#define CEC_MSG_TIMER_CLEARED_STATUS			0x43
> +/* Timer Cleared Status Data Operand (timer_cleared_status) */
> +#define CEC_OP_TIMER_CLR_STAT_RECORDING			0x00
> +#define CEC_OP_TIMER_CLR_STAT_NO_MATCHING		0x01
> +#define CEC_OP_TIMER_CLR_STAT_NO_INFO			0x02
> +#define CEC_OP_TIMER_CLR_STAT_CLEARED			0x80
> +
> +#define CEC_MSG_TIMER_STATUS				0x35
> +/* Timer Overlap Warning Operand (timer_overlap_warning) */
> +#define CEC_OP_TIMER_OVERLAP_WARNING_NO_OVERLAP		0
> +#define CEC_OP_TIMER_OVERLAP_WARNING_OVERLAP		1
> +/* Media Info Operand (media_info) */
> +#define CEC_OP_MEDIA_INFO_UNPROT_MEDIA			0
> +#define CEC_OP_MEDIA_INFO_PROT_MEDIA			1
> +#define CEC_OP_MEDIA_INFO_NO_MEDIA			2
> +/* Programmed Indicator Operand (prog_indicator) */
> +#define CEC_OP_PROG_IND_NOT_PROGRAMMED			0
> +#define CEC_OP_PROG_IND_PROGRAMMED			1
> +/* Programmed Info Operand (prog_info) */
> +#define CEC_OP_PROG_INFO_ENOUGH_SPACE			0x08
> +#define CEC_OP_PROG_INFO_NOT_ENOUGH_SPACE		0x09
> +#define CEC_OP_PROG_INFO_MIGHT_NOT_BE_ENOUGH_SPACE	0x0b
> +#define CEC_OP_PROG_INFO_NONE_AVAILABLE			0x0a
> +/* Not Programmed Error Info Operand (prog_error) */
> +#define CEC_OP_PROG_ERROR_NO_FREE_TIMER			0x01
> +#define CEC_OP_PROG_ERROR_DATE_OUT_OF_RANGE		0x02
> +#define CEC_OP_PROG_ERROR_REC_SEQ_ERROR			0x03
> +#define CEC_OP_PROG_ERROR_INV_EXT_PLUG			0x04
> +#define CEC_OP_PROG_ERROR_INV_EXT_PHYS_ADDR		0x05
> +#define CEC_OP_PROG_ERROR_CA_UNSUPP			0x06
> +#define CEC_OP_PROG_ERROR_INSUF_CA_ENTITLEMENTS		0x07
> +#define CEC_OP_PROG_ERROR_RESOLUTION_UNSUPP		0x08
> +#define CEC_OP_PROG_ERROR_PARENTAL_LOCK			0x09
> +#define CEC_OP_PROG_ERROR_CLOCK_FAILURE			0x0a
> +#define CEC_OP_PROG_ERROR_DUPLICATE			0x0e
> +
> +
> +/* System Information Feature */
> +#define CEC_MSG_CEC_VERSION				0x9e
> +/* CEC Version Operand (cec_version) */
> +#define CEC_OP_CEC_VERSION_1_3A				4
> +#define CEC_OP_CEC_VERSION_1_4				5
> +#define CEC_OP_CEC_VERSION_2_0				6
> +
> +#define CEC_MSG_GET_CEC_VERSION				0x9f
> +#define CEC_MSG_GIVE_PHYSICAL_ADDR			0x83
> +#define CEC_MSG_GET_MENU_LANGUAGE			0x91
> +#define CEC_MSG_REPORT_PHYSICAL_ADDR			0x84
> +/* Primary Device Type Operand (prim_devtype) */
> +#define CEC_OP_PRIM_DEVTYPE_TV				0
> +#define CEC_OP_PRIM_DEVTYPE_RECORD			1
> +#define CEC_OP_PRIM_DEVTYPE_TUNER			3
> +#define CEC_OP_PRIM_DEVTYPE_PLAYBACK			4
> +#define CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM			5
> +#define CEC_OP_PRIM_DEVTYPE_SWITCH			6
> +#define CEC_OP_PRIM_DEVTYPE_PROCESSOR			7
> +
> +#define CEC_MSG_SET_MENU_LANGUAGE			0x32
> +#define CEC_MSG_REPORT_FEATURES				0xa6	/* HDMI 2.0 */
> +/* All Device Types Operand (all_device_types) */
> +#define CEC_OP_ALL_DEVTYPE_TV				0x80
> +#define CEC_OP_ALL_DEVTYPE_RECORD			0x40
> +#define CEC_OP_ALL_DEVTYPE_TUNER			0x20
> +#define CEC_OP_ALL_DEVTYPE_PLAYBACK			0x10
> +#define CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM			0x08
> +#define CEC_OP_ALL_DEVTYPE_SWITCH			0x04
> +/*
> + * And if you wondering what happened to PROCESSOR devices: those should
> + * be mapped to a SWITCH.
> + */
> +
> +/* Valid for RC Profile and Device Feature operands */
> +#define CEC_OP_FEAT_EXT					0x80	/* Extension bit */
> +/* RC Profile Operand (rc_profile) */
> +#define CEC_OP_FEAT_RC_TV_PROFILE_NONE			0x00
> +#define CEC_OP_FEAT_RC_TV_PROFILE_1			0x02
> +#define CEC_OP_FEAT_RC_TV_PROFILE_2			0x06
> +#define CEC_OP_FEAT_RC_TV_PROFILE_3			0x0a
> +#define CEC_OP_FEAT_RC_TV_PROFILE_4			0x0e
> +#define CEC_OP_FEAT_RC_SRC_HAS_DEV_ROOT_MENU		0x50
> +#define CEC_OP_FEAT_RC_SRC_HAS_DEV_SETUP_MENU		0x48
> +#define CEC_OP_FEAT_RC_SRC_HAS_CONTENTS_MENU		0x44
> +#define CEC_OP_FEAT_RC_SRC_HAS_MEDIA_TOP_MENU		0x42
> +#define CEC_OP_FEAT_RC_SRC_HAS_MEDIA_CONTEXT_MENU	0x41
> +/* Device Feature Operand (dev_features) */
> +#define CEC_OP_FEAT_DEV_HAS_RECORD_TV_SCREEN		0x40
> +#define CEC_OP_FEAT_DEV_HAS_SET_OSD_STRING		0x20
> +#define CEC_OP_FEAT_DEV_HAS_DECK_CONTROL		0x10
> +#define CEC_OP_FEAT_DEV_HAS_SET_AUDIO_RATE		0x08
> +#define CEC_OP_FEAT_DEV_SINK_HAS_ARC_TX			0x04
> +#define CEC_OP_FEAT_DEV_SOURCE_HAS_ARC_RX		0x02
> +
> +#define CEC_MSG_GIVE_FEATURES				0xa5	/* HDMI 2.0 */
> +
> +
> +/* Deck Control Feature */
> +#define CEC_MSG_DECK_CONTROL				0x42
> +/* Deck Control Mode Operand (deck_control_mode) */
> +#define CEC_OP_DECK_CTL_MODE_SKIP_FWD			1
> +#define CEC_OP_DECK_CTL_MODE_SKIP_REV			2
> +#define CEC_OP_DECK_CTL_MODE_STOP			3
> +#define CEC_OP_DECK_CTL_MODE_EJECT			4
> +
> +#define CEC_MSG_DECK_STATUS				0x1b
> +/* Deck Info Operand (deck_info) */
> +#define CEC_OP_DECK_INFO_PLAY				0x11
> +#define CEC_OP_DECK_INFO_RECORD				0x12
> +#define CEC_OP_DECK_INFO_PLAY_REV			0x13
> +#define CEC_OP_DECK_INFO_STILL				0x14
> +#define CEC_OP_DECK_INFO_SLOW				0x15
> +#define CEC_OP_DECK_INFO_SLOW_REV			0x16
> +#define CEC_OP_DECK_INFO_FAST_FWD			0x17
> +#define CEC_OP_DECK_INFO_FAST_REV			0x18
> +#define CEC_OP_DECK_INFO_NO_MEDIA			0x19
> +#define CEC_OP_DECK_INFO_STOP				0x1a
> +#define CEC_OP_DECK_INFO_SKIP_FWD			0x1b
> +#define CEC_OP_DECK_INFO_SKIP_REV			0x1c
> +#define CEC_OP_DECK_INFO_INDEX_SEARCH_FWD		0x1d
> +#define CEC_OP_DECK_INFO_INDEX_SEARCH_REV		0x1e
> +#define CEC_OP_DECK_INFO_OTHER				0x1f
> +
> +#define CEC_MSG_GIVE_DECK_STATUS			0x1a
> +/* Status Request Operand (status_req) */
> +#define CEC_OP_STATUS_REQ_ON				1
> +#define CEC_OP_STATUS_REQ_OFF				2
> +#define CEC_OP_STATUS_REQ_ONCE				3
> +
> +#define CEC_MSG_PLAY					0x41
> +/* Play Mode Operand (play_mode) */
> +#define CEC_OP_PLAY_MODE_PLAY_FWD			0x24
> +#define CEC_OP_PLAY_MODE_PLAY_REV			0x20
> +#define CEC_OP_PLAY_MODE_PLAY_STILL			0x25
> +#define CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MIN		0x05
> +#define CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MED		0x06
> +#define CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MAX		0x07
> +#define CEC_OP_PLAY_MODE_PLAY_FAST_REV_MIN		0x09
> +#define CEC_OP_PLAY_MODE_PLAY_FAST_REV_MED		0x0a
> +#define CEC_OP_PLAY_MODE_PLAY_FAST_REV_MAX		0x0b
> +#define CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MIN		0x15
> +#define CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MED		0x16
> +#define CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MAX		0x17
> +#define CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MIN		0x19
> +#define CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MED		0x1a
> +#define CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MAX		0x1b
> +
> +
> +/* Tuner Control Feature */
> +#define CEC_MSG_GIVE_TUNER_DEVICE_STATUS		0x08
> +#define CEC_MSG_SELECT_ANALOGUE_SERVICE			0x92
> +#define CEC_MSG_SELECT_DIGITAL_SERVICE			0x93
> +#define CEC_MSG_TUNER_DEVICE_STATUS			0x07
> +/* Recording Flag Operand (rec_flag) */
> +#define CEC_OP_REC_FLAG_USED				0
> +#define CEC_OP_REC_FLAG_NOT_USED			1
> +/* Tuner Display Info Operand (tuner_display_info) */
> +#define CEC_OP_TUNER_DISPLAY_INFO_DIGITAL		0
> +#define CEC_OP_TUNER_DISPLAY_INFO_NONE			1
> +#define CEC_OP_TUNER_DISPLAY_INFO_ANALOGUE		2
> +
> +#define CEC_MSG_TUNER_STEP_DECREMENT			0x06
> +#define CEC_MSG_TUNER_STEP_INCREMENT			0x05
> +
> +
> +/* Vendor Specific Commands Feature */
> +
> +/*
> + * Has also:
> + *	CEC_MSG_CEC_VERSION
> + *	CEC_MSG_GET_CEC_VERSION
> + */
> +#define CEC_MSG_DEVICE_VENDOR_ID			0x87
> +#define CEC_MSG_GIVE_DEVICE_VENDOR_ID			0x8c
> +#define CEC_MSG_VENDOR_COMMAND				0x89
> +#define CEC_MSG_VENDOR_COMMAND_WITH_ID			0xa0
> +#define CEC_MSG_VENDOR_REMOTE_BUTTON_DOWN		0x8a
> +#define CEC_MSG_VENDOR_REMOTE_BUTTON_UP			0x8b
> +
> +
> +/* OSD Display Feature */
> +#define CEC_MSG_SET_OSD_STRING				0x64
> +/* Display Control Operand (disp_ctl) */
> +#define CEC_OP_DISP_CTL_DEFAULT				0x00
> +#define CEC_OP_DISP_CTL_UNTIL_CLEARED			0x40
> +#define CEC_OP_DISP_CTL_CLEAR				0x80
> +
> +
> +/* Device OSD Transfer Feature */
> +#define CEC_MSG_GIVE_OSD_NAME				0x46
> +#define CEC_MSG_SET_OSD_NAME				0x47
> +
> +
> +/* Device Menu Control Feature */
> +#define CEC_MSG_MENU_REQUEST				0x8d
> +/* Menu Request Type Operand (menu_req) */
> +#define CEC_OP_MENU_REQUEST_ACTIVATE			0x00
> +#define CEC_OP_MENU_REQUEST_DEACTIVATE			0x01
> +#define CEC_OP_MENU_REQUEST_QUERY			0x02
> +
> +#define CEC_MSG_MENU_STATUS				0x8e
> +/* Menu State Operand (menu_state) */
> +#define CEC_OP_MENU_STATE_ACTIVATED			0x00
> +#define CEC_OP_MENU_STATE_DEACTIVATED			0x01
> +
> +#define CEC_MSG_USER_CONTROL_PRESSED			0x44
> +/* UI Broadcast Type Operand (ui_bcast_type) */
> +#define CEC_OP_UI_BCAST_TYPE_TOGGLE_ALL			0x00
> +#define CEC_OP_UI_BCAST_TYPE_TOGGLE_DIG_ANA		0x01
> +#define CEC_OP_UI_BCAST_TYPE_ANALOGUE			0x10
> +#define CEC_OP_UI_BCAST_TYPE_ANALOGUE_T			0x20
> +#define CEC_OP_UI_BCAST_TYPE_ANALOGUE_CABLE		0x30
> +#define CEC_OP_UI_BCAST_TYPE_ANALOGUE_SAT		0x40
> +#define CEC_OP_UI_BCAST_TYPE_DIGITAL			0x50
> +#define CEC_OP_UI_BCAST_TYPE_DIGITAL_T			0x60
> +#define CEC_OP_UI_BCAST_TYPE_DIGITAL_CABLE		0x70
> +#define CEC_OP_UI_BCAST_TYPE_DIGITAL_SAT		0x80
> +#define CEC_OP_UI_BCAST_TYPE_DIGITAL_COM_SAT		0x90
> +#define CEC_OP_UI_BCAST_TYPE_DIGITAL_COM_SAT2		0x91
> +#define CEC_OP_UI_BCAST_TYPE_IP				0xa0
> +/* UI Sound Presentation Control Operand (ui_snd_pres_ctl) */
> +#define CEC_OP_UI_SND_PRES_CTL_DUAL_MONO		0x10
> +#define CEC_OP_UI_SND_PRES_CTL_KARAOKE			0x20
> +#define CEC_OP_UI_SND_PRES_CTL_DOWNMIX			0x80
> +#define CEC_OP_UI_SND_PRES_CTL_REVERB			0x90
> +#define CEC_OP_UI_SND_PRES_CTL_EQUALIZER		0xa0
> +#define CEC_OP_UI_SND_PRES_CTL_BASS_UP			0xb1
> +#define CEC_OP_UI_SND_PRES_CTL_BASS_NEUTRAL		0xb2
> +#define CEC_OP_UI_SND_PRES_CTL_BASS_DOWN		0xb3
> +#define CEC_OP_UI_SND_PRES_CTL_TREBLE_UP		0xc1
> +#define CEC_OP_UI_SND_PRES_CTL_TREBLE_NEUTRAL		0xc2
> +#define CEC_OP_UI_SND_PRES_CTL_TREBLE_DOWN		0xc3
> +
> +#define CEC_MSG_USER_CONTROL_RELEASED			0x45
> +
> +
> +/* Remote Control Passthrough Feature */
> +
> +/*
> + * Has also:
> + *	CEC_MSG_USER_CONTROL_PRESSED
> + *	CEC_MSG_USER_CONTROL_RELEASED
> + */
> +
> +
> +/* Power Status Feature */
> +#define CEC_MSG_GIVE_DEVICE_POWER_STATUS		0x8f
> +#define CEC_MSG_REPORT_POWER_STATUS			0x90
> +/* Power Status Operand (pwr_state) */
> +#define CEC_OP_POWER_STATUS_ON				0
> +#define CEC_OP_POWER_STATUS_STANDBY			1
> +#define CEC_OP_POWER_STATUS_TO_ON			2
> +#define CEC_OP_POWER_STATUS_TO_STANDBY			3
> +
> +
> +/* General Protocol Messages */
> +#define CEC_MSG_FEATURE_ABORT				0x00
> +/* Abort Reason Operand (reason) */
> +#define CEC_OP_ABORT_UNRECOGNIZED_OP			0
> +#define CEC_OP_ABORT_INCORRECT_MODE			1
> +#define CEC_OP_ABORT_NO_SOURCE				2
> +#define CEC_OP_ABORT_INVALID_OP				3
> +#define CEC_OP_ABORT_REFUSED				4
> +#define CEC_OP_ABORT_UNDETERMINED			5
> +
> +#define CEC_MSG_ABORT					0xff
> +
> +
> +/* System Audio Control Feature */
> +
> +/*
> + * Has also:
> + *	CEC_MSG_USER_CONTROL_PRESSED
> + *	CEC_MSG_USER_CONTROL_RELEASED
> + */
> +#define CEC_MSG_GIVE_AUDIO_STATUS			0x71
> +#define CEC_MSG_GIVE_SYSTEM_AUDIO_MODE_STATUS		0x7d
> +#define CEC_MSG_REPORT_AUDIO_STATUS			0x7a
> +/* Audio Mute Status Operand (aud_mute_status) */
> +#define CEC_OP_AUD_MUTE_STATUS_OFF			0
> +#define CEC_OP_AUD_MUTE_STATUS_ON			1
> +
> +#define CEC_MSG_REPORT_SHORT_AUDIO_DESCRIPTOR		0xa3
> +#define CEC_MSG_REQUEST_SHORT_AUDIO_DESCRIPTOR		0xa4
> +#define CEC_MSG_SET_SYSTEM_AUDIO_MODE			0x72
> +/* System Audio Status Operand (sys_aud_status) */
> +#define CEC_OP_SYS_AUD_STATUS_OFF			0
> +#define CEC_OP_SYS_AUD_STATUS_ON			1
> +
> +#define CEC_MSG_SYSTEM_AUDIO_MODE_REQUEST		0x70
> +#define CEC_MSG_SYSTEM_AUDIO_MODE_STATUS		0x7e
> +/* Audio Format ID Operand (audio_format_id) */
> +#define CEC_OP_AUD_FMT_ID_CEA861			0
> +#define CEC_OP_AUD_FMT_ID_CEA861_CXT			1
> +
> +
> +/* Audio Rate Control Feature */
> +#define CEC_MSG_SET_AUDIO_RATE				0x9a
> +/* Audio Rate Operand (audio_rate) */
> +#define CEC_OP_AUD_RATE_OFF				0
> +#define CEC_OP_AUD_RATE_WIDE_STD			1
> +#define CEC_OP_AUD_RATE_WIDE_FAST			2
> +#define CEC_OP_AUD_RATE_WIDE_SLOW			3
> +#define CEC_OP_AUD_RATE_NARROW_STD			4
> +#define CEC_OP_AUD_RATE_NARROW_FAST			5
> +#define CEC_OP_AUD_RATE_NARROW_SLOW			6
> +
> +
> +/* Audio Return Channel Control Feature */
> +#define CEC_MSG_INITIATE_ARC				0xc0
> +#define CEC_MSG_REPORT_ARC_INITIATED			0xc1
> +#define CEC_MSG_REPORT_ARC_TERMINATED			0xc2
> +#define CEC_MSG_REQUEST_ARC_INITIATION			0xc3
> +#define CEC_MSG_REQUEST_ARC_TERMINATION			0xc4
> +#define CEC_MSG_TERMINATE_ARC				0xc5
> +
> +
> +/* Dynamic Audio Lipsync Feature */
> +/* Only for CEC 2.0 and up */
> +#define CEC_MSG_REQUEST_CURRENT_LATENCY			0xa7
> +#define CEC_MSG_REPORT_CURRENT_LATENCY			0xa8
> +/* Low Latency Mode Operand (low_latency_mode) */
> +#define CEC_OP_LOW_LATENCY_MODE_OFF			0
> +#define CEC_OP_LOW_LATENCY_MODE_ON			1
> +/* Audio Output Compensated Operand (audio_out_compensated) */
> +#define CEC_OP_AUD_OUT_COMPENSATED_NA			0
> +#define CEC_OP_AUD_OUT_COMPENSATED_DELAY		1
> +#define CEC_OP_AUD_OUT_COMPENSATED_NO_DELAY		2
> +#define CEC_OP_AUD_OUT_COMPENSATED_PARTIAL_DELAY	3
> +
> +
> +/* Capability Discovery and Control Feature */
> +#define CEC_MSG_CDC_MESSAGE				0xf8
> +/* Ethernet-over-HDMI: nobody ever does this... */
> +#define CEC_MSG_CDC_HEC_INQUIRE_STATE			0x00
> +#define CEC_MSG_CDC_HEC_REPORT_STATE			0x01
> +/* HEC Functionality State Operand (hec_func_state) */
> +#define CEC_OP_HEC_FUNC_STATE_NOT_SUPPORTED		0
> +#define CEC_OP_HEC_FUNC_STATE_INACTIVE			1
> +#define CEC_OP_HEC_FUNC_STATE_ACTIVE			2
> +#define CEC_OP_HEC_FUNC_STATE_ACTIVATION_FIELD		3
> +/* Host Functionality State Operand (host_func_state) */
> +#define CEC_OP_HOST_FUNC_STATE_NOT_SUPPORTED		0
> +#define CEC_OP_HOST_FUNC_STATE_INACTIVE			1
> +#define CEC_OP_HOST_FUNC_STATE_ACTIVE			2
> +/* ENC Functionality State Operand (enc_func_state) */
> +#define CEC_OP_ENC_FUNC_STATE_EXT_CON_NOT_SUPPORTED	0
> +#define CEC_OP_ENC_FUNC_STATE_EXT_CON_INACTIVE		1
> +#define CEC_OP_ENC_FUNC_STATE_EXT_CON_ACTIVE		2
> +/* CDC Error Code Operand (cdc_errcode) */
> +#define CEC_OP_CDC_ERROR_CODE_NONE			0
> +#define CEC_OP_CDC_ERROR_CODE_CAP_UNSUPPORTED		1
> +#define CEC_OP_CDC_ERROR_CODE_WRONG_STATE		2
> +#define CEC_OP_CDC_ERROR_CODE_OTHER			3
> +/* HEC Support Operand (hec_support) */
> +#define CEC_OP_HEC_SUPPORT_NO				0
> +#define CEC_OP_HEC_SUPPORT_YES				1
> +/* HEC Activation Operand (hec_activation) */
> +#define CEC_OP_HEC_ACTIVATION_ON			0
> +#define CEC_OP_HEC_ACTIVATION_OFF			1
> +
> +#define CEC_MSG_CDC_HEC_SET_STATE_ADJACENT		0x02
> +#define CEC_MSG_CDC_HEC_SET_STATE			0x03
> +/* HEC Set State Operand (hec_set_state) */
> +#define CEC_OP_HEC_SET_STATE_DEACTIVATE			0
> +#define CEC_OP_HEC_SET_STATE_ACTIVATE			1
> +
> +#define CEC_MSG_CDC_HEC_REQUEST_DEACTIVATION		0x04
> +#define CEC_MSG_CDC_HEC_NOTIFY_ALIVE			0x05
> +#define CEC_MSG_CDC_HEC_DISCOVER			0x06
> +/* Hotplug Detect messages */
> +#define CEC_MSG_CDC_HPD_SET_STATE			0x10
> +/* HPD State Operand (hpd_state) */
> +#define CEC_OP_HPD_STATE_CP_EDID_DISABLE		0
> +#define CEC_OP_HPD_STATE_CP_EDID_ENABLE			1
> +#define CEC_OP_HPD_STATE_CP_EDID_DISABLE_ENABLE		2
> +#define CEC_OP_HPD_STATE_EDID_DISABLE			3
> +#define CEC_OP_HPD_STATE_EDID_ENABLE			4
> +#define CEC_OP_HPD_STATE_EDID_DISABLE_ENABLE		5
> +#define CEC_MSG_CDC_HPD_REPORT_STATE			0x11
> +/* HPD Error Code Operand (hpd_error) */
> +#define CEC_OP_HPD_ERROR_NONE				0
> +#define CEC_OP_HPD_ERROR_INITIATOR_NOT_CAPABLE		1
> +#define CEC_OP_HPD_ERROR_INITIATOR_WRONG_STATE		2
> +#define CEC_OP_HPD_ERROR_OTHER				3
> +#define CEC_OP_HPD_ERROR_NONE_NO_VIDEO			4
> +
> +#endif
> diff --git a/include/media/cec-edid.h b/include/media/cec-edid.h
> new file mode 100644
> index 0000000..d6e39ca
> --- /dev/null
> +++ b/include/media/cec-edid.h
> @@ -0,0 +1,103 @@
> +/*
> + * cec-edid - HDMI Consumer Electronics Control & EDID helpers
> + *
> + * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
> + *
> + * This program is free software; you may redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 2 of the License.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
> + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
> + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
> + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> + * SOFTWARE.
> + */
> +
> +#ifndef _MEDIA_CEC_EDID_H
> +#define _MEDIA_CEC_EDID_H
> +
> +#include <linux/types.h>
> +#include <linux/cec.h>
> +
> +#define cec_phys_addr_exp(pa) \
> +	((pa) >> 12), ((pa) >> 8) & 0xf, ((pa) >> 4) & 0xf, (pa) & 0xf
> +
> +/**
> + * cec_get_edid_phys_addr() - find and return the physical address
> + *
> + * @edid:	pointer to the EDID data
> + * @size:	size in bytes of the EDID data
> + * @offset:	If not %NULL then the location of the physical address
> + *		bytes in the EDID will be returned here. This is set to 0
> + *		if there is no physical address found.
> + *
> + * Return: the physical address or CEC_PHYS_ADDR_INVALID if there is none.
> + */
> +u16 cec_get_edid_phys_addr(const u8 *edid, unsigned size, unsigned *offset);
> +
> +/**
> + * cec_set_edid_phys_addr() - find and set the physical address
> + *
> + * @edid:	pointer to the EDID data
> + * @size:	size in bytes of the EDID data
> + * @phys_addr:	the new physical address
> + *
> + * This function finds the location of the physical address in the EDID
> + * and fills in the given physical address and updates the checksum
> + * at the end of the EDID block. It does nothing if the EDID doesn't
> + * contain a physical address.
> + */
> +void cec_set_edid_phys_addr(u8 *edid, unsigned size, u16 phys_addr);
> +
> +/**
> + * cec_phys_addr_for_input() - calculate the PA for an input
> + *
> + * @phys_addr:	the physical address of the parent
> + * @input:	the number of the input port, must be between 1 and 15
> + *
> + * This function calculates a new physical address based on the input
> + * port number. For example:
> + *
> + * PA = 0.0.0.0 and input = 2 becomes 2.0.0.0
> + *
> + * PA = 3.0.0.0 and input = 1 becomes 3.1.0.0
> + *
> + * PA = 3.2.1.0 and input = 5 becomes 3.2.1.5
> + *
> + * PA = 3.2.1.3 and input = 5 becomes f.f.f.f since it maxed out the depth.
> + *
> + * Return: the new physical address or CEC_PHYS_ADDR_INVALID.
> + */
> +u16 cec_phys_addr_for_input(u16 phys_addr, u8 input);
> +
> +/**
> + * cec_phys_addr_validate() - validate a physical address from an EDID
> + *
> + * @phys_addr:	the physical address to validate
> + * @parent:	if not %NULL, then this is filled with the parents PA.
> + * @port:	if not %NULL, then this is filled with the input port.
> + *
> + * This validates a physical address as read from an EDID. If the
> + * PA is invalid (such as 1.0.1.0 since '0' is only allowed at the end),
> + * then it will return -EINVAL.
> + *
> + * The parent PA is passed into %parent and the input port is passed into
> + * %port. For example:
> + *
> + * PA = 0.0.0.0: has parent 0.0.0.0 and input port 0.
> + *
> + * PA = 1.0.0.0: has parent 0.0.0.0 and input port 1.
> + *
> + * PA = 3.2.0.0: has parent 3.0.0.0 and input port 2.
> + *
> + * PA = f.f.f.f: has parent f.f.f.f and input port 0.
> + *
> + * Return: 0 if the PA is valid, -EINVAL if not.
> + */
> +int cec_phys_addr_validate(u16 phys_addr, u16 *parent, u16 *port);
> +
> +#endif /* _MEDIA_CEC_EDID_H */
> diff --git a/include/media/cec.h b/include/media/cec.h
> new file mode 100644
> index 0000000..2dcd838
> --- /dev/null
> +++ b/include/media/cec.h
> @@ -0,0 +1,236 @@
> +/*
> + * cec - HDMI Consumer Electronics Control support header
> + *
> + * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
> + *
> + * This program is free software; you may redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 2 of the License.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
> + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
> + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
> + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> + * SOFTWARE.
> + */
> +
> +#ifndef _MEDIA_CEC_H
> +#define _MEDIA_CEC_H
> +
> +#include <linux/poll.h>
> +#include <linux/fs.h>
> +#include <linux/debugfs.h>
> +#include <linux/device.h>
> +#include <linux/cdev.h>
> +#include <linux/kthread.h>
> +#include <linux/timer.h>
> +#include <linux/cec-funcs.h>
> +#include <media/rc-core.h>
> +#include <media/cec-edid.h>
> +
> +/**
> + * struct cec_devnode - cec device node
> + * @dev:	cec device
> + * @cdev:	cec character device
> + * @parent:	parent device
> + * @minor:	device node minor number
> + * @registered:	the device was correctly registered
> + * @unregistered: the device was unregistered
> + * @fhs_lock:	lock to control access to the filehandle list
> + * @fhs:	the list of open filehandles (cec_fh)
> + *
> + * This structure represents a cec-related device node.
> + *
> + * The @parent is a physical device. It must be set by core or device drivers
> + * before registering the node.
> + */
> +struct cec_devnode {
> +	/* sysfs */
> +	struct device dev;
> +	struct cdev cdev;
> +	struct device *parent;
> +
> +	/* device info */
> +	int minor;
> +	bool registered;
> +	bool unregistered;
> +	struct mutex fhs_lock;
> +	struct list_head fhs;
> +};
> +
> +struct cec_adapter;
> +struct cec_data;
> +
> +struct cec_data {
> +	struct list_head list;
> +	struct list_head xfer_list;
> +	struct cec_adapter *adap;
> +	struct cec_msg msg;
> +	struct cec_fh *fh;
> +	struct delayed_work work;
> +	struct completion c;
> +	u8 attempts;
> +	bool new_initiator;
> +	bool blocking;
> +	bool completed;
> +};
> +
> +struct cec_msg_entry {
> +	struct list_head	list;
> +	struct cec_msg		msg;
> +};
> +
> +#define CEC_NUM_EVENTS		CEC_EVENT_LOST_MSGS
> +
> +struct cec_event_queue {
> +	unsigned		elems;
> +	unsigned		num_events;
> +	struct cec_event	*events;
> +};
> +
> +struct cec_fh {
> +	struct list_head	list;
> +	struct list_head	xfer_list;
> +	struct cec_adapter	*adap;
> +	u8			mode_initiator;
> +	u8			mode_follower;
> +
> +	/* Events */
> +	wait_queue_head_t	wait;
> +	unsigned		events;
> +	struct cec_event_queue	evqueue[CEC_NUM_EVENTS];
> +	struct mutex		lock;
> +	struct list_head	msgs; /* queued messages */
> +	unsigned		queued_msgs;
> +	unsigned		lost_msgs;
> +};
> +
> +#define CEC_SIGNAL_FREE_TIME_RETRY		3
> +#define CEC_SIGNAL_FREE_TIME_NEW_INITIATOR	5
> +#define CEC_SIGNAL_FREE_TIME_NEXT_XFER		7
> +
> +/* The nominal data bit period is 2.4 ms */
> +#define CEC_FREE_TIME_TO_USEC(ft)		((ft) * 2400)
> +
> +struct cec_adap_ops {
> +	/* Low-level callbacks */
> +	int (*adap_enable)(struct cec_adapter *adap, bool enable);
> +	int (*adap_monitor_all_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, u8 attempts,
> +			     u32 signal_free_time, struct cec_msg *msg);
> +	void (*adap_status)(struct cec_adapter *adap, struct seq_file *file);
> +
> +	/* High-level CEC message callback */
> +	int (*received)(struct cec_adapter *adap, struct cec_msg *msg);
> +};
> +
> +/*
> + * The minimum message length you can receive (excepting poll messages) is 2.
> + * With a transfer rate of at most 36 bytes per second this makes 18 messages
> + * per second worst case.
> + *
> + * We queue at most 10 seconds worth of messages.
> + */
> +#define CEC_MAX_MSG_QUEUE_SZ		(18 * 10)
> +
> +struct cec_adapter {
> +	struct module *owner;
> +	char name[32];
> +	struct cec_devnode devnode;
> +	struct mutex lock;
> +	struct rc_dev *rc;
> +
> +	struct list_head transmit_queue;
> +	struct list_head wait_queue;
> +	struct cec_data *transmitting;
> +
> +	struct task_struct *kthread_config;
> +	struct completion config_completion;
> +
> +	struct task_struct *kthread;
> +	wait_queue_head_t kthread_waitq;
> +	wait_queue_head_t waitq;
> +
> +	const struct cec_adap_ops *ops;
> +	void *priv;
> +	u32 capabilities;
> +	u8 available_log_addrs;
> +
> +	u16 phys_addr;
> +	bool is_configuring;
> +	bool is_configured;
> +	u32 monitor_all_cnt;
> +	u32 follower_cnt;
> +	struct cec_fh *cec_follower;
> +	struct cec_fh *cec_initiator;
> +	bool passthrough;
> +	struct cec_log_addrs log_addrs;
> +
> +	struct dentry *cec_dir;
> +	struct dentry *status_file;
> +
> +	u16 phys_addrs[15];
> +	u32 sequence;
> +
> +	char input_name[32];
> +	char input_phys[32];
> +	char input_drv[32];
> +};
> +
> +static inline bool cec_has_log_addr(const struct cec_adapter *adap, u8 log_addr)
> +{
> +	return adap->log_addrs.log_addr_mask & (1 << log_addr);
> +}
> +
> +static inline bool cec_is_sink(const struct cec_adapter *adap)
> +{
> +	return adap->phys_addr == 0;
> +}
> +
> +#if IS_ENABLED(CONFIG_MEDIA_CEC)
> +struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
> +		void *priv, const char *name, u32 caps, u8 available_las,
> +		struct device *parent);
> +int cec_register_adapter(struct cec_adapter *adap);
> +void cec_unregister_adapter(struct cec_adapter *adap);
> +void cec_delete_adapter(struct cec_adapter *adap);
> +
> +int cec_s_log_addrs(struct cec_adapter *adap, struct cec_log_addrs *log_addrs,
> +		    bool block);
> +void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr,
> +		     bool block);
> +int cec_transmit_msg(struct cec_adapter *adap, struct cec_msg *msg,
> +		     bool block);
> +
> +/* Called by the adapter */
> +void cec_transmit_done(struct cec_adapter *adap, u8 status, u8 arb_lost_cnt,
> +		       u8 nack_cnt, u8 low_drive_cnt, u8 error_cnt);
> +void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg);
> +
> +#else
> +
> +static inline int cec_register_adapter(struct cec_adapter *adap)
> +{
> +	return 0;
> +}
> +
> +static inline void cec_unregister_adapter(struct cec_adapter *adap)
> +{
> +}
> +
> +static inline void cec_delete_adapter(struct cec_adapter *adap)
> +{
> +}
> +
> +static inline void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr,
> +				   bool block)
> +{
> +}
> +
> +#endif
> +
> +#endif /* _MEDIA_CEC_H */



Thanks,
Mauro

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

* Re: [PATCHv16 05/13] cec/TODO: add TODO file so we know why this is still in staging
  2016-04-29 13:52   ` Hans Verkuil
  (?)
@ 2016-06-16 19:53   ` Mauro Carvalho Chehab
  -1 siblings, 0 replies; 35+ messages in thread
From: Mauro Carvalho Chehab @ 2016-06-16 19:53 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: linux-media, dri-devel, linux-samsung-soc, linux-input, lars,
	linux, Hans Verkuil

Em Fri, 29 Apr 2016 15:52:20 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> Explain why cec.c is still in staging.

Hmm... as this is for staging, even having pointed several things to
be improved, I may end merging this series. Will decide after finishing
the patch review.

> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> ---
>  drivers/staging/media/cec/TODO | 13 +++++++++++++
>  1 file changed, 13 insertions(+)
>  create mode 100644 drivers/staging/media/cec/TODO
> 
> diff --git a/drivers/staging/media/cec/TODO b/drivers/staging/media/cec/TODO
> new file mode 100644
> index 0000000..c0751ef
> --- /dev/null
> +++ b/drivers/staging/media/cec/TODO
> @@ -0,0 +1,13 @@
> +The reason why cec.c is still in staging is that I would like
> +to have a bit more confidence in the uABI. The kABI is fine,
> +no problem there, but I would like to let the public API mature
> +a bit.
> +
> +Once I'm confident that I didn't miss anything then the cec.c source
> +can move to drivers/media and the linux/cec.h and linux/cec-funcs.h
> +headers can move to uapi/linux and added to uapi/linux/Kbuild to make
> +them public.
> +
> +Hopefully this will happen later in 2016.
> +
> +Hans Verkuil <hans.verkuil@cisco.com>



Thanks,
Mauro

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

* Re: [PATCHv16 07/13] cec.txt: add CEC framework documentation
  2016-04-29 13:52   ` Hans Verkuil
  (?)
@ 2016-06-16 20:12   ` Mauro Carvalho Chehab
  2016-06-17  7:22     ` Hans Verkuil
  -1 siblings, 1 reply; 35+ messages in thread
From: Mauro Carvalho Chehab @ 2016-06-16 20:12 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: linux-media, dri-devel, linux-samsung-soc, linux-input, lars,
	linux, Hans Verkuil, Kamil Debski, Hans Verkuil

Em Fri, 29 Apr 2016 15:52:22 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hansverk@cisco.com>
> 
> Document the new HDMI CEC framework.

As we'll be moving documentation to Sphinx/Rst, it would be good if
you could make it work fine with sphinx, as this will likely be needed
for Kernel 4.9. Right now it most works, although several warnings are
produced: 

/devel/v4l/patchwork/tmp/cec.txt:40: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:40: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:40: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:40: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:40: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:42: WARNING: Definition list ends without a blank line; unexpected unindent.
/devel/v4l/patchwork/tmp/cec.txt:42: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:67: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:71: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:92: ERROR: Unexpected indentation.
/devel/v4l/patchwork/tmp/cec.txt:87: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:87: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:87: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:87: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:87: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:87: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:87: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:87: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:87: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:92: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:93: WARNING: Block quote ends without a blank line; unexpected unindent.
/devel/v4l/patchwork/tmp/cec.txt:93: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:93: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:95: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:97: WARNING: Definition list ends without a blank line; unexpected unindent.
/devel/v4l/patchwork/tmp/cec.txt:105: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:105: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:118: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:118: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:130: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:130: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:144: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:144: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:144: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:161: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:161: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:161: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:173: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:194: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:203: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:203: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:214: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:217: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:217: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:217: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:217: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:219: WARNING: Definition list ends without a blank line; unexpected unindent.
/devel/v4l/patchwork/tmp/cec.txt:224: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:224: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:224: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:238: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:238: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:243: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:258: WARNING: Inline emphasis start-string without end-string.
/devel/v4l/patchwork/tmp/cec.txt:258: WARNING: Inline emphasis start-string without end-string.


> 
> Signed-off-by: Hans Verkuil <hansverk@cisco.com>
> [k.debski@samsung.com: add DocBook documentation by Hans Verkuil, with
> Signed-off-by: Kamil Debski <kamil@wypas.org>
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> ---
>  Documentation/cec.txt | 267 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 267 insertions(+)
>  create mode 100644 Documentation/cec.txt
> 
> diff --git a/Documentation/cec.txt b/Documentation/cec.txt
> new file mode 100644
> index 0000000..75155fe
> --- /dev/null
> +++ b/Documentation/cec.txt
> @@ -0,0 +1,267 @@
> +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_allocate_adapter() and deleted by calling cec_delete_adapter():
> +
> +struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
> +	       void *priv, const char *name, u32 caps, u8 available_las,
> +	       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. Note: this name will be copied.
> +
> +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.
> +
> +available_las: the number of simultaneous logical addresses that this
> +	adapter can handle. Must be 1 <= available_las <= CEC_MAX_LOG_ADDRS.
> +
> +parent: the parent device.
> +
> +
> +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);
> +
> +Note: if cec_register_adapter() fails, then call cec_delete_adapter() to
> +clean up. But if cec_register_adapter() succeeded, then only call
> +cec_unregister_adapter() to clean up, never cec_delete_adapter(). The
> +unregister function will delete the adapter automatically once the last user
> +of that /dev/cecX device has closed its file handle.
> +
> +
> +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_monitor_all_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, u8 attempts,
> +			     u32 signal_free_time, struct cec_msg *msg);
> +	void (*adap_log_status)(struct cec_adapter *adap);
> +
> +	/* 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_allocate_adapter() is disabled.
> +
> +Note that adap_enable must return 0 if enable is false.
> +
> +
> +To enable/disable the 'monitor all' mode:
> +
> +	int (*adap_monitor_all_enable)(struct cec_adapter *adap, bool enable);
> +
> +If enabled, then the adapter should be put in a mode to also monitor messages
> +that not for us. Not all hardware supports this and this function is only
> +called if the CEC_CAP_MONITOR_ALL capability is set. This callback is optional
> +(some hardware may always be in 'monitor all' mode).
> +
> +Note that adap_monitor_all_enable must return 0 if enable is false.
> +
> +
> +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 receive directed messages to that address.
> +
> +Note that adap_log_addr must return 0 if logical_addr is CEC_LOG_ADDR_INVALID.
> +
> +
> +To transmit a new message:
> +
> +	int (*adap_transmit)(struct cec_adapter *adap, u8 attempts,
> +			     u32 signal_free_time, struct cec_msg *msg);
> +
> +This transmits a new message. The attempts argument is the suggested number of
> +attempts for the transmit.
> +
> +The signal_free_time is the number of data bit periods that the adapter should
> +wait when the line is free before attempting to send a message. This value
> +depends on whether this transmit is a retry, a message from a new initiator or
> +a new message for the same initiator. Most hardware will handle this
> +automatically, but in some cases this information is needed.
> +
> +The CEC_FREE_TIME_TO_USEC macro can be used to convert signal_free_time to
> +microseconds (one data bit period is 2.4 ms).
> +
> +
> +To log the current CEC hardware status:
> +
> +	void (*adap_status)(struct cec_adapter *adap, struct seq_file *file);
> +
> +This optional callback can be used to show the status of the CEC hardware.
> +The status is available through debugfs: cat /sys/kernel/debug/cec/cecX/status
> +
> +
> +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, u8 status, u8 arb_lost_cnt,
> +		       u8 nack_cnt, u8 low_drive_cnt, u8 error_cnt);
> +
> +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
> +took control of the CEC line and you lost the arbitration.
> +CEC_TX_STATUS_NACK: the message was nacked (for a directed message) or
> +acked (for a broadcast message). A retransmission is needed.
> +CEC_TX_STATUS_LOW_DRIVE: low drive was detected on the CEC bus. This
> +indicates that a follower detected an error on the bus and requested a
> +retransmission.
> +CEC_TX_STATUS_ERROR: some unspecified error occurred: this can be one of
> +the previous two if the hardware cannot differentiate or something else
> +entirely.
> +CEC_TX_STATUS_MAX_RETRIES: could not transmit the message after
> +trying multiple times. Should only be set by the driver if it has hardware
> +support for retrying messages. If set, then the framework assumes that it
> +doesn't have to make another attempt to transmit the message since the
> +hardware did that already.
> +
> +The *_cnt arguments are the number of error conditions that were seen.
> +This may be 0 if no information is available. Drivers that do not support
> +hardware retry can just set the counter corresponding to the transmit error
> +to 1, if the hardware does support retry then either set these counters to
> +0 if the hardware provides no feedback of which errors occurred and how many
> +times, or fill in the correct values as reported by the hardware.
> +
> +When a CEC message was received:
> +
> +void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg);
> +
> +Speaks for itself.
> +
> +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);
> +};
> +
> +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.
> +
> +
> +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.
> +
> +void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block);
> +
> +Change the physical address. This function will set adap->phys_addr and
> +send an event if it has changed. If cec_s_log_addrs() has been called and
> +the physical address has become valid, then the CEC framework will start
> +claiming the logical addresses. If block is true, then this function won't
> +return until this process has finished.
> +
> +When the physical address is set to a valid value the CEC adapter will
> +be enabled (see the adap_enable op). When it is set to CEC_PHYS_ADDR_INVALID,
> +then the CEC adapter will be disabled. If you change a valid physical address
> +to another valid physical address, then this function will first set the
> +address to CEC_PHYS_ADDR_INVALID before enabling the new physical address.
> +
> +int cec_s_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. To unconfigure all logical
> +addresses call this function with log_addrs set to NULL or with
> +log_addrs->num_log_addrs set to 0. The block argument is ignored when
> +unconfiguring. This function will just return if the physical address is
> +invalid. Once the physical address becomes valid, then the framework will
> +attempt to claim these logical addresses.



Thanks,
Mauro

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

* Re: [PATCHv16 08/13] DocBook/media: add CEC documentation
  2016-04-29 13:52 ` [PATCHv16 08/13] DocBook/media: add CEC documentation Hans Verkuil
@ 2016-06-16 21:09   ` Mauro Carvalho Chehab
  2016-06-17  7:58     ` Hans Verkuil
  0 siblings, 1 reply; 35+ messages in thread
From: Mauro Carvalho Chehab @ 2016-06-16 21:09 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: linux-media, dri-devel, linux-samsung-soc, linux-input, lars,
	linux, Hans Verkuil, Kamil Debski, Hans Verkuil

Em Fri, 29 Apr 2016 15:52:23 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> Add DocBook documentation for the CEC API.

Please always send the documentation patch *before* the code,
in order to make easier to do the code review.

> 
> Signed-off-by: Hans Verkuil <hansverk@cisco.com>
> [k.debski@samsung.com: add documentation for passthrough mode]
> [k.debski@samsung.com: minor fixes and change of reserved field sizes]
> Signed-off-by: Kamil Debski <kamil@wypas.org>
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> ---
>  Documentation/DocBook/device-drivers.tmpl          |   4 +
>  Documentation/DocBook/media/Makefile               |   2 +
>  Documentation/DocBook/media/v4l/biblio.xml         |  10 +
>  Documentation/DocBook/media/v4l/cec-api.xml        |  72 +++++
>  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      | 140 +++++++++
>  .../DocBook/media/v4l/cec-ioc-adap-g-log-addrs.xml | 324 +++++++++++++++++++++
>  .../DocBook/media/v4l/cec-ioc-adap-g-phys-addr.xml |  82 ++++++
>  .../DocBook/media/v4l/cec-ioc-dqevent.xml          | 190 ++++++++++++
>  Documentation/DocBook/media/v4l/cec-ioc-g-mode.xml | 245 ++++++++++++++++
>  .../DocBook/media/v4l/cec-ioc-receive.xml          | 260 +++++++++++++++++
>  Documentation/DocBook/media_api.tmpl               |   6 +-
>  15 files changed, 1649 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-dqevent.xml
>  create mode 100644 Documentation/DocBook/media/v4l/cec-ioc-g-mode.xml
>  create mode 100644 Documentation/DocBook/media/v4l/cec-ioc-receive.xml

Hmm... as CEC is at staging, I'm not very comfortable on having
it documented there as-is, as the documentation is expected to
be for non-staging stuff. Maybe the best would be to add, on every
xml above, a note saying that this is a *proposed* documentation
for a feature yet to be accepted.

> 
> diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl
> index 893b2ca..31258bf 100644
> --- a/Documentation/DocBook/device-drivers.tmpl
> +++ b/Documentation/DocBook/device-drivers.tmpl
> @@ -270,6 +270,10 @@ 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
> +!Iinclude/media/cec-edid.h
> +     </sect1>

For the same reason, I would not add those yet.

>  
>    </chapter>
>  
> diff --git a/Documentation/DocBook/media/Makefile b/Documentation/DocBook/media/Makefile
> index 2840ff4..fdc1386 100644
> --- a/Documentation/DocBook/media/Makefile
> +++ b/Documentation/DocBook/media/Makefile
> @@ -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/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/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)
>  
> diff --git a/Documentation/DocBook/media/v4l/biblio.xml b/Documentation/DocBook/media/v4l/biblio.xml
> index 9beb30f..87f1d24 100644
> --- a/Documentation/DocBook/media/v4l/biblio.xml
> +++ b/Documentation/DocBook/media/v4l/biblio.xml
> @@ -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>
> diff --git a/Documentation/DocBook/media/v4l/cec-api.xml b/Documentation/DocBook/media/v4l/cec-api.xml
> new file mode 100644
> index 0000000..caa04c0
> --- /dev/null
> +++ b/Documentation/DocBook/media/v4l/cec-api.xml
> @@ -0,0 +1,72 @@
> +<partinfo>
> +  <authorgroup>
> +    <author>
> +      <firstname>Hans</firstname>
> +      <surname>Verkuil</surname>
> +      <affiliation><address><email>hans.verkuil@cisco.com</email></address></affiliation>
> +      <contrib>Initial version.</contrib>
> +    </author>
> +  </authorgroup>
> +  <copyright>
> +    <year>2016</year>
> +    <holder>Hans Verkuil</holder>
> +  </copyright>
> +
> +  <revhistory>
> +    <!-- Put document revisions here, newest first. -->
> +    <revision>
> +      <revnumber>1.0.0</revnumber>
> +      <date>2016-03-17</date>
> +      <authorinitials>hv</authorinitials>
> +      <revremark>Initial revision</revremark>
> +    </revision>
> +  </revhistory>
> +</partinfo>
> +
> +<title>CEC API</title>
> +
> +<chapter id="cec-api">
> +  <title>CEC: Consumer Electronics Control</title>
> +
> +  <section id="cec-intro">
> +    <title>Introduction</title>
> +    <para>HDMI connectors provide a single pin for use by the Consumer Electronics
> +    Control protocol. This protocol allows different devices connected by an HDMI cable
> +    to communicate. The protocol for CEC version 1.4 is defined in supplements 1 (CEC)
> +    and 2 (HEAC or HDMI Ethernet and Audio Return Channel) of the HDMI 1.4a
> +    (<xref linkend="hdmi" />) specification and the extensions added to CEC version 2.0
> +    are defined in chapter 11 of the HDMI 2.0 (<xref linkend="hdmi2" />) specification.
> +    </para>
> +
> +    <para>The bitrate is very slow (effectively no more than 36 bytes per second) and
> +    is based on the ancient AV.link protocol used in old SCART connectors. The protocol
> +    closely resembles a crazy Rube Goldberg contraption and is an unholy mix of low and
> +    high level messages. Some messages, especially those part of the HEAC protocol layered
> +    on top of CEC, need to be handled by the kernel, others can be handled either by the
> +    kernel or by userspace.</para>
> +
> +    <para>In addition, CEC can be implemented in HDMI receivers, transmitters and in USB
> +    devices that have an HDMI input and an HDMI output and that control just the CEC pin.</para>
> +
> +    <para>Drivers that support CEC and that allow (or require) userspace to handle CEC

Either allow or require. If it works out of the box like a RC controller
where no userspace is needed to have it working, allow is ok. Otherwise,
if it only works after userspace enables it, it is require.

> +    messages and/or configure the CEC adapter will create a CEC device node (/dev/cecX)
> +    to give userspace access to the CEC adapter. The &CEC-ADAP-G-CAPS; ioctl will tell userspace
> +    what it is allowed to do.</para>
> +  </section>
> +</chapter>
> +
> +<appendix id="cec-user-func">
> +  <title>Function Reference</title>
> +  <!-- Keep this alphabetically sorted. -->
> +  &sub-cec-func-open;
> +  &sub-cec-func-close;
> +  &sub-cec-func-ioctl;
> +  &sub-cec-func-poll;
> +  <!-- All ioctls go here. -->
> +  &sub-cec-ioc-adap-g-caps;
> +  &sub-cec-ioc-adap-g-log-addrs;
> +  &sub-cec-ioc-adap-g-phys-addr;
> +  &sub-cec-ioc-dqevent;
> +  &sub-cec-ioc-g-mode;
> +  &sub-cec-ioc-receive;
> +</appendix>
> diff --git a/Documentation/DocBook/media/v4l/cec-func-close.xml b/Documentation/DocBook/media/v4l/cec-func-close.xml
> new file mode 100644
> index 0000000..c3978af
> --- /dev/null
> +++ b/Documentation/DocBook/media/v4l/cec-func-close.xml
> @@ -0,0 +1,59 @@
> +<refentry id="cec-func-close">
> +  <refmeta>
> +    <refentrytitle>cec close()</refentrytitle>
> +    &manvol;
> +  </refmeta>
> +
> +  <refnamediv>
> +    <refname>cec-close</refname>
> +    <refpurpose>Close a cec device</refpurpose>
> +  </refnamediv>
> +
> +  <refsynopsisdiv>
> +    <funcsynopsis>
> +      <funcsynopsisinfo>#include &lt;unistd.h&gt;</funcsynopsisinfo>
> +      <funcprototype>
> +	<funcdef>int <function>close</function></funcdef>
> +	<paramdef>int <parameter>fd</parameter></paramdef>
> +      </funcprototype>
> +    </funcsynopsis>
> +  </refsynopsisdiv>
> +
> +  <refsect1>
> +    <title>Arguments</title>
> +
> +    <variablelist>
> +      <varlistentry>
> +	<term><parameter>fd</parameter></term>
> +	<listitem>
> +	  <para>&fd;</para>
> +	</listitem>
> +      </varlistentry>
> +    </variablelist>
> +  </refsect1>
> +
> +  <refsect1>
> +    <title>Description</title>
> +
> +    <para>Closes the cec device. Resources associated with the file descriptor
> +    are freed. The device configuration remain unchanged.</para>
> +  </refsect1>
> +
> +  <refsect1>
> +    <title>Return Value</title>
> +
> +    <para><function>close</function> returns 0 on success. On error, -1 is
> +    returned, and <varname>errno</varname> is set appropriately. Possible error
> +    codes are:</para>
> +
> +    <variablelist>
> +      <varlistentry>
> +	<term><errorcode>EBADF</errorcode></term>
> +	<listitem>
> +	  <para><parameter>fd</parameter> is not a valid open file descriptor.
> +	  </para>
> +	</listitem>
> +      </varlistentry>
> +    </variablelist>
> +  </refsect1>
> +</refentry>
> diff --git a/Documentation/DocBook/media/v4l/cec-func-ioctl.xml b/Documentation/DocBook/media/v4l/cec-func-ioctl.xml
> new file mode 100644
> index 0000000..0480eeb
> --- /dev/null
> +++ b/Documentation/DocBook/media/v4l/cec-func-ioctl.xml
> @@ -0,0 +1,73 @@
> +<refentry id="cec-func-ioctl">
> +  <refmeta>
> +    <refentrytitle>cec ioctl()</refentrytitle>
> +    &manvol;
> +  </refmeta>
> +
> +  <refnamediv>
> +    <refname>cec-ioctl</refname>
> +    <refpurpose>Control a cec device</refpurpose>
> +  </refnamediv>
> +
> +  <refsynopsisdiv>
> +    <funcsynopsis>
> +      <funcsynopsisinfo>#include &lt;sys/ioctl.h&gt;</funcsynopsisinfo>
> +      <funcprototype>
> +	<funcdef>int <function>ioctl</function></funcdef>
> +	<paramdef>int <parameter>fd</parameter></paramdef>
> +	<paramdef>int <parameter>request</parameter></paramdef>
> +	<paramdef>void *<parameter>argp</parameter></paramdef>
> +      </funcprototype>
> +    </funcsynopsis>
> +  </refsynopsisdiv>
> +
> +  <refsect1>
> +    <title>Arguments</title>
> +
> +    <variablelist>
> +      <varlistentry>
> +	<term><parameter>fd</parameter></term>
> +	<listitem>
> +	  <para>&fd;</para>
> +	</listitem>
> +      </varlistentry>
> +      <varlistentry>
> +	<term><parameter>request</parameter></term>
> +	<listitem>
> +	  <para>CEC ioctl request code as defined in the cec.h header file,
> +	  for example CEC_ADAP_G_CAPS.</para>
> +	</listitem>
> +      </varlistentry>
> +      <varlistentry>
> +	<term><parameter>argp</parameter></term>
> +	<listitem>
> +	  <para>Pointer to a request-specific structure.</para>
> +	</listitem>
> +      </varlistentry>
> +    </variablelist>
> +  </refsect1>
> +
> +  <refsect1>
> +    <title>Description</title>
> +    <para>The <function>ioctl()</function> function manipulates cec device
> +    parameters. The argument <parameter>fd</parameter> must be an open file
> +    descriptor.</para>
> +    <para>The ioctl <parameter>request</parameter> code specifies the cec
> +    function to be called. It has encoded in it whether the argument is an
> +    input, output or read/write parameter, and the size of the argument
> +    <parameter>argp</parameter> in bytes.</para>
> +    <para>Macros and structures definitions specifying cec ioctl requests and
> +    their parameters are located in the cec.h header file. All cec ioctl
> +    requests, their respective function and parameters are specified in
> +    <xref linkend="cec-user-func" />.</para>
> +  </refsect1>
> +
> +  <refsect1>
> +    &return-value;
> +
> +    <para>Request-specific error codes are listed in the
> +    individual requests descriptions.</para>
> +    <para>When an ioctl that takes an output or read/write parameter fails,
> +    the parameter remains unmodified.</para>
> +  </refsect1>
> +</refentry>
> diff --git a/Documentation/DocBook/media/v4l/cec-func-open.xml b/Documentation/DocBook/media/v4l/cec-func-open.xml
> new file mode 100644
> index 0000000..d814847
> --- /dev/null
> +++ b/Documentation/DocBook/media/v4l/cec-func-open.xml
> @@ -0,0 +1,94 @@
> +<refentry id="cec-func-open">
> +  <refmeta>
> +    <refentrytitle>cec open()</refentrytitle>
> +    &manvol;
> +  </refmeta>
> +
> +  <refnamediv>
> +    <refname>cec-open</refname>
> +    <refpurpose>Open a cec device</refpurpose>
> +  </refnamediv>
> +
> +  <refsynopsisdiv>
> +    <funcsynopsis>
> +      <funcsynopsisinfo>#include &lt;fcntl.h&gt;</funcsynopsisinfo>
> +      <funcprototype>
> +	<funcdef>int <function>open</function></funcdef>
> +	<paramdef>const char *<parameter>device_name</parameter></paramdef>
> +	<paramdef>int <parameter>flags</parameter></paramdef>
> +      </funcprototype>
> +    </funcsynopsis>
> +  </refsynopsisdiv>
> +
> +  <refsect1>
> +    <title>Arguments</title>
> +
> +    <variablelist>
> +      <varlistentry>
> +	<term><parameter>device_name</parameter></term>
> +	<listitem>
> +	  <para>Device to be opened.</para>
> +	</listitem>
> +      </varlistentry>
> +      <varlistentry>
> +	<term><parameter>flags</parameter></term>
> +	<listitem>
> +	  <para>Open flags. Access mode must be either <constant>O_RDONLY</constant>
> +	  or <constant>O_RDWR</constant>. Other flags have no effect.</para>

What about O_NOBLOCK? I saw something at patch 4/13 that seems to
indicate that it support non-block mode.

> +	</listitem>
> +      </varlistentry>
> +    </variablelist>
> +  </refsect1>
> +  <refsect1>
> +    <title>Description</title>
> +    <para>To open a cec device applications call <function>open()</function>
> +    with the desired device name. The function has no side effects; the device
> +    configuration remain unchanged.</para>
> +    <para>When the device is opened in read-only mode, attempts to modify its
> +    configuration will result in an error, and <varname>errno</varname> will be
> +    set to <errorcode>EBADF</errorcode>.</para>
> +  </refsect1>
> +  <refsect1>
> +    <title>Return Value</title>
> +
> +    <para><function>open</function> returns the new file descriptor on success.
> +    On error, -1 is returned, and <varname>errno</varname> is set appropriately.
> +    Possible error codes are:</para>

This is more a side note that applies also to other DocBooks. Perhaps
we should change the word to:
	"Possible error codes include:"

To make clearer that the list is non-exhaustive and other error codes
may also happen. I remember we had a bug in the past with some
multimedia application that were causing infinite loops while handling
some VIDIOC_ENUM_* ioctl because it was programmed to expect *just*
one error code (-EINVAL - I guess, to identify the end of an enum).

> +
> +    <variablelist>
> +      <varlistentry>
> +	<term><errorcode>EACCES</errorcode></term>
> +	<listitem>
> +	  <para>The requested access to the file is not allowed.</para>
> +	</listitem>
> +      </varlistentry>
> +      <varlistentry>
> +	<term><errorcode>EMFILE</errorcode></term>
> +	<listitem>
> +	  <para>The  process  already  has  the  maximum number of files open.
> +	  </para>
> +	</listitem>
> +      </varlistentry>
> +      <varlistentry>
> +	<term><errorcode>ENFILE</errorcode></term>
> +	<listitem>
> +	  <para>The system limit on the total number of open files has been
> +	  reached.</para>
> +	</listitem>
> +      </varlistentry>
> +      <varlistentry>
> +	<term><errorcode>ENOMEM</errorcode></term>
> +	<listitem>
> +	  <para>Insufficient kernel memory was available.</para>
> +	</listitem>
> +      </varlistentry>
> +      <varlistentry>
> +	<term><errorcode>ENXIO</errorcode></term>
> +	<listitem>
> +	  <para>No device corresponding to this device special file exists.
> +	  </para>
> +	</listitem>
> +      </varlistentry>
> +    </variablelist>
> +  </refsect1>
> +</refentry>
> diff --git a/Documentation/DocBook/media/v4l/cec-func-poll.xml b/Documentation/DocBook/media/v4l/cec-func-poll.xml
> new file mode 100644
> index 0000000..6853817
> --- /dev/null
> +++ b/Documentation/DocBook/media/v4l/cec-func-poll.xml
> @@ -0,0 +1,89 @@
> +<refentry id="cec-func-poll">
> +  <refmeta>
> +    <refentrytitle>cec poll()</refentrytitle>
> +    &manvol;
> +  </refmeta>
> +
> +  <refnamediv>
> +    <refname>cec-poll</refname>
> +    <refpurpose>Wait for some event on a file descriptor</refpurpose>
> +  </refnamediv>
> +
> +  <refsynopsisdiv>
> +    <funcsynopsis>
> +      <funcsynopsisinfo>#include &lt;sys/poll.h&gt;</funcsynopsisinfo>
> +      <funcprototype>
> +	<funcdef>int <function>poll</function></funcdef>
> +	<paramdef>struct pollfd *<parameter>ufds</parameter></paramdef>
> +	<paramdef>unsigned int <parameter>nfds</parameter></paramdef>
> +	<paramdef>int <parameter>timeout</parameter></paramdef>
> +      </funcprototype>
> +    </funcsynopsis>
> +  </refsynopsisdiv>
> +
> +  <refsect1>
> +    <title>Description</title>
> +
> +    <para>With the <function>poll()</function> function applications
> +can wait for CEC events.</para>
> +
> +    <para>On success <function>poll()</function> returns the number of
> +file descriptors that have been selected (that is, file descriptors
> +for which the <structfield>revents</structfield> field of the
> +respective <structname>pollfd</structname> structure is non-zero).
> +CEC devices set the <constant>POLLIN</constant> and
> +<constant>POLLRDNORM</constant> flags in the
> +<structfield>revents</structfield> field if there are messages in the
> +receive queue. If the transmit queue has room for new messages, the
> +<constant>POLLOUT</constant> and <constant>POLLWRNORM</constant>
> +flags are set. If there are events in the event queue, then the
> +<constant>POLLPRI</constant> flag is set.
> +When the function timed out it returns a value of zero, on
> +failure it returns <returnvalue>-1</returnvalue> and the
> +<varname>errno</varname> variable is set appropriately.
> +</para>
> +
> +    <para>For more details see the
> +<function>poll()</function> manual page.</para>
> +  </refsect1>
> +
> +  <refsect1>
> +    <title>Return Value</title>
> +
> +    <para>On success, <function>poll()</function> returns the number
> +structures which have non-zero <structfield>revents</structfield>
> +fields, or zero if the call timed out. On error
> +<returnvalue>-1</returnvalue> is returned, and the
> +<varname>errno</varname> variable is set appropriately:</para>
> +
> +    <variablelist>
> +      <varlistentry>
> +	<term><errorcode>EBADF</errorcode></term>
> +	<listitem>
> +	  <para>One or more of the <parameter>ufds</parameter> members
> +specify an invalid file descriptor.</para>
> +	</listitem>
> +      </varlistentry>
> +      <varlistentry>
> +	<term><errorcode>EFAULT</errorcode></term>
> +	<listitem>
> +	  <para><parameter>ufds</parameter> references an inaccessible
> +memory area.</para>
> +	</listitem>
> +      </varlistentry>
> +      <varlistentry>
> +	<term><errorcode>EINTR</errorcode></term>
> +	<listitem>
> +	  <para>The call was interrupted by a signal.</para>
> +	</listitem>
> +      </varlistentry>
> +      <varlistentry>
> +	<term><errorcode>EINVAL</errorcode></term>
> +	<listitem>
> +	  <para>The <parameter>nfds</parameter> argument is greater
> +than <constant>OPEN_MAX</constant>.</para>
> +	</listitem>
> +      </varlistentry>
> +    </variablelist>
> +  </refsect1>
> +</refentry>
> diff --git a/Documentation/DocBook/media/v4l/cec-ioc-adap-g-caps.xml b/Documentation/DocBook/media/v4l/cec-ioc-adap-g-caps.xml
> new file mode 100644
> index 0000000..b99ed22
> --- /dev/null
> +++ b/Documentation/DocBook/media/v4l/cec-ioc-adap-g-caps.xml
> @@ -0,0 +1,140 @@
> +<refentry id="cec-ioc-adap-g-caps">
> +  <refmeta>
> +    <refentrytitle>ioctl CEC_ADAP_G_CAPS</refentrytitle>
> +    &manvol;
> +  </refmeta>
> +
> +  <refnamediv>
> +    <refname>CEC_ADAP_G_CAPS</refname>
> +    <refpurpose>Query device capabilities</refpurpose>
> +  </refnamediv>
> +
> +  <refsynopsisdiv>
> +    <funcsynopsis>
> +      <funcprototype>
> +	<funcdef>int <function>ioctl</function></funcdef>
> +	<paramdef>int <parameter>fd</parameter></paramdef>
> +	<paramdef>int <parameter>request</parameter></paramdef>
> +	<paramdef>struct cec_caps *<parameter>argp</parameter></paramdef>
> +      </funcprototype>
> +    </funcsynopsis>
> +  </refsynopsisdiv>
> +
> +  <refsect1>
> +    <title>Arguments</title>
> +
> +    <variablelist>
> +      <varlistentry>
> +	<term><parameter>fd</parameter></term>
> +	<listitem>
> +	  <para>File descriptor returned by
> +	  <link linkend='cec-func-open'><function>open()</function></link>.</para>
> +	</listitem>
> +      </varlistentry>
> +      <varlistentry>
> +	<term><parameter>request</parameter></term>
> +	<listitem>
> +	  <para>CEC_ADAP_G_CAPS</para>
> +	</listitem>
> +      </varlistentry>
> +      <varlistentry>
> +	<term><parameter>argp</parameter></term>
> +	<listitem>
> +	  <para></para>
> +	</listitem>
> +      </varlistentry>
> +    </variablelist>
> +  </refsect1>
> +
> +  <refsect1>
> +    <title>Description</title>
> +
> +    <para>All cec devices must support the <constant>CEC_ADAP_G_CAPS</constant>
> +    ioctl. To query device information, applications call the ioctl with a
> +    pointer to a &cec-caps;. The driver fills the structure and returns
> +    the information to the application.
> +    The ioctl never fails.</para>
> +
> +    <table pgwide="1" frame="none" id="cec-caps">
> +      <title>struct <structname>cec_caps</structname></title>
> +      <tgroup cols="3">
> +	&cs-str;
> +	<tbody valign="top">
> +	  <row>
> +	    <entry>char</entry>
> +	    <entry><structfield>driver[32]</structfield></entry>
> +	    <entry>The name of the cec adapter driver.</entry>
> +	  </row>
> +	  <row>
> +	    <entry>char</entry>
> +	    <entry><structfield>name[32]</structfield></entry>
> +	    <entry>The name of this CEC adapter. The combination <structfield>driver</structfield>
> +	    and <structfield>name</structfield> must be unique.</entry>
> +	  </row>
> +	  <row>
> +	    <entry>__u32</entry>
> +	    <entry><structfield>capabilities</structfield></entry>
> +	    <entry>The capabilities of the CEC adapter, see <xref
> +		linkend="cec-capabilities" />.</entry>
> +	  </row>
> +	  <row>
> +	    <entry>__u32</entry>
> +	    <entry><structfield>version</structfield></entry>
> +	    <entry>CEC Framework API version, formatted with the
> +	    <constant>KERNEL_VERSION()</constant> macro.</entry>
> +	  </row>
> +	</tbody>
> +      </tgroup>
> +    </table>
> +
> +    <table pgwide="1" frame="none" id="cec-capabilities">
> +      <title>CEC Capabilities Flags</title>
> +      <tgroup cols="3">
> +	&cs-def;
> +	<tbody valign="top">
> +	  <row>
> +	    <entry><constant>CEC_CAP_PHYS_ADDR</constant></entry>
> +	    <entry>0x00000001</entry>
> +	    <entry>Userspace has to configure the physical address by
> +	    calling &CEC-ADAP-S-PHYS-ADDR;.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_CAP_LOG_ADDRS</constant></entry>
> +	    <entry>0x00000002</entry>
> +	    <entry>Userspace has to configure the logical addresses by
> +	    calling &CEC-ADAP-S-LOG-ADDRS;.</entry>

IMHO, on the two above, it is not clear what they means.

I mean, it should clearly state what userspace should do when:
- the flag is active (that's described);
- the flag is not active. This is not described. As all CEC
  devices need some address, if the capability is disabled,
  what userspace is supposed to do? How the device will get the
  address?

> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_CAP_TRANSMIT</constant></entry>
> +	    <entry>0x00000004</entry>
> +	    <entry>Userspace can transmit CEC messages by calling &CEC-TRANSMIT;. This
> +	    implies that userspace can be a follower as well, since being able to
> +	    transmit messages is a prerequisite of becoming a follower.
> +	    </entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_CAP_PASSTHROUGH</constant></entry>
> +	    <entry>0x00000008</entry>
> +	    <entry>Userspace can use the passthrough mode by
> +	    calling &CEC-S-MODE;.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_CAP_RC</constant></entry>
> +	    <entry>0x00000010</entry>
> +	    <entry>This adapter supports the remote control protocol.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_CAP_MONITOR_ALL</constant></entry>
> +	    <entry>0x00000020</entry>
> +	    <entry>The CEC hardware can monitor all messages, not just directed and
> +	    broadcast messages.</entry>
> +	  </row>
> +	</tbody>
> +      </tgroup>
> +    </table>
> +  </refsect1>
> +
> +  <refsect1>
> +    &return-value;
> +  </refsect1>
> +</refentry>
> diff --git a/Documentation/DocBook/media/v4l/cec-ioc-adap-g-log-addrs.xml b/Documentation/DocBook/media/v4l/cec-ioc-adap-g-log-addrs.xml
> new file mode 100644
> index 0000000..01bc5ab
> --- /dev/null
> +++ b/Documentation/DocBook/media/v4l/cec-ioc-adap-g-log-addrs.xml
> @@ -0,0 +1,324 @@
> +<refentry id="cec-ioc-adap-g-log-addrs">
> +  <refmeta>
> +    <refentrytitle>ioctl CEC_ADAP_G_LOG_ADDRS, CEC_ADAP_S_LOG_ADDRS</refentrytitle>
> +    &manvol;
> +  </refmeta>
> +
> +  <refnamediv>
> +    <refname>CEC_ADAP_G_LOG_ADDRS</refname>
> +    <refname>CEC_ADAP_S_LOG_ADDRS</refname>
> +    <refpurpose>Get or set the logical addresses</refpurpose>
> +  </refnamediv>
> +
> +  <refsynopsisdiv>
> +    <funcsynopsis>
> +      <funcprototype>
> +	<funcdef>int <function>ioctl</function></funcdef>
> +	<paramdef>int <parameter>fd</parameter></paramdef>
> +	<paramdef>int <parameter>request</parameter></paramdef>
> +	<paramdef>struct cec_log_addrs *<parameter>argp</parameter></paramdef>
> +      </funcprototype>
> +    </funcsynopsis>
> +  </refsynopsisdiv>
> +
> +  <refsect1>
> +    <title>Arguments</title>
> +
> +    <variablelist>
> +      <varlistentry>
> +	<term><parameter>fd</parameter></term>
> +	<listitem>
> +	  <para>File descriptor returned by
> +	  <link linkend='cec-func-open'><function>open()</function></link>.</para>
> +	</listitem>
> +      </varlistentry>
> +      <varlistentry>
> +	<term><parameter>request</parameter></term>
> +	<listitem>
> +	  <para>CEC_ADAP_G_LOG_ADDRS, CEC_ADAP_S_LOG_ADDRS</para>
> +	</listitem>
> +      </varlistentry>
> +      <varlistentry>
> +	<term><parameter>argp</parameter></term>
> +	<listitem>
> +	  <para></para>
> +	</listitem>
> +      </varlistentry>
> +    </variablelist>
> +  </refsect1>
> +
> +  <refsect1>
> +    <title>Description</title>
> +
> +    <para>To query the current CEC logical addresses applications call the

 <para>To query the current CEC logical addresses applications, call the
	(comma is missing)


> +<constant>CEC_ADAP_G_LOG_ADDRS</constant> ioctl with a pointer to a
> +<structname>cec_log_addrs</structname> structure where the drivers stores the
> +logical addresses.</para>
	"store"

> +
> +    <para>To set new logical addresses applications fill in struct <structname>cec_log_addrs</structname>

Comma is also missed above.

> +and call the <constant>CEC_ADAP_S_LOG_ADDRS</constant> ioctl with a pointer to this struct.
> +The <constant>CEC_ADAP_S_LOG_ADDRS</constant> ioctl is only available if
> +<constant>CEC_CAP_LOG_ADDRS</constant> is set. 

Please mention the return code when the ioctl is not available.

> This ioctl will block until all
> +requested logical addresses have been claimed. <constant>CEC_ADAP_S_LOG_ADDRS</constant>
> +can only be called by a file handle in initiator mode (see &CEC-S-MODE;).</para>
> +
> +    <table pgwide="1" frame="none" id="cec-log-addrs">
> +      <title>struct <structname>cec_log_addrs</structname></title>
> +      <tgroup cols="3">
> +	&cs-str;
> +	<tbody valign="top">
> +	  <row>
> +	    <entry>__u8</entry>
> +	    <entry><structfield>log_addr</structfield>[CEC_MAX_LOG_ADDRS]</entry>
> +	    <entry>The actual logical addresses that were claimed. This is set by the
> +	    driver. If no logical address could be claimed, then it is set to
> +	    <constant>CEC_LOG_ADDR_INVALID</constant>. If this adapter is Unregistered,
> +	    then <structfield>log_addr[0]</structfield> is set to 0xf and all others to
> +	    <constant>CEC_LOG_ADDR_INVALID</constant>.</entry>
> +	  </row>
> +	  <row>
> +	    <entry>__u16</entry>
> +	    <entry><structfield>log_addr_mask</structfield></entry>
> +	    <entry>The bitmask of all logical addresses this adapter has claimed.
> +	    If this adapter is Unregistered then <structfield>log_addr_mask</structfield>
> +	    sets bit 15, if this adapter is not configured at all, then
> +	    <structfield>log_addr_mask</structfield> is set to 0. Set by the driver.</entry>

Huh? that "If .. then .., if then" is very confusing. Didn't get what you're meant
to say. Should it be a period instead of a comma before the second if?

> +	  </row>
> +	  <row>
> +	    <entry>__u8</entry>
> +	    <entry><structfield>cec_version</structfield></entry>
> +	    <entry>The CEC version that this adapter shall use. See
> +	    <xref linkend="cec-versions" />.
> +	    Used to implement the <constant>CEC_MSG_CEC_VERSION</constant> and
> +	    <constant>CEC_MSG_REPORT_FEATURES</constant> messages. Note that
> +	    <constant>CEC_OP_CEC_VERSION_1_3A</constant> is not allowed
> +	    by the CEC framework.
> +	    </entry>
> +	  </row>
> +	  <row>
> +	    <entry>__u8</entry>
> +	    <entry><structfield>num_log_addrs</structfield></entry>
> +	    <entry>Number of logical addresses to set up. Must be &le;
> +	    <structfield>available_log_addrs</structfield> as returned by
> +	    &CEC-ADAP-G-CAPS;. All arrays in this structure are only filled up to
> +	    index <structfield>available_log_addrs</structfield>-1. The remaining
> +	    array elements will be ignored. Note that the CEC 2.0 standard allows
> +	    for a maximum of 2 logical addresses, although some hardware has support
> +	    for more. <constant>CEC_MAX_LOG_ADDRS</constant> is 4. The driver will
> +	    return the actual number of logical addresses it could claim, which may
> +	    be less than what was requested. If this field is set to 0, then the
> +	    CEC adapter shall clear all claimed logical addresses and all other
> +	    fields will be ignored.</entry>
> +	  </row>
> +	  <row>
> +	    <entry>__u32</entry>
> +	    <entry><structfield>vendor_id</structfield></entry>
> +	    <entry>The vendor ID is a 24-bit number that identifies the specific
> +	    vendor or entity. Based on this ID vendor specific commands may be
> +	    defined. If you do not want a vendor ID then set it to
> +	    <constant>CEC_VENDOR_ID_NONE</constant>.</entry>
> +	  </row>
> +	  <row>
> +	    <entry>__u32</entry>
> +	    <entry><structfield>flags</structfield></entry>
> +	    <entry>Flags. No flags are defined yet, so set this to 0.</entry>
> +	  </row>
> +	  <row>
> +	    <entry>char</entry>
> +	    <entry><structfield>osd_name</structfield>[15]</entry>
> +	    <entry>The On-Screen Display name as is returned by the
> +	    <constant>CEC_MSG_SET_OSD_NAME</constant> message.</entry>
> +	  </row>
> +	  <row>
> +	    <entry>__u8</entry>
> +	    <entry><structfield>primary_device_type</structfield>[CEC_MAX_LOG_ADDRS]</entry>
> +	    <entry>Primary device type for each logical address. See
> +	    <xref linkend="cec-prim-dev-types" /> for possible types.</entry>
> +	  </row>
> +	  <row>
> +	    <entry>__u8</entry>
> +	    <entry><structfield>log_addr_type</structfield>[CEC_MAX_LOG_ADDRS]</entry>
> +	    <entry>Logical address types. See <xref linkend="cec-log-addr-types" /> for
> +	    possible types. The driver will update this with the actual logical address
> +	    type that it claimed (e.g. it may have to fallback to
> +	    <constant>CEC_LOG_ADDR_TYPE_UNREGISTERED</constant>).</entry>
> +	  </row>
> +	  <row>
> +	    <entry>__u8</entry>
> +	    <entry><structfield>all_device_types</structfield>[CEC_MAX_LOG_ADDRS]</entry>
> +	    <entry>CEC 2.0 specific: all device types. See <xref linkend="cec-all-dev-types-flags" />.
> +	    Used to implement the <constant>CEC_MSG_REPORT_FEATURES</constant> message.
> +	    This field is ignored if <structfield>cec_version</structfield> &lt;
> +	    <constant>CEC_OP_CEC_VERSION_2_0</constant>.</entry>
> +	  </row>
> +	  <row>
> +	    <entry>__u8</entry>
> +	    <entry><structfield>features</structfield>[CEC_MAX_LOG_ADDRS][12]</entry>
> +	    <entry>Features for each logical address. Used to implement the
> +	    <constant>CEC_MSG_REPORT_FEATURES</constant> message. The 12 bytes include
> +	    both the RC Profile and the Device Features.
> +	    This field is ignored if <structfield>cec_version</structfield> &lt;
> +	    <constant>CEC_OP_CEC_VERSION_2_0</constant>.</entry>
> +	  </row>
> +	</tbody>
> +      </tgroup>
> +    </table>
> +
> +    <table pgwide="1" frame="none" id="cec-versions">
> +      <title>CEC Versions</title>
> +      <tgroup cols="3">
> +	&cs-def;
> +	<tbody valign="top">
> +	  <row>
> +	    <entry><constant>CEC_OP_CEC_VERSION_1_3A</constant></entry>
> +	    <entry>4</entry>
> +	    <entry>CEC version according to the HDMI 1.3a standard.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_OP_CEC_VERSION_1_4B</constant></entry>
> +	    <entry>5</entry>
> +	    <entry>CEC version according to the HDMI 1.4b standard.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_OP_CEC_VERSION_2_0</constant></entry>
> +	    <entry>6</entry>
> +	    <entry>CEC version according to the HDMI 2.0 standard.</entry>
> +	  </row>
> +	</tbody>
> +      </tgroup>
> +    </table>
> +
> +    <table pgwide="1" frame="none" id="cec-prim-dev-types">
> +      <title>CEC Primary Device Types</title>
> +      <tgroup cols="3">
> +	&cs-def;
> +	<tbody valign="top">
> +	  <row>
> +	    <entry><constant>CEC_OP_PRIM_DEVTYPE_TV</constant></entry>
> +	    <entry>0</entry>
> +	    <entry>Use for a TV.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_OP_PRIM_DEVTYPE_RECORD</constant></entry>
> +	    <entry>1</entry>
> +	    <entry>Use for a recording device.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_OP_PRIM_DEVTYPE_TUNER</constant></entry>
> +	    <entry>3</entry>
> +	    <entry>Use for a device with a tuner.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_OP_PRIM_DEVTYPE_PLAYBACK</constant></entry>
> +	    <entry>4</entry>
> +	    <entry>Use for a playback device.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM</constant></entry>
> +	    <entry>5</entry>
> +	    <entry>Use for an audio system (e.g. an audio/video receiver).</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_OP_PRIM_DEVTYPE_SWITCH</constant></entry>
> +	    <entry>6</entry>
> +	    <entry>Use for a CEC switch.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_OP_PRIM_DEVTYPE_VIDEOPROC</constant></entry>
> +	    <entry>7</entry>
> +	    <entry>Use for a video processor device.</entry>
> +	  </row>
> +	</tbody>
> +      </tgroup>
> +    </table>
> +
> +    <table pgwide="1" frame="none" id="cec-log-addr-types">
> +      <title>CEC Logical Address Types</title>
> +      <tgroup cols="3">
> +	&cs-def;
> +	<tbody valign="top">
> +	  <row>
> +	    <entry><constant>CEC_LOG_ADDR_TYPE_TV</constant></entry>
> +	    <entry>0</entry>
> +	    <entry>Use for a TV.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_LOG_ADDR_TYPE_RECORD</constant></entry>
> +	    <entry>1</entry>
> +	    <entry>Use for a recording device.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_LOG_ADDR_TYPE_TUNER</constant></entry>
> +	    <entry>2</entry>
> +	    <entry>Use for a tuner device.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_LOG_ADDR_TYPE_PLAYBACK</constant></entry>
> +	    <entry>3</entry>
> +	    <entry>Use for a playback device.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_LOG_ADDR_TYPE_AUDIOSYSTEM</constant></entry>
> +	    <entry>4</entry>
> +	    <entry>Use for an audio system device.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_LOG_ADDR_TYPE_SPECIFIC</constant></entry>
> +	    <entry>5</entry>
> +	    <entry>Use for a second TV or for a video processor device.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_LOG_ADDR_TYPE_UNREGISTERED</constant></entry>
> +	    <entry>6</entry>
> +	    <entry>Use this if you just want to remain unregistered.
> +	    Used for pure CEC switches or CDC-only devices (CDC:
> +	    Capability Discovery and Control).</entry>
> +	  </row>
> +	</tbody>
> +      </tgroup>
> +    </table>
> +
> +    <table pgwide="1" frame="none" id="cec-all-dev-types-flags">
> +      <title>CEC All Device Types Flags</title>
> +      <tgroup cols="3">
> +	&cs-def;
> +	<tbody valign="top">
> +	  <row>
> +	    <entry><constant>CEC_OP_ALL_DEVTYPE_TV</constant></entry>
> +	    <entry>0x80</entry>
> +	    <entry>This supports the TV type.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_OP_ALL_DEVTYPE_RECORD</constant></entry>
> +	    <entry>0x40</entry>
> +	    <entry>This supports the Recording type.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_OP_ALL_DEVTYPE_TUNER</constant></entry>
> +	    <entry>0x20</entry>
> +	    <entry>This supports the Tuner type.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_OP_ALL_DEVTYPE_PLAYBACK</constant></entry>
> +	    <entry>0x10</entry>
> +	    <entry>This supports the Playback type.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM</constant></entry>
> +	    <entry>0x08</entry>
> +	    <entry>This supports the Audio System type.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_OP_ALL_DEVTYPE_SWITCH</constant></entry>
> +	    <entry>0x04</entry>
> +	    <entry>This supports the CEC Switch or Video Processing type.</entry>
> +	  </row>
> +	</tbody>
> +      </tgroup>
> +    </table>
> +  </refsect1>
> +
> +  <refsect1>
> +    &return-value;
> +  </refsect1>
> +</refentry>
> diff --git a/Documentation/DocBook/media/v4l/cec-ioc-adap-g-phys-addr.xml b/Documentation/DocBook/media/v4l/cec-ioc-adap-g-phys-addr.xml
> new file mode 100644
> index 0000000..7a08269
> --- /dev/null
> +++ b/Documentation/DocBook/media/v4l/cec-ioc-adap-g-phys-addr.xml
> @@ -0,0 +1,82 @@
> +<refentry id="cec-ioc-adap-g-phys-addr">
> +  <refmeta>
> +    <refentrytitle>ioctl CEC_ADAP_G_PHYS_ADDR, CEC_ADAP_S_PHYS_ADDR</refentrytitle>
> +    &manvol;
> +  </refmeta>
> +
> +  <refnamediv>
> +    <refname>CEC_ADAP_G_PHYS_ADDR</refname>
> +    <refname>CEC_ADAP_S_PHYS_ADDR</refname>
> +    <refpurpose>Get or set the physical address</refpurpose>
> +  </refnamediv>
> +
> +  <refsynopsisdiv>
> +    <funcsynopsis>
> +      <funcprototype>
> +	<funcdef>int <function>ioctl</function></funcdef>
> +	<paramdef>int <parameter>fd</parameter></paramdef>
> +	<paramdef>int <parameter>request</parameter></paramdef>
> +	<paramdef>__u16 *<parameter>argp</parameter></paramdef>
> +      </funcprototype>
> +    </funcsynopsis>
> +  </refsynopsisdiv>
> +
> +  <refsect1>
> +    <title>Arguments</title>
> +
> +    <variablelist>
> +      <varlistentry>
> +	<term><parameter>fd</parameter></term>
> +	<listitem>
> +	  <para>File descriptor returned by
> +	  <link linkend='cec-func-open'><function>open()</function></link>.</para>
> +	</listitem>
> +      </varlistentry>
> +      <varlistentry>
> +	<term><parameter>request</parameter></term>
> +	<listitem>
> +	  <para>CEC_ADAP_G_PHYS_ADDR, CEC_ADAP_S_PHYS_ADDR</para>
> +	</listitem>
> +      </varlistentry>
> +      <varlistentry>
> +	<term><parameter>argp</parameter></term>
> +	<listitem>
> +	  <para></para>
> +	</listitem>
> +      </varlistentry>
> +    </variablelist>
> +  </refsect1>
> +
> +  <refsect1>
> +    <title>Description</title>
> +
> +    <para>To query the current physical address applications call the
> +<constant>CEC_ADAP_G_PHYS_ADDR</constant> ioctl with a pointer to an __u16
> +where the driver stores the physical address.</para>
> +
> +    <para>To set a new physical address applications store the physical address in
> +an __u16 and call the <constant>CEC_ADAP_S_PHYS_ADDR</constant> ioctl with a
> +pointer to this integer. <constant>CEC_ADAP_S_PHYS_ADDR</constant> is only
> +available if <constant>CEC_CAP_PHYS_ADDR</constant> is set. It is not allowed
> +to change a valid physical address to another valid physical address: you must
> +select <constant>CEC_PHYS_ADDR_INVALID</constant> (f.f.f.f) first.
> +<constant>CEC_ADAP_S_PHYS_ADDR</constant>
> +can only be called by a file handle in initiator mode (see &CEC-S-MODE;).</para>

Please mention the error code that will be returned if the user tries to change
from one valid address to another one.


> +
> +    <para>The physical address is a 16-bit number where each group of 4 bits
> +represent a digit of the physical address a.b.c.d where the most significant
> +4 bits represent 'a'. The CEC root device (usually the TV) has address 0.0.0.0.
> +Every device that is hooked up to an input of the TV has address a.0.0.0 (where
> +'a' is &ge; 1), devices hooked up to those in turn have addresses a.b.0.0, etc.
> +So a topology of up to 5 devices deep is supported. The physical address a
> +device shall use is stored in the EDID of the sink.</para>
> +
> +<para>For example, the EDID for each HDMI input of the TV will have a different
> +physical address of the form a.0.0.0 that the sources will read out and use as
> +their physical address.</para>
> +  </refsect1>
> +
> +  <refsect1>
> +    &return-value;
> +  </refsect1>
> +</refentry>
> diff --git a/Documentation/DocBook/media/v4l/cec-ioc-dqevent.xml b/Documentation/DocBook/media/v4l/cec-ioc-dqevent.xml
> new file mode 100644
> index 0000000..6a9960f
> --- /dev/null
> +++ b/Documentation/DocBook/media/v4l/cec-ioc-dqevent.xml
> @@ -0,0 +1,190 @@
> +<refentry id="cec-ioc-g-event">
> +  <refmeta>
> +    <refentrytitle>ioctl CEC_DQEVENT</refentrytitle>
> +    &manvol;
> +  </refmeta>
> +
> +  <refnamediv>
> +    <refname>CEC_DQEVENT</refname>
> +    <refpurpose>Dequeue a CEC event</refpurpose>
> +  </refnamediv>
> +
> +  <refsynopsisdiv>
> +    <funcsynopsis>
> +      <funcprototype>
> +	<funcdef>int <function>ioctl</function></funcdef>
> +	<paramdef>int <parameter>fd</parameter></paramdef>
> +	<paramdef>int <parameter>request</parameter></paramdef>
> +	<paramdef>struct cec_event *<parameter>argp</parameter></paramdef>
> +      </funcprototype>
> +    </funcsynopsis>
> +  </refsynopsisdiv>
> +
> +  <refsect1>
> +    <title>Arguments</title>
> +
> +    <variablelist>
> +      <varlistentry>
> +	<term><parameter>fd</parameter></term>
> +	<listitem>
> +	  <para>File descriptor returned by
> +	  <link linkend='cec-func-open'><function>open()</function></link>.</para>
> +	</listitem>
> +      </varlistentry>
> +      <varlistentry>
> +	<term><parameter>request</parameter></term>
> +	<listitem>
> +	  <para>CEC_DQEVENT</para>
> +	</listitem>
> +      </varlistentry>
> +      <varlistentry>
> +	<term><parameter>argp</parameter></term>
> +	<listitem>
> +	  <para></para>
> +	</listitem>
> +      </varlistentry>
> +    </variablelist>
> +  </refsect1>
> +
> +  <refsect1>
> +    <title>Description</title>
> +
> +    <para>CEC devices can send asynchronous events. These can be retrieved by calling
> +    the <constant>CEC_DQEVENT</constant> ioctl. If the file descriptor is in non-blocking
> +    mode and no event is pending, then it will return -1 and set errno to the &EAGAIN;.</para>
> +
> +    <para>The internal event queues are per-filehandle and per-event type. If there is
> +    no more room in a queue then the last event is overwritten with the new one. This
> +    means that intermediate results can be thrown away but that the latest event is always
> +    available. This also mean that is it possible to read two successive events that have
> +    the same value (e.g. two CEC_EVENT_STATE_CHANGE events with the same state). In that
> +    case the intermediate state changes were lost but it is guaranteed that the state
> +    did change in between the two events.</para>
> +
> +    <table pgwide="1" frame="none" id="cec-event-state-change">
> +      <title>struct <structname>cec_event_state_change</structname></title>
> +      <tgroup cols="3">
> +	&cs-str;
> +	<tbody valign="top">
> +	  <row>
> +	    <entry>__u16</entry>
> +	    <entry><structfield>phys_addr</structfield></entry>
> +	    <entry>The current physical address.</entry>
> +	  </row>
> +	  <row>
> +	    <entry>__u16</entry>
> +	    <entry><structfield>log_addr_mask</structfield></entry>
> +	    <entry>The current set of claimed logical addresses.</entry>
> +	  </row>
> +	</tbody>
> +      </tgroup>
> +    </table>
> +
> +    <table pgwide="1" frame="none" id="cec-event-lost-msgs">
> +      <title>struct <structname>cec_event_lost_msgs</structname></title>
> +      <tgroup cols="3">
> +	&cs-str;
> +	<tbody valign="top">
> +	  <row>
> +	    <entry>__u32</entry>
> +	    <entry><structfield>lost_msgs</structfield></entry>
> +	    <entry>Set to the number of lost messages since the filehandle
> +	    was opened or since the last time this event was dequeued for
> +	    this filehandle.</entry>
> +	  </row>
> +	</tbody>
> +      </tgroup>
> +    </table>
> +
> +    <table pgwide="1" frame="none" id="cec-event">
> +      <title>struct <structname>cec_event</structname></title>
> +      <tgroup cols="4">
> +	&cs-str;
> +	<tbody valign="top">
> +	  <row>
> +	    <entry>__u64</entry>
> +	    <entry><structfield>ts</structfield></entry>
> +	    <entry>Timestamp of the event in ns.</entry>
> +	    <entry></entry>
> +	  </row>
> +	  <row>
> +	    <entry>__u32</entry>
> +	    <entry><structfield>event</structfield></entry>
> +	    <entry>The event, see <xref linkend="cec-events" />.</entry>

"The Event" sounds like the name of a movie ;) Hmm... wait a moment...
it *is* the name of a movie already: http://www.imdb.com/title/tt0314039/ :)

I guess it would be better to describe it as "The event type", as
this is actually the type of the event, as the event itself is 
described by the hole structure.

> +	    <entry></entry>
> +	  </row>
> +	  <row>
> +	    <entry>__u32</entry>
> +	    <entry><structfield>flags</structfield></entry>
> +	    <entry>Event flags, see <xref linkend="cec-event-flags" />.</entry>
> +	    <entry></entry>
> +	  </row>
> +	  <row>
> +	    <entry>union</entry>
> +	    <entry>(anonymous)</entry>
> +	    <entry></entry>
> +	    <entry></entry>
> +	  </row>
> +	  <row>
> +	    <entry></entry>
> +	    <entry>struct cec_event_state_change</entry>
> +	    <entry><structfield>state_change</structfield></entry>
> +	    <entry>The new adapter state as sent by the <constant>CEC_EVENT_STATE_CHANGE</constant>
> +	    event.</entry>
> +	  </row>
> +	  <row>
> +	    <entry></entry>
> +	    <entry>struct cec_event_lost_msgs</entry>
> +	    <entry><structfield>lost_msgs</structfield></entry>
> +	    <entry>The number of lost messages as sent by the <constant>CEC_EVENT_LOST_MSGS</constant>
> +	    event.</entry>
> +	  </row>
> +	</tbody>
> +      </tgroup>
> +    </table>
> +
> +    <table pgwide="1" frame="none" id="cec-events">
> +      <title>CEC Events</title>
> +      <tgroup cols="3">
> +	&cs-def;
> +	<tbody valign="top">
> +	  <row>
> +	    <entry><constant>CEC_EVENT_STATE_CHANGE</constant></entry>
> +	    <entry>1</entry>
> +	    <entry>Generated when the CEC Adapter's state changes. When open() is
> +	    called an initial event will be generated for that filehandle with the
> +	    CEC Adapter's state at that time.
> +	    </entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_EVENT_LOST_MSGS</constant></entry>
> +	    <entry>2</entry>
> +	    <entry>Generated if one or more CEC messages were lost because the
> +	    application didn't dequeue CEC messages fast enough.</entry>
> +	  </row>
> +	</tbody>
> +      </tgroup>
> +    </table>
> +
> +    <table pgwide="1" frame="none" id="cec-event-flags">
> +      <title>CEC Event Flags</title>
> +      <tgroup cols="3">
> +	&cs-def;
> +	<tbody valign="top">
> +	  <row>
> +	    <entry><constant>CEC_EVENT_FL_INITIAL_VALUE</constant></entry>
> +	    <entry>1</entry>
> +	    <entry>Set for the initial events that are generated when the device is
> +	    opened. See the table above for which events do this. This allows
> +	    applications to learn the initial state of the CEC adapter at open()
> +	    time.</entry>
> +	  </row>
> +	</tbody>
> +      </tgroup>
> +    </table>
> +  </refsect1>
> +
> +  <refsect1>
> +    &return-value;
> +  </refsect1>
> +</refentry>
> diff --git a/Documentation/DocBook/media/v4l/cec-ioc-g-mode.xml b/Documentation/DocBook/media/v4l/cec-ioc-g-mode.xml
> new file mode 100644
> index 0000000..0cb1941
> --- /dev/null
> +++ b/Documentation/DocBook/media/v4l/cec-ioc-g-mode.xml
> @@ -0,0 +1,245 @@
> +<refentry id="cec-ioc-g-mode">
> +  <refmeta>
> +    <refentrytitle>ioctl CEC_G_MODE, CEC_S_MODE</refentrytitle>
> +    &manvol;
> +  </refmeta>
> +
> +  <refnamediv>
> +    <refname>CEC_G_MODE</refname>
> +    <refname>CEC_S_MODE</refname>
> +    <refpurpose>Get or set exclusive use of the CEC adapter</refpurpose>
> +  </refnamediv>
> +
> +  <refsynopsisdiv>
> +    <funcsynopsis>
> +      <funcprototype>
> +	<funcdef>int <function>ioctl</function></funcdef>
> +	<paramdef>int <parameter>fd</parameter></paramdef>
> +	<paramdef>int <parameter>request</parameter></paramdef>
> +	<paramdef>__u32 *<parameter>argp</parameter></paramdef>
> +      </funcprototype>
> +    </funcsynopsis>
> +  </refsynopsisdiv>
> +
> +  <refsect1>
> +    <title>Arguments</title>
> +
> +    <variablelist>
> +      <varlistentry>
> +	<term><parameter>fd</parameter></term>
> +	<listitem>
> +	  <para>File descriptor returned by
> +	  <link linkend='cec-func-open'><function>open()</function></link>.</para>
> +	</listitem>
> +      </varlistentry>
> +      <varlistentry>
> +	<term><parameter>request</parameter></term>
> +	<listitem>
> +	  <para>CEC_G_MODE, CEC_S_MODE</para>
> +	</listitem>
> +      </varlistentry>
> +      <varlistentry>
> +	<term><parameter>argp</parameter></term>
> +	<listitem>
> +	  <para></para>
> +	</listitem>
> +      </varlistentry>
> +    </variablelist>
> +  </refsect1>
> +
> +  <refsect1>
> +    <title>Description</title>
> +
> +    <para>By default any filehandle can use &CEC-TRANSMIT; and &CEC-RECEIVE;, but
> +in order to prevent applications from stepping on each others toes you want to get
> +exclusive access to the CEC adapter. This ioctl allows you to try and become the
> +exclusive initiator and/or follower. The initiator is the filehandle that is used
> +to initiate messages, i.e. it commands other CEC devices. The follower is the filehandle
> +that receives messages sent to our CEC adapter and processes them. The same filehandle

"to our CEC adapter" -> "to the directly controlled CEC adapter"

> +can be both initiator and follower, or this role can be taken by two different
> +filehandles.</para>
> +
> +    <para>When a CEC message is received, then the CEC framework will decide how
> +it will be processed. If the message is a reply to an earlier transmitted message,
> +then the reply is sent back to the filehandle that is waiting for it. In addition
> +the CEC framework will process it.</para>
> +
> +    <para>If the message is not a reply, then the CEC framework will process it
> +first. If there is no follower, then the message is just discarded and a feature
> +abort is sent back to the initiator if the framework couldn't process it. If there
> +is a follower, then the message is passed on to the follower who will use
> +&CEC-RECEIVE; to dequeue the new message. The framework expects the follower to
> +make the right decisions.</para>
> +
> +    <para>The CEC framework will process core messages unless requested otherwise
> +by the follower. The follower can enable the passthrough mode. In that case the

Comma missing: "In that case, "

> +CEC framework will pass on most core messages without processing them. In that
> +case the follower will have to implement those messages. There are some messages
> +that the core will always process, regardless of the passthrough mode.</para>
> +
> +    <para>If there is no initiator, then any CEC filehandle can use &CEC-TRANSMIT;.
> +If there is an exclusive initiator then only that initiator can call &CEC-TRANSMIT;.
> +The follower can of course always call &CEC-TRANSMIT;.</para>
> +
> +    <para>Available initiator modes are:</para>
> +
> +    <table pgwide="1" frame="none" id="cec-mode-initiator">
> +      <title>Initiator Modes</title>
> +      <tgroup cols="3">
> +	&cs-def;
> +	<tbody valign="top">
> +	  <row>
> +	    <entry><constant>CEC_MODE_NO_INITIATOR</constant></entry>
> +	    <entry>0x0</entry>
> +	    <entry>This is not an initiator, i.e. it cannot transmit CEC messages
> +	    or make any other changes to the CEC adapter.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_MODE_INITIATOR</constant></entry>
> +	    <entry>0x1</entry>
> +	    <entry>This is an initiator (the default when the device is opened) and it
> +	    can transmit CEC messages and make changes to the CEC adapter, unless there
> +	    is an exclusive initiator.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_MODE_EXCL_INITIATOR</constant></entry>
> +	    <entry>0x2</entry>
> +	    <entry>This is an exclusive initiator and this file descriptor is the only one
> +	    that can transmit CEC messages and make changes to the CEC adapter. If someone
> +	    else is already the exclusive initiator then an attempt to become one will return
> +	    the &EBUSY; error.</entry>
> +	  </row>
> +	</tbody>
> +      </tgroup>
> +    </table>
> +
> +    <para>Available follower modes are:</para>
> +
> +    <table pgwide="1" frame="none" id="cec-mode-follower">
> +      <title>Follower Modes</title>
> +      <tgroup cols="3">
> +	&cs-def;
> +	<tbody valign="top">
> +	  <row>
> +	    <entry><constant>CEC_MODE_NO_FOLLOWER</constant></entry>
> +	    <entry>0x00</entry>
> +	    <entry>This is not a follower (the default when the device is opened).</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_MODE_FOLLOWER</constant></entry>
> +	    <entry>0x10</entry>
> +	    <entry>This is a follower and it will receive CEC messages unless there is
> +	    an exclusive follower. You cannot become a follower if <constant>CEC_CAP_TRANSMIT</constant>
> +	    is not set or if <constant>CEC_MODE_NO_INITIATOR</constant> was specified,
> +	    &EINVAL; is returned in that case.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_MODE_EXCL_FOLLOWER</constant></entry>
> +	    <entry>0x20</entry>
> +	    <entry>This is an exclusive follower and only this file descriptor will receive
> +	    CEC messages for processing. If someone else is already the exclusive follower
> +	    then an attempt to become one will return the &EBUSY; error. You cannot become
> +	    a follower if <constant>CEC_CAP_TRANSMIT</constant> is not set or if
> +	    <constant>CEC_MODE_NO_INITIATOR</constant> was specified, &EINVAL; is returned
> +	    in that case.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_MODE_EXCL_FOLLOWER_PASSTHRU</constant></entry>
> +	    <entry>0x30</entry>
> +	    <entry>This is an exclusive follower and only this file descriptor will receive
> +	    CEC messages for processing. In addition it will put the CEC device into
> +	    passthrough mode, allowing the exclusive follower to handle most core messages
> +	    instead of relying on the CEC framework for that. If someone else is already the
> +	    exclusive follower then an attempt to become one will return the &EBUSY; error.
> +	    You cannot become a follower if <constant>CEC_CAP_TRANSMIT</constant>
> +            is not set or if <constant>CEC_MODE_NO_INITIATOR</constant> was specified,
> +            &EINVAL; is returned in that case.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_MODE_MONITOR</constant></entry>
> +	    <entry>0xe0</entry>
> +	    <entry>Put the file descriptor into monitor mode. Can only be used in combination
> +	    with <constant>CEC_MODE_NO_INITIATOR</constant>, otherwise &EINVAL; will be
> +	    returned. In monitor mode all messages this CEC device transmits and all messages
> +	    it receives (both broadcast messages and directed messages for one its logical
> +	    addresses) will be reported. This is very useful for debugging.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_MODE_MONITOR_ALL</constant></entry>
> +	    <entry>0xf0</entry>
> +	    <entry>Put the file descriptor into 'monitor all' mode. Can only be used in combination
> +            with <constant>CEC_MODE_NO_INITIATOR</constant>, otherwise &EINVAL; will be
> +            returned. In 'monitor all' mode all messages this CEC device transmits and all messages
> +            it receives, including directed messages for other CEC devices will be reported. This
> +	    is very useful for debugging, but not all devices support this. This mode requires that
> +	    the <constant>CEC_CAP_MONITOR_ALL</constant> capability is set, and depending on the
> +	    hardware, you may have to be root to select this mode.</entry>

Please mention the error codes when it fails.

"and depending on the hardware, you may have to be root to select this mode."

No. We should define if CAP_SYS_ADMIN (or maybe CAP_NET_ADMIN, with
is required to set promiscuous mode for network) will be required or
not and enforce it for all hardware.

IMHO, putting the device into monitor mode should require it.

> +	  </row>
> +	</tbody>
> +      </tgroup>
> +    </table>
> +
> +    <para>Core message processing details:</para>
> +
> +    <table pgwide="1" frame="none" id="cec-core-processing">
> +      <title>Core Message Processing</title>
> +      <tgroup cols="2">
> +	&cs-def;
> +	<tbody valign="top">
> +	  <row>
> +	    <entry><constant>CEC_MSG_GET_CEC_VERSION</constant></entry>
> +	    <entry>When in passthrough mode this message has to be handled by userspace,
> +	    otherwise the core will return the CEC version that was set with &CEC-ADAP-S-LOG-ADDRS;.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_MSG_GIVE_DEVICE_VENDOR_ID</constant></entry>
> +	    <entry>When in passthrough mode this message has to be handled by userspace,
> +	    otherwise the core will return the vendor ID that was set with &CEC-ADAP-S-LOG-ADDRS;.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_MSG_ABORT</constant></entry>
> +	    <entry>When in passthrough mode this message has to be handled by userspace,
> +	    otherwise the core will return a feature refused message as per the specification.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_MSG_GIVE_PHYSICAL_ADDR</constant></entry>
> +	    <entry>When in passthrough mode this message has to be handled by userspace,
> +	    otherwise the core will report the current physical address.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_MSG_GIVE_OSD_NAME</constant></entry>
> +	    <entry>When in passthrough mode this message has to be handled by userspace,
> +	    otherwise the core will report the current OSD name as was set with
> +	    &CEC-ADAP-S-LOG-ADDRS;.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_MSG_GIVE_FEATURES</constant></entry>
> +	    <entry>When in passthrough mode this message has to be handled by userspace,
> +	    otherwise the core will report the current features as was set with
> +	    &CEC-ADAP-S-LOG-ADDRS; or the message is ignore if the CEC version was
> +	    older than 2.0.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_MSG_USER_CONTROL_PRESSED</constant></entry>
> +	    <entry>If <constant>CEC_CAP_RC</constant> is set, then generate a remote control
> +	    key press. This message is always passed on to userspace.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_MSG_USER_CONTROL_RELEASED</constant></entry>
> +	    <entry>If <constant>CEC_CAP_RC</constant> is set, then generate a remote control
> +	    key release. This message is always passed on to userspace.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_MSG_REPORT_PHYSICAL_ADDR</constant></entry>
> +	    <entry>The CEC framework will make note of the reported physical address
> +	    and then just pass the message on to userspace.</entry>
> +	  </row>
> +	</tbody>
> +      </tgroup>
> +    </table>
> +  </refsect1>
> +
> +  <refsect1>
> +    &return-value;
> +  </refsect1>
> +</refentry>
> diff --git a/Documentation/DocBook/media/v4l/cec-ioc-receive.xml b/Documentation/DocBook/media/v4l/cec-ioc-receive.xml
> new file mode 100644
> index 0000000..66bf82f
> --- /dev/null
> +++ b/Documentation/DocBook/media/v4l/cec-ioc-receive.xml
> @@ -0,0 +1,260 @@
> +<refentry id="cec-ioc-receive">
> +  <refmeta>
> +    <refentrytitle>ioctl CEC_RECEIVE, CEC_TRANSMIT</refentrytitle>
> +    &manvol;
> +  </refmeta>
> +
> +  <refnamediv>
> +    <refname>CEC_RECEIVE</refname>
> +    <refname>CEC_TRANSMIT</refname>
> +    <refpurpose>Receive or transmit a CEC message</refpurpose>
> +  </refnamediv>
> +
> +  <refsynopsisdiv>
> +    <funcsynopsis>
> +      <funcprototype>
> +	<funcdef>int <function>ioctl</function></funcdef>
> +	<paramdef>int <parameter>fd</parameter></paramdef>
> +	<paramdef>int <parameter>request</parameter></paramdef>
> +	<paramdef>struct cec_msg *<parameter>argp</parameter></paramdef>
> +      </funcprototype>
> +    </funcsynopsis>
> +  </refsynopsisdiv>
> +
> +  <refsect1>
> +    <title>Arguments</title>
> +
> +    <variablelist>
> +      <varlistentry>
> +	<term><parameter>fd</parameter></term>
> +	<listitem>
> +	  <para>File descriptor returned by
> +	  <link linkend='cec-func-open'><function>open()</function></link>.</para>
> +	</listitem>
> +      </varlistentry>
> +      <varlistentry>
> +	<term><parameter>request</parameter></term>
> +	<listitem>
> +	  <para>CEC_RECEIVE, CEC_TRANSMIT</para>
> +	</listitem>
> +      </varlistentry>
> +      <varlistentry>
> +	<term><parameter>argp</parameter></term>
> +	<listitem>
> +	  <para></para>
> +	</listitem>
> +      </varlistentry>
> +    </variablelist>
> +  </refsect1>
> +
> +  <refsect1>
> +    <title>Description</title>
> +
> +    <para>To receive a CEC message the application has to fill in the
> +    <structname>cec_msg</structname> structure and pass it to the
> +    <constant>CEC_RECEIVE</constant> ioctl. <constant>CEC_RECEIVE</constant> is
> +    only available if <constant>CEC_CAP_RECEIVE</constant> is set. If the
> +    file descriptor is in non-blocking mode and there are no received
> +    messages pending, then it will return -1 and set errno to the &EAGAIN;.
> +    If the file descriptor is in blocking mode and <structfield>timeout</structfield>
> +    is non-zero and no message arrived within <structfield>timeout</structfield>
> +    milliseconds, then it will return -1 and set errno to the &ETIMEDOUT;.</para>
> +
> +    <para>To send a CEC message the application has to fill in the
> +    <structname>cec_msg</structname> structure and pass it to the
> +    <constant>CEC_TRANSMIT</constant> ioctl. <constant>CEC_TRANSMIT</constant> is
> +    only available if <constant>CEC_CAP_TRANSMIT</constant> is set.
> +    If there is no more room in the transmit queue, then it will return
> +    -1 and set errno to the &EBUSY;.</para>
> +
> +    <table pgwide="1" frame="none" id="cec-msg">
> +      <title>struct <structname>cec_msg</structname></title>
> +      <tgroup cols="3">
> +	&cs-str;
> +	<tbody valign="top">
> +	  <row>
> +	    <entry>__u64</entry>
> +	    <entry><structfield>ts</structfield></entry>
> +	    <entry>Timestamp of when the message was transmitted in ns in the case
> +	    of <constant>CEC_TRANSMIT</constant> with <structfield>reply</structfield>
> +	    set to 0, or the timestamp of the received message in all other cases.</entry>
> +	  </row>
> +	  <row>
> +	    <entry>__u32</entry>
> +	    <entry><structfield>len</structfield></entry>
> +	    <entry>The length of the message. For <constant>CEC_TRANSMIT</constant> this
> +	    is filled in by the application. The driver will fill this in for
> +	    <constant>CEC_RECEIVE</constant> and for <constant>CEC_TRANSMIT</constant>
> +	    it will be filled in with the length of the reply message if
> +	    <structfield>reply</structfield> was set.</entry>
> +	  </row>
> +	  <row>
> +	    <entry>__u32</entry>
> +	    <entry><structfield>timeout</structfield></entry>
> +	    <entry>The timeout in milliseconds. This is the time we wait for a message to
> +	    be received. If it is set to 0, then we wait indefinitely.
> +	    It is ignored by <constant>CEC_TRANSMIT</constant>.</entry>
> +	  </row>
> +	  <row>
> +	    <entry>__u32</entry>
> +	    <entry><structfield>sequence</structfield></entry>
> +	    <entry>The sequence number is automatically assigned by the CEC
> +	    framework for all transmitted messages. It can be later used by the
> +	    framework to generate an event if a reply for a message was
> +	    requested and the message was transmitted in a non-blocking mode.
> +	    </entry>
> +	  </row>
> +	  <row>
> +	    <entry>__u32</entry>
> +	    <entry><structfield>flags</structfield></entry>
> +	    <entry>Flags. No flags are defined yet, so set this to 0.</entry>
> +	  </row>
> +	  <row>
> +	    <entry>__u8</entry>
> +	    <entry><structfield>rx_status</structfield></entry>
> +	    <entry>The status bits of the received message. See <xref linkend="cec-rx-status" />
> +	    for the possible status values. It is 0 if this message was transmitted, not
> +	    received, unless this is the reply to a transmitted message. In that case both
> +	    <structfield>rx_status</structfield> and <structfield>tx_status</structfield>
> +	    are set.</entry>
> +	  </row>
> +	  <row>
> +	    <entry>__u8</entry>
> +	    <entry><structfield>tx_status</structfield></entry>
> +	    <entry>The status bits of the transmitted message. See <xref linkend="cec-tx-status" />
> +	    for the possible status values. It is 0 if this messages was received, not
> +	    transmitted.</entry>
> +	  </row>
> +	  <row>
> +	    <entry>__u8</entry>
> +	    <entry><structfield>msg</structfield>[16]</entry>
> +	    <entry>The message payload. For <constant>CEC_TRANSMIT</constant> this
> +	    is filled in by the application. The driver will fill this in for
> +	    <constant>CEC_RECEIVE</constant> and for <constant>CEC_TRANSMIT</constant>
> +	    it will be filled in with the payload of the reply message if
> +	    <structfield>reply</structfield> was set.</entry>
> +	  </row>
> +	  <row>
> +	    <entry>__u8</entry>
> +	    <entry><structfield>reply</structfield></entry>
> +	    <entry>Wait until this message is replied. If <structfield>reply</structfield>
> +	    is 0, then don't wait for a reply but return after transmitting the
> +	    message. If there was an error as indicated by a non-zero <structfield>status</structfield>
> +	    field, then <structfield>reply</structfield> is set to 0 by the driver.
> +	    Ignored by <constant>CEC_RECEIVE</constant>.</entry>
> +	  </row>
> +	  <row>
> +	    <entry>__u8</entry>
> +	    <entry><structfield>tx_arb_lost_cnt</structfield></entry>
> +	    <entry>A counter of the number of transmit attempts that resulted in the
> +	    Arbitration Lost error. This is only set if the hardware supports this, otherwise
> +	    it is always 0. This counter is only valid if the <constant>CEC_TX_STATUS_ARB_LOST</constant>
> +	    status bit is set.</entry>
> +	  </row>
> +	  <row>
> +	    <entry>__u8</entry>
> +	    <entry><structfield>tx_nack_cnt</structfield></entry>
> +	    <entry>A counter of the number of transmit attempts that resulted in the
> +	    Not Acknowledged error. This is only set if the hardware supports this, otherwise
> +	    it is always 0. This counter is only valid if the <constant>CEC_TX_STATUS_NACK</constant>
> +            status bit is set.</entry>
> +	  </row>
> +	  <row>
> +	    <entry>__u8</entry>
> +	    <entry><structfield>tx_low_drive_cnt</structfield></entry>
> +	    <entry>A counter of the number of transmit attempts that resulted in the
> +	    Arbitration Lost error. This is only set if the hardware supports this, otherwise
> +	    it is always 0. This counter is only valid if the <constant>CEC_TX_STATUS_LOW_DRIVE</constant>
> +            status bit is set.</entry>
> +	  </row>
> +	  <row>
> +	    <entry>__u8</entry>
> +	    <entry><structfield>tx_error_cnt</structfield></entry>
> +	    <entry>A counter of the number of transmit errors other than Arbitration Lost
> +	    or Not Acknowledged. This is only set if the hardware supports this, otherwise
> +	    it is always 0. This counter is only valid if the <constant>CEC_TX_STATUS_ERROR</constant>
> +	    status bit is set.</entry>
> +	  </row>
> +	</tbody>
> +      </tgroup>
> +    </table>
> +
> +    <table pgwide="1" frame="none" id="cec-tx-status">
> +      <title>CEC Transmit Status</title>
> +      <tgroup cols="3">
> +	&cs-def;
> +	<tbody valign="top">
> +	  <row>
> +	    <entry><constant>CEC_TX_STATUS_OK</constant></entry>
> +	    <entry>0x01</entry>
> +	    <entry>The message was transmitted successfully. This is mutually exclusive with
> +	    <constant>CEC_TX_STATUS_MAX_RETRIES</constant>. Other bits can still be set if
> +	    earlier attempts met with failure before the transmit was eventually successful.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_TX_STATUS_ARB_LOST</constant></entry>
> +	    <entry>0x02</entry>
> +	    <entry>CEC line arbitration was lost.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_TX_STATUS_NACK</constant></entry>
> +	    <entry>0x04</entry>
> +	    <entry>Message was not acknowledged.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_TX_STATUS_LOW_DRIVE</constant></entry>
> +	    <entry>0x08</entry>
> +	    <entry>Low drive was detected on the CEC bus. This indicates that a follower
> +	    detected an error on the bus and requests a retransmission.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_TX_STATUS_ERROR</constant></entry>
> +	    <entry>0x10</entry>
> +	    <entry>Some error occurred. This is used for any errors that do not
> +	    fit the previous two, either because the hardware could not tell
> +	    which error occurred, or because the hardware tested for other conditions
> +	    besides those two.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_TX_STATUS_MAX_RETRIES</constant></entry>
> +	    <entry>0x20</entry>
> +	    <entry>The transmit failed after one or more retries. This status bit is mutually
> +	    exclusive with <constant>CEC_TX_STATUS_OK</constant>. Other bits can still be set
> +	    to explain which failures were seen.</entry>
> +	  </row>
> +	</tbody>
> +      </tgroup>
> +    </table>
> +
> +    <table pgwide="1" frame="none" id="cec-rx-status">
> +      <title>CEC Receive Status</title>
> +      <tgroup cols="3">
> +	&cs-def;
> +	<tbody valign="top">
> +	  <row>
> +	    <entry><constant>CEC_RX_STATUS_OK</constant></entry>
> +	    <entry>0x01</entry>
> +	    <entry>The message was received successfully.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_RX_STATUS_TIMEOUT</constant></entry>
> +	    <entry>0x02</entry>
> +	    <entry>The reply to an earlier transmitted message timed out.</entry>
> +	  </row>
> +	  <row>
> +	    <entry><constant>CEC_RX_STATUS_FEATURE_ABORT</constant></entry>
> +	    <entry>0x04</entry>
> +	    <entry>The message was received successfully but the reply was
> +	    <constant>CEC_MSG_FEATURE_ABORT</constant>. This status is only
> +	    set if this message was the reply to an earlier transmitted
> +	    message.</entry>
> +	  </row>
> +	</tbody>
> +      </tgroup>
> +    </table>
> +  </refsect1>
> +
> +  <refsect1>
> +    &return-value;
> +  </refsect1>
> +</refentry>
> diff --git a/Documentation/DocBook/media_api.tmpl b/Documentation/DocBook/media_api.tmpl
> index 7b77e0f..a2765d8 100644
> --- a/Documentation/DocBook/media_api.tmpl
> +++ b/Documentation/DocBook/media_api.tmpl
> @@ -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;



Thanks,
Mauro

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

* Re: [PATCHv16 09/13] cec: adv7604: add cec support.
  2016-04-29 13:52 ` [PATCHv16 09/13] cec: adv7604: add cec support Hans Verkuil
@ 2016-06-16 21:17   ` Mauro Carvalho Chehab
  2016-06-17  8:03     ` Hans Verkuil
  0 siblings, 1 reply; 35+ messages in thread
From: Mauro Carvalho Chehab @ 2016-06-16 21:17 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: linux-media, dri-devel, linux-samsung-soc, linux-input, lars,
	linux, Hans Verkuil

Em Fri, 29 Apr 2016 15:52:24 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> Add CEC support to the adv7604 driver.
> 
> Signed-off-by: Hans Verkuil <hansverk@cisco.com>
> [k.debski@samsung.com: Merged changes from CEC Updates commit by Hans Verkuil]
> [k.debski@samsung.com: add missing methods cec/io_write_and_or]
> [k.debski@samsung.com: change adv7604 to adv76xx in added functions]
> [hansverk@cisco.com: use _clr_set instead of _and_or]
> ---
>  drivers/media/i2c/Kconfig   |   9 ++
>  drivers/media/i2c/adv7604.c | 332 +++++++++++++++++++++++++++++++++++++++-----
>  2 files changed, 305 insertions(+), 36 deletions(-)
> 
> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> index 993dc50..cba1fc7 100644
> --- a/drivers/media/i2c/Kconfig
> +++ b/drivers/media/i2c/Kconfig
> @@ -209,6 +209,7 @@ config VIDEO_ADV7604
>  	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
>  	depends on GPIOLIB || COMPILE_TEST
>  	select HDMI
> +	select MEDIA_CEC_EDID

Same note for all CEC drivers: don't do that! CEC is in staging. We
should not have any code outside staging depending or selecting CEC.

Instead, put all CEC dependent code inside #ifdefs.


>  	---help---
>  	  Support for the Analog Devices ADV7604 video decoder.
>  
> @@ -218,6 +219,14 @@ config VIDEO_ADV7604
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called adv7604.
>  
> +config VIDEO_ADV7604_CEC
> +	bool "Enable Analog Devices ADV7604 CEC support"
> +	depends on VIDEO_ADV7604 && MEDIA_CEC
> +	default y

Remove "default y". CEC is experimental. Users should explicitly
agree adding experimental code that depends on staging stuff.

> +	---help---
> +	  When selected the adv7604 will support the optional
> +	  HDMI CEC feature.

Please add a notice that CEC is an experimental feature under
staging and may be changed or even removed in the future.

We don't want any application to use it yet, as we're still
experimenting with it.

For the rest of the code below, and for patches 10-13: all the above
applies to them.

> +
>  config VIDEO_ADV7842
>  	tristate "Analog Devices ADV7842 decoder"
>  	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
> diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
> index beb2841..f462585 100644
> --- a/drivers/media/i2c/adv7604.c
> +++ b/drivers/media/i2c/adv7604.c
> @@ -40,6 +40,7 @@
>  #include <linux/regmap.h>
>  
>  #include <media/i2c/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,12 @@ struct adv76xx_state {
>  	u16 spa_port_a[2];
>  	struct v4l2_fract aspect_ratio;
>  	u32 rgb_quantization_range;
> +
> +	struct cec_adapter *cec_adap;
> +	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;
> @@ -381,7 +390,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);
>  }
> @@ -414,6 +424,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);
> @@ -872,9 +888,9 @@ 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);
>  
> -	return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl,
> -				info->read_cable_det(sd));
> +	return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, cable_det);
>  }
>  
>  static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd,
> @@ -1900,6 +1916,210 @@ static int adv76xx_set_format(struct v4l2_subdev *sd,
>  	return 0;
>  }
>  
> +#if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC)
> +static void adv76xx_cec_tx_raw_status(struct v4l2_subdev *sd, u8 tx_raw_status)
> +{
> +	struct adv76xx_state *state = to_state(sd);
> +
> +	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__);
> +		cec_transmit_done(state->cec_adap, CEC_TX_STATUS_ARB_LOST,
> +				  1, 0, 0, 0);
> +	}
> +	if (tx_raw_status & 0x04) {
> +		u8 status;
> +		u8 nack_cnt;
> +		u8 low_drive_cnt;
> +
> +		v4l2_dbg(1, debug, sd, "%s: tx raw: retry failed\n", __func__);
> +		/*
> +		 * We set this status bit since this hardware performs
> +		 * retransmissions.
> +		 */
> +		status = CEC_TX_STATUS_MAX_RETRIES;
> +		nack_cnt = cec_read(sd, 0x14) & 0xf;
> +		if (nack_cnt)
> +			status |= CEC_TX_STATUS_NACK;
> +		low_drive_cnt = cec_read(sd, 0x14) >> 4;
> +		if (low_drive_cnt)
> +			status |= CEC_TX_STATUS_LOW_DRIVE;
> +		cec_transmit_done(state->cec_adap, status,
> +				  0, nack_cnt, low_drive_cnt, 0);
> +		return;
> +	}
> +	if (tx_raw_status & 0x01) {
> +		v4l2_dbg(1, debug, sd, "%s: tx raw: ready ok\n", __func__);
> +		cec_transmit_done(state->cec_adap, CEC_TX_STATUS_OK, 0, 0, 0, 0);
> +		return;
> +	}
> +}
> +
> +static void adv76xx_cec_isr(struct v4l2_subdev *sd, bool *handled)
> +{
> +	struct adv76xx_state *state = to_state(sd);
> +	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 */
> +			cec_received_msg(state->cec_adap, &msg);
> +		}
> +	}
> +
> +	/* note: the bit order is swapped between 0x4d and 0x4e */
> +	cec_irq = ((cec_irq & 0x08) >> 3) | ((cec_irq & 0x04) >> 1) |
> +		  ((cec_irq & 0x02) << 1) | ((cec_irq & 0x01) << 3);
> +	io_write(sd, 0x4e, cec_irq);
> +
> +	if (handled)
> +		*handled = true;
> +}
> +
> +static int adv76xx_cec_adap_enable(struct cec_adapter *adap, bool enable)
> +{
> +	struct adv76xx_state *state = adap->priv;
> +	struct v4l2_subdev *sd = &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_adap_log_addr(struct cec_adapter *adap, u8 addr)
> +{
> +	struct adv76xx_state *state = adap->priv;
> +	struct v4l2_subdev *sd = &state->sd;
> +	unsigned i, free_idx = ADV76XX_MAX_ADDRS;
> +
> +	if (!state->cec_enabled_adap)
> +		return addr == CEC_LOG_ADDR_INVALID ? 0 : -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_adap_transmit(struct cec_adapter *adap, u8 attempts,
> +				     u32 signal_free_time, struct cec_msg *msg)
> +{
> +	struct adv76xx_state *state = adap->priv;
> +	struct v4l2_subdev *sd = &state->sd;
> +	u8 len = msg->len;
> +	unsigned i;
> +
> +	/*
> +	 * The number of retries is the number of attempts - 1, but retry
> +	 * at least once. It's not clear if a value of 0 is allowed, so
> +	 * let's do at least one retry.
> +	 */
> +	cec_write_clr_set(sd, 0x12, 0x70, max(1, attempts - 1) << 4);
> +
> +	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 const struct cec_adap_ops adv76xx_cec_adap_ops = {
> +	.adap_enable = adv76xx_cec_adap_enable,
> +	.adap_log_addr = adv76xx_cec_adap_log_addr,
> +	.adap_transmit = adv76xx_cec_adap_transmit,
> +};
> +#endif
> +
>  static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
>  {
>  	struct adv76xx_state *state = to_state(sd);
> @@ -1945,6 +2165,11 @@ static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
>  			*handled = true;
>  	}
>  
> +#if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC)
> +	/* cec */
> +	adv76xx_cec_isr(sd, handled);
> +#endif
> +
>  	/* tx 5v detect */
>  	tx_5v = irq_reg_0x70 & info->cable_det_mask;
>  	if (tx_5v) {
> @@ -1994,39 +2219,12 @@ static int adv76xx_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
>  	return 0;
>  }
>  
> -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;
> -}

Very likely, you can't drop the above, as it won't compile if !CEC.

While CEC is under staging, no code here should use a function
defined there. Even afterwards, CEC should be an optional feature.
So, IMHO, it is better to move the EDID parsing code out of the
CEC core, putting them elsewhere.

> -
>  static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
>  {
>  	struct adv76xx_state *state = to_state(sd);
>  	const struct adv76xx_chip_info *info = state->info;
> -	int spa_loc;
> +	unsigned spa_loc;
> +	u16 pa;
>  	int err;
>  	int i;
>  
> @@ -2057,6 +2255,10 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
>  		edid->blocks = 2;
>  		return -E2BIG;
>  	}
> +	pa = cec_get_edid_phys_addr(edid->edid, edid->blocks * 128, &spa_loc);
> +	err = cec_phys_addr_validate(pa, &pa, NULL);
> +	if (err)
> +		return err;
>  
>  	v4l2_dbg(2, debug, sd, "%s: write EDID pad %d, edid.present = 0x%x\n",
>  			__func__, edid->pad, state->edid.present);
> @@ -2066,9 +2268,12 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
>  	adv76xx_set_hpd(state, 0);
>  	rep_write_clr_set(sd, info->edid_enable_reg, 0x0f, 0x00);
>  
> -	spa_loc = get_edid_spa_location(edid->edid);
> -	if (spa_loc < 0)
> -		spa_loc = 0xc0; /* Default value [REF_02, p. 116] */
> +	/*
> +	 * Return an error if no location of the source physical address
> +	 * was found.
> +	 */
> +	if (spa_loc == 0)
> +		return -EINVAL;
>  
>  	switch (edid->pad) {
>  	case ADV76XX_PAD_HDMI_PORT_A:
> @@ -2128,6 +2333,7 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
>  		v4l2_err(sd, "error enabling edid (0x%x)\n", state->edid.present);
>  		return -EIO;
>  	}
> +	cec_s_phys_addr(state->cec_adap, pa, false);
>  
>  	/* enable hotplug after 100 ms */
>  	queue_delayed_work(state->work_queues,
> @@ -2252,8 +2458,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);
> @@ -2363,6 +2580,24 @@ static int adv76xx_subscribe_event(struct v4l2_subdev *sd,
>  	}
>  }
>  
> +static int adv76xx_registered(struct v4l2_subdev *sd)
> +{
> +	struct adv76xx_state *state = to_state(sd);
> +	int err;
> +
> +	err = cec_register_adapter(state->cec_adap);
> +	if (err)
> +		cec_delete_adapter(state->cec_adap);
> +	return err;
> +}
> +
> +static void adv76xx_unregistered(struct v4l2_subdev *sd)
> +{
> +	struct adv76xx_state *state = to_state(sd);
> +
> +	cec_unregister_adapter(state->cec_adap);
> +}
> +
>  /* ----------------------------------------------------------------------- */
>  
>  static const struct v4l2_ctrl_ops adv76xx_ctrl_ops = {
> @@ -2406,6 +2641,11 @@ static const struct v4l2_subdev_ops adv76xx_ops = {
>  	.pad = &adv76xx_pad_ops,
>  };
>  
> +static const struct v4l2_subdev_internal_ops adv76xx_int_ops = {
> +	.registered = adv76xx_registered,
> +	.unregistered = adv76xx_unregistered,
> +};
> +
>  /* -------------------------- custom ctrls ---------------------------------- */
>  
>  static const struct v4l2_ctrl_config adv7604_ctrl_analog_sampling_phase = {
> @@ -3069,6 +3309,7 @@ static int adv76xx_probe(struct i2c_client *client,
>  		id->name, i2c_adapter_id(client->adapter),
>  		client->addr);
>  	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
> +	sd->internal_ops = &adv76xx_int_ops;
>  
>  	/* Configure IO Regmap region */
>  	err = configure_regmap(state, ADV76XX_PAGE_IO);
> @@ -3212,6 +3453,18 @@ static int adv76xx_probe(struct i2c_client *client,
>  	err = adv76xx_core_init(sd);
>  	if (err)
>  		goto err_entity;
> +
> +#if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC)
> +	state->cec_adap = cec_allocate_adapter(&adv76xx_cec_adap_ops,
> +		state, dev_name(&client->dev),
> +		CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS |
> +		CEC_CAP_PASSTHROUGH | CEC_CAP_RC, ADV76XX_MAX_ADDRS,
> +		&client->dev);
> +	err = PTR_ERR_OR_ZERO(state->cec_adap);
> +	if (err)
> +		goto err_entity;
> +#endif
> +
>  	v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name,
>  			client->addr << 1, client->adapter->name);
>  
> @@ -3240,6 +3493,13 @@ 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);
> +
>  	cancel_delayed_work(&state->delayed_work_enable_hotplug);
>  	destroy_workqueue(state->work_queues);
>  	v4l2_async_unregister_subdev(sd);



Thanks,
Mauro

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

* Re: [PATCHv16 10/13] cec: adv7842: add cec support
  2016-04-29 13:52 ` [PATCHv16 10/13] cec: adv7842: " Hans Verkuil
@ 2016-06-16 21:22   ` Mauro Carvalho Chehab
  2016-06-17  8:06       ` Hans Verkuil
  0 siblings, 1 reply; 35+ messages in thread
From: Mauro Carvalho Chehab @ 2016-06-16 21:22 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: linux-media, dri-devel, linux-samsung-soc, linux-input, lars,
	linux, Hans Verkuil

Em Fri, 29 Apr 2016 15:52:25 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> Add CEC support to the adv7842 driver.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>

Won't review patches 10-13, as the same reviews I made for patch 9
very likely applies.

As this series is causing non-staging drivers to be dependent of a
staging driver, I'll wait for the next version that should be
solving this issue.

For the new 9-13 patches, please be sure that checkpatch will be
happy. For the staging stuff, the checkpatch issues can be solved
later, as I'll re-check against checkpatch when it moves from staging
to mainstream.

Regards,
Mauro

Thanks,
Mauro

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

* Re: [PATCHv16 07/13] cec.txt: add CEC framework documentation
  2016-06-16 20:12   ` Mauro Carvalho Chehab
@ 2016-06-17  7:22     ` Hans Verkuil
  2016-06-17  8:55       ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 35+ messages in thread
From: Hans Verkuil @ 2016-06-17  7:22 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: linux-media, dri-devel, linux-samsung-soc, linux-input, lars,
	linux, Hans Verkuil, Kamil Debski, Hans Verkuil

On 06/16/2016 10:12 PM, Mauro Carvalho Chehab wrote:
> Em Fri, 29 Apr 2016 15:52:22 +0200
> Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> 
>> From: Hans Verkuil <hansverk@cisco.com>
>>
>> Document the new HDMI CEC framework.
> 
> As we'll be moving documentation to Sphinx/Rst, it would be good if
> you could make it work fine with sphinx, as this will likely be needed
> for Kernel 4.9. Right now it most works, although several warnings are
> produced: 

Would it be a problem if this conversion is done later in a separate patch?

I'm all for it, but I don't have the time at the moment to do this.

Regards,

	Hans

> 
> /devel/v4l/patchwork/tmp/cec.txt:40: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:40: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:40: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:40: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:40: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:42: WARNING: Definition list ends without a blank line; unexpected unindent.
> /devel/v4l/patchwork/tmp/cec.txt:42: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:67: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:71: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:92: ERROR: Unexpected indentation.
> /devel/v4l/patchwork/tmp/cec.txt:87: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:87: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:87: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:87: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:87: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:87: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:87: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:87: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:87: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:92: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:93: WARNING: Block quote ends without a blank line; unexpected unindent.
> /devel/v4l/patchwork/tmp/cec.txt:93: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:93: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:95: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:97: WARNING: Definition list ends without a blank line; unexpected unindent.
> /devel/v4l/patchwork/tmp/cec.txt:105: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:105: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:118: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:118: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:130: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:130: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:144: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:144: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:144: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:161: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:161: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:161: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:173: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:194: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:203: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:203: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:214: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:217: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:217: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:217: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:217: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:219: WARNING: Definition list ends without a blank line; unexpected unindent.
> /devel/v4l/patchwork/tmp/cec.txt:224: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:224: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:224: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:238: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:238: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:243: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:258: WARNING: Inline emphasis start-string without end-string.
> /devel/v4l/patchwork/tmp/cec.txt:258: WARNING: Inline emphasis start-string without end-string.
> 
> 
>>
>> Signed-off-by: Hans Verkuil <hansverk@cisco.com>
>> [k.debski@samsung.com: add DocBook documentation by Hans Verkuil, with
>> Signed-off-by: Kamil Debski <kamil@wypas.org>
>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
>> ---
>>  Documentation/cec.txt | 267 ++++++++++++++++++++++++++++++++++++++++++++++++++
>>  1 file changed, 267 insertions(+)
>>  create mode 100644 Documentation/cec.txt
>>
>> diff --git a/Documentation/cec.txt b/Documentation/cec.txt
>> new file mode 100644
>> index 0000000..75155fe
>> --- /dev/null
>> +++ b/Documentation/cec.txt
>> @@ -0,0 +1,267 @@
>> +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_allocate_adapter() and deleted by calling cec_delete_adapter():
>> +
>> +struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
>> +	       void *priv, const char *name, u32 caps, u8 available_las,
>> +	       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. Note: this name will be copied.
>> +
>> +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.
>> +
>> +available_las: the number of simultaneous logical addresses that this
>> +	adapter can handle. Must be 1 <= available_las <= CEC_MAX_LOG_ADDRS.
>> +
>> +parent: the parent device.
>> +
>> +
>> +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);
>> +
>> +Note: if cec_register_adapter() fails, then call cec_delete_adapter() to
>> +clean up. But if cec_register_adapter() succeeded, then only call
>> +cec_unregister_adapter() to clean up, never cec_delete_adapter(). The
>> +unregister function will delete the adapter automatically once the last user
>> +of that /dev/cecX device has closed its file handle.
>> +
>> +
>> +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_monitor_all_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, u8 attempts,
>> +			     u32 signal_free_time, struct cec_msg *msg);
>> +	void (*adap_log_status)(struct cec_adapter *adap);
>> +
>> +	/* 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_allocate_adapter() is disabled.
>> +
>> +Note that adap_enable must return 0 if enable is false.
>> +
>> +
>> +To enable/disable the 'monitor all' mode:
>> +
>> +	int (*adap_monitor_all_enable)(struct cec_adapter *adap, bool enable);
>> +
>> +If enabled, then the adapter should be put in a mode to also monitor messages
>> +that not for us. Not all hardware supports this and this function is only
>> +called if the CEC_CAP_MONITOR_ALL capability is set. This callback is optional
>> +(some hardware may always be in 'monitor all' mode).
>> +
>> +Note that adap_monitor_all_enable must return 0 if enable is false.
>> +
>> +
>> +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 receive directed messages to that address.
>> +
>> +Note that adap_log_addr must return 0 if logical_addr is CEC_LOG_ADDR_INVALID.
>> +
>> +
>> +To transmit a new message:
>> +
>> +	int (*adap_transmit)(struct cec_adapter *adap, u8 attempts,
>> +			     u32 signal_free_time, struct cec_msg *msg);
>> +
>> +This transmits a new message. The attempts argument is the suggested number of
>> +attempts for the transmit.
>> +
>> +The signal_free_time is the number of data bit periods that the adapter should
>> +wait when the line is free before attempting to send a message. This value
>> +depends on whether this transmit is a retry, a message from a new initiator or
>> +a new message for the same initiator. Most hardware will handle this
>> +automatically, but in some cases this information is needed.
>> +
>> +The CEC_FREE_TIME_TO_USEC macro can be used to convert signal_free_time to
>> +microseconds (one data bit period is 2.4 ms).
>> +
>> +
>> +To log the current CEC hardware status:
>> +
>> +	void (*adap_status)(struct cec_adapter *adap, struct seq_file *file);
>> +
>> +This optional callback can be used to show the status of the CEC hardware.
>> +The status is available through debugfs: cat /sys/kernel/debug/cec/cecX/status
>> +
>> +
>> +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, u8 status, u8 arb_lost_cnt,
>> +		       u8 nack_cnt, u8 low_drive_cnt, u8 error_cnt);
>> +
>> +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
>> +took control of the CEC line and you lost the arbitration.
>> +CEC_TX_STATUS_NACK: the message was nacked (for a directed message) or
>> +acked (for a broadcast message). A retransmission is needed.
>> +CEC_TX_STATUS_LOW_DRIVE: low drive was detected on the CEC bus. This
>> +indicates that a follower detected an error on the bus and requested a
>> +retransmission.
>> +CEC_TX_STATUS_ERROR: some unspecified error occurred: this can be one of
>> +the previous two if the hardware cannot differentiate or something else
>> +entirely.
>> +CEC_TX_STATUS_MAX_RETRIES: could not transmit the message after
>> +trying multiple times. Should only be set by the driver if it has hardware
>> +support for retrying messages. If set, then the framework assumes that it
>> +doesn't have to make another attempt to transmit the message since the
>> +hardware did that already.
>> +
>> +The *_cnt arguments are the number of error conditions that were seen.
>> +This may be 0 if no information is available. Drivers that do not support
>> +hardware retry can just set the counter corresponding to the transmit error
>> +to 1, if the hardware does support retry then either set these counters to
>> +0 if the hardware provides no feedback of which errors occurred and how many
>> +times, or fill in the correct values as reported by the hardware.
>> +
>> +When a CEC message was received:
>> +
>> +void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg);
>> +
>> +Speaks for itself.
>> +
>> +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);
>> +};
>> +
>> +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.
>> +
>> +
>> +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.
>> +
>> +void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block);
>> +
>> +Change the physical address. This function will set adap->phys_addr and
>> +send an event if it has changed. If cec_s_log_addrs() has been called and
>> +the physical address has become valid, then the CEC framework will start
>> +claiming the logical addresses. If block is true, then this function won't
>> +return until this process has finished.
>> +
>> +When the physical address is set to a valid value the CEC adapter will
>> +be enabled (see the adap_enable op). When it is set to CEC_PHYS_ADDR_INVALID,
>> +then the CEC adapter will be disabled. If you change a valid physical address
>> +to another valid physical address, then this function will first set the
>> +address to CEC_PHYS_ADDR_INVALID before enabling the new physical address.
>> +
>> +int cec_s_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. To unconfigure all logical
>> +addresses call this function with log_addrs set to NULL or with
>> +log_addrs->num_log_addrs set to 0. The block argument is ignored when
>> +unconfiguring. This function will just return if the physical address is
>> +invalid. Once the physical address becomes valid, then the framework will
>> +attempt to claim these logical addresses.
> 
> 
> 
> Thanks,
> Mauro
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

* Re: [PATCHv16 08/13] DocBook/media: add CEC documentation
  2016-06-16 21:09   ` Mauro Carvalho Chehab
@ 2016-06-17  7:58     ` Hans Verkuil
  2016-06-17  9:50       ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 35+ messages in thread
From: Hans Verkuil @ 2016-06-17  7:58 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: linux-media, dri-devel, linux-samsung-soc, linux-input, lars,
	linux, Hans Verkuil, Kamil Debski, Hans Verkuil

On 06/16/2016 11:09 PM, Mauro Carvalho Chehab wrote:
> Em Fri, 29 Apr 2016 15:52:23 +0200
> Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> 
>> From: Hans Verkuil <hans.verkuil@cisco.com>
>>
>> Add DocBook documentation for the CEC API.
> 
> Please always send the documentation patch *before* the code,
> in order to make easier to do the code review.
> 
>>
>> Signed-off-by: Hans Verkuil <hansverk@cisco.com>
>> [k.debski@samsung.com: add documentation for passthrough mode]
>> [k.debski@samsung.com: minor fixes and change of reserved field sizes]
>> Signed-off-by: Kamil Debski <kamil@wypas.org>
>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
>> ---
>>  Documentation/DocBook/device-drivers.tmpl          |   4 +
>>  Documentation/DocBook/media/Makefile               |   2 +
>>  Documentation/DocBook/media/v4l/biblio.xml         |  10 +
>>  Documentation/DocBook/media/v4l/cec-api.xml        |  72 +++++
>>  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      | 140 +++++++++
>>  .../DocBook/media/v4l/cec-ioc-adap-g-log-addrs.xml | 324 +++++++++++++++++++++
>>  .../DocBook/media/v4l/cec-ioc-adap-g-phys-addr.xml |  82 ++++++
>>  .../DocBook/media/v4l/cec-ioc-dqevent.xml          | 190 ++++++++++++
>>  Documentation/DocBook/media/v4l/cec-ioc-g-mode.xml | 245 ++++++++++++++++
>>  .../DocBook/media/v4l/cec-ioc-receive.xml          | 260 +++++++++++++++++
>>  Documentation/DocBook/media_api.tmpl               |   6 +-
>>  15 files changed, 1649 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-dqevent.xml
>>  create mode 100644 Documentation/DocBook/media/v4l/cec-ioc-g-mode.xml
>>  create mode 100644 Documentation/DocBook/media/v4l/cec-ioc-receive.xml
> 
> Hmm... as CEC is at staging, I'm not very comfortable on having
> it documented there as-is, as the documentation is expected to
> be for non-staging stuff. Maybe the best would be to add, on every
> xml above, a note saying that this is a *proposed* documentation
> for a feature yet to be accepted.

No problem.

> 
>>
>> diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl
>> index 893b2ca..31258bf 100644
>> --- a/Documentation/DocBook/device-drivers.tmpl
>> +++ b/Documentation/DocBook/device-drivers.tmpl
>> @@ -270,6 +270,10 @@ 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
>> +!Iinclude/media/cec-edid.h
>> +     </sect1>
> 
> For the same reason, I would not add those yet.

Ack.

> 
>>  
>>    </chapter>
>>  
>> diff --git a/Documentation/DocBook/media/Makefile b/Documentation/DocBook/media/Makefile
>> index 2840ff4..fdc1386 100644
>> --- a/Documentation/DocBook/media/Makefile
>> +++ b/Documentation/DocBook/media/Makefile
>> @@ -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/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/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)
>>  
>> diff --git a/Documentation/DocBook/media/v4l/biblio.xml b/Documentation/DocBook/media/v4l/biblio.xml
>> index 9beb30f..87f1d24 100644
>> --- a/Documentation/DocBook/media/v4l/biblio.xml
>> +++ b/Documentation/DocBook/media/v4l/biblio.xml
>> @@ -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>
>> diff --git a/Documentation/DocBook/media/v4l/cec-api.xml b/Documentation/DocBook/media/v4l/cec-api.xml
>> new file mode 100644
>> index 0000000..caa04c0
>> --- /dev/null
>> +++ b/Documentation/DocBook/media/v4l/cec-api.xml
>> @@ -0,0 +1,72 @@
>> +<partinfo>
>> +  <authorgroup>
>> +    <author>
>> +      <firstname>Hans</firstname>
>> +      <surname>Verkuil</surname>
>> +      <affiliation><address><email>hans.verkuil@cisco.com</email></address></affiliation>
>> +      <contrib>Initial version.</contrib>
>> +    </author>
>> +  </authorgroup>
>> +  <copyright>
>> +    <year>2016</year>
>> +    <holder>Hans Verkuil</holder>
>> +  </copyright>
>> +
>> +  <revhistory>
>> +    <!-- Put document revisions here, newest first. -->
>> +    <revision>
>> +      <revnumber>1.0.0</revnumber>
>> +      <date>2016-03-17</date>
>> +      <authorinitials>hv</authorinitials>
>> +      <revremark>Initial revision</revremark>
>> +    </revision>
>> +  </revhistory>
>> +</partinfo>
>> +
>> +<title>CEC API</title>
>> +
>> +<chapter id="cec-api">
>> +  <title>CEC: Consumer Electronics Control</title>
>> +
>> +  <section id="cec-intro">
>> +    <title>Introduction</title>
>> +    <para>HDMI connectors provide a single pin for use by the Consumer Electronics
>> +    Control protocol. This protocol allows different devices connected by an HDMI cable
>> +    to communicate. The protocol for CEC version 1.4 is defined in supplements 1 (CEC)
>> +    and 2 (HEAC or HDMI Ethernet and Audio Return Channel) of the HDMI 1.4a
>> +    (<xref linkend="hdmi" />) specification and the extensions added to CEC version 2.0
>> +    are defined in chapter 11 of the HDMI 2.0 (<xref linkend="hdmi2" />) specification.
>> +    </para>
>> +
>> +    <para>The bitrate is very slow (effectively no more than 36 bytes per second) and
>> +    is based on the ancient AV.link protocol used in old SCART connectors. The protocol
>> +    closely resembles a crazy Rube Goldberg contraption and is an unholy mix of low and
>> +    high level messages. Some messages, especially those part of the HEAC protocol layered
>> +    on top of CEC, need to be handled by the kernel, others can be handled either by the
>> +    kernel or by userspace.</para>
>> +
>> +    <para>In addition, CEC can be implemented in HDMI receivers, transmitters and in USB
>> +    devices that have an HDMI input and an HDMI output and that control just the CEC pin.</para>
>> +
>> +    <para>Drivers that support CEC and that allow (or require) userspace to handle CEC
> 
> Either allow or require. If it works out of the box like a RC controller
> where no userspace is needed to have it working, allow is ok. Otherwise,
> if it only works after userspace enables it, it is require.

I don't know which it is. It is allowed for driver to handle everything in the
kernel. This will be useful in cases where the developer wants to prevent userspace
from influencing what how the CEC bus behaves. In that case no cec device is created
(although the RC device may appear, depending on how the driver sets it up).

That said, if no cec device appears, then this userspace API isn't available anyway,
so I don't have to mention this special case here at all. I don't expect such drivers
(that handle all the CEC traffic in kernelspace) to be mainlined anyway: I think
this will be limited to out-of-tree drivers on embedded systems. But I may be wrong,
it's hard to predict this.

I'll rewrite this paragraph.

> 
>> +    messages and/or configure the CEC adapter will create a CEC device node (/dev/cecX)
>> +    to give userspace access to the CEC adapter. The &CEC-ADAP-G-CAPS; ioctl will tell userspace
>> +    what it is allowed to do.</para>
>> +  </section>
>> +</chapter>
>> +
>> +<appendix id="cec-user-func">
>> +  <title>Function Reference</title>
>> +  <!-- Keep this alphabetically sorted. -->
>> +  &sub-cec-func-open;
>> +  &sub-cec-func-close;
>> +  &sub-cec-func-ioctl;
>> +  &sub-cec-func-poll;
>> +  <!-- All ioctls go here. -->
>> +  &sub-cec-ioc-adap-g-caps;
>> +  &sub-cec-ioc-adap-g-log-addrs;
>> +  &sub-cec-ioc-adap-g-phys-addr;
>> +  &sub-cec-ioc-dqevent;
>> +  &sub-cec-ioc-g-mode;
>> +  &sub-cec-ioc-receive;
>> +</appendix>
>> diff --git a/Documentation/DocBook/media/v4l/cec-func-close.xml b/Documentation/DocBook/media/v4l/cec-func-close.xml
>> new file mode 100644
>> index 0000000..c3978af
>> --- /dev/null
>> +++ b/Documentation/DocBook/media/v4l/cec-func-close.xml
>> @@ -0,0 +1,59 @@
>> +<refentry id="cec-func-close">
>> +  <refmeta>
>> +    <refentrytitle>cec close()</refentrytitle>
>> +    &manvol;
>> +  </refmeta>
>> +
>> +  <refnamediv>
>> +    <refname>cec-close</refname>
>> +    <refpurpose>Close a cec device</refpurpose>
>> +  </refnamediv>
>> +
>> +  <refsynopsisdiv>
>> +    <funcsynopsis>
>> +      <funcsynopsisinfo>#include &lt;unistd.h&gt;</funcsynopsisinfo>
>> +      <funcprototype>
>> +	<funcdef>int <function>close</function></funcdef>
>> +	<paramdef>int <parameter>fd</parameter></paramdef>
>> +      </funcprototype>
>> +    </funcsynopsis>
>> +  </refsynopsisdiv>
>> +
>> +  <refsect1>
>> +    <title>Arguments</title>
>> +
>> +    <variablelist>
>> +      <varlistentry>
>> +	<term><parameter>fd</parameter></term>
>> +	<listitem>
>> +	  <para>&fd;</para>
>> +	</listitem>
>> +      </varlistentry>
>> +    </variablelist>
>> +  </refsect1>
>> +
>> +  <refsect1>
>> +    <title>Description</title>
>> +
>> +    <para>Closes the cec device. Resources associated with the file descriptor
>> +    are freed. The device configuration remain unchanged.</para>
>> +  </refsect1>
>> +
>> +  <refsect1>
>> +    <title>Return Value</title>
>> +
>> +    <para><function>close</function> returns 0 on success. On error, -1 is
>> +    returned, and <varname>errno</varname> is set appropriately. Possible error
>> +    codes are:</para>
>> +
>> +    <variablelist>
>> +      <varlistentry>
>> +	<term><errorcode>EBADF</errorcode></term>
>> +	<listitem>
>> +	  <para><parameter>fd</parameter> is not a valid open file descriptor.
>> +	  </para>
>> +	</listitem>
>> +      </varlistentry>
>> +    </variablelist>
>> +  </refsect1>
>> +</refentry>
>> diff --git a/Documentation/DocBook/media/v4l/cec-func-ioctl.xml b/Documentation/DocBook/media/v4l/cec-func-ioctl.xml
>> new file mode 100644
>> index 0000000..0480eeb
>> --- /dev/null
>> +++ b/Documentation/DocBook/media/v4l/cec-func-ioctl.xml
>> @@ -0,0 +1,73 @@
>> +<refentry id="cec-func-ioctl">
>> +  <refmeta>
>> +    <refentrytitle>cec ioctl()</refentrytitle>
>> +    &manvol;
>> +  </refmeta>
>> +
>> +  <refnamediv>
>> +    <refname>cec-ioctl</refname>
>> +    <refpurpose>Control a cec device</refpurpose>
>> +  </refnamediv>
>> +
>> +  <refsynopsisdiv>
>> +    <funcsynopsis>
>> +      <funcsynopsisinfo>#include &lt;sys/ioctl.h&gt;</funcsynopsisinfo>
>> +      <funcprototype>
>> +	<funcdef>int <function>ioctl</function></funcdef>
>> +	<paramdef>int <parameter>fd</parameter></paramdef>
>> +	<paramdef>int <parameter>request</parameter></paramdef>
>> +	<paramdef>void *<parameter>argp</parameter></paramdef>
>> +      </funcprototype>
>> +    </funcsynopsis>
>> +  </refsynopsisdiv>
>> +
>> +  <refsect1>
>> +    <title>Arguments</title>
>> +
>> +    <variablelist>
>> +      <varlistentry>
>> +	<term><parameter>fd</parameter></term>
>> +	<listitem>
>> +	  <para>&fd;</para>
>> +	</listitem>
>> +      </varlistentry>
>> +      <varlistentry>
>> +	<term><parameter>request</parameter></term>
>> +	<listitem>
>> +	  <para>CEC ioctl request code as defined in the cec.h header file,
>> +	  for example CEC_ADAP_G_CAPS.</para>
>> +	</listitem>
>> +      </varlistentry>
>> +      <varlistentry>
>> +	<term><parameter>argp</parameter></term>
>> +	<listitem>
>> +	  <para>Pointer to a request-specific structure.</para>
>> +	</listitem>
>> +      </varlistentry>
>> +    </variablelist>
>> +  </refsect1>
>> +
>> +  <refsect1>
>> +    <title>Description</title>
>> +    <para>The <function>ioctl()</function> function manipulates cec device
>> +    parameters. The argument <parameter>fd</parameter> must be an open file
>> +    descriptor.</para>
>> +    <para>The ioctl <parameter>request</parameter> code specifies the cec
>> +    function to be called. It has encoded in it whether the argument is an
>> +    input, output or read/write parameter, and the size of the argument
>> +    <parameter>argp</parameter> in bytes.</para>
>> +    <para>Macros and structures definitions specifying cec ioctl requests and
>> +    their parameters are located in the cec.h header file. All cec ioctl
>> +    requests, their respective function and parameters are specified in
>> +    <xref linkend="cec-user-func" />.</para>
>> +  </refsect1>
>> +
>> +  <refsect1>
>> +    &return-value;
>> +
>> +    <para>Request-specific error codes are listed in the
>> +    individual requests descriptions.</para>
>> +    <para>When an ioctl that takes an output or read/write parameter fails,
>> +    the parameter remains unmodified.</para>
>> +  </refsect1>
>> +</refentry>
>> diff --git a/Documentation/DocBook/media/v4l/cec-func-open.xml b/Documentation/DocBook/media/v4l/cec-func-open.xml
>> new file mode 100644
>> index 0000000..d814847
>> --- /dev/null
>> +++ b/Documentation/DocBook/media/v4l/cec-func-open.xml
>> @@ -0,0 +1,94 @@
>> +<refentry id="cec-func-open">
>> +  <refmeta>
>> +    <refentrytitle>cec open()</refentrytitle>
>> +    &manvol;
>> +  </refmeta>
>> +
>> +  <refnamediv>
>> +    <refname>cec-open</refname>
>> +    <refpurpose>Open a cec device</refpurpose>
>> +  </refnamediv>
>> +
>> +  <refsynopsisdiv>
>> +    <funcsynopsis>
>> +      <funcsynopsisinfo>#include &lt;fcntl.h&gt;</funcsynopsisinfo>
>> +      <funcprototype>
>> +	<funcdef>int <function>open</function></funcdef>
>> +	<paramdef>const char *<parameter>device_name</parameter></paramdef>
>> +	<paramdef>int <parameter>flags</parameter></paramdef>
>> +      </funcprototype>
>> +    </funcsynopsis>
>> +  </refsynopsisdiv>
>> +
>> +  <refsect1>
>> +    <title>Arguments</title>
>> +
>> +    <variablelist>
>> +      <varlistentry>
>> +	<term><parameter>device_name</parameter></term>
>> +	<listitem>
>> +	  <para>Device to be opened.</para>
>> +	</listitem>
>> +      </varlistentry>
>> +      <varlistentry>
>> +	<term><parameter>flags</parameter></term>
>> +	<listitem>
>> +	  <para>Open flags. Access mode must be either <constant>O_RDONLY</constant>
>> +	  or <constant>O_RDWR</constant>. Other flags have no effect.</para>
> 
> What about O_NOBLOCK? I saw something at patch 4/13 that seems to
> indicate that it support non-block mode.

True. This was copy-and-paste from media-open and I forgot about O_NONBLOCK. I'll add
that.

> 
>> +	</listitem>
>> +      </varlistentry>
>> +    </variablelist>
>> +  </refsect1>
>> +  <refsect1>
>> +    <title>Description</title>
>> +    <para>To open a cec device applications call <function>open()</function>
>> +    with the desired device name. The function has no side effects; the device
>> +    configuration remain unchanged.</para>
>> +    <para>When the device is opened in read-only mode, attempts to modify its
>> +    configuration will result in an error, and <varname>errno</varname> will be
>> +    set to <errorcode>EBADF</errorcode>.</para>
>> +  </refsect1>
>> +  <refsect1>
>> +    <title>Return Value</title>
>> +
>> +    <para><function>open</function> returns the new file descriptor on success.
>> +    On error, -1 is returned, and <varname>errno</varname> is set appropriately.
>> +    Possible error codes are:</para>
> 
> This is more a side note that applies also to other DocBooks. Perhaps
> we should change the word to:
> 	"Possible error codes include:"
> 
> To make clearer that the list is non-exhaustive and other error codes
> may also happen. I remember we had a bug in the past with some
> multimedia application that were causing infinite loops while handling
> some VIDIOC_ENUM_* ioctl because it was programmed to expect *just*
> one error code (-EINVAL - I guess, to identify the end of an enum).

Good point, I'll change that.

> 
>> +
>> +    <variablelist>
>> +      <varlistentry>
>> +	<term><errorcode>EACCES</errorcode></term>
>> +	<listitem>
>> +	  <para>The requested access to the file is not allowed.</para>
>> +	</listitem>
>> +      </varlistentry>
>> +      <varlistentry>
>> +	<term><errorcode>EMFILE</errorcode></term>
>> +	<listitem>
>> +	  <para>The  process  already  has  the  maximum number of files open.
>> +	  </para>
>> +	</listitem>
>> +      </varlistentry>
>> +      <varlistentry>
>> +	<term><errorcode>ENFILE</errorcode></term>
>> +	<listitem>
>> +	  <para>The system limit on the total number of open files has been
>> +	  reached.</para>
>> +	</listitem>
>> +      </varlistentry>
>> +      <varlistentry>
>> +	<term><errorcode>ENOMEM</errorcode></term>
>> +	<listitem>
>> +	  <para>Insufficient kernel memory was available.</para>
>> +	</listitem>
>> +      </varlistentry>
>> +      <varlistentry>
>> +	<term><errorcode>ENXIO</errorcode></term>
>> +	<listitem>
>> +	  <para>No device corresponding to this device special file exists.
>> +	  </para>
>> +	</listitem>
>> +      </varlistentry>
>> +    </variablelist>
>> +  </refsect1>
>> +</refentry>
>> diff --git a/Documentation/DocBook/media/v4l/cec-func-poll.xml b/Documentation/DocBook/media/v4l/cec-func-poll.xml
>> new file mode 100644
>> index 0000000..6853817
>> --- /dev/null
>> +++ b/Documentation/DocBook/media/v4l/cec-func-poll.xml
>> @@ -0,0 +1,89 @@
>> +<refentry id="cec-func-poll">
>> +  <refmeta>
>> +    <refentrytitle>cec poll()</refentrytitle>
>> +    &manvol;
>> +  </refmeta>
>> +
>> +  <refnamediv>
>> +    <refname>cec-poll</refname>
>> +    <refpurpose>Wait for some event on a file descriptor</refpurpose>
>> +  </refnamediv>
>> +
>> +  <refsynopsisdiv>
>> +    <funcsynopsis>
>> +      <funcsynopsisinfo>#include &lt;sys/poll.h&gt;</funcsynopsisinfo>
>> +      <funcprototype>
>> +	<funcdef>int <function>poll</function></funcdef>
>> +	<paramdef>struct pollfd *<parameter>ufds</parameter></paramdef>
>> +	<paramdef>unsigned int <parameter>nfds</parameter></paramdef>
>> +	<paramdef>int <parameter>timeout</parameter></paramdef>
>> +      </funcprototype>
>> +    </funcsynopsis>
>> +  </refsynopsisdiv>
>> +
>> +  <refsect1>
>> +    <title>Description</title>
>> +
>> +    <para>With the <function>poll()</function> function applications
>> +can wait for CEC events.</para>
>> +
>> +    <para>On success <function>poll()</function> returns the number of
>> +file descriptors that have been selected (that is, file descriptors
>> +for which the <structfield>revents</structfield> field of the
>> +respective <structname>pollfd</structname> structure is non-zero).
>> +CEC devices set the <constant>POLLIN</constant> and
>> +<constant>POLLRDNORM</constant> flags in the
>> +<structfield>revents</structfield> field if there are messages in the
>> +receive queue. If the transmit queue has room for new messages, the
>> +<constant>POLLOUT</constant> and <constant>POLLWRNORM</constant>
>> +flags are set. If there are events in the event queue, then the
>> +<constant>POLLPRI</constant> flag is set.
>> +When the function timed out it returns a value of zero, on
>> +failure it returns <returnvalue>-1</returnvalue> and the
>> +<varname>errno</varname> variable is set appropriately.
>> +</para>
>> +
>> +    <para>For more details see the
>> +<function>poll()</function> manual page.</para>
>> +  </refsect1>
>> +
>> +  <refsect1>
>> +    <title>Return Value</title>
>> +
>> +    <para>On success, <function>poll()</function> returns the number
>> +structures which have non-zero <structfield>revents</structfield>
>> +fields, or zero if the call timed out. On error
>> +<returnvalue>-1</returnvalue> is returned, and the
>> +<varname>errno</varname> variable is set appropriately:</para>
>> +
>> +    <variablelist>
>> +      <varlistentry>
>> +	<term><errorcode>EBADF</errorcode></term>
>> +	<listitem>
>> +	  <para>One or more of the <parameter>ufds</parameter> members
>> +specify an invalid file descriptor.</para>
>> +	</listitem>
>> +      </varlistentry>
>> +      <varlistentry>
>> +	<term><errorcode>EFAULT</errorcode></term>
>> +	<listitem>
>> +	  <para><parameter>ufds</parameter> references an inaccessible
>> +memory area.</para>
>> +	</listitem>
>> +      </varlistentry>
>> +      <varlistentry>
>> +	<term><errorcode>EINTR</errorcode></term>
>> +	<listitem>
>> +	  <para>The call was interrupted by a signal.</para>
>> +	</listitem>
>> +      </varlistentry>
>> +      <varlistentry>
>> +	<term><errorcode>EINVAL</errorcode></term>
>> +	<listitem>
>> +	  <para>The <parameter>nfds</parameter> argument is greater
>> +than <constant>OPEN_MAX</constant>.</para>
>> +	</listitem>
>> +      </varlistentry>
>> +    </variablelist>
>> +  </refsect1>
>> +</refentry>
>> diff --git a/Documentation/DocBook/media/v4l/cec-ioc-adap-g-caps.xml b/Documentation/DocBook/media/v4l/cec-ioc-adap-g-caps.xml
>> new file mode 100644
>> index 0000000..b99ed22
>> --- /dev/null
>> +++ b/Documentation/DocBook/media/v4l/cec-ioc-adap-g-caps.xml
>> @@ -0,0 +1,140 @@
>> +<refentry id="cec-ioc-adap-g-caps">
>> +  <refmeta>
>> +    <refentrytitle>ioctl CEC_ADAP_G_CAPS</refentrytitle>
>> +    &manvol;
>> +  </refmeta>
>> +
>> +  <refnamediv>
>> +    <refname>CEC_ADAP_G_CAPS</refname>
>> +    <refpurpose>Query device capabilities</refpurpose>
>> +  </refnamediv>
>> +
>> +  <refsynopsisdiv>
>> +    <funcsynopsis>
>> +      <funcprototype>
>> +	<funcdef>int <function>ioctl</function></funcdef>
>> +	<paramdef>int <parameter>fd</parameter></paramdef>
>> +	<paramdef>int <parameter>request</parameter></paramdef>
>> +	<paramdef>struct cec_caps *<parameter>argp</parameter></paramdef>
>> +      </funcprototype>
>> +    </funcsynopsis>
>> +  </refsynopsisdiv>
>> +
>> +  <refsect1>
>> +    <title>Arguments</title>
>> +
>> +    <variablelist>
>> +      <varlistentry>
>> +	<term><parameter>fd</parameter></term>
>> +	<listitem>
>> +	  <para>File descriptor returned by
>> +	  <link linkend='cec-func-open'><function>open()</function></link>.</para>
>> +	</listitem>
>> +      </varlistentry>
>> +      <varlistentry>
>> +	<term><parameter>request</parameter></term>
>> +	<listitem>
>> +	  <para>CEC_ADAP_G_CAPS</para>
>> +	</listitem>
>> +      </varlistentry>
>> +      <varlistentry>
>> +	<term><parameter>argp</parameter></term>
>> +	<listitem>
>> +	  <para></para>
>> +	</listitem>
>> +      </varlistentry>
>> +    </variablelist>
>> +  </refsect1>
>> +
>> +  <refsect1>
>> +    <title>Description</title>
>> +
>> +    <para>All cec devices must support the <constant>CEC_ADAP_G_CAPS</constant>
>> +    ioctl. To query device information, applications call the ioctl with a
>> +    pointer to a &cec-caps;. The driver fills the structure and returns
>> +    the information to the application.
>> +    The ioctl never fails.</para>
>> +
>> +    <table pgwide="1" frame="none" id="cec-caps">
>> +      <title>struct <structname>cec_caps</structname></title>
>> +      <tgroup cols="3">
>> +	&cs-str;
>> +	<tbody valign="top">
>> +	  <row>
>> +	    <entry>char</entry>
>> +	    <entry><structfield>driver[32]</structfield></entry>
>> +	    <entry>The name of the cec adapter driver.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry>char</entry>
>> +	    <entry><structfield>name[32]</structfield></entry>
>> +	    <entry>The name of this CEC adapter. The combination <structfield>driver</structfield>
>> +	    and <structfield>name</structfield> must be unique.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry>__u32</entry>
>> +	    <entry><structfield>capabilities</structfield></entry>
>> +	    <entry>The capabilities of the CEC adapter, see <xref
>> +		linkend="cec-capabilities" />.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry>__u32</entry>
>> +	    <entry><structfield>version</structfield></entry>
>> +	    <entry>CEC Framework API version, formatted with the
>> +	    <constant>KERNEL_VERSION()</constant> macro.</entry>
>> +	  </row>
>> +	</tbody>
>> +      </tgroup>
>> +    </table>
>> +
>> +    <table pgwide="1" frame="none" id="cec-capabilities">
>> +      <title>CEC Capabilities Flags</title>
>> +      <tgroup cols="3">
>> +	&cs-def;
>> +	<tbody valign="top">
>> +	  <row>
>> +	    <entry><constant>CEC_CAP_PHYS_ADDR</constant></entry>
>> +	    <entry>0x00000001</entry>
>> +	    <entry>Userspace has to configure the physical address by
>> +	    calling &CEC-ADAP-S-PHYS-ADDR;.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_CAP_LOG_ADDRS</constant></entry>
>> +	    <entry>0x00000002</entry>
>> +	    <entry>Userspace has to configure the logical addresses by
>> +	    calling &CEC-ADAP-S-LOG-ADDRS;.</entry>
> 
> IMHO, on the two above, it is not clear what they means.
> 
> I mean, it should clearly state what userspace should do when:
> - the flag is active (that's described);
> - the flag is not active. This is not described. As all CEC
>   devices need some address, if the capability is disabled,
>   what userspace is supposed to do? How the device will get the
>   address?

If it is not set, then the driver takes care of it. I'll mention that
explicitly.

Ideally CEC_CAP_PHYS_ADDR should only be set for USB CEC dongles (where
the kernel driver has no idea about the EDID) and in all other cases the
HDMI tx/rx driver will automatically provide the physical address based
on the EDID, but this is no always possible.

> 
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_CAP_TRANSMIT</constant></entry>
>> +	    <entry>0x00000004</entry>
>> +	    <entry>Userspace can transmit CEC messages by calling &CEC-TRANSMIT;. This
>> +	    implies that userspace can be a follower as well, since being able to
>> +	    transmit messages is a prerequisite of becoming a follower.
>> +	    </entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_CAP_PASSTHROUGH</constant></entry>
>> +	    <entry>0x00000008</entry>
>> +	    <entry>Userspace can use the passthrough mode by
>> +	    calling &CEC-S-MODE;.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_CAP_RC</constant></entry>
>> +	    <entry>0x00000010</entry>
>> +	    <entry>This adapter supports the remote control protocol.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_CAP_MONITOR_ALL</constant></entry>
>> +	    <entry>0x00000020</entry>
>> +	    <entry>The CEC hardware can monitor all messages, not just directed and
>> +	    broadcast messages.</entry>
>> +	  </row>
>> +	</tbody>
>> +      </tgroup>
>> +    </table>
>> +  </refsect1>
>> +
>> +  <refsect1>
>> +    &return-value;
>> +  </refsect1>
>> +</refentry>
>> diff --git a/Documentation/DocBook/media/v4l/cec-ioc-adap-g-log-addrs.xml b/Documentation/DocBook/media/v4l/cec-ioc-adap-g-log-addrs.xml
>> new file mode 100644
>> index 0000000..01bc5ab
>> --- /dev/null
>> +++ b/Documentation/DocBook/media/v4l/cec-ioc-adap-g-log-addrs.xml
>> @@ -0,0 +1,324 @@
>> +<refentry id="cec-ioc-adap-g-log-addrs">
>> +  <refmeta>
>> +    <refentrytitle>ioctl CEC_ADAP_G_LOG_ADDRS, CEC_ADAP_S_LOG_ADDRS</refentrytitle>
>> +    &manvol;
>> +  </refmeta>
>> +
>> +  <refnamediv>
>> +    <refname>CEC_ADAP_G_LOG_ADDRS</refname>
>> +    <refname>CEC_ADAP_S_LOG_ADDRS</refname>
>> +    <refpurpose>Get or set the logical addresses</refpurpose>
>> +  </refnamediv>
>> +
>> +  <refsynopsisdiv>
>> +    <funcsynopsis>
>> +      <funcprototype>
>> +	<funcdef>int <function>ioctl</function></funcdef>
>> +	<paramdef>int <parameter>fd</parameter></paramdef>
>> +	<paramdef>int <parameter>request</parameter></paramdef>
>> +	<paramdef>struct cec_log_addrs *<parameter>argp</parameter></paramdef>
>> +      </funcprototype>
>> +    </funcsynopsis>
>> +  </refsynopsisdiv>
>> +
>> +  <refsect1>
>> +    <title>Arguments</title>
>> +
>> +    <variablelist>
>> +      <varlistentry>
>> +	<term><parameter>fd</parameter></term>
>> +	<listitem>
>> +	  <para>File descriptor returned by
>> +	  <link linkend='cec-func-open'><function>open()</function></link>.</para>
>> +	</listitem>
>> +      </varlistentry>
>> +      <varlistentry>
>> +	<term><parameter>request</parameter></term>
>> +	<listitem>
>> +	  <para>CEC_ADAP_G_LOG_ADDRS, CEC_ADAP_S_LOG_ADDRS</para>
>> +	</listitem>
>> +      </varlistentry>
>> +      <varlistentry>
>> +	<term><parameter>argp</parameter></term>
>> +	<listitem>
>> +	  <para></para>
>> +	</listitem>
>> +      </varlistentry>
>> +    </variablelist>
>> +  </refsect1>
>> +
>> +  <refsect1>
>> +    <title>Description</title>
>> +
>> +    <para>To query the current CEC logical addresses applications call the
> 
>  <para>To query the current CEC logical addresses applications, call the
> 	(comma is missing)

The comma should be after 'addresses', but other than that I agree.

> 
> 
>> +<constant>CEC_ADAP_G_LOG_ADDRS</constant> ioctl with a pointer to a
>> +<structname>cec_log_addrs</structname> structure where the drivers stores the
>> +logical addresses.</para>
> 	"store"
> 
>> +
>> +    <para>To set new logical addresses applications fill in struct <structname>cec_log_addrs</structname>
> 
> Comma is also missed above.
> 
>> +and call the <constant>CEC_ADAP_S_LOG_ADDRS</constant> ioctl with a pointer to this struct.
>> +The <constant>CEC_ADAP_S_LOG_ADDRS</constant> ioctl is only available if
>> +<constant>CEC_CAP_LOG_ADDRS</constant> is set. 
> 
> Please mention the return code when the ioctl is not available.

Will do.

> 
>> This ioctl will block until all
>> +requested logical addresses have been claimed. <constant>CEC_ADAP_S_LOG_ADDRS</constant>
>> +can only be called by a file handle in initiator mode (see &CEC-S-MODE;).</para>
>> +
>> +    <table pgwide="1" frame="none" id="cec-log-addrs">
>> +      <title>struct <structname>cec_log_addrs</structname></title>
>> +      <tgroup cols="3">
>> +	&cs-str;
>> +	<tbody valign="top">
>> +	  <row>
>> +	    <entry>__u8</entry>
>> +	    <entry><structfield>log_addr</structfield>[CEC_MAX_LOG_ADDRS]</entry>
>> +	    <entry>The actual logical addresses that were claimed. This is set by the
>> +	    driver. If no logical address could be claimed, then it is set to
>> +	    <constant>CEC_LOG_ADDR_INVALID</constant>. If this adapter is Unregistered,
>> +	    then <structfield>log_addr[0]</structfield> is set to 0xf and all others to
>> +	    <constant>CEC_LOG_ADDR_INVALID</constant>.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry>__u16</entry>
>> +	    <entry><structfield>log_addr_mask</structfield></entry>
>> +	    <entry>The bitmask of all logical addresses this adapter has claimed.
>> +	    If this adapter is Unregistered then <structfield>log_addr_mask</structfield>
>> +	    sets bit 15, if this adapter is not configured at all, then
>> +	    <structfield>log_addr_mask</structfield> is set to 0. Set by the driver.</entry>
> 
> Huh? that "If .. then .., if then" is very confusing. Didn't get what you're meant
> to say. Should it be a period instead of a comma before the second if?

I'll clarify. It should be a period before the second if, and I should also be more explicit
about the value in case of Unregistered: it should say that bit 15 is set and all other bits
are cleared: when a CEC adapter is unregistered it claims logical address 15 and no others.

> 
>> +	  </row>
>> +	  <row>
>> +	    <entry>__u8</entry>
>> +	    <entry><structfield>cec_version</structfield></entry>
>> +	    <entry>The CEC version that this adapter shall use. See
>> +	    <xref linkend="cec-versions" />.
>> +	    Used to implement the <constant>CEC_MSG_CEC_VERSION</constant> and
>> +	    <constant>CEC_MSG_REPORT_FEATURES</constant> messages. Note that
>> +	    <constant>CEC_OP_CEC_VERSION_1_3A</constant> is not allowed
>> +	    by the CEC framework.
>> +	    </entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry>__u8</entry>
>> +	    <entry><structfield>num_log_addrs</structfield></entry>
>> +	    <entry>Number of logical addresses to set up. Must be &le;
>> +	    <structfield>available_log_addrs</structfield> as returned by
>> +	    &CEC-ADAP-G-CAPS;. All arrays in this structure are only filled up to
>> +	    index <structfield>available_log_addrs</structfield>-1. The remaining
>> +	    array elements will be ignored. Note that the CEC 2.0 standard allows
>> +	    for a maximum of 2 logical addresses, although some hardware has support
>> +	    for more. <constant>CEC_MAX_LOG_ADDRS</constant> is 4. The driver will
>> +	    return the actual number of logical addresses it could claim, which may
>> +	    be less than what was requested. If this field is set to 0, then the
>> +	    CEC adapter shall clear all claimed logical addresses and all other
>> +	    fields will be ignored.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry>__u32</entry>
>> +	    <entry><structfield>vendor_id</structfield></entry>
>> +	    <entry>The vendor ID is a 24-bit number that identifies the specific
>> +	    vendor or entity. Based on this ID vendor specific commands may be
>> +	    defined. If you do not want a vendor ID then set it to
>> +	    <constant>CEC_VENDOR_ID_NONE</constant>.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry>__u32</entry>
>> +	    <entry><structfield>flags</structfield></entry>
>> +	    <entry>Flags. No flags are defined yet, so set this to 0.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry>char</entry>
>> +	    <entry><structfield>osd_name</structfield>[15]</entry>
>> +	    <entry>The On-Screen Display name as is returned by the
>> +	    <constant>CEC_MSG_SET_OSD_NAME</constant> message.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry>__u8</entry>
>> +	    <entry><structfield>primary_device_type</structfield>[CEC_MAX_LOG_ADDRS]</entry>
>> +	    <entry>Primary device type for each logical address. See
>> +	    <xref linkend="cec-prim-dev-types" /> for possible types.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry>__u8</entry>
>> +	    <entry><structfield>log_addr_type</structfield>[CEC_MAX_LOG_ADDRS]</entry>
>> +	    <entry>Logical address types. See <xref linkend="cec-log-addr-types" /> for
>> +	    possible types. The driver will update this with the actual logical address
>> +	    type that it claimed (e.g. it may have to fallback to
>> +	    <constant>CEC_LOG_ADDR_TYPE_UNREGISTERED</constant>).</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry>__u8</entry>
>> +	    <entry><structfield>all_device_types</structfield>[CEC_MAX_LOG_ADDRS]</entry>
>> +	    <entry>CEC 2.0 specific: all device types. See <xref linkend="cec-all-dev-types-flags" />.
>> +	    Used to implement the <constant>CEC_MSG_REPORT_FEATURES</constant> message.
>> +	    This field is ignored if <structfield>cec_version</structfield> &lt;
>> +	    <constant>CEC_OP_CEC_VERSION_2_0</constant>.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry>__u8</entry>
>> +	    <entry><structfield>features</structfield>[CEC_MAX_LOG_ADDRS][12]</entry>
>> +	    <entry>Features for each logical address. Used to implement the
>> +	    <constant>CEC_MSG_REPORT_FEATURES</constant> message. The 12 bytes include
>> +	    both the RC Profile and the Device Features.
>> +	    This field is ignored if <structfield>cec_version</structfield> &lt;
>> +	    <constant>CEC_OP_CEC_VERSION_2_0</constant>.</entry>
>> +	  </row>
>> +	</tbody>
>> +      </tgroup>
>> +    </table>
>> +
>> +    <table pgwide="1" frame="none" id="cec-versions">
>> +      <title>CEC Versions</title>
>> +      <tgroup cols="3">
>> +	&cs-def;
>> +	<tbody valign="top">
>> +	  <row>
>> +	    <entry><constant>CEC_OP_CEC_VERSION_1_3A</constant></entry>
>> +	    <entry>4</entry>
>> +	    <entry>CEC version according to the HDMI 1.3a standard.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_OP_CEC_VERSION_1_4B</constant></entry>
>> +	    <entry>5</entry>
>> +	    <entry>CEC version according to the HDMI 1.4b standard.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_OP_CEC_VERSION_2_0</constant></entry>
>> +	    <entry>6</entry>
>> +	    <entry>CEC version according to the HDMI 2.0 standard.</entry>
>> +	  </row>
>> +	</tbody>
>> +      </tgroup>
>> +    </table>
>> +
>> +    <table pgwide="1" frame="none" id="cec-prim-dev-types">
>> +      <title>CEC Primary Device Types</title>
>> +      <tgroup cols="3">
>> +	&cs-def;
>> +	<tbody valign="top">
>> +	  <row>
>> +	    <entry><constant>CEC_OP_PRIM_DEVTYPE_TV</constant></entry>
>> +	    <entry>0</entry>
>> +	    <entry>Use for a TV.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_OP_PRIM_DEVTYPE_RECORD</constant></entry>
>> +	    <entry>1</entry>
>> +	    <entry>Use for a recording device.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_OP_PRIM_DEVTYPE_TUNER</constant></entry>
>> +	    <entry>3</entry>
>> +	    <entry>Use for a device with a tuner.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_OP_PRIM_DEVTYPE_PLAYBACK</constant></entry>
>> +	    <entry>4</entry>
>> +	    <entry>Use for a playback device.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM</constant></entry>
>> +	    <entry>5</entry>
>> +	    <entry>Use for an audio system (e.g. an audio/video receiver).</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_OP_PRIM_DEVTYPE_SWITCH</constant></entry>
>> +	    <entry>6</entry>
>> +	    <entry>Use for a CEC switch.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_OP_PRIM_DEVTYPE_VIDEOPROC</constant></entry>
>> +	    <entry>7</entry>
>> +	    <entry>Use for a video processor device.</entry>
>> +	  </row>
>> +	</tbody>
>> +      </tgroup>
>> +    </table>
>> +
>> +    <table pgwide="1" frame="none" id="cec-log-addr-types">
>> +      <title>CEC Logical Address Types</title>
>> +      <tgroup cols="3">
>> +	&cs-def;
>> +	<tbody valign="top">
>> +	  <row>
>> +	    <entry><constant>CEC_LOG_ADDR_TYPE_TV</constant></entry>
>> +	    <entry>0</entry>
>> +	    <entry>Use for a TV.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_LOG_ADDR_TYPE_RECORD</constant></entry>
>> +	    <entry>1</entry>
>> +	    <entry>Use for a recording device.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_LOG_ADDR_TYPE_TUNER</constant></entry>
>> +	    <entry>2</entry>
>> +	    <entry>Use for a tuner device.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_LOG_ADDR_TYPE_PLAYBACK</constant></entry>
>> +	    <entry>3</entry>
>> +	    <entry>Use for a playback device.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_LOG_ADDR_TYPE_AUDIOSYSTEM</constant></entry>
>> +	    <entry>4</entry>
>> +	    <entry>Use for an audio system device.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_LOG_ADDR_TYPE_SPECIFIC</constant></entry>
>> +	    <entry>5</entry>
>> +	    <entry>Use for a second TV or for a video processor device.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_LOG_ADDR_TYPE_UNREGISTERED</constant></entry>
>> +	    <entry>6</entry>
>> +	    <entry>Use this if you just want to remain unregistered.
>> +	    Used for pure CEC switches or CDC-only devices (CDC:
>> +	    Capability Discovery and Control).</entry>
>> +	  </row>
>> +	</tbody>
>> +      </tgroup>
>> +    </table>
>> +
>> +    <table pgwide="1" frame="none" id="cec-all-dev-types-flags">
>> +      <title>CEC All Device Types Flags</title>
>> +      <tgroup cols="3">
>> +	&cs-def;
>> +	<tbody valign="top">
>> +	  <row>
>> +	    <entry><constant>CEC_OP_ALL_DEVTYPE_TV</constant></entry>
>> +	    <entry>0x80</entry>
>> +	    <entry>This supports the TV type.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_OP_ALL_DEVTYPE_RECORD</constant></entry>
>> +	    <entry>0x40</entry>
>> +	    <entry>This supports the Recording type.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_OP_ALL_DEVTYPE_TUNER</constant></entry>
>> +	    <entry>0x20</entry>
>> +	    <entry>This supports the Tuner type.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_OP_ALL_DEVTYPE_PLAYBACK</constant></entry>
>> +	    <entry>0x10</entry>
>> +	    <entry>This supports the Playback type.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM</constant></entry>
>> +	    <entry>0x08</entry>
>> +	    <entry>This supports the Audio System type.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_OP_ALL_DEVTYPE_SWITCH</constant></entry>
>> +	    <entry>0x04</entry>
>> +	    <entry>This supports the CEC Switch or Video Processing type.</entry>
>> +	  </row>
>> +	</tbody>
>> +      </tgroup>
>> +    </table>
>> +  </refsect1>
>> +
>> +  <refsect1>
>> +    &return-value;
>> +  </refsect1>
>> +</refentry>
>> diff --git a/Documentation/DocBook/media/v4l/cec-ioc-adap-g-phys-addr.xml b/Documentation/DocBook/media/v4l/cec-ioc-adap-g-phys-addr.xml
>> new file mode 100644
>> index 0000000..7a08269
>> --- /dev/null
>> +++ b/Documentation/DocBook/media/v4l/cec-ioc-adap-g-phys-addr.xml
>> @@ -0,0 +1,82 @@
>> +<refentry id="cec-ioc-adap-g-phys-addr">
>> +  <refmeta>
>> +    <refentrytitle>ioctl CEC_ADAP_G_PHYS_ADDR, CEC_ADAP_S_PHYS_ADDR</refentrytitle>
>> +    &manvol;
>> +  </refmeta>
>> +
>> +  <refnamediv>
>> +    <refname>CEC_ADAP_G_PHYS_ADDR</refname>
>> +    <refname>CEC_ADAP_S_PHYS_ADDR</refname>
>> +    <refpurpose>Get or set the physical address</refpurpose>
>> +  </refnamediv>
>> +
>> +  <refsynopsisdiv>
>> +    <funcsynopsis>
>> +      <funcprototype>
>> +	<funcdef>int <function>ioctl</function></funcdef>
>> +	<paramdef>int <parameter>fd</parameter></paramdef>
>> +	<paramdef>int <parameter>request</parameter></paramdef>
>> +	<paramdef>__u16 *<parameter>argp</parameter></paramdef>
>> +      </funcprototype>
>> +    </funcsynopsis>
>> +  </refsynopsisdiv>
>> +
>> +  <refsect1>
>> +    <title>Arguments</title>
>> +
>> +    <variablelist>
>> +      <varlistentry>
>> +	<term><parameter>fd</parameter></term>
>> +	<listitem>
>> +	  <para>File descriptor returned by
>> +	  <link linkend='cec-func-open'><function>open()</function></link>.</para>
>> +	</listitem>
>> +      </varlistentry>
>> +      <varlistentry>
>> +	<term><parameter>request</parameter></term>
>> +	<listitem>
>> +	  <para>CEC_ADAP_G_PHYS_ADDR, CEC_ADAP_S_PHYS_ADDR</para>
>> +	</listitem>
>> +      </varlistentry>
>> +      <varlistentry>
>> +	<term><parameter>argp</parameter></term>
>> +	<listitem>
>> +	  <para></para>
>> +	</listitem>
>> +      </varlistentry>
>> +    </variablelist>
>> +  </refsect1>
>> +
>> +  <refsect1>
>> +    <title>Description</title>
>> +
>> +    <para>To query the current physical address applications call the
>> +<constant>CEC_ADAP_G_PHYS_ADDR</constant> ioctl with a pointer to an __u16
>> +where the driver stores the physical address.</para>
>> +
>> +    <para>To set a new physical address applications store the physical address in
>> +an __u16 and call the <constant>CEC_ADAP_S_PHYS_ADDR</constant> ioctl with a
>> +pointer to this integer. <constant>CEC_ADAP_S_PHYS_ADDR</constant> is only
>> +available if <constant>CEC_CAP_PHYS_ADDR</constant> is set. It is not allowed
>> +to change a valid physical address to another valid physical address: you must
>> +select <constant>CEC_PHYS_ADDR_INVALID</constant> (f.f.f.f) first.
>> +<constant>CEC_ADAP_S_PHYS_ADDR</constant>
>> +can only be called by a file handle in initiator mode (see &CEC-S-MODE;).</para>
> 
> Please mention the error code that will be returned if the user tries to change
> from one valid address to another one.

Will do.

> 
> 
>> +
>> +    <para>The physical address is a 16-bit number where each group of 4 bits
>> +represent a digit of the physical address a.b.c.d where the most significant
>> +4 bits represent 'a'. The CEC root device (usually the TV) has address 0.0.0.0.
>> +Every device that is hooked up to an input of the TV has address a.0.0.0 (where
>> +'a' is &ge; 1), devices hooked up to those in turn have addresses a.b.0.0, etc.
>> +So a topology of up to 5 devices deep is supported. The physical address a
>> +device shall use is stored in the EDID of the sink.</para>
>> +
>> +<para>For example, the EDID for each HDMI input of the TV will have a different
>> +physical address of the form a.0.0.0 that the sources will read out and use as
>> +their physical address.</para>
>> +  </refsect1>
>> +
>> +  <refsect1>
>> +    &return-value;
>> +  </refsect1>
>> +</refentry>
>> diff --git a/Documentation/DocBook/media/v4l/cec-ioc-dqevent.xml b/Documentation/DocBook/media/v4l/cec-ioc-dqevent.xml
>> new file mode 100644
>> index 0000000..6a9960f
>> --- /dev/null
>> +++ b/Documentation/DocBook/media/v4l/cec-ioc-dqevent.xml
>> @@ -0,0 +1,190 @@
>> +<refentry id="cec-ioc-g-event">
>> +  <refmeta>
>> +    <refentrytitle>ioctl CEC_DQEVENT</refentrytitle>
>> +    &manvol;
>> +  </refmeta>
>> +
>> +  <refnamediv>
>> +    <refname>CEC_DQEVENT</refname>
>> +    <refpurpose>Dequeue a CEC event</refpurpose>
>> +  </refnamediv>
>> +
>> +  <refsynopsisdiv>
>> +    <funcsynopsis>
>> +      <funcprototype>
>> +	<funcdef>int <function>ioctl</function></funcdef>
>> +	<paramdef>int <parameter>fd</parameter></paramdef>
>> +	<paramdef>int <parameter>request</parameter></paramdef>
>> +	<paramdef>struct cec_event *<parameter>argp</parameter></paramdef>
>> +      </funcprototype>
>> +    </funcsynopsis>
>> +  </refsynopsisdiv>
>> +
>> +  <refsect1>
>> +    <title>Arguments</title>
>> +
>> +    <variablelist>
>> +      <varlistentry>
>> +	<term><parameter>fd</parameter></term>
>> +	<listitem>
>> +	  <para>File descriptor returned by
>> +	  <link linkend='cec-func-open'><function>open()</function></link>.</para>
>> +	</listitem>
>> +      </varlistentry>
>> +      <varlistentry>
>> +	<term><parameter>request</parameter></term>
>> +	<listitem>
>> +	  <para>CEC_DQEVENT</para>
>> +	</listitem>
>> +      </varlistentry>
>> +      <varlistentry>
>> +	<term><parameter>argp</parameter></term>
>> +	<listitem>
>> +	  <para></para>
>> +	</listitem>
>> +      </varlistentry>
>> +    </variablelist>
>> +  </refsect1>
>> +
>> +  <refsect1>
>> +    <title>Description</title>
>> +
>> +    <para>CEC devices can send asynchronous events. These can be retrieved by calling
>> +    the <constant>CEC_DQEVENT</constant> ioctl. If the file descriptor is in non-blocking
>> +    mode and no event is pending, then it will return -1 and set errno to the &EAGAIN;.</para>
>> +
>> +    <para>The internal event queues are per-filehandle and per-event type. If there is
>> +    no more room in a queue then the last event is overwritten with the new one. This
>> +    means that intermediate results can be thrown away but that the latest event is always
>> +    available. This also mean that is it possible to read two successive events that have
>> +    the same value (e.g. two CEC_EVENT_STATE_CHANGE events with the same state). In that
>> +    case the intermediate state changes were lost but it is guaranteed that the state
>> +    did change in between the two events.</para>
>> +
>> +    <table pgwide="1" frame="none" id="cec-event-state-change">
>> +      <title>struct <structname>cec_event_state_change</structname></title>
>> +      <tgroup cols="3">
>> +	&cs-str;
>> +	<tbody valign="top">
>> +	  <row>
>> +	    <entry>__u16</entry>
>> +	    <entry><structfield>phys_addr</structfield></entry>
>> +	    <entry>The current physical address.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry>__u16</entry>
>> +	    <entry><structfield>log_addr_mask</structfield></entry>
>> +	    <entry>The current set of claimed logical addresses.</entry>
>> +	  </row>
>> +	</tbody>
>> +      </tgroup>
>> +    </table>
>> +
>> +    <table pgwide="1" frame="none" id="cec-event-lost-msgs">
>> +      <title>struct <structname>cec_event_lost_msgs</structname></title>
>> +      <tgroup cols="3">
>> +	&cs-str;
>> +	<tbody valign="top">
>> +	  <row>
>> +	    <entry>__u32</entry>
>> +	    <entry><structfield>lost_msgs</structfield></entry>
>> +	    <entry>Set to the number of lost messages since the filehandle
>> +	    was opened or since the last time this event was dequeued for
>> +	    this filehandle.</entry>
>> +	  </row>
>> +	</tbody>
>> +      </tgroup>
>> +    </table>
>> +
>> +    <table pgwide="1" frame="none" id="cec-event">
>> +      <title>struct <structname>cec_event</structname></title>
>> +      <tgroup cols="4">
>> +	&cs-str;
>> +	<tbody valign="top">
>> +	  <row>
>> +	    <entry>__u64</entry>
>> +	    <entry><structfield>ts</structfield></entry>
>> +	    <entry>Timestamp of the event in ns.</entry>
>> +	    <entry></entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry>__u32</entry>
>> +	    <entry><structfield>event</structfield></entry>
>> +	    <entry>The event, see <xref linkend="cec-events" />.</entry>
> 
> "The Event" sounds like the name of a movie ;) Hmm... wait a moment...
> it *is* the name of a movie already: http://www.imdb.com/title/tt0314039/ :)
> 
> I guess it would be better to describe it as "The event type", as
> this is actually the type of the event, as the event itself is 
> described by the hole structure.

I'll make it less movie-like :-)

> 
>> +	    <entry></entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry>__u32</entry>
>> +	    <entry><structfield>flags</structfield></entry>
>> +	    <entry>Event flags, see <xref linkend="cec-event-flags" />.</entry>
>> +	    <entry></entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry>union</entry>
>> +	    <entry>(anonymous)</entry>
>> +	    <entry></entry>
>> +	    <entry></entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry></entry>
>> +	    <entry>struct cec_event_state_change</entry>
>> +	    <entry><structfield>state_change</structfield></entry>
>> +	    <entry>The new adapter state as sent by the <constant>CEC_EVENT_STATE_CHANGE</constant>
>> +	    event.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry></entry>
>> +	    <entry>struct cec_event_lost_msgs</entry>
>> +	    <entry><structfield>lost_msgs</structfield></entry>
>> +	    <entry>The number of lost messages as sent by the <constant>CEC_EVENT_LOST_MSGS</constant>
>> +	    event.</entry>
>> +	  </row>
>> +	</tbody>
>> +      </tgroup>
>> +    </table>
>> +
>> +    <table pgwide="1" frame="none" id="cec-events">
>> +      <title>CEC Events</title>
>> +      <tgroup cols="3">
>> +	&cs-def;
>> +	<tbody valign="top">
>> +	  <row>
>> +	    <entry><constant>CEC_EVENT_STATE_CHANGE</constant></entry>
>> +	    <entry>1</entry>
>> +	    <entry>Generated when the CEC Adapter's state changes. When open() is
>> +	    called an initial event will be generated for that filehandle with the
>> +	    CEC Adapter's state at that time.
>> +	    </entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_EVENT_LOST_MSGS</constant></entry>
>> +	    <entry>2</entry>
>> +	    <entry>Generated if one or more CEC messages were lost because the
>> +	    application didn't dequeue CEC messages fast enough.</entry>
>> +	  </row>
>> +	</tbody>
>> +      </tgroup>
>> +    </table>
>> +
>> +    <table pgwide="1" frame="none" id="cec-event-flags">
>> +      <title>CEC Event Flags</title>
>> +      <tgroup cols="3">
>> +	&cs-def;
>> +	<tbody valign="top">
>> +	  <row>
>> +	    <entry><constant>CEC_EVENT_FL_INITIAL_VALUE</constant></entry>
>> +	    <entry>1</entry>
>> +	    <entry>Set for the initial events that are generated when the device is
>> +	    opened. See the table above for which events do this. This allows
>> +	    applications to learn the initial state of the CEC adapter at open()
>> +	    time.</entry>
>> +	  </row>
>> +	</tbody>
>> +      </tgroup>
>> +    </table>
>> +  </refsect1>
>> +
>> +  <refsect1>
>> +    &return-value;
>> +  </refsect1>
>> +</refentry>
>> diff --git a/Documentation/DocBook/media/v4l/cec-ioc-g-mode.xml b/Documentation/DocBook/media/v4l/cec-ioc-g-mode.xml
>> new file mode 100644
>> index 0000000..0cb1941
>> --- /dev/null
>> +++ b/Documentation/DocBook/media/v4l/cec-ioc-g-mode.xml
>> @@ -0,0 +1,245 @@
>> +<refentry id="cec-ioc-g-mode">
>> +  <refmeta>
>> +    <refentrytitle>ioctl CEC_G_MODE, CEC_S_MODE</refentrytitle>
>> +    &manvol;
>> +  </refmeta>
>> +
>> +  <refnamediv>
>> +    <refname>CEC_G_MODE</refname>
>> +    <refname>CEC_S_MODE</refname>
>> +    <refpurpose>Get or set exclusive use of the CEC adapter</refpurpose>
>> +  </refnamediv>
>> +
>> +  <refsynopsisdiv>
>> +    <funcsynopsis>
>> +      <funcprototype>
>> +	<funcdef>int <function>ioctl</function></funcdef>
>> +	<paramdef>int <parameter>fd</parameter></paramdef>
>> +	<paramdef>int <parameter>request</parameter></paramdef>
>> +	<paramdef>__u32 *<parameter>argp</parameter></paramdef>
>> +      </funcprototype>
>> +    </funcsynopsis>
>> +  </refsynopsisdiv>
>> +
>> +  <refsect1>
>> +    <title>Arguments</title>
>> +
>> +    <variablelist>
>> +      <varlistentry>
>> +	<term><parameter>fd</parameter></term>
>> +	<listitem>
>> +	  <para>File descriptor returned by
>> +	  <link linkend='cec-func-open'><function>open()</function></link>.</para>
>> +	</listitem>
>> +      </varlistentry>
>> +      <varlistentry>
>> +	<term><parameter>request</parameter></term>
>> +	<listitem>
>> +	  <para>CEC_G_MODE, CEC_S_MODE</para>
>> +	</listitem>
>> +      </varlistentry>
>> +      <varlistentry>
>> +	<term><parameter>argp</parameter></term>
>> +	<listitem>
>> +	  <para></para>
>> +	</listitem>
>> +      </varlistentry>
>> +    </variablelist>
>> +  </refsect1>
>> +
>> +  <refsect1>
>> +    <title>Description</title>
>> +
>> +    <para>By default any filehandle can use &CEC-TRANSMIT; and &CEC-RECEIVE;, but
>> +in order to prevent applications from stepping on each others toes you want to get
>> +exclusive access to the CEC adapter. This ioctl allows you to try and become the
>> +exclusive initiator and/or follower. The initiator is the filehandle that is used
>> +to initiate messages, i.e. it commands other CEC devices. The follower is the filehandle
>> +that receives messages sent to our CEC adapter and processes them. The same filehandle
> 
> "to our CEC adapter" -> "to the directly controlled CEC adapter"

Hmm, I don't like your suggestion. How about just saying: "to the CEC adapter"?

> 
>> +can be both initiator and follower, or this role can be taken by two different
>> +filehandles.</para>
>> +
>> +    <para>When a CEC message is received, then the CEC framework will decide how
>> +it will be processed. If the message is a reply to an earlier transmitted message,
>> +then the reply is sent back to the filehandle that is waiting for it. In addition
>> +the CEC framework will process it.</para>
>> +
>> +    <para>If the message is not a reply, then the CEC framework will process it
>> +first. If there is no follower, then the message is just discarded and a feature
>> +abort is sent back to the initiator if the framework couldn't process it. If there
>> +is a follower, then the message is passed on to the follower who will use
>> +&CEC-RECEIVE; to dequeue the new message. The framework expects the follower to
>> +make the right decisions.</para>
>> +
>> +    <para>The CEC framework will process core messages unless requested otherwise
>> +by the follower. The follower can enable the passthrough mode. In that case the
> 
> Comma missing: "In that case, "

Ack.

> 
>> +CEC framework will pass on most core messages without processing them. In that
>> +case the follower will have to implement those messages. There are some messages
>> +that the core will always process, regardless of the passthrough mode.</para>
>> +
>> +    <para>If there is no initiator, then any CEC filehandle can use &CEC-TRANSMIT;.
>> +If there is an exclusive initiator then only that initiator can call &CEC-TRANSMIT;.
>> +The follower can of course always call &CEC-TRANSMIT;.</para>
>> +
>> +    <para>Available initiator modes are:</para>
>> +
>> +    <table pgwide="1" frame="none" id="cec-mode-initiator">
>> +      <title>Initiator Modes</title>
>> +      <tgroup cols="3">
>> +	&cs-def;
>> +	<tbody valign="top">
>> +	  <row>
>> +	    <entry><constant>CEC_MODE_NO_INITIATOR</constant></entry>
>> +	    <entry>0x0</entry>
>> +	    <entry>This is not an initiator, i.e. it cannot transmit CEC messages
>> +	    or make any other changes to the CEC adapter.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_MODE_INITIATOR</constant></entry>
>> +	    <entry>0x1</entry>
>> +	    <entry>This is an initiator (the default when the device is opened) and it
>> +	    can transmit CEC messages and make changes to the CEC adapter, unless there
>> +	    is an exclusive initiator.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_MODE_EXCL_INITIATOR</constant></entry>
>> +	    <entry>0x2</entry>
>> +	    <entry>This is an exclusive initiator and this file descriptor is the only one
>> +	    that can transmit CEC messages and make changes to the CEC adapter. If someone
>> +	    else is already the exclusive initiator then an attempt to become one will return
>> +	    the &EBUSY; error.</entry>
>> +	  </row>
>> +	</tbody>
>> +      </tgroup>
>> +    </table>
>> +
>> +    <para>Available follower modes are:</para>
>> +
>> +    <table pgwide="1" frame="none" id="cec-mode-follower">
>> +      <title>Follower Modes</title>
>> +      <tgroup cols="3">
>> +	&cs-def;
>> +	<tbody valign="top">
>> +	  <row>
>> +	    <entry><constant>CEC_MODE_NO_FOLLOWER</constant></entry>
>> +	    <entry>0x00</entry>
>> +	    <entry>This is not a follower (the default when the device is opened).</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_MODE_FOLLOWER</constant></entry>
>> +	    <entry>0x10</entry>
>> +	    <entry>This is a follower and it will receive CEC messages unless there is
>> +	    an exclusive follower. You cannot become a follower if <constant>CEC_CAP_TRANSMIT</constant>
>> +	    is not set or if <constant>CEC_MODE_NO_INITIATOR</constant> was specified,
>> +	    &EINVAL; is returned in that case.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_MODE_EXCL_FOLLOWER</constant></entry>
>> +	    <entry>0x20</entry>
>> +	    <entry>This is an exclusive follower and only this file descriptor will receive
>> +	    CEC messages for processing. If someone else is already the exclusive follower
>> +	    then an attempt to become one will return the &EBUSY; error. You cannot become
>> +	    a follower if <constant>CEC_CAP_TRANSMIT</constant> is not set or if
>> +	    <constant>CEC_MODE_NO_INITIATOR</constant> was specified, &EINVAL; is returned
>> +	    in that case.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_MODE_EXCL_FOLLOWER_PASSTHRU</constant></entry>
>> +	    <entry>0x30</entry>
>> +	    <entry>This is an exclusive follower and only this file descriptor will receive
>> +	    CEC messages for processing. In addition it will put the CEC device into
>> +	    passthrough mode, allowing the exclusive follower to handle most core messages
>> +	    instead of relying on the CEC framework for that. If someone else is already the
>> +	    exclusive follower then an attempt to become one will return the &EBUSY; error.
>> +	    You cannot become a follower if <constant>CEC_CAP_TRANSMIT</constant>
>> +            is not set or if <constant>CEC_MODE_NO_INITIATOR</constant> was specified,
>> +            &EINVAL; is returned in that case.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_MODE_MONITOR</constant></entry>
>> +	    <entry>0xe0</entry>
>> +	    <entry>Put the file descriptor into monitor mode. Can only be used in combination
>> +	    with <constant>CEC_MODE_NO_INITIATOR</constant>, otherwise &EINVAL; will be
>> +	    returned. In monitor mode all messages this CEC device transmits and all messages
>> +	    it receives (both broadcast messages and directed messages for one its logical
>> +	    addresses) will be reported. This is very useful for debugging.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_MODE_MONITOR_ALL</constant></entry>
>> +	    <entry>0xf0</entry>
>> +	    <entry>Put the file descriptor into 'monitor all' mode. Can only be used in combination
>> +            with <constant>CEC_MODE_NO_INITIATOR</constant>, otherwise &EINVAL; will be
>> +            returned. In 'monitor all' mode all messages this CEC device transmits and all messages
>> +            it receives, including directed messages for other CEC devices will be reported. This
>> +	    is very useful for debugging, but not all devices support this. This mode requires that
>> +	    the <constant>CEC_CAP_MONITOR_ALL</constant> capability is set, and depending on the
>> +	    hardware, you may have to be root to select this mode.</entry>
> 
> Please mention the error codes when it fails.

Ack.

> "and depending on the hardware, you may have to be root to select this mode."
> 
> No. We should define if CAP_SYS_ADMIN (or maybe CAP_NET_ADMIN, with
> is required to set promiscuous mode for network) will be required or
> not and enforce it for all hardware.

Ack for CAP_SYS_ADMIN (or possible NET_ADMIN, I'll look into that).

> IMHO, putting the device into monitor mode should require it.

No. The CEC 2.0 spec explicitly recommends that the CEC adapter should be able to
monitor all messages. The problem is that 1) not all hardware supports this, and 2)
hardware that does support this tends to mention that it is for testing only and
it shouldn't be used otherwise.

If the hardware is fine with allowing monitoring of all messages, then anyone
should be able to do that. But if it comes with all these 'for testing only' caveats,
then I think it should should be protected against 'casual' use. Unfortunately they
never tell you why it should be used for testing only (overly cautious or could
something actually fail when in this mode?).

The reality is that being able to monitor the CEC bus is extremely useful when debugging.

The simple MONITOR mode is different in that it requires no special hardware configuration.
But it won't be able to see directed messages between CEC devices other than us.

On a related note: if an application tries to become initiator or follower and someone
else has already exclusive access, then EBUSY is returned. I based this on what happens
in V4L2 with the S_PRIORITY ioctl.

However, I think EACCES is a much better error code. It's probably what S_PRIO should
have used as well.

Do you agree?

> 
>> +	  </row>
>> +	</tbody>
>> +      </tgroup>
>> +    </table>
>> +
>> +    <para>Core message processing details:</para>
>> +
>> +    <table pgwide="1" frame="none" id="cec-core-processing">
>> +      <title>Core Message Processing</title>
>> +      <tgroup cols="2">
>> +	&cs-def;
>> +	<tbody valign="top">
>> +	  <row>
>> +	    <entry><constant>CEC_MSG_GET_CEC_VERSION</constant></entry>
>> +	    <entry>When in passthrough mode this message has to be handled by userspace,
>> +	    otherwise the core will return the CEC version that was set with &CEC-ADAP-S-LOG-ADDRS;.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_MSG_GIVE_DEVICE_VENDOR_ID</constant></entry>
>> +	    <entry>When in passthrough mode this message has to be handled by userspace,
>> +	    otherwise the core will return the vendor ID that was set with &CEC-ADAP-S-LOG-ADDRS;.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_MSG_ABORT</constant></entry>
>> +	    <entry>When in passthrough mode this message has to be handled by userspace,
>> +	    otherwise the core will return a feature refused message as per the specification.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_MSG_GIVE_PHYSICAL_ADDR</constant></entry>
>> +	    <entry>When in passthrough mode this message has to be handled by userspace,
>> +	    otherwise the core will report the current physical address.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_MSG_GIVE_OSD_NAME</constant></entry>
>> +	    <entry>When in passthrough mode this message has to be handled by userspace,
>> +	    otherwise the core will report the current OSD name as was set with
>> +	    &CEC-ADAP-S-LOG-ADDRS;.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_MSG_GIVE_FEATURES</constant></entry>
>> +	    <entry>When in passthrough mode this message has to be handled by userspace,
>> +	    otherwise the core will report the current features as was set with
>> +	    &CEC-ADAP-S-LOG-ADDRS; or the message is ignore if the CEC version was
>> +	    older than 2.0.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_MSG_USER_CONTROL_PRESSED</constant></entry>
>> +	    <entry>If <constant>CEC_CAP_RC</constant> is set, then generate a remote control
>> +	    key press. This message is always passed on to userspace.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_MSG_USER_CONTROL_RELEASED</constant></entry>
>> +	    <entry>If <constant>CEC_CAP_RC</constant> is set, then generate a remote control
>> +	    key release. This message is always passed on to userspace.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_MSG_REPORT_PHYSICAL_ADDR</constant></entry>
>> +	    <entry>The CEC framework will make note of the reported physical address
>> +	    and then just pass the message on to userspace.</entry>
>> +	  </row>
>> +	</tbody>
>> +      </tgroup>
>> +    </table>
>> +  </refsect1>
>> +
>> +  <refsect1>
>> +    &return-value;
>> +  </refsect1>
>> +</refentry>
>> diff --git a/Documentation/DocBook/media/v4l/cec-ioc-receive.xml b/Documentation/DocBook/media/v4l/cec-ioc-receive.xml
>> new file mode 100644
>> index 0000000..66bf82f
>> --- /dev/null
>> +++ b/Documentation/DocBook/media/v4l/cec-ioc-receive.xml
>> @@ -0,0 +1,260 @@
>> +<refentry id="cec-ioc-receive">
>> +  <refmeta>
>> +    <refentrytitle>ioctl CEC_RECEIVE, CEC_TRANSMIT</refentrytitle>
>> +    &manvol;
>> +  </refmeta>
>> +
>> +  <refnamediv>
>> +    <refname>CEC_RECEIVE</refname>
>> +    <refname>CEC_TRANSMIT</refname>
>> +    <refpurpose>Receive or transmit a CEC message</refpurpose>
>> +  </refnamediv>
>> +
>> +  <refsynopsisdiv>
>> +    <funcsynopsis>
>> +      <funcprototype>
>> +	<funcdef>int <function>ioctl</function></funcdef>
>> +	<paramdef>int <parameter>fd</parameter></paramdef>
>> +	<paramdef>int <parameter>request</parameter></paramdef>
>> +	<paramdef>struct cec_msg *<parameter>argp</parameter></paramdef>
>> +      </funcprototype>
>> +    </funcsynopsis>
>> +  </refsynopsisdiv>
>> +
>> +  <refsect1>
>> +    <title>Arguments</title>
>> +
>> +    <variablelist>
>> +      <varlistentry>
>> +	<term><parameter>fd</parameter></term>
>> +	<listitem>
>> +	  <para>File descriptor returned by
>> +	  <link linkend='cec-func-open'><function>open()</function></link>.</para>
>> +	</listitem>
>> +      </varlistentry>
>> +      <varlistentry>
>> +	<term><parameter>request</parameter></term>
>> +	<listitem>
>> +	  <para>CEC_RECEIVE, CEC_TRANSMIT</para>
>> +	</listitem>
>> +      </varlistentry>
>> +      <varlistentry>
>> +	<term><parameter>argp</parameter></term>
>> +	<listitem>
>> +	  <para></para>
>> +	</listitem>
>> +      </varlistentry>
>> +    </variablelist>
>> +  </refsect1>
>> +
>> +  <refsect1>
>> +    <title>Description</title>
>> +
>> +    <para>To receive a CEC message the application has to fill in the
>> +    <structname>cec_msg</structname> structure and pass it to the
>> +    <constant>CEC_RECEIVE</constant> ioctl. <constant>CEC_RECEIVE</constant> is
>> +    only available if <constant>CEC_CAP_RECEIVE</constant> is set. If the
>> +    file descriptor is in non-blocking mode and there are no received
>> +    messages pending, then it will return -1 and set errno to the &EAGAIN;.
>> +    If the file descriptor is in blocking mode and <structfield>timeout</structfield>
>> +    is non-zero and no message arrived within <structfield>timeout</structfield>
>> +    milliseconds, then it will return -1 and set errno to the &ETIMEDOUT;.</para>
>> +
>> +    <para>To send a CEC message the application has to fill in the
>> +    <structname>cec_msg</structname> structure and pass it to the
>> +    <constant>CEC_TRANSMIT</constant> ioctl. <constant>CEC_TRANSMIT</constant> is
>> +    only available if <constant>CEC_CAP_TRANSMIT</constant> is set.
>> +    If there is no more room in the transmit queue, then it will return
>> +    -1 and set errno to the &EBUSY;.</para>
>> +
>> +    <table pgwide="1" frame="none" id="cec-msg">
>> +      <title>struct <structname>cec_msg</structname></title>
>> +      <tgroup cols="3">
>> +	&cs-str;
>> +	<tbody valign="top">
>> +	  <row>
>> +	    <entry>__u64</entry>
>> +	    <entry><structfield>ts</structfield></entry>
>> +	    <entry>Timestamp of when the message was transmitted in ns in the case
>> +	    of <constant>CEC_TRANSMIT</constant> with <structfield>reply</structfield>
>> +	    set to 0, or the timestamp of the received message in all other cases.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry>__u32</entry>
>> +	    <entry><structfield>len</structfield></entry>
>> +	    <entry>The length of the message. For <constant>CEC_TRANSMIT</constant> this
>> +	    is filled in by the application. The driver will fill this in for
>> +	    <constant>CEC_RECEIVE</constant> and for <constant>CEC_TRANSMIT</constant>
>> +	    it will be filled in with the length of the reply message if
>> +	    <structfield>reply</structfield> was set.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry>__u32</entry>
>> +	    <entry><structfield>timeout</structfield></entry>
>> +	    <entry>The timeout in milliseconds. This is the time we wait for a message to
>> +	    be received. If it is set to 0, then we wait indefinitely.
>> +	    It is ignored by <constant>CEC_TRANSMIT</constant>.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry>__u32</entry>
>> +	    <entry><structfield>sequence</structfield></entry>
>> +	    <entry>The sequence number is automatically assigned by the CEC
>> +	    framework for all transmitted messages. It can be later used by the
>> +	    framework to generate an event if a reply for a message was
>> +	    requested and the message was transmitted in a non-blocking mode.
>> +	    </entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry>__u32</entry>
>> +	    <entry><structfield>flags</structfield></entry>
>> +	    <entry>Flags. No flags are defined yet, so set this to 0.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry>__u8</entry>
>> +	    <entry><structfield>rx_status</structfield></entry>
>> +	    <entry>The status bits of the received message. See <xref linkend="cec-rx-status" />
>> +	    for the possible status values. It is 0 if this message was transmitted, not
>> +	    received, unless this is the reply to a transmitted message. In that case both
>> +	    <structfield>rx_status</structfield> and <structfield>tx_status</structfield>
>> +	    are set.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry>__u8</entry>
>> +	    <entry><structfield>tx_status</structfield></entry>
>> +	    <entry>The status bits of the transmitted message. See <xref linkend="cec-tx-status" />
>> +	    for the possible status values. It is 0 if this messages was received, not
>> +	    transmitted.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry>__u8</entry>
>> +	    <entry><structfield>msg</structfield>[16]</entry>
>> +	    <entry>The message payload. For <constant>CEC_TRANSMIT</constant> this
>> +	    is filled in by the application. The driver will fill this in for
>> +	    <constant>CEC_RECEIVE</constant> and for <constant>CEC_TRANSMIT</constant>
>> +	    it will be filled in with the payload of the reply message if
>> +	    <structfield>reply</structfield> was set.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry>__u8</entry>
>> +	    <entry><structfield>reply</structfield></entry>
>> +	    <entry>Wait until this message is replied. If <structfield>reply</structfield>
>> +	    is 0, then don't wait for a reply but return after transmitting the
>> +	    message. If there was an error as indicated by a non-zero <structfield>status</structfield>
>> +	    field, then <structfield>reply</structfield> is set to 0 by the driver.
>> +	    Ignored by <constant>CEC_RECEIVE</constant>.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry>__u8</entry>
>> +	    <entry><structfield>tx_arb_lost_cnt</structfield></entry>
>> +	    <entry>A counter of the number of transmit attempts that resulted in the
>> +	    Arbitration Lost error. This is only set if the hardware supports this, otherwise
>> +	    it is always 0. This counter is only valid if the <constant>CEC_TX_STATUS_ARB_LOST</constant>
>> +	    status bit is set.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry>__u8</entry>
>> +	    <entry><structfield>tx_nack_cnt</structfield></entry>
>> +	    <entry>A counter of the number of transmit attempts that resulted in the
>> +	    Not Acknowledged error. This is only set if the hardware supports this, otherwise
>> +	    it is always 0. This counter is only valid if the <constant>CEC_TX_STATUS_NACK</constant>
>> +            status bit is set.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry>__u8</entry>
>> +	    <entry><structfield>tx_low_drive_cnt</structfield></entry>
>> +	    <entry>A counter of the number of transmit attempts that resulted in the
>> +	    Arbitration Lost error. This is only set if the hardware supports this, otherwise
>> +	    it is always 0. This counter is only valid if the <constant>CEC_TX_STATUS_LOW_DRIVE</constant>
>> +            status bit is set.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry>__u8</entry>
>> +	    <entry><structfield>tx_error_cnt</structfield></entry>
>> +	    <entry>A counter of the number of transmit errors other than Arbitration Lost
>> +	    or Not Acknowledged. This is only set if the hardware supports this, otherwise
>> +	    it is always 0. This counter is only valid if the <constant>CEC_TX_STATUS_ERROR</constant>
>> +	    status bit is set.</entry>
>> +	  </row>
>> +	</tbody>
>> +      </tgroup>
>> +    </table>
>> +
>> +    <table pgwide="1" frame="none" id="cec-tx-status">
>> +      <title>CEC Transmit Status</title>
>> +      <tgroup cols="3">
>> +	&cs-def;
>> +	<tbody valign="top">
>> +	  <row>
>> +	    <entry><constant>CEC_TX_STATUS_OK</constant></entry>
>> +	    <entry>0x01</entry>
>> +	    <entry>The message was transmitted successfully. This is mutually exclusive with
>> +	    <constant>CEC_TX_STATUS_MAX_RETRIES</constant>. Other bits can still be set if
>> +	    earlier attempts met with failure before the transmit was eventually successful.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_TX_STATUS_ARB_LOST</constant></entry>
>> +	    <entry>0x02</entry>
>> +	    <entry>CEC line arbitration was lost.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_TX_STATUS_NACK</constant></entry>
>> +	    <entry>0x04</entry>
>> +	    <entry>Message was not acknowledged.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_TX_STATUS_LOW_DRIVE</constant></entry>
>> +	    <entry>0x08</entry>
>> +	    <entry>Low drive was detected on the CEC bus. This indicates that a follower
>> +	    detected an error on the bus and requests a retransmission.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_TX_STATUS_ERROR</constant></entry>
>> +	    <entry>0x10</entry>
>> +	    <entry>Some error occurred. This is used for any errors that do not
>> +	    fit the previous two, either because the hardware could not tell
>> +	    which error occurred, or because the hardware tested for other conditions
>> +	    besides those two.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_TX_STATUS_MAX_RETRIES</constant></entry>
>> +	    <entry>0x20</entry>
>> +	    <entry>The transmit failed after one or more retries. This status bit is mutually
>> +	    exclusive with <constant>CEC_TX_STATUS_OK</constant>. Other bits can still be set
>> +	    to explain which failures were seen.</entry>
>> +	  </row>
>> +	</tbody>
>> +      </tgroup>
>> +    </table>
>> +
>> +    <table pgwide="1" frame="none" id="cec-rx-status">
>> +      <title>CEC Receive Status</title>
>> +      <tgroup cols="3">
>> +	&cs-def;
>> +	<tbody valign="top">
>> +	  <row>
>> +	    <entry><constant>CEC_RX_STATUS_OK</constant></entry>
>> +	    <entry>0x01</entry>
>> +	    <entry>The message was received successfully.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_RX_STATUS_TIMEOUT</constant></entry>
>> +	    <entry>0x02</entry>
>> +	    <entry>The reply to an earlier transmitted message timed out.</entry>
>> +	  </row>
>> +	  <row>
>> +	    <entry><constant>CEC_RX_STATUS_FEATURE_ABORT</constant></entry>
>> +	    <entry>0x04</entry>
>> +	    <entry>The message was received successfully but the reply was
>> +	    <constant>CEC_MSG_FEATURE_ABORT</constant>. This status is only
>> +	    set if this message was the reply to an earlier transmitted
>> +	    message.</entry>
>> +	  </row>
>> +	</tbody>
>> +      </tgroup>
>> +    </table>
>> +  </refsect1>
>> +
>> +  <refsect1>
>> +    &return-value;
>> +  </refsect1>
>> +</refentry>
>> diff --git a/Documentation/DocBook/media_api.tmpl b/Documentation/DocBook/media_api.tmpl
>> index 7b77e0f..a2765d8 100644
>> --- a/Documentation/DocBook/media_api.tmpl
>> +++ b/Documentation/DocBook/media_api.tmpl
>> @@ -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;
> 
> 
> 
> Thanks,
> Mauro

Thank you very much for the excellent points you raised! I'll update the doc
accordingly.

Regards,

	Hans

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

* Re: [PATCHv16 09/13] cec: adv7604: add cec support.
  2016-06-16 21:17   ` Mauro Carvalho Chehab
@ 2016-06-17  8:03     ` Hans Verkuil
  2016-06-17  9:53       ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 35+ messages in thread
From: Hans Verkuil @ 2016-06-17  8:03 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: linux-media, dri-devel, linux-samsung-soc, linux-input, lars,
	linux, Hans Verkuil

On 06/16/2016 11:17 PM, Mauro Carvalho Chehab wrote:
> Em Fri, 29 Apr 2016 15:52:24 +0200
> Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> 
>> From: Hans Verkuil <hans.verkuil@cisco.com>
>>
>> Add CEC support to the adv7604 driver.
>>
>> Signed-off-by: Hans Verkuil <hansverk@cisco.com>
>> [k.debski@samsung.com: Merged changes from CEC Updates commit by Hans Verkuil]
>> [k.debski@samsung.com: add missing methods cec/io_write_and_or]
>> [k.debski@samsung.com: change adv7604 to adv76xx in added functions]
>> [hansverk@cisco.com: use _clr_set instead of _and_or]
>> ---
>>  drivers/media/i2c/Kconfig   |   9 ++
>>  drivers/media/i2c/adv7604.c | 332 +++++++++++++++++++++++++++++++++++++++-----
>>  2 files changed, 305 insertions(+), 36 deletions(-)
>>
>> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
>> index 993dc50..cba1fc7 100644
>> --- a/drivers/media/i2c/Kconfig
>> +++ b/drivers/media/i2c/Kconfig
>> @@ -209,6 +209,7 @@ config VIDEO_ADV7604
>>  	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
>>  	depends on GPIOLIB || COMPILE_TEST
>>  	select HDMI
>> +	select MEDIA_CEC_EDID
> 
> Same note for all CEC drivers: don't do that! CEC is in staging. We
> should not have any code outside staging depending or selecting CEC.

CEC_EDID is not in staging. The cec-edid module has a number of helper functions that
can parse and process the physical address contained in the EDID. It used to be
part of the cec module, but I realized that these functions are needed regardless of
whether CEC is used or not. E.g. even if you don't use CEC you don't want to set an
EDID containing an invalid physical address as that can cause problems on the
transmitter side.

> 
> Instead, put all CEC dependent code inside #ifdefs.
> 
> 
>>  	---help---
>>  	  Support for the Analog Devices ADV7604 video decoder.
>>  
>> @@ -218,6 +219,14 @@ config VIDEO_ADV7604
>>  	  To compile this driver as a module, choose M here: the
>>  	  module will be called adv7604.
>>  
>> +config VIDEO_ADV7604_CEC
>> +	bool "Enable Analog Devices ADV7604 CEC support"
>> +	depends on VIDEO_ADV7604 && MEDIA_CEC
>> +	default y
> 
> Remove "default y". CEC is experimental. Users should explicitly
> agree adding experimental code that depends on staging stuff.

OK.

> 
>> +	---help---
>> +	  When selected the adv7604 will support the optional
>> +	  HDMI CEC feature.
> 
> Please add a notice that CEC is an experimental feature under
> staging and may be changed or even removed in the future.
> 
> We don't want any application to use it yet, as we're still
> experimenting with it.
> 
> For the rest of the code below, and for patches 10-13: all the above
> applies to them.

Ack.

> 
>> +
>>  config VIDEO_ADV7842
>>  	tristate "Analog Devices ADV7842 decoder"
>>  	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
>> diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
>> index beb2841..f462585 100644
>> --- a/drivers/media/i2c/adv7604.c
>> +++ b/drivers/media/i2c/adv7604.c
>> @@ -40,6 +40,7 @@
>>  #include <linux/regmap.h>
>>  
>>  #include <media/i2c/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,12 @@ struct adv76xx_state {
>>  	u16 spa_port_a[2];
>>  	struct v4l2_fract aspect_ratio;
>>  	u32 rgb_quantization_range;
>> +
>> +	struct cec_adapter *cec_adap;
>> +	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;
>> @@ -381,7 +390,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);
>>  }
>> @@ -414,6 +424,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);
>> @@ -872,9 +888,9 @@ 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);
>>  
>> -	return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl,
>> -				info->read_cable_det(sd));
>> +	return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, cable_det);
>>  }
>>  
>>  static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd,
>> @@ -1900,6 +1916,210 @@ static int adv76xx_set_format(struct v4l2_subdev *sd,
>>  	return 0;
>>  }
>>  
>> +#if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC)
>> +static void adv76xx_cec_tx_raw_status(struct v4l2_subdev *sd, u8 tx_raw_status)
>> +{
>> +	struct adv76xx_state *state = to_state(sd);
>> +
>> +	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__);
>> +		cec_transmit_done(state->cec_adap, CEC_TX_STATUS_ARB_LOST,
>> +				  1, 0, 0, 0);
>> +	}
>> +	if (tx_raw_status & 0x04) {
>> +		u8 status;
>> +		u8 nack_cnt;
>> +		u8 low_drive_cnt;
>> +
>> +		v4l2_dbg(1, debug, sd, "%s: tx raw: retry failed\n", __func__);
>> +		/*
>> +		 * We set this status bit since this hardware performs
>> +		 * retransmissions.
>> +		 */
>> +		status = CEC_TX_STATUS_MAX_RETRIES;
>> +		nack_cnt = cec_read(sd, 0x14) & 0xf;
>> +		if (nack_cnt)
>> +			status |= CEC_TX_STATUS_NACK;
>> +		low_drive_cnt = cec_read(sd, 0x14) >> 4;
>> +		if (low_drive_cnt)
>> +			status |= CEC_TX_STATUS_LOW_DRIVE;
>> +		cec_transmit_done(state->cec_adap, status,
>> +				  0, nack_cnt, low_drive_cnt, 0);
>> +		return;
>> +	}
>> +	if (tx_raw_status & 0x01) {
>> +		v4l2_dbg(1, debug, sd, "%s: tx raw: ready ok\n", __func__);
>> +		cec_transmit_done(state->cec_adap, CEC_TX_STATUS_OK, 0, 0, 0, 0);
>> +		return;
>> +	}
>> +}
>> +
>> +static void adv76xx_cec_isr(struct v4l2_subdev *sd, bool *handled)
>> +{
>> +	struct adv76xx_state *state = to_state(sd);
>> +	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 */
>> +			cec_received_msg(state->cec_adap, &msg);
>> +		}
>> +	}
>> +
>> +	/* note: the bit order is swapped between 0x4d and 0x4e */
>> +	cec_irq = ((cec_irq & 0x08) >> 3) | ((cec_irq & 0x04) >> 1) |
>> +		  ((cec_irq & 0x02) << 1) | ((cec_irq & 0x01) << 3);
>> +	io_write(sd, 0x4e, cec_irq);
>> +
>> +	if (handled)
>> +		*handled = true;
>> +}
>> +
>> +static int adv76xx_cec_adap_enable(struct cec_adapter *adap, bool enable)
>> +{
>> +	struct adv76xx_state *state = adap->priv;
>> +	struct v4l2_subdev *sd = &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_adap_log_addr(struct cec_adapter *adap, u8 addr)
>> +{
>> +	struct adv76xx_state *state = adap->priv;
>> +	struct v4l2_subdev *sd = &state->sd;
>> +	unsigned i, free_idx = ADV76XX_MAX_ADDRS;
>> +
>> +	if (!state->cec_enabled_adap)
>> +		return addr == CEC_LOG_ADDR_INVALID ? 0 : -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_adap_transmit(struct cec_adapter *adap, u8 attempts,
>> +				     u32 signal_free_time, struct cec_msg *msg)
>> +{
>> +	struct adv76xx_state *state = adap->priv;
>> +	struct v4l2_subdev *sd = &state->sd;
>> +	u8 len = msg->len;
>> +	unsigned i;
>> +
>> +	/*
>> +	 * The number of retries is the number of attempts - 1, but retry
>> +	 * at least once. It's not clear if a value of 0 is allowed, so
>> +	 * let's do at least one retry.
>> +	 */
>> +	cec_write_clr_set(sd, 0x12, 0x70, max(1, attempts - 1) << 4);
>> +
>> +	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 const struct cec_adap_ops adv76xx_cec_adap_ops = {
>> +	.adap_enable = adv76xx_cec_adap_enable,
>> +	.adap_log_addr = adv76xx_cec_adap_log_addr,
>> +	.adap_transmit = adv76xx_cec_adap_transmit,
>> +};
>> +#endif
>> +
>>  static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
>>  {
>>  	struct adv76xx_state *state = to_state(sd);
>> @@ -1945,6 +2165,11 @@ static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
>>  			*handled = true;
>>  	}
>>  
>> +#if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC)
>> +	/* cec */
>> +	adv76xx_cec_isr(sd, handled);
>> +#endif
>> +
>>  	/* tx 5v detect */
>>  	tx_5v = irq_reg_0x70 & info->cable_det_mask;
>>  	if (tx_5v) {
>> @@ -1994,39 +2219,12 @@ static int adv76xx_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
>>  	return 0;
>>  }
>>  
>> -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;
>> -}
> 
> Very likely, you can't drop the above, as it won't compile if !CEC.
> 
> While CEC is under staging, no code here should use a function
> defined there. Even afterwards, CEC should be an optional feature.
> So, IMHO, it is better to move the EDID parsing code out of the
> CEC core, putting them elsewhere.

As mentioned above the cec-edid helper module isn't in staging.

> 
>> -
>>  static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
>>  {
>>  	struct adv76xx_state *state = to_state(sd);
>>  	const struct adv76xx_chip_info *info = state->info;
>> -	int spa_loc;
>> +	unsigned spa_loc;
>> +	u16 pa;
>>  	int err;
>>  	int i;
>>  
>> @@ -2057,6 +2255,10 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
>>  		edid->blocks = 2;
>>  		return -E2BIG;
>>  	}
>> +	pa = cec_get_edid_phys_addr(edid->edid, edid->blocks * 128, &spa_loc);
>> +	err = cec_phys_addr_validate(pa, &pa, NULL);
>> +	if (err)
>> +		return err;
>>  
>>  	v4l2_dbg(2, debug, sd, "%s: write EDID pad %d, edid.present = 0x%x\n",
>>  			__func__, edid->pad, state->edid.present);
>> @@ -2066,9 +2268,12 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
>>  	adv76xx_set_hpd(state, 0);
>>  	rep_write_clr_set(sd, info->edid_enable_reg, 0x0f, 0x00);
>>  
>> -	spa_loc = get_edid_spa_location(edid->edid);
>> -	if (spa_loc < 0)
>> -		spa_loc = 0xc0; /* Default value [REF_02, p. 116] */
>> +	/*
>> +	 * Return an error if no location of the source physical address
>> +	 * was found.
>> +	 */
>> +	if (spa_loc == 0)
>> +		return -EINVAL;
>>  
>>  	switch (edid->pad) {
>>  	case ADV76XX_PAD_HDMI_PORT_A:
>> @@ -2128,6 +2333,7 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
>>  		v4l2_err(sd, "error enabling edid (0x%x)\n", state->edid.present);
>>  		return -EIO;
>>  	}
>> +	cec_s_phys_addr(state->cec_adap, pa, false);
>>  
>>  	/* enable hotplug after 100 ms */
>>  	queue_delayed_work(state->work_queues,
>> @@ -2252,8 +2458,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);
>> @@ -2363,6 +2580,24 @@ static int adv76xx_subscribe_event(struct v4l2_subdev *sd,
>>  	}
>>  }
>>  
>> +static int adv76xx_registered(struct v4l2_subdev *sd)
>> +{
>> +	struct adv76xx_state *state = to_state(sd);
>> +	int err;
>> +
>> +	err = cec_register_adapter(state->cec_adap);
>> +	if (err)
>> +		cec_delete_adapter(state->cec_adap);
>> +	return err;
>> +}
>> +
>> +static void adv76xx_unregistered(struct v4l2_subdev *sd)
>> +{
>> +	struct adv76xx_state *state = to_state(sd);
>> +
>> +	cec_unregister_adapter(state->cec_adap);
>> +}
>> +
>>  /* ----------------------------------------------------------------------- */
>>  
>>  static const struct v4l2_ctrl_ops adv76xx_ctrl_ops = {
>> @@ -2406,6 +2641,11 @@ static const struct v4l2_subdev_ops adv76xx_ops = {
>>  	.pad = &adv76xx_pad_ops,
>>  };
>>  
>> +static const struct v4l2_subdev_internal_ops adv76xx_int_ops = {
>> +	.registered = adv76xx_registered,
>> +	.unregistered = adv76xx_unregistered,
>> +};
>> +
>>  /* -------------------------- custom ctrls ---------------------------------- */
>>  
>>  static const struct v4l2_ctrl_config adv7604_ctrl_analog_sampling_phase = {
>> @@ -3069,6 +3309,7 @@ static int adv76xx_probe(struct i2c_client *client,
>>  		id->name, i2c_adapter_id(client->adapter),
>>  		client->addr);
>>  	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
>> +	sd->internal_ops = &adv76xx_int_ops;
>>  
>>  	/* Configure IO Regmap region */
>>  	err = configure_regmap(state, ADV76XX_PAGE_IO);
>> @@ -3212,6 +3453,18 @@ static int adv76xx_probe(struct i2c_client *client,
>>  	err = adv76xx_core_init(sd);
>>  	if (err)
>>  		goto err_entity;
>> +
>> +#if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC)
>> +	state->cec_adap = cec_allocate_adapter(&adv76xx_cec_adap_ops,
>> +		state, dev_name(&client->dev),
>> +		CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS |
>> +		CEC_CAP_PASSTHROUGH | CEC_CAP_RC, ADV76XX_MAX_ADDRS,
>> +		&client->dev);
>> +	err = PTR_ERR_OR_ZERO(state->cec_adap);
>> +	if (err)
>> +		goto err_entity;
>> +#endif
>> +
>>  	v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name,
>>  			client->addr << 1, client->adapter->name);
>>  
>> @@ -3240,6 +3493,13 @@ 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);
>> +
>>  	cancel_delayed_work(&state->delayed_work_enable_hotplug);
>>  	destroy_workqueue(state->work_queues);
>>  	v4l2_async_unregister_subdev(sd);
> 
> 
> 
> Thanks,
> Mauro
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

Regards,

	Hans

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

* Re: [PATCHv16 10/13] cec: adv7842: add cec support
  2016-06-16 21:22   ` Mauro Carvalho Chehab
@ 2016-06-17  8:06       ` Hans Verkuil
  0 siblings, 0 replies; 35+ messages in thread
From: Hans Verkuil @ 2016-06-17  8:06 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: linux-media, dri-devel, linux-samsung-soc, linux-input, lars,
	linux, Hans Verkuil

On 06/16/2016 11:22 PM, Mauro Carvalho Chehab wrote:
> Em Fri, 29 Apr 2016 15:52:25 +0200
> Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> 
>> From: Hans Verkuil <hans.verkuil@cisco.com>
>>
>> Add CEC support to the adv7842 driver.
>>
>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> 
> Won't review patches 10-13, as the same reviews I made for patch 9
> very likely applies.
> 
> As this series is causing non-staging drivers to be dependent of a
> staging driver, I'll wait for the next version that should be
> solving this issue.
> 
> For the new 9-13 patches, please be sure that checkpatch will be
> happy. For the staging stuff, the checkpatch issues can be solved
> later, as I'll re-check against checkpatch when it moves from staging
> to mainstream.

I have to make changes anyway so I'll make a new pull request later
today fixing all the comments and replacing unsigned with unsigned int
(which is a majority of all the checkpatch warnings).

Did I mention yet how much I hate this new checkpatch warning? In almost all
cases I agree with the checkpatch rules, but this one is just stupid IMHO.

Oh well, I'll make the change. Perhaps it will grow on me over time.

Regards,

	Hans

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

* Re: [PATCHv16 10/13] cec: adv7842: add cec support
@ 2016-06-17  8:06       ` Hans Verkuil
  0 siblings, 0 replies; 35+ messages in thread
From: Hans Verkuil @ 2016-06-17  8:06 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: linux-samsung-soc, linux, dri-devel, lars, Hans Verkuil,
	linux-input, linux-media

On 06/16/2016 11:22 PM, Mauro Carvalho Chehab wrote:
> Em Fri, 29 Apr 2016 15:52:25 +0200
> Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> 
>> From: Hans Verkuil <hans.verkuil@cisco.com>
>>
>> Add CEC support to the adv7842 driver.
>>
>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> 
> Won't review patches 10-13, as the same reviews I made for patch 9
> very likely applies.
> 
> As this series is causing non-staging drivers to be dependent of a
> staging driver, I'll wait for the next version that should be
> solving this issue.
> 
> For the new 9-13 patches, please be sure that checkpatch will be
> happy. For the staging stuff, the checkpatch issues can be solved
> later, as I'll re-check against checkpatch when it moves from staging
> to mainstream.

I have to make changes anyway so I'll make a new pull request later
today fixing all the comments and replacing unsigned with unsigned int
(which is a majority of all the checkpatch warnings).

Did I mention yet how much I hate this new checkpatch warning? In almost all
cases I agree with the checkpatch rules, but this one is just stupid IMHO.

Oh well, I'll make the change. Perhaps it will grow on me over time.

Regards,

	Hans
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCHv16 07/13] cec.txt: add CEC framework documentation
  2016-06-17  7:22     ` Hans Verkuil
@ 2016-06-17  8:55       ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 35+ messages in thread
From: Mauro Carvalho Chehab @ 2016-06-17  8:55 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: linux-media, dri-devel, linux-samsung-soc, linux-input, lars,
	linux, Hans Verkuil, Kamil Debski, Hans Verkuil

Em Fri, 17 Jun 2016 09:22:05 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> On 06/16/2016 10:12 PM, Mauro Carvalho Chehab wrote:
> > Em Fri, 29 Apr 2016 15:52:22 +0200
> > Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> >   
> >> From: Hans Verkuil <hansverk@cisco.com>
> >>
> >> Document the new HDMI CEC framework.  
> > 
> > As we'll be moving documentation to Sphinx/Rst, it would be good if
> > you could make it work fine with sphinx, as this will likely be needed
> > for Kernel 4.9. Right now it most works, although several warnings are
> > produced:   
> 
> Would it be a problem if this conversion is done later in a separate patch?
> 
> I'm all for it, but I don't have the time at the moment to do this.

No problem. This can be done later.

> 
> Regards,
> 
> 	Hans
> 
> > 
> > /devel/v4l/patchwork/tmp/cec.txt:40: WARNING: Inline emphasis start-string without end-string.
> > /devel/v4l/patchwork/tmp/cec.txt:40: WARNING: Inline emphasis start-string without end-string.
> > /devel/v4l/patchwork/tmp/cec.txt:40: WARNING: Inline emphasis start-string without end-string.
> > /devel/v4l/patchwork/tmp/cec.txt:40: WARNING: Inline emphasis start-string without end-string.
> > /devel/v4l/patchwork/tmp/cec.txt:40: WARNING: Inline emphasis start-string without end-string.
> > /devel/v4l/patchwork/tmp/cec.txt:42: WARNING: Definition list ends without a blank line; unexpected unindent.
> > /devel/v4l/patchwork/tmp/cec.txt:42: WARNING: Inline emphasis start-string without end-string.
> > /devel/v4l/patchwork/tmp/cec.txt:67: WARNING: Inline emphasis start-string without end-string.
> > /devel/v4l/patchwork/tmp/cec.txt:71: WARNING: Inline emphasis start-string without end-string.
> > /devel/v4l/patchwork/tmp/cec.txt:92: ERROR: Unexpected indentation.
> > /devel/v4l/patchwork/tmp/cec.txt:87: WARNING: Inline emphasis start-string without end-string.
> > /devel/v4l/patchwork/tmp/cec.txt:87: WARNING: Inline emphasis start-string without end-string.
> > /devel/v4l/patchwork/tmp/cec.txt:87: WARNING: Inline emphasis start-string without end-string.
> > /devel/v4l/patchwork/tmp/cec.txt:87: WARNING: Inline emphasis start-string without end-string.
> > /devel/v4l/patchwork/tmp/cec.txt:87: WARNING: Inline emphasis start-string without end-string.
> > /devel/v4l/patchwork/tmp/cec.txt:87: WARNING: Inline emphasis start-string without end-string.
> > /devel/v4l/patchwork/tmp/cec.txt:87: WARNING: Inline emphasis start-string without end-string.
> > /devel/v4l/patchwork/tmp/cec.txt:87: WARNING: Inline emphasis start-string without end-string.
> > /devel/v4l/patchwork/tmp/cec.txt:87: WARNING: Inline emphasis start-string without end-string.
> > /devel/v4l/patchwork/tmp/cec.txt:92: WARNING: Inline emphasis start-string without end-string.
> > /devel/v4l/patchwork/tmp/cec.txt:93: WARNING: Block quote ends without a blank line; unexpected unindent.
> > /devel/v4l/patchwork/tmp/cec.txt:93: WARNING: Inline emphasis start-string without end-string.
> > /devel/v4l/patchwork/tmp/cec.txt:93: WARNING: Inline emphasis start-string without end-string.
> > /devel/v4l/patchwork/tmp/cec.txt:95: WARNING: Inline emphasis start-string without end-string.
> > /devel/v4l/patchwork/tmp/cec.txt:97: WARNING: Definition list ends without a blank line; unexpected unindent.
> > /devel/v4l/patchwork/tmp/cec.txt:105: WARNING: Inline emphasis start-string without end-string.
> > /devel/v4l/