Merge branch 'topic/cec' into patchwork
* 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
This commit is contained in:
commit
cbb5c8355a
|
@ -300,6 +300,9 @@ X!Isound/sound_firmware.c
|
|||
!Iinclude/media/media-devnode.h
|
||||
!Iinclude/media/media-entity.h
|
||||
</sect1>
|
||||
<sect1><title>Consumer Electronics Control devices</title>
|
||||
!Iinclude/media/cec-edid.h
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
||||
|
||||
|
|
|
@ -64,6 +64,7 @@ IOCTLS = \
|
|||
$(shell perl -ne 'print "$$1 " if /\#define\s+([A-Z][^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/dvb/net.h) \
|
||||
$(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/dvb/video.h) \
|
||||
$(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/media.h) \
|
||||
$(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/linux/cec.h) \
|
||||
$(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/v4l2-subdev.h) \
|
||||
|
||||
DEFINES = \
|
||||
|
@ -100,6 +101,7 @@ STRUCTS = \
|
|||
$(shell perl -ne 'print "$$1 " if (/^struct\s+([^\s]+)\s+/ && !/_old/)' $(srctree)/include/uapi/linux/dvb/net.h) \
|
||||
$(shell perl -ne 'print "$$1 " if (/^struct\s+([^\s]+)\s+/)' $(srctree)/include/uapi/linux/dvb/video.h) \
|
||||
$(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/uapi/linux/media.h) \
|
||||
$(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/linux/cec.h) \
|
||||
$(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/uapi/linux/v4l2-subdev.h) \
|
||||
$(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/uapi/linux/v4l2-mediabus.h)
|
||||
|
||||
|
|
|
@ -342,6 +342,16 @@ in the frequency range from 87,5 to 108,0 MHz</title>
|
|||
<subtitle>Specification Version 1.4a</subtitle>
|
||||
</biblioentry>
|
||||
|
||||
<biblioentry id="hdmi2">
|
||||
<abbrev>HDMI2</abbrev>
|
||||
<authorgroup>
|
||||
<corpauthor>HDMI Licensing LLC
|
||||
(<ulink url="http://www.hdmi.org">http://www.hdmi.org</ulink>)</corpauthor>
|
||||
</authorgroup>
|
||||
<title>High-Definition Multimedia Interface</title>
|
||||
<subtitle>Specification Version 2.0</subtitle>
|
||||
</biblioentry>
|
||||
|
||||
<biblioentry id="dp">
|
||||
<abbrev>DP</abbrev>
|
||||
<authorgroup>
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
<partinfo>
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>Hans</firstname>
|
||||
<surname>Verkuil</surname>
|
||||
<affiliation><address><email>hans.verkuil@cisco.com</email></address></affiliation>
|
||||
<contrib>Initial version.</contrib>
|
||||
</author>
|
||||
</authorgroup>
|
||||
<copyright>
|
||||
<year>2016</year>
|
||||
<holder>Hans Verkuil</holder>
|
||||
</copyright>
|
||||
|
||||
<revhistory>
|
||||
<!-- Put document revisions here, newest first. -->
|
||||
<revision>
|
||||
<revnumber>1.0.0</revnumber>
|
||||
<date>2016-03-17</date>
|
||||
<authorinitials>hv</authorinitials>
|
||||
<revremark>Initial revision</revremark>
|
||||
</revision>
|
||||
</revhistory>
|
||||
</partinfo>
|
||||
|
||||
<title>CEC API</title>
|
||||
|
||||
<chapter id="cec-api">
|
||||
<title>CEC: Consumer Electronics Control</title>
|
||||
|
||||
<section id="cec-intro">
|
||||
<title>Introduction</title>
|
||||
<para>
|
||||
Note: this documents the proposed CEC API. This API is not yet finalized and
|
||||
is currently only available as a staging kernel module.
|
||||
</para>
|
||||
<para>HDMI connectors provide a single pin for use by the Consumer Electronics
|
||||
Control protocol. This protocol allows different devices connected by an HDMI cable
|
||||
to communicate. The protocol for CEC version 1.4 is defined in supplements 1 (CEC)
|
||||
and 2 (HEAC or HDMI Ethernet and Audio Return Channel) of the HDMI 1.4a
|
||||
(<xref linkend="hdmi" />) specification and the extensions added to CEC version 2.0
|
||||
are defined in chapter 11 of the HDMI 2.0 (<xref linkend="hdmi2" />) specification.
|
||||
</para>
|
||||
|
||||
<para>The bitrate is very slow (effectively no more than 36 bytes per second) and
|
||||
is based on the ancient AV.link protocol used in old SCART connectors. The protocol
|
||||
closely resembles a crazy Rube Goldberg contraption and is an unholy mix of low and
|
||||
high level messages. Some messages, especially those part of the HEAC protocol layered
|
||||
on top of CEC, need to be handled by the kernel, others can be handled either by the
|
||||
kernel or by userspace.</para>
|
||||
|
||||
<para>In addition, CEC can be implemented in HDMI receivers, transmitters and in USB
|
||||
devices that have an HDMI input and an HDMI output and that control just the CEC pin.</para>
|
||||
|
||||
<para>Drivers that support CEC will create a CEC device node (/dev/cecX)
|
||||
to give userspace access to the CEC adapter. The &CEC-ADAP-G-CAPS; ioctl will tell userspace
|
||||
what it is allowed to do.</para>
|
||||
</section>
|
||||
</chapter>
|
||||
|
||||
<appendix id="cec-user-func">
|
||||
<title>Function Reference</title>
|
||||
<!-- Keep this alphabetically sorted. -->
|
||||
&sub-cec-func-open;
|
||||
&sub-cec-func-close;
|
||||
&sub-cec-func-ioctl;
|
||||
&sub-cec-func-poll;
|
||||
<!-- All ioctls go here. -->
|
||||
&sub-cec-ioc-adap-g-caps;
|
||||
&sub-cec-ioc-adap-g-log-addrs;
|
||||
&sub-cec-ioc-adap-g-phys-addr;
|
||||
&sub-cec-ioc-dqevent;
|
||||
&sub-cec-ioc-g-mode;
|
||||
&sub-cec-ioc-receive;
|
||||
</appendix>
|
|
@ -0,0 +1,64 @@
|
|||
<refentry id="cec-func-close">
|
||||
<refmeta>
|
||||
<refentrytitle>cec close()</refentrytitle>
|
||||
&manvol;
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>cec-close</refname>
|
||||
<refpurpose>Close a cec device</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<funcsynopsis>
|
||||
<funcsynopsisinfo>#include <unistd.h></funcsynopsisinfo>
|
||||
<funcprototype>
|
||||
<funcdef>int <function>close</function></funcdef>
|
||||
<paramdef>int <parameter>fd</parameter></paramdef>
|
||||
</funcprototype>
|
||||
</funcsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Arguments</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><parameter>fd</parameter></term>
|
||||
<listitem>
|
||||
<para>&fd;</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para>
|
||||
Note: this documents the proposed CEC API. This API is not yet finalized and
|
||||
is currently only available as a staging kernel module.
|
||||
</para>
|
||||
|
||||
<para>Closes the cec device. Resources associated with the file descriptor
|
||||
are freed. The device configuration remain unchanged.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Return Value</title>
|
||||
|
||||
<para><function>close</function> returns 0 on success. On error, -1 is
|
||||
returned, and <varname>errno</varname> is set appropriately. Possible error
|
||||
codes are:</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><errorcode>EBADF</errorcode></term>
|
||||
<listitem>
|
||||
<para><parameter>fd</parameter> is not a valid open file descriptor.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
</refentry>
|
|
@ -0,0 +1,78 @@
|
|||
<refentry id="cec-func-ioctl">
|
||||
<refmeta>
|
||||
<refentrytitle>cec ioctl()</refentrytitle>
|
||||
&manvol;
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>cec-ioctl</refname>
|
||||
<refpurpose>Control a cec device</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<funcsynopsis>
|
||||
<funcsynopsisinfo>#include <sys/ioctl.h></funcsynopsisinfo>
|
||||
<funcprototype>
|
||||
<funcdef>int <function>ioctl</function></funcdef>
|
||||
<paramdef>int <parameter>fd</parameter></paramdef>
|
||||
<paramdef>int <parameter>request</parameter></paramdef>
|
||||
<paramdef>void *<parameter>argp</parameter></paramdef>
|
||||
</funcprototype>
|
||||
</funcsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Arguments</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><parameter>fd</parameter></term>
|
||||
<listitem>
|
||||
<para>&fd;</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><parameter>request</parameter></term>
|
||||
<listitem>
|
||||
<para>CEC ioctl request code as defined in the cec.h header file,
|
||||
for example CEC_ADAP_G_CAPS.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><parameter>argp</parameter></term>
|
||||
<listitem>
|
||||
<para>Pointer to a request-specific structure.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
<para>
|
||||
Note: this documents the proposed CEC API. This API is not yet finalized and
|
||||
is currently only available as a staging kernel module.
|
||||
</para>
|
||||
|
||||
<para>The <function>ioctl()</function> function manipulates cec device
|
||||
parameters. The argument <parameter>fd</parameter> must be an open file
|
||||
descriptor.</para>
|
||||
<para>The ioctl <parameter>request</parameter> code specifies the cec
|
||||
function to be called. It has encoded in it whether the argument is an
|
||||
input, output or read/write parameter, and the size of the argument
|
||||
<parameter>argp</parameter> in bytes.</para>
|
||||
<para>Macros and structures definitions specifying cec ioctl requests and
|
||||
their parameters are located in the cec.h header file. All cec ioctl
|
||||
requests, their respective function and parameters are specified in
|
||||
<xref linkend="cec-user-func" />.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
&return-value;
|
||||
|
||||
<para>Request-specific error codes are listed in the
|
||||
individual requests descriptions.</para>
|
||||
<para>When an ioctl that takes an output or read/write parameter fails,
|
||||
the parameter remains unmodified.</para>
|
||||
</refsect1>
|
||||
</refentry>
|
|
@ -0,0 +1,104 @@
|
|||
<refentry id="cec-func-open">
|
||||
<refmeta>
|
||||
<refentrytitle>cec open()</refentrytitle>
|
||||
&manvol;
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>cec-open</refname>
|
||||
<refpurpose>Open a cec device</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<funcsynopsis>
|
||||
<funcsynopsisinfo>#include <fcntl.h></funcsynopsisinfo>
|
||||
<funcprototype>
|
||||
<funcdef>int <function>open</function></funcdef>
|
||||
<paramdef>const char *<parameter>device_name</parameter></paramdef>
|
||||
<paramdef>int <parameter>flags</parameter></paramdef>
|
||||
</funcprototype>
|
||||
</funcsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Arguments</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><parameter>device_name</parameter></term>
|
||||
<listitem>
|
||||
<para>Device to be opened.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><parameter>flags</parameter></term>
|
||||
<listitem>
|
||||
<para>Open flags. Access mode must be <constant>O_RDWR</constant>.
|
||||
</para>
|
||||
<para>When the <constant>O_NONBLOCK</constant> flag is
|
||||
given, the &CEC-RECEIVE; ioctl will return &EAGAIN; when no message is
|
||||
available, and the &CEC-TRANSMIT;, &CEC-ADAP-S-PHYS-ADDR; and
|
||||
&CEC-ADAP-S-LOG-ADDRS; ioctls all act in non-blocking mode.</para>
|
||||
<para>Other flags have no effect.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
<para>
|
||||
Note: this documents the proposed CEC API. This API is not yet finalized and
|
||||
is currently only available as a staging kernel module.
|
||||
</para>
|
||||
|
||||
<para>To open a cec device applications call <function>open()</function>
|
||||
with the desired device name. The function has no side effects; the device
|
||||
configuration remain unchanged.</para>
|
||||
<para>When the device is opened in read-only mode, attempts to modify its
|
||||
configuration will result in an error, and <varname>errno</varname> will be
|
||||
set to <errorcode>EBADF</errorcode>.</para>
|
||||
</refsect1>
|
||||
<refsect1>
|
||||
<title>Return Value</title>
|
||||
|
||||
<para><function>open</function> returns the new file descriptor on success.
|
||||
On error, -1 is returned, and <varname>errno</varname> is set appropriately.
|
||||
Possible error codes include:</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><errorcode>EACCES</errorcode></term>
|
||||
<listitem>
|
||||
<para>The requested access to the file is not allowed.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><errorcode>EMFILE</errorcode></term>
|
||||
<listitem>
|
||||
<para>The process already has the maximum number of files open.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><errorcode>ENFILE</errorcode></term>
|
||||
<listitem>
|
||||
<para>The system limit on the total number of open files has been
|
||||
reached.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><errorcode>ENOMEM</errorcode></term>
|
||||
<listitem>
|
||||
<para>Insufficient kernel memory was available.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><errorcode>ENXIO</errorcode></term>
|
||||
<listitem>
|
||||
<para>No device corresponding to this device special file exists.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
</refentry>
|
|
@ -0,0 +1,94 @@
|
|||
<refentry id="cec-func-poll">
|
||||
<refmeta>
|
||||
<refentrytitle>cec poll()</refentrytitle>
|
||||
&manvol;
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>cec-poll</refname>
|
||||
<refpurpose>Wait for some event on a file descriptor</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<funcsynopsis>
|
||||
<funcsynopsisinfo>#include <sys/poll.h></funcsynopsisinfo>
|
||||
<funcprototype>
|
||||
<funcdef>int <function>poll</function></funcdef>
|
||||
<paramdef>struct pollfd *<parameter>ufds</parameter></paramdef>
|
||||
<paramdef>unsigned int <parameter>nfds</parameter></paramdef>
|
||||
<paramdef>int <parameter>timeout</parameter></paramdef>
|
||||
</funcprototype>
|
||||
</funcsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para>
|
||||
Note: this documents the proposed CEC API. This API is not yet finalized and
|
||||
is currently only available as a staging kernel module.
|
||||
</para>
|
||||
|
||||
<para>With the <function>poll()</function> function applications
|
||||
can wait for CEC events.</para>
|
||||
|
||||
<para>On success <function>poll()</function> returns the number of
|
||||
file descriptors that have been selected (that is, file descriptors
|
||||
for which the <structfield>revents</structfield> field of the
|
||||
respective <structname>pollfd</structname> structure is non-zero).
|
||||
CEC devices set the <constant>POLLIN</constant> and
|
||||
<constant>POLLRDNORM</constant> flags in the
|
||||
<structfield>revents</structfield> field if there are messages in the
|
||||
receive queue. If the transmit queue has room for new messages, the
|
||||
<constant>POLLOUT</constant> and <constant>POLLWRNORM</constant>
|
||||
flags are set. If there are events in the event queue, then the
|
||||
<constant>POLLPRI</constant> flag is set.
|
||||
When the function timed out it returns a value of zero, on
|
||||
failure it returns <returnvalue>-1</returnvalue> and the
|
||||
<varname>errno</varname> variable is set appropriately.
|
||||
</para>
|
||||
|
||||
<para>For more details see the
|
||||
<function>poll()</function> manual page.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Return Value</title>
|
||||
|
||||
<para>On success, <function>poll()</function> returns the number
|
||||
structures which have non-zero <structfield>revents</structfield>
|
||||
fields, or zero if the call timed out. On error
|
||||
<returnvalue>-1</returnvalue> is returned, and the
|
||||
<varname>errno</varname> variable is set appropriately:</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><errorcode>EBADF</errorcode></term>
|
||||
<listitem>
|
||||
<para>One or more of the <parameter>ufds</parameter> members
|
||||
specify an invalid file descriptor.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><errorcode>EFAULT</errorcode></term>
|
||||
<listitem>
|
||||
<para><parameter>ufds</parameter> references an inaccessible
|
||||
memory area.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><errorcode>EINTR</errorcode></term>
|
||||
<listitem>
|
||||
<para>The call was interrupted by a signal.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><errorcode>EINVAL</errorcode></term>
|
||||
<listitem>
|
||||
<para>The <parameter>nfds</parameter> argument is greater
|
||||
than <constant>OPEN_MAX</constant>.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
</refentry>
|
|
@ -0,0 +1,151 @@
|
|||
<refentry id="cec-ioc-adap-g-caps">
|
||||
<refmeta>
|
||||
<refentrytitle>ioctl CEC_ADAP_G_CAPS</refentrytitle>
|
||||
&manvol;
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>CEC_ADAP_G_CAPS</refname>
|
||||
<refpurpose>Query device capabilities</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<funcsynopsis>
|
||||
<funcprototype>
|
||||
<funcdef>int <function>ioctl</function></funcdef>
|
||||
<paramdef>int <parameter>fd</parameter></paramdef>
|
||||
<paramdef>int <parameter>request</parameter></paramdef>
|
||||
<paramdef>struct cec_caps *<parameter>argp</parameter></paramdef>
|
||||
</funcprototype>
|
||||
</funcsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Arguments</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><parameter>fd</parameter></term>
|
||||
<listitem>
|
||||
<para>File descriptor returned by
|
||||
<link linkend='cec-func-open'><function>open()</function></link>.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><parameter>request</parameter></term>
|
||||
<listitem>
|
||||
<para>CEC_ADAP_G_CAPS</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><parameter>argp</parameter></term>
|
||||
<listitem>
|
||||
<para></para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para>
|
||||
Note: this documents the proposed CEC API. This API is not yet finalized and
|
||||
is currently only available as a staging kernel module.
|
||||
</para>
|
||||
|
||||
<para>All cec devices must support the <constant>CEC_ADAP_G_CAPS</constant>
|
||||
ioctl. To query device information, applications call the ioctl with a
|
||||
pointer to a &cec-caps;. The driver fills the structure and returns
|
||||
the information to the application.
|
||||
The ioctl never fails.</para>
|
||||
|
||||
<table pgwide="1" frame="none" id="cec-caps">
|
||||
<title>struct <structname>cec_caps</structname></title>
|
||||
<tgroup cols="3">
|
||||
&cs-str;
|
||||
<tbody valign="top">
|
||||
<row>
|
||||
<entry>char</entry>
|
||||
<entry><structfield>driver[32]</structfield></entry>
|
||||
<entry>The name of the cec adapter driver.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>char</entry>
|
||||
<entry><structfield>name[32]</structfield></entry>
|
||||
<entry>The name of this CEC adapter. The combination <structfield>driver</structfield>
|
||||
and <structfield>name</structfield> must be unique.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u32</entry>
|
||||
<entry><structfield>capabilities</structfield></entry>
|
||||
<entry>The capabilities of the CEC adapter, see <xref
|
||||
linkend="cec-capabilities" />.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u32</entry>
|
||||
<entry><structfield>version</structfield></entry>
|
||||
<entry>CEC Framework API version, formatted with the
|
||||
<constant>KERNEL_VERSION()</constant> macro.</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
<table pgwide="1" frame="none" id="cec-capabilities">
|
||||
<title>CEC Capabilities Flags</title>
|
||||
<tgroup cols="3">
|
||||
&cs-def;
|
||||
<tbody valign="top">
|
||||
<row>
|
||||
<entry><constant>CEC_CAP_PHYS_ADDR</constant></entry>
|
||||
<entry>0x00000001</entry>
|
||||
<entry>Userspace has to configure the physical address by
|
||||
calling &CEC-ADAP-S-PHYS-ADDR;. If this capability isn't set,
|
||||
then setting the physical address is handled by the kernel
|
||||
whenever the EDID is set (for an HDMI receiver) or read (for
|
||||
an HDMI transmitter).</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_CAP_LOG_ADDRS</constant></entry>
|
||||
<entry>0x00000002</entry>
|
||||
<entry>Userspace has to configure the logical addresses by
|
||||
calling &CEC-ADAP-S-LOG-ADDRS;. If this capability isn't set,
|
||||
then the kernel will have configured this.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_CAP_TRANSMIT</constant></entry>
|
||||
<entry>0x00000004</entry>
|
||||
<entry>Userspace can transmit CEC messages by calling &CEC-TRANSMIT;. This
|
||||
implies that userspace can be a follower as well, since being able to
|
||||
transmit messages is a prerequisite of becoming a follower. If this
|
||||
capability isn't set, then the kernel will handle all CEC transmits
|
||||
and process all CEC messages it receives.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_CAP_PASSTHROUGH</constant></entry>
|
||||
<entry>0x00000008</entry>
|
||||
<entry>Userspace can use the passthrough mode by
|
||||
calling &CEC-S-MODE;.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_CAP_RC</constant></entry>
|
||||
<entry>0x00000010</entry>
|
||||
<entry>This adapter supports the remote control protocol.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_CAP_MONITOR_ALL</constant></entry>
|
||||
<entry>0x00000020</entry>
|
||||
<entry>The CEC hardware can monitor all messages, not just directed and
|
||||
broadcast messages.</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
&return-value;
|
||||
</refsect1>
|
||||
</refentry>
|
|
@ -0,0 +1,329 @@
|
|||
<refentry id="cec-ioc-adap-g-log-addrs">
|
||||
<refmeta>
|
||||
<refentrytitle>ioctl CEC_ADAP_G_LOG_ADDRS, CEC_ADAP_S_LOG_ADDRS</refentrytitle>
|
||||
&manvol;
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>CEC_ADAP_G_LOG_ADDRS</refname>
|
||||
<refname>CEC_ADAP_S_LOG_ADDRS</refname>
|
||||
<refpurpose>Get or set the logical addresses</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<funcsynopsis>
|
||||
<funcprototype>
|
||||
<funcdef>int <function>ioctl</function></funcdef>
|
||||
<paramdef>int <parameter>fd</parameter></paramdef>
|
||||
<paramdef>int <parameter>request</parameter></paramdef>
|
||||
<paramdef>struct cec_log_addrs *<parameter>argp</parameter></paramdef>
|
||||
</funcprototype>
|
||||
</funcsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Arguments</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><parameter>fd</parameter></term>
|
||||
<listitem>
|
||||
<para>File descriptor returned by
|
||||
<link linkend='cec-func-open'><function>open()</function></link>.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><parameter>request</parameter></term>
|
||||
<listitem>
|
||||
<para>CEC_ADAP_G_LOG_ADDRS, CEC_ADAP_S_LOG_ADDRS</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><parameter>argp</parameter></term>
|
||||
<listitem>
|
||||
<para></para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para>
|
||||
Note: this documents the proposed CEC API. This API is not yet finalized and
|
||||
is currently only available as a staging kernel module.
|
||||
</para>
|
||||
|
||||
<para>To query the current CEC logical addresses, applications call the
|
||||
<constant>CEC_ADAP_G_LOG_ADDRS</constant> ioctl with a pointer to a
|
||||
<structname>cec_log_addrs</structname> structure where the drivers stores the
|
||||
logical addresses.</para>
|
||||
|
||||
<para>To set new logical addresses, applications fill in struct <structname>cec_log_addrs</structname>
|
||||
and call the <constant>CEC_ADAP_S_LOG_ADDRS</constant> ioctl with a pointer to this struct.
|
||||
The <constant>CEC_ADAP_S_LOG_ADDRS</constant> ioctl is only available if
|
||||
<constant>CEC_CAP_LOG_ADDRS</constant> is set (&ENOTTY; is returned otherwise). This ioctl will block until all
|
||||
requested logical addresses have been claimed. <constant>CEC_ADAP_S_LOG_ADDRS</constant>
|
||||
can only be called by a file handle in initiator mode (see &CEC-S-MODE;).</para>
|
||||
|
||||
<table pgwide="1" frame="none" id="cec-log-addrs">
|
||||
<title>struct <structname>cec_log_addrs</structname></title>
|
||||
<tgroup cols="3">
|
||||
&cs-str;
|
||||
<tbody valign="top">
|
||||
<row>
|
||||
<entry>__u8</entry>
|
||||
<entry><structfield>log_addr</structfield>[CEC_MAX_LOG_ADDRS]</entry>
|
||||
<entry>The actual logical addresses that were claimed. This is set by the
|
||||
driver. If no logical address could be claimed, then it is set to
|
||||
<constant>CEC_LOG_ADDR_INVALID</constant>. If this adapter is Unregistered,
|
||||
then <structfield>log_addr[0]</structfield> is set to 0xf and all others to
|
||||
<constant>CEC_LOG_ADDR_INVALID</constant>.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u16</entry>
|
||||
<entry><structfield>log_addr_mask</structfield></entry>
|
||||
<entry>The bitmask of all logical addresses this adapter has claimed.
|
||||
If this adapter is Unregistered then <structfield>log_addr_mask</structfield>
|
||||
sets bit 15 and clears all other bits. If this adapter is not configured at all, then
|
||||
<structfield>log_addr_mask</structfield> is set to 0. Set by the driver.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u8</entry>
|
||||
<entry><structfield>cec_version</structfield></entry>
|
||||
<entry>The CEC version that this adapter shall use. See
|
||||
<xref linkend="cec-versions" />.
|
||||
Used to implement the <constant>CEC_MSG_CEC_VERSION</constant> and
|
||||
<constant>CEC_MSG_REPORT_FEATURES</constant> messages. Note that
|
||||
<constant>CEC_OP_CEC_VERSION_1_3A</constant> is not allowed
|
||||
by the CEC framework.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u8</entry>
|
||||
<entry><structfield>num_log_addrs</structfield></entry>
|
||||
<entry>Number of logical addresses to set up. Must be ≤
|
||||
<structfield>available_log_addrs</structfield> as returned by
|
||||
&CEC-ADAP-G-CAPS;. All arrays in this structure are only filled up to
|
||||
index <structfield>available_log_addrs</structfield>-1. The remaining
|
||||
array elements will be ignored. Note that the CEC 2.0 standard allows
|
||||
for a maximum of 2 logical addresses, although some hardware has support
|
||||
for more. <constant>CEC_MAX_LOG_ADDRS</constant> is 4. The driver will
|
||||
return the actual number of logical addresses it could claim, which may
|
||||
be less than what was requested. If this field is set to 0, then the
|
||||
CEC adapter shall clear all claimed logical addresses and all other
|
||||
fields will be ignored.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u32</entry>
|
||||
<entry><structfield>vendor_id</structfield></entry>
|
||||
<entry>The vendor ID is a 24-bit number that identifies the specific
|
||||
vendor or entity. Based on this ID vendor specific commands may be
|
||||
defined. If you do not want a vendor ID then set it to
|
||||
<constant>CEC_VENDOR_ID_NONE</constant>.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u32</entry>
|
||||
<entry><structfield>flags</structfield></entry>
|
||||
<entry>Flags. No flags are defined yet, so set this to 0.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>char</entry>
|
||||
<entry><structfield>osd_name</structfield>[15]</entry>
|
||||
<entry>The On-Screen Display name as is returned by the
|
||||
<constant>CEC_MSG_SET_OSD_NAME</constant> message.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u8</entry>
|
||||
<entry><structfield>primary_device_type</structfield>[CEC_MAX_LOG_ADDRS]</entry>
|
||||
<entry>Primary device type for each logical address. See
|
||||
<xref linkend="cec-prim-dev-types" /> for possible types.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u8</entry>
|
||||
<entry><structfield>log_addr_type</structfield>[CEC_MAX_LOG_ADDRS]</entry>
|
||||
<entry>Logical address types. See <xref linkend="cec-log-addr-types" /> for
|
||||
possible types. The driver will update this with the actual logical address
|
||||
type that it claimed (e.g. it may have to fallback to
|
||||
<constant>CEC_LOG_ADDR_TYPE_UNREGISTERED</constant>).</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u8</entry>
|
||||
<entry><structfield>all_device_types</structfield>[CEC_MAX_LOG_ADDRS]</entry>
|
||||
<entry>CEC 2.0 specific: all device types. See <xref linkend="cec-all-dev-types-flags" />.
|
||||
Used to implement the <constant>CEC_MSG_REPORT_FEATURES</constant> message.
|
||||
This field is ignored if <structfield>cec_version</structfield> <
|
||||
<constant>CEC_OP_CEC_VERSION_2_0</constant>.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u8</entry>
|
||||
<entry><structfield>features</structfield>[CEC_MAX_LOG_ADDRS][12]</entry>
|
||||
<entry>Features for each logical address. Used to implement the
|
||||
<constant>CEC_MSG_REPORT_FEATURES</constant> message. The 12 bytes include
|
||||
both the RC Profile and the Device Features.
|
||||
This field is ignored if <structfield>cec_version</structfield> <
|
||||
<constant>CEC_OP_CEC_VERSION_2_0</constant>.</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
<table pgwide="1" frame="none" id="cec-versions">
|
||||
<title>CEC Versions</title>
|
||||
<tgroup cols="3">
|
||||
&cs-def;
|
||||
<tbody valign="top">
|
||||
<row>
|
||||
<entry><constant>CEC_OP_CEC_VERSION_1_3A</constant></entry>
|
||||
<entry>4</entry>
|
||||
<entry>CEC version according to the HDMI 1.3a standard.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_OP_CEC_VERSION_1_4B</constant></entry>
|
||||
<entry>5</entry>
|
||||
<entry>CEC version according to the HDMI 1.4b standard.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_OP_CEC_VERSION_2_0</constant></entry>
|
||||
<entry>6</entry>
|
||||
<entry>CEC version according to the HDMI 2.0 standard.</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
<table pgwide="1" frame="none" id="cec-prim-dev-types">
|
||||
<title>CEC Primary Device Types</title>
|
||||
<tgroup cols="3">
|
||||
&cs-def;
|
||||
<tbody valign="top">
|
||||
<row>
|
||||
<entry><constant>CEC_OP_PRIM_DEVTYPE_TV</constant></entry>
|
||||
<entry>0</entry>
|
||||
<entry>Use for a TV.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_OP_PRIM_DEVTYPE_RECORD</constant></entry>
|
||||
<entry>1</entry>
|
||||
<entry>Use for a recording device.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_OP_PRIM_DEVTYPE_TUNER</constant></entry>
|
||||
<entry>3</entry>
|
||||
<entry>Use for a device with a tuner.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_OP_PRIM_DEVTYPE_PLAYBACK</constant></entry>
|
||||
<entry>4</entry>
|
||||
<entry>Use for a playback device.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM</constant></entry>
|
||||
<entry>5</entry>
|
||||
<entry>Use for an audio system (e.g. an audio/video receiver).</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_OP_PRIM_DEVTYPE_SWITCH</constant></entry>
|
||||
<entry>6</entry>
|
||||
<entry>Use for a CEC switch.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_OP_PRIM_DEVTYPE_VIDEOPROC</constant></entry>
|
||||
<entry>7</entry>
|
||||
<entry>Use for a video processor device.</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
<table pgwide="1" frame="none" id="cec-log-addr-types">
|
||||
<title>CEC Logical Address Types</title>
|
||||
<tgroup cols="3">
|
||||
&cs-def;
|
||||
<tbody valign="top">
|
||||
<row>
|
||||
<entry><constant>CEC_LOG_ADDR_TYPE_TV</constant></entry>
|
||||
<entry>0</entry>
|
||||
<entry>Use for a TV.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_LOG_ADDR_TYPE_RECORD</constant></entry>
|
||||
<entry>1</entry>
|
||||
<entry>Use for a recording device.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_LOG_ADDR_TYPE_TUNER</constant></entry>
|
||||
<entry>2</entry>
|
||||
<entry>Use for a tuner device.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_LOG_ADDR_TYPE_PLAYBACK</constant></entry>
|
||||
<entry>3</entry>
|
||||
<entry>Use for a playback device.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_LOG_ADDR_TYPE_AUDIOSYSTEM</constant></entry>
|
||||
<entry>4</entry>
|
||||
<entry>Use for an audio system device.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_LOG_ADDR_TYPE_SPECIFIC</constant></entry>
|
||||
<entry>5</entry>
|
||||
<entry>Use for a second TV or for a video processor device.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_LOG_ADDR_TYPE_UNREGISTERED</constant></entry>
|
||||
<entry>6</entry>
|
||||
<entry>Use this if you just want to remain unregistered.
|
||||
Used for pure CEC switches or CDC-only devices (CDC:
|
||||
Capability Discovery and Control).</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
<table pgwide="1" frame="none" id="cec-all-dev-types-flags">
|
||||
<title>CEC All Device Types Flags</title>
|
||||
<tgroup cols="3">
|
||||
&cs-def;
|
||||
<tbody valign="top">
|
||||
<row>
|
||||
<entry><constant>CEC_OP_ALL_DEVTYPE_TV</constant></entry>
|
||||
<entry>0x80</entry>
|
||||
<entry>This supports the TV type.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_OP_ALL_DEVTYPE_RECORD</constant></entry>
|
||||
<entry>0x40</entry>
|
||||
<entry>This supports the Recording type.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_OP_ALL_DEVTYPE_TUNER</constant></entry>
|
||||
<entry>0x20</entry>
|
||||
<entry>This supports the Tuner type.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_OP_ALL_DEVTYPE_PLAYBACK</constant></entry>
|
||||
<entry>0x10</entry>
|
||||
<entry>This supports the Playback type.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM</constant></entry>
|
||||
<entry>0x08</entry>
|
||||
<entry>This supports the Audio System type.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_OP_ALL_DEVTYPE_SWITCH</constant></entry>
|
||||
<entry>0x04</entry>
|
||||
<entry>This supports the CEC Switch or Video Processing type.</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
&return-value;
|
||||
</refsect1>
|
||||
</refentry>
|
|
@ -0,0 +1,86 @@
|
|||
<refentry id="cec-ioc-adap-g-phys-addr">
|
||||
<refmeta>
|
||||
<refentrytitle>ioctl CEC_ADAP_G_PHYS_ADDR, CEC_ADAP_S_PHYS_ADDR</refentrytitle>
|
||||
&manvol;
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>CEC_ADAP_G_PHYS_ADDR</refname>
|
||||
<refname>CEC_ADAP_S_PHYS_ADDR</refname>
|
||||
<refpurpose>Get or set the physical address</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<funcsynopsis>
|
||||
<funcprototype>
|
||||
<funcdef>int <function>ioctl</function></funcdef>
|
||||
<paramdef>int <parameter>fd</parameter></paramdef>
|
||||
<paramdef>int <parameter>request</parameter></paramdef>
|
||||
<paramdef>__u16 *<parameter>argp</parameter></paramdef>
|
||||
</funcprototype>
|
||||
</funcsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Arguments</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><parameter>fd</parameter></term>
|
||||
<listitem>
|
||||
<para>File descriptor returned by
|
||||
<link linkend='cec-func-open'><function>open()</function></link>.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><parameter>request</parameter></term>
|
||||
<listitem>
|
||||
<para>CEC_ADAP_G_PHYS_ADDR, CEC_ADAP_S_PHYS_ADDR</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><parameter>argp</parameter></term>
|
||||
<listitem>
|
||||
<para></para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para>
|
||||
Note: this documents the proposed CEC API. This API is not yet finalized and
|
||||
is currently only available as a staging kernel module.
|
||||
</para>
|
||||
|
||||
<para>To query the current physical address applications call the
|
||||
<constant>CEC_ADAP_G_PHYS_ADDR</constant> ioctl with a pointer to an __u16
|
||||
where the driver stores the physical address.</para>
|
||||
|
||||
<para>To set a new physical address applications store the physical address in
|
||||
an __u16 and call the <constant>CEC_ADAP_S_PHYS_ADDR</constant> ioctl with a
|
||||
pointer to this integer. <constant>CEC_ADAP_S_PHYS_ADDR</constant> is only
|
||||
available if <constant>CEC_CAP_PHYS_ADDR</constant> is set (&ENOTTY; will be returned
|
||||
otherwise). <constant>CEC_ADAP_S_PHYS_ADDR</constant>
|
||||
can only be called by a file handle in initiator mode (see &CEC-S-MODE;), if not
|
||||
&EBUSY; will be returned.</para>
|
||||
|
||||
<para>The physical address is a 16-bit number where each group of 4 bits
|
||||
represent a digit of the physical address a.b.c.d where the most significant
|
||||
4 bits represent 'a'. The CEC root device (usually the TV) has address 0.0.0.0.
|
||||
Every device that is hooked up to an input of the TV has address a.0.0.0 (where
|
||||
'a' is ≥ 1), devices hooked up to those in turn have addresses a.b.0.0, etc.
|
||||
So a topology of up to 5 devices deep is supported. The physical address a
|
||||
device shall use is stored in the EDID of the sink.</para>
|
||||
|
||||
<para>For example, the EDID for each HDMI input of the TV will have a different
|
||||
physical address of the form a.0.0.0 that the sources will read out and use as
|
||||
their physical address.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
&return-value;
|
||||
</refsect1>
|
||||
</refentry>
|
|
@ -0,0 +1,202 @@
|
|||
<refentry id="cec-ioc-g-event">
|
||||
<refmeta>
|
||||
<refentrytitle>ioctl CEC_DQEVENT</refentrytitle>
|
||||
&manvol;
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>CEC_DQEVENT</refname>
|
||||
<refpurpose>Dequeue a CEC event</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<funcsynopsis>
|
||||
<funcprototype>
|
||||
<funcdef>int <function>ioctl</function></funcdef>
|
||||
<paramdef>int <parameter>fd</parameter></paramdef>
|
||||
<paramdef>int <parameter>request</parameter></paramdef>
|
||||
<paramdef>struct cec_event *<parameter>argp</parameter></paramdef>
|
||||
</funcprototype>
|
||||
</funcsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Arguments</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><parameter>fd</parameter></term>
|
||||
<listitem>
|
||||
<para>File descriptor returned by
|
||||
<link linkend='cec-func-open'><function>open()</function></link>.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><parameter>request</parameter></term>
|
||||
<listitem>
|
||||
<para>CEC_DQEVENT</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><parameter>argp</parameter></term>
|
||||
<listitem>
|
||||
<para></para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para>
|
||||
Note: this documents the proposed CEC API. This API is not yet finalized and
|
||||
is currently only available as a staging kernel module.
|
||||
</para>
|
||||
|
||||
<para>CEC devices can send asynchronous events. These can be retrieved by calling
|
||||
the <constant>CEC_DQEVENT</constant> ioctl. If the file descriptor is in non-blocking
|
||||
mode and no event is pending, then it will return -1 and set errno to the &EAGAIN;.</para>
|
||||
|
||||
<para>The internal event queues are per-filehandle and per-event type. If there is
|
||||
no more room in a queue then the last event is overwritten with the new one. This
|
||||
means that intermediate results can be thrown away but that the latest event is always
|
||||
available. This also means that is it possible to read two successive events that have
|
||||
the same value (e.g. two CEC_EVENT_STATE_CHANGE events with the same state). In that
|
||||
case the intermediate state changes were lost but it is guaranteed that the state
|
||||
did change in between the two events.</para>
|
||||
|
||||
<table pgwide="1" frame="none" id="cec-event-state-change">
|
||||
<title>struct <structname>cec_event_state_change</structname></title>
|
||||
<tgroup cols="3">
|
||||
&cs-str;
|
||||
<tbody valign="top">
|
||||
<row>
|
||||
<entry>__u16</entry>
|
||||
<entry><structfield>phys_addr</structfield></entry>
|
||||
<entry>The current physical address.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u16</entry>
|
||||
<entry><structfield>log_addr_mask</structfield></entry>
|
||||
<entry>The current set of claimed logical addresses.</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
<table pgwide="1" frame="none" id="cec-event-lost-msgs">
|
||||
<title>struct <structname>cec_event_lost_msgs</structname></title>
|
||||
<tgroup cols="3">
|
||||
&cs-str;
|
||||
<tbody valign="top">
|
||||
<row>
|
||||
<entry>__u32</entry>
|
||||
<entry><structfield>lost_msgs</structfield></entry>
|
||||
<entry>Set to the number of lost messages since the filehandle
|
||||
was opened or since the last time this event was dequeued for
|
||||
this filehandle. The messages lost are the oldest messages. So
|
||||
when a new message arrives and there is no more room, then the
|
||||
oldest message is discarded to make room for the new one. The
|
||||
internal size of the message queue guarantees that all messages
|
||||
received in the last two seconds will be stored. Since messages
|
||||
should be replied to within a second according to the CEC
|
||||
specification, this is more than enough.
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
<table pgwide="1" frame="none" id="cec-event">
|
||||
<title>struct <structname>cec_event</structname></title>
|
||||
<tgroup cols="4">
|
||||
&cs-str;
|
||||
<tbody valign="top">
|
||||
<row>
|
||||
<entry>__u64</entry>
|
||||
<entry><structfield>ts</structfield></entry>
|
||||
<entry>Timestamp of the event in ns.</entry>
|
||||
<entry></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u32</entry>
|
||||
<entry><structfield>event</structfield></entry>
|
||||
<entry>The CEC event type, see <xref linkend="cec-events" />.</entry>
|
||||
<entry></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u32</entry>
|
||||
<entry><structfield>flags</structfield></entry>
|
||||
<entry>Event flags, see <xref linkend="cec-event-flags" />.</entry>
|
||||
<entry></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>union</entry>
|
||||
<entry>(anonymous)</entry>
|
||||
<entry></entry>
|
||||
<entry></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry></entry>
|
||||
<entry>struct cec_event_state_change</entry>
|
||||
<entry><structfield>state_change</structfield></entry>
|
||||
<entry>The new adapter state as sent by the <constant>CEC_EVENT_STATE_CHANGE</constant>
|
||||
event.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry></entry>
|
||||
<entry>struct cec_event_lost_msgs</entry>
|
||||
<entry><structfield>lost_msgs</structfield></entry>
|
||||
<entry>The number of lost messages as sent by the <constant>CEC_EVENT_LOST_MSGS</constant>
|
||||
event.</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
<table pgwide="1" frame="none" id="cec-events">
|
||||
<title>CEC Events Types</title>
|
||||
<tgroup cols="3">
|
||||
&cs-def;
|
||||
<tbody valign="top">
|
||||
<row>
|
||||
<entry><constant>CEC_EVENT_STATE_CHANGE</constant></entry>
|
||||
<entry>1</entry>
|
||||
<entry>Generated when the CEC Adapter's state changes. When open() is
|
||||
called an initial event will be generated for that filehandle with the
|
||||
CEC Adapter's state at that time.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_EVENT_LOST_MSGS</constant></entry>
|
||||
<entry>2</entry>
|
||||
<entry>Generated if one or more CEC messages were lost because the
|
||||
application didn't dequeue CEC messages fast enough.</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
<table pgwide="1" frame="none" id="cec-event-flags">
|
||||
<title>CEC Event Flags</title>
|
||||
<tgroup cols="3">
|
||||
&cs-def;
|
||||
<tbody valign="top">
|
||||
<row>
|
||||
<entry><constant>CEC_EVENT_FL_INITIAL_VALUE</constant></entry>
|
||||
<entry>1</entry>
|
||||
<entry>Set for the initial events that are generated when the device is
|
||||
opened. See the table above for which events do this. This allows
|
||||
applications to learn the initial state of the CEC adapter at open()
|
||||
time.</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
&return-value;
|
||||
</refsect1>
|
||||
</refentry>
|
|
@ -0,0 +1,255 @@
|
|||
<refentry id="cec-ioc-g-mode">
|
||||
<refmeta>
|
||||
<refentrytitle>ioctl CEC_G_MODE, CEC_S_MODE</refentrytitle>
|
||||
&manvol;
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>CEC_G_MODE</refname>
|
||||
<refname>CEC_S_MODE</refname>
|
||||
<refpurpose>Get or set exclusive use of the CEC adapter</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<funcsynopsis>
|
||||
<funcprototype>
|
||||
<funcdef>int <function>ioctl</function></funcdef>
|
||||
<paramdef>int <parameter>fd</parameter></paramdef>
|
||||
<paramdef>int <parameter>request</parameter></paramdef>
|
||||
<paramdef>__u32 *<parameter>argp</parameter></paramdef>
|
||||
</funcprototype>
|
||||
</funcsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Arguments</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><parameter>fd</parameter></term>
|
||||
<listitem>
|
||||
<para>File descriptor returned by
|
||||
<link linkend='cec-func-open'><function>open()</function></link>.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><parameter>request</parameter></term>
|
||||
<listitem>
|
||||
<para>CEC_G_MODE, CEC_S_MODE</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><parameter>argp</parameter></term>
|
||||
<listitem>
|
||||
<para></para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para>
|
||||
Note: this documents the proposed CEC API. This API is not yet finalized and
|
||||
is currently only available as a staging kernel module.
|
||||
</para>
|
||||
|
||||
<para>By default any filehandle can use &CEC-TRANSMIT; and &CEC-RECEIVE;, but
|
||||
in order to prevent applications from stepping on each others toes it must be possible
|
||||
to obtain exclusive access to the CEC adapter. This ioctl sets the filehandle
|
||||
to initiator and/or follower mode which can be exclusive depending on the chosen
|
||||
mode. The initiator is the filehandle that is used
|
||||
to initiate messages, i.e. it commands other CEC devices. The follower is the filehandle
|
||||
that receives messages sent to the CEC adapter and processes them. The same filehandle
|
||||
can be both initiator and follower, or this role can be taken by two different
|
||||
filehandles.</para>
|
||||
|
||||
<para>When a CEC message is received, then the CEC framework will decide how
|
||||
it will be processed. If the message is a reply to an earlier transmitted message,
|
||||
then the reply is sent back to the filehandle that is waiting for it. In addition
|
||||
the CEC framework will process it.</para>
|
||||
|
||||
<para>If the message is not a reply, then the CEC framework will process it
|
||||
first. If there is no follower, then the message is just discarded and a feature
|
||||
abort is sent back to the initiator if the framework couldn't process it. If there
|
||||
is a follower, then the message is passed on to the follower who will use
|
||||
&CEC-RECEIVE; to dequeue the new message. The framework expects the follower to
|
||||
make the right decisions.</para>
|
||||
|
||||
<para>The CEC framework will process core messages unless requested otherwise
|
||||
by the follower. The follower can enable the passthrough mode. In that case, the
|
||||
CEC framework will pass on most core messages without processing them and
|
||||
the follower will have to implement those messages. There are some messages
|
||||
that the core will always process, regardless of the passthrough mode. See
|
||||
<xref linkend="cec-core-processing" /> for details.</para>
|
||||
|
||||
<para>If there is no initiator, then any CEC filehandle can use &CEC-TRANSMIT;.
|
||||
If there is an exclusive initiator then only that initiator can call &CEC-TRANSMIT;.
|
||||
The follower can of course always call &CEC-TRANSMIT;.</para>
|
||||
|
||||
<para>Available initiator modes are:</para>
|
||||
|
||||
<table pgwide="1" frame="none" id="cec-mode-initiator">
|
||||
<title>Initiator Modes</title>
|
||||
<tgroup cols="3">
|
||||
&cs-def;
|
||||
<tbody valign="top">
|
||||
<row>
|
||||
<entry><constant>CEC_MODE_NO_INITIATOR</constant></entry>
|
||||
<entry>0x0</entry>
|
||||
<entry>This is not an initiator, i.e. it cannot transmit CEC messages
|
||||
or make any other changes to the CEC adapter.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_MODE_INITIATOR</constant></entry>
|
||||
<entry>0x1</entry>
|
||||
<entry>This is an initiator (the default when the device is opened) and it
|
||||
can transmit CEC messages and make changes to the CEC adapter, unless there
|
||||
is an exclusive initiator.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_MODE_EXCL_INITIATOR</constant></entry>
|
||||
<entry>0x2</entry>
|
||||
<entry>This is an exclusive initiator and this file descriptor is the only one
|
||||
that can transmit CEC messages and make changes to the CEC adapter. If someone
|
||||
else is already the exclusive initiator then an attempt to become one will return
|
||||
the &EBUSY; error.</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
<para>Available follower modes are:</para>
|
||||
|
||||
<table pgwide="1" frame="none" id="cec-mode-follower">
|
||||
<title>Follower Modes</title>
|
||||
<tgroup cols="3">
|
||||
&cs-def;
|
||||
<tbody valign="top">
|
||||
<row>
|
||||
<entry><constant>CEC_MODE_NO_FOLLOWER</constant></entry>
|
||||
<entry>0x00</entry>
|
||||
<entry>This is not a follower (the default when the device is opened).</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_MODE_FOLLOWER</constant></entry>
|
||||
<entry>0x10</entry>
|
||||
<entry>This is a follower and it will receive CEC messages unless there is
|
||||
an exclusive follower. You cannot become a follower if <constant>CEC_CAP_TRANSMIT</constant>
|
||||
is not set or if <constant>CEC_MODE_NO_INITIATOR</constant> was specified,
|
||||
&EINVAL; is returned in that case.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_MODE_EXCL_FOLLOWER</constant></entry>
|
||||
<entry>0x20</entry>
|
||||
<entry>This is an exclusive follower and only this file descriptor will receive
|
||||
CEC messages for processing. If someone else is already the exclusive follower
|
||||
then an attempt to become one will return the &EBUSY; error. You cannot become
|
||||
a follower if <constant>CEC_CAP_TRANSMIT</constant> is not set or if
|
||||
<constant>CEC_MODE_NO_INITIATOR</constant> was specified, &EINVAL; is returned
|
||||
in that case.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_MODE_EXCL_FOLLOWER_PASSTHRU</constant></entry>
|
||||
<entry>0x30</entry>
|
||||
<entry>This is an exclusive follower and only this file descriptor will receive
|
||||
CEC messages for processing. In addition it will put the CEC device into
|
||||
passthrough mode, allowing the exclusive follower to handle most core messages
|
||||
instead of relying on the CEC framework for that. If someone else is already the
|
||||
exclusive follower then an attempt to become one will return the &EBUSY; error.
|
||||
You cannot become a follower if <constant>CEC_CAP_TRANSMIT</constant>
|
||||
is not set or if <constant>CEC_MODE_NO_INITIATOR</constant> was specified,
|
||||
&EINVAL; is returned in that case.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_MODE_MONITOR</constant></entry>
|
||||
<entry>0xe0</entry>
|
||||
<entry>Put the file descriptor into monitor mode. Can only be used in combination
|
||||
with <constant>CEC_MODE_NO_INITIATOR</constant>, otherwise &EINVAL; will be
|
||||
returned. In monitor mode all messages this CEC device transmits and all messages
|
||||
it receives (both broadcast messages and directed messages for one its logical
|
||||
addresses) will be reported. This is very useful for debugging. This is only
|
||||
allowed if the process has the <constant>CAP_NET_ADMIN</constant>
|
||||
capability. If that is not set, then &EPERM; is returned.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_MODE_MONITOR_ALL</constant></entry>
|
||||
<entry>0xf0</entry>
|
||||
<entry>Put the file descriptor into 'monitor all' mode. Can only be used in combination
|
||||
with <constant>CEC_MODE_NO_INITIATOR</constant>, otherwise &EINVAL; will be
|
||||
returned. In 'monitor all' mode all messages this CEC device transmits and all messages
|
||||
it receives, including directed messages for other CEC devices will be reported. This
|
||||
is very useful for debugging, but not all devices support this. This mode requires that
|
||||
the <constant>CEC_CAP_MONITOR_ALL</constant> capability is set, otherwise &EINVAL; is
|
||||
returned. This is only allowed if the process has the <constant>CAP_NET_ADMIN</constant>
|
||||
capability. If that is not set, then &EPERM; is returned.</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
<para>Core message processing details:</para>
|
||||
|
||||
<table pgwide="1" frame="none" id="cec-core-processing">
|
||||
<title>Core Message Processing</title>
|
||||
<tgroup cols="2">
|
||||
&cs-def;
|
||||
<tbody valign="top">
|
||||
<row>
|
||||
<entry><constant>CEC_MSG_GET_CEC_VERSION</constant></entry>
|
||||
<entry>When in passthrough mode this message has to be handled by userspace,
|
||||
otherwise the core will return the CEC version that was set with &CEC-ADAP-S-LOG-ADDRS;.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_MSG_GIVE_DEVICE_VENDOR_ID</constant></entry>
|
||||
<entry>When in passthrough mode this message has to be handled by userspace,
|
||||
otherwise the core will return the vendor ID that was set with &CEC-ADAP-S-LOG-ADDRS;.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_MSG_ABORT</constant></entry>
|
||||
<entry>When in passthrough mode this message has to be handled by userspace,
|
||||
otherwise the core will return a feature refused message as per the specification.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_MSG_GIVE_PHYSICAL_ADDR</constant></entry>
|
||||
<entry>When in passthrough mode this message has to be handled by userspace,
|
||||
otherwise the core will report the current physical address.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_MSG_GIVE_OSD_NAME</constant></entry>
|
||||
<entry>When in passthrough mode this message has to be handled by userspace,
|
||||
otherwise the core will report the current OSD name as was set with
|
||||
&CEC-ADAP-S-LOG-ADDRS;.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_MSG_GIVE_FEATURES</constant></entry>
|
||||
<entry>When in passthrough mode this message has to be handled by userspace,
|
||||
otherwise the core will report the current features as was set with
|
||||
&CEC-ADAP-S-LOG-ADDRS; or the message is ignore if the CEC version was
|
||||
older than 2.0.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_MSG_USER_CONTROL_PRESSED</constant></entry>
|
||||
<entry>If <constant>CEC_CAP_RC</constant> is set, then generate a remote control
|
||||
key press. This message is always passed on to userspace.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_MSG_USER_CONTROL_RELEASED</constant></entry>
|
||||
<entry>If <constant>CEC_CAP_RC</constant> is set, then generate a remote control
|
||||
key release. This message is always passed on to userspace.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_MSG_REPORT_PHYSICAL_ADDR</constant></entry>
|
||||
<entry>The CEC framework will make note of the reported physical address
|
||||
and then just pass the message on to userspace.</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
&return-value;
|
||||
</refsect1>
|
||||
</refentry>
|
|
@ -0,0 +1,274 @@
|
|||
<refentry id="cec-ioc-receive">
|
||||
<refmeta>
|
||||
<refentrytitle>ioctl CEC_RECEIVE, CEC_TRANSMIT</refentrytitle>
|
||||
&manvol;
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>CEC_RECEIVE</refname>
|
||||
<refname>CEC_TRANSMIT</refname>
|
||||
<refpurpose>Receive or transmit a CEC message</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<funcsynopsis>
|
||||
<funcprototype>
|
||||
<funcdef>int <function>ioctl</function></funcdef>
|
||||
<paramdef>int <parameter>fd</parameter></paramdef>
|
||||
<paramdef>int <parameter>request</parameter></paramdef>
|
||||
<paramdef>struct cec_msg *<parameter>argp</parameter></paramdef>
|
||||
</funcprototype>
|
||||
</funcsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Arguments</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><parameter>fd</parameter></term>
|
||||
<listitem>
|
||||
<para>File descriptor returned by
|
||||
<link linkend='cec-func-open'><function>open()</function></link>.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><parameter>request</parameter></term>
|
||||
<listitem>
|
||||
<para>CEC_RECEIVE, CEC_TRANSMIT</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><parameter>argp</parameter></term>
|
||||
<listitem>
|
||||
<para></para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para>
|
||||
Note: this documents the proposed CEC API. This API is not yet finalized and
|
||||
is currently only available as a staging kernel module.
|
||||
</para>
|
||||
|
||||
<para>To receive a CEC message the application has to fill in the
|
||||
<structname>cec_msg</structname> structure and pass it to the
|
||||
<constant>CEC_RECEIVE</constant> ioctl. <constant>CEC_RECEIVE</constant> is
|
||||
only available if <constant>CEC_CAP_RECEIVE</constant> is set. If the
|
||||
file descriptor is in non-blocking mode and there are no received
|
||||
messages pending, then it will return -1 and set errno to the &EAGAIN;.
|
||||
If the file descriptor is in blocking mode and <structfield>timeout</structfield>
|
||||
is non-zero and no message arrived within <structfield>timeout</structfield>
|
||||
milliseconds, then it will return -1 and set errno to the &ETIMEDOUT;.</para>
|
||||
|
||||
<para>To send a CEC message the application has to fill in the
|
||||
<structname>cec_msg</structname> structure and pass it to the
|
||||
<constant>CEC_TRANSMIT</constant> ioctl. <constant>CEC_TRANSMIT</constant> is
|
||||
only available if <constant>CEC_CAP_TRANSMIT</constant> is set.
|
||||
If there is no more room in the transmit queue, then it will return
|
||||
-1 and set errno to the &EBUSY;.</para>
|
||||
|
||||
<table pgwide="1" frame="none" id="cec-msg">
|
||||
<title>struct <structname>cec_msg</structname></title>
|
||||
<tgroup cols="3">
|
||||
&cs-str;
|
||||
<tbody valign="top">
|
||||
<row>
|
||||
<entry>__u64</entry>
|
||||
<entry><structfield>ts</structfield></entry>
|
||||
<entry>Timestamp of when the message was transmitted in ns in the case
|
||||
of <constant>CEC_TRANSMIT</constant> with <structfield>reply</structfield>
|
||||
set to 0, or the timestamp of the received message in all other cases.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u32</entry>
|
||||
<entry><structfield>len</structfield></entry>
|
||||
<entry>The length of the message. For <constant>CEC_TRANSMIT</constant> this
|
||||
is filled in by the application. The driver will fill this in for
|
||||
<constant>CEC_RECEIVE</constant> and for <constant>CEC_TRANSMIT</constant>
|
||||
it will be filled in with the length of the reply message if
|
||||
<structfield>reply</structfield> was set.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u32</entry>
|
||||
<entry><structfield>timeout</structfield></entry>
|
||||
<entry>The timeout in milliseconds. This is the time the device will wait for a message to
|
||||
be received before timing out. If it is set to 0, then it will wait indefinitely when it
|
||||
is called by <constant>CEC_RECEIVE</constant>. If it is 0 and it is called by
|
||||
<constant>CEC_TRANSMIT</constant>, then it will be replaced by 1000 if the
|
||||
<structfield>reply</structfield> is non-zero or ignored if <structfield>reply</structfield>
|
||||
is 0.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u32</entry>
|
||||
<entry><structfield>sequence</structfield></entry>
|
||||
<entry>The sequence number is automatically assigned by the CEC
|
||||
framework for all transmitted messages. It can be later used by the
|
||||
framework to generate an event if a reply for a message was
|
||||
requested and the message was transmitted in a non-blocking mode.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u32</entry>
|
||||
<entry><structfield>flags</structfield></entry>
|
||||
<entry>Flags. No flags are defined yet, so set this to 0.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u8</entry>
|
||||
<entry><structfield>rx_status</structfield></entry>
|
||||
<entry>The status bits of the received message. See <xref linkend="cec-rx-status" />
|
||||
for the possible status values. It is 0 if this message was transmitted, not
|
||||
received, unless this is the reply to a transmitted message. In that case both
|
||||
<structfield>rx_status</structfield> and <structfield>tx_status</structfield>
|
||||
are set.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u8</entry>
|
||||
<entry><structfield>tx_status</structfield></entry>
|
||||
<entry>The status bits of the transmitted message. See <xref linkend="cec-tx-status" />
|
||||
for the possible status values. It is 0 if this messages was received, not
|
||||
transmitted.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u8</entry>
|
||||
<entry><structfield>msg</structfield>[16]</entry>
|
||||
<entry>The message payload. For <constant>CEC_TRANSMIT</constant> this
|
||||
is filled in by the application. The driver will fill this in for
|
||||
<constant>CEC_RECEIVE</constant> and for <constant>CEC_TRANSMIT</constant>
|
||||
it will be filled in with the payload of the reply message if
|
||||
<structfield>reply</structfield> was set.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u8</entry>
|
||||
<entry><structfield>reply</structfield></entry>
|
||||
<entry>Wait until this message is replied. If <structfield>reply</structfield>
|
||||
is 0 and the <structfield>timeout</structfield> is 0, then don't wait for a reply but
|
||||
return after transmitting the message. If there was an error as indicated by a non-zero
|
||||
<structfield>tx_status</structfield> field, then <structfield>reply</structfield> and
|
||||
<structfield>timeout</structfield> are both set to 0 by the driver. Ignored by
|
||||
<constant>CEC_RECEIVE</constant>. The case where <structfield>reply</structfield> is 0
|
||||
(this is the opcode for the Feature Abort message) and <structfield>timeout</structfield>
|
||||
is non-zero is specifically allowed to send a message and wait up to <structfield>timeout</structfield>
|
||||
milliseconds for a Feature Abort reply. In this case <structfield>rx_status</structfield>
|
||||
will either be set to <constant>CEC_RX_STATUS_TIMEOUT</constant> or
|
||||
<constant>CEC_RX_STATUS_FEATURE_ABORT</constant>.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u8</entry>
|
||||
<entry><structfield>tx_arb_lost_cnt</structfield></entry>
|
||||
<entry>A counter of the number of transmit attempts that resulted in the
|
||||
Arbitration Lost error. This is only set if the hardware supports this, otherwise
|
||||
it is always 0. This counter is only valid if the <constant>CEC_TX_STATUS_ARB_LOST</constant>
|
||||
status bit is set.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u8</entry>
|
||||
<entry><structfield>tx_nack_cnt</structfield></entry>
|
||||
<entry>A counter of the number of transmit attempts that resulted in the
|
||||
Not Acknowledged error. This is only set if the hardware supports this, otherwise
|
||||
it is always 0. This counter is only valid if the <constant>CEC_TX_STATUS_NACK</constant>
|
||||
status bit is set.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u8</entry>
|
||||
<entry><structfield>tx_low_drive_cnt</structfield></entry>
|
||||
<entry>A counter of the number of transmit attempts that resulted in the
|
||||
Arbitration Lost error. This is only set if the hardware supports this, otherwise
|
||||
it is always 0. This counter is only valid if the <constant>CEC_TX_STATUS_LOW_DRIVE</constant>
|
||||
status bit is set.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u8</entry>
|
||||
<entry><structfield>tx_error_cnt</structfield></entry>
|
||||
<entry>A counter of the number of transmit errors other than Arbitration Lost
|
||||
or Not Acknowledged. This is only set if the hardware supports this, otherwise
|
||||
it is always 0. This counter is only valid if the <constant>CEC_TX_STATUS_ERROR</constant>
|
||||
status bit is set.</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
<table pgwide="1" frame="none" id="cec-tx-status">
|
||||
<title>CEC Transmit Status</title>
|
||||
<tgroup cols="3">
|
||||
&cs-def;
|
||||
<tbody valign="top">
|
||||
<row>
|
||||
<entry><constant>CEC_TX_STATUS_OK</constant></entry>
|
||||
<entry>0x01</entry>
|
||||
<entry>The message was transmitted successfully. This is mutually exclusive with
|
||||
<constant>CEC_TX_STATUS_MAX_RETRIES</constant>. Other bits can still be set if
|
||||
earlier attempts met with failure before the transmit was eventually successful.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_TX_STATUS_ARB_LOST</constant></entry>
|
||||
<entry>0x02</entry>
|
||||
<entry>CEC line arbitration was lost.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_TX_STATUS_NACK</constant></entry>
|
||||
<entry>0x04</entry>
|
||||
<entry>Message was not acknowledged.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_TX_STATUS_LOW_DRIVE</constant></entry>
|
||||
<entry>0x08</entry>
|
||||
<entry>Low drive was detected on the CEC bus. This indicates that a follower
|
||||
detected an error on the bus and requests a retransmission.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_TX_STATUS_ERROR</constant></entry>
|
||||
<entry>0x10</entry>
|
||||
<entry>Some error occurred. This is used for any errors that do not
|
||||
fit the previous two, either because the hardware could not tell
|
||||
which error occurred, or because the hardware tested for other conditions
|
||||
besides those two.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_TX_STATUS_MAX_RETRIES</constant></entry>
|
||||
<entry>0x20</entry>
|
||||
<entry>The transmit failed after one or more retries. This status bit is mutually
|
||||
exclusive with <constant>CEC_TX_STATUS_OK</constant>. Other bits can still be set
|
||||
to explain which failures were seen.</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
<table pgwide="1" frame="none" id="cec-rx-status">
|
||||
<title>CEC Receive Status</title>
|
||||
<tgroup cols="3">
|
||||
&cs-def;
|
||||
<tbody valign="top">
|
||||
<row>
|
||||
<entry><constant>CEC_RX_STATUS_OK</constant></entry>
|
||||
<entry>0x01</entry>
|
||||
<entry>The message was received successfully.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_RX_STATUS_TIMEOUT</constant></entry>
|
||||
<entry>0x02</entry>
|
||||
<entry>The reply to an earlier transmitted message timed out.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>CEC_RX_STATUS_FEATURE_ABORT</constant></entry>
|
||||
<entry>0x04</entry>
|
||||
<entry>The message was received successfully but the reply was
|
||||
<constant>CEC_MSG_FEATURE_ABORT</constant>. This status is only
|
||||
set if this message was the reply to an earlier transmitted
|
||||
message.</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
&return-value;
|
||||
</refsect1>
|
||||
</refentry>
|
|
@ -75,7 +75,7 @@
|
|||
</mediaobject>
|
||||
</figure>
|
||||
<para>The media infrastructure API was designed to control such
|
||||
devices. It is divided into four parts.</para>
|
||||
devices. It is divided into five parts.</para>
|
||||
<para>The first part covers radio, video capture and output,
|
||||
cameras, analog TV devices and codecs.</para>
|
||||
<para>The second part covers the
|
||||
|
@ -87,6 +87,7 @@
|
|||
<xref linkend="fe-delivery-system-t" />.</para>
|
||||
<para>The third part covers the Remote Controller API.</para>
|
||||
<para>The fourth part covers the Media Controller API.</para>
|
||||
<para>The fifth part covers the CEC (Consumer Electronics Control) API.</para>
|
||||
<para>It should also be noted that a media device may also have audio
|
||||
components, like mixers, PCM capture, PCM playback, etc, which
|
||||
are controlled via ALSA API.</para>
|
||||
|
@ -107,6 +108,9 @@
|
|||
<part id="media_common">
|
||||
&sub-media-controller;
|
||||
</part>
|
||||
<part id="cec">
|
||||
&sub-cec-api;
|
||||
</part>
|
||||
|
||||
<chapter id="gen_errors">
|
||||
&sub-gen-errors;
|
||||
|
|
|
@ -0,0 +1,267 @@
|
|||
CEC Kernel Support
|
||||
==================
|
||||
|
||||
The CEC framework provides a unified kernel interface for use with HDMI CEC
|
||||
hardware. It is designed to handle a multiple types of hardware (receivers,
|
||||
transmitters, USB dongles). The framework also gives the option to decide
|
||||
what to do in the kernel driver and what should be handled by userspace
|
||||
applications. In addition it integrates the remote control passthrough
|
||||
feature into the kernel's remote control framework.
|
||||
|
||||
|
||||
The CEC Protocol
|
||||
----------------
|
||||
|
||||
The CEC protocol enables consumer electronic devices to communicate with each
|
||||
other through the HDMI connection. The protocol uses logical addresses in the
|
||||
communication. The logical address is strictly connected with the functionality
|
||||
provided by the device. The TV acting as the communication hub is always
|
||||
assigned address 0. The physical address is determined by the physical
|
||||
connection between devices.
|
||||
|
||||
The CEC framework described here is up to date with the CEC 2.0 specification.
|
||||
It is documented in the HDMI 1.4 specification with the new 2.0 bits documented
|
||||
in the HDMI 2.0 specification. But for most of the features the freely available
|
||||
HDMI 1.3a specification is sufficient:
|
||||
|
||||
http://www.microprocessor.org/HDMISpecification13a.pdf
|
||||
|
||||
|
||||
The Kernel Interface
|
||||
====================
|
||||
|
||||
CEC Adapter
|
||||
-----------
|
||||
|
||||
The struct cec_adapter represents the CEC adapter hardware. It is created by
|
||||
calling cec_allocate_adapter() and deleted by calling cec_delete_adapter():
|
||||
|
||||
struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
|
||||
void *priv, const char *name, u32 caps, u8 available_las,
|
||||
struct device *parent);
|
||||
void cec_delete_adapter(struct cec_adapter *adap);
|
||||
|
||||
To create an adapter you need to pass the following information:
|
||||
|
||||
ops: adapter operations which are called by the CEC framework and that you
|
||||
have to implement.
|
||||
|
||||
priv: will be stored in adap->priv and can be used by the adapter ops.
|
||||
|
||||
name: the name of the CEC adapter. Note: this name will be copied.
|
||||
|
||||
caps: capabilities of the CEC adapter. These capabilities determine the
|
||||
capabilities of the hardware and which parts are to be handled
|
||||
by userspace and which parts are handled by kernelspace. The
|
||||
capabilities are returned by CEC_ADAP_G_CAPS.
|
||||
|
||||
available_las: the number of simultaneous logical addresses that this
|
||||
adapter can handle. Must be 1 <= available_las <= CEC_MAX_LOG_ADDRS.
|
||||
|
||||
parent: the parent device.
|
||||
|
||||
|
||||
To register the /dev/cecX device node and the remote control device (if
|
||||
CEC_CAP_RC is set) you call:
|
||||
|
||||
int cec_register_adapter(struct cec_adapter *adap);
|
||||
|
||||
To unregister the devices call:
|
||||
|
||||
void cec_unregister_adapter(struct cec_adapter *adap);
|
||||
|
||||
Note: if cec_register_adapter() fails, then call cec_delete_adapter() to
|
||||
clean up. But if cec_register_adapter() succeeded, then only call
|
||||
cec_unregister_adapter() to clean up, never cec_delete_adapter(). The
|
||||
unregister function will delete the adapter automatically once the last user
|
||||
of that /dev/cecX device has closed its file handle.
|
||||
|
||||
|
||||
Implementing the Low-Level CEC Adapter
|
||||
--------------------------------------
|
||||
|
||||
The following low-level adapter operations have to be implemented in
|
||||
your driver:
|
||||
|
||||
struct cec_adap_ops {
|
||||
/* Low-level callbacks */
|
||||
int (*adap_enable)(struct cec_adapter *adap, bool enable);
|
||||
int (*adap_monitor_all_enable)(struct cec_adapter *adap, bool enable);
|
||||
int (*adap_log_addr)(struct cec_adapter *adap, u8 logical_addr);
|
||||
int (*adap_transmit)(struct cec_adapter *adap, u8 attempts,
|
||||
u32 signal_free_time, struct cec_msg *msg);
|
||||
void (*adap_log_status)(struct cec_adapter *adap);
|
||||
|
||||
/* High-level callbacks */
|
||||
...
|
||||
};
|
||||
|
||||
The three low-level ops deal with various aspects of controlling the CEC adapter
|
||||
hardware:
|
||||
|
||||
|
||||
To enable/disable the hardware:
|
||||
|
||||
int (*adap_enable)(struct cec_adapter *adap, bool enable);
|
||||
|
||||
This callback enables or disables the CEC hardware. Enabling the CEC hardware
|
||||
means powering it up in a state where no logical addresses are claimed. This
|
||||
op assumes that the physical address (adap->phys_addr) is valid when enable is
|
||||
true and will not change while the CEC adapter remains enabled. The initial
|
||||
state of the CEC adapter after calling cec_allocate_adapter() is disabled.
|
||||
|
||||
Note that adap_enable must return 0 if enable is false.
|
||||
|
||||
|
||||
To enable/disable the 'monitor all' mode:
|
||||
|
||||
int (*adap_monitor_all_enable)(struct cec_adapter *adap, bool enable);
|
||||
|
||||
If enabled, then the adapter should be put in a mode to also monitor messages
|
||||
that not for us. Not all hardware supports this and this function is only
|
||||
called if the CEC_CAP_MONITOR_ALL capability is set. This callback is optional
|
||||
(some hardware may always be in 'monitor all' mode).
|
||||
|
||||
Note that adap_monitor_all_enable must return 0 if enable is false.
|
||||
|
||||
|
||||
To program a new logical address:
|
||||
|
||||
int (*adap_log_addr)(struct cec_adapter *adap, u8 logical_addr);
|
||||
|
||||
If logical_addr == CEC_LOG_ADDR_INVALID then all programmed logical addresses
|
||||
are to be erased. Otherwise the given logical address should be programmed.
|
||||
If the maximum number of available logical addresses is exceeded, then it
|
||||
should return -ENXIO. Once a logical address is programmed the CEC hardware
|
||||
can receive directed messages to that address.
|
||||
|
||||
Note that adap_log_addr must return 0 if logical_addr is CEC_LOG_ADDR_INVALID.
|
||||
|
||||
|
||||
To transmit a new message:
|
||||
|
||||
int (*adap_transmit)(struct cec_adapter *adap, u8 attempts,
|
||||
u32 signal_free_time, struct cec_msg *msg);
|
||||
|
||||
This transmits a new message. The attempts argument is the suggested number of
|
||||
attempts for the transmit.
|
||||
|
||||
The signal_free_time is the number of data bit periods that the adapter should
|
||||
wait when the line is free before attempting to send a message. This value
|
||||
depends on whether this transmit is a retry, a message from a new initiator or
|
||||
a new message for the same initiator. Most hardware will handle this
|
||||
automatically, but in some cases this information is needed.
|
||||
|
||||
The CEC_FREE_TIME_TO_USEC macro can be used to convert signal_free_time to
|
||||
microseconds (one data bit period is 2.4 ms).
|
||||
|
||||
|
||||
To log the current CEC hardware status:
|
||||
|
||||
void (*adap_status)(struct cec_adapter *adap, struct seq_file *file);
|
||||
|
||||
This optional callback can be used to show the status of the CEC hardware.
|
||||
The status is available through debugfs: cat /sys/kernel/debug/cec/cecX/status
|
||||
|
||||
|
||||
Your adapter driver will also have to react to events (typically interrupt
|
||||
driven) by calling into the framework in the following situations:
|
||||
|
||||
When a transmit finished (successfully or otherwise):
|
||||
|
||||
void cec_transmit_done(struct cec_adapter *adap, u8 status, u8 arb_lost_cnt,
|
||||
u8 nack_cnt, u8 low_drive_cnt, u8 error_cnt);
|
||||
|
||||
The status can be one of:
|
||||
|
||||
CEC_TX_STATUS_OK: the transmit was successful.
|
||||
CEC_TX_STATUS_ARB_LOST: arbitration was lost: another CEC initiator
|
||||
took control of the CEC line and you lost the arbitration.
|
||||
CEC_TX_STATUS_NACK: the message was nacked (for a directed message) or
|
||||
acked (for a broadcast message). A retransmission is needed.
|
||||
CEC_TX_STATUS_LOW_DRIVE: low drive was detected on the CEC bus. This
|
||||
indicates that a follower detected an error on the bus and requested a
|
||||
retransmission.
|
||||
CEC_TX_STATUS_ERROR: some unspecified error occurred: this can be one of
|
||||
the previous two if the hardware cannot differentiate or something else
|
||||
entirely.
|
||||
CEC_TX_STATUS_MAX_RETRIES: could not transmit the message after
|
||||
trying multiple times. Should only be set by the driver if it has hardware
|
||||
support for retrying messages. If set, then the framework assumes that it
|
||||
doesn't have to make another attempt to transmit the message since the
|
||||
hardware did that already.
|
||||
|
||||
The *_cnt arguments are the number of error conditions that were seen.
|
||||
This may be 0 if no information is available. Drivers that do not support
|
||||
hardware retry can just set the counter corresponding to the transmit error
|
||||
to 1, if the hardware does support retry then either set these counters to
|
||||
0 if the hardware provides no feedback of which errors occurred and how many
|
||||
times, or fill in the correct values as reported by the hardware.
|
||||
|
||||
When a CEC message was received:
|
||||
|
||||
void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg);
|
||||
|
||||
Speaks for itself.
|
||||
|
||||
Implementing the High-Level CEC Adapter
|
||||
---------------------------------------
|
||||
|
||||
The low-level operations drive the hardware, the high-level operations are
|
||||
CEC protocol driven. The following high-level callbacks are available:
|
||||
|
||||
struct cec_adap_ops {
|
||||
/* Low-level callbacks */
|
||||
...
|
||||
|
||||
/* High-level CEC message callback */
|
||||
int (*received)(struct cec_adapter *adap, struct cec_msg *msg);
|
||||
};
|
||||
|
||||
The received() callback allows the driver to optionally handle a newly
|
||||
received CEC message
|
||||
|
||||
int (*received)(struct cec_adapter *adap, struct cec_msg *msg);
|
||||
|
||||
If the driver wants to process a CEC message, then it can implement this
|
||||
callback. If it doesn't want to handle this message, then it should return
|
||||
-ENOMSG, otherwise the CEC framework assumes it processed this message and
|
||||
it will not no anything with it.
|
||||
|
||||
|
||||
CEC framework functions
|
||||
-----------------------
|
||||
|
||||
CEC Adapter drivers can call the following CEC framework functions:
|
||||
|
||||
int cec_transmit_msg(struct cec_adapter *adap, struct cec_msg *msg,
|
||||
bool block);
|
||||
|
||||
Transmit a CEC message. If block is true, then wait until the message has been
|
||||
transmitted, otherwise just queue it and return.
|
||||
|
||||
void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block);
|
||||
|
||||
Change the physical address. This function will set adap->phys_addr and
|
||||
send an event if it has changed. If cec_s_log_addrs() has been called and
|
||||
the physical address has become valid, then the CEC framework will start
|
||||
claiming the logical addresses. If block is true, then this function won't
|
||||
return until this process has finished.
|
||||
|
||||
When the physical address is set to a valid value the CEC adapter will
|
||||
be enabled (see the adap_enable op). When it is set to CEC_PHYS_ADDR_INVALID,
|
||||
then the CEC adapter will be disabled. If you change a valid physical address
|
||||
to another valid physical address, then this function will first set the
|
||||
address to CEC_PHYS_ADDR_INVALID before enabling the new physical address.
|
||||
|
||||
int cec_s_log_addrs(struct cec_adapter *adap,
|
||||
struct cec_log_addrs *log_addrs, bool block);
|
||||
|
||||
Claim the CEC logical addresses. Should never be called if CEC_CAP_LOG_ADDRS
|
||||
is set. If block is true, then wait until the logical addresses have been
|
||||
claimed, otherwise just queue it and return. To unconfigure all logical
|
||||
addresses call this function with log_addrs set to NULL or with
|
||||
log_addrs->num_log_addrs set to 0. The block argument is ignored when
|
||||
unconfiguring. This function will just return if the physical address is
|
||||
invalid. Once the physical address becomes valid, then the framework will
|
||||
attempt to claim these logical addresses.
|
|
@ -0,0 +1,31 @@
|
|||
* Samsung HDMI CEC driver
|
||||
|
||||
The HDMI CEC module is present is Samsung SoCs and its purpose is to
|
||||
handle communication between HDMI connected devices over the CEC bus.
|
||||
|
||||
Required properties:
|
||||
- compatible : value should be following
|
||||
"samsung,s5p-cec"
|
||||
|
||||
- reg : Physical base address of the IP registers and length of memory
|
||||
mapped region.
|
||||
|
||||
- interrupts : HDMI CEC interrupt number to the CPU.
|
||||
- clocks : from common clock binding: handle to HDMI CEC clock.
|
||||
- clock-names : from common clock binding: must contain "hdmicec",
|
||||
corresponding to entry in the clocks property.
|
||||
- samsung,syscon-phandle - phandle to the PMU system controller
|
||||
|
||||
Example:
|
||||
|
||||
hdmicec: cec@100B0000 {
|
||||
compatible = "samsung,s5p-cec";
|
||||
reg = <0x100B0000 0x200>;
|
||||
interrupts = <0 114 0>;
|
||||
clocks = <&clock CLK_HDMI_CEC>;
|
||||
clock-names = "hdmicec";
|
||||
samsung,syscon-phandle = <&pmu_system_controller>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&hdmi_cec>;
|
||||
status = "okay";
|
||||
};
|
|
@ -74,7 +74,8 @@ Section 11: Cropping, Composing, Scaling
|
|||
Section 12: Formats
|
||||
Section 13: Capture Overlay
|
||||
Section 14: Output Overlay
|
||||
Section 15: Some Future Improvements
|
||||
Section 15: CEC (Consumer Electronics Control)
|
||||
Section 16: Some Future Improvements
|
||||
|
||||
|
||||
Section 1: Configuring the driver
|
||||
|
@ -364,7 +365,11 @@ For HDMI inputs it is possible to set the EDID. By default a simple EDID
|
|||
is provided. You can only set the EDID for HDMI inputs. Internally, however,
|
||||
the EDID is shared between all HDMI inputs.
|
||||
|
||||
No interpretation is done of the EDID data.
|
||||
No interpretation is done of the EDID data with the exception of the
|
||||
physical address. See the CEC section for more details.
|
||||
|
||||
There is a maximum of 15 HDMI inputs (if there are more, then they will be
|
||||
reduced to 15) since that's the limitation of the EDID physical address.
|
||||
|
||||
|
||||
Section 3: Video Output
|
||||
|
@ -409,6 +414,9 @@ standard, and for all others a 1:1 pixel aspect ratio is returned.
|
|||
|
||||
An HDMI output has a valid EDID which can be obtained through VIDIOC_G_EDID.
|
||||
|
||||
There is a maximum of 15 HDMI outputs (if there are more, then they will be
|
||||
reduced to 15) since that's the limitation of the EDID physical address. See
|
||||
also the CEC section for more details.
|
||||
|
||||
Section 4: VBI Capture
|
||||
----------------------
|
||||
|
@ -1108,7 +1116,26 @@ capabilities will slow down the video loop considerably as a lot of checks have
|
|||
to be done per pixel.
|
||||
|
||||
|
||||
Section 15: Some Future Improvements
|
||||
Section 15: CEC (Consumer Electronics Control)
|
||||
----------------------------------------------
|
||||
|
||||
If there are HDMI inputs then a CEC adapter will be created that has
|
||||
the same number of input ports. This is the equivalent of e.g. a TV that
|
||||
has that number of inputs. Each HDMI output will also create a
|
||||
CEC adapter that is hooked up to the corresponding input port, or (if there
|
||||
are more outputs than inputs) is not hooked up at all. In other words,
|
||||
this is the equivalent of hooking up each output device to an input port of
|
||||
the TV. Any remaining output devices remain unconnected.
|
||||
|
||||
The EDID that each output reads reports a unique CEC physical address that is
|
||||
based on the physical address of the EDID of the input. So if the EDID of the
|
||||
receiver has physical address A.B.0.0, then each output will see an EDID
|
||||
containing physical address A.B.C.0 where C is 1 to the number of inputs. If
|
||||
there are more outputs than inputs then the remaining outputs have a CEC adapter
|
||||
that is disabled and reports an invalid physical address.
|
||||
|
||||
|
||||
Section 16: Some Future Improvements
|
||||
------------------------------------
|
||||
|
||||
Just as a reminder and in no particular order:
|
||||
|
@ -1121,8 +1148,6 @@ Just as a reminder and in no particular order:
|
|||
- Fix sequence/field numbering when looping of video with alternate fields
|
||||
- Add support for V4L2_CID_BG_COLOR for video outputs
|
||||
- Add ARGB888 overlay support: better testing of the alpha channel
|
||||
- Add custom DV timings support
|
||||
- Add support for V4L2_DV_FL_REDUCED_FPS
|
||||
- Improve pixel aspect support in the tpg code by passing a real v4l2_fract
|
||||
- Use per-queue locks and/or per-device locks to improve throughput
|
||||
- Add support to loop from a specific output to a specific input across
|
||||
|
@ -1133,3 +1158,4 @@ Just as a reminder and in no particular order:
|
|||
- Make a thread for the RDS generation, that would help in particular for the
|
||||
"Controls" RDS Rx I/O Mode as the read-only RDS controls could be updated
|
||||
in real-time.
|
||||
- Changing the EDID should cause hotplug detect emulation to happen.
|
||||
|
|
23
MAINTAINERS
23
MAINTAINERS
|
@ -1647,6 +1647,13 @@ L: linux-media@vger.kernel.org
|
|||
S: Maintained
|
||||
F: drivers/media/platform/s5p-tv/
|
||||
|
||||
ARM/SAMSUNG S5P SERIES HDMI CEC SUBSYSTEM SUPPORT
|
||||
M: Kyungmin Park <kyungmin.park@samsung.com>
|
||||
L: linux-arm-kernel@lists.infradead.org
|
||||
L: linux-media@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/staging/media/platform/s5p-cec/
|
||||
|
||||
ARM/SAMSUNG S5P SERIES JPEG CODEC SUPPORT
|
||||
M: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
|
||||
M: Jacek Anaszewski <j.anaszewski@samsung.com>
|
||||
|
@ -2852,6 +2859,22 @@ F: drivers/net/ieee802154/cc2520.c
|
|||
F: include/linux/spi/cc2520.h
|
||||
F: Documentation/devicetree/bindings/net/ieee802154/cc2520.txt
|
||||
|
||||
CEC DRIVER
|
||||
M: Hans Verkuil <hans.verkuil@cisco.com>
|
||||
L: linux-media@vger.kernel.org
|
||||
T: git git://linuxtv.org/media_tree.git
|
||||
W: http://linuxtv.org
|
||||
S: Supported
|
||||
F: Documentation/cec.txt
|
||||
F: Documentation/DocBook/media/v4l/cec*
|
||||
F: drivers/staging/media/cec/
|
||||
F: drivers/media/cec-edid.c
|
||||
F: drivers/media/rc/keymaps/rc-cec.c
|
||||
F: include/media/cec.h
|
||||
F: include/media/cec-edid.h
|
||||
F: include/linux/cec.h
|
||||
F: include/linux/cec-funcs.h
|
||||
|
||||
CELL BROADBAND ENGINE ARCHITECTURE
|
||||
M: Arnd Bergmann <arnd@arndb.de>
|
||||
L: linuxppc-dev@lists.ozlabs.org
|
||||
|
|
|
@ -80,6 +80,9 @@ config MEDIA_RC_SUPPORT
|
|||
|
||||
Say Y when you have a TV or an IR device.
|
||||
|
||||
config MEDIA_CEC_EDID
|
||||
tristate
|
||||
|
||||
#
|
||||
# Media controller
|
||||
# Selectable only for webcam/grabbers, as other drivers don't use it
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
# Makefile for the kernel multimedia device drivers.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_MEDIA_CEC_EDID) += cec-edid.o
|
||||
|
||||
media-objs := media-device.o media-devnode.o media-entity.o
|
||||
|
||||
#
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* cec-edid - HDMI Consumer Electronics Control EDID & CEC helper functions
|
||||
*
|
||||
* Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
||||
*
|
||||
* This program is free software; you may redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <media/cec-edid.h>
|
||||
|
||||
/*
|
||||
* This EDID is expected to be a CEA-861 compliant, which means that there are
|
||||
* at least two blocks and one or more of the extensions blocks are CEA-861
|
||||
* blocks.
|
||||
*
|
||||
* The returned location is guaranteed to be < size - 1.
|
||||
*/
|
||||
static unsigned int cec_get_edid_spa_location(const u8 *edid, unsigned int size)
|
||||
{
|
||||
unsigned int blocks = size / 128;
|
||||
unsigned int block;
|
||||
u8 d;
|
||||
|
||||
/* Sanity check: at least 2 blocks and a multiple of the block size */
|
||||
if (blocks < 2 || size % 128)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If there are fewer extension blocks than the size, then update
|
||||
* 'blocks'. It is allowed to have more extension blocks than the size,
|
||||
* since some hardware can only read e.g. 256 bytes of the EDID, even
|
||||
* though more blocks are present. The first CEA-861 extension block
|
||||
* should normally be in block 1 anyway.
|
||||
*/
|
||||
if (edid[0x7e] + 1 < blocks)
|
||||
blocks = edid[0x7e] + 1;
|
||||
|
||||
for (block = 1; block < blocks; block++) {
|
||||
unsigned int offset = block * 128;
|
||||
|
||||
/* Skip any non-CEA-861 extension blocks */
|
||||
if (edid[offset] != 0x02 || edid[offset + 1] != 0x03)
|
||||
continue;
|
||||
|
||||
/* search Vendor Specific Data Block (tag 3) */
|
||||
d = edid[offset + 2] & 0x7f;
|
||||
/* Check if there are Data Blocks */
|
||||
if (d <= 4)
|
||||
continue;
|
||||
if (d > 4) {
|
||||
unsigned int i = offset + 4;
|
||||
unsigned int end = offset + d;
|
||||
|
||||
/* Note: 'end' is always < 'size' */
|
||||
do {
|
||||
u8 tag = edid[i] >> 5;
|
||||
u8 len = edid[i] & 0x1f;
|
||||
|
||||
if (tag == 3 && len >= 5 && i + len <= end)
|
||||
return i + 4;
|
||||
i += len + 1;
|
||||
} while (i < end);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
u16 cec_get_edid_phys_addr(const u8 *edid, unsigned int size,
|
||||
unsigned int *offset)
|
||||
{
|
||||
unsigned int loc = cec_get_edid_spa_location(edid, size);
|
||||
|
||||
if (offset)
|
||||
*offset = loc;
|
||||
if (loc == 0)
|
||||
return CEC_PHYS_ADDR_INVALID;
|
||||
return (edid[loc] << 8) | edid[loc + 1];
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cec_get_edid_phys_addr);
|
||||
|
||||
void cec_set_edid_phys_addr(u8 *edid, unsigned int size, u16 phys_addr)
|
||||
{
|
||||
unsigned int loc = cec_get_edid_spa_location(edid, size);
|
||||
u8 sum = 0;
|
||||
unsigned int i;
|
||||
|
||||
if (loc == 0)
|
||||
return;
|
||||
edid[loc] = phys_addr >> 8;
|
||||
edid[loc + 1] = phys_addr & 0xff;
|
||||
loc &= ~0x7f;
|
||||
|
||||
/* update the checksum */
|
||||
for (i = loc; i < loc + 127; i++)
|
||||
sum += edid[i];
|
||||
edid[i] = 256 - sum;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cec_set_edid_phys_addr);
|
||||
|
||||
u16 cec_phys_addr_for_input(u16 phys_addr, u8 input)
|
||||
{
|
||||
/* Check if input is sane */
|
||||
if (WARN_ON(input == 0 || input > 0xf))
|
||||
return CEC_PHYS_ADDR_INVALID;
|
||||
|
||||
if (phys_addr == 0)
|
||||
return input << 12;
|
||||
|
||||
if ((phys_addr & 0x0fff) == 0)
|
||||
return phys_addr | (input << 8);
|
||||
|
||||
if ((phys_addr & 0x00ff) == 0)
|
||||
return phys_addr | (input << 4);
|
||||
|
||||
if ((phys_addr & 0x000f) == 0)
|
||||
return phys_addr | input;
|
||||
|
||||
/*
|
||||
* All nibbles are used so no valid physical addresses can be assigned
|
||||
* to the input.
|
||||
*/
|
||||
return CEC_PHYS_ADDR_INVALID;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cec_phys_addr_for_input);
|
||||
|
||||
int cec_phys_addr_validate(u16 phys_addr, u16 *parent, u16 *port)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (parent)
|
||||
*parent = phys_addr;
|
||||
if (port)
|
||||
*port = 0;
|
||||
if (phys_addr == CEC_PHYS_ADDR_INVALID)
|
||||
return 0;
|
||||
for (i = 0; i < 16; i += 4)
|
||||
if (phys_addr & (0xf << i))
|
||||
break;
|
||||
if (i == 16)
|
||||
return 0;
|
||||
if (parent)
|
||||
*parent = phys_addr & (0xfff0 << i);
|
||||
if (port)
|
||||
*port = (phys_addr >> i) & 0xf;
|
||||
for (i += 4; i < 16; i += 4)
|
||||
if ((phys_addr & (0xf << i)) == 0)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cec_phys_addr_validate);
|
||||
|
||||
MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>");
|
||||
MODULE_DESCRIPTION("CEC EDID helper functions");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -209,6 +209,7 @@ config VIDEO_ADV7604
|
|||
depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
select HDMI
|
||||
select MEDIA_CEC_EDID
|
||||
---help---
|
||||
Support for the Analog Devices ADV7604 video decoder.
|
||||
|
||||
|
@ -218,10 +219,18 @@ config VIDEO_ADV7604
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called adv7604.
|
||||
|
||||
config VIDEO_ADV7604_CEC
|
||||
bool "Enable Analog Devices ADV7604 CEC support"
|
||||
depends on VIDEO_ADV7604 && MEDIA_CEC
|
||||
---help---
|
||||
When selected the adv7604 will support the optional
|
||||
HDMI CEC feature.
|
||||
|
||||
config VIDEO_ADV7842
|
||||
tristate "Analog Devices ADV7842 decoder"
|
||||
depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
|
||||
select HDMI
|
||||
select MEDIA_CEC_EDID
|
||||
---help---
|
||||
Support for the Analog Devices ADV7842 video decoder.
|
||||
|
||||
|
@ -231,6 +240,13 @@ config VIDEO_ADV7842
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called adv7842.
|
||||
|
||||
config VIDEO_ADV7842_CEC
|
||||
bool "Enable Analog Devices ADV7842 CEC support"
|
||||
depends on VIDEO_ADV7842 && MEDIA_CEC
|
||||
---help---
|
||||
When selected the adv7842 will support the optional
|
||||
HDMI CEC feature.
|
||||
|
||||
config VIDEO_BT819
|
||||
tristate "BT819A VideoStream decoder"
|
||||
depends on VIDEO_V4L2 && I2C
|
||||
|
@ -447,6 +463,7 @@ config VIDEO_ADV7511
|
|||
tristate "Analog Devices ADV7511 encoder"
|
||||
depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
|
||||
select HDMI
|
||||
select MEDIA_CEC_EDID
|
||||
---help---
|
||||
Support for the Analog Devices ADV7511 video encoder.
|
||||
|
||||
|
@ -455,6 +472,13 @@ config VIDEO_ADV7511
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called adv7511.
|
||||
|
||||
config VIDEO_ADV7511_CEC
|
||||
bool "Enable Analog Devices ADV7511 CEC support"
|
||||
depends on VIDEO_ADV7511 && MEDIA_CEC
|
||||
---help---
|
||||
When selected the adv7511 will support the optional
|
||||
HDMI CEC feature.
|
||||
|
||||
config VIDEO_AD9389B
|
||||
tristate "Analog Devices AD9389B encoder"
|
||||
depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include <media/v4l2-ctrls.h>
|
||||
#include <media/v4l2-dv-timings.h>
|
||||
#include <media/i2c/adv7511.h>
|
||||
#include <media/cec.h>
|
||||
|
||||
static int debug;
|
||||
module_param(debug, int, 0644);
|
||||
|
@ -59,6 +60,8 @@ MODULE_LICENSE("GPL v2");
|
|||
#define ADV7511_MIN_PIXELCLOCK 20000000
|
||||
#define ADV7511_MAX_PIXELCLOCK 225000000
|
||||
|
||||
#define ADV7511_MAX_ADDRS (3)
|
||||
|
||||
/*
|
||||
**********************************************************************
|
||||
*
|
||||
|
@ -90,12 +93,20 @@ struct adv7511_state {
|
|||
struct v4l2_ctrl_handler hdl;
|
||||
int chip_revision;
|
||||
u8 i2c_edid_addr;
|
||||
u8 i2c_cec_addr;
|
||||
u8 i2c_pktmem_addr;
|
||||
u8 i2c_cec_addr;
|
||||
|
||||
struct i2c_client *i2c_cec;
|
||||
struct cec_adapter *cec_adap;
|
||||
u8 cec_addr[ADV7511_MAX_ADDRS];
|
||||
u8 cec_valid_addrs;
|
||||
bool cec_enabled_adap;
|
||||
|
||||
/* Is the adv7511 powered on? */
|
||||
bool power_on;
|
||||
/* Did we receive hotplug and rx-sense signals? */
|
||||
bool have_monitor;
|
||||
bool enabled_irq;
|
||||
/* timings from s_dv_timings */
|
||||
struct v4l2_dv_timings dv_timings;
|
||||
u32 fmt_code;
|
||||
|
@ -227,7 +238,7 @@ static int adv_smbus_read_i2c_block_data(struct i2c_client *client,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static inline void adv7511_edid_rd(struct v4l2_subdev *sd, u16 len, u8 *buf)
|
||||
static void adv7511_edid_rd(struct v4l2_subdev *sd, uint16_t len, uint8_t *buf)
|
||||
{
|
||||
struct adv7511_state *state = get_adv7511_state(sd);
|
||||
int i;
|
||||
|
@ -242,6 +253,34 @@ static inline void adv7511_edid_rd(struct v4l2_subdev *sd, u16 len, u8 *buf)
|
|||
v4l2_err(sd, "%s: i2c read error\n", __func__);
|
||||
}
|
||||
|
||||
static inline int adv7511_cec_read(struct v4l2_subdev *sd, u8 reg)
|
||||
{
|
||||
struct adv7511_state *state = get_adv7511_state(sd);
|
||||
|
||||
return i2c_smbus_read_byte_data(state->i2c_cec, reg);
|
||||
}
|
||||
|
||||
static int adv7511_cec_write(struct v4l2_subdev *sd, u8 reg, u8 val)
|
||||
{
|
||||
struct adv7511_state *state = get_adv7511_state(sd);
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
ret = i2c_smbus_write_byte_data(state->i2c_cec, reg, val);
|
||||
if (ret == 0)
|
||||
return 0;
|
||||
}
|
||||
v4l2_err(sd, "%s: I2C Write Problem\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int adv7511_cec_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask,
|
||||
u8 val)
|
||||
{
|
||||
return adv7511_cec_write(sd, reg, (adv7511_cec_read(sd, reg) & mask) | val);
|
||||
}
|
||||
|
||||
static int adv7511_pktmem_rd(struct v4l2_subdev *sd, u8 reg)
|
||||
{
|
||||
struct adv7511_state *state = get_adv7511_state(sd);
|
||||
|
@ -425,16 +464,28 @@ static const struct v4l2_ctrl_ops adv7511_ctrl_ops = {
|
|||
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
||||
static void adv7511_inv_register(struct v4l2_subdev *sd)
|
||||
{
|
||||
struct adv7511_state *state = get_adv7511_state(sd);
|
||||
|
||||
v4l2_info(sd, "0x000-0x0ff: Main Map\n");
|
||||
if (state->i2c_cec)
|
||||
v4l2_info(sd, "0x100-0x1ff: CEC Map\n");
|
||||
}
|
||||
|
||||
static int adv7511_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
|
||||
{
|
||||
struct adv7511_state *state = get_adv7511_state(sd);
|
||||
|
||||
reg->size = 1;
|
||||
switch (reg->reg >> 8) {
|
||||
case 0:
|
||||
reg->val = adv7511_rd(sd, reg->reg & 0xff);
|
||||
break;
|
||||
case 1:
|
||||
if (state->i2c_cec) {
|
||||
reg->val = adv7511_cec_read(sd, reg->reg & 0xff);
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
default:
|
||||
v4l2_info(sd, "Register %03llx not supported\n", reg->reg);
|
||||
adv7511_inv_register(sd);
|
||||
|
@ -445,10 +496,18 @@ static int adv7511_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *
|
|||
|
||||
static int adv7511_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)
|
||||
{
|
||||
struct adv7511_state *state = get_adv7511_state(sd);
|
||||
|
||||
switch (reg->reg >> 8) {
|
||||
case 0:
|
||||
adv7511_wr(sd, reg->reg & 0xff, reg->val & 0xff);
|
||||
break;
|
||||
case 1:
|
||||
if (state->i2c_cec) {
|
||||
adv7511_cec_write(sd, reg->reg & 0xff, reg->val & 0xff);
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
default:
|
||||
v4l2_info(sd, "Register %03llx not supported\n", reg->reg);
|
||||
adv7511_inv_register(sd);
|
||||
|
@ -536,6 +595,7 @@ static int adv7511_log_status(struct v4l2_subdev *sd)
|
|||
{
|
||||
struct adv7511_state *state = get_adv7511_state(sd);
|
||||
struct adv7511_state_edid *edid = &state->edid;
|
||||
int i;
|
||||
|
||||
static const char * const states[] = {
|
||||
"in reset",
|
||||
|
@ -605,7 +665,23 @@ static int adv7511_log_status(struct v4l2_subdev *sd)
|
|||
else
|
||||
v4l2_info(sd, "no timings set\n");
|
||||
v4l2_info(sd, "i2c edid addr: 0x%x\n", state->i2c_edid_addr);
|
||||
|
||||
if (state->i2c_cec == NULL)
|
||||
return 0;
|
||||
|
||||
v4l2_info(sd, "i2c cec addr: 0x%x\n", state->i2c_cec_addr);
|
||||
|
||||
v4l2_info(sd, "CEC: %s\n", state->cec_enabled_adap ?
|
||||
"enabled" : "disabled");
|
||||
if (state->cec_enabled_adap) {
|
||||
for (i = 0; i < ADV7511_MAX_ADDRS; i++) {
|
||||
bool is_valid = state->cec_valid_addrs & (1 << i);
|
||||
|
||||
if (is_valid)
|
||||
v4l2_info(sd, "CEC Logical Address: 0x%x\n",
|
||||
state->cec_addr[i]);
|
||||
}
|
||||
}
|
||||
v4l2_info(sd, "i2c pktmem addr: 0x%x\n", state->i2c_pktmem_addr);
|
||||
return 0;
|
||||
}
|
||||
|
@ -663,15 +739,197 @@ static int adv7511_s_power(struct v4l2_subdev *sd, int on)
|
|||
return true;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_VIDEO_ADV7511_CEC)
|
||||
static int adv7511_cec_adap_enable(struct cec_adapter *adap, bool enable)
|
||||
{
|
||||
struct adv7511_state *state = adap->priv;
|
||||
struct v4l2_subdev *sd = &state->sd;
|
||||
|
||||
if (state->i2c_cec == NULL)
|
||||
return -EIO;
|
||||
|
||||
if (!state->cec_enabled_adap && enable) {
|
||||
/* power up cec section */
|
||||
adv7511_cec_write_and_or(sd, 0x4e, 0xfc, 0x01);
|
||||
/* legacy mode and clear all rx buffers */
|
||||
adv7511_cec_write(sd, 0x4a, 0x07);
|
||||
adv7511_cec_write(sd, 0x4a, 0);
|
||||
adv7511_cec_write_and_or(sd, 0x11, 0xfe, 0); /* initially disable tx */
|
||||
/* enabled irqs: */
|
||||
/* tx: ready */
|
||||
/* tx: arbitration lost */
|
||||
/* tx: retry timeout */
|
||||
/* rx: ready 1 */
|
||||
if (state->enabled_irq)
|
||||
adv7511_wr_and_or(sd, 0x95, 0xc0, 0x39);
|
||||
} else if (state->cec_enabled_adap && !enable) {
|
||||
if (state->enabled_irq)
|
||||
adv7511_wr_and_or(sd, 0x95, 0xc0, 0x00);
|
||||
/* disable address mask 1-3 */
|
||||
adv7511_cec_write_and_or(sd, 0x4b, 0x8f, 0x00);
|
||||
/* power down cec section */
|
||||
adv7511_cec_write_and_or(sd, 0x4e, 0xfc, 0x00);
|
||||
state->cec_valid_addrs = 0;
|
||||
}
|
||||
state->cec_enabled_adap = enable;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adv7511_cec_adap_log_addr(struct cec_adapter *adap, u8 addr)
|
||||
{
|
||||
struct adv7511_state *state = adap->priv;
|
||||
struct v4l2_subdev *sd = &state->sd;
|
||||
unsigned int i, free_idx = ADV7511_MAX_ADDRS;
|
||||
|
||||
if (!state->cec_enabled_adap)
|
||||
return addr == CEC_LOG_ADDR_INVALID ? 0 : -EIO;
|
||||
|
||||
if (addr == CEC_LOG_ADDR_INVALID) {
|
||||
adv7511_cec_write_and_or(sd, 0x4b, 0x8f, 0);
|
||||
state->cec_valid_addrs = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < ADV7511_MAX_ADDRS; i++) {
|
||||
bool is_valid = state->cec_valid_addrs & (1 << i);
|
||||
|
||||
if (free_idx == ADV7511_MAX_ADDRS && !is_valid)
|
||||
free_idx = i;
|
||||
if (is_valid && state->cec_addr[i] == addr)
|
||||
return 0;
|
||||
}
|
||||
if (i == ADV7511_MAX_ADDRS) {
|
||||
i = free_idx;
|
||||
if (i == ADV7511_MAX_ADDRS)
|
||||
return -ENXIO;
|
||||
}
|
||||
state->cec_addr[i] = addr;
|
||||
state->cec_valid_addrs |= 1 << i;
|
||||
|
||||
switch (i) {
|
||||
case 0:
|
||||
/* enable address mask 0 */
|
||||
adv7511_cec_write_and_or(sd, 0x4b, 0xef, 0x10);
|
||||
/* set address for mask 0 */
|
||||
adv7511_cec_write_and_or(sd, 0x4c, 0xf0, addr);
|
||||
break;
|
||||
case 1:
|
||||
/* enable address mask 1 */
|
||||
adv7511_cec_write_and_or(sd, 0x4b, 0xdf, 0x20);
|
||||
/* set address for mask 1 */
|
||||
adv7511_cec_write_and_or(sd, 0x4c, 0x0f, addr << 4);
|
||||
break;
|
||||
case 2:
|
||||
/* enable address mask 2 */
|
||||
adv7511_cec_write_and_or(sd, 0x4b, 0xbf, 0x40);
|
||||
/* set address for mask 1 */
|
||||
adv7511_cec_write_and_or(sd, 0x4d, 0xf0, addr);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adv7511_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
|
||||
u32 signal_free_time, struct cec_msg *msg)
|
||||
{
|
||||
struct adv7511_state *state = adap->priv;
|
||||
struct v4l2_subdev *sd = &state->sd;
|
||||
u8 len = msg->len;
|
||||
unsigned int i;
|
||||
|
||||
v4l2_dbg(1, debug, sd, "%s: len %d\n", __func__, len);
|
||||
|
||||
if (len > 16) {
|
||||
v4l2_err(sd, "%s: len exceeded 16 (%d)\n", __func__, len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* The number of retries is the number of attempts - 1, but retry
|
||||
* at least once. It's not clear if a value of 0 is allowed, so
|
||||
* let's do at least one retry.
|
||||
*/
|
||||
adv7511_cec_write_and_or(sd, 0x12, ~0x70, max(1, attempts - 1) << 4);
|
||||
|
||||
/* blocking, clear cec tx irq status */
|
||||
adv7511_wr_and_or(sd, 0x97, 0xc7, 0x38);
|
||||
|
||||
/* write data */
|
||||
for (i = 0; i < len; i++)
|
||||
adv7511_cec_write(sd, i, msg->msg[i]);
|
||||
|
||||
/* set length (data + header) */
|
||||
adv7511_cec_write(sd, 0x10, len);
|
||||
/* start transmit, enable tx */
|
||||
adv7511_cec_write(sd, 0x11, 0x01);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void adv_cec_tx_raw_status(struct v4l2_subdev *sd, u8 tx_raw_status)
|
||||
{
|
||||
struct adv7511_state *state = get_adv7511_state(sd);
|
||||
|
||||
if ((adv7511_cec_read(sd, 0x11) & 0x01) == 0) {
|
||||
v4l2_dbg(1, debug, sd, "%s: tx raw: tx disabled\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tx_raw_status & 0x10) {
|
||||
v4l2_dbg(1, debug, sd,
|
||||
"%s: tx raw: arbitration lost\n", __func__);
|
||||
cec_transmit_done(state->cec_adap, CEC_TX_STATUS_ARB_LOST,
|
||||
1, 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
if (tx_raw_status & 0x08) {
|
||||
u8 status;
|
||||
u8 nack_cnt;
|
||||
u8 low_drive_cnt;
|
||||
|
||||
v4l2_dbg(1, debug, sd, "%s: tx raw: retry failed\n", __func__);
|
||||
/*
|
||||
* We set this status bit since this hardware performs
|
||||
* retransmissions.
|
||||
*/
|
||||
status = CEC_TX_STATUS_MAX_RETRIES;
|
||||
nack_cnt = adv7511_cec_read(sd, 0x14) & 0xf;
|
||||
if (nack_cnt)
|
||||
status |= CEC_TX_STATUS_NACK;
|
||||
low_drive_cnt = adv7511_cec_read(sd, 0x14) >> 4;
|
||||
if (low_drive_cnt)
|
||||
status |= CEC_TX_STATUS_LOW_DRIVE;
|
||||
cec_transmit_done(state->cec_adap, status,
|
||||
0, nack_cnt, low_drive_cnt, 0);
|
||||
return;
|
||||
}
|
||||
if (tx_raw_status & 0x20) {
|
||||
v4l2_dbg(1, debug, sd, "%s: tx raw: ready ok\n", __func__);
|
||||
cec_transmit_done(state->cec_adap, CEC_TX_STATUS_OK, 0, 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct cec_adap_ops adv7511_cec_adap_ops = {
|
||||
.adap_enable = adv7511_cec_adap_enable,
|
||||
.adap_log_addr = adv7511_cec_adap_log_addr,
|
||||
.adap_transmit = adv7511_cec_adap_transmit,
|
||||
};
|
||||
#endif
|
||||
|
||||
/* Enable interrupts */
|
||||
static void adv7511_set_isr(struct v4l2_subdev *sd, bool enable)
|
||||
{
|
||||
struct adv7511_state *state = get_adv7511_state(sd);
|
||||
u8 irqs = MASK_ADV7511_HPD_INT | MASK_ADV7511_MSEN_INT;
|
||||
u8 irqs_rd;
|
||||
int retries = 100;
|
||||
|
||||
v4l2_dbg(2, debug, sd, "%s: %s\n", __func__, enable ? "enable" : "disable");
|
||||
|
||||
if (state->enabled_irq == enable)
|
||||
return;
|
||||
state->enabled_irq = enable;
|
||||
|
||||
/* The datasheet says that the EDID ready interrupt should be
|
||||
disabled if there is no hotplug. */
|
||||
if (!enable)
|
||||
|
@ -679,6 +937,9 @@ static void adv7511_set_isr(struct v4l2_subdev *sd, bool enable)
|
|||
else if (adv7511_have_hotplug(sd))
|
||||
irqs |= MASK_ADV7511_EDID_RDY_INT;
|
||||
|
||||
adv7511_wr_and_or(sd, 0x95, 0xc0,
|
||||
(state->cec_enabled_adap && enable) ? 0x39 : 0x00);
|
||||
|
||||
/*
|
||||
* This i2c write can fail (approx. 1 in 1000 writes). But it
|
||||
* is essential that this register is correct, so retry it
|
||||
|
@ -701,20 +962,53 @@ static void adv7511_set_isr(struct v4l2_subdev *sd, bool enable)
|
|||
static int adv7511_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
|
||||
{
|
||||
u8 irq_status;
|
||||
u8 cec_irq;
|
||||
|
||||
/* disable interrupts to prevent a race condition */
|
||||
adv7511_set_isr(sd, false);
|
||||
irq_status = adv7511_rd(sd, 0x96);
|
||||
cec_irq = adv7511_rd(sd, 0x97);
|
||||
/* clear detected interrupts */
|
||||
adv7511_wr(sd, 0x96, irq_status);
|
||||
adv7511_wr(sd, 0x97, cec_irq);
|
||||
|
||||
v4l2_dbg(1, debug, sd, "%s: irq 0x%x\n", __func__, irq_status);
|
||||
v4l2_dbg(1, debug, sd, "%s: irq 0x%x, cec-irq 0x%x\n", __func__,
|
||||
irq_status, cec_irq);
|
||||
|
||||
if (irq_status & (MASK_ADV7511_HPD_INT | MASK_ADV7511_MSEN_INT))
|
||||
adv7511_check_monitor_present_status(sd);
|
||||
if (irq_status & MASK_ADV7511_EDID_RDY_INT)
|
||||
adv7511_check_edid_status(sd);
|
||||
|
||||
#if IS_ENABLED(CONFIG_VIDEO_ADV7511_CEC)
|
||||
if (cec_irq & 0x38)
|
||||
adv_cec_tx_raw_status(sd, cec_irq);
|
||||
|
||||
if (cec_irq & 1) {
|
||||
struct adv7511_state *state = get_adv7511_state(sd);
|
||||
struct cec_msg msg;
|
||||
|
||||
msg.len = adv7511_cec_read(sd, 0x25) & 0x1f;
|
||||
|
||||
v4l2_dbg(1, debug, sd, "%s: cec msg len %d\n", __func__,
|
||||
msg.len);
|
||||
|
||||
if (msg.len > 16)
|
||||
msg.len = 16;
|
||||
|
||||
if (msg.len) {
|
||||
u8 i;
|
||||
|
||||
for (i = 0; i < msg.len; i++)
|
||||
msg.msg[i] = adv7511_cec_read(sd, i + 0x15);
|
||||
|
||||
adv7511_cec_write(sd, 0x4a, 1); /* toggle to re-enable rx 1 */
|
||||
adv7511_cec_write(sd, 0x4a, 0);
|
||||
cec_received_msg(state->cec_adap, &msg);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* enable interrupts */
|
||||
adv7511_set_isr(sd, true);
|
||||
|
||||
|
@ -1183,6 +1477,8 @@ static void adv7511_notify_no_edid(struct v4l2_subdev *sd)
|
|||
/* We failed to read the EDID, so send an event for this. */
|
||||
ed.present = false;
|
||||
ed.segment = adv7511_rd(sd, 0xc4);
|
||||
ed.phys_addr = CEC_PHYS_ADDR_INVALID;
|
||||
cec_s_phys_addr(state->cec_adap, ed.phys_addr, false);
|
||||
v4l2_subdev_notify(sd, ADV7511_EDID_DETECT, (void *)&ed);
|
||||
v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, 0x0);
|
||||
}
|
||||
|
@ -1406,13 +1702,16 @@ static bool adv7511_check_edid_status(struct v4l2_subdev *sd)
|
|||
|
||||
v4l2_dbg(1, debug, sd, "%s: edid complete with %d segment(s)\n", __func__, state->edid.segments);
|
||||
state->edid.complete = true;
|
||||
|
||||
ed.phys_addr = cec_get_edid_phys_addr(state->edid.data,
|
||||
state->edid.segments * 256,
|
||||
NULL);
|
||||
/* report when we have all segments
|
||||
but report only for segment 0
|
||||
*/
|
||||
ed.present = true;
|
||||
ed.segment = 0;
|
||||
state->edid_detect_counter++;
|
||||
cec_s_phys_addr(state->cec_adap, ed.phys_addr, false);
|
||||
v4l2_subdev_notify(sd, ADV7511_EDID_DETECT, (void *)&ed);
|
||||
return ed.present;
|
||||
}
|
||||
|
@ -1420,17 +1719,43 @@ static bool adv7511_check_edid_status(struct v4l2_subdev *sd)
|
|||
return false;
|
||||
}
|
||||
|
||||
static int adv7511_registered(struct v4l2_subdev *sd)
|
||||
{
|
||||
struct adv7511_state *state = get_adv7511_state(sd);
|
||||
int err;
|
||||
|
||||
err = cec_register_adapter(state->cec_adap);
|
||||
if (err)
|
||||
cec_delete_adapter(state->cec_adap);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void adv7511_unregistered(struct v4l2_subdev *sd)
|
||||
{
|
||||
struct adv7511_state *state = get_adv7511_state(sd);
|
||||
|
||||
cec_unregister_adapter(state->cec_adap);
|
||||
}
|
||||
|
||||
static const struct v4l2_subdev_internal_ops adv7511_int_ops = {
|
||||
.registered = adv7511_registered,
|
||||
.unregistered = adv7511_unregistered,
|
||||
};
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* Setup ADV7511 */
|
||||
static void adv7511_init_setup(struct v4l2_subdev *sd)
|
||||
{
|
||||
struct adv7511_state *state = get_adv7511_state(sd);
|
||||
struct adv7511_state_edid *edid = &state->edid;
|
||||
u32 cec_clk = state->pdata.cec_clk;
|
||||
u8 ratio;
|
||||
|
||||
v4l2_dbg(1, debug, sd, "%s\n", __func__);
|
||||
|
||||
/* clear all interrupts */
|
||||
adv7511_wr(sd, 0x96, 0xff);
|
||||
adv7511_wr(sd, 0x97, 0xff);
|
||||
/*
|
||||
* Stop HPD from resetting a lot of registers.
|
||||
* It might leave the chip in a partly un-initialized state,
|
||||
|
@ -1442,6 +1767,25 @@ static void adv7511_init_setup(struct v4l2_subdev *sd)
|
|||
adv7511_set_isr(sd, false);
|
||||
adv7511_s_stream(sd, false);
|
||||
adv7511_s_audio_stream(sd, false);
|
||||
|
||||
if (state->i2c_cec == NULL)
|
||||
return;
|
||||
|
||||
v4l2_dbg(1, debug, sd, "%s: cec_clk %d\n", __func__, cec_clk);
|
||||
|
||||
/* cec soft reset */
|
||||
adv7511_cec_write(sd, 0x50, 0x01);
|
||||
adv7511_cec_write(sd, 0x50, 0x00);
|
||||
|
||||
/* legacy mode */
|
||||
adv7511_cec_write(sd, 0x4a, 0x00);
|
||||
|
||||
if (cec_clk % 750000 != 0)
|
||||
v4l2_err(sd, "%s: cec_clk %d, not multiple of 750 Khz\n",
|
||||
__func__, cec_clk);
|
||||
|
||||
ratio = (cec_clk / 750000) - 1;
|
||||
adv7511_cec_write(sd, 0x4e, ratio << 2);
|
||||
}
|
||||
|
||||
static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
|
@ -1476,6 +1820,7 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *
|
|||
client->addr << 1);
|
||||
|
||||
v4l2_i2c_subdev_init(sd, client, &adv7511_ops);
|
||||
sd->internal_ops = &adv7511_int_ops;
|
||||
|
||||
hdl = &state->hdl;
|
||||
v4l2_ctrl_handler_init(hdl, 10);
|
||||
|
@ -1516,26 +1861,47 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *
|
|||
chip_id[0] = adv7511_rd(sd, 0xf5);
|
||||
chip_id[1] = adv7511_rd(sd, 0xf6);
|
||||
if (chip_id[0] != 0x75 || chip_id[1] != 0x11) {
|
||||
v4l2_err(sd, "chip_id != 0x7511, read 0x%02x%02x\n", chip_id[0], chip_id[1]);
|
||||
v4l2_err(sd, "chip_id != 0x7511, read 0x%02x%02x\n", chip_id[0],
|
||||
chip_id[1]);
|
||||
err = -EIO;
|
||||
goto err_entity;
|
||||
}
|
||||
|
||||
state->i2c_edid = i2c_new_dummy(client->adapter, state->i2c_edid_addr >> 1);
|
||||
state->i2c_edid = i2c_new_dummy(client->adapter,
|
||||
state->i2c_edid_addr >> 1);
|
||||
if (state->i2c_edid == NULL) {
|
||||
v4l2_err(sd, "failed to register edid i2c client\n");
|
||||
err = -ENOMEM;
|
||||
goto err_entity;
|
||||
}
|
||||
|
||||
adv7511_wr(sd, 0xe1, state->i2c_cec_addr);
|
||||
if (state->pdata.cec_clk < 3000000 ||
|
||||
state->pdata.cec_clk > 100000000) {
|
||||
v4l2_err(sd, "%s: cec_clk %u outside range, disabling cec\n",
|
||||
__func__, state->pdata.cec_clk);
|
||||
state->pdata.cec_clk = 0;
|
||||
}
|
||||
|
||||
if (state->pdata.cec_clk) {
|
||||
state->i2c_cec = i2c_new_dummy(client->adapter,
|
||||
state->i2c_cec_addr >> 1);
|
||||
if (state->i2c_cec == NULL) {
|
||||
v4l2_err(sd, "failed to register cec i2c client\n");
|
||||
goto err_unreg_edid;
|
||||
}
|
||||
adv7511_wr(sd, 0xe2, 0x00); /* power up cec section */
|
||||
} else {
|
||||
adv7511_wr(sd, 0xe2, 0x01); /* power down cec section */
|
||||
}
|
||||
|
||||
state->i2c_pktmem = i2c_new_dummy(client->adapter, state->i2c_pktmem_addr >> 1);
|
||||
if (state->i2c_pktmem == NULL) {
|
||||
v4l2_err(sd, "failed to register pktmem i2c client\n");
|
||||
err = -ENOMEM;
|
||||
goto err_unreg_edid;
|
||||
goto err_unreg_cec;
|
||||
}
|
||||
|
||||
adv7511_wr(sd, 0xe2, 0x01); /* power down cec section */
|
||||
state->work_queue = create_singlethread_workqueue(sd->name);
|
||||
if (state->work_queue == NULL) {
|
||||
v4l2_err(sd, "could not create workqueue\n");
|
||||
|
@ -1546,6 +1912,19 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *
|
|||
INIT_DELAYED_WORK(&state->edid_handler, adv7511_edid_handler);
|
||||
|
||||
adv7511_init_setup(sd);
|
||||
|
||||
#if IS_ENABLED(CONFIG_VIDEO_ADV7511_CEC)
|
||||
state->cec_adap = cec_allocate_adapter(&adv7511_cec_adap_ops,
|
||||
state, dev_name(&client->dev), CEC_CAP_TRANSMIT |
|
||||
CEC_CAP_LOG_ADDRS | CEC_CAP_PASSTHROUGH | CEC_CAP_RC,
|
||||
ADV7511_MAX_ADDRS, &client->dev);
|
||||
err = PTR_ERR_OR_ZERO(state->cec_adap);
|
||||
if (err) {
|
||||
destroy_workqueue(state->work_queue);
|
||||
goto err_unreg_pktmem;
|
||||
}
|
||||
#endif
|
||||
|
||||
adv7511_set_isr(sd, true);
|
||||
adv7511_check_monitor_present_status(sd);
|
||||
|
||||
|
@ -1555,6 +1934,9 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *
|
|||
|
||||
err_unreg_pktmem:
|
||||
i2c_unregister_device(state->i2c_pktmem);
|
||||
err_unreg_cec:
|
||||
if (state->i2c_cec)
|
||||
i2c_unregister_device(state->i2c_cec);
|
||||
err_unreg_edid:
|
||||
i2c_unregister_device(state->i2c_edid);
|
||||
err_entity:
|
||||
|
@ -1576,9 +1958,12 @@ static int adv7511_remove(struct i2c_client *client)
|
|||
v4l2_dbg(1, debug, sd, "%s removed @ 0x%x (%s)\n", client->name,
|
||||
client->addr << 1, client->adapter->name);
|
||||
|
||||
adv7511_set_isr(sd, false);
|
||||
adv7511_init_setup(sd);
|
||||
cancel_delayed_work(&state->edid_handler);
|
||||
i2c_unregister_device(state->i2c_edid);
|
||||
if (state->i2c_cec)
|
||||
i2c_unregister_device(state->i2c_cec);
|
||||
i2c_unregister_device(state->i2c_pktmem);
|
||||
destroy_workqueue(state->work_queue);
|
||||
v4l2_device_unregister_subdev(sd);
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include <linux/regmap.h>
|
||||
|
||||
#include <media/i2c/adv7604.h>
|
||||
#include <media/cec.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <media/v4l2-event.h>
|
||||
|
@ -80,6 +81,8 @@ MODULE_LICENSE("GPL");
|
|||
|
||||
#define ADV76XX_OP_SWAP_CB_CR (1 << 0)
|
||||
|
||||
#define ADV76XX_MAX_ADDRS (3)
|
||||
|
||||
enum adv76xx_type {
|
||||
ADV7604,
|
||||
ADV7611,
|
||||
|
@ -188,6 +191,12 @@ struct adv76xx_state {
|
|||
struct delayed_work delayed_work_enable_hotplug;
|
||||
bool restart_stdi_once;
|
||||
|
||||
/* CEC */
|
||||
struct cec_adapter *cec_adap;
|
||||
u8 cec_addr[ADV76XX_MAX_ADDRS];
|
||||
u8 cec_valid_addrs;
|
||||
bool cec_enabled_adap;
|
||||
|
||||
/* i2c clients */
|
||||
struct i2c_client *i2c_clients[ADV76XX_PAGE_MAX];
|
||||
|
||||
|
@ -381,7 +390,8 @@ static inline int io_write(struct v4l2_subdev *sd, u8 reg, u8 val)
|
|||
return regmap_write(state->regmap[ADV76XX_PAGE_IO], reg, val);
|
||||
}
|
||||
|
||||
static inline int io_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
|
||||
static inline int io_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask,
|
||||
u8 val)
|
||||
{
|
||||
return io_write(sd, reg, (io_read(sd, reg) & ~mask) | val);
|
||||
}
|
||||
|
@ -414,6 +424,12 @@ static inline int cec_write(struct v4l2_subdev *sd, u8 reg, u8 val)
|
|||
return regmap_write(state->regmap[ADV76XX_PAGE_CEC], reg, val);
|
||||
}
|
||||
|
||||
static inline int cec_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask,
|
||||
u8 val)
|
||||
{
|
||||
return cec_write(sd, reg, (cec_read(sd, reg) & ~mask) | val);
|
||||
}
|
||||
|
||||
static inline int infoframe_read(struct v4l2_subdev *sd, u8 reg)
|
||||
{
|
||||
struct adv76xx_state *state = to_state(sd);
|
||||
|
@ -892,9 +908,9 @@ static int adv76xx_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd)
|
|||
{
|
||||
struct adv76xx_state *state = to_state(sd);
|
||||
const struct adv76xx_chip_info *info = state->info;
|
||||
u16 cable_det = info->read_cable_det(sd);
|
||||
|
||||
return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl,
|
||||
info->read_cable_det(sd));
|
||||
return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, cable_det);
|
||||
}
|
||||
|
||||
static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd,
|
||||
|
@ -1924,6 +1940,210 @@ static int adv76xx_set_format(struct v4l2_subdev *sd,
|
|||
return 0;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC)
|
||||
static void adv76xx_cec_tx_raw_status(struct v4l2_subdev *sd, u8 tx_raw_status)
|
||||
{
|
||||
struct adv76xx_state *state = to_state(sd);
|
||||
|
||||
if ((cec_read(sd, 0x11) & 0x01) == 0) {
|
||||
v4l2_dbg(1, debug, sd, "%s: tx raw: tx disabled\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tx_raw_status & 0x02) {
|
||||
v4l2_dbg(1, debug, sd, "%s: tx raw: arbitration lost\n",
|
||||
__func__);
|
||||
cec_transmit_done(state->cec_adap, CEC_TX_STATUS_ARB_LOST,
|
||||
1, 0, 0, 0);
|
||||
}
|
||||
if (tx_raw_status & 0x04) {
|
||||
u8 status;
|
||||
u8 nack_cnt;
|
||||
u8 low_drive_cnt;
|
||||
|
||||
v4l2_dbg(1, debug, sd, "%s: tx raw: retry failed\n", __func__);
|
||||
/*
|
||||
* We set this status bit since this hardware performs
|
||||
* retransmissions.
|
||||
*/
|
||||
status = CEC_TX_STATUS_MAX_RETRIES;
|
||||
nack_cnt = cec_read(sd, 0x14) & 0xf;
|
||||
if (nack_cnt)
|
||||
status |= CEC_TX_STATUS_NACK;
|
||||
low_drive_cnt = cec_read(sd, 0x14) >> 4;
|
||||
if (low_drive_cnt)
|
||||
status |= CEC_TX_STATUS_LOW_DRIVE;
|
||||
cec_transmit_done(state->cec_adap, status,
|
||||
0, nack_cnt, low_drive_cnt, 0);
|
||||
return;
|
||||
}
|
||||
if (tx_raw_status & 0x01) {
|
||||
v4l2_dbg(1, debug, sd, "%s: tx raw: ready ok\n", __func__);
|
||||
cec_transmit_done(state->cec_adap, CEC_TX_STATUS_OK, 0, 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void adv76xx_cec_isr(struct v4l2_subdev *sd, bool *handled)
|
||||
{
|
||||
struct adv76xx_state *state = to_state(sd);
|
||||
u8 cec_irq;
|
||||
|
||||
/* cec controller */
|
||||
cec_irq = io_read(sd, 0x4d) & 0x0f;
|
||||
if (!cec_irq)
|
||||
return;
|
||||
|
||||
v4l2_dbg(1, debug, sd, "%s: cec: irq 0x%x\n", __func__, cec_irq);
|
||||
adv76xx_cec_tx_raw_status(sd, cec_irq);
|
||||
if (cec_irq & 0x08) {
|
||||
struct cec_msg msg;
|
||||
|
||||
msg.len = cec_read(sd, 0x25) & 0x1f;
|
||||
if (msg.len > 16)
|
||||
msg.len = 16;
|
||||
|
||||
if (msg.len) {
|
||||
u8 i;
|
||||
|
||||
for (i = 0; i < msg.len; i++)
|
||||
msg.msg[i] = cec_read(sd, i + 0x15);
|
||||
cec_write(sd, 0x26, 0x01); /* re-enable rx */
|
||||
cec_received_msg(state->cec_adap, &msg);
|
||||
}
|
||||
}
|
||||
|
||||
/* note: the bit order is swapped between 0x4d and 0x4e */
|
||||
cec_irq = ((cec_irq & 0x08) >> 3) | ((cec_irq & 0x04) >> 1) |
|
||||
((cec_irq & 0x02) << 1) | ((cec_irq & 0x01) << 3);
|
||||
io_write(sd, 0x4e, cec_irq);
|
||||
|
||||
if (handled)
|
||||
*handled = true;
|
||||
}
|
||||
|
||||
static int adv76xx_cec_adap_enable(struct cec_adapter *adap, bool enable)
|
||||
{
|
||||
struct adv76xx_state *state = adap->priv;
|
||||
struct v4l2_subdev *sd = &state->sd;
|
||||
|
||||
if (!state->cec_enabled_adap && enable) {
|
||||
cec_write_clr_set(sd, 0x2a, 0x01, 0x01); /* power up cec */
|
||||
cec_write(sd, 0x2c, 0x01); /* cec soft reset */
|
||||
cec_write_clr_set(sd, 0x11, 0x01, 0); /* initially disable tx */
|
||||
/* enabled irqs: */
|
||||
/* tx: ready */
|
||||
/* tx: arbitration lost */
|
||||
/* tx: retry timeout */
|
||||
/* rx: ready */
|
||||
io_write_clr_set(sd, 0x50, 0x0f, 0x0f);
|
||||
cec_write(sd, 0x26, 0x01); /* enable rx */
|
||||
} else if (state->cec_enabled_adap && !enable) {
|
||||
/* disable cec interrupts */
|
||||
io_write_clr_set(sd, 0x50, 0x0f, 0x00);
|
||||
/* disable address mask 1-3 */
|
||||
cec_write_clr_set(sd, 0x27, 0x70, 0x00);
|
||||
/* power down cec section */
|
||||
cec_write_clr_set(sd, 0x2a, 0x01, 0x00);
|
||||
state->cec_valid_addrs = 0;
|
||||
}
|
||||
state->cec_enabled_adap = enable;
|
||||
adv76xx_s_detect_tx_5v_ctrl(sd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adv76xx_cec_adap_log_addr(struct cec_adapter *adap, u8 addr)
|
||||
{
|
||||
struct adv76xx_state *state = adap->priv;
|
||||
struct v4l2_subdev *sd = &state->sd;
|
||||
unsigned int i, free_idx = ADV76XX_MAX_ADDRS;
|
||||
|
||||
if (!state->cec_enabled_adap)
|
||||
return addr == CEC_LOG_ADDR_INVALID ? 0 : -EIO;
|
||||
|
||||
if (addr == CEC_LOG_ADDR_INVALID) {
|
||||
cec_write_clr_set(sd, 0x27, 0x70, 0);
|
||||
state->cec_valid_addrs = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < ADV76XX_MAX_ADDRS; i++) {
|
||||
bool is_valid = state->cec_valid_addrs & (1 << i);
|
||||
|
||||
if (free_idx == ADV76XX_MAX_ADDRS && !is_valid)
|
||||
free_idx = i;
|
||||
if (is_valid && state->cec_addr[i] == addr)
|
||||
return 0;
|
||||
}
|
||||
if (i == ADV76XX_MAX_ADDRS) {
|
||||
i = free_idx;
|
||||
if (i == ADV76XX_MAX_ADDRS)
|
||||
return -ENXIO;
|
||||
}
|
||||
state->cec_addr[i] = addr;
|
||||
state->cec_valid_addrs |= 1 << i;
|
||||
|
||||
switch (i) {
|
||||
case 0:
|
||||
/* enable address mask 0 */
|
||||
cec_write_clr_set(sd, 0x27, 0x10, 0x10);
|
||||
/* set address for mask 0 */
|
||||
cec_write_clr_set(sd, 0x28, 0x0f, addr);
|
||||
break;
|
||||
case 1:
|
||||
/* enable address mask 1 */
|
||||
cec_write_clr_set(sd, 0x27, 0x20, 0x20);
|
||||
/* set address for mask 1 */
|
||||
cec_write_clr_set(sd, 0x28, 0xf0, addr << 4);
|
||||
break;
|
||||
case 2:
|
||||
/* enable address mask 2 */
|
||||
cec_write_clr_set(sd, 0x27, 0x40, 0x40);
|
||||
/* set address for mask 1 */
|
||||
cec_write_clr_set(sd, 0x29, 0x0f, addr);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adv76xx_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
|
||||
u32 signal_free_time, struct cec_msg *msg)
|
||||
{
|
||||
struct adv76xx_state *state = adap->priv;
|
||||
struct v4l2_subdev *sd = &state->sd;
|
||||
u8 len = msg->len;
|
||||
unsigned int i;
|
||||
|
||||
/*
|
||||
* The number of retries is the number of attempts - 1, but retry
|
||||
* at least once. It's not clear if a value of 0 is allowed, so
|
||||
* let's do at least one retry.
|
||||
*/
|
||||
cec_write_clr_set(sd, 0x12, 0x70, max(1, attempts - 1) << 4);
|
||||
|
||||
if (len > 16) {
|
||||
v4l2_err(sd, "%s: len exceeded 16 (%d)\n", __func__, len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* write data */
|
||||
for (i = 0; i < len; i++)
|
||||
cec_write(sd, i, msg->msg[i]);
|
||||
|
||||
/* set length (data + header) */
|
||||
cec_write(sd, 0x10, len);
|
||||
/* start transmit, enable tx */
|
||||
cec_write(sd, 0x11, 0x01);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct cec_adap_ops adv76xx_cec_adap_ops = {
|
||||
.adap_enable = adv76xx_cec_adap_enable,
|
||||
.adap_log_addr = adv76xx_cec_adap_log_addr,
|
||||
.adap_transmit = adv76xx_cec_adap_transmit,
|
||||
};
|
||||
#endif
|
||||
|
||||
static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
|
||||
{
|
||||
struct adv76xx_state *state = to_state(sd);
|
||||
|
@ -1969,6 +2189,11 @@ static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
|
|||
*handled = true;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC)
|
||||
/* cec */
|
||||
adv76xx_cec_isr(sd, handled);
|
||||
#endif
|
||||
|
||||
/* tx 5v detect */
|
||||
tx_5v = irq_reg_0x70 & info->cable_det_mask;
|
||||
if (tx_5v) {
|
||||
|
@ -2018,39 +2243,12 @@ static int adv76xx_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int get_edid_spa_location(const u8 *edid)
|
||||
{
|
||||
u8 d;
|
||||
|
||||
if ((edid[0x7e] != 1) ||
|
||||
(edid[0x80] != 0x02) ||
|
||||
(edid[0x81] != 0x03)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* search Vendor Specific Data Block (tag 3) */
|
||||
d = edid[0x82] & 0x7f;
|
||||
if (d > 4) {
|
||||
int i = 0x84;
|
||||
int end = 0x80 + d;
|
||||
|
||||
do {
|
||||
u8 tag = edid[i] >> 5;
|
||||
u8 len = edid[i] & 0x1f;
|
||||
|
||||
if ((tag == 3) && (len >= 5))
|
||||
return i + 4;
|
||||
i += len + 1;
|
||||
} while (i < end);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
|
||||
{
|
||||
struct adv76xx_state *state = to_state(sd);
|
||||
const struct adv76xx_chip_info *info = state->info;
|
||||
int spa_loc;
|
||||
unsigned int spa_loc;
|
||||
u16 pa;
|
||||
int err;
|
||||
int i;
|
||||
|
||||
|
@ -2081,6 +2279,10 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
|
|||
edid->blocks = 2;
|
||||
return -E2BIG;
|
||||
}
|
||||
pa = cec_get_edid_phys_addr(edid->edid, edid->blocks * 128, &spa_loc);
|
||||
err = cec_phys_addr_validate(pa, &pa, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
v4l2_dbg(2, debug, sd, "%s: write EDID pad %d, edid.present = 0x%x\n",
|
||||
__func__, edid->pad, state->edid.present);
|
||||
|
@ -2090,9 +2292,12 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
|
|||
adv76xx_set_hpd(state, 0);
|
||||
rep_write_clr_set(sd, info->edid_enable_reg, 0x0f, 0x00);
|
||||
|
||||
spa_loc = get_edid_spa_location(edid->edid);
|
||||
if (spa_loc < 0)
|
||||
spa_loc = 0xc0; /* Default value [REF_02, p. 116] */
|
||||
/*
|
||||
* Return an error if no location of the source physical address
|
||||
* was found.
|
||||
*/
|
||||
if (spa_loc == 0)
|
||||
return -EINVAL;
|
||||
|
||||
switch (edid->pad) {
|
||||
case ADV76XX_PAD_HDMI_PORT_A:
|
||||
|
@ -2152,6 +2357,7 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
|
|||
v4l2_err(sd, "error enabling edid (0x%x)\n", state->edid.present);
|
||||
return -EIO;
|
||||
}
|
||||
cec_s_phys_addr(state->cec_adap, pa, false);
|
||||
|
||||
/* enable hotplug after 100 ms */
|
||||
schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 10);
|
||||
|
@ -2275,8 +2481,19 @@ static int adv76xx_log_status(struct v4l2_subdev *sd)
|
|||
((edid_enabled & 0x02) ? "Yes" : "No"),
|
||||
((edid_enabled & 0x04) ? "Yes" : "No"),
|
||||
((edid_enabled & 0x08) ? "Yes" : "No"));
|
||||
v4l2_info(sd, "CEC: %s\n", !!(cec_read(sd, 0x2a) & 0x01) ?
|
||||
v4l2_info(sd, "CEC: %s\n", state->cec_enabled_adap ?
|
||||
"enabled" : "disabled");
|
||||
if (state->cec_enabled_adap) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ADV76XX_MAX_ADDRS; i++) {
|
||||
bool is_valid = state->cec_valid_addrs & (1 << i);
|
||||
|
||||
if (is_valid)
|
||||
v4l2_info(sd, "CEC Logical Address: 0x%x\n",
|
||||
state->cec_addr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
v4l2_info(sd, "-----Signal status-----\n");
|
||||
cable_det = info->read_cable_det(sd);
|
||||
|
@ -2386,6 +2603,24 @@ static int adv76xx_subscribe_event(struct v4l2_subdev *sd,
|
|||
}
|
||||
}
|
||||
|
||||
static int adv76xx_registered(struct v4l2_subdev *sd)
|
||||
{
|
||||
struct adv76xx_state *state = to_state(sd);
|
||||
int err;
|
||||
|
||||
err = cec_register_adapter(state->cec_adap);
|
||||
if (err)
|
||||
cec_delete_adapter(state->cec_adap);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void adv76xx_unregistered(struct v4l2_subdev *sd)
|
||||
{
|
||||
struct adv76xx_state *state = to_state(sd);
|
||||
|
||||
cec_unregister_adapter(state->cec_adap);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
static const struct v4l2_ctrl_ops adv76xx_ctrl_ops = {
|
||||
|
@ -2429,6 +2664,11 @@ static const struct v4l2_subdev_ops adv76xx_ops = {
|
|||
.pad = &adv76xx_pad_ops,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_internal_ops adv76xx_int_ops = {
|
||||
.registered = adv76xx_registered,
|
||||
.unregistered = adv76xx_unregistered,
|
||||
};
|
||||
|
||||
/* -------------------------- custom ctrls ---------------------------------- */
|
||||
|
||||
static const struct v4l2_ctrl_config adv7604_ctrl_analog_sampling_phase = {
|
||||
|
@ -3111,6 +3351,7 @@ static int adv76xx_probe(struct i2c_client *client,
|
|||
id->name, i2c_adapter_id(client->adapter),
|
||||
client->addr);
|
||||
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
|
||||
sd->internal_ops = &adv76xx_int_ops;
|
||||
|
||||
/* Configure IO Regmap region */
|
||||
err = configure_regmap(state, ADV76XX_PAGE_IO);
|
||||
|
@ -3246,6 +3487,18 @@ static int adv76xx_probe(struct i2c_client *client,
|
|||
err = adv76xx_core_init(sd);
|
||||
if (err)
|
||||
goto err_entity;
|
||||
|
||||
#if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC)
|
||||
state->cec_adap = cec_allocate_adapter(&adv76xx_cec_adap_ops,
|
||||
state, dev_name(&client->dev),
|
||||
CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS |
|
||||
CEC_CAP_PASSTHROUGH | CEC_CAP_RC, ADV76XX_MAX_ADDRS,
|
||||
&client->dev);
|
||||
err = PTR_ERR_OR_ZERO(state->cec_adap);
|
||||
if (err)
|
||||
goto err_entity;
|
||||
#endif
|
||||
|
||||
v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name,
|
||||
client->addr << 1, client->adapter->name);
|
||||
|
||||
|
@ -3273,6 +3526,13 @@ static int adv76xx_remove(struct i2c_client *client)
|
|||
struct v4l2_subdev *sd = i2c_get_clientdata(client);
|
||||
struct adv76xx_state *state = to_state(sd);
|
||||
|
||||
/* disable interrupts */
|
||||
io_write(sd, 0x40, 0);
|
||||
io_write(sd, 0x41, 0);
|
||||
io_write(sd, 0x46, 0);
|
||||
io_write(sd, 0x6e, 0);
|
||||
io_write(sd, 0x73, 0);
|
||||
|
||||
cancel_delayed_work(&state->delayed_work_enable_hotplug);
|
||||
v4l2_async_unregister_subdev(sd);
|
||||
media_entity_cleanup(&sd->entity);
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include <linux/workqueue.h>
|
||||
#include <linux/v4l2-dv-timings.h>
|
||||
#include <linux/hdmi.h>
|
||||
#include <media/cec.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <media/v4l2-event.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
|
@ -79,6 +80,8 @@ MODULE_LICENSE("GPL");
|
|||
|
||||
#define ADV7842_OP_SWAP_CB_CR (1 << 0)
|
||||
|
||||
#define ADV7842_MAX_ADDRS (3)
|
||||
|
||||
/*
|
||||
**********************************************************************
|
||||
*
|
||||
|
@ -141,6 +144,11 @@ struct adv7842_state {
|
|||
struct v4l2_ctrl *free_run_color_ctrl_manual;
|
||||
struct v4l2_ctrl *free_run_color_ctrl;
|
||||
struct v4l2_ctrl *rgb_quantization_range_ctrl;
|
||||
|
||||
struct cec_adapter *cec_adap;
|
||||
u8 cec_addr[ADV7842_MAX_ADDRS];
|
||||
u8 cec_valid_addrs;
|
||||
bool cec_enabled_adap;
|
||||
};
|
||||
|
||||
/* Unsupported timings. This device cannot support 720p30. */
|
||||
|
@ -417,9 +425,9 @@ static inline int cec_write(struct v4l2_subdev *sd, u8 reg, u8 val)
|
|||
return adv_smbus_write_byte_data(state->i2c_cec, reg, val);
|
||||
}
|
||||
|
||||
static inline int cec_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
|
||||
static inline int cec_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
|
||||
{
|
||||
return cec_write(sd, reg, (cec_read(sd, reg) & mask) | val);
|
||||
return cec_write(sd, reg, (cec_read(sd, reg) & ~mask) | val);
|
||||
}
|
||||
|
||||
static inline int infoframe_read(struct v4l2_subdev *sd, u8 reg)
|
||||
|
@ -695,6 +703,18 @@ adv7842_get_dv_timings_cap(struct v4l2_subdev *sd)
|
|||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
static u16 adv7842_read_cable_det(struct v4l2_subdev *sd)
|
||||
{
|
||||
u8 reg = io_read(sd, 0x6f);
|
||||
u16 val = 0;
|
||||
|
||||
if (reg & 0x02)
|
||||
val |= 1; /* port A */
|
||||
if (reg & 0x01)
|
||||
val |= 2; /* port B */
|
||||
return val;
|
||||
}
|
||||
|
||||
static void adv7842_delayed_work_enable_hotplug(struct work_struct *work)
|
||||
{
|
||||
struct delayed_work *dwork = to_delayed_work(work);
|
||||
|
@ -760,50 +780,18 @@ static int edid_write_vga_segment(struct v4l2_subdev *sd)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int edid_spa_location(const u8 *edid)
|
||||
{
|
||||
u8 d;
|
||||
|
||||
/*
|
||||
* TODO, improve and update for other CEA extensions
|
||||
* currently only for 1 segment (256 bytes),
|
||||
* i.e. 1 extension block and CEA revision 3.
|
||||
*/
|
||||
if ((edid[0x7e] != 1) ||
|
||||
(edid[0x80] != 0x02) ||
|
||||
(edid[0x81] != 0x03)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
/*
|
||||
* search Vendor Specific Data Block (tag 3)
|
||||
*/
|
||||
d = edid[0x82] & 0x7f;
|
||||
if (d > 4) {
|
||||
int i = 0x84;
|
||||
int end = 0x80 + d;
|
||||
do {
|
||||
u8 tag = edid[i]>>5;
|
||||
u8 len = edid[i] & 0x1f;
|
||||
|
||||
if ((tag == 3) && (len >= 5))
|
||||
return i + 4;
|
||||
i += len + 1;
|
||||
} while (i < end);
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct adv7842_state *state = to_state(sd);
|
||||
const u8 *val = state->hdmi_edid.edid;
|
||||
int spa_loc = edid_spa_location(val);
|
||||
const u8 *edid = state->hdmi_edid.edid;
|
||||
int spa_loc;
|
||||
u16 pa;
|
||||
int err = 0;
|
||||
int i;
|
||||
|
||||
v4l2_dbg(2, debug, sd, "%s: write EDID on port %c (spa at 0x%x)\n",
|
||||
__func__, (port == ADV7842_EDID_PORT_A) ? 'A' : 'B', spa_loc);
|
||||
v4l2_dbg(2, debug, sd, "%s: write EDID on port %c\n",
|
||||
__func__, (port == ADV7842_EDID_PORT_A) ? 'A' : 'B');
|
||||
|
||||
/* HPA disable on port A and B */
|
||||
io_write_and_or(sd, 0x20, 0xcf, 0x00);
|
||||
|
@ -814,24 +802,33 @@ static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port)
|
|||
if (!state->hdmi_edid.present)
|
||||
return 0;
|
||||
|
||||
pa = cec_get_edid_phys_addr(edid, 256, &spa_loc);
|
||||
err = cec_phys_addr_validate(pa, &pa, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* Return an error if no location of the source physical address
|
||||
* was found.
|
||||
*/
|
||||
if (spa_loc == 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* edid segment pointer '0' for HDMI ports */
|
||||
rep_write_and_or(sd, 0x77, 0xef, 0x00);
|
||||
|
||||
for (i = 0; !err && i < 256; i += I2C_SMBUS_BLOCK_MAX)
|
||||
err = adv_smbus_write_i2c_block_data(state->i2c_edid, i,
|
||||
I2C_SMBUS_BLOCK_MAX, val + i);
|
||||
I2C_SMBUS_BLOCK_MAX, edid + i);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (spa_loc < 0)
|
||||
spa_loc = 0xc0; /* Default value [REF_02, p. 199] */
|
||||
|
||||
if (port == ADV7842_EDID_PORT_A) {
|
||||
rep_write(sd, 0x72, val[spa_loc]);
|
||||
rep_write(sd, 0x73, val[spa_loc + 1]);
|
||||
rep_write(sd, 0x72, edid[spa_loc]);
|
||||
rep_write(sd, 0x73, edid[spa_loc + 1]);
|
||||
} else {
|
||||
rep_write(sd, 0x74, val[spa_loc]);
|
||||
rep_write(sd, 0x75, val[spa_loc + 1]);
|
||||
rep_write(sd, 0x74, edid[spa_loc]);
|
||||
rep_write(sd, 0x75, edid[spa_loc + 1]);
|
||||
}
|
||||
rep_write(sd, 0x76, spa_loc & 0xff);
|
||||
rep_write_and_or(sd, 0x77, 0xbf, (spa_loc >> 2) & 0x40);
|
||||
|
@ -851,6 +848,7 @@ static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port)
|
|||
(port == ADV7842_EDID_PORT_A) ? 'A' : 'B');
|
||||
return -EIO;
|
||||
}
|
||||
cec_s_phys_addr(state->cec_adap, pa, false);
|
||||
|
||||
/* enable hotplug after 200 ms */
|
||||
schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 5);
|
||||
|
@ -980,20 +978,11 @@ static int adv7842_s_register(struct v4l2_subdev *sd,
|
|||
static int adv7842_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd)
|
||||
{
|
||||
struct adv7842_state *state = to_state(sd);
|
||||
int prev = v4l2_ctrl_g_ctrl(state->detect_tx_5v_ctrl);
|
||||
u8 reg_io_6f = io_read(sd, 0x6f);
|
||||
int val = 0;
|
||||
u16 cable_det = adv7842_read_cable_det(sd);
|
||||
|
||||
if (reg_io_6f & 0x02)
|
||||
val |= 1; /* port A */
|
||||
if (reg_io_6f & 0x01)
|
||||
val |= 2; /* port B */
|
||||
v4l2_dbg(1, debug, sd, "%s: 0x%x\n", __func__, cable_det);
|
||||
|
||||
v4l2_dbg(1, debug, sd, "%s: 0x%x -> 0x%x\n", __func__, prev, val);
|
||||
|
||||
if (val != prev)
|
||||
return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, val);
|
||||
return 0;
|
||||
return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, cable_det);
|
||||
}
|
||||
|
||||
static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd,
|
||||
|
@ -2167,6 +2156,207 @@ static void adv7842_irq_enable(struct v4l2_subdev *sd, bool enable)
|
|||
}
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_VIDEO_ADV7842_CEC)
|
||||
static void adv7842_cec_tx_raw_status(struct v4l2_subdev *sd, u8 tx_raw_status)
|
||||
{
|
||||
struct adv7842_state *state = to_state(sd);
|
||||
|
||||
if ((cec_read(sd, 0x11) & 0x01) == 0) {
|
||||
v4l2_dbg(1, debug, sd, "%s: tx raw: tx disabled\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tx_raw_status & 0x02) {
|
||||
v4l2_dbg(1, debug, sd, "%s: tx raw: arbitration lost\n",
|
||||
__func__);
|
||||
cec_transmit_done(state->cec_adap, CEC_TX_STATUS_ARB_LOST,
|
||||
1, 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
if (tx_raw_status & 0x04) {
|
||||
u8 status;
|
||||
u8 nack_cnt;
|
||||
u8 low_drive_cnt;
|
||||
|
||||
v4l2_dbg(1, debug, sd, "%s: tx raw: retry failed\n", __func__);
|
||||
/*
|
||||
* We set this status bit since this hardware performs
|
||||
* retransmissions.
|
||||
*/
|
||||
status = CEC_TX_STATUS_MAX_RETRIES;
|
||||
nack_cnt = cec_read(sd, 0x14) & 0xf;
|
||||
if (nack_cnt)
|
||||
status |= CEC_TX_STATUS_NACK;
|
||||
low_drive_cnt = cec_read(sd, 0x14) >> 4;
|
||||
if (low_drive_cnt)
|
||||
status |= CEC_TX_STATUS_LOW_DRIVE;
|
||||
cec_transmit_done(state->cec_adap, status,
|
||||
0, nack_cnt, low_drive_cnt, 0);
|
||||
return;
|
||||
}
|
||||
if (tx_raw_status & 0x01) {
|
||||
v4l2_dbg(1, debug, sd, "%s: tx raw: ready ok\n", __func__);
|
||||
cec_transmit_done(state->cec_adap, CEC_TX_STATUS_OK, 0, 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void adv7842_cec_isr(struct v4l2_subdev *sd, bool *handled)
|
||||
{
|
||||
u8 cec_irq;
|
||||
|
||||
/* cec controller */
|
||||
cec_irq = io_read(sd, 0x93) & 0x0f;
|
||||
if (!cec_irq)
|
||||
return;
|
||||
|
||||
v4l2_dbg(1, debug, sd, "%s: cec: irq 0x%x\n", __func__, cec_irq);
|
||||
adv7842_cec_tx_raw_status(sd, cec_irq);
|
||||
if (cec_irq & 0x08) {
|
||||
struct adv7842_state *state = to_state(sd);
|
||||
struct cec_msg msg;
|
||||
|
||||
msg.len = cec_read(sd, 0x25) & 0x1f;
|
||||
if (msg.len > 16)
|
||||
msg.len = 16;
|
||||
|
||||
if (msg.len) {
|
||||
u8 i;
|
||||
|
||||
for (i = 0; i < msg.len; i++)
|
||||
msg.msg[i] = cec_read(sd, i + 0x15);
|
||||
cec_write(sd, 0x26, 0x01); /* re-enable rx */
|
||||
cec_received_msg(state->cec_adap, &msg);
|
||||
}
|
||||
}
|
||||
|
||||
io_write(sd, 0x94, cec_irq);
|
||||
|
||||
if (handled)
|
||||
*handled = true;
|
||||
}
|
||||
|
||||
static int adv7842_cec_adap_enable(struct cec_adapter *adap, bool enable)
|
||||
{
|
||||
struct adv7842_state *state = adap->priv;
|
||||
struct v4l2_subdev *sd = &state->sd;
|
||||
|
||||
if (!state->cec_enabled_adap && enable) {
|
||||
cec_write_clr_set(sd, 0x2a, 0x01, 0x01); /* power up cec */
|
||||
cec_write(sd, 0x2c, 0x01); /* cec soft reset */
|
||||
cec_write_clr_set(sd, 0x11, 0x01, 0); /* initially disable tx */
|
||||
/* enabled irqs: */
|
||||
/* tx: ready */
|
||||
/* tx: arbitration lost */
|
||||
/* tx: retry timeout */
|
||||
/* rx: ready */
|
||||
io_write_clr_set(sd, 0x96, 0x0f, 0x0f);
|
||||
cec_write(sd, 0x26, 0x01); /* enable rx */
|
||||
} else if (state->cec_enabled_adap && !enable) {
|
||||
/* disable cec interrupts */
|
||||
io_write_clr_set(sd, 0x96, 0x0f, 0x00);
|
||||
/* disable address mask 1-3 */
|
||||
cec_write_clr_set(sd, 0x27, 0x70, 0x00);
|
||||
/* power down cec section */
|
||||
cec_write_clr_set(sd, 0x2a, 0x01, 0x00);
|
||||
state->cec_valid_addrs = 0;
|
||||
}
|
||||
state->cec_enabled_adap = enable;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adv7842_cec_adap_log_addr(struct cec_adapter *adap, u8 addr)
|
||||
{
|
||||
struct adv7842_state *state = adap->priv;
|
||||
struct v4l2_subdev *sd = &state->sd;
|
||||
unsigned int i, free_idx = ADV7842_MAX_ADDRS;
|
||||
|
||||
if (!state->cec_enabled_adap)
|
||||
return addr == CEC_LOG_ADDR_INVALID ? 0 : -EIO;
|
||||
|
||||
if (addr == CEC_LOG_ADDR_INVALID) {
|
||||
cec_write_clr_set(sd, 0x27, 0x70, 0);
|
||||
state->cec_valid_addrs = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < ADV7842_MAX_ADDRS; i++) {
|
||||
bool is_valid = state->cec_valid_addrs & (1 << i);
|
||||
|
||||
if (free_idx == ADV7842_MAX_ADDRS && !is_valid)
|
||||
free_idx = i;
|
||||
if (is_valid && state->cec_addr[i] == addr)
|
||||
return 0;
|
||||
}
|
||||
if (i == ADV7842_MAX_ADDRS) {
|
||||
i = free_idx;
|
||||
if (i == ADV7842_MAX_ADDRS)
|
||||
return -ENXIO;
|
||||
}
|
||||
state->cec_addr[i] = addr;
|
||||
state->cec_valid_addrs |= 1 << i;
|
||||
|
||||
switch (i) {
|
||||
case 0:
|
||||
/* enable address mask 0 */
|
||||
cec_write_clr_set(sd, 0x27, 0x10, 0x10);
|
||||
/* set address for mask 0 */
|
||||
cec_write_clr_set(sd, 0x28, 0x0f, addr);
|
||||
break;
|
||||
case 1:
|
||||
/* enable address mask 1 */
|
||||
cec_write_clr_set(sd, 0x27, 0x20, 0x20);
|
||||
/* set address for mask 1 */
|
||||
cec_write_clr_set(sd, 0x28, 0xf0, addr << 4);
|
||||
break;
|
||||
case 2:
|
||||
/* enable address mask 2 */
|
||||
cec_write_clr_set(sd, 0x27, 0x40, 0x40);
|
||||
/* set address for mask 1 */
|
||||
cec_write_clr_set(sd, 0x29, 0x0f, addr);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adv7842_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
|
||||
u32 signal_free_time, struct cec_msg *msg)
|
||||
{
|
||||
struct adv7842_state *state = adap->priv;
|
||||
struct v4l2_subdev *sd = &state->sd;
|
||||
u8 len = msg->len;
|
||||
unsigned int i;
|
||||
|
||||
/*
|
||||
* The number of retries is the number of attempts - 1, but retry
|
||||
* at least once. It's not clear if a value of 0 is allowed, so
|
||||
* let's do at least one retry.
|
||||
*/
|
||||
cec_write_clr_set(sd, 0x12, 0x70, max(1, attempts - 1) << 4);
|
||||
|
||||
if (len > 16) {
|
||||
v4l2_err(sd, "%s: len exceeded 16 (%d)\n", __func__, len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* write data */
|
||||
for (i = 0; i < len; i++)
|
||||
cec_write(sd, i, msg->msg[i]);
|
||||
|
||||
/* set length (data + header) */
|
||||
cec_write(sd, 0x10, len);
|
||||
/* start transmit, enable tx */
|
||||
cec_write(sd, 0x11, 0x01);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct cec_adap_ops adv7842_cec_adap_ops = {
|
||||
.adap_enable = adv7842_cec_adap_enable,
|
||||
.adap_log_addr = adv7842_cec_adap_log_addr,
|
||||
.adap_transmit = adv7842_cec_adap_transmit,
|
||||
};
|
||||
#endif
|
||||
|
||||
static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
|
||||
{
|
||||
struct adv7842_state *state = to_state(sd);
|
||||
|
@ -2238,6 +2428,11 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
|
|||
*handled = true;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_VIDEO_ADV7842_CEC)
|
||||
/* cec */
|
||||
adv7842_cec_isr(sd, handled);
|
||||
#endif
|
||||
|
||||
/* tx 5v detect */
|
||||
if (irq_status[2] & 0x3) {
|
||||
v4l2_dbg(1, debug, sd, "%s: irq tx_5v\n", __func__);
|
||||
|
@ -2318,10 +2513,12 @@ static int adv7842_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *e)
|
|||
case ADV7842_EDID_PORT_A:
|
||||
case ADV7842_EDID_PORT_B:
|
||||
memset(&state->hdmi_edid.edid, 0, 256);
|
||||
if (e->blocks)
|
||||
if (e->blocks) {
|
||||
state->hdmi_edid.present |= 0x04 << e->pad;
|
||||
else
|
||||
} else {
|
||||
state->hdmi_edid.present &= ~(0x04 << e->pad);
|
||||
adv7842_s_detect_tx_5v_ctrl(sd);
|
||||
}
|
||||
memcpy(&state->hdmi_edid.edid, e->edid, 128 * e->blocks);
|
||||
err = edid_write_hdmi_segment(sd, e->pad);
|
||||
break;
|
||||
|
@ -2509,8 +2706,19 @@ static int adv7842_cp_log_status(struct v4l2_subdev *sd)
|
|||
v4l2_info(sd, "HPD A %s, B %s\n",
|
||||
reg_io_0x21 & 0x02 ? "enabled" : "disabled",
|
||||
reg_io_0x21 & 0x01 ? "enabled" : "disabled");
|
||||
v4l2_info(sd, "CEC %s\n", !!(cec_read(sd, 0x2a) & 0x01) ?
|
||||
v4l2_info(sd, "CEC: %s\n", state->cec_enabled_adap ?
|
||||
"enabled" : "disabled");
|
||||
if (state->cec_enabled_adap) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ADV7842_MAX_ADDRS; i++) {
|
||||
bool is_valid = state->cec_valid_addrs & (1 << i);
|
||||
|
||||
if (is_valid)
|
||||
v4l2_info(sd, "CEC Logical Address: 0x%x\n",
|
||||
state->cec_addr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
v4l2_info(sd, "-----Signal status-----\n");
|
||||
if (state->hdmi_port_a) {
|
||||
|
@ -3031,6 +3239,24 @@ static int adv7842_subscribe_event(struct v4l2_subdev *sd,
|
|||
}
|
||||
}
|
||||
|
||||
static int adv7842_registered(struct v4l2_subdev *sd)
|
||||
{
|
||||
struct adv7842_state *state = to_state(sd);
|
||||
int err;
|
||||
|
||||
err = cec_register_adapter(state->cec_adap);
|
||||
if (err)
|
||||
cec_delete_adapter(state->cec_adap);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void adv7842_unregistered(struct v4l2_subdev *sd)
|
||||
{
|
||||
struct adv7842_state *state = to_state(sd);
|
||||
|
||||
cec_unregister_adapter(state->cec_adap);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
static const struct v4l2_ctrl_ops adv7842_ctrl_ops = {
|
||||
|
@ -3077,6 +3303,11 @@ static const struct v4l2_subdev_ops adv7842_ops = {
|
|||
.pad = &adv7842_pad_ops,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_internal_ops adv7842_int_ops = {
|
||||
.registered = adv7842_registered,
|
||||
.unregistered = adv7842_unregistered,
|
||||
};
|
||||
|
||||
/* -------------------------- custom ctrls ---------------------------------- */
|
||||
|
||||
static const struct v4l2_ctrl_config adv7842_ctrl_analog_sampling_phase = {
|
||||
|
@ -3241,6 +3472,7 @@ static int adv7842_probe(struct i2c_client *client,
|
|||
sd = &state->sd;
|
||||
v4l2_i2c_subdev_init(sd, client, &adv7842_ops);
|
||||
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
|
||||
sd->internal_ops = &adv7842_int_ops;
|
||||
state->mode = pdata->mode;
|
||||
|
||||
state->hdmi_port_a = pdata->input == ADV7842_SELECT_HDMI_PORT_A;
|
||||
|
@ -3324,6 +3556,17 @@ static int adv7842_probe(struct i2c_client *client,
|
|||
if (err)
|
||||
goto err_entity;
|
||||
|
||||
#if IS_ENABLED(CONFIG_VIDEO_ADV7842_CEC)
|
||||
state->cec_adap = cec_allocate_adapter(&adv7842_cec_adap_ops,
|
||||
state, dev_name(&client->dev),
|
||||
CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS |
|
||||
CEC_CAP_PASSTHROUGH | CEC_CAP_RC, ADV7842_MAX_ADDRS,
|
||||
&client->dev);
|
||||
err = PTR_ERR_OR_ZERO(state->cec_adap);
|
||||
if (err)
|
||||
goto err_entity;
|
||||
#endif
|
||||
|
||||
v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name,
|
||||
client->addr << 1, client->adapter->name);
|
||||
return 0;
|
||||
|
@ -3347,7 +3590,6 @@ static int adv7842_remove(struct i2c_client *client)
|
|||
struct adv7842_state *state = to_state(sd);
|
||||
|
||||
adv7842_irq_enable(sd, false);
|
||||
|
||||
cancel_delayed_work(&state->delayed_work_enable_hotplug);
|
||||
v4l2_device_unregister_subdev(sd);
|
||||
media_entity_cleanup(&sd->entity);
|
||||
|
|
|
@ -6,6 +6,7 @@ config VIDEO_VIVID
|
|||
select FB_CFB_FILLRECT
|
||||
select FB_CFB_COPYAREA
|
||||
select FB_CFB_IMAGEBLIT
|
||||
select MEDIA_CEC_EDID
|
||||
select VIDEOBUF2_VMALLOC
|
||||
select VIDEO_V4L2_TPG
|
||||
default n
|
||||
|
@ -22,6 +23,13 @@ config VIDEO_VIVID
|
|||
Say Y here if you want to test video apps or debug V4L devices.
|
||||
When in doubt, say N.
|
||||
|
||||
config VIDEO_VIVID_CEC
|
||||
bool "Enable CEC emulation support"
|
||||
depends on VIDEO_VIVID && MEDIA_CEC
|
||||
---help---
|
||||
When selected the vivid module will emulate the optional
|
||||
HDMI CEC feature.
|
||||
|
||||
config VIDEO_VIVID_MAX_DEVS
|
||||
int "Maximum number of devices"
|
||||
depends on VIDEO_VIVID
|
||||
|
|
|
@ -3,4 +3,8 @@ vivid-objs := vivid-core.o vivid-ctrls.o vivid-vid-common.o vivid-vbi-gen.o \
|
|||
vivid-radio-rx.o vivid-radio-tx.o vivid-radio-common.o \
|
||||
vivid-rds-gen.o vivid-sdr-cap.o vivid-vbi-cap.o vivid-vbi-out.o \
|
||||
vivid-osd.o
|
||||
ifeq ($(CONFIG_VIDEO_VIVID_CEC),y)
|
||||
vivid-objs += vivid-cec.o
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_VIDEO_VIVID) += vivid.o
|
||||
|
|
|
@ -0,0 +1,255 @@
|
|||
/*
|
||||
* vivid-cec.c - A Virtual Video Test Driver, cec emulation
|
||||
*
|
||||
* Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
||||
*
|
||||
* This program is free software; you may redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <media/cec.h>
|
||||
|
||||
#include "vivid-core.h"
|
||||
#include "vivid-cec.h"
|
||||
|
||||
void vivid_cec_bus_free_work(struct vivid_dev *dev)
|
||||
{
|
||||
spin_lock(&dev->cec_slock);
|
||||
while (!list_empty(&dev->cec_work_list)) {
|
||||
struct vivid_cec_work *cw =
|
||||
list_first_entry(&dev->cec_work_list,
|
||||
struct vivid_cec_work, list);
|
||||
|
||||
spin_unlock(&dev->cec_slock);
|
||||
cancel_delayed_work_sync(&cw->work);
|
||||
spin_lock(&dev->cec_slock);
|
||||
list_del(&cw->list);
|
||||
cec_transmit_done(cw->adap, CEC_TX_STATUS_LOW_DRIVE, 0, 0, 1, 0);
|
||||
kfree(cw);
|
||||
}
|
||||
spin_unlock(&dev->cec_slock);
|
||||
}
|
||||
|
||||
static struct cec_adapter *vivid_cec_find_dest_adap(struct vivid_dev *dev,
|
||||
struct cec_adapter *adap,
|
||||
u8 dest)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (dest >= 0xf)
|
||||
return NULL;
|
||||
|
||||
if (adap != dev->cec_rx_adap && dev->cec_rx_adap &&
|
||||
dev->cec_rx_adap->is_configured &&
|
||||
cec_has_log_addr(dev->cec_rx_adap, dest))
|
||||
return dev->cec_rx_adap;
|
||||
|
||||
for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++) {
|
||||
if (adap == dev->cec_tx_adap[i])
|
||||
continue;
|
||||
if (!dev->cec_tx_adap[i]->is_configured)
|
||||
continue;
|
||||
if (cec_has_log_addr(dev->cec_tx_adap[i], dest))
|
||||
return dev->cec_tx_adap[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void vivid_cec_xfer_done_worker(struct work_struct *work)
|
||||
{
|
||||
struct vivid_cec_work *cw =
|
||||
container_of(work, struct vivid_cec_work, work.work);
|
||||
struct vivid_dev *dev = cw->dev;
|
||||
struct cec_adapter *adap = cw->adap;
|
||||
bool is_poll = cw->msg.len == 1;
|
||||
u8 dest = cec_msg_destination(&cw->msg);
|
||||
struct cec_adapter *dest_adap = NULL;
|
||||
bool valid_dest;
|
||||
unsigned int i;
|
||||
|
||||
valid_dest = cec_msg_is_broadcast(&cw->msg);
|
||||
if (!valid_dest) {
|
||||
dest_adap = vivid_cec_find_dest_adap(dev, adap, dest);
|
||||
if (dest_adap)
|
||||
valid_dest = true;
|
||||
}
|
||||
cw->tx_status = valid_dest ? CEC_TX_STATUS_OK : CEC_TX_STATUS_NACK;
|
||||
spin_lock(&dev->cec_slock);
|
||||
dev->cec_xfer_time_jiffies = 0;
|
||||
dev->cec_xfer_start_jiffies = 0;
|
||||
list_del(&cw->list);
|
||||
spin_unlock(&dev->cec_slock);
|
||||
cec_transmit_done(cw->adap, cw->tx_status, 0, valid_dest ? 0 : 1, 0, 0);
|
||||
|
||||
if (!is_poll && dest_adap) {
|
||||
/* Directed message */
|
||||
cec_received_msg(dest_adap, &cw->msg);
|
||||
} else if (!is_poll && valid_dest) {
|
||||
/* Broadcast message */
|
||||
if (adap != dev->cec_rx_adap &&
|
||||
dev->cec_rx_adap->log_addrs.log_addr_mask)
|
||||
cec_received_msg(dev->cec_rx_adap, &cw->msg);
|
||||
for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++) {
|
||||
if (adap == dev->cec_tx_adap[i] ||
|
||||
!dev->cec_tx_adap[i]->log_addrs.log_addr_mask)
|
||||
continue;
|
||||
cec_received_msg(dev->cec_tx_adap[i], &cw->msg);
|
||||
}
|
||||
}
|
||||
kfree(cw);
|
||||
}
|
||||
|
||||
static void vivid_cec_xfer_try_worker(struct work_struct *work)
|
||||
{
|
||||
struct vivid_cec_work *cw =
|
||||
container_of(work, struct vivid_cec_work, work.work);
|
||||
struct vivid_dev *dev = cw->dev;
|
||||
|
||||
spin_lock(&dev->cec_slock);
|
||||
if (dev->cec_xfer_time_jiffies) {
|
||||
list_del(&cw->list);
|
||||
spin_unlock(&dev->cec_slock);
|
||||
cec_transmit_done(cw->adap, CEC_TX_STATUS_ARB_LOST, 1, 0, 0, 0);
|
||||
kfree(cw);
|
||||
} else {
|
||||
INIT_DELAYED_WORK(&cw->work, vivid_cec_xfer_done_worker);
|
||||
dev->cec_xfer_start_jiffies = jiffies;
|
||||
dev->cec_xfer_time_jiffies = usecs_to_jiffies(cw->usecs);
|
||||
spin_unlock(&dev->cec_slock);
|
||||
schedule_delayed_work(&cw->work, dev->cec_xfer_time_jiffies);
|
||||
}
|
||||
}
|
||||
|
||||
static int vivid_cec_adap_enable(struct cec_adapter *adap, bool enable)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vivid_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* One data bit takes 2400 us, each byte needs 10 bits so that's 24000 us
|
||||
* per byte.
|
||||
*/
|
||||
#define USECS_PER_BYTE 24000
|
||||
|
||||
static int vivid_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
|
||||
u32 signal_free_time, struct cec_msg *msg)
|
||||
{
|
||||
struct vivid_dev *dev = adap->priv;
|
||||
struct vivid_cec_work *cw = kzalloc(sizeof(*cw), GFP_KERNEL);
|
||||
long delta_jiffies = 0;
|
||||
|
||||
if (cw == NULL)
|
||||
return -ENOMEM;
|
||||
cw->dev = dev;
|
||||
cw->adap = adap;
|
||||
cw->usecs = CEC_FREE_TIME_TO_USEC(signal_free_time) +
|
||||
msg->len * USECS_PER_BYTE;
|
||||
cw->msg = *msg;
|
||||
|
||||
spin_lock(&dev->cec_slock);
|
||||
list_add(&cw->list, &dev->cec_work_list);
|
||||
if (dev->cec_xfer_time_jiffies == 0) {
|
||||
INIT_DELAYED_WORK(&cw->work, vivid_cec_xfer_done_worker);
|
||||
dev->cec_xfer_start_jiffies = jiffies;
|
||||
dev->cec_xfer_time_jiffies = usecs_to_jiffies(cw->usecs);
|
||||
delta_jiffies = dev->cec_xfer_time_jiffies;
|
||||
} else {
|
||||
INIT_DELAYED_WORK(&cw->work, vivid_cec_xfer_try_worker);
|
||||
delta_jiffies = dev->cec_xfer_start_jiffies +
|
||||
dev->cec_xfer_time_jiffies - jiffies;
|
||||
}
|
||||
spin_unlock(&dev->cec_slock);
|
||||
schedule_delayed_work(&cw->work, delta_jiffies < 0 ? 0 : delta_jiffies);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vivid_received(struct cec_adapter *adap, struct cec_msg *msg)
|
||||
{
|
||||
struct vivid_dev *dev = adap->priv;
|
||||
struct cec_msg reply;
|
||||
u8 dest = cec_msg_destination(msg);
|
||||
u16 pa;
|
||||
u8 disp_ctl;
|
||||
char osd[14];
|
||||
|
||||
if (cec_msg_is_broadcast(msg))
|
||||
dest = adap->log_addrs.log_addr[0];
|
||||
cec_msg_init(&reply, dest, cec_msg_initiator(msg));
|
||||
|
||||
switch (cec_msg_opcode(msg)) {
|
||||
case CEC_MSG_SET_STREAM_PATH:
|
||||
if (cec_is_sink(adap))
|
||||
return -ENOMSG;
|
||||
cec_ops_set_stream_path(msg, &pa);
|
||||
if (pa != adap->phys_addr)
|
||||
return -ENOMSG;
|
||||
cec_msg_active_source(&reply, adap->phys_addr);
|
||||
cec_transmit_msg(adap, &reply, false);
|
||||
break;
|
||||
case CEC_MSG_SET_OSD_STRING:
|
||||
if (!cec_is_sink(adap))
|
||||
return -ENOMSG;
|
||||
cec_ops_set_osd_string(msg, &disp_ctl, osd);
|
||||
switch (disp_ctl) {
|
||||
case CEC_OP_DISP_CTL_DEFAULT:
|
||||
strcpy(dev->osd, osd);
|
||||
dev->osd_jiffies = jiffies;
|
||||
break;
|
||||
case CEC_OP_DISP_CTL_UNTIL_CLEARED:
|
||||
strcpy(dev->osd, osd);
|
||||
dev->osd_jiffies = 0;
|
||||
break;
|
||||
case CEC_OP_DISP_CTL_CLEAR:
|
||||
dev->osd[0] = 0;
|
||||
dev->osd_jiffies = 0;
|
||||
break;
|
||||
default:
|
||||
cec_msg_feature_abort(&reply, cec_msg_opcode(msg),
|
||||
CEC_OP_ABORT_INVALID_OP);
|
||||
cec_transmit_msg(adap, &reply, false);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -ENOMSG;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct cec_adap_ops vivid_cec_adap_ops = {
|
||||
.adap_enable = vivid_cec_adap_enable,
|
||||
.adap_log_addr = vivid_cec_adap_log_addr,
|
||||
.adap_transmit = vivid_cec_adap_transmit,
|
||||
.received = vivid_received,
|
||||
};
|
||||
|
||||
struct cec_adapter *vivid_cec_alloc_adap(struct vivid_dev *dev,
|
||||
unsigned int idx,
|
||||
struct device *parent,
|
||||
bool is_source)
|
||||
{
|
||||
char name[sizeof(dev->vid_out_dev.name) + 2];
|
||||
u32 caps = CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS |
|
||||
CEC_CAP_PASSTHROUGH | CEC_CAP_RC;
|
||||
|
||||
snprintf(name, sizeof(name), "%s%d",
|
||||
is_source ? dev->vid_out_dev.name : dev->vid_cap_dev.name,
|
||||
idx);
|
||||
return cec_allocate_adapter(&vivid_cec_adap_ops, dev,
|
||||
name, caps, 1, parent);
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* vivid-cec.h - A Virtual Video Test Driver, cec emulation
|
||||
*
|
||||
* Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
||||
*
|
||||
* This program is free software; you may redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_VIDEO_VIVID_CEC
|
||||
struct cec_adapter *vivid_cec_alloc_adap(struct vivid_dev *dev,
|
||||
unsigned int idx,
|
||||
struct device *parent,
|
||||
bool is_source);
|
||||
void vivid_cec_bus_free_work(struct vivid_dev *dev);
|
||||
|
||||
#else
|
||||
|
||||
static inline void vivid_cec_bus_free_work(struct vivid_dev *dev)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
|
@ -46,6 +46,7 @@
|
|||
#include "vivid-vbi-cap.h"
|
||||
#include "vivid-vbi-out.h"
|
||||
#include "vivid-osd.h"
|
||||
#include "vivid-cec.h"
|
||||
#include "vivid-ctrls.h"
|
||||
|
||||
#define VIVID_MODULE_NAME "vivid"
|
||||
|
@ -684,6 +685,11 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
|
|||
dev->input_name_counter[i] = in_type_counter[dev->input_type[i]]++;
|
||||
}
|
||||
dev->has_audio_inputs = in_type_counter[TV] && in_type_counter[SVID];
|
||||
if (in_type_counter[HDMI] == 16) {
|
||||
/* The CEC physical address only allows for max 15 inputs */
|
||||
in_type_counter[HDMI]--;
|
||||
dev->num_inputs--;
|
||||
}
|
||||
|
||||
/* how many outputs do we have and of what type? */
|
||||
dev->num_outputs = num_outputs[inst];
|
||||
|
@ -696,6 +702,15 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
|
|||
dev->output_name_counter[i] = out_type_counter[dev->output_type[i]]++;
|
||||
}
|
||||
dev->has_audio_outputs = out_type_counter[SVID];
|
||||
if (out_type_counter[HDMI] == 16) {
|
||||
/*
|
||||
* The CEC physical address only allows for max 15 inputs,
|
||||
* so outputs are also limited to 15 to allow for easy
|
||||
* CEC output to input mapping.
|
||||
*/
|
||||
out_type_counter[HDMI]--;
|
||||
dev->num_outputs--;
|
||||
}
|
||||
|
||||
/* do we create a video capture device? */
|
||||
dev->has_vid_cap = node_type & 0x0001;
|
||||
|
@ -1010,6 +1025,17 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
|
|||
INIT_LIST_HEAD(&dev->vbi_out_active);
|
||||
INIT_LIST_HEAD(&dev->sdr_cap_active);
|
||||
|
||||
INIT_LIST_HEAD(&dev->cec_work_list);
|
||||
spin_lock_init(&dev->cec_slock);
|
||||
/*
|
||||
* Same as create_singlethread_workqueue, but now I can use the
|
||||
* string formatting of alloc_ordered_workqueue.
|
||||
*/
|
||||
dev->cec_workqueue =
|
||||
alloc_ordered_workqueue("vivid-%03d-cec", WQ_MEM_RECLAIM, inst);
|
||||
if (!dev->cec_workqueue)
|
||||
goto unreg_dev;
|
||||
|
||||
/* start creating the vb2 queues */
|
||||
if (dev->has_vid_cap) {
|
||||
/* initialize vid_cap queue */
|
||||
|
@ -1117,7 +1143,8 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
|
|||
/* finally start creating the device nodes */
|
||||
if (dev->has_vid_cap) {
|
||||
vfd = &dev->vid_cap_dev;
|
||||
strlcpy(vfd->name, "vivid-vid-cap", sizeof(vfd->name));
|
||||
snprintf(vfd->name, sizeof(vfd->name),
|
||||
"vivid-%03d-vid-cap", inst);
|
||||
vfd->fops = &vivid_fops;
|
||||
vfd->ioctl_ops = &vivid_ioctl_ops;
|
||||
vfd->device_caps = dev->vid_cap_caps;
|
||||
|
@ -1133,6 +1160,27 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
|
|||
vfd->lock = &dev->mutex;
|
||||
video_set_drvdata(vfd, dev);
|
||||
|
||||
#ifdef CONFIG_VIDEO_VIVID_CEC
|
||||
if (in_type_counter[HDMI]) {
|
||||
struct cec_adapter *adap;
|
||||
|
||||
adap = vivid_cec_alloc_adap(dev, 0, &pdev->dev, false);
|
||||
ret = PTR_ERR_OR_ZERO(adap);
|
||||
if (ret < 0)
|
||||
goto unreg_dev;
|
||||
dev->cec_rx_adap = adap;
|
||||
ret = cec_register_adapter(adap);
|
||||
if (ret < 0) {
|
||||
cec_delete_adapter(adap);
|
||||
dev->cec_rx_adap = NULL;
|
||||
goto unreg_dev;
|
||||
}
|
||||
cec_s_phys_addr(adap, 0, false);
|
||||
v4l2_info(&dev->v4l2_dev, "CEC adapter %s registered for HDMI input %d\n",
|
||||
dev_name(&adap->devnode.dev), i);
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = video_register_device(vfd, VFL_TYPE_GRABBER, vid_cap_nr[inst]);
|
||||
if (ret < 0)
|
||||
goto unreg_dev;
|
||||
|
@ -1141,8 +1189,13 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
|
|||
}
|
||||
|
||||
if (dev->has_vid_out) {
|
||||
#ifdef CONFIG_VIDEO_VIVID_CEC
|
||||
unsigned int bus_cnt = 0;
|
||||
#endif
|
||||
|
||||
vfd = &dev->vid_out_dev;
|
||||
strlcpy(vfd->name, "vivid-vid-out", sizeof(vfd->name));
|
||||
snprintf(vfd->name, sizeof(vfd->name),
|
||||
"vivid-%03d-vid-out", inst);
|
||||
vfd->vfl_dir = VFL_DIR_TX;
|
||||
vfd->fops = &vivid_fops;
|
||||
vfd->ioctl_ops = &vivid_ioctl_ops;
|
||||
|
@ -1159,6 +1212,35 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
|
|||
vfd->lock = &dev->mutex;
|
||||
video_set_drvdata(vfd, dev);
|
||||
|
||||
#ifdef CONFIG_VIDEO_VIVID_CEC
|
||||
for (i = 0; i < dev->num_outputs; i++) {
|
||||
struct cec_adapter *adap;
|
||||
|
||||
if (dev->output_type[i] != HDMI)
|
||||
continue;
|
||||
dev->cec_output2bus_map[i] = bus_cnt;
|
||||
adap = vivid_cec_alloc_adap(dev, bus_cnt,
|
||||
&pdev->dev, true);
|
||||
ret = PTR_ERR_OR_ZERO(adap);
|
||||
if (ret < 0)
|
||||
goto unreg_dev;
|
||||
dev->cec_tx_adap[bus_cnt] = adap;
|
||||
ret = cec_register_adapter(adap);
|
||||
if (ret < 0) {
|
||||
cec_delete_adapter(adap);
|
||||
dev->cec_tx_adap[bus_cnt] = NULL;
|
||||
goto unreg_dev;
|
||||
}
|
||||
bus_cnt++;
|
||||
if (bus_cnt <= in_type_counter[HDMI])
|
||||
cec_s_phys_addr(adap, bus_cnt << 12, false);
|
||||
else
|
||||
cec_s_phys_addr(adap, 0x1000, false);
|
||||
v4l2_info(&dev->v4l2_dev, "CEC adapter %s registered for HDMI output %d\n",
|
||||
dev_name(&adap->devnode.dev), i);
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = video_register_device(vfd, VFL_TYPE_GRABBER, vid_out_nr[inst]);
|
||||
if (ret < 0)
|
||||
goto unreg_dev;
|
||||
|
@ -1168,7 +1250,8 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
|
|||
|
||||
if (dev->has_vbi_cap) {
|
||||
vfd = &dev->vbi_cap_dev;
|
||||
strlcpy(vfd->name, "vivid-vbi-cap", sizeof(vfd->name));
|
||||
snprintf(vfd->name, sizeof(vfd->name),
|
||||
"vivid-%03d-vbi-cap", inst);
|
||||
vfd->fops = &vivid_fops;
|
||||
vfd->ioctl_ops = &vivid_ioctl_ops;
|
||||
vfd->device_caps = dev->vbi_cap_caps;
|
||||
|
@ -1191,7 +1274,8 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
|
|||
|
||||
if (dev->has_vbi_out) {
|
||||
vfd = &dev->vbi_out_dev;
|
||||
strlcpy(vfd->name, "vivid-vbi-out", sizeof(vfd->name));
|
||||
snprintf(vfd->name, sizeof(vfd->name),
|
||||
"vivid-%03d-vbi-out", inst);
|
||||
vfd->vfl_dir = VFL_DIR_TX;
|
||||
vfd->fops = &vivid_fops;
|
||||
vfd->ioctl_ops = &vivid_ioctl_ops;
|
||||
|
@ -1215,7 +1299,8 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
|
|||
|
||||
if (dev->has_sdr_cap) {
|
||||
vfd = &dev->sdr_cap_dev;
|
||||
strlcpy(vfd->name, "vivid-sdr-cap", sizeof(vfd->name));
|
||||
snprintf(vfd->name, sizeof(vfd->name),
|
||||
"vivid-%03d-sdr-cap", inst);
|
||||
vfd->fops = &vivid_fops;
|
||||
vfd->ioctl_ops = &vivid_ioctl_ops;
|
||||
vfd->device_caps = dev->sdr_cap_caps;
|
||||
|
@ -1234,7 +1319,8 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
|
|||
|
||||
if (dev->has_radio_rx) {
|
||||
vfd = &dev->radio_rx_dev;
|
||||
strlcpy(vfd->name, "vivid-rad-rx", sizeof(vfd->name));
|
||||
snprintf(vfd->name, sizeof(vfd->name),
|
||||
"vivid-%03d-rad-rx", inst);
|
||||
vfd->fops = &vivid_radio_fops;
|
||||
vfd->ioctl_ops = &vivid_ioctl_ops;
|
||||
vfd->device_caps = dev->radio_rx_caps;
|
||||
|
@ -1252,7 +1338,8 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
|
|||
|
||||
if (dev->has_radio_tx) {
|
||||
vfd = &dev->radio_tx_dev;
|
||||
strlcpy(vfd->name, "vivid-rad-tx", sizeof(vfd->name));
|
||||
snprintf(vfd->name, sizeof(vfd->name),
|
||||
"vivid-%03d-rad-tx", inst);
|
||||
vfd->vfl_dir = VFL_DIR_TX;
|
||||
vfd->fops = &vivid_radio_fops;
|
||||
vfd->ioctl_ops = &vivid_ioctl_ops;
|
||||
|
@ -1282,6 +1369,13 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
|
|||
video_unregister_device(&dev->vbi_cap_dev);
|
||||
video_unregister_device(&dev->vid_out_dev);
|
||||
video_unregister_device(&dev->vid_cap_dev);
|
||||
cec_unregister_adapter(dev->cec_rx_adap);
|
||||
for (i = 0; i < MAX_OUTPUTS; i++)
|
||||
cec_unregister_adapter(dev->cec_tx_adap[i]);
|
||||
if (dev->cec_workqueue) {
|
||||
vivid_cec_bus_free_work(dev);
|
||||
destroy_workqueue(dev->cec_workqueue);
|
||||
}
|
||||
free_dev:
|
||||
v4l2_device_put(&dev->v4l2_dev);
|
||||
return ret;
|
||||
|
@ -1331,8 +1425,7 @@ static int vivid_probe(struct platform_device *pdev)
|
|||
static int vivid_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct vivid_dev *dev;
|
||||
unsigned i;
|
||||
|
||||
unsigned int i, j;
|
||||
|
||||
for (i = 0; i < n_devs; i++) {
|
||||
dev = vivid_devs[i];
|
||||
|
@ -1380,6 +1473,13 @@ static int vivid_remove(struct platform_device *pdev)
|
|||
unregister_framebuffer(&dev->fb_info);
|
||||
vivid_fb_release_buffers(dev);
|
||||
}
|
||||
cec_unregister_adapter(dev->cec_rx_adap);
|
||||
for (j = 0; j < MAX_OUTPUTS; j++)
|
||||
cec_unregister_adapter(dev->cec_tx_adap[j]);
|
||||
if (dev->cec_workqueue) {
|
||||
vivid_cec_bus_free_work(dev);
|
||||
destroy_workqueue(dev->cec_workqueue);
|
||||
}
|
||||
v4l2_device_put(&dev->v4l2_dev);
|
||||
vivid_devs[i] = NULL;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#define _VIVID_CORE_H_
|
||||
|
||||
#include <linux/fb.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <media/cec.h>
|
||||
#include <media/videobuf2-v4l2.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <media/v4l2-dev.h>
|
||||
|
@ -132,6 +134,17 @@ enum vivid_colorspace {
|
|||
#define VIVID_INVALID_SIGNAL(mode) \
|
||||
((mode) == NO_SIGNAL || (mode) == NO_LOCK || (mode) == OUT_OF_RANGE)
|
||||
|
||||
struct vivid_cec_work {
|
||||
struct list_head list;
|
||||
struct delayed_work work;
|
||||
struct cec_adapter *adap;
|
||||
struct vivid_dev *dev;
|
||||
unsigned int usecs;
|
||||
unsigned int timeout_ms;
|
||||
u8 tx_status;
|
||||
struct cec_msg msg;
|
||||
};
|
||||
|
||||
struct vivid_dev {
|
||||
unsigned inst;
|
||||
struct v4l2_device v4l2_dev;
|
||||
|
@ -497,6 +510,20 @@ struct vivid_dev {
|
|||
/* Shared between radio receiver and transmitter */
|
||||
bool radio_rds_loop;
|
||||
struct timespec radio_rds_init_ts;
|
||||
|
||||
/* CEC */
|
||||
struct cec_adapter *cec_rx_adap;
|
||||
struct cec_adapter *cec_tx_adap[MAX_OUTPUTS];
|
||||
struct workqueue_struct *cec_workqueue;
|
||||
spinlock_t cec_slock;
|
||||
struct list_head cec_work_list;
|
||||
unsigned int cec_xfer_time_jiffies;
|
||||
unsigned long cec_xfer_start_jiffies;
|
||||
u8 cec_output2bus_map[MAX_OUTPUTS];
|
||||
|
||||
/* CEC OSD String */
|
||||
char osd[14];
|
||||
unsigned long osd_jiffies;
|
||||
};
|
||||
|
||||
static inline bool vivid_is_webcam(const struct vivid_dev *dev)
|
||||
|
|
|
@ -552,6 +552,19 @@ static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf)
|
|||
snprintf(str, sizeof(str), " button pressed!");
|
||||
tpg_gen_text(tpg, basep, line++ * line_height, 16, str);
|
||||
}
|
||||
if (dev->osd[0]) {
|
||||
if (vivid_is_hdmi_cap(dev)) {
|
||||
snprintf(str, sizeof(str),
|
||||
" OSD \"%s\"", dev->osd);
|
||||
tpg_gen_text(tpg, basep, line++ * line_height,
|
||||
16, str);
|
||||
}
|
||||
if (dev->osd_jiffies &&
|
||||
time_is_before_jiffies(dev->osd_jiffies + 5 * HZ)) {
|
||||
dev->osd[0] = 0;
|
||||
dev->osd_jiffies = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -1695,6 +1695,9 @@ int vidioc_s_edid(struct file *file, void *_fh,
|
|||
struct v4l2_edid *edid)
|
||||
{
|
||||
struct vivid_dev *dev = video_drvdata(file);
|
||||
u16 phys_addr;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
memset(edid->reserved, 0, sizeof(edid->reserved));
|
||||
if (edid->pad >= dev->num_inputs)
|
||||
|
@ -1703,14 +1706,32 @@ int vidioc_s_edid(struct file *file, void *_fh,
|
|||
return -EINVAL;
|
||||
if (edid->blocks == 0) {
|
||||
dev->edid_blocks = 0;
|
||||
return 0;
|
||||
phys_addr = CEC_PHYS_ADDR_INVALID;
|
||||
goto set_phys_addr;
|
||||
}
|
||||
if (edid->blocks > dev->edid_max_blocks) {
|
||||
edid->blocks = dev->edid_max_blocks;
|
||||
return -E2BIG;
|
||||
}
|
||||
phys_addr = cec_get_edid_phys_addr(edid->edid, edid->blocks * 128, NULL);
|
||||
ret = cec_phys_addr_validate(phys_addr, &phys_addr, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (vb2_is_busy(&dev->vb_vid_cap_q))
|
||||
return -EBUSY;
|
||||
|
||||
dev->edid_blocks = edid->blocks;
|
||||
memcpy(dev->edid, edid->edid, edid->blocks * 128);
|
||||
|
||||
set_phys_addr:
|
||||
/* TODO: a proper hotplug detect cycle should be emulated here */
|
||||
cec_s_phys_addr(dev->cec_rx_adap, phys_addr, false);
|
||||
|
||||
for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++)
|
||||
cec_s_phys_addr(dev->cec_tx_adap[i],
|
||||
cec_phys_addr_for_input(phys_addr, i + 1),
|
||||
false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -811,6 +811,7 @@ int vidioc_g_edid(struct file *file, void *_fh,
|
|||
{
|
||||
struct vivid_dev *dev = video_drvdata(file);
|
||||
struct video_device *vdev = video_devdata(file);
|
||||
struct cec_adapter *adap;
|
||||
|
||||
memset(edid->reserved, 0, sizeof(edid->reserved));
|
||||
if (vdev->vfl_dir == VFL_DIR_RX) {
|
||||
|
@ -818,11 +819,16 @@ int vidioc_g_edid(struct file *file, void *_fh,
|
|||
return -EINVAL;
|
||||
if (dev->input_type[edid->pad] != HDMI)
|
||||
return -EINVAL;
|
||||
adap = dev->cec_rx_adap;
|
||||
} else {
|
||||
unsigned int bus_idx;
|
||||
|
||||
if (edid->pad >= dev->num_outputs)
|
||||
return -EINVAL;
|
||||
if (dev->output_type[edid->pad] != HDMI)
|
||||
return -EINVAL;
|
||||
bus_idx = dev->cec_output2bus_map[edid->pad];
|
||||
adap = dev->cec_tx_adap[bus_idx];
|
||||
}
|
||||
if (edid->start_block == 0 && edid->blocks == 0) {
|
||||
edid->blocks = dev->edid_blocks;
|
||||
|
@ -835,5 +841,6 @@ int vidioc_g_edid(struct file *file, void *_fh,
|
|||
if (edid->start_block + edid->blocks > dev->edid_blocks)
|
||||
edid->blocks = dev->edid_blocks - edid->start_block;
|
||||
memcpy(edid->edid, dev->edid, edid->blocks * 128);
|
||||
cec_set_edid_phys_addr(edid->edid, edid->blocks * 128, adap->phys_addr);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -810,6 +810,7 @@ static const struct {
|
|||
{ RC_BIT_SHARP, "sharp", "ir-sharp-decoder" },
|
||||
{ RC_BIT_MCE_KBD, "mce_kbd", "ir-mce_kbd-decoder" },
|
||||
{ RC_BIT_XMP, "xmp", "ir-xmp-decoder" },
|
||||
{ RC_BIT_CEC, "cec", NULL },
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -21,6 +21,8 @@ if STAGING_MEDIA
|
|||
# Please keep them in alphabetic order
|
||||
source "drivers/staging/media/bcm2048/Kconfig"
|
||||
|
||||
source "drivers/staging/media/cec/Kconfig"
|
||||
|
||||
source "drivers/staging/media/cxd2099/Kconfig"
|
||||
|
||||
source "drivers/staging/media/davinci_vpfe/Kconfig"
|
||||
|
@ -29,6 +31,8 @@ source "drivers/staging/media/omap4iss/Kconfig"
|
|||
|
||||
source "drivers/staging/media/tw686x-kh/Kconfig"
|
||||
|
||||
source "drivers/staging/media/s5p-cec/Kconfig"
|
||||
|
||||
# Keep LIRC at the end, as it has sub-menus
|
||||
source "drivers/staging/media/lirc/Kconfig"
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
obj-$(CONFIG_I2C_BCM2048) += bcm2048/
|
||||
obj-$(CONFIG_MEDIA_CEC) += cec/
|
||||
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_CEC) += s5p-cec/
|
||||
obj-$(CONFIG_DVB_CXD2099) += cxd2099/
|
||||
obj-$(CONFIG_LIRC_STAGING) += lirc/
|
||||
obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
config MEDIA_CEC
|
||||
tristate "CEC API (EXPERIMENTAL)"
|
||||
select MEDIA_CEC_EDID
|
||||
---help---
|
||||
Enable the CEC API.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called cec.
|
||||
|
||||
config MEDIA_CEC_DEBUG
|
||||
bool "CEC debugfs interface (EXPERIMENTAL)"
|
||||
depends on MEDIA_CEC && DEBUG_FS
|
||||
---help---
|
||||
Turns on the DebugFS interface for CEC devices.
|
|
@ -0,0 +1,3 @@
|
|||
cec-objs := cec-core.o cec-adap.o cec-api.o
|
||||
|
||||
obj-$(CONFIG_MEDIA_CEC) += cec.o
|
|
@ -0,0 +1,27 @@
|
|||
The reason why cec.c is still in staging is that I would like
|
||||
to have a bit more confidence in the uABI. The kABI is fine,
|
||||
no problem there, but I would like to let the public API mature
|
||||
a bit.
|
||||
|
||||
Once I'm confident that I didn't miss anything then the cec.c source
|
||||
can move to drivers/media and the linux/cec.h and linux/cec-funcs.h
|
||||
headers can move to uapi/linux and added to uapi/linux/Kbuild to make
|
||||
them public.
|
||||
|
||||
Hopefully this will happen later in 2016.
|
||||
|
||||
Other TODOs:
|
||||
|
||||
- Add a flag to inhibit passing CEC RC messages to the rc subsystem.
|
||||
Applications should be able to choose this when calling S_LOG_ADDRS.
|
||||
- Convert cec.txt to sphinx.
|
||||
- If the reply field of cec_msg is set then when the reply arrives it
|
||||
is only sent to the filehandle that transmitted the original message
|
||||
and not to any followers. Should this behavior change or perhaps
|
||||
controlled through a cec_msg flag?
|
||||
- Should CEC_LOG_ADDR_TYPE_SPECIFIC be replaced by TYPE_2ND_TV and TYPE_PROCESSOR?
|
||||
And also TYPE_SWITCH and TYPE_CDC_ONLY in addition to the TYPE_UNREGISTERED?
|
||||
This should give the framework more information about the device type
|
||||
since SPECIFIC and UNREGISTERED give no useful information.
|
||||
|
||||
Hans Verkuil <hans.verkuil@cisco.com>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,578 @@
|
|||
/*
|
||||
* cec-api.c - HDMI Consumer Electronics Control framework - API
|
||||
*
|
||||
* Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
||||
*
|
||||
* This program is free software; you may redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kmod.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#include "cec-priv.h"
|
||||
|
||||
static inline struct cec_devnode *cec_devnode_data(struct file *filp)
|
||||
{
|
||||
struct cec_fh *fh = filp->private_data;
|
||||
|
||||
return &fh->adap->devnode;
|
||||
}
|
||||
|
||||
/* CEC file operations */
|
||||
|
||||
static unsigned int cec_poll(struct file *filp,
|
||||
struct poll_table_struct *poll)
|
||||
{
|
||||
struct cec_devnode *devnode = cec_devnode_data(filp);
|
||||
struct cec_fh *fh = filp->private_data;
|
||||
struct cec_adapter *adap = fh->adap;
|
||||
unsigned int res = 0;
|
||||
|
||||
if (!devnode->registered)
|
||||
return POLLERR | POLLHUP;
|
||||
mutex_lock(&adap->lock);
|
||||
if (adap->is_configured)
|
||||
res |= POLLOUT | POLLWRNORM;
|
||||
if (fh->queued_msgs)
|
||||
res |= POLLIN | POLLRDNORM;
|
||||
if (fh->pending_events)
|
||||
res |= POLLPRI;
|
||||
poll_wait(filp, &fh->wait, poll);
|
||||
mutex_unlock(&adap->lock);
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool cec_is_busy(const struct cec_adapter *adap,
|
||||
const struct cec_fh *fh)
|
||||
{
|
||||
bool valid_initiator = adap->cec_initiator && adap->cec_initiator == fh;
|
||||
bool valid_follower = adap->cec_follower && adap->cec_follower == fh;
|
||||
|
||||
/*
|
||||
* Exclusive initiators and followers can always access the CEC adapter
|
||||
*/
|
||||
if (valid_initiator || valid_follower)
|
||||
return false;
|
||||
/*
|
||||
* All others can only access the CEC adapter if there is no
|
||||
* exclusive initiator and they are in INITIATOR mode.
|
||||
*/
|
||||
return adap->cec_initiator ||
|
||||
fh->mode_initiator == CEC_MODE_NO_INITIATOR;
|
||||
}
|
||||
|
||||
static long cec_adap_g_caps(struct cec_adapter *adap,
|
||||
struct cec_caps __user *parg)
|
||||
{
|
||||
struct cec_caps caps = {};
|
||||
|
||||
strlcpy(caps.driver, adap->devnode.parent->driver->name,
|
||||
sizeof(caps.driver));
|
||||
strlcpy(caps.name, adap->name, sizeof(caps.name));
|
||||
caps.available_log_addrs = adap->available_log_addrs;
|
||||
caps.capabilities = adap->capabilities;
|
||||
caps.version = LINUX_VERSION_CODE;
|
||||
if (copy_to_user(parg, &caps, sizeof(caps)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long cec_adap_g_phys_addr(struct cec_adapter *adap,
|
||||
__u16 __user *parg)
|
||||
{
|
||||
u16 phys_addr;
|
||||
|
||||
mutex_lock(&adap->lock);
|
||||
phys_addr = adap->phys_addr;
|
||||
mutex_unlock(&adap->lock);
|
||||
if (copy_to_user(parg, &phys_addr, sizeof(phys_addr)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long cec_adap_s_phys_addr(struct cec_adapter *adap, struct cec_fh *fh,
|
||||
bool block, __u16 __user *parg)
|
||||
{
|
||||
u16 phys_addr;
|
||||
long err;
|
||||
|
||||
if (!(adap->capabilities & CEC_CAP_PHYS_ADDR))
|
||||
return -ENOTTY;
|
||||
if (copy_from_user(&phys_addr, parg, sizeof(phys_addr)))
|
||||
return -EFAULT;
|
||||
|
||||
err = cec_phys_addr_validate(phys_addr, NULL, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
mutex_lock(&adap->lock);
|
||||
if (cec_is_busy(adap, fh))
|
||||
err = -EBUSY;
|
||||
else
|
||||
__cec_s_phys_addr(adap, phys_addr, block);
|
||||
mutex_unlock(&adap->lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
static long cec_adap_g_log_addrs(struct cec_adapter *adap,
|
||||
struct cec_log_addrs __user *parg)
|
||||
{
|
||||
struct cec_log_addrs log_addrs;
|
||||
|
||||
mutex_lock(&adap->lock);
|
||||
log_addrs = adap->log_addrs;
|
||||
if (!adap->is_configured)
|
||||
memset(log_addrs.log_addr, CEC_LOG_ADDR_INVALID,
|
||||
sizeof(log_addrs.log_addr));
|
||||
mutex_unlock(&adap->lock);
|
||||
|
||||
if (copy_to_user(parg, &log_addrs, sizeof(log_addrs)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long cec_adap_s_log_addrs(struct cec_adapter *adap, struct cec_fh *fh,
|
||||
bool block, struct cec_log_addrs __user *parg)
|
||||
{
|
||||
struct cec_log_addrs log_addrs;
|
||||
long err = -EBUSY;
|
||||
|
||||
if (!(adap->capabilities & CEC_CAP_LOG_ADDRS))
|
||||
return -ENOTTY;
|
||||
if (copy_from_user(&log_addrs, parg, sizeof(log_addrs)))
|
||||
return -EFAULT;
|
||||
log_addrs.flags = 0;
|
||||
mutex_lock(&adap->lock);
|
||||
if (!adap->is_configuring &&
|
||||
(!log_addrs.num_log_addrs || !adap->is_configured) &&
|
||||
!cec_is_busy(adap, fh)) {
|
||||
err = __cec_s_log_addrs(adap, &log_addrs, block);
|
||||
if (!err)
|
||||
log_addrs = adap->log_addrs;
|
||||
}
|
||||
mutex_unlock(&adap->lock);
|
||||
if (err)
|
||||
return err;
|
||||
if (copy_to_user(parg, &log_addrs, sizeof(log_addrs)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long cec_transmit(struct cec_adapter *adap, struct cec_fh *fh,
|
||||
bool block, struct cec_msg __user *parg)
|
||||
{
|
||||
struct cec_msg msg = {};
|
||||
long err = 0;
|
||||
|
||||
if (!(adap->capabilities & CEC_CAP_TRANSMIT))
|
||||
return -ENOTTY;
|
||||
if (copy_from_user(&msg, parg, sizeof(msg)))
|
||||
return -EFAULT;
|
||||
mutex_lock(&adap->lock);
|
||||
if (!adap->is_configured) {
|
||||
err = -ENONET;
|
||||
} else if (cec_is_busy(adap, fh)) {
|
||||
err = -EBUSY;
|
||||
} else {
|
||||
if (!block || !msg.reply)
|
||||
fh = NULL;
|
||||
err = cec_transmit_msg_fh(adap, &msg, fh, block);
|
||||
}
|
||||
mutex_unlock(&adap->lock);
|
||||
if (err)
|
||||
return err;
|
||||
if (copy_to_user(parg, &msg, sizeof(msg)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called by CEC_RECEIVE: wait for a message to arrive */
|
||||
static int cec_receive_msg(struct cec_fh *fh, struct cec_msg *msg, bool block)
|
||||
{
|
||||
int res;
|
||||
|
||||
do {
|
||||
mutex_lock(&fh->lock);
|
||||
/* Are there received messages queued up? */
|
||||
if (fh->queued_msgs) {
|
||||
/* Yes, return the first one */
|
||||
struct cec_msg_entry *entry =
|
||||
list_first_entry(&fh->msgs,
|
||||
struct cec_msg_entry, list);
|
||||
|
||||
list_del(&entry->list);
|
||||
*msg = entry->msg;
|
||||
kfree(entry);
|
||||
fh->queued_msgs--;
|
||||
mutex_unlock(&fh->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* No, return EAGAIN in non-blocking mode or wait */
|
||||
mutex_unlock(&fh->lock);
|
||||
|
||||
/* Return when in non-blocking mode */
|
||||
if (!block)
|
||||
return -EAGAIN;
|
||||
|
||||
if (msg->timeout) {
|
||||
/* The user specified a timeout */
|
||||
res = wait_event_interruptible_timeout(fh->wait,
|
||||
fh->queued_msgs,
|
||||
msecs_to_jiffies(msg->timeout));
|
||||
if (res == 0)
|
||||
res = -ETIMEDOUT;
|
||||
else if (res > 0)
|
||||
res = 0;
|
||||
} else {
|
||||
/* Wait indefinitely */
|
||||
res = wait_event_interruptible(fh->wait,
|
||||
fh->queued_msgs);
|
||||
}
|
||||
/* Exit on error, otherwise loop to get the new message */
|
||||
} while (!res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static long cec_receive(struct cec_adapter *adap, struct cec_fh *fh,
|
||||
bool block, struct cec_msg __user *parg)
|
||||
{
|
||||
struct cec_msg msg = {};
|
||||
long err = 0;
|
||||
|
||||
if (copy_from_user(&msg, parg, sizeof(msg)))
|
||||
return -EFAULT;
|
||||
mutex_lock(&adap->lock);
|
||||
if (!adap->is_configured)
|
||||
err = -ENONET;
|
||||
mutex_unlock(&adap->lock);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = cec_receive_msg(fh, &msg, block);
|
||||
if (err)
|
||||
return err;
|
||||
if (copy_to_user(parg, &msg, sizeof(msg)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long cec_dqevent(struct cec_adapter *adap, struct cec_fh *fh,
|
||||
bool block, struct cec_event __user *parg)
|
||||
{
|
||||
struct cec_event *ev = NULL;
|
||||
u64 ts = ~0ULL;
|
||||
unsigned int i;
|
||||
long err = 0;
|
||||
|
||||
mutex_lock(&fh->lock);
|
||||
while (!fh->pending_events && block) {
|
||||
mutex_unlock(&fh->lock);
|
||||
err = wait_event_interruptible(fh->wait, fh->pending_events);
|
||||
if (err)
|
||||
return err;
|
||||
mutex_lock(&fh->lock);
|
||||
}
|
||||
|
||||
/* Find the oldest event */
|
||||
for (i = 0; i < CEC_NUM_EVENTS; i++) {
|
||||
if (fh->pending_events & (1 << (i + 1)) &&
|
||||
fh->events[i].ts <= ts) {
|
||||
ev = &fh->events[i];
|
||||
ts = ev->ts;
|
||||
}
|
||||
}
|
||||
if (!ev) {
|
||||
err = -EAGAIN;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (copy_to_user(parg, ev, sizeof(*ev))) {
|
||||
err = -EFAULT;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
fh->pending_events &= ~(1 << ev->event);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&fh->lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
static long cec_g_mode(struct cec_adapter *adap, struct cec_fh *fh,
|
||||
u32 __user *parg)
|
||||
{
|
||||
u32 mode = fh->mode_initiator | fh->mode_follower;
|
||||
|
||||
if (copy_to_user(parg, &mode, sizeof(mode)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long cec_s_mode(struct cec_adapter *adap, struct cec_fh *fh,
|
||||
u32 __user *parg)
|
||||
{
|
||||
u32 mode;
|
||||
u8 mode_initiator;
|
||||
u8 mode_follower;
|
||||
long err = 0;
|
||||
|
||||
if (copy_from_user(&mode, parg, sizeof(mode)))
|
||||
return -EFAULT;
|
||||
if (mode & ~(CEC_MODE_INITIATOR_MSK | CEC_MODE_FOLLOWER_MSK))
|
||||
return -EINVAL;
|
||||
|
||||
mode_initiator = mode & CEC_MODE_INITIATOR_MSK;
|
||||
mode_follower = mode & CEC_MODE_FOLLOWER_MSK;
|
||||
|
||||
if (mode_initiator > CEC_MODE_EXCL_INITIATOR ||
|
||||
mode_follower > CEC_MODE_MONITOR_ALL)
|
||||
return -EINVAL;
|
||||
|
||||
if (mode_follower == CEC_MODE_MONITOR_ALL &&
|
||||
!(adap->capabilities & CEC_CAP_MONITOR_ALL))
|
||||
return -EINVAL;
|
||||
|
||||
/* Follower modes should always be able to send CEC messages */
|
||||
if ((mode_initiator == CEC_MODE_NO_INITIATOR ||
|
||||
!(adap->capabilities & CEC_CAP_TRANSMIT)) &&
|
||||
mode_follower >= CEC_MODE_FOLLOWER &&
|
||||
mode_follower <= CEC_MODE_EXCL_FOLLOWER_PASSTHRU)
|
||||
return -EINVAL;
|
||||
|
||||
/* Monitor modes require CEC_MODE_NO_INITIATOR */
|
||||
if (mode_initiator && mode_follower >= CEC_MODE_MONITOR)
|
||||
return -EINVAL;
|
||||
|
||||
/* Monitor modes require CAP_NET_ADMIN */
|
||||
if (mode_follower >= CEC_MODE_MONITOR && !capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
mutex_lock(&adap->lock);
|
||||
/*
|
||||
* You can't become exclusive follower if someone else already
|
||||
* has that job.
|
||||
*/
|
||||
if ((mode_follower == CEC_MODE_EXCL_FOLLOWER ||
|
||||
mode_follower == CEC_MODE_EXCL_FOLLOWER_PASSTHRU) &&
|
||||
adap->cec_follower && adap->cec_follower != fh)
|
||||
err = -EBUSY;
|
||||
/*
|
||||
* You can't become exclusive initiator if someone else already
|
||||
* has that job.
|
||||
*/
|
||||
if (mode_initiator == CEC_MODE_EXCL_INITIATOR &&
|
||||
adap->cec_initiator && adap->cec_initiator != fh)
|
||||
err = -EBUSY;
|
||||
|
||||
if (!err) {
|
||||
bool old_mon_all = fh->mode_follower == CEC_MODE_MONITOR_ALL;
|
||||
bool new_mon_all = mode_follower == CEC_MODE_MONITOR_ALL;
|
||||
|
||||
if (old_mon_all != new_mon_all) {
|
||||
if (new_mon_all)
|
||||
err = cec_monitor_all_cnt_inc(adap);
|
||||
else
|
||||
cec_monitor_all_cnt_dec(adap);
|
||||
}
|
||||
}
|
||||
|
||||
if (err) {
|
||||
mutex_unlock(&adap->lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (fh->mode_follower == CEC_MODE_FOLLOWER)
|
||||
adap->follower_cnt--;
|
||||
if (mode_follower == CEC_MODE_FOLLOWER)
|
||||
adap->follower_cnt++;
|
||||
if (mode_follower == CEC_MODE_EXCL_FOLLOWER ||
|
||||
mode_follower == CEC_MODE_EXCL_FOLLOWER_PASSTHRU) {
|
||||
adap->passthrough =
|
||||
mode_follower == CEC_MODE_EXCL_FOLLOWER_PASSTHRU;
|
||||
adap->cec_follower = fh;
|
||||
} else if (adap->cec_follower == fh) {
|
||||
adap->passthrough = false;
|
||||
adap->cec_follower = NULL;
|
||||
}
|
||||
if (mode_initiator == CEC_MODE_EXCL_INITIATOR)
|
||||
adap->cec_initiator = fh;
|
||||
else if (adap->cec_initiator == fh)
|
||||
adap->cec_initiator = NULL;
|
||||
fh->mode_initiator = mode_initiator;
|
||||
fh->mode_follower = mode_follower;
|
||||
mutex_unlock(&adap->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long cec_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct cec_devnode *devnode = cec_devnode_data(filp);
|
||||
struct cec_fh *fh = filp->private_data;
|
||||
struct cec_adapter *adap = fh->adap;
|
||||
bool block = !(filp->f_flags & O_NONBLOCK);
|
||||
void __user *parg = (void __user *)arg;
|
||||
|
||||
if (!devnode->registered)
|
||||
return -EIO;
|
||||
|
||||
switch (cmd) {
|
||||
case CEC_ADAP_G_CAPS:
|
||||
return cec_adap_g_caps(adap, parg);
|
||||
|
||||
case CEC_ADAP_G_PHYS_ADDR:
|
||||
return cec_adap_g_phys_addr(adap, parg);
|
||||
|
||||
case CEC_ADAP_S_PHYS_ADDR:
|
||||
return cec_adap_s_phys_addr(adap, fh, block, parg);
|
||||
|
||||
case CEC_ADAP_G_LOG_ADDRS:
|
||||
return cec_adap_g_log_addrs(adap, parg);
|
||||
|
||||
case CEC_ADAP_S_LOG_ADDRS:
|
||||
return cec_adap_s_log_addrs(adap, fh, block, parg);
|
||||
|
||||
case CEC_TRANSMIT:
|
||||
return cec_transmit(adap, fh, block, parg);
|
||||
|
||||
case CEC_RECEIVE:
|
||||
return cec_receive(adap, fh, block, parg);
|
||||
|
||||
case CEC_DQEVENT:
|
||||
return cec_dqevent(adap, fh, block, parg);
|
||||
|
||||
case CEC_G_MODE:
|
||||
return cec_g_mode(adap, fh, parg);
|
||||
|
||||
case CEC_S_MODE:
|
||||
return cec_s_mode(adap, fh, parg);
|
||||
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
}
|
||||
|
||||
static int cec_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct cec_devnode *devnode =
|
||||
container_of(inode->i_cdev, struct cec_devnode, cdev);
|
||||
struct cec_adapter *adap = to_cec_adapter(devnode);
|
||||
struct cec_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL);
|
||||
/*
|
||||
* Initial events that are automatically sent when the cec device is
|
||||
* opened.
|
||||
*/
|
||||
struct cec_event ev_state = {
|
||||
.event = CEC_EVENT_STATE_CHANGE,
|
||||
.flags = CEC_EVENT_FL_INITIAL_STATE,
|
||||
};
|
||||
int err;
|
||||
|
||||
if (!fh)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&fh->msgs);
|
||||
INIT_LIST_HEAD(&fh->xfer_list);
|
||||
mutex_init(&fh->lock);
|
||||
init_waitqueue_head(&fh->wait);
|
||||
|
||||
fh->mode_initiator = CEC_MODE_INITIATOR;
|
||||
fh->adap = adap;
|
||||
|
||||
err = cec_get_device(devnode);
|
||||
if (err) {
|
||||
kfree(fh);
|
||||
return err;
|
||||
}
|
||||
|
||||
filp->private_data = fh;
|
||||
|
||||
mutex_lock(&devnode->fhs_lock);
|
||||
/* Queue up initial state events */
|
||||
ev_state.state_change.phys_addr = adap->phys_addr;
|
||||
ev_state.state_change.log_addr_mask = adap->log_addrs.log_addr_mask;
|
||||
cec_queue_event_fh(fh, &ev_state, 0);
|
||||
|
||||
list_add(&fh->list, &devnode->fhs);
|
||||
mutex_unlock(&devnode->fhs_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Override for the release function */
|
||||
static int cec_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct cec_devnode *devnode = cec_devnode_data(filp);
|
||||
struct cec_adapter *adap = to_cec_adapter(devnode);
|
||||
struct cec_fh *fh = filp->private_data;
|
||||
|
||||
mutex_lock(&adap->lock);
|
||||
if (adap->cec_initiator == fh)
|
||||
adap->cec_initiator = NULL;
|
||||
if (adap->cec_follower == fh) {
|
||||
adap->cec_follower = NULL;
|
||||
adap->passthrough = false;
|
||||
}
|
||||
if (fh->mode_follower == CEC_MODE_FOLLOWER)
|
||||
adap->follower_cnt--;
|
||||
if (fh->mode_follower == CEC_MODE_MONITOR_ALL)
|
||||
cec_monitor_all_cnt_dec(adap);
|
||||
mutex_unlock(&adap->lock);
|
||||
|
||||
mutex_lock(&devnode->fhs_lock);
|
||||
list_del(&fh->list);
|
||||
mutex_unlock(&devnode->fhs_lock);
|
||||
|
||||
/* Unhook pending transmits from this filehandle. */
|
||||
mutex_lock(&adap->lock);
|
||||
while (!list_empty(&fh->xfer_list)) {
|
||||
struct cec_data *data =
|
||||
list_first_entry(&fh->xfer_list, struct cec_data, xfer_list);
|
||||
|
||||
data->blocking = false;
|
||||
data->fh = NULL;
|
||||
list_del(&data->xfer_list);
|
||||
}
|
||||
mutex_unlock(&adap->lock);
|
||||
while (!list_empty(&fh->msgs)) {
|
||||
struct cec_msg_entry *entry =
|
||||
list_first_entry(&fh->msgs, struct cec_msg_entry, list);
|
||||
|
||||
list_del(&entry->list);
|
||||
kfree(entry);
|
||||
}
|
||||
kfree(fh);
|
||||
|
||||
cec_put_device(devnode);
|
||||
filp->private_data = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct file_operations cec_devnode_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = cec_open,
|
||||
.unlocked_ioctl = cec_ioctl,
|
||||
.release = cec_release,
|
||||
.poll = cec_poll,
|
||||
.llseek = no_llseek,
|
||||
};
|
|
@ -0,0 +1,409 @@
|
|||
/*
|
||||
* cec-core.c - HDMI Consumer Electronics Control framework - Core
|
||||
*
|
||||
* Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
||||
*
|
||||
* This program is free software; you may redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kmod.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "cec-priv.h"
|
||||
|
||||
#define CEC_NUM_DEVICES 256
|
||||
#define CEC_NAME "cec"
|
||||
|
||||
int cec_debug;
|
||||
module_param_named(debug, cec_debug, int, 0644);
|
||||
MODULE_PARM_DESC(debug, "debug level (0-2)");
|
||||
|
||||
static dev_t cec_dev_t;
|
||||
|
||||
/* Active devices */
|
||||
static DEFINE_MUTEX(cec_devnode_lock);
|
||||
static DECLARE_BITMAP(cec_devnode_nums, CEC_NUM_DEVICES);
|
||||
|
||||
static struct dentry *top_cec_dir;
|
||||
|
||||
/* dev to cec_devnode */
|
||||
#define to_cec_devnode(cd) container_of(cd, struct cec_devnode, dev)
|
||||
|
||||
int cec_get_device(struct cec_devnode *devnode)
|
||||
{
|
||||
/*
|
||||
* Check if the cec device is available. This needs to be done with
|
||||
* the cec_devnode_lock held to prevent an open/unregister race:
|
||||
* without the lock, the device could be unregistered and freed between
|
||||
* the devnode->registered check and get_device() calls, leading to
|
||||
* a crash.
|
||||
*/
|
||||
mutex_lock(&cec_devnode_lock);
|
||||
/*
|
||||
* return ENXIO if the cec device has been removed
|
||||
* already or if it is not registered anymore.
|
||||
*/
|
||||
if (!devnode->registered) {
|
||||
mutex_unlock(&cec_devnode_lock);
|
||||
return -ENXIO;
|
||||
}
|
||||
/* and increase the device refcount */
|
||||
get_device(&devnode->dev);
|
||||
mutex_unlock(&cec_devnode_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cec_put_device(struct cec_devnode *devnode)
|
||||
{
|
||||
mutex_lock(&cec_devnode_lock);
|
||||
put_device(&devnode->dev);
|
||||
mutex_unlock(&cec_devnode_lock);
|
||||
}
|
||||
|
||||
/* Called when the last user of the cec device exits. */
|
||||
static void cec_devnode_release(struct device *cd)
|
||||
{
|
||||
struct cec_devnode *devnode = to_cec_devnode(cd);
|
||||
|
||||
mutex_lock(&cec_devnode_lock);
|
||||
|
||||
/* Mark device node number as free */
|
||||
clear_bit(devnode->minor, cec_devnode_nums);
|
||||
|
||||
mutex_unlock(&cec_devnode_lock);
|
||||
cec_delete_adapter(to_cec_adapter(devnode));
|
||||
}
|
||||
|
||||
static struct bus_type cec_bus_type = {
|
||||
.name = CEC_NAME,
|
||||
};
|
||||
|
||||
/*
|
||||
* Register a cec device node
|
||||
*
|
||||
* The registration code assigns minor numbers and registers the new device node
|
||||
* with the kernel. An error is returned if no free minor number can be found,
|
||||
* or if the registration of the device node fails.
|
||||
*
|
||||
* Zero is returned on success.
|
||||
*
|
||||
* Note that if the cec_devnode_register call fails, the release() callback of
|
||||
* the cec_devnode structure is *not* called, so the caller is responsible for
|
||||
* freeing any data.
|
||||
*/
|
||||
static int __must_check cec_devnode_register(struct cec_devnode *devnode,
|
||||
struct module *owner)
|
||||
{
|
||||
int minor;
|
||||
int ret;
|
||||
|
||||
/* Initialization */
|
||||
INIT_LIST_HEAD(&devnode->fhs);
|
||||
mutex_init(&devnode->fhs_lock);
|
||||
|
||||
/* Part 1: Find a free minor number */
|
||||
mutex_lock(&cec_devnode_lock);
|
||||
minor = find_next_zero_bit(cec_devnode_nums, CEC_NUM_DEVICES, 0);
|
||||
if (minor == CEC_NUM_DEVICES) {
|
||||
mutex_unlock(&cec_devnode_lock);
|
||||
pr_err("could not get a free minor\n");
|
||||
return -ENFILE;
|
||||
}
|
||||
|
||||
set_bit(minor, cec_devnode_nums);
|
||||
mutex_unlock(&cec_devnode_lock);
|
||||
|
||||
devnode->minor = minor;
|
||||
devnode->dev.bus = &cec_bus_type;
|
||||
devnode->dev.devt = MKDEV(MAJOR(cec_dev_t), minor);
|
||||
devnode->dev.release = cec_devnode_release;
|
||||
devnode->dev.parent = devnode->parent;
|
||||
dev_set_name(&devnode->dev, "cec%d", devnode->minor);
|
||||
device_initialize(&devnode->dev);
|
||||
|
||||
/* Part 2: Initialize and register the character device */
|
||||
cdev_init(&devnode->cdev, &cec_devnode_fops);
|
||||
devnode->cdev.kobj.parent = &devnode->dev.kobj;
|
||||
devnode->cdev.owner = owner;
|
||||
|
||||
ret = cdev_add(&devnode->cdev, devnode->dev.devt, 1);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: cdev_add failed\n", __func__);
|
||||
goto clr_bit;
|
||||
}
|
||||
|
||||
ret = device_add(&devnode->dev);
|
||||
if (ret)
|
||||
goto cdev_del;
|
||||
|
||||
devnode->registered = true;
|
||||
return 0;
|
||||
|
||||
cdev_del:
|
||||
cdev_del(&devnode->cdev);
|
||||
clr_bit:
|
||||
clear_bit(devnode->minor, cec_devnode_nums);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unregister a cec device node
|
||||
*
|
||||
* This unregisters the passed device. Future open calls will be met with
|
||||
* errors.
|
||||
*
|
||||
* This function can safely be called if the device node has never been
|
||||
* registered or has already been unregistered.
|
||||
*/
|
||||
static void cec_devnode_unregister(struct cec_devnode *devnode)
|
||||
{
|
||||
struct cec_fh *fh;
|
||||
|
||||
/* Check if devnode was never registered or already unregistered */
|
||||
if (!devnode->registered || devnode->unregistered)
|
||||
return;
|
||||
|
||||
mutex_lock(&devnode->fhs_lock);
|
||||
list_for_each_entry(fh, &devnode->fhs, list)
|
||||
wake_up_interruptible(&fh->wait);
|
||||
mutex_unlock(&devnode->fhs_lock);
|
||||
|
||||
devnode->registered = false;
|
||||
devnode->unregistered = true;
|
||||
device_del(&devnode->dev);
|
||||
cdev_del(&devnode->cdev);
|
||||
put_device(&devnode->dev);
|
||||
}
|
||||
|
||||
struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
|
||||
void *priv, const char *name, u32 caps,
|
||||
u8 available_las, struct device *parent)
|
||||
{
|
||||
struct cec_adapter *adap;
|
||||
int res;
|
||||
|
||||
if (WARN_ON(!parent))
|
||||
return ERR_PTR(-EINVAL);
|
||||
if (WARN_ON(!caps))
|
||||
return ERR_PTR(-EINVAL);
|
||||
if (WARN_ON(!ops))
|
||||
return ERR_PTR(-EINVAL);
|
||||
if (WARN_ON(!available_las || available_las > CEC_MAX_LOG_ADDRS))
|
||||
return ERR_PTR(-EINVAL);
|
||||
adap = kzalloc(sizeof(*adap), GFP_KERNEL);
|
||||
if (!adap)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
adap->owner = parent->driver->owner;
|
||||
adap->devnode.parent = parent;
|
||||
strlcpy(adap->name, name, sizeof(adap->name));
|
||||
adap->phys_addr = CEC_PHYS_ADDR_INVALID;
|
||||
adap->log_addrs.cec_version = CEC_OP_CEC_VERSION_2_0;
|
||||
adap->log_addrs.vendor_id = CEC_VENDOR_ID_NONE;
|
||||
adap->capabilities = caps;
|
||||
adap->available_log_addrs = available_las;
|
||||
adap->sequence = 0;
|
||||
adap->ops = ops;
|
||||
adap->priv = priv;
|
||||
memset(adap->phys_addrs, 0xff, sizeof(adap->phys_addrs));
|
||||
mutex_init(&adap->lock);
|
||||
INIT_LIST_HEAD(&adap->transmit_queue);
|
||||
INIT_LIST_HEAD(&adap->wait_queue);
|
||||
init_waitqueue_head(&adap->kthread_waitq);
|
||||
|
||||
adap->kthread = kthread_run(cec_thread_func, adap, "cec-%s", name);
|
||||
if (IS_ERR(adap->kthread)) {
|
||||
pr_err("cec-%s: kernel_thread() failed\n", name);
|
||||
res = PTR_ERR(adap->kthread);
|
||||
kfree(adap);
|
||||
return ERR_PTR(res);
|
||||
}
|
||||
|
||||
if (!(caps & CEC_CAP_RC))
|
||||
return adap;
|
||||
|
||||
#if IS_ENABLED(CONFIG_RC_CORE)
|
||||
/* Prepare the RC input device */
|
||||
adap->rc = rc_allocate_device();
|
||||
if (!adap->rc) {
|
||||
pr_err("cec-%s: failed to allocate memory for rc_dev\n",
|
||||
name);
|
||||
kthread_stop(adap->kthread);
|
||||
kfree(adap);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
snprintf(adap->input_name, sizeof(adap->input_name),
|
||||
"RC for %s", name);
|
||||
snprintf(adap->input_phys, sizeof(adap->input_phys),
|
||||
"%s/input0", name);
|
||||
|
||||
adap->rc->input_name = adap->input_name;
|
||||
adap->rc->input_phys = adap->input_phys;
|
||||
adap->rc->input_id.bustype = BUS_CEC;
|
||||
adap->rc->input_id.vendor = 0;
|
||||
adap->rc->input_id.product = 0;
|
||||
adap->rc->input_id.version = 1;
|
||||
adap->rc->dev.parent = parent;
|
||||
adap->rc->driver_type = RC_DRIVER_SCANCODE;
|
||||
adap->rc->driver_name = CEC_NAME;
|
||||
adap->rc->allowed_protocols = RC_BIT_CEC;
|
||||
adap->rc->priv = adap;
|
||||
adap->rc->map_name = RC_MAP_CEC;
|
||||
adap->rc->timeout = MS_TO_NS(100);
|
||||
#else
|
||||
adap->capabilities &= ~CEC_CAP_RC;
|
||||
#endif
|
||||
return adap;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cec_allocate_adapter);
|
||||
|
||||
int cec_register_adapter(struct cec_adapter *adap)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (IS_ERR_OR_NULL(adap))
|
||||
return 0;
|
||||
|
||||
#if IS_ENABLED(CONFIG_RC_CORE)
|
||||
if (adap->capabilities & CEC_CAP_RC) {
|
||||
res = rc_register_device(adap->rc);
|
||||
|
||||
if (res) {
|
||||
pr_err("cec-%s: failed to prepare input device\n",
|
||||
adap->name);
|
||||
rc_free_device(adap->rc);
|
||||
adap->rc = NULL;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
res = cec_devnode_register(&adap->devnode, adap->owner);
|
||||
if (res) {
|
||||
#if IS_ENABLED(CONFIG_RC_CORE)
|
||||
/* Note: rc_unregister also calls rc_free */
|
||||
rc_unregister_device(adap->rc);
|
||||
adap->rc = NULL;
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
dev_set_drvdata(&adap->devnode.dev, adap);
|
||||
#ifdef CONFIG_MEDIA_CEC_DEBUG
|
||||
if (!top_cec_dir)
|
||||
return 0;
|
||||
|
||||
adap->cec_dir = debugfs_create_dir(dev_name(&adap->devnode.dev), top_cec_dir);
|
||||
if (IS_ERR_OR_NULL(adap->cec_dir)) {
|
||||
pr_warn("cec-%s: Failed to create debugfs dir\n", adap->name);
|
||||
return 0;
|
||||
}
|
||||
adap->status_file = debugfs_create_devm_seqfile(&adap->devnode.dev,
|
||||
"status", adap->cec_dir, cec_adap_status);
|
||||
if (IS_ERR_OR_NULL(adap->status_file)) {
|
||||
pr_warn("cec-%s: Failed to create status file\n", adap->name);
|
||||
debugfs_remove_recursive(adap->cec_dir);
|
||||
adap->cec_dir = NULL;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cec_register_adapter);
|
||||
|
||||
void cec_unregister_adapter(struct cec_adapter *adap)
|
||||
{
|
||||
if (IS_ERR_OR_NULL(adap))
|
||||
return;
|
||||
|
||||
#if IS_ENABLED(CONFIG_RC_CORE)
|
||||
/* Note: rc_unregister also calls rc_free */
|
||||
rc_unregister_device(adap->rc);
|
||||
adap->rc = NULL;
|
||||
#endif
|
||||
debugfs_remove_recursive(adap->cec_dir);
|
||||
cec_devnode_unregister(&adap->devnode);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cec_unregister_adapter);
|
||||
|
||||
void cec_delete_adapter(struct cec_adapter *adap)
|
||||
{
|
||||
if (IS_ERR_OR_NULL(adap))
|
||||
return;
|
||||
mutex_lock(&adap->lock);
|
||||
__cec_s_phys_addr(adap, CEC_PHYS_ADDR_INVALID, false);
|
||||
mutex_unlock(&adap->lock);
|
||||
kthread_stop(adap->kthread);
|
||||
if (adap->kthread_config)
|
||||
kthread_stop(adap->kthread_config);
|
||||
#if IS_ENABLED(CONFIG_RC_CORE)
|
||||
if (adap->rc)
|
||||
rc_free_device(adap->rc);
|
||||
#endif
|
||||
kfree(adap);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cec_delete_adapter);
|
||||
|
||||
/*
|
||||
* Initialise cec for linux
|
||||
*/
|
||||
static int __init cec_devnode_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pr_info("Linux cec interface: v0.10\n");
|
||||
ret = alloc_chrdev_region(&cec_dev_t, 0, CEC_NUM_DEVICES,
|
||||
CEC_NAME);
|
||||
if (ret < 0) {
|
||||
pr_warn("cec: unable to allocate major\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MEDIA_CEC_DEBUG
|
||||
top_cec_dir = debugfs_create_dir("cec", NULL);
|
||||
if (IS_ERR_OR_NULL(top_cec_dir)) {
|
||||
pr_warn("cec: Failed to create debugfs cec dir\n");
|
||||
top_cec_dir = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = bus_register(&cec_bus_type);
|
||||
if (ret < 0) {
|
||||
unregister_chrdev_region(cec_dev_t, CEC_NUM_DEVICES);
|
||||
pr_warn("cec: bus_register failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit cec_devnode_exit(void)
|
||||
{
|
||||
debugfs_remove_recursive(top_cec_dir);
|
||||
bus_unregister(&cec_bus_type);
|
||||
unregister_chrdev_region(cec_dev_t, CEC_NUM_DEVICES);
|
||||
}
|
||||
|
||||
subsys_initcall(cec_devnode_init);
|
||||
module_exit(cec_devnode_exit)
|
||||
|
||||
MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>");
|
||||
MODULE_DESCRIPTION("Device node registration for cec drivers");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* cec-priv.h - HDMI Consumer Electronics Control internal header
|
||||
*
|
||||
* Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
||||
*
|
||||
* This program is free software; you may redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _CEC_PRIV_H
|
||||
#define _CEC_PRIV_H
|
||||
|
||||
#include <linux/cec-funcs.h>
|
||||
#include <media/cec.h>
|
||||
|
||||
#define dprintk(lvl, fmt, arg...) \
|
||||
do { \
|
||||
if (lvl <= cec_debug) \
|
||||
pr_info("cec-%s: " fmt, adap->name, ## arg); \
|
||||
} while (0)
|
||||
|
||||
/* devnode to cec_adapter */
|
||||
#define to_cec_adapter(node) container_of(node, struct cec_adapter, devnode)
|
||||
|
||||
/* cec-core.c */
|
||||
extern int cec_debug;
|
||||
int cec_get_device(struct cec_devnode *devnode);
|
||||
void cec_put_device(struct cec_devnode *devnode);
|
||||
|
||||
/* cec-adap.c */
|
||||
int cec_monitor_all_cnt_inc(struct cec_adapter *adap);
|
||||
void cec_monitor_all_cnt_dec(struct cec_adapter *adap);
|
||||
int cec_adap_status(struct seq_file *file, void *priv);
|
||||
int cec_thread_func(void *_adap);
|
||||
void __cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block);
|
||||
int __cec_s_log_addrs(struct cec_adapter *adap,
|
||||
struct cec_log_addrs *log_addrs, bool block);
|
||||
int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg,
|
||||
struct cec_fh *fh, bool block);
|
||||
void cec_queue_event_fh(struct cec_fh *fh,
|
||||
const struct cec_event *new_ev, u64 ts);
|
||||
|
||||
/* cec-api.c */
|
||||
extern const struct file_operations cec_devnode_fops;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,9 @@
|
|||
config VIDEO_SAMSUNG_S5P_CEC
|
||||
tristate "Samsung S5P CEC driver"
|
||||
depends on VIDEO_DEV && MEDIA_CEC && (PLAT_S5P || ARCH_EXYNOS || COMPILE_TEST)
|
||||
---help---
|
||||
This is a driver for Samsung S5P HDMI CEC interface. It uses the
|
||||
generic CEC framework interface.
|
||||
CEC bus is present in the HDMI connector and enables communication
|
||||
between compatible devices.
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_CEC) += s5p-cec.o
|
||||
s5p-cec-y += s5p_cec.o exynos_hdmi_cecctrl.o
|
|
@ -0,0 +1,3 @@
|
|||
There's nothing wrong on this driver, except that it depends on
|
||||
the media staging core, that it is currently at staging. So,
|
||||
this should be kept here while the core is not promoted.
|
|
@ -0,0 +1,38 @@
|
|||
/* drivers/media/platform/s5p-cec/exynos_hdmi_cec.h
|
||||
*
|
||||
* Copyright (c) 2010, 2014 Samsung Electronics
|
||||
* http://www.samsung.com/
|
||||
*
|
||||
* Header file for interface of Samsung Exynos hdmi cec hardware
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef _EXYNOS_HDMI_CEC_H_
|
||||
#define _EXYNOS_HDMI_CEC_H_ __FILE__
|
||||
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include "s5p_cec.h"
|
||||
|
||||
void s5p_cec_set_divider(struct s5p_cec_dev *cec);
|
||||
void s5p_cec_enable_rx(struct s5p_cec_dev *cec);
|
||||
void s5p_cec_mask_rx_interrupts(struct s5p_cec_dev *cec);
|
||||
void s5p_cec_unmask_rx_interrupts(struct s5p_cec_dev *cec);
|
||||
void s5p_cec_mask_tx_interrupts(struct s5p_cec_dev *cec);
|
||||
void s5p_cec_unmask_tx_interrupts(struct s5p_cec_dev *cec);
|
||||
void s5p_cec_reset(struct s5p_cec_dev *cec);
|
||||
void s5p_cec_tx_reset(struct s5p_cec_dev *cec);
|
||||
void s5p_cec_rx_reset(struct s5p_cec_dev *cec);
|
||||
void s5p_cec_threshold(struct s5p_cec_dev *cec);
|
||||
void s5p_cec_copy_packet(struct s5p_cec_dev *cec, char *data,
|
||||
size_t count, u8 retries);
|
||||
void s5p_cec_set_addr(struct s5p_cec_dev *cec, u32 addr);
|
||||
u32 s5p_cec_get_status(struct s5p_cec_dev *cec);
|
||||
void s5p_clr_pending_tx(struct s5p_cec_dev *cec);
|
||||
void s5p_clr_pending_rx(struct s5p_cec_dev *cec);
|
||||
void s5p_cec_get_rx_buf(struct s5p_cec_dev *cec, u32 size, u8 *buffer);
|
||||
|
||||
#endif /* _EXYNOS_HDMI_CEC_H_ */
|
|
@ -0,0 +1,209 @@
|
|||
/* drivers/media/platform/s5p-cec/exynos_hdmi_cecctrl.c
|
||||
*
|
||||
* Copyright (c) 2009, 2014 Samsung Electronics
|
||||
* http://www.samsung.com/
|
||||
*
|
||||
* cec ftn file for Samsung TVOUT driver
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include "exynos_hdmi_cec.h"
|
||||
#include "regs-cec.h"
|
||||
|
||||
#define S5P_HDMI_FIN 24000000
|
||||
#define CEC_DIV_RATIO 320000
|
||||
|
||||
#define CEC_MESSAGE_BROADCAST_MASK 0x0F
|
||||
#define CEC_MESSAGE_BROADCAST 0x0F
|
||||
#define CEC_FILTER_THRESHOLD 0x15
|
||||
|
||||
void s5p_cec_set_divider(struct s5p_cec_dev *cec)
|
||||
{
|
||||
u32 div_ratio, div_val;
|
||||
unsigned int reg;
|
||||
|
||||
div_ratio = S5P_HDMI_FIN / CEC_DIV_RATIO - 1;
|
||||
|
||||
if (regmap_read(cec->pmu, EXYNOS_HDMI_PHY_CONTROL, ®)) {
|
||||
dev_err(cec->dev, "failed to read phy control\n");
|
||||
return;
|
||||
}
|
||||
|
||||
reg = (reg & ~(0x3FF << 16)) | (div_ratio << 16);
|
||||
|
||||
if (regmap_write(cec->pmu, EXYNOS_HDMI_PHY_CONTROL, reg)) {
|
||||
dev_err(cec->dev, "failed to write phy control\n");
|
||||
return;
|
||||
}
|
||||
|
||||
div_val = CEC_DIV_RATIO * 0.00005 - 1;
|
||||
|
||||
writeb(0x0, cec->reg + S5P_CEC_DIVISOR_3);
|
||||
writeb(0x0, cec->reg + S5P_CEC_DIVISOR_2);
|
||||
writeb(0x0, cec->reg + S5P_CEC_DIVISOR_1);
|
||||
writeb(div_val, cec->reg + S5P_CEC_DIVISOR_0);
|
||||
}
|
||||
|
||||
void s5p_cec_enable_rx(struct s5p_cec_dev *cec)
|
||||
{
|
||||
u8 reg;
|
||||
|
||||
reg = readb(cec->reg + S5P_CEC_RX_CTRL);
|
||||
reg |= S5P_CEC_RX_CTRL_ENABLE;
|
||||
writeb(reg, cec->reg + S5P_CEC_RX_CTRL);
|
||||
}
|
||||
|
||||
void s5p_cec_mask_rx_interrupts(struct s5p_cec_dev *cec)
|
||||
{
|
||||
u8 reg;
|
||||
|
||||
reg = readb(cec->reg + S5P_CEC_IRQ_MASK);
|
||||
reg |= S5P_CEC_IRQ_RX_DONE;
|
||||
reg |= S5P_CEC_IRQ_RX_ERROR;
|
||||
writeb(reg, cec->reg + S5P_CEC_IRQ_MASK);
|
||||
}
|
||||
|
||||
void s5p_cec_unmask_rx_interrupts(struct s5p_cec_dev *cec)
|
||||
{
|
||||
u8 reg;
|
||||
|
||||
reg = readb(cec->reg + S5P_CEC_IRQ_MASK);
|
||||
reg &= ~S5P_CEC_IRQ_RX_DONE;
|
||||
reg &= ~S5P_CEC_IRQ_RX_ERROR;
|
||||
writeb(reg, cec->reg + S5P_CEC_IRQ_MASK);
|
||||
}
|
||||
|
||||
void s5p_cec_mask_tx_interrupts(struct s5p_cec_dev *cec)
|
||||
{
|
||||
u8 reg;
|
||||
|
||||
reg = readb(cec->reg + S5P_CEC_IRQ_MASK);
|
||||
reg |= S5P_CEC_IRQ_TX_DONE;
|
||||
reg |= S5P_CEC_IRQ_TX_ERROR;
|
||||
writeb(reg, cec->reg + S5P_CEC_IRQ_MASK);
|
||||
|
||||
}
|
||||
|
||||
void s5p_cec_unmask_tx_interrupts(struct s5p_cec_dev *cec)
|
||||
{
|
||||
u8 reg;
|
||||
|
||||
reg = readb(cec->reg + S5P_CEC_IRQ_MASK);
|
||||
reg &= ~S5P_CEC_IRQ_TX_DONE;
|
||||
reg &= ~S5P_CEC_IRQ_TX_ERROR;
|
||||
writeb(reg, cec->reg + S5P_CEC_IRQ_MASK);
|
||||
}
|
||||
|
||||
void s5p_cec_reset(struct s5p_cec_dev *cec)
|
||||
{
|
||||
u8 reg;
|
||||
|
||||
writeb(S5P_CEC_RX_CTRL_RESET, cec->reg + S5P_CEC_RX_CTRL);
|
||||
writeb(S5P_CEC_TX_CTRL_RESET, cec->reg + S5P_CEC_TX_CTRL);
|
||||
|
||||
reg = readb(cec->reg + 0xc4);
|
||||
reg &= ~0x1;
|
||||
writeb(reg, cec->reg + 0xc4);
|
||||
}
|
||||
|
||||
void s5p_cec_tx_reset(struct s5p_cec_dev *cec)
|
||||
{
|
||||
writeb(S5P_CEC_TX_CTRL_RESET, cec->reg + S5P_CEC_TX_CTRL);
|
||||
}
|
||||
|
||||
void s5p_cec_rx_reset(struct s5p_cec_dev *cec)
|
||||
{
|
||||
u8 reg;
|
||||
|
||||
writeb(S5P_CEC_RX_CTRL_RESET, cec->reg + S5P_CEC_RX_CTRL);
|
||||
|
||||
reg = readb(cec->reg + 0xc4);
|
||||
reg &= ~0x1;
|
||||
writeb(reg, cec->reg + 0xc4);
|
||||
}
|
||||
|
||||
void s5p_cec_threshold(struct s5p_cec_dev *cec)
|
||||
{
|
||||
writeb(CEC_FILTER_THRESHOLD, cec->reg + S5P_CEC_RX_FILTER_TH);
|
||||
writeb(0, cec->reg + S5P_CEC_RX_FILTER_CTRL);
|
||||
}
|
||||
|
||||
void s5p_cec_copy_packet(struct s5p_cec_dev *cec, char *data,
|
||||
size_t count, u8 retries)
|
||||
{
|
||||
int i = 0;
|
||||
u8 reg;
|
||||
|
||||
while (i < count) {
|
||||
writeb(data[i], cec->reg + (S5P_CEC_TX_BUFF0 + (i * 4)));
|
||||
i++;
|
||||
}
|
||||
|
||||
writeb(count, cec->reg + S5P_CEC_TX_BYTES);
|
||||
reg = readb(cec->reg + S5P_CEC_TX_CTRL);
|
||||
reg |= S5P_CEC_TX_CTRL_START;
|
||||
reg &= ~0x70;
|
||||
reg |= retries << 4;
|
||||
|
||||
if ((data[0] & CEC_MESSAGE_BROADCAST_MASK) == CEC_MESSAGE_BROADCAST) {
|
||||
dev_dbg(cec->dev, "Broadcast");
|
||||
reg |= S5P_CEC_TX_CTRL_BCAST;
|
||||
} else {
|
||||
dev_dbg(cec->dev, "No Broadcast");
|
||||
reg &= ~S5P_CEC_TX_CTRL_BCAST;
|
||||
}
|
||||
|
||||
writeb(reg, cec->reg + S5P_CEC_TX_CTRL);
|
||||
dev_dbg(cec->dev, "cec-tx: cec count (%zu): %*ph", count,
|
||||
(int)count, data);
|
||||
}
|
||||
|
||||
void s5p_cec_set_addr(struct s5p_cec_dev *cec, u32 addr)
|
||||
{
|
||||
writeb(addr & 0x0F, cec->reg + S5P_CEC_LOGIC_ADDR);
|
||||
}
|
||||
|
||||
u32 s5p_cec_get_status(struct s5p_cec_dev *cec)
|
||||
{
|
||||
u32 status = 0;
|
||||
|
||||
status = readb(cec->reg + S5P_CEC_STATUS_0);
|
||||
status |= readb(cec->reg + S5P_CEC_STATUS_1) << 8;
|
||||
status |= readb(cec->reg + S5P_CEC_STATUS_2) << 16;
|
||||
status |= readb(cec->reg + S5P_CEC_STATUS_3) << 24;
|
||||
|
||||
dev_dbg(cec->dev, "status = 0x%x!\n", status);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void s5p_clr_pending_tx(struct s5p_cec_dev *cec)
|
||||
{
|
||||
writeb(S5P_CEC_IRQ_TX_DONE | S5P_CEC_IRQ_TX_ERROR,
|
||||
cec->reg + S5P_CEC_IRQ_CLEAR);
|
||||
}
|
||||
|
||||
void s5p_clr_pending_rx(struct s5p_cec_dev *cec)
|
||||
{
|
||||
writeb(S5P_CEC_IRQ_RX_DONE | S5P_CEC_IRQ_RX_ERROR,
|
||||
cec->reg + S5P_CEC_IRQ_CLEAR);
|
||||
}
|
||||
|
||||
void s5p_cec_get_rx_buf(struct s5p_cec_dev *cec, u32 size, u8 *buffer)
|
||||
{
|
||||
u32 i = 0;
|
||||
char debug[40];
|
||||
|
||||
while (i < size) {
|
||||
buffer[i] = readb(cec->reg + S5P_CEC_RX_BUFF0 + (i * 4));
|
||||
sprintf(debug + i * 2, "%02x ", buffer[i]);
|
||||
i++;
|
||||
}
|
||||
dev_dbg(cec->dev, "cec-rx: cec size(%d): %s", size, debug);
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
/* drivers/media/platform/s5p-cec/regs-cec.h
|
||||
*
|
||||
* Copyright (c) 2010 Samsung Electronics
|
||||
* http://www.samsung.com/
|
||||
*
|
||||
* register header file for Samsung TVOUT driver
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __EXYNOS_REGS__H
|
||||
#define __EXYNOS_REGS__H
|
||||
|
||||
/*
|
||||
* Register part
|
||||
*/
|
||||
#define S5P_CEC_STATUS_0 (0x0000)
|
||||
#define S5P_CEC_STATUS_1 (0x0004)
|
||||
#define S5P_CEC_STATUS_2 (0x0008)
|
||||
#define S5P_CEC_STATUS_3 (0x000C)
|
||||
#define S5P_CEC_IRQ_MASK (0x0010)
|
||||
#define S5P_CEC_IRQ_CLEAR (0x0014)
|
||||
#define S5P_CEC_LOGIC_ADDR (0x0020)
|
||||
#define S5P_CEC_DIVISOR_0 (0x0030)
|
||||
#define S5P_CEC_DIVISOR_1 (0x0034)
|
||||
#define S5P_CEC_DIVISOR_2 (0x0038)
|
||||
#define S5P_CEC_DIVISOR_3 (0x003C)
|
||||
|
||||
#define S5P_CEC_TX_CTRL (0x0040)
|
||||
#define S5P_CEC_TX_BYTES (0x0044)
|
||||
#define S5P_CEC_TX_STAT0 (0x0060)
|
||||
#define S5P_CEC_TX_STAT1 (0x0064)
|
||||
#define S5P_CEC_TX_BUFF0 (0x0080)
|
||||
#define S5P_CEC_TX_BUFF1 (0x0084)
|
||||
#define S5P_CEC_TX_BUFF2 (0x0088)
|
||||
#define S5P_CEC_TX_BUFF3 (0x008C)
|
||||
#define S5P_CEC_TX_BUFF4 (0x0090)
|
||||
#define S5P_CEC_TX_BUFF5 (0x0094)
|
||||
#define S5P_CEC_TX_BUFF6 (0x0098)
|
||||
#define S5P_CEC_TX_BUFF7 (0x009C)
|
||||
#define S5P_CEC_TX_BUFF8 (0x00A0)
|
||||
#define S5P_CEC_TX_BUFF9 (0x00A4)
|
||||
#define S5P_CEC_TX_BUFF10 (0x00A8)
|
||||
#define S5P_CEC_TX_BUFF11 (0x00AC)
|
||||
#define S5P_CEC_TX_BUFF12 (0x00B0)
|
||||
#define S5P_CEC_TX_BUFF13 (0x00B4)
|
||||
#define S5P_CEC_TX_BUFF14 (0x00B8)
|
||||
#define S5P_CEC_TX_BUFF15 (0x00BC)
|
||||
|
||||
#define S5P_CEC_RX_CTRL (0x00C0)
|
||||
#define S5P_CEC_RX_STAT0 (0x00E0)
|
||||
#define S5P_CEC_RX_STAT1 (0x00E4)
|
||||
#define S5P_CEC_RX_BUFF0 (0x0100)
|
||||
#define S5P_CEC_RX_BUFF1 (0x0104)
|
||||
#define S5P_CEC_RX_BUFF2 (0x0108)
|
||||
#define S5P_CEC_RX_BUFF3 (0x010C)
|
||||
#define S5P_CEC_RX_BUFF4 (0x0110)
|
||||
#define S5P_CEC_RX_BUFF5 (0x0114)
|
||||
#define S5P_CEC_RX_BUFF6 (0x0118)
|
||||
#define S5P_CEC_RX_BUFF7 (0x011C)
|
||||
#define S5P_CEC_RX_BUFF8 (0x0120)
|
||||
#define S5P_CEC_RX_BUFF9 (0x0124)
|
||||
#define S5P_CEC_RX_BUFF10 (0x0128)
|
||||
#define S5P_CEC_RX_BUFF11 (0x012C)
|
||||
#define S5P_CEC_RX_BUFF12 (0x0130)
|
||||
#define S5P_CEC_RX_BUFF13 (0x0134)
|
||||
#define S5P_CEC_RX_BUFF14 (0x0138)
|
||||
#define S5P_CEC_RX_BUFF15 (0x013C)
|
||||
|
||||
#define S5P_CEC_RX_FILTER_CTRL (0x0180)
|
||||
#define S5P_CEC_RX_FILTER_TH (0x0184)
|
||||
|
||||
/*
|
||||
* Bit definition part
|
||||
*/
|
||||
#define S5P_CEC_IRQ_TX_DONE (1<<0)
|
||||
#define S5P_CEC_IRQ_TX_ERROR (1<<1)
|
||||
#define S5P_CEC_IRQ_RX_DONE (1<<4)
|
||||
#define S5P_CEC_IRQ_RX_ERROR (1<<5)
|
||||
|
||||
#define S5P_CEC_TX_CTRL_START (1<<0)
|
||||
#define S5P_CEC_TX_CTRL_BCAST (1<<1)
|
||||
#define S5P_CEC_TX_CTRL_RETRY (0x04<<4)
|
||||
#define S5P_CEC_TX_CTRL_RESET (1<<7)
|
||||
|
||||
#define S5P_CEC_RX_CTRL_ENABLE (1<<0)
|
||||
#define S5P_CEC_RX_CTRL_RESET (1<<7)
|
||||
|
||||
#define S5P_CEC_LOGIC_ADDR_MASK (0xF)
|
||||
|
||||
/* PMU Registers for PHY */
|
||||
#define EXYNOS_HDMI_PHY_CONTROL 0x700
|
||||
|
||||
#endif /* __EXYNOS_REGS__H */
|
|
@ -0,0 +1,294 @@
|
|||
/* drivers/media/platform/s5p-cec/s5p_cec.c
|
||||
*
|
||||
* Samsung S5P CEC driver
|
||||
*
|
||||
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This driver is based on the "cec interface driver for exynos soc" by
|
||||
* SangPil Moon.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <media/cec.h>
|
||||
|
||||
#include "exynos_hdmi_cec.h"
|
||||
#include "regs-cec.h"
|
||||
#include "s5p_cec.h"
|
||||
|
||||
#define CEC_NAME "s5p-cec"
|
||||
|
||||
static int debug;
|
||||
module_param(debug, int, 0644);
|
||||
MODULE_PARM_DESC(debug, "debug level (0-2)");
|
||||
|
||||
static int s5p_cec_adap_enable(struct cec_adapter *adap, bool enable)
|
||||
{
|
||||
struct s5p_cec_dev *cec = adap->priv;
|
||||
|
||||
if (enable) {
|
||||
pm_runtime_get_sync(cec->dev);
|
||||
|
||||
s5p_cec_reset(cec);
|
||||
|
||||
s5p_cec_set_divider(cec);
|
||||
s5p_cec_threshold(cec);
|
||||
|
||||
s5p_cec_unmask_tx_interrupts(cec);
|
||||
s5p_cec_unmask_rx_interrupts(cec);
|
||||
s5p_cec_enable_rx(cec);
|
||||
} else {
|
||||
s5p_cec_mask_tx_interrupts(cec);
|
||||
s5p_cec_mask_rx_interrupts(cec);
|
||||
pm_runtime_disable(cec->dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s5p_cec_adap_log_addr(struct cec_adapter *adap, u8 addr)
|
||||
{
|
||||
struct s5p_cec_dev *cec = adap->priv;
|
||||
|
||||
s5p_cec_set_addr(cec, addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s5p_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
|
||||
u32 signal_free_time, struct cec_msg *msg)
|
||||
{
|
||||
struct s5p_cec_dev *cec = adap->priv;
|
||||
|
||||
/*
|
||||
* Unclear if 0 retries are allowed by the hardware, so have 1 as
|
||||
* the minimum.
|
||||
*/
|
||||
s5p_cec_copy_packet(cec, msg->msg, msg->len, max(1, attempts - 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t s5p_cec_irq_handler(int irq, void *priv)
|
||||
{
|
||||
struct s5p_cec_dev *cec = priv;
|
||||
u32 status = 0;
|
||||
|
||||
status = s5p_cec_get_status(cec);
|
||||
|
||||
dev_dbg(cec->dev, "irq received\n");
|
||||
|
||||
if (status & CEC_STATUS_TX_DONE) {
|
||||
if (status & CEC_STATUS_TX_ERROR) {
|
||||
dev_dbg(cec->dev, "CEC_STATUS_TX_ERROR set\n");
|
||||
cec->tx = STATE_ERROR;
|
||||
} else {
|
||||
dev_dbg(cec->dev, "CEC_STATUS_TX_DONE\n");
|
||||
cec->tx = STATE_DONE;
|
||||
}
|
||||
s5p_clr_pending_tx(cec);
|
||||
}
|
||||
|
||||
if (status & CEC_STATUS_RX_DONE) {
|
||||
if (status & CEC_STATUS_RX_ERROR) {
|
||||
dev_dbg(cec->dev, "CEC_STATUS_RX_ERROR set\n");
|
||||
s5p_cec_rx_reset(cec);
|
||||
s5p_cec_enable_rx(cec);
|
||||
} else {
|
||||
dev_dbg(cec->dev, "CEC_STATUS_RX_DONE set\n");
|
||||
if (cec->rx != STATE_IDLE)
|
||||
dev_dbg(cec->dev, "Buffer overrun (worker did not process previous message)\n");
|
||||
cec->rx = STATE_BUSY;
|
||||
cec->msg.len = status >> 24;
|
||||
cec->msg.rx_status = CEC_RX_STATUS_OK;
|
||||
s5p_cec_get_rx_buf(cec, cec->msg.len,
|
||||
cec->msg.msg);
|
||||
cec->rx = STATE_DONE;
|
||||
s5p_cec_enable_rx(cec);
|
||||
}
|
||||
/* Clear interrupt pending bit */
|
||||
s5p_clr_pending_rx(cec);
|
||||
}
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
static irqreturn_t s5p_cec_irq_handler_thread(int irq, void *priv)
|
||||
{
|
||||
struct s5p_cec_dev *cec = priv;
|
||||
|
||||
dev_dbg(cec->dev, "irq processing thread\n");
|
||||
switch (cec->tx) {
|
||||
case STATE_DONE:
|
||||
cec_transmit_done(cec->adap, CEC_TX_STATUS_OK, 0, 0, 0, 0);
|
||||
cec->tx = STATE_IDLE;
|
||||
break;
|
||||
case STATE_ERROR:
|
||||
cec_transmit_done(cec->adap,
|
||||
CEC_TX_STATUS_MAX_RETRIES | CEC_TX_STATUS_ERROR,
|
||||
0, 0, 0, 1);
|
||||
cec->tx = STATE_IDLE;
|
||||
break;
|
||||
case STATE_BUSY:
|
||||
dev_err(cec->dev, "state set to busy, this should not occur here\n");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (cec->rx) {
|
||||
case STATE_DONE:
|
||||
cec_received_msg(cec->adap, &cec->msg);
|
||||
cec->rx = STATE_IDLE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct cec_adap_ops s5p_cec_adap_ops = {
|
||||
.adap_enable = s5p_cec_adap_enable,
|
||||
.adap_log_addr = s5p_cec_adap_log_addr,
|
||||
.adap_transmit = s5p_cec_adap_transmit,
|
||||
};
|
||||
|
||||
static int s5p_cec_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
struct s5p_cec_dev *cec;
|
||||
int ret;
|
||||
|
||||
cec = devm_kzalloc(&pdev->dev, sizeof(*cec), GFP_KERNEL);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
cec->dev = dev;
|
||||
|
||||
cec->irq = platform_get_irq(pdev, 0);
|
||||
if (cec->irq < 0)
|
||||
return cec->irq;
|
||||
|
||||
ret = devm_request_threaded_irq(dev, cec->irq, s5p_cec_irq_handler,
|
||||
s5p_cec_irq_handler_thread, 0, pdev->name, cec);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cec->clk = devm_clk_get(dev, "hdmicec");
|
||||
if (IS_ERR(cec->clk))
|
||||
return PTR_ERR(cec->clk);
|
||||
|
||||
cec->pmu = syscon_regmap_lookup_by_phandle(dev->of_node,
|
||||
"samsung,syscon-phandle");
|
||||
if (IS_ERR(cec->pmu))
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
cec->reg = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(cec->reg))
|
||||
return PTR_ERR(cec->reg);
|
||||
|
||||
cec->adap = cec_allocate_adapter(&s5p_cec_adap_ops, cec,
|
||||
CEC_NAME,
|
||||
CEC_CAP_PHYS_ADDR | CEC_CAP_LOG_ADDRS | CEC_CAP_TRANSMIT |
|
||||
CEC_CAP_PASSTHROUGH | CEC_CAP_RC,
|
||||
1, &pdev->dev);
|
||||
ret = PTR_ERR_OR_ZERO(cec->adap);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = cec_register_adapter(cec->adap);
|
||||
if (ret) {
|
||||
cec_delete_adapter(cec->adap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, cec);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
dev_dbg(dev, "successfuly probed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s5p_cec_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct s5p_cec_dev *cec = platform_get_drvdata(pdev);
|
||||
|
||||
cec_unregister_adapter(cec->adap);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s5p_cec_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct s5p_cec_dev *cec = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable_unprepare(cec->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s5p_cec_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct s5p_cec_dev *cec = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(cec->clk);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s5p_cec_suspend(struct device *dev)
|
||||
{
|
||||
if (pm_runtime_suspended(dev))
|
||||
return 0;
|
||||
return s5p_cec_runtime_suspend(dev);
|
||||
}
|
||||
|
||||
static int s5p_cec_resume(struct device *dev)
|
||||
{
|
||||
if (pm_runtime_suspended(dev))
|
||||
return 0;
|
||||
return s5p_cec_runtime_resume(dev);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops s5p_cec_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(s5p_cec_suspend, s5p_cec_resume)
|
||||
SET_RUNTIME_PM_OPS(s5p_cec_runtime_suspend, s5p_cec_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
|
||||
static const struct of_device_id s5p_cec_match[] = {
|
||||
{
|
||||
.compatible = "samsung,s5p-cec",
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver s5p_cec_pdrv = {
|
||||
.probe = s5p_cec_probe,
|
||||
.remove = s5p_cec_remove,
|
||||
.driver = {
|
||||
.name = CEC_NAME,
|
||||
.of_match_table = s5p_cec_match,
|
||||
.pm = &s5p_cec_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(s5p_cec_pdrv);
|
||||
|
||||
MODULE_AUTHOR("Kamil Debski <kamil@wypas.org>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Samsung S5P CEC driver");
|
|
@ -0,0 +1,76 @@
|
|||
/* drivers/media/platform/s5p-cec/s5p_cec.h
|
||||
*
|
||||
* Samsung S5P HDMI CEC driver
|
||||
*
|
||||
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef _S5P_CEC_H_
|
||||
#define _S5P_CEC_H_ __FILE__
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <media/cec.h>
|
||||
|
||||
#include "exynos_hdmi_cec.h"
|
||||
#include "regs-cec.h"
|
||||
#include "s5p_cec.h"
|
||||
|
||||
#define CEC_NAME "s5p-cec"
|
||||
|
||||
#define CEC_STATUS_TX_RUNNING (1 << 0)
|
||||
#define CEC_STATUS_TX_TRANSFERRING (1 << 1)
|
||||
#define CEC_STATUS_TX_DONE (1 << 2)
|
||||
#define CEC_STATUS_TX_ERROR (1 << 3)
|
||||
#define CEC_STATUS_TX_BYTES (0xFF << 8)
|
||||
#define CEC_STATUS_RX_RUNNING (1 << 16)
|
||||
#define CEC_STATUS_RX_RECEIVING (1 << 17)
|
||||
#define CEC_STATUS_RX_DONE (1 << 18)
|
||||
#define CEC_STATUS_RX_ERROR (1 << 19)
|
||||
#define CEC_STATUS_RX_BCAST (1 << 20)
|
||||
#define CEC_STATUS_RX_BYTES (0xFF << 24)
|
||||
|
||||
#define CEC_WORKER_TX_DONE (1 << 0)
|
||||
#define CEC_WORKER_RX_MSG (1 << 1)
|
||||
|
||||
/* CEC Rx buffer size */
|
||||
#define CEC_RX_BUFF_SIZE 16
|
||||
/* CEC Tx buffer size */
|
||||
#define CEC_TX_BUFF_SIZE 16
|
||||
|
||||
enum cec_state {
|
||||
STATE_IDLE,
|
||||
STATE_BUSY,
|
||||
STATE_DONE,
|
||||
STATE_ERROR
|
||||
};
|
||||
|
||||
struct s5p_cec_dev {
|
||||
struct cec_adapter *adap;
|
||||
struct clk *clk;
|
||||
struct device *dev;
|
||||
struct mutex lock;
|
||||
struct regmap *pmu;
|
||||
int irq;
|
||||
void __iomem *reg;
|
||||
|
||||
enum cec_state rx;
|
||||
enum cec_state tx;
|
||||
struct cec_msg msg;
|
||||
};
|
||||
|
||||
#endif /* _S5P_CEC_H_ */
|
|
@ -57,6 +57,7 @@
|
|||
#include <linux/i2c-dev.h>
|
||||
#include <linux/atalk.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/cec.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
|
@ -1377,6 +1378,17 @@ COMPATIBLE_IOCTL(VIDEO_GET_NAVI)
|
|||
COMPATIBLE_IOCTL(VIDEO_SET_ATTRIBUTES)
|
||||
COMPATIBLE_IOCTL(VIDEO_GET_SIZE)
|
||||
COMPATIBLE_IOCTL(VIDEO_GET_FRAME_RATE)
|
||||
/* cec */
|
||||
COMPATIBLE_IOCTL(CEC_ADAP_G_CAPS)
|
||||
COMPATIBLE_IOCTL(CEC_ADAP_G_LOG_ADDRS)
|
||||
COMPATIBLE_IOCTL(CEC_ADAP_S_LOG_ADDRS)
|
||||
COMPATIBLE_IOCTL(CEC_ADAP_G_PHYS_ADDR)
|
||||
COMPATIBLE_IOCTL(CEC_ADAP_S_PHYS_ADDR)
|
||||
COMPATIBLE_IOCTL(CEC_G_MODE)
|
||||
COMPATIBLE_IOCTL(CEC_S_MODE)
|
||||
COMPATIBLE_IOCTL(CEC_TRANSMIT)
|
||||
COMPATIBLE_IOCTL(CEC_RECEIVE)
|
||||
COMPATIBLE_IOCTL(CEC_DQEVENT)
|
||||
|
||||
/* joystick */
|
||||
COMPATIBLE_IOCTL(JSIOCGVERSION)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,993 @@
|
|||
/*
|
||||
* cec - HDMI Consumer Electronics Control public header
|
||||
*
|
||||
* Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
||||
*
|
||||
* This program is free software; you may redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Note: this framework is still in staging and it is likely the API
|
||||
* will change before it goes out of staging.
|
||||
*
|
||||
* Once it is moved out of staging this header will move to uapi.
|
||||
*/
|
||||
#ifndef _CEC_UAPI_H
|
||||
#define _CEC_UAPI_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define CEC_MAX_MSG_SIZE 16
|
||||
|
||||
/**
|
||||
* struct cec_msg - CEC message structure.
|
||||
* @ts: Timestamp in nanoseconds using CLOCK_MONOTONIC. Set by the
|
||||
* driver. It is set when the message transmission has finished
|
||||
* and it is set when a message was received.
|
||||
* @len: Length in bytes of the message.
|
||||
* @timeout: The timeout (in ms) that is used to timeout CEC_RECEIVE.
|
||||
* Set to 0 if you want to wait forever. This timeout can also be
|
||||
* used with CEC_TRANSMIT as the timeout for waiting for a reply.
|
||||
* If 0, then it will use a 1 second timeout instead of waiting
|
||||
* forever as is done with CEC_RECEIVE.
|
||||
* @sequence: The framework assigns a sequence number to messages that are
|
||||
* sent. This can be used to track replies to previously sent
|
||||
* messages.
|
||||
* @flags: Set to 0.
|
||||
* @rx_status: The message receive status bits. Set by the driver.
|
||||
* @tx_status: The message transmit status bits. Set by the driver.
|
||||
* @msg: The message payload.
|
||||
* @reply: This field is ignored with CEC_RECEIVE and is only used by
|
||||
* CEC_TRANSMIT. If non-zero, then wait for a reply with this
|
||||
* opcode. Set to CEC_MSG_FEATURE_ABORT if you want to wait for
|
||||
* a possible ABORT reply. If there was an error when sending the
|
||||
* msg or FeatureAbort was returned, then reply is set to 0.
|
||||
* If reply is non-zero upon return, then len/msg are set to
|
||||
* the received message.
|
||||
* If reply is zero upon return and status has the
|
||||
* CEC_TX_STATUS_FEATURE_ABORT bit set, then len/msg are set to
|
||||
* the received feature abort message.
|
||||
* If reply is zero upon return and status has the
|
||||
* CEC_TX_STATUS_MAX_RETRIES bit set, then no reply was seen at
|
||||
* all. If reply is non-zero for CEC_TRANSMIT and the message is a
|
||||
* broadcast, then -EINVAL is returned.
|
||||
* if reply is non-zero, then timeout is set to 1000 (the required
|
||||
* maximum response time).
|
||||
* @tx_arb_lost_cnt: The number of 'Arbitration Lost' events. Set by the driver.
|
||||
* @tx_nack_cnt: The number of 'Not Acknowledged' events. Set by the driver.
|
||||
* @tx_low_drive_cnt: The number of 'Low Drive Detected' events. Set by the
|
||||
* driver.
|
||||
* @tx_error_cnt: The number of 'Error' events. Set by the driver.
|
||||
*/
|
||||
struct cec_msg {
|
||||
__u64 ts;
|
||||
__u32 len;
|
||||
__u32 timeout;
|
||||
__u32 sequence;
|
||||
__u32 flags;
|
||||
__u8 rx_status;
|
||||
__u8 tx_status;
|
||||
__u8 msg[CEC_MAX_MSG_SIZE];
|
||||
__u8 reply;
|
||||
__u8 tx_arb_lost_cnt;
|
||||
__u8 tx_nack_cnt;
|
||||
__u8 tx_low_drive_cnt;
|
||||
__u8 tx_error_cnt;
|
||||
};
|
||||
|
||||
/**
|
||||
* cec_msg_initiator - return the initiator's logical address.
|
||||
* @msg: the message structure
|
||||
*/
|
||||
static inline __u8 cec_msg_initiator(const struct cec_msg *msg)
|
||||
{
|
||||
return msg->msg[0] >> 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* cec_msg_destination - return the destination's logical address.
|
||||
* @msg: the message structure
|
||||
*/
|
||||
static inline __u8 cec_msg_destination(const struct cec_msg *msg)
|
||||
{
|
||||
return msg->msg[0] & 0xf;
|
||||
}
|
||||
|
||||
/**
|
||||
* cec_msg_opcode - return the opcode of the message, -1 for poll
|
||||
* @msg: the message structure
|
||||
*/
|
||||
static inline int cec_msg_opcode(const struct cec_msg *msg)
|
||||
{
|
||||
return msg->len > 1 ? msg->msg[1] : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* cec_msg_is_broadcast - return true if this is a broadcast message.
|
||||
* @msg: the message structure
|
||||
*/
|
||||
static inline bool cec_msg_is_broadcast(const struct cec_msg *msg)
|
||||
{
|
||||
return (msg->msg[0] & 0xf) == 0xf;
|
||||
}
|
||||
|
||||
/**
|
||||
* cec_msg_init - initialize the message structure.
|
||||
* @msg: the message structure
|
||||
* @initiator: the logical address of the initiator
|
||||
* @destination:the logical address of the destination (0xf for broadcast)
|
||||
*
|
||||
* The whole structure is zeroed, the len field is set to 1 (i.e. a poll
|
||||
* message) and the initiator and destination are filled in.
|
||||
*/
|
||||
static inline void cec_msg_init(struct cec_msg *msg,
|
||||
__u8 initiator, __u8 destination)
|
||||
{
|
||||
memset(msg, 0, sizeof(*msg));
|
||||
msg->msg[0] = (initiator << 4) | destination;
|
||||
msg->len = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* cec_msg_set_reply_to - fill in destination/initiator in a reply message.
|
||||
* @msg: the message structure for the reply
|
||||
* @orig: the original message structure
|
||||
*
|
||||
* Set the msg destination to the orig initiator and the msg initiator to the
|
||||
* orig destination. Note that msg and orig may be the same pointer, in which
|
||||
* case the change is done in place.
|
||||
*/
|
||||
static inline void cec_msg_set_reply_to(struct cec_msg *msg,
|
||||
struct cec_msg *orig)
|
||||
{
|
||||
/* The destination becomes the initiator and vice versa */
|
||||
msg->msg[0] = (cec_msg_destination(orig) << 4) |
|
||||
cec_msg_initiator(orig);
|
||||
msg->reply = msg->timeout = 0;
|
||||
}
|
||||
|
||||
/* cec status field */
|
||||
#define CEC_TX_STATUS_OK (1 << 0)
|
||||
#define CEC_TX_STATUS_ARB_LOST (1 << 1)
|
||||
#define CEC_TX_STATUS_NACK (1 << 2)
|
||||
#define CEC_TX_STATUS_LOW_DRIVE (1 << 3)
|
||||
#define CEC_TX_STATUS_ERROR (1 << 4)
|
||||
#define CEC_TX_STATUS_MAX_RETRIES (1 << 5)
|
||||
|
||||
#define CEC_RX_STATUS_OK (1 << 0)
|
||||
#define CEC_RX_STATUS_TIMEOUT (1 << 1)
|
||||
#define CEC_RX_STATUS_FEATURE_ABORT (1 << 2)
|
||||
|
||||
static inline bool cec_msg_status_is_ok(const struct cec_msg *msg)
|
||||
{
|
||||
if (msg->tx_status && !(msg->tx_status & CEC_TX_STATUS_OK))
|
||||
return false;
|
||||
if (msg->rx_status && !(msg->rx_status & CEC_RX_STATUS_OK))
|
||||
return false;
|
||||
if (!msg->tx_status && !msg->rx_status)
|
||||
return false;
|
||||
return !(msg->rx_status & CEC_RX_STATUS_FEATURE_ABORT);
|
||||
}
|
||||
|
||||
#define CEC_LOG_ADDR_INVALID 0xff
|
||||
#define CEC_PHYS_ADDR_INVALID 0xffff
|
||||
|
||||
/*
|
||||
* The maximum number of logical addresses one device can be assigned to.
|
||||
* The CEC 2.0 spec allows for only 2 logical addresses at the moment. The
|
||||
* Analog Devices CEC hardware supports 3. So let's go wild and go for 4.
|
||||
*/
|
||||
#define CEC_MAX_LOG_ADDRS 4
|
||||
|
||||
/* The logical addresses defined by CEC 2.0 */
|
||||
#define CEC_LOG_ADDR_TV 0
|
||||
#define CEC_LOG_ADDR_RECORD_1 1
|
||||
#define CEC_LOG_ADDR_RECORD_2 2
|
||||
#define CEC_LOG_ADDR_TUNER_1 3
|
||||
#define CEC_LOG_ADDR_PLAYBACK_1 4
|
||||
#define CEC_LOG_ADDR_AUDIOSYSTEM 5
|
||||
#define CEC_LOG_ADDR_TUNER_2 6
|
||||
#define CEC_LOG_ADDR_TUNER_3 7
|
||||
#define CEC_LOG_ADDR_PLAYBACK_2 8
|
||||
#define CEC_LOG_ADDR_RECORD_3 9
|
||||
#define CEC_LOG_ADDR_TUNER_4 10
|
||||
#define CEC_LOG_ADDR_PLAYBACK_3 11
|
||||
#define CEC_LOG_ADDR_BACKUP_1 12
|
||||
#define CEC_LOG_ADDR_BACKUP_2 13
|
||||
#define CEC_LOG_ADDR_SPECIFIC 14
|
||||
#define CEC_LOG_ADDR_UNREGISTERED 15 /* as initiator address */
|
||||
#define CEC_LOG_ADDR_BROADCAST 15 /* ad destination address */
|
||||
|
||||
/* The logical address types that the CEC device wants to claim */
|
||||
#define CEC_LOG_ADDR_TYPE_TV 0
|
||||
#define CEC_LOG_ADDR_TYPE_RECORD 1
|
||||
#define CEC_LOG_ADDR_TYPE_TUNER 2
|
||||
#define CEC_LOG_ADDR_TYPE_PLAYBACK 3
|
||||
#define CEC_LOG_ADDR_TYPE_AUDIOSYSTEM 4
|
||||
#define CEC_LOG_ADDR_TYPE_SPECIFIC 5
|
||||
#define CEC_LOG_ADDR_TYPE_UNREGISTERED 6
|
||||
/*
|
||||
* Switches should use UNREGISTERED.
|
||||
* Processors should use SPECIFIC.
|
||||
*/
|
||||
|
||||
#define CEC_LOG_ADDR_MASK_TV (1 << CEC_LOG_ADDR_TV)
|
||||
#define CEC_LOG_ADDR_MASK_RECORD ((1 << CEC_LOG_ADDR_RECORD_1) | \
|
||||
(1 << CEC_LOG_ADDR_RECORD_2) | \
|
||||
(1 << CEC_LOG_ADDR_RECORD_3))
|
||||
#define CEC_LOG_ADDR_MASK_TUNER ((1 << CEC_LOG_ADDR_TUNER_1) | \
|
||||
(1 << CEC_LOG_ADDR_TUNER_2) | \
|
||||
(1 << CEC_LOG_ADDR_TUNER_3) | \
|
||||
(1 << CEC_LOG_ADDR_TUNER_4))
|
||||
#define CEC_LOG_ADDR_MASK_PLAYBACK ((1 << CEC_LOG_ADDR_PLAYBACK_1) | \
|
||||
(1 << CEC_LOG_ADDR_PLAYBACK_2) | \
|
||||
(1 << CEC_LOG_ADDR_PLAYBACK_3))
|
||||
#define CEC_LOG_ADDR_MASK_AUDIOSYSTEM (1 << CEC_LOG_ADDR_AUDIOSYSTEM)
|
||||
#define CEC_LOG_ADDR_MASK_BACKUP ((1 << CEC_LOG_ADDR_BACKUP_1) | \
|
||||
(1 << CEC_LOG_ADDR_BACKUP_2))
|
||||
#define CEC_LOG_ADDR_MASK_SPECIFIC (1 << CEC_LOG_ADDR_SPECIFIC)
|
||||
#define CEC_LOG_ADDR_MASK_UNREGISTERED (1 << CEC_LOG_ADDR_UNREGISTERED)
|
||||
|
||||
static inline bool cec_has_tv(__u16 log_addr_mask)
|
||||
{
|
||||
return log_addr_mask & CEC_LOG_ADDR_MASK_TV;
|
||||
}
|
||||
|
||||
static inline bool cec_has_record(__u16 log_addr_mask)
|
||||
{
|
||||
return log_addr_mask & CEC_LOG_ADDR_MASK_RECORD;
|
||||
}
|
||||
|
||||
static inline bool cec_has_tuner(__u16 log_addr_mask)
|
||||
{
|
||||
return log_addr_mask & CEC_LOG_ADDR_MASK_TUNER;
|
||||
}
|
||||
|
||||
static inline bool cec_has_playback(__u16 log_addr_mask)
|
||||
{
|
||||
return log_addr_mask & CEC_LOG_ADDR_MASK_PLAYBACK;
|
||||
}
|
||||
|
||||
static inline bool cec_has_audiosystem(__u16 log_addr_mask)
|
||||
{
|
||||
return log_addr_mask & CEC_LOG_ADDR_MASK_AUDIOSYSTEM;
|
||||
}
|
||||
|
||||
static inline bool cec_has_backup(__u16 log_addr_mask)
|
||||
{
|
||||
return log_addr_mask & CEC_LOG_ADDR_MASK_BACKUP;
|
||||
}
|
||||
|
||||
static inline bool cec_has_specific(__u16 log_addr_mask)
|
||||
{
|
||||
return log_addr_mask & CEC_LOG_ADDR_MASK_SPECIFIC;
|
||||
}
|
||||
|
||||
static inline bool cec_is_unregistered(__u16 log_addr_mask)
|
||||
{
|
||||
return log_addr_mask & CEC_LOG_ADDR_MASK_UNREGISTERED;
|
||||
}
|
||||
|
||||
static inline bool cec_is_unconfigured(__u16 log_addr_mask)
|
||||
{
|
||||
return log_addr_mask == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Use this if there is no vendor ID (CEC_G_VENDOR_ID) or if the vendor ID
|
||||
* should be disabled (CEC_S_VENDOR_ID)
|
||||
*/
|
||||
#define CEC_VENDOR_ID_NONE 0xffffffff
|
||||
|
||||
/* The message handling modes */
|
||||
/* Modes for initiator */
|
||||
#define CEC_MODE_NO_INITIATOR (0x0 << 0)
|
||||
#define CEC_MODE_INITIATOR (0x1 << 0)
|
||||
#define CEC_MODE_EXCL_INITIATOR (0x2 << 0)
|
||||
#define CEC_MODE_INITIATOR_MSK 0x0f
|
||||
|
||||
/* Modes for follower */
|
||||
#define CEC_MODE_NO_FOLLOWER (0x0 << 4)
|
||||
#define CEC_MODE_FOLLOWER (0x1 << 4)
|
||||
#define CEC_MODE_EXCL_FOLLOWER (0x2 << 4)
|
||||
#define CEC_MODE_EXCL_FOLLOWER_PASSTHRU (0x3 << 4)
|
||||
#define CEC_MODE_MONITOR (0xe << 4)
|
||||
#define CEC_MODE_MONITOR_ALL (0xf << 4)
|
||||
#define CEC_MODE_FOLLOWER_MSK 0xf0
|
||||
|
||||
/* Userspace has to configure the physical address */
|
||||
#define CEC_CAP_PHYS_ADDR (1 << 0)
|
||||
/* Userspace has to configure the logical addresses */
|
||||
#define CEC_CAP_LOG_ADDRS (1 << 1)
|
||||
/* Userspace can transmit messages (and thus become follower as well) */
|
||||
#define CEC_CAP_TRANSMIT (1 << 2)
|
||||
/*
|
||||
* Passthrough all messages instead of processing them.
|
||||
*/
|
||||
#define CEC_CAP_PASSTHROUGH (1 << 3)
|
||||
/* Supports remote control */
|
||||
#define CEC_CAP_RC (1 << 4)
|
||||
/* Hardware can monitor all messages, not just directed and broadcast. */
|
||||
#define CEC_CAP_MONITOR_ALL (1 << 5)
|
||||
|
||||
/**
|
||||
* struct cec_caps - CEC capabilities structure.
|
||||
* @driver: name of the CEC device driver.
|
||||
* @name: name of the CEC device. @driver + @name must be unique.
|
||||
* @available_log_addrs: number of available logical addresses.
|
||||
* @capabilities: capabilities of the CEC adapter.
|
||||
* @version: version of the CEC adapter framework.
|
||||
*/
|
||||
struct cec_caps {
|
||||
char driver[32];
|
||||
char name[32];
|
||||
__u32 available_log_addrs;
|
||||
__u32 capabilities;
|
||||
__u32 version;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cec_log_addrs - CEC logical addresses structure.
|
||||
* @log_addr: the claimed logical addresses. Set by the driver.
|
||||
* @log_addr_mask: current logical address mask. Set by the driver.
|
||||
* @cec_version: the CEC version that the adapter should implement. Set by the
|
||||
* caller.
|
||||
* @num_log_addrs: how many logical addresses should be claimed. Set by the
|
||||
* caller.
|
||||
* @vendor_id: the vendor ID of the device. Set by the caller.
|
||||
* @flags: set to 0.
|
||||
* @osd_name: the OSD name of the device. Set by the caller.
|
||||
* @primary_device_type: the primary device type for each logical address.
|
||||
* Set by the caller.
|
||||
* @log_addr_type: the logical address types. Set by the caller.
|
||||
* @all_device_types: CEC 2.0: all device types represented by the logical
|
||||
* address. Set by the caller.
|
||||
* @features: CEC 2.0: The logical address features. Set by the caller.
|
||||
*/
|
||||
struct cec_log_addrs {
|
||||
__u8 log_addr[CEC_MAX_LOG_ADDRS];
|
||||
__u16 log_addr_mask;
|
||||
__u8 cec_version;
|
||||
__u8 num_log_addrs;
|
||||
__u32 vendor_id;
|
||||
__u32 flags;
|
||||
char osd_name[15];
|
||||
__u8 primary_device_type[CEC_MAX_LOG_ADDRS];
|
||||
__u8 log_addr_type[CEC_MAX_LOG_ADDRS];
|
||||
|
||||
/* CEC 2.0 */
|
||||
__u8 all_device_types[CEC_MAX_LOG_ADDRS];
|
||||
__u8 features[CEC_MAX_LOG_ADDRS][12];
|
||||
};
|
||||
|
||||
/* Events */
|
||||
|
||||
/* Event that occurs when the adapter state changes */
|
||||
#define CEC_EVENT_STATE_CHANGE 1
|
||||
/*
|
||||
* This event is sent when messages are lost because the application
|
||||
* didn't empty the message queue in time
|
||||
*/
|
||||
#define CEC_EVENT_LOST_MSGS 2
|
||||
|
||||
#define CEC_EVENT_FL_INITIAL_STATE (1 << 0)
|
||||
|
||||
/**
|
||||
* struct cec_event_state_change - used when the CEC adapter changes state.
|
||||
* @phys_addr: the current physical address
|
||||
* @log_addr_mask: the current logical address mask
|
||||
*/
|
||||
struct cec_event_state_change {
|
||||
__u16 phys_addr;
|
||||
__u16 log_addr_mask;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cec_event_lost_msgs - tells you how many messages were lost due.
|
||||
* @lost_msgs: how many messages were lost.
|
||||
*/
|
||||
struct cec_event_lost_msgs {
|
||||
__u32 lost_msgs;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cec_event - CEC event structure
|
||||
* @ts: the timestamp of when the event was sent.
|
||||
* @event: the event.
|
||||
* array.
|
||||
* @state_change: the event payload for CEC_EVENT_STATE_CHANGE.
|
||||
* @lost_msgs: the event payload for CEC_EVENT_LOST_MSGS.
|
||||
* @raw: array to pad the union.
|
||||
*/
|
||||
struct cec_event {
|
||||
__u64 ts;
|
||||
__u32 event;
|
||||
__u32 flags;
|
||||
union {
|
||||
struct cec_event_state_change state_change;
|
||||
struct cec_event_lost_msgs lost_msgs;
|
||||
__u32 raw[16];
|
||||
};
|
||||
};
|
||||
|
||||
/* ioctls */
|
||||
|
||||
/* Adapter capabilities */
|
||||
#define CEC_ADAP_G_CAPS _IOWR('a', 0, struct cec_caps)
|
||||
|
||||
/*
|
||||
* phys_addr is either 0 (if this is the CEC root device)
|
||||
* or a valid physical address obtained from the sink's EDID
|
||||
* as read by this CEC device (if this is a source device)
|
||||
* or a physical address obtained and modified from a sink
|
||||
* EDID and used for a sink CEC device.
|
||||
* If nothing is connected, then phys_addr is 0xffff.
|
||||
* See HDMI 1.4b, section 8.7 (Physical Address).
|
||||
*
|
||||
* The CEC_ADAP_S_PHYS_ADDR ioctl may not be available if that is handled
|
||||
* internally.
|
||||
*/
|
||||
#define CEC_ADAP_G_PHYS_ADDR _IOR('a', 1, __u16)
|
||||
#define CEC_ADAP_S_PHYS_ADDR _IOW('a', 2, __u16)
|
||||
|
||||
/*
|
||||
* Configure the CEC adapter. It sets the device type and which
|
||||
* logical types it will try to claim. It will return which
|
||||
* logical addresses it could actually claim.
|
||||
* An error is returned if the adapter is disabled or if there
|
||||
* is no physical address assigned.
|
||||
*/
|
||||
|
||||
#define CEC_ADAP_G_LOG_ADDRS _IOR('a', 3, struct cec_log_addrs)
|
||||
#define CEC_ADAP_S_LOG_ADDRS _IOWR('a', 4, struct cec_log_addrs)
|
||||
|
||||
/* Transmit/receive a CEC command */
|
||||
#define CEC_TRANSMIT _IOWR('a', 5, struct cec_msg)
|
||||
#define CEC_RECEIVE _IOWR('a', 6, struct cec_msg)
|
||||
|
||||
/* Dequeue CEC events */
|
||||
#define CEC_DQEVENT _IOWR('a', 7, struct cec_event)
|
||||
|
||||
/*
|
||||
* Get and set the message handling mode for this filehandle.
|
||||
*/
|
||||
#define CEC_G_MODE _IOR('a', 8, __u32)
|
||||
#define CEC_S_MODE _IOW('a', 9, __u32)
|
||||
|
||||
/*
|
||||
* The remainder of this header defines all CEC messages and operands.
|
||||
* The format matters since it the cec-ctl utility parses it to generate
|
||||
* code for implementing all these messages.
|
||||
*
|
||||
* Comments ending with 'Feature' group messages for each feature.
|
||||
* If messages are part of multiple features, then the "Has also"
|
||||
* comment is used to list the previously defined messages that are
|
||||
* supported by the feature.
|
||||
*
|
||||
* Before operands are defined a comment is added that gives the
|
||||
* name of the operand and in brackets the variable name of the
|
||||
* corresponding argument in the cec-funcs.h function.
|
||||
*/
|
||||
|
||||
/* Messages */
|
||||
|
||||
/* One Touch Play Feature */
|
||||
#define CEC_MSG_ACTIVE_SOURCE 0x82
|
||||
#define CEC_MSG_IMAGE_VIEW_ON 0x04
|
||||
#define CEC_MSG_TEXT_VIEW_ON 0x0d
|
||||
|
||||
|
||||
/* Routing Control Feature */
|
||||
|
||||
/*
|
||||
* Has also:
|
||||
* CEC_MSG_ACTIVE_SOURCE
|
||||
*/
|
||||
|
||||
#define CEC_MSG_INACTIVE_SOURCE 0x9d
|
||||
#define CEC_MSG_REQUEST_ACTIVE_SOURCE 0x85
|
||||
#define CEC_MSG_ROUTING_CHANGE 0x80
|
||||
#define CEC_MSG_ROUTING_INFORMATION 0x81
|
||||
#define CEC_MSG_SET_STREAM_PATH 0x86
|
||||
|
||||
|
||||
/* Standby Feature */
|
||||
#define CEC_MSG_STANDBY 0x36
|
||||
|
||||
|
||||
/* One Touch Record Feature */
|
||||
#define CEC_MSG_RECORD_OFF 0x0b
|
||||
#define CEC_MSG_RECORD_ON 0x09
|
||||
/* Record Source Type Operand (rec_src_type) */
|
||||
#define CEC_OP_RECORD_SRC_OWN 1
|
||||
#define CEC_OP_RECORD_SRC_DIGITAL 2
|
||||
#define CEC_OP_RECORD_SRC_ANALOG 3
|
||||
#define CEC_OP_RECORD_SRC_EXT_PLUG 4
|
||||
#define CEC_OP_RECORD_SRC_EXT_PHYS_ADDR 5
|
||||
/* Service Identification Method Operand (service_id_method) */
|
||||
#define CEC_OP_SERVICE_ID_METHOD_BY_DIG_ID 0
|
||||
#define CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL 1
|
||||
/* Digital Service Broadcast System Operand (dig_bcast_system) */
|
||||
#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_GEN 0x00
|
||||
#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_GEN 0x01
|
||||
#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_GEN 0x02
|
||||
#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_BS 0x08
|
||||
#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_CS 0x09
|
||||
#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_T 0x0a
|
||||
#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_CABLE 0x10
|
||||
#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_SAT 0x11
|
||||
#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_T 0x12
|
||||
#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_C 0x18
|
||||
#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S 0x19
|
||||
#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S2 0x1a
|
||||
#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_T 0x1b
|
||||
/* Analogue Broadcast Type Operand (ana_bcast_type) */
|
||||
#define CEC_OP_ANA_BCAST_TYPE_CABLE 0
|
||||
#define CEC_OP_ANA_BCAST_TYPE_SATELLITE 1
|
||||
#define CEC_OP_ANA_BCAST_TYPE_TERRESTRIAL 2
|
||||
/* Broadcast System Operand (bcast_system) */
|
||||
#define CEC_OP_BCAST_SYSTEM_PAL_BG 0x00
|
||||
#define CEC_OP_BCAST_SYSTEM_SECAM_LQ 0x01 /* SECAM L' */
|
||||
#define CEC_OP_BCAST_SYSTEM_PAL_M 0x02
|
||||
#define CEC_OP_BCAST_SYSTEM_NTSC_M 0x03
|
||||
#define CEC_OP_BCAST_SYSTEM_PAL_I 0x04
|
||||
#define CEC_OP_BCAST_SYSTEM_SECAM_DK 0x05
|
||||
#define CEC_OP_BCAST_SYSTEM_SECAM_BG 0x06
|
||||
#define CEC_OP_BCAST_SYSTEM_SECAM_L 0x07
|
||||
#define CEC_OP_BCAST_SYSTEM_PAL_DK 0x08
|
||||
#define CEC_OP_BCAST_SYSTEM_OTHER 0x1f
|
||||
/* Channel Number Format Operand (channel_number_fmt) */
|
||||
#define CEC_OP_CHANNEL_NUMBER_FMT_1_PART 0x01
|
||||
#define CEC_OP_CHANNEL_NUMBER_FMT_2_PART 0x02
|
||||
|
||||
#define CEC_MSG_RECORD_STATUS 0x0a
|
||||
/* Record Status Operand (rec_status) */
|
||||
#define CEC_OP_RECORD_STATUS_CUR_SRC 0x01
|
||||
#define CEC_OP_RECORD_STATUS_DIG_SERVICE 0x02
|
||||
#define CEC_OP_RECORD_STATUS_ANA_SERVICE 0x03
|
||||
#define CEC_OP_RECORD_STATUS_EXT_INPUT 0x04
|
||||
#define CEC_OP_RECORD_STATUS_NO_DIG_SERVICE 0x05
|
||||
#define CEC_OP_RECORD_STATUS_NO_ANA_SERVICE 0x06
|
||||
#define CEC_OP_RECORD_STATUS_NO_SERVICE 0x07
|
||||
#define CEC_OP_RECORD_STATUS_INVALID_EXT_PLUG 0x09
|
||||
#define CEC_OP_RECORD_STATUS_INVALID_EXT_PHYS_ADDR 0x0a
|
||||
#define CEC_OP_RECORD_STATUS_UNSUP_CA 0x0b
|
||||
#define CEC_OP_RECORD_STATUS_NO_CA_ENTITLEMENTS 0x0c
|
||||
#define CEC_OP_RECORD_STATUS_CANT_COPY_SRC 0x0d
|
||||
#define CEC_OP_RECORD_STATUS_NO_MORE_COPIES 0x0e
|
||||
#define CEC_OP_RECORD_STATUS_NO_MEDIA 0x10
|
||||
#define CEC_OP_RECORD_STATUS_PLAYING 0x11
|
||||
#define CEC_OP_RECORD_STATUS_ALREADY_RECORDING 0x12
|
||||
#define CEC_OP_RECORD_STATUS_MEDIA_PROT 0x13
|
||||
#define CEC_OP_RECORD_STATUS_NO_SIGNAL 0x14
|
||||
#define CEC_OP_RECORD_STATUS_MEDIA_PROBLEM 0x15
|
||||
#define CEC_OP_RECORD_STATUS_NO_SPACE 0x16
|
||||
#define CEC_OP_RECORD_STATUS_PARENTAL_LOCK 0x17
|
||||
#define CEC_OP_RECORD_STATUS_TERMINATED_OK 0x1a
|
||||
#define CEC_OP_RECORD_STATUS_ALREADY_TERM 0x1b
|
||||
#define CEC_OP_RECORD_STATUS_OTHER 0x1f
|
||||
|
||||
#define CEC_MSG_RECORD_TV_SCREEN 0x0f
|
||||
|
||||
|
||||
/* Timer Programming Feature */
|
||||
#define CEC_MSG_CLEAR_ANALOGUE_TIMER 0x33
|
||||
/* Recording Sequence Operand (recording_seq) */
|
||||
#define CEC_OP_REC_SEQ_SUNDAY 0x01
|
||||
#define CEC_OP_REC_SEQ_MONDAY 0x02
|
||||
#define CEC_OP_REC_SEQ_TUESDAY 0x04
|
||||
#define CEC_OP_REC_SEQ_WEDNESDAY 0x08
|
||||
#define CEC_OP_REC_SEQ_THURSDAY 0x10
|
||||
#define CEC_OP_REC_SEQ_FRIDAY 0x20
|
||||
#define CEC_OP_REC_SEQ_SATERDAY 0x40
|
||||
#define CEC_OP_REC_SEQ_ONCE_ONLY 0x00
|
||||
|
||||
#define CEC_MSG_CLEAR_DIGITAL_TIMER 0x99
|
||||
|
||||
#define CEC_MSG_CLEAR_EXT_TIMER 0xa1
|
||||
/* External Source Specifier Operand (ext_src_spec) */
|
||||
#define CEC_OP_EXT_SRC_PLUG 0x04
|
||||
#define CEC_OP_EXT_SRC_PHYS_ADDR 0x05
|
||||
|
||||
#define CEC_MSG_SET_ANALOGUE_TIMER 0x34
|
||||
#define CEC_MSG_SET_DIGITAL_TIMER 0x97
|
||||
#define CEC_MSG_SET_EXT_TIMER 0xa2
|
||||
|
||||
#define CEC_MSG_SET_TIMER_PROGRAM_TITLE 0x67
|
||||
#define CEC_MSG_TIMER_CLEARED_STATUS 0x43
|
||||
/* Timer Cleared Status Data Operand (timer_cleared_status) */
|
||||
#define CEC_OP_TIMER_CLR_STAT_RECORDING 0x00
|
||||
#define CEC_OP_TIMER_CLR_STAT_NO_MATCHING 0x01
|
||||
#define CEC_OP_TIMER_CLR_STAT_NO_INFO 0x02
|
||||
#define CEC_OP_TIMER_CLR_STAT_CLEARED 0x80
|
||||
|
||||
#define CEC_MSG_TIMER_STATUS 0x35
|
||||
/* Timer Overlap Warning Operand (timer_overlap_warning) */
|
||||
#define CEC_OP_TIMER_OVERLAP_WARNING_NO_OVERLAP 0
|
||||
#define CEC_OP_TIMER_OVERLAP_WARNING_OVERLAP 1
|
||||
/* Media Info Operand (media_info) */
|
||||
#define CEC_OP_MEDIA_INFO_UNPROT_MEDIA 0
|
||||
#define CEC_OP_MEDIA_INFO_PROT_MEDIA 1
|
||||
#define CEC_OP_MEDIA_INFO_NO_MEDIA 2
|
||||
/* Programmed Indicator Operand (prog_indicator) */
|
||||
#define CEC_OP_PROG_IND_NOT_PROGRAMMED 0
|
||||
#define CEC_OP_PROG_IND_PROGRAMMED 1
|
||||
/* Programmed Info Operand (prog_info) */
|
||||
#define CEC_OP_PROG_INFO_ENOUGH_SPACE 0x08
|
||||
#define CEC_OP_PROG_INFO_NOT_ENOUGH_SPACE 0x09
|
||||
#define CEC_OP_PROG_INFO_MIGHT_NOT_BE_ENOUGH_SPACE 0x0b
|
||||
#define CEC_OP_PROG_INFO_NONE_AVAILABLE 0x0a
|
||||
/* Not Programmed Error Info Operand (prog_error) */
|
||||
#define CEC_OP_PROG_ERROR_NO_FREE_TIMER 0x01
|
||||
#define CEC_OP_PROG_ERROR_DATE_OUT_OF_RANGE 0x02
|
||||
#define CEC_OP_PROG_ERROR_REC_SEQ_ERROR 0x03
|
||||
#define CEC_OP_PROG_ERROR_INV_EXT_PLUG 0x04
|
||||
#define CEC_OP_PROG_ERROR_INV_EXT_PHYS_ADDR 0x05
|
||||
#define CEC_OP_PROG_ERROR_CA_UNSUPP 0x06
|
||||
#define CEC_OP_PROG_ERROR_INSUF_CA_ENTITLEMENTS 0x07
|
||||
#define CEC_OP_PROG_ERROR_RESOLUTION_UNSUPP 0x08
|
||||
#define CEC_OP_PROG_ERROR_PARENTAL_LOCK 0x09
|
||||
#define CEC_OP_PROG_ERROR_CLOCK_FAILURE 0x0a
|
||||
#define CEC_OP_PROG_ERROR_DUPLICATE 0x0e
|
||||
|
||||
|
||||
/* System Information Feature */
|
||||
#define CEC_MSG_CEC_VERSION 0x9e
|
||||
/* CEC Version Operand (cec_version) */
|
||||
#define CEC_OP_CEC_VERSION_1_3A 4
|
||||
#define CEC_OP_CEC_VERSION_1_4 5
|
||||
#define CEC_OP_CEC_VERSION_2_0 6
|
||||
|
||||
#define CEC_MSG_GET_CEC_VERSION 0x9f
|
||||
#define CEC_MSG_GIVE_PHYSICAL_ADDR 0x83
|
||||
#define CEC_MSG_GET_MENU_LANGUAGE 0x91
|
||||
#define CEC_MSG_REPORT_PHYSICAL_ADDR 0x84
|
||||
/* Primary Device Type Operand (prim_devtype) */
|
||||
#define CEC_OP_PRIM_DEVTYPE_TV 0
|
||||
#define CEC_OP_PRIM_DEVTYPE_RECORD 1
|
||||
#define CEC_OP_PRIM_DEVTYPE_TUNER 3
|
||||
#define CEC_OP_PRIM_DEVTYPE_PLAYBACK 4
|
||||
#define CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM 5
|
||||
#define CEC_OP_PRIM_DEVTYPE_SWITCH 6
|
||||
#define CEC_OP_PRIM_DEVTYPE_PROCESSOR 7
|
||||
|
||||
#define CEC_MSG_SET_MENU_LANGUAGE 0x32
|
||||
#define CEC_MSG_REPORT_FEATURES 0xa6 /* HDMI 2.0 */
|
||||
/* All Device Types Operand (all_device_types) */
|
||||
#define CEC_OP_ALL_DEVTYPE_TV 0x80
|
||||
#define CEC_OP_ALL_DEVTYPE_RECORD 0x40
|
||||
#define CEC_OP_ALL_DEVTYPE_TUNER 0x20
|
||||
#define CEC_OP_ALL_DEVTYPE_PLAYBACK 0x10
|
||||
#define CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM 0x08
|
||||
#define CEC_OP_ALL_DEVTYPE_SWITCH 0x04
|
||||
/*
|
||||
* And if you wondering what happened to PROCESSOR devices: those should
|
||||
* be mapped to a SWITCH.
|
||||
*/
|
||||
|
||||
/* Valid for RC Profile and Device Feature operands */
|
||||
#define CEC_OP_FEAT_EXT 0x80 /* Extension bit */
|
||||
/* RC Profile Operand (rc_profile) */
|
||||
#define CEC_OP_FEAT_RC_TV_PROFILE_NONE 0x00
|
||||
#define CEC_OP_FEAT_RC_TV_PROFILE_1 0x02
|
||||
#define CEC_OP_FEAT_RC_TV_PROFILE_2 0x06
|
||||
#define CEC_OP_FEAT_RC_TV_PROFILE_3 0x0a
|
||||
#define CEC_OP_FEAT_RC_TV_PROFILE_4 0x0e
|
||||
#define CEC_OP_FEAT_RC_SRC_HAS_DEV_ROOT_MENU 0x50
|
||||
#define CEC_OP_FEAT_RC_SRC_HAS_DEV_SETUP_MENU 0x48
|
||||
#define CEC_OP_FEAT_RC_SRC_HAS_CONTENTS_MENU 0x44
|
||||
#define CEC_OP_FEAT_RC_SRC_HAS_MEDIA_TOP_MENU 0x42
|
||||
#define CEC_OP_FEAT_RC_SRC_HAS_MEDIA_CONTEXT_MENU 0x41
|
||||
/* Device Feature Operand (dev_features) */
|
||||
#define CEC_OP_FEAT_DEV_HAS_RECORD_TV_SCREEN 0x40
|
||||
#define CEC_OP_FEAT_DEV_HAS_SET_OSD_STRING 0x20
|
||||
#define CEC_OP_FEAT_DEV_HAS_DECK_CONTROL 0x10
|
||||
#define CEC_OP_FEAT_DEV_HAS_SET_AUDIO_RATE 0x08
|
||||
#define CEC_OP_FEAT_DEV_SINK_HAS_ARC_TX 0x04
|
||||
#define CEC_OP_FEAT_DEV_SOURCE_HAS_ARC_RX 0x02
|
||||
|
||||
#define CEC_MSG_GIVE_FEATURES 0xa5 /* HDMI 2.0 */
|
||||
|
||||
|
||||
/* Deck Control Feature */
|
||||
#define CEC_MSG_DECK_CONTROL 0x42
|
||||
/* Deck Control Mode Operand (deck_control_mode) */
|
||||
#define CEC_OP_DECK_CTL_MODE_SKIP_FWD 1
|
||||
#define CEC_OP_DECK_CTL_MODE_SKIP_REV 2
|
||||
#define CEC_OP_DECK_CTL_MODE_STOP 3
|
||||
#define CEC_OP_DECK_CTL_MODE_EJECT 4
|
||||
|
||||
#define CEC_MSG_DECK_STATUS 0x1b
|
||||
/* Deck Info Operand (deck_info) */
|
||||
#define CEC_OP_DECK_INFO_PLAY 0x11
|
||||
#define CEC_OP_DECK_INFO_RECORD 0x12
|
||||
#define CEC_OP_DECK_INFO_PLAY_REV 0x13
|
||||
#define CEC_OP_DECK_INFO_STILL 0x14
|
||||
#define CEC_OP_DECK_INFO_SLOW 0x15
|
||||
#define CEC_OP_DECK_INFO_SLOW_REV 0x16
|
||||
#define CEC_OP_DECK_INFO_FAST_FWD 0x17
|
||||
#define CEC_OP_DECK_INFO_FAST_REV 0x18
|
||||
#define CEC_OP_DECK_INFO_NO_MEDIA 0x19
|
||||
#define CEC_OP_DECK_INFO_STOP 0x1a
|
||||
#define CEC_OP_DECK_INFO_SKIP_FWD 0x1b
|
||||
#define CEC_OP_DECK_INFO_SKIP_REV 0x1c
|
||||
#define CEC_OP_DECK_INFO_INDEX_SEARCH_FWD 0x1d
|
||||
#define CEC_OP_DECK_INFO_INDEX_SEARCH_REV 0x1e
|
||||
#define CEC_OP_DECK_INFO_OTHER 0x1f
|
||||
|
||||
#define CEC_MSG_GIVE_DECK_STATUS 0x1a
|
||||
/* Status Request Operand (status_req) */
|
||||
#define CEC_OP_STATUS_REQ_ON 1
|
||||
#define CEC_OP_STATUS_REQ_OFF 2
|
||||
#define CEC_OP_STATUS_REQ_ONCE 3
|
||||
|
||||
#define CEC_MSG_PLAY 0x41
|
||||
/* Play Mode Operand (play_mode) */
|
||||
#define CEC_OP_PLAY_MODE_PLAY_FWD 0x24
|
||||
#define CEC_OP_PLAY_MODE_PLAY_REV 0x20
|
||||
#define CEC_OP_PLAY_MODE_PLAY_STILL 0x25
|
||||
#define CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MIN 0x05
|
||||
#define CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MED 0x06
|
||||
#define CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MAX 0x07
|
||||
#define CEC_OP_PLAY_MODE_PLAY_FAST_REV_MIN 0x09
|
||||
#define CEC_OP_PLAY_MODE_PLAY_FAST_REV_MED 0x0a
|
||||
#define CEC_OP_PLAY_MODE_PLAY_FAST_REV_MAX 0x0b
|
||||
#define CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MIN 0x15
|
||||
#define CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MED 0x16
|
||||
#define CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MAX 0x17
|
||||
#define CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MIN 0x19
|
||||
#define CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MED 0x1a
|
||||
#define CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MAX 0x1b
|
||||
|
||||
|
||||
/* Tuner Control Feature */
|
||||
#define CEC_MSG_GIVE_TUNER_DEVICE_STATUS 0x08
|
||||
#define CEC_MSG_SELECT_ANALOGUE_SERVICE 0x92
|
||||
#define CEC_MSG_SELECT_DIGITAL_SERVICE 0x93
|
||||
#define CEC_MSG_TUNER_DEVICE_STATUS 0x07
|
||||
/* Recording Flag Operand (rec_flag) */
|
||||
#define CEC_OP_REC_FLAG_USED 0
|
||||
#define CEC_OP_REC_FLAG_NOT_USED 1
|
||||
/* Tuner Display Info Operand (tuner_display_info) */
|
||||
#define CEC_OP_TUNER_DISPLAY_INFO_DIGITAL 0
|
||||
#define CEC_OP_TUNER_DISPLAY_INFO_NONE 1
|
||||
#define CEC_OP_TUNER_DISPLAY_INFO_ANALOGUE 2
|
||||
|
||||
#define CEC_MSG_TUNER_STEP_DECREMENT 0x06
|
||||
#define CEC_MSG_TUNER_STEP_INCREMENT 0x05
|
||||
|
||||
|
||||
/* Vendor Specific Commands Feature */
|
||||
|
||||
/*
|
||||
* Has also:
|
||||
* CEC_MSG_CEC_VERSION
|
||||
* CEC_MSG_GET_CEC_VERSION
|
||||
*/
|
||||
#define CEC_MSG_DEVICE_VENDOR_ID 0x87
|
||||
#define CEC_MSG_GIVE_DEVICE_VENDOR_ID 0x8c
|
||||
#define CEC_MSG_VENDOR_COMMAND 0x89
|
||||
#define CEC_MSG_VENDOR_COMMAND_WITH_ID 0xa0
|
||||
#define CEC_MSG_VENDOR_REMOTE_BUTTON_DOWN 0x8a
|
||||
#define CEC_MSG_VENDOR_REMOTE_BUTTON_UP 0x8b
|
||||
|
||||
|
||||
/* OSD Display Feature */
|
||||
#define CEC_MSG_SET_OSD_STRING 0x64
|
||||
/* Display Control Operand (disp_ctl) */
|
||||
#define CEC_OP_DISP_CTL_DEFAULT 0x00
|
||||
#define CEC_OP_DISP_CTL_UNTIL_CLEARED 0x40
|
||||
#define CEC_OP_DISP_CTL_CLEAR 0x80
|
||||
|
||||
|
||||
/* Device OSD Transfer Feature */
|
||||
#define CEC_MSG_GIVE_OSD_NAME 0x46
|
||||
#define CEC_MSG_SET_OSD_NAME 0x47
|
||||
|
||||
|
||||
/* Device Menu Control Feature */
|
||||
#define CEC_MSG_MENU_REQUEST 0x8d
|
||||
/* Menu Request Type Operand (menu_req) */
|
||||
#define CEC_OP_MENU_REQUEST_ACTIVATE 0x00
|
||||
#define CEC_OP_MENU_REQUEST_DEACTIVATE 0x01
|
||||
#define CEC_OP_MENU_REQUEST_QUERY 0x02
|
||||
|
||||
#define CEC_MSG_MENU_STATUS 0x8e
|
||||
/* Menu State Operand (menu_state) */
|
||||
#define CEC_OP_MENU_STATE_ACTIVATED 0x00
|
||||
#define CEC_OP_MENU_STATE_DEACTIVATED 0x01
|
||||
|
||||
#define CEC_MSG_USER_CONTROL_PRESSED 0x44
|
||||
/* UI Broadcast Type Operand (ui_bcast_type) */
|
||||
#define CEC_OP_UI_BCAST_TYPE_TOGGLE_ALL 0x00
|
||||
#define CEC_OP_UI_BCAST_TYPE_TOGGLE_DIG_ANA 0x01
|
||||
#define CEC_OP_UI_BCAST_TYPE_ANALOGUE 0x10
|
||||
#define CEC_OP_UI_BCAST_TYPE_ANALOGUE_T 0x20
|
||||
#define CEC_OP_UI_BCAST_TYPE_ANALOGUE_CABLE 0x30
|
||||
#define CEC_OP_UI_BCAST_TYPE_ANALOGUE_SAT 0x40
|
||||
#define CEC_OP_UI_BCAST_TYPE_DIGITAL 0x50
|
||||
#define CEC_OP_UI_BCAST_TYPE_DIGITAL_T 0x60
|
||||
#define CEC_OP_UI_BCAST_TYPE_DIGITAL_CABLE 0x70
|
||||
#define CEC_OP_UI_BCAST_TYPE_DIGITAL_SAT 0x80
|
||||
#define CEC_OP_UI_BCAST_TYPE_DIGITAL_COM_SAT 0x90
|
||||
#define CEC_OP_UI_BCAST_TYPE_DIGITAL_COM_SAT2 0x91
|
||||
#define CEC_OP_UI_BCAST_TYPE_IP 0xa0
|
||||
/* UI Sound Presentation Control Operand (ui_snd_pres_ctl) */
|
||||
#define CEC_OP_UI_SND_PRES_CTL_DUAL_MONO 0x10
|
||||
#define CEC_OP_UI_SND_PRES_CTL_KARAOKE 0x20
|
||||
#define CEC_OP_UI_SND_PRES_CTL_DOWNMIX 0x80
|
||||
#define CEC_OP_UI_SND_PRES_CTL_REVERB 0x90
|
||||
#define CEC_OP_UI_SND_PRES_CTL_EQUALIZER 0xa0
|
||||
#define CEC_OP_UI_SND_PRES_CTL_BASS_UP 0xb1
|
||||
#define CEC_OP_UI_SND_PRES_CTL_BASS_NEUTRAL 0xb2
|
||||
#define CEC_OP_UI_SND_PRES_CTL_BASS_DOWN 0xb3
|
||||
#define CEC_OP_UI_SND_PRES_CTL_TREBLE_UP 0xc1
|
||||
#define CEC_OP_UI_SND_PRES_CTL_TREBLE_NEUTRAL 0xc2
|
||||
#define CEC_OP_UI_SND_PRES_CTL_TREBLE_DOWN 0xc3
|
||||
|
||||
#define CEC_MSG_USER_CONTROL_RELEASED 0x45
|
||||
|
||||
|
||||
/* Remote Control Passthrough Feature */
|
||||
|
||||
/*
|
||||
* Has also:
|
||||
* CEC_MSG_USER_CONTROL_PRESSED
|
||||
* CEC_MSG_USER_CONTROL_RELEASED
|
||||
*/
|
||||
|
||||
|
||||
/* Power Status Feature */
|
||||
#define CEC_MSG_GIVE_DEVICE_POWER_STATUS 0x8f
|
||||
#define CEC_MSG_REPORT_POWER_STATUS 0x90
|
||||
/* Power Status Operand (pwr_state) */
|
||||
#define CEC_OP_POWER_STATUS_ON 0
|
||||
#define CEC_OP_POWER_STATUS_STANDBY 1
|
||||
#define CEC_OP_POWER_STATUS_TO_ON 2
|
||||
#define CEC_OP_POWER_STATUS_TO_STANDBY 3
|
||||
|
||||
|
||||
/* General Protocol Messages */
|
||||
#define CEC_MSG_FEATURE_ABORT 0x00
|
||||
/* Abort Reason Operand (reason) */
|
||||
#define CEC_OP_ABORT_UNRECOGNIZED_OP 0
|
||||
#define CEC_OP_ABORT_INCORRECT_MODE 1
|
||||
#define CEC_OP_ABORT_NO_SOURCE 2
|
||||
#define CEC_OP_ABORT_INVALID_OP 3
|
||||
#define CEC_OP_ABORT_REFUSED 4
|
||||
#define CEC_OP_ABORT_UNDETERMINED 5
|
||||
|
||||
#define CEC_MSG_ABORT 0xff
|
||||
|
||||
|
||||
/* System Audio Control Feature */
|
||||
|
||||
/*
|
||||
* Has also:
|
||||
* CEC_MSG_USER_CONTROL_PRESSED
|
||||
* CEC_MSG_USER_CONTROL_RELEASED
|
||||
*/
|
||||
#define CEC_MSG_GIVE_AUDIO_STATUS 0x71
|
||||
#define CEC_MSG_GIVE_SYSTEM_AUDIO_MODE_STATUS 0x7d
|
||||
#define CEC_MSG_REPORT_AUDIO_STATUS 0x7a
|
||||
/* Audio Mute Status Operand (aud_mute_status) */
|
||||
#define CEC_OP_AUD_MUTE_STATUS_OFF 0
|
||||
#define CEC_OP_AUD_MUTE_STATUS_ON 1
|
||||
|
||||
#define CEC_MSG_REPORT_SHORT_AUDIO_DESCRIPTOR 0xa3
|
||||
#define CEC_MSG_REQUEST_SHORT_AUDIO_DESCRIPTOR 0xa4
|
||||
#define CEC_MSG_SET_SYSTEM_AUDIO_MODE 0x72
|
||||
/* System Audio Status Operand (sys_aud_status) */
|
||||
#define CEC_OP_SYS_AUD_STATUS_OFF 0
|
||||
#define CEC_OP_SYS_AUD_STATUS_ON 1
|
||||
|
||||
#define CEC_MSG_SYSTEM_AUDIO_MODE_REQUEST 0x70
|
||||
#define CEC_MSG_SYSTEM_AUDIO_MODE_STATUS 0x7e
|
||||
/* Audio Format ID Operand (audio_format_id) */
|
||||
#define CEC_OP_AUD_FMT_ID_CEA861 0
|
||||
#define CEC_OP_AUD_FMT_ID_CEA861_CXT 1
|
||||
|
||||
|
||||
/* Audio Rate Control Feature */
|
||||
#define CEC_MSG_SET_AUDIO_RATE 0x9a
|
||||
/* Audio Rate Operand (audio_rate) */
|
||||
#define CEC_OP_AUD_RATE_OFF 0
|
||||
#define CEC_OP_AUD_RATE_WIDE_STD 1
|
||||
#define CEC_OP_AUD_RATE_WIDE_FAST 2
|
||||
#define CEC_OP_AUD_RATE_WIDE_SLOW 3
|
||||
#define CEC_OP_AUD_RATE_NARROW_STD 4
|
||||
#define CEC_OP_AUD_RATE_NARROW_FAST 5
|
||||
#define CEC_OP_AUD_RATE_NARROW_SLOW 6
|
||||
|
||||
|
||||
/* Audio Return Channel Control Feature */
|
||||
#define CEC_MSG_INITIATE_ARC 0xc0
|
||||
#define CEC_MSG_REPORT_ARC_INITIATED 0xc1
|
||||
#define CEC_MSG_REPORT_ARC_TERMINATED 0xc2
|
||||
#define CEC_MSG_REQUEST_ARC_INITIATION 0xc3
|
||||
#define CEC_MSG_REQUEST_ARC_TERMINATION 0xc4
|
||||
#define CEC_MSG_TERMINATE_ARC 0xc5
|
||||
|
||||
|
||||
/* Dynamic Audio Lipsync Feature */
|
||||
/* Only for CEC 2.0 and up */
|
||||
#define CEC_MSG_REQUEST_CURRENT_LATENCY 0xa7
|
||||
#define CEC_MSG_REPORT_CURRENT_LATENCY 0xa8
|
||||
/* Low Latency Mode Operand (low_latency_mode) */
|
||||
#define CEC_OP_LOW_LATENCY_MODE_OFF 0
|
||||
#define CEC_OP_LOW_LATENCY_MODE_ON 1
|
||||
/* Audio Output Compensated Operand (audio_out_compensated) */
|
||||
#define CEC_OP_AUD_OUT_COMPENSATED_NA 0
|
||||
#define CEC_OP_AUD_OUT_COMPENSATED_DELAY 1
|
||||
#define CEC_OP_AUD_OUT_COMPENSATED_NO_DELAY 2
|
||||
#define CEC_OP_AUD_OUT_COMPENSATED_PARTIAL_DELAY 3
|
||||
|
||||
|
||||
/* Capability Discovery and Control Feature */
|
||||
#define CEC_MSG_CDC_MESSAGE 0xf8
|
||||
/* Ethernet-over-HDMI: nobody ever does this... */
|
||||
#define CEC_MSG_CDC_HEC_INQUIRE_STATE 0x00
|
||||
#define CEC_MSG_CDC_HEC_REPORT_STATE 0x01
|
||||
/* HEC Functionality State Operand (hec_func_state) */
|
||||
#define CEC_OP_HEC_FUNC_STATE_NOT_SUPPORTED 0
|
||||
#define CEC_OP_HEC_FUNC_STATE_INACTIVE 1
|
||||
#define CEC_OP_HEC_FUNC_STATE_ACTIVE 2
|
||||
#define CEC_OP_HEC_FUNC_STATE_ACTIVATION_FIELD 3
|
||||
/* Host Functionality State Operand (host_func_state) */
|
||||
#define CEC_OP_HOST_FUNC_STATE_NOT_SUPPORTED 0
|
||||
#define CEC_OP_HOST_FUNC_STATE_INACTIVE 1
|
||||
#define CEC_OP_HOST_FUNC_STATE_ACTIVE 2
|
||||
/* ENC Functionality State Operand (enc_func_state) */
|
||||
#define CEC_OP_ENC_FUNC_STATE_EXT_CON_NOT_SUPPORTED 0
|
||||
#define CEC_OP_ENC_FUNC_STATE_EXT_CON_INACTIVE 1
|
||||
#define CEC_OP_ENC_FUNC_STATE_EXT_CON_ACTIVE 2
|
||||
/* CDC Error Code Operand (cdc_errcode) */
|
||||
#define CEC_OP_CDC_ERROR_CODE_NONE 0
|
||||
#define CEC_OP_CDC_ERROR_CODE_CAP_UNSUPPORTED 1
|
||||
#define CEC_OP_CDC_ERROR_CODE_WRONG_STATE 2
|
||||
#define CEC_OP_CDC_ERROR_CODE_OTHER 3
|
||||
/* HEC Support Operand (hec_support) */
|
||||
#define CEC_OP_HEC_SUPPORT_NO 0
|
||||
#define CEC_OP_HEC_SUPPORT_YES 1
|
||||
/* HEC Activation Operand (hec_activation) */
|
||||
#define CEC_OP_HEC_ACTIVATION_ON 0
|
||||
#define CEC_OP_HEC_ACTIVATION_OFF 1
|
||||
|
||||
#define CEC_MSG_CDC_HEC_SET_STATE_ADJACENT 0x02
|
||||
#define CEC_MSG_CDC_HEC_SET_STATE 0x03
|
||||
/* HEC Set State Operand (hec_set_state) */
|
||||
#define CEC_OP_HEC_SET_STATE_DEACTIVATE 0
|
||||
#define CEC_OP_HEC_SET_STATE_ACTIVATE 1
|
||||
|
||||
#define CEC_MSG_CDC_HEC_REQUEST_DEACTIVATION 0x04
|
||||
#define CEC_MSG_CDC_HEC_NOTIFY_ALIVE 0x05
|
||||
#define CEC_MSG_CDC_HEC_DISCOVER 0x06
|
||||
/* Hotplug Detect messages */
|
||||
#define CEC_MSG_CDC_HPD_SET_STATE 0x10
|
||||
/* HPD State Operand (hpd_state) */
|
||||
#define CEC_OP_HPD_STATE_CP_EDID_DISABLE 0
|
||||
#define CEC_OP_HPD_STATE_CP_EDID_ENABLE 1
|
||||
#define CEC_OP_HPD_STATE_CP_EDID_DISABLE_ENABLE 2
|
||||
#define CEC_OP_HPD_STATE_EDID_DISABLE 3
|
||||
#define CEC_OP_HPD_STATE_EDID_ENABLE 4
|
||||
#define CEC_OP_HPD_STATE_EDID_DISABLE_ENABLE 5
|
||||
#define CEC_MSG_CDC_HPD_REPORT_STATE 0x11
|
||||
/* HPD Error Code Operand (hpd_error) */
|
||||
#define CEC_OP_HPD_ERROR_NONE 0
|
||||
#define CEC_OP_HPD_ERROR_INITIATOR_NOT_CAPABLE 1
|
||||
#define CEC_OP_HPD_ERROR_INITIATOR_WRONG_STATE 2
|
||||
#define CEC_OP_HPD_ERROR_OTHER 3
|
||||
#define CEC_OP_HPD_ERROR_NONE_NO_VIDEO 4
|
||||
|
||||
#endif
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* cec-edid - HDMI Consumer Electronics Control & EDID helpers
|
||||
*
|
||||
* Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
||||
*
|
||||
* This program is free software; you may redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _MEDIA_CEC_EDID_H
|
||||
#define _MEDIA_CEC_EDID_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define CEC_PHYS_ADDR_INVALID 0xffff
|
||||
#define cec_phys_addr_exp(pa) \
|
||||
((pa) >> 12), ((pa) >> 8) & 0xf, ((pa) >> 4) & 0xf, (pa) & 0xf
|
||||
|
||||
/**
|
||||
* cec_get_edid_phys_addr() - find and return the physical address
|
||||
*
|
||||
* @edid: pointer to the EDID data
|
||||
* @size: size in bytes of the EDID data
|
||||
* @offset: If not %NULL then the location of the physical address
|
||||
* bytes in the EDID will be returned here. This is set to 0
|
||||
* if there is no physical address found.
|
||||
*
|
||||
* Return: the physical address or CEC_PHYS_ADDR_INVALID if there is none.
|
||||
*/
|
||||
u16 cec_get_edid_phys_addr(const u8 *edid, unsigned int size,
|
||||
unsigned int *offset);
|
||||
|
||||
/**
|
||||
* cec_set_edid_phys_addr() - find and set the physical address
|
||||
*
|
||||
* @edid: pointer to the EDID data
|
||||
* @size: size in bytes of the EDID data
|
||||
* @phys_addr: the new physical address
|
||||
*
|
||||
* This function finds the location of the physical address in the EDID
|
||||
* and fills in the given physical address and updates the checksum
|
||||
* at the end of the EDID block. It does nothing if the EDID doesn't
|
||||
* contain a physical address.
|
||||
*/
|
||||
void cec_set_edid_phys_addr(u8 *edid, unsigned int size, u16 phys_addr);
|
||||
|
||||
/**
|
||||
* cec_phys_addr_for_input() - calculate the PA for an input
|
||||
*
|
||||
* @phys_addr: the physical address of the parent
|
||||
* @input: the number of the input port, must be between 1 and 15
|
||||
*
|
||||
* This function calculates a new physical address based on the input
|
||||
* port number. For example:
|
||||
*
|
||||
* PA = 0.0.0.0 and input = 2 becomes 2.0.0.0
|
||||
*
|
||||
* PA = 3.0.0.0 and input = 1 becomes 3.1.0.0
|
||||
*
|
||||
* PA = 3.2.1.0 and input = 5 becomes 3.2.1.5
|
||||
*
|
||||
* PA = 3.2.1.3 and input = 5 becomes f.f.f.f since it maxed out the depth.
|
||||
*
|
||||
* Return: the new physical address or CEC_PHYS_ADDR_INVALID.
|
||||
*/
|
||||
u16 cec_phys_addr_for_input(u16 phys_addr, u8 input);
|
||||
|
||||
/**
|
||||
* cec_phys_addr_validate() - validate a physical address from an EDID
|
||||
*
|
||||
* @phys_addr: the physical address to validate
|
||||
* @parent: if not %NULL, then this is filled with the parents PA.
|
||||
* @port: if not %NULL, then this is filled with the input port.
|
||||
*
|
||||
* This validates a physical address as read from an EDID. If the
|
||||
* PA is invalid (such as 1.0.1.0 since '0' is only allowed at the end),
|
||||
* then it will return -EINVAL.
|
||||
*
|
||||
* The parent PA is passed into %parent and the input port is passed into
|
||||
* %port. For example:
|
||||
*
|
||||
* PA = 0.0.0.0: has parent 0.0.0.0 and input port 0.
|
||||
*
|
||||
* PA = 1.0.0.0: has parent 0.0.0.0 and input port 1.
|
||||
*
|
||||
* PA = 3.2.0.0: has parent 3.0.0.0 and input port 2.
|
||||
*
|
||||
* PA = f.f.f.f: has parent f.f.f.f and input port 0.
|
||||
*
|
||||
* Return: 0 if the PA is valid, -EINVAL if not.
|
||||
*/
|
||||
int cec_phys_addr_validate(u16 phys_addr, u16 *parent, u16 *port);
|
||||
|
||||
#endif /* _MEDIA_CEC_EDID_H */
|
|
@ -0,0 +1,232 @@
|
|||
/*
|
||||
* cec - HDMI Consumer Electronics Control support header
|
||||
*
|
||||
* Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
||||
*
|
||||
* This program is free software; you may redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _MEDIA_CEC_H
|
||||
#define _MEDIA_CEC_H
|
||||
|
||||
#include <linux/poll.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/cec-funcs.h>
|
||||
#include <media/rc-core.h>
|
||||
#include <media/cec-edid.h>
|
||||
|
||||
/**
|
||||
* struct cec_devnode - cec device node
|
||||
* @dev: cec device
|
||||
* @cdev: cec character device
|
||||
* @parent: parent device
|
||||
* @minor: device node minor number
|
||||
* @registered: the device was correctly registered
|
||||
* @unregistered: the device was unregistered
|
||||
* @fhs_lock: lock to control access to the filehandle list
|
||||
* @fhs: the list of open filehandles (cec_fh)
|
||||
*
|
||||
* This structure represents a cec-related device node.
|
||||
*
|
||||
* The @parent is a physical device. It must be set by core or device drivers
|
||||
* before registering the node.
|
||||
*/
|
||||
struct cec_devnode {
|
||||
/* sysfs */
|
||||
struct device dev;
|
||||
struct cdev cdev;
|
||||
struct device *parent;
|
||||
|
||||
/* device info */
|
||||
int minor;
|
||||
bool registered;
|
||||
bool unregistered;
|
||||
struct mutex fhs_lock;
|
||||
struct list_head fhs;
|
||||
};
|
||||
|
||||
struct cec_adapter;
|
||||
struct cec_data;
|
||||
|
||||
struct cec_data {
|
||||
struct list_head list;
|
||||
struct list_head xfer_list;
|
||||
struct cec_adapter *adap;
|
||||
struct cec_msg msg;
|
||||
struct cec_fh *fh;
|
||||
struct delayed_work work;
|
||||
struct completion c;
|
||||
u8 attempts;
|
||||
bool new_initiator;
|
||||
bool blocking;
|
||||
bool completed;
|
||||
};
|
||||
|
||||
struct cec_msg_entry {
|
||||
struct list_head list;
|
||||
struct cec_msg msg;
|
||||
};
|
||||
|
||||
#define CEC_NUM_EVENTS CEC_EVENT_LOST_MSGS
|
||||
|
||||
struct cec_fh {
|
||||
struct list_head list;
|
||||
struct list_head xfer_list;
|
||||
struct cec_adapter *adap;
|
||||
u8 mode_initiator;
|
||||
u8 mode_follower;
|
||||
|
||||
/* Events */
|
||||
wait_queue_head_t wait;
|
||||
unsigned int pending_events;
|
||||
struct cec_event events[CEC_NUM_EVENTS];
|
||||
struct mutex lock;
|
||||
struct list_head msgs; /* queued messages */
|
||||
unsigned int queued_msgs;
|
||||
};
|
||||
|
||||
#define CEC_SIGNAL_FREE_TIME_RETRY 3
|
||||
#define CEC_SIGNAL_FREE_TIME_NEW_INITIATOR 5
|
||||
#define CEC_SIGNAL_FREE_TIME_NEXT_XFER 7
|
||||
|
||||
/* The nominal data bit period is 2.4 ms */
|
||||
#define CEC_FREE_TIME_TO_USEC(ft) ((ft) * 2400)
|
||||
|
||||
struct cec_adap_ops {
|
||||
/* Low-level callbacks */
|
||||
int (*adap_enable)(struct cec_adapter *adap, bool enable);
|
||||
int (*adap_monitor_all_enable)(struct cec_adapter *adap, bool enable);
|
||||
int (*adap_log_addr)(struct cec_adapter *adap, u8 logical_addr);
|
||||
int (*adap_transmit)(struct cec_adapter *adap, u8 attempts,
|
||||
u32 signal_free_time, struct cec_msg *msg);
|
||||
void (*adap_status)(struct cec_adapter *adap, struct seq_file *file);
|
||||
|
||||
/* High-level CEC message callback */
|
||||
int (*received)(struct cec_adapter *adap, struct cec_msg *msg);
|
||||
};
|
||||
|
||||
/*
|
||||
* The minimum message length you can receive (excepting poll messages) is 2.
|
||||
* With a transfer rate of at most 36 bytes per second this makes 18 messages
|
||||
* per second worst case.
|
||||
*
|
||||
* We queue at most 3 seconds worth of messages. The CEC specification requires
|
||||
* that messages are replied to within a second, so 3 seconds should give more
|
||||
* than enough margin. Since most messages are actually more than 2 bytes, this
|
||||
* is in practice a lot more than 3 seconds.
|
||||
*/
|
||||
#define CEC_MAX_MSG_QUEUE_SZ (18 * 3)
|
||||
|
||||
struct cec_adapter {
|
||||
struct module *owner;
|
||||
char name[32];
|
||||
struct cec_devnode devnode;
|
||||
struct mutex lock;
|
||||
struct rc_dev *rc;
|
||||
|
||||
struct list_head transmit_queue;
|
||||
struct list_head wait_queue;
|
||||
struct cec_data *transmitting;
|
||||
|
||||
struct task_struct *kthread_config;
|
||||
struct completion config_completion;
|
||||
|
||||
struct task_struct *kthread;
|
||||
wait_queue_head_t kthread_waitq;
|
||||
wait_queue_head_t waitq;
|
||||
|
||||
const struct cec_adap_ops *ops;
|
||||
void *priv;
|
||||
u32 capabilities;
|
||||
u8 available_log_addrs;
|
||||
|
||||
u16 phys_addr;
|
||||
bool is_configuring;
|
||||
bool is_configured;
|
||||
u32 monitor_all_cnt;
|
||||
u32 follower_cnt;
|
||||
struct cec_fh *cec_follower;
|
||||
struct cec_fh *cec_initiator;
|
||||
bool passthrough;
|
||||
struct cec_log_addrs log_addrs;
|
||||
|
||||
struct dentry *cec_dir;
|
||||
struct dentry *status_file;
|
||||
|
||||
u16 phys_addrs[15];
|
||||
u32 sequence;
|
||||
|
||||
char input_name[32];
|
||||
char input_phys[32];
|
||||
char input_drv[32];
|
||||
};
|
||||
|
||||
static inline bool cec_has_log_addr(const struct cec_adapter *adap, u8 log_addr)
|
||||
{
|
||||
return adap->log_addrs.log_addr_mask & (1 << log_addr);
|
||||
}
|
||||
|
||||
static inline bool cec_is_sink(const struct cec_adapter *adap)
|
||||
{
|
||||
return adap->phys_addr == 0;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_MEDIA_CEC)
|
||||
struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
|
||||
void *priv, const char *name, u32 caps, u8 available_las,
|
||||
struct device *parent);
|
||||
int cec_register_adapter(struct cec_adapter *adap);
|
||||
void cec_unregister_adapter(struct cec_adapter *adap);
|
||||
void cec_delete_adapter(struct cec_adapter *adap);
|
||||
|
||||
int cec_s_log_addrs(struct cec_adapter *adap, struct cec_log_addrs *log_addrs,
|
||||
bool block);
|
||||
void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr,
|
||||
bool block);
|
||||
int cec_transmit_msg(struct cec_adapter *adap, struct cec_msg *msg,
|
||||
bool block);
|
||||
|
||||
/* Called by the adapter */
|
||||
void cec_transmit_done(struct cec_adapter *adap, u8 status, u8 arb_lost_cnt,
|
||||
u8 nack_cnt, u8 low_drive_cnt, u8 error_cnt);
|
||||
void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg);
|
||||
|
||||
#else
|
||||
|
||||
static inline int cec_register_adapter(struct cec_adapter *adap)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void cec_unregister_adapter(struct cec_adapter *adap)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void cec_delete_adapter(struct cec_adapter *adap)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr,
|
||||
bool block)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* _MEDIA_CEC_H */
|
|
@ -32,11 +32,7 @@ struct adv7511_monitor_detect {
|
|||
struct adv7511_edid_detect {
|
||||
int present;
|
||||
int segment;
|
||||
};
|
||||
|
||||
struct adv7511_cec_arg {
|
||||
void *arg;
|
||||
u32 f_flags;
|
||||
uint16_t phys_addr;
|
||||
};
|
||||
|
||||
struct adv7511_platform_data {
|
||||
|
|
|
@ -31,6 +31,7 @@ enum rc_type {
|
|||
RC_TYPE_RC6_MCE = 16, /* MCE (Philips RC6-6A-32 subtype) protocol */
|
||||
RC_TYPE_SHARP = 17, /* Sharp protocol */
|
||||
RC_TYPE_XMP = 18, /* XMP protocol */
|
||||
RC_TYPE_CEC = 19, /* CEC protocol */
|
||||
};
|
||||
|
||||
#define RC_BIT_NONE 0ULL
|
||||
|
@ -53,6 +54,7 @@ enum rc_type {
|
|||
#define RC_BIT_RC6_MCE (1ULL << RC_TYPE_RC6_MCE)
|
||||
#define RC_BIT_SHARP (1ULL << RC_TYPE_SHARP)
|
||||
#define RC_BIT_XMP (1ULL << RC_TYPE_XMP)
|
||||
#define RC_BIT_CEC (1ULL << RC_TYPE_CEC)
|
||||
|
||||
#define RC_BIT_ALL (RC_BIT_UNKNOWN | RC_BIT_OTHER | \
|
||||
RC_BIT_RC5 | RC_BIT_RC5X | RC_BIT_RC5_SZ | \
|
||||
|
@ -61,7 +63,7 @@ enum rc_type {
|
|||
RC_BIT_NEC | RC_BIT_SANYO | RC_BIT_MCE_KBD | \
|
||||
RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 | RC_BIT_RC6_6A_24 | \
|
||||
RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE | RC_BIT_SHARP | \
|
||||
RC_BIT_XMP)
|
||||
RC_BIT_XMP | RC_BIT_CEC)
|
||||
|
||||
|
||||
#define RC_SCANCODE_UNKNOWN(x) (x)
|
||||
|
@ -123,6 +125,7 @@ void rc_map_init(void);
|
|||
#define RC_MAP_BEHOLD_COLUMBUS "rc-behold-columbus"
|
||||
#define RC_MAP_BEHOLD "rc-behold"
|
||||
#define RC_MAP_BUDGET_CI_OLD "rc-budget-ci-old"
|
||||
#define RC_MAP_CEC "rc-cec"
|
||||
#define RC_MAP_CINERGY_1400 "rc-cinergy-1400"
|
||||
#define RC_MAP_CINERGY "rc-cinergy"
|
||||
#define RC_MAP_DELOCK_61959 "rc-delock-61959"
|
||||
|
|
Loading…
Reference in New Issue