1
0
Fork 0

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
  ...
hifive-unleashed-5.1
Linus Torvalds 2016-07-26 18:59:59 -07:00
commit 9c1958fc32
446 changed files with 28797 additions and 11198 deletions

View File

@ -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>

View File

@ -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)

View File

@ -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>

View 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>

View 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 &lt;unistd.h&gt;</funcsynopsisinfo>
<funcprototype>
<funcdef>int <function>close</function></funcdef>
<paramdef>int <parameter>fd</parameter></paramdef>
</funcprototype>
</funcsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Arguments</title>
<variablelist>
<varlistentry>
<term><parameter>fd</parameter></term>
<listitem>
<para>&fd;</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Description</title>
<para>
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>

View 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 &lt;sys/ioctl.h&gt;</funcsynopsisinfo>
<funcprototype>
<funcdef>int <function>ioctl</function></funcdef>
<paramdef>int <parameter>fd</parameter></paramdef>
<paramdef>int <parameter>request</parameter></paramdef>
<paramdef>void *<parameter>argp</parameter></paramdef>
</funcprototype>
</funcsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Arguments</title>
<variablelist>
<varlistentry>
<term><parameter>fd</parameter></term>
<listitem>
<para>&fd;</para>
</listitem>
</varlistentry>
<varlistentry>
<term><parameter>request</parameter></term>
<listitem>
<para>CEC ioctl request code as defined in the cec.h header file,
for example CEC_ADAP_G_CAPS.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><parameter>argp</parameter></term>
<listitem>
<para>Pointer to a request-specific structure.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Description</title>
<para>
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>

View 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 &lt;fcntl.h&gt;</funcsynopsisinfo>
<funcprototype>
<funcdef>int <function>open</function></funcdef>
<paramdef>const char *<parameter>device_name</parameter></paramdef>
<paramdef>int <parameter>flags</parameter></paramdef>
</funcprototype>
</funcsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Arguments</title>
<variablelist>
<varlistentry>
<term><parameter>device_name</parameter></term>
<listitem>
<para>Device to be opened.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><parameter>flags</parameter></term>
<listitem>
<para>Open flags. Access mode must be <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>

View 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 &lt;sys/poll.h&gt;</funcsynopsisinfo>
<funcprototype>
<funcdef>int <function>poll</function></funcdef>
<paramdef>struct pollfd *<parameter>ufds</parameter></paramdef>
<paramdef>unsigned int <parameter>nfds</parameter></paramdef>
<paramdef>int <parameter>timeout</parameter></paramdef>
</funcprototype>
</funcsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para>
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>

View 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>

