* topic/cec: [media] DocBook/media: add CEC documentation [media] s5p_cec: get rid of an unused var [media] move s5p-cec to staging [media] vivid: add CEC emulation [media] cec: s5p-cec: Add s5p-cec driver [media] cec: adv7511: add cec support [media] cec: adv7842: add cec support [media] cec: adv7604: add cec support [media] cec: add compat32 ioctl support [media] cec/TODO: add TODO file so we know why this is still in staging [media] cec: add HDMI CEC framework (api) [media] cec: add HDMI CEC framework (adapter) [media] cec: add HDMI CEC framework (core) [media] cec-funcs.h: static inlines to pack/unpack CEC messages [media] cec.h: add cec header [media] cec-edid: add module for EDID CEC helper functions [media] cec.txt: add CEC framework documentation [media] rc: Add HDMI CEC protocol handling Input: add HDMI CEC specific keycodes Input: add BUS_CEC type
Unified · Split
| @@ -272,6 +272,9 @@ X!Isound/sound_firmware.c · 271 unmodified lines | |||
| 272 | !Iinclude/media/media-devnode.h | 272 | !Iinclude/media/media-devnode.h |
| 273 | !Iinclude/media/media-entity.h | 273 | !Iinclude/media/media-entity.h |
| 274 | </sect1> | 274 | </sect1> |
| 275 | <sect1><title>Consumer Electronics Control devices</title> | ||
| 276 | !Iinclude/media/cec-edid.h | ||
| 277 | </sect1> | ||
| 275 | 278 | ||
| 276 | </chapter> | 279 | </chapter> |
| 277 | 280 | ||
| @@ -64,6 +64,7 @@ IOCTLS = \ · 63 unmodified lines | |||
| 64 | $(shell perl -ne 'print "$$1 " if /\#define\s+([A-Z][^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/dvb/net.h) \ | 64 | $(shell perl -ne 'print "$$1 " if /\#define\s+([A-Z][^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/dvb/net.h) \ |
| 65 | $(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/dvb/video.h) \ | 65 | $(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/dvb/video.h) \ |
| 66 | $(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/media.h) \ | 66 | $(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/media.h) \ |
| 67 | $(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/linux/cec.h) \ | ||
| 67 | $(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/v4l2-subdev.h) \ | 68 | $(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/v4l2-subdev.h) \ |
| 68 | 69 | ||
| 69 | DEFINES = \ | 70 | DEFINES = \ |
| @@ -100,6 +101,7 @@ STRUCTS = \ · 30 unmodified lines | |||
| 100 | $(shell perl -ne 'print "$$1 " if (/^struct\s+([^\s]+)\s+/ && !/_old/)' $(srctree)/include/uapi/linux/dvb/net.h) \ | 101 | $(shell perl -ne 'print "$$1 " if (/^struct\s+([^\s]+)\s+/ && !/_old/)' $(srctree)/include/uapi/linux/dvb/net.h) \ |
| 101 | $(shell perl -ne 'print "$$1 " if (/^struct\s+([^\s]+)\s+/)' $(srctree)/include/uapi/linux/dvb/video.h) \ | 102 | $(shell perl -ne 'print "$$1 " if (/^struct\s+([^\s]+)\s+/)' $(srctree)/include/uapi/linux/dvb/video.h) \ |
| 102 | $(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/uapi/linux/media.h) \ | 103 | $(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/uapi/linux/media.h) \ |
| 104 | $(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/linux/cec.h) \ | ||
| 103 | $(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/uapi/linux/v4l2-subdev.h) \ | 105 | $(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/uapi/linux/v4l2-subdev.h) \ |
| 104 | $(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/uapi/linux/v4l2-mediabus.h) | 106 | $(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/uapi/linux/v4l2-mediabus.h) |
| 105 | 107 | ||
| @@ -342,6 +342,16 @@ in the frequency range from 87,5 to 108,0 MHz</title> · 341 unmodified lines | |||
| 342 | <subtitle>Specification Version 1.4a</subtitle> | 342 | <subtitle>Specification Version 1.4a</subtitle> |
| 343 | </biblioentry> | 343 | </biblioentry> |
| 344 | 344 | ||
| 345 | <biblioentry id="hdmi2"> | ||
| 346 | <abbrev>HDMI2</abbrev> | ||
| 347 | <authorgroup> | ||
| 348 | <corpauthor>HDMI Licensing LLC | ||
| 349 | (<ulink url="http://www.hdmi.org">http://www.hdmi.org</ulink>)</corpauthor> | ||
| 350 | </authorgroup> | ||
| 351 | <title>High-Definition Multimedia Interface</title> | ||
| 352 | <subtitle>Specification Version 2.0</subtitle> | ||
| 353 | </biblioentry> | ||
| 354 | |||
| 345 | <biblioentry id="dp"> | 355 | <biblioentry id="dp"> |
| 346 | <abbrev>DP</abbrev> | 356 | <abbrev>DP</abbrev> |
| 347 | <authorgroup> | 357 | <authorgroup> |
| @@ -0,0 +1,75 @@ | |||
| 1 | <partinfo> | ||
| 2 | <authorgroup> | ||
| 3 | <author> | ||
| 4 | <firstname>Hans</firstname> | ||
| 5 | <surname>Verkuil</surname> | ||
| 6 | <affiliation><address><email>hans.verkuil@cisco.com</email></address></affiliation> | ||
| 7 | <contrib>Initial version.</contrib> | ||
| 8 | </author> | ||
| 9 | </authorgroup> | ||
| 10 | <copyright> | ||
| 11 | <year>2016</year> | ||
| 12 | <holder>Hans Verkuil</holder> | ||
| 13 | </copyright> | ||
| 14 | |||
| 15 | <revhistory> | ||
| 16 | <!-- Put document revisions here, newest first. --> | ||
| 17 | <revision> | ||
| 18 | <revnumber>1.0.0</revnumber> | ||
| 19 | <date>2016-03-17</date> | ||
| 20 | <authorinitials>hv</authorinitials> | ||
| 21 | <revremark>Initial revision</revremark> | ||
| 22 | </revision> | ||
| 23 | </revhistory> | ||
| 24 | </partinfo> | ||
| 25 | |||
| 26 | <title>CEC API</title> | ||
| 27 | |||
| 28 | <chapter id="cec-api"> | ||
| 29 | <title>CEC: Consumer Electronics Control</title> | ||
| 30 | |||
| 31 | <section id="cec-intro"> | ||
| 32 | <title>Introduction</title> | ||
| 33 | <para> | ||
| 34 | Note: this documents the proposed CEC API. This API is not yet finalized and | ||
| 35 | is currently only available as a staging kernel module. | ||
| 36 | </para> | ||
| 37 | <para>HDMI connectors provide a single pin for use by the Consumer Electronics | ||
| 38 | Control protocol. This protocol allows different devices connected by an HDMI cable | ||
| 39 | to communicate. The protocol for CEC version 1.4 is defined in supplements 1 (CEC) | ||
| 40 | and 2 (HEAC or HDMI Ethernet and Audio Return Channel) of the HDMI 1.4a | ||
| 41 | (<xref linkend="hdmi" />) specification and the extensions added to CEC version 2.0 | ||
| 42 | are defined in chapter 11 of the HDMI 2.0 (<xref linkend="hdmi2" />) specification. | ||
| 43 | </para> | ||
| 44 | |||
| 45 | <para>The bitrate is very slow (effectively no more than 36 bytes per second) and | ||
| 46 | is based on the ancient AV.link protocol used in old SCART connectors. The protocol | ||
| 47 | closely resembles a crazy Rube Goldberg contraption and is an unholy mix of low and | ||
| 48 | high level messages. Some messages, especially those part of the HEAC protocol layered | ||
| 49 | on top of CEC, need to be handled by the kernel, others can be handled either by the | ||
| 50 | kernel or by userspace.</para> | ||
| 51 | |||
| 52 | <para>In addition, CEC can be implemented in HDMI receivers, transmitters and in USB | ||
| 53 | devices that have an HDMI input and an HDMI output and that control just the CEC pin.</para> | ||
| 54 | |||
| 55 | <para>Drivers that support CEC will create a CEC device node (/dev/cecX) | ||
| 56 | to give userspace access to the CEC adapter. The &CEC-ADAP-G-CAPS; ioctl will tell userspace | ||
| 57 | what it is allowed to do.</para> | ||
| 58 | </section> | ||
| 59 | </chapter> | ||
| 60 | |||
| 61 | <appendix id="cec-user-func"> | ||
| 62 | <title>Function Reference</title> | ||
| 63 | <!-- Keep this alphabetically sorted. --> | ||
| 64 | &sub-cec-func-open; | ||
| 65 | &sub-cec-func-close; | ||
| 66 | &sub-cec-func-ioctl; | ||
| 67 | &sub-cec-func-poll; | ||
| 68 | <!-- All ioctls go here. --> | ||
| 69 | &sub-cec-ioc-adap-g-caps; | ||
| 70 | &sub-cec-ioc-adap-g-log-addrs; | ||
| 71 | &sub-cec-ioc-adap-g-phys-addr; | ||
| 72 | &sub-cec-ioc-dqevent; | ||
| 73 | &sub-cec-ioc-g-mode; | ||
| 74 | &sub-cec-ioc-receive; | ||
| 75 | </appendix> | ||
| @@ -0,0 +1,64 @@ | |||
| 1 | <refentry id="cec-func-close"> | ||
| 2 | <refmeta> | ||
| 3 | <refentrytitle>cec close()</refentrytitle> | ||
| 4 | &manvol; | ||
| 5 | </refmeta> | ||
| 6 | |||
| 7 | <refnamediv> | ||
| 8 | <refname>cec-close</refname> | ||
| 9 | <refpurpose>Close a cec device</refpurpose> | ||
| 10 | </refnamediv> | ||
| 11 | |||
| 12 | <refsynopsisdiv> | ||
| 13 | <funcsynopsis> | ||
| 14 | <funcsynopsisinfo>#include <unistd.h></funcsynopsisinfo> | ||
| 15 | <funcprototype> | ||
| 16 | <funcdef>int <function>close</function></funcdef> | ||
| 17 | <paramdef>int <parameter>fd</parameter></paramdef> | ||
| 18 | </funcprototype> | ||
| 19 | </funcsynopsis> | ||
| 20 | </refsynopsisdiv> | ||
| 21 | |||
| 22 | <refsect1> | ||
| 23 | <title>Arguments</title> | ||
| 24 | |||
| 25 | <variablelist> | ||
| 26 | <varlistentry> | ||
| 27 | <term><parameter>fd</parameter></term> | ||
| 28 | <listitem> | ||
| 29 | <para>&fd;</para> | ||
| 30 | </listitem> | ||
| 31 | </varlistentry> | ||
| 32 | </variablelist> | ||
| 33 | </refsect1> | ||
| 34 | |||
| 35 | <refsect1> | ||
| 36 | <title>Description</title> | ||
| 37 | |||
| 38 | <para> | ||
| 39 | Note: this documents the proposed CEC API. This API is not yet finalized and | ||
| 40 | is currently only available as a staging kernel module. | ||
| 41 | </para> | ||
| 42 | |||
| 43 | <para>Closes the cec device. Resources associated with the file descriptor | ||
| 44 | are freed. The device configuration remain unchanged.</para> | ||
| 45 | </refsect1> | ||
| 46 | |||
| 47 | <refsect1> | ||
| 48 | <title>Return Value</title> | ||
| 49 | |||
| 50 | <para><function>close</function> returns 0 on success. On error, -1 is | ||
| 51 | returned, and <varname>errno</varname> is set appropriately. Possible error | ||
| 52 | codes are:</para> | ||
| 53 | |||
| 54 | <variablelist> | ||
| 55 | <varlistentry> | ||
| 56 | <term><errorcode>EBADF</errorcode></term> | ||
| 57 | <listitem> | ||
| 58 | <para><parameter>fd</parameter> is not a valid open file descriptor. | ||
| 59 | </para> | ||
| 60 | </listitem> | ||
| 61 | </varlistentry> | ||
| 62 | </variablelist> | ||
| 63 | </refsect1> | ||
| 64 | </refentry> | ||
| @@ -0,0 +1,78 @@ | |||
| 1 | <refentry id="cec-func-ioctl"> | ||
| 2 | <refmeta> | ||
| 3 | <refentrytitle>cec ioctl()</refentrytitle> | ||
| 4 | &manvol; | ||
| 5 | </refmeta> | ||
| 6 | |||
| 7 | <refnamediv> | ||
| 8 | <refname>cec-ioctl</refname> | ||
| 9 | <refpurpose>Control a cec device</refpurpose> | ||
| 10 | </refnamediv> | ||
| 11 | |||
| 12 | <refsynopsisdiv> | ||
| 13 | <funcsynopsis> | ||
| 14 | <funcsynopsisinfo>#include <sys/ioctl.h></funcsynopsisinfo> | ||
| 15 | <funcprototype> | ||
| 16 | <funcdef>int <function>ioctl</function></funcdef> | ||
| 17 | <paramdef>int <parameter>fd</parameter></paramdef> | ||
| 18 | <paramdef>int <parameter>request</parameter></paramdef> | ||
| 19 | <paramdef>void *<parameter>argp</parameter></paramdef> | ||
| 20 | </funcprototype> | ||
| 21 | </funcsynopsis> | ||
| 22 | </refsynopsisdiv> | ||
| 23 | |||
| 24 | <refsect1> | ||
| 25 | <title>Arguments</title> | ||
| 26 | |||
| 27 | <variablelist> | ||
| 28 | <varlistentry> | ||
| 29 | <term><parameter>fd</parameter></term> | ||
| 30 | <listitem> | ||
| 31 | <para>&fd;</para> | ||
| 32 | </listitem> | ||
| 33 | </varlistentry> | ||
| 34 | <varlistentry> | ||
| 35 | <term><parameter>request</parameter></term> | ||
| 36 | <listitem> | ||
| 37 | <para>CEC ioctl request code as defined in the cec.h header file, | ||
| 38 | for example CEC_ADAP_G_CAPS.</para> | ||
| 39 | </listitem> | ||
| 40 | </varlistentry> | ||
| 41 | <varlistentry> | ||
| 42 | <term><parameter>argp</parameter></term> | ||
| 43 | <listitem> | ||
| 44 | <para>Pointer to a request-specific structure.</para> | ||
| 45 | </listitem> | ||
| 46 | </varlistentry> | ||
| 47 | </variablelist> | ||
| 48 | </refsect1> | ||
| 49 | |||
| 50 | <refsect1> | ||
| 51 | <title>Description</title> | ||
| 52 | <para> | ||
| 53 | Note: this documents the proposed CEC API. This API is not yet finalized and | ||
| 54 | is currently only available as a staging kernel module. | ||
| 55 | </para> | ||
| 56 | |||
| 57 | <para>The <function>ioctl()</function> function manipulates cec device | ||
| 58 | parameters. The argument <parameter>fd</parameter> must be an open file | ||
| 59 | descriptor.</para> | ||
| 60 | <para>The ioctl <parameter>request</parameter> code specifies the cec | ||
| 61 | function to be called. It has encoded in it whether the argument is an | ||
| 62 | input, output or read/write parameter, and the size of the argument | ||
| 63 | <parameter>argp</parameter> in bytes.</para> | ||
| 64 | <para>Macros and structures definitions specifying cec ioctl requests and | ||
| 65 | their parameters are located in the cec.h header file. All cec ioctl | ||
| 66 | requests, their respective function and parameters are specified in | ||
| 67 | <xref linkend="cec-user-func" />.</para> | ||
| 68 | </refsect1> | ||
| 69 | |||
| 70 | <refsect1> | ||
| 71 | &return-value; | ||
| 72 | |||
| 73 | <para>Request-specific error codes are listed in the | ||
| 74 | individual requests descriptions.</para> | ||
| 75 | <para>When an ioctl that takes an output or read/write parameter fails, | ||
| 76 | the parameter remains unmodified.</para> | ||
| 77 | </refsect1> | ||
| 78 | </refentry> | ||
| @@ -0,0 +1,104 @@ | |||
| 1 | <refentry id="cec-func-open"> | ||
| 2 | <refmeta> | ||
| 3 | <refentrytitle>cec open()</refentrytitle> | ||
| 4 | &manvol; | ||
| 5 | </refmeta> | ||
| 6 | |||
| 7 | <refnamediv> | ||
| 8 | <refname>cec-open</refname> | ||
| 9 | <refpurpose>Open a cec device</refpurpose> | ||
| 10 | </refnamediv> | ||
| 11 | |||
| 12 | <refsynopsisdiv> | ||
| 13 | <funcsynopsis> | ||
| 14 | <funcsynopsisinfo>#include <fcntl.h></funcsynopsisinfo> | ||
| 15 | <funcprototype> | ||
| 16 | <funcdef>int <function>open</function></funcdef> | ||
| 17 | <paramdef>const char *<parameter>device_name</parameter></paramdef> | ||
| 18 | <paramdef>int <parameter>flags</parameter></paramdef> | ||
| 19 | </funcprototype> | ||
| 20 | </funcsynopsis> | ||
| 21 | </refsynopsisdiv> | ||
| 22 | |||
| 23 | <refsect1> | ||
| 24 | <title>Arguments</title> | ||
| 25 | |||
| 26 | <variablelist> | ||
| 27 | <varlistentry> | ||
| 28 | <term><parameter>device_name</parameter></term> | ||
| 29 | <listitem> | ||
| 30 | <para>Device to be opened.</para> | ||
| 31 | </listitem> | ||
| 32 | </varlistentry> | ||
| 33 | <varlistentry> | ||
| 34 | <term><parameter>flags</parameter></term> | ||
| 35 | <listitem> | ||
| 36 | <para>Open flags. Access mode must be <constant>O_RDWR</constant>. | ||
| 37 | </para> | ||
| 38 | <para>When the <constant>O_NONBLOCK</constant> flag is | ||
| 39 | given, the &CEC-RECEIVE; ioctl will return &EAGAIN; when no message is | ||
| 40 | available, and the &CEC-TRANSMIT;, &CEC-ADAP-S-PHYS-ADDR; and | ||
| 41 | &CEC-ADAP-S-LOG-ADDRS; ioctls all act in non-blocking mode.</para> | ||
| 42 | <para>Other flags have no effect.</para> | ||
| 43 | </listitem> | ||
| 44 | </varlistentry> | ||
| 45 | </variablelist> | ||
| 46 | </refsect1> | ||
| 47 | <refsect1> | ||
| 48 | <title>Description</title> | ||
| 49 | <para> | ||
| 50 | Note: this documents the proposed CEC API. This API is not yet finalized and | ||
| 51 | is currently only available as a staging kernel module. | ||
| 52 | </para> | ||
| 53 | |||
| 54 | <para>To open a cec device applications call <function>open()</function> | ||
| 55 | with the desired device name. The function has no side effects; the device | ||
| 56 | configuration remain unchanged.</para> | ||
| 57 | <para>When the device is opened in read-only mode, attempts to modify its | ||
| 58 | configuration will result in an error, and <varname>errno</varname> will be | ||
| 59 | set to <errorcode>EBADF</errorcode>.</para> | ||
| 60 | </refsect1> | ||
| 61 | <refsect1> | ||
| 62 | <title>Return Value</title> | ||
| 63 | |||
| 64 | <para><function>open</function> returns the new file descriptor on success. | ||
| 65 | On error, -1 is returned, and <varname>errno</varname> is set appropriately. | ||
| 66 | Possible error codes include:</para> | ||
| 67 | |||
| 68 | <variablelist> | ||
| 69 | <varlistentry> | ||
| 70 | <term><errorcode>EACCES</errorcode></term> | ||
| 71 | <listitem> | ||
| 72 | <para>The requested access to the file is not allowed.</para> | ||
| 73 | </listitem> | ||
| 74 | </varlistentry> | ||
| 75 | <varlistentry> | ||
| 76 | <term><errorcode>EMFILE</errorcode></term> | ||
| 77 | <listitem> | ||
| 78 | <para>The process already has the maximum number of files open. | ||
| 79 | </para> | ||
| 80 | </listitem> | ||
| 81 | </varlistentry> | ||
| 82 | <varlistentry> | ||
| 83 | <term><errorcode>ENFILE</errorcode></term> | ||
| 84 | <listitem> | ||
| 85 | <para>The system limit on the total number of open files has been | ||
| 86 | reached.</para> | ||
| 87 | </listitem> | ||
| 88 | </varlistentry> | ||
| 89 | <varlistentry> | ||
| 90 | <term><errorcode>ENOMEM</errorcode></term> | ||
| 91 | <listitem> | ||
| 92 | <para>Insufficient kernel memory was available.</para> | ||
| 93 | </listitem> | ||
| 94 | </varlistentry> | ||
| 95 | <varlistentry> | ||
| 96 | <term><errorcode>ENXIO</errorcode></term> | ||
| 97 | <listitem> | ||
| 98 | <para>No device corresponding to this device special file exists. | ||
| 99 | </para> | ||
| 100 | </listitem> | ||
| 101 | </varlistentry> | ||
| 102 | </variablelist> | ||
| 103 | </refsect1> | ||
| 104 | </refentry> | ||
| @@ -0,0 +1,94 @@ | |||
| 1 | <refentry id="cec-func-poll"> | ||
| 2 | <refmeta> | ||
| 3 | <refentrytitle>cec poll()</refentrytitle> | ||
| 4 | &manvol; | ||
| 5 | </refmeta> | ||
| 6 | |||
| 7 | <refnamediv> | ||
| 8 | <refname>cec-poll</refname> | ||
| 9 | <refpurpose>Wait for some event on a file descriptor</refpurpose> | ||
| 10 | </refnamediv> | ||
| 11 | |||
| 12 | <refsynopsisdiv> | ||
| 13 | <funcsynopsis> | ||
| 14 | <funcsynopsisinfo>#include <sys/poll.h></funcsynopsisinfo> | ||
| 15 | <funcprototype> | ||
| 16 | <funcdef>int <function>poll</function></funcdef> | ||
| 17 | <paramdef>struct pollfd *<parameter>ufds</parameter></paramdef> | ||
| 18 | <paramdef>unsigned int <parameter>nfds</parameter></paramdef> | ||
| 19 | <paramdef>int <parameter>timeout</parameter></paramdef> | ||
| 20 | </funcprototype> | ||
| 21 | </funcsynopsis> | ||
| 22 | </refsynopsisdiv> | ||
| 23 | |||
| 24 | <refsect1> | ||
| 25 | <title>Description</title> | ||
| 26 | |||
| 27 | <para> | ||
| 28 | Note: this documents the proposed CEC API. This API is not yet finalized and | ||
| 29 | is currently only available as a staging kernel module. | ||
| 30 | </para> | ||
| 31 | |||
| 32 | <para>With the <function>poll()</function> function applications | ||
| 33 | can wait for CEC events.</para> | ||
| 34 | |||
| 35 | <para>On success <function>poll()</function> returns the number of | ||
| 36 | file descriptors that have been selected (that is, file descriptors | ||
| 37 | for which the <structfield>revents</structfield> field of the | ||
| 38 | respective <structname>pollfd</structname> structure is non-zero). | ||
| 39 | CEC devices set the <constant>POLLIN</constant> and | ||
| 40 | <constant>POLLRDNORM</constant> flags in the | ||
| 41 | <structfield>revents</structfield> field if there are messages in the | ||
| 42 | receive queue. If the transmit queue has room for new messages, the | ||
| 43 | <constant>POLLOUT</constant> and <constant>POLLWRNORM</constant> | ||
| 44 | flags are set. If there are events in the event queue, then the | ||
| 45 | <constant>POLLPRI</constant> flag is set. | ||
| 46 | When the function timed out it returns a value of zero, on | ||
| 47 | failure it returns <returnvalue>-1</returnvalue> and the | ||
| 48 | <varname>errno</varname> variable is set appropriately. | ||
| 49 | </para> | ||
| 50 | |||
| 51 | <para>For more details see the | ||
| 52 | <function>poll()</function> manual page.</para> | ||
| 53 | </refsect1> | ||
| 54 | |||
| 55 | <refsect1> | ||
| 56 | <title>Return Value</title> | ||
| 57 | |||
| 58 | <para>On success, <function>poll()</function> returns the number | ||
| 59 | structures which have non-zero <structfield>revents</structfield> | ||
| 60 | fields, or zero if the call timed out. On error | ||
| 61 | <returnvalue>-1</returnvalue> is returned, and the | ||
| 62 | <varname>errno</varname> variable is set appropriately:</para> | ||
| 63 | |||
| 64 | <variablelist> | ||
| 65 | <varlistentry> | ||
| 66 | <term><errorcode>EBADF</errorcode></term> | ||
| 67 | <listitem> | ||
| 68 | <para>One or more of the <parameter>ufds</parameter> members | ||
| 69 | specify an invalid file descriptor.</para> | ||
| 70 | </listitem> | ||
| 71 | </varlistentry> | ||
| 72 | <varlistentry> | ||
| 73 | <term><errorcode>EFAULT</errorcode></term> | ||
| 74 | <listitem> | ||
| 75 | <para><parameter>ufds</parameter> references an inaccessible | ||
| 76 | memory area.</para> | ||
| 77 | </listitem> | ||
| 78 | </varlistentry> | ||
| 79 | <varlistentry> | ||
| 80 | <term><errorcode>EINTR</errorcode></term> | ||
| 81 | <listitem> | ||
| 82 | <para>The call was interrupted by a signal.</para> | ||
| 83 | </listitem> | ||
| 84 | </varlistentry> | ||
| 85 | <varlistentry> | ||
| 86 | <term><errorcode>EINVAL</errorcode></term> | ||
| 87 | <listitem> | ||
| 88 | <para>The <parameter>nfds</parameter> argument is greater | ||
| 89 | than <constant>OPEN_MAX</constant>.</para> | ||
| 90 | </listitem> | ||
| 91 | </varlistentry> | ||
| 92 | </variablelist> | ||
| 93 | </refsect1> | ||
| 94 | </refentry> | ||
| @@ -0,0 +1,151 @@ | |||
| 1 | <refentry id="cec-ioc-adap-g-caps"> | ||
| 2 | <refmeta> | ||
| 3 | <refentrytitle>ioctl CEC_ADAP_G_CAPS</refentrytitle> | ||
| 4 | &manvol; | ||
| 5 | </refmeta> | ||
| 6 | |||
| 7 | <refnamediv> | ||
| 8 | <refname>CEC_ADAP_G_CAPS</refname> | ||
| 9 | <refpurpose>Query device capabilities</refpurpose> | ||
| 10 | </refnamediv> | ||
| 11 | |||
| 12 | <refsynopsisdiv> | ||
| 13 | <funcsynopsis> | ||
| 14 | <funcprototype> | ||
| 15 | <funcdef>int <function>ioctl</function></funcdef> | ||
| 16 | <paramdef>int <parameter>fd</parameter></paramdef> | ||
| 17 | <paramdef>int <parameter>request</parameter></paramdef> | ||
| 18 | <paramdef>struct cec_caps *<parameter>argp</parameter></paramdef> | ||
| 19 | </funcprototype> | ||
| 20 | </funcsynopsis> | ||
| 21 | </refsynopsisdiv> | ||
| 22 | |||
| 23 | <refsect1> | ||
| 24 | <title>Arguments</title> | ||
| 25 | |||
| 26 | <variablelist> | ||
| 27 | <varlistentry> | ||
| 28 | <term><parameter>fd</parameter></term> | ||
| 29 | <listitem> | ||
| 30 | <para>File descriptor returned by | ||
| 31 | <link linkend='cec-func-open'><function>open()</function></link>.</para> | ||
| 32 | </listitem> | ||
| 33 | </varlistentry> | ||
| 34 | <varlistentry> | ||
| 35 | <term><parameter>request</parameter></term> | ||
| 36 | <listitem> | ||
| 37 | <para>CEC_ADAP_G_CAPS</para> | ||
| 38 | </listitem> | ||
| 39 | </varlistentry> | ||
| 40 | <varlistentry> | ||
| 41 | <term><parameter>argp</parameter></term> | ||
| 42 | <listitem> | ||
| 43 | <para></para> | ||
| 44 | </listitem> | ||
| 45 | </varlistentry> | ||
| 46 | </variablelist> | ||
| 47 | </refsect1> | ||
| 48 | |||
| 49 | <refsect1> | ||
| 50 | <title>Description</title> | ||
| 51 | |||
| 52 | <para> | ||
| 53 | Note: this documents the proposed CEC API. This API is not yet finalized and | ||
| 54 | is currently only available as a staging kernel module. | ||
| 55 | </para> | ||
| 56 | |||
| 57 | <para>All cec devices must support the <constant>CEC_ADAP_G_CAPS</constant> | ||
| 58 | ioctl. To query device information, applications call the ioctl with a | ||
| 59 | pointer to a &cec-caps;. The driver fills the structure and returns | ||
| 60 | the information to the application. | ||
| 61 | The ioctl never fails.</para> | ||
| 62 | |||
| 63 | <table pgwide="1" frame="none" id="cec-caps"> | ||
| 64 | <title>struct <structname>cec_caps</structname></title> | ||
| 65 | <tgroup cols="3"> | ||
| 66 | &cs-str; | ||
| 67 | <tbody valign="top"> | ||
| 68 | <row> | ||
| 69 | <entry>char</entry> | ||
| 70 | <entry><structfield>driver[32]</structfield></entry> | ||
| 71 | <entry>The name of the cec adapter driver.</entry> | ||
| 72 | </row> | ||
| 73 | <row> | ||
| 74 | <entry>char</entry> | ||
| 75 | <entry><structfield>name[32]</structfield></entry> | ||
| 76 | <entry>The name of this CEC adapter. The combination <structfield>driver</structfield> | ||
| 77 | and <structfield>name</structfield> must be unique.</entry> | ||
| 78 | </row> | ||
| 79 | <row> | ||
| 80 | <entry>__u32</entry> | ||
| 81 | <entry><structfield>capabilities</structfield></entry> | ||
| 82 | <entry>The capabilities of the CEC adapter, see <xref | ||
| 83 | linkend="cec-capabilities" />.</entry> | ||
| 84 | </row> | ||
| 85 | <row> | ||
| 86 | <entry>__u32</entry> | ||
| 87 | <entry><structfield>version</structfield></entry> | ||
| 88 | <entry>CEC Framework API version, formatted with the | ||
| 89 | <constant>KERNEL_VERSION()</constant> macro.</entry> | ||
| 90 | </row> | ||
| 91 | </tbody> | ||
| 92 | </tgroup> | ||
| 93 | </table> | ||
| 94 | |||
| 95 | <table pgwide="1" frame="none" id="cec-capabilities"> | ||
| 96 | <title>CEC Capabilities Flags</title> | ||
| 97 | <tgroup cols="3"> | ||
| 98 | &cs-def; | ||
| 99 | <tbody valign="top"> | ||
| 100 | <row> | ||
| 101 | <entry><constant>CEC_CAP_PHYS_ADDR</constant></entry> | ||
| 102 | <entry>0x00000001</entry> | ||
| 103 | <entry>Userspace has to configure the physical address by | ||
| 104 | calling &CEC-ADAP-S-PHYS-ADDR;. If this capability isn't set, | ||
| 105 | then setting the physical address is handled by the kernel | ||
| 106 | whenever the EDID is set (for an HDMI receiver) or read (for | ||
| 107 | an HDMI transmitter).</entry> | ||
| 108 | </row> | ||
| 109 | <row> | ||
| 110 | <entry><constant>CEC_CAP_LOG_ADDRS</constant></entry> | ||
| 111 | <entry>0x00000002</entry> | ||
| 112 | <entry>Userspace has to configure the logical addresses by | ||
| 113 | calling &CEC-ADAP-S-LOG-ADDRS;. If this capability isn't set, | ||
| 114 | then the kernel will have configured this.</entry> | ||
| 115 | </row> | ||
| 116 | <row> | ||
| 117 | <entry><constant>CEC_CAP_TRANSMIT</constant></entry> | ||
| 118 | <entry>0x00000004</entry> | ||
| 119 | <entry>Userspace can transmit CEC messages by calling &CEC-TRANSMIT;. This | ||
| 120 | implies that userspace can be a follower as well, since being able to | ||
| 121 | transmit messages is a prerequisite of becoming a follower. If this | ||
| 122 | capability isn't set, then the kernel will handle all CEC transmits | ||
| 123 | and process all CEC messages it receives. | ||
| 124 | </entry> | ||
| 125 | </row> | ||
| 126 | <row> | ||
| 127 | <entry><constant>CEC_CAP_PASSTHROUGH</constant></entry> | ||
| 128 | <entry>0x00000008</entry> | ||
| 129 | <entry>Userspace can use the passthrough mode by | ||
| 130 | calling &CEC-S-MODE;.</entry> | ||
| 131 | </row> | ||
| 132 | <row> | ||
| 133 | <entry><constant>CEC_CAP_RC</constant></entry> | ||
| 134 | <entry>0x00000010</entry> | ||
| 135 | <entry>This adapter supports the remote control protocol.</entry> | ||
| 136 | </row> | ||
| 137 | <row> | ||
| 138 | <entry><constant>CEC_CAP_MONITOR_ALL</constant></entry> | ||
| 139 | <entry>0x00000020</entry> | ||
| 140 | <entry>The CEC hardware can monitor all messages, not just directed and | ||
| 141 | broadcast messages.</entry> | ||
| 142 | </row> | ||
| 143 | </tbody> | ||
| 144 | </tgroup> | ||
| 145 | </table> | ||
| 146 | </refsect1> | ||
| 147 | |||
| 148 | <refsect1> | ||
| 149 | &return-value; | ||
| 150 | </refsect1> | ||
| 151 | </refentry> | ||
| @@ -0,0 +1,329 @@ | |||
| 1 | <refentry id="cec-ioc-adap-g-log-addrs"> | ||
| 2 | <refmeta> | ||
| 3 | <refentrytitle>ioctl CEC_ADAP_G_LOG_ADDRS, CEC_ADAP_S_LOG_ADDRS</refentrytitle> | ||
| 4 | &manvol; | ||
| 5 | </refmeta> | ||
| 6 | |||
| 7 | <refnamediv> | ||
| 8 | <refname>CEC_ADAP_G_LOG_ADDRS</refname> | ||
| 9 | <refname>CEC_ADAP_S_LOG_ADDRS</refname> | ||
| 10 | <refpurpose>Get or set the logical addresses</refpurpose> | ||
| 11 | </refnamediv> | ||
| 12 | |||
| 13 | <refsynopsisdiv> | ||
| 14 | <funcsynopsis> | ||
| 15 | <funcprototype> | ||
| 16 | <funcdef>int <function>ioctl</function></funcdef> | ||
| 17 | <paramdef>int <parameter>fd</parameter></paramdef> | ||
| 18 | <paramdef>int <parameter>request</parameter></paramdef> | ||
| 19 | <paramdef>struct cec_log_addrs *<parameter>argp</parameter></paramdef> | ||
| 20 | </funcprototype> | ||
| 21 | </funcsynopsis> | ||
| 22 | </refsynopsisdiv> | ||
| 23 | |||
| 24 | <refsect1> | ||
| 25 | <title>Arguments</title> | ||
| 26 | |||
| 27 | <variablelist> | ||
| 28 | <varlistentry> | ||
| 29 | <term><parameter>fd</parameter></term> | ||
| 30 | <listitem> | ||
| 31 | <para>File descriptor returned by | ||
| 32 | <link linkend='cec-func-open'><function>open()</function></link>.</para> | ||
| 33 | </listitem> | ||
| 34 | </varlistentry> | ||
| 35 | <varlistentry> | ||
| 36 | <term><parameter>request</parameter></term> | ||
| 37 | <listitem> | ||
| 38 | <para>CEC_ADAP_G_LOG_ADDRS, CEC_ADAP_S_LOG_ADDRS</para> | ||
| 39 | </listitem> | ||
| 40 | </varlistentry> | ||
| 41 | <varlistentry> | ||
| 42 | <term><parameter>argp</parameter></term> | ||
| 43 | <listitem> | ||
| 44 | <para></para> | ||
| 45 | </listitem> | ||
| 46 | </varlistentry> | ||
| 47 | </variablelist> | ||
| 48 | </refsect1> | ||
| 49 | |||
| 50 | <refsect1> | ||
| 51 | <title>Description</title> | ||
| 52 | |||
| 53 | <para> | ||
| 54 | Note: this documents the proposed CEC API. This API is not yet finalized and | ||
| 55 | is currently only available as a staging kernel module. | ||
| 56 | </para> | ||
| 57 | |||
| 58 | <para>To query the current CEC logical addresses, applications call the | ||
| 59 | <constant>CEC_ADAP_G_LOG_ADDRS</constant> ioctl with a pointer to a | ||
| 60 | <structname>cec_log_addrs</structname> structure where the drivers stores the | ||
| 61 | logical addresses.</para> | ||
| 62 | |||
| 63 | <para>To set new logical addresses, applications fill in struct <structname>cec_log_addrs</structname> | ||
| 64 | and call the <constant>CEC_ADAP_S_LOG_ADDRS</constant> ioctl with a pointer to this struct. | ||
| 65 | The <constant>CEC_ADAP_S_LOG_ADDRS</constant> ioctl is only available if | ||
| 66 | <constant>CEC_CAP_LOG_ADDRS</constant> is set (&ENOTTY; is returned otherwise). This ioctl will block until all | ||
| 67 | requested logical addresses have been claimed. <constant>CEC_ADAP_S_LOG_ADDRS</constant> | ||
| 68 | can only be called by a file handle in initiator mode (see &CEC-S-MODE;).</para> | ||
| 69 | |||
| 70 | <table pgwide="1" frame="none" id="cec-log-addrs"> | ||
| 71 | <title>struct <structname>cec_log_addrs</structname></title> | ||
| 72 | <tgroup cols="3"> | ||
| 73 | &cs-str; | ||
| 74 | <tbody valign="top"> | ||
| 75 | <row> | ||
| 76 | <entry>__u8</entry> | ||
| 77 | <entry><structfield>log_addr</structfield>[CEC_MAX_LOG_ADDRS]</entry> | ||
| 78 | <entry>The actual logical addresses that were claimed. This is set by the | ||
| 79 | driver. If no logical address could be claimed, then it is set to | ||
| 80 | <constant>CEC_LOG_ADDR_INVALID</constant>. If this adapter is Unregistered, | ||
| 81 | then <structfield>log_addr[0]</structfield> is set to 0xf and all others to | ||
| 82 | <constant>CEC_LOG_ADDR_INVALID</constant>.</entry> | ||
| 83 | </row> | ||
| 84 | <row> | ||
| 85 | <entry>__u16</entry> | ||
| 86 | <entry><structfield>log_addr_mask</structfield></entry> | ||
| 87 | <entry>The bitmask of all logical addresses this adapter has claimed. | ||
| 88 | If this adapter is Unregistered then <structfield>log_addr_mask</structfield> | ||
| 89 | sets bit 15 and clears all other bits. If this adapter is not configured at all, then | ||
| 90 | <structfield>log_addr_mask</structfield> is set to 0. Set by the driver.</entry> | ||
| 91 | </row> | ||
| 92 | <row> | ||
| 93 | <entry>__u8</entry> | ||
| 94 | <entry><structfield>cec_version</structfield></entry> | ||
| 95 | <entry>The CEC version that this adapter shall use. See | ||
| 96 | <xref linkend="cec-versions" />. | ||
| 97 | Used to implement the <constant>CEC_MSG_CEC_VERSION</constant> and | ||
| 98 | <constant>CEC_MSG_REPORT_FEATURES</constant> messages. Note that | ||
| 99 | <constant>CEC_OP_CEC_VERSION_1_3A</constant> is not allowed | ||
| 100 | by the CEC framework. | ||
| 101 | </entry> | ||
| 102 | </row> | ||
| 103 | <row> | ||
| 104 | <entry>__u8</entry> | ||
| 105 | <entry><structfield>num_log_addrs</structfield></entry> | ||
| 106 | <entry>Number of logical addresses to set up. Must be ≤ | ||
| 107 | <structfield>available_log_addrs</structfield> as returned by | ||
| 108 | &CEC-ADAP-G-CAPS;. All arrays in this structure are only filled up to | ||
| 109 | index <structfield>available_log_addrs</structfield>-1. The remaining | ||
| 110 | array elements will be ignored. Note that the CEC 2.0 standard allows | ||
| 111 | for a maximum of 2 logical addresses, although some hardware has support | ||
| 112 | for more. <constant>CEC_MAX_LOG_ADDRS</constant> is 4. The driver will | ||
| 113 | return the actual number of logical addresses it could claim, which may | ||
| 114 | be less than what was requested. If this field is set to 0, then the | ||
| 115 | CEC adapter shall clear all claimed logical addresses and all other | ||
| 116 | fields will be ignored.</entry> | ||
| 117 | </row> | ||
| 118 | <row> | ||
| 119 | <entry>__u32</entry> | ||
| 120 | <entry><structfield>vendor_id</structfield></entry> | ||
| 121 | <entry>The vendor ID is a 24-bit number that identifies the specific | ||
| 122 | vendor or entity. Based on this ID vendor specific commands may be | ||
| 123 | defined. If you do not want a vendor ID then set it to | ||
| 124 | <constant>CEC_VENDOR_ID_NONE</constant>.</entry> | ||
| 125 | </row> | ||
| 126 | <row> | ||
| 127 | <entry>__u32</entry> | ||
| 128 | <entry><structfield>flags</structfield></entry> | ||
| 129 | <entry>Flags. No flags are defined yet, so set this to 0.</entry> | ||
| 130 | </row> | ||
| 131 | <row> | ||
| 132 | <entry>char</entry> | ||
| 133 | <entry><structfield>osd_name</structfield>[15]</entry> | ||
| 134 | <entry>The On-Screen Display name as is returned by the | ||
| 135 | <constant>CEC_MSG_SET_OSD_NAME</constant> message.</entry> | ||
| 136 | </row> | ||
| 137 | <row> | ||
| 138 | <entry>__u8</entry> | ||
| 139 | <entry><structfield>primary_device_type</structfield>[CEC_MAX_LOG_ADDRS]</entry> | ||
| 140 | <entry>Primary device type for each logical address. See | ||
| 141 | <xref linkend="cec-prim-dev-types" /> for possible types.</entry> | ||
| 142 | </row> | ||
| 143 | <row> | ||
| 144 | <entry>__u8</entry> | ||
| 145 | <entry><structfield>log_addr_type</structfield>[CEC_MAX_LOG_ADDRS]</entry> | ||
| 146 | <entry>Logical address types. See <xref linkend="cec-log-addr-types" /> for | ||
| 147 | possible types. The driver will update this with the actual logical address | ||
| 148 | type that it claimed (e.g. it may have to fallback to | ||
| 149 | <constant>CEC_LOG_ADDR_TYPE_UNREGISTERED</constant>).</entry> | ||
| 150 | </row> | ||
| 151 | <row> | ||
| 152 | <entry>__u8</entry> | ||
| 153 | <entry><structfield>all_device_types</structfield>[CEC_MAX_LOG_ADDRS]</entry> | ||
| 154 | <entry>CEC 2.0 specific: all device types. See <xref linkend="cec-all-dev-types-flags" />. | ||
| 155 | Used to implement the <constant>CEC_MSG_REPORT_FEATURES</constant> message. | ||
| 156 | This field is ignored if <structfield>cec_version</structfield> < | ||
| 157 | <constant>CEC_OP_CEC_VERSION_2_0</constant>.</entry> | ||
| 158 | </row> | ||
| 159 | <row> | ||
| 160 | <entry>__u8</entry> | ||
| 161 | <entry><structfield>features</structfield>[CEC_MAX_LOG_ADDRS][12]</entry> | ||
| 162 | <entry>Features for each logical address. Used to implement the | ||
| 163 | <constant>CEC_MSG_REPORT_FEATURES</constant> message. The 12 bytes include | ||
| 164 | both the RC Profile and the Device Features. | ||
| 165 | This field is ignored if <structfield>cec_version</structfield> < | ||
| 166 | <constant>CEC_OP_CEC_VERSION_2_0</constant>.</entry> | ||
| 167 | </row> | ||
| 168 | </tbody> | ||
| 169 | </tgroup> | ||
| 170 | </table> | ||
| 171 | |||
| 172 | <table pgwide="1" frame="none" id="cec-versions"> | ||
| 173 | <title>CEC Versions</title> | ||
| 174 | <tgroup cols="3"> | ||
| 175 | &cs-def; | ||
| 176 | <tbody valign="top"> | ||
| 177 | <row> | ||
| 178 | <entry><constant>CEC_OP_CEC_VERSION_1_3A</constant></entry> | ||
| 179 | <entry>4</entry> | ||
| 180 | <entry>CEC version according to the HDMI 1.3a standard.</entry> | ||
| 181 | </row> | ||
| 182 | <row> | ||
| 183 | <entry><constant>CEC_OP_CEC_VERSION_1_4B</constant></entry> | ||
| 184 | <entry>5</entry> | ||
| 185 | <entry>CEC version according to the HDMI 1.4b standard.</entry> | ||
| 186 | </row> | ||
| 187 | <row> | ||
| 188 | <entry><constant>CEC_OP_CEC_VERSION_2_0</constant></entry> | ||
| 189 | <entry>6</entry> | ||
| 190 | <entry>CEC version according to the HDMI 2.0 standard.</entry> | ||
| 191 | </row> | ||
| 192 | </tbody> | ||
| 193 | </tgroup> | ||
| 194 | </table> | ||
| 195 | |||
| 196 | <table pgwide="1" frame="none" id="cec-prim-dev-types"> | ||
| 197 | <title>CEC Primary Device Types</title> | ||
| 198 | <tgroup cols="3"> | ||
| 199 | &cs-def; | ||
| 200 | <tbody valign="top"> | ||
| 201 | <row> | ||
| 202 | <entry><constant>CEC_OP_PRIM_DEVTYPE_TV</constant></entry> | ||
| 203 | <entry>0</entry> | ||
| 204 | <entry>Use for a TV.</entry> | ||
| 205 | </row> | ||
| 206 | <row> | ||
| 207 | <entry><constant>CEC_OP_PRIM_DEVTYPE_RECORD</constant></entry> | ||
| 208 | <entry>1</entry> | ||
| 209 | <entry>Use for a recording device.</entry> | ||
| 210 | </row> | ||
| 211 | <row> | ||
| 212 | <entry><constant>CEC_OP_PRIM_DEVTYPE_TUNER</constant></entry> | ||
| 213 | <entry>3</entry> | ||
| 214 | <entry>Use for a device with a tuner.</entry> | ||
| 215 | </row> | ||
| 216 | <row> | ||
| 217 | <entry><constant>CEC_OP_PRIM_DEVTYPE_PLAYBACK</constant></entry> | ||
| 218 | <entry>4</entry> | ||
| 219 | <entry>Use for a playback device.</entry> | ||
| 220 | </row> | ||
| 221 | <row> | ||
| 222 | <entry><constant>CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM</constant></entry> | ||
| 223 | <entry>5</entry> | ||
| 224 | <entry>Use for an audio system (e.g. an audio/video receiver).</entry> | ||
| 225 | </row> | ||
| 226 | <row> | ||
| 227 | <entry><constant>CEC_OP_PRIM_DEVTYPE_SWITCH</constant></entry> | ||
| 228 | <entry>6</entry> | ||
| 229 | <entry>Use for a CEC switch.</entry> | ||
| 230 | </row> | ||
| 231 | <row> | ||
| 232 | <entry><constant>CEC_OP_PRIM_DEVTYPE_VIDEOPROC</constant></entry> | ||
| 233 | <entry>7</entry> | ||
| 234 | <entry>Use for a video processor device.</entry> | ||
| 235 | </row> | ||
| 236 | </tbody> | ||
| 237 | </tgroup> | ||
| 238 | </table> | ||
| 239 | |||
| 240 | <table pgwide="1" frame="none" id="cec-log-addr-types"> | ||
| 241 | <title>CEC Logical Address Types</title> | ||
| 242 | <tgroup cols="3"> | ||
| 243 | &cs-def; | ||
| 244 | <tbody valign="top"> | ||
| 245 | <row> | ||
| 246 | <entry><constant>CEC_LOG_ADDR_TYPE_TV</constant></entry> | ||
| 247 | <entry>0</entry> | ||
| 248 | <entry>Use for a TV.</entry> | ||
| 249 | </row> | ||
| 250 | <row> | ||
| 251 | <entry><constant>CEC_LOG_ADDR_TYPE_RECORD</constant></entry> | ||
| 252 | <entry>1</entry> | ||
| 253 | <entry>Use for a recording device.</entry> | ||
| 254 | </row> | ||
| 255 | <row> | ||
| 256 | <entry><constant>CEC_LOG_ADDR_TYPE_TUNER</constant></entry> | ||
| 257 | <entry>2</entry> | ||
| 258 | <entry>Use for a tuner device.</entry> | ||
| 259 | </row> | ||
| 260 | <row> | ||
| 261 | <entry><constant>CEC_LOG_ADDR_TYPE_PLAYBACK</constant></entry> | ||
| 262 | <entry>3</entry> | ||
| 263 | <entry>Use for a playback device.</entry> | ||
| 264 | </row> | ||
| 265 | <row> | ||
| 266 | <entry><constant>CEC_LOG_ADDR_TYPE_AUDIOSYSTEM</constant></entry> | ||
| 267 | <entry>4</entry> | ||
| 268 | <entry>Use for an audio system device.</entry> | ||
| 269 | </row> | ||
| 270 | <row> | ||
| 271 | <entry><constant>CEC_LOG_ADDR_TYPE_SPECIFIC</constant></entry> | ||
| 272 | <entry>5</entry> | ||
| 273 | <entry>Use for a second TV or for a video processor device.</entry> | ||
| 274 | </row> | ||
| 275 | <row> | ||
| 276 | <entry><constant>CEC_LOG_ADDR_TYPE_UNREGISTERED</constant></entry> | ||
| 277 | <entry>6</entry> | ||
| 278 | <entry>Use this if you just want to remain unregistered. | ||
| 279 | Used for pure CEC switches or CDC-only devices (CDC: | ||
| 280 | Capability Discovery and Control).</entry> | ||
| 281 | </row> | ||
| 282 | </tbody> | ||
| 283 | </tgroup> | ||
| 284 | </table> | ||
| 285 | |||
| 286 | <table pgwide="1" frame="none" id="cec-all-dev-types-flags"> | ||
| 287 | <title>CEC All Device Types Flags</title> | ||
| 288 | <tgroup cols="3"> | ||
| 289 | &cs-def; | ||
| 290 | <tbody valign="top"> | ||
| 291 | <row> | ||
| 292 | <entry><constant>CEC_OP_ALL_DEVTYPE_TV</constant></entry> | ||
| 293 | <entry>0x80</entry> | ||
| 294 | <entry>This supports the TV type.</entry> | ||
| 295 | </row> | ||
| 296 | <row> | ||
| 297 | <entry><constant>CEC_OP_ALL_DEVTYPE_RECORD</constant></entry> | ||
| 298 | <entry>0x40</entry> | ||
| 299 | <entry>This supports the Recording type.</entry> | ||
| 300 | </row> | ||
| 301 | <row> | ||
| 302 | <entry><constant>CEC_OP_ALL_DEVTYPE_TUNER</constant></entry> | ||
| 303 | <entry>0x20</entry> | ||
| 304 | <entry>This supports the Tuner type.</entry> | ||
| 305 | </row> | ||
| 306 | <row> | ||
| 307 | <entry><constant>CEC_OP_ALL_DEVTYPE_PLAYBACK</constant></entry> | ||
| 308 | <entry>0x10</entry> | ||
| 309 | <entry>This supports the Playback type.</entry> | ||
| 310 | </row> | ||
| 311 | <row> | ||
| 312 | <entry><constant>CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM</constant></entry> | ||
| 313 | <entry>0x08</entry> | ||
| 314 | <entry>This supports the Audio System type.</entry> | ||
| 315 | </row> | ||
| 316 | <row> | ||
| 317 | <entry><constant>CEC_OP_ALL_DEVTYPE_SWITCH</constant></entry> | ||
| 318 | <entry>0x04</entry> | ||
| 319 | <entry>This supports the CEC Switch or Video Processing type.</entry> | ||
| 320 | </row> | ||
| 321 | </tbody> | ||
| 322 | </tgroup> | ||
| 323 | </table> | ||
| 324 | </refsect1> | ||
| 325 | |||
| 326 | <refsect1> | ||
| 327 | &return-value; | ||
| 328 | </refsect1> | ||
| 329 | </refentry> | ||
| @@ -0,0 +1,86 @@ | |||
| 1 | <refentry id="cec-ioc-adap-g-phys-addr"> | ||
| 2 | <refmeta> | ||
| 3 | <refentrytitle>ioctl CEC_ADAP_G_PHYS_ADDR, CEC_ADAP_S_PHYS_ADDR</refentrytitle> | ||
| 4 | &manvol; | ||
| 5 | </refmeta> | ||
| 6 | |||
| 7 | <refnamediv> | ||
| 8 | <refname>CEC_ADAP_G_PHYS_ADDR</refname> | ||
| 9 | <refname>CEC_ADAP_S_PHYS_ADDR</refname> | ||
| 10 | <refpurpose>Get or set the physical address</refpurpose> | ||
| 11 | </refnamediv> | ||
| 12 | |||
| 13 | <refsynopsisdiv> | ||
| 14 | <funcsynopsis> | ||
| 15 | <funcprototype> | ||
| 16 | <funcdef>int <function>ioctl</function></funcdef> | ||
| 17 | <paramdef>int <parameter>fd</parameter></paramdef> | ||
| 18 | <paramdef>int <parameter>request</parameter></paramdef> | ||
| 19 | <paramdef>__u16 *<parameter>argp</parameter></paramdef> | ||
| 20 | </funcprototype> | ||
| 21 | </funcsynopsis> | ||
| 22 | </refsynopsisdiv> | ||
| 23 | |||
| 24 | <refsect1> | ||
| 25 | <title>Arguments</title> | ||
| 26 | |||
| 27 | <variablelist> | ||
| 28 | <varlistentry> | ||
| 29 | <term><parameter>fd</parameter></term> | ||
| 30 | <listitem> | ||
| 31 | <para>File descriptor returned by | ||
| 32 | <link linkend='cec-func-open'><function>open()</function></link>.</para> | ||
| 33 | </listitem> | ||
| 34 | </varlistentry> | ||
| 35 | <varlistentry> | ||
| 36 | <term><parameter>request</parameter></term> | ||
| 37 | <listitem> | ||
| 38 | <para>CEC_ADAP_G_PHYS_ADDR, CEC_ADAP_S_PHYS_ADDR</para> | ||
| 39 | </listitem> | ||
| 40 | </varlistentry> | ||
| 41 | <varlistentry> | ||
| 42 | <term><parameter>argp</parameter></term> | ||
| 43 | <listitem> | ||
| 44 | <para></para> | ||
| 45 | </listitem> | ||
| 46 | </varlistentry> | ||
| 47 | </variablelist> | ||
| 48 | </refsect1> | ||
| 49 | |||
| 50 | <refsect1> | ||
| 51 | <title>Description</title> | ||
| 52 | |||
| 53 | <para> | ||
| 54 | Note: this documents the proposed CEC API. This API is not yet finalized and | ||
| 55 | is currently only available as a staging kernel module. | ||
| 56 | </para> | ||
| 57 | |||
| 58 | <para>To query the current physical address applications call the | ||
| 59 | <constant>CEC_ADAP_G_PHYS_ADDR</constant> ioctl with a pointer to an __u16 | ||
| 60 | where the driver stores the physical address.</para> | ||
| 61 | |||
| 62 | <para>To set a new physical address applications store the physical address in | ||
| 63 | an __u16 and call the <constant>CEC_ADAP_S_PHYS_ADDR</constant> ioctl with a | ||
| 64 | pointer to this integer. <constant>CEC_ADAP_S_PHYS_ADDR</constant> is only | ||
| 65 | available if <constant>CEC_CAP_PHYS_ADDR</constant> is set (&ENOTTY; will be returned | ||
| 66 | otherwise). <constant>CEC_ADAP_S_PHYS_ADDR</constant> | ||
| 67 | can only be called by a file handle in initiator mode (see &CEC-S-MODE;), if not | ||
| 68 | &EBUSY; will be returned.</para> | ||
| 69 | |||
| 70 | <para>The physical address is a 16-bit number where each group of 4 bits | ||
| 71 | represent a digit of the physical address a.b.c.d where the most significant | ||
| 72 | 4 bits represent 'a'. The CEC root device (usually the TV) has address 0.0.0.0. | ||
| 73 | Every device that is hooked up to an input of the TV has address a.0.0.0 (where | ||
| 74 | 'a' is ≥ 1), devices hooked up to those in turn have addresses a.b.0.0, etc. | ||
| 75 | So a topology of up to 5 devices deep is supported. The physical address a | ||
| 76 | device shall use is stored in the EDID of the sink.</para> | ||
| 77 | |||
| 78 | <para>For example, the EDID for each HDMI input of the TV will have a different | ||
| 79 | physical address of the form a.0.0.0 that the sources will read out and use as | ||
| 80 | their physical address.</para> | ||
| 81 | </refsect1> | ||
| 82 | |||
| 83 | <refsect1> | ||
| 84 | &return-value; | ||
| 85 | </refsect1> | ||
| 86 | </refentry> | ||
| @@ -0,0 +1,202 @@ | |||
| 1 | <refentry id="cec-ioc-g-event"> | ||
| 2 | <refmeta> | ||
| 3 | <refentrytitle>ioctl CEC_DQEVENT</refentrytitle> | ||
| 4 | &manvol; | ||
| 5 | </refmeta> | ||
| 6 | |||
| 7 | <refnamediv> | ||
| 8 | <refname>CEC_DQEVENT</refname> | ||
| 9 | <refpurpose>Dequeue a CEC event</refpurpose> | ||
| 10 | </refnamediv> | ||
| 11 | |||
| 12 | <refsynopsisdiv> | ||
| 13 | <funcsynopsis> | ||
| 14 | <funcprototype> | ||
| 15 | <funcdef>int <function>ioctl</function></funcdef> | ||
| 16 | <paramdef>int <parameter>fd</parameter></paramdef> | ||
| 17 | <paramdef>int <parameter>request</parameter></paramdef> | ||
| 18 | <paramdef>struct cec_event *<parameter>argp</parameter></paramdef> | ||
| 19 | </funcprototype> | ||
| 20 | </funcsynopsis> | ||
| 21 | </refsynopsisdiv> | ||
| 22 | |||
| 23 | <refsect1> | ||
| 24 | <title>Arguments</title> | ||
| 25 | |||
| 26 | <variablelist> | ||
| 27 | <varlistentry> | ||
| 28 | <term><parameter>fd</parameter></term> | ||
| 29 | <listitem> | ||
| 30 | <para>File descriptor returned by | ||
| 31 | <link linkend='cec-func-open'><function>open()</function></link>.</para> | ||
| 32 | </listitem> | ||
| 33 | </varlistentry> | ||
| 34 | <varlistentry> | ||
| 35 | <term><parameter>request</parameter></term> | ||
| 36 | <listitem> | ||
| 37 | <para>CEC_DQEVENT</para> | ||
| 38 | </listitem> | ||
| 39 | </varlistentry> | ||
| 40 | <varlistentry> | ||
| 41 | <term><parameter>argp</parameter></term> | ||
| 42 | <listitem> | ||
| 43 | <para></para> | ||
| 44 | </listitem> | ||
| 45 | </varlistentry> | ||
| 46 | </variablelist> | ||
| 47 | </refsect1> | ||
| 48 | |||
| 49 | <refsect1> | ||
| 50 | <title>Description</title> | ||
| 51 | |||
| 52 | <para> | ||
| 53 | Note: this documents the proposed CEC API. This API is not yet finalized and | ||
| 54 | is currently only available as a staging kernel module. | ||
| 55 | </para> | ||
| 56 | |||
| 57 | <para>CEC devices can send asynchronous events. These can be retrieved by calling | ||
| 58 | the <constant>CEC_DQEVENT</constant> ioctl. If the file descriptor is in non-blocking | ||
| 59 | mode and no event is pending, then it will return -1 and set errno to the &EAGAIN;.</para> | ||
| 60 | |||
| 61 | <para>The internal event queues are per-filehandle and per-event type. If there is | ||
| 62 | no more room in a queue then the last event is overwritten with the new one. This | ||
| 63 | means that intermediate results can be thrown away but that the latest event is always | ||
| 64 | available. This also means that is it possible to read two successive events that have | ||
| 65 | the same value (e.g. two CEC_EVENT_STATE_CHANGE events with the same state). In that | ||
| 66 | case the intermediate state changes were lost but it is guaranteed that the state | ||
| 67 | did change in between the two events.</para> | ||
| 68 | |||
| 69 | <table pgwide="1" frame="none" id="cec-event-state-change"> | ||
| 70 | <title>struct <structname>cec_event_state_change</structname></title> | ||
| 71 | <tgroup cols="3"> | ||
| 72 | &cs-str; | ||
| 73 | <tbody valign="top"> | ||
| 74 | <row> | ||
| 75 | <entry>__u16</entry> | ||
| 76 | <entry><structfield>phys_addr</structfield></entry> | ||
| 77 | <entry>The current physical address.</entry> | ||
| 78 | </row> | ||
| 79 | <row> | ||
| 80 | <entry>__u16</entry> | ||
| 81 | <entry><structfield>log_addr_mask</structfield></entry> | ||
| 82 | <entry>The current set of claimed logical addresses.</entry> | ||
| 83 | </row> | ||
| 84 | </tbody> | ||
| 85 | </tgroup> | ||
| 86 | </table> | ||
| 87 | |||
| 88 | <table pgwide="1" frame="none" id="cec-event-lost-msgs"> | ||
| 89 | <title>struct <structname>cec_event_lost_msgs</structname></title> | ||
| 90 | <tgroup cols="3"> | ||
| 91 | &cs-str; | ||
| 92 | <tbody valign="top"> | ||
| 93 | <row> | ||
| 94 | <entry>__u32</entry> | ||
| 95 | <entry><structfield>lost_msgs</structfield></entry> | ||
| 96 | <entry>Set to the number of lost messages since the filehandle | ||
| 97 | was opened or since the last time this event was dequeued for | ||
| 98 | this filehandle. The messages lost are the oldest messages. So | ||
| 99 | when a new message arrives and there is no more room, then the | ||
| 100 | oldest message is discarded to make room for the new one. The | ||
| 101 | internal size of the message queue guarantees that all messages | ||
| 102 | received in the last two seconds will be stored. Since messages | ||
| 103 | should be replied to within a second according to the CEC | ||
| 104 | specification, this is more than enough. | ||
| 105 | </entry> | ||
| 106 | </row> | ||
| 107 | </tbody> | ||
| 108 | </tgroup> | ||
| 109 | </table> | ||
| 110 | |||
| 111 | <table pgwide="1" frame="none" id="cec-event"> | ||
| 112 | <title>struct <structname>cec_event</structname></title> | ||
| 113 | <tgroup cols="4"> | ||
| 114 | &cs-str; | ||
| 115 | <tbody valign="top"> | ||
| 116 | <row> | ||
| 117 | <entry>__u64</entry> | ||
| 118 | <entry><structfield>ts</structfield></entry> | ||
| 119 | <entry>Timestamp of the event in ns.</entry> | ||
| 120 | <entry></entry> | ||
| 121 | </row> | ||
| 122 | <row> | ||
| 123 | <entry>__u32</entry> | ||
| 124 | <entry><structfield>event</structfield></entry> | ||
| 125 | <entry>The CEC event type, see <xref linkend="cec-events" />.</entry> | ||
| 126 | <entry></entry> | ||
| 127 | </row> | ||
| 128 | <row> | ||
| 129 | <entry>__u32</entry> | ||
| 130 | <entry><structfield>flags</structfield></entry> | ||
| 131 | <entry>Event flags, see <xref linkend="cec-event-flags" />.</entry> | ||
| 132 | <entry></entry> | ||
| 133 | </row> | ||
| 134 | <row> | ||
| 135 | <entry>union</entry> | ||
| 136 | <entry>(anonymous)</entry> | ||
| 137 | <entry></entry> | ||
| 138 | <entry></entry> | ||
| 139 | </row> | ||
| 140 | <row> | ||
| 141 | <entry></entry> | ||
| 142 | <entry>struct cec_event_state_change</entry> | ||
| 143 | <entry><structfield>state_change</structfield></entry> | ||
| 144 | <entry>The new adapter state as sent by the <constant>CEC_EVENT_STATE_CHANGE</constant> | ||
| 145 | event.</entry> | ||
| 146 | </row> | ||
| 147 | <row> | ||
| 148 | <entry></entry> | ||
| 149 | <entry>struct cec_event_lost_msgs</entry> | ||
| 150 | <entry><structfield>lost_msgs</structfield></entry> | ||
| 151 | <entry>The number of lost messages as sent by the <constant>CEC_EVENT_LOST_MSGS</constant> | ||
| 152 | event.</entry> | ||
| 153 | </row> | ||
| 154 | </tbody> | ||
| 155 | </tgroup> | ||
| 156 | </table> | ||
| 157 | |||
| 158 | <table pgwide="1" frame="none" id="cec-events"> | ||
| 159 | <title>CEC Events Types</title> | ||
| 160 | <tgroup cols="3"> | ||
| 161 | &cs-def; | ||
| 162 | <tbody valign="top"> | ||
| 163 | <row> | ||
| 164 | <entry><constant>CEC_EVENT_STATE_CHANGE</constant></entry> | ||
| 165 | <entry>1</entry> | ||
| 166 | <entry>Generated when the CEC Adapter's state changes. When open() is | ||
| 167 | called an initial event will be generated for that filehandle with the | ||
| 168 | CEC Adapter's state at that time. | ||
| 169 | </entry> | ||
| 170 | </row> | ||
| 171 | <row> | ||
| 172 | <entry><constant>CEC_EVENT_LOST_MSGS</constant></entry> | ||
| 173 | <entry>2</entry> | ||
| 174 | <entry>Generated if one or more CEC messages were lost because the | ||
| 175 | application didn't dequeue CEC messages fast enough.</entry> | ||
| 176 | </row> | ||
| 177 | </tbody> | ||
| 178 | </tgroup> | ||
| 179 | </table> | ||
| 180 | |||
| 181 | <table pgwide="1" frame="none" id="cec-event-flags"> | ||
| 182 | <title>CEC Event Flags</title> | ||
| 183 | <tgroup cols="3"> | ||
| 184 | &cs-def; | ||
| 185 | <tbody valign="top"> | ||
| 186 | <row> | ||
| 187 | <entry><constant>CEC_EVENT_FL_INITIAL_VALUE</constant></entry> | ||
| 188 | <entry>1</entry> | ||
| 189 | <entry>Set for the initial events that are generated when the device is | ||
| 190 | opened. See the table above for which events do this. This allows | ||
| 191 | applications to learn the initial state of the CEC adapter at open() | ||
| 192 | time.</entry> | ||
| 193 | </row> | ||
| 194 | </tbody> | ||
| 195 | </tgroup> | ||
| 196 | </table> | ||
| 197 | </refsect1> | ||
| 198 | |||
| 199 | <refsect1> | ||
| 200 | &return-value; | ||
| 201 | </refsect1> | ||
| 202 | </refentry> | ||
| @@ -0,0 +1,255 @@ | |||
| 1 | <refentry id="cec-ioc-g-mode"> | ||
| 2 | <refmeta> | ||
| 3 | <refentrytitle>ioctl CEC_G_MODE, CEC_S_MODE</refentrytitle> | ||
| 4 | &manvol; | ||
| 5 | </refmeta> | ||
| 6 | |||
| 7 | <refnamediv> | ||
| 8 | <refname>CEC_G_MODE</refname> | ||
| 9 | <refname>CEC_S_MODE</refname> | ||
| 10 | <refpurpose>Get or set exclusive use of the CEC adapter</refpurpose> | ||
| 11 | </refnamediv> | ||
| 12 | |||
| 13 | <refsynopsisdiv> | ||
| 14 | <funcsynopsis> | ||
| 15 | <funcprototype> | ||
| 16 | <funcdef>int <function>ioctl</function></funcdef> | ||
| 17 | <paramdef>int <parameter>fd</parameter></paramdef> | ||
| 18 | <paramdef>int <parameter>request</parameter></paramdef> | ||
| 19 | <paramdef>__u32 *<parameter>argp</parameter></paramdef> | ||
| 20 | </funcprototype> | ||
| 21 | </funcsynopsis> | ||
| 22 | </refsynopsisdiv> | ||
| 23 | |||
| 24 | <refsect1> | ||
| 25 | <title>Arguments</title> | ||
| 26 | |||
| 27 | <variablelist> | ||
| 28 | <varlistentry> | ||
| 29 | <term><parameter>fd</parameter></term> | ||
| 30 | <listitem> | ||
| 31 | <para>File descriptor returned by | ||
| 32 | <link linkend='cec-func-open'><function>open()</function></link>.</para> | ||
| 33 | </listitem> | ||
| 34 | </varlistentry> | ||
| 35 | <varlistentry> | ||
| 36 | <term><parameter>request</parameter></term> | ||
| 37 | <listitem> | ||
| 38 | <para>CEC_G_MODE, CEC_S_MODE</para> | ||
| 39 | </listitem> | ||
| 40 | </varlistentry> | ||
| 41 | <varlistentry> | ||
| 42 | <term><parameter>argp</parameter></term> | ||
| 43 | <listitem> | ||
| 44 | <para></para> | ||
| 45 | </listitem> | ||
| 46 | </varlistentry> | ||
| 47 | </variablelist> | ||
| 48 | </refsect1> | ||
| 49 | |||
| 50 | <refsect1> | ||
| 51 | <title>Description</title> | ||
| 52 | |||
| 53 | <para> | ||
| 54 | Note: this documents the proposed CEC API. This API is not yet finalized and | ||
| 55 | is currently only available as a staging kernel module. | ||
| 56 | </para> | ||
| 57 | |||
| 58 | <para>By default any filehandle can use &CEC-TRANSMIT; and &CEC-RECEIVE;, but | ||
| 59 | in order to prevent applications from stepping on each others toes it must be possible | ||
| 60 | to obtain exclusive access to the CEC adapter. This ioctl sets the filehandle | ||
| 61 | to initiator and/or follower mode which can be exclusive depending on the chosen | ||
| 62 | mode. The initiator is the filehandle that is used | ||
| 63 | to initiate messages, i.e. it commands other CEC devices. The follower is the filehandle | ||
| 64 | that receives messages sent to the CEC adapter and processes them. The same filehandle | ||
| 65 | can be both initiator and follower, or this role can be taken by two different | ||
| 66 | filehandles.</para> | ||
| 67 | |||
| 68 | <para>When a CEC message is received, then the CEC framework will decide how | ||
| 69 | it will be processed. If the message is a reply to an earlier transmitted message, | ||
| 70 | then the reply is sent back to the filehandle that is waiting for it. In addition | ||
| 71 | the CEC framework will process it.</para> | ||
| 72 | |||
| 73 | <para>If the message is not a reply, then the CEC framework will process it | ||
| 74 | first. If there is no follower, then the message is just discarded and a feature | ||
| 75 | abort is sent back to the initiator if the framework couldn't process it. If there | ||
| 76 | is a follower, then the message is passed on to the follower who will use | ||
| 77 | &CEC-RECEIVE; to dequeue the new message. The framework expects the follower to | ||
| 78 | make the right decisions.</para> | ||
| 79 | |||
| 80 | <para>The CEC framework will process core messages unless requested otherwise | ||
| 81 | by the follower. The follower can enable the passthrough mode. In that case, the | ||
| 82 | CEC framework will pass on most core messages without processing them and | ||
| 83 | the follower will have to implement those messages. There are some messages | ||
| 84 | that the core will always process, regardless of the passthrough mode. See | ||
| 85 | <xref linkend="cec-core-processing" /> for details.</para> | ||
| 86 | |||
| 87 | <para>If there is no initiator, then any CEC filehandle can use &CEC-TRANSMIT;. | ||
| 88 | If there is an exclusive initiator then only that initiator can call &CEC-TRANSMIT;. | ||
| 89 | The follower can of course always call &CEC-TRANSMIT;.</para> | ||
| 90 | |||
| 91 | <para>Available initiator modes are:</para> | ||
| 92 | |||
| 93 | <table pgwide="1" frame="none" id="cec-mode-initiator"> | ||
| 94 | <title>Initiator Modes</title> | ||
| 95 | <tgroup cols="3"> | ||
| 96 | &cs-def; | ||
| 97 | <tbody valign="top"> | ||
| 98 | <row> | ||
| 99 | <entry><constant>CEC_MODE_NO_INITIATOR</constant></entry> | ||
| 100 | <entry>0x0</entry> | ||
| 101 | <entry>This is not an initiator, i.e. it cannot transmit CEC messages | ||
| 102 | or make any other changes to the CEC adapter.</entry> | ||
| 103 | </row> | ||
| 104 | <row> | ||
| 105 | <entry><constant>CEC_MODE_INITIATOR</constant></entry> | ||
| 106 | <entry>0x1</entry> | ||
| 107 | <entry>This is an initiator (the default when the device is opened) and it | ||
| 108 | can transmit CEC messages and make changes to the CEC adapter, unless there | ||
| 109 | is an exclusive initiator.</entry> | ||
| 110 | </row> | ||
| 111 | <row> | ||
| 112 | <entry><constant>CEC_MODE_EXCL_INITIATOR</constant></entry> | ||
| 113 | <entry>0x2</entry> | ||
| 114 | <entry>This is an exclusive initiator and this file descriptor is the only one | ||
| 115 | that can transmit CEC messages and make changes to the CEC adapter. If someone | ||
| 116 | else is already the exclusive initiator then an attempt to become one will return | ||
| 117 | the &EBUSY; error.</entry> | ||
| 118 | </row> | ||
| 119 | </tbody> | ||
| 120 | </tgroup> | ||
| 121 | </table> | ||
| 122 | |||
| 123 | <para>Available follower modes are:</para> | ||
| 124 | |||
| 125 | <table pgwide="1" frame="none" id="cec-mode-follower"> | ||
| 126 | <title>Follower Modes</title> | ||
| 127 | <tgroup cols="3"> | ||
| 128 | &cs-def; | ||
| 129 | <tbody valign="top"> | ||
| 130 | <row> | ||
| 131 | <entry><constant>CEC_MODE_NO_FOLLOWER</constant></entry> | ||
| 132 | <entry>0x00</entry> | ||
| 133 | <entry>This is not a follower (the default when the device is opened).</entry> | ||
| 134 | </row> | ||
| 135 | <row> | ||
| 136 | <entry><constant>CEC_MODE_FOLLOWER</constant></entry> | ||
| 137 | <entry>0x10</entry> | ||
| 138 | <entry>This is a follower and it will receive CEC messages unless there is | ||
| 139 | an exclusive follower. You cannot become a follower if <constant>CEC_CAP_TRANSMIT</constant> | ||
| 140 | is not set or if <constant>CEC_MODE_NO_INITIATOR</constant> was specified, | ||
| 141 | &EINVAL; is returned in that case.</entry> | ||
| 142 | </row> | ||
| 143 | <row> | ||
| 144 | <entry><constant>CEC_MODE_EXCL_FOLLOWER</constant></entry> | ||
| 145 | <entry>0x20</entry> | ||
| 146 | <entry>This is an exclusive follower and only this file descriptor will receive | ||
| 147 | CEC messages for processing. If someone else is already the exclusive follower | ||
| 148 | then an attempt to become one will return the &EBUSY; error. You cannot become | ||
| 149 | a follower if <constant>CEC_CAP_TRANSMIT</constant> is not set or if | ||
| 150 | <constant>CEC_MODE_NO_INITIATOR</constant> was specified, &EINVAL; is returned | ||
| 151 | in that case.</entry> | ||
| 152 | </row> | ||
| 153 | <row> | ||
| 154 | <entry><constant>CEC_MODE_EXCL_FOLLOWER_PASSTHRU</constant></entry> | ||
| 155 | <entry>0x30</entry> | ||
| 156 | <entry>This is an exclusive follower and only this file descriptor will receive | ||
| 157 | CEC messages for processing. In addition it will put the CEC device into | ||
| 158 | passthrough mode, allowing the exclusive follower to handle most core messages | ||
| 159 | instead of relying on the CEC framework for that. If someone else is already the | ||
| 160 | exclusive follower then an attempt to become one will return the &EBUSY; error. | ||
| 161 | You cannot become a follower if <constant>CEC_CAP_TRANSMIT</constant> | ||
| 162 | is not set or if <constant>CEC_MODE_NO_INITIATOR</constant> was specified, | ||
| 163 | &EINVAL; is returned in that case.</entry> | ||
| 164 | </row> | ||
| 165 | <row> | ||
| 166 | <entry><constant>CEC_MODE_MONITOR</constant></entry> | ||
| 167 | <entry>0xe0</entry> | ||
| 168 | <entry>Put the file descriptor into monitor mode. Can only be used in combination | ||
| 169 | with <constant>CEC_MODE_NO_INITIATOR</constant>, otherwise &EINVAL; will be | ||
| 170 | returned. In monitor mode all messages this CEC device transmits and all messages | ||
| 171 | it receives (both broadcast messages and directed messages for one its logical | ||
| 172 | addresses) will be reported. This is very useful for debugging. This is only | ||
| 173 | allowed if the process has the <constant>CAP_NET_ADMIN</constant> | ||
| 174 | capability. If that is not set, then &EPERM; is returned.</entry> | ||
| 175 | </row> | ||
| 176 | <row> | ||
| 177 | <entry><constant>CEC_MODE_MONITOR_ALL</constant></entry> | ||
| 178 | <entry>0xf0</entry> | ||
| 179 | <entry>Put the file descriptor into 'monitor all' mode. Can only be used in combination | ||
| 180 | with <constant>CEC_MODE_NO_INITIATOR</constant>, otherwise &EINVAL; will be | ||
| 181 | returned. In 'monitor all' mode all messages this CEC device transmits and all messages | ||
| 182 | it receives, including directed messages for other CEC devices will be reported. This | ||
| 183 | is very useful for debugging, but not all devices support this. This mode requires that | ||
| 184 | the <constant>CEC_CAP_MONITOR_ALL</constant> capability is set, otherwise &EINVAL; is | ||
| 185 | returned. This is only allowed if the process has the <constant>CAP_NET_ADMIN</constant> | ||
| 186 | capability. If that is not set, then &EPERM; is returned.</entry> | ||
| 187 | </row> | ||
| 188 | </tbody> | ||
| 189 | </tgroup> | ||
| 190 | </table> | ||
| 191 | |||
| 192 | <para>Core message processing details:</para> | ||
| 193 | |||
| 194 | <table pgwide="1" frame="none" id="cec-core-processing"> | ||
| 195 | <title>Core Message Processing</title> | ||
| 196 | <tgroup cols="2"> | ||
| 197 | &cs-def; | ||
| 198 | <tbody valign="top"> | ||
| 199 | <row> | ||
| 200 | <entry><constant>CEC_MSG_GET_CEC_VERSION</constant></entry> | ||
| 201 | <entry>When in passthrough mode this message has to be handled by userspace, | ||
| 202 | otherwise the core will return the CEC version that was set with &CEC-ADAP-S-LOG-ADDRS;.</entry> | ||
| 203 | </row> | ||
| 204 | <row> | ||
| 205 | <entry><constant>CEC_MSG_GIVE_DEVICE_VENDOR_ID</constant></entry> | ||
| 206 | <entry>When in passthrough mode this message has to be handled by userspace, | ||
| 207 | otherwise the core will return the vendor ID that was set with &CEC-ADAP-S-LOG-ADDRS;.</entry> | ||
| 208 | </row> | ||
| 209 | <row> | ||
| 210 | <entry><constant>CEC_MSG_ABORT</constant></entry> | ||
| 211 | <entry>When in passthrough mode this message has to be handled by userspace, | ||
| 212 | otherwise the core will return a feature refused message as per the specification.</entry> | ||
| 213 | </row> | ||
| 214 | <row> | ||
| 215 | <entry><constant>CEC_MSG_GIVE_PHYSICAL_ADDR</constant></entry> | ||
| 216 | <entry>When in passthrough mode this message has to be handled by userspace, | ||
| 217 | otherwise the core will report the current physical address.</entry> | ||
| 218 | </row> | ||
| 219 | <row> | ||
| 220 | <entry><constant>CEC_MSG_GIVE_OSD_NAME</constant></entry> | ||
| 221 | <entry>When in passthrough mode this message has to be handled by userspace, | ||
| 222 | otherwise the core will report the current OSD name as was set with | ||
| 223 | &CEC-ADAP-S-LOG-ADDRS;.</entry> | ||
| 224 | </row> | ||
| 225 | <row> | ||
| 226 | <entry><constant>CEC_MSG_GIVE_FEATURES</constant></entry> | ||
| 227 | <entry>When in passthrough mode this message has to be handled by userspace, | ||
| 228 | otherwise the core will report the current features as was set with | ||
| 229 | &CEC-ADAP-S-LOG-ADDRS; or the message is ignore if the CEC version was | ||
| 230 | older than 2.0.</entry> | ||
| 231 | </row> | ||
| 232 | <row> | ||
| 233 | <entry><constant>CEC_MSG_USER_CONTROL_PRESSED</constant></entry> | ||
| 234 | <entry>If <constant>CEC_CAP_RC</constant> is set, then generate a remote control | ||
| 235 | key press. This message is always passed on to userspace.</entry> | ||
| 236 | </row> | ||
| 237 | <row> | ||
| 238 | <entry><constant>CEC_MSG_USER_CONTROL_RELEASED</constant></entry> | ||
| 239 | <entry>If <constant>CEC_CAP_RC</constant> is set, then generate a remote control | ||
| 240 | key release. This message is always passed on to userspace.</entry> | ||
| 241 | </row> | ||
| 242 | <row> | ||
| 243 | <entry><constant>CEC_MSG_REPORT_PHYSICAL_ADDR</constant></entry> | ||
| 244 | <entry>The CEC framework will make note of the reported physical address | ||
| 245 | and then just pass the message on to userspace.</entry> | ||
| 246 | </row> | ||
| 247 | </tbody> | ||
| 248 | </tgroup> | ||
| 249 | </table> | ||
| 250 | </refsect1> | ||
| 251 | |||
| 252 | <refsect1> | ||
| 253 | &return-value; | ||
| 254 | </refsect1> | ||
| 255 | </refentry> | ||
| @@ -0,0 +1,274 @@ | |||
| 1 | <refentry id="cec-ioc-receive"> | ||
| 2 | <refmeta> | ||
| 3 | <refentrytitle>ioctl CEC_RECEIVE, CEC_TRANSMIT</refentrytitle> | ||
| 4 | &manvol; | ||
| 5 | </refmeta> | ||
| 6 | |||
| 7 | <refnamediv> | ||
| 8 | <refname>CEC_RECEIVE</refname> | ||
| 9 | <refname>CEC_TRANSMIT</refname> | ||
| 10 | <refpurpose>Receive or transmit a CEC message</refpurpose> | ||
| 11 | </refnamediv> | ||
| 12 | |||
| 13 | <refsynopsisdiv> | ||
| 14 | <funcsynopsis> | ||
| 15 | <funcprototype> | ||
| 16 | <funcdef>int <function>ioctl</function></funcdef> | ||
| 17 | <paramdef>int <parameter>fd</parameter></paramdef> | ||
| 18 | <paramdef>int <parameter>request</parameter></paramdef> | ||
| 19 | <paramdef>struct cec_msg *<parameter>argp</parameter></paramdef> | ||
| 20 | </funcprototype> | ||
| 21 | </funcsynopsis> | ||
| 22 | </refsynopsisdiv> | ||
| 23 | |||
| 24 | <refsect1> | ||
| 25 | <title>Arguments</title> | ||
| 26 | |||
| 27 | <variablelist> | ||
| 28 | <varlistentry> | ||
| 29 | <term><parameter>fd</parameter></term> | ||
| 30 | <listitem> | ||
| 31 | <para>File descriptor returned by | ||
| 32 | <link linkend='cec-func-open'><function>open()</function></link>.</para> | ||
| 33 | </listitem> | ||
| 34 | </varlistentry> | ||
| 35 | <varlistentry> | ||
| 36 | <term><parameter>request</parameter></term> | ||
| 37 | <listitem> | ||
| 38 | <para>CEC_RECEIVE, CEC_TRANSMIT</para> | ||
| 39 | </listitem> | ||
| 40 | </varlistentry> | ||
| 41 | <varlistentry> | ||
| 42 | <term><parameter>argp</parameter></term> | ||
| 43 | <listitem> | ||
| 44 | <para></para> | ||
| 45 | </listitem> | ||
| 46 | </varlistentry> | ||
| 47 | </variablelist> | ||
| 48 | </refsect1> | ||
| 49 | |||
| 50 | <refsect1> | ||
| 51 | <title>Description</title> | ||
| 52 | |||
| 53 | <para> | ||
| 54 | Note: this documents the proposed CEC API. This API is not yet finalized and | ||
| 55 | is currently only available as a staging kernel module. | ||
| 56 | </para> | ||
| 57 | |||
| 58 | <para>To receive a CEC message the application has to fill in the | ||
| 59 | <structname>cec_msg</structname> structure and pass it to the | ||
| 60 | <constant>CEC_RECEIVE</constant> ioctl. <constant>CEC_RECEIVE</constant> is | ||
| 61 | only available if <constant>CEC_CAP_RECEIVE</constant> is set. If the | ||
| 62 | file descriptor is in non-blocking mode and there are no received | ||
| 63 | messages pending, then it will return -1 and set errno to the &EAGAIN;. | ||
| 64 | If the file descriptor is in blocking mode and <structfield>timeout</structfield> | ||
| 65 | is non-zero and no message arrived within <structfield>timeout</structfield> | ||
| 66 | milliseconds, then it will return -1 and set errno to the &ETIMEDOUT;.</para> | ||
| 67 | |||
| 68 | <para>To send a CEC message the application has to fill in the | ||
| 69 | <structname>cec_msg</structname> structure and pass it to the | ||
| 70 | <constant>CEC_TRANSMIT</constant> ioctl. <constant>CEC_TRANSMIT</constant> is | ||
| 71 | only available if <constant>CEC_CAP_TRANSMIT</constant> is set. | ||
| 72 | If there is no more room in the transmit queue, then it will return | ||
| 73 | -1 and set errno to the &EBUSY;.</para> | ||
| 74 | |||
| 75 | <table pgwide="1" frame="none" id="cec-msg"> | ||
| 76 | <title>struct <structname>cec_msg</structname></title> | ||
| 77 | <tgroup cols="3"> | ||
| 78 | &cs-str; | ||
| 79 | <tbody valign="top"> | ||
| 80 | <row> | ||
| 81 | <entry>__u64</entry> | ||
| 82 | <entry><structfield>ts</structfield></entry> | ||
| 83 | <entry>Timestamp of when the message was transmitted in ns in the case | ||
| 84 | of <constant>CEC_TRANSMIT</constant> with <structfield>reply</structfield> | ||
| 85 | set to 0, or the timestamp of the received message in all other cases.</entry> | ||
| 86 | </row> | ||
| 87 | <row> | ||
| 88 | <entry>__u32</entry> | ||
| 89 | <entry><structfield>len</structfield></entry> | ||
| 90 | <entry>The length of the message. For <constant>CEC_TRANSMIT</constant> this | ||
| 91 | is filled in by the application. The driver will fill this in for | ||
| 92 | <constant>CEC_RECEIVE</constant> and for <constant>CEC_TRANSMIT</constant> | ||
| 93 | it will be filled in with the length of the reply message if | ||
| 94 | <structfield>reply</structfield> was set.</entry> | ||
| 95 | </row> | ||
| 96 | <row> | ||
| 97 | <entry>__u32</entry> | ||
| 98 | <entry><structfield>timeout</structfield></entry> | ||
| 99 | <entry>The timeout in milliseconds. This is the time the device will wait for a message to | ||
| 100 | be received before timing out. If it is set to 0, then it will wait indefinitely when it | ||
| 101 | is called by <constant>CEC_RECEIVE</constant>. If it is 0 and it is called by | ||
| 102 | <constant>CEC_TRANSMIT</constant>, then it will be replaced by 1000 if the | ||
| 103 | <structfield>reply</structfield> is non-zero or ignored if <structfield>reply</structfield> | ||
| 104 | is 0.</entry> | ||
| 105 | </row> | ||
| 106 | <row> | ||
| 107 | <entry>__u32</entry> | ||
| 108 | <entry><structfield>sequence</structfield></entry> | ||
| 109 | <entry>The sequence number is automatically assigned by the CEC | ||
| 110 | framework for all transmitted messages. It can be later used by the | ||
| 111 | framework to generate an event if a reply for a message was | ||
| 112 | requested and the message was transmitted in a non-blocking mode. | ||
| 113 | </entry> | ||
| 114 | </row> | ||
| 115 | <row> | ||
| 116 | <entry>__u32</entry> | ||
| 117 | <entry><structfield>flags</structfield></entry> | ||
| 118 | <entry>Flags. No flags are defined yet, so set this to 0.</entry> | ||
| 119 | </row> | ||
| 120 | <row> | ||
| 121 | <entry>__u8</entry> | ||
| 122 | <entry><structfield>rx_status</structfield></entry> | ||
| 123 | <entry>The status bits of the received message. See <xref linkend="cec-rx-status" /> | ||
| 124 | for the possible status values. It is 0 if this message was transmitted, not | ||
| 125 | received, unless this is the reply to a transmitted message. In that case both | ||
| 126 | <structfield>rx_status</structfield> and <structfield>tx_status</structfield> | ||
| 127 | are set.</entry> | ||
| 128 | </row> | ||
| 129 | <row> | ||
| 130 | <entry>__u8</entry> | ||
| 131 | <entry><structfield>tx_status</structfield></entry> | ||
| 132 | <entry>The status bits of the transmitted message. See <xref linkend="cec-tx-status" /> | ||
| 133 | for the possible status values. It is 0 if this messages was received, not | ||
| 134 | transmitted.</entry> | ||
| 135 | </row> | ||
| 136 | <row> | ||
| 137 | <entry>__u8</entry> | ||
| 138 | <entry><structfield>msg</structfield>[16]</entry> | ||
| 139 | <entry>The message payload. For <constant>CEC_TRANSMIT</constant> this | ||
| 140 | is filled in by the application. The driver will fill this in for | ||
| 141 | <constant>CEC_RECEIVE</constant> and for <constant>CEC_TRANSMIT</constant> | ||
| 142 | it will be filled in with the payload of the reply message if | ||
| 143 | <structfield>reply</structfield> was set.</entry> | ||
| 144 | </row> | ||
| 145 | <row> | ||
| 146 | <entry>__u8</entry> | ||
| 147 | <entry><structfield>reply</structfield></entry> | ||
| 148 | <entry>Wait until this message is replied. If <structfield>reply</structfield> | ||
| 149 | is 0 and the <structfield>timeout</structfield> is 0, then don't wait for a reply but | ||
| 150 | return after transmitting the message. If there was an error as indicated by a non-zero | ||
| 151 | <structfield>tx_status</structfield> field, then <structfield>reply</structfield> and | ||
| 152 | <structfield>timeout</structfield> are both set to 0 by the driver. Ignored by | ||
| 153 | <constant>CEC_RECEIVE</constant>. The case where <structfield>reply</structfield> is 0 | ||
| 154 | (this is the opcode for the Feature Abort message) and <structfield>timeout</structfield> | ||
| 155 | is non-zero is specifically allowed to send a message and wait up to <structfield>timeout</structfield> | ||
| 156 | milliseconds for a Feature Abort reply. In this case <structfield>rx_status</structfield> | ||
| 157 | will either be set to <constant>CEC_RX_STATUS_TIMEOUT</constant> or | ||
| 158 | <constant>CEC_RX_STATUS_FEATURE_ABORT</constant>.</entry> | ||
| 159 | </row> | ||
| 160 | <row> | ||
| 161 | <entry>__u8</entry> | ||
| 162 | <entry><structfield>tx_arb_lost_cnt</structfield></entry> | ||
| 163 | <entry>A counter of the number of transmit attempts that resulted in the | ||
| 164 | Arbitration Lost error. This is only set if the hardware supports this, otherwise | ||
| 165 | it is always 0. This counter is only valid if the <constant>CEC_TX_STATUS_ARB_LOST</constant> | ||
| 166 | status bit is set.</entry> | ||
| 167 | </row> | ||
| 168 | <row> | ||
| 169 | <entry>__u8</entry> | ||
| 170 | <entry><structfield>tx_nack_cnt</structfield></entry> | ||
| 171 | <entry>A counter of the number of transmit attempts that resulted in the | ||
| 172 | Not Acknowledged error. This is only set if the hardware supports this, otherwise | ||
| 173 | it is always 0. This counter is only valid if the <constant>CEC_TX_STATUS_NACK</constant> | ||
| 174 | status bit is set.</entry> | ||
| 175 | </row> | ||
| 176 | <row> | ||
| 177 | <entry>__u8</entry> | ||
| 178 | <entry><structfield>tx_low_drive_cnt</structfield></entry> | ||
| 179 | <entry>A counter of the number of transmit attempts that resulted in the | ||
| 180 | Arbitration Lost error. This is only set if the hardware supports this, otherwise | ||
| 181 | it is always 0. This counter is only valid if the <constant>CEC_TX_STATUS_LOW_DRIVE</constant> | ||
| 182 | status bit is set.</entry> | ||
| 183 | </row> | ||
| 184 | <row> | ||
| 185 | <entry>__u8</entry> | ||
| 186 | <entry><structfield>tx_error_cnt</structfield></entry> | ||
| 187 | <entry>A counter of the number of transmit errors other than Arbitration Lost | ||
| 188 | or Not Acknowledged. This is only set if the hardware supports this, otherwise | ||
| 189 | it is always 0. This counter is only valid if the <constant>CEC_TX_STATUS_ERROR</constant> | ||
| 190 | status bit is set.</entry> | ||
| 191 | </row> | ||
| 192 | </tbody> | ||
| 193 | </tgroup> | ||
| 194 | </table> | ||
| 195 | |||
| 196 | <table pgwide="1" frame="none" id="cec-tx-status"> | ||
| 197 | <title>CEC Transmit Status</title> | ||
| 198 | <tgroup cols="3"> | ||
| 199 | &cs-def; | ||
| 200 | <tbody valign="top"> | ||
| 201 | <row> | ||
| 202 | <entry><constant>CEC_TX_STATUS_OK</constant></entry> | ||
| 203 | <entry>0x01</entry> | ||
| 204 | <entry>The message was transmitted successfully. This is mutually exclusive with | ||
| 205 | <constant>CEC_TX_STATUS_MAX_RETRIES</constant>. Other bits can still be set if | ||
| 206 | earlier attempts met with failure before the transmit was eventually successful.</entry> | ||
| 207 | </row> | ||
| 208 | <row> | ||
| 209 | <entry><constant>CEC_TX_STATUS_ARB_LOST</constant></entry> | ||
| 210 | <entry>0x02</entry> | ||
| 211 | <entry>CEC line arbitration was lost.</entry> | ||
| 212 | </row> | ||
| 213 | <row> | ||
| 214 | <entry><constant>CEC_TX_STATUS_NACK</constant></entry> | ||
| 215 | <entry>0x04</entry> | ||
| 216 | <entry>Message was not acknowledged.</entry> | ||
| 217 | </row> | ||
| 218 | <row> | ||
| 219 | <entry><constant>CEC_TX_STATUS_LOW_DRIVE</constant></entry> | ||
| 220 | <entry>0x08</entry> | ||
| 221 | <entry>Low drive was detected on the CEC bus. This indicates that a follower | ||
| 222 | detected an error on the bus and requests a retransmission.</entry> | ||
| 223 | </row> | ||
| 224 | <row> | ||
| 225 | <entry><constant>CEC_TX_STATUS_ERROR</constant></entry> | ||
| 226 | <entry>0x10</entry> | ||
| 227 | <entry>Some error occurred. This is used for any errors that do not | ||
| 228 | fit the previous two, either because the hardware could not tell | ||
| 229 | which error occurred, or because the hardware tested for other conditions | ||
| 230 | besides those two.</entry> | ||
| 231 | </row> | ||
| 232 | <row> | ||
| 233 | <entry><constant>CEC_TX_STATUS_MAX_RETRIES</constant></entry> | ||
| 234 | <entry>0x20</entry> | ||
| 235 | <entry>The transmit failed after one or more retries. This status bit is mutually | ||
| 236 | exclusive with <constant>CEC_TX_STATUS_OK</constant>. Other bits can still be set | ||
| 237 | to explain which failures were seen.</entry> | ||
| 238 | </row> | ||
| 239 | </tbody> | ||
| 240 | </tgroup> | ||
| 241 | </table> | ||
| 242 | |||
| 243 | <table pgwide="1" frame="none" id="cec-rx-status"> | ||
| 244 | <title>CEC Receive Status</title> | ||
| 245 | <tgroup cols="3"> | ||
| 246 | &cs-def; | ||
| 247 | <tbody valign="top"> | ||
| 248 | <row> | ||
| 249 | <entry><constant>CEC_RX_STATUS_OK</constant></entry> | ||
| 250 | <entry>0x01</entry> | ||
| 251 | <entry>The message was received successfully.</entry> | ||
| 252 | </row> | ||
| 253 | <row> | ||
| 254 | <entry><constant>CEC_RX_STATUS_TIMEOUT</constant></entry> | ||
| 255 | <entry>0x02</entry> | ||
| 256 | <entry>The reply to an earlier transmitted message timed out.</entry> | ||
| 257 | </row> | ||
| 258 | <row> | ||
| 259 | <entry><constant>CEC_RX_STATUS_FEATURE_ABORT</constant></entry> | ||
| 260 | <entry>0x04</entry> | ||
| 261 | <entry>The message was received successfully but the reply was | ||
| 262 | <constant>CEC_MSG_FEATURE_ABORT</constant>. This status is only | ||
| 263 | set if this message was the reply to an earlier transmitted | ||
| 264 | message.</entry> | ||
| 265 | </row> | ||
| 266 | </tbody> | ||
| 267 | </tgroup> | ||
| 268 | </table> | ||
| 269 | </refsect1> | ||
| 270 | |||
| 271 | <refsect1> | ||
| 272 | &return-value; | ||
| 273 | </refsect1> | ||
| 274 | </refentry> | ||
| @@ -75,7 +75,7 @@ · 74 unmodified lines | |||
| 75 | </mediaobject> | 75 | </mediaobject> |
| 76 | </figure> | 76 | </figure> |
| 77 | <para>The media infrastructure API was designed to control such | 77 | <para>The media infrastructure API was designed to control such |
| 78 | devices. It is divided into four parts.</para> | 78 | devices. It is divided into five parts.</para> |
| 79 | <para>The first part covers radio, video capture and output, | 79 | <para>The first part covers radio, video capture and output, |
| 80 | cameras, analog TV devices and codecs.</para> | 80 | cameras, analog TV devices and codecs.</para> |
| 81 | <para>The second part covers the | 81 | <para>The second part covers the |
| @@ -87,6 +87,7 @@ · 5 unmodified lines | |||
| 87 | <xref linkend="fe-delivery-system-t" />.</para> | 87 | <xref linkend="fe-delivery-system-t" />.</para> |
| 88 | <para>The third part covers the Remote Controller API.</para> | 88 | <para>The third part covers the Remote Controller API.</para> |
| 89 | <para>The fourth part covers the Media Controller API.</para> | 89 | <para>The fourth part covers the Media Controller API.</para> |
| 90 | <para>The fifth part covers the CEC (Consumer Electronics Control) API.</para> | ||
| 90 | <para>It should also be noted that a media device may also have audio | 91 | <para>It should also be noted that a media device may also have audio |
| 91 | components, like mixers, PCM capture, PCM playback, etc, which | 92 | components, like mixers, PCM capture, PCM playback, etc, which |
| 92 | are controlled via ALSA API.</para> | 93 | are controlled via ALSA API.</para> |
| @@ -107,6 +108,9 @@ · 14 unmodified lines | |||
| 107 | <part id="media_common"> | 108 | <part id="media_common"> |
| 108 | &sub-media-controller; | 109 | &sub-media-controller; |
| 109 | </part> | 110 | </part> |
| 111 | <part id="cec"> | ||
| 112 | &sub-cec-api; | ||
| 113 | </part> | ||
| 110 | 114 | ||
| 111 | <chapter id="gen_errors"> | 115 | <chapter id="gen_errors"> |
| 112 | &sub-gen-errors; | 116 | &sub-gen-errors; |
| @@ -0,0 +1,267 @@ | |||
| 1 | CEC Kernel Support | ||
| 2 | ================== | ||
| 3 | |||
| 4 | The CEC framework provides a unified kernel interface for use with HDMI CEC | ||
| 5 | hardware. It is designed to handle a multiple types of hardware (receivers, | ||
| 6 | transmitters, USB dongles). The framework also gives the option to decide | ||
| 7 | what to do in the kernel driver and what should be handled by userspace | ||
| 8 | applications. In addition it integrates the remote control passthrough | ||
| 9 | feature into the kernel's remote control framework. | ||
| 10 | |||
| 11 | |||
| 12 | The CEC Protocol | ||
| 13 | ---------------- | ||
| 14 | |||
| 15 | The CEC protocol enables consumer electronic devices to communicate with each | ||
| 16 | other through the HDMI connection. The protocol uses logical addresses in the | ||
| 17 | communication. The logical address is strictly connected with the functionality | ||
| 18 | provided by the device. The TV acting as the communication hub is always | ||
| 19 | assigned address 0. The physical address is determined by the physical | ||
| 20 | connection between devices. | ||
| 21 | |||
| 22 | The CEC framework described here is up to date with the CEC 2.0 specification. | ||
| 23 | It is documented in the HDMI 1.4 specification with the new 2.0 bits documented | ||
| 24 | in the HDMI 2.0 specification. But for most of the features the freely available | ||
| 25 | HDMI 1.3a specification is sufficient: | ||
| 26 | |||
| 27 | http://www.microprocessor.org/HDMISpecification13a.pdf | ||
| 28 | |||
| 29 | |||
| 30 | The Kernel Interface | ||
| 31 | ==================== | ||
| 32 | |||
| 33 | CEC Adapter | ||
| 34 | ----------- | ||
| 35 | |||
| 36 | The struct cec_adapter represents the CEC adapter hardware. It is created by | ||
| 37 | calling cec_allocate_adapter() and deleted by calling cec_delete_adapter(): | ||
| 38 | |||
| 39 | struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, | ||
| 40 | void *priv, const char *name, u32 caps, u8 available_las, | ||
| 41 | struct device *parent); | ||
| 42 | void cec_delete_adapter(struct cec_adapter *adap); | ||
| 43 | |||
| 44 | To create an adapter you need to pass the following information: | ||
| 45 | |||
| 46 | ops: adapter operations which are called by the CEC framework and that you | ||
| 47 | have to implement. | ||
| 48 | |||
| 49 | priv: will be stored in adap->priv and can be used by the adapter ops. | ||
| 50 | |||
| 51 | name: the name of the CEC adapter. Note: this name will be copied. | ||
| 52 | |||
| 53 | caps: capabilities of the CEC adapter. These capabilities determine the | ||
| 54 | capabilities of the hardware and which parts are to be handled | ||
| 55 | by userspace and which parts are handled by kernelspace. The | ||
| 56 | capabilities are returned by CEC_ADAP_G_CAPS. | ||
| 57 | |||
| 58 | available_las: the number of simultaneous logical addresses that this | ||
| 59 | adapter can handle. Must be 1 <= available_las <= CEC_MAX_LOG_ADDRS. | ||
| 60 | |||
| 61 | parent: the parent device. | ||
| 62 | |||
| 63 | |||
| 64 | To register the /dev/cecX device node and the remote control device (if | ||
| 65 | CEC_CAP_RC is set) you call: | ||
| 66 | |||
| 67 | int cec_register_adapter(struct cec_adapter *adap); | ||
| 68 | |||
| 69 | To unregister the devices call: | ||
| 70 | |||
| 71 | void cec_unregister_adapter(struct cec_adapter *adap); | ||
| 72 | |||
| 73 | Note: if cec_register_adapter() fails, then call cec_delete_adapter() to | ||
| 74 | clean up. But if cec_register_adapter() succeeded, then only call | ||
| 75 | cec_unregister_adapter() to clean up, never cec_delete_adapter(). The | ||
| 76 | unregister function will delete the adapter automatically once the last user | ||
| 77 | of that /dev/cecX device has closed its file handle. | ||
| 78 | |||
| 79 | |||
| 80 | Implementing the Low-Level CEC Adapter | ||
| 81 | -------------------------------------- | ||
| 82 | |||
| 83 | The following low-level adapter operations have to be implemented in | ||
| 84 | your driver: | ||
| 85 | |||
| 86 | struct cec_adap_ops { | ||
| 87 | /* Low-level callbacks */ | ||
| 88 | int (*adap_enable)(struct cec_adapter *adap, bool enable); | ||
| 89 | int (*adap_monitor_all_enable)(struct cec_adapter *adap, bool enable); | ||
| 90 | int (*adap_log_addr)(struct cec_adapter *adap, u8 logical_addr); | ||
| 91 | int (*adap_transmit)(struct cec_adapter *adap, u8 attempts, | ||
| 92 | u32 signal_free_time, struct cec_msg *msg); | ||
| 93 | void (*adap_log_status)(struct cec_adapter *adap); | ||
| 94 | |||
| 95 | /* High-level callbacks */ | ||
| 96 | ... | ||
| 97 | }; | ||
| 98 | |||
| 99 | The three low-level ops deal with various aspects of controlling the CEC adapter | ||
| 100 | hardware: | ||
| 101 | |||
| 102 | |||
| 103 | To enable/disable the hardware: | ||
| 104 | |||
| 105 | int (*adap_enable)(struct cec_adapter *adap, bool enable); | ||
| 106 | |||
| 107 | This callback enables or disables the CEC hardware. Enabling the CEC hardware | ||
| 108 | means powering it up in a state where no logical addresses are claimed. This | ||
| 109 | op assumes that the physical address (adap->phys_addr) is valid when enable is | ||
| 110 | true and will not change while the CEC adapter remains enabled. The initial | ||
| 111 | state of the CEC adapter after calling cec_allocate_adapter() is disabled. | ||
| 112 | |||
| 113 | Note that adap_enable must return 0 if enable is false. | ||
| 114 | |||
| 115 | |||
| 116 | To enable/disable the 'monitor all' mode: | ||
| 117 | |||
| 118 | int (*adap_monitor_all_enable)(struct cec_adapter *adap, bool enable); | ||
| 119 | |||
| 120 | If enabled, then the adapter should be put in a mode to also monitor messages | ||
| 121 | that not for us. Not all hardware supports this and this function is only | ||
| 122 | called if the CEC_CAP_MONITOR_ALL capability is set. This callback is optional | ||
| 123 | (some hardware may always be in 'monitor all' mode). | ||
| 124 | |||
| 125 | Note that adap_monitor_all_enable must return 0 if enable is false. | ||
| 126 | |||
| 127 | |||
| 128 | To program a new logical address: | ||
| 129 | |||
| 130 | int (*adap_log_addr)(struct cec_adapter *adap, u8 logical_addr); | ||
| 131 | |||
| 132 | If logical_addr == CEC_LOG_ADDR_INVALID then all programmed logical addresses | ||
| 133 | are to be erased. Otherwise the given logical address should be programmed. | ||
| 134 | If the maximum number of available logical addresses is exceeded, then it | ||
| 135 | should return -ENXIO. Once a logical address is programmed the CEC hardware | ||
| 136 | can receive directed messages to that address. | ||
| 137 | |||
| 138 | Note that adap_log_addr must return 0 if logical_addr is CEC_LOG_ADDR_INVALID. | ||
| 139 | |||
| 140 | |||
| 141 | To transmit a new message: | ||
| 142 | |||
| 143 | int (*adap_transmit)(struct cec_adapter *adap, u8 attempts, | ||
| 144 | u32 signal_free_time, struct cec_msg *msg); | ||
| 145 | |||
| 146 | This transmits a new message. The attempts argument is the suggested number of | ||
| 147 | attempts for the transmit. | ||
| 148 | |||
| 149 | The signal_free_time is the number of data bit periods that the adapter should | ||
| 150 | wait when the line is free before attempting to send a message. This value | ||
| 151 | depends on whether this transmit is a retry, a message from a new initiator or | ||
| 152 | a new message for the same initiator. Most hardware will handle this | ||
| 153 | automatically, but in some cases this information is needed. | ||
| 154 | |||
| 155 | The CEC_FREE_TIME_TO_USEC macro can be used to convert signal_free_time to | ||
| 156 | microseconds (one data bit period is 2.4 ms). | ||
| 157 | |||
| 158 | |||
| 159 | To log the current CEC hardware status: | ||
| 160 | |||
| 161 | void (*adap_status)(struct cec_adapter *adap, struct seq_file *file); | ||
| 162 | |||
| 163 | This optional callback can be used to show the status of the CEC hardware. | ||
| 164 | The status is available through debugfs: cat /sys/kernel/debug/cec/cecX/status | ||
| 165 | |||
| 166 | |||
| 167 | Your adapter driver will also have to react to events (typically interrupt | ||
| 168 | driven) by calling into the framework in the following situations: | ||
| 169 | |||
| 170 | When a transmit finished (successfully or otherwise): | ||
| 171 | |||
| 172 | void cec_transmit_done(struct cec_adapter *adap, u8 status, u8 arb_lost_cnt, | ||
| 173 | u8 nack_cnt, u8 low_drive_cnt, u8 error_cnt); | ||
| 174 | |||
| 175 | The status can be one of: | ||
| 176 | |||
| 177 | CEC_TX_STATUS_OK: the transmit was successful. | ||
| 178 | CEC_TX_STATUS_ARB_LOST: arbitration was lost: another CEC initiator | ||
| 179 | took control of the CEC line and you lost the arbitration. | ||
| 180 | CEC_TX_STATUS_NACK: the message was nacked (for a directed message) or | ||
| 181 | acked (for a broadcast message). A retransmission is needed. | ||
| 182 | CEC_TX_STATUS_LOW_DRIVE: low drive was detected on the CEC bus. This | ||
| 183 | indicates that a follower detected an error on the bus and requested a | ||
| 184 | retransmission. | ||
| 185 | CEC_TX_STATUS_ERROR: some unspecified error occurred: this can be one of | ||
| 186 | the previous two if the hardware cannot differentiate or something else | ||
| 187 | entirely. | ||
| 188 | CEC_TX_STATUS_MAX_RETRIES: could not transmit the message after | ||
| 189 | trying multiple times. Should only be set by the driver if it has hardware | ||
| 190 | support for retrying messages. If set, then the framework assumes that it | ||
| 191 | doesn't have to make another attempt to transmit the message since the | ||
| 192 | hardware did that already. | ||
| 193 | |||
| 194 | The *_cnt arguments are the number of error conditions that were seen. | ||
| 195 | This may be 0 if no information is available. Drivers that do not support | ||
| 196 | hardware retry can just set the counter corresponding to the transmit error | ||
| 197 | to 1, if the hardware does support retry then either set these counters to | ||
| 198 | 0 if the hardware provides no feedback of which errors occurred and how many | ||
| 199 | times, or fill in the correct values as reported by the hardware. | ||
| 200 | |||
| 201 | When a CEC message was received: | ||
| 202 | |||
| 203 | void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg); | ||
| 204 | |||
| 205 | Speaks for itself. | ||
| 206 | |||
| 207 | Implementing the High-Level CEC Adapter | ||
| 208 | --------------------------------------- | ||
| 209 | |||
| 210 | The low-level operations drive the hardware, the high-level operations are | ||
| 211 | CEC protocol driven. The following high-level callbacks are available: | ||
| 212 | |||
| 213 | struct cec_adap_ops { | ||
| 214 | /* Low-level callbacks */ | ||
| 215 | ... | ||
| 216 | |||
| 217 | /* High-level CEC message callback */ | ||
| 218 | int (*received)(struct cec_adapter *adap, struct cec_msg *msg); | ||
| 219 | }; | ||
| 220 | |||
| 221 | The received() callback allows the driver to optionally handle a newly | ||
| 222 | received CEC message | ||
| 223 | |||
| 224 | int (*received)(struct cec_adapter *adap, struct cec_msg *msg); | ||
| 225 | |||
| 226 | If the driver wants to process a CEC message, then it can implement this | ||
| 227 | callback. If it doesn't want to handle this message, then it should return | ||
| 228 | -ENOMSG, otherwise the CEC framework assumes it processed this message and | ||
| 229 | it will not no anything with it. | ||
| 230 | |||
| 231 | |||
| 232 | CEC framework functions | ||
| 233 | ----------------------- | ||
| 234 | |||
| 235 | CEC Adapter drivers can call the following CEC framework functions: | ||
| 236 | |||
| 237 | int cec_transmit_msg(struct cec_adapter *adap, struct cec_msg *msg, | ||
| 238 | bool block); | ||
| 239 | |||
| 240 | Transmit a CEC message. If block is true, then wait until the message has been | ||
| 241 | transmitted, otherwise just queue it and return. | ||
| 242 | |||
| 243 | void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block); | ||
| 244 | |||
| 245 | Change the physical address. This function will set adap->phys_addr and | ||
| 246 | send an event if it has changed. If cec_s_log_addrs() has been called and | ||
| 247 | the physical address has become valid, then the CEC framework will start | ||
| 248 | claiming the logical addresses. If block is true, then this function won't | ||
| 249 | return until this process has finished. | ||
| 250 | |||
| 251 | When the physical address is set to a valid value the CEC adapter will | ||
| 252 | be enabled (see the adap_enable op). When it is set to CEC_PHYS_ADDR_INVALID, | ||
| 253 | then the CEC adapter will be disabled. If you change a valid physical address | ||
| 254 | to another valid physical address, then this function will first set the | ||
| 255 | address to CEC_PHYS_ADDR_INVALID before enabling the new physical address. | ||
| 256 | |||
| 257 | int cec_s_log_addrs(struct cec_adapter *adap, | ||
| 258 | struct cec_log_addrs *log_addrs, bool block); | ||
| 259 | |||
| 260 | Claim the CEC logical addresses. Should never be called if CEC_CAP_LOG_ADDRS | ||
| 261 | is set. If block is true, then wait until the logical addresses have been | ||
| 262 | claimed, otherwise just queue it and return. To unconfigure all logical | ||
| 263 | addresses call this function with log_addrs set to NULL or with | ||
| 264 | log_addrs->num_log_addrs set to 0. The block argument is ignored when | ||
| 265 | unconfiguring. This function will just return if the physical address is | ||
| 266 | invalid. Once the physical address becomes valid, then the framework will | ||
| 267 | attempt to claim these logical addresses. | ||
| @@ -0,0 +1,31 @@ | |||
| 1 | * Samsung HDMI CEC driver | ||
| 2 | |||
| 3 | The HDMI CEC module is present is Samsung SoCs and its purpose is to | ||
| 4 | handle communication between HDMI connected devices over the CEC bus. | ||
| 5 | |||
| 6 | Required properties: | ||
| 7 | - compatible : value should be following | ||
| 8 | "samsung,s5p-cec" | ||
| 9 | |||
| 10 | - reg : Physical base address of the IP registers and length of memory | ||
| 11 | mapped region. | ||
| 12 | |||
| 13 | - interrupts : HDMI CEC interrupt number to the CPU. | ||
| 14 | - clocks : from common clock binding: handle to HDMI CEC clock. | ||
| 15 | - clock-names : from common clock binding: must contain "hdmicec", | ||
| 16 | corresponding to entry in the clocks property. | ||
| 17 | - samsung,syscon-phandle - phandle to the PMU system controller | ||
| 18 | |||
| 19 | Example: | ||
| 20 | |||
| 21 | hdmicec: cec@100B0000 { | ||
| 22 | compatible = "samsung,s5p-cec"; | ||
| 23 | reg = <0x100B0000 0x200>; | ||
| 24 | interrupts = <0 114 0>; | ||
| 25 | clocks = <&clock CLK_HDMI_CEC>; | ||
| 26 | clock-names = "hdmicec"; | ||
| 27 | samsung,syscon-phandle = <&pmu_system_controller>; | ||
| 28 | pinctrl-names = "default"; | ||
| 29 | pinctrl-0 = <&hdmi_cec>; | ||
| 30 | status = "okay"; | ||
| 31 | }; | ||
| @@ -74,7 +74,8 @@ Section 11: Cropping, Composing, Scaling · 73 unmodified lines | |||
| 74 | Section 12: Formats | 74 | Section 12: Formats |
| 75 | Section 13: Capture Overlay | 75 | Section 13: Capture Overlay |
| 76 | Section 14: Output Overlay | 76 | Section 14: Output Overlay |
| 77 | Section 15: Some Future Improvements | 77 | Section 15: CEC (Consumer Electronics Control) |
| 78 | Section 16: Some Future Improvements | ||
| 78 | 79 | ||
| 79 | 80 | ||
| 80 | Section 1: Configuring the driver | 81 | Section 1: Configuring the driver |
| @@ -364,7 +365,11 @@ For HDMI inputs it is possible to set the EDID. By default a simple EDID · 283 unmodified lines | |||
| 364 | is provided. You can only set the EDID for HDMI inputs. Internally, however, | 365 | is provided. You can only set the EDID for HDMI inputs. Internally, however, |
| 365 | the EDID is shared between all HDMI inputs. | 366 | the EDID is shared between all HDMI inputs. |
| 366 | 367 | ||
| 367 | No interpretation is done of the EDID data. | 368 | No interpretation is done of the EDID data with the exception of the |
| 369 | physical address. See the CEC section for more details. | ||
| 370 | |||
| 371 | There is a maximum of 15 HDMI inputs (if there are more, then they will be | ||
| 372 | reduced to 15) since that's the limitation of the EDID physical address. | ||
| 368 | 373 | ||
| 369 | 374 | ||
| 370 | Section 3: Video Output | 375 | Section 3: Video Output |
| @@ -409,6 +414,9 @@ standard, and for all others a 1:1 pixel aspect ratio is returned. · 38 unmodified lines | |||
| 409 | 414 | ||
| 410 | An HDMI output has a valid EDID which can be obtained through VIDIOC_G_EDID. | 415 | An HDMI output has a valid EDID which can be obtained through VIDIOC_G_EDID. |
| 411 | 416 | ||
| 417 | There is a maximum of 15 HDMI outputs (if there are more, then they will be | ||
| 418 | reduced to 15) since that's the limitation of the EDID physical address. See | ||
| 419 | also the CEC section for more details. | ||
| 412 | 420 | ||
| 413 | Section 4: VBI Capture | 421 | Section 4: VBI Capture |
| 414 | ---------------------- | 422 | ---------------------- |
| @@ -1108,7 +1116,26 @@ capabilities will slow down the video loop considerably as a lot of checks have · 693 unmodified lines | |||
| 1108 | to be done per pixel. | 1116 | to be done per pixel. |
| 1109 | 1117 | ||
| 1110 | 1118 | ||
| 1111 | Section 15: Some Future Improvements | 1119 | Section 15: CEC (Consumer Electronics Control) |
| 1120 | ---------------------------------------------- | ||
| 1121 | |||
| 1122 | If there are HDMI inputs then a CEC adapter will be created that has | ||
| 1123 | the same number of input ports. This is the equivalent of e.g. a TV that | ||
| 1124 | has that number of inputs. Each HDMI output will also create a | ||
| 1125 | CEC adapter that is hooked up to the corresponding input port, or (if there | ||
| 1126 | are more outputs than inputs) is not hooked up at all. In other words, | ||
| 1127 | this is the equivalent of hooking up each output device to an input port of | ||
| 1128 | the TV. Any remaining output devices remain unconnected. | ||
| 1129 | |||
| 1130 | The EDID that each output reads reports a unique CEC physical address that is | ||
| 1131 | based on the physical address of the EDID of the input. So if the EDID of the | ||
| 1132 | receiver has physical address A.B.0.0, then each output will see an EDID | ||
| 1133 | containing physical address A.B.C.0 where C is 1 to the number of inputs. If | ||
| 1134 | there are more outputs than inputs then the remaining outputs have a CEC adapter | ||
| 1135 | that is disabled and reports an invalid physical address. | ||
| 1136 | |||
| 1137 | |||
| 1138 | Section 16: Some Future Improvements | ||
| 1112 | ------------------------------------ | 1139 | ------------------------------------ |
| 1113 | 1140 | ||
| 1114 | Just as a reminder and in no particular order: | 1141 | Just as a reminder and in no particular order: |
| @@ -1121,8 +1148,6 @@ Just as a reminder and in no particular order: · 6 unmodified lines | |||
| 1121 | - Fix sequence/field numbering when looping of video with alternate fields | 1148 | - Fix sequence/field numbering when looping of video with alternate fields |
| 1122 | - Add support for V4L2_CID_BG_COLOR for video outputs | 1149 | - Add support for V4L2_CID_BG_COLOR for video outputs |
| 1123 | - Add ARGB888 overlay support: better testing of the alpha channel | 1150 | - Add ARGB888 overlay support: better testing of the alpha channel |
| 1124 | - Add custom DV timings support | ||
| 1125 | - Add support for V4L2_DV_FL_REDUCED_FPS | ||
| 1126 | - Improve pixel aspect support in the tpg code by passing a real v4l2_fract | 1151 | - Improve pixel aspect support in the tpg code by passing a real v4l2_fract |
| 1127 | - Use per-queue locks and/or per-device locks to improve throughput | 1152 | - Use per-queue locks and/or per-device locks to improve throughput |
| 1128 | - Add support to loop from a specific output to a specific input across | 1153 | - 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: · 4 unmodified lines | |||
| 1133 | - Make a thread for the RDS generation, that would help in particular for the | 1158 | - Make a thread for the RDS generation, that would help in particular for the |
| 1134 | "Controls" RDS Rx I/O Mode as the read-only RDS controls could be updated | 1159 | "Controls" RDS Rx I/O Mode as the read-only RDS controls could be updated |
| 1135 | in real-time. | 1160 | in real-time. |
| 1161 | - Changing the EDID should cause hotplug detect emulation to happen. | ||
| @@ -1642,6 +1642,13 @@ L: linux-media@vger.kernel.org · 1641 unmodified lines | |||
| 1642 | S: Maintained | 1642 | S: Maintained |
| 1643 | F: drivers/media/platform/s5p-tv/ | 1643 | F: drivers/media/platform/s5p-tv/ |
| 1644 | 1644 | ||
| 1645 | ARM/SAMSUNG S5P SERIES HDMI CEC SUBSYSTEM SUPPORT | ||
| 1646 | M: Kyungmin Park <kyungmin.park@samsung.com> | ||
| 1647 | L: linux-arm-kernel@lists.infradead.org | ||
| 1648 | L: linux-media@vger.kernel.org | ||
| 1649 | S: Maintained | ||
| 1650 | F: drivers/staging/media/platform/s5p-cec/ | ||
| 1651 | |||
| 1645 | ARM/SAMSUNG S5P SERIES JPEG CODEC SUPPORT | 1652 | ARM/SAMSUNG S5P SERIES JPEG CODEC SUPPORT |
| 1646 | M: Andrzej Pietrasiewicz <andrzej.p@samsung.com> | 1653 | M: Andrzej Pietrasiewicz <andrzej.p@samsung.com> |
| 1647 | M: Jacek Anaszewski <j.anaszewski@samsung.com> | 1654 | M: Jacek Anaszewski <j.anaszewski@samsung.com> |
| @@ -2847,6 +2854,22 @@ F: drivers/net/ieee802154/cc2520.c · 1199 unmodified lines | |||
| 2847 | F: include/linux/spi/cc2520.h | 2854 | F: include/linux/spi/cc2520.h |
| 2848 | F: Documentation/devicetree/bindings/net/ieee802154/cc2520.txt | 2855 | F: Documentation/devicetree/bindings/net/ieee802154/cc2520.txt |
| 2849 | 2856 | ||
| 2857 | CEC DRIVER | ||
| 2858 | M: Hans Verkuil <hans.verkuil@cisco.com> | ||
| 2859 | L: linux-media@vger.kernel.org | ||
| 2860 | T: git git://linuxtv.org/media_tree.git | ||
| 2861 | W: http://linuxtv.org | ||
| 2862 | S: Supported | ||
| 2863 | F: Documentation/cec.txt | ||
| 2864 | F: Documentation/DocBook/media/v4l/cec* | ||
| 2865 | F: drivers/staging/media/cec/ | ||
| 2866 | F: drivers/media/cec-edid.c | ||
| 2867 | F: drivers/media/rc/keymaps/rc-cec.c | ||
| 2868 | F: include/media/cec.h | ||
| 2869 | F: include/media/cec-edid.h | ||
| 2870 | F: include/linux/cec.h | ||
| 2871 | F: include/linux/cec-funcs.h | ||
| 2872 | |||
| 2850 | CELL BROADBAND ENGINE ARCHITECTURE | 2873 | CELL BROADBAND ENGINE ARCHITECTURE |
| 2851 | M: Arnd Bergmann <arnd@arndb.de> | 2874 | M: Arnd Bergmann <arnd@arndb.de> |
| 2852 | L: linuxppc-dev@lists.ozlabs.org | 2875 | L: linuxppc-dev@lists.ozlabs.org |
| @@ -80,6 +80,9 @@ config MEDIA_RC_SUPPORT · 79 unmodified lines | |||
| 80 | 80 | ||
| 81 | Say Y when you have a TV or an IR device. | 81 | Say Y when you have a TV or an IR device. |
| 82 | 82 | ||
| 83 | config MEDIA_CEC_EDID | ||
| 84 | tristate | ||
| 85 | |||
| 83 | # | 86 | # |
| 84 | # Media controller | 87 | # Media controller |
| 85 | # Selectable only for webcam/grabbers, as other drivers don't use it | 88 | # Selectable only for webcam/grabbers, as other drivers don't use it |
| @@ -2,6 +2,8 @@ · 1 unmodified line | |||
| 2 | # Makefile for the kernel multimedia device drivers. | 2 | # Makefile for the kernel multimedia device drivers. |
| 3 | # | 3 | # |
| 4 | 4 | ||
| 5 | obj-$(CONFIG_MEDIA_CEC_EDID) += cec-edid.o | ||
| 6 | |||
| 5 | media-objs := media-device.o media-devnode.o media-entity.o | 7 | media-objs := media-device.o media-devnode.o media-entity.o |
| 6 | 8 | ||
| 7 | # | 9 | # |
| @@ -0,0 +1,168 @@ | |||
| 1 | /* | ||
| 2 | * cec-edid - HDMI Consumer Electronics Control EDID & CEC helper functions | ||
| 3 | * | ||
| 4 | * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved. | ||
| 5 | * | ||
| 6 | * This program is free software; you may redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; version 2 of the License. | ||
| 9 | * | ||
| 10 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
| 11 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
| 12 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
| 13 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||
| 14 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
| 15 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
| 16 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| 17 | * SOFTWARE. | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include <linux/module.h> | ||
| 21 | #include <linux/kernel.h> | ||
| 22 | #include <linux/types.h> | ||
| 23 | #include <media/cec-edid.h> | ||
| 24 | |||
| 25 | /* | ||
| 26 | * This EDID is expected to be a CEA-861 compliant, which means that there are | ||
| 27 | * at least two blocks and one or more of the extensions blocks are CEA-861 | ||
| 28 | * blocks. | ||
| 29 | * | ||
| 30 | * The returned location is guaranteed to be < size - 1. | ||
| 31 | */ | ||
| 32 | static unsigned int cec_get_edid_spa_location(const u8 *edid, unsigned int size) | ||
| 33 | { | ||
| 34 | unsigned int blocks = size / 128; | ||
| 35 | unsigned int block; | ||
| 36 | u8 d; | ||
| 37 | |||
| 38 | /* Sanity check: at least 2 blocks and a multiple of the block size */ | ||
| 39 | if (blocks < 2 || size % 128) | ||
| 40 | return 0; | ||
| 41 | |||
| 42 | /* | ||
| 43 | * If there are fewer extension blocks than the size, then update | ||
| 44 | * 'blocks'. It is allowed to have more extension blocks than the size, | ||
| 45 | * since some hardware can only read e.g. 256 bytes of the EDID, even | ||
| 46 | * though more blocks are present. The first CEA-861 extension block | ||
| 47 | * should normally be in block 1 anyway. | ||
| 48 | */ | ||
| 49 | if (edid[0x7e] + 1 < blocks) | ||
| 50 | blocks = edid[0x7e] + 1; | ||
| 51 | |||
| 52 | for (block = 1; block < blocks; block++) { | ||
| 53 | unsigned int offset = block * 128; | ||
| 54 | |||
| 55 | /* Skip any non-CEA-861 extension blocks */ | ||
| 56 | if (edid[offset] != 0x02 || edid[offset + 1] != 0x03) | ||
| 57 | continue; | ||
| 58 | |||
| 59 | /* search Vendor Specific Data Block (tag 3) */ | ||
| 60 | d = edid[offset + 2] & 0x7f; | ||
| 61 | /* Check if there are Data Blocks */ | ||
| 62 | if (d <= 4) | ||
| 63 | continue; | ||
| 64 | if (d > 4) { | ||
| 65 | unsigned int i = offset + 4; | ||
| 66 | unsigned int end = offset + d; | ||
| 67 | |||
| 68 | /* Note: 'end' is always < 'size' */ | ||
| 69 | do { | ||
| 70 | u8 tag = edid[i] >> 5; | ||
| 71 | u8 len = edid[i] & 0x1f; | ||
| 72 | |||
| 73 | if (tag == 3 && len >= 5 && i + len <= end) | ||
| 74 | return i + 4; | ||
| 75 | i += len + 1; | ||
| 76 | } while (i < end); | ||
| 77 | } | ||
| 78 | } | ||
| 79 | return 0; | ||
| 80 | } | ||
| 81 | |||
| 82 | u16 cec_get_edid_phys_addr(const u8 *edid, unsigned int size, | ||
| 83 | unsigned int *offset) | ||
| 84 | { | ||
| 85 | unsigned int loc = cec_get_edid_spa_location(edid, size); | ||
| 86 | |||
| 87 | if (offset) | ||
| 88 | *offset = loc; | ||
| 89 | if (loc == 0) | ||
| 90 | return CEC_PHYS_ADDR_INVALID; | ||
| 91 | return (edid[loc] << 8) | edid[loc + 1]; | ||
| 92 | } | ||
| 93 | EXPORT_SYMBOL_GPL(cec_get_edid_phys_addr); | ||
| 94 | |||
| 95 | void cec_set_edid_phys_addr(u8 *edid, unsigned int size, u16 phys_addr) | ||
| 96 | { | ||
| 97 | unsigned int loc = cec_get_edid_spa_location(edid, size); | ||
| 98 | u8 sum = 0; | ||
| 99 | unsigned int i; | ||
| 100 | |||
| 101 | if (loc == 0) | ||
| 102 | return; | ||
| 103 | edid[loc] = phys_addr >> 8; | ||
| 104 | edid[loc + 1] = phys_addr & 0xff; | ||
| 105 | loc &= ~0x7f; | ||
| 106 | |||
| 107 | /* update the checksum */ | ||
| 108 | for (i = loc; i < loc + 127; i++) | ||
| 109 | sum += edid[i]; | ||
| 110 | edid[i] = 256 - sum; | ||
| 111 | } | ||
| 112 | EXPORT_SYMBOL_GPL(cec_set_edid_phys_addr); | ||
| 113 | |||
| 114 | u16 cec_phys_addr_for_input(u16 phys_addr, u8 input) | ||
| 115 | { | ||
| 116 | /* Check if input is sane */ | ||
| 117 | if (WARN_ON(input == 0 || input > 0xf)) | ||
| 118 | return CEC_PHYS_ADDR_INVALID; | ||
| 119 | |||
| 120 | if (phys_addr == 0) | ||
| 121 | return input << 12; | ||
| 122 | |||
| 123 | if ((phys_addr & 0x0fff) == 0) | ||
| 124 | return phys_addr | (input << 8); | ||
| 125 | |||
| 126 | if ((phys_addr & 0x00ff) == 0) | ||
| 127 | return phys_addr | (input << 4); | ||
| 128 | |||
| 129 | if ((phys_addr & 0x000f) == 0) | ||
| 130 | return phys_addr | input; | ||
| 131 | |||
| 132 | /* | ||
| 133 | * All nibbles are used so no valid physical addresses can be assigned | ||
| 134 | * to the input. | ||
| 135 | */ | ||
| 136 | return CEC_PHYS_ADDR_INVALID; | ||
| 137 | } | ||
| 138 | EXPORT_SYMBOL_GPL(cec_phys_addr_for_input); | ||
| 139 | |||
| 140 | int cec_phys_addr_validate(u16 phys_addr, u16 *parent, u16 *port) | ||
| 141 | { | ||
| 142 | int i; | ||
| 143 | |||
| 144 | if (parent) | ||
| 145 | *parent = phys_addr; | ||
| 146 | if (port) | ||
| 147 | *port = 0; | ||
| 148 | if (phys_addr == CEC_PHYS_ADDR_INVALID) | ||
| 149 | return 0; | ||
| 150 | for (i = 0; i < 16; i += 4) | ||
| 151 | if (phys_addr & (0xf << i)) | ||
| 152 | break; | ||
| 153 | if (i == 16) | ||
| 154 | return 0; | ||
| 155 | if (parent) | ||
| 156 | *parent = phys_addr & (0xfff0 << i); | ||
| 157 | if (port) | ||
| 158 | *port = (phys_addr >> i) & 0xf; | ||
| 159 | for (i += 4; i < 16; i += 4) | ||
| 160 | if ((phys_addr & (0xf << i)) == 0) | ||
| 161 | return -EINVAL; | ||
| 162 | return 0; | ||
| 163 | } | ||
| 164 | EXPORT_SYMBOL_GPL(cec_phys_addr_validate); | ||
| 165 | |||
| 166 | MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>"); | ||
| 167 | MODULE_DESCRIPTION("CEC EDID helper functions"); | ||
| 168 | MODULE_LICENSE("GPL"); | ||
| @@ -209,6 +209,7 @@ config VIDEO_ADV7604 · 208 unmodified lines | |||
| 209 | depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API | 209 | depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API |
| 210 | depends on GPIOLIB || COMPILE_TEST | 210 | depends on GPIOLIB || COMPILE_TEST |
| 211 | select HDMI | 211 | select HDMI |
| 212 | select MEDIA_CEC_EDID | ||
| 212 | ---help--- | 213 | ---help--- |
| 213 | Support for the Analog Devices ADV7604 video decoder. | 214 | Support for the Analog Devices ADV7604 video decoder. |
| 214 | 215 | ||
| @@ -218,10 +219,18 @@ config VIDEO_ADV7604 · 3 unmodified lines | |||
| 218 | To compile this driver as a module, choose M here: the | 219 | To compile this driver as a module, choose M here: the |
| 219 | module will be called adv7604. | 220 | module will be called adv7604. |
| 220 | 221 | ||
| 222 | config VIDEO_ADV7604_CEC | ||
| 223 | bool "Enable Analog Devices ADV7604 CEC support" | ||
| 224 | depends on VIDEO_ADV7604 && MEDIA_CEC | ||
| 225 | ---help--- | ||
| 226 | When selected the adv7604 will support the optional | ||
| 227 | HDMI CEC feature. | ||
| 228 | |||
| 221 | config VIDEO_ADV7842 | 229 | config VIDEO_ADV7842 |
| 222 | tristate "Analog Devices ADV7842 decoder" | 230 | tristate "Analog Devices ADV7842 decoder" |
| 223 | depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API | 231 | depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API |
| 224 | select HDMI | 232 | select HDMI |
| 233 | select MEDIA_CEC_EDID | ||
| 225 | ---help--- | 234 | ---help--- |
| 226 | Support for the Analog Devices ADV7842 video decoder. | 235 | Support for the Analog Devices ADV7842 video decoder. |
| 227 | 236 | ||
| @@ -231,6 +240,13 @@ config VIDEO_ADV7842 · 3 unmodified lines | |||
| 231 | To compile this driver as a module, choose M here: the | 240 | To compile this driver as a module, choose M here: the |
| 232 | module will be called adv7842. | 241 | module will be called adv7842. |
| 233 | 242 | ||
| 243 | config VIDEO_ADV7842_CEC | ||
| 244 | bool "Enable Analog Devices ADV7842 CEC support" | ||
| 245 | depends on VIDEO_ADV7842 && MEDIA_CEC | ||
| 246 | ---help--- | ||
| 247 | When selected the adv7842 will support the optional | ||
| 248 | HDMI CEC feature. | ||
| 249 | |||
| 234 | config VIDEO_BT819 | 250 | config VIDEO_BT819 |
| 235 | tristate "BT819A VideoStream decoder" | 251 | tristate "BT819A VideoStream decoder" |
| 236 | depends on VIDEO_V4L2 && I2C | 252 | depends on VIDEO_V4L2 && I2C |
| @@ -447,6 +463,7 @@ config VIDEO_ADV7511 · 210 unmodified lines | |||
| 447 | tristate "Analog Devices ADV7511 encoder" | 463 | tristate "Analog Devices ADV7511 encoder" |
| 448 | depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API | 464 | depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API |
| 449 | select HDMI | 465 | select HDMI |
| 466 | select MEDIA_CEC_EDID | ||
| 450 | ---help--- | 467 | ---help--- |
| 451 | Support for the Analog Devices ADV7511 video encoder. | 468 | Support for the Analog Devices ADV7511 video encoder. |
| 452 | 469 | ||
| @@ -455,6 +472,13 @@ config VIDEO_ADV7511 · 2 unmodified lines | |||
| 455 | To compile this driver as a module, choose M here: the | 472 | To compile this driver as a module, choose M here: the |
| 456 | module will be called adv7511. | 473 | module will be called adv7511. |
| 457 | 474 | ||
| 475 | config VIDEO_ADV7511_CEC | ||
| 476 | bool "Enable Analog Devices ADV7511 CEC support" | ||
| 477 | depends on VIDEO_ADV7511 && MEDIA_CEC | ||
| 478 | ---help--- | ||
| 479 | When selected the adv7511 will support the optional | ||
| 480 | HDMI CEC feature. | ||
| 481 | |||
| 458 | config VIDEO_AD9389B | 482 | config VIDEO_AD9389B |
| 459 | tristate "Analog Devices AD9389B encoder" | 483 | tristate "Analog Devices AD9389B encoder" |
| 460 | depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API | 484 | depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API |
| @@ -33,6 +33,7 @@ · 32 unmodified lines | |||
| 33 | #include <media/v4l2-ctrls.h> | 33 | #include <media/v4l2-ctrls.h> |
| 34 | #include <media/v4l2-dv-timings.h> | 34 | #include <media/v4l2-dv-timings.h> |
| 35 | #include <media/i2c/adv7511.h> | 35 | #include <media/i2c/adv7511.h> |
| 36 | #include <media/cec.h> | ||
| 36 | 37 | ||
| 37 | static int debug; | 38 | static int debug; |
| 38 | module_param(debug, int, 0644); | 39 | module_param(debug, int, 0644); |
| @@ -59,6 +60,8 @@ MODULE_LICENSE("GPL v2"); · 20 unmodified lines | |||
| 59 | #define ADV7511_MIN_PIXELCLOCK 20000000 | 60 | #define ADV7511_MIN_PIXELCLOCK 20000000 |
| 60 | #define ADV7511_MAX_PIXELCLOCK 225000000 | 61 | #define ADV7511_MAX_PIXELCLOCK 225000000 |
| 61 | 62 | ||
| 63 | #define ADV7511_MAX_ADDRS (3) | ||
| 64 | |||
| 62 | /* | 65 | /* |
| 63 | ********************************************************************** | 66 | ********************************************************************** |
| 64 | * | 67 | * |
| @@ -90,12 +93,20 @@ struct adv7511_state { · 25 unmodified lines | |||
| 90 | struct v4l2_ctrl_handler hdl; | 93 | struct v4l2_ctrl_handler hdl; |
| 91 | int chip_revision; | 94 | int chip_revision; |
| 92 | u8 i2c_edid_addr; | 95 | u8 i2c_edid_addr; |
| 93 | u8 i2c_cec_addr; | ||
| 94 | u8 i2c_pktmem_addr; | 96 | u8 i2c_pktmem_addr; |
| 97 | u8 i2c_cec_addr; | ||
| 98 | |||
| 99 | struct i2c_client *i2c_cec; | ||
| 100 | struct cec_adapter *cec_adap; | ||
| 101 | u8 cec_addr[ADV7511_MAX_ADDRS]; | ||
| 102 | u8 cec_valid_addrs; | ||
| 103 | bool cec_enabled_adap; | ||
| 104 | |||
| 95 | /* Is the adv7511 powered on? */ | 105 | /* Is the adv7511 powered on? */ |
| 96 | bool power_on; | 106 | bool power_on; |
| 97 | /* Did we receive hotplug and rx-sense signals? */ | 107 | /* Did we receive hotplug and rx-sense signals? */ |
| 98 | bool have_monitor; | 108 | bool have_monitor; |
| 109 | bool enabled_irq; | ||
| 99 | /* timings from s_dv_timings */ | 110 | /* timings from s_dv_timings */ |
| 100 | struct v4l2_dv_timings dv_timings; | 111 | struct v4l2_dv_timings dv_timings; |
| 101 | u32 fmt_code; | 112 | u32 fmt_code; |
| @@ -227,7 +238,7 @@ static int adv_smbus_read_i2c_block_data(struct i2c_client *client, · 125 unmodified lines | |||
| 227 | return ret; | 238 | return ret; |
| 228 | } | 239 | } |
| 229 | 240 | ||
| 230 | static inline void adv7511_edid_rd(struct v4l2_subdev *sd, u16 len, u8 *buf) | 241 | static void adv7511_edid_rd(struct v4l2_subdev *sd, uint16_t len, uint8_t *buf) |
| 231 | { | 242 | { |
| 232 | struct adv7511_state *state = get_adv7511_state(sd); | 243 | struct adv7511_state *state = get_adv7511_state(sd); |
| 233 | int i; | 244 | int i; |
| @@ -242,6 +253,34 @@ static inline void adv7511_edid_rd(struct v4l2_subdev *sd, u16 len, u8 *buf) · 8 unmodified lines | |||
| 242 | v4l2_err(sd, "%s: i2c read error\n", __func__); | 253 | v4l2_err(sd, "%s: i2c read error\n", __func__); |
| 243 | } | 254 | } |
| 244 | 255 | ||
| 256 | static inline int adv7511_cec_read(struct v4l2_subdev *sd, u8 reg) | ||
| 257 | { | ||
| 258 | struct adv7511_state *state = get_adv7511_state(sd); | ||
| 259 | |||
| 260 | return i2c_smbus_read_byte_data(state->i2c_cec, reg); | ||
| 261 | } | ||
| 262 | |||
| 263 | static int adv7511_cec_write(struct v4l2_subdev *sd, u8 reg, u8 val) | ||
| 264 | { | ||
| 265 | struct adv7511_state *state = get_adv7511_state(sd); | ||
| 266 | int ret; | ||
| 267 | int i; | ||
| 268 | |||
| 269 | for (i = 0; i < 3; i++) { | ||
| 270 | ret = i2c_smbus_write_byte_data(state->i2c_cec, reg, val); | ||
| 271 | if (ret == 0) | ||
| 272 | return 0; | ||
| 273 | } | ||
| 274 | v4l2_err(sd, "%s: I2C Write Problem\n", __func__); | ||
| 275 | return ret; | ||
| 276 | } | ||
| 277 | |||
| 278 | static inline int adv7511_cec_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, | ||
| 279 | u8 val) | ||
| 280 | { | ||
| 281 | return adv7511_cec_write(sd, reg, (adv7511_cec_read(sd, reg) & mask) | val); | ||
| 282 | } | ||
| 283 | |||
| 245 | static int adv7511_pktmem_rd(struct v4l2_subdev *sd, u8 reg) | 284 | static int adv7511_pktmem_rd(struct v4l2_subdev *sd, u8 reg) |
| 246 | { | 285 | { |
| 247 | struct adv7511_state *state = get_adv7511_state(sd); | 286 | struct adv7511_state *state = get_adv7511_state(sd); |
| @@ -425,16 +464,28 @@ static const struct v4l2_ctrl_ops adv7511_ctrl_ops = { · 177 unmodified lines | |||
| 425 | #ifdef CONFIG_VIDEO_ADV_DEBUG | 464 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
| 426 | static void adv7511_inv_register(struct v4l2_subdev *sd) | 465 | static void adv7511_inv_register(struct v4l2_subdev *sd) |
| 427 | { | 466 | { |
| 467 | struct adv7511_state *state = get_adv7511_state(sd); | ||
| 468 | |||
| 428 | v4l2_info(sd, "0x000-0x0ff: Main Map\n"); | 469 | v4l2_info(sd, "0x000-0x0ff: Main Map\n"); |
| 470 | if (state->i2c_cec) | ||
| 471 | v4l2_info(sd, "0x100-0x1ff: CEC Map\n"); | ||
| 429 | } | 472 | } |
| 430 | 473 | ||
| 431 | static int adv7511_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) | 474 | static int adv7511_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) |
| 432 | { | 475 | { |
| 476 | struct adv7511_state *state = get_adv7511_state(sd); | ||
| 477 | |||
| 433 | reg->size = 1; | 478 | reg->size = 1; |
| 434 | switch (reg->reg >> 8) { | 479 | switch (reg->reg >> 8) { |
| 435 | case 0: | 480 | case 0: |
| 436 | reg->val = adv7511_rd(sd, reg->reg & 0xff); | 481 | reg->val = adv7511_rd(sd, reg->reg & 0xff); |
| 437 | break; | 482 | break; |
| 483 | case 1: | ||
| 484 | if (state->i2c_cec) { | ||
| 485 | reg->val = adv7511_cec_read(sd, reg->reg & 0xff); | ||
| 486 | break; | ||
| 487 | } | ||
| 488 | /* fall through */ | ||
| 438 | default: | 489 | default: |
| 439 | v4l2_info(sd, "Register %03llx not supported\n", reg->reg); | 490 | v4l2_info(sd, "Register %03llx not supported\n", reg->reg); |
| 440 | adv7511_inv_register(sd); | 491 | adv7511_inv_register(sd); |
| @@ -445,10 +496,18 @@ static int adv7511_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register * · 4 unmodified lines | |||
| 445 | 496 | ||
| 446 | static int adv7511_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) | 497 | static int adv7511_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) |
| 447 | { | 498 | { |
| 499 | struct adv7511_state *state = get_adv7511_state(sd); | ||
| 500 | |||
| 448 | switch (reg->reg >> 8) { | 501 | switch (reg->reg >> 8) { |
| 449 | case 0: | 502 | case 0: |
| 450 | adv7511_wr(sd, reg->reg & 0xff, reg->val & 0xff); | 503 | adv7511_wr(sd, reg->reg & 0xff, reg->val & 0xff); |
| 451 | break; | 504 | break; |
| 505 | case 1: | ||
| 506 | if (state->i2c_cec) { | ||
| 507 | adv7511_cec_write(sd, reg->reg & 0xff, reg->val & 0xff); | ||
| 508 | break; | ||
| 509 | } | ||
| 510 | /* fall through */ | ||
| 452 | default: | 511 | default: |
| 453 | v4l2_info(sd, "Register %03llx not supported\n", reg->reg); | 512 | v4l2_info(sd, "Register %03llx not supported\n", reg->reg); |
| 454 | adv7511_inv_register(sd); | 513 | adv7511_inv_register(sd); |
| @@ -536,6 +595,7 @@ static int adv7511_log_status(struct v4l2_subdev *sd) · 81 unmodified lines | |||
| 536 | { | 595 | { |
| 537 | struct adv7511_state *state = get_adv7511_state(sd); | 596 | struct adv7511_state *state = get_adv7511_state(sd); |
| 538 | struct adv7511_state_edid *edid = &state->edid; | 597 | struct adv7511_state_edid *edid = &state->edid; |
| 598 | int i; | ||
| 539 | 599 | ||
| 540 | static const char * const states[] = { | 600 | static const char * const states[] = { |
| 541 | "in reset", | 601 | "in reset", |
| @@ -605,7 +665,23 @@ static int adv7511_log_status(struct v4l2_subdev *sd) · 63 unmodified lines | |||
| 605 | else | 665 | else |
| 606 | v4l2_info(sd, "no timings set\n"); | 666 | v4l2_info(sd, "no timings set\n"); |
| 607 | v4l2_info(sd, "i2c edid addr: 0x%x\n", state->i2c_edid_addr); | 667 | v4l2_info(sd, "i2c edid addr: 0x%x\n", state->i2c_edid_addr); |
| 668 | |||
| 669 | if (state->i2c_cec == NULL) | ||
| 670 | return 0; | ||
| 671 | |||
| 608 | v4l2_info(sd, "i2c cec addr: 0x%x\n", state->i2c_cec_addr); | 672 | v4l2_info(sd, "i2c cec addr: 0x%x\n", state->i2c_cec_addr); |
| 673 | |||
| 674 | v4l2_info(sd, "CEC: %s\n", state->cec_enabled_adap ? | ||
| 675 | "enabled" : "disabled"); | ||
| 676 | if (state->cec_enabled_adap) { | ||
| 677 | for (i = 0; i < ADV7511_MAX_ADDRS; i++) { | ||
| 678 | bool is_valid = state->cec_valid_addrs & (1 << i); | ||
| 679 | |||
| 680 | if (is_valid) | ||
| 681 | v4l2_info(sd, "CEC Logical Address: 0x%x\n", | ||
| 682 | state->cec_addr[i]); | ||
| 683 | } | ||
| 684 | } | ||
| 609 | v4l2_info(sd, "i2c pktmem addr: 0x%x\n", state->i2c_pktmem_addr); | 685 | v4l2_info(sd, "i2c pktmem addr: 0x%x\n", state->i2c_pktmem_addr); |
| 610 | return 0; | 686 | return 0; |
| 611 | } | 687 | } |
| @@ -663,15 +739,197 @@ static int adv7511_s_power(struct v4l2_subdev *sd, int on) · 51 unmodified lines | |||
| 663 | return true; | 739 | return true; |
| 664 | } | 740 | } |
| 665 | 741 | ||
| 742 | #if IS_ENABLED(CONFIG_VIDEO_ADV7511_CEC) | ||
| 743 | static int adv7511_cec_adap_enable(struct cec_adapter *adap, bool enable) | ||
| 744 | { | ||
| 745 | struct adv7511_state *state = adap->priv; | ||
| 746 | struct v4l2_subdev *sd = &state->sd; | ||
| 747 | |||
| 748 | if (state->i2c_cec == NULL) | ||
| 749 | return -EIO; | ||
| 750 | |||
| 751 | if (!state->cec_enabled_adap && enable) { | ||
| 752 | /* power up cec section */ | ||
| 753 | adv7511_cec_write_and_or(sd, 0x4e, 0xfc, 0x01); | ||
| 754 | /* legacy mode and clear all rx buffers */ | ||
| 755 | adv7511_cec_write(sd, 0x4a, 0x07); | ||
| 756 | adv7511_cec_write(sd, 0x4a, 0); | ||
| 757 | adv7511_cec_write_and_or(sd, 0x11, 0xfe, 0); /* initially disable tx */ | ||
| 758 | /* enabled irqs: */ | ||
| 759 | /* tx: ready */ | ||
| 760 | /* tx: arbitration lost */ | ||
| 761 | /* tx: retry timeout */ | ||
| 762 | /* rx: ready 1 */ | ||
| 763 | if (state->enabled_irq) | ||
| 764 | adv7511_wr_and_or(sd, 0x95, 0xc0, 0x39); | ||
| 765 | } else if (state->cec_enabled_adap && !enable) { | ||
| 766 | if (state->enabled_irq) | ||
| 767 | adv7511_wr_and_or(sd, 0x95, 0xc0, 0x00); | ||
| 768 | /* disable address mask 1-3 */ | ||
| 769 | adv7511_cec_write_and_or(sd, 0x4b, 0x8f, 0x00); | ||
| 770 | /* power down cec section */ | ||
| 771 | adv7511_cec_write_and_or(sd, 0x4e, 0xfc, 0x00); | ||
| 772 | state->cec_valid_addrs = 0; | ||
| 773 | } | ||
| 774 | state->cec_enabled_adap = enable; | ||
| 775 | return 0; | ||
| 776 | } | ||
| 777 | |||
| 778 | static int adv7511_cec_adap_log_addr(struct cec_adapter *adap, u8 addr) | ||
| 779 | { | ||
| 780 | struct adv7511_state *state = adap->priv; | ||
| 781 | struct v4l2_subdev *sd = &state->sd; | ||
| 782 | unsigned int i, free_idx = ADV7511_MAX_ADDRS; | ||
| 783 | |||
| 784 | if (!state->cec_enabled_adap) | ||
| 785 | return addr == CEC_LOG_ADDR_INVALID ? 0 : -EIO; | ||
| 786 | |||
| 787 | if (addr == CEC_LOG_ADDR_INVALID) { | ||
| 788 | adv7511_cec_write_and_or(sd, 0x4b, 0x8f, 0); | ||
| 789 | state->cec_valid_addrs = 0; | ||
| 790 | return 0; | ||
| 791 | } | ||
| 792 | |||
| 793 | for (i = 0; i < ADV7511_MAX_ADDRS; i++) { | ||
| 794 | bool is_valid = state->cec_valid_addrs & (1 << i); | ||
| 795 | |||
| 796 | if (free_idx == ADV7511_MAX_ADDRS && !is_valid) | ||
| 797 | free_idx = i; | ||
| 798 | if (is_valid && state->cec_addr[i] == addr) | ||
| 799 | return 0; | ||
| 800 | } | ||
| 801 | if (i == ADV7511_MAX_ADDRS) { | ||
| 802 | i = free_idx; | ||
| 803 | if (i == ADV7511_MAX_ADDRS) | ||
| 804 | return -ENXIO; | ||
| 805 | } | ||
| 806 | state->cec_addr[i] = addr; | ||
| 807 | state->cec_valid_addrs |= 1 << i; | ||
| 808 | |||
| 809 | switch (i) { | ||
| 810 | case 0: | ||
| 811 | /* enable address mask 0 */ | ||
| 812 | adv7511_cec_write_and_or(sd, 0x4b, 0xef, 0x10); | ||
| 813 | /* set address for mask 0 */ | ||
| 814 | adv7511_cec_write_and_or(sd, 0x4c, 0xf0, addr); | ||
| 815 | break; | ||
| 816 | case 1: | ||
| 817 | /* enable address mask 1 */ | ||
| 818 | adv7511_cec_write_and_or(sd, 0x4b, 0xdf, 0x20); | ||
| 819 | /* set address for mask 1 */ | ||
| 820 | adv7511_cec_write_and_or(sd, 0x4c, 0x0f, addr << 4); | ||
| 821 | break; | ||
| 822 | case 2: | ||
| 823 | /* enable address mask 2 */ | ||
| 824 | adv7511_cec_write_and_or(sd, 0x4b, 0xbf, 0x40); | ||
| 825 | /* set address for mask 1 */ | ||
| 826 | adv7511_cec_write_and_or(sd, 0x4d, 0xf0, addr); | ||
| 827 | break; | ||
| 828 | } | ||
| 829 | return 0; | ||
| 830 | } | ||
| 831 | |||
| 832 | static int adv7511_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, | ||
| 833 | u32 signal_free_time, struct cec_msg *msg) | ||
| 834 | { | ||
| 835 | struct adv7511_state *state = adap->priv; | ||
| 836 | struct v4l2_subdev *sd = &state->sd; | ||
| 837 | u8 len = msg->len; | ||
| 838 | unsigned int i; | ||
| 839 | |||
| 840 | v4l2_dbg(1, debug, sd, "%s: len %d\n", __func__, len); | ||
| 841 | |||
| 842 | if (len > 16) { | ||
| 843 | v4l2_err(sd, "%s: len exceeded 16 (%d)\n", __func__, len); | ||
| 844 | return -EINVAL; | ||
| 845 | } | ||
| 846 | |||
| 847 | /* | ||
| 848 | * The number of retries is the number of attempts - 1, but retry | ||
| 849 | * at least once. It's not clear if a value of 0 is allowed, so | ||
| 850 | * let's do at least one retry. | ||
| 851 | */ | ||
| 852 | adv7511_cec_write_and_or(sd, 0x12, ~0x70, max(1, attempts - 1) << 4); | ||
| 853 | |||
| 854 | /* blocking, clear cec tx irq status */ | ||
| 855 | adv7511_wr_and_or(sd, 0x97, 0xc7, 0x38); | ||
| 856 | |||
| 857 | /* write data */ | ||
| 858 | for (i = 0; i < len; i++) | ||
| 859 | adv7511_cec_write(sd, i, msg->msg[i]); | ||
| 860 | |||
| 861 | /* set length (data + header) */ | ||
| 862 | adv7511_cec_write(sd, 0x10, len); | ||
| 863 | /* start transmit, enable tx */ | ||
| 864 | adv7511_cec_write(sd, 0x11, 0x01); | ||
| 865 | return 0; | ||
| 866 | } | ||
| 867 | |||
| 868 | static void adv_cec_tx_raw_status(struct v4l2_subdev *sd, u8 tx_raw_status) | ||
| 869 | { | ||
| 870 | struct adv7511_state *state = get_adv7511_state(sd); | ||
| 871 | |||
| 872 | if ((adv7511_cec_read(sd, 0x11) & 0x01) == 0) { | ||
| 873 | v4l2_dbg(1, debug, sd, "%s: tx raw: tx disabled\n", __func__); | ||
| 874 | return; | ||
| 875 | } | ||
| 876 | |||
| 877 | if (tx_raw_status & 0x10) { | ||
| 878 | v4l2_dbg(1, debug, sd, | ||
| 879 | "%s: tx raw: arbitration lost\n", __func__); | ||
| 880 | cec_transmit_done(state->cec_adap, CEC_TX_STATUS_ARB_LOST, | ||
| 881 | 1, 0, 0, 0); | ||
| 882 | return; | ||
| 883 | } | ||
| 884 | if (tx_raw_status & 0x08) { | ||
| 885 | u8 status; | ||
| 886 | u8 nack_cnt; | ||
| 887 | u8 low_drive_cnt; | ||
| 888 | |||
| 889 | v4l2_dbg(1, debug, sd, "%s: tx raw: retry failed\n", __func__); | ||
| 890 | /* | ||
| 891 | * We set this status bit since this hardware performs | ||
| 892 | * retransmissions. | ||
| 893 | */ | ||
| 894 | status = CEC_TX_STATUS_MAX_RETRIES; | ||
| 895 | nack_cnt = adv7511_cec_read(sd, 0x14) & 0xf; | ||
| 896 | if (nack_cnt) | ||
| 897 | status |= CEC_TX_STATUS_NACK; | ||
| 898 | low_drive_cnt = adv7511_cec_read(sd, 0x14) >> 4; | ||
| 899 | if (low_drive_cnt) | ||
| 900 | status |= CEC_TX_STATUS_LOW_DRIVE; | ||
| 901 | cec_transmit_done(state->cec_adap, status, | ||
| 902 | 0, nack_cnt, low_drive_cnt, 0); | ||
| 903 | return; | ||
| 904 | } | ||
| 905 | if (tx_raw_status & 0x20) { | ||
| 906 | v4l2_dbg(1, debug, sd, "%s: tx raw: ready ok\n", __func__); | ||
| 907 | cec_transmit_done(state->cec_adap, CEC_TX_STATUS_OK, 0, 0, 0, 0); | ||
| 908 | return; | ||
| 909 | } | ||
| 910 | } | ||
| 911 | |||
| 912 | static const struct cec_adap_ops adv7511_cec_adap_ops = { | ||
| 913 | .adap_enable = adv7511_cec_adap_enable, | ||
| 914 | .adap_log_addr = adv7511_cec_adap_log_addr, | ||
| 915 | .adap_transmit = adv7511_cec_adap_transmit, | ||
| 916 | }; | ||
| 917 | #endif | ||
| 918 | |||
| 666 | /* Enable interrupts */ | 919 | /* Enable interrupts */ |
| 667 | static void adv7511_set_isr(struct v4l2_subdev *sd, bool enable) | 920 | static void adv7511_set_isr(struct v4l2_subdev *sd, bool enable) |
| 668 | { | 921 | { |
| 922 | struct adv7511_state *state = get_adv7511_state(sd); | ||
| 669 | u8 irqs = MASK_ADV7511_HPD_INT | MASK_ADV7511_MSEN_INT; | 923 | u8 irqs = MASK_ADV7511_HPD_INT | MASK_ADV7511_MSEN_INT; |
| 670 | u8 irqs_rd; | 924 | u8 irqs_rd; |
| 671 | int retries = 100; | 925 | int retries = 100; |
| 672 | 926 | ||
| 673 | v4l2_dbg(2, debug, sd, "%s: %s\n", __func__, enable ? "enable" : "disable"); | 927 | v4l2_dbg(2, debug, sd, "%s: %s\n", __func__, enable ? "enable" : "disable"); |
| 674 | 928 | ||
| 929 | if (state->enabled_irq == enable) | ||
| 930 | return; | ||
| 931 | state->enabled_irq = enable; | ||
| 932 | |||
| 675 | /* The datasheet says that the EDID ready interrupt should be | 933 | /* The datasheet says that the EDID ready interrupt should be |
| 676 | disabled if there is no hotplug. */ | 934 | disabled if there is no hotplug. */ |
| 677 | if (!enable) | 935 | if (!enable) |
| @@ -679,6 +937,9 @@ static void adv7511_set_isr(struct v4l2_subdev *sd, bool enable) · 1 unmodified line | |||
| 679 | else if (adv7511_have_hotplug(sd)) | 937 | else if (adv7511_have_hotplug(sd)) |
| 680 | irqs |= MASK_ADV7511_EDID_RDY_INT; | 938 | irqs |= MASK_ADV7511_EDID_RDY_INT; |
| 681 | 939 | ||
| 940 | adv7511_wr_and_or(sd, 0x95, 0xc0, | ||
| 941 | (state->cec_enabled_adap && enable) ? 0x39 : 0x00); | ||
| 942 | |||
| 682 | /* | 943 | /* |
| 683 | * This i2c write can fail (approx. 1 in 1000 writes). But it | 944 | * This i2c write can fail (approx. 1 in 1000 writes). But it |
| 684 | * is essential that this register is correct, so retry it | 945 | * 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) · 16 unmodified lines | |||
| 701 | static int adv7511_isr(struct v4l2_subdev *sd, u32 status, bool *handled) | 962 | static int adv7511_isr(struct v4l2_subdev *sd, u32 status, bool *handled) |
| 702 | { | 963 | { |
| 703 | u8 irq_status; | 964 | u8 irq_status; |
| 965 | u8 cec_irq; | ||
| 704 | 966 | ||
| 705 | /* disable interrupts to prevent a race condition */ | 967 | /* disable interrupts to prevent a race condition */ |
| 706 | adv7511_set_isr(sd, false); | 968 | adv7511_set_isr(sd, false); |
| 707 | irq_status = adv7511_rd(sd, 0x96); | 969 | irq_status = adv7511_rd(sd, 0x96); |
| 970 | cec_irq = adv7511_rd(sd, 0x97); | ||
| 708 | /* clear detected interrupts */ | 971 | /* clear detected interrupts */ |
| 709 | adv7511_wr(sd, 0x96, irq_status); | 972 | adv7511_wr(sd, 0x96, irq_status); |
| 973 | adv7511_wr(sd, 0x97, cec_irq); | ||
| 710 | 974 | ||
| 711 | v4l2_dbg(1, debug, sd, "%s: irq 0x%x\n", __func__, irq_status); | 975 | v4l2_dbg(1, debug, sd, "%s: irq 0x%x, cec-irq 0x%x\n", __func__, |
| 976 | irq_status, cec_irq); | ||
| 712 | 977 | ||
| 713 | if (irq_status & (MASK_ADV7511_HPD_INT | MASK_ADV7511_MSEN_INT)) | 978 | if (irq_status & (MASK_ADV7511_HPD_INT | MASK_ADV7511_MSEN_INT)) |
| 714 | adv7511_check_monitor_present_status(sd); | 979 | adv7511_check_monitor_present_status(sd); |
| 715 | if (irq_status & MASK_ADV7511_EDID_RDY_INT) | 980 | if (irq_status & MASK_ADV7511_EDID_RDY_INT) |
| 716 | adv7511_check_edid_status(sd); | 981 | adv7511_check_edid_status(sd); |
| 717 | 982 | ||
| 983 | #if IS_ENABLED(CONFIG_VIDEO_ADV7511_CEC) | ||
| 984 | if (cec_irq & 0x38) | ||
| 985 | adv_cec_tx_raw_status(sd, cec_irq); | ||
| 986 | |||
| 987 | if (cec_irq & 1) { | ||
| 988 | struct adv7511_state *state = get_adv7511_state(sd); | ||
| 989 | struct cec_msg msg; | ||
| 990 | |||
| 991 | msg.len = adv7511_cec_read(sd, 0x25) & 0x1f; | ||
| 992 | |||
| 993 | v4l2_dbg(1, debug, sd, "%s: cec msg len %d\n", __func__, | ||
| 994 | msg.len); | ||
| 995 | |||
| 996 | if (msg.len > 16) | ||
| 997 | msg.len = 16; | ||
| 998 | |||
| 999 | if (msg.len) { | ||
| 1000 | u8 i; | ||
| 1001 | |||
| 1002 | for (i = 0; i < msg.len; i++) | ||
| 1003 | msg.msg[i] = adv7511_cec_read(sd, i + 0x15); | ||
| 1004 | |||
| 1005 | adv7511_cec_write(sd, 0x4a, 1); /* toggle to re-enable rx 1 */ | ||
| 1006 | adv7511_cec_write(sd, 0x4a, 0); | ||
| 1007 | cec_received_msg(state->cec_adap, &msg); | ||
| 1008 | } | ||
| 1009 | } | ||
| 1010 | #endif | ||
| 1011 | |||
| 718 | /* enable interrupts */ | 1012 | /* enable interrupts */ |
| 719 | adv7511_set_isr(sd, true); | 1013 | adv7511_set_isr(sd, true); |
| 720 | 1014 | ||
| @@ -1183,6 +1477,8 @@ static void adv7511_notify_no_edid(struct v4l2_subdev *sd) · 462 unmodified lines | |||
| 1183 | /* We failed to read the EDID, so send an event for this. */ | 1477 | /* We failed to read the EDID, so send an event for this. */ |
| 1184 | ed.present = false; | 1478 | ed.present = false; |
| 1185 | ed.segment = adv7511_rd(sd, 0xc4); | 1479 | ed.segment = adv7511_rd(sd, 0xc4); |
| 1480 | ed.phys_addr = CEC_PHYS_ADDR_INVALID; | ||
| 1481 | cec_s_phys_addr(state->cec_adap, ed.phys_addr, false); | ||
| 1186 | v4l2_subdev_notify(sd, ADV7511_EDID_DETECT, (void *)&ed); | 1482 | v4l2_subdev_notify(sd, ADV7511_EDID_DETECT, (void *)&ed); |
| 1187 | v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, 0x0); | 1483 | v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, 0x0); |
| 1188 | } | 1484 | } |
| @@ -1406,13 +1702,16 @@ static bool adv7511_check_edid_status(struct v4l2_subdev *sd) · 217 unmodified lines | |||
| 1406 | 1702 | ||
| 1407 | v4l2_dbg(1, debug, sd, "%s: edid complete with %d segment(s)\n", __func__, state->edid.segments); | 1703 | v4l2_dbg(1, debug, sd, "%s: edid complete with %d segment(s)\n", __func__, state->edid.segments); |
| 1408 | state->edid.complete = true; | 1704 | state->edid.complete = true; |
| 1409 | 1705 | ed.phys_addr = cec_get_edid_phys_addr(state->edid.data, | |
| 1706 | state->edid.segments * 256, | ||
| 1707 | NULL); | ||
| 1410 | /* report when we have all segments | 1708 | /* report when we have all segments |
| 1411 | but report only for segment 0 | 1709 | but report only for segment 0 |
| 1412 | */ | 1710 | */ |
| 1413 | ed.present = true; | 1711 | ed.present = true; |
| 1414 | ed.segment = 0; | 1712 | ed.segment = 0; |
| 1415 | state->edid_detect_counter++; | 1713 | state->edid_detect_counter++; |
| 1714 | cec_s_phys_addr(state->cec_adap, ed.phys_addr, false); | ||
| 1416 | v4l2_subdev_notify(sd, ADV7511_EDID_DETECT, (void *)&ed); | 1715 | v4l2_subdev_notify(sd, ADV7511_EDID_DETECT, (void *)&ed); |
| 1417 | return ed.present; | 1716 | return ed.present; |
| 1418 | } | 1717 | } |
| @@ -1420,17 +1719,43 @@ static bool adv7511_check_edid_status(struct v4l2_subdev *sd) · 1 unmodified line | |||
| 1420 | return false; | 1719 | return false; |
| 1421 | } | 1720 | } |
| 1422 | 1721 | ||
| 1722 | static int adv7511_registered(struct v4l2_subdev *sd) | ||
| 1723 | { | ||
| 1724 | struct adv7511_state *state = get_adv7511_state(sd); | ||
| 1725 | int err; | ||
| 1726 | |||
| 1727 | err = cec_register_adapter(state->cec_adap); | ||
| 1728 | if (err) | ||
| 1729 | cec_delete_adapter(state->cec_adap); | ||
| 1730 | return err; | ||
| 1731 | } | ||
| 1732 | |||
| 1733 | static void adv7511_unregistered(struct v4l2_subdev *sd) | ||
| 1734 | { | ||
| 1735 | struct adv7511_state *state = get_adv7511_state(sd); | ||
| 1736 | |||
| 1737 | cec_unregister_adapter(state->cec_adap); | ||
| 1738 | } | ||
| 1739 | |||
| 1740 | static const struct v4l2_subdev_internal_ops adv7511_int_ops = { | ||
| 1741 | .registered = adv7511_registered, | ||
| 1742 | .unregistered = adv7511_unregistered, | ||
| 1743 | }; | ||
| 1744 | |||
| 1423 | /* ----------------------------------------------------------------------- */ | 1745 | /* ----------------------------------------------------------------------- */ |
| 1424 | /* Setup ADV7511 */ | 1746 | /* Setup ADV7511 */ |
| 1425 | static void adv7511_init_setup(struct v4l2_subdev *sd) | 1747 | static void adv7511_init_setup(struct v4l2_subdev *sd) |
| 1426 | { | 1748 | { |
| 1427 | struct adv7511_state *state = get_adv7511_state(sd); | 1749 | struct adv7511_state *state = get_adv7511_state(sd); |
| 1428 | struct adv7511_state_edid *edid = &state->edid; | 1750 | struct adv7511_state_edid *edid = &state->edid; |
| 1751 | u32 cec_clk = state->pdata.cec_clk; | ||
| 1752 | u8 ratio; | ||
| 1429 | 1753 | ||
| 1430 | v4l2_dbg(1, debug, sd, "%s\n", __func__); | 1754 | v4l2_dbg(1, debug, sd, "%s\n", __func__); |
| 1431 | 1755 | ||
| 1432 | /* clear all interrupts */ | 1756 | /* clear all interrupts */ |
| 1433 | adv7511_wr(sd, 0x96, 0xff); | 1757 | adv7511_wr(sd, 0x96, 0xff); |
| 1758 | adv7511_wr(sd, 0x97, 0xff); | ||
| 1434 | /* | 1759 | /* |
| 1435 | * Stop HPD from resetting a lot of registers. | 1760 | * Stop HPD from resetting a lot of registers. |
| 1436 | * It might leave the chip in a partly un-initialized state, | 1761 | * It might leave the chip in a partly un-initialized state, |
| @@ -1442,6 +1767,25 @@ static void adv7511_init_setup(struct v4l2_subdev *sd) · 5 unmodified lines | |||
| 1442 | adv7511_set_isr(sd, false); | 1767 | adv7511_set_isr(sd, false); |
| 1443 | adv7511_s_stream(sd, false); | 1768 | adv7511_s_stream(sd, false); |
| 1444 | adv7511_s_audio_stream(sd, false); | 1769 | adv7511_s_audio_stream(sd, false); |
| 1770 | |||
| 1771 | if (state->i2c_cec == NULL) | ||
| 1772 | return; | ||
| 1773 | |||
| 1774 | v4l2_dbg(1, debug, sd, "%s: cec_clk %d\n", __func__, cec_clk); | ||
| 1775 | |||
| 1776 | /* cec soft reset */ | ||
| 1777 | adv7511_cec_write(sd, 0x50, 0x01); | ||
| 1778 | adv7511_cec_write(sd, 0x50, 0x00); | ||
| 1779 | |||
| 1780 | /* legacy mode */ | ||
| 1781 | adv7511_cec_write(sd, 0x4a, 0x00); | ||
| 1782 | |||
| 1783 | if (cec_clk % 750000 != 0) | ||
| 1784 | v4l2_err(sd, "%s: cec_clk %d, not multiple of 750 Khz\n", | ||
| 1785 | __func__, cec_clk); | ||
| 1786 | |||
| 1787 | ratio = (cec_clk / 750000) - 1; | ||
| 1788 | adv7511_cec_write(sd, 0x4e, ratio << 2); | ||
| 1445 | } | 1789 | } |
| 1446 | 1790 | ||
| 1447 | static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *id) | 1791 | 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 * · 28 unmodified lines | |||
| 1476 | client->addr << 1); | 1820 | client->addr << 1); |
| 1477 | 1821 | ||
| 1478 | v4l2_i2c_subdev_init(sd, client, &adv7511_ops); | 1822 | v4l2_i2c_subdev_init(sd, client, &adv7511_ops); |
| 1823 | sd->internal_ops = &adv7511_int_ops; | ||
| 1479 | 1824 | ||
| 1480 | hdl = &state->hdl; | 1825 | hdl = &state->hdl; |
| 1481 | v4l2_ctrl_handler_init(hdl, 10); | 1826 | v4l2_ctrl_handler_init(hdl, 10); |
| @@ -1516,26 +1861,47 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id * · 34 unmodified lines | |||
| 1516 | chip_id[0] = adv7511_rd(sd, 0xf5); | 1861 | chip_id[0] = adv7511_rd(sd, 0xf5); |
| 1517 | chip_id[1] = adv7511_rd(sd, 0xf6); | 1862 | chip_id[1] = adv7511_rd(sd, 0xf6); |
| 1518 | if (chip_id[0] != 0x75 || chip_id[1] != 0x11) { | 1863 | if (chip_id[0] != 0x75 || chip_id[1] != 0x11) { |
| 1519 | v4l2_err(sd, "chip_id != 0x7511, read 0x%02x%02x\n", chip_id[0], chip_id[1]); | 1864 | v4l2_err(sd, "chip_id != 0x7511, read 0x%02x%02x\n", chip_id[0], |
| 1865 | chip_id[1]); | ||
| 1520 | err = -EIO; | 1866 | err = -EIO; |
| 1521 | goto err_entity; | 1867 | goto err_entity; |
| 1522 | } | 1868 | } |
| 1523 | 1869 | ||
| 1524 | state->i2c_edid = i2c_new_dummy(client->adapter, state->i2c_edid_addr >> 1); | 1870 | state->i2c_edid = i2c_new_dummy(client->adapter, |
| 1871 | state->i2c_edid_addr >> 1); | ||
| 1525 | if (state->i2c_edid == NULL) { | 1872 | if (state->i2c_edid == NULL) { |
| 1526 | v4l2_err(sd, "failed to register edid i2c client\n"); | 1873 | v4l2_err(sd, "failed to register edid i2c client\n"); |
| 1527 | err = -ENOMEM; | 1874 | err = -ENOMEM; |
| 1528 | goto err_entity; | 1875 | goto err_entity; |
| 1529 | } | 1876 | } |
| 1530 | 1877 | ||
| 1878 | adv7511_wr(sd, 0xe1, state->i2c_cec_addr); | ||
| 1879 | if (state->pdata.cec_clk < 3000000 || | ||
| 1880 | state->pdata.cec_clk > 100000000) { | ||
| 1881 | v4l2_err(sd, "%s: cec_clk %u outside range, disabling cec\n", | ||
| 1882 | __func__, state->pdata.cec_clk); | ||
| 1883 | state->pdata.cec_clk = 0; | ||
| 1884 | } | ||
| 1885 | |||
| 1886 | if (state->pdata.cec_clk) { | ||
| 1887 | state->i2c_cec = i2c_new_dummy(client->adapter, | ||
| 1888 | state->i2c_cec_addr >> 1); | ||
| 1889 | if (state->i2c_cec == NULL) { | ||
| 1890 | v4l2_err(sd, "failed to register cec i2c client\n"); | ||
| 1891 | goto err_unreg_edid; | ||
| 1892 | } | ||
| 1893 | adv7511_wr(sd, 0xe2, 0x00); /* power up cec section */ | ||
| 1894 | } else { | ||
| 1895 | adv7511_wr(sd, 0xe2, 0x01); /* power down cec section */ | ||
| 1896 | } | ||
| 1897 | |||
| 1531 | state->i2c_pktmem = i2c_new_dummy(client->adapter, state->i2c_pktmem_addr >> 1); | 1898 | state->i2c_pktmem = i2c_new_dummy(client->adapter, state->i2c_pktmem_addr >> 1); |
| 1532 | if (state->i2c_pktmem == NULL) { | 1899 | if (state->i2c_pktmem == NULL) { |
| 1533 | v4l2_err(sd, "failed to register pktmem i2c client\n"); | 1900 | v4l2_err(sd, "failed to register pktmem i2c client\n"); |
| 1534 | err = -ENOMEM; | 1901 | err = -ENOMEM; |
| 1535 | goto err_unreg_edid; | 1902 | goto err_unreg_cec; |
| 1536 | } | 1903 | } |
| 1537 | 1904 | ||
| 1538 | adv7511_wr(sd, 0xe2, 0x01); /* power down cec section */ | ||
| 1539 | state->work_queue = create_singlethread_workqueue(sd->name); | 1905 | state->work_queue = create_singlethread_workqueue(sd->name); |
| 1540 | if (state->work_queue == NULL) { | 1906 | if (state->work_queue == NULL) { |
| 1541 | v4l2_err(sd, "could not create workqueue\n"); | 1907 | 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 * · 4 unmodified lines | |||
| 1546 | INIT_DELAYED_WORK(&state->edid_handler, adv7511_edid_handler); | 1912 | INIT_DELAYED_WORK(&state->edid_handler, adv7511_edid_handler); |
| 1547 | 1913 | ||
| 1548 | adv7511_init_setup(sd); | 1914 | adv7511_init_setup(sd); |
| 1915 | |||
| 1916 | #if IS_ENABLED(CONFIG_VIDEO_ADV7511_CEC) | ||
| 1917 | state->cec_adap = cec_allocate_adapter(&adv7511_cec_adap_ops, | ||
| 1918 | state, dev_name(&client->dev), CEC_CAP_TRANSMIT | | ||
| 1919 | CEC_CAP_LOG_ADDRS | CEC_CAP_PASSTHROUGH | CEC_CAP_RC, | ||
| 1920 | ADV7511_MAX_ADDRS, &client->dev); | ||
| 1921 | err = PTR_ERR_OR_ZERO(state->cec_adap); | ||
| 1922 | if (err) { | ||
| 1923 | destroy_workqueue(state->work_queue); | ||
| 1924 | goto err_unreg_pktmem; | ||
| 1925 | } | ||
| 1926 | #endif | ||
| 1927 | |||
| 1549 | adv7511_set_isr(sd, true); | 1928 | adv7511_set_isr(sd, true); |
| 1550 | adv7511_check_monitor_present_status(sd); | 1929 | adv7511_check_monitor_present_status(sd); |
| 1551 | 1930 | ||
| @@ -1555,6 +1934,9 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id * · 3 unmodified lines | |||
| 1555 | 1934 | ||
| 1556 | err_unreg_pktmem: | 1935 | err_unreg_pktmem: |
| 1557 | i2c_unregister_device(state->i2c_pktmem); | 1936 | i2c_unregister_device(state->i2c_pktmem); |
| 1937 | err_unreg_cec: | ||
| 1938 | if (state->i2c_cec) | ||
| 1939 | i2c_unregister_device(state->i2c_cec); | ||
| 1558 | err_unreg_edid: | 1940 | err_unreg_edid: |
| 1559 | i2c_unregister_device(state->i2c_edid); | 1941 | i2c_unregister_device(state->i2c_edid); |
| 1560 | err_entity: | 1942 | err_entity: |
| @@ -1576,9 +1958,12 @@ static int adv7511_remove(struct i2c_client *client) · 15 unmodified lines | |||
| 1576 | v4l2_dbg(1, debug, sd, "%s removed @ 0x%x (%s)\n", client->name, | 1958 | v4l2_dbg(1, debug, sd, "%s removed @ 0x%x (%s)\n", client->name, |
| 1577 | client->addr << 1, client->adapter->name); | 1959 | client->addr << 1, client->adapter->name); |
| 1578 | 1960 | ||
| 1961 | adv7511_set_isr(sd, false); | ||
| 1579 | adv7511_init_setup(sd); | 1962 | adv7511_init_setup(sd); |
| 1580 | cancel_delayed_work(&state->edid_handler); | 1963 | cancel_delayed_work(&state->edid_handler); |
| 1581 | i2c_unregister_device(state->i2c_edid); | 1964 | i2c_unregister_device(state->i2c_edid); |
| 1965 | if (state->i2c_cec) | ||
| 1966 | i2c_unregister_device(state->i2c_cec); | ||
| 1582 | i2c_unregister_device(state->i2c_pktmem); | 1967 | i2c_unregister_device(state->i2c_pktmem); |
| 1583 | destroy_workqueue(state->work_queue); | 1968 | destroy_workqueue(state->work_queue); |
| 1584 | v4l2_device_unregister_subdev(sd); | 1969 | v4l2_device_unregister_subdev(sd); |
| @@ -40,6 +40,7 @@ · 39 unmodified lines | |||
| 40 | #include <linux/regmap.h> | 40 | #include <linux/regmap.h> |
| 41 | 41 | ||
| 42 | #include <media/i2c/adv7604.h> | 42 | #include <media/i2c/adv7604.h> |
| 43 | #include <media/cec.h> | ||
| 43 | #include <media/v4l2-ctrls.h> | 44 | #include <media/v4l2-ctrls.h> |
| 44 | #include <media/v4l2-device.h> | 45 | #include <media/v4l2-device.h> |
| 45 | #include <media/v4l2-event.h> | 46 | #include <media/v4l2-event.h> |
| @@ -80,6 +81,8 @@ MODULE_LICENSE("GPL"); · 34 unmodified lines | |||
| 80 | 81 | ||
| 81 | #define ADV76XX_OP_SWAP_CB_CR (1 << 0) | 82 | #define ADV76XX_OP_SWAP_CB_CR (1 << 0) |
| 82 | 83 | ||
| 84 | #define ADV76XX_MAX_ADDRS (3) | ||
| 85 | |||
| 83 | enum adv76xx_type { | 86 | enum adv76xx_type { |
| 84 | ADV7604, | 87 | ADV7604, |
| 85 | ADV7611, | 88 | ADV7611, |
| @@ -184,6 +187,12 @@ struct adv76xx_state { · 98 unmodified lines | |||
| 184 | u16 spa_port_a[2]; | 187 | u16 spa_port_a[2]; |
| 185 | struct v4l2_fract aspect_ratio; | 188 | struct v4l2_fract aspect_ratio; |
| 186 | u32 rgb_quantization_range; | 189 | u32 rgb_quantization_range; |
| 190 | |||
| 191 | struct cec_adapter *cec_adap; | ||
| 192 | u8 cec_addr[ADV76XX_MAX_ADDRS]; | ||
| 193 | u8 cec_valid_addrs; | ||
| 194 | bool cec_enabled_adap; | ||
| 195 | |||
| 187 | struct workqueue_struct *work_queues; | 196 | struct workqueue_struct *work_queues; |
| 188 | struct delayed_work delayed_work_enable_hotplug; | 197 | struct delayed_work delayed_work_enable_hotplug; |
| 189 | bool restart_stdi_once; | 198 | bool restart_stdi_once; |
| @@ -381,7 +390,8 @@ static inline int io_write(struct v4l2_subdev *sd, u8 reg, u8 val) · 191 unmodified lines | |||
| 381 | return regmap_write(state->regmap[ADV76XX_PAGE_IO], reg, val); | 390 | return regmap_write(state->regmap[ADV76XX_PAGE_IO], reg, val); |
| 382 | } | 391 | } |
| 383 | 392 | ||
| 384 | static inline int io_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) | 393 | static inline int io_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, |
| 394 | u8 val) | ||
| 385 | { | 395 | { |
| 386 | return io_write(sd, reg, (io_read(sd, reg) & ~mask) | val); | 396 | return io_write(sd, reg, (io_read(sd, reg) & ~mask) | val); |
| 387 | } | 397 | } |
| @@ -414,6 +424,12 @@ static inline int cec_write(struct v4l2_subdev *sd, u8 reg, u8 val) · 26 unmodified lines | |||
| 414 | return regmap_write(state->regmap[ADV76XX_PAGE_CEC], reg, val); | 424 | return regmap_write(state->regmap[ADV76XX_PAGE_CEC], reg, val); |
| 415 | } | 425 | } |
| 416 | 426 | ||
| 427 | static inline int cec_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, | ||
| 428 | u8 val) | ||
| 429 | { | ||
| 430 | return cec_write(sd, reg, (cec_read(sd, reg) & ~mask) | val); | ||
| 431 | } | ||
| 432 | |||
| 417 | static inline int infoframe_read(struct v4l2_subdev *sd, u8 reg) | 433 | static inline int infoframe_read(struct v4l2_subdev *sd, u8 reg) |
| 418 | { | 434 | { |
| 419 | struct adv76xx_state *state = to_state(sd); | 435 | struct adv76xx_state *state = to_state(sd); |
| @@ -892,9 +908,9 @@ static int adv76xx_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd) · 472 unmodified lines | |||
| 892 | { | 908 | { |
| 893 | struct adv76xx_state *state = to_state(sd); | 909 | struct adv76xx_state *state = to_state(sd); |
| 894 | const struct adv76xx_chip_info *info = state->info; | 910 | const struct adv76xx_chip_info *info = state->info; |
| 911 | u16 cable_det = info->read_cable_det(sd); | ||
| 895 | 912 | ||
| 896 | return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, | 913 | return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, cable_det); |
| 897 | info->read_cable_det(sd)); | ||
| 898 | } | 914 | } |
| 899 | 915 | ||
| 900 | static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd, | 916 | static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd, |
| @@ -1924,6 +1940,210 @@ static int adv76xx_set_format(struct v4l2_subdev *sd, · 1023 unmodified lines | |||
| 1924 | return 0; | 1940 | return 0; |
| 1925 | } | 1941 | } |
| 1926 | 1942 | ||
| 1943 | #if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC) | ||
| 1944 | static void adv76xx_cec_tx_raw_status(struct v4l2_subdev *sd, u8 tx_raw_status) | ||
| 1945 | { | ||
| 1946 | struct adv76xx_state *state = to_state(sd); | ||
| 1947 | |||
| 1948 | if ((cec_read(sd, 0x11) & 0x01) == 0) { | ||
| 1949 | v4l2_dbg(1, debug, sd, "%s: tx raw: tx disabled\n", __func__); | ||
| 1950 | return; | ||
| 1951 | } | ||
| 1952 | |||
| 1953 | if (tx_raw_status & 0x02) { | ||
| 1954 | v4l2_dbg(1, debug, sd, "%s: tx raw: arbitration lost\n", | ||
| 1955 | __func__); | ||
| 1956 | cec_transmit_done(state->cec_adap, CEC_TX_STATUS_ARB_LOST, | ||
| 1957 | 1, 0, 0, 0); | ||
| 1958 | } | ||
| 1959 | if (tx_raw_status & 0x04) { | ||
| 1960 | u8 status; | ||
| 1961 | u8 nack_cnt; | ||
| 1962 | u8 low_drive_cnt; | ||
| 1963 | |||
| 1964 | v4l2_dbg(1, debug, sd, "%s: tx raw: retry failed\n", __func__); | ||
| 1965 | /* | ||
| 1966 | * We set this status bit since this hardware performs | ||
| 1967 | * retransmissions. | ||
| 1968 | */ | ||
| 1969 | status = CEC_TX_STATUS_MAX_RETRIES; | ||
| 1970 | nack_cnt = cec_read(sd, 0x14) & 0xf; | ||
| 1971 | if (nack_cnt) | ||
| 1972 | status |= CEC_TX_STATUS_NACK; | ||
| 1973 | low_drive_cnt = cec_read(sd, 0x14) >> 4; | ||
| 1974 | if (low_drive_cnt) | ||
| 1975 | status |= CEC_TX_STATUS_LOW_DRIVE; | ||
| 1976 | cec_transmit_done(state->cec_adap, status, | ||
| 1977 | 0, nack_cnt, low_drive_cnt, 0); | ||
| 1978 | return; | ||
| 1979 | } | ||
| 1980 | if (tx_raw_status & 0x01) { | ||
| 1981 | v4l2_dbg(1, debug, sd, "%s: tx raw: ready ok\n", __func__); | ||
| 1982 | cec_transmit_done(state->cec_adap, CEC_TX_STATUS_OK, 0, 0, 0, 0); | ||
| 1983 | return; | ||
| 1984 | } | ||
| 1985 | } | ||
| 1986 | |||
| 1987 | static void adv76xx_cec_isr(struct v4l2_subdev *sd, bool *handled) | ||
| 1988 | { | ||
| 1989 | struct adv76xx_state *state = to_state(sd); | ||
| 1990 | u8 cec_irq; | ||
| 1991 | |||
| 1992 | /* cec controller */ | ||
| 1993 | cec_irq = io_read(sd, 0x4d) & 0x0f; | ||
| 1994 | if (!cec_irq) | ||
| 1995 | return; | ||
| 1996 | |||
| 1997 | v4l2_dbg(1, debug, sd, "%s: cec: irq 0x%x\n", __func__, cec_irq); | ||
| 1998 | adv76xx_cec_tx_raw_status(sd, cec_irq); | ||
| 1999 | if (cec_irq & 0x08) { | ||
| 2000 | struct cec_msg msg; | ||
| 2001 | |||
| 2002 | msg.len = cec_read(sd, 0x25) & 0x1f; | ||
| 2003 | if (msg.len > 16) | ||
| 2004 | msg.len = 16; | ||
| 2005 | |||
| 2006 | if (msg.len) { | ||
| 2007 | u8 i; | ||
| 2008 | |||
| 2009 | for (i = 0; i < msg.len; i++) | ||
| 2010 | msg.msg[i] = cec_read(sd, i + 0x15); | ||
| 2011 | cec_write(sd, 0x26, 0x01); /* re-enable rx */ | ||
| 2012 | cec_received_msg(state->cec_adap, &msg); | ||
| 2013 | } | ||
| 2014 | } | ||
| 2015 | |||
| 2016 | /* note: the bit order is swapped between 0x4d and 0x4e */ | ||
| 2017 | cec_irq = ((cec_irq & 0x08) >> 3) | ((cec_irq & 0x04) >> 1) | | ||
| 2018 | ((cec_irq & 0x02) << 1) | ((cec_irq & 0x01) << 3); | ||
| 2019 | io_write(sd, 0x4e, cec_irq); | ||
| 2020 | |||
| 2021 | if (handled) | ||
| 2022 | *handled = true; | ||
| 2023 | } | ||
| 2024 | |||
| 2025 | static int adv76xx_cec_adap_enable(struct cec_adapter *adap, bool enable) | ||
| 2026 | { | ||
| 2027 | struct adv76xx_state *state = adap->priv; | ||
| 2028 | struct v4l2_subdev *sd = &state->sd; | ||
| 2029 | |||
| 2030 | if (!state->cec_enabled_adap && enable) { | ||
| 2031 | cec_write_clr_set(sd, 0x2a, 0x01, 0x01); /* power up cec */ | ||
| 2032 | cec_write(sd, 0x2c, 0x01); /* cec soft reset */ | ||
| 2033 | cec_write_clr_set(sd, 0x11, 0x01, 0); /* initially disable tx */ | ||
| 2034 | /* enabled irqs: */ | ||
| 2035 | /* tx: ready */ | ||
| 2036 | /* tx: arbitration lost */ | ||
| 2037 | /* tx: retry timeout */ | ||
| 2038 | /* rx: ready */ | ||
| 2039 | io_write_clr_set(sd, 0x50, 0x0f, 0x0f); | ||
| 2040 | cec_write(sd, 0x26, 0x01); /* enable rx */ | ||
| 2041 | } else if (state->cec_enabled_adap && !enable) { | ||
| 2042 | /* disable cec interrupts */ | ||
| 2043 | io_write_clr_set(sd, 0x50, 0x0f, 0x00); | ||
| 2044 | /* disable address mask 1-3 */ | ||
| 2045 | cec_write_clr_set(sd, 0x27, 0x70, 0x00); | ||
| 2046 | /* power down cec section */ | ||
| 2047 | cec_write_clr_set(sd, 0x2a, 0x01, 0x00); | ||
| 2048 | state->cec_valid_addrs = 0; | ||
| 2049 | } | ||
| 2050 | state->cec_enabled_adap = enable; | ||
| 2051 | adv76xx_s_detect_tx_5v_ctrl(sd); | ||
| 2052 | return 0; | ||
| 2053 | } | ||
| 2054 | |||
| 2055 | static int adv76xx_cec_adap_log_addr(struct cec_adapter *adap, u8 addr) | ||
| 2056 | { | ||
| 2057 | struct adv76xx_state *state = adap->priv; | ||
| 2058 | struct v4l2_subdev *sd = &state->sd; | ||
| 2059 | unsigned int i, free_idx = ADV76XX_MAX_ADDRS; | ||
| 2060 | |||
| 2061 | if (!state->cec_enabled_adap) | ||
| 2062 | return addr == CEC_LOG_ADDR_INVALID ? 0 : -EIO; | ||
| 2063 | |||
| 2064 | if (addr == CEC_LOG_ADDR_INVALID) { | ||
| 2065 | cec_write_clr_set(sd, 0x27, 0x70, 0); | ||
| 2066 | state->cec_valid_addrs = 0; | ||
| 2067 | return 0; | ||
| 2068 | } | ||
| 2069 | |||
| 2070 | for (i = 0; i < ADV76XX_MAX_ADDRS; i++) { | ||
| 2071 | bool is_valid = state->cec_valid_addrs & (1 << i); | ||
| 2072 | |||
| 2073 | if (free_idx == ADV76XX_MAX_ADDRS && !is_valid) | ||
| 2074 | free_idx = i; | ||
| 2075 | if (is_valid && state->cec_addr[i] == addr) | ||
| 2076 | return 0; | ||
| 2077 | } | ||
| 2078 | if (i == ADV76XX_MAX_ADDRS) { | ||
| 2079 | i = free_idx; | ||
| 2080 | if (i == ADV76XX_MAX_ADDRS) | ||
| 2081 | return -ENXIO; | ||
| 2082 | } | ||
| 2083 | state->cec_addr[i] = addr; | ||
| 2084 | state->cec_valid_addrs |= 1 << i; | ||
| 2085 | |||
| 2086 | switch (i) { | ||
| 2087 | case 0: | ||
| 2088 | /* enable address mask 0 */ | ||
| 2089 | cec_write_clr_set(sd, 0x27, 0x10, 0x10); | ||
| 2090 | /* set address for mask 0 */ | ||
| 2091 | cec_write_clr_set(sd, 0x28, 0x0f, addr); | ||
| 2092 | break; | ||
| 2093 | case 1: | ||
| 2094 | /* enable address mask 1 */ | ||
| 2095 | cec_write_clr_set(sd, 0x27, 0x20, 0x20); | ||
| 2096 | /* set address for mask 1 */ | ||
| 2097 | cec_write_clr_set(sd, 0x28, 0xf0, addr << 4); | ||
| 2098 | break; | ||
| 2099 | case 2: | ||
| 2100 | /* enable address mask 2 */ | ||
| 2101 | cec_write_clr_set(sd, 0x27, 0x40, 0x40); | ||
| 2102 | /* set address for mask 1 */ | ||
| 2103 | cec_write_clr_set(sd, 0x29, 0x0f, addr); | ||
| 2104 | break; | ||
| 2105 | } | ||
| 2106 | return 0; | ||
| 2107 | } | ||
| 2108 | |||
| 2109 | static int adv76xx_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, | ||
| 2110 | u32 signal_free_time, struct cec_msg *msg) | ||
| 2111 | { | ||
| 2112 | struct adv76xx_state *state = adap->priv; | ||
| 2113 | struct v4l2_subdev *sd = &state->sd; | ||
| 2114 | u8 len = msg->len; | ||
| 2115 | unsigned int i; | ||
| 2116 | |||
| 2117 | /* | ||
| 2118 | * The number of retries is the number of attempts - 1, but retry | ||
| 2119 | * at least once. It's not clear if a value of 0 is allowed, so | ||
| 2120 | * let's do at least one retry. | ||
| 2121 | */ | ||
| 2122 | cec_write_clr_set(sd, 0x12, 0x70, max(1, attempts - 1) << 4); | ||
| 2123 | |||
| 2124 | if (len > 16) { | ||
| 2125 | v4l2_err(sd, "%s: len exceeded 16 (%d)\n", __func__, len); | ||
| 2126 | return -EINVAL; | ||
| 2127 | } | ||
| 2128 | |||
| 2129 | /* write data */ | ||
| 2130 | for (i = 0; i < len; i++) | ||
| 2131 | cec_write(sd, i, msg->msg[i]); | ||
| 2132 | |||
| 2133 | /* set length (data + header) */ | ||
| 2134 | cec_write(sd, 0x10, len); | ||
| 2135 | /* start transmit, enable tx */ | ||
| 2136 | cec_write(sd, 0x11, 0x01); | ||
| 2137 | return 0; | ||
| 2138 | } | ||
| 2139 | |||
| 2140 | static const struct cec_adap_ops adv76xx_cec_adap_ops = { | ||
| 2141 | .adap_enable = adv76xx_cec_adap_enable, | ||
| 2142 | .adap_log_addr = adv76xx_cec_adap_log_addr, | ||
| 2143 | .adap_transmit = adv76xx_cec_adap_transmit, | ||
| 2144 | }; | ||
| 2145 | #endif | ||
| 2146 | |||
| 1927 | static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled) | 2147 | static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled) |
| 1928 | { | 2148 | { |
| 1929 | struct adv76xx_state *state = to_state(sd); | 2149 | struct adv76xx_state *state = to_state(sd); |
| @@ -1969,6 +2189,11 @@ static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled) · 39 unmodified lines | |||
| 1969 | *handled = true; | 2189 | *handled = true; |
| 1970 | } | 2190 | } |
| 1971 | 2191 | ||
| 2192 | #if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC) | ||
| 2193 | /* cec */ | ||
| 2194 | adv76xx_cec_isr(sd, handled); | ||
| 2195 | #endif | ||
| 2196 | |||
| 1972 | /* tx 5v detect */ | 2197 | /* tx 5v detect */ |
| 1973 | tx_5v = irq_reg_0x70 & info->cable_det_mask; | 2198 | tx_5v = irq_reg_0x70 & info->cable_det_mask; |
| 1974 | if (tx_5v) { | 2199 | if (tx_5v) { |
| @@ -2018,39 +2243,12 @@ static int adv76xx_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) · 43 unmodified lines | |||
| 2018 | return 0; | 2243 | return 0; |
| 2019 | } | 2244 | } |
| 2020 | 2245 | ||
| 2021 | static int get_edid_spa_location(const u8 *edid) | ||
| 2022 | { | ||
| 2023 | u8 d; | ||
| 2024 | |||
| 2025 | if ((edid[0x7e] != 1) || | ||
| 2026 | (edid[0x80] != 0x02) || | ||
| 2027 | (edid[0x81] != 0x03)) { | ||
| 2028 | return -1; | ||
| 2029 | } | ||
| 2030 | |||
| 2031 | /* search Vendor Specific Data Block (tag 3) */ | ||
| 2032 | d = edid[0x82] & 0x7f; | ||
| 2033 | if (d > 4) { | ||
| 2034 | int i = 0x84; | ||
| 2035 | int end = 0x80 + d; | ||
| 2036 | |||
| 2037 | do { | ||
| 2038 | u8 tag = edid[i] >> 5; | ||
| 2039 | u8 len = edid[i] & 0x1f; | ||
| 2040 | |||
| 2041 | if ((tag == 3) && (len >= 5)) | ||
| 2042 | return i + 4; | ||
| 2043 | i += len + 1; | ||
| 2044 | } while (i < end); | ||
| 2045 | } | ||
| 2046 | return -1; | ||
| 2047 | } | ||
| 2048 | |||
| 2049 | static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) | 2246 | static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) |
| 2050 | { | 2247 | { |
| 2051 | struct adv76xx_state *state = to_state(sd); | 2248 | struct adv76xx_state *state = to_state(sd); |
| 2052 | const struct adv76xx_chip_info *info = state->info; | 2249 | const struct adv76xx_chip_info *info = state->info; |
| 2053 | int spa_loc; | 2250 | unsigned int spa_loc; |
| 2251 | u16 pa; | ||
| 2054 | int err; | 2252 | int err; |
| 2055 | int i; | 2253 | int i; |
| 2056 | 2254 | ||
| @@ -2081,6 +2279,10 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) · 24 unmodified lines | |||
| 2081 | edid->blocks = 2; | 2279 | edid->blocks = 2; |
| 2082 | return -E2BIG; | 2280 | return -E2BIG; |
| 2083 | } | 2281 | } |
| 2282 | pa = cec_get_edid_phys_addr(edid->edid, edid->blocks * 128, &spa_loc); | ||
| 2283 | err = cec_phys_addr_validate(pa, &pa, NULL); | ||
| 2284 | if (err) | ||
| 2285 | return err; | ||
| 2084 | 2286 | ||
| 2085 | v4l2_dbg(2, debug, sd, "%s: write EDID pad %d, edid.present = 0x%x\n", | 2287 | v4l2_dbg(2, debug, sd, "%s: write EDID pad %d, edid.present = 0x%x\n", |
| 2086 | __func__, edid->pad, state->edid.present); | 2288 | __func__, edid->pad, state->edid.present); |
| @@ -2090,9 +2292,12 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) · 3 unmodified lines | |||
| 2090 | adv76xx_set_hpd(state, 0); | 2292 | adv76xx_set_hpd(state, 0); |
| 2091 | rep_write_clr_set(sd, info->edid_enable_reg, 0x0f, 0x00); | 2293 | rep_write_clr_set(sd, info->edid_enable_reg, 0x0f, 0x00); |
| 2092 | 2294 | ||
| 2093 | spa_loc = get_edid_spa_location(edid->edid); | 2295 | /* |
| 2094 | if (spa_loc < 0) | 2296 | * Return an error if no location of the source physical address |
| 2095 | spa_loc = 0xc0; /* Default value [REF_02, p. 116] */ | 2297 | * was found. |
| 2298 | */ | ||
| 2299 | if (spa_loc == 0) | ||
| 2300 | return -EINVAL; | ||
| 2096 | 2301 | ||
| 2097 | switch (edid->pad) { | 2302 | switch (edid->pad) { |
| 2098 | case ADV76XX_PAD_HDMI_PORT_A: | 2303 | case ADV76XX_PAD_HDMI_PORT_A: |
| @@ -2152,6 +2357,7 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) · 53 unmodified lines | |||
| 2152 | v4l2_err(sd, "error enabling edid (0x%x)\n", state->edid.present); | 2357 | v4l2_err(sd, "error enabling edid (0x%x)\n", state->edid.present); |
| 2153 | return -EIO; | 2358 | return -EIO; |
| 2154 | } | 2359 | } |
| 2360 | cec_s_phys_addr(state->cec_adap, pa, false); | ||
| 2155 | 2361 | ||
| 2156 | /* enable hotplug after 100 ms */ | 2362 | /* enable hotplug after 100 ms */ |
| 2157 | queue_delayed_work(state->work_queues, | 2363 | queue_delayed_work(state->work_queues, |
| @@ -2276,8 +2482,19 @@ static int adv76xx_log_status(struct v4l2_subdev *sd) · 118 unmodified lines | |||
| 2276 | ((edid_enabled & 0x02) ? "Yes" : "No"), | 2482 | ((edid_enabled & 0x02) ? "Yes" : "No"), |
| 2277 | ((edid_enabled & 0x04) ? "Yes" : "No"), | 2483 | ((edid_enabled & 0x04) ? "Yes" : "No"), |
| 2278 | ((edid_enabled & 0x08) ? "Yes" : "No")); | 2484 | ((edid_enabled & 0x08) ? "Yes" : "No")); |
| 2279 | v4l2_info(sd, "CEC: %s\n", !!(cec_read(sd, 0x2a) & 0x01) ? | 2485 | v4l2_info(sd, "CEC: %s\n", state->cec_enabled_adap ? |
| 2280 | "enabled" : "disabled"); | 2486 | "enabled" : "disabled"); |
| 2487 | if (state->cec_enabled_adap) { | ||
| 2488 | int i; | ||
| 2489 | |||
| 2490 | for (i = 0; i < ADV76XX_MAX_ADDRS; i++) { | ||
| 2491 | bool is_valid = state->cec_valid_addrs & (1 << i); | ||
| 2492 | |||
| 2493 | if (is_valid) | ||
| 2494 | v4l2_info(sd, "CEC Logical Address: 0x%x\n", | ||
| 2495 | state->cec_addr[i]); | ||
| 2496 | } | ||
| 2497 | } | ||
| 2281 | 2498 | ||
| 2282 | v4l2_info(sd, "-----Signal status-----\n"); | 2499 | v4l2_info(sd, "-----Signal status-----\n"); |
| 2283 | cable_det = info->read_cable_det(sd); | 2500 | cable_det = info->read_cable_det(sd); |
| @@ -2387,6 +2604,24 @@ static int adv76xx_subscribe_event(struct v4l2_subdev *sd, · 103 unmodified lines | |||
| 2387 | } | 2604 | } |
| 2388 | } | 2605 | } |
| 2389 | 2606 | ||
| 2607 | static int adv76xx_registered(struct v4l2_subdev *sd) | ||
| 2608 | { | ||
| 2609 | struct adv76xx_state *state = to_state(sd); | ||
| 2610 | int err; | ||
| 2611 | |||
| 2612 | err = cec_register_adapter(state->cec_adap); | ||
| 2613 | if (err) | ||
| 2614 | cec_delete_adapter(state->cec_adap); | ||
| 2615 | return err; | ||
| 2616 | } | ||
| 2617 | |||
| 2618 | static void adv76xx_unregistered(struct v4l2_subdev *sd) | ||
| 2619 | { | ||
| 2620 | struct adv76xx_state *state = to_state(sd); | ||
| 2621 | |||
| 2622 | cec_unregister_adapter(state->cec_adap); | ||
| 2623 | } | ||
| 2624 | |||
| 2390 | /* ----------------------------------------------------------------------- */ | 2625 | /* ----------------------------------------------------------------------- */ |
| 2391 | 2626 | ||
| 2392 | static const struct v4l2_ctrl_ops adv76xx_ctrl_ops = { | 2627 | static const struct v4l2_ctrl_ops adv76xx_ctrl_ops = { |
| @@ -2430,6 +2665,11 @@ static const struct v4l2_subdev_ops adv76xx_ops = { · 37 unmodified lines | |||
| 2430 | .pad = &adv76xx_pad_ops, | 2665 | .pad = &adv76xx_pad_ops, |
| 2431 | }; | 2666 | }; |
| 2432 | 2667 | ||
| 2668 | static const struct v4l2_subdev_internal_ops adv76xx_int_ops = { | ||
| 2669 | .registered = adv76xx_registered, | ||
| 2670 | .unregistered = adv76xx_unregistered, | ||
| 2671 | }; | ||
| 2672 | |||
| 2433 | /* -------------------------- custom ctrls ---------------------------------- */ | 2673 | /* -------------------------- custom ctrls ---------------------------------- */ |
| 2434 | 2674 | ||
| 2435 | static const struct v4l2_ctrl_config adv7604_ctrl_analog_sampling_phase = { | 2675 | static const struct v4l2_ctrl_config adv7604_ctrl_analog_sampling_phase = { |
| @@ -3093,6 +3333,7 @@ static int adv76xx_probe(struct i2c_client *client, · 657 unmodified lines | |||
| 3093 | id->name, i2c_adapter_id(client->adapter), | 3333 | id->name, i2c_adapter_id(client->adapter), |
| 3094 | client->addr); | 3334 | client->addr); |
| 3095 | sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; | 3335 | sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; |
| 3336 | sd->internal_ops = &adv76xx_int_ops; | ||
| 3096 | 3337 | ||
| 3097 | /* Configure IO Regmap region */ | 3338 | /* Configure IO Regmap region */ |
| 3098 | err = configure_regmap(state, ADV76XX_PAGE_IO); | 3339 | err = configure_regmap(state, ADV76XX_PAGE_IO); |
| @@ -3236,6 +3477,18 @@ static int adv76xx_probe(struct i2c_client *client, · 137 unmodified lines | |||
| 3236 | err = adv76xx_core_init(sd); | 3477 | err = adv76xx_core_init(sd); |
| 3237 | if (err) | 3478 | if (err) |
| 3238 | goto err_entity; | 3479 | goto err_entity; |
| 3480 | |||
| 3481 | #if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC) | ||
| 3482 | state->cec_adap = cec_allocate_adapter(&adv76xx_cec_adap_ops, | ||
| 3483 | state, dev_name(&client->dev), | ||
| 3484 | CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS | | ||
| 3485 | CEC_CAP_PASSTHROUGH | CEC_CAP_RC, ADV76XX_MAX_ADDRS, | ||
| 3486 | &client->dev); | ||
| 3487 | err = PTR_ERR_OR_ZERO(state->cec_adap); | ||
| 3488 | if (err) | ||
| 3489 | goto err_entity; | ||
| 3490 | #endif | ||
| 3491 | |||
| 3239 | v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, | 3492 | v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, |
| 3240 | client->addr << 1, client->adapter->name); | 3493 | client->addr << 1, client->adapter->name); |
| 3241 | 3494 | ||
| @@ -3264,6 +3517,13 @@ static int adv76xx_remove(struct i2c_client *client) · 22 unmodified lines | |||
| 3264 | struct v4l2_subdev *sd = i2c_get_clientdata(client); | 3517 | struct v4l2_subdev *sd = i2c_get_clientdata(client); |
| 3265 | struct adv76xx_state *state = to_state(sd); | 3518 | struct adv76xx_state *state = to_state(sd); |
| 3266 | 3519 | ||
| 3520 | /* disable interrupts */ | ||
| 3521 | io_write(sd, 0x40, 0); | ||
| 3522 | io_write(sd, 0x41, 0); | ||
| 3523 | io_write(sd, 0x46, 0); | ||
| 3524 | io_write(sd, 0x6e, 0); | ||
| 3525 | io_write(sd, 0x73, 0); | ||
| 3526 | |||
| 3267 | cancel_delayed_work(&state->delayed_work_enable_hotplug); | 3527 | cancel_delayed_work(&state->delayed_work_enable_hotplug); |
| 3268 | destroy_workqueue(state->work_queues); | 3528 | destroy_workqueue(state->work_queues); |
| 3269 | v4l2_async_unregister_subdev(sd); | 3529 | v4l2_async_unregister_subdev(sd); |
| @@ -39,6 +39,7 @@ · 38 unmodified lines | |||
| 39 | #include <linux/workqueue.h> | 39 | #include <linux/workqueue.h> |
| 40 | #include <linux/v4l2-dv-timings.h> | 40 | #include <linux/v4l2-dv-timings.h> |
| 41 | #include <linux/hdmi.h> | 41 | #include <linux/hdmi.h> |
| 42 | #include <media/cec.h> | ||
| 42 | #include <media/v4l2-device.h> | 43 | #include <media/v4l2-device.h> |
| 43 | #include <media/v4l2-event.h> | 44 | #include <media/v4l2-event.h> |
| 44 | #include <media/v4l2-ctrls.h> | 45 | #include <media/v4l2-ctrls.h> |
| @@ -79,6 +80,8 @@ MODULE_LICENSE("GPL"); · 34 unmodified lines | |||
| 79 | 80 | ||
| 80 | #define ADV7842_OP_SWAP_CB_CR (1 << 0) | 81 | #define ADV7842_OP_SWAP_CB_CR (1 << 0) |
| 81 | 82 | ||
| 83 | #define ADV7842_MAX_ADDRS (3) | ||
| 84 | |||
| 82 | /* | 85 | /* |
| 83 | ********************************************************************** | 86 | ********************************************************************** |
| 84 | * | 87 | * |
| @@ -142,6 +145,11 @@ struct adv7842_state { · 57 unmodified lines | |||
| 142 | struct v4l2_ctrl *free_run_color_ctrl_manual; | 145 | struct v4l2_ctrl *free_run_color_ctrl_manual; |
| 143 | struct v4l2_ctrl *free_run_color_ctrl; | 146 | struct v4l2_ctrl *free_run_color_ctrl; |
| 144 | struct v4l2_ctrl *rgb_quantization_range_ctrl; | 147 | struct v4l2_ctrl *rgb_quantization_range_ctrl; |
| 148 | |||
| 149 | struct cec_adapter *cec_adap; | ||
| 150 | u8 cec_addr[ADV7842_MAX_ADDRS]; | ||
| 151 | u8 cec_valid_addrs; | ||
| 152 | bool cec_enabled_adap; | ||
| 145 | }; | 153 | }; |
| 146 | 154 | ||
| 147 | /* Unsupported timings. This device cannot support 720p30. */ | 155 | /* Unsupported timings. This device cannot support 720p30. */ |
| @@ -418,9 +426,9 @@ static inline int cec_write(struct v4l2_subdev *sd, u8 reg, u8 val) · 270 unmodified lines | |||
| 418 | return adv_smbus_write_byte_data(state->i2c_cec, reg, val); | 426 | return adv_smbus_write_byte_data(state->i2c_cec, reg, val); |
| 419 | } | 427 | } |
| 420 | 428 | ||
| 421 | static inline int cec_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) | 429 | static inline int cec_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) |
| 422 | { | 430 | { |
| 423 | return cec_write(sd, reg, (cec_read(sd, reg) & mask) | val); | 431 | return cec_write(sd, reg, (cec_read(sd, reg) & ~mask) | val); |
| 424 | } | 432 | } |
| 425 | 433 | ||
| 426 | static inline int infoframe_read(struct v4l2_subdev *sd, u8 reg) | 434 | static inline int infoframe_read(struct v4l2_subdev *sd, u8 reg) |
| @@ -696,6 +704,18 @@ adv7842_get_dv_timings_cap(struct v4l2_subdev *sd) · 269 unmodified lines | |||
| 696 | 704 | ||
| 697 | /* ----------------------------------------------------------------------- */ | 705 | /* ----------------------------------------------------------------------- */ |
| 698 | 706 | ||
| 707 | static u16 adv7842_read_cable_det(struct v4l2_subdev *sd) | ||
| 708 | { | ||
| 709 | u8 reg = io_read(sd, 0x6f); | ||
| 710 | u16 val = 0; | ||
| 711 | |||
| 712 | if (reg & 0x02) | ||
| 713 | val |= 1; /* port A */ | ||
| 714 | if (reg & 0x01) | ||
| 715 | val |= 2; /* port B */ | ||
| 716 | return val; | ||
| 717 | } | ||
| 718 | |||
| 699 | static void adv7842_delayed_work_enable_hotplug(struct work_struct *work) | 719 | static void adv7842_delayed_work_enable_hotplug(struct work_struct *work) |
| 700 | { | 720 | { |
| 701 | struct delayed_work *dwork = to_delayed_work(work); | 721 | struct delayed_work *dwork = to_delayed_work(work); |
| @@ -762,50 +782,18 @@ static int edid_write_vga_segment(struct v4l2_subdev *sd) · 60 unmodified lines | |||
| 762 | return 0; | 782 | return 0; |
| 763 | } | 783 | } |
| 764 | 784 | ||
| 765 | static int edid_spa_location(const u8 *edid) | ||
| 766 | { | ||
| 767 | u8 d; | ||
| 768 | |||
| 769 | /* | ||
| 770 | * TODO, improve and update for other CEA extensions | ||
| 771 | * currently only for 1 segment (256 bytes), | ||
| 772 | * i.e. 1 extension block and CEA revision 3. | ||
| 773 | */ | ||
| 774 | if ((edid[0x7e] != 1) || | ||
| 775 | (edid[0x80] != 0x02) || | ||
| 776 | (edid[0x81] != 0x03)) { | ||
| 777 | return -EINVAL; | ||
| 778 | } | ||
| 779 | /* | ||
| 780 | * search Vendor Specific Data Block (tag 3) | ||
| 781 | */ | ||
| 782 | d = edid[0x82] & 0x7f; | ||
| 783 | if (d > 4) { | ||
| 784 | int i = 0x84; | ||
| 785 | int end = 0x80 + d; | ||
| 786 | do { | ||
| 787 | u8 tag = edid[i]>>5; | ||
| 788 | u8 len = edid[i] & 0x1f; | ||
| 789 | |||
| 790 | if ((tag == 3) && (len >= 5)) | ||
| 791 | return i + 4; | ||
| 792 | i += len + 1; | ||
| 793 | } while (i < end); | ||
| 794 | } | ||
| 795 | return -EINVAL; | ||
| 796 | } | ||
| 797 | |||
| 798 | static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port) | 785 | static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port) |
| 799 | { | 786 | { |
| 800 | struct i2c_client *client = v4l2_get_subdevdata(sd); | 787 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
| 801 | struct adv7842_state *state = to_state(sd); | 788 | struct adv7842_state *state = to_state(sd); |
| 802 | const u8 *val = state->hdmi_edid.edid; | 789 | const u8 *edid = state->hdmi_edid.edid; |
| 803 | int spa_loc = edid_spa_location(val); | 790 | int spa_loc; |
| 791 | u16 pa; | ||
| 804 | int err = 0; | 792 | int err = 0; |
| 805 | int i; | 793 | int i; |
| 806 | 794 | ||
| 807 | v4l2_dbg(2, debug, sd, "%s: write EDID on port %c (spa at 0x%x)\n", | 795 | v4l2_dbg(2, debug, sd, "%s: write EDID on port %c\n", |
| 808 | __func__, (port == ADV7842_EDID_PORT_A) ? 'A' : 'B', spa_loc); | 796 | __func__, (port == ADV7842_EDID_PORT_A) ? 'A' : 'B'); |
| 809 | 797 | ||
| 810 | /* HPA disable on port A and B */ | 798 | /* HPA disable on port A and B */ |
| 811 | io_write_and_or(sd, 0x20, 0xcf, 0x00); | 799 | io_write_and_or(sd, 0x20, 0xcf, 0x00); |
| @@ -816,24 +804,33 @@ static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port) · 4 unmodified lines | |||
| 816 | if (!state->hdmi_edid.present) | 804 | if (!state->hdmi_edid.present) |
| 817 | return 0; | 805 | return 0; |
| 818 | 806 | ||
| 807 | pa = cec_get_edid_phys_addr(edid, 256, &spa_loc); | ||
| 808 | err = cec_phys_addr_validate(pa, &pa, NULL); | ||
| 809 | if (err) | ||
| 810 | return err; | ||
| 811 | |||
| 812 | /* | ||
| 813 | * Return an error if no location of the source physical address | ||
| 814 | * was found. | ||
| 815 | */ | ||
| 816 | if (spa_loc == 0) | ||
| 817 | return -EINVAL; | ||
| 818 | |||
| 819 | /* edid segment pointer '0' for HDMI ports */ | 819 | /* edid segment pointer '0' for HDMI ports */ |
| 820 | rep_write_and_or(sd, 0x77, 0xef, 0x00); | 820 | rep_write_and_or(sd, 0x77, 0xef, 0x00); |
| 821 | 821 | ||
| 822 | for (i = 0; !err && i < 256; i += I2C_SMBUS_BLOCK_MAX) | 822 | for (i = 0; !err && i < 256; i += I2C_SMBUS_BLOCK_MAX) |
| 823 | err = adv_smbus_write_i2c_block_data(state->i2c_edid, i, | 823 | err = adv_smbus_write_i2c_block_data(state->i2c_edid, i, |
| 824 | I2C_SMBUS_BLOCK_MAX, val + i); | 824 | I2C_SMBUS_BLOCK_MAX, edid + i); |
| 825 | if (err) | 825 | if (err) |
| 826 | return err; | 826 | return err; |
| 827 | 827 | ||
| 828 | if (spa_loc < 0) | ||
| 829 | spa_loc = 0xc0; /* Default value [REF_02, p. 199] */ | ||
| 830 | |||
| 831 | if (port == ADV7842_EDID_PORT_A) { | 828 | if (port == ADV7842_EDID_PORT_A) { |
| 832 | rep_write(sd, 0x72, val[spa_loc]); | 829 | rep_write(sd, 0x72, edid[spa_loc]); |
| 833 | rep_write(sd, 0x73, val[spa_loc + 1]); | 830 | rep_write(sd, 0x73, edid[spa_loc + 1]); |
| 834 | } else { | 831 | } else { |
| 835 | rep_write(sd, 0x74, val[spa_loc]); | 832 | rep_write(sd, 0x74, edid[spa_loc]); |
| 836 | rep_write(sd, 0x75, val[spa_loc + 1]); | 833 | rep_write(sd, 0x75, edid[spa_loc + 1]); |
| 837 | } | 834 | } |
| 838 | rep_write(sd, 0x76, spa_loc & 0xff); | 835 | rep_write(sd, 0x76, spa_loc & 0xff); |
| 839 | rep_write_and_or(sd, 0x77, 0xbf, (spa_loc >> 2) & 0x40); | 836 | 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) · 13 unmodified lines | |||
| 853 | (port == ADV7842_EDID_PORT_A) ? 'A' : 'B'); | 850 | (port == ADV7842_EDID_PORT_A) ? 'A' : 'B'); |
| 854 | return -EIO; | 851 | return -EIO; |
| 855 | } | 852 | } |
| 853 | cec_s_phys_addr(state->cec_adap, pa, false); | ||
| 856 | 854 | ||
| 857 | /* enable hotplug after 200 ms */ | 855 | /* enable hotplug after 200 ms */ |
| 858 | queue_delayed_work(state->work_queues, | 856 | queue_delayed_work(state->work_queues, |
| @@ -983,20 +981,11 @@ static int adv7842_s_register(struct v4l2_subdev *sd, · 124 unmodified lines | |||
| 983 | static int adv7842_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd) | 981 | static int adv7842_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd) |
| 984 | { | 982 | { |
| 985 | struct adv7842_state *state = to_state(sd); | 983 | struct adv7842_state *state = to_state(sd); |
| 986 | int prev = v4l2_ctrl_g_ctrl(state->detect_tx_5v_ctrl); | 984 | u16 cable_det = adv7842_read_cable_det(sd); |
| 987 | u8 reg_io_6f = io_read(sd, 0x6f); | ||
| 988 | int val = 0; | ||
| 989 | |||
| 990 | if (reg_io_6f & 0x02) | ||
| 991 | val |= 1; /* port A */ | ||
| 992 | if (reg_io_6f & 0x01) | ||
| 993 | val |= 2; /* port B */ | ||
| 994 | 985 | ||
| 995 | v4l2_dbg(1, debug, sd, "%s: 0x%x -> 0x%x\n", __func__, prev, val); | 986 | v4l2_dbg(1, debug, sd, "%s: 0x%x\n", __func__, cable_det); |
| 996 | 987 | ||
| 997 | if (val != prev) | 988 | return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, cable_det); |
| 998 | return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, val); | ||
| 999 | return 0; | ||
| 1000 | } | 989 | } |
| 1001 | 990 | ||
| 1002 | static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd, | 991 | 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) · 1167 unmodified lines | |||
| 2170 | } | 2159 | } |
| 2171 | } | 2160 | } |
| 2172 | 2161 | ||
| 2162 | #if IS_ENABLED(CONFIG_VIDEO_ADV7842_CEC) | ||
| 2163 | static void adv7842_cec_tx_raw_status(struct v4l2_subdev *sd, u8 tx_raw_status) | ||
| 2164 | { | ||
| 2165 | struct adv7842_state *state = to_state(sd); | ||
| 2166 | |||
| 2167 | if ((cec_read(sd, 0x11) & 0x01) == 0) { | ||
| 2168 | v4l2_dbg(1, debug, sd, "%s: tx raw: tx disabled\n", __func__); | ||
| 2169 | return; | ||
| 2170 | } | ||
| 2171 | |||
| 2172 | if (tx_raw_status & 0x02) { | ||
| 2173 | v4l2_dbg(1, debug, sd, "%s: tx raw: arbitration lost\n", | ||
| 2174 | __func__); | ||
| 2175 | cec_transmit_done(state->cec_adap, CEC_TX_STATUS_ARB_LOST, | ||
| 2176 | 1, 0, 0, 0); | ||
| 2177 | return; | ||
| 2178 | } | ||
| 2179 | if (tx_raw_status & 0x04) { | ||
| 2180 | u8 status; | ||
| 2181 | u8 nack_cnt; | ||
| 2182 | u8 low_drive_cnt; | ||
| 2183 | |||
| 2184 | v4l2_dbg(1, debug, sd, "%s: tx raw: retry failed\n", __func__); | ||
| 2185 | /* | ||
| 2186 | * We set this status bit since this hardware performs | ||
| 2187 | * retransmissions. | ||
| 2188 | */ | ||
| 2189 | status = CEC_TX_STATUS_MAX_RETRIES; | ||
| 2190 | nack_cnt = cec_read(sd, 0x14) & 0xf; | ||
| 2191 | if (nack_cnt) | ||
| 2192 | status |= CEC_TX_STATUS_NACK; | ||
| 2193 | low_drive_cnt = cec_read(sd, 0x14) >> 4; | ||
| 2194 | if (low_drive_cnt) | ||
| 2195 | status |= CEC_TX_STATUS_LOW_DRIVE; | ||
| 2196 | cec_transmit_done(state->cec_adap, status, | ||
| 2197 | 0, nack_cnt, low_drive_cnt, 0); | ||
| 2198 | return; | ||
| 2199 | } | ||
| 2200 | if (tx_raw_status & 0x01) { | ||
| 2201 | v4l2_dbg(1, debug, sd, "%s: tx raw: ready ok\n", __func__); | ||
| 2202 | cec_transmit_done(state->cec_adap, CEC_TX_STATUS_OK, 0, 0, 0, 0); | ||
| 2203 | return; | ||
| 2204 | } | ||
| 2205 | } | ||
| 2206 | |||
| 2207 | static void adv7842_cec_isr(struct v4l2_subdev *sd, bool *handled) | ||
| 2208 | { | ||
| 2209 | u8 cec_irq; | ||
| 2210 | |||
| 2211 | /* cec controller */ | ||
| 2212 | cec_irq = io_read(sd, 0x93) & 0x0f; | ||
| 2213 | if (!cec_irq) | ||
| 2214 | return; | ||
| 2215 | |||
| 2216 | v4l2_dbg(1, debug, sd, "%s: cec: irq 0x%x\n", __func__, cec_irq); | ||
| 2217 | adv7842_cec_tx_raw_status(sd, cec_irq); | ||
| 2218 | if (cec_irq & 0x08) { | ||
| 2219 | struct adv7842_state *state = to_state(sd); | ||
| 2220 | struct cec_msg msg; | ||
| 2221 | |||
| 2222 | msg.len = cec_read(sd, 0x25) & 0x1f; | ||
| 2223 | if (msg.len > 16) | ||
| 2224 | msg.len = 16; | ||
| 2225 | |||
| 2226 | if (msg.len) { | ||
| 2227 | u8 i; | ||
| 2228 | |||
| 2229 | for (i = 0; i < msg.len; i++) | ||
| 2230 | msg.msg[i] = cec_read(sd, i + 0x15); | ||
| 2231 | cec_write(sd, 0x26, 0x01); /* re-enable rx */ | ||
| 2232 | cec_received_msg(state->cec_adap, &msg); | ||
| 2233 | } | ||
| 2234 | } | ||
| 2235 | |||
| 2236 | io_write(sd, 0x94, cec_irq); | ||
| 2237 | |||
| 2238 | if (handled) | ||
| 2239 | *handled = true; | ||
| 2240 | } | ||
| 2241 | |||
| 2242 | static int adv7842_cec_adap_enable(struct cec_adapter *adap, bool enable) | ||
| 2243 | { | ||
| 2244 | struct adv7842_state *state = adap->priv; | ||
| 2245 | struct v4l2_subdev *sd = &state->sd; | ||
| 2246 | |||
| 2247 | if (!state->cec_enabled_adap && enable) { | ||
| 2248 | cec_write_clr_set(sd, 0x2a, 0x01, 0x01); /* power up cec */ | ||
| 2249 | cec_write(sd, 0x2c, 0x01); /* cec soft reset */ | ||
| 2250 | cec_write_clr_set(sd, 0x11, 0x01, 0); /* initially disable tx */ | ||
| 2251 | /* enabled irqs: */ | ||
| 2252 | /* tx: ready */ | ||
| 2253 | /* tx: arbitration lost */ | ||
| 2254 | /* tx: retry timeout */ | ||
| 2255 | /* rx: ready */ | ||
| 2256 | io_write_clr_set(sd, 0x96, 0x0f, 0x0f); | ||
| 2257 | cec_write(sd, 0x26, 0x01); /* enable rx */ | ||
| 2258 | } else if (state->cec_enabled_adap && !enable) { | ||
| 2259 | /* disable cec interrupts */ | ||
| 2260 | io_write_clr_set(sd, 0x96, 0x0f, 0x00); | ||
| 2261 | /* disable address mask 1-3 */ | ||
| 2262 | cec_write_clr_set(sd, 0x27, 0x70, 0x00); | ||
| 2263 | /* power down cec section */ | ||
| 2264 | cec_write_clr_set(sd, 0x2a, 0x01, 0x00); | ||
| 2265 | state->cec_valid_addrs = 0; | ||
| 2266 | } | ||
| 2267 | state->cec_enabled_adap = enable; | ||
| 2268 | return 0; | ||
| 2269 | } | ||
| 2270 | |||
| 2271 | static int adv7842_cec_adap_log_addr(struct cec_adapter *adap, u8 addr) | ||
| 2272 | { | ||
| 2273 | struct adv7842_state *state = adap->priv; | ||
| 2274 | struct v4l2_subdev *sd = &state->sd; | ||
| 2275 | unsigned int i, free_idx = ADV7842_MAX_ADDRS; | ||
| 2276 | |||
| 2277 | if (!state->cec_enabled_adap) | ||
| 2278 | return addr == CEC_LOG_ADDR_INVALID ? 0 : -EIO; | ||
| 2279 | |||
| 2280 | if (addr == CEC_LOG_ADDR_INVALID) { | ||
| 2281 | cec_write_clr_set(sd, 0x27, 0x70, 0); | ||
| 2282 | state->cec_valid_addrs = 0; | ||
| 2283 | return 0; | ||
| 2284 | } | ||
| 2285 | |||
| 2286 | for (i = 0; i < ADV7842_MAX_ADDRS; i++) { | ||
| 2287 | bool is_valid = state->cec_valid_addrs & (1 << i); | ||
| 2288 | |||
| 2289 | if (free_idx == ADV7842_MAX_ADDRS && !is_valid) | ||
| 2290 | free_idx = i; | ||
| 2291 | if (is_valid && state->cec_addr[i] == addr) | ||
| 2292 | return 0; | ||
| 2293 | } | ||
| 2294 | if (i == ADV7842_MAX_ADDRS) { | ||
| 2295 | i = free_idx; | ||
| 2296 | if (i == ADV7842_MAX_ADDRS) | ||
| 2297 | return -ENXIO; | ||
| 2298 | } | ||
| 2299 | state->cec_addr[i] = addr; | ||
| 2300 | state->cec_valid_addrs |= 1 << i; | ||
| 2301 | |||
| 2302 | switch (i) { | ||
| 2303 | case 0: | ||
| 2304 | /* enable address mask 0 */ | ||
| 2305 | cec_write_clr_set(sd, 0x27, 0x10, 0x10); | ||
| 2306 | /* set address for mask 0 */ | ||
| 2307 | cec_write_clr_set(sd, 0x28, 0x0f, addr); | ||
| 2308 | break; | ||
| 2309 | case 1: | ||
| 2310 | /* enable address mask 1 */ | ||
| 2311 | cec_write_clr_set(sd, 0x27, 0x20, 0x20); | ||
| 2312 | /* set address for mask 1 */ | ||
| 2313 | cec_write_clr_set(sd, 0x28, 0xf0, addr << 4); | ||
| 2314 | break; | ||
| 2315 | case 2: | ||
| 2316 | /* enable address mask 2 */ | ||
| 2317 | cec_write_clr_set(sd, 0x27, 0x40, 0x40); | ||
| 2318 | /* set address for mask 1 */ | ||
| 2319 | cec_write_clr_set(sd, 0x29, 0x0f, addr); | ||
| 2320 | break; | ||
| 2321 | } | ||
| 2322 | return 0; | ||
| 2323 | } | ||
| 2324 | |||
| 2325 | static int adv7842_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, | ||
| 2326 | u32 signal_free_time, struct cec_msg *msg) | ||
| 2327 | { | ||
| 2328 | struct adv7842_state *state = adap->priv; | ||
| 2329 | struct v4l2_subdev *sd = &state->sd; | ||
| 2330 | u8 len = msg->len; | ||
| 2331 | unsigned int i; | ||
| 2332 | |||
| 2333 | /* | ||
| 2334 | * The number of retries is the number of attempts - 1, but retry | ||
| 2335 | * at least once. It's not clear if a value of 0 is allowed, so | ||
| 2336 | * let's do at least one retry. | ||
| 2337 | */ | ||
| 2338 | cec_write_clr_set(sd, 0x12, 0x70, max(1, attempts - 1) << 4); | ||
| 2339 | |||
| 2340 | if (len > 16) { | ||
| 2341 | v4l2_err(sd, "%s: len exceeded 16 (%d)\n", __func__, len); | ||
| 2342 | return -EINVAL; | ||
| 2343 | } | ||
| 2344 | |||
| 2345 | /* write data */ | ||
| 2346 | for (i = 0; i < len; i++) | ||
| 2347 | cec_write(sd, i, msg->msg[i]); | ||
| 2348 | |||
| 2349 | /* set length (data + header) */ | ||
| 2350 | cec_write(sd, 0x10, len); | ||
| 2351 | /* start transmit, enable tx */ | ||
| 2352 | cec_write(sd, 0x11, 0x01); | ||
| 2353 | return 0; | ||
| 2354 | } | ||
| 2355 | |||
| 2356 | static const struct cec_adap_ops adv7842_cec_adap_ops = { | ||
| 2357 | .adap_enable = adv7842_cec_adap_enable, | ||
| 2358 | .adap_log_addr = adv7842_cec_adap_log_addr, | ||
| 2359 | .adap_transmit = adv7842_cec_adap_transmit, | ||
| 2360 | }; | ||
| 2361 | #endif | ||
| 2362 | |||
| 2173 | static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled) | 2363 | static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled) |
| 2174 | { | 2364 | { |
| 2175 | struct adv7842_state *state = to_state(sd); | 2365 | struct adv7842_state *state = to_state(sd); |
| @@ -2241,6 +2431,11 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled) · 65 unmodified lines | |||
| 2241 | *handled = true; | 2431 | *handled = true; |
| 2242 | } | 2432 | } |
| 2243 | 2433 | ||
| 2434 | #if IS_ENABLED(CONFIG_VIDEO_ADV7842_CEC) | ||
| 2435 | /* cec */ | ||
| 2436 | adv7842_cec_isr(sd, handled); | ||
| 2437 | #endif | ||
| 2438 | |||
| 2244 | /* tx 5v detect */ | 2439 | /* tx 5v detect */ |
| 2245 | if (irq_status[2] & 0x3) { | 2440 | if (irq_status[2] & 0x3) { |
| 2246 | v4l2_dbg(1, debug, sd, "%s: irq tx_5v\n", __func__); | 2441 | 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) · 74 unmodified lines | |||
| 2321 | case ADV7842_EDID_PORT_A: | 2516 | case ADV7842_EDID_PORT_A: |
| 2322 | case ADV7842_EDID_PORT_B: | 2517 | case ADV7842_EDID_PORT_B: |
| 2323 | memset(&state->hdmi_edid.edid, 0, 256); | 2518 | memset(&state->hdmi_edid.edid, 0, 256); |
| 2324 | if (e->blocks) | 2519 | if (e->blocks) { |
| 2325 | state->hdmi_edid.present |= 0x04 << e->pad; | 2520 | state->hdmi_edid.present |= 0x04 << e->pad; |
| 2326 | else | 2521 | } else { |
| 2327 | state->hdmi_edid.present &= ~(0x04 << e->pad); | 2522 | state->hdmi_edid.present &= ~(0x04 << e->pad); |
| 2523 | adv7842_s_detect_tx_5v_ctrl(sd); | ||
| 2524 | } | ||
| 2328 | memcpy(&state->hdmi_edid.edid, e->edid, 128 * e->blocks); | 2525 | memcpy(&state->hdmi_edid.edid, e->edid, 128 * e->blocks); |
| 2329 | err = edid_write_hdmi_segment(sd, e->pad); | 2526 | err = edid_write_hdmi_segment(sd, e->pad); |
| 2330 | break; | 2527 | break; |
| @@ -2512,8 +2709,19 @@ static int adv7842_cp_log_status(struct v4l2_subdev *sd) · 181 unmodified lines | |||
| 2512 | v4l2_info(sd, "HPD A %s, B %s\n", | 2709 | v4l2_info(sd, "HPD A %s, B %s\n", |
| 2513 | reg_io_0x21 & 0x02 ? "enabled" : "disabled", | 2710 | reg_io_0x21 & 0x02 ? "enabled" : "disabled", |
| 2514 | reg_io_0x21 & 0x01 ? "enabled" : "disabled"); | 2711 | reg_io_0x21 & 0x01 ? "enabled" : "disabled"); |
| 2515 | v4l2_info(sd, "CEC %s\n", !!(cec_read(sd, 0x2a) & 0x01) ? | 2712 | v4l2_info(sd, "CEC: %s\n", state->cec_enabled_adap ? |
| 2516 | "enabled" : "disabled"); | 2713 | "enabled" : "disabled"); |
| 2714 | if (state->cec_enabled_adap) { | ||
| 2715 | int i; | ||
| 2716 | |||
| 2717 | for (i = 0; i < ADV7842_MAX_ADDRS; i++) { | ||
| 2718 | bool is_valid = state->cec_valid_addrs & (1 << i); | ||
| 2719 | |||
| 2720 | if (is_valid) | ||
| 2721 | v4l2_info(sd, "CEC Logical Address: 0x%x\n", | ||
| 2722 | state->cec_addr[i]); | ||
| 2723 | } | ||
| 2724 | } | ||
| 2517 | 2725 | ||
| 2518 | v4l2_info(sd, "-----Signal status-----\n"); | 2726 | v4l2_info(sd, "-----Signal status-----\n"); |
| 2519 | if (state->hdmi_port_a) { | 2727 | if (state->hdmi_port_a) { |
| @@ -3034,6 +3242,24 @@ static int adv7842_subscribe_event(struct v4l2_subdev *sd, · 514 unmodified lines | |||
| 3034 | } | 3242 | } |
| 3035 | } | 3243 | } |
| 3036 | 3244 | ||
| 3245 | static int adv7842_registered(struct v4l2_subdev *sd) | ||
| 3246 | { | ||
| 3247 | struct adv7842_state *state = to_state(sd); | ||
| 3248 | int err; | ||
| 3249 | |||
| 3250 | err = cec_register_adapter(state->cec_adap); | ||
| 3251 | if (err) | ||
| 3252 | cec_delete_adapter(state->cec_adap); | ||
| 3253 | return err; | ||
| 3254 | } | ||
| 3255 | |||
| 3256 | static void adv7842_unregistered(struct v4l2_subdev *sd) | ||
| 3257 | { | ||
| 3258 | struct adv7842_state *state = to_state(sd); | ||
| 3259 | |||
| 3260 | cec_unregister_adapter(state->cec_adap); | ||
| 3261 | } | ||
| 3262 | |||
| 3037 | /* ----------------------------------------------------------------------- */ | 3263 | /* ----------------------------------------------------------------------- */ |
| 3038 | 3264 | ||
| 3039 | static const struct v4l2_ctrl_ops adv7842_ctrl_ops = { | 3265 | static const struct v4l2_ctrl_ops adv7842_ctrl_ops = { |
| @@ -3080,6 +3306,11 @@ static const struct v4l2_subdev_ops adv7842_ops = { · 40 unmodified lines | |||
| 3080 | .pad = &adv7842_pad_ops, | 3306 | .pad = &adv7842_pad_ops, |
| 3081 | }; | 3307 | }; |
| 3082 | 3308 | ||
| 3309 | static const struct v4l2_subdev_internal_ops adv7842_int_ops = { | ||
| 3310 | .registered = adv7842_registered, | ||
| 3311 | .unregistered = adv7842_unregistered, | ||
| 3312 | }; | ||
| 3313 | |||
| 3083 | /* -------------------------- custom ctrls ---------------------------------- */ | 3314 | /* -------------------------- custom ctrls ---------------------------------- */ |
| 3084 | 3315 | ||
| 3085 | static const struct v4l2_ctrl_config adv7842_ctrl_analog_sampling_phase = { | 3316 | static const struct v4l2_ctrl_config adv7842_ctrl_analog_sampling_phase = { |
| @@ -3244,6 +3475,7 @@ static int adv7842_probe(struct i2c_client *client, · 158 unmodified lines | |||
| 3244 | sd = &state->sd; | 3475 | sd = &state->sd; |
| 3245 | v4l2_i2c_subdev_init(sd, client, &adv7842_ops); | 3476 | v4l2_i2c_subdev_init(sd, client, &adv7842_ops); |
| 3246 | sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; | 3477 | sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; |
| 3478 | sd->internal_ops = &adv7842_int_ops; | ||
| 3247 | state->mode = pdata->mode; | 3479 | state->mode = pdata->mode; |
| 3248 | 3480 | ||
| 3249 | state->hdmi_port_a = pdata->input == ADV7842_SELECT_HDMI_PORT_A; | 3481 | state->hdmi_port_a = pdata->input == ADV7842_SELECT_HDMI_PORT_A; |
| @@ -3334,6 +3566,17 @@ static int adv7842_probe(struct i2c_client *client, · 84 unmodified lines | |||
| 3334 | if (err) | 3566 | if (err) |
| 3335 | goto err_entity; | 3567 | goto err_entity; |
| 3336 | 3568 | ||
| 3569 | #if IS_ENABLED(CONFIG_VIDEO_ADV7842_CEC) | ||
| 3570 | state->cec_adap = cec_allocate_adapter(&adv7842_cec_adap_ops, | ||
| 3571 | state, dev_name(&client->dev), | ||
| 3572 | CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS | | ||
| 3573 | CEC_CAP_PASSTHROUGH | CEC_CAP_RC, ADV7842_MAX_ADDRS, | ||
| 3574 | &client->dev); | ||
| 3575 | err = PTR_ERR_OR_ZERO(state->cec_adap); | ||
| 3576 | if (err) | ||
| 3577 | goto err_entity; | ||
| 3578 | #endif | ||
| 3579 | |||
| 3337 | v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, | 3580 | v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, |
| 3338 | client->addr << 1, client->adapter->name); | 3581 | client->addr << 1, client->adapter->name); |
| 3339 | return 0; | 3582 | return 0; |
| @@ -3358,7 +3601,6 @@ static int adv7842_remove(struct i2c_client *client) · 18 unmodified lines | |||
| 3358 | struct adv7842_state *state = to_state(sd); | 3601 | struct adv7842_state *state = to_state(sd); |
| 3359 | 3602 | ||
| 3360 | adv7842_irq_enable(sd, false); | 3603 | adv7842_irq_enable(sd, false); |
| 3361 | |||
| 3362 | cancel_delayed_work(&state->delayed_work_enable_hotplug); | 3604 | cancel_delayed_work(&state->delayed_work_enable_hotplug); |
| 3363 | destroy_workqueue(state->work_queues); | 3605 | destroy_workqueue(state->work_queues); |
| 3364 | v4l2_device_unregister_subdev(sd); | 3606 | v4l2_device_unregister_subdev(sd); |
| @@ -6,6 +6,7 @@ config VIDEO_VIVID · 5 unmodified lines | |||
| 6 | select FB_CFB_FILLRECT | 6 | select FB_CFB_FILLRECT |
| 7 | select FB_CFB_COPYAREA | 7 | select FB_CFB_COPYAREA |
| 8 | select FB_CFB_IMAGEBLIT | 8 | select FB_CFB_IMAGEBLIT |
| 9 | select MEDIA_CEC_EDID | ||
| 9 | select VIDEOBUF2_VMALLOC | 10 | select VIDEOBUF2_VMALLOC |
| 10 | select VIDEO_V4L2_TPG | 11 | select VIDEO_V4L2_TPG |
| 11 | default n | 12 | default n |
| @@ -22,6 +23,13 @@ config VIDEO_VIVID · 10 unmodified lines | |||
| 22 | Say Y here if you want to test video apps or debug V4L devices. | 23 | Say Y here if you want to test video apps or debug V4L devices. |
| 23 | When in doubt, say N. | 24 | When in doubt, say N. |
| 24 | 25 | ||
| 26 | config VIDEO_VIVID_CEC | ||
| 27 | bool "Enable CEC emulation support" | ||
| 28 | depends on VIDEO_VIVID && MEDIA_CEC | ||
| 29 | ---help--- | ||
| 30 | When selected the vivid module will emulate the optional | ||
| 31 | HDMI CEC feature. | ||
| 32 | |||
| 25 | config VIDEO_VIVID_MAX_DEVS | 33 | config VIDEO_VIVID_MAX_DEVS |
| 26 | int "Maximum number of devices" | 34 | int "Maximum number of devices" |
| 27 | depends on VIDEO_VIVID | 35 | depends on VIDEO_VIVID |
| @@ -3,4 +3,8 @@ vivid-objs := vivid-core.o vivid-ctrls.o vivid-vid-common.o vivid-vbi-gen.o \ · 2 unmodified lines | |||
| 3 | vivid-radio-rx.o vivid-radio-tx.o vivid-radio-common.o \ | 3 | vivid-radio-rx.o vivid-radio-tx.o vivid-radio-common.o \ |
| 4 | vivid-rds-gen.o vivid-sdr-cap.o vivid-vbi-cap.o vivid-vbi-out.o \ | 4 | vivid-rds-gen.o vivid-sdr-cap.o vivid-vbi-cap.o vivid-vbi-out.o \ |
| 5 | vivid-osd.o | 5 | vivid-osd.o |
| 6 | ifeq ($(CONFIG_VIDEO_VIVID_CEC),y) | ||
| 7 | vivid-objs += vivid-cec.o | ||
| 8 | endif | ||
| 9 | |||
| 6 | obj-$(CONFIG_VIDEO_VIVID) += vivid.o | 10 | obj-$(CONFIG_VIDEO_VIVID) += vivid.o |
| @@ -0,0 +1,255 @@ | |||
| 1 | /* | ||
| 2 | * vivid-cec.c - A Virtual Video Test Driver, cec emulation | ||
| 3 | * | ||
| 4 | * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved. | ||
| 5 | * | ||
| 6 | * This program is free software; you may redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; version 2 of the License. | ||
| 9 | * | ||
| 10 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
| 11 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
| 12 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
| 13 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||
| 14 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
| 15 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
| 16 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| 17 | * SOFTWARE. | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include <media/cec.h> | ||
| 21 | |||
| 22 | #include "vivid-core.h" | ||
| 23 | #include "vivid-cec.h" | ||
| 24 | |||
| 25 | void vivid_cec_bus_free_work(struct vivid_dev *dev) | ||
| 26 | { | ||
| 27 | spin_lock(&dev->cec_slock); | ||
| 28 | while (!list_empty(&dev->cec_work_list)) { | ||
| 29 | struct vivid_cec_work *cw = | ||
| 30 | list_first_entry(&dev->cec_work_list, | ||
| 31 | struct vivid_cec_work, list); | ||
| 32 | |||
| 33 | spin_unlock(&dev->cec_slock); | ||
| 34 | cancel_delayed_work_sync(&cw->work); | ||
| 35 | spin_lock(&dev->cec_slock); | ||
| 36 | list_del(&cw->list); | ||
| 37 | cec_transmit_done(cw->adap, CEC_TX_STATUS_LOW_DRIVE, 0, 0, 1, 0); | ||
| 38 | kfree(cw); | ||
| 39 | } | ||
| 40 | spin_unlock(&dev->cec_slock); | ||
| 41 | } | ||
| 42 | |||
| 43 | static struct cec_adapter *vivid_cec_find_dest_adap(struct vivid_dev *dev, | ||
| 44 | struct cec_adapter *adap, | ||
| 45 | u8 dest) | ||
| 46 | { | ||
| 47 | unsigned int i; | ||
| 48 | |||
| 49 | if (dest >= 0xf) | ||
| 50 | return NULL; | ||
| 51 | |||
| 52 | if (adap != dev->cec_rx_adap && dev->cec_rx_adap && | ||
| 53 | dev->cec_rx_adap->is_configured && | ||
| 54 | cec_has_log_addr(dev->cec_rx_adap, dest)) | ||
| 55 | return dev->cec_rx_adap; | ||
| 56 | |||
| 57 | for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++) { | ||
| 58 | if (adap == dev->cec_tx_adap[i]) | ||
| 59 | continue; | ||
| 60 | if (!dev->cec_tx_adap[i]->is_configured) | ||
| 61 | continue; | ||
| 62 | if (cec_has_log_addr(dev->cec_tx_adap[i], dest)) | ||
| 63 | return dev->cec_tx_adap[i]; | ||
| 64 | } | ||
| 65 | return NULL; | ||
| 66 | } | ||
| 67 | |||
| 68 | static void vivid_cec_xfer_done_worker(struct work_struct *work) | ||
| 69 | { | ||
| 70 | struct vivid_cec_work *cw = | ||
| 71 | container_of(work, struct vivid_cec_work, work.work); | ||
| 72 | struct vivid_dev *dev = cw->dev; | ||
| 73 | struct cec_adapter *adap = cw->adap; | ||
| 74 | bool is_poll = cw->msg.len == 1; | ||
| 75 | u8 dest = cec_msg_destination(&cw->msg); | ||
| 76 | struct cec_adapter *dest_adap = NULL; | ||
| 77 | bool valid_dest; | ||
| 78 | unsigned int i; | ||
| 79 | |||
| 80 | valid_dest = cec_msg_is_broadcast(&cw->msg); | ||
| 81 | if (!valid_dest) { | ||
| 82 | dest_adap = vivid_cec_find_dest_adap(dev, adap, dest); | ||
| 83 | if (dest_adap) | ||
| 84 | valid_dest = true; | ||
| 85 | } | ||
| 86 | cw->tx_status = valid_dest ? CEC_TX_STATUS_OK : CEC_TX_STATUS_NACK; | ||
| 87 | spin_lock(&dev->cec_slock); | ||
| 88 | dev->cec_xfer_time_jiffies = 0; | ||
| 89 | dev->cec_xfer_start_jiffies = 0; | ||
| 90 | list_del(&cw->list); | ||
| 91 | spin_unlock(&dev->cec_slock); | ||
| 92 | cec_transmit_done(cw->adap, cw->tx_status, 0, valid_dest ? 0 : 1, 0, 0); | ||
| 93 | |||
| 94 | if (!is_poll && dest_adap) { | ||
| 95 | /* Directed message */ | ||
| 96 | cec_received_msg(dest_adap, &cw->msg); | ||
| 97 | } else if (!is_poll && valid_dest) { | ||
| 98 | /* Broadcast message */ | ||
| 99 | if (adap != dev->cec_rx_adap && | ||
| 100 | dev->cec_rx_adap->log_addrs.log_addr_mask) | ||
| 101 | cec_received_msg(dev->cec_rx_adap, &cw->msg); | ||
| 102 | for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++) { | ||
| 103 | if (adap == dev->cec_tx_adap[i] || | ||
| 104 | !dev->cec_tx_adap[i]->log_addrs.log_addr_mask) | ||
| 105 | continue; | ||
| 106 | cec_received_msg(dev->cec_tx_adap[i], &cw->msg); | ||
| 107 | } | ||
| 108 | } | ||
| 109 | kfree(cw); | ||
| 110 | } | ||
| 111 | |||
| 112 | static void vivid_cec_xfer_try_worker(struct work_struct *work) | ||
| 113 | { | ||
| 114 | struct vivid_cec_work *cw = | ||
| 115 | container_of(work, struct vivid_cec_work, work.work); | ||
| 116 | struct vivid_dev *dev = cw->dev; | ||
| 117 | |||
| 118 | spin_lock(&dev->cec_slock); | ||
| 119 | if (dev->cec_xfer_time_jiffies) { | ||
| 120 | list_del(&cw->list); | ||
| 121 | spin_unlock(&dev->cec_slock); | ||
| 122 | cec_transmit_done(cw->adap, CEC_TX_STATUS_ARB_LOST, 1, 0, 0, 0); | ||
| 123 | kfree(cw); | ||
| 124 | } else { | ||
| 125 | INIT_DELAYED_WORK(&cw->work, vivid_cec_xfer_done_worker); | ||
| 126 | dev->cec_xfer_start_jiffies = jiffies; | ||
| 127 | dev->cec_xfer_time_jiffies = usecs_to_jiffies(cw->usecs); | ||
| 128 | spin_unlock(&dev->cec_slock); | ||
| 129 | schedule_delayed_work(&cw->work, dev->cec_xfer_time_jiffies); | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | static int vivid_cec_adap_enable(struct cec_adapter *adap, bool enable) | ||
| 134 | { | ||
| 135 | return 0; | ||
| 136 | } | ||
| 137 | |||
| 138 | static int vivid_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr) | ||
| 139 | { | ||
| 140 | return 0; | ||
| 141 | } | ||
| 142 | |||
| 143 | /* | ||
| 144 | * One data bit takes 2400 us, each byte needs 10 bits so that's 24000 us | ||
| 145 | * per byte. | ||
| 146 | */ | ||
| 147 | #define USECS_PER_BYTE 24000 | ||
| 148 | |||
| 149 | static int vivid_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, | ||
| 150 | u32 signal_free_time, struct cec_msg *msg) | ||
| 151 | { | ||
| 152 | struct vivid_dev *dev = adap->priv; | ||
| 153 | struct vivid_cec_work *cw = kzalloc(sizeof(*cw), GFP_KERNEL); | ||
| 154 | long delta_jiffies = 0; | ||
| 155 | |||
| 156 | if (cw == NULL) | ||
| 157 | return -ENOMEM; | ||
| 158 | cw->dev = dev; | ||
| 159 | cw->adap = adap; | ||
| 160 | cw->usecs = CEC_FREE_TIME_TO_USEC(signal_free_time) + | ||
| 161 | msg->len * USECS_PER_BYTE; | ||
| 162 | cw->msg = *msg; | ||
| 163 | |||
| 164 | spin_lock(&dev->cec_slock); | ||
| 165 | list_add(&cw->list, &dev->cec_work_list); | ||
| 166 | if (dev->cec_xfer_time_jiffies == 0) { | ||
| 167 | INIT_DELAYED_WORK(&cw->work, vivid_cec_xfer_done_worker); | ||
| 168 | dev->cec_xfer_start_jiffies = jiffies; | ||
| 169 | dev->cec_xfer_time_jiffies = usecs_to_jiffies(cw->usecs); | ||
| 170 | delta_jiffies = dev->cec_xfer_time_jiffies; | ||
| 171 | } else { | ||
| 172 | INIT_DELAYED_WORK(&cw->work, vivid_cec_xfer_try_worker); | ||
| 173 | delta_jiffies = dev->cec_xfer_start_jiffies + | ||
| 174 | dev->cec_xfer_time_jiffies - jiffies; | ||
| 175 | } | ||
| 176 | spin_unlock(&dev->cec_slock); | ||
| 177 | schedule_delayed_work(&cw->work, delta_jiffies < 0 ? 0 : delta_jiffies); | ||
| 178 | return 0; | ||
| 179 | } | ||
| 180 | |||
| 181 | static int vivid_received(struct cec_adapter *adap, struct cec_msg *msg) | ||
| 182 | { | ||
| 183 | struct vivid_dev *dev = adap->priv; | ||
| 184 | struct cec_msg reply; | ||
| 185 | u8 dest = cec_msg_destination(msg); | ||
| 186 | u16 pa; | ||
| 187 | u8 disp_ctl; | ||
| 188 | char osd[14]; | ||
| 189 | |||
| 190 | if (cec_msg_is_broadcast(msg)) | ||
| 191 | dest = adap->log_addrs.log_addr[0]; | ||
| 192 | cec_msg_init(&reply, dest, cec_msg_initiator(msg)); | ||
| 193 | |||
| 194 | switch (cec_msg_opcode(msg)) { | ||
| 195 | case CEC_MSG_SET_STREAM_PATH: | ||
| 196 | if (cec_is_sink(adap)) | ||
| 197 | return -ENOMSG; | ||
| 198 | cec_ops_set_stream_path(msg, &pa); | ||
| 199 | if (pa != adap->phys_addr) | ||
| 200 | return -ENOMSG; | ||
| 201 | cec_msg_active_source(&reply, adap->phys_addr); | ||
| 202 | cec_transmit_msg(adap, &reply, false); | ||
| 203 | break; | ||
| 204 | case CEC_MSG_SET_OSD_STRING: | ||
| 205 | if (!cec_is_sink(adap)) | ||
| 206 | return -ENOMSG; | ||
| 207 | cec_ops_set_osd_string(msg, &disp_ctl, osd); | ||
| 208 | switch (disp_ctl) { | ||
| 209 | case CEC_OP_DISP_CTL_DEFAULT: | ||
| 210 | strcpy(dev->osd, osd); | ||
| 211 | dev->osd_jiffies = jiffies; | ||
| 212 | break; | ||
| 213 | case CEC_OP_DISP_CTL_UNTIL_CLEARED: | ||
| 214 | strcpy(dev->osd, osd); | ||
| 215 | dev->osd_jiffies = 0; | ||
| 216 | break; | ||
| 217 | case CEC_OP_DISP_CTL_CLEAR: | ||
| 218 | dev->osd[0] = 0; | ||
| 219 | dev->osd_jiffies = 0; | ||
| 220 | break; | ||
| 221 | default: | ||
| 222 | cec_msg_feature_abort(&reply, cec_msg_opcode(msg), | ||
| 223 | CEC_OP_ABORT_INVALID_OP); | ||
| 224 | cec_transmit_msg(adap, &reply, false); | ||
| 225 | break; | ||
| 226 | } | ||
| 227 | break; | ||
| 228 | default: | ||
| 229 | return -ENOMSG; | ||
| 230 | } | ||
| 231 | return 0; | ||
| 232 | } | ||
| 233 | |||
| 234 | static const struct cec_adap_ops vivid_cec_adap_ops = { | ||
| 235 | .adap_enable = vivid_cec_adap_enable, | ||
| 236 | .adap_log_addr = vivid_cec_adap_log_addr, | ||
| 237 | .adap_transmit = vivid_cec_adap_transmit, | ||
| 238 | .received = vivid_received, | ||
| 239 | }; | ||
| 240 | |||
| 241 | struct cec_adapter *vivid_cec_alloc_adap(struct vivid_dev *dev, | ||
| 242 | unsigned int idx, | ||
| 243 | struct device *parent, | ||
| 244 | bool is_source) | ||
| 245 | { | ||
| 246 | char name[sizeof(dev->vid_out_dev.name) + 2]; | ||
| 247 | u32 caps = CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS | | ||
| 248 | CEC_CAP_PASSTHROUGH | CEC_CAP_RC; | ||
| 249 | |||
| 250 | snprintf(name, sizeof(name), "%s%d", | ||
| 251 | is_source ? dev->vid_out_dev.name : dev->vid_cap_dev.name, | ||
| 252 | idx); | ||
| 253 | return cec_allocate_adapter(&vivid_cec_adap_ops, dev, | ||
| 254 | name, caps, 1, parent); | ||
| 255 | } | ||
| @@ -0,0 +1,33 @@ | |||
| 1 | /* | ||
| 2 | * vivid-cec.h - A Virtual Video Test Driver, cec emulation | ||
| 3 | * | ||
| 4 | * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved. | ||
| 5 | * | ||
| 6 | * This program is free software; you may redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; version 2 of the License. | ||
| 9 | * | ||
| 10 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
| 11 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
| 12 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
| 13 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||
| 14 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
| 15 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
| 16 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| 17 | * SOFTWARE. | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifdef CONFIG_VIDEO_VIVID_CEC | ||
| 21 | struct cec_adapter *vivid_cec_alloc_adap(struct vivid_dev *dev, | ||
| 22 | unsigned int idx, | ||
| 23 | struct device *parent, | ||
| 24 | bool is_source); | ||
| 25 | void vivid_cec_bus_free_work(struct vivid_dev *dev); | ||
| 26 | |||
| 27 | #else | ||
| 28 | |||
| 29 | static inline void vivid_cec_bus_free_work(struct vivid_dev *dev) | ||
| 30 | { | ||
| 31 | } | ||
| 32 | |||
| 33 | #endif | ||
| @@ -46,6 +46,7 @@ · 45 unmodified lines | |||
| 46 | #include "vivid-vbi-cap.h" | 46 | #include "vivid-vbi-cap.h" |
| 47 | #include "vivid-vbi-out.h" | 47 | #include "vivid-vbi-out.h" |
| 48 | #include "vivid-osd.h" | 48 | #include "vivid-osd.h" |
| 49 | #include "vivid-cec.h" | ||
| 49 | #include "vivid-ctrls.h" | 50 | #include "vivid-ctrls.h" |
| 50 | 51 | ||
| 51 | #define VIVID_MODULE_NAME "vivid" | 52 | #define VIVID_MODULE_NAME "vivid" |
| @@ -684,6 +685,11 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) · 632 unmodified lines | |||
| 684 | dev->input_name_counter[i] = in_type_counter[dev->input_type[i]]++; | 685 | dev->input_name_counter[i] = in_type_counter[dev->input_type[i]]++; |
| 685 | } | 686 | } |
| 686 | dev->has_audio_inputs = in_type_counter[TV] && in_type_counter[SVID]; | 687 | dev->has_audio_inputs = in_type_counter[TV] && in_type_counter[SVID]; |
| 688 | if (in_type_counter[HDMI] == 16) { | ||
| 689 | /* The CEC physical address only allows for max 15 inputs */ | ||
| 690 | in_type_counter[HDMI]--; | ||
| 691 | dev->num_inputs--; | ||
| 692 | } | ||
| 687 | 693 | ||
| 688 | /* how many outputs do we have and of what type? */ | 694 | /* how many outputs do we have and of what type? */ |
| 689 | dev->num_outputs = num_outputs[inst]; | 695 | dev->num_outputs = num_outputs[inst]; |
| @@ -696,6 +702,15 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) · 6 unmodified lines | |||
| 696 | dev->output_name_counter[i] = out_type_counter[dev->output_type[i]]++; | 702 | dev->output_name_counter[i] = out_type_counter[dev->output_type[i]]++; |
| 697 | } | 703 | } |
| 698 | dev->has_audio_outputs = out_type_counter[SVID]; | 704 | dev->has_audio_outputs = out_type_counter[SVID]; |
| 705 | if (out_type_counter[HDMI] == 16) { | ||
| 706 | /* | ||
| 707 | * The CEC physical address only allows for max 15 inputs, | ||
| 708 | * so outputs are also limited to 15 to allow for easy | ||
| 709 | * CEC output to input mapping. | ||
| 710 | */ | ||
| 711 | out_type_counter[HDMI]--; | ||
| 712 | dev->num_outputs--; | ||
| 713 | } | ||
| 699 | 714 | ||
| 700 | /* do we create a video capture device? */ | 715 | /* do we create a video capture device? */ |
| 701 | dev->has_vid_cap = node_type & 0x0001; | 716 | dev->has_vid_cap = node_type & 0x0001; |
| @@ -1010,6 +1025,17 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) · 308 unmodified lines | |||
| 1010 | INIT_LIST_HEAD(&dev->vbi_out_active); | 1025 | INIT_LIST_HEAD(&dev->vbi_out_active); |
| 1011 | INIT_LIST_HEAD(&dev->sdr_cap_active); | 1026 | INIT_LIST_HEAD(&dev->sdr_cap_active); |
| 1012 | 1027 | ||
| 1028 | INIT_LIST_HEAD(&dev->cec_work_list); | ||
| 1029 | spin_lock_init(&dev->cec_slock); | ||
| 1030 | /* | ||
| 1031 | * Same as create_singlethread_workqueue, but now I can use the | ||
| 1032 | * string formatting of alloc_ordered_workqueue. | ||
| 1033 | */ | ||
| 1034 | dev->cec_workqueue = | ||
| 1035 | alloc_ordered_workqueue("vivid-%03d-cec", WQ_MEM_RECLAIM, inst); | ||
| 1036 | if (!dev->cec_workqueue) | ||
| 1037 | goto unreg_dev; | ||
| 1038 | |||
| 1013 | /* start creating the vb2 queues */ | 1039 | /* start creating the vb2 queues */ |
| 1014 | if (dev->has_vid_cap) { | 1040 | if (dev->has_vid_cap) { |
| 1015 | /* initialize vid_cap queue */ | 1041 | /* initialize vid_cap queue */ |
| @@ -1117,7 +1143,8 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) · 101 unmodified lines | |||
| 1117 | /* finally start creating the device nodes */ | 1143 | /* finally start creating the device nodes */ |
| 1118 | if (dev->has_vid_cap) { | 1144 | if (dev->has_vid_cap) { |
| 1119 | vfd = &dev->vid_cap_dev; | 1145 | vfd = &dev->vid_cap_dev; |
| 1120 | strlcpy(vfd->name, "vivid-vid-cap", sizeof(vfd->name)); | 1146 | snprintf(vfd->name, sizeof(vfd->name), |
| 1147 | "vivid-%03d-vid-cap", inst); | ||
| 1121 | vfd->fops = &vivid_fops; | 1148 | vfd->fops = &vivid_fops; |
| 1122 | vfd->ioctl_ops = &vivid_ioctl_ops; | 1149 | vfd->ioctl_ops = &vivid_ioctl_ops; |
| 1123 | vfd->device_caps = dev->vid_cap_caps; | 1150 | vfd->device_caps = dev->vid_cap_caps; |
| @@ -1133,6 +1160,27 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) · 9 unmodified lines | |||
| 1133 | vfd->lock = &dev->mutex; | 1160 | vfd->lock = &dev->mutex; |
| 1134 | video_set_drvdata(vfd, dev); | 1161 | video_set_drvdata(vfd, dev); |
| 1135 | 1162 | ||
| 1163 | #ifdef CONFIG_VIDEO_VIVID_CEC | ||
| 1164 | if (in_type_counter[HDMI]) { | ||
| 1165 | struct cec_adapter *adap; | ||
| 1166 | |||
| 1167 | adap = vivid_cec_alloc_adap(dev, 0, &pdev->dev, false); | ||
| 1168 | ret = PTR_ERR_OR_ZERO(adap); | ||
| 1169 | if (ret < 0) | ||
| 1170 | goto unreg_dev; | ||
| 1171 | dev->cec_rx_adap = adap; | ||
| 1172 | ret = cec_register_adapter(adap); | ||
| 1173 | if (ret < 0) { | ||
| 1174 | cec_delete_adapter(adap); | ||
| 1175 | dev->cec_rx_adap = NULL; | ||
| 1176 | goto unreg_dev; | ||
| 1177 | } | ||
| 1178 | cec_s_phys_addr(adap, 0, false); | ||
| 1179 | v4l2_info(&dev->v4l2_dev, "CEC adapter %s registered for HDMI input %d\n", | ||
| 1180 | dev_name(&adap->devnode.dev), i); | ||
| 1181 | } | ||
| 1182 | #endif | ||
| 1183 | |||
| 1136 | ret = video_register_device(vfd, VFL_TYPE_GRABBER, vid_cap_nr[inst]); | 1184 | ret = video_register_device(vfd, VFL_TYPE_GRABBER, vid_cap_nr[inst]); |
| 1137 | if (ret < 0) | 1185 | if (ret < 0) |
| 1138 | goto unreg_dev; | 1186 | goto unreg_dev; |
| @@ -1141,8 +1189,13 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) · 2 unmodified lines | |||
| 1141 | } | 1189 | } |
| 1142 | 1190 | ||
| 1143 | if (dev->has_vid_out) { | 1191 | if (dev->has_vid_out) { |
| 1192 | #ifdef CONFIG_VIDEO_VIVID_CEC | ||
| 1193 | unsigned int bus_cnt = 0; | ||
| 1194 | #endif | ||
| 1195 | |||
| 1144 | vfd = &dev->vid_out_dev; | 1196 | vfd = &dev->vid_out_dev; |
| 1145 | strlcpy(vfd->name, "vivid-vid-out", sizeof(vfd->name)); | 1197 | snprintf(vfd->name, sizeof(vfd->name), |
| 1198 | "vivid-%03d-vid-out", inst); | ||
| 1146 | vfd->vfl_dir = VFL_DIR_TX; | 1199 | vfd->vfl_dir = VFL_DIR_TX; |
| 1147 | vfd->fops = &vivid_fops; | 1200 | vfd->fops = &vivid_fops; |
| 1148 | vfd->ioctl_ops = &vivid_ioctl_ops; | 1201 | vfd->ioctl_ops = &vivid_ioctl_ops; |
| @@ -1159,6 +1212,35 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) · 10 unmodified lines | |||
| 1159 | vfd->lock = &dev->mutex; | 1212 | vfd->lock = &dev->mutex; |
| 1160 | video_set_drvdata(vfd, dev); | 1213 | video_set_drvdata(vfd, dev); |
| 1161 | 1214 | ||
| 1215 | #ifdef CONFIG_VIDEO_VIVID_CEC | ||
| 1216 | for (i = 0; i < dev->num_outputs; i++) { | ||
| 1217 | struct cec_adapter *adap; | ||
| 1218 | |||
| 1219 | if (dev->output_type[i] != HDMI) | ||
| 1220 | continue; | ||
| 1221 | dev->cec_output2bus_map[i] = bus_cnt; | ||
| 1222 | adap = vivid_cec_alloc_adap(dev, bus_cnt, | ||
| 1223 | &pdev->dev, true); | ||
| 1224 | ret = PTR_ERR_OR_ZERO(adap); | ||
| 1225 | if (ret < 0) | ||
| 1226 | goto unreg_dev; | ||
| 1227 | dev->cec_tx_adap[bus_cnt] = adap; | ||
| 1228 | ret = cec_register_adapter(adap); | ||
| 1229 | if (ret < 0) { | ||
| 1230 | cec_delete_adapter(adap); | ||
| 1231 | dev->cec_tx_adap[bus_cnt] = NULL; | ||
| 1232 | goto unreg_dev; | ||
| 1233 | } | ||
| 1234 | bus_cnt++; | ||
| 1235 | if (bus_cnt <= in_type_counter[HDMI]) | ||
| 1236 | cec_s_phys_addr(adap, bus_cnt << 12, false); | ||
| 1237 | else | ||
| 1238 | cec_s_phys_addr(adap, 0x1000, false); | ||
| 1239 | v4l2_info(&dev->v4l2_dev, "CEC adapter %s registered for HDMI output %d\n", | ||
| 1240 | dev_name(&adap->devnode.dev), i); | ||
| 1241 | } | ||
| 1242 | #endif | ||
| 1243 | |||
| 1162 | ret = video_register_device(vfd, VFL_TYPE_GRABBER, vid_out_nr[inst]); | 1244 | ret = video_register_device(vfd, VFL_TYPE_GRABBER, vid_out_nr[inst]); |
| 1163 | if (ret < 0) | 1245 | if (ret < 0) |
| 1164 | goto unreg_dev; | 1246 | goto unreg_dev; |
| @@ -1168,7 +1250,8 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) · 3 unmodified lines | |||
| 1168 | 1250 | ||
| 1169 | if (dev->has_vbi_cap) { | 1251 | if (dev->has_vbi_cap) { |
| 1170 | vfd = &dev->vbi_cap_dev; | 1252 | vfd = &dev->vbi_cap_dev; |
| 1171 | strlcpy(vfd->name, "vivid-vbi-cap", sizeof(vfd->name)); | 1253 | snprintf(vfd->name, sizeof(vfd->name), |
| 1254 | "vivid-%03d-vbi-cap", inst); | ||
| 1172 | vfd->fops = &vivid_fops; | 1255 | vfd->fops = &vivid_fops; |
| 1173 | vfd->ioctl_ops = &vivid_ioctl_ops; | 1256 | vfd->ioctl_ops = &vivid_ioctl_ops; |
| 1174 | vfd->device_caps = dev->vbi_cap_caps; | 1257 | vfd->device_caps = dev->vbi_cap_caps; |
| @@ -1191,7 +1274,8 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) · 16 unmodified lines | |||
| 1191 | 1274 | ||
| 1192 | if (dev->has_vbi_out) { | 1275 | if (dev->has_vbi_out) { |
| 1193 | vfd = &dev->vbi_out_dev; | 1276 | vfd = &dev->vbi_out_dev; |
| 1194 | strlcpy(vfd->name, "vivid-vbi-out", sizeof(vfd->name)); | 1277 | snprintf(vfd->name, sizeof(vfd->name), |
| 1278 | "vivid-%03d-vbi-out", inst); | ||
| 1195 | vfd->vfl_dir = VFL_DIR_TX; | 1279 | vfd->vfl_dir = VFL_DIR_TX; |
| 1196 | vfd->fops = &vivid_fops; | 1280 | vfd->fops = &vivid_fops; |
| 1197 | vfd->ioctl_ops = &vivid_ioctl_ops; | 1281 | vfd->ioctl_ops = &vivid_ioctl_ops; |
| @@ -1215,7 +1299,8 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) · 17 unmodified lines | |||
| 1215 | 1299 | ||
| 1216 | if (dev->has_sdr_cap) { | 1300 | if (dev->has_sdr_cap) { |
| 1217 | vfd = &dev->sdr_cap_dev; | 1301 | vfd = &dev->sdr_cap_dev; |
| 1218 | strlcpy(vfd->name, "vivid-sdr-cap", sizeof(vfd->name)); | 1302 | snprintf(vfd->name, sizeof(vfd->name), |
| 1303 | "vivid-%03d-sdr-cap", inst); | ||
| 1219 | vfd->fops = &vivid_fops; | 1304 | vfd->fops = &vivid_fops; |
| 1220 | vfd->ioctl_ops = &vivid_ioctl_ops; | 1305 | vfd->ioctl_ops = &vivid_ioctl_ops; |
| 1221 | vfd->device_caps = dev->sdr_cap_caps; | 1306 | vfd->device_caps = dev->sdr_cap_caps; |
| @@ -1234,7 +1319,8 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) · 12 unmodified lines | |||
| 1234 | 1319 | ||
| 1235 | if (dev->has_radio_rx) { | 1320 | if (dev->has_radio_rx) { |
| 1236 | vfd = &dev->radio_rx_dev; | 1321 | vfd = &dev->radio_rx_dev; |
| 1237 | strlcpy(vfd->name, "vivid-rad-rx", sizeof(vfd->name)); | 1322 | snprintf(vfd->name, sizeof(vfd->name), |
| 1323 | "vivid-%03d-rad-rx", inst); | ||
| 1238 | vfd->fops = &vivid_radio_fops; | 1324 | vfd->fops = &vivid_radio_fops; |
| 1239 | vfd->ioctl_ops = &vivid_ioctl_ops; | 1325 | vfd->ioctl_ops = &vivid_ioctl_ops; |
| 1240 | vfd->device_caps = dev->radio_rx_caps; | 1326 | vfd->device_caps = dev->radio_rx_caps; |
| @@ -1252,7 +1338,8 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) · 11 unmodified lines | |||
| 1252 | 1338 | ||
| 1253 | if (dev->has_radio_tx) { | 1339 | if (dev->has_radio_tx) { |
| 1254 | vfd = &dev->radio_tx_dev; | 1340 | vfd = &dev->radio_tx_dev; |
| 1255 | strlcpy(vfd->name, "vivid-rad-tx", sizeof(vfd->name)); | 1341 | snprintf(vfd->name, sizeof(vfd->name), |
| 1342 | "vivid-%03d-rad-tx", inst); | ||
| 1256 | vfd->vfl_dir = VFL_DIR_TX; | 1343 | vfd->vfl_dir = VFL_DIR_TX; |
| 1257 | vfd->fops = &vivid_radio_fops; | 1344 | vfd->fops = &vivid_radio_fops; |
| 1258 | vfd->ioctl_ops = &vivid_ioctl_ops; | 1345 | vfd->ioctl_ops = &vivid_ioctl_ops; |
| @@ -1282,6 +1369,13 @@ unreg_dev: · 23 unmodified lines | |||
| 1282 | video_unregister_device(&dev->vbi_cap_dev); | 1369 | video_unregister_device(&dev->vbi_cap_dev); |
| 1283 | video_unregister_device(&dev->vid_out_dev); | 1370 | video_unregister_device(&dev->vid_out_dev); |
| 1284 | video_unregister_device(&dev->vid_cap_dev); | 1371 | video_unregister_device(&dev->vid_cap_dev); |
| 1372 | cec_unregister_adapter(dev->cec_rx_adap); | ||
| 1373 | for (i = 0; i < MAX_OUTPUTS; i++) | ||
| 1374 | cec_unregister_adapter(dev->cec_tx_adap[i]); | ||
| 1375 | if (dev->cec_workqueue) { | ||
| 1376 | vivid_cec_bus_free_work(dev); | ||
| 1377 | destroy_workqueue(dev->cec_workqueue); | ||
| 1378 | } | ||
| 1285 | free_dev: | 1379 | free_dev: |
| 1286 | v4l2_device_put(&dev->v4l2_dev); | 1380 | v4l2_device_put(&dev->v4l2_dev); |
| 1287 | return ret; | 1381 | return ret; |
| @@ -1331,8 +1425,7 @@ static int vivid_probe(struct platform_device *pdev) · 43 unmodified lines | |||
| 1331 | static int vivid_remove(struct platform_device *pdev) | 1425 | static int vivid_remove(struct platform_device *pdev) |
| 1332 | { | 1426 | { |
| 1333 | struct vivid_dev *dev; | 1427 | struct vivid_dev *dev; |
| 1334 | unsigned i; | 1428 | unsigned int i, j; |
| 1335 | |||
| 1336 | 1429 | ||
| 1337 | for (i = 0; i < n_devs; i++) { | 1430 | for (i = 0; i < n_devs; i++) { |
| 1338 | dev = vivid_devs[i]; | 1431 | dev = vivid_devs[i]; |
| @@ -1380,6 +1473,13 @@ static int vivid_remove(struct platform_device *pdev) · 41 unmodified lines | |||
| 1380 | unregister_framebuffer(&dev->fb_info); | 1473 | unregister_framebuffer(&dev->fb_info); |
| 1381 | vivid_fb_release_buffers(dev); | 1474 | vivid_fb_release_buffers(dev); |
| 1382 | } | 1475 | } |
| 1476 | cec_unregister_adapter(dev->cec_rx_adap); | ||
| 1477 | for (j = 0; j < MAX_OUTPUTS; j++) | ||
| 1478 | cec_unregister_adapter(dev->cec_tx_adap[j]); | ||
| 1479 | if (dev->cec_workqueue) { | ||
| 1480 | vivid_cec_bus_free_work(dev); | ||
| 1481 | destroy_workqueue(dev->cec_workqueue); | ||
| 1482 | } | ||
| 1383 | v4l2_device_put(&dev->v4l2_dev); | 1483 | v4l2_device_put(&dev->v4l2_dev); |
| 1384 | vivid_devs[i] = NULL; | 1484 | vivid_devs[i] = NULL; |
| 1385 | } | 1485 | } |
| @@ -21,6 +21,8 @@ · 20 unmodified lines | |||
| 21 | #define _VIVID_CORE_H_ | 21 | #define _VIVID_CORE_H_ |
| 22 | 22 | ||
| 23 | #include <linux/fb.h> | 23 | #include <linux/fb.h> |
| 24 | #include <linux/workqueue.h> | ||
| 25 | #include <media/cec.h> | ||
| 24 | #include <media/videobuf2-v4l2.h> | 26 | #include <media/videobuf2-v4l2.h> |
| 25 | #include <media/v4l2-device.h> | 27 | #include <media/v4l2-device.h> |
| 26 | #include <media/v4l2-dev.h> | 28 | #include <media/v4l2-dev.h> |
| @@ -132,6 +134,17 @@ enum vivid_colorspace { · 105 unmodified lines | |||
| 132 | #define VIVID_INVALID_SIGNAL(mode) \ | 134 | #define VIVID_INVALID_SIGNAL(mode) \ |
| 133 | ((mode) == NO_SIGNAL || (mode) == NO_LOCK || (mode) == OUT_OF_RANGE) | 135 | ((mode) == NO_SIGNAL || (mode) == NO_LOCK || (mode) == OUT_OF_RANGE) |
| 134 | 136 | ||
| 137 | struct vivid_cec_work { | ||
| 138 | struct list_head list; | ||
| 139 | struct delayed_work work; | ||
| 140 | struct cec_adapter *adap; | ||
| 141 | struct vivid_dev *dev; | ||
| 142 | unsigned int usecs; | ||
| 143 | unsigned int timeout_ms; | ||
| 144 | u8 tx_status; | ||
| 145 | struct cec_msg msg; | ||
| 146 | }; | ||
| 147 | |||
| 135 | struct vivid_dev { | 148 | struct vivid_dev { |
| 136 | unsigned inst; | 149 | unsigned inst; |
| 137 | struct v4l2_device v4l2_dev; | 150 | struct v4l2_device v4l2_dev; |
| @@ -497,6 +510,20 @@ struct vivid_dev { · 359 unmodified lines | |||
| 497 | /* Shared between radio receiver and transmitter */ | 510 | /* Shared between radio receiver and transmitter */ |
| 498 | bool radio_rds_loop; | 511 | bool radio_rds_loop; |
| 499 | struct timespec radio_rds_init_ts; | 512 | struct timespec radio_rds_init_ts; |
| 513 | |||
| 514 | /* CEC */ | ||
| 515 | struct cec_adapter *cec_rx_adap; | ||
| 516 | struct cec_adapter *cec_tx_adap[MAX_OUTPUTS]; | ||
| 517 | struct workqueue_struct *cec_workqueue; | ||
| 518 | spinlock_t cec_slock; | ||
| 519 | struct list_head cec_work_list; | ||
| 520 | unsigned int cec_xfer_time_jiffies; | ||
| 521 | unsigned long cec_xfer_start_jiffies; | ||
| 522 | u8 cec_output2bus_map[MAX_OUTPUTS]; | ||
| 523 | |||
| 524 | /* CEC OSD String */ | ||
| 525 | char osd[14]; | ||
| 526 | unsigned long osd_jiffies; | ||
| 500 | }; | 527 | }; |
| 501 | 528 | ||
| 502 | static inline bool vivid_is_webcam(const struct vivid_dev *dev) | 529 | static inline bool vivid_is_webcam(const struct vivid_dev *dev) |
| @@ -552,6 +552,19 @@ static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf) · 551 unmodified lines | |||
| 552 | snprintf(str, sizeof(str), " button pressed!"); | 552 | snprintf(str, sizeof(str), " button pressed!"); |
| 553 | tpg_gen_text(tpg, basep, line++ * line_height, 16, str); | 553 | tpg_gen_text(tpg, basep, line++ * line_height, 16, str); |
| 554 | } | 554 | } |
| 555 | if (dev->osd[0]) { | ||
| 556 | if (vivid_is_hdmi_cap(dev)) { | ||
| 557 | snprintf(str, sizeof(str), | ||
| 558 | " OSD \"%s\"", dev->osd); | ||
| 559 | tpg_gen_text(tpg, basep, line++ * line_height, | ||
| 560 | 16, str); | ||
| 561 | } | ||
| 562 | if (dev->osd_jiffies && | ||
| 563 | time_is_before_jiffies(dev->osd_jiffies + 5 * HZ)) { | ||
| 564 | dev->osd[0] = 0; | ||
| 565 | dev->osd_jiffies = 0; | ||
| 566 | } | ||
| 567 | } | ||
| 555 | } | 568 | } |
| 556 | 569 | ||
| 557 | /* | 570 | /* |
| @@ -1700,6 +1700,9 @@ int vidioc_s_edid(struct file *file, void *_fh, · 1699 unmodified lines | |||
| 1700 | struct v4l2_edid *edid) | 1700 | struct v4l2_edid *edid) |
| 1701 | { | 1701 | { |
| 1702 | struct vivid_dev *dev = video_drvdata(file); | 1702 | struct vivid_dev *dev = video_drvdata(file); |
| 1703 | u16 phys_addr; | ||
| 1704 | unsigned int i; | ||
| 1705 | int ret; | ||
| 1703 | 1706 | ||
| 1704 | memset(edid->reserved, 0, sizeof(edid->reserved)); | 1707 | memset(edid->reserved, 0, sizeof(edid->reserved)); |
| 1705 | if (edid->pad >= dev->num_inputs) | 1708 | if (edid->pad >= dev->num_inputs) |
| @@ -1708,14 +1711,32 @@ int vidioc_s_edid(struct file *file, void *_fh, · 2 unmodified lines | |||
| 1708 | return -EINVAL; | 1711 | return -EINVAL; |
| 1709 | if (edid->blocks == 0) { | 1712 | if (edid->blocks == 0) { |
| 1710 | dev->edid_blocks = 0; | 1713 | dev->edid_blocks = 0; |
| 1711 | return 0; | 1714 | phys_addr = CEC_PHYS_ADDR_INVALID; |
| 1715 | goto set_phys_addr; | ||
| 1712 | } | 1716 | } |
| 1713 | if (edid->blocks > dev->edid_max_blocks) { | 1717 | if (edid->blocks > dev->edid_max_blocks) { |
| 1714 | edid->blocks = dev->edid_max_blocks; | 1718 | edid->blocks = dev->edid_max_blocks; |
| 1715 | return -E2BIG; | 1719 | return -E2BIG; |
| 1716 | } | 1720 | } |
| 1721 | phys_addr = cec_get_edid_phys_addr(edid->edid, edid->blocks * 128, NULL); | ||
| 1722 | ret = cec_phys_addr_validate(phys_addr, &phys_addr, NULL); | ||
| 1723 | if (ret) | ||
| 1724 | return ret; | ||
| 1725 | |||
| 1726 | if (vb2_is_busy(&dev->vb_vid_cap_q)) | ||
| 1727 | return -EBUSY; | ||
| 1728 | |||
| 1717 | dev->edid_blocks = edid->blocks; | 1729 | dev->edid_blocks = edid->blocks; |
| 1718 | memcpy(dev->edid, edid->edid, edid->blocks * 128); | 1730 | memcpy(dev->edid, edid->edid, edid->blocks * 128); |
| 1731 | |||
| 1732 | set_phys_addr: | ||
| 1733 | /* TODO: a proper hotplug detect cycle should be emulated here */ | ||
| 1734 | cec_s_phys_addr(dev->cec_rx_adap, phys_addr, false); | ||
| 1735 | |||
| 1736 | for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++) | ||
| 1737 | cec_s_phys_addr(dev->cec_tx_adap[i], | ||
| 1738 | cec_phys_addr_for_input(phys_addr, i + 1), | ||
| 1739 | false); | ||
| 1719 | return 0; | 1740 | return 0; |
| 1720 | } | 1741 | } |
| 1721 | 1742 | ||
| @@ -811,6 +811,7 @@ int vidioc_g_edid(struct file *file, void *_fh, · 810 unmodified lines | |||
| 811 | { | 811 | { |
| 812 | struct vivid_dev *dev = video_drvdata(file); | 812 | struct vivid_dev *dev = video_drvdata(file); |
| 813 | struct video_device *vdev = video_devdata(file); | 813 | struct video_device *vdev = video_devdata(file); |
| 814 | struct cec_adapter *adap; | ||
| 814 | 815 | ||
| 815 | memset(edid->reserved, 0, sizeof(edid->reserved)); | 816 | memset(edid->reserved, 0, sizeof(edid->reserved)); |
| 816 | if (vdev->vfl_dir == VFL_DIR_RX) { | 817 | if (vdev->vfl_dir == VFL_DIR_RX) { |
| @@ -818,11 +819,16 @@ int vidioc_g_edid(struct file *file, void *_fh, · 1 unmodified line | |||
| 818 | return -EINVAL; | 819 | return -EINVAL; |
| 819 | if (dev->input_type[edid->pad] != HDMI) | 820 | if (dev->input_type[edid->pad] != HDMI) |
| 820 | return -EINVAL; | 821 | return -EINVAL; |
| 822 | adap = dev->cec_rx_adap; | ||
| 821 | } else { | 823 | } else { |
| 824 | unsigned int bus_idx; | ||
| 825 | |||
| 822 | if (edid->pad >= dev->num_outputs) | 826 | if (edid->pad >= dev->num_outputs) |
| 823 | return -EINVAL; | 827 | return -EINVAL; |
| 824 | if (dev->output_type[edid->pad] != HDMI) | 828 | if (dev->output_type[edid->pad] != HDMI) |
| 825 | return -EINVAL; | 829 | return -EINVAL; |
| 830 | bus_idx = dev->cec_output2bus_map[edid->pad]; | ||
| 831 | adap = dev->cec_tx_adap[bus_idx]; | ||
| 826 | } | 832 | } |
| 827 | if (edid->start_block == 0 && edid->blocks == 0) { | 833 | if (edid->start_block == 0 && edid->blocks == 0) { |
| 828 | edid->blocks = dev->edid_blocks; | 834 | edid->blocks = dev->edid_blocks; |
| @@ -835,5 +841,6 @@ int vidioc_g_edid(struct file *file, void *_fh, · 6 unmodified lines | |||
| 835 | if (edid->start_block + edid->blocks > dev->edid_blocks) | 841 | if (edid->start_block + edid->blocks > dev->edid_blocks) |
| 836 | edid->blocks = dev->edid_blocks - edid->start_block; | 842 | edid->blocks = dev->edid_blocks - edid->start_block; |
| 837 | memcpy(edid->edid, dev->edid, edid->blocks * 128); | 843 | memcpy(edid->edid, dev->edid, edid->blocks * 128); |
| 844 | cec_set_edid_phys_addr(edid->edid, edid->blocks * 128, adap->phys_addr); | ||
| 838 | return 0; | 845 | return 0; |
| 839 | } | 846 | } |
| @@ -804,6 +804,7 @@ static const struct { · 803 unmodified lines | |||
| 804 | { RC_BIT_SHARP, "sharp", "ir-sharp-decoder" }, | 804 | { RC_BIT_SHARP, "sharp", "ir-sharp-decoder" }, |
| 805 | { RC_BIT_MCE_KBD, "mce_kbd", "ir-mce_kbd-decoder" }, | 805 | { RC_BIT_MCE_KBD, "mce_kbd", "ir-mce_kbd-decoder" }, |
| 806 | { RC_BIT_XMP, "xmp", "ir-xmp-decoder" }, | 806 | { RC_BIT_XMP, "xmp", "ir-xmp-decoder" }, |
| 807 | { RC_BIT_CEC, "cec", NULL }, | ||
| 807 | }; | 808 | }; |
| 808 | 809 | ||
| 809 | /** | 810 | /** |
| @@ -21,6 +21,8 @@ if STAGING_MEDIA · 20 unmodified lines | |||
| 21 | # Please keep them in alphabetic order | 21 | # Please keep them in alphabetic order |
| 22 | source "drivers/staging/media/bcm2048/Kconfig" | 22 | source "drivers/staging/media/bcm2048/Kconfig" |
| 23 | 23 | ||
| 24 | source "drivers/staging/media/cec/Kconfig" | ||
| 25 | |||
| 24 | source "drivers/staging/media/cxd2099/Kconfig" | 26 | source "drivers/staging/media/cxd2099/Kconfig" |
| 25 | 27 | ||
| 26 | source "drivers/staging/media/davinci_vpfe/Kconfig" | 28 | source "drivers/staging/media/davinci_vpfe/Kconfig" |
| @@ -29,6 +31,8 @@ source "drivers/staging/media/omap4iss/Kconfig" · 2 unmodified lines | |||
| 29 | 31 | ||
| 30 | source "drivers/staging/media/tw686x-kh/Kconfig" | 32 | source "drivers/staging/media/tw686x-kh/Kconfig" |
| 31 | 33 | ||
| 34 | source "drivers/staging/media/s5p-cec/Kconfig" | ||
| 35 | |||
| 32 | # Keep LIRC at the end, as it has sub-menus | 36 | # Keep LIRC at the end, as it has sub-menus |
| 33 | source "drivers/staging/media/lirc/Kconfig" | 37 | source "drivers/staging/media/lirc/Kconfig" |
| 34 | 38 | ||
| @@ -1,4 +1,6 @@ | |||
| 1 | obj-$(CONFIG_I2C_BCM2048) += bcm2048/ | 1 | obj-$(CONFIG_I2C_BCM2048) += bcm2048/ |
| 2 | obj-$(CONFIG_MEDIA_CEC) += cec/ | ||
| 3 | obj-$(CONFIG_VIDEO_SAMSUNG_S5P_CEC) += s5p-cec/ | ||
| 2 | obj-$(CONFIG_DVB_CXD2099) += cxd2099/ | 4 | obj-$(CONFIG_DVB_CXD2099) += cxd2099/ |
| 3 | obj-$(CONFIG_LIRC_STAGING) += lirc/ | 5 | obj-$(CONFIG_LIRC_STAGING) += lirc/ |
| 4 | obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/ | 6 | obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/ |
| @@ -0,0 +1,14 @@ | |||
| 1 | config MEDIA_CEC | ||
| 2 | tristate "CEC API (EXPERIMENTAL)" | ||
| 3 | select MEDIA_CEC_EDID | ||
| 4 | ---help--- | ||
| 5 | Enable the CEC API. | ||
| 6 | |||
| 7 | To compile this driver as a module, choose M here: the | ||
| 8 | module will be called cec. | ||
| 9 | |||
| 10 | config MEDIA_CEC_DEBUG | ||
| 11 | bool "CEC debugfs interface (EXPERIMENTAL)" | ||
| 12 | depends on MEDIA_CEC && DEBUG_FS | ||
| 13 | ---help--- | ||
| 14 | Turns on the DebugFS interface for CEC devices. | ||
| @@ -0,0 +1,3 @@ | |||
| 1 | cec-objs := cec-core.o cec-adap.o cec-api.o | ||
| 2 | |||
| 3 | obj-$(CONFIG_MEDIA_CEC) += cec.o | ||
| @@ -0,0 +1,27 @@ | |||
| 1 | The reason why cec.c is still in staging is that I would like | ||
| 2 | to have a bit more confidence in the uABI. The kABI is fine, | ||
| 3 | no problem there, but I would like to let the public API mature | ||
| 4 | a bit. | ||
| 5 | |||
| 6 | Once I'm confident that I didn't miss anything then the cec.c source | ||
| 7 | can move to drivers/media and the linux/cec.h and linux/cec-funcs.h | ||
| 8 | headers can move to uapi/linux and added to uapi/linux/Kbuild to make | ||
| 9 | them public. | ||
| 10 | |||
| 11 | Hopefully this will happen later in 2016. | ||
| 12 | |||
| 13 | Other TODOs: | ||
| 14 | |||
| 15 | - Add a flag to inhibit passing CEC RC messages to the rc subsystem. | ||
| 16 | Applications should be able to choose this when calling S_LOG_ADDRS. | ||
| 17 | - Convert cec.txt to sphinx. | ||
| 18 | - If the reply field of cec_msg is set then when the reply arrives it | ||
| 19 | is only sent to the filehandle that transmitted the original message | ||
| 20 | and not to any followers. Should this behavior change or perhaps | ||
| 21 | controlled through a cec_msg flag? | ||
| 22 | - Should CEC_LOG_ADDR_TYPE_SPECIFIC be replaced by TYPE_2ND_TV and TYPE_PROCESSOR? | ||
| 23 | And also TYPE_SWITCH and TYPE_CDC_ONLY in addition to the TYPE_UNREGISTERED? | ||
| 24 | This should give the framework more information about the device type | ||
| 25 | since SPECIFIC and UNREGISTERED give no useful information. | ||
| 26 | |||
| 27 | Hans Verkuil <hans.verkuil@cisco.com> | ||
| @@ -0,0 +1,1627 @@ | |||
| 1 | /* | ||
| 2 | * cec-adap.c - HDMI Consumer Electronics Control framework - CEC adapter | ||
| 3 | * | ||
| 4 | * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved. | ||
| 5 | * | ||
| 6 | * This program is free software; you may redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; version 2 of the License. | ||
| 9 | * | ||
| 10 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
| 11 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
| 12 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
| 13 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||
| 14 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
| 15 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
| 16 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| 17 | * SOFTWARE. | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include <linux/errno.h> | ||
| 21 | #include <linux/init.h> | ||
| 22 | #include <linux/module.h> | ||
| 23 | #include <linux/kernel.h> | ||
| 24 | #include <linux/kmod.h> | ||
| 25 | #include <linux/ktime.h> | ||
| 26 | #include <linux/slab.h> | ||
| 27 | #include <linux/mm.h> | ||
| 28 | #include <linux/string.h> | ||
| 29 | #include <linux/types.h> | ||
| 30 | |||
| 31 | #include "cec-priv.h" | ||
| 32 | |||
| 33 | static int cec_report_features(struct cec_adapter *adap, unsigned int la_idx); | ||
| 34 | static int cec_report_phys_addr(struct cec_adapter *adap, unsigned int la_idx); | ||
| 35 | |||
| 36 | /* | ||
| 37 | * 400 ms is the time it takes for one 16 byte message to be | ||
| 38 | * transferred and 5 is the maximum number of retries. Add | ||
| 39 | * another 100 ms as a margin. So if the transmit doesn't | ||
| 40 | * finish before that time something is really wrong and we | ||
| 41 | * have to time out. | ||
| 42 | * | ||
| 43 | * This is a sign that something it really wrong and a warning | ||
| 44 | * will be issued. | ||
| 45 | */ | ||
| 46 | #define CEC_XFER_TIMEOUT_MS (5 * 400 + 100) | ||
| 47 | |||
| 48 | #define call_op(adap, op, arg...) \ | ||
| 49 | (adap->ops->op ? adap->ops->op(adap, ## arg) : 0) | ||
| 50 | |||
| 51 | #define call_void_op(adap, op, arg...) \ | ||
| 52 | do { \ | ||
| 53 | if (adap->ops->op) \ | ||
| 54 | adap->ops->op(adap, ## arg); \ | ||
| 55 | } while (0) | ||
| 56 | |||
| 57 | static int cec_log_addr2idx(const struct cec_adapter *adap, u8 log_addr) | ||
| 58 | { | ||
| 59 | int i; | ||
| 60 | |||
| 61 | for (i = 0; i < adap->log_addrs.num_log_addrs; i++) | ||
| 62 | if (adap->log_addrs.log_addr[i] == log_addr) | ||
| 63 | return i; | ||
| 64 | return -1; | ||
| 65 | } | ||
| 66 | |||
| 67 | static unsigned int cec_log_addr2dev(const struct cec_adapter *adap, u8 log_addr) | ||
| 68 | { | ||
| 69 | int i = cec_log_addr2idx(adap, log_addr); | ||
| 70 | |||
| 71 | return adap->log_addrs.primary_device_type[i < 0 ? 0 : i]; | ||
| 72 | } | ||
| 73 | |||
| 74 | /* | ||
| 75 | * Queue a new event for this filehandle. If ts == 0, then set it | ||
| 76 | * to the current time. | ||
| 77 | * | ||
| 78 | * The two events that are currently defined do not need to keep track | ||
| 79 | * of intermediate events, so no actual queue of events is needed, | ||
| 80 | * instead just store the latest state and the total number of lost | ||
| 81 | * messages. | ||
| 82 | * | ||
| 83 | * Should new events be added in the future that require intermediate | ||
| 84 | * results to be queued as well, then a proper queue data structure is | ||
| 85 | * required. But until then, just keep it simple. | ||
| 86 | */ | ||
| 87 | void cec_queue_event_fh(struct cec_fh *fh, | ||
| 88 | const struct cec_event *new_ev, u64 ts) | ||
| 89 | { | ||
| 90 | struct cec_event *ev = &fh->events[new_ev->event - 1]; | ||
| 91 | |||
| 92 | if (ts == 0) | ||
| 93 | ts = ktime_get_ns(); | ||
| 94 | |||
| 95 | mutex_lock(&fh->lock); | ||
| 96 | if (new_ev->event == CEC_EVENT_LOST_MSGS && | ||
| 97 | fh->pending_events & (1 << new_ev->event)) { | ||
| 98 | /* | ||
| 99 | * If there is already a lost_msgs event, then just | ||
| 100 | * update the lost_msgs count. This effectively | ||
| 101 | * merges the old and new events into one. | ||
| 102 | */ | ||
| 103 | ev->lost_msgs.lost_msgs += new_ev->lost_msgs.lost_msgs; | ||
| 104 | goto unlock; | ||
| 105 | } | ||
| 106 | |||
| 107 | /* | ||
| 108 | * Intermediate states are not interesting, so just | ||
| 109 | * overwrite any older event. | ||
| 110 | */ | ||
| 111 | *ev = *new_ev; | ||
| 112 | ev->ts = ts; | ||
| 113 | fh->pending_events |= 1 << new_ev->event; | ||
| 114 | |||
| 115 | unlock: | ||
| 116 | mutex_unlock(&fh->lock); | ||
| 117 | wake_up_interruptible(&fh->wait); | ||
| 118 | } | ||
| 119 | |||
| 120 | /* Queue a new event for all open filehandles. */ | ||
| 121 | static void cec_queue_event(struct cec_adapter *adap, | ||
| 122 | const struct cec_event *ev) | ||
| 123 | { | ||
| 124 | u64 ts = ktime_get_ns(); | ||
| 125 | struct cec_fh *fh; | ||
| 126 | |||
| 127 | mutex_lock(&adap->devnode.fhs_lock); | ||
| 128 | list_for_each_entry(fh, &adap->devnode.fhs, list) | ||
| 129 | cec_queue_event_fh(fh, ev, ts); | ||
| 130 | mutex_unlock(&adap->devnode.fhs_lock); | ||
| 131 | } | ||
| 132 | |||
| 133 | /* | ||
| 134 | * Queue a new message for this filehandle. If there is no more room | ||
| 135 | * in the queue, then send the LOST_MSGS event instead. | ||
| 136 | */ | ||
| 137 | static void cec_queue_msg_fh(struct cec_fh *fh, const struct cec_msg *msg) | ||
| 138 | { | ||
| 139 | static const struct cec_event ev_lost_msg = { | ||
| 140 | .event = CEC_EVENT_LOST_MSGS, | ||
| 141 | .lost_msgs.lost_msgs = 1, | ||
| 142 | }; | ||
| 143 | struct cec_msg_entry *entry; | ||
| 144 | |||
| 145 | mutex_lock(&fh->lock); | ||
| 146 | entry = kmalloc(sizeof(*entry), GFP_KERNEL); | ||
| 147 | if (!entry) | ||
| 148 | goto lost_msgs; | ||
| 149 | |||
| 150 | entry->msg = *msg; | ||
| 151 | /* Add new msg at the end of the queue */ | ||
| 152 | list_add_tail(&entry->list, &fh->msgs); | ||
| 153 | |||
| 154 | /* | ||
| 155 | * if the queue now has more than CEC_MAX_MSG_QUEUE_SZ | ||
| 156 | * messages, drop the oldest one and send a lost message event. | ||
| 157 | */ | ||
| 158 | if (fh->queued_msgs == CEC_MAX_MSG_QUEUE_SZ) { | ||
| 159 | list_del(&entry->list); | ||
| 160 | goto lost_msgs; | ||
| 161 | } | ||
| 162 | fh->queued_msgs++; | ||
| 163 | mutex_unlock(&fh->lock); | ||
| 164 | wake_up_interruptible(&fh->wait); | ||
| 165 | return; | ||
| 166 | |||
| 167 | lost_msgs: | ||
| 168 | mutex_unlock(&fh->lock); | ||
| 169 | cec_queue_event_fh(fh, &ev_lost_msg, 0); | ||
| 170 | } | ||
| 171 | |||
| 172 | /* | ||
| 173 | * Queue the message for those filehandles that are in monitor mode. | ||
| 174 | * If valid_la is true (this message is for us or was sent by us), | ||
| 175 | * then pass it on to any monitoring filehandle. If this message | ||
| 176 | * isn't for us or from us, then only give it to filehandles that | ||
| 177 | * are in MONITOR_ALL mode. | ||
| 178 | * | ||
| 179 | * This can only happen if the CEC_CAP_MONITOR_ALL capability is | ||
| 180 | * set and the CEC adapter was placed in 'monitor all' mode. | ||
| 181 | */ | ||
| 182 | static void cec_queue_msg_monitor(struct cec_adapter *adap, | ||
| 183 | const struct cec_msg *msg, | ||
| 184 | bool valid_la) | ||
| 185 | { | ||
| 186 | struct cec_fh *fh; | ||
| 187 | u32 monitor_mode = valid_la ? CEC_MODE_MONITOR : | ||
| 188 | CEC_MODE_MONITOR_ALL; | ||
| 189 | |||
| 190 | mutex_lock(&adap->devnode.fhs_lock); | ||
| 191 | list_for_each_entry(fh, &adap->devnode.fhs, list) { | ||
| 192 | if (fh->mode_follower >= monitor_mode) | ||
| 193 | cec_queue_msg_fh(fh, msg); | ||
| 194 | } | ||
| 195 | mutex_unlock(&adap->devnode.fhs_lock); | ||
| 196 | } | ||
| 197 | |||
| 198 | /* | ||
| 199 | * Queue the message for follower filehandles. | ||
| 200 | */ | ||
| 201 | static void cec_queue_msg_followers(struct cec_adapter *adap, | ||
| 202 | const struct cec_msg *msg) | ||
| 203 | { | ||
| 204 | struct cec_fh *fh; | ||
| 205 | |||
| 206 | mutex_lock(&adap->devnode.fhs_lock); | ||
| 207 | list_for_each_entry(fh, &adap->devnode.fhs, list) { | ||
| 208 | if (fh->mode_follower == CEC_MODE_FOLLOWER) | ||
| 209 | cec_queue_msg_fh(fh, msg); | ||
| 210 | } | ||
| 211 | mutex_unlock(&adap->devnode.fhs_lock); | ||
| 212 | } | ||
| 213 | |||
| 214 | /* Notify userspace of an adapter state change. */ | ||
| 215 | static void cec_post_state_event(struct cec_adapter *adap) | ||
| 216 | { | ||
| 217 | struct cec_event ev = { | ||
| 218 | .event = CEC_EVENT_STATE_CHANGE, | ||
| 219 | }; | ||
| 220 | |||
| 221 | ev.state_change.phys_addr = adap->phys_addr; | ||
| 222 | ev.state_change.log_addr_mask = adap->log_addrs.log_addr_mask; | ||
| 223 | cec_queue_event(adap, &ev); | ||
| 224 | } | ||
| 225 | |||
| 226 | /* | ||
| 227 | * A CEC transmit (and a possible wait for reply) completed. | ||
| 228 | * If this was in blocking mode, then complete it, otherwise | ||
| 229 | * queue the message for userspace to dequeue later. | ||
| 230 | * | ||
| 231 | * This function is called with adap->lock held. | ||
| 232 | */ | ||
| 233 | static void cec_data_completed(struct cec_data *data) | ||
| 234 | { | ||
| 235 | /* | ||
| 236 | * Delete this transmit from the filehandle's xfer_list since | ||
| 237 | * we're done with it. | ||
| 238 | * | ||
| 239 | * Note that if the filehandle is closed before this transmit | ||
| 240 | * finished, then the release() function will set data->fh to NULL. | ||
| 241 | * Without that we would be referring to a closed filehandle. | ||
| 242 | */ | ||
| 243 | if (data->fh) | ||
| 244 | list_del(&data->xfer_list); | ||
| 245 | |||
| 246 | if (data->blocking) { | ||
| 247 | /* | ||
| 248 | * Someone is blocking so mark the message as completed | ||
| 249 | * and call complete. | ||
| 250 | */ | ||
| 251 | data->completed = true; | ||
| 252 | complete(&data->c); | ||
| 253 | } else { | ||
| 254 | /* | ||
| 255 | * No blocking, so just queue the message if needed and | ||
| 256 | * free the memory. | ||
| 257 | */ | ||
| 258 | if (data->fh) | ||
| 259 | cec_queue_msg_fh(data->fh, &data->msg); | ||
| 260 | kfree(data); | ||
| 261 | } | ||
| 262 | } | ||
| 263 | |||
| 264 | /* | ||
| 265 | * A pending CEC transmit needs to be cancelled, either because the CEC | ||
| 266 | * adapter is disabled or the transmit takes an impossibly long time to | ||
| 267 | * finish. | ||
| 268 | * | ||
| 269 | * This function is called with adap->lock held. | ||
| 270 | */ | ||
| 271 | static void cec_data_cancel(struct cec_data *data) | ||
| 272 | { | ||
| 273 | /* | ||
| 274 | * It's either the current transmit, or it is a pending | ||
| 275 | * transmit. Take the appropriate action to clear it. | ||
| 276 | */ | ||
| 277 | if (data->adap->transmitting == data) | ||
| 278 | data->adap->transmitting = NULL; | ||
| 279 | else | ||
| 280 | list_del_init(&data->list); | ||
| 281 | |||
| 282 | /* Mark it as an error */ | ||
| 283 | data->msg.ts = ktime_get_ns(); | ||
| 284 | data->msg.tx_status = CEC_TX_STATUS_ERROR | | ||
| 285 | CEC_TX_STATUS_MAX_RETRIES; | ||
| 286 | data->attempts = 0; | ||
| 287 | data->msg.tx_error_cnt = 1; | ||
| 288 | data->msg.reply = 0; | ||
| 289 | /* Queue transmitted message for monitoring purposes */ | ||
| 290 | cec_queue_msg_monitor(data->adap, &data->msg, 1); | ||
| 291 | |||
| 292 | cec_data_completed(data); | ||
| 293 | } | ||
| 294 | |||
| 295 | /* | ||
| 296 | * Main CEC state machine | ||
| 297 | * | ||
| 298 | * Wait until the thread should be stopped, or we are not transmitting and | ||
| 299 | * a new transmit message is queued up, in which case we start transmitting | ||
| 300 | * that message. When the adapter finished transmitting the message it will | ||
| 301 | * call cec_transmit_done(). | ||
| 302 | * | ||
| 303 | * If the adapter is disabled, then remove all queued messages instead. | ||
| 304 | * | ||
| 305 | * If the current transmit times out, then cancel that transmit. | ||
| 306 | */ | ||
| 307 | int cec_thread_func(void *_adap) | ||
| 308 | { | ||
| 309 | struct cec_adapter *adap = _adap; | ||
| 310 | |||
| 311 | for (;;) { | ||
| 312 | unsigned int signal_free_time; | ||
| 313 | struct cec_data *data; | ||
| 314 | bool timeout = false; | ||
| 315 | u8 attempts; | ||
| 316 | |||
| 317 | if (adap->transmitting) { | ||
| 318 | int err; | ||
| 319 | |||
| 320 | /* | ||
| 321 | * We are transmitting a message, so add a timeout | ||
| 322 | * to prevent the state machine to get stuck waiting | ||
| 323 | * for this message to finalize and add a check to | ||
| 324 | * see if the adapter is disabled in which case the | ||
| 325 | * transmit should be canceled. | ||
| 326 | */ | ||
| 327 | err = wait_event_interruptible_timeout(adap->kthread_waitq, | ||
| 328 | kthread_should_stop() || | ||
| 329 | adap->phys_addr == CEC_PHYS_ADDR_INVALID || | ||
| 330 | (!adap->transmitting && | ||
| 331 | !list_empty(&adap->transmit_queue)), | ||
| 332 | msecs_to_jiffies(CEC_XFER_TIMEOUT_MS)); | ||
| 333 | timeout = err == 0; | ||
| 334 | } else { | ||
| 335 | /* Otherwise we just wait for something to happen. */ | ||
| 336 | wait_event_interruptible(adap->kthread_waitq, | ||
| 337 | kthread_should_stop() || | ||
| 338 | (!adap->transmitting && | ||
| 339 | !list_empty(&adap->transmit_queue))); | ||
| 340 | } | ||
| 341 | |||
| 342 | mutex_lock(&adap->lock); | ||
| 343 | |||
| 344 | if (adap->phys_addr == CEC_PHYS_ADDR_INVALID || | ||
| 345 | kthread_should_stop()) { | ||
| 346 | /* | ||
| 347 | * If the adapter is disabled, or we're asked to stop, | ||
| 348 | * then cancel any pending transmits. | ||
| 349 | */ | ||
| 350 | while (!list_empty(&adap->transmit_queue)) { | ||
| 351 | data = list_first_entry(&adap->transmit_queue, | ||
| 352 | struct cec_data, list); | ||
| 353 | cec_data_cancel(data); | ||
| 354 | } | ||
| 355 | if (adap->transmitting) | ||
| 356 | cec_data_cancel(adap->transmitting); | ||
| 357 | |||
| 358 | /* | ||
| 359 | * Cancel the pending timeout work. We have to unlock | ||
| 360 | * the mutex when flushing the work since | ||
| 361 | * cec_wait_timeout() will take it. This is OK since | ||
| 362 | * no new entries can be added to wait_queue as long | ||
| 363 | * as adap->transmitting is NULL, which it is due to | ||
| 364 | * the cec_data_cancel() above. | ||
| 365 | */ | ||
| 366 | while (!list_empty(&adap->wait_queue)) { | ||
| 367 | data = list_first_entry(&adap->wait_queue, | ||
| 368 | struct cec_data, list); | ||
| 369 | |||
| 370 | if (!cancel_delayed_work(&data->work)) { | ||
| 371 | mutex_unlock(&adap->lock); | ||
| 372 | flush_scheduled_work(); | ||
| 373 | mutex_lock(&adap->lock); | ||
| 374 | } | ||
| 375 | cec_data_cancel(data); | ||
| 376 | } | ||
| 377 | goto unlock; | ||
| 378 | } | ||
| 379 | |||
| 380 | if (adap->transmitting && timeout) { | ||
| 381 | /* | ||
| 382 | * If we timeout, then log that. This really shouldn't | ||
| 383 | * happen and is an indication of a faulty CEC adapter | ||
| 384 | * driver, or the CEC bus is in some weird state. | ||
| 385 | */ | ||
| 386 | dprintk(0, "message %*ph timed out!\n", | ||
| 387 | adap->transmitting->msg.len, | ||
| 388 | adap->transmitting->msg.msg); | ||
| 389 | /* Just give up on this. */ | ||
| 390 | cec_data_cancel(adap->transmitting); | ||
| 391 | goto unlock; | ||
| 392 | } | ||
| 393 | |||
| 394 | /* | ||
| 395 | * If we are still transmitting, or there is nothing new to | ||
| 396 | * transmit, then just continue waiting. | ||
| 397 | */ | ||
| 398 | if (adap->transmitting || list_empty(&adap->transmit_queue)) | ||
| 399 | goto unlock; | ||
| 400 | |||
| 401 | /* Get a new message to transmit */ | ||
| 402 | data = list_first_entry(&adap->transmit_queue, | ||
| 403 | struct cec_data, list); | ||
| 404 | list_del_init(&data->list); | ||
| 405 | /* Make this the current transmitting message */ | ||
| 406 | adap->transmitting = data; | ||
| 407 | |||
| 408 | /* | ||
| 409 | * Suggested number of attempts as per the CEC 2.0 spec: | ||
| 410 | * 4 attempts is the default, except for 'secondary poll | ||
| 411 | * messages', i.e. poll messages not sent during the adapter | ||
| 412 | * configuration phase when it allocates logical addresses. | ||
| 413 | */ | ||
| 414 | if (data->msg.len == 1 && adap->is_configured) | ||
| 415 | attempts = 2; | ||
| 416 | else | ||
| 417 | attempts = 4; | ||
| 418 | |||
| 419 | /* Set the suggested signal free time */ | ||
| 420 | if (data->attempts) { | ||
| 421 | /* should be >= 3 data bit periods for a retry */ | ||
| 422 | signal_free_time = CEC_SIGNAL_FREE_TIME_RETRY; | ||
| 423 | } else if (data->new_initiator) { | ||
| 424 | /* should be >= 5 data bit periods for new initiator */ | ||
| 425 | signal_free_time = CEC_SIGNAL_FREE_TIME_NEW_INITIATOR; | ||
| 426 | } else { | ||
| 427 | /* | ||
| 428 | * should be >= 7 data bit periods for sending another | ||
| 429 | * frame immediately after another. | ||
| 430 | */ | ||
| 431 | signal_free_time = CEC_SIGNAL_FREE_TIME_NEXT_XFER; | ||
| 432 | } | ||
| 433 | if (data->attempts == 0) | ||
| 434 | data->attempts = attempts; | ||
| 435 | |||
| 436 | /* Tell the adapter to transmit, cancel on error */ | ||
| 437 | if (adap->ops->adap_transmit(adap, data->attempts, | ||
| 438 | signal_free_time, &data->msg)) | ||
| 439 | cec_data_cancel(data); | ||
| 440 | |||
| 441 | unlock: | ||
| 442 | mutex_unlock(&adap->lock); | ||
| 443 | |||
| 444 | if (kthread_should_stop()) | ||
| 445 | break; | ||
| 446 | } | ||
| 447 | return 0; | ||
| 448 | } | ||
| 449 | |||
| 450 | /* | ||
| 451 | * Called by the CEC adapter if a transmit finished. | ||
| 452 | */ | ||
| 453 | void cec_transmit_done(struct cec_adapter *adap, u8 status, u8 arb_lost_cnt, | ||
| 454 | u8 nack_cnt, u8 low_drive_cnt, u8 error_cnt) | ||
| 455 | { | ||
| 456 | struct cec_data *data; | ||
| 457 | struct cec_msg *msg; | ||
| 458 | |||
| 459 | dprintk(2, "cec_transmit_done %02x\n", status); | ||
| 460 | mutex_lock(&adap->lock); | ||
| 461 | data = adap->transmitting; | ||
| 462 | if (!data) { | ||
| 463 | /* | ||
| 464 | * This can happen if a transmit was issued and the cable is | ||
| 465 | * unplugged while the transmit is ongoing. Ignore this | ||
| 466 | * transmit in that case. | ||
| 467 | */ | ||
| 468 | dprintk(1, "cec_transmit_done without an ongoing transmit!\n"); | ||
| 469 | goto unlock; | ||
| 470 | } | ||
| 471 | |||
| 472 | msg = &data->msg; | ||
| 473 | |||
| 474 | /* Drivers must fill in the status! */ | ||
| 475 | WARN_ON(status == 0); | ||
| 476 | msg->ts = ktime_get_ns(); | ||
| 477 | msg->tx_status |= status; | ||
| 478 | msg->tx_arb_lost_cnt += arb_lost_cnt; | ||
| 479 | msg->tx_nack_cnt += nack_cnt; | ||
| 480 | msg->tx_low_drive_cnt += low_drive_cnt; | ||
| 481 | msg->tx_error_cnt += error_cnt; | ||
| 482 | |||
| 483 | /* Mark that we're done with this transmit */ | ||
| 484 | adap->transmitting = NULL; | ||
| 485 | |||
| 486 | /* | ||
| 487 | * If there are still retry attempts left and there was an error and | ||
| 488 | * the hardware didn't signal that it retried itself (by setting | ||
| 489 | * CEC_TX_STATUS_MAX_RETRIES), then we will retry ourselves. | ||
| 490 | */ | ||
| 491 | if (data->attempts > 1 && | ||
| 492 | !(status & (CEC_TX_STATUS_MAX_RETRIES | CEC_TX_STATUS_OK))) { | ||
| 493 | /* Retry this message */ | ||
| 494 | data->attempts--; | ||
| 495 | /* Add the message in front of the transmit queue */ | ||
| 496 | list_add(&data->list, &adap->transmit_queue); | ||
| 497 | goto wake_thread; | ||
| 498 | } | ||
| 499 | |||
| 500 | data->attempts = 0; | ||
| 501 | |||
| 502 | /* Always set CEC_TX_STATUS_MAX_RETRIES on error */ | ||
| 503 | if (!(status & CEC_TX_STATUS_OK)) | ||
| 504 | msg->tx_status |= CEC_TX_STATUS_MAX_RETRIES; | ||
| 505 | |||
| 506 | /* Queue transmitted message for monitoring purposes */ | ||
| 507 | cec_queue_msg_monitor(adap, msg, 1); | ||
| 508 | |||
| 509 | /* | ||
| 510 | * Clear reply and timeout on error or if the adapter is no longer | ||
| 511 | * configured. It makes no sense to wait for a reply in that case. | ||
| 512 | */ | ||
| 513 | if (!(status & CEC_TX_STATUS_OK) || !adap->is_configured) { | ||
| 514 | msg->reply = 0; | ||
| 515 | msg->timeout = 0; | ||
| 516 | } | ||
| 517 | |||
| 518 | if (msg->timeout) { | ||
| 519 | /* | ||
| 520 | * Queue the message into the wait queue if we want to wait | ||
| 521 | * for a reply. | ||
| 522 | */ | ||
| 523 | list_add_tail(&data->list, &adap->wait_queue); | ||
| 524 | schedule_delayed_work(&data->work, | ||
| 525 | msecs_to_jiffies(msg->timeout)); | ||
| 526 | } else { | ||
| 527 | /* Otherwise we're done */ | ||
| 528 | cec_data_completed(data); | ||
| 529 | } | ||
| 530 | |||
| 531 | wake_thread: | ||
| 532 | /* | ||
| 533 | * Wake up the main thread to see if another message is ready | ||
| 534 | * for transmitting or to retry the current message. | ||
| 535 | */ | ||
| 536 | wake_up_interruptible(&adap->kthread_waitq); | ||
| 537 | unlock: | ||
| 538 | mutex_unlock(&adap->lock); | ||
| 539 | } | ||
| 540 | EXPORT_SYMBOL_GPL(cec_transmit_done); | ||
| 541 | |||
| 542 | /* | ||
| 543 | * Called when waiting for a reply times out. | ||
| 544 | */ | ||
| 545 | static void cec_wait_timeout(struct work_struct *work) | ||
| 546 | { | ||
| 547 | struct cec_data *data = container_of(work, struct cec_data, work.work); | ||
| 548 | struct cec_adapter *adap = data->adap; | ||
| 549 | |||
| 550 | mutex_lock(&adap->lock); | ||
| 551 | /* | ||
| 552 | * Sanity check in case the timeout and the arrival of the message | ||
| 553 | * happened at the same time. | ||
| 554 | */ | ||
| 555 | if (list_empty(&data->list)) | ||
| 556 | goto unlock; | ||
| 557 | |||
| 558 | /* Mark the message as timed out */ | ||
| 559 | list_del_init(&data->list); | ||
| 560 | data->msg.ts = ktime_get_ns(); | ||
| 561 | data->msg.rx_status = CEC_RX_STATUS_TIMEOUT; | ||
| 562 | cec_data_completed(data); | ||
| 563 | unlock: | ||
| 564 | mutex_unlock(&adap->lock); | ||
| 565 | } | ||
| 566 | |||
| 567 | /* | ||
| 568 | * Transmit a message. The fh argument may be NULL if the transmit is not | ||
| 569 | * associated with a specific filehandle. | ||
| 570 | * | ||
| 571 | * This function is called with adap->lock held. | ||
| 572 | */ | ||
| 573 | int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg, | ||
| 574 | struct cec_fh *fh, bool block) | ||
| 575 | { | ||
| 576 | struct cec_data *data; | ||
| 577 | u8 last_initiator = 0xff; | ||
| 578 | unsigned int timeout; | ||
| 579 | int res = 0; | ||
| 580 | |||
| 581 | if (msg->reply && msg->timeout == 0) { | ||
| 582 | /* Make sure the timeout isn't 0. */ | ||
| 583 | msg->timeout = 1000; | ||
| 584 | } | ||
| 585 | |||
| 586 | /* Sanity checks */ | ||
| 587 | if (msg->len == 0 || msg->len > CEC_MAX_MSG_SIZE) { | ||
| 588 | dprintk(1, "cec_transmit_msg: invalid length %d\n", msg->len); | ||
| 589 | return -EINVAL; | ||
| 590 | } | ||
| 591 | if (msg->timeout && msg->len == 1) { | ||
| 592 | dprintk(1, "cec_transmit_msg: can't reply for poll msg\n"); | ||
| 593 | return -EINVAL; | ||
| 594 | } | ||
| 595 | if (msg->len == 1) { | ||
| 596 | if (cec_msg_initiator(msg) != 0xf || | ||
| 597 | cec_msg_destination(msg) == 0xf) { | ||
| 598 | dprintk(1, "cec_transmit_msg: invalid poll message\n"); | ||
| 599 | return -EINVAL; | ||
| 600 | } | ||
| 601 | if (cec_has_log_addr(adap, cec_msg_destination(msg))) { | ||
| 602 | /* | ||
| 603 | * If the destination is a logical address our adapter | ||
| 604 | * has already claimed, then just NACK this. | ||
| 605 | * It depends on the hardware what it will do with a | ||
| 606 | * POLL to itself (some OK this), so it is just as | ||
| 607 | * easy to handle it here so the behavior will be | ||
| 608 | * consistent. | ||
| 609 | */ | ||
| 610 | msg->tx_status = CEC_TX_STATUS_NACK | | ||
| 611 | CEC_TX_STATUS_MAX_RETRIES; | ||
| 612 | msg->tx_nack_cnt = 1; | ||
| 613 | return 0; | ||
| 614 | } | ||
| 615 | } | ||
| 616 | if (msg->len > 1 && !cec_msg_is_broadcast(msg) && | ||
| 617 | cec_has_log_addr(adap, cec_msg_destination(msg))) { | ||
| 618 | dprintk(1, "cec_transmit_msg: destination is the adapter itself\n"); | ||
| 619 | return -EINVAL; | ||
| 620 | } | ||
| 621 | if (cec_msg_initiator(msg) != 0xf && | ||
| 622 | !cec_has_log_addr(adap, cec_msg_initiator(msg))) { | ||
| 623 | dprintk(1, "cec_transmit_msg: initiator has unknown logical address %d\n", | ||
| 624 | cec_msg_initiator(msg)); | ||
| 625 | return -EINVAL; | ||
| 626 | } | ||
| 627 | if (!adap->is_configured && !adap->is_configuring) | ||
| 628 | return -ENONET; | ||
| 629 | |||
| 630 | data = kzalloc(sizeof(*data), GFP_KERNEL); | ||
| 631 | if (!data) | ||
| 632 | return -ENOMEM; | ||
| 633 | |||
| 634 | if (msg->len > 1 && msg->msg[1] == CEC_MSG_CDC_MESSAGE) { | ||
| 635 | msg->msg[2] = adap->phys_addr >> 8; | ||
| 636 | msg->msg[3] = adap->phys_addr & 0xff; | ||
| 637 | } | ||
| 638 | |||
| 639 | if (msg->timeout) | ||
| 640 | dprintk(2, "cec_transmit_msg: %*ph (wait for 0x%02x%s)\n", | ||
| 641 | msg->len, msg->msg, msg->reply, !block ? ", nb" : ""); | ||
| 642 | else | ||
| 643 | dprintk(2, "cec_transmit_msg: %*ph%s\n", | ||
| 644 | msg->len, msg->msg, !block ? " (nb)" : ""); | ||
| 645 | |||
| 646 | msg->rx_status = 0; | ||
| 647 | msg->tx_status = 0; | ||
| 648 | msg->tx_arb_lost_cnt = 0; | ||
| 649 | msg->tx_nack_cnt = 0; | ||
| 650 | msg->tx_low_drive_cnt = 0; | ||
| 651 | msg->tx_error_cnt = 0; | ||
| 652 | data->msg = *msg; | ||
| 653 | data->fh = fh; | ||
| 654 | data->adap = adap; | ||
| 655 | data->blocking = block; | ||
| 656 | |||
| 657 | /* | ||
| 658 | * Determine if this message follows a message from the same | ||
| 659 | * initiator. Needed to determine the free signal time later on. | ||
| 660 | */ | ||
| 661 | if (msg->len > 1) { | ||
| 662 | if (!(list_empty(&adap->transmit_queue))) { | ||
| 663 | const struct cec_data *last; | ||
| 664 | |||
| 665 | last = list_last_entry(&adap->transmit_queue, | ||
| 666 | const struct cec_data, list); | ||
| 667 | last_initiator = cec_msg_initiator(&last->msg); | ||
| 668 | } else if (adap->transmitting) { | ||
| 669 | last_initiator = | ||
| 670 | cec_msg_initiator(&adap->transmitting->msg); | ||
| 671 | } | ||
| 672 | } | ||
| 673 | data->new_initiator = last_initiator != cec_msg_initiator(msg); | ||
| 674 | init_completion(&data->c); | ||
| 675 | INIT_DELAYED_WORK(&data->work, cec_wait_timeout); | ||
| 676 | |||
| 677 | data->msg.sequence = adap->sequence++; | ||
| 678 | if (fh) | ||
| 679 | list_add_tail(&data->xfer_list, &fh->xfer_list); | ||
| 680 | list_add_tail(&data->list, &adap->transmit_queue); | ||
| 681 | if (!adap->transmitting) | ||
| 682 | wake_up_interruptible(&adap->kthread_waitq); | ||
| 683 | |||
| 684 | /* All done if we don't need to block waiting for completion */ | ||
| 685 | if (!block) | ||
| 686 | return 0; | ||
| 687 | |||
| 688 | /* | ||
| 689 | * If we don't get a completion before this time something is really | ||
| 690 | * wrong and we time out. | ||
| 691 | */ | ||
| 692 | timeout = CEC_XFER_TIMEOUT_MS; | ||
| 693 | /* Add the requested timeout if we have to wait for a reply as well */ | ||
| 694 | if (msg->timeout) | ||
| 695 | timeout += msg->timeout; | ||
| 696 | |||
| 697 | /* | ||
| 698 | * Release the lock and wait, retake the lock afterwards. | ||
| 699 | */ | ||
| 700 | mutex_unlock(&adap->lock); | ||
| 701 | res = wait_for_completion_killable_timeout(&data->c, | ||
| 702 | msecs_to_jiffies(timeout)); | ||
| 703 | mutex_lock(&adap->lock); | ||
| 704 | |||
| 705 | if (data->completed) { | ||
| 706 | /* The transmit completed (possibly with an error) */ | ||
| 707 | *msg = data->msg; | ||
| 708 | kfree(data); | ||
| 709 | return 0; | ||
| 710 | } | ||
| 711 | /* | ||
| 712 | * The wait for completion timed out or was interrupted, so mark this | ||
| 713 | * as non-blocking and disconnect from the filehandle since it is | ||
| 714 | * still 'in flight'. When it finally completes it will just drop the | ||
| 715 | * result silently. | ||
| 716 | */ | ||
| 717 | data->blocking = false; | ||
| 718 | if (data->fh) | ||
| 719 | list_del(&data->xfer_list); | ||
| 720 | data->fh = NULL; | ||
| 721 | |||
| 722 | if (res == 0) { /* timed out */ | ||
| 723 | /* Check if the reply or the transmit failed */ | ||
| 724 | if (msg->timeout && (msg->tx_status & CEC_TX_STATUS_OK)) | ||
| 725 | msg->rx_status = CEC_RX_STATUS_TIMEOUT; | ||
| 726 | else | ||
| 727 | msg->tx_status = CEC_TX_STATUS_MAX_RETRIES; | ||
| 728 | } | ||
| 729 | return res > 0 ? 0 : res; | ||
| 730 | } | ||
| 731 | |||
| 732 | /* Helper function to be used by drivers and this framework. */ | ||
| 733 | int cec_transmit_msg(struct cec_adapter *adap, struct cec_msg *msg, | ||
| 734 | bool block) | ||
| 735 | { | ||
| 736 | int ret; | ||
| 737 | |||
| 738 | mutex_lock(&adap->lock); | ||
| 739 | ret = cec_transmit_msg_fh(adap, msg, NULL, block); | ||
| 740 | mutex_unlock(&adap->lock); | ||
| 741 | return ret; | ||
| 742 | } | ||
| 743 | EXPORT_SYMBOL_GPL(cec_transmit_msg); | ||
| 744 | |||
| 745 | /* | ||
| 746 | * I don't like forward references but without this the low-level | ||
| 747 | * cec_received_msg() function would come after a bunch of high-level | ||
| 748 | * CEC protocol handling functions. That was very confusing. | ||
| 749 | */ | ||
| 750 | static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg, | ||
| 751 | bool is_reply); | ||
| 752 | |||
| 753 | /* Called by the CEC adapter if a message is received */ | ||
| 754 | void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg) | ||
| 755 | { | ||
| 756 | struct cec_data *data; | ||
| 757 | u8 msg_init = cec_msg_initiator(msg); | ||
| 758 | u8 msg_dest = cec_msg_destination(msg); | ||
| 759 | bool is_reply = false; | ||
| 760 | bool valid_la = true; | ||
| 761 | |||
| 762 | mutex_lock(&adap->lock); | ||
| 763 | msg->ts = ktime_get_ns(); | ||
| 764 | msg->rx_status = CEC_RX_STATUS_OK; | ||
| 765 | msg->tx_status = 0; | ||
| 766 | msg->sequence = msg->reply = msg->timeout = 0; | ||
| 767 | msg->flags = 0; | ||
| 768 | |||
| 769 | dprintk(2, "cec_received_msg: %*ph\n", msg->len, msg->msg); | ||
| 770 | |||
| 771 | /* Check if this message was for us (directed or broadcast). */ | ||
| 772 | if (!cec_msg_is_broadcast(msg)) | ||
| 773 | valid_la = cec_has_log_addr(adap, msg_dest); | ||
| 774 | |||
| 775 | /* It's a valid message and not a poll or CDC message */ | ||
| 776 | if (valid_la && msg->len > 1 && msg->msg[1] != CEC_MSG_CDC_MESSAGE) { | ||
| 777 | u8 cmd = msg->msg[1]; | ||
| 778 | bool abort = cmd == CEC_MSG_FEATURE_ABORT; | ||
| 779 | |||
| 780 | /* The aborted command is in msg[2] */ | ||
| 781 | if (abort) | ||
| 782 | cmd = msg->msg[2]; | ||
| 783 | |||
| 784 | /* | ||
| 785 | * Walk over all transmitted messages that are waiting for a | ||
| 786 | * reply. | ||
| 787 | */ | ||
| 788 | list_for_each_entry(data, &adap->wait_queue, list) { | ||
| 789 | struct cec_msg *dst = &data->msg; | ||
| 790 | u8 dst_reply; | ||
| 791 | |||
| 792 | /* Does the command match? */ | ||
| 793 | if ((abort && cmd != dst->msg[1]) || | ||
| 794 | (!abort && cmd != dst->reply)) | ||
| 795 | continue; | ||
| 796 | |||
| 797 | /* Does the addressing match? */ | ||
| 798 | if (msg_init != cec_msg_destination(dst) && | ||
| 799 | !cec_msg_is_broadcast(dst)) | ||
| 800 | continue; | ||
| 801 | |||
| 802 | /* We got a reply */ | ||
| 803 | msg->sequence = dst->sequence; | ||
| 804 | dst_reply = dst->reply; | ||
| 805 | *dst = *msg; | ||
| 806 | dst->reply = dst_reply; | ||
| 807 | if (abort) { | ||
| 808 | dst->reply = 0; | ||
| 809 | dst->rx_status |= CEC_RX_STATUS_FEATURE_ABORT; | ||
| 810 | } | ||
| 811 | /* Remove it from the wait_queue */ | ||
| 812 | list_del_init(&data->list); | ||
| 813 | |||
| 814 | /* Cancel the pending timeout work */ | ||
| 815 | if (!cancel_delayed_work(&data->work)) { | ||
| 816 | mutex_unlock(&adap->lock); | ||
| 817 | flush_scheduled_work(); | ||
| 818 | mutex_lock(&adap->lock); | ||
| 819 | } | ||
| 820 | /* | ||
| 821 | * Mark this as a reply, provided someone is still | ||
| 822 | * waiting for the answer. | ||
| 823 | */ | ||
| 824 | if (data->fh) | ||
| 825 | is_reply = true; | ||
| 826 | cec_data_completed(data); | ||
| 827 | break; | ||
| 828 | } | ||
| 829 | } | ||
| 830 | mutex_unlock(&adap->lock); | ||
| 831 | |||
| 832 | /* Pass the message on to any monitoring filehandles */ | ||
| 833 | cec_queue_msg_monitor(adap, msg, valid_la); | ||
| 834 | |||
| 835 | /* We're done if it is not for us or a poll message */ | ||
| 836 | if (!valid_la || msg->len <= 1) | ||
| 837 | return; | ||
| 838 | |||
| 839 | /* | ||
| 840 | * Process the message on the protocol level. If is_reply is true, | ||
| 841 | * then cec_receive_notify() won't pass on the reply to the listener(s) | ||
| 842 | * since that was already done by cec_data_completed() above. | ||
| 843 | */ | ||
| 844 | cec_receive_notify(adap, msg, is_reply); | ||
| 845 | } | ||
| 846 | EXPORT_SYMBOL_GPL(cec_received_msg); | ||
| 847 | |||
| 848 | /* Logical Address Handling */ | ||
| 849 | |||
| 850 | /* | ||
| 851 | * Attempt to claim a specific logical address. | ||
| 852 | * | ||
| 853 | * This function is called with adap->lock held. | ||
| 854 | */ | ||
| 855 | static int cec_config_log_addr(struct cec_adapter *adap, | ||
| 856 | unsigned int idx, | ||
| 857 | unsigned int log_addr) | ||
| 858 | { | ||
| 859 | struct cec_log_addrs *las = &adap->log_addrs; | ||
| 860 | struct cec_msg msg = { }; | ||
| 861 | int err; | ||
| 862 | |||
| 863 | if (cec_has_log_addr(adap, log_addr)) | ||
| 864 | return 0; | ||
| 865 | |||
| 866 | /* Send poll message */ | ||
| 867 | msg.len = 1; | ||
| 868 | msg.msg[0] = 0xf0 | log_addr; | ||
| 869 | err = cec_transmit_msg_fh(adap, &msg, NULL, true); | ||
| 870 | |||
| 871 | /* | ||
| 872 | * While trying to poll the physical address was reset | ||
| 873 | * and the adapter was unconfigured, so bail out. | ||
| 874 | */ | ||
| 875 | if (!adap->is_configuring) | ||
| 876 | return -EINTR; | ||
| 877 | |||
| 878 | if (err) | ||
| 879 | return err; | ||
| 880 | |||
| 881 | if (msg.tx_status & CEC_TX_STATUS_OK) | ||
| 882 | return 0; | ||
| 883 | |||
| 884 | /* | ||
| 885 | * Message not acknowledged, so this logical | ||
| 886 | * address is free to use. | ||
| 887 | */ | ||
| 888 | err = adap->ops->adap_log_addr(adap, log_addr); | ||
| 889 | if (err) | ||
| 890 | return err; | ||
| 891 | |||
| 892 | las->log_addr[idx] = log_addr; | ||
| 893 | las->log_addr_mask |= 1 << log_addr; | ||
| 894 | adap->phys_addrs[log_addr] = adap->phys_addr; | ||
| 895 | |||
| 896 | dprintk(2, "claimed addr %d (%d)\n", log_addr, | ||
| 897 | las->primary_device_type[idx]); | ||
| 898 | return 1; | ||
| 899 | } | ||
| 900 | |||
| 901 | /* | ||
| 902 | * Unconfigure the adapter: clear all logical addresses and send | ||
| 903 | * the state changed event. | ||
| 904 | * | ||
| 905 | * This function is called with adap->lock held. | ||
| 906 | */ | ||
| 907 | static void cec_adap_unconfigure(struct cec_adapter *adap) | ||
| 908 | { | ||
| 909 | WARN_ON(adap->ops->adap_log_addr(adap, CEC_LOG_ADDR_INVALID)); | ||
| 910 | adap->log_addrs.log_addr_mask = 0; | ||
| 911 | adap->is_configuring = false; | ||
| 912 | adap->is_configured = false; | ||
| 913 | memset(adap->phys_addrs, 0xff, sizeof(adap->phys_addrs)); | ||
| 914 | wake_up_interruptible(&adap->kthread_waitq); | ||
| 915 | cec_post_state_event(adap); | ||
| 916 | } | ||
| 917 | |||
| 918 | /* | ||
| 919 | * Attempt to claim the required logical addresses. | ||
| 920 | */ | ||
| 921 | static int cec_config_thread_func(void *arg) | ||
| 922 | { | ||
| 923 | /* The various LAs for each type of device */ | ||
| 924 | static const u8 tv_log_addrs[] = { | ||
| 925 | CEC_LOG_ADDR_TV, CEC_LOG_ADDR_SPECIFIC, | ||
| 926 | CEC_LOG_ADDR_INVALID | ||
| 927 | }; | ||
| 928 | static const u8 record_log_addrs[] = { | ||
| 929 | CEC_LOG_ADDR_RECORD_1, CEC_LOG_ADDR_RECORD_2, | ||
| 930 | CEC_LOG_ADDR_RECORD_3, | ||
| 931 | CEC_LOG_ADDR_BACKUP_1, CEC_LOG_ADDR_BACKUP_2, | ||
| 932 | CEC_LOG_ADDR_INVALID | ||
| 933 | }; | ||
| 934 | static const u8 tuner_log_addrs[] = { | ||
| 935 | CEC_LOG_ADDR_TUNER_1, CEC_LOG_ADDR_TUNER_2, | ||
| 936 | CEC_LOG_ADDR_TUNER_3, CEC_LOG_ADDR_TUNER_4, | ||
| 937 | CEC_LOG_ADDR_BACKUP_1, CEC_LOG_ADDR_BACKUP_2, | ||
| 938 | CEC_LOG_ADDR_INVALID | ||
| 939 | }; | ||
| 940 | static const u8 playback_log_addrs[] = { | ||
| 941 | CEC_LOG_ADDR_PLAYBACK_1, CEC_LOG_ADDR_PLAYBACK_2, | ||
| 942 | CEC_LOG_ADDR_PLAYBACK_3, | ||
| 943 | CEC_LOG_ADDR_BACKUP_1, CEC_LOG_ADDR_BACKUP_2, | ||
| 944 | CEC_LOG_ADDR_INVALID | ||
| 945 | }; | ||
| 946 | static const u8 audiosystem_log_addrs[] = { | ||
| 947 | CEC_LOG_ADDR_AUDIOSYSTEM, | ||
| 948 | CEC_LOG_ADDR_INVALID | ||
| 949 | }; | ||
| 950 | static const u8 specific_use_log_addrs[] = { | ||
| 951 | CEC_LOG_ADDR_SPECIFIC, | ||
| 952 | CEC_LOG_ADDR_BACKUP_1, CEC_LOG_ADDR_BACKUP_2, | ||
| 953 | CEC_LOG_ADDR_INVALID | ||
| 954 | }; | ||
| 955 | static const u8 *type2addrs[6] = { | ||
| 956 | [CEC_LOG_ADDR_TYPE_TV] = tv_log_addrs, | ||
| 957 | [CEC_LOG_ADDR_TYPE_RECORD] = record_log_addrs, | ||
| 958 | [CEC_LOG_ADDR_TYPE_TUNER] = tuner_log_addrs, | ||
| 959 | [CEC_LOG_ADDR_TYPE_PLAYBACK] = playback_log_addrs, | ||
| 960 | [CEC_LOG_ADDR_TYPE_AUDIOSYSTEM] = audiosystem_log_addrs, | ||
| 961 | [CEC_LOG_ADDR_TYPE_SPECIFIC] = specific_use_log_addrs, | ||
| 962 | }; | ||
| 963 | static const u16 type2mask[] = { | ||
| 964 | [CEC_LOG_ADDR_TYPE_TV] = CEC_LOG_ADDR_MASK_TV, | ||
| 965 | [CEC_LOG_ADDR_TYPE_RECORD] = CEC_LOG_ADDR_MASK_RECORD, | ||
| 966 | [CEC_LOG_ADDR_TYPE_TUNER] = CEC_LOG_ADDR_MASK_TUNER, | ||
| 967 | [CEC_LOG_ADDR_TYPE_PLAYBACK] = CEC_LOG_ADDR_MASK_PLAYBACK, | ||
| 968 | [CEC_LOG_ADDR_TYPE_AUDIOSYSTEM] = CEC_LOG_ADDR_MASK_AUDIOSYSTEM, | ||
| 969 | [CEC_LOG_ADDR_TYPE_SPECIFIC] = CEC_LOG_ADDR_MASK_SPECIFIC, | ||
| 970 | }; | ||
| 971 | struct cec_adapter *adap = arg; | ||
| 972 | struct cec_log_addrs *las = &adap->log_addrs; | ||
| 973 | int err; | ||
| 974 | int i, j; | ||
| 975 | |||
| 976 | mutex_lock(&adap->lock); | ||
| 977 | dprintk(1, "physical address: %x.%x.%x.%x, claim %d logical addresses\n", | ||
| 978 | cec_phys_addr_exp(adap->phys_addr), las->num_log_addrs); | ||
| 979 | las->log_addr_mask = 0; | ||
| 980 | |||
| 981 | if (las->log_addr_type[0] == CEC_LOG_ADDR_TYPE_UNREGISTERED) | ||
| 982 | goto configured; | ||
| 983 | |||
| 984 | for (i = 0; i < las->num_log_addrs; i++) { | ||
| 985 | unsigned int type = las->log_addr_type[i]; | ||
| 986 | const u8 *la_list; | ||
| 987 | u8 last_la; | ||
| 988 | |||
| 989 | /* | ||
| 990 | * The TV functionality can only map to physical address 0. | ||
| 991 | * For any other address, try the Specific functionality | ||
| 992 | * instead as per the spec. | ||
| 993 | */ | ||
| 994 | if (adap->phys_addr && type == CEC_LOG_ADDR_TYPE_TV) | ||
| 995 | type = CEC_LOG_ADDR_TYPE_SPECIFIC; | ||
| 996 | |||
| 997 | la_list = type2addrs[type]; | ||
| 998 | last_la = las->log_addr[i]; | ||
| 999 | las->log_addr[i] = CEC_LOG_ADDR_INVALID; | ||
| 1000 | if (last_la == CEC_LOG_ADDR_INVALID || | ||
| 1001 | last_la == CEC_LOG_ADDR_UNREGISTERED || | ||
| 1002 | !(last_la & type2mask[type])) | ||
| 1003 | last_la = la_list[0]; | ||
| 1004 | |||
| 1005 | err = cec_config_log_addr(adap, i, last_la); | ||
| 1006 | if (err > 0) /* Reused last LA */ | ||
| 1007 | continue; | ||
| 1008 | |||
| 1009 | if (err < 0) | ||
| 1010 | goto unconfigure; | ||
| 1011 | |||
| 1012 | for (j = 0; la_list[j] != CEC_LOG_ADDR_INVALID; j++) { | ||
| 1013 | /* Tried this one already, skip it */ | ||
| 1014 | if (la_list[j] == last_la) | ||
| 1015 | continue; | ||
| 1016 | /* The backup addresses are CEC 2.0 specific */ | ||
| 1017 | if ((la_list[j] == CEC_LOG_ADDR_BACKUP_1 || | ||
| 1018 | la_list[j] == CEC_LOG_ADDR_BACKUP_2) && | ||
| 1019 | las->cec_version < CEC_OP_CEC_VERSION_2_0) | ||
| 1020 | continue; | ||
| 1021 | |||
| 1022 | err = cec_config_log_addr(adap, i, la_list[j]); | ||
| 1023 | if (err == 0) /* LA is in use */ | ||
| 1024 | continue; | ||
| 1025 | if (err < 0) | ||
| 1026 | goto unconfigure; | ||
| 1027 | /* Done, claimed an LA */ | ||
| 1028 | break; | ||
| 1029 | } | ||
| 1030 | |||
| 1031 | if (la_list[j] == CEC_LOG_ADDR_INVALID) | ||
| 1032 | dprintk(1, "could not claim LA %d\n", i); | ||
| 1033 | } | ||
| 1034 | |||
| 1035 | configured: | ||
| 1036 | if (adap->log_addrs.log_addr_mask == 0) { | ||
| 1037 | /* Fall back to unregistered */ | ||
| 1038 | las->log_addr[0] = CEC_LOG_ADDR_UNREGISTERED; | ||
| 1039 | las->log_addr_mask = 1 << las->log_addr[0]; | ||
| 1040 | } | ||
| 1041 | adap->is_configured = true; | ||
| 1042 | adap->is_configuring = false; | ||
| 1043 | cec_post_state_event(adap); | ||
| 1044 | mutex_unlock(&adap->lock); | ||
| 1045 | |||
| 1046 | for (i = 0; i < las->num_log_addrs; i++) { | ||
| 1047 | if (las->log_addr[i] == CEC_LOG_ADDR_INVALID) | ||
| 1048 | continue; | ||
| 1049 | |||
| 1050 | /* | ||
| 1051 | * Report Features must come first according | ||
| 1052 | * to CEC 2.0 | ||
| 1053 | */ | ||
| 1054 | if (las->log_addr[i] != CEC_LOG_ADDR_UNREGISTERED) | ||
| 1055 | cec_report_features(adap, i); | ||
| 1056 | cec_report_phys_addr(adap, i); | ||
| 1057 | } | ||
| 1058 | mutex_lock(&adap->lock); | ||
| 1059 | adap->kthread_config = NULL; | ||
| 1060 | mutex_unlock(&adap->lock); | ||
| 1061 | complete(&adap->config_completion); | ||
| 1062 | return 0; | ||
| 1063 | |||
| 1064 | unconfigure: | ||
| 1065 | for (i = 0; i < las->num_log_addrs; i++) | ||
| 1066 | las->log_addr[i] = CEC_LOG_ADDR_INVALID; | ||
| 1067 | cec_adap_unconfigure(adap); | ||
| 1068 | adap->kthread_config = NULL; | ||
| 1069 | mutex_unlock(&adap->lock); | ||
| 1070 | complete(&adap->config_completion); | ||
| 1071 | return 0; | ||
| 1072 | } | ||
| 1073 | |||
| 1074 | /* | ||
| 1075 | * Called from either __cec_s_phys_addr or __cec_s_log_addrs to claim the | ||
| 1076 | * logical addresses. | ||
| 1077 | * | ||
| 1078 | * This function is called with adap->lock held. | ||
| 1079 | */ | ||
| 1080 | static void cec_claim_log_addrs(struct cec_adapter *adap, bool block) | ||
| 1081 | { | ||
| 1082 | if (WARN_ON(adap->is_configuring || adap->is_configured)) | ||
| 1083 | return; | ||
| 1084 | |||
| 1085 | init_completion(&adap->config_completion); | ||
| 1086 | |||
| 1087 | /* Ready to kick off the thread */ | ||
| 1088 | adap->is_configuring = true; | ||
| 1089 | adap->kthread_config = kthread_run(cec_config_thread_func, adap, | ||
| 1090 | "ceccfg-%s", adap->name); | ||
| 1091 | if (IS_ERR(adap->kthread_config)) { | ||
| 1092 | adap->kthread_config = NULL; | ||
| 1093 | } else if (block) { | ||
| 1094 | mutex_unlock(&adap->lock); | ||
| 1095 | wait_for_completion(&adap->config_completion); | ||
| 1096 | mutex_lock(&adap->lock); | ||
| 1097 | } | ||
| 1098 | } | ||
| 1099 | |||
| 1100 | /* Set a new physical address and send an event notifying userspace of this. | ||
| 1101 | * | ||
| 1102 | * This function is called with adap->lock held. | ||
| 1103 | */ | ||
| 1104 | void __cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block) | ||
| 1105 | { | ||
| 1106 | if (phys_addr == adap->phys_addr) | ||
| 1107 | return; | ||
| 1108 | |||
| 1109 | if (phys_addr == CEC_PHYS_ADDR_INVALID || | ||
| 1110 | adap->phys_addr != CEC_PHYS_ADDR_INVALID) { | ||
| 1111 | adap->phys_addr = CEC_PHYS_ADDR_INVALID; | ||
| 1112 | cec_post_state_event(adap); | ||
| 1113 | cec_adap_unconfigure(adap); | ||
| 1114 | /* Disabling monitor all mode should always succeed */ | ||
| 1115 | if (adap->monitor_all_cnt) | ||
| 1116 | WARN_ON(call_op(adap, adap_monitor_all_enable, false)); | ||
| 1117 | WARN_ON(adap->ops->adap_enable(adap, false)); | ||
| 1118 | if (phys_addr == CEC_PHYS_ADDR_INVALID) | ||
| 1119 | return; | ||
| 1120 | } | ||
| 1121 | |||
| 1122 | if (adap->ops->adap_enable(adap, true)) | ||
| 1123 | return; | ||
| 1124 | |||
| 1125 | if (adap->monitor_all_cnt && | ||
| 1126 | call_op(adap, adap_monitor_all_enable, true)) { | ||
| 1127 | WARN_ON(adap->ops->adap_enable(adap, false)); | ||
| 1128 | return; | ||
| 1129 | } | ||
| 1130 | adap->phys_addr = phys_addr; | ||
| 1131 | cec_post_state_event(adap); | ||
| 1132 | if (adap->log_addrs.num_log_addrs) | ||
| 1133 | cec_claim_log_addrs(adap, block); | ||
| 1134 | } | ||
| 1135 | |||
| 1136 | void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block) | ||
| 1137 | { | ||
| 1138 | if (IS_ERR_OR_NULL(adap)) | ||
| 1139 | return; | ||
| 1140 | |||
| 1141 | if (WARN_ON(adap->capabilities & CEC_CAP_PHYS_ADDR)) | ||
| 1142 | return; | ||
| 1143 | mutex_lock(&adap->lock); | ||
| 1144 | __cec_s_phys_addr(adap, phys_addr, block); | ||
| 1145 | mutex_unlock(&adap->lock); | ||
| 1146 | } | ||
| 1147 | EXPORT_SYMBOL_GPL(cec_s_phys_addr); | ||
| 1148 | |||
| 1149 | /* | ||
| 1150 | * Called from either the ioctl or a driver to set the logical addresses. | ||
| 1151 | * | ||
| 1152 | * This function is called with adap->lock held. | ||
| 1153 | */ | ||
| 1154 | int __cec_s_log_addrs(struct cec_adapter *adap, | ||
| 1155 | struct cec_log_addrs *log_addrs, bool block) | ||
| 1156 | { | ||
| 1157 | u16 type_mask = 0; | ||
| 1158 | int i; | ||
| 1159 | |||
| 1160 | if (!log_addrs || log_addrs->num_log_addrs == 0) { | ||
| 1161 | adap->log_addrs.num_log_addrs = 0; | ||
| 1162 | cec_adap_unconfigure(adap); | ||
| 1163 | return 0; | ||
| 1164 | } | ||
| 1165 | |||
| 1166 | /* Ensure the osd name is 0-terminated */ | ||
| 1167 | log_addrs->osd_name[sizeof(log_addrs->osd_name) - 1] = '\0'; | ||
| 1168 | |||
| 1169 | /* Sanity checks */ | ||
| 1170 | if (log_addrs->num_log_addrs > adap->available_log_addrs) { | ||
| 1171 | dprintk(1, "num_log_addrs > %d\n", adap->available_log_addrs); | ||
| 1172 | return -EINVAL; | ||
| 1173 | } | ||
| 1174 | |||
| 1175 | /* | ||
| 1176 | * Vendor ID is a 24 bit number, so check if the value is | ||
| 1177 | * within the correct range. | ||
| 1178 | */ | ||
| 1179 | if (log_addrs->vendor_id != CEC_VENDOR_ID_NONE && | ||
| 1180 | (log_addrs->vendor_id & 0xff000000) != 0) | ||
| 1181 | return -EINVAL; | ||
| 1182 | |||
| 1183 | if (log_addrs->cec_version != CEC_OP_CEC_VERSION_1_4 && | ||
| 1184 | log_addrs->cec_version != CEC_OP_CEC_VERSION_2_0) | ||
| 1185 | return -EINVAL; | ||
| 1186 | |||
| 1187 | if (log_addrs->num_log_addrs > 1) | ||
| 1188 | for (i = 0; i < log_addrs->num_log_addrs; i++) | ||
| 1189 | if (log_addrs->log_addr_type[i] == | ||
| 1190 | CEC_LOG_ADDR_TYPE_UNREGISTERED) { | ||
| 1191 | dprintk(1, "num_log_addrs > 1 can't be combined with unregistered LA\n"); | ||
| 1192 | return -EINVAL; | ||
| 1193 | } | ||
| 1194 | |||
| 1195 | if (log_addrs->cec_version < CEC_OP_CEC_VERSION_2_0) { | ||
| 1196 | memset(log_addrs->all_device_types, 0, | ||
| 1197 | sizeof(log_addrs->all_device_types)); | ||
| 1198 | memset(log_addrs->features, 0, sizeof(log_addrs->features)); | ||
| 1199 | } | ||
| 1200 | |||
| 1201 | for (i = 0; i < log_addrs->num_log_addrs; i++) { | ||
| 1202 | u8 *features = log_addrs->features[i]; | ||
| 1203 | bool op_is_dev_features = false; | ||
| 1204 | |||
| 1205 | log_addrs->log_addr[i] = CEC_LOG_ADDR_INVALID; | ||
| 1206 | if (type_mask & (1 << log_addrs->log_addr_type[i])) { | ||
| 1207 | dprintk(1, "duplicate logical address type\n"); | ||
| 1208 | return -EINVAL; | ||
| 1209 | } | ||
| 1210 | type_mask |= 1 << log_addrs->log_addr_type[i]; | ||
| 1211 | if ((type_mask & (1 << CEC_LOG_ADDR_TYPE_RECORD)) && | ||
| 1212 | (type_mask & (1 << CEC_LOG_ADDR_TYPE_PLAYBACK))) { | ||
| 1213 | /* Record already contains the playback functionality */ | ||
| 1214 | dprintk(1, "invalid record + playback combination\n"); | ||
| 1215 | return -EINVAL; | ||
| 1216 | } | ||
| 1217 | if (log_addrs->primary_device_type[i] > | ||
| 1218 | CEC_OP_PRIM_DEVTYPE_PROCESSOR) { | ||
| 1219 | dprintk(1, "unknown primary device type\n"); | ||
| 1220 | return -EINVAL; | ||
| 1221 | } | ||
| 1222 | if (log_addrs->primary_device_type[i] == 2) { | ||
| 1223 | dprintk(1, "invalid primary device type\n"); | ||
| 1224 | return -EINVAL; | ||
| 1225 | } | ||
| 1226 | if (log_addrs->log_addr_type[i] > CEC_LOG_ADDR_TYPE_UNREGISTERED) { | ||
| 1227 | dprintk(1, "unknown logical address type\n"); | ||
| 1228 | return -EINVAL; | ||
| 1229 | } | ||
| 1230 | if (log_addrs->cec_version < CEC_OP_CEC_VERSION_2_0) | ||
| 1231 | continue; | ||
| 1232 | |||
| 1233 | for (i = 0; i < ARRAY_SIZE(log_addrs->features[0]); i++) { | ||
| 1234 | if ((features[i] & 0x80) == 0) { | ||
| 1235 | if (op_is_dev_features) | ||
| 1236 | break; | ||
| 1237 | op_is_dev_features = true; | ||
| 1238 | } | ||
| 1239 | } | ||
| 1240 | if (!op_is_dev_features || | ||
| 1241 | i == ARRAY_SIZE(log_addrs->features[0])) { | ||
| 1242 | dprintk(1, "malformed features\n"); | ||
| 1243 | return -EINVAL; | ||
| 1244 | } | ||
| 1245 | } | ||
| 1246 | |||
| 1247 | if (log_addrs->cec_version >= CEC_OP_CEC_VERSION_2_0) { | ||
| 1248 | if (log_addrs->num_log_addrs > 2) { | ||
| 1249 | dprintk(1, "CEC 2.0 allows no more than 2 logical addresses\n"); | ||
| 1250 | return -EINVAL; | ||
| 1251 | } | ||
| 1252 | if (log_addrs->num_log_addrs == 2) { | ||
| 1253 | if (!(type_mask & ((1 << CEC_LOG_ADDR_TYPE_AUDIOSYSTEM) | | ||
| 1254 | (1 << CEC_LOG_ADDR_TYPE_TV)))) { | ||
| 1255 | dprintk(1, "Two LAs is only allowed for audiosystem and TV\n"); | ||
| 1256 | return -EINVAL; | ||
| 1257 | } | ||
| 1258 | if (!(type_mask & ((1 << CEC_LOG_ADDR_TYPE_PLAYBACK) | | ||
| 1259 | (1 << CEC_LOG_ADDR_TYPE_RECORD)))) { | ||
| 1260 | dprintk(1, "An audiosystem/TV can only be combined with record or playback\n"); | ||
| 1261 | return -EINVAL; | ||
| 1262 | } | ||
| 1263 | } | ||
| 1264 | } | ||
| 1265 | |||
| 1266 | log_addrs->log_addr_mask = adap->log_addrs.log_addr_mask; | ||
| 1267 | adap->log_addrs = *log_addrs; | ||
| 1268 | if (adap->phys_addr != CEC_PHYS_ADDR_INVALID) | ||
| 1269 | cec_claim_log_addrs(adap, block); | ||
| 1270 | return 0; | ||
| 1271 | } | ||
| 1272 | |||
| 1273 | int cec_s_log_addrs(struct cec_adapter *adap, | ||
| 1274 | struct cec_log_addrs *log_addrs, bool block) | ||
| 1275 | { | ||
| 1276 | int err; | ||
| 1277 | |||
| 1278 | if (WARN_ON(adap->capabilities & CEC_CAP_LOG_ADDRS)) | ||
| 1279 | return -EINVAL; | ||
| 1280 | mutex_lock(&adap->lock); | ||
| 1281 | err = __cec_s_log_addrs(adap, log_addrs, block); | ||
| 1282 | mutex_unlock(&adap->lock); | ||
| 1283 | return err; | ||
| 1284 | } | ||
| 1285 | EXPORT_SYMBOL_GPL(cec_s_log_addrs); | ||
| 1286 | |||
| 1287 | /* High-level core CEC message handling */ | ||
| 1288 | |||
| 1289 | /* Transmit the Report Features message */ | ||
| 1290 | static int cec_report_features(struct cec_adapter *adap, unsigned int la_idx) | ||
| 1291 | { | ||
| 1292 | struct cec_msg msg = { }; | ||
| 1293 | const struct cec_log_addrs *las = &adap->log_addrs; | ||
| 1294 | const u8 *features = las->features[la_idx]; | ||
| 1295 | bool op_is_dev_features = false; | ||
| 1296 | unsigned int idx; | ||
| 1297 | |||
| 1298 | /* This is 2.0 and up only */ | ||
| 1299 | if (adap->log_addrs.cec_version < CEC_OP_CEC_VERSION_2_0) | ||
| 1300 | return 0; | ||
| 1301 | |||
| 1302 | /* Report Features */ | ||
| 1303 | msg.msg[0] = (las->log_addr[la_idx] << 4) | 0x0f; | ||
| 1304 | msg.len = 4; | ||
| 1305 | msg.msg[1] = CEC_MSG_REPORT_FEATURES; | ||
| 1306 | msg.msg[2] = adap->log_addrs.cec_version; | ||
| 1307 | msg.msg[3] = las->all_device_types[la_idx]; | ||
| 1308 | |||
| 1309 | /* Write RC Profiles first, then Device Features */ | ||
| 1310 | for (idx = 0; idx < ARRAY_SIZE(las->features[0]); idx++) { | ||
| 1311 | msg.msg[msg.len++] = features[idx]; | ||
| 1312 | if ((features[idx] & CEC_OP_FEAT_EXT) == 0) { | ||
| 1313 | if (op_is_dev_features) | ||
| 1314 | break; | ||
| 1315 | op_is_dev_features = true; | ||
| 1316 | } | ||
| 1317 | } | ||
| 1318 | return cec_transmit_msg(adap, &msg, false); | ||
| 1319 | } | ||
| 1320 | |||
| 1321 | /* Transmit the Report Physical Address message */ | ||
| 1322 | static int cec_report_phys_addr(struct cec_adapter *adap, unsigned int la_idx) | ||
| 1323 | { | ||
| 1324 | const struct cec_log_addrs *las = &adap->log_addrs; | ||
| 1325 | struct cec_msg msg = { }; | ||
| 1326 | |||
| 1327 | /* Report Physical Address */ | ||
| 1328 | msg.msg[0] = (las->log_addr[la_idx] << 4) | 0x0f; | ||
| 1329 | cec_msg_report_physical_addr(&msg, adap->phys_addr, | ||
| 1330 | las->primary_device_type[la_idx]); | ||
| 1331 | dprintk(2, "config: la %d pa %x.%x.%x.%x\n", | ||
| 1332 | las->log_addr[la_idx], | ||
| 1333 | cec_phys_addr_exp(adap->phys_addr)); | ||
| 1334 | return cec_transmit_msg(adap, &msg, false); | ||
| 1335 | } | ||
| 1336 | |||
| 1337 | /* Transmit the Feature Abort message */ | ||
| 1338 | static int cec_feature_abort_reason(struct cec_adapter *adap, | ||
| 1339 | struct cec_msg *msg, u8 reason) | ||
| 1340 | { | ||
| 1341 | struct cec_msg tx_msg = { }; | ||
| 1342 | |||
| 1343 | /* | ||
| 1344 | * Don't reply with CEC_MSG_FEATURE_ABORT to a CEC_MSG_FEATURE_ABORT | ||
| 1345 | * message! | ||
| 1346 | */ | ||
| 1347 | if (msg->msg[1] == CEC_MSG_FEATURE_ABORT) | ||
| 1348 | return 0; | ||
| 1349 | cec_msg_set_reply_to(&tx_msg, msg); | ||
| 1350 | cec_msg_feature_abort(&tx_msg, msg->msg[1], reason); | ||
| 1351 | return cec_transmit_msg(adap, &tx_msg, false); | ||
| 1352 | } | ||
| 1353 | |||
| 1354 | static int cec_feature_abort(struct cec_adapter *adap, struct cec_msg *msg) | ||
| 1355 | { | ||
| 1356 | return cec_feature_abort_reason(adap, msg, | ||
| 1357 | CEC_OP_ABORT_UNRECOGNIZED_OP); | ||
| 1358 | } | ||
| 1359 | |||
| 1360 | static int cec_feature_refused(struct cec_adapter *adap, struct cec_msg *msg) | ||
| 1361 | { | ||
| 1362 | return cec_feature_abort_reason(adap, msg, | ||
| 1363 | CEC_OP_ABORT_REFUSED); | ||
| 1364 | } | ||
| 1365 | |||
| 1366 | /* | ||
| 1367 | * Called when a CEC message is received. This function will do any | ||
| 1368 | * necessary core processing. The is_reply bool is true if this message | ||
| 1369 | * is a reply to an earlier transmit. | ||
| 1370 | * | ||
| 1371 | * The message is either a broadcast message or a valid directed message. | ||
| 1372 | */ | ||
| 1373 | static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg, | ||
| 1374 | bool is_reply) | ||
| 1375 | { | ||
| 1376 | bool is_broadcast = cec_msg_is_broadcast(msg); | ||
| 1377 | u8 dest_laddr = cec_msg_destination(msg); | ||
| 1378 | u8 init_laddr = cec_msg_initiator(msg); | ||
| 1379 | u8 devtype = cec_log_addr2dev(adap, dest_laddr); | ||
| 1380 | int la_idx = cec_log_addr2idx(adap, dest_laddr); | ||
| 1381 | bool is_directed = la_idx >= 0; | ||
| 1382 | bool from_unregistered = init_laddr == 0xf; | ||
| 1383 | struct cec_msg tx_cec_msg = { }; | ||
| 1384 | |||
| 1385 | dprintk(1, "cec_receive_notify: %*ph\n", msg->len, msg->msg); | ||
| 1386 | |||
| 1387 | if (adap->ops->received) { | ||
| 1388 | /* Allow drivers to process the message first */ | ||
| 1389 | if (adap->ops->received(adap, msg) != -ENOMSG) | ||
| 1390 | return 0; | ||
| 1391 | } | ||
| 1392 | |||
| 1393 | /* | ||
| 1394 | * REPORT_PHYSICAL_ADDR, CEC_MSG_USER_CONTROL_PRESSED and | ||
| 1395 | * CEC_MSG_USER_CONTROL_RELEASED messages always have to be | ||
| 1396 | * handled by the CEC core, even if the passthrough mode is on. | ||
| 1397 | * The others are just ignored if passthrough mode is on. | ||
| 1398 | */ | ||
| 1399 | switch (msg->msg[1]) { | ||
| 1400 | case CEC_MSG_GET_CEC_VERSION: | ||
| 1401 | case CEC_MSG_GIVE_DEVICE_VENDOR_ID: | ||
| 1402 | case CEC_MSG_ABORT: | ||
| 1403 | case CEC_MSG_GIVE_DEVICE_POWER_STATUS: | ||
| 1404 | case CEC_MSG_GIVE_PHYSICAL_ADDR: | ||
| 1405 | case CEC_MSG_GIVE_OSD_NAME: | ||
| 1406 | case CEC_MSG_GIVE_FEATURES: | ||
| 1407 | /* | ||
| 1408 | * Skip processing these messages if the passthrough mode | ||
| 1409 | * is on. | ||
| 1410 | */ | ||
| 1411 | if (adap->passthrough) | ||
| 1412 | goto skip_processing; | ||
| 1413 | /* Ignore if addressing is wrong */ | ||
| 1414 | if (is_broadcast || from_unregistered) | ||
| 1415 | return 0; | ||
| 1416 | break; | ||
| 1417 | |||
| 1418 | case CEC_MSG_USER_CONTROL_PRESSED: | ||
| 1419 | case CEC_MSG_USER_CONTROL_RELEASED: | ||
| 1420 | /* Wrong addressing mode: don't process */ | ||
| 1421 | if (is_broadcast || from_unregistered) | ||
| 1422 | goto skip_processing; | ||
| 1423 | break; | ||
| 1424 | |||
| 1425 | case CEC_MSG_REPORT_PHYSICAL_ADDR: | ||
| 1426 | /* | ||
| 1427 | * This message is always processed, regardless of the | ||
| 1428 | * passthrough setting. | ||
| 1429 | * | ||
| 1430 | * Exception: don't process if wrong addressing mode. | ||
| 1431 | */ | ||
| 1432 | if (!is_broadcast) | ||
| 1433 | goto skip_processing; | ||
| 1434 | break; | ||
| 1435 | |||
| 1436 | default: | ||
| 1437 | break; | ||
| 1438 | } | ||
| 1439 | |||
| 1440 | cec_msg_set_reply_to(&tx_cec_msg, msg); | ||
| 1441 | |||
| 1442 | switch (msg->msg[1]) { | ||
| 1443 | /* The following messages are processed but still passed through */ | ||
| 1444 | case CEC_MSG_REPORT_PHYSICAL_ADDR: | ||
| 1445 | adap->phys_addrs[init_laddr] = | ||
| 1446 | (msg->msg[2] << 8) | msg->msg[3]; | ||
| 1447 | dprintk(1, "Reported physical address %04x for logical address %d\n", | ||
| 1448 | adap->phys_addrs[init_laddr], init_laddr); | ||
| 1449 | break; | ||
| 1450 | |||
| 1451 | case CEC_MSG_USER_CONTROL_PRESSED: | ||
| 1452 | if (!(adap->capabilities & CEC_CAP_RC)) | ||
| 1453 | break; | ||
| 1454 | |||
| 1455 | #if IS_ENABLED(CONFIG_RC_CORE) | ||
| 1456 | switch (msg->msg[2]) { | ||
| 1457 | /* | ||
| 1458 | * Play function, this message can have variable length | ||
| 1459 | * depending on the specific play function that is used. | ||
| 1460 | */ | ||
| 1461 | case 0x60: | ||
| 1462 | if (msg->len == 2) | ||
| 1463 | rc_keydown(adap->rc, RC_TYPE_CEC, | ||
| 1464 | msg->msg[2], 0); | ||
| 1465 | else | ||
| 1466 | rc_keydown(adap->rc, RC_TYPE_CEC, | ||
| 1467 | msg->msg[2] << 8 | msg->msg[3], 0); | ||
| 1468 | break; | ||
| 1469 | /* | ||
| 1470 | * Other function messages that are not handled. | ||
| 1471 | * Currently the RC framework does not allow to supply an | ||
| 1472 | * additional parameter to a keypress. These "keys" contain | ||
| 1473 | * other information such as channel number, an input number | ||
| 1474 | * etc. | ||
| 1475 | * For the time being these messages are not processed by the | ||
| 1476 | * framework and are simply forwarded to the user space. | ||
| 1477 | */ | ||
| 1478 | case 0x56: case 0x57: | ||
| 1479 | case 0x67: case 0x68: case 0x69: case 0x6a: | ||
| 1480 | break; | ||
| 1481 | default: | ||
| 1482 | rc_keydown(adap->rc, RC_TYPE_CEC, msg->msg[2], 0); | ||
| 1483 | break; | ||
| 1484 | } | ||
| 1485 | #endif | ||
| 1486 | break; | ||
| 1487 | |||
| 1488 | case CEC_MSG_USER_CONTROL_RELEASED: | ||
| 1489 | if (!(adap->capabilities & CEC_CAP_RC)) | ||
| 1490 | break; | ||
| 1491 | #if IS_ENABLED(CONFIG_RC_CORE) | ||
| 1492 | rc_keyup(adap->rc); | ||
| 1493 | #endif | ||
| 1494 | break; | ||
| 1495 | |||
| 1496 | /* | ||
| 1497 | * The remaining messages are only processed if the passthrough mode | ||
| 1498 | * is off. | ||
| 1499 | */ | ||
| 1500 | case CEC_MSG_GET_CEC_VERSION: | ||
| 1501 | cec_msg_cec_version(&tx_cec_msg, adap->log_addrs.cec_version); | ||
| 1502 | return cec_transmit_msg(adap, &tx_cec_msg, false); | ||
| 1503 | |||
| 1504 | case CEC_MSG_GIVE_PHYSICAL_ADDR: | ||
| 1505 | /* Do nothing for CEC switches using addr 15 */ | ||
| 1506 | if (devtype == CEC_OP_PRIM_DEVTYPE_SWITCH && dest_laddr == 15) | ||
| 1507 | return 0; | ||
| 1508 | cec_msg_report_physical_addr(&tx_cec_msg, adap->phys_addr, devtype); | ||
| 1509 | return cec_transmit_msg(adap, &tx_cec_msg, false); | ||
| 1510 | |||
| 1511 | case CEC_MSG_GIVE_DEVICE_VENDOR_ID: | ||
| 1512 | if (adap->log_addrs.vendor_id == CEC_VENDOR_ID_NONE) | ||
| 1513 | return cec_feature_abort(adap, msg); | ||
| 1514 | cec_msg_device_vendor_id(&tx_cec_msg, adap->log_addrs.vendor_id); | ||
| 1515 | return cec_transmit_msg(adap, &tx_cec_msg, false); | ||
| 1516 | |||
| 1517 | case CEC_MSG_ABORT: | ||
| 1518 | /* Do nothing for CEC switches */ | ||
| 1519 | if (devtype == CEC_OP_PRIM_DEVTYPE_SWITCH) | ||
| 1520 | return 0; | ||
| 1521 | return cec_feature_refused(adap, msg); | ||
| 1522 | |||
| 1523 | case CEC_MSG_GIVE_OSD_NAME: { | ||
| 1524 | if (adap->log_addrs.osd_name[0] == 0) | ||
| 1525 | return cec_feature_abort(adap, msg); | ||
| 1526 | cec_msg_set_osd_name(&tx_cec_msg, adap->log_addrs.osd_name); | ||
| 1527 | return cec_transmit_msg(adap, &tx_cec_msg, false); | ||
| 1528 | } | ||
| 1529 | |||
| 1530 | case CEC_MSG_GIVE_FEATURES: | ||
| 1531 | if (adap->log_addrs.cec_version >= CEC_OP_CEC_VERSION_2_0) | ||
| 1532 | return cec_report_features(adap, la_idx); | ||
| 1533 | return 0; | ||
| 1534 | |||
| 1535 | default: | ||
| 1536 | /* | ||
| 1537 | * Unprocessed messages are aborted if userspace isn't doing | ||
| 1538 | * any processing either. | ||
| 1539 | */ | ||
| 1540 | if (is_directed && !is_reply && !adap->follower_cnt && | ||
| 1541 | !adap->cec_follower && msg->msg[1] != CEC_MSG_FEATURE_ABORT) | ||
| 1542 | return cec_feature_abort(adap, msg); | ||
| 1543 | break; | ||
| 1544 | } | ||
| 1545 | |||
| 1546 | skip_processing: | ||
| 1547 | /* If this was a reply, then we're done */ | ||
| 1548 | if (is_reply) | ||
| 1549 | return 0; | ||
| 1550 | |||
| 1551 | /* | ||
| 1552 | * Send to the exclusive follower if there is one, otherwise send | ||
| 1553 | * to all followers. | ||
| 1554 | */ | ||
| 1555 | if (adap->cec_follower) | ||
| 1556 | cec_queue_msg_fh(adap->cec_follower, msg); | ||
| 1557 | else | ||
| 1558 | cec_queue_msg_followers(adap, msg); | ||
| 1559 | return 0; | ||
| 1560 | } | ||
| 1561 | |||
| 1562 | /* | ||
| 1563 | * Helper functions to keep track of the 'monitor all' use count. | ||
| 1564 | * | ||
| 1565 | * These functions are called with adap->lock held. | ||
| 1566 | */ | ||
| 1567 | int cec_monitor_all_cnt_inc(struct cec_adapter *adap) | ||
| 1568 | { | ||
| 1569 | int ret = 0; | ||
| 1570 | |||
| 1571 | if (adap->monitor_all_cnt == 0) | ||
| 1572 | ret = call_op(adap, adap_monitor_all_enable, 1); | ||
| 1573 | if (ret == 0) | ||
| 1574 | adap->monitor_all_cnt++; | ||
| 1575 | return ret; | ||
| 1576 | } | ||
| 1577 | |||
| 1578 | void cec_monitor_all_cnt_dec(struct cec_adapter *adap) | ||
| 1579 | { | ||
| 1580 | adap->monitor_all_cnt--; | ||
| 1581 | if (adap->monitor_all_cnt == 0) | ||
| 1582 | WARN_ON(call_op(adap, adap_monitor_all_enable, 0)); | ||
| 1583 | } | ||
| 1584 | |||
| 1585 | #ifdef CONFIG_MEDIA_CEC_DEBUG | ||
| 1586 | /* | ||
| 1587 | * Log the current state of the CEC adapter. | ||
| 1588 | * Very useful for debugging. | ||
| 1589 | */ | ||
| 1590 | int cec_adap_status(struct seq_file *file, void *priv) | ||
| 1591 | { | ||
| 1592 | struct cec_adapter *adap = dev_get_drvdata(file->private); | ||
| 1593 | struct cec_data *data; | ||
| 1594 | |||
| 1595 | mutex_lock(&adap->lock); | ||
| 1596 | seq_printf(file, "configured: %d\n", adap->is_configured); | ||
| 1597 | seq_printf(file, "configuring: %d\n", adap->is_configuring); | ||
| 1598 | seq_printf(file, "phys_addr: %x.%x.%x.%x\n", | ||
| 1599 | cec_phys_addr_exp(adap->phys_addr)); | ||
| 1600 | seq_printf(file, "number of LAs: %d\n", adap->log_addrs.num_log_addrs); | ||
| 1601 | seq_printf(file, "LA mask: 0x%04x\n", adap->log_addrs.log_addr_mask); | ||
| 1602 | if (adap->cec_follower) | ||
| 1603 | seq_printf(file, "has CEC follower%s\n", | ||
| 1604 | adap->passthrough ? " (in passthrough mode)" : ""); | ||
| 1605 | if (adap->cec_initiator) | ||
| 1606 | seq_puts(file, "has CEC initiator\n"); | ||
| 1607 | if (adap->monitor_all_cnt) | ||
| 1608 | seq_printf(file, "file handles in Monitor All mode: %u\n", | ||
| 1609 | adap->monitor_all_cnt); | ||
| 1610 | data = adap->transmitting; | ||
| 1611 | if (data) | ||
| 1612 | seq_printf(file, "transmitting message: %*ph (reply: %02x)\n", | ||
| 1613 | data->msg.len, data->msg.msg, data->msg.reply); | ||
| 1614 | list_for_each_entry(data, &adap->transmit_queue, list) { | ||
| 1615 | seq_printf(file, "queued tx message: %*ph (reply: %02x)\n", | ||
| 1616 | data->msg.len, data->msg.msg, data->msg.reply); | ||
| 1617 | } | ||
| 1618 | list_for_each_entry(data, &adap->wait_queue, list) { | ||
| 1619 | seq_printf(file, "message waiting for reply: %*ph (reply: %02x)\n", | ||
| 1620 | data->msg.len, data->msg.msg, data->msg.reply); | ||
| 1621 | } | ||
| 1622 | |||
| 1623 | call_void_op(adap, adap_status, file); | ||
| 1624 | mutex_unlock(&adap->lock); | ||
| 1625 | return 0; | ||
| 1626 | } | ||
| 1627 | #endif | ||
| @@ -0,0 +1,578 @@ | |||
| 1 | /* | ||
| 2 | * cec-api.c - HDMI Consumer Electronics Control framework - API | ||
| 3 | * | ||
| 4 | * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved. | ||
| 5 | * | ||
| 6 | * This program is free software; you may redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; version 2 of the License. | ||
| 9 | * | ||
| 10 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
| 11 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
| 12 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
| 13 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||
| 14 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
| 15 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
| 16 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| 17 | * SOFTWARE. | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include <linux/errno.h> | ||
| 21 | #include <linux/init.h> | ||
| 22 | #include <linux/module.h> | ||
| 23 | #include <linux/kernel.h> | ||
| 24 | #include <linux/kmod.h> | ||
| 25 | #include <linux/ktime.h> | ||
| 26 | #include <linux/slab.h> | ||
| 27 | #include <linux/mm.h> | ||
| 28 | #include <linux/string.h> | ||
| 29 | #include <linux/types.h> | ||
| 30 | #include <linux/uaccess.h> | ||
| 31 | #include <linux/version.h> | ||
| 32 | |||
| 33 | #include "cec-priv.h" | ||
| 34 | |||
| 35 | static inline struct cec_devnode *cec_devnode_data(struct file *filp) | ||
| 36 | { | ||
| 37 | struct cec_fh *fh = filp->private_data; | ||
| 38 | |||
| 39 | return &fh->adap->devnode; | ||
| 40 | } | ||
| 41 | |||
| 42 | /* CEC file operations */ | ||
| 43 | |||
| 44 | static unsigned int cec_poll(struct file *filp, | ||
| 45 | struct poll_table_struct *poll) | ||
| 46 | { | ||
| 47 | struct cec_devnode *devnode = cec_devnode_data(filp); | ||
| 48 | struct cec_fh *fh = filp->private_data; | ||
| 49 | struct cec_adapter *adap = fh->adap; | ||
| 50 | unsigned int res = 0; | ||
| 51 | |||
| 52 | if (!devnode->registered) | ||
| 53 | return POLLERR | POLLHUP; | ||
| 54 | mutex_lock(&adap->lock); | ||
| 55 | if (adap->is_configured) | ||
| 56 | res |= POLLOUT | POLLWRNORM; | ||
| 57 | if (fh->queued_msgs) | ||
| 58 | res |= POLLIN | POLLRDNORM; | ||
| 59 | if (fh->pending_events) | ||
| 60 | res |= POLLPRI; | ||
| 61 | poll_wait(filp, &fh->wait, poll); | ||
| 62 | mutex_unlock(&adap->lock); | ||
| 63 | return res; | ||
| 64 | } | ||
| 65 | |||
| 66 | static bool cec_is_busy(const struct cec_adapter *adap, | ||
| 67 | const struct cec_fh *fh) | ||
| 68 | { | ||
| 69 | bool valid_initiator = adap->cec_initiator && adap->cec_initiator == fh; | ||
| 70 | bool valid_follower = adap->cec_follower && adap->cec_follower == fh; | ||
| 71 | |||
| 72 | /* | ||
| 73 | * Exclusive initiators and followers can always access the CEC adapter | ||
| 74 | */ | ||
| 75 | if (valid_initiator || valid_follower) | ||
| 76 | return false; | ||
| 77 | /* | ||
| 78 | * All others can only access the CEC adapter if there is no | ||
| 79 | * exclusive initiator and they are in INITIATOR mode. | ||
| 80 | */ | ||
| 81 | return adap->cec_initiator || | ||
| 82 | fh->mode_initiator == CEC_MODE_NO_INITIATOR; | ||
| 83 | } | ||
| 84 | |||
| 85 | static long cec_adap_g_caps(struct cec_adapter *adap, | ||
| 86 | struct cec_caps __user *parg) | ||
| 87 | { | ||
| 88 | struct cec_caps caps = {}; | ||
| 89 | |||
| 90 | strlcpy(caps.driver, adap->devnode.parent->driver->name, | ||
| 91 | sizeof(caps.driver)); | ||
| 92 | strlcpy(caps.name, adap->name, sizeof(caps.name)); | ||
| 93 | caps.available_log_addrs = adap->available_log_addrs; | ||
| 94 | caps.capabilities = adap->capabilities; | ||
| 95 | caps.version = LINUX_VERSION_CODE; | ||
| 96 | if (copy_to_user(parg, &caps, sizeof(caps))) | ||
| 97 | return -EFAULT; | ||
| 98 | return 0; | ||
| 99 | } | ||
| 100 | |||
| 101 | static long cec_adap_g_phys_addr(struct cec_adapter *adap, | ||
| 102 | __u16 __user *parg) | ||
| 103 | { | ||
| 104 | u16 phys_addr; | ||
| 105 | |||
| 106 | mutex_lock(&adap->lock); | ||
| 107 | phys_addr = adap->phys_addr; | ||
| 108 | mutex_unlock(&adap->lock); | ||
| 109 | if (copy_to_user(parg, &phys_addr, sizeof(phys_addr))) | ||
| 110 | return -EFAULT; | ||
| 111 | return 0; | ||
| 112 | } | ||
| 113 | |||
| 114 | static long cec_adap_s_phys_addr(struct cec_adapter *adap, struct cec_fh *fh, | ||
| 115 | bool block, __u16 __user *parg) | ||
| 116 | { | ||
| 117 | u16 phys_addr; | ||
| 118 | long err; | ||
| 119 | |||
| 120 | if (!(adap->capabilities & CEC_CAP_PHYS_ADDR)) | ||
| 121 | return -ENOTTY; | ||
| 122 | if (copy_from_user(&phys_addr, parg, sizeof(phys_addr))) | ||
| 123 | return -EFAULT; | ||
| 124 | |||
| 125 | err = cec_phys_addr_validate(phys_addr, NULL, NULL); | ||
| 126 | if (err) | ||
| 127 | return err; | ||
| 128 | mutex_lock(&adap->lock); | ||
| 129 | if (cec_is_busy(adap, fh)) | ||
| 130 | err = -EBUSY; | ||
| 131 | else | ||
| 132 | __cec_s_phys_addr(adap, phys_addr, block); | ||
| 133 | mutex_unlock(&adap->lock); | ||
| 134 | return err; | ||
| 135 | } | ||
| 136 | |||
| 137 | static long cec_adap_g_log_addrs(struct cec_adapter *adap, | ||
| 138 | struct cec_log_addrs __user *parg) | ||
| 139 | { | ||
| 140 | struct cec_log_addrs log_addrs; | ||
| 141 | |||
| 142 | mutex_lock(&adap->lock); | ||
| 143 | log_addrs = adap->log_addrs; | ||
| 144 | if (!adap->is_configured) | ||
| 145 | memset(log_addrs.log_addr, CEC_LOG_ADDR_INVALID, | ||
| 146 | sizeof(log_addrs.log_addr)); | ||
| 147 | mutex_unlock(&adap->lock); | ||
| 148 | |||
| 149 | if (copy_to_user(parg, &log_addrs, sizeof(log_addrs))) | ||
| 150 | return -EFAULT; | ||
| 151 | return 0; | ||
| 152 | } | ||
| 153 | |||
| 154 | static long cec_adap_s_log_addrs(struct cec_adapter *adap, struct cec_fh *fh, | ||
| 155 | bool block, struct cec_log_addrs __user *parg) | ||
| 156 | { | ||
| 157 | struct cec_log_addrs log_addrs; | ||
| 158 | long err = -EBUSY; | ||
| 159 | |||
| 160 | if (!(adap->capabilities & CEC_CAP_LOG_ADDRS)) | ||
| 161 | return -ENOTTY; | ||
| 162 | if (copy_from_user(&log_addrs, parg, sizeof(log_addrs))) | ||
| 163 | return -EFAULT; | ||
| 164 | log_addrs.flags = 0; | ||
| 165 | mutex_lock(&adap->lock); | ||
| 166 | if (!adap->is_configuring && | ||
| 167 | (!log_addrs.num_log_addrs || !adap->is_configured) && | ||
| 168 | !cec_is_busy(adap, fh)) { | ||
| 169 | err = __cec_s_log_addrs(adap, &log_addrs, block); | ||
| 170 | if (!err) | ||
| 171 | log_addrs = adap->log_addrs; | ||
| 172 | } | ||
| 173 | mutex_unlock(&adap->lock); | ||
| 174 | if (err) | ||
| 175 | return err; | ||
| 176 | if (copy_to_user(parg, &log_addrs, sizeof(log_addrs))) | ||
| 177 | return -EFAULT; | ||
| 178 | return 0; | ||
| 179 | } | ||
| 180 | |||
| 181 | static long cec_transmit(struct cec_adapter *adap, struct cec_fh *fh, | ||
| 182 | bool block, struct cec_msg __user *parg) | ||
| 183 | { | ||
| 184 | struct cec_msg msg = {}; | ||
| 185 | long err = 0; | ||
| 186 | |||
| 187 | if (!(adap->capabilities & CEC_CAP_TRANSMIT)) | ||
| 188 | return -ENOTTY; | ||
| 189 | if (copy_from_user(&msg, parg, sizeof(msg))) | ||
| 190 | return -EFAULT; | ||
| 191 | mutex_lock(&adap->lock); | ||
| 192 | if (!adap->is_configured) { | ||
| 193 | err = -ENONET; | ||
| 194 | } else if (cec_is_busy(adap, fh)) { | ||
| 195 | err = -EBUSY; | ||
| 196 | } else { | ||
| 197 | if (!block || !msg.reply) | ||
| 198 | fh = NULL; | ||
| 199 | err = cec_transmit_msg_fh(adap, &msg, fh, block); | ||
| 200 | } | ||
| 201 | mutex_unlock(&adap->lock); | ||
| 202 | if (err) | ||
| 203 | return err; | ||
| 204 | if (copy_to_user(parg, &msg, sizeof(msg))) | ||
| 205 | return -EFAULT; | ||
| 206 | return 0; | ||
| 207 | } | ||
| 208 | |||
| 209 | /* Called by CEC_RECEIVE: wait for a message to arrive */ | ||
| 210 | static int cec_receive_msg(struct cec_fh *fh, struct cec_msg *msg, bool block) | ||
| 211 | { | ||
| 212 | int res; | ||
| 213 | |||
| 214 | do { | ||
| 215 | mutex_lock(&fh->lock); | ||
| 216 | /* Are there received messages queued up? */ | ||
| 217 | if (fh->queued_msgs) { | ||
| 218 | /* Yes, return the first one */ | ||
| 219 | struct cec_msg_entry *entry = | ||
| 220 | list_first_entry(&fh->msgs, | ||
| 221 | struct cec_msg_entry, list); | ||
| 222 | |||
| 223 | list_del(&entry->list); | ||
| 224 | *msg = entry->msg; | ||
| 225 | kfree(entry); | ||
| 226 | fh->queued_msgs--; | ||
| 227 | mutex_unlock(&fh->lock); | ||
| 228 | return 0; | ||
| 229 | } | ||
| 230 | |||
| 231 | /* No, return EAGAIN in non-blocking mode or wait */ | ||
| 232 | mutex_unlock(&fh->lock); | ||
| 233 | |||
| 234 | /* Return when in non-blocking mode */ | ||
| 235 | if (!block) | ||
| 236 | return -EAGAIN; | ||
| 237 | |||
| 238 | if (msg->timeout) { | ||
| 239 | /* The user specified a timeout */ | ||
| 240 | res = wait_event_interruptible_timeout(fh->wait, | ||
| 241 | fh->queued_msgs, | ||
| 242 | msecs_to_jiffies(msg->timeout)); | ||
| 243 | if (res == 0) | ||
| 244 | res = -ETIMEDOUT; | ||
| 245 | else if (res > 0) | ||
| 246 | res = 0; | ||
| 247 | } else { | ||
| 248 | /* Wait indefinitely */ | ||
| 249 | res = wait_event_interruptible(fh->wait, | ||
| 250 | fh->queued_msgs); | ||
| 251 | } | ||
| 252 | /* Exit on error, otherwise loop to get the new message */ | ||
| 253 | } while (!res); | ||
| 254 | return res; | ||
| 255 | } | ||
| 256 | |||
| 257 | static long cec_receive(struct cec_adapter *adap, struct cec_fh *fh, | ||
| 258 | bool block, struct cec_msg __user *parg) | ||
| 259 | { | ||
| 260 | struct cec_msg msg = {}; | ||
| 261 | long err = 0; | ||
| 262 | |||
| 263 | if (copy_from_user(&msg, parg, sizeof(msg))) | ||
| 264 | return -EFAULT; | ||
| 265 | mutex_lock(&adap->lock); | ||
| 266 | if (!adap->is_configured) | ||
| 267 | err = -ENONET; | ||
| 268 | mutex_unlock(&adap->lock); | ||
| 269 | if (err) | ||
| 270 | return err; | ||
| 271 | |||
| 272 | err = cec_receive_msg(fh, &msg, block); | ||
| 273 | if (err) | ||
| 274 | return err; | ||
| 275 | if (copy_to_user(parg, &msg, sizeof(msg))) | ||
| 276 | return -EFAULT; | ||
| 277 | return 0; | ||
| 278 | } | ||
| 279 | |||
| 280 | static long cec_dqevent(struct cec_adapter *adap, struct cec_fh *fh, | ||
| 281 | bool block, struct cec_event __user *parg) | ||
| 282 | { | ||
| 283 | struct cec_event *ev = NULL; | ||
| 284 | u64 ts = ~0ULL; | ||
| 285 | unsigned int i; | ||
| 286 | long err = 0; | ||
| 287 | |||
| 288 | mutex_lock(&fh->lock); | ||
| 289 | while (!fh->pending_events && block) { | ||
| 290 | mutex_unlock(&fh->lock); | ||
| 291 | err = wait_event_interruptible(fh->wait, fh->pending_events); | ||
| 292 | if (err) | ||
| 293 | return err; | ||
| 294 | mutex_lock(&fh->lock); | ||
| 295 | } | ||
| 296 | |||
| 297 | /* Find the oldest event */ | ||
| 298 | for (i = 0; i < CEC_NUM_EVENTS; i++) { | ||
| 299 | if (fh->pending_events & (1 << (i + 1)) && | ||
| 300 | fh->events[i].ts <= ts) { | ||
| 301 | ev = &fh->events[i]; | ||
| 302 | ts = ev->ts; | ||
| 303 | } | ||
| 304 | } | ||
| 305 | if (!ev) { | ||
| 306 | err = -EAGAIN; | ||
| 307 | goto unlock; | ||
| 308 | } | ||
| 309 | |||
| 310 | if (copy_to_user(parg, ev, sizeof(*ev))) { | ||
| 311 | err = -EFAULT; | ||
| 312 | goto unlock; | ||
| 313 | } | ||
| 314 | |||
| 315 | fh->pending_events &= ~(1 << ev->event); | ||
| 316 | |||
| 317 | unlock: | ||
| 318 | mutex_unlock(&fh->lock); | ||
| 319 | return err; | ||
| 320 | } | ||
| 321 | |||
| 322 | static long cec_g_mode(struct cec_adapter *adap, struct cec_fh *fh, | ||
| 323 | u32 __user *parg) | ||
| 324 | { | ||
| 325 | u32 mode = fh->mode_initiator | fh->mode_follower; | ||
| 326 | |||
| 327 | if (copy_to_user(parg, &mode, sizeof(mode))) | ||
| 328 | return -EFAULT; | ||
| 329 | return 0; | ||
| 330 | } | ||
| 331 | |||
| 332 | static long cec_s_mode(struct cec_adapter *adap, struct cec_fh *fh, | ||
| 333 | u32 __user *parg) | ||
| 334 | { | ||
| 335 | u32 mode; | ||
| 336 | u8 mode_initiator; | ||
| 337 | u8 mode_follower; | ||
| 338 | long err = 0; | ||
| 339 | |||
| 340 | if (copy_from_user(&mode, parg, sizeof(mode))) | ||
| 341 | return -EFAULT; | ||
| 342 | if (mode & ~(CEC_MODE_INITIATOR_MSK | CEC_MODE_FOLLOWER_MSK)) | ||
| 343 | return -EINVAL; | ||
| 344 | |||
| 345 | mode_initiator = mode & CEC_MODE_INITIATOR_MSK; | ||
| 346 | mode_follower = mode & CEC_MODE_FOLLOWER_MSK; | ||
| 347 | |||
| 348 | if (mode_initiator > CEC_MODE_EXCL_INITIATOR || | ||
| 349 | mode_follower > CEC_MODE_MONITOR_ALL) | ||
| 350 | return -EINVAL; | ||
| 351 | |||
| 352 | if (mode_follower == CEC_MODE_MONITOR_ALL && | ||
| 353 | !(adap->capabilities & CEC_CAP_MONITOR_ALL)) | ||
| 354 | return -EINVAL; | ||
| 355 | |||
| 356 | /* Follower modes should always be able to send CEC messages */ | ||
| 357 | if ((mode_initiator == CEC_MODE_NO_INITIATOR || | ||
| 358 | !(adap->capabilities & CEC_CAP_TRANSMIT)) && | ||
| 359 | mode_follower >= CEC_MODE_FOLLOWER && | ||
| 360 | mode_follower <= CEC_MODE_EXCL_FOLLOWER_PASSTHRU) | ||
| 361 | return -EINVAL; | ||
| 362 | |||
| 363 | /* Monitor modes require CEC_MODE_NO_INITIATOR */ | ||
| 364 | if (mode_initiator && mode_follower >= CEC_MODE_MONITOR) | ||
| 365 | return -EINVAL; | ||
| 366 | |||
| 367 | /* Monitor modes require CAP_NET_ADMIN */ | ||
| 368 | if (mode_follower >= CEC_MODE_MONITOR && !capable(CAP_NET_ADMIN)) | ||
| 369 | return -EPERM; | ||
| 370 | |||
| 371 | mutex_lock(&adap->lock); | ||
| 372 | /* | ||
| 373 | * You can't become exclusive follower if someone else already | ||
| 374 | * has that job. | ||
| 375 | */ | ||
| 376 | if ((mode_follower == CEC_MODE_EXCL_FOLLOWER || | ||
| 377 | mode_follower == CEC_MODE_EXCL_FOLLOWER_PASSTHRU) && | ||
| 378 | adap->cec_follower && adap->cec_follower != fh) | ||
| 379 | err = -EBUSY; | ||
| 380 | /* | ||
| 381 | * You can't become exclusive initiator if someone else already | ||
| 382 | * has that job. | ||
| 383 | */ | ||
| 384 | if (mode_initiator == CEC_MODE_EXCL_INITIATOR && | ||
| 385 | adap->cec_initiator && adap->cec_initiator != fh) | ||
| 386 | err = -EBUSY; | ||
| 387 | |||
| 388 | if (!err) { | ||
| 389 | bool old_mon_all = fh->mode_follower == CEC_MODE_MONITOR_ALL; | ||
| 390 | bool new_mon_all = mode_follower == CEC_MODE_MONITOR_ALL; | ||
| 391 | |||
| 392 | if (old_mon_all != new_mon_all) { | ||
| 393 | if (new_mon_all) | ||
| 394 | err = cec_monitor_all_cnt_inc(adap); | ||
| 395 | else | ||
| 396 | cec_monitor_all_cnt_dec(adap); | ||
| 397 | } | ||
| 398 | } | ||
| 399 | |||
| 400 | if (err) { | ||
| 401 | mutex_unlock(&adap->lock); | ||
| 402 | return err; | ||
| 403 | } | ||
| 404 | |||
| 405 | if (fh->mode_follower == CEC_MODE_FOLLOWER) | ||
| 406 | adap->follower_cnt--; | ||
| 407 | if (mode_follower == CEC_MODE_FOLLOWER) | ||
| 408 | adap->follower_cnt++; | ||
| 409 | if (mode_follower == CEC_MODE_EXCL_FOLLOWER || | ||
| 410 | mode_follower == CEC_MODE_EXCL_FOLLOWER_PASSTHRU) { | ||
| 411 | adap->passthrough = | ||
| 412 | mode_follower == CEC_MODE_EXCL_FOLLOWER_PASSTHRU; | ||
| 413 | adap->cec_follower = fh; | ||
| 414 | } else if (adap->cec_follower == fh) { | ||
| 415 | adap->passthrough = false; | ||
| 416 | adap->cec_follower = NULL; | ||
| 417 | } | ||
| 418 | if (mode_initiator == CEC_MODE_EXCL_INITIATOR) | ||
| 419 | adap->cec_initiator = fh; | ||
| 420 | else if (adap->cec_initiator == fh) | ||
| 421 | adap->cec_initiator = NULL; | ||
| 422 | fh->mode_initiator = mode_initiator; | ||
| 423 | fh->mode_follower = mode_follower; | ||
| 424 | mutex_unlock(&adap->lock); | ||
| 425 | return 0; | ||
| 426 | } | ||
| 427 | |||
| 428 | static long cec_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | ||
| 429 | { | ||
| 430 | struct cec_devnode *devnode = cec_devnode_data(filp); | ||
| 431 | struct cec_fh *fh = filp->private_data; | ||
| 432 | struct cec_adapter *adap = fh->adap; | ||
| 433 | bool block = !(filp->f_flags & O_NONBLOCK); | ||
| 434 | void __user *parg = (void __user *)arg; | ||
| 435 | |||
| 436 | if (!devnode->registered) | ||
| 437 | return -EIO; | ||
| 438 | |||
| 439 | switch (cmd) { | ||
| 440 | case CEC_ADAP_G_CAPS: | ||
| 441 | return cec_adap_g_caps(adap, parg); | ||
| 442 | |||
| 443 | case CEC_ADAP_G_PHYS_ADDR: | ||
| 444 | return cec_adap_g_phys_addr(adap, parg); | ||
| 445 | |||
| 446 | case CEC_ADAP_S_PHYS_ADDR: | ||
| 447 | return cec_adap_s_phys_addr(adap, fh, block, parg); | ||
| 448 | |||
| 449 | case CEC_ADAP_G_LOG_ADDRS: | ||
| 450 | return cec_adap_g_log_addrs(adap, parg); | ||
| 451 | |||
| 452 | case CEC_ADAP_S_LOG_ADDRS: | ||
| 453 | return cec_adap_s_log_addrs(adap, fh, block, parg); | ||
| 454 | |||
| 455 | case CEC_TRANSMIT: | ||
| 456 | return cec_transmit(adap, fh, block, parg); | ||
| 457 | |||
| 458 | case CEC_RECEIVE: | ||
| 459 | return cec_receive(adap, fh, block, parg); | ||
| 460 | |||
| 461 | case CEC_DQEVENT: | ||
| 462 | return cec_dqevent(adap, fh, block, parg); | ||
| 463 | |||
| 464 | case CEC_G_MODE: | ||
| 465 | return cec_g_mode(adap, fh, parg); | ||
| 466 | |||
| 467 | case CEC_S_MODE: | ||
| 468 | return cec_s_mode(adap, fh, parg); | ||
| 469 | |||
| 470 | default: | ||
| 471 | return -ENOTTY; | ||
| 472 | } | ||
| 473 | } | ||
| 474 | |||
| 475 | static int cec_open(struct inode *inode, struct file *filp) | ||
| 476 | { | ||
| 477 | struct cec_devnode *devnode = | ||
| 478 | container_of(inode->i_cdev, struct cec_devnode, cdev); | ||
| 479 | struct cec_adapter *adap = to_cec_adapter(devnode); | ||
| 480 | struct cec_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL); | ||
| 481 | /* | ||
| 482 | * Initial events that are automatically sent when the cec device is | ||
| 483 | * opened. | ||
| 484 | */ | ||
| 485 | struct cec_event ev_state = { | ||
| 486 | .event = CEC_EVENT_STATE_CHANGE, | ||
| 487 | .flags = CEC_EVENT_FL_INITIAL_STATE, | ||
| 488 | }; | ||
| 489 | int err; | ||
| 490 | |||
| 491 | if (!fh) | ||
| 492 | return -ENOMEM; | ||
| 493 | |||
| 494 | INIT_LIST_HEAD(&fh->msgs); | ||
| 495 | INIT_LIST_HEAD(&fh->xfer_list); | ||
| 496 | mutex_init(&fh->lock); | ||
| 497 | init_waitqueue_head(&fh->wait); | ||
| 498 | |||
| 499 | fh->mode_initiator = CEC_MODE_INITIATOR; | ||
| 500 | fh->adap = adap; | ||
| 501 | |||
| 502 | err = cec_get_device(devnode); | ||
| 503 | if (err) { | ||
| 504 | kfree(fh); | ||
| 505 | return err; | ||
| 506 | } | ||
| 507 | |||
| 508 | filp->private_data = fh; | ||
| 509 | |||
| 510 | mutex_lock(&devnode->fhs_lock); | ||
| 511 | /* Queue up initial state events */ | ||
| 512 | ev_state.state_change.phys_addr = adap->phys_addr; | ||
| 513 | ev_state.state_change.log_addr_mask = adap->log_addrs.log_addr_mask; | ||
| 514 | cec_queue_event_fh(fh, &ev_state, 0); | ||
| 515 | |||
| 516 | list_add(&fh->list, &devnode->fhs); | ||
| 517 | mutex_unlock(&devnode->fhs_lock); | ||
| 518 | |||
| 519 | return 0; | ||
| 520 | } | ||
| 521 | |||
| 522 | /* Override for the release function */ | ||
| 523 | static int cec_release(struct inode *inode, struct file *filp) | ||
| 524 | { | ||
| 525 | struct cec_devnode *devnode = cec_devnode_data(filp); | ||
| 526 | struct cec_adapter *adap = to_cec_adapter(devnode); | ||
| 527 | struct cec_fh *fh = filp->private_data; | ||
| 528 | |||
| 529 | mutex_lock(&adap->lock); | ||
| 530 | if (adap->cec_initiator == fh) | ||
| 531 | adap->cec_initiator = NULL; | ||
| 532 | if (adap->cec_follower == fh) { | ||
| 533 | adap->cec_follower = NULL; | ||
| 534 | adap->passthrough = false; | ||
| 535 | } | ||
| 536 | if (fh->mode_follower == CEC_MODE_FOLLOWER) | ||
| 537 | adap->follower_cnt--; | ||
| 538 | if (fh->mode_follower == CEC_MODE_MONITOR_ALL) | ||
| 539 | cec_monitor_all_cnt_dec(adap); | ||
| 540 | mutex_unlock(&adap->lock); | ||
| 541 | |||
| 542 | mutex_lock(&devnode->fhs_lock); | ||
| 543 | list_del(&fh->list); | ||
| 544 | mutex_unlock(&devnode->fhs_lock); | ||
| 545 | |||
| 546 | /* Unhook pending transmits from this filehandle. */ | ||
| 547 | mutex_lock(&adap->lock); | ||
| 548 | while (!list_empty(&fh->xfer_list)) { | ||
| 549 | struct cec_data *data = | ||
| 550 | list_first_entry(&fh->xfer_list, struct cec_data, xfer_list); | ||
| 551 | |||
| 552 | data->blocking = false; | ||
| 553 | data->fh = NULL; | ||
| 554 | list_del(&data->xfer_list); | ||
| 555 | } | ||
| 556 | mutex_unlock(&adap->lock); | ||
| 557 | while (!list_empty(&fh->msgs)) { | ||
| 558 | struct cec_msg_entry *entry = | ||
| 559 | list_first_entry(&fh->msgs, struct cec_msg_entry, list); | ||
| 560 | |||
| 561 | list_del(&entry->list); | ||
| 562 | kfree(entry); | ||
| 563 | } | ||
| 564 | kfree(fh); | ||
| 565 | |||
| 566 | cec_put_device(devnode); | ||
| 567 | filp->private_data = NULL; | ||
| 568 | return 0; | ||
| 569 | } | ||
| 570 | |||
| 571 | const struct file_operations cec_devnode_fops = { | ||
| 572 | .owner = THIS_MODULE, | ||
| 573 | .open = cec_open, | ||
| 574 | .unlocked_ioctl = cec_ioctl, | ||
| 575 | .release = cec_release, | ||
| 576 | .poll = cec_poll, | ||
| 577 | .llseek = no_llseek, | ||
| 578 | }; | ||
| @@ -0,0 +1,409 @@ | |||
| 1 | /* | ||
| 2 | * cec-core.c - HDMI Consumer Electronics Control framework - Core | ||
| 3 | * | ||
| 4 | * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved. | ||
| 5 | * | ||
| 6 | * This program is free software; you may redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; version 2 of the License. | ||
| 9 | * | ||
| 10 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
| 11 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
| 12 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
| 13 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||
| 14 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
| 15 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
| 16 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| 17 | * SOFTWARE. | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include <linux/errno.h> | ||
| 21 | #include <linux/init.h> | ||
| 22 | #include <linux/module.h> | ||
| 23 | #include <linux/kernel.h> | ||
| 24 | #include <linux/kmod.h> | ||
| 25 | #include <linux/slab.h> | ||
| 26 | #include <linux/mm.h> | ||
| 27 | #include <linux/string.h> | ||
| 28 | #include <linux/types.h> | ||
| 29 | |||
| 30 | #include "cec-priv.h" | ||
| 31 | |||
| 32 | #define CEC_NUM_DEVICES 256 | ||
| 33 | #define CEC_NAME "cec" | ||
| 34 | |||
| 35 | int cec_debug; | ||
| 36 | module_param_named(debug, cec_debug, int, 0644); | ||
| 37 | MODULE_PARM_DESC(debug, "debug level (0-2)"); | ||
| 38 | |||
| 39 | static dev_t cec_dev_t; | ||
| 40 | |||
| 41 | /* Active devices */ | ||
| 42 | static DEFINE_MUTEX(cec_devnode_lock); | ||
| 43 | static DECLARE_BITMAP(cec_devnode_nums, CEC_NUM_DEVICES); | ||
| 44 | |||
| 45 | static struct dentry *top_cec_dir; | ||
| 46 | |||
| 47 | /* dev to cec_devnode */ | ||
| 48 | #define to_cec_devnode(cd) container_of(cd, struct cec_devnode, dev) | ||
| 49 | |||
| 50 | int cec_get_device(struct cec_devnode *devnode) | ||
| 51 | { | ||
| 52 | /* | ||
| 53 | * Check if the cec device is available. This needs to be done with | ||
| 54 | * the cec_devnode_lock held to prevent an open/unregister race: | ||
| 55 | * without the lock, the device could be unregistered and freed between | ||
| 56 | * the devnode->registered check and get_device() calls, leading to | ||
| 57 | * a crash. | ||
| 58 | */ | ||
| 59 | mutex_lock(&cec_devnode_lock); | ||
| 60 | /* | ||
| 61 | * return ENXIO if the cec device has been removed | ||
| 62 | * already or if it is not registered anymore. | ||
| 63 | */ | ||
| 64 | if (!devnode->registered) { | ||
| 65 | mutex_unlock(&cec_devnode_lock); | ||
| 66 | return -ENXIO; | ||
| 67 | } | ||
| 68 | /* and increase the device refcount */ | ||
| 69 | get_device(&devnode->dev); | ||
| 70 | mutex_unlock(&cec_devnode_lock); | ||
| 71 | return 0; | ||
| 72 | } | ||
| 73 | |||
| 74 | void cec_put_device(struct cec_devnode *devnode) | ||
| 75 | { | ||
| 76 | mutex_lock(&cec_devnode_lock); | ||
| 77 | put_device(&devnode->dev); | ||
| 78 | mutex_unlock(&cec_devnode_lock); | ||
| 79 | } | ||
| 80 | |||
| 81 | /* Called when the last user of the cec device exits. */ | ||
| 82 | static void cec_devnode_release(struct device *cd) | ||
| 83 | { | ||
| 84 | struct cec_devnode *devnode = to_cec_devnode(cd); | ||
| 85 | |||
| 86 | mutex_lock(&cec_devnode_lock); | ||
| 87 | |||
| 88 | /* Mark device node number as free */ | ||
| 89 | clear_bit(devnode->minor, cec_devnode_nums); | ||
| 90 | |||
| 91 | mutex_unlock(&cec_devnode_lock); | ||
| 92 | cec_delete_adapter(to_cec_adapter(devnode)); | ||
| 93 | } | ||
| 94 | |||
| 95 | static struct bus_type cec_bus_type = { | ||
| 96 | .name = CEC_NAME, | ||
| 97 | }; | ||
| 98 | |||
| 99 | /* | ||
| 100 | * Register a cec device node | ||
| 101 | * | ||
| 102 | * The registration code assigns minor numbers and registers the new device node | ||
| 103 | * with the kernel. An error is returned if no free minor number can be found, | ||
| 104 | * or if the registration of the device node fails. | ||
| 105 | * | ||
| 106 | * Zero is returned on success. | ||
| 107 | * | ||
| 108 | * Note that if the cec_devnode_register call fails, the release() callback of | ||
| 109 | * the cec_devnode structure is *not* called, so the caller is responsible for | ||
| 110 | * freeing any data. | ||
| 111 | */ | ||
| 112 | static int __must_check cec_devnode_register(struct cec_devnode *devnode, | ||
| 113 | struct module *owner) | ||
| 114 | { | ||
| 115 | int minor; | ||
| 116 | int ret; | ||
| 117 | |||
| 118 | /* Initialization */ | ||
| 119 | INIT_LIST_HEAD(&devnode->fhs); | ||
| 120 | mutex_init(&devnode->fhs_lock); | ||
| 121 | |||
| 122 | /* Part 1: Find a free minor number */ | ||
| 123 | mutex_lock(&cec_devnode_lock); | ||
| 124 | minor = find_next_zero_bit(cec_devnode_nums, CEC_NUM_DEVICES, 0); | ||
| 125 | if (minor == CEC_NUM_DEVICES) { | ||
| 126 | mutex_unlock(&cec_devnode_lock); | ||
| 127 | pr_err("could not get a free minor\n"); | ||
| 128 | return -ENFILE; | ||
| 129 | } | ||
| 130 | |||
| 131 | set_bit(minor, cec_devnode_nums); | ||
| 132 | mutex_unlock(&cec_devnode_lock); | ||
| 133 | |||
| 134 | devnode->minor = minor; | ||
| 135 | devnode->dev.bus = &cec_bus_type; | ||
| 136 | devnode->dev.devt = MKDEV(MAJOR(cec_dev_t), minor); | ||
| 137 | devnode->dev.release = cec_devnode_release; | ||
| 138 | devnode->dev.parent = devnode->parent; | ||
| 139 | dev_set_name(&devnode->dev, "cec%d", devnode->minor); | ||
| 140 | device_initialize(&devnode->dev); | ||
| 141 | |||
| 142 | /* Part 2: Initialize and register the character device */ | ||
| 143 | cdev_init(&devnode->cdev, &cec_devnode_fops); | ||
| 144 | devnode->cdev.kobj.parent = &devnode->dev.kobj; | ||
| 145 | devnode->cdev.owner = owner; | ||
| 146 | |||
| 147 | ret = cdev_add(&devnode->cdev, devnode->dev.devt, 1); | ||
| 148 | if (ret < 0) { | ||
| 149 | pr_err("%s: cdev_add failed\n", __func__); | ||
| 150 | goto clr_bit; | ||
| 151 | } | ||
| 152 | |||
| 153 | ret = device_add(&devnode->dev); | ||
| 154 | if (ret) | ||
| 155 | goto cdev_del; | ||
| 156 | |||
| 157 | devnode->registered = true; | ||
| 158 | return 0; | ||
| 159 | |||
| 160 | cdev_del: | ||
| 161 | cdev_del(&devnode->cdev); | ||
| 162 | clr_bit: | ||
| 163 | clear_bit(devnode->minor, cec_devnode_nums); | ||
| 164 | return ret; | ||
| 165 | } | ||
| 166 | |||
| 167 | /* | ||
| 168 | * Unregister a cec device node | ||
| 169 | * | ||
| 170 | * This unregisters the passed device. Future open calls will be met with | ||
| 171 | * errors. | ||
| 172 | * | ||
| 173 | * This function can safely be called if the device node has never been | ||
| 174 | * registered or has already been unregistered. | ||
| 175 | */ | ||
| 176 | static void cec_devnode_unregister(struct cec_devnode *devnode) | ||
| 177 | { | ||
| 178 | struct cec_fh *fh; | ||
| 179 | |||
| 180 | /* Check if devnode was never registered or already unregistered */ | ||
| 181 | if (!devnode->registered || devnode->unregistered) | ||
| 182 | return; | ||
| 183 | |||
| 184 | mutex_lock(&devnode->fhs_lock); | ||
| 185 | list_for_each_entry(fh, &devnode->fhs, list) | ||
| 186 | wake_up_interruptible(&fh->wait); | ||
| 187 | mutex_unlock(&devnode->fhs_lock); | ||
| 188 | |||
| 189 | devnode->registered = false; | ||
| 190 | devnode->unregistered = true; | ||
| 191 | device_del(&devnode->dev); | ||
| 192 | cdev_del(&devnode->cdev); | ||
| 193 | put_device(&devnode->dev); | ||
| 194 | } | ||
| 195 | |||
| 196 | struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, | ||
| 197 | void *priv, const char *name, u32 caps, | ||
| 198 | u8 available_las, struct device *parent) | ||
| 199 | { | ||
| 200 | struct cec_adapter *adap; | ||
| 201 | int res; | ||
| 202 | |||
| 203 | if (WARN_ON(!parent)) | ||
| 204 | return ERR_PTR(-EINVAL); | ||
| 205 | if (WARN_ON(!caps)) | ||
| 206 | return ERR_PTR(-EINVAL); | ||
| 207 | if (WARN_ON(!ops)) | ||
| 208 | return ERR_PTR(-EINVAL); | ||
| 209 | if (WARN_ON(!available_las || available_las > CEC_MAX_LOG_ADDRS)) | ||
| 210 | return ERR_PTR(-EINVAL); | ||
| 211 | adap = kzalloc(sizeof(*adap), GFP_KERNEL); | ||
| 212 | if (!adap) | ||
| 213 | return ERR_PTR(-ENOMEM); | ||
| 214 | adap->owner = parent->driver->owner; | ||
| 215 | adap->devnode.parent = parent; | ||
| 216 | strlcpy(adap->name, name, sizeof(adap->name)); | ||
| 217 | adap->phys_addr = CEC_PHYS_ADDR_INVALID; | ||
| 218 | adap->log_addrs.cec_version = CEC_OP_CEC_VERSION_2_0; | ||
| 219 | adap->log_addrs.vendor_id = CEC_VENDOR_ID_NONE; | ||
| 220 | adap->capabilities = caps; | ||
| 221 | adap->available_log_addrs = available_las; | ||
| 222 | adap->sequence = 0; | ||
| 223 | adap->ops = ops; | ||
| 224 | adap->priv = priv; | ||
| 225 | memset(adap->phys_addrs, 0xff, sizeof(adap->phys_addrs)); | ||
| 226 | mutex_init(&adap->lock); | ||
| 227 | INIT_LIST_HEAD(&adap->transmit_queue); | ||
| 228 | INIT_LIST_HEAD(&adap->wait_queue); | ||
| 229 | init_waitqueue_head(&adap->kthread_waitq); | ||
| 230 | |||
| 231 | adap->kthread = kthread_run(cec_thread_func, adap, "cec-%s", name); | ||
| 232 | if (IS_ERR(adap->kthread)) { | ||
| 233 | pr_err("cec-%s: kernel_thread() failed\n", name); | ||
| 234 | res = PTR_ERR(adap->kthread); | ||
| 235 | kfree(adap); | ||
| 236 | return ERR_PTR(res); | ||
| 237 | } | ||
| 238 | |||
| 239 | if (!(caps & CEC_CAP_RC)) | ||
| 240 | return adap; | ||
| 241 | |||
| 242 | #if IS_ENABLED(CONFIG_RC_CORE) | ||
| 243 | /* Prepare the RC input device */ | ||
| 244 | adap->rc = rc_allocate_device(); | ||
| 245 | if (!adap->rc) { | ||
| 246 | pr_err("cec-%s: failed to allocate memory for rc_dev\n", | ||
| 247 | name); | ||
| 248 | kthread_stop(adap->kthread); | ||
| 249 | kfree(adap); | ||
| 250 | return ERR_PTR(-ENOMEM); | ||
| 251 | } | ||
| 252 | |||
| 253 | snprintf(adap->input_name, sizeof(adap->input_name), | ||
| 254 | "RC for %s", name); | ||
| 255 | snprintf(adap->input_phys, sizeof(adap->input_phys), | ||
| 256 | "%s/input0", name); | ||
| 257 | |||
| 258 | adap->rc->input_name = adap->input_name; | ||
| 259 | adap->rc->input_phys = adap->input_phys; | ||
| 260 | adap->rc->input_id.bustype = BUS_CEC; | ||
| 261 | adap->rc->input_id.vendor = 0; | ||
| 262 | adap->rc->input_id.product = 0; | ||
| 263 | adap->rc->input_id.version = 1; | ||
| 264 | adap->rc->dev.parent = parent; | ||
| 265 | adap->rc->driver_type = RC_DRIVER_SCANCODE; | ||
| 266 | adap->rc->driver_name = CEC_NAME; | ||
| 267 | adap->rc->allowed_protocols = RC_BIT_CEC; | ||
| 268 | adap->rc->priv = adap; | ||
| 269 | adap->rc->map_name = RC_MAP_CEC; | ||
| 270 | adap->rc->timeout = MS_TO_NS(100); | ||
| 271 | #else | ||
| 272 | adap->capabilities &= ~CEC_CAP_RC; | ||
| 273 | #endif | ||
| 274 | return adap; | ||
| 275 | } | ||
| 276 | EXPORT_SYMBOL_GPL(cec_allocate_adapter); | ||
| 277 | |||
| 278 | int cec_register_adapter(struct cec_adapter *adap) | ||
| 279 | { | ||
| 280 | int res; | ||
| 281 | |||
| 282 | if (IS_ERR_OR_NULL(adap)) | ||
| 283 | return 0; | ||
| 284 | |||
| 285 | #if IS_ENABLED(CONFIG_RC_CORE) | ||
| 286 | if (adap->capabilities & CEC_CAP_RC) { | ||
| 287 | res = rc_register_device(adap->rc); | ||
| 288 | |||
| 289 | if (res) { | ||
| 290 | pr_err("cec-%s: failed to prepare input device\n", | ||
| 291 | adap->name); | ||
| 292 | rc_free_device(adap->rc); | ||
| 293 | adap->rc = NULL; | ||
| 294 | return res; | ||
| 295 | } | ||
| 296 | } | ||
| 297 | #endif | ||
| 298 | |||
| 299 | res = cec_devnode_register(&adap->devnode, adap->owner); | ||
| 300 | if (res) { | ||
| 301 | #if IS_ENABLED(CONFIG_RC_CORE) | ||
| 302 | /* Note: rc_unregister also calls rc_free */ | ||
| 303 | rc_unregister_device(adap->rc); | ||
| 304 | adap->rc = NULL; | ||
| 305 | #endif | ||
| 306 | return res; | ||
| 307 | } | ||
| 308 | |||
| 309 | dev_set_drvdata(&adap->devnode.dev, adap); | ||
| 310 | #ifdef CONFIG_MEDIA_CEC_DEBUG | ||
| 311 | if (!top_cec_dir) | ||
| 312 | return 0; | ||
| 313 | |||
| 314 | adap->cec_dir = debugfs_create_dir(dev_name(&adap->devnode.dev), top_cec_dir); | ||
| 315 | if (IS_ERR_OR_NULL(adap->cec_dir)) { | ||
| 316 | pr_warn("cec-%s: Failed to create debugfs dir\n", adap->name); | ||
| 317 | return 0; | ||
| 318 | } | ||
| 319 | adap->status_file = debugfs_create_devm_seqfile(&adap->devnode.dev, | ||
| 320 | "status", adap->cec_dir, cec_adap_status); | ||
| 321 | if (IS_ERR_OR_NULL(adap->status_file)) { | ||
| 322 | pr_warn("cec-%s: Failed to create status file\n", adap->name); | ||
| 323 | debugfs_remove_recursive(adap->cec_dir); | ||
| 324 | adap->cec_dir = NULL; | ||
| 325 | } | ||
| 326 | #endif | ||
| 327 | return 0; | ||
| 328 | } | ||
| 329 | EXPORT_SYMBOL_GPL(cec_register_adapter); | ||
| 330 | |||
| 331 | void cec_unregister_adapter(struct cec_adapter *adap) | ||
| 332 | { | ||
| 333 | if (IS_ERR_OR_NULL(adap)) | ||
| 334 | return; | ||
| 335 | |||
| 336 | #if IS_ENABLED(CONFIG_RC_CORE) | ||
| 337 | /* Note: rc_unregister also calls rc_free */ | ||
| 338 | rc_unregister_device(adap->rc); | ||
| 339 | adap->rc = NULL; | ||
| 340 | #endif | ||
| 341 | debugfs_remove_recursive(adap->cec_dir); | ||
| 342 | cec_devnode_unregister(&adap->devnode); | ||
| 343 | } | ||
| 344 | EXPORT_SYMBOL_GPL(cec_unregister_adapter); | ||
| 345 | |||
| 346 | void cec_delete_adapter(struct cec_adapter *adap) | ||
| 347 | { | ||
| 348 | if (IS_ERR_OR_NULL(adap)) | ||
| 349 | return; | ||
| 350 | mutex_lock(&adap->lock); | ||
| 351 | __cec_s_phys_addr(adap, CEC_PHYS_ADDR_INVALID, false); | ||
| 352 | mutex_unlock(&adap->lock); | ||
| 353 | kthread_stop(adap->kthread); | ||
| 354 | if (adap->kthread_config) | ||
| 355 | kthread_stop(adap->kthread_config); | ||
| 356 | #if IS_ENABLED(CONFIG_RC_CORE) | ||
| 357 | if (adap->rc) | ||
| 358 | rc_free_device(adap->rc); | ||
| 359 | #endif | ||
| 360 | kfree(adap); | ||
| 361 | } | ||
| 362 | EXPORT_SYMBOL_GPL(cec_delete_adapter); | ||
| 363 | |||
| 364 | /* | ||
| 365 | * Initialise cec for linux | ||
| 366 | */ | ||
| 367 | static int __init cec_devnode_init(void) | ||
| 368 | { | ||
| 369 | int ret; | ||
| 370 | |||
| 371 | pr_info("Linux cec interface: v0.10\n"); | ||
| 372 | ret = alloc_chrdev_region(&cec_dev_t, 0, CEC_NUM_DEVICES, | ||
| 373 | CEC_NAME); | ||
| 374 | if (ret < 0) { | ||
| 375 | pr_warn("cec: unable to allocate major\n"); | ||
| 376 | return ret; | ||
| 377 | } | ||
| 378 | |||
| 379 | #ifdef CONFIG_MEDIA_CEC_DEBUG | ||
| 380 | top_cec_dir = debugfs_create_dir("cec", NULL); | ||
| 381 | if (IS_ERR_OR_NULL(top_cec_dir)) { | ||
| 382 | pr_warn("cec: Failed to create debugfs cec dir\n"); | ||
| 383 | top_cec_dir = NULL; | ||
| 384 | } | ||
| 385 | #endif | ||
| 386 | |||
| 387 | ret = bus_register(&cec_bus_type); | ||
| 388 | if (ret < 0) { | ||
| 389 | unregister_chrdev_region(cec_dev_t, CEC_NUM_DEVICES); | ||
| 390 | pr_warn("cec: bus_register failed\n"); | ||
| 391 | return -EIO; | ||
| 392 | } | ||
| 393 | |||
| 394 | return 0; | ||
| 395 | } | ||
| 396 | |||
| 397 | static void __exit cec_devnode_exit(void) | ||
| 398 | { | ||
| 399 | debugfs_remove_recursive(top_cec_dir); | ||
| 400 | bus_unregister(&cec_bus_type); | ||
| 401 | unregister_chrdev_region(cec_dev_t, CEC_NUM_DEVICES); | ||
| 402 | } | ||
| 403 | |||
| 404 | subsys_initcall(cec_devnode_init); | ||
| 405 | module_exit(cec_devnode_exit) | ||
| 406 | |||
| 407 | MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>"); | ||
| 408 | MODULE_DESCRIPTION("Device node registration for cec drivers"); | ||
| 409 | MODULE_LICENSE("GPL"); | ||
| @@ -0,0 +1,56 @@ | |||
| 1 | /* | ||
| 2 | * cec-priv.h - HDMI Consumer Electronics Control internal header | ||
| 3 | * | ||
| 4 | * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved. | ||
| 5 | * | ||
| 6 | * This program is free software; you may redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; version 2 of the License. | ||
| 9 | * | ||
| 10 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
| 11 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
| 12 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
| 13 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||
| 14 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
| 15 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
| 16 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| 17 | * SOFTWARE. | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef _CEC_PRIV_H | ||
| 21 | #define _CEC_PRIV_H | ||
| 22 | |||
| 23 | #include <linux/cec-funcs.h> | ||
| 24 | #include <media/cec.h> | ||
| 25 | |||
| 26 | #define dprintk(lvl, fmt, arg...) \ | ||
| 27 | do { \ | ||
| 28 | if (lvl <= cec_debug) \ | ||
| 29 | pr_info("cec-%s: " fmt, adap->name, ## arg); \ | ||
| 30 | } while (0) | ||
| 31 | |||
| 32 | /* devnode to cec_adapter */ | ||
| 33 | #define to_cec_adapter(node) container_of(node, struct cec_adapter, devnode) | ||
| 34 | |||
| 35 | /* cec-core.c */ | ||
| 36 | extern int cec_debug; | ||
| 37 | int cec_get_device(struct cec_devnode *devnode); | ||
| 38 | void cec_put_device(struct cec_devnode *devnode); | ||
| 39 | |||
| 40 | /* cec-adap.c */ | ||
| 41 | int cec_monitor_all_cnt_inc(struct cec_adapter *adap); | ||
| 42 | void cec_monitor_all_cnt_dec(struct cec_adapter *adap); | ||
| 43 | int cec_adap_status(struct seq_file *file, void *priv); | ||
| 44 | int cec_thread_func(void *_adap); | ||
| 45 | void __cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block); | ||
| 46 | int __cec_s_log_addrs(struct cec_adapter *adap, | ||
| 47 | struct cec_log_addrs *log_addrs, bool block); | ||
| 48 | int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg, | ||
| 49 | struct cec_fh *fh, bool block); | ||
| 50 | void cec_queue_event_fh(struct cec_fh *fh, | ||
| 51 | const struct cec_event *new_ev, u64 ts); | ||
| 52 | |||
| 53 | /* cec-api.c */ | ||
| 54 | extern const struct file_operations cec_devnode_fops; | ||
| 55 | |||
| 56 | #endif | ||
| @@ -0,0 +1,9 @@ | |||
| 1 | config VIDEO_SAMSUNG_S5P_CEC | ||
| 2 | tristate "Samsung S5P CEC driver" | ||
| 3 | depends on VIDEO_DEV && MEDIA_CEC && (PLAT_S5P || ARCH_EXYNOS || COMPILE_TEST) | ||
| 4 | ---help--- | ||
| 5 | This is a driver for Samsung S5P HDMI CEC interface. It uses the | ||
| 6 | generic CEC framework interface. | ||
| 7 | CEC bus is present in the HDMI connector and enables communication | ||
| 8 | between compatible devices. | ||
| 9 | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | obj-$(CONFIG_VIDEO_SAMSUNG_S5P_CEC) += s5p-cec.o | ||
| 2 | s5p-cec-y += s5p_cec.o exynos_hdmi_cecctrl.o | ||
| @@ -0,0 +1,3 @@ | |||
| 1 | There's nothing wrong on this driver, except that it depends on | ||
| 2 | the media staging core, that it is currently at staging. So, | ||
| 3 | this should be kept here while the core is not promoted. | ||
| @@ -0,0 +1,38 @@ | |||
| 1 | /* drivers/media/platform/s5p-cec/exynos_hdmi_cec.h | ||
| 2 | * | ||
| 3 | * Copyright (c) 2010, 2014 Samsung Electronics | ||
| 4 | * http://www.samsung.com/ | ||
| 5 | * | ||
| 6 | * Header file for interface of Samsung Exynos hdmi cec hardware | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License version 2 as | ||
| 10 | * published by the Free Software Foundation. | ||
| 11 | */ | ||
| 12 | |||
| 13 | #ifndef _EXYNOS_HDMI_CEC_H_ | ||
| 14 | #define _EXYNOS_HDMI_CEC_H_ __FILE__ | ||
| 15 | |||
| 16 | #include <linux/regmap.h> | ||
| 17 | #include <linux/miscdevice.h> | ||
| 18 | #include "s5p_cec.h" | ||
| 19 | |||
| 20 | void s5p_cec_set_divider(struct s5p_cec_dev *cec); | ||
| 21 | void s5p_cec_enable_rx(struct s5p_cec_dev *cec); | ||
| 22 | void s5p_cec_mask_rx_interrupts(struct s5p_cec_dev *cec); | ||
| 23 | void s5p_cec_unmask_rx_interrupts(struct s5p_cec_dev *cec); | ||
| 24 | void s5p_cec_mask_tx_interrupts(struct s5p_cec_dev *cec); | ||
| 25 | void s5p_cec_unmask_tx_interrupts(struct s5p_cec_dev *cec); | ||
| 26 | void s5p_cec_reset(struct s5p_cec_dev *cec); | ||
| 27 | void s5p_cec_tx_reset(struct s5p_cec_dev *cec); | ||
| 28 | void s5p_cec_rx_reset(struct s5p_cec_dev *cec); | ||
| 29 | void s5p_cec_threshold(struct s5p_cec_dev *cec); | ||
| 30 | void s5p_cec_copy_packet(struct s5p_cec_dev *cec, char *data, | ||
| 31 | size_t count, u8 retries); | ||
| 32 | void s5p_cec_set_addr(struct s5p_cec_dev *cec, u32 addr); | ||
| 33 | u32 s5p_cec_get_status(struct s5p_cec_dev *cec); | ||
| 34 | void s5p_clr_pending_tx(struct s5p_cec_dev *cec); | ||
| 35 | void s5p_clr_pending_rx(struct s5p_cec_dev *cec); | ||
| 36 | void s5p_cec_get_rx_buf(struct s5p_cec_dev *cec, u32 size, u8 *buffer); | ||
| 37 | |||
| 38 | #endif /* _EXYNOS_HDMI_CEC_H_ */ | ||
| @@ -0,0 +1,209 @@ | |||
| 1 | /* drivers/media/platform/s5p-cec/exynos_hdmi_cecctrl.c | ||
| 2 | * | ||
| 3 | * Copyright (c) 2009, 2014 Samsung Electronics | ||
| 4 | * http://www.samsung.com/ | ||
| 5 | * | ||
| 6 | * cec ftn file for Samsung TVOUT driver | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License version 2 as | ||
| 10 | * published by the Free Software Foundation. | ||
| 11 | */ | ||
| 12 | |||
| 13 | #include <linux/io.h> | ||
| 14 | #include <linux/device.h> | ||
| 15 | |||
| 16 | #include "exynos_hdmi_cec.h" | ||
| 17 | #include "regs-cec.h" | ||
| 18 | |||
| 19 | #define S5P_HDMI_FIN 24000000 | ||
| 20 | #define CEC_DIV_RATIO 320000 | ||
| 21 | |||
| 22 | #define CEC_MESSAGE_BROADCAST_MASK 0x0F | ||
| 23 | #define CEC_MESSAGE_BROADCAST 0x0F | ||
| 24 | #define CEC_FILTER_THRESHOLD 0x15 | ||
| 25 | |||
| 26 | void s5p_cec_set_divider(struct s5p_cec_dev *cec) | ||
| 27 | { | ||
| 28 | u32 div_ratio, div_val; | ||
| 29 | unsigned int reg; | ||
| 30 | |||
| 31 | div_ratio = S5P_HDMI_FIN / CEC_DIV_RATIO - 1; | ||
| 32 | |||
| 33 | if (regmap_read(cec->pmu, EXYNOS_HDMI_PHY_CONTROL, ®)) { | ||
| 34 | dev_err(cec->dev, "failed to read phy control\n"); | ||
| 35 | return; | ||
| 36 | } | ||
| 37 | |||
| 38 | reg = (reg & ~(0x3FF << 16)) | (div_ratio << 16); | ||
| 39 | |||
| 40 | if (regmap_write(cec->pmu, EXYNOS_HDMI_PHY_CONTROL, reg)) { | ||
| 41 | dev_err(cec->dev, "failed to write phy control\n"); | ||
| 42 | return; | ||
| 43 | } | ||
| 44 | |||
| 45 | div_val = CEC_DIV_RATIO * 0.00005 - 1; | ||
| 46 | |||
| 47 | writeb(0x0, cec->reg + S5P_CEC_DIVISOR_3); | ||
| 48 | writeb(0x0, cec->reg + S5P_CEC_DIVISOR_2); | ||
| 49 | writeb(0x0, cec->reg + S5P_CEC_DIVISOR_1); | ||
| 50 | writeb(div_val, cec->reg + S5P_CEC_DIVISOR_0); | ||
| 51 | } | ||
| 52 | |||
| 53 | void s5p_cec_enable_rx(struct s5p_cec_dev *cec) | ||
| 54 | { | ||
| 55 | u8 reg; | ||
| 56 | |||
| 57 | reg = readb(cec->reg + S5P_CEC_RX_CTRL); | ||
| 58 | reg |= S5P_CEC_RX_CTRL_ENABLE; | ||
| 59 | writeb(reg, cec->reg + S5P_CEC_RX_CTRL); | ||
| 60 | } | ||
| 61 | |||
| 62 | void s5p_cec_mask_rx_interrupts(struct s5p_cec_dev *cec) | ||
| 63 | { | ||
| 64 | u8 reg; | ||
| 65 | |||
| 66 | reg = readb(cec->reg + S5P_CEC_IRQ_MASK); | ||
| 67 | reg |= S5P_CEC_IRQ_RX_DONE; | ||
| 68 | reg |= S5P_CEC_IRQ_RX_ERROR; | ||
| 69 | writeb(reg, cec->reg + S5P_CEC_IRQ_MASK); | ||
| 70 | } | ||
| 71 | |||
| 72 | void s5p_cec_unmask_rx_interrupts(struct s5p_cec_dev *cec) | ||
| 73 | { | ||
| 74 | u8 reg; | ||
| 75 | |||
| 76 | reg = readb(cec->reg + S5P_CEC_IRQ_MASK); | ||
| 77 | reg &= ~S5P_CEC_IRQ_RX_DONE; | ||
| 78 | reg &= ~S5P_CEC_IRQ_RX_ERROR; | ||
| 79 | writeb(reg, cec->reg + S5P_CEC_IRQ_MASK); | ||
| 80 | } | ||
| 81 | |||
| 82 | void s5p_cec_mask_tx_interrupts(struct s5p_cec_dev *cec) | ||
| 83 | { | ||
| 84 | u8 reg; | ||
| 85 | |||
| 86 | reg = readb(cec->reg + S5P_CEC_IRQ_MASK); | ||
| 87 | reg |= S5P_CEC_IRQ_TX_DONE; | ||
| 88 | reg |= S5P_CEC_IRQ_TX_ERROR; | ||
| 89 | writeb(reg, cec->reg + S5P_CEC_IRQ_MASK); | ||
| 90 | |||
| 91 | } | ||
| 92 | |||
| 93 | void s5p_cec_unmask_tx_interrupts(struct s5p_cec_dev *cec) | ||
| 94 | { | ||
| 95 | u8 reg; | ||
| 96 | |||
| 97 | reg = readb(cec->reg + S5P_CEC_IRQ_MASK); | ||
| 98 | reg &= ~S5P_CEC_IRQ_TX_DONE; | ||
| 99 | reg &= ~S5P_CEC_IRQ_TX_ERROR; | ||
| 100 | writeb(reg, cec->reg + S5P_CEC_IRQ_MASK); | ||
| 101 | } | ||
| 102 | |||
| 103 | void s5p_cec_reset(struct s5p_cec_dev *cec) | ||
| 104 | { | ||
| 105 | u8 reg; | ||
| 106 | |||
| 107 | writeb(S5P_CEC_RX_CTRL_RESET, cec->reg + S5P_CEC_RX_CTRL); | ||
| 108 | writeb(S5P_CEC_TX_CTRL_RESET, cec->reg + S5P_CEC_TX_CTRL); | ||
| 109 | |||
| 110 | reg = readb(cec->reg + 0xc4); | ||
| 111 | reg &= ~0x1; | ||
| 112 | writeb(reg, cec->reg + 0xc4); | ||
| 113 | } | ||
| 114 | |||
| 115 | void s5p_cec_tx_reset(struct s5p_cec_dev *cec) | ||
| 116 | { | ||
| 117 | writeb(S5P_CEC_TX_CTRL_RESET, cec->reg + S5P_CEC_TX_CTRL); | ||
| 118 | } | ||
| 119 | |||
| 120 | void s5p_cec_rx_reset(struct s5p_cec_dev *cec) | ||
| 121 | { | ||
| 122 | u8 reg; | ||
| 123 | |||
| 124 | writeb(S5P_CEC_RX_CTRL_RESET, cec->reg + S5P_CEC_RX_CTRL); | ||
| 125 | |||
| 126 | reg = readb(cec->reg + 0xc4); | ||
| 127 | reg &= ~0x1; | ||
| 128 | writeb(reg, cec->reg + 0xc4); | ||
| 129 | } | ||
| 130 | |||
| 131 | void s5p_cec_threshold(struct s5p_cec_dev *cec) | ||
| 132 | { | ||
| 133 | writeb(CEC_FILTER_THRESHOLD, cec->reg + S5P_CEC_RX_FILTER_TH); | ||
| 134 | writeb(0, cec->reg + S5P_CEC_RX_FILTER_CTRL); | ||
| 135 | } | ||
| 136 | |||
| 137 | void s5p_cec_copy_packet(struct s5p_cec_dev *cec, char *data, | ||
| 138 | size_t count, u8 retries) | ||
| 139 | { | ||
| 140 | int i = 0; | ||
| 141 | u8 reg; | ||
| 142 | |||
| 143 | while (i < count) { | ||
| 144 | writeb(data[i], cec->reg + (S5P_CEC_TX_BUFF0 + (i * 4))); | ||
| 145 | i++; | ||
| 146 | } | ||
| 147 | |||
| 148 | writeb(count, cec->reg + S5P_CEC_TX_BYTES); | ||
| 149 | reg = readb(cec->reg + S5P_CEC_TX_CTRL); | ||
| 150 | reg |= S5P_CEC_TX_CTRL_START; | ||
| 151 | reg &= ~0x70; | ||
| 152 | reg |= retries << 4; | ||
| 153 | |||
| 154 | if ((data[0] & CEC_MESSAGE_BROADCAST_MASK) == CEC_MESSAGE_BROADCAST) { | ||
| 155 | dev_dbg(cec->dev, "Broadcast"); | ||
| 156 | reg |= S5P_CEC_TX_CTRL_BCAST; | ||
| 157 | } else { | ||
| 158 | dev_dbg(cec->dev, "No Broadcast"); | ||
| 159 | reg &= ~S5P_CEC_TX_CTRL_BCAST; | ||
| 160 | } | ||
| 161 | |||
| 162 | writeb(reg, cec->reg + S5P_CEC_TX_CTRL); | ||
| 163 | dev_dbg(cec->dev, "cec-tx: cec count (%zu): %*ph", count, | ||
| 164 | (int)count, data); | ||
| 165 | } | ||
| 166 | |||
| 167 | void s5p_cec_set_addr(struct s5p_cec_dev *cec, u32 addr) | ||
| 168 | { | ||
| 169 | writeb(addr & 0x0F, cec->reg + S5P_CEC_LOGIC_ADDR); | ||
| 170 | } | ||
| 171 | |||
| 172 | u32 s5p_cec_get_status(struct s5p_cec_dev *cec) | ||
| 173 | { | ||
| 174 | u32 status = 0; | ||
| 175 | |||
| 176 | status = readb(cec->reg + S5P_CEC_STATUS_0); | ||
| 177 | status |= readb(cec->reg + S5P_CEC_STATUS_1) << 8; | ||
| 178 | status |= readb(cec->reg + S5P_CEC_STATUS_2) << 16; | ||
| 179 | status |= readb(cec->reg + S5P_CEC_STATUS_3) << 24; | ||
| 180 | |||
| 181 | dev_dbg(cec->dev, "status = 0x%x!\n", status); | ||
| 182 | |||
| 183 | return status; | ||
| 184 | } | ||
| 185 | |||
| 186 | void s5p_clr_pending_tx(struct s5p_cec_dev *cec) | ||
| 187 | { | ||
| 188 | writeb(S5P_CEC_IRQ_TX_DONE | S5P_CEC_IRQ_TX_ERROR, | ||
| 189 | cec->reg + S5P_CEC_IRQ_CLEAR); | ||
| 190 | } | ||
| 191 | |||
| 192 | void s5p_clr_pending_rx(struct s5p_cec_dev *cec) | ||
| 193 | { | ||
| 194 | writeb(S5P_CEC_IRQ_RX_DONE | S5P_CEC_IRQ_RX_ERROR, | ||
| 195 | cec->reg + S5P_CEC_IRQ_CLEAR); | ||
| 196 | } | ||
| 197 | |||
| 198 | void s5p_cec_get_rx_buf(struct s5p_cec_dev *cec, u32 size, u8 *buffer) | ||
| 199 | { | ||
| 200 | u32 i = 0; | ||
| 201 | char debug[40]; | ||
| 202 | |||
| 203 | while (i < size) { | ||
| 204 | buffer[i] = readb(cec->reg + S5P_CEC_RX_BUFF0 + (i * 4)); | ||
| 205 | sprintf(debug + i * 2, "%02x ", buffer[i]); | ||
| 206 | i++; | ||
| 207 | } | ||
| 208 | dev_dbg(cec->dev, "cec-rx: cec size(%d): %s", size, debug); | ||
| 209 | } | ||
| @@ -0,0 +1,96 @@ | |||
| 1 | /* drivers/media/platform/s5p-cec/regs-cec.h | ||
| 2 | * | ||
| 3 | * Copyright (c) 2010 Samsung Electronics | ||
| 4 | * http://www.samsung.com/ | ||
| 5 | * | ||
| 6 | * register header file for Samsung TVOUT driver | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License version 2 as | ||
| 10 | * published by the Free Software Foundation. | ||
| 11 | */ | ||
| 12 | |||
| 13 | #ifndef __EXYNOS_REGS__H | ||
| 14 | #define __EXYNOS_REGS__H | ||
| 15 | |||
| 16 | /* | ||
| 17 | * Register part | ||
| 18 | */ | ||
| 19 | #define S5P_CEC_STATUS_0 (0x0000) | ||
| 20 | #define S5P_CEC_STATUS_1 (0x0004) | ||
| 21 | #define S5P_CEC_STATUS_2 (0x0008) | ||
| 22 | #define S5P_CEC_STATUS_3 (0x000C) | ||
| 23 | #define S5P_CEC_IRQ_MASK (0x0010) | ||
| 24 | #define S5P_CEC_IRQ_CLEAR (0x0014) | ||
| 25 | #define S5P_CEC_LOGIC_ADDR (0x0020) | ||
| 26 | #define S5P_CEC_DIVISOR_0 (0x0030) | ||
| 27 | #define S5P_CEC_DIVISOR_1 (0x0034) | ||
| 28 | #define S5P_CEC_DIVISOR_2 (0x0038) | ||
| 29 | #define S5P_CEC_DIVISOR_3 (0x003C) | ||
| 30 | |||
| 31 | #define S5P_CEC_TX_CTRL (0x0040) | ||
| 32 | #define S5P_CEC_TX_BYTES (0x0044) | ||
| 33 | #define S5P_CEC_TX_STAT0 (0x0060) | ||
| 34 | #define S5P_CEC_TX_STAT1 (0x0064) | ||
| 35 | #define S5P_CEC_TX_BUFF0 (0x0080) | ||
| 36 | #define S5P_CEC_TX_BUFF1 (0x0084) | ||
| 37 | #define S5P_CEC_TX_BUFF2 (0x0088) | ||
| 38 | #define S5P_CEC_TX_BUFF3 (0x008C) | ||
| 39 | #define S5P_CEC_TX_BUFF4 (0x0090) | ||
| 40 | #define S5P_CEC_TX_BUFF5 (0x0094) | ||
| 41 | #define S5P_CEC_TX_BUFF6 (0x0098) | ||
| 42 | #define S5P_CEC_TX_BUFF7 (0x009C) | ||
| 43 | #define S5P_CEC_TX_BUFF8 (0x00A0) | ||
| 44 | #define S5P_CEC_TX_BUFF9 (0x00A4) | ||
| 45 | #define S5P_CEC_TX_BUFF10 (0x00A8) | ||
| 46 | #define S5P_CEC_TX_BUFF11 (0x00AC) | ||
| 47 | #define S5P_CEC_TX_BUFF12 (0x00B0) | ||
| 48 | #define S5P_CEC_TX_BUFF13 (0x00B4) | ||
| 49 | #define S5P_CEC_TX_BUFF14 (0x00B8) | ||
| 50 | #define S5P_CEC_TX_BUFF15 (0x00BC) | ||
| 51 | |||
| 52 | #define S5P_CEC_RX_CTRL (0x00C0) | ||
| 53 | #define S5P_CEC_RX_STAT0 (0x00E0) | ||
| 54 | #define S5P_CEC_RX_STAT1 (0x00E4) | ||
| 55 | #define S5P_CEC_RX_BUFF0 (0x0100) | ||
| 56 | #define S5P_CEC_RX_BUFF1 (0x0104) | ||
| 57 | #define S5P_CEC_RX_BUFF2 (0x0108) | ||
| 58 | #define S5P_CEC_RX_BUFF3 (0x010C) | ||
| 59 | #define S5P_CEC_RX_BUFF4 (0x0110) | ||
| 60 | #define S5P_CEC_RX_BUFF5 (0x0114) | ||
| 61 | #define S5P_CEC_RX_BUFF6 (0x0118) | ||
| 62 | #define S5P_CEC_RX_BUFF7 (0x011C) | ||
| 63 | #define S5P_CEC_RX_BUFF8 (0x0120) | ||
| 64 | #define S5P_CEC_RX_BUFF9 (0x0124) | ||
| 65 | #define S5P_CEC_RX_BUFF10 (0x0128) | ||
| 66 | #define S5P_CEC_RX_BUFF11 (0x012C) | ||
| 67 | #define S5P_CEC_RX_BUFF12 (0x0130) | ||
| 68 | #define S5P_CEC_RX_BUFF13 (0x0134) | ||
| 69 | #define S5P_CEC_RX_BUFF14 (0x0138) | ||
| 70 | #define S5P_CEC_RX_BUFF15 (0x013C) | ||
| 71 | |||
| 72 | #define S5P_CEC_RX_FILTER_CTRL (0x0180) | ||
| 73 | #define S5P_CEC_RX_FILTER_TH (0x0184) | ||
| 74 | |||
| 75 | /* | ||
| 76 | * Bit definition part | ||
| 77 | */ | ||
| 78 | #define S5P_CEC_IRQ_TX_DONE (1<<0) | ||
| 79 | #define S5P_CEC_IRQ_TX_ERROR (1<<1) | ||
| 80 | #define S5P_CEC_IRQ_RX_DONE (1<<4) | ||
| 81 | #define S5P_CEC_IRQ_RX_ERROR (1<<5) | ||
| 82 | |||
| 83 | #define S5P_CEC_TX_CTRL_START (1<<0) | ||
| 84 | #define S5P_CEC_TX_CTRL_BCAST (1<<1) | ||
| 85 | #define S5P_CEC_TX_CTRL_RETRY (0x04<<4) | ||
| 86 | #define S5P_CEC_TX_CTRL_RESET (1<<7) | ||
| 87 | |||
| 88 | #define S5P_CEC_RX_CTRL_ENABLE (1<<0) | ||
| 89 | #define S5P_CEC_RX_CTRL_RESET (1<<7) | ||
| 90 | |||
| 91 | #define S5P_CEC_LOGIC_ADDR_MASK (0xF) | ||
| 92 | |||
| 93 | /* PMU Registers for PHY */ | ||
| 94 | #define EXYNOS_HDMI_PHY_CONTROL 0x700 | ||
| 95 | |||
| 96 | #endif /* __EXYNOS_REGS__H */ | ||
| @@ -0,0 +1,294 @@ | |||
| 1 | /* drivers/media/platform/s5p-cec/s5p_cec.c | ||
| 2 | * | ||
| 3 | * Samsung S5P CEC driver | ||
| 4 | * | ||
| 5 | * Copyright (c) 2014 Samsung Electronics Co., Ltd. | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License, or | ||
| 10 | * (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This driver is based on the "cec interface driver for exynos soc" by | ||
| 13 | * SangPil Moon. | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <linux/clk.h> | ||
| 17 | #include <linux/interrupt.h> | ||
| 18 | #include <linux/kernel.h> | ||
| 19 | #include <linux/mfd/syscon.h> | ||
| 20 | #include <linux/module.h> | ||
| 21 | #include <linux/of.h> | ||
| 22 | #include <linux/platform_device.h> | ||
| 23 | #include <linux/pm_runtime.h> | ||
| 24 | #include <linux/timer.h> | ||
| 25 | #include <linux/version.h> | ||
| 26 | #include <linux/workqueue.h> | ||
| 27 | #include <media/cec.h> | ||
| 28 | |||
| 29 | #include "exynos_hdmi_cec.h" | ||
| 30 | #include "regs-cec.h" | ||
| 31 | #include "s5p_cec.h" | ||
| 32 | |||
| 33 | #define CEC_NAME "s5p-cec" | ||
| 34 | |||
| 35 | static int debug; | ||
| 36 | module_param(debug, int, 0644); | ||
| 37 | MODULE_PARM_DESC(debug, "debug level (0-2)"); | ||
| 38 | |||
| 39 | static int s5p_cec_adap_enable(struct cec_adapter *adap, bool enable) | ||
| 40 | { | ||
| 41 | struct s5p_cec_dev *cec = adap->priv; | ||
| 42 | |||
| 43 | if (enable) { | ||
| 44 | pm_runtime_get_sync(cec->dev); | ||
| 45 | |||
| 46 | s5p_cec_reset(cec); | ||
| 47 | |||
| 48 | s5p_cec_set_divider(cec); | ||
| 49 | s5p_cec_threshold(cec); | ||
| 50 | |||
| 51 | s5p_cec_unmask_tx_interrupts(cec); | ||
| 52 | s5p_cec_unmask_rx_interrupts(cec); | ||
| 53 | s5p_cec_enable_rx(cec); | ||
| 54 | } else { | ||
| 55 | s5p_cec_mask_tx_interrupts(cec); | ||
| 56 | s5p_cec_mask_rx_interrupts(cec); | ||
| 57 | pm_runtime_disable(cec->dev); | ||
| 58 | } | ||
| 59 | |||
| 60 | return 0; | ||
| 61 | } | ||
| 62 | |||
| 63 | static int s5p_cec_adap_log_addr(struct cec_adapter *adap, u8 addr) | ||
| 64 | { | ||
| 65 | struct s5p_cec_dev *cec = adap->priv; | ||
| 66 | |||
| 67 | s5p_cec_set_addr(cec, addr); | ||
| 68 | return 0; | ||
| 69 | } | ||
| 70 | |||
| 71 | static int s5p_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, | ||
| 72 | u32 signal_free_time, struct cec_msg *msg) | ||
| 73 | { | ||
| 74 | struct s5p_cec_dev *cec = adap->priv; | ||
| 75 | |||
| 76 | /* | ||
| 77 | * Unclear if 0 retries are allowed by the hardware, so have 1 as | ||
| 78 | * the minimum. | ||
| 79 | */ | ||
| 80 | s5p_cec_copy_packet(cec, msg->msg, msg->len, max(1, attempts - 1)); | ||
| 81 | return 0; | ||
| 82 | } | ||
| 83 | |||
| 84 | static irqreturn_t s5p_cec_irq_handler(int irq, void *priv) | ||
| 85 | { | ||
| 86 | struct s5p_cec_dev *cec = priv; | ||
| 87 | u32 status = 0; | ||
| 88 | |||
| 89 | status = s5p_cec_get_status(cec); | ||
| 90 | |||
| 91 | dev_dbg(cec->dev, "irq received\n"); | ||
| 92 | |||
| 93 | if (status & CEC_STATUS_TX_DONE) { | ||
| 94 | if (status & CEC_STATUS_TX_ERROR) { | ||
| 95 | dev_dbg(cec->dev, "CEC_STATUS_TX_ERROR set\n"); | ||
| 96 | cec->tx = STATE_ERROR; | ||
| 97 | } else { | ||
| 98 | dev_dbg(cec->dev, "CEC_STATUS_TX_DONE\n"); | ||
| 99 | cec->tx = STATE_DONE; | ||
| 100 | } | ||
| 101 | s5p_clr_pending_tx(cec); | ||
| 102 | } | ||
| 103 | |||
| 104 | if (status & CEC_STATUS_RX_DONE) { | ||
| 105 | if (status & CEC_STATUS_RX_ERROR) { | ||
| 106 | dev_dbg(cec->dev, "CEC_STATUS_RX_ERROR set\n"); | ||
| 107 | s5p_cec_rx_reset(cec); | ||
| 108 | s5p_cec_enable_rx(cec); | ||
| 109 | } else { | ||
| 110 | dev_dbg(cec->dev, "CEC_STATUS_RX_DONE set\n"); | ||
| 111 | if (cec->rx != STATE_IDLE) | ||
| 112 | dev_dbg(cec->dev, "Buffer overrun (worker did not process previous message)\n"); | ||
| 113 | cec->rx = STATE_BUSY; | ||
| 114 | cec->msg.len = status >> 24; | ||
| 115 | cec->msg.rx_status = CEC_RX_STATUS_OK; | ||
| 116 | s5p_cec_get_rx_buf(cec, cec->msg.len, | ||
| 117 | cec->msg.msg); | ||
| 118 | cec->rx = STATE_DONE; | ||
| 119 | s5p_cec_enable_rx(cec); | ||
| 120 | } | ||
| 121 | /* Clear interrupt pending bit */ | ||
| 122 | s5p_clr_pending_rx(cec); | ||
| 123 | } | ||
| 124 | return IRQ_WAKE_THREAD; | ||
| 125 | } | ||
| 126 | |||
| 127 | static irqreturn_t s5p_cec_irq_handler_thread(int irq, void *priv) | ||
| 128 | { | ||
| 129 | struct s5p_cec_dev *cec = priv; | ||
| 130 | |||
| 131 | dev_dbg(cec->dev, "irq processing thread\n"); | ||
| 132 | switch (cec->tx) { | ||
| 133 | case STATE_DONE: | ||
| 134 | cec_transmit_done(cec->adap, CEC_TX_STATUS_OK, 0, 0, 0, 0); | ||
| 135 | cec->tx = STATE_IDLE; | ||
| 136 | break; | ||
| 137 | case STATE_ERROR: | ||
| 138 | cec_transmit_done(cec->adap, | ||
| 139 | CEC_TX_STATUS_MAX_RETRIES | CEC_TX_STATUS_ERROR, | ||
| 140 | 0, 0, 0, 1); | ||
| 141 | cec->tx = STATE_IDLE; | ||
| 142 | break; | ||
| 143 | case STATE_BUSY: | ||
| 144 | dev_err(cec->dev, "state set to busy, this should not occur here\n"); | ||
| 145 | break; | ||
| 146 | default: | ||
| 147 | break; | ||
| 148 | } | ||
| 149 | |||
| 150 | switch (cec->rx) { | ||
| 151 | case STATE_DONE: | ||
| 152 | cec_received_msg(cec->adap, &cec->msg); | ||
| 153 | cec->rx = STATE_IDLE; | ||
| 154 | break; | ||
| 155 | default: | ||
| 156 | break; | ||
| 157 | } | ||
| 158 | |||
| 159 | return IRQ_HANDLED; | ||
| 160 | } | ||
| 161 | |||
| 162 | static const struct cec_adap_ops s5p_cec_adap_ops = { | ||
| 163 | .adap_enable = s5p_cec_adap_enable, | ||
| 164 | .adap_log_addr = s5p_cec_adap_log_addr, | ||
| 165 | .adap_transmit = s5p_cec_adap_transmit, | ||
| 166 | }; | ||
| 167 | |||
| 168 | static int s5p_cec_probe(struct platform_device *pdev) | ||
| 169 | { | ||
| 170 | struct device *dev = &pdev->dev; | ||
| 171 | struct resource *res; | ||
| 172 | struct s5p_cec_dev *cec; | ||
| 173 | int ret; | ||
| 174 | |||
| 175 | cec = devm_kzalloc(&pdev->dev, sizeof(*cec), GFP_KERNEL); | ||
| 176 | if (!dev) | ||
| 177 | return -ENOMEM; | ||
| 178 | |||
| 179 | cec->dev = dev; | ||
| 180 | |||
| 181 | cec->irq = platform_get_irq(pdev, 0); | ||
| 182 | if (cec->irq < 0) | ||
| 183 | return cec->irq; | ||
| 184 | |||
| 185 | ret = devm_request_threaded_irq(dev, cec->irq, s5p_cec_irq_handler, | ||
| 186 | s5p_cec_irq_handler_thread, 0, pdev->name, cec); | ||
| 187 | if (ret) | ||
| 188 | return ret; | ||
| 189 | |||
| 190 | cec->clk = devm_clk_get(dev, "hdmicec"); | ||
| 191 | if (IS_ERR(cec->clk)) | ||
| 192 | return PTR_ERR(cec->clk); | ||
| 193 | |||
| 194 | cec->pmu = syscon_regmap_lookup_by_phandle(dev->of_node, | ||
| 195 | "samsung,syscon-phandle"); | ||
| 196 | if (IS_ERR(cec->pmu)) | ||
| 197 | return -EPROBE_DEFER; | ||
| 198 | |||
| 199 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 200 | cec->reg = devm_ioremap_resource(dev, res); | ||
| 201 | if (IS_ERR(cec->reg)) | ||
| 202 | return PTR_ERR(cec->reg); | ||
| 203 | |||
| 204 | cec->adap = cec_allocate_adapter(&s5p_cec_adap_ops, cec, | ||
| 205 | CEC_NAME, | ||
| 206 | CEC_CAP_PHYS_ADDR | CEC_CAP_LOG_ADDRS | CEC_CAP_TRANSMIT | | ||
| 207 | CEC_CAP_PASSTHROUGH | CEC_CAP_RC, | ||
| 208 | 1, &pdev->dev); | ||
| 209 | ret = PTR_ERR_OR_ZERO(cec->adap); | ||
| 210 | if (ret) | ||
| 211 | return ret; | ||
| 212 | ret = cec_register_adapter(cec->adap); | ||
| 213 | if (ret) { | ||
| 214 | cec_delete_adapter(cec->adap); | ||
| 215 | return ret; | ||
| 216 | } | ||
| 217 | |||
| 218 | platform_set_drvdata(pdev, cec); | ||
| 219 | pm_runtime_enable(dev); | ||
| 220 | |||
| 221 | dev_dbg(dev, "successfuly probed\n"); | ||
| 222 | return 0; | ||
| 223 | } | ||
| 224 | |||
| 225 | static int s5p_cec_remove(struct platform_device *pdev) | ||
| 226 | { | ||
| 227 | struct s5p_cec_dev *cec = platform_get_drvdata(pdev); | ||
| 228 | |||
| 229 | cec_unregister_adapter(cec->adap); | ||
| 230 | pm_runtime_disable(&pdev->dev); | ||
| 231 | return 0; | ||
| 232 | } | ||
| 233 | |||
| 234 | static int s5p_cec_runtime_suspend(struct device *dev) | ||
| 235 | { | ||
| 236 | struct s5p_cec_dev *cec = dev_get_drvdata(dev); | ||
| 237 | |||
| 238 | clk_disable_unprepare(cec->clk); | ||
| 239 | return 0; | ||
| 240 | } | ||
| 241 | |||
| 242 | static int s5p_cec_runtime_resume(struct device *dev) | ||
| 243 | { | ||
| 244 | struct s5p_cec_dev *cec = dev_get_drvdata(dev); | ||
| 245 | int ret; | ||
| 246 | |||
| 247 | ret = clk_prepare_enable(cec->clk); | ||
| 248 | if (ret < 0) | ||
| 249 | return ret; | ||
| 250 | return 0; | ||
| 251 | } | ||
| 252 | |||
| 253 | static int s5p_cec_suspend(struct device *dev) | ||
| 254 | { | ||
| 255 | if (pm_runtime_suspended(dev)) | ||
| 256 | return 0; | ||
| 257 | return s5p_cec_runtime_suspend(dev); | ||
| 258 | } | ||
| 259 | |||
| 260 | static int s5p_cec_resume(struct device *dev) | ||
| 261 | { | ||
| 262 | if (pm_runtime_suspended(dev)) | ||
| 263 | return 0; | ||
| 264 | return s5p_cec_runtime_resume(dev); | ||
| 265 | } | ||
| 266 | |||
| 267 | static const struct dev_pm_ops s5p_cec_pm_ops = { | ||
| 268 | SET_SYSTEM_SLEEP_PM_OPS(s5p_cec_suspend, s5p_cec_resume) | ||
| 269 | SET_RUNTIME_PM_OPS(s5p_cec_runtime_suspend, s5p_cec_runtime_resume, | ||
| 270 | NULL) | ||
| 271 | }; | ||
| 272 | |||
| 273 | static const struct of_device_id s5p_cec_match[] = { | ||
| 274 | { | ||
| 275 | .compatible = "samsung,s5p-cec", | ||
| 276 | }, | ||
| 277 | {}, | ||
| 278 | }; | ||
| 279 | |||
| 280 | static struct platform_driver s5p_cec_pdrv = { | ||
| 281 | .probe = s5p_cec_probe, | ||
| 282 | .remove = s5p_cec_remove, | ||
| 283 | .driver = { | ||
| 284 | .name = CEC_NAME, | ||
| 285 | .of_match_table = s5p_cec_match, | ||
| 286 | .pm = &s5p_cec_pm_ops, | ||
| 287 | }, | ||
| 288 | }; | ||
| 289 | |||
| 290 | module_platform_driver(s5p_cec_pdrv); | ||
| 291 | |||
| 292 | MODULE_AUTHOR("Kamil Debski <kamil@wypas.org>"); | ||
| 293 | MODULE_LICENSE("GPL"); | ||
| 294 | MODULE_DESCRIPTION("Samsung S5P CEC driver"); | ||
| @@ -0,0 +1,76 @@ | |||
| 1 | /* drivers/media/platform/s5p-cec/s5p_cec.h | ||
| 2 | * | ||
| 3 | * Samsung S5P HDMI CEC driver | ||
| 4 | * | ||
| 5 | * Copyright (c) 2014 Samsung Electronics Co., Ltd. | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License, or | ||
| 10 | * (at your option) any later version. | ||
| 11 | */ | ||
| 12 | |||
| 13 | #ifndef _S5P_CEC_H_ | ||
| 14 | #define _S5P_CEC_H_ __FILE__ | ||
| 15 | |||
| 16 | #include <linux/clk.h> | ||
| 17 | #include <linux/interrupt.h> | ||
| 18 | #include <linux/kernel.h> | ||
| 19 | #include <linux/mfd/syscon.h> | ||
| 20 | #include <linux/module.h> | ||
| 21 | #include <linux/of.h> | ||
| 22 | #include <linux/platform_device.h> | ||
| 23 | #include <linux/pm_runtime.h> | ||
| 24 | #include <linux/timer.h> | ||
| 25 | #include <linux/version.h> | ||
| 26 | #include <linux/workqueue.h> | ||
| 27 | #include <media/cec.h> | ||
| 28 | |||
| 29 | #include "exynos_hdmi_cec.h" | ||
| 30 | #include "regs-cec.h" | ||
| 31 | #include "s5p_cec.h" | ||
| 32 | |||
| 33 | #define CEC_NAME "s5p-cec" | ||
| 34 | |||
| 35 | #define CEC_STATUS_TX_RUNNING (1 << 0) | ||
| 36 | #define CEC_STATUS_TX_TRANSFERRING (1 << 1) | ||
| 37 | #define CEC_STATUS_TX_DONE (1 << 2) | ||
| 38 | #define CEC_STATUS_TX_ERROR (1 << 3) | ||
| 39 | #define CEC_STATUS_TX_BYTES (0xFF << 8) | ||
| 40 | #define CEC_STATUS_RX_RUNNING (1 << 16) | ||
| 41 | #define CEC_STATUS_RX_RECEIVING (1 << 17) | ||
| 42 | #define CEC_STATUS_RX_DONE (1 << 18) | ||
| 43 | #define CEC_STATUS_RX_ERROR (1 << 19) | ||
| 44 | #define CEC_STATUS_RX_BCAST (1 << 20) | ||
| 45 | #define CEC_STATUS_RX_BYTES (0xFF << 24) | ||
| 46 | |||
| 47 | #define CEC_WORKER_TX_DONE (1 << 0) | ||
| 48 | #define CEC_WORKER_RX_MSG (1 << 1) | ||
| 49 | |||
| 50 | /* CEC Rx buffer size */ | ||
| 51 | #define CEC_RX_BUFF_SIZE 16 | ||
| 52 | /* CEC Tx buffer size */ | ||
| 53 | #define CEC_TX_BUFF_SIZE 16 | ||
| 54 | |||
| 55 | enum cec_state { | ||
| 56 | STATE_IDLE, | ||
| 57 | STATE_BUSY, | ||
| 58 | STATE_DONE, | ||
| 59 | STATE_ERROR | ||
| 60 | }; | ||
| 61 | |||
| 62 | struct s5p_cec_dev { | ||
| 63 | struct cec_adapter *adap; | ||
| 64 | struct clk *clk; | ||
| 65 | struct device *dev; | ||
| 66 | struct mutex lock; | ||
| 67 | struct regmap *pmu; | ||
| 68 | int irq; | ||
| 69 | void __iomem *reg; | ||
| 70 | |||
| 71 | enum cec_state rx; | ||
| 72 | enum cec_state tx; | ||
| 73 | struct cec_msg msg; | ||
| 74 | }; | ||
| 75 | |||
| 76 | #endif /* _S5P_CEC_H_ */ | ||
| @@ -57,6 +57,7 @@ · 56 unmodified lines | |||
| 57 | #include <linux/i2c-dev.h> | 57 | #include <linux/i2c-dev.h> |
| 58 | #include <linux/atalk.h> | 58 | #include <linux/atalk.h> |
| 59 | #include <linux/gfp.h> | 59 | #include <linux/gfp.h> |
| 60 | #include <linux/cec.h> | ||
| 60 | 61 | ||
| 61 | #include "internal.h" | 62 | #include "internal.h" |
| 62 | 63 | ||
| @@ -1377,6 +1378,17 @@ COMPATIBLE_IOCTL(VIDEO_GET_NAVI) · 1314 unmodified lines | |||
| 1377 | COMPATIBLE_IOCTL(VIDEO_SET_ATTRIBUTES) | 1378 | COMPATIBLE_IOCTL(VIDEO_SET_ATTRIBUTES) |
| 1378 | COMPATIBLE_IOCTL(VIDEO_GET_SIZE) | 1379 | COMPATIBLE_IOCTL(VIDEO_GET_SIZE) |
| 1379 | COMPATIBLE_IOCTL(VIDEO_GET_FRAME_RATE) | 1380 | COMPATIBLE_IOCTL(VIDEO_GET_FRAME_RATE) |
| 1381 | /* cec */ | ||
| 1382 | COMPATIBLE_IOCTL(CEC_ADAP_G_CAPS) | ||
| 1383 | COMPATIBLE_IOCTL(CEC_ADAP_G_LOG_ADDRS) | ||
| 1384 | COMPATIBLE_IOCTL(CEC_ADAP_S_LOG_ADDRS) | ||
| 1385 | COMPATIBLE_IOCTL(CEC_ADAP_G_PHYS_ADDR) | ||
| 1386 | COMPATIBLE_IOCTL(CEC_ADAP_S_PHYS_ADDR) | ||
| 1387 | COMPATIBLE_IOCTL(CEC_G_MODE) | ||
| 1388 | COMPATIBLE_IOCTL(CEC_S_MODE) | ||
| 1389 | COMPATIBLE_IOCTL(CEC_TRANSMIT) | ||
| 1390 | COMPATIBLE_IOCTL(CEC_RECEIVE) | ||
| 1391 | COMPATIBLE_IOCTL(CEC_DQEVENT) | ||
| 1380 | 1392 | ||
| 1381 | /* joystick */ | 1393 | /* joystick */ |
| 1382 | COMPATIBLE_IOCTL(JSIOCGVERSION) | 1394 | COMPATIBLE_IOCTL(JSIOCGVERSION) |
| @@ -0,0 +1,1881 @@ | |||
| 1 | /* | ||
| 2 | * cec - HDMI Consumer Electronics Control message functions | ||
| 3 | * | ||
| 4 | * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved. | ||
| 5 | * | ||
| 6 | * This program is free software; you may redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; version 2 of the License. | ||
| 9 | * | ||
| 10 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
| 11 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
| 12 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
| 13 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||
| 14 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
| 15 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
| 16 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| 17 | * SOFTWARE. | ||
| 18 | */ | ||
| 19 | |||
| 20 | /* | ||
| 21 | * Note: this framework is still in staging and it is likely the API | ||
| 22 | * will change before it goes out of staging. | ||
| 23 | * | ||
| 24 | * Once it is moved out of staging this header will move to uapi. | ||
| 25 | */ | ||
| 26 | #ifndef _CEC_UAPI_FUNCS_H | ||
| 27 | #define _CEC_UAPI_FUNCS_H | ||
| 28 | |||
| 29 | #include <linux/cec.h> | ||
| 30 | |||
| 31 | /* One Touch Play Feature */ | ||
| 32 | static inline void cec_msg_active_source(struct cec_msg *msg, __u16 phys_addr) | ||
| 33 | { | ||
| 34 | msg->len = 4; | ||
| 35 | msg->msg[0] |= 0xf; /* broadcast */ | ||
| 36 | msg->msg[1] = CEC_MSG_ACTIVE_SOURCE; | ||
| 37 | msg->msg[2] = phys_addr >> 8; | ||
| 38 | msg->msg[3] = phys_addr & 0xff; | ||
| 39 | } | ||
| 40 | |||
| 41 | static inline void cec_ops_active_source(const struct cec_msg *msg, | ||
| 42 | __u16 *phys_addr) | ||
| 43 | { | ||
| 44 | *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; | ||
| 45 | } | ||
| 46 | |||
| 47 | static inline void cec_msg_image_view_on(struct cec_msg *msg) | ||
| 48 | { | ||
| 49 | msg->len = 2; | ||
| 50 | msg->msg[1] = CEC_MSG_IMAGE_VIEW_ON; | ||
| 51 | } | ||
| 52 | |||
| 53 | static inline void cec_msg_text_view_on(struct cec_msg *msg) | ||
| 54 | { | ||
| 55 | msg->len = 2; | ||
| 56 | msg->msg[1] = CEC_MSG_TEXT_VIEW_ON; | ||
| 57 | } | ||
| 58 | |||
| 59 | |||
| 60 | /* Routing Control Feature */ | ||
| 61 | static inline void cec_msg_inactive_source(struct cec_msg *msg, | ||
| 62 | __u16 phys_addr) | ||
| 63 | { | ||
| 64 | msg->len = 4; | ||
| 65 | msg->msg[1] = CEC_MSG_INACTIVE_SOURCE; | ||
| 66 | msg->msg[2] = phys_addr >> 8; | ||
| 67 | msg->msg[3] = phys_addr & 0xff; | ||
| 68 | } | ||
| 69 | |||
| 70 | static inline void cec_ops_inactive_source(const struct cec_msg *msg, | ||
| 71 | __u16 *phys_addr) | ||
| 72 | { | ||
| 73 | *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; | ||
| 74 | } | ||
| 75 | |||
| 76 | static inline void cec_msg_request_active_source(struct cec_msg *msg, | ||
| 77 | bool reply) | ||
| 78 | { | ||
| 79 | msg->len = 2; | ||
| 80 | msg->msg[0] |= 0xf; /* broadcast */ | ||
| 81 | msg->msg[1] = CEC_MSG_REQUEST_ACTIVE_SOURCE; | ||
| 82 | msg->reply = reply ? CEC_MSG_ACTIVE_SOURCE : 0; | ||
| 83 | } | ||
| 84 | |||
| 85 | static inline void cec_msg_routing_information(struct cec_msg *msg, | ||
| 86 | __u16 phys_addr) | ||
| 87 | { | ||
| 88 | msg->len = 4; | ||
| 89 | msg->msg[0] |= 0xf; /* broadcast */ | ||
| 90 | msg->msg[1] = CEC_MSG_ROUTING_INFORMATION; | ||
| 91 | msg->msg[2] = phys_addr >> 8; | ||
| 92 | msg->msg[3] = phys_addr & 0xff; | ||
| 93 | } | ||
| 94 | |||
| 95 | static inline void cec_ops_routing_information(const struct cec_msg *msg, | ||
| 96 | __u16 *phys_addr) | ||
| 97 | { | ||
| 98 | *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; | ||
| 99 | } | ||
| 100 | |||
| 101 | static inline void cec_msg_routing_change(struct cec_msg *msg, | ||
| 102 | bool reply, | ||
| 103 | __u16 orig_phys_addr, | ||
| 104 | __u16 new_phys_addr) | ||
| 105 | { | ||
| 106 | msg->len = 6; | ||
| 107 | msg->msg[0] |= 0xf; /* broadcast */ | ||
| 108 | msg->msg[1] = CEC_MSG_ROUTING_CHANGE; | ||
| 109 | msg->msg[2] = orig_phys_addr >> 8; | ||
| 110 | msg->msg[3] = orig_phys_addr & 0xff; | ||
| 111 | msg->msg[4] = new_phys_addr >> 8; | ||
| 112 | msg->msg[5] = new_phys_addr & 0xff; | ||
| 113 | msg->reply = reply ? CEC_MSG_ROUTING_INFORMATION : 0; | ||
| 114 | } | ||
| 115 | |||
| 116 | static inline void cec_ops_routing_change(const struct cec_msg *msg, | ||
| 117 | __u16 *orig_phys_addr, | ||
| 118 | __u16 *new_phys_addr) | ||
| 119 | { | ||
| 120 | *orig_phys_addr = (msg->msg[2] << 8) | msg->msg[3]; | ||
| 121 | *new_phys_addr = (msg->msg[4] << 8) | msg->msg[5]; | ||
| 122 | } | ||
| 123 | |||
| 124 | static inline void cec_msg_set_stream_path(struct cec_msg *msg, __u16 phys_addr) | ||
| 125 | { | ||
| 126 | msg->len = 4; | ||
| 127 | msg->msg[0] |= 0xf; /* broadcast */ | ||
| 128 | msg->msg[1] = CEC_MSG_SET_STREAM_PATH; | ||
| 129 | msg->msg[2] = phys_addr >> 8; | ||
| 130 | msg->msg[3] = phys_addr & 0xff; | ||
| 131 | } | ||
| 132 | |||
| 133 | static inline void cec_ops_set_stream_path(const struct cec_msg *msg, | ||
| 134 | __u16 *phys_addr) | ||
| 135 | { | ||
| 136 | *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; | ||
| 137 | } | ||
| 138 | |||
| 139 | |||
| 140 | /* Standby Feature */ | ||
| 141 | static inline void cec_msg_standby(struct cec_msg *msg) | ||
| 142 | { | ||
| 143 | msg->len = 2; | ||
| 144 | msg->msg[1] = CEC_MSG_STANDBY; | ||
| 145 | } | ||
| 146 | |||
| 147 | |||
| 148 | /* One Touch Record Feature */ | ||
| 149 | static inline void cec_msg_record_off(struct cec_msg *msg) | ||
| 150 | { | ||
| 151 | msg->len = 2; | ||
| 152 | msg->msg[1] = CEC_MSG_RECORD_OFF; | ||
| 153 | } | ||
| 154 | |||
| 155 | struct cec_op_arib_data { | ||
| 156 | __u16 transport_id; | ||
| 157 | __u16 service_id; | ||
| 158 | __u16 orig_network_id; | ||
| 159 | }; | ||
| 160 | |||
| 161 | struct cec_op_atsc_data { | ||
| 162 | __u16 transport_id; | ||
| 163 | __u16 program_number; | ||
| 164 | }; | ||
| 165 | |||
| 166 | struct cec_op_dvb_data { | ||
| 167 | __u16 transport_id; | ||
| 168 | __u16 service_id; | ||
| 169 | __u16 orig_network_id; | ||
| 170 | }; | ||
| 171 | |||
| 172 | struct cec_op_channel_data { | ||
| 173 | __u8 channel_number_fmt; | ||
| 174 | __u16 major; | ||
| 175 | __u16 minor; | ||
| 176 | }; | ||
| 177 | |||
| 178 | struct cec_op_digital_service_id { | ||
| 179 | __u8 service_id_method; | ||
| 180 | __u8 dig_bcast_system; | ||
| 181 | union { | ||
| 182 | struct cec_op_arib_data arib; | ||
| 183 | struct cec_op_atsc_data atsc; | ||
| 184 | struct cec_op_dvb_data dvb; | ||
| 185 | struct cec_op_channel_data channel; | ||
| 186 | }; | ||
| 187 | }; | ||
| 188 | |||
| 189 | struct cec_op_record_src { | ||
| 190 | __u8 type; | ||
| 191 | union { | ||
| 192 | struct cec_op_digital_service_id digital; | ||
| 193 | struct { | ||
| 194 | __u8 ana_bcast_type; | ||
| 195 | __u16 ana_freq; | ||
| 196 | __u8 bcast_system; | ||
| 197 | } analog; | ||
| 198 | struct { | ||
| 199 | __u8 plug; | ||
| 200 | } ext_plug; | ||
| 201 | struct { | ||
| 202 | __u16 phys_addr; | ||
| 203 | } ext_phys_addr; | ||
| 204 | }; | ||
| 205 | }; | ||
| 206 | |||
| 207 | static inline void cec_set_digital_service_id(__u8 *msg, | ||
| 208 | const struct cec_op_digital_service_id *digital) | ||
| 209 | { | ||
| 210 | *msg++ = (digital->service_id_method << 7) | digital->dig_bcast_system; | ||
| 211 | if (digital->service_id_method == CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL) { | ||
| 212 | *msg++ = (digital->channel.channel_number_fmt << 2) | | ||
| 213 | (digital->channel.major >> 8); | ||
| 214 | *msg++ = digital->channel.major && 0xff; | ||
| 215 | *msg++ = digital->channel.minor >> 8; | ||
| 216 | *msg++ = digital->channel.minor & 0xff; | ||
| 217 | *msg++ = 0; | ||
| 218 | *msg++ = 0; | ||
| 219 | return; | ||
| 220 | } | ||
| 221 | switch (digital->dig_bcast_system) { | ||
| 222 | case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_GEN: | ||
| 223 | case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_CABLE: | ||
| 224 | case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_SAT: | ||
| 225 | case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_T: | ||
| 226 | *msg++ = digital->atsc.transport_id >> 8; | ||
| 227 | *msg++ = digital->atsc.transport_id & 0xff; | ||
| 228 | *msg++ = digital->atsc.program_number >> 8; | ||
| 229 | *msg++ = digital->atsc.program_number & 0xff; | ||
| 230 | *msg++ = 0; | ||
| 231 | *msg++ = 0; | ||
| 232 | break; | ||
| 233 | default: | ||
| 234 | *msg++ = digital->dvb.transport_id >> 8; | ||
| 235 | *msg++ = digital->dvb.transport_id & 0xff; | ||
| 236 | *msg++ = digital->dvb.service_id >> 8; | ||
| 237 | *msg++ = digital->dvb.service_id & 0xff; | ||
| 238 | *msg++ = digital->dvb.orig_network_id >> 8; | ||
| 239 | *msg++ = digital->dvb.orig_network_id & 0xff; | ||
| 240 | break; | ||
| 241 | } | ||
| 242 | } | ||
| 243 | |||
| 244 | static inline void cec_get_digital_service_id(const __u8 *msg, | ||
| 245 | struct cec_op_digital_service_id *digital) | ||
| 246 | { | ||
| 247 | digital->service_id_method = msg[0] >> 7; | ||
| 248 | digital->dig_bcast_system = msg[0] & 0x7f; | ||
| 249 | if (digital->service_id_method == CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL) { | ||
| 250 | digital->channel.channel_number_fmt = msg[1] >> 2; | ||
| 251 | digital->channel.major = ((msg[1] & 3) << 6) | msg[2]; | ||
| 252 | digital->channel.minor = (msg[3] << 8) | msg[4]; | ||
| 253 | return; | ||
| 254 | } | ||
| 255 | digital->dvb.transport_id = (msg[1] << 8) | msg[2]; | ||
| 256 | digital->dvb.service_id = (msg[3] << 8) | msg[4]; | ||
| 257 | digital->dvb.orig_network_id = (msg[5] << 8) | msg[6]; | ||
| 258 | } | ||
| 259 | |||
| 260 | static inline void cec_msg_record_on_own(struct cec_msg *msg) | ||
| 261 | { | ||
| 262 | msg->len = 3; | ||
| 263 | msg->msg[1] = CEC_MSG_RECORD_ON; | ||
| 264 | msg->msg[2] = CEC_OP_RECORD_SRC_OWN; | ||
| 265 | } | ||
| 266 | |||
| 267 | static inline void cec_msg_record_on_digital(struct cec_msg *msg, | ||
| 268 | const struct cec_op_digital_service_id *digital) | ||
| 269 | { | ||
| 270 | msg->len = 10; | ||
| 271 | msg->msg[1] = CEC_MSG_RECORD_ON; | ||
| 272 | msg->msg[2] = CEC_OP_RECORD_SRC_DIGITAL; | ||
| 273 | cec_set_digital_service_id(msg->msg + 3, digital); | ||
| 274 | } | ||
| 275 | |||
| 276 | static inline void cec_msg_record_on_analog(struct cec_msg *msg, | ||
| 277 | __u8 ana_bcast_type, | ||
| 278 | __u16 ana_freq, | ||
| 279 | __u8 bcast_system) | ||
| 280 | { | ||
| 281 | msg->len = 7; | ||
| 282 | msg->msg[1] = CEC_MSG_RECORD_ON; | ||
| 283 | msg->msg[2] = CEC_OP_RECORD_SRC_ANALOG; | ||
| 284 | msg->msg[3] = ana_bcast_type; | ||
| 285 | msg->msg[4] = ana_freq >> 8; | ||
| 286 | msg->msg[5] = ana_freq & 0xff; | ||
| 287 | msg->msg[6] = bcast_system; | ||
| 288 | } | ||
| 289 | |||
| 290 | static inline void cec_msg_record_on_plug(struct cec_msg *msg, | ||
| 291 | __u8 plug) | ||
| 292 | { | ||
| 293 | msg->len = 4; | ||
| 294 | msg->msg[1] = CEC_MSG_RECORD_ON; | ||
| 295 | msg->msg[2] = CEC_OP_RECORD_SRC_EXT_PLUG; | ||
| 296 | msg->msg[3] = plug; | ||
| 297 | } | ||
| 298 | |||
| 299 | static inline void cec_msg_record_on_phys_addr(struct cec_msg *msg, | ||
| 300 | __u16 phys_addr) | ||
| 301 | { | ||
| 302 | msg->len = 5; | ||
| 303 | msg->msg[1] = CEC_MSG_RECORD_ON; | ||
| 304 | msg->msg[2] = CEC_OP_RECORD_SRC_EXT_PHYS_ADDR; | ||
| 305 | msg->msg[3] = phys_addr >> 8; | ||
| 306 | msg->msg[4] = phys_addr & 0xff; | ||
| 307 | } | ||
| 308 | |||
| 309 | static inline void cec_msg_record_on(struct cec_msg *msg, | ||
| 310 | const struct cec_op_record_src *rec_src) | ||
| 311 | { | ||
| 312 | switch (rec_src->type) { | ||
| 313 | case CEC_OP_RECORD_SRC_OWN: | ||
| 314 | cec_msg_record_on_own(msg); | ||
| 315 | break; | ||
| 316 | case CEC_OP_RECORD_SRC_DIGITAL: | ||
| 317 | cec_msg_record_on_digital(msg, &rec_src->digital); | ||
| 318 | break; | ||
| 319 | case CEC_OP_RECORD_SRC_ANALOG: | ||
| 320 | cec_msg_record_on_analog(msg, | ||
| 321 | rec_src->analog.ana_bcast_type, | ||
| 322 | rec_src->analog.ana_freq, | ||
| 323 | rec_src->analog.bcast_system); | ||
| 324 | break; | ||
| 325 | case CEC_OP_RECORD_SRC_EXT_PLUG: | ||
| 326 | cec_msg_record_on_plug(msg, rec_src->ext_plug.plug); | ||
| 327 | break; | ||
| 328 | case CEC_OP_RECORD_SRC_EXT_PHYS_ADDR: | ||
| 329 | cec_msg_record_on_phys_addr(msg, | ||
| 330 | rec_src->ext_phys_addr.phys_addr); | ||
| 331 | break; | ||
| 332 | } | ||
| 333 | } | ||
| 334 | |||
| 335 | static inline void cec_ops_record_on(const struct cec_msg *msg, | ||
| 336 | struct cec_op_record_src *rec_src) | ||
| 337 | { | ||
| 338 | rec_src->type = msg->msg[2]; | ||
| 339 | switch (rec_src->type) { | ||
| 340 | case CEC_OP_RECORD_SRC_OWN: | ||
| 341 | break; | ||
| 342 | case CEC_OP_RECORD_SRC_DIGITAL: | ||
| 343 | cec_get_digital_service_id(msg->msg + 3, &rec_src->digital); | ||
| 344 | break; | ||
| 345 | case CEC_OP_RECORD_SRC_ANALOG: | ||
| 346 | rec_src->analog.ana_bcast_type = msg->msg[3]; | ||
| 347 | rec_src->analog.ana_freq = | ||
| 348 | (msg->msg[4] << 8) | msg->msg[5]; | ||
| 349 | rec_src->analog.bcast_system = msg->msg[6]; | ||
| 350 | break; | ||
| 351 | case CEC_OP_RECORD_SRC_EXT_PLUG: | ||
| 352 | rec_src->ext_plug.plug = msg->msg[3]; | ||
| 353 | break; | ||
| 354 | case CEC_OP_RECORD_SRC_EXT_PHYS_ADDR: | ||
| 355 | rec_src->ext_phys_addr.phys_addr = | ||
| 356 | (msg->msg[3] << 8) | msg->msg[4]; | ||
| 357 | break; | ||
| 358 | } | ||
| 359 | } | ||
| 360 | |||
| 361 | static inline void cec_msg_record_status(struct cec_msg *msg, __u8 rec_status) | ||
| 362 | { | ||
| 363 | msg->len = 3; | ||
| 364 | msg->msg[1] = CEC_MSG_RECORD_STATUS; | ||
| 365 | msg->msg[2] = rec_status; | ||
| 366 | } | ||
| 367 | |||
| 368 | static inline void cec_ops_record_status(const struct cec_msg *msg, | ||
| 369 | __u8 *rec_status) | ||
| 370 | { | ||
| 371 | *rec_status = msg->msg[2]; | ||
| 372 | } | ||
| 373 | |||
| 374 | static inline void cec_msg_record_tv_screen(struct cec_msg *msg, | ||
| 375 | bool reply) | ||
| 376 | { | ||
| 377 | msg->len = 2; | ||
| 378 | msg->msg[1] = CEC_MSG_RECORD_TV_SCREEN; | ||
| 379 | msg->reply = reply ? CEC_MSG_RECORD_ON : 0; | ||
| 380 | } | ||
| 381 | |||
| 382 | |||
| 383 | /* Timer Programming Feature */ | ||
| 384 | static inline void cec_msg_timer_status(struct cec_msg *msg, | ||
| 385 | __u8 timer_overlap_warning, | ||
| 386 | __u8 media_info, | ||
| 387 | __u8 prog_info, | ||
| 388 | __u8 prog_error, | ||
| 389 | __u8 duration_hr, | ||
| 390 | __u8 duration_min) | ||
| 391 | { | ||
| 392 | msg->len = 3; | ||
| 393 | msg->msg[1] = CEC_MSG_TIMER_STATUS; | ||
| 394 | msg->msg[2] = (timer_overlap_warning << 7) | | ||
| 395 | (media_info << 5) | | ||
| 396 | (prog_info ? 0x10 : 0) | | ||
| 397 | (prog_info ? prog_info : prog_error); | ||
| 398 | if (prog_info == CEC_OP_PROG_INFO_NOT_ENOUGH_SPACE || | ||
| 399 | prog_info == CEC_OP_PROG_INFO_MIGHT_NOT_BE_ENOUGH_SPACE || | ||
| 400 | prog_error == CEC_OP_PROG_ERROR_DUPLICATE) { | ||
| 401 | msg->len += 2; | ||
| 402 | msg->msg[3] = ((duration_hr / 10) << 4) | (duration_hr % 10); | ||
| 403 | msg->msg[4] = ((duration_min / 10) << 4) | (duration_min % 10); | ||
| 404 | } | ||
| 405 | } | ||
| 406 | |||
| 407 | static inline void cec_ops_timer_status(struct cec_msg *msg, | ||
| 408 | __u8 *timer_overlap_warning, | ||
| 409 | __u8 *media_info, | ||
| 410 | __u8 *prog_info, | ||
| 411 | __u8 *prog_error, | ||
| 412 | __u8 *duration_hr, | ||
| 413 | __u8 *duration_min) | ||
| 414 | { | ||
| 415 | *timer_overlap_warning = msg->msg[2] >> 7; | ||
| 416 | *media_info = (msg->msg[2] >> 5) & 3; | ||
| 417 | if (msg->msg[2] & 0x10) { | ||
| 418 | *prog_info = msg->msg[2] & 0xf; | ||
| 419 | *prog_error = 0; | ||
| 420 | } else { | ||
| 421 | *prog_info = 0; | ||
| 422 | *prog_error = msg->msg[2] & 0xf; | ||
| 423 | } | ||
| 424 | if (*prog_info == CEC_OP_PROG_INFO_NOT_ENOUGH_SPACE || | ||
| 425 | *prog_info == CEC_OP_PROG_INFO_MIGHT_NOT_BE_ENOUGH_SPACE || | ||
| 426 | *prog_error == CEC_OP_PROG_ERROR_DUPLICATE) { | ||
| 427 | *duration_hr = (msg->msg[3] >> 4) * 10 + (msg->msg[3] & 0xf); | ||
| 428 | *duration_min = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf); | ||
| 429 | } else { | ||
| 430 | *duration_hr = *duration_min = 0; | ||
| 431 | } | ||
| 432 | } | ||
| 433 | |||
| 434 | static inline void cec_msg_timer_cleared_status(struct cec_msg *msg, | ||
| 435 | __u8 timer_cleared_status) | ||
| 436 | { | ||
| 437 | msg->len = 3; | ||
| 438 | msg->msg[1] = CEC_MSG_TIMER_CLEARED_STATUS; | ||
| 439 | msg->msg[2] = timer_cleared_status; | ||
| 440 | } | ||
| 441 | |||
| 442 | static inline void cec_ops_timer_cleared_status(struct cec_msg *msg, | ||
| 443 | __u8 *timer_cleared_status) | ||
| 444 | { | ||
| 445 | *timer_cleared_status = msg->msg[2]; | ||
| 446 | } | ||
| 447 | |||
| 448 | static inline void cec_msg_clear_analogue_timer(struct cec_msg *msg, | ||
| 449 | bool reply, | ||
| 450 | __u8 day, | ||
| 451 | __u8 month, | ||
| 452 | __u8 start_hr, | ||
| 453 | __u8 start_min, | ||
| 454 | __u8 duration_hr, | ||
| 455 | __u8 duration_min, | ||
| 456 | __u8 recording_seq, | ||
| 457 | __u8 ana_bcast_type, | ||
| 458 | __u16 ana_freq, | ||
| 459 | __u8 bcast_system) | ||
| 460 | { | ||
| 461 | msg->len = 13; | ||
| 462 | msg->msg[1] = CEC_MSG_CLEAR_ANALOGUE_TIMER; | ||
| 463 | msg->msg[2] = day; | ||
| 464 | msg->msg[3] = month; | ||
| 465 | /* Hours and minutes are in BCD format */ | ||
| 466 | msg->msg[4] = ((start_hr / 10) << 4) | (start_hr % 10); | ||
| 467 | msg->msg[5] = ((start_min / 10) << 4) | (start_min % 10); | ||
| 468 | msg->msg[6] = ((duration_hr / 10) << 4) | (duration_hr % 10); | ||
| 469 | msg->msg[7] = ((duration_min / 10) << 4) | (duration_min % 10); | ||
| 470 | msg->msg[8] = recording_seq; | ||
| 471 | msg->msg[9] = ana_bcast_type; | ||
| 472 | msg->msg[10] = ana_freq >> 8; | ||
| 473 | msg->msg[11] = ana_freq & 0xff; | ||
| 474 | msg->msg[12] = bcast_system; | ||
| 475 | msg->reply = reply ? CEC_MSG_TIMER_CLEARED_STATUS : 0; | ||
| 476 | } | ||
| 477 | |||
| 478 | static inline void cec_ops_clear_analogue_timer(struct cec_msg *msg, | ||
| 479 | __u8 *day, | ||
| 480 | __u8 *month, | ||
| 481 | __u8 *start_hr, | ||
| 482 | __u8 *start_min, | ||
| 483 | __u8 *duration_hr, | ||
| 484 | __u8 *duration_min, | ||
| 485 | __u8 *recording_seq, | ||
| 486 | __u8 *ana_bcast_type, | ||
| 487 | __u16 *ana_freq, | ||
| 488 | __u8 *bcast_system) | ||
| 489 | { | ||
| 490 | *day = msg->msg[2]; | ||
| 491 | *month = msg->msg[3]; | ||
| 492 | /* Hours and minutes are in BCD format */ | ||
| 493 | *start_hr = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf); | ||
| 494 | *start_min = (msg->msg[5] >> 4) * 10 + (msg->msg[5] & 0xf); | ||
| 495 | *duration_hr = (msg->msg[6] >> 4) * 10 + (msg->msg[6] & 0xf); | ||
| 496 | *duration_min = (msg->msg[7] >> 4) * 10 + (msg->msg[7] & 0xf); | ||
| 497 | *recording_seq = msg->msg[8]; | ||
| 498 | *ana_bcast_type = msg->msg[9]; | ||
| 499 | *ana_freq = (msg->msg[10] << 8) | msg->msg[11]; | ||
| 500 | *bcast_system = msg->msg[12]; | ||
| 501 | } | ||
| 502 | |||
| 503 | static inline void cec_msg_clear_digital_timer(struct cec_msg *msg, | ||
| 504 | bool reply, | ||
| 505 | __u8 day, | ||
| 506 | __u8 month, | ||
| 507 | __u8 start_hr, | ||
| 508 | __u8 start_min, | ||
| 509 | __u8 duration_hr, | ||
| 510 | __u8 duration_min, | ||
| 511 | __u8 recording_seq, | ||
| 512 | const struct cec_op_digital_service_id *digital) | ||
| 513 | { | ||
| 514 | msg->len = 16; | ||
| 515 | msg->reply = reply ? CEC_MSG_TIMER_CLEARED_STATUS : 0; | ||
| 516 | msg->msg[1] = CEC_MSG_CLEAR_DIGITAL_TIMER; | ||
| 517 | msg->msg[2] = day; | ||
| 518 | msg->msg[3] = month; | ||
| 519 | /* Hours and minutes are in BCD format */ | ||
| 520 | msg->msg[4] = ((start_hr / 10) << 4) | (start_hr % 10); | ||
| 521 | msg->msg[5] = ((start_min / 10) << 4) | (start_min % 10); | ||
| 522 | msg->msg[6] = ((duration_hr / 10) << 4) | (duration_hr % 10); | ||
| 523 | msg->msg[7] = ((duration_min / 10) << 4) | (duration_min % 10); | ||
| 524 | msg->msg[8] = recording_seq; | ||
| 525 | cec_set_digital_service_id(msg->msg + 9, digital); | ||
| 526 | } | ||
| 527 | |||
| 528 | static inline void cec_ops_clear_digital_timer(struct cec_msg *msg, | ||
| 529 | __u8 *day, | ||
| 530 | __u8 *month, | ||
| 531 | __u8 *start_hr, | ||
| 532 | __u8 *start_min, | ||
| 533 | __u8 *duration_hr, | ||
| 534 | __u8 *duration_min, | ||
| 535 | __u8 *recording_seq, | ||
| 536 | struct cec_op_digital_service_id *digital) | ||
| 537 | { | ||
| 538 | *day = msg->msg[2]; | ||
| 539 | *month = msg->msg[3]; | ||
| 540 | /* Hours and minutes are in BCD format */ | ||
| 541 | *start_hr = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf); | ||
| 542 | *start_min = (msg->msg[5] >> 4) * 10 + (msg->msg[5] & 0xf); | ||
| 543 | *duration_hr = (msg->msg[6] >> 4) * 10 + (msg->msg[6] & 0xf); | ||
| 544 | *duration_min = (msg->msg[7] >> 4) * 10 + (msg->msg[7] & 0xf); | ||
| 545 | *recording_seq = msg->msg[8]; | ||
| 546 | cec_get_digital_service_id(msg->msg + 9, digital); | ||
| 547 | } | ||
| 548 | |||
| 549 | static inline void cec_msg_clear_ext_timer(struct cec_msg *msg, | ||
| 550 | bool reply, | ||
| 551 | __u8 day, | ||
| 552 | __u8 month, | ||
| 553 | __u8 start_hr, | ||
| 554 | __u8 start_min, | ||
| 555 | __u8 duration_hr, | ||
| 556 | __u8 duration_min, | ||
| 557 | __u8 recording_seq, | ||
| 558 | __u8 ext_src_spec, | ||
| 559 | __u8 plug, | ||
| 560 | __u16 phys_addr) | ||
| 561 | { | ||
| 562 | msg->len = 13; | ||
| 563 | msg->msg[1] = CEC_MSG_CLEAR_EXT_TIMER; | ||
| 564 | msg->msg[2] = day; | ||
| 565 | msg->msg[3] = month; | ||
| 566 | /* Hours and minutes are in BCD format */ | ||
| 567 | msg->msg[4] = ((start_hr / 10) << 4) | (start_hr % 10); | ||
| 568 | msg->msg[5] = ((start_min / 10) << 4) | (start_min % 10); | ||
| 569 | msg->msg[6] = ((duration_hr / 10) << 4) | (duration_hr % 10); | ||
| 570 | msg->msg[7] = ((duration_min / 10) << 4) | (duration_min % 10); | ||
| 571 | msg->msg[8] = recording_seq; | ||
| 572 | msg->msg[9] = ext_src_spec; | ||
| 573 | msg->msg[10] = plug; | ||
| 574 | msg->msg[11] = phys_addr >> 8; | ||
| 575 | msg->msg[12] = phys_addr & 0xff; | ||
| 576 | msg->reply = reply ? CEC_MSG_TIMER_CLEARED_STATUS : 0; | ||
| 577 | } | ||
| 578 | |||
| 579 | static inline void cec_ops_clear_ext_timer(struct cec_msg *msg, | ||
| 580 | __u8 *day, | ||
| 581 | __u8 *month, | ||
| 582 | __u8 *start_hr, | ||
| 583 | __u8 *start_min, | ||
| 584 | __u8 *duration_hr, | ||
| 585 | __u8 *duration_min, | ||
| 586 | __u8 *recording_seq, | ||
| 587 | __u8 *ext_src_spec, | ||
| 588 | __u8 *plug, | ||
| 589 | __u16 *phys_addr) | ||
| 590 | { | ||
| 591 | *day = msg->msg[2]; | ||
| 592 | *month = msg->msg[3]; | ||
| 593 | /* Hours and minutes are in BCD format */ | ||
| 594 | *start_hr = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf); | ||
| 595 | *start_min = (msg->msg[5] >> 4) * 10 + (msg->msg[5] & 0xf); | ||
| 596 | *duration_hr = (msg->msg[6] >> 4) * 10 + (msg->msg[6] & 0xf); | ||
| 597 | *duration_min = (msg->msg[7] >> 4) * 10 + (msg->msg[7] & 0xf); | ||
| 598 | *recording_seq = msg->msg[8]; | ||
| 599 | *ext_src_spec = msg->msg[9]; | ||
| 600 | *plug = msg->msg[10]; | ||
| 601 | *phys_addr = (msg->msg[11] << 8) | msg->msg[12]; | ||
| 602 | } | ||
| 603 | |||
| 604 | static inline void cec_msg_set_analogue_timer(struct cec_msg *msg, | ||
| 605 | bool reply, | ||
| 606 | __u8 day, | ||
| 607 | __u8 month, | ||
| 608 | __u8 start_hr, | ||
| 609 | __u8 start_min, | ||
| 610 | __u8 duration_hr, | ||
| 611 | __u8 duration_min, | ||
| 612 | __u8 recording_seq, | ||
| 613 | __u8 ana_bcast_type, | ||
| 614 | __u16 ana_freq, | ||
| 615 | __u8 bcast_system) | ||
| 616 | { | ||
| 617 | msg->len = 13; | ||
| 618 | msg->msg[1] = CEC_MSG_SET_ANALOGUE_TIMER; | ||
| 619 | msg->msg[2] = day; | ||
| 620 | msg->msg[3] = month; | ||
| 621 | /* Hours and minutes are in BCD format */ | ||
| 622 | msg->msg[4] = ((start_hr / 10) << 4) | (start_hr % 10); | ||
| 623 | msg->msg[5] = ((start_min / 10) << 4) | (start_min % 10); | ||
| 624 | msg->msg[6] = ((duration_hr / 10) << 4) | (duration_hr % 10); | ||
| 625 | msg->msg[7] = ((duration_min / 10) << 4) | (duration_min % 10); | ||
| 626 | msg->msg[8] = recording_seq; | ||
| 627 | msg->msg[9] = ana_bcast_type; | ||
| 628 | msg->msg[10] = ana_freq >> 8; | ||
| 629 | msg->msg[11] = ana_freq & 0xff; | ||
| 630 | msg->msg[12] = bcast_system; | ||
| 631 | msg->reply = reply ? CEC_MSG_TIMER_STATUS : 0; | ||
| 632 | } | ||
| 633 | |||
| 634 | static inline void cec_ops_set_analogue_timer(struct cec_msg *msg, | ||
| 635 | __u8 *day, | ||
| 636 | __u8 *month, | ||
| 637 | __u8 *start_hr, | ||
| 638 | __u8 *start_min, | ||
| 639 | __u8 *duration_hr, | ||
| 640 | __u8 *duration_min, | ||
| 641 | __u8 *recording_seq, | ||
| 642 | __u8 *ana_bcast_type, | ||
| 643 | __u16 *ana_freq, | ||
| 644 | __u8 *bcast_system) | ||
| 645 | { | ||
| 646 | *day = msg->msg[2]; | ||
| 647 | *month = msg->msg[3]; | ||
| 648 | /* Hours and minutes are in BCD format */ | ||
| 649 | *start_hr = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf); | ||
| 650 | *start_min = (msg->msg[5] >> 4) * 10 + (msg->msg[5] & 0xf); | ||
| 651 | *duration_hr = (msg->msg[6] >> 4) * 10 + (msg->msg[6] & 0xf); | ||
| 652 | *duration_min = (msg->msg[7] >> 4) * 10 + (msg->msg[7] & 0xf); | ||
| 653 | *recording_seq = msg->msg[8]; | ||
| 654 | *ana_bcast_type = msg->msg[9]; | ||
| 655 | *ana_freq = (msg->msg[10] << 8) | msg->msg[11]; | ||
| 656 | *bcast_system = msg->msg[12]; | ||
| 657 | } | ||
| 658 | |||
| 659 | static inline void cec_msg_set_digital_timer(struct cec_msg *msg, | ||
| 660 | bool reply, | ||
| 661 | __u8 day, | ||
| 662 | __u8 month, | ||
| 663 | __u8 start_hr, | ||
| 664 | __u8 start_min, | ||
| 665 | __u8 duration_hr, | ||
| 666 | __u8 duration_min, | ||
| 667 | __u8 recording_seq, | ||
| 668 | const struct cec_op_digital_service_id *digital) | ||
| 669 | { | ||
| 670 | msg->len = 16; | ||
| 671 | msg->reply = reply ? CEC_MSG_TIMER_STATUS : 0; | ||
| 672 | msg->msg[1] = CEC_MSG_SET_DIGITAL_TIMER; | ||
| 673 | msg->msg[2] = day; | ||
| 674 | msg->msg[3] = month; | ||
| 675 | /* Hours and minutes are in BCD format */ | ||
| 676 | msg->msg[4] = ((start_hr / 10) << 4) | (start_hr % 10); | ||
| 677 | msg->msg[5] = ((start_min / 10) << 4) | (start_min % 10); | ||
| 678 | msg->msg[6] = ((duration_hr / 10) << 4) | (duration_hr % 10); | ||
| 679 | msg->msg[7] = ((duration_min / 10) << 4) | (duration_min % 10); | ||
| 680 | msg->msg[8] = recording_seq; | ||
| 681 | cec_set_digital_service_id(msg->msg + 9, digital); | ||
| 682 | } | ||
| 683 | |||
| 684 | static inline void cec_ops_set_digital_timer(struct cec_msg *msg, | ||
| 685 | __u8 *day, | ||
| 686 | __u8 *month, | ||
| 687 | __u8 *start_hr, | ||
| 688 | __u8 *start_min, | ||
| 689 | __u8 *duration_hr, | ||
| 690 | __u8 *duration_min, | ||
| 691 | __u8 *recording_seq, | ||
| 692 | struct cec_op_digital_service_id *digital) | ||
| 693 | { | ||
| 694 | *day = msg->msg[2]; | ||
| 695 | *month = msg->msg[3]; | ||
| 696 | /* Hours and minutes are in BCD format */ | ||
| 697 | *start_hr = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf); | ||
| 698 | *start_min = (msg->msg[5] >> 4) * 10 + (msg->msg[5] & 0xf); | ||
| 699 | *duration_hr = (msg->msg[6] >> 4) * 10 + (msg->msg[6] & 0xf); | ||
| 700 | *duration_min = (msg->msg[7] >> 4) * 10 + (msg->msg[7] & 0xf); | ||
| 701 | *recording_seq = msg->msg[8]; | ||
| 702 | cec_get_digital_service_id(msg->msg + 9, digital); | ||
| 703 | } | ||
| 704 | |||
| 705 | static inline void cec_msg_set_ext_timer(struct cec_msg *msg, | ||
| 706 | bool reply, | ||
| 707 | __u8 day, | ||
| 708 | __u8 month, | ||
| 709 | __u8 start_hr, | ||
| 710 | __u8 start_min, | ||
| 711 | __u8 duration_hr, | ||
| 712 | __u8 duration_min, | ||
| 713 | __u8 recording_seq, | ||
| 714 | __u8 ext_src_spec, | ||
| 715 | __u8 plug, | ||
| 716 | __u16 phys_addr) | ||
| 717 | { | ||
| 718 | msg->len = 13; | ||
| 719 | msg->msg[1] = CEC_MSG_SET_EXT_TIMER; | ||
| 720 | msg->msg[2] = day; | ||
| 721 | msg->msg[3] = month; | ||
| 722 | /* Hours and minutes are in BCD format */ | ||
| 723 | msg->msg[4] = ((start_hr / 10) << 4) | (start_hr % 10); | ||
| 724 | msg->msg[5] = ((start_min / 10) << 4) | (start_min % 10); | ||
| 725 | msg->msg[6] = ((duration_hr / 10) << 4) | (duration_hr % 10); | ||
| 726 | msg->msg[7] = ((duration_min / 10) << 4) | (duration_min % 10); | ||
| 727 | msg->msg[8] = recording_seq; | ||
| 728 | msg->msg[9] = ext_src_spec; | ||
| 729 | msg->msg[10] = plug; | ||
| 730 | msg->msg[11] = phys_addr >> 8; | ||
| 731 | msg->msg[12] = phys_addr & 0xff; | ||
| 732 | msg->reply = reply ? CEC_MSG_TIMER_STATUS : 0; | ||
| 733 | } | ||
| 734 | |||
| 735 | static inline void cec_ops_set_ext_timer(struct cec_msg *msg, | ||
| 736 | __u8 *day, | ||
| 737 | __u8 *month, | ||
| 738 | __u8 *start_hr, | ||
| 739 | __u8 *start_min, | ||
| 740 | __u8 *duration_hr, | ||
| 741 | __u8 *duration_min, | ||
| 742 | __u8 *recording_seq, | ||
| 743 | __u8 *ext_src_spec, | ||
| 744 | __u8 *plug, | ||
| 745 | __u16 *phys_addr) | ||
| 746 | { | ||
| 747 | *day = msg->msg[2]; | ||
| 748 | *month = msg->msg[3]; | ||
| 749 | /* Hours and minutes are in BCD format */ | ||
| 750 | *start_hr = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf); | ||
| 751 | *start_min = (msg->msg[5] >> 4) * 10 + (msg->msg[5] & 0xf); | ||
| 752 | *duration_hr = (msg->msg[6] >> 4) * 10 + (msg->msg[6] & 0xf); | ||
| 753 | *duration_min = (msg->msg[7] >> 4) * 10 + (msg->msg[7] & 0xf); | ||
| 754 | *recording_seq = msg->msg[8]; | ||
| 755 | *ext_src_spec = msg->msg[9]; | ||
| 756 | *plug = msg->msg[10]; | ||
| 757 | *phys_addr = (msg->msg[11] << 8) | msg->msg[12]; | ||
| 758 | } | ||
| 759 | |||
| 760 | static inline void cec_msg_set_timer_program_title(struct cec_msg *msg, | ||
| 761 | const char *prog_title) | ||
| 762 | { | ||
| 763 | unsigned int len = strlen(prog_title); | ||
| 764 | |||
| 765 | if (len > 14) | ||
| 766 | len = 14; | ||
| 767 | msg->len = 2 + len; | ||
| 768 | msg->msg[1] = CEC_MSG_SET_TIMER_PROGRAM_TITLE; | ||
| 769 | memcpy(msg->msg + 2, prog_title, len); | ||
| 770 | } | ||
| 771 | |||
| 772 | static inline void cec_ops_set_timer_program_title(const struct cec_msg *msg, | ||
| 773 | char *prog_title) | ||
| 774 | { | ||
| 775 | unsigned int len = msg->len - 2; | ||
| 776 | |||
| 777 | if (len > 14) | ||
| 778 | len = 14; | ||
| 779 | memcpy(prog_title, msg->msg + 2, len); | ||
| 780 | prog_title[len] = '\0'; | ||
| 781 | } | ||
| 782 | |||
| 783 | /* System Information Feature */ | ||
| 784 | static inline void cec_msg_cec_version(struct cec_msg *msg, __u8 cec_version) | ||
| 785 | { | ||
| 786 | msg->len = 3; | ||
| 787 | msg->msg[1] = CEC_MSG_CEC_VERSION; | ||
| 788 | msg->msg[2] = cec_version; | ||
| 789 | } | ||
| 790 | |||
| 791 | static inline void cec_ops_cec_version(const struct cec_msg *msg, | ||
| 792 | __u8 *cec_version) | ||
| 793 | { | ||
| 794 | *cec_version = msg->msg[2]; | ||
| 795 | } | ||
| 796 | |||
| 797 | static inline void cec_msg_get_cec_version(struct cec_msg *msg, | ||
| 798 | bool reply) | ||
| 799 | { | ||
| 800 | msg->len = 2; | ||
| 801 | msg->msg[1] = CEC_MSG_GET_CEC_VERSION; | ||
| 802 | msg->reply = reply ? CEC_MSG_CEC_VERSION : 0; | ||
| 803 | } | ||
| 804 | |||
| 805 | static inline void cec_msg_report_physical_addr(struct cec_msg *msg, | ||
| 806 | __u16 phys_addr, __u8 prim_devtype) | ||
| 807 | { | ||
| 808 | msg->len = 5; | ||
| 809 | msg->msg[0] |= 0xf; /* broadcast */ | ||
| 810 | msg->msg[1] = CEC_MSG_REPORT_PHYSICAL_ADDR; | ||
| 811 | msg->msg[2] = phys_addr >> 8; | ||
| 812 | msg->msg[3] = phys_addr & 0xff; | ||
| 813 | msg->msg[4] = prim_devtype; | ||
| 814 | } | ||
| 815 | |||
| 816 | static inline void cec_ops_report_physical_addr(const struct cec_msg *msg, | ||
| 817 | __u16 *phys_addr, __u8 *prim_devtype) | ||
| 818 | { | ||
| 819 | *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; | ||
| 820 | *prim_devtype = msg->msg[4]; | ||
| 821 | } | ||
| 822 | |||
| 823 | static inline void cec_msg_give_physical_addr(struct cec_msg *msg, | ||
| 824 | bool reply) | ||
| 825 | { | ||
| 826 | msg->len = 2; | ||
| 827 | msg->msg[1] = CEC_MSG_GIVE_PHYSICAL_ADDR; | ||
| 828 | msg->reply = reply ? CEC_MSG_REPORT_PHYSICAL_ADDR : 0; | ||
| 829 | } | ||
| 830 | |||
| 831 | static inline void cec_msg_set_menu_language(struct cec_msg *msg, | ||
| 832 | const char *language) | ||
| 833 | { | ||
| 834 | msg->len = 5; | ||
| 835 | msg->msg[0] |= 0xf; /* broadcast */ | ||
| 836 | msg->msg[1] = CEC_MSG_SET_MENU_LANGUAGE; | ||
| 837 | memcpy(msg->msg + 2, language, 3); | ||
| 838 | } | ||
| 839 | |||
| 840 | static inline void cec_ops_set_menu_language(struct cec_msg *msg, | ||
| 841 | char *language) | ||
| 842 | { | ||
| 843 | memcpy(language, msg->msg + 2, 3); | ||
| 844 | language[3] = '\0'; | ||
| 845 | } | ||
| 846 | |||
| 847 | static inline void cec_msg_get_menu_language(struct cec_msg *msg, | ||
| 848 | bool reply) | ||
| 849 | { | ||
| 850 | msg->len = 2; | ||
| 851 | msg->msg[1] = CEC_MSG_GET_MENU_LANGUAGE; | ||
| 852 | msg->reply = reply ? CEC_MSG_SET_MENU_LANGUAGE : 0; | ||
| 853 | } | ||
| 854 | |||
| 855 | /* | ||
| 856 | * Assumes a single RC Profile byte and a single Device Features byte, | ||
| 857 | * i.e. no extended features are supported by this helper function. | ||
| 858 | * | ||
| 859 | * As of CEC 2.0 no extended features are defined, should those be added | ||
| 860 | * in the future, then this function needs to be adapted or a new function | ||
| 861 | * should be added. | ||
| 862 | */ | ||
| 863 | static inline void cec_msg_report_features(struct cec_msg *msg, | ||
| 864 | __u8 cec_version, __u8 all_device_types, | ||
| 865 | __u8 rc_profile, __u8 dev_features) | ||
| 866 | { | ||
| 867 | msg->len = 6; | ||
| 868 | msg->msg[0] |= 0xf; /* broadcast */ | ||
| 869 | msg->msg[1] = CEC_MSG_REPORT_FEATURES; | ||
| 870 | msg->msg[2] = cec_version; | ||
| 871 | msg->msg[3] = all_device_types; | ||
| 872 | msg->msg[4] = rc_profile; | ||
| 873 | msg->msg[5] = dev_features; | ||
| 874 | } | ||
| 875 | |||
| 876 | static inline void cec_ops_report_features(const struct cec_msg *msg, | ||
| 877 | __u8 *cec_version, __u8 *all_device_types, | ||
| 878 | const __u8 **rc_profile, const __u8 **dev_features) | ||
| 879 | { | ||
| 880 | const __u8 *p = &msg->msg[4]; | ||
| 881 | |||
| 882 | *cec_version = msg->msg[2]; | ||
| 883 | *all_device_types = msg->msg[3]; | ||
| 884 | *rc_profile = p; | ||
| 885 | while (p < &msg->msg[14] && (*p & CEC_OP_FEAT_EXT)) | ||
| 886 | p++; | ||
| 887 | if (!(*p & CEC_OP_FEAT_EXT)) { | ||
| 888 | *dev_features = p + 1; | ||
| 889 | while (p < &msg->msg[15] && (*p & CEC_OP_FEAT_EXT)) | ||
| 890 | p++; | ||
| 891 | } | ||
| 892 | if (*p & CEC_OP_FEAT_EXT) | ||
| 893 | *rc_profile = *dev_features = NULL; | ||
| 894 | } | ||
| 895 | |||
| 896 | static inline void cec_msg_give_features(struct cec_msg *msg, | ||
| 897 | bool reply) | ||
| 898 | { | ||
| 899 | msg->len = 2; | ||
| 900 | msg->msg[1] = CEC_MSG_GIVE_FEATURES; | ||
| 901 | msg->reply = reply ? CEC_MSG_REPORT_FEATURES : 0; | ||
| 902 | } | ||
| 903 | |||
| 904 | /* Deck Control Feature */ | ||
| 905 | static inline void cec_msg_deck_control(struct cec_msg *msg, | ||
| 906 | __u8 deck_control_mode) | ||
| 907 | { | ||
| 908 | msg->len = 3; | ||
| 909 | msg->msg[1] = CEC_MSG_DECK_CONTROL; | ||
| 910 | msg->msg[2] = deck_control_mode; | ||
| 911 | } | ||
| 912 | |||
| 913 | static inline void cec_ops_deck_control(struct cec_msg *msg, | ||
| 914 | __u8 *deck_control_mode) | ||
| 915 | { | ||
| 916 | *deck_control_mode = msg->msg[2]; | ||
| 917 | } | ||
| 918 | |||
| 919 | static inline void cec_msg_deck_status(struct cec_msg *msg, | ||
| 920 | __u8 deck_info) | ||
| 921 | { | ||
| 922 | msg->len = 3; | ||
| 923 | msg->msg[1] = CEC_MSG_DECK_STATUS; | ||
| 924 | msg->msg[2] = deck_info; | ||
| 925 | } | ||
| 926 | |||
| 927 | static inline void cec_ops_deck_status(struct cec_msg *msg, | ||
| 928 | __u8 *deck_info) | ||
| 929 | { | ||
| 930 | *deck_info = msg->msg[2]; | ||
| 931 | } | ||
| 932 | |||
| 933 | static inline void cec_msg_give_deck_status(struct cec_msg *msg, | ||
| 934 | bool reply, | ||
| 935 | __u8 status_req) | ||
| 936 | { | ||
| 937 | msg->len = 3; | ||
| 938 | msg->msg[1] = CEC_MSG_GIVE_DECK_STATUS; | ||
| 939 | msg->msg[2] = status_req; | ||
| 940 | msg->reply = reply ? CEC_MSG_DECK_STATUS : 0; | ||
| 941 | } | ||
| 942 | |||
| 943 | static inline void cec_ops_give_deck_status(struct cec_msg *msg, | ||
| 944 | __u8 *status_req) | ||
| 945 | { | ||
| 946 | *status_req = msg->msg[2]; | ||
| 947 | } | ||
| 948 | |||
| 949 | static inline void cec_msg_play(struct cec_msg *msg, | ||
| 950 | __u8 play_mode) | ||
| 951 | { | ||
| 952 | msg->len = 3; | ||
| 953 | msg->msg[1] = CEC_MSG_PLAY; | ||
| 954 | msg->msg[2] = play_mode; | ||
| 955 | } | ||
| 956 | |||
| 957 | static inline void cec_ops_play(struct cec_msg *msg, | ||
| 958 | __u8 *play_mode) | ||
| 959 | { | ||
| 960 | *play_mode = msg->msg[2]; | ||
| 961 | } | ||
| 962 | |||
| 963 | |||
| 964 | /* Tuner Control Feature */ | ||
| 965 | struct cec_op_tuner_device_info { | ||
| 966 | __u8 rec_flag; | ||
| 967 | __u8 tuner_display_info; | ||
| 968 | bool is_analog; | ||
| 969 | union { | ||
| 970 | struct cec_op_digital_service_id digital; | ||
| 971 | struct { | ||
| 972 | __u8 ana_bcast_type; | ||
| 973 | __u16 ana_freq; | ||
| 974 | __u8 bcast_system; | ||
| 975 | } analog; | ||
| 976 | }; | ||
| 977 | }; | ||
| 978 | |||
| 979 | static inline void cec_msg_tuner_device_status_analog(struct cec_msg *msg, | ||
| 980 | __u8 rec_flag, | ||
| 981 | __u8 tuner_display_info, | ||
| 982 | __u8 ana_bcast_type, | ||
| 983 | __u16 ana_freq, | ||
| 984 | __u8 bcast_system) | ||
| 985 | { | ||
| 986 | msg->len = 7; | ||
| 987 | msg->msg[1] = CEC_MSG_TUNER_DEVICE_STATUS; | ||
| 988 | msg->msg[2] = (rec_flag << 7) | tuner_display_info; | ||
| 989 | msg->msg[3] = ana_bcast_type; | ||
| 990 | msg->msg[4] = ana_freq >> 8; | ||
| 991 | msg->msg[5] = ana_freq & 0xff; | ||
| 992 | msg->msg[6] = bcast_system; | ||
| 993 | } | ||
| 994 | |||
| 995 | static inline void cec_msg_tuner_device_status_digital(struct cec_msg *msg, | ||
| 996 | __u8 rec_flag, __u8 tuner_display_info, | ||
| 997 | const struct cec_op_digital_service_id *digital) | ||
| 998 | { | ||
| 999 | msg->len = 10; | ||
| 1000 | msg->msg[1] = CEC_MSG_TUNER_DEVICE_STATUS; | ||
| 1001 | msg->msg[2] = (rec_flag << 7) | tuner_display_info; | ||
| 1002 | cec_set_digital_service_id(msg->msg + 3, digital); | ||
| 1003 | } | ||
| 1004 | |||
| 1005 | static inline void cec_msg_tuner_device_status(struct cec_msg *msg, | ||
| 1006 | const struct cec_op_tuner_device_info *tuner_dev_info) | ||
| 1007 | { | ||
| 1008 | if (tuner_dev_info->is_analog) | ||
| 1009 | cec_msg_tuner_device_status_analog(msg, | ||
| 1010 | tuner_dev_info->rec_flag, | ||
| 1011 | tuner_dev_info->tuner_display_info, | ||
| 1012 | tuner_dev_info->analog.ana_bcast_type, | ||
| 1013 | tuner_dev_info->analog.ana_freq, | ||
| 1014 | tuner_dev_info->analog.bcast_system); | ||
| 1015 | else | ||
| 1016 | cec_msg_tuner_device_status_digital(msg, | ||
| 1017 | tuner_dev_info->rec_flag, | ||
| 1018 | tuner_dev_info->tuner_display_info, | ||
| 1019 | &tuner_dev_info->digital); | ||
| 1020 | } | ||
| 1021 | |||
| 1022 | static inline void cec_ops_tuner_device_status(struct cec_msg *msg, | ||
| 1023 | struct cec_op_tuner_device_info *tuner_dev_info) | ||
| 1024 | { | ||
| 1025 | tuner_dev_info->is_analog = msg->len < 10; | ||
| 1026 | tuner_dev_info->rec_flag = msg->msg[2] >> 7; | ||
| 1027 | tuner_dev_info->tuner_display_info = msg->msg[2] & 0x7f; | ||
| 1028 | if (tuner_dev_info->is_analog) { | ||
| 1029 | tuner_dev_info->analog.ana_bcast_type = msg->msg[3]; | ||
| 1030 | tuner_dev_info->analog.ana_freq = (msg->msg[4] << 8) | msg->msg[5]; | ||
| 1031 | tuner_dev_info->analog.bcast_system = msg->msg[6]; | ||
| 1032 | return; | ||
| 1033 | } | ||
| 1034 | cec_get_digital_service_id(msg->msg + 3, &tuner_dev_info->digital); | ||
| 1035 | } | ||
| 1036 | |||
| 1037 | static inline void cec_msg_give_tuner_device_status(struct cec_msg *msg, | ||
| 1038 | bool reply, | ||
| 1039 | __u8 status_req) | ||
| 1040 | { | ||
| 1041 | msg->len = 3; | ||
| 1042 | msg->msg[1] = CEC_MSG_GIVE_TUNER_DEVICE_STATUS; | ||
| 1043 | msg->msg[2] = status_req; | ||
| 1044 | msg->reply = reply ? CEC_MSG_TUNER_DEVICE_STATUS : 0; | ||
| 1045 | } | ||
| 1046 | |||
| 1047 | static inline void cec_ops_give_tuner_device_status(struct cec_msg *msg, | ||
| 1048 | __u8 *status_req) | ||
| 1049 | { | ||
| 1050 | *status_req = msg->msg[2]; | ||
| 1051 | } | ||
| 1052 | |||
| 1053 | static inline void cec_msg_select_analogue_service(struct cec_msg *msg, | ||
| 1054 | __u8 ana_bcast_type, | ||
| 1055 | __u16 ana_freq, | ||
| 1056 | __u8 bcast_system) | ||
| 1057 | { | ||
| 1058 | msg->len = 6; | ||
| 1059 | msg->msg[1] = CEC_MSG_SELECT_ANALOGUE_SERVICE; | ||
| 1060 | msg->msg[2] = ana_bcast_type; | ||
| 1061 | msg->msg[3] = ana_freq >> 8; | ||
| 1062 | msg->msg[4] = ana_freq & 0xff; | ||
| 1063 | msg->msg[5] = bcast_system; | ||
| 1064 | } | ||
| 1065 | |||
| 1066 | static inline void cec_ops_select_analogue_service(struct cec_msg *msg, | ||
| 1067 | __u8 *ana_bcast_type, | ||
| 1068 | __u16 *ana_freq, | ||
| 1069 | __u8 *bcast_system) | ||
| 1070 | { | ||
| 1071 | *ana_bcast_type = msg->msg[2]; | ||
| 1072 | *ana_freq = (msg->msg[3] << 8) | msg->msg[4]; | ||
| 1073 | *bcast_system = msg->msg[5]; | ||
| 1074 | } | ||
| 1075 | |||
| 1076 | static inline void cec_msg_select_digital_service(struct cec_msg *msg, | ||
| 1077 | const struct cec_op_digital_service_id *digital) | ||
| 1078 | { | ||
| 1079 | msg->len = 9; | ||
| 1080 | msg->msg[1] = CEC_MSG_SELECT_DIGITAL_SERVICE; | ||
| 1081 | cec_set_digital_service_id(msg->msg + 2, digital); | ||
| 1082 | } | ||
| 1083 | |||
| 1084 | static inline void cec_ops_select_digital_service(struct cec_msg *msg, | ||
| 1085 | struct cec_op_digital_service_id *digital) | ||
| 1086 | { | ||
| 1087 | cec_get_digital_service_id(msg->msg + 2, digital); | ||
| 1088 | } | ||
| 1089 | |||
| 1090 | static inline void cec_msg_tuner_step_decrement(struct cec_msg *msg) | ||
| 1091 | { | ||
| 1092 | msg->len = 2; | ||
| 1093 | msg->msg[1] = CEC_MSG_TUNER_STEP_DECREMENT; | ||
| 1094 | } | ||
| 1095 | |||
| 1096 | static inline void cec_msg_tuner_step_increment(struct cec_msg *msg) | ||
| 1097 | { | ||
| 1098 | msg->len = 2; | ||
| 1099 | msg->msg[1] = CEC_MSG_TUNER_STEP_INCREMENT; | ||
| 1100 | } | ||
| 1101 | |||
| 1102 | |||
| 1103 | /* Vendor Specific Commands Feature */ | ||
| 1104 | static inline void cec_msg_device_vendor_id(struct cec_msg *msg, __u32 vendor_id) | ||
| 1105 | { | ||
| 1106 | msg->len = 5; | ||
| 1107 | msg->msg[0] |= 0xf; /* broadcast */ | ||
| 1108 | msg->msg[1] = CEC_MSG_DEVICE_VENDOR_ID; | ||
| 1109 | msg->msg[2] = vendor_id >> 16; | ||
| 1110 | msg->msg[3] = (vendor_id >> 8) & 0xff; | ||
| 1111 | msg->msg[4] = vendor_id & 0xff; | ||
| 1112 | } | ||
| 1113 | |||
| 1114 | static inline void cec_ops_device_vendor_id(const struct cec_msg *msg, | ||
| 1115 | __u32 *vendor_id) | ||
| 1116 | { | ||
| 1117 | *vendor_id = (msg->msg[2] << 16) | (msg->msg[3] << 8) | msg->msg[4]; | ||
| 1118 | } | ||
| 1119 | |||
| 1120 | static inline void cec_msg_give_device_vendor_id(struct cec_msg *msg, | ||
| 1121 | bool reply) | ||
| 1122 | { | ||
| 1123 | msg->len = 2; | ||
| 1124 | msg->msg[1] = CEC_MSG_GIVE_DEVICE_VENDOR_ID; | ||
| 1125 | msg->reply = reply ? CEC_MSG_DEVICE_VENDOR_ID : 0; | ||
| 1126 | } | ||
| 1127 | |||
| 1128 | static inline void cec_msg_vendor_remote_button_up(struct cec_msg *msg) | ||
| 1129 | { | ||
| 1130 | msg->len = 2; | ||
| 1131 | msg->msg[1] = CEC_MSG_VENDOR_REMOTE_BUTTON_UP; | ||
| 1132 | } | ||
| 1133 | |||
| 1134 | |||
| 1135 | /* OSD Display Feature */ | ||
| 1136 | static inline void cec_msg_set_osd_string(struct cec_msg *msg, | ||
| 1137 | __u8 disp_ctl, | ||
| 1138 | const char *osd) | ||
| 1139 | { | ||
| 1140 | unsigned int len = strlen(osd); | ||
| 1141 | |||
| 1142 | if (len > 13) | ||
| 1143 | len = 13; | ||
| 1144 | msg->len = 3 + len; | ||
| 1145 | msg->msg[1] = CEC_MSG_SET_OSD_STRING; | ||
| 1146 | msg->msg[2] = disp_ctl; | ||
| 1147 | memcpy(msg->msg + 3, osd, len); | ||
| 1148 | } | ||
| 1149 | |||
| 1150 | static inline void cec_ops_set_osd_string(const struct cec_msg *msg, | ||
| 1151 | __u8 *disp_ctl, | ||
| 1152 | char *osd) | ||
| 1153 | { | ||
| 1154 | unsigned int len = msg->len - 3; | ||
| 1155 | |||
| 1156 | *disp_ctl = msg->msg[2]; | ||
| 1157 | if (len > 13) | ||
| 1158 | len = 13; | ||
| 1159 | memcpy(osd, msg->msg + 3, len); | ||
| 1160 | osd[len] = '\0'; | ||
| 1161 | } | ||
| 1162 | |||
| 1163 | |||
| 1164 | /* Device OSD Transfer Feature */ | ||
| 1165 | static inline void cec_msg_set_osd_name(struct cec_msg *msg, const char *name) | ||
| 1166 | { | ||
| 1167 | unsigned int len = strlen(name); | ||
| 1168 | |||
| 1169 | if (len > 14) | ||
| 1170 | len = 14; | ||
| 1171 | msg->len = 2 + len; | ||
| 1172 | msg->msg[1] = CEC_MSG_SET_OSD_NAME; | ||
| 1173 | memcpy(msg->msg + 2, name, len); | ||
| 1174 | } | ||
| 1175 | |||
| 1176 | static inline void cec_ops_set_osd_name(const struct cec_msg *msg, | ||
| 1177 | char *name) | ||
| 1178 | { | ||
| 1179 | unsigned int len = msg->len - 2; | ||
| 1180 | |||
| 1181 | if (len > 14) | ||
| 1182 | len = 14; | ||
| 1183 | memcpy(name, msg->msg + 2, len); | ||
| 1184 | name[len] = '\0'; | ||
| 1185 | } | ||
| 1186 | |||
| 1187 | static inline void cec_msg_give_osd_name(struct cec_msg *msg, | ||
| 1188 | bool reply) | ||
| 1189 | { | ||
| 1190 | msg->len = 2; | ||
| 1191 | msg->msg[1] = CEC_MSG_GIVE_OSD_NAME; | ||
| 1192 | msg->reply = reply ? CEC_MSG_SET_OSD_NAME : 0; | ||
| 1193 | } | ||
| 1194 | |||
| 1195 | |||
| 1196 | /* Device Menu Control Feature */ | ||
| 1197 | static inline void cec_msg_menu_status(struct cec_msg *msg, | ||
| 1198 | __u8 menu_state) | ||
| 1199 | { | ||
| 1200 | msg->len = 3; | ||
| 1201 | msg->msg[1] = CEC_MSG_MENU_STATUS; | ||
| 1202 | msg->msg[2] = menu_state; | ||
| 1203 | } | ||
| 1204 | |||
| 1205 | static inline void cec_ops_menu_status(struct cec_msg *msg, | ||
| 1206 | __u8 *menu_state) | ||
| 1207 | { | ||
| 1208 | *menu_state = msg->msg[2]; | ||
| 1209 | } | ||
| 1210 | |||
| 1211 | static inline void cec_msg_menu_request(struct cec_msg *msg, | ||
| 1212 | bool reply, | ||
| 1213 | __u8 menu_req) | ||
| 1214 | { | ||
| 1215 | msg->len = 3; | ||
| 1216 | msg->msg[1] = CEC_MSG_MENU_REQUEST; | ||
| 1217 | msg->msg[2] = menu_req; | ||
| 1218 | msg->reply = reply ? CEC_MSG_MENU_STATUS : 0; | ||
| 1219 | } | ||
| 1220 | |||
| 1221 | static inline void cec_ops_menu_request(struct cec_msg *msg, | ||
| 1222 | __u8 *menu_req) | ||
| 1223 | { | ||
| 1224 | *menu_req = msg->msg[2]; | ||
| 1225 | } | ||
| 1226 | |||
| 1227 | struct cec_op_ui_command { | ||
| 1228 | __u8 ui_cmd; | ||
| 1229 | bool has_opt_arg; | ||
| 1230 | union { | ||
| 1231 | struct cec_op_channel_data channel_identifier; | ||
| 1232 | __u8 ui_broadcast_type; | ||
| 1233 | __u8 ui_sound_presentation_control; | ||
| 1234 | __u8 play_mode; | ||
| 1235 | __u8 ui_function_media; | ||
| 1236 | __u8 ui_function_select_av_input; | ||
| 1237 | __u8 ui_function_select_audio_input; | ||
| 1238 | }; | ||
| 1239 | }; | ||
| 1240 | |||
| 1241 | static inline void cec_msg_user_control_pressed(struct cec_msg *msg, | ||
| 1242 | const struct cec_op_ui_command *ui_cmd) | ||
| 1243 | { | ||
| 1244 | msg->len = 3; | ||
| 1245 | msg->msg[1] = CEC_MSG_USER_CONTROL_PRESSED; | ||
| 1246 | msg->msg[2] = ui_cmd->ui_cmd; | ||
| 1247 | if (!ui_cmd->has_opt_arg) | ||
| 1248 | return; | ||
| 1249 | switch (ui_cmd->ui_cmd) { | ||
| 1250 | case 0x56: | ||
| 1251 | case 0x57: | ||
| 1252 | case 0x60: | ||
| 1253 | case 0x68: | ||
| 1254 | case 0x69: | ||
| 1255 | case 0x6a: | ||
| 1256 | /* The optional operand is one byte for all these ui commands */ | ||
| 1257 | msg->len++; | ||
| 1258 | msg->msg[3] = ui_cmd->play_mode; | ||
| 1259 | break; | ||
| 1260 | case 0x67: | ||
| 1261 | msg->len += 4; | ||
| 1262 | msg->msg[3] = (ui_cmd->channel_identifier.channel_number_fmt << 2) | | ||
| 1263 | (ui_cmd->channel_identifier.major >> 8); | ||
| 1264 | msg->msg[4] = ui_cmd->channel_identifier.major && 0xff; | ||
| 1265 | msg->msg[5] = ui_cmd->channel_identifier.minor >> 8; | ||
| 1266 | msg->msg[6] = ui_cmd->channel_identifier.minor & 0xff; | ||
| 1267 | break; | ||
| 1268 | } | ||
| 1269 | } | ||
| 1270 | |||
| 1271 | static inline void cec_ops_user_control_pressed(struct cec_msg *msg, | ||
| 1272 | struct cec_op_ui_command *ui_cmd) | ||
| 1273 | { | ||
| 1274 | ui_cmd->ui_cmd = msg->msg[2]; | ||
| 1275 | ui_cmd->has_opt_arg = false; | ||
| 1276 | if (msg->len == 3) | ||
| 1277 | return; | ||
| 1278 | switch (ui_cmd->ui_cmd) { | ||
| 1279 | case 0x56: | ||
| 1280 | case 0x57: | ||
| 1281 | case 0x60: | ||
| 1282 | case 0x68: | ||
| 1283 | case 0x69: | ||
| 1284 | case 0x6a: | ||
| 1285 | /* The optional operand is one byte for all these ui commands */ | ||
| 1286 | ui_cmd->play_mode = msg->msg[3]; | ||
| 1287 | ui_cmd->has_opt_arg = true; | ||
| 1288 | break; | ||
| 1289 | case 0x67: | ||
| 1290 | if (msg->len < 7) | ||
| 1291 | break; | ||
| 1292 | ui_cmd->has_opt_arg = true; | ||
| 1293 | ui_cmd->channel_identifier.channel_number_fmt = msg->msg[3] >> 2; | ||
| 1294 | ui_cmd->channel_identifier.major = ((msg->msg[3] & 3) << 6) | msg->msg[4]; | ||
| 1295 | ui_cmd->channel_identifier.minor = (msg->msg[5] << 8) | msg->msg[6]; | ||
| 1296 | break; | ||
| 1297 | } | ||
| 1298 | } | ||
| 1299 | |||
| 1300 | static inline void cec_msg_user_control_released(struct cec_msg *msg) | ||
| 1301 | { | ||
| 1302 | msg->len = 2; | ||
| 1303 | msg->msg[1] = CEC_MSG_USER_CONTROL_RELEASED; | ||
| 1304 | } | ||
| 1305 | |||
| 1306 | /* Remote Control Passthrough Feature */ | ||
| 1307 | |||
| 1308 | /* Power Status Feature */ | ||
| 1309 | static inline void cec_msg_report_power_status(struct cec_msg *msg, | ||
| 1310 | __u8 pwr_state) | ||
| 1311 | { | ||
| 1312 | msg->len = 3; | ||
| 1313 | msg->msg[1] = CEC_MSG_REPORT_POWER_STATUS; | ||
| 1314 | msg->msg[2] = pwr_state; | ||
| 1315 | } | ||
| 1316 | |||
| 1317 | static inline void cec_ops_report_power_status(const struct cec_msg *msg, | ||
| 1318 | __u8 *pwr_state) | ||
| 1319 | { | ||
| 1320 | *pwr_state = msg->msg[2]; | ||
| 1321 | } | ||
| 1322 | |||
| 1323 | static inline void cec_msg_give_device_power_status(struct cec_msg *msg, | ||
| 1324 | bool reply) | ||
| 1325 | { | ||
| 1326 | msg->len = 2; | ||
| 1327 | msg->msg[1] = CEC_MSG_GIVE_DEVICE_POWER_STATUS; | ||
| 1328 | msg->reply = reply ? CEC_MSG_REPORT_POWER_STATUS : 0; | ||
| 1329 | } | ||
| 1330 | |||
| 1331 | /* General Protocol Messages */ | ||
| 1332 | static inline void cec_msg_feature_abort(struct cec_msg *msg, | ||
| 1333 | __u8 abort_msg, __u8 reason) | ||
| 1334 | { | ||
| 1335 | msg->len = 4; | ||
| 1336 | msg->msg[1] = CEC_MSG_FEATURE_ABORT; | ||
| 1337 | msg->msg[2] = abort_msg; | ||
| 1338 | msg->msg[3] = reason; | ||
| 1339 | } | ||
| 1340 | |||
| 1341 | static inline void cec_ops_feature_abort(const struct cec_msg *msg, | ||
| 1342 | __u8 *abort_msg, __u8 *reason) | ||
| 1343 | { | ||
| 1344 | *abort_msg = msg->msg[2]; | ||
| 1345 | *reason = msg->msg[3]; | ||
| 1346 | } | ||
| 1347 | |||
| 1348 | /* This changes the current message into a feature abort message */ | ||
| 1349 | static inline void cec_msg_reply_feature_abort(struct cec_msg *msg, __u8 reason) | ||
| 1350 | { | ||
| 1351 | cec_msg_set_reply_to(msg, msg); | ||
| 1352 | msg->len = 4; | ||
| 1353 | msg->msg[2] = msg->msg[1]; | ||
| 1354 | msg->msg[3] = reason; | ||
| 1355 | msg->msg[1] = CEC_MSG_FEATURE_ABORT; | ||
| 1356 | } | ||
| 1357 | |||
| 1358 | static inline void cec_msg_abort(struct cec_msg *msg) | ||
| 1359 | { | ||
| 1360 | msg->len = 2; | ||
| 1361 | msg->msg[1] = CEC_MSG_ABORT; | ||
| 1362 | } | ||
| 1363 | |||
| 1364 | |||
| 1365 | /* System Audio Control Feature */ | ||
| 1366 | static inline void cec_msg_report_audio_status(struct cec_msg *msg, | ||
| 1367 | __u8 aud_mute_status, | ||
| 1368 | __u8 aud_vol_status) | ||
| 1369 | { | ||
| 1370 | msg->len = 3; | ||
| 1371 | msg->msg[1] = CEC_MSG_REPORT_AUDIO_STATUS; | ||
| 1372 | msg->msg[2] = (aud_mute_status << 7) | (aud_vol_status & 0x7f); | ||
| 1373 | } | ||
| 1374 | |||
| 1375 | static inline void cec_ops_report_audio_status(const struct cec_msg *msg, | ||
| 1376 | __u8 *aud_mute_status, | ||
| 1377 | __u8 *aud_vol_status) | ||
| 1378 | { | ||
| 1379 | *aud_mute_status = msg->msg[2] >> 7; | ||
| 1380 | *aud_vol_status = msg->msg[2] & 0x7f; | ||
| 1381 | } | ||
| 1382 | |||
| 1383 | static inline void cec_msg_give_audio_status(struct cec_msg *msg, | ||
| 1384 | bool reply) | ||
| 1385 | { | ||
| 1386 | msg->len = 2; | ||
| 1387 | msg->msg[1] = CEC_MSG_GIVE_AUDIO_STATUS; | ||
| 1388 | msg->reply = reply ? CEC_MSG_REPORT_AUDIO_STATUS : 0; | ||
| 1389 | } | ||
| 1390 | |||
| 1391 | static inline void cec_msg_set_system_audio_mode(struct cec_msg *msg, | ||
| 1392 | __u8 sys_aud_status) | ||
| 1393 | { | ||
| 1394 | msg->len = 3; | ||
| 1395 | msg->msg[1] = CEC_MSG_SET_SYSTEM_AUDIO_MODE; | ||
| 1396 | msg->msg[2] = sys_aud_status; | ||
| 1397 | } | ||
| 1398 | |||
| 1399 | static inline void cec_ops_set_system_audio_mode(const struct cec_msg *msg, | ||
| 1400 | __u8 *sys_aud_status) | ||
| 1401 | { | ||
| 1402 | *sys_aud_status = msg->msg[2]; | ||
| 1403 | } | ||
| 1404 | |||
| 1405 | static inline void cec_msg_system_audio_mode_request(struct cec_msg *msg, | ||
| 1406 | bool reply, | ||
| 1407 | __u16 phys_addr) | ||
| 1408 | { | ||
| 1409 | msg->len = phys_addr == 0xffff ? 2 : 4; | ||
| 1410 | msg->msg[1] = CEC_MSG_SYSTEM_AUDIO_MODE_REQUEST; | ||
| 1411 | msg->msg[2] = phys_addr >> 8; | ||
| 1412 | msg->msg[3] = phys_addr & 0xff; | ||
| 1413 | msg->reply = reply ? CEC_MSG_SET_SYSTEM_AUDIO_MODE : 0; | ||
| 1414 | |||
| 1415 | } | ||
| 1416 | |||
| 1417 | static inline void cec_ops_system_audio_mode_request(const struct cec_msg *msg, | ||
| 1418 | __u16 *phys_addr) | ||
| 1419 | { | ||
| 1420 | if (msg->len < 4) | ||
| 1421 | *phys_addr = 0xffff; | ||
| 1422 | else | ||
| 1423 | *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; | ||
| 1424 | } | ||
| 1425 | |||
| 1426 | static inline void cec_msg_system_audio_mode_status(struct cec_msg *msg, | ||
| 1427 | __u8 sys_aud_status) | ||
| 1428 | { | ||
| 1429 | msg->len = 3; | ||
| 1430 | msg->msg[1] = CEC_MSG_SYSTEM_AUDIO_MODE_STATUS; | ||
| 1431 | msg->msg[2] = sys_aud_status; | ||
| 1432 | } | ||
| 1433 | |||
| 1434 | static inline void cec_ops_system_audio_mode_status(const struct cec_msg *msg, | ||
| 1435 | __u8 *sys_aud_status) | ||
| 1436 | { | ||
| 1437 | *sys_aud_status = msg->msg[2]; | ||
| 1438 | } | ||
| 1439 | |||
| 1440 | static inline void cec_msg_give_system_audio_mode_status(struct cec_msg *msg, | ||
| 1441 | bool reply) | ||
| 1442 | { | ||
| 1443 | msg->len = 2; | ||
| 1444 | msg->msg[1] = CEC_MSG_GIVE_SYSTEM_AUDIO_MODE_STATUS; | ||
| 1445 | msg->reply = reply ? CEC_MSG_SYSTEM_AUDIO_MODE_STATUS : 0; | ||
| 1446 | } | ||
| 1447 | |||
| 1448 | static inline void cec_msg_report_short_audio_descriptor(struct cec_msg *msg, | ||
| 1449 | __u8 num_descriptors, | ||
| 1450 | const __u32 *descriptors) | ||
| 1451 | { | ||
| 1452 | unsigned int i; | ||
| 1453 | |||
| 1454 | if (num_descriptors > 4) | ||
| 1455 | num_descriptors = 4; | ||
| 1456 | msg->len = 2 + num_descriptors * 3; | ||
| 1457 | msg->msg[1] = CEC_MSG_REPORT_SHORT_AUDIO_DESCRIPTOR; | ||
| 1458 | for (i = 0; i < num_descriptors; i++) { | ||
| 1459 | msg->msg[2 + i * 3] = (descriptors[i] >> 16) & 0xff; | ||
| 1460 | msg->msg[3 + i * 3] = (descriptors[i] >> 8) & 0xff; | ||
| 1461 | msg->msg[4 + i * 3] = descriptors[i] & 0xff; | ||
| 1462 | } | ||
| 1463 | } | ||
| 1464 | |||
| 1465 | static inline void cec_ops_report_short_audio_descriptor(const struct cec_msg *msg, | ||
| 1466 | __u8 *num_descriptors, | ||
| 1467 | __u32 *descriptors) | ||
| 1468 | { | ||
| 1469 | unsigned int i; | ||
| 1470 | |||
| 1471 | *num_descriptors = (msg->len - 2) / 3; | ||
| 1472 | if (*num_descriptors > 4) | ||
| 1473 | *num_descriptors = 4; | ||
| 1474 | for (i = 0; i < *num_descriptors; i++) | ||
| 1475 | descriptors[i] = (msg->msg[2 + i * 3] << 16) | | ||
| 1476 | (msg->msg[3 + i * 3] << 8) | | ||
| 1477 | msg->msg[4 + i * 3]; | ||
| 1478 | } | ||
| 1479 | |||
| 1480 | static inline void cec_msg_request_short_audio_descriptor(struct cec_msg *msg, | ||
| 1481 | __u8 num_descriptors, | ||
| 1482 | const __u8 *audio_format_id, | ||
| 1483 | const __u8 *audio_format_code) | ||
| 1484 | { | ||
| 1485 | unsigned int i; | ||
| 1486 | |||
| 1487 | if (num_descriptors > 4) | ||
| 1488 | num_descriptors = 4; | ||
| 1489 | msg->len = 2 + num_descriptors; | ||
| 1490 | msg->msg[1] = CEC_MSG_REQUEST_SHORT_AUDIO_DESCRIPTOR; | ||
| 1491 | for (i = 0; i < num_descriptors; i++) | ||
| 1492 | msg->msg[2 + i] = (audio_format_id[i] << 6) | | ||
| 1493 | (audio_format_code[i] & 0x3f); | ||
| 1494 | } | ||
| 1495 | |||
| 1496 | static inline void cec_ops_request_short_audio_descriptor(const struct cec_msg *msg, | ||
| 1497 | __u8 *num_descriptors, | ||
| 1498 | __u8 *audio_format_id, | ||
| 1499 | __u8 *audio_format_code) | ||
| 1500 | { | ||
| 1501 | unsigned int i; | ||
| 1502 | |||
| 1503 | *num_descriptors = msg->len - 2; | ||
| 1504 | if (*num_descriptors > 4) | ||
| 1505 | *num_descriptors = 4; | ||
| 1506 | for (i = 0; i < *num_descriptors; i++) { | ||
| 1507 | audio_format_id[i] = msg->msg[2 + i] >> 6; | ||
| 1508 | audio_format_code[i] = msg->msg[2 + i] & 0x3f; | ||
| 1509 | } | ||
| 1510 | } | ||
| 1511 | |||
| 1512 | |||
| 1513 | /* Audio Rate Control Feature */ | ||
| 1514 | static inline void cec_msg_set_audio_rate(struct cec_msg *msg, | ||
| 1515 | __u8 audio_rate) | ||
| 1516 | { | ||
| 1517 | msg->len = 3; | ||
| 1518 | msg->msg[1] = CEC_MSG_SET_AUDIO_RATE; | ||
| 1519 | msg->msg[2] = audio_rate; | ||
| 1520 | } | ||
| 1521 | |||
| 1522 | static inline void cec_ops_set_audio_rate(const struct cec_msg *msg, | ||
| 1523 | __u8 *audio_rate) | ||
| 1524 | { | ||
| 1525 | *audio_rate = msg->msg[2]; | ||
| 1526 | } | ||
| 1527 | |||
| 1528 | |||
| 1529 | /* Audio Return Channel Control Feature */ | ||
| 1530 | static inline void cec_msg_report_arc_initiated(struct cec_msg *msg) | ||
| 1531 | { | ||
| 1532 | msg->len = 2; | ||
| 1533 | msg->msg[1] = CEC_MSG_REPORT_ARC_INITIATED; | ||
| 1534 | } | ||
| 1535 | |||
| 1536 | static inline void cec_msg_initiate_arc(struct cec_msg *msg, | ||
| 1537 | bool reply) | ||
| 1538 | { | ||
| 1539 | msg->len = 2; | ||
| 1540 | msg->msg[1] = CEC_MSG_INITIATE_ARC; | ||
| 1541 | msg->reply = reply ? CEC_MSG_REPORT_ARC_INITIATED : 0; | ||
| 1542 | } | ||
| 1543 | |||
| 1544 | static inline void cec_msg_request_arc_initiation(struct cec_msg *msg, | ||
| 1545 | bool reply) | ||
| 1546 | { | ||
| 1547 | msg->len = 2; | ||
| 1548 | msg->msg[1] = CEC_MSG_REQUEST_ARC_INITIATION; | ||
| 1549 | msg->reply = reply ? CEC_MSG_INITIATE_ARC : 0; | ||
| 1550 | } | ||
| 1551 | |||
| 1552 | static inline void cec_msg_report_arc_terminated(struct cec_msg *msg) | ||
| 1553 | { | ||
| 1554 | msg->len = 2; | ||
| 1555 | msg->msg[1] = CEC_MSG_REPORT_ARC_TERMINATED; | ||
| 1556 | } | ||
| 1557 | |||
| 1558 | static inline void cec_msg_terminate_arc(struct cec_msg *msg, | ||
| 1559 | bool reply) | ||
| 1560 | { | ||
| 1561 | msg->len = 2; | ||
| 1562 | msg->msg[1] = CEC_MSG_TERMINATE_ARC; | ||
| 1563 | msg->reply = reply ? CEC_MSG_REPORT_ARC_TERMINATED : 0; | ||
| 1564 | } | ||
| 1565 | |||
| 1566 | static inline void cec_msg_request_arc_termination(struct cec_msg *msg, | ||
| 1567 | bool reply) | ||
| 1568 | { | ||
| 1569 | msg->len = 2; | ||
| 1570 | msg->msg[1] = CEC_MSG_REQUEST_ARC_TERMINATION; | ||
| 1571 | msg->reply = reply ? CEC_MSG_TERMINATE_ARC : 0; | ||
| 1572 | } | ||
| 1573 | |||
| 1574 | |||
| 1575 | /* Dynamic Audio Lipsync Feature */ | ||
| 1576 | /* Only for CEC 2.0 and up */ | ||
| 1577 | static inline void cec_msg_report_current_latency(struct cec_msg *msg, | ||
| 1578 | __u16 phys_addr, | ||
| 1579 | __u8 video_latency, | ||
| 1580 | __u8 low_latency_mode, | ||
| 1581 | __u8 audio_out_compensated, | ||
| 1582 | __u8 audio_out_delay) | ||
| 1583 | { | ||
| 1584 | msg->len = 7; | ||
| 1585 | msg->msg[0] |= 0xf; /* broadcast */ | ||
| 1586 | msg->msg[1] = CEC_MSG_REPORT_CURRENT_LATENCY; | ||
| 1587 | msg->msg[2] = phys_addr >> 8; | ||
| 1588 | msg->msg[3] = phys_addr & 0xff; | ||
| 1589 | msg->msg[4] = video_latency; | ||
| 1590 | msg->msg[5] = (low_latency_mode << 2) | audio_out_compensated; | ||
| 1591 | msg->msg[6] = audio_out_delay; | ||
| 1592 | } | ||
| 1593 | |||
| 1594 | static inline void cec_ops_report_current_latency(const struct cec_msg *msg, | ||
| 1595 | __u16 *phys_addr, | ||
| 1596 | __u8 *video_latency, | ||
| 1597 | __u8 *low_latency_mode, | ||
| 1598 | __u8 *audio_out_compensated, | ||
| 1599 | __u8 *audio_out_delay) | ||
| 1600 | { | ||
| 1601 | *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; | ||
| 1602 | *video_latency = msg->msg[4]; | ||
| 1603 | *low_latency_mode = (msg->msg[5] >> 2) & 1; | ||
| 1604 | *audio_out_compensated = msg->msg[5] & 3; | ||
| 1605 | *audio_out_delay = msg->msg[6]; | ||
| 1606 | } | ||
| 1607 | |||
| 1608 | static inline void cec_msg_request_current_latency(struct cec_msg *msg, | ||
| 1609 | bool reply, | ||
| 1610 | __u16 phys_addr) | ||
| 1611 | { | ||
| 1612 | msg->len = 4; | ||
| 1613 | msg->msg[0] |= 0xf; /* broadcast */ | ||
| 1614 | msg->msg[1] = CEC_MSG_REQUEST_CURRENT_LATENCY; | ||
| 1615 | msg->msg[2] = phys_addr >> 8; | ||
| 1616 | msg->msg[3] = phys_addr & 0xff; | ||
| 1617 | msg->reply = reply ? CEC_MSG_REPORT_CURRENT_LATENCY : 0; | ||
| 1618 | } | ||
| 1619 | |||
| 1620 | static inline void cec_ops_request_current_latency(const struct cec_msg *msg, | ||
| 1621 | __u16 *phys_addr) | ||
| 1622 | { | ||
| 1623 | *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; | ||
| 1624 | } | ||
| 1625 | |||
| 1626 | |||
| 1627 | /* Capability Discovery and Control Feature */ | ||
| 1628 | static inline void cec_msg_cdc_hec_inquire_state(struct cec_msg *msg, | ||
| 1629 | __u16 phys_addr1, | ||
| 1630 | __u16 phys_addr2) | ||
| 1631 | { | ||
| 1632 | msg->len = 9; | ||
| 1633 | msg->msg[0] |= 0xf; /* broadcast */ | ||
| 1634 | msg->msg[1] = CEC_MSG_CDC_MESSAGE; | ||
| 1635 | /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */ | ||
| 1636 | msg->msg[4] = CEC_MSG_CDC_HEC_INQUIRE_STATE; | ||
| 1637 | msg->msg[5] = phys_addr1 >> 8; | ||
| 1638 | msg->msg[6] = phys_addr1 & 0xff; | ||
| 1639 | msg->msg[7] = phys_addr2 >> 8; | ||
| 1640 | msg->msg[8] = phys_addr2 & 0xff; | ||
| 1641 | } | ||
| 1642 | |||
| 1643 | static inline void cec_ops_cdc_hec_inquire_state(const struct cec_msg *msg, | ||
| 1644 | __u16 *phys_addr, | ||
| 1645 | __u16 *phys_addr1, | ||
| 1646 | __u16 *phys_addr2) | ||
| 1647 | { | ||
| 1648 | *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; | ||
| 1649 | *phys_addr1 = (msg->msg[5] << 8) | msg->msg[6]; | ||
| 1650 | *phys_addr2 = (msg->msg[7] << 8) | msg->msg[8]; | ||
| 1651 | } | ||
| 1652 | |||
| 1653 | static inline void cec_msg_cdc_hec_report_state(struct cec_msg *msg, | ||
| 1654 | __u16 target_phys_addr, | ||
| 1655 | __u8 hec_func_state, | ||
| 1656 | __u8 host_func_state, | ||
| 1657 | __u8 enc_func_state, | ||
| 1658 | __u8 cdc_errcode, | ||
| 1659 | __u8 has_field, | ||
| 1660 | __u16 hec_field) | ||
| 1661 | { | ||
| 1662 | msg->len = has_field ? 10 : 8; | ||
| 1663 | msg->msg[0] |= 0xf; /* broadcast */ | ||
| 1664 | msg->msg[1] = CEC_MSG_CDC_MESSAGE; | ||
| 1665 | /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */ | ||
| 1666 | msg->msg[4] = CEC_MSG_CDC_HEC_REPORT_STATE; | ||
| 1667 | msg->msg[5] = target_phys_addr >> 8; | ||
| 1668 | msg->msg[6] = target_phys_addr & 0xff; | ||
| 1669 | msg->msg[7] = (hec_func_state << 6) | | ||
| 1670 | (host_func_state << 4) | | ||
| 1671 | (enc_func_state << 2) | | ||
| 1672 | cdc_errcode; | ||
| 1673 | if (has_field) { | ||
| 1674 | msg->msg[8] = hec_field >> 8; | ||
| 1675 | msg->msg[9] = hec_field & 0xff; | ||
| 1676 | } | ||
| 1677 | } | ||
| 1678 | |||
| 1679 | static inline void cec_ops_cdc_hec_report_state(const struct cec_msg *msg, | ||
| 1680 | __u16 *phys_addr, | ||
| 1681 | __u16 *target_phys_addr, | ||
| 1682 | __u8 *hec_func_state, | ||
| 1683 | __u8 *host_func_state, | ||
| 1684 | __u8 *enc_func_state, | ||
| 1685 | __u8 *cdc_errcode, | ||
| 1686 | __u8 *has_field, | ||
| 1687 | __u16 *hec_field) | ||
| 1688 | { | ||
| 1689 | *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; | ||
| 1690 | *target_phys_addr = (msg->msg[5] << 8) | msg->msg[6]; | ||
| 1691 | *hec_func_state = msg->msg[7] >> 6; | ||
| 1692 | *host_func_state = (msg->msg[7] >> 4) & 3; | ||
| 1693 | *enc_func_state = (msg->msg[7] >> 4) & 3; | ||
| 1694 | *cdc_errcode = msg->msg[7] & 3; | ||
| 1695 | *has_field = msg->len >= 10; | ||
| 1696 | *hec_field = *has_field ? ((msg->msg[8] << 8) | msg->msg[9]) : 0; | ||
| 1697 | } | ||
| 1698 | |||
| 1699 | static inline void cec_msg_cdc_hec_set_state(struct cec_msg *msg, | ||
| 1700 | __u16 phys_addr1, | ||
| 1701 | __u16 phys_addr2, | ||
| 1702 | __u8 hec_set_state, | ||
| 1703 | __u16 phys_addr3, | ||
| 1704 | __u16 phys_addr4, | ||
| 1705 | __u16 phys_addr5) | ||
| 1706 | { | ||
| 1707 | msg->len = 10; | ||
| 1708 | msg->msg[0] |= 0xf; /* broadcast */ | ||
| 1709 | msg->msg[1] = CEC_MSG_CDC_MESSAGE; | ||
| 1710 | /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */ | ||
| 1711 | msg->msg[4] = CEC_MSG_CDC_HEC_INQUIRE_STATE; | ||
| 1712 | msg->msg[5] = phys_addr1 >> 8; | ||
| 1713 | msg->msg[6] = phys_addr1 & 0xff; | ||
| 1714 | msg->msg[7] = phys_addr2 >> 8; | ||
| 1715 | msg->msg[8] = phys_addr2 & 0xff; | ||
| 1716 | msg->msg[9] = hec_set_state; | ||
| 1717 | if (phys_addr3 != CEC_PHYS_ADDR_INVALID) { | ||
| 1718 | msg->msg[msg->len++] = phys_addr3 >> 8; | ||
| 1719 | msg->msg[msg->len++] = phys_addr3 & 0xff; | ||
| 1720 | if (phys_addr4 != CEC_PHYS_ADDR_INVALID) { | ||
| 1721 | msg->msg[msg->len++] = phys_addr4 >> 8; | ||
| 1722 | msg->msg[msg->len++] = phys_addr4 & 0xff; | ||
| 1723 | if (phys_addr5 != CEC_PHYS_ADDR_INVALID) { | ||
| 1724 | msg->msg[msg->len++] = phys_addr5 >> 8; | ||
| 1725 | msg->msg[msg->len++] = phys_addr5 & 0xff; | ||
| 1726 | } | ||
| 1727 | } | ||
| 1728 | } | ||
| 1729 | } | ||
| 1730 | |||
| 1731 | static inline void cec_ops_cdc_hec_set_state(const struct cec_msg *msg, | ||
| 1732 | __u16 *phys_addr, | ||
| 1733 | __u16 *phys_addr1, | ||
| 1734 | __u16 *phys_addr2, | ||
| 1735 | __u8 *hec_set_state, | ||
| 1736 | __u16 *phys_addr3, | ||
| 1737 | __u16 *phys_addr4, | ||
| 1738 | __u16 *phys_addr5) | ||
| 1739 | { | ||
| 1740 | *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; | ||
| 1741 | *phys_addr1 = (msg->msg[5] << 8) | msg->msg[6]; | ||
| 1742 | *phys_addr2 = (msg->msg[7] << 8) | msg->msg[8]; | ||
| 1743 | *hec_set_state = msg->msg[9]; | ||
| 1744 | *phys_addr3 = *phys_addr4 = *phys_addr5 = CEC_PHYS_ADDR_INVALID; | ||
| 1745 | if (msg->len >= 12) | ||
| 1746 | *phys_addr3 = (msg->msg[10] << 8) | msg->msg[11]; | ||
| 1747 | if (msg->len >= 14) | ||
| 1748 | *phys_addr4 = (msg->msg[12] << 8) | msg->msg[13]; | ||
| 1749 | if (msg->len >= 16) | ||
| 1750 | *phys_addr5 = (msg->msg[14] << 8) | msg->msg[15]; | ||
| 1751 | } | ||
| 1752 | |||
| 1753 | static inline void cec_msg_cdc_hec_set_state_adjacent(struct cec_msg *msg, | ||
| 1754 | __u16 phys_addr1, | ||
| 1755 | __u8 hec_set_state) | ||
| 1756 | { | ||
| 1757 | msg->len = 8; | ||
| 1758 | msg->msg[0] |= 0xf; /* broadcast */ | ||
| 1759 | msg->msg[1] = CEC_MSG_CDC_MESSAGE; | ||
| 1760 | /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */ | ||
| 1761 | msg->msg[4] = CEC_MSG_CDC_HEC_SET_STATE_ADJACENT; | ||
| 1762 | msg->msg[5] = phys_addr1 >> 8; | ||
| 1763 | msg->msg[6] = phys_addr1 & 0xff; | ||
| 1764 | msg->msg[7] = hec_set_state; | ||
| 1765 | } | ||
| 1766 | |||
| 1767 | static inline void cec_ops_cdc_hec_set_state_adjacent(const struct cec_msg *msg, | ||
| 1768 | __u16 *phys_addr, | ||
| 1769 | __u16 *phys_addr1, | ||
| 1770 | __u8 *hec_set_state) | ||
| 1771 | { | ||
| 1772 | *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; | ||
| 1773 | *phys_addr1 = (msg->msg[5] << 8) | msg->msg[6]; | ||
| 1774 | *hec_set_state = msg->msg[7]; | ||
| 1775 | } | ||
| 1776 | |||
| 1777 | static inline void cec_msg_cdc_hec_request_deactivation(struct cec_msg *msg, | ||
| 1778 | __u16 phys_addr1, | ||
| 1779 | __u16 phys_addr2, | ||
| 1780 | __u16 phys_addr3) | ||
| 1781 | { | ||
| 1782 | msg->len = 11; | ||
| 1783 | msg->msg[0] |= 0xf; /* broadcast */ | ||
| 1784 | msg->msg[1] = CEC_MSG_CDC_MESSAGE; | ||
| 1785 | /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */ | ||
| 1786 | msg->msg[4] = CEC_MSG_CDC_HEC_REQUEST_DEACTIVATION; | ||
| 1787 | msg->msg[5] = phys_addr1 >> 8; | ||
| 1788 | msg->msg[6] = phys_addr1 & 0xff; | ||
| 1789 | msg->msg[7] = phys_addr2 >> 8; | ||
| 1790 | msg->msg[8] = phys_addr2 & 0xff; | ||
| 1791 | msg->msg[9] = phys_addr3 >> 8; | ||
| 1792 | msg->msg[10] = phys_addr3 & 0xff; | ||
| 1793 | } | ||
| 1794 | |||
| 1795 | static inline void cec_ops_cdc_hec_request_deactivation(const struct cec_msg *msg, | ||
| 1796 | __u16 *phys_addr, | ||
| 1797 | __u16 *phys_addr1, | ||
| 1798 | __u16 *phys_addr2, | ||
| 1799 | __u16 *phys_addr3) | ||
| 1800 | { | ||
| 1801 | *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; | ||
| 1802 | *phys_addr1 = (msg->msg[5] << 8) | msg->msg[6]; | ||
| 1803 | *phys_addr2 = (msg->msg[7] << 8) | msg->msg[8]; | ||
| 1804 | *phys_addr3 = (msg->msg[9] << 8) | msg->msg[10]; | ||
| 1805 | } | ||
| 1806 | |||
| 1807 | static inline void cec_msg_cdc_hec_notify_alive(struct cec_msg *msg) | ||
| 1808 | { | ||
| 1809 | msg->len = 5; | ||
| 1810 | msg->msg[0] |= 0xf; /* broadcast */ | ||
| 1811 | msg->msg[1] = CEC_MSG_CDC_MESSAGE; | ||
| 1812 | /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */ | ||
| 1813 | msg->msg[4] = CEC_MSG_CDC_HEC_NOTIFY_ALIVE; | ||
| 1814 | } | ||
| 1815 | |||
| 1816 | static inline void cec_ops_cdc_hec_notify_alive(const struct cec_msg *msg, | ||
| 1817 | __u16 *phys_addr) | ||
| 1818 | { | ||
| 1819 | *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; | ||
| 1820 | } | ||
| 1821 | |||
| 1822 | static inline void cec_msg_cdc_hec_discover(struct cec_msg *msg) | ||
| 1823 | { | ||
| 1824 | msg->len = 5; | ||
| 1825 | msg->msg[0] |= 0xf; /* broadcast */ | ||
| 1826 | msg->msg[1] = CEC_MSG_CDC_MESSAGE; | ||
| 1827 | /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */ | ||
| 1828 | msg->msg[4] = CEC_MSG_CDC_HEC_DISCOVER; | ||
| 1829 | } | ||
| 1830 | |||
| 1831 | static inline void cec_ops_cdc_hec_discover(const struct cec_msg *msg, | ||
| 1832 | __u16 *phys_addr) | ||
| 1833 | { | ||
| 1834 | *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; | ||
| 1835 | } | ||
| 1836 | |||
| 1837 | static inline void cec_msg_cdc_hpd_set_state(struct cec_msg *msg, | ||
| 1838 | __u8 input_port, | ||
| 1839 | __u8 hpd_state) | ||
| 1840 | { | ||
| 1841 | msg->len = 6; | ||
| 1842 | msg->msg[0] |= 0xf; /* broadcast */ | ||
| 1843 | msg->msg[1] = CEC_MSG_CDC_MESSAGE; | ||
| 1844 | /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */ | ||
| 1845 | msg->msg[4] = CEC_MSG_CDC_HPD_SET_STATE; | ||
| 1846 | msg->msg[5] = (input_port << 4) | hpd_state; | ||
| 1847 | } | ||
| 1848 | |||
| 1849 | static inline void cec_ops_cdc_hpd_set_state(const struct cec_msg *msg, | ||
| 1850 | __u16 *phys_addr, | ||
| 1851 | __u8 *input_port, | ||
| 1852 | __u8 *hpd_state) | ||
| 1853 | { | ||
| 1854 | *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; | ||
| 1855 | *input_port = msg->msg[5] >> 4; | ||
| 1856 | *hpd_state = msg->msg[5] & 0xf; | ||
| 1857 | } | ||
| 1858 | |||
| 1859 | static inline void cec_msg_cdc_hpd_report_state(struct cec_msg *msg, | ||
| 1860 | __u8 hpd_state, | ||
| 1861 | __u8 hpd_error) | ||
| 1862 | { | ||
| 1863 | msg->len = 6; | ||
| 1864 | msg->msg[0] |= 0xf; /* broadcast */ | ||
| 1865 | msg->msg[1] = CEC_MSG_CDC_MESSAGE; | ||
| 1866 | /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */ | ||
| 1867 | msg->msg[4] = CEC_MSG_CDC_HPD_REPORT_STATE; | ||
| 1868 | msg->msg[5] = (hpd_state << 4) | hpd_error; | ||
| 1869 | } | ||
| 1870 | |||
| 1871 | static inline void cec_ops_cdc_hpd_report_state(const struct cec_msg *msg, | ||
| 1872 | __u16 *phys_addr, | ||
| 1873 | __u8 *hpd_state, | ||
| 1874 | __u8 *hpd_error) | ||
| 1875 | { | ||
| 1876 | *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; | ||
| 1877 | *hpd_state = msg->msg[5] >> 4; | ||
| 1878 | *hpd_error = msg->msg[5] & 0xf; | ||
| 1879 | } | ||
| 1880 | |||
| 1881 | #endif | ||
| @@ -0,0 +1,993 @@ | |||
| 1 | /* | ||
| 2 | * cec - HDMI Consumer Electronics Control public header | ||
| 3 | * | ||
| 4 | * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved. | ||
| 5 | * | ||
| 6 | * This program is free software; you may redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; version 2 of the License. | ||
| 9 | * | ||
| 10 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
| 11 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
| 12 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
| 13 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||
| 14 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
| 15 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
| 16 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| 17 | * SOFTWARE. | ||
| 18 | */ | ||
| 19 | |||
| 20 | /* | ||
| 21 | * Note: this framework is still in staging and it is likely the API | ||
| 22 | * will change before it goes out of staging. | ||
| 23 | * | ||
| 24 | * Once it is moved out of staging this header will move to uapi. | ||
| 25 | */ | ||
| 26 | #ifndef _CEC_UAPI_H | ||
| 27 | #define _CEC_UAPI_H | ||
| 28 | |||
| 29 | #include <linux/types.h> | ||
| 30 | |||
| 31 | #define CEC_MAX_MSG_SIZE 16 | ||
| 32 | |||
| 33 | /** | ||
| 34 | * struct cec_msg - CEC message structure. | ||
| 35 | * @ts: Timestamp in nanoseconds using CLOCK_MONOTONIC. Set by the | ||
| 36 | * driver. It is set when the message transmission has finished | ||
| 37 | * and it is set when a message was received. | ||
| 38 | * @len: Length in bytes of the message. | ||
| 39 | * @timeout: The timeout (in ms) that is used to timeout CEC_RECEIVE. | ||
| 40 | * Set to 0 if you want to wait forever. This timeout can also be | ||
| 41 | * used with CEC_TRANSMIT as the timeout for waiting for a reply. | ||
| 42 | * If 0, then it will use a 1 second timeout instead of waiting | ||
| 43 | * forever as is done with CEC_RECEIVE. | ||
| 44 | * @sequence: The framework assigns a sequence number to messages that are | ||
| 45 | * sent. This can be used to track replies to previously sent | ||
| 46 | * messages. | ||
| 47 | * @flags: Set to 0. | ||
| 48 | * @rx_status: The message receive status bits. Set by the driver. | ||
| 49 | * @tx_status: The message transmit status bits. Set by the driver. | ||
| 50 | * @msg: The message payload. | ||
| 51 | * @reply: This field is ignored with CEC_RECEIVE and is only used by | ||
| 52 | * CEC_TRANSMIT. If non-zero, then wait for a reply with this | ||
| 53 | * opcode. Set to CEC_MSG_FEATURE_ABORT if you want to wait for | ||
| 54 | * a possible ABORT reply. If there was an error when sending the | ||
| 55 | * msg or FeatureAbort was returned, then reply is set to 0. | ||
| 56 | * If reply is non-zero upon return, then len/msg are set to | ||
| 57 | * the received message. | ||
| 58 | * If reply is zero upon return and status has the | ||
| 59 | * CEC_TX_STATUS_FEATURE_ABORT bit set, then len/msg are set to | ||
| 60 | * the received feature abort message. | ||
| 61 | * If reply is zero upon return and status has the | ||
| 62 | * CEC_TX_STATUS_MAX_RETRIES bit set, then no reply was seen at | ||
| 63 | * all. If reply is non-zero for CEC_TRANSMIT and the message is a | ||
| 64 | * broadcast, then -EINVAL is returned. | ||
| 65 | * if reply is non-zero, then timeout is set to 1000 (the required | ||
| 66 | * maximum response time). | ||
| 67 | * @tx_arb_lost_cnt: The number of 'Arbitration Lost' events. Set by the driver. | ||
| 68 | * @tx_nack_cnt: The number of 'Not Acknowledged' events. Set by the driver. | ||
| 69 | * @tx_low_drive_cnt: The number of 'Low Drive Detected' events. Set by the | ||
| 70 | * driver. | ||
| 71 | * @tx_error_cnt: The number of 'Error' events. Set by the driver. | ||
| 72 | */ | ||
| 73 | struct cec_msg { | ||
| 74 | __u64 ts; | ||
| 75 | __u32 len; | ||
| 76 | __u32 timeout; | ||
| 77 | __u32 sequence; | ||
| 78 | __u32 flags; | ||
| 79 | __u8 rx_status; | ||
| 80 | __u8 tx_status; | ||
| 81 | __u8 msg[CEC_MAX_MSG_SIZE]; | ||
| 82 | __u8 reply; | ||
| 83 | __u8 tx_arb_lost_cnt; | ||
| 84 | __u8 tx_nack_cnt; | ||
| 85 | __u8 tx_low_drive_cnt; | ||
| 86 | __u8 tx_error_cnt; | ||
| 87 | }; | ||
| 88 | |||
| 89 | /** | ||
| 90 | * cec_msg_initiator - return the initiator's logical address. | ||
| 91 | * @msg: the message structure | ||
| 92 | */ | ||
| 93 | static inline __u8 cec_msg_initiator(const struct cec_msg *msg) | ||
| 94 | { | ||
| 95 | return msg->msg[0] >> 4; | ||
| 96 | } | ||
| 97 | |||
| 98 | /** | ||
| 99 | * cec_msg_destination - return the destination's logical address. | ||
| 100 | * @msg: the message structure | ||
| 101 | */ | ||
| 102 | static inline __u8 cec_msg_destination(const struct cec_msg *msg) | ||
| 103 | { | ||
| 104 | return msg->msg[0] & 0xf; | ||
| 105 | } | ||
| 106 | |||
| 107 | /** | ||
| 108 | * cec_msg_opcode - return the opcode of the message, -1 for poll | ||
| 109 | * @msg: the message structure | ||
| 110 | */ | ||
| 111 | static inline int cec_msg_opcode(const struct cec_msg *msg) | ||
| 112 | { | ||
| 113 | return msg->len > 1 ? msg->msg[1] : -1; | ||
| 114 | } | ||
| 115 | |||
| 116 | /** | ||
| 117 | * cec_msg_is_broadcast - return true if this is a broadcast message. | ||
| 118 | * @msg: the message structure | ||
| 119 | */ | ||
| 120 | static inline bool cec_msg_is_broadcast(const struct cec_msg *msg) | ||
| 121 | { | ||
| 122 | return (msg->msg[0] & 0xf) == 0xf; | ||
| 123 | } | ||
| 124 | |||
| 125 | /** | ||
| 126 | * cec_msg_init - initialize the message structure. | ||
| 127 | * @msg: the message structure | ||
| 128 | * @initiator: the logical address of the initiator | ||
| 129 | * @destination:the logical address of the destination (0xf for broadcast) | ||
| 130 | * | ||
| 131 | * The whole structure is zeroed, the len field is set to 1 (i.e. a poll | ||
| 132 | * message) and the initiator and destination are filled in. | ||
| 133 | */ | ||
| 134 | static inline void cec_msg_init(struct cec_msg *msg, | ||
| 135 | __u8 initiator, __u8 destination) | ||
| 136 | { | ||
| 137 | memset(msg, 0, sizeof(*msg)); | ||
| 138 | msg->msg[0] = (initiator << 4) | destination; | ||
| 139 | msg->len = 1; | ||
| 140 | } | ||
| 141 | |||
| 142 | /** | ||
| 143 | * cec_msg_set_reply_to - fill in destination/initiator in a reply message. | ||
| 144 | * @msg: the message structure for the reply | ||
| 145 | * @orig: the original message structure | ||
| 146 | * | ||
| 147 | * Set the msg destination to the orig initiator and the msg initiator to the | ||
| 148 | * orig destination. Note that msg and orig may be the same pointer, in which | ||
| 149 | * case the change is done in place. | ||
| 150 | */ | ||
| 151 | static inline void cec_msg_set_reply_to(struct cec_msg *msg, | ||
| 152 | struct cec_msg *orig) | ||
| 153 | { | ||
| 154 | /* The destination becomes the initiator and vice versa */ | ||
| 155 | msg->msg[0] = (cec_msg_destination(orig) << 4) | | ||
| 156 | cec_msg_initiator(orig); | ||
| 157 | msg->reply = msg->timeout = 0; | ||
| 158 | } | ||
| 159 | |||
| 160 | /* cec status field */ | ||
| 161 | #define CEC_TX_STATUS_OK (1 << 0) | ||
| 162 | #define CEC_TX_STATUS_ARB_LOST (1 << 1) | ||
| 163 | #define CEC_TX_STATUS_NACK (1 << 2) | ||
| 164 | #define CEC_TX_STATUS_LOW_DRIVE (1 << 3) | ||
| 165 | #define CEC_TX_STATUS_ERROR (1 << 4) | ||
| 166 | #define CEC_TX_STATUS_MAX_RETRIES (1 << 5) | ||
| 167 | |||
| 168 | #define CEC_RX_STATUS_OK (1 << 0) | ||
| 169 | #define CEC_RX_STATUS_TIMEOUT (1 << 1) | ||
| 170 | #define CEC_RX_STATUS_FEATURE_ABORT (1 << 2) | ||
| 171 | |||
| 172 | static inline bool cec_msg_status_is_ok(const struct cec_msg *msg) | ||
| 173 | { | ||
| 174 | if (msg->tx_status && !(msg->tx_status & CEC_TX_STATUS_OK)) | ||
| 175 | return false; | ||
| 176 | if (msg->rx_status && !(msg->rx_status & CEC_RX_STATUS_OK)) | ||
| 177 | return false; | ||
| 178 | if (!msg->tx_status && !msg->rx_status) | ||
| 179 | return false; | ||
| 180 | return !(msg->rx_status & CEC_RX_STATUS_FEATURE_ABORT); | ||
| 181 | } | ||
| 182 | |||
| 183 | #define CEC_LOG_ADDR_INVALID 0xff | ||
| 184 | #define CEC_PHYS_ADDR_INVALID 0xffff | ||
| 185 | |||
| 186 | /* | ||
| 187 | * The maximum number of logical addresses one device can be assigned to. | ||
| 188 | * The CEC 2.0 spec allows for only 2 logical addresses at the moment. The | ||
| 189 | * Analog Devices CEC hardware supports 3. So let's go wild and go for 4. | ||
| 190 | */ | ||
| 191 | #define CEC_MAX_LOG_ADDRS 4 | ||
| 192 | |||
| 193 | /* The logical addresses defined by CEC 2.0 */ | ||
| 194 | #define CEC_LOG_ADDR_TV 0 | ||
| 195 | #define CEC_LOG_ADDR_RECORD_1 1 | ||
| 196 | #define CEC_LOG_ADDR_RECORD_2 2 | ||
| 197 | #define CEC_LOG_ADDR_TUNER_1 3 | ||
| 198 | #define CEC_LOG_ADDR_PLAYBACK_1 4 | ||
| 199 | #define CEC_LOG_ADDR_AUDIOSYSTEM 5 | ||
| 200 | #define CEC_LOG_ADDR_TUNER_2 6 | ||
| 201 | #define CEC_LOG_ADDR_TUNER_3 7 | ||
| 202 | #define CEC_LOG_ADDR_PLAYBACK_2 8 | ||
| 203 | #define CEC_LOG_ADDR_RECORD_3 9 | ||
| 204 | #define CEC_LOG_ADDR_TUNER_4 10 | ||
| 205 | #define CEC_LOG_ADDR_PLAYBACK_3 11 | ||
| 206 | #define CEC_LOG_ADDR_BACKUP_1 12 | ||
| 207 | #define CEC_LOG_ADDR_BACKUP_2 13 | ||
| 208 | #define CEC_LOG_ADDR_SPECIFIC 14 | ||
| 209 | #define CEC_LOG_ADDR_UNREGISTERED 15 /* as initiator address */ | ||
| 210 | #define CEC_LOG_ADDR_BROADCAST 15 /* ad destination address */ | ||
| 211 | |||
| 212 | /* The logical address types that the CEC device wants to claim */ | ||
| 213 | #define CEC_LOG_ADDR_TYPE_TV 0 | ||
| 214 | #define CEC_LOG_ADDR_TYPE_RECORD 1 | ||
| 215 | #define CEC_LOG_ADDR_TYPE_TUNER 2 | ||
| 216 | #define CEC_LOG_ADDR_TYPE_PLAYBACK 3 | ||
| 217 | #define CEC_LOG_ADDR_TYPE_AUDIOSYSTEM 4 | ||
| 218 | #define CEC_LOG_ADDR_TYPE_SPECIFIC 5 | ||
| 219 | #define CEC_LOG_ADDR_TYPE_UNREGISTERED 6 | ||
| 220 | /* | ||
| 221 | * Switches should use UNREGISTERED. | ||
| 222 | * Processors should use SPECIFIC. | ||
| 223 | */ | ||
| 224 | |||
| 225 | #define CEC_LOG_ADDR_MASK_TV (1 << CEC_LOG_ADDR_TV) | ||
| 226 | #define CEC_LOG_ADDR_MASK_RECORD ((1 << CEC_LOG_ADDR_RECORD_1) | \ | ||
| 227 | (1 << CEC_LOG_ADDR_RECORD_2) | \ | ||
| 228 | (1 << CEC_LOG_ADDR_RECORD_3)) | ||
| 229 | #define CEC_LOG_ADDR_MASK_TUNER ((1 << CEC_LOG_ADDR_TUNER_1) | \ | ||
| 230 | (1 << CEC_LOG_ADDR_TUNER_2) | \ | ||
| 231 | (1 << CEC_LOG_ADDR_TUNER_3) | \ | ||
| 232 | (1 << CEC_LOG_ADDR_TUNER_4)) | ||
| 233 | #define CEC_LOG_ADDR_MASK_PLAYBACK ((1 << CEC_LOG_ADDR_PLAYBACK_1) | \ | ||
| 234 | (1 << CEC_LOG_ADDR_PLAYBACK_2) | \ | ||
| 235 | (1 << CEC_LOG_ADDR_PLAYBACK_3)) | ||
| 236 | #define CEC_LOG_ADDR_MASK_AUDIOSYSTEM (1 << CEC_LOG_ADDR_AUDIOSYSTEM) | ||
| 237 | #define CEC_LOG_ADDR_MASK_BACKUP ((1 << CEC_LOG_ADDR_BACKUP_1) | \ | ||
| 238 | (1 << CEC_LOG_ADDR_BACKUP_2)) | ||
| 239 | #define CEC_LOG_ADDR_MASK_SPECIFIC (1 << CEC_LOG_ADDR_SPECIFIC) | ||
| 240 | #define CEC_LOG_ADDR_MASK_UNREGISTERED (1 << CEC_LOG_ADDR_UNREGISTERED) | ||
| 241 | |||
| 242 | static inline bool cec_has_tv(__u16 log_addr_mask) | ||
| 243 | { | ||
| 244 | return log_addr_mask & CEC_LOG_ADDR_MASK_TV; | ||
| 245 | } | ||
| 246 | |||
| 247 | static inline bool cec_has_record(__u16 log_addr_mask) | ||
| 248 | { | ||
| 249 | return log_addr_mask & CEC_LOG_ADDR_MASK_RECORD; | ||
| 250 | } | ||
| 251 | |||
| 252 | static inline bool cec_has_tuner(__u16 log_addr_mask) | ||
| 253 | { | ||
| 254 | return log_addr_mask & CEC_LOG_ADDR_MASK_TUNER; | ||
| 255 | } | ||
| 256 | |||
| 257 | static inline bool cec_has_playback(__u16 log_addr_mask) | ||
| 258 | { | ||
| 259 | return log_addr_mask & CEC_LOG_ADDR_MASK_PLAYBACK; | ||
| 260 | } | ||
| 261 | |||
| 262 | static inline bool cec_has_audiosystem(__u16 log_addr_mask) | ||
| 263 | { | ||
| 264 | return log_addr_mask & CEC_LOG_ADDR_MASK_AUDIOSYSTEM; | ||
| 265 | } | ||
| 266 | |||
| 267 | static inline bool cec_has_backup(__u16 log_addr_mask) | ||
| 268 | { | ||
| 269 | return log_addr_mask & CEC_LOG_ADDR_MASK_BACKUP; | ||
| 270 | } | ||
| 271 | |||
| 272 | static inline bool cec_has_specific(__u16 log_addr_mask) | ||
| 273 | { | ||
| 274 | return log_addr_mask & CEC_LOG_ADDR_MASK_SPECIFIC; | ||
| 275 | } | ||
| 276 | |||
| 277 | static inline bool cec_is_unregistered(__u16 log_addr_mask) | ||
| 278 | { | ||
| 279 | return log_addr_mask & CEC_LOG_ADDR_MASK_UNREGISTERED; | ||
| 280 | } | ||
| 281 | |||
| 282 | static inline bool cec_is_unconfigured(__u16 log_addr_mask) | ||
| 283 | { | ||
| 284 | return log_addr_mask == 0; | ||
| 285 | } | ||
| 286 | |||
| 287 | /* | ||
| 288 | * Use this if there is no vendor ID (CEC_G_VENDOR_ID) or if the vendor ID | ||
| 289 | * should be disabled (CEC_S_VENDOR_ID) | ||
| 290 | */ | ||
| 291 | #define CEC_VENDOR_ID_NONE 0xffffffff | ||
| 292 | |||
| 293 | /* The message handling modes */ | ||
| 294 | /* Modes for initiator */ | ||
| 295 | #define CEC_MODE_NO_INITIATOR (0x0 << 0) | ||
| 296 | #define CEC_MODE_INITIATOR (0x1 << 0) | ||
| 297 | #define CEC_MODE_EXCL_INITIATOR (0x2 << 0) | ||
| 298 | #define CEC_MODE_INITIATOR_MSK 0x0f | ||
| 299 | |||
| 300 | /* Modes for follower */ | ||
| 301 | #define CEC_MODE_NO_FOLLOWER (0x0 << 4) | ||
| 302 | #define CEC_MODE_FOLLOWER (0x1 << 4) | ||
| 303 | #define CEC_MODE_EXCL_FOLLOWER (0x2 << 4) | ||
| 304 | #define CEC_MODE_EXCL_FOLLOWER_PASSTHRU (0x3 << 4) | ||
| 305 | #define CEC_MODE_MONITOR (0xe << 4) | ||
| 306 | #define CEC_MODE_MONITOR_ALL (0xf << 4) | ||
| 307 | #define CEC_MODE_FOLLOWER_MSK 0xf0 | ||
| 308 | |||
| 309 | /* Userspace has to configure the physical address */ | ||
| 310 | #define CEC_CAP_PHYS_ADDR (1 << 0) | ||
| 311 | /* Userspace has to configure the logical addresses */ | ||
| 312 | #define CEC_CAP_LOG_ADDRS (1 << 1) | ||
| 313 | /* Userspace can transmit messages (and thus become follower as well) */ | ||
| 314 | #define CEC_CAP_TRANSMIT (1 << 2) | ||
| 315 | /* | ||
| 316 | * Passthrough all messages instead of processing them. | ||
| 317 | */ | ||
| 318 | #define CEC_CAP_PASSTHROUGH (1 << 3) | ||
| 319 | /* Supports remote control */ | ||
| 320 | #define CEC_CAP_RC (1 << 4) | ||
| 321 | /* Hardware can monitor all messages, not just directed and broadcast. */ | ||
| 322 | #define CEC_CAP_MONITOR_ALL (1 << 5) | ||
| 323 | |||
| 324 | /** | ||
| 325 | * struct cec_caps - CEC capabilities structure. | ||
| 326 | * @driver: name of the CEC device driver. | ||
| 327 | * @name: name of the CEC device. @driver + @name must be unique. | ||
| 328 | * @available_log_addrs: number of available logical addresses. | ||
| 329 | * @capabilities: capabilities of the CEC adapter. | ||
| 330 | * @version: version of the CEC adapter framework. | ||
| 331 | */ | ||
| 332 | struct cec_caps { | ||
| 333 | char driver[32]; | ||
| 334 | char name[32]; | ||
| 335 | __u32 available_log_addrs; | ||
| 336 | __u32 capabilities; | ||
| 337 | __u32 version; | ||
| 338 | }; | ||
| 339 | |||
| 340 | /** | ||
| 341 | * struct cec_log_addrs - CEC logical addresses structure. | ||
| 342 | * @log_addr: the claimed logical addresses. Set by the driver. | ||
| 343 | * @log_addr_mask: current logical address mask. Set by the driver. | ||
| 344 | * @cec_version: the CEC version that the adapter should implement. Set by the | ||
| 345 | * caller. | ||
| 346 | * @num_log_addrs: how many logical addresses should be claimed. Set by the | ||
| 347 | * caller. | ||
| 348 | * @vendor_id: the vendor ID of the device. Set by the caller. | ||
| 349 | * @flags: set to 0. | ||
| 350 | * @osd_name: the OSD name of the device. Set by the caller. | ||
| 351 | * @primary_device_type: the primary device type for each logical address. | ||
| 352 | * Set by the caller. | ||
| 353 | * @log_addr_type: the logical address types. Set by the caller. | ||
| 354 | * @all_device_types: CEC 2.0: all device types represented by the logical | ||
| 355 | * address. Set by the caller. | ||
| 356 | * @features: CEC 2.0: The logical address features. Set by the caller. | ||
| 357 | */ | ||
| 358 | struct cec_log_addrs { | ||
| 359 | __u8 log_addr[CEC_MAX_LOG_ADDRS]; | ||
| 360 | __u16 log_addr_mask; | ||
| 361 | __u8 cec_version; | ||
| 362 | __u8 num_log_addrs; | ||
| 363 | __u32 vendor_id; | ||
| 364 | __u32 flags; | ||
| 365 | char osd_name[15]; | ||
| 366 | __u8 primary_device_type[CEC_MAX_LOG_ADDRS]; | ||
| 367 | __u8 log_addr_type[CEC_MAX_LOG_ADDRS]; | ||
| 368 | |||
| 369 | /* CEC 2.0 */ | ||
| 370 | __u8 all_device_types[CEC_MAX_LOG_ADDRS]; | ||
| 371 | __u8 features[CEC_MAX_LOG_ADDRS][12]; | ||
| 372 | }; | ||
| 373 | |||
| 374 | /* Events */ | ||
| 375 | |||
| 376 | /* Event that occurs when the adapter state changes */ | ||
| 377 | #define CEC_EVENT_STATE_CHANGE 1 | ||
| 378 | /* | ||
| 379 | * This event is sent when messages are lost because the application | ||
| 380 | * didn't empty the message queue in time | ||
| 381 | */ | ||
| 382 | #define CEC_EVENT_LOST_MSGS 2 | ||
| 383 | |||
| 384 | #define CEC_EVENT_FL_INITIAL_STATE (1 << 0) | ||
| 385 | |||
| 386 | /** | ||
| 387 | * struct cec_event_state_change - used when the CEC adapter changes state. | ||
| 388 | * @phys_addr: the current physical address | ||
| 389 | * @log_addr_mask: the current logical address mask | ||
| 390 | */ | ||
| 391 | struct cec_event_state_change { | ||
| 392 | __u16 phys_addr; | ||
| 393 | __u16 log_addr_mask; | ||
| 394 | }; | ||
| 395 | |||
| 396 | /** | ||
| 397 | * struct cec_event_lost_msgs - tells you how many messages were lost due. | ||
| 398 | * @lost_msgs: how many messages were lost. | ||
| 399 | */ | ||
| 400 | struct cec_event_lost_msgs { | ||
| 401 | __u32 lost_msgs; | ||
| 402 | }; | ||
| 403 | |||
| 404 | /** | ||
| 405 | * struct cec_event - CEC event structure | ||
| 406 | * @ts: the timestamp of when the event was sent. | ||
| 407 | * @event: the event. | ||
| 408 | * array. | ||
| 409 | * @state_change: the event payload for CEC_EVENT_STATE_CHANGE. | ||
| 410 | * @lost_msgs: the event payload for CEC_EVENT_LOST_MSGS. | ||
| 411 | * @raw: array to pad the union. | ||
| 412 | */ | ||
| 413 | struct cec_event { | ||
| 414 | __u64 ts; | ||
| 415 | __u32 event; | ||
| 416 | __u32 flags; | ||
| 417 | union { | ||
| 418 | struct cec_event_state_change state_change; | ||
| 419 | struct cec_event_lost_msgs lost_msgs; | ||
| 420 | __u32 raw[16]; | ||
| 421 | }; | ||
| 422 | }; | ||
| 423 | |||
| 424 | /* ioctls */ | ||
| 425 | |||
| 426 | /* Adapter capabilities */ | ||
| 427 | #define CEC_ADAP_G_CAPS _IOWR('a', 0, struct cec_caps) | ||
| 428 | |||
| 429 | /* | ||
| 430 | * phys_addr is either 0 (if this is the CEC root device) | ||
| 431 | * or a valid physical address obtained from the sink's EDID | ||
| 432 | * as read by this CEC device (if this is a source device) | ||
| 433 | * or a physical address obtained and modified from a sink | ||
| 434 | * EDID and used for a sink CEC device. | ||
| 435 | * If nothing is connected, then phys_addr is 0xffff. | ||
| 436 | * See HDMI 1.4b, section 8.7 (Physical Address). | ||
| 437 | * | ||
| 438 | * The CEC_ADAP_S_PHYS_ADDR ioctl may not be available if that is handled | ||
| 439 | * internally. | ||
| 440 | */ | ||
| 441 | #define CEC_ADAP_G_PHYS_ADDR _IOR('a', 1, __u16) | ||
| 442 | #define CEC_ADAP_S_PHYS_ADDR _IOW('a', 2, __u16) | ||
| 443 | |||
| 444 | /* | ||
| 445 | * Configure the CEC adapter. It sets the device type and which | ||
| 446 | * logical types it will try to claim. It will return which | ||
| 447 | * logical addresses it could actually claim. | ||
| 448 | * An error is returned if the adapter is disabled or if there | ||
| 449 | * is no physical address assigned. | ||
| 450 | */ | ||
| 451 | |||
| 452 | #define CEC_ADAP_G_LOG_ADDRS _IOR('a', 3, struct cec_log_addrs) | ||
| 453 | #define CEC_ADAP_S_LOG_ADDRS _IOWR('a', 4, struct cec_log_addrs) | ||
| 454 | |||
| 455 | /* Transmit/receive a CEC command */ | ||
| 456 | #define CEC_TRANSMIT _IOWR('a', 5, struct cec_msg) | ||
| 457 | #define CEC_RECEIVE _IOWR('a', 6, struct cec_msg) | ||
| 458 | |||
| 459 | /* Dequeue CEC events */ | ||
| 460 | #define CEC_DQEVENT _IOWR('a', 7, struct cec_event) | ||
| 461 | |||
| 462 | /* | ||
| 463 | * Get and set the message handling mode for this filehandle. | ||
| 464 | */ | ||
| 465 | #define CEC_G_MODE _IOR('a', 8, __u32) | ||
| 466 | #define CEC_S_MODE _IOW('a', 9, __u32) | ||
| 467 | |||
| 468 | /* | ||
| 469 | * The remainder of this header defines all CEC messages and operands. | ||
| 470 | * The format matters since it the cec-ctl utility parses it to generate | ||
| 471 | * code for implementing all these messages. | ||
| 472 | * | ||
| 473 | * Comments ending with 'Feature' group messages for each feature. | ||
| 474 | * If messages are part of multiple features, then the "Has also" | ||
| 475 | * comment is used to list the previously defined messages that are | ||
| 476 | * supported by the feature. | ||
| 477 | * | ||
| 478 | * Before operands are defined a comment is added that gives the | ||
| 479 | * name of the operand and in brackets the variable name of the | ||
| 480 | * corresponding argument in the cec-funcs.h function. | ||
| 481 | */ | ||
| 482 | |||
| 483 | /* Messages */ | ||
| 484 | |||
| 485 | /* One Touch Play Feature */ | ||
| 486 | #define CEC_MSG_ACTIVE_SOURCE 0x82 | ||
| 487 | #define CEC_MSG_IMAGE_VIEW_ON 0x04 | ||
| 488 | #define CEC_MSG_TEXT_VIEW_ON 0x0d | ||
| 489 | |||
| 490 | |||
| 491 | /* Routing Control Feature */ | ||
| 492 | |||
| 493 | /* | ||
| 494 | * Has also: | ||
| 495 | * CEC_MSG_ACTIVE_SOURCE | ||
| 496 | */ | ||
| 497 | |||
| 498 | #define CEC_MSG_INACTIVE_SOURCE 0x9d | ||
| 499 | #define CEC_MSG_REQUEST_ACTIVE_SOURCE 0x85 | ||
| 500 | #define CEC_MSG_ROUTING_CHANGE 0x80 | ||
| 501 | #define CEC_MSG_ROUTING_INFORMATION 0x81 | ||
| 502 | #define CEC_MSG_SET_STREAM_PATH 0x86 | ||
| 503 | |||
| 504 | |||
| 505 | /* Standby Feature */ | ||
| 506 | #define CEC_MSG_STANDBY 0x36 | ||
| 507 | |||
| 508 | |||
| 509 | /* One Touch Record Feature */ | ||
| 510 | #define CEC_MSG_RECORD_OFF 0x0b | ||
| 511 | #define CEC_MSG_RECORD_ON 0x09 | ||
| 512 | /* Record Source Type Operand (rec_src_type) */ | ||
| 513 | #define CEC_OP_RECORD_SRC_OWN 1 | ||
| 514 | #define CEC_OP_RECORD_SRC_DIGITAL 2 | ||
| 515 | #define CEC_OP_RECORD_SRC_ANALOG 3 | ||
| 516 | #define CEC_OP_RECORD_SRC_EXT_PLUG 4 | ||
| 517 | #define CEC_OP_RECORD_SRC_EXT_PHYS_ADDR 5 | ||
| 518 | /* Service Identification Method Operand (service_id_method) */ | ||
| 519 | #define CEC_OP_SERVICE_ID_METHOD_BY_DIG_ID 0 | ||
| 520 | #define CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL 1 | ||
| 521 | /* Digital Service Broadcast System Operand (dig_bcast_system) */ | ||
| 522 | #define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_GEN 0x00 | ||
| 523 | #define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_GEN 0x01 | ||
| 524 | #define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_GEN 0x02 | ||
| 525 | #define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_BS 0x08 | ||
| 526 | #define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_CS 0x09 | ||
| 527 | #define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_T 0x0a | ||
| 528 | #define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_CABLE 0x10 | ||
| 529 | #define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_SAT 0x11 | ||
| 530 | #define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_T 0x12 | ||
| 531 | #define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_C 0x18 | ||
| 532 | #define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S 0x19 | ||
| 533 | #define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S2 0x1a | ||
| 534 | #define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_T 0x1b | ||
| 535 | /* Analogue Broadcast Type Operand (ana_bcast_type) */ | ||
| 536 | #define CEC_OP_ANA_BCAST_TYPE_CABLE 0 | ||
| 537 | #define CEC_OP_ANA_BCAST_TYPE_SATELLITE 1 | ||
| 538 | #define CEC_OP_ANA_BCAST_TYPE_TERRESTRIAL 2 | ||
| 539 | /* Broadcast System Operand (bcast_system) */ | ||
| 540 | #define CEC_OP_BCAST_SYSTEM_PAL_BG 0x00 | ||
| 541 | #define CEC_OP_BCAST_SYSTEM_SECAM_LQ 0x01 /* SECAM L' */ | ||
| 542 | #define CEC_OP_BCAST_SYSTEM_PAL_M 0x02 | ||
| 543 | #define CEC_OP_BCAST_SYSTEM_NTSC_M 0x03 | ||
| 544 | #define CEC_OP_BCAST_SYSTEM_PAL_I 0x04 | ||
| 545 | #define CEC_OP_BCAST_SYSTEM_SECAM_DK 0x05 | ||
| 546 | #define CEC_OP_BCAST_SYSTEM_SECAM_BG 0x06 | ||
| 547 | #define CEC_OP_BCAST_SYSTEM_SECAM_L 0x07 | ||
| 548 | #define CEC_OP_BCAST_SYSTEM_PAL_DK 0x08 | ||
| 549 | #define CEC_OP_BCAST_SYSTEM_OTHER 0x1f | ||
| 550 | /* Channel Number Format Operand (channel_number_fmt) */ | ||
| 551 | #define CEC_OP_CHANNEL_NUMBER_FMT_1_PART 0x01 | ||
| 552 | #define CEC_OP_CHANNEL_NUMBER_FMT_2_PART 0x02 | ||
| 553 | |||
| 554 | #define CEC_MSG_RECORD_STATUS 0x0a | ||
| 555 | /* Record Status Operand (rec_status) */ | ||
| 556 | #define CEC_OP_RECORD_STATUS_CUR_SRC 0x01 | ||
| 557 | #define CEC_OP_RECORD_STATUS_DIG_SERVICE 0x02 | ||
| 558 | #define CEC_OP_RECORD_STATUS_ANA_SERVICE 0x03 | ||
| 559 | #define CEC_OP_RECORD_STATUS_EXT_INPUT 0x04 | ||
| 560 | #define CEC_OP_RECORD_STATUS_NO_DIG_SERVICE 0x05 | ||
| 561 | #define CEC_OP_RECORD_STATUS_NO_ANA_SERVICE 0x06 | ||
| 562 | #define CEC_OP_RECORD_STATUS_NO_SERVICE 0x07 | ||
| 563 | #define CEC_OP_RECORD_STATUS_INVALID_EXT_PLUG 0x09 | ||
| 564 | #define CEC_OP_RECORD_STATUS_INVALID_EXT_PHYS_ADDR 0x0a | ||
| 565 | #define CEC_OP_RECORD_STATUS_UNSUP_CA 0x0b | ||
| 566 | #define CEC_OP_RECORD_STATUS_NO_CA_ENTITLEMENTS 0x0c | ||
| 567 | #define CEC_OP_RECORD_STATUS_CANT_COPY_SRC 0x0d | ||
| 568 | #define CEC_OP_RECORD_STATUS_NO_MORE_COPIES 0x0e | ||
| 569 | #define CEC_OP_RECORD_STATUS_NO_MEDIA 0x10 | ||
| 570 | #define CEC_OP_RECORD_STATUS_PLAYING 0x11 | ||
| 571 | #define CEC_OP_RECORD_STATUS_ALREADY_RECORDING 0x12 | ||
| 572 | #define CEC_OP_RECORD_STATUS_MEDIA_PROT 0x13 | ||
| 573 | #define CEC_OP_RECORD_STATUS_NO_SIGNAL 0x14 | ||
| 574 | #define CEC_OP_RECORD_STATUS_MEDIA_PROBLEM 0x15 | ||
| 575 | #define CEC_OP_RECORD_STATUS_NO_SPACE 0x16 | ||
| 576 | #define CEC_OP_RECORD_STATUS_PARENTAL_LOCK 0x17 | ||
| 577 | #define CEC_OP_RECORD_STATUS_TERMINATED_OK 0x1a | ||
| 578 | #define CEC_OP_RECORD_STATUS_ALREADY_TERM 0x1b | ||
| 579 | #define CEC_OP_RECORD_STATUS_OTHER 0x1f | ||
| 580 | |||
| 581 | #define CEC_MSG_RECORD_TV_SCREEN 0x0f | ||
| 582 | |||
| 583 | |||
| 584 | /* Timer Programming Feature */ | ||
| 585 | #define CEC_MSG_CLEAR_ANALOGUE_TIMER 0x33 | ||
| 586 | /* Recording Sequence Operand (recording_seq) */ | ||
| 587 | #define CEC_OP_REC_SEQ_SUNDAY 0x01 | ||
| 588 | #define CEC_OP_REC_SEQ_MONDAY 0x02 | ||
| 589 | #define CEC_OP_REC_SEQ_TUESDAY 0x04 | ||
| 590 | #define CEC_OP_REC_SEQ_WEDNESDAY 0x08 | ||
| 591 | #define CEC_OP_REC_SEQ_THURSDAY 0x10 | ||
| 592 | #define CEC_OP_REC_SEQ_FRIDAY 0x20 | ||
| 593 | #define CEC_OP_REC_SEQ_SATERDAY 0x40 | ||
| 594 | #define CEC_OP_REC_SEQ_ONCE_ONLY 0x00 | ||
| 595 | |||
| 596 | #define CEC_MSG_CLEAR_DIGITAL_TIMER 0x99 | ||
| 597 | |||
| 598 | #define CEC_MSG_CLEAR_EXT_TIMER 0xa1 | ||
| 599 | /* External Source Specifier Operand (ext_src_spec) */ | ||
| 600 | #define CEC_OP_EXT_SRC_PLUG 0x04 | ||
| 601 | #define CEC_OP_EXT_SRC_PHYS_ADDR 0x05 | ||
| 602 | |||
| 603 | #define CEC_MSG_SET_ANALOGUE_TIMER 0x34 | ||
| 604 | #define CEC_MSG_SET_DIGITAL_TIMER 0x97 | ||
| 605 | #define CEC_MSG_SET_EXT_TIMER 0xa2 | ||
| 606 | |||
| 607 | #define CEC_MSG_SET_TIMER_PROGRAM_TITLE 0x67 | ||
| 608 | #define CEC_MSG_TIMER_CLEARED_STATUS 0x43 | ||
| 609 | /* Timer Cleared Status Data Operand (timer_cleared_status) */ | ||
| 610 | #define CEC_OP_TIMER_CLR_STAT_RECORDING 0x00 | ||
| 611 | #define CEC_OP_TIMER_CLR_STAT_NO_MATCHING 0x01 | ||
| 612 | #define CEC_OP_TIMER_CLR_STAT_NO_INFO 0x02 | ||
| 613 | #define CEC_OP_TIMER_CLR_STAT_CLEARED 0x80 | ||
| 614 | |||
| 615 | #define CEC_MSG_TIMER_STATUS 0x35 | ||
| 616 | /* Timer Overlap Warning Operand (timer_overlap_warning) */ | ||
| 617 | #define CEC_OP_TIMER_OVERLAP_WARNING_NO_OVERLAP 0 | ||
| 618 | #define CEC_OP_TIMER_OVERLAP_WARNING_OVERLAP 1 | ||
| 619 | /* Media Info Operand (media_info) */ | ||
| 620 | #define CEC_OP_MEDIA_INFO_UNPROT_MEDIA 0 | ||
| 621 | #define CEC_OP_MEDIA_INFO_PROT_MEDIA 1 | ||
| 622 | #define CEC_OP_MEDIA_INFO_NO_MEDIA 2 | ||
| 623 | /* Programmed Indicator Operand (prog_indicator) */ | ||
| 624 | #define CEC_OP_PROG_IND_NOT_PROGRAMMED 0 | ||
| 625 | #define CEC_OP_PROG_IND_PROGRAMMED 1 | ||
| 626 | /* Programmed Info Operand (prog_info) */ | ||
| 627 | #define CEC_OP_PROG_INFO_ENOUGH_SPACE 0x08 | ||
| 628 | #define CEC_OP_PROG_INFO_NOT_ENOUGH_SPACE 0x09 | ||
| 629 | #define CEC_OP_PROG_INFO_MIGHT_NOT_BE_ENOUGH_SPACE 0x0b | ||
| 630 | #define CEC_OP_PROG_INFO_NONE_AVAILABLE 0x0a | ||
| 631 | /* Not Programmed Error Info Operand (prog_error) */ | ||
| 632 | #define CEC_OP_PROG_ERROR_NO_FREE_TIMER 0x01 | ||
| 633 | #define CEC_OP_PROG_ERROR_DATE_OUT_OF_RANGE 0x02 | ||
| 634 | #define CEC_OP_PROG_ERROR_REC_SEQ_ERROR 0x03 | ||
| 635 | #define CEC_OP_PROG_ERROR_INV_EXT_PLUG 0x04 | ||
| 636 | #define CEC_OP_PROG_ERROR_INV_EXT_PHYS_ADDR 0x05 | ||
| 637 | #define CEC_OP_PROG_ERROR_CA_UNSUPP 0x06 | ||
| 638 | #define CEC_OP_PROG_ERROR_INSUF_CA_ENTITLEMENTS 0x07 | ||
| 639 | #define CEC_OP_PROG_ERROR_RESOLUTION_UNSUPP 0x08 | ||
| 640 | #define CEC_OP_PROG_ERROR_PARENTAL_LOCK 0x09 | ||
| 641 | #define CEC_OP_PROG_ERROR_CLOCK_FAILURE 0x0a | ||
| 642 | #define CEC_OP_PROG_ERROR_DUPLICATE 0x0e | ||
| 643 | |||
| 644 | |||
| 645 | /* System Information Feature */ | ||
| 646 | #define CEC_MSG_CEC_VERSION 0x9e | ||
| 647 | /* CEC Version Operand (cec_version) */ | ||
| 648 | #define CEC_OP_CEC_VERSION_1_3A 4 | ||
| 649 | #define CEC_OP_CEC_VERSION_1_4 5 | ||
| 650 | #define CEC_OP_CEC_VERSION_2_0 6 | ||
| 651 | |||
| 652 | #define CEC_MSG_GET_CEC_VERSION 0x9f | ||
| 653 | #define CEC_MSG_GIVE_PHYSICAL_ADDR 0x83 | ||
| 654 | #define CEC_MSG_GET_MENU_LANGUAGE 0x91 | ||
| 655 | #define CEC_MSG_REPORT_PHYSICAL_ADDR 0x84 | ||
| 656 | /* Primary Device Type Operand (prim_devtype) */ | ||
| 657 | #define CEC_OP_PRIM_DEVTYPE_TV 0 | ||
| 658 | #define CEC_OP_PRIM_DEVTYPE_RECORD 1 | ||
| 659 | #define CEC_OP_PRIM_DEVTYPE_TUNER 3 | ||
| 660 | #define CEC_OP_PRIM_DEVTYPE_PLAYBACK 4 | ||
| 661 | #define CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM 5 | ||
| 662 | #define CEC_OP_PRIM_DEVTYPE_SWITCH 6 | ||
| 663 | #define CEC_OP_PRIM_DEVTYPE_PROCESSOR 7 | ||
| 664 | |||
| 665 | #define CEC_MSG_SET_MENU_LANGUAGE 0x32 | ||
| 666 | #define CEC_MSG_REPORT_FEATURES 0xa6 /* HDMI 2.0 */ | ||
| 667 | /* All Device Types Operand (all_device_types) */ | ||
| 668 | #define CEC_OP_ALL_DEVTYPE_TV 0x80 | ||
| 669 | #define CEC_OP_ALL_DEVTYPE_RECORD 0x40 | ||
| 670 | #define CEC_OP_ALL_DEVTYPE_TUNER 0x20 | ||
| 671 | #define CEC_OP_ALL_DEVTYPE_PLAYBACK 0x10 | ||
| 672 | #define CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM 0x08 | ||
| 673 | #define CEC_OP_ALL_DEVTYPE_SWITCH 0x04 | ||
| 674 | /* | ||
| 675 | * And if you wondering what happened to PROCESSOR devices: those should | ||
| 676 | * be mapped to a SWITCH. | ||
| 677 | */ | ||
| 678 | |||
| 679 | /* Valid for RC Profile and Device Feature operands */ | ||
| 680 | #define CEC_OP_FEAT_EXT 0x80 /* Extension bit */ | ||
| 681 | /* RC Profile Operand (rc_profile) */ | ||
| 682 | #define CEC_OP_FEAT_RC_TV_PROFILE_NONE 0x00 | ||
| 683 | #define CEC_OP_FEAT_RC_TV_PROFILE_1 0x02 | ||
| 684 | #define CEC_OP_FEAT_RC_TV_PROFILE_2 0x06 | ||
| 685 | #define CEC_OP_FEAT_RC_TV_PROFILE_3 0x0a | ||
| 686 | #define CEC_OP_FEAT_RC_TV_PROFILE_4 0x0e | ||
| 687 | #define CEC_OP_FEAT_RC_SRC_HAS_DEV_ROOT_MENU 0x50 | ||
| 688 | #define CEC_OP_FEAT_RC_SRC_HAS_DEV_SETUP_MENU 0x48 | ||
| 689 | #define CEC_OP_FEAT_RC_SRC_HAS_CONTENTS_MENU 0x44 | ||
| 690 | #define CEC_OP_FEAT_RC_SRC_HAS_MEDIA_TOP_MENU 0x42 | ||
| 691 | #define CEC_OP_FEAT_RC_SRC_HAS_MEDIA_CONTEXT_MENU 0x41 | ||
| 692 | /* Device Feature Operand (dev_features) */ | ||
| 693 | #define CEC_OP_FEAT_DEV_HAS_RECORD_TV_SCREEN 0x40 | ||
| 694 | #define CEC_OP_FEAT_DEV_HAS_SET_OSD_STRING 0x20 | ||
| 695 | #define CEC_OP_FEAT_DEV_HAS_DECK_CONTROL 0x10 | ||
| 696 | #define CEC_OP_FEAT_DEV_HAS_SET_AUDIO_RATE 0x08 | ||
| 697 | #define CEC_OP_FEAT_DEV_SINK_HAS_ARC_TX 0x04 | ||
| 698 | #define CEC_OP_FEAT_DEV_SOURCE_HAS_ARC_RX 0x02 | ||
| 699 | |||
| 700 | #define CEC_MSG_GIVE_FEATURES 0xa5 /* HDMI 2.0 */ | ||
| 701 | |||
| 702 | |||
| 703 | /* Deck Control Feature */ | ||
| 704 | #define CEC_MSG_DECK_CONTROL 0x42 | ||
| 705 | /* Deck Control Mode Operand (deck_control_mode) */ | ||
| 706 | #define CEC_OP_DECK_CTL_MODE_SKIP_FWD 1 | ||
| 707 | #define CEC_OP_DECK_CTL_MODE_SKIP_REV 2 | ||
| 708 | #define CEC_OP_DECK_CTL_MODE_STOP 3 | ||
| 709 | #define CEC_OP_DECK_CTL_MODE_EJECT 4 | ||
| 710 | |||
| 711 | #define CEC_MSG_DECK_STATUS 0x1b | ||
| 712 | /* Deck Info Operand (deck_info) */ | ||
| 713 | #define CEC_OP_DECK_INFO_PLAY 0x11 | ||
| 714 | #define CEC_OP_DECK_INFO_RECORD 0x12 | ||
| 715 | #define CEC_OP_DECK_INFO_PLAY_REV 0x13 | ||
| 716 | #define CEC_OP_DECK_INFO_STILL 0x14 | ||
| 717 | #define CEC_OP_DECK_INFO_SLOW 0x15 | ||
| 718 | #define CEC_OP_DECK_INFO_SLOW_REV 0x16 | ||
| 719 | #define CEC_OP_DECK_INFO_FAST_FWD 0x17 | ||
| 720 | #define CEC_OP_DECK_INFO_FAST_REV 0x18 | ||
| 721 | #define CEC_OP_DECK_INFO_NO_MEDIA 0x19 | ||
| 722 | #define CEC_OP_DECK_INFO_STOP 0x1a | ||
| 723 | #define CEC_OP_DECK_INFO_SKIP_FWD 0x1b | ||
| 724 | #define CEC_OP_DECK_INFO_SKIP_REV 0x1c | ||
| 725 | #define CEC_OP_DECK_INFO_INDEX_SEARCH_FWD 0x1d | ||
| 726 | #define CEC_OP_DECK_INFO_INDEX_SEARCH_REV 0x1e | ||
| 727 | #define CEC_OP_DECK_INFO_OTHER 0x1f | ||
| 728 | |||
| 729 | #define CEC_MSG_GIVE_DECK_STATUS 0x1a | ||
| 730 | /* Status Request Operand (status_req) */ | ||
| 731 | #define CEC_OP_STATUS_REQ_ON 1 | ||
| 732 | #define CEC_OP_STATUS_REQ_OFF 2 | ||
| 733 | #define CEC_OP_STATUS_REQ_ONCE 3 | ||
| 734 | |||
| 735 | #define CEC_MSG_PLAY 0x41 | ||
| 736 | /* Play Mode Operand (play_mode) */ | ||
| 737 | #define CEC_OP_PLAY_MODE_PLAY_FWD 0x24 | ||
| 738 | #define CEC_OP_PLAY_MODE_PLAY_REV 0x20 | ||
| 739 | #define CEC_OP_PLAY_MODE_PLAY_STILL 0x25 | ||
| 740 | #define CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MIN 0x05 | ||
| 741 | #define CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MED 0x06 | ||
| 742 | #define CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MAX 0x07 | ||
| 743 | #define CEC_OP_PLAY_MODE_PLAY_FAST_REV_MIN 0x09 | ||
| 744 | #define CEC_OP_PLAY_MODE_PLAY_FAST_REV_MED 0x0a | ||
| 745 | #define CEC_OP_PLAY_MODE_PLAY_FAST_REV_MAX 0x0b | ||
| 746 | #define CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MIN 0x15 | ||
| 747 | #define CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MED 0x16 | ||
| 748 | #define CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MAX 0x17 | ||
| 749 | #define CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MIN 0x19 | ||
| 750 | #define CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MED 0x1a | ||
| 751 | #define CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MAX 0x1b | ||
| 752 | |||
| 753 | |||
| 754 | /* Tuner Control Feature */ | ||
| 755 | #define CEC_MSG_GIVE_TUNER_DEVICE_STATUS 0x08 | ||
| 756 | #define CEC_MSG_SELECT_ANALOGUE_SERVICE 0x92 | ||
| 757 | #define CEC_MSG_SELECT_DIGITAL_SERVICE 0x93 | ||
| 758 | #define CEC_MSG_TUNER_DEVICE_STATUS 0x07 | ||
| 759 | /* Recording Flag Operand (rec_flag) */ | ||
| 760 | #define CEC_OP_REC_FLAG_USED 0 | ||
| 761 | #define CEC_OP_REC_FLAG_NOT_USED 1 | ||
| 762 | /* Tuner Display Info Operand (tuner_display_info) */ | ||
| 763 | #define CEC_OP_TUNER_DISPLAY_INFO_DIGITAL 0 | ||
| 764 | #define CEC_OP_TUNER_DISPLAY_INFO_NONE 1 | ||
| 765 | #define CEC_OP_TUNER_DISPLAY_INFO_ANALOGUE 2 | ||
| 766 | |||
| 767 | #define CEC_MSG_TUNER_STEP_DECREMENT 0x06 | ||
| 768 | #define CEC_MSG_TUNER_STEP_INCREMENT 0x05 | ||
| 769 | |||
| 770 | |||
| 771 | /* Vendor Specific Commands Feature */ | ||
| 772 | |||
| 773 | /* | ||
| 774 | * Has also: | ||
| 775 | * CEC_MSG_CEC_VERSION | ||
| 776 | * CEC_MSG_GET_CEC_VERSION | ||
| 777 | */ | ||
| 778 | #define CEC_MSG_DEVICE_VENDOR_ID 0x87 | ||
| 779 | #define CEC_MSG_GIVE_DEVICE_VENDOR_ID 0x8c | ||
| 780 | #define CEC_MSG_VENDOR_COMMAND 0x89 | ||
| 781 | #define CEC_MSG_VENDOR_COMMAND_WITH_ID 0xa0 | ||
| 782 | #define CEC_MSG_VENDOR_REMOTE_BUTTON_DOWN 0x8a | ||
| 783 | #define CEC_MSG_VENDOR_REMOTE_BUTTON_UP 0x8b | ||
| 784 | |||
| 785 | |||
| 786 | /* OSD Display Feature */ | ||
| 787 | #define CEC_MSG_SET_OSD_STRING 0x64 | ||
| 788 | /* Display Control Operand (disp_ctl) */ | ||
| 789 | #define CEC_OP_DISP_CTL_DEFAULT 0x00 | ||
| 790 | #define CEC_OP_DISP_CTL_UNTIL_CLEARED 0x40 | ||
| 791 | #define CEC_OP_DISP_CTL_CLEAR 0x80 | ||
| 792 | |||
| 793 | |||
| 794 | /* Device OSD Transfer Feature */ | ||
| 795 | #define CEC_MSG_GIVE_OSD_NAME 0x46 | ||
| 796 | #define CEC_MSG_SET_OSD_NAME 0x47 | ||
| 797 | |||
| 798 | |||
| 799 | /* Device Menu Control Feature */ | ||
| 800 | #define CEC_MSG_MENU_REQUEST 0x8d | ||
| 801 | /* Menu Request Type Operand (menu_req) */ | ||
| 802 | #define CEC_OP_MENU_REQUEST_ACTIVATE 0x00 | ||
| 803 | #define CEC_OP_MENU_REQUEST_DEACTIVATE 0x01 | ||
| 804 | #define CEC_OP_MENU_REQUEST_QUERY 0x02 | ||
| 805 | |||
| 806 | #define CEC_MSG_MENU_STATUS 0x8e | ||
| 807 | /* Menu State Operand (menu_state) */ | ||
| 808 | #define CEC_OP_MENU_STATE_ACTIVATED 0x00 | ||
| 809 | #define CEC_OP_MENU_STATE_DEACTIVATED 0x01 | ||
| 810 | |||
| 811 | #define CEC_MSG_USER_CONTROL_PRESSED 0x44 | ||
| 812 | /* UI Broadcast Type Operand (ui_bcast_type) */ | ||
| 813 | #define CEC_OP_UI_BCAST_TYPE_TOGGLE_ALL 0x00 | ||
| 814 | #define CEC_OP_UI_BCAST_TYPE_TOGGLE_DIG_ANA 0x01 | ||
| 815 | #define CEC_OP_UI_BCAST_TYPE_ANALOGUE 0x10 | ||
| 816 | #define CEC_OP_UI_BCAST_TYPE_ANALOGUE_T 0x20 | ||
| 817 | #define CEC_OP_UI_BCAST_TYPE_ANALOGUE_CABLE 0x30 | ||
| 818 | #define CEC_OP_UI_BCAST_TYPE_ANALOGUE_SAT 0x40 | ||
| 819 | #define CEC_OP_UI_BCAST_TYPE_DIGITAL 0x50 | ||
| 820 | #define CEC_OP_UI_BCAST_TYPE_DIGITAL_T 0x60 | ||
| 821 | #define CEC_OP_UI_BCAST_TYPE_DIGITAL_CABLE 0x70 | ||
| 822 | #define CEC_OP_UI_BCAST_TYPE_DIGITAL_SAT 0x80 | ||
| 823 | #define CEC_OP_UI_BCAST_TYPE_DIGITAL_COM_SAT 0x90 | ||
| 824 | #define CEC_OP_UI_BCAST_TYPE_DIGITAL_COM_SAT2 0x91 | ||
| 825 | #define CEC_OP_UI_BCAST_TYPE_IP 0xa0 | ||
| 826 | /* UI Sound Presentation Control Operand (ui_snd_pres_ctl) */ | ||
| 827 | #define CEC_OP_UI_SND_PRES_CTL_DUAL_MONO 0x10 | ||
| 828 | #define CEC_OP_UI_SND_PRES_CTL_KARAOKE 0x20 | ||
| 829 | #define CEC_OP_UI_SND_PRES_CTL_DOWNMIX 0x80 | ||
| 830 | #define CEC_OP_UI_SND_PRES_CTL_REVERB 0x90 | ||
| 831 | #define CEC_OP_UI_SND_PRES_CTL_EQUALIZER 0xa0 | ||
| 832 | #define CEC_OP_UI_SND_PRES_CTL_BASS_UP 0xb1 | ||
| 833 | #define CEC_OP_UI_SND_PRES_CTL_BASS_NEUTRAL 0xb2 | ||
| 834 | #define CEC_OP_UI_SND_PRES_CTL_BASS_DOWN 0xb3 | ||
| 835 | #define CEC_OP_UI_SND_PRES_CTL_TREBLE_UP 0xc1 | ||
| 836 | #define CEC_OP_UI_SND_PRES_CTL_TREBLE_NEUTRAL 0xc2 | ||
| 837 | #define CEC_OP_UI_SND_PRES_CTL_TREBLE_DOWN 0xc3 | ||
| 838 | |||
| 839 | #define CEC_MSG_USER_CONTROL_RELEASED 0x45 | ||
| 840 | |||
| 841 | |||
| 842 | /* Remote Control Passthrough Feature */ | ||
| 843 | |||
| 844 | /* | ||
| 845 | * Has also: | ||
| 846 | * CEC_MSG_USER_CONTROL_PRESSED | ||
| 847 | * CEC_MSG_USER_CONTROL_RELEASED | ||
| 848 | */ | ||
| 849 | |||
| 850 | |||
| 851 | /* Power Status Feature */ | ||
| 852 | #define CEC_MSG_GIVE_DEVICE_POWER_STATUS 0x8f | ||
| 853 | #define CEC_MSG_REPORT_POWER_STATUS 0x90 | ||
| 854 | /* Power Status Operand (pwr_state) */ | ||
| 855 | #define CEC_OP_POWER_STATUS_ON 0 | ||
| 856 | #define CEC_OP_POWER_STATUS_STANDBY 1 | ||
| 857 | #define CEC_OP_POWER_STATUS_TO_ON 2 | ||
| 858 | #define CEC_OP_POWER_STATUS_TO_STANDBY 3 | ||
| 859 | |||
| 860 | |||
| 861 | /* General Protocol Messages */ | ||
| 862 | #define CEC_MSG_FEATURE_ABORT 0x00 | ||
| 863 | /* Abort Reason Operand (reason) */ | ||
| 864 | #define CEC_OP_ABORT_UNRECOGNIZED_OP 0 | ||
| 865 | #define CEC_OP_ABORT_INCORRECT_MODE 1 | ||
| 866 | #define CEC_OP_ABORT_NO_SOURCE 2 | ||
| 867 | #define CEC_OP_ABORT_INVALID_OP 3 | ||
| 868 | #define CEC_OP_ABORT_REFUSED 4 | ||
| 869 | #define CEC_OP_ABORT_UNDETERMINED 5 | ||
| 870 | |||
| 871 | #define CEC_MSG_ABORT 0xff | ||
| 872 | |||
| 873 | |||
| 874 | /* System Audio Control Feature */ | ||
| 875 | |||
| 876 | /* | ||
| 877 | * Has also: | ||
| 878 | * CEC_MSG_USER_CONTROL_PRESSED | ||
| 879 | * CEC_MSG_USER_CONTROL_RELEASED | ||
| 880 | */ | ||
| 881 | #define CEC_MSG_GIVE_AUDIO_STATUS 0x71 | ||
| 882 | #define CEC_MSG_GIVE_SYSTEM_AUDIO_MODE_STATUS 0x7d | ||
| 883 | #define CEC_MSG_REPORT_AUDIO_STATUS 0x7a | ||
| 884 | /* Audio Mute Status Operand (aud_mute_status) */ | ||
| 885 | #define CEC_OP_AUD_MUTE_STATUS_OFF 0 | ||
| 886 | #define CEC_OP_AUD_MUTE_STATUS_ON 1 | ||
| 887 | |||
| 888 | #define CEC_MSG_REPORT_SHORT_AUDIO_DESCRIPTOR 0xa3 | ||
| 889 | #define CEC_MSG_REQUEST_SHORT_AUDIO_DESCRIPTOR 0xa4 | ||
| 890 | #define CEC_MSG_SET_SYSTEM_AUDIO_MODE 0x72 | ||
| 891 | /* System Audio Status Operand (sys_aud_status) */ | ||
| 892 | #define CEC_OP_SYS_AUD_STATUS_OFF 0 | ||
| 893 | #define CEC_OP_SYS_AUD_STATUS_ON 1 | ||
| 894 | |||
| 895 | #define CEC_MSG_SYSTEM_AUDIO_MODE_REQUEST 0x70 | ||
| 896 | #define CEC_MSG_SYSTEM_AUDIO_MODE_STATUS 0x7e | ||
| 897 | /* Audio Format ID Operand (audio_format_id) */ | ||
| 898 | #define CEC_OP_AUD_FMT_ID_CEA861 0 | ||
| 899 | #define CEC_OP_AUD_FMT_ID_CEA861_CXT 1 | ||
| 900 | |||
| 901 | |||
| 902 | /* Audio Rate Control Feature */ | ||
| 903 | #define CEC_MSG_SET_AUDIO_RATE 0x9a | ||
| 904 | /* Audio Rate Operand (audio_rate) */ | ||
| 905 | #define CEC_OP_AUD_RATE_OFF 0 | ||
| 906 | #define CEC_OP_AUD_RATE_WIDE_STD 1 | ||
| 907 | #define CEC_OP_AUD_RATE_WIDE_FAST 2 | ||
| 908 | #define CEC_OP_AUD_RATE_WIDE_SLOW 3 | ||
| 909 | #define CEC_OP_AUD_RATE_NARROW_STD 4 | ||
| 910 | #define CEC_OP_AUD_RATE_NARROW_FAST 5 | ||
| 911 | #define CEC_OP_AUD_RATE_NARROW_SLOW 6 | ||
| 912 | |||
| 913 | |||
| 914 | /* Audio Return Channel Control Feature */ | ||
| 915 | #define CEC_MSG_INITIATE_ARC 0xc0 | ||
| 916 | #define CEC_MSG_REPORT_ARC_INITIATED 0xc1 | ||
| 917 | #define CEC_MSG_REPORT_ARC_TERMINATED 0xc2 | ||
| 918 | #define CEC_MSG_REQUEST_ARC_INITIATION 0xc3 | ||
| 919 | #define CEC_MSG_REQUEST_ARC_TERMINATION 0xc4 | ||
| 920 | #define CEC_MSG_TERMINATE_ARC 0xc5 | ||
| 921 | |||
| 922 | |||
| 923 | /* Dynamic Audio Lipsync Feature */ | ||
| 924 | /* Only for CEC 2.0 and up */ | ||
| 925 | #define CEC_MSG_REQUEST_CURRENT_LATENCY 0xa7 | ||
| 926 | #define CEC_MSG_REPORT_CURRENT_LATENCY 0xa8 | ||
| 927 | /* Low Latency Mode Operand (low_latency_mode) */ | ||
| 928 | #define CEC_OP_LOW_LATENCY_MODE_OFF 0 | ||
| 929 | #define CEC_OP_LOW_LATENCY_MODE_ON 1 | ||
| 930 | /* Audio Output Compensated Operand (audio_out_compensated) */ | ||
| 931 | #define CEC_OP_AUD_OUT_COMPENSATED_NA 0 | ||
| 932 | #define CEC_OP_AUD_OUT_COMPENSATED_DELAY 1 | ||
| 933 | #define CEC_OP_AUD_OUT_COMPENSATED_NO_DELAY 2 | ||
| 934 | #define CEC_OP_AUD_OUT_COMPENSATED_PARTIAL_DELAY 3 | ||
| 935 | |||
| 936 | |||
| 937 | /* Capability Discovery and Control Feature */ | ||
| 938 | #define CEC_MSG_CDC_MESSAGE 0xf8 | ||
| 939 | /* Ethernet-over-HDMI: nobody ever does this... */ | ||
| 940 | #define CEC_MSG_CDC_HEC_INQUIRE_STATE 0x00 | ||
| 941 | #define CEC_MSG_CDC_HEC_REPORT_STATE 0x01 | ||
| 942 | /* HEC Functionality State Operand (hec_func_state) */ | ||
| 943 | #define CEC_OP_HEC_FUNC_STATE_NOT_SUPPORTED 0 | ||
| 944 | #define CEC_OP_HEC_FUNC_STATE_INACTIVE 1 | ||
| 945 | #define CEC_OP_HEC_FUNC_STATE_ACTIVE 2 | ||
| 946 | #define CEC_OP_HEC_FUNC_STATE_ACTIVATION_FIELD 3 | ||
| 947 | /* Host Functionality State Operand (host_func_state) */ | ||
| 948 | #define CEC_OP_HOST_FUNC_STATE_NOT_SUPPORTED 0 | ||
| 949 | #define CEC_OP_HOST_FUNC_STATE_INACTIVE 1 | ||
| 950 | #define CEC_OP_HOST_FUNC_STATE_ACTIVE 2 | ||
| 951 | /* ENC Functionality State Operand (enc_func_state) */ | ||
| 952 | #define CEC_OP_ENC_FUNC_STATE_EXT_CON_NOT_SUPPORTED 0 | ||
| 953 | #define CEC_OP_ENC_FUNC_STATE_EXT_CON_INACTIVE 1 | ||
| 954 | #define CEC_OP_ENC_FUNC_STATE_EXT_CON_ACTIVE 2 | ||
| 955 | /* CDC Error Code Operand (cdc_errcode) */ | ||
| 956 | #define CEC_OP_CDC_ERROR_CODE_NONE 0 | ||
| 957 | #define CEC_OP_CDC_ERROR_CODE_CAP_UNSUPPORTED 1 | ||
| 958 | #define CEC_OP_CDC_ERROR_CODE_WRONG_STATE 2 | ||
| 959 | #define CEC_OP_CDC_ERROR_CODE_OTHER 3 | ||
| 960 | /* HEC Support Operand (hec_support) */ | ||
| 961 | #define CEC_OP_HEC_SUPPORT_NO 0 | ||
| 962 | #define CEC_OP_HEC_SUPPORT_YES 1 | ||
| 963 | /* HEC Activation Operand (hec_activation) */ | ||
| 964 | #define CEC_OP_HEC_ACTIVATION_ON 0 | ||
| 965 | #define CEC_OP_HEC_ACTIVATION_OFF 1 | ||
| 966 | |||
| 967 | #define CEC_MSG_CDC_HEC_SET_STATE_ADJACENT 0x02 | ||
| 968 | #define CEC_MSG_CDC_HEC_SET_STATE 0x03 | ||
| 969 | /* HEC Set State Operand (hec_set_state) */ | ||
| 970 | #define CEC_OP_HEC_SET_STATE_DEACTIVATE 0 | ||
| 971 | #define CEC_OP_HEC_SET_STATE_ACTIVATE 1 | ||
| 972 | |||
| 973 | #define CEC_MSG_CDC_HEC_REQUEST_DEACTIVATION 0x04 | ||
| 974 | #define CEC_MSG_CDC_HEC_NOTIFY_ALIVE 0x05 | ||
| 975 | #define CEC_MSG_CDC_HEC_DISCOVER 0x06 | ||
| 976 | /* Hotplug Detect messages */ | ||
| 977 | #define CEC_MSG_CDC_HPD_SET_STATE 0x10 | ||
| 978 | /* HPD State Operand (hpd_state) */ | ||
| 979 | #define CEC_OP_HPD_STATE_CP_EDID_DISABLE 0 | ||
| 980 | #define CEC_OP_HPD_STATE_CP_EDID_ENABLE 1 | ||
| 981 | #define CEC_OP_HPD_STATE_CP_EDID_DISABLE_ENABLE 2 | ||
| 982 | #define CEC_OP_HPD_STATE_EDID_DISABLE 3 | ||
| 983 | #define CEC_OP_HPD_STATE_EDID_ENABLE 4 | ||
| 984 | #define CEC_OP_HPD_STATE_EDID_DISABLE_ENABLE 5 | ||
| 985 | #define CEC_MSG_CDC_HPD_REPORT_STATE 0x11 | ||
| 986 | /* HPD Error Code Operand (hpd_error) */ | ||
| 987 | #define CEC_OP_HPD_ERROR_NONE 0 | ||
| 988 | #define CEC_OP_HPD_ERROR_INITIATOR_NOT_CAPABLE 1 | ||
| 989 | #define CEC_OP_HPD_ERROR_INITIATOR_WRONG_STATE 2 | ||
| 990 | #define CEC_OP_HPD_ERROR_OTHER 3 | ||
| 991 | #define CEC_OP_HPD_ERROR_NONE_NO_VIDEO 4 | ||
| 992 | |||
| 993 | #endif | ||
| @@ -0,0 +1,104 @@ | |||
| 1 | /* | ||
| 2 | * cec-edid - HDMI Consumer Electronics Control & EDID helpers | ||
| 3 | * | ||
| 4 | * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved. | ||
| 5 | * | ||
| 6 | * This program is free software; you may redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; version 2 of the License. | ||
| 9 | * | ||
| 10 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
| 11 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
| 12 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
| 13 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||
| 14 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
| 15 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
| 16 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| 17 | * SOFTWARE. | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef _MEDIA_CEC_EDID_H | ||
| 21 | #define _MEDIA_CEC_EDID_H | ||
| 22 | |||
| 23 | #include <linux/types.h> | ||
| 24 | |||
| 25 | #define CEC_PHYS_ADDR_INVALID 0xffff | ||
| 26 | #define cec_phys_addr_exp(pa) \ | ||
| 27 | ((pa) >> 12), ((pa) >> 8) & 0xf, ((pa) >> 4) & 0xf, (pa) & 0xf | ||
| 28 | |||
| 29 | /** | ||
| 30 | * cec_get_edid_phys_addr() - find and return the physical address | ||
| 31 | * | ||
| 32 | * @edid: pointer to the EDID data | ||
| 33 | * @size: size in bytes of the EDID data | ||
| 34 | * @offset: If not %NULL then the location of the physical address | ||
| 35 | * bytes in the EDID will be returned here. This is set to 0 | ||
| 36 | * if there is no physical address found. | ||
| 37 | * | ||
| 38 | * Return: the physical address or CEC_PHYS_ADDR_INVALID if there is none. | ||
| 39 | */ | ||
| 40 | u16 cec_get_edid_phys_addr(const u8 *edid, unsigned int size, | ||
| 41 | unsigned int *offset); | ||
| 42 | |||
| 43 | /** | ||
| 44 | * cec_set_edid_phys_addr() - find and set the physical address | ||
| 45 | * | ||
| 46 | * @edid: pointer to the EDID data | ||
| 47 | * @size: size in bytes of the EDID data | ||
| 48 | * @phys_addr: the new physical address | ||
| 49 | * | ||
| 50 | * This function finds the location of the physical address in the EDID | ||
| 51 | * and fills in the given physical address and updates the checksum | ||
| 52 | * at the end of the EDID block. It does nothing if the EDID doesn't | ||
| 53 | * contain a physical address. | ||
| 54 | */ | ||
| 55 | void cec_set_edid_phys_addr(u8 *edid, unsigned int size, u16 phys_addr); | ||
| 56 | |||
| 57 | /** | ||
| 58 | * cec_phys_addr_for_input() - calculate the PA for an input | ||
| 59 | * | ||
| 60 | * @phys_addr: the physical address of the parent | ||
| 61 | * @input: the number of the input port, must be between 1 and 15 | ||
| 62 | * | ||
| 63 | * This function calculates a new physical address based on the input | ||
| 64 | * port number. For example: | ||
| 65 | * | ||
| 66 | * PA = 0.0.0.0 and input = 2 becomes 2.0.0.0 | ||
| 67 | * | ||
| 68 | * PA = 3.0.0.0 and input = 1 becomes 3.1.0.0 | ||
| 69 | * | ||
| 70 | * PA = 3.2.1.0 and input = 5 becomes 3.2.1.5 | ||
| 71 | * | ||
| 72 | * PA = 3.2.1.3 and input = 5 becomes f.f.f.f since it maxed out the depth. | ||
| 73 | * | ||
| 74 | * Return: the new physical address or CEC_PHYS_ADDR_INVALID. | ||
| 75 | */ | ||
| 76 | u16 cec_phys_addr_for_input(u16 phys_addr, u8 input); | ||
| 77 | |||
| 78 | /** | ||
| 79 | * cec_phys_addr_validate() - validate a physical address from an EDID | ||
| 80 | * | ||
| 81 | * @phys_addr: the physical address to validate | ||
| 82 | * @parent: if not %NULL, then this is filled with the parents PA. | ||
| 83 | * @port: if not %NULL, then this is filled with the input port. | ||
| 84 | * | ||
| 85 | * This validates a physical address as read from an EDID. If the | ||
| 86 | * PA is invalid (such as 1.0.1.0 since '0' is only allowed at the end), | ||
| 87 | * then it will return -EINVAL. | ||
| 88 | * | ||
| 89 | * The parent PA is passed into %parent and the input port is passed into | ||
| 90 | * %port. For example: | ||
| 91 | * | ||
| 92 | * PA = 0.0.0.0: has parent 0.0.0.0 and input port 0. | ||
| 93 | * | ||
| 94 | * PA = 1.0.0.0: has parent 0.0.0.0 and input port 1. | ||
| 95 | * | ||
| 96 | * PA = 3.2.0.0: has parent 3.0.0.0 and input port 2. | ||
| 97 | * | ||
| 98 | * PA = f.f.f.f: has parent f.f.f.f and input port 0. | ||
| 99 | * | ||
| 100 | * Return: 0 if the PA is valid, -EINVAL if not. | ||
| 101 | */ | ||
| 102 | int cec_phys_addr_validate(u16 phys_addr, u16 *parent, u16 *port); | ||
| 103 | |||
| 104 | #endif /* _MEDIA_CEC_EDID_H */ | ||
| @@ -0,0 +1,232 @@ | |||
| 1 | /* | ||
| 2 | * cec - HDMI Consumer Electronics Control support header | ||
| 3 | * | ||
| 4 | * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved. | ||
| 5 | * | ||
| 6 | * This program is free software; you may redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; version 2 of the License. | ||
| 9 | * | ||
| 10 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
| 11 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
| 12 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
| 13 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||
| 14 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
| 15 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
| 16 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| 17 | * SOFTWARE. | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef _MEDIA_CEC_H | ||
| 21 | #define _MEDIA_CEC_H | ||
| 22 | |||
| 23 | #include <linux/poll.h> | ||
| 24 | #include <linux/fs.h> | ||
| 25 | #include <linux/debugfs.h> | ||
| 26 | #include <linux/device.h> | ||
| 27 | #include <linux/cdev.h> | ||
| 28 | #include <linux/kthread.h> | ||
| 29 | #include <linux/timer.h> | ||
| 30 | #include <linux/cec-funcs.h> | ||
| 31 | #include <media/rc-core.h> | ||
| 32 | #include <media/cec-edid.h> | ||
| 33 | |||
| 34 | /** | ||
| 35 | * struct cec_devnode - cec device node | ||
| 36 | * @dev: cec device | ||
| 37 | * @cdev: cec character device | ||
| 38 | * @parent: parent device | ||
| 39 | * @minor: device node minor number | ||
| 40 | * @registered: the device was correctly registered | ||
| 41 | * @unregistered: the device was unregistered | ||
| 42 | * @fhs_lock: lock to control access to the filehandle list | ||
| 43 | * @fhs: the list of open filehandles (cec_fh) | ||
| 44 | * | ||
| 45 | * This structure represents a cec-related device node. | ||
| 46 | * | ||
| 47 | * The @parent is a physical device. It must be set by core or device drivers | ||
| 48 | * before registering the node. | ||
| 49 | */ | ||
| 50 | struct cec_devnode { | ||
| 51 | /* sysfs */ | ||
| 52 | struct device dev; | ||
| 53 | struct cdev cdev; | ||
| 54 | struct device *parent; | ||
| 55 | |||
| 56 | /* device info */ | ||
| 57 | int minor; | ||
| 58 | bool registered; | ||
| 59 | bool unregistered; | ||
| 60 | struct mutex fhs_lock; | ||
| 61 | struct list_head fhs; | ||
| 62 | }; | ||
| 63 | |||
| 64 | struct cec_adapter; | ||
| 65 | struct cec_data; | ||
| 66 | |||
| 67 | struct cec_data { | ||
| 68 | struct list_head list; | ||
| 69 | struct list_head xfer_list; | ||
| 70 | struct cec_adapter *adap; | ||
| 71 | struct cec_msg msg; | ||
| 72 | struct cec_fh *fh; | ||
| 73 | struct delayed_work work; | ||
| 74 | struct completion c; | ||
| 75 | u8 attempts; | ||
| 76 | bool new_initiator; | ||
| 77 | bool blocking; | ||
| 78 | bool completed; | ||
| 79 | }; | ||
| 80 | |||
| 81 | struct cec_msg_entry { | ||
| 82 | struct list_head list; | ||
| 83 | struct cec_msg msg; | ||
| 84 | }; | ||
| 85 | |||
| 86 | #define CEC_NUM_EVENTS CEC_EVENT_LOST_MSGS | ||
| 87 | |||
| 88 | struct cec_fh { | ||
| 89 | struct list_head list; | ||
| 90 | struct list_head xfer_list; | ||
| 91 | struct cec_adapter *adap; | ||
| 92 | u8 mode_initiator; | ||
| 93 | u8 mode_follower; | ||
| 94 | |||
| 95 | /* Events */ | ||
| 96 | wait_queue_head_t wait; | ||
| 97 | unsigned int pending_events; | ||
| 98 | struct cec_event events[CEC_NUM_EVENTS]; | ||
| 99 | struct mutex lock; | ||
| 100 | struct list_head msgs; /* queued messages */ | ||
| 101 | unsigned int queued_msgs; | ||
| 102 | }; | ||
| 103 | |||
| 104 | #define CEC_SIGNAL_FREE_TIME_RETRY 3 | ||
| 105 | #define CEC_SIGNAL_FREE_TIME_NEW_INITIATOR 5 | ||
| 106 | #define CEC_SIGNAL_FREE_TIME_NEXT_XFER 7 | ||
| 107 | |||
| 108 | /* The nominal data bit period is 2.4 ms */ | ||
| 109 | #define CEC_FREE_TIME_TO_USEC(ft) ((ft) * 2400) | ||
| 110 | |||
| 111 | struct cec_adap_ops { | ||
| 112 | /* Low-level callbacks */ | ||
| 113 | int (*adap_enable)(struct cec_adapter *adap, bool enable); | ||
| 114 | int (*adap_monitor_all_enable)(struct cec_adapter *adap, bool enable); | ||
| 115 | int (*adap_log_addr)(struct cec_adapter *adap, u8 logical_addr); | ||
| 116 | int (*adap_transmit)(struct cec_adapter *adap, u8 attempts, | ||
| 117 | u32 signal_free_time, struct cec_msg *msg); | ||
| 118 | void (*adap_status)(struct cec_adapter *adap, struct seq_file *file); | ||
| 119 | |||
| 120 | /* High-level CEC message callback */ | ||
| 121 | int (*received)(struct cec_adapter *adap, struct cec_msg *msg); | ||
| 122 | }; | ||
| 123 | |||
| 124 | /* | ||
| 125 | * The minimum message length you can receive (excepting poll messages) is 2. | ||
| 126 | * With a transfer rate of at most 36 bytes per second this makes 18 messages | ||
| 127 | * per second worst case. | ||
| 128 | * | ||
| 129 | * We queue at most 3 seconds worth of messages. The CEC specification requires | ||
| 130 | * that messages are replied to within a second, so 3 seconds should give more | ||
| 131 | * than enough margin. Since most messages are actually more than 2 bytes, this | ||
| 132 | * is in practice a lot more than 3 seconds. | ||
| 133 | */ | ||
| 134 | #define CEC_MAX_MSG_QUEUE_SZ (18 * 3) | ||
| 135 | |||
| 136 | struct cec_adapter { | ||
| 137 | struct module *owner; | ||
| 138 | char name[32]; | ||
| 139 | struct cec_devnode devnode; | ||
| 140 | struct mutex lock; | ||
| 141 | struct rc_dev *rc; | ||
| 142 | |||
| 143 | struct list_head transmit_queue; | ||
| 144 | struct list_head wait_queue; | ||
| 145 | struct cec_data *transmitting; | ||
| 146 | |||
| 147 | struct task_struct *kthread_config; | ||
| 148 | struct completion config_completion; | ||
| 149 | |||
| 150 | struct task_struct *kthread; | ||
| 151 | wait_queue_head_t kthread_waitq; | ||
| 152 | wait_queue_head_t waitq; | ||
| 153 | |||
| 154 | const struct cec_adap_ops *ops; | ||
| 155 | void *priv; | ||
| 156 | u32 capabilities; | ||
| 157 | u8 available_log_addrs; | ||
| 158 | |||
| 159 | u16 phys_addr; | ||
| 160 | bool is_configuring; | ||
| 161 | bool is_configured; | ||
| 162 | u32 monitor_all_cnt; | ||
| 163 | u32 follower_cnt; | ||
| 164 | struct cec_fh *cec_follower; | ||
| 165 | struct cec_fh *cec_initiator; | ||
| 166 | bool passthrough; | ||
| 167 | struct cec_log_addrs log_addrs; | ||
| 168 | |||
| 169 | struct dentry *cec_dir; | ||
| 170 | struct dentry *status_file; | ||
| 171 | |||
| 172 | u16 phys_addrs[15]; | ||
| 173 | u32 sequence; | ||
| 174 | |||
| 175 | char input_name[32]; | ||
| 176 | char input_phys[32]; | ||
| 177 | char input_drv[32]; | ||
| 178 | }; | ||
| 179 | |||
| 180 | static inline bool cec_has_log_addr(const struct cec_adapter *adap, u8 log_addr) | ||
| 181 | { | ||
| 182 | return adap->log_addrs.log_addr_mask & (1 << log_addr); | ||
| 183 | } | ||
| 184 | |||
| 185 | static inline bool cec_is_sink(const struct cec_adapter *adap) | ||
| 186 | { | ||
| 187 | return adap->phys_addr == 0; | ||
| 188 | } | ||
| 189 | |||
| 190 | #if IS_ENABLED(CONFIG_MEDIA_CEC) | ||
| 191 | struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, | ||
| 192 | void *priv, const char *name, u32 caps, u8 available_las, | ||
| 193 | struct device *parent); | ||
| 194 | int cec_register_adapter(struct cec_adapter *adap); | ||
| 195 | void cec_unregister_adapter(struct cec_adapter *adap); | ||
| 196 | void cec_delete_adapter(struct cec_adapter *adap); | ||
| 197 | |||
| 198 | int cec_s_log_addrs(struct cec_adapter *adap, struct cec_log_addrs *log_addrs, | ||
| 199 | bool block); | ||
| 200 | void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, | ||
| 201 | bool block); | ||
| 202 | int cec_transmit_msg(struct cec_adapter *adap, struct cec_msg *msg, | ||
| 203 | bool block); | ||
| 204 | |||
| 205 | /* Called by the adapter */ | ||
| 206 | void cec_transmit_done(struct cec_adapter *adap, u8 status, u8 arb_lost_cnt, | ||
| 207 | u8 nack_cnt, u8 low_drive_cnt, u8 error_cnt); | ||
| 208 | void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg); | ||
| 209 | |||
| 210 | #else | ||
| 211 | |||
| 212 | static inline int cec_register_adapter(struct cec_adapter *adap) | ||
| 213 | { | ||
| 214 | return 0; | ||
| 215 | } | ||
| 216 | |||
| 217 | static inline void cec_unregister_adapter(struct cec_adapter *adap) | ||
| 218 | { | ||
| 219 | } | ||
| 220 | |||
| 221 | static inline void cec_delete_adapter(struct cec_adapter *adap) | ||
| 222 | { | ||
| 223 | } | ||
| 224 | |||
| 225 | static inline void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, | ||
| 226 | bool block) | ||
| 227 | { | ||
| 228 | } | ||
| 229 | |||
| 230 | #endif | ||
| 231 | |||
| 232 | #endif /* _MEDIA_CEC_H */ | ||
| @@ -32,11 +32,7 @@ struct adv7511_monitor_detect { · 31 unmodified lines | |||
| 32 | struct adv7511_edid_detect { | 32 | struct adv7511_edid_detect { |
| 33 | int present; | 33 | int present; |
| 34 | int segment; | 34 | int segment; |
| 35 | }; | 35 | uint16_t phys_addr; |
| 36 | |||
| 37 | struct adv7511_cec_arg { | ||
| 38 | void *arg; | ||
| 39 | u32 f_flags; | ||
| 40 | }; | 36 | }; |
| 41 | 37 | ||
| 42 | struct adv7511_platform_data { | 38 | struct adv7511_platform_data { |
| @@ -31,6 +31,7 @@ enum rc_type { · 30 unmodified lines | |||
| 31 | RC_TYPE_RC6_MCE = 16, /* MCE (Philips RC6-6A-32 subtype) protocol */ | 31 | RC_TYPE_RC6_MCE = 16, /* MCE (Philips RC6-6A-32 subtype) protocol */ |
| 32 | RC_TYPE_SHARP = 17, /* Sharp protocol */ | 32 | RC_TYPE_SHARP = 17, /* Sharp protocol */ |
| 33 | RC_TYPE_XMP = 18, /* XMP protocol */ | 33 | RC_TYPE_XMP = 18, /* XMP protocol */ |
| 34 | RC_TYPE_CEC = 19, /* CEC protocol */ | ||
| 34 | }; | 35 | }; |
| 35 | 36 | ||
| 36 | #define RC_BIT_NONE 0ULL | 37 | #define RC_BIT_NONE 0ULL |
| @@ -53,6 +54,7 @@ enum rc_type { · 16 unmodified lines | |||
| 53 | #define RC_BIT_RC6_MCE (1ULL << RC_TYPE_RC6_MCE) | 54 | #define RC_BIT_RC6_MCE (1ULL << RC_TYPE_RC6_MCE) |
| 54 | #define RC_BIT_SHARP (1ULL << RC_TYPE_SHARP) | 55 | #define RC_BIT_SHARP (1ULL << RC_TYPE_SHARP) |
| 55 | #define RC_BIT_XMP (1ULL << RC_TYPE_XMP) | 56 | #define RC_BIT_XMP (1ULL << RC_TYPE_XMP) |
| 57 | #define RC_BIT_CEC (1ULL << RC_TYPE_CEC) | ||
| 56 | 58 | ||
| 57 | #define RC_BIT_ALL (RC_BIT_UNKNOWN | RC_BIT_OTHER | \ | 59 | #define RC_BIT_ALL (RC_BIT_UNKNOWN | RC_BIT_OTHER | \ |
| 58 | RC_BIT_RC5 | RC_BIT_RC5X | RC_BIT_RC5_SZ | \ | 60 | RC_BIT_RC5 | RC_BIT_RC5X | RC_BIT_RC5_SZ | \ |
| @@ -61,7 +63,7 @@ enum rc_type { · 2 unmodified lines | |||
| 61 | RC_BIT_NEC | RC_BIT_SANYO | RC_BIT_MCE_KBD | \ | 63 | RC_BIT_NEC | RC_BIT_SANYO | RC_BIT_MCE_KBD | \ |
| 62 | RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 | RC_BIT_RC6_6A_24 | \ | 64 | RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 | RC_BIT_RC6_6A_24 | \ |
| 63 | RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE | RC_BIT_SHARP | \ | 65 | RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE | RC_BIT_SHARP | \ |
| 64 | RC_BIT_XMP) | 66 | RC_BIT_XMP | RC_BIT_CEC) |
| 65 | 67 | ||
| 66 | 68 | ||
| 67 | #define RC_SCANCODE_UNKNOWN(x) (x) | 69 | #define RC_SCANCODE_UNKNOWN(x) (x) |
| @@ -123,6 +125,7 @@ void rc_map_init(void); · 55 unmodified lines | |||
| 123 | #define RC_MAP_BEHOLD_COLUMBUS "rc-behold-columbus" | 125 | #define RC_MAP_BEHOLD_COLUMBUS "rc-behold-columbus" |
| 124 | #define RC_MAP_BEHOLD "rc-behold" | 126 | #define RC_MAP_BEHOLD "rc-behold" |
| 125 | #define RC_MAP_BUDGET_CI_OLD "rc-budget-ci-old" | 127 | #define RC_MAP_BUDGET_CI_OLD "rc-budget-ci-old" |
| 128 | #define RC_MAP_CEC "rc-cec" | ||
| 126 | #define RC_MAP_CINERGY_1400 "rc-cinergy-1400" | 129 | #define RC_MAP_CINERGY_1400 "rc-cinergy-1400" |
| 127 | #define RC_MAP_CINERGY "rc-cinergy" | 130 | #define RC_MAP_CINERGY "rc-cinergy" |
| 128 | #define RC_MAP_DELOCK_61959 "rc-delock-61959" | 131 | #define RC_MAP_DELOCK_61959 "rc-delock-61959" |
| @@ -611,6 +611,37 @@ · 610 unmodified lines | |||
| 611 | #define KEY_KBDINPUTASSIST_ACCEPT 0x264 | 611 | #define KEY_KBDINPUTASSIST_ACCEPT 0x264 |
| 612 | #define KEY_KBDINPUTASSIST_CANCEL 0x265 | 612 | #define KEY_KBDINPUTASSIST_CANCEL 0x265 |
| 613 | 613 | ||
| 614 | /* Diagonal movement keys */ | ||
| 615 | #define KEY_RIGHT_UP 0x266 | ||
| 616 | #define KEY_RIGHT_DOWN 0x267 | ||
| 617 | #define KEY_LEFT_UP 0x268 | ||
| 618 | #define KEY_LEFT_DOWN 0x269 | ||
| 619 | |||
| 620 | #define KEY_ROOT_MENU 0x26a /* Show Device's Root Menu */ | ||
| 621 | /* Show Top Menu of the Media (e.g. DVD) */ | ||
| 622 | #define KEY_MEDIA_TOP_MENU 0x26b | ||
| 623 | #define KEY_NUMERIC_11 0x26c | ||
| 624 | #define KEY_NUMERIC_12 0x26d | ||
| 625 | /* | ||
| 626 | * Toggle Audio Description: refers to an audio service that helps blind and | ||
| 627 | * visually impaired consumers understand the action in a program. Note: in | ||
| 628 | * some countries this is referred to as "Video Description". | ||
| 629 | */ | ||
| 630 | #define KEY_AUDIO_DESC 0x26e | ||
| 631 | #define KEY_3D_MODE 0x26f | ||
| 632 | #define KEY_NEXT_FAVORITE 0x270 | ||
| 633 | #define KEY_STOP_RECORD 0x271 | ||
| 634 | #define KEY_PAUSE_RECORD 0x272 | ||
| 635 | #define KEY_VOD 0x273 /* Video on Demand */ | ||
| 636 | #define KEY_UNMUTE 0x274 | ||
| 637 | #define KEY_FASTREVERSE 0x275 | ||
| 638 | #define KEY_SLOWREVERSE 0x276 | ||
| 639 | /* | ||
| 640 | * Control a data application associated with the currently viewed channel, | ||
| 641 | * e.g. teletext or data broadcast application (MHEG, MHP, HbbTV, etc.) | ||
| 642 | */ | ||
| 643 | #define KEY_DATA 0x275 | ||
| 644 | |||
| 614 | #define BTN_TRIGGER_HAPPY 0x2c0 | 645 | #define BTN_TRIGGER_HAPPY 0x2c0 |
| 615 | #define BTN_TRIGGER_HAPPY1 0x2c0 | 646 | #define BTN_TRIGGER_HAPPY1 0x2c0 |
| 616 | #define BTN_TRIGGER_HAPPY2 0x2c1 | 647 | #define BTN_TRIGGER_HAPPY2 0x2c1 |
| @@ -247,6 +247,7 @@ struct input_mask { · 246 unmodified lines | |||
| 247 | #define BUS_ATARI 0x1B | 247 | #define BUS_ATARI 0x1B |
| 248 | #define BUS_SPI 0x1C | 248 | #define BUS_SPI 0x1C |
| 249 | #define BUS_RMI 0x1D | 249 | #define BUS_RMI 0x1D |
| 250 | #define BUS_CEC 0x1E | ||
| 250 | 251 | ||
| 251 | /* | 252 | /* |
| 252 | * MT_TOOL types | 253 | * MT_TOOL types |