forked from Minki/linux
media updates for v4.8-rc1
-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJXlfJvAAoJEAhfPr2O5OEVtLUP/RpCQ+W3YVryIdmLkdmYXoY7 m2rXtUh7GmzBjaBkFzbRCGZtgROF7zl0e1R3nm4tLbCV4Becw8HO7YiMjqFJm9xr b6IngIyshsHf60Eii3RpLqUFvYrc/DDIMeYf8miwj/PvFAfI2BV9apraexJlpUuI wdyi28cfBHq4WYhubaXKoAyBQ8YRA/t8KNRAkDlifaOaMbSAxWHlmqoSmJWeQx73 KHkSvbRPu4Hjo3R6q/ab8VhqmXeSnbqnQB9lgnxz7AmAZGhOlMYeAhV/K2ZwbBH8 swv36RmJVO59Ov+vNR4p7GGGDL3+qk8JLj4LNVVfOcW0A+t7WrPQEmrL6VsyaZAy /+r4NEOcQN6Z5nFwbr3E0tYJ2Y5jFHOvsBfKd3EEGwty+hCl634akgb0vqtg06cg E2KG+XW983RBadVwEBnEudxJb0fWPWHGhXEqRrwOD+718FNmTqYM6dEvTEyxRup8 EtCLj+eQQ4LmAyZxWyE8A+keKoMFQlHqk9LN9vQ7t7Wxq9mQ+V2l12T/lN4VhdTq 4QZ4mrCMCGEvNcNzgSg6R/9lVb6RHDtMXZ3htbB/w+5xET/IKIANYyg1Hr7ahtdh rTW/4q6n3jtsu6tp5poteFvPzZKAblbrj2EptVzZYkonQ5BeAUisFTtneUL10Jmj EUf/sH0fqoOA0VvV6Tu+ =mrOW -----END PGP SIGNATURE----- Merge tag 'media/v4.8-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media Pull media updates from Mauro Carvalho Chehab: - new framework support for HDMI CEC and remote control support - new encoding codec driver for Mediatek SoC - new frontend driver: helene tuner - added support for NetUp almost universal devices, with supports DVB-C/S/S2/T/T2 and ISDB-T - the mn88472 frontend driver got promoted from staging - a new driver for RCar video input - some soc_camera legacy drivers got removed: timb, omap1, mx2, mx3 - lots of driver cleanups, improvements and fixups * tag 'media/v4.8-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (377 commits) [media] cec: always check all_device_types and features [media] cec: poll should check if there is room in the tx queue [media] vivid: support monitor all mode [media] cec: fix test for unconfigured adapter in main message loop [media] cec: limit the size of the transmit queue [media] cec: zero unused msg part after msg->len [media] cec: don't set fh to NULL in CEC_TRANSMIT [media] cec: clear all status fields before transmit and always fill in sequence [media] cec: CEC_RECEIVE overwrote the timeout field [media] cxd2841er: Reading SNR for DVB-C added [media] cxd2841er: Reading BER and UCB for DVB-C added [media] cxd2841er: fix switch-case for DVB-C [media] cxd2841er: fix signal strength scale for ISDB-T [media] cxd2841er: adjust the dB scale for DVB-C [media] cxd2841er: provide signal strength for DVB-C [media] cxd2841er: fix BER report via DVBv5 stats API [media] mb86a20s: apply mask to val after checking for read failure [media] airspy: fix error logic during device register [media] s5p-cec/TODO: add TODO item [media] cec/TODO: drop comment about sphinx documentation ...
This commit is contained in:
commit
9c1958fc32
@ -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>
|
||||
|
75
Documentation/DocBook/media/v4l/cec-api.xml
Normal file
75
Documentation/DocBook/media/v4l/cec-api.xml
Normal file
@ -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>
|
64
Documentation/DocBook/media/v4l/cec-func-close.xml
Normal file
64
Documentation/DocBook/media/v4l/cec-func-close.xml
Normal file
@ -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>
|
78
Documentation/DocBook/media/v4l/cec-func-ioctl.xml
Normal file
78
Documentation/DocBook/media/v4l/cec-func-ioctl.xml
Normal file
@ -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>
|
104
Documentation/DocBook/media/v4l/cec-func-open.xml
Normal file
104
Documentation/DocBook/media/v4l/cec-func-open.xml
Normal file
@ -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>
|
94
Documentation/DocBook/media/v4l/cec-func-poll.xml
Normal file
94
Documentation/DocBook/media/v4l/cec-func-poll.xml
Normal file
@ -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>
|
151
Documentation/DocBook/media/v4l/cec-ioc-adap-g-caps.xml
Normal file
151
Documentation/DocBook/media/v4l/cec-ioc-adap-g-caps.xml
Normal file
@ -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>
|
329
Documentation/DocBook/media/v4l/cec-ioc-adap-g-log-addrs.xml
Normal file
329
Documentation/DocBook/media/v4l/cec-ioc-adap-g-log-addrs.xml
Normal file
@ -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>
|
86
Documentation/DocBook/media/v4l/cec-ioc-adap-g-phys-addr.xml
Normal file
86
Documentation/DocBook/media/v4l/cec-ioc-adap-g-phys-addr.xml
Normal file
@ -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>
|
202
Documentation/DocBook/media/v4l/cec-ioc-dqevent.xml
Normal file
202
Documentation/DocBook/media/v4l/cec-ioc-dqevent.xml
Normal file
@ -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>
|
255
Documentation/DocBook/media/v4l/cec-ioc-g-mode.xml
Normal file
255
Documentation/DocBook/media/v4l/cec-ioc-g-mode.xml
Normal file
@ -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>
|
274
Documentation/DocBook/media/v4l/cec-ioc-receive.xml
Normal file
274
Documentation/DocBook/media/v4l/cec-ioc-receive.xml
Normal file
@ -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>
|
@ -88,7 +88,7 @@ function.<footnote>
|
||||
<structfield>capabilities</structfield> field of &v4l2-capability;
|
||||
returned by the &VIDIOC-QUERYCAP; ioctl is set. There are two
|
||||
streaming methods, to determine if the memory mapping flavor is
|
||||
supported applications must call the &VIDIOC-REQBUFS; ioctl.</para>
|
||||
supported applications must call the &VIDIOC-REQBUFS; ioctl with the memory type set to <constant>V4L2_MEMORY_MMAP</constant>.</para>
|
||||
|
||||
<para>Streaming is an I/O method where only pointers to buffers
|
||||
are exchanged between application and driver, the data itself is not
|
||||
@ -369,7 +369,7 @@ rest should be evident.</para>
|
||||
<structfield>capabilities</structfield> field of &v4l2-capability;
|
||||
returned by the &VIDIOC-QUERYCAP; ioctl is set. If the particular user
|
||||
pointer method (not only memory mapping) is supported must be
|
||||
determined by calling the &VIDIOC-REQBUFS; ioctl.</para>
|
||||
determined by calling the &VIDIOC-REQBUFS; ioctl with the memory type set to <constant>V4L2_MEMORY_USERPTR</constant>.</para>
|
||||
|
||||
<para>This I/O method combines advantages of the read/write and
|
||||
memory mapping methods. Buffers (planes) are allocated by the application
|
||||
|
@ -157,7 +157,7 @@ on working with the default settings initially.</para>
|
||||
<varlistentry>
|
||||
<term>LIRC_SET_{SEND,REC}_CARRIER</term>
|
||||
<listitem>
|
||||
<para>Set send/receive carrier (in Hz).</para>
|
||||
<para>Set send/receive carrier (in Hz). Return 0 on success.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
|
@ -121,6 +121,70 @@
|
||||
<entry><constant>MEDIA_ENT_F_AUDIO_MIXER</constant></entry>
|
||||
<entry>Audio Mixer Function Entity.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>MEDIA_ENT_F_PROC_VIDEO_COMPOSER</constant></entry>
|
||||
<entry>Video composer (blender). An entity capable of video
|
||||
composing must have at least two sink pads and one source
|
||||
pad, and composes input video frames onto output video
|
||||
frames. Composition can be performed using alpha blending,
|
||||
color keying, raster operations (ROP), stitching or any other
|
||||
means.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER</constant></entry>
|
||||
<entry>Video pixel formatter. An entity capable of pixel formatting
|
||||
must have at least one sink pad and one source pad. Read
|
||||
pixel formatters read pixels from memory and perform a subset
|
||||
of unpacking, cropping, color keying, alpha multiplication
|
||||
and pixel encoding conversion. Write pixel formatters perform
|
||||
a subset of dithering, pixel encoding conversion and packing
|
||||
and write pixels to memory.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV</constant></entry>
|
||||
<entry>Video pixel encoding converter. An entity capable of pixel
|
||||
enconding conversion must have at least one sink pad and one
|
||||
source pad, and convert the encoding of pixels received on
|
||||
its sink pad(s) to a different encoding output on its source
|
||||
pad(s). Pixel encoding conversion includes but isn't limited
|
||||
to RGB to/from HSV, RGB to/from YUV and CFA (Bayer) to RGB
|
||||
conversions.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>MEDIA_ENT_F_PROC_VIDEO_LUT</constant></entry>
|
||||
<entry>Video look-up table. An entity capable of video lookup table
|
||||
processing must have one sink pad and one source pad. It uses
|
||||
the values of the pixels received on its sink pad to look up
|
||||
entries in internal tables and output them on its source pad.
|
||||
The lookup processing can be performed on all components
|
||||
separately or combine them for multi-dimensional table
|
||||
lookups.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>MEDIA_ENT_F_PROC_VIDEO_SCALER</constant></entry>
|
||||
<entry>Video scaler. An entity capable of video scaling must have
|
||||
at least one sink pad and one source pad, and scale the
|
||||
video frame(s) received on its sink pad(s) to a different
|
||||
resolution output on its source pad(s). The range of
|
||||
supported scaling ratios is entity-specific and can differ
|
||||
between the horizontal and vertical directions (in particular
|
||||
scaling can be supported in one direction only). Binning and
|
||||
skipping are considered as scaling.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>MEDIA_ENT_F_PROC_VIDEO_STATISTICS</constant></entry>
|
||||
<entry>Video statistics computation (histogram, 3A, ...). An entity
|
||||
capable of statistics computation must have one sink pad and
|
||||
one source pad. It computes statistics over the frames
|
||||
received on its sink pad and outputs the statistics data on
|
||||
its source pad.
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
@ -5,7 +5,7 @@
|
||||
</refmeta>
|
||||
<refnamediv>
|
||||
<refname><constant>V4L2_PIX_FMT_Z16</constant></refname>
|
||||
<refpurpose>Interleaved grey-scale image, e.g. from a stereo-pair</refpurpose>
|
||||
<refpurpose>16-bit depth data with distance values at each pixel</refpurpose>
|
||||
</refnamediv>
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
<refnamediv>
|
||||
<refname>VIDIOC_REQBUFS</refname>
|
||||
<refpurpose>Initiate Memory Mapping or User Pointer I/O</refpurpose>
|
||||
<refpurpose>Initiate Memory Mapping, User Pointer or DMA Buffer I/O</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
|
@ -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;
|
||||
|
267
Documentation/cec.txt
Normal file
267
Documentation/cec.txt
Normal file
@ -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.
|
59
Documentation/devicetree/bindings/media/mediatek-vcodec.txt
Normal file
59
Documentation/devicetree/bindings/media/mediatek-vcodec.txt
Normal file
@ -0,0 +1,59 @@
|
||||
Mediatek Video Codec
|
||||
|
||||
Mediatek Video Codec is the video codec hw present in Mediatek SoCs which
|
||||
supports high resolution encoding functionalities.
|
||||
|
||||
Required properties:
|
||||
- compatible : "mediatek,mt8173-vcodec-enc" for encoder
|
||||
- reg : Physical base address of the video codec registers and length of
|
||||
memory mapped region.
|
||||
- interrupts : interrupt number to the cpu.
|
||||
- mediatek,larb : must contain the local arbiters in the current Socs.
|
||||
- clocks : list of clock specifiers, corresponding to entries in
|
||||
the clock-names property.
|
||||
- clock-names: encoder must contain "venc_sel_src", "venc_sel",
|
||||
- "venc_lt_sel_src", "venc_lt_sel".
|
||||
- iommus : should point to the respective IOMMU block with master port as
|
||||
argument, see Documentation/devicetree/bindings/iommu/mediatek,iommu.txt
|
||||
for details.
|
||||
- mediatek,vpu : the node of video processor unit
|
||||
|
||||
Example:
|
||||
vcodec_enc: vcodec@0x18002000 {
|
||||
compatible = "mediatek,mt8173-vcodec-enc";
|
||||
reg = <0 0x18002000 0 0x1000>, /*VENC_SYS*/
|
||||
<0 0x19002000 0 0x1000>; /*VENC_LT_SYS*/
|
||||
interrupts = <GIC_SPI 198 IRQ_TYPE_LEVEL_LOW>,
|
||||
<GIC_SPI 202 IRQ_TYPE_LEVEL_LOW>;
|
||||
mediatek,larb = <&larb3>,
|
||||
<&larb5>;
|
||||
iommus = <&iommu M4U_PORT_VENC_RCPU>,
|
||||
<&iommu M4U_PORT_VENC_REC>,
|
||||
<&iommu M4U_PORT_VENC_BSDMA>,
|
||||
<&iommu M4U_PORT_VENC_SV_COMV>,
|
||||
<&iommu M4U_PORT_VENC_RD_COMV>,
|
||||
<&iommu M4U_PORT_VENC_CUR_LUMA>,
|
||||
<&iommu M4U_PORT_VENC_CUR_CHROMA>,
|
||||
<&iommu M4U_PORT_VENC_REF_LUMA>,
|
||||
<&iommu M4U_PORT_VENC_REF_CHROMA>,
|
||||
<&iommu M4U_PORT_VENC_NBM_RDMA>,
|
||||
<&iommu M4U_PORT_VENC_NBM_WDMA>,
|
||||
<&iommu M4U_PORT_VENC_RCPU_SET2>,
|
||||
<&iommu M4U_PORT_VENC_REC_FRM_SET2>,
|
||||
<&iommu M4U_PORT_VENC_BSDMA_SET2>,
|
||||
<&iommu M4U_PORT_VENC_SV_COMA_SET2>,
|
||||
<&iommu M4U_PORT_VENC_RD_COMA_SET2>,
|
||||
<&iommu M4U_PORT_VENC_CUR_LUMA_SET2>,
|
||||
<&iommu M4U_PORT_VENC_CUR_CHROMA_SET2>,
|
||||
<&iommu M4U_PORT_VENC_REF_LUMA_SET2>,
|
||||
<&iommu M4U_PORT_VENC_REC_CHROMA_SET2>;
|
||||
mediatek,vpu = <&vpu>;
|
||||
clocks = <&topckgen CLK_TOP_VENCPLL_D2>,
|
||||
<&topckgen CLK_TOP_VENC_SEL>,
|
||||
<&topckgen CLK_TOP_UNIVPLL1_D2>,
|
||||
<&topckgen CLK_TOP_VENC_LT_SEL>;
|
||||
clock-names = "venc_sel_src",
|
||||
"venc_sel",
|
||||
"venc_lt_sel_src",
|
||||
"venc_lt_sel";
|
||||
};
|
31
Documentation/devicetree/bindings/media/mediatek-vpu.txt
Normal file
31
Documentation/devicetree/bindings/media/mediatek-vpu.txt
Normal file
@ -0,0 +1,31 @@
|
||||
* Mediatek Video Processor Unit
|
||||
|
||||
Video Processor Unit is a HW video controller. It controls HW Codec including
|
||||
H.264/VP8/VP9 Decode, H.264/VP8 Encode and Image Processor (scale/rotate/color convert).
|
||||
|
||||
Required properties:
|
||||
- compatible: "mediatek,mt8173-vpu"
|
||||
- reg: Must contain an entry for each entry in reg-names.
|
||||
- reg-names: Must include the following entries:
|
||||
"tcm": tcm base
|
||||
"cfg_reg": Main configuration registers base
|
||||
- interrupts: interrupt number to the cpu.
|
||||
- clocks : clock name from clock manager
|
||||
- clock-names: must be main. It is the main clock of VPU
|
||||
|
||||
Optional properties:
|
||||
- memory-region: phandle to a node describing memory (see
|
||||
Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt)
|
||||
to be used for VPU extended memory; if not present, VPU may be located
|
||||
anywhere in the memory
|
||||
|
||||
Example:
|
||||
vpu: vpu@10020000 {
|
||||
compatible = "mediatek,mt8173-vpu";
|
||||
reg = <0 0x10020000 0 0x30000>,
|
||||
<0 0x10050000 0 0x100>;
|
||||
reg-names = "tcm", "cfg_reg";
|
||||
interrupts = <GIC_SPI 166 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&topckgen TOP_SCP_SEL>;
|
||||
clock-names = "main";
|
||||
};
|
32
Documentation/devicetree/bindings/media/renesas,fcp.txt
Normal file
32
Documentation/devicetree/bindings/media/renesas,fcp.txt
Normal file
@ -0,0 +1,32 @@
|
||||
Renesas R-Car Frame Compression Processor (FCP)
|
||||
-----------------------------------------------
|
||||
|
||||
The FCP is a companion module of video processing modules in the Renesas R-Car
|
||||
Gen3 SoCs. It provides data compression and decompression, data caching, and
|
||||
conversion of AXI transactions in order to reduce the memory bandwidth.
|
||||
|
||||
There are three types of FCP: FCP for Codec (FCPC), FCP for VSP (FCPV) and FCP
|
||||
for FDP (FCPF). Their configuration and behaviour depend on the module they
|
||||
are paired with. These DT bindings currently support the FCPV only.
|
||||
|
||||
- compatible: Must be one or more of the following
|
||||
|
||||
- "renesas,r8a7795-fcpv" for R8A7795 (R-Car H3) compatible 'FCP for VSP'
|
||||
- "renesas,fcpv" for generic compatible 'FCP for VSP'
|
||||
|
||||
When compatible with the generic version, nodes must list the
|
||||
SoC-specific version corresponding to the platform first, followed by the
|
||||
family-specific and/or generic versions.
|
||||
|
||||
- reg: the register base and size for the device registers
|
||||
- clocks: Reference to the functional clock
|
||||
|
||||
|
||||
Device node example
|
||||
-------------------
|
||||
|
||||
fcpvd1: fcp@fea2f000 {
|
||||
compatible = "renesas,r8a7795-fcpv", "renesas,fcpv";
|
||||
reg = <0 0xfea2f000 0 0x200>;
|
||||
clocks = <&cpg CPG_MOD 602>;
|
||||
};
|
@ -14,6 +14,11 @@ Required properties:
|
||||
- interrupts: VSP interrupt specifier.
|
||||
- clocks: A phandle + clock-specifier pair for the VSP functional clock.
|
||||
|
||||
Optional properties:
|
||||
|
||||
- renesas,fcp: A phandle referencing the FCP that handles memory accesses
|
||||
for the VSP. Not needed on Gen2, mandatory on Gen3.
|
||||
|
||||
|
||||
Example: R8A7790 (R-Car H2) VSP1-S node
|
||||
|
||||
|
31
Documentation/devicetree/bindings/media/s5p-cec.txt
Normal file
31
Documentation/devicetree/bindings/media/s5p-cec.txt
Normal file
@ -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";
|
||||
};
|
@ -21,15 +21,18 @@ Required properties:
|
||||
- clock-names : from common clock binding: must contain "mfc",
|
||||
corresponding to entry in the clocks property.
|
||||
|
||||
- samsung,mfc-r : Base address of the first memory bank used by MFC
|
||||
for DMA contiguous memory allocation and its size.
|
||||
|
||||
- samsung,mfc-l : Base address of the second memory bank used by MFC
|
||||
for DMA contiguous memory allocation and its size.
|
||||
|
||||
Optional properties:
|
||||
- power-domains : power-domain property defined with a phandle
|
||||
to respective power domain.
|
||||
- memory-region : from reserved memory binding: phandles to two reserved
|
||||
memory regions, first is for "left" mfc memory bus interfaces,
|
||||
second if for the "right" mfc memory bus, used when no SYSMMU
|
||||
support is available
|
||||
|
||||
Obsolete properties:
|
||||
- samsung,mfc-r, samsung,mfc-l : support removed, please use memory-region
|
||||
property instead
|
||||
|
||||
|
||||
Example:
|
||||
SoC specific DT entry:
|
||||
@ -43,9 +46,29 @@ mfc: codec@13400000 {
|
||||
clock-names = "mfc";
|
||||
};
|
||||
|
||||
Reserved memory specific DT entry for given board (see reserved memory binding
|
||||
for more information):
|
||||
|
||||
reserved-memory {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
|
||||
mfc_left: region@51000000 {
|
||||
compatible = "shared-dma-pool";
|
||||
no-map;
|
||||
reg = <0x51000000 0x800000>;
|
||||
};
|
||||
|
||||
mfc_right: region@43000000 {
|
||||
compatible = "shared-dma-pool";
|
||||
no-map;
|
||||
reg = <0x43000000 0x800000>;
|
||||
};
|
||||
};
|
||||
|
||||
Board specific DT entry:
|
||||
|
||||
codec@13400000 {
|
||||
samsung,mfc-r = <0x43000000 0x800000>;
|
||||
samsung,mfc-l = <0x51000000 0x800000>;
|
||||
memory-region = <&mfc_left>, <&mfc_right>;
|
||||
};
|
||||
|
@ -54,3 +54,4 @@
|
||||
53 -> Hauppauge WinTV Starburst [0070:c12a]
|
||||
54 -> ViewCast 260e [1576:0260]
|
||||
55 -> ViewCast 460e [1576:0460]
|
||||
56 -> Hauppauge WinTV-quadHD (DVB) [0070:6a28,0070:6b28]
|
||||
|
@ -96,21 +96,6 @@ Basic usage for V4L2 and sub-device drivers
|
||||
|
||||
Where foo->sd is of type struct v4l2_subdev.
|
||||
|
||||
And set all core control ops in your struct v4l2_subdev_core_ops to these
|
||||
helpers:
|
||||
|
||||
.queryctrl = v4l2_subdev_queryctrl,
|
||||
.querymenu = v4l2_subdev_querymenu,
|
||||
.g_ctrl = v4l2_subdev_g_ctrl,
|
||||
.s_ctrl = v4l2_subdev_s_ctrl,
|
||||
.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
|
||||
.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
|
||||
.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
|
||||
|
||||
Note: this is a temporary solution only. Once all V4L2 drivers that depend
|
||||
on subdev drivers are converted to the control framework these helpers will
|
||||
no longer be needed.
|
||||
|
||||
1.4) Clean up the handler at the end:
|
||||
|
||||
v4l2_ctrl_handler_free(&foo->ctrl_handler);
|
||||
|
@ -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.
|
||||
|
74
MAINTAINERS
74
MAINTAINERS
@ -1648,6 +1648,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>
|
||||
@ -2851,6 +2858,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
|
||||
@ -5177,10 +5200,10 @@ S: Maintained
|
||||
F: drivers/media/usb/gspca/m5602/
|
||||
|
||||
GSPCA PAC207 SONIXB SUBDRIVER
|
||||
M: Hans de Goede <hdegoede@redhat.com>
|
||||
M: Hans Verkuil <hverkuil@xs4all.nl>
|
||||
L: linux-media@vger.kernel.org
|
||||
T: git git://linuxtv.org/media_tree.git
|
||||
S: Maintained
|
||||
S: Odd Fixes
|
||||
F: drivers/media/usb/gspca/pac207.c
|
||||
|
||||
GSPCA SN9C20X SUBDRIVER
|
||||
@ -5198,10 +5221,10 @@ S: Maintained
|
||||
F: drivers/media/usb/gspca/t613.c
|
||||
|
||||
GSPCA USB WEBCAM DRIVER
|
||||
M: Hans de Goede <hdegoede@redhat.com>
|
||||
M: Hans Verkuil <hverkuil@xs4all.nl>
|
||||
L: linux-media@vger.kernel.org
|
||||
T: git git://linuxtv.org/media_tree.git
|
||||
S: Maintained
|
||||
S: Odd Fixes
|
||||
F: drivers/media/usb/gspca/
|
||||
|
||||
GUID PARTITION TABLE (GPT)
|
||||
@ -7344,6 +7367,16 @@ L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/iio/potentiometer/mcp4531.c
|
||||
|
||||
MEDIA DRIVERS FOR RENESAS - FCP
|
||||
M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
|
||||
L: linux-media@vger.kernel.org
|
||||
L: linux-renesas-soc@vger.kernel.org
|
||||
T: git git://linuxtv.org/media_tree.git
|
||||
S: Supported
|
||||
F: Documentation/devicetree/bindings/media/renesas,fcp.txt
|
||||
F: drivers/media/platform/rcar-fcp.c
|
||||
F: include/media/rcar-fcp.h
|
||||
|
||||
MEDIA DRIVERS FOR RENESAS - VSP1
|
||||
M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
|
||||
L: linux-media@vger.kernel.org
|
||||
@ -7353,8 +7386,18 @@ S: Supported
|
||||
F: Documentation/devicetree/bindings/media/renesas,vsp1.txt
|
||||
F: drivers/media/platform/vsp1/
|
||||
|
||||
MEDIA DRIVERS FOR HELENE
|
||||
M: Abylay Ospan <aospan@netup.ru>
|
||||
L: linux-media@vger.kernel.org
|
||||
W: https://linuxtv.org
|
||||
W: http://netup.tv/
|
||||
T: git git://linuxtv.org/media_tree.git
|
||||
S: Supported
|
||||
F: drivers/media/dvb-frontends/helene*
|
||||
|
||||
MEDIA DRIVERS FOR ASCOT2E
|
||||
M: Sergey Kozlov <serjk@netup.ru>
|
||||
M: Abylay Ospan <aospan@netup.ru>
|
||||
L: linux-media@vger.kernel.org
|
||||
W: https://linuxtv.org
|
||||
W: http://netup.tv/
|
||||
@ -7364,6 +7407,7 @@ F: drivers/media/dvb-frontends/ascot2e*
|
||||
|
||||
MEDIA DRIVERS FOR CXD2841ER
|
||||
M: Sergey Kozlov <serjk@netup.ru>
|
||||
M: Abylay Ospan <aospan@netup.ru>
|
||||
L: linux-media@vger.kernel.org
|
||||
W: https://linuxtv.org
|
||||
W: http://netup.tv/
|
||||
@ -7373,6 +7417,7 @@ F: drivers/media/dvb-frontends/cxd2841er*
|
||||
|
||||
MEDIA DRIVERS FOR HORUS3A
|
||||
M: Sergey Kozlov <serjk@netup.ru>
|
||||
M: Abylay Ospan <aospan@netup.ru>
|
||||
L: linux-media@vger.kernel.org
|
||||
W: https://linuxtv.org
|
||||
W: http://netup.tv/
|
||||
@ -7382,6 +7427,7 @@ F: drivers/media/dvb-frontends/horus3a*
|
||||
|
||||
MEDIA DRIVERS FOR LNBH25
|
||||
M: Sergey Kozlov <serjk@netup.ru>
|
||||
M: Abylay Ospan <aospan@netup.ru>
|
||||
L: linux-media@vger.kernel.org
|
||||
W: https://linuxtv.org
|
||||
W: http://netup.tv/
|
||||
@ -7391,6 +7437,7 @@ F: drivers/media/dvb-frontends/lnbh25*
|
||||
|
||||
MEDIA DRIVERS FOR NETUP PCI UNIVERSAL DVB devices
|
||||
M: Sergey Kozlov <serjk@netup.ru>
|
||||
M: Abylay Ospan <aospan@netup.ru>
|
||||
L: linux-media@vger.kernel.org
|
||||
W: https://linuxtv.org
|
||||
W: http://netup.tv/
|
||||
@ -7640,10 +7687,8 @@ L: linux-media@vger.kernel.org
|
||||
W: https://linuxtv.org
|
||||
W: http://palosaari.fi/linux/
|
||||
Q: http://patchwork.linuxtv.org/project/linux-media/list/
|
||||
T: git git://linuxtv.org/anttip/media_tree.git
|
||||
S: Maintained
|
||||
F: drivers/staging/media/mn88472/
|
||||
F: drivers/media/dvb-frontends/mn88472.h
|
||||
F: drivers/media/dvb-frontends/mn88472*
|
||||
|
||||
MN88473 MEDIA DRIVER
|
||||
M: Antti Palosaari <crope@iki.fi>
|
||||
@ -9255,6 +9300,13 @@ F: include/linux/tracehook.h
|
||||
F: include/uapi/linux/ptrace.h
|
||||
F: kernel/ptrace.c
|
||||
|
||||
PULSE8-CEC DRIVER
|
||||
M: Hans Verkuil <hverkuil@xs4all.nl>
|
||||
L: linux-media@vger.kernel.org
|
||||
T: git git://linuxtv.org/media_tree.git
|
||||
S: Maintained
|
||||
F: drivers/staging/media/pulse8-cec
|
||||
|
||||
PVRUSB2 VIDEO4LINUX DRIVER
|
||||
M: Mike Isely <isely@pobox.com>
|
||||
L: pvrusb2@isely.net (subscribers-only)
|
||||
@ -9266,10 +9318,10 @@ F: Documentation/video4linux/README.pvrusb2
|
||||
F: drivers/media/usb/pvrusb2/
|
||||
|
||||
PWC WEBCAM DRIVER
|
||||
M: Hans de Goede <hdegoede@redhat.com>
|
||||
M: Hans Verkuil <hverkuil@xs4all.nl>
|
||||
L: linux-media@vger.kernel.org
|
||||
T: git git://linuxtv.org/media_tree.git
|
||||
S: Maintained
|
||||
S: Odd Fixes
|
||||
F: drivers/media/usb/pwc/*
|
||||
|
||||
PWM FAN DRIVER
|
||||
@ -9485,14 +9537,14 @@ F: drivers/video/fbdev/aty/radeon*
|
||||
F: include/uapi/linux/radeonfb.h
|
||||
|
||||
RADIOSHARK RADIO DRIVER
|
||||
M: Hans de Goede <hdegoede@redhat.com>
|
||||
M: Hans Verkuil <hverkuil@xs4all.nl>
|
||||
L: linux-media@vger.kernel.org
|
||||
T: git git://linuxtv.org/media_tree.git
|
||||
S: Maintained
|
||||
F: drivers/media/radio/radio-shark.c
|
||||
|
||||
RADIOSHARK2 RADIO DRIVER
|
||||
M: Hans de Goede <hdegoede@redhat.com>
|
||||
M: Hans Verkuil <hverkuil@xs4all.nl>
|
||||
L: linux-media@vger.kernel.org
|
||||
T: git git://linuxtv.org/media_tree.git
|
||||
S: Maintained
|
||||
|
@ -168,6 +168,18 @@
|
||||
};
|
||||
};
|
||||
|
||||
reserved-memory {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
vpu_dma_reserved: vpu_dma_mem_region {
|
||||
compatible = "shared-dma-pool";
|
||||
reg = <0 0xb7000000 0 0x500000>;
|
||||
alignment = <0x1000>;
|
||||
no-map;
|
||||
};
|
||||
};
|
||||
|
||||
timer {
|
||||
compatible = "arm,armv8-timer";
|
||||
interrupt-parent = <&gic>;
|
||||
@ -312,6 +324,17 @@
|
||||
clock-names = "spi", "wrap";
|
||||
};
|
||||
|
||||
vpu: vpu@10020000 {
|
||||
compatible = "mediatek,mt8173-vpu";
|
||||
reg = <0 0x10020000 0 0x30000>,
|
||||
<0 0x10050000 0 0x100>;
|
||||
reg-names = "tcm", "cfg_reg";
|
||||
interrupts = <GIC_SPI 166 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&topckgen CLK_TOP_SCP_SEL>;
|
||||
clock-names = "main";
|
||||
memory-region = <&vpu_dma_reserved>;
|
||||
};
|
||||
|
||||
sysirq: intpol-controller@10200620 {
|
||||
compatible = "mediatek,mt8173-sysirq",
|
||||
"mediatek,mt6577-sysirq";
|
||||
@ -754,6 +777,45 @@
|
||||
clock-names = "apb", "smi";
|
||||
};
|
||||
|
||||
vcodec_enc: vcodec@18002000 {
|
||||
compatible = "mediatek,mt8173-vcodec-enc";
|
||||
reg = <0 0x18002000 0 0x1000>, /* VENC_SYS */
|
||||
<0 0x19002000 0 0x1000>; /* VENC_LT_SYS */
|
||||
interrupts = <GIC_SPI 198 IRQ_TYPE_LEVEL_LOW>,
|
||||
<GIC_SPI 202 IRQ_TYPE_LEVEL_LOW>;
|
||||
mediatek,larb = <&larb3>,
|
||||
<&larb5>;
|
||||
iommus = <&iommu M4U_PORT_VENC_RCPU>,
|
||||
<&iommu M4U_PORT_VENC_REC>,
|
||||
<&iommu M4U_PORT_VENC_BSDMA>,
|
||||
<&iommu M4U_PORT_VENC_SV_COMV>,
|
||||
<&iommu M4U_PORT_VENC_RD_COMV>,
|
||||
<&iommu M4U_PORT_VENC_CUR_LUMA>,
|
||||
<&iommu M4U_PORT_VENC_CUR_CHROMA>,
|
||||
<&iommu M4U_PORT_VENC_REF_LUMA>,
|
||||
<&iommu M4U_PORT_VENC_REF_CHROMA>,
|
||||
<&iommu M4U_PORT_VENC_NBM_RDMA>,
|
||||
<&iommu M4U_PORT_VENC_NBM_WDMA>,
|
||||
<&iommu M4U_PORT_VENC_RCPU_SET2>,
|
||||
<&iommu M4U_PORT_VENC_REC_FRM_SET2>,
|
||||
<&iommu M4U_PORT_VENC_BSDMA_SET2>,
|
||||
<&iommu M4U_PORT_VENC_SV_COMA_SET2>,
|
||||
<&iommu M4U_PORT_VENC_RD_COMA_SET2>,
|
||||
<&iommu M4U_PORT_VENC_CUR_LUMA_SET2>,
|
||||
<&iommu M4U_PORT_VENC_CUR_CHROMA_SET2>,
|
||||
<&iommu M4U_PORT_VENC_REF_LUMA_SET2>,
|
||||
<&iommu M4U_PORT_VENC_REC_CHROMA_SET2>;
|
||||
mediatek,vpu = <&vpu>;
|
||||
clocks = <&topckgen CLK_TOP_VENCPLL_D2>,
|
||||
<&topckgen CLK_TOP_VENC_SEL>,
|
||||
<&topckgen CLK_TOP_UNIVPLL1_D2>,
|
||||
<&topckgen CLK_TOP_VENC_LT_SEL>;
|
||||
clock-names = "venc_sel_src",
|
||||
"venc_sel",
|
||||
"venc_lt_sel_src",
|
||||
"venc_lt_sel";
|
||||
};
|
||||
|
||||
vencltsys: clock-controller@19000000 {
|
||||
compatible = "mediatek,mt8173-vencltsys", "syscon";
|
||||
reg = <0 0x19000000 0 0x1000>;
|
||||
|
@ -1002,14 +1002,12 @@ static struct adv7842_output_format adv7842_opf[] = {
|
||||
{
|
||||
.op_ch_sel = ADV7842_OP_CH_SEL_BRG,
|
||||
.op_format_sel = ADV7842_OP_FORMAT_SEL_SDR_ITU656_8,
|
||||
.op_656_range = 1,
|
||||
.blank_data = 1,
|
||||
.insert_av_codes = 1,
|
||||
},
|
||||
{
|
||||
.op_ch_sel = ADV7842_OP_CH_SEL_RGB,
|
||||
.op_format_sel = ADV7842_OP_FORMAT_SEL_SDR_ITU656_16,
|
||||
.op_656_range = 1,
|
||||
.blank_data = 1,
|
||||
},
|
||||
};
|
||||
|
@ -148,40 +148,39 @@ static void rcar_du_vsp_plane_setup(struct rcar_du_vsp_plane *plane)
|
||||
struct rcar_du_vsp_plane_state *state =
|
||||
to_rcar_vsp_plane_state(plane->plane.state);
|
||||
struct drm_framebuffer *fb = plane->plane.state->fb;
|
||||
struct v4l2_rect src;
|
||||
struct v4l2_rect dst;
|
||||
dma_addr_t paddr[2] = { 0, };
|
||||
u32 pixelformat = 0;
|
||||
struct vsp1_du_atomic_config cfg = {
|
||||
.pixelformat = 0,
|
||||
.pitch = fb->pitches[0],
|
||||
.alpha = state->alpha,
|
||||
.zpos = state->zpos,
|
||||
};
|
||||
unsigned int i;
|
||||
|
||||
src.left = state->state.src_x >> 16;
|
||||
src.top = state->state.src_y >> 16;
|
||||
src.width = state->state.src_w >> 16;
|
||||
src.height = state->state.src_h >> 16;
|
||||
cfg.src.left = state->state.src_x >> 16;
|
||||
cfg.src.top = state->state.src_y >> 16;
|
||||
cfg.src.width = state->state.src_w >> 16;
|
||||
cfg.src.height = state->state.src_h >> 16;
|
||||
|
||||
dst.left = state->state.crtc_x;
|
||||
dst.top = state->state.crtc_y;
|
||||
dst.width = state->state.crtc_w;
|
||||
dst.height = state->state.crtc_h;
|
||||
cfg.dst.left = state->state.crtc_x;
|
||||
cfg.dst.top = state->state.crtc_y;
|
||||
cfg.dst.width = state->state.crtc_w;
|
||||
cfg.dst.height = state->state.crtc_h;
|
||||
|
||||
for (i = 0; i < state->format->planes; ++i) {
|
||||
struct drm_gem_cma_object *gem;
|
||||
|
||||
gem = drm_fb_cma_get_gem_obj(fb, i);
|
||||
paddr[i] = gem->paddr + fb->offsets[i];
|
||||
cfg.mem[i] = gem->paddr + fb->offsets[i];
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(formats_kms); ++i) {
|
||||
if (formats_kms[i] == state->format->fourcc) {
|
||||
pixelformat = formats_v4l2[i];
|
||||
cfg.pixelformat = formats_v4l2[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
WARN_ON(!pixelformat);
|
||||
|
||||
vsp1_du_atomic_update(plane->vsp->vsp, plane->index, pixelformat,
|
||||
fb->pitches[0], paddr, &src, &dst);
|
||||
vsp1_du_atomic_update(plane->vsp->vsp, plane->index, &cfg);
|
||||
}
|
||||
|
||||
static int rcar_du_vsp_plane_atomic_check(struct drm_plane *plane,
|
||||
@ -220,8 +219,7 @@ static void rcar_du_vsp_plane_atomic_update(struct drm_plane *plane,
|
||||
if (plane->state->crtc)
|
||||
rcar_du_vsp_plane_setup(rplane);
|
||||
else
|
||||
vsp1_du_atomic_update(rplane->vsp->vsp, rplane->index, 0, 0, 0,
|
||||
NULL, NULL);
|
||||
vsp1_du_atomic_update(rplane->vsp->vsp, rplane->index, NULL);
|
||||
}
|
||||
|
||||
static const struct drm_plane_helper_funcs rcar_du_vsp_plane_helper_funcs = {
|
||||
@ -269,6 +267,7 @@ static void rcar_du_vsp_plane_reset(struct drm_plane *plane)
|
||||
return;
|
||||
|
||||
state->alpha = 255;
|
||||
state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1;
|
||||
|
||||
plane->state = &state->state;
|
||||
plane->state->plane = plane;
|
||||
@ -283,6 +282,8 @@ static int rcar_du_vsp_plane_atomic_set_property(struct drm_plane *plane,
|
||||
|
||||
if (property == rcdu->props.alpha)
|
||||
rstate->alpha = val;
|
||||
else if (property == rcdu->props.zpos)
|
||||
rstate->zpos = val;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
@ -299,6 +300,8 @@ static int rcar_du_vsp_plane_atomic_get_property(struct drm_plane *plane,
|
||||
|
||||
if (property == rcdu->props.alpha)
|
||||
*val = rstate->alpha;
|
||||
else if (property == rcdu->props.zpos)
|
||||
*val = rstate->zpos;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
@ -378,6 +381,8 @@ int rcar_du_vsp_init(struct rcar_du_vsp *vsp)
|
||||
|
||||
drm_object_attach_property(&plane->plane.base,
|
||||
rcdu->props.alpha, 255);
|
||||
drm_object_attach_property(&plane->plane.base,
|
||||
rcdu->props.zpos, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -44,6 +44,7 @@ static inline struct rcar_du_vsp_plane *to_rcar_vsp_plane(struct drm_plane *p)
|
||||
* @state: base DRM plane state
|
||||
* @format: information about the pixel format used by the plane
|
||||
* @alpha: value of the plane alpha property
|
||||
* @zpos: value of the plane zpos property
|
||||
*/
|
||||
struct rcar_du_vsp_plane_state {
|
||||
struct drm_plane_state state;
|
||||
@ -51,6 +52,7 @@ struct rcar_du_vsp_plane_state {
|
||||
const struct rcar_du_format_info *format;
|
||||
|
||||
unsigned int alpha;
|
||||
unsigned int zpos;
|
||||
};
|
||||
|
||||
static inline struct rcar_du_vsp_plane_state *
|
||||
|
@ -126,7 +126,7 @@ struct sur40_image_header {
|
||||
#define VIDEO_PACKET_SIZE 16384
|
||||
|
||||
/* polling interval (ms) */
|
||||
#define POLL_INTERVAL 4
|
||||
#define POLL_INTERVAL 1
|
||||
|
||||
/* maximum number of contacts FIXME: this is a guess? */
|
||||
#define MAX_CONTACTS 64
|
||||
@ -151,7 +151,6 @@ struct sur40_state {
|
||||
struct mutex lock;
|
||||
|
||||
struct vb2_queue queue;
|
||||
struct vb2_alloc_ctx *alloc_ctx;
|
||||
struct list_head buf_list;
|
||||
spinlock_t qlock;
|
||||
int sequence;
|
||||
@ -448,7 +447,7 @@ static void sur40_process_video(struct sur40_state *sur40)
|
||||
|
||||
/* return error if streaming was stopped in the meantime */
|
||||
if (sur40->sequence == -1)
|
||||
goto err_poll;
|
||||
return;
|
||||
|
||||
/* mark as finished */
|
||||
new_buf->vb.vb2_buf.timestamp = ktime_get_ns();
|
||||
@ -580,19 +579,13 @@ static int sur40_probe(struct usb_interface *interface,
|
||||
sur40->queue = sur40_queue;
|
||||
sur40->queue.drv_priv = sur40;
|
||||
sur40->queue.lock = &sur40->lock;
|
||||
sur40->queue.dev = sur40->dev;
|
||||
|
||||
/* initialize the queue */
|
||||
error = vb2_queue_init(&sur40->queue);
|
||||
if (error)
|
||||
goto err_unreg_v4l2;
|
||||
|
||||
sur40->alloc_ctx = vb2_dma_sg_init_ctx(sur40->dev);
|
||||
if (IS_ERR(sur40->alloc_ctx)) {
|
||||
dev_err(sur40->dev, "Can't allocate buffer context");
|
||||
error = PTR_ERR(sur40->alloc_ctx);
|
||||
goto err_unreg_v4l2;
|
||||
}
|
||||
|
||||
sur40->vdev = sur40_video_device;
|
||||
sur40->vdev.v4l2_dev = &sur40->v4l2;
|
||||
sur40->vdev.lock = &sur40->lock;
|
||||
@ -633,7 +626,6 @@ static void sur40_disconnect(struct usb_interface *interface)
|
||||
|
||||
video_unregister_device(&sur40->vdev);
|
||||
v4l2_device_unregister(&sur40->v4l2);
|
||||
vb2_dma_sg_cleanup_ctx(sur40->alloc_ctx);
|
||||
|
||||
input_unregister_polled_device(sur40->input);
|
||||
input_free_polled_device(sur40->input);
|
||||
@ -653,13 +645,10 @@ static void sur40_disconnect(struct usb_interface *interface)
|
||||
*/
|
||||
static int sur40_queue_setup(struct vb2_queue *q,
|
||||
unsigned int *nbuffers, unsigned int *nplanes,
|
||||
unsigned int sizes[], void *alloc_ctxs[])
|
||||
unsigned int sizes[], struct device *alloc_devs[])
|
||||
{
|
||||
struct sur40_state *sur40 = vb2_get_drv_priv(q);
|
||||
|
||||
if (q->num_buffers + *nbuffers < 3)
|
||||
*nbuffers = 3 - q->num_buffers;
|
||||
alloc_ctxs[0] = sur40->alloc_ctx;
|
||||
|
||||
if (*nplanes)
|
||||
return sizes[0] < sur40_video_format.sizeimage ? -EINVAL : 0;
|
||||
@ -736,6 +725,7 @@ static int sur40_start_streaming(struct vb2_queue *vq, unsigned int count)
|
||||
static void sur40_stop_streaming(struct vb2_queue *vq)
|
||||
{
|
||||
struct sur40_state *sur40 = vb2_get_drv_priv(vq);
|
||||
vb2_wait_for_all_buffers(vq);
|
||||
sur40->sequence = -1;
|
||||
|
||||
/* Release all active buffers */
|
||||
@ -793,7 +783,6 @@ static int sur40_vidioc_enum_fmt(struct file *file, void *priv,
|
||||
{
|
||||
if (f->index != 0)
|
||||
return -EINVAL;
|
||||
strlcpy(f->description, "8-bit greyscale", sizeof(f->description));
|
||||
f->pixelformat = V4L2_PIX_FMT_GREY;
|
||||
f->flags = 0;
|
||||
return 0;
|
||||
|
@ -80,6 +80,9 @@ config MEDIA_RC_SUPPORT
|
||||
|
||||
Say Y when you have a TV or an IR device.
|
||||
|
||||
config MEDIA_CEC_EDID
|
||||
bool
|
||||
|
||||
#
|
||||
# Media controller
|
||||
# Selectable only for webcam/grabbers, as other drivers don't use it
|
||||
|
@ -2,6 +2,10 @@
|
||||
# Makefile for the kernel multimedia device drivers.
|
||||
#
|
||||
|
||||
ifeq ($(CONFIG_MEDIA_CEC_EDID),y)
|
||||
obj-$(CONFIG_MEDIA_SUPPORT) += cec-edid.o
|
||||
endif
|
||||
|
||||
media-objs := media-device.o media-devnode.o media-entity.o
|
||||
|
||||
#
|
||||
|
168
drivers/media/cec-edid.c
Normal file
168
drivers/media/cec-edid.c
Normal file
@ -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");
|
@ -777,7 +777,7 @@ static void precalculate_color(struct tpg_data *tpg, int k)
|
||||
* Remember that r, g and b are still in the 0 - 0xff0 range.
|
||||
*/
|
||||
if (tpg->real_rgb_range == V4L2_DV_RGB_RANGE_LIMITED &&
|
||||
tpg->rgb_range == V4L2_DV_RGB_RANGE_FULL) {
|
||||
tpg->rgb_range == V4L2_DV_RGB_RANGE_FULL && !tpg->is_yuv) {
|
||||
/*
|
||||
* Convert from full range (which is what r, g and b are)
|
||||
* to limited range (which is the 'real' RGB range), which
|
||||
@ -787,7 +787,7 @@ static void precalculate_color(struct tpg_data *tpg, int k)
|
||||
g = (g * 219) / 255 + (16 << 4);
|
||||
b = (b * 219) / 255 + (16 << 4);
|
||||
} else if (tpg->real_rgb_range != V4L2_DV_RGB_RANGE_LIMITED &&
|
||||
tpg->rgb_range == V4L2_DV_RGB_RANGE_LIMITED) {
|
||||
tpg->rgb_range == V4L2_DV_RGB_RANGE_LIMITED && !tpg->is_yuv) {
|
||||
/*
|
||||
* Clamp r, g and b to the limited range and convert to full
|
||||
* range since that's what we deliver.
|
||||
|
@ -143,7 +143,7 @@ struct dmx_ts_feed {
|
||||
int type,
|
||||
enum dmx_ts_pes pes_type,
|
||||
size_t circular_buffer_size,
|
||||
struct timespec timeout);
|
||||
ktime_t timeout);
|
||||
int (*start_filtering)(struct dmx_ts_feed *feed);
|
||||
int (*stop_filtering)(struct dmx_ts_feed *feed);
|
||||
};
|
||||
|
@ -556,7 +556,7 @@ static int dvb_dmxdev_start_feed(struct dmxdev *dmxdev,
|
||||
struct dmxdev_filter *filter,
|
||||
struct dmxdev_feed *feed)
|
||||
{
|
||||
struct timespec timeout = { 0 };
|
||||
ktime_t timeout = ktime_set(0, 0);
|
||||
struct dmx_pes_filter_params *para = &filter->params.pes;
|
||||
dmx_output_t otype;
|
||||
int ret;
|
||||
|
@ -123,6 +123,7 @@ struct dvb_ca_slot {
|
||||
|
||||
/* Private CA-interface information */
|
||||
struct dvb_ca_private {
|
||||
struct kref refcount;
|
||||
|
||||
/* pointer back to the public data structure */
|
||||
struct dvb_ca_en50221 *pub;
|
||||
@ -161,6 +162,34 @@ struct dvb_ca_private {
|
||||
struct mutex ioctl_mutex;
|
||||
};
|
||||
|
||||
static void dvb_ca_private_free(struct dvb_ca_private *ca)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
dvb_unregister_device(ca->dvbdev);
|
||||
for (i = 0; i < ca->slot_count; i++)
|
||||
vfree(ca->slot_info[i].rx_buffer.data);
|
||||
|
||||
kfree(ca->slot_info);
|
||||
kfree(ca);
|
||||
}
|
||||
|
||||
static void dvb_ca_private_release(struct kref *ref)
|
||||
{
|
||||
struct dvb_ca_private *ca = container_of(ref, struct dvb_ca_private, refcount);
|
||||
dvb_ca_private_free(ca);
|
||||
}
|
||||
|
||||
static void dvb_ca_private_get(struct dvb_ca_private *ca)
|
||||
{
|
||||
kref_get(&ca->refcount);
|
||||
}
|
||||
|
||||
static void dvb_ca_private_put(struct dvb_ca_private *ca)
|
||||
{
|
||||
kref_put(&ca->refcount, dvb_ca_private_release);
|
||||
}
|
||||
|
||||
static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca);
|
||||
static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount);
|
||||
static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount);
|
||||
@ -1558,6 +1587,8 @@ static int dvb_ca_en50221_io_open(struct inode *inode, struct file *file)
|
||||
dvb_ca_en50221_thread_update_delay(ca);
|
||||
dvb_ca_en50221_thread_wakeup(ca);
|
||||
|
||||
dvb_ca_private_get(ca);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1586,6 +1617,8 @@ static int dvb_ca_en50221_io_release(struct inode *inode, struct file *file)
|
||||
|
||||
module_put(ca->pub->owner);
|
||||
|
||||
dvb_ca_private_put(ca);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -1681,6 +1714,7 @@ int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter,
|
||||
ret = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
kref_init(&ca->refcount);
|
||||
ca->pub = pubca;
|
||||
ca->flags = flags;
|
||||
ca->slot_count = slot_count;
|
||||
@ -1759,10 +1793,7 @@ void dvb_ca_en50221_release(struct dvb_ca_en50221 *pubca)
|
||||
|
||||
for (i = 0; i < ca->slot_count; i++) {
|
||||
dvb_ca_en50221_slot_shutdown(ca, i);
|
||||
vfree(ca->slot_info[i].rx_buffer.data);
|
||||
}
|
||||
kfree(ca->slot_info);
|
||||
dvb_unregister_device(ca->dvbdev);
|
||||
kfree(ca);
|
||||
dvb_ca_private_put(ca);
|
||||
pubca->private = NULL;
|
||||
}
|
||||
|
@ -398,28 +398,23 @@ static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf)
|
||||
int dvr_done = 0;
|
||||
|
||||
if (dvb_demux_speedcheck) {
|
||||
struct timespec cur_time, delta_time;
|
||||
ktime_t cur_time;
|
||||
u64 speed_bytes, speed_timedelta;
|
||||
|
||||
demux->speed_pkts_cnt++;
|
||||
|
||||
/* show speed every SPEED_PKTS_INTERVAL packets */
|
||||
if (!(demux->speed_pkts_cnt % SPEED_PKTS_INTERVAL)) {
|
||||
cur_time = current_kernel_time();
|
||||
cur_time = ktime_get();
|
||||
|
||||
if (demux->speed_last_time.tv_sec != 0 &&
|
||||
demux->speed_last_time.tv_nsec != 0) {
|
||||
delta_time = timespec_sub(cur_time,
|
||||
demux->speed_last_time);
|
||||
if (ktime_to_ns(demux->speed_last_time) != 0) {
|
||||
speed_bytes = (u64)demux->speed_pkts_cnt
|
||||
* 188 * 8;
|
||||
/* convert to 1024 basis */
|
||||
speed_bytes = 1000 * div64_u64(speed_bytes,
|
||||
1024);
|
||||
speed_timedelta =
|
||||
(u64)timespec_to_ns(&delta_time);
|
||||
speed_timedelta = div64_u64(speed_timedelta,
|
||||
1000000); /* nsec -> usec */
|
||||
speed_timedelta = ktime_ms_delta(cur_time,
|
||||
demux->speed_last_time);
|
||||
printk(KERN_INFO "TS speed %llu Kbits/sec \n",
|
||||
div64_u64(speed_bytes,
|
||||
speed_timedelta));
|
||||
@ -666,7 +661,7 @@ out:
|
||||
|
||||
static int dmx_ts_feed_set(struct dmx_ts_feed *ts_feed, u16 pid, int ts_type,
|
||||
enum dmx_ts_pes pes_type,
|
||||
size_t circular_buffer_size, struct timespec timeout)
|
||||
size_t circular_buffer_size, ktime_t timeout)
|
||||
{
|
||||
struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
|
||||
struct dvb_demux *demux = feed->demux;
|
||||
|
@ -83,7 +83,7 @@ struct dvb_demux_feed {
|
||||
u8 *buffer;
|
||||
int buffer_size;
|
||||
|
||||
struct timespec timeout;
|
||||
ktime_t timeout;
|
||||
struct dvb_demux_filter *filter;
|
||||
|
||||
int ts_type;
|
||||
@ -134,7 +134,7 @@ struct dvb_demux {
|
||||
|
||||
uint8_t *cnt_storage; /* for TS continuity check */
|
||||
|
||||
struct timespec speed_last_time; /* for TS speed check */
|
||||
ktime_t speed_last_time; /* for TS speed check */
|
||||
uint32_t speed_pkts_cnt; /* for TS speed check */
|
||||
};
|
||||
|
||||
|
@ -99,6 +99,7 @@ MODULE_PARM_DESC(dvb_mfe_wait_time, "Wait up to <mfe_wait_time> seconds on open(
|
||||
static DEFINE_MUTEX(frontend_mutex);
|
||||
|
||||
struct dvb_frontend_private {
|
||||
struct kref refcount;
|
||||
|
||||
/* thread/frontend values */
|
||||
struct dvb_device *dvbdev;
|
||||
@ -137,6 +138,23 @@ struct dvb_frontend_private {
|
||||
#endif
|
||||
};
|
||||
|
||||
static void dvb_frontend_private_free(struct kref *ref)
|
||||
{
|
||||
struct dvb_frontend_private *fepriv =
|
||||
container_of(ref, struct dvb_frontend_private, refcount);
|
||||
kfree(fepriv);
|
||||
}
|
||||
|
||||
static void dvb_frontend_private_put(struct dvb_frontend_private *fepriv)
|
||||
{
|
||||
kref_put(&fepriv->refcount, dvb_frontend_private_free);
|
||||
}
|
||||
|
||||
static void dvb_frontend_private_get(struct dvb_frontend_private *fepriv)
|
||||
{
|
||||
kref_get(&fepriv->refcount);
|
||||
}
|
||||
|
||||
static void dvb_frontend_wakeup(struct dvb_frontend *fe);
|
||||
static int dtv_get_frontend(struct dvb_frontend *fe,
|
||||
struct dtv_frontend_properties *c,
|
||||
@ -2543,6 +2561,8 @@ static int dvb_frontend_open(struct inode *inode, struct file *file)
|
||||
fepriv->events.eventr = fepriv->events.eventw = 0;
|
||||
}
|
||||
|
||||
dvb_frontend_private_get(fepriv);
|
||||
|
||||
if (adapter->mfe_shared)
|
||||
mutex_unlock (&adapter->mfe_lock);
|
||||
return ret;
|
||||
@ -2591,6 +2611,8 @@ static int dvb_frontend_release(struct inode *inode, struct file *file)
|
||||
fe->ops.ts_bus_ctrl(fe, 0);
|
||||
}
|
||||
|
||||
dvb_frontend_private_put(fepriv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -2679,6 +2701,8 @@ int dvb_register_frontend(struct dvb_adapter* dvb,
|
||||
}
|
||||
fepriv = fe->frontend_priv;
|
||||
|
||||
kref_init(&fepriv->refcount);
|
||||
|
||||
sema_init(&fepriv->sem, 1);
|
||||
init_waitqueue_head (&fepriv->wait_queue);
|
||||
init_waitqueue_head (&fepriv->events.wait_queue);
|
||||
@ -2713,18 +2737,11 @@ int dvb_unregister_frontend(struct dvb_frontend* fe)
|
||||
|
||||
mutex_lock(&frontend_mutex);
|
||||
dvb_frontend_stop (fe);
|
||||
mutex_unlock(&frontend_mutex);
|
||||
|
||||
if (fepriv->dvbdev->users < -1)
|
||||
wait_event(fepriv->dvbdev->wait_queue,
|
||||
fepriv->dvbdev->users==-1);
|
||||
|
||||
mutex_lock(&frontend_mutex);
|
||||
dvb_unregister_device (fepriv->dvbdev);
|
||||
|
||||
/* fe is invalid now */
|
||||
kfree(fepriv);
|
||||
mutex_unlock(&frontend_mutex);
|
||||
dvb_frontend_private_put(fepriv);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(dvb_unregister_frontend);
|
||||
|
@ -997,7 +997,7 @@ static int dvb_net_feed_start(struct net_device *dev)
|
||||
netdev_dbg(dev, "start filtering\n");
|
||||
priv->secfeed->start_filtering(priv->secfeed);
|
||||
} else if (priv->feedtype == DVB_NET_FEEDTYPE_ULE) {
|
||||
struct timespec timeout = { 0, 10000000 }; // 10 msec
|
||||
ktime_t timeout = ns_to_ktime(10 * NSEC_PER_MSEC);
|
||||
|
||||
/* we have payloads encapsulated in TS */
|
||||
netdev_dbg(dev, "alloc tsfeed\n");
|
||||
|
@ -55,7 +55,13 @@ void dvb_ringbuffer_init(struct dvb_ringbuffer *rbuf, void *data, size_t len)
|
||||
|
||||
int dvb_ringbuffer_empty(struct dvb_ringbuffer *rbuf)
|
||||
{
|
||||
return (rbuf->pread==rbuf->pwrite);
|
||||
/* smp_load_acquire() to load write pointer on reader side
|
||||
* this pairs with smp_store_release() in dvb_ringbuffer_write(),
|
||||
* dvb_ringbuffer_write_user(), or dvb_ringbuffer_reset()
|
||||
*
|
||||
* for memory barriers also see Documentation/circular-buffers.txt
|
||||
*/
|
||||
return (rbuf->pread == smp_load_acquire(&rbuf->pwrite));
|
||||
}
|
||||
|
||||
|
||||
@ -64,7 +70,12 @@ ssize_t dvb_ringbuffer_free(struct dvb_ringbuffer *rbuf)
|
||||
{
|
||||
ssize_t free;
|
||||
|
||||
free = rbuf->pread - rbuf->pwrite;
|
||||
/* ACCESS_ONCE() to load read pointer on writer side
|
||||
* this pairs with smp_store_release() in dvb_ringbuffer_read(),
|
||||
* dvb_ringbuffer_read_user(), dvb_ringbuffer_flush(),
|
||||
* or dvb_ringbuffer_reset()
|
||||
*/
|
||||
free = ACCESS_ONCE(rbuf->pread) - rbuf->pwrite;
|
||||
if (free <= 0)
|
||||
free += rbuf->size;
|
||||
return free-1;
|
||||
@ -76,7 +87,11 @@ ssize_t dvb_ringbuffer_avail(struct dvb_ringbuffer *rbuf)
|
||||
{
|
||||
ssize_t avail;
|
||||
|
||||
avail = rbuf->pwrite - rbuf->pread;
|
||||
/* smp_load_acquire() to load write pointer on reader side
|
||||
* this pairs with smp_store_release() in dvb_ringbuffer_write(),
|
||||
* dvb_ringbuffer_write_user(), or dvb_ringbuffer_reset()
|
||||
*/
|
||||
avail = smp_load_acquire(&rbuf->pwrite) - rbuf->pread;
|
||||
if (avail < 0)
|
||||
avail += rbuf->size;
|
||||
return avail;
|
||||
@ -86,14 +101,25 @@ ssize_t dvb_ringbuffer_avail(struct dvb_ringbuffer *rbuf)
|
||||
|
||||
void dvb_ringbuffer_flush(struct dvb_ringbuffer *rbuf)
|
||||
{
|
||||
rbuf->pread = rbuf->pwrite;
|
||||
/* dvb_ringbuffer_flush() counts as read operation
|
||||
* smp_load_acquire() to load write pointer
|
||||
* smp_store_release() to update read pointer, this ensures that the
|
||||
* correct pointer is visible for subsequent dvb_ringbuffer_free()
|
||||
* calls on other cpu cores
|
||||
*/
|
||||
smp_store_release(&rbuf->pread, smp_load_acquire(&rbuf->pwrite));
|
||||
rbuf->error = 0;
|
||||
}
|
||||
EXPORT_SYMBOL(dvb_ringbuffer_flush);
|
||||
|
||||
void dvb_ringbuffer_reset(struct dvb_ringbuffer *rbuf)
|
||||
{
|
||||
rbuf->pread = rbuf->pwrite = 0;
|
||||
/* dvb_ringbuffer_reset() counts as read and write operation
|
||||
* smp_store_release() to update read pointer
|
||||
*/
|
||||
smp_store_release(&rbuf->pread, 0);
|
||||
/* smp_store_release() to update write pointer */
|
||||
smp_store_release(&rbuf->pwrite, 0);
|
||||
rbuf->error = 0;
|
||||
}
|
||||
|
||||
@ -119,12 +145,17 @@ ssize_t dvb_ringbuffer_read_user(struct dvb_ringbuffer *rbuf, u8 __user *buf, si
|
||||
return -EFAULT;
|
||||
buf += split;
|
||||
todo -= split;
|
||||
rbuf->pread = 0;
|
||||
/* smp_store_release() for read pointer update to ensure
|
||||
* that buf is not overwritten until read is complete,
|
||||
* this pairs with ACCESS_ONCE() in dvb_ringbuffer_free()
|
||||
*/
|
||||
smp_store_release(&rbuf->pread, 0);
|
||||
}
|
||||
if (copy_to_user(buf, rbuf->data+rbuf->pread, todo))
|
||||
return -EFAULT;
|
||||
|
||||
rbuf->pread = (rbuf->pread + todo) % rbuf->size;
|
||||
/* smp_store_release() to update read pointer, see above */
|
||||
smp_store_release(&rbuf->pread, (rbuf->pread + todo) % rbuf->size);
|
||||
|
||||
return len;
|
||||
}
|
||||
@ -139,11 +170,16 @@ void dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf, size_t len)
|
||||
memcpy(buf, rbuf->data+rbuf->pread, split);
|
||||
buf += split;
|
||||
todo -= split;
|
||||
rbuf->pread = 0;
|
||||
/* smp_store_release() for read pointer update to ensure
|
||||
* that buf is not overwritten until read is complete,
|
||||
* this pairs with ACCESS_ONCE() in dvb_ringbuffer_free()
|
||||
*/
|
||||
smp_store_release(&rbuf->pread, 0);
|
||||
}
|
||||
memcpy(buf, rbuf->data+rbuf->pread, todo);
|
||||
|
||||
rbuf->pread = (rbuf->pread + todo) % rbuf->size;
|
||||
/* smp_store_release() to update read pointer, see above */
|
||||
smp_store_release(&rbuf->pread, (rbuf->pread + todo) % rbuf->size);
|
||||
}
|
||||
|
||||
|
||||
@ -158,10 +194,16 @@ ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf, size_t
|
||||
memcpy(rbuf->data+rbuf->pwrite, buf, split);
|
||||
buf += split;
|
||||
todo -= split;
|
||||
rbuf->pwrite = 0;
|
||||
/* smp_store_release() for write pointer update to ensure that
|
||||
* written data is visible on other cpu cores before the pointer
|
||||
* update, this pairs with smp_load_acquire() in
|
||||
* dvb_ringbuffer_empty() or dvb_ringbuffer_avail()
|
||||
*/
|
||||
smp_store_release(&rbuf->pwrite, 0);
|
||||
}
|
||||
memcpy(rbuf->data+rbuf->pwrite, buf, todo);
|
||||
rbuf->pwrite = (rbuf->pwrite + todo) % rbuf->size;
|
||||
/* smp_store_release() for write pointer update, see above */
|
||||
smp_store_release(&rbuf->pwrite, (rbuf->pwrite + todo) % rbuf->size);
|
||||
|
||||
return len;
|
||||
}
|
||||
@ -181,12 +223,18 @@ ssize_t dvb_ringbuffer_write_user(struct dvb_ringbuffer *rbuf,
|
||||
return len - todo;
|
||||
buf += split;
|
||||
todo -= split;
|
||||
rbuf->pwrite = 0;
|
||||
/* smp_store_release() for write pointer update to ensure that
|
||||
* written data is visible on other cpu cores before the pointer
|
||||
* update, this pairs with smp_load_acquire() in
|
||||
* dvb_ringbuffer_empty() or dvb_ringbuffer_avail()
|
||||
*/
|
||||
smp_store_release(&rbuf->pwrite, 0);
|
||||
}
|
||||
status = copy_from_user(rbuf->data+rbuf->pwrite, buf, todo);
|
||||
if (status)
|
||||
return len - todo;
|
||||
rbuf->pwrite = (rbuf->pwrite + todo) % rbuf->size;
|
||||
/* smp_store_release() for write pointer update, see above */
|
||||
smp_store_release(&rbuf->pwrite, (rbuf->pwrite + todo) % rbuf->size);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
@ -73,6 +73,14 @@ config DVB_SI2165
|
||||
|
||||
Say Y when you want to support this frontend.
|
||||
|
||||
config DVB_MN88472
|
||||
tristate "Panasonic MN88472"
|
||||
depends on DVB_CORE && I2C
|
||||
select REGMAP_I2C
|
||||
default m if !MEDIA_SUBDRV_AUTOSELECT
|
||||
help
|
||||
Say Y when you want to support this frontend.
|
||||
|
||||
config DVB_MN88473
|
||||
tristate "Panasonic MN88473"
|
||||
depends on DVB_CORE && I2C
|
||||
@ -853,6 +861,13 @@ config DVB_ASCOT2E
|
||||
help
|
||||
Say Y when you want to support this frontend.
|
||||
|
||||
config DVB_HELENE
|
||||
tristate "Sony HELENE Sat/Ter tuner (CXD2858ER)"
|
||||
depends on DVB_CORE && I2C
|
||||
default m if !MEDIA_SUBDRV_AUTOSELECT
|
||||
help
|
||||
Say Y when you want to support this frontend.
|
||||
|
||||
comment "Tools to develop new frontends"
|
||||
|
||||
config DVB_DUMMY_FE
|
||||
|
@ -95,6 +95,7 @@ obj-$(CONFIG_DVB_STV0900) += stv0900.o
|
||||
obj-$(CONFIG_DVB_STV090x) += stv090x.o
|
||||
obj-$(CONFIG_DVB_STV6110x) += stv6110x.o
|
||||
obj-$(CONFIG_DVB_M88DS3103) += m88ds3103.o
|
||||
obj-$(CONFIG_DVB_MN88472) += mn88472.o
|
||||
obj-$(CONFIG_DVB_MN88473) += mn88473.o
|
||||
obj-$(CONFIG_DVB_ISL6423) += isl6423.o
|
||||
obj-$(CONFIG_DVB_EC100) += ec100.o
|
||||
@ -123,3 +124,4 @@ obj-$(CONFIG_DVB_AS102_FE) += as102_fe.o
|
||||
obj-$(CONFIG_DVB_TC90522) += tc90522.o
|
||||
obj-$(CONFIG_DVB_HORUS3A) += horus3a.o
|
||||
obj-$(CONFIG_DVB_ASCOT2E) += ascot2e.o
|
||||
obj-$(CONFIG_DVB_HELENE) += helene.o
|
||||
|
@ -41,7 +41,6 @@ struct af9033_dev {
|
||||
u64 post_bit_count;
|
||||
u64 error_block_count;
|
||||
u64 total_block_count;
|
||||
struct delayed_work stat_work;
|
||||
};
|
||||
|
||||
/* write multiple registers */
|
||||
@ -468,8 +467,6 @@ static int af9033_init(struct dvb_frontend *fe)
|
||||
c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
|
||||
c->post_bit_error.len = 1;
|
||||
c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
|
||||
/* start statistics polling */
|
||||
schedule_delayed_work(&dev->stat_work, msecs_to_jiffies(2000));
|
||||
|
||||
return 0;
|
||||
|
||||
@ -485,9 +482,6 @@ static int af9033_sleep(struct dvb_frontend *fe)
|
||||
int ret, i;
|
||||
u8 tmp;
|
||||
|
||||
/* stop statistics polling */
|
||||
cancel_delayed_work_sync(&dev->stat_work);
|
||||
|
||||
ret = af9033_wr_reg(dev, 0x80004c, 1);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
@ -821,36 +815,39 @@ err:
|
||||
static int af9033_read_status(struct dvb_frontend *fe, enum fe_status *status)
|
||||
{
|
||||
struct af9033_dev *dev = fe->demodulator_priv;
|
||||
int ret;
|
||||
u8 tmp;
|
||||
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
||||
int ret, i, tmp = 0;
|
||||
u8 u8tmp, buf[7];
|
||||
|
||||
dev_dbg(&dev->client->dev, "\n");
|
||||
|
||||
*status = 0;
|
||||
|
||||
/* radio channel status, 0=no result, 1=has signal, 2=no signal */
|
||||
ret = af9033_rd_reg(dev, 0x800047, &tmp);
|
||||
ret = af9033_rd_reg(dev, 0x800047, &u8tmp);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
/* has signal */
|
||||
if (tmp == 0x01)
|
||||
if (u8tmp == 0x01)
|
||||
*status |= FE_HAS_SIGNAL;
|
||||
|
||||
if (tmp != 0x02) {
|
||||
if (u8tmp != 0x02) {
|
||||
/* TPS lock */
|
||||
ret = af9033_rd_reg_mask(dev, 0x80f5a9, &tmp, 0x01);
|
||||
ret = af9033_rd_reg_mask(dev, 0x80f5a9, &u8tmp, 0x01);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
if (tmp)
|
||||
if (u8tmp)
|
||||
*status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
|
||||
FE_HAS_VITERBI;
|
||||
|
||||
/* full lock */
|
||||
ret = af9033_rd_reg_mask(dev, 0x80f999, &tmp, 0x01);
|
||||
ret = af9033_rd_reg_mask(dev, 0x80f999, &u8tmp, 0x01);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
if (tmp)
|
||||
if (u8tmp)
|
||||
*status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
|
||||
FE_HAS_VITERBI | FE_HAS_SYNC |
|
||||
FE_HAS_LOCK;
|
||||
@ -858,6 +855,148 @@ static int af9033_read_status(struct dvb_frontend *fe, enum fe_status *status)
|
||||
|
||||
dev->fe_status = *status;
|
||||
|
||||
/* signal strength */
|
||||
if (dev->fe_status & FE_HAS_SIGNAL) {
|
||||
if (dev->is_af9035) {
|
||||
ret = af9033_rd_reg(dev, 0x80004a, &u8tmp);
|
||||
if (ret)
|
||||
goto err;
|
||||
tmp = -u8tmp * 1000;
|
||||
} else {
|
||||
ret = af9033_rd_reg(dev, 0x8000f7, &u8tmp);
|
||||
if (ret)
|
||||
goto err;
|
||||
tmp = (u8tmp - 100) * 1000;
|
||||
}
|
||||
|
||||
c->strength.len = 1;
|
||||
c->strength.stat[0].scale = FE_SCALE_DECIBEL;
|
||||
c->strength.stat[0].svalue = tmp;
|
||||
} else {
|
||||
c->strength.len = 1;
|
||||
c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
/* CNR */
|
||||
if (dev->fe_status & FE_HAS_VITERBI) {
|
||||
u32 snr_val, snr_lut_size;
|
||||
const struct val_snr *snr_lut = NULL;
|
||||
|
||||
/* read value */
|
||||
ret = af9033_rd_regs(dev, 0x80002c, buf, 3);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
snr_val = (buf[2] << 16) | (buf[1] << 8) | (buf[0] << 0);
|
||||
|
||||
/* read superframe number */
|
||||
ret = af9033_rd_reg(dev, 0x80f78b, &u8tmp);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
if (u8tmp)
|
||||
snr_val /= u8tmp;
|
||||
|
||||
/* read current transmission mode */
|
||||
ret = af9033_rd_reg(dev, 0x80f900, &u8tmp);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
switch ((u8tmp >> 0) & 3) {
|
||||
case 0:
|
||||
snr_val *= 4;
|
||||
break;
|
||||
case 1:
|
||||
snr_val *= 1;
|
||||
break;
|
||||
case 2:
|
||||
snr_val *= 2;
|
||||
break;
|
||||
default:
|
||||
snr_val *= 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* read current modulation */
|
||||
ret = af9033_rd_reg(dev, 0x80f903, &u8tmp);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
switch ((u8tmp >> 0) & 3) {
|
||||
case 0:
|
||||
snr_lut_size = ARRAY_SIZE(qpsk_snr_lut);
|
||||
snr_lut = qpsk_snr_lut;
|
||||
break;
|
||||
case 1:
|
||||
snr_lut_size = ARRAY_SIZE(qam16_snr_lut);
|
||||
snr_lut = qam16_snr_lut;
|
||||
break;
|
||||
case 2:
|
||||
snr_lut_size = ARRAY_SIZE(qam64_snr_lut);
|
||||
snr_lut = qam64_snr_lut;
|
||||
break;
|
||||
default:
|
||||
snr_lut_size = 0;
|
||||
tmp = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < snr_lut_size; i++) {
|
||||
tmp = snr_lut[i].snr * 1000;
|
||||
if (snr_val < snr_lut[i].val)
|
||||
break;
|
||||
}
|
||||
|
||||
c->cnr.len = 1;
|
||||
c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
|
||||
c->cnr.stat[0].svalue = tmp;
|
||||
} else {
|
||||
c->cnr.len = 1;
|
||||
c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
/* UCB/PER/BER */
|
||||
if (dev->fe_status & FE_HAS_LOCK) {
|
||||
/* outer FEC, 204 byte packets */
|
||||
u16 abort_packet_count, rsd_packet_count;
|
||||
/* inner FEC, bits */
|
||||
u32 rsd_bit_err_count;
|
||||
|
||||
/*
|
||||
* Packet count used for measurement is 10000
|
||||
* (rsd_packet_count). Maybe it should be increased?
|
||||
*/
|
||||
|
||||
ret = af9033_rd_regs(dev, 0x800032, buf, 7);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
abort_packet_count = (buf[1] << 8) | (buf[0] << 0);
|
||||
rsd_bit_err_count = (buf[4] << 16) | (buf[3] << 8) | buf[2];
|
||||
rsd_packet_count = (buf[6] << 8) | (buf[5] << 0);
|
||||
|
||||
dev->error_block_count += abort_packet_count;
|
||||
dev->total_block_count += rsd_packet_count;
|
||||
dev->post_bit_error += rsd_bit_err_count;
|
||||
dev->post_bit_count += rsd_packet_count * 204 * 8;
|
||||
|
||||
c->block_count.len = 1;
|
||||
c->block_count.stat[0].scale = FE_SCALE_COUNTER;
|
||||
c->block_count.stat[0].uvalue = dev->total_block_count;
|
||||
|
||||
c->block_error.len = 1;
|
||||
c->block_error.stat[0].scale = FE_SCALE_COUNTER;
|
||||
c->block_error.stat[0].uvalue = dev->error_block_count;
|
||||
|
||||
c->post_bit_count.len = 1;
|
||||
c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
|
||||
c->post_bit_count.stat[0].uvalue = dev->post_bit_count;
|
||||
|
||||
c->post_bit_error.len = 1;
|
||||
c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
|
||||
c->post_bit_error.stat[0].uvalue = dev->post_bit_error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
@ -1059,159 +1198,6 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void af9033_stat_work(struct work_struct *work)
|
||||
{
|
||||
struct af9033_dev *dev = container_of(work, struct af9033_dev, stat_work.work);
|
||||
struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache;
|
||||
int ret, tmp, i, len;
|
||||
u8 u8tmp, buf[7];
|
||||
|
||||
dev_dbg(&dev->client->dev, "\n");
|
||||
|
||||
/* signal strength */
|
||||
if (dev->fe_status & FE_HAS_SIGNAL) {
|
||||
if (dev->is_af9035) {
|
||||
ret = af9033_rd_reg(dev, 0x80004a, &u8tmp);
|
||||
tmp = -u8tmp * 1000;
|
||||
} else {
|
||||
ret = af9033_rd_reg(dev, 0x8000f7, &u8tmp);
|
||||
tmp = (u8tmp - 100) * 1000;
|
||||
}
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
c->strength.len = 1;
|
||||
c->strength.stat[0].scale = FE_SCALE_DECIBEL;
|
||||
c->strength.stat[0].svalue = tmp;
|
||||
} else {
|
||||
c->strength.len = 1;
|
||||
c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
/* CNR */
|
||||
if (dev->fe_status & FE_HAS_VITERBI) {
|
||||
u32 snr_val;
|
||||
const struct val_snr *snr_lut;
|
||||
|
||||
/* read value */
|
||||
ret = af9033_rd_regs(dev, 0x80002c, buf, 3);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
snr_val = (buf[2] << 16) | (buf[1] << 8) | (buf[0] << 0);
|
||||
|
||||
/* read superframe number */
|
||||
ret = af9033_rd_reg(dev, 0x80f78b, &u8tmp);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
if (u8tmp)
|
||||
snr_val /= u8tmp;
|
||||
|
||||
/* read current transmission mode */
|
||||
ret = af9033_rd_reg(dev, 0x80f900, &u8tmp);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
switch ((u8tmp >> 0) & 3) {
|
||||
case 0:
|
||||
snr_val *= 4;
|
||||
break;
|
||||
case 1:
|
||||
snr_val *= 1;
|
||||
break;
|
||||
case 2:
|
||||
snr_val *= 2;
|
||||
break;
|
||||
default:
|
||||
goto err_schedule_delayed_work;
|
||||
}
|
||||
|
||||
/* read current modulation */
|
||||
ret = af9033_rd_reg(dev, 0x80f903, &u8tmp);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
switch ((u8tmp >> 0) & 3) {
|
||||
case 0:
|
||||
len = ARRAY_SIZE(qpsk_snr_lut);
|
||||
snr_lut = qpsk_snr_lut;
|
||||
break;
|
||||
case 1:
|
||||
len = ARRAY_SIZE(qam16_snr_lut);
|
||||
snr_lut = qam16_snr_lut;
|
||||
break;
|
||||
case 2:
|
||||
len = ARRAY_SIZE(qam64_snr_lut);
|
||||
snr_lut = qam64_snr_lut;
|
||||
break;
|
||||
default:
|
||||
goto err_schedule_delayed_work;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
tmp = snr_lut[i].snr * 1000;
|
||||
if (snr_val < snr_lut[i].val)
|
||||
break;
|
||||
}
|
||||
|
||||
c->cnr.len = 1;
|
||||
c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
|
||||
c->cnr.stat[0].svalue = tmp;
|
||||
} else {
|
||||
c->cnr.len = 1;
|
||||
c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
/* UCB/PER/BER */
|
||||
if (dev->fe_status & FE_HAS_LOCK) {
|
||||
/* outer FEC, 204 byte packets */
|
||||
u16 abort_packet_count, rsd_packet_count;
|
||||
/* inner FEC, bits */
|
||||
u32 rsd_bit_err_count;
|
||||
|
||||
/*
|
||||
* Packet count used for measurement is 10000
|
||||
* (rsd_packet_count). Maybe it should be increased?
|
||||
*/
|
||||
|
||||
ret = af9033_rd_regs(dev, 0x800032, buf, 7);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
abort_packet_count = (buf[1] << 8) | (buf[0] << 0);
|
||||
rsd_bit_err_count = (buf[4] << 16) | (buf[3] << 8) | buf[2];
|
||||
rsd_packet_count = (buf[6] << 8) | (buf[5] << 0);
|
||||
|
||||
dev->error_block_count += abort_packet_count;
|
||||
dev->total_block_count += rsd_packet_count;
|
||||
dev->post_bit_error += rsd_bit_err_count;
|
||||
dev->post_bit_count += rsd_packet_count * 204 * 8;
|
||||
|
||||
c->block_count.len = 1;
|
||||
c->block_count.stat[0].scale = FE_SCALE_COUNTER;
|
||||
c->block_count.stat[0].uvalue = dev->total_block_count;
|
||||
|
||||
c->block_error.len = 1;
|
||||
c->block_error.stat[0].scale = FE_SCALE_COUNTER;
|
||||
c->block_error.stat[0].uvalue = dev->error_block_count;
|
||||
|
||||
c->post_bit_count.len = 1;
|
||||
c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
|
||||
c->post_bit_count.stat[0].uvalue = dev->post_bit_count;
|
||||
|
||||
c->post_bit_error.len = 1;
|
||||
c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
|
||||
c->post_bit_error.stat[0].uvalue = dev->post_bit_error;
|
||||
}
|
||||
|
||||
err_schedule_delayed_work:
|
||||
schedule_delayed_work(&dev->stat_work, msecs_to_jiffies(2000));
|
||||
return;
|
||||
err:
|
||||
dev_dbg(&dev->client->dev, "failed=%d\n", ret);
|
||||
}
|
||||
|
||||
static struct dvb_frontend_ops af9033_ops = {
|
||||
.delsys = { SYS_DVBT },
|
||||
.info = {
|
||||
@ -1272,7 +1258,6 @@ static int af9033_probe(struct i2c_client *client,
|
||||
|
||||
/* setup the state */
|
||||
dev->client = client;
|
||||
INIT_DELAYED_WORK(&dev->stat_work, af9033_stat_work);
|
||||
memcpy(&dev->cfg, cfg, sizeof(struct af9033_config));
|
||||
|
||||
if (dev->cfg.clock != 12000000) {
|
||||
@ -1372,9 +1357,6 @@ static int af9033_remove(struct i2c_client *client)
|
||||
|
||||
dev_dbg(&dev->client->dev, "\n");
|
||||
|
||||
/* stop statistics polling */
|
||||
cancel_delayed_work_sync(&dev->stat_work);
|
||||
|
||||
dev->fe.ops.release = NULL;
|
||||
dev->fe.demodulator_priv = NULL;
|
||||
kfree(dev);
|
||||
@ -1391,6 +1373,7 @@ MODULE_DEVICE_TABLE(i2c, af9033_id_table);
|
||||
static struct i2c_driver af9033_driver = {
|
||||
.driver = {
|
||||
.name = "af9033",
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.probe = af9033_probe,
|
||||
.remove = af9033_remove,
|
||||
|
@ -132,7 +132,7 @@ static int ascot2e_write_regs(struct ascot2e_priv *priv,
|
||||
}
|
||||
};
|
||||
|
||||
if (len + 1 >= sizeof(buf)) {
|
||||
if (len + 1 > sizeof(buf)) {
|
||||
dev_warn(&priv->i2c->dev,"wr reg=%04x: len=%d is too big!\n",
|
||||
reg, len + 1);
|
||||
return -E2BIG;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -25,41 +25,39 @@
|
||||
#include <linux/kconfig.h>
|
||||
#include <linux/dvb/frontend.h>
|
||||
|
||||
enum cxd2841er_xtal {
|
||||
SONY_XTAL_20500, /* 20.5 MHz */
|
||||
SONY_XTAL_24000, /* 24 MHz */
|
||||
SONY_XTAL_41000 /* 41 MHz */
|
||||
};
|
||||
|
||||
struct cxd2841er_config {
|
||||
u8 i2c_addr;
|
||||
enum cxd2841er_xtal xtal;
|
||||
};
|
||||
|
||||
#if IS_REACHABLE(CONFIG_DVB_CXD2841ER)
|
||||
extern struct dvb_frontend *cxd2841er_attach_s(struct cxd2841er_config *cfg,
|
||||
struct i2c_adapter *i2c);
|
||||
|
||||
extern struct dvb_frontend *cxd2841er_attach_t(struct cxd2841er_config *cfg,
|
||||
struct i2c_adapter *i2c);
|
||||
|
||||
extern struct dvb_frontend *cxd2841er_attach_c(struct cxd2841er_config *cfg,
|
||||
extern struct dvb_frontend *cxd2841er_attach_t_c(struct cxd2841er_config *cfg,
|
||||
struct i2c_adapter *i2c);
|
||||
#else
|
||||
static inline struct dvb_frontend *cxd2841er_attach_s(
|
||||
struct cxd2841er_config *cfg,
|
||||
struct i2c_adapter *i2c)
|
||||
{
|
||||
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
|
||||
pr_warn("%s: driver disabled by Kconfig\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct dvb_frontend *cxd2841er_attach_t(
|
||||
static inline struct dvb_frontend *cxd2841er_attach_t_c(
|
||||
struct cxd2841er_config *cfg, struct i2c_adapter *i2c)
|
||||
{
|
||||
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
|
||||
pr_warn("%s: driver disabled by Kconfig\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct dvb_frontend *cxd2841er_attach_c(
|
||||
struct cxd2841er_config *cfg, struct i2c_adapter *i2c)
|
||||
{
|
||||
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -26,6 +26,7 @@
|
||||
#define I2C_SLVT 1
|
||||
|
||||
#define CXD2841ER_CHIP_ID 0xa7
|
||||
#define CXD2854ER_CHIP_ID 0xc1
|
||||
|
||||
#define CXD2841ER_DVBS_POLLING_INVL 10
|
||||
|
||||
|
@ -797,6 +797,8 @@ static const u16 bb_ramp_pwm_normal[] = {
|
||||
(0 << 9) | 400, /* BB_RAMP6 */
|
||||
};
|
||||
|
||||
#if 0
|
||||
/* Currently unused */
|
||||
static const u16 bb_ramp_pwm_boost[] = {
|
||||
550, /* max BB gain in 10th of dB */
|
||||
8, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> BB_RAMP2 */
|
||||
@ -806,6 +808,7 @@ static const u16 bb_ramp_pwm_boost[] = {
|
||||
(2 << 9) | 208, /* BB_RAMP5 = 29dB */
|
||||
(0 << 9) | 440, /* BB_RAMP6 */
|
||||
};
|
||||
#endif
|
||||
|
||||
static const u16 rf_ramp_pwm_cband[] = {
|
||||
314, /* max RF gain in 10th of dB */
|
||||
@ -849,6 +852,8 @@ static const u16 rf_ramp_pwm_uhf[] = {
|
||||
(0 << 10) | 580, /* GAIN_4_2, LNA 4 */
|
||||
};
|
||||
|
||||
#if 0
|
||||
/* Currently unused */
|
||||
static const u16 rf_ramp_pwm_sband[] = {
|
||||
253, /* max RF gain in 10th of dB */
|
||||
38, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */
|
||||
@ -862,6 +867,7 @@ static const u16 rf_ramp_pwm_sband[] = {
|
||||
(0 << 10) | 0, /* GAIN_4_1, LNA 4 = 0dB */
|
||||
(0 << 10) | 0, /* GAIN_4_2, LNA 4 */
|
||||
};
|
||||
#endif
|
||||
|
||||
struct slope {
|
||||
s16 range;
|
||||
|
@ -1240,12 +1240,15 @@ static u32 frac_times1e6(u32 N, u32 D)
|
||||
* and rounded. For calc used formula: 16*10^(prescaleGain[dB]/20).
|
||||
*
|
||||
*/
|
||||
#if 0
|
||||
/* Currently, unused as we lack support for analog TV */
|
||||
static const u16 nicam_presc_table_val[43] = {
|
||||
1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4,
|
||||
5, 5, 6, 6, 7, 8, 9, 10, 11, 13, 14, 16,
|
||||
18, 20, 23, 25, 28, 32, 36, 40, 45,
|
||||
51, 57, 64, 71, 80, 90, 101, 113, 127
|
||||
};
|
||||
#endif
|
||||
|
||||
/*============================================================================*/
|
||||
/*== END HELPER FUNCTIONS ==*/
|
||||
|
@ -959,6 +959,15 @@ static int ds3000_set_frontend(struct dvb_frontend *fe)
|
||||
/* enable ac coupling */
|
||||
ds3000_writereg(state, 0x25, 0x8a);
|
||||
|
||||
if ((c->symbol_rate < ds3000_ops.info.symbol_rate_min) ||
|
||||
(c->symbol_rate > ds3000_ops.info.symbol_rate_max)) {
|
||||
dprintk("%s() symbol_rate %u out of range (%u ... %u)\n",
|
||||
__func__, c->symbol_rate,
|
||||
ds3000_ops.info.symbol_rate_min,
|
||||
ds3000_ops.info.symbol_rate_max);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* enhance symbol rate performance */
|
||||
if ((c->symbol_rate / 1000) <= 5000) {
|
||||
value = 29777 / (c->symbol_rate / 1000) + 1;
|
||||
|
1042
drivers/media/dvb-frontends/helene.c
Normal file
1042
drivers/media/dvb-frontends/helene.c
Normal file
File diff suppressed because it is too large
Load Diff
79
drivers/media/dvb-frontends/helene.h
Normal file
79
drivers/media/dvb-frontends/helene.h
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* helene.h
|
||||
*
|
||||
* Sony HELENE DVB-S/S2/T/T2/C/C2/ISDB-T/S tuner driver (CXD2858ER)
|
||||
*
|
||||
* Copyright 2012 Sony Corporation
|
||||
* Copyright (C) 2014 NetUP Inc.
|
||||
* Copyright (C) 2014 Abylay Ospan <aospan@netup.ru>
|
||||
*
|
||||
* 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 program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __DVB_HELENE_H__
|
||||
#define __DVB_HELENE_H__
|
||||
|
||||
#include <linux/kconfig.h>
|
||||
#include <linux/dvb/frontend.h>
|
||||
#include <linux/i2c.h>
|
||||
|
||||
enum helene_xtal {
|
||||
SONY_HELENE_XTAL_16000, /* 16 MHz */
|
||||
SONY_HELENE_XTAL_20500, /* 20.5 MHz */
|
||||
SONY_HELENE_XTAL_24000, /* 24 MHz */
|
||||
SONY_HELENE_XTAL_41000 /* 41 MHz */
|
||||
};
|
||||
|
||||
/**
|
||||
* struct helene_config - the configuration of 'Helene' tuner driver
|
||||
* @i2c_address: I2C address of the tuner
|
||||
* @xtal_freq_mhz: Oscillator frequency, MHz
|
||||
* @set_tuner_priv: Callback function private context
|
||||
* @set_tuner_callback: Callback function that notifies the parent driver
|
||||
* which tuner is active now
|
||||
*/
|
||||
struct helene_config {
|
||||
u8 i2c_address;
|
||||
u8 xtal_freq_mhz;
|
||||
void *set_tuner_priv;
|
||||
int (*set_tuner_callback)(void *, int);
|
||||
enum helene_xtal xtal;
|
||||
};
|
||||
|
||||
#if IS_REACHABLE(CONFIG_DVB_HELENE)
|
||||
extern struct dvb_frontend *helene_attach(struct dvb_frontend *fe,
|
||||
const struct helene_config *config,
|
||||
struct i2c_adapter *i2c);
|
||||
#else
|
||||
static inline struct dvb_frontend *helene_attach(struct dvb_frontend *fe,
|
||||
const struct helene_config *config,
|
||||
struct i2c_adapter *i2c)
|
||||
{
|
||||
pr_warn("%s: driver disabled by Kconfig\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if IS_REACHABLE(CONFIG_DVB_HELENE)
|
||||
extern struct dvb_frontend *helene_attach_s(struct dvb_frontend *fe,
|
||||
const struct helene_config *config,
|
||||
struct i2c_adapter *i2c);
|
||||
#else
|
||||
static inline struct dvb_frontend *helene_attach_s(struct dvb_frontend *fe,
|
||||
const struct helene_config *config,
|
||||
struct i2c_adapter *i2c)
|
||||
{
|
||||
pr_warn("%s: driver disabled by Kconfig\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -66,7 +66,7 @@ static int horus3a_write_regs(struct horus3a_priv *priv,
|
||||
}
|
||||
};
|
||||
|
||||
if (len + 1 >= sizeof(buf)) {
|
||||
if (len + 1 > sizeof(buf)) {
|
||||
dev_warn(&priv->i2c->dev,"wr reg=%04x: len=%d is too big!\n",
|
||||
reg, len + 1);
|
||||
return -E2BIG;
|
||||
@ -272,24 +272,6 @@ static int horus3a_set_params(struct dvb_frontend *fe)
|
||||
if (fc_lpf > 36)
|
||||
fc_lpf = 36;
|
||||
} else if (p->delivery_system == SYS_DVBS2) {
|
||||
int rolloff;
|
||||
|
||||
switch (p->rolloff) {
|
||||
case ROLLOFF_35:
|
||||
rolloff = 35;
|
||||
break;
|
||||
case ROLLOFF_25:
|
||||
rolloff = 25;
|
||||
break;
|
||||
case ROLLOFF_20:
|
||||
rolloff = 20;
|
||||
break;
|
||||
case ROLLOFF_AUTO:
|
||||
default:
|
||||
dev_err(&priv->i2c->dev,
|
||||
"horus3a: auto roll-off is not supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
/*
|
||||
* SR <= 4.5:
|
||||
* fc_lpf = 5
|
||||
@ -302,11 +284,9 @@ static int horus3a_set_params(struct dvb_frontend *fe)
|
||||
if (symbol_rate <= 4500)
|
||||
fc_lpf = 5;
|
||||
else if (symbol_rate <= 10000)
|
||||
fc_lpf = (u8)DIV_ROUND_UP(
|
||||
symbol_rate * (200 + rolloff), 200000);
|
||||
fc_lpf = (u8)((symbol_rate * 11 + (10000-1)) / 10000);
|
||||
else
|
||||
fc_lpf = (u8)DIV_ROUND_UP(
|
||||
symbol_rate * (100 + rolloff), 200000) + 5;
|
||||
fc_lpf = (u8)((symbol_rate * 3 + (5000-1)) / 5000 + 5);
|
||||
/* 5 <= fc_lpf <= 36 is valid */
|
||||
if (fc_lpf > 36)
|
||||
fc_lpf = 36;
|
||||
|
@ -306,8 +306,8 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
|
||||
const struct m88ds3103_reg_val *init;
|
||||
u8 u8tmp, u8tmp1 = 0, u8tmp2 = 0; /* silence compiler warning */
|
||||
u8 buf[3];
|
||||
u16 u16tmp, divide_ratio = 0;
|
||||
u32 tuner_frequency, target_mclk;
|
||||
u16 u16tmp;
|
||||
u32 tuner_frequency_khz, target_mclk;
|
||||
s32 s32tmp;
|
||||
|
||||
dev_dbg(&client->dev,
|
||||
@ -344,7 +344,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
|
||||
}
|
||||
|
||||
if (fe->ops.tuner_ops.get_frequency) {
|
||||
ret = fe->ops.tuner_ops.get_frequency(fe, &tuner_frequency);
|
||||
ret = fe->ops.tuner_ops.get_frequency(fe, &tuner_frequency_khz);
|
||||
if (ret)
|
||||
goto err;
|
||||
} else {
|
||||
@ -353,20 +353,20 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
|
||||
* actual frequency used. Carrier offset calculation is not
|
||||
* valid.
|
||||
*/
|
||||
tuner_frequency = c->frequency;
|
||||
tuner_frequency_khz = c->frequency;
|
||||
}
|
||||
|
||||
/* select M88RS6000 demod main mclk and ts mclk from tuner die. */
|
||||
if (dev->chip_id == M88RS6000_CHIP_ID) {
|
||||
if (c->symbol_rate > 45010000)
|
||||
dev->mclk_khz = 110250;
|
||||
dev->mclk = 110250000;
|
||||
else
|
||||
dev->mclk_khz = 96000;
|
||||
dev->mclk = 96000000;
|
||||
|
||||
if (c->delivery_system == SYS_DVBS)
|
||||
target_mclk = 96000;
|
||||
target_mclk = 96000000;
|
||||
else
|
||||
target_mclk = 144000;
|
||||
target_mclk = 144000000;
|
||||
|
||||
/* Enable demod clock path */
|
||||
ret = regmap_write(dev->regmap, 0x06, 0x00);
|
||||
@ -375,7 +375,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
|
||||
usleep_range(10000, 20000);
|
||||
} else {
|
||||
/* set M88DS3103 mclk and ts mclk. */
|
||||
dev->mclk_khz = 96000;
|
||||
dev->mclk = 96000000;
|
||||
|
||||
switch (dev->cfg->ts_mode) {
|
||||
case M88DS3103_TS_SERIAL:
|
||||
@ -385,14 +385,14 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
|
||||
case M88DS3103_TS_PARALLEL:
|
||||
case M88DS3103_TS_CI:
|
||||
if (c->delivery_system == SYS_DVBS)
|
||||
target_mclk = 96000;
|
||||
target_mclk = 96000000;
|
||||
else {
|
||||
if (c->symbol_rate < 18000000)
|
||||
target_mclk = 96000;
|
||||
target_mclk = 96000000;
|
||||
else if (c->symbol_rate < 28000000)
|
||||
target_mclk = 144000;
|
||||
target_mclk = 144000000;
|
||||
else
|
||||
target_mclk = 192000;
|
||||
target_mclk = 192000000;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -402,15 +402,15 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
|
||||
}
|
||||
|
||||
switch (target_mclk) {
|
||||
case 96000:
|
||||
case 96000000:
|
||||
u8tmp1 = 0x02; /* 0b10 */
|
||||
u8tmp2 = 0x01; /* 0b01 */
|
||||
break;
|
||||
case 144000:
|
||||
case 144000000:
|
||||
u8tmp1 = 0x00; /* 0b00 */
|
||||
u8tmp2 = 0x01; /* 0b01 */
|
||||
break;
|
||||
case 192000:
|
||||
case 192000000:
|
||||
u8tmp1 = 0x03; /* 0b11 */
|
||||
u8tmp2 = 0x00; /* 0b00 */
|
||||
break;
|
||||
@ -464,8 +464,8 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
|
||||
}
|
||||
|
||||
if (dev->chip_id == M88RS6000_CHIP_ID) {
|
||||
if ((c->delivery_system == SYS_DVBS2)
|
||||
&& ((c->symbol_rate / 1000) <= 5000)) {
|
||||
if (c->delivery_system == SYS_DVBS2 &&
|
||||
c->symbol_rate <= 5000000) {
|
||||
ret = regmap_write(dev->regmap, 0xc0, 0x04);
|
||||
if (ret)
|
||||
goto err;
|
||||
@ -522,37 +522,25 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
|
||||
ret = m88ds3103_update_bits(dev, 0x29, 0x20, u8tmp1);
|
||||
if (ret)
|
||||
goto err;
|
||||
u8tmp1 = 0;
|
||||
u8tmp2 = 0;
|
||||
u16tmp = 0;
|
||||
u8tmp1 = 0x3f;
|
||||
u8tmp2 = 0x3f;
|
||||
break;
|
||||
default:
|
||||
if (dev->cfg->ts_clk) {
|
||||
divide_ratio = DIV_ROUND_UP(target_mclk, dev->cfg->ts_clk);
|
||||
u8tmp1 = divide_ratio / 2;
|
||||
u8tmp2 = DIV_ROUND_UP(divide_ratio, 2);
|
||||
}
|
||||
u16tmp = DIV_ROUND_UP(target_mclk, dev->cfg->ts_clk);
|
||||
u8tmp1 = u16tmp / 2 - 1;
|
||||
u8tmp2 = DIV_ROUND_UP(u16tmp, 2) - 1;
|
||||
}
|
||||
|
||||
dev_dbg(&client->dev,
|
||||
"target_mclk=%d ts_clk=%d divide_ratio=%d\n",
|
||||
target_mclk, dev->cfg->ts_clk, divide_ratio);
|
||||
dev_dbg(&client->dev, "target_mclk=%u ts_clk=%u ts_clk_divide_ratio=%u\n",
|
||||
target_mclk, dev->cfg->ts_clk, u16tmp);
|
||||
|
||||
u8tmp1--;
|
||||
u8tmp2--;
|
||||
/* u8tmp1[5:2] => fe[3:0], u8tmp1[1:0] => ea[7:6] */
|
||||
u8tmp1 &= 0x3f;
|
||||
/* u8tmp2[5:0] => ea[5:0] */
|
||||
u8tmp2 &= 0x3f;
|
||||
|
||||
ret = regmap_bulk_read(dev->regmap, 0xfe, &u8tmp, 1);
|
||||
u8tmp = (u8tmp1 >> 2) & 0x0f;
|
||||
ret = regmap_update_bits(dev->regmap, 0xfe, 0x0f, u8tmp);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
u8tmp = ((u8tmp & 0xf0) << 0) | u8tmp1 >> 2;
|
||||
ret = regmap_write(dev->regmap, 0xfe, u8tmp);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
u8tmp = ((u8tmp1 & 0x03) << 6) | u8tmp2 >> 0;
|
||||
ret = regmap_write(dev->regmap, 0xea, u8tmp);
|
||||
if (ret)
|
||||
@ -581,7 +569,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
u16tmp = DIV_ROUND_CLOSEST((c->symbol_rate / 1000) << 15, dev->mclk_khz / 2);
|
||||
u16tmp = DIV_ROUND_CLOSEST_ULL((u64)c->symbol_rate * 0x10000, dev->mclk);
|
||||
buf[0] = (u16tmp >> 0) & 0xff;
|
||||
buf[1] = (u16tmp >> 8) & 0xff;
|
||||
ret = regmap_bulk_write(dev->regmap, 0x61, buf, 2);
|
||||
@ -601,13 +589,11 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
|
||||
goto err;
|
||||
|
||||
dev_dbg(&client->dev, "carrier offset=%d\n",
|
||||
(tuner_frequency - c->frequency));
|
||||
|
||||
s32tmp = 0x10000 * (tuner_frequency - c->frequency);
|
||||
s32tmp = DIV_ROUND_CLOSEST(s32tmp, dev->mclk_khz);
|
||||
if (s32tmp < 0)
|
||||
s32tmp += 0x10000;
|
||||
(tuner_frequency_khz - c->frequency));
|
||||
|
||||
/* Use 32-bit calc as there is no s64 version of DIV_ROUND_CLOSEST() */
|
||||
s32tmp = 0x10000 * (tuner_frequency_khz - c->frequency);
|
||||
s32tmp = DIV_ROUND_CLOSEST(s32tmp, dev->mclk / 1000);
|
||||
buf[0] = (s32tmp >> 0) & 0xff;
|
||||
buf[1] = (s32tmp >> 8) & 0xff;
|
||||
ret = regmap_bulk_write(dev->regmap, 0x5e, buf, 2);
|
||||
@ -635,10 +621,10 @@ static int m88ds3103_init(struct dvb_frontend *fe)
|
||||
struct m88ds3103_dev *dev = fe->demodulator_priv;
|
||||
struct i2c_client *client = dev->client;
|
||||
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
||||
int ret, len, remaining;
|
||||
int ret, len, rem;
|
||||
unsigned int utmp;
|
||||
const struct firmware *fw = NULL;
|
||||
u8 *fw_file;
|
||||
const struct firmware *firmware;
|
||||
const char *name;
|
||||
|
||||
dev_dbg(&client->dev, "\n");
|
||||
|
||||
@ -664,7 +650,7 @@ static int m88ds3103_init(struct dvb_frontend *fe)
|
||||
dev_dbg(&client->dev, "firmware=%02x\n", utmp);
|
||||
|
||||
if (utmp)
|
||||
goto skip_fw_download;
|
||||
goto warm;
|
||||
|
||||
/* global reset, global diseqc reset, golbal fec reset */
|
||||
ret = regmap_write(dev->regmap, 0x07, 0xe0);
|
||||
@ -679,52 +665,47 @@ static int m88ds3103_init(struct dvb_frontend *fe)
|
||||
m88ds3103_ops.info.name);
|
||||
|
||||
if (dev->chip_id == M88RS6000_CHIP_ID)
|
||||
fw_file = M88RS6000_FIRMWARE;
|
||||
name = M88RS6000_FIRMWARE;
|
||||
else
|
||||
fw_file = M88DS3103_FIRMWARE;
|
||||
name = M88DS3103_FIRMWARE;
|
||||
/* request the firmware, this will block and timeout */
|
||||
ret = request_firmware(&fw, fw_file, &client->dev);
|
||||
ret = request_firmware(&firmware, name, &client->dev);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "firmware file '%s' not found\n", fw_file);
|
||||
dev_err(&client->dev, "firmware file '%s' not found\n", name);
|
||||
goto err;
|
||||
}
|
||||
|
||||
dev_info(&client->dev, "downloading firmware from file '%s'\n",
|
||||
fw_file);
|
||||
dev_info(&client->dev, "downloading firmware from file '%s'\n", name);
|
||||
|
||||
ret = regmap_write(dev->regmap, 0xb2, 0x01);
|
||||
if (ret)
|
||||
goto error_fw_release;
|
||||
|
||||
for (remaining = fw->size; remaining > 0;
|
||||
remaining -= (dev->cfg->i2c_wr_max - 1)) {
|
||||
len = remaining;
|
||||
if (len > (dev->cfg->i2c_wr_max - 1))
|
||||
len = (dev->cfg->i2c_wr_max - 1);
|
||||
goto err_release_firmware;
|
||||
|
||||
for (rem = firmware->size; rem > 0; rem -= (dev->cfg->i2c_wr_max - 1)) {
|
||||
len = min(dev->cfg->i2c_wr_max - 1, rem);
|
||||
ret = regmap_bulk_write(dev->regmap, 0xb0,
|
||||
&fw->data[fw->size - remaining], len);
|
||||
&firmware->data[firmware->size - rem],
|
||||
len);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "firmware download failed=%d\n",
|
||||
dev_err(&client->dev, "firmware download failed %d\n",
|
||||
ret);
|
||||
goto error_fw_release;
|
||||
goto err_release_firmware;
|
||||
}
|
||||
}
|
||||
|
||||
ret = regmap_write(dev->regmap, 0xb2, 0x00);
|
||||
if (ret)
|
||||
goto error_fw_release;
|
||||
goto err_release_firmware;
|
||||
|
||||
release_firmware(fw);
|
||||
fw = NULL;
|
||||
release_firmware(firmware);
|
||||
|
||||
ret = regmap_read(dev->regmap, 0xb9, &utmp);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
if (!utmp) {
|
||||
ret = -EINVAL;
|
||||
dev_info(&client->dev, "firmware did not run\n");
|
||||
ret = -EFAULT;
|
||||
goto err;
|
||||
}
|
||||
|
||||
@ -733,7 +714,7 @@ static int m88ds3103_init(struct dvb_frontend *fe)
|
||||
dev_info(&client->dev, "firmware version: %X.%X\n",
|
||||
(utmp >> 4) & 0xf, (utmp >> 0 & 0xf));
|
||||
|
||||
skip_fw_download:
|
||||
warm:
|
||||
/* warm state */
|
||||
dev->warm = true;
|
||||
|
||||
@ -746,8 +727,8 @@ skip_fw_download:
|
||||
c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
|
||||
|
||||
return 0;
|
||||
error_fw_release:
|
||||
release_firmware(fw);
|
||||
err_release_firmware:
|
||||
release_firmware(firmware);
|
||||
err:
|
||||
dev_dbg(&client->dev, "failed=%d\n", ret);
|
||||
return ret;
|
||||
@ -952,8 +933,7 @@ static int m88ds3103_get_frontend(struct dvb_frontend *fe,
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
c->symbol_rate = 1ull * ((buf[1] << 8) | (buf[0] << 0)) *
|
||||
dev->mclk_khz * 1000 / 0x10000;
|
||||
c->symbol_rate = DIV_ROUND_CLOSEST_ULL((u64)(buf[1] << 8 | buf[0] << 0) * dev->mclk, 0x10000);
|
||||
|
||||
return 0;
|
||||
err:
|
||||
@ -1119,8 +1099,9 @@ static int m88ds3103_diseqc_send_master_cmd(struct dvb_frontend *fe,
|
||||
#define SEND_MASTER_CMD_TIMEOUT 120
|
||||
timeout = jiffies + msecs_to_jiffies(SEND_MASTER_CMD_TIMEOUT);
|
||||
|
||||
/* DiSEqC message typical period is 54 ms */
|
||||
usleep_range(50000, 54000);
|
||||
/* DiSEqC message period is 13.5 ms per byte */
|
||||
utmp = diseqc_cmd->msg_len * 13500;
|
||||
usleep_range(utmp - 4000, utmp);
|
||||
|
||||
for (utmp = 1; !time_after(jiffies, timeout) && utmp;) {
|
||||
ret = regmap_read(dev->regmap, 0xa1, &utmp);
|
||||
@ -1395,7 +1376,7 @@ static int m88ds3103_probe(struct i2c_client *client,
|
||||
dev->config.clock = pdata->clk;
|
||||
dev->config.i2c_wr_max = pdata->i2c_wr_max;
|
||||
dev->config.ts_mode = pdata->ts_mode;
|
||||
dev->config.ts_clk = pdata->ts_clk;
|
||||
dev->config.ts_clk = pdata->ts_clk * 1000;
|
||||
dev->config.ts_clk_pol = pdata->ts_clk_pol;
|
||||
dev->config.spec_inv = pdata->spec_inv;
|
||||
dev->config.agc_inv = pdata->agc_inv;
|
||||
@ -1446,6 +1427,11 @@ static int m88ds3103_probe(struct i2c_client *client,
|
||||
goto err_kfree;
|
||||
}
|
||||
|
||||
if (!pdata->ts_clk) {
|
||||
ret = -EINVAL;
|
||||
goto err_kfree;
|
||||
}
|
||||
|
||||
/* 0x29 register is defined differently for m88rs6000. */
|
||||
/* set internal tuner address to 0x21 */
|
||||
if (dev->chip_id == M88RS6000_CHIP_ID)
|
||||
|
@ -27,7 +27,6 @@
|
||||
|
||||
#define M88DS3103_FIRMWARE "dvb-demod-m88ds3103.fw"
|
||||
#define M88RS6000_FIRMWARE "dvb-demod-m88rs6000.fw"
|
||||
#define M88DS3103_MCLK_KHZ 96000
|
||||
#define M88RS6000_CHIP_ID 0x74
|
||||
#define M88DS3103_CHIP_ID 0x70
|
||||
|
||||
@ -46,7 +45,7 @@ struct m88ds3103_dev {
|
||||
/* auto detect chip id to do different config */
|
||||
u8 chip_id;
|
||||
/* main mclk is calculated for M88RS6000 dynamically */
|
||||
s32 mclk_khz;
|
||||
s32 mclk;
|
||||
u64 post_bit_error;
|
||||
u64 post_bit_count;
|
||||
};
|
||||
|
@ -609,7 +609,7 @@ static int m88rs2000_set_frontend(struct dvb_frontend *fe)
|
||||
{
|
||||
struct m88rs2000_state *state = fe->demodulator_priv;
|
||||
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
||||
enum fe_status status;
|
||||
enum fe_status status = 0;
|
||||
int i, ret = 0;
|
||||
u32 tuner_freq;
|
||||
s16 offset = 0;
|
||||
|
@ -301,10 +301,11 @@ static int mb86a20s_read_status(struct dvb_frontend *fe, enum fe_status *status)
|
||||
|
||||
*status = 0;
|
||||
|
||||
val = mb86a20s_readreg(state, 0x0a) & 0xf;
|
||||
val = mb86a20s_readreg(state, 0x0a);
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
val &= 0xf;
|
||||
if (val >= 2)
|
||||
*status |= FE_HAS_SIGNAL;
|
||||
|
||||
|
@ -17,28 +17,90 @@
|
||||
#include "mn88472_priv.h"
|
||||
|
||||
static int mn88472_get_tune_settings(struct dvb_frontend *fe,
|
||||
struct dvb_frontend_tune_settings *s)
|
||||
struct dvb_frontend_tune_settings *s)
|
||||
{
|
||||
s->min_delay_ms = 800;
|
||||
s->min_delay_ms = 1000;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mn88472_read_status(struct dvb_frontend *fe, enum fe_status *status)
|
||||
{
|
||||
struct i2c_client *client = fe->demodulator_priv;
|
||||
struct mn88472_dev *dev = i2c_get_clientdata(client);
|
||||
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
||||
int ret;
|
||||
unsigned int utmp;
|
||||
|
||||
if (!dev->active) {
|
||||
ret = -EAGAIN;
|
||||
goto err;
|
||||
}
|
||||
|
||||
switch (c->delivery_system) {
|
||||
case SYS_DVBT:
|
||||
ret = regmap_read(dev->regmap[0], 0x7f, &utmp);
|
||||
if (ret)
|
||||
goto err;
|
||||
if ((utmp & 0x0f) >= 0x09)
|
||||
*status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
|
||||
FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
|
||||
else
|
||||
*status = 0;
|
||||
break;
|
||||
case SYS_DVBT2:
|
||||
ret = regmap_read(dev->regmap[2], 0x92, &utmp);
|
||||
if (ret)
|
||||
goto err;
|
||||
if ((utmp & 0x0f) >= 0x0d)
|
||||
*status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
|
||||
FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
|
||||
else if ((utmp & 0x0f) >= 0x0a)
|
||||
*status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
|
||||
FE_HAS_VITERBI;
|
||||
else if ((utmp & 0x0f) >= 0x07)
|
||||
*status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
|
||||
else
|
||||
*status = 0;
|
||||
break;
|
||||
case SYS_DVBC_ANNEX_A:
|
||||
ret = regmap_read(dev->regmap[1], 0x84, &utmp);
|
||||
if (ret)
|
||||
goto err;
|
||||
if ((utmp & 0x0f) >= 0x08)
|
||||
*status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
|
||||
FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
|
||||
else
|
||||
*status = 0;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
dev_dbg(&client->dev, "failed=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mn88472_set_frontend(struct dvb_frontend *fe)
|
||||
{
|
||||
struct i2c_client *client = fe->demodulator_priv;
|
||||
struct mn88472_dev *dev = i2c_get_clientdata(client);
|
||||
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
||||
int ret, i;
|
||||
u32 if_frequency = 0;
|
||||
u64 tmp;
|
||||
u8 delivery_system_val, if_val[3], bw_val[7], bw_val2;
|
||||
unsigned int utmp;
|
||||
u32 if_frequency;
|
||||
u8 buf[3], delivery_system_val, bandwidth_val, *bandwidth_vals_ptr;
|
||||
u8 reg_bank0_b4_val, reg_bank0_cd_val, reg_bank0_d4_val;
|
||||
u8 reg_bank0_d6_val;
|
||||
|
||||
dev_dbg(&client->dev,
|
||||
"delivery_system=%d modulation=%d frequency=%d symbol_rate=%d inversion=%d\n",
|
||||
c->delivery_system, c->modulation,
|
||||
c->frequency, c->symbol_rate, c->inversion);
|
||||
"delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%d stream_id=%d\n",
|
||||
c->delivery_system, c->modulation, c->frequency,
|
||||
c->bandwidth_hz, c->symbol_rate, c->inversion, c->stream_id);
|
||||
|
||||
if (!dev->warm) {
|
||||
if (!dev->active) {
|
||||
ret = -EAGAIN;
|
||||
goto err;
|
||||
}
|
||||
@ -46,39 +108,64 @@ static int mn88472_set_frontend(struct dvb_frontend *fe)
|
||||
switch (c->delivery_system) {
|
||||
case SYS_DVBT:
|
||||
delivery_system_val = 0x02;
|
||||
reg_bank0_b4_val = 0x00;
|
||||
reg_bank0_cd_val = 0x1f;
|
||||
reg_bank0_d4_val = 0x0a;
|
||||
reg_bank0_d6_val = 0x48;
|
||||
break;
|
||||
case SYS_DVBT2:
|
||||
delivery_system_val = 0x03;
|
||||
reg_bank0_b4_val = 0xf6;
|
||||
reg_bank0_cd_val = 0x01;
|
||||
reg_bank0_d4_val = 0x09;
|
||||
reg_bank0_d6_val = 0x46;
|
||||
break;
|
||||
case SYS_DVBC_ANNEX_A:
|
||||
delivery_system_val = 0x04;
|
||||
reg_bank0_b4_val = 0x00;
|
||||
reg_bank0_cd_val = 0x17;
|
||||
reg_bank0_d4_val = 0x09;
|
||||
reg_bank0_d6_val = 0x48;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (c->bandwidth_hz <= 5000000) {
|
||||
memcpy(bw_val, "\xe5\x99\x9a\x1b\xa9\x1b\xa9", 7);
|
||||
bw_val2 = 0x03;
|
||||
} else if (c->bandwidth_hz <= 6000000) {
|
||||
/* IF 3570000 Hz, BW 6000000 Hz */
|
||||
memcpy(bw_val, "\xbf\x55\x55\x15\x6b\x15\x6b", 7);
|
||||
bw_val2 = 0x02;
|
||||
} else if (c->bandwidth_hz <= 7000000) {
|
||||
/* IF 4570000 Hz, BW 7000000 Hz */
|
||||
memcpy(bw_val, "\xa4\x00\x00\x0f\x2c\x0f\x2c", 7);
|
||||
bw_val2 = 0x01;
|
||||
} else if (c->bandwidth_hz <= 8000000) {
|
||||
/* IF 4570000 Hz, BW 8000000 Hz */
|
||||
memcpy(bw_val, "\x8f\x80\x00\x08\xee\x08\xee", 7);
|
||||
bw_val2 = 0x00;
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
switch (c->delivery_system) {
|
||||
case SYS_DVBT:
|
||||
case SYS_DVBT2:
|
||||
switch (c->bandwidth_hz) {
|
||||
case 5000000:
|
||||
bandwidth_vals_ptr = "\xe5\x99\x9a\x1b\xa9\x1b\xa9";
|
||||
bandwidth_val = 0x03;
|
||||
break;
|
||||
case 6000000:
|
||||
bandwidth_vals_ptr = "\xbf\x55\x55\x15\x6b\x15\x6b";
|
||||
bandwidth_val = 0x02;
|
||||
break;
|
||||
case 7000000:
|
||||
bandwidth_vals_ptr = "\xa4\x00\x00\x0f\x2c\x0f\x2c";
|
||||
bandwidth_val = 0x01;
|
||||
break;
|
||||
case 8000000:
|
||||
bandwidth_vals_ptr = "\x8f\x80\x00\x08\xee\x08\xee";
|
||||
bandwidth_val = 0x00;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
break;
|
||||
case SYS_DVBC_ANNEX_A:
|
||||
bandwidth_vals_ptr = NULL;
|
||||
bandwidth_val = 0x00;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* program tuner */
|
||||
/* Program tuner */
|
||||
if (fe->ops.tuner_ops.set_params) {
|
||||
ret = fe->ops.tuner_ops.set_params(fe);
|
||||
if (ret)
|
||||
@ -91,20 +178,10 @@ static int mn88472_set_frontend(struct dvb_frontend *fe)
|
||||
goto err;
|
||||
|
||||
dev_dbg(&client->dev, "get_if_frequency=%d\n", if_frequency);
|
||||
}
|
||||
|
||||
/* Calculate IF registers ( (1<<24)*IF / Xtal ) */
|
||||
tmp = div_u64(if_frequency * (u64)(1<<24) + (dev->xtal / 2),
|
||||
dev->xtal);
|
||||
if_val[0] = (tmp >> 16) & 0xff;
|
||||
if_val[1] = (tmp >> 8) & 0xff;
|
||||
if_val[2] = (tmp >> 0) & 0xff;
|
||||
|
||||
ret = regmap_write(dev->regmap[2], 0xfb, 0x13);
|
||||
ret = regmap_write(dev->regmap[2], 0xef, 0x13);
|
||||
ret = regmap_write(dev->regmap[2], 0xf9, 0x13);
|
||||
if (ret)
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = regmap_write(dev->regmap[2], 0x00, 0x66);
|
||||
if (ret)
|
||||
@ -118,157 +195,81 @@ static int mn88472_set_frontend(struct dvb_frontend *fe)
|
||||
ret = regmap_write(dev->regmap[2], 0x03, delivery_system_val);
|
||||
if (ret)
|
||||
goto err;
|
||||
ret = regmap_write(dev->regmap[2], 0x04, bw_val2);
|
||||
ret = regmap_write(dev->regmap[2], 0x04, bandwidth_val);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
for (i = 0; i < sizeof(if_val); i++) {
|
||||
ret = regmap_write(dev->regmap[2], 0x10 + i, if_val[i]);
|
||||
/* IF */
|
||||
utmp = DIV_ROUND_CLOSEST_ULL((u64)if_frequency * 0x1000000, dev->clk);
|
||||
buf[0] = (utmp >> 16) & 0xff;
|
||||
buf[1] = (utmp >> 8) & 0xff;
|
||||
buf[2] = (utmp >> 0) & 0xff;
|
||||
for (i = 0; i < 3; i++) {
|
||||
ret = regmap_write(dev->regmap[2], 0x10 + i, buf[i]);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
for (i = 0; i < sizeof(bw_val); i++) {
|
||||
ret = regmap_write(dev->regmap[2], 0x13 + i, bw_val[i]);
|
||||
if (ret)
|
||||
goto err;
|
||||
/* Bandwidth */
|
||||
if (bandwidth_vals_ptr) {
|
||||
for (i = 0; i < 7; i++) {
|
||||
ret = regmap_write(dev->regmap[2], 0x13 + i,
|
||||
bandwidth_vals_ptr[i]);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
ret = regmap_write(dev->regmap[0], 0xb4, reg_bank0_b4_val);
|
||||
if (ret)
|
||||
goto err;
|
||||
ret = regmap_write(dev->regmap[0], 0xcd, reg_bank0_cd_val);
|
||||
if (ret)
|
||||
goto err;
|
||||
ret = regmap_write(dev->regmap[0], 0xd4, reg_bank0_d4_val);
|
||||
if (ret)
|
||||
goto err;
|
||||
ret = regmap_write(dev->regmap[0], 0xd6, reg_bank0_d6_val);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
switch (c->delivery_system) {
|
||||
case SYS_DVBT:
|
||||
ret = regmap_write(dev->regmap[0], 0x07, 0x26);
|
||||
ret = regmap_write(dev->regmap[0], 0xb0, 0x0a);
|
||||
ret = regmap_write(dev->regmap[0], 0xb4, 0x00);
|
||||
ret = regmap_write(dev->regmap[0], 0xcd, 0x1f);
|
||||
ret = regmap_write(dev->regmap[0], 0xd4, 0x0a);
|
||||
ret = regmap_write(dev->regmap[0], 0xd6, 0x48);
|
||||
if (ret)
|
||||
goto err;
|
||||
ret = regmap_write(dev->regmap[0], 0x00, 0xba);
|
||||
if (ret)
|
||||
goto err;
|
||||
ret = regmap_write(dev->regmap[0], 0x01, 0x13);
|
||||
if (ret)
|
||||
goto err;
|
||||
break;
|
||||
case SYS_DVBT2:
|
||||
ret = regmap_write(dev->regmap[2], 0x2b, 0x13);
|
||||
if (ret)
|
||||
goto err;
|
||||
ret = regmap_write(dev->regmap[2], 0x4f, 0x05);
|
||||
if (ret)
|
||||
goto err;
|
||||
ret = regmap_write(dev->regmap[1], 0xf6, 0x05);
|
||||
ret = regmap_write(dev->regmap[0], 0xb0, 0x0a);
|
||||
ret = regmap_write(dev->regmap[0], 0xb4, 0xf6);
|
||||
ret = regmap_write(dev->regmap[0], 0xcd, 0x01);
|
||||
ret = regmap_write(dev->regmap[0], 0xd4, 0x09);
|
||||
ret = regmap_write(dev->regmap[0], 0xd6, 0x46);
|
||||
ret = regmap_write(dev->regmap[2], 0x30, 0x80);
|
||||
ret = regmap_write(dev->regmap[2], 0x32, 0x00);
|
||||
if (ret)
|
||||
goto err;
|
||||
ret = regmap_write(dev->regmap[2], 0x32, c->stream_id);
|
||||
if (ret)
|
||||
goto err;
|
||||
break;
|
||||
case SYS_DVBC_ANNEX_A:
|
||||
ret = regmap_write(dev->regmap[0], 0xb0, 0x0b);
|
||||
ret = regmap_write(dev->regmap[0], 0xb4, 0x00);
|
||||
ret = regmap_write(dev->regmap[0], 0xcd, 0x17);
|
||||
ret = regmap_write(dev->regmap[0], 0xd4, 0x09);
|
||||
ret = regmap_write(dev->regmap[0], 0xd6, 0x48);
|
||||
ret = regmap_write(dev->regmap[1], 0x00, 0xb0);
|
||||
if (ret)
|
||||
goto err;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = regmap_write(dev->regmap[0], 0x46, 0x00);
|
||||
ret = regmap_write(dev->regmap[0], 0xae, 0x00);
|
||||
|
||||
switch (dev->ts_mode) {
|
||||
case SERIAL_TS_MODE:
|
||||
ret = regmap_write(dev->regmap[2], 0x08, 0x1d);
|
||||
break;
|
||||
case PARALLEL_TS_MODE:
|
||||
ret = regmap_write(dev->regmap[2], 0x08, 0x00);
|
||||
break;
|
||||
default:
|
||||
dev_dbg(&client->dev, "ts_mode error: %d\n", dev->ts_mode);
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
switch (dev->ts_clock) {
|
||||
case VARIABLE_TS_CLOCK:
|
||||
ret = regmap_write(dev->regmap[0], 0xd9, 0xe3);
|
||||
break;
|
||||
case FIXED_TS_CLOCK:
|
||||
ret = regmap_write(dev->regmap[0], 0xd9, 0xe1);
|
||||
break;
|
||||
default:
|
||||
dev_dbg(&client->dev, "ts_clock error: %d\n", dev->ts_clock);
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Reset demod */
|
||||
/* Reset FSM */
|
||||
ret = regmap_write(dev->regmap[2], 0xf8, 0x9f);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
dev->delivery_system = c->delivery_system;
|
||||
|
||||
return 0;
|
||||
err:
|
||||
dev_dbg(&client->dev, "failed=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mn88472_read_status(struct dvb_frontend *fe, enum fe_status *status)
|
||||
{
|
||||
struct i2c_client *client = fe->demodulator_priv;
|
||||
struct mn88472_dev *dev = i2c_get_clientdata(client);
|
||||
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
||||
int ret;
|
||||
unsigned int utmp;
|
||||
int lock = 0;
|
||||
|
||||
*status = 0;
|
||||
|
||||
if (!dev->warm) {
|
||||
ret = -EAGAIN;
|
||||
goto err;
|
||||
}
|
||||
|
||||
switch (c->delivery_system) {
|
||||
case SYS_DVBT:
|
||||
ret = regmap_read(dev->regmap[0], 0x7F, &utmp);
|
||||
if (ret)
|
||||
goto err;
|
||||
if ((utmp & 0xF) >= 0x09)
|
||||
lock = 1;
|
||||
break;
|
||||
case SYS_DVBT2:
|
||||
ret = regmap_read(dev->regmap[2], 0x92, &utmp);
|
||||
if (ret)
|
||||
goto err;
|
||||
if ((utmp & 0xF) >= 0x07)
|
||||
*status |= FE_HAS_SIGNAL;
|
||||
if ((utmp & 0xF) >= 0x0a)
|
||||
*status |= FE_HAS_CARRIER;
|
||||
if ((utmp & 0xF) >= 0x0d)
|
||||
*status |= FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
|
||||
break;
|
||||
case SYS_DVBC_ANNEX_A:
|
||||
ret = regmap_read(dev->regmap[1], 0x84, &utmp);
|
||||
if (ret)
|
||||
goto err;
|
||||
if ((utmp & 0xF) >= 0x08)
|
||||
lock = 1;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (lock)
|
||||
*status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI |
|
||||
FE_HAS_SYNC | FE_HAS_LOCK;
|
||||
|
||||
return 0;
|
||||
err:
|
||||
dev_dbg(&client->dev, "failed=%d\n", ret);
|
||||
@ -279,93 +280,107 @@ static int mn88472_init(struct dvb_frontend *fe)
|
||||
{
|
||||
struct i2c_client *client = fe->demodulator_priv;
|
||||
struct mn88472_dev *dev = i2c_get_clientdata(client);
|
||||
int ret, len, remaining;
|
||||
const struct firmware *fw = NULL;
|
||||
u8 *fw_file = MN88472_FIRMWARE;
|
||||
unsigned int tmp;
|
||||
int ret, len, rem;
|
||||
unsigned int utmp;
|
||||
const struct firmware *firmware;
|
||||
const char *name = MN88472_FIRMWARE;
|
||||
|
||||
dev_dbg(&client->dev, "\n");
|
||||
|
||||
/* set cold state by default */
|
||||
dev->warm = false;
|
||||
|
||||
/* power on */
|
||||
/* Power up */
|
||||
ret = regmap_write(dev->regmap[2], 0x05, 0x00);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = regmap_bulk_write(dev->regmap[2], 0x0b, "\x00\x00", 2);
|
||||
ret = regmap_write(dev->regmap[2], 0x0b, 0x00);
|
||||
if (ret)
|
||||
goto err;
|
||||
ret = regmap_write(dev->regmap[2], 0x0c, 0x00);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
/* check if firmware is already running */
|
||||
ret = regmap_read(dev->regmap[0], 0xf5, &tmp);
|
||||
/* Check if firmware is already running */
|
||||
ret = regmap_read(dev->regmap[0], 0xf5, &utmp);
|
||||
if (ret)
|
||||
goto err;
|
||||
if (!(utmp & 0x01))
|
||||
goto warm;
|
||||
|
||||
if (!(tmp & 0x1)) {
|
||||
dev_info(&client->dev, "firmware already running\n");
|
||||
dev->warm = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* request the firmware, this will block and timeout */
|
||||
ret = request_firmware(&fw, fw_file, &client->dev);
|
||||
ret = request_firmware(&firmware, name, &client->dev);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "firmare file '%s' not found\n",
|
||||
fw_file);
|
||||
dev_err(&client->dev, "firmware file '%s' not found\n", name);
|
||||
goto err;
|
||||
}
|
||||
|
||||
dev_info(&client->dev, "downloading firmware from file '%s'\n",
|
||||
fw_file);
|
||||
dev_info(&client->dev, "downloading firmware from file '%s'\n", name);
|
||||
|
||||
ret = regmap_write(dev->regmap[0], 0xf5, 0x03);
|
||||
if (ret)
|
||||
goto firmware_release;
|
||||
|
||||
for (remaining = fw->size; remaining > 0;
|
||||
remaining -= (dev->i2c_wr_max - 1)) {
|
||||
len = remaining;
|
||||
if (len > (dev->i2c_wr_max - 1))
|
||||
len = dev->i2c_wr_max - 1;
|
||||
goto err_release_firmware;
|
||||
|
||||
for (rem = firmware->size; rem > 0; rem -= (dev->i2c_write_max - 1)) {
|
||||
len = min(dev->i2c_write_max - 1, rem);
|
||||
ret = regmap_bulk_write(dev->regmap[0], 0xf6,
|
||||
&fw->data[fw->size - remaining], len);
|
||||
&firmware->data[firmware->size - rem],
|
||||
len);
|
||||
if (ret) {
|
||||
dev_err(&client->dev,
|
||||
"firmware download failed=%d\n", ret);
|
||||
goto firmware_release;
|
||||
dev_err(&client->dev, "firmware download failed %d\n",
|
||||
ret);
|
||||
goto err_release_firmware;
|
||||
}
|
||||
}
|
||||
|
||||
/* parity check of firmware */
|
||||
ret = regmap_read(dev->regmap[0], 0xf8, &tmp);
|
||||
if (ret) {
|
||||
dev_err(&client->dev,
|
||||
"parity reg read failed=%d\n", ret);
|
||||
goto firmware_release;
|
||||
/* Parity check of firmware */
|
||||
ret = regmap_read(dev->regmap[0], 0xf8, &utmp);
|
||||
if (ret)
|
||||
goto err_release_firmware;
|
||||
if (utmp & 0x10) {
|
||||
ret = -EINVAL;
|
||||
dev_err(&client->dev, "firmware did not run\n");
|
||||
goto err_release_firmware;
|
||||
}
|
||||
if (tmp & 0x10) {
|
||||
dev_err(&client->dev,
|
||||
"firmware parity check failed=0x%x\n", tmp);
|
||||
goto firmware_release;
|
||||
}
|
||||
dev_err(&client->dev, "firmware parity check succeeded=0x%x\n", tmp);
|
||||
|
||||
ret = regmap_write(dev->regmap[0], 0xf5, 0x00);
|
||||
if (ret)
|
||||
goto firmware_release;
|
||||
goto err_release_firmware;
|
||||
|
||||
release_firmware(fw);
|
||||
fw = NULL;
|
||||
release_firmware(firmware);
|
||||
warm:
|
||||
/* TS config */
|
||||
switch (dev->ts_mode) {
|
||||
case SERIAL_TS_MODE:
|
||||
utmp = 0x1d;
|
||||
break;
|
||||
case PARALLEL_TS_MODE:
|
||||
utmp = 0x00;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
ret = regmap_write(dev->regmap[2], 0x08, utmp);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
/* warm state */
|
||||
dev->warm = true;
|
||||
switch (dev->ts_clk) {
|
||||
case VARIABLE_TS_CLOCK:
|
||||
utmp = 0xe3;
|
||||
break;
|
||||
case FIXED_TS_CLOCK:
|
||||
utmp = 0xe1;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
ret = regmap_write(dev->regmap[0], 0xd9, utmp);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
dev->active = true;
|
||||
|
||||
return 0;
|
||||
firmware_release:
|
||||
release_firmware(fw);
|
||||
err_release_firmware:
|
||||
release_firmware(firmware);
|
||||
err:
|
||||
dev_dbg(&client->dev, "failed=%d\n", ret);
|
||||
return ret;
|
||||
@ -379,18 +394,17 @@ static int mn88472_sleep(struct dvb_frontend *fe)
|
||||
|
||||
dev_dbg(&client->dev, "\n");
|
||||
|
||||
/* power off */
|
||||
ret = regmap_write(dev->regmap[2], 0x0b, 0x30);
|
||||
|
||||
/* Power down */
|
||||
ret = regmap_write(dev->regmap[2], 0x0c, 0x30);
|
||||
if (ret)
|
||||
goto err;
|
||||
ret = regmap_write(dev->regmap[2], 0x0b, 0x30);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = regmap_write(dev->regmap[2], 0x05, 0x3e);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
dev->delivery_system = SYS_UNDEFINED;
|
||||
|
||||
return 0;
|
||||
err:
|
||||
dev_dbg(&client->dev, "failed=%d\n", ret);
|
||||
@ -434,10 +448,19 @@ static struct dvb_frontend_ops mn88472_ops = {
|
||||
.read_status = mn88472_read_status,
|
||||
};
|
||||
|
||||
static int mn88472_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static struct dvb_frontend *mn88472_get_dvb_frontend(struct i2c_client *client)
|
||||
{
|
||||
struct mn88472_config *config = client->dev.platform_data;
|
||||
struct mn88472_dev *dev = i2c_get_clientdata(client);
|
||||
|
||||
dev_dbg(&client->dev, "\n");
|
||||
|
||||
return &dev->fe;
|
||||
}
|
||||
|
||||
static int mn88472_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct mn88472_config *pdata = client->dev.platform_data;
|
||||
struct mn88472_dev *dev;
|
||||
int ret;
|
||||
unsigned int utmp;
|
||||
@ -448,23 +471,16 @@ static int mn88472_probe(struct i2c_client *client,
|
||||
|
||||
dev_dbg(&client->dev, "\n");
|
||||
|
||||
/* Caller really need to provide pointer for frontend we create. */
|
||||
if (config->fe == NULL) {
|
||||
dev_err(&client->dev, "frontend pointer not defined\n");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
||||
if (!dev) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
dev->i2c_wr_max = config->i2c_wr_max;
|
||||
dev->xtal = config->xtal;
|
||||
dev->ts_mode = config->ts_mode;
|
||||
dev->ts_clock = config->ts_clock;
|
||||
dev->i2c_write_max = pdata->i2c_wr_max ? pdata->i2c_wr_max : ~0;
|
||||
dev->clk = pdata->xtal;
|
||||
dev->ts_mode = pdata->ts_mode;
|
||||
dev->ts_clk = pdata->ts_clock;
|
||||
dev->client[0] = client;
|
||||
dev->regmap[0] = regmap_init_i2c(dev->client[0], ®map_config);
|
||||
if (IS_ERR(dev->regmap[0])) {
|
||||
@ -472,15 +488,25 @@ static int mn88472_probe(struct i2c_client *client,
|
||||
goto err_kfree;
|
||||
}
|
||||
|
||||
/* check demod answers to I2C */
|
||||
ret = regmap_read(dev->regmap[0], 0x00, &utmp);
|
||||
/* Check demod answers with correct chip id */
|
||||
ret = regmap_read(dev->regmap[0], 0xff, &utmp);
|
||||
if (ret)
|
||||
goto err_regmap_0_regmap_exit;
|
||||
|
||||
dev_dbg(&client->dev, "chip id=%02x\n", utmp);
|
||||
|
||||
if (utmp != 0x02) {
|
||||
ret = -ENODEV;
|
||||
goto err_regmap_0_regmap_exit;
|
||||
}
|
||||
|
||||
/*
|
||||
* Chip has three I2C addresses for different register pages. Used
|
||||
* Chip has three I2C addresses for different register banks. Used
|
||||
* addresses are 0x18, 0x1a and 0x1c. We register two dummy clients,
|
||||
* 0x1a and 0x1c, in order to get own I2C client for each register page.
|
||||
* 0x1a and 0x1c, in order to get own I2C client for each register bank.
|
||||
*
|
||||
* Also, register bank 2 do not support sequential I/O. Only single
|
||||
* register write or read is allowed to that bank.
|
||||
*/
|
||||
dev->client[1] = i2c_new_dummy(client->adapter, 0x1a);
|
||||
if (!dev->client[1]) {
|
||||
@ -510,15 +536,25 @@ static int mn88472_probe(struct i2c_client *client,
|
||||
}
|
||||
i2c_set_clientdata(dev->client[2], dev);
|
||||
|
||||
/* create dvb_frontend */
|
||||
/* Sleep because chip is active by default */
|
||||
ret = regmap_write(dev->regmap[2], 0x05, 0x3e);
|
||||
if (ret)
|
||||
goto err_regmap_2_regmap_exit;
|
||||
|
||||
/* Create dvb frontend */
|
||||
memcpy(&dev->fe.ops, &mn88472_ops, sizeof(struct dvb_frontend_ops));
|
||||
dev->fe.demodulator_priv = client;
|
||||
*config->fe = &dev->fe;
|
||||
*pdata->fe = &dev->fe;
|
||||
i2c_set_clientdata(client, dev);
|
||||
|
||||
dev_info(&client->dev, "Panasonic MN88472 successfully attached\n");
|
||||
return 0;
|
||||
/* Setup callbacks */
|
||||
pdata->get_dvb_frontend = mn88472_get_dvb_frontend;
|
||||
|
||||
dev_info(&client->dev, "Panasonic MN88472 successfully identified\n");
|
||||
|
||||
return 0;
|
||||
err_regmap_2_regmap_exit:
|
||||
regmap_exit(dev->regmap[2]);
|
||||
err_client_2_i2c_unregister_device:
|
||||
i2c_unregister_device(dev->client[2]);
|
||||
err_regmap_1_regmap_exit:
|
||||
@ -561,11 +597,12 @@ MODULE_DEVICE_TABLE(i2c, mn88472_id_table);
|
||||
|
||||
static struct i2c_driver mn88472_driver = {
|
||||
.driver = {
|
||||
.name = "mn88472",
|
||||
.name = "mn88472",
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.probe = mn88472_probe,
|
||||
.remove = mn88472_remove,
|
||||
.id_table = mn88472_id_table,
|
||||
.probe = mn88472_probe,
|
||||
.remove = mn88472_remove,
|
||||
.id_table = mn88472_id_table,
|
||||
};
|
||||
|
||||
module_i2c_driver(mn88472_driver);
|
@ -19,23 +19,33 @@
|
||||
|
||||
#include <linux/dvb/frontend.h>
|
||||
|
||||
enum ts_clock {
|
||||
VARIABLE_TS_CLOCK,
|
||||
FIXED_TS_CLOCK,
|
||||
};
|
||||
/**
|
||||
* struct mn88472_config - Platform data for the mn88472 driver
|
||||
* @xtal: Clock frequency.
|
||||
* @ts_mode: TS mode.
|
||||
* @ts_clock: TS clock config.
|
||||
* @i2c_wr_max: Max number of bytes driver writes to I2C at once.
|
||||
* @get_dvb_frontend: Get DVB frontend.
|
||||
*/
|
||||
|
||||
enum ts_mode {
|
||||
SERIAL_TS_MODE,
|
||||
PARALLEL_TS_MODE,
|
||||
};
|
||||
/* Define old names for backward compatibility */
|
||||
#define VARIABLE_TS_CLOCK MN88472_TS_CLK_VARIABLE
|
||||
#define FIXED_TS_CLOCK MN88472_TS_CLK_FIXED
|
||||
#define SERIAL_TS_MODE MN88472_TS_MODE_SERIAL
|
||||
#define PARALLEL_TS_MODE MN88472_TS_MODE_PARALLEL
|
||||
|
||||
struct mn88472_config {
|
||||
/*
|
||||
* Max num of bytes given I2C adapter could write at once.
|
||||
* Default: none
|
||||
*/
|
||||
u16 i2c_wr_max;
|
||||
unsigned int xtal;
|
||||
|
||||
#define MN88472_TS_MODE_SERIAL 0
|
||||
#define MN88472_TS_MODE_PARALLEL 1
|
||||
int ts_mode;
|
||||
|
||||
#define MN88472_TS_CLK_FIXED 0
|
||||
#define MN88472_TS_CLK_VARIABLE 1
|
||||
int ts_clock;
|
||||
|
||||
u16 i2c_wr_max;
|
||||
|
||||
/* Everything after that is returned by the driver. */
|
||||
|
||||
@ -43,14 +53,7 @@ struct mn88472_config {
|
||||
* DVB frontend.
|
||||
*/
|
||||
struct dvb_frontend **fe;
|
||||
|
||||
/*
|
||||
* Xtal frequency.
|
||||
* Hz
|
||||
*/
|
||||
u32 xtal;
|
||||
int ts_mode;
|
||||
int ts_clock;
|
||||
struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -28,12 +28,11 @@ struct mn88472_dev {
|
||||
struct i2c_client *client[3];
|
||||
struct regmap *regmap[3];
|
||||
struct dvb_frontend fe;
|
||||
u16 i2c_wr_max;
|
||||
enum fe_delivery_system delivery_system;
|
||||
bool warm; /* FW running */
|
||||
u32 xtal;
|
||||
int ts_mode;
|
||||
int ts_clock;
|
||||
u16 i2c_write_max;
|
||||
unsigned int clk;
|
||||
unsigned int active:1;
|
||||
unsigned int ts_mode:1;
|
||||
unsigned int ts_clk:1;
|
||||
};
|
||||
|
||||
#endif
|
@ -330,7 +330,7 @@ static int mn88473_init(struct dvb_frontend *fe)
|
||||
/* Request the firmware, this will block and timeout */
|
||||
ret = request_firmware(&fw, name, &client->dev);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "firmare file '%s' not found\n", name);
|
||||
dev_err(&client->dev, "firmware file '%s' not found\n", name);
|
||||
goto err;
|
||||
}
|
||||
|
||||
@ -536,7 +536,7 @@ static int mn88473_probe(struct i2c_client *client,
|
||||
/* Sleep because chip is active by default */
|
||||
ret = regmap_write(dev->regmap[2], 0x05, 0x3e);
|
||||
if (ret)
|
||||
goto err_client_2_i2c_unregister_device;
|
||||
goto err_regmap_2_regmap_exit;
|
||||
|
||||
/* Create dvb frontend */
|
||||
memcpy(&dev->frontend.ops, &mn88473_ops, sizeof(dev->frontend.ops));
|
||||
@ -547,7 +547,8 @@ static int mn88473_probe(struct i2c_client *client,
|
||||
dev_info(&client->dev, "Panasonic MN88473 successfully identified\n");
|
||||
|
||||
return 0;
|
||||
|
||||
err_regmap_2_regmap_exit:
|
||||
regmap_exit(dev->regmap[2]);
|
||||
err_client_2_i2c_unregister_device:
|
||||
i2c_unregister_device(dev->client[2]);
|
||||
err_regmap_1_regmap_exit:
|
||||
|
@ -135,8 +135,6 @@ static int rtl2830_init(struct dvb_frontend *fe)
|
||||
c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
|
||||
c->post_bit_count.len = 1;
|
||||
c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
|
||||
/* start statistics polling */
|
||||
schedule_delayed_work(&dev->stat_work, msecs_to_jiffies(2000));
|
||||
|
||||
dev->sleeping = false;
|
||||
|
||||
@ -152,8 +150,6 @@ static int rtl2830_sleep(struct dvb_frontend *fe)
|
||||
struct rtl2830_dev *dev = i2c_get_clientdata(client);
|
||||
|
||||
dev->sleeping = true;
|
||||
/* stop statistics polling */
|
||||
cancel_delayed_work_sync(&dev->stat_work);
|
||||
dev->fe_status = 0;
|
||||
|
||||
return 0;
|
||||
@ -396,8 +392,10 @@ static int rtl2830_read_status(struct dvb_frontend *fe, enum fe_status *status)
|
||||
{
|
||||
struct i2c_client *client = fe->demodulator_priv;
|
||||
struct rtl2830_dev *dev = i2c_get_clientdata(client);
|
||||
int ret;
|
||||
u8 u8tmp;
|
||||
struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache;
|
||||
int ret, stmp;
|
||||
unsigned int utmp;
|
||||
u8 u8tmp, buf[2];
|
||||
|
||||
*status = 0;
|
||||
|
||||
@ -419,6 +417,89 @@ static int rtl2830_read_status(struct dvb_frontend *fe, enum fe_status *status)
|
||||
|
||||
dev->fe_status = *status;
|
||||
|
||||
/* Signal strength */
|
||||
if (dev->fe_status & FE_HAS_SIGNAL) {
|
||||
/* Read IF AGC */
|
||||
ret = rtl2830_bulk_read(client, 0x359, buf, 2);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
stmp = buf[0] << 8 | buf[1] << 0;
|
||||
stmp = sign_extend32(stmp, 13);
|
||||
utmp = clamp_val(-4 * stmp + 32767, 0x0000, 0xffff);
|
||||
|
||||
dev_dbg(&client->dev, "IF AGC=%d\n", stmp);
|
||||
|
||||
c->strength.stat[0].scale = FE_SCALE_RELATIVE;
|
||||
c->strength.stat[0].uvalue = utmp;
|
||||
} else {
|
||||
c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
/* CNR */
|
||||
if (dev->fe_status & FE_HAS_VITERBI) {
|
||||
unsigned int hierarchy, constellation;
|
||||
#define CONSTELLATION_NUM 3
|
||||
#define HIERARCHY_NUM 4
|
||||
static const u32 constant[CONSTELLATION_NUM][HIERARCHY_NUM] = {
|
||||
{70705899, 70705899, 70705899, 70705899},
|
||||
{82433173, 82433173, 87483115, 94445660},
|
||||
{92888734, 92888734, 95487525, 99770748},
|
||||
};
|
||||
|
||||
ret = rtl2830_bulk_read(client, 0x33c, &u8tmp, 1);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
constellation = (u8tmp >> 2) & 0x03; /* [3:2] */
|
||||
if (constellation > CONSTELLATION_NUM - 1)
|
||||
goto err;
|
||||
|
||||
hierarchy = (u8tmp >> 4) & 0x07; /* [6:4] */
|
||||
if (hierarchy > HIERARCHY_NUM - 1)
|
||||
goto err;
|
||||
|
||||
ret = rtl2830_bulk_read(client, 0x40c, buf, 2);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
utmp = buf[0] << 8 | buf[1] << 0;
|
||||
if (utmp)
|
||||
stmp = (constant[constellation][hierarchy] -
|
||||
intlog10(utmp)) / ((1 << 24) / 10000);
|
||||
else
|
||||
stmp = 0;
|
||||
|
||||
dev_dbg(&client->dev, "CNR raw=%u\n", utmp);
|
||||
|
||||
c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
|
||||
c->cnr.stat[0].svalue = stmp;
|
||||
} else {
|
||||
c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
/* BER */
|
||||
if (dev->fe_status & FE_HAS_LOCK) {
|
||||
ret = rtl2830_bulk_read(client, 0x34e, buf, 2);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
utmp = buf[0] << 8 | buf[1] << 0;
|
||||
dev->post_bit_error += utmp;
|
||||
dev->post_bit_count += 1000000;
|
||||
|
||||
dev_dbg(&client->dev, "BER errors=%u total=1000000\n", utmp);
|
||||
|
||||
c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
|
||||
c->post_bit_error.stat[0].uvalue = dev->post_bit_error;
|
||||
c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
|
||||
c->post_bit_count.stat[0].uvalue = dev->post_bit_count;
|
||||
} else {
|
||||
c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
|
||||
c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
|
||||
return ret;
|
||||
err:
|
||||
dev_dbg(&client->dev, "failed=%d\n", ret);
|
||||
@ -503,109 +584,6 @@ static struct dvb_frontend_ops rtl2830_ops = {
|
||||
.read_signal_strength = rtl2830_read_signal_strength,
|
||||
};
|
||||
|
||||
static void rtl2830_stat_work(struct work_struct *work)
|
||||
{
|
||||
struct rtl2830_dev *dev = container_of(work, struct rtl2830_dev, stat_work.work);
|
||||
struct i2c_client *client = dev->client;
|
||||
struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache;
|
||||
int ret, tmp;
|
||||
u8 u8tmp, buf[2];
|
||||
u16 u16tmp;
|
||||
|
||||
dev_dbg(&client->dev, "\n");
|
||||
|
||||
/* signal strength */
|
||||
if (dev->fe_status & FE_HAS_SIGNAL) {
|
||||
struct {signed int x:14; } s;
|
||||
|
||||
/* read IF AGC */
|
||||
ret = rtl2830_bulk_read(client, 0x359, buf, 2);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
u16tmp = buf[0] << 8 | buf[1] << 0;
|
||||
u16tmp &= 0x3fff; /* [13:0] */
|
||||
tmp = s.x = u16tmp; /* 14-bit bin to 2 complement */
|
||||
u16tmp = clamp_val(-4 * tmp + 32767, 0x0000, 0xffff);
|
||||
|
||||
dev_dbg(&client->dev, "IF AGC=%d\n", tmp);
|
||||
|
||||
c->strength.stat[0].scale = FE_SCALE_RELATIVE;
|
||||
c->strength.stat[0].uvalue = u16tmp;
|
||||
} else {
|
||||
c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
/* CNR */
|
||||
if (dev->fe_status & FE_HAS_VITERBI) {
|
||||
unsigned hierarchy, constellation;
|
||||
#define CONSTELLATION_NUM 3
|
||||
#define HIERARCHY_NUM 4
|
||||
static const u32 constant[CONSTELLATION_NUM][HIERARCHY_NUM] = {
|
||||
{70705899, 70705899, 70705899, 70705899},
|
||||
{82433173, 82433173, 87483115, 94445660},
|
||||
{92888734, 92888734, 95487525, 99770748},
|
||||
};
|
||||
|
||||
ret = rtl2830_bulk_read(client, 0x33c, &u8tmp, 1);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
constellation = (u8tmp >> 2) & 0x03; /* [3:2] */
|
||||
if (constellation > CONSTELLATION_NUM - 1)
|
||||
goto err_schedule_delayed_work;
|
||||
|
||||
hierarchy = (u8tmp >> 4) & 0x07; /* [6:4] */
|
||||
if (hierarchy > HIERARCHY_NUM - 1)
|
||||
goto err_schedule_delayed_work;
|
||||
|
||||
ret = rtl2830_bulk_read(client, 0x40c, buf, 2);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
u16tmp = buf[0] << 8 | buf[1] << 0;
|
||||
if (u16tmp)
|
||||
tmp = (constant[constellation][hierarchy] -
|
||||
intlog10(u16tmp)) / ((1 << 24) / 10000);
|
||||
else
|
||||
tmp = 0;
|
||||
|
||||
dev_dbg(&client->dev, "CNR raw=%u\n", u16tmp);
|
||||
|
||||
c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
|
||||
c->cnr.stat[0].svalue = tmp;
|
||||
} else {
|
||||
c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
/* BER */
|
||||
if (dev->fe_status & FE_HAS_LOCK) {
|
||||
ret = rtl2830_bulk_read(client, 0x34e, buf, 2);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
u16tmp = buf[0] << 8 | buf[1] << 0;
|
||||
dev->post_bit_error += u16tmp;
|
||||
dev->post_bit_count += 1000000;
|
||||
|
||||
dev_dbg(&client->dev, "BER errors=%u total=1000000\n", u16tmp);
|
||||
|
||||
c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
|
||||
c->post_bit_error.stat[0].uvalue = dev->post_bit_error;
|
||||
c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
|
||||
c->post_bit_count.stat[0].uvalue = dev->post_bit_count;
|
||||
} else {
|
||||
c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
|
||||
c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
err_schedule_delayed_work:
|
||||
schedule_delayed_work(&dev->stat_work, msecs_to_jiffies(2000));
|
||||
return;
|
||||
err:
|
||||
dev_dbg(&client->dev, "failed=%d\n", ret);
|
||||
}
|
||||
|
||||
static int rtl2830_pid_filter_ctrl(struct dvb_frontend *fe, int onoff)
|
||||
{
|
||||
struct i2c_client *client = fe->demodulator_priv;
|
||||
@ -851,7 +829,6 @@ static int rtl2830_probe(struct i2c_client *client,
|
||||
dev->client = client;
|
||||
dev->pdata = client->dev.platform_data;
|
||||
dev->sleeping = true;
|
||||
INIT_DELAYED_WORK(&dev->stat_work, rtl2830_stat_work);
|
||||
dev->regmap = regmap_init(&client->dev, ®map_bus, client,
|
||||
®map_config);
|
||||
if (IS_ERR(dev->regmap)) {
|
||||
@ -904,9 +881,6 @@ static int rtl2830_remove(struct i2c_client *client)
|
||||
|
||||
dev_dbg(&client->dev, "\n");
|
||||
|
||||
/* stop statistics polling */
|
||||
cancel_delayed_work_sync(&dev->stat_work);
|
||||
|
||||
i2c_mux_del_adapters(dev->muxc);
|
||||
regmap_exit(dev->regmap);
|
||||
kfree(dev);
|
||||
@ -922,7 +896,8 @@ MODULE_DEVICE_TABLE(i2c, rtl2830_id_table);
|
||||
|
||||
static struct i2c_driver rtl2830_driver = {
|
||||
.driver = {
|
||||
.name = "rtl2830",
|
||||
.name = "rtl2830",
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.probe = rtl2830_probe,
|
||||
.remove = rtl2830_remove,
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <linux/i2c-mux.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
struct rtl2830_dev {
|
||||
struct rtl2830_platform_data *pdata;
|
||||
@ -33,7 +34,6 @@ struct rtl2830_dev {
|
||||
struct dvb_frontend fe;
|
||||
bool sleeping;
|
||||
unsigned long filters;
|
||||
struct delayed_work stat_work;
|
||||
enum fe_status fe_status;
|
||||
u64 post_bit_error_prev; /* for old DVBv3 read_ber() calculation */
|
||||
u64 post_bit_error;
|
||||
|
@ -947,6 +947,8 @@ static int rtl2832_slave_ts_ctrl(struct i2c_client *client, bool enable)
|
||||
goto err;
|
||||
}
|
||||
|
||||
dev->slave_ts = enable;
|
||||
|
||||
return 0;
|
||||
err:
|
||||
dev_dbg(&client->dev, "failed=%d\n", ret);
|
||||
@ -960,7 +962,7 @@ static int rtl2832_pid_filter_ctrl(struct dvb_frontend *fe, int onoff)
|
||||
int ret;
|
||||
u8 u8tmp;
|
||||
|
||||
dev_dbg(&client->dev, "onoff=%d\n", onoff);
|
||||
dev_dbg(&client->dev, "onoff=%d, slave_ts=%d\n", onoff, dev->slave_ts);
|
||||
|
||||
/* enable / disable PID filter */
|
||||
if (onoff)
|
||||
@ -968,7 +970,10 @@ static int rtl2832_pid_filter_ctrl(struct dvb_frontend *fe, int onoff)
|
||||
else
|
||||
u8tmp = 0x00;
|
||||
|
||||
ret = regmap_update_bits(dev->regmap, 0x061, 0xc0, u8tmp);
|
||||
if (dev->slave_ts)
|
||||
ret = regmap_update_bits(dev->regmap, 0x021, 0xc0, u8tmp);
|
||||
else
|
||||
ret = regmap_update_bits(dev->regmap, 0x061, 0xc0, u8tmp);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
@ -986,8 +991,8 @@ static int rtl2832_pid_filter(struct dvb_frontend *fe, u8 index, u16 pid,
|
||||
int ret;
|
||||
u8 buf[4];
|
||||
|
||||
dev_dbg(&client->dev, "index=%d pid=%04x onoff=%d\n",
|
||||
index, pid, onoff);
|
||||
dev_dbg(&client->dev, "index=%d pid=%04x onoff=%d slave_ts=%d\n",
|
||||
index, pid, onoff, dev->slave_ts);
|
||||
|
||||
/* skip invalid PIDs (0x2000) */
|
||||
if (pid > 0x1fff || index > 32)
|
||||
@ -1003,14 +1008,22 @@ static int rtl2832_pid_filter(struct dvb_frontend *fe, u8 index, u16 pid,
|
||||
buf[1] = (dev->filters >> 8) & 0xff;
|
||||
buf[2] = (dev->filters >> 16) & 0xff;
|
||||
buf[3] = (dev->filters >> 24) & 0xff;
|
||||
ret = regmap_bulk_write(dev->regmap, 0x062, buf, 4);
|
||||
|
||||
if (dev->slave_ts)
|
||||
ret = regmap_bulk_write(dev->regmap, 0x022, buf, 4);
|
||||
else
|
||||
ret = regmap_bulk_write(dev->regmap, 0x062, buf, 4);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
/* add PID */
|
||||
buf[0] = (pid >> 8) & 0xff;
|
||||
buf[1] = (pid >> 0) & 0xff;
|
||||
ret = regmap_bulk_write(dev->regmap, 0x066 + 2 * index, buf, 2);
|
||||
|
||||
if (dev->slave_ts)
|
||||
ret = regmap_bulk_write(dev->regmap, 0x026 + 2 * index, buf, 2);
|
||||
else
|
||||
ret = regmap_bulk_write(dev->regmap, 0x066 + 2 * index, buf, 2);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
@ -1135,6 +1148,7 @@ MODULE_DEVICE_TABLE(i2c, rtl2832_id_table);
|
||||
static struct i2c_driver rtl2832_driver = {
|
||||
.driver = {
|
||||
.name = "rtl2832",
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.probe = rtl2832_probe,
|
||||
.remove = rtl2832_remove,
|
||||
|
@ -44,6 +44,7 @@ struct rtl2832_dev {
|
||||
bool sleeping;
|
||||
struct delayed_work i2c_gate_work;
|
||||
unsigned long filters; /* PID filter */
|
||||
bool slave_ts;
|
||||
};
|
||||
|
||||
struct rtl2832_reg_entry {
|
||||
|
@ -452,7 +452,7 @@ static int rtl2832_sdr_querycap(struct file *file, void *fh,
|
||||
/* Videobuf2 operations */
|
||||
static int rtl2832_sdr_queue_setup(struct vb2_queue *vq,
|
||||
unsigned int *nbuffers,
|
||||
unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[])
|
||||
unsigned int *nplanes, unsigned int sizes[], struct device *alloc_devs[])
|
||||
{
|
||||
struct rtl2832_sdr_dev *dev = vb2_get_drv_priv(vq);
|
||||
struct platform_device *pdev = dev->pdev;
|
||||
|
@ -357,9 +357,7 @@ static int si2168_init(struct dvb_frontend *fe)
|
||||
struct si2168_dev *dev = i2c_get_clientdata(client);
|
||||
int ret, len, remaining;
|
||||
const struct firmware *fw;
|
||||
const char *fw_name;
|
||||
struct si2168_cmd cmd;
|
||||
unsigned int chip_id;
|
||||
|
||||
dev_dbg(&client->dev, "\n");
|
||||
|
||||
@ -371,7 +369,7 @@ static int si2168_init(struct dvb_frontend *fe)
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
if (dev->fw_loaded) {
|
||||
if (dev->warm) {
|
||||
/* resume */
|
||||
memcpy(cmd.args, "\xc0\x06\x08\x0f\x00\x20\x21\x01", 8);
|
||||
cmd.wlen = 8;
|
||||
@ -398,49 +396,14 @@ static int si2168_init(struct dvb_frontend *fe)
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
/* query chip revision */
|
||||
memcpy(cmd.args, "\x02", 1);
|
||||
cmd.wlen = 1;
|
||||
cmd.rlen = 13;
|
||||
ret = si2168_cmd_execute(client, &cmd);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
chip_id = cmd.args[1] << 24 | cmd.args[2] << 16 | cmd.args[3] << 8 |
|
||||
cmd.args[4] << 0;
|
||||
|
||||
#define SI2168_A20 ('A' << 24 | 68 << 16 | '2' << 8 | '0' << 0)
|
||||
#define SI2168_A30 ('A' << 24 | 68 << 16 | '3' << 8 | '0' << 0)
|
||||
#define SI2168_B40 ('B' << 24 | 68 << 16 | '4' << 8 | '0' << 0)
|
||||
|
||||
switch (chip_id) {
|
||||
case SI2168_A20:
|
||||
fw_name = SI2168_A20_FIRMWARE;
|
||||
break;
|
||||
case SI2168_A30:
|
||||
fw_name = SI2168_A30_FIRMWARE;
|
||||
break;
|
||||
case SI2168_B40:
|
||||
fw_name = SI2168_B40_FIRMWARE;
|
||||
break;
|
||||
default:
|
||||
dev_err(&client->dev, "unknown chip version Si21%d-%c%c%c\n",
|
||||
cmd.args[2], cmd.args[1],
|
||||
cmd.args[3], cmd.args[4]);
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
dev_info(&client->dev, "found a 'Silicon Labs Si21%d-%c%c%c'\n",
|
||||
cmd.args[2], cmd.args[1], cmd.args[3], cmd.args[4]);
|
||||
|
||||
/* request the firmware, this will block and timeout */
|
||||
ret = request_firmware(&fw, fw_name, &client->dev);
|
||||
ret = request_firmware(&fw, dev->firmware_name, &client->dev);
|
||||
if (ret) {
|
||||
/* fallback mechanism to handle old name for Si2168 B40 fw */
|
||||
if (chip_id == SI2168_B40) {
|
||||
fw_name = SI2168_B40_FIRMWARE_FALLBACK;
|
||||
ret = request_firmware(&fw, fw_name, &client->dev);
|
||||
if (dev->chip_id == SI2168_CHIP_ID_B40) {
|
||||
dev->firmware_name = SI2168_B40_FIRMWARE_FALLBACK;
|
||||
ret = request_firmware(&fw, dev->firmware_name,
|
||||
&client->dev);
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
@ -450,13 +413,13 @@ static int si2168_init(struct dvb_frontend *fe)
|
||||
} else {
|
||||
dev_err(&client->dev,
|
||||
"firmware file '%s' not found\n",
|
||||
fw_name);
|
||||
dev->firmware_name);
|
||||
goto err_release_firmware;
|
||||
}
|
||||
}
|
||||
|
||||
dev_info(&client->dev, "downloading firmware from file '%s'\n",
|
||||
fw_name);
|
||||
dev->firmware_name);
|
||||
|
||||
if ((fw->size % 17 == 0) && (fw->data[0] > 5)) {
|
||||
/* firmware is in the new format */
|
||||
@ -511,8 +474,11 @@ static int si2168_init(struct dvb_frontend *fe)
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
dev_info(&client->dev, "firmware version: %c.%c.%d\n",
|
||||
cmd.args[6], cmd.args[7], cmd.args[8]);
|
||||
dev->version = (cmd.args[9] + '@') << 24 | (cmd.args[6] - '0') << 16 |
|
||||
(cmd.args[7] - '0') << 8 | (cmd.args[8]) << 0;
|
||||
dev_info(&client->dev, "firmware version: %c %d.%d.%d\n",
|
||||
dev->version >> 24 & 0xff, dev->version >> 16 & 0xff,
|
||||
dev->version >> 8 & 0xff, dev->version >> 0 & 0xff);
|
||||
|
||||
/* set ts mode */
|
||||
memcpy(cmd.args, "\x14\x00\x01\x10\x10\x00", 6);
|
||||
@ -525,7 +491,7 @@ static int si2168_init(struct dvb_frontend *fe)
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
dev->fw_loaded = true;
|
||||
dev->warm = true;
|
||||
warm:
|
||||
dev->active = true;
|
||||
|
||||
@ -549,6 +515,10 @@ static int si2168_sleep(struct dvb_frontend *fe)
|
||||
|
||||
dev->active = false;
|
||||
|
||||
/* Firmware B 4.0-11 or later loses warm state during sleep */
|
||||
if (dev->version > ('B' << 24 | 4 << 16 | 0 << 8 | 11 << 0))
|
||||
dev->warm = false;
|
||||
|
||||
memcpy(cmd.args, "\x13", 1);
|
||||
cmd.wlen = 1;
|
||||
cmd.rlen = 0;
|
||||
@ -653,6 +623,7 @@ static int si2168_probe(struct i2c_client *client,
|
||||
struct si2168_config *config = client->dev.platform_data;
|
||||
struct si2168_dev *dev;
|
||||
int ret;
|
||||
struct si2168_cmd cmd;
|
||||
|
||||
dev_dbg(&client->dev, "\n");
|
||||
|
||||
@ -663,8 +634,56 @@ static int si2168_probe(struct i2c_client *client,
|
||||
goto err;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, dev);
|
||||
mutex_init(&dev->i2c_mutex);
|
||||
|
||||
/* Initialize */
|
||||
memcpy(cmd.args, "\xc0\x12\x00\x0c\x00\x0d\x16\x00\x00\x00\x00\x00\x00", 13);
|
||||
cmd.wlen = 13;
|
||||
cmd.rlen = 0;
|
||||
ret = si2168_cmd_execute(client, &cmd);
|
||||
if (ret)
|
||||
goto err_kfree;
|
||||
|
||||
/* Power up */
|
||||
memcpy(cmd.args, "\xc0\x06\x01\x0f\x00\x20\x20\x01", 8);
|
||||
cmd.wlen = 8;
|
||||
cmd.rlen = 1;
|
||||
ret = si2168_cmd_execute(client, &cmd);
|
||||
if (ret)
|
||||
goto err_kfree;
|
||||
|
||||
/* Query chip revision */
|
||||
memcpy(cmd.args, "\x02", 1);
|
||||
cmd.wlen = 1;
|
||||
cmd.rlen = 13;
|
||||
ret = si2168_cmd_execute(client, &cmd);
|
||||
if (ret)
|
||||
goto err_kfree;
|
||||
|
||||
dev->chip_id = cmd.args[1] << 24 | cmd.args[2] << 16 |
|
||||
cmd.args[3] << 8 | cmd.args[4] << 0;
|
||||
|
||||
switch (dev->chip_id) {
|
||||
case SI2168_CHIP_ID_A20:
|
||||
dev->firmware_name = SI2168_A20_FIRMWARE;
|
||||
break;
|
||||
case SI2168_CHIP_ID_A30:
|
||||
dev->firmware_name = SI2168_A30_FIRMWARE;
|
||||
break;
|
||||
case SI2168_CHIP_ID_B40:
|
||||
dev->firmware_name = SI2168_B40_FIRMWARE;
|
||||
break;
|
||||
default:
|
||||
dev_dbg(&client->dev, "unknown chip version Si21%d-%c%c%c\n",
|
||||
cmd.args[2], cmd.args[1], cmd.args[3], cmd.args[4]);
|
||||
ret = -ENODEV;
|
||||
goto err_kfree;
|
||||
}
|
||||
|
||||
dev->version = (cmd.args[1]) << 24 | (cmd.args[3] - '0') << 16 |
|
||||
(cmd.args[4] - '0') << 8 | (cmd.args[5]) << 0;
|
||||
|
||||
/* create mux i2c adapter for tuner */
|
||||
dev->muxc = i2c_mux_alloc(client->adapter, &client->dev,
|
||||
1, 0, I2C_MUX_LOCKED,
|
||||
@ -686,11 +705,14 @@ static int si2168_probe(struct i2c_client *client,
|
||||
dev->ts_mode = config->ts_mode;
|
||||
dev->ts_clock_inv = config->ts_clock_inv;
|
||||
dev->ts_clock_gapped = config->ts_clock_gapped;
|
||||
dev->fw_loaded = false;
|
||||
|
||||
i2c_set_clientdata(client, dev);
|
||||
dev_info(&client->dev, "Silicon Labs Si2168-%c%d%d successfully identified\n",
|
||||
dev->version >> 24 & 0xff, dev->version >> 16 & 0xff,
|
||||
dev->version >> 8 & 0xff);
|
||||
dev_info(&client->dev, "firmware version: %c %d.%d.%d\n",
|
||||
dev->version >> 24 & 0xff, dev->version >> 16 & 0xff,
|
||||
dev->version >> 8 & 0xff, dev->version >> 0 & 0xff);
|
||||
|
||||
dev_info(&client->dev, "Silicon Labs Si2168 successfully attached\n");
|
||||
return 0;
|
||||
err_kfree:
|
||||
kfree(dev);
|
||||
@ -723,7 +745,8 @@ MODULE_DEVICE_TABLE(i2c, si2168_id_table);
|
||||
|
||||
static struct i2c_driver si2168_driver = {
|
||||
.driver = {
|
||||
.name = "si2168",
|
||||
.name = "si2168",
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.probe = si2168_probe,
|
||||
.remove = si2168_remove,
|
||||
|
@ -34,8 +34,14 @@ struct si2168_dev {
|
||||
struct dvb_frontend fe;
|
||||
enum fe_delivery_system delivery_system;
|
||||
enum fe_status fe_status;
|
||||
#define SI2168_CHIP_ID_A20 ('A' << 24 | 68 << 16 | '2' << 8 | '0' << 0)
|
||||
#define SI2168_CHIP_ID_A30 ('A' << 24 | 68 << 16 | '3' << 8 | '0' << 0)
|
||||
#define SI2168_CHIP_ID_B40 ('B' << 24 | 68 << 16 | '4' << 8 | '0' << 0)
|
||||
unsigned int chip_id;
|
||||
unsigned int version;
|
||||
const char *firmware_name;
|
||||
bool active;
|
||||
bool fw_loaded;
|
||||
bool warm;
|
||||
u8 ts_mode;
|
||||
bool ts_clock_inv;
|
||||
bool ts_clock_gapped;
|
||||
|
@ -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);
|
||||
@ -343,28 +382,20 @@ static void adv7511_csc_rgb_full2limit(struct v4l2_subdev *sd, bool enable)
|
||||
}
|
||||
}
|
||||
|
||||
static void adv7511_set_IT_content_AVI_InfoFrame(struct v4l2_subdev *sd)
|
||||
static void adv7511_set_rgb_quantization_mode(struct v4l2_subdev *sd, struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct adv7511_state *state = get_adv7511_state(sd);
|
||||
if (state->dv_timings.bt.flags & V4L2_DV_FL_IS_CE_VIDEO) {
|
||||
/* CE format, not IT */
|
||||
adv7511_wr_and_or(sd, 0x57, 0x7f, 0x00);
|
||||
} else {
|
||||
/* IT format */
|
||||
adv7511_wr_and_or(sd, 0x57, 0x7f, 0x80);
|
||||
|
||||
/* Only makes sense for RGB formats */
|
||||
if (state->fmt_code != MEDIA_BUS_FMT_RGB888_1X24) {
|
||||
/* so just keep quantization */
|
||||
adv7511_csc_rgb_full2limit(sd, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int adv7511_set_rgb_quantization_mode(struct v4l2_subdev *sd, struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
switch (ctrl->val) {
|
||||
default:
|
||||
return -EINVAL;
|
||||
break;
|
||||
case V4L2_DV_RGB_RANGE_AUTO: {
|
||||
case V4L2_DV_RGB_RANGE_AUTO:
|
||||
/* automatic */
|
||||
struct adv7511_state *state = get_adv7511_state(sd);
|
||||
|
||||
if (state->dv_timings.bt.flags & V4L2_DV_FL_IS_CE_VIDEO) {
|
||||
/* CE format, RGB limited range (16-235) */
|
||||
adv7511_csc_rgb_full2limit(sd, true);
|
||||
@ -372,7 +403,6 @@ static int adv7511_set_rgb_quantization_mode(struct v4l2_subdev *sd, struct v4l2
|
||||
/* not CE format, RGB full range (0-255) */
|
||||
adv7511_csc_rgb_full2limit(sd, false);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case V4L2_DV_RGB_RANGE_LIMITED:
|
||||
/* RGB limited range (16-235) */
|
||||
@ -383,7 +413,6 @@ static int adv7511_set_rgb_quantization_mode(struct v4l2_subdev *sd, struct v4l2
|
||||
adv7511_csc_rgb_full2limit(sd, false);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ------------------------------ CTRL OPS ------------------------------ */
|
||||
@ -400,8 +429,10 @@ static int adv7511_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
adv7511_wr_and_or(sd, 0xaf, 0xfd, ctrl->val == V4L2_DV_TX_MODE_HDMI ? 0x02 : 0x00);
|
||||
return 0;
|
||||
}
|
||||
if (state->rgb_quantization_range_ctrl == ctrl)
|
||||
return adv7511_set_rgb_quantization_mode(sd, ctrl);
|
||||
if (state->rgb_quantization_range_ctrl == ctrl) {
|
||||
adv7511_set_rgb_quantization_mode(sd, ctrl);
|
||||
return 0;
|
||||
}
|
||||
if (state->content_type_ctrl == ctrl) {
|
||||
u8 itc, cn;
|
||||
|
||||
@ -425,16 +456,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 +488,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 +587,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 +657,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 +731,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 +929,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 +954,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);
|
||||
|
||||
@ -771,12 +1057,14 @@ static int adv7511_s_dv_timings(struct v4l2_subdev *sd,
|
||||
/* save timings */
|
||||
state->dv_timings = *timings;
|
||||
|
||||
/* set h/vsync polarities */
|
||||
adv7511_wr_and_or(sd, 0x17, 0x9f,
|
||||
((timings->bt.polarities & V4L2_DV_VSYNC_POS_POL) ? 0 : 0x40) |
|
||||
((timings->bt.polarities & V4L2_DV_HSYNC_POS_POL) ? 0 : 0x20));
|
||||
|
||||
/* update quantization range based on new dv_timings */
|
||||
adv7511_set_rgb_quantization_mode(sd, state->rgb_quantization_range_ctrl);
|
||||
|
||||
/* update AVI infoframe */
|
||||
adv7511_set_IT_content_AVI_InfoFrame(sd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -956,8 +1244,6 @@ static int adv7511_enum_mbus_code(struct v4l2_subdev *sd,
|
||||
static void adv7511_fill_format(struct adv7511_state *state,
|
||||
struct v4l2_mbus_framefmt *format)
|
||||
{
|
||||
memset(format, 0, sizeof(*format));
|
||||
|
||||
format->width = state->dv_timings.bt.width;
|
||||
format->height = state->dv_timings.bt.height;
|
||||
format->field = V4L2_FIELD_NONE;
|
||||
@ -972,6 +1258,7 @@ static int adv7511_get_fmt(struct v4l2_subdev *sd,
|
||||
if (format->pad != 0)
|
||||
return -EINVAL;
|
||||
|
||||
memset(&format->format, 0, sizeof(format->format));
|
||||
adv7511_fill_format(state, &format->format);
|
||||
|
||||
if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
|
||||
@ -1132,6 +1419,7 @@ static int adv7511_set_fmt(struct v4l2_subdev *sd,
|
||||
adv7511_wr_and_or(sd, 0x57, 0x83, (ec << 4) | (q << 2) | (itc << 7));
|
||||
adv7511_wr_and_or(sd, 0x59, 0x0f, (yq << 6) | (cn << 4));
|
||||
adv7511_wr_and_or(sd, 0x4a, 0xff, 1);
|
||||
adv7511_set_rgb_quantization_mode(sd, state->rgb_quantization_range_ctrl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1183,6 +1471,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 +1696,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 +1713,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 +1761,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 +1814,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 +1855,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 +1906,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 +1928,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 +1952,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,
|
||||
@ -164,6 +167,7 @@ struct adv76xx_state {
|
||||
struct adv76xx_platform_data pdata;
|
||||
|
||||
struct gpio_desc *hpd_gpio[4];
|
||||
struct gpio_desc *reset_gpio;
|
||||
|
||||
struct v4l2_subdev sd;
|
||||
struct media_pad pads[ADV76XX_PAD_MAX];
|
||||
@ -184,10 +188,15 @@ struct adv76xx_state {
|
||||
u16 spa_port_a[2];
|
||||
struct v4l2_fract aspect_ratio;
|
||||
u32 rgb_quantization_range;
|
||||
struct workqueue_struct *work_queues;
|
||||
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,
|
||||
@ -1086,6 +1102,10 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd)
|
||||
struct adv76xx_state *state = to_state(sd);
|
||||
bool rgb_output = io_read(sd, 0x02) & 0x02;
|
||||
bool hdmi_signal = hdmi_read(sd, 0x05) & 0x80;
|
||||
u8 y = HDMI_COLORSPACE_RGB;
|
||||
|
||||
if (hdmi_signal && (io_read(sd, 0x60) & 1))
|
||||
y = infoframe_read(sd, 0x01) >> 5;
|
||||
|
||||
v4l2_dbg(2, debug, sd, "%s: RGB quantization range: %d, RGB out: %d, HDMI: %d\n",
|
||||
__func__, state->rgb_quantization_range,
|
||||
@ -1093,6 +1113,7 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd)
|
||||
|
||||
adv76xx_set_gain(sd, true, 0x0, 0x0, 0x0);
|
||||
adv76xx_set_offset(sd, true, 0x0, 0x0, 0x0);
|
||||
io_write_clr_set(sd, 0x02, 0x04, rgb_output ? 0 : 4);
|
||||
|
||||
switch (state->rgb_quantization_range) {
|
||||
case V4L2_DV_RGB_RANGE_AUTO:
|
||||
@ -1142,6 +1163,9 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd)
|
||||
break;
|
||||
}
|
||||
|
||||
if (y != HDMI_COLORSPACE_RGB)
|
||||
break;
|
||||
|
||||
/* RGB limited range (16-235) */
|
||||
io_write_clr_set(sd, 0x02, 0xf0, 0x00);
|
||||
|
||||
@ -1153,6 +1177,9 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd)
|
||||
break;
|
||||
}
|
||||
|
||||
if (y != HDMI_COLORSPACE_RGB)
|
||||
break;
|
||||
|
||||
/* RGB full range (0-255) */
|
||||
io_write_clr_set(sd, 0x02, 0xf0, 0x10);
|
||||
|
||||
@ -1849,6 +1876,7 @@ static void adv76xx_setup_format(struct adv76xx_state *state)
|
||||
io_write_clr_set(sd, 0x04, 0xe0, adv76xx_op_ch_sel(state));
|
||||
io_write_clr_set(sd, 0x05, 0x01,
|
||||
state->format->swap_cb_cr ? ADV76XX_OP_SWAP_CB_CR : 0);
|
||||
set_rgb_quantization_range(sd);
|
||||
}
|
||||
|
||||
static int adv76xx_get_format(struct v4l2_subdev *sd,
|
||||
@ -1924,6 +1952,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 +2201,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 +2255,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 +2291,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 +2304,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,10 +2369,10 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
|
||||
v4l2_err(sd, "error enabling edid (0x%x)\n", state->edid.present);
|
||||
return -EIO;
|
||||
}
|
||||
cec_s_phys_addr(state->cec_adap, pa, false);
|
||||
|
||||
/* enable hotplug after 100 ms */
|
||||
queue_delayed_work(state->work_queues,
|
||||
&state->delayed_work_enable_hotplug, HZ / 10);
|
||||
schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 10);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2276,8 +2493,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);
|
||||
@ -2323,11 +2551,10 @@ static int adv76xx_log_status(struct v4l2_subdev *sd)
|
||||
rgb_quantization_range_txt[state->rgb_quantization_range]);
|
||||
v4l2_info(sd, "Input color space: %s\n",
|
||||
input_color_space_txt[reg_io_0x02 >> 4]);
|
||||
v4l2_info(sd, "Output color space: %s %s, saturator %s, alt-gamma %s\n",
|
||||
v4l2_info(sd, "Output color space: %s %s, alt-gamma %s\n",
|
||||
(reg_io_0x02 & 0x02) ? "RGB" : "YCbCr",
|
||||
(reg_io_0x02 & 0x04) ? "(16-235)" : "(0-255)",
|
||||
(((reg_io_0x02 >> 2) & 0x01) ^ (reg_io_0x02 & 0x01)) ?
|
||||
"enabled" : "disabled",
|
||||
"(16-235)" : "(0-255)",
|
||||
(reg_io_0x02 & 0x08) ? "enabled" : "disabled");
|
||||
v4l2_info(sd, "Color space conversion: %s\n",
|
||||
csc_coeff_sel_rb[cp_read(sd, info->cp_csc) >> 4]);
|
||||
@ -2387,6 +2614,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 = {
|
||||
@ -2430,6 +2675,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 = {
|
||||
@ -2492,10 +2742,7 @@ static int adv76xx_core_init(struct v4l2_subdev *sd)
|
||||
cp_write(sd, 0xcf, 0x01); /* Power down macrovision */
|
||||
|
||||
/* video format */
|
||||
io_write_clr_set(sd, 0x02, 0x0f,
|
||||
pdata->alt_gamma << 3 |
|
||||
pdata->op_656_range << 2 |
|
||||
pdata->alt_data_sat << 0);
|
||||
io_write_clr_set(sd, 0x02, 0x0f, pdata->alt_gamma << 3);
|
||||
io_write_clr_set(sd, 0x05, 0x0e, pdata->blank_data << 3 |
|
||||
pdata->insert_av_codes << 2 |
|
||||
pdata->replicate_av_codes << 1);
|
||||
@ -2845,10 +3092,8 @@ static int adv76xx_parse_dt(struct adv76xx_state *state)
|
||||
if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
|
||||
state->pdata.inv_llc_pol = 1;
|
||||
|
||||
if (bus_cfg.bus_type == V4L2_MBUS_BT656) {
|
||||
if (bus_cfg.bus_type == V4L2_MBUS_BT656)
|
||||
state->pdata.insert_av_codes = 1;
|
||||
state->pdata.op_656_range = 1;
|
||||
}
|
||||
|
||||
/* Disable the interrupt for now as no DT-based board uses it. */
|
||||
state->pdata.int1_config = ADV76XX_INT1_CONFIG_DISABLED;
|
||||
@ -2871,7 +3116,6 @@ static int adv76xx_parse_dt(struct adv76xx_state *state)
|
||||
state->pdata.disable_pwrdnb = 0;
|
||||
state->pdata.disable_cable_det_rst = 0;
|
||||
state->pdata.blank_data = 1;
|
||||
state->pdata.alt_data_sat = 1;
|
||||
state->pdata.op_format_mode_sel = ADV7604_OP_FORMAT_MODE0;
|
||||
state->pdata.bus_order = ADV7604_BUS_ORDER_RGB;
|
||||
|
||||
@ -3020,6 +3264,19 @@ static int configure_regmaps(struct adv76xx_state *state)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void adv76xx_reset(struct adv76xx_state *state)
|
||||
{
|
||||
if (state->reset_gpio) {
|
||||
/* ADV76XX can be reset by a low reset pulse of minimum 5 ms. */
|
||||
gpiod_set_value_cansleep(state->reset_gpio, 0);
|
||||
usleep_range(5000, 10000);
|
||||
gpiod_set_value_cansleep(state->reset_gpio, 1);
|
||||
/* It is recommended to wait 5 ms after the low pulse before */
|
||||
/* an I2C write is performed to the ADV76XX. */
|
||||
usleep_range(5000, 10000);
|
||||
}
|
||||
}
|
||||
|
||||
static int adv76xx_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
@ -3083,6 +3340,12 @@ static int adv76xx_probe(struct i2c_client *client,
|
||||
if (state->hpd_gpio[i])
|
||||
v4l_info(client, "Handling HPD %u GPIO\n", i);
|
||||
}
|
||||
state->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(state->reset_gpio))
|
||||
return PTR_ERR(state->reset_gpio);
|
||||
|
||||
adv76xx_reset(state);
|
||||
|
||||
state->timings = cea640x480;
|
||||
state->format = adv76xx_format_info(state, MEDIA_BUS_FMT_YUYV8_2X8);
|
||||
@ -3093,6 +3356,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);
|
||||
@ -3206,14 +3470,6 @@ static int adv76xx_probe(struct i2c_client *client,
|
||||
}
|
||||
}
|
||||
|
||||
/* work queues */
|
||||
state->work_queues = create_singlethread_workqueue(client->name);
|
||||
if (!state->work_queues) {
|
||||
v4l2_err(sd, "Could not create work queue\n");
|
||||
err = -ENOMEM;
|
||||
goto err_i2c;
|
||||
}
|
||||
|
||||
INIT_DELAYED_WORK(&state->delayed_work_enable_hotplug,
|
||||
adv76xx_delayed_work_enable_hotplug);
|
||||
|
||||
@ -3236,6 +3492,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);
|
||||
|
||||
@ -3249,7 +3517,6 @@ err_entity:
|
||||
media_entity_cleanup(&sd->entity);
|
||||
err_work_queues:
|
||||
cancel_delayed_work(&state->delayed_work_enable_hotplug);
|
||||
destroy_workqueue(state->work_queues);
|
||||
err_i2c:
|
||||
adv76xx_unregister_clients(state);
|
||||
err_hdl:
|
||||
@ -3264,8 +3531,14 @@ static int adv76xx_remove(struct i2c_client *client)
|
||||
struct v4l2_subdev *sd = i2c_get_clientdata(client);
|
||||
struct adv76xx_state *state = to_state(sd);
|
||||
|
||||
/* disable interrupts */
|
||||
io_write(sd, 0x40, 0);
|
||||
io_write(sd, 0x41, 0);
|
||||
io_write(sd, 0x46, 0);
|
||||
io_write(sd, 0x6e, 0);
|
||||
io_write(sd, 0x73, 0);
|
||||
|
||||
cancel_delayed_work(&state->delayed_work_enable_hotplug);
|
||||
destroy_workqueue(state->work_queues);
|
||||
v4l2_async_unregister_subdev(sd);
|
||||
media_entity_cleanup(&sd->entity);
|
||||
adv76xx_unregister_clients(to_state(sd));
|
||||
|
@ -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)
|
||||
|
||||
/*
|
||||
**********************************************************************
|
||||
*
|
||||
@ -118,7 +121,6 @@ struct adv7842_state {
|
||||
struct v4l2_fract aspect_ratio;
|
||||
u32 rgb_quantization_range;
|
||||
bool is_cea_format;
|
||||
struct workqueue_struct *work_queues;
|
||||
struct delayed_work delayed_work_enable_hotplug;
|
||||
bool restart_stdi_once;
|
||||
bool hdmi_port_a;
|
||||
@ -142,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. */
|
||||
@ -418,9 +425,9 @@ static inline int cec_write(struct v4l2_subdev *sd, u8 reg, u8 val)
|
||||
return adv_smbus_write_byte_data(state->i2c_cec, reg, val);
|
||||
}
|
||||
|
||||
static inline int cec_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
|
||||
static inline int cec_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
|
||||
{
|
||||
return cec_write(sd, reg, (cec_read(sd, reg) & mask) | val);
|
||||
return cec_write(sd, reg, (cec_read(sd, reg) & ~mask) | val);
|
||||
}
|
||||
|
||||
static inline int infoframe_read(struct v4l2_subdev *sd, u8 reg)
|
||||
@ -696,6 +703,18 @@ adv7842_get_dv_timings_cap(struct v4l2_subdev *sd)
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
static u16 adv7842_read_cable_det(struct v4l2_subdev *sd)
|
||||
{
|
||||
u8 reg = io_read(sd, 0x6f);
|
||||
u16 val = 0;
|
||||
|
||||
if (reg & 0x02)
|
||||
val |= 1; /* port A */
|
||||
if (reg & 0x01)
|
||||
val |= 2; /* port B */
|
||||
return val;
|
||||
}
|
||||
|
||||
static void adv7842_delayed_work_enable_hotplug(struct work_struct *work)
|
||||
{
|
||||
struct delayed_work *dwork = to_delayed_work(work);
|
||||
@ -756,56 +775,23 @@ static int edid_write_vga_segment(struct v4l2_subdev *sd)
|
||||
}
|
||||
|
||||
/* enable hotplug after 200 ms */
|
||||
queue_delayed_work(state->work_queues,
|
||||
&state->delayed_work_enable_hotplug, HZ / 5);
|
||||
schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 5);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int edid_spa_location(const u8 *edid)
|
||||
{
|
||||
u8 d;
|
||||
|
||||
/*
|
||||
* TODO, improve and update for other CEA extensions
|
||||
* currently only for 1 segment (256 bytes),
|
||||
* i.e. 1 extension block and CEA revision 3.
|
||||
*/
|
||||
if ((edid[0x7e] != 1) ||
|
||||
(edid[0x80] != 0x02) ||
|
||||
(edid[0x81] != 0x03)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
/*
|
||||
* search Vendor Specific Data Block (tag 3)
|
||||
*/
|
||||
d = edid[0x82] & 0x7f;
|
||||
if (d > 4) {
|
||||
int i = 0x84;
|
||||
int end = 0x80 + d;
|
||||
do {
|
||||
u8 tag = edid[i]>>5;
|
||||
u8 len = edid[i] & 0x1f;
|
||||
|
||||
if ((tag == 3) && (len >= 5))
|
||||
return i + 4;
|
||||
i += len + 1;
|
||||
} while (i < end);
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct adv7842_state *state = to_state(sd);
|
||||
const u8 *val = state->hdmi_edid.edid;
|
||||
int spa_loc = edid_spa_location(val);
|
||||
const u8 *edid = state->hdmi_edid.edid;
|
||||
int spa_loc;
|
||||
u16 pa;
|
||||
int err = 0;
|
||||
int i;
|
||||
|
||||
v4l2_dbg(2, debug, sd, "%s: write EDID on port %c (spa at 0x%x)\n",
|
||||
__func__, (port == ADV7842_EDID_PORT_A) ? 'A' : 'B', spa_loc);
|
||||
v4l2_dbg(2, debug, sd, "%s: write EDID on port %c\n",
|
||||
__func__, (port == ADV7842_EDID_PORT_A) ? 'A' : 'B');
|
||||
|
||||
/* HPA disable on port A and B */
|
||||
io_write_and_or(sd, 0x20, 0xcf, 0x00);
|
||||
@ -816,24 +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);
|
||||
@ -853,10 +848,10 @@ static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port)
|
||||
(port == ADV7842_EDID_PORT_A) ? 'A' : 'B');
|
||||
return -EIO;
|
||||
}
|
||||
cec_s_phys_addr(state->cec_adap, pa, false);
|
||||
|
||||
/* enable hotplug after 200 ms */
|
||||
queue_delayed_work(state->work_queues,
|
||||
&state->delayed_work_enable_hotplug, HZ / 5);
|
||||
schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 5);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -983,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,
|
||||
@ -1198,6 +1184,10 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd)
|
||||
struct adv7842_state *state = to_state(sd);
|
||||
bool rgb_output = io_read(sd, 0x02) & 0x02;
|
||||
bool hdmi_signal = hdmi_read(sd, 0x05) & 0x80;
|
||||
u8 y = HDMI_COLORSPACE_RGB;
|
||||
|
||||
if (hdmi_signal && (io_read(sd, 0x60) & 1))
|
||||
y = infoframe_read(sd, 0x01) >> 5;
|
||||
|
||||
v4l2_dbg(2, debug, sd, "%s: RGB quantization range: %d, RGB out: %d, HDMI: %d\n",
|
||||
__func__, state->rgb_quantization_range,
|
||||
@ -1205,6 +1195,7 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd)
|
||||
|
||||
adv7842_set_gain(sd, true, 0x0, 0x0, 0x0);
|
||||
adv7842_set_offset(sd, true, 0x0, 0x0, 0x0);
|
||||
io_write_clr_set(sd, 0x02, 0x04, rgb_output ? 0 : 4);
|
||||
|
||||
switch (state->rgb_quantization_range) {
|
||||
case V4L2_DV_RGB_RANGE_AUTO:
|
||||
@ -1254,6 +1245,9 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd)
|
||||
break;
|
||||
}
|
||||
|
||||
if (y != HDMI_COLORSPACE_RGB)
|
||||
break;
|
||||
|
||||
/* RGB limited range (16-235) */
|
||||
io_write_and_or(sd, 0x02, 0x0f, 0x00);
|
||||
|
||||
@ -1265,6 +1259,9 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd)
|
||||
break;
|
||||
}
|
||||
|
||||
if (y != HDMI_COLORSPACE_RGB)
|
||||
break;
|
||||
|
||||
/* RGB full range (0-255) */
|
||||
io_write_and_or(sd, 0x02, 0x0f, 0x10);
|
||||
|
||||
@ -2072,6 +2069,7 @@ static void adv7842_setup_format(struct adv7842_state *state)
|
||||
io_write_clr_set(sd, 0x04, 0xe0, adv7842_op_ch_sel(state));
|
||||
io_write_clr_set(sd, 0x05, 0x01,
|
||||
state->format->swap_cb_cr ? ADV7842_OP_SWAP_CB_CR : 0);
|
||||
set_rgb_quantization_range(sd);
|
||||
}
|
||||
|
||||
static int adv7842_get_format(struct v4l2_subdev *sd,
|
||||
@ -2170,6 +2168,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);
|
||||
@ -2241,6 +2440,11 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
|
||||
*handled = true;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_VIDEO_ADV7842_CEC)
|
||||
/* cec */
|
||||
adv7842_cec_isr(sd, handled);
|
||||
#endif
|
||||
|
||||
/* tx 5v detect */
|
||||
if (irq_status[2] & 0x3) {
|
||||
v4l2_dbg(1, debug, sd, "%s: irq tx_5v\n", __func__);
|
||||
@ -2321,10 +2525,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;
|
||||
@ -2397,6 +2603,8 @@ static void adv7842_log_infoframes(struct v4l2_subdev *sd)
|
||||
log_infoframe(sd, &cri[i]);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Let's keep it here for now, as it could be useful for debug */
|
||||
static const char * const prim_mode_txt[] = {
|
||||
"SDP",
|
||||
"Component",
|
||||
@ -2415,6 +2623,7 @@ static const char * const prim_mode_txt[] = {
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
};
|
||||
#endif
|
||||
|
||||
static int adv7842_sdp_log_status(struct v4l2_subdev *sd)
|
||||
{
|
||||
@ -2509,8 +2718,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) {
|
||||
@ -2569,11 +2789,11 @@ static int adv7842_cp_log_status(struct v4l2_subdev *sd)
|
||||
rgb_quantization_range_txt[state->rgb_quantization_range]);
|
||||
v4l2_info(sd, "Input color space: %s\n",
|
||||
input_color_space_txt[reg_io_0x02 >> 4]);
|
||||
v4l2_info(sd, "Output color space: %s %s, saturator %s\n",
|
||||
v4l2_info(sd, "Output color space: %s %s, alt-gamma %s\n",
|
||||
(reg_io_0x02 & 0x02) ? "RGB" : "YCbCr",
|
||||
(reg_io_0x02 & 0x04) ? "(16-235)" : "(0-255)",
|
||||
((reg_io_0x02 & 0x04) ^ (reg_io_0x02 & 0x01)) ?
|
||||
"enabled" : "disabled");
|
||||
(((reg_io_0x02 >> 2) & 0x01) ^ (reg_io_0x02 & 0x01)) ?
|
||||
"(16-235)" : "(0-255)",
|
||||
(reg_io_0x02 & 0x08) ? "enabled" : "disabled");
|
||||
v4l2_info(sd, "Color space conversion: %s\n",
|
||||
csc_coeff_sel_rb[cp_read(sd, 0xf4) >> 4]);
|
||||
|
||||
@ -2777,11 +2997,7 @@ static int adv7842_core_init(struct v4l2_subdev *sd)
|
||||
io_write(sd, 0x15, 0x80); /* Power up pads */
|
||||
|
||||
/* video format */
|
||||
io_write(sd, 0x02,
|
||||
0xf0 |
|
||||
pdata->alt_gamma << 3 |
|
||||
pdata->op_656_range << 2 |
|
||||
pdata->alt_data_sat << 0);
|
||||
io_write(sd, 0x02, 0xf0 | pdata->alt_gamma << 3);
|
||||
io_write_and_or(sd, 0x05, 0xf0, pdata->blank_data << 3 |
|
||||
pdata->insert_av_codes << 2 |
|
||||
pdata->replicate_av_codes << 1);
|
||||
@ -3031,6 +3247,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 +3311,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 +3480,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;
|
||||
@ -3311,13 +3551,6 @@ static int adv7842_probe(struct i2c_client *client,
|
||||
goto err_i2c;
|
||||
}
|
||||
|
||||
/* work queues */
|
||||
state->work_queues = create_singlethread_workqueue(client->name);
|
||||
if (!state->work_queues) {
|
||||
v4l2_err(sd, "Could not create work queue\n");
|
||||
err = -ENOMEM;
|
||||
goto err_i2c;
|
||||
}
|
||||
|
||||
INIT_DELAYED_WORK(&state->delayed_work_enable_hotplug,
|
||||
adv7842_delayed_work_enable_hotplug);
|
||||
@ -3331,6 +3564,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;
|
||||
@ -3339,7 +3583,6 @@ err_entity:
|
||||
media_entity_cleanup(&sd->entity);
|
||||
err_work_queues:
|
||||
cancel_delayed_work(&state->delayed_work_enable_hotplug);
|
||||
destroy_workqueue(state->work_queues);
|
||||
err_i2c:
|
||||
adv7842_unregister_clients(sd);
|
||||
err_hdl:
|
||||
@ -3355,9 +3598,7 @@ static int adv7842_remove(struct i2c_client *client)
|
||||
struct adv7842_state *state = to_state(sd);
|
||||
|
||||
adv7842_irq_enable(sd, false);
|
||||
|
||||
cancel_delayed_work(&state->delayed_work_enable_hotplug);
|
||||
destroy_workqueue(state->work_queues);
|
||||
v4l2_device_unregister_subdev(sd);
|
||||
media_entity_cleanup(&sd->entity);
|
||||
adv7842_unregister_clients(sd);
|
||||
|
@ -121,13 +121,6 @@ static const struct v4l2_ctrl_ops cs53l32a_ctrl_ops = {
|
||||
|
||||
static const struct v4l2_subdev_core_ops cs53l32a_core_ops = {
|
||||
.log_status = cs53l32a_log_status,
|
||||
.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
|
||||
.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
|
||||
.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
|
||||
.g_ctrl = v4l2_subdev_g_ctrl,
|
||||
.s_ctrl = v4l2_subdev_s_ctrl,
|
||||
.queryctrl = v4l2_subdev_queryctrl,
|
||||
.querymenu = v4l2_subdev_querymenu,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_audio_ops cs53l32a_audio_ops = {
|
||||
|
@ -5042,13 +5042,6 @@ static const struct v4l2_ctrl_ops cx25840_ctrl_ops = {
|
||||
|
||||
static const struct v4l2_subdev_core_ops cx25840_core_ops = {
|
||||
.log_status = cx25840_log_status,
|
||||
.g_ctrl = v4l2_subdev_g_ctrl,
|
||||
.s_ctrl = v4l2_subdev_s_ctrl,
|
||||
.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
|
||||
.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
|
||||
.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
|
||||
.queryctrl = v4l2_subdev_queryctrl,
|
||||
.querymenu = v4l2_subdev_querymenu,
|
||||
.reset = cx25840_reset,
|
||||
.load_fw = cx25840_load_fw,
|
||||
.s_io_pin_config = common_s_io_pin_config,
|
||||
|
@ -642,13 +642,6 @@ static const struct v4l2_ctrl_ops msp_ctrl_ops = {
|
||||
|
||||
static const struct v4l2_subdev_core_ops msp_core_ops = {
|
||||
.log_status = msp_log_status,
|
||||
.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
|
||||
.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
|
||||
.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
|
||||
.g_ctrl = v4l2_subdev_g_ctrl,
|
||||
.s_ctrl = v4l2_subdev_s_ctrl,
|
||||
.queryctrl = v4l2_subdev_queryctrl,
|
||||
.querymenu = v4l2_subdev_querymenu,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_video_ops msp_video_ops = {
|
||||
|
@ -233,10 +233,21 @@ static int __mt9t001_set_power(struct mt9t001 *mt9t001, bool on)
|
||||
ret = mt9t001_reset(mt9t001);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Failed to reset the camera\n");
|
||||
return ret;
|
||||
goto e_power;
|
||||
}
|
||||
|
||||
return v4l2_ctrl_handler_setup(&mt9t001->ctrls);
|
||||
ret = v4l2_ctrl_handler_setup(&mt9t001->ctrls);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Failed to set up control handlers\n");
|
||||
goto e_power;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
e_power:
|
||||
mt9t001_power_off(mt9t001);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
@ -834,7 +845,7 @@ static struct v4l2_subdev_ops mt9t001_subdev_ops = {
|
||||
.pad = &mt9t001_subdev_pad_ops,
|
||||
};
|
||||
|
||||
static struct v4l2_subdev_internal_ops mt9t001_subdev_internal_ops = {
|
||||
static const struct v4l2_subdev_internal_ops mt9t001_subdev_internal_ops = {
|
||||
.registered = mt9t001_registered,
|
||||
.open = mt9t001_open,
|
||||
.close = mt9t001_close,
|
||||
|
@ -19,7 +19,6 @@
|
||||
#include <linux/log2.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/videodev2.h>
|
||||
@ -133,9 +132,16 @@
|
||||
#define MT9V032_TEST_PATTERN_GRAY_DIAGONAL (3 << 11)
|
||||
#define MT9V032_TEST_PATTERN_ENABLE (1 << 13)
|
||||
#define MT9V032_TEST_PATTERN_FLIP (1 << 14)
|
||||
#define MT9V032_AEGC_DESIRED_BIN 0xa5
|
||||
#define MT9V032_AEC_UPDATE_FREQUENCY 0xa6
|
||||
#define MT9V032_AEC_LPF 0xa8
|
||||
#define MT9V032_AGC_UPDATE_FREQUENCY 0xa9
|
||||
#define MT9V032_AGC_LPF 0xaa
|
||||
#define MT9V032_AEC_AGC_ENABLE 0xaf
|
||||
#define MT9V032_AEC_ENABLE (1 << 0)
|
||||
#define MT9V032_AGC_ENABLE (1 << 1)
|
||||
#define MT9V034_AEC_MAX_SHUTTER_WIDTH 0xad
|
||||
#define MT9V032_AEC_MAX_SHUTTER_WIDTH 0xbd
|
||||
#define MT9V032_THERMAL_INFO 0xc1
|
||||
|
||||
enum mt9v032_model {
|
||||
@ -162,6 +168,8 @@ struct mt9v032_model_data {
|
||||
unsigned int min_shutter;
|
||||
unsigned int max_shutter;
|
||||
unsigned int pclk_reg;
|
||||
unsigned int aec_max_shutter_reg;
|
||||
const struct v4l2_ctrl_config * const aec_max_shutter_v4l2_ctrl;
|
||||
};
|
||||
|
||||
struct mt9v032_model_info {
|
||||
@ -175,63 +183,6 @@ static const struct mt9v032_model_version mt9v032_versions[] = {
|
||||
{ MT9V034_CHIP_ID_REV1, "MT9V024/MT9V034 rev1" },
|
||||
};
|
||||
|
||||
static const struct mt9v032_model_data mt9v032_model_data[] = {
|
||||
{
|
||||
/* MT9V022, MT9V032 revisions 1/2/3 */
|
||||
.min_row_time = 660,
|
||||
.min_hblank = MT9V032_HORIZONTAL_BLANKING_MIN,
|
||||
.min_vblank = MT9V032_VERTICAL_BLANKING_MIN,
|
||||
.max_vblank = MT9V032_VERTICAL_BLANKING_MAX,
|
||||
.min_shutter = MT9V032_TOTAL_SHUTTER_WIDTH_MIN,
|
||||
.max_shutter = MT9V032_TOTAL_SHUTTER_WIDTH_MAX,
|
||||
.pclk_reg = MT9V032_PIXEL_CLOCK,
|
||||
}, {
|
||||
/* MT9V024, MT9V034 */
|
||||
.min_row_time = 690,
|
||||
.min_hblank = MT9V034_HORIZONTAL_BLANKING_MIN,
|
||||
.min_vblank = MT9V034_VERTICAL_BLANKING_MIN,
|
||||
.max_vblank = MT9V034_VERTICAL_BLANKING_MAX,
|
||||
.min_shutter = MT9V034_TOTAL_SHUTTER_WIDTH_MIN,
|
||||
.max_shutter = MT9V034_TOTAL_SHUTTER_WIDTH_MAX,
|
||||
.pclk_reg = MT9V034_PIXEL_CLOCK,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct mt9v032_model_info mt9v032_models[] = {
|
||||
[MT9V032_MODEL_V022_COLOR] = {
|
||||
.data = &mt9v032_model_data[0],
|
||||
.color = true,
|
||||
},
|
||||
[MT9V032_MODEL_V022_MONO] = {
|
||||
.data = &mt9v032_model_data[0],
|
||||
.color = false,
|
||||
},
|
||||
[MT9V032_MODEL_V024_COLOR] = {
|
||||
.data = &mt9v032_model_data[1],
|
||||
.color = true,
|
||||
},
|
||||
[MT9V032_MODEL_V024_MONO] = {
|
||||
.data = &mt9v032_model_data[1],
|
||||
.color = false,
|
||||
},
|
||||
[MT9V032_MODEL_V032_COLOR] = {
|
||||
.data = &mt9v032_model_data[0],
|
||||
.color = true,
|
||||
},
|
||||
[MT9V032_MODEL_V032_MONO] = {
|
||||
.data = &mt9v032_model_data[0],
|
||||
.color = false,
|
||||
},
|
||||
[MT9V032_MODEL_V034_COLOR] = {
|
||||
.data = &mt9v032_model_data[1],
|
||||
.color = true,
|
||||
},
|
||||
[MT9V032_MODEL_V034_MONO] = {
|
||||
.data = &mt9v032_model_data[1],
|
||||
.color = false,
|
||||
},
|
||||
};
|
||||
|
||||
struct mt9v032 {
|
||||
struct v4l2_subdev subdev;
|
||||
struct media_pad pad;
|
||||
@ -349,7 +300,8 @@ static int mt9v032_power_on(struct mt9v032 *mt9v032)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return regmap_write(map, MT9V032_CHIP_CONTROL, 0);
|
||||
return regmap_write(map, MT9V032_CHIP_CONTROL,
|
||||
MT9V032_CHIP_CONTROL_MASTER_MODE);
|
||||
}
|
||||
|
||||
static void mt9v032_power_off(struct mt9v032 *mt9v032)
|
||||
@ -421,8 +373,7 @@ __mt9v032_get_pad_crop(struct mt9v032 *mt9v032, struct v4l2_subdev_pad_config *c
|
||||
|
||||
static int mt9v032_s_stream(struct v4l2_subdev *subdev, int enable)
|
||||
{
|
||||
const u16 mode = MT9V032_CHIP_CONTROL_MASTER_MODE
|
||||
| MT9V032_CHIP_CONTROL_DOUT_ENABLE
|
||||
const u16 mode = MT9V032_CHIP_CONTROL_DOUT_ENABLE
|
||||
| MT9V032_CHIP_CONTROL_SEQUENTIAL;
|
||||
struct mt9v032 *mt9v032 = to_mt9v032(subdev);
|
||||
struct v4l2_rect *crop = &mt9v032->crop;
|
||||
@ -647,6 +598,34 @@ static int mt9v032_set_selection(struct v4l2_subdev *subdev,
|
||||
*/
|
||||
|
||||
#define V4L2_CID_TEST_PATTERN_COLOR (V4L2_CID_USER_BASE | 0x1001)
|
||||
/*
|
||||
* Value between 1 and 64 to set the desired bin. This is effectively a measure
|
||||
* of how bright the image is supposed to be. Both AGC and AEC try to reach
|
||||
* this.
|
||||
*/
|
||||
#define V4L2_CID_AEGC_DESIRED_BIN (V4L2_CID_USER_BASE | 0x1002)
|
||||
/*
|
||||
* LPF is the low pass filter capability of the chip. Both AEC and AGC have
|
||||
* this setting. This limits the speed in which AGC/AEC adjust their settings.
|
||||
* Possible values are 0-2. 0 means no LPF. For 1 and 2 this equation is used:
|
||||
*
|
||||
* if |(calculated new exp - current exp)| > (current exp / 4)
|
||||
* next exp = calculated new exp
|
||||
* else
|
||||
* next exp = current exp + ((calculated new exp - current exp) / 2^LPF)
|
||||
*/
|
||||
#define V4L2_CID_AEC_LPF (V4L2_CID_USER_BASE | 0x1003)
|
||||
#define V4L2_CID_AGC_LPF (V4L2_CID_USER_BASE | 0x1004)
|
||||
/*
|
||||
* Value between 0 and 15. This is the number of frames being skipped before
|
||||
* updating the auto exposure/gain.
|
||||
*/
|
||||
#define V4L2_CID_AEC_UPDATE_INTERVAL (V4L2_CID_USER_BASE | 0x1005)
|
||||
#define V4L2_CID_AGC_UPDATE_INTERVAL (V4L2_CID_USER_BASE | 0x1006)
|
||||
/*
|
||||
* Maximum shutter width used for AEC.
|
||||
*/
|
||||
#define V4L2_CID_AEC_MAX_SHUTTER_WIDTH (V4L2_CID_USER_BASE | 0x1007)
|
||||
|
||||
static int mt9v032_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
@ -716,6 +695,28 @@ static int mt9v032_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
break;
|
||||
}
|
||||
return regmap_write(map, MT9V032_TEST_PATTERN, data);
|
||||
|
||||
case V4L2_CID_AEGC_DESIRED_BIN:
|
||||
return regmap_write(map, MT9V032_AEGC_DESIRED_BIN, ctrl->val);
|
||||
|
||||
case V4L2_CID_AEC_LPF:
|
||||
return regmap_write(map, MT9V032_AEC_LPF, ctrl->val);
|
||||
|
||||
case V4L2_CID_AGC_LPF:
|
||||
return regmap_write(map, MT9V032_AGC_LPF, ctrl->val);
|
||||
|
||||
case V4L2_CID_AEC_UPDATE_INTERVAL:
|
||||
return regmap_write(map, MT9V032_AEC_UPDATE_FREQUENCY,
|
||||
ctrl->val);
|
||||
|
||||
case V4L2_CID_AGC_UPDATE_INTERVAL:
|
||||
return regmap_write(map, MT9V032_AGC_UPDATE_FREQUENCY,
|
||||
ctrl->val);
|
||||
|
||||
case V4L2_CID_AEC_MAX_SHUTTER_WIDTH:
|
||||
return regmap_write(map,
|
||||
mt9v032->model->data->aec_max_shutter_reg,
|
||||
ctrl->val);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -745,6 +746,84 @@ static const struct v4l2_ctrl_config mt9v032_test_pattern_color = {
|
||||
.flags = 0,
|
||||
};
|
||||
|
||||
static const struct v4l2_ctrl_config mt9v032_aegc_controls[] = {
|
||||
{
|
||||
.ops = &mt9v032_ctrl_ops,
|
||||
.id = V4L2_CID_AEGC_DESIRED_BIN,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "AEC/AGC Desired Bin",
|
||||
.min = 1,
|
||||
.max = 64,
|
||||
.step = 1,
|
||||
.def = 58,
|
||||
.flags = 0,
|
||||
}, {
|
||||
.ops = &mt9v032_ctrl_ops,
|
||||
.id = V4L2_CID_AEC_LPF,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "AEC Low Pass Filter",
|
||||
.min = 0,
|
||||
.max = 2,
|
||||
.step = 1,
|
||||
.def = 0,
|
||||
.flags = 0,
|
||||
}, {
|
||||
.ops = &mt9v032_ctrl_ops,
|
||||
.id = V4L2_CID_AGC_LPF,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "AGC Low Pass Filter",
|
||||
.min = 0,
|
||||
.max = 2,
|
||||
.step = 1,
|
||||
.def = 2,
|
||||
.flags = 0,
|
||||
}, {
|
||||
.ops = &mt9v032_ctrl_ops,
|
||||
.id = V4L2_CID_AEC_UPDATE_INTERVAL,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "AEC Update Interval",
|
||||
.min = 0,
|
||||
.max = 16,
|
||||
.step = 1,
|
||||
.def = 2,
|
||||
.flags = 0,
|
||||
}, {
|
||||
.ops = &mt9v032_ctrl_ops,
|
||||
.id = V4L2_CID_AGC_UPDATE_INTERVAL,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "AGC Update Interval",
|
||||
.min = 0,
|
||||
.max = 16,
|
||||
.step = 1,
|
||||
.def = 2,
|
||||
.flags = 0,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct v4l2_ctrl_config mt9v032_aec_max_shutter_width = {
|
||||
.ops = &mt9v032_ctrl_ops,
|
||||
.id = V4L2_CID_AEC_MAX_SHUTTER_WIDTH,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "AEC Max Shutter Width",
|
||||
.min = 1,
|
||||
.max = 2047,
|
||||
.step = 1,
|
||||
.def = 480,
|
||||
.flags = 0,
|
||||
};
|
||||
|
||||
static const struct v4l2_ctrl_config mt9v034_aec_max_shutter_width = {
|
||||
.ops = &mt9v032_ctrl_ops,
|
||||
.id = V4L2_CID_AEC_MAX_SHUTTER_WIDTH,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "AEC Max Shutter Width",
|
||||
.min = 1,
|
||||
.max = 32765,
|
||||
.step = 1,
|
||||
.def = 480,
|
||||
.flags = 0,
|
||||
};
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* V4L2 subdev core operations
|
||||
*/
|
||||
@ -953,13 +1032,6 @@ static int mt9v032_probe(struct i2c_client *client,
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_WORD_DATA)) {
|
||||
dev_warn(&client->adapter->dev,
|
||||
"I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
mt9v032 = devm_kzalloc(&client->dev, sizeof(*mt9v032), GFP_KERNEL);
|
||||
if (!mt9v032)
|
||||
return -ENOMEM;
|
||||
@ -986,7 +1058,8 @@ static int mt9v032_probe(struct i2c_client *client,
|
||||
mt9v032->pdata = pdata;
|
||||
mt9v032->model = (const void *)did->driver_data;
|
||||
|
||||
v4l2_ctrl_handler_init(&mt9v032->ctrls, 10);
|
||||
v4l2_ctrl_handler_init(&mt9v032->ctrls, 11 +
|
||||
ARRAY_SIZE(mt9v032_aegc_controls));
|
||||
|
||||
v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops,
|
||||
V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
|
||||
@ -1015,6 +1088,13 @@ static int mt9v032_probe(struct i2c_client *client,
|
||||
mt9v032->test_pattern_color = v4l2_ctrl_new_custom(&mt9v032->ctrls,
|
||||
&mt9v032_test_pattern_color, NULL);
|
||||
|
||||
v4l2_ctrl_new_custom(&mt9v032->ctrls,
|
||||
mt9v032->model->data->aec_max_shutter_v4l2_ctrl,
|
||||
NULL);
|
||||
for (i = 0; i < ARRAY_SIZE(mt9v032_aegc_controls); ++i)
|
||||
v4l2_ctrl_new_custom(&mt9v032->ctrls, &mt9v032_aegc_controls[i],
|
||||
NULL);
|
||||
|
||||
v4l2_ctrl_cluster(2, &mt9v032->test_pattern);
|
||||
|
||||
mt9v032->pixel_rate =
|
||||
@ -1103,6 +1183,67 @@ static int mt9v032_remove(struct i2c_client *client)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct mt9v032_model_data mt9v032_model_data[] = {
|
||||
{
|
||||
/* MT9V022, MT9V032 revisions 1/2/3 */
|
||||
.min_row_time = 660,
|
||||
.min_hblank = MT9V032_HORIZONTAL_BLANKING_MIN,
|
||||
.min_vblank = MT9V032_VERTICAL_BLANKING_MIN,
|
||||
.max_vblank = MT9V032_VERTICAL_BLANKING_MAX,
|
||||
.min_shutter = MT9V032_TOTAL_SHUTTER_WIDTH_MIN,
|
||||
.max_shutter = MT9V032_TOTAL_SHUTTER_WIDTH_MAX,
|
||||
.pclk_reg = MT9V032_PIXEL_CLOCK,
|
||||
.aec_max_shutter_reg = MT9V032_AEC_MAX_SHUTTER_WIDTH,
|
||||
.aec_max_shutter_v4l2_ctrl = &mt9v032_aec_max_shutter_width,
|
||||
}, {
|
||||
/* MT9V024, MT9V034 */
|
||||
.min_row_time = 690,
|
||||
.min_hblank = MT9V034_HORIZONTAL_BLANKING_MIN,
|
||||
.min_vblank = MT9V034_VERTICAL_BLANKING_MIN,
|
||||
.max_vblank = MT9V034_VERTICAL_BLANKING_MAX,
|
||||
.min_shutter = MT9V034_TOTAL_SHUTTER_WIDTH_MIN,
|
||||
.max_shutter = MT9V034_TOTAL_SHUTTER_WIDTH_MAX,
|
||||
.pclk_reg = MT9V034_PIXEL_CLOCK,
|
||||
.aec_max_shutter_reg = MT9V034_AEC_MAX_SHUTTER_WIDTH,
|
||||
.aec_max_shutter_v4l2_ctrl = &mt9v034_aec_max_shutter_width,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct mt9v032_model_info mt9v032_models[] = {
|
||||
[MT9V032_MODEL_V022_COLOR] = {
|
||||
.data = &mt9v032_model_data[0],
|
||||
.color = true,
|
||||
},
|
||||
[MT9V032_MODEL_V022_MONO] = {
|
||||
.data = &mt9v032_model_data[0],
|
||||
.color = false,
|
||||
},
|
||||
[MT9V032_MODEL_V024_COLOR] = {
|
||||
.data = &mt9v032_model_data[1],
|
||||
.color = true,
|
||||
},
|
||||
[MT9V032_MODEL_V024_MONO] = {
|
||||
.data = &mt9v032_model_data[1],
|
||||
.color = false,
|
||||
},
|
||||
[MT9V032_MODEL_V032_COLOR] = {
|
||||
.data = &mt9v032_model_data[0],
|
||||
.color = true,
|
||||
},
|
||||
[MT9V032_MODEL_V032_MONO] = {
|
||||
.data = &mt9v032_model_data[0],
|
||||
.color = false,
|
||||
},
|
||||
[MT9V032_MODEL_V034_COLOR] = {
|
||||
.data = &mt9v032_model_data[1],
|
||||
.color = true,
|
||||
},
|
||||
[MT9V032_MODEL_V034_MONO] = {
|
||||
.data = &mt9v032_model_data[1],
|
||||
.color = false,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct i2c_device_id mt9v032_id[] = {
|
||||
{ "mt9v022", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V022_COLOR] },
|
||||
{ "mt9v022m", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V022_MONO] },
|
||||
|
@ -1585,13 +1585,6 @@ static const struct v4l2_ctrl_ops saa711x_ctrl_ops = {
|
||||
|
||||
static const struct v4l2_subdev_core_ops saa711x_core_ops = {
|
||||
.log_status = saa711x_log_status,
|
||||
.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
|
||||
.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
|
||||
.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
|
||||
.g_ctrl = v4l2_subdev_g_ctrl,
|
||||
.s_ctrl = v4l2_subdev_s_ctrl,
|
||||
.queryctrl = v4l2_subdev_queryctrl,
|
||||
.querymenu = v4l2_subdev_querymenu,
|
||||
.reset = saa711x_reset,
|
||||
.s_gpio = saa711x_s_gpio,
|
||||
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
||||
|
@ -3044,10 +3044,8 @@ static struct smiapp_platform_data *smiapp_get_pdata(struct device *dev)
|
||||
pdata->op_sys_clock = devm_kcalloc(
|
||||
dev, bus_cfg->nr_of_link_frequencies + 1 /* guardian */,
|
||||
sizeof(*pdata->op_sys_clock), GFP_KERNEL);
|
||||
if (!pdata->op_sys_clock) {
|
||||
rval = -ENOMEM;
|
||||
if (!pdata->op_sys_clock)
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
for (i = 0; i < bus_cfg->nr_of_link_frequencies; i++) {
|
||||
pdata->op_sys_clock[i] = bus_cfg->link_frequencies[i];
|
||||
|
@ -89,8 +89,6 @@ struct tc358743_state {
|
||||
struct v4l2_ctrl *audio_sampling_rate_ctrl;
|
||||
struct v4l2_ctrl *audio_present_ctrl;
|
||||
|
||||
/* work queues */
|
||||
struct workqueue_struct *work_queues;
|
||||
struct delayed_work delayed_work_enable_hotplug;
|
||||
|
||||
/* edid */
|
||||
@ -425,8 +423,7 @@ static void tc358743_enable_edid(struct v4l2_subdev *sd)
|
||||
|
||||
/* Enable hotplug after 100 ms. DDC access to EDID is also enabled when
|
||||
* hotplug is enabled. See register DDC_CTL */
|
||||
queue_delayed_work(state->work_queues,
|
||||
&state->delayed_work_enable_hotplug, HZ / 10);
|
||||
schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 10);
|
||||
|
||||
tc358743_enable_interrupts(sd, true);
|
||||
tc358743_s_ctrl_detect_tx_5v(sd);
|
||||
@ -1884,14 +1881,6 @@ static int tc358743_probe(struct i2c_client *client,
|
||||
goto err_hdl;
|
||||
}
|
||||
|
||||
/* work queues */
|
||||
state->work_queues = create_singlethread_workqueue(client->name);
|
||||
if (!state->work_queues) {
|
||||
v4l2_err(sd, "Could not create work queue\n");
|
||||
err = -ENOMEM;
|
||||
goto err_hdl;
|
||||
}
|
||||
|
||||
state->pad.flags = MEDIA_PAD_FL_SOURCE;
|
||||
err = media_entity_pads_init(&sd->entity, 1, &state->pad);
|
||||
if (err < 0)
|
||||
@ -1940,7 +1929,6 @@ static int tc358743_probe(struct i2c_client *client,
|
||||
|
||||
err_work_queues:
|
||||
cancel_delayed_work(&state->delayed_work_enable_hotplug);
|
||||
destroy_workqueue(state->work_queues);
|
||||
mutex_destroy(&state->confctl_mutex);
|
||||
err_hdl:
|
||||
media_entity_cleanup(&sd->entity);
|
||||
@ -1954,7 +1942,6 @@ static int tc358743_remove(struct i2c_client *client)
|
||||
struct tc358743_state *state = to_state(sd);
|
||||
|
||||
cancel_delayed_work(&state->delayed_work_enable_hotplug);
|
||||
destroy_workqueue(state->work_queues);
|
||||
v4l2_async_unregister_subdev(sd);
|
||||
v4l2_device_unregister_subdev(sd);
|
||||
mutex_destroy(&state->confctl_mutex);
|
||||
|
@ -1855,13 +1855,6 @@ static const struct v4l2_ctrl_ops tvaudio_ctrl_ops = {
|
||||
|
||||
static const struct v4l2_subdev_core_ops tvaudio_core_ops = {
|
||||
.log_status = tvaudio_log_status,
|
||||
.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
|
||||
.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
|
||||
.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
|
||||
.g_ctrl = v4l2_subdev_g_ctrl,
|
||||
.s_ctrl = v4l2_subdev_s_ctrl,
|
||||
.queryctrl = v4l2_subdev_queryctrl,
|
||||
.querymenu = v4l2_subdev_querymenu,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_tuner_ops tvaudio_tuner_ops = {
|
||||
|
@ -178,13 +178,6 @@ static const struct v4l2_ctrl_ops wm8775_ctrl_ops = {
|
||||
|
||||
static const struct v4l2_subdev_core_ops wm8775_core_ops = {
|
||||
.log_status = wm8775_log_status,
|
||||
.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
|
||||
.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
|
||||
.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
|
||||
.g_ctrl = v4l2_subdev_g_ctrl,
|
||||
.s_ctrl = v4l2_subdev_s_ctrl,
|
||||
.queryctrl = v4l2_subdev_queryctrl,
|
||||
.querymenu = v4l2_subdev_querymenu,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_tuner_ops wm8775_tuner_ops = {
|
||||
|
@ -423,7 +423,7 @@ static long media_device_ioctl(struct file *filp, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct media_devnode *devnode = media_devnode_data(filp);
|
||||
struct media_device *dev = to_media_device(devnode);
|
||||
struct media_device *dev = devnode->media_dev;
|
||||
long ret;
|
||||
|
||||
mutex_lock(&dev->graph_mutex);
|
||||
@ -495,7 +495,7 @@ static long media_device_compat_ioctl(struct file *filp, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct media_devnode *devnode = media_devnode_data(filp);
|
||||
struct media_device *dev = to_media_device(devnode);
|
||||
struct media_device *dev = devnode->media_dev;
|
||||
long ret;
|
||||
|
||||
switch (cmd) {
|
||||
@ -531,7 +531,8 @@ static const struct media_file_operations media_device_fops = {
|
||||
static ssize_t show_model(struct device *cd,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct media_device *mdev = to_media_device(to_media_devnode(cd));
|
||||
struct media_devnode *devnode = to_media_devnode(cd);
|
||||
struct media_device *mdev = devnode->media_dev;
|
||||
|
||||
return sprintf(buf, "%.*s\n", (int)sizeof(mdev->model), mdev->model);
|
||||
}
|
||||
@ -704,23 +705,35 @@ EXPORT_SYMBOL_GPL(media_device_cleanup);
|
||||
int __must_check __media_device_register(struct media_device *mdev,
|
||||
struct module *owner)
|
||||
{
|
||||
struct media_devnode *devnode;
|
||||
int ret;
|
||||
|
||||
devnode = kzalloc(sizeof(*devnode), GFP_KERNEL);
|
||||
if (!devnode)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Register the device node. */
|
||||
mdev->devnode.fops = &media_device_fops;
|
||||
mdev->devnode.parent = mdev->dev;
|
||||
mdev->devnode.release = media_device_release;
|
||||
mdev->devnode = devnode;
|
||||
devnode->fops = &media_device_fops;
|
||||
devnode->parent = mdev->dev;
|
||||
devnode->release = media_device_release;
|
||||
|
||||
/* Set version 0 to indicate user-space that the graph is static */
|
||||
mdev->topology_version = 0;
|
||||
|
||||
ret = media_devnode_register(&mdev->devnode, owner);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = device_create_file(&mdev->devnode.dev, &dev_attr_model);
|
||||
ret = media_devnode_register(mdev, devnode, owner);
|
||||
if (ret < 0) {
|
||||
media_devnode_unregister(&mdev->devnode);
|
||||
/* devnode free is handled in media_devnode_*() */
|
||||
mdev->devnode = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = device_create_file(&devnode->dev, &dev_attr_model);
|
||||
if (ret < 0) {
|
||||
/* devnode free is handled in media_devnode_*() */
|
||||
mdev->devnode = NULL;
|
||||
media_devnode_unregister_prepare(devnode);
|
||||
media_devnode_unregister(devnode);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -771,11 +784,14 @@ void media_device_unregister(struct media_device *mdev)
|
||||
mutex_lock(&mdev->graph_mutex);
|
||||
|
||||
/* Check if mdev was ever registered at all */
|
||||
if (!media_devnode_is_registered(&mdev->devnode)) {
|
||||
if (!media_devnode_is_registered(mdev->devnode)) {
|
||||
mutex_unlock(&mdev->graph_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Clear the devnode register bit to avoid races with media dev open */
|
||||
media_devnode_unregister_prepare(mdev->devnode);
|
||||
|
||||
/* Remove all entities from the media device */
|
||||
list_for_each_entry_safe(entity, next, &mdev->entities, graph_obj.list)
|
||||
__media_device_unregister_entity(entity);
|
||||
@ -794,9 +810,12 @@ void media_device_unregister(struct media_device *mdev)
|
||||
|
||||
mutex_unlock(&mdev->graph_mutex);
|
||||
|
||||
device_remove_file(&mdev->devnode.dev, &dev_attr_model);
|
||||
dev_dbg(mdev->dev, "Media device unregistering\n");
|
||||
media_devnode_unregister(&mdev->devnode);
|
||||
dev_dbg(mdev->dev, "Media device unregistered\n");
|
||||
|
||||
device_remove_file(&mdev->devnode->dev, &dev_attr_model);
|
||||
media_devnode_unregister(mdev->devnode);
|
||||
/* devnode free is handled in media_devnode_*() */
|
||||
mdev->devnode = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(media_device_unregister);
|
||||
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include <media/media-devnode.h>
|
||||
#include <media/media-device.h>
|
||||
|
||||
#define MEDIA_NUM_DEVICES 256
|
||||
#define MEDIA_NAME "media"
|
||||
@ -59,21 +60,19 @@ static DECLARE_BITMAP(media_devnode_nums, MEDIA_NUM_DEVICES);
|
||||
/* Called when the last user of the media device exits. */
|
||||
static void media_devnode_release(struct device *cd)
|
||||
{
|
||||
struct media_devnode *mdev = to_media_devnode(cd);
|
||||
struct media_devnode *devnode = to_media_devnode(cd);
|
||||
|
||||
mutex_lock(&media_devnode_lock);
|
||||
|
||||
/* Delete the cdev on this minor as well */
|
||||
cdev_del(&mdev->cdev);
|
||||
|
||||
/* Mark device node number as free */
|
||||
clear_bit(mdev->minor, media_devnode_nums);
|
||||
|
||||
clear_bit(devnode->minor, media_devnode_nums);
|
||||
mutex_unlock(&media_devnode_lock);
|
||||
|
||||
/* Release media_devnode and perform other cleanups as needed. */
|
||||
if (mdev->release)
|
||||
mdev->release(mdev);
|
||||
if (devnode->release)
|
||||
devnode->release(devnode);
|
||||
|
||||
kfree(devnode);
|
||||
pr_debug("%s: Media Devnode Deallocated\n", __func__);
|
||||
}
|
||||
|
||||
static struct bus_type media_bus_type = {
|
||||
@ -83,37 +82,37 @@ static struct bus_type media_bus_type = {
|
||||
static ssize_t media_read(struct file *filp, char __user *buf,
|
||||
size_t sz, loff_t *off)
|
||||
{
|
||||
struct media_devnode *mdev = media_devnode_data(filp);
|
||||
struct media_devnode *devnode = media_devnode_data(filp);
|
||||
|
||||
if (!mdev->fops->read)
|
||||
if (!devnode->fops->read)
|
||||
return -EINVAL;
|
||||
if (!media_devnode_is_registered(mdev))
|
||||
if (!media_devnode_is_registered(devnode))
|
||||
return -EIO;
|
||||
return mdev->fops->read(filp, buf, sz, off);
|
||||
return devnode->fops->read(filp, buf, sz, off);
|
||||
}
|
||||
|
||||
static ssize_t media_write(struct file *filp, const char __user *buf,
|
||||
size_t sz, loff_t *off)
|
||||
{
|
||||
struct media_devnode *mdev = media_devnode_data(filp);
|
||||
struct media_devnode *devnode = media_devnode_data(filp);
|
||||
|
||||
if (!mdev->fops->write)
|
||||
if (!devnode->fops->write)
|
||||
return -EINVAL;
|
||||
if (!media_devnode_is_registered(mdev))
|
||||
if (!media_devnode_is_registered(devnode))
|
||||
return -EIO;
|
||||
return mdev->fops->write(filp, buf, sz, off);
|
||||
return devnode->fops->write(filp, buf, sz, off);
|
||||
}
|
||||
|
||||
static unsigned int media_poll(struct file *filp,
|
||||
struct poll_table_struct *poll)
|
||||
{
|
||||
struct media_devnode *mdev = media_devnode_data(filp);
|
||||
struct media_devnode *devnode = media_devnode_data(filp);
|
||||
|
||||
if (!media_devnode_is_registered(mdev))
|
||||
if (!media_devnode_is_registered(devnode))
|
||||
return POLLERR | POLLHUP;
|
||||
if (!mdev->fops->poll)
|
||||
if (!devnode->fops->poll)
|
||||
return DEFAULT_POLLMASK;
|
||||
return mdev->fops->poll(filp, poll);
|
||||
return devnode->fops->poll(filp, poll);
|
||||
}
|
||||
|
||||
static long
|
||||
@ -121,12 +120,12 @@ __media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg,
|
||||
long (*ioctl_func)(struct file *filp, unsigned int cmd,
|
||||
unsigned long arg))
|
||||
{
|
||||
struct media_devnode *mdev = media_devnode_data(filp);
|
||||
struct media_devnode *devnode = media_devnode_data(filp);
|
||||
|
||||
if (!ioctl_func)
|
||||
return -ENOTTY;
|
||||
|
||||
if (!media_devnode_is_registered(mdev))
|
||||
if (!media_devnode_is_registered(devnode))
|
||||
return -EIO;
|
||||
|
||||
return ioctl_func(filp, cmd, arg);
|
||||
@ -134,9 +133,9 @@ __media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg,
|
||||
|
||||
static long media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct media_devnode *mdev = media_devnode_data(filp);
|
||||
struct media_devnode *devnode = media_devnode_data(filp);
|
||||
|
||||
return __media_ioctl(filp, cmd, arg, mdev->fops->ioctl);
|
||||
return __media_ioctl(filp, cmd, arg, devnode->fops->ioctl);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
@ -144,9 +143,9 @@ static long media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
static long media_compat_ioctl(struct file *filp, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct media_devnode *mdev = media_devnode_data(filp);
|
||||
struct media_devnode *devnode = media_devnode_data(filp);
|
||||
|
||||
return __media_ioctl(filp, cmd, arg, mdev->fops->compat_ioctl);
|
||||
return __media_ioctl(filp, cmd, arg, devnode->fops->compat_ioctl);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_COMPAT */
|
||||
@ -154,7 +153,7 @@ static long media_compat_ioctl(struct file *filp, unsigned int cmd,
|
||||
/* Override for the open function */
|
||||
static int media_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct media_devnode *mdev;
|
||||
struct media_devnode *devnode;
|
||||
int ret;
|
||||
|
||||
/* Check if the media device is available. This needs to be done with
|
||||
@ -164,23 +163,23 @@ static int media_open(struct inode *inode, struct file *filp)
|
||||
* a crash.
|
||||
*/
|
||||
mutex_lock(&media_devnode_lock);
|
||||
mdev = container_of(inode->i_cdev, struct media_devnode, cdev);
|
||||
devnode = container_of(inode->i_cdev, struct media_devnode, cdev);
|
||||
/* return ENXIO if the media device has been removed
|
||||
already or if it is not registered anymore. */
|
||||
if (!media_devnode_is_registered(mdev)) {
|
||||
if (!media_devnode_is_registered(devnode)) {
|
||||
mutex_unlock(&media_devnode_lock);
|
||||
return -ENXIO;
|
||||
}
|
||||
/* and increase the device refcount */
|
||||
get_device(&mdev->dev);
|
||||
get_device(&devnode->dev);
|
||||
mutex_unlock(&media_devnode_lock);
|
||||
|
||||
filp->private_data = mdev;
|
||||
filp->private_data = devnode;
|
||||
|
||||
if (mdev->fops->open) {
|
||||
ret = mdev->fops->open(filp);
|
||||
if (devnode->fops->open) {
|
||||
ret = devnode->fops->open(filp);
|
||||
if (ret) {
|
||||
put_device(&mdev->dev);
|
||||
put_device(&devnode->dev);
|
||||
filp->private_data = NULL;
|
||||
return ret;
|
||||
}
|
||||
@ -192,16 +191,18 @@ static int media_open(struct inode *inode, struct file *filp)
|
||||
/* Override for the release function */
|
||||
static int media_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct media_devnode *mdev = media_devnode_data(filp);
|
||||
struct media_devnode *devnode = media_devnode_data(filp);
|
||||
|
||||
if (mdev->fops->release)
|
||||
mdev->fops->release(filp);
|
||||
if (devnode->fops->release)
|
||||
devnode->fops->release(filp);
|
||||
|
||||
filp->private_data = NULL;
|
||||
|
||||
/* decrease the refcount unconditionally since the release()
|
||||
return value is ignored. */
|
||||
put_device(&mdev->dev);
|
||||
put_device(&devnode->dev);
|
||||
|
||||
pr_debug("%s: Media Release\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -219,7 +220,8 @@ static const struct file_operations media_devnode_fops = {
|
||||
.llseek = no_llseek,
|
||||
};
|
||||
|
||||
int __must_check media_devnode_register(struct media_devnode *mdev,
|
||||
int __must_check media_devnode_register(struct media_device *mdev,
|
||||
struct media_devnode *devnode,
|
||||
struct module *owner)
|
||||
{
|
||||
int minor;
|
||||
@ -231,61 +233,80 @@ int __must_check media_devnode_register(struct media_devnode *mdev,
|
||||
if (minor == MEDIA_NUM_DEVICES) {
|
||||
mutex_unlock(&media_devnode_lock);
|
||||
pr_err("could not get a free minor\n");
|
||||
kfree(devnode);
|
||||
return -ENFILE;
|
||||
}
|
||||
|
||||
set_bit(minor, media_devnode_nums);
|
||||
mutex_unlock(&media_devnode_lock);
|
||||
|
||||
mdev->minor = minor;
|
||||
devnode->minor = minor;
|
||||
devnode->media_dev = mdev;
|
||||
|
||||
/* Part 1: Initialize dev now to use dev.kobj for cdev.kobj.parent */
|
||||
devnode->dev.bus = &media_bus_type;
|
||||
devnode->dev.devt = MKDEV(MAJOR(media_dev_t), devnode->minor);
|
||||
devnode->dev.release = media_devnode_release;
|
||||
if (devnode->parent)
|
||||
devnode->dev.parent = devnode->parent;
|
||||
dev_set_name(&devnode->dev, "media%d", devnode->minor);
|
||||
device_initialize(&devnode->dev);
|
||||
|
||||
/* Part 2: Initialize and register the character device */
|
||||
cdev_init(&mdev->cdev, &media_devnode_fops);
|
||||
mdev->cdev.owner = owner;
|
||||
cdev_init(&devnode->cdev, &media_devnode_fops);
|
||||
devnode->cdev.owner = owner;
|
||||
devnode->cdev.kobj.parent = &devnode->dev.kobj;
|
||||
|
||||
ret = cdev_add(&mdev->cdev, MKDEV(MAJOR(media_dev_t), mdev->minor), 1);
|
||||
ret = cdev_add(&devnode->cdev, MKDEV(MAJOR(media_dev_t), devnode->minor), 1);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: cdev_add failed\n", __func__);
|
||||
goto error;
|
||||
goto cdev_add_error;
|
||||
}
|
||||
|
||||
/* Part 3: Register the media device */
|
||||
mdev->dev.bus = &media_bus_type;
|
||||
mdev->dev.devt = MKDEV(MAJOR(media_dev_t), mdev->minor);
|
||||
mdev->dev.release = media_devnode_release;
|
||||
if (mdev->parent)
|
||||
mdev->dev.parent = mdev->parent;
|
||||
dev_set_name(&mdev->dev, "media%d", mdev->minor);
|
||||
ret = device_register(&mdev->dev);
|
||||
/* Part 3: Add the media device */
|
||||
ret = device_add(&devnode->dev);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: device_register failed\n", __func__);
|
||||
goto error;
|
||||
pr_err("%s: device_add failed\n", __func__);
|
||||
goto device_add_error;
|
||||
}
|
||||
|
||||
/* Part 4: Activate this minor. The char device can now be used. */
|
||||
set_bit(MEDIA_FLAG_REGISTERED, &mdev->flags);
|
||||
set_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
device_add_error:
|
||||
cdev_del(&devnode->cdev);
|
||||
cdev_add_error:
|
||||
mutex_lock(&media_devnode_lock);
|
||||
cdev_del(&mdev->cdev);
|
||||
clear_bit(mdev->minor, media_devnode_nums);
|
||||
clear_bit(devnode->minor, media_devnode_nums);
|
||||
devnode->media_dev = NULL;
|
||||
mutex_unlock(&media_devnode_lock);
|
||||
|
||||
put_device(&devnode->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void media_devnode_unregister(struct media_devnode *mdev)
|
||||
void media_devnode_unregister_prepare(struct media_devnode *devnode)
|
||||
{
|
||||
/* Check if mdev was ever registered at all */
|
||||
if (!media_devnode_is_registered(mdev))
|
||||
/* Check if devnode was ever registered at all */
|
||||
if (!media_devnode_is_registered(devnode))
|
||||
return;
|
||||
|
||||
mutex_lock(&media_devnode_lock);
|
||||
clear_bit(MEDIA_FLAG_REGISTERED, &mdev->flags);
|
||||
clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
|
||||
mutex_unlock(&media_devnode_lock);
|
||||
device_unregister(&mdev->dev);
|
||||
}
|
||||
|
||||
void media_devnode_unregister(struct media_devnode *devnode)
|
||||
{
|
||||
mutex_lock(&media_devnode_lock);
|
||||
/* Delete the cdev on this minor as well */
|
||||
cdev_del(&devnode->cdev);
|
||||
mutex_unlock(&media_devnode_lock);
|
||||
device_del(&devnode->dev);
|
||||
devnode->media_dev = NULL;
|
||||
put_device(&devnode->dev);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -655,7 +655,6 @@ static long dst_ca_ioctl(struct file *file, unsigned int cmd, unsigned long ioct
|
||||
static int dst_ca_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
dprintk(verbose, DST_CA_DEBUG, 1, " Device opened [%p] ", file);
|
||||
try_module_get(THIS_MODULE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -663,7 +662,6 @@ static int dst_ca_open(struct inode *inode, struct file *file)
|
||||
static int dst_ca_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
dprintk(verbose, DST_CA_DEBUG, 1, " Device closed.");
|
||||
module_put(THIS_MODULE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -492,7 +492,6 @@ static int cobalt_subdevs_init(struct cobalt *cobalt)
|
||||
.ain_sel = ADV7604_AIN7_8_9_NC_SYNC_3_1,
|
||||
.bus_order = ADV7604_BUS_ORDER_BRG,
|
||||
.blank_data = 1,
|
||||
.op_656_range = 1,
|
||||
.op_format_mode_sel = ADV7604_OP_FORMAT_MODE0,
|
||||
.int1_config = ADV76XX_INT1_CONFIG_ACTIVE_HIGH,
|
||||
.dr_str_data = ADV76XX_DR_STR_HIGH,
|
||||
@ -571,7 +570,6 @@ static int cobalt_subdevs_hsma_init(struct cobalt *cobalt)
|
||||
.bus_order = ADV7842_BUS_ORDER_RBG,
|
||||
.op_format_mode_sel = ADV7842_OP_FORMAT_MODE0,
|
||||
.blank_data = 1,
|
||||
.op_656_range = 1,
|
||||
.dr_str_data = 3,
|
||||
.dr_str_clk = 3,
|
||||
.dr_str_sync = 3,
|
||||
@ -691,17 +689,10 @@ static int cobalt_probe(struct pci_dev *pci_dev,
|
||||
cobalt->pci_dev = pci_dev;
|
||||
cobalt->instance = i;
|
||||
|
||||
cobalt->alloc_ctx = vb2_dma_sg_init_ctx(&pci_dev->dev);
|
||||
if (IS_ERR(cobalt->alloc_ctx)) {
|
||||
kfree(cobalt);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
retval = v4l2_device_register(&pci_dev->dev, &cobalt->v4l2_dev);
|
||||
if (retval) {
|
||||
pr_err("cobalt: v4l2_device_register of card %d failed\n",
|
||||
cobalt->instance);
|
||||
vb2_dma_sg_cleanup_ctx(cobalt->alloc_ctx);
|
||||
kfree(cobalt);
|
||||
return retval;
|
||||
}
|
||||
@ -782,7 +773,6 @@ err:
|
||||
cobalt_err("error %d on initialization\n", retval);
|
||||
|
||||
v4l2_device_unregister(&cobalt->v4l2_dev);
|
||||
vb2_dma_sg_cleanup_ctx(cobalt->alloc_ctx);
|
||||
kfree(cobalt);
|
||||
return retval;
|
||||
}
|
||||
@ -818,7 +808,6 @@ static void cobalt_remove(struct pci_dev *pci_dev)
|
||||
cobalt_info("removed cobalt card\n");
|
||||
|
||||
v4l2_device_unregister(v4l2_dev);
|
||||
vb2_dma_sg_cleanup_ctx(cobalt->alloc_ctx);
|
||||
kfree(cobalt);
|
||||
}
|
||||
|
||||
|
@ -262,7 +262,6 @@ struct cobalt {
|
||||
int instance;
|
||||
struct pci_dev *pci_dev;
|
||||
struct v4l2_device v4l2_dev;
|
||||
void *alloc_ctx;
|
||||
|
||||
void __iomem *bar0, *bar1;
|
||||
|
||||
|
@ -45,7 +45,7 @@ static const struct v4l2_dv_timings cea1080p60 = V4L2_DV_BT_CEA_1920X1080P60;
|
||||
|
||||
static int cobalt_queue_setup(struct vb2_queue *q,
|
||||
unsigned int *num_buffers, unsigned int *num_planes,
|
||||
unsigned int sizes[], void *alloc_ctxs[])
|
||||
unsigned int sizes[], struct device *alloc_devs[])
|
||||
{
|
||||
struct cobalt_stream *s = q->drv_priv;
|
||||
unsigned size = s->stride * s->height;
|
||||
@ -54,7 +54,6 @@ static int cobalt_queue_setup(struct vb2_queue *q,
|
||||
*num_buffers = 3;
|
||||
if (*num_buffers > NR_BUFS)
|
||||
*num_buffers = NR_BUFS;
|
||||
alloc_ctxs[0] = s->cobalt->alloc_ctx;
|
||||
if (*num_planes)
|
||||
return sizes[0] < size ? -EINVAL : 0;
|
||||
*num_planes = 1;
|
||||
@ -1224,6 +1223,7 @@ static int cobalt_node_register(struct cobalt *cobalt, int node)
|
||||
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
|
||||
q->min_buffers_needed = 2;
|
||||
q->lock = &s->lock;
|
||||
q->dev = &cobalt->pci_dev->dev;
|
||||
vdev->queue = q;
|
||||
|
||||
video_set_drvdata(vdev, s);
|
||||
|
@ -93,7 +93,7 @@ static int snd_cx18_mixer_tv_vol_get(struct snd_kcontrol *kctl,
|
||||
vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]);
|
||||
|
||||
snd_cx18_lock(cxsc);
|
||||
ret = v4l2_subdev_call(cx->sd_av, core, g_ctrl, &vctrl);
|
||||
ret = v4l2_g_ctrl(cx->sd_av->ctrl_handler, &vctrl);
|
||||
snd_cx18_unlock(cxsc);
|
||||
|
||||
if (!ret)
|
||||
@ -115,14 +115,14 @@ static int snd_cx18_mixer_tv_vol_put(struct snd_kcontrol *kctl,
|
||||
snd_cx18_lock(cxsc);
|
||||
|
||||
/* Fetch current state */
|
||||
ret = v4l2_subdev_call(cx->sd_av, core, g_ctrl, &vctrl);
|
||||
ret = v4l2_g_ctrl(cx->sd_av->ctrl_handler, &vctrl);
|
||||
|
||||
if (ret ||
|
||||
(cx18_av_vol_to_dB(vctrl.value) != uctl->value.integer.value[0])) {
|
||||
|
||||
/* Set, if needed */
|
||||
vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]);
|
||||
ret = v4l2_subdev_call(cx->sd_av, core, s_ctrl, &vctrl);
|
||||
ret = v4l2_s_ctrl(cx->sd_av->ctrl_handler, &vctrl);
|
||||
if (!ret)
|
||||
ret = 1; /* Indicate control was changed w/o error */
|
||||
}
|
||||
|
@ -560,7 +560,7 @@ static void cx18_process_options(struct cx18 *cx)
|
||||
cx->stream_buf_size[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_bufsize;
|
||||
cx->stream_buf_size[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_bufsize;
|
||||
cx->stream_buf_size[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_bufsize;
|
||||
cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = vbi_active_samples * 36;
|
||||
cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = VBI_ACTIVE_SAMPLES * 36;
|
||||
cx->stream_buf_size[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_bufsize;
|
||||
cx->stream_buf_size[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control no data */
|
||||
|
||||
|
@ -492,9 +492,9 @@ struct cx18_card;
|
||||
* (1/15.625 kHz) * 2 * 13.5 MHz = 1728 samples/line =
|
||||
* 4 bytes SAV + 280 bytes anc data + 4 bytes SAV + 1440 active samples
|
||||
*/
|
||||
static const u32 vbi_active_samples = 1444; /* 4 byte SAV + 720 Y + 720 U/V */
|
||||
static const u32 vbi_hblank_samples_60Hz = 272; /* 4 byte EAV + 268 anc/fill */
|
||||
static const u32 vbi_hblank_samples_50Hz = 284; /* 4 byte EAV + 280 anc/fill */
|
||||
#define VBI_ACTIVE_SAMPLES 1444 /* 4 byte SAV + 720 Y + 720 U/V */
|
||||
#define VBI_HBLANK_SAMPLES_60HZ 272 /* 4 byte EAV + 268 anc/fill */
|
||||
#define VBI_HBLANK_SAMPLES_50HZ 284 /* 4 byte EAV + 280 anc/fill */
|
||||
|
||||
#define CX18_VBI_FRAMES 32
|
||||
|
||||
|
@ -177,7 +177,7 @@ static int cx18_g_fmt_vbi_cap(struct file *file, void *fh,
|
||||
|
||||
vbifmt->sampling_rate = 27000000;
|
||||
vbifmt->offset = 248; /* FIXME - slightly wrong for both 50 & 60 Hz */
|
||||
vbifmt->samples_per_line = vbi_active_samples - 4;
|
||||
vbifmt->samples_per_line = VBI_ACTIVE_SAMPLES - 4;
|
||||
vbifmt->sample_format = V4L2_PIX_FMT_GREY;
|
||||
vbifmt->start[0] = cx->vbi.start[0];
|
||||
vbifmt->start[1] = cx->vbi.start[1];
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user