View 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 &le;
<structfield>available_log_addrs</structfield> as returned by
&CEC-ADAP-G-CAPS;. All arrays in this structure are only filled up to
index <structfield>available_log_addrs</structfield>-1. The remaining
array elements will be ignored. Note that the CEC 2.0 standard allows
for a maximum of 2 logical addresses, although some hardware has support
for more. <constant>CEC_MAX_LOG_ADDRS</constant> is 4. The driver will
return the actual number of logical addresses it could claim, which may
be less than what was requested. If this field is set to 0, then the
CEC adapter shall clear all claimed logical addresses and all other
fields will be ignored.</entry>
</row>
<row>
<entry>__u32</entry>
<entry><structfield>vendor_id</structfield></entry>
<entry>The vendor ID is a 24-bit number that identifies the specific
vendor or entity. Based on this ID vendor specific commands may be
defined. If you do not want a vendor ID then set it to
<constant>CEC_VENDOR_ID_NONE</constant>.</entry>
</row>
<row>
<entry>__u32</entry>
<entry><structfield>flags</structfield></entry>
<entry>Flags. No flags are defined yet, so set this to 0.</entry>
</row>
<row>
<entry>char</entry>
<entry><structfield>osd_name</structfield>[15]</entry>
<entry>The On-Screen Display name as is returned by the
<constant>CEC_MSG_SET_OSD_NAME</constant> message.</entry>
</row>
<row>
<entry>__u8</entry>
<entry><structfield>primary_device_type</structfield>[CEC_MAX_LOG_ADDRS]</entry>
<entry>Primary device type for each logical address. See
<xref linkend="cec-prim-dev-types" /> for possible types.</entry>
</row>
<row>
<entry>__u8</entry>
<entry><structfield>log_addr_type</structfield>[CEC_MAX_LOG_ADDRS]</entry>
<entry>Logical address types. See <xref linkend="cec-log-addr-types" /> for
possible types. The driver will update this with the actual logical address
type that it claimed (e.g. it may have to fallback to
<constant>CEC_LOG_ADDR_TYPE_UNREGISTERED</constant>).</entry>
</row>
<row>
<entry>__u8</entry>
<entry><structfield>all_device_types</structfield>[CEC_MAX_LOG_ADDRS]</entry>
<entry>CEC 2.0 specific: all device types. See <xref linkend="cec-all-dev-types-flags" />.
Used to implement the <constant>CEC_MSG_REPORT_FEATURES</constant> message.
This field is ignored if <structfield>cec_version</structfield> &lt;
<constant>CEC_OP_CEC_VERSION_2_0</constant>.</entry>
</row>
<row>
<entry>__u8</entry>
<entry><structfield>features</structfield>[CEC_MAX_LOG_ADDRS][12]</entry>
<entry>Features for each logical address. Used to implement the
<constant>CEC_MSG_REPORT_FEATURES</constant> message. The 12 bytes include
both the RC Profile and the Device Features.
This field is ignored if <structfield>cec_version</structfield> &lt;
<constant>CEC_OP_CEC_VERSION_2_0</constant>.</entry>
</row>
</tbody>
</tgroup>
</table>
<table pgwide="1" frame="none" id="cec-versions">
<title>CEC Versions</title>
<tgroup cols="3">
&cs-def;
<tbody valign="top">
<row>
<entry><constant>CEC_OP_CEC_VERSION_1_3A</constant></entry>
<entry>4</entry>
<entry>CEC version according to the HDMI 1.3a standard.</entry>
</row>
<row>
<entry><constant>CEC_OP_CEC_VERSION_1_4B</constant></entry>
<entry>5</entry>
<entry>CEC version according to the HDMI 1.4b standard.</entry>
</row>
<row>
<entry><constant>CEC_OP_CEC_VERSION_2_0</constant></entry>
<entry>6</entry>
<entry>CEC version according to the HDMI 2.0 standard.</entry>
</row>
</tbody>
</tgroup>
</table>
<table pgwide="1" frame="none" id="cec-prim-dev-types">
<title>CEC Primary Device Types</title>
<tgroup cols="3">
&cs-def;
<tbody valign="top">
<row>
<entry><constant>CEC_OP_PRIM_DEVTYPE_TV</constant></entry>
<entry>0</entry>
<entry>Use for a TV.</entry>
</row>
<row>
<entry><constant>CEC_OP_PRIM_DEVTYPE_RECORD</constant></entry>
<entry>1</entry>
<entry>Use for a recording device.</entry>
</row>
<row>
<entry><constant>CEC_OP_PRIM_DEVTYPE_TUNER</constant></entry>
<entry>3</entry>
<entry>Use for a device with a tuner.</entry>
</row>
<row>
<entry><constant>CEC_OP_PRIM_DEVTYPE_PLAYBACK</constant></entry>
<entry>4</entry>
<entry>Use for a playback device.</entry>
</row>
<row>
<entry><constant>CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM</constant></entry>
<entry>5</entry>
<entry>Use for an audio system (e.g. an audio/video receiver).</entry>
</row>
<row>
<entry><constant>CEC_OP_PRIM_DEVTYPE_SWITCH</constant></entry>
<entry>6</entry>
<entry>Use for a CEC switch.</entry>
</row>
<row>
<entry><constant>CEC_OP_PRIM_DEVTYPE_VIDEOPROC</constant></entry>
<entry>7</entry>
<entry>Use for a video processor device.</entry>
</row>
</tbody>
</tgroup>
</table>
<table pgwide="1" frame="none" id="cec-log-addr-types">
<title>CEC Logical Address Types</title>
<tgroup cols="3">
&cs-def;
<tbody valign="top">
<row>
<entry><constant>CEC_LOG_ADDR_TYPE_TV</constant></entry>
<entry>0</entry>
<entry>Use for a TV.</entry>
</row>
<row>
<entry><constant>CEC_LOG_ADDR_TYPE_RECORD</constant></entry>
<entry>1</entry>
<entry>Use for a recording device.</entry>
</row>
<row>
<entry><constant>CEC_LOG_ADDR_TYPE_TUNER</constant></entry>
<entry>2</entry>
<entry>Use for a tuner device.</entry>
</row>
<row>
<entry><constant>CEC_LOG_ADDR_TYPE_PLAYBACK</constant></entry>
<entry>3</entry>
<entry>Use for a playback device.</entry>
</row>
<row>
<entry><constant>CEC_LOG_ADDR_TYPE_AUDIOSYSTEM</constant></entry>
<entry>4</entry>
<entry>Use for an audio system device.</entry>
</row>
<row>
<entry><constant>CEC_LOG_ADDR_TYPE_SPECIFIC</constant></entry>
<entry>5</entry>
<entry>Use for a second TV or for a video processor device.</entry>
</row>
<row>
<entry><constant>CEC_LOG_ADDR_TYPE_UNREGISTERED</constant></entry>
<entry>6</entry>
<entry>Use this if you just want to remain unregistered.
Used for pure CEC switches or CDC-only devices (CDC:
Capability Discovery and Control).</entry>
</row>
</tbody>
</tgroup>
</table>
<table pgwide="1" frame="none" id="cec-all-dev-types-flags">
<title>CEC All Device Types Flags</title>
<tgroup cols="3">
&cs-def;
<tbody valign="top">
<row>
<entry><constant>CEC_OP_ALL_DEVTYPE_TV</constant></entry>
<entry>0x80</entry>
<entry>This supports the TV type.</entry>
</row>
<row>
<entry><constant>CEC_OP_ALL_DEVTYPE_RECORD</constant></entry>
<entry>0x40</entry>
<entry>This supports the Recording type.</entry>
</row>
<row>
<entry><constant>CEC_OP_ALL_DEVTYPE_TUNER</constant></entry>
<entry>0x20</entry>
<entry>This supports the Tuner type.</entry>
</row>
<row>
<entry><constant>CEC_OP_ALL_DEVTYPE_PLAYBACK</constant></entry>
<entry>0x10</entry>
<entry>This supports the Playback type.</entry>
</row>
<row>
<entry><constant>CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM</constant></entry>
<entry>0x08</entry>
<entry>This supports the Audio System type.</entry>
</row>
<row>
<entry><constant>CEC_OP_ALL_DEVTYPE_SWITCH</constant></entry>
<entry>0x04</entry>
<entry>This supports the CEC Switch or Video Processing type.</entry>
</row>
</tbody>
</tgroup>
</table>
</refsect1>
<refsect1>
&return-value;
</refsect1>
</refentry>

