* 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 | 272 | !Iinclude/media/media-devnode.h |
| 273 | 273 | !Iinclude/media/media-entity.h |
| 274 | 274 | </sect1> |
| 275 | + <sect1><title>Consumer Electronics Control devices</title> | |
| 276 | +!Iinclude/media/cec-edid.h | |
| 277 | + </sect1> | |
| 275 | 278 | |
| 276 | 279 | </chapter> |
| 277 | 280 | |
| @@ -64,6 +64,7 @@ IOCTLS = \ · 63 unmodified lines | ||
| 64 | 64 | $(shell perl -ne 'print "$$1 " if /\#define\s+([A-Z][^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/dvb/net.h) \ |
| 65 | 65 | $(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/dvb/video.h) \ |
| 66 | 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 | 68 | $(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/v4l2-subdev.h) \ |
| 68 | 69 | |
| 69 | 70 | DEFINES = \ |
| @@ -100,6 +101,7 @@ STRUCTS = \ · 30 unmodified lines | ||
| 100 | 101 | $(shell perl -ne 'print "$$1 " if (/^struct\s+([^\s]+)\s+/ && !/_old/)' $(srctree)/include/uapi/linux/dvb/net.h) \ |
| 101 | 102 | $(shell perl -ne 'print "$$1 " if (/^struct\s+([^\s]+)\s+/)' $(srctree)/include/uapi/linux/dvb/video.h) \ |
| 102 | 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 | 105 | $(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/uapi/linux/v4l2-subdev.h) \ |
| 104 | 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 | 342 | <subtitle>Specification Version 1.4a</subtitle> |
| 343 | 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 | 355 | <biblioentry id="dp"> |
| 346 | 356 | <abbrev>DP</abbrev> |
| 347 | 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 | 75 | </mediaobject> |
| 76 | 76 | </figure> |
| 77 | 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 | 79 | <para>The first part covers radio, video capture and output, |
| 80 | 80 | cameras, analog TV devices and codecs.</para> |
| 81 | 81 | <para>The second part covers the |
| @@ -87,6 +87,7 @@ · 5 unmodified lines | ||
| 87 | 87 | <xref linkend="fe-delivery-system-t" />.</para> |
| 88 | 88 | <para>The third part covers the Remote Controller API.</para> |
| 89 | 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 | 91 | <para>It should also be noted that a media device may also have audio |
| 91 | 92 | components, like mixers, PCM capture, PCM playback, etc, which |
| 92 | 93 | are controlled via ALSA API.</para> |
| @@ -107,6 +108,9 @@ · 14 unmodified lines | ||
| 107 | 108 | <part id="media_common"> |
| 108 | 109 | &sub-media-controller; |
| 109 | 110 | </part> |
| 111 | +<part id="cec"> | |
| 112 | +&sub-cec-api; | |
| 113 | +</part> | |
| 110 | 114 | |
| 111 | 115 | <chapter id="gen_errors"> |
| 112 | 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 | 74 | Section 12: Formats |
| 75 | 75 | Section 13: Capture Overlay |
| 76 | 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 | 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 | 365 | is provided. You can only set the EDID for HDMI inputs. Internally, however, |
| 365 | 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 | 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 | 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 | 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 | 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 | 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 | 1148 | - Fix sequence/field numbering when looping of video with alternate fields |
| 1122 | 1149 | - Add support for V4L2_CID_BG_COLOR for video outputs |
| 1123 | 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 | 1151 | - Improve pixel aspect support in the tpg code by passing a real v4l2_fract |
| 1127 | 1152 | - Use per-queue locks and/or per-device locks to improve throughput |
| 1128 | 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 | 1158 | - Make a thread for the RDS generation, that would help in particular for the |
| 1134 | 1159 | "Controls" RDS Rx I/O Mode as the read-only RDS controls could be updated |
| 1135 | 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 | 1642 | S: Maintained |
| 1643 | 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 | 1652 | ARM/SAMSUNG S5P SERIES JPEG CODEC SUPPORT |
| 1646 | 1653 | M: Andrzej Pietrasiewicz <andrzej.p@samsung.com> |
| 1647 | 1654 | M: Jacek Anaszewski <j.anaszewski@samsung.com> |
| @@ -2847,6 +2854,22 @@ F: drivers/net/ieee802154/cc2520.c · 1199 unmodified lines | ||
| 2847 | 2854 | F: include/linux/spi/cc2520.h |
| 2848 | 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 | 2873 | CELL BROADBAND ENGINE ARCHITECTURE |
| 2851 | 2874 | M: Arnd Bergmann <arnd@arndb.de> |
| 2852 | 2875 | L: linuxppc-dev@lists.ozlabs.org |
| @@ -80,6 +80,9 @@ config MEDIA_RC_SUPPORT · 79 unmodified lines | ||
| 80 | 80 | |
| 81 | 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 | 87 | # Media controller |
| 85 | 88 | # Selectable only for webcam/grabbers, as other drivers don't use it |
| @@ -2,6 +2,8 @@ · 1 unmodified line | ||
| 2 | 2 | # Makefile for the kernel multimedia device drivers. |
| 3 | 3 | # |
| 4 | 4 | |
| 5 | +obj-$(CONFIG_MEDIA_CEC_EDID) += cec-edid.o | |
| 6 | + | |
| 5 | 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 | 209 | depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API |
| 210 | 210 | depends on GPIOLIB || COMPILE_TEST |
| 211 | 211 | select HDMI |
| 212 | + select MEDIA_CEC_EDID | |
| 212 | 213 | ---help--- |
| 213 | 214 | Support for the Analog Devices ADV7604 video decoder. |
| 214 | 215 | |
| @@ -218,10 +219,18 @@ config VIDEO_ADV7604 · 3 unmodified lines | ||
| 218 | 219 | To compile this driver as a module, choose M here: the |
| 219 | 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 | 229 | config VIDEO_ADV7842 |
| 222 | 230 | tristate "Analog Devices ADV7842 decoder" |
| 223 | 231 | depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API |
| 224 | 232 | select HDMI |
| 233 | + select MEDIA_CEC_EDID | |
| 225 | 234 | ---help--- |
| 226 | 235 | Support for the Analog Devices ADV7842 video decoder. |
| 227 | 236 | |
| @@ -231,6 +240,13 @@ config VIDEO_ADV7842 · 3 unmodified lines | ||
| 231 | 240 | To compile this driver as a module, choose M here: the |
| 232 | 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 | 250 | config VIDEO_BT819 |
| 235 | 251 | tristate "BT819A VideoStream decoder" |
| 236 | 252 | depends on VIDEO_V4L2 && I2C |
| @@ -447,6 +463,7 @@ config VIDEO_ADV7511 · 210 unmodified lines | ||
| 447 | 463 | tristate "Analog Devices ADV7511 encoder" |
| 448 | 464 | depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API |
| 449 | 465 | select HDMI |
| 466 | + select MEDIA_CEC_EDID | |
| 450 | 467 | ---help--- |
| 451 | 468 | Support for the Analog Devices ADV7511 video encoder. |
| 452 | 469 | |
| @@ -455,6 +472,13 @@ config VIDEO_ADV7511 · 2 unmodified lines | ||
| 455 | 472 | To compile this driver as a module, choose M here: the |
| 456 | 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 | 482 | config VIDEO_AD9389B |
| 459 | 483 | tristate "Analog Devices AD9389B encoder" |
| 460 | 484 | depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API |
| @@ -33,6 +33,7 @@ · 32 unmodified lines | ||
| 33 | 33 | #include <media/v4l2-ctrls.h> |
| 34 | 34 | #include <media/v4l2-dv-timings.h> |
| 35 | 35 | #include <media/i2c/adv7511.h> |
| 36 | +#include <media/cec.h> | |
| 36 | 37 | |
| 37 | 38 | static int debug; |
| 38 | 39 | module_param(debug, int, 0644); |
| @@ -59,6 +60,8 @@ MODULE_LICENSE("GPL v2"); · 20 unmodified lines | ||
| 59 | 60 | #define ADV7511_MIN_PIXELCLOCK 20000000 |
| 60 | 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 | 93 | struct v4l2_ctrl_handler hdl; |
| 91 | 94 | int chip_revision; |
| 92 | 95 | u8 i2c_edid_addr; |
| 93 | − u8 i2c_cec_addr; | |
| 94 | 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 | 105 | /* Is the adv7511 powered on? */ |
| 96 | 106 | bool power_on; |
| 97 | 107 | /* Did we receive hotplug and rx-sense signals? */ |
| 98 | 108 | bool have_monitor; |
| 109 | + bool enabled_irq; | |
| 99 | 110 | /* timings from s_dv_timings */ |
| 100 | 111 | struct v4l2_dv_timings dv_timings; |
| 101 | 112 | u32 fmt_code; |
| @@ -227,7 +238,7 @@ static int adv_smbus_read_i2c_block_data(struct i2c_client *client, · 125 unmodified lines | ||
| 227 | 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 | 243 | struct adv7511_state *state = get_adv7511_state(sd); |
| 233 | 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 | 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 | 284 | static int adv7511_pktmem_rd(struct v4l2_subdev *sd, u8 reg) |
| 246 | 285 | { |
| 247 | 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 | 464 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
| 426 | 465 | static void adv7511_inv_register(struct v4l2_subdev *sd) |
| 427 | 466 | { |
| 467 | + struct adv7511_state *state = get_adv7511_state(sd); | |
| 468 | + | |
| 428 | 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 | 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 | 478 | reg->size = 1; |
| 434 | 479 | switch (reg->reg >> 8) { |
| 435 | 480 | case 0: |
| 436 | 481 | reg->val = adv7511_rd(sd, reg->reg & 0xff); |
| 437 | 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 | 489 | default: |
| 439 | 490 | v4l2_info(sd, "Register %03llx not supported\n", reg->reg); |
| 440 | 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 | 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 | 501 | switch (reg->reg >> 8) { |
| 449 | 502 | case 0: |
| 450 | 503 | adv7511_wr(sd, reg->reg & 0xff, reg->val & 0xff); |
| 451 | 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 | 511 | default: |
| 453 | 512 | v4l2_info(sd, "Register %03llx not supported\n", reg->reg); |
| 454 | 513 | adv7511_inv_register(sd); |
| @@ -536,6 +595,7 @@ static int adv7511_log_status(struct v4l2_subdev *sd) · 81 unmodified lines | ||
| 536 | 595 | { |
| 537 | 596 | struct adv7511_state *state = get_adv7511_state(sd); |
| 538 | 597 | struct adv7511_state_edid *edid = &state->edid; |
| 598 | + int i; | |
| 539 | 599 | |
| 540 | 600 | static const char * const states[] = { |
| 541 | 601 | "in reset", |
| @@ -605,7 +665,23 @@ static int adv7511_log_status(struct v4l2_subdev *sd) · 63 unmodified lines | ||
| 605 | 665 | else |
| 606 | 666 | v4l2_info(sd, "no timings set\n"); |
| 607 | 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 | 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 | 685 | v4l2_info(sd, "i2c pktmem addr: 0x%x\n", state->i2c_pktmem_addr); |
| 610 | 686 | return 0; |
| 611 | 687 | } |
| @@ -663,15 +739,197 @@ static int adv7511_s_power(struct v4l2_subdev *sd, int on) · 51 unmodified lines | ||
| 663 | 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 | 919 | /* Enable interrupts */ |
| 667 | 920 | static void adv7511_set_isr(struct v4l2_subdev *sd, bool enable) |
| 668 | 921 | { |
| 922 | + struct adv7511_state *state = get_adv7511_state(sd); | |
| 669 | 923 | u8 irqs = MASK_ADV7511_HPD_INT | MASK_ADV7511_MSEN_INT; |
| 670 | 924 | u8 irqs_rd; |
| 671 | 925 | int retries = 100; |
| 672 | 926 | |
| 673 | 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 | 933 | /* The datasheet says that the EDID ready interrupt should be |
| 676 | 934 | disabled if there is no hotplug. */ |
| 677 | 935 | if (!enable) |
| @@ -679,6 +937,9 @@ static void adv7511_set_isr(struct v4l2_subdev *sd, bool enable) · 1 unmodified line | ||
| 679 | 937 | else if (adv7511_have_hotplug(sd)) |
| 680 | 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 | 944 | * This i2c write can fail (approx. 1 in 1000 writes). But it |
| 684 | 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 | 962 | static int adv7511_isr(struct v4l2_subdev *sd, u32 status, bool *handled) |
| 702 | 963 | { |
| 703 | 964 | u8 irq_status; |
| 965 | + u8 cec_irq; | |
| 704 | 966 | |
| 705 | 967 | /* disable interrupts to prevent a race condition */ |
| 706 | 968 | adv7511_set_isr(sd, false); |
| 707 | 969 | irq_status = adv7511_rd(sd, 0x96); |
| 970 | + cec_irq = adv7511_rd(sd, 0x97); | |
| 708 | 971 | /* clear detected interrupts */ |
| 709 | 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 | 978 | if (irq_status & (MASK_ADV7511_HPD_INT | MASK_ADV7511_MSEN_INT)) |
| 714 | 979 | adv7511_check_monitor_present_status(sd); |
| 715 | 980 | if (irq_status & MASK_ADV7511_EDID_RDY_INT) |
| 716 | 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 | 1012 | /* enable interrupts */ |
| 719 | 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 | 1477 | /* We failed to read the EDID, so send an event for this. */ |
| 1184 | 1478 | ed.present = false; |
| 1185 | 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 | 1482 | v4l2_subdev_notify(sd, ADV7511_EDID_DETECT, (void *)&ed); |
| 1187 | 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 | 1703 | v4l2_dbg(1, debug, sd, "%s: edid complete with %d segment(s)\n", __func__, state->edid.segments); |
| 1408 | 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 | 1708 | /* report when we have all segments |
| 1411 | 1709 | but report only for segment 0 |
| 1412 | 1710 | */ |
| 1413 | 1711 | ed.present = true; |
| 1414 | 1712 | ed.segment = 0; |
| 1415 | 1713 | state->edid_detect_counter++; |
| 1714 | + cec_s_phys_addr(state->cec_adap, ed.phys_addr, false); | |
| 1416 | 1715 | v4l2_subdev_notify(sd, ADV7511_EDID_DETECT, (void *)&ed); |
| 1417 | 1716 | return ed.present; |
| 1418 | 1717 | } |
| @@ -1420,17 +1719,43 @@ static bool adv7511_check_edid_status(struct v4l2_subdev *sd) · 1 unmodified line | ||
| 1420 | 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 | 1746 | /* Setup ADV7511 */ |
| 1425 | 1747 | static void adv7511_init_setup(struct v4l2_subdev *sd) |
| 1426 | 1748 | { |
| 1427 | 1749 | struct adv7511_state *state = get_adv7511_state(sd); |
| 1428 | 1750 | struct adv7511_state_edid *edid = &state->edid; |
| 1751 | + u32 cec_clk = state->pdata.cec_clk; | |
| 1752 | + u8 ratio; | |
| 1429 | 1753 | |
| 1430 | 1754 | v4l2_dbg(1, debug, sd, "%s\n", __func__); |
| 1431 | 1755 | |
| 1432 | 1756 | /* clear all interrupts */ |
| 1433 | 1757 | adv7511_wr(sd, 0x96, 0xff); |
| 1758 | + adv7511_wr(sd, 0x97, 0xff); | |
| 1434 | 1759 | /* |
| 1435 | 1760 | * Stop HPD from resetting a lot of registers. |
| 1436 | 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 | 1767 | adv7511_set_isr(sd, false); |
| 1443 | 1768 | adv7511_s_stream(sd, false); |
| 1444 | 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 | 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 | 1820 | client->addr << 1); |
| 1477 | 1821 | |
| 1478 | 1822 | v4l2_i2c_subdev_init(sd, client, &adv7511_ops); |
| 1823 | + sd->internal_ops = &adv7511_int_ops; | |
| 1479 | 1824 | |
| 1480 | 1825 | hdl = &state->hdl; |
| 1481 | 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 | 1861 | chip_id[0] = adv7511_rd(sd, 0xf5); |
| 1517 | 1862 | chip_id[1] = adv7511_rd(sd, 0xf6); |
| 1518 | 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 | 1866 | err = -EIO; |
| 1521 | 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 | 1872 | if (state->i2c_edid == NULL) { |
| 1526 | 1873 | v4l2_err(sd, "failed to register edid i2c client\n"); |
| 1527 | 1874 | err = -ENOMEM; |
| 1528 | 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 | 1898 | state->i2c_pktmem = i2c_new_dummy(client->adapter, state->i2c_pktmem_addr >> 1); |
| 1532 | 1899 | if (state->i2c_pktmem == NULL) { |
| 1533 | 1900 | v4l2_err(sd, "failed to register pktmem i2c client\n"); |
| 1534 | 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 | 1905 | state->work_queue = create_singlethread_workqueue(sd->name); |
| 1540 | 1906 | if (state->work_queue == NULL) { |
| 1541 | 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 | 1912 | INIT_DELAYED_WORK(&state->edid_handler, adv7511_edid_handler); |
| 1547 | 1913 | |
| 1548 | 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 | 1928 | adv7511_set_isr(sd, true); |
| 1550 | 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 | 1935 | err_unreg_pktmem: |
| 1557 | 1936 | i2c_unregister_device(state->i2c_pktmem); |
| 1937 | +err_unreg_cec: | |
| 1938 | + if (state->i2c_cec) | |
| 1939 | + i2c_unregister_device(state->i2c_cec); | |
| 1558 | 1940 | err_unreg_edid: |
| 1559 | 1941 | i2c_unregister_device(state->i2c_edid); |
| 1560 | 1942 | err_entity: |
| @@ -1576,9 +1958,12 @@ static int adv7511_remove(struct i2c_client *client) · 15 unmodified lines | ||
| 1576 | 1958 | v4l2_dbg(1, debug, sd, "%s removed @ 0x%x (%s)\n", client->name, |
| 1577 | 1959 | client->addr << 1, client->adapter->name); |
| 1578 | 1960 | |
| 1961 | + adv7511_set_isr(sd, false); | |
| 1579 | 1962 | adv7511_init_setup(sd); |
| 1580 | 1963 | cancel_delayed_work(&state->edid_handler); |
| 1581 | 1964 | i2c_unregister_device(state->i2c_edid); |
| 1965 | + if (state->i2c_cec) | |
| 1966 | + i2c_unregister_device(state->i2c_cec); | |
| 1582 | 1967 | i2c_unregister_device(state->i2c_pktmem); |
| 1583 | 1968 | destroy_workqueue(state->work_queue); |
| 1584 | 1969 | v4l2_device_unregister_subdev(sd); |
| @@ -40,6 +40,7 @@ · 39 unmodified lines | ||
| 40 | 40 | #include <linux/regmap.h> |
| 41 | 41 | |
| 42 | 42 | #include <media/i2c/adv7604.h> |
| 43 | +#include <media/cec.h> | |
| 43 | 44 | #include <media/v4l2-ctrls.h> |
| 44 | 45 | #include <media/v4l2-device.h> |
| 45 | 46 | #include <media/v4l2-event.h> |
| @@ -80,6 +81,8 @@ MODULE_LICENSE("GPL"); · 34 unmodified lines | ||
| 80 | 81 | |
| 81 | 82 | #define ADV76XX_OP_SWAP_CB_CR (1 << 0) |
| 82 | 83 | |
| 84 | +#define ADV76XX_MAX_ADDRS (3) | |
| 85 | + | |
| 83 | 86 | enum adv76xx_type { |
| 84 | 87 | ADV7604, |
| 85 | 88 | ADV7611, |
| @@ -184,6 +187,12 @@ struct adv76xx_state { · 98 unmodified lines | ||
| 184 | 187 | u16 spa_port_a[2]; |
| 185 | 188 | struct v4l2_fract aspect_ratio; |
| 186 | 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 | 196 | struct workqueue_struct *work_queues; |
| 188 | 197 | struct delayed_work delayed_work_enable_hotplug; |
| 189 | 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 | 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 | 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 | 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 | 433 | static inline int infoframe_read(struct v4l2_subdev *sd, u8 reg) |
| 418 | 434 | { |
| 419 | 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 | 909 | struct adv76xx_state *state = to_state(sd); |
| 894 | 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, | |
| 897 | − info->read_cable_det(sd)); | |
| 913 | + return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, cable_det); | |
| 898 | 914 | } |
| 899 | 915 | |
| 900 | 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 | 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 | 2147 | static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled) |
| 1928 | 2148 | { |
| 1929 | 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 | 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 | 2197 | /* tx 5v detect */ |
| 1973 | 2198 | tx_5v = irq_reg_0x70 & info->cable_det_mask; |
| 1974 | 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 | 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 | 2246 | static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) |
| 2050 | 2247 | { |
| 2051 | 2248 | struct adv76xx_state *state = to_state(sd); |
| 2052 | 2249 | const struct adv76xx_chip_info *info = state->info; |
| 2053 | − int spa_loc; | |
| 2250 | + unsigned int spa_loc; | |
| 2251 | + u16 pa; | |
| 2054 | 2252 | int err; |
| 2055 | 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 | 2279 | edid->blocks = 2; |
| 2082 | 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 | 2287 | v4l2_dbg(2, debug, sd, "%s: write EDID pad %d, edid.present = 0x%x\n", |
| 2086 | 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 | 2292 | adv76xx_set_hpd(state, 0); |
| 2091 | 2293 | rep_write_clr_set(sd, info->edid_enable_reg, 0x0f, 0x00); |
| 2092 | 2294 | |
| 2093 | − spa_loc = get_edid_spa_location(edid->edid); | |
| 2094 | − if (spa_loc < 0) | |
| 2095 | − spa_loc = 0xc0; /* Default value [REF_02, p. 116] */ | |
| 2295 | + /* | |
| 2296 | + * Return an error if no location of the source physical address | |
| 2297 | + * was found. | |
| 2298 | + */ | |
| 2299 | + if (spa_loc == 0) | |
| 2300 | + return -EINVAL; | |
| 2096 | 2301 | |
| 2097 | 2302 | switch (edid->pad) { |
| 2098 | 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 | 2357 | v4l2_err(sd, "error enabling edid (0x%x)\n", state->edid.present); |
| 2153 | 2358 | return -EIO; |
| 2154 | 2359 | } |
| 2360 | + cec_s_phys_addr(state->cec_adap, pa, false); | |
| 2155 | 2361 | |
| 2156 | 2362 | /* enable hotplug after 100 ms */ |
| 2157 | 2363 | queue_delayed_work(state->work_queues, |
| @@ -2276,8 +2482,19 @@ static int adv76xx_log_status(struct v4l2_subdev *sd) · 118 unmodified lines | ||
| 2276 | 2482 | ((edid_enabled & 0x02) ? "Yes" : "No"), |
| 2277 | 2483 | ((edid_enabled & 0x04) ? "Yes" : "No"), |
| 2278 | 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 | 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 | 2499 | v4l2_info(sd, "-----Signal status-----\n"); |
| 2283 | 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 | 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 | 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 | 2673 | /* -------------------------- custom ctrls ---------------------------------- */ |
| 2434 | 2674 | |
| 2435 | 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 | 3333 | id->name, i2c_adapter_id(client->adapter), |
| 3094 | 3334 | client->addr); |
| 3095 | 3335 | sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; |
| 3336 | + sd->internal_ops = &adv76xx_int_ops; | |
| 3096 | 3337 | |
| 3097 | 3338 | /* Configure IO Regmap region */ |
| 3098 | 3339 | err = configure_regmap(state, ADV76XX_PAGE_IO); |
| @@ -3236,6 +3477,18 @@ static int adv76xx_probe(struct i2c_client *client, · 137 unmodified lines | ||
| 3236 | 3477 | err = adv76xx_core_init(sd); |
| 3237 | 3478 | if (err) |
| 3238 | 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 | 3492 | v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, |
| 3240 | 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 | 3517 | struct v4l2_subdev *sd = i2c_get_clientdata(client); |
| 3265 | 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 | 3527 | cancel_delayed_work(&state->delayed_work_enable_hotplug); |
| 3268 | 3528 | destroy_workqueue(state->work_queues); |
| 3269 | 3529 | v4l2_async_unregister_subdev(sd); |
| @@ -39,6 +39,7 @@ · 38 unmodified lines | ||
| 39 | 39 | #include <linux/workqueue.h> |
| 40 | 40 | #include <linux/v4l2-dv-timings.h> |
| 41 | 41 | #include <linux/hdmi.h> |
| 42 | +#include <media/cec.h> | |
| 42 | 43 | #include <media/v4l2-device.h> |
| 43 | 44 | #include <media/v4l2-event.h> |
| 44 | 45 | #include <media/v4l2-ctrls.h> |
| @@ -79,6 +80,8 @@ MODULE_LICENSE("GPL"); · 34 unmodified lines | ||
| 79 | 80 | |
| 80 | 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 | 145 | struct v4l2_ctrl *free_run_color_ctrl_manual; |
| 143 | 146 | struct v4l2_ctrl *free_run_color_ctrl; |
| 144 | 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 | 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 | 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 | 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 | 719 | static void adv7842_delayed_work_enable_hotplug(struct work_struct *work) |
| 700 | 720 | { |
| 701 | 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 | 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 | 785 | static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port) |
| 799 | 786 | { |
| 800 | 787 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
| 801 | 788 | struct adv7842_state *state = to_state(sd); |
| 802 | − const u8 *val = state->hdmi_edid.edid; | |
| 803 | − int spa_loc = edid_spa_location(val); | |
| 789 | + const u8 *edid = state->hdmi_edid.edid; | |
| 790 | + int spa_loc; | |
| 791 | + u16 pa; | |
| 804 | 792 | int err = 0; |
| 805 | 793 | int i; |
| 806 | 794 | |
| 807 | − v4l2_dbg(2, debug, sd, "%s: write EDID on port %c (spa at 0x%x)\n", | |
| 808 | − __func__, (port == ADV7842_EDID_PORT_A) ? 'A' : 'B', spa_loc); | |
| 795 | + v4l2_dbg(2, debug, sd, "%s: write EDID on port %c\n", | |
| 796 | + __func__, (port == ADV7842_EDID_PORT_A) ? 'A' : 'B'); | |
| 809 | 797 | |
| 810 | 798 | /* HPA disable on port A and B */ |
| 811 | 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 | 804 | if (!state->hdmi_edid.present) |
| 817 | 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 | 819 | /* edid segment pointer '0' for HDMI ports */ |
| 820 | 820 | rep_write_and_or(sd, 0x77, 0xef, 0x00); |
| 821 | 821 | |
| 822 | 822 | for (i = 0; !err && i < 256; i += I2C_SMBUS_BLOCK_MAX) |
| 823 | 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 | 825 | if (err) |
| 826 | 826 | return err; |
| 827 | 827 | |
| 828 | − if (spa_loc < 0) | |
| 829 | − spa_loc = 0xc0; /* Default value [REF_02, p. 199] */ | |
| 830 | − | |
| 831 | 828 | if (port == ADV7842_EDID_PORT_A) { |
| 832 | − rep_write(sd, 0x72, val[spa_loc]); | |
| 833 | − rep_write(sd, 0x73, val[spa_loc + 1]); | |
| 829 | + rep_write(sd, 0x72, edid[spa_loc]); | |
| 830 | + rep_write(sd, 0x73, edid[spa_loc + 1]); | |
| 834 | 831 | } else { |
| 835 | − rep_write(sd, 0x74, val[spa_loc]); | |
| 836 | − rep_write(sd, 0x75, val[spa_loc + 1]); | |
| 832 | + rep_write(sd, 0x74, edid[spa_loc]); | |
| 833 | + rep_write(sd, 0x75, edid[spa_loc + 1]); | |
| 837 | 834 | } |
| 838 | 835 | rep_write(sd, 0x76, spa_loc & 0xff); |
| 839 | 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 | 850 | (port == ADV7842_EDID_PORT_A) ? 'A' : 'B'); |
| 854 | 851 | return -EIO; |
| 855 | 852 | } |
| 853 | + cec_s_phys_addr(state->cec_adap, pa, false); | |
| 856 | 854 | |
| 857 | 855 | /* enable hotplug after 200 ms */ |
| 858 | 856 | queue_delayed_work(state->work_queues, |
| @@ -983,20 +981,11 @@ static int adv7842_s_register(struct v4l2_subdev *sd, · 124 unmodified lines | ||
| 983 | 981 | static int adv7842_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd) |
| 984 | 982 | { |
| 985 | 983 | struct adv7842_state *state = to_state(sd); |
| 986 | − int prev = v4l2_ctrl_g_ctrl(state->detect_tx_5v_ctrl); | |
| 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 */ | |
| 984 | + u16 cable_det = adv7842_read_cable_det(sd); | |
| 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) | |
| 998 | − return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, val); | |
| 999 | − return 0; | |
| 988 | + return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, cable_det); | |
| 1000 | 989 | } |
| 1001 | 990 | |
| 1002 | 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 | 2363 | static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled) |
| 2174 | 2364 | { |
| 2175 | 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 | 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 | 2439 | /* tx 5v detect */ |
| 2245 | 2440 | if (irq_status[2] & 0x3) { |
| 2246 | 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 | 2516 | case ADV7842_EDID_PORT_A: |
| 2322 | 2517 | case ADV7842_EDID_PORT_B: |
| 2323 | 2518 | memset(&state->hdmi_edid.edid, 0, 256); |
| 2324 | − if (e->blocks) | |
| 2519 | + if (e->blocks) { | |
| 2325 | 2520 | state->hdmi_edid.present |= 0x04 << e->pad; |
| 2326 | − else | |
| 2521 | + } else { | |
| 2327 | 2522 | state->hdmi_edid.present &= ~(0x04 << e->pad); |
| 2523 | + adv7842_s_detect_tx_5v_ctrl(sd); | |
| 2524 | + } | |
| 2328 | 2525 | memcpy(&state->hdmi_edid.edid, e->edid, 128 * e->blocks); |
| 2329 | 2526 | err = edid_write_hdmi_segment(sd, e->pad); |
| 2330 | 2527 | break; |
| @@ -2512,8 +2709,19 @@ static int adv7842_cp_log_status(struct v4l2_subdev *sd) · 181 unmodified lines | ||
| 2512 | 2709 | v4l2_info(sd, "HPD A %s, B %s\n", |
| 2513 | 2710 | reg_io_0x21 & 0x02 ? "enabled" : "disabled", |
| 2514 | 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 | 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 | 2726 | v4l2_info(sd, "-----Signal status-----\n"); |
| 2519 | 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 | 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 | 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 | 3314 | /* -------------------------- custom ctrls ---------------------------------- */ |
| 3084 | 3315 | |
| 3085 | 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 | 3475 | sd = &state->sd; |
| 3245 | 3476 | v4l2_i2c_subdev_init(sd, client, &adv7842_ops); |
| 3246 | 3477 | sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; |
| 3478 | + sd->internal_ops = &adv7842_int_ops; | |
| 3247 | 3479 | state->mode = pdata->mode; |
| 3248 | 3480 | |
| 3249 | 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 | 3566 | if (err) |
| 3335 | 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 | 3580 | v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, |
| 3338 | 3581 | client->addr << 1, client->adapter->name); |
| 3339 | 3582 | return 0; |
| @@ -3358,7 +3601,6 @@ static int adv7842_remove(struct i2c_client *client) · 18 unmodified lines | ||
| 3358 | 3601 | struct adv7842_state *state = to_state(sd); |
| 3359 | 3602 | |
| 3360 | 3603 | adv7842_irq_enable(sd, false); |
| 3361 | − | |
| 3362 | 3604 | cancel_delayed_work(&state->delayed_work_enable_hotplug); |
| 3363 | 3605 | destroy_workqueue(state->work_queues); |
| 3364 | 3606 | v4l2_device_unregister_subdev(sd); |
| @@ -6,6 +6,7 @@ config VIDEO_VIVID · 5 unmodified lines | ||
| 6 | 6 | select FB_CFB_FILLRECT |
| 7 | 7 | select FB_CFB_COPYAREA |
| 8 | 8 | select FB_CFB_IMAGEBLIT |
| 9 | + select MEDIA_CEC_EDID | |
| 9 | 10 | select VIDEOBUF2_VMALLOC |
| 10 | 11 | select VIDEO_V4L2_TPG |
| 11 | 12 | default n |
| @@ -22,6 +23,13 @@ config VIDEO_VIVID · 10 unmodified lines | ||
| 22 | 23 | Say Y here if you want to test video apps or debug V4L devices. |
| 23 | 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 | 33 | config VIDEO_VIVID_MAX_DEVS |
| 26 | 34 | int "Maximum number of devices" |
| 27 | 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 | 3 | vivid-radio-rx.o vivid-radio-tx.o vivid-radio-common.o \ |
| 4 | 4 | vivid-rds-gen.o vivid-sdr-cap.o vivid-vbi-cap.o vivid-vbi-out.o \ |
| 5 | 5 | vivid-osd.o |
| 6 | +ifeq ($(CONFIG_VIDEO_VIVID_CEC),y) | |
| 7 | + vivid-objs += vivid-cec.o | |
| 8 | +endif | |
| 9 | + | |
| 6 | 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 | 46 | #include "vivid-vbi-cap.h" |
| 47 | 47 | #include "vivid-vbi-out.h" |
| 48 | 48 | #include "vivid-osd.h" |
| 49 | +#include "vivid-cec.h" | |
| 49 | 50 | #include "vivid-ctrls.h" |
| 50 | 51 | |
| 51 | 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 | 685 | dev->input_name_counter[i] = in_type_counter[dev->input_type[i]]++; |
| 685 | 686 | } |
| 686 | 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 | 694 | /* how many outputs do we have and of what type? */ |
| 689 | 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 | 702 | dev->output_name_counter[i] = out_type_counter[dev->output_type[i]]++; |
| 697 | 703 | } |
| 698 | 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 | 715 | /* do we create a video capture device? */ |
| 701 | 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 | 1025 | INIT_LIST_HEAD(&dev->vbi_out_active); |
| 1011 | 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 | 1039 | /* start creating the vb2 queues */ |
| 1014 | 1040 | if (dev->has_vid_cap) { |
| 1015 | 1041 | /* initialize vid_cap queue */ |
| @@ -1117,7 +1143,8 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) · 101 unmodified lines | ||
| 1117 | 1143 | /* finally start creating the device nodes */ |
| 1118 | 1144 | if (dev->has_vid_cap) { |
| 1119 | 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 | 1148 | vfd->fops = &vivid_fops; |
| 1122 | 1149 | vfd->ioctl_ops = &vivid_ioctl_ops; |
| 1123 | 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 | 1160 | vfd->lock = &dev->mutex; |
| 1134 | 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 | 1184 | ret = video_register_device(vfd, VFL_TYPE_GRABBER, vid_cap_nr[inst]); |
| 1137 | 1185 | if (ret < 0) |
| 1138 | 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 | 1191 | if (dev->has_vid_out) { |
| 1192 | +#ifdef CONFIG_VIDEO_VIVID_CEC | |
| 1193 | + unsigned int bus_cnt = 0; | |
| 1194 | +#endif | |
| 1195 | + | |
| 1144 | 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 | 1199 | vfd->vfl_dir = VFL_DIR_TX; |
| 1147 | 1200 | vfd->fops = &vivid_fops; |
| 1148 | 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 | 1212 | vfd->lock = &dev->mutex; |
| 1160 | 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 | 1244 | ret = video_register_device(vfd, VFL_TYPE_GRABBER, vid_out_nr[inst]); |
| 1163 | 1245 | if (ret < 0) |
| 1164 | 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 | 1251 | if (dev->has_vbi_cap) { |
| 1170 | 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 | 1255 | vfd->fops = &vivid_fops; |
| 1173 | 1256 | vfd->ioctl_ops = &vivid_ioctl_ops; |
| 1174 | 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 | 1275 | if (dev->has_vbi_out) { |
| 1193 | 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 | 1279 | vfd->vfl_dir = VFL_DIR_TX; |
| 1196 | 1280 | vfd->fops = &vivid_fops; |
| 1197 | 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 | 1300 | if (dev->has_sdr_cap) { |
| 1217 | 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 | 1304 | vfd->fops = &vivid_fops; |
| 1220 | 1305 | vfd->ioctl_ops = &vivid_ioctl_ops; |
| 1221 | 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 | 1320 | if (dev->has_radio_rx) { |
| 1236 | 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 | 1324 | vfd->fops = &vivid_radio_fops; |
| 1239 | 1325 | vfd->ioctl_ops = &vivid_ioctl_ops; |
| 1240 | 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 | 1339 | if (dev->has_radio_tx) { |
| 1254 | 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 | 1343 | vfd->vfl_dir = VFL_DIR_TX; |
| 1257 | 1344 | vfd->fops = &vivid_radio_fops; |
| 1258 | 1345 | vfd->ioctl_ops = &vivid_ioctl_ops; |
| @@ -1282,6 +1369,13 @@ unreg_dev: · 23 unmodified lines | ||
| 1282 | 1369 | video_unregister_device(&dev->vbi_cap_dev); |
| 1283 | 1370 | video_unregister_device(&dev->vid_out_dev); |
| 1284 | 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 | 1379 | free_dev: |
| 1286 | 1380 | v4l2_device_put(&dev->v4l2_dev); |
| 1287 | 1381 | return ret; |
| @@ -1331,8 +1425,7 @@ static int vivid_probe(struct platform_device *pdev) · 43 unmodified lines | ||
| 1331 | 1425 | static int vivid_remove(struct platform_device *pdev) |
| 1332 | 1426 | { |
| 1333 | 1427 | struct vivid_dev *dev; |
| 1334 | − unsigned i; | |
| 1335 | − | |
| 1428 | + unsigned int i, j; | |
| 1336 | 1429 | |
| 1337 | 1430 | for (i = 0; i < n_devs; i++) { |
| 1338 | 1431 | dev = vivid_devs[i]; |
| @@ -1380,6 +1473,13 @@ static int vivid_remove(struct platform_device *pdev) · 41 unmodified lines | ||
| 1380 | 1473 | unregister_framebuffer(&dev->fb_info); |
| 1381 | 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 | 1483 | v4l2_device_put(&dev->v4l2_dev); |
| 1384 | 1484 | vivid_devs[i] = NULL; |
| 1385 | 1485 | } |
| @@ -21,6 +21,8 @@ · 20 unmodified lines | ||
| 21 | 21 | #define _VIVID_CORE_H_ |
| 22 | 22 | |
| 23 | 23 | #include <linux/fb.h> |
| 24 | +#include <linux/workqueue.h> | |
| 25 | +#include <media/cec.h> | |
| 24 | 26 | #include <media/videobuf2-v4l2.h> |
| 25 | 27 | #include <media/v4l2-device.h> |
| 26 | 28 | #include <media/v4l2-dev.h> |
| @@ -132,6 +134,17 @@ enum vivid_colorspace { · 105 unmodified lines | ||
| 132 | 134 | #define VIVID_INVALID_SIGNAL(mode) \ |
| 133 | 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 | 148 | struct vivid_dev { |
| 136 | 149 | unsigned inst; |
| 137 | 150 | struct v4l2_device v4l2_dev; |
| @@ -497,6 +510,20 @@ struct vivid_dev { · 359 unmodified lines | ||
| 497 | 510 | /* Shared between radio receiver and transmitter */ |
| 498 | 511 | bool radio_rds_loop; |
| 499 | 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 | 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 | 552 | snprintf(str, sizeof(str), " button pressed!"); |
| 553 | 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 | 1700 | struct v4l2_edid *edid) |
| 1701 | 1701 | { |
| 1702 | 1702 | struct vivid_dev *dev = video_drvdata(file); |
| 1703 | + u16 phys_addr; | |
| 1704 | + unsigned int i; | |
| 1705 | + int ret; | |
| 1703 | 1706 | |
| 1704 | 1707 | memset(edid->reserved, 0, sizeof(edid->reserved)); |
| 1705 | 1708 | if (edid->pad >= dev->num_inputs) |
| @@ -1708,14 +1711,32 @@ int vidioc_s_edid(struct file *file, void *_fh, · 2 unmodified lines | ||
| 1708 | 1711 | return -EINVAL; |
| 1709 | 1712 | if (edid->blocks == 0) { |
| 1710 | 1713 | dev->edid_blocks = 0; |
| 1711 | − return 0; | |
| 1714 | + phys_addr = CEC_PHYS_ADDR_INVALID; | |
| 1715 | + goto set_phys_addr; | |
| 1712 | 1716 | } |
| 1713 | 1717 | if (edid->blocks > dev->edid_max_blocks) { |
| 1714 | 1718 | edid->blocks = dev->edid_max_blocks; |
| 1715 | 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 | 1729 | dev->edid_blocks = edid->blocks; |
| 1718 | 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 | 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 | 812 | struct vivid_dev *dev = video_drvdata(file); |
| 813 | 813 | struct video_device *vdev = video_devdata(file); |
| 814 | + struct cec_adapter *adap; | |
| 814 | 815 | |
| 815 | 816 | memset(edid->reserved, 0, sizeof(edid->reserved)); |
| 816 | 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 | 819 | return -EINVAL; |
| 819 | 820 | if (dev->input_type[edid->pad] != HDMI) |
| 820 | 821 | return -EINVAL; |
| 822 | + adap = dev->cec_rx_adap; | |
| 821 | 823 | } else { |
| 824 | + unsigned int bus_idx; | |
| 825 | + | |
| 822 | 826 | if (edid->pad >= dev->num_outputs) |
| 823 | 827 | return -EINVAL; |
| 824 | 828 | if (dev->output_type[edid->pad] != HDMI) |
| 825 | 829 | return -EINVAL; |
| 830 | + bus_idx = dev->cec_output2bus_map[edid->pad]; | |
| 831 | + adap = dev->cec_tx_adap[bus_idx]; | |
| 826 | 832 | } |
| 827 | 833 | if (edid->start_block == 0 && edid->blocks == 0) { |
| 828 | 834 | edid->blocks = dev->edid_blocks; |
| @@ -835,5 +841,6 @@ int vidioc_g_edid(struct file *file, void *_fh, · 6 unmodified lines | ||
| 835 | 841 | if (edid->start_block + edid->blocks > dev->edid_blocks) |
| 836 | 842 | edid->blocks = dev->edid_blocks - edid->start_block; |
| 837 | 843 | memcpy(edid->edid, dev->edid, edid->blocks * 128); |
| 844 | + cec_set_edid_phys_addr(edid->edid, edid->blocks * 128, adap->phys_addr); | |
| 838 | 845 | return 0; |
| 839 | 846 | } |
| @@ -804,6 +804,7 @@ static const struct { · 803 unmodified lines | ||
| 804 | 804 | { RC_BIT_SHARP, "sharp", "ir-sharp-decoder" }, |
| 805 | 805 | { RC_BIT_MCE_KBD, "mce_kbd", "ir-mce_kbd-decoder" }, |
| 806 | 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 | 21 | # Please keep them in alphabetic order |
| 22 | 22 | source "drivers/staging/media/bcm2048/Kconfig" |
| 23 | 23 | |
| 24 | +source "drivers/staging/media/cec/Kconfig" | |
| 25 | + | |
| 24 | 26 | source "drivers/staging/media/cxd2099/Kconfig" |
| 25 | 27 | |
| 26 | 28 | source "drivers/staging/media/davinci_vpfe/Kconfig" |
| @@ -29,6 +31,8 @@ source "drivers/staging/media/omap4iss/Kconfig" · 2 unmodified lines | ||
| 29 | 31 | |
| 30 | 32 | source "drivers/staging/media/tw686x-kh/Kconfig" |
| 31 | 33 | |
| 34 | +source "drivers/staging/media/s5p-cec/Kconfig" | |
| 35 | + | |
| 32 | 36 | # Keep LIRC at the end, as it has sub-menus |
| 33 | 37 | source "drivers/staging/media/lirc/Kconfig" |
| 34 | 38 | |
| @@ -1,4 +1,6 @@ | ||
| 1 | 1 | obj-$(CONFIG_I2C_BCM2048) += bcm2048/ |
| 2 | +obj-$(CONFIG_MEDIA_CEC) += cec/ | |
| 3 | +obj-$(CONFIG_VIDEO_SAMSUNG_S5P_CEC) += s5p-cec/ | |
| 2 | 4 | obj-$(CONFIG_DVB_CXD2099) += cxd2099/ |
| 3 | 5 | obj-$(CONFIG_LIRC_STAGING) += lirc/ |
| 4 | 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 | 57 | #include <linux/i2c-dev.h> |
| 58 | 58 | #include <linux/atalk.h> |
| 59 | 59 | #include <linux/gfp.h> |
| 60 | +#include <linux/cec.h> | |
| 60 | 61 | |
| 61 | 62 | #include "internal.h" |
| 62 | 63 | |
| @@ -1377,6 +1378,17 @@ COMPATIBLE_IOCTL(VIDEO_GET_NAVI) · 1314 unmodified lines | ||
| 1377 | 1378 | COMPATIBLE_IOCTL(VIDEO_SET_ATTRIBUTES) |
| 1378 | 1379 | COMPATIBLE_IOCTL(VIDEO_GET_SIZE) |
| 1379 | 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 | 1393 | /* joystick */ |
| 1382 | 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 | 32 | struct adv7511_edid_detect { |
| 33 | 33 | int present; |
| 34 | 34 | int segment; |
| 35 | −}; | |
| 36 | − | |
| 37 | −struct adv7511_cec_arg { | |
| 38 | − void *arg; | |
| 39 | − u32 f_flags; | |
| 35 | + uint16_t phys_addr; | |
| 40 | 36 | }; |
| 41 | 37 | |
| 42 | 38 | struct adv7511_platform_data { |
| @@ -31,6 +31,7 @@ enum rc_type { · 30 unmodified lines | ||
| 31 | 31 | RC_TYPE_RC6_MCE = 16, /* MCE (Philips RC6-6A-32 subtype) protocol */ |
| 32 | 32 | RC_TYPE_SHARP = 17, /* Sharp protocol */ |
| 33 | 33 | RC_TYPE_XMP = 18, /* XMP protocol */ |
| 34 | + RC_TYPE_CEC = 19, /* CEC protocol */ | |
| 34 | 35 | }; |
| 35 | 36 | |
| 36 | 37 | #define RC_BIT_NONE 0ULL |
| @@ -53,6 +54,7 @@ enum rc_type { · 16 unmodified lines | ||
| 53 | 54 | #define RC_BIT_RC6_MCE (1ULL << RC_TYPE_RC6_MCE) |
| 54 | 55 | #define RC_BIT_SHARP (1ULL << RC_TYPE_SHARP) |
| 55 | 56 | #define RC_BIT_XMP (1ULL << RC_TYPE_XMP) |
| 57 | +#define RC_BIT_CEC (1ULL << RC_TYPE_CEC) | |
| 56 | 58 | |
| 57 | 59 | #define RC_BIT_ALL (RC_BIT_UNKNOWN | RC_BIT_OTHER | \ |
| 58 | 60 | RC_BIT_RC5 | RC_BIT_RC5X | RC_BIT_RC5_SZ | \ |
| @@ -61,7 +63,7 @@ enum rc_type { · 2 unmodified lines | ||
| 61 | 63 | RC_BIT_NEC | RC_BIT_SANYO | RC_BIT_MCE_KBD | \ |
| 62 | 64 | RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 | RC_BIT_RC6_6A_24 | \ |
| 63 | 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 | 69 | #define RC_SCANCODE_UNKNOWN(x) (x) |
| @@ -123,6 +125,7 @@ void rc_map_init(void); · 55 unmodified lines | ||
| 123 | 125 | #define RC_MAP_BEHOLD_COLUMBUS "rc-behold-columbus" |
| 124 | 126 | #define RC_MAP_BEHOLD "rc-behold" |
| 125 | 127 | #define RC_MAP_BUDGET_CI_OLD "rc-budget-ci-old" |
| 128 | +#define RC_MAP_CEC "rc-cec" | |
| 126 | 129 | #define RC_MAP_CINERGY_1400 "rc-cinergy-1400" |
| 127 | 130 | #define RC_MAP_CINERGY "rc-cinergy" |
| 128 | 131 | #define RC_MAP_DELOCK_61959 "rc-delock-61959" |
| @@ -611,6 +611,37 @@ · 610 unmodified lines | ||
| 611 | 611 | #define KEY_KBDINPUTASSIST_ACCEPT 0x264 |
| 612 | 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 | 645 | #define BTN_TRIGGER_HAPPY 0x2c0 |
| 615 | 646 | #define BTN_TRIGGER_HAPPY1 0x2c0 |
| 616 | 647 | #define BTN_TRIGGER_HAPPY2 0x2c1 |
| @@ -247,6 +247,7 @@ struct input_mask { · 246 unmodified lines | ||
| 247 | 247 | #define BUS_ATARI 0x1B |
| 248 | 248 | #define BUS_SPI 0x1C |
| 249 | 249 | #define BUS_RMI 0x1D |
| 250 | +#define BUS_CEC 0x1E | |
| 250 | 251 | |
| 251 | 252 | /* |
| 252 | 253 | * MT_TOOL types |