View 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 &ge; 1), devices hooked up to those in turn have addresses a.b.0.0, etc.
So a topology of up to 5 devices deep is supported. The physical address a
device shall use is stored in the EDID of the sink.</para>
<para>For example, the EDID for each HDMI input of the TV will have a different
physical address of the form a.0.0.0 that the sources will read out and use as
their physical address.</para>
</refsect1>
<refsect1>
&return-value;
</refsect1>
</refentry>

View 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>

View 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>

View 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>

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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;

View 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.

View 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";
};

View 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";
};

View 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>;
};

View File

@ -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

View 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";
};

View File

@ -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>;
};

View File

@ -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]

View File

@ -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);

View File

@ -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.

View File

@ -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

View File

@ -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>;

View File

@ -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,
},
};

View File

@ -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;

View File

@ -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 *

View File

@ -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;

View File

@ -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

View File

@ -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
#

View 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");

View File

@ -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.

View File

@ -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);
};

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

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

View File

@ -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);

View File

@ -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");

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -26,6 +26,7 @@
#define I2C_SLVT 1
#define CXD2841ER_CHIP_ID 0xa7
#define CXD2854ER_CHIP_ID 0xc1
#define CXD2841ER_DVBS_POLLING_INVL 10

View File

@ -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;

View File

@ -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 ==*/

View File

@ -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;

File diff suppressed because it is too large Load Diff

View 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

View File

@ -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;

View File

@ -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)

View File

@ -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;
};

View File

@ -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;

View File

@ -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;

View File

@ -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], &regmap_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);

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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, &regmap_bus, client,
&regmap_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,

View File

@ -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;

View File

@ -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,

View File

@ -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 {

View File

@ -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;

View File

@ -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,

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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));

View File

@ -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);

View File

@ -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 = {

View File

@ -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,

View File

@ -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 = {

View File

@ -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,

View File

@ -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] },

View File

@ -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

View File

@ -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];

View File

@ -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);

View File

@ -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 = {

View File

@ -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 = {

View File

@ -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);

View File

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

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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);

View File

@ -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 */
}

View File

@ -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 */

View File

@ -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

View File

@ -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