--- /dev/null
+What: /sys/class/rc/rcN/wakeup_data
+Date: Mar 2016
+KernelVersion: 4.6
+Contact: Mauro Carvalho Chehab <m.chehab@samsung.com>
+Description:
+ Reading this file returns the stored CIR wakeup sequence.
+ It starts with a pulse, followed by a space, pulse etc.
+ All values are in microseconds.
+ The same format can be used to store a wakeup sequence
+ in the Nuvoton chip by writing to this file.
+
+ Note: Some systems reset the stored wakeup sequence to a
+ factory default on each boot. On such systems store the
+ wakeup sequence in a file and set it on boot using e.g.
+ a udev rule.
!Iinclude/media/v4l2-dv-timings.h
!Iinclude/media/v4l2-event.h
!Iinclude/media/v4l2-flash-led-class.h
+!Iinclude/media/v4l2-mc.h
!Iinclude/media/v4l2-mediabus.h
!Iinclude/media/v4l2-mem2mem.h
!Iinclude/media/v4l2-of.h
vertical search range for motion estimation module in video encoder.</entry>
</row>
+ <row><entry></entry></row>
+ <row id="v4l2-mpeg-video-force-key-frame">
+ <entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME</constant> </entry>
+ <entry>button</entry>
+ </row><row><entry spanname="descr">Force a key frame for the next queued buffer. Applicable to encoders.
+This is a general, codec-agnostic keyframe control.</entry>
+ </row>
+
<row><entry></entry></row>
<row>
<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE</constant> </entry>
This control is applicable to VGA, DVI-A/D, HDMI and DisplayPort connectors.
</entry>
</row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_DV_TX_IT_CONTENT_TYPE</constant></entry>
+ <entry id="v4l2-dv-content-type">enum v4l2_dv_it_content_type</entry>
+ </row>
+ <row><entry spanname="descr">Configures the IT Content Type
+ of the transmitted video. This information is sent over HDMI and DisplayPort connectors
+ as part of the AVI InfoFrame. The term 'IT Content' is used for content that originates
+ from a computer as opposed to content from a TV broadcast or an analog source. The
+ enum v4l2_dv_it_content_type defines the possible content types:</entry>
+ </row>
+ <row>
+ <entrytbl spanname="descr" cols="2">
+ <tbody valign="top">
+ <row>
+ <entry><constant>V4L2_DV_IT_CONTENT_TYPE_GRAPHICS</constant> </entry>
+ <entry>Graphics content. Pixel data should be passed unfiltered and without
+ analog reconstruction.</entry>
+ </row>
+ <row>
+ <entry><constant>V4L2_DV_IT_CONTENT_TYPE_PHOTO</constant> </entry>
+ <entry>Photo content. The content is derived from digital still pictures.
+ The content should be passed through with minimal scaling and picture
+ enhancements.</entry>
+ </row>
+ <row>
+ <entry><constant>V4L2_DV_IT_CONTENT_TYPE_CINEMA</constant> </entry>
+ <entry>Cinema content.</entry>
+ </row>
+ <row>
+ <entry><constant>V4L2_DV_IT_CONTENT_TYPE_GAME</constant> </entry>
+ <entry>Game content. Audio and video latency should be minimized.</entry>
+ </row>
+ <row>
+ <entry><constant>V4L2_DV_IT_CONTENT_TYPE_NO_ITC</constant> </entry>
+ <entry>No IT Content information is available and the ITC bit in the AVI
+ InfoFrame is set to 0.</entry>
+ </row>
+ </tbody>
+ </entrytbl>
+ </row>
<row>
<entry spanname="id"><constant>V4L2_CID_DV_RX_POWER_PRESENT</constant></entry>
<entry>bitmask</entry>
This control is applicable to VGA, DVI-A/D, HDMI and DisplayPort connectors.
</entry>
</row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_DV_RX_IT_CONTENT_TYPE</constant></entry>
+ <entry>enum v4l2_dv_it_content_type</entry>
+ </row>
+ <row><entry spanname="descr">Reads the IT Content Type
+ of the received video. This information is sent over HDMI and DisplayPort connectors
+ as part of the AVI InfoFrame. The term 'IT Content' is used for content that originates
+ from a computer as opposed to content from a TV broadcast or an analog source. See
+ <constant>V4L2_CID_DV_TX_IT_CONTENT_TYPE</constant> for the available content types.</entry>
+ </row>
<row><entry></entry></row>
</tbody>
</tgroup>
<refsect1>
<title>Description</title>
-
- <para><emphasis role="bold">NOTE:</emphasis> This new ioctl is programmed to be added on Kernel 4.6. Its definition/arguments may change until its final version.</para>
-
<para>The typical usage of this ioctl is to call it twice.
On the first call, the structure defined at &media-v2-topology; should
be zeroed. At return, if no errors happen, this ioctl will return the
</row>
<row>
<entry><constant>MEDIA_ENT_F_TUNER</constant></entry>
- <entry>Digital TV, analog TV, radio and/or software radio tuner.</entry>
+ <entry>Digital TV, analog TV, radio and/or software radio tuner,
+ with consists on a PLL tuning stage that converts radio
+ frequency (RF) signal into an Intermediate Frequency (IF).
+ Modern tuners have internally IF-PLL decoders for audio
+ and video, but older models have those stages implemented
+ on separate entities.
+ </entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_ENT_F_IF_VID_DECODER</constant></entry>
+ <entry>IF-PLL video decoder. It receives the IF from a PLL
+ and decodes the analog TV video signal. This is commonly
+ found on some very old analog tuners, like Philips MK3
+ designs. They all contain a tda9887 (or some software
+ compatible similar chip, like tda9885). Those devices
+ use a different I2C address than the tuner PLL.
+ </entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_ENT_F_IF_AUD_DECODER</constant></entry>
+ <entry>IF-PLL sound decoder. It receives the IF from a PLL
+ and decodes the analog TV audio signal. This is commonly
+ found on some very old analog hardware, like Micronas
+ msp3400, Philips tda9840, tda985x, etc. Those devices
+ use a different I2C address than the tuner PLL and
+ should be controlled together with the IF-PLL video
+ decoder.
+ </entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_ENT_F_AUDIO_CAPTURE</constant></entry>
+ <entry>Audio Capture Function Entity.</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_ENT_F_AUDIO_PLAYBACK</constant></entry>
+ <entry>Audio Playback Function Entity.</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_ENT_F_AUDIO_MIXER</constant></entry>
+ <entry>Audio Mixer Function Entity.</entry>
</row>
</tbody>
</tgroup>
<entry>Device node interface for Software Defined Radio (V4L)</entry>
<entry>typically, /dev/swradio?</entry>
</row>
+ <row>
+ <entry><constant>MEDIA_INTF_T_ALSA_PCM_CAPTURE</constant></entry>
+ <entry>Device node interface for ALSA PCM Capture</entry>
+ <entry>typically, /dev/snd/pcmC?D?c</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_INTF_T_ALSA_PCM_PLAYBACK</constant></entry>
+ <entry>Device node interface for ALSA PCM Playback</entry>
+ <entry>typically, /dev/snd/pcmC?D?p</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_INTF_T_ALSA_CONTROL</constant></entry>
+ <entry>Device node interface for ALSA Control</entry>
+ <entry>typically, /dev/snd/controlC?</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_INTF_T_ALSA_COMPRESS</constant></entry>
+ <entry>Device node interface for ALSA Compress</entry>
+ <entry>typically, /dev/snd/compr?</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_INTF_T_ALSA_RAWMIDI</constant></entry>
+ <entry>Device node interface for ALSA Raw MIDI</entry>
+ <entry>typically, /dev/snd/midi?</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_INTF_T_ALSA_HWDEP</constant></entry>
+ <entry>Device node interface for ALSA Hardware Dependent</entry>
+ <entry>typically, /dev/snd/hwC?D?</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_INTF_T_ALSA_SEQUENCER</constant></entry>
+ <entry>Device node interface for ALSA Sequencer</entry>
+ <entry>typically, /dev/snd/seq</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_INTF_T_ALSA_TIMER</constant></entry>
+ <entry>Device node interface for ALSA Timer</entry>
+ <entry>typically, /dev/snd/timer</entry>
+ </row>
</tbody>
</tgroup>
</table>
--- /dev/null
+<refentry id="V4L2-PIX-FMT-Y12I">
+ <refmeta>
+ <refentrytitle>V4L2_PIX_FMT_Y12I ('Y12I')</refentrytitle>
+ &manvol;
+ </refmeta>
+ <refnamediv>
+ <refname><constant>V4L2_PIX_FMT_Y12I</constant></refname>
+ <refpurpose>Interleaved grey-scale image, e.g. from a stereo-pair</refpurpose>
+ </refnamediv>
+ <refsect1>
+ <title>Description</title>
+
+ <para>This is a grey-scale image with a depth of 12 bits per pixel, but with
+pixels from 2 sources interleaved and bit-packed. Each pixel is stored in a
+24-bit word in the little-endian order. On a little-endian machine these pixels
+can be deinterlaced using</para>
+
+<para>
+<programlisting>
+__u8 *buf;
+left0 = 0xfff & *(__u16 *)buf;
+right0 = *(__u16 *)(buf + 1) >> 4;
+</programlisting>
+</para>
+
+ <example>
+ <title><constant>V4L2_PIX_FMT_Y12I</constant> 2 pixel data stream taking 3 bytes</title>
+
+ <formalpara>
+ <title>Bit-packed representation</title>
+ <para>pixels cross the byte boundary and have a ratio of 3 bytes for each
+ interleaved pixel.
+ <informaltable frame="all">
+ <tgroup cols="3" align="center">
+ <colspec align="left" colwidth="2*" />
+ <tbody valign="top">
+ <row>
+ <entry>Y'<subscript>0left[7:0]</subscript></entry>
+ <entry>Y'<subscript>0right[3:0]</subscript>Y'<subscript>0left[11:8]</subscript></entry>
+ <entry>Y'<subscript>0right[11:4]</subscript></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </para>
+ </formalpara>
+ </example>
+ </refsect1>
+</refentry>
--- /dev/null
+<refentry id="V4L2-PIX-FMT-Y8I">
+ <refmeta>
+ <refentrytitle>V4L2_PIX_FMT_Y8I ('Y8I ')</refentrytitle>
+ &manvol;
+ </refmeta>
+ <refnamediv>
+ <refname><constant>V4L2_PIX_FMT_Y8I</constant></refname>
+ <refpurpose>Interleaved grey-scale image, e.g. from a stereo-pair</refpurpose>
+ </refnamediv>
+ <refsect1>
+ <title>Description</title>
+
+ <para>This is a grey-scale image with a depth of 8 bits per pixel, but with
+pixels from 2 sources interleaved. Each pixel is stored in a 16-bit word. E.g.
+the R200 RealSense camera stores pixel from the left sensor in lower and from
+the right sensor in the higher 8 bits.</para>
+
+ <example>
+ <title><constant>V4L2_PIX_FMT_Y8I</constant> 4 × 4
+pixel image</title>
+
+ <formalpara>
+ <title>Byte Order.</title>
+ <para>Each cell is one byte.
+ <informaltable frame="none">
+ <tgroup cols="9" align="center">
+ <colspec align="left" colwidth="2*" />
+ <tbody valign="top">
+ <row>
+ <entry>start + 0:</entry>
+ <entry>Y'<subscript>00left</subscript></entry>
+ <entry>Y'<subscript>00right</subscript></entry>
+ <entry>Y'<subscript>01left</subscript></entry>
+ <entry>Y'<subscript>01right</subscript></entry>
+ <entry>Y'<subscript>02left</subscript></entry>
+ <entry>Y'<subscript>02right</subscript></entry>
+ <entry>Y'<subscript>03left</subscript></entry>
+ <entry>Y'<subscript>03right</subscript></entry>
+ </row>
+ <row>
+ <entry>start + 8:</entry>
+ <entry>Y'<subscript>10left</subscript></entry>
+ <entry>Y'<subscript>10right</subscript></entry>
+ <entry>Y'<subscript>11left</subscript></entry>
+ <entry>Y'<subscript>11right</subscript></entry>
+ <entry>Y'<subscript>12left</subscript></entry>
+ <entry>Y'<subscript>12right</subscript></entry>
+ <entry>Y'<subscript>13left</subscript></entry>
+ <entry>Y'<subscript>13right</subscript></entry>
+ </row>
+ <row>
+ <entry>start + 16:</entry>
+ <entry>Y'<subscript>20left</subscript></entry>
+ <entry>Y'<subscript>20right</subscript></entry>
+ <entry>Y'<subscript>21left</subscript></entry>
+ <entry>Y'<subscript>21right</subscript></entry>
+ <entry>Y'<subscript>22left</subscript></entry>
+ <entry>Y'<subscript>22right</subscript></entry>
+ <entry>Y'<subscript>23left</subscript></entry>
+ <entry>Y'<subscript>23right</subscript></entry>
+ </row>
+ <row>
+ <entry>start + 24:</entry>
+ <entry>Y'<subscript>30left</subscript></entry>
+ <entry>Y'<subscript>30right</subscript></entry>
+ <entry>Y'<subscript>31left</subscript></entry>
+ <entry>Y'<subscript>31right</subscript></entry>
+ <entry>Y'<subscript>32left</subscript></entry>
+ <entry>Y'<subscript>32right</subscript></entry>
+ <entry>Y'<subscript>33left</subscript></entry>
+ <entry>Y'<subscript>33right</subscript></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </para>
+ </formalpara>
+ </example>
+ </refsect1>
+</refentry>
- <refentry id="V4L2-PIX-FMT-YUV420M">
+ <refentry>
<refmeta>
- <refentrytitle>V4L2_PIX_FMT_YUV420M ('YM12')</refentrytitle>
+ <refentrytitle>V4L2_PIX_FMT_YUV420M ('YM12'), V4L2_PIX_FMT_YVU420M ('YM21')</refentrytitle>
&manvol;
</refmeta>
<refnamediv>
- <refname> <constant>V4L2_PIX_FMT_YUV420M</constant></refname>
- <refpurpose>Variation of <constant>V4L2_PIX_FMT_YUV420</constant>
- with planes non contiguous in memory. </refpurpose>
+ <refname id="V4L2-PIX-FMT-YUV420M"><constant>V4L2_PIX_FMT_YUV420M</constant></refname>
+ <refname id="V4L2-PIX-FMT-YVU420M"><constant>V4L2_PIX_FMT_YVU420M</constant></refname>
+ <refpurpose>Variation of <constant>V4L2_PIX_FMT_YUV420</constant> and
+ <constant>V4L2_PIX_FMT_YVU420</constant> with planes non contiguous
+ in memory.</refpurpose>
</refnamediv>
<refsect1>
<title>Description</title>
<para>This is a multi-planar format, as opposed to a packed format.
-The three components are separated into three sub- images or planes.
+The three components are separated into three sub-images or planes.</para>
-The Y plane is first. The Y plane has one byte per pixel. The Cb data
+ <para>The Y plane is first. The Y plane has one byte per pixel.
+For <constant>V4L2_PIX_FMT_YUV420M</constant> the Cb data
constitutes the second plane which is half the width and half
the height of the Y plane (and of the image). Each Cb belongs to four
pixels, a two-by-two square of the image. For example,
Cb<subscript>0</subscript> belongs to Y'<subscript>00</subscript>,
Y'<subscript>01</subscript>, Y'<subscript>10</subscript>, and
Y'<subscript>11</subscript>. The Cr data, just like the Cb plane, is
-in the third plane. </para>
+in the third plane.</para>
+
+ <para><constant>V4L2_PIX_FMT_YVU420M</constant> is the same except
+the Cr data is stored in the second plane and the Cb data in the third plane.
+</para>
<para>If the Y plane has pad bytes after each row, then the Cb
and Cr planes have half as many pad bytes after their rows. In other
words, two Cx rows (including padding) is exactly as long as one Y row
(including padding).</para>
- <para><constant>V4L2_PIX_FMT_YUV420M</constant> is intended to be
+ <para><constant>V4L2_PIX_FMT_YUV420M</constant> and
+<constant>V4L2_PIX_FMT_YVU420M</constant> are intended to be
used only in drivers and applications that support the multi-planar API,
described in <xref linkend="planar-apis"/>. </para>
--- /dev/null
+ <refentry>
+ <refmeta>
+ <refentrytitle>V4L2_PIX_FMT_YUV422M ('YM16'), V4L2_PIX_FMT_YVU422M ('YM61')</refentrytitle>
+ &manvol;
+ </refmeta>
+ <refnamediv>
+ <refname id="V4L2-PIX-FMT-YUV422M"><constant>V4L2_PIX_FMT_YUV422M</constant></refname>
+ <refname id="V4L2-PIX-FMT-YVU422M"><constant>V4L2_PIX_FMT_YVU422M</constant></refname>
+ <refpurpose>Planar formats with ½ horizontal resolution, also
+ known as YUV and YVU 4:2:2</refpurpose>
+ </refnamediv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>This is a multi-planar format, as opposed to a packed format.
+The three components are separated into three sub-images or planes.</para>
+
+ <para>The Y plane is first. The Y plane has one byte per pixel.
+For <constant>V4L2_PIX_FMT_YUV422M</constant> the Cb data
+constitutes the second plane which is half the width of the Y plane (and of the
+image). Each Cb belongs to two pixels. For example,
+Cb<subscript>0</subscript> belongs to Y'<subscript>00</subscript>,
+Y'<subscript>01</subscript>. The Cr data, just like the Cb plane, is
+in the third plane. </para>
+
+ <para><constant>V4L2_PIX_FMT_YVU422M</constant> is the same except
+the Cr data is stored in the second plane and the Cb data in the third plane.
+</para>
+
+ <para>If the Y plane has pad bytes after each row, then the Cb
+and Cr planes have half as many pad bytes after their rows. In other
+words, two Cx rows (including padding) is exactly as long as one Y row
+(including padding).</para>
+
+ <para><constant>V4L2_PIX_FMT_YUV422M</constant> and
+<constant>V4L2_PIX_FMT_YVU422M</constant> are intended to be
+used only in drivers and applications that support the multi-planar API,
+described in <xref linkend="planar-apis"/>. </para>
+
+ <example>
+ <title><constant>V4L2_PIX_FMT_YUV422M</constant> 4 × 4
+pixel image</title>
+
+ <formalpara>
+ <title>Byte Order.</title>
+ <para>Each cell is one byte.
+ <informaltable frame="none">
+ <tgroup cols="5" align="center">
+ <colspec align="left" colwidth="2*" />
+ <tbody valign="top">
+ <row>
+ <entry>start0 + 0:</entry>
+ <entry>Y'<subscript>00</subscript></entry>
+ <entry>Y'<subscript>01</subscript></entry>
+ <entry>Y'<subscript>02</subscript></entry>
+ <entry>Y'<subscript>03</subscript></entry>
+ </row>
+ <row>
+ <entry>start0 + 4:</entry>
+ <entry>Y'<subscript>10</subscript></entry>
+ <entry>Y'<subscript>11</subscript></entry>
+ <entry>Y'<subscript>12</subscript></entry>
+ <entry>Y'<subscript>13</subscript></entry>
+ </row>
+ <row>
+ <entry>start0 + 8:</entry>
+ <entry>Y'<subscript>20</subscript></entry>
+ <entry>Y'<subscript>21</subscript></entry>
+ <entry>Y'<subscript>22</subscript></entry>
+ <entry>Y'<subscript>23</subscript></entry>
+ </row>
+ <row>
+ <entry>start0 + 12:</entry>
+ <entry>Y'<subscript>30</subscript></entry>
+ <entry>Y'<subscript>31</subscript></entry>
+ <entry>Y'<subscript>32</subscript></entry>
+ <entry>Y'<subscript>33</subscript></entry>
+ </row>
+ <row><entry></entry></row>
+ <row>
+ <entry>start1 + 0:</entry>
+ <entry>Cb<subscript>00</subscript></entry>
+ <entry>Cb<subscript>01</subscript></entry>
+ </row>
+ <row>
+ <entry>start1 + 2:</entry>
+ <entry>Cb<subscript>10</subscript></entry>
+ <entry>Cb<subscript>11</subscript></entry>
+ </row>
+ <row>
+ <entry>start1 + 4:</entry>
+ <entry>Cb<subscript>20</subscript></entry>
+ <entry>Cb<subscript>21</subscript></entry>
+ </row>
+ <row>
+ <entry>start1 + 6:</entry>
+ <entry>Cb<subscript>30</subscript></entry>
+ <entry>Cb<subscript>31</subscript></entry>
+ </row>
+ <row><entry></entry></row>
+ <row>
+ <entry>start2 + 0:</entry>
+ <entry>Cr<subscript>00</subscript></entry>
+ <entry>Cr<subscript>01</subscript></entry>
+ </row>
+ <row>
+ <entry>start2 + 2:</entry>
+ <entry>Cr<subscript>10</subscript></entry>
+ <entry>Cr<subscript>11</subscript></entry>
+ </row>
+ <row>
+ <entry>start2 + 4:</entry>
+ <entry>Cr<subscript>20</subscript></entry>
+ <entry>Cr<subscript>21</subscript></entry>
+ </row>
+ <row>
+ <entry>start2 + 6:</entry>
+ <entry>Cr<subscript>30</subscript></entry>
+ <entry>Cr<subscript>31</subscript></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </para>
+ </formalpara>
+
+ <formalpara>
+ <title>Color Sample Location.</title>
+ <para>
+ <informaltable frame="none">
+ <tgroup cols="7" align="center">
+ <tbody valign="top">
+ <row>
+ <entry></entry>
+ <entry>0</entry><entry></entry><entry>1</entry><entry></entry>
+ <entry>2</entry><entry></entry><entry>3</entry>
+ </row>
+ <row>
+ <entry>0</entry>
+ <entry>Y</entry><entry>C</entry><entry>Y</entry><entry></entry>
+ <entry>Y</entry><entry>C</entry><entry>Y</entry>
+ </row>
+ <row>
+ <entry>1</entry>
+ <entry>Y</entry><entry>C</entry><entry>Y</entry><entry></entry>
+ <entry>Y</entry><entry>C</entry><entry>Y</entry>
+ </row>
+ <row>
+ <entry>2</entry>
+ <entry>Y</entry><entry>C</entry><entry>Y</entry><entry></entry>
+ <entry>Y</entry><entry>C</entry><entry>Y</entry>
+ </row>
+ <row>
+ <entry>3</entry>
+ <entry>Y</entry><entry>C</entry><entry>Y</entry><entry></entry>
+ <entry>Y</entry><entry>C</entry><entry>Y</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </para>
+ </formalpara>
+ </example>
+ </refsect1>
+ </refentry>
--- /dev/null
+ <refentry>
+ <refmeta>
+ <refentrytitle>V4L2_PIX_FMT_YUV444M ('YM24'), V4L2_PIX_FMT_YVU444M ('YM42')</refentrytitle>
+ &manvol;
+ </refmeta>
+ <refnamediv>
+ <refname id="V4L2-PIX-FMT-YUV444M"><constant>V4L2_PIX_FMT_YUV444M</constant></refname>
+ <refname id="V4L2-PIX-FMT-YVU444M"><constant>V4L2_PIX_FMT_YVU444M</constant></refname>
+ <refpurpose>Planar formats with full horizontal resolution, also
+ known as YUV and YVU 4:4:4</refpurpose>
+ </refnamediv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>This is a multi-planar format, as opposed to a packed format.
+The three components are separated into three sub-images or planes.</para>
+
+ <para>The Y plane is first. The Y plane has one byte per pixel.
+For <constant>V4L2_PIX_FMT_YUV444M</constant> the Cb data
+constitutes the second plane which is the same width and height as the Y plane
+(and as the image). The Cr data, just like the Cb plane, is in the third plane.
+</para>
+
+ <para><constant>V4L2_PIX_FMT_YVU444M</constant> is the same except
+the Cr data is stored in the second plane and the Cb data in the third plane.
+</para>
+ <para>If the Y plane has pad bytes after each row, then the Cb
+and Cr planes have the same number of pad bytes after their rows.</para>
+
+ <para><constant>V4L2_PIX_FMT_YUV444M</constant> and
+<constant>V4L2_PIX_FMT_YUV444M</constant> are intended to be
+used only in drivers and applications that support the multi-planar API,
+described in <xref linkend="planar-apis"/>. </para>
+
+ <example>
+ <title><constant>V4L2_PIX_FMT_YUV444M</constant> 4 × 4
+pixel image</title>
+
+ <formalpara>
+ <title>Byte Order.</title>
+ <para>Each cell is one byte.
+ <informaltable frame="none">
+ <tgroup cols="5" align="center">
+ <colspec align="left" colwidth="2*" />
+ <tbody valign="top">
+ <row>
+ <entry>start0 + 0:</entry>
+ <entry>Y'<subscript>00</subscript></entry>
+ <entry>Y'<subscript>01</subscript></entry>
+ <entry>Y'<subscript>02</subscript></entry>
+ <entry>Y'<subscript>03</subscript></entry>
+ </row>
+ <row>
+ <entry>start0 + 4:</entry>
+ <entry>Y'<subscript>10</subscript></entry>
+ <entry>Y'<subscript>11</subscript></entry>
+ <entry>Y'<subscript>12</subscript></entry>
+ <entry>Y'<subscript>13</subscript></entry>
+ </row>
+ <row>
+ <entry>start0 + 8:</entry>
+ <entry>Y'<subscript>20</subscript></entry>
+ <entry>Y'<subscript>21</subscript></entry>
+ <entry>Y'<subscript>22</subscript></entry>
+ <entry>Y'<subscript>23</subscript></entry>
+ </row>
+ <row>
+ <entry>start0 + 12:</entry>
+ <entry>Y'<subscript>30</subscript></entry>
+ <entry>Y'<subscript>31</subscript></entry>
+ <entry>Y'<subscript>32</subscript></entry>
+ <entry>Y'<subscript>33</subscript></entry>
+ </row>
+ <row><entry></entry></row>
+ <row>
+ <entry>start1 + 0:</entry>
+ <entry>Cb<subscript>00</subscript></entry>
+ <entry>Cb<subscript>01</subscript></entry>
+ <entry>Cb<subscript>02</subscript></entry>
+ <entry>Cb<subscript>03</subscript></entry>
+ </row>
+ <row>
+ <entry>start1 + 4:</entry>
+ <entry>Cb<subscript>10</subscript></entry>
+ <entry>Cb<subscript>11</subscript></entry>
+ <entry>Cb<subscript>12</subscript></entry>
+ <entry>Cb<subscript>13</subscript></entry>
+ </row>
+ <row>
+ <entry>start1 + 8:</entry>
+ <entry>Cb<subscript>20</subscript></entry>
+ <entry>Cb<subscript>21</subscript></entry>
+ <entry>Cb<subscript>22</subscript></entry>
+ <entry>Cb<subscript>23</subscript></entry>
+ </row>
+ <row>
+ <entry>start1 + 12:</entry>
+ <entry>Cb<subscript>20</subscript></entry>
+ <entry>Cb<subscript>21</subscript></entry>
+ <entry>Cb<subscript>32</subscript></entry>
+ <entry>Cb<subscript>33</subscript></entry>
+ </row>
+ <row><entry></entry></row>
+ <row>
+ <entry>start2 + 0:</entry>
+ <entry>Cr<subscript>00</subscript></entry>
+ <entry>Cr<subscript>01</subscript></entry>
+ <entry>Cr<subscript>02</subscript></entry>
+ <entry>Cr<subscript>03</subscript></entry>
+ </row>
+ <row>
+ <entry>start2 + 4:</entry>
+ <entry>Cr<subscript>10</subscript></entry>
+ <entry>Cr<subscript>11</subscript></entry>
+ <entry>Cr<subscript>12</subscript></entry>
+ <entry>Cr<subscript>13</subscript></entry>
+ </row>
+ <row>
+ <entry>start2 + 8:</entry>
+ <entry>Cr<subscript>20</subscript></entry>
+ <entry>Cr<subscript>21</subscript></entry>
+ <entry>Cr<subscript>22</subscript></entry>
+ <entry>Cr<subscript>23</subscript></entry>
+ </row>
+ <row>
+ <entry>start2 + 12:</entry>
+ <entry>Cr<subscript>30</subscript></entry>
+ <entry>Cr<subscript>31</subscript></entry>
+ <entry>Cr<subscript>32</subscript></entry>
+ <entry>Cr<subscript>33</subscript></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </para>
+ </formalpara>
+
+ <formalpara>
+ <title>Color Sample Location.</title>
+ <para>
+ <informaltable frame="none">
+ <tgroup cols="7" align="center">
+ <tbody valign="top">
+ <row>
+ <entry></entry>
+ <entry>0</entry><entry></entry><entry>1</entry><entry></entry>
+ <entry>2</entry><entry></entry><entry>3</entry>
+ </row>
+ <row>
+ <entry>0</entry>
+ <entry>YC</entry><entry></entry><entry>YC</entry><entry></entry>
+ <entry>YC</entry><entry></entry><entry>YC</entry>
+ </row>
+ <row>
+ <entry>1</entry>
+ <entry>YC</entry><entry></entry><entry>YC</entry><entry></entry>
+ <entry>YC</entry><entry></entry><entry>YC</entry>
+ </row>
+ <row>
+ <entry>2</entry>
+ <entry>YC</entry><entry></entry><entry>YC</entry><entry></entry>
+ <entry>YC</entry><entry></entry><entry>YC</entry>
+ </row>
+ <row>
+ <entry>3</entry>
+ <entry>YC</entry><entry></entry><entry>YC</entry><entry></entry>
+ <entry>YC</entry><entry></entry><entry>YC</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </para>
+ </formalpara>
+ </example>
+ </refsect1>
+ </refentry>
+++ /dev/null
- <refentry id="V4L2-PIX-FMT-YVU420M">
- <refmeta>
- <refentrytitle>V4L2_PIX_FMT_YVU420M ('YM21')</refentrytitle>
- &manvol;
- </refmeta>
- <refnamediv>
- <refname> <constant>V4L2_PIX_FMT_YVU420M</constant></refname>
- <refpurpose>Variation of <constant>V4L2_PIX_FMT_YVU420</constant>
- with planes non contiguous in memory. </refpurpose>
- </refnamediv>
-
- <refsect1>
- <title>Description</title>
-
- <para>This is a multi-planar format, as opposed to a packed format.
-The three components are separated into three sub-images or planes.
-
-The Y plane is first. The Y plane has one byte per pixel. The Cr data
-constitutes the second plane which is half the width and half
-the height of the Y plane (and of the image). Each Cr belongs to four
-pixels, a two-by-two square of the image. For example,
-Cr<subscript>0</subscript> belongs to Y'<subscript>00</subscript>,
-Y'<subscript>01</subscript>, Y'<subscript>10</subscript>, and
-Y'<subscript>11</subscript>. The Cb data, just like the Cr plane, constitutes
-the third plane. </para>
-
- <para>If the Y plane has pad bytes after each row, then the Cr
-and Cb planes have half as many pad bytes after their rows. In other
-words, two Cx rows (including padding) is exactly as long as one Y row
-(including padding).</para>
-
- <para><constant>V4L2_PIX_FMT_YVU420M</constant> is intended to be
-used only in drivers and applications that support the multi-planar API,
-described in <xref linkend="planar-apis"/>. </para>
-
- <example>
- <title><constant>V4L2_PIX_FMT_YVU420M</constant> 4 × 4
-pixel image</title>
-
- <formalpara>
- <title>Byte Order.</title>
- <para>Each cell is one byte.
- <informaltable frame="none">
- <tgroup cols="5" align="center">
- <colspec align="left" colwidth="2*" />
- <tbody valign="top">
- <row>
- <entry>start0 + 0:</entry>
- <entry>Y'<subscript>00</subscript></entry>
- <entry>Y'<subscript>01</subscript></entry>
- <entry>Y'<subscript>02</subscript></entry>
- <entry>Y'<subscript>03</subscript></entry>
- </row>
- <row>
- <entry>start0 + 4:</entry>
- <entry>Y'<subscript>10</subscript></entry>
- <entry>Y'<subscript>11</subscript></entry>
- <entry>Y'<subscript>12</subscript></entry>
- <entry>Y'<subscript>13</subscript></entry>
- </row>
- <row>
- <entry>start0 + 8:</entry>
- <entry>Y'<subscript>20</subscript></entry>
- <entry>Y'<subscript>21</subscript></entry>
- <entry>Y'<subscript>22</subscript></entry>
- <entry>Y'<subscript>23</subscript></entry>
- </row>
- <row>
- <entry>start0 + 12:</entry>
- <entry>Y'<subscript>30</subscript></entry>
- <entry>Y'<subscript>31</subscript></entry>
- <entry>Y'<subscript>32</subscript></entry>
- <entry>Y'<subscript>33</subscript></entry>
- </row>
- <row><entry></entry></row>
- <row>
- <entry>start1 + 0:</entry>
- <entry>Cr<subscript>00</subscript></entry>
- <entry>Cr<subscript>01</subscript></entry>
- </row>
- <row>
- <entry>start1 + 2:</entry>
- <entry>Cr<subscript>10</subscript></entry>
- <entry>Cr<subscript>11</subscript></entry>
- </row>
- <row><entry></entry></row>
- <row>
- <entry>start2 + 0:</entry>
- <entry>Cb<subscript>00</subscript></entry>
- <entry>Cb<subscript>01</subscript></entry>
- </row>
- <row>
- <entry>start2 + 2:</entry>
- <entry>Cb<subscript>10</subscript></entry>
- <entry>Cb<subscript>11</subscript></entry>
- </row>
- </tbody>
- </tgroup>
- </informaltable>
- </para>
- </formalpara>
-
- <formalpara>
- <title>Color Sample Location.</title>
- <para>
- <informaltable frame="none">
- <tgroup cols="7" align="center">
- <tbody valign="top">
- <row>
- <entry></entry>
- <entry>0</entry><entry></entry><entry>1</entry><entry></entry>
- <entry>2</entry><entry></entry><entry>3</entry>
- </row>
- <row>
- <entry>0</entry>
- <entry>Y</entry><entry></entry><entry>Y</entry><entry></entry>
- <entry>Y</entry><entry></entry><entry>Y</entry>
- </row>
- <row>
- <entry></entry>
- <entry></entry><entry>C</entry><entry></entry><entry></entry>
- <entry></entry><entry>C</entry><entry></entry>
- </row>
- <row>
- <entry>1</entry>
- <entry>Y</entry><entry></entry><entry>Y</entry><entry></entry>
- <entry>Y</entry><entry></entry><entry>Y</entry>
- </row>
- <row>
- <entry></entry>
- </row>
- <row>
- <entry>2</entry>
- <entry>Y</entry><entry></entry><entry>Y</entry><entry></entry>
- <entry>Y</entry><entry></entry><entry>Y</entry>
- </row>
- <row>
- <entry></entry>
- <entry></entry><entry>C</entry><entry></entry><entry></entry>
- <entry></entry><entry>C</entry><entry></entry>
- </row>
- <row>
- <entry>3</entry>
- <entry>Y</entry><entry></entry><entry>Y</entry><entry></entry>
- <entry>Y</entry><entry></entry><entry>Y</entry>
- </row>
- </tbody>
- </tgroup>
- </informaltable>
- </para>
- </formalpara>
- </example>
- </refsect1>
- </refentry>
--- /dev/null
+<refentry id="V4L2-PIX-FMT-Z16">
+ <refmeta>
+ <refentrytitle>V4L2_PIX_FMT_Z16 ('Z16 ')</refentrytitle>
+ &manvol;
+ </refmeta>
+ <refnamediv>
+ <refname><constant>V4L2_PIX_FMT_Z16</constant></refname>
+ <refpurpose>Interleaved grey-scale image, e.g. from a stereo-pair</refpurpose>
+ </refnamediv>
+ <refsect1>
+ <title>Description</title>
+
+ <para>This is a 16-bit format, representing depth data. Each pixel is a
+distance to the respective point in the image coordinates. Distance unit can
+vary and has to be negotiated with the device separately. Each pixel is stored
+in a 16-bit word in the little endian byte order.
+</para>
+
+ <example>
+ <title><constant>V4L2_PIX_FMT_Z16</constant> 4 × 4
+pixel image</title>
+
+ <formalpara>
+ <title>Byte Order.</title>
+ <para>Each cell is one byte.
+ <informaltable frame="none">
+ <tgroup cols="9" align="center">
+ <colspec align="left" colwidth="2*" />
+ <tbody valign="top">
+ <row>
+ <entry>start + 0:</entry>
+ <entry>Z<subscript>00low</subscript></entry>
+ <entry>Z<subscript>00high</subscript></entry>
+ <entry>Z<subscript>01low</subscript></entry>
+ <entry>Z<subscript>01high</subscript></entry>
+ <entry>Z<subscript>02low</subscript></entry>
+ <entry>Z<subscript>02high</subscript></entry>
+ <entry>Z<subscript>03low</subscript></entry>
+ <entry>Z<subscript>03high</subscript></entry>
+ </row>
+ <row>
+ <entry>start + 8:</entry>
+ <entry>Z<subscript>10low</subscript></entry>
+ <entry>Z<subscript>10high</subscript></entry>
+ <entry>Z<subscript>11low</subscript></entry>
+ <entry>Z<subscript>11high</subscript></entry>
+ <entry>Z<subscript>12low</subscript></entry>
+ <entry>Z<subscript>12high</subscript></entry>
+ <entry>Z<subscript>13low</subscript></entry>
+ <entry>Z<subscript>13high</subscript></entry>
+ </row>
+ <row>
+ <entry>start + 16:</entry>
+ <entry>Z<subscript>20low</subscript></entry>
+ <entry>Z<subscript>20high</subscript></entry>
+ <entry>Z<subscript>21low</subscript></entry>
+ <entry>Z<subscript>21high</subscript></entry>
+ <entry>Z<subscript>22low</subscript></entry>
+ <entry>Z<subscript>22high</subscript></entry>
+ <entry>Z<subscript>23low</subscript></entry>
+ <entry>Z<subscript>23high</subscript></entry>
+ </row>
+ <row>
+ <entry>start + 24:</entry>
+ <entry>Z<subscript>30low</subscript></entry>
+ <entry>Z<subscript>30high</subscript></entry>
+ <entry>Z<subscript>31low</subscript></entry>
+ <entry>Z<subscript>31high</subscript></entry>
+ <entry>Z<subscript>32low</subscript></entry>
+ <entry>Z<subscript>32high</subscript></entry>
+ <entry>Z<subscript>33low</subscript></entry>
+ <entry>Z<subscript>33high</subscript></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </para>
+ </formalpara>
+ </example>
+ </refsect1>
+</refentry>
&sub-y10b;
&sub-y16;
&sub-y16-be;
+ &sub-y8i;
+ &sub-y12i;
&sub-uv8;
&sub-yuyv;
&sub-uyvy;
&sub-y41p;
&sub-yuv420;
&sub-yuv420m;
- &sub-yvu420m;
+ &sub-yuv422m;
+ &sub-yuv444m;
&sub-yuv410;
&sub-yuv422p;
&sub-yuv411p;
&sub-m420;
</section>
+ <section id="depth-formats">
+ <title>Depth Formats</title>
+ <para>Depth data provides distance to points, mapped onto the image plane
+ </para>
+
+ &sub-z16;
+ </section>
+
<section>
<title>Compressed Formats</title>
automatically, similar to sensing the video standard. To do so, applications
call <constant>VIDIOC_QUERY_DV_TIMINGS</constant> with a pointer to a
&v4l2-dv-timings;. Once the hardware detects the timings, it will fill in the
-timings structure.
+timings structure.</para>
-If the timings could not be detected because there was no signal, then
+<para>Please note that drivers shall <emphasis>not</emphasis> switch timings automatically
+if new timings are detected. Instead, drivers should send the
+<constant>V4L2_EVENT_SOURCE_CHANGE</constant> event (if they support this) and expect
+that userspace will take action by calling <constant>VIDIOC_QUERY_DV_TIMINGS</constant>.
+The reason is that new timings usually mean different buffer sizes as well, and you
+cannot change buffer sizes on the fly. In general, applications that receive the
+Source Change event will have to call <constant>VIDIOC_QUERY_DV_TIMINGS</constant>,
+and if the detected timings are valid they will have to stop streaming, set the new
+timings, allocate new buffers and start streaming again.</para>
+
+<para>If the timings could not be detected because there was no signal, then
<errorcode>ENOLINK</errorcode> is returned. If a signal was detected, but
it was unstable and the receiver could not lock to the signal, then
<errorcode>ENOLCK</errorcode> is returned. If the receiver could lock to the signal,
possible or fails, the set must contain all standards supported by the
current video input or output.</para>
+<para>Please note that drivers shall <emphasis>not</emphasis> switch the video standard
+automatically if a new video standard is detected. Instead, drivers should send the
+<constant>V4L2_EVENT_SOURCE_CHANGE</constant> event (if they support this) and expect
+that userspace will take action by calling <constant>VIDIOC_QUERYSTD</constant>.
+The reason is that a new video standard can mean different buffer sizes as well, and you
+cannot change buffer sizes on the fly. In general, applications that receive the
+Source Change event will have to call <constant>VIDIOC_QUERYSTD</constant>,
+and if the detected video standard is valid they will have to stop streaming, set the new
+standard, allocate new buffers and start streaming again.</para>
+
</refsect1>
<refsect1>
- link-frequencies: List of allowed link frequencies in Hz. Each frequency is
expressed as a 64-bit big-endian integer.
+- reset-gpios: GPIO handle which is connected to the reset pin of the chip.
+- standby-gpios: GPIO handle which is connected to the standby pin of the chip.
For further reading on port node refer to
Documentation/devicetree/bindings/media/video-interfaces.txt.
--- /dev/null
+* Texas Instruments TVP5150 and TVP5151 video decoders
+
+The TVP5150 and TVP5151 are video decoders that convert baseband NTSC and PAL
+(and also SECAM in the TVP5151 case) video signals to either 8-bit 4:2:2 YUV
+with discrete syncs or 8-bit ITU-R BT.656 with embedded syncs output formats.
+
+Required Properties:
+- compatible: value must be "ti,tvp5150"
+- reg: I2C slave address
+
+Optional Properties:
+- pdn-gpios: phandle for the GPIO connected to the PDN pin, if any.
+- reset-gpios: phandle for the GPIO connected to the RESETB pin, if any.
+
+The device node must contain one 'port' child node for its digital output
+video port, in accordance with the video interface bindings defined in
+Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+Required Endpoint Properties for parallel synchronization:
+
+- hsync-active: active state of the HSYNC signal. Must be <1> (HIGH).
+- vsync-active: active state of the VSYNC signal. Must be <1> (HIGH).
+- field-even-active: field signal level during the even field data
+ transmission. Must be <0>.
+
+If none of hsync-active, vsync-active and field-even-active is specified,
+the endpoint is assumed to use embedded BT.656 synchronization.
+
+Example:
+
+&i2c2 {
+ ...
+ tvp5150@5c {
+ compatible = "ti,tvp5150";
+ reg = <0x5c>;
+ pdn-gpios = <&gpio4 30 GPIO_ACTIVE_LOW>;
+ reset-gpios = <&gpio6 7 GPIO_ACTIVE_LOW>;
+
+ port {
+ tvp5150_1: endpoint {
+ remote-endpoint = <&ccdc_ep>;
+ };
+ };
+ };
+};
channel which can be either RGB, YUYV or BT656.
- compatible: Must be one of the following
+ - "renesas,vin-r8a7795" for the R8A7795 device
- "renesas,vin-r8a7794" for the R8A7794 device
- "renesas,vin-r8a7793" for the R8A7793 device
- "renesas,vin-r8a7791" for the R8A7791 device
can encode image data and decode JPEG data quickly.
Required properties:
- - compatible: should containg one of the following:
- - "renesas,jpu-r8a7790" for R-Car H2
- - "renesas,jpu-r8a7791" for R-Car M2-W
- - "renesas,jpu-r8a7792" for R-Car V2H
- - "renesas,jpu-r8a7793" for R-Car M2-N
+- compatible: "renesas,jpu-<soctype>", "renesas,rcar-gen2-jpu" as fallback.
+ Examples with soctypes are:
+ - "renesas,jpu-r8a7790" for R-Car H2
+ - "renesas,jpu-r8a7791" for R-Car M2-W
+ - "renesas,jpu-r8a7792" for R-Car V2H
+ - "renesas,jpu-r8a7793" for R-Car M2-N
- reg: Base address and length of the registers block for the JPU.
- interrupts: JPU interrupt specifier.
Example: R8A7790 (R-Car H2) JPU node
jpeg-codec@fe980000 {
- compatible = "renesas,jpu-r8a7790";
+ compatible = "renesas,jpu-r8a7790", "renesas,rcar-gen2-jpu";
reg = <0 0xfe980000 0 0x10300>;
interrupts = <0 272 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&mstp1_clks R8A7790_CLK_JPU>;
-* Renesas VSP1 Video Processing Engine
+* Renesas VSP Video Processing Engine
-The VSP1 is a video processing engine that supports up-/down-scaling, alpha
+The VSP is a video processing engine that supports up-/down-scaling, alpha
blending, color space conversion and various other image processing features.
It can be found in the Renesas R-Car second generation SoCs.
Required properties:
- - compatible: Must contain "renesas,vsp1"
+ - compatible: Must contain one of the following values
+ - "renesas,vsp1" for the R-Car Gen2 VSP1
+ - "renesas,vsp2" for the R-Car Gen3 VSP2
- - reg: Base address and length of the registers block for the VSP1.
- - interrupts: VSP1 interrupt specifier.
- - clocks: A phandle + clock-specifier pair for the VSP1 functional clock.
-
- - renesas,#rpf: Number of Read Pixel Formatter (RPF) modules in the VSP1.
- - renesas,#uds: Number of Up Down Scaler (UDS) modules in the VSP1.
- - renesas,#wpf: Number of Write Pixel Formatter (WPF) modules in the VSP1.
-
-
-Optional properties:
-
- - renesas,has-lif: Boolean, indicates that the LCD Interface (LIF) module is
- available.
- - renesas,has-lut: Boolean, indicates that the Look Up Table (LUT) module is
- available.
- - renesas,has-sru: Boolean, indicates that the Super Resolution Unit (SRU)
- module is available.
+ - reg: Base address and length of the registers block for the VSP.
+ - interrupts: VSP interrupt specifier.
+ - clocks: A phandle + clock-specifier pair for the VSP functional clock.
Example: R8A7790 (R-Car H2) VSP1-S node
reg = <0 0xfe928000 0 0x8000>;
interrupts = <0 267 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&mstp1_clks R8A7790_CLK_VSP1_S>;
-
- renesas,has-lut;
- renesas,has-sru;
- renesas,#rpf = <5>;
- renesas,#uds = <3>;
- renesas,#wpf = <4>;
};
--- /dev/null
+Texas Instruments DRA72x CAMERA ADAPTATION LAYER (CAL)
+------------------------------------------------------
+
+The Camera Adaptation Layer (CAL) is a key component for image capture
+applications. The capture module provides the system interface and the
+processing capability to connect CSI2 image-sensor modules to the
+DRA72x device.
+
+Required properties:
+- compatible: must be "ti,dra72-cal"
+- reg: CAL Top level, Receiver Core #0, Receiver Core #1 and Camera RX
+ control address space
+- reg-names: cal_top, cal_rx_core0, cal_rx_core1, and camerrx_control
+ registers
+- interrupts: should contain IRQ line for the CAL;
+
+CAL supports 2 camera port nodes on MIPI bus. Each CSI2 camera port nodes
+should contain a 'port' child node with child 'endpoint' node. Please
+refer to the bindings defined in
+Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+Example:
+ cal: cal@4845b000 {
+ compatible = "ti,dra72-cal";
+ ti,hwmods = "cal";
+ reg = <0x4845B000 0x400>,
+ <0x4845B800 0x40>,
+ <0x4845B900 0x40>,
+ <0x4A002e94 0x4>;
+ reg-names = "cal_top",
+ "cal_rx_core0",
+ "cal_rx_core1",
+ "camerrx_control";
+ interrupts = <GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ csi2_0: port@0 {
+ reg = <0>;
+ endpoint {
+ slave-mode;
+ remote-endpoint = <&ar0330_1>;
+ };
+ };
+ csi2_1: port@1 {
+ reg = <1>;
+ };
+ };
+ };
+
+ i2c5: i2c@4807c000 {
+ ar0330@10 {
+ compatible = "ti,ar0330";
+ reg = <0x10>;
+
+ port {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ar0330_1: endpoint {
+ reg = <0>;
+ clock-lanes = <1>;
+ data-lanes = <0 2 3 4>;
+ remote-endpoint = <&csi2_0>;
+ };
+ };
+ };
+ };
Patches, comments and suggestions are very very welcome.
3. Acknowledgements
- Amaury Demol (ademol@dibcom.fr) and Francois Kanounnikoff from DiBcom for
+ Amaury Demol (Amaury.Demol@parrot.com) and Francois Kanounnikoff from DiBcom for
providing specs, code and help, on which the dvb-dibusb, dib3000mb and
dib3000mc are based.
193 -> WIS Voyager or compatible [1905:7007]
194 -> AverMedia AverTV/505 [1461:a10a]
195 -> Leadtek Winfast TV2100 FM [107d:6f3a]
+196 -> SnaZio* TVPVR PRO [1779:13cf]
volume = v4l2_ctrl_new_std(&video_ctrl_handler, &ops, V4L2_CID_AUDIO_VOLUME, ...);
v4l2_ctrl_new_std(&video_ctrl_handler, &ops, V4L2_CID_BRIGHTNESS, ...);
v4l2_ctrl_new_std(&video_ctrl_handler, &ops, V4L2_CID_CONTRAST, ...);
- v4l2_ctrl_add_ctrl(&radio_ctrl_handler, volume);
What you should not do is make two identical controls for two handlers.
For example:
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/mn88473/
-F: drivers/media/dvb-frontends/mn88473.h
+F: drivers/media/dvb-frontends/mn88473*
MODULE SUPPORT
M: Rusty Russell <rusty@rustcorp.com.au>
S: Maintained
F: drivers/thermal/ti-soc-thermal/
+TI VPE/CAL DRIVERS
+M: Benoit Parrot <bparrot@ti.com>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+S: Maintained
+F: drivers/media/platform/ti-vpe/
+
TI CDCE706 CLOCK DRIVER
M: Max Filippov <jcmvbkbc@gmail.com>
S: Maintained
#endif
/* lnb control */
-#if FE_SUPPORTED(MT312) || FE_SUPPORTED(STV0299)
+#if (FE_SUPPORTED(MT312) || FE_SUPPORTED(STV0299)) && FE_SUPPORTED(PLL)
static int flexcop_set_voltage(struct dvb_frontend *fe,
enum fe_sec_voltage voltage)
{
#endif
#if FE_SUPPORTED(S5H1420) || FE_SUPPORTED(STV0299) || FE_SUPPORTED(MT312)
-static int flexcop_sleep(struct dvb_frontend* fe)
+static int __maybe_unused flexcop_sleep(struct dvb_frontend* fe)
{
struct flexcop_device *fc = fe->dvb->priv;
if (fc->fe_sleep)
/*
* Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
* flexcop.c - main module part
- * Copyright (C) 2004-9 Patrick Boettcher <patrick.boettcher@desy.de>
+ * Copyright (C) 2004-9 Patrick Boettcher <patrick.boettcher@posteo.de>
* based on skystar2-driver Copyright (C) 2003 Vadim Catana, skystar@moldova.cc
*
* Acknowledgements:
#include "flexcop.h"
#define DRIVER_NAME "B2C2 FlexcopII/II(b)/III digital TV receiver chip"
-#define DRIVER_AUTHOR "Patrick Boettcher <patrick.boettcher@desy.de"
+#define DRIVER_AUTHOR "Patrick Boettcher <patrick.boettcher@posteo.de"
#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG
#define DEBSTATUS ""
/* cypress_firmware.c is part of the DVB USB library.
*
- * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de)
+ * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@posteo.de)
* see dvb-usb-init.c for copyright information.
*
* This file contains functions for downloading the firmware to Cypress FX 1
/*
- * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de)
+ * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@posteo.de)
* see dvb-usb-init.c for copyright information.
*
* This file contains functions for downloading the firmware to Cypress FX 1
return rc;
}
pr_debug("read fw %s, buffer size=0x%zx\n", fw_filename, fw->size);
- fw_buf = kmalloc(ALIGN(fw->size, SMS_ALLOC_ALIGNMENT),
- GFP_KERNEL | GFP_DMA);
+ fw_buf = kmalloc(ALIGN(fw->size + sizeof(struct sms_firmware),
+ SMS_ALLOC_ALIGNMENT), GFP_KERNEL | GFP_DMA);
if (!fw_buf) {
pr_err("failed to allocate firmware buffer\n");
rc = -ENOMEM;
}
}
-/* Nothing to do here, as stats are automatically updated */
-static int smsdvb_get_frontend(struct dvb_frontend *fe)
-{
- return 0;
-}
-
static int smsdvb_init(struct dvb_frontend *fe)
{
struct smsdvb_client_t *client =
.release = smsdvb_release,
.set_frontend = smsdvb_set_frontend,
- .get_frontend = smsdvb_get_frontend,
.get_tune_settings = smsdvb_get_tune_settings,
.read_status = smsdvb_read_status,
/* dvb-usb-ids.h is part of the DVB USB library.
*
- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) see
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de) see
* dvb-usb-init.c for copyright information.
*
* a header file containing define's for the USB device supported by the
#define USB_PID_DIBCOM_STK807XP 0x1f90
#define USB_PID_DIBCOM_STK807XPVR 0x1f98
#define USB_PID_DIBCOM_STK8096GP 0x1fa0
+#define USB_PID_DIBCOM_STK8096PVR 0x1faa
#define USB_PID_DIBCOM_NIM8096MD 0x1fa8
#define USB_PID_DIBCOM_TFE8096P 0x1f9C
#define USB_PID_DIBCOM_ANCHOR_2135_COLD 0x2131
#define USB_PID_AVERMEDIA_1867 0x1867
#define USB_PID_AVERMEDIA_A867 0xa867
#define USB_PID_AVERMEDIA_H335 0x0335
+#define USB_PID_AVERMEDIA_TD110 0xa110
#define USB_PID_AVERMEDIA_TWINSTAR 0x0825
#define USB_PID_TECHNOTREND_CONNECT_S2400 0x3006
#define USB_PID_TECHNOTREND_CONNECT_S2400_8KEEPROM 0x3009
#define USB_PID_TECHNOTREND_CONNECT_CT3650 0x300d
#define USB_PID_TECHNOTREND_CONNECT_S2_4600 0x3011
#define USB_PID_TECHNOTREND_CONNECT_CT2_4650_CI 0x3012
+#define USB_PID_TECHNOTREND_CONNECT_CT2_4650_CI_2 0x3015
#define USB_PID_TECHNOTREND_TVSTICK_CT2_4400 0x3014
#define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY 0x005a
#define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY_2 0x0081
#define USB_PID_TERRATEC_CINERGY_T_EXPRESS 0x0062
#define USB_PID_TERRATEC_CINERGY_T_XXS 0x0078
#define USB_PID_TERRATEC_CINERGY_T_XXS_2 0x00ab
+#define USB_PID_TERRATEC_CINERGY_S2_R1 0x00a8
+#define USB_PID_TERRATEC_CINERGY_S2_R2 0x00b0
+#define USB_PID_TERRATEC_CINERGY_S2_R3 0x0102
+#define USB_PID_TERRATEC_CINERGY_S2_R4 0x0105
#define USB_PID_TERRATEC_H7 0x10b4
#define USB_PID_TERRATEC_H7_2 0x10a3
#define USB_PID_TERRATEC_H7_3 0x10a5
#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
struct media_pipeline pipe;
- struct media_entity *pipe_start_entity;
#endif
};
static void dvb_frontend_wakeup(struct dvb_frontend *fe);
static int dtv_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c,
struct dvb_frontend_parameters *p_out);
-static int dtv_property_legacy_params_sync(struct dvb_frontend *fe,
- struct dvb_frontend_parameters *p);
+static int
+dtv_property_legacy_params_sync(struct dvb_frontend *fe,
+ const struct dtv_frontend_properties *c,
+ struct dvb_frontend_parameters *p);
static bool has_get_frontend(struct dvb_frontend *fe)
{
enum fe_status status)
{
struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
struct dvb_fe_events *events = &fepriv->events;
struct dvb_frontend_event *e;
int wp;
dev_dbg(fe->dvb->device, "%s:\n", __func__);
if ((status & FE_HAS_LOCK) && has_get_frontend(fe))
- dtv_get_frontend(fe, &fepriv->parameters_out);
+ dtv_get_frontend(fe, c, &fepriv->parameters_out);
mutex_lock(&events->mtx);
wake_up_interruptible(&fepriv->wait_queue);
}
-/**
- * dvb_enable_media_tuner() - tries to enable the DVB tuner
- *
- * @fe: struct dvb_frontend pointer
- *
- * This function ensures that just one media tuner is enabled for a given
- * frontend. It has two different behaviors:
- * - For trivial devices with just one tuner:
- * it just enables the existing tuner->fe link
- * - For devices with more than one tuner:
- * It is up to the driver to implement the logic that will enable one tuner
- * and disable the other ones. However, if more than one tuner is enabled for
- * the same frontend, it will print an error message and return -EINVAL.
- *
- * At return, it will return the error code returned by media_entity_setup_link,
- * or 0 if everything is OK, if no tuner is linked to the frontend or if the
- * mdev is NULL.
- */
-#ifdef CONFIG_MEDIA_CONTROLLER_DVB
-static int dvb_enable_media_tuner(struct dvb_frontend *fe)
-{
- struct dvb_frontend_private *fepriv = fe->frontend_priv;
- struct dvb_adapter *adapter = fe->dvb;
- struct media_device *mdev = adapter->mdev;
- struct media_entity *entity, *source;
- struct media_link *link, *found_link = NULL;
- int ret, n_links = 0, active_links = 0;
-
- fepriv->pipe_start_entity = NULL;
-
- if (!mdev)
- return 0;
-
- entity = fepriv->dvbdev->entity;
- fepriv->pipe_start_entity = entity;
-
- list_for_each_entry(link, &entity->links, list) {
- if (link->sink->entity == entity) {
- found_link = link;
- n_links++;
- if (link->flags & MEDIA_LNK_FL_ENABLED)
- active_links++;
- }
- }
-
- if (!n_links || active_links == 1 || !found_link)
- return 0;
-
- /*
- * If a frontend has more than one tuner linked, it is up to the driver
- * to select with one will be the active one, as the frontend core can't
- * guess. If the driver doesn't do that, it is a bug.
- */
- if (n_links > 1 && active_links != 1) {
- dev_err(fe->dvb->device,
- "WARNING: there are %d active links among %d tuners. This is a driver's bug!\n",
- active_links, n_links);
- return -EINVAL;
- }
-
- source = found_link->source->entity;
- fepriv->pipe_start_entity = source;
- list_for_each_entry(link, &source->links, list) {
- struct media_entity *sink;
- int flags = 0;
-
- sink = link->sink->entity;
- if (sink == entity)
- flags = MEDIA_LNK_FL_ENABLED;
-
- ret = media_entity_setup_link(link, flags);
- if (ret) {
- dev_err(fe->dvb->device,
- "Couldn't change link %s->%s to %s. Error %d\n",
- source->name, sink->name,
- flags ? "enabled" : "disabled",
- ret);
- return ret;
- } else
- dev_dbg(fe->dvb->device,
- "link %s->%s was %s\n",
- source->name, sink->name,
- flags ? "ENABLED" : "disabled");
- }
- return 0;
-}
-#endif
-
static int dvb_frontend_thread(void *data)
{
struct dvb_frontend *fe = data;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
struct dvb_frontend_private *fepriv = fe->frontend_priv;
enum fe_status s;
enum dvbfe_algo algo;
-#ifdef CONFIG_MEDIA_CONTROLLER_DVB
- int ret;
-#endif
-
bool re_tune = false;
bool semheld = false;
fepriv->wakeup = 0;
fepriv->reinitialise = 0;
-#ifdef CONFIG_MEDIA_CONTROLLER_DVB
- ret = dvb_enable_media_tuner(fe);
- if (ret) {
- /* FIXME: return an error if it fails */
- dev_info(fe->dvb->device,
- "proceeding with FE task\n");
- } else if (fepriv->pipe_start_entity) {
- ret = media_entity_pipeline_start(fepriv->pipe_start_entity,
- &fepriv->pipe);
- if (ret)
- return ret;
- }
-#endif
-
dvb_frontend_init(fe);
set_freezable();
fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN;
fepriv->delay = HZ / 2;
}
- dtv_property_legacy_params_sync(fe, &fepriv->parameters_out);
+ dtv_property_legacy_params_sync(fe, c, &fepriv->parameters_out);
fe->ops.read_status(fe, &s);
if (s != fepriv->status) {
dvb_frontend_add_event(fe, s); /* update event list */
}
}
-#ifdef CONFIG_MEDIA_CONTROLLER_DVB
- if (fepriv->pipe_start_entity)
- media_entity_pipeline_stop(fepriv->pipe_start_entity);
- fepriv->pipe_start_entity = NULL;
-#endif
-
if (dvb_powerdown_on_sleep) {
if (fe->ops.set_voltage)
fe->ops.set_voltage(fe, SEC_VOLTAGE_OFF);
s32 delta;
*waketime = ktime_add_us(*waketime, add_usec);
- delta = ktime_us_delta(ktime_get_real(), *waketime);
+ delta = ktime_us_delta(ktime_get_boottime(), *waketime);
if (delta > 2500) {
msleep((delta - 1500) / 1000);
- delta = ktime_us_delta(ktime_get_real(), *waketime);
+ delta = ktime_us_delta(ktime_get_boottime(), *waketime);
}
if (delta > 0)
udelay(delta);
_DTV_CMD(DTV_STAT_TOTAL_BLOCK_COUNT, 0, 0),
};
-static void dtv_property_dump(struct dvb_frontend *fe, struct dtv_property *tvp)
+static void dtv_property_dump(struct dvb_frontend *fe,
+ bool is_set,
+ struct dtv_property *tvp)
{
int i;
if (tvp->cmd <= 0 || tvp->cmd > DTV_MAX_COMMAND) {
- dev_warn(fe->dvb->device, "%s: tvp.cmd = 0x%08x undefined\n",
- __func__, tvp->cmd);
+ dev_warn(fe->dvb->device, "%s: %s tvp.cmd = 0x%08x undefined\n",
+ __func__,
+ is_set ? "SET" : "GET",
+ tvp->cmd);
return;
}
- dev_dbg(fe->dvb->device, "%s: tvp.cmd = 0x%08x (%s)\n", __func__,
- tvp->cmd, dtv_cmds[tvp->cmd].name);
+ dev_dbg(fe->dvb->device, "%s: %s tvp.cmd = 0x%08x (%s)\n", __func__,
+ is_set ? "SET" : "GET",
+ tvp->cmd,
+ dtv_cmds[tvp->cmd].name);
if (dtv_cmds[tvp->cmd].buffer) {
dev_dbg(fe->dvb->device, "%s: tvp.u.buffer.len = 0x%02x\n",
/* Ensure the cached values are set correctly in the frontend
* legacy tuning structures, for the advanced tuning API.
*/
-static int dtv_property_legacy_params_sync(struct dvb_frontend *fe,
- struct dvb_frontend_parameters *p)
+static int
+dtv_property_legacy_params_sync(struct dvb_frontend *fe,
+ const struct dtv_frontend_properties *c,
+ struct dvb_frontend_parameters *p)
{
- const struct dtv_frontend_properties *c = &fe->dtv_property_cache;
-
p->frequency = c->frequency;
p->inversion = c->inversion;
* If p_out is not null, it will update the DVBv3 params pointed by it.
*/
static int dtv_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c,
struct dvb_frontend_parameters *p_out)
{
int r;
if (fe->ops.get_frontend) {
- r = fe->ops.get_frontend(fe);
+ r = fe->ops.get_frontend(fe, c);
if (unlikely(r < 0))
return r;
if (p_out)
- dtv_property_legacy_params_sync(fe, p_out);
+ dtv_property_legacy_params_sync(fe, c, p_out);
return 0;
}
return r;
}
- dtv_property_dump(fe, tvp);
+ dtv_property_dump(fe, false, tvp);
return 0;
}
return r;
}
+ dtv_property_dump(fe, true, tvp);
+
switch(tvp->cmd) {
case DTV_CLEAR:
/*
dev_dbg(fe->dvb->device, "%s: Property cache is full, tuning\n", __func__);
} else if (cmd == FE_GET_PROPERTY) {
+ struct dtv_frontend_properties getp = fe->dtv_property_cache;
+
dev_dbg(fe->dvb->device, "%s: properties.num = %d\n", __func__, tvps->num);
dev_dbg(fe->dvb->device, "%s: properties.props = %p\n", __func__, tvps->props);
}
/*
- * Fills the cache out struct with the cache contents, plus
- * the data retrieved from get_frontend, if the frontend
- * is not idle. Otherwise, returns the cached content
+ * Let's use our own copy of property cache, in order to
+ * avoid mangling with DTV zigzag logic, as drivers might
+ * return crap, if they don't check if the data is available
+ * before updating the properties cache.
*/
if (fepriv->state != FESTATE_IDLE) {
- err = dtv_get_frontend(fe, NULL);
+ err = dtv_get_frontend(fe, &getp, NULL);
if (err < 0)
goto out;
}
for (i = 0; i < tvps->num; i++) {
- err = dtv_property_process_get(fe, c, tvp + i, file);
+ err = dtv_property_process_get(fe, &getp, tvp + i, file);
if (err < 0)
goto out;
(tvp + i)->result = err;
* the user. FE_SET_FRONTEND triggers an initial frontend event
* with status = 0, which copies output parameters to userspace.
*/
- dtv_property_legacy_params_sync(fe, &fepriv->parameters_out);
+ dtv_property_legacy_params_sync(fe, c, &fepriv->parameters_out);
/*
* Be sure that the bandwidth will be filled for all
u8 last = 1;
if (dvb_frontend_debug)
printk("%s switch command: 0x%04lx\n", __func__, swcmd);
- nexttime = ktime_get_real();
+ nexttime = ktime_get_boottime();
if (dvb_frontend_debug)
tv[0] = nexttime;
/* before sending a command, initialize by sending
for (i = 0; i < 9; i++) {
if (dvb_frontend_debug)
- tv[i+1] = ktime_get_real();
+ tv[i+1] = ktime_get_boottime();
if ((swcmd & 0x01) != last) {
/* set voltage to (last ? 13V : 18V) */
fe->ops.set_voltage(fe, (last) ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18);
err = dvb_frontend_get_event (fe, parg, file->f_flags);
break;
- case FE_GET_FRONTEND:
- err = dtv_get_frontend(fe, parg);
- break;
+ case FE_GET_FRONTEND: {
+ struct dtv_frontend_properties getp = fe->dtv_property_cache;
+ /*
+ * Let's use our own copy of property cache, in order to
+ * avoid mangling with DTV zigzag logic, as drivers might
+ * return crap, if they don't check if the data is available
+ * before updating the properties cache.
+ */
+ err = dtv_get_frontend(fe, &getp, parg);
+ break;
+ }
case FE_SET_FRONTEND_TUNE_MODE:
fepriv->tune_mode_flags = (unsigned long) parg;
err = 0;
fepriv->tone = -1;
fepriv->voltage = -1;
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ if (fe->dvb->mdev && fe->dvb->mdev->enable_source) {
+ ret = fe->dvb->mdev->enable_source(dvbdev->entity,
+ &fepriv->pipe);
+ if (ret) {
+ dev_err(fe->dvb->device,
+ "Tuner is busy. Error %d\n", ret);
+ goto err2;
+ }
+ }
+#endif
ret = dvb_frontend_start (fe);
if (ret)
- goto err2;
+ goto err3;
/* empty event queue */
fepriv->events.eventr = fepriv->events.eventw = 0;
mutex_unlock (&adapter->mfe_lock);
return ret;
+err3:
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ if (fe->dvb->mdev && fe->dvb->mdev->disable_source)
+ fe->dvb->mdev->disable_source(dvbdev->entity);
err2:
+#endif
dvb_generic_release(inode, file);
err1:
if (dvbdev->users == -1 && fe->ops.ts_bus_ctrl)
if (dvbdev->users == -1) {
wake_up(&fepriv->wait_queue);
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ if (fe->dvb->mdev && fe->dvb->mdev->disable_source)
+ fe->dvb->mdev->disable_source(dvbdev->entity);
+#endif
if (fe->exit != DVB_FE_NO_EXIT)
wake_up(&dvbdev->wait_queue);
if (fe->ops.ts_bus_ctrl)
int (*set_frontend)(struct dvb_frontend *fe);
int (*get_tune_settings)(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* settings);
- int (*get_frontend)(struct dvb_frontend *fe);
+ int (*get_frontend)(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *props);
int (*read_status)(struct dvb_frontend *fe, enum fe_status *status);
int (*read_ber)(struct dvb_frontend* fe, u32* ber);
#define DVB_MAX_IDS MAX_DVB_MINORS
#else
#define DVB_MAX_IDS 4
-#define nums2minor(num,type,id) ((num << 6) | (id << 4) | type)
+#define nums2minor(num, type, id) ((num << 6) | (id << 4) | type)
#define MAX_DVB_MINORS (DVB_MAX_ADAPTERS*64)
#endif
file->private_data = dvbdev;
replace_fops(file, new_fops);
if (file->f_op->open)
- err = file->f_op->open(inode,file);
+ err = file->f_op->open(inode, file);
up_read(&minor_rwsem);
mutex_unlock(&dvbdev_mutex);
return err;
ret = media_device_register_entity(dvbdev->adapter->mdev,
dvbdev->entity);
if (ret)
- return (ret);
+ return ret;
printk(KERN_DEBUG "%s: media entity '%s' registered.\n",
__func__, dvbdev->entity->name);
return -ENOMEM;
adap->conn = conn;
- adap->conn_pads = kcalloc(1, sizeof(*adap->conn_pads),
- GFP_KERNEL);
+ adap->conn_pads = kzalloc(sizeof(*adap->conn_pads), GFP_KERNEL);
if (!adap->conn_pads)
return -ENOMEM;
if (ntuner && ndemod) {
ret = media_create_pad_links(mdev,
MEDIA_ENT_F_TUNER,
- tuner, TUNER_PAD_IF_OUTPUT,
+ tuner, TUNER_PAD_OUTPUT,
MEDIA_ENT_F_DTV_DEMOD,
demod, 0, MEDIA_LNK_FL_ENABLED,
false);
if (demux && ca) {
ret = media_create_pad_link(demux, 1, ca,
0, MEDIA_LNK_FL_ENABLED);
- if (!ret)
+ if (ret)
return -ENOMEM;
}
parg = sbuf;
} else {
/* too big to allocate from stack */
- mbuf = kmalloc(_IOC_SIZE(cmd),GFP_KERNEL);
+ mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL);
if (NULL == mbuf)
return -ENOMEM;
parg = mbuf;
Say Y when you want to support this frontend.
+config DVB_MN88473
+ tristate "Panasonic MN88473"
+ depends on DVB_CORE && I2C
+ select REGMAP_I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y when you want to support this frontend.
+
comment "DVB-S (satellite) frontends"
depends on DVB_CORE
obj-$(CONFIG_DVB_STV090x) += stv090x.o
obj-$(CONFIG_DVB_STV6110x) += stv6110x.o
obj-$(CONFIG_DVB_M88DS3103) += m88ds3103.o
+obj-$(CONFIG_DVB_MN88473) += mn88473.o
obj-$(CONFIG_DVB_ISL6423) += isl6423.o
obj-$(CONFIG_DVB_EC100) += ec100.o
obj-$(CONFIG_DVB_HD29L2) += hd29l2.o
return ret;
}
-static int af9013_get_frontend(struct dvb_frontend *fe)
+static int af9013_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
struct af9013_state *state = fe->demodulator_priv;
int ret;
u8 buf[3];
static void af9013_release(struct dvb_frontend *fe)
{
struct af9013_state *state = fe->demodulator_priv;
+
+ /* stop statistics polling */
+ cancel_delayed_work_sync(&state->statistics_work);
+
kfree(state);
}
return ret;
}
-static int af9033_get_frontend(struct dvb_frontend *fe)
+static int af9033_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
{
struct af9033_dev *dev = fe->demodulator_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
int ret;
u8 buf[8];
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);
return state->ops->set_tune(state->priv, &tune_args);
}
-static int as102_fe_get_frontend(struct dvb_frontend *fe)
+static int as102_fe_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
{
struct as102_state *state = fe->demodulator_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
int ret = 0;
struct as10x_tps tps = { 0 };
return 0;
}
-static int atbm8830_get_fe(struct dvb_frontend *fe)
+static int atbm8830_get_fe(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
dprintk("%s\n", __func__);
/* TODO: get real readings from device */
AU8522_AUDIO_NONE,
AU8522_AUDIO_SIF,
};
-
#endif /* __AU8522_H__ */
v4l2_i2c_subdev_init(sd, client, &au8522_ops);
#if defined(CONFIG_MEDIA_CONTROLLER)
- state->pads[AU8522_PAD_INPUT].flags = MEDIA_PAD_FL_SINK;
- state->pads[AU8522_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE;
- state->pads[AU8522_PAD_VBI_OUT].flags = MEDIA_PAD_FL_SOURCE;
+ state->pads[DEMOD_PAD_IF_INPUT].flags = MEDIA_PAD_FL_SINK;
+ state->pads[DEMOD_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE;
+ state->pads[DEMOD_PAD_VBI_OUT].flags = MEDIA_PAD_FL_SOURCE;
+ state->pads[DEMOD_PAD_AUDIO_OUT].flags = MEDIA_PAD_FL_SOURCE;
sd->entity.function = MEDIA_ENT_F_ATV_DECODER;
ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(state->pads),
return au8522_read_ucblocks(fe, ber);
}
-static int au8522_get_frontend(struct dvb_frontend *fe)
+static int au8522_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
struct au8522_state *state = fe->demodulator_priv;
c->frequency = state->current_frequency;
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
+#include <media/v4l2-mc.h>
#include <linux/i2c.h>
#include "dvb_frontend.h"
#include "au8522.h"
#define AU8522_DIGITAL_MODE 1
#define AU8522_SUSPEND_MODE 2
-enum au8522_media_pads {
- AU8522_PAD_INPUT,
- AU8522_PAD_VID_OUT,
- AU8522_PAD_VBI_OUT,
-
- AU8522_NUM_PADS
-};
-
struct au8522_state {
struct i2c_client *c;
struct i2c_adapter *i2c;
struct v4l2_ctrl_handler hdl;
#ifdef CONFIG_MEDIA_CONTROLLER
- struct media_pad pads[AU8522_NUM_PADS];
+ struct media_pad pads[DEMOD_NUM_PADS];
#endif
};
*
* Copyright (C) 2001-5, B2C2 inc.
*
- * GPL/Linux driver written by Patrick Boettcher <patrick.boettcher@desy.de>
+ * GPL/Linux driver written by Patrick Boettcher <patrick.boettcher@posteo.de>
*
* This driver is "hard-coded" to be used with the 1st generation of
* Technisat/B2C2's Air2PC ATSC PCI/USB cards/boxes. The pll-programming
};
MODULE_DESCRIPTION("Broadcom BCM3510 ATSC (8VSB/16VSB & ITU J83 AnnexB FEC QAM64/256) demodulator driver");
-MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
MODULE_LICENSE("GPL");
*
* Copyright (C) 2001-5, B2C2 inc.
*
- * GPL/Linux driver written by Patrick Boettcher <patrick.boettcher@desy.de>
+ * GPL/Linux driver written by Patrick Boettcher <patrick.boettcher@posteo.de>
*
* 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
*
* Copyright (C) 2001-5, B2C2 inc.
*
- * GPL/Linux driver written by Patrick Boettcher <patrick.boettcher@desy.de>
+ * GPL/Linux driver written by Patrick Boettcher <patrick.boettcher@posteo.de>
*
* 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
return 0;
}
-static int cx22700_get_frontend(struct dvb_frontend *fe)
+static int cx22700_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
struct cx22700_state* state = fe->demodulator_priv;
u8 reg09 = cx22700_readreg (state, 0x09);
return 0;
}
-static int cx22702_get_frontend(struct dvb_frontend *fe)
+static int cx22702_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
struct cx22702_state *state = fe->demodulator_priv;
u8 reg0C = cx22702_readreg(state, 0x0C);
return 0;
}
-static int cx24110_get_frontend(struct dvb_frontend *fe)
+static int cx24110_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct cx24110_state *state = fe->demodulator_priv;
s32 afc; unsigned sclk;
return DVBFE_ALGO_HW;
}
-static int cx24117_get_frontend(struct dvb_frontend *fe)
+static int cx24117_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
{
struct cx24117_state *state = fe->demodulator_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
struct cx24117_cmd cmd;
u8 reg, st, inv;
int ret, idx;
return 0;
}
-static int cx24120_get_frontend(struct dvb_frontend *fe)
+static int cx24120_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
struct cx24120_state *state = fe->demodulator_priv;
u8 freq1, freq2, freq3;
+ int status;
dev_dbg(&state->i2c->dev, "\n");
/* don't return empty data if we're not tuned in */
- if ((state->fe_status & FE_HAS_LOCK) == 0)
+ status = cx24120_readreg(state, CX24120_REG_STATUS);
+ if (!(status & CX24120_HAS_LOCK))
return 0;
/* Get frequency */
return 0;
}
-static int cx24123_get_frontend(struct dvb_frontend *fe)
+static int cx24123_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct cx24123_state *state = fe->demodulator_priv;
dprintk("\n");
return ret;
}
-int cxd2820r_get_frontend_c(struct dvb_frontend *fe)
+int cxd2820r_get_frontend_c(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
{
struct cxd2820r_priv *priv = fe->demodulator_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
int ret;
u8 buf[2];
return ret;
}
-static int cxd2820r_get_frontend(struct dvb_frontend *fe)
+static int cxd2820r_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
struct cxd2820r_priv *priv = fe->demodulator_priv;
int ret;
switch (fe->dtv_property_cache.delivery_system) {
case SYS_DVBT:
- ret = cxd2820r_get_frontend_t(fe);
+ ret = cxd2820r_get_frontend_t(fe, p);
break;
case SYS_DVBT2:
- ret = cxd2820r_get_frontend_t2(fe);
+ ret = cxd2820r_get_frontend_t2(fe, p);
break;
case SYS_DVBC_ANNEX_A:
- ret = cxd2820r_get_frontend_c(fe);
+ ret = cxd2820r_get_frontend_c(fe, p);
break;
default:
ret = -EINVAL;
static int cxd2820r_gpio_direction_output(struct gpio_chip *chip, unsigned nr,
int val)
{
- struct cxd2820r_priv *priv =
- container_of(chip, struct cxd2820r_priv, gpio_chip);
+ struct cxd2820r_priv *priv = gpiochip_get_data(chip);
u8 gpio[GPIO_COUNT];
dev_dbg(&priv->i2c->dev, "%s: nr=%d val=%d\n", __func__, nr, val);
static void cxd2820r_gpio_set(struct gpio_chip *chip, unsigned nr, int val)
{
- struct cxd2820r_priv *priv =
- container_of(chip, struct cxd2820r_priv, gpio_chip);
+ struct cxd2820r_priv *priv = gpiochip_get_data(chip);
u8 gpio[GPIO_COUNT];
dev_dbg(&priv->i2c->dev, "%s: nr=%d val=%d\n", __func__, nr, val);
static int cxd2820r_gpio_get(struct gpio_chip *chip, unsigned nr)
{
- struct cxd2820r_priv *priv =
- container_of(chip, struct cxd2820r_priv, gpio_chip);
+ struct cxd2820r_priv *priv = gpiochip_get_data(chip);
dev_dbg(&priv->i2c->dev, "%s: nr=%d\n", __func__, nr);
priv->gpio_chip.base = -1; /* dynamic allocation */
priv->gpio_chip.ngpio = GPIO_COUNT;
priv->gpio_chip.can_sleep = 1;
- ret = gpiochip_add(&priv->gpio_chip);
+ ret = gpiochip_add_data(&priv->gpio_chip, priv);
if (ret)
goto error;
/* cxd2820r_c.c */
-int cxd2820r_get_frontend_c(struct dvb_frontend *fe);
+int cxd2820r_get_frontend_c(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p);
int cxd2820r_set_frontend_c(struct dvb_frontend *fe);
/* cxd2820r_t.c */
-int cxd2820r_get_frontend_t(struct dvb_frontend *fe);
+int cxd2820r_get_frontend_t(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p);
int cxd2820r_set_frontend_t(struct dvb_frontend *fe);
/* cxd2820r_t2.c */
-int cxd2820r_get_frontend_t2(struct dvb_frontend *fe);
+int cxd2820r_get_frontend_t2(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p);
int cxd2820r_set_frontend_t2(struct dvb_frontend *fe);
return ret;
}
-int cxd2820r_get_frontend_t(struct dvb_frontend *fe)
+int cxd2820r_get_frontend_t(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
{
struct cxd2820r_priv *priv = fe->demodulator_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
int ret;
u8 buf[2];
int cxd2820r_set_frontend_t2(struct dvb_frontend *fe)
{
- struct cxd2820r_priv *priv = fe->demodulator_priv;
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
int ret, i, bw_i;
u32 if_freq, if_ctl;
u64 num;
}
-int cxd2820r_get_frontend_t2(struct dvb_frontend *fe)
+int cxd2820r_get_frontend_t2(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
{
struct cxd2820r_priv *priv = fe->demodulator_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
int ret;
u8 buf[2];
return 0;
}
-static int cxd2841er_get_frontend(struct dvb_frontend *fe)
+static int cxd2841er_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
enum fe_status status = 0;
u16 strength = 0, snr = 0;
u32 errors = 0, ber = 0;
struct cxd2841er_priv *priv = fe->demodulator_priv;
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
if (priv->state == STATE_ACTIVE_S)
}
EXPORT_SYMBOL(dib0070_attach);
-MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
MODULE_DESCRIPTION("Driver for the DiBcom 0070 base-band RF Tuner");
MODULE_LICENSE("GPL");
dib0090_set_bbramp_pwm(state, bb_ramp);
/* activate the ramp generator using PWM control */
- dprintk("ramp RF gain = %d BAND = %s version = %d", state->rf_ramp[0], (state->current_band == BAND_CBAND) ? "CBAND" : "NOT CBAND", state->identity.version & 0x1f);
-
- if ((state->rf_ramp[0] == 0) || (state->current_band == BAND_CBAND && (state->identity.version & 0x1f) <= P1D_E_F)) {
+ if (state->rf_ramp)
+ dprintk("ramp RF gain = %d BAND = %s version = %d",
+ state->rf_ramp[0],
+ (state->current_band == BAND_CBAND) ? "CBAND" : "NOT CBAND",
+ state->identity.version & 0x1f);
+
+ if (rf_ramp && ((state->rf_ramp[0] == 0) ||
+ (state->current_band == BAND_CBAND &&
+ (state->identity.version & 0x1f) <= P1D_E_F))) {
dprintk("DE-Engage mux for direct gain reg control");
en_pwm_rf_mux = 0;
} else
}
EXPORT_SYMBOL(dib0090_fw_register);
-MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
-MODULE_AUTHOR("Olivier Grenie <olivier.grenie@dibcom.fr>");
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
+MODULE_AUTHOR("Olivier Grenie <olivier.grenie@parrot.com>");
MODULE_DESCRIPTION("Driver for the DiBcom 0090 base-band RF Tuner");
MODULE_LICENSE("GPL");
* public header file of the frontend drivers for mobile DVB-T demodulators
* DiBcom 3000M-B and DiBcom 3000P/M-C (http://www.dibcom.fr/)
*
- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de)
*
* based on GPL code from DibCom, which has
*
- * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr)
+ * Copyright (C) 2004 Amaury Demol for DiBcom
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
*
* Acknowledgements
*
- * Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver
+ * Amaury Demol from DiBcom for providing specs and driver
* sources, on which this driver (and the dvb-dibusb) are based.
*
* see Documentation/dvb/README.dvb-usb for more information
* Frontend driver for mobile DVB-T demodulator DiBcom 3000M-B
* DiBcom (http://www.dibcom.fr/)
*
- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de)
*
* based on GPL code from DibCom, which has
*
- * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr)
+ * Copyright (C) 2004 Amaury Demol for DiBcom
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
*
* Acknowledgements
*
- * Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver
+ * Amaury Demol from DiBcom for providing specs and driver
* sources, on which this driver (and the dvb-dibusb) are based.
*
* see Documentation/dvb/README.dvb-usb for more information
/* Version information */
#define DRIVER_VERSION "0.1"
#define DRIVER_DESC "DiBcom 3000M-B DVB-T demodulator"
-#define DRIVER_AUTHOR "Patrick Boettcher, patrick.boettcher@desy.de"
+#define DRIVER_AUTHOR "Patrick Boettcher, patrick.boettcher@posteo.de"
static int debug;
module_param(debug, int, 0644);
}
};
-static int dib3000mb_get_frontend(struct dvb_frontend* fe);
+static int dib3000mb_get_frontend(struct dvb_frontend* fe,
+ struct dtv_frontend_properties *c);
static int dib3000mb_set_frontend(struct dvb_frontend *fe, int tuner)
{
deb_setf("search_state after autosearch %d after %d checks\n",search_state,as_count);
if (search_state == 1) {
- if (dib3000mb_get_frontend(fe) == 0) {
+ if (dib3000mb_get_frontend(fe, c) == 0) {
deb_setf("reading tuning data from frontend succeeded.\n");
return dib3000mb_set_frontend(fe, 0);
}
return 0;
}
-static int dib3000mb_get_frontend(struct dvb_frontend* fe)
+static int dib3000mb_get_frontend(struct dvb_frontend* fe,
+ struct dtv_frontend_properties *c)
{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
struct dib3000_state* state = fe->demodulator_priv;
enum fe_code_rate *cr;
u16 tps_val;
/*
* dib3000mb_priv.h
*
- * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.de)
+ * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@posteo.de)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* Driver for DiBcom DiB3000MC/P-demodulator.
*
* Copyright (C) 2004-7 DiBcom (http://www.dibcom.fr/)
- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de)
*
* This code is partially based on the previous dib3000mc.c .
*
EXPORT_SYMBOL(dib3000mc_get_tuner_i2c_master);
-static int dib3000mc_get_frontend(struct dvb_frontend* fe)
+static int dib3000mc_get_frontend(struct dvb_frontend* fe,
+ struct dtv_frontend_properties *fep)
{
- struct dtv_frontend_properties *fep = &fe->dtv_property_cache;
struct dib3000mc_state *state = fe->demodulator_priv;
u16 tps = dib3000mc_read_word(state,458);
if (found == 0 || found == 1)
return 0; // no channel found
- dib3000mc_get_frontend(fe);
+ dib3000mc_get_frontend(fe, fep);
}
ret = dib3000mc_tune(fe);
.read_ucblocks = dib3000mc_read_unc_blocks,
};
-MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
MODULE_DESCRIPTION("Driver for the DiBcom 3000MC/P COFDM demodulator");
MODULE_LICENSE("GPL");
* Driver for DiBcom DiB3000MC/P-demodulator.
*
* Copyright (C) 2004-6 DiBcom (http://www.dibcom.fr/)
- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher\@desy.de)
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de)
*
* This code is partially based on the previous dib3000mc.c .
*
}
-static int dib7000m_get_frontend(struct dvb_frontend* fe)
+static int dib7000m_get_frontend(struct dvb_frontend* fe,
+ struct dtv_frontend_properties *fep)
{
- struct dtv_frontend_properties *fep = &fe->dtv_property_cache;
struct dib7000m_state *state = fe->demodulator_priv;
u16 tps = dib7000m_read_word(state,480);
if (found == 0 || found == 1)
return 0; // no channel found
- dib7000m_get_frontend(fe);
+ dib7000m_get_frontend(fe, fep);
}
ret = dib7000m_tune(fe);
.read_ucblocks = dib7000m_read_unc_blocks,
};
-MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
MODULE_DESCRIPTION("Driver for the DiBcom 7000MA/MB/PA/PB/MC COFDM demodulator");
MODULE_LICENSE("GPL");
return 0;
}
-static int dib7000p_get_frontend(struct dvb_frontend *fe)
+static int dib7000p_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *fep)
{
- struct dtv_frontend_properties *fep = &fe->dtv_property_cache;
struct dib7000p_state *state = fe->demodulator_priv;
u16 tps = dib7000p_read_word(state, 463);
if (found == 0 || found == 1)
return 0;
- dib7000p_get_frontend(fe);
+ dib7000p_get_frontend(fe, fep);
}
ret = dib7000p_tune(fe);
.read_ucblocks = dib7000p_read_unc_blocks,
};
-MODULE_AUTHOR("Olivier Grenie <ogrenie@dibcom.fr>");
-MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
+MODULE_AUTHOR("Olivier Grenie <olivie.grenie@parrot.com>");
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
MODULE_DESCRIPTION("Driver for the DiBcom 7000PC COFDM demodulator");
MODULE_LICENSE("GPL");
static int dib8000_read_status(struct dvb_frontend *fe, enum fe_status *stat);
-static int dib8000_get_frontend(struct dvb_frontend *fe)
+static int dib8000_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
{
struct dib8000_state *state = fe->demodulator_priv;
u16 i, val = 0;
enum fe_status stat = 0;
u8 index_frontend, sub_index_frontend;
- fe->dtv_property_cache.bandwidth_hz = 6000000;
+ c->bandwidth_hz = 6000000;
/*
* If called to early, get_frontend makes dib8000_tune to either
if (stat&FE_HAS_SYNC) {
dprintk("TMCC lock on the slave%i", index_frontend);
/* synchronize the cache with the other frontends */
- state->fe[index_frontend]->ops.get_frontend(state->fe[index_frontend]);
+ state->fe[index_frontend]->ops.get_frontend(state->fe[index_frontend], c);
for (sub_index_frontend = 0; (sub_index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[sub_index_frontend] != NULL); sub_index_frontend++) {
if (sub_index_frontend != index_frontend) {
state->fe[sub_index_frontend]->dtv_property_cache.isdbt_sb_mode = state->fe[index_frontend]->dtv_property_cache.isdbt_sb_mode;
}
}
- fe->dtv_property_cache.isdbt_sb_mode = dib8000_read_word(state, 508) & 0x1;
+ c->isdbt_sb_mode = dib8000_read_word(state, 508) & 0x1;
if (state->revision == 0x8090)
val = dib8000_read_word(state, 572);
else
val = dib8000_read_word(state, 570);
- fe->dtv_property_cache.inversion = (val & 0x40) >> 6;
+ c->inversion = (val & 0x40) >> 6;
switch ((val & 0x30) >> 4) {
case 1:
- fe->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_2K;
+ c->transmission_mode = TRANSMISSION_MODE_2K;
dprintk("dib8000_get_frontend: transmission mode 2K");
break;
case 2:
- fe->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_4K;
+ c->transmission_mode = TRANSMISSION_MODE_4K;
dprintk("dib8000_get_frontend: transmission mode 4K");
break;
case 3:
default:
- fe->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K;
+ c->transmission_mode = TRANSMISSION_MODE_8K;
dprintk("dib8000_get_frontend: transmission mode 8K");
break;
}
switch (val & 0x3) {
case 0:
- fe->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_32;
+ c->guard_interval = GUARD_INTERVAL_1_32;
dprintk("dib8000_get_frontend: Guard Interval = 1/32 ");
break;
case 1:
- fe->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_16;
+ c->guard_interval = GUARD_INTERVAL_1_16;
dprintk("dib8000_get_frontend: Guard Interval = 1/16 ");
break;
case 2:
dprintk("dib8000_get_frontend: Guard Interval = 1/8 ");
- fe->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8;
+ c->guard_interval = GUARD_INTERVAL_1_8;
break;
case 3:
dprintk("dib8000_get_frontend: Guard Interval = 1/4 ");
- fe->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_4;
+ c->guard_interval = GUARD_INTERVAL_1_4;
break;
}
val = dib8000_read_word(state, 505);
- fe->dtv_property_cache.isdbt_partial_reception = val & 1;
- dprintk("dib8000_get_frontend: partial_reception = %d ", fe->dtv_property_cache.isdbt_partial_reception);
+ c->isdbt_partial_reception = val & 1;
+ dprintk("dib8000_get_frontend: partial_reception = %d ", c->isdbt_partial_reception);
for (i = 0; i < 3; i++) {
int show;
val = dib8000_read_word(state, 493 + i) & 0x0f;
- fe->dtv_property_cache.layer[i].segment_count = val;
+ c->layer[i].segment_count = val;
if (val == 0 || val > 13)
show = 0;
if (show)
dprintk("dib8000_get_frontend: Layer %d segments = %d ",
- i, fe->dtv_property_cache.layer[i].segment_count);
+ i, c->layer[i].segment_count);
val = dib8000_read_word(state, 499 + i) & 0x3;
/* Interleaving can be 0, 1, 2 or 4 */
if (val == 3)
val = 4;
- fe->dtv_property_cache.layer[i].interleaving = val;
+ c->layer[i].interleaving = val;
if (show)
dprintk("dib8000_get_frontend: Layer %d time_intlv = %d ",
- i, fe->dtv_property_cache.layer[i].interleaving);
+ i, c->layer[i].interleaving);
val = dib8000_read_word(state, 481 + i);
switch (val & 0x7) {
case 1:
- fe->dtv_property_cache.layer[i].fec = FEC_1_2;
+ c->layer[i].fec = FEC_1_2;
if (show)
dprintk("dib8000_get_frontend: Layer %d Code Rate = 1/2 ", i);
break;
case 2:
- fe->dtv_property_cache.layer[i].fec = FEC_2_3;
+ c->layer[i].fec = FEC_2_3;
if (show)
dprintk("dib8000_get_frontend: Layer %d Code Rate = 2/3 ", i);
break;
case 3:
- fe->dtv_property_cache.layer[i].fec = FEC_3_4;
+ c->layer[i].fec = FEC_3_4;
if (show)
dprintk("dib8000_get_frontend: Layer %d Code Rate = 3/4 ", i);
break;
case 5:
- fe->dtv_property_cache.layer[i].fec = FEC_5_6;
+ c->layer[i].fec = FEC_5_6;
if (show)
dprintk("dib8000_get_frontend: Layer %d Code Rate = 5/6 ", i);
break;
default:
- fe->dtv_property_cache.layer[i].fec = FEC_7_8;
+ c->layer[i].fec = FEC_7_8;
if (show)
dprintk("dib8000_get_frontend: Layer %d Code Rate = 7/8 ", i);
break;
val = dib8000_read_word(state, 487 + i);
switch (val & 0x3) {
case 0:
- fe->dtv_property_cache.layer[i].modulation = DQPSK;
+ c->layer[i].modulation = DQPSK;
if (show)
dprintk("dib8000_get_frontend: Layer %d DQPSK ", i);
break;
case 1:
- fe->dtv_property_cache.layer[i].modulation = QPSK;
+ c->layer[i].modulation = QPSK;
if (show)
dprintk("dib8000_get_frontend: Layer %d QPSK ", i);
break;
case 2:
- fe->dtv_property_cache.layer[i].modulation = QAM_16;
+ c->layer[i].modulation = QAM_16;
if (show)
dprintk("dib8000_get_frontend: Layer %d QAM16 ", i);
break;
case 3:
default:
- fe->dtv_property_cache.layer[i].modulation = QAM_64;
+ c->layer[i].modulation = QAM_64;
if (show)
dprintk("dib8000_get_frontend: Layer %d QAM64 ", i);
break;
/* synchronize the cache with the other frontends */
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
- state->fe[index_frontend]->dtv_property_cache.isdbt_sb_mode = fe->dtv_property_cache.isdbt_sb_mode;
- state->fe[index_frontend]->dtv_property_cache.inversion = fe->dtv_property_cache.inversion;
- state->fe[index_frontend]->dtv_property_cache.transmission_mode = fe->dtv_property_cache.transmission_mode;
- state->fe[index_frontend]->dtv_property_cache.guard_interval = fe->dtv_property_cache.guard_interval;
- state->fe[index_frontend]->dtv_property_cache.isdbt_partial_reception = fe->dtv_property_cache.isdbt_partial_reception;
+ state->fe[index_frontend]->dtv_property_cache.isdbt_sb_mode = c->isdbt_sb_mode;
+ state->fe[index_frontend]->dtv_property_cache.inversion = c->inversion;
+ state->fe[index_frontend]->dtv_property_cache.transmission_mode = c->transmission_mode;
+ state->fe[index_frontend]->dtv_property_cache.guard_interval = c->guard_interval;
+ state->fe[index_frontend]->dtv_property_cache.isdbt_partial_reception = c->isdbt_partial_reception;
for (i = 0; i < 3; i++) {
- state->fe[index_frontend]->dtv_property_cache.layer[i].segment_count = fe->dtv_property_cache.layer[i].segment_count;
- state->fe[index_frontend]->dtv_property_cache.layer[i].interleaving = fe->dtv_property_cache.layer[i].interleaving;
- state->fe[index_frontend]->dtv_property_cache.layer[i].fec = fe->dtv_property_cache.layer[i].fec;
- state->fe[index_frontend]->dtv_property_cache.layer[i].modulation = fe->dtv_property_cache.layer[i].modulation;
+ state->fe[index_frontend]->dtv_property_cache.layer[i].segment_count = c->layer[i].segment_count;
+ state->fe[index_frontend]->dtv_property_cache.layer[i].interleaving = c->layer[i].interleaving;
+ state->fe[index_frontend]->dtv_property_cache.layer[i].fec = c->layer[i].fec;
+ state->fe[index_frontend]->dtv_property_cache.layer[i].modulation = c->layer[i].modulation;
}
}
return 0;
if (state->channel_parameters_set == 0) { /* searching */
if ((dib8000_get_status(state->fe[index_frontend]) == FE_STATUS_DEMOD_SUCCESS) || (dib8000_get_status(state->fe[index_frontend]) == FE_STATUS_FFT_SUCCESS)) {
dprintk("autosearch succeeded on fe%i", index_frontend);
- dib8000_get_frontend(state->fe[index_frontend]); /* we read the channel parameters from the frontend which was successful */
+ dib8000_get_frontend(state->fe[index_frontend], c); /* we read the channel parameters from the frontend which was successful */
state->channel_parameters_set = 1;
for (l = 0; (l < MAX_NUMBER_OF_FRONTENDS) && (state->fe[l] != NULL); l++) {
}
EXPORT_SYMBOL(dib8000_attach);
-MODULE_AUTHOR("Olivier Grenie <Olivier.Grenie@dibcom.fr, " "Patrick Boettcher <pboettcher@dibcom.fr>");
+MODULE_AUTHOR("Olivier Grenie <Olivier.Grenie@parrot.com, Patrick Boettcher <patrick.boettcher@posteo.de>");
MODULE_DESCRIPTION("Driver for the DiBcom 8000 ISDB-T demodulator");
MODULE_LICENSE("GPL");
}
}
-static u16 dib9000_read16_attr(struct dib9000_state *state, u16 reg, u8 * b, u32 len, u16 attribute)
+static int dib9000_read16_attr(struct dib9000_state *state, u16 reg, u8 *b, u32 len, u16 attribute)
{
u32 chunk_size = 126;
u32 l;
#define dib9000_read16_noinc_attr(state, reg, b, len, attribute) dib9000_read16_attr(state, reg, b, len, (attribute) | DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT)
-static u16 dib9000_write16_attr(struct dib9000_state *state, u16 reg, const u8 * buf, u32 len, u16 attribute)
+static int dib9000_write16_attr(struct dib9000_state *state, u16 reg, const u8 *buf, u32 len, u16 attribute)
{
u32 chunk_size = 126;
u32 l;
return 0;
}
-static int dib9000_get_frontend(struct dvb_frontend *fe)
+static int dib9000_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
{
struct dib9000_state *state = fe->demodulator_priv;
u8 index_frontend, sub_index_frontend;
dprintk("TPS lock on the slave%i", index_frontend);
/* synchronize the cache with the other frontends */
- state->fe[index_frontend]->ops.get_frontend(state->fe[index_frontend]);
+ state->fe[index_frontend]->ops.get_frontend(state->fe[index_frontend], c);
for (sub_index_frontend = 0; (sub_index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[sub_index_frontend] != NULL);
sub_index_frontend++) {
if (sub_index_frontend != index_frontend) {
/* synchronize the cache with the other frontends */
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
- state->fe[index_frontend]->dtv_property_cache.inversion = fe->dtv_property_cache.inversion;
- state->fe[index_frontend]->dtv_property_cache.transmission_mode = fe->dtv_property_cache.transmission_mode;
- state->fe[index_frontend]->dtv_property_cache.guard_interval = fe->dtv_property_cache.guard_interval;
- state->fe[index_frontend]->dtv_property_cache.modulation = fe->dtv_property_cache.modulation;
- state->fe[index_frontend]->dtv_property_cache.hierarchy = fe->dtv_property_cache.hierarchy;
- state->fe[index_frontend]->dtv_property_cache.code_rate_HP = fe->dtv_property_cache.code_rate_HP;
- state->fe[index_frontend]->dtv_property_cache.code_rate_LP = fe->dtv_property_cache.code_rate_LP;
- state->fe[index_frontend]->dtv_property_cache.rolloff = fe->dtv_property_cache.rolloff;
+ state->fe[index_frontend]->dtv_property_cache.inversion = c->inversion;
+ state->fe[index_frontend]->dtv_property_cache.transmission_mode = c->transmission_mode;
+ state->fe[index_frontend]->dtv_property_cache.guard_interval = c->guard_interval;
+ state->fe[index_frontend]->dtv_property_cache.modulation = c->modulation;
+ state->fe[index_frontend]->dtv_property_cache.hierarchy = c->hierarchy;
+ state->fe[index_frontend]->dtv_property_cache.code_rate_HP = c->code_rate_HP;
+ state->fe[index_frontend]->dtv_property_cache.code_rate_LP = c->code_rate_LP;
+ state->fe[index_frontend]->dtv_property_cache.rolloff = c->rolloff;
}
ret = 0;
/* synchronize all the channel cache */
state->get_frontend_internal = 1;
- dib9000_get_frontend(state->fe[0]);
+ dib9000_get_frontend(state->fe[0], &state->fe[0]->dtv_property_cache);
state->get_frontend_internal = 0;
/* retune the other frontends with the found channel */
.read_ucblocks = dib9000_read_unc_blocks,
};
-MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
-MODULE_AUTHOR("Olivier Grenie <ogrenie@dibcom.fr>");
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
+MODULE_AUTHOR("Olivier Grenie <olivier.grenie@parrot.com>");
MODULE_DESCRIPTION("Driver for the DiBcom 9000 COFDM demodulator");
MODULE_LICENSE("GPL");
}
EXPORT_SYMBOL(systime);
-MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
MODULE_DESCRIPTION("Common function the DiBcom demodulator family");
MODULE_LICENSE("GPL");
{
struct drxjscu_cmd scu_cmd;
int rc;
- u16 set_param_parameters[15];
+ u16 set_param_parameters[18];
u16 cmd_result[15];
/* Parameter check */
Precision errors still possible.
*/
- e = post_bit_err_rs * 742686;
- m = fec_oc_period * 100;
- if (fec_oc_period == 0)
+ if (!fec_oc_period) {
qam_post_rs_ber = 0xFFFFFFFF;
- else
+ } else {
+ e = post_bit_err_rs * 742686;
+ m = fec_oc_period * 100;
qam_post_rs_ber = e / m;
+ }
/* fill signal quality data structure */
p->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER;
}
/*
- * Only needed if it actually reads something from the hardware
+ * Should only be implemented if it actually reads something from the hardware.
+ * Also, it should check for the locks, in order to avoid report wrong data
+ * to userspace.
*/
-static int dvb_dummy_fe_get_frontend(struct dvb_frontend *fe)
+static int dvb_dummy_fe_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
return 0;
}
return DVBFE_ALGO_CUSTOM;
}
-static int hd29l2_get_frontend(struct dvb_frontend *fe)
+static int hd29l2_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
{
int ret;
struct hd29l2_priv *priv = fe->demodulator_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
u8 buf[3];
u32 if_ctl;
char *str_constellation, *str_code_rate, *str_constellation_code_rate,
return 0;
}
-static int get_frontend(struct dvb_frontend *fe)
+static int get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct l64781_state* state = fe->demodulator_priv;
int tmp;
/* ------------------------------------------------------------------------ */
-static int lg216x_get_frontend(struct dvb_frontend *fe)
+static int lg216x_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
{
struct lg216x_state *state = fe->demodulator_priv;
int ret;
lg_dbg("\n");
- fe->dtv_property_cache.modulation = VSB_8;
- fe->dtv_property_cache.frequency = state->current_frequency;
- fe->dtv_property_cache.delivery_system = SYS_ATSCMH;
+ c->modulation = VSB_8;
+ c->frequency = state->current_frequency;
+ c->delivery_system = SYS_ATSCMH;
ret = lg216x_get_fic_version(state,
- &fe->dtv_property_cache.atscmh_fic_ver);
+ &c->atscmh_fic_ver);
if (lg_fail(ret))
goto fail;
- if (state->fic_ver != fe->dtv_property_cache.atscmh_fic_ver) {
- state->fic_ver = fe->dtv_property_cache.atscmh_fic_ver;
+ if (state->fic_ver != c->atscmh_fic_ver) {
+ state->fic_ver = c->atscmh_fic_ver;
#if 0
ret = lg2160_get_parade_id(state,
- &fe->dtv_property_cache.atscmh_parade_id);
+ &c->atscmh_parade_id);
if (lg_fail(ret))
goto fail;
/* #else */
- fe->dtv_property_cache.atscmh_parade_id = state->parade_id;
+ c->atscmh_parade_id = state->parade_id;
#endif
ret = lg216x_get_nog(state,
- &fe->dtv_property_cache.atscmh_nog);
+ &c->atscmh_nog);
if (lg_fail(ret))
goto fail;
ret = lg216x_get_tnog(state,
- &fe->dtv_property_cache.atscmh_tnog);
+ &c->atscmh_tnog);
if (lg_fail(ret))
goto fail;
ret = lg216x_get_sgn(state,
- &fe->dtv_property_cache.atscmh_sgn);
+ &c->atscmh_sgn);
if (lg_fail(ret))
goto fail;
ret = lg216x_get_prc(state,
- &fe->dtv_property_cache.atscmh_prc);
+ &c->atscmh_prc);
if (lg_fail(ret))
goto fail;
ret = lg216x_get_rs_frame_mode(state,
(enum atscmh_rs_frame_mode *)
- &fe->dtv_property_cache.atscmh_rs_frame_mode);
+ &c->atscmh_rs_frame_mode);
if (lg_fail(ret))
goto fail;
ret = lg216x_get_rs_frame_ensemble(state,
(enum atscmh_rs_frame_ensemble *)
- &fe->dtv_property_cache.atscmh_rs_frame_ensemble);
+ &c->atscmh_rs_frame_ensemble);
if (lg_fail(ret))
goto fail;
ret = lg216x_get_rs_code_mode(state,
(enum atscmh_rs_code_mode *)
- &fe->dtv_property_cache.atscmh_rs_code_mode_pri,
+ &c->atscmh_rs_code_mode_pri,
(enum atscmh_rs_code_mode *)
- &fe->dtv_property_cache.atscmh_rs_code_mode_sec);
+ &c->atscmh_rs_code_mode_sec);
if (lg_fail(ret))
goto fail;
ret = lg216x_get_sccc_block_mode(state,
(enum atscmh_sccc_block_mode *)
- &fe->dtv_property_cache.atscmh_sccc_block_mode);
+ &c->atscmh_sccc_block_mode);
if (lg_fail(ret))
goto fail;
ret = lg216x_get_sccc_code_mode(state,
(enum atscmh_sccc_code_mode *)
- &fe->dtv_property_cache.atscmh_sccc_code_mode_a,
+ &c->atscmh_sccc_code_mode_a,
(enum atscmh_sccc_code_mode *)
- &fe->dtv_property_cache.atscmh_sccc_code_mode_b,
+ &c->atscmh_sccc_code_mode_b,
(enum atscmh_sccc_code_mode *)
- &fe->dtv_property_cache.atscmh_sccc_code_mode_c,
+ &c->atscmh_sccc_code_mode_c,
(enum atscmh_sccc_code_mode *)
- &fe->dtv_property_cache.atscmh_sccc_code_mode_d);
+ &c->atscmh_sccc_code_mode_d);
if (lg_fail(ret))
goto fail;
}
#if 0
ret = lg216x_read_fic_err_count(state,
- (u8 *)&fe->dtv_property_cache.atscmh_fic_err);
+ (u8 *)&c->atscmh_fic_err);
if (lg_fail(ret))
goto fail;
ret = lg216x_read_crc_err_count(state,
- &fe->dtv_property_cache.atscmh_crc_err);
+ &c->atscmh_crc_err);
if (lg_fail(ret))
goto fail;
ret = lg216x_read_rs_err_count(state,
- &fe->dtv_property_cache.atscmh_rs_err);
+ &c->atscmh_rs_err);
if (lg_fail(ret))
goto fail;
switch (state->cfg->lg_chip) {
case LG2160:
- if (((fe->dtv_property_cache.atscmh_rs_err >= 240) &&
- (fe->dtv_property_cache.atscmh_crc_err >= 240)) &&
+ if (((c->atscmh_rs_err >= 240) &&
+ (c->atscmh_crc_err >= 240)) &&
((jiffies_to_msecs(jiffies) - state->last_reset) > 6000))
ret = lg216x_soft_reset(state);
break;
static int lg216x_get_property(struct dvb_frontend *fe,
struct dtv_property *tvp)
{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
return (DTV_ATSCMH_FIC_VER == tvp->cmd) ?
- lg216x_get_frontend(fe) : 0;
+ lg216x_get_frontend(fe, c) : 0;
}
static int lg2160_set_frontend(struct dvb_frontend *fe)
{
struct lg216x_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
int ret;
lg_dbg("(%d)\n", fe->dtv_property_cache.frequency);
ret = lg216x_enable_fic(state, 1);
lg_fail(ret);
- lg216x_get_frontend(fe);
+ lg216x_get_frontend(fe, c);
fail:
return ret;
}
return ret;
}
-static int lgdt3305_get_frontend(struct dvb_frontend *fe)
+static int lgdt3305_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct lgdt3305_state *state = fe->demodulator_priv;
lg_dbg("\n");
return ret;
}
-static int lgdt3306a_get_frontend(struct dvb_frontend *fe)
+static int lgdt3306a_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
struct lgdt3306a_state *state = fe->demodulator_priv;
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
dbg_info("(%u, %d)\n",
state->current_frequency, state->current_modulation);
return 0;
}
-static int lgdt330x_get_frontend(struct dvb_frontend *fe)
+static int lgdt330x_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct lgdt330x_state *state = fe->demodulator_priv;
+
p->frequency = state->current_frequency;
return 0;
}
static int
-lgs8gl5_get_frontend(struct dvb_frontend *fe)
+lgs8gl5_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct lgs8gl5_state *state = fe->demodulator_priv;
+
u8 inv = lgs8gl5_read_reg(state, REG_INVERSION);
p->inversion = (inv & REG_INVERSION_ON) ? INVERSION_ON : INVERSION_OFF;
static int lgs8gxx_set_fe(struct dvb_frontend *fe)
{
-
+ struct dtv_frontend_properties *fe_params = &fe->dtv_property_cache;
struct lgs8gxx_state *priv = fe->demodulator_priv;
dprintk("%s\n", __func__);
msleep(10);
- return 0;
-}
-
-static int lgs8gxx_get_fe(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *fe_params = &fe->dtv_property_cache;
- dprintk("%s\n", __func__);
-
/* TODO: get real readings from device */
- /* inversion status */
- fe_params->inversion = INVERSION_OFF;
/* bandwidth */
fe_params->bandwidth_hz = 8000000;
.i2c_gate_ctrl = lgs8gxx_i2c_gate_ctrl,
.set_frontend = lgs8gxx_set_fe,
- .get_frontend = lgs8gxx_get_fe,
.get_tune_settings = lgs8gxx_get_tune_settings,
.read_status = lgs8gxx_read_status,
return ret;
}
-static int m88ds3103_get_frontend(struct dvb_frontend *fe)
+static int m88ds3103_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
{
struct m88ds3103_dev *dev = fe->demodulator_priv;
struct i2c_client *client = dev->client;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
int ret;
u8 buf[3];
return 0;
}
-static int m88rs2000_get_frontend(struct dvb_frontend *fe)
+static int m88rs2000_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
struct m88rs2000_state *state = fe->demodulator_priv;
+
c->fec_inner = state->fec_inner;
c->frequency = state->tuner_frequency;
c->symbol_rate = state->symbol_rate;
return 0;
}
-static int mb86a20s_get_frontend_dummy(struct dvb_frontend *fe)
-{
- /*
- * get_frontend is now handled together with other stats
- * retrival, when read_status() is called, as some statistics
- * will depend on the layers detection.
- */
- return 0;
-};
-
static int mb86a20s_tune(struct dvb_frontend *fe,
bool re_tune,
unsigned int mode_flags,
.init = mb86a20s_initfe,
.set_frontend = mb86a20s_set_frontend,
- .get_frontend = mb86a20s_get_frontend_dummy,
.read_status = mb86a20s_read_status_and_stats,
.read_signal_strength = mb86a20s_read_signal_strength_from_cache,
.tune = mb86a20s_tune,
--- /dev/null
+/*
+ * Panasonic MN88473 DVB-T/T2/C demodulator driver
+ *
+ * Copyright (C) 2014 Antti Palosaari <crope@iki.fi>
+ *
+ * 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.
+ */
+
+#include "mn88473_priv.h"
+
+static int mn88473_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *s)
+{
+ s->min_delay_ms = 1000;
+ return 0;
+}
+
+static int mn88473_set_frontend(struct dvb_frontend *fe)
+{
+ struct i2c_client *client = fe->demodulator_priv;
+ struct mn88473_dev *dev = i2c_get_clientdata(client);
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i;
+ unsigned int uitmp;
+ u32 if_frequency;
+ u8 delivery_system_val, if_val[3], *conf_val_ptr;
+ u8 reg_bank2_2d_val, reg_bank0_d2_val;
+
+ dev_dbg(&client->dev,
+ "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->active) {
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ switch (c->delivery_system) {
+ case SYS_DVBT:
+ delivery_system_val = 0x02;
+ reg_bank2_2d_val = 0x23;
+ reg_bank0_d2_val = 0x2a;
+ break;
+ case SYS_DVBT2:
+ delivery_system_val = 0x03;
+ reg_bank2_2d_val = 0x3b;
+ reg_bank0_d2_val = 0x29;
+ break;
+ case SYS_DVBC_ANNEX_A:
+ delivery_system_val = 0x04;
+ reg_bank2_2d_val = 0x3b;
+ reg_bank0_d2_val = 0x29;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ switch (c->delivery_system) {
+ case SYS_DVBT:
+ case SYS_DVBT2:
+ switch (c->bandwidth_hz) {
+ case 6000000:
+ conf_val_ptr = "\xe9\x55\x55\x1c\x29\x1c\x29";
+ break;
+ case 7000000:
+ conf_val_ptr = "\xc8\x00\x00\x17\x0a\x17\x0a";
+ break;
+ case 8000000:
+ conf_val_ptr = "\xaf\x00\x00\x11\xec\x11\xec";
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+ break;
+ case SYS_DVBC_ANNEX_A:
+ conf_val_ptr = "\x10\xab\x0d\xae\x1d\x9d";
+ break;
+ default:
+ break;
+ }
+
+ /* Program tuner */
+ if (fe->ops.tuner_ops.set_params) {
+ ret = fe->ops.tuner_ops.set_params(fe);
+ if (ret)
+ goto err;
+ }
+
+ if (fe->ops.tuner_ops.get_if_frequency) {
+ ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency);
+ if (ret)
+ goto err;
+
+ dev_dbg(&client->dev, "get_if_frequency=%u\n", if_frequency);
+ } else {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* Calculate IF registers */
+ uitmp = DIV_ROUND_CLOSEST_ULL((u64) if_frequency * 0x1000000, dev->clk);
+ if_val[0] = (uitmp >> 16) & 0xff;
+ if_val[1] = (uitmp >> 8) & 0xff;
+ if_val[2] = (uitmp >> 0) & 0xff;
+
+ ret = regmap_write(dev->regmap[2], 0x05, 0x00);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0xfb, 0x13);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0xef, 0x13);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0xf9, 0x13);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0x00, 0x18);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0x01, 0x01);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0x02, 0x21);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0x03, delivery_system_val);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0x0b, 0x00);
+ if (ret)
+ goto err;
+
+ for (i = 0; i < sizeof(if_val); i++) {
+ ret = regmap_write(dev->regmap[2], 0x10 + i, if_val[i]);
+ if (ret)
+ goto err;
+ }
+
+ switch (c->delivery_system) {
+ case SYS_DVBT:
+ case SYS_DVBT2:
+ for (i = 0; i < 7; i++) {
+ ret = regmap_write(dev->regmap[2], 0x13 + i,
+ conf_val_ptr[i]);
+ if (ret)
+ goto err;
+ }
+ break;
+ case SYS_DVBC_ANNEX_A:
+ ret = regmap_bulk_write(dev->regmap[1], 0x10, conf_val_ptr, 6);
+ if (ret)
+ goto err;
+ break;
+ default:
+ break;
+ }
+
+ ret = regmap_write(dev->regmap[2], 0x2d, reg_bank2_2d_val);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0x2e, 0x00);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0x56, 0x0d);
+ if (ret)
+ goto err;
+ ret = regmap_bulk_write(dev->regmap[0], 0x01,
+ "\xba\x13\x80\xba\x91\xdd\xe7\x28", 8);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[0], 0x0a, 0x1a);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[0], 0x13, 0x1f);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[0], 0x19, 0x03);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[0], 0x1d, 0xb0);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[0], 0x2a, 0x72);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[0], 0x2d, 0x00);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[0], 0x3c, 0x00);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[0], 0x3f, 0xf8);
+ if (ret)
+ goto err;
+ ret = regmap_bulk_write(dev->regmap[0], 0x40, "\xf4\x08", 2);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[0], 0xd2, reg_bank0_d2_val);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[0], 0xd4, 0x55);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[1], 0xbe, 0x08);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[0], 0xb2, 0x37);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[0], 0xd7, 0x04);
+ if (ret)
+ goto err;
+
+ /* Reset FSM */
+ ret = regmap_write(dev->regmap[2], 0xf8, 0x9f);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int mn88473_read_status(struct dvb_frontend *fe, enum fe_status *status)
+{
+ struct i2c_client *client = fe->demodulator_priv;
+ struct mn88473_dev *dev = i2c_get_clientdata(client);
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret;
+ unsigned int uitmp;
+
+ if (!dev->active) {
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ *status = 0;
+
+ switch (c->delivery_system) {
+ case SYS_DVBT:
+ ret = regmap_read(dev->regmap[0], 0x62, &uitmp);
+ if (ret)
+ goto err;
+
+ if (!(uitmp & 0xa0)) {
+ if ((uitmp & 0x0f) >= 0x09)
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC |
+ FE_HAS_LOCK;
+ else if ((uitmp & 0x0f) >= 0x03)
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
+ }
+ break;
+ case SYS_DVBT2:
+ ret = regmap_read(dev->regmap[2], 0x8b, &uitmp);
+ if (ret)
+ goto err;
+
+ if (!(uitmp & 0x40)) {
+ if ((uitmp & 0x0f) >= 0x0d)
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC |
+ FE_HAS_LOCK;
+ else if ((uitmp & 0x0f) >= 0x0a)
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI;
+ else if ((uitmp & 0x0f) >= 0x07)
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
+ }
+ break;
+ case SYS_DVBC_ANNEX_A:
+ ret = regmap_read(dev->regmap[1], 0x85, &uitmp);
+ if (ret)
+ goto err;
+
+ if (!(uitmp & 0x40)) {
+ ret = regmap_read(dev->regmap[1], 0x89, &uitmp);
+ if (ret)
+ goto err;
+
+ if (uitmp & 0x01)
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC |
+ FE_HAS_LOCK;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int mn88473_init(struct dvb_frontend *fe)
+{
+ struct i2c_client *client = fe->demodulator_priv;
+ struct mn88473_dev *dev = i2c_get_clientdata(client);
+ int ret, len, remain;
+ unsigned int uitmp;
+ const struct firmware *fw;
+ const char *name = MN88473_FIRMWARE;
+
+ dev_dbg(&client->dev, "\n");
+
+ /* Check if firmware is already running */
+ ret = regmap_read(dev->regmap[0], 0xf5, &uitmp);
+ if (ret)
+ goto err;
+
+ if (!(uitmp & 0x01))
+ goto warm;
+
+ /* 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);
+ goto err;
+ }
+
+ dev_info(&client->dev, "downloading firmware from file '%s'\n", name);
+
+ ret = regmap_write(dev->regmap[0], 0xf5, 0x03);
+ if (ret)
+ goto err_release_firmware;
+
+ for (remain = fw->size; remain > 0; remain -= (dev->i2c_wr_max - 1)) {
+ len = min(dev->i2c_wr_max - 1, remain);
+ ret = regmap_bulk_write(dev->regmap[0], 0xf6,
+ &fw->data[fw->size - remain], len);
+ if (ret) {
+ dev_err(&client->dev, "firmware download failed %d\n",
+ ret);
+ goto err_release_firmware;
+ }
+ }
+
+ release_firmware(fw);
+
+ /* Parity check of firmware */
+ ret = regmap_read(dev->regmap[0], 0xf8, &uitmp);
+ if (ret)
+ goto err;
+
+ if (uitmp & 0x10) {
+ dev_err(&client->dev, "firmware parity check failed\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = regmap_write(dev->regmap[0], 0xf5, 0x00);
+ if (ret)
+ goto err;
+warm:
+ /* TS config */
+ ret = regmap_write(dev->regmap[2], 0x09, 0x08);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0x08, 0x1d);
+ if (ret)
+ goto err;
+
+ dev->active = true;
+
+ return 0;
+err_release_firmware:
+ release_firmware(fw);
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int mn88473_sleep(struct dvb_frontend *fe)
+{
+ struct i2c_client *client = fe->demodulator_priv;
+ struct mn88473_dev *dev = i2c_get_clientdata(client);
+ int ret;
+
+ dev_dbg(&client->dev, "\n");
+
+ dev->active = false;
+
+ ret = regmap_write(dev->regmap[2], 0x05, 0x3e);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static const struct dvb_frontend_ops mn88473_ops = {
+ .delsys = {SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A},
+ .info = {
+ .name = "Panasonic MN88473",
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 7200000,
+ .caps = FE_CAN_FEC_1_2 |
+ FE_CAN_FEC_2_3 |
+ FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 |
+ FE_CAN_FEC_7_8 |
+ FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK |
+ FE_CAN_QAM_16 |
+ FE_CAN_QAM_32 |
+ FE_CAN_QAM_64 |
+ FE_CAN_QAM_128 |
+ FE_CAN_QAM_256 |
+ FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO |
+ FE_CAN_MUTE_TS |
+ FE_CAN_2G_MODULATION
+ },
+
+ .get_tune_settings = mn88473_get_tune_settings,
+
+ .init = mn88473_init,
+ .sleep = mn88473_sleep,
+
+ .set_frontend = mn88473_set_frontend,
+
+ .read_status = mn88473_read_status,
+};
+
+static int mn88473_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct mn88473_config *config = client->dev.platform_data;
+ struct mn88473_dev *dev;
+ int ret;
+ unsigned int uitmp;
+ static const struct regmap_config regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ };
+
+ 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 == NULL) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ if (config->i2c_wr_max)
+ dev->i2c_wr_max = config->i2c_wr_max;
+ else
+ dev->i2c_wr_max = ~0;
+
+ if (config->xtal)
+ dev->clk = config->xtal;
+ else
+ dev->clk = 25000000;
+ dev->client[0] = client;
+ dev->regmap[0] = regmap_init_i2c(dev->client[0], ®map_config);
+ if (IS_ERR(dev->regmap[0])) {
+ ret = PTR_ERR(dev->regmap[0]);
+ goto err_kfree;
+ }
+
+ /* Check demod answers with correct chip id */
+ ret = regmap_read(dev->regmap[0], 0xff, &uitmp);
+ if (ret)
+ goto err_regmap_0_regmap_exit;
+
+ dev_dbg(&client->dev, "chip id=%02x\n", uitmp);
+
+ if (uitmp != 0x03) {
+ ret = -ENODEV;
+ goto err_regmap_0_regmap_exit;
+ }
+
+ /*
+ * 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 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] == NULL) {
+ ret = -ENODEV;
+ dev_err(&client->dev, "I2C registration failed\n");
+ if (ret)
+ goto err_regmap_0_regmap_exit;
+ }
+ dev->regmap[1] = regmap_init_i2c(dev->client[1], ®map_config);
+ if (IS_ERR(dev->regmap[1])) {
+ ret = PTR_ERR(dev->regmap[1]);
+ goto err_client_1_i2c_unregister_device;
+ }
+ i2c_set_clientdata(dev->client[1], dev);
+
+ dev->client[2] = i2c_new_dummy(client->adapter, 0x1c);
+ if (dev->client[2] == NULL) {
+ ret = -ENODEV;
+ dev_err(&client->dev, "2nd I2C registration failed\n");
+ if (ret)
+ goto err_regmap_1_regmap_exit;
+ }
+ dev->regmap[2] = regmap_init_i2c(dev->client[2], ®map_config);
+ if (IS_ERR(dev->regmap[2])) {
+ ret = PTR_ERR(dev->regmap[2]);
+ goto err_client_2_i2c_unregister_device;
+ }
+ i2c_set_clientdata(dev->client[2], dev);
+
+ /* Sleep because chip is active by default */
+ ret = regmap_write(dev->regmap[2], 0x05, 0x3e);
+ if (ret)
+ goto err_client_2_i2c_unregister_device;
+
+ /* Create dvb frontend */
+ memcpy(&dev->frontend.ops, &mn88473_ops, sizeof(dev->frontend.ops));
+ dev->frontend.demodulator_priv = client;
+ *config->fe = &dev->frontend;
+ i2c_set_clientdata(client, dev);
+
+ dev_info(&client->dev, "Panasonic MN88473 successfully identified\n");
+
+ return 0;
+
+err_client_2_i2c_unregister_device:
+ i2c_unregister_device(dev->client[2]);
+err_regmap_1_regmap_exit:
+ regmap_exit(dev->regmap[1]);
+err_client_1_i2c_unregister_device:
+ i2c_unregister_device(dev->client[1]);
+err_regmap_0_regmap_exit:
+ regmap_exit(dev->regmap[0]);
+err_kfree:
+ kfree(dev);
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int mn88473_remove(struct i2c_client *client)
+{
+ struct mn88473_dev *dev = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "\n");
+
+ regmap_exit(dev->regmap[2]);
+ i2c_unregister_device(dev->client[2]);
+
+ regmap_exit(dev->regmap[1]);
+ i2c_unregister_device(dev->client[1]);
+
+ regmap_exit(dev->regmap[0]);
+
+ kfree(dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id mn88473_id_table[] = {
+ {"mn88473", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, mn88473_id_table);
+
+static struct i2c_driver mn88473_driver = {
+ .driver = {
+ .name = "mn88473",
+ .suppress_bind_attrs = true,
+ },
+ .probe = mn88473_probe,
+ .remove = mn88473_remove,
+ .id_table = mn88473_id_table,
+};
+
+module_i2c_driver(mn88473_driver);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Panasonic MN88473 DVB-T/T2/C demodulator driver");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(MN88473_FIRMWARE);
struct mn88473_config {
/*
* Max num of bytes given I2C adapter could write at once.
- * Default: none
+ * Default: unlimited
*/
u16 i2c_wr_max;
+ /*
+ * Xtal frequency Hz.
+ * Default: 25000000
+ */
+ u32 xtal;
+
/* Everything after that is returned by the driver. */
* DVB frontend.
*/
struct dvb_frontend **fe;
-
- /*
- * Xtal frequency.
- * Hz
- */
- u32 xtal;
};
#endif
--- /dev/null
+/*
+ * Panasonic MN88473 DVB-T/T2/C demodulator driver
+ *
+ * Copyright (C) 2014 Antti Palosaari <crope@iki.fi>
+ *
+ * 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 MN88473_PRIV_H
+#define MN88473_PRIV_H
+
+#include "dvb_frontend.h"
+#include "mn88473.h"
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+
+#define MN88473_FIRMWARE "dvb-demod-mn88473-01.fw"
+
+struct mn88473_dev {
+ struct i2c_client *client[3];
+ struct regmap *regmap[3];
+ struct dvb_frontend frontend;
+ u16 i2c_wr_max;
+ bool active;
+ u32 clk;
+};
+
+#endif
return 0;
}
-static int mt312_get_frontend(struct dvb_frontend *fe)
+static int mt312_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct mt312_state *state = fe->demodulator_priv;
int ret;
return 0;
}
-static int mt352_get_parameters(struct dvb_frontend* fe)
+static int mt352_get_parameters(struct dvb_frontend* fe,
+ struct dtv_frontend_properties *op)
{
- struct dtv_frontend_properties *op = &fe->dtv_property_cache;
struct mt352_state* state = fe->demodulator_priv;
u16 tps;
u16 div;
return 0;
}
-static int or51132_get_parameters(struct dvb_frontend* fe)
+static int or51132_get_parameters(struct dvb_frontend* fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct or51132_state* state = fe->demodulator_priv;
int status;
int retry = 1;
return ret;
}
-static int rtl2830_get_frontend(struct dvb_frontend *fe)
+static int rtl2830_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
{
struct i2c_client *client = fe->demodulator_priv;
struct rtl2830_dev *dev = i2c_get_clientdata(client);
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
int ret;
u8 buf[3];
dev_dbg(&client->dev, "\n");
+ /* stop statistics polling */
+ cancel_delayed_work_sync(&dev->stat_work);
+
i2c_del_mux_adapter(dev->adapter);
regmap_exit(dev->regmap);
kfree(dev);
dev_dbg(&client->dev, "\n");
+ ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x0);
+ if (ret)
+ goto err;
+
for (i = 0; i < ARRAY_SIZE(rtl2832_initial_regs); i++) {
ret = rtl2832_wr_demod_reg(dev, rtl2832_initial_regs[i].reg,
rtl2832_initial_regs[i].value);
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;
return 0;
dev_dbg(&client->dev, "\n");
dev->sleeping = true;
- /* stop statistics polling */
- cancel_delayed_work_sync(&dev->stat_work);
dev->fe_status = 0;
ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x1);
if (fe->ops.tuner_ops.set_params)
fe->ops.tuner_ops.set_params(fe);
- /* PIP mode related */
- ret = rtl2832_bulk_write(client, 0x192, "\x00\x0f\xff", 3);
- if (ret)
- goto err;
-
/* If the frontend has get_if_frequency(), use it */
if (fe->ops.tuner_ops.get_if_frequency) {
u32 if_freq;
return ret;
}
-static int rtl2832_get_frontend(struct dvb_frontend *fe)
+static int rtl2832_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
{
struct rtl2832_dev *dev = fe->demodulator_priv;
struct i2c_client *client = dev->client;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
int ret;
u8 buf[3];
{
struct rtl2832_dev *dev = fe->demodulator_priv;
struct i2c_client *client = dev->client;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
int ret;
u32 uninitialized_var(tmp);
+ u8 u8tmp, buf[2];
+ u16 u16tmp;
dev_dbg(&client->dev, "\n");
}
dev->fe_status = *status;
- return 0;
-err:
- dev_dbg(&client->dev, "failed=%d\n", ret);
- return ret;
-}
-
-static int rtl2832_read_snr(struct dvb_frontend *fe, u16 *snr)
-{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
-
- /* report SNR in resolution of 0.1 dB */
- if (c->cnr.stat[0].scale == FE_SCALE_DECIBEL)
- *snr = div_s64(c->cnr.stat[0].svalue, 100);
- else
- *snr = 0;
-
- return 0;
-}
-
-static int rtl2832_read_ber(struct dvb_frontend *fe, u32 *ber)
-{
- struct rtl2832_dev *dev = fe->demodulator_priv;
-
- *ber = (dev->post_bit_error - dev->post_bit_error_prev);
- dev->post_bit_error_prev = dev->post_bit_error;
-
- return 0;
-}
-
-static void rtl2832_stat_work(struct work_struct *work)
-{
- struct rtl2832_dev *dev = container_of(work, struct rtl2832_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) {
constellation = (u8tmp >> 2) & 0x03; /* [3:2] */
if (constellation > CONSTELLATION_NUM - 1)
- goto err_schedule_delayed_work;
+ goto err;
hierarchy = (u8tmp >> 4) & 0x07; /* [6:4] */
if (hierarchy > HIERARCHY_NUM - 1)
- goto err_schedule_delayed_work;
+ goto err;
ret = rtl2832_bulk_read(client, 0x40c, buf, 2);
if (ret)
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;
+ return 0;
err:
dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int rtl2832_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ /* report SNR in resolution of 0.1 dB */
+ if (c->cnr.stat[0].scale == FE_SCALE_DECIBEL)
+ *snr = div_s64(c->cnr.stat[0].svalue, 100);
+ else
+ *snr = 0;
+
+ return 0;
+}
+
+static int rtl2832_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct rtl2832_dev *dev = fe->demodulator_priv;
+
+ *ber = (dev->post_bit_error - dev->post_bit_error_prev);
+ dev->post_bit_error_prev = dev->post_bit_error;
+
+ return 0;
}
/*
return dev->i2c_adapter_tuner;
}
-static int rtl2832_enable_slave_ts(struct i2c_client *client)
+static int rtl2832_slave_ts_ctrl(struct i2c_client *client, bool enable)
{
struct rtl2832_dev *dev = i2c_get_clientdata(client);
int ret;
- dev_dbg(&client->dev, "\n");
-
- ret = rtl2832_bulk_write(client, 0x10c, "\x5f\xff", 2);
- if (ret)
- goto err;
-
- ret = rtl2832_wr_demod_reg(dev, DVBT_PIP_ON, 0x1);
- if (ret)
- goto err;
+ dev_dbg(&client->dev, "enable=%d\n", enable);
- ret = rtl2832_bulk_write(client, 0x0bc, "\x18", 1);
- if (ret)
- goto err;
-
- ret = rtl2832_bulk_write(client, 0x192, "\x7f\xf7\xff", 3);
- if (ret)
- goto err;
-
- /* soft reset */
- ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x1);
- if (ret)
- goto err;
-
- ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x0);
- if (ret)
- goto err;
+ if (enable) {
+ ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x0);
+ if (ret)
+ goto err;
+ ret = rtl2832_bulk_write(client, 0x10c, "\x5f\xff", 2);
+ if (ret)
+ goto err;
+ ret = rtl2832_wr_demod_reg(dev, DVBT_PIP_ON, 0x1);
+ if (ret)
+ goto err;
+ ret = rtl2832_bulk_write(client, 0x0bc, "\x18", 1);
+ if (ret)
+ goto err;
+ ret = rtl2832_bulk_write(client, 0x192, "\x7f\xf7\xff", 3);
+ if (ret)
+ goto err;
+ } else {
+ ret = rtl2832_bulk_write(client, 0x192, "\x00\x0f\xff", 3);
+ if (ret)
+ goto err;
+ ret = rtl2832_bulk_write(client, 0x0bc, "\x08", 1);
+ if (ret)
+ goto err;
+ ret = rtl2832_wr_demod_reg(dev, DVBT_PIP_ON, 0x0);
+ if (ret)
+ goto err;
+ ret = rtl2832_bulk_write(client, 0x10c, "\x00\x00", 2);
+ if (ret)
+ goto err;
+ ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x1);
+ if (ret)
+ goto err;
+ }
return 0;
err:
dev->pdata = client->dev.platform_data;
dev->sleeping = true;
INIT_DELAYED_WORK(&dev->i2c_gate_work, rtl2832_i2c_gate_work);
- INIT_DELAYED_WORK(&dev->stat_work, rtl2832_stat_work);
/* create regmap */
mutex_init(&dev->regmap_mutex);
dev->regmap_config.reg_bits = 8,
/* setup callbacks */
pdata->get_dvb_frontend = rtl2832_get_dvb_frontend;
pdata->get_i2c_adapter = rtl2832_get_i2c_adapter;
- pdata->enable_slave_ts = rtl2832_enable_slave_ts;
+ pdata->slave_ts_ctrl = rtl2832_slave_ts_ctrl;
pdata->pid_filter = rtl2832_pid_filter;
pdata->pid_filter_ctrl = rtl2832_pid_filter_ctrl;
pdata->bulk_read = rtl2832_bulk_read;
* @tuner: Used tuner model.
* @get_dvb_frontend: Get DVB frontend.
* @get_i2c_adapter: Get I2C adapter.
- * @enable_slave_ts: Enable slave TS IF.
+ * @slave_ts_ctrl: Control slave TS interface.
* @pid_filter: Set PID to PID filter.
* @pid_filter_ctrl: Control PID filter.
*/
struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *);
struct i2c_adapter* (*get_i2c_adapter)(struct i2c_client *);
- int (*enable_slave_ts)(struct i2c_client *);
+ int (*slave_ts_ctrl)(struct i2c_client *, bool);
int (*pid_filter)(struct dvb_frontend *, u8, u16, int);
int (*pid_filter_ctrl)(struct dvb_frontend *, int);
/* private: Register access for SDR module use only */
struct regmap *regmap;
struct i2c_adapter *i2c_adapter_tuner;
struct dvb_frontend fe;
- 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;
return s5h1409_read_ucblocks(fe, ber);
}
-static int s5h1409_get_frontend(struct dvb_frontend *fe)
+static int s5h1409_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct s5h1409_state *state = fe->demodulator_priv;
p->frequency = state->current_frequency;
return s5h1411_read_ucblocks(fe, ber);
}
-static int s5h1411_get_frontend(struct dvb_frontend *fe)
+static int s5h1411_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct s5h1411_state *state = fe->demodulator_priv;
p->frequency = state->current_frequency;
return 0;
}
-static int s5h1420_get_frontend(struct dvb_frontend* fe)
+static int s5h1420_get_frontend(struct dvb_frontend* fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct s5h1420_state* state = fe->demodulator_priv;
p->frequency = state->tunedfreq + s5h1420_getfreqoffset(state);
return 0;
}
-static int s921_get_frontend(struct dvb_frontend *fe)
+static int s921_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct s921_state *state = fe->demodulator_priv;
/* FIXME: Probably it is possible to get it from regs f1 and f2 */
static int si2165_writereg_mask8(struct si2165_state *state, const u16 reg,
u8 val, u8 mask)
{
- int ret;
- u8 tmp;
-
if (mask != 0xff) {
- ret = si2165_readreg8(state, reg, &tmp);
+ u8 tmp;
+ int ret = si2165_readreg8(state, reg, &tmp);
+
if (ret < 0)
- goto err;
+ return ret;
val &= mask;
tmp &= ~mask;
val |= tmp;
}
-
- ret = si2165_writereg8(state, reg, val);
-err:
- return ret;
+ return si2165_writereg8(state, reg, val);
}
#define REG16(reg, val) { (reg), (val) & 0xff }, { (reg)+1, (val)>>8 & 0xff }
struct si2165_state *state = fe->demodulator_priv;
u32 dvb_rate = 0;
u16 bw10k;
+ u32 bw_hz = p->bandwidth_hz;
dprintk("%s: called\n", __func__);
if (!state->has_dvbt)
return -EINVAL;
- if (p->bandwidth_hz > 0) {
- dvb_rate = p->bandwidth_hz * 8 / 7;
- bw10k = p->bandwidth_hz / 10000;
- } else {
- dvb_rate = 8 * 8 / 7;
- bw10k = 800;
- }
+ /* no bandwidth auto-detection */
+ if (bw_hz == 0)
+ return -EINVAL;
+
+ dvb_rate = bw_hz * 8 / 7;
+ bw10k = bw_hz / 10000;
ret = si2165_adjust_pll_divl(state, 12);
if (ret < 0)
return DVBFE_ALGO_SEARCH_ERROR;
}
-static int stb0899_get_frontend(struct dvb_frontend *fe)
+static int stb0899_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct stb0899_state *state = fe->demodulator_priv;
struct stb0899_internal *internal = &state->internal;
if (fe->ops.get_frontend) {
dprintk(verbose, FE_DEBUG, 1, "Get frontend parameters");
- fe->ops.get_frontend(fe);
+ fe->ops.get_frontend(fe, p);
}
srate = p->symbol_rate;
return 0;
}
-static int stv0297_get_frontend(struct dvb_frontend *fe)
+static int stv0297_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct stv0297_state *state = fe->demodulator_priv;
int reg_00, reg_83;
if (debug_legacy_dish_switch)
printk ("%s switch command: 0x%04lx\n",__func__, cmd);
- nexttime = ktime_get_real();
+ nexttime = ktime_get_boottime();
if (debug_legacy_dish_switch)
tv[0] = nexttime;
stv0299_writeregI (state, 0x0c, reg0x0c | 0x50); /* set LNB to 18V */
for (i=0; i<9; i++) {
if (debug_legacy_dish_switch)
- tv[i+1] = ktime_get_real();
+ tv[i+1] = ktime_get_boottime();
if((cmd & 0x01) != last) {
/* set voltage to (last ? 13V : 18V) */
stv0299_writeregI (state, 0x0c, reg0x0c | (last ? lv_mask : 0x50));
return 0;
}
-static int stv0299_get_frontend(struct dvb_frontend *fe)
+static int stv0299_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct stv0299_state* state = fe->demodulator_priv;
s32 derot_freq;
int invval;
return 0;
}
-static int stv0367ter_get_frontend(struct dvb_frontend *fe)
+static int stv0367ter_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct stv0367_state *state = fe->demodulator_priv;
struct stv0367ter_state *ter_state = state->ter_state;
enum stv0367_ter_mode mode;
return 0;
}
-static int stv0367cab_get_frontend(struct dvb_frontend *fe)
+static int stv0367cab_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct stv0367_state *state = fe->demodulator_priv;
struct stv0367cab_state *cab_state = state->cab_state;
s32 pilot, u8 chip_id)
{
u8 aclc_value = 0x29;
- s32 i;
+ s32 i, cllas2_size;
const struct stv0900_car_loop_optim *cls2, *cllqs2, *cllas2;
dprintk("%s\n", __func__);
cls2 = FE_STV0900_S2CarLoop;
cllqs2 = FE_STV0900_S2LowQPCarLoopCut30;
cllas2 = FE_STV0900_S2APSKCarLoopCut30;
+ cllas2_size = ARRAY_SIZE(FE_STV0900_S2APSKCarLoopCut30);
} else if (chip_id == 0x20) {
cls2 = FE_STV0900_S2CarLoopCut20;
cllqs2 = FE_STV0900_S2LowQPCarLoopCut20;
cllas2 = FE_STV0900_S2APSKCarLoopCut20;
+ cllas2_size = ARRAY_SIZE(FE_STV0900_S2APSKCarLoopCut20);
} else {
cls2 = FE_STV0900_S2CarLoopCut30;
cllqs2 = FE_STV0900_S2LowQPCarLoopCut30;
cllas2 = FE_STV0900_S2APSKCarLoopCut30;
+ cllas2_size = ARRAY_SIZE(FE_STV0900_S2APSKCarLoopCut30);
}
if (modcode < STV0900_QPSK_12) {
aclc_value = cls2[i].car_loop_pilots_off_30;
}
- } else {
+ } else if (i < cllas2_size) {
if (srate <= 3000000)
aclc_value = cllas2[i].car_loop_pilots_on_2;
else if (srate <= 7000000)
return 0;
}
-static int stv0900_get_frontend(struct dvb_frontend *fe)
+static int stv0900_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct stv0900_state *state = fe->demodulator_priv;
struct stv0900_internal *intp = state->internal;
enum fe_stv0900_demod_num demod = state->demod;
.release = stv6110x_release
};
-static struct stv6110x_devctl stv6110x_ctl = {
+static const struct stv6110x_devctl stv6110x_ctl = {
.tuner_init = stv6110x_init,
.tuner_sleep = stv6110x_sleep,
.tuner_set_mode = stv6110x_set_mode,
.tuner_get_status = stv6110x_get_status,
};
-struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe,
+const struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe,
const struct stv6110x_config *config,
struct i2c_adapter *i2c)
{
#if IS_REACHABLE(CONFIG_DVB_STV6110x)
-extern struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe,
+extern const struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe,
const struct stv6110x_config *config,
struct i2c_adapter *i2c);
#else
-static inline struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe,
+static inline const struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe,
const struct stv6110x_config *config,
struct i2c_adapter *i2c)
{
const struct stv6110x_config *config;
u8 regs[8];
- struct stv6110x_devctl *devctl;
+ const struct stv6110x_devctl *devctl;
};
#endif /* __STV6110x_PRIV_H */
FEC_2_3, /* for 8PSK. (trellis code) */
};
-static int tc90522s_get_frontend(struct dvb_frontend *fe)
+static int tc90522s_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
{
struct tc90522_state *state;
- struct dtv_frontend_properties *c;
struct dtv_fe_stats *stats;
int ret, i;
int layers;
u32 cndat;
state = fe->demodulator_priv;
- c = &fe->dtv_property_cache;
c->delivery_system = SYS_ISDBS;
c->symbol_rate = 28860000;
DQPSK, QPSK, QAM_16, QAM_64, 0, 0, 0, 0
};
-static int tc90522t_get_frontend(struct dvb_frontend *fe)
+static int tc90522t_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
{
struct tc90522_state *state;
- struct dtv_frontend_properties *c;
struct dtv_fe_stats *stats;
int ret, i;
int layers;
u32 cndat;
state = fe->demodulator_priv;
- c = &fe->dtv_property_cache;
c->delivery_system = SYS_ISDBT;
c->bandwidth_hz = 6000000;
mode = 1;
return 0;
}
-static int tda10021_get_frontend(struct dvb_frontend *fe)
+static int tda10021_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct tda10021_state* state = fe->demodulator_priv;
int sync;
s8 afc = 0;
return 0;
}
-static int tda10023_get_frontend(struct dvb_frontend *fe)
+static int tda10023_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct tda10023_state* state = fe->demodulator_priv;
int sync,inv;
s8 afc = 0;
return 0;
}
-static int tda10048_get_frontend(struct dvb_frontend *fe)
+static int tda10048_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct tda10048_state *state = fe->demodulator_priv;
dprintk(1, "%s()\n", __func__);
return 0;
}
-static int tda1004x_get_fe(struct dvb_frontend *fe)
+static int tda1004x_get_fe(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *fe_params)
{
- struct dtv_frontend_properties *fe_params = &fe->dtv_property_cache;
struct tda1004x_state* state = fe->demodulator_priv;
int status;
return ret;
}
-static int tda10071_get_frontend(struct dvb_frontend *fe)
+static int tda10071_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
{
struct tda10071_dev *dev = fe->demodulator_priv;
struct i2c_client *client = dev->client;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
int ret, i;
u8 buf[5], tmp;
return 0;
}
-static int tda10086_get_frontend(struct dvb_frontend *fe)
+static int tda10086_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *fe_params)
{
- struct dtv_frontend_properties *fe_params = &fe->dtv_property_cache;
struct tda10086_state* state = fe->demodulator_priv;
u8 val;
int tmp;
return 0;
}
-static int tda8083_get_frontend(struct dvb_frontend *fe)
+static int tda8083_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct tda8083_state* state = fe->demodulator_priv;
/* FIXME: get symbolrate & frequency offset...*/
dev_dbg(&client->dev, "\n");
+ /* stop statistics polling */
+ if (!dev->dont_poll)
+ cancel_delayed_work_sync(&dev->stat_work);
+
regmap_exit(dev->regmap);
kfree(dev);
return 0;
return 0;
}
-static int ves1820_get_frontend(struct dvb_frontend *fe)
+static int ves1820_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct ves1820_state* state = fe->demodulator_priv;
int sync;
s8 afc = 0;
return 0;
}
-static int ves1x93_get_frontend(struct dvb_frontend *fe)
+static int ves1x93_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct ves1x93_state* state = fe->demodulator_priv;
int afc;
return 0;
}
-static int zl10353_get_parameters(struct dvb_frontend *fe)
+static int zl10353_get_parameters(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
struct zl10353_state *state = fe->demodulator_priv;
int s6, s9;
u16 tps;
u32 ycbcr_enc;
u32 quantization;
u32 xfer_func;
+ u32 content_type;
/* controls */
struct v4l2_ctrl *hdmi_mode_ctrl;
struct v4l2_ctrl *hotplug_ctrl;
struct v4l2_ctrl *rx_sense_ctrl;
struct v4l2_ctrl *have_edid0_ctrl;
struct v4l2_ctrl *rgb_quantization_range_ctrl;
+ struct v4l2_ctrl *content_type_ctrl;
struct i2c_client *i2c_edid;
struct i2c_client *i2c_pktmem;
struct adv7511_state_edid edid;
}
if (state->rgb_quantization_range_ctrl == ctrl)
return adv7511_set_rgb_quantization_mode(sd, ctrl);
+ if (state->content_type_ctrl == ctrl) {
+ u8 itc, cn;
+
+ state->content_type = ctrl->val;
+ itc = state->content_type != V4L2_DV_IT_CONTENT_TYPE_NO_ITC;
+ cn = itc ? state->content_type : V4L2_DV_IT_CONTENT_TYPE_GRAPHICS;
+ adv7511_wr_and_or(sd, 0x57, 0x7f, itc << 7);
+ adv7511_wr_and_or(sd, 0x59, 0xcf, cn << 4);
+ return 0;
+ }
return -EINVAL;
}
u8 y = HDMI_COLORSPACE_RGB;
u8 q = HDMI_QUANTIZATION_RANGE_DEFAULT;
u8 yq = HDMI_YCC_QUANTIZATION_RANGE_LIMITED;
+ u8 itc = state->content_type != V4L2_DV_IT_CONTENT_TYPE_NO_ITC;
+ u8 cn = itc ? state->content_type : V4L2_DV_IT_CONTENT_TYPE_GRAPHICS;
if (format->pad != 0)
return -EINVAL;
adv7511_wr_and_or(sd, 0x4a, 0xbf, 0);
adv7511_wr_and_or(sd, 0x55, 0x9f, y << 5);
adv7511_wr_and_or(sd, 0x56, 0x3f, c << 6);
- adv7511_wr_and_or(sd, 0x57, 0x83, (ec << 4) | (q << 2));
- adv7511_wr_and_or(sd, 0x59, 0x3f, yq << 6);
+ 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);
return 0;
}
}
+static void adv7511_notify_no_edid(struct v4l2_subdev *sd)
+{
+ struct adv7511_state *state = get_adv7511_state(sd);
+ struct adv7511_edid_detect ed;
+
+ /* We failed to read the EDID, so send an event for this. */
+ ed.present = false;
+ ed.segment = adv7511_rd(sd, 0xc4);
+ v4l2_subdev_notify(sd, ADV7511_EDID_DETECT, (void *)&ed);
+ v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, 0x0);
+}
+
static void adv7511_edid_handler(struct work_struct *work)
{
struct delayed_work *dwork = to_delayed_work(work);
struct adv7511_state *state = container_of(dwork, struct adv7511_state, edid_handler);
struct v4l2_subdev *sd = &state->sd;
- struct adv7511_edid_detect ed;
v4l2_dbg(1, debug, sd, "%s:\n", __func__);
}
/* We failed to read the EDID, so send an event for this. */
- ed.present = false;
- ed.segment = adv7511_rd(sd, 0xc4);
- v4l2_subdev_notify(sd, ADV7511_EDID_DETECT, (void *)&ed);
+ adv7511_notify_no_edid(sd);
v4l2_dbg(1, debug, sd, "%s: no edid found\n", __func__);
}
/* update read only ctrls */
v4l2_ctrl_s_ctrl(state->hotplug_ctrl, adv7511_have_hotplug(sd) ? 0x1 : 0x0);
v4l2_ctrl_s_ctrl(state->rx_sense_ctrl, adv7511_have_rx_sense(sd) ? 0x1 : 0x0);
- v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, state->edid.segments ? 0x1 : 0x0);
if ((status & MASK_ADV7511_HPD_DETECT) && ((status & MASK_ADV7511_MSEN_DETECT) || state->edid.segments)) {
v4l2_dbg(1, debug, sd, "%s: hotplug and (rx-sense or edid)\n", __func__);
}
adv7511_s_power(sd, false);
memset(&state->edid, 0, sizeof(struct adv7511_state_edid));
+ adv7511_notify_no_edid(sd);
}
}
}
/* one more segment read ok */
state->edid.segments = segment + 1;
+ v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, 0x1);
if (((state->edid.data[0x7e] >> 1) + 1) > state->edid.segments) {
/* Request next EDID segment */
v4l2_dbg(1, debug, sd, "%s: request segment %d\n", __func__, state->edid.segments);
ed.present = true;
ed.segment = 0;
state->edid_detect_counter++;
- v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, state->edid.segments ? 0x1 : 0x0);
v4l2_subdev_notify(sd, ADV7511_EDID_DETECT, (void *)&ed);
return ed.present;
}
v4l2_ctrl_new_std_menu(hdl, &adv7511_ctrl_ops,
V4L2_CID_DV_TX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL,
0, V4L2_DV_RGB_RANGE_AUTO);
+ state->content_type_ctrl =
+ v4l2_ctrl_new_std_menu(hdl, &adv7511_ctrl_ops,
+ V4L2_CID_DV_TX_IT_CONTENT_TYPE, V4L2_DV_IT_CONTENT_TYPE_NO_ITC,
+ 0, V4L2_DV_IT_CONTENT_TYPE_NO_ITC);
sd->ctrl_handler = hdl;
if (hdl->error) {
err = hdl->error;
return state->info->has_afe;
}
-/* Supported CEA and DMT timings */
-static const struct v4l2_dv_timings adv76xx_timings[] = {
- V4L2_DV_BT_CEA_720X480P59_94,
- V4L2_DV_BT_CEA_720X576P50,
- V4L2_DV_BT_CEA_1280X720P24,
- V4L2_DV_BT_CEA_1280X720P25,
- V4L2_DV_BT_CEA_1280X720P50,
- V4L2_DV_BT_CEA_1280X720P60,
- V4L2_DV_BT_CEA_1920X1080P24,
- V4L2_DV_BT_CEA_1920X1080P25,
- V4L2_DV_BT_CEA_1920X1080P30,
- V4L2_DV_BT_CEA_1920X1080P50,
- V4L2_DV_BT_CEA_1920X1080P60,
-
- /* sorted by DMT ID */
- V4L2_DV_BT_DMT_640X350P85,
- V4L2_DV_BT_DMT_640X400P85,
- V4L2_DV_BT_DMT_720X400P85,
- V4L2_DV_BT_DMT_640X480P60,
- V4L2_DV_BT_DMT_640X480P72,
- V4L2_DV_BT_DMT_640X480P75,
- V4L2_DV_BT_DMT_640X480P85,
- V4L2_DV_BT_DMT_800X600P56,
- V4L2_DV_BT_DMT_800X600P60,
- V4L2_DV_BT_DMT_800X600P72,
- V4L2_DV_BT_DMT_800X600P75,
- V4L2_DV_BT_DMT_800X600P85,
- V4L2_DV_BT_DMT_848X480P60,
- V4L2_DV_BT_DMT_1024X768P60,
- V4L2_DV_BT_DMT_1024X768P70,
- V4L2_DV_BT_DMT_1024X768P75,
- V4L2_DV_BT_DMT_1024X768P85,
- V4L2_DV_BT_DMT_1152X864P75,
- V4L2_DV_BT_DMT_1280X768P60_RB,
- V4L2_DV_BT_DMT_1280X768P60,
- V4L2_DV_BT_DMT_1280X768P75,
- V4L2_DV_BT_DMT_1280X768P85,
- V4L2_DV_BT_DMT_1280X800P60_RB,
- V4L2_DV_BT_DMT_1280X800P60,
- V4L2_DV_BT_DMT_1280X800P75,
- V4L2_DV_BT_DMT_1280X800P85,
- V4L2_DV_BT_DMT_1280X960P60,
- V4L2_DV_BT_DMT_1280X960P85,
- V4L2_DV_BT_DMT_1280X1024P60,
- V4L2_DV_BT_DMT_1280X1024P75,
- V4L2_DV_BT_DMT_1280X1024P85,
- V4L2_DV_BT_DMT_1360X768P60,
- V4L2_DV_BT_DMT_1400X1050P60_RB,
- V4L2_DV_BT_DMT_1400X1050P60,
- V4L2_DV_BT_DMT_1400X1050P75,
- V4L2_DV_BT_DMT_1400X1050P85,
- V4L2_DV_BT_DMT_1440X900P60_RB,
- V4L2_DV_BT_DMT_1440X900P60,
- V4L2_DV_BT_DMT_1600X1200P60,
- V4L2_DV_BT_DMT_1680X1050P60_RB,
- V4L2_DV_BT_DMT_1680X1050P60,
- V4L2_DV_BT_DMT_1792X1344P60,
- V4L2_DV_BT_DMT_1856X1392P60,
- V4L2_DV_BT_DMT_1920X1200P60_RB,
- V4L2_DV_BT_DMT_1366X768P60_RB,
- V4L2_DV_BT_DMT_1366X768P60,
- V4L2_DV_BT_DMT_1920X1080P60,
- { },
+/* Unsupported timings. This device cannot support 720p30. */
+static const struct v4l2_dv_timings adv76xx_timings_exceptions[] = {
+ V4L2_DV_BT_CEA_1280X720P30,
+ { }
};
+static bool adv76xx_check_dv_timings(const struct v4l2_dv_timings *t, void *hdl)
+{
+ int i;
+
+ for (i = 0; adv76xx_timings_exceptions[i].bt.width; i++)
+ if (v4l2_match_dv_timings(t, adv76xx_timings_exceptions + i, 0, false))
+ return false;
+ return true;
+}
+
struct adv76xx_video_standards {
struct v4l2_dv_timings timings;
u8 vid_std;
state->selected_input == ADV7604_PAD_HDMI_PORT_D;
}
+static const struct v4l2_dv_timings_cap adv7604_timings_cap_analog = {
+ .type = V4L2_DV_BT_656_1120,
+ /* keep this initialization for compatibility with GCC < 4.4.6 */
+ .reserved = { 0 },
+ V4L2_INIT_BT_TIMINGS(0, 1920, 0, 1200, 25000000, 170000000,
+ V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
+ V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT,
+ V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_REDUCED_BLANKING |
+ V4L2_DV_BT_CAP_CUSTOM)
+};
+
+static const struct v4l2_dv_timings_cap adv76xx_timings_cap_digital = {
+ .type = V4L2_DV_BT_656_1120,
+ /* keep this initialization for compatibility with GCC < 4.4.6 */
+ .reserved = { 0 },
+ V4L2_INIT_BT_TIMINGS(0, 1920, 0, 1200, 25000000, 225000000,
+ V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
+ V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT,
+ V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_REDUCED_BLANKING |
+ V4L2_DV_BT_CAP_CUSTOM)
+};
+
+static inline const struct v4l2_dv_timings_cap *
+adv76xx_get_dv_timings_cap(struct v4l2_subdev *sd)
+{
+ return is_digital_input(sd) ? &adv76xx_timings_cap_digital :
+ &adv7604_timings_cap_analog;
+}
+
+
/* ----------------------------------------------------------------------- */
#ifdef CONFIG_VIDEO_ADV_DEBUG
return -EINVAL;
}
+static int adv76xx_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd =
+ &container_of(ctrl->handler, struct adv76xx_state, hdl)->sd;
+
+ if (ctrl->id == V4L2_CID_DV_RX_IT_CONTENT_TYPE) {
+ ctrl->val = V4L2_DV_IT_CONTENT_TYPE_NO_ITC;
+ if ((io_read(sd, 0x60) & 1) && (infoframe_read(sd, 0x03) & 0x80))
+ ctrl->val = (infoframe_read(sd, 0x05) >> 4) & 3;
+ return 0;
+ }
+ return -EINVAL;
+}
+
/* ----------------------------------------------------------------------- */
static inline bool no_power(struct v4l2_subdev *sd)
u32 pix_clk;
int i;
- for (i = 0; adv76xx_timings[i].bt.height; i++) {
- if (vtotal(&adv76xx_timings[i].bt) != stdi->lcf + 1)
+ for (i = 0; v4l2_dv_timings_presets[i].bt.width; i++) {
+ const struct v4l2_bt_timings *bt = &v4l2_dv_timings_presets[i].bt;
+
+ if (!v4l2_valid_dv_timings(&v4l2_dv_timings_presets[i],
+ adv76xx_get_dv_timings_cap(sd),
+ adv76xx_check_dv_timings, NULL))
+ continue;
+ if (vtotal(bt) != stdi->lcf + 1)
continue;
- if (adv76xx_timings[i].bt.vsync != stdi->lcvs)
+ if (bt->vsync != stdi->lcvs)
continue;
- pix_clk = hfreq * htotal(&adv76xx_timings[i].bt);
+ pix_clk = hfreq * htotal(bt);
- if ((pix_clk < adv76xx_timings[i].bt.pixelclock + 1000000) &&
- (pix_clk > adv76xx_timings[i].bt.pixelclock - 1000000)) {
- *timings = adv76xx_timings[i];
+ if ((pix_clk < bt->pixelclock + 1000000) &&
+ (pix_clk > bt->pixelclock - 1000000)) {
+ *timings = v4l2_dv_timings_presets[i];
return 0;
}
}
{
struct adv76xx_state *state = to_state(sd);
- if (timings->index >= ARRAY_SIZE(adv76xx_timings) - 1)
- return -EINVAL;
-
if (timings->pad >= state->source_pad)
return -EINVAL;
- memset(timings->reserved, 0, sizeof(timings->reserved));
- timings->timings = adv76xx_timings[timings->index];
- return 0;
+ return v4l2_enum_dv_timings_cap(timings,
+ adv76xx_get_dv_timings_cap(sd), adv76xx_check_dv_timings, NULL);
}
static int adv76xx_dv_timings_cap(struct v4l2_subdev *sd,
if (cap->pad >= state->source_pad)
return -EINVAL;
- cap->type = V4L2_DV_BT_656_1120;
- cap->bt.max_width = 1920;
- cap->bt.max_height = 1200;
- cap->bt.min_pixelclock = 25000000;
-
- switch (cap->pad) {
- case ADV76XX_PAD_HDMI_PORT_A:
- case ADV7604_PAD_HDMI_PORT_B:
- case ADV7604_PAD_HDMI_PORT_C:
- case ADV7604_PAD_HDMI_PORT_D:
- cap->bt.max_pixelclock = 225000000;
- break;
- case ADV7604_PAD_VGA_RGB:
- case ADV7604_PAD_VGA_COMP:
- default:
- cap->bt.max_pixelclock = 170000000;
- break;
- }
-
- cap->bt.standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
- V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT;
- cap->bt.capabilities = V4L2_DV_BT_CAP_PROGRESSIVE |
- V4L2_DV_BT_CAP_REDUCED_BLANKING | V4L2_DV_BT_CAP_CUSTOM;
+ *cap = *adv76xx_get_dv_timings_cap(sd);
return 0;
}
static void adv76xx_fill_optional_dv_timings_fields(struct v4l2_subdev *sd,
struct v4l2_dv_timings *timings)
{
- int i;
-
- for (i = 0; adv76xx_timings[i].bt.width; i++) {
- if (v4l2_match_dv_timings(timings, &adv76xx_timings[i],
- is_digital_input(sd) ? 250000 : 1000000, false)) {
- *timings = adv76xx_timings[i];
- break;
- }
- }
+ v4l2_find_dv_timings_cap(timings, adv76xx_get_dv_timings_cap(sd),
+ is_digital_input(sd) ? 250000 : 1000000,
+ adv76xx_check_dv_timings, NULL);
}
static unsigned int adv7604_read_hdmi_pixelclock(struct v4l2_subdev *sd)
bt = &timings->bt;
- if ((is_analog_input(sd) && bt->pixelclock > 170000000) ||
- (is_digital_input(sd) && bt->pixelclock > 225000000)) {
- v4l2_dbg(1, debug, sd, "%s: pixelclock out of range %d\n",
- __func__, (u32)bt->pixelclock);
+ if (!v4l2_valid_dv_timings(timings, adv76xx_get_dv_timings_cap(sd),
+ adv76xx_check_dv_timings, NULL))
return -ERANGE;
- }
adv76xx_fill_optional_dv_timings_fields(sd, timings);
return 0;
}
+static int adv76xx_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_selection *sel)
+{
+ struct adv76xx_state *state = to_state(sd);
+
+ if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+ /* Only CROP, CROP_DEFAULT and CROP_BOUNDS are supported */
+ if (sel->target > V4L2_SEL_TGT_CROP_BOUNDS)
+ return -EINVAL;
+
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = state->timings.bt.width;
+ sel->r.height = state->timings.bt.height;
+
+ return 0;
+}
+
static int adv76xx_set_format(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *format)
rep_write(sd, 0x76, spa_loc & 0xff);
rep_write_clr_set(sd, 0x77, 0x40, (spa_loc & 0x100) >> 2);
} else {
- /* FIXME: Where is the SPA location LSB register ? */
+ /* ADV7612 Software Manual Rev. A, p. 15 */
+ rep_write(sd, 0x70, spa_loc & 0xff);
rep_write_clr_set(sd, 0x71, 0x01, (spa_loc & 0x100) >> 8);
}
static const struct v4l2_ctrl_ops adv76xx_ctrl_ops = {
.s_ctrl = adv76xx_s_ctrl,
+ .g_volatile_ctrl = adv76xx_g_volatile_ctrl,
};
static const struct v4l2_subdev_core_ops adv76xx_core_ops = {
static const struct v4l2_subdev_pad_ops adv76xx_pad_ops = {
.enum_mbus_code = adv76xx_enum_mbus_code,
+ .get_selection = adv76xx_get_selection,
.get_fmt = adv76xx_get_format,
.set_fmt = adv76xx_set_format,
.get_edid = adv76xx_get_edid,
struct device_node *endpoint;
struct device_node *np;
unsigned int flags;
+ int ret;
u32 v;
np = state->i2c_clients[ADV76XX_PAGE_IO]->dev.of_node;
if (!endpoint)
return -EINVAL;
- v4l2_of_parse_endpoint(endpoint, &bus_cfg);
+ ret = v4l2_of_parse_endpoint(endpoint, &bus_cfg);
+ if (ret) {
+ of_node_put(endpoint);
+ return ret;
+ }
if (!of_property_read_u32(endpoint, "default-input", &v))
state->pdata.default_input = v;
V4L2_DV_BT_CEA_640X480P59_94;
struct adv76xx_state *state;
struct v4l2_ctrl_handler *hdl;
+ struct v4l2_ctrl *ctrl;
struct v4l2_subdev *sd;
unsigned int i;
unsigned int val, val2;
V4L2_CID_SATURATION, 0, 255, 1, 128);
v4l2_ctrl_new_std(hdl, &adv76xx_ctrl_ops,
V4L2_CID_HUE, 0, 128, 1, 0);
+ ctrl = v4l2_ctrl_new_std_menu(hdl, &adv76xx_ctrl_ops,
+ V4L2_CID_DV_RX_IT_CONTENT_TYPE, V4L2_DV_IT_CONTENT_TYPE_NO_ITC,
+ 0, V4L2_DV_IT_CONTENT_TYPE_NO_ITC);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
/* private controls */
state->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL,
return -EINVAL;
}
+static int adv7842_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+
+ if (ctrl->id == V4L2_CID_DV_RX_IT_CONTENT_TYPE) {
+ ctrl->val = V4L2_DV_IT_CONTENT_TYPE_NO_ITC;
+ if ((io_read(sd, 0x60) & 1) && (infoframe_read(sd, 0x03) & 0x80))
+ ctrl->val = (infoframe_read(sd, 0x05) >> 4) & 3;
+ return 0;
+ }
+ return -EINVAL;
+}
+
static inline bool no_power(struct v4l2_subdev *sd)
{
return io_read(sd, 0x0c) & 0x24;
static const struct v4l2_ctrl_ops adv7842_ctrl_ops = {
.s_ctrl = adv7842_s_ctrl,
+ .g_volatile_ctrl = adv7842_g_volatile_ctrl,
};
static const struct v4l2_subdev_core_ops adv7842_core_ops = {
V4L2_DV_BT_CEA_640X480P59_94;
struct adv7842_platform_data *pdata = client->dev.platform_data;
struct v4l2_ctrl_handler *hdl;
+ struct v4l2_ctrl *ctrl;
struct v4l2_subdev *sd;
u16 rev;
int err;
V4L2_CID_SATURATION, 0, 255, 1, 128);
v4l2_ctrl_new_std(hdl, &adv7842_ctrl_ops,
V4L2_CID_HUE, 0, 128, 1, 0);
+ ctrl = v4l2_ctrl_new_std_menu(hdl, &adv7842_ctrl_ops,
+ V4L2_CID_DV_RX_IT_CONTENT_TYPE, V4L2_DV_IT_CONTENT_TYPE_NO_ITC,
+ 0, V4L2_DV_IT_CONTENT_TYPE_NO_ITC);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
/* custom controls */
state->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL,
int msp_revision;
int msp_product, msp_prod_hi, msp_prod_lo;
int msp_rom;
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ int ret;
+#endif
if (!id)
strlcpy(client->name, "msp3400", sizeof(client->name));
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &msp_ops);
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ state->pads[IF_AUD_DEC_PAD_IF_INPUT].flags = MEDIA_PAD_FL_SINK;
+ state->pads[IF_AUD_DEC_PAD_OUT].flags = MEDIA_PAD_FL_SOURCE;
+
+ sd->entity.function = MEDIA_ENT_F_IF_AUD_DECODER;
+
+ ret = media_entity_pads_init(&sd->entity, 2, state->pads);
+ if (ret < 0)
+ return ret;
+#endif
+
state->v4l2_std = V4L2_STD_NTSC;
state->detected_std = V4L2_STD_ALL;
state->audmode = V4L2_TUNER_MODE_STEREO;
#include <media/drv-intf/msp3400.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
+#include <media/v4l2-mc.h>
/* ---------------------------------------------------------------------- */
wait_queue_head_t wq;
unsigned int restart:1;
unsigned int watch_stereo:1;
+
+#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER)
+ struct media_pad pads[IF_AUD_DEC_PAD_NUM_PADS];
+#endif
};
static inline struct msp_state *to_state(struct v4l2_subdev *sd)
struct mt9v011 {
struct v4l2_subdev sd;
+#ifdef CONFIG_MEDIA_CONTROLLER
+ struct media_pad pad;
+#endif
struct v4l2_ctrl_handler ctrls;
unsigned width, height;
unsigned xtal;
u16 version;
struct mt9v011 *core;
struct v4l2_subdev *sd;
+#ifdef CONFIG_MEDIA_CONTROLLER
+ int ret;
+#endif
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(c->adapter,
sd = &core->sd;
v4l2_i2c_subdev_init(sd, c, &mt9v011_ops);
+#ifdef CONFIG_MEDIA_CONTROLLER
+ core->pad.flags = MEDIA_PAD_FL_SOURCE;
+ sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
+
+ ret = media_entity_pads_init(&sd->entity, 1, &core->pad);
+ if (ret < 0)
+ return ret;
+#endif
+
/* Check if the sensor is really a MT9V011 */
version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION);
if ((version != MT9V011_VERSION) &&
#include <linux/clk.h>
#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/log2.h>
#include <linux/mutex.h>
struct regmap *regmap;
struct clk *clk;
+ struct gpio_desc *reset_gpio;
+ struct gpio_desc *standby_gpio;
struct mt9v032_platform_data *pdata;
const struct mt9v032_model_info *model;
struct regmap *map = mt9v032->regmap;
int ret;
+ if (mt9v032->reset_gpio)
+ gpiod_set_value_cansleep(mt9v032->reset_gpio, 1);
+
ret = clk_set_rate(mt9v032->clk, mt9v032->sysclk);
if (ret < 0)
return ret;
+ /* System clock has to be enabled before releasing the reset */
ret = clk_prepare_enable(mt9v032->clk);
if (ret)
return ret;
udelay(1);
+ if (mt9v032->reset_gpio) {
+ gpiod_set_value_cansleep(mt9v032->reset_gpio, 0);
+
+ /* After releasing reset we need to wait 10 clock cycles
+ * before accessing the sensor over I2C. As the minimum SYSCLK
+ * frequency is 13MHz, waiting 1µs will be enough in the worst
+ * case.
+ */
+ udelay(1);
+ }
+
/* Reset the chip and stop data read out */
ret = regmap_write(map, MT9V032_RESET, 1);
if (ret < 0)
if (IS_ERR(mt9v032->clk))
return PTR_ERR(mt9v032->clk);
+ mt9v032->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(mt9v032->reset_gpio))
+ return PTR_ERR(mt9v032->reset_gpio);
+
+ mt9v032->standby_gpio = devm_gpiod_get_optional(&client->dev, "standby",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(mt9v032->standby_gpio))
+ return PTR_ERR(mt9v032->standby_gpio);
+
mutex_init(&mt9v032->power_lock);
mt9v032->pdata = pdata;
mt9v032->model = (const void *)did->driver_data;
}
usleep_range(1000, 2000);
- ret = ov2659_init(sd, 0);
- if (ret < 0)
- return ret;
-
/* Check sensor revision */
ret = ov2659_read(client, REG_SC_CHIP_ID_H, &pid);
if (!ret)
dev_err(&client->dev,
"Sensor detection failed (%04X, %d)\n",
id, ret);
- else
+ else {
dev_info(&client->dev, "Found OV%04X sensor\n", id);
+ ret = ov2659_init(sd, 0);
+ }
}
return ret;
ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE;
v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false);
- v4l2_ctrl_auto_cluster(3, &ctrls->auto_gain, 0, true);
- v4l2_ctrl_auto_cluster(3, &ctrls->auto_exp, 1, true);
+ v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true);
+ v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, 1, true);
v4l2_ctrl_cluster(2, &ctrls->hflip);
ov965x->sd.ctrl_handler = hdl;
return 0;
}
- v4l2_of_parse_endpoint(node_ep, &ep);
+ ret = v4l2_of_parse_endpoint(node_ep, &ep);
of_node_put(node_ep);
+ if (ret)
+ return ret;
if (ep.bus_type != V4L2_MBUS_CSI2) {
dev_err(dev, "unsupported bus type\n");
SPI_DIR_RX,
SPI_DIR_TX
};
-MODULE_DEVICE_TABLE(of, s5c73m3_spi_ids);
static int spi_xmit(struct spi_device *spi_dev, void *addr, const int len,
enum spi_direction dir)
return -EINVAL;
}
- v4l2_of_parse_endpoint(node_ep, &ep);
+ ret = v4l2_of_parse_endpoint(node_ep, &ep);
of_node_put(node_ep);
+ if (ret)
+ return ret;
+
state->bus_type = ep.bus_type;
switch (state->bus_type) {
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
+#include <media/v4l2-mc.h>
#include <media/i2c/saa7115.h>
#include <asm/div64.h>
struct saa711x_state {
struct v4l2_subdev sd;
+#ifdef CONFIG_MEDIA_CONTROLLER
+ struct media_pad pads[DEMOD_NUM_PADS];
+#endif
struct v4l2_ctrl_handler hdl;
struct {
struct saa7115_platform_data *pdata;
int ident;
char name[CHIP_VER_SIZE + 1];
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ int ret;
+#endif
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &saa711x_ops);
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ state->pads[DEMOD_PAD_IF_INPUT].flags = MEDIA_PAD_FL_SINK;
+ state->pads[DEMOD_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE;
+ state->pads[DEMOD_PAD_VBI_OUT].flags = MEDIA_PAD_FL_SOURCE;
+
+ sd->entity.function = MEDIA_ENT_F_ATV_DECODER;
+
+ ret = media_entity_pads_init(&sd->entity, DEMOD_NUM_PADS, state->pads);
+ if (ret < 0)
+ return ret;
+#endif
+
v4l_info(client, "%s found @ 0x%x (%s)\n", name,
client->addr << 1, client->adapter->name);
hdl = &state->hdl;
.s_mbus_config = mt9m001_s_mbus_config,
};
-static struct v4l2_subdev_sensor_ops mt9m001_subdev_sensor_ops = {
+static const struct v4l2_subdev_sensor_ops mt9m001_subdev_sensor_ops = {
.g_skip_top_lines = mt9m001_g_skip_top_lines,
};
.s_mbus_config = mt9t031_s_mbus_config,
};
-static struct v4l2_subdev_sensor_ops mt9t031_subdev_sensor_ops = {
+static const struct v4l2_subdev_sensor_ops mt9t031_subdev_sensor_ops = {
.g_skip_top_lines = mt9t031_g_skip_top_lines,
};
.s_mbus_config = mt9v022_s_mbus_config,
};
-static struct v4l2_subdev_sensor_ops mt9v022_subdev_sensor_ops = {
+static const struct v4l2_subdev_sensor_ops mt9v022_subdev_sensor_ops = {
.g_skip_top_lines = mt9v022_g_skip_top_lines,
};
#define EDID_NUM_BLOCKS_MAX 8
#define EDID_BLOCK_SIZE 128
-/* Max transfer size done by I2C transfer functions */
-#define MAX_XFER_SIZE (EDID_NUM_BLOCKS_MAX * EDID_BLOCK_SIZE + 2)
+#define I2C_MAX_XFER_SIZE (EDID_BLOCK_SIZE + 2)
static const struct v4l2_dv_timings_cap tc358743_timings_cap = {
.type = V4L2_DV_BT_656_1120,
/* edid */
u8 edid_blocks_written;
- /* used by i2c_wr() */
- u8 wr_data[MAX_XFER_SIZE];
-
struct v4l2_dv_timings timings;
u32 mbus_fmt_code;
{
struct tc358743_state *state = to_state(sd);
struct i2c_client *client = state->i2c_client;
- u8 *data = state->wr_data;
int err, i;
struct i2c_msg msg;
+ u8 data[I2C_MAX_XFER_SIZE];
- if ((2 + n) > sizeof(state->wr_data))
+ if ((2 + n) > I2C_MAX_XFER_SIZE) {
+ n = I2C_MAX_XFER_SIZE - 2;
v4l2_warn(sd, "i2c wr reg=%04x: len=%d is too big!\n",
reg, 2 + n);
+ }
msg.addr = client->addr;
msg.buf = data;
if (tc358743_get_detected_timings(sd, &timings)) {
enable_stream(sd, false);
- v4l2_dbg(1, debug, sd, "%s: Format changed. No signal\n",
+ v4l2_dbg(1, debug, sd, "%s: No signal\n",
__func__);
} else {
if (!v4l2_match_dv_timings(&state->timings, &timings, 0, false))
enable_stream(sd, false);
- v4l2_print_dv_timings(sd->name,
- "tc358743_format_change: Format changed. New format: ",
- &timings, false);
+ if (debug)
+ v4l2_print_dv_timings(sd->name,
+ "tc358743_format_change: New format: ",
+ &timings, false);
}
if (sd->devnode)
#ifdef CONFIG_VIDEO_ADV_DEBUG
static void tc358743_print_register_map(struct v4l2_subdev *sd)
{
- v4l2_info(sd, "0x0000–0x00FF: Global Control Register\n");
- v4l2_info(sd, "0x0100–0x01FF: CSI2-TX PHY Register\n");
- v4l2_info(sd, "0x0200–0x03FF: CSI2-TX PPI Register\n");
- v4l2_info(sd, "0x0400–0x05FF: Reserved\n");
- v4l2_info(sd, "0x0600–0x06FF: CEC Register\n");
- v4l2_info(sd, "0x0700–0x84FF: Reserved\n");
- v4l2_info(sd, "0x8500–0x85FF: HDMIRX System Control Register\n");
- v4l2_info(sd, "0x8600–0x86FF: HDMIRX Audio Control Register\n");
- v4l2_info(sd, "0x8700–0x87FF: HDMIRX InfoFrame packet data Register\n");
- v4l2_info(sd, "0x8800–0x88FF: HDMIRX HDCP Port Register\n");
- v4l2_info(sd, "0x8900–0x89FF: HDMIRX Video Output Port & 3D Register\n");
- v4l2_info(sd, "0x8A00–0x8BFF: Reserved\n");
- v4l2_info(sd, "0x8C00–0x8FFF: HDMIRX EDID-RAM (1024bytes)\n");
- v4l2_info(sd, "0x9000–0x90FF: HDMIRX GBD Extraction Control\n");
- v4l2_info(sd, "0x9100–0x92FF: HDMIRX GBD RAM read\n");
+ v4l2_info(sd, "0x0000-0x00FF: Global Control Register\n");
+ v4l2_info(sd, "0x0100-0x01FF: CSI2-TX PHY Register\n");
+ v4l2_info(sd, "0x0200-0x03FF: CSI2-TX PPI Register\n");
+ v4l2_info(sd, "0x0400-0x05FF: Reserved\n");
+ v4l2_info(sd, "0x0600-0x06FF: CEC Register\n");
+ v4l2_info(sd, "0x0700-0x84FF: Reserved\n");
+ v4l2_info(sd, "0x8500-0x85FF: HDMIRX System Control Register\n");
+ v4l2_info(sd, "0x8600-0x86FF: HDMIRX Audio Control Register\n");
+ v4l2_info(sd, "0x8700-0x87FF: HDMIRX InfoFrame packet data Register\n");
+ v4l2_info(sd, "0x8800-0x88FF: HDMIRX HDCP Port Register\n");
+ v4l2_info(sd, "0x8900-0x89FF: HDMIRX Video Output Port & 3D Register\n");
+ v4l2_info(sd, "0x8A00-0x8BFF: Reserved\n");
+ v4l2_info(sd, "0x8C00-0x8FFF: HDMIRX EDID-RAM (1024bytes)\n");
+ v4l2_info(sd, "0x9000-0x90FF: HDMIRX GBD Extraction Control\n");
+ v4l2_info(sd, "0x9100-0x92FF: HDMIRX GBD RAM read\n");
v4l2_info(sd, "0x9300- : Reserved\n");
}
{
struct tc358743_state *state = to_state(sd);
u16 edid_len = edid->blocks * EDID_BLOCK_SIZE;
+ int i;
v4l2_dbg(2, debug, sd, "%s, pad %d, start block %d, blocks %d\n",
__func__, edid->pad, edid->start_block, edid->blocks);
return 0;
}
- i2c_wr(sd, EDID_RAM, edid->edid, edid_len);
+ for (i = 0; i < edid_len; i += EDID_BLOCK_SIZE)
+ i2c_wr(sd, EDID_RAM + i, edid->edid + i, EDID_BLOCK_SIZE);
state->edid_blocks_written = edid->blocks;
static struct tvp514x_platform_data *
tvp514x_get_pdata(struct i2c_client *client)
{
- struct tvp514x_platform_data *pdata;
+ struct tvp514x_platform_data *pdata = NULL;
struct v4l2_of_endpoint bus_cfg;
struct device_node *endpoint;
unsigned int flags;
if (!endpoint)
return NULL;
+ if (v4l2_of_parse_endpoint(endpoint, &bus_cfg))
+ goto done;
+
pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
goto done;
- v4l2_of_parse_endpoint(endpoint, &bus_cfg);
flags = bus_cfg.bus.parallel.flags;
if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
/*
- * tvp5150 - Texas Instruments TVP5150A/AM1 video decoder driver
+ * tvp5150 - Texas Instruments TVP5150A/AM1 and TVP5151 video decoder driver
*
* Copyright (c) 2005,2006 Mauro Carvalho Chehab (mchehab@infradead.org)
* This code is placed under the terms of the GNU General Public License v2
*/
+#include <dt-bindings/media/tvp5150.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <media/v4l2-async.h>
#include <media/v4l2-device.h>
-#include <media/i2c/tvp5150.h>
#include <media/v4l2-ctrls.h>
+#include <media/v4l2-of.h>
+#include <media/v4l2-mc.h>
#include "tvp5150_reg.h"
#define TVP5150_MAX_CROP_TOP 127
#define TVP5150_CROP_SHIFT 2
-MODULE_DESCRIPTION("Texas Instruments TVP5150A video decoder driver");
+MODULE_DESCRIPTION("Texas Instruments TVP5150A/TVP5150AM1/TVP5151 video decoder driver");
MODULE_AUTHOR("Mauro Carvalho Chehab");
MODULE_LICENSE("GPL");
struct tvp5150 {
struct v4l2_subdev sd;
+#ifdef CONFIG_MEDIA_CONTROLLER
+ struct media_pad pads[DEMOD_NUM_PADS];
+ struct media_entity input_ent[TVP5150_INPUT_NUM];
+ struct media_pad input_pad[TVP5150_INPUT_NUM];
+#endif
struct v4l2_ctrl_handler hdl;
struct v4l2_rect rect;
u32 input;
u32 output;
int enable;
+
+ u16 dev_id;
+ u16 rom_ver;
+
+ enum v4l2_mbus_type mbus_type;
};
static inline struct tvp5150 *to_tvp5150(struct v4l2_subdev *sd)
int input = 0;
int val;
- if ((decoder->output & TVP5150_BLACK_SCREEN) || !decoder->enable)
- input = 8;
+ /* Only tvp5150am1 and tvp5151 have signal generator support */
+ if ((decoder->dev_id == 0x5150 && decoder->rom_ver == 0x0400) ||
+ (decoder->dev_id == 0x5151 && decoder->rom_ver == 0x0100)) {
+ if (!decoder->enable)
+ input = 8;
+ }
switch (decoder->input) {
case TVP5150_COMPOSITE1:
v4l2_ctrl_handler_setup(&decoder->hdl);
tvp5150_set_std(sd, decoder->norm);
+
+ if (decoder->mbus_type == V4L2_MBUS_PARALLEL)
+ tvp5150_write(sd, TVP5150_DATA_RATE_SEL, 0x40);
+
return 0;
};
static int tvp5150_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct v4l2_subdev *sd = to_sd(ctrl);
+ struct tvp5150 *decoder = to_tvp5150(sd);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
return 0;
case V4L2_CID_HUE:
tvp5150_write(sd, TVP5150_HUE_CTL, ctrl->val);
+ case V4L2_CID_TEST_PATTERN:
+ decoder->enable = ctrl->val ? false : true;
+ tvp5150_selmux(sd);
return 0;
}
return -EINVAL;
}
}
-static int tvp5150_enum_mbus_code(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- if (code->pad || code->index)
- return -EINVAL;
-
- code->code = MEDIA_BUS_FMT_UYVY8_2X8;
- return 0;
-}
-
static int tvp5150_fill_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *format)
tvp5150_reset(sd, 0);
f->width = decoder->rect.width;
- f->height = decoder->rect.height;
+ f->height = decoder->rect.height / 2;
f->code = MEDIA_BUS_FMT_UYVY8_2X8;
- f->field = V4L2_FIELD_SEQ_TB;
+ f->field = V4L2_FIELD_ALTERNATE;
f->colorspace = V4L2_COLORSPACE_SMPTE170M;
v4l2_dbg(1, debug, sd, "width = %d, height = %d\n", f->width,
return 0;
}
+static int tvp5150_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct tvp5150 *decoder = to_tvp5150(sd);
+
+ cfg->type = decoder->mbus_type;
+ cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING
+ | V4L2_MBUS_FIELD_EVEN_LOW | V4L2_MBUS_DATA_ACTIVE_HIGH;
+
+ return 0;
+}
+
+/****************************************************************************
+ V4L2 subdev pad ops
+ ****************************************************************************/
+static int tvp5150_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->pad || code->index)
+ return -EINVAL;
+
+ code->code = MEDIA_BUS_FMT_UYVY8_2X8;
+ return 0;
+}
+
+static int tvp5150_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct tvp5150 *decoder = to_tvp5150(sd);
+
+ if (fse->index >= 8 || fse->code != MEDIA_BUS_FMT_UYVY8_2X8)
+ return -EINVAL;
+
+ fse->code = MEDIA_BUS_FMT_UYVY8_2X8;
+ fse->min_width = decoder->rect.width;
+ fse->max_width = decoder->rect.width;
+ fse->min_height = decoder->rect.height / 2;
+ fse->max_height = decoder->rect.height / 2;
+
+ return 0;
+}
+
+/****************************************************************************
+ Media entity ops
+ ****************************************************************************/
+
+static int tvp5150_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+#ifdef CONFIG_MEDIA_CONTROLLER
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct tvp5150 *decoder = to_tvp5150(sd);
+ int i;
+
+ for (i = 0; i < TVP5150_INPUT_NUM; i++) {
+ if (remote->entity == &decoder->input_ent[i])
+ break;
+ }
+
+ /* Do nothing for entities that are not input connectors */
+ if (i == TVP5150_INPUT_NUM)
+ return 0;
+
+ decoder->input = i;
+
+ tvp5150_selmux(sd);
+#endif
+
+ return 0;
+}
+
+static const struct media_entity_operations tvp5150_sd_media_ops = {
+ .link_setup = tvp5150_link_setup,
+};
+
/****************************************************************************
I2C Command
****************************************************************************/
+static int tvp5150_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct tvp5150 *decoder = to_tvp5150(sd);
+ /* Output format: 8-bit ITU-R BT.656 with embedded syncs */
+ int val = 0x09;
+
+ /* Output format: 8-bit 4:2:2 YUV with discrete sync */
+ if (decoder->mbus_type == V4L2_MBUS_PARALLEL)
+ val = 0x0d;
+
+ /* Initializes TVP5150 to its default values */
+ /* # set PCLK (27MHz) */
+ tvp5150_write(sd, TVP5150_CONF_SHARED_PIN, 0x00);
+
+ if (enable)
+ tvp5150_write(sd, TVP5150_MISC_CTL, val);
+ else
+ tvp5150_write(sd, TVP5150_MISC_CTL, 0x00);
+
+ return 0;
+}
+
static int tvp5150_s_routing(struct v4l2_subdev *sd,
u32 input, u32 output, u32 config)
{
decoder->input = input;
decoder->output = output;
+
+ if (output == TVP5150_BLACK_SCREEN)
+ decoder->enable = false;
+ else
+ decoder->enable = true;
+
tvp5150_selmux(sd);
return 0;
}
return 0;
}
+static int tvp5150_registered_async(struct v4l2_subdev *sd)
+{
+#ifdef CONFIG_MEDIA_CONTROLLER
+ struct tvp5150 *decoder = to_tvp5150(sd);
+ int ret = 0;
+ int i;
+
+ for (i = 0; i < TVP5150_INPUT_NUM; i++) {
+ struct media_entity *input = &decoder->input_ent[i];
+ struct media_pad *pad = &decoder->input_pad[i];
+
+ if (!input->name)
+ continue;
+
+ decoder->input_pad[i].flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(input, 1, pad);
+ if (ret < 0)
+ return ret;
+
+ ret = media_device_register_entity(sd->v4l2_dev->mdev, input);
+ if (ret < 0)
+ return ret;
+
+ ret = media_create_pad_link(input, 0, &sd->entity,
+ DEMOD_PAD_IF_INPUT, 0);
+ if (ret < 0) {
+ media_device_unregister_entity(input);
+ return ret;
+ }
+ }
+#endif
+
+ return 0;
+}
+
/* ----------------------------------------------------------------------- */
static const struct v4l2_ctrl_ops tvp5150_ctrl_ops = {
.g_register = tvp5150_g_register,
.s_register = tvp5150_s_register,
#endif
+ .registered_async = tvp5150_registered_async,
};
static const struct v4l2_subdev_tuner_ops tvp5150_tuner_ops = {
static const struct v4l2_subdev_video_ops tvp5150_video_ops = {
.s_std = tvp5150_s_std,
+ .s_stream = tvp5150_s_stream,
.s_routing = tvp5150_s_routing,
.s_crop = tvp5150_s_crop,
.g_crop = tvp5150_g_crop,
.cropcap = tvp5150_cropcap,
+ .g_mbus_config = tvp5150_g_mbus_config,
};
static const struct v4l2_subdev_vbi_ops tvp5150_vbi_ops = {
static const struct v4l2_subdev_pad_ops tvp5150_pad_ops = {
.enum_mbus_code = tvp5150_enum_mbus_code,
+ .enum_frame_size = tvp5150_enum_frame_size,
.set_fmt = tvp5150_fill_fmt,
.get_fmt = tvp5150_fill_fmt,
};
I2C Client & Driver
****************************************************************************/
+static int tvp5150_detect_version(struct tvp5150 *core)
+{
+ struct v4l2_subdev *sd = &core->sd;
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
+ unsigned int i;
+ u8 regs[4];
+ int res;
+
+ /*
+ * Read consequent registers - TVP5150_MSB_DEV_ID, TVP5150_LSB_DEV_ID,
+ * TVP5150_ROM_MAJOR_VER, TVP5150_ROM_MINOR_VER
+ */
+ for (i = 0; i < 4; i++) {
+ res = tvp5150_read(sd, TVP5150_MSB_DEV_ID + i);
+ if (res < 0)
+ return res;
+ regs[i] = res;
+ }
+
+ core->dev_id = (regs[0] << 8) | regs[1];
+ core->rom_ver = (regs[2] << 8) | regs[3];
+
+ v4l2_info(sd, "tvp%04x (%u.%u) chip found @ 0x%02x (%s)\n",
+ core->dev_id, regs[2], regs[3], c->addr << 1,
+ c->adapter->name);
+
+ if (core->dev_id == 0x5150 && core->rom_ver == 0x0321) {
+ v4l2_info(sd, "tvp5150a detected.\n");
+ } else if (core->dev_id == 0x5150 && core->rom_ver == 0x0400) {
+ v4l2_info(sd, "tvp5150am1 detected.\n");
+
+ /* ITU-T BT.656.4 timing */
+ tvp5150_write(sd, TVP5150_REV_SELECT, 0);
+ } else if (core->dev_id == 0x5151 && core->rom_ver == 0x0100) {
+ v4l2_info(sd, "tvp5151 detected.\n");
+ } else {
+ v4l2_info(sd, "*** unknown tvp%04x chip detected.\n",
+ core->dev_id);
+ }
+
+ return 0;
+}
+
+static int tvp5150_init(struct i2c_client *c)
+{
+ struct gpio_desc *pdn_gpio;
+ struct gpio_desc *reset_gpio;
+
+ pdn_gpio = devm_gpiod_get_optional(&c->dev, "pdn", GPIOD_OUT_HIGH);
+ if (IS_ERR(pdn_gpio))
+ return PTR_ERR(pdn_gpio);
+
+ if (pdn_gpio) {
+ gpiod_set_value_cansleep(pdn_gpio, 0);
+ /* Delay time between power supplies active and reset */
+ msleep(20);
+ }
+
+ reset_gpio = devm_gpiod_get_optional(&c->dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(reset_gpio))
+ return PTR_ERR(reset_gpio);
+
+ if (reset_gpio) {
+ /* RESETB pulse duration */
+ ndelay(500);
+ gpiod_set_value_cansleep(reset_gpio, 0);
+ /* Delay time between end of reset to I2C active */
+ usleep_range(200, 250);
+ }
+
+ return 0;
+}
+
+static int tvp5150_parse_dt(struct tvp5150 *decoder, struct device_node *np)
+{
+ struct v4l2_of_endpoint bus_cfg;
+ struct device_node *ep;
+#ifdef CONFIG_MEDIA_CONTROLLER
+ struct device_node *connectors, *child;
+ struct media_entity *input;
+ const char *name;
+ u32 input_type;
+#endif
+ unsigned int flags;
+ int ret = 0;
+
+ ep = of_graph_get_next_endpoint(np, NULL);
+ if (!ep)
+ return -EINVAL;
+
+ ret = v4l2_of_parse_endpoint(ep, &bus_cfg);
+ if (ret)
+ goto err;
+
+ flags = bus_cfg.bus.parallel.flags;
+
+ if (bus_cfg.bus_type == V4L2_MBUS_PARALLEL &&
+ !(flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH &&
+ flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH &&
+ flags & V4L2_MBUS_FIELD_EVEN_LOW)) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ decoder->mbus_type = bus_cfg.bus_type;
+
+#ifdef CONFIG_MEDIA_CONTROLLER
+ connectors = of_get_child_by_name(np, "connectors");
+
+ if (!connectors)
+ goto err;
+
+ for_each_available_child_of_node(connectors, child) {
+ ret = of_property_read_u32(child, "input", &input_type);
+ if (ret) {
+ v4l2_err(&decoder->sd,
+ "missing type property in node %s\n",
+ child->name);
+ goto err_connector;
+ }
+
+ if (input_type >= TVP5150_INPUT_NUM) {
+ ret = -EINVAL;
+ goto err_connector;
+ }
+
+ input = &decoder->input_ent[input_type];
+
+ /* Each input connector can only be defined once */
+ if (input->name) {
+ v4l2_err(&decoder->sd,
+ "input %s with same type already exists\n",
+ input->name);
+ ret = -EINVAL;
+ goto err_connector;
+ }
+
+ switch (input_type) {
+ case TVP5150_COMPOSITE0:
+ case TVP5150_COMPOSITE1:
+ input->function = MEDIA_ENT_F_CONN_COMPOSITE;
+ break;
+ case TVP5150_SVIDEO:
+ input->function = MEDIA_ENT_F_CONN_SVIDEO;
+ break;
+ }
+
+ input->flags = MEDIA_ENT_FL_CONNECTOR;
+
+ ret = of_property_read_string(child, "label", &name);
+ if (ret < 0) {
+ v4l2_err(&decoder->sd,
+ "missing label property in node %s\n",
+ child->name);
+ goto err_connector;
+ }
+
+ input->name = name;
+ }
+
+err_connector:
+ of_node_put(connectors);
+#endif
+err:
+ of_node_put(ep);
+ return ret;
+}
+
+static const char * const tvp5150_test_patterns[2] = {
+ "Disabled",
+ "Black screen"
+};
+
static int tvp5150_probe(struct i2c_client *c,
const struct i2c_device_id *id)
{
struct tvp5150 *core;
struct v4l2_subdev *sd;
- int tvp5150_id[4];
- int i, res;
+ struct device_node *np = c->dev.of_node;
+ int res;
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(c->adapter,
I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
return -EIO;
+ res = tvp5150_init(c);
+ if (res)
+ return res;
+
core = devm_kzalloc(&c->dev, sizeof(*core), GFP_KERNEL);
if (!core)
return -ENOMEM;
+
sd = &core->sd;
- v4l2_i2c_subdev_init(sd, c, &tvp5150_ops);
- /*
- * Read consequent registers - TVP5150_MSB_DEV_ID, TVP5150_LSB_DEV_ID,
- * TVP5150_ROM_MAJOR_VER, TVP5150_ROM_MINOR_VER
- */
- for (i = 0; i < 4; i++) {
- res = tvp5150_read(sd, TVP5150_MSB_DEV_ID + i);
- if (res < 0)
+ if (IS_ENABLED(CONFIG_OF) && np) {
+ res = tvp5150_parse_dt(core, np);
+ if (res) {
+ v4l2_err(sd, "DT parsing error: %d\n", res);
return res;
- tvp5150_id[i] = res;
+ }
+ } else {
+ /* Default to BT.656 embedded sync */
+ core->mbus_type = V4L2_MBUS_BT656;
}
- v4l_info(c, "chip found @ 0x%02x (%s)\n",
- c->addr << 1, c->adapter->name);
+ v4l2_i2c_subdev_init(sd, c, &tvp5150_ops);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
- if (tvp5150_id[2] == 4 && tvp5150_id[3] == 0) { /* Is TVP5150AM1 */
- v4l2_info(sd, "tvp%02x%02xam1 detected.\n",
- tvp5150_id[0], tvp5150_id[1]);
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ core->pads[DEMOD_PAD_IF_INPUT].flags = MEDIA_PAD_FL_SINK;
+ core->pads[DEMOD_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE;
+ core->pads[DEMOD_PAD_VBI_OUT].flags = MEDIA_PAD_FL_SOURCE;
- /* ITU-T BT.656.4 timing */
- tvp5150_write(sd, TVP5150_REV_SELECT, 0);
- } else {
- /* Is TVP5150A */
- if (tvp5150_id[2] == 3 || tvp5150_id[3] == 0x21) {
- v4l2_info(sd, "tvp%02x%02xa detected.\n",
- tvp5150_id[0], tvp5150_id[1]);
- } else {
- v4l2_info(sd, "*** unknown tvp%02x%02x chip detected.\n",
- tvp5150_id[0], tvp5150_id[1]);
- v4l2_info(sd, "*** Rom ver is %d.%d\n",
- tvp5150_id[2], tvp5150_id[3]);
- }
- }
+ sd->entity.function = MEDIA_ENT_F_ATV_DECODER;
+
+ res = media_entity_pads_init(&sd->entity, DEMOD_NUM_PADS, core->pads);
+ if (res < 0)
+ return res;
+
+ sd->entity.ops = &tvp5150_sd_media_ops;
+#endif
+
+ res = tvp5150_detect_version(core);
+ if (res < 0)
+ return res;
core->norm = V4L2_STD_ALL; /* Default is autodetect */
core->input = TVP5150_COMPOSITE1;
- core->enable = 1;
+ core->enable = true;
- v4l2_ctrl_handler_init(&core->hdl, 4);
+ v4l2_ctrl_handler_init(&core->hdl, 5);
v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops,
V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops,
V4L2_CID_SATURATION, 0, 255, 1, 128);
v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops,
V4L2_CID_HUE, -128, 127, 1, 0);
+ v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops,
+ V4L2_CID_PIXEL_RATE, 27000000,
+ 27000000, 1, 27000000);
+ v4l2_ctrl_new_std_menu_items(&core->hdl, &tvp5150_ctrl_ops,
+ V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(tvp5150_test_patterns),
+ 0, 0, tvp5150_test_patterns);
sd->ctrl_handler = &core->hdl;
if (core->hdl.error) {
res = core->hdl.error;
};
MODULE_DEVICE_TABLE(i2c, tvp5150_id);
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id tvp5150_of_match[] = {
+ { .compatible = "ti,tvp5150", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, tvp5150_of_match);
+#endif
+
static struct i2c_driver tvp5150_driver = {
.driver = {
+ .of_match_table = of_match_ptr(tvp5150_of_match),
.name = "tvp5150",
},
.probe = tvp5150_probe,
tvp7002_get_pdata(struct i2c_client *client)
{
struct v4l2_of_endpoint bus_cfg;
- struct tvp7002_config *pdata;
+ struct tvp7002_config *pdata = NULL;
struct device_node *endpoint;
unsigned int flags;
if (!endpoint)
return NULL;
+ if (v4l2_of_parse_endpoint(endpoint, &bus_cfg))
+ goto done;
+
pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
goto done;
- v4l2_of_parse_endpoint(endpoint, &bus_cfg);
flags = bus_cfg.bus.parallel.flags;
if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
return 0;
}
-static u16 vpx3220_fp_read(struct v4l2_subdev *sd, u16 fpaddr)
+static int vpx3220_fp_read(struct v4l2_subdev *sd, u16 fpaddr)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
s16 data;
#include <linux/media.h>
#include <linux/slab.h>
#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/usb.h>
#include <media/media-device.h>
#include <media/media-devnode.h>
* Userspace API
*/
+static inline void __user *media_get_uptr(__u64 arg)
+{
+ return (void __user *)(uintptr_t)arg;
+}
+
static int media_device_open(struct file *filp)
{
return 0;
memset(&info, 0, sizeof(info));
- strlcpy(info.driver, dev->dev->driver->name, sizeof(info.driver));
+ if (dev->driver_name[0])
+ strlcpy(info.driver, dev->driver_name, sizeof(info.driver));
+ else
+ strlcpy(info.driver, dev->dev->driver->name, sizeof(info.driver));
+
strlcpy(info.model, dev->model, sizeof(info.model));
strlcpy(info.serial, dev->serial, sizeof(info.serial));
strlcpy(info.bus_info, dev->bus_info, sizeof(info.bus_info));
return ret;
}
-#if 0 /* Let's postpone it to Kernel 4.6 */
static long __media_device_get_topology(struct media_device *mdev,
struct media_v2_topology *topo)
{
struct media_interface *intf;
struct media_pad *pad;
struct media_link *link;
- struct media_v2_entity kentity, *uentity;
- struct media_v2_interface kintf, *uintf;
- struct media_v2_pad kpad, *upad;
- struct media_v2_link klink, *ulink;
+ struct media_v2_entity kentity, __user *uentity;
+ struct media_v2_interface kintf, __user *uintf;
+ struct media_v2_pad kpad, __user *upad;
+ struct media_v2_link klink, __user *ulink;
unsigned int i;
int ret = 0;
return 0;
}
-#endif
static long media_device_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
mutex_unlock(&dev->graph_mutex);
break;
-#if 0 /* Let's postpone it to Kernel 4.6 */
case MEDIA_IOC_G_TOPOLOGY:
mutex_lock(&dev->graph_mutex);
ret = media_device_get_topology(dev,
(struct media_v2_topology __user *)arg);
mutex_unlock(&dev->graph_mutex);
break;
-#endif
+
default:
ret = -ENOIOCTLCMD;
}
case MEDIA_IOC_DEVICE_INFO:
case MEDIA_IOC_ENUM_ENTITIES:
case MEDIA_IOC_SETUP_LINK:
-#if 0 /* Let's postpone it to Kernel 4.6 */
case MEDIA_IOC_G_TOPOLOGY:
-#endif
return media_device_ioctl(filp, cmd, arg);
case MEDIA_IOC_ENUM_LINKS32:
int __must_check media_device_register_entity(struct media_device *mdev,
struct media_entity *entity)
{
+ struct media_entity_notify *notify, *next;
unsigned int i;
int ret;
media_gobj_create(mdev, MEDIA_GRAPH_PAD,
&entity->pads[i].graph_obj);
+ /* invoke entity_notify callbacks */
+ list_for_each_entry_safe(notify, next, &mdev->entity_notify, list) {
+ (notify)->notify(entity, notify->notify_data);
+ }
+
spin_unlock(&mdev->lock);
+ mutex_lock(&mdev->graph_mutex);
+ if (mdev->entity_internal_idx_max
+ >= mdev->pm_count_walk.ent_enum.idx_max) {
+ struct media_entity_graph new = { .top = 0 };
+
+ /*
+ * Initialise the new graph walk before cleaning up
+ * the old one in order not to spoil the graph walk
+ * object of the media device if graph walk init fails.
+ */
+ ret = media_entity_graph_walk_init(&new, mdev);
+ if (ret) {
+ mutex_unlock(&mdev->graph_mutex);
+ return ret;
+ }
+ media_entity_graph_walk_cleanup(&mdev->pm_count_walk);
+ mdev->pm_count_walk = new;
+ }
+ mutex_unlock(&mdev->graph_mutex);
+
return 0;
}
EXPORT_SYMBOL_GPL(media_device_register_entity);
/* Remove the entity */
media_gobj_destroy(&entity->graph_obj);
+ /* invoke entity_notify callbacks to handle entity removal?? */
+
entity->graph_obj.mdev = NULL;
}
INIT_LIST_HEAD(&mdev->interfaces);
INIT_LIST_HEAD(&mdev->pads);
INIT_LIST_HEAD(&mdev->links);
+ INIT_LIST_HEAD(&mdev->entity_notify);
spin_lock_init(&mdev->lock);
mutex_init(&mdev->graph_mutex);
ida_init(&mdev->entity_internal_idx);
{
ida_destroy(&mdev->entity_internal_idx);
mdev->entity_internal_idx_max = 0;
+ media_entity_graph_walk_cleanup(&mdev->pm_count_walk);
mutex_destroy(&mdev->graph_mutex);
}
EXPORT_SYMBOL_GPL(media_device_cleanup);
}
EXPORT_SYMBOL_GPL(__media_device_register);
+int __must_check media_device_register_entity_notify(struct media_device *mdev,
+ struct media_entity_notify *nptr)
+{
+ spin_lock(&mdev->lock);
+ list_add_tail(&nptr->list, &mdev->entity_notify);
+ spin_unlock(&mdev->lock);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(media_device_register_entity_notify);
+
+/*
+ * Note: Should be called with mdev->lock held.
+ */
+static void __media_device_unregister_entity_notify(struct media_device *mdev,
+ struct media_entity_notify *nptr)
+{
+ list_del(&nptr->list);
+}
+
+void media_device_unregister_entity_notify(struct media_device *mdev,
+ struct media_entity_notify *nptr)
+{
+ spin_lock(&mdev->lock);
+ __media_device_unregister_entity_notify(mdev, nptr);
+ spin_unlock(&mdev->lock);
+}
+EXPORT_SYMBOL_GPL(media_device_unregister_entity_notify);
+
void media_device_unregister(struct media_device *mdev)
{
struct media_entity *entity;
struct media_entity *next;
struct media_interface *intf, *tmp_intf;
+ struct media_entity_notify *notify, *nextp;
if (mdev == NULL)
return;
list_for_each_entry_safe(entity, next, &mdev->entities, graph_obj.list)
__media_device_unregister_entity(entity);
+ /* Remove all entity_notify callbacks from the media device */
+ list_for_each_entry_safe(notify, nextp, &mdev->entity_notify, list)
+ __media_device_unregister_entity_notify(mdev, notify);
+
/* Remove all interfaces from the media device */
list_for_each_entry_safe(intf, tmp_intf, &mdev->interfaces,
graph_obj.list) {
}
EXPORT_SYMBOL_GPL(media_device_find_devres);
+void media_device_pci_init(struct media_device *mdev,
+ struct pci_dev *pci_dev,
+ const char *name)
+{
+#ifdef CONFIG_PCI
+ mdev->dev = &pci_dev->dev;
+
+ if (name)
+ strlcpy(mdev->model, name, sizeof(mdev->model));
+ else
+ strlcpy(mdev->model, pci_name(pci_dev), sizeof(mdev->model));
+
+ sprintf(mdev->bus_info, "PCI:%s", pci_name(pci_dev));
+
+ mdev->hw_revision = (pci_dev->subsystem_vendor << 16)
+ | pci_dev->subsystem_device;
+
+ mdev->driver_version = LINUX_VERSION_CODE;
+
+ media_device_init(mdev);
+#endif
+}
+EXPORT_SYMBOL_GPL(media_device_pci_init);
+
+void __media_device_usb_init(struct media_device *mdev,
+ struct usb_device *udev,
+ const char *board_name,
+ const char *driver_name)
+{
+#ifdef CONFIG_USB
+ mdev->dev = &udev->dev;
+
+ if (driver_name)
+ strlcpy(mdev->driver_name, driver_name,
+ sizeof(mdev->driver_name));
+
+ if (board_name)
+ strlcpy(mdev->model, board_name, sizeof(mdev->model));
+ else if (udev->product)
+ strlcpy(mdev->model, udev->product, sizeof(mdev->model));
+ else
+ strlcpy(mdev->model, "unknown model", sizeof(mdev->model));
+ if (udev->serial)
+ strlcpy(mdev->serial, udev->serial, sizeof(mdev->serial));
+ usb_make_path(udev, mdev->bus_info, sizeof(mdev->bus_info));
+ mdev->hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
+ mdev->driver_version = LINUX_VERSION_CODE;
+
+ media_device_init(mdev);
+#endif
+}
+EXPORT_SYMBOL_GPL(__media_device_usb_init);
+
+
#endif /* CONFIG_MEDIA_CONTROLLER */
ret = mdev->fops->open(filp);
if (ret) {
put_device(&mdev->dev);
+ filp->private_data = NULL;
return ret;
}
}
{
switch (intf->type) {
case MEDIA_INTF_T_DVB_FE:
- return "frontend";
+ return "dvb-frontend";
case MEDIA_INTF_T_DVB_DEMUX:
- return "demux";
+ return "dvb-demux";
case MEDIA_INTF_T_DVB_DVR:
- return "DVR";
+ return "dvb-dvr";
case MEDIA_INTF_T_DVB_CA:
- return "CA";
+ return "dvb-ca";
case MEDIA_INTF_T_DVB_NET:
- return "dvbnet";
+ return "dvb-net";
case MEDIA_INTF_T_V4L_VIDEO:
- return "video";
+ return "v4l-video";
case MEDIA_INTF_T_V4L_VBI:
- return "vbi";
+ return "v4l-vbi";
case MEDIA_INTF_T_V4L_RADIO:
- return "radio";
+ return "v4l-radio";
case MEDIA_INTF_T_V4L_SUBDEV:
- return "v4l2-subdev";
+ return "v4l-subdev";
case MEDIA_INTF_T_V4L_SWRADIO:
- return "swradio";
+ return "v4l-swradio";
+ case MEDIA_INTF_T_ALSA_PCM_CAPTURE:
+ return "alsa-pcm-capture";
+ case MEDIA_INTF_T_ALSA_PCM_PLAYBACK:
+ return "alsa-pcm-playback";
+ case MEDIA_INTF_T_ALSA_CONTROL:
+ return "alsa-control";
+ case MEDIA_INTF_T_ALSA_COMPRESS:
+ return "alsa-compress";
+ case MEDIA_INTF_T_ALSA_RAWMIDI:
+ return "alsa-rawmidi";
+ case MEDIA_INTF_T_ALSA_HWDEP:
+ return "alsa-hwdep";
+ case MEDIA_INTF_T_ALSA_SEQUENCER:
+ return "alsa-sequencer";
+ case MEDIA_INTF_T_ALSA_TIMER:
+ return "alsa-timer";
default:
return "unknown-intf";
}
__must_check int __media_entity_enum_init(struct media_entity_enum *ent_enum,
int idx_max)
{
- ent_enum->bmap = kcalloc(DIV_ROUND_UP(idx_max, BITS_PER_LONG),
- sizeof(long), GFP_KERNEL);
+ idx_max = ALIGN(idx_max, BITS_PER_LONG);
+ ent_enum->bmap = kcalloc(idx_max / BITS_PER_LONG, sizeof(long),
+ GFP_KERNEL);
if (!ent_enum->bmap)
return -ENOMEM;
* Pipeline management
*/
-__must_check int media_entity_pipeline_start(struct media_entity *entity,
- struct media_pipeline *pipe)
+__must_check int __media_entity_pipeline_start(struct media_entity *entity,
+ struct media_pipeline *pipe)
{
struct media_device *mdev = entity->graph_obj.mdev;
struct media_entity_graph *graph = &pipe->graph;
struct media_link *link;
int ret;
- mutex_lock(&mdev->graph_mutex);
-
if (!pipe->streaming_count++) {
ret = media_entity_graph_walk_init(&pipe->graph, mdev);
if (ret)
}
}
- mutex_unlock(&mdev->graph_mutex);
-
return 0;
error:
media_entity_graph_walk_start(graph, entity_err);
while ((entity_err = media_entity_graph_walk_next(graph))) {
- entity_err->stream_count--;
- if (entity_err->stream_count == 0)
- entity_err->pipe = NULL;
+ /* don't let the stream_count go negative */
+ if (entity->stream_count > 0) {
+ entity_err->stream_count--;
+ if (entity_err->stream_count == 0)
+ entity_err->pipe = NULL;
+ }
/*
* We haven't increased stream_count further than this
if (!--pipe->streaming_count)
media_entity_graph_walk_cleanup(graph);
- mutex_unlock(&mdev->graph_mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(__media_entity_pipeline_start);
+__must_check int media_entity_pipeline_start(struct media_entity *entity,
+ struct media_pipeline *pipe)
+{
+ struct media_device *mdev = entity->graph_obj.mdev;
+ int ret;
+
+ mutex_lock(&mdev->graph_mutex);
+ ret = __media_entity_pipeline_start(entity, pipe);
+ mutex_unlock(&mdev->graph_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(media_entity_pipeline_start);
-void media_entity_pipeline_stop(struct media_entity *entity)
+void __media_entity_pipeline_stop(struct media_entity *entity)
{
- struct media_device *mdev = entity->graph_obj.mdev;
struct media_entity_graph *graph = &entity->pipe->graph;
struct media_pipeline *pipe = entity->pipe;
- mutex_lock(&mdev->graph_mutex);
WARN_ON(!pipe->streaming_count);
media_entity_graph_walk_start(graph, entity);
while ((entity = media_entity_graph_walk_next(graph))) {
- entity->stream_count--;
- if (entity->stream_count == 0)
- entity->pipe = NULL;
+ /* don't let the stream_count go negative */
+ if (entity->stream_count > 0) {
+ entity->stream_count--;
+ if (entity->stream_count == 0)
+ entity->pipe = NULL;
+ }
}
if (!--pipe->streaming_count)
media_entity_graph_walk_cleanup(graph);
+}
+EXPORT_SYMBOL_GPL(__media_entity_pipeline_stop);
+
+void media_entity_pipeline_stop(struct media_entity *entity)
+{
+ struct media_device *mdev = entity->graph_obj.mdev;
+
+ mutex_lock(&mdev->graph_mutex);
+ __media_entity_pipeline_stop(entity);
mutex_unlock(&mdev->graph_mutex);
}
EXPORT_SYMBOL_GPL(media_entity_pipeline_stop);
return ret;
}
+EXPORT_SYMBOL_GPL(__media_entity_setup_link);
int media_entity_setup_link(struct media_link *link, u32 flags)
{
#define DRIVER_VERSION "0.1"
#define DRIVER_NAME "flexcop-pci"
-#define DRIVER_AUTHOR "Patrick Boettcher <patrick.boettcher@desy.de>"
+#define DRIVER_AUTHOR "Patrick Boettcher <patrick.boettcher@posteo.de>"
struct flexcop_pci {
struct pci_dev *pdev;
static ssize_t show_card(struct device *cd,
struct device_attribute *attr, char *buf)
{
- struct video_device *vfd = container_of(cd, struct video_device, dev);
+ struct video_device *vfd = to_video_device(cd);
struct bttv *btv = video_get_drvdata(vfd);
return sprintf(buf, "%d\n", btv ? btv->c.type : UNSET);
}
struct bttv_fh *fh = priv;
struct bttv *btv = fh->btv;
unsigned int i;
- int err = 0;
for (i = 0; i < BTTV_TVNORMS; i++)
if (id & bttv_tvnorms[i].v4l2_id)
break;
- if (i == BTTV_TVNORMS) {
- err = -EINVAL;
- goto err;
- }
-
+ if (i == BTTV_TVNORMS)
+ return -EINVAL;
btv->std = id;
set_tvnorm(btv, i);
-
-err:
-
- return err;
+ return 0;
}
static int bttv_g_std(struct file *file, void *priv, v4l2_std_id *id)
{
struct bttv_fh *fh = priv;
struct bttv *btv = fh->btv;
- int rc = 0;
- if (i->index >= bttv_tvcards[btv->c.type].video_inputs) {
- rc = -EINVAL;
- goto err;
- }
+ if (i->index >= bttv_tvcards[btv->c.type].video_inputs)
+ return -EINVAL;
i->type = V4L2_INPUT_TYPE_CAMERA;
i->audioset = 0;
}
i->std = BTTV_NORMS;
-
-err:
-
- return rc;
+ return 0;
}
static int bttv_g_input(struct file *file, void *priv, unsigned int *i)
return 0;
}
+static void bttv_get_width_mask_vid_cap(const struct bttv_format *fmt,
+ unsigned int *width_mask,
+ unsigned int *width_bias)
+{
+ if (fmt->flags & FORMAT_FLAGS_PLANAR) {
+ *width_mask = ~15; /* width must be a multiple of 16 pixels */
+ *width_bias = 8; /* nearest */
+ } else {
+ *width_mask = ~3; /* width must be a multiple of 4 pixels */
+ *width_bias = 2; /* nearest */
+ }
+}
+
static int bttv_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
enum v4l2_field field;
__s32 width, height;
__s32 height2;
+ unsigned int width_mask, width_bias;
int rc;
fmt = format_by_fourcc(f->fmt.pix.pixelformat);
width = f->fmt.pix.width;
height = f->fmt.pix.height;
+ bttv_get_width_mask_vid_cap(fmt, &width_mask, &width_bias);
rc = limit_scaled_size_lock(fh, &width, &height, field,
- /* width_mask: 4 pixels */ ~3,
- /* width_bias: nearest */ 2,
+ width_mask, width_bias,
/* adjust_size */ 1,
/* adjust_crop */ 0);
if (0 != rc)
struct bttv_fh *fh = priv;
struct bttv *btv = fh->btv;
__s32 width, height;
+ unsigned int width_mask, width_bias;
enum v4l2_field field;
retval = bttv_switch_type(fh, f->type);
height = f->fmt.pix.height;
field = f->fmt.pix.field;
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ bttv_get_width_mask_vid_cap(fmt, &width_mask, &width_bias);
retval = limit_scaled_size_lock(fh, &width, &height, f->fmt.pix.field,
- /* width_mask: 4 pixels */ ~3,
- /* width_bias: nearest */ 2,
+ width_mask, width_bias,
/* adjust_size */ 1,
/* adjust_crop */ 1);
if (0 != retval)
f->fmt.pix.field = field;
- fmt = format_by_fourcc(f->fmt.pix.pixelformat);
-
/* update our state informations */
fh->fmt = fmt;
fh->cap.field = f->fmt.pix.field;
return dst_algo ? DVBFE_ALGO_HW : DVBFE_ALGO_SW;
}
-static int dst_get_frontend(struct dvb_frontend *fe)
+static int dst_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct dst_state *state = fe->demodulator_priv;
p->frequency = state->decode_freq;
return request_firmware(fw, name, &bt->bt->dev->dev);
}
-static struct sp887x_config microtune_mt7202dtf_config = {
+static const struct sp887x_config microtune_mt7202dtf_config = {
.demod_address = 0x70,
.request_firmware = microtune_mt7202dtf_request_firmware,
};
bttv_write_gpio(bt->bttv_nr, 0x0001, 0x0000);
}
-static struct or51211_config or51211_config = {
+static const struct or51211_config or51211_config = {
.demod_address = 0x15,
.request_firmware = or51211_request_firmware,
.setmode = or51211_setmode,
u8 eeprom[256]; /* 24C02 i2c eeprom */
struct sp2_config sp2_config;
struct i2c_board_info info;
- struct cx23885_i2c *i2c_bus2 = &dev->i2c_bus[1];
+ struct cx23885_i2c *i2c_bus = &dev->i2c_bus[0];
/* attach CI */
memset(&sp2_config, 0, sizeof(sp2_config));
info.addr = 0x40;
info.platform_data = &sp2_config;
request_module(info.type);
- client_ci = i2c_new_device(&i2c_bus2->i2c_adap, &info);
+ client_ci = i2c_new_device(&i2c_bus->i2c_adap, &info);
if (client_ci == NULL || client_ci->dev.driver == NULL)
return -ENODEV;
if (!try_module_get(client_ci->dev.driver->owner)) {
break;
case CX23885_BOARD_DVBSKY_T980C:
case CX23885_BOARD_TT_CT2_4500_CI:
- i2c_bus = &dev->i2c_bus[1];
- i2c_bus2 = &dev->i2c_bus[0];
+ i2c_bus = &dev->i2c_bus[0];
+ i2c_bus2 = &dev->i2c_bus[1];
/* attach frontend */
memset(&si2168_config, 0, sizeof(si2168_config));
info.addr = 0x64;
info.platform_data = &si2168_config;
request_module(info.type);
- client_demod = i2c_new_device(&i2c_bus->i2c_adap, &info);
+ client_demod = i2c_new_device(&i2c_bus2->i2c_adap, &info);
if (client_demod == NULL || client_demod->dev.driver == NULL)
goto frontend_detach;
if (!try_module_get(client_demod->dev.driver->owner)) {
port->i2c_client_tuner = client_tuner;
break;
case CX23885_BOARD_DVBSKY_S950C:
- i2c_bus = &dev->i2c_bus[1];
- i2c_bus2 = &dev->i2c_bus[0];
+ i2c_bus = &dev->i2c_bus[0];
+ i2c_bus2 = &dev->i2c_bus[1];
/* attach frontend */
fe0->dvb.frontend = dvb_attach(m88ds3103_attach,
&dvbsky_s950c_m88ds3103_config,
- &i2c_bus->i2c_adap, &adapter);
+ &i2c_bus2->i2c_adap, &adapter);
if (fe0->dvb.frontend == NULL)
break;
/* register everything */
ret = vb2_dvb_register_bus(&port->frontends, THIS_MODULE, port,
- &dev->pci->dev, adapter_nr, mfe_shared);
+ &dev->pci->dev, NULL,
+ adapter_nr, mfe_shared);
if (ret)
goto frontend_detach;
/* register everything */
res = vb2_dvb_register_bus(&dev->frontends, THIS_MODULE, dev,
- &dev->pci->dev, adapter_nr, mfe_shared);
+ &dev->pci->dev, NULL, adapter_nr,
+ mfe_shared);
if (res)
goto frontend_detach;
return res;
struct stv090x_config *feconf = type ? &stv0900_aa : &stv0900;
struct stv6110x_config *tunerconf = (input->nr & 1) ?
&stv6110b : &stv6110a;
- struct stv6110x_devctl *ctl;
+ const struct stv6110x_devctl *ctl;
ctl = dvb_attach(stv6110x_attach, input->fe, tunerconf, i2c);
if (!ctl) {
spin_unlock_irqrestore(&s->qlock, flags);
return -ENOMEM;
}
- while (bytes_available < needed_bytes) {
+ while (steal && bytes_available < needed_bytes) {
struct ivtv_buffer *buf = list_entry(steal->list.prev, struct ivtv_buffer, list);
u16 dma_xfer_cnt = buf->dma_xfer_cnt;
}
if (vb2_dvb_register_bus(&ndev->frontends[num],
- THIS_MODULE, NULL,
- &ndev->pci_dev->dev, adapter_nr, 1)) {
+ THIS_MODULE, NULL,
+ &ndev->pci_dev->dev, NULL, adapter_nr, 1)) {
dev_dbg(&ndev->pci_dev->dev,
"%s(): unable to register DVB bus %d\n",
__func__, num);
/* allocate device context */
ndev = kzalloc(sizeof(*ndev), GFP_KERNEL);
-
if (!ndev)
goto dev_alloc_err;
- memset(ndev, 0, sizeof(*ndev));
+
ndev->old_fw = old_firmware;
ndev->wq = create_singlethread_workqueue(NETUP_UNIDVB_NAME);
if (!ndev->wq) {
chan->dev->card_info->fe_config[chan->number];
struct stv6110x_config *tunerconf = (struct stv6110x_config *)
chan->dev->card_info->tuner_config[chan->number];
- struct stv6110x_devctl *ctl;
+ const struct stv6110x_devctl *ctl;
/* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */
if (chan->number < 2)
if (!try_module_get(cl->dev.driver->owner))
goto err_demod_i2c_unregister_device;
- if (!strncmp(cl->name, TC90522_I2C_DEV_SAT, sizeof(cl->name))) {
+ if (!strncmp(cl->name, TC90522_I2C_DEV_SAT,
+ strlen(TC90522_I2C_DEV_SAT))) {
struct qm1d1c0042_config tcfg;
tcfg = adap_conf[i].tuner_cfg.qm1d1c0042;
#include "xc5000.h"
#include "s5h1411.h"
-/* commly used strings */
-static char name_mute[] = "mute";
-static char name_radio[] = "Radio";
-static char name_tv[] = "Television";
-static char name_tv_mono[] = "TV (mono only)";
-static char name_comp[] = "Composite";
-static char name_comp1[] = "Composite1";
-static char name_comp2[] = "Composite2";
-static char name_comp3[] = "Composite3";
-static char name_comp4[] = "Composite4";
-static char name_svideo[] = "S-Video";
+/* Input names */
+const char * const saa7134_input_name[] = {
+ [SAA7134_INPUT_MUTE] = "mute",
+ [SAA7134_INPUT_RADIO] = "Radio",
+ [SAA7134_INPUT_TV] = "Television",
+ [SAA7134_INPUT_TV_MONO] = "TV (mono only)",
+ [SAA7134_INPUT_COMPOSITE] = "Composite",
+ [SAA7134_INPUT_COMPOSITE0] = "Composite0",
+ [SAA7134_INPUT_COMPOSITE1] = "Composite1",
+ [SAA7134_INPUT_COMPOSITE2] = "Composite2",
+ [SAA7134_INPUT_COMPOSITE3] = "Composite3",
+ [SAA7134_INPUT_COMPOSITE4] = "Composite4",
+ [SAA7134_INPUT_SVIDEO] = "S-Video",
+ [SAA7134_INPUT_SVIDEO0] = "S-Video0",
+ [SAA7134_INPUT_SVIDEO1] = "S-Video1",
+ [SAA7134_INPUT_COMPOSITE_OVER_SVIDEO] = "Composite over S-Video",
+};
/* ------------------------------------------------------------------ */
/* board config info */
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = "default",
+ .type = SAA7134_INPUT_COMPOSITE,
.vmux = 0,
.amux = LINE1,
}},
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE1,
},{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_tv_mono,
+ .type = SAA7134_INPUT_TV_MONO,
.vmux = 1,
.amux = LINE2,
- .tv = 1,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
},
.gpiomask = 0xe000,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
.gpio = 0x8000,
- .tv = 1,
},{
- .name = name_tv_mono,
+ .type = SAA7134_INPUT_TV_MONO,
.vmux = 1,
.amux = LINE2,
.gpio = 0x0000,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE2,
.gpio = 0x4000,
},{
- .name = name_comp2,
+ .type = SAA7134_INPUT_COMPOSITE2,
.vmux = 3,
.amux = LINE2,
.gpio = 0x4000,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
.gpio = 0x4000,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
.gpio = 0x2000,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = TV,
.gpio = 0x8000,
},
.gpiomask = 0xe000,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = LINE2,
.gpio = 0x0000,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE2,
.gpio = 0x4000,
},{
- .name = name_comp2,
+ .type = SAA7134_INPUT_COMPOSITE2,
.vmux = 3,
.amux = LINE2,
.gpio = 0x4000,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
.gpio = 0x4000,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
.gpio = 0x2000,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = LINE2,
.gpio = 0x8000,
},
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1, /* Composite signal on S-Video input */
+ .type = SAA7134_INPUT_COMPOSITE_OVER_SVIDEO,
.vmux = 0,
.amux = LINE2,
},{
- .name = name_comp2, /* Composite input */
+ .type = SAA7134_INPUT_COMPOSITE,
.vmux = 3,
.amux = LINE2,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
}},
.gpiomask = 0x1E000, /* Set GP16 and unused 15,14,13 to Output */
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
.gpio = 0x10000, /* GP16=1 selects TV input */
- .tv = 1,
},{
-/* .name = name_tv_mono,
+/* .type = SAA7134_INPUT_TV_MONO,
.vmux = 1,
.amux = LINE2,
.gpio = 0x0000,
- .tv = 1,
},{
-*/ .name = name_comp1, /* Composite signal on S-Video input */
+*/ .type = SAA7134_INPUT_COMPOSITE_OVER_SVIDEO,
.vmux = 0,
.amux = LINE2,
/* .gpio = 0x4000, */
},{
- .name = name_comp2, /* Composite input */
+ .type = SAA7134_INPUT_COMPOSITE,
.vmux = 3,
.amux = LINE2,
/* .gpio = 0x4000, */
},{
- .name = name_svideo, /* S-Video signal on S-Video input */
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
/* .gpio = 0x4000, */
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
.gpio = 0x00000, /* GP16=0 selects FM radio antenna */
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = TV,
.gpio = 0x10000,
},
.tda9887_conf = TDA9887_PRESENT,
.gpiomask = 0xe000,
.inputs = { {
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
.gpio = 0x8000,
- .tv = 1,
}, {
- .name = name_tv_mono,
+ .type = SAA7134_INPUT_TV_MONO,
.vmux = 1,
.amux = LINE2,
.gpio = 0x0000,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE2,
.gpio = 0x4000,
}, {
- .name = name_comp2,
+ .type = SAA7134_INPUT_COMPOSITE2,
.vmux = 3,
.amux = LINE2,
.gpio = 0x4000,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
.gpio = 0x4000,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
.gpio = 0x2000,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = TV,
.gpio = 0x8000,
},
.empress_addr = 0x20,
.inputs = {{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
},{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = LINE2,
- .tv = 1,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
.mpeg = SAA7134_MPEG_EMPRESS,
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
},
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
/* workaround for problems with normal TV sound */
- .name = name_tv_mono,
+ .type = SAA7134_INPUT_TV_MONO,
.vmux = 1,
.amux = LINE2,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE1,
},{
- .name = name_comp2,
+ .type = SAA7134_INPUT_COMPOSITE2,
.vmux = 3,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = TV,
},
},
.radio_addr = ADDR_UNSET,
.tda9887_conf = TDA9887_PRESENT,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_tv_mono,
+ .type = SAA7134_INPUT_TV_MONO,
.vmux = 1,
.amux = LINE2,
- .tv = 1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
},{
- .name = "CVid over SVid",
+ .type = SAA7134_INPUT_COMPOSITE_OVER_SVIDEO,
.vmux = 0,
.amux = LINE1,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
},
.tda9887_conf = TDA9887_PRESENT,
.gpiomask = 0x820000,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
.gpio = 0x20000,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
.gpio = 0x20000,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
.gpio = 0x20000,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
.gpio = 0x20000,
},
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE,
.vmux = 4,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
},{
- .name = name_comp2, /* CVideo over SVideo Connector */
+ .type = SAA7134_INPUT_COMPOSITE_OVER_SVIDEO,
.vmux = 0,
.amux = LINE1,
}}
.radio_addr = ADDR_UNSET,
.tda9887_conf = TDA9887_PRESENT,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
/* workaround for problems with normal TV sound */
- .name = name_tv_mono,
+ .type = SAA7134_INPUT_TV_MONO,
.vmux = 1,
.amux = LINE2,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE2,
},{
- .name = name_comp2,
+ .type = SAA7134_INPUT_COMPOSITE2,
.vmux = 3,
.amux = LINE2,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
},
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
},{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = LINE2,
- .tv = 1,
}},
},
[SAA7134_BOARD_CINERGY600] = {
.radio_addr = ADDR_UNSET,
.tda9887_conf = TDA9887_PRESENT,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 4,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
},{
- .name = name_comp2, /* CVideo over SVideo Connector */
+ .type = SAA7134_INPUT_COMPOSITE_OVER_SVIDEO,
.vmux = 0,
.amux = LINE1,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
},
.tda9887_conf = TDA9887_PRESENT,
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = TV,
},
},
.radio_addr = ADDR_UNSET,
.tda9887_conf = TDA9887_PRESENT,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
},
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE1,
},{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 4,
.amux = LINE2,
- .tv = 1,
}},
},
[SAA7134_BOARD_ELSA_500TV] = {
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 7,
.amux = LINE1,
},{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 8,
.amux = TV,
- .tv = 1,
},{
- .name = name_tv_mono,
+ .type = SAA7134_INPUT_TV_MONO,
.vmux = 8,
.amux = LINE2,
- .tv = 1,
}},
},
[SAA7134_BOARD_ELSA_700TV] = {
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 4,
.amux = LINE2,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 6,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 7,
.amux = LINE1,
}},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = TV,
},
},
.radio_addr = ADDR_UNSET,
.tda9887_conf = TDA9887_PRESENT,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 4,
.amux = LINE2,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 6,
.amux = LINE2,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE1,
},
},
.radio_addr = ADDR_UNSET,
.gpiomask = 0x200000,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
.gpio = 0x0000,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 4,
.amux = LINE2,
.gpio = 0x0000,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 6,
.amux = LINE2,
.gpio = 0x0000,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
.gpio = 0x200000,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.gpio = 0x0000,
},
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
},{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = LINE2,
- .tv = 1,
}},
},
[SAA7134_BOARD_10MOONSTVMASTER] = {
.radio_addr = ADDR_UNSET,
.gpiomask = 0xe000,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = LINE2,
.gpio = 0x0000,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE2,
.gpio = 0x4000,
},{
- .name = name_comp2,
+ .type = SAA7134_INPUT_COMPOSITE2,
.vmux = 3,
.amux = LINE2,
.gpio = 0x4000,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
.gpio = 0x4000,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
.gpio = 0x2000,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = LINE2,
.gpio = 0x8000,
},
.radio_addr = ADDR_UNSET,
.empress_addr = 0x20,
.inputs = {{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 4,
.amux = LINE1,
},{
- .name = name_comp2,
+ .type = SAA7134_INPUT_COMPOSITE2,
.vmux = 3,
.amux = LINE1,
},{
- .name = name_comp3,
+ .type = SAA7134_INPUT_COMPOSITE3,
.vmux = 0,
.amux = LINE1,
},{
- .name = name_comp4,
+ .type = SAA7134_INPUT_COMPOSITE4,
.vmux = 1,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
}},
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
},{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = LINE2,
- .tv = 1,
}},
},
[SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUS] = {
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
.gpio = 0x06c00012,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
.gpio = 0x0ac20012,
},{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = LINE2,
.gpio = 0x08c20012,
- .tv = 1,
}}, /* radio and probably mute is missing */
},
[SAA7134_BOARD_CRONOS_PLUS] = {
.radio_addr = ADDR_UNSET,
.gpiomask = 0xcf00,
.inputs = {{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.gpio = 2 << 14,
},{
- .name = name_comp2,
+ .type = SAA7134_INPUT_COMPOSITE2,
.vmux = 0,
.gpio = 1 << 14,
},{
- .name = name_comp3,
+ .type = SAA7134_INPUT_COMPOSITE3,
.vmux = 0,
.gpio = 0 << 14,
},{
- .name = name_comp4,
+ .type = SAA7134_INPUT_COMPOSITE4,
.vmux = 0,
.gpio = 3 << 14,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.gpio = 2 << 14,
}},
.tda9887_conf = TDA9887_PRESENT,
.gpiomask = 0x03,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
.gpio = 0x00,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
.gpio = 0x02,
}, {
- .name = name_comp2,
+ .type = SAA7134_INPUT_COMPOSITE2,
.vmux = 0,
.amux = LINE1,
.gpio = 0x02,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
.gpio = 0x02,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE1,
.gpio = 0x01,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = TV,
.gpio = 0x00,
},
.radio_addr = ADDR_UNSET,
.empress_addr = 0x20,
.inputs = {{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
},{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = TV,
- .tv = 1,
}},
.mpeg = SAA7134_MPEG_EMPRESS,
.video_out = CCIR656,
.radio_addr = ADDR_UNSET,
.tda9887_conf = TDA9887_PRESENT,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 4,
.amux = LINE2,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 6,
.amux = LINE2,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE1,
},
},
.radio_addr = ADDR_UNSET,
.tda9887_conf = TDA9887_PRESENT | TDA9887_INTERCARRIER | TDA9887_PORT2_INACTIVE,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE2,
},{
- .name = name_comp2,
+ .type = SAA7134_INPUT_COMPOSITE2,
.vmux = 1,
.amux = LINE2,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
}},
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
},{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = LINE2,
- .tv = 1,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
},
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
},{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = LINE2,
- .tv = 1,
}},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = LINE1,
},
},
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = LINE2,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE2,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
}},
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_tv_mono,
+ .type = SAA7134_INPUT_TV_MONO,
.vmux = 1,
.amux = LINE2,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
},{
- .name = "CVid over SVid",
+ .type = SAA7134_INPUT_COMPOSITE_OVER_SVIDEO,
.vmux = 0,
.amux = LINE1,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
},
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_tv_mono,
+ .type = SAA7134_INPUT_TV_MONO,
.vmux = 1,
.amux = LINE2,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
},{
- .name = "CVid over SVid",
+ .type = SAA7134_INPUT_COMPOSITE_OVER_SVIDEO,
.vmux = 0,
.amux = LINE1,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
},
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_tv_mono,
+ .type = SAA7134_INPUT_TV_MONO,
.vmux = 1,
.amux = LINE2,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
},{
- .name = "CVid over SVid",
+ .type = SAA7134_INPUT_COMPOSITE_OVER_SVIDEO,
.vmux = 0,
.amux = LINE1,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
},
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_tv_mono,
+ .type = SAA7134_INPUT_TV_MONO,
.vmux = 1,
.amux = LINE2,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE2,
},{
- .name = name_comp2,
+ .type = SAA7134_INPUT_COMPOSITE2,
.vmux = 3,
.amux = LINE2,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
.gpio = 0x200000,
},
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
}},
},
.radio_addr = ADDR_UNSET,
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
} },
},
[SAA7134_BOARD_NOVAC_PRIMETV7133] = {
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
},{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
}},
},
.radio_addr = ADDR_UNSET,
.tda9887_conf = TDA9887_PRESENT,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = LINE2,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE2,
},{
- .name = name_comp2,
+ .type = SAA7134_INPUT_COMPOSITE2,
.vmux = 3,
.amux = LINE2,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = LINE1,
},
},
.radio_addr = ADDR_UNSET,
.tda9887_conf = TDA9887_PRESENT,
.inputs = { {
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = LINE2,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE2,
}, {
- .name = name_comp2,
+ .type = SAA7134_INPUT_COMPOSITE2,
.vmux = 3,
.amux = LINE2,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = LINE1,
},
},
.radio_addr = ADDR_UNSET,
.tda9887_conf = TDA9887_PRESENT,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 7,
.amux = TV,
- .tv = 1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 7,
.amux = LINE1,
}},
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
},
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = LINE2,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 4,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
},{
- .name = name_comp2, /* CVideo over SVideo Connector */
+ .type = SAA7134_INPUT_COMPOSITE_OVER_SVIDEO,
.vmux = 0,
.amux = LINE1,
}},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = LINE2,
},
},
.radio_addr = ADDR_UNSET,
.gpiomask = 0x808c0080,
.inputs = {{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
.gpio = 0x00080,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
.gpio = 0x00080,
},{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = LINE2_LEFT,
- .tv = 1,
.gpio = 0x00080,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
.gpio = 0x80000,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = LINE2,
.gpio = 0x40000,
},
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
},{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = LINE2,
- .tv = 1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
},
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
},{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = LINE2,
- .tv = 1,
}},
},
[SAA7134_BOARD_EMPIRE_PCI_TV_RADIO_LE] = {
.radio_addr = ADDR_UNSET,
.gpiomask = 0x4000,
.inputs = {{
- .name = name_tv_mono,
+ .type = SAA7134_INPUT_TV_MONO,
.vmux = 1,
.amux = LINE2,
.gpio = 0x8000,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
.gpio = 0x8000,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 6,
.amux = LINE1,
.gpio = 0x8000,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE1,
.gpio = 0x8000,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = TV,
.gpio =0x8000,
}
.tda9887_conf = TDA9887_PRESENT,
.gpiomask = 0x03,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
.gpio = 0x00,
},{
- .name = name_comp,
+ .type = SAA7134_INPUT_COMPOSITE,
.vmux = 3,
.amux = LINE1,
.gpio = 0x02,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
.gpio = 0x02,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE1,
.gpio = 0x01,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = LINE1,
.gpio = 0x00,
},
.gpiomask = 0x00300003,
/* .gpiomask = 0x8c240003, */
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
.gpio = 0x01,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE1,
.gpio = 0x02,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 6,
.amux = LINE1,
.gpio = 0x02,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
.gpio = 0x00300001,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = TV,
.gpio = 0x01,
},
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE2,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE1,
},
},
.tda9887_conf = TDA9887_PRESENT,
.gpiomask = 0x08000000,
.inputs = { {
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
.gpio = 0x08000000,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
.gpio = 0x08000000,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
.gpio = 0x08000000,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
.gpio = 0x00000000,
},
.radio_addr = ADDR_UNSET,
.tda9887_conf = TDA9887_PRESENT,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_tv_mono,
+ .type = SAA7134_INPUT_TV_MONO,
.vmux = 1,
.amux = LINE2,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
}},
.rds_addr = 0x10,
.tda9887_conf = TDA9887_PRESENT,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 4,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
},{
- .name = name_comp2, /* CVideo over SVideo Connector */
+ .type = SAA7134_INPUT_COMPOSITE_OVER_SVIDEO,
.vmux = 0,
.amux = LINE1,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
},
.radio_addr = ADDR_UNSET,
.gpiomask = 0x1ce780,
.inputs = {{
- .name = name_svideo,
- .vmux = 0, /* CVideo over SVideo Connector - ok? */
+ .type = SAA7134_INPUT_COMPOSITE_OVER_SVIDEO,
+ .vmux = 0,
.amux = LINE1,
.gpio = 0x008080,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
.gpio = 0x008080,
},{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
.gpio = 0x008080,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
.gpio = 0x80000,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = LINE2,
.gpio = 0x0c8000,
},
.tda9887_conf = TDA9887_PRESENT | TDA9887_INTERCARRIER | TDA9887_PORT2_INACTIVE,
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE2,
},{
- .name = name_comp2,
+ .type = SAA7134_INPUT_COMPOSITE2,
.vmux = 1,
.amux = LINE2,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
}},
.radio_addr = ADDR_UNSET,
.tda9887_conf = TDA9887_PRESENT,
.inputs = {{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE1,
},{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_tv_mono,
+ .type = SAA7134_INPUT_TV_MONO,
.vmux = 1,
.amux = LINE2,
- .tv = 1,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
},
.radio_addr = ADDR_UNSET,
.tda9887_conf = TDA9887_PRESENT,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = LINE2,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE2,
},{
- .name = name_comp2,
+ .type = SAA7134_INPUT_COMPOSITE2,
.vmux = 3,
.amux = LINE2,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
}},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = LINE1,
},
},
.gpiomask = 0x00200000,
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
.gpio = 0x200000, /* GPIO21=High for TV input */
- .tv = 1,
},{
- .name = name_comp1, /* Composite signal on S-Video input */
+ .type = SAA7134_INPUT_COMPOSITE_OVER_SVIDEO,
.vmux = 0,
.amux = LINE2,
},{
- .name = name_comp2, /* Composite input */
+ .type = SAA7134_INPUT_COMPOSITE,
.vmux = 3,
.amux = LINE2,
},{
- .name = name_svideo, /* S-Video signal on S-Video input */
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
.gpio = 0x000000, /* GPIO21=Low for FM radio antenna */
},
.radio_addr = ADDR_UNSET,
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
}},
.radio_addr = ADDR_UNSET,
.tda9887_conf = TDA9887_PRESENT,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE1,
},{
- .name = name_comp2,
+ .type = SAA7134_INPUT_COMPOSITE2,
.vmux = 3,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
}},
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE2,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
}},
.radio_addr = ADDR_UNSET,
.gpiomask = 0x0700,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
.gpio = 0x000,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
.gpio = 0x200, /* gpio by DScaler */
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 0,
.amux = LINE1,
.gpio = 0x200,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE1,
.gpio = 0x100,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = TV,
.gpio = 0x000,
},
.mpeg = SAA7134_MPEG_DVB,
.gpiomask = 0x00200000,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
.gpio = 0x200000, /* GPIO21=High for TV input */
- .tv = 1,
},{
- .name = name_svideo, /* S-Video signal on S-Video input */
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
},{
- .name = name_comp1, /* Composite signal on S-Video input */
+ .type = SAA7134_INPUT_COMPOSITE_OVER_SVIDEO,
.vmux = 0,
.amux = LINE2,
},{
- .name = name_comp2, /* Composite input */
+ .type = SAA7134_INPUT_COMPOSITE2,
.vmux = 3,
.amux = LINE2,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
.gpio = 0x000000, /* GPIO21=Low for FM radio antenna */
},
.radio_addr = 0x60,
.gpiomask = 0x8c1880,
.inputs = {{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 0,
.amux = LINE1,
.gpio = 0x800800,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
.gpio = 0x801000,
},{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
.gpio = 0x800000,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
.gpio = 0x880000,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = LINE2,
.gpio = 0x840000,
},
.radio_addr = 0x60,
.gpiomask = 0x0700,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
.gpio = 0x000,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
.gpio = 0x200, /* gpio by DScaler */
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 0,
.amux = LINE1,
.gpio = 0x200,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE1,
.gpio = 0x100,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = TV,
.gpio = 0x000,
},
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_tv_mono,
+ .type = SAA7134_INPUT_TV_MONO,
.vmux = 1,
.amux = LINE2,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE2,
},{
- .name = name_comp2,
+ .type = SAA7134_INPUT_COMPOSITE2,
.vmux = 3,
.amux = LINE2,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
}},
.radio = {
- .name = name_radio, /* radio unconfirmed */
+ .type = SAA7134_INPUT_RADIO, /* radio unconfirmed */
.amux = LINE2,
},
},
.radio_addr = ADDR_UNSET,
.gpiomask = 1 << 21,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
.gpio = 0x0000000,
- .tv = 1,
},{
- .name = name_comp1, /* Composite input */
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE2,
.gpio = 0x0000000,
},{
- .name = name_svideo, /* S-Video input */
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
.gpio = 0x0000000,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
.gpio = 0x0200000,
},
.radio_addr= ADDR_UNSET,
.gpiomask = 0x00010003,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
.gpio = 0x01,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE2,
.gpio = 0x02,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 6,
.amux = LINE2,
.gpio = 0x02,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE1,
.gpio = 0x00010003,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = TV,
.gpio = 0x01,
},
.tda9887_conf = TDA9887_PRESENT,
.gpiomask = 0x00008000,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
},
.tda9887_conf = TDA9887_PRESENT,
.gpiomask = 0x00200003,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
.gpio = 0x00200003,
},{
- .name = name_tv_mono,
+ .type = SAA7134_INPUT_TV_MONO,
.vmux = 1,
.amux = LINE2,
.gpio = 0x00200003,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
.gpio = 0x00200003,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
.gpio = 0x00200003,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
.gpio = 0x00200003,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = TV,
.gpio = 0x00200003,
},
.tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE,
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE2,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
}},
.tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE,
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE2,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
}},
.radio_addr = ADDR_UNSET,
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
}},
.radio_addr = ADDR_UNSET,
.empress_addr = 0x21,
.inputs = {{
- .name = "Composite 0",
+ .type = SAA7134_INPUT_COMPOSITE0,
.vmux = 0,
.amux = LINE1,
},{
- .name = "Composite 1",
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE2,
},{
- .name = "Composite 2",
+ .type = SAA7134_INPUT_COMPOSITE2,
.vmux = 2,
.amux = LINE1,
},{
- .name = "Composite 3",
+ .type = SAA7134_INPUT_COMPOSITE3,
.vmux = 3,
.amux = LINE2,
},{
- .name = "S-Video 0",
+ .type = SAA7134_INPUT_SVIDEO0,
+
.vmux = 8,
.amux = LINE1,
},{
- .name = "S-Video 1",
+ .type = SAA7134_INPUT_SVIDEO1,
.vmux = 9,
.amux = LINE2,
}},
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = "Composite 0",
+ .type = SAA7134_INPUT_COMPOSITE0,
.vmux = 0,
.amux = LINE1,
},{
- .name = "Composite 1",
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE2,
},{
- .name = "Composite 2",
+ .type = SAA7134_INPUT_COMPOSITE2,
.vmux = 2,
.amux = LINE1,
},{
- .name = "Composite 3",
+ .type = SAA7134_INPUT_COMPOSITE3,
.vmux = 3,
.amux = LINE2,
},{
- .name = "S-Video 0",
+ .type = SAA7134_INPUT_SVIDEO0,
.vmux = 8,
.amux = LINE1,
},{
- .name = "S-Video 1",
+ .type = SAA7134_INPUT_SVIDEO1,
.vmux = 9,
.amux = LINE2,
}},
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1, /* Composite signal on S-Video input */
+ .type = SAA7134_INPUT_COMPOSITE_OVER_SVIDEO,
.vmux = 0,
.amux = LINE2,
},{
- .name = name_comp2, /* Composite input */
+ .type = SAA7134_INPUT_COMPOSITE,
.vmux = 3,
.amux = LINE2,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
}},
.radio_addr = ADDR_UNSET,
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE2,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
}},
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 6,
.amux = LINE1,
}},
.radio_addr = ADDR_UNSET,
.gpiomask = 0x080200000,
.inputs = { {
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 4,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE2,
}, {
- .name = name_comp2,
+ .type = SAA7134_INPUT_COMPOSITE2,
.vmux = 0,
.amux = LINE2,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
.gpio = 0x0200000,
},
.gpiomask = 1 << 21,
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
.gpio = 0x0000000,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE2,
.gpio = 0x0200000,
},{
- .name = name_comp2,
+ .type = SAA7134_INPUT_COMPOSITE2,
.vmux = 0,
.amux = LINE2,
.gpio = 0x0200000,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
.gpio = 0x0200000,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
.gpio = 0x0200000,
},
.radio_addr = ADDR_UNSET,
.gpiomask = 0xe880c0,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 6,
.amux = LINE1,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
},
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
}},
.mpeg = SAA7134_MPEG_DVB,
.gpiomask = 0x0200000,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
.gpio = 0x0200000,
},
.radio_addr = ADDR_UNSET,
.gpiomask = 1 << 21,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE,
.vmux = 3,
.amux = LINE2, /* unconfirmed, taken from Philips driver */
},{
- .name = name_comp2,
- .vmux = 0, /* untested, Composite over S-Video */
+ .type = SAA7134_INPUT_COMPOSITE_OVER_SVIDEO,
+ .vmux = 0, /* untested */
.amux = LINE2,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
.gpio = 0x0200000,
},
.radio_addr = ADDR_UNSET,
.gpiomask = 0x80200000,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_svideo, /* NOT tested */
+ .type = SAA7134_INPUT_SVIDEO, /* NOT tested */
.vmux = 8,
.amux = LINE1,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
.gpio = 0x0200000,
},
.gpiomask = 0x00200000,
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
- .name = name_tv, /* Analog broadcast/cable TV */
+ .type = SAA7134_INPUT_TV, /* Analog broadcast/cable TV */
.vmux = 1,
.amux = TV,
.gpio = 0x200000, /* GPIO21=High for TV input */
- .tv = 1,
},{
- .name = name_svideo, /* S-Video signal on S-Video input */
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
},{
- .name = name_comp1, /* Composite signal on S-Video input */
+ .type = SAA7134_INPUT_COMPOSITE_OVER_SVIDEO,
.vmux = 0,
.amux = LINE2,
},{
- .name = name_comp2, /* Composite input */
+ .type = SAA7134_INPUT_COMPOSITE2,
.vmux = 3,
.amux = LINE2,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
.gpio = 0x000000, /* GPIO21=Low for FM radio antenna */
},
.radio_addr = ADDR_UNSET,
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
}},
.radio_addr = ADDR_UNSET,
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
- .name = name_comp1, /* Composite input */
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE2,
},{
- .name = name_svideo, /* S-Video signal on S-Video input */
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
}},
.mpeg = SAA7134_MPEG_DVB,
.gpiomask = 0x00600000, /* Bit 21 0=Radio, Bit 22 0=TV */
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
.gpio = 0x00200000,
}},
},
.mpeg = SAA7134_MPEG_DVB,
.gpiomask = 1 << 21,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
},{
- .name = name_comp2,
+ .type = SAA7134_INPUT_COMPOSITE2,
.vmux = 0,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
.gpio = 0x0200000,
},
.mpeg = SAA7134_MPEG_DVB,
.gpiomask = 1 << 21,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
.gpio = 0x0200000,
},
.tda9887_conf = TDA9887_PRESENT,
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE2,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
}},
.tda9887_conf = TDA9887_PRESENT,
.gpiomask = 0xca60000,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 4,
.amux = TV,
- .tv = 1,
.gpio = 0x04a61000,
},{
- .name = name_comp2, /* Composite SVIDEO (B/W if signal is carried with SVIDEO) */
+ .type = SAA7134_INPUT_COMPOSITE_OVER_SVIDEO,
.vmux = 1,
.amux = LINE2,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 9, /* 9 is correct as S-VIDEO1 according to a169.inf! */
.amux = LINE1,
}},
.mpeg = SAA7134_MPEG_DVB,
.gpiomask = 0x00600000, /* Bit 21 0=Radio, Bit 22 0=TV */
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
.gpio = 0x200000, /* GPIO21=High for TV input */
- .tv = 1,
},{
- .name = name_svideo, /* S-Video signal on S-Video input */
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
},{
- .name = name_comp1, /* Composite signal on S-Video input */
+ .type = SAA7134_INPUT_COMPOSITE_OVER_SVIDEO,
.vmux = 0,
.amux = LINE2,
},{
- .name = name_comp2, /* Composite input */
+ .type = SAA7134_INPUT_COMPOSITE,
.vmux = 3,
.amux = LINE2,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
.gpio = 0x000000, /* GPIO21=Low for FM radio antenna */
},
.gpiomask = 0xe000,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
.gpio = 0x8000,
- .tv = 1,
},{
- .name = name_tv_mono,
+ .type = SAA7134_INPUT_TV_MONO,
.vmux = 1,
.amux = LINE2,
.gpio = 0x0000,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE2,
.gpio = 0x4000,
},{
- .name = name_comp2,
+ .type = SAA7134_INPUT_COMPOSITE2,
.vmux = 3,
.amux = LINE2,
.gpio = 0x4000,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
.gpio = 0x4000,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
.gpio = 0x2000,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = TV,
.gpio = 0x8000,
},
.radio_addr = ADDR_UNSET,
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
}},
.radio_addr = ADDR_UNSET,
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
- .name = name_comp1, /* Composite input */
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
},{
- .name = name_svideo, /* S-Video signal on S-Video input */
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
}},
.radio_addr = ADDR_UNSET,
.tda9887_conf = TDA9887_PRESENT,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = LINE2,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE2,
},{
- .name = name_comp2,
+ .type = SAA7134_INPUT_COMPOSITE2,
.vmux = 3,
.amux = LINE2,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
}},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = LINE1,
},
},
.tda9887_conf = TDA9887_PRESENT,
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE2,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE1,
},
},
.tda9887_conf = TDA9887_PRESENT| TDA9887_PORT1_ACTIVE | TDA9887_PORT2_ACTIVE,
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 4,
.amux = LINE2,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE1,
},
},
.mpeg = SAA7134_MPEG_DVB,
.gpiomask = 0x000200000,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 4,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE2,
},{
- .name = name_comp2,
+ .type = SAA7134_INPUT_COMPOSITE2,
.vmux = 0,
.amux = LINE2,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
.gpio = 0x0200000,
},
.tda9887_conf = TDA9887_PRESENT,
.gpiomask = 0x03,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
.gpio = 0x00,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE2,
.gpio = 0x00,
},{
- .name = name_comp2,
+ .type = SAA7134_INPUT_COMPOSITE2,
.vmux = 3,
.amux = LINE2,
.gpio = 0x00,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
.gpio = 0x00,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
.gpio = 0x01,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = LINE1,
.gpio = 0x00,
},
.tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE,
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE2,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
}},
.mpeg = SAA7134_MPEG_DVB,
.gpiomask = 0x0200100,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
.gpio = 0x0000100,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
.gpio = 0x0200100,
},
.ts_force_val = 1,
.gpiomask = 0x0800100, /* GPIO 21 is an INPUT */
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
.gpio = 0x0000100,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
.gpio = 0x0800100, /* GPIO 23 HI for FM */
},
.ts_type = SAA7134_MPEG_TS_SERIAL,
.gpiomask = 0x0800100, /* GPIO 21 is an INPUT */
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
.gpio = 0x0000100,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
.gpio = 0x0800100, /* GPIO 23 HI for FM */
},
.radio_addr = ADDR_UNSET,
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 6,
.amux = LINE1,
}},
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = 3,
- .tv = 1,
},{
- .name = name_tv_mono,
+ .type = SAA7134_INPUT_TV_MONO,
.vmux = 7,
.amux = 4,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = 2,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 0,
.amux = 2,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
/* .gpio = 0x00300001,*/
.gpio = 0x20000,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = 0,
},
},
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = 3,
- .tv = 1,
},{
- .name = name_tv_mono,
+ .type = SAA7134_INPUT_TV_MONO,
.vmux = 7,
.amux = 4,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = 2,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 0,
.amux = 2,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
.gpio = 0x20000,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = 0,
},
},
.radio_addr = ADDR_UNSET,
.gpiomask = 0x7000,
.inputs = { {
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = 1,
- .tv = 1,
.gpio = 0x50000,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = 2,
.gpio = 0x2000,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = 2,
.gpio = 0x2000,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.vmux = 1,
.amux = 1,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.gpio = 0xf000,
.amux = 0,
},
.tuner_addr = 0x61,
.radio_addr = 0x60,
.inputs = { {
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = LINE2,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.vmux = 1,
.amux = LINE1,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = LINE1,
.gpio = 0x43000,
},
.radio_addr = ADDR_UNSET,
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 6,
.amux = LINE1,
}},
.mpeg = SAA7134_MPEG_DVB,
.gpiomask = 0x0200000,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
.gpio = 0x0200000,
},
.radio_addr = ADDR_UNSET,
.gpiomask = 1<<21,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE2,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 6,
.amux = LINE2,
}},
.mpeg = SAA7134_MPEG_DVB,
.gpiomask = 0x0200000,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
.gpio = 0x0200000,
}},
},
.gpiomask = 1 << 21,
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
.gpio = 0x0000000,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE2,
.gpio = 0x0200000,
},{
- .name = name_comp2,
+ .type = SAA7134_INPUT_COMPOSITE2,
.vmux = 0,
.amux = LINE2,
.gpio = 0x0200000,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
.gpio = 0x0200000,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
.gpio = 0x0200000,
},
.radio_addr = ADDR_UNSET,
.gpiomask = 1 << 21,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
.gpio = 0x0000000,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE2,
}, {
- .name = name_comp2,
+ .type = SAA7134_INPUT_COMPOSITE2,
.vmux = 0,
.amux = LINE2,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
.gpio = 0x0200000,
},
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
},{
- .name = name_comp2,
+ .type = SAA7134_INPUT_COMPOSITE2,
.vmux = 0,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
}},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = TV,
},
},
.radio_addr = ADDR_UNSET,
.gpiomask = 0x7000,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = LINE2,
.gpio = 0x0000,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
.gpio = 0x2000,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
.gpio = 0x2000,
}},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = LINE2,
.gpio = 0x3000,
},
.tda829x_conf = { .lna_cfg = TDA8290_LNA_OFF },
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
- .name = name_tv, /* FIXME: analog tv untested */
+ .type = SAA7134_INPUT_TV, /* FIXME: analog tv untested */
.vmux = 1,
.amux = TV,
- .tv = 1,
}},
},
[SAA7134_BOARD_AVERMEDIA_M135A] = {
.tda829x_conf = { .lna_cfg = TDA8290_LNA_GP0_HIGH_OFF },
.gpiomask = 0x020200000,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
.gpio = 0x00200000,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = TV,
.gpio = 0x01,
},
.tda829x_conf = { .lna_cfg = TDA8290_LNA_OFF },
.gpiomask = 0x020200000,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
.gpio = 0x00200000,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = TV,
.gpio = 0x01,
},
.radio_addr = ADDR_UNSET,
.gpiomask = 0x00008000,
.inputs = {{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
},{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = LINE2,
- .tv = 1,
}},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = LINE1,
},
},
.radio_addr = ADDR_UNSET,
.gpiomask = 0x00008000,
.inputs = {{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
},{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = LINE2,
- .tv = 1,
}},
},
[SAA7134_BOARD_BEHOLD_403FM] = {
.radio_addr = ADDR_UNSET,
.gpiomask = 0x00008000,
.inputs = {{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
},{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = LINE2,
- .tv = 1,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
},
.tda9887_conf = TDA9887_PRESENT,
.gpiomask = 0x00008000,
.inputs = {{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
},{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = LINE2,
- .tv = 1,
}},
},
[SAA7134_BOARD_BEHOLD_405FM] = {
.tda9887_conf = TDA9887_PRESENT,
.gpiomask = 0x00008000,
.inputs = {{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
},{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = LINE2,
- .tv = 1,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
},
.tda9887_conf = TDA9887_PRESENT,
.gpiomask = 0x00008000,
.inputs = {{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
.gpio = 0xc0c000,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
.gpio = 0xc0c000,
},{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = TV,
- .tv = 1,
.gpio = 0xc0c000,
}},
},
.tda9887_conf = TDA9887_PRESENT,
.gpiomask = 0x00008000,
.inputs = {{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
.gpio = 0xc0c000,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
.gpio = 0xc0c000,
},{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = TV,
- .tv = 1,
.gpio = 0xc0c000,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
.gpio = 0xc0c000,
},
.tda9887_conf = TDA9887_PRESENT,
.gpiomask = 0x00008000,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
}},
.tda9887_conf = TDA9887_PRESENT,
.gpiomask = 0x00008000,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = LINE2,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
} },
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = LINE1,
},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
},
.tda9887_conf = TDA9887_PRESENT,
.gpiomask = 0x00008000,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = LINE2,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
}},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = LINE1,
},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
},
.tda9887_conf = TDA9887_PRESENT,
.gpiomask = 0x00008000,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
},
.tda9887_conf = TDA9887_PRESENT,
.gpiomask = 0x00008000,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
},
.tda9887_conf = TDA9887_PRESENT,
.gpiomask = 0x00008000,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
},
.tda9887_conf = TDA9887_PRESENT,
.gpiomask = 0x000A8004,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = TV,
- .tv = 1,
.gpio = 0x000A8004,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
.gpio = 0x000A8000,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
.gpio = 0x000A8000,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
.gpio = 0x000A8000,
},
.radio_addr = ADDR_UNSET,
.tda9887_conf = TDA9887_PRESENT,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
},
.radio_addr = ADDR_UNSET,
.tda9887_conf = TDA9887_PRESENT,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
},
.radio_addr = ADDR_UNSET,
.tda9887_conf = TDA9887_PRESENT,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
},
.radio_addr = ADDR_UNSET,
.tda9887_conf = TDA9887_PRESENT,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
},
.rds_addr = 0x10,
.tda9887_conf = TDA9887_PRESENT,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
},
.rds_addr = 0x10,
.tda9887_conf = TDA9887_PRESENT,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
},
.rds_addr = 0x10,
.tda9887_conf = TDA9887_PRESENT,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
},
.rds_addr = 0x10,
.tda9887_conf = TDA9887_PRESENT,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = TV,
- .tv = 1,
},{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
},{
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
}},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
},
.empress_addr = 0x20,
.tda9887_conf = TDA9887_PRESENT,
.inputs = { {
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
.mpeg = SAA7134_MPEG_EMPRESS,
.empress_addr = 0x20,
.tda9887_conf = TDA9887_PRESENT,
.inputs = { {
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
.mpeg = SAA7134_MPEG_EMPRESS,
.empress_addr = 0x20,
.tda9887_conf = TDA9887_PRESENT,
.inputs = { {
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
.mpeg = SAA7134_MPEG_EMPRESS,
.mpeg = SAA7134_MPEG_DVB,
.gpiomask = 0x0200000,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8, /* untested */
.amux = LINE1,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
.gpio = 0x0200000,
},
.radio_addr = ADDR_UNSET,
.gpiomask = 0xf000,
.inputs = {{
- .name = name_tv_mono,
+ .type = SAA7134_INPUT_TV_MONO,
.vmux = 1,
.amux = LINE2,
.gpio = 0x0000,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
.gpio = 0x2000,
- .tv = 1
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
.gpio = 0x2000,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
.gpio = 0x1000,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = LINE2,
.gpio = 0x6000,
},
.radio_addr = ADDR_UNSET,
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
} },
.tda829x_conf = { .lna_cfg = TDA8290_LNA_OFF },
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
} },
.mpeg = SAA7134_MPEG_DVB,
.gpiomask = 0x0200000,
.inputs = { {
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
.gpio = 0x0200000,
},
.radio_addr = ADDR_UNSET,
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
},
},
.radio_addr = ADDR_UNSET,
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
}, {
- .name = name_comp,
+ .type = SAA7134_INPUT_COMPOSITE,
.vmux = 0,
.amux = LINE1,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
},
},
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
} },
.radio_addr = ADDR_UNSET,
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE2,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
}
},
.radio_addr = ADDR_UNSET,
.mpeg = SAA7134_MPEG_DVB,
.inputs = { {
- .name = name_comp,
+ .type = SAA7134_INPUT_COMPOSITE,
.vmux = 1,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 6,
.amux = LINE1,
} },
.radio_addr = ADDR_UNSET,
.mpeg = SAA7134_MPEG_DVB,
.inputs = { {
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 4,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp,
+ .type = SAA7134_INPUT_COMPOSITE,
.vmux = 1,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 6,
.amux = LINE1,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
},
},
.tda9887_conf = TDA9887_PRESENT,
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
},
.gpiomask = 1 << 21,
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp,
+ .type = SAA7134_INPUT_COMPOSITE,
.vmux = 0,
.amux = LINE2,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
.gpio = 0x0200000,
},
.gpiomask = 1 << 21,
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp,
+ .type = SAA7134_INPUT_COMPOSITE,
.vmux = 0,
.amux = LINE2,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
.gpio = 0x0200000,
},
.radio_addr = ADDR_UNSET,
.gpiomask = 0x801a8087,
.inputs = { {
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = LINE2,
- .tv = 1,
.gpio = 0x624000,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
.gpio = 0x624000,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 1,
.amux = LINE1,
.gpio = 0x624000,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
.gpio = 0x624001,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = TV,
},
},
.tda9887_conf = TDA9887_PRESENT,
.mpeg = SAA7134_MPEG_DVB,
.inputs = { {
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp,
+ .type = SAA7134_INPUT_COMPOSITE,
.vmux = 4,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
} },
.mpeg = SAA7134_MPEG_DVB,
.gpiomask = 0x0200000,
.inputs = { {
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE2,
}, {
- .name = name_comp2,
+ .type = SAA7134_INPUT_COMPOSITE2,
.vmux = 0,
.amux = LINE2,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
.gpio = 0x0200000,
},
.radio_addr = 0x60,
.gpiomask = 0x80000700,
.inputs = { {
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = LINE2,
- .tv = 1,
.gpio = 0x100,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
.gpio = 0x200,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
.gpio = 0x200,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.vmux = 1,
.amux = LINE1,
.gpio = 0x100,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.vmux = 8,
.amux = 2,
},
.mpeg = SAA7134_MPEG_DVB,
.ts_type = SAA7134_MPEG_TS_PARALLEL,
.inputs = { {
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
#if 0 /* FIXME */
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
.gpio = 0x200,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
.gpio = 0x200,
} },
#if 0
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.vmux = 1,
.amux = LINE1,
.gpio = 0x100,
},
#endif
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.vmux = 0,
.amux = TV,
},
.gpiomask = 0x00300003,
/* .gpiomask = 0x8c240003, */
.inputs = { {
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
.gpio = 0x01,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 6,
.amux = LINE1,
.gpio = 0x02,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
.gpio = 0x00300001,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = TV,
.gpio = 0x01,
},
.tda9887_conf = TDA9887_PRESENT,
.gpiomask = 0x03,
.inputs = { {
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
.gpio = 0x00,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
.gpio = 0x00,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
.gpio = 0x00,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
.gpio = 0x01,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = LINE1,
.gpio = 0x00,
},
.radio_addr = ADDR_UNSET,
.mpeg = SAA7134_MPEG_DVB,
.inputs = { {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8, /* Not tested */
.amux = LINE1
} },
.radio_addr = ADDR_UNSET,
.mpeg = SAA7134_MPEG_DVB,
.inputs = { {
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 2,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 9,
.amux = LINE1,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
},
},
.mpeg = SAA7134_MPEG_DVB,
.ts_type = SAA7134_MPEG_TS_PARALLEL,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
} },
.radio = { /* untested */
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
},
},
.tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE,
.mpeg = SAA7134_MPEG_DVB,
.inputs = { {
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 4,
.amux = LINE2,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
} },
.radio_addr = ADDR_UNSET,
.mpeg = SAA7134_MPEG_DVB,
.inputs = { {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
} },
},
.tda9887_conf = TDA9887_PRESENT,
.gpiomask = 0x00008000,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = LINE2,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
} },
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = LINE1,
},
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE2,
},
},
.radio_addr = ADDR_UNSET,
.gpiomask = 0x389c00,
.inputs = {{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
.gpio = 0x01fc00,
.mpeg = SAA7134_MPEG_DVB,
.ts_type = SAA7134_MPEG_TS_PARALLEL,
.inputs = { {
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 2,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 9,
.amux = LINE1,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
},
},
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.inputs = { {
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 2,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 9,
.amux = LINE1,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
},
},
.tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE,
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE2,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
} },
.tuner_addr = ADDR_UNSET,
.radio_addr = 0x60,
.inputs = { {
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE2,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE1,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = TV,
},
},
.mpeg = SAA7134_MPEG_DVB,
.ts_type = SAA7134_MPEG_TS_PARALLEL,
.inputs = { {
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
.gpio = 0x00050000,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
.gpio = 0x00050000,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
.gpio = 0x00050000,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
.gpio = 0x00050000,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.vmux = 0,
.amux = TV,
.gpio = 0x00050000,
.radio_addr = ADDR_UNSET,
.gpiomask = 0x00008000,
.inputs = { {
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = LINE2,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
} },
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = LINE1,
},
},
.radio_addr = ADDR_UNSET,
.gpiomask = 0x00008000,
.inputs = { {
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = LINE2,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 1,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
} },
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = LINE1,
},
},
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.inputs = {{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE1,
}, {
- .name = name_comp3,
+ .type = SAA7134_INPUT_COMPOSITE3,
.vmux = 2,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
} },
.gpiomask = 1 << 21,
.ts_type = SAA7134_MPEG_TS_PARALLEL,
.inputs = { {
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp,
+ .type = SAA7134_INPUT_COMPOSITE,
.vmux = 3,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
.gpio = 0x0000000,
},
.radio_addr = ADDR_UNSET,
.gpiomask = 0x618E700,
.inputs = {{
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE1,
.gpio = 0x6010000,
.gpiomask = 1 << 11,
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = TV,
- .tv = 1,
}, {
- .name = name_comp,
+ .type = SAA7134_INPUT_COMPOSITE,
.vmux = 4,
.amux = LINE1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE1,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = TV,
.gpio = 0x0000800,
},
.radio_addr = ADDR_UNSET,
.mpeg = SAA7134_MPEG_GO7007,
.inputs = { {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE2,
}, {
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 3,
.amux = TV,
- .tv = 1,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 6,
.amux = LINE1,
} },
.radio_addr = ADDR_UNSET,
.tda9887_conf = TDA9887_PRESENT,
.inputs = {{
- .name = name_tv,
+ .type = SAA7134_INPUT_TV,
.vmux = 1,
.amux = LINE2,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 0,
.amux = LINE2,
}, {
- .name = name_comp2,
+ .type = SAA7134_INPUT_COMPOSITE2,
.vmux = 3,
.amux = LINE2,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
} },
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = LINE1,
},
},
.radio_addr = ADDR_UNSET,
.gpiomask = 0x0d,
.inputs = {{
- .name = name_tv_mono,
+ .type = SAA7134_INPUT_TV_MONO,
.vmux = 1,
.amux = LINE1,
.gpio = 0x00,
- .tv = 1,
}, {
- .name = name_comp1,
+ .type = SAA7134_INPUT_COMPOSITE1,
.vmux = 3,
.amux = LINE2,
.gpio = 0x08,
}, {
- .name = name_svideo,
+ .type = SAA7134_INPUT_SVIDEO,
.vmux = 8,
.amux = LINE2,
.gpio = 0x08,
} },
.radio = {
- .name = name_radio,
+ .type = SAA7134_INPUT_RADIO,
.amux = LINE1,
.gpio = 0x04,
},
.mute = {
- .name = name_mute,
+ .type = SAA7134_INPUT_MUTE,
.amux = LINE1,
.gpio = 0x08,
},
},
-
+ [SAA7134_BOARD_SNAZIO_TVPVR_PRO] = {
+ .name = "SnaZio* TVPVR PRO",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 1 << 21,
+ .inputs = { {
+ .type = SAA7134_INPUT_TV,
+ .vmux = 1,
+ .amux = TV,
+ .gpio = 0x0000000,
+ }, {
+ .type = SAA7134_INPUT_COMPOSITE1,
+ .vmux = 3,
+ .amux = LINE2,
+ .gpio = 0x0000000,
+ }, {
+ .type = SAA7134_INPUT_SVIDEO,
+ .vmux = 8,
+ .amux = LINE2,
+ .gpio = 0x0000000,
+ } },
+ .radio = {
+ .type = SAA7134_INPUT_RADIO,
+ .amux = TV,
+ .gpio = 0x0200000,
+ },
+ },
};
const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards);
.subvendor = 0x107d,
.subdevice = 0x6f3a,
.driver_data = SAA7134_BOARD_LEADTEK_WINFAST_TV2100_FM,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1779, /* V One Multimedia PTE Ltd */
+ .subdevice = 0x13cf,
+ .driver_data = SAA7134_BOARD_SNAZIO_TVPVR_PRO,
}, {
/* --- boards without eeprom + subsystem ID --- */
.vendor = PCI_VENDOR_ID_PHILIPS,
case SAA7134_BOARD_BEHOLD_H7:
case SAA7134_BOARD_BEHOLD_A7:
case SAA7134_BOARD_KWORLD_PC150U:
+ case SAA7134_BOARD_SNAZIO_TVPVR_PRO:
dev->has_remote = SAA7134_REMOTE_I2C;
break;
case SAA7134_BOARD_AVERMEDIA_A169_B:
printk(KERN_DEBUG pr_fmt("irq: " fmt), ## arg); \
} while (0)
-void saa7134_track_gpio(struct saa7134_dev *dev, char *msg)
+void saa7134_track_gpio(struct saa7134_dev *dev, const char *msg)
{
unsigned long mode,status;
}
}
+static void saa7134_unregister_media_device(struct saa7134_dev *dev)
+{
+
+#ifdef CONFIG_MEDIA_CONTROLLER
+ if (!dev->media_dev)
+ return;
+ media_device_unregister(dev->media_dev);
+ media_device_cleanup(dev->media_dev);
+ kfree(dev->media_dev);
+ dev->media_dev = NULL;
+#endif
+}
+
+static void saa7134_media_release(struct saa7134_dev *dev)
+{
+#ifdef CONFIG_MEDIA_CONTROLLER
+ int i;
+
+ for (i = 0; i < SAA7134_INPUT_MAX + 1; i++)
+ media_device_unregister_entity(&dev->input_ent[i]);
+#endif
+}
+
+#if defined(CONFIG_MEDIA_CONTROLLER)
+static void saa7134_create_entities(struct saa7134_dev *dev)
+{
+ int ret, i;
+ struct media_entity *entity;
+ struct media_entity *decoder = NULL;
+
+ /* Check if it is using an external analog TV demod */
+ media_device_for_each_entity(entity, dev->media_dev) {
+ if (entity->function == MEDIA_ENT_F_ATV_DECODER) {
+ decoder = entity;
+ break;
+ }
+ }
+
+ /*
+ * saa713x is not using an external ATV demod.
+ * Register the internal one
+ */
+ if (!decoder) {
+ dev->demod.name = "saa713x";
+ dev->demod_pad[DEMOD_PAD_IF_INPUT].flags = MEDIA_PAD_FL_SINK;
+ dev->demod_pad[DEMOD_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE;
+ dev->demod_pad[DEMOD_PAD_VBI_OUT].flags = MEDIA_PAD_FL_SOURCE;
+ dev->demod.function = MEDIA_ENT_F_ATV_DECODER;
+
+ ret = media_entity_pads_init(&dev->demod, DEMOD_NUM_PADS,
+ dev->demod_pad);
+ if (ret < 0)
+ pr_err("failed to initialize demod pad!\n");
+
+ ret = media_device_register_entity(dev->media_dev, &dev->demod);
+ if (ret < 0)
+ pr_err("failed to register demod entity!\n");
+
+ dev->decoder = &dev->demod;
+ } else {
+ dev->decoder = decoder;
+ }
+
+ /* Initialize Video, VBI and Radio pads */
+ dev->video_pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_pads_init(&dev->video_dev->entity, 1,
+ &dev->video_pad);
+ if (ret < 0)
+ pr_err("failed to initialize video media entity!\n");
+
+ dev->vbi_pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_pads_init(&dev->vbi_dev->entity, 1,
+ &dev->vbi_pad);
+ if (ret < 0)
+ pr_err("failed to initialize vbi media entity!\n");
+
+ /* Create entities for each input connector */
+ for (i = 0; i < SAA7134_INPUT_MAX; i++) {
+ struct media_entity *ent = &dev->input_ent[i];
+ struct saa7134_input *in = &card_in(dev, i);
+
+ if (in->type == SAA7134_NO_INPUT)
+ break;
+
+ /* This input uses the S-Video connector */
+ if (in->type == SAA7134_INPUT_COMPOSITE_OVER_SVIDEO)
+ continue;
+
+ ent->name = saa7134_input_name[in->type];
+ ent->flags = MEDIA_ENT_FL_CONNECTOR;
+ dev->input_pad[i].flags = MEDIA_PAD_FL_SOURCE;
+
+ switch (in->type) {
+ case SAA7134_INPUT_COMPOSITE:
+ case SAA7134_INPUT_COMPOSITE0:
+ case SAA7134_INPUT_COMPOSITE1:
+ case SAA7134_INPUT_COMPOSITE2:
+ case SAA7134_INPUT_COMPOSITE3:
+ case SAA7134_INPUT_COMPOSITE4:
+ ent->function = MEDIA_ENT_F_CONN_COMPOSITE;
+ break;
+ case SAA7134_INPUT_SVIDEO:
+ case SAA7134_INPUT_SVIDEO0:
+ case SAA7134_INPUT_SVIDEO1:
+ ent->function = MEDIA_ENT_F_CONN_SVIDEO;
+ break;
+ default:
+ /*
+ * SAA7134_INPUT_TV and SAA7134_INPUT_TV_MONO.
+ *
+ * Please notice that neither SAA7134_INPUT_MUTE or
+ * SAA7134_INPUT_RADIO are defined at
+ * saa7134_board.input.
+ */
+ ent->function = MEDIA_ENT_F_CONN_RF;
+ break;
+ }
+
+ ret = media_entity_pads_init(ent, 1, &dev->input_pad[i]);
+ if (ret < 0)
+ pr_err("failed to initialize input pad[%d]!\n", i);
+
+ ret = media_device_register_entity(dev->media_dev, ent);
+ if (ret < 0)
+ pr_err("failed to register input entity %d!\n", i);
+ }
+
+ /* Create input for Radio RF connector */
+ if (card_has_radio(dev)) {
+ struct saa7134_input *in = &saa7134_boards[dev->board].radio;
+ struct media_entity *ent = &dev->input_ent[i];
+
+ ent->name = saa7134_input_name[in->type];
+ ent->flags = MEDIA_ENT_FL_CONNECTOR;
+ dev->input_pad[i].flags = MEDIA_PAD_FL_SOURCE;
+ ent->function = MEDIA_ENT_F_CONN_RF;
+
+ ret = media_entity_pads_init(ent, 1, &dev->input_pad[i]);
+ if (ret < 0)
+ pr_err("failed to initialize input pad[%d]!\n", i);
+
+ ret = media_device_register_entity(dev->media_dev, ent);
+ if (ret < 0)
+ pr_err("failed to register input entity %d!\n", i);
+ }
+}
+#endif
+
static struct video_device *vdev_init(struct saa7134_dev *dev,
struct video_device *template,
char *type)
static void saa7134_unregister_video(struct saa7134_dev *dev)
{
+ saa7134_media_release(dev);
+
if (dev->video_dev) {
if (video_is_registered(dev->video_dev))
video_unregister_device(dev->video_dev);
if (NULL == dev)
return -ENOMEM;
+ dev->nr = saa7134_devcount;
+ sprintf(dev->name, "saa%x[%d]", pci_dev->device, dev->nr);
+
+#ifdef CONFIG_MEDIA_CONTROLLER
+ dev->media_dev = kzalloc(sizeof(*dev->media_dev), GFP_KERNEL);
+ if (!dev->media_dev) {
+ err = -ENOMEM;
+ goto fail0;
+ }
+ media_device_pci_init(dev->media_dev, pci_dev, dev->name);
+ dev->v4l2_dev.mdev = dev->media_dev;
+#endif
+
err = v4l2_device_register(&pci_dev->dev, &dev->v4l2_dev);
if (err)
goto fail0;
goto fail1;
}
- dev->nr = saa7134_devcount;
- sprintf(dev->name,"saa%x[%d]",pci_dev->device,dev->nr);
-
/* pci quirks */
if (pci_pci_problems) {
if (pci_pci_problems & PCIPCI_TRITON)
dev->name, video_device_node_name(dev->radio_dev));
}
+#ifdef CONFIG_MEDIA_CONTROLLER
+ saa7134_create_entities(dev);
+
+ err = v4l2_mc_create_media_graph(dev->media_dev);
+ if (err) {
+ pr_err("failed to create media graph\n");
+ goto fail5;
+ }
+#endif
/* everything worked */
saa7134_devcount++;
saa7134_dmasound_init(dev);
request_submodules(dev);
+
+ /*
+ * Do it at the end, to reduce dynamic configuration changes during
+ * the device init. Yet, as request_modules() can be async, the
+ * topology will likely change after load the saa7134 subdrivers.
+ */
+#ifdef CONFIG_MEDIA_CONTROLLER
+ err = media_device_register(dev->media_dev);
+ if (err)
+ goto fail5;
+#endif
+
return 0;
fail5:
fail1:
v4l2_device_unregister(&dev->v4l2_dev);
fail0:
+#ifdef CONFIG_MEDIA_CONTROLLER
+ kfree(dev->media_dev);
+#endif
kfree(dev);
return err;
}
release_mem_region(pci_resource_start(pci_dev,0),
pci_resource_len(pci_dev,0));
-
v4l2_device_unregister(&dev->v4l2_dev);
+ saa7134_unregister_media_device(dev);
+
/* free memory */
kfree(dev);
}
fe0->dvb.frontend->callback = saa7134_tuner_callback;
/* register everything else */
+#ifndef CONFIG_MEDIA_CONTROLLER_DVB
ret = vb2_dvb_register_bus(&dev->frontends, THIS_MODULE, dev,
- &dev->pci->dev, adapter_nr, 0);
+ &dev->pci->dev, NULL,
+ adapter_nr, 0);
+#else
+ ret = vb2_dvb_register_bus(&dev->frontends, THIS_MODULE, dev,
+ &dev->pci->dev, dev->media_dev,
+ adapter_nr, 0);
+#endif
/* this sequence is necessary to make the tda1004x load its firmware
* and to enter analog mode of hybrid boards
.vidioc_querybuf = vb2_ioctl_querybuf,
.vidioc_qbuf = vb2_ioctl_qbuf,
.vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
.vidioc_streamon = vb2_ioctl_streamon,
.vidioc_streamoff = vb2_ioctl_streamoff,
.vidioc_g_frequency = saa7134_g_frequency,
* transfers that do not start at the beginning of a page. A USERPTR
* can start anywhere in a page, so USERPTR support is a no-go.
*/
- q->io_modes = VB2_MMAP | VB2_READ;
+ q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
q->drv_priv = &dev->ts_q;
q->ops = &saa7134_empress_qops;
q->gfp_flags = GFP_DMA32;
return 0;
}
-static struct go7007_hpi_ops saa7134_go7007_hpi_ops = {
+static const struct go7007_hpi_ops saa7134_go7007_hpi_ops = {
.interface_reset = saa7134_go7007_interface_reset,
.write_interrupt = saa7134_go7007_write_interrupt,
.read_interrupt = saa7134_go7007_read_interrupt,
msg_msi.addr, dev->i2c_adap.name,
(1 == rc) ? "yes" : "no");
break;
+ case SAA7134_BOARD_SNAZIO_TVPVR_PRO:
+ dev->init_data.name = "SnaZio* TVPVR PRO";
+ dev->init_data.get_key = get_key_msi_tvanywhere_plus;
+ dev->init_data.ir_codes = RC_MAP_MSI_TVANYWHERE_PLUS;
+ /*
+ * MSI TV@nyware Plus requires more frequent polling
+ * otherwise it will miss some keypresses
+ */
+ dev->init_data.polling_interval = 50;
+ info.addr = 0x30;
+ /*
+ * MSI TV@nywhere Plus controller doesn't seem to
+ * respond to probes unless we read something from
+ * an existing device. Weird...
+ * REVISIT: might no longer be needed
+ */
+ rc = i2c_transfer(&dev->i2c_adap, &msg_msi, 1);
+ input_dbg("probe 0x%02x @ %s: %s\n",
+ msg_msi.addr, dev->i2c_adap.name,
+ (rc == 1) ? "yes" : "no");
+ break;
case SAA7134_BOARD_KWORLD_PC150U:
/* copied and modified from MSI TV@nywhere Plus */
dev->init_data.name = "Kworld PC150-U";
in = dev->input;
mute = (dev->ctl_mute ||
(dev->automute && (&card(dev).radio) != in));
- if (card(dev).mute.name) {
+ if (card(dev).mute.type) {
/*
* 7130 - we'll mute using some unconnected audio input
* 7134 - we'll probably should switch external mux with gpio
if (dev->hw_mute == mute &&
dev->hw_input == in && !dev->insuspend) {
audio_dbg(1, "mute/input: nothing to do [mute=%d,input=%s]\n",
- mute, in->name);
+ mute, saa7134_input_name[in->type]);
return;
}
audio_dbg(1, "ctl_mute=%d automute=%d input=%s => mute=%d input=%s\n",
dev->ctl_mute, dev->automute,
- dev->input->name, mute, in->name);
+ saa7134_input_name[dev->input->type], mute,
+ saa7134_input_name[in->type]);
dev->hw_mute = mute;
dev->hw_input = in;
mask = card(dev).gpiomask;
saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, mask, mask);
saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, mask, in->gpio);
- saa7134_track_gpio(dev,in->name);
+ saa7134_track_gpio(dev, saa7134_input_name[in->type]);
}
static void tvaudio_setmode(struct saa7134_dev *dev,
if (0 != card(dev).gpiomask) {
mask = card(dev).gpiomask;
- if (card(dev).mute.name && dev->ctl_mute)
+ if (card(dev).mute.type && dev->ctl_mute)
in = &card(dev).mute;
else
in = dev->input;
saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, mask, mask);
saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, mask, in->gpio);
- saa7134_track_gpio(dev,in->name);
+ saa7134_track_gpio(dev, saa7134_input_name[in->type]);
}
return 0;
static void video_mux(struct saa7134_dev *dev, int input)
{
- video_dbg("video input = %d [%s]\n", input, card_in(dev, input).name);
+ video_dbg("video input = %d [%s]\n",
+ input, saa7134_input_name[card_in(dev, input).type]);
dev->ctl_input = input;
set_tvnorm(dev, dev->tvnorm);
saa7134_tvaudio_setinput(dev, &card_in(dev, input));
{
saa7134_set_decoder(dev);
- if (card_in(dev, dev->ctl_input).tv)
- saa_call_all(dev, video, s_std, dev->tvnorm->id);
+ saa_call_all(dev, video, s_std, dev->tvnorm->id);
/* Set the correct norm for the saa6752hs. This function
does nothing if there is no saa6752hs. */
saa_call_empress(dev, video, s_std, dev->tvnorm->id);
return 0;
}
+/*
+ * Media Controller helper functions
+ */
+
+static int saa7134_enable_analog_tuner(struct saa7134_dev *dev)
+{
+#ifdef CONFIG_MEDIA_CONTROLLER
+ struct media_device *mdev = dev->media_dev;
+ struct media_entity *source;
+ struct media_link *link, *found_link = NULL;
+ int ret, active_links = 0;
+
+ if (!mdev || !dev->decoder)
+ return 0;
+
+ /*
+ * This will find the tuner that is connected into the decoder.
+ * Technically, this is not 100% correct, as the device may be
+ * using an analog input instead of the tuner. However, as we can't
+ * do DVB streaming while the DMA engine is being used for V4L2,
+ * this should be enough for the actual needs.
+ */
+ list_for_each_entry(link, &dev->decoder->links, list) {
+ if (link->sink->entity == dev->decoder) {
+ found_link = link;
+ if (link->flags & MEDIA_LNK_FL_ENABLED)
+ active_links++;
+ break;
+ }
+ }
+
+ if (active_links == 1 || !found_link)
+ return 0;
+
+ source = found_link->source->entity;
+ list_for_each_entry(link, &source->links, list) {
+ struct media_entity *sink;
+ int flags = 0;
+
+ sink = link->sink->entity;
+
+ if (sink == dev->decoder)
+ flags = MEDIA_LNK_FL_ENABLED;
+
+ ret = media_entity_setup_link(link, flags);
+ if (ret) {
+ pr_err("Couldn't change link %s->%s to %s. Error %d\n",
+ source->name, sink->name,
+ flags ? "enabled" : "disabled",
+ ret);
+ return ret;
+ }
+ }
+#endif
+ return 0;
+}
+
/* ------------------------------------------------------------------ */
static int buffer_activate(struct saa7134_dev *dev,
*nplanes = 1;
sizes[0] = size;
alloc_ctxs[0] = dev->alloc_ctx;
+
+ saa7134_enable_analog_tuner(dev);
+
return 0;
}
f->fmt.pix.height = dev->height;
f->fmt.pix.field = dev->field;
f->fmt.pix.pixelformat = dev->fmt->fourcc;
- f->fmt.pix.bytesperline =
- (f->fmt.pix.width * dev->fmt->depth) >> 3;
+ if (dev->fmt->planar)
+ f->fmt.pix.bytesperline = f->fmt.pix.width;
+ else
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * dev->fmt->depth) / 8;
f->fmt.pix.sizeimage =
- f->fmt.pix.height * f->fmt.pix.bytesperline;
+ (f->fmt.pix.height * f->fmt.pix.width * dev->fmt->depth) / 8;
f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
return 0;
}
if (f->fmt.pix.height > maxh)
f->fmt.pix.height = maxh;
f->fmt.pix.width &= ~0x03;
- f->fmt.pix.bytesperline =
- (f->fmt.pix.width * fmt->depth) >> 3;
+ if (fmt->planar)
+ f->fmt.pix.bytesperline = f->fmt.pix.width;
+ else
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fmt->depth) / 8;
f->fmt.pix.sizeimage =
- f->fmt.pix.height * f->fmt.pix.bytesperline;
+ (f->fmt.pix.height * f->fmt.pix.width * fmt->depth) / 8;
f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
return 0;
n = i->index;
if (n >= SAA7134_INPUT_MAX)
return -EINVAL;
- if (NULL == card_in(dev, i->index).name)
+ if (card_in(dev, i->index).type == SAA7134_NO_INPUT)
return -EINVAL;
i->index = n;
- i->type = V4L2_INPUT_TYPE_CAMERA;
- strcpy(i->name, card_in(dev, n).name);
- if (card_in(dev, n).tv)
+ strcpy(i->name, saa7134_input_name[card_in(dev, n).type]);
+ switch (card_in(dev, n).type) {
+ case SAA7134_INPUT_TV:
+ case SAA7134_INPUT_TV_MONO:
i->type = V4L2_INPUT_TYPE_TUNER;
+ break;
+ default:
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ break;
+ }
if (n == dev->ctl_input) {
int v1 = saa_readb(SAA7134_STATUS_VIDEO1);
int v2 = saa_readb(SAA7134_STATUS_VIDEO2);
if (i >= SAA7134_INPUT_MAX)
return -EINVAL;
- if (NULL == card_in(dev, i).name)
+ if (card_in(dev, i).type == SAA7134_NO_INPUT)
return -EINVAL;
video_mux(dev, i);
return 0;
return -EINVAL;
memset(t, 0, sizeof(*t));
for (n = 0; n < SAA7134_INPUT_MAX; n++) {
- if (card_in(dev, n).tv)
+ if (card_in(dev, n).type == SAA7134_INPUT_TV ||
+ card_in(dev, n).type == SAA7134_INPUT_TV_MONO)
break;
}
if (n == SAA7134_INPUT_MAX)
return -EINVAL;
- if (NULL != card_in(dev, n).name) {
+ if (card_in(dev, n).type != SAA7134_NO_INPUT) {
strcpy(t->name, "Television");
t->type = V4L2_TUNER_ANALOG_TV;
saa_call_all(dev, tuner, g_tuner, t);
.vidioc_querybuf = vb2_ioctl_querybuf,
.vidioc_qbuf = vb2_ioctl_qbuf,
.vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
.vidioc_s_std = saa7134_s_std,
.vidioc_g_std = saa7134_g_std,
.vidioc_querystd = saa7134_querystd,
* USERPTR support is a no-go unless the application knows about these
* limitations and has special support for this.
*/
- q->io_modes = VB2_MMAP | VB2_READ;
+ q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
if (saa7134_userptr)
q->io_modes |= VB2_USERPTR;
q->drv_priv = &dev->video_q;
#define SAA7134_BOARD_WIS_VOYAGER 193
#define SAA7134_BOARD_AVERMEDIA_505 194
#define SAA7134_BOARD_LEADTEK_WINFAST_TV2100_FM 195
+#define SAA7134_BOARD_SNAZIO_TVPVR_PRO 196
#define SAA7134_MAXBOARDS 32
#define SAA7134_INPUT_MAX 8
#define SET_CLOCK_INVERTED (1 << 2)
#define SET_VSYNC_OFF (1 << 3)
+enum saa7134_input_types {
+ SAA7134_NO_INPUT = 0,
+ SAA7134_INPUT_MUTE,
+ SAA7134_INPUT_RADIO,
+ SAA7134_INPUT_TV,
+ SAA7134_INPUT_TV_MONO,
+ SAA7134_INPUT_COMPOSITE,
+ SAA7134_INPUT_COMPOSITE0,
+ SAA7134_INPUT_COMPOSITE1,
+ SAA7134_INPUT_COMPOSITE2,
+ SAA7134_INPUT_COMPOSITE3,
+ SAA7134_INPUT_COMPOSITE4,
+ SAA7134_INPUT_SVIDEO,
+ SAA7134_INPUT_SVIDEO0,
+ SAA7134_INPUT_SVIDEO1,
+ SAA7134_INPUT_COMPOSITE_OVER_SVIDEO,
+};
+
struct saa7134_input {
- char *name;
- unsigned int vmux;
- enum saa7134_audio_in amux;
- unsigned int gpio;
- unsigned int tv:1;
+ enum saa7134_input_types type;
+ unsigned int vmux;
+ enum saa7134_audio_in amux;
+ unsigned int gpio;
};
enum saa7134_mpeg_type {
unsigned int ts_force_val:1;
};
-#define card_has_radio(dev) (NULL != saa7134_boards[dev->board].radio.name)
+#define card_has_radio(dev) (SAA7134_NO_INPUT != saa7134_boards[dev->board].radio.type)
#define card_is_empress(dev) (SAA7134_MPEG_EMPRESS == saa7134_boards[dev->board].mpeg)
#define card_is_dvb(dev) (SAA7134_MPEG_DVB == saa7134_boards[dev->board].mpeg)
#define card_is_go7007(dev) (SAA7134_MPEG_GO7007 == saa7134_boards[dev->board].mpeg)
/* I2C keyboard data */
struct IR_i2c_init_data init_data;
+#ifdef CONFIG_MEDIA_CONTROLLER
+ struct media_device *media_dev;
+
+ struct media_entity input_ent[SAA7134_INPUT_MAX + 1];
+ struct media_pad input_pad[SAA7134_INPUT_MAX + 1];
+
+ struct media_entity demod;
+ struct media_pad demod_pad[DEMOD_NUM_PADS];
+
+ struct media_pad video_pad, vbi_pad;
+ struct media_entity *decoder;
+#endif
+
#if IS_ENABLED(CONFIG_VIDEO_SAA7134_DVB)
/* SAA7134_MPEG_DVB only */
struct vb2_dvb_frontends frontends;
extern int saa7134_no_overlay;
extern bool saa7134_userptr;
-void saa7134_track_gpio(struct saa7134_dev *dev, char *msg);
+void saa7134_track_gpio(struct saa7134_dev *dev, const char *msg);
void saa7134_set_gpio(struct saa7134_dev *dev, int bit_no, int value);
#define SAA7134_PGTABLE_SIZE 4096
/* saa7134-cards.c */
extern struct saa7134_board saa7134_boards[];
+extern const char * const saa7134_input_name[];
extern const unsigned int saa7134_bcount;
extern struct pci_device_id saa7134_pci_tbl[];
#endif
}
-static struct sp8870_config alps_tdlb7_config = {
+static const struct sp8870_config alps_tdlb7_config = {
.demod_address = 0x71,
.request_firmware = alps_tdlb7_request_firmware,
break;
case 0x0001: // Hauppauge/TT Nexus-T premium rev1.X
+ {
+ struct dvb_frontend *fe;
+
// try ALPS TDLB7 first, then Grundig 29504-401
- av7110->fe = dvb_attach(sp8870_attach, &alps_tdlb7_config, &av7110->i2c_adap);
- if (av7110->fe) {
- av7110->fe->ops.tuner_ops.set_params = alps_tdlb7_tuner_set_params;
+ fe = dvb_attach(sp8870_attach, &alps_tdlb7_config, &av7110->i2c_adap);
+ if (fe) {
+ fe->ops.tuner_ops.set_params = alps_tdlb7_tuner_set_params;
+ av7110->fe = fe;
break;
}
- /* fall-thru */
+ }
+ /* fall-thru */
case 0x0008: // Hauppauge/TT DVB-T
// Grundig 29504-401
break;
case 0x1016: // Hauppauge/TT Nova-S SE (samsung s5h1420/????(tda8260))
- budget->dvb_frontend = dvb_attach(s5h1420_attach, &s5h1420_config, &budget->i2c_adap);
- if (budget->dvb_frontend) {
- budget->dvb_frontend->ops.tuner_ops.set_params = s5h1420_tuner_set_params;
- if (dvb_attach(lnbp21_attach, budget->dvb_frontend, &budget->i2c_adap, 0, 0) == NULL) {
+ {
+ struct dvb_frontend *fe;
+
+ fe = dvb_attach(s5h1420_attach, &s5h1420_config, &budget->i2c_adap);
+ if (fe) {
+ fe->ops.tuner_ops.set_params = s5h1420_tuner_set_params;
+ budget->dvb_frontend = fe;
+ if (dvb_attach(lnbp21_attach, fe, &budget->i2c_adap,
+ 0, 0) == NULL) {
printk("%s: No LNBP21 found!\n", __func__);
goto error_out;
}
break;
}
-
+ }
+ /* fall through */
case 0x1018: // TT Budget-S-1401 (philips tda10086/philips tda8262)
+ {
+ struct dvb_frontend *fe;
+
// gpio2 is connected to CLB - reset it + leave it high
saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO);
msleep(1);
saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI);
msleep(1);
- budget->dvb_frontend = dvb_attach(tda10086_attach, &tda10086_config, &budget->i2c_adap);
- if (budget->dvb_frontend) {
- if (dvb_attach(tda826x_attach, budget->dvb_frontend, 0x60, &budget->i2c_adap, 0) == NULL)
+ fe = dvb_attach(tda10086_attach, &tda10086_config, &budget->i2c_adap);
+ if (fe) {
+ budget->dvb_frontend = fe;
+ if (dvb_attach(tda826x_attach, fe, 0x60,
+ &budget->i2c_adap, 0) == NULL)
printk("%s: No tda826x found!\n", __func__);
- if (dvb_attach(lnbp21_attach, budget->dvb_frontend, &budget->i2c_adap, 0, 0) == NULL) {
+ if (dvb_attach(lnbp21_attach, fe,
+ &budget->i2c_adap, 0, 0) == NULL) {
printk("%s: No LNBP21 found!\n", __func__);
goto error_out;
}
break;
}
+ }
+ /* fall through */
case 0x101c: { /* TT S2-1600 */
- struct stv6110x_devctl *ctl;
+ const struct stv6110x_devctl *ctl;
saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO);
msleep(50);
saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI);
break;
case 0x1020: { /* Omicom S2 */
- struct stv6110x_devctl *ctl;
+ const struct stv6110x_devctl *ctl;
saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO);
msleep(50);
saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI);
Say Y here if you want to enable VIU device on MPC5121e Rev2+.
In doubt, say N.
-config VIDEO_TIMBERDALE
- tristate "Support for timberdale Video In/LogiWIN"
- depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && HAS_DMA
- depends on (MFD_TIMBERDALE && TIMB_DMA) || COMPILE_TEST
- select VIDEO_ADV7180
- select VIDEOBUF_DMA_CONTIG
- ---help---
- Add support for the Video In peripherial of the timberdale FPGA.
-
config VIDEO_M32R_AR
tristate "AR devices"
depends on VIDEO_V4L2
source "drivers/media/platform/am437x/Kconfig"
source "drivers/media/platform/xilinx/Kconfig"
+config VIDEO_TI_CAL
+ tristate "TI CAL (Camera Adaptation Layer) driver"
+ depends on VIDEO_DEV && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ depends on SOC_DRA7XX || COMPILE_TEST
+ depends on HAS_DMA
+ select VIDEOBUF2_DMA_CONTIG
+ default n
+ ---help---
+ Support for the TI CAL (Camera Adaptation Layer) block
+ found on DRA72X SoC.
+ In TI Technical Reference Manual this module is referred as
+ Camera Interface Subsystem (CAMSS).
+
endif # V4L_PLATFORM_DRIVERS
menuconfig V4L_MEM2MEM_DRIVERS
# Makefile for the video capture/playback device drivers.
#
-obj-$(CONFIG_VIDEO_TIMBERDALE) += timblogiw.o
obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o
obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o
obj-$(CONFIG_VIDEO_TI_VPE) += ti-vpe/
+obj-$(CONFIG_VIDEO_TI_CAL) += ti-vpe/
+
obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o
obj-$(CONFIG_VIDEO_CODA) += coda/
/* Calculate bytesused field */
if (dst_buf->sequence == 0) {
- vb2_set_plane_payload(&dst_buf->vb2_buf, 0,
+ vb2_set_plane_payload(&dst_buf->vb2_buf, 0, wr_ptr - start_ptr +
ctx->vpu_header_size[0] +
ctx->vpu_header_size[1] +
ctx->vpu_header_size[2]);
return video_register_device(vfd, VFL_TYPE_GRABBER, 0);
}
+static void coda_copy_firmware(struct coda_dev *dev, const u8 * const buf,
+ size_t size)
+{
+ u32 *src = (u32 *)buf;
+
+ /* Check if the firmware has a 16-byte Freescale header, skip it */
+ if (buf[0] == 'M' && buf[1] == 'X')
+ src += 4;
+ /*
+ * Check whether the firmware is in native order or pre-reordered for
+ * memory access. The first instruction opcode always is 0xe40e.
+ */
+ if (__le16_to_cpup((__le16 *)src) == 0xe40e) {
+ u32 *dst = dev->codebuf.vaddr;
+ int i;
+
+ /* Firmware in native order, reorder while copying */
+ if (dev->devtype->product == CODA_DX6) {
+ for (i = 0; i < (size - 16) / 4; i++)
+ dst[i] = (src[i] << 16) | (src[i] >> 16);
+ } else {
+ for (i = 0; i < (size - 16) / 4; i += 2) {
+ dst[i] = (src[i + 1] << 16) | (src[i + 1] >> 16);
+ dst[i + 1] = (src[i] << 16) | (src[i] >> 16);
+ }
+ }
+ } else {
+ /* Copy the already reordered firmware image */
+ memcpy(dev->codebuf.vaddr, src, size);
+ }
+}
+
+static void coda_fw_callback(const struct firmware *fw, void *context);
+
+static int coda_firmware_request(struct coda_dev *dev)
+{
+ char *fw = dev->devtype->firmware[dev->firmware];
+
+ dev_dbg(&dev->plat_dev->dev, "requesting firmware '%s' for %s\n", fw,
+ coda_product_name(dev->devtype->product));
+
+ return request_firmware_nowait(THIS_MODULE, true, fw,
+ &dev->plat_dev->dev, GFP_KERNEL, dev,
+ coda_fw_callback);
+}
+
static void coda_fw_callback(const struct firmware *fw, void *context)
{
struct coda_dev *dev = context;
struct platform_device *pdev = dev->plat_dev;
int i, ret;
- if (!fw) {
+ if (!fw && dev->firmware == 1) {
v4l2_err(&dev->v4l2_dev, "firmware request failed\n");
goto put_pm;
}
+ if (!fw) {
+ dev->firmware = 1;
+ coda_firmware_request(dev);
+ return;
+ }
+ if (dev->firmware == 1) {
+ /*
+ * Since we can't suppress warnings for failed asynchronous
+ * firmware requests, report that the fallback firmware was
+ * found.
+ */
+ dev_info(&pdev->dev, "Using fallback firmware %s\n",
+ dev->devtype->firmware[dev->firmware]);
+ }
/* allocate auxiliary per-device code buffer for the BIT processor */
ret = coda_alloc_aux_buf(dev, &dev->codebuf, fw->size, "codebuf",
if (ret < 0)
goto put_pm;
- /* Copy the whole firmware image to the code buffer */
- memcpy(dev->codebuf.vaddr, fw->data, fw->size);
+ coda_copy_firmware(dev, fw->data, fw->size);
release_firmware(fw);
ret = coda_hw_init(dev);
pm_runtime_put_sync(&pdev->dev);
}
-static int coda_firmware_request(struct coda_dev *dev)
-{
- char *fw = dev->devtype->firmware;
-
- dev_dbg(&dev->plat_dev->dev, "requesting firmware '%s' for %s\n", fw,
- coda_product_name(dev->devtype->product));
-
- return request_firmware_nowait(THIS_MODULE, true,
- fw, &dev->plat_dev->dev, GFP_KERNEL, dev, coda_fw_callback);
-}
-
enum coda_platform {
CODA_IMX27,
CODA_IMX53,
static const struct coda_devtype coda_devdata[] = {
[CODA_IMX27] = {
- .firmware = "v4l-codadx6-imx27.bin",
+ .firmware = {
+ "vpu_fw_imx27_TO2.bin",
+ "v4l-codadx6-imx27.bin"
+ },
.product = CODA_DX6,
.codecs = codadx6_codecs,
.num_codecs = ARRAY_SIZE(codadx6_codecs),
.iram_size = 0xb000,
},
[CODA_IMX53] = {
- .firmware = "v4l-coda7541-imx53.bin",
+ .firmware = {
+ "vpu_fw_imx53.bin",
+ "v4l-coda7541-imx53.bin"
+ },
.product = CODA_7541,
.codecs = coda7_codecs,
.num_codecs = ARRAY_SIZE(coda7_codecs),
.iram_size = 0x14000,
},
[CODA_IMX6Q] = {
- .firmware = "v4l-coda960-imx6q.bin",
+ .firmware = {
+ "vpu_fw_imx6q.bin",
+ "v4l-coda960-imx6q.bin"
+ },
.product = CODA_960,
.codecs = coda9_codecs,
.num_codecs = ARRAY_SIZE(coda9_codecs),
.iram_size = 0x21000,
},
[CODA_IMX6DL] = {
- .firmware = "v4l-coda960-imx6dl.bin",
+ .firmware = {
+ "vpu_fw_imx6d.bin",
+ "v4l-coda960-imx6dl.bin"
+ },
.product = CODA_960,
.codecs = coda9_codecs,
.num_codecs = ARRAY_SIZE(coda9_codecs),
pdev_id = of_id ? of_id->data : platform_get_device_id(pdev);
- if (of_id) {
+ if (of_id)
dev->devtype = of_id->data;
- } else if (pdev_id) {
+ else if (pdev_id)
dev->devtype = &coda_devdata[pdev_id->driver_data];
- } else {
- ret = -EINVAL;
- goto err_v4l2_register;
- }
+ else
+ return -EINVAL;
spin_lock_init(&dev->irqlock);
INIT_LIST_HEAD(&dev->instances);
struct coda_video_device;
struct coda_devtype {
- char *firmware;
+ char *firmware[2];
enum coda_product product;
const struct coda_codec *codecs;
unsigned int num_codecs;
struct video_device vfd[5];
struct platform_device *plat_dev;
const struct coda_devtype *devtype;
+ int firmware;
void __iomem *regs_base;
struct clk *clk_per;
*/
if (raw_params->fault_pxl.fp_num != config_params->fault_pxl.fp_num) {
if (fpc_physaddr != NULL) {
- free_pages((unsigned long)fpc_physaddr,
+ free_pages((unsigned long)fpc_virtaddr,
get_order
(config_params->fault_pxl.fp_num *
FP_NUM_BYTES));
{
struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
struct gsc_dev *gsc = ctx->gsc_dev;
- int ret;
+ unsigned int ret;
if (mutex_lock_interruptible(&gsc->lock))
return -ERESTARTSYS;
switch (local->index) {
case FLITE_SD_PAD_SINK:
- if (!is_media_entity_v4l2_subdev(remote->entity)) {
- ret = -EINVAL;
- break;
- }
if (flags & MEDIA_LNK_FL_ENABLED) {
if (fimc->source_subdev_grp_id == 0)
fimc->source_subdev_grp_id = sd->grp_id;
case FLITE_SD_PAD_SOURCE_DMA:
if (!(flags & MEDIA_LNK_FL_ENABLED))
atomic_set(&fimc->out_path, FIMC_IO_NONE);
- else if (is_media_entity_v4l2_io(remote->entity))
- atomic_set(&fimc->out_path, FIMC_IO_DMA);
else
- ret = -EINVAL;
+ atomic_set(&fimc->out_path, FIMC_IO_DMA);
break;
case FLITE_SD_PAD_SOURCE_ISP:
if (!(flags & MEDIA_LNK_FL_ENABLED))
atomic_set(&fimc->out_path, FIMC_IO_NONE);
- else if (is_media_entity_v4l2_subdev(remote->entity))
- atomic_set(&fimc->out_path, FIMC_IO_ISP);
else
- ret = -EINVAL;
+ atomic_set(&fimc->out_path, FIMC_IO_ISP);
break;
default:
struct fimc_source_info *pd = &fmd->sensor[index].pdata;
struct device_node *rem, *ep, *np;
struct v4l2_of_endpoint endpoint;
+ int ret;
/* Assume here a port node can have only one endpoint node. */
ep = of_get_next_child(port, NULL);
if (!ep)
return 0;
- v4l2_of_parse_endpoint(ep, &endpoint);
+ ret = v4l2_of_parse_endpoint(ep, &endpoint);
+ if (ret) {
+ of_node_put(ep);
+ return ret;
+ }
+
if (WARN_ON(endpoint.base.port == 0) || index >= FIMC_MAX_SENSORS)
return -EINVAL;
continue;
ret = fimc_md_parse_port_node(fmd, port, index);
- if (ret < 0)
+ if (ret < 0) {
+ of_node_put(node);
goto rpm_put;
+ }
index++;
}
for_each_child_of_node(ports, node) {
ret = fimc_md_parse_port_node(fmd, node, index);
- if (ret < 0)
+ if (ret < 0) {
+ of_node_put(node);
break;
+ }
index++;
}
rpm_put:
ret = fimc_md_register_platform_entity(fmd, pdev,
plat_entity);
put_device(&pdev->dev);
- if (ret < 0)
+ if (ret < 0) {
+ of_node_put(node);
break;
+ }
}
return ret;
{
struct device_node *node = pdev->dev.of_node;
struct v4l2_of_endpoint endpoint;
+ int ret;
if (of_property_read_u32(node, "clock-frequency",
&state->clk_frequency))
return -EINVAL;
}
/* Get port node and validate MIPI-CSI channel id. */
- v4l2_of_parse_endpoint(node, &endpoint);
+ ret = v4l2_of_parse_endpoint(node, &endpoint);
+ if (ret)
+ goto err;
state->index = endpoint.base.port - FIMC_INPUT_MIPI_CSI2_0;
if (state->index >= CSIS_MAX_ENTITIES)
"samsung,csis-wclk");
state->num_lanes = endpoint.bus.mipi_csi2.num_data_lanes;
- of_node_put(node);
- return 0;
+err:
+ of_node_put(node);
+ return ret;
}
static int s5pcsis_pm_resume(struct device *dev, bool runtime);
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-mc.h>
#include <media/v4l2-of.h>
#include "isp.h"
case CCDC_INPUT_PARALLEL:
ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_PARALLEL;
ispctrl_val |= parcfg->clk_pol << ISPCTRL_PAR_CLK_POL_SHIFT;
- shift += parcfg->data_lane_shift * 2;
+ shift += parcfg->data_lane_shift;
break;
case CCDC_INPUT_CSI2A:
return IRQ_HANDLED;
}
-/* -----------------------------------------------------------------------------
- * Pipeline power management
- *
- * Entities must be powered up when part of a pipeline that contains at least
- * one open video device node.
- *
- * To achieve this use the entity use_count field to track the number of users.
- * For entities corresponding to video device nodes the use_count field stores
- * the users count of the node. For entities corresponding to subdevs the
- * use_count field stores the total number of users of all video device nodes
- * in the pipeline.
- *
- * The omap3isp_pipeline_pm_use() function must be called in the open() and
- * close() handlers of video device nodes. It increments or decrements the use
- * count of all subdev entities in the pipeline.
- *
- * To react to link management on powered pipelines, the link setup notification
- * callback updates the use count of all entities in the source and sink sides
- * of the link.
- */
-
-/*
- * isp_pipeline_pm_use_count - Count the number of users of a pipeline
- * @entity: The entity
- *
- * Return the total number of users of all video device nodes in the pipeline.
- */
-static int isp_pipeline_pm_use_count(struct media_entity *entity,
- struct media_entity_graph *graph)
-{
- int use = 0;
-
- media_entity_graph_walk_start(graph, entity);
-
- while ((entity = media_entity_graph_walk_next(graph))) {
- if (is_media_entity_v4l2_io(entity))
- use += entity->use_count;
- }
-
- return use;
-}
-
-/*
- * isp_pipeline_pm_power_one - Apply power change to an entity
- * @entity: The entity
- * @change: Use count change
- *
- * Change the entity use count by @change. If the entity is a subdev update its
- * power state by calling the core::s_power operation when the use count goes
- * from 0 to != 0 or from != 0 to 0.
- *
- * Return 0 on success or a negative error code on failure.
- */
-static int isp_pipeline_pm_power_one(struct media_entity *entity, int change)
-{
- struct v4l2_subdev *subdev;
- int ret;
-
- subdev = is_media_entity_v4l2_subdev(entity)
- ? media_entity_to_v4l2_subdev(entity) : NULL;
-
- if (entity->use_count == 0 && change > 0 && subdev != NULL) {
- ret = v4l2_subdev_call(subdev, core, s_power, 1);
- if (ret < 0 && ret != -ENOIOCTLCMD)
- return ret;
- }
-
- entity->use_count += change;
- WARN_ON(entity->use_count < 0);
-
- if (entity->use_count == 0 && change < 0 && subdev != NULL)
- v4l2_subdev_call(subdev, core, s_power, 0);
-
- return 0;
-}
-
-/*
- * isp_pipeline_pm_power - Apply power change to all entities in a pipeline
- * @entity: The entity
- * @change: Use count change
- *
- * Walk the pipeline to update the use count and the power state of all non-node
- * entities.
- *
- * Return 0 on success or a negative error code on failure.
- */
-static int isp_pipeline_pm_power(struct media_entity *entity, int change,
- struct media_entity_graph *graph)
-{
- struct media_entity *first = entity;
- int ret = 0;
-
- if (!change)
- return 0;
-
- media_entity_graph_walk_start(graph, entity);
-
- while (!ret && (entity = media_entity_graph_walk_next(graph)))
- if (is_media_entity_v4l2_subdev(entity))
- ret = isp_pipeline_pm_power_one(entity, change);
-
- if (!ret)
- return ret;
-
- media_entity_graph_walk_start(graph, first);
-
- while ((first = media_entity_graph_walk_next(graph))
- && first != entity)
- if (is_media_entity_v4l2_subdev(first))
- isp_pipeline_pm_power_one(first, -change);
-
- return ret;
-}
-
-/*
- * omap3isp_pipeline_pm_use - Update the use count of an entity
- * @entity: The entity
- * @use: Use (1) or stop using (0) the entity
- *
- * Update the use count of all entities in the pipeline and power entities on or
- * off accordingly.
- *
- * Return 0 on success or a negative error code on failure. Powering entities
- * off is assumed to never fail. No failure can occur when the use parameter is
- * set to 0.
- */
-int omap3isp_pipeline_pm_use(struct media_entity *entity, int use,
- struct media_entity_graph *graph)
-{
- int change = use ? 1 : -1;
- int ret;
-
- mutex_lock(&entity->graph_obj.mdev->graph_mutex);
-
- /* Apply use count to node. */
- entity->use_count += change;
- WARN_ON(entity->use_count < 0);
-
- /* Apply power change to connected non-nodes. */
- ret = isp_pipeline_pm_power(entity, change, graph);
- if (ret < 0)
- entity->use_count -= change;
-
- mutex_unlock(&entity->graph_obj.mdev->graph_mutex);
-
- return ret;
-}
-
-/*
- * isp_pipeline_link_notify - Link management notification callback
- * @link: The link
- * @flags: New link flags that will be applied
- * @notification: The link's state change notification type (MEDIA_DEV_NOTIFY_*)
- *
- * React to link management on powered pipelines by updating the use count of
- * all entities in the source and sink sides of the link. Entities are powered
- * on or off accordingly.
- *
- * Return 0 on success or a negative error code on failure. Powering entities
- * off is assumed to never fail. This function will not fail for disconnection
- * events.
- */
-static int isp_pipeline_link_notify(struct media_link *link, u32 flags,
- unsigned int notification)
-{
- struct media_entity_graph *graph =
- &container_of(link->graph_obj.mdev, struct isp_device,
- media_dev)->pm_count_graph;
- struct media_entity *source = link->source->entity;
- struct media_entity *sink = link->sink->entity;
- int source_use;
- int sink_use;
- int ret = 0;
-
- if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH) {
- ret = media_entity_graph_walk_init(graph,
- link->graph_obj.mdev);
- if (ret)
- return ret;
- }
-
- source_use = isp_pipeline_pm_use_count(source, graph);
- sink_use = isp_pipeline_pm_use_count(sink, graph);
-
- if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&
- !(flags & MEDIA_LNK_FL_ENABLED)) {
- /* Powering off entities is assumed to never fail. */
- isp_pipeline_pm_power(source, -sink_use, graph);
- isp_pipeline_pm_power(sink, -source_use, graph);
- return 0;
- }
-
- if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH &&
- (flags & MEDIA_LNK_FL_ENABLED)) {
-
- ret = isp_pipeline_pm_power(source, sink_use, graph);
- if (ret < 0)
- return ret;
-
- ret = isp_pipeline_pm_power(sink, source_use, graph);
- if (ret < 0)
- isp_pipeline_pm_power(source, -sink_use, graph);
- }
-
- if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH)
- media_entity_graph_walk_cleanup(graph);
-
- return ret;
-}
-
/* -----------------------------------------------------------------------------
* Pipeline stream management
*/
strlcpy(isp->media_dev.model, "TI OMAP3 ISP",
sizeof(isp->media_dev.model));
isp->media_dev.hw_revision = isp->revision;
- isp->media_dev.link_notify = isp_pipeline_link_notify;
+ isp->media_dev.link_notify = v4l2_pipeline_link_notify;
media_device_init(&isp->media_dev);
isp->v4l2_dev.mdev = &isp->media_dev;
struct isp_bus_cfg *buscfg = &isd->bus;
struct v4l2_of_endpoint vep;
unsigned int i;
+ int ret;
- v4l2_of_parse_endpoint(node, &vep);
+ ret = v4l2_of_parse_endpoint(node, &vep);
+ if (ret)
+ return ret;
dev_dbg(dev, "parsing endpoint %s, interface %u\n", node->full_name,
vep.base.port);
}
/* Interrupt */
- isp->irq_num = platform_get_irq(pdev, 0);
- if (isp->irq_num <= 0) {
+ ret = platform_get_irq(pdev, 0);
+ if (ret <= 0) {
dev_err(isp->dev, "No IRQ resource\n");
ret = -ENODEV;
goto error_iommu;
}
+ isp->irq_num = ret;
if (devm_request_irq(isp->dev, isp->irq_num, isp_isr, IRQF_SHARED,
"OMAP3 ISP", isp)) {
{ .compatible = "ti,omap3-isp" },
{ },
};
+MODULE_DEVICE_TABLE(of, omap3isp_of_table);
static struct platform_driver omap3isp_driver = {
.probe = isp_probe,
struct v4l2_device v4l2_dev;
struct v4l2_async_notifier notifier;
struct media_device media_dev;
- struct media_entity_graph pm_count_graph;
struct device *dev;
u32 revision;
void omap3isp_subclk_disable(struct isp_device *isp,
enum isp_subclk_resource res);
-int omap3isp_pipeline_pm_use(struct media_entity *entity, int use,
- struct media_entity_graph *graph);
-
int omap3isp_register_entities(struct platform_device *pdev,
struct v4l2_device *v4l2_dev);
void omap3isp_unregister_entities(struct platform_device *pdev);
&((struct isp_bus_cfg *)
media_entity_to_v4l2_subdev(link->source->entity)
->host_priv)->bus.parallel;
- parallel_shift = parcfg->data_lane_shift * 2;
+ parallel_shift = parcfg->data_lane_shift;
} else {
parallel_shift = 0;
}
struct isp_buffer *buffer;
int restart = 0;
- if (prev->input == PREVIEW_INPUT_MEMORY) {
- buffer = omap3isp_video_buffer_next(&prev->video_in);
- if (buffer != NULL)
- preview_set_inaddr(prev, buffer->dma);
- pipe->state |= ISP_PIPELINE_IDLE_INPUT;
- }
-
if (prev->output & PREVIEW_OUTPUT_MEMORY) {
buffer = omap3isp_video_buffer_next(&prev->video_out);
if (buffer != NULL) {
pipe->state |= ISP_PIPELINE_IDLE_OUTPUT;
}
+ if (prev->input == PREVIEW_INPUT_MEMORY) {
+ buffer = omap3isp_video_buffer_next(&prev->video_in);
+ if (buffer != NULL)
+ preview_set_inaddr(prev, buffer->dma);
+ pipe->state |= ISP_PIPELINE_IDLE_INPUT;
+ }
+
switch (prev->state) {
case ISP_PIPELINE_STREAM_SINGLESHOT:
if (isp_pipeline_ready(pipe))
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
+
#include <media/v4l2-dev.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mc.h>
#include <media/videobuf2-dma-contig.h>
#include "ispvideo.h"
}
}
+/*
+ * omap3isp_video_return_buffers - Return all queued buffers to videobuf2
+ * @video: ISP video object
+ * @state: new state for the returned buffers
+ *
+ * Return all buffers queued on the video node to videobuf2 in the given state.
+ * The buffer state should be VB2_BUF_STATE_QUEUED if called due to an error
+ * when starting the stream, or VB2_BUF_STATE_ERROR otherwise.
+ *
+ * The function must be called with the video irqlock held.
+ */
+static void omap3isp_video_return_buffers(struct isp_video *video,
+ enum vb2_buffer_state state)
+{
+ while (!list_empty(&video->dmaqueue)) {
+ struct isp_buffer *buf;
+
+ buf = list_first_entry(&video->dmaqueue,
+ struct isp_buffer, irqlist);
+ list_del(&buf->irqlist);
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
+ }
+}
+
+static int isp_video_start_streaming(struct vb2_queue *queue,
+ unsigned int count)
+{
+ struct isp_video_fh *vfh = vb2_get_drv_priv(queue);
+ struct isp_video *video = vfh->video;
+ struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity);
+ unsigned long flags;
+ int ret;
+
+ /* In sensor-to-memory mode, the stream can be started synchronously
+ * to the stream on command. In memory-to-memory mode, it will be
+ * started when buffers are queued on both the input and output.
+ */
+ if (pipe->input)
+ return 0;
+
+ ret = omap3isp_pipeline_set_stream(pipe,
+ ISP_PIPELINE_STREAM_CONTINUOUS);
+ if (ret < 0) {
+ spin_lock_irqsave(&video->irqlock, flags);
+ omap3isp_video_return_buffers(video, VB2_BUF_STATE_QUEUED);
+ spin_unlock_irqrestore(&video->irqlock, flags);
+ return ret;
+ }
+
+ spin_lock_irqsave(&video->irqlock, flags);
+ if (list_empty(&video->dmaqueue))
+ video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN;
+ spin_unlock_irqrestore(&video->irqlock, flags);
+
+ return 0;
+}
+
static const struct vb2_ops isp_video_queue_ops = {
.queue_setup = isp_video_queue_setup,
.buf_prepare = isp_video_buffer_prepare,
.buf_queue = isp_video_buffer_queue,
+ .start_streaming = isp_video_start_streaming,
};
/*
struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video)
{
struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity);
- enum isp_pipeline_state state;
+ enum vb2_buffer_state vb_state;
struct isp_buffer *buf;
unsigned long flags;
/* Report pipeline errors to userspace on the capture device side. */
if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->error) {
- state = VB2_BUF_STATE_ERROR;
+ vb_state = VB2_BUF_STATE_ERROR;
pipe->error = false;
} else {
- state = VB2_BUF_STATE_DONE;
+ vb_state = VB2_BUF_STATE_DONE;
}
- vb2_buffer_done(&buf->vb.vb2_buf, state);
+ vb2_buffer_done(&buf->vb.vb2_buf, vb_state);
spin_lock_irqsave(&video->irqlock, flags);
if (list_empty(&video->dmaqueue)) {
+ enum isp_pipeline_state state;
+
spin_unlock_irqrestore(&video->irqlock, flags);
if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
* omap3isp_video_cancel_stream - Cancel stream on a video node
* @video: ISP video object
*
- * Cancelling a stream mark all buffers on the video node as erroneous and makes
- * sure no new buffer can be queued.
+ * Cancelling a stream returns all buffers queued on the video node to videobuf2
+ * in the erroneous state and makes sure no new buffer can be queued.
*/
void omap3isp_video_cancel_stream(struct isp_video *video)
{
unsigned long flags;
spin_lock_irqsave(&video->irqlock, flags);
-
- while (!list_empty(&video->dmaqueue)) {
- struct isp_buffer *buf;
-
- buf = list_first_entry(&video->dmaqueue,
- struct isp_buffer, irqlist);
- list_del(&buf->irqlist);
- vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
- }
-
+ omap3isp_video_return_buffers(video, VB2_BUF_STATE_ERROR);
video->error = true;
-
spin_unlock_irqrestore(&video->irqlock, flags);
}
if (ret < 0)
goto err_check_format;
- /* In sensor-to-memory mode, the stream can be started synchronously
- * to the stream on command. In memory-to-memory mode, it will be
- * started when buffers are queued on both the input and output.
- */
- if (pipe->input == NULL) {
- ret = omap3isp_pipeline_set_stream(pipe,
- ISP_PIPELINE_STREAM_CONTINUOUS);
- if (ret < 0)
- goto err_set_stream;
- spin_lock_irqsave(&video->irqlock, flags);
- if (list_empty(&video->dmaqueue))
- video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN;
- spin_unlock_irqrestore(&video->irqlock, flags);
- }
-
mutex_unlock(&video->stream_lock);
return 0;
-err_set_stream:
- mutex_lock(&video->queue_lock);
- vb2_streamoff(&vfh->queue, type);
- mutex_unlock(&video->queue_lock);
err_check_format:
media_entity_pipeline_stop(&video->video.entity);
err_pipeline_start:
goto done;
}
- ret = media_entity_graph_walk_init(&handle->graph,
- &video->isp->media_dev);
- if (ret)
- goto done;
-
- ret = omap3isp_pipeline_pm_use(&video->video.entity, 1, &handle->graph);
+ ret = v4l2_pipeline_pm_use(&video->video.entity, 1);
if (ret < 0) {
omap3isp_put(video->isp);
goto done;
done:
if (ret < 0) {
v4l2_fh_del(&handle->vfh);
- media_entity_graph_walk_cleanup(&handle->graph);
kfree(handle);
}
vb2_queue_release(&handle->queue);
mutex_unlock(&video->queue_lock);
- omap3isp_pipeline_pm_use(&video->video.entity, 0, &handle->graph);
- media_entity_graph_walk_cleanup(&handle->graph);
+ v4l2_pipeline_pm_use(&video->video.entity, 0);
/* Release the file handle. */
v4l2_fh_del(vfh);
struct vb2_queue queue;
struct v4l2_format format;
struct v4l2_fract timeperframe;
- struct media_entity_graph graph;
};
#define to_isp_video_fh(fh) container_of(fh, struct isp_video_fh, vfh)
* struct isp_parallel_cfg - Parallel interface configuration
* @data_lane_shift: Data lane shifter
* 0 - CAMEXT[13:0] -> CAM[13:0]
- * 1 - CAMEXT[13:2] -> CAM[11:0]
- * 2 - CAMEXT[13:4] -> CAM[9:0]
- * 3 - CAMEXT[13:6] -> CAM[7:0]
+ * 2 - CAMEXT[13:2] -> CAM[11:0]
+ * 4 - CAMEXT[13:4] -> CAM[9:0]
+ * 6 - CAMEXT[13:6] -> CAM[7:0]
* @clk_pol: Pixel clock polarity
* 0 - Sample on rising edge, 1 - Sample on falling edge
* @hs_pol: Horizontal synchronization polarity
* 0 - Normal, 1 - One's complement
*/
struct isp_parallel_cfg {
- unsigned int data_lane_shift:2;
+ unsigned int data_lane_shift:3;
unsigned int clk_pol:1;
unsigned int hs_pol:1;
unsigned int vs_pol:1;
{ .compatible = "renesas,jpu-r8a7791" }, /* M2-W */
{ .compatible = "renesas,jpu-r8a7792" }, /* V2H */
{ .compatible = "renesas,jpu-r8a7793" }, /* M2-N */
+ { .compatible = "renesas,rcar-gen2-jpu" },
{ },
};
MODULE_DEVICE_TABLE(of, jpu_dt_ids);
.default_value = V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_DISABLED,
.menu_skip_mask = 0,
},
+ {
+ .id = V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME,
+ .type = V4L2_CTRL_TYPE_BUTTON,
+ .minimum = 0,
+ .maximum = 0,
+ .step = 0,
+ .default_value = 0,
+ },
{
.id = V4L2_CID_MPEG_VIDEO_VBV_SIZE,
.type = V4L2_CTRL_TYPE_INTEGER,
case V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE:
ctx->force_frame_type = ctrl->val;
break;
+ case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME:
+ ctx->force_frame_type =
+ V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_I_FRAME;
+ break;
case V4L2_CID_MPEG_VIDEO_VBV_SIZE:
p->vbv_size = ctrl->val;
break;
help
This is a generic SoC camera platform driver, useful for testing
-config VIDEO_MX3
- tristate "i.MX3x Camera Sensor Interface driver"
- depends on VIDEO_DEV && MX3_IPU && SOC_CAMERA
- depends on MX3_IPU || COMPILE_TEST
- depends on HAS_DMA
- select VIDEOBUF2_DMA_CONTIG
- ---help---
- This is a v4l2 driver for the i.MX3x Camera Sensor Interface
-
config VIDEO_PXA27x
tristate "PXA27x Quick Capture Interface driver"
depends on VIDEO_DEV && PXA27x && SOC_CAMERA
select VIDEOBUF_DMA_SG
+ select SG_SPLIT
---help---
This is a v4l2 driver for the PXA27x Quick Capture Interface
---help---
This is a v4l2 driver for the SuperH Mobile CEU Interface
-config VIDEO_OMAP1
- tristate "OMAP1 Camera Interface driver"
- depends on VIDEO_DEV && SOC_CAMERA
- depends on ARCH_OMAP1
- depends on HAS_DMA
- select VIDEOBUF_DMA_CONTIG
- select VIDEOBUF_DMA_SG
- ---help---
- This is a v4l2 driver for the TI OMAP1 camera interface
-
-config VIDEO_MX2
- tristate "i.MX27 Camera Sensor Interface driver"
- depends on VIDEO_DEV && SOC_CAMERA
- depends on SOC_IMX27 || COMPILE_TEST
- depends on HAS_DMA
- select VIDEOBUF2_DMA_CONTIG
- ---help---
- This is a v4l2 driver for the i.MX27 Camera Sensor Interface
-
config VIDEO_ATMEL_ISI
tristate "ATMEL Image Sensor Interface (ISI) support"
depends on VIDEO_DEV && SOC_CAMERA
# soc-camera host drivers have to be linked after camera drivers
obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel-isi.o
-obj-$(CONFIG_VIDEO_MX2) += mx2_camera.o
-obj-$(CONFIG_VIDEO_MX3) += mx3_camera.o
-obj-$(CONFIG_VIDEO_OMAP1) += omap1_camera.o
obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o
obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o
obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2) += sh_mobile_csi2.o
static int atmel_isi_probe(struct platform_device *pdev)
{
- unsigned int irq;
+ int irq;
struct atmel_isi *isi;
struct resource *regs;
int ret, i;
isi->width_flags |= 1 << 9;
irq = platform_get_irq(pdev, 0);
- if (IS_ERR_VALUE(irq)) {
+ if (irq < 0) {
ret = irq;
goto err_req_irq;
}
+++ /dev/null
-/*
- * V4L2 Driver for i.MX27 camera host
- *
- * Copyright (C) 2008, Sascha Hauer, Pengutronix
- * Copyright (C) 2010, Baruch Siach, Orex Computed Radiography
- * Copyright (C) 2012, Javier Martin, Vista Silicon S.L.
- *
- * 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.
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/io.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/dma-mapping.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/gcd.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/math64.h>
-#include <linux/mm.h>
-#include <linux/moduleparam.h>
-#include <linux/time.h>
-#include <linux/device.h>
-#include <linux/platform_device.h>
-#include <linux/clk.h>
-
-#include <media/v4l2-common.h>
-#include <media/v4l2-dev.h>
-#include <media/videobuf2-v4l2.h>
-#include <media/videobuf2-dma-contig.h>
-#include <media/soc_camera.h>
-#include <media/drv-intf/soc_mediabus.h>
-
-#include <linux/videodev2.h>
-
-#include <linux/platform_data/media/camera-mx2.h>
-
-#include <asm/dma.h>
-
-#define MX2_CAM_DRV_NAME "mx2-camera"
-#define MX2_CAM_VERSION "0.0.6"
-#define MX2_CAM_DRIVER_DESCRIPTION "i.MX2x_Camera"
-
-/* reset values */
-#define CSICR1_RESET_VAL 0x40000800
-#define CSICR2_RESET_VAL 0x0
-#define CSICR3_RESET_VAL 0x0
-
-/* csi control reg 1 */
-#define CSICR1_SWAP16_EN (1 << 31)
-#define CSICR1_EXT_VSYNC (1 << 30)
-#define CSICR1_EOF_INTEN (1 << 29)
-#define CSICR1_PRP_IF_EN (1 << 28)
-#define CSICR1_CCIR_MODE (1 << 27)
-#define CSICR1_COF_INTEN (1 << 26)
-#define CSICR1_SF_OR_INTEN (1 << 25)
-#define CSICR1_RF_OR_INTEN (1 << 24)
-#define CSICR1_STATFF_LEVEL (3 << 22)
-#define CSICR1_STATFF_INTEN (1 << 21)
-#define CSICR1_RXFF_LEVEL(l) (((l) & 3) << 19)
-#define CSICR1_RXFF_INTEN (1 << 18)
-#define CSICR1_SOF_POL (1 << 17)
-#define CSICR1_SOF_INTEN (1 << 16)
-#define CSICR1_MCLKDIV(d) (((d) & 0xF) << 12)
-#define CSICR1_HSYNC_POL (1 << 11)
-#define CSICR1_CCIR_EN (1 << 10)
-#define CSICR1_MCLKEN (1 << 9)
-#define CSICR1_FCC (1 << 8)
-#define CSICR1_PACK_DIR (1 << 7)
-#define CSICR1_CLR_STATFIFO (1 << 6)
-#define CSICR1_CLR_RXFIFO (1 << 5)
-#define CSICR1_GCLK_MODE (1 << 4)
-#define CSICR1_INV_DATA (1 << 3)
-#define CSICR1_INV_PCLK (1 << 2)
-#define CSICR1_REDGE (1 << 1)
-#define CSICR1_FMT_MASK (CSICR1_PACK_DIR | CSICR1_SWAP16_EN)
-
-#define SHIFT_STATFF_LEVEL 22
-#define SHIFT_RXFF_LEVEL 19
-#define SHIFT_MCLKDIV 12
-
-#define SHIFT_FRMCNT 16
-
-#define CSICR1 0x00
-#define CSICR2 0x04
-#define CSISR 0x08
-#define CSISTATFIFO 0x0c
-#define CSIRFIFO 0x10
-#define CSIRXCNT 0x14
-#define CSICR3 0x1c
-#define CSIDMASA_STATFIFO 0x20
-#define CSIDMATA_STATFIFO 0x24
-#define CSIDMASA_FB1 0x28
-#define CSIDMASA_FB2 0x2c
-#define CSIFBUF_PARA 0x30
-#define CSIIMAG_PARA 0x34
-
-/* EMMA PrP */
-#define PRP_CNTL 0x00
-#define PRP_INTR_CNTL 0x04
-#define PRP_INTRSTATUS 0x08
-#define PRP_SOURCE_Y_PTR 0x0c
-#define PRP_SOURCE_CB_PTR 0x10
-#define PRP_SOURCE_CR_PTR 0x14
-#define PRP_DEST_RGB1_PTR 0x18
-#define PRP_DEST_RGB2_PTR 0x1c
-#define PRP_DEST_Y_PTR 0x20
-#define PRP_DEST_CB_PTR 0x24
-#define PRP_DEST_CR_PTR 0x28
-#define PRP_SRC_FRAME_SIZE 0x2c
-#define PRP_DEST_CH1_LINE_STRIDE 0x30
-#define PRP_SRC_PIXEL_FORMAT_CNTL 0x34
-#define PRP_CH1_PIXEL_FORMAT_CNTL 0x38
-#define PRP_CH1_OUT_IMAGE_SIZE 0x3c
-#define PRP_CH2_OUT_IMAGE_SIZE 0x40
-#define PRP_SRC_LINE_STRIDE 0x44
-#define PRP_CSC_COEF_012 0x48
-#define PRP_CSC_COEF_345 0x4c
-#define PRP_CSC_COEF_678 0x50
-#define PRP_CH1_RZ_HORI_COEF1 0x54
-#define PRP_CH1_RZ_HORI_COEF2 0x58
-#define PRP_CH1_RZ_HORI_VALID 0x5c
-#define PRP_CH1_RZ_VERT_COEF1 0x60
-#define PRP_CH1_RZ_VERT_COEF2 0x64
-#define PRP_CH1_RZ_VERT_VALID 0x68
-#define PRP_CH2_RZ_HORI_COEF1 0x6c
-#define PRP_CH2_RZ_HORI_COEF2 0x70
-#define PRP_CH2_RZ_HORI_VALID 0x74
-#define PRP_CH2_RZ_VERT_COEF1 0x78
-#define PRP_CH2_RZ_VERT_COEF2 0x7c
-#define PRP_CH2_RZ_VERT_VALID 0x80
-
-#define PRP_CNTL_CH1EN (1 << 0)
-#define PRP_CNTL_CH2EN (1 << 1)
-#define PRP_CNTL_CSIEN (1 << 2)
-#define PRP_CNTL_DATA_IN_YUV420 (0 << 3)
-#define PRP_CNTL_DATA_IN_YUV422 (1 << 3)
-#define PRP_CNTL_DATA_IN_RGB16 (2 << 3)
-#define PRP_CNTL_DATA_IN_RGB32 (3 << 3)
-#define PRP_CNTL_CH1_OUT_RGB8 (0 << 5)
-#define PRP_CNTL_CH1_OUT_RGB16 (1 << 5)
-#define PRP_CNTL_CH1_OUT_RGB32 (2 << 5)
-#define PRP_CNTL_CH1_OUT_YUV422 (3 << 5)
-#define PRP_CNTL_CH2_OUT_YUV420 (0 << 7)
-#define PRP_CNTL_CH2_OUT_YUV422 (1 << 7)
-#define PRP_CNTL_CH2_OUT_YUV444 (2 << 7)
-#define PRP_CNTL_CH1_LEN (1 << 9)
-#define PRP_CNTL_CH2_LEN (1 << 10)
-#define PRP_CNTL_SKIP_FRAME (1 << 11)
-#define PRP_CNTL_SWRST (1 << 12)
-#define PRP_CNTL_CLKEN (1 << 13)
-#define PRP_CNTL_WEN (1 << 14)
-#define PRP_CNTL_CH1BYP (1 << 15)
-#define PRP_CNTL_IN_TSKIP(x) ((x) << 16)
-#define PRP_CNTL_CH1_TSKIP(x) ((x) << 19)
-#define PRP_CNTL_CH2_TSKIP(x) ((x) << 22)
-#define PRP_CNTL_INPUT_FIFO_LEVEL(x) ((x) << 25)
-#define PRP_CNTL_RZ_FIFO_LEVEL(x) ((x) << 27)
-#define PRP_CNTL_CH2B1EN (1 << 29)
-#define PRP_CNTL_CH2B2EN (1 << 30)
-#define PRP_CNTL_CH2FEN (1 << 31)
-
-/* IRQ Enable and status register */
-#define PRP_INTR_RDERR (1 << 0)
-#define PRP_INTR_CH1WERR (1 << 1)
-#define PRP_INTR_CH2WERR (1 << 2)
-#define PRP_INTR_CH1FC (1 << 3)
-#define PRP_INTR_CH2FC (1 << 5)
-#define PRP_INTR_LBOVF (1 << 7)
-#define PRP_INTR_CH2OVF (1 << 8)
-
-/* Resizing registers */
-#define PRP_RZ_VALID_TBL_LEN(x) ((x) << 24)
-#define PRP_RZ_VALID_BILINEAR (1 << 31)
-
-#define MAX_VIDEO_MEM 16
-
-#define RESIZE_NUM_MIN 1
-#define RESIZE_NUM_MAX 20
-#define BC_COEF 3
-#define SZ_COEF (1 << BC_COEF)
-
-#define RESIZE_DIR_H 0
-#define RESIZE_DIR_V 1
-
-#define RESIZE_ALGO_BILINEAR 0
-#define RESIZE_ALGO_AVERAGING 1
-
-struct mx2_prp_cfg {
- int channel;
- u32 in_fmt;
- u32 out_fmt;
- u32 src_pixel;
- u32 ch1_pixel;
- u32 irq_flags;
- u32 csicr1;
-};
-
-/* prp resizing parameters */
-struct emma_prp_resize {
- int algo; /* type of algorithm used */
- int len; /* number of coefficients */
- unsigned char s[RESIZE_NUM_MAX]; /* table of coefficients */
-};
-
-/* prp configuration for a client-host fmt pair */
-struct mx2_fmt_cfg {
- u32 in_fmt;
- u32 out_fmt;
- struct mx2_prp_cfg cfg;
-};
-
-struct mx2_buf_internal {
- struct list_head queue;
- int bufnum;
- bool discard;
-};
-
-/* buffer for one video frame */
-struct mx2_buffer {
- /* common v4l buffer stuff -- must be first */
- struct vb2_v4l2_buffer vb;
- struct mx2_buf_internal internal;
-};
-
-enum mx2_camera_type {
- IMX27_CAMERA,
-};
-
-struct mx2_camera_dev {
- struct device *dev;
- struct soc_camera_host soc_host;
- struct clk *clk_emma_ahb, *clk_emma_ipg;
- struct clk *clk_csi_ahb, *clk_csi_per;
-
- void __iomem *base_csi, *base_emma;
-
- struct mx2_camera_platform_data *pdata;
- unsigned long platform_flags;
-
- struct list_head capture;
- struct list_head active_bufs;
- struct list_head discard;
-
- spinlock_t lock;
-
- int dma;
- struct mx2_buffer *active;
- struct mx2_buffer *fb1_active;
- struct mx2_buffer *fb2_active;
-
- u32 csicr1;
- enum mx2_camera_type devtype;
-
- struct mx2_buf_internal buf_discard[2];
- void *discard_buffer;
- dma_addr_t discard_buffer_dma;
- size_t discard_size;
- struct mx2_fmt_cfg *emma_prp;
- struct emma_prp_resize resizing[2];
- unsigned int s_width, s_height;
- u32 frame_count;
- struct vb2_alloc_ctx *alloc_ctx;
-};
-
-static struct platform_device_id mx2_camera_devtype[] = {
- {
- .name = "imx27-camera",
- .driver_data = IMX27_CAMERA,
- }, {
- /* sentinel */
- }
-};
-MODULE_DEVICE_TABLE(platform, mx2_camera_devtype);
-
-static struct mx2_buffer *mx2_ibuf_to_buf(struct mx2_buf_internal *int_buf)
-{
- return container_of(int_buf, struct mx2_buffer, internal);
-}
-
-static struct mx2_fmt_cfg mx27_emma_prp_table[] = {
- /*
- * This is a generic configuration which is valid for most
- * prp input-output format combinations.
- * We set the incoming and outgoing pixelformat to a
- * 16 Bit wide format and adjust the bytesperline
- * accordingly. With this configuration the inputdata
- * will not be changed by the emma and could be any type
- * of 16 Bit Pixelformat.
- */
- {
- .in_fmt = 0,
- .out_fmt = 0,
- .cfg = {
- .channel = 1,
- .in_fmt = PRP_CNTL_DATA_IN_RGB16,
- .out_fmt = PRP_CNTL_CH1_OUT_RGB16,
- .src_pixel = 0x2ca00565, /* RGB565 */
- .ch1_pixel = 0x2ca00565, /* RGB565 */
- .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH1WERR |
- PRP_INTR_CH1FC | PRP_INTR_LBOVF,
- .csicr1 = 0,
- }
- },
- {
- .in_fmt = MEDIA_BUS_FMT_UYVY8_2X8,
- .out_fmt = V4L2_PIX_FMT_YUYV,
- .cfg = {
- .channel = 1,
- .in_fmt = PRP_CNTL_DATA_IN_YUV422,
- .out_fmt = PRP_CNTL_CH1_OUT_YUV422,
- .src_pixel = 0x22000888, /* YUV422 (YUYV) */
- .ch1_pixel = 0x62000888, /* YUV422 (YUYV) */
- .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH1WERR |
- PRP_INTR_CH1FC | PRP_INTR_LBOVF,
- .csicr1 = CSICR1_SWAP16_EN,
- }
- },
- {
- .in_fmt = MEDIA_BUS_FMT_YUYV8_2X8,
- .out_fmt = V4L2_PIX_FMT_YUYV,
- .cfg = {
- .channel = 1,
- .in_fmt = PRP_CNTL_DATA_IN_YUV422,
- .out_fmt = PRP_CNTL_CH1_OUT_YUV422,
- .src_pixel = 0x22000888, /* YUV422 (YUYV) */
- .ch1_pixel = 0x62000888, /* YUV422 (YUYV) */
- .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH1WERR |
- PRP_INTR_CH1FC | PRP_INTR_LBOVF,
- .csicr1 = CSICR1_PACK_DIR,
- }
- },
- {
- .in_fmt = MEDIA_BUS_FMT_YUYV8_2X8,
- .out_fmt = V4L2_PIX_FMT_YUV420,
- .cfg = {
- .channel = 2,
- .in_fmt = PRP_CNTL_DATA_IN_YUV422,
- .out_fmt = PRP_CNTL_CH2_OUT_YUV420,
- .src_pixel = 0x22000888, /* YUV422 (YUYV) */
- .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH2WERR |
- PRP_INTR_CH2FC | PRP_INTR_LBOVF |
- PRP_INTR_CH2OVF,
- .csicr1 = CSICR1_PACK_DIR,
- }
- },
- {
- .in_fmt = MEDIA_BUS_FMT_UYVY8_2X8,
- .out_fmt = V4L2_PIX_FMT_YUV420,
- .cfg = {
- .channel = 2,
- .in_fmt = PRP_CNTL_DATA_IN_YUV422,
- .out_fmt = PRP_CNTL_CH2_OUT_YUV420,
- .src_pixel = 0x22000888, /* YUV422 (YUYV) */
- .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH2WERR |
- PRP_INTR_CH2FC | PRP_INTR_LBOVF |
- PRP_INTR_CH2OVF,
- .csicr1 = CSICR1_SWAP16_EN,
- }
- },
-};
-
-static struct mx2_fmt_cfg *mx27_emma_prp_get_format(u32 in_fmt, u32 out_fmt)
-{
- int i;
-
- for (i = 1; i < ARRAY_SIZE(mx27_emma_prp_table); i++)
- if ((mx27_emma_prp_table[i].in_fmt == in_fmt) &&
- (mx27_emma_prp_table[i].out_fmt == out_fmt)) {
- return &mx27_emma_prp_table[i];
- }
- /* If no match return the most generic configuration */
- return &mx27_emma_prp_table[0];
-};
-
-static void mx27_update_emma_buf(struct mx2_camera_dev *pcdev,
- unsigned long phys, int bufnum)
-{
- struct mx2_fmt_cfg *prp = pcdev->emma_prp;
-
- if (prp->cfg.channel == 1) {
- writel(phys, pcdev->base_emma +
- PRP_DEST_RGB1_PTR + 4 * bufnum);
- } else {
- writel(phys, pcdev->base_emma +
- PRP_DEST_Y_PTR - 0x14 * bufnum);
- if (prp->out_fmt == V4L2_PIX_FMT_YUV420) {
- u32 imgsize = pcdev->soc_host.icd->user_height *
- pcdev->soc_host.icd->user_width;
-
- writel(phys + imgsize, pcdev->base_emma +
- PRP_DEST_CB_PTR - 0x14 * bufnum);
- writel(phys + ((5 * imgsize) / 4), pcdev->base_emma +
- PRP_DEST_CR_PTR - 0x14 * bufnum);
- }
- }
-}
-
-static void mx2_camera_deactivate(struct mx2_camera_dev *pcdev)
-{
- clk_disable_unprepare(pcdev->clk_csi_ahb);
- clk_disable_unprepare(pcdev->clk_csi_per);
- writel(0, pcdev->base_csi + CSICR1);
- writel(0, pcdev->base_emma + PRP_CNTL);
-}
-
-static int mx2_camera_add_device(struct soc_camera_device *icd)
-{
- dev_info(icd->parent, "Camera driver attached to camera %d\n",
- icd->devnum);
-
- return 0;
-}
-
-static void mx2_camera_remove_device(struct soc_camera_device *icd)
-{
- dev_info(icd->parent, "Camera driver detached from camera %d\n",
- icd->devnum);
-}
-
-/*
- * The following two functions absolutely depend on the fact, that
- * there can be only one camera on mx2 camera sensor interface
- */
-static int mx2_camera_clock_start(struct soc_camera_host *ici)
-{
- struct mx2_camera_dev *pcdev = ici->priv;
- int ret;
- u32 csicr1;
-
- ret = clk_prepare_enable(pcdev->clk_csi_ahb);
- if (ret < 0)
- return ret;
-
- ret = clk_prepare_enable(pcdev->clk_csi_per);
- if (ret < 0)
- goto exit_csi_ahb;
-
- csicr1 = CSICR1_MCLKEN | CSICR1_PRP_IF_EN | CSICR1_FCC |
- CSICR1_RXFF_LEVEL(0);
-
- pcdev->csicr1 = csicr1;
- writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
-
- pcdev->frame_count = 0;
-
- return 0;
-
-exit_csi_ahb:
- clk_disable_unprepare(pcdev->clk_csi_ahb);
-
- return ret;
-}
-
-static void mx2_camera_clock_stop(struct soc_camera_host *ici)
-{
- struct mx2_camera_dev *pcdev = ici->priv;
-
- mx2_camera_deactivate(pcdev);
-}
-
-/*
- * Videobuf operations
- */
-static int mx2_videobuf_setup(struct vb2_queue *vq,
- unsigned int *count, unsigned int *num_planes,
- unsigned int sizes[], void *alloc_ctxs[])
-{
- struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx2_camera_dev *pcdev = ici->priv;
-
- dev_dbg(icd->parent, "count=%d, size=%d\n", *count, sizes[0]);
-
- alloc_ctxs[0] = pcdev->alloc_ctx;
-
- sizes[0] = icd->sizeimage;
-
- if (0 == *count)
- *count = 32;
- if (!*num_planes &&
- sizes[0] * *count > MAX_VIDEO_MEM * 1024 * 1024)
- *count = (MAX_VIDEO_MEM * 1024 * 1024) / sizes[0];
-
- *num_planes = 1;
-
- return 0;
-}
-
-static int mx2_videobuf_prepare(struct vb2_buffer *vb)
-{
- struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
- int ret = 0;
-
- dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
- vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
-
-#ifdef DEBUG
- /*
- * This can be useful if you want to see if we actually fill
- * the buffer with something
- */
- memset((void *)vb2_plane_vaddr(vb, 0),
- 0xaa, vb2_get_plane_payload(vb, 0));
-#endif
-
- vb2_set_plane_payload(vb, 0, icd->sizeimage);
- if (vb2_plane_vaddr(vb, 0) &&
- vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) {
- ret = -EINVAL;
- goto out;
- }
-
- return 0;
-
-out:
- return ret;
-}
-
-static void mx2_videobuf_queue(struct vb2_buffer *vb)
-{
- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->parent);
- struct mx2_camera_dev *pcdev = ici->priv;
- struct mx2_buffer *buf = container_of(vbuf, struct mx2_buffer, vb);
- unsigned long flags;
-
- dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
- vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
-
- spin_lock_irqsave(&pcdev->lock, flags);
-
- list_add_tail(&buf->internal.queue, &pcdev->capture);
-
- spin_unlock_irqrestore(&pcdev->lock, flags);
-}
-
-static void mx27_camera_emma_buf_init(struct soc_camera_device *icd,
- int bytesperline)
-{
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->parent);
- struct mx2_camera_dev *pcdev = ici->priv;
- struct mx2_fmt_cfg *prp = pcdev->emma_prp;
-
- writel((pcdev->s_width << 16) | pcdev->s_height,
- pcdev->base_emma + PRP_SRC_FRAME_SIZE);
- writel(prp->cfg.src_pixel,
- pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL);
- if (prp->cfg.channel == 1) {
- writel((icd->user_width << 16) | icd->user_height,
- pcdev->base_emma + PRP_CH1_OUT_IMAGE_SIZE);
- writel(bytesperline,
- pcdev->base_emma + PRP_DEST_CH1_LINE_STRIDE);
- writel(prp->cfg.ch1_pixel,
- pcdev->base_emma + PRP_CH1_PIXEL_FORMAT_CNTL);
- } else { /* channel 2 */
- writel((icd->user_width << 16) | icd->user_height,
- pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE);
- }
-
- /* Enable interrupts */
- writel(prp->cfg.irq_flags, pcdev->base_emma + PRP_INTR_CNTL);
-}
-
-static void mx2_prp_resize_commit(struct mx2_camera_dev *pcdev)
-{
- int dir;
-
- for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) {
- unsigned char *s = pcdev->resizing[dir].s;
- int len = pcdev->resizing[dir].len;
- unsigned int coeff[2] = {0, 0};
- unsigned int valid = 0;
- int i;
-
- if (len == 0)
- continue;
-
- for (i = RESIZE_NUM_MAX - 1; i >= 0; i--) {
- int j;
-
- j = i > 9 ? 1 : 0;
- coeff[j] = (coeff[j] << BC_COEF) |
- (s[i] & (SZ_COEF - 1));
-
- if (i == 5 || i == 15)
- coeff[j] <<= 1;
-
- valid = (valid << 1) | (s[i] >> BC_COEF);
- }
-
- valid |= PRP_RZ_VALID_TBL_LEN(len);
-
- if (pcdev->resizing[dir].algo == RESIZE_ALGO_BILINEAR)
- valid |= PRP_RZ_VALID_BILINEAR;
-
- if (pcdev->emma_prp->cfg.channel == 1) {
- if (dir == RESIZE_DIR_H) {
- writel(coeff[0], pcdev->base_emma +
- PRP_CH1_RZ_HORI_COEF1);
- writel(coeff[1], pcdev->base_emma +
- PRP_CH1_RZ_HORI_COEF2);
- writel(valid, pcdev->base_emma +
- PRP_CH1_RZ_HORI_VALID);
- } else {
- writel(coeff[0], pcdev->base_emma +
- PRP_CH1_RZ_VERT_COEF1);
- writel(coeff[1], pcdev->base_emma +
- PRP_CH1_RZ_VERT_COEF2);
- writel(valid, pcdev->base_emma +
- PRP_CH1_RZ_VERT_VALID);
- }
- } else {
- if (dir == RESIZE_DIR_H) {
- writel(coeff[0], pcdev->base_emma +
- PRP_CH2_RZ_HORI_COEF1);
- writel(coeff[1], pcdev->base_emma +
- PRP_CH2_RZ_HORI_COEF2);
- writel(valid, pcdev->base_emma +
- PRP_CH2_RZ_HORI_VALID);
- } else {
- writel(coeff[0], pcdev->base_emma +
- PRP_CH2_RZ_VERT_COEF1);
- writel(coeff[1], pcdev->base_emma +
- PRP_CH2_RZ_VERT_COEF2);
- writel(valid, pcdev->base_emma +
- PRP_CH2_RZ_VERT_VALID);
- }
- }
- }
-}
-
-static int mx2_start_streaming(struct vb2_queue *q, unsigned int count)
-{
- struct soc_camera_device *icd = soc_camera_from_vb2q(q);
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->parent);
- struct mx2_camera_dev *pcdev = ici->priv;
- struct mx2_fmt_cfg *prp = pcdev->emma_prp;
- struct vb2_buffer *vb;
- struct mx2_buffer *buf;
- unsigned long phys;
- int bytesperline;
- unsigned long flags;
-
- if (count < 2)
- return -ENOBUFS;
-
- spin_lock_irqsave(&pcdev->lock, flags);
-
- buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
- internal.queue);
- buf->internal.bufnum = 0;
- vb = &buf->vb.vb2_buf;
-
- phys = vb2_dma_contig_plane_dma_addr(vb, 0);
- mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum);
- list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
-
- buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
- internal.queue);
- buf->internal.bufnum = 1;
- vb = &buf->vb.vb2_buf;
-
- phys = vb2_dma_contig_plane_dma_addr(vb, 0);
- mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum);
- list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
-
- bytesperline = soc_mbus_bytes_per_line(icd->user_width,
- icd->current_fmt->host_fmt);
- if (bytesperline < 0) {
- spin_unlock_irqrestore(&pcdev->lock, flags);
- return bytesperline;
- }
-
- /*
- * I didn't manage to properly enable/disable the prp
- * on a per frame basis during running transfers,
- * thus we allocate a buffer here and use it to
- * discard frames when no buffer is available.
- * Feel free to work on this ;)
- */
- pcdev->discard_size = icd->user_height * bytesperline;
- pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev,
- pcdev->discard_size,
- &pcdev->discard_buffer_dma, GFP_ATOMIC);
- if (!pcdev->discard_buffer) {
- spin_unlock_irqrestore(&pcdev->lock, flags);
- return -ENOMEM;
- }
-
- pcdev->buf_discard[0].discard = true;
- list_add_tail(&pcdev->buf_discard[0].queue,
- &pcdev->discard);
-
- pcdev->buf_discard[1].discard = true;
- list_add_tail(&pcdev->buf_discard[1].queue,
- &pcdev->discard);
-
- mx2_prp_resize_commit(pcdev);
-
- mx27_camera_emma_buf_init(icd, bytesperline);
-
- if (prp->cfg.channel == 1) {
- writel(PRP_CNTL_CH1EN |
- PRP_CNTL_CSIEN |
- prp->cfg.in_fmt |
- prp->cfg.out_fmt |
- PRP_CNTL_CH1_LEN |
- PRP_CNTL_CH1BYP |
- PRP_CNTL_CH1_TSKIP(0) |
- PRP_CNTL_IN_TSKIP(0),
- pcdev->base_emma + PRP_CNTL);
- } else {
- writel(PRP_CNTL_CH2EN |
- PRP_CNTL_CSIEN |
- prp->cfg.in_fmt |
- prp->cfg.out_fmt |
- PRP_CNTL_CH2_LEN |
- PRP_CNTL_CH2_TSKIP(0) |
- PRP_CNTL_IN_TSKIP(0),
- pcdev->base_emma + PRP_CNTL);
- }
- spin_unlock_irqrestore(&pcdev->lock, flags);
-
- return 0;
-}
-
-static void mx2_stop_streaming(struct vb2_queue *q)
-{
- struct soc_camera_device *icd = soc_camera_from_vb2q(q);
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->parent);
- struct mx2_camera_dev *pcdev = ici->priv;
- struct mx2_fmt_cfg *prp = pcdev->emma_prp;
- unsigned long flags;
- void *b;
- u32 cntl;
-
- spin_lock_irqsave(&pcdev->lock, flags);
-
- cntl = readl(pcdev->base_emma + PRP_CNTL);
- if (prp->cfg.channel == 1) {
- writel(cntl & ~PRP_CNTL_CH1EN,
- pcdev->base_emma + PRP_CNTL);
- } else {
- writel(cntl & ~PRP_CNTL_CH2EN,
- pcdev->base_emma + PRP_CNTL);
- }
- INIT_LIST_HEAD(&pcdev->capture);
- INIT_LIST_HEAD(&pcdev->active_bufs);
- INIT_LIST_HEAD(&pcdev->discard);
-
- b = pcdev->discard_buffer;
- pcdev->discard_buffer = NULL;
-
- spin_unlock_irqrestore(&pcdev->lock, flags);
-
- dma_free_coherent(ici->v4l2_dev.dev,
- pcdev->discard_size, b, pcdev->discard_buffer_dma);
-}
-
-static struct vb2_ops mx2_videobuf_ops = {
- .queue_setup = mx2_videobuf_setup,
- .buf_prepare = mx2_videobuf_prepare,
- .buf_queue = mx2_videobuf_queue,
- .start_streaming = mx2_start_streaming,
- .stop_streaming = mx2_stop_streaming,
-};
-
-static int mx2_camera_init_videobuf(struct vb2_queue *q,
- struct soc_camera_device *icd)
-{
- q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- q->io_modes = VB2_MMAP | VB2_USERPTR;
- q->drv_priv = icd;
- q->ops = &mx2_videobuf_ops;
- q->mem_ops = &vb2_dma_contig_memops;
- q->buf_struct_size = sizeof(struct mx2_buffer);
- q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
-
- return vb2_queue_init(q);
-}
-
-#define MX2_BUS_FLAGS (V4L2_MBUS_MASTER | \
- V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
- V4L2_MBUS_VSYNC_ACTIVE_LOW | \
- V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
- V4L2_MBUS_HSYNC_ACTIVE_LOW | \
- V4L2_MBUS_PCLK_SAMPLE_RISING | \
- V4L2_MBUS_PCLK_SAMPLE_FALLING | \
- V4L2_MBUS_DATA_ACTIVE_HIGH | \
- V4L2_MBUS_DATA_ACTIVE_LOW)
-
-static int mx27_camera_emma_prp_reset(struct mx2_camera_dev *pcdev)
-{
- int count = 0;
-
- readl(pcdev->base_emma + PRP_CNTL);
- writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL);
- while (count++ < 100) {
- if (!(readl(pcdev->base_emma + PRP_CNTL) & PRP_CNTL_SWRST))
- return 0;
- barrier();
- udelay(1);
- }
-
- return -ETIMEDOUT;
-}
-
-static int mx2_camera_set_bus_param(struct soc_camera_device *icd)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx2_camera_dev *pcdev = ici->priv;
- struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
- unsigned long common_flags;
- int ret;
- int bytesperline;
- u32 csicr1 = pcdev->csicr1;
-
- ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
- if (!ret) {
- common_flags = soc_mbus_config_compatible(&cfg, MX2_BUS_FLAGS);
- if (!common_flags) {
- dev_warn(icd->parent,
- "Flags incompatible: camera 0x%x, host 0x%x\n",
- cfg.flags, MX2_BUS_FLAGS);
- return -EINVAL;
- }
- } else if (ret != -ENOIOCTLCMD) {
- return ret;
- } else {
- common_flags = MX2_BUS_FLAGS;
- }
-
- if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
- (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
- if (pcdev->platform_flags & MX2_CAMERA_HSYNC_HIGH)
- common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
- else
- common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
- }
-
- if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
- (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
- if (pcdev->platform_flags & MX2_CAMERA_PCLK_SAMPLE_RISING)
- common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
- else
- common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
- }
-
- cfg.flags = common_flags;
- ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
- if (ret < 0 && ret != -ENOIOCTLCMD) {
- dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n",
- common_flags, ret);
- return ret;
- }
-
- csicr1 = (csicr1 & ~CSICR1_FMT_MASK) | pcdev->emma_prp->cfg.csicr1;
-
- if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
- csicr1 |= CSICR1_REDGE;
- if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
- csicr1 |= CSICR1_SOF_POL;
- if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
- csicr1 |= CSICR1_HSYNC_POL;
- if (pcdev->platform_flags & MX2_CAMERA_EXT_VSYNC)
- csicr1 |= CSICR1_EXT_VSYNC;
- if (pcdev->platform_flags & MX2_CAMERA_CCIR)
- csicr1 |= CSICR1_CCIR_EN;
- if (pcdev->platform_flags & MX2_CAMERA_CCIR_INTERLACE)
- csicr1 |= CSICR1_CCIR_MODE;
- if (pcdev->platform_flags & MX2_CAMERA_GATED_CLOCK)
- csicr1 |= CSICR1_GCLK_MODE;
- if (pcdev->platform_flags & MX2_CAMERA_INV_DATA)
- csicr1 |= CSICR1_INV_DATA;
-
- pcdev->csicr1 = csicr1;
-
- bytesperline = soc_mbus_bytes_per_line(icd->user_width,
- icd->current_fmt->host_fmt);
- if (bytesperline < 0)
- return bytesperline;
-
- ret = mx27_camera_emma_prp_reset(pcdev);
- if (ret)
- return ret;
-
- writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
-
- return 0;
-}
-
-static int mx2_camera_set_crop(struct soc_camera_device *icd,
- const struct v4l2_crop *a)
-{
- struct v4l2_crop a_writable = *a;
- struct v4l2_rect *rect = &a_writable.c;
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct v4l2_subdev_format fmt = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- };
- struct v4l2_mbus_framefmt *mf = &fmt.format;
- int ret;
-
- soc_camera_limit_side(&rect->left, &rect->width, 0, 2, 4096);
- soc_camera_limit_side(&rect->top, &rect->height, 0, 2, 4096);
-
- ret = v4l2_subdev_call(sd, video, s_crop, a);
- if (ret < 0)
- return ret;
-
- /* The capture device might have changed its output */
- ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt);
- if (ret < 0)
- return ret;
-
- dev_dbg(icd->parent, "Sensor cropped %dx%d\n",
- mf->width, mf->height);
-
- icd->user_width = mf->width;
- icd->user_height = mf->height;
-
- return ret;
-}
-
-static int mx2_camera_get_formats(struct soc_camera_device *icd,
- unsigned int idx,
- struct soc_camera_format_xlate *xlate)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- const struct soc_mbus_pixelfmt *fmt;
- struct device *dev = icd->parent;
- struct v4l2_subdev_mbus_code_enum code = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- .index = idx,
- };
- int ret, formats = 0;
-
- ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code);
- if (ret < 0)
- /* no more formats */
- return 0;
-
- fmt = soc_mbus_get_fmtdesc(code.code);
- if (!fmt) {
- dev_err(dev, "Invalid format code #%u: %d\n", idx, code.code);
- return 0;
- }
-
- if (code.code == MEDIA_BUS_FMT_YUYV8_2X8 ||
- code.code == MEDIA_BUS_FMT_UYVY8_2X8) {
- formats++;
- if (xlate) {
- /*
- * CH2 can output YUV420 which is a standard format in
- * soc_mediabus.c
- */
- xlate->host_fmt =
- soc_mbus_get_fmtdesc(MEDIA_BUS_FMT_YUYV8_1_5X8);
- xlate->code = code.code;
- dev_dbg(dev, "Providing host format %s for sensor code %d\n",
- xlate->host_fmt->name, code.code);
- xlate++;
- }
- }
-
- if (code.code == MEDIA_BUS_FMT_UYVY8_2X8) {
- formats++;
- if (xlate) {
- xlate->host_fmt =
- soc_mbus_get_fmtdesc(MEDIA_BUS_FMT_YUYV8_2X8);
- xlate->code = code.code;
- dev_dbg(dev, "Providing host format %s for sensor code %d\n",
- xlate->host_fmt->name, code.code);
- xlate++;
- }
- }
-
- /* Generic pass-trough */
- formats++;
- if (xlate) {
- xlate->host_fmt = fmt;
- xlate->code = code.code;
- xlate++;
- }
- return formats;
-}
-
-static int mx2_emmaprp_resize(struct mx2_camera_dev *pcdev,
- struct v4l2_mbus_framefmt *mf_in,
- struct v4l2_pix_format *pix_out, bool apply)
-{
- unsigned int num, den;
- unsigned long m;
- int i, dir;
-
- for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) {
- struct emma_prp_resize tmprsz;
- unsigned char *s = tmprsz.s;
- int len = 0;
- int in, out;
-
- if (dir == RESIZE_DIR_H) {
- in = mf_in->width;
- out = pix_out->width;
- } else {
- in = mf_in->height;
- out = pix_out->height;
- }
-
- if (in < out)
- return -EINVAL;
- else if (in == out)
- continue;
-
- /* Calculate ratio */
- m = gcd(in, out);
- num = in / m;
- den = out / m;
- if (num > RESIZE_NUM_MAX)
- return -EINVAL;
-
- if ((num >= 2 * den) && (den == 1) &&
- (num < 9) && (!(num & 0x01))) {
- int sum = 0;
- int j;
-
- /* Average scaling for >= 2:1 ratios */
- /* Support can be added for num >=9 and odd values */
-
- tmprsz.algo = RESIZE_ALGO_AVERAGING;
- len = num;
-
- for (i = 0; i < (len / 2); i++)
- s[i] = 8;
-
- do {
- for (i = 0; i < (len / 2); i++) {
- s[i] = s[i] >> 1;
- sum = 0;
- for (j = 0; j < (len / 2); j++)
- sum += s[j];
- if (sum == 4)
- break;
- }
- } while (sum != 4);
-
- for (i = (len / 2); i < len; i++)
- s[i] = s[len - i - 1];
-
- s[len - 1] |= SZ_COEF;
- } else {
- /* bilinear scaling for < 2:1 ratios */
- int v; /* overflow counter */
- int coeff, nxt; /* table output */
- int in_pos_inc = 2 * den;
- int out_pos = num;
- int out_pos_inc = 2 * num;
- int init_carry = num - den;
- int carry = init_carry;
-
- tmprsz.algo = RESIZE_ALGO_BILINEAR;
- v = den + in_pos_inc;
- do {
- coeff = v - out_pos;
- out_pos += out_pos_inc;
- carry += out_pos_inc;
- for (nxt = 0; v < out_pos; nxt++) {
- v += in_pos_inc;
- carry -= in_pos_inc;
- }
-
- if (len > RESIZE_NUM_MAX)
- return -EINVAL;
-
- coeff = ((coeff << BC_COEF) +
- (in_pos_inc >> 1)) / in_pos_inc;
-
- if (coeff >= (SZ_COEF - 1))
- coeff--;
-
- coeff |= SZ_COEF;
- s[len] = (unsigned char)coeff;
- len++;
-
- for (i = 1; i < nxt; i++) {
- if (len >= RESIZE_NUM_MAX)
- return -EINVAL;
- s[len] = 0;
- len++;
- }
- } while (carry != init_carry);
- }
- tmprsz.len = len;
- if (dir == RESIZE_DIR_H)
- mf_in->width = pix_out->width;
- else
- mf_in->height = pix_out->height;
-
- if (apply)
- memcpy(&pcdev->resizing[dir], &tmprsz, sizeof(tmprsz));
- }
- return 0;
-}
-
-static int mx2_camera_set_fmt(struct soc_camera_device *icd,
- struct v4l2_format *f)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx2_camera_dev *pcdev = ici->priv;
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- const struct soc_camera_format_xlate *xlate;
- struct v4l2_pix_format *pix = &f->fmt.pix;
- struct v4l2_subdev_format format = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- };
- struct v4l2_mbus_framefmt *mf = &format.format;
- int ret;
-
- dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n",
- __func__, pix->width, pix->height);
-
- xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
- if (!xlate) {
- dev_warn(icd->parent, "Format %x not found\n",
- pix->pixelformat);
- return -EINVAL;
- }
-
- mf->width = pix->width;
- mf->height = pix->height;
- mf->field = pix->field;
- mf->colorspace = pix->colorspace;
- mf->code = xlate->code;
-
- ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &format);
- if (ret < 0 && ret != -ENOIOCTLCMD)
- return ret;
-
- /* Store width and height returned by the sensor for resizing */
- pcdev->s_width = mf->width;
- pcdev->s_height = mf->height;
- dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n",
- __func__, pcdev->s_width, pcdev->s_height);
-
- pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code,
- xlate->host_fmt->fourcc);
-
- memset(pcdev->resizing, 0, sizeof(pcdev->resizing));
- if ((mf->width != pix->width || mf->height != pix->height) &&
- pcdev->emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) {
- if (mx2_emmaprp_resize(pcdev, mf, pix, true) < 0)
- dev_dbg(icd->parent, "%s: can't resize\n", __func__);
- }
-
- if (mf->code != xlate->code)
- return -EINVAL;
-
- pix->width = mf->width;
- pix->height = mf->height;
- pix->field = mf->field;
- pix->colorspace = mf->colorspace;
- icd->current_fmt = xlate;
-
- dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n",
- __func__, pix->width, pix->height);
-
- return 0;
-}
-
-static int mx2_camera_try_fmt(struct soc_camera_device *icd,
- struct v4l2_format *f)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- const struct soc_camera_format_xlate *xlate;
- struct v4l2_pix_format *pix = &f->fmt.pix;
- struct v4l2_subdev_pad_config pad_cfg;
- struct v4l2_subdev_format format = {
- .which = V4L2_SUBDEV_FORMAT_TRY,
- };
- struct v4l2_mbus_framefmt *mf = &format.format;
- __u32 pixfmt = pix->pixelformat;
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx2_camera_dev *pcdev = ici->priv;
- struct mx2_fmt_cfg *emma_prp;
- int ret;
-
- dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n",
- __func__, pix->width, pix->height);
-
- xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
- if (pixfmt && !xlate) {
- dev_warn(icd->parent, "Format %x not found\n", pixfmt);
- return -EINVAL;
- }
-
- /*
- * limit to MX27 hardware capabilities: width must be a multiple of 8 as
- * requested by the CSI. (Table 39-2 in the i.MX27 Reference Manual).
- */
- pix->width &= ~0x7;
-
- /* limit to sensor capabilities */
- mf->width = pix->width;
- mf->height = pix->height;
- mf->field = pix->field;
- mf->colorspace = pix->colorspace;
- mf->code = xlate->code;
-
- ret = v4l2_subdev_call(sd, pad, set_fmt, &pad_cfg, &format);
- if (ret < 0)
- return ret;
-
- dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n",
- __func__, pcdev->s_width, pcdev->s_height);
-
- /* If the sensor does not support image size try PrP resizing */
- emma_prp = mx27_emma_prp_get_format(xlate->code,
- xlate->host_fmt->fourcc);
-
- if ((mf->width != pix->width || mf->height != pix->height) &&
- emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) {
- if (mx2_emmaprp_resize(pcdev, mf, pix, false) < 0)
- dev_dbg(icd->parent, "%s: can't resize\n", __func__);
- }
-
- if (mf->field == V4L2_FIELD_ANY)
- mf->field = V4L2_FIELD_NONE;
- /*
- * Driver supports interlaced images provided they have
- * both fields so that they can be processed as if they
- * were progressive.
- */
- if (mf->field != V4L2_FIELD_NONE && !V4L2_FIELD_HAS_BOTH(mf->field)) {
- dev_err(icd->parent, "Field type %d unsupported.\n",
- mf->field);
- return -EINVAL;
- }
-
- pix->width = mf->width;
- pix->height = mf->height;
- pix->field = mf->field;
- pix->colorspace = mf->colorspace;
-
- dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n",
- __func__, pix->width, pix->height);
-
- return 0;
-}
-
-static int mx2_camera_querycap(struct soc_camera_host *ici,
- struct v4l2_capability *cap)
-{
- /* cap->name is set by the friendly caller:-> */
- strlcpy(cap->card, MX2_CAM_DRIVER_DESCRIPTION, sizeof(cap->card));
- cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
- cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
-
- return 0;
-}
-
-static unsigned int mx2_camera_poll(struct file *file, poll_table *pt)
-{
- struct soc_camera_device *icd = file->private_data;
-
- return vb2_poll(&icd->vb2_vidq, file, pt);
-}
-
-static struct soc_camera_host_ops mx2_soc_camera_host_ops = {
- .owner = THIS_MODULE,
- .add = mx2_camera_add_device,
- .remove = mx2_camera_remove_device,
- .clock_start = mx2_camera_clock_start,
- .clock_stop = mx2_camera_clock_stop,
- .set_fmt = mx2_camera_set_fmt,
- .set_crop = mx2_camera_set_crop,
- .get_formats = mx2_camera_get_formats,
- .try_fmt = mx2_camera_try_fmt,
- .init_videobuf2 = mx2_camera_init_videobuf,
- .poll = mx2_camera_poll,
- .querycap = mx2_camera_querycap,
- .set_bus_param = mx2_camera_set_bus_param,
-};
-
-static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev,
- int bufnum, bool err)
-{
-#ifdef DEBUG
- struct mx2_fmt_cfg *prp = pcdev->emma_prp;
-#endif
- struct mx2_buf_internal *ibuf;
- struct mx2_buffer *buf;
- struct vb2_buffer *vb;
- struct vb2_v4l2_buffer *vbuf;
- unsigned long phys;
-
- ibuf = list_first_entry(&pcdev->active_bufs, struct mx2_buf_internal,
- queue);
-
- BUG_ON(ibuf->bufnum != bufnum);
-
- if (ibuf->discard) {
- /*
- * Discard buffer must not be returned to user space.
- * Just return it to the discard queue.
- */
- list_move_tail(pcdev->active_bufs.next, &pcdev->discard);
- } else {
- buf = mx2_ibuf_to_buf(ibuf);
-
- vb = &buf->vb.vb2_buf;
- vbuf = to_vb2_v4l2_buffer(vb);
-#ifdef DEBUG
- phys = vb2_dma_contig_plane_dma_addr(vb, 0);
- if (prp->cfg.channel == 1) {
- if (readl(pcdev->base_emma + PRP_DEST_RGB1_PTR +
- 4 * bufnum) != phys) {
- dev_err(pcdev->dev, "%lx != %x\n", phys,
- readl(pcdev->base_emma +
- PRP_DEST_RGB1_PTR + 4 * bufnum));
- }
- } else {
- if (readl(pcdev->base_emma + PRP_DEST_Y_PTR -
- 0x14 * bufnum) != phys) {
- dev_err(pcdev->dev, "%lx != %x\n", phys,
- readl(pcdev->base_emma +
- PRP_DEST_Y_PTR - 0x14 * bufnum));
- }
- }
-#endif
- dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__, vb,
- vb2_plane_vaddr(vb, 0),
- vb2_get_plane_payload(vb, 0));
-
- list_del_init(&buf->internal.queue);
- vb->timestamp = ktime_get_ns();
- vbuf->sequence = pcdev->frame_count;
- if (err)
- vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
- else
- vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
- }
-
- pcdev->frame_count++;
-
- if (list_empty(&pcdev->capture)) {
- if (list_empty(&pcdev->discard)) {
- dev_warn(pcdev->dev, "%s: trying to access empty discard list\n",
- __func__);
- return;
- }
-
- ibuf = list_first_entry(&pcdev->discard,
- struct mx2_buf_internal, queue);
- ibuf->bufnum = bufnum;
-
- list_move_tail(pcdev->discard.next, &pcdev->active_bufs);
- mx27_update_emma_buf(pcdev, pcdev->discard_buffer_dma, bufnum);
- return;
- }
-
- buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
- internal.queue);
-
- buf->internal.bufnum = bufnum;
-
- list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
-
- vb = &buf->vb.vb2_buf;
-
- phys = vb2_dma_contig_plane_dma_addr(vb, 0);
- mx27_update_emma_buf(pcdev, phys, bufnum);
-}
-
-static irqreturn_t mx27_camera_emma_irq(int irq_emma, void *data)
-{
- struct mx2_camera_dev *pcdev = data;
- unsigned int status = readl(pcdev->base_emma + PRP_INTRSTATUS);
- struct mx2_buf_internal *ibuf;
-
- spin_lock(&pcdev->lock);
-
- if (list_empty(&pcdev->active_bufs)) {
- dev_warn(pcdev->dev, "%s: called while active list is empty\n",
- __func__);
-
- if (!status) {
- spin_unlock(&pcdev->lock);
- return IRQ_NONE;
- }
- }
-
- if (status & (1 << 7)) { /* overflow */
- u32 cntl = readl(pcdev->base_emma + PRP_CNTL);
- writel(cntl & ~(PRP_CNTL_CH1EN | PRP_CNTL_CH2EN),
- pcdev->base_emma + PRP_CNTL);
- writel(cntl, pcdev->base_emma + PRP_CNTL);
-
- ibuf = list_first_entry(&pcdev->active_bufs,
- struct mx2_buf_internal, queue);
- mx27_camera_frame_done_emma(pcdev,
- ibuf->bufnum, true);
-
- status &= ~(1 << 7);
- } else if (((status & (3 << 5)) == (3 << 5)) ||
- ((status & (3 << 3)) == (3 << 3))) {
- /*
- * Both buffers have triggered, process the one we're expecting
- * to first
- */
- ibuf = list_first_entry(&pcdev->active_bufs,
- struct mx2_buf_internal, queue);
- mx27_camera_frame_done_emma(pcdev, ibuf->bufnum, false);
- status &= ~(1 << (6 - ibuf->bufnum)); /* mark processed */
- } else if ((status & (1 << 6)) || (status & (1 << 4))) {
- mx27_camera_frame_done_emma(pcdev, 0, false);
- } else if ((status & (1 << 5)) || (status & (1 << 3))) {
- mx27_camera_frame_done_emma(pcdev, 1, false);
- }
-
- spin_unlock(&pcdev->lock);
- writel(status, pcdev->base_emma + PRP_INTRSTATUS);
-
- return IRQ_HANDLED;
-}
-
-static int mx27_camera_emma_init(struct platform_device *pdev)
-{
- struct mx2_camera_dev *pcdev = platform_get_drvdata(pdev);
- struct resource *res_emma;
- int irq_emma;
- int err = 0;
-
- res_emma = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- irq_emma = platform_get_irq(pdev, 1);
- if (!res_emma || !irq_emma) {
- dev_err(pcdev->dev, "no EMMA resources\n");
- err = -ENODEV;
- goto out;
- }
-
- pcdev->base_emma = devm_ioremap_resource(pcdev->dev, res_emma);
- if (IS_ERR(pcdev->base_emma)) {
- err = PTR_ERR(pcdev->base_emma);
- goto out;
- }
-
- err = devm_request_irq(pcdev->dev, irq_emma, mx27_camera_emma_irq, 0,
- MX2_CAM_DRV_NAME, pcdev);
- if (err) {
- dev_err(pcdev->dev, "Camera EMMA interrupt register failed\n");
- goto out;
- }
-
- pcdev->clk_emma_ipg = devm_clk_get(pcdev->dev, "emma-ipg");
- if (IS_ERR(pcdev->clk_emma_ipg)) {
- err = PTR_ERR(pcdev->clk_emma_ipg);
- goto out;
- }
-
- clk_prepare_enable(pcdev->clk_emma_ipg);
-
- pcdev->clk_emma_ahb = devm_clk_get(pcdev->dev, "emma-ahb");
- if (IS_ERR(pcdev->clk_emma_ahb)) {
- err = PTR_ERR(pcdev->clk_emma_ahb);
- goto exit_clk_emma_ipg;
- }
-
- clk_prepare_enable(pcdev->clk_emma_ahb);
-
- err = mx27_camera_emma_prp_reset(pcdev);
- if (err)
- goto exit_clk_emma_ahb;
-
- return err;
-
-exit_clk_emma_ahb:
- clk_disable_unprepare(pcdev->clk_emma_ahb);
-exit_clk_emma_ipg:
- clk_disable_unprepare(pcdev->clk_emma_ipg);
-out:
- return err;
-}
-
-static int mx2_camera_probe(struct platform_device *pdev)
-{
- struct mx2_camera_dev *pcdev;
- struct resource *res_csi;
- int irq_csi;
- int err = 0;
-
- dev_dbg(&pdev->dev, "initialising\n");
-
- res_csi = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- irq_csi = platform_get_irq(pdev, 0);
- if (res_csi == NULL || irq_csi < 0) {
- dev_err(&pdev->dev, "Missing platform resources data\n");
- err = -ENODEV;
- goto exit;
- }
-
- pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL);
- if (!pcdev) {
- dev_err(&pdev->dev, "Could not allocate pcdev\n");
- err = -ENOMEM;
- goto exit;
- }
-
- pcdev->clk_csi_ahb = devm_clk_get(&pdev->dev, "ahb");
- if (IS_ERR(pcdev->clk_csi_ahb)) {
- dev_err(&pdev->dev, "Could not get csi ahb clock\n");
- err = PTR_ERR(pcdev->clk_csi_ahb);
- goto exit;
- }
-
- pcdev->clk_csi_per = devm_clk_get(&pdev->dev, "per");
- if (IS_ERR(pcdev->clk_csi_per)) {
- dev_err(&pdev->dev, "Could not get csi per clock\n");
- err = PTR_ERR(pcdev->clk_csi_per);
- goto exit;
- }
-
- pcdev->pdata = pdev->dev.platform_data;
- if (pcdev->pdata) {
- long rate;
-
- pcdev->platform_flags = pcdev->pdata->flags;
-
- rate = clk_round_rate(pcdev->clk_csi_per,
- pcdev->pdata->clk * 2);
- if (rate <= 0) {
- err = -ENODEV;
- goto exit;
- }
- err = clk_set_rate(pcdev->clk_csi_per, rate);
- if (err < 0)
- goto exit;
- }
-
- INIT_LIST_HEAD(&pcdev->capture);
- INIT_LIST_HEAD(&pcdev->active_bufs);
- INIT_LIST_HEAD(&pcdev->discard);
- spin_lock_init(&pcdev->lock);
-
- pcdev->base_csi = devm_ioremap_resource(&pdev->dev, res_csi);
- if (IS_ERR(pcdev->base_csi)) {
- err = PTR_ERR(pcdev->base_csi);
- goto exit;
- }
-
- pcdev->dev = &pdev->dev;
- platform_set_drvdata(pdev, pcdev);
-
- err = mx27_camera_emma_init(pdev);
- if (err)
- goto exit;
-
- /*
- * We're done with drvdata here. Clear the pointer so that
- * v4l2 core can start using drvdata on its purpose.
- */
- platform_set_drvdata(pdev, NULL);
-
- pcdev->soc_host.drv_name = MX2_CAM_DRV_NAME,
- pcdev->soc_host.ops = &mx2_soc_camera_host_ops,
- pcdev->soc_host.priv = pcdev;
- pcdev->soc_host.v4l2_dev.dev = &pdev->dev;
- pcdev->soc_host.nr = pdev->id;
-
- pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
- if (IS_ERR(pcdev->alloc_ctx)) {
- err = PTR_ERR(pcdev->alloc_ctx);
- goto eallocctx;
- }
- err = soc_camera_host_register(&pcdev->soc_host);
- if (err)
- goto exit_free_emma;
-
- dev_info(&pdev->dev, "MX2 Camera (CSI) driver probed, clock frequency: %ld\n",
- clk_get_rate(pcdev->clk_csi_per));
-
- return 0;
-
-exit_free_emma:
- vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
-eallocctx:
- clk_disable_unprepare(pcdev->clk_emma_ipg);
- clk_disable_unprepare(pcdev->clk_emma_ahb);
-exit:
- return err;
-}
-
-static int mx2_camera_remove(struct platform_device *pdev)
-{
- struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
- struct mx2_camera_dev *pcdev = container_of(soc_host,
- struct mx2_camera_dev, soc_host);
-
- soc_camera_host_unregister(&pcdev->soc_host);
-
- vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
-
- clk_disable_unprepare(pcdev->clk_emma_ipg);
- clk_disable_unprepare(pcdev->clk_emma_ahb);
-
- dev_info(&pdev->dev, "MX2 Camera driver unloaded\n");
-
- return 0;
-}
-
-static struct platform_driver mx2_camera_driver = {
- .driver = {
- .name = MX2_CAM_DRV_NAME,
- },
- .id_table = mx2_camera_devtype,
- .remove = mx2_camera_remove,
-};
-
-module_platform_driver_probe(mx2_camera_driver, mx2_camera_probe);
-
-MODULE_DESCRIPTION("i.MX27 SoC Camera Host driver");
-MODULE_AUTHOR("Sascha Hauer <sha@pengutronix.de>");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(MX2_CAM_VERSION);
+++ /dev/null
-/*
- * V4L2 Driver for i.MX3x camera host
- *
- * Copyright (C) 2008
- * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/videodev2.h>
-#include <linux/platform_device.h>
-#include <linux/clk.h>
-#include <linux/vmalloc.h>
-#include <linux/interrupt.h>
-#include <linux/sched.h>
-#include <linux/dma/ipu-dma.h>
-
-#include <media/v4l2-common.h>
-#include <media/v4l2-dev.h>
-#include <media/videobuf2-dma-contig.h>
-#include <media/soc_camera.h>
-#include <media/drv-intf/soc_mediabus.h>
-
-#include <linux/platform_data/media/camera-mx3.h>
-#include <linux/platform_data/dma-imx.h>
-
-#define MX3_CAM_DRV_NAME "mx3-camera"
-
-/* CMOS Sensor Interface Registers */
-#define CSI_REG_START 0x60
-
-#define CSI_SENS_CONF (0x60 - CSI_REG_START)
-#define CSI_SENS_FRM_SIZE (0x64 - CSI_REG_START)
-#define CSI_ACT_FRM_SIZE (0x68 - CSI_REG_START)
-#define CSI_OUT_FRM_CTRL (0x6C - CSI_REG_START)
-#define CSI_TST_CTRL (0x70 - CSI_REG_START)
-#define CSI_CCIR_CODE_1 (0x74 - CSI_REG_START)
-#define CSI_CCIR_CODE_2 (0x78 - CSI_REG_START)
-#define CSI_CCIR_CODE_3 (0x7C - CSI_REG_START)
-#define CSI_FLASH_STROBE_1 (0x80 - CSI_REG_START)
-#define CSI_FLASH_STROBE_2 (0x84 - CSI_REG_START)
-
-#define CSI_SENS_CONF_VSYNC_POL_SHIFT 0
-#define CSI_SENS_CONF_HSYNC_POL_SHIFT 1
-#define CSI_SENS_CONF_DATA_POL_SHIFT 2
-#define CSI_SENS_CONF_PIX_CLK_POL_SHIFT 3
-#define CSI_SENS_CONF_SENS_PRTCL_SHIFT 4
-#define CSI_SENS_CONF_SENS_CLKSRC_SHIFT 7
-#define CSI_SENS_CONF_DATA_FMT_SHIFT 8
-#define CSI_SENS_CONF_DATA_WIDTH_SHIFT 10
-#define CSI_SENS_CONF_EXT_VSYNC_SHIFT 15
-#define CSI_SENS_CONF_DIVRATIO_SHIFT 16
-
-#define CSI_SENS_CONF_DATA_FMT_RGB_YUV444 (0UL << CSI_SENS_CONF_DATA_FMT_SHIFT)
-#define CSI_SENS_CONF_DATA_FMT_YUV422 (2UL << CSI_SENS_CONF_DATA_FMT_SHIFT)
-#define CSI_SENS_CONF_DATA_FMT_BAYER (3UL << CSI_SENS_CONF_DATA_FMT_SHIFT)
-
-#define MAX_VIDEO_MEM 16
-
-struct mx3_camera_buffer {
- /* common v4l buffer stuff -- must be first */
- struct vb2_v4l2_buffer vb;
- struct list_head queue;
-
- /* One descriptot per scatterlist (per frame) */
- struct dma_async_tx_descriptor *txd;
-
- /* We have to "build" a scatterlist ourselves - one element per frame */
- struct scatterlist sg;
-};
-
-/**
- * struct mx3_camera_dev - i.MX3x camera (CSI) object
- * @dev: camera device, to which the coherent buffer is attached
- * @icd: currently attached camera sensor
- * @clk: pointer to clock
- * @base: remapped register base address
- * @pdata: platform data
- * @platform_flags: platform flags
- * @mclk: master clock frequency in Hz
- * @capture: list of capture videobuffers
- * @lock: protects video buffer lists
- * @active: active video buffer
- * @idmac_channel: array of pointers to IPU DMAC DMA channels
- * @soc_host: embedded soc_host object
- */
-struct mx3_camera_dev {
- /*
- * i.MX3x is only supposed to handle one camera on its Camera Sensor
- * Interface. If anyone ever builds hardware to enable more than one
- * camera _simultaneously_, they will have to modify this driver too
- */
- struct clk *clk;
-
- void __iomem *base;
-
- struct mx3_camera_pdata *pdata;
-
- unsigned long platform_flags;
- unsigned long mclk;
- u16 width_flags; /* max 15 bits */
-
- struct list_head capture;
- spinlock_t lock; /* Protects video buffer lists */
- struct mx3_camera_buffer *active;
- size_t buf_total;
- struct vb2_alloc_ctx *alloc_ctx;
- enum v4l2_field field;
- int sequence;
-
- /* IDMAC / dmaengine interface */
- struct idmac_channel *idmac_channel[1]; /* We need one channel */
-
- struct soc_camera_host soc_host;
-};
-
-struct dma_chan_request {
- struct mx3_camera_dev *mx3_cam;
- enum ipu_channel id;
-};
-
-static u32 csi_reg_read(struct mx3_camera_dev *mx3, off_t reg)
-{
- return __raw_readl(mx3->base + reg);
-}
-
-static void csi_reg_write(struct mx3_camera_dev *mx3, u32 value, off_t reg)
-{
- __raw_writel(value, mx3->base + reg);
-}
-
-static struct mx3_camera_buffer *to_mx3_vb(struct vb2_v4l2_buffer *vb)
-{
- return container_of(vb, struct mx3_camera_buffer, vb);
-}
-
-/* Called from the IPU IDMAC ISR */
-static void mx3_cam_dma_done(void *arg)
-{
- struct idmac_tx_desc *desc = to_tx_desc(arg);
- struct dma_chan *chan = desc->txd.chan;
- struct idmac_channel *ichannel = to_idmac_chan(chan);
- struct mx3_camera_dev *mx3_cam = ichannel->client;
-
- dev_dbg(chan->device->dev, "callback cookie %d, active DMA 0x%08x\n",
- desc->txd.cookie, mx3_cam->active ? sg_dma_address(&mx3_cam->active->sg) : 0);
-
- spin_lock(&mx3_cam->lock);
- if (mx3_cam->active) {
- struct vb2_v4l2_buffer *vb = &mx3_cam->active->vb;
- struct mx3_camera_buffer *buf = to_mx3_vb(vb);
-
- list_del_init(&buf->queue);
- vb->vb2_buf.timestamp = ktime_get_ns();
- vb->field = mx3_cam->field;
- vb->sequence = mx3_cam->sequence++;
- vb2_buffer_done(&vb->vb2_buf, VB2_BUF_STATE_DONE);
- }
-
- if (list_empty(&mx3_cam->capture)) {
- mx3_cam->active = NULL;
- spin_unlock(&mx3_cam->lock);
-
- /*
- * stop capture - without further buffers IPU_CHA_BUF0_RDY will
- * not get updated
- */
- return;
- }
-
- mx3_cam->active = list_entry(mx3_cam->capture.next,
- struct mx3_camera_buffer, queue);
- spin_unlock(&mx3_cam->lock);
-}
-
-/*
- * Videobuf operations
- */
-
-/*
- * Calculate the __buffer__ (not data) size and number of buffers.
- */
-static int mx3_videobuf_setup(struct vb2_queue *vq,
- unsigned int *count, unsigned int *num_planes,
- unsigned int sizes[], void *alloc_ctxs[])
-{
- struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx3_camera_dev *mx3_cam = ici->priv;
-
- if (!mx3_cam->idmac_channel[0])
- return -EINVAL;
-
- alloc_ctxs[0] = mx3_cam->alloc_ctx;
-
- if (!vq->num_buffers)
- mx3_cam->sequence = 0;
-
- if (!*count)
- *count = 2;
-
- /* Called from VIDIOC_REQBUFS or in compatibility mode */
- if (!*num_planes)
- sizes[0] = icd->sizeimage;
- else if (sizes[0] < icd->sizeimage)
- return -EINVAL;
-
- /* If *num_planes != 0, we have already verified *count. */
- if (sizes[0] * *count + mx3_cam->buf_total > MAX_VIDEO_MEM * 1024 * 1024)
- *count = (MAX_VIDEO_MEM * 1024 * 1024 - mx3_cam->buf_total) /
- sizes[0];
-
- *num_planes = 1;
-
- return 0;
-}
-
-static enum pixel_fmt fourcc_to_ipu_pix(__u32 fourcc)
-{
- /* Add more formats as need arises and test possibilities appear... */
- switch (fourcc) {
- case V4L2_PIX_FMT_RGB24:
- return IPU_PIX_FMT_RGB24;
- case V4L2_PIX_FMT_UYVY:
- case V4L2_PIX_FMT_RGB565:
- default:
- return IPU_PIX_FMT_GENERIC;
- }
-}
-
-static void mx3_videobuf_queue(struct vb2_buffer *vb)
-{
- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx3_camera_dev *mx3_cam = ici->priv;
- struct mx3_camera_buffer *buf = to_mx3_vb(vbuf);
- struct scatterlist *sg = &buf->sg;
- struct dma_async_tx_descriptor *txd;
- struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
- struct idmac_video_param *video = &ichan->params.video;
- const struct soc_mbus_pixelfmt *host_fmt = icd->current_fmt->host_fmt;
- dma_cookie_t cookie;
- size_t new_size;
-
- new_size = icd->sizeimage;
-
- if (vb2_plane_size(vb, 0) < new_size) {
- dev_err(icd->parent, "Buffer #%d too small (%lu < %zu)\n",
- vbuf->vb2_buf.index, vb2_plane_size(vb, 0), new_size);
- goto error;
- }
-
- if (!buf->txd) {
- sg_dma_address(sg) = vb2_dma_contig_plane_dma_addr(vb, 0);
- sg_dma_len(sg) = new_size;
-
- txd = dmaengine_prep_slave_sg(
- &ichan->dma_chan, sg, 1, DMA_DEV_TO_MEM,
- DMA_PREP_INTERRUPT);
- if (!txd)
- goto error;
-
- txd->callback_param = txd;
- txd->callback = mx3_cam_dma_done;
-
- buf->txd = txd;
- } else {
- txd = buf->txd;
- }
-
- vb2_set_plane_payload(vb, 0, new_size);
-
- /* This is the configuration of one sg-element */
- video->out_pixel_fmt = fourcc_to_ipu_pix(host_fmt->fourcc);
-
- if (video->out_pixel_fmt == IPU_PIX_FMT_GENERIC) {
- /*
- * If the IPU DMA channel is configured to transfer generic
- * 8-bit data, we have to set up the geometry parameters
- * correctly, according to the current pixel format. The DMA
- * horizontal parameters in this case are expressed in bytes,
- * not in pixels.
- */
- video->out_width = icd->bytesperline;
- video->out_height = icd->user_height;
- video->out_stride = icd->bytesperline;
- } else {
- /*
- * For IPU known formats the pixel unit will be managed
- * successfully by the IPU code
- */
- video->out_width = icd->user_width;
- video->out_height = icd->user_height;
- video->out_stride = icd->user_width;
- }
-
-#ifdef DEBUG
- /* helps to see what DMA actually has written */
- if (vb2_plane_vaddr(vb, 0))
- memset(vb2_plane_vaddr(vb, 0), 0xaa, vb2_get_plane_payload(vb, 0));
-#endif
-
- spin_lock_irq(&mx3_cam->lock);
- list_add_tail(&buf->queue, &mx3_cam->capture);
-
- if (!mx3_cam->active)
- mx3_cam->active = buf;
-
- spin_unlock_irq(&mx3_cam->lock);
-
- cookie = txd->tx_submit(txd);
- dev_dbg(icd->parent, "Submitted cookie %d DMA 0x%08x\n",
- cookie, sg_dma_address(&buf->sg));
-
- if (cookie >= 0)
- return;
-
- spin_lock_irq(&mx3_cam->lock);
-
- /* Submit error */
- list_del_init(&buf->queue);
-
- if (mx3_cam->active == buf)
- mx3_cam->active = NULL;
-
- spin_unlock_irq(&mx3_cam->lock);
-error:
- vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
-}
-
-static void mx3_videobuf_release(struct vb2_buffer *vb)
-{
- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx3_camera_dev *mx3_cam = ici->priv;
- struct mx3_camera_buffer *buf = to_mx3_vb(vbuf);
- struct dma_async_tx_descriptor *txd = buf->txd;
- unsigned long flags;
-
- dev_dbg(icd->parent,
- "Release%s DMA 0x%08x, queue %sempty\n",
- mx3_cam->active == buf ? " active" : "", sg_dma_address(&buf->sg),
- list_empty(&buf->queue) ? "" : "not ");
-
- spin_lock_irqsave(&mx3_cam->lock, flags);
-
- if (mx3_cam->active == buf)
- mx3_cam->active = NULL;
-
- /* Doesn't hurt also if the list is empty */
- list_del_init(&buf->queue);
-
- if (txd) {
- buf->txd = NULL;
- if (mx3_cam->idmac_channel[0])
- async_tx_ack(txd);
- }
-
- spin_unlock_irqrestore(&mx3_cam->lock, flags);
-
- mx3_cam->buf_total -= vb2_plane_size(vb, 0);
-}
-
-static int mx3_videobuf_init(struct vb2_buffer *vb)
-{
- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx3_camera_dev *mx3_cam = ici->priv;
- struct mx3_camera_buffer *buf = to_mx3_vb(vbuf);
-
- if (!buf->txd) {
- /* This is for locking debugging only */
- INIT_LIST_HEAD(&buf->queue);
- sg_init_table(&buf->sg, 1);
-
- mx3_cam->buf_total += vb2_plane_size(vb, 0);
- }
-
- return 0;
-}
-
-static void mx3_stop_streaming(struct vb2_queue *q)
-{
- struct soc_camera_device *icd = soc_camera_from_vb2q(q);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx3_camera_dev *mx3_cam = ici->priv;
- struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
- struct mx3_camera_buffer *buf, *tmp;
- unsigned long flags;
-
- if (ichan)
- dmaengine_pause(&ichan->dma_chan);
-
- spin_lock_irqsave(&mx3_cam->lock, flags);
-
- mx3_cam->active = NULL;
-
- list_for_each_entry_safe(buf, tmp, &mx3_cam->capture, queue) {
- list_del_init(&buf->queue);
- vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
- }
-
- spin_unlock_irqrestore(&mx3_cam->lock, flags);
-}
-
-static struct vb2_ops mx3_videobuf_ops = {
- .queue_setup = mx3_videobuf_setup,
- .buf_queue = mx3_videobuf_queue,
- .buf_cleanup = mx3_videobuf_release,
- .buf_init = mx3_videobuf_init,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
- .stop_streaming = mx3_stop_streaming,
-};
-
-static int mx3_camera_init_videobuf(struct vb2_queue *q,
- struct soc_camera_device *icd)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
-
- q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- q->io_modes = VB2_MMAP | VB2_USERPTR;
- q->drv_priv = icd;
- q->ops = &mx3_videobuf_ops;
- q->mem_ops = &vb2_dma_contig_memops;
- q->buf_struct_size = sizeof(struct mx3_camera_buffer);
- q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
- q->lock = &ici->host_lock;
-
- return vb2_queue_init(q);
-}
-
-/* First part of ipu_csi_init_interface() */
-static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam)
-{
- u32 conf;
- long rate;
-
- /* Set default size: ipu_csi_set_window_size() */
- csi_reg_write(mx3_cam, (640 - 1) | ((480 - 1) << 16), CSI_ACT_FRM_SIZE);
- /* ...and position to 0:0: ipu_csi_set_window_pos() */
- conf = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000;
- csi_reg_write(mx3_cam, conf, CSI_OUT_FRM_CTRL);
-
- /* We use only gated clock synchronisation mode so far */
- conf = 0 << CSI_SENS_CONF_SENS_PRTCL_SHIFT;
-
- /* Set generic data, platform-biggest bus-width */
- conf |= CSI_SENS_CONF_DATA_FMT_BAYER;
-
- if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15)
- conf |= 3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
- else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10)
- conf |= 2 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
- else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8)
- conf |= 1 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
- else/* if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)*/
- conf |= 0 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
-
- if (mx3_cam->platform_flags & MX3_CAMERA_CLK_SRC)
- conf |= 1 << CSI_SENS_CONF_SENS_CLKSRC_SHIFT;
- if (mx3_cam->platform_flags & MX3_CAMERA_EXT_VSYNC)
- conf |= 1 << CSI_SENS_CONF_EXT_VSYNC_SHIFT;
- if (mx3_cam->platform_flags & MX3_CAMERA_DP)
- conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT;
- if (mx3_cam->platform_flags & MX3_CAMERA_PCP)
- conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT;
- if (mx3_cam->platform_flags & MX3_CAMERA_HSP)
- conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT;
- if (mx3_cam->platform_flags & MX3_CAMERA_VSP)
- conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT;
-
- /* ipu_csi_init_interface() */
- csi_reg_write(mx3_cam, conf, CSI_SENS_CONF);
-
- clk_prepare_enable(mx3_cam->clk);
- rate = clk_round_rate(mx3_cam->clk, mx3_cam->mclk);
- dev_dbg(mx3_cam->soc_host.v4l2_dev.dev, "Set SENS_CONF to %x, rate %ld\n", conf, rate);
- if (rate)
- clk_set_rate(mx3_cam->clk, rate);
-}
-
-static int mx3_camera_add_device(struct soc_camera_device *icd)
-{
- dev_info(icd->parent, "MX3 Camera driver attached to camera %d\n",
- icd->devnum);
-
- return 0;
-}
-
-static void mx3_camera_remove_device(struct soc_camera_device *icd)
-{
- dev_info(icd->parent, "MX3 Camera driver detached from camera %d\n",
- icd->devnum);
-}
-
-/* Called with .host_lock held */
-static int mx3_camera_clock_start(struct soc_camera_host *ici)
-{
- struct mx3_camera_dev *mx3_cam = ici->priv;
-
- mx3_camera_activate(mx3_cam);
-
- mx3_cam->buf_total = 0;
-
- return 0;
-}
-
-/* Called with .host_lock held */
-static void mx3_camera_clock_stop(struct soc_camera_host *ici)
-{
- struct mx3_camera_dev *mx3_cam = ici->priv;
- struct idmac_channel **ichan = &mx3_cam->idmac_channel[0];
-
- if (*ichan) {
- dma_release_channel(&(*ichan)->dma_chan);
- *ichan = NULL;
- }
-
- clk_disable_unprepare(mx3_cam->clk);
-}
-
-static int test_platform_param(struct mx3_camera_dev *mx3_cam,
- unsigned char buswidth, unsigned long *flags)
-{
- /*
- * If requested data width is supported by the platform, use it or any
- * possible lower value - i.MX31 is smart enough to shift bits
- */
- if (buswidth > fls(mx3_cam->width_flags))
- return -EINVAL;
-
- /*
- * Platform specified synchronization and pixel clock polarities are
- * only a recommendation and are only used during probing. MX3x
- * camera interface only works in master mode, i.e., uses HSYNC and
- * VSYNC signals from the sensor
- */
- *flags = V4L2_MBUS_MASTER |
- V4L2_MBUS_HSYNC_ACTIVE_HIGH |
- V4L2_MBUS_HSYNC_ACTIVE_LOW |
- V4L2_MBUS_VSYNC_ACTIVE_HIGH |
- V4L2_MBUS_VSYNC_ACTIVE_LOW |
- V4L2_MBUS_PCLK_SAMPLE_RISING |
- V4L2_MBUS_PCLK_SAMPLE_FALLING |
- V4L2_MBUS_DATA_ACTIVE_HIGH |
- V4L2_MBUS_DATA_ACTIVE_LOW;
-
- return 0;
-}
-
-static int mx3_camera_try_bus_param(struct soc_camera_device *icd,
- const unsigned int depth)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx3_camera_dev *mx3_cam = ici->priv;
- struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
- unsigned long bus_flags, common_flags;
- int ret = test_platform_param(mx3_cam, depth, &bus_flags);
-
- dev_dbg(icd->parent, "request bus width %d bit: %d\n", depth, ret);
-
- if (ret < 0)
- return ret;
-
- ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
- if (!ret) {
- common_flags = soc_mbus_config_compatible(&cfg,
- bus_flags);
- if (!common_flags) {
- dev_warn(icd->parent,
- "Flags incompatible: camera 0x%x, host 0x%lx\n",
- cfg.flags, bus_flags);
- return -EINVAL;
- }
- } else if (ret != -ENOIOCTLCMD) {
- return ret;
- }
-
- return 0;
-}
-
-static bool chan_filter(struct dma_chan *chan, void *arg)
-{
- struct dma_chan_request *rq = arg;
- struct mx3_camera_pdata *pdata;
-
- if (!imx_dma_is_ipu(chan))
- return false;
-
- if (!rq)
- return false;
-
- pdata = rq->mx3_cam->soc_host.v4l2_dev.dev->platform_data;
-
- return rq->id == chan->chan_id &&
- pdata->dma_dev == chan->device->dev;
-}
-
-static const struct soc_mbus_pixelfmt mx3_camera_formats[] = {
- {
- .fourcc = V4L2_PIX_FMT_SBGGR8,
- .name = "Bayer BGGR (sRGB) 8 bit",
- .bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_NONE,
- .order = SOC_MBUS_ORDER_LE,
- .layout = SOC_MBUS_LAYOUT_PACKED,
- }, {
- .fourcc = V4L2_PIX_FMT_GREY,
- .name = "Monochrome 8 bit",
- .bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_NONE,
- .order = SOC_MBUS_ORDER_LE,
- .layout = SOC_MBUS_LAYOUT_PACKED,
- },
-};
-
-/* This will be corrected as we get more formats */
-static bool mx3_camera_packing_supported(const struct soc_mbus_pixelfmt *fmt)
-{
- return fmt->packing == SOC_MBUS_PACKING_NONE ||
- (fmt->bits_per_sample == 8 &&
- fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) ||
- (fmt->bits_per_sample > 8 &&
- fmt->packing == SOC_MBUS_PACKING_EXTEND16);
-}
-
-static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int idx,
- struct soc_camera_format_xlate *xlate)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct device *dev = icd->parent;
- int formats = 0, ret;
- struct v4l2_subdev_mbus_code_enum code = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- .index = idx,
- };
- const struct soc_mbus_pixelfmt *fmt;
-
- ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code);
- if (ret < 0)
- /* No more formats */
- return 0;
-
- fmt = soc_mbus_get_fmtdesc(code.code);
- if (!fmt) {
- dev_warn(icd->parent,
- "Unsupported format code #%u: 0x%x\n", idx, code.code);
- return 0;
- }
-
- /* This also checks support for the requested bits-per-sample */
- ret = mx3_camera_try_bus_param(icd, fmt->bits_per_sample);
- if (ret < 0)
- return 0;
-
- switch (code.code) {
- case MEDIA_BUS_FMT_SBGGR10_1X10:
- formats++;
- if (xlate) {
- xlate->host_fmt = &mx3_camera_formats[0];
- xlate->code = code.code;
- xlate++;
- dev_dbg(dev, "Providing format %s using code 0x%x\n",
- mx3_camera_formats[0].name, code.code);
- }
- break;
- case MEDIA_BUS_FMT_Y10_1X10:
- formats++;
- if (xlate) {
- xlate->host_fmt = &mx3_camera_formats[1];
- xlate->code = code.code;
- xlate++;
- dev_dbg(dev, "Providing format %s using code 0x%x\n",
- mx3_camera_formats[1].name, code.code);
- }
- break;
- default:
- if (!mx3_camera_packing_supported(fmt))
- return 0;
- }
-
- /* Generic pass-through */
- formats++;
- if (xlate) {
- xlate->host_fmt = fmt;
- xlate->code = code.code;
- dev_dbg(dev, "Providing format %c%c%c%c in pass-through mode\n",
- (fmt->fourcc >> (0*8)) & 0xFF,
- (fmt->fourcc >> (1*8)) & 0xFF,
- (fmt->fourcc >> (2*8)) & 0xFF,
- (fmt->fourcc >> (3*8)) & 0xFF);
- xlate++;
- }
-
- return formats;
-}
-
-static void configure_geometry(struct mx3_camera_dev *mx3_cam,
- unsigned int width, unsigned int height,
- const struct soc_mbus_pixelfmt *fmt)
-{
- u32 ctrl, width_field, height_field;
-
- if (fourcc_to_ipu_pix(fmt->fourcc) == IPU_PIX_FMT_GENERIC) {
- /*
- * As the CSI will be configured to output BAYER, here
- * the width parameter count the number of samples to
- * capture to complete the whole image width.
- */
- unsigned int num, den;
- int ret = soc_mbus_samples_per_pixel(fmt, &num, &den);
- BUG_ON(ret < 0);
- width = width * num / den;
- }
-
- /* Setup frame size - this cannot be changed on-the-fly... */
- width_field = width - 1;
- height_field = height - 1;
- csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_SENS_FRM_SIZE);
-
- csi_reg_write(mx3_cam, width_field << 16, CSI_FLASH_STROBE_1);
- csi_reg_write(mx3_cam, (height_field << 16) | 0x22, CSI_FLASH_STROBE_2);
-
- csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_ACT_FRM_SIZE);
-
- /* ...and position */
- ctrl = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000;
- /* Sensor does the cropping */
- csi_reg_write(mx3_cam, ctrl | 0 | (0 << 8), CSI_OUT_FRM_CTRL);
-}
-
-static int acquire_dma_channel(struct mx3_camera_dev *mx3_cam)
-{
- dma_cap_mask_t mask;
- struct dma_chan *chan;
- struct idmac_channel **ichan = &mx3_cam->idmac_channel[0];
- /* We have to use IDMAC_IC_7 for Bayer / generic data */
- struct dma_chan_request rq = {.mx3_cam = mx3_cam,
- .id = IDMAC_IC_7};
-
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
- dma_cap_set(DMA_PRIVATE, mask);
- chan = dma_request_channel(mask, chan_filter, &rq);
- if (!chan)
- return -EBUSY;
-
- *ichan = to_idmac_chan(chan);
- (*ichan)->client = mx3_cam;
-
- return 0;
-}
-
-/*
- * FIXME: learn to use stride != width, then we can keep stride properly aligned
- * and support arbitrary (even) widths.
- */
-static inline void stride_align(__u32 *width)
-{
- if (ALIGN(*width, 8) < 4096)
- *width = ALIGN(*width, 8);
- else
- *width = *width & ~7;
-}
-
-/*
- * As long as we don't implement host-side cropping and scaling, we can use
- * default g_crop and cropcap from soc_camera.c
- */
-static int mx3_camera_set_crop(struct soc_camera_device *icd,
- const struct v4l2_crop *a)
-{
- struct v4l2_crop a_writable = *a;
- struct v4l2_rect *rect = &a_writable.c;
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx3_camera_dev *mx3_cam = ici->priv;
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct v4l2_subdev_format fmt = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- };
- struct v4l2_mbus_framefmt *mf = &fmt.format;
- int ret;
-
- soc_camera_limit_side(&rect->left, &rect->width, 0, 2, 4096);
- soc_camera_limit_side(&rect->top, &rect->height, 0, 2, 4096);
-
- ret = v4l2_subdev_call(sd, video, s_crop, a);
- if (ret < 0)
- return ret;
-
- /* The capture device might have changed its output sizes */
- ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt);
- if (ret < 0)
- return ret;
-
- if (mf->code != icd->current_fmt->code)
- return -EINVAL;
-
- if (mf->width & 7) {
- /* Ouch! We can only handle 8-byte aligned width... */
- stride_align(&mf->width);
- ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &fmt);
- if (ret < 0)
- return ret;
- }
-
- if (mf->width != icd->user_width || mf->height != icd->user_height)
- configure_geometry(mx3_cam, mf->width, mf->height,
- icd->current_fmt->host_fmt);
-
- dev_dbg(icd->parent, "Sensor cropped %dx%d\n",
- mf->width, mf->height);
-
- icd->user_width = mf->width;
- icd->user_height = mf->height;
-
- return ret;
-}
-
-static int mx3_camera_set_fmt(struct soc_camera_device *icd,
- struct v4l2_format *f)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx3_camera_dev *mx3_cam = ici->priv;
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- const struct soc_camera_format_xlate *xlate;
- struct v4l2_pix_format *pix = &f->fmt.pix;
- struct v4l2_subdev_format format = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- };
- struct v4l2_mbus_framefmt *mf = &format.format;
- int ret;
-
- xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
- if (!xlate) {
- dev_warn(icd->parent, "Format %x not found\n",
- pix->pixelformat);
- return -EINVAL;
- }
-
- stride_align(&pix->width);
- dev_dbg(icd->parent, "Set format %dx%d\n", pix->width, pix->height);
-
- /*
- * Might have to perform a complete interface initialisation like in
- * ipu_csi_init_interface() in mxc_v4l2_s_param(). Also consider
- * mxc_v4l2_s_fmt()
- */
-
- configure_geometry(mx3_cam, pix->width, pix->height, xlate->host_fmt);
-
- mf->width = pix->width;
- mf->height = pix->height;
- mf->field = pix->field;
- mf->colorspace = pix->colorspace;
- mf->code = xlate->code;
-
- ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &format);
- if (ret < 0)
- return ret;
-
- if (mf->code != xlate->code)
- return -EINVAL;
-
- if (!mx3_cam->idmac_channel[0]) {
- ret = acquire_dma_channel(mx3_cam);
- if (ret < 0)
- return ret;
- }
-
- pix->width = mf->width;
- pix->height = mf->height;
- pix->field = mf->field;
- mx3_cam->field = mf->field;
- pix->colorspace = mf->colorspace;
- icd->current_fmt = xlate;
-
- dev_dbg(icd->parent, "Sensor set %dx%d\n", pix->width, pix->height);
-
- return ret;
-}
-
-static int mx3_camera_try_fmt(struct soc_camera_device *icd,
- struct v4l2_format *f)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- const struct soc_camera_format_xlate *xlate;
- struct v4l2_pix_format *pix = &f->fmt.pix;
- struct v4l2_subdev_pad_config pad_cfg;
- struct v4l2_subdev_format format = {
- .which = V4L2_SUBDEV_FORMAT_TRY,
- };
- struct v4l2_mbus_framefmt *mf = &format.format;
- __u32 pixfmt = pix->pixelformat;
- int ret;
-
- xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
- if (pixfmt && !xlate) {
- dev_warn(icd->parent, "Format %x not found\n", pixfmt);
- return -EINVAL;
- }
-
- /* limit to MX3 hardware capabilities */
- if (pix->height > 4096)
- pix->height = 4096;
- if (pix->width > 4096)
- pix->width = 4096;
-
- /* limit to sensor capabilities */
- mf->width = pix->width;
- mf->height = pix->height;
- mf->field = pix->field;
- mf->colorspace = pix->colorspace;
- mf->code = xlate->code;
-
- ret = v4l2_subdev_call(sd, pad, set_fmt, &pad_cfg, &format);
- if (ret < 0)
- return ret;
-
- pix->width = mf->width;
- pix->height = mf->height;
- pix->colorspace = mf->colorspace;
-
- switch (mf->field) {
- case V4L2_FIELD_ANY:
- pix->field = V4L2_FIELD_NONE;
- break;
- case V4L2_FIELD_NONE:
- break;
- default:
- dev_err(icd->parent, "Field type %d unsupported.\n",
- mf->field);
- ret = -EINVAL;
- }
-
- return ret;
-}
-
-static int mx3_camera_reqbufs(struct soc_camera_device *icd,
- struct v4l2_requestbuffers *p)
-{
- return 0;
-}
-
-static unsigned int mx3_camera_poll(struct file *file, poll_table *pt)
-{
- struct soc_camera_device *icd = file->private_data;
-
- return vb2_poll(&icd->vb2_vidq, file, pt);
-}
-
-static int mx3_camera_querycap(struct soc_camera_host *ici,
- struct v4l2_capability *cap)
-{
- /* cap->name is set by the firendly caller:-> */
- strlcpy(cap->card, "i.MX3x Camera", sizeof(cap->card));
- cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
- cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
-
- return 0;
-}
-
-static int mx3_camera_set_bus_param(struct soc_camera_device *icd)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx3_camera_dev *mx3_cam = ici->priv;
- struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
- u32 pixfmt = icd->current_fmt->host_fmt->fourcc;
- unsigned long bus_flags, common_flags;
- u32 dw, sens_conf;
- const struct soc_mbus_pixelfmt *fmt;
- int buswidth;
- int ret;
- const struct soc_camera_format_xlate *xlate;
- struct device *dev = icd->parent;
-
- fmt = soc_mbus_get_fmtdesc(icd->current_fmt->code);
- if (!fmt)
- return -EINVAL;
-
- xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
- if (!xlate) {
- dev_warn(dev, "Format %x not found\n", pixfmt);
- return -EINVAL;
- }
-
- buswidth = fmt->bits_per_sample;
- ret = test_platform_param(mx3_cam, buswidth, &bus_flags);
-
- dev_dbg(dev, "requested bus width %d bit: %d\n", buswidth, ret);
-
- if (ret < 0)
- return ret;
-
- ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
- if (!ret) {
- common_flags = soc_mbus_config_compatible(&cfg,
- bus_flags);
- if (!common_flags) {
- dev_warn(icd->parent,
- "Flags incompatible: camera 0x%x, host 0x%lx\n",
- cfg.flags, bus_flags);
- return -EINVAL;
- }
- } else if (ret != -ENOIOCTLCMD) {
- return ret;
- } else {
- common_flags = bus_flags;
- }
-
- dev_dbg(dev, "Flags cam: 0x%x host: 0x%lx common: 0x%lx\n",
- cfg.flags, bus_flags, common_flags);
-
- /* Make choices, based on platform preferences */
- if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
- (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
- if (mx3_cam->platform_flags & MX3_CAMERA_HSP)
- common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
- else
- common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
- }
-
- if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
- (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
- if (mx3_cam->platform_flags & MX3_CAMERA_VSP)
- common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
- else
- common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
- }
-
- if ((common_flags & V4L2_MBUS_DATA_ACTIVE_HIGH) &&
- (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)) {
- if (mx3_cam->platform_flags & MX3_CAMERA_DP)
- common_flags &= ~V4L2_MBUS_DATA_ACTIVE_HIGH;
- else
- common_flags &= ~V4L2_MBUS_DATA_ACTIVE_LOW;
- }
-
- if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
- (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
- if (mx3_cam->platform_flags & MX3_CAMERA_PCP)
- common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
- else
- common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
- }
-
- cfg.flags = common_flags;
- ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
- if (ret < 0 && ret != -ENOIOCTLCMD) {
- dev_dbg(dev, "camera s_mbus_config(0x%lx) returned %d\n",
- common_flags, ret);
- return ret;
- }
-
- /*
- * So far only gated clock mode is supported. Add a line
- * (3 << CSI_SENS_CONF_SENS_PRTCL_SHIFT) |
- * below and select the required mode when supporting other
- * synchronisation protocols.
- */
- sens_conf = csi_reg_read(mx3_cam, CSI_SENS_CONF) &
- ~((1 << CSI_SENS_CONF_VSYNC_POL_SHIFT) |
- (1 << CSI_SENS_CONF_HSYNC_POL_SHIFT) |
- (1 << CSI_SENS_CONF_DATA_POL_SHIFT) |
- (1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT) |
- (3 << CSI_SENS_CONF_DATA_FMT_SHIFT) |
- (3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT));
-
- /* TODO: Support RGB and YUV formats */
-
- /* This has been set in mx3_camera_activate(), but we clear it above */
- sens_conf |= CSI_SENS_CONF_DATA_FMT_BAYER;
-
- if (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
- sens_conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT;
- if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
- sens_conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT;
- if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
- sens_conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT;
- if (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)
- sens_conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT;
-
- /* Just do what we're asked to do */
- switch (xlate->host_fmt->bits_per_sample) {
- case 4:
- dw = 0 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
- break;
- case 8:
- dw = 1 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
- break;
- case 10:
- dw = 2 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
- break;
- default:
- /*
- * Actually it can only be 15 now, default is just to silence
- * compiler warnings
- */
- case 15:
- dw = 3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
- }
-
- csi_reg_write(mx3_cam, sens_conf | dw, CSI_SENS_CONF);
-
- dev_dbg(dev, "Set SENS_CONF to %x\n", sens_conf | dw);
-
- return 0;
-}
-
-static struct soc_camera_host_ops mx3_soc_camera_host_ops = {
- .owner = THIS_MODULE,
- .add = mx3_camera_add_device,
- .remove = mx3_camera_remove_device,
- .clock_start = mx3_camera_clock_start,
- .clock_stop = mx3_camera_clock_stop,
- .set_crop = mx3_camera_set_crop,
- .set_fmt = mx3_camera_set_fmt,
- .try_fmt = mx3_camera_try_fmt,
- .get_formats = mx3_camera_get_formats,
- .init_videobuf2 = mx3_camera_init_videobuf,
- .reqbufs = mx3_camera_reqbufs,
- .poll = mx3_camera_poll,
- .querycap = mx3_camera_querycap,
- .set_bus_param = mx3_camera_set_bus_param,
-};
-
-static int mx3_camera_probe(struct platform_device *pdev)
-{
- struct mx3_camera_pdata *pdata = pdev->dev.platform_data;
- struct mx3_camera_dev *mx3_cam;
- struct resource *res;
- void __iomem *base;
- int err = 0;
- struct soc_camera_host *soc_host;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(base))
- return PTR_ERR(base);
-
- if (!pdata)
- return -EINVAL;
-
- mx3_cam = devm_kzalloc(&pdev->dev, sizeof(*mx3_cam), GFP_KERNEL);
- if (!mx3_cam) {
- dev_err(&pdev->dev, "Could not allocate mx3 camera object\n");
- return -ENOMEM;
- }
-
- mx3_cam->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(mx3_cam->clk))
- return PTR_ERR(mx3_cam->clk);
-
- mx3_cam->pdata = pdata;
- mx3_cam->platform_flags = pdata->flags;
- if (!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_MASK)) {
- /*
- * Platform hasn't set available data widths. This is bad.
- * Warn and use a default.
- */
- dev_warn(&pdev->dev, "WARNING! Platform hasn't set available "
- "data widths, using default 8 bit\n");
- mx3_cam->platform_flags |= MX3_CAMERA_DATAWIDTH_8;
- }
- if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)
- mx3_cam->width_flags = 1 << 3;
- if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8)
- mx3_cam->width_flags |= 1 << 7;
- if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10)
- mx3_cam->width_flags |= 1 << 9;
- if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15)
- mx3_cam->width_flags |= 1 << 14;
-
- mx3_cam->mclk = pdata->mclk_10khz * 10000;
- if (!mx3_cam->mclk) {
- dev_warn(&pdev->dev,
- "mclk_10khz == 0! Please, fix your platform data. "
- "Using default 20MHz\n");
- mx3_cam->mclk = 20000000;
- }
-
- /* list of video-buffers */
- INIT_LIST_HEAD(&mx3_cam->capture);
- spin_lock_init(&mx3_cam->lock);
-
- mx3_cam->base = base;
-
- soc_host = &mx3_cam->soc_host;
- soc_host->drv_name = MX3_CAM_DRV_NAME;
- soc_host->ops = &mx3_soc_camera_host_ops;
- soc_host->priv = mx3_cam;
- soc_host->v4l2_dev.dev = &pdev->dev;
- soc_host->nr = pdev->id;
-
- mx3_cam->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
- if (IS_ERR(mx3_cam->alloc_ctx))
- return PTR_ERR(mx3_cam->alloc_ctx);
-
- if (pdata->asd_sizes) {
- soc_host->asd = pdata->asd;
- soc_host->asd_sizes = pdata->asd_sizes;
- }
-
- err = soc_camera_host_register(soc_host);
- if (err)
- goto ecamhostreg;
-
- /* IDMAC interface */
- dmaengine_get();
-
- return 0;
-
-ecamhostreg:
- vb2_dma_contig_cleanup_ctx(mx3_cam->alloc_ctx);
- return err;
-}
-
-static int mx3_camera_remove(struct platform_device *pdev)
-{
- struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
- struct mx3_camera_dev *mx3_cam = container_of(soc_host,
- struct mx3_camera_dev, soc_host);
-
- soc_camera_host_unregister(soc_host);
-
- /*
- * The channel has either not been allocated,
- * or should have been released
- */
- if (WARN_ON(mx3_cam->idmac_channel[0]))
- dma_release_channel(&mx3_cam->idmac_channel[0]->dma_chan);
-
- vb2_dma_contig_cleanup_ctx(mx3_cam->alloc_ctx);
-
- dmaengine_put();
-
- return 0;
-}
-
-static struct platform_driver mx3_camera_driver = {
- .driver = {
- .name = MX3_CAM_DRV_NAME,
- },
- .probe = mx3_camera_probe,
- .remove = mx3_camera_remove,
-};
-
-module_platform_driver(mx3_camera_driver);
-
-MODULE_DESCRIPTION("i.MX3x SoC Camera Host driver");
-MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>");
-MODULE_LICENSE("GPL v2");
-MODULE_VERSION("0.2.3");
-MODULE_ALIAS("platform:" MX3_CAM_DRV_NAME);
+++ /dev/null
-/*
- * V4L2 SoC Camera driver for OMAP1 Camera Interface
- *
- * Copyright (C) 2010, Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
- *
- * Based on V4L2 Driver for i.MXL/i.MXL camera (CSI) host
- * Copyright (C) 2008, Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
- * Copyright (C) 2009, Darius Augulis <augulis.darius@gmail.com>
- *
- * Based on PXA SoC camera driver
- * Copyright (C) 2006, Sascha Hauer, Pengutronix
- * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
- *
- * Hardware specific bits initialy based on former work by Matt Callow
- * drivers/media/platform/omap/omap1510cam.c
- * Copyright (C) 2006 Matt Callow
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-
-#include <linux/clk.h>
-#include <linux/dma-mapping.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-
-#include <linux/platform_data/media/omap1_camera.h>
-#include <media/soc_camera.h>
-#include <media/drv-intf/soc_mediabus.h>
-#include <media/videobuf-dma-contig.h>
-#include <media/videobuf-dma-sg.h>
-
-#include <linux/omap-dma.h>
-
-
-#define DRIVER_NAME "omap1-camera"
-#define DRIVER_VERSION "0.0.2"
-
-#define OMAP_DMA_CAMERA_IF_RX 20
-
-/*
- * ---------------------------------------------------------------------------
- * OMAP1 Camera Interface registers
- * ---------------------------------------------------------------------------
- */
-
-#define REG_CTRLCLOCK 0x00
-#define REG_IT_STATUS 0x04
-#define REG_MODE 0x08
-#define REG_STATUS 0x0C
-#define REG_CAMDATA 0x10
-#define REG_GPIO 0x14
-#define REG_PEAK_COUNTER 0x18
-
-/* CTRLCLOCK bit shifts */
-#define LCLK_EN BIT(7)
-#define DPLL_EN BIT(6)
-#define MCLK_EN BIT(5)
-#define CAMEXCLK_EN BIT(4)
-#define POLCLK BIT(3)
-#define FOSCMOD_SHIFT 0
-#define FOSCMOD_MASK (0x7 << FOSCMOD_SHIFT)
-#define FOSCMOD_12MHz 0x0
-#define FOSCMOD_6MHz 0x2
-#define FOSCMOD_9_6MHz 0x4
-#define FOSCMOD_24MHz 0x5
-#define FOSCMOD_8MHz 0x6
-
-/* IT_STATUS bit shifts */
-#define DATA_TRANSFER BIT(5)
-#define FIFO_FULL BIT(4)
-#define H_DOWN BIT(3)
-#define H_UP BIT(2)
-#define V_DOWN BIT(1)
-#define V_UP BIT(0)
-
-/* MODE bit shifts */
-#define RAZ_FIFO BIT(18)
-#define EN_FIFO_FULL BIT(17)
-#define EN_NIRQ BIT(16)
-#define THRESHOLD_SHIFT 9
-#define THRESHOLD_MASK (0x7f << THRESHOLD_SHIFT)
-#define DMA BIT(8)
-#define EN_H_DOWN BIT(7)
-#define EN_H_UP BIT(6)
-#define EN_V_DOWN BIT(5)
-#define EN_V_UP BIT(4)
-#define ORDERCAMD BIT(3)
-
-#define IRQ_MASK (EN_V_UP | EN_V_DOWN | EN_H_UP | EN_H_DOWN | \
- EN_NIRQ | EN_FIFO_FULL)
-
-/* STATUS bit shifts */
-#define HSTATUS BIT(1)
-#define VSTATUS BIT(0)
-
-/* GPIO bit shifts */
-#define CAM_RST BIT(0)
-
-/* end of OMAP1 Camera Interface registers */
-
-
-#define SOCAM_BUS_FLAGS (V4L2_MBUS_MASTER | \
- V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
- V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | \
- V4L2_MBUS_DATA_ACTIVE_HIGH)
-
-
-#define FIFO_SIZE ((THRESHOLD_MASK >> THRESHOLD_SHIFT) + 1)
-#define FIFO_SHIFT __fls(FIFO_SIZE)
-
-#define DMA_BURST_SHIFT (1 + OMAP_DMA_DATA_BURST_4)
-#define DMA_BURST_SIZE (1 << DMA_BURST_SHIFT)
-
-#define DMA_ELEMENT_SHIFT OMAP_DMA_DATA_TYPE_S32
-#define DMA_ELEMENT_SIZE (1 << DMA_ELEMENT_SHIFT)
-
-#define DMA_FRAME_SHIFT_CONTIG (FIFO_SHIFT - 1)
-#define DMA_FRAME_SHIFT_SG DMA_BURST_SHIFT
-
-#define DMA_FRAME_SHIFT(x) ((x) == OMAP1_CAM_DMA_CONTIG ? \
- DMA_FRAME_SHIFT_CONTIG : \
- DMA_FRAME_SHIFT_SG)
-#define DMA_FRAME_SIZE(x) (1 << DMA_FRAME_SHIFT(x))
-#define DMA_SYNC OMAP_DMA_SYNC_FRAME
-#define THRESHOLD_LEVEL DMA_FRAME_SIZE
-
-
-#define MAX_VIDEO_MEM 4 /* arbitrary video memory limit in MB */
-
-
-/*
- * Structures
- */
-
-/* buffer for one video frame */
-struct omap1_cam_buf {
- struct videobuf_buffer vb;
- u32 code;
- int inwork;
- struct scatterlist *sgbuf;
- int sgcount;
- int bytes_left;
- enum videobuf_state result;
-};
-
-struct omap1_cam_dev {
- struct soc_camera_host soc_host;
- struct clk *clk;
-
- unsigned int irq;
- void __iomem *base;
-
- int dma_ch;
-
- struct omap1_cam_platform_data *pdata;
- struct resource *res;
- unsigned long pflags;
- unsigned long camexclk;
-
- struct list_head capture;
-
- /* lock used to protect videobuf */
- spinlock_t lock;
-
- /* Pointers to DMA buffers */
- struct omap1_cam_buf *active;
- struct omap1_cam_buf *ready;
-
- enum omap1_cam_vb_mode vb_mode;
- int (*mmap_mapper)(struct videobuf_queue *q,
- struct videobuf_buffer *buf,
- struct vm_area_struct *vma);
-
- u32 reg_cache[0];
-};
-
-
-static void cam_write(struct omap1_cam_dev *pcdev, u16 reg, u32 val)
-{
- pcdev->reg_cache[reg / sizeof(u32)] = val;
- __raw_writel(val, pcdev->base + reg);
-}
-
-static u32 cam_read(struct omap1_cam_dev *pcdev, u16 reg, bool from_cache)
-{
- return !from_cache ? __raw_readl(pcdev->base + reg) :
- pcdev->reg_cache[reg / sizeof(u32)];
-}
-
-#define CAM_READ(pcdev, reg) \
- cam_read(pcdev, REG_##reg, false)
-#define CAM_WRITE(pcdev, reg, val) \
- cam_write(pcdev, REG_##reg, val)
-#define CAM_READ_CACHE(pcdev, reg) \
- cam_read(pcdev, REG_##reg, true)
-
-/*
- * Videobuf operations
- */
-static int omap1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
- unsigned int *size)
-{
- struct soc_camera_device *icd = vq->priv_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct omap1_cam_dev *pcdev = ici->priv;
-
- *size = icd->sizeimage;
-
- if (!*count || *count < OMAP1_CAMERA_MIN_BUF_COUNT(pcdev->vb_mode))
- *count = OMAP1_CAMERA_MIN_BUF_COUNT(pcdev->vb_mode);
-
- if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024)
- *count = (MAX_VIDEO_MEM * 1024 * 1024) / *size;
-
- dev_dbg(icd->parent,
- "%s: count=%d, size=%d\n", __func__, *count, *size);
-
- return 0;
-}
-
-static void free_buffer(struct videobuf_queue *vq, struct omap1_cam_buf *buf,
- enum omap1_cam_vb_mode vb_mode)
-{
- struct videobuf_buffer *vb = &buf->vb;
-
- BUG_ON(in_interrupt());
-
- videobuf_waiton(vq, vb, 0, 0);
-
- if (vb_mode == OMAP1_CAM_DMA_CONTIG) {
- videobuf_dma_contig_free(vq, vb);
- } else {
- struct soc_camera_device *icd = vq->priv_data;
- struct device *dev = icd->parent;
- struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
-
- videobuf_dma_unmap(dev, dma);
- videobuf_dma_free(dma);
- }
-
- vb->state = VIDEOBUF_NEEDS_INIT;
-}
-
-static int omap1_videobuf_prepare(struct videobuf_queue *vq,
- struct videobuf_buffer *vb, enum v4l2_field field)
-{
- struct soc_camera_device *icd = vq->priv_data;
- struct omap1_cam_buf *buf = container_of(vb, struct omap1_cam_buf, vb);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct omap1_cam_dev *pcdev = ici->priv;
- int ret;
-
- WARN_ON(!list_empty(&vb->queue));
-
- BUG_ON(NULL == icd->current_fmt);
-
- buf->inwork = 1;
-
- if (buf->code != icd->current_fmt->code || vb->field != field ||
- vb->width != icd->user_width ||
- vb->height != icd->user_height) {
- buf->code = icd->current_fmt->code;
- vb->width = icd->user_width;
- vb->height = icd->user_height;
- vb->field = field;
- vb->state = VIDEOBUF_NEEDS_INIT;
- }
-
- vb->size = icd->sizeimage;
-
- if (vb->baddr && vb->bsize < vb->size) {
- ret = -EINVAL;
- goto out;
- }
-
- if (vb->state == VIDEOBUF_NEEDS_INIT) {
- ret = videobuf_iolock(vq, vb, NULL);
- if (ret)
- goto fail;
-
- vb->state = VIDEOBUF_PREPARED;
- }
- buf->inwork = 0;
-
- return 0;
-fail:
- free_buffer(vq, buf, pcdev->vb_mode);
-out:
- buf->inwork = 0;
- return ret;
-}
-
-static void set_dma_dest_params(int dma_ch, struct omap1_cam_buf *buf,
- enum omap1_cam_vb_mode vb_mode)
-{
- dma_addr_t dma_addr;
- unsigned int block_size;
-
- if (vb_mode == OMAP1_CAM_DMA_CONTIG) {
- dma_addr = videobuf_to_dma_contig(&buf->vb);
- block_size = buf->vb.size;
- } else {
- if (WARN_ON(!buf->sgbuf)) {
- buf->result = VIDEOBUF_ERROR;
- return;
- }
- dma_addr = sg_dma_address(buf->sgbuf);
- if (WARN_ON(!dma_addr)) {
- buf->sgbuf = NULL;
- buf->result = VIDEOBUF_ERROR;
- return;
- }
- block_size = sg_dma_len(buf->sgbuf);
- if (WARN_ON(!block_size)) {
- buf->sgbuf = NULL;
- buf->result = VIDEOBUF_ERROR;
- return;
- }
- if (unlikely(buf->bytes_left < block_size))
- block_size = buf->bytes_left;
- if (WARN_ON(dma_addr & (DMA_FRAME_SIZE(vb_mode) *
- DMA_ELEMENT_SIZE - 1))) {
- dma_addr = ALIGN(dma_addr, DMA_FRAME_SIZE(vb_mode) *
- DMA_ELEMENT_SIZE);
- block_size &= ~(DMA_FRAME_SIZE(vb_mode) *
- DMA_ELEMENT_SIZE - 1);
- }
- buf->bytes_left -= block_size;
- buf->sgcount++;
- }
-
- omap_set_dma_dest_params(dma_ch,
- OMAP_DMA_PORT_EMIFF, OMAP_DMA_AMODE_POST_INC, dma_addr, 0, 0);
- omap_set_dma_transfer_params(dma_ch,
- OMAP_DMA_DATA_TYPE_S32, DMA_FRAME_SIZE(vb_mode),
- block_size >> (DMA_FRAME_SHIFT(vb_mode) + DMA_ELEMENT_SHIFT),
- DMA_SYNC, 0, 0);
-}
-
-static struct omap1_cam_buf *prepare_next_vb(struct omap1_cam_dev *pcdev)
-{
- struct omap1_cam_buf *buf;
-
- /*
- * If there is already a buffer pointed out by the pcdev->ready,
- * (re)use it, otherwise try to fetch and configure a new one.
- */
- buf = pcdev->ready;
- if (!buf) {
- if (list_empty(&pcdev->capture))
- return buf;
- buf = list_entry(pcdev->capture.next,
- struct omap1_cam_buf, vb.queue);
- buf->vb.state = VIDEOBUF_ACTIVE;
- pcdev->ready = buf;
- list_del_init(&buf->vb.queue);
- }
-
- if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
- /*
- * In CONTIG mode, we can safely enter next buffer parameters
- * into the DMA programming register set after the DMA
- * has already been activated on the previous buffer
- */
- set_dma_dest_params(pcdev->dma_ch, buf, pcdev->vb_mode);
- } else {
- /*
- * In SG mode, the above is not safe since there are probably
- * a bunch of sgbufs from previous sglist still pending.
- * Instead, mark the sglist fresh for the upcoming
- * try_next_sgbuf().
- */
- buf->sgbuf = NULL;
- }
-
- return buf;
-}
-
-static struct scatterlist *try_next_sgbuf(int dma_ch, struct omap1_cam_buf *buf)
-{
- struct scatterlist *sgbuf;
-
- if (likely(buf->sgbuf)) {
- /* current sglist is active */
- if (unlikely(!buf->bytes_left)) {
- /* indicate sglist complete */
- sgbuf = NULL;
- } else {
- /* process next sgbuf */
- sgbuf = sg_next(buf->sgbuf);
- if (WARN_ON(!sgbuf)) {
- buf->result = VIDEOBUF_ERROR;
- } else if (WARN_ON(!sg_dma_len(sgbuf))) {
- sgbuf = NULL;
- buf->result = VIDEOBUF_ERROR;
- }
- }
- buf->sgbuf = sgbuf;
- } else {
- /* sglist is fresh, initialize it before using */
- struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
-
- sgbuf = dma->sglist;
- if (!(WARN_ON(!sgbuf))) {
- buf->sgbuf = sgbuf;
- buf->sgcount = 0;
- buf->bytes_left = buf->vb.size;
- buf->result = VIDEOBUF_DONE;
- }
- }
- if (sgbuf)
- /*
- * Put our next sgbuf parameters (address, size)
- * into the DMA programming register set.
- */
- set_dma_dest_params(dma_ch, buf, OMAP1_CAM_DMA_SG);
-
- return sgbuf;
-}
-
-static void start_capture(struct omap1_cam_dev *pcdev)
-{
- struct omap1_cam_buf *buf = pcdev->active;
- u32 ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
- u32 mode = CAM_READ_CACHE(pcdev, MODE) & ~EN_V_DOWN;
-
- if (WARN_ON(!buf))
- return;
-
- /*
- * Enable start of frame interrupt, which we will use for activating
- * our end of frame watchdog when capture actually starts.
- */
- mode |= EN_V_UP;
-
- if (unlikely(ctrlclock & LCLK_EN))
- /* stop pixel clock before FIFO reset */
- CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
- /* reset FIFO */
- CAM_WRITE(pcdev, MODE, mode | RAZ_FIFO);
-
- omap_start_dma(pcdev->dma_ch);
-
- if (pcdev->vb_mode == OMAP1_CAM_DMA_SG) {
- /*
- * In SG mode, it's a good moment for fetching next sgbuf
- * from the current sglist and, if available, already putting
- * its parameters into the DMA programming register set.
- */
- try_next_sgbuf(pcdev->dma_ch, buf);
- }
-
- /* (re)enable pixel clock */
- CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock | LCLK_EN);
- /* release FIFO reset */
- CAM_WRITE(pcdev, MODE, mode);
-}
-
-static void suspend_capture(struct omap1_cam_dev *pcdev)
-{
- u32 ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
-
- CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
- omap_stop_dma(pcdev->dma_ch);
-}
-
-static void disable_capture(struct omap1_cam_dev *pcdev)
-{
- u32 mode = CAM_READ_CACHE(pcdev, MODE);
-
- CAM_WRITE(pcdev, MODE, mode & ~(IRQ_MASK | DMA));
-}
-
-static void omap1_videobuf_queue(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
-{
- struct soc_camera_device *icd = vq->priv_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct omap1_cam_dev *pcdev = ici->priv;
- struct omap1_cam_buf *buf;
- u32 mode;
-
- list_add_tail(&vb->queue, &pcdev->capture);
- vb->state = VIDEOBUF_QUEUED;
-
- if (pcdev->active) {
- /*
- * Capture in progress, so don't touch pcdev->ready even if
- * empty. Since the transfer of the DMA programming register set
- * content to the DMA working register set is done automatically
- * by the DMA hardware, this can pretty well happen while we
- * are keeping the lock here. Leave fetching it from the queue
- * to be done when a next DMA interrupt occures instead.
- */
- return;
- }
-
- WARN_ON(pcdev->ready);
-
- buf = prepare_next_vb(pcdev);
- if (WARN_ON(!buf))
- return;
-
- pcdev->active = buf;
- pcdev->ready = NULL;
-
- dev_dbg(icd->parent,
- "%s: capture not active, setup FIFO, start DMA\n", __func__);
- mode = CAM_READ_CACHE(pcdev, MODE) & ~THRESHOLD_MASK;
- mode |= THRESHOLD_LEVEL(pcdev->vb_mode) << THRESHOLD_SHIFT;
- CAM_WRITE(pcdev, MODE, mode | EN_FIFO_FULL | DMA);
-
- if (pcdev->vb_mode == OMAP1_CAM_DMA_SG) {
- /*
- * In SG mode, the above prepare_next_vb() didn't actually
- * put anything into the DMA programming register set,
- * so we have to do it now, before activating DMA.
- */
- try_next_sgbuf(pcdev->dma_ch, buf);
- }
-
- start_capture(pcdev);
-}
-
-static void omap1_videobuf_release(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
-{
- struct omap1_cam_buf *buf =
- container_of(vb, struct omap1_cam_buf, vb);
- struct soc_camera_device *icd = vq->priv_data;
- struct device *dev = icd->parent;
- struct soc_camera_host *ici = to_soc_camera_host(dev);
- struct omap1_cam_dev *pcdev = ici->priv;
-
- switch (vb->state) {
- case VIDEOBUF_DONE:
- dev_dbg(dev, "%s (done)\n", __func__);
- break;
- case VIDEOBUF_ACTIVE:
- dev_dbg(dev, "%s (active)\n", __func__);
- break;
- case VIDEOBUF_QUEUED:
- dev_dbg(dev, "%s (queued)\n", __func__);
- break;
- case VIDEOBUF_PREPARED:
- dev_dbg(dev, "%s (prepared)\n", __func__);
- break;
- default:
- dev_dbg(dev, "%s (unknown %d)\n", __func__, vb->state);
- break;
- }
-
- free_buffer(vq, buf, pcdev->vb_mode);
-}
-
-static void videobuf_done(struct omap1_cam_dev *pcdev,
- enum videobuf_state result)
-{
- struct omap1_cam_buf *buf = pcdev->active;
- struct videobuf_buffer *vb;
- struct device *dev = pcdev->soc_host.icd->parent;
-
- if (WARN_ON(!buf)) {
- suspend_capture(pcdev);
- disable_capture(pcdev);
- return;
- }
-
- if (result == VIDEOBUF_ERROR)
- suspend_capture(pcdev);
-
- vb = &buf->vb;
- if (waitqueue_active(&vb->done)) {
- if (!pcdev->ready && result != VIDEOBUF_ERROR) {
- /*
- * No next buffer has been entered into the DMA
- * programming register set on time (could be done only
- * while the previous DMA interurpt was processed, not
- * later), so the last DMA block, be it a whole buffer
- * if in CONTIG or its last sgbuf if in SG mode, is
- * about to be reused by the just autoreinitialized DMA
- * engine, and overwritten with next frame data. Best we
- * can do is stopping the capture as soon as possible,
- * hopefully before the next frame start.
- */
- suspend_capture(pcdev);
- }
- vb->state = result;
- v4l2_get_timestamp(&vb->ts);
- if (result != VIDEOBUF_ERROR)
- vb->field_count++;
- wake_up(&vb->done);
-
- /* shift in next buffer */
- buf = pcdev->ready;
- pcdev->active = buf;
- pcdev->ready = NULL;
-
- if (!buf) {
- /*
- * No next buffer was ready on time (see above), so
- * indicate error condition to force capture restart or
- * stop, depending on next buffer already queued or not.
- */
- result = VIDEOBUF_ERROR;
- prepare_next_vb(pcdev);
-
- buf = pcdev->ready;
- pcdev->active = buf;
- pcdev->ready = NULL;
- }
- } else if (pcdev->ready) {
- /*
- * In both CONTIG and SG mode, the DMA engine has possibly
- * been already autoreinitialized with the preprogrammed
- * pcdev->ready buffer. We can either accept this fact
- * and just swap the buffers, or provoke an error condition
- * and restart capture. The former seems less intrusive.
- */
- dev_dbg(dev, "%s: nobody waiting on videobuf, swap with next\n",
- __func__);
- pcdev->active = pcdev->ready;
-
- if (pcdev->vb_mode == OMAP1_CAM_DMA_SG) {
- /*
- * In SG mode, we have to make sure that the buffer we
- * are putting back into the pcdev->ready is marked
- * fresh.
- */
- buf->sgbuf = NULL;
- }
- pcdev->ready = buf;
-
- buf = pcdev->active;
- } else {
- /*
- * No next buffer has been entered into
- * the DMA programming register set on time.
- */
- if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
- /*
- * In CONTIG mode, the DMA engine has already been
- * reinitialized with the current buffer. Best we can do
- * is not touching it.
- */
- dev_dbg(dev,
- "%s: nobody waiting on videobuf, reuse it\n",
- __func__);
- } else {
- /*
- * In SG mode, the DMA engine has just been
- * autoreinitialized with the last sgbuf from the
- * current list. Restart capture in order to transfer
- * next frame start into the first sgbuf, not the last
- * one.
- */
- if (result != VIDEOBUF_ERROR) {
- suspend_capture(pcdev);
- result = VIDEOBUF_ERROR;
- }
- }
- }
-
- if (!buf) {
- dev_dbg(dev, "%s: no more videobufs, stop capture\n", __func__);
- disable_capture(pcdev);
- return;
- }
-
- if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
- /*
- * In CONTIG mode, the current buffer parameters had already
- * been entered into the DMA programming register set while the
- * buffer was fetched with prepare_next_vb(), they may have also
- * been transferred into the runtime set and already active if
- * the DMA still running.
- */
- } else {
- /* In SG mode, extra steps are required */
- if (result == VIDEOBUF_ERROR)
- /* make sure we (re)use sglist from start on error */
- buf->sgbuf = NULL;
-
- /*
- * In any case, enter the next sgbuf parameters into the DMA
- * programming register set. They will be used either during
- * nearest DMA autoreinitialization or, in case of an error,
- * on DMA startup below.
- */
- try_next_sgbuf(pcdev->dma_ch, buf);
- }
-
- if (result == VIDEOBUF_ERROR) {
- dev_dbg(dev, "%s: videobuf error; reset FIFO, restart DMA\n",
- __func__);
- start_capture(pcdev);
- /*
- * In SG mode, the above also resulted in the next sgbuf
- * parameters being entered into the DMA programming register
- * set, making them ready for next DMA autoreinitialization.
- */
- }
-
- /*
- * Finally, try fetching next buffer.
- * In CONTIG mode, it will also enter it into the DMA programming
- * register set, making it ready for next DMA autoreinitialization.
- */
- prepare_next_vb(pcdev);
-}
-
-static void dma_isr(int channel, unsigned short status, void *data)
-{
- struct omap1_cam_dev *pcdev = data;
- struct omap1_cam_buf *buf = pcdev->active;
- unsigned long flags;
-
- spin_lock_irqsave(&pcdev->lock, flags);
-
- if (WARN_ON(!buf)) {
- suspend_capture(pcdev);
- disable_capture(pcdev);
- goto out;
- }
-
- if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
- /*
- * In CONTIG mode, assume we have just managed to collect the
- * whole frame, hopefully before our end of frame watchdog is
- * triggered. Then, all we have to do is disabling the watchdog
- * for this frame, and calling videobuf_done() with success
- * indicated.
- */
- CAM_WRITE(pcdev, MODE,
- CAM_READ_CACHE(pcdev, MODE) & ~EN_V_DOWN);
- videobuf_done(pcdev, VIDEOBUF_DONE);
- } else {
- /*
- * In SG mode, we have to process every sgbuf from the current
- * sglist, one after another.
- */
- if (buf->sgbuf) {
- /*
- * Current sglist not completed yet, try fetching next
- * sgbuf, hopefully putting it into the DMA programming
- * register set, making it ready for next DMA
- * autoreinitialization.
- */
- try_next_sgbuf(pcdev->dma_ch, buf);
- if (buf->sgbuf)
- goto out;
-
- /*
- * No more sgbufs left in the current sglist. This
- * doesn't mean that the whole videobuffer is already
- * complete, but only that the last sgbuf from the
- * current sglist is about to be filled. It will be
- * ready on next DMA interrupt, signalled with the
- * buf->sgbuf set back to NULL.
- */
- if (buf->result != VIDEOBUF_ERROR) {
- /*
- * Video frame collected without errors so far,
- * we can prepare for collecting a next one
- * as soon as DMA gets autoreinitialized
- * after the current (last) sgbuf is completed.
- */
- buf = prepare_next_vb(pcdev);
- if (!buf)
- goto out;
-
- try_next_sgbuf(pcdev->dma_ch, buf);
- goto out;
- }
- }
- /* end of videobuf */
- videobuf_done(pcdev, buf->result);
- }
-
-out:
- spin_unlock_irqrestore(&pcdev->lock, flags);
-}
-
-static irqreturn_t cam_isr(int irq, void *data)
-{
- struct omap1_cam_dev *pcdev = data;
- struct device *dev = pcdev->soc_host.icd->parent;
- struct omap1_cam_buf *buf = pcdev->active;
- u32 it_status;
- unsigned long flags;
-
- it_status = CAM_READ(pcdev, IT_STATUS);
- if (!it_status)
- return IRQ_NONE;
-
- spin_lock_irqsave(&pcdev->lock, flags);
-
- if (WARN_ON(!buf)) {
- dev_warn(dev, "%s: unhandled camera interrupt, status == %#x\n",
- __func__, it_status);
- suspend_capture(pcdev);
- disable_capture(pcdev);
- goto out;
- }
-
- if (unlikely(it_status & FIFO_FULL)) {
- dev_warn(dev, "%s: FIFO overflow\n", __func__);
-
- } else if (it_status & V_DOWN) {
- /* end of video frame watchdog */
- if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
- /*
- * In CONTIG mode, the watchdog is disabled with
- * successful DMA end of block interrupt, and reenabled
- * on next frame start. If we get here, there is nothing
- * to check, we must be out of sync.
- */
- } else {
- if (buf->sgcount == 2) {
- /*
- * If exactly 2 sgbufs from the next sglist have
- * been programmed into the DMA engine (the
- * first one already transferred into the DMA
- * runtime register set, the second one still
- * in the programming set), then we are in sync.
- */
- goto out;
- }
- }
- dev_notice(dev, "%s: unexpected end of video frame\n",
- __func__);
-
- } else if (it_status & V_UP) {
- u32 mode;
-
- if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
- /*
- * In CONTIG mode, we need this interrupt every frame
- * in oredr to reenable our end of frame watchdog.
- */
- mode = CAM_READ_CACHE(pcdev, MODE);
- } else {
- /*
- * In SG mode, the below enabled end of frame watchdog
- * is kept on permanently, so we can turn this one shot
- * setup off.
- */
- mode = CAM_READ_CACHE(pcdev, MODE) & ~EN_V_UP;
- }
-
- if (!(mode & EN_V_DOWN)) {
- /* (re)enable end of frame watchdog interrupt */
- mode |= EN_V_DOWN;
- }
- CAM_WRITE(pcdev, MODE, mode);
- goto out;
-
- } else {
- dev_warn(dev, "%s: unhandled camera interrupt, status == %#x\n",
- __func__, it_status);
- goto out;
- }
-
- videobuf_done(pcdev, VIDEOBUF_ERROR);
-out:
- spin_unlock_irqrestore(&pcdev->lock, flags);
- return IRQ_HANDLED;
-}
-
-static struct videobuf_queue_ops omap1_videobuf_ops = {
- .buf_setup = omap1_videobuf_setup,
- .buf_prepare = omap1_videobuf_prepare,
- .buf_queue = omap1_videobuf_queue,
- .buf_release = omap1_videobuf_release,
-};
-
-
-/*
- * SOC Camera host operations
- */
-
-static void sensor_reset(struct omap1_cam_dev *pcdev, bool reset)
-{
- /* apply/release camera sensor reset if requested by platform data */
- if (pcdev->pflags & OMAP1_CAMERA_RST_HIGH)
- CAM_WRITE(pcdev, GPIO, reset);
- else if (pcdev->pflags & OMAP1_CAMERA_RST_LOW)
- CAM_WRITE(pcdev, GPIO, !reset);
-}
-
-static int omap1_cam_add_device(struct soc_camera_device *icd)
-{
- dev_dbg(icd->parent, "OMAP1 Camera driver attached to camera %d\n",
- icd->devnum);
-
- return 0;
-}
-
-static void omap1_cam_remove_device(struct soc_camera_device *icd)
-{
- dev_dbg(icd->parent,
- "OMAP1 Camera driver detached from camera %d\n", icd->devnum);
-}
-
-/*
- * The following two functions absolutely depend on the fact, that
- * there can be only one camera on OMAP1 camera sensor interface
- */
-static int omap1_cam_clock_start(struct soc_camera_host *ici)
-{
- struct omap1_cam_dev *pcdev = ici->priv;
- u32 ctrlclock;
-
- clk_enable(pcdev->clk);
-
- /* setup sensor clock */
- ctrlclock = CAM_READ(pcdev, CTRLCLOCK);
- ctrlclock &= ~(CAMEXCLK_EN | MCLK_EN | DPLL_EN);
- CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
-
- ctrlclock &= ~FOSCMOD_MASK;
- switch (pcdev->camexclk) {
- case 6000000:
- ctrlclock |= CAMEXCLK_EN | FOSCMOD_6MHz;
- break;
- case 8000000:
- ctrlclock |= CAMEXCLK_EN | FOSCMOD_8MHz | DPLL_EN;
- break;
- case 9600000:
- ctrlclock |= CAMEXCLK_EN | FOSCMOD_9_6MHz | DPLL_EN;
- break;
- case 12000000:
- ctrlclock |= CAMEXCLK_EN | FOSCMOD_12MHz;
- break;
- case 24000000:
- ctrlclock |= CAMEXCLK_EN | FOSCMOD_24MHz | DPLL_EN;
- default:
- break;
- }
- CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~DPLL_EN);
-
- /* enable internal clock */
- ctrlclock |= MCLK_EN;
- CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
-
- sensor_reset(pcdev, false);
-
- return 0;
-}
-
-static void omap1_cam_clock_stop(struct soc_camera_host *ici)
-{
- struct omap1_cam_dev *pcdev = ici->priv;
- u32 ctrlclock;
-
- suspend_capture(pcdev);
- disable_capture(pcdev);
-
- sensor_reset(pcdev, true);
-
- /* disable and release system clocks */
- ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
- ctrlclock &= ~(MCLK_EN | DPLL_EN | CAMEXCLK_EN);
- CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
-
- ctrlclock = (ctrlclock & ~FOSCMOD_MASK) | FOSCMOD_12MHz;
- CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
- CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock | MCLK_EN);
-
- CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~MCLK_EN);
-
- clk_disable(pcdev->clk);
-}
-
-/* Duplicate standard formats based on host capability of byte swapping */
-static const struct soc_mbus_lookup omap1_cam_formats[] = {
-{
- .code = MEDIA_BUS_FMT_UYVY8_2X8,
- .fmt = {
- .fourcc = V4L2_PIX_FMT_YUYV,
- .name = "YUYV",
- .bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_2X8_PADHI,
- .order = SOC_MBUS_ORDER_BE,
- .layout = SOC_MBUS_LAYOUT_PACKED,
- },
-}, {
- .code = MEDIA_BUS_FMT_VYUY8_2X8,
- .fmt = {
- .fourcc = V4L2_PIX_FMT_YVYU,
- .name = "YVYU",
- .bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_2X8_PADHI,
- .order = SOC_MBUS_ORDER_BE,
- .layout = SOC_MBUS_LAYOUT_PACKED,
- },
-}, {
- .code = MEDIA_BUS_FMT_YUYV8_2X8,
- .fmt = {
- .fourcc = V4L2_PIX_FMT_UYVY,
- .name = "UYVY",
- .bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_2X8_PADHI,
- .order = SOC_MBUS_ORDER_BE,
- .layout = SOC_MBUS_LAYOUT_PACKED,
- },
-}, {
- .code = MEDIA_BUS_FMT_YVYU8_2X8,
- .fmt = {
- .fourcc = V4L2_PIX_FMT_VYUY,
- .name = "VYUY",
- .bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_2X8_PADHI,
- .order = SOC_MBUS_ORDER_BE,
- .layout = SOC_MBUS_LAYOUT_PACKED,
- },
-}, {
- .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE,
- .fmt = {
- .fourcc = V4L2_PIX_FMT_RGB555,
- .name = "RGB555",
- .bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_2X8_PADHI,
- .order = SOC_MBUS_ORDER_BE,
- .layout = SOC_MBUS_LAYOUT_PACKED,
- },
-}, {
- .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
- .fmt = {
- .fourcc = V4L2_PIX_FMT_RGB555X,
- .name = "RGB555X",
- .bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_2X8_PADHI,
- .order = SOC_MBUS_ORDER_BE,
- .layout = SOC_MBUS_LAYOUT_PACKED,
- },
-}, {
- .code = MEDIA_BUS_FMT_RGB565_2X8_BE,
- .fmt = {
- .fourcc = V4L2_PIX_FMT_RGB565,
- .name = "RGB565",
- .bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_2X8_PADHI,
- .order = SOC_MBUS_ORDER_BE,
- .layout = SOC_MBUS_LAYOUT_PACKED,
- },
-}, {
- .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
- .fmt = {
- .fourcc = V4L2_PIX_FMT_RGB565X,
- .name = "RGB565X",
- .bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_2X8_PADHI,
- .order = SOC_MBUS_ORDER_BE,
- .layout = SOC_MBUS_LAYOUT_PACKED,
- },
-},
-};
-
-static int omap1_cam_get_formats(struct soc_camera_device *icd,
- unsigned int idx, struct soc_camera_format_xlate *xlate)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct device *dev = icd->parent;
- int formats = 0, ret;
- struct v4l2_subdev_mbus_code_enum code = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- .index = idx,
- };
- const struct soc_mbus_pixelfmt *fmt;
-
- ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code);
- if (ret < 0)
- /* No more formats */
- return 0;
-
- fmt = soc_mbus_get_fmtdesc(code.code);
- if (!fmt) {
- dev_warn(dev, "%s: unsupported format code #%d: %d\n", __func__,
- idx, code.code);
- return 0;
- }
-
- /* Check support for the requested bits-per-sample */
- if (fmt->bits_per_sample != 8)
- return 0;
-
- switch (code.code) {
- case MEDIA_BUS_FMT_YUYV8_2X8:
- case MEDIA_BUS_FMT_YVYU8_2X8:
- case MEDIA_BUS_FMT_UYVY8_2X8:
- case MEDIA_BUS_FMT_VYUY8_2X8:
- case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE:
- case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE:
- case MEDIA_BUS_FMT_RGB565_2X8_BE:
- case MEDIA_BUS_FMT_RGB565_2X8_LE:
- formats++;
- if (xlate) {
- xlate->host_fmt = soc_mbus_find_fmtdesc(code.code,
- omap1_cam_formats,
- ARRAY_SIZE(omap1_cam_formats));
- xlate->code = code.code;
- xlate++;
- dev_dbg(dev,
- "%s: providing format %s as byte swapped code #%d\n",
- __func__, xlate->host_fmt->name, code.code);
- }
- default:
- if (xlate)
- dev_dbg(dev,
- "%s: providing format %s in pass-through mode\n",
- __func__, fmt->name);
- }
- formats++;
- if (xlate) {
- xlate->host_fmt = fmt;
- xlate->code = code.code;
- xlate++;
- }
-
- return formats;
-}
-
-static bool is_dma_aligned(s32 bytes_per_line, unsigned int height,
- enum omap1_cam_vb_mode vb_mode)
-{
- int size = bytes_per_line * height;
-
- return IS_ALIGNED(bytes_per_line, DMA_ELEMENT_SIZE) &&
- IS_ALIGNED(size, DMA_FRAME_SIZE(vb_mode) * DMA_ELEMENT_SIZE);
-}
-
-static int dma_align(int *width, int *height,
- const struct soc_mbus_pixelfmt *fmt,
- enum omap1_cam_vb_mode vb_mode, bool enlarge)
-{
- s32 bytes_per_line = soc_mbus_bytes_per_line(*width, fmt);
-
- if (bytes_per_line < 0)
- return bytes_per_line;
-
- if (!is_dma_aligned(bytes_per_line, *height, vb_mode)) {
- unsigned int pxalign = __fls(bytes_per_line / *width);
- unsigned int salign = DMA_FRAME_SHIFT(vb_mode) +
- DMA_ELEMENT_SHIFT - pxalign;
- unsigned int incr = enlarge << salign;
-
- v4l_bound_align_image(width, 1, *width + incr, 0,
- height, 1, *height + incr, 0, salign);
- return 0;
- }
- return 1;
-}
-
-#define subdev_call_with_sense(pcdev, dev, icd, sd, op, function, args...) \
-({ \
- struct soc_camera_sense sense = { \
- .master_clock = pcdev->camexclk, \
- .pixel_clock_max = 0, \
- }; \
- int __ret; \
- \
- if (pcdev->pdata) \
- sense.pixel_clock_max = pcdev->pdata->lclk_khz_max * 1000; \
- icd->sense = &sense; \
- __ret = v4l2_subdev_call(sd, op, function, ##args); \
- icd->sense = NULL; \
- \
- if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) { \
- if (sense.pixel_clock > sense.pixel_clock_max) { \
- dev_err(dev, \
- "%s: pixel clock %lu set by the camera too high!\n", \
- __func__, sense.pixel_clock); \
- __ret = -EINVAL; \
- } \
- } \
- __ret; \
-})
-
-static int set_format(struct omap1_cam_dev *pcdev, struct device *dev,
- struct soc_camera_device *icd, struct v4l2_subdev *sd,
- struct v4l2_subdev_format *format,
- const struct soc_camera_format_xlate *xlate)
-{
- s32 bytes_per_line;
- struct v4l2_mbus_framefmt *mf = &format->format;
- int ret = subdev_call_with_sense(pcdev, dev, icd, sd, pad, set_fmt, NULL, format);
-
- if (ret < 0) {
- dev_err(dev, "%s: set_fmt failed\n", __func__);
- return ret;
- }
-
- if (mf->code != xlate->code) {
- dev_err(dev, "%s: unexpected pixel code change\n", __func__);
- return -EINVAL;
- }
-
- bytes_per_line = soc_mbus_bytes_per_line(mf->width, xlate->host_fmt);
- if (bytes_per_line < 0) {
- dev_err(dev, "%s: soc_mbus_bytes_per_line() failed\n",
- __func__);
- return bytes_per_line;
- }
-
- if (!is_dma_aligned(bytes_per_line, mf->height, pcdev->vb_mode)) {
- dev_err(dev, "%s: resulting geometry %ux%u not DMA aligned\n",
- __func__, mf->width, mf->height);
- return -EINVAL;
- }
- return 0;
-}
-
-static int omap1_cam_set_crop(struct soc_camera_device *icd,
- const struct v4l2_crop *crop)
-{
- const struct v4l2_rect *rect = &crop->c;
- const struct soc_camera_format_xlate *xlate = icd->current_fmt;
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct device *dev = icd->parent;
- struct soc_camera_host *ici = to_soc_camera_host(dev);
- struct omap1_cam_dev *pcdev = ici->priv;
- struct v4l2_subdev_format fmt = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- };
- struct v4l2_mbus_framefmt *mf = &fmt.format;
- int ret;
-
- ret = subdev_call_with_sense(pcdev, dev, icd, sd, video, s_crop, crop);
- if (ret < 0) {
- dev_warn(dev, "%s: failed to crop to %ux%u@%u:%u\n", __func__,
- rect->width, rect->height, rect->left, rect->top);
- return ret;
- }
-
- ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt);
- if (ret < 0) {
- dev_warn(dev, "%s: failed to fetch current format\n", __func__);
- return ret;
- }
-
- ret = dma_align(&mf->width, &mf->height, xlate->host_fmt, pcdev->vb_mode,
- false);
- if (ret < 0) {
- dev_err(dev, "%s: failed to align %ux%u %s with DMA\n",
- __func__, mf->width, mf->height,
- xlate->host_fmt->name);
- return ret;
- }
-
- if (!ret) {
- /* sensor returned geometry not DMA aligned, trying to fix */
- ret = set_format(pcdev, dev, icd, sd, &fmt, xlate);
- if (ret < 0) {
- dev_err(dev, "%s: failed to set format\n", __func__);
- return ret;
- }
- }
-
- icd->user_width = mf->width;
- icd->user_height = mf->height;
-
- return 0;
-}
-
-static int omap1_cam_set_fmt(struct soc_camera_device *icd,
- struct v4l2_format *f)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- const struct soc_camera_format_xlate *xlate;
- struct device *dev = icd->parent;
- struct soc_camera_host *ici = to_soc_camera_host(dev);
- struct omap1_cam_dev *pcdev = ici->priv;
- struct v4l2_pix_format *pix = &f->fmt.pix;
- struct v4l2_subdev_format format = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- };
- struct v4l2_mbus_framefmt *mf = &format.format;
- int ret;
-
- xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
- if (!xlate) {
- dev_warn(dev, "%s: format %#x not found\n", __func__,
- pix->pixelformat);
- return -EINVAL;
- }
-
- mf->width = pix->width;
- mf->height = pix->height;
- mf->field = pix->field;
- mf->colorspace = pix->colorspace;
- mf->code = xlate->code;
-
- ret = dma_align(&mf->width, &mf->height, xlate->host_fmt, pcdev->vb_mode,
- true);
- if (ret < 0) {
- dev_err(dev, "%s: failed to align %ux%u %s with DMA\n",
- __func__, pix->width, pix->height,
- xlate->host_fmt->name);
- return ret;
- }
-
- ret = set_format(pcdev, dev, icd, sd, &format, xlate);
- if (ret < 0) {
- dev_err(dev, "%s: failed to set format\n", __func__);
- return ret;
- }
-
- pix->width = mf->width;
- pix->height = mf->height;
- pix->field = mf->field;
- pix->colorspace = mf->colorspace;
- icd->current_fmt = xlate;
-
- return 0;
-}
-
-static int omap1_cam_try_fmt(struct soc_camera_device *icd,
- struct v4l2_format *f)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- const struct soc_camera_format_xlate *xlate;
- struct v4l2_pix_format *pix = &f->fmt.pix;
- struct v4l2_subdev_pad_config pad_cfg;
- struct v4l2_subdev_format format = {
- .which = V4L2_SUBDEV_FORMAT_TRY,
- };
- struct v4l2_mbus_framefmt *mf = &format.format;
- int ret;
- /* TODO: limit to mx1 hardware capabilities */
-
- xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
- if (!xlate) {
- dev_warn(icd->parent, "Format %#x not found\n",
- pix->pixelformat);
- return -EINVAL;
- }
-
- mf->width = pix->width;
- mf->height = pix->height;
- mf->field = pix->field;
- mf->colorspace = pix->colorspace;
- mf->code = xlate->code;
-
- /* limit to sensor capabilities */
- ret = v4l2_subdev_call(sd, pad, set_fmt, &pad_cfg, &format);
- if (ret < 0)
- return ret;
-
- pix->width = mf->width;
- pix->height = mf->height;
- pix->field = mf->field;
- pix->colorspace = mf->colorspace;
-
- return 0;
-}
-
-static bool sg_mode;
-
-/*
- * Local mmap_mapper wrapper,
- * used for detecting videobuf-dma-contig buffer allocation failures
- * and switching to videobuf-dma-sg automatically for future attempts.
- */
-static int omap1_cam_mmap_mapper(struct videobuf_queue *q,
- struct videobuf_buffer *buf,
- struct vm_area_struct *vma)
-{
- struct soc_camera_device *icd = q->priv_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct omap1_cam_dev *pcdev = ici->priv;
- int ret;
-
- ret = pcdev->mmap_mapper(q, buf, vma);
-
- if (ret == -ENOMEM)
- sg_mode = true;
-
- return ret;
-}
-
-static void omap1_cam_init_videobuf(struct videobuf_queue *q,
- struct soc_camera_device *icd)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct omap1_cam_dev *pcdev = ici->priv;
-
- if (!sg_mode)
- videobuf_queue_dma_contig_init(q, &omap1_videobuf_ops,
- icd->parent, &pcdev->lock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
- sizeof(struct omap1_cam_buf), icd, &ici->host_lock);
- else
- videobuf_queue_sg_init(q, &omap1_videobuf_ops,
- icd->parent, &pcdev->lock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
- sizeof(struct omap1_cam_buf), icd, &ici->host_lock);
-
- /* use videobuf mode (auto)selected with the module parameter */
- pcdev->vb_mode = sg_mode ? OMAP1_CAM_DMA_SG : OMAP1_CAM_DMA_CONTIG;
-
- /*
- * Ensure we substitute the videobuf-dma-contig version of the
- * mmap_mapper() callback with our own wrapper, used for switching
- * automatically to videobuf-dma-sg on buffer allocation failure.
- */
- if (!sg_mode && q->int_ops->mmap_mapper != omap1_cam_mmap_mapper) {
- pcdev->mmap_mapper = q->int_ops->mmap_mapper;
- q->int_ops->mmap_mapper = omap1_cam_mmap_mapper;
- }
-}
-
-static int omap1_cam_reqbufs(struct soc_camera_device *icd,
- struct v4l2_requestbuffers *p)
-{
- int i;
-
- /*
- * This is for locking debugging only. I removed spinlocks and now I
- * check whether .prepare is ever called on a linked buffer, or whether
- * a dma IRQ can occur for an in-work or unlinked buffer. Until now
- * it hadn't triggered
- */
- for (i = 0; i < p->count; i++) {
- struct omap1_cam_buf *buf = container_of(icd->vb_vidq.bufs[i],
- struct omap1_cam_buf, vb);
- buf->inwork = 0;
- INIT_LIST_HEAD(&buf->vb.queue);
- }
-
- return 0;
-}
-
-static int omap1_cam_querycap(struct soc_camera_host *ici,
- struct v4l2_capability *cap)
-{
- /* cap->name is set by the friendly caller:-> */
- strlcpy(cap->card, "OMAP1 Camera", sizeof(cap->card));
- cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
- cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
-
- return 0;
-}
-
-static int omap1_cam_set_bus_param(struct soc_camera_device *icd)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct device *dev = icd->parent;
- struct soc_camera_host *ici = to_soc_camera_host(dev);
- struct omap1_cam_dev *pcdev = ici->priv;
- u32 pixfmt = icd->current_fmt->host_fmt->fourcc;
- const struct soc_camera_format_xlate *xlate;
- const struct soc_mbus_pixelfmt *fmt;
- struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
- unsigned long common_flags;
- u32 ctrlclock, mode;
- int ret;
-
- ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
- if (!ret) {
- common_flags = soc_mbus_config_compatible(&cfg, SOCAM_BUS_FLAGS);
- if (!common_flags) {
- dev_warn(dev,
- "Flags incompatible: camera 0x%x, host 0x%x\n",
- cfg.flags, SOCAM_BUS_FLAGS);
- return -EINVAL;
- }
- } else if (ret != -ENOIOCTLCMD) {
- return ret;
- } else {
- common_flags = SOCAM_BUS_FLAGS;
- }
-
- /* Make choices, possibly based on platform configuration */
- if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
- (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
- if (!pcdev->pdata ||
- pcdev->pdata->flags & OMAP1_CAMERA_LCLK_RISING)
- common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
- else
- common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
- }
-
- cfg.flags = common_flags;
- ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
- if (ret < 0 && ret != -ENOIOCTLCMD) {
- dev_dbg(dev, "camera s_mbus_config(0x%lx) returned %d\n",
- common_flags, ret);
- return ret;
- }
-
- ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
- if (ctrlclock & LCLK_EN)
- CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
-
- if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) {
- dev_dbg(dev, "CTRLCLOCK_REG |= POLCLK\n");
- ctrlclock |= POLCLK;
- } else {
- dev_dbg(dev, "CTRLCLOCK_REG &= ~POLCLK\n");
- ctrlclock &= ~POLCLK;
- }
- CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
-
- if (ctrlclock & LCLK_EN)
- CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
-
- /* select bus endianness */
- xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
- fmt = xlate->host_fmt;
-
- mode = CAM_READ(pcdev, MODE) & ~(RAZ_FIFO | IRQ_MASK | DMA);
- if (fmt->order == SOC_MBUS_ORDER_LE) {
- dev_dbg(dev, "MODE_REG &= ~ORDERCAMD\n");
- CAM_WRITE(pcdev, MODE, mode & ~ORDERCAMD);
- } else {
- dev_dbg(dev, "MODE_REG |= ORDERCAMD\n");
- CAM_WRITE(pcdev, MODE, mode | ORDERCAMD);
- }
-
- return 0;
-}
-
-static unsigned int omap1_cam_poll(struct file *file, poll_table *pt)
-{
- struct soc_camera_device *icd = file->private_data;
- struct omap1_cam_buf *buf;
-
- buf = list_entry(icd->vb_vidq.stream.next, struct omap1_cam_buf,
- vb.stream);
-
- poll_wait(file, &buf->vb.done, pt);
-
- if (buf->vb.state == VIDEOBUF_DONE ||
- buf->vb.state == VIDEOBUF_ERROR)
- return POLLIN | POLLRDNORM;
-
- return 0;
-}
-
-static struct soc_camera_host_ops omap1_host_ops = {
- .owner = THIS_MODULE,
- .add = omap1_cam_add_device,
- .remove = omap1_cam_remove_device,
- .clock_start = omap1_cam_clock_start,
- .clock_stop = omap1_cam_clock_stop,
- .get_formats = omap1_cam_get_formats,
- .set_crop = omap1_cam_set_crop,
- .set_fmt = omap1_cam_set_fmt,
- .try_fmt = omap1_cam_try_fmt,
- .init_videobuf = omap1_cam_init_videobuf,
- .reqbufs = omap1_cam_reqbufs,
- .querycap = omap1_cam_querycap,
- .set_bus_param = omap1_cam_set_bus_param,
- .poll = omap1_cam_poll,
-};
-
-static int omap1_cam_probe(struct platform_device *pdev)
-{
- struct omap1_cam_dev *pcdev;
- struct resource *res;
- struct clk *clk;
- void __iomem *base;
- unsigned int irq;
- int err = 0;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- irq = platform_get_irq(pdev, 0);
- if (!res || (int)irq <= 0) {
- err = -ENODEV;
- goto exit;
- }
-
- clk = clk_get(&pdev->dev, "armper_ck");
- if (IS_ERR(clk)) {
- err = PTR_ERR(clk);
- goto exit;
- }
-
- pcdev = kzalloc(sizeof(*pcdev) + resource_size(res), GFP_KERNEL);
- if (!pcdev) {
- dev_err(&pdev->dev, "Could not allocate pcdev\n");
- err = -ENOMEM;
- goto exit_put_clk;
- }
-
- pcdev->res = res;
- pcdev->clk = clk;
-
- pcdev->pdata = pdev->dev.platform_data;
- if (pcdev->pdata) {
- pcdev->pflags = pcdev->pdata->flags;
- pcdev->camexclk = pcdev->pdata->camexclk_khz * 1000;
- }
-
- switch (pcdev->camexclk) {
- case 6000000:
- case 8000000:
- case 9600000:
- case 12000000:
- case 24000000:
- break;
- default:
- /* pcdev->camexclk != 0 => pcdev->pdata != NULL */
- dev_warn(&pdev->dev,
- "Incorrect sensor clock frequency %ld kHz, "
- "should be one of 0, 6, 8, 9.6, 12 or 24 MHz, "
- "please correct your platform data\n",
- pcdev->pdata->camexclk_khz);
- pcdev->camexclk = 0;
- case 0:
- dev_info(&pdev->dev, "Not providing sensor clock\n");
- }
-
- INIT_LIST_HEAD(&pcdev->capture);
- spin_lock_init(&pcdev->lock);
-
- /*
- * Request the region.
- */
- if (!request_mem_region(res->start, resource_size(res), DRIVER_NAME)) {
- err = -EBUSY;
- goto exit_kfree;
- }
-
- base = ioremap(res->start, resource_size(res));
- if (!base) {
- err = -ENOMEM;
- goto exit_release;
- }
- pcdev->irq = irq;
- pcdev->base = base;
-
- sensor_reset(pcdev, true);
-
- err = omap_request_dma(OMAP_DMA_CAMERA_IF_RX, DRIVER_NAME,
- dma_isr, (void *)pcdev, &pcdev->dma_ch);
- if (err < 0) {
- dev_err(&pdev->dev, "Can't request DMA for OMAP1 Camera\n");
- err = -EBUSY;
- goto exit_iounmap;
- }
- dev_dbg(&pdev->dev, "got DMA channel %d\n", pcdev->dma_ch);
-
- /* preconfigure DMA */
- omap_set_dma_src_params(pcdev->dma_ch, OMAP_DMA_PORT_TIPB,
- OMAP_DMA_AMODE_CONSTANT, res->start + REG_CAMDATA,
- 0, 0);
- omap_set_dma_dest_burst_mode(pcdev->dma_ch, OMAP_DMA_DATA_BURST_4);
- /* setup DMA autoinitialization */
- omap_dma_link_lch(pcdev->dma_ch, pcdev->dma_ch);
-
- err = request_irq(pcdev->irq, cam_isr, 0, DRIVER_NAME, pcdev);
- if (err) {
- dev_err(&pdev->dev, "Camera interrupt register failed\n");
- goto exit_free_dma;
- }
-
- pcdev->soc_host.drv_name = DRIVER_NAME;
- pcdev->soc_host.ops = &omap1_host_ops;
- pcdev->soc_host.priv = pcdev;
- pcdev->soc_host.v4l2_dev.dev = &pdev->dev;
- pcdev->soc_host.nr = pdev->id;
-
- err = soc_camera_host_register(&pcdev->soc_host);
- if (err)
- goto exit_free_irq;
-
- dev_info(&pdev->dev, "OMAP1 Camera Interface driver loaded\n");
-
- return 0;
-
-exit_free_irq:
- free_irq(pcdev->irq, pcdev);
-exit_free_dma:
- omap_free_dma(pcdev->dma_ch);
-exit_iounmap:
- iounmap(base);
-exit_release:
- release_mem_region(res->start, resource_size(res));
-exit_kfree:
- kfree(pcdev);
-exit_put_clk:
- clk_put(clk);
-exit:
- return err;
-}
-
-static int omap1_cam_remove(struct platform_device *pdev)
-{
- struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
- struct omap1_cam_dev *pcdev = container_of(soc_host,
- struct omap1_cam_dev, soc_host);
- struct resource *res;
-
- free_irq(pcdev->irq, pcdev);
-
- omap_free_dma(pcdev->dma_ch);
-
- soc_camera_host_unregister(soc_host);
-
- iounmap(pcdev->base);
-
- res = pcdev->res;
- release_mem_region(res->start, resource_size(res));
-
- clk_put(pcdev->clk);
-
- kfree(pcdev);
-
- dev_info(&pdev->dev, "OMAP1 Camera Interface driver unloaded\n");
-
- return 0;
-}
-
-static struct platform_driver omap1_cam_driver = {
- .driver = {
- .name = DRIVER_NAME,
- },
- .probe = omap1_cam_probe,
- .remove = omap1_cam_remove,
-};
-
-module_platform_driver(omap1_cam_driver);
-
-module_param(sg_mode, bool, 0644);
-MODULE_PARM_DESC(sg_mode, "videobuf mode, 0: dma-contig (default), 1: dma-sg");
-
-MODULE_DESCRIPTION("OMAP1 Camera Interface driver");
-MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>");
-MODULE_LICENSE("GPL v2");
-MODULE_VERSION(DRIVER_VERSION);
-MODULE_ALIAS("platform:" DRIVER_NAME);
#include <linux/clk.h>
#include <linux/sched.h>
#include <linux/slab.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/dma/pxa-dma.h>
#include <media/v4l2-common.h>
#include <media/v4l2-dev.h>
#include <linux/videodev2.h>
-#include <mach/dma.h>
#include <linux/platform_data/media/camera-pxa.h>
#define PXA_CAM_VERSION "0.0.6"
DMA_V = 0x4,
};
-/* descriptor needed for the PXA DMA engine */
-struct pxa_cam_dma {
- dma_addr_t sg_dma;
- struct pxa_dma_desc *sg_cpu;
- size_t sg_size;
- int sglen;
-};
-
/* buffer for one video frame */
struct pxa_buffer {
/* common v4l buffer stuff -- must be first */
struct videobuf_buffer vb;
u32 code;
/* our descriptor lists for Y, U and V channels */
- struct pxa_cam_dma dmas[3];
+ struct dma_async_tx_descriptor *descs[3];
+ dma_cookie_t cookie[3];
+ struct scatterlist *sg[3];
+ int sg_len[3];
int inwork;
enum pxa_camera_active_dma active_dma;
};
void __iomem *base;
int channels;
- unsigned int dma_chans[3];
+ struct dma_chan *dma_chans[3];
struct pxacamera_platform_data *pdata;
struct resource *res;
spinlock_t lock;
struct pxa_buffer *active;
- struct pxa_dma_desc *sg_tail[3];
+ struct tasklet_struct task_eof;
u32 save_cicr[5];
};
static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf)
{
struct soc_camera_device *icd = vq->priv_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
int i;
* longer in STATE_QUEUED or STATE_ACTIVE
*/
videobuf_waiton(vq, &buf->vb, 0, 0);
- videobuf_dma_unmap(vq->dev, dma);
- videobuf_dma_free(dma);
- for (i = 0; i < ARRAY_SIZE(buf->dmas); i++) {
- if (buf->dmas[i].sg_cpu)
- dma_free_coherent(ici->v4l2_dev.dev,
- buf->dmas[i].sg_size,
- buf->dmas[i].sg_cpu,
- buf->dmas[i].sg_dma);
- buf->dmas[i].sg_cpu = NULL;
+ for (i = 0; i < 3 && buf->descs[i]; i++) {
+ dmaengine_desc_free(buf->descs[i]);
+ kfree(buf->sg[i]);
+ buf->descs[i] = NULL;
+ buf->sg[i] = NULL;
+ buf->sg_len[i] = 0;
}
+ videobuf_dma_unmap(vq->dev, dma);
+ videobuf_dma_free(dma);
buf->vb.state = VIDEOBUF_NEEDS_INIT;
+
+ dev_dbg(icd->parent, "%s end (vb=0x%p) 0x%08lx %d\n", __func__,
+ &buf->vb, buf->vb.baddr, buf->vb.bsize);
}
-static int calculate_dma_sglen(struct scatterlist *sglist, int sglen,
- int sg_first_ofs, int size)
+static void pxa_camera_dma_irq(struct pxa_camera_dev *pcdev,
+ enum pxa_camera_active_dma act_dma);
+
+static void pxa_camera_dma_irq_y(void *data)
{
- int i, offset, dma_len, xfer_len;
- struct scatterlist *sg;
+ struct pxa_camera_dev *pcdev = data;
- offset = sg_first_ofs;
- for_each_sg(sglist, sg, sglen, i) {
- dma_len = sg_dma_len(sg);
+ pxa_camera_dma_irq(pcdev, DMA_Y);
+}
- /* PXA27x Developer's Manual 27.4.4.1: round up to 8 bytes */
- xfer_len = roundup(min(dma_len - offset, size), 8);
+static void pxa_camera_dma_irq_u(void *data)
+{
+ struct pxa_camera_dev *pcdev = data;
- size = max(0, size - xfer_len);
- offset = 0;
- if (size == 0)
- break;
- }
+ pxa_camera_dma_irq(pcdev, DMA_U);
+}
+
+static void pxa_camera_dma_irq_v(void *data)
+{
+ struct pxa_camera_dev *pcdev = data;
- BUG_ON(size != 0);
- return i + 1;
+ pxa_camera_dma_irq(pcdev, DMA_V);
}
/**
* @channel: dma channel (0 => 'Y', 1 => 'U', 2 => 'V')
* @cibr: camera Receive Buffer Register
* @size: bytes to transfer
- * @sg_first: first element of sg_list
- * @sg_first_ofs: offset in first element of sg_list
+ * @offset: offset in videobuffer of the first byte to transfer
*
* Prepares the pxa dma descriptors to transfer one camera channel.
- * Beware sg_first and sg_first_ofs are both input and output parameters.
*
- * Returns 0 or -ENOMEM if no coherent memory is available
+ * Returns 0 if success or -ENOMEM if no memory is available
*/
static int pxa_init_dma_channel(struct pxa_camera_dev *pcdev,
struct pxa_buffer *buf,
struct videobuf_dmabuf *dma, int channel,
- int cibr, int size,
- struct scatterlist **sg_first, int *sg_first_ofs)
+ int cibr, int size, int offset)
{
- struct pxa_cam_dma *pxa_dma = &buf->dmas[channel];
- struct device *dev = pcdev->soc_host.v4l2_dev.dev;
- struct scatterlist *sg;
- int i, offset, sglen;
- int dma_len = 0, xfer_len = 0;
-
- if (pxa_dma->sg_cpu)
- dma_free_coherent(dev, pxa_dma->sg_size,
- pxa_dma->sg_cpu, pxa_dma->sg_dma);
-
- sglen = calculate_dma_sglen(*sg_first, dma->sglen,
- *sg_first_ofs, size);
-
- pxa_dma->sg_size = (sglen + 1) * sizeof(struct pxa_dma_desc);
- pxa_dma->sg_cpu = dma_alloc_coherent(dev, pxa_dma->sg_size,
- &pxa_dma->sg_dma, GFP_KERNEL);
- if (!pxa_dma->sg_cpu)
- return -ENOMEM;
-
- pxa_dma->sglen = sglen;
- offset = *sg_first_ofs;
-
- dev_dbg(dev, "DMA: sg_first=%p, sglen=%d, ofs=%d, dma.desc=%x\n",
- *sg_first, sglen, *sg_first_ofs, pxa_dma->sg_dma);
-
-
- for_each_sg(*sg_first, sg, sglen, i) {
- dma_len = sg_dma_len(sg);
-
- /* PXA27x Developer's Manual 27.4.4.1: round up to 8 bytes */
- xfer_len = roundup(min(dma_len - offset, size), 8);
-
- size = max(0, size - xfer_len);
-
- pxa_dma->sg_cpu[i].dsadr = pcdev->res->start + cibr;
- pxa_dma->sg_cpu[i].dtadr = sg_dma_address(sg) + offset;
- pxa_dma->sg_cpu[i].dcmd =
- DCMD_FLOWSRC | DCMD_BURST8 | DCMD_INCTRGADDR | xfer_len;
-#ifdef DEBUG
- if (!i)
- pxa_dma->sg_cpu[i].dcmd |= DCMD_STARTIRQEN;
-#endif
- pxa_dma->sg_cpu[i].ddadr =
- pxa_dma->sg_dma + (i + 1) * sizeof(struct pxa_dma_desc);
-
- dev_vdbg(dev, "DMA: desc.%08x->@phys=0x%08x, len=%d\n",
- pxa_dma->sg_dma + i * sizeof(struct pxa_dma_desc),
- sg_dma_address(sg) + offset, xfer_len);
- offset = 0;
-
- if (size == 0)
- break;
+ struct dma_chan *dma_chan = pcdev->dma_chans[channel];
+ struct scatterlist *sg = buf->sg[channel];
+ int sglen = buf->sg_len[channel];
+ struct dma_async_tx_descriptor *tx;
+
+ tx = dmaengine_prep_slave_sg(dma_chan, sg, sglen, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT | DMA_CTRL_REUSE);
+ if (!tx) {
+ dev_err(pcdev->soc_host.v4l2_dev.dev,
+ "dmaengine_prep_slave_sg failed\n");
+ goto fail;
}
- pxa_dma->sg_cpu[sglen].ddadr = DDADR_STOP;
- pxa_dma->sg_cpu[sglen].dcmd = DCMD_FLOWSRC | DCMD_BURST8 | DCMD_ENDIRQEN;
-
- /*
- * Handle 1 special case :
- * - in 3 planes (YUV422P format), we might finish with xfer_len equal
- * to dma_len (end on PAGE boundary). In this case, the sg element
- * for next plane should be the next after the last used to store the
- * last scatter gather RAM page
- */
- if (xfer_len >= dma_len) {
- *sg_first_ofs = xfer_len - dma_len;
- *sg_first = sg_next(sg);
- } else {
- *sg_first_ofs = xfer_len;
- *sg_first = sg;
+ tx->callback_param = pcdev;
+ switch (channel) {
+ case 0:
+ tx->callback = pxa_camera_dma_irq_y;
+ break;
+ case 1:
+ tx->callback = pxa_camera_dma_irq_u;
+ break;
+ case 2:
+ tx->callback = pxa_camera_dma_irq_v;
+ break;
}
+ buf->descs[channel] = tx;
return 0;
+fail:
+ kfree(sg);
+
+ dev_dbg(pcdev->soc_host.v4l2_dev.dev,
+ "%s (vb=0x%p) dma_tx=%p\n",
+ __func__, &buf->vb, tx);
+
+ return -ENOMEM;
}
static void pxa_videobuf_set_actdma(struct pxa_camera_dev *pcdev,
struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb);
int ret;
int size_y, size_u = 0, size_v = 0;
+ size_t sizes[3];
dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
vb, vb->baddr, vb->bsize);
if (vb->state == VIDEOBUF_NEEDS_INIT) {
int size = vb->size;
- int next_ofs = 0;
struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
- struct scatterlist *sg;
ret = videobuf_iolock(vq, vb, NULL);
if (ret)
- goto fail;
+ goto out;
if (pcdev->channels == 3) {
size_y = size / 2;
size_y = size;
}
- sg = dma->sglist;
+ sizes[0] = size_y;
+ sizes[1] = size_u;
+ sizes[2] = size_v;
+ ret = sg_split(dma->sglist, dma->sglen, 0, pcdev->channels,
+ sizes, buf->sg, buf->sg_len, GFP_KERNEL);
+ if (ret < 0) {
+ dev_err(dev, "sg_split failed: %d\n", ret);
+ goto fail;
+ }
/* init DMA for Y channel */
- ret = pxa_init_dma_channel(pcdev, buf, dma, 0, CIBR0, size_y,
- &sg, &next_ofs);
+ ret = pxa_init_dma_channel(pcdev, buf, dma, 0, CIBR0,
+ size_y, 0);
if (ret) {
dev_err(dev, "DMA initialization for Y/RGB failed\n");
goto fail;
/* init DMA for U channel */
if (size_u)
ret = pxa_init_dma_channel(pcdev, buf, dma, 1, CIBR1,
- size_u, &sg, &next_ofs);
+ size_u, size_y);
if (ret) {
dev_err(dev, "DMA initialization for U failed\n");
- goto fail_u;
+ goto fail;
}
/* init DMA for V channel */
if (size_v)
ret = pxa_init_dma_channel(pcdev, buf, dma, 2, CIBR2,
- size_v, &sg, &next_ofs);
+ size_v, size_y + size_u);
if (ret) {
dev_err(dev, "DMA initialization for V failed\n");
- goto fail_v;
+ goto fail;
}
vb->state = VIDEOBUF_PREPARED;
return 0;
-fail_v:
- dma_free_coherent(dev, buf->dmas[1].sg_size,
- buf->dmas[1].sg_cpu, buf->dmas[1].sg_dma);
-fail_u:
- dma_free_coherent(dev, buf->dmas[0].sg_size,
- buf->dmas[0].sg_cpu, buf->dmas[0].sg_dma);
fail:
free_buffer(vq, buf);
out:
for (i = 0; i < pcdev->channels; i++) {
dev_dbg(pcdev->soc_host.v4l2_dev.dev,
- "%s (channel=%d) ddadr=%08x\n", __func__,
- i, active->dmas[i].sg_dma);
- DDADR(pcdev->dma_chans[i]) = active->dmas[i].sg_dma;
- DCSR(pcdev->dma_chans[i]) = DCSR_RUN;
+ "%s (channel=%d)\n", __func__, i);
+ dma_async_issue_pending(pcdev->dma_chans[i]);
}
}
for (i = 0; i < pcdev->channels; i++) {
dev_dbg(pcdev->soc_host.v4l2_dev.dev,
"%s (channel=%d)\n", __func__, i);
- DCSR(pcdev->dma_chans[i]) = 0;
+ dmaengine_terminate_all(pcdev->dma_chans[i]);
}
}
struct pxa_buffer *buf)
{
int i;
- struct pxa_dma_desc *buf_last_desc;
for (i = 0; i < pcdev->channels; i++) {
- buf_last_desc = buf->dmas[i].sg_cpu + buf->dmas[i].sglen;
- buf_last_desc->ddadr = DDADR_STOP;
-
- if (pcdev->sg_tail[i])
- /* Link the new buffer to the old tail */
- pcdev->sg_tail[i]->ddadr = buf->dmas[i].sg_dma;
-
- /* Update the channel tail */
- pcdev->sg_tail[i] = buf_last_desc;
+ buf->cookie[i] = dmaengine_submit(buf->descs[i]);
+ dev_dbg(pcdev->soc_host.v4l2_dev.dev,
+ "%s (channel=%d) : submit vb=%p cookie=%d\n",
+ __func__, i, buf, buf->descs[i]->cookie);
}
}
unsigned long cicr0;
dev_dbg(pcdev->soc_host.v4l2_dev.dev, "%s\n", __func__);
+ __raw_writel(__raw_readl(pcdev->base + CISR), pcdev->base + CISR);
/* Enable End-Of-Frame Interrupt */
cicr0 = __raw_readl(pcdev->base + CICR0) | CICR0_ENB;
cicr0 &= ~CICR0_EOFM;
struct videobuf_buffer *vb,
struct pxa_buffer *buf)
{
- int i;
-
/* _init is used to debug races, see comment in pxa_camera_reqbufs() */
list_del_init(&vb->queue);
vb->state = VIDEOBUF_DONE;
if (list_empty(&pcdev->capture)) {
pxa_camera_stop_capture(pcdev);
- for (i = 0; i < pcdev->channels; i++)
- pcdev->sg_tail[i] = NULL;
return;
}
*
* Context: should only be called within the dma irq handler
*/
-static void pxa_camera_check_link_miss(struct pxa_camera_dev *pcdev)
+static void pxa_camera_check_link_miss(struct pxa_camera_dev *pcdev,
+ dma_cookie_t last_submitted,
+ dma_cookie_t last_issued)
{
- int i, is_dma_stopped = 1;
+ bool is_dma_stopped = last_submitted != last_issued;
- for (i = 0; i < pcdev->channels; i++)
- if (DDADR(pcdev->dma_chans[i]) != DDADR_STOP)
- is_dma_stopped = 0;
dev_dbg(pcdev->soc_host.v4l2_dev.dev,
- "%s : top queued buffer=%p, dma_stopped=%d\n",
+ "%s : top queued buffer=%p, is_dma_stopped=%d\n",
__func__, pcdev->active, is_dma_stopped);
+
if (pcdev->active && is_dma_stopped)
pxa_camera_start_capture(pcdev);
}
-static void pxa_camera_dma_irq(int channel, struct pxa_camera_dev *pcdev,
+static void pxa_camera_dma_irq(struct pxa_camera_dev *pcdev,
enum pxa_camera_active_dma act_dma)
{
struct device *dev = pcdev->soc_host.v4l2_dev.dev;
- struct pxa_buffer *buf;
+ struct pxa_buffer *buf, *last_buf;
unsigned long flags;
- u32 status, camera_status, overrun;
+ u32 camera_status, overrun;
+ int chan;
struct videobuf_buffer *vb;
+ enum dma_status last_status;
+ dma_cookie_t last_issued;
spin_lock_irqsave(&pcdev->lock, flags);
- status = DCSR(channel);
- DCSR(channel) = status;
-
camera_status = __raw_readl(pcdev->base + CISR);
+ dev_dbg(dev, "camera dma irq, cisr=0x%x dma=%d\n",
+ camera_status, act_dma);
overrun = CISR_IFO_0;
if (pcdev->channels == 3)
overrun |= CISR_IFO_1 | CISR_IFO_2;
- if (status & DCSR_BUSERR) {
- dev_err(dev, "DMA Bus Error IRQ!\n");
- goto out;
- }
-
- if (!(status & (DCSR_ENDINTR | DCSR_STARTINTR))) {
- dev_err(dev, "Unknown DMA IRQ source, status: 0x%08x\n",
- status);
- goto out;
- }
-
/*
* pcdev->active should not be NULL in DMA irq handler.
*
buf = container_of(vb, struct pxa_buffer, vb);
WARN_ON(buf->inwork || list_empty(&vb->queue));
- dev_dbg(dev, "%s channel=%d %s%s(vb=0x%p) dma.desc=%x\n",
- __func__, channel, status & DCSR_STARTINTR ? "SOF " : "",
- status & DCSR_ENDINTR ? "EOF " : "", vb, DDADR(channel));
-
- if (status & DCSR_ENDINTR) {
- /*
- * It's normal if the last frame creates an overrun, as there
- * are no more DMA descriptors to fetch from QCI fifos
- */
- if (camera_status & overrun &&
- !list_is_last(pcdev->capture.next, &pcdev->capture)) {
- dev_dbg(dev, "FIFO overrun! CISR: %x\n",
- camera_status);
- pxa_camera_stop_capture(pcdev);
- pxa_camera_start_capture(pcdev);
- goto out;
- }
- buf->active_dma &= ~act_dma;
- if (!buf->active_dma) {
- pxa_camera_wakeup(pcdev, vb, buf);
- pxa_camera_check_link_miss(pcdev);
- }
+ /*
+ * It's normal if the last frame creates an overrun, as there
+ * are no more DMA descriptors to fetch from QCI fifos
+ */
+ switch (act_dma) {
+ case DMA_U:
+ chan = 1;
+ break;
+ case DMA_V:
+ chan = 2;
+ break;
+ default:
+ chan = 0;
+ break;
+ }
+ last_buf = list_entry(pcdev->capture.prev,
+ struct pxa_buffer, vb.queue);
+ last_status = dma_async_is_tx_complete(pcdev->dma_chans[chan],
+ last_buf->cookie[chan],
+ NULL, &last_issued);
+ if (camera_status & overrun &&
+ last_status != DMA_COMPLETE) {
+ dev_dbg(dev, "FIFO overrun! CISR: %x\n",
+ camera_status);
+ pxa_camera_stop_capture(pcdev);
+ list_for_each_entry(buf, &pcdev->capture, vb.queue)
+ pxa_dma_add_tail_buf(pcdev, buf);
+ pxa_camera_start_capture(pcdev);
+ goto out;
+ }
+ buf->active_dma &= ~act_dma;
+ if (!buf->active_dma) {
+ pxa_camera_wakeup(pcdev, vb, buf);
+ pxa_camera_check_link_miss(pcdev, last_buf->cookie[chan],
+ last_issued);
}
out:
spin_unlock_irqrestore(&pcdev->lock, flags);
}
-static void pxa_camera_dma_irq_y(int channel, void *data)
-{
- struct pxa_camera_dev *pcdev = data;
- pxa_camera_dma_irq(channel, pcdev, DMA_Y);
-}
-
-static void pxa_camera_dma_irq_u(int channel, void *data)
-{
- struct pxa_camera_dev *pcdev = data;
- pxa_camera_dma_irq(channel, pcdev, DMA_U);
-}
-
-static void pxa_camera_dma_irq_v(int channel, void *data)
-{
- struct pxa_camera_dev *pcdev = data;
- pxa_camera_dma_irq(channel, pcdev, DMA_V);
-}
-
static struct videobuf_queue_ops pxa_videobuf_ops = {
.buf_setup = pxa_videobuf_setup,
.buf_prepare = pxa_videobuf_prepare,
clk_disable_unprepare(pcdev->clk);
}
-static irqreturn_t pxa_camera_irq(int irq, void *data)
+static void pxa_camera_eof(unsigned long arg)
{
- struct pxa_camera_dev *pcdev = data;
- unsigned long status, cifr, cicr0;
+ struct pxa_camera_dev *pcdev = (struct pxa_camera_dev *)arg;
+ unsigned long cifr;
struct pxa_buffer *buf;
struct videobuf_buffer *vb;
+ dev_dbg(pcdev->soc_host.v4l2_dev.dev,
+ "Camera interrupt status 0x%x\n",
+ __raw_readl(pcdev->base + CISR));
+
+ /* Reset the FIFOs */
+ cifr = __raw_readl(pcdev->base + CIFR) | CIFR_RESET_F;
+ __raw_writel(cifr, pcdev->base + CIFR);
+
+ pcdev->active = list_first_entry(&pcdev->capture,
+ struct pxa_buffer, vb.queue);
+ vb = &pcdev->active->vb;
+ buf = container_of(vb, struct pxa_buffer, vb);
+ pxa_videobuf_set_actdma(pcdev, buf);
+
+ pxa_dma_start_channels(pcdev);
+}
+
+static irqreturn_t pxa_camera_irq(int irq, void *data)
+{
+ struct pxa_camera_dev *pcdev = data;
+ unsigned long status, cicr0;
+
status = __raw_readl(pcdev->base + CISR);
dev_dbg(pcdev->soc_host.v4l2_dev.dev,
"Camera interrupt status 0x%lx\n", status);
__raw_writel(status, pcdev->base + CISR);
if (status & CISR_EOF) {
- /* Reset the FIFOs */
- cifr = __raw_readl(pcdev->base + CIFR) | CIFR_RESET_F;
- __raw_writel(cifr, pcdev->base + CIFR);
-
- pcdev->active = list_first_entry(&pcdev->capture,
- struct pxa_buffer, vb.queue);
- vb = &pcdev->active->vb;
- buf = container_of(vb, struct pxa_buffer, vb);
- pxa_videobuf_set_actdma(pcdev, buf);
-
- pxa_dma_start_channels(pcdev);
-
cicr0 = __raw_readl(pcdev->base + CICR0) | CICR0_EOFM;
__raw_writel(cicr0, pcdev->base + CICR0);
+ tasklet_schedule(&pcdev->task_eof);
}
return IRQ_HANDLED;
__raw_writel(0x3ff, pcdev->base + CICR0);
/* Stop DMA engine */
- DCSR(pcdev->dma_chans[0]) = 0;
- DCSR(pcdev->dma_chans[1]) = 0;
- DCSR(pcdev->dma_chans[2]) = 0;
-
+ pxa_dma_stop_channels(pcdev);
pxa_camera_deactivate(pcdev);
}
struct pxa_camera_dev *pcdev = ici->priv;
int i = 0, ret = 0;
- DRCMR(68) = pcdev->dma_chans[0] | DRCMR_MAPVLD;
- DRCMR(69) = pcdev->dma_chans[1] | DRCMR_MAPVLD;
- DRCMR(70) = pcdev->dma_chans[2] | DRCMR_MAPVLD;
-
__raw_writel(pcdev->save_cicr[i++] & ~CICR0_ENB, pcdev->base + CICR0);
__raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR1);
__raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR2);
struct pxa_camera_dev *pcdev;
struct resource *res;
void __iomem *base;
+ struct dma_slave_config config = {
+ .src_addr_width = 0,
+ .src_maxburst = 8,
+ .direction = DMA_DEV_TO_MEM,
+ };
+ dma_cap_mask_t mask;
+ struct pxad_param params;
int irq;
- int err = 0;
+ int err = 0, i;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0);
pcdev->base = base;
/* request dma */
- err = pxa_request_dma("CI_Y", DMA_PRIO_HIGH,
- pxa_camera_dma_irq_y, pcdev);
- if (err < 0) {
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ dma_cap_set(DMA_PRIVATE, mask);
+
+ params.prio = 0;
+ params.drcmr = 68;
+ pcdev->dma_chans[0] =
+ dma_request_slave_channel_compat(mask, pxad_filter_fn,
+ ¶ms, &pdev->dev, "CI_Y");
+ if (!pcdev->dma_chans[0]) {
dev_err(&pdev->dev, "Can't request DMA for Y\n");
- return err;
+ return -ENODEV;
}
- pcdev->dma_chans[0] = err;
- dev_dbg(&pdev->dev, "got DMA channel %d\n", pcdev->dma_chans[0]);
- err = pxa_request_dma("CI_U", DMA_PRIO_HIGH,
- pxa_camera_dma_irq_u, pcdev);
- if (err < 0) {
- dev_err(&pdev->dev, "Can't request DMA for U\n");
+ params.drcmr = 69;
+ pcdev->dma_chans[1] =
+ dma_request_slave_channel_compat(mask, pxad_filter_fn,
+ ¶ms, &pdev->dev, "CI_U");
+ if (!pcdev->dma_chans[1]) {
+ dev_err(&pdev->dev, "Can't request DMA for Y\n");
goto exit_free_dma_y;
}
- pcdev->dma_chans[1] = err;
- dev_dbg(&pdev->dev, "got DMA channel (U) %d\n", pcdev->dma_chans[1]);
- err = pxa_request_dma("CI_V", DMA_PRIO_HIGH,
- pxa_camera_dma_irq_v, pcdev);
- if (err < 0) {
+ params.drcmr = 70;
+ pcdev->dma_chans[2] =
+ dma_request_slave_channel_compat(mask, pxad_filter_fn,
+ ¶ms, &pdev->dev, "CI_V");
+ if (!pcdev->dma_chans[2]) {
dev_err(&pdev->dev, "Can't request DMA for V\n");
goto exit_free_dma_u;
}
- pcdev->dma_chans[2] = err;
- dev_dbg(&pdev->dev, "got DMA channel (V) %d\n", pcdev->dma_chans[2]);
- DRCMR(68) = pcdev->dma_chans[0] | DRCMR_MAPVLD;
- DRCMR(69) = pcdev->dma_chans[1] | DRCMR_MAPVLD;
- DRCMR(70) = pcdev->dma_chans[2] | DRCMR_MAPVLD;
+ for (i = 0; i < 3; i++) {
+ config.src_addr = pcdev->res->start + CIBR0 + i * 8;
+ err = dmaengine_slave_config(pcdev->dma_chans[i], &config);
+ if (err < 0) {
+ dev_err(&pdev->dev, "dma slave config failed: %d\n",
+ err);
+ goto exit_free_dma;
+ }
+ }
/* request irq */
err = devm_request_irq(&pdev->dev, pcdev->irq, pxa_camera_irq, 0,
pcdev->soc_host.priv = pcdev;
pcdev->soc_host.v4l2_dev.dev = &pdev->dev;
pcdev->soc_host.nr = pdev->id;
+ tasklet_init(&pcdev->task_eof, pxa_camera_eof, (unsigned long)pcdev);
err = soc_camera_host_register(&pcdev->soc_host);
if (err)
return 0;
exit_free_dma:
- pxa_free_dma(pcdev->dma_chans[2]);
+ dma_release_channel(pcdev->dma_chans[2]);
exit_free_dma_u:
- pxa_free_dma(pcdev->dma_chans[1]);
+ dma_release_channel(pcdev->dma_chans[1]);
exit_free_dma_y:
- pxa_free_dma(pcdev->dma_chans[0]);
+ dma_release_channel(pcdev->dma_chans[0]);
return err;
}
struct pxa_camera_dev *pcdev = container_of(soc_host,
struct pxa_camera_dev, soc_host);
- pxa_free_dma(pcdev->dma_chans[0]);
- pxa_free_dma(pcdev->dma_chans[1]);
- pxa_free_dma(pcdev->dma_chans[2]);
+ dma_release_channel(pcdev->dma_chans[0]);
+ dma_release_channel(pcdev->dma_chans[1]);
+ dma_release_channel(pcdev->dma_chans[2]);
soc_camera_host_unregister(soc_host);
#define VNDMR_EXRGB (1 << 8)
#define VNDMR_BPSM (1 << 4)
#define VNDMR_DTMD_YCSEP (1 << 1)
-#define VNDMR_DTMD_ARGB1555 (1 << 0)
+#define VNDMR_DTMD_ARGB (1 << 0)
/* Video n Data Mode Register 2 bits */
#define VNDMR2_VPS (1 << 30)
#define RCAR_VIN_BT656 (1 << 3)
enum chip_id {
+ RCAR_GEN3,
RCAR_GEN2,
RCAR_H1,
RCAR_M1,
output_is_yuv = true;
break;
case V4L2_PIX_FMT_RGB555X:
- dmr = VNDMR_DTMD_ARGB1555;
+ dmr = VNDMR_DTMD_ARGB;
break;
case V4L2_PIX_FMT_RGB565:
dmr = 0;
break;
case V4L2_PIX_FMT_RGB32:
- if (priv->chip == RCAR_GEN2 || priv->chip == RCAR_H1 ||
- priv->chip == RCAR_E1) {
- dmr = VNDMR_EXRGB;
- break;
- }
+ if (priv->chip != RCAR_GEN2 && priv->chip != RCAR_H1 &&
+ priv->chip != RCAR_E1)
+ goto e_format;
+
+ dmr = VNDMR_EXRGB;
+ break;
+ case V4L2_PIX_FMT_ARGB32:
+ if (priv->chip != RCAR_GEN3)
+ goto e_format;
+
+ dmr = VNDMR_EXRGB | VNDMR_DTMD_ARGB;
+ break;
default:
- dev_warn(icd->parent, "Invalid fourcc format (0x%x)\n",
- icd->current_fmt->host_fmt->fourcc);
- return -EINVAL;
+ goto e_format;
}
/* Always update on field change */
iowrite32(vnmc | VNMC_ME, priv->base + VNMC_REG);
return 0;
+
+e_format:
+ dev_warn(icd->parent, "Invalid fourcc format (0x%x)\n",
+ icd->current_fmt->host_fmt->fourcc);
+ return -EINVAL;
}
static void rcar_vin_capture(struct rcar_vin_priv *priv)
.order = SOC_MBUS_ORDER_LE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
+ {
+ .fourcc = V4L2_PIX_FMT_ARGB32,
+ .name = "ARGB8888",
+ .bits_per_sample = 32,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
};
static int rcar_vin_get_formats(struct soc_camera_device *icd, unsigned int idx,
case V4L2_PIX_FMT_RGB32:
can_scale = priv->chip != RCAR_E1;
break;
+ case V4L2_PIX_FMT_ARGB32:
case V4L2_PIX_FMT_UYVY:
case V4L2_PIX_FMT_YUYV:
case V4L2_PIX_FMT_RGB565:
#ifdef CONFIG_OF
static const struct of_device_id rcar_vin_of_table[] = {
+ { .compatible = "renesas,vin-r8a7795", .data = (void *)RCAR_GEN3 },
{ .compatible = "renesas,vin-r8a7794", .data = (void *)RCAR_GEN2 },
{ .compatible = "renesas,vin-r8a7793", .data = (void *)RCAR_GEN2 },
{ .compatible = "renesas,vin-r8a7791", .data = (void *)RCAR_GEN2 },
unsigned int *count, unsigned int *num_planes,
unsigned int sizes[], void *alloc_ctxs[])
{
- struct soc_camera_device *icd = container_of(vq,
- struct soc_camera_device, vb2_vidq);
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- struct soc_camera_device *icd = container_of(vb->vb2_queue,
- struct soc_camera_device, vb2_vidq);
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vbuf);
static void sh_mobile_ceu_videobuf_release(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- struct soc_camera_device *icd = container_of(vb->vb2_queue,
- struct soc_camera_device, vb2_vidq);
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vbuf);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
static int sh_mobile_ceu_videobuf_init(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- struct soc_camera_device *icd = container_of(vb->vb2_queue,
- struct soc_camera_device, vb2_vidq);
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
static void sh_mobile_ceu_stop_streaming(struct vb2_queue *q)
{
- struct soc_camera_device *icd = container_of(q, struct soc_camera_device, vb2_vidq);
+ struct soc_camera_device *icd = soc_camera_from_vb2q(q);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
struct list_head *buf_head, *tmp;
struct channel_info *tsin, int chan_num)
{
struct tda18212_config *tda18212;
- struct stv6110x_devctl *fe2;
+ const struct stv6110x_devctl *fe2;
struct i2c_client *client;
struct i2c_board_info tda18212_info = {
.type = "tda18212",
ti-vpe-y := vpe.o sc.o csc.o vpdma.o
ccflags-$(CONFIG_VIDEO_TI_VPE_DEBUG) += -DDEBUG
+
+obj-$(CONFIG_VIDEO_TI_CAL) += ti-cal.o
+
+ti-cal-y := cal.o
--- /dev/null
+/*
+ * TI CAL camera interface driver
+ *
+ * Copyright (c) 2015 Texas Instruments Inc.
+ * Benoit Parrot, <bparrot@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation
+ */
+
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioctl.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+
+#include <media/v4l2-of.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-common.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+#include "cal_regs.h"
+
+#define CAL_MODULE_NAME "cal"
+
+#define MAX_WIDTH 1920
+#define MAX_HEIGHT 1200
+
+#define CAL_VERSION "0.1.0"
+
+MODULE_DESCRIPTION("TI CAL driver");
+MODULE_AUTHOR("Benoit Parrot, <bparrot@ti.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(CAL_VERSION);
+
+static unsigned video_nr = -1;
+module_param(video_nr, uint, 0644);
+MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect");
+
+static unsigned debug;
+module_param(debug, uint, 0644);
+MODULE_PARM_DESC(debug, "activates debug info");
+
+/* timeperframe: min/max and default */
+static const struct v4l2_fract
+ tpf_default = {.numerator = 1001, .denominator = 30000};
+
+#define cal_dbg(level, caldev, fmt, arg...) \
+ v4l2_dbg(level, debug, &caldev->v4l2_dev, fmt, ##arg)
+#define cal_info(caldev, fmt, arg...) \
+ v4l2_info(&caldev->v4l2_dev, fmt, ##arg)
+#define cal_err(caldev, fmt, arg...) \
+ v4l2_err(&caldev->v4l2_dev, fmt, ##arg)
+
+#define ctx_dbg(level, ctx, fmt, arg...) \
+ v4l2_dbg(level, debug, &ctx->v4l2_dev, fmt, ##arg)
+#define ctx_info(ctx, fmt, arg...) \
+ v4l2_info(&ctx->v4l2_dev, fmt, ##arg)
+#define ctx_err(ctx, fmt, arg...) \
+ v4l2_err(&ctx->v4l2_dev, fmt, ##arg)
+
+#define CAL_NUM_INPUT 1
+#define CAL_NUM_CONTEXT 2
+
+#define bytes_per_line(pixel, bpp) (ALIGN(pixel * bpp, 16))
+
+#define reg_read(dev, offset) ioread32(dev->base + offset)
+#define reg_write(dev, offset, val) iowrite32(val, dev->base + offset)
+
+#define reg_read_field(dev, offset, mask) get_field(reg_read(dev, offset), \
+ mask)
+#define reg_write_field(dev, offset, field, mask) { \
+ u32 val = reg_read(dev, offset); \
+ set_field(&val, field, mask); \
+ reg_write(dev, offset, val); }
+
+/* ------------------------------------------------------------------
+ * Basic structures
+ * ------------------------------------------------------------------
+ */
+
+struct cal_fmt {
+ u32 fourcc;
+ u32 code;
+ u8 depth;
+};
+
+static struct cal_fmt cal_formats[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .code = MEDIA_BUS_FMT_YUYV8_2X8,
+ .depth = 16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .depth = 16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .code = MEDIA_BUS_FMT_YVYU8_2X8,
+ .depth = 16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .code = MEDIA_BUS_FMT_VYUY8_2X8,
+ .depth = 16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
+ .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
+ .depth = 16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
+ .code = MEDIA_BUS_FMT_RGB565_2X8_BE,
+ .depth = 16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */
+ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
+ .depth = 16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */
+ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE,
+ .depth = 16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */
+ .code = MEDIA_BUS_FMT_RGB888_2X12_LE,
+ .depth = 24,
+ }, {
+ .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */
+ .code = MEDIA_BUS_FMT_RGB888_2X12_BE,
+ .depth = 24,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB32, /* argb */
+ .code = MEDIA_BUS_FMT_ARGB8888_1X32,
+ .depth = 32,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .depth = 8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
+ .code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .depth = 8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ .code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .depth = 8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SRGGB8,
+ .code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .depth = 8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SBGGR10,
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .depth = 16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG10,
+ .code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .depth = 16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG10,
+ .code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .depth = 16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SRGGB10,
+ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .depth = 16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SBGGR12,
+ .code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .depth = 16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG12,
+ .code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .depth = 16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG12,
+ .code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .depth = 16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SRGGB12,
+ .code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .depth = 16,
+ },
+};
+
+/* Print Four-character-code (FOURCC) */
+static char *fourcc_to_str(u32 fmt)
+{
+ static char code[5];
+
+ code[0] = (unsigned char)(fmt & 0xff);
+ code[1] = (unsigned char)((fmt >> 8) & 0xff);
+ code[2] = (unsigned char)((fmt >> 16) & 0xff);
+ code[3] = (unsigned char)((fmt >> 24) & 0xff);
+ code[4] = '\0';
+
+ return code;
+}
+
+/* buffer for one video frame */
+struct cal_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct vb2_v4l2_buffer vb;
+ struct list_head list;
+ const struct cal_fmt *fmt;
+};
+
+struct cal_dmaqueue {
+ struct list_head active;
+
+ /* Counters to control fps rate */
+ int frame;
+ int ini_jiffies;
+};
+
+struct cm_data {
+ void __iomem *base;
+ struct resource *res;
+
+ unsigned int camerrx_control;
+
+ struct platform_device *pdev;
+};
+
+struct cc_data {
+ void __iomem *base;
+ struct resource *res;
+
+ struct platform_device *pdev;
+};
+
+/*
+ * there is one cal_dev structure in the driver, it is shared by
+ * all instances.
+ */
+struct cal_dev {
+ int irq;
+ void __iomem *base;
+ struct resource *res;
+ struct platform_device *pdev;
+ struct v4l2_device v4l2_dev;
+
+ /* Control Module handle */
+ struct cm_data *cm;
+ /* Camera Core Module handle */
+ struct cc_data *cc[CAL_NUM_CSI2_PORTS];
+
+ struct cal_ctx *ctx[CAL_NUM_CONTEXT];
+};
+
+/*
+ * There is one cal_ctx structure for each camera core context.
+ */
+struct cal_ctx {
+ struct v4l2_device v4l2_dev;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct video_device vdev;
+ struct v4l2_async_notifier notifier;
+ struct v4l2_subdev *sensor;
+ struct v4l2_of_endpoint endpoint;
+
+ struct v4l2_async_subdev asd;
+ struct v4l2_async_subdev *asd_list[1];
+
+ struct v4l2_fh fh;
+ struct cal_dev *dev;
+ struct cc_data *cc;
+
+ /* v4l2_ioctl mutex */
+ struct mutex mutex;
+ /* v4l2 buffers lock */
+ spinlock_t slock;
+
+ /* Several counters */
+ unsigned long jiffies;
+
+ struct vb2_alloc_ctx *alloc_ctx;
+ struct cal_dmaqueue vidq;
+
+ /* Input Number */
+ int input;
+
+ /* video capture */
+ const struct cal_fmt *fmt;
+ /* Used to store current pixel format */
+ struct v4l2_format v_fmt;
+ /* Used to store current mbus frame format */
+ struct v4l2_mbus_framefmt m_fmt;
+
+ /* Current subdev enumerated format */
+ struct cal_fmt *active_fmt[ARRAY_SIZE(cal_formats)];
+ int num_active_fmt;
+
+ struct v4l2_fract timeperframe;
+ unsigned int sequence;
+ unsigned int external_rate;
+ struct vb2_queue vb_vidq;
+ unsigned int seq_count;
+ unsigned int csi2_port;
+ unsigned int virtual_channel;
+
+ /* Pointer pointing to current v4l2_buffer */
+ struct cal_buffer *cur_frm;
+ /* Pointer pointing to next v4l2_buffer */
+ struct cal_buffer *next_frm;
+};
+
+static const struct cal_fmt *find_format_by_pix(struct cal_ctx *ctx,
+ u32 pixelformat)
+{
+ const struct cal_fmt *fmt;
+ unsigned int k;
+
+ for (k = 0; k < ctx->num_active_fmt; k++) {
+ fmt = ctx->active_fmt[k];
+ if (fmt->fourcc == pixelformat)
+ return fmt;
+ }
+
+ return NULL;
+}
+
+static const struct cal_fmt *find_format_by_code(struct cal_ctx *ctx,
+ u32 code)
+{
+ const struct cal_fmt *fmt;
+ unsigned int k;
+
+ for (k = 0; k < ctx->num_active_fmt; k++) {
+ fmt = ctx->active_fmt[k];
+ if (fmt->code == code)
+ return fmt;
+ }
+
+ return NULL;
+}
+
+static inline struct cal_ctx *notifier_to_ctx(struct v4l2_async_notifier *n)
+{
+ return container_of(n, struct cal_ctx, notifier);
+}
+
+static inline int get_field(u32 value, u32 mask)
+{
+ return (value & mask) >> __ffs(mask);
+}
+
+static inline void set_field(u32 *valp, u32 field, u32 mask)
+{
+ u32 val = *valp;
+
+ val &= ~mask;
+ val |= (field << __ffs(mask)) & mask;
+ *valp = val;
+}
+
+/*
+ * Control Module block access
+ */
+static struct cm_data *cm_create(struct cal_dev *dev)
+{
+ struct platform_device *pdev = dev->pdev;
+ struct cm_data *cm;
+
+ cm = devm_kzalloc(&pdev->dev, sizeof(*cm), GFP_KERNEL);
+ if (!cm)
+ return ERR_PTR(-ENOMEM);
+
+ cm->res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "camerrx_control");
+ cm->base = devm_ioremap_resource(&pdev->dev, cm->res);
+ if (IS_ERR(cm->base)) {
+ cal_err(dev, "failed to ioremap\n");
+ return ERR_CAST(cm->base);
+ }
+
+ cal_dbg(1, dev, "ioresource %s at %pa - %pa\n",
+ cm->res->name, &cm->res->start, &cm->res->end);
+
+ return cm;
+}
+
+static void camerarx_phy_enable(struct cal_ctx *ctx)
+{
+ u32 val;
+
+ if (!ctx->dev->cm->base) {
+ ctx_err(ctx, "cm not mapped\n");
+ return;
+ }
+
+ val = reg_read(ctx->dev->cm, CM_CTRL_CORE_CAMERRX_CONTROL);
+ if (ctx->csi2_port == 1) {
+ set_field(&val, 1, CM_CAMERRX_CTRL_CSI0_CTRLCLKEN_MASK);
+ set_field(&val, 0, CM_CAMERRX_CTRL_CSI0_CAMMODE_MASK);
+ /* enable all lanes by default */
+ set_field(&val, 0xf, CM_CAMERRX_CTRL_CSI0_LANEENABLE_MASK);
+ set_field(&val, 1, CM_CAMERRX_CTRL_CSI0_MODE_MASK);
+ } else if (ctx->csi2_port == 2) {
+ set_field(&val, 1, CM_CAMERRX_CTRL_CSI1_CTRLCLKEN_MASK);
+ set_field(&val, 0, CM_CAMERRX_CTRL_CSI1_CAMMODE_MASK);
+ /* enable all lanes by default */
+ set_field(&val, 0x3, CM_CAMERRX_CTRL_CSI1_LANEENABLE_MASK);
+ set_field(&val, 1, CM_CAMERRX_CTRL_CSI1_MODE_MASK);
+ }
+ reg_write(ctx->dev->cm, CM_CTRL_CORE_CAMERRX_CONTROL, val);
+}
+
+static void camerarx_phy_disable(struct cal_ctx *ctx)
+{
+ u32 val;
+
+ if (!ctx->dev->cm->base) {
+ ctx_err(ctx, "cm not mapped\n");
+ return;
+ }
+
+ val = reg_read(ctx->dev->cm, CM_CTRL_CORE_CAMERRX_CONTROL);
+ if (ctx->csi2_port == 1)
+ set_field(&val, 0x0, CM_CAMERRX_CTRL_CSI0_CTRLCLKEN_MASK);
+ else if (ctx->csi2_port == 2)
+ set_field(&val, 0x0, CM_CAMERRX_CTRL_CSI1_CTRLCLKEN_MASK);
+ reg_write(ctx->dev->cm, CM_CTRL_CORE_CAMERRX_CONTROL, val);
+}
+
+/*
+ * Camera Instance access block
+ */
+static struct cc_data *cc_create(struct cal_dev *dev, unsigned int core)
+{
+ struct platform_device *pdev = dev->pdev;
+ struct cc_data *cc;
+
+ cc = devm_kzalloc(&pdev->dev, sizeof(*cc), GFP_KERNEL);
+ if (!cc)
+ return ERR_PTR(-ENOMEM);
+
+ cc->res = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM,
+ (core == 0) ?
+ "cal_rx_core0" :
+ "cal_rx_core1");
+ cc->base = devm_ioremap_resource(&pdev->dev, cc->res);
+ if (IS_ERR(cc->base)) {
+ cal_err(dev, "failed to ioremap\n");
+ return ERR_CAST(cc->base);
+ }
+
+ cal_dbg(1, dev, "ioresource %s at %pa - %pa\n",
+ cc->res->name, &cc->res->start, &cc->res->end);
+
+ return cc;
+}
+
+/*
+ * Get Revision and HW info
+ */
+static void cal_get_hwinfo(struct cal_dev *dev)
+{
+ u32 revision = 0;
+ u32 hwinfo = 0;
+
+ revision = reg_read(dev, CAL_HL_REVISION);
+ cal_dbg(3, dev, "CAL_HL_REVISION = 0x%08x (expecting 0x40000200)\n",
+ revision);
+
+ hwinfo = reg_read(dev, CAL_HL_HWINFO);
+ cal_dbg(3, dev, "CAL_HL_HWINFO = 0x%08x (expecting 0xA3C90469)\n",
+ hwinfo);
+}
+
+static inline int cal_runtime_get(struct cal_dev *dev)
+{
+ int r;
+
+ r = pm_runtime_get_sync(&dev->pdev->dev);
+
+ return r;
+}
+
+static inline void cal_runtime_put(struct cal_dev *dev)
+{
+ pm_runtime_put_sync(&dev->pdev->dev);
+}
+
+static void cal_quickdump_regs(struct cal_dev *dev)
+{
+ cal_info(dev, "CAL Registers @ 0x%pa:\n", &dev->res->start);
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4,
+ (__force const void *)dev->base,
+ resource_size(dev->res), false);
+
+ if (dev->ctx[0]) {
+ cal_info(dev, "CSI2 Core 0 Registers @ %pa:\n",
+ &dev->ctx[0]->cc->res->start);
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4,
+ (__force const void *)dev->ctx[0]->cc->base,
+ resource_size(dev->ctx[0]->cc->res),
+ false);
+ }
+
+ if (dev->ctx[1]) {
+ cal_info(dev, "CSI2 Core 1 Registers @ %pa:\n",
+ &dev->ctx[1]->cc->res->start);
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4,
+ (__force const void *)dev->ctx[1]->cc->base,
+ resource_size(dev->ctx[1]->cc->res),
+ false);
+ }
+
+ cal_info(dev, "CAMERRX_Control Registers @ %pa:\n",
+ &dev->cm->res->start);
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4,
+ (__force const void *)dev->cm->base,
+ resource_size(dev->cm->res), false);
+}
+
+/*
+ * Enable the expected IRQ sources
+ */
+static void enable_irqs(struct cal_ctx *ctx)
+{
+ /* Enable IRQ_WDMA_END 0/1 */
+ reg_write_field(ctx->dev,
+ CAL_HL_IRQENABLE_SET(2),
+ CAL_HL_IRQ_ENABLE,
+ CAL_HL_IRQ_MASK(ctx->csi2_port));
+ /* Enable IRQ_WDMA_START 0/1 */
+ reg_write_field(ctx->dev,
+ CAL_HL_IRQENABLE_SET(3),
+ CAL_HL_IRQ_ENABLE,
+ CAL_HL_IRQ_MASK(ctx->csi2_port));
+ /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */
+ reg_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(1), 0xFF000000);
+}
+
+static void disable_irqs(struct cal_ctx *ctx)
+{
+ /* Disable IRQ_WDMA_END 0/1 */
+ reg_write_field(ctx->dev,
+ CAL_HL_IRQENABLE_CLR(2),
+ CAL_HL_IRQ_CLEAR,
+ CAL_HL_IRQ_MASK(ctx->csi2_port));
+ /* Disable IRQ_WDMA_START 0/1 */
+ reg_write_field(ctx->dev,
+ CAL_HL_IRQENABLE_CLR(3),
+ CAL_HL_IRQ_CLEAR,
+ CAL_HL_IRQ_MASK(ctx->csi2_port));
+ /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */
+ reg_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(1), 0);
+}
+
+static void csi2_init(struct cal_ctx *ctx)
+{
+ int i;
+ u32 val;
+
+ val = reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port));
+ set_field(&val, CAL_GEN_ENABLE,
+ CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK);
+ set_field(&val, CAL_GEN_ENABLE,
+ CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK);
+ set_field(&val, CAL_GEN_DISABLE,
+ CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK);
+ set_field(&val, 407, CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK);
+ reg_write(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port), val);
+ ctx_dbg(3, ctx, "CAL_CSI2_TIMING(%d) = 0x%08x\n", ctx->csi2_port,
+ reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port)));
+
+ val = reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port));
+ set_field(&val, CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL,
+ CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK);
+ set_field(&val, CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON,
+ CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK);
+ reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), val);
+ for (i = 0; i < 10; i++) {
+ if (reg_read_field(ctx->dev,
+ CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port),
+ CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK) ==
+ CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_STATE_ON)
+ break;
+ usleep_range(1000, 1100);
+ }
+ ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n", ctx->csi2_port,
+ reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)));
+
+ val = reg_read(ctx->dev, CAL_CTRL);
+ set_field(&val, CAL_CTRL_BURSTSIZE_BURST128, CAL_CTRL_BURSTSIZE_MASK);
+ set_field(&val, 0xF, CAL_CTRL_TAGCNT_MASK);
+ set_field(&val, CAL_CTRL_POSTED_WRITES_NONPOSTED,
+ CAL_CTRL_POSTED_WRITES_MASK);
+ set_field(&val, 0xFF, CAL_CTRL_MFLAGL_MASK);
+ set_field(&val, 0xFF, CAL_CTRL_MFLAGH_MASK);
+ reg_write(ctx->dev, CAL_CTRL, val);
+ ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", reg_read(ctx->dev, CAL_CTRL));
+}
+
+static void csi2_lane_config(struct cal_ctx *ctx)
+{
+ u32 val = reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port));
+ u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK;
+ u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK;
+ struct v4l2_of_bus_mipi_csi2 *mipi_csi2 = &ctx->endpoint.bus.mipi_csi2;
+ int lane;
+
+ set_field(&val, mipi_csi2->clock_lane + 1, lane_mask);
+ set_field(&val, mipi_csi2->lane_polarities[0], polarity_mask);
+ for (lane = 0; lane < mipi_csi2->num_data_lanes; lane++) {
+ /*
+ * Every lane are one nibble apart starting with the
+ * clock followed by the data lanes so shift masks by 4.
+ */
+ lane_mask <<= 4;
+ polarity_mask <<= 4;
+ set_field(&val, mipi_csi2->data_lanes[lane] + 1, lane_mask);
+ set_field(&val, mipi_csi2->lane_polarities[lane + 1],
+ polarity_mask);
+ }
+
+ reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), val);
+ ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n",
+ ctx->csi2_port, val);
+}
+
+static void csi2_ppi_enable(struct cal_ctx *ctx)
+{
+ reg_write_field(ctx->dev, CAL_CSI2_PPI_CTRL(ctx->csi2_port),
+ CAL_GEN_ENABLE, CAL_CSI2_PPI_CTRL_IF_EN_MASK);
+}
+
+static void csi2_ppi_disable(struct cal_ctx *ctx)
+{
+ reg_write_field(ctx->dev, CAL_CSI2_PPI_CTRL(ctx->csi2_port),
+ CAL_GEN_DISABLE, CAL_CSI2_PPI_CTRL_IF_EN_MASK);
+}
+
+static void csi2_ctx_config(struct cal_ctx *ctx)
+{
+ u32 val;
+
+ val = reg_read(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port));
+ set_field(&val, ctx->csi2_port, CAL_CSI2_CTX_CPORT_MASK);
+ /*
+ * DT type: MIPI CSI-2 Specs
+ * 0x1: All - DT filter is disabled
+ * 0x24: RGB888 1 pixel = 3 bytes
+ * 0x2B: RAW10 4 pixels = 5 bytes
+ * 0x2A: RAW8 1 pixel = 1 byte
+ * 0x1E: YUV422 2 pixels = 4 bytes
+ */
+ set_field(&val, 0x1, CAL_CSI2_CTX_DT_MASK);
+ /* Virtual Channel from the CSI2 sensor usually 0! */
+ set_field(&val, ctx->virtual_channel, CAL_CSI2_CTX_VC_MASK);
+ /* NUM_LINES_PER_FRAME => 0 means auto detect */
+ set_field(&val, 0, CAL_CSI2_CTX_LINES_MASK);
+ set_field(&val, CAL_CSI2_CTX_ATT_PIX, CAL_CSI2_CTX_ATT_MASK);
+ set_field(&val, CAL_CSI2_CTX_PACK_MODE_LINE,
+ CAL_CSI2_CTX_PACK_MODE_MASK);
+ reg_write(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port), val);
+ ctx_dbg(3, ctx, "CAL_CSI2_CTX0(%d) = 0x%08x\n", ctx->csi2_port,
+ reg_read(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port)));
+}
+
+static void pix_proc_config(struct cal_ctx *ctx)
+{
+ u32 val;
+
+ val = reg_read(ctx->dev, CAL_PIX_PROC(ctx->csi2_port));
+ set_field(&val, CAL_PIX_PROC_EXTRACT_B8, CAL_PIX_PROC_EXTRACT_MASK);
+ set_field(&val, CAL_PIX_PROC_DPCMD_BYPASS, CAL_PIX_PROC_DPCMD_MASK);
+ set_field(&val, CAL_PIX_PROC_DPCME_BYPASS, CAL_PIX_PROC_DPCME_MASK);
+ set_field(&val, CAL_PIX_PROC_PACK_B8, CAL_PIX_PROC_PACK_MASK);
+ set_field(&val, ctx->csi2_port, CAL_PIX_PROC_CPORT_MASK);
+ set_field(&val, CAL_GEN_ENABLE, CAL_PIX_PROC_EN_MASK);
+ reg_write(ctx->dev, CAL_PIX_PROC(ctx->csi2_port), val);
+ ctx_dbg(3, ctx, "CAL_PIX_PROC(%d) = 0x%08x\n", ctx->csi2_port,
+ reg_read(ctx->dev, CAL_PIX_PROC(ctx->csi2_port)));
+}
+
+static void cal_wr_dma_config(struct cal_ctx *ctx,
+ unsigned int width)
+{
+ u32 val;
+
+ val = reg_read(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port));
+ set_field(&val, ctx->csi2_port, CAL_WR_DMA_CTRL_CPORT_MASK);
+ set_field(&val, CAL_WR_DMA_CTRL_DTAG_PIX_DAT,
+ CAL_WR_DMA_CTRL_DTAG_MASK);
+ set_field(&val, CAL_WR_DMA_CTRL_MODE_CONST,
+ CAL_WR_DMA_CTRL_MODE_MASK);
+ set_field(&val, CAL_WR_DMA_CTRL_PATTERN_LINEAR,
+ CAL_WR_DMA_CTRL_PATTERN_MASK);
+ set_field(&val, CAL_GEN_ENABLE, CAL_WR_DMA_CTRL_STALL_RD_MASK);
+ reg_write(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port), val);
+ ctx_dbg(3, ctx, "CAL_WR_DMA_CTRL(%d) = 0x%08x\n", ctx->csi2_port,
+ reg_read(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port)));
+
+ /*
+ * width/16 not sure but giving it a whirl.
+ * zero does not work right
+ */
+ reg_write_field(ctx->dev,
+ CAL_WR_DMA_OFST(ctx->csi2_port),
+ (width / 16),
+ CAL_WR_DMA_OFST_MASK);
+ ctx_dbg(3, ctx, "CAL_WR_DMA_OFST(%d) = 0x%08x\n", ctx->csi2_port,
+ reg_read(ctx->dev, CAL_WR_DMA_OFST(ctx->csi2_port)));
+
+ val = reg_read(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port));
+ /* 64 bit word means no skipping */
+ set_field(&val, 0, CAL_WR_DMA_XSIZE_XSKIP_MASK);
+ /*
+ * (width*8)/64 this should be size of an entire line
+ * in 64bit word but 0 means all data until the end
+ * is detected automagically
+ */
+ set_field(&val, (width / 8), CAL_WR_DMA_XSIZE_MASK);
+ reg_write(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port), val);
+ ctx_dbg(3, ctx, "CAL_WR_DMA_XSIZE(%d) = 0x%08x\n", ctx->csi2_port,
+ reg_read(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port)));
+}
+
+static void cal_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr)
+{
+ reg_write(ctx->dev, CAL_WR_DMA_ADDR(ctx->csi2_port), dmaaddr);
+}
+
+/*
+ * TCLK values are OK at their reset values
+ */
+#define TCLK_TERM 0
+#define TCLK_MISS 1
+#define TCLK_SETTLE 14
+#define THS_SETTLE 15
+
+static void csi2_phy_config(struct cal_ctx *ctx)
+{
+ unsigned int reg0, reg1;
+ unsigned int ths_term, ths_settle;
+ unsigned int ddrclkperiod_us;
+
+ /*
+ * THS_TERM: Programmed value = floor(20 ns/DDRClk period) - 2.
+ */
+ ddrclkperiod_us = ctx->external_rate / 2000000;
+ ddrclkperiod_us = 1000000 / ddrclkperiod_us;
+ ctx_dbg(1, ctx, "ddrclkperiod_us: %d\n", ddrclkperiod_us);
+
+ ths_term = 20000 / ddrclkperiod_us;
+ ths_term = (ths_term >= 2) ? ths_term - 2 : ths_term;
+ ctx_dbg(1, ctx, "ths_term: %d (0x%02x)\n", ths_term, ths_term);
+
+ /*
+ * THS_SETTLE: Programmed value = floor(176.3 ns/CtrlClk period) - 1.
+ * Since CtrlClk is fixed at 96Mhz then we get
+ * ths_settle = floor(176.3 / 10.416) - 1 = 15
+ * If we ever switch to a dynamic clock then this code might be useful
+ *
+ * unsigned int ctrlclkperiod_us;
+ * ctrlclkperiod_us = 96000000 / 1000000;
+ * ctrlclkperiod_us = 1000000 / ctrlclkperiod_us;
+ * ctx_dbg(1, ctx, "ctrlclkperiod_us: %d\n", ctrlclkperiod_us);
+
+ * ths_settle = 176300 / ctrlclkperiod_us;
+ * ths_settle = (ths_settle > 1) ? ths_settle - 1 : ths_settle;
+ */
+
+ ths_settle = THS_SETTLE;
+ ctx_dbg(1, ctx, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle);
+
+ reg0 = reg_read(ctx->cc, CAL_CSI2_PHY_REG0);
+ set_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE,
+ CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK);
+ set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK);
+ set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK);
+
+ ctx_dbg(1, ctx, "CSI2_%d_REG0 = 0x%08x\n", (ctx->csi2_port - 1), reg0);
+ reg_write(ctx->cc, CAL_CSI2_PHY_REG0, reg0);
+
+ reg1 = reg_read(ctx->cc, CAL_CSI2_PHY_REG1);
+ set_field(®1, TCLK_TERM, CAL_CSI2_PHY_REG1_TCLK_TERM_MASK);
+ set_field(®1, 0xb8, CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK);
+ set_field(®1, TCLK_MISS, CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK);
+ set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK);
+
+ ctx_dbg(1, ctx, "CSI2_%d_REG1 = 0x%08x\n", (ctx->csi2_port - 1), reg1);
+ reg_write(ctx->cc, CAL_CSI2_PHY_REG1, reg1);
+}
+
+static int cal_get_external_info(struct cal_ctx *ctx)
+{
+ struct v4l2_ctrl *ctrl;
+
+ if (!ctx->sensor)
+ return -ENODEV;
+
+ ctrl = v4l2_ctrl_find(ctx->sensor->ctrl_handler, V4L2_CID_PIXEL_RATE);
+ if (!ctrl) {
+ ctx_err(ctx, "no pixel rate control in subdev: %s\n",
+ ctx->sensor->name);
+ return -EPIPE;
+ }
+
+ ctx->external_rate = v4l2_ctrl_g_ctrl_int64(ctrl);
+ ctx_dbg(3, ctx, "sensor Pixel Rate: %d\n", ctx->external_rate);
+
+ return 0;
+}
+
+static inline void cal_schedule_next_buffer(struct cal_ctx *ctx)
+{
+ struct cal_dmaqueue *dma_q = &ctx->vidq;
+ struct cal_buffer *buf;
+ unsigned long addr;
+
+ buf = list_entry(dma_q->active.next, struct cal_buffer, list);
+ ctx->next_frm = buf;
+ list_del(&buf->list);
+
+ addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
+ cal_wr_dma_addr(ctx, addr);
+}
+
+static inline void cal_process_buffer_complete(struct cal_ctx *ctx)
+{
+ ctx->cur_frm->vb.vb2_buf.timestamp = ktime_get_ns();
+ ctx->cur_frm->vb.field = ctx->m_fmt.field;
+ ctx->cur_frm->vb.sequence = ctx->sequence++;
+
+ vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE);
+ ctx->cur_frm = ctx->next_frm;
+}
+
+#define isvcirqset(irq, vc, ff) (irq & \
+ (CAL_CSI2_VC_IRQENABLE_ ##ff ##_IRQ_##vc ##_MASK))
+
+#define isportirqset(irq, port) (irq & CAL_HL_IRQ_MASK(port))
+
+static irqreturn_t cal_irq(int irq_cal, void *data)
+{
+ struct cal_dev *dev = (struct cal_dev *)data;
+ struct cal_ctx *ctx;
+ struct cal_dmaqueue *dma_q;
+ u32 irqst2, irqst3;
+
+ /* Check which DMA just finished */
+ irqst2 = reg_read(dev, CAL_HL_IRQSTATUS(2));
+ if (irqst2) {
+ /* Clear Interrupt status */
+ reg_write(dev, CAL_HL_IRQSTATUS(2), irqst2);
+
+ /* Need to check both port */
+ if (isportirqset(irqst2, 1)) {
+ ctx = dev->ctx[0];
+
+ if (ctx->cur_frm != ctx->next_frm)
+ cal_process_buffer_complete(ctx);
+ }
+
+ if (isportirqset(irqst2, 2)) {
+ ctx = dev->ctx[1];
+
+ if (ctx->cur_frm != ctx->next_frm)
+ cal_process_buffer_complete(ctx);
+ }
+ }
+
+ /* Check which DMA just started */
+ irqst3 = reg_read(dev, CAL_HL_IRQSTATUS(3));
+ if (irqst3) {
+ /* Clear Interrupt status */
+ reg_write(dev, CAL_HL_IRQSTATUS(3), irqst3);
+
+ /* Need to check both port */
+ if (isportirqset(irqst3, 1)) {
+ ctx = dev->ctx[0];
+ dma_q = &ctx->vidq;
+
+ spin_lock(&ctx->slock);
+ if (!list_empty(&dma_q->active) &&
+ ctx->cur_frm == ctx->next_frm)
+ cal_schedule_next_buffer(ctx);
+ spin_unlock(&ctx->slock);
+ }
+
+ if (isportirqset(irqst3, 2)) {
+ ctx = dev->ctx[1];
+ dma_q = &ctx->vidq;
+
+ spin_lock(&ctx->slock);
+ if (!list_empty(&dma_q->active) &&
+ ctx->cur_frm == ctx->next_frm)
+ cal_schedule_next_buffer(ctx);
+ spin_unlock(&ctx->slock);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * video ioctls
+ */
+static int cal_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct cal_ctx *ctx = video_drvdata(file);
+
+ strlcpy(cap->driver, CAL_MODULE_NAME, sizeof(cap->driver));
+ strlcpy(cap->card, CAL_MODULE_NAME, sizeof(cap->card));
+
+ snprintf(cap->bus_info, sizeof(cap->bus_info),
+ "platform:%s", ctx->v4l2_dev.name);
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
+ V4L2_CAP_READWRITE;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+ return 0;
+}
+
+static int cal_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct cal_ctx *ctx = video_drvdata(file);
+ const struct cal_fmt *fmt = NULL;
+
+ if (f->index >= ctx->num_active_fmt)
+ return -EINVAL;
+
+ fmt = ctx->active_fmt[f->index];
+
+ f->pixelformat = fmt->fourcc;
+ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ return 0;
+}
+
+static int __subdev_get_format(struct cal_ctx *ctx,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct v4l2_subdev_format sd_fmt;
+ struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
+ int ret;
+
+ sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ sd_fmt.pad = 0;
+
+ ret = v4l2_subdev_call(ctx->sensor, pad, get_fmt, NULL, &sd_fmt);
+ if (ret)
+ return ret;
+
+ *fmt = *mbus_fmt;
+
+ ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__,
+ fmt->width, fmt->height, fmt->code);
+
+ return 0;
+}
+
+static int __subdev_set_format(struct cal_ctx *ctx,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct v4l2_subdev_format sd_fmt;
+ struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
+ int ret;
+
+ sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ sd_fmt.pad = 0;
+ *mbus_fmt = *fmt;
+
+ ret = v4l2_subdev_call(ctx->sensor, pad, set_fmt, NULL, &sd_fmt);
+ if (ret)
+ return ret;
+
+ ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__,
+ fmt->width, fmt->height, fmt->code);
+
+ return 0;
+}
+
+static int cal_calc_format_size(struct cal_ctx *ctx,
+ const struct cal_fmt *fmt,
+ struct v4l2_format *f)
+{
+ if (!fmt) {
+ ctx_dbg(3, ctx, "No cal_fmt provided!\n");
+ return -EINVAL;
+ }
+
+ v4l_bound_align_image(&f->fmt.pix.width, 48, MAX_WIDTH, 2,
+ &f->fmt.pix.height, 32, MAX_HEIGHT, 0, 0);
+ f->fmt.pix.bytesperline = bytes_per_line(f->fmt.pix.width,
+ fmt->depth >> 3);
+ f->fmt.pix.sizeimage = f->fmt.pix.height *
+ f->fmt.pix.bytesperline;
+
+ ctx_dbg(3, ctx, "%s: fourcc: %s size: %dx%d bpl:%d img_size:%d\n",
+ __func__, fourcc_to_str(f->fmt.pix.pixelformat),
+ f->fmt.pix.width, f->fmt.pix.height,
+ f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
+
+ return 0;
+}
+
+static int cal_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cal_ctx *ctx = video_drvdata(file);
+
+ *f = ctx->v_fmt;
+
+ return 0;
+}
+
+static int cal_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cal_ctx *ctx = video_drvdata(file);
+ const struct cal_fmt *fmt;
+ struct v4l2_subdev_frame_size_enum fse;
+ int ret, found;
+
+ fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat);
+ if (!fmt) {
+ ctx_dbg(3, ctx, "Fourcc format (0x%08x) not found.\n",
+ f->fmt.pix.pixelformat);
+
+ /* Just get the first one enumerated */
+ fmt = ctx->active_fmt[0];
+ f->fmt.pix.pixelformat = fmt->fourcc;
+ }
+
+ f->fmt.pix.field = ctx->v_fmt.fmt.pix.field;
+
+ /* check for/find a valid width/height */
+ ret = 0;
+ found = false;
+ fse.pad = 0;
+ fse.code = fmt->code;
+ fse.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ for (fse.index = 0; ; fse.index++) {
+ ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_size,
+ NULL, &fse);
+ if (ret)
+ break;
+
+ if ((f->fmt.pix.width == fse.max_width) &&
+ (f->fmt.pix.height == fse.max_height)) {
+ found = true;
+ break;
+ } else if ((f->fmt.pix.width >= fse.min_width) &&
+ (f->fmt.pix.width <= fse.max_width) &&
+ (f->fmt.pix.height >= fse.min_height) &&
+ (f->fmt.pix.height <= fse.max_height)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ /* use existing values as default */
+ f->fmt.pix.width = ctx->v_fmt.fmt.pix.width;
+ f->fmt.pix.height = ctx->v_fmt.fmt.pix.height;
+ }
+
+ /*
+ * Use current colorspace for now, it will get
+ * updated properly during s_fmt
+ */
+ f->fmt.pix.colorspace = ctx->v_fmt.fmt.pix.colorspace;
+ return cal_calc_format_size(ctx, fmt, f);
+}
+
+static int cal_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cal_ctx *ctx = video_drvdata(file);
+ struct vb2_queue *q = &ctx->vb_vidq;
+ const struct cal_fmt *fmt;
+ struct v4l2_mbus_framefmt mbus_fmt;
+ int ret;
+
+ if (vb2_is_busy(q)) {
+ ctx_dbg(3, ctx, "%s device busy\n", __func__);
+ return -EBUSY;
+ }
+
+ ret = cal_try_fmt_vid_cap(file, priv, f);
+ if (ret < 0)
+ return ret;
+
+ fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat);
+
+ v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code);
+
+ ret = __subdev_set_format(ctx, &mbus_fmt);
+ if (ret)
+ return ret;
+
+ /* Just double check nothing has gone wrong */
+ if (mbus_fmt.code != fmt->code) {
+ ctx_dbg(3, ctx,
+ "%s subdev changed format on us, this should not happen\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt);
+ ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc;
+ cal_calc_format_size(ctx, fmt, &ctx->v_fmt);
+ ctx->fmt = fmt;
+ ctx->m_fmt = mbus_fmt;
+ *f = ctx->v_fmt;
+
+ return 0;
+}
+
+static int cal_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct cal_ctx *ctx = video_drvdata(file);
+ const struct cal_fmt *fmt;
+ struct v4l2_subdev_frame_size_enum fse;
+ int ret;
+
+ /* check for valid format */
+ fmt = find_format_by_pix(ctx, fsize->pixel_format);
+ if (!fmt) {
+ ctx_dbg(3, ctx, "Invalid pixel code: %x\n",
+ fsize->pixel_format);
+ return -EINVAL;
+ }
+
+ fse.index = fsize->index;
+ fse.pad = 0;
+ fse.code = fmt->code;
+
+ ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_size, NULL, &fse);
+ if (ret)
+ return ret;
+
+ ctx_dbg(1, ctx, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n",
+ __func__, fse.index, fse.code, fse.min_width, fse.max_width,
+ fse.min_height, fse.max_height);
+
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = fse.max_width;
+ fsize->discrete.height = fse.max_height;
+
+ return 0;
+}
+
+static int cal_enum_input(struct file *file, void *priv,
+ struct v4l2_input *inp)
+{
+ if (inp->index >= CAL_NUM_INPUT)
+ return -EINVAL;
+
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+ sprintf(inp->name, "Camera %u", inp->index);
+ return 0;
+}
+
+static int cal_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct cal_ctx *ctx = video_drvdata(file);
+
+ *i = ctx->input;
+ return 0;
+}
+
+static int cal_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct cal_ctx *ctx = video_drvdata(file);
+
+ if (i >= CAL_NUM_INPUT)
+ return -EINVAL;
+
+ ctx->input = i;
+ return 0;
+}
+
+/* timeperframe is arbitrary and continuous */
+static int cal_enum_frameintervals(struct file *file, void *priv,
+ struct v4l2_frmivalenum *fival)
+{
+ struct cal_ctx *ctx = video_drvdata(file);
+ const struct cal_fmt *fmt;
+ struct v4l2_subdev_frame_interval_enum fie = {
+ .index = fival->index,
+ .width = fival->width,
+ .height = fival->height,
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ int ret;
+
+ fmt = find_format_by_pix(ctx, fival->pixel_format);
+ if (!fmt)
+ return -EINVAL;
+
+ fie.code = fmt->code;
+ ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_interval,
+ NULL, &fie);
+ if (ret)
+ return ret;
+ fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+ fival->discrete = fie.interval;
+
+ return 0;
+}
+
+/*
+ * Videobuf operations
+ */
+static int cal_queue_setup(struct vb2_queue *vq,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct cal_ctx *ctx = vb2_get_drv_priv(vq);
+ unsigned size = ctx->v_fmt.fmt.pix.sizeimage;
+
+ if (vq->num_buffers + *nbuffers < 3)
+ *nbuffers = 3 - vq->num_buffers;
+ alloc_ctxs[0] = ctx->alloc_ctx;
+
+ if (*nplanes) {
+ if (sizes[0] < size)
+ return -EINVAL;
+ size = sizes[0];
+ }
+
+ *nplanes = 1;
+ sizes[0] = size;
+
+ ctx_dbg(3, ctx, "nbuffers=%d, size=%d\n", *nbuffers, sizes[0]);
+
+ return 0;
+}
+
+static int cal_buffer_prepare(struct vb2_buffer *vb)
+{
+ struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct cal_buffer *buf = container_of(vb, struct cal_buffer,
+ vb.vb2_buf);
+ unsigned long size;
+
+ if (WARN_ON(!ctx->fmt))
+ return -EINVAL;
+
+ size = ctx->v_fmt.fmt.pix.sizeimage;
+ if (vb2_plane_size(vb, 0) < size) {
+ ctx_err(ctx,
+ "data will not fit into plane (%lu < %lu)\n",
+ vb2_plane_size(vb, 0), size);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size);
+ return 0;
+}
+
+static void cal_buffer_queue(struct vb2_buffer *vb)
+{
+ struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct cal_buffer *buf = container_of(vb, struct cal_buffer,
+ vb.vb2_buf);
+ struct cal_dmaqueue *vidq = &ctx->vidq;
+ unsigned long flags = 0;
+
+ /* recheck locking */
+ spin_lock_irqsave(&ctx->slock, flags);
+ list_add_tail(&buf->list, &vidq->active);
+ spin_unlock_irqrestore(&ctx->slock, flags);
+}
+
+static int cal_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct cal_ctx *ctx = vb2_get_drv_priv(vq);
+ struct cal_dmaqueue *dma_q = &ctx->vidq;
+ struct cal_buffer *buf, *tmp;
+ unsigned long addr = 0;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&ctx->slock, flags);
+ if (list_empty(&dma_q->active)) {
+ spin_unlock_irqrestore(&ctx->slock, flags);
+ ctx_dbg(3, ctx, "buffer queue is empty\n");
+ return -EIO;
+ }
+
+ buf = list_entry(dma_q->active.next, struct cal_buffer, list);
+ ctx->cur_frm = buf;
+ ctx->next_frm = buf;
+ list_del(&buf->list);
+ spin_unlock_irqrestore(&ctx->slock, flags);
+
+ addr = vb2_dma_contig_plane_dma_addr(&ctx->cur_frm->vb.vb2_buf, 0);
+ ctx->sequence = 0;
+
+ ret = cal_get_external_info(ctx);
+ if (ret < 0)
+ goto err;
+
+ cal_runtime_get(ctx->dev);
+
+ enable_irqs(ctx);
+ camerarx_phy_enable(ctx);
+ csi2_init(ctx);
+ csi2_phy_config(ctx);
+ csi2_lane_config(ctx);
+ csi2_ctx_config(ctx);
+ pix_proc_config(ctx);
+ cal_wr_dma_config(ctx, ctx->v_fmt.fmt.pix.bytesperline);
+ cal_wr_dma_addr(ctx, addr);
+ csi2_ppi_enable(ctx);
+
+ ret = v4l2_subdev_call(ctx->sensor, video, s_stream, 1);
+ if (ret) {
+ ctx_err(ctx, "stream on failed in subdev\n");
+ cal_runtime_put(ctx->dev);
+ goto err;
+ }
+
+ if (debug >= 4)
+ cal_quickdump_regs(ctx->dev);
+
+ return 0;
+
+err:
+ list_for_each_entry_safe(buf, tmp, &dma_q->active, list) {
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
+ }
+ return ret;
+}
+
+static void cal_stop_streaming(struct vb2_queue *vq)
+{
+ struct cal_ctx *ctx = vb2_get_drv_priv(vq);
+ struct cal_dmaqueue *dma_q = &ctx->vidq;
+ struct cal_buffer *buf, *tmp;
+ unsigned long flags;
+
+ if (v4l2_subdev_call(ctx->sensor, video, s_stream, 0))
+ ctx_err(ctx, "stream off failed in subdev\n");
+
+ csi2_ppi_disable(ctx);
+ disable_irqs(ctx);
+
+ /* Release all active buffers */
+ spin_lock_irqsave(&ctx->slock, flags);
+ list_for_each_entry_safe(buf, tmp, &dma_q->active, list) {
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ }
+
+ if (ctx->cur_frm == ctx->next_frm) {
+ vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ } else {
+ vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ vb2_buffer_done(&ctx->next_frm->vb.vb2_buf,
+ VB2_BUF_STATE_ERROR);
+ }
+ ctx->cur_frm = NULL;
+ ctx->next_frm = NULL;
+ spin_unlock_irqrestore(&ctx->slock, flags);
+
+ cal_runtime_put(ctx->dev);
+}
+
+static struct vb2_ops cal_video_qops = {
+ .queue_setup = cal_queue_setup,
+ .buf_prepare = cal_buffer_prepare,
+ .buf_queue = cal_buffer_queue,
+ .start_streaming = cal_start_streaming,
+ .stop_streaming = cal_stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
+static const struct v4l2_file_operations cal_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .read = vb2_fop_read,
+ .poll = vb2_fop_poll,
+ .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
+ .mmap = vb2_fop_mmap,
+};
+
+static const struct v4l2_ioctl_ops cal_ioctl_ops = {
+ .vidioc_querycap = cal_querycap,
+ .vidioc_enum_fmt_vid_cap = cal_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = cal_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = cal_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = cal_s_fmt_vid_cap,
+ .vidioc_enum_framesizes = cal_enum_framesizes,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_enum_input = cal_enum_input,
+ .vidioc_g_input = cal_g_input,
+ .vidioc_s_input = cal_s_input,
+ .vidioc_enum_frameintervals = cal_enum_frameintervals,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static struct video_device cal_videodev = {
+ .name = CAL_MODULE_NAME,
+ .fops = &cal_fops,
+ .ioctl_ops = &cal_ioctl_ops,
+ .minor = -1,
+ .release = video_device_release_empty,
+};
+
+/* -----------------------------------------------------------------
+ * Initialization and module stuff
+ * ------------------------------------------------------------------
+ */
+static int cal_complete_ctx(struct cal_ctx *ctx);
+
+static int cal_async_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ struct cal_ctx *ctx = notifier_to_ctx(notifier);
+ struct v4l2_subdev_mbus_code_enum mbus_code;
+ int ret = 0;
+ int i, j, k;
+
+ if (ctx->sensor) {
+ ctx_info(ctx, "Rejecting subdev %s (Already set!!)",
+ subdev->name);
+ return 0;
+ }
+
+ ctx->sensor = subdev;
+ ctx_dbg(1, ctx, "Using sensor %s for capture\n", subdev->name);
+
+ /* Enumerate sub device formats and enable all matching local formats */
+ ctx->num_active_fmt = 0;
+ for (j = 0, i = 0; ret != -EINVAL; ++j) {
+ struct cal_fmt *fmt;
+
+ memset(&mbus_code, 0, sizeof(mbus_code));
+ mbus_code.index = j;
+ ret = v4l2_subdev_call(subdev, pad, enum_mbus_code,
+ NULL, &mbus_code);
+ if (ret)
+ continue;
+
+ ctx_dbg(2, ctx,
+ "subdev %s: code: %04x idx: %d\n",
+ subdev->name, mbus_code.code, j);
+
+ for (k = 0; k < ARRAY_SIZE(cal_formats); k++) {
+ fmt = &cal_formats[k];
+
+ if (mbus_code.code == fmt->code) {
+ ctx->active_fmt[i] = fmt;
+ ctx_dbg(2, ctx,
+ "matched fourcc: %s: code: %04x idx: %d\n",
+ fourcc_to_str(fmt->fourcc),
+ fmt->code, i);
+ ctx->num_active_fmt = ++i;
+ }
+ }
+ }
+
+ if (i == 0) {
+ ctx_err(ctx, "No suitable format reported by subdev %s\n",
+ subdev->name);
+ return -EINVAL;
+ }
+
+ cal_complete_ctx(ctx);
+
+ return 0;
+}
+
+static int cal_async_complete(struct v4l2_async_notifier *notifier)
+{
+ struct cal_ctx *ctx = notifier_to_ctx(notifier);
+ const struct cal_fmt *fmt;
+ struct v4l2_mbus_framefmt mbus_fmt;
+ int ret;
+
+ ret = __subdev_get_format(ctx, &mbus_fmt);
+ if (ret)
+ return ret;
+
+ fmt = find_format_by_code(ctx, mbus_fmt.code);
+ if (!fmt) {
+ ctx_dbg(3, ctx, "mbus code format (0x%08x) not found.\n",
+ mbus_fmt.code);
+ return -EINVAL;
+ }
+
+ /* Save current subdev format */
+ v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt);
+ ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc;
+ cal_calc_format_size(ctx, fmt, &ctx->v_fmt);
+ ctx->fmt = fmt;
+ ctx->m_fmt = mbus_fmt;
+
+ return 0;
+}
+
+static int cal_complete_ctx(struct cal_ctx *ctx)
+{
+ struct video_device *vfd;
+ struct vb2_queue *q;
+ int ret;
+
+ ctx->timeperframe = tpf_default;
+ ctx->external_rate = 192000000;
+
+ /* initialize locks */
+ spin_lock_init(&ctx->slock);
+ mutex_init(&ctx->mutex);
+
+ /* initialize queue */
+ q = &ctx->vb_vidq;
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
+ q->drv_priv = ctx;
+ q->buf_struct_size = sizeof(struct cal_buffer);
+ q->ops = &cal_video_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &ctx->mutex;
+ q->min_buffers_needed = 3;
+
+ ret = vb2_queue_init(q);
+ if (ret)
+ return ret;
+
+ /* init video dma queues */
+ INIT_LIST_HEAD(&ctx->vidq.active);
+
+ vfd = &ctx->vdev;
+ *vfd = cal_videodev;
+ vfd->v4l2_dev = &ctx->v4l2_dev;
+ vfd->queue = q;
+
+ /*
+ * Provide a mutex to v4l2 core. It will be used to protect
+ * all fops and v4l2 ioctls.
+ */
+ vfd->lock = &ctx->mutex;
+ video_set_drvdata(vfd, ctx);
+
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
+ if (ret < 0)
+ return ret;
+
+ v4l2_info(&ctx->v4l2_dev, "V4L2 device registered as %s\n",
+ video_device_node_name(vfd));
+
+ ctx->alloc_ctx = vb2_dma_contig_init_ctx(vfd->v4l2_dev->dev);
+ if (IS_ERR(ctx->alloc_ctx)) {
+ ctx_err(ctx, "Failed to alloc vb2 context\n");
+ ret = PTR_ERR(ctx->alloc_ctx);
+ goto vdev_unreg;
+ }
+
+ return 0;
+
+vdev_unreg:
+ video_unregister_device(vfd);
+ return ret;
+}
+
+static struct device_node *
+of_get_next_port(const struct device_node *parent,
+ struct device_node *prev)
+{
+ struct device_node *port = NULL;
+
+ if (!parent)
+ return NULL;
+
+ if (!prev) {
+ struct device_node *ports;
+ /*
+ * It's the first call, we have to find a port subnode
+ * within this node or within an optional 'ports' node.
+ */
+ ports = of_get_child_by_name(parent, "ports");
+ if (ports)
+ parent = ports;
+
+ port = of_get_child_by_name(parent, "port");
+
+ /* release the 'ports' node */
+ of_node_put(ports);
+ } else {
+ struct device_node *ports;
+
+ ports = of_get_parent(prev);
+ if (!ports)
+ return NULL;
+
+ do {
+ port = of_get_next_child(ports, prev);
+ if (!port) {
+ of_node_put(ports);
+ return NULL;
+ }
+ prev = port;
+ } while (of_node_cmp(port->name, "port") != 0);
+ }
+
+ return port;
+}
+
+static struct device_node *
+of_get_next_endpoint(const struct device_node *parent,
+ struct device_node *prev)
+{
+ struct device_node *ep = NULL;
+
+ if (!parent)
+ return NULL;
+
+ do {
+ ep = of_get_next_child(parent, prev);
+ if (!ep)
+ return NULL;
+ prev = ep;
+ } while (of_node_cmp(ep->name, "endpoint") != 0);
+
+ return ep;
+}
+
+static int of_cal_create_instance(struct cal_ctx *ctx, int inst)
+{
+ struct platform_device *pdev = ctx->dev->pdev;
+ struct device_node *ep_node, *port, *remote_ep,
+ *sensor_node, *parent;
+ struct v4l2_of_endpoint *endpoint;
+ struct v4l2_async_subdev *asd;
+ u32 regval = 0;
+ int ret, index, found_port = 0, lane;
+
+ parent = pdev->dev.of_node;
+
+ asd = &ctx->asd;
+ endpoint = &ctx->endpoint;
+
+ ep_node = NULL;
+ port = NULL;
+ remote_ep = NULL;
+ sensor_node = NULL;
+ ret = -EINVAL;
+
+ ctx_dbg(3, ctx, "Scanning Port node for csi2 port: %d\n", inst);
+ for (index = 0; index < CAL_NUM_CSI2_PORTS; index++) {
+ port = of_get_next_port(parent, port);
+ if (!port) {
+ ctx_dbg(1, ctx, "No port node found for csi2 port:%d\n",
+ index);
+ goto cleanup_exit;
+ }
+
+ /* Match the slice number with <REG> */
+ of_property_read_u32(port, "reg", ®val);
+ ctx_dbg(3, ctx, "port:%d inst:%d <reg>:%d\n",
+ index, inst, regval);
+ if ((regval == inst) && (index == inst)) {
+ found_port = 1;
+ break;
+ }
+ }
+
+ if (!found_port) {
+ ctx_dbg(1, ctx, "No port node matches csi2 port:%d\n",
+ inst);
+ goto cleanup_exit;
+ }
+
+ ctx_dbg(3, ctx, "Scanning sub-device for csi2 port: %d\n",
+ inst);
+
+ ep_node = of_get_next_endpoint(port, ep_node);
+ if (!ep_node) {
+ ctx_dbg(3, ctx, "can't get next endpoint\n");
+ goto cleanup_exit;
+ }
+
+ sensor_node = of_graph_get_remote_port_parent(ep_node);
+ if (!sensor_node) {
+ ctx_dbg(3, ctx, "can't get remote parent\n");
+ goto cleanup_exit;
+ }
+ asd->match_type = V4L2_ASYNC_MATCH_OF;
+ asd->match.of.node = sensor_node;
+
+ remote_ep = of_parse_phandle(ep_node, "remote-endpoint", 0);
+ if (!remote_ep) {
+ ctx_dbg(3, ctx, "can't get remote-endpoint\n");
+ goto cleanup_exit;
+ }
+ v4l2_of_parse_endpoint(remote_ep, endpoint);
+
+ if (endpoint->bus_type != V4L2_MBUS_CSI2) {
+ ctx_err(ctx, "Port:%d sub-device %s is not a CSI2 device\n",
+ inst, sensor_node->name);
+ goto cleanup_exit;
+ }
+
+ /* Store Virtual Channel number */
+ ctx->virtual_channel = endpoint->base.id;
+
+ ctx_dbg(3, ctx, "Port:%d v4l2-endpoint: CSI2\n", inst);
+ ctx_dbg(3, ctx, "Virtual Channel=%d\n", ctx->virtual_channel);
+ ctx_dbg(3, ctx, "flags=0x%08x\n", endpoint->bus.mipi_csi2.flags);
+ ctx_dbg(3, ctx, "clock_lane=%d\n", endpoint->bus.mipi_csi2.clock_lane);
+ ctx_dbg(3, ctx, "num_data_lanes=%d\n",
+ endpoint->bus.mipi_csi2.num_data_lanes);
+ ctx_dbg(3, ctx, "data_lanes= <\n");
+ for (lane = 0; lane < endpoint->bus.mipi_csi2.num_data_lanes; lane++)
+ ctx_dbg(3, ctx, "\t%d\n",
+ endpoint->bus.mipi_csi2.data_lanes[lane]);
+ ctx_dbg(3, ctx, "\t>\n");
+
+ ctx_dbg(1, ctx, "Port: %d found sub-device %s\n",
+ inst, sensor_node->name);
+
+ ctx->asd_list[0] = asd;
+ ctx->notifier.subdevs = ctx->asd_list;
+ ctx->notifier.num_subdevs = 1;
+ ctx->notifier.bound = cal_async_bound;
+ ctx->notifier.complete = cal_async_complete;
+ ret = v4l2_async_notifier_register(&ctx->v4l2_dev,
+ &ctx->notifier);
+ if (ret) {
+ ctx_err(ctx, "Error registering async notifier\n");
+ ret = -EINVAL;
+ }
+
+cleanup_exit:
+ if (!remote_ep)
+ of_node_put(remote_ep);
+ if (!sensor_node)
+ of_node_put(sensor_node);
+ if (!ep_node)
+ of_node_put(ep_node);
+ if (!port)
+ of_node_put(port);
+
+ return ret;
+}
+
+static struct cal_ctx *cal_create_instance(struct cal_dev *dev, int inst)
+{
+ struct cal_ctx *ctx;
+ struct v4l2_ctrl_handler *hdl;
+ int ret;
+
+ ctx = devm_kzalloc(&dev->pdev->dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return NULL;
+
+ /* save the cal_dev * for future ref */
+ ctx->dev = dev;
+
+ snprintf(ctx->v4l2_dev.name, sizeof(ctx->v4l2_dev.name),
+ "%s-%03d", CAL_MODULE_NAME, inst);
+ ret = v4l2_device_register(&dev->pdev->dev, &ctx->v4l2_dev);
+ if (ret)
+ goto err_exit;
+
+ hdl = &ctx->ctrl_handler;
+ ret = v4l2_ctrl_handler_init(hdl, 11);
+ if (ret) {
+ ctx_err(ctx, "Failed to init ctrl handler\n");
+ goto unreg_dev;
+ }
+ ctx->v4l2_dev.ctrl_handler = hdl;
+
+ /* Make sure Camera Core H/W register area is available */
+ ctx->cc = dev->cc[inst];
+
+ /* Store the instance id */
+ ctx->csi2_port = inst + 1;
+
+ ret = of_cal_create_instance(ctx, inst);
+ if (ret) {
+ ret = -EINVAL;
+ goto free_hdl;
+ }
+ return ctx;
+
+free_hdl:
+ v4l2_ctrl_handler_free(hdl);
+unreg_dev:
+ v4l2_device_unregister(&ctx->v4l2_dev);
+err_exit:
+ return NULL;
+}
+
+static int cal_probe(struct platform_device *pdev)
+{
+ struct cal_dev *dev;
+ int ret;
+ int irq;
+
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ /* set pseudo v4l2 device name so we can use v4l2_printk */
+ strlcpy(dev->v4l2_dev.name, CAL_MODULE_NAME,
+ sizeof(dev->v4l2_dev.name));
+
+ /* save pdev pointer */
+ dev->pdev = pdev;
+
+ dev->res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "cal_top");
+ dev->base = devm_ioremap_resource(&pdev->dev, dev->res);
+ if (IS_ERR(dev->base))
+ return PTR_ERR(dev->base);
+
+ cal_dbg(1, dev, "ioresource %s at %pa - %pa\n",
+ dev->res->name, &dev->res->start, &dev->res->end);
+
+ irq = platform_get_irq(pdev, 0);
+ cal_dbg(1, dev, "got irq# %d\n", irq);
+ ret = devm_request_irq(&pdev->dev, irq, cal_irq, 0, CAL_MODULE_NAME,
+ dev);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, dev);
+
+ dev->cm = cm_create(dev);
+ if (IS_ERR(dev->cm))
+ return PTR_ERR(dev->cm);
+
+ dev->cc[0] = cc_create(dev, 0);
+ if (IS_ERR(dev->cc[0]))
+ return PTR_ERR(dev->cc[0]);
+
+ dev->cc[1] = cc_create(dev, 1);
+ if (IS_ERR(dev->cc[1]))
+ return PTR_ERR(dev->cc[1]);
+
+ dev->ctx[0] = NULL;
+ dev->ctx[1] = NULL;
+
+ dev->ctx[0] = cal_create_instance(dev, 0);
+ dev->ctx[1] = cal_create_instance(dev, 1);
+ if (!dev->ctx[0] && !dev->ctx[1]) {
+ cal_err(dev, "Neither port is configured, no point in staying up\n");
+ return -ENODEV;
+ }
+
+ pm_runtime_enable(&pdev->dev);
+
+ ret = cal_runtime_get(dev);
+ if (ret)
+ goto runtime_disable;
+
+ /* Just check we can actually access the module */
+ cal_get_hwinfo(dev);
+
+ cal_runtime_put(dev);
+
+ return 0;
+
+runtime_disable:
+ pm_runtime_disable(&pdev->dev);
+ return ret;
+}
+
+static int cal_remove(struct platform_device *pdev)
+{
+ struct cal_dev *dev =
+ (struct cal_dev *)platform_get_drvdata(pdev);
+ struct cal_ctx *ctx;
+ int i;
+
+ cal_dbg(1, dev, "Removing %s\n", CAL_MODULE_NAME);
+
+ cal_runtime_get(dev);
+
+ for (i = 0; i < CAL_NUM_CONTEXT; i++) {
+ ctx = dev->ctx[i];
+ if (ctx) {
+ ctx_dbg(1, ctx, "unregistering %s\n",
+ video_device_node_name(&ctx->vdev));
+ camerarx_phy_disable(ctx);
+ v4l2_async_notifier_unregister(&ctx->notifier);
+ vb2_dma_contig_cleanup_ctx(ctx->alloc_ctx);
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ v4l2_device_unregister(&ctx->v4l2_dev);
+ video_unregister_device(&ctx->vdev);
+ }
+ }
+
+ cal_runtime_put(dev);
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+#if defined(CONFIG_OF)
+static const struct of_device_id cal_of_match[] = {
+ { .compatible = "ti,dra72-cal", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cal_of_match);
+#endif
+
+static struct platform_driver cal_pdrv = {
+ .probe = cal_probe,
+ .remove = cal_remove,
+ .driver = {
+ .name = CAL_MODULE_NAME,
+ .of_match_table = of_match_ptr(cal_of_match),
+ },
+};
+
+module_platform_driver(cal_pdrv);
--- /dev/null
+/*
+ * TI CAL camera interface driver
+ *
+ * Copyright (c) 2015 Texas Instruments Inc.
+ *
+ * Benoit Parrot, <bparrot@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef __TI_CAL_REGS_H
+#define __TI_CAL_REGS_H
+
+#define CAL_NUM_CSI2_PORTS 2
+
+/* CAL register offsets */
+
+#define CAL_HL_REVISION 0x0000
+#define CAL_HL_HWINFO 0x0004
+#define CAL_HL_SYSCONFIG 0x0010
+#define CAL_HL_IRQ_EOI 0x001c
+#define CAL_HL_IRQSTATUS_RAW(m) (0x20U + ((m-1) * 0x10U))
+#define CAL_HL_IRQSTATUS(m) (0x24U + ((m-1) * 0x10U))
+#define CAL_HL_IRQENABLE_SET(m) (0x28U + ((m-1) * 0x10U))
+#define CAL_HL_IRQENABLE_CLR(m) (0x2cU + ((m-1) * 0x10U))
+#define CAL_PIX_PROC(m) (0xc0U + ((m-1) * 0x4U))
+#define CAL_CTRL 0x100
+#define CAL_CTRL1 0x104
+#define CAL_LINE_NUMBER_EVT 0x108
+#define CAL_VPORT_CTRL1 0x120
+#define CAL_VPORT_CTRL2 0x124
+#define CAL_BYS_CTRL1 0x130
+#define CAL_BYS_CTRL2 0x134
+#define CAL_RD_DMA_CTRL 0x140
+#define CAL_RD_DMA_PIX_ADDR 0x144
+#define CAL_RD_DMA_PIX_OFST 0x148
+#define CAL_RD_DMA_XSIZE 0x14c
+#define CAL_RD_DMA_YSIZE 0x150
+#define CAL_RD_DMA_INIT_ADDR 0x154
+#define CAL_RD_DMA_INIT_OFST 0x168
+#define CAL_RD_DMA_CTRL2 0x16c
+#define CAL_WR_DMA_CTRL(m) (0x200U + ((m-1) * 0x10U))
+#define CAL_WR_DMA_ADDR(m) (0x204U + ((m-1) * 0x10U))
+#define CAL_WR_DMA_OFST(m) (0x208U + ((m-1) * 0x10U))
+#define CAL_WR_DMA_XSIZE(m) (0x20cU + ((m-1) * 0x10U))
+#define CAL_CSI2_PPI_CTRL(m) (0x300U + ((m-1) * 0x80U))
+#define CAL_CSI2_COMPLEXIO_CFG(m) (0x304U + ((m-1) * 0x80U))
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS(m) (0x308U + ((m-1) * 0x80U))
+#define CAL_CSI2_SHORT_PACKET(m) (0x30cU + ((m-1) * 0x80U))
+#define CAL_CSI2_COMPLEXIO_IRQENABLE(m) (0x310U + ((m-1) * 0x80U))
+#define CAL_CSI2_TIMING(m) (0x314U + ((m-1) * 0x80U))
+#define CAL_CSI2_VC_IRQENABLE(m) (0x318U + ((m-1) * 0x80U))
+#define CAL_CSI2_VC_IRQSTATUS(m) (0x328U + ((m-1) * 0x80U))
+#define CAL_CSI2_CTX0(m) (0x330U + ((m-1) * 0x80U))
+#define CAL_CSI2_CTX1(m) (0x334U + ((m-1) * 0x80U))
+#define CAL_CSI2_CTX2(m) (0x338U + ((m-1) * 0x80U))
+#define CAL_CSI2_CTX3(m) (0x33cU + ((m-1) * 0x80U))
+#define CAL_CSI2_CTX4(m) (0x340U + ((m-1) * 0x80U))
+#define CAL_CSI2_CTX5(m) (0x344U + ((m-1) * 0x80U))
+#define CAL_CSI2_CTX6(m) (0x348U + ((m-1) * 0x80U))
+#define CAL_CSI2_CTX7(m) (0x34cU + ((m-1) * 0x80U))
+#define CAL_CSI2_STATUS0(m) (0x350U + ((m-1) * 0x80U))
+#define CAL_CSI2_STATUS1(m) (0x354U + ((m-1) * 0x80U))
+#define CAL_CSI2_STATUS2(m) (0x358U + ((m-1) * 0x80U))
+#define CAL_CSI2_STATUS3(m) (0x35cU + ((m-1) * 0x80U))
+#define CAL_CSI2_STATUS4(m) (0x360U + ((m-1) * 0x80U))
+#define CAL_CSI2_STATUS5(m) (0x364U + ((m-1) * 0x80U))
+#define CAL_CSI2_STATUS6(m) (0x368U + ((m-1) * 0x80U))
+#define CAL_CSI2_STATUS7(m) (0x36cU + ((m-1) * 0x80U))
+
+/* CAL CSI2 PHY register offsets */
+#define CAL_CSI2_PHY_REG0 0x000
+#define CAL_CSI2_PHY_REG1 0x004
+#define CAL_CSI2_PHY_REG2 0x008
+
+/* CAL Control Module Core Camerrx Control register offsets */
+#define CM_CTRL_CORE_CAMERRX_CONTROL 0x000
+
+/*********************************************************************
+* Generic value used in various field below
+*********************************************************************/
+
+#define CAL_GEN_DISABLE 0
+#define CAL_GEN_ENABLE 1
+#define CAL_GEN_FALSE 0
+#define CAL_GEN_TRUE 1
+
+/*********************************************************************
+* Field Definition Macros
+*********************************************************************/
+
+#define CAL_HL_REVISION_MINOR_MASK GENMASK(5, 0)
+#define CAL_HL_REVISION_CUSTOM_MASK GENMASK(7, 6)
+#define CAL_HL_REVISION_MAJOR_MASK GENMASK(10, 8)
+#define CAL_HL_REVISION_RTL_MASK GENMASK(15, 11)
+#define CAL_HL_REVISION_FUNC_MASK GENMASK(27, 16)
+#define CAL_HL_REVISION_SCHEME_MASK GENMASK(31, 30)
+#define CAL_HL_REVISION_SCHEME_H08 1
+#define CAL_HL_REVISION_SCHEME_LEGACY 0
+
+#define CAL_HL_HWINFO_WFIFO_MASK GENMASK(3, 0)
+#define CAL_HL_HWINFO_RFIFO_MASK GENMASK(7, 4)
+#define CAL_HL_HWINFO_PCTX_MASK GENMASK(12, 8)
+#define CAL_HL_HWINFO_WCTX_MASK GENMASK(18, 13)
+#define CAL_HL_HWINFO_VFIFO_MASK GENMASK(22, 19)
+#define CAL_HL_HWINFO_NCPORT_MASK GENMASK(27, 23)
+#define CAL_HL_HWINFO_NPPI_CTXS0_MASK GENMASK(29, 28)
+#define CAL_HL_HWINFO_NPPI_CTXS1_MASK GENMASK(31, 30)
+#define CAL_HL_HWINFO_NPPI_CONTEXTS_ZERO 0
+#define CAL_HL_HWINFO_NPPI_CONTEXTS_FOUR 1
+#define CAL_HL_HWINFO_NPPI_CONTEXTS_EIGHT 2
+#define CAL_HL_HWINFO_NPPI_CONTEXTS_RESERVED 3
+
+#define CAL_HL_SYSCONFIG_SOFTRESET_MASK BIT_MASK(0)
+#define CAL_HL_SYSCONFIG_SOFTRESET_DONE 0x0
+#define CAL_HL_SYSCONFIG_SOFTRESET_PENDING 0x1
+#define CAL_HL_SYSCONFIG_SOFTRESET_NOACTION 0x0
+#define CAL_HL_SYSCONFIG_SOFTRESET_RESET 0x1
+#define CAL_HL_SYSCONFIG_IDLE_MASK GENMASK(3, 2)
+#define CAL_HL_SYSCONFIG_IDLEMODE_FORCE 0
+#define CAL_HL_SYSCONFIG_IDLEMODE_NO 1
+#define CAL_HL_SYSCONFIG_IDLEMODE_SMART1 2
+#define CAL_HL_SYSCONFIG_IDLEMODE_SMART2 3
+
+#define CAL_HL_IRQ_EOI_LINE_NUMBER_MASK BIT_MASK(0)
+#define CAL_HL_IRQ_EOI_LINE_NUMBER_READ0 0
+#define CAL_HL_IRQ_EOI_LINE_NUMBER_EOI0 0
+
+#define CAL_HL_IRQ_MASK(m) BIT_MASK(m-1)
+#define CAL_HL_IRQ_NOACTION 0x0
+#define CAL_HL_IRQ_ENABLE 0x1
+#define CAL_HL_IRQ_CLEAR 0x1
+#define CAL_HL_IRQ_DISABLED 0x0
+#define CAL_HL_IRQ_ENABLED 0x1
+#define CAL_HL_IRQ_PENDING 0x1
+
+#define CAL_PIX_PROC_EN_MASK BIT_MASK(0)
+#define CAL_PIX_PROC_EXTRACT_MASK GENMASK(4, 1)
+#define CAL_PIX_PROC_EXTRACT_B6 0x0
+#define CAL_PIX_PROC_EXTRACT_B7 0x1
+#define CAL_PIX_PROC_EXTRACT_B8 0x2
+#define CAL_PIX_PROC_EXTRACT_B10 0x3
+#define CAL_PIX_PROC_EXTRACT_B10_MIPI 0x4
+#define CAL_PIX_PROC_EXTRACT_B12 0x5
+#define CAL_PIX_PROC_EXTRACT_B12_MIPI 0x6
+#define CAL_PIX_PROC_EXTRACT_B14 0x7
+#define CAL_PIX_PROC_EXTRACT_B14_MIPI 0x8
+#define CAL_PIX_PROC_EXTRACT_B16_BE 0x9
+#define CAL_PIX_PROC_EXTRACT_B16_LE 0xa
+#define CAL_PIX_PROC_DPCMD_MASK GENMASK(9, 5)
+#define CAL_PIX_PROC_DPCMD_BYPASS 0x0
+#define CAL_PIX_PROC_DPCMD_DPCM_10_8_1 0x2
+#define CAL_PIX_PROC_DPCMD_DPCM_12_8_1 0x8
+#define CAL_PIX_PROC_DPCMD_DPCM_10_7_1 0x4
+#define CAL_PIX_PROC_DPCMD_DPCM_10_7_2 0x5
+#define CAL_PIX_PROC_DPCMD_DPCM_10_6_1 0x6
+#define CAL_PIX_PROC_DPCMD_DPCM_10_6_2 0x7
+#define CAL_PIX_PROC_DPCMD_DPCM_12_7_1 0xa
+#define CAL_PIX_PROC_DPCMD_DPCM_12_6_1 0xc
+#define CAL_PIX_PROC_DPCMD_DPCM_14_10 0xe
+#define CAL_PIX_PROC_DPCMD_DPCM_14_8_1 0x10
+#define CAL_PIX_PROC_DPCMD_DPCM_16_12_1 0x12
+#define CAL_PIX_PROC_DPCMD_DPCM_16_10_1 0x14
+#define CAL_PIX_PROC_DPCMD_DPCM_16_8_1 0x16
+#define CAL_PIX_PROC_DPCME_MASK GENMASK(15, 11)
+#define CAL_PIX_PROC_DPCME_BYPASS 0x0
+#define CAL_PIX_PROC_DPCME_DPCM_10_8_1 0x2
+#define CAL_PIX_PROC_DPCME_DPCM_12_8_1 0x8
+#define CAL_PIX_PROC_DPCME_DPCM_14_10 0xe
+#define CAL_PIX_PROC_DPCME_DPCM_14_8_1 0x10
+#define CAL_PIX_PROC_DPCME_DPCM_16_12_1 0x12
+#define CAL_PIX_PROC_DPCME_DPCM_16_10_1 0x14
+#define CAL_PIX_PROC_DPCME_DPCM_16_8_1 0x16
+#define CAL_PIX_PROC_PACK_MASK GENMASK(18, 16)
+#define CAL_PIX_PROC_PACK_B8 0x0
+#define CAL_PIX_PROC_PACK_B10_MIPI 0x2
+#define CAL_PIX_PROC_PACK_B12 0x3
+#define CAL_PIX_PROC_PACK_B12_MIPI 0x4
+#define CAL_PIX_PROC_PACK_B16 0x5
+#define CAL_PIX_PROC_PACK_ARGB 0x6
+#define CAL_PIX_PROC_CPORT_MASK GENMASK(23, 19)
+
+#define CAL_CTRL_POSTED_WRITES_MASK BIT_MASK(0)
+#define CAL_CTRL_POSTED_WRITES_NONPOSTED 0
+#define CAL_CTRL_POSTED_WRITES 1
+#define CAL_CTRL_TAGCNT_MASK GENMASK(4, 1)
+#define CAL_CTRL_BURSTSIZE_MASK GENMASK(6, 5)
+#define CAL_CTRL_BURSTSIZE_BURST16 0x0
+#define CAL_CTRL_BURSTSIZE_BURST32 0x1
+#define CAL_CTRL_BURSTSIZE_BURST64 0x2
+#define CAL_CTRL_BURSTSIZE_BURST128 0x3
+#define CAL_CTRL_LL_FORCE_STATE_MASK GENMASK(12, 7)
+#define CAL_CTRL_MFLAGL_MASK GENMASK(20, 13)
+#define CAL_CTRL_PWRSCPCLK_MASK BIT_MASK(21)
+#define CAL_CTRL_PWRSCPCLK_AUTO 0
+#define CAL_CTRL_PWRSCPCLK_FORCE 1
+#define CAL_CTRL_RD_DMA_STALL_MASK BIT_MASK(22)
+#define CAL_CTRL_MFLAGH_MASK GENMASK(31, 24)
+
+#define CAL_CTRL1_PPI_GROUPING_MASK GENMASK(1, 0)
+#define CAL_CTRL1_PPI_GROUPING_DISABLED 0
+#define CAL_CTRL1_PPI_GROUPING_RESERVED 1
+#define CAL_CTRL1_PPI_GROUPING_0 2
+#define CAL_CTRL1_PPI_GROUPING_1 3
+#define CAL_CTRL1_INTERLEAVE01_MASK GENMASK(3, 2)
+#define CAL_CTRL1_INTERLEAVE01_DISABLED 0
+#define CAL_CTRL1_INTERLEAVE01_PIX1 1
+#define CAL_CTRL1_INTERLEAVE01_PIX4 2
+#define CAL_CTRL1_INTERLEAVE01_RESERVED 3
+#define CAL_CTRL1_INTERLEAVE23_MASK GENMASK(5, 4)
+#define CAL_CTRL1_INTERLEAVE23_DISABLED 0
+#define CAL_CTRL1_INTERLEAVE23_PIX1 1
+#define CAL_CTRL1_INTERLEAVE23_PIX4 2
+#define CAL_CTRL1_INTERLEAVE23_RESERVED 3
+
+#define CAL_LINE_NUMBER_EVT_CPORT_MASK GENMASK(4, 0)
+#define CAL_LINE_NUMBER_EVT_MASK GENMASK(29, 16)
+
+#define CAL_VPORT_CTRL1_PCLK_MASK GENMASK(16, 0)
+#define CAL_VPORT_CTRL1_XBLK_MASK GENMASK(24, 17)
+#define CAL_VPORT_CTRL1_YBLK_MASK GENMASK(30, 25)
+#define CAL_VPORT_CTRL1_WIDTH_MASK BIT_MASK(31)
+#define CAL_VPORT_CTRL1_WIDTH_ONE 0
+#define CAL_VPORT_CTRL1_WIDTH_TWO 1
+
+#define CAL_VPORT_CTRL2_CPORT_MASK GENMASK(4, 0)
+#define CAL_VPORT_CTRL2_FREERUNNING_MASK BIT_MASK(15)
+#define CAL_VPORT_CTRL2_FREERUNNING_GATED 0
+#define CAL_VPORT_CTRL2_FREERUNNING_FREE 1
+#define CAL_VPORT_CTRL2_FS_RESETS_MASK BIT_MASK(16)
+#define CAL_VPORT_CTRL2_FS_RESETS_NO 0
+#define CAL_VPORT_CTRL2_FS_RESETS_YES 1
+#define CAL_VPORT_CTRL2_FSM_RESET_MASK BIT_MASK(17)
+#define CAL_VPORT_CTRL2_FSM_RESET_NOEFFECT 0
+#define CAL_VPORT_CTRL2_FSM_RESET 1
+#define CAL_VPORT_CTRL2_RDY_THR_MASK GENMASK(31, 18)
+
+#define CAL_BYS_CTRL1_PCLK_MASK GENMASK(16, 0)
+#define CAL_BYS_CTRL1_XBLK_MASK GENMASK(24, 17)
+#define CAL_BYS_CTRL1_YBLK_MASK GENMASK(30, 25)
+#define CAL_BYS_CTRL1_BYSINEN_MASK BIT_MASK(31)
+
+#define CAL_BYS_CTRL2_CPORTIN_MASK GENMASK(4, 0)
+#define CAL_BYS_CTRL2_CPORTOUT_MASK GENMASK(9, 5)
+#define CAL_BYS_CTRL2_DUPLICATEDDATA_MASK BIT_MASK(10)
+#define CAL_BYS_CTRL2_DUPLICATEDDATA_NO 0
+#define CAL_BYS_CTRL2_DUPLICATEDDATA_YES 1
+#define CAL_BYS_CTRL2_FREERUNNING_MASK BIT_MASK(11)
+#define CAL_BYS_CTRL2_FREERUNNING_NO 0
+#define CAL_BYS_CTRL2_FREERUNNING_YES 1
+
+#define CAL_RD_DMA_CTRL_GO_MASK BIT_MASK(0)
+#define CAL_RD_DMA_CTRL_GO_DIS 0
+#define CAL_RD_DMA_CTRL_GO_EN 1
+#define CAL_RD_DMA_CTRL_GO_IDLE 0
+#define CAL_RD_DMA_CTRL_GO_BUSY 1
+#define CAL_RD_DMA_CTRL_INIT_MASK BIT_MASK(1)
+#define CAL_RD_DMA_CTRL_BW_LIMITER_MASK GENMASK(10, 2)
+#define CAL_RD_DMA_CTRL_OCP_TAG_CNT_MASK GENMASK(14, 11)
+#define CAL_RD_DMA_CTRL_PCLK_MASK GENMASK(31, 15)
+
+#define CAL_RD_DMA_PIX_ADDR_MASK GENMASK(31, 3)
+
+#define CAL_RD_DMA_PIX_OFST_MASK GENMASK(31, 4)
+
+#define CAL_RD_DMA_XSIZE_MASK GENMASK(31, 19)
+
+#define CAL_RD_DMA_YSIZE_MASK GENMASK(29, 16)
+
+#define CAL_RD_DMA_INIT_ADDR_MASK GENMASK(31, 3)
+
+#define CAL_RD_DMA_INIT_OFST_MASK GENMASK(31, 3)
+
+#define CAL_RD_DMA_CTRL2_CIRC_MODE_MASK GENMASK(2, 0)
+#define CAL_RD_DMA_CTRL2_CIRC_MODE_DIS 0
+#define CAL_RD_DMA_CTRL2_CIRC_MODE_ONE 1
+#define CAL_RD_DMA_CTRL2_CIRC_MODE_FOUR 2
+#define CAL_RD_DMA_CTRL2_CIRC_MODE_SIXTEEN 3
+#define CAL_RD_DMA_CTRL2_CIRC_MODE_SIXTYFOUR 4
+#define CAL_RD_DMA_CTRL2_CIRC_MODE_RESERVED 5
+#define CAL_RD_DMA_CTRL2_ICM_CSTART_MASK BIT_MASK(3)
+#define CAL_RD_DMA_CTRL2_PATTERN_MASK GENMASK(5, 4)
+#define CAL_RD_DMA_CTRL2_PATTERN_LINEAR 0
+#define CAL_RD_DMA_CTRL2_PATTERN_YUV420 1
+#define CAL_RD_DMA_CTRL2_PATTERN_RD2SKIP2 2
+#define CAL_RD_DMA_CTRL2_PATTERN_RD2SKIP4 3
+#define CAL_RD_DMA_CTRL2_BYSOUT_LE_WAIT_MASK BIT_MASK(6)
+#define CAL_RD_DMA_CTRL2_BYSOUT_LE_WAIT_FREERUNNING 0
+#define CAL_RD_DMA_CTRL2_BYSOUT_LE_WAIT_WAITFORBYSOUT 1
+#define CAL_RD_DMA_CTRL2_CIRC_SIZE_MASK GENMASK(29, 16)
+
+#define CAL_WR_DMA_CTRL_MODE_MASK GENMASK(2, 0)
+#define CAL_WR_DMA_CTRL_MODE_DIS 0
+#define CAL_WR_DMA_CTRL_MODE_SHD 1
+#define CAL_WR_DMA_CTRL_MODE_CNT 2
+#define CAL_WR_DMA_CTRL_MODE_CNT_INIT 3
+#define CAL_WR_DMA_CTRL_MODE_CONST 4
+#define CAL_WR_DMA_CTRL_MODE_RESERVED 5
+#define CAL_WR_DMA_CTRL_PATTERN_MASK GENMASK(4, 3)
+#define CAL_WR_DMA_CTRL_PATTERN_LINEAR 0
+#define CAL_WR_DMA_CTRL_PATTERN_WR2SKIP2 2
+#define CAL_WR_DMA_CTRL_PATTERN_WR2SKIP4 3
+#define CAL_WR_DMA_CTRL_PATTERN_RESERVED 1
+#define CAL_WR_DMA_CTRL_ICM_PSTART_MASK BIT_MASK(5)
+#define CAL_WR_DMA_CTRL_DTAG_MASK GENMASK(8, 6)
+#define CAL_WR_DMA_CTRL_DTAG_ATT_HDR 0
+#define CAL_WR_DMA_CTRL_DTAG_ATT_DAT 1
+#define CAL_WR_DMA_CTRL_DTAG 2
+#define CAL_WR_DMA_CTRL_DTAG_PIX_HDR 3
+#define CAL_WR_DMA_CTRL_DTAG_PIX_DAT 4
+#define CAL_WR_DMA_CTRL_DTAG_D5 5
+#define CAL_WR_DMA_CTRL_DTAG_D6 6
+#define CAL_WR_DMA_CTRL_DTAG_D7 7
+#define CAL_WR_DMA_CTRL_CPORT_MASK GENMASK(13, 9)
+#define CAL_WR_DMA_CTRL_STALL_RD_MASK BIT_MASK(14)
+#define CAL_WR_DMA_CTRL_YSIZE_MASK GENMASK(31, 18)
+
+#define CAL_WR_DMA_ADDR_MASK GENMASK(31, 4)
+
+#define CAL_WR_DMA_OFST_MASK GENMASK(18, 4)
+#define CAL_WR_DMA_OFST_CIRC_MODE_MASK GENMASK(23, 22)
+#define CAL_WR_DMA_OFST_CIRC_MODE_ONE 1
+#define CAL_WR_DMA_OFST_CIRC_MODE_FOUR 2
+#define CAL_WR_DMA_OFST_CIRC_MODE_SIXTYFOUR 3
+#define CAL_WR_DMA_OFST_CIRC_MODE_DISABLED 0
+#define CAL_WR_DMA_OFST_CIRC_SIZE_MASK GENMASK(31, 24)
+
+#define CAL_WR_DMA_XSIZE_XSKIP_MASK GENMASK(15, 3)
+#define CAL_WR_DMA_XSIZE_MASK GENMASK(31, 19)
+
+#define CAL_CSI2_PPI_CTRL_IF_EN_MASK BIT_MASK(0)
+#define CAL_CSI2_PPI_CTRL_ECC_EN_MASK BIT_MASK(2)
+#define CAL_CSI2_PPI_CTRL_FRAME_MASK BIT_MASK(3)
+#define CAL_CSI2_PPI_CTRL_FRAME_IMMEDIATE 0
+#define CAL_CSI2_PPI_CTRL_FRAME 1
+
+#define CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK GENMASK(2, 0)
+#define CAL_CSI2_COMPLEXIO_CFG_POSITION_5 5
+#define CAL_CSI2_COMPLEXIO_CFG_POSITION_4 4
+#define CAL_CSI2_COMPLEXIO_CFG_POSITION_3 3
+#define CAL_CSI2_COMPLEXIO_CFG_POSITION_2 2
+#define CAL_CSI2_COMPLEXIO_CFG_POSITION_1 1
+#define CAL_CSI2_COMPLEXIO_CFG_POSITION_NOT_USED 0
+#define CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK BIT_MASK(3)
+#define CAL_CSI2_COMPLEXIO_CFG_POL_PLUSMINUS 0
+#define CAL_CSI2_COMPLEXIO_CFG_POL_MINUSPLUS 1
+#define CAL_CSI2_COMPLEXIO_CFG_DATA1_POSITION_MASK GENMASK(6, 4)
+#define CAL_CSI2_COMPLEXIO_CFG_DATA1_POL_MASK BIT_MASK(7)
+#define CAL_CSI2_COMPLEXIO_CFG_DATA2_POSITION_MASK GENMASK(10, 8)
+#define CAL_CSI2_COMPLEXIO_CFG_DATA2_POL_MASK BIT_MASK(11)
+#define CAL_CSI2_COMPLEXIO_CFG_DATA3_POSITION_MASK GENMASK(14, 12)
+#define CAL_CSI2_COMPLEXIO_CFG_DATA3_POL_MASK BIT_MASK(15)
+#define CAL_CSI2_COMPLEXIO_CFG_DATA4_POSITION_MASK GENMASK(18, 16)
+#define CAL_CSI2_COMPLEXIO_CFG_DATA4_POL_MASK BIT_MASK(19)
+#define CAL_CSI2_COMPLEXIO_CFG_PWR_AUTO_MASK BIT_MASK(24)
+#define CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK GENMASK(26, 25)
+#define CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_STATE_OFF 0
+#define CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_STATE_ON 1
+#define CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_STATE_ULP 2
+#define CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK GENMASK(28, 27)
+#define CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_OFF 0
+#define CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON 1
+#define CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ULP 2
+#define CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK BIT_MASK(29)
+#define CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED 1
+#define CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETONGOING 0
+#define CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK BIT_MASK(30)
+#define CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL 0
+#define CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL 1
+
+#define CAL_CSI2_SHORT_PACKET_MASK GENMASK(23, 0)
+
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTHS1_MASK BIT_MASK(0)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTHS2_MASK BIT_MASK(1)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTHS3_MASK BIT_MASK(2)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTHS4_MASK BIT_MASK(3)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTHS5_MASK BIT_MASK(4)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS1_MASK BIT_MASK(5)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS2_MASK BIT_MASK(6)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS3_MASK BIT_MASK(7)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS4_MASK BIT_MASK(8)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS5_MASK BIT_MASK(9)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRESC1_MASK BIT_MASK(10)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRESC2_MASK BIT_MASK(11)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRESC3_MASK BIT_MASK(12)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRESC4_MASK BIT_MASK(13)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRESC5_MASK BIT_MASK(14)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRCONTROL1_MASK BIT_MASK(15)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRCONTROL2_MASK BIT_MASK(16)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRCONTROL3_MASK BIT_MASK(17)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRCONTROL4_MASK BIT_MASK(18)
+#define CAL_CSI2_COMPLEXIO_IRQ_ERRCONTROL5_MASK BIT_MASK(19)
+#define CAL_CSI2_COMPLEXIO_IRQ_STATEULPM1_MASK BIT_MASK(20)
+#define CAL_CSI2_COMPLEXIO_IRQ_STATEULPM2_MASK BIT_MASK(21)
+#define CAL_CSI2_COMPLEXIO_IRQ_STATEULPM3_MASK BIT_MASK(22)
+#define CAL_CSI2_COMPLEXIO_IRQ_STATEULPM4_MASK BIT_MASK(23)
+#define CAL_CSI2_COMPLEXIO_IRQ_STATEULPM5_MASK BIT_MASK(24)
+#define CAL_CSI2_COMPLEXIO_IRQ_STATEALLULPMENTER_MASK BIT_MASK(25)
+#define CAL_CSI2_COMPLEXIO_IRQ_STATEALLULPMEXIT_MASK BIT_MASK(26)
+#define CAL_CSI2_COMPLEXIO_IRQ_FIFO_OVR_MASK BIT_MASK(27)
+#define CAL_CSI2_COMPLEXIO_IRQ_SHORT_PACKET_MASK BIT_MASK(28)
+#define CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK BIT_MASK(30)
+
+#define CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK GENMASK(12, 0)
+#define CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK BIT_MASK(13)
+#define CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK BIT_MASK(14)
+#define CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK BIT_MASK(15)
+
+#define CAL_CSI2_VC_IRQ_FS_IRQ_0_MASK BIT_MASK(0)
+#define CAL_CSI2_VC_IRQ_FE_IRQ_0_MASK BIT_MASK(1)
+#define CAL_CSI2_VC_IRQ_LS_IRQ_0_MASK BIT_MASK(2)
+#define CAL_CSI2_VC_IRQ_LE_IRQ_0_MASK BIT_MASK(3)
+#define CAL_CSI2_VC_IRQ_CS_IRQ_0_MASK BIT_MASK(4)
+#define CAL_CSI2_VC_IRQ_ECC_CORRECTION0_IRQ_0_MASK BIT_MASK(5)
+#define CAL_CSI2_VC_IRQ_FS_IRQ_1_MASK BIT_MASK(8)
+#define CAL_CSI2_VC_IRQ_FE_IRQ_1_MASK BIT_MASK(9)
+#define CAL_CSI2_VC_IRQ_LS_IRQ_1_MASK BIT_MASK(10)
+#define CAL_CSI2_VC_IRQ_LE_IRQ_1_MASK BIT_MASK(11)
+#define CAL_CSI2_VC_IRQ_CS_IRQ_1_MASK BIT_MASK(12)
+#define CAL_CSI2_VC_IRQ_ECC_CORRECTION0_IRQ_1_MASK BIT_MASK(13)
+#define CAL_CSI2_VC_IRQ_FS_IRQ_2_MASK BIT_MASK(16)
+#define CAL_CSI2_VC_IRQ_FE_IRQ_2_MASK BIT_MASK(17)
+#define CAL_CSI2_VC_IRQ_LS_IRQ_2_MASK BIT_MASK(18)
+#define CAL_CSI2_VC_IRQ_LE_IRQ_2_MASK BIT_MASK(19)
+#define CAL_CSI2_VC_IRQ_CS_IRQ_2_MASK BIT_MASK(20)
+#define CAL_CSI2_VC_IRQ_ECC_CORRECTION0_IRQ_2_MASK BIT_MASK(21)
+#define CAL_CSI2_VC_IRQ_FS_IRQ_3_MASK BIT_MASK(24)
+#define CAL_CSI2_VC_IRQ_FE_IRQ_3_MASK BIT_MASK(25)
+#define CAL_CSI2_VC_IRQ_LS_IRQ_3_MASK BIT_MASK(26)
+#define CAL_CSI2_VC_IRQ_LE_IRQ_3_MASK BIT_MASK(27)
+#define CAL_CSI2_VC_IRQ_CS_IRQ_3_MASK BIT_MASK(28)
+#define CAL_CSI2_VC_IRQ_ECC_CORRECTION0_IRQ_3_MASK BIT_MASK(29)
+
+#define CAL_CSI2_CTX_DT_MASK GENMASK(5, 0)
+#define CAL_CSI2_CTX_VC_MASK GENMASK(7, 6)
+#define CAL_CSI2_CTX_CPORT_MASK GENMASK(12, 8)
+#define CAL_CSI2_CTX_ATT_MASK BIT_MASK(13)
+#define CAL_CSI2_CTX_ATT_PIX 0
+#define CAL_CSI2_CTX_ATT 1
+#define CAL_CSI2_CTX_PACK_MODE_MASK BIT_MASK(14)
+#define CAL_CSI2_CTX_PACK_MODE_LINE 0
+#define CAL_CSI2_CTX_PACK_MODE_FRAME 1
+#define CAL_CSI2_CTX_LINES_MASK GENMASK(29, 16)
+
+#define CAL_CSI2_STATUS_FRAME_MASK GENMASK(15, 0)
+
+#define CAL_CSI2_PHY_REG0_THS_SETTLE_MASK GENMASK(7, 0)
+#define CAL_CSI2_PHY_REG0_THS_TERM_MASK GENMASK(15, 8)
+#define CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK BIT_MASK(24)
+#define CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE 1
+#define CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_ENABLE 0
+
+#define CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK GENMASK(7, 0)
+#define CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK GENMASK(9, 8)
+#define CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK GENMASK(17, 10)
+#define CAL_CSI2_PHY_REG1_TCLK_TERM_MASK GENMASK(24, 18)
+#define CAL_CSI2_PHY_REG1_CLOCK_MISS_DETECTOR_STATUS_MASK BIT_MASK(25)
+#define CAL_CSI2_PHY_REG1_CLOCK_MISS_DETECTOR_STATUS_ERROR 1
+#define CAL_CSI2_PHY_REG1_CLOCK_MISS_DETECTOR_STATUS_SUCCESS 0
+#define CAL_CSI2_PHY_REG1_RESET_DONE_STATUS_MASK GENMASK(29, 28)
+
+#define CAL_CSI2_PHY_REG2_CCP2_SYNC_PATTERN_MASK GENMASK(23, 0)
+#define CAL_CSI2_PHY_REG2_TRIGGER_CMD_RXTRIGESC3_MASK GENMASK(25, 24)
+#define CAL_CSI2_PHY_REG2_TRIGGER_CMD_RXTRIGESC2_MASK GENMASK(27, 26)
+#define CAL_CSI2_PHY_REG2_TRIGGER_CMD_RXTRIGESC1_MASK GENMASK(29, 28)
+#define CAL_CSI2_PHY_REG2_TRIGGER_CMD_RXTRIGESC0_MASK GENMASK(31, 30)
+
+#define CM_CAMERRX_CTRL_CSI1_CTRLCLKEN_MASK BIT_MASK(0)
+#define CM_CAMERRX_CTRL_CSI1_CAMMODE_MASK GENMASK(2, 1)
+#define CM_CAMERRX_CTRL_CSI1_LANEENABLE_MASK GENMASK(4, 3)
+#define CM_CAMERRX_CTRL_CSI1_MODE_MASK BIT_MASK(5)
+#define CM_CAMERRX_CTRL_CSI0_CTRLCLKEN_MASK BIT_MASK(10)
+#define CM_CAMERRX_CTRL_CSI0_CAMMODE_MASK GENMASK(12, 11)
+#define CM_CAMERRX_CTRL_CSI0_LANEENABLE_MASK GENMASK(16, 13)
+#define CM_CAMERRX_CTRL_CSI0_MODE_MASK BIT_MASK(17)
+
+#endif
+++ /dev/null
-/*
- * timblogiw.c timberdale FPGA LogiWin Video In driver
- * Copyright (c) 2009-2010 Intel Corporation
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-/* Supports:
- * Timberdale FPGA LogiWin Video In
- */
-
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/dmaengine.h>
-#include <linux/scatterlist.h>
-#include <linux/interrupt.h>
-#include <linux/list.h>
-#include <linux/i2c.h>
-#include <linux/module.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-device.h>
-#include <media/videobuf-dma-contig.h>
-#include <linux/platform_data/media/timb_video.h>
-
-#define DRIVER_NAME "timb-video"
-
-#define TIMBLOGIWIN_NAME "Timberdale Video-In"
-#define TIMBLOGIW_VERSION_CODE 0x04
-
-#define TIMBLOGIW_LINES_PER_DESC 44
-#define TIMBLOGIW_MAX_VIDEO_MEM 16
-
-#define TIMBLOGIW_HAS_DECODER(lw) (lw->pdata.encoder.module_name)
-
-
-struct timblogiw {
- struct video_device video_dev;
- struct v4l2_device v4l2_dev; /* mutual exclusion */
- struct mutex lock;
- struct device *dev;
- struct timb_video_platform_data pdata;
- struct v4l2_subdev *sd_enc; /* encoder */
- bool opened;
-};
-
-struct timblogiw_tvnorm {
- v4l2_std_id std;
- u16 width;
- u16 height;
- u8 fps;
-};
-
-struct timblogiw_fh {
- struct videobuf_queue vb_vidq;
- struct timblogiw_tvnorm const *cur_norm;
- struct list_head capture;
- struct dma_chan *chan;
- spinlock_t queue_lock; /* mutual exclusion */
- unsigned int frame_count;
-};
-
-struct timblogiw_buffer {
- /* common v4l buffer stuff -- must be first */
- struct videobuf_buffer vb;
- struct scatterlist sg[16];
- dma_cookie_t cookie;
- struct timblogiw_fh *fh;
-};
-
-static const struct timblogiw_tvnorm timblogiw_tvnorms[] = {
- {
- .std = V4L2_STD_PAL,
- .width = 720,
- .height = 576,
- .fps = 25
- },
- {
- .std = V4L2_STD_NTSC,
- .width = 720,
- .height = 480,
- .fps = 30
- }
-};
-
-static int timblogiw_bytes_per_line(const struct timblogiw_tvnorm *norm)
-{
- return norm->width * 2;
-}
-
-
-static int timblogiw_frame_size(const struct timblogiw_tvnorm *norm)
-{
- return norm->height * timblogiw_bytes_per_line(norm);
-}
-
-static const struct timblogiw_tvnorm *timblogiw_get_norm(const v4l2_std_id std)
-{
- int i;
- for (i = 0; i < ARRAY_SIZE(timblogiw_tvnorms); i++)
- if (timblogiw_tvnorms[i].std & std)
- return timblogiw_tvnorms + i;
-
- /* default to first element */
- return timblogiw_tvnorms;
-}
-
-static void timblogiw_dma_cb(void *data)
-{
- struct timblogiw_buffer *buf = data;
- struct timblogiw_fh *fh = buf->fh;
- struct videobuf_buffer *vb = &buf->vb;
-
- spin_lock(&fh->queue_lock);
-
- /* mark the transfer done */
- buf->cookie = -1;
-
- fh->frame_count++;
-
- if (vb->state != VIDEOBUF_ERROR) {
- list_del(&vb->queue);
- v4l2_get_timestamp(&vb->ts);
- vb->field_count = fh->frame_count * 2;
- vb->state = VIDEOBUF_DONE;
-
- wake_up(&vb->done);
- }
-
- if (!list_empty(&fh->capture)) {
- vb = list_entry(fh->capture.next, struct videobuf_buffer,
- queue);
- vb->state = VIDEOBUF_ACTIVE;
- }
-
- spin_unlock(&fh->queue_lock);
-}
-
-static bool timblogiw_dma_filter_fn(struct dma_chan *chan, void *filter_param)
-{
- return chan->chan_id == (uintptr_t)filter_param;
-}
-
-/* IOCTL functions */
-
-static int timblogiw_g_fmt(struct file *file, void *priv,
- struct v4l2_format *format)
-{
- struct video_device *vdev = video_devdata(file);
- struct timblogiw *lw = video_get_drvdata(vdev);
- struct timblogiw_fh *fh = priv;
-
- dev_dbg(&vdev->dev, "%s entry\n", __func__);
-
- if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- mutex_lock(&lw->lock);
-
- format->fmt.pix.width = fh->cur_norm->width;
- format->fmt.pix.height = fh->cur_norm->height;
- format->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
- format->fmt.pix.bytesperline = timblogiw_bytes_per_line(fh->cur_norm);
- format->fmt.pix.sizeimage = timblogiw_frame_size(fh->cur_norm);
- format->fmt.pix.field = V4L2_FIELD_NONE;
-
- mutex_unlock(&lw->lock);
-
- return 0;
-}
-
-static int timblogiw_try_fmt(struct file *file, void *priv,
- struct v4l2_format *format)
-{
- struct video_device *vdev = video_devdata(file);
- struct v4l2_pix_format *pix = &format->fmt.pix;
-
- dev_dbg(&vdev->dev,
- "%s - width=%d, height=%d, pixelformat=%d, field=%d\n"
- "bytes per line %d, size image: %d, colorspace: %d\n",
- __func__,
- pix->width, pix->height, pix->pixelformat, pix->field,
- pix->bytesperline, pix->sizeimage, pix->colorspace);
-
- if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- if (pix->field != V4L2_FIELD_NONE)
- return -EINVAL;
-
- if (pix->pixelformat != V4L2_PIX_FMT_UYVY)
- return -EINVAL;
-
- return 0;
-}
-
-static int timblogiw_s_fmt(struct file *file, void *priv,
- struct v4l2_format *format)
-{
- struct video_device *vdev = video_devdata(file);
- struct timblogiw *lw = video_get_drvdata(vdev);
- struct timblogiw_fh *fh = priv;
- struct v4l2_pix_format *pix = &format->fmt.pix;
- int err;
-
- mutex_lock(&lw->lock);
-
- err = timblogiw_try_fmt(file, priv, format);
- if (err)
- goto out;
-
- if (videobuf_queue_is_busy(&fh->vb_vidq)) {
- dev_err(&vdev->dev, "%s queue busy\n", __func__);
- err = -EBUSY;
- goto out;
- }
-
- pix->width = fh->cur_norm->width;
- pix->height = fh->cur_norm->height;
-
-out:
- mutex_unlock(&lw->lock);
- return err;
-}
-
-static int timblogiw_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct video_device *vdev = video_devdata(file);
-
- dev_dbg(&vdev->dev, "%s: Entry\n", __func__);
- strncpy(cap->card, TIMBLOGIWIN_NAME, sizeof(cap->card)-1);
- strncpy(cap->driver, DRIVER_NAME, sizeof(cap->driver) - 1);
- snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", vdev->name);
- cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
- V4L2_CAP_READWRITE;
- cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
-
- return 0;
-}
-
-static int timblogiw_enum_fmt(struct file *file, void *priv,
- struct v4l2_fmtdesc *fmt)
-{
- struct video_device *vdev = video_devdata(file);
-
- dev_dbg(&vdev->dev, "%s, index: %d\n", __func__, fmt->index);
-
- if (fmt->index != 0)
- return -EINVAL;
- memset(fmt, 0, sizeof(*fmt));
- fmt->index = 0;
- fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- strncpy(fmt->description, "4:2:2, packed, YUYV",
- sizeof(fmt->description)-1);
- fmt->pixelformat = V4L2_PIX_FMT_UYVY;
-
- return 0;
-}
-
-static int timblogiw_g_parm(struct file *file, void *priv,
- struct v4l2_streamparm *sp)
-{
- struct timblogiw_fh *fh = priv;
- struct v4l2_captureparm *cp = &sp->parm.capture;
-
- cp->capability = V4L2_CAP_TIMEPERFRAME;
- cp->timeperframe.numerator = 1;
- cp->timeperframe.denominator = fh->cur_norm->fps;
-
- return 0;
-}
-
-static int timblogiw_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *rb)
-{
- struct video_device *vdev = video_devdata(file);
- struct timblogiw_fh *fh = priv;
-
- dev_dbg(&vdev->dev, "%s: entry\n", __func__);
-
- return videobuf_reqbufs(&fh->vb_vidq, rb);
-}
-
-static int timblogiw_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *b)
-{
- struct video_device *vdev = video_devdata(file);
- struct timblogiw_fh *fh = priv;
-
- dev_dbg(&vdev->dev, "%s: entry\n", __func__);
-
- return videobuf_querybuf(&fh->vb_vidq, b);
-}
-
-static int timblogiw_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
-{
- struct video_device *vdev = video_devdata(file);
- struct timblogiw_fh *fh = priv;
-
- dev_dbg(&vdev->dev, "%s: entry\n", __func__);
-
- return videobuf_qbuf(&fh->vb_vidq, b);
-}
-
-static int timblogiw_dqbuf(struct file *file, void *priv,
- struct v4l2_buffer *b)
-{
- struct video_device *vdev = video_devdata(file);
- struct timblogiw_fh *fh = priv;
-
- dev_dbg(&vdev->dev, "%s: entry\n", __func__);
-
- return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK);
-}
-
-static int timblogiw_g_std(struct file *file, void *priv, v4l2_std_id *std)
-{
- struct video_device *vdev = video_devdata(file);
- struct timblogiw_fh *fh = priv;
-
- dev_dbg(&vdev->dev, "%s: entry\n", __func__);
-
- *std = fh->cur_norm->std;
- return 0;
-}
-
-static int timblogiw_s_std(struct file *file, void *priv, v4l2_std_id std)
-{
- struct video_device *vdev = video_devdata(file);
- struct timblogiw *lw = video_get_drvdata(vdev);
- struct timblogiw_fh *fh = priv;
- int err = 0;
-
- dev_dbg(&vdev->dev, "%s: entry\n", __func__);
-
- mutex_lock(&lw->lock);
-
- if (TIMBLOGIW_HAS_DECODER(lw))
- err = v4l2_subdev_call(lw->sd_enc, video, s_std, std);
-
- if (!err)
- fh->cur_norm = timblogiw_get_norm(std);
-
- mutex_unlock(&lw->lock);
-
- return err;
-}
-
-static int timblogiw_enuminput(struct file *file, void *priv,
- struct v4l2_input *inp)
-{
- struct video_device *vdev = video_devdata(file);
- int i;
-
- dev_dbg(&vdev->dev, "%s: Entry\n", __func__);
-
- if (inp->index != 0)
- return -EINVAL;
-
- inp->index = 0;
-
- strncpy(inp->name, "Timb input 1", sizeof(inp->name) - 1);
- inp->type = V4L2_INPUT_TYPE_CAMERA;
-
- inp->std = 0;
- for (i = 0; i < ARRAY_SIZE(timblogiw_tvnorms); i++)
- inp->std |= timblogiw_tvnorms[i].std;
-
- return 0;
-}
-
-static int timblogiw_g_input(struct file *file, void *priv,
- unsigned int *input)
-{
- struct video_device *vdev = video_devdata(file);
-
- dev_dbg(&vdev->dev, "%s: Entry\n", __func__);
-
- *input = 0;
-
- return 0;
-}
-
-static int timblogiw_s_input(struct file *file, void *priv, unsigned int input)
-{
- struct video_device *vdev = video_devdata(file);
-
- dev_dbg(&vdev->dev, "%s: Entry\n", __func__);
-
- if (input != 0)
- return -EINVAL;
- return 0;
-}
-
-static int timblogiw_streamon(struct file *file, void *priv, enum v4l2_buf_type type)
-{
- struct video_device *vdev = video_devdata(file);
- struct timblogiw_fh *fh = priv;
-
- dev_dbg(&vdev->dev, "%s: entry\n", __func__);
-
- if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- dev_dbg(&vdev->dev, "%s - No capture device\n", __func__);
- return -EINVAL;
- }
-
- fh->frame_count = 0;
- return videobuf_streamon(&fh->vb_vidq);
-}
-
-static int timblogiw_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct video_device *vdev = video_devdata(file);
- struct timblogiw_fh *fh = priv;
-
- dev_dbg(&vdev->dev, "%s entry\n", __func__);
-
- if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- return videobuf_streamoff(&fh->vb_vidq);
-}
-
-static int timblogiw_querystd(struct file *file, void *priv, v4l2_std_id *std)
-{
- struct video_device *vdev = video_devdata(file);
- struct timblogiw *lw = video_get_drvdata(vdev);
- struct timblogiw_fh *fh = priv;
-
- dev_dbg(&vdev->dev, "%s entry\n", __func__);
-
- if (TIMBLOGIW_HAS_DECODER(lw))
- return v4l2_subdev_call(lw->sd_enc, video, querystd, std);
- else {
- *std = fh->cur_norm->std;
- return 0;
- }
-}
-
-static int timblogiw_enum_framesizes(struct file *file, void *priv,
- struct v4l2_frmsizeenum *fsize)
-{
- struct video_device *vdev = video_devdata(file);
- struct timblogiw_fh *fh = priv;
-
- dev_dbg(&vdev->dev, "%s - index: %d, format: %d\n", __func__,
- fsize->index, fsize->pixel_format);
-
- if ((fsize->index != 0) ||
- (fsize->pixel_format != V4L2_PIX_FMT_UYVY))
- return -EINVAL;
-
- fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
- fsize->discrete.width = fh->cur_norm->width;
- fsize->discrete.height = fh->cur_norm->height;
-
- return 0;
-}
-
-/* Video buffer functions */
-
-static int buffer_setup(struct videobuf_queue *vq, unsigned int *count,
- unsigned int *size)
-{
- struct timblogiw_fh *fh = vq->priv_data;
-
- *size = timblogiw_frame_size(fh->cur_norm);
-
- if (!*count)
- *count = 32;
-
- while (*size * *count > TIMBLOGIW_MAX_VIDEO_MEM * 1024 * 1024)
- (*count)--;
-
- return 0;
-}
-
-static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
- enum v4l2_field field)
-{
- struct timblogiw_fh *fh = vq->priv_data;
- struct timblogiw_buffer *buf = container_of(vb, struct timblogiw_buffer,
- vb);
- unsigned int data_size = timblogiw_frame_size(fh->cur_norm);
- int err = 0;
-
- if (vb->baddr && vb->bsize < data_size)
- /* User provided buffer, but it is too small */
- return -ENOMEM;
-
- vb->size = data_size;
- vb->width = fh->cur_norm->width;
- vb->height = fh->cur_norm->height;
- vb->field = field;
-
- if (vb->state == VIDEOBUF_NEEDS_INIT) {
- int i;
- unsigned int size;
- unsigned int bytes_per_desc = TIMBLOGIW_LINES_PER_DESC *
- timblogiw_bytes_per_line(fh->cur_norm);
- dma_addr_t addr;
-
- sg_init_table(buf->sg, ARRAY_SIZE(buf->sg));
-
- err = videobuf_iolock(vq, vb, NULL);
- if (err)
- goto err;
-
- addr = videobuf_to_dma_contig(vb);
- for (i = 0, size = 0; size < data_size; i++) {
- sg_dma_address(buf->sg + i) = addr + size;
- size += bytes_per_desc;
- sg_dma_len(buf->sg + i) = (size > data_size) ?
- (bytes_per_desc - (size - data_size)) :
- bytes_per_desc;
- }
-
- vb->state = VIDEOBUF_PREPARED;
- buf->cookie = -1;
- buf->fh = fh;
- }
-
- return 0;
-
-err:
- videobuf_dma_contig_free(vq, vb);
- vb->state = VIDEOBUF_NEEDS_INIT;
- return err;
-}
-
-static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
-{
- struct timblogiw_fh *fh = vq->priv_data;
- struct timblogiw_buffer *buf = container_of(vb, struct timblogiw_buffer,
- vb);
- struct dma_async_tx_descriptor *desc;
- int sg_elems;
- int bytes_per_desc = TIMBLOGIW_LINES_PER_DESC *
- timblogiw_bytes_per_line(fh->cur_norm);
-
- sg_elems = timblogiw_frame_size(fh->cur_norm) / bytes_per_desc;
- sg_elems +=
- (timblogiw_frame_size(fh->cur_norm) % bytes_per_desc) ? 1 : 0;
-
- if (list_empty(&fh->capture))
- vb->state = VIDEOBUF_ACTIVE;
- else
- vb->state = VIDEOBUF_QUEUED;
-
- list_add_tail(&vb->queue, &fh->capture);
-
- spin_unlock_irq(&fh->queue_lock);
-
- desc = dmaengine_prep_slave_sg(fh->chan,
- buf->sg, sg_elems, DMA_DEV_TO_MEM,
- DMA_PREP_INTERRUPT);
- if (!desc) {
- spin_lock_irq(&fh->queue_lock);
- list_del_init(&vb->queue);
- vb->state = VIDEOBUF_PREPARED;
- return;
- }
-
- desc->callback_param = buf;
- desc->callback = timblogiw_dma_cb;
-
- buf->cookie = desc->tx_submit(desc);
-
- spin_lock_irq(&fh->queue_lock);
-}
-
-static void buffer_release(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
-{
- struct timblogiw_fh *fh = vq->priv_data;
- struct timblogiw_buffer *buf = container_of(vb, struct timblogiw_buffer,
- vb);
-
- videobuf_waiton(vq, vb, 0, 0);
- if (buf->cookie >= 0)
- dma_sync_wait(fh->chan, buf->cookie);
-
- videobuf_dma_contig_free(vq, vb);
- vb->state = VIDEOBUF_NEEDS_INIT;
-}
-
-static struct videobuf_queue_ops timblogiw_video_qops = {
- .buf_setup = buffer_setup,
- .buf_prepare = buffer_prepare,
- .buf_queue = buffer_queue,
- .buf_release = buffer_release,
-};
-
-/* Device Operations functions */
-
-static int timblogiw_open(struct file *file)
-{
- struct video_device *vdev = video_devdata(file);
- struct timblogiw *lw = video_get_drvdata(vdev);
- struct timblogiw_fh *fh;
- v4l2_std_id std;
- dma_cap_mask_t mask;
- int err = 0;
-
- dev_dbg(&vdev->dev, "%s: entry\n", __func__);
-
- mutex_lock(&lw->lock);
- if (lw->opened) {
- err = -EBUSY;
- goto out;
- }
-
- if (TIMBLOGIW_HAS_DECODER(lw) && !lw->sd_enc) {
- struct i2c_adapter *adapt;
-
- /* find the video decoder */
- adapt = i2c_get_adapter(lw->pdata.i2c_adapter);
- if (!adapt) {
- dev_err(&vdev->dev, "No I2C bus #%d\n",
- lw->pdata.i2c_adapter);
- err = -ENODEV;
- goto out;
- }
-
- /* now find the encoder */
- lw->sd_enc = v4l2_i2c_new_subdev_board(&lw->v4l2_dev, adapt,
- lw->pdata.encoder.info, NULL);
-
- i2c_put_adapter(adapt);
-
- if (!lw->sd_enc) {
- dev_err(&vdev->dev, "Failed to get encoder: %s\n",
- lw->pdata.encoder.module_name);
- err = -ENODEV;
- goto out;
- }
- }
-
- fh = kzalloc(sizeof(*fh), GFP_KERNEL);
- if (!fh) {
- err = -ENOMEM;
- goto out;
- }
-
- fh->cur_norm = timblogiw_tvnorms;
- timblogiw_querystd(file, fh, &std);
- fh->cur_norm = timblogiw_get_norm(std);
-
- INIT_LIST_HEAD(&fh->capture);
- spin_lock_init(&fh->queue_lock);
-
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
- dma_cap_set(DMA_PRIVATE, mask);
-
- /* find the DMA channel */
- fh->chan = dma_request_channel(mask, timblogiw_dma_filter_fn,
- (void *)(uintptr_t)lw->pdata.dma_channel);
- if (!fh->chan) {
- dev_err(&vdev->dev, "Failed to get DMA channel\n");
- kfree(fh);
- err = -ENODEV;
- goto out;
- }
-
- file->private_data = fh;
- videobuf_queue_dma_contig_init(&fh->vb_vidq,
- &timblogiw_video_qops, lw->dev, &fh->queue_lock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
- sizeof(struct timblogiw_buffer), fh, NULL);
-
- lw->opened = true;
-out:
- mutex_unlock(&lw->lock);
-
- return err;
-}
-
-static int timblogiw_close(struct file *file)
-{
- struct video_device *vdev = video_devdata(file);
- struct timblogiw *lw = video_get_drvdata(vdev);
- struct timblogiw_fh *fh = file->private_data;
-
- dev_dbg(&vdev->dev, "%s: Entry\n", __func__);
-
- videobuf_stop(&fh->vb_vidq);
- videobuf_mmap_free(&fh->vb_vidq);
-
- dma_release_channel(fh->chan);
-
- kfree(fh);
-
- mutex_lock(&lw->lock);
- lw->opened = false;
- mutex_unlock(&lw->lock);
- return 0;
-}
-
-static ssize_t timblogiw_read(struct file *file, char __user *data,
- size_t count, loff_t *ppos)
-{
- struct video_device *vdev = video_devdata(file);
- struct timblogiw_fh *fh = file->private_data;
-
- dev_dbg(&vdev->dev, "%s: entry\n", __func__);
-
- return videobuf_read_stream(&fh->vb_vidq, data, count, ppos, 0,
- file->f_flags & O_NONBLOCK);
-}
-
-static unsigned int timblogiw_poll(struct file *file,
- struct poll_table_struct *wait)
-{
- struct video_device *vdev = video_devdata(file);
- struct timblogiw_fh *fh = file->private_data;
-
- dev_dbg(&vdev->dev, "%s: entry\n", __func__);
-
- return videobuf_poll_stream(file, &fh->vb_vidq, wait);
-}
-
-static int timblogiw_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct video_device *vdev = video_devdata(file);
- struct timblogiw_fh *fh = file->private_data;
-
- dev_dbg(&vdev->dev, "%s: entry\n", __func__);
-
- return videobuf_mmap_mapper(&fh->vb_vidq, vma);
-}
-
-/* Platform device functions */
-
-static struct v4l2_ioctl_ops timblogiw_ioctl_ops = {
- .vidioc_querycap = timblogiw_querycap,
- .vidioc_enum_fmt_vid_cap = timblogiw_enum_fmt,
- .vidioc_g_fmt_vid_cap = timblogiw_g_fmt,
- .vidioc_try_fmt_vid_cap = timblogiw_try_fmt,
- .vidioc_s_fmt_vid_cap = timblogiw_s_fmt,
- .vidioc_g_parm = timblogiw_g_parm,
- .vidioc_reqbufs = timblogiw_reqbufs,
- .vidioc_querybuf = timblogiw_querybuf,
- .vidioc_qbuf = timblogiw_qbuf,
- .vidioc_dqbuf = timblogiw_dqbuf,
- .vidioc_g_std = timblogiw_g_std,
- .vidioc_s_std = timblogiw_s_std,
- .vidioc_enum_input = timblogiw_enuminput,
- .vidioc_g_input = timblogiw_g_input,
- .vidioc_s_input = timblogiw_s_input,
- .vidioc_streamon = timblogiw_streamon,
- .vidioc_streamoff = timblogiw_streamoff,
- .vidioc_querystd = timblogiw_querystd,
- .vidioc_enum_framesizes = timblogiw_enum_framesizes,
-};
-
-static struct v4l2_file_operations timblogiw_fops = {
- .owner = THIS_MODULE,
- .open = timblogiw_open,
- .release = timblogiw_close,
- .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
- .mmap = timblogiw_mmap,
- .read = timblogiw_read,
- .poll = timblogiw_poll,
-};
-
-static struct video_device timblogiw_template = {
- .name = TIMBLOGIWIN_NAME,
- .fops = &timblogiw_fops,
- .ioctl_ops = &timblogiw_ioctl_ops,
- .release = video_device_release_empty,
- .minor = -1,
- .tvnorms = V4L2_STD_PAL | V4L2_STD_NTSC
-};
-
-static int timblogiw_probe(struct platform_device *pdev)
-{
- int err;
- struct timblogiw *lw = NULL;
- struct timb_video_platform_data *pdata = pdev->dev.platform_data;
-
- if (!pdata) {
- dev_err(&pdev->dev, "No platform data\n");
- err = -EINVAL;
- goto err;
- }
-
- if (!pdata->encoder.module_name)
- dev_info(&pdev->dev, "Running without decoder\n");
-
- lw = devm_kzalloc(&pdev->dev, sizeof(*lw), GFP_KERNEL);
- if (!lw) {
- err = -ENOMEM;
- goto err;
- }
-
- if (pdev->dev.parent)
- lw->dev = pdev->dev.parent;
- else
- lw->dev = &pdev->dev;
-
- memcpy(&lw->pdata, pdata, sizeof(lw->pdata));
-
- mutex_init(&lw->lock);
-
- lw->video_dev = timblogiw_template;
-
- strlcpy(lw->v4l2_dev.name, DRIVER_NAME, sizeof(lw->v4l2_dev.name));
- err = v4l2_device_register(NULL, &lw->v4l2_dev);
- if (err)
- goto err;
-
- lw->video_dev.v4l2_dev = &lw->v4l2_dev;
-
- platform_set_drvdata(pdev, lw);
- video_set_drvdata(&lw->video_dev, lw);
-
- err = video_register_device(&lw->video_dev, VFL_TYPE_GRABBER, 0);
- if (err) {
- dev_err(&pdev->dev, "Error reg video: %d\n", err);
- goto err_request;
- }
-
- return 0;
-
-err_request:
- v4l2_device_unregister(&lw->v4l2_dev);
-err:
- dev_err(&pdev->dev, "Failed to register: %d\n", err);
-
- return err;
-}
-
-static int timblogiw_remove(struct platform_device *pdev)
-{
- struct timblogiw *lw = platform_get_drvdata(pdev);
-
- video_unregister_device(&lw->video_dev);
-
- v4l2_device_unregister(&lw->v4l2_dev);
-
- return 0;
-}
-
-static struct platform_driver timblogiw_platform_driver = {
- .driver = {
- .name = DRIVER_NAME,
- },
- .probe = timblogiw_probe,
- .remove = timblogiw_remove,
-};
-
-module_platform_driver(timblogiw_platform_driver);
-
-MODULE_DESCRIPTION(TIMBLOGIWIN_NAME);
-MODULE_AUTHOR("Pelagicore AB <info@pelagicore.com>");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:"DRIVER_NAME);
if (ret)
platform_device_unregister(&vim2m_pdev);
- return 0;
+ return ret;
}
module_init(vim2m_init);
/* Release pseudo palette */
kfree(dev->fb_info.pseudo_palette);
- kfree((void *)dev->video_vbase);
+ kfree(dev->video_vbase);
}
/* Initialize the specified card */
tpg->planes = 3;
tpg->is_yuv = true;
break;
+ case V4L2_PIX_FMT_YUV422M:
+ case V4L2_PIX_FMT_YVU422M:
+ tpg->buffers = 3;
+ /* fall through */
case V4L2_PIX_FMT_YUV422P:
tpg->vdownsampling[1] = 1;
tpg->vdownsampling[2] = 1;
tpg->planes = 2;
tpg->is_yuv = true;
break;
+ case V4L2_PIX_FMT_YUV444M:
+ case V4L2_PIX_FMT_YVU444M:
+ tpg->buffers = 3;
+ tpg->planes = 3;
+ tpg->vdownsampling[1] = 1;
+ tpg->vdownsampling[2] = 1;
+ tpg->hdownsampling[1] = 1;
+ tpg->hdownsampling[2] = 1;
+ tpg->is_yuv = true;
+ break;
case V4L2_PIX_FMT_NV24:
case V4L2_PIX_FMT_NV42:
tpg->vdownsampling[1] = 1;
tpg->twopixelsize[0] = 4;
tpg->twopixelsize[1] = 4;
break;
+ case V4L2_PIX_FMT_YUV444M:
+ case V4L2_PIX_FMT_YVU444M:
+ case V4L2_PIX_FMT_YUV422M:
+ case V4L2_PIX_FMT_YVU422M:
case V4L2_PIX_FMT_YUV422P:
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YVU420:
buf[0][offset] = r_y;
buf[0][offset+1] = r_y == 0xff ? r_y : 0;
break;
+ case V4L2_PIX_FMT_YUV422M:
case V4L2_PIX_FMT_YUV422P:
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YUV420M:
buf[1][0] = g_u;
buf[2][0] = b_v;
break;
+ case V4L2_PIX_FMT_YVU422M:
case V4L2_PIX_FMT_YVU420:
case V4L2_PIX_FMT_YVU420M:
buf[0][offset] = r_y;
buf[1][1] = g_u;
break;
+ case V4L2_PIX_FMT_YUV444M:
+ buf[0][offset] = r_y;
+ buf[1][offset] = g_u;
+ buf[2][offset] = b_v;
+ break;
+
+ case V4L2_PIX_FMT_YVU444M:
+ buf[0][offset] = r_y;
+ buf[1][offset] = b_v;
+ buf[2][offset] = g_u;
+ break;
+
case V4L2_PIX_FMT_NV24:
buf[0][offset] = r_y;
buf[1][2 * offset] = g_u;
tpg->bytesperline[p] = plane_w / tpg->hdownsampling[p];
}
+ if (tpg_g_interleaved(tpg))
+ tpg->bytesperline[1] = tpg->bytesperline[0];
}
.planes = 1,
.buffers = 1,
},
+
+ /* Multiplanar formats */
+
{
.fourcc = V4L2_PIX_FMT_NV16M,
.vdownsampling = { 1, 1 },
.planes = 2,
.buffers = 2,
},
+ {
+ .fourcc = V4L2_PIX_FMT_YUV422M,
+ .vdownsampling = { 1, 1, 1 },
+ .bit_depth = { 8, 4, 4 },
+ .is_yuv = true,
+ .planes = 3,
+ .buffers = 3,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YVU422M,
+ .vdownsampling = { 1, 1, 1 },
+ .bit_depth = { 8, 4, 4 },
+ .is_yuv = true,
+ .planes = 3,
+ .buffers = 3,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YUV444M,
+ .vdownsampling = { 1, 1, 1 },
+ .bit_depth = { 8, 8, 8 },
+ .is_yuv = true,
+ .planes = 3,
+ .buffers = 3,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YVU444M,
+ .vdownsampling = { 1, 1, 1 },
+ .bit_depth = { 8, 8, 8 },
+ .is_yuv = true,
+ .planes = 3,
+ .buffers = 3,
+ },
};
-/* There are 6 multiplanar formats in the list */
-#define VIVID_MPLANAR_FORMATS 6
+/* There are this many multiplanar formats in the list */
+#define VIVID_MPLANAR_FORMATS 10
const struct vivid_fmt *vivid_get_format(struct vivid_dev *dev, u32 pixelformat)
{
-vsp1-y := vsp1_drv.o vsp1_entity.o vsp1_video.o
+vsp1-y := vsp1_drv.o vsp1_entity.o vsp1_pipe.o
+vsp1-y += vsp1_dl.o vsp1_drm.o vsp1_video.o
vsp1-y += vsp1_rpf.o vsp1_rwpf.o vsp1_wpf.o
vsp1-y += vsp1_hsit.o vsp1_lif.o vsp1_lut.o
vsp1-y += vsp1_bru.o vsp1_sru.o vsp1_uds.o
struct clk;
struct device;
+struct vsp1_dl;
+struct vsp1_drm;
+struct vsp1_entity;
struct vsp1_platform_data;
struct vsp1_bru;
struct vsp1_hsit;
#define VSP1_HAS_LIF (1 << 0)
#define VSP1_HAS_LUT (1 << 1)
#define VSP1_HAS_SRU (1 << 2)
+#define VSP1_HAS_BRU (1 << 3)
-struct vsp1_platform_data {
+struct vsp1_device_info {
+ u32 version;
unsigned int features;
unsigned int rpf_count;
unsigned int uds_count;
unsigned int wpf_count;
+ unsigned int num_bru_inputs;
+ bool uapi;
};
struct vsp1_device {
struct device *dev;
- struct vsp1_platform_data pdata;
+ const struct vsp1_device_info *info;
void __iomem *mmio;
struct clk *clock;
struct vsp1_rwpf *wpf[VSP1_MAX_WPF];
struct list_head entities;
+ struct list_head videos;
struct v4l2_device v4l2_dev;
struct media_device media_dev;
+ struct media_entity_operations media_ops;
+
+ struct vsp1_drm *drm;
+
+ bool use_dl;
};
int vsp1_device_get(struct vsp1_device *vsp1);
void vsp1_device_put(struct vsp1_device *vsp1);
+int vsp1_reset_wpf(struct vsp1_device *vsp1, unsigned int index);
+
static inline u32 vsp1_read(struct vsp1_device *vsp1, u32 reg)
{
return ioread32(vsp1->mmio + reg);
iowrite32(data, vsp1->mmio + reg);
}
+#include "vsp1_dl.h"
+
+static inline void vsp1_mod_write(struct vsp1_entity *e, u32 reg, u32 data)
+{
+ if (e->vsp1->use_dl)
+ vsp1_dl_add(e, reg, data);
+ else
+ vsp1_write(e->vsp1, reg, data);
+}
+
#endif /* __VSP1_H__ */
#include "vsp1.h"
#include "vsp1_bru.h"
#include "vsp1_rwpf.h"
+#include "vsp1_video.h"
#define BRU_MIN_SIZE 1U
#define BRU_MAX_SIZE 8190U
* Device Access
*/
-static inline u32 vsp1_bru_read(struct vsp1_bru *bru, u32 reg)
-{
- return vsp1_read(bru->entity.vsp1, reg);
-}
-
static inline void vsp1_bru_write(struct vsp1_bru *bru, u32 reg, u32 data)
{
- vsp1_write(bru->entity.vsp1, reg, data);
+ vsp1_mod_write(&bru->entity, reg, data);
}
/* -----------------------------------------------------------------------------
if (!enable)
return 0;
- format = &bru->entity.formats[BRU_PAD_SOURCE];
+ format = &bru->entity.formats[bru->entity.source_pad];
/* The hardware is extremely flexible but we have no userspace API to
* expose all the parameters, nor is it clear whether we would have use
/* Disable dithering and enable color data normalization unless the
* format at the pipeline output is premultiplied.
*/
- flags = pipe->output ? pipe->output->video.format.flags : 0;
+ flags = pipe->output ? pipe->output->format.flags : 0;
vsp1_bru_write(bru, VI6_BRU_INCTRL,
flags & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA ?
0 : VI6_BRU_INCTRL_NRM);
VI6_BRU_ROP_CROP(VI6_ROP_NOP) |
VI6_BRU_ROP_AROP(VI6_ROP_NOP));
- for (i = 0; i < 4; ++i) {
+ for (i = 0; i < bru->entity.source_pad; ++i) {
bool premultiplied = false;
u32 ctrl = 0;
if (bru->inputs[i].rpf) {
ctrl |= VI6_BRU_CTRL_RBC;
- premultiplied = bru->inputs[i].rpf->video.format.flags
+ premultiplied = bru->inputs[i].rpf->format.flags
& V4L2_PIX_FMT_FLAG_PREMUL_ALPHA;
} else {
ctrl |= VI6_BRU_CTRL_CROP(VI6_ROP_NOP)
*format = fmt->format;
/* Reset the compose rectangle */
- if (fmt->pad != BRU_PAD_SOURCE) {
+ if (fmt->pad != bru->entity.source_pad) {
struct v4l2_rect *compose;
compose = bru_get_compose(bru, cfg, fmt->pad, fmt->which);
if (fmt->pad == BRU_PAD_SINK(0)) {
unsigned int i;
- for (i = 0; i <= BRU_PAD_SOURCE; ++i) {
+ for (i = 0; i <= bru->entity.source_pad; ++i) {
format = vsp1_entity_get_pad_format(&bru->entity, cfg,
i, fmt->which);
format->code = fmt->format.code;
{
struct vsp1_bru *bru = to_bru(subdev);
- if (sel->pad == BRU_PAD_SOURCE)
+ if (sel->pad == bru->entity.source_pad)
return -EINVAL;
switch (sel->target) {
struct v4l2_mbus_framefmt *format;
struct v4l2_rect *compose;
- if (sel->pad == BRU_PAD_SOURCE)
+ if (sel->pad == bru->entity.source_pad)
return -EINVAL;
if (sel->target != V4L2_SEL_TGT_COMPOSE)
/* The compose rectangle top left corner must be inside the output
* frame.
*/
- format = vsp1_entity_get_pad_format(&bru->entity, cfg, BRU_PAD_SOURCE,
- sel->which);
+ format = vsp1_entity_get_pad_format(&bru->entity, cfg,
+ bru->entity.source_pad, sel->which);
sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1);
sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1);
bru->entity.type = VSP1_ENTITY_BRU;
- ret = vsp1_entity_init(vsp1, &bru->entity, 5);
+ ret = vsp1_entity_init(vsp1, &bru->entity,
+ vsp1->info->num_bru_inputs + 1);
if (ret < 0)
return ERR_PTR(ret);
subdev = &bru->entity.subdev;
v4l2_subdev_init(subdev, &bru_ops);
- subdev->entity.ops = &vsp1_media_ops;
+ subdev->entity.ops = &vsp1->media_ops;
subdev->internal_ops = &vsp1_subdev_internal_ops;
snprintf(subdev->name, sizeof(subdev->name), "%s bru",
dev_name(vsp1->dev));
struct vsp1_rwpf;
#define BRU_PAD_SINK(n) (n)
-#define BRU_PAD_SOURCE 4
struct vsp1_bru {
struct vsp1_entity entity;
struct {
struct vsp1_rwpf *rpf;
struct v4l2_rect compose;
- } inputs[4];
+ } inputs[VSP1_MAX_RPF];
};
static inline struct vsp1_bru *to_bru(struct v4l2_subdev *subdev)
--- /dev/null
+/*
+ * vsp1_dl.h -- R-Car VSP1 Display List
+ *
+ * Copyright (C) 2015 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * 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.
+ */
+
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/gfp.h>
+#include <linux/slab.h>
+
+#include "vsp1.h"
+#include "vsp1_dl.h"
+#include "vsp1_pipe.h"
+
+/*
+ * Global resources
+ *
+ * - Display-related interrupts (can be used for vblank evasion ?)
+ * - Display-list enable
+ * - Header-less for WPF0
+ * - DL swap
+ */
+
+#define VSP1_DL_BODY_SIZE (2 * 4 * 256)
+#define VSP1_DL_NUM_LISTS 3
+
+struct vsp1_dl_entry {
+ u32 addr;
+ u32 data;
+} __attribute__((__packed__));
+
+struct vsp1_dl_list {
+ size_t size;
+ int reg_count;
+
+ bool in_use;
+
+ struct vsp1_dl_entry *body;
+ dma_addr_t dma;
+};
+
+/**
+ * struct vsp1_dl - Display List manager
+ * @vsp1: the VSP1 device
+ * @lock: protects the active, queued and pending lists
+ * @lists.all: array of all allocate display lists
+ * @lists.active: list currently being processed (loaded) by hardware
+ * @lists.queued: list queued to the hardware (written to the DL registers)
+ * @lists.pending: list waiting to be queued to the hardware
+ * @lists.write: list being written to by software
+ */
+struct vsp1_dl {
+ struct vsp1_device *vsp1;
+
+ spinlock_t lock;
+
+ size_t size;
+ dma_addr_t dma;
+ void *mem;
+
+ struct {
+ struct vsp1_dl_list all[VSP1_DL_NUM_LISTS];
+
+ struct vsp1_dl_list *active;
+ struct vsp1_dl_list *queued;
+ struct vsp1_dl_list *pending;
+ struct vsp1_dl_list *write;
+ } lists;
+};
+
+/* -----------------------------------------------------------------------------
+ * Display List Transaction Management
+ */
+
+static void vsp1_dl_free_list(struct vsp1_dl_list *list)
+{
+ if (!list)
+ return;
+
+ list->in_use = false;
+}
+
+void vsp1_dl_reset(struct vsp1_dl *dl)
+{
+ unsigned int i;
+
+ dl->lists.active = NULL;
+ dl->lists.queued = NULL;
+ dl->lists.pending = NULL;
+ dl->lists.write = NULL;
+
+ for (i = 0; i < ARRAY_SIZE(dl->lists.all); ++i)
+ dl->lists.all[i].in_use = false;
+}
+
+void vsp1_dl_begin(struct vsp1_dl *dl)
+{
+ struct vsp1_dl_list *list = NULL;
+ unsigned long flags;
+ unsigned int i;
+
+ spin_lock_irqsave(&dl->lock, flags);
+
+ for (i = 0; i < ARRAY_SIZE(dl->lists.all); ++i) {
+ if (!dl->lists.all[i].in_use) {
+ list = &dl->lists.all[i];
+ break;
+ }
+ }
+
+ if (!list) {
+ list = dl->lists.pending;
+ dl->lists.pending = NULL;
+ }
+
+ spin_unlock_irqrestore(&dl->lock, flags);
+
+ dl->lists.write = list;
+
+ list->in_use = true;
+ list->reg_count = 0;
+}
+
+void vsp1_dl_add(struct vsp1_entity *e, u32 reg, u32 data)
+{
+ struct vsp1_pipeline *pipe = to_vsp1_pipeline(&e->subdev.entity);
+ struct vsp1_dl *dl = pipe->dl;
+ struct vsp1_dl_list *list = dl->lists.write;
+
+ list->body[list->reg_count].addr = reg;
+ list->body[list->reg_count].data = data;
+ list->reg_count++;
+}
+
+void vsp1_dl_commit(struct vsp1_dl *dl)
+{
+ struct vsp1_device *vsp1 = dl->vsp1;
+ struct vsp1_dl_list *list;
+ unsigned long flags;
+ bool update;
+
+ list = dl->lists.write;
+ dl->lists.write = NULL;
+
+ spin_lock_irqsave(&dl->lock, flags);
+
+ /* Once the UPD bit has been set the hardware can start processing the
+ * display list at any time and we can't touch the address and size
+ * registers. In that case mark the update as pending, it will be
+ * queued up to the hardware by the frame end interrupt handler.
+ */
+ update = !!(vsp1_read(vsp1, VI6_DL_BODY_SIZE) & VI6_DL_BODY_SIZE_UPD);
+ if (update) {
+ vsp1_dl_free_list(dl->lists.pending);
+ dl->lists.pending = list;
+ goto done;
+ }
+
+ /* Program the hardware with the display list body address and size.
+ * The UPD bit will be cleared by the device when the display list is
+ * processed.
+ */
+ vsp1_write(vsp1, VI6_DL_HDR_ADDR(0), list->dma);
+ vsp1_write(vsp1, VI6_DL_BODY_SIZE, VI6_DL_BODY_SIZE_UPD |
+ (list->reg_count * 8));
+
+ vsp1_dl_free_list(dl->lists.queued);
+ dl->lists.queued = list;
+
+done:
+ spin_unlock_irqrestore(&dl->lock, flags);
+}
+
+/* -----------------------------------------------------------------------------
+ * Interrupt Handling
+ */
+
+void vsp1_dl_irq_display_start(struct vsp1_dl *dl)
+{
+ spin_lock(&dl->lock);
+
+ /* The display start interrupt signals the end of the display list
+ * processing by the device. The active display list, if any, won't be
+ * accessed anymore and can be reused.
+ */
+ if (dl->lists.active) {
+ vsp1_dl_free_list(dl->lists.active);
+ dl->lists.active = NULL;
+ }
+
+ spin_unlock(&dl->lock);
+}
+
+void vsp1_dl_irq_frame_end(struct vsp1_dl *dl)
+{
+ struct vsp1_device *vsp1 = dl->vsp1;
+
+ spin_lock(&dl->lock);
+
+ /* The UPD bit set indicates that the commit operation raced with the
+ * interrupt and occurred after the frame end event and UPD clear but
+ * before interrupt processing. The hardware hasn't taken the update
+ * into account yet, we'll thus skip one frame and retry.
+ */
+ if (vsp1_read(vsp1, VI6_DL_BODY_SIZE) & VI6_DL_BODY_SIZE_UPD)
+ goto done;
+
+ /* The device starts processing the queued display list right after the
+ * frame end interrupt. The display list thus becomes active.
+ */
+ if (dl->lists.queued) {
+ WARN_ON(dl->lists.active);
+ dl->lists.active = dl->lists.queued;
+ dl->lists.queued = NULL;
+ }
+
+ /* Now that the UPD bit has been cleared we can queue the next display
+ * list to the hardware if one has been prepared.
+ */
+ if (dl->lists.pending) {
+ struct vsp1_dl_list *list = dl->lists.pending;
+
+ vsp1_write(vsp1, VI6_DL_HDR_ADDR(0), list->dma);
+ vsp1_write(vsp1, VI6_DL_BODY_SIZE, VI6_DL_BODY_SIZE_UPD |
+ (list->reg_count * 8));
+
+ dl->lists.queued = list;
+ dl->lists.pending = NULL;
+ }
+
+done:
+ spin_unlock(&dl->lock);
+}
+
+/* -----------------------------------------------------------------------------
+ * Hardware Setup
+ */
+
+void vsp1_dl_setup(struct vsp1_device *vsp1)
+{
+ u32 ctrl = (256 << VI6_DL_CTRL_AR_WAIT_SHIFT)
+ | VI6_DL_CTRL_DC2 | VI6_DL_CTRL_DC1 | VI6_DL_CTRL_DC0
+ | VI6_DL_CTRL_DLE;
+
+ /* The DRM pipeline operates with header-less display lists in
+ * Continuous Frame Mode.
+ */
+ if (vsp1->drm)
+ ctrl |= VI6_DL_CTRL_CFM0 | VI6_DL_CTRL_NH0;
+
+ vsp1_write(vsp1, VI6_DL_CTRL, ctrl);
+ vsp1_write(vsp1, VI6_DL_SWAP, VI6_DL_SWAP_LWS);
+}
+
+/* -----------------------------------------------------------------------------
+ * Initialization and Cleanup
+ */
+
+struct vsp1_dl *vsp1_dl_create(struct vsp1_device *vsp1)
+{
+ struct vsp1_dl *dl;
+ unsigned int i;
+
+ dl = kzalloc(sizeof(*dl), GFP_KERNEL);
+ if (!dl)
+ return NULL;
+
+ spin_lock_init(&dl->lock);
+
+ dl->vsp1 = vsp1;
+ dl->size = VSP1_DL_BODY_SIZE * ARRAY_SIZE(dl->lists.all);
+
+ dl->mem = dma_alloc_writecombine(vsp1->dev, dl->size, &dl->dma,
+ GFP_KERNEL);
+ if (!dl->mem) {
+ kfree(dl);
+ return NULL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(dl->lists.all); ++i) {
+ struct vsp1_dl_list *list = &dl->lists.all[i];
+
+ list->size = VSP1_DL_BODY_SIZE;
+ list->reg_count = 0;
+ list->in_use = false;
+ list->dma = dl->dma + VSP1_DL_BODY_SIZE * i;
+ list->body = dl->mem + VSP1_DL_BODY_SIZE * i;
+ }
+
+ return dl;
+}
+
+void vsp1_dl_destroy(struct vsp1_dl *dl)
+{
+ dma_free_writecombine(dl->vsp1->dev, dl->size, dl->mem, dl->dma);
+ kfree(dl);
+}
--- /dev/null
+/*
+ * vsp1_dl.h -- R-Car VSP1 Display List
+ *
+ * Copyright (C) 2015 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef __VSP1_DL_H__
+#define __VSP1_DL_H__
+
+#include "vsp1_entity.h"
+
+struct vsp1_device;
+struct vsp1_dl;
+
+struct vsp1_dl *vsp1_dl_create(struct vsp1_device *vsp1);
+void vsp1_dl_destroy(struct vsp1_dl *dl);
+
+void vsp1_dl_setup(struct vsp1_device *vsp1);
+
+void vsp1_dl_reset(struct vsp1_dl *dl);
+void vsp1_dl_begin(struct vsp1_dl *dl);
+void vsp1_dl_add(struct vsp1_entity *e, u32 reg, u32 data);
+void vsp1_dl_commit(struct vsp1_dl *dl);
+
+void vsp1_dl_irq_display_start(struct vsp1_dl *dl);
+void vsp1_dl_irq_frame_end(struct vsp1_dl *dl);
+
+static inline void vsp1_dl_mod_write(struct vsp1_entity *e, u32 reg, u32 data)
+{
+ if (e->vsp1->use_dl)
+ vsp1_dl_add(e, reg, data);
+ else
+ vsp1_write(e->vsp1, reg, data);
+}
+
+#endif /* __VSP1_DL_H__ */
--- /dev/null
+/*
+ * vsp1_drm.c -- R-Car VSP1 DRM API
+ *
+ * Copyright (C) 2015 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * 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.
+ */
+
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/vsp1.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-subdev.h>
+
+#include "vsp1.h"
+#include "vsp1_bru.h"
+#include "vsp1_dl.h"
+#include "vsp1_drm.h"
+#include "vsp1_lif.h"
+#include "vsp1_pipe.h"
+#include "vsp1_rwpf.h"
+
+/* -----------------------------------------------------------------------------
+ * Runtime Handling
+ */
+
+static void vsp1_drm_pipeline_frame_end(struct vsp1_pipeline *pipe)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&pipe->irqlock, flags);
+ if (pipe->num_inputs)
+ vsp1_pipeline_run(pipe);
+ spin_unlock_irqrestore(&pipe->irqlock, flags);
+}
+
+/* -----------------------------------------------------------------------------
+ * DU Driver API
+ */
+
+int vsp1_du_init(struct device *dev)
+{
+ struct vsp1_device *vsp1 = dev_get_drvdata(dev);
+
+ if (!vsp1)
+ return -EPROBE_DEFER;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vsp1_du_init);
+
+/**
+ * vsp1_du_setup_lif - Setup the output part of the VSP pipeline
+ * @dev: the VSP device
+ * @width: output frame width in pixels
+ * @height: output frame height in pixels
+ *
+ * Configure the output part of VSP DRM pipeline for the given frame @width and
+ * @height. This sets up formats on the BRU source pad, the WPF0 sink and source
+ * pads, and the LIF sink pad.
+ *
+ * As the media bus code on the BRU source pad is conditioned by the
+ * configuration of the BRU sink 0 pad, we also set up the formats on all BRU
+ * sinks, even if the configuration will be overwritten later by
+ * vsp1_du_setup_rpf(). This ensures that the BRU configuration is set to a well
+ * defined state.
+ *
+ * Return 0 on success or a negative error code on failure.
+ */
+int vsp1_du_setup_lif(struct device *dev, unsigned int width,
+ unsigned int height)
+{
+ struct vsp1_device *vsp1 = dev_get_drvdata(dev);
+ struct vsp1_pipeline *pipe = &vsp1->drm->pipe;
+ struct vsp1_bru *bru = vsp1->bru;
+ struct v4l2_subdev_format format;
+ unsigned int i;
+ int ret;
+
+ dev_dbg(vsp1->dev, "%s: configuring LIF with format %ux%u\n",
+ __func__, width, height);
+
+ if (width == 0 || height == 0) {
+ /* Zero width or height means the CRTC is being disabled, stop
+ * the pipeline and turn the light off.
+ */
+ ret = vsp1_pipeline_stop(pipe);
+ if (ret == -ETIMEDOUT)
+ dev_err(vsp1->dev, "DRM pipeline stop timeout\n");
+
+ media_entity_pipeline_stop(&pipe->output->entity.subdev.entity);
+
+ for (i = 0; i < bru->entity.source_pad; ++i) {
+ bru->inputs[i].rpf = NULL;
+ pipe->inputs[i] = NULL;
+ }
+
+ pipe->num_inputs = 0;
+
+ vsp1_device_put(vsp1);
+
+ dev_dbg(vsp1->dev, "%s: pipeline disabled\n", __func__);
+
+ return 0;
+ }
+
+ vsp1_dl_reset(vsp1->drm->dl);
+
+ /* Configure the format at the BRU sinks and propagate it through the
+ * pipeline.
+ */
+ memset(&format, 0, sizeof(format));
+ format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+
+ for (i = 0; i < bru->entity.source_pad; ++i) {
+ format.pad = i;
+
+ format.format.width = width;
+ format.format.height = height;
+ format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32;
+ format.format.field = V4L2_FIELD_NONE;
+
+ ret = v4l2_subdev_call(&bru->entity.subdev, pad,
+ set_fmt, NULL, &format);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on BRU pad %u\n",
+ __func__, format.format.width, format.format.height,
+ format.format.code, i);
+ }
+
+ format.pad = bru->entity.source_pad;
+ format.format.width = width;
+ format.format.height = height;
+ format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32;
+ format.format.field = V4L2_FIELD_NONE;
+
+ ret = v4l2_subdev_call(&bru->entity.subdev, pad, set_fmt, NULL,
+ &format);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on BRU pad %u\n",
+ __func__, format.format.width, format.format.height,
+ format.format.code, i);
+
+ format.pad = RWPF_PAD_SINK;
+ ret = v4l2_subdev_call(&vsp1->wpf[0]->entity.subdev, pad, set_fmt, NULL,
+ &format);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on WPF0 sink\n",
+ __func__, format.format.width, format.format.height,
+ format.format.code);
+
+ format.pad = RWPF_PAD_SOURCE;
+ ret = v4l2_subdev_call(&vsp1->wpf[0]->entity.subdev, pad, get_fmt, NULL,
+ &format);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(vsp1->dev, "%s: got format %ux%u (%x) on WPF0 source\n",
+ __func__, format.format.width, format.format.height,
+ format.format.code);
+
+ format.pad = LIF_PAD_SINK;
+ ret = v4l2_subdev_call(&vsp1->lif->entity.subdev, pad, set_fmt, NULL,
+ &format);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on LIF sink\n",
+ __func__, format.format.width, format.format.height,
+ format.format.code);
+
+ /* Verify that the format at the output of the pipeline matches the
+ * requested frame size and media bus code.
+ */
+ if (format.format.width != width || format.format.height != height ||
+ format.format.code != MEDIA_BUS_FMT_ARGB8888_1X32) {
+ dev_dbg(vsp1->dev, "%s: format mismatch\n", __func__);
+ return -EPIPE;
+ }
+
+ /* Mark the pipeline as streaming and enable the VSP1. This will store
+ * the pipeline pointer in all entities, which the s_stream handlers
+ * will need. We don't start the entities themselves right at this point
+ * as there's no plane configured yet, so we can't start processing
+ * buffers.
+ */
+ ret = vsp1_device_get(vsp1);
+ if (ret < 0)
+ return ret;
+
+ ret = media_entity_pipeline_start(&pipe->output->entity.subdev.entity,
+ &pipe->pipe);
+ if (ret < 0) {
+ dev_dbg(vsp1->dev, "%s: pipeline start failed\n", __func__);
+ vsp1_device_put(vsp1);
+ return ret;
+ }
+
+ dev_dbg(vsp1->dev, "%s: pipeline enabled\n", __func__);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vsp1_du_setup_lif);
+
+/**
+ * vsp1_du_atomic_begin - Prepare for an atomic update
+ * @dev: the VSP device
+ */
+void vsp1_du_atomic_begin(struct device *dev)
+{
+ struct vsp1_device *vsp1 = dev_get_drvdata(dev);
+ struct vsp1_pipeline *pipe = &vsp1->drm->pipe;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pipe->irqlock, flags);
+
+ vsp1->drm->num_inputs = pipe->num_inputs;
+
+ spin_unlock_irqrestore(&pipe->irqlock, flags);
+
+ /* Prepare the display list. */
+ vsp1_dl_begin(vsp1->drm->dl);
+}
+EXPORT_SYMBOL_GPL(vsp1_du_atomic_begin);
+
+/**
+ * vsp1_du_atomic_update - Setup one RPF input of the VSP pipeline
+ * @dev: the VSP device
+ * @rpf_index: index of the RPF to setup (0-based)
+ * @pixelformat: V4L2 pixel format for the RPF memory input
+ * @pitch: number of bytes per line in the image stored in memory
+ * @mem: DMA addresses of the memory buffers (one per plane)
+ * @src: the source crop rectangle for the RPF
+ * @dst: the destination compose rectangle for the BRU input
+ *
+ * Configure the VSP to perform composition of the image referenced by @mem
+ * through RPF @rpf_index, using the @src crop rectangle and the @dst
+ * composition rectangle. The Z-order is fixed with RPF 0 at the bottom.
+ *
+ * Image format as stored in memory is expressed as a V4L2 @pixelformat value.
+ * As a special case, setting the pixel format to 0 will disable the RPF. The
+ * @pitch, @mem, @src and @dst parameters are ignored in that case. Calling the
+ * function on a disabled RPF is allowed.
+ *
+ * The memory pitch is configurable to allow for padding at end of lines, or
+ * simple for images that extend beyond the crop rectangle boundaries. The
+ * @pitch value is expressed in bytes and applies to all planes for multiplanar
+ * formats.
+ *
+ * The source memory buffer is referenced by the DMA address of its planes in
+ * the @mem array. Up to two planes are supported. The second plane DMA address
+ * is ignored for formats using a single plane.
+ *
+ * This function isn't reentrant, the caller needs to serialize calls.
+ *
+ * TODO: Implement Z-order control by decoupling the RPF index from the BRU
+ * input index.
+ *
+ * Return 0 on success or a negative error code on failure.
+ */
+int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index,
+ u32 pixelformat, unsigned int pitch,
+ dma_addr_t mem[2], const struct v4l2_rect *src,
+ const struct v4l2_rect *dst)
+{
+ struct vsp1_device *vsp1 = dev_get_drvdata(dev);
+ struct vsp1_pipeline *pipe = &vsp1->drm->pipe;
+ const struct vsp1_format_info *fmtinfo;
+ struct v4l2_subdev_selection sel;
+ struct v4l2_subdev_format format;
+ struct vsp1_rwpf_memory memory;
+ struct vsp1_rwpf *rpf;
+ unsigned long flags;
+ int ret;
+
+ if (rpf_index >= vsp1->info->rpf_count)
+ return -EINVAL;
+
+ rpf = vsp1->rpf[rpf_index];
+
+ if (pixelformat == 0) {
+ dev_dbg(vsp1->dev, "%s: RPF%u: disable requested\n", __func__,
+ rpf_index);
+
+ spin_lock_irqsave(&pipe->irqlock, flags);
+
+ if (pipe->inputs[rpf_index]) {
+ /* Remove the RPF from the pipeline if it was previously
+ * enabled.
+ */
+ vsp1->bru->inputs[rpf_index].rpf = NULL;
+ pipe->inputs[rpf_index] = NULL;
+
+ pipe->num_inputs--;
+ }
+
+ spin_unlock_irqrestore(&pipe->irqlock, flags);
+
+ return 0;
+ }
+
+ dev_dbg(vsp1->dev,
+ "%s: RPF%u: (%u,%u)/%ux%u -> (%u,%u)/%ux%u (%08x), pitch %u dma { %pad, %pad }\n",
+ __func__, rpf_index,
+ src->left, src->top, src->width, src->height,
+ dst->left, dst->top, dst->width, dst->height,
+ pixelformat, pitch, &mem[0], &mem[1]);
+
+ /* Set the stride at the RPF input. */
+ fmtinfo = vsp1_get_format_info(pixelformat);
+ if (!fmtinfo) {
+ dev_dbg(vsp1->dev, "Unsupport pixel format %08x for RPF\n",
+ pixelformat);
+ return -EINVAL;
+ }
+
+ rpf->fmtinfo = fmtinfo;
+ rpf->format.num_planes = fmtinfo->planes;
+ rpf->format.plane_fmt[0].bytesperline = pitch;
+ rpf->format.plane_fmt[1].bytesperline = pitch;
+
+ /* Configure the format on the RPF sink pad and propagate it up to the
+ * BRU sink pad.
+ */
+ memset(&format, 0, sizeof(format));
+ format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ format.pad = RWPF_PAD_SINK;
+ format.format.width = src->width + src->left;
+ format.format.height = src->height + src->top;
+ format.format.code = fmtinfo->mbus;
+ format.format.field = V4L2_FIELD_NONE;
+
+ ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_fmt, NULL,
+ &format);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(vsp1->dev,
+ "%s: set format %ux%u (%x) on RPF%u sink\n",
+ __func__, format.format.width, format.format.height,
+ format.format.code, rpf->entity.index);
+
+ memset(&sel, 0, sizeof(sel));
+ sel.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ sel.pad = RWPF_PAD_SINK;
+ sel.target = V4L2_SEL_TGT_CROP;
+ sel.r = *src;
+
+ ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_selection, NULL,
+ &sel);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(vsp1->dev,
+ "%s: set selection (%u,%u)/%ux%u on RPF%u sink\n",
+ __func__, sel.r.left, sel.r.top, sel.r.width, sel.r.height,
+ rpf->entity.index);
+
+ /* RPF source, hardcode the format to ARGB8888 to turn on format
+ * conversion if needed.
+ */
+ format.pad = RWPF_PAD_SOURCE;
+
+ ret = v4l2_subdev_call(&rpf->entity.subdev, pad, get_fmt, NULL,
+ &format);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(vsp1->dev,
+ "%s: got format %ux%u (%x) on RPF%u source\n",
+ __func__, format.format.width, format.format.height,
+ format.format.code, rpf->entity.index);
+
+ format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32;
+
+ ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_fmt, NULL,
+ &format);
+ if (ret < 0)
+ return ret;
+
+ /* BRU sink, propagate the format from the RPF source. */
+ format.pad = rpf->entity.index;
+
+ ret = v4l2_subdev_call(&vsp1->bru->entity.subdev, pad, set_fmt, NULL,
+ &format);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on BRU pad %u\n",
+ __func__, format.format.width, format.format.height,
+ format.format.code, format.pad);
+
+ sel.pad = rpf->entity.index;
+ sel.target = V4L2_SEL_TGT_COMPOSE;
+ sel.r = *dst;
+
+ ret = v4l2_subdev_call(&vsp1->bru->entity.subdev, pad, set_selection,
+ NULL, &sel);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(vsp1->dev,
+ "%s: set selection (%u,%u)/%ux%u on BRU pad %u\n",
+ __func__, sel.r.left, sel.r.top, sel.r.width, sel.r.height,
+ sel.pad);
+
+ /* Store the compose rectangle coordinates in the RPF. */
+ rpf->location.left = dst->left;
+ rpf->location.top = dst->top;
+
+ /* Set the memory buffer address. */
+ memory.num_planes = fmtinfo->planes;
+ memory.addr[0] = mem[0];
+ memory.addr[1] = mem[1];
+
+ rpf->ops->set_memory(rpf, &memory);
+
+ spin_lock_irqsave(&pipe->irqlock, flags);
+
+ /* If the RPF was previously stopped set the BRU input to the RPF and
+ * store the RPF in the pipeline inputs array.
+ */
+ if (!pipe->inputs[rpf->entity.index]) {
+ vsp1->bru->inputs[rpf_index].rpf = rpf;
+ pipe->inputs[rpf->entity.index] = rpf;
+ pipe->num_inputs++;
+ }
+
+ spin_unlock_irqrestore(&pipe->irqlock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vsp1_du_atomic_update);
+
+/**
+ * vsp1_du_atomic_flush - Commit an atomic update
+ * @dev: the VSP device
+ */
+void vsp1_du_atomic_flush(struct device *dev)
+{
+ struct vsp1_device *vsp1 = dev_get_drvdata(dev);
+ struct vsp1_pipeline *pipe = &vsp1->drm->pipe;
+ struct vsp1_entity *entity;
+ unsigned long flags;
+ bool stop = false;
+ int ret;
+
+ list_for_each_entry(entity, &pipe->entities, list_pipe) {
+ /* Disconnect unused RPFs from the pipeline. */
+ if (entity->type == VSP1_ENTITY_RPF) {
+ struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev);
+
+ if (!pipe->inputs[rpf->entity.index]) {
+ vsp1_mod_write(entity, entity->route->reg,
+ VI6_DPR_NODE_UNUSED);
+ continue;
+ }
+ }
+
+ vsp1_entity_route_setup(entity);
+
+ ret = v4l2_subdev_call(&entity->subdev, video,
+ s_stream, 1);
+ if (ret < 0) {
+ dev_err(vsp1->dev,
+ "DRM pipeline start failure on entity %s\n",
+ entity->subdev.name);
+ return;
+ }
+ }
+
+ vsp1_dl_commit(vsp1->drm->dl);
+
+ spin_lock_irqsave(&pipe->irqlock, flags);
+
+ /* Start or stop the pipeline if needed. */
+ if (!vsp1->drm->num_inputs && pipe->num_inputs) {
+ vsp1_write(vsp1, VI6_DISP_IRQ_STA, 0);
+ vsp1_write(vsp1, VI6_DISP_IRQ_ENB, VI6_DISP_IRQ_ENB_DSTE);
+ vsp1_pipeline_run(pipe);
+ } else if (vsp1->drm->num_inputs && !pipe->num_inputs) {
+ stop = true;
+ }
+
+ spin_unlock_irqrestore(&pipe->irqlock, flags);
+
+ if (stop) {
+ vsp1_write(vsp1, VI6_DISP_IRQ_ENB, 0);
+ vsp1_pipeline_stop(pipe);
+ }
+}
+EXPORT_SYMBOL_GPL(vsp1_du_atomic_flush);
+
+/* -----------------------------------------------------------------------------
+ * Initialization
+ */
+
+int vsp1_drm_create_links(struct vsp1_device *vsp1)
+{
+ const u32 flags = MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE;
+ unsigned int i;
+ int ret;
+
+ /* VSPD instances require a BRU to perform composition and a LIF to
+ * output to the DU.
+ */
+ if (!vsp1->bru || !vsp1->lif)
+ return -ENXIO;
+
+ for (i = 0; i < vsp1->info->rpf_count; ++i) {
+ struct vsp1_rwpf *rpf = vsp1->rpf[i];
+
+ ret = media_create_pad_link(&rpf->entity.subdev.entity,
+ RWPF_PAD_SOURCE,
+ &vsp1->bru->entity.subdev.entity,
+ i, flags);
+ if (ret < 0)
+ return ret;
+
+ rpf->entity.sink = &vsp1->bru->entity.subdev.entity;
+ rpf->entity.sink_pad = i;
+ }
+
+ ret = media_create_pad_link(&vsp1->bru->entity.subdev.entity,
+ vsp1->bru->entity.source_pad,
+ &vsp1->wpf[0]->entity.subdev.entity,
+ RWPF_PAD_SINK, flags);
+ if (ret < 0)
+ return ret;
+
+ vsp1->bru->entity.sink = &vsp1->wpf[0]->entity.subdev.entity;
+ vsp1->bru->entity.sink_pad = RWPF_PAD_SINK;
+
+ ret = media_create_pad_link(&vsp1->wpf[0]->entity.subdev.entity,
+ RWPF_PAD_SOURCE,
+ &vsp1->lif->entity.subdev.entity,
+ LIF_PAD_SINK, flags);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+int vsp1_drm_init(struct vsp1_device *vsp1)
+{
+ struct vsp1_pipeline *pipe;
+ unsigned int i;
+
+ vsp1->drm = devm_kzalloc(vsp1->dev, sizeof(*vsp1->drm), GFP_KERNEL);
+ if (!vsp1->drm)
+ return -ENOMEM;
+
+ vsp1->drm->dl = vsp1_dl_create(vsp1);
+ if (!vsp1->drm->dl)
+ return -ENOMEM;
+
+ pipe = &vsp1->drm->pipe;
+
+ vsp1_pipeline_init(pipe);
+ pipe->frame_end = vsp1_drm_pipeline_frame_end;
+
+ /* The DRM pipeline is static, add entities manually. */
+ for (i = 0; i < vsp1->info->rpf_count; ++i) {
+ struct vsp1_rwpf *input = vsp1->rpf[i];
+
+ list_add_tail(&input->entity.list_pipe, &pipe->entities);
+ }
+
+ list_add_tail(&vsp1->bru->entity.list_pipe, &pipe->entities);
+ list_add_tail(&vsp1->wpf[0]->entity.list_pipe, &pipe->entities);
+ list_add_tail(&vsp1->lif->entity.list_pipe, &pipe->entities);
+
+ pipe->bru = &vsp1->bru->entity;
+ pipe->lif = &vsp1->lif->entity;
+ pipe->output = vsp1->wpf[0];
+
+ pipe->dl = vsp1->drm->dl;
+
+ return 0;
+}
+
+void vsp1_drm_cleanup(struct vsp1_device *vsp1)
+{
+ vsp1_dl_destroy(vsp1->drm->dl);
+}
--- /dev/null
+/*
+ * vsp1_drm.h -- R-Car VSP1 DRM/KMS Interface
+ *
+ * Copyright (C) 2015 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef __VSP1_DRM_H__
+#define __VSP1_DRM_H__
+
+#include "vsp1_pipe.h"
+
+struct vsp1_dl;
+
+/**
+ * vsp1_drm - State for the API exposed to the DRM driver
+ * @dl: display list for DRM pipeline operation
+ * @pipe: the VSP1 pipeline used for display
+ * @num_inputs: number of active pipeline inputs at the beginning of an update
+ * @update: the pipeline configuration has been updated
+ */
+struct vsp1_drm {
+ struct vsp1_dl *dl;
+ struct vsp1_pipeline pipe;
+ unsigned int num_inputs;
+ bool update;
+};
+
+int vsp1_drm_init(struct vsp1_device *vsp1);
+void vsp1_drm_cleanup(struct vsp1_device *vsp1);
+int vsp1_drm_create_links(struct vsp1_device *vsp1);
+
+int vsp1_du_init(struct device *dev);
+int vsp1_du_setup_lif(struct device *dev, unsigned int width,
+ unsigned int height);
+void vsp1_du_atomic_begin(struct device *dev);
+int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index,
+ u32 pixelformat, unsigned int pitch,
+ dma_addr_t mem[2], const struct v4l2_rect *src,
+ const struct v4l2_rect *dst);
+void vsp1_du_atomic_flush(struct device *dev);
+
+
+#endif /* __VSP1_DRM_H__ */
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/videodev2.h>
+#include <media/v4l2-subdev.h>
+
#include "vsp1.h"
#include "vsp1_bru.h"
+#include "vsp1_dl.h"
+#include "vsp1_drm.h"
#include "vsp1_hsit.h"
#include "vsp1_lif.h"
#include "vsp1_lut.h"
#include "vsp1_rwpf.h"
#include "vsp1_sru.h"
#include "vsp1_uds.h"
+#include "vsp1_video.h"
/* -----------------------------------------------------------------------------
* Interrupt Handling
struct vsp1_device *vsp1 = data;
irqreturn_t ret = IRQ_NONE;
unsigned int i;
+ u32 status;
- for (i = 0; i < vsp1->pdata.wpf_count; ++i) {
+ for (i = 0; i < vsp1->info->wpf_count; ++i) {
struct vsp1_rwpf *wpf = vsp1->wpf[i];
struct vsp1_pipeline *pipe;
- u32 status;
if (wpf == NULL)
continue;
}
}
+ status = vsp1_read(vsp1, VI6_DISP_IRQ_STA);
+ vsp1_write(vsp1, VI6_DISP_IRQ_STA, ~status & VI6_DISP_IRQ_STA_DST);
+
+ if (status & VI6_DISP_IRQ_STA_DST) {
+ struct vsp1_rwpf *wpf = vsp1->wpf[0];
+ struct vsp1_pipeline *pipe;
+
+ if (wpf) {
+ pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity);
+ vsp1_pipeline_display_start(pipe);
+ }
+
+ ret = IRQ_HANDLED;
+ }
+
return ret;
}
*/
/*
- * vsp1_create_links - Create links from all sources to the given sink
+ * vsp1_create_sink_links - Create links from all sources to the given sink
*
* This function creates media links from all valid sources to the given sink
* pad. Links that would be invalid according to the VSP1 hardware capabilities
* - from a UDS to a UDS (UDS entities can't be chained)
* - from an entity to itself (no loops are allowed)
*/
-static int vsp1_create_links(struct vsp1_device *vsp1, struct vsp1_entity *sink)
+static int vsp1_create_sink_links(struct vsp1_device *vsp1,
+ struct vsp1_entity *sink)
{
struct media_entity *entity = &sink->subdev.entity;
struct vsp1_entity *source;
return 0;
}
-static void vsp1_destroy_entities(struct vsp1_device *vsp1)
+static int vsp1_uapi_create_links(struct vsp1_device *vsp1)
{
struct vsp1_entity *entity;
- struct vsp1_entity *next;
+ unsigned int i;
+ int ret;
+
+ list_for_each_entry(entity, &vsp1->entities, list_dev) {
+ if (entity->type == VSP1_ENTITY_LIF ||
+ entity->type == VSP1_ENTITY_RPF)
+ continue;
+
+ ret = vsp1_create_sink_links(vsp1, entity);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (vsp1->info->features & VSP1_HAS_LIF) {
+ ret = media_create_pad_link(&vsp1->wpf[0]->entity.subdev.entity,
+ RWPF_PAD_SOURCE,
+ &vsp1->lif->entity.subdev.entity,
+ LIF_PAD_SINK, 0);
+ if (ret < 0)
+ return ret;
+ }
+
+ for (i = 0; i < vsp1->info->rpf_count; ++i) {
+ struct vsp1_rwpf *rpf = vsp1->rpf[i];
- list_for_each_entry_safe(entity, next, &vsp1->entities, list_dev) {
+ ret = media_create_pad_link(&rpf->video->video.entity, 0,
+ &rpf->entity.subdev.entity,
+ RWPF_PAD_SINK,
+ MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+ if (ret < 0)
+ return ret;
+ }
+
+ for (i = 0; i < vsp1->info->wpf_count; ++i) {
+ /* Connect the video device to the WPF. All connections are
+ * immutable except for the WPF0 source link if a LIF is
+ * present.
+ */
+ struct vsp1_rwpf *wpf = vsp1->wpf[i];
+ unsigned int flags = MEDIA_LNK_FL_ENABLED;
+
+ if (!(vsp1->info->features & VSP1_HAS_LIF) || i != 0)
+ flags |= MEDIA_LNK_FL_IMMUTABLE;
+
+ ret = media_create_pad_link(&wpf->entity.subdev.entity,
+ RWPF_PAD_SOURCE,
+ &wpf->video->video.entity, 0,
+ flags);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void vsp1_destroy_entities(struct vsp1_device *vsp1)
+{
+ struct vsp1_entity *entity, *_entity;
+ struct vsp1_video *video, *_video;
+
+ list_for_each_entry_safe(entity, _entity, &vsp1->entities, list_dev) {
list_del(&entity->list_dev);
vsp1_entity_destroy(entity);
}
+ list_for_each_entry_safe(video, _video, &vsp1->videos, list) {
+ list_del(&video->list);
+ vsp1_video_cleanup(video);
+ }
+
v4l2_device_unregister(&vsp1->v4l2_dev);
media_device_unregister(&vsp1->media_dev);
media_device_cleanup(&vsp1->media_dev);
+
+ if (!vsp1->info->uapi)
+ vsp1_drm_cleanup(vsp1);
}
static int vsp1_create_entities(struct vsp1_device *vsp1)
dev_name(mdev->dev));
media_device_init(mdev);
+ vsp1->media_ops.link_setup = vsp1_entity_link_setup;
+ /* Don't perform link validation when the userspace API is disabled as
+ * the pipeline is configured internally by the driver in that case, and
+ * its configuration can thus be trusted.
+ */
+ if (vsp1->info->uapi)
+ vsp1->media_ops.link_validate = v4l2_subdev_link_validate;
+
vdev->mdev = mdev;
ret = v4l2_device_register(vsp1->dev, vdev);
if (ret < 0) {
}
/* Instantiate all the entities. */
- vsp1->bru = vsp1_bru_create(vsp1);
- if (IS_ERR(vsp1->bru)) {
- ret = PTR_ERR(vsp1->bru);
- goto done;
- }
+ if (vsp1->info->features & VSP1_HAS_BRU) {
+ vsp1->bru = vsp1_bru_create(vsp1);
+ if (IS_ERR(vsp1->bru)) {
+ ret = PTR_ERR(vsp1->bru);
+ goto done;
+ }
- list_add_tail(&vsp1->bru->entity.list_dev, &vsp1->entities);
+ list_add_tail(&vsp1->bru->entity.list_dev, &vsp1->entities);
+ }
vsp1->hsi = vsp1_hsit_create(vsp1, true);
if (IS_ERR(vsp1->hsi)) {
list_add_tail(&vsp1->hst->entity.list_dev, &vsp1->entities);
- if (vsp1->pdata.features & VSP1_HAS_LIF) {
+ if (vsp1->info->features & VSP1_HAS_LIF) {
vsp1->lif = vsp1_lif_create(vsp1);
if (IS_ERR(vsp1->lif)) {
ret = PTR_ERR(vsp1->lif);
list_add_tail(&vsp1->lif->entity.list_dev, &vsp1->entities);
}
- if (vsp1->pdata.features & VSP1_HAS_LUT) {
+ if (vsp1->info->features & VSP1_HAS_LUT) {
vsp1->lut = vsp1_lut_create(vsp1);
if (IS_ERR(vsp1->lut)) {
ret = PTR_ERR(vsp1->lut);
list_add_tail(&vsp1->lut->entity.list_dev, &vsp1->entities);
}
- for (i = 0; i < vsp1->pdata.rpf_count; ++i) {
+ for (i = 0; i < vsp1->info->rpf_count; ++i) {
struct vsp1_rwpf *rpf;
rpf = vsp1_rpf_create(vsp1, i);
vsp1->rpf[i] = rpf;
list_add_tail(&rpf->entity.list_dev, &vsp1->entities);
+
+ if (vsp1->info->uapi) {
+ struct vsp1_video *video = vsp1_video_create(vsp1, rpf);
+
+ if (IS_ERR(video)) {
+ ret = PTR_ERR(video);
+ goto done;
+ }
+
+ list_add_tail(&video->list, &vsp1->videos);
+ }
}
- if (vsp1->pdata.features & VSP1_HAS_SRU) {
+ if (vsp1->info->features & VSP1_HAS_SRU) {
vsp1->sru = vsp1_sru_create(vsp1);
if (IS_ERR(vsp1->sru)) {
ret = PTR_ERR(vsp1->sru);
list_add_tail(&vsp1->sru->entity.list_dev, &vsp1->entities);
}
- for (i = 0; i < vsp1->pdata.uds_count; ++i) {
+ for (i = 0; i < vsp1->info->uds_count; ++i) {
struct vsp1_uds *uds;
uds = vsp1_uds_create(vsp1, i);
list_add_tail(&uds->entity.list_dev, &vsp1->entities);
}
- for (i = 0; i < vsp1->pdata.wpf_count; ++i) {
+ for (i = 0; i < vsp1->info->wpf_count; ++i) {
struct vsp1_rwpf *wpf;
wpf = vsp1_wpf_create(vsp1, i);
vsp1->wpf[i] = wpf;
list_add_tail(&wpf->entity.list_dev, &vsp1->entities);
+
+ if (vsp1->info->uapi) {
+ struct vsp1_video *video = vsp1_video_create(vsp1, wpf);
+
+ if (IS_ERR(video)) {
+ ret = PTR_ERR(video);
+ goto done;
+ }
+
+ list_add_tail(&video->list, &vsp1->videos);
+ wpf->entity.sink = &video->video.entity;
+ }
}
/* Register all subdevs. */
}
/* Create links. */
- list_for_each_entry(entity, &vsp1->entities, list_dev) {
- if (entity->type == VSP1_ENTITY_WPF) {
- ret = vsp1_wpf_create_links(vsp1, entity);
- if (ret < 0)
- goto done;
- } else if (entity->type == VSP1_ENTITY_RPF) {
- ret = vsp1_rpf_create_links(vsp1, entity);
- if (ret < 0)
- goto done;
- }
-
- if (entity->type != VSP1_ENTITY_LIF &&
- entity->type != VSP1_ENTITY_RPF) {
- ret = vsp1_create_links(vsp1, entity);
- if (ret < 0)
- goto done;
- }
- }
+ if (vsp1->info->uapi)
+ ret = vsp1_uapi_create_links(vsp1);
+ else
+ ret = vsp1_drm_create_links(vsp1);
+ if (ret < 0)
+ goto done;
- if (vsp1->pdata.features & VSP1_HAS_LIF) {
- ret = media_create_pad_link(
- &vsp1->wpf[0]->entity.subdev.entity, RWPF_PAD_SOURCE,
- &vsp1->lif->entity.subdev.entity, LIF_PAD_SINK, 0);
- if (ret < 0)
- return ret;
+ /* Register subdev nodes if the userspace API is enabled or initialize
+ * the DRM pipeline otherwise.
+ */
+ if (vsp1->info->uapi) {
+ vsp1->use_dl = false;
+ ret = v4l2_device_register_subdev_nodes(&vsp1->v4l2_dev);
+ } else {
+ vsp1->use_dl = true;
+ ret = vsp1_drm_init(vsp1);
}
-
- ret = v4l2_device_register_subdev_nodes(&vsp1->v4l2_dev);
if (ret < 0)
goto done;
return ret;
}
-static int vsp1_device_init(struct vsp1_device *vsp1)
+int vsp1_reset_wpf(struct vsp1_device *vsp1, unsigned int index)
{
- unsigned int i;
+ unsigned int timeout;
u32 status;
- /* Reset any channel that might be running. */
status = vsp1_read(vsp1, VI6_STATUS);
+ if (!(status & VI6_STATUS_SYS_ACT(index)))
+ return 0;
- for (i = 0; i < vsp1->pdata.wpf_count; ++i) {
- unsigned int timeout;
+ vsp1_write(vsp1, VI6_SRESET, VI6_SRESET_SRTS(index));
+ for (timeout = 10; timeout > 0; --timeout) {
+ status = vsp1_read(vsp1, VI6_STATUS);
+ if (!(status & VI6_STATUS_SYS_ACT(index)))
+ break;
- if (!(status & VI6_STATUS_SYS_ACT(i)))
- continue;
+ usleep_range(1000, 2000);
+ }
- vsp1_write(vsp1, VI6_SRESET, VI6_SRESET_SRTS(i));
- for (timeout = 10; timeout > 0; --timeout) {
- status = vsp1_read(vsp1, VI6_STATUS);
- if (!(status & VI6_STATUS_SYS_ACT(i)))
- break;
+ if (!timeout) {
+ dev_err(vsp1->dev, "failed to reset wpf.%u\n", index);
+ return -ETIMEDOUT;
+ }
- usleep_range(1000, 2000);
- }
+ return 0;
+}
- if (!timeout) {
- dev_err(vsp1->dev, "failed to reset wpf.%u\n", i);
- return -ETIMEDOUT;
- }
+static int vsp1_device_init(struct vsp1_device *vsp1)
+{
+ unsigned int i;
+ int ret;
+
+ /* Reset any channel that might be running. */
+ for (i = 0; i < vsp1->info->wpf_count; ++i) {
+ ret = vsp1_reset_wpf(vsp1, i);
+ if (ret < 0)
+ return ret;
}
vsp1_write(vsp1, VI6_CLK_DCSWT, (8 << VI6_CLK_DCSWT_CSTPW_SHIFT) |
(8 << VI6_CLK_DCSWT_CSTRW_SHIFT));
- for (i = 0; i < vsp1->pdata.rpf_count; ++i)
+ for (i = 0; i < vsp1->info->rpf_count; ++i)
vsp1_write(vsp1, VI6_DPR_RPF_ROUTE(i), VI6_DPR_NODE_UNUSED);
- for (i = 0; i < vsp1->pdata.uds_count; ++i)
+ for (i = 0; i < vsp1->info->uds_count; ++i)
vsp1_write(vsp1, VI6_DPR_UDS_ROUTE(i), VI6_DPR_NODE_UNUSED);
vsp1_write(vsp1, VI6_DPR_SRU_ROUTE, VI6_DPR_NODE_UNUSED);
vsp1_write(vsp1, VI6_DPR_HGT_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) |
(VI6_DPR_NODE_UNUSED << VI6_DPR_SMPPT_PT_SHIFT));
+ if (vsp1->use_dl)
+ vsp1_dl_setup(vsp1);
+
return 0;
}
* Platform Driver
*/
-static int vsp1_parse_dt(struct vsp1_device *vsp1)
-{
- struct device_node *np = vsp1->dev->of_node;
- struct vsp1_platform_data *pdata = &vsp1->pdata;
-
- if (of_property_read_bool(np, "renesas,has-lif"))
- pdata->features |= VSP1_HAS_LIF;
- if (of_property_read_bool(np, "renesas,has-lut"))
- pdata->features |= VSP1_HAS_LUT;
- if (of_property_read_bool(np, "renesas,has-sru"))
- pdata->features |= VSP1_HAS_SRU;
-
- of_property_read_u32(np, "renesas,#rpf", &pdata->rpf_count);
- of_property_read_u32(np, "renesas,#uds", &pdata->uds_count);
- of_property_read_u32(np, "renesas,#wpf", &pdata->wpf_count);
-
- if (pdata->rpf_count <= 0 || pdata->rpf_count > VSP1_MAX_RPF) {
- dev_err(vsp1->dev, "invalid number of RPF (%u)\n",
- pdata->rpf_count);
- return -EINVAL;
- }
-
- if (pdata->uds_count <= 0 || pdata->uds_count > VSP1_MAX_UDS) {
- dev_err(vsp1->dev, "invalid number of UDS (%u)\n",
- pdata->uds_count);
- return -EINVAL;
- }
-
- if (pdata->wpf_count <= 0 || pdata->wpf_count > VSP1_MAX_WPF) {
- dev_err(vsp1->dev, "invalid number of WPF (%u)\n",
- pdata->wpf_count);
- return -EINVAL;
- }
-
- return 0;
-}
+static const struct vsp1_device_info vsp1_device_infos[] = {
+ {
+ .version = VI6_IP_VERSION_MODEL_VSPS_H2,
+ .features = VSP1_HAS_BRU | VSP1_HAS_LUT | VSP1_HAS_SRU,
+ .rpf_count = 5,
+ .uds_count = 3,
+ .wpf_count = 4,
+ .num_bru_inputs = 4,
+ .uapi = true,
+ }, {
+ .version = VI6_IP_VERSION_MODEL_VSPR_H2,
+ .features = VSP1_HAS_BRU | VSP1_HAS_SRU,
+ .rpf_count = 5,
+ .uds_count = 1,
+ .wpf_count = 4,
+ .num_bru_inputs = 4,
+ .uapi = true,
+ }, {
+ .version = VI6_IP_VERSION_MODEL_VSPD_GEN2,
+ .features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_LUT,
+ .rpf_count = 4,
+ .uds_count = 1,
+ .wpf_count = 4,
+ .num_bru_inputs = 4,
+ .uapi = true,
+ }, {
+ .version = VI6_IP_VERSION_MODEL_VSPS_M2,
+ .features = VSP1_HAS_BRU | VSP1_HAS_LUT | VSP1_HAS_SRU,
+ .rpf_count = 5,
+ .uds_count = 3,
+ .wpf_count = 4,
+ .num_bru_inputs = 4,
+ .uapi = true,
+ }, {
+ .version = VI6_IP_VERSION_MODEL_VSPI_GEN3,
+ .features = VSP1_HAS_LUT | VSP1_HAS_SRU,
+ .rpf_count = 1,
+ .uds_count = 1,
+ .wpf_count = 1,
+ .uapi = true,
+ }, {
+ .version = VI6_IP_VERSION_MODEL_VSPBD_GEN3,
+ .features = VSP1_HAS_BRU,
+ .rpf_count = 5,
+ .wpf_count = 1,
+ .num_bru_inputs = 5,
+ .uapi = true,
+ }, {
+ .version = VI6_IP_VERSION_MODEL_VSPBC_GEN3,
+ .features = VSP1_HAS_BRU | VSP1_HAS_LUT,
+ .rpf_count = 5,
+ .wpf_count = 1,
+ .num_bru_inputs = 5,
+ .uapi = true,
+ }, {
+ .version = VI6_IP_VERSION_MODEL_VSPD_GEN3,
+ .features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_LUT,
+ .rpf_count = 5,
+ .wpf_count = 2,
+ .num_bru_inputs = 5,
+ },
+};
static int vsp1_probe(struct platform_device *pdev)
{
struct vsp1_device *vsp1;
struct resource *irq;
struct resource *io;
+ unsigned int i;
+ u32 version;
int ret;
vsp1 = devm_kzalloc(&pdev->dev, sizeof(*vsp1), GFP_KERNEL);
vsp1->dev = &pdev->dev;
mutex_init(&vsp1->lock);
INIT_LIST_HEAD(&vsp1->entities);
-
- ret = vsp1_parse_dt(vsp1);
- if (ret < 0)
- return ret;
+ INIT_LIST_HEAD(&vsp1->videos);
/* I/O, IRQ and clock resources */
io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
return ret;
}
+ /* Configure device parameters based on the version register. */
+ ret = clk_prepare_enable(vsp1->clock);
+ if (ret < 0)
+ return ret;
+
+ version = vsp1_read(vsp1, VI6_IP_VERSION);
+ clk_disable_unprepare(vsp1->clock);
+
+ for (i = 0; i < ARRAY_SIZE(vsp1_device_infos); ++i) {
+ if ((version & VI6_IP_VERSION_MODEL_MASK) ==
+ vsp1_device_infos[i].version) {
+ vsp1->info = &vsp1_device_infos[i];
+ break;
+ }
+ }
+
+ if (!vsp1->info) {
+ dev_err(&pdev->dev, "unsupported IP version 0x%08x\n", version);
+ return -ENXIO;
+ }
+
+ dev_dbg(&pdev->dev, "IP version 0x%08x\n", version);
+
/* Instanciate entities */
ret = vsp1_create_entities(vsp1);
if (ret < 0) {
static const struct of_device_id vsp1_of_match[] = {
{ .compatible = "renesas,vsp1" },
+ { .compatible = "renesas,vsp2" },
{ },
};
#include "vsp1.h"
#include "vsp1_entity.h"
-#include "vsp1_video.h"
bool vsp1_entity_is_streaming(struct vsp1_entity *entity)
{
if (!streaming)
return 0;
- if (!entity->subdev.ctrl_handler)
+ if (!entity->vsp1->info->uapi || !entity->subdev.ctrl_handler)
return 0;
ret = v4l2_ctrl_handler_setup(entity->subdev.ctrl_handler);
return ret;
}
+void vsp1_entity_route_setup(struct vsp1_entity *source)
+{
+ struct vsp1_entity *sink;
+
+ if (source->route->reg == 0)
+ return;
+
+ sink = container_of(source->sink, struct vsp1_entity, subdev.entity);
+ vsp1_mod_write(source, source->route->reg,
+ sink->route->inputs[source->sink_pad]);
+}
+
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Operations
*/
* Media Operations
*/
-static int vsp1_entity_link_setup(struct media_entity *entity,
- const struct media_pad *local,
- const struct media_pad *remote, u32 flags)
+int vsp1_entity_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
{
struct vsp1_entity *source;
return 0;
}
-const struct media_entity_operations vsp1_media_ops = {
- .link_setup = vsp1_entity_link_setup,
- .link_validate = v4l2_subdev_link_validate,
-};
-
/* -----------------------------------------------------------------------------
* Initialization
*/
static const struct vsp1_route vsp1_routes[] = {
{ VSP1_ENTITY_BRU, 0, VI6_DPR_BRU_ROUTE,
{ VI6_DPR_NODE_BRU_IN(0), VI6_DPR_NODE_BRU_IN(1),
- VI6_DPR_NODE_BRU_IN(2), VI6_DPR_NODE_BRU_IN(3), } },
+ VI6_DPR_NODE_BRU_IN(2), VI6_DPR_NODE_BRU_IN(3),
+ VI6_DPR_NODE_BRU_IN(4) } },
{ VSP1_ENTITY_HSI, 0, VI6_DPR_HSI_ROUTE, { VI6_DPR_NODE_HSI, } },
{ VSP1_ENTITY_HST, 0, VI6_DPR_HST_ROUTE, { VI6_DPR_NODE_HST, } },
{ VSP1_ENTITY_LIF, 0, 0, { VI6_DPR_NODE_LIF, } },
void vsp1_entity_destroy(struct vsp1_entity *entity)
{
- if (entity->video)
- vsp1_video_cleanup(entity->video);
if (entity->subdev.ctrl_handler)
v4l2_ctrl_handler_free(entity->subdev.ctrl_handler);
media_entity_cleanup(&entity->subdev.entity);
#include <media/v4l2-subdev.h>
struct vsp1_device;
-struct vsp1_video;
enum vsp1_entity_type {
VSP1_ENTITY_BRU,
VSP1_ENTITY_WPF,
};
+#define VSP1_ENTITY_MAX_INPUTS 5 /* For the BRU */
+
/*
* struct vsp1_route - Entity routing configuration
* @type: Entity type this routing entry is associated with
enum vsp1_entity_type type;
unsigned int index;
unsigned int reg;
- unsigned int inputs[4];
+ unsigned int inputs[VSP1_ENTITY_MAX_INPUTS];
};
struct vsp1_entity {
struct v4l2_subdev subdev;
struct v4l2_mbus_framefmt *formats;
- struct vsp1_video *video;
-
spinlock_t lock; /* Protects the streaming field */
bool streaming;
};
void vsp1_entity_destroy(struct vsp1_entity *entity);
extern const struct v4l2_subdev_internal_ops vsp1_subdev_internal_ops;
-extern const struct media_entity_operations vsp1_media_ops;
+
+int vsp1_entity_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags);
struct v4l2_mbus_framefmt *
vsp1_entity_get_pad_format(struct vsp1_entity *entity,
bool vsp1_entity_is_streaming(struct vsp1_entity *entity);
int vsp1_entity_set_streaming(struct vsp1_entity *entity, bool streaming);
+void vsp1_entity_route_setup(struct vsp1_entity *source);
+
#endif /* __VSP1_ENTITY_H__ */
subdev = &hsit->entity.subdev;
v4l2_subdev_init(subdev, &hsit_ops);
- subdev->entity.ops = &vsp1_media_ops;
+ subdev->entity.ops = &vsp1->media_ops;
subdev->internal_ops = &vsp1_subdev_internal_ops;
snprintf(subdev->name, sizeof(subdev->name), "%s %s",
dev_name(vsp1->dev), inverse ? "hsi" : "hst");
* Device Access
*/
-static inline u32 vsp1_lif_read(struct vsp1_lif *lif, u32 reg)
-{
- return vsp1_read(lif->entity.vsp1, reg);
-}
-
static inline void vsp1_lif_write(struct vsp1_lif *lif, u32 reg, u32 data)
{
- vsp1_write(lif->entity.vsp1, reg, data);
+ vsp1_mod_write(&lif->entity, reg, data);
}
/* -----------------------------------------------------------------------------
unsigned int lbth = 200;
if (!enable) {
- vsp1_lif_write(lif, VI6_LIF_CTRL, 0);
+ vsp1_write(lif->entity.vsp1, VI6_LIF_CTRL, 0);
return 0;
}
subdev = &lif->entity.subdev;
v4l2_subdev_init(subdev, &lif_ops);
- subdev->entity.ops = &vsp1_media_ops;
+ subdev->entity.ops = &vsp1->media_ops;
subdev->internal_ops = &vsp1_subdev_internal_ops;
snprintf(subdev->name, sizeof(subdev->name), "%s lif",
dev_name(vsp1->dev));
* Device Access
*/
-static inline u32 vsp1_lut_read(struct vsp1_lut *lut, u32 reg)
-{
- return vsp1_read(lut->entity.vsp1, reg);
-}
-
static inline void vsp1_lut_write(struct vsp1_lut *lut, u32 reg, u32 data)
{
vsp1_write(lut->entity.vsp1, reg, data);
subdev = &lut->entity.subdev;
v4l2_subdev_init(subdev, &lut_ops);
- subdev->entity.ops = &vsp1_media_ops;
+ subdev->entity.ops = &vsp1->media_ops;
subdev->internal_ops = &vsp1_subdev_internal_ops;
snprintf(subdev->name, sizeof(subdev->name), "%s lut",
dev_name(vsp1->dev));
--- /dev/null
+/*
+ * vsp1_pipe.c -- R-Car VSP1 Pipeline
+ *
+ * Copyright (C) 2013-2015 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-subdev.h>
+
+#include "vsp1.h"
+#include "vsp1_bru.h"
+#include "vsp1_dl.h"
+#include "vsp1_entity.h"
+#include "vsp1_pipe.h"
+#include "vsp1_rwpf.h"
+#include "vsp1_uds.h"
+
+/* -----------------------------------------------------------------------------
+ * Helper Functions
+ */
+
+static const struct vsp1_format_info vsp1_video_formats[] = {
+ { V4L2_PIX_FMT_RGB332, MEDIA_BUS_FMT_ARGB8888_1X32,
+ VI6_FMT_RGB_332, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 1, { 8, 0, 0 }, false, false, 1, 1, false },
+ { V4L2_PIX_FMT_ARGB444, MEDIA_BUS_FMT_ARGB8888_1X32,
+ VI6_FMT_ARGB_4444, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS,
+ 1, { 16, 0, 0 }, false, false, 1, 1, true },
+ { V4L2_PIX_FMT_XRGB444, MEDIA_BUS_FMT_ARGB8888_1X32,
+ VI6_FMT_XRGB_4444, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS,
+ 1, { 16, 0, 0 }, false, false, 1, 1, true },
+ { V4L2_PIX_FMT_ARGB555, MEDIA_BUS_FMT_ARGB8888_1X32,
+ VI6_FMT_ARGB_1555, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS,
+ 1, { 16, 0, 0 }, false, false, 1, 1, true },
+ { V4L2_PIX_FMT_XRGB555, MEDIA_BUS_FMT_ARGB8888_1X32,
+ VI6_FMT_XRGB_1555, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS,
+ 1, { 16, 0, 0 }, false, false, 1, 1, false },
+ { V4L2_PIX_FMT_RGB565, MEDIA_BUS_FMT_ARGB8888_1X32,
+ VI6_FMT_RGB_565, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS,
+ 1, { 16, 0, 0 }, false, false, 1, 1, false },
+ { V4L2_PIX_FMT_BGR24, MEDIA_BUS_FMT_ARGB8888_1X32,
+ VI6_FMT_BGR_888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 1, { 24, 0, 0 }, false, false, 1, 1, false },
+ { V4L2_PIX_FMT_RGB24, MEDIA_BUS_FMT_ARGB8888_1X32,
+ VI6_FMT_RGB_888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 1, { 24, 0, 0 }, false, false, 1, 1, false },
+ { V4L2_PIX_FMT_ABGR32, MEDIA_BUS_FMT_ARGB8888_1X32,
+ VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS,
+ 1, { 32, 0, 0 }, false, false, 1, 1, true },
+ { V4L2_PIX_FMT_XBGR32, MEDIA_BUS_FMT_ARGB8888_1X32,
+ VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS,
+ 1, { 32, 0, 0 }, false, false, 1, 1, false },
+ { V4L2_PIX_FMT_ARGB32, MEDIA_BUS_FMT_ARGB8888_1X32,
+ VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 1, { 32, 0, 0 }, false, false, 1, 1, true },
+ { V4L2_PIX_FMT_XRGB32, MEDIA_BUS_FMT_ARGB8888_1X32,
+ VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 1, { 32, 0, 0 }, false, false, 1, 1, false },
+ { V4L2_PIX_FMT_UYVY, MEDIA_BUS_FMT_AYUV8_1X32,
+ VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 1, { 16, 0, 0 }, false, false, 2, 1, false },
+ { V4L2_PIX_FMT_VYUY, MEDIA_BUS_FMT_AYUV8_1X32,
+ VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 1, { 16, 0, 0 }, false, true, 2, 1, false },
+ { V4L2_PIX_FMT_YUYV, MEDIA_BUS_FMT_AYUV8_1X32,
+ VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 1, { 16, 0, 0 }, true, false, 2, 1, false },
+ { V4L2_PIX_FMT_YVYU, MEDIA_BUS_FMT_AYUV8_1X32,
+ VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 1, { 16, 0, 0 }, true, true, 2, 1, false },
+ { V4L2_PIX_FMT_NV12M, MEDIA_BUS_FMT_AYUV8_1X32,
+ VI6_FMT_Y_UV_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 2, { 8, 16, 0 }, false, false, 2, 2, false },
+ { V4L2_PIX_FMT_NV21M, MEDIA_BUS_FMT_AYUV8_1X32,
+ VI6_FMT_Y_UV_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 2, { 8, 16, 0 }, false, true, 2, 2, false },
+ { V4L2_PIX_FMT_NV16M, MEDIA_BUS_FMT_AYUV8_1X32,
+ VI6_FMT_Y_UV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 2, { 8, 16, 0 }, false, false, 2, 1, false },
+ { V4L2_PIX_FMT_NV61M, MEDIA_BUS_FMT_AYUV8_1X32,
+ VI6_FMT_Y_UV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 2, { 8, 16, 0 }, false, true, 2, 1, false },
+ { V4L2_PIX_FMT_YUV420M, MEDIA_BUS_FMT_AYUV8_1X32,
+ VI6_FMT_Y_U_V_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 3, { 8, 8, 8 }, false, false, 2, 2, false },
+ { V4L2_PIX_FMT_YVU420M, MEDIA_BUS_FMT_AYUV8_1X32,
+ VI6_FMT_Y_U_V_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 3, { 8, 8, 8 }, false, true, 2, 2, false },
+ { V4L2_PIX_FMT_YUV422M, MEDIA_BUS_FMT_AYUV8_1X32,
+ VI6_FMT_Y_U_V_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 3, { 8, 8, 8 }, false, false, 2, 1, false },
+ { V4L2_PIX_FMT_YVU422M, MEDIA_BUS_FMT_AYUV8_1X32,
+ VI6_FMT_Y_U_V_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 3, { 8, 8, 8 }, false, true, 2, 1, false },
+ { V4L2_PIX_FMT_YUV444M, MEDIA_BUS_FMT_AYUV8_1X32,
+ VI6_FMT_Y_U_V_444, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 3, { 8, 8, 8 }, false, false, 1, 1, false },
+ { V4L2_PIX_FMT_YVU444M, MEDIA_BUS_FMT_AYUV8_1X32,
+ VI6_FMT_Y_U_V_444, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 3, { 8, 8, 8 }, false, true, 1, 1, false },
+};
+
+/*
+ * vsp1_get_format_info - Retrieve format information for a 4CC
+ * @fourcc: the format 4CC
+ *
+ * Return a pointer to the format information structure corresponding to the
+ * given V4L2 format 4CC, or NULL if no corresponding format can be found.
+ */
+const struct vsp1_format_info *vsp1_get_format_info(u32 fourcc)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(vsp1_video_formats); ++i) {
+ const struct vsp1_format_info *info = &vsp1_video_formats[i];
+
+ if (info->fourcc == fourcc)
+ return info;
+ }
+
+ return NULL;
+}
+
+/* -----------------------------------------------------------------------------
+ * Pipeline Management
+ */
+
+void vsp1_pipeline_reset(struct vsp1_pipeline *pipe)
+{
+ unsigned int i;
+
+ if (pipe->bru) {
+ struct vsp1_bru *bru = to_bru(&pipe->bru->subdev);
+
+ for (i = 0; i < ARRAY_SIZE(bru->inputs); ++i)
+ bru->inputs[i].rpf = NULL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i)
+ pipe->inputs[i] = NULL;
+
+ INIT_LIST_HEAD(&pipe->entities);
+ pipe->state = VSP1_PIPELINE_STOPPED;
+ pipe->buffers_ready = 0;
+ pipe->num_inputs = 0;
+ pipe->output = NULL;
+ pipe->bru = NULL;
+ pipe->lif = NULL;
+ pipe->uds = NULL;
+}
+
+void vsp1_pipeline_init(struct vsp1_pipeline *pipe)
+{
+ mutex_init(&pipe->lock);
+ spin_lock_init(&pipe->irqlock);
+ init_waitqueue_head(&pipe->wq);
+
+ INIT_LIST_HEAD(&pipe->entities);
+ pipe->state = VSP1_PIPELINE_STOPPED;
+}
+
+void vsp1_pipeline_run(struct vsp1_pipeline *pipe)
+{
+ struct vsp1_device *vsp1 = pipe->output->entity.vsp1;
+
+ if (pipe->state == VSP1_PIPELINE_STOPPED) {
+ vsp1_write(vsp1, VI6_CMD(pipe->output->entity.index),
+ VI6_CMD_STRCMD);
+ pipe->state = VSP1_PIPELINE_RUNNING;
+ }
+
+ pipe->buffers_ready = 0;
+}
+
+bool vsp1_pipeline_stopped(struct vsp1_pipeline *pipe)
+{
+ unsigned long flags;
+ bool stopped;
+
+ spin_lock_irqsave(&pipe->irqlock, flags);
+ stopped = pipe->state == VSP1_PIPELINE_STOPPED;
+ spin_unlock_irqrestore(&pipe->irqlock, flags);
+
+ return stopped;
+}
+
+int vsp1_pipeline_stop(struct vsp1_pipeline *pipe)
+{
+ struct vsp1_entity *entity;
+ unsigned long flags;
+ int ret;
+
+ if (pipe->dl) {
+ /* When using display lists in continuous frame mode the only
+ * way to stop the pipeline is to reset the hardware.
+ */
+ ret = vsp1_reset_wpf(pipe->output->entity.vsp1,
+ pipe->output->entity.index);
+ if (ret == 0) {
+ spin_lock_irqsave(&pipe->irqlock, flags);
+ pipe->state = VSP1_PIPELINE_STOPPED;
+ spin_unlock_irqrestore(&pipe->irqlock, flags);
+ }
+ } else {
+ /* Otherwise just request a stop and wait. */
+ spin_lock_irqsave(&pipe->irqlock, flags);
+ if (pipe->state == VSP1_PIPELINE_RUNNING)
+ pipe->state = VSP1_PIPELINE_STOPPING;
+ spin_unlock_irqrestore(&pipe->irqlock, flags);
+
+ ret = wait_event_timeout(pipe->wq, vsp1_pipeline_stopped(pipe),
+ msecs_to_jiffies(500));
+ ret = ret == 0 ? -ETIMEDOUT : 0;
+ }
+
+ list_for_each_entry(entity, &pipe->entities, list_pipe) {
+ if (entity->route && entity->route->reg)
+ vsp1_write(entity->vsp1, entity->route->reg,
+ VI6_DPR_NODE_UNUSED);
+
+ v4l2_subdev_call(&entity->subdev, video, s_stream, 0);
+ }
+
+ return ret;
+}
+
+bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe)
+{
+ unsigned int mask;
+
+ mask = ((1 << pipe->num_inputs) - 1) << 1;
+ if (!pipe->lif)
+ mask |= 1 << 0;
+
+ return pipe->buffers_ready == mask;
+}
+
+void vsp1_pipeline_display_start(struct vsp1_pipeline *pipe)
+{
+ if (pipe->dl)
+ vsp1_dl_irq_display_start(pipe->dl);
+}
+
+void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe)
+{
+ enum vsp1_pipeline_state state;
+ unsigned long flags;
+
+ if (pipe == NULL)
+ return;
+
+ if (pipe->dl)
+ vsp1_dl_irq_frame_end(pipe->dl);
+
+ /* Signal frame end to the pipeline handler. */
+ pipe->frame_end(pipe);
+
+ spin_lock_irqsave(&pipe->irqlock, flags);
+
+ state = pipe->state;
+
+ /* When using display lists in continuous frame mode the pipeline is
+ * automatically restarted by the hardware.
+ */
+ if (!pipe->dl)
+ pipe->state = VSP1_PIPELINE_STOPPED;
+
+ /* If a stop has been requested, mark the pipeline as stopped and
+ * return.
+ */
+ if (state == VSP1_PIPELINE_STOPPING) {
+ wake_up(&pipe->wq);
+ goto done;
+ }
+
+ /* Restart the pipeline if ready. */
+ if (vsp1_pipeline_ready(pipe))
+ vsp1_pipeline_run(pipe);
+
+done:
+ spin_unlock_irqrestore(&pipe->irqlock, flags);
+}
+
+/*
+ * Propagate the alpha value through the pipeline.
+ *
+ * As the UDS has restricted scaling capabilities when the alpha component needs
+ * to be scaled, we disable alpha scaling when the UDS input has a fixed alpha
+ * value. The UDS then outputs a fixed alpha value which needs to be programmed
+ * from the input RPF alpha.
+ */
+void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe,
+ struct vsp1_entity *input,
+ unsigned int alpha)
+{
+ struct vsp1_entity *entity;
+ struct media_pad *pad;
+
+ pad = media_entity_remote_pad(&input->pads[RWPF_PAD_SOURCE]);
+
+ while (pad) {
+ if (!is_media_entity_v4l2_subdev(pad->entity))
+ break;
+
+ entity = to_vsp1_entity(media_entity_to_v4l2_subdev(pad->entity));
+
+ /* The BRU background color has a fixed alpha value set to 255,
+ * the output alpha value is thus always equal to 255.
+ */
+ if (entity->type == VSP1_ENTITY_BRU)
+ alpha = 255;
+
+ if (entity->type == VSP1_ENTITY_UDS) {
+ struct vsp1_uds *uds = to_uds(&entity->subdev);
+
+ vsp1_uds_set_alpha(uds, alpha);
+ break;
+ }
+
+ pad = &entity->pads[entity->source_pad];
+ pad = media_entity_remote_pad(pad);
+ }
+}
+
+void vsp1_pipelines_suspend(struct vsp1_device *vsp1)
+{
+ unsigned long flags;
+ unsigned int i;
+ int ret;
+
+ /* To avoid increasing the system suspend time needlessly, loop over the
+ * pipelines twice, first to set them all to the stopping state, and
+ * then to wait for the stop to complete.
+ */
+ for (i = 0; i < vsp1->info->wpf_count; ++i) {
+ struct vsp1_rwpf *wpf = vsp1->wpf[i];
+ struct vsp1_pipeline *pipe;
+
+ if (wpf == NULL)
+ continue;
+
+ pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity);
+ if (pipe == NULL)
+ continue;
+
+ spin_lock_irqsave(&pipe->irqlock, flags);
+ if (pipe->state == VSP1_PIPELINE_RUNNING)
+ pipe->state = VSP1_PIPELINE_STOPPING;
+ spin_unlock_irqrestore(&pipe->irqlock, flags);
+ }
+
+ for (i = 0; i < vsp1->info->wpf_count; ++i) {
+ struct vsp1_rwpf *wpf = vsp1->wpf[i];
+ struct vsp1_pipeline *pipe;
+
+ if (wpf == NULL)
+ continue;
+
+ pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity);
+ if (pipe == NULL)
+ continue;
+
+ ret = wait_event_timeout(pipe->wq, vsp1_pipeline_stopped(pipe),
+ msecs_to_jiffies(500));
+ if (ret == 0)
+ dev_warn(vsp1->dev, "pipeline %u stop timeout\n",
+ wpf->entity.index);
+ }
+}
+
+void vsp1_pipelines_resume(struct vsp1_device *vsp1)
+{
+ unsigned int i;
+
+ /* Resume pipeline all running pipelines. */
+ for (i = 0; i < vsp1->info->wpf_count; ++i) {
+ struct vsp1_rwpf *wpf = vsp1->wpf[i];
+ struct vsp1_pipeline *pipe;
+
+ if (wpf == NULL)
+ continue;
+
+ pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity);
+ if (pipe == NULL)
+ continue;
+
+ if (vsp1_pipeline_ready(pipe))
+ vsp1_pipeline_run(pipe);
+ }
+}
--- /dev/null
+/*
+ * vsp1_pipe.h -- R-Car VSP1 Pipeline
+ *
+ * Copyright (C) 2013-2015 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef __VSP1_PIPE_H__
+#define __VSP1_PIPE_H__
+
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+
+#include <media/media-entity.h>
+
+struct vsp1_dl;
+struct vsp1_rwpf;
+
+/*
+ * struct vsp1_format_info - VSP1 video format description
+ * @mbus: media bus format code
+ * @fourcc: V4L2 pixel format FCC identifier
+ * @planes: number of planes
+ * @bpp: bits per pixel
+ * @hwfmt: VSP1 hardware format
+ * @swap_yc: the Y and C components are swapped (Y comes before C)
+ * @swap_uv: the U and V components are swapped (V comes before U)
+ * @hsub: horizontal subsampling factor
+ * @vsub: vertical subsampling factor
+ * @alpha: has an alpha channel
+ */
+struct vsp1_format_info {
+ u32 fourcc;
+ unsigned int mbus;
+ unsigned int hwfmt;
+ unsigned int swap;
+ unsigned int planes;
+ unsigned int bpp[3];
+ bool swap_yc;
+ bool swap_uv;
+ unsigned int hsub;
+ unsigned int vsub;
+ bool alpha;
+};
+
+enum vsp1_pipeline_state {
+ VSP1_PIPELINE_STOPPED,
+ VSP1_PIPELINE_RUNNING,
+ VSP1_PIPELINE_STOPPING,
+};
+
+/*
+ * struct vsp1_pipeline - A VSP1 hardware pipeline
+ * @pipe: the media pipeline
+ * @irqlock: protects the pipeline state
+ * @state: current state
+ * @wq: work queue to wait for state change completion
+ * @frame_end: frame end interrupt handler
+ * @lock: protects the pipeline use count and stream count
+ * @use_count: number of video nodes using the pipeline
+ * @stream_count: number of streaming video nodes
+ * @buffers_ready: bitmask of RPFs and WPFs with at least one buffer available
+ * @num_inputs: number of RPFs
+ * @inputs: array of RPFs in the pipeline (indexed by RPF index)
+ * @output: WPF at the output of the pipeline
+ * @bru: BRU entity, if present
+ * @lif: LIF entity, if present
+ * @uds: UDS entity, if present
+ * @uds_input: entity at the input of the UDS, if the UDS is present
+ * @entities: list of entities in the pipeline
+ * @dl: display list associated with the pipeline
+ */
+struct vsp1_pipeline {
+ struct media_pipeline pipe;
+
+ spinlock_t irqlock;
+ enum vsp1_pipeline_state state;
+ wait_queue_head_t wq;
+
+ void (*frame_end)(struct vsp1_pipeline *pipe);
+
+ struct mutex lock;
+ unsigned int use_count;
+ unsigned int stream_count;
+ unsigned int buffers_ready;
+
+ unsigned int num_inputs;
+ struct vsp1_rwpf *inputs[VSP1_MAX_RPF];
+ struct vsp1_rwpf *output;
+ struct vsp1_entity *bru;
+ struct vsp1_entity *lif;
+ struct vsp1_entity *uds;
+ struct vsp1_entity *uds_input;
+
+ struct list_head entities;
+
+ struct vsp1_dl *dl;
+};
+
+static inline struct vsp1_pipeline *to_vsp1_pipeline(struct media_entity *e)
+{
+ if (likely(e->pipe))
+ return container_of(e->pipe, struct vsp1_pipeline, pipe);
+ else
+ return NULL;
+}
+
+void vsp1_pipeline_reset(struct vsp1_pipeline *pipe);
+void vsp1_pipeline_init(struct vsp1_pipeline *pipe);
+
+void vsp1_pipeline_run(struct vsp1_pipeline *pipe);
+bool vsp1_pipeline_stopped(struct vsp1_pipeline *pipe);
+int vsp1_pipeline_stop(struct vsp1_pipeline *pipe);
+bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe);
+
+void vsp1_pipeline_display_start(struct vsp1_pipeline *pipe);
+void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe);
+
+void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe,
+ struct vsp1_entity *input,
+ unsigned int alpha);
+
+void vsp1_pipelines_suspend(struct vsp1_device *vsp1);
+void vsp1_pipelines_resume(struct vsp1_device *vsp1);
+
+const struct vsp1_format_info *vsp1_get_format_info(u32 fourcc);
+
+#endif /* __VSP1_PIPE_H__ */
#define VI6_DISP_IRQ_ENB_LNEE(n) (1 << (n))
#define VI6_DISP_IRQ_STA 0x007c
-#define VI6_DISP_IRQ_STA_DSE (1 << 8)
+#define VI6_DISP_IRQ_STA_DST (1 << 8)
#define VI6_DISP_IRQ_STA_MAE (1 << 5)
#define VI6_DISP_IRQ_STA_LNE(n) (1 << (n))
#define VI6_DPR_NODE_SRU 16
#define VI6_DPR_NODE_UDS(n) (17 + (n))
#define VI6_DPR_NODE_LUT 22
-#define VI6_DPR_NODE_BRU_IN(n) (23 + (n))
+#define VI6_DPR_NODE_BRU_IN(n) (((n) <= 3) ? 23 + (n) : 49)
#define VI6_DPR_NODE_BRU_OUT 27
#define VI6_DPR_NODE_CLU 29
#define VI6_DPR_NODE_HST 30
#define VI6_BRU_VIRRPF_COL_BCB_MASK (0xff << 0)
#define VI6_BRU_VIRRPF_COL_BCB_SHIFT 0
-#define VI6_BRU_CTRL(n) (0x2c10 + (n) * 8)
+#define VI6_BRU_CTRL(n) (0x2c10 + (n) * 8 + ((n) <= 3 ? 0 : 4))
#define VI6_BRU_CTRL_RBC (1 << 31)
-#define VI6_BRU_CTRL_DSTSEL_BRUIN(n) ((n) << 20)
+#define VI6_BRU_CTRL_DSTSEL_BRUIN(n) (((n) <= 3 ? (n) : (n)+1) << 20)
#define VI6_BRU_CTRL_DSTSEL_VRPF (4 << 20)
#define VI6_BRU_CTRL_DSTSEL_MASK (7 << 20)
-#define VI6_BRU_CTRL_SRCSEL_BRUIN(n) ((n) << 16)
+#define VI6_BRU_CTRL_SRCSEL_BRUIN(n) (((n) <= 3 ? (n) : (n)+1) << 16)
#define VI6_BRU_CTRL_SRCSEL_VRPF (4 << 16)
#define VI6_BRU_CTRL_SRCSEL_MASK (7 << 16)
#define VI6_BRU_CTRL_CROP(rop) ((rop) << 4)
#define VI6_BRU_CTRL_AROP(rop) ((rop) << 0)
#define VI6_BRU_CTRL_AROP_MASK (0xf << 0)
-#define VI6_BRU_BLD(n) (0x2c14 + (n) * 8)
+#define VI6_BRU_BLD(n) (0x2c14 + (n) * 8 + ((n) <= 3 ? 0 : 4))
#define VI6_BRU_BLD_CBES (1 << 31)
#define VI6_BRU_BLD_CCMDX_DST_A (0 << 28)
#define VI6_BRU_BLD_CCMDX_255_DST_A (1 << 28)
#define VI6_BRU_BLD_COEFY_SHIFT 0
#define VI6_BRU_ROP 0x2c30
-#define VI6_BRU_ROP_DSTSEL_BRUIN(n) ((n) << 20)
+#define VI6_BRU_ROP_DSTSEL_BRUIN(n) (((n) <= 3 ? (n) : (n)+1) << 20)
#define VI6_BRU_ROP_DSTSEL_VRPF (4 << 20)
#define VI6_BRU_ROP_DSTSEL_MASK (7 << 20)
#define VI6_BRU_ROP_CROP(rop) ((rop) << 4)
#define VI6_SECURITY_CTRL0 0x3d00
#define VI6_SECURITY_CTRL1 0x3d04
+/* -----------------------------------------------------------------------------
+ * IP Version Registers
+ */
+
+#define VI6_IP_VERSION 0x3f00
+#define VI6_IP_VERSION_MODEL_MASK (0xff << 8)
+#define VI6_IP_VERSION_MODEL_VSPS_H2 (0x09 << 8)
+#define VI6_IP_VERSION_MODEL_VSPR_H2 (0x0a << 8)
+#define VI6_IP_VERSION_MODEL_VSPD_GEN2 (0x0b << 8)
+#define VI6_IP_VERSION_MODEL_VSPS_M2 (0x0c << 8)
+#define VI6_IP_VERSION_MODEL_VSPI_GEN3 (0x14 << 8)
+#define VI6_IP_VERSION_MODEL_VSPBD_GEN3 (0x15 << 8)
+#define VI6_IP_VERSION_MODEL_VSPBC_GEN3 (0x16 << 8)
+#define VI6_IP_VERSION_MODEL_VSPD_GEN3 (0x17 << 8)
+#define VI6_IP_VERSION_SOC_MASK (0xff << 0)
+#define VI6_IP_VERSION_SOC_H (0x01 << 0)
+#define VI6_IP_VERSION_SOC_M (0x02 << 0)
+
/* -----------------------------------------------------------------------------
* RPF CLUT Registers
*/
* Device Access
*/
-static inline u32 vsp1_rpf_read(struct vsp1_rwpf *rpf, u32 reg)
-{
- return vsp1_read(rpf->entity.vsp1,
- reg + rpf->entity.index * VI6_RPF_OFFSET);
-}
-
static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf, u32 reg, u32 data)
{
- vsp1_write(rpf->entity.vsp1,
- reg + rpf->entity.index * VI6_RPF_OFFSET, data);
+ vsp1_mod_write(&rpf->entity, reg + rpf->entity.index * VI6_RPF_OFFSET,
+ data);
}
/* -----------------------------------------------------------------------------
static int rpf_s_stream(struct v4l2_subdev *subdev, int enable)
{
+ struct vsp1_pipeline *pipe = to_vsp1_pipeline(&subdev->entity);
struct vsp1_rwpf *rpf = to_rwpf(subdev);
- const struct vsp1_format_info *fmtinfo = rpf->video.fmtinfo;
- const struct v4l2_pix_format_mplane *format = &rpf->video.format;
+ struct vsp1_device *vsp1 = rpf->entity.vsp1;
+ const struct vsp1_format_info *fmtinfo = rpf->fmtinfo;
+ const struct v4l2_pix_format_mplane *format = &rpf->format;
const struct v4l2_rect *crop = &rpf->crop;
u32 pstride;
u32 infmt;
vsp1_rpf_write(rpf, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_AEXT_EXT |
(fmtinfo->alpha ? VI6_RPF_ALPH_SEL_ASEL_PACKED
: VI6_RPF_ALPH_SEL_ASEL_FIXED));
+
+ if (vsp1->info->uapi)
+ mutex_lock(rpf->ctrls.lock);
+ vsp1_rpf_write(rpf, VI6_RPF_VRTCOL_SET,
+ rpf->alpha->cur.val << VI6_RPF_VRTCOL_SET_LAYA_SHIFT);
+ vsp1_pipeline_propagate_alpha(pipe, &rpf->entity, rpf->alpha->cur.val);
+ if (vsp1->info->uapi)
+ mutex_unlock(rpf->ctrls.lock);
+
vsp1_rpf_write(rpf, VI6_RPF_MSK_CTRL, 0);
vsp1_rpf_write(rpf, VI6_RPF_CKEY_CTRL, 0);
* Video Device Operations
*/
-static void rpf_vdev_queue(struct vsp1_video *video,
- struct vsp1_video_buffer *buf)
+static void rpf_set_memory(struct vsp1_rwpf *rpf, struct vsp1_rwpf_memory *mem)
{
- struct vsp1_rwpf *rpf = container_of(video, struct vsp1_rwpf, video);
unsigned int i;
for (i = 0; i < 3; ++i)
- rpf->buf_addr[i] = buf->addr[i];
+ rpf->buf_addr[i] = mem->addr[i];
if (!vsp1_entity_is_streaming(&rpf->entity))
return;
vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y,
- buf->addr[0] + rpf->offsets[0]);
- if (buf->buf.vb2_buf.num_planes > 1)
+ mem->addr[0] + rpf->offsets[0]);
+ if (mem->num_planes > 1)
vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C0,
- buf->addr[1] + rpf->offsets[1]);
- if (buf->buf.vb2_buf.num_planes > 2)
+ mem->addr[1] + rpf->offsets[1]);
+ if (mem->num_planes > 2)
vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C1,
- buf->addr[2] + rpf->offsets[1]);
+ mem->addr[2] + rpf->offsets[1]);
}
-static const struct vsp1_video_operations rpf_vdev_ops = {
- .queue = rpf_vdev_queue,
+static const struct vsp1_rwpf_operations rpf_vdev_ops = {
+ .set_memory = rpf_set_memory,
};
/* -----------------------------------------------------------------------------
struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index)
{
struct v4l2_subdev *subdev;
- struct vsp1_video *video;
struct vsp1_rwpf *rpf;
int ret;
if (rpf == NULL)
return ERR_PTR(-ENOMEM);
+ rpf->ops = &rpf_vdev_ops;
+
rpf->max_width = RPF_MAX_WIDTH;
rpf->max_height = RPF_MAX_HEIGHT;
subdev = &rpf->entity.subdev;
v4l2_subdev_init(subdev, &rpf_ops);
- subdev->entity.ops = &vsp1_media_ops;
+ subdev->entity.ops = &vsp1->media_ops;
subdev->internal_ops = &vsp1_subdev_internal_ops;
snprintf(subdev->name, sizeof(subdev->name), "%s rpf.%u",
dev_name(vsp1->dev), index);
/* Initialize the control handler. */
v4l2_ctrl_handler_init(&rpf->ctrls, 1);
- v4l2_ctrl_new_std(&rpf->ctrls, &rpf_ctrl_ops, V4L2_CID_ALPHA_COMPONENT,
- 0, 255, 1, 255);
+ rpf->alpha = v4l2_ctrl_new_std(&rpf->ctrls, &rpf_ctrl_ops,
+ V4L2_CID_ALPHA_COMPONENT,
+ 0, 255, 1, 255);
rpf->entity.subdev.ctrl_handler = &rpf->ctrls;
goto error;
}
- /* Initialize the video device. */
- video = &rpf->video;
-
- video->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
- video->vsp1 = vsp1;
- video->ops = &rpf_vdev_ops;
-
- ret = vsp1_video_init(video, &rpf->entity);
- if (ret < 0)
- goto error;
-
- rpf->entity.video = video;
-
return rpf;
error:
vsp1_entity_destroy(&rpf->entity);
return ERR_PTR(ret);
}
-
-/*
- * vsp1_rpf_create_links() - RPF pads links creation
- * @vsp1: Pointer to VSP1 device
- * @entity: Pointer to VSP1 entity
- *
- * return negative error code or zero on success
- */
-int vsp1_rpf_create_links(struct vsp1_device *vsp1,
- struct vsp1_entity *entity)
-{
- struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev);
-
- /* Connect the video device to the RPF. */
- return media_create_pad_link(&rpf->video.video.entity, 0,
- &rpf->entity.subdev.entity,
- RWPF_PAD_SINK,
- MEDIA_LNK_FL_ENABLED |
- MEDIA_LNK_FL_IMMUTABLE);
-}
#include "vsp1.h"
#include "vsp1_entity.h"
-#include "vsp1_video.h"
#define RWPF_PAD_SINK 0
#define RWPF_PAD_SOURCE 1
+struct v4l2_ctrl;
+struct vsp1_rwpf;
+struct vsp1_video;
+
+struct vsp1_rwpf_memory {
+ unsigned int num_planes;
+ dma_addr_t addr[3];
+ unsigned int length[3];
+};
+
+struct vsp1_rwpf_operations {
+ void (*set_memory)(struct vsp1_rwpf *rwpf,
+ struct vsp1_rwpf_memory *mem);
+};
+
struct vsp1_rwpf {
struct vsp1_entity entity;
- struct vsp1_video video;
struct v4l2_ctrl_handler ctrls;
+ struct v4l2_ctrl *alpha;
+
+ struct vsp1_video *video;
+
+ const struct vsp1_rwpf_operations *ops;
unsigned int max_width;
unsigned int max_height;
+ struct v4l2_pix_format_mplane format;
+ const struct vsp1_format_info *fmtinfo;
struct {
unsigned int left;
unsigned int top;
struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index);
struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index);
-int vsp1_rpf_create_links(struct vsp1_device *vsp1,
- struct vsp1_entity *entity);
-int vsp1_wpf_create_links(struct vsp1_device *vsp1,
- struct vsp1_entity *entity);
-
int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_mbus_code_enum *code);
/* Take the control handler lock to ensure that the CTRL0 value won't be
* changed behind our back by a set control operation.
*/
- mutex_lock(sru->ctrls.lock);
+ if (sru->entity.vsp1->info->uapi)
+ mutex_lock(sru->ctrls.lock);
ctrl0 |= vsp1_sru_read(sru, VI6_SRU_CTRL0)
& (VI6_SRU_CTRL0_PARAM0_MASK | VI6_SRU_CTRL0_PARAM1_MASK);
- mutex_unlock(sru->ctrls.lock);
+ vsp1_sru_write(sru, VI6_SRU_CTRL0, ctrl0);
+ if (sru->entity.vsp1->info->uapi)
+ mutex_unlock(sru->ctrls.lock);
vsp1_sru_write(sru, VI6_SRU_CTRL1, VI6_SRU_CTRL1_PARAM5);
subdev = &sru->entity.subdev;
v4l2_subdev_init(subdev, &sru_ops);
- subdev->entity.ops = &vsp1_media_ops;
+ subdev->entity.ops = &vsp1->media_ops;
subdev->internal_ops = &vsp1_subdev_internal_ops;
snprintf(subdev->name, sizeof(subdev->name), "%s sru",
dev_name(vsp1->dev));
* Device Access
*/
-static inline u32 vsp1_uds_read(struct vsp1_uds *uds, u32 reg)
-{
- return vsp1_read(uds->entity.vsp1,
- reg + uds->entity.index * VI6_UDS_OFFSET);
-}
-
static inline void vsp1_uds_write(struct vsp1_uds *uds, u32 reg, u32 data)
{
vsp1_write(uds->entity.vsp1,
subdev = &uds->entity.subdev;
v4l2_subdev_init(subdev, &uds_ops);
- subdev->entity.ops = &vsp1_media_ops;
+ subdev->entity.ops = &vsp1->media_ops;
subdev->internal_ops = &vsp1_subdev_internal_ops;
snprintf(subdev->name, sizeof(subdev->name), "%s uds.%u",
dev_name(vsp1->dev), index);
#include <linux/list.h>
#include <linux/module.h>
#include <linux/mutex.h>
-#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/v4l2-mediabus.h>
#include <linux/videodev2.h>
+#include <linux/wait.h>
#include <media/media-entity.h>
#include <media/v4l2-dev.h>
#include "vsp1.h"
#include "vsp1_bru.h"
#include "vsp1_entity.h"
+#include "vsp1_pipe.h"
#include "vsp1_rwpf.h"
#include "vsp1_uds.h"
#include "vsp1_video.h"
* Helper functions
*/
-static const struct vsp1_format_info vsp1_video_formats[] = {
- { V4L2_PIX_FMT_RGB332, MEDIA_BUS_FMT_ARGB8888_1X32,
- VI6_FMT_RGB_332, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
- VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 1, { 8, 0, 0 }, false, false, 1, 1, false },
- { V4L2_PIX_FMT_ARGB444, MEDIA_BUS_FMT_ARGB8888_1X32,
- VI6_FMT_ARGB_4444, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
- VI6_RPF_DSWAP_P_WDS,
- 1, { 16, 0, 0 }, false, false, 1, 1, true },
- { V4L2_PIX_FMT_XRGB444, MEDIA_BUS_FMT_ARGB8888_1X32,
- VI6_FMT_XRGB_4444, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
- VI6_RPF_DSWAP_P_WDS,
- 1, { 16, 0, 0 }, false, false, 1, 1, true },
- { V4L2_PIX_FMT_ARGB555, MEDIA_BUS_FMT_ARGB8888_1X32,
- VI6_FMT_ARGB_1555, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
- VI6_RPF_DSWAP_P_WDS,
- 1, { 16, 0, 0 }, false, false, 1, 1, true },
- { V4L2_PIX_FMT_XRGB555, MEDIA_BUS_FMT_ARGB8888_1X32,
- VI6_FMT_XRGB_1555, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
- VI6_RPF_DSWAP_P_WDS,
- 1, { 16, 0, 0 }, false, false, 1, 1, false },
- { V4L2_PIX_FMT_RGB565, MEDIA_BUS_FMT_ARGB8888_1X32,
- VI6_FMT_RGB_565, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
- VI6_RPF_DSWAP_P_WDS,
- 1, { 16, 0, 0 }, false, false, 1, 1, false },
- { V4L2_PIX_FMT_BGR24, MEDIA_BUS_FMT_ARGB8888_1X32,
- VI6_FMT_BGR_888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
- VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 1, { 24, 0, 0 }, false, false, 1, 1, false },
- { V4L2_PIX_FMT_RGB24, MEDIA_BUS_FMT_ARGB8888_1X32,
- VI6_FMT_RGB_888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
- VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 1, { 24, 0, 0 }, false, false, 1, 1, false },
- { V4L2_PIX_FMT_ABGR32, MEDIA_BUS_FMT_ARGB8888_1X32,
- VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS,
- 1, { 32, 0, 0 }, false, false, 1, 1, true },
- { V4L2_PIX_FMT_XBGR32, MEDIA_BUS_FMT_ARGB8888_1X32,
- VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS,
- 1, { 32, 0, 0 }, false, false, 1, 1, false },
- { V4L2_PIX_FMT_ARGB32, MEDIA_BUS_FMT_ARGB8888_1X32,
- VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
- VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 1, { 32, 0, 0 }, false, false, 1, 1, true },
- { V4L2_PIX_FMT_XRGB32, MEDIA_BUS_FMT_ARGB8888_1X32,
- VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
- VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 1, { 32, 0, 0 }, false, false, 1, 1, false },
- { V4L2_PIX_FMT_UYVY, MEDIA_BUS_FMT_AYUV8_1X32,
- VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
- VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 1, { 16, 0, 0 }, false, false, 2, 1, false },
- { V4L2_PIX_FMT_VYUY, MEDIA_BUS_FMT_AYUV8_1X32,
- VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
- VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 1, { 16, 0, 0 }, false, true, 2, 1, false },
- { V4L2_PIX_FMT_YUYV, MEDIA_BUS_FMT_AYUV8_1X32,
- VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
- VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 1, { 16, 0, 0 }, true, false, 2, 1, false },
- { V4L2_PIX_FMT_YVYU, MEDIA_BUS_FMT_AYUV8_1X32,
- VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
- VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 1, { 16, 0, 0 }, true, true, 2, 1, false },
- { V4L2_PIX_FMT_NV12M, MEDIA_BUS_FMT_AYUV8_1X32,
- VI6_FMT_Y_UV_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
- VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 2, { 8, 16, 0 }, false, false, 2, 2, false },
- { V4L2_PIX_FMT_NV21M, MEDIA_BUS_FMT_AYUV8_1X32,
- VI6_FMT_Y_UV_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
- VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 2, { 8, 16, 0 }, false, true, 2, 2, false },
- { V4L2_PIX_FMT_NV16M, MEDIA_BUS_FMT_AYUV8_1X32,
- VI6_FMT_Y_UV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
- VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 2, { 8, 16, 0 }, false, false, 2, 1, false },
- { V4L2_PIX_FMT_NV61M, MEDIA_BUS_FMT_AYUV8_1X32,
- VI6_FMT_Y_UV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
- VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 2, { 8, 16, 0 }, false, true, 2, 1, false },
- { V4L2_PIX_FMT_YUV420M, MEDIA_BUS_FMT_AYUV8_1X32,
- VI6_FMT_Y_U_V_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
- VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 3, { 8, 8, 8 }, false, false, 2, 2, false },
-};
-
-/*
- * vsp1_get_format_info - Retrieve format information for a 4CC
- * @fourcc: the format 4CC
- *
- * Return a pointer to the format information structure corresponding to the
- * given V4L2 format 4CC, or NULL if no corresponding format can be found.
- */
-static const struct vsp1_format_info *vsp1_get_format_info(u32 fourcc)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(vsp1_video_formats); ++i) {
- const struct vsp1_format_info *info = &vsp1_video_formats[i];
-
- if (info->fourcc == fourcc)
- return info;
- }
-
- return NULL;
-}
-
-
static struct v4l2_subdev *
vsp1_video_remote_subdev(struct media_pad *local, u32 *pad)
{
if (ret < 0)
return ret == -ENOIOCTLCMD ? -EINVAL : ret;
- if (video->fmtinfo->mbus != fmt.format.code ||
- video->format.height != fmt.format.height ||
- video->format.width != fmt.format.width)
+ if (video->rwpf->fmtinfo->mbus != fmt.format.code ||
+ video->rwpf->format.height != fmt.format.height ||
+ video->rwpf->format.width != fmt.format.width)
return -EINVAL;
return 0;
* Pipeline Management
*/
-static int vsp1_pipeline_validate_branch(struct vsp1_pipeline *pipe,
- struct vsp1_rwpf *input,
- struct vsp1_rwpf *output)
+static int vsp1_video_pipeline_validate_branch(struct vsp1_pipeline *pipe,
+ struct vsp1_rwpf *input,
+ struct vsp1_rwpf *output)
{
struct vsp1_entity *entity;
struct media_entity_enum ent_enum;
return rval;
}
-static void __vsp1_pipeline_cleanup(struct vsp1_pipeline *pipe)
-{
- if (pipe->bru) {
- struct vsp1_bru *bru = to_bru(&pipe->bru->subdev);
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(bru->inputs); ++i)
- bru->inputs[i].rpf = NULL;
- }
-
- INIT_LIST_HEAD(&pipe->entities);
- pipe->state = VSP1_PIPELINE_STOPPED;
- pipe->buffers_ready = 0;
- pipe->num_video = 0;
- pipe->num_inputs = 0;
- pipe->output = NULL;
- pipe->bru = NULL;
- pipe->lif = NULL;
- pipe->uds = NULL;
-}
-
-static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe,
- struct vsp1_video *video)
+static int vsp1_video_pipeline_validate(struct vsp1_pipeline *pipe,
+ struct vsp1_video *video)
{
struct media_entity_graph graph;
struct media_entity *entity = &video->video.entity;
struct vsp1_rwpf *rwpf;
struct vsp1_entity *e;
- if (is_media_entity_v4l2_io(entity)) {
- pipe->num_video++;
+ if (!is_media_entity_v4l2_subdev(entity))
continue;
- }
subdev = media_entity_to_v4l2_subdev(entity);
e = to_vsp1_entity(subdev);
if (e->type == VSP1_ENTITY_RPF) {
rwpf = to_rwpf(subdev);
- pipe->inputs[pipe->num_inputs++] = rwpf;
- rwpf->video.pipe_index = pipe->num_inputs;
+ pipe->inputs[rwpf->entity.index] = rwpf;
+ rwpf->video->pipe_index = ++pipe->num_inputs;
} else if (e->type == VSP1_ENTITY_WPF) {
rwpf = to_rwpf(subdev);
- pipe->output = to_rwpf(subdev);
- rwpf->video.pipe_index = 0;
+ pipe->output = rwpf;
+ rwpf->video->pipe_index = 0;
} else if (e->type == VSP1_ENTITY_LIF) {
pipe->lif = e;
} else if (e->type == VSP1_ENTITY_BRU) {
/* Follow links downstream for each input and make sure the graph
* contains no loop and that all branches end at the output WPF.
*/
- for (i = 0; i < pipe->num_inputs; ++i) {
- ret = vsp1_pipeline_validate_branch(pipe, pipe->inputs[i],
- pipe->output);
+ for (i = 0; i < video->vsp1->info->rpf_count; ++i) {
+ if (!pipe->inputs[i])
+ continue;
+
+ ret = vsp1_video_pipeline_validate_branch(pipe, pipe->inputs[i],
+ pipe->output);
if (ret < 0)
goto error;
}
return 0;
error:
- __vsp1_pipeline_cleanup(pipe);
+ vsp1_pipeline_reset(pipe);
return ret;
}
-static int vsp1_pipeline_init(struct vsp1_pipeline *pipe,
- struct vsp1_video *video)
+static int vsp1_video_pipeline_init(struct vsp1_pipeline *pipe,
+ struct vsp1_video *video)
{
int ret;
/* If we're the first user validate and initialize the pipeline. */
if (pipe->use_count == 0) {
- ret = vsp1_pipeline_validate(pipe, video);
+ ret = vsp1_video_pipeline_validate(pipe, video);
if (ret < 0)
goto done;
}
return ret;
}
-static void vsp1_pipeline_cleanup(struct vsp1_pipeline *pipe)
+static void vsp1_video_pipeline_cleanup(struct vsp1_pipeline *pipe)
{
mutex_lock(&pipe->lock);
/* If we're the last user clean up the pipeline. */
if (--pipe->use_count == 0)
- __vsp1_pipeline_cleanup(pipe);
+ vsp1_pipeline_reset(pipe);
mutex_unlock(&pipe->lock);
}
-static void vsp1_pipeline_run(struct vsp1_pipeline *pipe)
-{
- struct vsp1_device *vsp1 = pipe->output->entity.vsp1;
-
- vsp1_write(vsp1, VI6_CMD(pipe->output->entity.index), VI6_CMD_STRCMD);
- pipe->state = VSP1_PIPELINE_RUNNING;
- pipe->buffers_ready = 0;
-}
-
-static bool vsp1_pipeline_stopped(struct vsp1_pipeline *pipe)
-{
- unsigned long flags;
- bool stopped;
-
- spin_lock_irqsave(&pipe->irqlock, flags);
- stopped = pipe->state == VSP1_PIPELINE_STOPPED;
- spin_unlock_irqrestore(&pipe->irqlock, flags);
-
- return stopped;
-}
-
-static int vsp1_pipeline_stop(struct vsp1_pipeline *pipe)
-{
- struct vsp1_entity *entity;
- unsigned long flags;
- int ret;
-
- spin_lock_irqsave(&pipe->irqlock, flags);
- if (pipe->state == VSP1_PIPELINE_RUNNING)
- pipe->state = VSP1_PIPELINE_STOPPING;
- spin_unlock_irqrestore(&pipe->irqlock, flags);
-
- ret = wait_event_timeout(pipe->wq, vsp1_pipeline_stopped(pipe),
- msecs_to_jiffies(500));
- ret = ret == 0 ? -ETIMEDOUT : 0;
-
- list_for_each_entry(entity, &pipe->entities, list_pipe) {
- if (entity->route && entity->route->reg)
- vsp1_write(entity->vsp1, entity->route->reg,
- VI6_DPR_NODE_UNUSED);
-
- v4l2_subdev_call(&entity->subdev, video, s_stream, 0);
- }
-
- return ret;
-}
-
-static bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe)
-{
- unsigned int mask;
-
- mask = ((1 << pipe->num_inputs) - 1) << 1;
- if (!pipe->lif)
- mask |= 1 << 0;
-
- return pipe->buffers_ready == mask;
-}
-
/*
* vsp1_video_complete_buffer - Complete the current buffer
* @video: the video node
*
* Return the next queued buffer or NULL if the queue is empty.
*/
-static struct vsp1_video_buffer *
+static struct vsp1_vb2_buffer *
vsp1_video_complete_buffer(struct vsp1_video *video)
{
struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity);
- struct vsp1_video_buffer *next = NULL;
- struct vsp1_video_buffer *done;
+ struct vsp1_vb2_buffer *next = NULL;
+ struct vsp1_vb2_buffer *done;
unsigned long flags;
unsigned int i;
}
done = list_first_entry(&video->irqqueue,
- struct vsp1_video_buffer, queue);
+ struct vsp1_vb2_buffer, queue);
/* In DU output mode reuse the buffer if the list is singular. */
if (pipe->lif && list_is_singular(&video->irqqueue)) {
if (!list_empty(&video->irqqueue))
next = list_first_entry(&video->irqqueue,
- struct vsp1_video_buffer, queue);
+ struct vsp1_vb2_buffer, queue);
spin_unlock_irqrestore(&video->irqlock, flags);
done->buf.sequence = video->sequence++;
done->buf.vb2_buf.timestamp = ktime_get_ns();
for (i = 0; i < done->buf.vb2_buf.num_planes; ++i)
- vb2_set_plane_payload(&done->buf.vb2_buf, i, done->length[i]);
+ vb2_set_plane_payload(&done->buf.vb2_buf, i,
+ done->mem.length[i]);
vb2_buffer_done(&done->buf.vb2_buf, VB2_BUF_STATE_DONE);
return next;
}
static void vsp1_video_frame_end(struct vsp1_pipeline *pipe,
- struct vsp1_video *video)
+ struct vsp1_rwpf *rwpf)
{
- struct vsp1_video_buffer *buf;
+ struct vsp1_video *video = rwpf->video;
+ struct vsp1_vb2_buffer *buf;
unsigned long flags;
buf = vsp1_video_complete_buffer(video);
spin_lock_irqsave(&pipe->irqlock, flags);
- video->ops->queue(video, buf);
+ video->rwpf->ops->set_memory(video->rwpf, &buf->mem);
pipe->buffers_ready |= 1 << video->pipe_index;
spin_unlock_irqrestore(&pipe->irqlock, flags);
}
-void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe)
+static void vsp1_video_pipeline_frame_end(struct vsp1_pipeline *pipe)
{
- enum vsp1_pipeline_state state;
- unsigned long flags;
+ struct vsp1_device *vsp1 = pipe->output->entity.vsp1;
unsigned int i;
- if (pipe == NULL)
- return;
-
/* Complete buffers on all video nodes. */
- for (i = 0; i < pipe->num_inputs; ++i)
- vsp1_video_frame_end(pipe, &pipe->inputs[i]->video);
-
- if (!pipe->lif)
- vsp1_video_frame_end(pipe, &pipe->output->video);
-
- spin_lock_irqsave(&pipe->irqlock, flags);
-
- state = pipe->state;
- pipe->state = VSP1_PIPELINE_STOPPED;
-
- /* If a stop has been requested, mark the pipeline as stopped and
- * return.
- */
- if (state == VSP1_PIPELINE_STOPPING) {
- wake_up(&pipe->wq);
- goto done;
- }
-
- /* Restart the pipeline if ready. */
- if (vsp1_pipeline_ready(pipe))
- vsp1_pipeline_run(pipe);
-
-done:
- spin_unlock_irqrestore(&pipe->irqlock, flags);
-}
-
-/*
- * Propagate the alpha value through the pipeline.
- *
- * As the UDS has restricted scaling capabilities when the alpha component needs
- * to be scaled, we disable alpha scaling when the UDS input has a fixed alpha
- * value. The UDS then outputs a fixed alpha value which needs to be programmed
- * from the input RPF alpha.
- */
-void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe,
- struct vsp1_entity *input,
- unsigned int alpha)
-{
- struct vsp1_entity *entity;
- struct media_pad *pad;
-
- pad = media_entity_remote_pad(&input->pads[RWPF_PAD_SOURCE]);
-
- while (pad) {
- if (!is_media_entity_v4l2_subdev(pad->entity))
- break;
-
- entity = to_vsp1_entity(media_entity_to_v4l2_subdev(pad->entity));
-
- /* The BRU background color has a fixed alpha value set to 255,
- * the output alpha value is thus always equal to 255.
- */
- if (entity->type == VSP1_ENTITY_BRU)
- alpha = 255;
-
- if (entity->type == VSP1_ENTITY_UDS) {
- struct vsp1_uds *uds = to_uds(&entity->subdev);
-
- vsp1_uds_set_alpha(uds, alpha);
- break;
- }
-
- pad = &entity->pads[entity->source_pad];
- pad = media_entity_remote_pad(pad);
- }
-}
-
-void vsp1_pipelines_suspend(struct vsp1_device *vsp1)
-{
- unsigned long flags;
- unsigned int i;
- int ret;
-
- /* To avoid increasing the system suspend time needlessly, loop over the
- * pipelines twice, first to set them all to the stopping state, and then
- * to wait for the stop to complete.
- */
- for (i = 0; i < vsp1->pdata.wpf_count; ++i) {
- struct vsp1_rwpf *wpf = vsp1->wpf[i];
- struct vsp1_pipeline *pipe;
-
- if (wpf == NULL)
- continue;
-
- pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity);
- if (pipe == NULL)
- continue;
-
- spin_lock_irqsave(&pipe->irqlock, flags);
- if (pipe->state == VSP1_PIPELINE_RUNNING)
- pipe->state = VSP1_PIPELINE_STOPPING;
- spin_unlock_irqrestore(&pipe->irqlock, flags);
- }
-
- for (i = 0; i < vsp1->pdata.wpf_count; ++i) {
- struct vsp1_rwpf *wpf = vsp1->wpf[i];
- struct vsp1_pipeline *pipe;
-
- if (wpf == NULL)
- continue;
-
- pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity);
- if (pipe == NULL)
+ for (i = 0; i < vsp1->info->rpf_count; ++i) {
+ if (!pipe->inputs[i])
continue;
- ret = wait_event_timeout(pipe->wq, vsp1_pipeline_stopped(pipe),
- msecs_to_jiffies(500));
- if (ret == 0)
- dev_warn(vsp1->dev, "pipeline %u stop timeout\n",
- wpf->entity.index);
+ vsp1_video_frame_end(pipe, pipe->inputs[i]);
}
-}
-
-void vsp1_pipelines_resume(struct vsp1_device *vsp1)
-{
- unsigned int i;
-
- /* Resume pipeline all running pipelines. */
- for (i = 0; i < vsp1->pdata.wpf_count; ++i) {
- struct vsp1_rwpf *wpf = vsp1->wpf[i];
- struct vsp1_pipeline *pipe;
- if (wpf == NULL)
- continue;
-
- pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity);
- if (pipe == NULL)
- continue;
-
- if (vsp1_pipeline_ready(pipe))
- vsp1_pipeline_run(pipe);
- }
+ if (!pipe->lif)
+ vsp1_video_frame_end(pipe, pipe->output);
}
/* -----------------------------------------------------------------------------
unsigned int sizes[], void *alloc_ctxs[])
{
struct vsp1_video *video = vb2_get_drv_priv(vq);
- const struct v4l2_pix_format_mplane *format = &video->format;
+ const struct v4l2_pix_format_mplane *format = &video->rwpf->format;
unsigned int i;
if (*nplanes) {
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct vsp1_video *video = vb2_get_drv_priv(vb->vb2_queue);
- struct vsp1_video_buffer *buf = to_vsp1_video_buffer(vbuf);
- const struct v4l2_pix_format_mplane *format = &video->format;
+ struct vsp1_vb2_buffer *buf = to_vsp1_vb2_buffer(vbuf);
+ const struct v4l2_pix_format_mplane *format = &video->rwpf->format;
unsigned int i;
if (vb->num_planes < format->num_planes)
return -EINVAL;
+ buf->mem.num_planes = vb->num_planes;
+
for (i = 0; i < vb->num_planes; ++i) {
- buf->addr[i] = vb2_dma_contig_plane_dma_addr(vb, i);
- buf->length[i] = vb2_plane_size(vb, i);
+ buf->mem.addr[i] = vb2_dma_contig_plane_dma_addr(vb, i);
+ buf->mem.length[i] = vb2_plane_size(vb, i);
- if (buf->length[i] < format->plane_fmt[i].sizeimage)
+ if (buf->mem.length[i] < format->plane_fmt[i].sizeimage)
return -EINVAL;
}
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct vsp1_video *video = vb2_get_drv_priv(vb->vb2_queue);
struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity);
- struct vsp1_video_buffer *buf = to_vsp1_video_buffer(vbuf);
+ struct vsp1_vb2_buffer *buf = to_vsp1_vb2_buffer(vbuf);
unsigned long flags;
bool empty;
spin_lock_irqsave(&pipe->irqlock, flags);
- video->ops->queue(video, buf);
+ video->rwpf->ops->set_memory(video->rwpf, &buf->mem);
pipe->buffers_ready |= 1 << video->pipe_index;
if (vb2_is_streaming(&video->queue) &&
spin_unlock_irqrestore(&pipe->irqlock, flags);
}
-static void vsp1_entity_route_setup(struct vsp1_entity *source)
-{
- struct vsp1_entity *sink;
-
- if (source->route->reg == 0)
- return;
-
- sink = container_of(source->sink, struct vsp1_entity, subdev.entity);
- vsp1_write(source->vsp1, source->route->reg,
- sink->route->inputs[source->sink_pad]);
-}
-
static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct vsp1_video *video = vb2_get_drv_priv(vq);
int ret;
mutex_lock(&pipe->lock);
- if (pipe->stream_count == pipe->num_video - 1) {
+ if (pipe->stream_count == pipe->num_inputs) {
if (pipe->uds) {
struct vsp1_uds *uds = to_uds(&pipe->uds->subdev);
struct vsp1_rwpf *rpf =
to_rwpf(&pipe->uds_input->subdev);
- uds->scale_alpha = rpf->video.fmtinfo->alpha;
+ uds->scale_alpha = rpf->fmtinfo->alpha;
}
}
{
struct vsp1_video *video = vb2_get_drv_priv(vq);
struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity);
- struct vsp1_video_buffer *buffer;
+ struct vsp1_vb2_buffer *buffer;
unsigned long flags;
int ret;
}
mutex_unlock(&pipe->lock);
- vsp1_pipeline_cleanup(pipe);
+ vsp1_video_pipeline_cleanup(pipe);
media_entity_pipeline_stop(&video->video.entity);
/* Remove all buffers from the IRQ queue. */
return -EINVAL;
mutex_lock(&video->lock);
- format->fmt.pix_mp = video->format;
+ format->fmt.pix_mp = video->rwpf->format;
mutex_unlock(&video->lock);
return 0;
goto done;
}
- video->format = format->fmt.pix_mp;
- video->fmtinfo = info;
+ video->rwpf->format = format->fmt.pix_mp;
+ video->rwpf->fmtinfo = info;
done:
mutex_unlock(&video->lock);
if (ret < 0)
goto err_stop;
- ret = vsp1_pipeline_init(pipe, video);
+ ret = vsp1_video_pipeline_init(pipe, video);
if (ret < 0)
goto err_stop;
return 0;
err_cleanup:
- vsp1_pipeline_cleanup(pipe);
+ vsp1_video_pipeline_cleanup(pipe);
err_stop:
media_entity_pipeline_stop(&video->video.entity);
return ret;
* Initialization and Cleanup
*/
-int vsp1_video_init(struct vsp1_video *video, struct vsp1_entity *rwpf)
+struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1,
+ struct vsp1_rwpf *rwpf)
{
+ struct vsp1_video *video;
const char *direction;
int ret;
- switch (video->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
- direction = "output";
- video->pad.flags = MEDIA_PAD_FL_SINK;
- break;
+ video = devm_kzalloc(vsp1->dev, sizeof(*video), GFP_KERNEL);
+ if (!video)
+ return ERR_PTR(-ENOMEM);
+
+ rwpf->video = video;
+
+ video->vsp1 = vsp1;
+ video->rwpf = rwpf;
- case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ if (rwpf->entity.type == VSP1_ENTITY_RPF) {
direction = "input";
+ video->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
video->pad.flags = MEDIA_PAD_FL_SOURCE;
video->video.vfl_dir = VFL_DIR_TX;
- break;
-
- default:
- return -EINVAL;
+ } else {
+ direction = "output";
+ video->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ video->pad.flags = MEDIA_PAD_FL_SINK;
+ video->video.vfl_dir = VFL_DIR_RX;
}
- video->rwpf = rwpf;
-
mutex_init(&video->lock);
spin_lock_init(&video->irqlock);
INIT_LIST_HEAD(&video->irqqueue);
- mutex_init(&video->pipe.lock);
- spin_lock_init(&video->pipe.irqlock);
- INIT_LIST_HEAD(&video->pipe.entities);
- init_waitqueue_head(&video->pipe.wq);
- video->pipe.state = VSP1_PIPELINE_STOPPED;
+ vsp1_pipeline_init(&video->pipe);
+ video->pipe.frame_end = vsp1_video_pipeline_frame_end;
/* Initialize the media entity... */
ret = media_entity_pads_init(&video->video.entity, 1, &video->pad);
if (ret < 0)
- return ret;
+ return ERR_PTR(ret);
/* ... and the format ... */
- video->fmtinfo = vsp1_get_format_info(VSP1_VIDEO_DEF_FORMAT);
- video->format.pixelformat = video->fmtinfo->fourcc;
- video->format.colorspace = V4L2_COLORSPACE_SRGB;
- video->format.field = V4L2_FIELD_NONE;
- video->format.width = VSP1_VIDEO_DEF_WIDTH;
- video->format.height = VSP1_VIDEO_DEF_HEIGHT;
- video->format.num_planes = 1;
- video->format.plane_fmt[0].bytesperline =
- video->format.width * video->fmtinfo->bpp[0] / 8;
- video->format.plane_fmt[0].sizeimage =
- video->format.plane_fmt[0].bytesperline * video->format.height;
+ rwpf->fmtinfo = vsp1_get_format_info(VSP1_VIDEO_DEF_FORMAT);
+ rwpf->format.pixelformat = rwpf->fmtinfo->fourcc;
+ rwpf->format.colorspace = V4L2_COLORSPACE_SRGB;
+ rwpf->format.field = V4L2_FIELD_NONE;
+ rwpf->format.width = VSP1_VIDEO_DEF_WIDTH;
+ rwpf->format.height = VSP1_VIDEO_DEF_HEIGHT;
+ rwpf->format.num_planes = 1;
+ rwpf->format.plane_fmt[0].bytesperline =
+ rwpf->format.width * rwpf->fmtinfo->bpp[0] / 8;
+ rwpf->format.plane_fmt[0].sizeimage =
+ rwpf->format.plane_fmt[0].bytesperline * rwpf->format.height;
/* ... and the video node... */
video->video.v4l2_dev = &video->vsp1->v4l2_dev;
video->video.fops = &vsp1_video_fops;
snprintf(video->video.name, sizeof(video->video.name), "%s %s",
- rwpf->subdev.name, direction);
+ rwpf->entity.subdev.name, direction);
video->video.vfl_type = VFL_TYPE_GRABBER;
video->video.release = video_device_release_empty;
video->video.ioctl_ops = &vsp1_video_ioctl_ops;
video->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
video->queue.lock = &video->lock;
video->queue.drv_priv = video;
- video->queue.buf_struct_size = sizeof(struct vsp1_video_buffer);
+ video->queue.buf_struct_size = sizeof(struct vsp1_vb2_buffer);
video->queue.ops = &vsp1_video_queue_qops;
video->queue.mem_ops = &vb2_dma_contig_memops;
video->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
goto error;
}
- return 0;
+ return video;
error:
vb2_dma_contig_cleanup_ctx(video->alloc_ctx);
vsp1_video_cleanup(video);
- return ret;
+ return ERR_PTR(ret);
}
void vsp1_video_cleanup(struct vsp1_video *video)
#include <linux/list.h>
#include <linux/spinlock.h>
-#include <linux/wait.h>
-#include <media/media-entity.h>
#include <media/videobuf2-v4l2.h>
-struct vsp1_video;
+#include "vsp1_pipe.h"
+#include "vsp1_rwpf.h"
-/*
- * struct vsp1_format_info - VSP1 video format description
- * @mbus: media bus format code
- * @fourcc: V4L2 pixel format FCC identifier
- * @planes: number of planes
- * @bpp: bits per pixel
- * @hwfmt: VSP1 hardware format
- * @swap_yc: the Y and C components are swapped (Y comes before C)
- * @swap_uv: the U and V components are swapped (V comes before U)
- * @hsub: horizontal subsampling factor
- * @vsub: vertical subsampling factor
- * @alpha: has an alpha channel
- */
-struct vsp1_format_info {
- u32 fourcc;
- unsigned int mbus;
- unsigned int hwfmt;
- unsigned int swap;
- unsigned int planes;
- unsigned int bpp[3];
- bool swap_yc;
- bool swap_uv;
- unsigned int hsub;
- unsigned int vsub;
- bool alpha;
-};
-
-enum vsp1_pipeline_state {
- VSP1_PIPELINE_STOPPED,
- VSP1_PIPELINE_RUNNING,
- VSP1_PIPELINE_STOPPING,
-};
-
-/*
- * struct vsp1_pipeline - A VSP1 hardware pipeline
- * @media: the media pipeline
- * @irqlock: protects the pipeline state
- * @lock: protects the pipeline use count and stream count
- */
-struct vsp1_pipeline {
- struct media_pipeline pipe;
-
- spinlock_t irqlock;
- enum vsp1_pipeline_state state;
- wait_queue_head_t wq;
-
- struct mutex lock;
- unsigned int use_count;
- unsigned int stream_count;
- unsigned int buffers_ready;
-
- unsigned int num_video;
- unsigned int num_inputs;
- struct vsp1_rwpf *inputs[VSP1_MAX_RPF];
- struct vsp1_rwpf *output;
- struct vsp1_entity *bru;
- struct vsp1_entity *lif;
- struct vsp1_entity *uds;
- struct vsp1_entity *uds_input;
-
- struct list_head entities;
-};
-
-static inline struct vsp1_pipeline *to_vsp1_pipeline(struct media_entity *e)
-{
- if (likely(e->pipe))
- return container_of(e->pipe, struct vsp1_pipeline, pipe);
- else
- return NULL;
-}
-
-struct vsp1_video_buffer {
+struct vsp1_vb2_buffer {
struct vb2_v4l2_buffer buf;
struct list_head queue;
-
- dma_addr_t addr[3];
- unsigned int length[3];
+ struct vsp1_rwpf_memory mem;
};
-static inline struct vsp1_video_buffer *
-to_vsp1_video_buffer(struct vb2_v4l2_buffer *vbuf)
+static inline struct vsp1_vb2_buffer *
+to_vsp1_vb2_buffer(struct vb2_v4l2_buffer *vbuf)
{
- return container_of(vbuf, struct vsp1_video_buffer, buf);
+ return container_of(vbuf, struct vsp1_vb2_buffer, buf);
}
-struct vsp1_video_operations {
- void (*queue)(struct vsp1_video *video, struct vsp1_video_buffer *buf);
-};
-
struct vsp1_video {
+ struct list_head list;
struct vsp1_device *vsp1;
- struct vsp1_entity *rwpf;
-
- const struct vsp1_video_operations *ops;
+ struct vsp1_rwpf *rwpf;
struct video_device video;
enum v4l2_buf_type type;
struct media_pad pad;
struct mutex lock;
- struct v4l2_pix_format_mplane format;
- const struct vsp1_format_info *fmtinfo;
struct vsp1_pipeline pipe;
unsigned int pipe_index;
return container_of(vdev, struct vsp1_video, video);
}
-int vsp1_video_init(struct vsp1_video *video, struct vsp1_entity *rwpf);
+struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1,
+ struct vsp1_rwpf *rwpf);
void vsp1_video_cleanup(struct vsp1_video *video);
-void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe);
-
-void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe,
- struct vsp1_entity *input,
- unsigned int alpha);
-
-void vsp1_pipelines_suspend(struct vsp1_device *vsp1);
-void vsp1_pipelines_resume(struct vsp1_device *vsp1);
-
#endif /* __VSP1_VIDEO_H__ */
static inline void vsp1_wpf_write(struct vsp1_rwpf *wpf, u32 reg, u32 data)
{
- vsp1_write(wpf->entity.vsp1,
- reg + wpf->entity.index * VI6_WPF_OFFSET, data);
+ vsp1_mod_write(&wpf->entity,
+ reg + wpf->entity.index * VI6_WPF_OFFSET, data);
}
/* -----------------------------------------------------------------------------
if (!enable) {
vsp1_write(vsp1, VI6_WPF_IRQ_ENB(wpf->entity.index), 0);
- vsp1_wpf_write(wpf, VI6_WPF_SRCRPF, 0);
+ vsp1_write(vsp1, wpf->entity.index * VI6_WPF_OFFSET +
+ VI6_WPF_SRCRPF, 0);
return 0;
}
* inputs as sub-layers and select the virtual RPF as the master
* layer.
*/
- for (i = 0; i < pipe->num_inputs; ++i) {
+ for (i = 0; i < vsp1->info->rpf_count; ++i) {
struct vsp1_rwpf *input = pipe->inputs[i];
+ if (!input)
+ continue;
+
srcrpf |= (!pipe->bru && pipe->num_inputs == 1)
? VI6_WPF_SRCRPF_RPF_ACT_MST(input->entity.index)
: VI6_WPF_SRCRPF_RPF_ACT_SUB(input->entity.index);
/* Destination stride. */
if (!pipe->lif) {
- struct v4l2_pix_format_mplane *format = &wpf->video.format;
+ struct v4l2_pix_format_mplane *format = &wpf->format;
vsp1_wpf_write(wpf, VI6_WPF_DSTM_STRIDE_Y,
format->plane_fmt[0].bytesperline);
/* Format */
if (!pipe->lif) {
- const struct vsp1_format_info *fmtinfo = wpf->video.fmtinfo;
+ const struct vsp1_format_info *fmtinfo = wpf->fmtinfo;
outfmt = fmtinfo->hwfmt << VI6_WPF_OUTFMT_WRFMT_SHIFT;
/* Take the control handler lock to ensure that the PDV value won't be
* changed behind our back by a set control operation.
*/
- mutex_lock(wpf->ctrls.lock);
- outfmt |= vsp1_wpf_read(wpf, VI6_WPF_OUTFMT) & VI6_WPF_OUTFMT_PDV_MASK;
+ if (vsp1->info->uapi)
+ mutex_lock(wpf->ctrls.lock);
+ outfmt |= wpf->alpha->cur.val << VI6_WPF_OUTFMT_PDV_SHIFT;
vsp1_wpf_write(wpf, VI6_WPF_OUTFMT, outfmt);
- mutex_unlock(wpf->ctrls.lock);
+ if (vsp1->info->uapi)
+ mutex_unlock(wpf->ctrls.lock);
- vsp1_write(vsp1, VI6_DPR_WPF_FPORCH(wpf->entity.index),
- VI6_DPR_WPF_FPORCH_FP_WPFN);
+ vsp1_mod_write(&wpf->entity, VI6_DPR_WPF_FPORCH(wpf->entity.index),
+ VI6_DPR_WPF_FPORCH_FP_WPFN);
- vsp1_write(vsp1, VI6_WPF_WRBCK_CTRL, 0);
+ vsp1_mod_write(&wpf->entity, VI6_WPF_WRBCK_CTRL, 0);
/* Enable interrupts */
vsp1_write(vsp1, VI6_WPF_IRQ_STA(wpf->entity.index), 0);
* Video Device Operations
*/
-static void wpf_vdev_queue(struct vsp1_video *video,
- struct vsp1_video_buffer *buf)
+static void wpf_set_memory(struct vsp1_rwpf *wpf, struct vsp1_rwpf_memory *mem)
{
- struct vsp1_rwpf *wpf = container_of(video, struct vsp1_rwpf, video);
-
- vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_Y, buf->addr[0]);
- if (buf->buf.vb2_buf.num_planes > 1)
- vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C0, buf->addr[1]);
- if (buf->buf.vb2_buf.num_planes > 2)
- vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C1, buf->addr[2]);
+ vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_Y, mem->addr[0]);
+ if (mem->num_planes > 1)
+ vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C0, mem->addr[1]);
+ if (mem->num_planes > 2)
+ vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C1, mem->addr[2]);
}
-static const struct vsp1_video_operations wpf_vdev_ops = {
- .queue = wpf_vdev_queue,
+static const struct vsp1_rwpf_operations wpf_vdev_ops = {
+ .set_memory = wpf_set_memory,
};
/* -----------------------------------------------------------------------------
struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index)
{
struct v4l2_subdev *subdev;
- struct vsp1_video *video;
struct vsp1_rwpf *wpf;
int ret;
if (wpf == NULL)
return ERR_PTR(-ENOMEM);
+ wpf->ops = &wpf_vdev_ops;
+
wpf->max_width = WPF_MAX_WIDTH;
wpf->max_height = WPF_MAX_HEIGHT;
subdev = &wpf->entity.subdev;
v4l2_subdev_init(subdev, &wpf_ops);
- subdev->entity.ops = &vsp1_media_ops;
+ subdev->entity.ops = &vsp1->media_ops;
subdev->internal_ops = &vsp1_subdev_internal_ops;
snprintf(subdev->name, sizeof(subdev->name), "%s wpf.%u",
dev_name(vsp1->dev), index);
/* Initialize the control handler. */
v4l2_ctrl_handler_init(&wpf->ctrls, 1);
- v4l2_ctrl_new_std(&wpf->ctrls, &wpf_ctrl_ops, V4L2_CID_ALPHA_COMPONENT,
- 0, 255, 1, 255);
+ wpf->alpha = v4l2_ctrl_new_std(&wpf->ctrls, &wpf_ctrl_ops,
+ V4L2_CID_ALPHA_COMPONENT,
+ 0, 255, 1, 255);
wpf->entity.subdev.ctrl_handler = &wpf->ctrls;
goto error;
}
- /* Initialize the video device. */
- video = &wpf->video;
-
- video->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
- video->vsp1 = vsp1;
- video->ops = &wpf_vdev_ops;
-
- ret = vsp1_video_init(video, &wpf->entity);
- if (ret < 0)
- goto error;
-
- wpf->entity.video = video;
- wpf->entity.sink = &wpf->video.video.entity;
-
return wpf;
error:
vsp1_entity_destroy(&wpf->entity);
return ERR_PTR(ret);
}
-
-/*
- * vsp1_wpf_create_links() - RPF pads links creation
- * @vsp1: Pointer to VSP1 device
- * @entity: Pointer to VSP1 entity
- *
- * return negative error code or zero on success
- */
-int vsp1_wpf_create_links(struct vsp1_device *vsp1,
- struct vsp1_entity *entity)
-{
- struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev);
- unsigned int flags;
-
- /* Connect the video device to the WPF. All connections are immutable
- * except for the WPF0 source link if a LIF is present.
- */
- flags = MEDIA_LNK_FL_ENABLED;
- if (!(vsp1->pdata.features & VSP1_HAS_LIF) || entity->index != 0)
- flags |= MEDIA_LNK_FL_IMMUTABLE;
-
- return media_create_pad_link(&wpf->entity.subdev.entity,
- RWPF_PAD_SOURCE,
- &wpf->video.video.entity, 0, flags);
-}
if (si476x_core_has_diversity(radio->core)) {
si476x_ctrls[SI476X_IDX_DIVERSITY_MODE].def =
si476x_phase_diversity_mode_to_idx(radio->core->diversity_mode);
- si476x_radio_add_new_custom(radio, SI476X_IDX_DIVERSITY_MODE);
+ rval = si476x_radio_add_new_custom(radio, SI476X_IDX_DIVERSITY_MODE);
if (rval < 0)
goto exit;
- si476x_radio_add_new_custom(radio, SI476X_IDX_INTERCHIP_LINK);
+ rval = si476x_radio_add_new_custom(radio, SI476X_IDX_INTERCHIP_LINK);
if (rval < 0)
goto exit;
}
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/delay.h>
snd_tea575x_write(tea, tea->val);
tea->freq = snd_tea575x_val_to_freq(tea, tea->val);
}
+EXPORT_SYMBOL(snd_tea575x_set_freq);
/*
* Linux Video interface
return 0;
}
+EXPORT_SYMBOL(snd_tea575x_init);
void snd_tea575x_exit(struct snd_tea575x *tea)
{
video_unregister_device(&tea->vd);
v4l2_ctrl_handler_free(tea->vd.ctrl_handler);
}
-
-static int __init alsa_tea575x_module_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_tea575x_module_exit(void)
-{
-}
-
-module_init(alsa_tea575x_module_init)
-module_exit(alsa_tea575x_module_exit)
-
-EXPORT_SYMBOL(snd_tea575x_init);
EXPORT_SYMBOL(snd_tea575x_exit);
-EXPORT_SYMBOL(snd_tea575x_set_freq);
/* RDS buffer blocks */
static u32 default_rds_buf = 300;
module_param(default_rds_buf, uint, 0444);
-MODULE_PARM_DESC(rds_buf, "RDS buffer entries");
+MODULE_PARM_DESC(default_rds_buf, "RDS buffer entries");
/* Radio Nr */
static u32 radio_nr = -1;
return retval;
}
+struct accel_times {
+ const char value;
+ unsigned int msecs;
+};
+
+static const struct accel_times accel[] = {
+ { 1, 125 },
+ { 2, 250 },
+ { 4, 500 },
+ { 6, 1000 },
+ { 9, 1500 },
+ { 13, 2000 },
+ { 20, 0 },
+};
+
/*
* ati_remote_compute_accel
*
*/
static int ati_remote_compute_accel(struct ati_remote *ati_remote)
{
- static const char accel[] = { 1, 2, 4, 6, 9, 13, 20 };
- unsigned long now = jiffies;
- int acc;
+ unsigned long now = jiffies, reset_time;
+ int i;
+
+ reset_time = msecs_to_jiffies(250);
- if (time_after(now, ati_remote->old_jiffies + msecs_to_jiffies(250))) {
- acc = 1;
+ if (time_after(now, ati_remote->old_jiffies + reset_time)) {
ati_remote->acc_jiffies = now;
+ return 1;
}
- else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(125)))
- acc = accel[0];
- else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(250)))
- acc = accel[1];
- else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(500)))
- acc = accel[2];
- else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(1000)))
- acc = accel[3];
- else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(1500)))
- acc = accel[4];
- else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(2000)))
- acc = accel[5];
- else
- acc = accel[6];
+ for (i = 0; i < ARRAY_SIZE(accel) - 1; i++) {
+ unsigned long timeout = msecs_to_jiffies(accel[i].msecs);
- return acc;
+ if (time_before(now, ati_remote->acc_jiffies + timeout))
+ return accel[i].value;
+ }
+ return accel[i].value;
}
/*
struct usb_endpoint_descriptor *ep;
struct igorplugusb *ir;
struct rc_dev *rc;
- int ret;
+ int ret = -ENOMEM;
udev = interface_to_usbdev(intf);
idesc = intf->cur_altsetting;
ir->urb = usb_alloc_urb(0, GFP_KERNEL);
if (!ir->urb)
- return -ENOMEM;
+ goto fail;
usb_fill_control_urb(ir->urb, udev,
usb_rcvctrlpipe(udev, 0), (uint8_t *)&ir->request,
usb_make_path(udev, ir->phys, sizeof(ir->phys));
rc = rc_allocate_device();
+ if (!rc)
+ goto fail;
+
rc->input_name = DRIVER_DESC;
rc->input_phys = ir->phys;
usb_to_input_id(udev, &rc->input_id);
ret = rc_register_device(rc);
if (ret) {
dev_err(&intf->dev, "failed to register rc device: %d", ret);
- rc_free_device(rc);
- usb_free_urb(ir->urb);
- return ret;
+ goto fail;
}
usb_set_intfdata(intf, ir);
igorplugusb_cmd(ir, SET_INFRABUFFER_EMPTY);
return 0;
+fail:
+ rc_free_device(ir->rc);
+ usb_free_urb(ir->urb);
+ del_timer(&ir->timer);
+
+ return ret;
}
static void igorplugusb_disconnect(struct usb_interface *intf)
/* Initial keytable is from Jose Alberto Reguero <jareguero@telefonica.net>
and Felipe Morales Moreno <felipe.morales.moreno@gmail.com> */
-/* FIXME: mappings are not 100% correct? */
+/* Keytable fixed by Philippe Valembois <lephilousophe@users.sourceforge.net> */
static struct rc_map_table avermedia_rm_ks[] = {
- { 0x0501, KEY_POWER2 },
- { 0x0502, KEY_CHANNELUP },
- { 0x0503, KEY_CHANNELDOWN },
- { 0x0504, KEY_VOLUMEUP },
- { 0x0505, KEY_VOLUMEDOWN },
- { 0x0506, KEY_MUTE },
- { 0x0507, KEY_RIGHT },
- { 0x0508, KEY_RED },
- { 0x0509, KEY_1 },
- { 0x050a, KEY_2 },
- { 0x050b, KEY_3 },
- { 0x050c, KEY_4 },
- { 0x050d, KEY_5 },
- { 0x050e, KEY_6 },
- { 0x050f, KEY_7 },
- { 0x0510, KEY_8 },
- { 0x0511, KEY_9 },
- { 0x0512, KEY_0 },
- { 0x0513, KEY_AUDIO },
- { 0x0515, KEY_EPG },
- { 0x0516, KEY_PLAY },
- { 0x0517, KEY_RECORD },
- { 0x0518, KEY_STOP },
- { 0x051c, KEY_BACK },
- { 0x051d, KEY_FORWARD },
- { 0x054d, KEY_LEFT },
- { 0x0556, KEY_ZOOM },
+ { 0x0501, KEY_POWER2 }, /* Power (RED POWER BUTTON) */
+ { 0x0502, KEY_CHANNELUP }, /* Channel+ */
+ { 0x0503, KEY_CHANNELDOWN }, /* Channel- */
+ { 0x0504, KEY_VOLUMEUP }, /* Volume+ */
+ { 0x0505, KEY_VOLUMEDOWN }, /* Volume- */
+ { 0x0506, KEY_MUTE }, /* Mute */
+ { 0x0507, KEY_AGAIN }, /* Recall */
+ { 0x0508, KEY_VIDEO }, /* Source */
+ { 0x0509, KEY_1 }, /* 1 */
+ { 0x050a, KEY_2 }, /* 2 */
+ { 0x050b, KEY_3 }, /* 3 */
+ { 0x050c, KEY_4 }, /* 4 */
+ { 0x050d, KEY_5 }, /* 5 */
+ { 0x050e, KEY_6 }, /* 6 */
+ { 0x050f, KEY_7 }, /* 7 */
+ { 0x0510, KEY_8 }, /* 8 */
+ { 0x0511, KEY_9 }, /* 9 */
+ { 0x0512, KEY_0 }, /* 0 */
+ { 0x0513, KEY_AUDIO }, /* Audio */
+ { 0x0515, KEY_EPG }, /* EPG */
+ { 0x0516, KEY_PLAYPAUSE }, /* Play/Pause */
+ { 0x0517, KEY_RECORD }, /* Record */
+ { 0x0518, KEY_STOP }, /* Stop */
+ { 0x051c, KEY_BACK }, /* << */
+ { 0x051d, KEY_FORWARD }, /* >> */
+ { 0x054d, KEY_INFO }, /* Display information */
+ { 0x0556, KEY_ZOOM }, /* Fullscreen */
};
static struct rc_map_list avermedia_rm_ks_map = {
{
struct irctl *ir = irctls[iminor(inode)];
struct cdev *cdev;
+ int ret;
if (!ir) {
printk(KERN_ERR "%s: called with invalid irctl\n", __func__);
dev_dbg(ir->d.dev, LOGHEAD "close called\n", ir->d.name, ir->d.minor);
- WARN_ON(mutex_lock_killable(&lirc_dev_lock));
+ ret = mutex_lock_killable(&lirc_dev_lock);
+ WARN_ON(ret);
rc_close(ir->d.rdev);
kfree(ir);
}
- mutex_unlock(&lirc_dev_lock);
+ if (!ret)
+ mutex_unlock(&lirc_dev_lock);
return 0;
}
if (len == 2)
dev_dbg(dev, "Get hw/sw rev?");
else
- dev_dbg(dev, "hw/sw rev 0x%02x 0x%02x 0x%02x 0x%02x",
- data1, data2,
- buf[start + 4], buf[start + 5]);
+ dev_dbg(dev, "hw/sw rev %*ph",
+ 4, &buf[start + 2]);
break;
case MCE_CMD_RESUME:
dev_dbg(dev, "Device resume requested");
#include "nuvoton-cir.h"
+static void nvt_clear_cir_wake_fifo(struct nvt_dev *nvt);
+
static const struct nvt_chip nvt_chips[] = {
{ "w83667hg", NVT_W83667HG },
{ "NCT6775F", NVT_6775F },
}
/* enter extended function mode */
-static inline void nvt_efm_enable(struct nvt_dev *nvt)
+static inline int nvt_efm_enable(struct nvt_dev *nvt)
{
+ if (!request_muxed_region(nvt->cr_efir, 2, NVT_DRIVER_NAME))
+ return -EBUSY;
+
/* Enabling Extended Function Mode explicitly requires writing 2x */
outb(EFER_EFM_ENABLE, nvt->cr_efir);
outb(EFER_EFM_ENABLE, nvt->cr_efir);
+
+ return 0;
}
/* exit extended function mode */
static inline void nvt_efm_disable(struct nvt_dev *nvt)
{
outb(EFER_EFM_DISABLE, nvt->cr_efir);
+
+ release_region(nvt->cr_efir, 2);
}
/*
*/
static inline void nvt_select_logical_dev(struct nvt_dev *nvt, u8 ldev)
{
- outb(CR_LOGICAL_DEV_SEL, nvt->cr_efir);
- outb(ldev, nvt->cr_efdr);
+ nvt_cr_write(nvt, ldev, CR_LOGICAL_DEV_SEL);
+}
+
+/* select and enable logical device with setting EFM mode*/
+static inline void nvt_enable_logical_dev(struct nvt_dev *nvt, u8 ldev)
+{
+ nvt_efm_enable(nvt);
+ nvt_select_logical_dev(nvt, ldev);
+ nvt_cr_write(nvt, LOGICAL_DEV_ENABLE, CR_LOGICAL_DEV_EN);
+ nvt_efm_disable(nvt);
+}
+
+/* select and disable logical device with setting EFM mode*/
+static inline void nvt_disable_logical_dev(struct nvt_dev *nvt, u8 ldev)
+{
+ nvt_efm_enable(nvt);
+ nvt_select_logical_dev(nvt, ldev);
+ nvt_cr_write(nvt, LOGICAL_DEV_DISABLE, CR_LOGICAL_DEV_EN);
+ nvt_efm_disable(nvt);
}
/* write val to cir config register */
return val;
}
+/* don't override io address if one is set already */
+static void nvt_set_ioaddr(struct nvt_dev *nvt, unsigned long *ioaddr)
+{
+ unsigned long old_addr;
+
+ old_addr = nvt_cr_read(nvt, CR_CIR_BASE_ADDR_HI) << 8;
+ old_addr |= nvt_cr_read(nvt, CR_CIR_BASE_ADDR_LO);
+
+ if (old_addr)
+ *ioaddr = old_addr;
+ else {
+ nvt_cr_write(nvt, *ioaddr >> 8, CR_CIR_BASE_ADDR_HI);
+ nvt_cr_write(nvt, *ioaddr & 0xff, CR_CIR_BASE_ADDR_LO);
+ }
+}
+
+static ssize_t wakeup_data_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rc_dev *rc_dev = to_rc_dev(dev);
+ struct nvt_dev *nvt = rc_dev->priv;
+ int fifo_len, duration;
+ unsigned long flags;
+ ssize_t buf_len = 0;
+ int i;
+
+ spin_lock_irqsave(&nvt->nvt_lock, flags);
+
+ fifo_len = nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_COUNT);
+ fifo_len = min(fifo_len, WAKEUP_MAX_SIZE);
+
+ /* go to first element to be read */
+ while (nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY_IDX))
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY);
+
+ for (i = 0; i < fifo_len; i++) {
+ duration = nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY);
+ duration = (duration & BUF_LEN_MASK) * SAMPLE_PERIOD;
+ buf_len += snprintf(buf + buf_len, PAGE_SIZE - buf_len,
+ "%d ", duration);
+ }
+ buf_len += snprintf(buf + buf_len, PAGE_SIZE - buf_len, "\n");
+
+ spin_unlock_irqrestore(&nvt->nvt_lock, flags);
+
+ return buf_len;
+}
+
+static ssize_t wakeup_data_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct rc_dev *rc_dev = to_rc_dev(dev);
+ struct nvt_dev *nvt = rc_dev->priv;
+ unsigned long flags;
+ u8 tolerance, config, wake_buf[WAKEUP_MAX_SIZE];
+ char **argv;
+ int i, count;
+ unsigned int val;
+ ssize_t ret;
+
+ argv = argv_split(GFP_KERNEL, buf, &count);
+ if (!argv)
+ return -ENOMEM;
+ if (!count || count > WAKEUP_MAX_SIZE) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ for (i = 0; i < count; i++) {
+ ret = kstrtouint(argv[i], 10, &val);
+ if (ret)
+ goto out;
+ val = DIV_ROUND_CLOSEST(val, SAMPLE_PERIOD);
+ if (!val || val > 0x7f) {
+ ret = -EINVAL;
+ goto out;
+ }
+ wake_buf[i] = val;
+ /* sequence must start with a pulse */
+ if (i % 2 == 0)
+ wake_buf[i] |= BUF_PULSE_BIT;
+ }
+
+ /* hardcode the tolerance to 10% */
+ tolerance = DIV_ROUND_UP(count, 10);
+
+ spin_lock_irqsave(&nvt->nvt_lock, flags);
+
+ nvt_clear_cir_wake_fifo(nvt);
+ nvt_cir_wake_reg_write(nvt, count, CIR_WAKE_FIFO_CMP_DEEP);
+ nvt_cir_wake_reg_write(nvt, tolerance, CIR_WAKE_FIFO_CMP_TOL);
+
+ config = nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRCON);
+
+ /* enable writes to wake fifo */
+ nvt_cir_wake_reg_write(nvt, config | CIR_WAKE_IRCON_MODE1,
+ CIR_WAKE_IRCON);
+
+ for (i = 0; i < count; i++)
+ nvt_cir_wake_reg_write(nvt, wake_buf[i], CIR_WAKE_WR_FIFO_DATA);
+
+ nvt_cir_wake_reg_write(nvt, config, CIR_WAKE_IRCON);
+
+ spin_unlock_irqrestore(&nvt->nvt_lock, flags);
+
+ ret = len;
+out:
+ argv_free(argv);
+ return ret;
+}
+static DEVICE_ATTR_RW(wakeup_data);
+
/* dump current cir register contents */
static void cir_dump_regs(struct nvt_dev *nvt)
{
/* detect hardware features */
-static void nvt_hw_detect(struct nvt_dev *nvt)
+static int nvt_hw_detect(struct nvt_dev *nvt)
{
const char *chip_name;
int chip_id;
nvt_efm_enable(nvt);
nvt->chip_major = nvt_cr_read(nvt, CR_CHIP_ID_HI);
}
-
nvt->chip_minor = nvt_cr_read(nvt, CR_CHIP_ID_LO);
+ nvt_efm_disable(nvt);
+
chip_id = nvt->chip_major << 8 | nvt->chip_minor;
+ if (chip_id == NVT_INVALID) {
+ dev_err(&nvt->pdev->dev,
+ "No device found on either EFM port\n");
+ return -ENODEV;
+ }
+
chip_name = nvt_find_chip(nvt, chip_id);
/* warn, but still let the driver load, if we don't know this chip */
"found %s or compatible: chip id: 0x%02x 0x%02x",
chip_name, nvt->chip_major, nvt->chip_minor);
- nvt_efm_disable(nvt);
+ return 0;
}
static void nvt_cir_ldev_init(struct nvt_dev *nvt)
val |= psval;
nvt_cr_write(nvt, val, psreg);
- /* Select CIR logical device and enable */
+ /* Select CIR logical device */
nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR);
- nvt_cr_write(nvt, LOGICAL_DEV_ENABLE, CR_LOGICAL_DEV_EN);
- nvt_cr_write(nvt, nvt->cir_addr >> 8, CR_CIR_BASE_ADDR_HI);
- nvt_cr_write(nvt, nvt->cir_addr & 0xff, CR_CIR_BASE_ADDR_LO);
+ nvt_set_ioaddr(nvt, &nvt->cir_addr);
nvt_cr_write(nvt, nvt->cir_irq, CR_CIR_IRQ_RSRC);
static void nvt_cir_wake_ldev_init(struct nvt_dev *nvt)
{
- /* Select ACPI logical device, enable it and CIR Wake */
+ /* Select ACPI logical device and anable it */
nvt_select_logical_dev(nvt, LOGICAL_DEV_ACPI);
nvt_cr_write(nvt, LOGICAL_DEV_ENABLE, CR_LOGICAL_DEV_EN);
/* enable pme interrupt of cir wakeup event */
nvt_set_reg_bit(nvt, PME_INTR_CIR_PASS_BIT, CR_ACPI_IRQ_EVENTS2);
- /* Select CIR Wake logical device and enable */
+ /* Select CIR Wake logical device */
nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR_WAKE);
- nvt_cr_write(nvt, LOGICAL_DEV_ENABLE, CR_LOGICAL_DEV_EN);
- nvt_cr_write(nvt, nvt->cir_wake_addr >> 8, CR_CIR_BASE_ADDR_HI);
- nvt_cr_write(nvt, nvt->cir_wake_addr & 0xff, CR_CIR_BASE_ADDR_LO);
+ nvt_set_ioaddr(nvt, &nvt->cir_wake_addr);
nvt_cr_write(nvt, nvt->cir_wake_irq, CR_CIR_IRQ_RSRC);
/* clear out the hardware's cir wake rx fifo */
static void nvt_clear_cir_wake_fifo(struct nvt_dev *nvt)
{
- u8 val;
+ u8 val, config;
+
+ config = nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRCON);
+
+ /* clearing wake fifo works in learning mode only */
+ nvt_cir_wake_reg_write(nvt, config & ~CIR_WAKE_IRCON_MODE0,
+ CIR_WAKE_IRCON);
val = nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFOCON);
nvt_cir_wake_reg_write(nvt, val | CIR_WAKE_FIFOCON_RXFIFOCLR,
CIR_WAKE_FIFOCON);
+
+ nvt_cir_wake_reg_write(nvt, config, CIR_WAKE_IRCON);
}
/* clear out the hardware's cir tx fifo */
/* and finally, enable interrupts */
nvt_set_cir_iren(nvt);
+
+ /* enable the CIR logical device */
+ nvt_enable_logical_dev(nvt, LOGICAL_DEV_CIR);
}
static void nvt_cir_wake_regs_init(struct nvt_dev *nvt)
/* clear any and all stray interrupts */
nvt_cir_wake_reg_write(nvt, 0xff, CIR_WAKE_IRSTS);
+
+ /* enable the CIR WAKE logical device */
+ nvt_enable_logical_dev(nvt, LOGICAL_DEV_CIR_WAKE);
}
static void nvt_enable_wake(struct nvt_dev *nvt)
{
+ unsigned long flags;
+
nvt_efm_enable(nvt);
nvt_select_logical_dev(nvt, LOGICAL_DEV_ACPI);
nvt_efm_disable(nvt);
+ spin_lock_irqsave(&nvt->nvt_lock, flags);
+
nvt_cir_wake_reg_write(nvt, CIR_WAKE_IRCON_MODE0 | CIR_WAKE_IRCON_RXEN |
CIR_WAKE_IRCON_R | CIR_WAKE_IRCON_RXINV |
CIR_WAKE_IRCON_SAMPLE_PERIOD_SEL,
CIR_WAKE_IRCON);
nvt_cir_wake_reg_write(nvt, 0xff, CIR_WAKE_IRSTS);
nvt_cir_wake_reg_write(nvt, 0, CIR_WAKE_IREN);
+
+ spin_unlock_irqrestore(&nvt->nvt_lock, flags);
}
#if 0 /* Currently unused */
/* copy data from hardware rx fifo into driver buffer */
static void nvt_get_rx_ir_data(struct nvt_dev *nvt)
{
- unsigned long flags;
u8 fifocount, val;
unsigned int b_idx;
bool overrun = false;
nvt_dbg("attempting to fetch %u bytes from hw rx fifo", fifocount);
- spin_lock_irqsave(&nvt->nvt_lock, flags);
-
b_idx = nvt->pkts;
/* This should never happen, but lets check anyway... */
if (overrun)
nvt_handle_rx_fifo_overrun(nvt);
-
- spin_unlock_irqrestore(&nvt->nvt_lock, flags);
}
static void nvt_cir_log_irqs(u8 status, u8 iren)
static bool nvt_cir_tx_inactive(struct nvt_dev *nvt)
{
unsigned long flags;
- bool tx_inactive;
u8 tx_state;
spin_lock_irqsave(&nvt->tx.lock, flags);
tx_state = nvt->tx.tx_state;
spin_unlock_irqrestore(&nvt->tx.lock, flags);
- tx_inactive = (tx_state == ST_TX_NONE);
-
- return tx_inactive;
+ return tx_state == ST_TX_NONE;
}
/* interrupt service routine for incoming and outgoing CIR data */
nvt_dbg_verbose("%s firing", __func__);
- nvt_efm_enable(nvt);
- nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR);
- nvt_efm_disable(nvt);
+ spin_lock_irqsave(&nvt->nvt_lock, flags);
/*
* Get IR Status register contents. Write 1 to ack/clear
* 0: CIR_IRSTS_GH - Min Length Detected
*/
status = nvt_cir_reg_read(nvt, CIR_IRSTS);
- if (!status) {
+ iren = nvt_cir_reg_read(nvt, CIR_IREN);
+
+ /* IRQ may be shared with CIR WAKE, therefore check for each
+ * status bit whether the related interrupt source is enabled
+ */
+ if (!(status & iren)) {
+ spin_unlock_irqrestore(&nvt->nvt_lock, flags);
nvt_dbg_verbose("%s exiting, IRSTS 0x0", __func__);
- nvt_cir_reg_write(nvt, 0xff, CIR_IRSTS);
return IRQ_NONE;
}
nvt_cir_reg_write(nvt, status, CIR_IRSTS);
nvt_cir_reg_write(nvt, 0, CIR_IRSTS);
- /* Interrupt may be shared with CIR Wake, bail if CIR not enabled */
- iren = nvt_cir_reg_read(nvt, CIR_IREN);
- if (!iren) {
- nvt_dbg_verbose("%s exiting, CIR not enabled", __func__);
- return IRQ_NONE;
- }
-
nvt_cir_log_irqs(status, iren);
if (status & CIR_IRSTS_RTR) {
if (nvt_cir_tx_inactive(nvt))
nvt_get_rx_ir_data(nvt);
- spin_lock_irqsave(&nvt->nvt_lock, flags);
-
cur_state = nvt->study_state;
- spin_unlock_irqrestore(&nvt->nvt_lock, flags);
-
if (cur_state == ST_STUDY_NONE)
nvt_clear_cir_fifo(nvt);
}
+ spin_unlock_irqrestore(&nvt->nvt_lock, flags);
+
if (status & CIR_IRSTS_TE)
nvt_clear_tx_fifo(nvt);
nvt_dbg_wake("%s firing", __func__);
+ spin_lock_irqsave(&nvt->nvt_lock, flags);
+
status = nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRSTS);
- if (!status)
+ iren = nvt_cir_wake_reg_read(nvt, CIR_WAKE_IREN);
+
+ /* IRQ may be shared with CIR, therefore check for each
+ * status bit whether the related interrupt source is enabled
+ */
+ if (!(status & iren)) {
+ spin_unlock_irqrestore(&nvt->nvt_lock, flags);
return IRQ_NONE;
+ }
if (status & CIR_WAKE_IRSTS_IR_PENDING)
nvt_clear_cir_wake_fifo(nvt);
nvt_cir_wake_reg_write(nvt, status, CIR_WAKE_IRSTS);
nvt_cir_wake_reg_write(nvt, 0, CIR_WAKE_IRSTS);
- /* Interrupt may be shared with CIR, bail if Wake not enabled */
- iren = nvt_cir_wake_reg_read(nvt, CIR_WAKE_IREN);
- if (!iren) {
- nvt_dbg_wake("%s exiting, wake not enabled", __func__);
- return IRQ_HANDLED;
- }
-
if ((status & CIR_WAKE_IRSTS_PE) &&
(nvt->wake_state == ST_WAKE_START)) {
while (nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY_IDX)) {
}
nvt_cir_wake_reg_write(nvt, 0, CIR_WAKE_IREN);
- spin_lock_irqsave(&nvt->nvt_lock, flags);
nvt->wake_state = ST_WAKE_FINISH;
- spin_unlock_irqrestore(&nvt->nvt_lock, flags);
}
+ spin_unlock_irqrestore(&nvt->nvt_lock, flags);
+
nvt_dbg_wake("%s done", __func__);
return IRQ_HANDLED;
}
-static void nvt_enable_cir(struct nvt_dev *nvt)
+static void nvt_disable_cir(struct nvt_dev *nvt)
{
- /* set function enable flags */
- nvt_cir_reg_write(nvt, CIR_IRCON_TXEN | CIR_IRCON_RXEN |
- CIR_IRCON_RXINV | CIR_IRCON_SAMPLE_PERIOD_SEL,
- CIR_IRCON);
-
- nvt_efm_enable(nvt);
-
- /* enable the CIR logical device */
- nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR);
- nvt_cr_write(nvt, LOGICAL_DEV_ENABLE, CR_LOGICAL_DEV_EN);
-
- nvt_efm_disable(nvt);
-
- /* clear all pending interrupts */
- nvt_cir_reg_write(nvt, 0xff, CIR_IRSTS);
+ unsigned long flags;
- /* enable interrupts */
- nvt_set_cir_iren(nvt);
-}
+ spin_lock_irqsave(&nvt->nvt_lock, flags);
-static void nvt_disable_cir(struct nvt_dev *nvt)
-{
/* disable CIR interrupts */
nvt_cir_reg_write(nvt, 0, CIR_IREN);
nvt_clear_cir_fifo(nvt);
nvt_clear_tx_fifo(nvt);
- nvt_efm_enable(nvt);
+ spin_unlock_irqrestore(&nvt->nvt_lock, flags);
/* disable the CIR logical device */
- nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR);
- nvt_cr_write(nvt, LOGICAL_DEV_DISABLE, CR_LOGICAL_DEV_EN);
-
- nvt_efm_disable(nvt);
+ nvt_disable_logical_dev(nvt, LOGICAL_DEV_CIR);
}
static int nvt_open(struct rc_dev *dev)
unsigned long flags;
spin_lock_irqsave(&nvt->nvt_lock, flags);
- nvt_enable_cir(nvt);
+
+ /* set function enable flags */
+ nvt_cir_reg_write(nvt, CIR_IRCON_TXEN | CIR_IRCON_RXEN |
+ CIR_IRCON_RXINV | CIR_IRCON_SAMPLE_PERIOD_SEL,
+ CIR_IRCON);
+
+ /* clear all pending interrupts */
+ nvt_cir_reg_write(nvt, 0xff, CIR_IRSTS);
+
+ /* enable interrupts */
+ nvt_set_cir_iren(nvt);
+
spin_unlock_irqrestore(&nvt->nvt_lock, flags);
+ /* enable the CIR logical device */
+ nvt_enable_logical_dev(nvt, LOGICAL_DEV_CIR);
+
return 0;
}
static void nvt_close(struct rc_dev *dev)
{
struct nvt_dev *nvt = dev->priv;
- unsigned long flags;
- spin_lock_irqsave(&nvt->nvt_lock, flags);
nvt_disable_cir(nvt);
- spin_unlock_irqrestore(&nvt->nvt_lock, flags);
}
/* Allocate memory, probe hardware, and initialize everything */
init_waitqueue_head(&nvt->tx.queue);
- nvt_hw_detect(nvt);
+ ret = nvt_hw_detect(nvt);
+ if (ret)
+ goto exit_free_dev_rdev;
/* Initialize CIR & CIR Wake Logical Devices */
nvt_efm_enable(nvt);
nvt_cir_wake_ldev_init(nvt);
nvt_efm_disable(nvt);
- /* Initialize CIR & CIR Wake Config Registers */
+ /*
+ * Initialize CIR & CIR Wake Config Registers
+ * and enable logical devices
+ */
nvt_cir_regs_init(nvt);
nvt_cir_wake_regs_init(nvt);
goto exit_unregister_device;
if (!devm_request_region(&pdev->dev, nvt->cir_wake_addr,
- CIR_IOREG_LENGTH, NVT_DRIVER_NAME))
+ CIR_IOREG_LENGTH, NVT_DRIVER_NAME "-wake"))
goto exit_unregister_device;
if (devm_request_irq(&pdev->dev, nvt->cir_wake_irq,
nvt_cir_wake_isr, IRQF_SHARED,
- NVT_DRIVER_NAME, (void *)nvt))
+ NVT_DRIVER_NAME "-wake", (void *)nvt))
+ goto exit_unregister_device;
+
+ ret = device_create_file(&rdev->dev, &dev_attr_wakeup_data);
+ if (ret)
goto exit_unregister_device;
device_init_wakeup(&pdev->dev, true);
static void nvt_remove(struct pnp_dev *pdev)
{
struct nvt_dev *nvt = pnp_get_drvdata(pdev);
- unsigned long flags;
- spin_lock_irqsave(&nvt->nvt_lock, flags);
- /* disable CIR */
- nvt_cir_reg_write(nvt, 0, CIR_IREN);
+ device_remove_file(&nvt->rdev->dev, &dev_attr_wakeup_data);
+
nvt_disable_cir(nvt);
+
/* enable CIR Wake (for IR power-on) */
nvt_enable_wake(nvt);
- spin_unlock_irqrestore(&nvt->nvt_lock, flags);
rc_unregister_device(nvt->rdev);
}
nvt_dbg("%s called", __func__);
- /* zero out misc state tracking */
- spin_lock_irqsave(&nvt->nvt_lock, flags);
- nvt->study_state = ST_STUDY_NONE;
- nvt->wake_state = ST_WAKE_NONE;
- spin_unlock_irqrestore(&nvt->nvt_lock, flags);
-
spin_lock_irqsave(&nvt->tx.lock, flags);
nvt->tx.tx_state = ST_TX_NONE;
spin_unlock_irqrestore(&nvt->tx.lock, flags);
+ spin_lock_irqsave(&nvt->nvt_lock, flags);
+
+ /* zero out misc state tracking */
+ nvt->study_state = ST_STUDY_NONE;
+ nvt->wake_state = ST_WAKE_NONE;
+
/* disable all CIR interrupts */
nvt_cir_reg_write(nvt, 0, CIR_IREN);
- nvt_efm_enable(nvt);
+ spin_unlock_irqrestore(&nvt->nvt_lock, flags);
/* disable cir logical dev */
- nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR);
- nvt_cr_write(nvt, LOGICAL_DEV_DISABLE, CR_LOGICAL_DEV_EN);
-
- nvt_efm_disable(nvt);
+ nvt_disable_logical_dev(nvt, LOGICAL_DEV_CIR);
/* make sure wake is enabled */
nvt_enable_wake(nvt);
nvt_dbg("%s called", __func__);
- /* open interrupt */
- nvt_set_cir_iren(nvt);
-
- /* Enable CIR logical device */
- nvt_efm_enable(nvt);
- nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR);
- nvt_cr_write(nvt, LOGICAL_DEV_ENABLE, CR_LOGICAL_DEV_EN);
-
- nvt_efm_disable(nvt);
-
nvt_cir_regs_init(nvt);
nvt_cir_wake_regs_init(nvt);
static void nvt_shutdown(struct pnp_dev *pdev)
{
struct nvt_dev *nvt = pnp_get_drvdata(pdev);
+
nvt_enable_wake(nvt);
}
NVT_W83667HG = 0xa510,
NVT_6775F = 0xb470,
NVT_6776F = 0xc330,
- NVT_6779D = 0xc560
+ NVT_6779D = 0xc560,
+ NVT_INVALID = 0xffff,
};
struct nvt_chip {
/* total length of CIR and CIR WAKE */
#define CIR_IOREG_LENGTH 0x0f
-/* RX limit length, 8 high bits for SLCH, 8 low bits for SLCL (0x7d0 = 2000) */
-#define CIR_RX_LIMIT_COUNT 0x7d0
+/* RX limit length, 8 high bits for SLCH, 8 low bits for SLCL */
+#define CIR_RX_LIMIT_COUNT (IR_DEFAULT_TIMEOUT / US_TO_NS(SAMPLE_PERIOD))
/* CIR Regs */
#define CIR_IRCON 0x00
#define CIR_WAKE_IREN_RTR 0x40
#define CIR_WAKE_IREN_PE 0x20
#define CIR_WAKE_IREN_RFO 0x10
-#define CIR_WAKE_IREN_TE 0x08
-#define CIR_WAKE_IREN_TTR 0x04
-#define CIR_WAKE_IREN_TFU 0x02
-#define CIR_WAKE_IREN_GH 0x01
+#define CIR_WAKE_IREN_GH 0x08
/* CIR WAKE FIFOCON settings */
#define CIR_WAKE_FIFOCON_RXFIFOCLR 0x08
/* as VISTA MCE definition, valid carrier value */
#define MAX_CARRIER 60000
#define MIN_CARRIER 30000
+
+/* max wakeup sequence length */
+#define WAKEUP_MAX_SIZE 65
#ifndef _RC_CORE_PRIV
#define _RC_CORE_PRIV
+/* Define the max number of pulse/space transitions to buffer */
+#define MAX_IR_EVENT_SIZE 512
+
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <media/rc-core.h>
struct list_head list; /* to keep track of raw clients */
struct task_struct *thread;
spinlock_t lock;
- struct kfifo_rec_ptr_1 kfifo; /* fifo for the pulse/space durations */
+ /* fifo for the pulse/space durations */
+ DECLARE_KFIFO(kfifo, struct ir_raw_event, MAX_IR_EVENT_SIZE);
ktime_t last_event; /* when last event occurred */
enum raw_event_type last_type; /* last event type */
struct rc_dev *dev; /* pointer to the parent rc_dev */
#include <linux/freezer.h>
#include "rc-core-priv.h"
-/* Define the max number of pulse/space transitions to buffer */
-#define MAX_IR_EVENT_SIZE 512
-
/* Used to keep track of IR raw clients, protected by ir_raw_handler_lock */
static LIST_HEAD(ir_raw_client_list);
struct ir_raw_event ev;
struct ir_raw_handler *handler;
struct ir_raw_event_ctrl *raw = (struct ir_raw_event_ctrl *)data;
- int retval;
while (!kthread_should_stop()) {
spin_lock_irq(&raw->lock);
- retval = kfifo_len(&raw->kfifo);
- if (retval < sizeof(ev)) {
+ if (!kfifo_len(&raw->kfifo)) {
set_current_state(TASK_INTERRUPTIBLE);
if (kthread_should_stop())
continue;
}
- retval = kfifo_out(&raw->kfifo, &ev, sizeof(ev));
+ if(!kfifo_out(&raw->kfifo, &ev, 1))
+ dev_err(&raw->dev->dev, "IR event FIFO is empty!\n");
spin_unlock_irq(&raw->lock);
mutex_lock(&ir_raw_handler_lock);
IR_dprintk(2, "sample: (%05dus %s)\n",
TO_US(ev->duration), TO_STR(ev->pulse));
- if (kfifo_in(&dev->raw->kfifo, ev, sizeof(*ev)) != sizeof(*ev))
- return -ENOMEM;
+ if (!kfifo_put(&dev->raw->kfifo, *ev)) {
+ dev_err(&dev->dev, "IR event FIFO is full!\n");
+ return -ENOSPC;
+ }
return 0;
}
dev->raw->dev = dev;
dev->change_protocol = change_protocol;
- rc = kfifo_alloc(&dev->raw->kfifo,
- sizeof(struct ir_raw_event) * MAX_IR_EVENT_SIZE,
- GFP_KERNEL);
- if (rc < 0)
- goto out;
+ INIT_KFIFO(dev->raw->kfifo);
spin_lock_init(&dev->raw->lock);
dev->raw->thread = kthread_run(ir_raw_event_thread, dev->raw,
handler->raw_unregister(dev);
mutex_unlock(&ir_raw_handler_lock);
- kfifo_free(&dev->raw->kfifo);
kfree(dev->raw);
dev->raw = NULL;
}
*/
#include <media/rc-core.h>
+#include <linux/atomic.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <linux/input.h>
return -EINVAL;
mutex_lock(&rdev->lock);
+
if (!rdev->users++ && rdev->open != NULL)
rval = rdev->open(rdev);
if (!dev)
return -EINVAL;
+ if (!atomic_read(&dev->initialized))
+ return -ERESTARTSYS;
+
mutex_lock(&dev->lock);
if (fattr->type == RC_FILTER_NORMAL) {
if (!dev)
return -EINVAL;
+ if (!atomic_read(&dev->initialized))
+ return -ERESTARTSYS;
+
if (fattr->type == RC_FILTER_NORMAL) {
IR_dprintk(1, "Normal protocol change requested\n");
current_protocols = &dev->enabled_protocols;
if (!dev)
return -EINVAL;
+ if (!atomic_read(&dev->initialized))
+ return -ERESTARTSYS;
+
+ mutex_lock(&dev->lock);
+
if (fattr->type == RC_FILTER_NORMAL)
filter = &dev->scancode_filter;
else
filter = &dev->scancode_wakeup_filter;
- mutex_lock(&dev->lock);
if (fattr->mask)
val = filter->mask;
else
if (!dev)
return -EINVAL;
+ if (!atomic_read(&dev->initialized))
+ return -ERESTARTSYS;
+
ret = kstrtoul(buf, 0, &val);
if (ret < 0)
return ret;
dev->minor = minor;
dev_set_name(&dev->dev, "rc%u", dev->minor);
dev_set_drvdata(&dev->dev, dev);
+ atomic_set(&dev->initialized, 0);
dev->dev.groups = dev->sysfs_groups;
dev->sysfs_groups[attr++] = &rc_dev_protocol_attr_grp;
dev->sysfs_groups[attr++] = &rc_dev_wakeup_protocol_attr_grp;
dev->sysfs_groups[attr++] = NULL;
- /*
- * Take the lock here, as the device sysfs node will appear
- * when device_add() is called, which may trigger an ir-keytable udev
- * rule, which will in turn call show_protocols and access
- * dev->enabled_protocols before it has been initialized.
- */
- mutex_lock(&dev->lock);
-
rc = device_add(&dev->dev);
if (rc)
goto out_unlock;
dev->input_dev->phys = dev->input_phys;
dev->input_dev->name = dev->input_name;
- /* input_register_device can call ir_open, so unlock mutex here */
- mutex_unlock(&dev->lock);
-
- rc = input_register_device(dev->input_dev);
-
- mutex_lock(&dev->lock);
-
- if (rc)
- goto out_table;
-
/*
* Default delay of 250ms is too short for some protocols, especially
* since the timeout is currently set to 250ms. Increase it to 500ms,
*/
dev->input_dev->rep[REP_PERIOD] = 125;
+ /* rc_open will be called here */
+ rc = input_register_device(dev->input_dev);
+ if (rc)
+ goto out_table;
+
path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
dev_info(&dev->dev, "%s as %s\n",
dev->input_name ?: "Unspecified device", path ?: "N/A");
request_module_nowait("ir-lirc-codec");
raw_init = true;
}
- /* calls ir_register_device so unlock mutex here*/
- mutex_unlock(&dev->lock);
rc = ir_raw_event_register(dev);
- mutex_lock(&dev->lock);
if (rc < 0)
goto out_input;
}
dev->enabled_protocols = rc_type;
}
+ /* Allow the RC sysfs nodes to be accessible */
+ mutex_lock(&dev->lock);
+ atomic_set(&dev->initialized, 1);
mutex_unlock(&dev->lock);
IR_dprintk(1, "Registered rc%u (driver: %s, remote: %s, mode %s)\n",
out_dev:
device_del(&dev->dev);
out_unlock:
- mutex_unlock(&dev->lock);
ida_simple_remove(&rc_ida, minor);
return rc;
}
{ .compatible = "allwinner,sun5i-a13-ir", },
{},
};
+MODULE_DEVICE_TABLE(of, sunxi_ir_match);
static struct platform_driver sunxi_ir_driver = {
.probe = sunxi_ir_probe,
dev_dbg(&dev->client->dev, "%s:\n", __func__);
ret = regmap_write(dev->regmap, 0x07, 0x6d);
- if (ret)
- goto err;
- usleep_range(5000, 10000);
-err:
- if (ret)
+ if (ret) {
dev_dbg(&dev->client->dev, "failed=%d\n", ret);
- return ret;
+ return ret;
+ }
+ usleep_range(5000, 10000);
+ return 0;
}
static int m88rs6000t_get_frequency(struct dvb_frontend *fe, u32 *frequency)
v4l2_std_id std, u32 delsys)
{
struct r820t_priv *priv = fe->tuner_priv;
- int rc = -EINVAL;
+ int rc;
u32 lo_freq;
tuner_dbg("should set frequency to %d kHz, bw %d MHz\n",
static const struct dvb_tuner_ops si2157_ops = {
.info = {
.name = "Silicon Labs Si2146/2147/2148/2157/2158",
- .frequency_min = 55000000,
- .frequency_max = 862000000,
+ .frequency_min = 42000000,
+ .frequency_max = 870000000,
},
.init = si2157_init,
}
static int si2157_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+ const struct i2c_device_id *id)
{
struct si2157_config *cfg = client->dev.platform_data;
struct dvb_frontend *fe = cfg->fe;
memcpy(&fe->ops.tuner_ops, &si2157_ops, sizeof(struct dvb_tuner_ops));
fe->tuner_priv = client;
+#ifdef CONFIG_MEDIA_CONTROLLER
+ if (cfg->mdev) {
+ dev->mdev = cfg->mdev;
+
+ dev->ent.name = KBUILD_MODNAME;
+ dev->ent.function = MEDIA_ENT_F_TUNER;
+
+ dev->pad[TUNER_PAD_RF_INPUT].flags = MEDIA_PAD_FL_SINK;
+ dev->pad[TUNER_PAD_OUTPUT].flags = MEDIA_PAD_FL_SOURCE;
+ dev->pad[TUNER_PAD_AUD_OUT].flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(&dev->ent, TUNER_NUM_PADS,
+ &dev->pad[0]);
+
+ if (ret)
+ goto err_kfree;
+
+ ret = media_device_register_entity(cfg->mdev, &dev->ent);
+ if (ret) {
+ media_entity_cleanup(&dev->ent);
+ goto err_kfree;
+ }
+ }
+#endif
+
dev_info(&client->dev, "Silicon Labs %s successfully attached\n",
dev->chiptype == SI2157_CHIPTYPE_SI2146 ?
"Si2146" : "Si2147/2148/2157/2158");
dev_dbg(&client->dev, "\n");
+ /* stop statistics polling */
+ cancel_delayed_work_sync(&dev->stat_work);
+
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ if (dev->mdev)
+ media_device_unregister_entity(&dev->ent);
+#endif
+
memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops));
fe->tuner_priv = NULL;
kfree(dev);
#define SI2157_H
#include <linux/kconfig.h>
+#include <media/media-device.h>
#include "dvb_frontend.h"
/*
*/
struct dvb_frontend *fe;
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ struct media_device *mdev;
+#endif
+
/*
* Spectral Inversion
*/
#define SI2157_PRIV_H
#include <linux/firmware.h>
+#include <media/v4l2-mc.h>
#include "si2157.h"
/* state struct */
u8 if_port;
u32 if_frequency;
struct delayed_work stat_work;
+
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ struct media_device *mdev;
+ struct media_entity ent;
+ struct media_pad pad[TUNER_NUM_PADS];
+#endif
+
};
#define SI2157_CHIPTYPE_SI2157 0
* in order to avoid troubles during device release.
*/
kfree(priv->ctrl.fname);
+ priv->ctrl.fname = NULL;
memcpy(&priv->ctrl, p, sizeof(priv->ctrl));
if (p->fname) {
priv->ctrl.fname = kstrdup(p->fname, GFP_KERNEL);
- if (priv->ctrl.fname == NULL)
+ if (priv->ctrl.fname == NULL) {
rc = -ENOMEM;
+ goto unlock;
+ }
}
/*
} else
priv->state = XC2028_WAITING_FIRMWARE;
}
+unlock:
mutex_unlock(&priv->lock);
return rc;
if (value >= 0x2000) {
value = 0;
} else {
- value = ~value << 3;
+ value = (~value << 3) & 0xffff;
}
goto ret;
};
struct airspy {
-#define POWER_ON (1 << 1)
-#define URB_BUF (1 << 2)
-#define USB_STATE_URB_BUF (1 << 3)
+#define POWER_ON 1
+#define USB_STATE_URB_BUF 2
unsigned long flags;
struct device *dev;
static int airspy_free_stream_bufs(struct airspy *s)
{
- if (s->flags & USB_STATE_URB_BUF) {
+ if (test_bit(USB_STATE_URB_BUF, &s->flags)) {
while (s->buf_num) {
s->buf_num--;
dev_dbg(s->dev, "free buf=%d\n", s->buf_num);
s->dma_addr[s->buf_num]);
}
}
- s->flags &= ~USB_STATE_URB_BUF;
+ clear_bit(USB_STATE_URB_BUF, &s->flags);
return 0;
}
dev_dbg(s->dev, "alloc buf=%d %p (dma %llu)\n", s->buf_num,
s->buf_list[s->buf_num],
(long long)s->dma_addr[s->buf_num]);
- s->flags |= USB_STATE_URB_BUF;
+ set_bit(USB_STATE_URB_BUF, &s->flags);
}
return 0;
struct as10x_cmd_t *cmd, *rsp;
/* bus adapter private ops callback */
- struct as102_priv_ops_t *ops;
+ const struct as102_priv_ops_t *ops;
};
struct as102_dev_t {
return actual_len;
}
-static struct as102_priv_ops_t as102_priv_ops = {
+static const struct as102_priv_ops_t as102_priv_ops = {
.upload_fw_pkt = as102_send_ep1,
.xfer_cmd = as102_usb_xfer_cmd,
.as102_read_ep2 = as102_read_ep2,
*/
#include "au0828.h"
+#include "au8522.h"
#include <linux/module.h>
#include <linux/slab.h>
{
#ifdef CONFIG_MEDIA_CONTROLLER
- if (dev->media_dev) {
+ if (dev->media_dev &&
+ media_devnode_is_registered(&dev->media_dev->devnode)) {
media_device_unregister(dev->media_dev);
media_device_cleanup(dev->media_dev);
- kfree(dev->media_dev);
dev->media_dev = NULL;
}
#endif
}
-static void au0828_usb_release(struct au0828_dev *dev)
+void au0828_usb_release(struct au0828_dev *dev)
{
au0828_unregister_media_device(dev);
kfree(dev);
}
-#ifdef CONFIG_VIDEO_AU0828_V4L2
-
-static void au0828_usb_v4l2_media_release(struct au0828_dev *dev)
-{
-#ifdef CONFIG_MEDIA_CONTROLLER
- int i;
-
- for (i = 0; i < AU0828_MAX_INPUT; i++) {
- if (AUVI_INPUT(i).type == AU0828_VMUX_UNDEFINED)
- return;
- media_device_unregister_entity(&dev->input_ent[i]);
- }
-#endif
-}
-
-static void au0828_usb_v4l2_release(struct v4l2_device *v4l2_dev)
-{
- struct au0828_dev *dev =
- container_of(v4l2_dev, struct au0828_dev, v4l2_dev);
-
- v4l2_ctrl_handler_free(&dev->v4l2_ctrl_hdl);
- v4l2_device_unregister(&dev->v4l2_dev);
- au0828_usb_v4l2_media_release(dev);
- au0828_usb_release(dev);
-}
-#endif
-
static void au0828_usb_disconnect(struct usb_interface *interface)
{
struct au0828_dev *dev = usb_get_intfdata(interface);
mutex_lock(&dev->mutex);
dev->usbdev = NULL;
mutex_unlock(&dev->mutex);
-#ifdef CONFIG_VIDEO_AU0828_V4L2
- if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) {
- au0828_analog_unregister(dev);
- v4l2_device_disconnect(&dev->v4l2_dev);
- v4l2_device_put(&dev->v4l2_dev);
+ if (au0828_analog_unregister(dev)) {
/*
* No need to call au0828_usb_release() if V4L2 is enabled,
* as this is already called via au0828_usb_v4l2_release()
*/
return;
}
-#endif
au0828_usb_release(dev);
}
#ifdef CONFIG_MEDIA_CONTROLLER
struct media_device *mdev;
- mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
+ mdev = media_device_get_devres(&udev->dev);
if (!mdev)
return -ENOMEM;
- mdev->dev = &udev->dev;
-
- if (!dev->board.name)
- strlcpy(mdev->model, "unknown au0828", sizeof(mdev->model));
- else
- strlcpy(mdev->model, dev->board.name, sizeof(mdev->model));
- if (udev->serial)
- strlcpy(mdev->serial, udev->serial, sizeof(mdev->serial));
- strcpy(mdev->bus_info, udev->devpath);
- mdev->hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
- mdev->driver_version = LINUX_VERSION_CODE;
-
- media_device_init(mdev);
+ /* check if media device is already initialized */
+ if (!mdev->dev)
+ media_device_usb_init(mdev, udev, udev->product);
dev->media_dev = mdev;
#endif
return 0;
}
+#ifdef CONFIG_MEDIA_CONTROLLER
+static void au0828_media_graph_notify(struct media_entity *new,
+ void *notify_data)
+{
+ struct au0828_dev *dev = (struct au0828_dev *) notify_data;
+ int ret;
+ struct media_entity *entity, *mixer = NULL, *decoder = NULL;
+
+ if (!new) {
+ /*
+ * Called during au0828 probe time to connect
+ * entites that were created prior to registering
+ * the notify handler. Find mixer and decoder.
+ */
+ media_device_for_each_entity(entity, dev->media_dev) {
+ if (entity->function == MEDIA_ENT_F_AUDIO_MIXER)
+ mixer = entity;
+ else if (entity->function == MEDIA_ENT_F_ATV_DECODER)
+ decoder = entity;
+ }
+ goto create_link;
+ }
+
+ switch (new->function) {
+ case MEDIA_ENT_F_AUDIO_MIXER:
+ mixer = new;
+ if (dev->decoder)
+ decoder = dev->decoder;
+ break;
+ case MEDIA_ENT_F_ATV_DECODER:
+ /* In case, Mixer is added first, find mixer and create link */
+ media_device_for_each_entity(entity, dev->media_dev) {
+ if (entity->function == MEDIA_ENT_F_AUDIO_MIXER)
+ mixer = entity;
+ }
+ decoder = new;
+ break;
+ default:
+ break;
+ }
+
+create_link:
+ if (decoder && mixer) {
+ ret = media_create_pad_link(decoder,
+ DEMOD_PAD_AUDIO_OUT,
+ mixer, 0,
+ MEDIA_LNK_FL_ENABLED);
+ if (ret)
+ dev_err(&dev->usbdev->dev,
+ "Mixer Pad Link Create Error: %d\n", ret);
+ }
+}
-static int au0828_create_media_graph(struct au0828_dev *dev)
+static int au0828_enable_source(struct media_entity *entity,
+ struct media_pipeline *pipe)
{
-#ifdef CONFIG_MEDIA_CONTROLLER
- struct media_device *mdev = dev->media_dev;
- struct media_entity *entity;
- struct media_entity *tuner = NULL, *decoder = NULL;
- int i, ret;
+ struct media_entity *source, *find_source;
+ struct media_entity *sink;
+ struct media_link *link, *found_link = NULL;
+ int ret = 0;
+ struct media_device *mdev = entity->graph_obj.mdev;
+ struct au0828_dev *dev;
if (!mdev)
- return 0;
+ return -ENODEV;
- media_device_for_each_entity(entity, mdev) {
- switch (entity->function) {
- case MEDIA_ENT_F_TUNER:
- tuner = entity;
- break;
- case MEDIA_ENT_F_ATV_DECODER:
- decoder = entity;
+ mutex_lock(&mdev->graph_mutex);
+
+ dev = mdev->source_priv;
+
+ /*
+ * For Audio and V4L2 entity, find the link to which decoder
+ * is the sink. Look for an active link between decoder and
+ * source (tuner/s-video/Composite), if one exists, nothing
+ * to do. If not, look for any active links between source
+ * and any other entity. If one exists, source is busy. If
+ * source is free, setup link and start pipeline from source.
+ * For DVB FE entity, the source for the link is the tuner.
+ * Check if tuner is available and setup link and start
+ * pipeline.
+ */
+ if (entity->function == MEDIA_ENT_F_DTV_DEMOD) {
+ sink = entity;
+ find_source = dev->tuner;
+ } else {
+ /* Analog isn't configured or register failed */
+ if (!dev->decoder) {
+ ret = -ENODEV;
+ goto end;
+ }
+
+ sink = dev->decoder;
+
+ /*
+ * Default input is tuner and default input_type
+ * is AU0828_VMUX_TELEVISION.
+ * FIXME:
+ * There is a problem when s_input is called to
+ * change the default input. s_input will try to
+ * enable_source before attempting to change the
+ * input on the device, and will end up enabling
+ * default source which is tuner.
+ *
+ * Additional logic is necessary in au0828
+ * to detect that the input has changed and
+ * enable the right source.
+ */
+
+ if (dev->input_type == AU0828_VMUX_TELEVISION)
+ find_source = dev->tuner;
+ else if (dev->input_type == AU0828_VMUX_SVIDEO ||
+ dev->input_type == AU0828_VMUX_COMPOSITE)
+ find_source = &dev->input_ent[dev->input_type];
+ else {
+ /* unknown input - let user select input */
+ ret = 0;
+ goto end;
+ }
+ }
+
+ /* Is an active link between sink and source */
+ if (dev->active_link) {
+ /*
+ * If DVB is using the tuner and calling entity is
+ * audio/video, the following check will be false,
+ * since sink is different. Result is Busy.
+ */
+ if (dev->active_link->sink->entity == sink &&
+ dev->active_link->source->entity == find_source) {
+ /*
+ * Either ALSA or Video own tuner. sink is
+ * the same for both. Prevent Video stepping
+ * on ALSA when ALSA owns the source.
+ */
+ if (dev->active_link_owner != entity &&
+ dev->active_link_owner->function ==
+ MEDIA_ENT_F_AUDIO_CAPTURE) {
+ pr_debug("ALSA has the tuner\n");
+ ret = -EBUSY;
+ goto end;
+ }
+ ret = 0;
+ goto end;
+ } else {
+ ret = -EBUSY;
+ goto end;
+ }
+ }
+
+ list_for_each_entry(link, &sink->links, list) {
+ /* Check sink, and source */
+ if (link->sink->entity == sink &&
+ link->source->entity == find_source) {
+ found_link = link;
break;
}
}
- /* Analog setup, using tuner as a link */
+ if (!found_link) {
+ ret = -ENODEV;
+ goto end;
+ }
- /* Something bad happened! */
- if (!decoder)
- return -EINVAL;
+ /* activate link between source and sink and start pipeline */
+ source = found_link->source->entity;
+ ret = __media_entity_setup_link(found_link, MEDIA_LNK_FL_ENABLED);
+ if (ret) {
+ pr_err("Activate tuner link %s->%s. Error %d\n",
+ source->name, sink->name, ret);
+ goto end;
+ }
- if (tuner) {
- ret = media_create_pad_link(tuner, TUNER_PAD_IF_OUTPUT,
- decoder, 0,
- MEDIA_LNK_FL_ENABLED);
+ ret = __media_entity_pipeline_start(entity, pipe);
+ if (ret) {
+ pr_err("Start Pipeline: %s->%s Error %d\n",
+ source->name, entity->name, ret);
+ ret = __media_entity_setup_link(found_link, 0);
+ pr_err("Deactivate link Error %d\n", ret);
+ goto end;
+ }
+ /*
+ * save active link and active link owner to avoid audio
+ * deactivating video owned link from disable_source and
+ * vice versa
+ */
+ dev->active_link = found_link;
+ dev->active_link_owner = entity;
+ dev->active_source = source;
+ dev->active_sink = sink;
+
+ pr_debug("Enabled Source: %s->%s->%s Ret %d\n",
+ dev->active_source->name, dev->active_sink->name,
+ dev->active_link_owner->name, ret);
+end:
+ mutex_unlock(&mdev->graph_mutex);
+ pr_debug("au0828_enable_source() end %s %d %d\n",
+ entity->name, entity->function, ret);
+ return ret;
+}
+
+static void au0828_disable_source(struct media_entity *entity)
+{
+ int ret = 0;
+ struct media_device *mdev = entity->graph_obj.mdev;
+ struct au0828_dev *dev;
+
+ if (!mdev)
+ return;
+
+ mutex_lock(&mdev->graph_mutex);
+ dev = mdev->source_priv;
+
+ if (!dev->active_link) {
+ ret = -ENODEV;
+ goto end;
+ }
+
+ /* link is active - stop pipeline from source (tuner) */
+ if (dev->active_link->sink->entity == dev->active_sink &&
+ dev->active_link->source->entity == dev->active_source) {
+ /*
+ * prevent video from deactivating link when audio
+ * has active pipeline
+ */
+ if (dev->active_link_owner != entity)
+ goto end;
+ __media_entity_pipeline_stop(entity);
+ ret = __media_entity_setup_link(dev->active_link, 0);
if (ret)
- return ret;
+ pr_err("Deactivate link Error %d\n", ret);
+
+ pr_debug("Disabled Source: %s->%s->%s Ret %d\n",
+ dev->active_source->name, dev->active_sink->name,
+ dev->active_link_owner->name, ret);
+
+ dev->active_link = NULL;
+ dev->active_link_owner = NULL;
+ dev->active_source = NULL;
+ dev->active_sink = NULL;
}
- ret = media_create_pad_link(decoder, 1, &dev->vdev.entity, 0,
- MEDIA_LNK_FL_ENABLED);
- if (ret)
- return ret;
- ret = media_create_pad_link(decoder, 2, &dev->vbi_dev.entity, 0,
- MEDIA_LNK_FL_ENABLED);
- if (ret)
- return ret;
- for (i = 0; i < AU0828_MAX_INPUT; i++) {
- struct media_entity *ent = &dev->input_ent[i];
+end:
+ mutex_unlock(&mdev->graph_mutex);
+}
+#endif
- if (AUVI_INPUT(i).type == AU0828_VMUX_UNDEFINED)
- break;
+static int au0828_media_device_register(struct au0828_dev *dev,
+ struct usb_device *udev)
+{
+#ifdef CONFIG_MEDIA_CONTROLLER
+ int ret;
+ struct media_entity *entity, *demod = NULL, *tuner = NULL;
- switch (AUVI_INPUT(i).type) {
- case AU0828_VMUX_CABLE:
- case AU0828_VMUX_TELEVISION:
- case AU0828_VMUX_DVB:
- if (!tuner)
- break;
-
- ret = media_create_pad_link(ent, 0, tuner,
- TUNER_PAD_RF_INPUT,
- MEDIA_LNK_FL_ENABLED);
- if (ret)
- return ret;
- break;
- case AU0828_VMUX_COMPOSITE:
- case AU0828_VMUX_SVIDEO:
- default: /* AU0828_VMUX_DEBUG */
- /* FIXME: fix the decoder PAD */
- ret = media_create_pad_link(ent, 0, decoder, 0, 0);
- if (ret)
- return ret;
- break;
+ if (!dev->media_dev)
+ return 0;
+
+ if (!media_devnode_is_registered(&dev->media_dev->devnode)) {
+
+ /* register media device */
+ ret = media_device_register(dev->media_dev);
+ if (ret) {
+ dev_err(&udev->dev,
+ "Media Device Register Error: %d\n", ret);
+ return ret;
+ }
+ } else {
+ /*
+ * Call au0828_media_graph_notify() to connect
+ * audio graph to our graph. In this case, audio
+ * driver registered the device and there is no
+ * entity_notify to be called when new entities
+ * are added. Invoke it now.
+ */
+ au0828_media_graph_notify(NULL, (void *) dev);
+ }
+
+ /*
+ * Find tuner and demod to disable the link between
+ * the two to avoid disable step when tuner is requested
+ * by video or audio. Note that this step can't be done
+ * until dvb graph is created during dvb register.
+ */
+ media_device_for_each_entity(entity, dev->media_dev) {
+ if (entity->function == MEDIA_ENT_F_DTV_DEMOD)
+ demod = entity;
+ else if (entity->function == MEDIA_ENT_F_TUNER)
+ tuner = entity;
+ }
+ /* Disable link between tuner and demod */
+ if (tuner && demod) {
+ struct media_link *link;
+
+ list_for_each_entry(link, &demod->links, list) {
+ if (link->sink->entity == demod &&
+ link->source->entity == tuner) {
+ media_entity_setup_link(link, 0);
+ }
}
}
+
+ /* register entity_notify callback */
+ dev->entity_notify.notify_data = (void *) dev;
+ dev->entity_notify.notify = (void *) au0828_media_graph_notify;
+ ret = media_device_register_entity_notify(dev->media_dev,
+ &dev->entity_notify);
+ if (ret) {
+ dev_err(&udev->dev,
+ "Media Device register entity_notify Error: %d\n",
+ ret);
+ return ret;
+ }
+ /* set enable_source */
+ dev->media_dev->source_priv = (void *) dev;
+ dev->media_dev->enable_source = au0828_enable_source;
+ dev->media_dev->disable_source = au0828_disable_source;
#endif
return 0;
}
return retval;
}
-#ifdef CONFIG_VIDEO_AU0828_V4L2
- dev->v4l2_dev.release = au0828_usb_v4l2_release;
-
- /* Create the v4l2_device */
-#ifdef CONFIG_MEDIA_CONTROLLER
- dev->v4l2_dev.mdev = dev->media_dev;
-#endif
- retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev);
+ retval = au0828_v4l2_device_register(interface, dev);
if (retval) {
- pr_err("%s() v4l2_device_register failed\n",
- __func__);
+ au0828_usb_v4l2_media_release(dev);
mutex_unlock(&dev->lock);
kfree(dev);
return retval;
}
- /* This control handler will inherit the controls from au8522 */
- retval = v4l2_ctrl_handler_init(&dev->v4l2_ctrl_hdl, 4);
- if (retval) {
- pr_err("%s() v4l2_ctrl_handler_init failed\n",
- __func__);
- mutex_unlock(&dev->lock);
- kfree(dev);
- return retval;
- }
- dev->v4l2_dev.ctrl_handler = &dev->v4l2_ctrl_hdl;
-#endif
/* Power Up the bridge */
au0828_write(dev, REG_600, 1 << 4);
/* Setup */
au0828_card_setup(dev);
-#ifdef CONFIG_VIDEO_AU0828_V4L2
/* Analog TV */
- if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED)
- au0828_analog_register(dev, interface);
-#endif
+ retval = au0828_analog_register(dev, interface);
+ if (retval) {
+ pr_err("%s() au0282_dev_register failed to register on V4L2\n",
+ __func__);
+ goto done;
+ }
/* Digital TV */
retval = au0828_dvb_register(dev);
mutex_unlock(&dev->lock);
- retval = au0828_create_media_graph(dev);
- if (retval) {
- pr_err("%s() au0282_dev_register failed to create graph\n",
- __func__);
- goto done;
- }
-
-#ifdef CONFIG_MEDIA_CONTROLLER
- retval = media_device_register(dev->media_dev);
-#endif
+ retval = au0828_media_device_register(dev, usbdev);
done:
if (retval < 0)
static int start_urb_transfer(struct au0828_dev *dev)
{
struct urb *purb;
- int i, ret = -ENOMEM;
+ int i, ret;
dprintk(2, "%s()\n", __func__);
dev->urbs[i] = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->urbs[i])
- goto err;
+ return -ENOMEM;
purb = dev->urbs[i];
if (!purb->transfer_buffer) {
usb_free_urb(purb);
dev->urbs[i] = NULL;
+ ret = -ENOMEM;
pr_err("%s: failed big buffer allocation, err = %d\n",
__func__, ret);
- goto err;
+ return ret;
}
purb->status = -EINPROGRESS;
}
dev->urb_streaming = true;
- ret = 0;
-
-err:
- return ret;
+ return 0;
}
static void au0828_start_transport(struct au0828_dev *dev)
*/
#include "au0828.h"
+#include "au8522.h"
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/device.h>
#include <media/v4l2-common.h>
+#include <media/v4l2-mc.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-event.h>
#include <media/tuner.h>
return rc;
}
-static int au0828_enable_analog_tuner(struct au0828_dev *dev)
+void au0828_usb_v4l2_media_release(struct au0828_dev *dev)
{
#ifdef CONFIG_MEDIA_CONTROLLER
- struct media_device *mdev = dev->media_dev;
- struct media_entity *source;
- struct media_link *link, *found_link = NULL;
- int ret, active_links = 0;
-
- if (!mdev || !dev->decoder)
- return 0;
+ int i;
- /*
- * This will find the tuner that is connected into the decoder.
- * Technically, this is not 100% correct, as the device may be
- * using an analog input instead of the tuner. However, as we can't
- * do DVB streaming while the DMA engine is being used for V4L2,
- * this should be enough for the actual needs.
- */
- list_for_each_entry(link, &dev->decoder->links, list) {
- if (link->sink->entity == dev->decoder) {
- found_link = link;
- if (link->flags & MEDIA_LNK_FL_ENABLED)
- active_links++;
- break;
- }
+ for (i = 0; i < AU0828_MAX_INPUT; i++) {
+ if (AUVI_INPUT(i).type == AU0828_VMUX_UNDEFINED)
+ return;
+ media_device_unregister_entity(&dev->input_ent[i]);
}
+#endif
+}
- if (active_links == 1 || !found_link)
- return 0;
+static void au0828_usb_v4l2_release(struct v4l2_device *v4l2_dev)
+{
+ struct au0828_dev *dev =
+ container_of(v4l2_dev, struct au0828_dev, v4l2_dev);
+
+ v4l2_ctrl_handler_free(&dev->v4l2_ctrl_hdl);
+ v4l2_device_unregister(&dev->v4l2_dev);
+ au0828_usb_v4l2_media_release(dev);
+ au0828_usb_release(dev);
+}
- source = found_link->source->entity;
- list_for_each_entry(link, &source->links, list) {
- struct media_entity *sink;
- int flags = 0;
+int au0828_v4l2_device_register(struct usb_interface *interface,
+ struct au0828_dev *dev)
+{
+ int retval;
- sink = link->sink->entity;
+ if (AUVI_INPUT(0).type == AU0828_VMUX_UNDEFINED)
+ return 0;
- if (sink == dev->decoder)
- flags = MEDIA_LNK_FL_ENABLED;
+ /* Create the v4l2_device */
+#ifdef CONFIG_MEDIA_CONTROLLER
+ dev->v4l2_dev.mdev = dev->media_dev;
+#endif
+ retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev);
+ if (retval) {
+ pr_err("%s() v4l2_device_register failed\n",
+ __func__);
+ mutex_unlock(&dev->lock);
+ kfree(dev);
+ return retval;
+ }
- ret = media_entity_setup_link(link, flags);
- if (ret) {
- pr_err(
- "Couldn't change link %s->%s to %s. Error %d\n",
- source->name, sink->name,
- flags ? "enabled" : "disabled",
- ret);
- return ret;
- } else
- au0828_isocdbg(
- "link %s->%s was %s\n",
- source->name, sink->name,
- flags ? "ENABLED" : "disabled");
+ dev->v4l2_dev.release = au0828_usb_v4l2_release;
+
+ /* This control handler will inherit the controls from au8522 */
+ retval = v4l2_ctrl_handler_init(&dev->v4l2_ctrl_hdl, 4);
+ if (retval) {
+ pr_err("%s() v4l2_ctrl_handler_init failed\n",
+ __func__);
+ mutex_unlock(&dev->lock);
+ kfree(dev);
+ return retval;
}
-#endif
+ dev->v4l2_dev.ctrl_handler = &dev->v4l2_ctrl_hdl;
+
return 0;
}
return sizes[0] < size ? -EINVAL : 0;
*nplanes = 1;
sizes[0] = size;
-
- au0828_enable_analog_tuner(dev);
-
return 0;
}
* au0828_analog_unregister
* unregister v4l2 devices
*/
-void au0828_analog_unregister(struct au0828_dev *dev)
+int au0828_analog_unregister(struct au0828_dev *dev)
{
dprintk(1, "au0828_analog_unregister called\n");
+
+ /* No analog TV */
+ if (AUVI_INPUT(0).type == AU0828_VMUX_UNDEFINED)
+ return 0;
+
mutex_lock(&au0828_sysfs_lock);
video_unregister_device(&dev->vdev);
video_unregister_device(&dev->vbi_dev);
mutex_unlock(&au0828_sysfs_lock);
+
+ v4l2_device_disconnect(&dev->v4l2_dev);
+ v4l2_device_put(&dev->v4l2_dev);
+
+ return 1;
}
/* This function ensures that video frames continue to be delivered even if
goto end;
if (dev->users == 1) {
- /* Save some power by putting tuner to sleep */
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0);
+ /*
+ * Avoid putting tuner in sleep if DVB or ALSA are
+ * streaming.
+ *
+ * On most USB devices like au0828 the tuner can
+ * be safely put in sleep stare here if ALSA isn't
+ * streaming. Exceptions are some very old USB tuner
+ * models such as em28xx-based WinTV USB2 which have
+ * a separate audio output jack. The devices that have
+ * a separate audio output jack have analog tuners,
+ * like Philips FM1236. Those devices are always on,
+ * so the s_power callback are silently ignored.
+ * So, the current logic here does the following:
+ * Disable (put tuner to sleep) when
+ * - ALSA and DVB aren't not streaming;
+ * - the last V4L2 file handler is closed.
+ *
+ * FIXME:
+ *
+ * Additionally, this logic could be improved to
+ * disable the media source if the above conditions
+ * are met and if the device:
+ * - doesn't have a separate audio out plug (or
+ * - doesn't use a silicon tuner like xc2028/3028/4000/5000).
+ *
+ * Once this additional logic is in place, a callback
+ * is needed to enable the media source and power on
+ * the tuner, for radio to work.
+ */
+ ret = v4l_enable_media_source(vdev);
+ if (ret == 0)
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core,
+ s_power, 0);
dev->std_set_in_tuner_core = 0;
/* When close the device, set the usb intf0 into alt0 to free
[AU0828_VMUX_CABLE] = "Cable TV",
[AU0828_VMUX_TELEVISION] = "Television",
[AU0828_VMUX_DVB] = "DVB",
- [AU0828_VMUX_DEBUG] = "tv debug"
};
dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
default:
dprintk(1, "unknown input type set [%d]\n",
AUVI_INPUT(index).type);
- break;
+ return;
}
+ dev->ctrl_input = index;
+
v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing,
AUVI_INPUT(index).vmux, 0, 0);
static int vidioc_s_input(struct file *file, void *priv, unsigned int index)
{
struct au0828_dev *dev = video_drvdata(file);
+ struct video_device *vfd = video_devdata(file);
dprintk(1, "VIDIOC_S_INPUT in function %s, input=%d\n", __func__,
index);
return -EINVAL;
if (AUVI_INPUT(index).type == 0)
return -EINVAL;
- dev->ctrl_input = index;
+
+ if (dev->ctrl_input == index)
+ return 0;
+
au0828_s_input(dev, index);
- return 0;
+
+ /*
+ * Input has been changed. Disable the media source
+ * associated with the old input and enable source
+ * for the newly set input
+ */
+ v4l_disable_media_source(vfd);
+ return v4l_enable_media_source(vfd);
}
static int vidioc_enumaudio(struct file *file, void *priv, struct v4l2_audio *a)
static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
{
struct au0828_dev *dev = video_drvdata(file);
+ struct video_device *vfd = video_devdata(file);
+ int ret;
if (t->index != 0)
return -EINVAL;
+ ret = v4l_enable_media_source(vfd);
+ if (ret)
+ return ret;
+
dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
dev->std_set_in_tuner_core, dev->dev_state);
[AU0828_VMUX_CABLE] = "Cable TV",
[AU0828_VMUX_TELEVISION] = "Television",
[AU0828_VMUX_DVB] = "DVB",
- [AU0828_VMUX_DEBUG] = "tv debug"
};
int ret, i;
case AU0828_VMUX_CABLE:
case AU0828_VMUX_TELEVISION:
case AU0828_VMUX_DVB:
+ default: /* Just to shut up a warning */
ent->function = MEDIA_ENT_F_CONN_RF;
break;
- default: /* AU0828_VMUX_DEBUG */
- continue;
}
ret = media_entity_pads_init(ent, 1, &dev->input_pad[i]);
dprintk(1, "au0828_analog_register called for intf#%d!\n",
interface->cur_altsetting->desc.bInterfaceNumber);
+ /* No analog TV */
+ if (AUVI_INPUT(0).type == AU0828_VMUX_UNDEFINED)
+ return 0;
+
/* set au0828 usb interface0 to as5 */
retval = usb_set_interface(dev->usbdev,
interface->cur_altsetting->desc.bInterfaceNumber, 5);
dev->ctrl_ainput = 0;
dev->ctrl_freq = 960;
dev->std = V4L2_STD_NTSC_M;
+ /* Default input is TV Tuner */
au0828_s_input(dev, 0);
mutex_init(&dev->vb_queue_lock);
goto err_reg_vbi_dev;
}
+#ifdef CONFIG_MEDIA_CONTROLLER
+ retval = v4l2_mc_create_media_graph(dev->media_dev);
+ if (retval) {
+ pr_err("%s() au0282_dev_register failed to create graph\n",
+ __func__);
+ ret = -ENODEV;
+ goto err_reg_vbi_dev;
+ }
+#endif
+
dprintk(1, "%s completed!\n", __func__);
return 0;
AU0828_VMUX_CABLE,
AU0828_VMUX_TELEVISION,
AU0828_VMUX_DVB,
- AU0828_VMUX_DEBUG
};
struct au0828_input {
struct media_entity *decoder;
struct media_entity input_ent[AU0828_MAX_INPUT];
struct media_pad input_pad[AU0828_MAX_INPUT];
+ struct media_entity_notify entity_notify;
+ struct media_entity *tuner;
+ struct media_link *active_link;
+ struct media_entity *active_link_owner;
+ struct media_entity *active_source;
+ struct media_entity *active_sink;
#endif
};
/* au0828-core.c */
extern u32 au0828_read(struct au0828_dev *dev, u16 reg);
extern u32 au0828_write(struct au0828_dev *dev, u16 reg, u32 val);
+extern void au0828_usb_release(struct au0828_dev *dev);
extern int au0828_debug;
/* ----------------------------------------------------------- */
/* ----------------------------------------------------------- */
/* au0828-video.c */
-extern int au0828_analog_register(struct au0828_dev *dev,
- struct usb_interface *interface);
-extern void au0828_analog_unregister(struct au0828_dev *dev);
extern int au0828_start_analog_streaming(struct vb2_queue *vq,
unsigned int count);
extern void au0828_stop_vbi_streaming(struct vb2_queue *vq);
#ifdef CONFIG_VIDEO_AU0828_V4L2
+extern int au0828_v4l2_device_register(struct usb_interface *interface,
+ struct au0828_dev *dev);
+
+extern int au0828_analog_register(struct au0828_dev *dev,
+ struct usb_interface *interface);
+extern int au0828_analog_unregister(struct au0828_dev *dev);
+extern void au0828_usb_v4l2_media_release(struct au0828_dev *dev);
extern void au0828_v4l2_suspend(struct au0828_dev *dev);
extern void au0828_v4l2_resume(struct au0828_dev *dev);
#else
+static inline int au0828_v4l2_device_register(struct usb_interface *interface,
+ struct au0828_dev *dev)
+{ return 0; };
+static inline int au0828_analog_register(struct au0828_dev *dev,
+ struct usb_interface *interface)
+{ return 0; };
+static inline int au0828_analog_unregister(struct au0828_dev *dev)
+{ return 0; };
+static inline void au0828_usb_v4l2_media_release(struct au0828_dev *dev) { };
static inline void au0828_v4l2_suspend(struct au0828_dev *dev) { };
static inline void au0828_v4l2_resume(struct au0828_dev *dev) { };
#endif
/* Version information */
#define DRIVER_VERSION "0.1"
#define DRIVER_NAME "Technisat/B2C2 FlexCop II/IIb/III Digital TV USB Driver"
-#define DRIVER_AUTHOR "Patrick Boettcher <patrick.boettcher@desy.de>"
+#define DRIVER_AUTHOR "Patrick Boettcher <patrick.boettcher@posteo.de>"
/* debug */
#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG
/* ... followed by the data payload */
for (i = 2; i < fw->size; i += 64) {
cmd.start = 0x0C; /* Data */
- cmd.reg_count = min_t(int, 64, fw->size - i);
+ cmd.reg_count = min_t(uint, 64, fw->size - i);
memcpy(cmd.buffer.block_data, &fw->data[i], cmd.reg_count);
cpia2_send_command(cam, &cmd);
}
buffer_size = urb->actual_length;
buffer = kmalloc(buffer_size, GFP_ATOMIC);
+ if (!buffer)
+ return -ENOMEM;
memcpy(buffer, dma_q->ps_head, 3);
memcpy(buffer+3, p_buffer, buffer_size-3);
}
dev->adev.users--;
+ if (substream->runtime->dma_area) {
+ dev_dbg(dev->dev, "freeing\n");
+ vfree(substream->runtime->dma_area);
+ substream->runtime->dma_area = NULL;
+ }
mutex_unlock(&dev->lock);
if (dev->adev.users == 0 && dev->adev.shutdown == 1) {
if (!mdev)
return -ENOMEM;
- mdev->dev = dev->dev;
- strlcpy(mdev->model, dev->board.name, sizeof(mdev->model));
- if (udev->serial)
- strlcpy(mdev->serial, udev->serial, sizeof(mdev->serial));
- strcpy(mdev->bus_info, udev->devpath);
- mdev->hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
- mdev->driver_version = LINUX_VERSION_CODE;
-
- media_device_init(mdev);
+ media_device_usb_init(mdev, udev, dev->board.name);
dev->media_dev = mdev;
#endif
return 0;
}
-static int cx231xx_create_media_graph(struct cx231xx *dev)
-{
-#ifdef CONFIG_MEDIA_CONTROLLER
- struct media_device *mdev = dev->media_dev;
- struct media_entity *entity;
- struct media_entity *tuner = NULL, *decoder = NULL;
- int ret;
-
- if (!mdev)
- return 0;
-
- media_device_for_each_entity(entity, mdev) {
- switch (entity->function) {
- case MEDIA_ENT_F_TUNER:
- tuner = entity;
- break;
- case MEDIA_ENT_F_ATV_DECODER:
- decoder = entity;
- break;
- }
- }
-
- /* Analog setup, using tuner as a link */
-
- if (!decoder)
- return 0;
-
- if (tuner) {
- ret = media_create_pad_link(tuner, TUNER_PAD_IF_OUTPUT, decoder, 0,
- MEDIA_LNK_FL_ENABLED);
- if (ret < 0)
- return ret;
- }
- ret = media_create_pad_link(decoder, 1, &dev->vdev.entity, 0,
- MEDIA_LNK_FL_ENABLED);
- if (ret < 0)
- return ret;
- ret = media_create_pad_link(decoder, 2, &dev->vbi_dev.entity, 0,
- MEDIA_LNK_FL_ENABLED);
- if (ret < 0)
- return ret;
-#endif
- return 0;
-}
-
/*
* cx231xx_init_dev()
* allocates and inits the device structs, registers i2c bus and v4l device
/* load other modules required */
request_modules(dev);
- retval = cx231xx_create_media_graph(dev);
- if (retval < 0)
- goto done;
-
#ifdef CONFIG_MEDIA_CONTROLLER
- retval = media_device_register(dev->media_dev);
-#endif
+ /* Init entities at the Media Controller */
+ cx231xx_v4l2_create_entities(dev);
-done:
+ retval = v4l2_mc_create_media_graph(dev->media_dev);
+ if (!retval)
+ retval = media_device_register(dev->media_dev);
+#endif
if (retval < 0)
cx231xx_release_resources(dev);
return retval;
#include <media/v4l2-common.h>
#include <media/videobuf-vmalloc.h>
+#include <media/tuner.h>
#include "xc5000.h"
#include "s5h1432.h"
/* register network adapter */
dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx);
- result = dvb_create_media_graph(&dvb->adapter, false);
+ result = dvb_create_media_graph(&dvb->adapter,
+ dev->tuner_type == TUNER_ABSENT);
if (result < 0)
goto fail_create_graph;
/* attach tuner */
memset(&si2157_config, 0, sizeof(si2157_config));
si2157_config.fe = dev->dvb->frontend;
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ si2157_config.mdev = dev->media_dev;
+#endif
si2157_config.if_port = 1;
si2157_config.inversion = true;
strlcpy(info.type, "si2157", I2C_NAME_SIZE);
/* attach tuner */
memset(&si2157_config, 0, sizeof(si2157_config));
si2157_config.fe = dev->dvb->frontend;
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ si2157_config.mdev = dev->media_dev;
+#endif
si2157_config.if_port = 1;
si2157_config.inversion = true;
strlcpy(info.type, "si2157", I2C_NAME_SIZE);
[CX231XX_VMUX_TELEVISION] = "Television",
[CX231XX_VMUX_CABLE] = "Cable TV",
[CX231XX_VMUX_DVB] = "DVB",
- [CX231XX_VMUX_DEBUG] = "for debug only",
};
+void cx231xx_v4l2_create_entities(struct cx231xx *dev)
+{
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ int ret, i;
+
+ /* Create entities for each input connector */
+ for (i = 0; i < MAX_CX231XX_INPUT; i++) {
+ struct media_entity *ent = &dev->input_ent[i];
+
+ if (!INPUT(i)->type)
+ break;
+
+ ent->name = iname[INPUT(i)->type];
+ ent->flags = MEDIA_ENT_FL_CONNECTOR;
+ dev->input_pad[i].flags = MEDIA_PAD_FL_SOURCE;
+
+ switch (INPUT(i)->type) {
+ case CX231XX_VMUX_COMPOSITE1:
+ ent->function = MEDIA_ENT_F_CONN_COMPOSITE;
+ break;
+ case CX231XX_VMUX_SVIDEO:
+ ent->function = MEDIA_ENT_F_CONN_SVIDEO;
+ break;
+ case CX231XX_VMUX_TELEVISION:
+ case CX231XX_VMUX_CABLE:
+ case CX231XX_VMUX_DVB:
+ /* The DVB core will handle it */
+ if (dev->tuner_type == TUNER_ABSENT)
+ continue;
+ /* fall though */
+ default: /* just to shut up a gcc warning */
+ ent->function = MEDIA_ENT_F_CONN_RF;
+ break;
+ }
+
+ ret = media_entity_pads_init(ent, 1, &dev->input_pad[i]);
+ if (ret < 0)
+ pr_err("failed to initialize input pad[%d]!\n", i);
+
+ ret = media_device_register_entity(dev->media_dev, ent);
+ if (ret < 0)
+ pr_err("failed to register input entity %d!\n", i);
+ }
+#endif
+}
+
int cx231xx_enum_input(struct file *file, void *priv,
struct v4l2_input *i)
{
CX231XX_VMUX_CABLE,
CX231XX_RADIO,
CX231XX_VMUX_DVB,
- CX231XX_VMUX_DEBUG
};
enum cx231xx_v_input {
#if defined(CONFIG_MEDIA_CONTROLLER)
struct media_device *media_dev;
struct media_pad video_pad, vbi_pad;
+ struct media_entity input_ent[MAX_CX231XX_INPUT];
+ struct media_pad input_pad[MAX_CX231XX_INPUT];
#endif
unsigned char eedata[256];
void cx231xx_unregister_extension(struct cx231xx_ops *dev);
void cx231xx_init_extension(struct cx231xx *dev);
void cx231xx_close_extension(struct cx231xx *dev);
+void cx231xx_v4l2_create_entities(struct cx231xx *dev);
int cx231xx_querycap(struct file *file, void *priv,
struct v4l2_capability *cap);
int cx231xx_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t);
if (ret < 0)
goto err;
- if (tmp == 1 || tmp == 3) {
+ if (tmp == 1 || tmp == 3 || tmp == 5) {
/* configure gpioh1, reset & power slave demod */
ret = af9035_wr_reg_mask(d, 0x00d8b0, 0x01, 0x01);
if (ret < 0)
if (ret < 0)
goto err;
- if (tmp == 1 || tmp == 3)
+ if (tmp == 1 || tmp == 3 || tmp == 5)
state->dual_mode = true;
dev_dbg(&d->udev->dev, "%s: ts mode=%d dual mode=%d\n", __func__,
&af9035_props, "Avermedia A835B(3835)", RC_MAP_IT913X_V2) },
{ DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835B_4835,
&af9035_props, "Avermedia A835B(4835)", RC_MAP_IT913X_V2) },
+ { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_TD110,
+ &af9035_props, "Avermedia AverTV Volar HD 2 (TD110)", RC_MAP_AVERMEDIA_RM_KS) },
{ DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_H335,
&af9035_props, "Avermedia H335", RC_MAP_IT913X_V2) },
{ DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_UB499_2T_T09,
* 0 TS
* 1 DCA + PIP
* 3 PIP
+ * 5 DCA + PIP
* n DCA
*
- * Values 0 and 3 are seen to this day. 0 for single TS and 3 for dual TS.
+ * Values 0, 3 and 5 are seen to this day. 0 for single TS and 3/5 for dual TS.
*/
#define EEPROM_BASE_AF9035 0x42fd
/*
* DVB USB framework
*
- * Copyright (C) 2004-6 Patrick Boettcher <patrick.boettcher@desy.de>
+ * Copyright (C) 2004-6 Patrick Boettcher <patrick.boettcher@posteo.de>
* Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
*
* This program is free software; you can redistribute it and/or modify
/*
* DVB USB framework
*
- * Copyright (C) 2004-6 Patrick Boettcher <patrick.boettcher@desy.de>
+ * Copyright (C) 2004-6 Patrick Boettcher <patrick.boettcher@posteo.de>
* Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
*
* This program is free software; you can redistribute it and/or modify
/*
* DVB USB framework
*
- * Copyright (C) 2004-6 Patrick Boettcher <patrick.boettcher@desy.de>
+ * Copyright (C) 2004-6 Patrick Boettcher <patrick.boettcher@posteo.de>
* Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
*
* This program is free software; you can redistribute it and/or modify
*/
#include "dvb_usb_common.h"
+#include <media/media-device.h>
static int dvb_usbv2_disable_rc_polling;
module_param_named(disable_rc_polling, dvb_usbv2_disable_rc_polling, int, 0644);
if (!mdev)
return -ENOMEM;
- mdev->dev = &udev->dev;
- strlcpy(mdev->model, d->name, sizeof(mdev->model));
- if (udev->serial)
- strlcpy(mdev->serial, udev->serial, sizeof(mdev->serial));
- strcpy(mdev->bus_info, udev->devpath);
- mdev->hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
- mdev->driver_version = LINUX_VERSION_CODE;
-
- media_device_init(mdev);
+ media_device_usb_init(mdev, udev, d->name);
dvb_register_media_controller(&adap->dvb_adap, mdev);
EXPORT_SYMBOL(dvb_usbv2_reset_resume);
MODULE_VERSION("2.0");
-MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
MODULE_DESCRIPTION("DVB USB common");
MODULE_LICENSE("GPL");
/*
* DVB USB framework
*
- * Copyright (C) 2004-6 Patrick Boettcher <patrick.boettcher@desy.de>
+ * Copyright (C) 2004-6 Patrick Boettcher <patrick.boettcher@posteo.de>
* Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
*
* This program is free software; you can redistribute it and/or modify
USB_PID_TECHNOTREND_CONNECT_CT2_4650_CI,
&dvbsky_t680c_props, "TechnoTrend TT-connect CT2-4650 CI",
RC_MAP_TT_1500) },
+ { DVB_USB_DEVICE(USB_VID_TECHNOTREND,
+ USB_PID_TECHNOTREND_CONNECT_CT2_4650_CI_2,
+ &dvbsky_t680c_props, "TechnoTrend TT-connect CT2-4650 CI v1.1",
+ RC_MAP_TT_1500) },
{ DVB_USB_DEVICE(USB_VID_TERRATEC,
USB_PID_TERRATEC_H7_3,
&dvbsky_t680c_props, "Terratec H7 Rev.4",
RC_MAP_TT_1500) },
+ { DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_S2_R4,
+ &dvbsky_s960_props, "Terratec Cinergy S2 Rev.4",
+ RC_MAP_DVBSKY) },
{ }
};
MODULE_DEVICE_TABLE(usb, dvbsky_id_table);
return 0;
}
-static int mxl111sf_demod_get_frontend(struct dvb_frontend *fe)
+static int mxl111sf_demod_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct mxl111sf_demod_state *state = fe->demodulator_priv;
mxl_dbg("()");
struct mxl111sf_tuner_state {
struct mxl111sf_state *mxl_state;
- struct mxl111sf_tuner_config *cfg;
+ const struct mxl111sf_tuner_config *cfg;
enum mxl_if_freq if_freq;
};
struct dvb_frontend *mxl111sf_tuner_attach(struct dvb_frontend *fe,
- struct mxl111sf_state *mxl_state,
- struct mxl111sf_tuner_config *cfg)
+ struct mxl111sf_state *mxl_state,
+ const struct mxl111sf_tuner_config *cfg)
{
struct mxl111sf_tuner_state *state = NULL;
#if IS_ENABLED(CONFIG_DVB_USB_MXL111SF)
extern
struct dvb_frontend *mxl111sf_tuner_attach(struct dvb_frontend *fe,
- struct mxl111sf_state *mxl_state,
- struct mxl111sf_tuner_config *cfg);
+ struct mxl111sf_state *mxl_state,
+ const struct mxl111sf_tuner_config *cfg);
#else
static inline
struct dvb_frontend *mxl111sf_tuner_attach(struct dvb_frontend *fe,
- struct mxl111sf_state *mxl_state,
- struct mxl111sf_tuner_config *cfg)
+ struct mxl111sf_state *mxl_state,
+ const struct mxl111sf_tuner_config *cfg)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return NULL;
return 0;
}
-static struct mxl111sf_tuner_config mxl_tuner_config = {
+static const struct mxl111sf_tuner_config mxl_tuner_config = {
.if_freq = MXL_IF_6_0, /* applies to external IF output, only */
.invert_spectrum = 0,
.read_reg = mxl111sf_read_reg,
state->tuner.function = MEDIA_ENT_F_TUNER;
state->tuner.name = "mxl111sf tuner";
state->tuner_pads[TUNER_PAD_RF_INPUT].flags = MEDIA_PAD_FL_SINK;
- state->tuner_pads[TUNER_PAD_IF_OUTPUT].flags = MEDIA_PAD_FL_SOURCE;
+ state->tuner_pads[TUNER_PAD_OUTPUT].flags = MEDIA_PAD_FL_SOURCE;
ret = media_entity_pads_init(&state->tuner,
TUNER_NUM_PADS, state->tuner_pads);
ret = -EOPNOTSUPP;
}
+ /* Retry failed I2C messages */
+ if (ret == -EPIPE)
+ ret = -EAGAIN;
+
err_mutex_unlock:
mutex_unlock(&d->i2c_mutex);
}
dev_dbg(&d->intf->dev, "chip_id=%u\n", dev->chip_id);
+ /* Retry failed I2C messages */
+ d->i2c_adap.retries = 1;
+ d->i2c_adap.timeout = msecs_to_jiffies(10);
+
return WARM;
err:
dev_dbg(&d->intf->dev, "failed=%d\n", ret);
if (dev->chip_id == CHIP_ID_RTL2831U)
return 0;
- /* control internal demod ADC */
- if (fe->id == 0 && onoff)
- val = 0x48; /* enable ADC */
- else
- val = 0x00; /* disable ADC */
-
- ret = rtl28xxu_wr_reg_mask(d, SYS_DEMOD_CTL, val, 0x48);
- if (ret)
- goto err;
+ if (fe->id == 0) {
+ /* control internal demod ADC */
+ if (onoff)
+ val = 0x48; /* enable ADC */
+ else
+ val = 0x00; /* disable ADC */
- /* bypass slave demod TS through master demod */
- if (fe->id == 1 && onoff) {
- ret = pdata->enable_slave_ts(dev->i2c_client_demod);
+ ret = rtl28xxu_wr_reg_mask(d, SYS_DEMOD_CTL, val, 0x48);
+ if (ret)
+ goto err;
+ } else if (fe->id == 1) {
+ /* bypass slave demod TS through master demod */
+ ret = pdata->slave_ts_ctrl(dev->i2c_client_demod, onoff);
if (ret)
goto err;
}
/* usb-urb.c is part of the DVB USB library.
*
- * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de)
+ * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@posteo.de)
* see dvb-usb-init.c for copyright information.
*
* This file keeps functions for initializing and handling the
/* DVB USB framework compliant Linux driver for the AVerMedia AverTV DVB-T
* USB2.0 (A800) DVB-T receiver.
*
- * Copyright (C) 2005 Patrick Boettcher (patrick.boettcher@desy.de)
+ * Copyright (C) 2005 Patrick Boettcher (patrick.boettcher@posteo.de)
*
* Thanks to
* - AVerMedia who kindly provided information and
module_usb_driver(a800_driver);
-MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
MODULE_DESCRIPTION("AVerMedia AverTV DVB-T USB 2.0 (A800)");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");
return 0;
}
-static int af9005_fe_get_frontend(struct dvb_frontend *fe)
+static int af9005_fe_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *fep)
{
- struct dtv_frontend_properties *fep = &fe->dtv_property_cache;
struct af9005_fe_state *state = fe->demodulator_priv;
int ret;
u8 temp;
*
* TODO: Use the cx25840-driver for the analogue part
*
- * Copyright (C) 2005 Patrick Boettcher (patrick.boettcher@desy.de)
+ * Copyright (C) 2005 Patrick Boettcher (patrick.boettcher@posteo.de)
* Copyright (C) 2006 Michael Krufky (mkrufky@linuxtv.org)
* Copyright (C) 2006, 2007 Chris Pascoe (c.pascoe@itee.uq.edu.au)
*
module_usb_driver(cxusb_driver);
-MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");
MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
MODULE_DESCRIPTION("Driver for Conexant USB2.0 hybrid reference design");
module_usb_driver(dib0700_driver);
MODULE_FIRMWARE("dvb-usb-dib0700-1.20.fw");
-MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
MODULE_DESCRIPTION("Driver for devices based on DiBcom DiB0700 - USB bridge");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");
struct dib0700_adapter_state *st = adap->priv;
struct i2c_adapter *tun_i2c = st->dib8000_ops.get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1);
- if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &dib809x_dib0090_config) == NULL)
- return -ENODEV;
+ if (adap->id == 0) {
+ if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &dib809x_dib0090_config) == NULL)
+ return -ENODEV;
+ } else {
+ if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &dib809x_dib0090_config) == NULL)
+ return -ENODEV;
+ }
st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params;
adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib8096_set_param_override;
return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
+static int stk809x_frontend1_attach(struct dvb_usb_adapter *adap)
+{
+ struct dib0700_adapter_state *state = adap->priv;
+
+ if (!dvb_attach(dib8000_attach, &state->dib8000_ops))
+ return -ENODEV;
+
+ state->dib8000_ops.i2c_enumeration(&adap->dev->i2c_adap, 1, 0x10, 0x82, 0);
+
+ adap->fe_adap[0].fe = state->dib8000_ops.init(&adap->dev->i2c_adap, 0x82, &dib809x_dib8000_config[1]);
+
+ return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
+}
+
static int nim8096md_tuner_attach(struct dvb_usb_adapter *adap)
{
struct dib0700_adapter_state *st = adap->priv;
/* 80 */{ USB_DEVICE(USB_VID_ELGATO, USB_PID_ELGATO_EYETV_DTT_2) },
{ USB_DEVICE(USB_VID_PCTV, USB_PID_PCTV_2002E) },
{ USB_DEVICE(USB_VID_PCTV, USB_PID_PCTV_2002E_SE) },
+ { USB_DEVICE(USB_VID_PCTV, USB_PID_DIBCOM_STK8096PVR) },
{ 0 } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table);
RC_BIT_NEC,
.change_protocol = dib0700_change_protocol,
},
+ }, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
+ .num_adapters = 2,
+ .adapter = {
+ {
+ .num_frontends = 1,
+ .fe = {{
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER |
+ DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+ .pid_filter_count = 32,
+ .pid_filter = stk80xx_pid_filter,
+ .pid_filter_ctrl = stk80xx_pid_filter_ctrl,
+ .frontend_attach = stk809x_frontend_attach,
+ .tuner_attach = dib809x_tuner_attach,
+
+ DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
+ } },
+ .size_of_priv =
+ sizeof(struct dib0700_adapter_state),
+ }, {
+ .num_frontends = 1,
+ .fe = { {
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER |
+ DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+ .pid_filter_count = 32,
+ .pid_filter = stk80xx_pid_filter,
+ .pid_filter_ctrl = stk80xx_pid_filter_ctrl,
+ .frontend_attach = stk809x_frontend1_attach,
+ .tuner_attach = dib809x_tuner_attach,
+
+ DIB0700_DEFAULT_STREAMING_CONFIG(0x03),
+ } },
+ .size_of_priv =
+ sizeof(struct dib0700_adapter_state),
+ },
+ },
+ .num_device_descs = 1,
+ .devices = {
+ { "DiBcom STK8096-PVR reference design",
+ { &dib0700_usb_id_table[83], NULL },
+ { NULL },
+ },
+ },
+
+ .rc.core = {
+ .rc_interval = DEFAULT_RC_INTERVAL,
+ .rc_codes = RC_MAP_DIB0700_RC5_TABLE,
+ .module_name = "dib0700",
+ .rc_query = dib0700_rc_query_old_firmware,
+ .allowed_protos = RC_BIT_RC5 |
+ RC_BIT_RC6_MCE |
+ RC_BIT_NEC,
+ .change_protocol = dib0700_change_protocol,
+ },
},
};
/* Common methods for dibusb-based-receivers.
*
- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de)
*
* 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
/* DVB USB compliant linux driver for mobile DVB-T USB devices based on
* reference designs made by DiBcom (http://www.dibcom.fr/) (DiB3000M-B)
*
- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de)
*
* based on GPL code from DiBcom, which has
- * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr)
+ * Copyright (C) 2004 Amaury Demol for DiBcom
*
* 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
module_usb_driver(dibusb_driver);
-MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
MODULE_DESCRIPTION("Driver for DiBcom USB DVB-T devices (DiB3000M-B based)");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");
/* DVB USB compliant linux driver for mobile DVB-T USB devices based on
* reference designs made by DiBcom (http://www.dibcom.fr/) (DiB3000M-C/P)
*
- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de)
*
* based on GPL code from DiBcom, which has
- * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr)
+ * Copyright (C) 2004 Amaury Demol for DiBcom
*
* 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
module_usb_driver(dibusb_mc_driver);
-MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
MODULE_DESCRIPTION("Driver for DiBcom USB2.0 DVB-T (DiB3000M-C/P based) devices");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");
/* Header file for all dibusb-based-receivers.
*
- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de)
*
* 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
/* DVB USB compliant linux driver for Nebula Electronics uDigiTV DVB-T USB2.0
* receiver
*
- * Copyright (C) 2005 Patrick Boettcher (patrick.boettcher@desy.de)
+ * Copyright (C) 2005 Patrick Boettcher (patrick.boettcher@posteo.de)
*
* partly based on the SDK published by Nebula Electronics
*
module_usb_driver(digitv_driver);
-MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
MODULE_DESCRIPTION("Driver for Nebula Electronics uDigiTV DVB-T USB2.0");
MODULE_VERSION("1.0-alpha");
MODULE_LICENSE("GPL");
/* Frontend part of the Linux driver for the WideView/ Yakumo/ Hama/
* Typhoon/ Yuan DVB-T USB2.0 receiver.
*
- * Copyright (C) 2005 Patrick Boettcher <patrick.boettcher@desy.de>
+ * Copyright (C) 2005 Patrick Boettcher <patrick.boettcher@posteo.de>
*
* 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
return 0;
}
-static int dtt200u_fe_get_frontend(struct dvb_frontend* fe)
+static int dtt200u_fe_get_frontend(struct dvb_frontend* fe,
+ struct dtv_frontend_properties *fep)
{
- struct dtv_frontend_properties *fep = &fe->dtv_property_cache;
struct dtt200u_fe_state *state = fe->demodulator_priv;
+
memcpy(fep, &state->fep, sizeof(struct dtv_frontend_properties));
return 0;
}
/* DVB USB library compliant Linux driver for the WideView/ Yakumo/ Hama/
* Typhoon/ Yuan/ Miglia DVB-T USB2.0 receiver.
*
- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de)
*
* Thanks to Steve Chang from WideView for providing support for the WT-220U.
*
module_usb_driver(dtt200u_usb_driver);
-MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
MODULE_DESCRIPTION("Driver for the WideView/Yakumo/Hama/Typhoon/Club3D/Miglia DVB-T USB2.0 devices");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");
/* Common header file of Linux driver for the WideView/ Yakumo/ Hama/
* Typhoon/ Yuan DVB-T USB2.0 receiver.
*
- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de)
*
* 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
/* dvb-usb-common.h is part of the DVB USB library.
*
- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de)
* see dvb-usb-init.c for copyright information.
*
* a header file containing prototypes and types for internal use of the dvb-usb-lib
/* dvb-usb-dvb.c is part of the DVB USB library.
*
- * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de)
+ * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@posteo.de)
* see dvb-usb-init.c for copyright information.
*
* This file contains functions for initializing and handling the
* linux-dvb API.
*/
#include "dvb-usb-common.h"
+#include <media/media-device.h>
/* does the complete input transfer handling */
static int dvb_usb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff)
if (!mdev)
return -ENOMEM;
- mdev->dev = &udev->dev;
- strlcpy(mdev->model, d->desc->name, sizeof(mdev->model));
- if (udev->serial)
- strlcpy(mdev->serial, udev->serial, sizeof(mdev->serial));
- strcpy(mdev->bus_info, udev->devpath);
- mdev->hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
- mdev->driver_version = LINUX_VERSION_CODE;
-
- media_device_init(mdev);
+ media_device_usb_init(mdev, udev, d->desc->name);
dvb_register_media_controller(&adap->dvb_adap, mdev);
/* dvb-usb-firmware.c is part of the DVB USB library.
*
- * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de)
+ * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@posteo.de)
* see dvb-usb-init.c for copyright information.
*
* This file contains functions for downloading the firmware to Cypress FX 1 and 2 based devices.
/* dvb-usb-i2c.c is part of the DVB USB library.
*
- * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de)
+ * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@posteo.de)
* see dvb-usb-init.c for copyright information.
*
* This file contains functions for (de-)initializing an I2C adapter.
*
* dvb-usb-init.c
*
- * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de)
+ * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@posteo.de)
*
* 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
EXPORT_SYMBOL(dvb_usb_device_exit);
MODULE_VERSION("1.0");
-MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
MODULE_DESCRIPTION("A library module containing commonly used USB and DVB function USB DVB devices");
MODULE_LICENSE("GPL");
/* dvb-usb-remote.c is part of the DVB USB library.
*
- * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de)
+ * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@posteo.de)
* see dvb-usb-init.c for copyright information.
*
* This file contains functions for initializing the input-device and for handling remote-control-queries.
/* dvb-usb-urb.c is part of the DVB USB library.
*
- * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de)
+ * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@posteo.de)
* see dvb-usb-init.c for copyright information.
*
* This file keeps functions for initializing and handling the
/* dvb-usb.h is part of the DVB USB library.
*
- * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de)
+ * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@posteo.de)
* see dvb-usb-init.c for copyright information.
*
* the headerfile, all dvb-usb-drivers have to include.
/* DVB USB framework compliant Linux driver for the
* DVBWorld DVB-S 2101, 2102, DVB-S2 2104, DVB-C 3101,
- * TeVii S600, S630, S650, S660, S480, S421, S632
+ * TeVii S421, S480, S482, S600, S630, S632, S650, S660, S662,
* Prof 1100, 7500,
* Geniatech SU3000, T220,
- * TechnoTrend S2-4600 Cards
+ * TechnoTrend S2-4600,
+ * Terratec Cinergy S2 cards
* Copyright (C) 2008-2012 Igor M. Liplianin (liplianin@me.by)
*
* This program is free software; you can redistribute it and/or modify it
#include "tda18271.h"
#include "cxd2820r.h"
#include "m88ds3103.h"
-#include "ts2020.h"
/* Max transfer size done by I2C transfer functions */
#define MAX_XFER_SIZE 64
#define USB_PID_TEVII_S660 0xd660
#endif
+#ifndef USB_PID_TEVII_S662
+#define USB_PID_TEVII_S662 0xd662
+#endif
+
#ifndef USB_PID_TEVII_S480_1
#define USB_PID_TEVII_S480_1 0xd481
#endif
struct dw2102_state {
u8 initialized;
u8 last_lock;
+ struct i2c_client *i2c_client_demod;
struct i2c_client *i2c_client_tuner;
/* fe hook functions*/
.gate = TDA18271_GATE_DIGITAL,
};
-static const struct m88ds3103_config tt_s2_4600_m88ds3103_config = {
- .i2c_addr = 0x68,
- .clock = 27000000,
- .i2c_wr_max = 33,
- .ts_mode = M88DS3103_TS_CI,
- .ts_clk = 16000,
- .ts_clk_pol = 0,
- .spec_inv = 0,
- .agc_inv = 0,
- .clock_out = M88DS3103_CLOCK_OUT_ENABLED,
- .envelope_mode = 0,
- .agc = 0x99,
- .lnb_hv_pol = 1,
- .lnb_en_pol = 0,
-};
-
static u8 m88rs2000_inittab[] = {
DEMOD_WRITE, 0x9a, 0x30,
DEMOD_WRITE, 0x00, 0x01,
u8 ibuf[] = { 0 };
struct i2c_adapter *i2c_adapter;
struct i2c_client *client;
- struct i2c_board_info info;
+ struct i2c_board_info board_info;
+ struct m88ds3103_platform_data m88ds3103_pdata = {};
struct ts2020_config ts2020_config = {};
if (dvb_usb_generic_rw(d, obuf, 3, ibuf, 1, 0) < 0)
if (dvb_usb_generic_rw(d, obuf, 1, ibuf, 1, 0) < 0)
err("command 0x51 transfer failed.");
- memset(&info, 0, sizeof(struct i2c_board_info));
-
- adap->fe_adap[0].fe = dvb_attach(m88ds3103_attach,
- &tt_s2_4600_m88ds3103_config,
- &d->i2c_adap,
- &i2c_adapter);
- if (adap->fe_adap[0].fe == NULL)
+ /* attach demod */
+ m88ds3103_pdata.clk = 27000000;
+ m88ds3103_pdata.i2c_wr_max = 33;
+ m88ds3103_pdata.ts_mode = M88DS3103_TS_CI;
+ m88ds3103_pdata.ts_clk = 16000;
+ m88ds3103_pdata.ts_clk_pol = 0;
+ m88ds3103_pdata.spec_inv = 0;
+ m88ds3103_pdata.agc = 0x99;
+ m88ds3103_pdata.agc_inv = 0;
+ m88ds3103_pdata.clk_out = M88DS3103_CLOCK_OUT_ENABLED;
+ m88ds3103_pdata.envelope_mode = 0;
+ m88ds3103_pdata.lnb_hv_pol = 1;
+ m88ds3103_pdata.lnb_en_pol = 0;
+ memset(&board_info, 0, sizeof(board_info));
+ strlcpy(board_info.type, "m88ds3103", I2C_NAME_SIZE);
+ board_info.addr = 0x68;
+ board_info.platform_data = &m88ds3103_pdata;
+ request_module("m88ds3103");
+ client = i2c_new_device(&d->i2c_adap, &board_info);
+ if (client == NULL || client->dev.driver == NULL)
return -ENODEV;
+ if (!try_module_get(client->dev.driver->owner)) {
+ i2c_unregister_device(client);
+ return -ENODEV;
+ }
+ adap->fe_adap[0].fe = m88ds3103_pdata.get_dvb_frontend(client);
+ i2c_adapter = m88ds3103_pdata.get_i2c_adapter(client);
+
+ state->i2c_client_demod = client;
/* attach tuner */
ts2020_config.fe = adap->fe_adap[0].fe;
- strlcpy(info.type, "ts2022", I2C_NAME_SIZE);
- info.addr = 0x60;
- info.platform_data = &ts2020_config;
+ memset(&board_info, 0, sizeof(board_info));
+ strlcpy(board_info.type, "ts2022", I2C_NAME_SIZE);
+ board_info.addr = 0x60;
+ board_info.platform_data = &ts2020_config;
request_module("ts2020");
- client = i2c_new_device(i2c_adapter, &info);
+ client = i2c_new_device(i2c_adapter, &board_info);
if (client == NULL || client->dev.driver == NULL) {
dvb_frontend_detach(adap->fe_adap[0].fe);
TECHNOTREND_S2_4600,
TEVII_S482_1,
TEVII_S482_2,
+ TERRATEC_CINERGY_S2_BOX,
+ TEVII_S662
};
static struct usb_device_id dw2102_table[] = {
[TEVII_S660] = {USB_DEVICE(0x9022, USB_PID_TEVII_S660)},
[PROF_7500] = {USB_DEVICE(0x3034, 0x7500)},
[GENIATECH_SU3000] = {USB_DEVICE(0x1f4d, 0x3000)},
- [TERRATEC_CINERGY_S2] = {USB_DEVICE(USB_VID_TERRATEC, 0x00a8)},
+ [TERRATEC_CINERGY_S2] = {USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_S2_R1)},
[TEVII_S480_1] = {USB_DEVICE(0x9022, USB_PID_TEVII_S480_1)},
[TEVII_S480_2] = {USB_DEVICE(0x9022, USB_PID_TEVII_S480_2)},
[X3M_SPC1400HD] = {USB_DEVICE(0x1f4d, 0x3100)},
[TEVII_S421] = {USB_DEVICE(0x9022, USB_PID_TEVII_S421)},
[TEVII_S632] = {USB_DEVICE(0x9022, USB_PID_TEVII_S632)},
- [TERRATEC_CINERGY_S2_R2] = {USB_DEVICE(USB_VID_TERRATEC, 0x00b0)},
+ [TERRATEC_CINERGY_S2_R2] = {USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_S2_R2)},
[GOTVIEW_SAT_HD] = {USB_DEVICE(0x1FE1, USB_PID_GOTVIEW_SAT_HD)},
[GENIATECH_T220] = {USB_DEVICE(0x1f4d, 0xD220)},
[TECHNOTREND_S2_4600] = {USB_DEVICE(USB_VID_TECHNOTREND,
USB_PID_TECHNOTREND_CONNECT_S2_4600)},
[TEVII_S482_1] = {USB_DEVICE(0x9022, 0xd483)},
[TEVII_S482_2] = {USB_DEVICE(0x9022, 0xd484)},
+ [TERRATEC_CINERGY_S2_BOX] = {USB_DEVICE(USB_VID_TERRATEC, 0x0105)},
+ [TEVII_S662] = {USB_DEVICE(0x9022, USB_PID_TEVII_S662)},
{ }
};
} },
}
},
- .num_device_descs = 3,
+ .num_device_descs = 5,
.devices = {
{ "TechnoTrend TT-connect S2-4600",
{ &dw2102_table[TECHNOTREND_S2_4600], NULL },
{ &dw2102_table[TEVII_S482_2], NULL },
{ NULL },
},
+ { "Terratec Cinergy S2 USB BOX",
+ { &dw2102_table[TERRATEC_CINERGY_S2_BOX], NULL },
+ { NULL },
+ },
+ { "TeVii S662",
+ { &dw2102_table[TEVII_S662], NULL },
+ { NULL },
+ },
}
};
i2c_unregister_device(client);
}
+ /* remove I2C client for demodulator */
+ client = st->i2c_client_demod;
+ if (client) {
+ module_put(client->dev.driver->owner);
+ i2c_unregister_device(client);
+ }
+
dvb_usb_device_exit(intf);
}
MODULE_AUTHOR("Igor M. Liplianin (c) liplianin@me.by");
MODULE_DESCRIPTION("Driver for DVBWorld DVB-S 2101, 2102, DVB-S2 2104,"
" DVB-C 3101 USB2.0,"
- " TeVii S600, S630, S650, S660, S480, S421, S632"
- " Prof 1100, 7500 USB2.0,"
+ " TeVii S421, S480, S482, S600, S630, S632, S650,"
+ " TeVii S660, S662, Prof 1100, 7500 USB2.0,"
" Geniatech SU3000, T220,"
- " TechnoTrend S2-4600 devices");
+ " TechnoTrend S2-4600, Terratec Cinergy S2 devices");
MODULE_VERSION("0.1");
MODULE_LICENSE("GPL");
MODULE_FIRMWARE(DW2101_FIRMWARE);
return r;
}
-static int jdvbt90502_get_frontend(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
- p->inversion = INVERSION_AUTO;
- p->bandwidth_hz = 6000000;
- p->code_rate_HP = FEC_AUTO;
- p->code_rate_LP = FEC_AUTO;
- p->modulation = QAM_64;
- p->transmission_mode = TRANSMISSION_MODE_AUTO;
- p->guard_interval = GUARD_INTERVAL_AUTO;
- p->hierarchy = HIERARCHY_AUTO;
- return 0;
-}
-
static int jdvbt90502_set_frontend(struct dvb_frontend *fe)
{
struct dtv_frontend_properties *p = &fe->dtv_property_cache;
deb_fe("%s: Freq:%d\n", __func__, p->frequency);
- /* for recovery from DTV_CLEAN */
- fe->dtv_property_cache.delivery_system = SYS_ISDBT;
+ /* This driver only works on auto mode */
+ p->inversion = INVERSION_AUTO;
+ p->bandwidth_hz = 6000000;
+ p->code_rate_HP = FEC_AUTO;
+ p->code_rate_LP = FEC_AUTO;
+ p->modulation = QAM_64;
+ p->transmission_mode = TRANSMISSION_MODE_AUTO;
+ p->guard_interval = GUARD_INTERVAL_AUTO;
+ p->hierarchy = HIERARCHY_AUTO;
+ p->delivery_system = SYS_ISDBT;
ret = jdvbt90502_pll_set_freq(state, p->frequency);
if (ret) {
.set_property = jdvbt90502_set_property,
.set_frontend = jdvbt90502_set_frontend,
- .get_frontend = jdvbt90502_get_frontend,
.read_status = jdvbt90502_read_status,
.read_signal_strength = jdvbt90502_read_signal_strength,
/* DVB USB framework compliant Linux driver for the Hauppauge WinTV-NOVA-T usb2
* DVB-T receiver.
*
- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de)
*
* 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
module_usb_driver(nova_t_driver);
-MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
MODULE_DESCRIPTION("Hauppauge WinTV-NOVA-T usb2");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");
u8 power_state;
u16 last_scan_code;
+
+ u8 buf[64];
};
/* debug print helpers */
TECH_LED_UNDEFINED
};
-static int technisat_usb2_set_led(struct dvb_usb_device *d, int red, enum technisat_usb2_led_state state)
+static int technisat_usb2_set_led(struct dvb_usb_device *d, int red,
+ enum technisat_usb2_led_state st)
{
+ struct technisat_usb2_state *state = d->priv;
+ u8 *led = state->buf;
int ret;
- u8 led[8] = {
- red ? SET_RED_LED_VENDOR_REQUEST : SET_GREEN_LED_VENDOR_REQUEST,
- 0
- };
+ led[0] = red ? SET_RED_LED_VENDOR_REQUEST : SET_GREEN_LED_VENDOR_REQUEST;
- if (disable_led_control && state != TECH_LED_OFF)
+ if (disable_led_control && st != TECH_LED_OFF)
return 0;
- switch (state) {
+ switch (st) {
case TECH_LED_ON:
led[1] = 0x82;
break;
red ? SET_RED_LED_VENDOR_REQUEST : SET_GREEN_LED_VENDOR_REQUEST,
USB_TYPE_VENDOR | USB_DIR_OUT,
0, 0,
- led, sizeof(led), 500);
+ led, 8, 500);
mutex_unlock(&d->i2c_mutex);
return ret;
static int technisat_usb2_set_led_timer(struct dvb_usb_device *d, u8 red, u8 green)
{
+ struct technisat_usb2_state *state = d->priv;
+ u8 *b = state->buf;
int ret;
- u8 b = 0;
+
+ b[0] = 0;
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
SET_LED_TIMER_DIVIDER_VENDOR_REQUEST,
USB_TYPE_VENDOR | USB_DIR_OUT,
(red << 8) | green, 0,
- &b, 1, 500);
+ b, 1, 500);
mutex_unlock(&d->i2c_mutex);
struct dvb_usb_device_description **desc, int *cold)
{
int ret;
- u8 version[3];
+ u8 *version;
+
+ version = kmalloc(3, GFP_KERNEL);
+ if (!version)
+ return -ENOMEM;
/* first select the interface */
if (usb_set_interface(udev, 0, 1) != 0)
GET_VERSION_INFO_VENDOR_REQUEST,
USB_TYPE_VENDOR | USB_DIR_IN,
0, 0,
- version, sizeof(version), 500);
+ version, 3, 500);
if (ret < 0)
*cold = 1;
*cold = 0;
}
+ kfree(version);
+
return 0;
}
&a->dev->i2c_adap, STV090x_DEMODULATOR_0);
if (a->fe_adap[0].fe) {
- struct stv6110x_devctl *ctl;
+ const struct stv6110x_devctl *ctl;
ctl = dvb_attach(stv6110x_attach,
a->fe_adap[0].fe,
static int technisat_usb2_get_ir(struct dvb_usb_device *d)
{
- u8 buf[62], *b;
+ struct technisat_usb2_state *state = d->priv;
+ u8 *buf = state->buf;
+ u8 *b;
int ret;
struct ir_raw_event ev;
GET_IR_DATA_VENDOR_REQUEST,
USB_TYPE_VENDOR | USB_DIR_IN,
0x8080, 0,
- buf, sizeof(buf), 500);
+ buf, 62, 500);
unlock:
mutex_unlock(&d->i2c_mutex);
module_usb_driver(ttusb2_driver);
-MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
MODULE_DESCRIPTION("Driver for Pinnacle PCTV 400e DVB-S USB2.0");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");
/* DVB USB framework compliant Linux driver for the HanfTek UMT-010 USB2.0
* DVB-T receiver.
*
- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de)
*
* 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
module_usb_driver(umt_driver);
-MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
MODULE_DESCRIPTION("Driver for HanfTek UMT 010 USB2.0 DVB-T device");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");
/* usb-urb.c is part of the DVB USB library.
*
- * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de)
+ * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@posteo.de)
* see dvb-usb-init.c for copyright information.
*
* This file keeps functions for initializing and handling the
* Copyright (C) 2005 Ralph Metzler <rjkm@metzlerbros.de>
* Metzler Brothers Systementwicklung GbR
*
- * Copyright (C) 2005 Patrick Boettcher <patrick.boettcher@desy.de>
+ * Copyright (C) 2005 Patrick Boettcher <patrick.boettcher@posteo.de>
*
* Thanks to Twinhan who kindly provided hardware and information.
*
* Copyright (C) 2005 Ralph Metzler <rjkm@metzlerbros.de>
* Metzler Brothers Systementwicklung GbR
*
- * Copyright (C) 2005 Patrick Boettcher <patrick.boettcher@desy.de>
+ * Copyright (C) 2005 Patrick Boettcher <patrick.boettcher@posteo.de>
*
* Thanks to Twinhan who kindly provided hardware and information.
*
module_usb_driver(vp702x_usb_driver);
-MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
MODULE_DESCRIPTION("Driver for Twinhan StarBox DVB-S USB2.0 and clones");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");
/* DVB frontend part of the Linux driver for TwinhanDTV Alpha/MagicBoxII USB2.0
* DVB-T receiver.
*
- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de)
*
* Thanks to Twinhan who kindly provided hardware and information.
*
* - TwinhanDTV Alpha/MagicBoxII USB2.0 DVB-T receiver
* - DigitalNow TinyUSB2 DVB-t receiver
*
- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de)
*
* Thanks to Twinhan who kindly provided hardware and information.
*
module_usb_driver(vp7045_usb_driver);
-MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
MODULE_DESCRIPTION("Driver for Twinhan MagicBox/Alpha and DNTV tinyUSB2 DVB-T USB2.0");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");
/* Common header-file of the Linux driver for the TwinhanDTV Alpha/MagicBoxII
* USB2.0 DVB-T receiver.
*
- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de)
*
* Thanks to Twinhan who kindly provided hardware and information.
*
i2c_master_send(&dev->i2c_client[dev->def_i2c_bus],
®s[i][0], 3);
+ /* FIXME: This won't be creating a sensor at the media graph */
+
return 0;
}
i2c_master_send(&dev->i2c_client[dev->def_i2c_bus],
®s[i][0], 3);
+ /* FIXME: This won't be creating a sensor at the media graph */
+
return 0;
}
#include <media/tuner.h>
#include <media/drv-intf/msp3400.h>
#include <media/i2c/saa7115.h>
-#include <media/i2c/tvp5150.h>
+#include <dt-bindings/media/tvp5150.h>
#include <media/i2c/tvaudio.h>
#include <media/i2c-addr.h>
#include <media/tveeprom.h>
#include <media/v4l2-common.h>
+#include <sound/ac97_codec.h>
#include "em28xx.h"
{-1, 0, 0, 0},
};
+static struct em28xx_led terratec_grabby_leds[] = {
+ {
+ .role = EM28XX_LED_ANALOG_CAPTURING,
+ .gpio_reg = EM2820_R08_GPIO_CTRL,
+ .gpio_mask = EM_GPIO_3,
+ .inverted = 1,
+ },
+ {-1, 0, 0, 0},
+};
+
/*
* Board definitions
*/
.tuner_type = TUNER_ABSENT,
.is_webcam = 1,
.input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = 0,
.amux = EM28XX_AMUX_VIDEO,
.gpio = silvercrest_reg_seq,
.decoder = EM28XX_SAA711X,
.tuner_type = TUNER_ABSENT,
.input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = SAA7115_COMPOSITE0,
.amux = EM28XX_AMUX_LINE_IN,
}, {
.tuner_type = TUNER_ABSENT,
.is_webcam = 1,
.input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = 0,
.amux = EM28XX_AMUX_VIDEO,
} },
.tda9887_conf = TDA9887_PRESENT,
.decoder = EM28XX_SAA711X,
.input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = SAA7115_COMPOSITE0,
.amux = EM28XX_AMUX_LINE_IN,
}, {
.vmux = SAA7115_COMPOSITE2,
.amux = EM28XX_AMUX_LINE_IN,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = SAA7115_COMPOSITE0,
.amux = EM28XX_AMUX_LINE_IN,
}, {
.vmux = SAA7115_COMPOSITE2,
.amux = EM28XX_AMUX_VIDEO,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = SAA7115_COMPOSITE0,
.amux = EM28XX_AMUX_LINE_IN,
}, {
.vmux = SAA7115_COMPOSITE2,
.amux = EM28XX_AMUX_VIDEO,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = SAA7115_COMPOSITE0,
.amux = EM28XX_AMUX_LINE_IN,
}, {
.vmux = SAA7115_COMPOSITE2,
.amux = EM28XX_AMUX_LINE_IN,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = SAA7115_COMPOSITE0,
.amux = EM28XX_AMUX_LINE_IN,
}, {
.vmux = SAA7115_COMPOSITE2,
.amux = EM28XX_AMUX_LINE_IN,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = SAA7115_COMPOSITE0,
.amux = EM28XX_AMUX_LINE_IN,
}, {
.vmux = SAA7115_COMPOSITE2,
.amux = EM28XX_AMUX_VIDEO,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = SAA7115_COMPOSITE0,
.amux = EM28XX_AMUX_LINE_IN,
}, {
.vmux = SAA7115_COMPOSITE1,
.amux = EM28XX_AMUX_LINE_IN,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = SAA7115_COMPOSITE0,
.amux = EM28XX_AMUX_LINE_IN,
}, {
.vmux = SAA7115_COMPOSITE4,
.amux = EM28XX_AMUX_AUX,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = SAA7115_COMPOSITE5,
.amux = EM28XX_AMUX_LINE_IN,
}, {
.tuner_type = TUNER_ABSENT,
.is_webcam = 1,
.input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = 0,
.amux = EM28XX_AMUX_VIDEO,
} },
.tuner_type = TUNER_ABSENT,
.is_webcam = 1,
.input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = 0,
.amux = EM28XX_AMUX_VIDEO,
.gpio = silvercrest_reg_seq,
.vmux = SAA7115_COMPOSITE2,
.amux = EM28XX_AMUX_LINE_IN,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = SAA7115_COMPOSITE0,
.amux = EM28XX_AMUX_VIDEO,
}, {
.tuner_type = TUNER_ABSENT, /* Capture only device */
.decoder = EM28XX_SAA711X,
.input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = SAA7115_COMPOSITE0,
.amux = EM28XX_AMUX_LINE_IN,
}, {
.tuner_type = TUNER_ABSENT,
.is_webcam = 1,
.input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = 0,
.amux = EM28XX_AMUX_VIDEO,
} },
.decoder = EM28XX_SAA711X,
.tuner_type = TUNER_ABSENT, /* Capture only device */
.input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = SAA7115_COMPOSITE0,
.amux = EM28XX_AMUX_LINE_IN,
}, {
.vmux = SAA7115_COMPOSITE2,
.amux = EM28XX_AMUX_VIDEO,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = SAA7115_COMPOSITE0,
.amux = EM28XX_AMUX_LINE_IN,
}, {
.vmux = SAA7115_COMPOSITE2,
.amux = EM28XX_AMUX_VIDEO,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = SAA7115_COMPOSITE0,
.amux = EM28XX_AMUX_LINE_IN,
}, {
.amux = EM28XX_AMUX_VIDEO,
.gpio = hauppauge_wintv_hvr_900_analog,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = TVP5150_COMPOSITE1,
.amux = EM28XX_AMUX_LINE_IN,
.gpio = hauppauge_wintv_hvr_900_analog,
.vmux = TVP5150_COMPOSITE0,
.amux = EM28XX_AMUX_VIDEO,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = TVP5150_COMPOSITE1,
.amux = EM28XX_AMUX_LINE_IN,
}, {
.vmux = TVP5150_COMPOSITE0,
.amux = EM28XX_AMUX_VIDEO,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = TVP5150_COMPOSITE1,
.amux = EM28XX_AMUX_LINE_IN,
}, {
.tuner_type = TUNER_ABSENT, /* Capture only device */
.decoder = EM28XX_TVP5150,
.input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = TVP5150_COMPOSITE1,
.amux = EM28XX_AMUX_LINE_IN,
}, {
.amux = EM28XX_AMUX_LINE_IN,
.gpio = pinnacle_hybrid_pro_analog,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = TVP5150_COMPOSITE1,
.amux = EM28XX_AMUX_LINE_IN,
.gpio = pinnacle_hybrid_pro_analog,
.amux = EM28XX_AMUX_VIDEO,
.gpio = terratec_cinergy_USB_XS_FR_analog,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = TVP5150_COMPOSITE1,
.amux = EM28XX_AMUX_LINE_IN,
.gpio = terratec_cinergy_USB_XS_FR_analog,
.amux = EM28XX_AMUX_VIDEO,
.gpio = hauppauge_wintv_hvr_900_analog,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = TVP5150_COMPOSITE1,
.amux = EM28XX_AMUX_LINE_IN,
.gpio = hauppauge_wintv_hvr_900_analog,
.amux = EM28XX_AMUX_VIDEO,
.gpio = hauppauge_wintv_hvr_900_analog,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = TVP5150_COMPOSITE1,
.amux = EM28XX_AMUX_LINE_IN,
.gpio = hauppauge_wintv_hvr_900_analog,
.amux = EM28XX_AMUX_VIDEO,
.gpio = hauppauge_wintv_hvr_900_analog,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = TVP5150_COMPOSITE1,
.amux = EM28XX_AMUX_LINE_IN,
.gpio = hauppauge_wintv_hvr_900_analog,
.amux = EM28XX_AMUX_VIDEO,
.gpio = hauppauge_wintv_hvr_900_analog,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = TVP5150_COMPOSITE1,
.amux = EM28XX_AMUX_LINE_IN,
.gpio = hauppauge_wintv_hvr_900_analog,
.amux = EM28XX_AMUX_VIDEO,
.gpio = hauppauge_wintv_hvr_900_analog,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = TVP5150_COMPOSITE1,
.amux = EM28XX_AMUX_LINE_IN,
.gpio = hauppauge_wintv_hvr_900_analog,
.amux = EM28XX_AMUX_VIDEO,
.gpio = hauppauge_wintv_hvr_900_analog,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = TVP5150_COMPOSITE1,
.amux = EM28XX_AMUX_LINE_IN,
.gpio = hauppauge_wintv_hvr_900_analog,
.amux = EM28XX_AMUX_VIDEO,
.gpio = default_analog,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = TVP5150_COMPOSITE1,
.amux = EM28XX_AMUX_LINE_IN,
.gpio = default_analog,
.amux = EM28XX_AMUX_VIDEO,
.gpio = hauppauge_wintv_hvr_900_analog,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = TVP5150_COMPOSITE1,
.amux = EM28XX_AMUX_LINE_IN,
.gpio = hauppauge_wintv_hvr_900_analog,
.vmux = SAA7115_COMPOSITE4,
.amux = EM28XX_AMUX_VIDEO,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = SAA7115_COMPOSITE0,
.amux = EM28XX_AMUX_LINE_IN,
}, {
.vmux = SAA7115_COMPOSITE2,
.amux = EM28XX_AMUX_VIDEO,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = SAA7115_COMPOSITE0,
.amux = EM28XX_AMUX_LINE_IN,
}, {
.decoder = EM28XX_SAA711X,
.tuner_type = TUNER_ABSENT, /* capture only board */
.input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = SAA7115_COMPOSITE0,
.amux = EM28XX_AMUX_LINE_IN,
}, {
.tuner_type = TUNER_ABSENT, /* Capture-only board */
.decoder = EM28XX_SAA711X,
.input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = SAA7115_COMPOSITE0,
.amux = EM28XX_AMUX_LINE_IN,
.gpio = vc211a_enable,
.vmux = SAA7115_COMPOSITE2,
.amux = EM28XX_AMUX_VIDEO,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = SAA7115_COMPOSITE0,
.amux = EM28XX_AMUX_LINE_IN,
}, {
.vmux = SAA7115_COMPOSITE2,
.amux = EM28XX_AMUX_VIDEO,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = SAA7115_COMPOSITE0,
.amux = EM28XX_AMUX_LINE_IN,
}, {
.tuner_type = TUNER_ABSENT, /* capture only board */
.decoder = EM28XX_SAA711X,
.input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = SAA7115_COMPOSITE0,
.amux = EM28XX_AMUX_LINE_IN,
}, {
.vmux = SAA7115_COMPOSITE2,
.amux = EM28XX_AMUX_VIDEO,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = SAA7115_COMPOSITE0,
.amux = EM28XX_AMUX_LINE_IN,
}, {
.aout = EM28XX_AOUT_MONO | /* I2S */
EM28XX_AOUT_MASTER, /* Line out pin */
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = SAA7115_COMPOSITE0,
.amux = EM28XX_AMUX_LINE_IN,
}, {
.buttons = std_snapshot_button,
.tda9887_conf = TDA9887_PRESENT,
.tuner_type = TUNER_YMEC_TVF_5533MF,
+ .tuner_addr = 0x60,
.decoder = EM28XX_SAA711X,
.input = { {
.type = EM28XX_VMUX_TELEVISION,
.aout = EM28XX_AOUT_MONO | /* I2S */
EM28XX_AOUT_MASTER, /* Line out pin */
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = SAA7115_COMPOSITE0,
.amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = SAA7115_COMPOSITE0,
} },
},
.amux = EM28XX_AMUX_VIDEO,
.gpio = em2880_msi_digivox_ad_analog,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = TVP5150_COMPOSITE1,
.amux = EM28XX_AMUX_LINE_IN,
.gpio = em2880_msi_digivox_ad_analog,
.amux = EM28XX_AMUX_VIDEO,
.gpio = em2880_msi_digivox_ad_analog,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = TVP5150_COMPOSITE1,
.amux = EM28XX_AMUX_LINE_IN,
.gpio = em2880_msi_digivox_ad_analog,
.vmux = TVP5150_COMPOSITE0,
.amux = EM28XX_AMUX_VIDEO,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = TVP5150_COMPOSITE1,
.amux = EM28XX_AMUX_LINE_IN,
}, {
.amux = EM28XX_AMUX_VIDEO,
.gpio = default_analog,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = TVP5150_COMPOSITE1,
.amux = EM28XX_AMUX_LINE_IN,
.gpio = default_analog,
.gpio = em2882_kworld_315u_analog,
.aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = SAA7115_COMPOSITE0,
.amux = EM28XX_AMUX_LINE_IN,
.gpio = em2882_kworld_315u_analog1,
.amux = EM28XX_AMUX_VIDEO,
.gpio = default_analog,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = TVP5150_COMPOSITE1,
.amux = EM28XX_AMUX_LINE_IN,
.gpio = default_analog,
.amux = EM28XX_AMUX_VIDEO,
.gpio = default_analog,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = TVP5150_COMPOSITE1,
.amux = EM28XX_AMUX_LINE_IN,
.gpio = default_analog,
.amux = EM28XX_AMUX_VIDEO,
.gpio = pinnacle_hybrid_pro_analog,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = TVP5150_COMPOSITE1,
.amux = EM28XX_AMUX_LINE_IN,
.gpio = pinnacle_hybrid_pro_analog,
.amux = EM28XX_AMUX_VIDEO,
.gpio = hauppauge_wintv_hvr_900_analog,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = TVP5150_COMPOSITE1,
.amux = EM28XX_AMUX_LINE_IN,
.gpio = hauppauge_wintv_hvr_900_analog,
.vmux = TVP5150_COMPOSITE0,
.amux = EM28XX_AMUX_VIDEO,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = TVP5150_COMPOSITE1,
.amux = EM28XX_AMUX_LINE_IN,
}, {
.amux = EM28XX_AMUX_VIDEO,
.gpio = hauppauge_wintv_hvr_900_analog,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = TVP5150_COMPOSITE1,
.amux = EM28XX_AMUX_LINE_IN,
.gpio = hauppauge_wintv_hvr_900_analog,
.gpio = kworld_330u_analog,
.aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = TVP5150_COMPOSITE1,
.amux = EM28XX_AMUX_LINE_IN,
.gpio = kworld_330u_analog,
.amux = EM28XX_AMUX_VIDEO,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = TVP5150_COMPOSITE1,
.amux = EM28XX_AMUX_LINE_IN,
}, {
.tuner_type = TUNER_ABSENT,
.decoder = EM28XX_SAA711X,
.input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = SAA7115_COMPOSITE0,
.amux = EM28XX_AMUX_LINE_IN,
}, {
.vmux = TVP5150_COMPOSITE0,
.amux = EM28XX_AMUX_VIDEO,
}, { /* Composite has not been tested yet */
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = TVP5150_COMPOSITE1,
.amux = EM28XX_AMUX_VIDEO,
}, { /* S-video has not been tested yet */
.decoder = EM28XX_SAA711X,
.xclk = EM28XX_XCLK_FREQUENCY_12MHZ,
.input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = SAA7115_COMPOSITE0,
.amux = EM28XX_AMUX_LINE_IN,
}, {
.vmux = SAA7115_SVIDEO3,
.amux = EM28XX_AMUX_LINE_IN,
} },
+ .buttons = std_snapshot_button,
+ .leds = terratec_grabby_leds,
},
[EM2860_BOARD_TERRATEC_AV350] = {
.name = "Terratec AV350",
.xclk = EM28XX_XCLK_FREQUENCY_12MHZ,
.mute_gpio = terratec_av350_mute_gpio,
.input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = TVP5150_COMPOSITE1,
.amux = EM28XX_AUDIO_SRC_LINE,
.gpio = terratec_av350_unmute_gpio,
.decoder = EM28XX_SAA711X,
.tuner_type = TUNER_ABSENT, /* Capture only device */
.input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = SAA7115_COMPOSITE0,
.amux = EM28XX_AMUX_LINE_IN,
}, {
.amux = EM28XX_AMUX_VIDEO,
.gpio = evga_indtube_analog,
}, {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = TVP5150_COMPOSITE1,
.amux = EM28XX_AMUX_LINE_IN,
.gpio = evga_indtube_analog,
.tuner_type = TUNER_ABSENT,
.decoder = EM28XX_SAA711X,
.input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = SAA7115_COMPOSITE0,
.amux = EM28XX_AMUX_LINE_IN,
}, {
.tuner_type = TUNER_ABSENT,
.is_webcam = 1,
.input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.amux = EM28XX_AMUX_VIDEO,
.gpio = speedlink_vad_laplace_reg_seq,
} },
.tuner_type = TUNER_ABSENT, /* Capture only device */
.decoder = EM28XX_TVP5150,
.input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
+ .type = EM28XX_VMUX_COMPOSITE,
.vmux = TVP5150_COMPOSITE1,
.amux = EM28XX_AMUX_LINE_IN,
}, {
dev->def_i2c_bus = dev->board.def_i2c_bus;
}
+/* Wait until AC97_RESET reports the expected value reliably before proceeding.
+ * We also check that two unrelated registers accesses don't return the same
+ * value to avoid premature return.
+ * This procedure helps ensuring AC97 register accesses are reliable.
+ */
+static int em28xx_wait_until_ac97_features_equals(struct em28xx *dev,
+ int expected_feat)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(2000);
+ int feat, powerdown;
+
+ while (time_is_after_jiffies(timeout)) {
+ feat = em28xx_read_ac97(dev, AC97_RESET);
+ if (feat < 0)
+ return feat;
+
+ powerdown = em28xx_read_ac97(dev, AC97_POWERDOWN);
+ if (powerdown < 0)
+ return powerdown;
+
+ if (feat == expected_feat && feat != powerdown)
+ return 0;
+
+ msleep(50);
+ }
+
+ em28xx_warn("AC97 registers access is not reliable !\n");
+ return -ETIMEDOUT;
+}
+
/* Since em28xx_pre_card_setup() requires a proper dev->model,
* this won't work for boards with generic PCI IDs
*/
em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfd);
msleep(70);
break;
+
+ case EM2860_BOARD_TERRATEC_GRABBY:
+ /* HACK?: Ensure AC97 register reading is reliable before
+ * proceeding. In practice, this will wait about 1.6 seconds.
+ */
+ em28xx_wait_until_ac97_features_equals(dev, 0x6a90);
+ break;
}
em28xx_gpio_set(dev, dev->board.tuner_gpio);
flush_work(&dev->request_module_wk);
}
+static int em28xx_media_device_init(struct em28xx *dev,
+ struct usb_device *udev)
+{
+#ifdef CONFIG_MEDIA_CONTROLLER
+ struct media_device *mdev;
+
+ mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
+ if (!mdev)
+ return -ENOMEM;
+
+ if (udev->product)
+ media_device_usb_init(mdev, udev, udev->product);
+ else if (udev->manufacturer)
+ media_device_usb_init(mdev, udev, udev->manufacturer);
+ else
+ media_device_usb_init(mdev, udev, dev->name);
+
+ dev->media_dev = mdev;
+#endif
+ return 0;
+}
+
+static void em28xx_unregister_media_device(struct em28xx *dev)
+{
+
+#ifdef CONFIG_MEDIA_CONTROLLER
+ if (dev->media_dev) {
+ media_device_unregister(dev->media_dev);
+ media_device_cleanup(dev->media_dev);
+ kfree(dev->media_dev);
+ dev->media_dev = NULL;
+ }
+#endif
+}
+
/*
* em28xx_release_resources()
* unregisters the v4l2,i2c and usb devices
mutex_lock(&dev->lock);
+ em28xx_unregister_media_device(dev);
+
if (dev->def_i2c_bus)
em28xx_i2c_unregister(dev, 1);
em28xx_i2c_unregister(dev, 0);
*/
snprintf(dev->name, sizeof(dev->name), "%s #%d", chip_name, dev->devno);
+ em28xx_media_device_init(dev, udev);
+
if (dev->is_audio_only) {
retval = em28xx_audio_setup(dev);
if (retval)
/* save our data pointer in this interface device */
usb_set_intfdata(interface, dev);
- /* allocate device struct */
+ /* allocate device struct and check if the device is a webcam */
mutex_init(&dev->lock);
retval = em28xx_init_dev(dev, udev, interface, nr);
if (retval) {
try_bulk = usb_xfer_mode > 0;
}
+ /* Disable V4L2 if the device doesn't have a decoder */
+ if (has_video &&
+ dev->board.decoder == EM28XX_NODECODER && !dev->board.is_webcam) {
+ printk(DRIVER_NAME
+ ": Currently, V4L2 is not supported on this model\n");
+ has_video = false;
+ dev->has_video = false;
+ }
+
/* Select USB transfer types to use */
if (has_video) {
if (!dev->analog_ep_isoc || (try_bulk && dev->analog_ep_bulk))
request_modules(dev);
- /* Should be the last thing to do, to avoid newer udev's to
- open the device before fully initializing it
+ /*
+ * Do it at the end, to reduce dynamic configuration changes during
+ * the device init. Yet, as request_modules() can be async, the
+ * topology will likely change after the load of the em28xx subdrivers.
*/
+#ifdef CONFIG_MEDIA_CONTROLLER
+ retval = media_device_register(dev->media_dev);
+#endif
return 0;
struct em28xx *dev, struct device *device)
{
int result;
+ bool create_rf_connector = false;
mutex_init(&dvb->lock);
dev->name, result);
goto fail_adapter;
}
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ dvb->adapter.mdev = dev->media_dev;
+#endif
/* Ensure all frontends negotiate bus access */
dvb->fe[0]->ops.ts_bus_ctrl = em28xx_dvb_bus_ctrl;
/* register network adapter */
dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx);
+
+ /* If the analog part won't create RF connectors, DVB will do it */
+ if (!dev->has_video || (dev->tuner_type == TUNER_ABSENT))
+ create_rf_connector = true;
+
+ result = dvb_create_media_graph(&dvb->adapter, create_rf_connector);
+ if (result < 0)
+ goto fail_create_graph;
+
return 0;
+fail_create_graph:
+ dvb_net_release(&dvb->net);
fail_fe_conn:
dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
fail_fe_mem:
memset(&si2157_config, 0, sizeof(si2157_config));
si2157_config.fe = dvb->fe[0];
si2157_config.if_port = 1;
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ si2157_config.mdev = dev->media_dev;
+#endif
memset(&info, 0, sizeof(struct i2c_board_info));
strlcpy(info.type, "si2157", I2C_NAME_SIZE);
info.addr = 0x60;
memset(&si2157_config, 0, sizeof(si2157_config));
si2157_config.fe = dvb->fe[0];
si2157_config.if_port = 0;
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ si2157_config.mdev = dev->media_dev;
+#endif
memset(&info, 0, sizeof(struct i2c_board_info));
strlcpy(info.type, "si2146", I2C_NAME_SIZE);
info.addr = 0x60;
v4l2_device_call_all(v4l2_dev, 0, core, reset, 0);
v4l2_device_call_all(v4l2_dev, 0, video, s_routing,
INPUT(dev->ctl_input)->vmux, 0, 0);
- v4l2_device_call_all(v4l2_dev, 0, video, s_stream, 0);
}
static int em28xx_colorlevels_set_default(struct em28xx *dev)
em28xx_videodbg("res: put %d\n", res_type);
}
+static void em28xx_v4l2_media_release(struct em28xx *dev)
+{
+#ifdef CONFIG_MEDIA_CONTROLLER
+ int i;
+
+ for (i = 0; i < MAX_EM28XX_INPUT; i++) {
+ if (!INPUT(i)->type)
+ return;
+ media_device_unregister_entity(&dev->input_ent[i]);
+ }
+#endif
+}
+
+/*
+ * Media Controller helper functions
+ */
+
+static int em28xx_enable_analog_tuner(struct em28xx *dev)
+{
+#ifdef CONFIG_MEDIA_CONTROLLER
+ struct media_device *mdev = dev->media_dev;
+ struct em28xx_v4l2 *v4l2 = dev->v4l2;
+ struct media_entity *source;
+ struct media_link *link, *found_link = NULL;
+ int ret, active_links = 0;
+
+ if (!mdev || !v4l2->decoder)
+ return 0;
+
+ /*
+ * This will find the tuner that is connected into the decoder.
+ * Technically, this is not 100% correct, as the device may be
+ * using an analog input instead of the tuner. However, as we can't
+ * do DVB streaming while the DMA engine is being used for V4L2,
+ * this should be enough for the actual needs.
+ */
+ list_for_each_entry(link, &v4l2->decoder->links, list) {
+ if (link->sink->entity == v4l2->decoder) {
+ found_link = link;
+ if (link->flags & MEDIA_LNK_FL_ENABLED)
+ active_links++;
+ break;
+ }
+ }
+
+ if (active_links == 1 || !found_link)
+ return 0;
+
+ source = found_link->source->entity;
+ list_for_each_entry(link, &source->links, list) {
+ struct media_entity *sink;
+ int flags = 0;
+
+ sink = link->sink->entity;
+
+ if (sink == v4l2->decoder)
+ flags = MEDIA_LNK_FL_ENABLED;
+
+ ret = media_entity_setup_link(link, flags);
+ if (ret) {
+ pr_err("Couldn't change link %s->%s to %s. Error %d\n",
+ source->name, sink->name,
+ flags ? "enabled" : "disabled",
+ ret);
+ return ret;
+ } else
+ em28xx_videodbg("link %s->%s was %s\n",
+ source->name, sink->name,
+ flags ? "ENABLED" : "disabled");
+ }
+#endif
+ return 0;
+}
+
+static const char * const iname[] = {
+ [EM28XX_VMUX_COMPOSITE] = "Composite",
+ [EM28XX_VMUX_SVIDEO] = "S-Video",
+ [EM28XX_VMUX_TELEVISION] = "Television",
+ [EM28XX_RADIO] = "Radio",
+};
+
+static void em28xx_v4l2_create_entities(struct em28xx *dev)
+{
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ struct em28xx_v4l2 *v4l2 = dev->v4l2;
+ int ret, i;
+
+ /* Initialize Video, VBI and Radio pads */
+ v4l2->video_pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_pads_init(&v4l2->vdev.entity, 1, &v4l2->video_pad);
+ if (ret < 0)
+ pr_err("failed to initialize video media entity!\n");
+
+ if (em28xx_vbi_supported(dev)) {
+ v4l2->vbi_pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_pads_init(&v4l2->vbi_dev.entity, 1,
+ &v4l2->vbi_pad);
+ if (ret < 0)
+ pr_err("failed to initialize vbi media entity!\n");
+ }
+
+ /* Webcams don't have input connectors */
+ if (dev->board.is_webcam)
+ return;
+
+ /* Create entities for each input connector */
+ for (i = 0; i < MAX_EM28XX_INPUT; i++) {
+ struct media_entity *ent = &dev->input_ent[i];
+
+ if (!INPUT(i)->type)
+ break;
+
+ ent->name = iname[INPUT(i)->type];
+ ent->flags = MEDIA_ENT_FL_CONNECTOR;
+ dev->input_pad[i].flags = MEDIA_PAD_FL_SOURCE;
+
+ switch (INPUT(i)->type) {
+ case EM28XX_VMUX_COMPOSITE:
+ ent->function = MEDIA_ENT_F_CONN_COMPOSITE;
+ break;
+ case EM28XX_VMUX_SVIDEO:
+ ent->function = MEDIA_ENT_F_CONN_SVIDEO;
+ break;
+ default: /* EM28XX_VMUX_TELEVISION or EM28XX_RADIO */
+ if (dev->tuner_type != TUNER_ABSENT)
+ ent->function = MEDIA_ENT_F_CONN_RF;
+ break;
+ }
+
+ ret = media_entity_pads_init(ent, 1, &dev->input_pad[i]);
+ if (ret < 0)
+ pr_err("failed to initialize input pad[%d]!\n", i);
+
+ ret = media_device_register_entity(dev->media_dev, ent);
+ if (ret < 0)
+ pr_err("failed to register input entity %d!\n", i);
+ }
+#endif
+}
+
+
/* ------------------------------------------------------------------
Videobuf2 operations
------------------------------------------------------------------*/
return sizes[0] < size ? -EINVAL : 0;
*nplanes = 1;
sizes[0] = size;
+
+ em28xx_enable_analog_tuner(dev);
+
return 0;
}
f.type = V4L2_TUNER_ANALOG_TV;
v4l2_device_call_all(&v4l2->v4l2_dev,
0, tuner, s_frequency, &f);
+
+ /* Enable video stream at TV decoder */
+ v4l2_device_call_all(&v4l2->v4l2_dev, 0, video, s_stream, 1);
}
v4l2->streaming_users++;
res_free(dev, vq->type);
if (v4l2->streaming_users-- == 1) {
+ /* Disable video stream at TV decoder */
+ v4l2_device_call_all(&v4l2->v4l2_dev, 0, video, s_stream, 0);
+
/* Last active user, so shutdown all the URBS */
em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE);
}
res_free(dev, vq->type);
if (v4l2->streaming_users-- == 1) {
+ /* Disable video stream at TV decoder */
+ v4l2_device_call_all(&v4l2->v4l2_dev, 0, video, s_stream, 0);
+
/* Last active user, so shutdown all the URBS */
em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE);
}
*width = (((unsigned long)maxw) << 12) / (hscale + 4096L);
*height = (((unsigned long)maxh) << 12) / (vscale + 4096L);
+
+ /* Don't let width or height to be zero */
+ if (*width < 1)
+ *width = 1;
+ if (*height < 1)
+ *height = 1;
}
/* ------------------------------------------------------------------
v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh,
1, 0);
}
+ /* Avoid division by zero at size_to_scale */
+ if (width < 1)
+ width = 1;
+ if (height < 1)
+ height = 1;
size_to_scale(dev, width, height, &hscale, &vscale);
scale_to_size(dev, hscale, vscale, &width, &height);
0, video, s_parm, p);
}
-static const char *iname[] = {
- [EM28XX_VMUX_COMPOSITE1] = "Composite1",
- [EM28XX_VMUX_COMPOSITE2] = "Composite2",
- [EM28XX_VMUX_COMPOSITE3] = "Composite3",
- [EM28XX_VMUX_COMPOSITE4] = "Composite4",
- [EM28XX_VMUX_SVIDEO] = "S-Video",
- [EM28XX_VMUX_TELEVISION] = "Television",
- [EM28XX_VMUX_CABLE] = "Cable TV",
- [EM28XX_VMUX_DVB] = "DVB",
- [EM28XX_VMUX_DEBUG] = "for debug only",
-};
-
static int vidioc_enum_input(struct file *file, void *priv,
struct v4l2_input *i)
{
strcpy(i->name, iname[INPUT(n)->type]);
- if ((EM28XX_VMUX_TELEVISION == INPUT(n)->type) ||
- (EM28XX_VMUX_CABLE == INPUT(n)->type))
+ if ((EM28XX_VMUX_TELEVISION == INPUT(n)->type))
i->type = V4L2_INPUT_TYPE_TUNER;
i->std = dev->v4l2->vdev.tvnorms;
em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE);
+ em28xx_v4l2_media_release(dev);
+
if (video_is_registered(&v4l2->radio_dev)) {
em28xx_info("V4L2 device %s deregistered\n",
video_device_node_name(&v4l2->radio_dev));
v4l2->dev = dev;
dev->v4l2 = v4l2;
+#ifdef CONFIG_MEDIA_CONTROLLER
+ v4l2->v4l2_dev.mdev = dev->media_dev;
+#endif
ret = v4l2_device_register(&dev->udev->dev, &v4l2->v4l2_dev);
if (ret < 0) {
em28xx_errdev("Call to v4l2_device_register() failed!\n");
video_device_node_name(&v4l2->radio_dev));
}
+ /* Init entities at the Media Controller */
+ em28xx_v4l2_create_entities(dev);
+
+#ifdef CONFIG_MEDIA_CONTROLLER
+ ret = v4l2_mc_create_media_graph(dev->media_dev);
+ if (ret) {
+ em28xx_errdev("failed to create media graph\n");
+ em28xx_v4l2_media_release(dev);
+ goto unregister_dev;
+ }
+#endif
+
em28xx_info("V4L2 video device registered as %s\n",
video_device_node_name(&v4l2->vdev));
return 0;
unregister_dev:
+ if (video_is_registered(&v4l2->radio_dev)) {
+ em28xx_info("V4L2 device %s deregistered\n",
+ video_device_node_name(&v4l2->radio_dev));
+ video_unregister_device(&v4l2->radio_dev);
+ }
+ if (video_is_registered(&v4l2->vbi_dev)) {
+ em28xx_info("V4L2 device %s deregistered\n",
+ video_device_node_name(&v4l2->vbi_dev));
+ video_unregister_device(&v4l2->vbi_dev);
+ }
+ if (video_is_registered(&v4l2->vdev)) {
+ em28xx_info("V4L2 device %s deregistered\n",
+ video_device_node_name(&v4l2->vdev));
+ video_unregister_device(&v4l2->vdev);
+ }
+
v4l2_ctrl_handler_free(&v4l2->ctrl_handler);
v4l2_device_unregister(&v4l2->v4l2_dev);
err:
#ifndef _EM28XX_H
#define _EM28XX_H
-#define EM28XX_VERSION "0.2.1"
+#define EM28XX_VERSION "0.2.2"
#define DRIVER_DESC "Empia em28xx device driver"
#include <linux/workqueue.h>
#define MAX_EM28XX_INPUT 4
enum enum28xx_itype {
- EM28XX_VMUX_COMPOSITE1 = 1,
- EM28XX_VMUX_COMPOSITE2,
- EM28XX_VMUX_COMPOSITE3,
- EM28XX_VMUX_COMPOSITE4,
+ EM28XX_VMUX_COMPOSITE = 1,
EM28XX_VMUX_SVIDEO,
EM28XX_VMUX_TELEVISION,
- EM28XX_VMUX_CABLE,
- EM28XX_VMUX_DVB,
- EM28XX_VMUX_DEBUG,
EM28XX_RADIO,
};
bool top_field;
int vbi_read;
unsigned int field_count;
+
+#ifdef CONFIG_MEDIA_CONTROLLER
+ struct media_pad video_pad, vbi_pad;
+ struct media_entity *decoder;
+#endif
};
struct em28xx_audio {
/* Snapshot button input device */
char snapshot_button_path[30]; /* path of the input dev */
struct input_dev *sbutton_input_dev;
+
+#ifdef CONFIG_MEDIA_CONTROLLER
+ struct media_device *media_dev;
+ struct media_entity input_ent[MAX_EM28XX_INPUT];
+ struct media_pad input_pad[MAX_EM28XX_INPUT];
+#endif
};
#define kref_to_dev(d) container_of(d, struct em28xx, ref)
struct i2c_adapter i2c_adapter;
/* HPI driver */
- struct go7007_hpi_ops *hpi_ops;
+ const struct go7007_hpi_ops *hpi_ops;
void *hpi_context;
int interrupt_available;
wait_queue_head_t interrupt_waitq;
kfree(go->hpi_context);
}
-static struct go7007_hpi_ops go7007_usb_ezusb_hpi_ops = {
+static const struct go7007_hpi_ops go7007_usb_ezusb_hpi_ops = {
.interface_reset = go7007_usb_interface_reset,
.write_interrupt = go7007_usb_ezusb_write_interrupt,
.read_interrupt = go7007_usb_read_interrupt,
.release = go7007_usb_release,
};
-static struct go7007_hpi_ops go7007_usb_onboard_hpi_ops = {
+static const struct go7007_hpi_ops go7007_usb_onboard_hpi_ops = {
.interface_reset = go7007_usb_interface_reset,
.write_interrupt = go7007_usb_onboard_write_interrupt,
.read_interrupt = go7007_usb_read_interrupt,
.priv = 0},
};
-static const struct v4l2_pix_format ovfx2_vga_mode[] = {
- {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 320 * 240,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 1},
- {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
- .bytesperline = 640,
- .sizeimage = 640 * 480,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 0},
-};
-static const struct v4l2_pix_format ovfx2_cif_mode[] = {
- {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
- .bytesperline = 160,
- .sizeimage = 160 * 120,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 3},
- {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
- .bytesperline = 176,
- .sizeimage = 176 * 144,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 1},
- {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 320 * 240,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 2},
- {352, 288, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
- .bytesperline = 352,
- .sizeimage = 352 * 288,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 0},
-};
static const struct v4l2_pix_format ovfx2_ov2610_mode[] = {
{800, 600, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
.bytesperline = 800,
if (sd->gspca_dev.usb_err < 0)
return;
+ /* Avoid things going to fast for the bridge with a xhci host */
+ udelay(150);
+
switch (sd->bridge) {
case BRIDGE_OV511:
case BRIDGE_OV511PLUS:
req = 1;
}
+ /* Avoid things going to fast for the bridge with a xhci host */
+ udelay(150);
ret = usb_control_msg(sd->gspca_dev.dev,
usb_rcvctrlpipe(sd->gspca_dev.dev, 0),
req,
if (sd->gspca_dev.usb_err < 0)
return -1;
+ /* Avoid things going to fast for the bridge with a xhci host */
+ udelay(150);
ret = usb_control_msg(sd->gspca_dev.dev,
usb_rcvctrlpipe(sd->gspca_dev.dev, 0),
1, /* REQ_IO */
*((__le32 *) sd->gspca_dev.usb_buf) = __cpu_to_le32(value);
+ /* Avoid things going to fast for the bridge with a xhci host */
+ udelay(150);
ret = usb_control_msg(sd->gspca_dev.dev,
usb_sndctrlpipe(sd->gspca_dev.dev, 0),
1 /* REG_IO */,
return -EIO;
}
if (reply[0] != 0x08) {
- PERR("Bad reply 0x%02X", reply[0]);
+ PERR("Bad reply 0x%02x", (int)reply[0]);
return -EIO;
}
return 0;
static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index)
{
- char buff[1];
+ char *buff = gspca_dev->usb_buf;
int rc;
PDEBUG(D_USBO,
value, index);
rc = usb_control_msg(gspca_dev->dev, usb_rcvctrlpipe(gspca_dev->dev, 0),
0x0B, 0xC0, value, index, buff, 1, 500);
- PDEBUG(D_USBO, "rc=%d, ret={0x%02X}", rc, buff[0]);
+ PDEBUG(D_USBO, "rc=%d, ret={0x%02x}", rc, (int)buff[0]);
if (rc < 0) {
PERR("Failed reg_w(0x0B, 0xC0, 0x%04X, 0x%04X) w/ rc %d\n",
value, index, rc);
static int configure(struct gspca_dev *gspca_dev)
{
int rc;
- uint8_t buff[4];
+ char *buff = gspca_dev->usb_buf;
PDEBUG(D_STREAM, "configure()\n");
value = *data++;
memcpy(sd->gspca_dev.usb_buf, data, 6);
+ /* Avoid things going to fast for the bridge with a xhci host */
+ udelay(150);
ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0,
USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
value, 0x06, sd->gspca_dev.usb_buf, 6, 500);
if (sd->gspca_dev.usb_err < 0)
return;
+ /* Avoid things going to fast for the bridge with a xhci host */
+ udelay(150);
+
/* We don't use reg_w here, as that would cause all writes when
bitbanging i2c to be logged, making the logs impossible to read */
ret = usb_control_msg(sd->gspca_dev.dev,
if (sd->gspca_dev.usb_err < 0)
return -1;
+ /* Avoid things going to fast for the bridge with a xhci host */
+ udelay(150);
+
/* We don't use reg_r here, as the w9968cf is special and has 16
bit registers instead of 8 bit */
ret = usb_control_msg(sd->gspca_dev.dev,
struct hdpvr_device *dev;
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
+#if IS_ENABLED(CONFIG_I2C)
struct i2c_client *client;
+#endif
size_t buffer_size;
int i;
int retval = -ENOMEM;
}
if (wait_event_interruptible(dev->wait_data,
- buf->status == BUFSTAT_READY)) {
- ret = -ERESTARTSYS;
- goto err;
- }
+ buf->status == BUFSTAT_READY))
+ return -ERESTARTSYS;
}
if (buf->status != BUFSTAT_READY)
goto err;
ret = msi2500_ctrl_msg(dev, CMD_WREG, reg3);
- if (ret)
- goto err;
err:
return ret;
}
pvr2_context_thread_ptr = kthread_run(pvr2_context_thread_func,
NULL,
"pvrusb2-context");
- return (pvr2_context_thread_ptr ? 0 : -ENOMEM);
+ return IS_ERR(pvr2_context_thread_ptr) ? -ENOMEM : 0;
}
printk(KERN_INFO "%s %.*s\n",hdw->name,ccnt,buf);
}
ccnt = pvr2_hdw_report_clients(hdw, buf, sizeof(buf));
+ if (ccnt >= sizeof(buf))
+ ccnt = sizeof(buf);
+
ucnt = 0;
while (ucnt < ccnt) {
lcnt = 0;
}
spin_unlock_irqrestore(&sp->list_lock,irq_flags);
pvr2_buffer_set_ready(bp);
- if (sp && sp->callback_func) {
+ if (sp->callback_func) {
sp->callback_func(sp->callback_data);
}
}
{ USB_DEVICE(0x0471, 0x0312) },
{ USB_DEVICE(0x0471, 0x0313) }, /* the 'new' 720K */
{ USB_DEVICE(0x0471, 0x0329) }, /* Philips SPC 900NC PC Camera */
+ { USB_DEVICE(0x0471, 0x032C) }, /* Philips SPC 880NC PC Camera */
{ USB_DEVICE(0x069A, 0x0001) }, /* Askey */
{ USB_DEVICE(0x046D, 0x08B0) }, /* Logitech QuickCam Pro 3000 */
{ USB_DEVICE(0x046D, 0x08B1) }, /* Logitech QuickCam Notebook Pro */
name = "Philips SPC 900NC webcam";
type_id = 740;
break;
+ case 0x032C:
+ PWC_INFO("Philips SPC 880NC USB webcam detected.\n");
+ name = "Philips SPC 880NC webcam";
+ type_id = 740;
+ break;
default:
return -ENODEV;
break;
#include <linux/firmware.h>
#include <linux/slab.h>
#include <linux/module.h>
+#include <media/media-device.h>
#include "sms-cards.h"
#include "smsendian.h"
struct smsusb_device_t *dev;
struct urb urb;
+
+ /* For the bottom half */
+ struct work_struct wq;
};
struct smsusb_device_t {
static int smsusb_submit_urb(struct smsusb_device_t *dev,
struct smsusb_urb_t *surb);
+/**
+ * Completing URB's callback handler - bottom half (proccess context)
+ * submits the URB prepared on smsusb_onresponse()
+ */
+static void do_submit_urb(struct work_struct *work)
+{
+ struct smsusb_urb_t *surb = container_of(work, struct smsusb_urb_t, wq);
+ struct smsusb_device_t *dev = surb->dev;
+
+ smsusb_submit_urb(dev, surb);
+}
+
/**
* Completing URB's callback handler - top half (interrupt context)
* adds completing sms urb to the global surbs list and activtes the worker
exit_and_resubmit:
- smsusb_submit_urb(dev, surb);
+ INIT_WORK(&surb->wq, do_submit_urb);
+ schedule_work(&surb->wq);
}
static int smsusb_submit_urb(struct smsusb_device_t *dev,
struct smsusb_urb_t *surb)
{
if (!surb->cb) {
+ /* This function can sleep */
surb->cb = smscore_getbuffer(dev->coredev);
if (!surb->cb) {
pr_err("smscore_getbuffer(...) returned NULL\n");
if (!mdev)
return NULL;
- mdev->dev = &udev->dev;
- strlcpy(mdev->model, board->name, sizeof(mdev->model));
- if (udev->serial)
- strlcpy(mdev->serial, udev->serial, sizeof(mdev->serial));
- strcpy(mdev->bus_info, udev->devpath);
- mdev->hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
- mdev->driver_version = LINUX_VERSION_CODE;
-
- media_device_init(mdev);
+ media_device_usb_init(mdev, udev, board->name);
ret = media_device_register(mdev);
if (ret) {
buf->vb.sequence = dev->sequence++;
buf->vb.field = V4L2_FIELD_INTERLACED;
- buf->vb.vb2_buf.planes[0].bytesused = buf->bytesused;
buf->vb.vb2_buf.timestamp = ktime_get_ns();
vb2_set_plane_payload(&buf->vb.vb2_buf, 0, buf->bytesused);
usbtv_chunk_to_vbuf(frame, &chunk[1], chunk_no, odd);
usbtv->chunks_done++;
- /* Last chunk in a frame, signalling an end */
- if (odd && chunk_no == usbtv->n_chunks-1) {
- int size = vb2_plane_size(&buf->vb.vb2_buf, 0);
- enum vb2_buffer_state state = usbtv->chunks_done ==
- usbtv->n_chunks ?
- VB2_BUF_STATE_DONE :
- VB2_BUF_STATE_ERROR;
-
- buf->vb.field = V4L2_FIELD_INTERLACED;
- buf->vb.sequence = usbtv->sequence++;
- buf->vb.vb2_buf.timestamp = ktime_get_ns();
- vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size);
- vb2_buffer_done(&buf->vb.vb2_buf, state);
- list_del(&buf->list);
+ /* Last chunk in a field */
+ if (chunk_no == usbtv->n_chunks-1) {
+ /* Last chunk in a frame, signalling an end */
+ if (odd && !usbtv->last_odd) {
+ int size = vb2_plane_size(&buf->vb.vb2_buf, 0);
+ enum vb2_buffer_state state = usbtv->chunks_done ==
+ usbtv->n_chunks ?
+ VB2_BUF_STATE_DONE :
+ VB2_BUF_STATE_ERROR;
+
+ buf->vb.field = V4L2_FIELD_INTERLACED;
+ buf->vb.sequence = usbtv->sequence++;
+ buf->vb.vb2_buf.timestamp = ktime_get_ns();
+ vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size);
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
+ list_del(&buf->list);
+ }
+ usbtv->last_odd = odd;
}
spin_unlock_irqrestore(&usbtv->buflock, flags);
ip->transfer_flags = URB_ISO_ASAP;
ip->transfer_buffer = kzalloc(size * USBTV_ISOC_PACKETS,
GFP_KERNEL);
+ if (!ip->transfer_buffer) {
+ usb_free_urb(ip);
+ return NULL;
+ }
ip->complete = usbtv_iso_cb;
ip->number_of_packets = USBTV_ISOC_PACKETS;
ip->transfer_buffer_length = size * USBTV_ISOC_PACKETS;
if (usbtv->udev == NULL)
return -ENODEV;
+ usbtv->last_odd = 1;
usbtv->sequence = 0;
return usbtv_start(usbtv);
}
int width, height;
int n_chunks;
int iso_size;
+ int last_odd;
unsigned int sequence;
struct urb *isoc_urbs[USBTV_ISOC_TRANSFERS];
static inline struct usb_usbvision *cd_to_usbvision(struct device *cd)
{
- struct video_device *vdev =
- container_of(cd, struct video_device, dev);
+ struct video_device *vdev = to_video_device(cd);
return video_get_drvdata(vdev);
}
static ssize_t show_model(struct device *cd,
struct device_attribute *attr, char *buf)
{
- struct video_device *vdev =
- container_of(cd, struct video_device, dev);
+ struct video_device *vdev = to_video_device(cd);
struct usb_usbvision *usbvision = video_get_drvdata(vdev);
return sprintf(buf, "%s\n",
usbvision_device_data[usbvision->dev_model].model_string);
static ssize_t show_hue(struct device *cd,
struct device_attribute *attr, char *buf)
{
- struct video_device *vdev =
- container_of(cd, struct video_device, dev);
+ struct video_device *vdev = to_video_device(cd);
struct usb_usbvision *usbvision = video_get_drvdata(vdev);
struct v4l2_control ctrl;
ctrl.id = V4L2_CID_HUE;
static ssize_t show_contrast(struct device *cd,
struct device_attribute *attr, char *buf)
{
- struct video_device *vdev =
- container_of(cd, struct video_device, dev);
+ struct video_device *vdev = to_video_device(cd);
struct usb_usbvision *usbvision = video_get_drvdata(vdev);
struct v4l2_control ctrl;
ctrl.id = V4L2_CID_CONTRAST;
static ssize_t show_brightness(struct device *cd,
struct device_attribute *attr, char *buf)
{
- struct video_device *vdev =
- container_of(cd, struct video_device, dev);
+ struct video_device *vdev = to_video_device(cd);
struct usb_usbvision *usbvision = video_get_drvdata(vdev);
struct v4l2_control ctrl;
ctrl.id = V4L2_CID_BRIGHTNESS;
static ssize_t show_saturation(struct device *cd,
struct device_attribute *attr, char *buf)
{
- struct video_device *vdev =
- container_of(cd, struct video_device, dev);
+ struct video_device *vdev = to_video_device(cd);
struct usb_usbvision *usbvision = video_get_drvdata(vdev);
struct v4l2_control ctrl;
ctrl.id = V4L2_CID_SATURATION;
static ssize_t show_streaming(struct device *cd,
struct device_attribute *attr, char *buf)
{
- struct video_device *vdev =
- container_of(cd, struct video_device, dev);
+ struct video_device *vdev = to_video_device(cd);
struct usb_usbvision *usbvision = video_get_drvdata(vdev);
return sprintf(buf, "%s\n",
YES_NO(usbvision->streaming == stream_on ? 1 : 0));
static ssize_t show_compression(struct device *cd,
struct device_attribute *attr, char *buf)
{
- struct video_device *vdev =
- container_of(cd, struct video_device, dev);
+ struct video_device *vdev = to_video_device(cd);
struct usb_usbvision *usbvision = video_get_drvdata(vdev);
return sprintf(buf, "%s\n",
YES_NO(usbvision->isoc_mode == ISOC_MODE_COMPRESS));
static ssize_t show_device_bridge(struct device *cd,
struct device_attribute *attr, char *buf)
{
- struct video_device *vdev =
- container_of(cd, struct video_device, dev);
+ struct video_device *vdev = to_video_device(cd);
struct usb_usbvision *usbvision = video_get_drvdata(vdev);
return sprintf(buf, "%d\n", usbvision->bridge_type);
}
usbvision_audio_off(usbvision);
usbvision->radio = 0;
usbvision->user--;
+ mutex_unlock(&usbvision->v4l2_lock);
if (usbvision->remove_pending) {
printk(KERN_INFO "%s: Final disconnect\n", __func__);
return 0;
}
- mutex_unlock(&usbvision->v4l2_lock);
PDEBUG(DBG_IO, "success");
return v4l2_fh_release(file);
}
.guid = UVC_GUID_FORMAT_H264,
.fcc = V4L2_PIX_FMT_H264,
},
+ {
+ .name = "Greyscale 8 L/R (Y8I)",
+ .guid = UVC_GUID_FORMAT_Y8I,
+ .fcc = V4L2_PIX_FMT_Y8I,
+ },
+ {
+ .name = "Greyscale 12 L/R (Y12I)",
+ .guid = UVC_GUID_FORMAT_Y12I,
+ .fcc = V4L2_PIX_FMT_Y12I,
+ },
+ {
+ .name = "Depth data 16-bit (Z16)",
+ .guid = UVC_GUID_FORMAT_Z16,
+ .fcc = V4L2_PIX_FMT_Z16,
+ },
+ {
+ .name = "Bayer 10-bit (SRGGB10P)",
+ .guid = UVC_GUID_FORMAT_RW10,
+ .fcc = V4L2_PIX_FMT_SRGGB10P,
+ },
};
/* ------------------------------------------------------------------------
#define UVC_GUID_FORMAT_H264 \
{ 'H', '2', '6', '4', 0x00, 0x00, 0x10, 0x00, \
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_Y8I \
+ { 'Y', '8', 'I', ' ', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_Y12I \
+ { 'Y', '1', '2', 'I', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_Z16 \
+ { 'Z', '1', '6', ' ', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_RW10 \
+ { 'R', 'W', '1', '0', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
/* ------------------------------------------------------------------------
* Driver specific constants.
# Used by drivers that need tuner.ko
config VIDEO_TUNER
tristate
- depends on MEDIA_TUNER
# Used by drivers that need v4l2-mem2mem.ko
config V4L2_MEM2MEM_DEV
ifeq ($(CONFIG_TRACEPOINTS),y)
videodev-objs += vb2-trace.o v4l2-trace.o
endif
+videodev-$(CONFIG_MEDIA_CONTROLLER) += v4l2-mc.o
obj-$(CONFIG_VIDEO_V4L2) += videodev.o
obj-$(CONFIG_VIDEO_V4L2) += v4l2-common.o
/* Should be just before return */
register_client:
#if defined(CONFIG_MEDIA_CONTROLLER)
- t->pad[TUNER_PAD_RF_INPUT].flags = MEDIA_PAD_FL_SINK;
- t->pad[TUNER_PAD_IF_OUTPUT].flags = MEDIA_PAD_FL_SOURCE;
- t->sd.entity.function = MEDIA_ENT_F_TUNER;
t->sd.entity.name = t->name;
+ /*
+ * Handle the special case where the tuner has actually
+ * two stages: the PLL to tune into a frequency and the
+ * IF-PLL demodulator (tda988x).
+ */
+ if (t->type == TUNER_TDA9887) {
+ t->pad[IF_VID_DEC_PAD_IF_INPUT].flags = MEDIA_PAD_FL_SINK;
+ t->pad[IF_VID_DEC_PAD_OUT].flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&t->sd.entity,
+ IF_VID_DEC_PAD_NUM_PADS,
+ &t->pad[0]);
+ t->sd.entity.function = MEDIA_ENT_F_IF_VID_DECODER;
+ } else {
+ t->pad[TUNER_PAD_RF_INPUT].flags = MEDIA_PAD_FL_SINK;
+ t->pad[TUNER_PAD_OUTPUT].flags = MEDIA_PAD_FL_SOURCE;
+ t->pad[TUNER_PAD_AUD_OUT].flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&t->sd.entity, TUNER_NUM_PADS,
+ &t->pad[0]);
+ t->sd.entity.function = MEDIA_ENT_F_TUNER;
+ }
- ret = media_entity_pads_init(&t->sd.entity, TUNER_NUM_PADS, &t->pad[0]);
if (ret < 0) {
tuner_err("failed to initialize media entity!\n");
kfree(t);
- return -ENODEV;
+ return ret;
}
#endif
/* Sets a default mode */
return ret;
}
+ ret = v4l2_subdev_call(sd, core, registered_async);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ if (notifier->unbind)
+ notifier->unbind(notifier, sd, asd);
+ return ret;
+ }
+
if (list_empty(¬ifier->waiting) && notifier->complete)
return notifier->complete(notifier);
get_user(kp->index, &up->index) ||
get_user(kp->type, &up->type) ||
get_user(kp->flags, &up->flags) ||
- get_user(kp->memory, &up->memory))
+ get_user(kp->memory, &up->memory) ||
+ get_user(kp->length, &up->length))
return -EFAULT;
if (V4L2_TYPE_IS_OUTPUT(kp->type))
return -EFAULT;
if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) {
- if (get_user(kp->length, &up->length))
- return -EFAULT;
-
num_planes = kp->length;
if (num_planes == 0) {
kp->m.planes = NULL;
} else {
switch (kp->memory) {
case V4L2_MEMORY_MMAP:
- if (get_user(kp->length, &up->length) ||
- get_user(kp->m.offset, &up->m.offset))
+ if (get_user(kp->m.offset, &up->m.offset))
return -EFAULT;
break;
case V4L2_MEMORY_USERPTR:
{
compat_long_t tmp;
- if (get_user(kp->length, &up->length) ||
- get_user(tmp, &up->m.userptr))
+ if (get_user(tmp, &up->m.userptr))
return -EFAULT;
kp->m.userptr = (unsigned long)compat_ptr(tmp);
copy_to_user(&up->timecode, &kp->timecode, sizeof(struct v4l2_timecode)) ||
put_user(kp->sequence, &up->sequence) ||
put_user(kp->reserved2, &up->reserved2) ||
- put_user(kp->reserved, &up->reserved))
+ put_user(kp->reserved, &up->reserved) ||
+ put_user(kp->length, &up->length))
return -EFAULT;
if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) {
} else {
switch (kp->memory) {
case V4L2_MEMORY_MMAP:
- if (put_user(kp->length, &up->length) ||
- put_user(kp->m.offset, &up->m.offset))
+ if (put_user(kp->m.offset, &up->m.offset))
return -EFAULT;
break;
case V4L2_MEMORY_USERPTR:
- if (put_user(kp->length, &up->length) ||
- put_user(kp->m.userptr, &up->m.userptr))
+ if (put_user(kp->m.userptr, &up->m.userptr))
return -EFAULT;
break;
case V4L2_MEMORY_OVERLAY:
"RGB full range (0-255)",
NULL,
};
+ static const char * const dv_it_content_type[] = {
+ "Graphics",
+ "Photo",
+ "Cinema",
+ "Game",
+ "No IT Content",
+ NULL,
+ };
static const char * const detect_md_mode[] = {
"Disabled",
"Global",
case V4L2_CID_DV_TX_RGB_RANGE:
case V4L2_CID_DV_RX_RGB_RANGE:
return dv_rgb_range;
+ case V4L2_CID_DV_TX_IT_CONTENT_TYPE:
+ case V4L2_CID_DV_RX_IT_CONTENT_TYPE:
+ return dv_it_content_type;
case V4L2_CID_DETECT_MD_MODE:
return detect_md_mode;
case V4L2_CID_MPEG_VIDEO_MV_H_SEARCH_RANGE: return "Horizontal MV Search Range";
case V4L2_CID_MPEG_VIDEO_MV_V_SEARCH_RANGE: return "Vertical MV Search Range";
case V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER: return "Repeat Sequence Header";
+ case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME: return "Force Key Frame";
/* VPX controls */
case V4L2_CID_MPEG_VIDEO_VPX_NUM_PARTITIONS: return "VPX Number of Partitions";
case V4L2_CID_DV_TX_EDID_PRESENT: return "EDID Present";
case V4L2_CID_DV_TX_MODE: return "Transmit Mode";
case V4L2_CID_DV_TX_RGB_RANGE: return "Tx RGB Quantization Range";
+ case V4L2_CID_DV_TX_IT_CONTENT_TYPE: return "Tx IT Content Type";
case V4L2_CID_DV_RX_POWER_PRESENT: return "Power Present";
case V4L2_CID_DV_RX_RGB_RANGE: return "Rx RGB Quantization Range";
+ case V4L2_CID_DV_RX_IT_CONTENT_TYPE: return "Rx IT Content Type";
case V4L2_CID_FM_RX_CLASS: return "FM Radio Receiver Controls";
case V4L2_CID_TUNE_DEEMPHASIS: return "De-Emphasis";
case V4L2_CID_MPEG_VIDEO_MV_V_SEARCH_RANGE:
*type = V4L2_CTRL_TYPE_INTEGER;
break;
+ case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME:
case V4L2_CID_PAN_RESET:
case V4L2_CID_TILT_RESET:
case V4L2_CID_FLASH_STROBE:
case V4L2_CID_SCENE_MODE:
case V4L2_CID_DV_TX_MODE:
case V4L2_CID_DV_TX_RGB_RANGE:
+ case V4L2_CID_DV_TX_IT_CONTENT_TYPE:
case V4L2_CID_DV_RX_RGB_RANGE:
+ case V4L2_CID_DV_RX_IT_CONTENT_TYPE:
case V4L2_CID_TEST_PATTERN:
case V4L2_CID_TUNE_DEEMPHASIS:
case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL:
case V4L2_CID_DV_TX_RXSENSE:
case V4L2_CID_DV_TX_EDID_PRESENT:
case V4L2_CID_DV_RX_POWER_PRESENT:
+ case V4L2_CID_DV_RX_IT_CONTENT_TYPE:
case V4L2_CID_RDS_RX_PTY:
case V4L2_CID_RDS_RX_PS_NAME:
case V4L2_CID_RDS_RX_RADIO_TEXT:
}
EXPORT_SYMBOL(v4l2_ctrl_new_int_menu);
-/* Add a control from another handler to this handler */
-struct v4l2_ctrl *v4l2_ctrl_add_ctrl(struct v4l2_ctrl_handler *hdl,
- struct v4l2_ctrl *ctrl)
-{
- if (hdl == NULL || hdl->error)
- return NULL;
- if (ctrl == NULL) {
- handler_set_err(hdl, -EINVAL);
- return NULL;
- }
- if (ctrl->handler == hdl)
- return ctrl;
- return handler_new_ref(hdl, ctrl) ? NULL : ctrl;
-}
-EXPORT_SYMBOL(v4l2_ctrl_add_ctrl);
-
/* Add the controls from another handler to our own. */
int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl,
struct v4l2_ctrl_handler *add,
bt->width > cap->max_width ||
bt->pixelclock < cap->min_pixelclock ||
bt->pixelclock > cap->max_pixelclock ||
- (cap->standards && bt->standards &&
+ (!(caps & V4L2_DV_BT_CAP_CUSTOM) &&
+ cap->standards && bt->standards &&
!(bt->standards & cap->standards)) ||
(bt->interlaced && !(caps & V4L2_DV_BT_CAP_INTERLACED)) ||
(!bt->interlaced && !(caps & V4L2_DV_BT_CAP_PROGRESSIVE)))
#include <media/v4l2-fh.h>
#include <media/v4l2-event.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mc.h>
void v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev)
{
{
if (fh->vdev == NULL)
return;
+ v4l_disable_media_source(fh->vdev);
v4l2_event_unsubscribe_all(fh);
fh->vdev = NULL;
}
#include <media/v4l2-event.h>
#include <media/v4l2-device.h>
#include <media/videobuf2-v4l2.h>
+#include <media/v4l2-mc.h>
#include <trace/events/v4l2.h>
static int v4l_s_input(const struct v4l2_ioctl_ops *ops,
struct file *file, void *fh, void *arg)
{
+ struct video_device *vfd = video_devdata(file);
+ int ret;
+
+ ret = v4l_enable_media_source(vfd);
+ if (ret)
+ return ret;
return ops->vidioc_s_input(file, fh, *(unsigned int *)arg);
}
case V4L2_PIX_FMT_YVYU: descr = "YVYU 4:2:2"; break;
case V4L2_PIX_FMT_UYVY: descr = "UYVY 4:2:2"; break;
case V4L2_PIX_FMT_VYUY: descr = "VYUY 4:2:2"; break;
- case V4L2_PIX_FMT_YUV422P: descr = "Planar YVU 4:2:2"; break;
+ case V4L2_PIX_FMT_YUV422P: descr = "Planar YUV 4:2:2"; break;
case V4L2_PIX_FMT_YUV411P: descr = "Planar YUV 4:1:1"; break;
case V4L2_PIX_FMT_Y41P: descr = "YUV 4:1:1 (Packed)"; break;
case V4L2_PIX_FMT_YUV444: descr = "16-bit A/XYUV 4-4-4-4"; break;
case V4L2_PIX_FMT_NV12MT_16X16: descr = "Y/CbCr 4:2:0 (16x16 MB, N-C)"; break;
case V4L2_PIX_FMT_YUV420M: descr = "Planar YUV 4:2:0 (N-C)"; break;
case V4L2_PIX_FMT_YVU420M: descr = "Planar YVU 4:2:0 (N-C)"; break;
+ case V4L2_PIX_FMT_YUV422M: descr = "Planar YUV 4:2:2 (N-C)"; break;
+ case V4L2_PIX_FMT_YVU422M: descr = "Planar YVU 4:2:2 (N-C)"; break;
+ case V4L2_PIX_FMT_YUV444M: descr = "Planar YUV 4:4:4 (N-C)"; break;
+ case V4L2_PIX_FMT_YVU444M: descr = "Planar YVU 4:4:4 (N-C)"; break;
case V4L2_PIX_FMT_SBGGR8: descr = "8-bit Bayer BGBG/GRGR"; break;
case V4L2_PIX_FMT_SGBRG8: descr = "8-bit Bayer GBGB/RGRG"; break;
case V4L2_PIX_FMT_SGRBG8: descr = "8-bit Bayer GRGR/BGBG"; break;
bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
int ret;
+ ret = v4l_enable_media_source(vfd);
+ if (ret)
+ return ret;
v4l_sanitize_format(p);
switch (p->type) {
{
struct video_device *vfd = video_devdata(file);
struct v4l2_tuner *p = arg;
+ int ret;
+ ret = v4l_enable_media_source(vfd);
+ if (ret)
+ return ret;
p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
return ops->vidioc_s_tuner(file, fh, p);
struct video_device *vfd = video_devdata(file);
const struct v4l2_frequency *p = arg;
enum v4l2_tuner_type type;
+ int ret;
+ ret = v4l_enable_media_source(vfd);
+ if (ret)
+ return ret;
if (vfd->vfl_type == VFL_TYPE_SDR) {
if (p->type != V4L2_TUNER_SDR && p->type != V4L2_TUNER_RF)
return -EINVAL;
{
struct video_device *vfd = video_devdata(file);
v4l2_std_id id = *(v4l2_std_id *)arg, norm;
+ int ret;
+ ret = v4l_enable_media_source(vfd);
+ if (ret)
+ return ret;
norm = id & vfd->tvnorms;
if (vfd->tvnorms && !norm) /* Check if std is supported */
return -EINVAL;
{
struct video_device *vfd = video_devdata(file);
v4l2_std_id *p = arg;
+ int ret;
+ ret = v4l_enable_media_source(vfd);
+ if (ret)
+ return ret;
/*
* If no signal is detected, then the driver should return
* V4L2_STD_UNKNOWN. Otherwise it should return tvnorms with
struct video_device *vfd = video_devdata(file);
struct v4l2_hw_freq_seek *p = arg;
enum v4l2_tuner_type type;
+ int ret;
+ ret = v4l_enable_media_source(vfd);
+ if (ret)
+ return ret;
/* s_hw_freq_seek is not supported for SDR for now */
if (vfd->vfl_type == VFL_TYPE_SDR)
return -EINVAL;
--- /dev/null
+/*
+ * Media Controller ancillary functions
+ *
+ * Copyright (c) 2016 Mauro Carvalho Chehab <mchehab@osg.samsung.com>
+ * Copyright (C) 2016 Shuah Khan <shuahkh@osg.samsung.com>
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (c) 2016 Intel Corporation.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/usb.h>
+#include <media/media-device.h>
+#include <media/media-entity.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-subdev.h>
+#include <media/media-device.h>
+#include <media/v4l2-mc.h>
+#include <media/videobuf2-core.h>
+
+int v4l2_mc_create_media_graph(struct media_device *mdev)
+
+{
+ struct media_entity *entity;
+ struct media_entity *if_vid = NULL, *if_aud = NULL;
+ struct media_entity *tuner = NULL, *decoder = NULL, *dtv_demod = NULL;
+ struct media_entity *io_v4l = NULL, *io_vbi = NULL, *io_swradio = NULL;
+ bool is_webcam = false;
+ u32 flags;
+ int ret;
+
+ if (!mdev)
+ return 0;
+
+ media_device_for_each_entity(entity, mdev) {
+ switch (entity->function) {
+ case MEDIA_ENT_F_IF_VID_DECODER:
+ if_vid = entity;
+ break;
+ case MEDIA_ENT_F_IF_AUD_DECODER:
+ if_aud = entity;
+ break;
+ case MEDIA_ENT_F_TUNER:
+ tuner = entity;
+ break;
+ case MEDIA_ENT_F_ATV_DECODER:
+ decoder = entity;
+ break;
+ case MEDIA_ENT_F_IO_V4L:
+ io_v4l = entity;
+ break;
+ case MEDIA_ENT_F_IO_VBI:
+ io_vbi = entity;
+ break;
+ case MEDIA_ENT_F_IO_SWRADIO:
+ io_swradio = entity;
+ break;
+ case MEDIA_ENT_F_CAM_SENSOR:
+ is_webcam = true;
+ break;
+ }
+ }
+
+ /* It should have at least one I/O entity */
+ if (!io_v4l && !io_vbi && !io_swradio)
+ return -EINVAL;
+
+ /*
+ * Here, webcams are modelled on a very simple way: the sensor is
+ * connected directly to the I/O entity. All dirty details, like
+ * scaler and crop HW are hidden. While such mapping is not enough
+ * for mc-centric hardware, it is enough for v4l2 interface centric
+ * PC-consumer's hardware.
+ */
+ if (is_webcam) {
+ if (!io_v4l)
+ return -EINVAL;
+
+ media_device_for_each_entity(entity, mdev) {
+ if (entity->function != MEDIA_ENT_F_CAM_SENSOR)
+ continue;
+ ret = media_create_pad_link(entity, 0,
+ io_v4l, 0,
+ MEDIA_LNK_FL_ENABLED);
+ if (ret)
+ return ret;
+ }
+ if (!decoder)
+ return 0;
+ }
+
+ /* The device isn't a webcam. So, it should have a decoder */
+ if (!decoder)
+ return -EINVAL;
+
+ /* Link the tuner and IF video output pads */
+ if (tuner) {
+ if (if_vid) {
+ ret = media_create_pad_link(tuner, TUNER_PAD_OUTPUT,
+ if_vid,
+ IF_VID_DEC_PAD_IF_INPUT,
+ MEDIA_LNK_FL_ENABLED);
+ if (ret)
+ return ret;
+ ret = media_create_pad_link(if_vid, IF_VID_DEC_PAD_OUT,
+ decoder, DEMOD_PAD_IF_INPUT,
+ MEDIA_LNK_FL_ENABLED);
+ if (ret)
+ return ret;
+ } else {
+ ret = media_create_pad_link(tuner, TUNER_PAD_OUTPUT,
+ decoder, DEMOD_PAD_IF_INPUT,
+ MEDIA_LNK_FL_ENABLED);
+ if (ret)
+ return ret;
+ }
+
+ if (if_aud) {
+ ret = media_create_pad_link(tuner, TUNER_PAD_AUD_OUT,
+ if_aud,
+ IF_AUD_DEC_PAD_IF_INPUT,
+ MEDIA_LNK_FL_ENABLED);
+ if (ret)
+ return ret;
+ } else {
+ if_aud = tuner;
+ }
+
+ }
+
+ /* Create demod to V4L, VBI and SDR radio links */
+ if (io_v4l) {
+ ret = media_create_pad_link(decoder, DEMOD_PAD_VID_OUT,
+ io_v4l, 0,
+ MEDIA_LNK_FL_ENABLED);
+ if (ret)
+ return ret;
+ }
+
+ if (io_swradio) {
+ ret = media_create_pad_link(decoder, DEMOD_PAD_VID_OUT,
+ io_swradio, 0,
+ MEDIA_LNK_FL_ENABLED);
+ if (ret)
+ return ret;
+ }
+
+ if (io_vbi) {
+ ret = media_create_pad_link(decoder, DEMOD_PAD_VBI_OUT,
+ io_vbi, 0,
+ MEDIA_LNK_FL_ENABLED);
+ if (ret)
+ return ret;
+ }
+
+ /* Create links for the media connectors */
+ flags = MEDIA_LNK_FL_ENABLED;
+ media_device_for_each_entity(entity, mdev) {
+ switch (entity->function) {
+ case MEDIA_ENT_F_CONN_RF:
+ if (!tuner)
+ continue;
+
+ ret = media_create_pad_link(entity, 0, tuner,
+ TUNER_PAD_RF_INPUT,
+ flags);
+ break;
+ case MEDIA_ENT_F_CONN_SVIDEO:
+ case MEDIA_ENT_F_CONN_COMPOSITE:
+ ret = media_create_pad_link(entity, 0, decoder,
+ DEMOD_PAD_IF_INPUT,
+ flags);
+ break;
+ default:
+ continue;
+ }
+ if (ret)
+ return ret;
+
+ flags = 0;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_mc_create_media_graph);
+
+int v4l_enable_media_source(struct video_device *vdev)
+{
+ struct media_device *mdev = vdev->entity.graph_obj.mdev;
+ int ret;
+
+ if (!mdev || !mdev->enable_source)
+ return 0;
+ ret = mdev->enable_source(&vdev->entity, &vdev->pipe);
+ if (ret)
+ return -EBUSY;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l_enable_media_source);
+
+void v4l_disable_media_source(struct video_device *vdev)
+{
+ struct media_device *mdev = vdev->entity.graph_obj.mdev;
+
+ if (mdev && mdev->disable_source)
+ mdev->disable_source(&vdev->entity);
+}
+EXPORT_SYMBOL_GPL(v4l_disable_media_source);
+
+int v4l_vb2q_enable_media_source(struct vb2_queue *q)
+{
+ struct v4l2_fh *fh = q->owner;
+
+ if (fh && fh->vdev)
+ return v4l_enable_media_source(fh->vdev);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l_vb2q_enable_media_source);
+
+/* -----------------------------------------------------------------------------
+ * Pipeline power management
+ *
+ * Entities must be powered up when part of a pipeline that contains at least
+ * one open video device node.
+ *
+ * To achieve this use the entity use_count field to track the number of users.
+ * For entities corresponding to video device nodes the use_count field stores
+ * the users count of the node. For entities corresponding to subdevs the
+ * use_count field stores the total number of users of all video device nodes
+ * in the pipeline.
+ *
+ * The v4l2_pipeline_pm_use() function must be called in the open() and
+ * close() handlers of video device nodes. It increments or decrements the use
+ * count of all subdev entities in the pipeline.
+ *
+ * To react to link management on powered pipelines, the link setup notification
+ * callback updates the use count of all entities in the source and sink sides
+ * of the link.
+ */
+
+/*
+ * pipeline_pm_use_count - Count the number of users of a pipeline
+ * @entity: The entity
+ *
+ * Return the total number of users of all video device nodes in the pipeline.
+ */
+static int pipeline_pm_use_count(struct media_entity *entity,
+ struct media_entity_graph *graph)
+{
+ int use = 0;
+
+ media_entity_graph_walk_start(graph, entity);
+
+ while ((entity = media_entity_graph_walk_next(graph))) {
+ if (is_media_entity_v4l2_io(entity))
+ use += entity->use_count;
+ }
+
+ return use;
+}
+
+/*
+ * pipeline_pm_power_one - Apply power change to an entity
+ * @entity: The entity
+ * @change: Use count change
+ *
+ * Change the entity use count by @change. If the entity is a subdev update its
+ * power state by calling the core::s_power operation when the use count goes
+ * from 0 to != 0 or from != 0 to 0.
+ *
+ * Return 0 on success or a negative error code on failure.
+ */
+static int pipeline_pm_power_one(struct media_entity *entity, int change)
+{
+ struct v4l2_subdev *subdev;
+ int ret;
+
+ subdev = is_media_entity_v4l2_subdev(entity)
+ ? media_entity_to_v4l2_subdev(entity) : NULL;
+
+ if (entity->use_count == 0 && change > 0 && subdev != NULL) {
+ ret = v4l2_subdev_call(subdev, core, s_power, 1);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return ret;
+ }
+
+ entity->use_count += change;
+ WARN_ON(entity->use_count < 0);
+
+ if (entity->use_count == 0 && change < 0 && subdev != NULL)
+ v4l2_subdev_call(subdev, core, s_power, 0);
+
+ return 0;
+}
+
+/*
+ * pipeline_pm_power - Apply power change to all entities in a pipeline
+ * @entity: The entity
+ * @change: Use count change
+ *
+ * Walk the pipeline to update the use count and the power state of all non-node
+ * entities.
+ *
+ * Return 0 on success or a negative error code on failure.
+ */
+static int pipeline_pm_power(struct media_entity *entity, int change,
+ struct media_entity_graph *graph)
+{
+ struct media_entity *first = entity;
+ int ret = 0;
+
+ if (!change)
+ return 0;
+
+ media_entity_graph_walk_start(graph, entity);
+
+ while (!ret && (entity = media_entity_graph_walk_next(graph)))
+ if (is_media_entity_v4l2_subdev(entity))
+ ret = pipeline_pm_power_one(entity, change);
+
+ if (!ret)
+ return ret;
+
+ media_entity_graph_walk_start(graph, first);
+
+ while ((first = media_entity_graph_walk_next(graph))
+ && first != entity)
+ if (is_media_entity_v4l2_subdev(first))
+ pipeline_pm_power_one(first, -change);
+
+ return ret;
+}
+
+int v4l2_pipeline_pm_use(struct media_entity *entity, int use)
+{
+ struct media_device *mdev = entity->graph_obj.mdev;
+ int change = use ? 1 : -1;
+ int ret;
+
+ mutex_lock(&mdev->graph_mutex);
+
+ /* Apply use count to node. */
+ entity->use_count += change;
+ WARN_ON(entity->use_count < 0);
+
+ /* Apply power change to connected non-nodes. */
+ ret = pipeline_pm_power(entity, change, &mdev->pm_count_walk);
+ if (ret < 0)
+ entity->use_count -= change;
+
+ mutex_unlock(&mdev->graph_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_pipeline_pm_use);
+
+int v4l2_pipeline_link_notify(struct media_link *link, u32 flags,
+ unsigned int notification)
+{
+ struct media_entity_graph *graph = &link->graph_obj.mdev->pm_count_walk;
+ struct media_entity *source = link->source->entity;
+ struct media_entity *sink = link->sink->entity;
+ int source_use;
+ int sink_use;
+ int ret = 0;
+
+ source_use = pipeline_pm_use_count(source, graph);
+ sink_use = pipeline_pm_use_count(sink, graph);
+
+ if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&
+ !(flags & MEDIA_LNK_FL_ENABLED)) {
+ /* Powering off entities is assumed to never fail. */
+ pipeline_pm_power(source, -sink_use, graph);
+ pipeline_pm_power(sink, -source_use, graph);
+ return 0;
+ }
+
+ if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH &&
+ (flags & MEDIA_LNK_FL_ENABLED)) {
+
+ ret = pipeline_pm_power(source, sink_use, graph);
+ if (ret < 0)
+ return ret;
+
+ ret = pipeline_pm_power(sink, source_use, graph);
+ if (ret < 0)
+ pipeline_pm_power(source, -sink_use, graph);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_pipeline_link_notify);
* variable without a low fixed limit. Please use
* v4l2_of_alloc_parse_endpoint() in new drivers instead.
*
- * Return: 0.
+ * Return: 0 on success or a negative error code on failure.
*/
int v4l2_of_parse_endpoint(const struct device_node *node,
struct v4l2_of_endpoint *endpoint)
}
EXPORT_SYMBOL_GPL(videobuf_alloc_vb);
-static int is_state_active_or_queued(struct videobuf_queue *q, struct videobuf_buffer *vb)
+static int state_neither_active_nor_queued(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
{
unsigned long flags;
bool rc;
MAGIC_CHECK(vb->magic, MAGIC_BUFFER);
if (non_blocking) {
- if (is_state_active_or_queued(q, vb))
+ if (state_neither_active_nor_queued(q, vb))
return 0;
return -EAGAIN;
}
if (is_ext_locked)
mutex_unlock(q->ext_lock);
if (intr)
- ret = wait_event_interruptible(vb->done, is_state_active_or_queued(q, vb));
+ ret = wait_event_interruptible(vb->done,
+ state_neither_active_nor_queued(q, vb));
else
- wait_event(vb->done, is_state_active_or_queued(q, vb));
+ wait_event(vb->done, state_neither_active_nor_queued(q, vb));
/* Relock */
if (is_ext_locked)
mutex_lock(q->ext_lock);
#include <linux/kthread.h>
#include <media/videobuf2-core.h>
+#include <media/v4l2-mc.h>
#include <trace/events/vb2.h>
if (planes[plane].length < vb->planes[plane].min_length) {
dprintk(1, "invalid dmabuf length for plane %d\n",
plane);
+ dma_buf_put(dbuf);
ret = -EINVAL;
goto err;
}
* are available.
*/
if (q->queued_count >= q->min_buffers_needed) {
+ ret = v4l_vb2q_enable_media_source(q);
+ if (ret)
+ return ret;
ret = vb2_start_streaming(q);
if (ret) {
__vb2_queue_cancel(q);
struct module *module,
void *adapter_priv,
struct device *device,
+ struct media_device *mdev,
char *adapter_name,
short *adapter_nr,
int mfe_shared)
}
fe->adapter.priv = adapter_priv;
fe->adapter.mfe_shared = mfe_shared;
-
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ if (mdev)
+ fe->adapter.mdev = mdev;
+#endif
return result;
}
struct module *module,
void *adapter_priv,
struct device *device,
+ struct media_device *mdev,
short *adapter_nr,
int mfe_shared)
{
}
/* Bring up the adapter */
- res = vb2_dvb_register_adapter(f, module, adapter_priv, device,
+ res = vb2_dvb_register_adapter(f, module, adapter_priv, device, mdev,
fe->dvb.name, adapter_nr, mfe_shared);
if (res < 0) {
pr_warn("vb2_dvb_register_adapter failed (errno = %d)\n", res);
fe->dvb.name, res);
goto err;
}
+ res = dvb_create_media_graph(&f->adapter, false);
+ if (res < 0)
+ goto err;
}
+
mutex_unlock(&f->lock);
return 0;
source "drivers/staging/media/mn88472/Kconfig"
-source "drivers/staging/media/mn88473/Kconfig"
+source "drivers/staging/media/mx2/Kconfig"
+
+source "drivers/staging/media/mx3/Kconfig"
+
+source "drivers/staging/media/omap1/Kconfig"
source "drivers/staging/media/omap4iss/Kconfig"
+source "drivers/staging/media/timb/Kconfig"
+
# Keep LIRC at the end, as it has sub-menus
source "drivers/staging/media/lirc/Kconfig"
obj-$(CONFIG_DVB_CXD2099) += cxd2099/
obj-$(CONFIG_LIRC_STAGING) += lirc/
obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/
+obj-$(CONFIG_VIDEO_MX2) += mx2/
+obj-$(CONFIG_VIDEO_MX3) += mx3/
+obj-$(CONFIG_VIDEO_OMAP1) += omap1/
obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/
obj-$(CONFIG_DVB_MN88472) += mn88472/
-obj-$(CONFIG_DVB_MN88473) += mn88473/
+obj-$(CONFIG_VIDEO_TIMBERDALE) += timb/
};
/**********************************************************************
- IPIPE API Structures
+* IPIPE API Structures
**********************************************************************/
/* IPIPE module configurations */
return 0;
}
}
- out(LIRC_LP_CONTROL, LP_PSELECP|LP_PINITP);
+ out(LIRC_LP_CONTROL, LP_PSELECP | LP_PINITP);
is_claimed = 1;
return 1;
}
init = 1;
}
- timeout = timer/10; /* timeout after 1/10 sec. */
+ timeout = timer / 10; /* timeout after 1/10 sec. */
signal = 1;
level = lirc_get_timer();
do {
/* adjust value to usecs */
__u64 helper;
- helper = ((__u64) signal)*1000000;
+ helper = ((__u64)signal) * 1000000;
do_div(helper, timer);
- signal = (long) helper;
+ signal = (long)helper;
if (signal > LIRC_SFH506_DELAY)
data = signal - LIRC_SFH506_DELAY;
else
data = 1;
- rbuf_write(PULSE_BIT|data); /* pulse */
+ rbuf_write(PULSE_BIT | data); /* pulse */
}
lastkt = ktime_get();
#else
set_current_state(TASK_INTERRUPTIBLE);
while (count < n) {
if (rptr != wptr) {
- if (copy_to_user(buf+count, &rbuf[rptr],
+ if (copy_to_user(buf + count, &rbuf[rptr],
sizeof(int))) {
result = -EFAULT;
break;
for (i = 0; i < count; i++) {
__u64 helper;
- helper = ((__u64) wbuf[i])*timer;
+ helper = ((__u64)wbuf[i]) * timer;
do_div(helper, 1000000);
- wbuf[i] = (int) helper;
+ wbuf[i] = (int)helper;
}
local_irq_save(flags);
goto exit_device_put;
pport = parport_find_base(io);
- if (pport == NULL) {
+ if (!pport) {
pr_notice("no port at %x found\n", io);
result = -ENXIO;
goto exit_device_put;
pf, kf, lirc_lirc_irq_handler, 0,
NULL);
parport_put_port(pport);
- if (ppdevice == NULL) {
+ if (!ppdevice) {
pr_notice("parport_register_device() failed\n");
result = -ENXIO;
goto exit_device_put;
if (parport_claim(ppdevice) != 0)
goto skip_init;
is_claimed = 1;
- out(LIRC_LP_CONTROL, LP_PSELECP|LP_PINITP);
+ out(LIRC_LP_CONTROL, LP_PSELECP | LP_PINITP);
#ifdef LIRC_TIMER
if (debug)
MODULE_PARM_DESC(irq, "Interrupt (7 or 5)");
module_param(tx_mask, int, S_IRUGO);
-MODULE_PARM_DESC(tx_maxk, "Transmitter mask (default: 0x01)");
+MODULE_PARM_DESC(tx_mask, "Transmitter mask (default: 0x01)");
module_param(debug, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Enable debugging messages");
module_exit(zilog_exit);
MODULE_DESCRIPTION("Zilog/Hauppauge infrared transmitter driver (i2c stack)");
-MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, "
- "Ulrich Mueller, Stefan Jahn, Jerome Brock, Mark Weaver, "
- "Andy Walls");
+MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, Ulrich Mueller, Stefan Jahn, Jerome Brock, Mark Weaver, Andy Walls");
MODULE_LICENSE("GPL");
/* for compat with old name, which isn't all that accurate anymore */
MODULE_ALIAS("lirc_pvr150");
+++ /dev/null
-config DVB_MN88473
- tristate "Panasonic MN88473"
- depends on DVB_CORE && I2C
- select REGMAP_I2C
- default m if !MEDIA_SUBDRV_AUTOSELECT
- help
- Say Y when you want to support this frontend.
+++ /dev/null
-obj-$(CONFIG_DVB_MN88473) += mn88473.o
-
-ccflags-y += -Idrivers/media/dvb-core/
-ccflags-y += -Idrivers/media/dvb-frontends/
-ccflags-y += -Idrivers/media/tuners/
+++ /dev/null
-Driver general quality is not good enough for mainline. Also, other
-device drivers (USB-bridge, tuner) needed for Astrometa receiver in
-question could need some changes. However, if that driver is mainlined
-due to some other device than Astrometa, unrelated TODOs could be
-skipped. In that case rtl28xxu driver needs module parameter to prevent
-driver loading.
-
-Required TODOs:
-* missing lock flags
-* I2C errors
-* tuner sensitivity
-
-*Do not* send any patch fixing checkpatch.pl issues. Currently it passes
-checkpatch.pl tests. I don't want waste my time to review this kind of
-trivial stuff. *Do not* add missing register I/O error checks. Those are
-missing for the reason it is much easier to compare I2C data sniffs when
-there is less lines. Those error checks are about the last thing to be added.
-
-Patches should be submitted to:
-linux-media@vger.kernel.org and Antti Palosaari <crope@iki.fi>
-
+++ /dev/null
-/*
- * Panasonic MN88473 DVB-T/T2/C demodulator driver
- *
- * Copyright (C) 2014 Antti Palosaari <crope@iki.fi>
- *
- * 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.
- */
-
-#include "mn88473_priv.h"
-
-static int mn88473_get_tune_settings(struct dvb_frontend *fe,
- struct dvb_frontend_tune_settings *s)
-{
- s->min_delay_ms = 1000;
- return 0;
-}
-
-static int mn88473_set_frontend(struct dvb_frontend *fe)
-{
- struct i2c_client *client = fe->demodulator_priv;
- struct mn88473_dev *dev = i2c_get_clientdata(client);
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- int ret, i;
- u32 if_frequency;
- u64 tmp;
- u8 delivery_system_val, if_val[3], bw_val[7];
-
- dev_dbg(&client->dev,
- "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) {
- ret = -EAGAIN;
- goto err;
- }
-
- switch (c->delivery_system) {
- case SYS_DVBT:
- delivery_system_val = 0x02;
- break;
- case SYS_DVBT2:
- delivery_system_val = 0x03;
- break;
- case SYS_DVBC_ANNEX_A:
- delivery_system_val = 0x04;
- break;
- default:
- ret = -EINVAL;
- goto err;
- }
-
- if (c->bandwidth_hz <= 6000000) {
- memcpy(bw_val, "\xe9\x55\x55\x1c\x29\x1c\x29", 7);
- } else if (c->bandwidth_hz <= 7000000) {
- memcpy(bw_val, "\xc8\x00\x00\x17\x0a\x17\x0a", 7);
- } else if (c->bandwidth_hz <= 8000000) {
- memcpy(bw_val, "\xaf\x00\x00\x11\xec\x11\xec", 7);
- } else {
- ret = -EINVAL;
- goto err;
- }
-
- /* program tuner */
- if (fe->ops.tuner_ops.set_params) {
- ret = fe->ops.tuner_ops.set_params(fe);
- if (ret)
- goto err;
- }
-
- if (fe->ops.tuner_ops.get_if_frequency) {
- ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency);
- if (ret)
- goto err;
-
- dev_dbg(&client->dev, "get_if_frequency=%d\n", if_frequency);
- } else {
- if_frequency = 0;
- }
-
- /* 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], 0x05, 0x00);
- ret = regmap_write(dev->regmap[2], 0xfb, 0x13);
- ret = regmap_write(dev->regmap[2], 0xef, 0x13);
- ret = regmap_write(dev->regmap[2], 0xf9, 0x13);
- ret = regmap_write(dev->regmap[2], 0x00, 0x18);
- ret = regmap_write(dev->regmap[2], 0x01, 0x01);
- ret = regmap_write(dev->regmap[2], 0x02, 0x21);
- ret = regmap_write(dev->regmap[2], 0x03, delivery_system_val);
- ret = regmap_write(dev->regmap[2], 0x0b, 0x00);
-
- for (i = 0; i < sizeof(if_val); i++) {
- ret = regmap_write(dev->regmap[2], 0x10 + i, if_val[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;
- }
-
- ret = regmap_write(dev->regmap[2], 0x2d, 0x3b);
- ret = regmap_write(dev->regmap[2], 0x2e, 0x00);
- ret = regmap_write(dev->regmap[2], 0x56, 0x0d);
- ret = regmap_write(dev->regmap[0], 0x01, 0xba);
- ret = regmap_write(dev->regmap[0], 0x02, 0x13);
- ret = regmap_write(dev->regmap[0], 0x03, 0x80);
- ret = regmap_write(dev->regmap[0], 0x04, 0xba);
- ret = regmap_write(dev->regmap[0], 0x05, 0x91);
- ret = regmap_write(dev->regmap[0], 0x07, 0xe7);
- ret = regmap_write(dev->regmap[0], 0x08, 0x28);
- ret = regmap_write(dev->regmap[0], 0x0a, 0x1a);
- ret = regmap_write(dev->regmap[0], 0x13, 0x1f);
- ret = regmap_write(dev->regmap[0], 0x19, 0x03);
- ret = regmap_write(dev->regmap[0], 0x1d, 0xb0);
- ret = regmap_write(dev->regmap[0], 0x2a, 0x72);
- ret = regmap_write(dev->regmap[0], 0x2d, 0x00);
- ret = regmap_write(dev->regmap[0], 0x3c, 0x00);
- ret = regmap_write(dev->regmap[0], 0x3f, 0xf8);
- ret = regmap_write(dev->regmap[0], 0x40, 0xf4);
- ret = regmap_write(dev->regmap[0], 0x41, 0x08);
- ret = regmap_write(dev->regmap[0], 0xd2, 0x29);
- ret = regmap_write(dev->regmap[0], 0xd4, 0x55);
- ret = regmap_write(dev->regmap[1], 0x10, 0x10);
- ret = regmap_write(dev->regmap[1], 0x11, 0xab);
- ret = regmap_write(dev->regmap[1], 0x12, 0x0d);
- ret = regmap_write(dev->regmap[1], 0x13, 0xae);
- ret = regmap_write(dev->regmap[1], 0x14, 0x1d);
- ret = regmap_write(dev->regmap[1], 0x15, 0x9d);
- ret = regmap_write(dev->regmap[1], 0xbe, 0x08);
- ret = regmap_write(dev->regmap[2], 0x09, 0x08);
- ret = regmap_write(dev->regmap[2], 0x08, 0x1d);
- ret = regmap_write(dev->regmap[0], 0xb2, 0x37);
- ret = regmap_write(dev->regmap[0], 0xd7, 0x04);
- ret = regmap_write(dev->regmap[2], 0x32, 0x80);
- ret = regmap_write(dev->regmap[2], 0x36, 0x00);
- 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 mn88473_read_status(struct dvb_frontend *fe, enum fe_status *status)
-{
- struct i2c_client *client = fe->demodulator_priv;
- struct mn88473_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], 0x62, &utmp);
- if (ret)
- goto err;
- if (!(utmp & 0xA0)) {
- if ((utmp & 0xF) >= 0x03)
- *status |= FE_HAS_SIGNAL;
- if ((utmp & 0xF) >= 0x09)
- lock = 1;
- }
- break;
- case SYS_DVBT2:
- ret = regmap_read(dev->regmap[2], 0x8B, &utmp);
- if (ret)
- goto err;
- if (!(utmp & 0x40)) {
- 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], 0x85, &utmp);
- if (ret)
- goto err;
- if (!(utmp & 0x40)) {
- ret = regmap_read(dev->regmap[1], 0x89, &utmp);
- if (ret)
- goto err;
- if (utmp & 0x01)
- 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);
- return ret;
-}
-
-static int mn88473_init(struct dvb_frontend *fe)
-{
- struct i2c_client *client = fe->demodulator_priv;
- struct mn88473_dev *dev = i2c_get_clientdata(client);
- int ret, len, remaining;
- const struct firmware *fw = NULL;
- u8 *fw_file = MN88473_FIRMWARE;
- unsigned int tmp;
-
- dev_dbg(&client->dev, "\n");
-
- /* set cold state by default */
- dev->warm = false;
-
- /* check if firmware is already running */
- ret = regmap_read(dev->regmap[0], 0xf5, &tmp);
- if (ret)
- goto err;
-
- 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);
- if (ret) {
- dev_err(&client->dev, "firmare file '%s' not found\n", fw_file);
- goto err_request_firmware;
- }
-
- dev_info(&client->dev, "downloading firmware from file '%s'\n",
- fw_file);
-
- ret = regmap_write(dev->regmap[0], 0xf5, 0x03);
- if (ret)
- goto err;
-
- 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;
-
- ret = regmap_bulk_write(dev->regmap[0], 0xf6,
- &fw->data[fw->size - remaining], len);
- if (ret) {
- dev_err(&client->dev, "firmware download failed=%d\n",
- ret);
- goto err;
- }
- }
-
- /* 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 err;
- }
- if (tmp & 0x10) {
- dev_err(&client->dev,
- "firmware parity check failed=0x%x\n", tmp);
- goto err;
- }
- dev_err(&client->dev, "firmware parity check succeeded=0x%x\n", tmp);
-
- ret = regmap_write(dev->regmap[0], 0xf5, 0x00);
- if (ret)
- goto err;
-
- release_firmware(fw);
- fw = NULL;
-
- /* warm state */
- dev->warm = true;
-
- return 0;
-
-err:
- release_firmware(fw);
-err_request_firmware:
- dev_dbg(&client->dev, "failed=%d\n", ret);
- return ret;
-}
-
-static int mn88473_sleep(struct dvb_frontend *fe)
-{
- struct i2c_client *client = fe->demodulator_priv;
- struct mn88473_dev *dev = i2c_get_clientdata(client);
- int ret;
-
- dev_dbg(&client->dev, "\n");
-
- 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);
- return ret;
-}
-
-static struct dvb_frontend_ops mn88473_ops = {
- .delsys = {SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_AC},
- .info = {
- .name = "Panasonic MN88473",
- .symbol_rate_min = 1000000,
- .symbol_rate_max = 7200000,
- .caps = FE_CAN_FEC_1_2 |
- FE_CAN_FEC_2_3 |
- FE_CAN_FEC_3_4 |
- FE_CAN_FEC_5_6 |
- FE_CAN_FEC_7_8 |
- FE_CAN_FEC_AUTO |
- FE_CAN_QPSK |
- FE_CAN_QAM_16 |
- FE_CAN_QAM_32 |
- FE_CAN_QAM_64 |
- FE_CAN_QAM_128 |
- FE_CAN_QAM_256 |
- FE_CAN_QAM_AUTO |
- FE_CAN_TRANSMISSION_MODE_AUTO |
- FE_CAN_GUARD_INTERVAL_AUTO |
- FE_CAN_HIERARCHY_AUTO |
- FE_CAN_MUTE_TS |
- FE_CAN_2G_MODULATION |
- FE_CAN_MULTISTREAM
- },
-
- .get_tune_settings = mn88473_get_tune_settings,
-
- .init = mn88473_init,
- .sleep = mn88473_sleep,
-
- .set_frontend = mn88473_set_frontend,
-
- .read_status = mn88473_read_status,
-};
-
-static int mn88473_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct mn88473_config *config = client->dev.platform_data;
- struct mn88473_dev *dev;
- int ret;
- unsigned int utmp;
- static const struct regmap_config regmap_config = {
- .reg_bits = 8,
- .val_bits = 8,
- };
-
- 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 == NULL) {
- ret = -ENOMEM;
- goto err;
- }
-
- dev->i2c_wr_max = config->i2c_wr_max;
- if (!config->xtal)
- dev->xtal = 25000000;
- else
- dev->xtal = config->xtal;
- dev->client[0] = client;
- dev->regmap[0] = regmap_init_i2c(dev->client[0], ®map_config);
- if (IS_ERR(dev->regmap[0])) {
- ret = PTR_ERR(dev->regmap[0]);
- goto err_kfree;
- }
-
- /* check demod answers to I2C */
- ret = regmap_read(dev->regmap[0], 0x00, &utmp);
- if (ret)
- goto err_regmap_0_regmap_exit;
-
- /*
- * Chip has three I2C addresses for different register pages. 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.
- */
- dev->client[1] = i2c_new_dummy(client->adapter, 0x1a);
- if (dev->client[1] == NULL) {
- ret = -ENODEV;
- dev_err(&client->dev, "I2C registration failed\n");
- if (ret)
- goto err_regmap_0_regmap_exit;
- }
- dev->regmap[1] = regmap_init_i2c(dev->client[1], ®map_config);
- if (IS_ERR(dev->regmap[1])) {
- ret = PTR_ERR(dev->regmap[1]);
- goto err_client_1_i2c_unregister_device;
- }
- i2c_set_clientdata(dev->client[1], dev);
-
- dev->client[2] = i2c_new_dummy(client->adapter, 0x1c);
- if (dev->client[2] == NULL) {
- ret = -ENODEV;
- dev_err(&client->dev, "2nd I2C registration failed\n");
- if (ret)
- goto err_regmap_1_regmap_exit;
- }
- dev->regmap[2] = regmap_init_i2c(dev->client[2], ®map_config);
- if (IS_ERR(dev->regmap[2])) {
- ret = PTR_ERR(dev->regmap[2]);
- goto err_client_2_i2c_unregister_device;
- }
- i2c_set_clientdata(dev->client[2], dev);
-
- /* create dvb_frontend */
- memcpy(&dev->fe.ops, &mn88473_ops, sizeof(struct dvb_frontend_ops));
- dev->fe.demodulator_priv = client;
- *config->fe = &dev->fe;
- i2c_set_clientdata(client, dev);
-
- dev_info(&dev->client[0]->dev, "Panasonic MN88473 successfully attached\n");
- return 0;
-
-err_client_2_i2c_unregister_device:
- i2c_unregister_device(dev->client[2]);
-err_regmap_1_regmap_exit:
- regmap_exit(dev->regmap[1]);
-err_client_1_i2c_unregister_device:
- i2c_unregister_device(dev->client[1]);
-err_regmap_0_regmap_exit:
- regmap_exit(dev->regmap[0]);
-err_kfree:
- kfree(dev);
-err:
- dev_dbg(&client->dev, "failed=%d\n", ret);
- return ret;
-}
-
-static int mn88473_remove(struct i2c_client *client)
-{
- struct mn88473_dev *dev = i2c_get_clientdata(client);
-
- dev_dbg(&client->dev, "\n");
-
- regmap_exit(dev->regmap[2]);
- i2c_unregister_device(dev->client[2]);
-
- regmap_exit(dev->regmap[1]);
- i2c_unregister_device(dev->client[1]);
-
- regmap_exit(dev->regmap[0]);
-
- kfree(dev);
-
- return 0;
-}
-
-static const struct i2c_device_id mn88473_id_table[] = {
- {"mn88473", 0},
- {}
-};
-MODULE_DEVICE_TABLE(i2c, mn88473_id_table);
-
-static struct i2c_driver mn88473_driver = {
- .driver = {
- .name = "mn88473",
- },
- .probe = mn88473_probe,
- .remove = mn88473_remove,
- .id_table = mn88473_id_table,
-};
-
-module_i2c_driver(mn88473_driver);
-
-MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
-MODULE_DESCRIPTION("Panasonic MN88473 DVB-T/T2/C demodulator driver");
-MODULE_LICENSE("GPL");
-MODULE_FIRMWARE(MN88473_FIRMWARE);
+++ /dev/null
-/*
- * Panasonic MN88473 DVB-T/T2/C demodulator driver
- *
- * Copyright (C) 2014 Antti Palosaari <crope@iki.fi>
- *
- * 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 MN88473_PRIV_H
-#define MN88473_PRIV_H
-
-#include "dvb_frontend.h"
-#include "mn88473.h"
-#include <linux/firmware.h>
-#include <linux/regmap.h>
-
-#define MN88473_FIRMWARE "dvb-demod-mn88473-01.fw"
-
-struct mn88473_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;
-};
-
-#endif
--- /dev/null
+config VIDEO_MX2
+ tristate "i.MX27 Camera Sensor Interface driver"
+ depends on VIDEO_DEV && SOC_CAMERA
+ depends on SOC_IMX27 || COMPILE_TEST
+ depends on HAS_DMA
+ select VIDEOBUF2_DMA_CONTIG
+ ---help---
+ This is a v4l2 driver for the i.MX27 Camera Sensor Interface
+
+ This driver is deprecated: it should become a stand-alone driver
+ instead of using the soc-camera framework.
+
+ Unless someone is willing to take this on (unlikely with such
+ ancient hardware) it is going to be removed from the kernel
+ soon.
--- /dev/null
+# Makefile for i.MX27 Camera Sensor driver
+
+obj-$(CONFIG_VIDEO_MX2) += mx2_camera.o
--- /dev/null
+This driver is deprecated: it should become a stand-alone driver instead of
+using the soc-camera framework.
+
+Unless someone is willing to take this on (unlikely with such ancient
+hardware) it is going to be removed from the kernel soon.
+
+Note that trivial patches will not be accepted anymore, only a full conversion.
+
+If you want to convert this driver, please contact the linux-media mailinglist
+(see http://linuxtv.org/lists.php).
--- /dev/null
+/*
+ * V4L2 Driver for i.MX27 camera host
+ *
+ * Copyright (C) 2008, Sascha Hauer, Pengutronix
+ * Copyright (C) 2010, Baruch Siach, Orex Computed Radiography
+ * Copyright (C) 2012, Javier Martin, Vista Silicon S.L.
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/gcd.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/mm.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/soc_camera.h>
+#include <media/drv-intf/soc_mediabus.h>
+
+#include <linux/videodev2.h>
+
+#include <linux/platform_data/media/camera-mx2.h>
+
+#include <asm/dma.h>
+
+#define MX2_CAM_DRV_NAME "mx2-camera"
+#define MX2_CAM_VERSION "0.0.6"
+#define MX2_CAM_DRIVER_DESCRIPTION "i.MX2x_Camera"
+
+/* reset values */
+#define CSICR1_RESET_VAL 0x40000800
+#define CSICR2_RESET_VAL 0x0
+#define CSICR3_RESET_VAL 0x0
+
+/* csi control reg 1 */
+#define CSICR1_SWAP16_EN (1 << 31)
+#define CSICR1_EXT_VSYNC (1 << 30)
+#define CSICR1_EOF_INTEN (1 << 29)
+#define CSICR1_PRP_IF_EN (1 << 28)
+#define CSICR1_CCIR_MODE (1 << 27)
+#define CSICR1_COF_INTEN (1 << 26)
+#define CSICR1_SF_OR_INTEN (1 << 25)
+#define CSICR1_RF_OR_INTEN (1 << 24)
+#define CSICR1_STATFF_LEVEL (3 << 22)
+#define CSICR1_STATFF_INTEN (1 << 21)
+#define CSICR1_RXFF_LEVEL(l) (((l) & 3) << 19)
+#define CSICR1_RXFF_INTEN (1 << 18)
+#define CSICR1_SOF_POL (1 << 17)
+#define CSICR1_SOF_INTEN (1 << 16)
+#define CSICR1_MCLKDIV(d) (((d) & 0xF) << 12)
+#define CSICR1_HSYNC_POL (1 << 11)
+#define CSICR1_CCIR_EN (1 << 10)
+#define CSICR1_MCLKEN (1 << 9)
+#define CSICR1_FCC (1 << 8)
+#define CSICR1_PACK_DIR (1 << 7)
+#define CSICR1_CLR_STATFIFO (1 << 6)
+#define CSICR1_CLR_RXFIFO (1 << 5)
+#define CSICR1_GCLK_MODE (1 << 4)
+#define CSICR1_INV_DATA (1 << 3)
+#define CSICR1_INV_PCLK (1 << 2)
+#define CSICR1_REDGE (1 << 1)
+#define CSICR1_FMT_MASK (CSICR1_PACK_DIR | CSICR1_SWAP16_EN)
+
+#define SHIFT_STATFF_LEVEL 22
+#define SHIFT_RXFF_LEVEL 19
+#define SHIFT_MCLKDIV 12
+
+#define SHIFT_FRMCNT 16
+
+#define CSICR1 0x00
+#define CSICR2 0x04
+#define CSISR 0x08
+#define CSISTATFIFO 0x0c
+#define CSIRFIFO 0x10
+#define CSIRXCNT 0x14
+#define CSICR3 0x1c
+#define CSIDMASA_STATFIFO 0x20
+#define CSIDMATA_STATFIFO 0x24
+#define CSIDMASA_FB1 0x28
+#define CSIDMASA_FB2 0x2c
+#define CSIFBUF_PARA 0x30
+#define CSIIMAG_PARA 0x34
+
+/* EMMA PrP */
+#define PRP_CNTL 0x00
+#define PRP_INTR_CNTL 0x04
+#define PRP_INTRSTATUS 0x08
+#define PRP_SOURCE_Y_PTR 0x0c
+#define PRP_SOURCE_CB_PTR 0x10
+#define PRP_SOURCE_CR_PTR 0x14
+#define PRP_DEST_RGB1_PTR 0x18
+#define PRP_DEST_RGB2_PTR 0x1c
+#define PRP_DEST_Y_PTR 0x20
+#define PRP_DEST_CB_PTR 0x24
+#define PRP_DEST_CR_PTR 0x28
+#define PRP_SRC_FRAME_SIZE 0x2c
+#define PRP_DEST_CH1_LINE_STRIDE 0x30
+#define PRP_SRC_PIXEL_FORMAT_CNTL 0x34
+#define PRP_CH1_PIXEL_FORMAT_CNTL 0x38
+#define PRP_CH1_OUT_IMAGE_SIZE 0x3c
+#define PRP_CH2_OUT_IMAGE_SIZE 0x40
+#define PRP_SRC_LINE_STRIDE 0x44
+#define PRP_CSC_COEF_012 0x48
+#define PRP_CSC_COEF_345 0x4c
+#define PRP_CSC_COEF_678 0x50
+#define PRP_CH1_RZ_HORI_COEF1 0x54
+#define PRP_CH1_RZ_HORI_COEF2 0x58
+#define PRP_CH1_RZ_HORI_VALID 0x5c
+#define PRP_CH1_RZ_VERT_COEF1 0x60
+#define PRP_CH1_RZ_VERT_COEF2 0x64
+#define PRP_CH1_RZ_VERT_VALID 0x68
+#define PRP_CH2_RZ_HORI_COEF1 0x6c
+#define PRP_CH2_RZ_HORI_COEF2 0x70
+#define PRP_CH2_RZ_HORI_VALID 0x74
+#define PRP_CH2_RZ_VERT_COEF1 0x78
+#define PRP_CH2_RZ_VERT_COEF2 0x7c
+#define PRP_CH2_RZ_VERT_VALID 0x80
+
+#define PRP_CNTL_CH1EN (1 << 0)
+#define PRP_CNTL_CH2EN (1 << 1)
+#define PRP_CNTL_CSIEN (1 << 2)
+#define PRP_CNTL_DATA_IN_YUV420 (0 << 3)
+#define PRP_CNTL_DATA_IN_YUV422 (1 << 3)
+#define PRP_CNTL_DATA_IN_RGB16 (2 << 3)
+#define PRP_CNTL_DATA_IN_RGB32 (3 << 3)
+#define PRP_CNTL_CH1_OUT_RGB8 (0 << 5)
+#define PRP_CNTL_CH1_OUT_RGB16 (1 << 5)
+#define PRP_CNTL_CH1_OUT_RGB32 (2 << 5)
+#define PRP_CNTL_CH1_OUT_YUV422 (3 << 5)
+#define PRP_CNTL_CH2_OUT_YUV420 (0 << 7)
+#define PRP_CNTL_CH2_OUT_YUV422 (1 << 7)
+#define PRP_CNTL_CH2_OUT_YUV444 (2 << 7)
+#define PRP_CNTL_CH1_LEN (1 << 9)
+#define PRP_CNTL_CH2_LEN (1 << 10)
+#define PRP_CNTL_SKIP_FRAME (1 << 11)
+#define PRP_CNTL_SWRST (1 << 12)
+#define PRP_CNTL_CLKEN (1 << 13)
+#define PRP_CNTL_WEN (1 << 14)
+#define PRP_CNTL_CH1BYP (1 << 15)
+#define PRP_CNTL_IN_TSKIP(x) ((x) << 16)
+#define PRP_CNTL_CH1_TSKIP(x) ((x) << 19)
+#define PRP_CNTL_CH2_TSKIP(x) ((x) << 22)
+#define PRP_CNTL_INPUT_FIFO_LEVEL(x) ((x) << 25)
+#define PRP_CNTL_RZ_FIFO_LEVEL(x) ((x) << 27)
+#define PRP_CNTL_CH2B1EN (1 << 29)
+#define PRP_CNTL_CH2B2EN (1 << 30)
+#define PRP_CNTL_CH2FEN (1 << 31)
+
+/* IRQ Enable and status register */
+#define PRP_INTR_RDERR (1 << 0)
+#define PRP_INTR_CH1WERR (1 << 1)
+#define PRP_INTR_CH2WERR (1 << 2)
+#define PRP_INTR_CH1FC (1 << 3)
+#define PRP_INTR_CH2FC (1 << 5)
+#define PRP_INTR_LBOVF (1 << 7)
+#define PRP_INTR_CH2OVF (1 << 8)
+
+/* Resizing registers */
+#define PRP_RZ_VALID_TBL_LEN(x) ((x) << 24)
+#define PRP_RZ_VALID_BILINEAR (1 << 31)
+
+#define MAX_VIDEO_MEM 16
+
+#define RESIZE_NUM_MIN 1
+#define RESIZE_NUM_MAX 20
+#define BC_COEF 3
+#define SZ_COEF (1 << BC_COEF)
+
+#define RESIZE_DIR_H 0
+#define RESIZE_DIR_V 1
+
+#define RESIZE_ALGO_BILINEAR 0
+#define RESIZE_ALGO_AVERAGING 1
+
+struct mx2_prp_cfg {
+ int channel;
+ u32 in_fmt;
+ u32 out_fmt;
+ u32 src_pixel;
+ u32 ch1_pixel;
+ u32 irq_flags;
+ u32 csicr1;
+};
+
+/* prp resizing parameters */
+struct emma_prp_resize {
+ int algo; /* type of algorithm used */
+ int len; /* number of coefficients */
+ unsigned char s[RESIZE_NUM_MAX]; /* table of coefficients */
+};
+
+/* prp configuration for a client-host fmt pair */
+struct mx2_fmt_cfg {
+ u32 in_fmt;
+ u32 out_fmt;
+ struct mx2_prp_cfg cfg;
+};
+
+struct mx2_buf_internal {
+ struct list_head queue;
+ int bufnum;
+ bool discard;
+};
+
+/* buffer for one video frame */
+struct mx2_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct vb2_v4l2_buffer vb;
+ struct mx2_buf_internal internal;
+};
+
+enum mx2_camera_type {
+ IMX27_CAMERA,
+};
+
+struct mx2_camera_dev {
+ struct device *dev;
+ struct soc_camera_host soc_host;
+ struct clk *clk_emma_ahb, *clk_emma_ipg;
+ struct clk *clk_csi_ahb, *clk_csi_per;
+
+ void __iomem *base_csi, *base_emma;
+
+ struct mx2_camera_platform_data *pdata;
+ unsigned long platform_flags;
+
+ struct list_head capture;
+ struct list_head active_bufs;
+ struct list_head discard;
+
+ spinlock_t lock;
+
+ int dma;
+ struct mx2_buffer *active;
+ struct mx2_buffer *fb1_active;
+ struct mx2_buffer *fb2_active;
+
+ u32 csicr1;
+ enum mx2_camera_type devtype;
+
+ struct mx2_buf_internal buf_discard[2];
+ void *discard_buffer;
+ dma_addr_t discard_buffer_dma;
+ size_t discard_size;
+ struct mx2_fmt_cfg *emma_prp;
+ struct emma_prp_resize resizing[2];
+ unsigned int s_width, s_height;
+ u32 frame_count;
+ struct vb2_alloc_ctx *alloc_ctx;
+};
+
+static struct platform_device_id mx2_camera_devtype[] = {
+ {
+ .name = "imx27-camera",
+ .driver_data = IMX27_CAMERA,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(platform, mx2_camera_devtype);
+
+static struct mx2_buffer *mx2_ibuf_to_buf(struct mx2_buf_internal *int_buf)
+{
+ return container_of(int_buf, struct mx2_buffer, internal);
+}
+
+static struct mx2_fmt_cfg mx27_emma_prp_table[] = {
+ /*
+ * This is a generic configuration which is valid for most
+ * prp input-output format combinations.
+ * We set the incoming and outgoing pixelformat to a
+ * 16 Bit wide format and adjust the bytesperline
+ * accordingly. With this configuration the inputdata
+ * will not be changed by the emma and could be any type
+ * of 16 Bit Pixelformat.
+ */
+ {
+ .in_fmt = 0,
+ .out_fmt = 0,
+ .cfg = {
+ .channel = 1,
+ .in_fmt = PRP_CNTL_DATA_IN_RGB16,
+ .out_fmt = PRP_CNTL_CH1_OUT_RGB16,
+ .src_pixel = 0x2ca00565, /* RGB565 */
+ .ch1_pixel = 0x2ca00565, /* RGB565 */
+ .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH1WERR |
+ PRP_INTR_CH1FC | PRP_INTR_LBOVF,
+ .csicr1 = 0,
+ }
+ },
+ {
+ .in_fmt = MEDIA_BUS_FMT_UYVY8_2X8,
+ .out_fmt = V4L2_PIX_FMT_YUYV,
+ .cfg = {
+ .channel = 1,
+ .in_fmt = PRP_CNTL_DATA_IN_YUV422,
+ .out_fmt = PRP_CNTL_CH1_OUT_YUV422,
+ .src_pixel = 0x22000888, /* YUV422 (YUYV) */
+ .ch1_pixel = 0x62000888, /* YUV422 (YUYV) */
+ .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH1WERR |
+ PRP_INTR_CH1FC | PRP_INTR_LBOVF,
+ .csicr1 = CSICR1_SWAP16_EN,
+ }
+ },
+ {
+ .in_fmt = MEDIA_BUS_FMT_YUYV8_2X8,
+ .out_fmt = V4L2_PIX_FMT_YUYV,
+ .cfg = {
+ .channel = 1,
+ .in_fmt = PRP_CNTL_DATA_IN_YUV422,
+ .out_fmt = PRP_CNTL_CH1_OUT_YUV422,
+ .src_pixel = 0x22000888, /* YUV422 (YUYV) */
+ .ch1_pixel = 0x62000888, /* YUV422 (YUYV) */
+ .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH1WERR |
+ PRP_INTR_CH1FC | PRP_INTR_LBOVF,
+ .csicr1 = CSICR1_PACK_DIR,
+ }
+ },
+ {
+ .in_fmt = MEDIA_BUS_FMT_YUYV8_2X8,
+ .out_fmt = V4L2_PIX_FMT_YUV420,
+ .cfg = {
+ .channel = 2,
+ .in_fmt = PRP_CNTL_DATA_IN_YUV422,
+ .out_fmt = PRP_CNTL_CH2_OUT_YUV420,
+ .src_pixel = 0x22000888, /* YUV422 (YUYV) */
+ .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH2WERR |
+ PRP_INTR_CH2FC | PRP_INTR_LBOVF |
+ PRP_INTR_CH2OVF,
+ .csicr1 = CSICR1_PACK_DIR,
+ }
+ },
+ {
+ .in_fmt = MEDIA_BUS_FMT_UYVY8_2X8,
+ .out_fmt = V4L2_PIX_FMT_YUV420,
+ .cfg = {
+ .channel = 2,
+ .in_fmt = PRP_CNTL_DATA_IN_YUV422,
+ .out_fmt = PRP_CNTL_CH2_OUT_YUV420,
+ .src_pixel = 0x22000888, /* YUV422 (YUYV) */
+ .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH2WERR |
+ PRP_INTR_CH2FC | PRP_INTR_LBOVF |
+ PRP_INTR_CH2OVF,
+ .csicr1 = CSICR1_SWAP16_EN,
+ }
+ },
+};
+
+static struct mx2_fmt_cfg *mx27_emma_prp_get_format(u32 in_fmt, u32 out_fmt)
+{
+ int i;
+
+ for (i = 1; i < ARRAY_SIZE(mx27_emma_prp_table); i++)
+ if ((mx27_emma_prp_table[i].in_fmt == in_fmt) &&
+ (mx27_emma_prp_table[i].out_fmt == out_fmt)) {
+ return &mx27_emma_prp_table[i];
+ }
+ /* If no match return the most generic configuration */
+ return &mx27_emma_prp_table[0];
+};
+
+static void mx27_update_emma_buf(struct mx2_camera_dev *pcdev,
+ unsigned long phys, int bufnum)
+{
+ struct mx2_fmt_cfg *prp = pcdev->emma_prp;
+
+ if (prp->cfg.channel == 1) {
+ writel(phys, pcdev->base_emma +
+ PRP_DEST_RGB1_PTR + 4 * bufnum);
+ } else {
+ writel(phys, pcdev->base_emma +
+ PRP_DEST_Y_PTR - 0x14 * bufnum);
+ if (prp->out_fmt == V4L2_PIX_FMT_YUV420) {
+ u32 imgsize = pcdev->soc_host.icd->user_height *
+ pcdev->soc_host.icd->user_width;
+
+ writel(phys + imgsize, pcdev->base_emma +
+ PRP_DEST_CB_PTR - 0x14 * bufnum);
+ writel(phys + ((5 * imgsize) / 4), pcdev->base_emma +
+ PRP_DEST_CR_PTR - 0x14 * bufnum);
+ }
+ }
+}
+
+static void mx2_camera_deactivate(struct mx2_camera_dev *pcdev)
+{
+ clk_disable_unprepare(pcdev->clk_csi_ahb);
+ clk_disable_unprepare(pcdev->clk_csi_per);
+ writel(0, pcdev->base_csi + CSICR1);
+ writel(0, pcdev->base_emma + PRP_CNTL);
+}
+
+static int mx2_camera_add_device(struct soc_camera_device *icd)
+{
+ dev_info(icd->parent, "Camera driver attached to camera %d\n",
+ icd->devnum);
+
+ return 0;
+}
+
+static void mx2_camera_remove_device(struct soc_camera_device *icd)
+{
+ dev_info(icd->parent, "Camera driver detached from camera %d\n",
+ icd->devnum);
+}
+
+/*
+ * The following two functions absolutely depend on the fact, that
+ * there can be only one camera on mx2 camera sensor interface
+ */
+static int mx2_camera_clock_start(struct soc_camera_host *ici)
+{
+ struct mx2_camera_dev *pcdev = ici->priv;
+ int ret;
+ u32 csicr1;
+
+ ret = clk_prepare_enable(pcdev->clk_csi_ahb);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_prepare_enable(pcdev->clk_csi_per);
+ if (ret < 0)
+ goto exit_csi_ahb;
+
+ csicr1 = CSICR1_MCLKEN | CSICR1_PRP_IF_EN | CSICR1_FCC |
+ CSICR1_RXFF_LEVEL(0);
+
+ pcdev->csicr1 = csicr1;
+ writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
+
+ pcdev->frame_count = 0;
+
+ return 0;
+
+exit_csi_ahb:
+ clk_disable_unprepare(pcdev->clk_csi_ahb);
+
+ return ret;
+}
+
+static void mx2_camera_clock_stop(struct soc_camera_host *ici)
+{
+ struct mx2_camera_dev *pcdev = ici->priv;
+
+ mx2_camera_deactivate(pcdev);
+}
+
+/*
+ * Videobuf operations
+ */
+static int mx2_videobuf_setup(struct vb2_queue *vq,
+ unsigned int *count, unsigned int *num_planes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+
+ dev_dbg(icd->parent, "count=%d, size=%d\n", *count, sizes[0]);
+
+ alloc_ctxs[0] = pcdev->alloc_ctx;
+
+ sizes[0] = icd->sizeimage;
+
+ if (0 == *count)
+ *count = 32;
+ if (!*num_planes &&
+ sizes[0] * *count > MAX_VIDEO_MEM * 1024 * 1024)
+ *count = (MAX_VIDEO_MEM * 1024 * 1024) / sizes[0];
+
+ *num_planes = 1;
+
+ return 0;
+}
+
+static int mx2_videobuf_prepare(struct vb2_buffer *vb)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+ int ret = 0;
+
+ dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+ vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
+
+#ifdef DEBUG
+ /*
+ * This can be useful if you want to see if we actually fill
+ * the buffer with something
+ */
+ memset((void *)vb2_plane_vaddr(vb, 0),
+ 0xaa, vb2_get_plane_payload(vb, 0));
+#endif
+
+ vb2_set_plane_payload(vb, 0, icd->sizeimage);
+ if (vb2_plane_vaddr(vb, 0) &&
+ vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ return 0;
+
+out:
+ return ret;
+}
+
+static void mx2_videobuf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+ struct soc_camera_host *ici =
+ to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+ struct mx2_buffer *buf = container_of(vbuf, struct mx2_buffer, vb);
+ unsigned long flags;
+
+ dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+ vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
+
+ spin_lock_irqsave(&pcdev->lock, flags);
+
+ list_add_tail(&buf->internal.queue, &pcdev->capture);
+
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+}
+
+static void mx27_camera_emma_buf_init(struct soc_camera_device *icd,
+ int bytesperline)
+{
+ struct soc_camera_host *ici =
+ to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+ struct mx2_fmt_cfg *prp = pcdev->emma_prp;
+
+ writel((pcdev->s_width << 16) | pcdev->s_height,
+ pcdev->base_emma + PRP_SRC_FRAME_SIZE);
+ writel(prp->cfg.src_pixel,
+ pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL);
+ if (prp->cfg.channel == 1) {
+ writel((icd->user_width << 16) | icd->user_height,
+ pcdev->base_emma + PRP_CH1_OUT_IMAGE_SIZE);
+ writel(bytesperline,
+ pcdev->base_emma + PRP_DEST_CH1_LINE_STRIDE);
+ writel(prp->cfg.ch1_pixel,
+ pcdev->base_emma + PRP_CH1_PIXEL_FORMAT_CNTL);
+ } else { /* channel 2 */
+ writel((icd->user_width << 16) | icd->user_height,
+ pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE);
+ }
+
+ /* Enable interrupts */
+ writel(prp->cfg.irq_flags, pcdev->base_emma + PRP_INTR_CNTL);
+}
+
+static void mx2_prp_resize_commit(struct mx2_camera_dev *pcdev)
+{
+ int dir;
+
+ for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) {
+ unsigned char *s = pcdev->resizing[dir].s;
+ int len = pcdev->resizing[dir].len;
+ unsigned int coeff[2] = {0, 0};
+ unsigned int valid = 0;
+ int i;
+
+ if (len == 0)
+ continue;
+
+ for (i = RESIZE_NUM_MAX - 1; i >= 0; i--) {
+ int j;
+
+ j = i > 9 ? 1 : 0;
+ coeff[j] = (coeff[j] << BC_COEF) |
+ (s[i] & (SZ_COEF - 1));
+
+ if (i == 5 || i == 15)
+ coeff[j] <<= 1;
+
+ valid = (valid << 1) | (s[i] >> BC_COEF);
+ }
+
+ valid |= PRP_RZ_VALID_TBL_LEN(len);
+
+ if (pcdev->resizing[dir].algo == RESIZE_ALGO_BILINEAR)
+ valid |= PRP_RZ_VALID_BILINEAR;
+
+ if (pcdev->emma_prp->cfg.channel == 1) {
+ if (dir == RESIZE_DIR_H) {
+ writel(coeff[0], pcdev->base_emma +
+ PRP_CH1_RZ_HORI_COEF1);
+ writel(coeff[1], pcdev->base_emma +
+ PRP_CH1_RZ_HORI_COEF2);
+ writel(valid, pcdev->base_emma +
+ PRP_CH1_RZ_HORI_VALID);
+ } else {
+ writel(coeff[0], pcdev->base_emma +
+ PRP_CH1_RZ_VERT_COEF1);
+ writel(coeff[1], pcdev->base_emma +
+ PRP_CH1_RZ_VERT_COEF2);
+ writel(valid, pcdev->base_emma +
+ PRP_CH1_RZ_VERT_VALID);
+ }
+ } else {
+ if (dir == RESIZE_DIR_H) {
+ writel(coeff[0], pcdev->base_emma +
+ PRP_CH2_RZ_HORI_COEF1);
+ writel(coeff[1], pcdev->base_emma +
+ PRP_CH2_RZ_HORI_COEF2);
+ writel(valid, pcdev->base_emma +
+ PRP_CH2_RZ_HORI_VALID);
+ } else {
+ writel(coeff[0], pcdev->base_emma +
+ PRP_CH2_RZ_VERT_COEF1);
+ writel(coeff[1], pcdev->base_emma +
+ PRP_CH2_RZ_VERT_COEF2);
+ writel(valid, pcdev->base_emma +
+ PRP_CH2_RZ_VERT_VALID);
+ }
+ }
+ }
+}
+
+static int mx2_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(q);
+ struct soc_camera_host *ici =
+ to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+ struct mx2_fmt_cfg *prp = pcdev->emma_prp;
+ struct vb2_buffer *vb;
+ struct mx2_buffer *buf;
+ unsigned long phys;
+ int bytesperline;
+ unsigned long flags;
+
+ if (count < 2)
+ return -ENOBUFS;
+
+ spin_lock_irqsave(&pcdev->lock, flags);
+
+ buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
+ internal.queue);
+ buf->internal.bufnum = 0;
+ vb = &buf->vb.vb2_buf;
+
+ phys = vb2_dma_contig_plane_dma_addr(vb, 0);
+ mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum);
+ list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
+
+ buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
+ internal.queue);
+ buf->internal.bufnum = 1;
+ vb = &buf->vb.vb2_buf;
+
+ phys = vb2_dma_contig_plane_dma_addr(vb, 0);
+ mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum);
+ list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
+
+ bytesperline = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+ if (bytesperline < 0) {
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+ return bytesperline;
+ }
+
+ /*
+ * I didn't manage to properly enable/disable the prp
+ * on a per frame basis during running transfers,
+ * thus we allocate a buffer here and use it to
+ * discard frames when no buffer is available.
+ * Feel free to work on this ;)
+ */
+ pcdev->discard_size = icd->user_height * bytesperline;
+ pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev,
+ pcdev->discard_size,
+ &pcdev->discard_buffer_dma, GFP_ATOMIC);
+ if (!pcdev->discard_buffer) {
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+ return -ENOMEM;
+ }
+
+ pcdev->buf_discard[0].discard = true;
+ list_add_tail(&pcdev->buf_discard[0].queue,
+ &pcdev->discard);
+
+ pcdev->buf_discard[1].discard = true;
+ list_add_tail(&pcdev->buf_discard[1].queue,
+ &pcdev->discard);
+
+ mx2_prp_resize_commit(pcdev);
+
+ mx27_camera_emma_buf_init(icd, bytesperline);
+
+ if (prp->cfg.channel == 1) {
+ writel(PRP_CNTL_CH1EN |
+ PRP_CNTL_CSIEN |
+ prp->cfg.in_fmt |
+ prp->cfg.out_fmt |
+ PRP_CNTL_CH1_LEN |
+ PRP_CNTL_CH1BYP |
+ PRP_CNTL_CH1_TSKIP(0) |
+ PRP_CNTL_IN_TSKIP(0),
+ pcdev->base_emma + PRP_CNTL);
+ } else {
+ writel(PRP_CNTL_CH2EN |
+ PRP_CNTL_CSIEN |
+ prp->cfg.in_fmt |
+ prp->cfg.out_fmt |
+ PRP_CNTL_CH2_LEN |
+ PRP_CNTL_CH2_TSKIP(0) |
+ PRP_CNTL_IN_TSKIP(0),
+ pcdev->base_emma + PRP_CNTL);
+ }
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+
+ return 0;
+}
+
+static void mx2_stop_streaming(struct vb2_queue *q)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(q);
+ struct soc_camera_host *ici =
+ to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+ struct mx2_fmt_cfg *prp = pcdev->emma_prp;
+ unsigned long flags;
+ void *b;
+ u32 cntl;
+
+ spin_lock_irqsave(&pcdev->lock, flags);
+
+ cntl = readl(pcdev->base_emma + PRP_CNTL);
+ if (prp->cfg.channel == 1) {
+ writel(cntl & ~PRP_CNTL_CH1EN,
+ pcdev->base_emma + PRP_CNTL);
+ } else {
+ writel(cntl & ~PRP_CNTL_CH2EN,
+ pcdev->base_emma + PRP_CNTL);
+ }
+ INIT_LIST_HEAD(&pcdev->capture);
+ INIT_LIST_HEAD(&pcdev->active_bufs);
+ INIT_LIST_HEAD(&pcdev->discard);
+
+ b = pcdev->discard_buffer;
+ pcdev->discard_buffer = NULL;
+
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+
+ dma_free_coherent(ici->v4l2_dev.dev,
+ pcdev->discard_size, b, pcdev->discard_buffer_dma);
+}
+
+static struct vb2_ops mx2_videobuf_ops = {
+ .queue_setup = mx2_videobuf_setup,
+ .buf_prepare = mx2_videobuf_prepare,
+ .buf_queue = mx2_videobuf_queue,
+ .start_streaming = mx2_start_streaming,
+ .stop_streaming = mx2_stop_streaming,
+};
+
+static int mx2_camera_init_videobuf(struct vb2_queue *q,
+ struct soc_camera_device *icd)
+{
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->drv_priv = icd;
+ q->ops = &mx2_videobuf_ops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct mx2_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+
+ return vb2_queue_init(q);
+}
+
+#define MX2_BUS_FLAGS (V4L2_MBUS_MASTER | \
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_VSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_HSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_PCLK_SAMPLE_RISING | \
+ V4L2_MBUS_PCLK_SAMPLE_FALLING | \
+ V4L2_MBUS_DATA_ACTIVE_HIGH | \
+ V4L2_MBUS_DATA_ACTIVE_LOW)
+
+static int mx27_camera_emma_prp_reset(struct mx2_camera_dev *pcdev)
+{
+ int count = 0;
+
+ readl(pcdev->base_emma + PRP_CNTL);
+ writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL);
+ while (count++ < 100) {
+ if (!(readl(pcdev->base_emma + PRP_CNTL) & PRP_CNTL_SWRST))
+ return 0;
+ barrier();
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int mx2_camera_set_bus_param(struct soc_camera_device *icd)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long common_flags;
+ int ret;
+ int bytesperline;
+ u32 csicr1 = pcdev->csicr1;
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg, MX2_BUS_FLAGS);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "Flags incompatible: camera 0x%x, host 0x%x\n",
+ cfg.flags, MX2_BUS_FLAGS);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ } else {
+ common_flags = MX2_BUS_FLAGS;
+ }
+
+ if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
+ if (pcdev->platform_flags & MX2_CAMERA_HSYNC_HIGH)
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
+ else
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
+ }
+
+ if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+ (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
+ if (pcdev->platform_flags & MX2_CAMERA_PCLK_SAMPLE_RISING)
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
+ else
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
+ }
+
+ cfg.flags = common_flags;
+ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n",
+ common_flags, ret);
+ return ret;
+ }
+
+ csicr1 = (csicr1 & ~CSICR1_FMT_MASK) | pcdev->emma_prp->cfg.csicr1;
+
+ if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
+ csicr1 |= CSICR1_REDGE;
+ if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
+ csicr1 |= CSICR1_SOF_POL;
+ if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
+ csicr1 |= CSICR1_HSYNC_POL;
+ if (pcdev->platform_flags & MX2_CAMERA_EXT_VSYNC)
+ csicr1 |= CSICR1_EXT_VSYNC;
+ if (pcdev->platform_flags & MX2_CAMERA_CCIR)
+ csicr1 |= CSICR1_CCIR_EN;
+ if (pcdev->platform_flags & MX2_CAMERA_CCIR_INTERLACE)
+ csicr1 |= CSICR1_CCIR_MODE;
+ if (pcdev->platform_flags & MX2_CAMERA_GATED_CLOCK)
+ csicr1 |= CSICR1_GCLK_MODE;
+ if (pcdev->platform_flags & MX2_CAMERA_INV_DATA)
+ csicr1 |= CSICR1_INV_DATA;
+
+ pcdev->csicr1 = csicr1;
+
+ bytesperline = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+ if (bytesperline < 0)
+ return bytesperline;
+
+ ret = mx27_camera_emma_prp_reset(pcdev);
+ if (ret)
+ return ret;
+
+ writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
+
+ return 0;
+}
+
+static int mx2_camera_set_crop(struct soc_camera_device *icd,
+ const struct v4l2_crop *a)
+{
+ struct v4l2_crop a_writable = *a;
+ struct v4l2_rect *rect = &a_writable.c;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct v4l2_subdev_format fmt = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ struct v4l2_mbus_framefmt *mf = &fmt.format;
+ int ret;
+
+ soc_camera_limit_side(&rect->left, &rect->width, 0, 2, 4096);
+ soc_camera_limit_side(&rect->top, &rect->height, 0, 2, 4096);
+
+ ret = v4l2_subdev_call(sd, video, s_crop, a);
+ if (ret < 0)
+ return ret;
+
+ /* The capture device might have changed its output */
+ ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(icd->parent, "Sensor cropped %dx%d\n",
+ mf->width, mf->height);
+
+ icd->user_width = mf->width;
+ icd->user_height = mf->height;
+
+ return ret;
+}
+
+static int mx2_camera_get_formats(struct soc_camera_device *icd,
+ unsigned int idx,
+ struct soc_camera_format_xlate *xlate)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_mbus_pixelfmt *fmt;
+ struct device *dev = icd->parent;
+ struct v4l2_subdev_mbus_code_enum code = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ .index = idx,
+ };
+ int ret, formats = 0;
+
+ ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code);
+ if (ret < 0)
+ /* no more formats */
+ return 0;
+
+ fmt = soc_mbus_get_fmtdesc(code.code);
+ if (!fmt) {
+ dev_err(dev, "Invalid format code #%u: %d\n", idx, code.code);
+ return 0;
+ }
+
+ if (code.code == MEDIA_BUS_FMT_YUYV8_2X8 ||
+ code.code == MEDIA_BUS_FMT_UYVY8_2X8) {
+ formats++;
+ if (xlate) {
+ /*
+ * CH2 can output YUV420 which is a standard format in
+ * soc_mediabus.c
+ */
+ xlate->host_fmt =
+ soc_mbus_get_fmtdesc(MEDIA_BUS_FMT_YUYV8_1_5X8);
+ xlate->code = code.code;
+ dev_dbg(dev, "Providing host format %s for sensor code %d\n",
+ xlate->host_fmt->name, code.code);
+ xlate++;
+ }
+ }
+
+ if (code.code == MEDIA_BUS_FMT_UYVY8_2X8) {
+ formats++;
+ if (xlate) {
+ xlate->host_fmt =
+ soc_mbus_get_fmtdesc(MEDIA_BUS_FMT_YUYV8_2X8);
+ xlate->code = code.code;
+ dev_dbg(dev, "Providing host format %s for sensor code %d\n",
+ xlate->host_fmt->name, code.code);
+ xlate++;
+ }
+ }
+
+ /* Generic pass-trough */
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = fmt;
+ xlate->code = code.code;
+ xlate++;
+ }
+ return formats;
+}
+
+static int mx2_emmaprp_resize(struct mx2_camera_dev *pcdev,
+ struct v4l2_mbus_framefmt *mf_in,
+ struct v4l2_pix_format *pix_out, bool apply)
+{
+ unsigned int num, den;
+ unsigned long m;
+ int i, dir;
+
+ for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) {
+ struct emma_prp_resize tmprsz;
+ unsigned char *s = tmprsz.s;
+ int len = 0;
+ int in, out;
+
+ if (dir == RESIZE_DIR_H) {
+ in = mf_in->width;
+ out = pix_out->width;
+ } else {
+ in = mf_in->height;
+ out = pix_out->height;
+ }
+
+ if (in < out)
+ return -EINVAL;
+ else if (in == out)
+ continue;
+
+ /* Calculate ratio */
+ m = gcd(in, out);
+ num = in / m;
+ den = out / m;
+ if (num > RESIZE_NUM_MAX)
+ return -EINVAL;
+
+ if ((num >= 2 * den) && (den == 1) &&
+ (num < 9) && (!(num & 0x01))) {
+ int sum = 0;
+ int j;
+
+ /* Average scaling for >= 2:1 ratios */
+ /* Support can be added for num >=9 and odd values */
+
+ tmprsz.algo = RESIZE_ALGO_AVERAGING;
+ len = num;
+
+ for (i = 0; i < (len / 2); i++)
+ s[i] = 8;
+
+ do {
+ for (i = 0; i < (len / 2); i++) {
+ s[i] = s[i] >> 1;
+ sum = 0;
+ for (j = 0; j < (len / 2); j++)
+ sum += s[j];
+ if (sum == 4)
+ break;
+ }
+ } while (sum != 4);
+
+ for (i = (len / 2); i < len; i++)
+ s[i] = s[len - i - 1];
+
+ s[len - 1] |= SZ_COEF;
+ } else {
+ /* bilinear scaling for < 2:1 ratios */
+ int v; /* overflow counter */
+ int coeff, nxt; /* table output */
+ int in_pos_inc = 2 * den;
+ int out_pos = num;
+ int out_pos_inc = 2 * num;
+ int init_carry = num - den;
+ int carry = init_carry;
+
+ tmprsz.algo = RESIZE_ALGO_BILINEAR;
+ v = den + in_pos_inc;
+ do {
+ coeff = v - out_pos;
+ out_pos += out_pos_inc;
+ carry += out_pos_inc;
+ for (nxt = 0; v < out_pos; nxt++) {
+ v += in_pos_inc;
+ carry -= in_pos_inc;
+ }
+
+ if (len > RESIZE_NUM_MAX)
+ return -EINVAL;
+
+ coeff = ((coeff << BC_COEF) +
+ (in_pos_inc >> 1)) / in_pos_inc;
+
+ if (coeff >= (SZ_COEF - 1))
+ coeff--;
+
+ coeff |= SZ_COEF;
+ s[len] = (unsigned char)coeff;
+ len++;
+
+ for (i = 1; i < nxt; i++) {
+ if (len >= RESIZE_NUM_MAX)
+ return -EINVAL;
+ s[len] = 0;
+ len++;
+ }
+ } while (carry != init_carry);
+ }
+ tmprsz.len = len;
+ if (dir == RESIZE_DIR_H)
+ mf_in->width = pix_out->width;
+ else
+ mf_in->height = pix_out->height;
+
+ if (apply)
+ memcpy(&pcdev->resizing[dir], &tmprsz, sizeof(tmprsz));
+ }
+ return 0;
+}
+
+static int mx2_camera_set_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_subdev_format format = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ struct v4l2_mbus_framefmt *mf = &format.format;
+ int ret;
+
+ dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n",
+ __func__, pix->width, pix->height);
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+ if (!xlate) {
+ dev_warn(icd->parent, "Format %x not found\n",
+ pix->pixelformat);
+ return -EINVAL;
+ }
+
+ mf->width = pix->width;
+ mf->height = pix->height;
+ mf->field = pix->field;
+ mf->colorspace = pix->colorspace;
+ mf->code = xlate->code;
+
+ ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &format);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return ret;
+
+ /* Store width and height returned by the sensor for resizing */
+ pcdev->s_width = mf->width;
+ pcdev->s_height = mf->height;
+ dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n",
+ __func__, pcdev->s_width, pcdev->s_height);
+
+ pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code,
+ xlate->host_fmt->fourcc);
+
+ memset(pcdev->resizing, 0, sizeof(pcdev->resizing));
+ if ((mf->width != pix->width || mf->height != pix->height) &&
+ pcdev->emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) {
+ if (mx2_emmaprp_resize(pcdev, mf, pix, true) < 0)
+ dev_dbg(icd->parent, "%s: can't resize\n", __func__);
+ }
+
+ if (mf->code != xlate->code)
+ return -EINVAL;
+
+ pix->width = mf->width;
+ pix->height = mf->height;
+ pix->field = mf->field;
+ pix->colorspace = mf->colorspace;
+ icd->current_fmt = xlate;
+
+ dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n",
+ __func__, pix->width, pix->height);
+
+ return 0;
+}
+
+static int mx2_camera_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_subdev_pad_config pad_cfg;
+ struct v4l2_subdev_format format = {
+ .which = V4L2_SUBDEV_FORMAT_TRY,
+ };
+ struct v4l2_mbus_framefmt *mf = &format.format;
+ __u32 pixfmt = pix->pixelformat;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+ struct mx2_fmt_cfg *emma_prp;
+ int ret;
+
+ dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n",
+ __func__, pix->width, pix->height);
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (pixfmt && !xlate) {
+ dev_warn(icd->parent, "Format %x not found\n", pixfmt);
+ return -EINVAL;
+ }
+
+ /*
+ * limit to MX27 hardware capabilities: width must be a multiple of 8 as
+ * requested by the CSI. (Table 39-2 in the i.MX27 Reference Manual).
+ */
+ pix->width &= ~0x7;
+
+ /* limit to sensor capabilities */
+ mf->width = pix->width;
+ mf->height = pix->height;
+ mf->field = pix->field;
+ mf->colorspace = pix->colorspace;
+ mf->code = xlate->code;
+
+ ret = v4l2_subdev_call(sd, pad, set_fmt, &pad_cfg, &format);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n",
+ __func__, pcdev->s_width, pcdev->s_height);
+
+ /* If the sensor does not support image size try PrP resizing */
+ emma_prp = mx27_emma_prp_get_format(xlate->code,
+ xlate->host_fmt->fourcc);
+
+ if ((mf->width != pix->width || mf->height != pix->height) &&
+ emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) {
+ if (mx2_emmaprp_resize(pcdev, mf, pix, false) < 0)
+ dev_dbg(icd->parent, "%s: can't resize\n", __func__);
+ }
+
+ if (mf->field == V4L2_FIELD_ANY)
+ mf->field = V4L2_FIELD_NONE;
+ /*
+ * Driver supports interlaced images provided they have
+ * both fields so that they can be processed as if they
+ * were progressive.
+ */
+ if (mf->field != V4L2_FIELD_NONE && !V4L2_FIELD_HAS_BOTH(mf->field)) {
+ dev_err(icd->parent, "Field type %d unsupported.\n",
+ mf->field);
+ return -EINVAL;
+ }
+
+ pix->width = mf->width;
+ pix->height = mf->height;
+ pix->field = mf->field;
+ pix->colorspace = mf->colorspace;
+
+ dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n",
+ __func__, pix->width, pix->height);
+
+ return 0;
+}
+
+static int mx2_camera_querycap(struct soc_camera_host *ici,
+ struct v4l2_capability *cap)
+{
+ /* cap->name is set by the friendly caller:-> */
+ strlcpy(cap->card, MX2_CAM_DRIVER_DESCRIPTION, sizeof(cap->card));
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+ return 0;
+}
+
+static unsigned int mx2_camera_poll(struct file *file, poll_table *pt)
+{
+ struct soc_camera_device *icd = file->private_data;
+
+ return vb2_poll(&icd->vb2_vidq, file, pt);
+}
+
+static struct soc_camera_host_ops mx2_soc_camera_host_ops = {
+ .owner = THIS_MODULE,
+ .add = mx2_camera_add_device,
+ .remove = mx2_camera_remove_device,
+ .clock_start = mx2_camera_clock_start,
+ .clock_stop = mx2_camera_clock_stop,
+ .set_fmt = mx2_camera_set_fmt,
+ .set_crop = mx2_camera_set_crop,
+ .get_formats = mx2_camera_get_formats,
+ .try_fmt = mx2_camera_try_fmt,
+ .init_videobuf2 = mx2_camera_init_videobuf,
+ .poll = mx2_camera_poll,
+ .querycap = mx2_camera_querycap,
+ .set_bus_param = mx2_camera_set_bus_param,
+};
+
+static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev,
+ int bufnum, bool err)
+{
+#ifdef DEBUG
+ struct mx2_fmt_cfg *prp = pcdev->emma_prp;
+#endif
+ struct mx2_buf_internal *ibuf;
+ struct mx2_buffer *buf;
+ struct vb2_buffer *vb;
+ struct vb2_v4l2_buffer *vbuf;
+ unsigned long phys;
+
+ ibuf = list_first_entry(&pcdev->active_bufs, struct mx2_buf_internal,
+ queue);
+
+ BUG_ON(ibuf->bufnum != bufnum);
+
+ if (ibuf->discard) {
+ /*
+ * Discard buffer must not be returned to user space.
+ * Just return it to the discard queue.
+ */
+ list_move_tail(pcdev->active_bufs.next, &pcdev->discard);
+ } else {
+ buf = mx2_ibuf_to_buf(ibuf);
+
+ vb = &buf->vb.vb2_buf;
+ vbuf = to_vb2_v4l2_buffer(vb);
+#ifdef DEBUG
+ phys = vb2_dma_contig_plane_dma_addr(vb, 0);
+ if (prp->cfg.channel == 1) {
+ if (readl(pcdev->base_emma + PRP_DEST_RGB1_PTR +
+ 4 * bufnum) != phys) {
+ dev_err(pcdev->dev, "%lx != %x\n", phys,
+ readl(pcdev->base_emma +
+ PRP_DEST_RGB1_PTR + 4 * bufnum));
+ }
+ } else {
+ if (readl(pcdev->base_emma + PRP_DEST_Y_PTR -
+ 0x14 * bufnum) != phys) {
+ dev_err(pcdev->dev, "%lx != %x\n", phys,
+ readl(pcdev->base_emma +
+ PRP_DEST_Y_PTR - 0x14 * bufnum));
+ }
+ }
+#endif
+ dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__, vb,
+ vb2_plane_vaddr(vb, 0),
+ vb2_get_plane_payload(vb, 0));
+
+ list_del_init(&buf->internal.queue);
+ vb->timestamp = ktime_get_ns();
+ vbuf->sequence = pcdev->frame_count;
+ if (err)
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+ else
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ }
+
+ pcdev->frame_count++;
+
+ if (list_empty(&pcdev->capture)) {
+ if (list_empty(&pcdev->discard)) {
+ dev_warn(pcdev->dev, "%s: trying to access empty discard list\n",
+ __func__);
+ return;
+ }
+
+ ibuf = list_first_entry(&pcdev->discard,
+ struct mx2_buf_internal, queue);
+ ibuf->bufnum = bufnum;
+
+ list_move_tail(pcdev->discard.next, &pcdev->active_bufs);
+ mx27_update_emma_buf(pcdev, pcdev->discard_buffer_dma, bufnum);
+ return;
+ }
+
+ buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
+ internal.queue);
+
+ buf->internal.bufnum = bufnum;
+
+ list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
+
+ vb = &buf->vb.vb2_buf;
+
+ phys = vb2_dma_contig_plane_dma_addr(vb, 0);
+ mx27_update_emma_buf(pcdev, phys, bufnum);
+}
+
+static irqreturn_t mx27_camera_emma_irq(int irq_emma, void *data)
+{
+ struct mx2_camera_dev *pcdev = data;
+ unsigned int status = readl(pcdev->base_emma + PRP_INTRSTATUS);
+ struct mx2_buf_internal *ibuf;
+
+ spin_lock(&pcdev->lock);
+
+ if (list_empty(&pcdev->active_bufs)) {
+ dev_warn(pcdev->dev, "%s: called while active list is empty\n",
+ __func__);
+
+ if (!status) {
+ spin_unlock(&pcdev->lock);
+ return IRQ_NONE;
+ }
+ }
+
+ if (status & (1 << 7)) { /* overflow */
+ u32 cntl = readl(pcdev->base_emma + PRP_CNTL);
+ writel(cntl & ~(PRP_CNTL_CH1EN | PRP_CNTL_CH2EN),
+ pcdev->base_emma + PRP_CNTL);
+ writel(cntl, pcdev->base_emma + PRP_CNTL);
+
+ ibuf = list_first_entry(&pcdev->active_bufs,
+ struct mx2_buf_internal, queue);
+ mx27_camera_frame_done_emma(pcdev,
+ ibuf->bufnum, true);
+
+ status &= ~(1 << 7);
+ } else if (((status & (3 << 5)) == (3 << 5)) ||
+ ((status & (3 << 3)) == (3 << 3))) {
+ /*
+ * Both buffers have triggered, process the one we're expecting
+ * to first
+ */
+ ibuf = list_first_entry(&pcdev->active_bufs,
+ struct mx2_buf_internal, queue);
+ mx27_camera_frame_done_emma(pcdev, ibuf->bufnum, false);
+ status &= ~(1 << (6 - ibuf->bufnum)); /* mark processed */
+ } else if ((status & (1 << 6)) || (status & (1 << 4))) {
+ mx27_camera_frame_done_emma(pcdev, 0, false);
+ } else if ((status & (1 << 5)) || (status & (1 << 3))) {
+ mx27_camera_frame_done_emma(pcdev, 1, false);
+ }
+
+ spin_unlock(&pcdev->lock);
+ writel(status, pcdev->base_emma + PRP_INTRSTATUS);
+
+ return IRQ_HANDLED;
+}
+
+static int mx27_camera_emma_init(struct platform_device *pdev)
+{
+ struct mx2_camera_dev *pcdev = platform_get_drvdata(pdev);
+ struct resource *res_emma;
+ int irq_emma;
+ int err = 0;
+
+ res_emma = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ irq_emma = platform_get_irq(pdev, 1);
+ if (!res_emma || !irq_emma) {
+ dev_err(pcdev->dev, "no EMMA resources\n");
+ err = -ENODEV;
+ goto out;
+ }
+
+ pcdev->base_emma = devm_ioremap_resource(pcdev->dev, res_emma);
+ if (IS_ERR(pcdev->base_emma)) {
+ err = PTR_ERR(pcdev->base_emma);
+ goto out;
+ }
+
+ err = devm_request_irq(pcdev->dev, irq_emma, mx27_camera_emma_irq, 0,
+ MX2_CAM_DRV_NAME, pcdev);
+ if (err) {
+ dev_err(pcdev->dev, "Camera EMMA interrupt register failed\n");
+ goto out;
+ }
+
+ pcdev->clk_emma_ipg = devm_clk_get(pcdev->dev, "emma-ipg");
+ if (IS_ERR(pcdev->clk_emma_ipg)) {
+ err = PTR_ERR(pcdev->clk_emma_ipg);
+ goto out;
+ }
+
+ clk_prepare_enable(pcdev->clk_emma_ipg);
+
+ pcdev->clk_emma_ahb = devm_clk_get(pcdev->dev, "emma-ahb");
+ if (IS_ERR(pcdev->clk_emma_ahb)) {
+ err = PTR_ERR(pcdev->clk_emma_ahb);
+ goto exit_clk_emma_ipg;
+ }
+
+ clk_prepare_enable(pcdev->clk_emma_ahb);
+
+ err = mx27_camera_emma_prp_reset(pcdev);
+ if (err)
+ goto exit_clk_emma_ahb;
+
+ return err;
+
+exit_clk_emma_ahb:
+ clk_disable_unprepare(pcdev->clk_emma_ahb);
+exit_clk_emma_ipg:
+ clk_disable_unprepare(pcdev->clk_emma_ipg);
+out:
+ return err;
+}
+
+static int mx2_camera_probe(struct platform_device *pdev)
+{
+ struct mx2_camera_dev *pcdev;
+ struct resource *res_csi;
+ int irq_csi;
+ int err = 0;
+
+ dev_dbg(&pdev->dev, "initialising\n");
+
+ res_csi = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq_csi = platform_get_irq(pdev, 0);
+ if (res_csi == NULL || irq_csi < 0) {
+ dev_err(&pdev->dev, "Missing platform resources data\n");
+ err = -ENODEV;
+ goto exit;
+ }
+
+ pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL);
+ if (!pcdev) {
+ dev_err(&pdev->dev, "Could not allocate pcdev\n");
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ pcdev->clk_csi_ahb = devm_clk_get(&pdev->dev, "ahb");
+ if (IS_ERR(pcdev->clk_csi_ahb)) {
+ dev_err(&pdev->dev, "Could not get csi ahb clock\n");
+ err = PTR_ERR(pcdev->clk_csi_ahb);
+ goto exit;
+ }
+
+ pcdev->clk_csi_per = devm_clk_get(&pdev->dev, "per");
+ if (IS_ERR(pcdev->clk_csi_per)) {
+ dev_err(&pdev->dev, "Could not get csi per clock\n");
+ err = PTR_ERR(pcdev->clk_csi_per);
+ goto exit;
+ }
+
+ pcdev->pdata = pdev->dev.platform_data;
+ if (pcdev->pdata) {
+ long rate;
+
+ pcdev->platform_flags = pcdev->pdata->flags;
+
+ rate = clk_round_rate(pcdev->clk_csi_per,
+ pcdev->pdata->clk * 2);
+ if (rate <= 0) {
+ err = -ENODEV;
+ goto exit;
+ }
+ err = clk_set_rate(pcdev->clk_csi_per, rate);
+ if (err < 0)
+ goto exit;
+ }
+
+ INIT_LIST_HEAD(&pcdev->capture);
+ INIT_LIST_HEAD(&pcdev->active_bufs);
+ INIT_LIST_HEAD(&pcdev->discard);
+ spin_lock_init(&pcdev->lock);
+
+ pcdev->base_csi = devm_ioremap_resource(&pdev->dev, res_csi);
+ if (IS_ERR(pcdev->base_csi)) {
+ err = PTR_ERR(pcdev->base_csi);
+ goto exit;
+ }
+
+ pcdev->dev = &pdev->dev;
+ platform_set_drvdata(pdev, pcdev);
+
+ err = mx27_camera_emma_init(pdev);
+ if (err)
+ goto exit;
+
+ /*
+ * We're done with drvdata here. Clear the pointer so that
+ * v4l2 core can start using drvdata on its purpose.
+ */
+ platform_set_drvdata(pdev, NULL);
+
+ pcdev->soc_host.drv_name = MX2_CAM_DRV_NAME,
+ pcdev->soc_host.ops = &mx2_soc_camera_host_ops,
+ pcdev->soc_host.priv = pcdev;
+ pcdev->soc_host.v4l2_dev.dev = &pdev->dev;
+ pcdev->soc_host.nr = pdev->id;
+
+ pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(pcdev->alloc_ctx)) {
+ err = PTR_ERR(pcdev->alloc_ctx);
+ goto eallocctx;
+ }
+ err = soc_camera_host_register(&pcdev->soc_host);
+ if (err)
+ goto exit_free_emma;
+
+ dev_info(&pdev->dev, "MX2 Camera (CSI) driver probed, clock frequency: %ld\n",
+ clk_get_rate(pcdev->clk_csi_per));
+
+ return 0;
+
+exit_free_emma:
+ vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
+eallocctx:
+ clk_disable_unprepare(pcdev->clk_emma_ipg);
+ clk_disable_unprepare(pcdev->clk_emma_ahb);
+exit:
+ return err;
+}
+
+static int mx2_camera_remove(struct platform_device *pdev)
+{
+ struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
+ struct mx2_camera_dev *pcdev = container_of(soc_host,
+ struct mx2_camera_dev, soc_host);
+
+ soc_camera_host_unregister(&pcdev->soc_host);
+
+ vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
+
+ clk_disable_unprepare(pcdev->clk_emma_ipg);
+ clk_disable_unprepare(pcdev->clk_emma_ahb);
+
+ dev_info(&pdev->dev, "MX2 Camera driver unloaded\n");
+
+ return 0;
+}
+
+static struct platform_driver mx2_camera_driver = {
+ .driver = {
+ .name = MX2_CAM_DRV_NAME,
+ },
+ .id_table = mx2_camera_devtype,
+ .remove = mx2_camera_remove,
+};
+
+module_platform_driver_probe(mx2_camera_driver, mx2_camera_probe);
+
+MODULE_DESCRIPTION("i.MX27 SoC Camera Host driver");
+MODULE_AUTHOR("Sascha Hauer <sha@pengutronix.de>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(MX2_CAM_VERSION);
--- /dev/null
+config VIDEO_MX3
+ tristate "i.MX3x Camera Sensor Interface driver"
+ depends on VIDEO_DEV && MX3_IPU && SOC_CAMERA
+ depends on MX3_IPU || COMPILE_TEST
+ depends on HAS_DMA
+ select VIDEOBUF2_DMA_CONTIG
+ ---help---
+ This is a v4l2 driver for the i.MX3x Camera Sensor Interface
+
+ This driver is deprecated: it should become a stand-alone driver
+ instead of using the soc-camera framework.
+
+ Unless someone is willing to take this on (unlikely with such
+ ancient hardware) it is going to be removed from the kernel
+ soon.
--- /dev/null
+# Makefile for i.MX3x Camera Sensor driver
+
+obj-$(CONFIG_VIDEO_MX3) += mx3_camera.o
--- /dev/null
+This driver is deprecated: it should become a stand-alone driver instead of
+using the soc-camera framework.
+
+Unless someone is willing to take this on (unlikely with such ancient
+hardware) it is going to be removed from the kernel soon.
+
+Note that trivial patches will not be accepted anymore, only a full conversion.
+
+If you want to convert this driver, please contact the linux-media mailinglist
+(see http://linuxtv.org/lists.php).
--- /dev/null
+/*
+ * V4L2 Driver for i.MX3x camera host
+ *
+ * Copyright (C) 2008
+ * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/videodev2.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/vmalloc.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/dma/ipu-dma.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/soc_camera.h>
+#include <media/drv-intf/soc_mediabus.h>
+
+#include <linux/platform_data/media/camera-mx3.h>
+#include <linux/platform_data/dma-imx.h>
+
+#define MX3_CAM_DRV_NAME "mx3-camera"
+
+/* CMOS Sensor Interface Registers */
+#define CSI_REG_START 0x60
+
+#define CSI_SENS_CONF (0x60 - CSI_REG_START)
+#define CSI_SENS_FRM_SIZE (0x64 - CSI_REG_START)
+#define CSI_ACT_FRM_SIZE (0x68 - CSI_REG_START)
+#define CSI_OUT_FRM_CTRL (0x6C - CSI_REG_START)
+#define CSI_TST_CTRL (0x70 - CSI_REG_START)
+#define CSI_CCIR_CODE_1 (0x74 - CSI_REG_START)
+#define CSI_CCIR_CODE_2 (0x78 - CSI_REG_START)
+#define CSI_CCIR_CODE_3 (0x7C - CSI_REG_START)
+#define CSI_FLASH_STROBE_1 (0x80 - CSI_REG_START)
+#define CSI_FLASH_STROBE_2 (0x84 - CSI_REG_START)
+
+#define CSI_SENS_CONF_VSYNC_POL_SHIFT 0
+#define CSI_SENS_CONF_HSYNC_POL_SHIFT 1
+#define CSI_SENS_CONF_DATA_POL_SHIFT 2
+#define CSI_SENS_CONF_PIX_CLK_POL_SHIFT 3
+#define CSI_SENS_CONF_SENS_PRTCL_SHIFT 4
+#define CSI_SENS_CONF_SENS_CLKSRC_SHIFT 7
+#define CSI_SENS_CONF_DATA_FMT_SHIFT 8
+#define CSI_SENS_CONF_DATA_WIDTH_SHIFT 10
+#define CSI_SENS_CONF_EXT_VSYNC_SHIFT 15
+#define CSI_SENS_CONF_DIVRATIO_SHIFT 16
+
+#define CSI_SENS_CONF_DATA_FMT_RGB_YUV444 (0UL << CSI_SENS_CONF_DATA_FMT_SHIFT)
+#define CSI_SENS_CONF_DATA_FMT_YUV422 (2UL << CSI_SENS_CONF_DATA_FMT_SHIFT)
+#define CSI_SENS_CONF_DATA_FMT_BAYER (3UL << CSI_SENS_CONF_DATA_FMT_SHIFT)
+
+#define MAX_VIDEO_MEM 16
+
+struct mx3_camera_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct vb2_v4l2_buffer vb;
+ struct list_head queue;
+
+ /* One descriptot per scatterlist (per frame) */
+ struct dma_async_tx_descriptor *txd;
+
+ /* We have to "build" a scatterlist ourselves - one element per frame */
+ struct scatterlist sg;
+};
+
+/**
+ * struct mx3_camera_dev - i.MX3x camera (CSI) object
+ * @dev: camera device, to which the coherent buffer is attached
+ * @icd: currently attached camera sensor
+ * @clk: pointer to clock
+ * @base: remapped register base address
+ * @pdata: platform data
+ * @platform_flags: platform flags
+ * @mclk: master clock frequency in Hz
+ * @capture: list of capture videobuffers
+ * @lock: protects video buffer lists
+ * @active: active video buffer
+ * @idmac_channel: array of pointers to IPU DMAC DMA channels
+ * @soc_host: embedded soc_host object
+ */
+struct mx3_camera_dev {
+ /*
+ * i.MX3x is only supposed to handle one camera on its Camera Sensor
+ * Interface. If anyone ever builds hardware to enable more than one
+ * camera _simultaneously_, they will have to modify this driver too
+ */
+ struct clk *clk;
+
+ void __iomem *base;
+
+ struct mx3_camera_pdata *pdata;
+
+ unsigned long platform_flags;
+ unsigned long mclk;
+ u16 width_flags; /* max 15 bits */
+
+ struct list_head capture;
+ spinlock_t lock; /* Protects video buffer lists */
+ struct mx3_camera_buffer *active;
+ size_t buf_total;
+ struct vb2_alloc_ctx *alloc_ctx;
+ enum v4l2_field field;
+ int sequence;
+
+ /* IDMAC / dmaengine interface */
+ struct idmac_channel *idmac_channel[1]; /* We need one channel */
+
+ struct soc_camera_host soc_host;
+};
+
+struct dma_chan_request {
+ struct mx3_camera_dev *mx3_cam;
+ enum ipu_channel id;
+};
+
+static u32 csi_reg_read(struct mx3_camera_dev *mx3, off_t reg)
+{
+ return __raw_readl(mx3->base + reg);
+}
+
+static void csi_reg_write(struct mx3_camera_dev *mx3, u32 value, off_t reg)
+{
+ __raw_writel(value, mx3->base + reg);
+}
+
+static struct mx3_camera_buffer *to_mx3_vb(struct vb2_v4l2_buffer *vb)
+{
+ return container_of(vb, struct mx3_camera_buffer, vb);
+}
+
+/* Called from the IPU IDMAC ISR */
+static void mx3_cam_dma_done(void *arg)
+{
+ struct idmac_tx_desc *desc = to_tx_desc(arg);
+ struct dma_chan *chan = desc->txd.chan;
+ struct idmac_channel *ichannel = to_idmac_chan(chan);
+ struct mx3_camera_dev *mx3_cam = ichannel->client;
+
+ dev_dbg(chan->device->dev, "callback cookie %d, active DMA %pad\n",
+ desc->txd.cookie, mx3_cam->active ? &sg_dma_address(&mx3_cam->active->sg) : NULL);
+
+ spin_lock(&mx3_cam->lock);
+ if (mx3_cam->active) {
+ struct vb2_v4l2_buffer *vb = &mx3_cam->active->vb;
+ struct mx3_camera_buffer *buf = to_mx3_vb(vb);
+
+ list_del_init(&buf->queue);
+ vb->vb2_buf.timestamp = ktime_get_ns();
+ vb->field = mx3_cam->field;
+ vb->sequence = mx3_cam->sequence++;
+ vb2_buffer_done(&vb->vb2_buf, VB2_BUF_STATE_DONE);
+ }
+
+ if (list_empty(&mx3_cam->capture)) {
+ mx3_cam->active = NULL;
+ spin_unlock(&mx3_cam->lock);
+
+ /*
+ * stop capture - without further buffers IPU_CHA_BUF0_RDY will
+ * not get updated
+ */
+ return;
+ }
+
+ mx3_cam->active = list_entry(mx3_cam->capture.next,
+ struct mx3_camera_buffer, queue);
+ spin_unlock(&mx3_cam->lock);
+}
+
+/*
+ * Videobuf operations
+ */
+
+/*
+ * Calculate the __buffer__ (not data) size and number of buffers.
+ */
+static int mx3_videobuf_setup(struct vb2_queue *vq,
+ unsigned int *count, unsigned int *num_planes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+
+ if (!mx3_cam->idmac_channel[0])
+ return -EINVAL;
+
+ alloc_ctxs[0] = mx3_cam->alloc_ctx;
+
+ if (!vq->num_buffers)
+ mx3_cam->sequence = 0;
+
+ if (!*count)
+ *count = 2;
+
+ /* Called from VIDIOC_REQBUFS or in compatibility mode */
+ if (!*num_planes)
+ sizes[0] = icd->sizeimage;
+ else if (sizes[0] < icd->sizeimage)
+ return -EINVAL;
+
+ /* If *num_planes != 0, we have already verified *count. */
+ if (sizes[0] * *count + mx3_cam->buf_total > MAX_VIDEO_MEM * 1024 * 1024)
+ *count = (MAX_VIDEO_MEM * 1024 * 1024 - mx3_cam->buf_total) /
+ sizes[0];
+
+ *num_planes = 1;
+
+ return 0;
+}
+
+static enum pixel_fmt fourcc_to_ipu_pix(__u32 fourcc)
+{
+ /* Add more formats as need arises and test possibilities appear... */
+ switch (fourcc) {
+ case V4L2_PIX_FMT_RGB24:
+ return IPU_PIX_FMT_RGB24;
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_RGB565:
+ default:
+ return IPU_PIX_FMT_GENERIC;
+ }
+}
+
+static void mx3_videobuf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct mx3_camera_buffer *buf = to_mx3_vb(vbuf);
+ struct scatterlist *sg = &buf->sg;
+ struct dma_async_tx_descriptor *txd;
+ struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
+ struct idmac_video_param *video = &ichan->params.video;
+ const struct soc_mbus_pixelfmt *host_fmt = icd->current_fmt->host_fmt;
+ dma_cookie_t cookie;
+ size_t new_size;
+
+ new_size = icd->sizeimage;
+
+ if (vb2_plane_size(vb, 0) < new_size) {
+ dev_err(icd->parent, "Buffer #%d too small (%lu < %zu)\n",
+ vbuf->vb2_buf.index, vb2_plane_size(vb, 0), new_size);
+ goto error;
+ }
+
+ if (!buf->txd) {
+ sg_dma_address(sg) = vb2_dma_contig_plane_dma_addr(vb, 0);
+ sg_dma_len(sg) = new_size;
+
+ txd = dmaengine_prep_slave_sg(
+ &ichan->dma_chan, sg, 1, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT);
+ if (!txd)
+ goto error;
+
+ txd->callback_param = txd;
+ txd->callback = mx3_cam_dma_done;
+
+ buf->txd = txd;
+ } else {
+ txd = buf->txd;
+ }
+
+ vb2_set_plane_payload(vb, 0, new_size);
+
+ /* This is the configuration of one sg-element */
+ video->out_pixel_fmt = fourcc_to_ipu_pix(host_fmt->fourcc);
+
+ if (video->out_pixel_fmt == IPU_PIX_FMT_GENERIC) {
+ /*
+ * If the IPU DMA channel is configured to transfer generic
+ * 8-bit data, we have to set up the geometry parameters
+ * correctly, according to the current pixel format. The DMA
+ * horizontal parameters in this case are expressed in bytes,
+ * not in pixels.
+ */
+ video->out_width = icd->bytesperline;
+ video->out_height = icd->user_height;
+ video->out_stride = icd->bytesperline;
+ } else {
+ /*
+ * For IPU known formats the pixel unit will be managed
+ * successfully by the IPU code
+ */
+ video->out_width = icd->user_width;
+ video->out_height = icd->user_height;
+ video->out_stride = icd->user_width;
+ }
+
+#ifdef DEBUG
+ /* helps to see what DMA actually has written */
+ if (vb2_plane_vaddr(vb, 0))
+ memset(vb2_plane_vaddr(vb, 0), 0xaa, vb2_get_plane_payload(vb, 0));
+#endif
+
+ spin_lock_irq(&mx3_cam->lock);
+ list_add_tail(&buf->queue, &mx3_cam->capture);
+
+ if (!mx3_cam->active)
+ mx3_cam->active = buf;
+
+ spin_unlock_irq(&mx3_cam->lock);
+
+ cookie = txd->tx_submit(txd);
+ dev_dbg(icd->parent, "Submitted cookie %d DMA %pad\n",
+ cookie, &sg_dma_address(&buf->sg));
+
+ if (cookie >= 0)
+ return;
+
+ spin_lock_irq(&mx3_cam->lock);
+
+ /* Submit error */
+ list_del_init(&buf->queue);
+
+ if (mx3_cam->active == buf)
+ mx3_cam->active = NULL;
+
+ spin_unlock_irq(&mx3_cam->lock);
+error:
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+}
+
+static void mx3_videobuf_release(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct mx3_camera_buffer *buf = to_mx3_vb(vbuf);
+ struct dma_async_tx_descriptor *txd = buf->txd;
+ unsigned long flags;
+
+ dev_dbg(icd->parent,
+ "Release%s DMA %pad, queue %sempty\n",
+ mx3_cam->active == buf ? " active" : "", &sg_dma_address(&buf->sg),
+ list_empty(&buf->queue) ? "" : "not ");
+
+ spin_lock_irqsave(&mx3_cam->lock, flags);
+
+ if (mx3_cam->active == buf)
+ mx3_cam->active = NULL;
+
+ /* Doesn't hurt also if the list is empty */
+ list_del_init(&buf->queue);
+
+ if (txd) {
+ buf->txd = NULL;
+ if (mx3_cam->idmac_channel[0])
+ async_tx_ack(txd);
+ }
+
+ spin_unlock_irqrestore(&mx3_cam->lock, flags);
+
+ mx3_cam->buf_total -= vb2_plane_size(vb, 0);
+}
+
+static int mx3_videobuf_init(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct mx3_camera_buffer *buf = to_mx3_vb(vbuf);
+
+ if (!buf->txd) {
+ /* This is for locking debugging only */
+ INIT_LIST_HEAD(&buf->queue);
+ sg_init_table(&buf->sg, 1);
+
+ mx3_cam->buf_total += vb2_plane_size(vb, 0);
+ }
+
+ return 0;
+}
+
+static void mx3_stop_streaming(struct vb2_queue *q)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(q);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
+ struct mx3_camera_buffer *buf, *tmp;
+ unsigned long flags;
+
+ if (ichan)
+ dmaengine_pause(&ichan->dma_chan);
+
+ spin_lock_irqsave(&mx3_cam->lock, flags);
+
+ mx3_cam->active = NULL;
+
+ list_for_each_entry_safe(buf, tmp, &mx3_cam->capture, queue) {
+ list_del_init(&buf->queue);
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ }
+
+ spin_unlock_irqrestore(&mx3_cam->lock, flags);
+}
+
+static struct vb2_ops mx3_videobuf_ops = {
+ .queue_setup = mx3_videobuf_setup,
+ .buf_queue = mx3_videobuf_queue,
+ .buf_cleanup = mx3_videobuf_release,
+ .buf_init = mx3_videobuf_init,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .stop_streaming = mx3_stop_streaming,
+};
+
+static int mx3_camera_init_videobuf(struct vb2_queue *q,
+ struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->drv_priv = icd;
+ q->ops = &mx3_videobuf_ops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct mx3_camera_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &ici->host_lock;
+
+ return vb2_queue_init(q);
+}
+
+/* First part of ipu_csi_init_interface() */
+static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam)
+{
+ u32 conf;
+ long rate;
+
+ /* Set default size: ipu_csi_set_window_size() */
+ csi_reg_write(mx3_cam, (640 - 1) | ((480 - 1) << 16), CSI_ACT_FRM_SIZE);
+ /* ...and position to 0:0: ipu_csi_set_window_pos() */
+ conf = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000;
+ csi_reg_write(mx3_cam, conf, CSI_OUT_FRM_CTRL);
+
+ /* We use only gated clock synchronisation mode so far */
+ conf = 0 << CSI_SENS_CONF_SENS_PRTCL_SHIFT;
+
+ /* Set generic data, platform-biggest bus-width */
+ conf |= CSI_SENS_CONF_DATA_FMT_BAYER;
+
+ if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15)
+ conf |= 3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+ else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10)
+ conf |= 2 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+ else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8)
+ conf |= 1 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+ else/* if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)*/
+ conf |= 0 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+
+ if (mx3_cam->platform_flags & MX3_CAMERA_CLK_SRC)
+ conf |= 1 << CSI_SENS_CONF_SENS_CLKSRC_SHIFT;
+ if (mx3_cam->platform_flags & MX3_CAMERA_EXT_VSYNC)
+ conf |= 1 << CSI_SENS_CONF_EXT_VSYNC_SHIFT;
+ if (mx3_cam->platform_flags & MX3_CAMERA_DP)
+ conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT;
+ if (mx3_cam->platform_flags & MX3_CAMERA_PCP)
+ conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT;
+ if (mx3_cam->platform_flags & MX3_CAMERA_HSP)
+ conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT;
+ if (mx3_cam->platform_flags & MX3_CAMERA_VSP)
+ conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT;
+
+ /* ipu_csi_init_interface() */
+ csi_reg_write(mx3_cam, conf, CSI_SENS_CONF);
+
+ clk_prepare_enable(mx3_cam->clk);
+ rate = clk_round_rate(mx3_cam->clk, mx3_cam->mclk);
+ dev_dbg(mx3_cam->soc_host.v4l2_dev.dev, "Set SENS_CONF to %x, rate %ld\n", conf, rate);
+ if (rate)
+ clk_set_rate(mx3_cam->clk, rate);
+}
+
+static int mx3_camera_add_device(struct soc_camera_device *icd)
+{
+ dev_info(icd->parent, "MX3 Camera driver attached to camera %d\n",
+ icd->devnum);
+
+ return 0;
+}
+
+static void mx3_camera_remove_device(struct soc_camera_device *icd)
+{
+ dev_info(icd->parent, "MX3 Camera driver detached from camera %d\n",
+ icd->devnum);
+}
+
+/* Called with .host_lock held */
+static int mx3_camera_clock_start(struct soc_camera_host *ici)
+{
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+
+ mx3_camera_activate(mx3_cam);
+
+ mx3_cam->buf_total = 0;
+
+ return 0;
+}
+
+/* Called with .host_lock held */
+static void mx3_camera_clock_stop(struct soc_camera_host *ici)
+{
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct idmac_channel **ichan = &mx3_cam->idmac_channel[0];
+
+ if (*ichan) {
+ dma_release_channel(&(*ichan)->dma_chan);
+ *ichan = NULL;
+ }
+
+ clk_disable_unprepare(mx3_cam->clk);
+}
+
+static int test_platform_param(struct mx3_camera_dev *mx3_cam,
+ unsigned char buswidth, unsigned long *flags)
+{
+ /*
+ * If requested data width is supported by the platform, use it or any
+ * possible lower value - i.MX31 is smart enough to shift bits
+ */
+ if (buswidth > fls(mx3_cam->width_flags))
+ return -EINVAL;
+
+ /*
+ * Platform specified synchronization and pixel clock polarities are
+ * only a recommendation and are only used during probing. MX3x
+ * camera interface only works in master mode, i.e., uses HSYNC and
+ * VSYNC signals from the sensor
+ */
+ *flags = V4L2_MBUS_MASTER |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_HSYNC_ACTIVE_LOW |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_VSYNC_ACTIVE_LOW |
+ V4L2_MBUS_PCLK_SAMPLE_RISING |
+ V4L2_MBUS_PCLK_SAMPLE_FALLING |
+ V4L2_MBUS_DATA_ACTIVE_HIGH |
+ V4L2_MBUS_DATA_ACTIVE_LOW;
+
+ return 0;
+}
+
+static int mx3_camera_try_bus_param(struct soc_camera_device *icd,
+ const unsigned int depth)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long bus_flags, common_flags;
+ int ret = test_platform_param(mx3_cam, depth, &bus_flags);
+
+ dev_dbg(icd->parent, "request bus width %d bit: %d\n", depth, ret);
+
+ if (ret < 0)
+ return ret;
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg,
+ bus_flags);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "Flags incompatible: camera 0x%x, host 0x%lx\n",
+ cfg.flags, bus_flags);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ }
+
+ return 0;
+}
+
+static bool chan_filter(struct dma_chan *chan, void *arg)
+{
+ struct dma_chan_request *rq = arg;
+ struct mx3_camera_pdata *pdata;
+
+ if (!imx_dma_is_ipu(chan))
+ return false;
+
+ if (!rq)
+ return false;
+
+ pdata = rq->mx3_cam->soc_host.v4l2_dev.dev->platform_data;
+
+ return rq->id == chan->chan_id &&
+ pdata->dma_dev == chan->device->dev;
+}
+
+static const struct soc_mbus_pixelfmt mx3_camera_formats[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ .name = "Bayer BGGR (sRGB) 8 bit",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ }, {
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .name = "Monochrome 8 bit",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+};
+
+/* This will be corrected as we get more formats */
+static bool mx3_camera_packing_supported(const struct soc_mbus_pixelfmt *fmt)
+{
+ return fmt->packing == SOC_MBUS_PACKING_NONE ||
+ (fmt->bits_per_sample == 8 &&
+ fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) ||
+ (fmt->bits_per_sample > 8 &&
+ fmt->packing == SOC_MBUS_PACKING_EXTEND16);
+}
+
+static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int idx,
+ struct soc_camera_format_xlate *xlate)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct device *dev = icd->parent;
+ int formats = 0, ret;
+ struct v4l2_subdev_mbus_code_enum code = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ .index = idx,
+ };
+ const struct soc_mbus_pixelfmt *fmt;
+
+ ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code);
+ if (ret < 0)
+ /* No more formats */
+ return 0;
+
+ fmt = soc_mbus_get_fmtdesc(code.code);
+ if (!fmt) {
+ dev_warn(icd->parent,
+ "Unsupported format code #%u: 0x%x\n", idx, code.code);
+ return 0;
+ }
+
+ /* This also checks support for the requested bits-per-sample */
+ ret = mx3_camera_try_bus_param(icd, fmt->bits_per_sample);
+ if (ret < 0)
+ return 0;
+
+ switch (code.code) {
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = &mx3_camera_formats[0];
+ xlate->code = code.code;
+ xlate++;
+ dev_dbg(dev, "Providing format %s using code 0x%x\n",
+ mx3_camera_formats[0].name, code.code);
+ }
+ break;
+ case MEDIA_BUS_FMT_Y10_1X10:
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = &mx3_camera_formats[1];
+ xlate->code = code.code;
+ xlate++;
+ dev_dbg(dev, "Providing format %s using code 0x%x\n",
+ mx3_camera_formats[1].name, code.code);
+ }
+ break;
+ default:
+ if (!mx3_camera_packing_supported(fmt))
+ return 0;
+ }
+
+ /* Generic pass-through */
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = fmt;
+ xlate->code = code.code;
+ dev_dbg(dev, "Providing format %c%c%c%c in pass-through mode\n",
+ (fmt->fourcc >> (0*8)) & 0xFF,
+ (fmt->fourcc >> (1*8)) & 0xFF,
+ (fmt->fourcc >> (2*8)) & 0xFF,
+ (fmt->fourcc >> (3*8)) & 0xFF);
+ xlate++;
+ }
+
+ return formats;
+}
+
+static void configure_geometry(struct mx3_camera_dev *mx3_cam,
+ unsigned int width, unsigned int height,
+ const struct soc_mbus_pixelfmt *fmt)
+{
+ u32 ctrl, width_field, height_field;
+
+ if (fourcc_to_ipu_pix(fmt->fourcc) == IPU_PIX_FMT_GENERIC) {
+ /*
+ * As the CSI will be configured to output BAYER, here
+ * the width parameter count the number of samples to
+ * capture to complete the whole image width.
+ */
+ unsigned int num, den;
+ int ret = soc_mbus_samples_per_pixel(fmt, &num, &den);
+ BUG_ON(ret < 0);
+ width = width * num / den;
+ }
+
+ /* Setup frame size - this cannot be changed on-the-fly... */
+ width_field = width - 1;
+ height_field = height - 1;
+ csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_SENS_FRM_SIZE);
+
+ csi_reg_write(mx3_cam, width_field << 16, CSI_FLASH_STROBE_1);
+ csi_reg_write(mx3_cam, (height_field << 16) | 0x22, CSI_FLASH_STROBE_2);
+
+ csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_ACT_FRM_SIZE);
+
+ /* ...and position */
+ ctrl = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000;
+ /* Sensor does the cropping */
+ csi_reg_write(mx3_cam, ctrl | 0 | (0 << 8), CSI_OUT_FRM_CTRL);
+}
+
+static int acquire_dma_channel(struct mx3_camera_dev *mx3_cam)
+{
+ dma_cap_mask_t mask;
+ struct dma_chan *chan;
+ struct idmac_channel **ichan = &mx3_cam->idmac_channel[0];
+ /* We have to use IDMAC_IC_7 for Bayer / generic data */
+ struct dma_chan_request rq = {.mx3_cam = mx3_cam,
+ .id = IDMAC_IC_7};
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ dma_cap_set(DMA_PRIVATE, mask);
+ chan = dma_request_channel(mask, chan_filter, &rq);
+ if (!chan)
+ return -EBUSY;
+
+ *ichan = to_idmac_chan(chan);
+ (*ichan)->client = mx3_cam;
+
+ return 0;
+}
+
+/*
+ * FIXME: learn to use stride != width, then we can keep stride properly aligned
+ * and support arbitrary (even) widths.
+ */
+static inline void stride_align(__u32 *width)
+{
+ if (ALIGN(*width, 8) < 4096)
+ *width = ALIGN(*width, 8);
+ else
+ *width = *width & ~7;
+}
+
+/*
+ * As long as we don't implement host-side cropping and scaling, we can use
+ * default g_crop and cropcap from soc_camera.c
+ */
+static int mx3_camera_set_crop(struct soc_camera_device *icd,
+ const struct v4l2_crop *a)
+{
+ struct v4l2_crop a_writable = *a;
+ struct v4l2_rect *rect = &a_writable.c;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct v4l2_subdev_format fmt = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ struct v4l2_mbus_framefmt *mf = &fmt.format;
+ int ret;
+
+ soc_camera_limit_side(&rect->left, &rect->width, 0, 2, 4096);
+ soc_camera_limit_side(&rect->top, &rect->height, 0, 2, 4096);
+
+ ret = v4l2_subdev_call(sd, video, s_crop, a);
+ if (ret < 0)
+ return ret;
+
+ /* The capture device might have changed its output sizes */
+ ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt);
+ if (ret < 0)
+ return ret;
+
+ if (mf->code != icd->current_fmt->code)
+ return -EINVAL;
+
+ if (mf->width & 7) {
+ /* Ouch! We can only handle 8-byte aligned width... */
+ stride_align(&mf->width);
+ ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &fmt);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (mf->width != icd->user_width || mf->height != icd->user_height)
+ configure_geometry(mx3_cam, mf->width, mf->height,
+ icd->current_fmt->host_fmt);
+
+ dev_dbg(icd->parent, "Sensor cropped %dx%d\n",
+ mf->width, mf->height);
+
+ icd->user_width = mf->width;
+ icd->user_height = mf->height;
+
+ return ret;
+}
+
+static int mx3_camera_set_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_subdev_format format = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ struct v4l2_mbus_framefmt *mf = &format.format;
+ int ret;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+ if (!xlate) {
+ dev_warn(icd->parent, "Format %x not found\n",
+ pix->pixelformat);
+ return -EINVAL;
+ }
+
+ stride_align(&pix->width);
+ dev_dbg(icd->parent, "Set format %dx%d\n", pix->width, pix->height);
+
+ /*
+ * Might have to perform a complete interface initialisation like in
+ * ipu_csi_init_interface() in mxc_v4l2_s_param(). Also consider
+ * mxc_v4l2_s_fmt()
+ */
+
+ configure_geometry(mx3_cam, pix->width, pix->height, xlate->host_fmt);
+
+ mf->width = pix->width;
+ mf->height = pix->height;
+ mf->field = pix->field;
+ mf->colorspace = pix->colorspace;
+ mf->code = xlate->code;
+
+ ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &format);
+ if (ret < 0)
+ return ret;
+
+ if (mf->code != xlate->code)
+ return -EINVAL;
+
+ if (!mx3_cam->idmac_channel[0]) {
+ ret = acquire_dma_channel(mx3_cam);
+ if (ret < 0)
+ return ret;
+ }
+
+ pix->width = mf->width;
+ pix->height = mf->height;
+ pix->field = mf->field;
+ mx3_cam->field = mf->field;
+ pix->colorspace = mf->colorspace;
+ icd->current_fmt = xlate;
+
+ dev_dbg(icd->parent, "Sensor set %dx%d\n", pix->width, pix->height);
+
+ return ret;
+}
+
+static int mx3_camera_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_subdev_pad_config pad_cfg;
+ struct v4l2_subdev_format format = {
+ .which = V4L2_SUBDEV_FORMAT_TRY,
+ };
+ struct v4l2_mbus_framefmt *mf = &format.format;
+ __u32 pixfmt = pix->pixelformat;
+ int ret;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (pixfmt && !xlate) {
+ dev_warn(icd->parent, "Format %x not found\n", pixfmt);
+ return -EINVAL;
+ }
+
+ /* limit to MX3 hardware capabilities */
+ if (pix->height > 4096)
+ pix->height = 4096;
+ if (pix->width > 4096)
+ pix->width = 4096;
+
+ /* limit to sensor capabilities */
+ mf->width = pix->width;
+ mf->height = pix->height;
+ mf->field = pix->field;
+ mf->colorspace = pix->colorspace;
+ mf->code = xlate->code;
+
+ ret = v4l2_subdev_call(sd, pad, set_fmt, &pad_cfg, &format);
+ if (ret < 0)
+ return ret;
+
+ pix->width = mf->width;
+ pix->height = mf->height;
+ pix->colorspace = mf->colorspace;
+
+ switch (mf->field) {
+ case V4L2_FIELD_ANY:
+ pix->field = V4L2_FIELD_NONE;
+ break;
+ case V4L2_FIELD_NONE:
+ break;
+ default:
+ dev_err(icd->parent, "Field type %d unsupported.\n",
+ mf->field);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int mx3_camera_reqbufs(struct soc_camera_device *icd,
+ struct v4l2_requestbuffers *p)
+{
+ return 0;
+}
+
+static unsigned int mx3_camera_poll(struct file *file, poll_table *pt)
+{
+ struct soc_camera_device *icd = file->private_data;
+
+ return vb2_poll(&icd->vb2_vidq, file, pt);
+}
+
+static int mx3_camera_querycap(struct soc_camera_host *ici,
+ struct v4l2_capability *cap)
+{
+ /* cap->name is set by the firendly caller:-> */
+ strlcpy(cap->card, "i.MX3x Camera", sizeof(cap->card));
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+ return 0;
+}
+
+static int mx3_camera_set_bus_param(struct soc_camera_device *icd)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ u32 pixfmt = icd->current_fmt->host_fmt->fourcc;
+ unsigned long bus_flags, common_flags;
+ u32 dw, sens_conf;
+ const struct soc_mbus_pixelfmt *fmt;
+ int buswidth;
+ int ret;
+ const struct soc_camera_format_xlate *xlate;
+ struct device *dev = icd->parent;
+
+ fmt = soc_mbus_get_fmtdesc(icd->current_fmt->code);
+ if (!fmt)
+ return -EINVAL;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (!xlate) {
+ dev_warn(dev, "Format %x not found\n", pixfmt);
+ return -EINVAL;
+ }
+
+ buswidth = fmt->bits_per_sample;
+ ret = test_platform_param(mx3_cam, buswidth, &bus_flags);
+
+ dev_dbg(dev, "requested bus width %d bit: %d\n", buswidth, ret);
+
+ if (ret < 0)
+ return ret;
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg,
+ bus_flags);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "Flags incompatible: camera 0x%x, host 0x%lx\n",
+ cfg.flags, bus_flags);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ } else {
+ common_flags = bus_flags;
+ }
+
+ dev_dbg(dev, "Flags cam: 0x%x host: 0x%lx common: 0x%lx\n",
+ cfg.flags, bus_flags, common_flags);
+
+ /* Make choices, based on platform preferences */
+ if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
+ if (mx3_cam->platform_flags & MX3_CAMERA_HSP)
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
+ else
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
+ }
+
+ if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
+ if (mx3_cam->platform_flags & MX3_CAMERA_VSP)
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
+ else
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
+ }
+
+ if ((common_flags & V4L2_MBUS_DATA_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)) {
+ if (mx3_cam->platform_flags & MX3_CAMERA_DP)
+ common_flags &= ~V4L2_MBUS_DATA_ACTIVE_HIGH;
+ else
+ common_flags &= ~V4L2_MBUS_DATA_ACTIVE_LOW;
+ }
+
+ if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+ (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
+ if (mx3_cam->platform_flags & MX3_CAMERA_PCP)
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
+ else
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
+ }
+
+ cfg.flags = common_flags;
+ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_dbg(dev, "camera s_mbus_config(0x%lx) returned %d\n",
+ common_flags, ret);
+ return ret;
+ }
+
+ /*
+ * So far only gated clock mode is supported. Add a line
+ * (3 << CSI_SENS_CONF_SENS_PRTCL_SHIFT) |
+ * below and select the required mode when supporting other
+ * synchronisation protocols.
+ */
+ sens_conf = csi_reg_read(mx3_cam, CSI_SENS_CONF) &
+ ~((1 << CSI_SENS_CONF_VSYNC_POL_SHIFT) |
+ (1 << CSI_SENS_CONF_HSYNC_POL_SHIFT) |
+ (1 << CSI_SENS_CONF_DATA_POL_SHIFT) |
+ (1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT) |
+ (3 << CSI_SENS_CONF_DATA_FMT_SHIFT) |
+ (3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT));
+
+ /* TODO: Support RGB and YUV formats */
+
+ /* This has been set in mx3_camera_activate(), but we clear it above */
+ sens_conf |= CSI_SENS_CONF_DATA_FMT_BAYER;
+
+ if (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
+ sens_conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT;
+ if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ sens_conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT;
+ if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
+ sens_conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT;
+ if (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)
+ sens_conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT;
+
+ /* Just do what we're asked to do */
+ switch (xlate->host_fmt->bits_per_sample) {
+ case 4:
+ dw = 0 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+ break;
+ case 8:
+ dw = 1 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+ break;
+ case 10:
+ dw = 2 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+ break;
+ default:
+ /*
+ * Actually it can only be 15 now, default is just to silence
+ * compiler warnings
+ */
+ case 15:
+ dw = 3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+ }
+
+ csi_reg_write(mx3_cam, sens_conf | dw, CSI_SENS_CONF);
+
+ dev_dbg(dev, "Set SENS_CONF to %x\n", sens_conf | dw);
+
+ return 0;
+}
+
+static struct soc_camera_host_ops mx3_soc_camera_host_ops = {
+ .owner = THIS_MODULE,
+ .add = mx3_camera_add_device,
+ .remove = mx3_camera_remove_device,
+ .clock_start = mx3_camera_clock_start,
+ .clock_stop = mx3_camera_clock_stop,
+ .set_crop = mx3_camera_set_crop,
+ .set_fmt = mx3_camera_set_fmt,
+ .try_fmt = mx3_camera_try_fmt,
+ .get_formats = mx3_camera_get_formats,
+ .init_videobuf2 = mx3_camera_init_videobuf,
+ .reqbufs = mx3_camera_reqbufs,
+ .poll = mx3_camera_poll,
+ .querycap = mx3_camera_querycap,
+ .set_bus_param = mx3_camera_set_bus_param,
+};
+
+static int mx3_camera_probe(struct platform_device *pdev)
+{
+ struct mx3_camera_pdata *pdata = pdev->dev.platform_data;
+ struct mx3_camera_dev *mx3_cam;
+ struct resource *res;
+ void __iomem *base;
+ int err = 0;
+ struct soc_camera_host *soc_host;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ if (!pdata)
+ return -EINVAL;
+
+ mx3_cam = devm_kzalloc(&pdev->dev, sizeof(*mx3_cam), GFP_KERNEL);
+ if (!mx3_cam) {
+ dev_err(&pdev->dev, "Could not allocate mx3 camera object\n");
+ return -ENOMEM;
+ }
+
+ mx3_cam->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(mx3_cam->clk))
+ return PTR_ERR(mx3_cam->clk);
+
+ mx3_cam->pdata = pdata;
+ mx3_cam->platform_flags = pdata->flags;
+ if (!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_MASK)) {
+ /*
+ * Platform hasn't set available data widths. This is bad.
+ * Warn and use a default.
+ */
+ dev_warn(&pdev->dev, "WARNING! Platform hasn't set available "
+ "data widths, using default 8 bit\n");
+ mx3_cam->platform_flags |= MX3_CAMERA_DATAWIDTH_8;
+ }
+ if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)
+ mx3_cam->width_flags = 1 << 3;
+ if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8)
+ mx3_cam->width_flags |= 1 << 7;
+ if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10)
+ mx3_cam->width_flags |= 1 << 9;
+ if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15)
+ mx3_cam->width_flags |= 1 << 14;
+
+ mx3_cam->mclk = pdata->mclk_10khz * 10000;
+ if (!mx3_cam->mclk) {
+ dev_warn(&pdev->dev,
+ "mclk_10khz == 0! Please, fix your platform data. "
+ "Using default 20MHz\n");
+ mx3_cam->mclk = 20000000;
+ }
+
+ /* list of video-buffers */
+ INIT_LIST_HEAD(&mx3_cam->capture);
+ spin_lock_init(&mx3_cam->lock);
+
+ mx3_cam->base = base;
+
+ soc_host = &mx3_cam->soc_host;
+ soc_host->drv_name = MX3_CAM_DRV_NAME;
+ soc_host->ops = &mx3_soc_camera_host_ops;
+ soc_host->priv = mx3_cam;
+ soc_host->v4l2_dev.dev = &pdev->dev;
+ soc_host->nr = pdev->id;
+
+ mx3_cam->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(mx3_cam->alloc_ctx))
+ return PTR_ERR(mx3_cam->alloc_ctx);
+
+ if (pdata->asd_sizes) {
+ soc_host->asd = pdata->asd;
+ soc_host->asd_sizes = pdata->asd_sizes;
+ }
+
+ err = soc_camera_host_register(soc_host);
+ if (err)
+ goto ecamhostreg;
+
+ /* IDMAC interface */
+ dmaengine_get();
+
+ return 0;
+
+ecamhostreg:
+ vb2_dma_contig_cleanup_ctx(mx3_cam->alloc_ctx);
+ return err;
+}
+
+static int mx3_camera_remove(struct platform_device *pdev)
+{
+ struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
+ struct mx3_camera_dev *mx3_cam = container_of(soc_host,
+ struct mx3_camera_dev, soc_host);
+
+ soc_camera_host_unregister(soc_host);
+
+ /*
+ * The channel has either not been allocated,
+ * or should have been released
+ */
+ if (WARN_ON(mx3_cam->idmac_channel[0]))
+ dma_release_channel(&mx3_cam->idmac_channel[0]->dma_chan);
+
+ vb2_dma_contig_cleanup_ctx(mx3_cam->alloc_ctx);
+
+ dmaengine_put();
+
+ return 0;
+}
+
+static struct platform_driver mx3_camera_driver = {
+ .driver = {
+ .name = MX3_CAM_DRV_NAME,
+ },
+ .probe = mx3_camera_probe,
+ .remove = mx3_camera_remove,
+};
+
+module_platform_driver(mx3_camera_driver);
+
+MODULE_DESCRIPTION("i.MX3x SoC Camera Host driver");
+MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.2.3");
+MODULE_ALIAS("platform:" MX3_CAM_DRV_NAME);
--- /dev/null
+config VIDEO_OMAP1
+ tristate "OMAP1 Camera Interface driver"
+ depends on VIDEO_DEV && SOC_CAMERA
+ depends on ARCH_OMAP1
+ depends on HAS_DMA
+ select VIDEOBUF_DMA_CONTIG
+ select VIDEOBUF_DMA_SG
+ ---help---
+ This is a v4l2 driver for the TI OMAP1 camera interface
+
+ This driver is deprecated and will be removed soon unless someone
+ will start the work to convert this driver to the vb2 framework
+ and remove the soc-camera dependency.
--- /dev/null
+# Makefile for OMAP1 driver
+
+obj-$(CONFIG_VIDEO_OMAP1) += omap1_camera.o
--- /dev/null
+This driver is deprecated and will be removed soon unless someone will start
+the work to convert this driver to the vb2 framework and remove the
+soc-camera dependency.
+
+Note that trivial patches will not be accepted anymore, only a full conversion.
+
+If you want to convert this driver, please contact the linux-media mailinglist
+(see http://linuxtv.org/lists.php).
--- /dev/null
+/*
+ * V4L2 SoC Camera driver for OMAP1 Camera Interface
+ *
+ * Copyright (C) 2010, Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
+ *
+ * Based on V4L2 Driver for i.MXL/i.MXL camera (CSI) host
+ * Copyright (C) 2008, Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
+ * Copyright (C) 2009, Darius Augulis <augulis.darius@gmail.com>
+ *
+ * Based on PXA SoC camera driver
+ * Copyright (C) 2006, Sascha Hauer, Pengutronix
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * Hardware specific bits initialy based on former work by Matt Callow
+ * drivers/media/platform/omap/omap1510cam.c
+ * Copyright (C) 2006 Matt Callow
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <linux/platform_data/media/omap1_camera.h>
+#include <media/soc_camera.h>
+#include <media/drv-intf/soc_mediabus.h>
+#include <media/videobuf-dma-contig.h>
+#include <media/videobuf-dma-sg.h>
+
+#include <linux/omap-dma.h>
+
+
+#define DRIVER_NAME "omap1-camera"
+#define DRIVER_VERSION "0.0.2"
+
+#define OMAP_DMA_CAMERA_IF_RX 20
+
+/*
+ * ---------------------------------------------------------------------------
+ * OMAP1 Camera Interface registers
+ * ---------------------------------------------------------------------------
+ */
+
+#define REG_CTRLCLOCK 0x00
+#define REG_IT_STATUS 0x04
+#define REG_MODE 0x08
+#define REG_STATUS 0x0C
+#define REG_CAMDATA 0x10
+#define REG_GPIO 0x14
+#define REG_PEAK_COUNTER 0x18
+
+/* CTRLCLOCK bit shifts */
+#define LCLK_EN BIT(7)
+#define DPLL_EN BIT(6)
+#define MCLK_EN BIT(5)
+#define CAMEXCLK_EN BIT(4)
+#define POLCLK BIT(3)
+#define FOSCMOD_SHIFT 0
+#define FOSCMOD_MASK (0x7 << FOSCMOD_SHIFT)
+#define FOSCMOD_12MHz 0x0
+#define FOSCMOD_6MHz 0x2
+#define FOSCMOD_9_6MHz 0x4
+#define FOSCMOD_24MHz 0x5
+#define FOSCMOD_8MHz 0x6
+
+/* IT_STATUS bit shifts */
+#define DATA_TRANSFER BIT(5)
+#define FIFO_FULL BIT(4)
+#define H_DOWN BIT(3)
+#define H_UP BIT(2)
+#define V_DOWN BIT(1)
+#define V_UP BIT(0)
+
+/* MODE bit shifts */
+#define RAZ_FIFO BIT(18)
+#define EN_FIFO_FULL BIT(17)
+#define EN_NIRQ BIT(16)
+#define THRESHOLD_SHIFT 9
+#define THRESHOLD_MASK (0x7f << THRESHOLD_SHIFT)
+#define DMA BIT(8)
+#define EN_H_DOWN BIT(7)
+#define EN_H_UP BIT(6)
+#define EN_V_DOWN BIT(5)
+#define EN_V_UP BIT(4)
+#define ORDERCAMD BIT(3)
+
+#define IRQ_MASK (EN_V_UP | EN_V_DOWN | EN_H_UP | EN_H_DOWN | \
+ EN_NIRQ | EN_FIFO_FULL)
+
+/* STATUS bit shifts */
+#define HSTATUS BIT(1)
+#define VSTATUS BIT(0)
+
+/* GPIO bit shifts */
+#define CAM_RST BIT(0)
+
+/* end of OMAP1 Camera Interface registers */
+
+
+#define SOCAM_BUS_FLAGS (V4L2_MBUS_MASTER | \
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | \
+ V4L2_MBUS_DATA_ACTIVE_HIGH)
+
+
+#define FIFO_SIZE ((THRESHOLD_MASK >> THRESHOLD_SHIFT) + 1)
+#define FIFO_SHIFT __fls(FIFO_SIZE)
+
+#define DMA_BURST_SHIFT (1 + OMAP_DMA_DATA_BURST_4)
+#define DMA_BURST_SIZE (1 << DMA_BURST_SHIFT)
+
+#define DMA_ELEMENT_SHIFT OMAP_DMA_DATA_TYPE_S32
+#define DMA_ELEMENT_SIZE (1 << DMA_ELEMENT_SHIFT)
+
+#define DMA_FRAME_SHIFT_CONTIG (FIFO_SHIFT - 1)
+#define DMA_FRAME_SHIFT_SG DMA_BURST_SHIFT
+
+#define DMA_FRAME_SHIFT(x) ((x) == OMAP1_CAM_DMA_CONTIG ? \
+ DMA_FRAME_SHIFT_CONTIG : \
+ DMA_FRAME_SHIFT_SG)
+#define DMA_FRAME_SIZE(x) (1 << DMA_FRAME_SHIFT(x))
+#define DMA_SYNC OMAP_DMA_SYNC_FRAME
+#define THRESHOLD_LEVEL DMA_FRAME_SIZE
+
+
+#define MAX_VIDEO_MEM 4 /* arbitrary video memory limit in MB */
+
+
+/*
+ * Structures
+ */
+
+/* buffer for one video frame */
+struct omap1_cam_buf {
+ struct videobuf_buffer vb;
+ u32 code;
+ int inwork;
+ struct scatterlist *sgbuf;
+ int sgcount;
+ int bytes_left;
+ enum videobuf_state result;
+};
+
+struct omap1_cam_dev {
+ struct soc_camera_host soc_host;
+ struct clk *clk;
+
+ unsigned int irq;
+ void __iomem *base;
+
+ int dma_ch;
+
+ struct omap1_cam_platform_data *pdata;
+ struct resource *res;
+ unsigned long pflags;
+ unsigned long camexclk;
+
+ struct list_head capture;
+
+ /* lock used to protect videobuf */
+ spinlock_t lock;
+
+ /* Pointers to DMA buffers */
+ struct omap1_cam_buf *active;
+ struct omap1_cam_buf *ready;
+
+ enum omap1_cam_vb_mode vb_mode;
+ int (*mmap_mapper)(struct videobuf_queue *q,
+ struct videobuf_buffer *buf,
+ struct vm_area_struct *vma);
+
+ u32 reg_cache[0];
+};
+
+
+static void cam_write(struct omap1_cam_dev *pcdev, u16 reg, u32 val)
+{
+ pcdev->reg_cache[reg / sizeof(u32)] = val;
+ __raw_writel(val, pcdev->base + reg);
+}
+
+static u32 cam_read(struct omap1_cam_dev *pcdev, u16 reg, bool from_cache)
+{
+ return !from_cache ? __raw_readl(pcdev->base + reg) :
+ pcdev->reg_cache[reg / sizeof(u32)];
+}
+
+#define CAM_READ(pcdev, reg) \
+ cam_read(pcdev, REG_##reg, false)
+#define CAM_WRITE(pcdev, reg, val) \
+ cam_write(pcdev, REG_##reg, val)
+#define CAM_READ_CACHE(pcdev, reg) \
+ cam_read(pcdev, REG_##reg, true)
+
+/*
+ * Videobuf operations
+ */
+static int omap1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
+ unsigned int *size)
+{
+ struct soc_camera_device *icd = vq->priv_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct omap1_cam_dev *pcdev = ici->priv;
+
+ *size = icd->sizeimage;
+
+ if (!*count || *count < OMAP1_CAMERA_MIN_BUF_COUNT(pcdev->vb_mode))
+ *count = OMAP1_CAMERA_MIN_BUF_COUNT(pcdev->vb_mode);
+
+ if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024)
+ *count = (MAX_VIDEO_MEM * 1024 * 1024) / *size;
+
+ dev_dbg(icd->parent,
+ "%s: count=%d, size=%d\n", __func__, *count, *size);
+
+ return 0;
+}
+
+static void free_buffer(struct videobuf_queue *vq, struct omap1_cam_buf *buf,
+ enum omap1_cam_vb_mode vb_mode)
+{
+ struct videobuf_buffer *vb = &buf->vb;
+
+ BUG_ON(in_interrupt());
+
+ videobuf_waiton(vq, vb, 0, 0);
+
+ if (vb_mode == OMAP1_CAM_DMA_CONTIG) {
+ videobuf_dma_contig_free(vq, vb);
+ } else {
+ struct soc_camera_device *icd = vq->priv_data;
+ struct device *dev = icd->parent;
+ struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+
+ videobuf_dma_unmap(dev, dma);
+ videobuf_dma_free(dma);
+ }
+
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int omap1_videobuf_prepare(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb, enum v4l2_field field)
+{
+ struct soc_camera_device *icd = vq->priv_data;
+ struct omap1_cam_buf *buf = container_of(vb, struct omap1_cam_buf, vb);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct omap1_cam_dev *pcdev = ici->priv;
+ int ret;
+
+ WARN_ON(!list_empty(&vb->queue));
+
+ BUG_ON(NULL == icd->current_fmt);
+
+ buf->inwork = 1;
+
+ if (buf->code != icd->current_fmt->code || vb->field != field ||
+ vb->width != icd->user_width ||
+ vb->height != icd->user_height) {
+ buf->code = icd->current_fmt->code;
+ vb->width = icd->user_width;
+ vb->height = icd->user_height;
+ vb->field = field;
+ vb->state = VIDEOBUF_NEEDS_INIT;
+ }
+
+ vb->size = icd->sizeimage;
+
+ if (vb->baddr && vb->bsize < vb->size) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (vb->state == VIDEOBUF_NEEDS_INIT) {
+ ret = videobuf_iolock(vq, vb, NULL);
+ if (ret)
+ goto fail;
+
+ vb->state = VIDEOBUF_PREPARED;
+ }
+ buf->inwork = 0;
+
+ return 0;
+fail:
+ free_buffer(vq, buf, pcdev->vb_mode);
+out:
+ buf->inwork = 0;
+ return ret;
+}
+
+static void set_dma_dest_params(int dma_ch, struct omap1_cam_buf *buf,
+ enum omap1_cam_vb_mode vb_mode)
+{
+ dma_addr_t dma_addr;
+ unsigned int block_size;
+
+ if (vb_mode == OMAP1_CAM_DMA_CONTIG) {
+ dma_addr = videobuf_to_dma_contig(&buf->vb);
+ block_size = buf->vb.size;
+ } else {
+ if (WARN_ON(!buf->sgbuf)) {
+ buf->result = VIDEOBUF_ERROR;
+ return;
+ }
+ dma_addr = sg_dma_address(buf->sgbuf);
+ if (WARN_ON(!dma_addr)) {
+ buf->sgbuf = NULL;
+ buf->result = VIDEOBUF_ERROR;
+ return;
+ }
+ block_size = sg_dma_len(buf->sgbuf);
+ if (WARN_ON(!block_size)) {
+ buf->sgbuf = NULL;
+ buf->result = VIDEOBUF_ERROR;
+ return;
+ }
+ if (unlikely(buf->bytes_left < block_size))
+ block_size = buf->bytes_left;
+ if (WARN_ON(dma_addr & (DMA_FRAME_SIZE(vb_mode) *
+ DMA_ELEMENT_SIZE - 1))) {
+ dma_addr = ALIGN(dma_addr, DMA_FRAME_SIZE(vb_mode) *
+ DMA_ELEMENT_SIZE);
+ block_size &= ~(DMA_FRAME_SIZE(vb_mode) *
+ DMA_ELEMENT_SIZE - 1);
+ }
+ buf->bytes_left -= block_size;
+ buf->sgcount++;
+ }
+
+ omap_set_dma_dest_params(dma_ch,
+ OMAP_DMA_PORT_EMIFF, OMAP_DMA_AMODE_POST_INC, dma_addr, 0, 0);
+ omap_set_dma_transfer_params(dma_ch,
+ OMAP_DMA_DATA_TYPE_S32, DMA_FRAME_SIZE(vb_mode),
+ block_size >> (DMA_FRAME_SHIFT(vb_mode) + DMA_ELEMENT_SHIFT),
+ DMA_SYNC, 0, 0);
+}
+
+static struct omap1_cam_buf *prepare_next_vb(struct omap1_cam_dev *pcdev)
+{
+ struct omap1_cam_buf *buf;
+
+ /*
+ * If there is already a buffer pointed out by the pcdev->ready,
+ * (re)use it, otherwise try to fetch and configure a new one.
+ */
+ buf = pcdev->ready;
+ if (!buf) {
+ if (list_empty(&pcdev->capture))
+ return buf;
+ buf = list_entry(pcdev->capture.next,
+ struct omap1_cam_buf, vb.queue);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ pcdev->ready = buf;
+ list_del_init(&buf->vb.queue);
+ }
+
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
+ /*
+ * In CONTIG mode, we can safely enter next buffer parameters
+ * into the DMA programming register set after the DMA
+ * has already been activated on the previous buffer
+ */
+ set_dma_dest_params(pcdev->dma_ch, buf, pcdev->vb_mode);
+ } else {
+ /*
+ * In SG mode, the above is not safe since there are probably
+ * a bunch of sgbufs from previous sglist still pending.
+ * Instead, mark the sglist fresh for the upcoming
+ * try_next_sgbuf().
+ */
+ buf->sgbuf = NULL;
+ }
+
+ return buf;
+}
+
+static struct scatterlist *try_next_sgbuf(int dma_ch, struct omap1_cam_buf *buf)
+{
+ struct scatterlist *sgbuf;
+
+ if (likely(buf->sgbuf)) {
+ /* current sglist is active */
+ if (unlikely(!buf->bytes_left)) {
+ /* indicate sglist complete */
+ sgbuf = NULL;
+ } else {
+ /* process next sgbuf */
+ sgbuf = sg_next(buf->sgbuf);
+ if (WARN_ON(!sgbuf)) {
+ buf->result = VIDEOBUF_ERROR;
+ } else if (WARN_ON(!sg_dma_len(sgbuf))) {
+ sgbuf = NULL;
+ buf->result = VIDEOBUF_ERROR;
+ }
+ }
+ buf->sgbuf = sgbuf;
+ } else {
+ /* sglist is fresh, initialize it before using */
+ struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
+
+ sgbuf = dma->sglist;
+ if (!(WARN_ON(!sgbuf))) {
+ buf->sgbuf = sgbuf;
+ buf->sgcount = 0;
+ buf->bytes_left = buf->vb.size;
+ buf->result = VIDEOBUF_DONE;
+ }
+ }
+ if (sgbuf)
+ /*
+ * Put our next sgbuf parameters (address, size)
+ * into the DMA programming register set.
+ */
+ set_dma_dest_params(dma_ch, buf, OMAP1_CAM_DMA_SG);
+
+ return sgbuf;
+}
+
+static void start_capture(struct omap1_cam_dev *pcdev)
+{
+ struct omap1_cam_buf *buf = pcdev->active;
+ u32 ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
+ u32 mode = CAM_READ_CACHE(pcdev, MODE) & ~EN_V_DOWN;
+
+ if (WARN_ON(!buf))
+ return;
+
+ /*
+ * Enable start of frame interrupt, which we will use for activating
+ * our end of frame watchdog when capture actually starts.
+ */
+ mode |= EN_V_UP;
+
+ if (unlikely(ctrlclock & LCLK_EN))
+ /* stop pixel clock before FIFO reset */
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
+ /* reset FIFO */
+ CAM_WRITE(pcdev, MODE, mode | RAZ_FIFO);
+
+ omap_start_dma(pcdev->dma_ch);
+
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_SG) {
+ /*
+ * In SG mode, it's a good moment for fetching next sgbuf
+ * from the current sglist and, if available, already putting
+ * its parameters into the DMA programming register set.
+ */
+ try_next_sgbuf(pcdev->dma_ch, buf);
+ }
+
+ /* (re)enable pixel clock */
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock | LCLK_EN);
+ /* release FIFO reset */
+ CAM_WRITE(pcdev, MODE, mode);
+}
+
+static void suspend_capture(struct omap1_cam_dev *pcdev)
+{
+ u32 ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
+
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
+ omap_stop_dma(pcdev->dma_ch);
+}
+
+static void disable_capture(struct omap1_cam_dev *pcdev)
+{
+ u32 mode = CAM_READ_CACHE(pcdev, MODE);
+
+ CAM_WRITE(pcdev, MODE, mode & ~(IRQ_MASK | DMA));
+}
+
+static void omap1_videobuf_queue(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct soc_camera_device *icd = vq->priv_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct omap1_cam_dev *pcdev = ici->priv;
+ struct omap1_cam_buf *buf;
+ u32 mode;
+
+ list_add_tail(&vb->queue, &pcdev->capture);
+ vb->state = VIDEOBUF_QUEUED;
+
+ if (pcdev->active) {
+ /*
+ * Capture in progress, so don't touch pcdev->ready even if
+ * empty. Since the transfer of the DMA programming register set
+ * content to the DMA working register set is done automatically
+ * by the DMA hardware, this can pretty well happen while we
+ * are keeping the lock here. Leave fetching it from the queue
+ * to be done when a next DMA interrupt occures instead.
+ */
+ return;
+ }
+
+ WARN_ON(pcdev->ready);
+
+ buf = prepare_next_vb(pcdev);
+ if (WARN_ON(!buf))
+ return;
+
+ pcdev->active = buf;
+ pcdev->ready = NULL;
+
+ dev_dbg(icd->parent,
+ "%s: capture not active, setup FIFO, start DMA\n", __func__);
+ mode = CAM_READ_CACHE(pcdev, MODE) & ~THRESHOLD_MASK;
+ mode |= THRESHOLD_LEVEL(pcdev->vb_mode) << THRESHOLD_SHIFT;
+ CAM_WRITE(pcdev, MODE, mode | EN_FIFO_FULL | DMA);
+
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_SG) {
+ /*
+ * In SG mode, the above prepare_next_vb() didn't actually
+ * put anything into the DMA programming register set,
+ * so we have to do it now, before activating DMA.
+ */
+ try_next_sgbuf(pcdev->dma_ch, buf);
+ }
+
+ start_capture(pcdev);
+}
+
+static void omap1_videobuf_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct omap1_cam_buf *buf =
+ container_of(vb, struct omap1_cam_buf, vb);
+ struct soc_camera_device *icd = vq->priv_data;
+ struct device *dev = icd->parent;
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
+ struct omap1_cam_dev *pcdev = ici->priv;
+
+ switch (vb->state) {
+ case VIDEOBUF_DONE:
+ dev_dbg(dev, "%s (done)\n", __func__);
+ break;
+ case VIDEOBUF_ACTIVE:
+ dev_dbg(dev, "%s (active)\n", __func__);
+ break;
+ case VIDEOBUF_QUEUED:
+ dev_dbg(dev, "%s (queued)\n", __func__);
+ break;
+ case VIDEOBUF_PREPARED:
+ dev_dbg(dev, "%s (prepared)\n", __func__);
+ break;
+ default:
+ dev_dbg(dev, "%s (unknown %d)\n", __func__, vb->state);
+ break;
+ }
+
+ free_buffer(vq, buf, pcdev->vb_mode);
+}
+
+static void videobuf_done(struct omap1_cam_dev *pcdev,
+ enum videobuf_state result)
+{
+ struct omap1_cam_buf *buf = pcdev->active;
+ struct videobuf_buffer *vb;
+ struct device *dev = pcdev->soc_host.icd->parent;
+
+ if (WARN_ON(!buf)) {
+ suspend_capture(pcdev);
+ disable_capture(pcdev);
+ return;
+ }
+
+ if (result == VIDEOBUF_ERROR)
+ suspend_capture(pcdev);
+
+ vb = &buf->vb;
+ if (waitqueue_active(&vb->done)) {
+ if (!pcdev->ready && result != VIDEOBUF_ERROR) {
+ /*
+ * No next buffer has been entered into the DMA
+ * programming register set on time (could be done only
+ * while the previous DMA interurpt was processed, not
+ * later), so the last DMA block, be it a whole buffer
+ * if in CONTIG or its last sgbuf if in SG mode, is
+ * about to be reused by the just autoreinitialized DMA
+ * engine, and overwritten with next frame data. Best we
+ * can do is stopping the capture as soon as possible,
+ * hopefully before the next frame start.
+ */
+ suspend_capture(pcdev);
+ }
+ vb->state = result;
+ v4l2_get_timestamp(&vb->ts);
+ if (result != VIDEOBUF_ERROR)
+ vb->field_count++;
+ wake_up(&vb->done);
+
+ /* shift in next buffer */
+ buf = pcdev->ready;
+ pcdev->active = buf;
+ pcdev->ready = NULL;
+
+ if (!buf) {
+ /*
+ * No next buffer was ready on time (see above), so
+ * indicate error condition to force capture restart or
+ * stop, depending on next buffer already queued or not.
+ */
+ result = VIDEOBUF_ERROR;
+ prepare_next_vb(pcdev);
+
+ buf = pcdev->ready;
+ pcdev->active = buf;
+ pcdev->ready = NULL;
+ }
+ } else if (pcdev->ready) {
+ /*
+ * In both CONTIG and SG mode, the DMA engine has possibly
+ * been already autoreinitialized with the preprogrammed
+ * pcdev->ready buffer. We can either accept this fact
+ * and just swap the buffers, or provoke an error condition
+ * and restart capture. The former seems less intrusive.
+ */
+ dev_dbg(dev, "%s: nobody waiting on videobuf, swap with next\n",
+ __func__);
+ pcdev->active = pcdev->ready;
+
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_SG) {
+ /*
+ * In SG mode, we have to make sure that the buffer we
+ * are putting back into the pcdev->ready is marked
+ * fresh.
+ */
+ buf->sgbuf = NULL;
+ }
+ pcdev->ready = buf;
+
+ buf = pcdev->active;
+ } else {
+ /*
+ * No next buffer has been entered into
+ * the DMA programming register set on time.
+ */
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
+ /*
+ * In CONTIG mode, the DMA engine has already been
+ * reinitialized with the current buffer. Best we can do
+ * is not touching it.
+ */
+ dev_dbg(dev,
+ "%s: nobody waiting on videobuf, reuse it\n",
+ __func__);
+ } else {
+ /*
+ * In SG mode, the DMA engine has just been
+ * autoreinitialized with the last sgbuf from the
+ * current list. Restart capture in order to transfer
+ * next frame start into the first sgbuf, not the last
+ * one.
+ */
+ if (result != VIDEOBUF_ERROR) {
+ suspend_capture(pcdev);
+ result = VIDEOBUF_ERROR;
+ }
+ }
+ }
+
+ if (!buf) {
+ dev_dbg(dev, "%s: no more videobufs, stop capture\n", __func__);
+ disable_capture(pcdev);
+ return;
+ }
+
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
+ /*
+ * In CONTIG mode, the current buffer parameters had already
+ * been entered into the DMA programming register set while the
+ * buffer was fetched with prepare_next_vb(), they may have also
+ * been transferred into the runtime set and already active if
+ * the DMA still running.
+ */
+ } else {
+ /* In SG mode, extra steps are required */
+ if (result == VIDEOBUF_ERROR)
+ /* make sure we (re)use sglist from start on error */
+ buf->sgbuf = NULL;
+
+ /*
+ * In any case, enter the next sgbuf parameters into the DMA
+ * programming register set. They will be used either during
+ * nearest DMA autoreinitialization or, in case of an error,
+ * on DMA startup below.
+ */
+ try_next_sgbuf(pcdev->dma_ch, buf);
+ }
+
+ if (result == VIDEOBUF_ERROR) {
+ dev_dbg(dev, "%s: videobuf error; reset FIFO, restart DMA\n",
+ __func__);
+ start_capture(pcdev);
+ /*
+ * In SG mode, the above also resulted in the next sgbuf
+ * parameters being entered into the DMA programming register
+ * set, making them ready for next DMA autoreinitialization.
+ */
+ }
+
+ /*
+ * Finally, try fetching next buffer.
+ * In CONTIG mode, it will also enter it into the DMA programming
+ * register set, making it ready for next DMA autoreinitialization.
+ */
+ prepare_next_vb(pcdev);
+}
+
+static void dma_isr(int channel, unsigned short status, void *data)
+{
+ struct omap1_cam_dev *pcdev = data;
+ struct omap1_cam_buf *buf = pcdev->active;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pcdev->lock, flags);
+
+ if (WARN_ON(!buf)) {
+ suspend_capture(pcdev);
+ disable_capture(pcdev);
+ goto out;
+ }
+
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
+ /*
+ * In CONTIG mode, assume we have just managed to collect the
+ * whole frame, hopefully before our end of frame watchdog is
+ * triggered. Then, all we have to do is disabling the watchdog
+ * for this frame, and calling videobuf_done() with success
+ * indicated.
+ */
+ CAM_WRITE(pcdev, MODE,
+ CAM_READ_CACHE(pcdev, MODE) & ~EN_V_DOWN);
+ videobuf_done(pcdev, VIDEOBUF_DONE);
+ } else {
+ /*
+ * In SG mode, we have to process every sgbuf from the current
+ * sglist, one after another.
+ */
+ if (buf->sgbuf) {
+ /*
+ * Current sglist not completed yet, try fetching next
+ * sgbuf, hopefully putting it into the DMA programming
+ * register set, making it ready for next DMA
+ * autoreinitialization.
+ */
+ try_next_sgbuf(pcdev->dma_ch, buf);
+ if (buf->sgbuf)
+ goto out;
+
+ /*
+ * No more sgbufs left in the current sglist. This
+ * doesn't mean that the whole videobuffer is already
+ * complete, but only that the last sgbuf from the
+ * current sglist is about to be filled. It will be
+ * ready on next DMA interrupt, signalled with the
+ * buf->sgbuf set back to NULL.
+ */
+ if (buf->result != VIDEOBUF_ERROR) {
+ /*
+ * Video frame collected without errors so far,
+ * we can prepare for collecting a next one
+ * as soon as DMA gets autoreinitialized
+ * after the current (last) sgbuf is completed.
+ */
+ buf = prepare_next_vb(pcdev);
+ if (!buf)
+ goto out;
+
+ try_next_sgbuf(pcdev->dma_ch, buf);
+ goto out;
+ }
+ }
+ /* end of videobuf */
+ videobuf_done(pcdev, buf->result);
+ }
+
+out:
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+}
+
+static irqreturn_t cam_isr(int irq, void *data)
+{
+ struct omap1_cam_dev *pcdev = data;
+ struct device *dev = pcdev->soc_host.icd->parent;
+ struct omap1_cam_buf *buf = pcdev->active;
+ u32 it_status;
+ unsigned long flags;
+
+ it_status = CAM_READ(pcdev, IT_STATUS);
+ if (!it_status)
+ return IRQ_NONE;
+
+ spin_lock_irqsave(&pcdev->lock, flags);
+
+ if (WARN_ON(!buf)) {
+ dev_warn(dev, "%s: unhandled camera interrupt, status == %#x\n",
+ __func__, it_status);
+ suspend_capture(pcdev);
+ disable_capture(pcdev);
+ goto out;
+ }
+
+ if (unlikely(it_status & FIFO_FULL)) {
+ dev_warn(dev, "%s: FIFO overflow\n", __func__);
+
+ } else if (it_status & V_DOWN) {
+ /* end of video frame watchdog */
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
+ /*
+ * In CONTIG mode, the watchdog is disabled with
+ * successful DMA end of block interrupt, and reenabled
+ * on next frame start. If we get here, there is nothing
+ * to check, we must be out of sync.
+ */
+ } else {
+ if (buf->sgcount == 2) {
+ /*
+ * If exactly 2 sgbufs from the next sglist have
+ * been programmed into the DMA engine (the
+ * first one already transferred into the DMA
+ * runtime register set, the second one still
+ * in the programming set), then we are in sync.
+ */
+ goto out;
+ }
+ }
+ dev_notice(dev, "%s: unexpected end of video frame\n",
+ __func__);
+
+ } else if (it_status & V_UP) {
+ u32 mode;
+
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
+ /*
+ * In CONTIG mode, we need this interrupt every frame
+ * in oredr to reenable our end of frame watchdog.
+ */
+ mode = CAM_READ_CACHE(pcdev, MODE);
+ } else {
+ /*
+ * In SG mode, the below enabled end of frame watchdog
+ * is kept on permanently, so we can turn this one shot
+ * setup off.
+ */
+ mode = CAM_READ_CACHE(pcdev, MODE) & ~EN_V_UP;
+ }
+
+ if (!(mode & EN_V_DOWN)) {
+ /* (re)enable end of frame watchdog interrupt */
+ mode |= EN_V_DOWN;
+ }
+ CAM_WRITE(pcdev, MODE, mode);
+ goto out;
+
+ } else {
+ dev_warn(dev, "%s: unhandled camera interrupt, status == %#x\n",
+ __func__, it_status);
+ goto out;
+ }
+
+ videobuf_done(pcdev, VIDEOBUF_ERROR);
+out:
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static struct videobuf_queue_ops omap1_videobuf_ops = {
+ .buf_setup = omap1_videobuf_setup,
+ .buf_prepare = omap1_videobuf_prepare,
+ .buf_queue = omap1_videobuf_queue,
+ .buf_release = omap1_videobuf_release,
+};
+
+
+/*
+ * SOC Camera host operations
+ */
+
+static void sensor_reset(struct omap1_cam_dev *pcdev, bool reset)
+{
+ /* apply/release camera sensor reset if requested by platform data */
+ if (pcdev->pflags & OMAP1_CAMERA_RST_HIGH)
+ CAM_WRITE(pcdev, GPIO, reset);
+ else if (pcdev->pflags & OMAP1_CAMERA_RST_LOW)
+ CAM_WRITE(pcdev, GPIO, !reset);
+}
+
+static int omap1_cam_add_device(struct soc_camera_device *icd)
+{
+ dev_dbg(icd->parent, "OMAP1 Camera driver attached to camera %d\n",
+ icd->devnum);
+
+ return 0;
+}
+
+static void omap1_cam_remove_device(struct soc_camera_device *icd)
+{
+ dev_dbg(icd->parent,
+ "OMAP1 Camera driver detached from camera %d\n", icd->devnum);
+}
+
+/*
+ * The following two functions absolutely depend on the fact, that
+ * there can be only one camera on OMAP1 camera sensor interface
+ */
+static int omap1_cam_clock_start(struct soc_camera_host *ici)
+{
+ struct omap1_cam_dev *pcdev = ici->priv;
+ u32 ctrlclock;
+
+ clk_enable(pcdev->clk);
+
+ /* setup sensor clock */
+ ctrlclock = CAM_READ(pcdev, CTRLCLOCK);
+ ctrlclock &= ~(CAMEXCLK_EN | MCLK_EN | DPLL_EN);
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
+
+ ctrlclock &= ~FOSCMOD_MASK;
+ switch (pcdev->camexclk) {
+ case 6000000:
+ ctrlclock |= CAMEXCLK_EN | FOSCMOD_6MHz;
+ break;
+ case 8000000:
+ ctrlclock |= CAMEXCLK_EN | FOSCMOD_8MHz | DPLL_EN;
+ break;
+ case 9600000:
+ ctrlclock |= CAMEXCLK_EN | FOSCMOD_9_6MHz | DPLL_EN;
+ break;
+ case 12000000:
+ ctrlclock |= CAMEXCLK_EN | FOSCMOD_12MHz;
+ break;
+ case 24000000:
+ ctrlclock |= CAMEXCLK_EN | FOSCMOD_24MHz | DPLL_EN;
+ default:
+ break;
+ }
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~DPLL_EN);
+
+ /* enable internal clock */
+ ctrlclock |= MCLK_EN;
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
+
+ sensor_reset(pcdev, false);
+
+ return 0;
+}
+
+static void omap1_cam_clock_stop(struct soc_camera_host *ici)
+{
+ struct omap1_cam_dev *pcdev = ici->priv;
+ u32 ctrlclock;
+
+ suspend_capture(pcdev);
+ disable_capture(pcdev);
+
+ sensor_reset(pcdev, true);
+
+ /* disable and release system clocks */
+ ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
+ ctrlclock &= ~(MCLK_EN | DPLL_EN | CAMEXCLK_EN);
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
+
+ ctrlclock = (ctrlclock & ~FOSCMOD_MASK) | FOSCMOD_12MHz;
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock | MCLK_EN);
+
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~MCLK_EN);
+
+ clk_disable(pcdev->clk);
+}
+
+/* Duplicate standard formats based on host capability of byte swapping */
+static const struct soc_mbus_lookup omap1_cam_formats[] = {
+{
+ .code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .name = "YUYV",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+}, {
+ .code = MEDIA_BUS_FMT_VYUY8_2X8,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .name = "YVYU",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+}, {
+ .code = MEDIA_BUS_FMT_YUYV8_2X8,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .name = "UYVY",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+}, {
+ .code = MEDIA_BUS_FMT_YVYU8_2X8,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .name = "VYUY",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+}, {
+ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_RGB555,
+ .name = "RGB555",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+}, {
+ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_RGB555X,
+ .name = "RGB555X",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+}, {
+ .code = MEDIA_BUS_FMT_RGB565_2X8_BE,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .name = "RGB565",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+}, {
+ .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_RGB565X,
+ .name = "RGB565X",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+},
+};
+
+static int omap1_cam_get_formats(struct soc_camera_device *icd,
+ unsigned int idx, struct soc_camera_format_xlate *xlate)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct device *dev = icd->parent;
+ int formats = 0, ret;
+ struct v4l2_subdev_mbus_code_enum code = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ .index = idx,
+ };
+ const struct soc_mbus_pixelfmt *fmt;
+
+ ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code);
+ if (ret < 0)
+ /* No more formats */
+ return 0;
+
+ fmt = soc_mbus_get_fmtdesc(code.code);
+ if (!fmt) {
+ dev_warn(dev, "%s: unsupported format code #%d: %d\n", __func__,
+ idx, code.code);
+ return 0;
+ }
+
+ /* Check support for the requested bits-per-sample */
+ if (fmt->bits_per_sample != 8)
+ return 0;
+
+ switch (code.code) {
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_YVYU8_2X8:
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ case MEDIA_BUS_FMT_VYUY8_2X8:
+ case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE:
+ case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE:
+ case MEDIA_BUS_FMT_RGB565_2X8_BE:
+ case MEDIA_BUS_FMT_RGB565_2X8_LE:
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = soc_mbus_find_fmtdesc(code.code,
+ omap1_cam_formats,
+ ARRAY_SIZE(omap1_cam_formats));
+ xlate->code = code.code;
+ xlate++;
+ dev_dbg(dev,
+ "%s: providing format %s as byte swapped code #%d\n",
+ __func__, xlate->host_fmt->name, code.code);
+ }
+ default:
+ if (xlate)
+ dev_dbg(dev,
+ "%s: providing format %s in pass-through mode\n",
+ __func__, fmt->name);
+ }
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = fmt;
+ xlate->code = code.code;
+ xlate++;
+ }
+
+ return formats;
+}
+
+static bool is_dma_aligned(s32 bytes_per_line, unsigned int height,
+ enum omap1_cam_vb_mode vb_mode)
+{
+ int size = bytes_per_line * height;
+
+ return IS_ALIGNED(bytes_per_line, DMA_ELEMENT_SIZE) &&
+ IS_ALIGNED(size, DMA_FRAME_SIZE(vb_mode) * DMA_ELEMENT_SIZE);
+}
+
+static int dma_align(int *width, int *height,
+ const struct soc_mbus_pixelfmt *fmt,
+ enum omap1_cam_vb_mode vb_mode, bool enlarge)
+{
+ s32 bytes_per_line = soc_mbus_bytes_per_line(*width, fmt);
+
+ if (bytes_per_line < 0)
+ return bytes_per_line;
+
+ if (!is_dma_aligned(bytes_per_line, *height, vb_mode)) {
+ unsigned int pxalign = __fls(bytes_per_line / *width);
+ unsigned int salign = DMA_FRAME_SHIFT(vb_mode) +
+ DMA_ELEMENT_SHIFT - pxalign;
+ unsigned int incr = enlarge << salign;
+
+ v4l_bound_align_image(width, 1, *width + incr, 0,
+ height, 1, *height + incr, 0, salign);
+ return 0;
+ }
+ return 1;
+}
+
+#define subdev_call_with_sense(pcdev, dev, icd, sd, op, function, args...) \
+({ \
+ struct soc_camera_sense sense = { \
+ .master_clock = pcdev->camexclk, \
+ .pixel_clock_max = 0, \
+ }; \
+ int __ret; \
+ \
+ if (pcdev->pdata) \
+ sense.pixel_clock_max = pcdev->pdata->lclk_khz_max * 1000; \
+ icd->sense = &sense; \
+ __ret = v4l2_subdev_call(sd, op, function, ##args); \
+ icd->sense = NULL; \
+ \
+ if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) { \
+ if (sense.pixel_clock > sense.pixel_clock_max) { \
+ dev_err(dev, \
+ "%s: pixel clock %lu set by the camera too high!\n", \
+ __func__, sense.pixel_clock); \
+ __ret = -EINVAL; \
+ } \
+ } \
+ __ret; \
+})
+
+static int set_format(struct omap1_cam_dev *pcdev, struct device *dev,
+ struct soc_camera_device *icd, struct v4l2_subdev *sd,
+ struct v4l2_subdev_format *format,
+ const struct soc_camera_format_xlate *xlate)
+{
+ s32 bytes_per_line;
+ struct v4l2_mbus_framefmt *mf = &format->format;
+ int ret = subdev_call_with_sense(pcdev, dev, icd, sd, pad, set_fmt, NULL, format);
+
+ if (ret < 0) {
+ dev_err(dev, "%s: set_fmt failed\n", __func__);
+ return ret;
+ }
+
+ if (mf->code != xlate->code) {
+ dev_err(dev, "%s: unexpected pixel code change\n", __func__);
+ return -EINVAL;
+ }
+
+ bytes_per_line = soc_mbus_bytes_per_line(mf->width, xlate->host_fmt);
+ if (bytes_per_line < 0) {
+ dev_err(dev, "%s: soc_mbus_bytes_per_line() failed\n",
+ __func__);
+ return bytes_per_line;
+ }
+
+ if (!is_dma_aligned(bytes_per_line, mf->height, pcdev->vb_mode)) {
+ dev_err(dev, "%s: resulting geometry %ux%u not DMA aligned\n",
+ __func__, mf->width, mf->height);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int omap1_cam_set_crop(struct soc_camera_device *icd,
+ const struct v4l2_crop *crop)
+{
+ const struct v4l2_rect *rect = &crop->c;
+ const struct soc_camera_format_xlate *xlate = icd->current_fmt;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct device *dev = icd->parent;
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
+ struct omap1_cam_dev *pcdev = ici->priv;
+ struct v4l2_subdev_format fmt = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ struct v4l2_mbus_framefmt *mf = &fmt.format;
+ int ret;
+
+ ret = subdev_call_with_sense(pcdev, dev, icd, sd, video, s_crop, crop);
+ if (ret < 0) {
+ dev_warn(dev, "%s: failed to crop to %ux%u@%u:%u\n", __func__,
+ rect->width, rect->height, rect->left, rect->top);
+ return ret;
+ }
+
+ ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt);
+ if (ret < 0) {
+ dev_warn(dev, "%s: failed to fetch current format\n", __func__);
+ return ret;
+ }
+
+ ret = dma_align(&mf->width, &mf->height, xlate->host_fmt, pcdev->vb_mode,
+ false);
+ if (ret < 0) {
+ dev_err(dev, "%s: failed to align %ux%u %s with DMA\n",
+ __func__, mf->width, mf->height,
+ xlate->host_fmt->name);
+ return ret;
+ }
+
+ if (!ret) {
+ /* sensor returned geometry not DMA aligned, trying to fix */
+ ret = set_format(pcdev, dev, icd, sd, &fmt, xlate);
+ if (ret < 0) {
+ dev_err(dev, "%s: failed to set format\n", __func__);
+ return ret;
+ }
+ }
+
+ icd->user_width = mf->width;
+ icd->user_height = mf->height;
+
+ return 0;
+}
+
+static int omap1_cam_set_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate;
+ struct device *dev = icd->parent;
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
+ struct omap1_cam_dev *pcdev = ici->priv;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_subdev_format format = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ struct v4l2_mbus_framefmt *mf = &format.format;
+ int ret;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+ if (!xlate) {
+ dev_warn(dev, "%s: format %#x not found\n", __func__,
+ pix->pixelformat);
+ return -EINVAL;
+ }
+
+ mf->width = pix->width;
+ mf->height = pix->height;
+ mf->field = pix->field;
+ mf->colorspace = pix->colorspace;
+ mf->code = xlate->code;
+
+ ret = dma_align(&mf->width, &mf->height, xlate->host_fmt, pcdev->vb_mode,
+ true);
+ if (ret < 0) {
+ dev_err(dev, "%s: failed to align %ux%u %s with DMA\n",
+ __func__, pix->width, pix->height,
+ xlate->host_fmt->name);
+ return ret;
+ }
+
+ ret = set_format(pcdev, dev, icd, sd, &format, xlate);
+ if (ret < 0) {
+ dev_err(dev, "%s: failed to set format\n", __func__);
+ return ret;
+ }
+
+ pix->width = mf->width;
+ pix->height = mf->height;
+ pix->field = mf->field;
+ pix->colorspace = mf->colorspace;
+ icd->current_fmt = xlate;
+
+ return 0;
+}
+
+static int omap1_cam_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_subdev_pad_config pad_cfg;
+ struct v4l2_subdev_format format = {
+ .which = V4L2_SUBDEV_FORMAT_TRY,
+ };
+ struct v4l2_mbus_framefmt *mf = &format.format;
+ int ret;
+ /* TODO: limit to mx1 hardware capabilities */
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+ if (!xlate) {
+ dev_warn(icd->parent, "Format %#x not found\n",
+ pix->pixelformat);
+ return -EINVAL;
+ }
+
+ mf->width = pix->width;
+ mf->height = pix->height;
+ mf->field = pix->field;
+ mf->colorspace = pix->colorspace;
+ mf->code = xlate->code;
+
+ /* limit to sensor capabilities */
+ ret = v4l2_subdev_call(sd, pad, set_fmt, &pad_cfg, &format);
+ if (ret < 0)
+ return ret;
+
+ pix->width = mf->width;
+ pix->height = mf->height;
+ pix->field = mf->field;
+ pix->colorspace = mf->colorspace;
+
+ return 0;
+}
+
+static bool sg_mode;
+
+/*
+ * Local mmap_mapper wrapper,
+ * used for detecting videobuf-dma-contig buffer allocation failures
+ * and switching to videobuf-dma-sg automatically for future attempts.
+ */
+static int omap1_cam_mmap_mapper(struct videobuf_queue *q,
+ struct videobuf_buffer *buf,
+ struct vm_area_struct *vma)
+{
+ struct soc_camera_device *icd = q->priv_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct omap1_cam_dev *pcdev = ici->priv;
+ int ret;
+
+ ret = pcdev->mmap_mapper(q, buf, vma);
+
+ if (ret == -ENOMEM)
+ sg_mode = true;
+
+ return ret;
+}
+
+static void omap1_cam_init_videobuf(struct videobuf_queue *q,
+ struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct omap1_cam_dev *pcdev = ici->priv;
+
+ if (!sg_mode)
+ videobuf_queue_dma_contig_init(q, &omap1_videobuf_ops,
+ icd->parent, &pcdev->lock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
+ sizeof(struct omap1_cam_buf), icd, &ici->host_lock);
+ else
+ videobuf_queue_sg_init(q, &omap1_videobuf_ops,
+ icd->parent, &pcdev->lock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
+ sizeof(struct omap1_cam_buf), icd, &ici->host_lock);
+
+ /* use videobuf mode (auto)selected with the module parameter */
+ pcdev->vb_mode = sg_mode ? OMAP1_CAM_DMA_SG : OMAP1_CAM_DMA_CONTIG;
+
+ /*
+ * Ensure we substitute the videobuf-dma-contig version of the
+ * mmap_mapper() callback with our own wrapper, used for switching
+ * automatically to videobuf-dma-sg on buffer allocation failure.
+ */
+ if (!sg_mode && q->int_ops->mmap_mapper != omap1_cam_mmap_mapper) {
+ pcdev->mmap_mapper = q->int_ops->mmap_mapper;
+ q->int_ops->mmap_mapper = omap1_cam_mmap_mapper;
+ }
+}
+
+static int omap1_cam_reqbufs(struct soc_camera_device *icd,
+ struct v4l2_requestbuffers *p)
+{
+ int i;
+
+ /*
+ * This is for locking debugging only. I removed spinlocks and now I
+ * check whether .prepare is ever called on a linked buffer, or whether
+ * a dma IRQ can occur for an in-work or unlinked buffer. Until now
+ * it hadn't triggered
+ */
+ for (i = 0; i < p->count; i++) {
+ struct omap1_cam_buf *buf = container_of(icd->vb_vidq.bufs[i],
+ struct omap1_cam_buf, vb);
+ buf->inwork = 0;
+ INIT_LIST_HEAD(&buf->vb.queue);
+ }
+
+ return 0;
+}
+
+static int omap1_cam_querycap(struct soc_camera_host *ici,
+ struct v4l2_capability *cap)
+{
+ /* cap->name is set by the friendly caller:-> */
+ strlcpy(cap->card, "OMAP1 Camera", sizeof(cap->card));
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+ return 0;
+}
+
+static int omap1_cam_set_bus_param(struct soc_camera_device *icd)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct device *dev = icd->parent;
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
+ struct omap1_cam_dev *pcdev = ici->priv;
+ u32 pixfmt = icd->current_fmt->host_fmt->fourcc;
+ const struct soc_camera_format_xlate *xlate;
+ const struct soc_mbus_pixelfmt *fmt;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long common_flags;
+ u32 ctrlclock, mode;
+ int ret;
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg, SOCAM_BUS_FLAGS);
+ if (!common_flags) {
+ dev_warn(dev,
+ "Flags incompatible: camera 0x%x, host 0x%x\n",
+ cfg.flags, SOCAM_BUS_FLAGS);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ } else {
+ common_flags = SOCAM_BUS_FLAGS;
+ }
+
+ /* Make choices, possibly based on platform configuration */
+ if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+ (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
+ if (!pcdev->pdata ||
+ pcdev->pdata->flags & OMAP1_CAMERA_LCLK_RISING)
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
+ else
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
+ }
+
+ cfg.flags = common_flags;
+ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_dbg(dev, "camera s_mbus_config(0x%lx) returned %d\n",
+ common_flags, ret);
+ return ret;
+ }
+
+ ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
+ if (ctrlclock & LCLK_EN)
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
+
+ if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) {
+ dev_dbg(dev, "CTRLCLOCK_REG |= POLCLK\n");
+ ctrlclock |= POLCLK;
+ } else {
+ dev_dbg(dev, "CTRLCLOCK_REG &= ~POLCLK\n");
+ ctrlclock &= ~POLCLK;
+ }
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
+
+ if (ctrlclock & LCLK_EN)
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
+
+ /* select bus endianness */
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ fmt = xlate->host_fmt;
+
+ mode = CAM_READ(pcdev, MODE) & ~(RAZ_FIFO | IRQ_MASK | DMA);
+ if (fmt->order == SOC_MBUS_ORDER_LE) {
+ dev_dbg(dev, "MODE_REG &= ~ORDERCAMD\n");
+ CAM_WRITE(pcdev, MODE, mode & ~ORDERCAMD);
+ } else {
+ dev_dbg(dev, "MODE_REG |= ORDERCAMD\n");
+ CAM_WRITE(pcdev, MODE, mode | ORDERCAMD);
+ }
+
+ return 0;
+}
+
+static unsigned int omap1_cam_poll(struct file *file, poll_table *pt)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct omap1_cam_buf *buf;
+
+ buf = list_entry(icd->vb_vidq.stream.next, struct omap1_cam_buf,
+ vb.stream);
+
+ poll_wait(file, &buf->vb.done, pt);
+
+ if (buf->vb.state == VIDEOBUF_DONE ||
+ buf->vb.state == VIDEOBUF_ERROR)
+ return POLLIN | POLLRDNORM;
+
+ return 0;
+}
+
+static struct soc_camera_host_ops omap1_host_ops = {
+ .owner = THIS_MODULE,
+ .add = omap1_cam_add_device,
+ .remove = omap1_cam_remove_device,
+ .clock_start = omap1_cam_clock_start,
+ .clock_stop = omap1_cam_clock_stop,
+ .get_formats = omap1_cam_get_formats,
+ .set_crop = omap1_cam_set_crop,
+ .set_fmt = omap1_cam_set_fmt,
+ .try_fmt = omap1_cam_try_fmt,
+ .init_videobuf = omap1_cam_init_videobuf,
+ .reqbufs = omap1_cam_reqbufs,
+ .querycap = omap1_cam_querycap,
+ .set_bus_param = omap1_cam_set_bus_param,
+ .poll = omap1_cam_poll,
+};
+
+static int omap1_cam_probe(struct platform_device *pdev)
+{
+ struct omap1_cam_dev *pcdev;
+ struct resource *res;
+ struct clk *clk;
+ void __iomem *base;
+ unsigned int irq;
+ int err = 0;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq = platform_get_irq(pdev, 0);
+ if (!res || (int)irq <= 0) {
+ err = -ENODEV;
+ goto exit;
+ }
+
+ clk = clk_get(&pdev->dev, "armper_ck");
+ if (IS_ERR(clk)) {
+ err = PTR_ERR(clk);
+ goto exit;
+ }
+
+ pcdev = kzalloc(sizeof(*pcdev) + resource_size(res), GFP_KERNEL);
+ if (!pcdev) {
+ dev_err(&pdev->dev, "Could not allocate pcdev\n");
+ err = -ENOMEM;
+ goto exit_put_clk;
+ }
+
+ pcdev->res = res;
+ pcdev->clk = clk;
+
+ pcdev->pdata = pdev->dev.platform_data;
+ if (pcdev->pdata) {
+ pcdev->pflags = pcdev->pdata->flags;
+ pcdev->camexclk = pcdev->pdata->camexclk_khz * 1000;
+ }
+
+ switch (pcdev->camexclk) {
+ case 6000000:
+ case 8000000:
+ case 9600000:
+ case 12000000:
+ case 24000000:
+ break;
+ default:
+ /* pcdev->camexclk != 0 => pcdev->pdata != NULL */
+ dev_warn(&pdev->dev,
+ "Incorrect sensor clock frequency %ld kHz, "
+ "should be one of 0, 6, 8, 9.6, 12 or 24 MHz, "
+ "please correct your platform data\n",
+ pcdev->pdata->camexclk_khz);
+ pcdev->camexclk = 0;
+ case 0:
+ dev_info(&pdev->dev, "Not providing sensor clock\n");
+ }
+
+ INIT_LIST_HEAD(&pcdev->capture);
+ spin_lock_init(&pcdev->lock);
+
+ /*
+ * Request the region.
+ */
+ if (!request_mem_region(res->start, resource_size(res), DRIVER_NAME)) {
+ err = -EBUSY;
+ goto exit_kfree;
+ }
+
+ base = ioremap(res->start, resource_size(res));
+ if (!base) {
+ err = -ENOMEM;
+ goto exit_release;
+ }
+ pcdev->irq = irq;
+ pcdev->base = base;
+
+ sensor_reset(pcdev, true);
+
+ err = omap_request_dma(OMAP_DMA_CAMERA_IF_RX, DRIVER_NAME,
+ dma_isr, (void *)pcdev, &pcdev->dma_ch);
+ if (err < 0) {
+ dev_err(&pdev->dev, "Can't request DMA for OMAP1 Camera\n");
+ err = -EBUSY;
+ goto exit_iounmap;
+ }
+ dev_dbg(&pdev->dev, "got DMA channel %d\n", pcdev->dma_ch);
+
+ /* preconfigure DMA */
+ omap_set_dma_src_params(pcdev->dma_ch, OMAP_DMA_PORT_TIPB,
+ OMAP_DMA_AMODE_CONSTANT, res->start + REG_CAMDATA,
+ 0, 0);
+ omap_set_dma_dest_burst_mode(pcdev->dma_ch, OMAP_DMA_DATA_BURST_4);
+ /* setup DMA autoinitialization */
+ omap_dma_link_lch(pcdev->dma_ch, pcdev->dma_ch);
+
+ err = request_irq(pcdev->irq, cam_isr, 0, DRIVER_NAME, pcdev);
+ if (err) {
+ dev_err(&pdev->dev, "Camera interrupt register failed\n");
+ goto exit_free_dma;
+ }
+
+ pcdev->soc_host.drv_name = DRIVER_NAME;
+ pcdev->soc_host.ops = &omap1_host_ops;
+ pcdev->soc_host.priv = pcdev;
+ pcdev->soc_host.v4l2_dev.dev = &pdev->dev;
+ pcdev->soc_host.nr = pdev->id;
+
+ err = soc_camera_host_register(&pcdev->soc_host);
+ if (err)
+ goto exit_free_irq;
+
+ dev_info(&pdev->dev, "OMAP1 Camera Interface driver loaded\n");
+
+ return 0;
+
+exit_free_irq:
+ free_irq(pcdev->irq, pcdev);
+exit_free_dma:
+ omap_free_dma(pcdev->dma_ch);
+exit_iounmap:
+ iounmap(base);
+exit_release:
+ release_mem_region(res->start, resource_size(res));
+exit_kfree:
+ kfree(pcdev);
+exit_put_clk:
+ clk_put(clk);
+exit:
+ return err;
+}
+
+static int omap1_cam_remove(struct platform_device *pdev)
+{
+ struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
+ struct omap1_cam_dev *pcdev = container_of(soc_host,
+ struct omap1_cam_dev, soc_host);
+ struct resource *res;
+
+ free_irq(pcdev->irq, pcdev);
+
+ omap_free_dma(pcdev->dma_ch);
+
+ soc_camera_host_unregister(soc_host);
+
+ iounmap(pcdev->base);
+
+ res = pcdev->res;
+ release_mem_region(res->start, resource_size(res));
+
+ clk_put(pcdev->clk);
+
+ kfree(pcdev);
+
+ dev_info(&pdev->dev, "OMAP1 Camera Interface driver unloaded\n");
+
+ return 0;
+}
+
+static struct platform_driver omap1_cam_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+ .probe = omap1_cam_probe,
+ .remove = omap1_cam_remove,
+};
+
+module_platform_driver(omap1_cam_driver);
+
+module_param(sg_mode, bool, 0644);
+MODULE_PARM_DESC(sg_mode, "videobuf mode, 0: dma-contig (default), 1: dma-sg");
+
+MODULE_DESCRIPTION("OMAP1 Camera Interface driver");
+MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_ALIAS("platform:" DRIVER_NAME);
return IRQ_HANDLED;
}
-/* -----------------------------------------------------------------------------
- * Pipeline power management
- *
- * Entities must be powered up when part of a pipeline that contains at least
- * one open video device node.
- *
- * To achieve this use the entity use_count field to track the number of users.
- * For entities corresponding to video device nodes the use_count field stores
- * the users count of the node. For entities corresponding to subdevs the
- * use_count field stores the total number of users of all video device nodes
- * in the pipeline.
- *
- * The omap4iss_pipeline_pm_use() function must be called in the open() and
- * close() handlers of video device nodes. It increments or decrements the use
- * count of all subdev entities in the pipeline.
- *
- * To react to link management on powered pipelines, the link setup notification
- * callback updates the use count of all entities in the source and sink sides
- * of the link.
- */
-
-/*
- * iss_pipeline_pm_use_count - Count the number of users of a pipeline
- * @entity: The entity
- *
- * Return the total number of users of all video device nodes in the pipeline.
- */
-static int iss_pipeline_pm_use_count(struct media_entity *entity,
- struct media_entity_graph *graph)
-{
- int use = 0;
-
- media_entity_graph_walk_start(graph, entity);
-
- while ((entity = media_entity_graph_walk_next(graph))) {
- if (is_media_entity_v4l2_io(entity))
- use += entity->use_count;
- }
-
- return use;
-}
-
-/*
- * iss_pipeline_pm_power_one - Apply power change to an entity
- * @entity: The entity
- * @change: Use count change
- *
- * Change the entity use count by @change. If the entity is a subdev update its
- * power state by calling the core::s_power operation when the use count goes
- * from 0 to != 0 or from != 0 to 0.
- *
- * Return 0 on success or a negative error code on failure.
- */
-static int iss_pipeline_pm_power_one(struct media_entity *entity, int change)
-{
- struct v4l2_subdev *subdev;
-
- subdev = is_media_entity_v4l2_subdev(entity)
- ? media_entity_to_v4l2_subdev(entity) : NULL;
-
- if (entity->use_count == 0 && change > 0 && subdev) {
- int ret;
-
- ret = v4l2_subdev_call(subdev, core, s_power, 1);
- if (ret < 0 && ret != -ENOIOCTLCMD)
- return ret;
- }
-
- entity->use_count += change;
- WARN_ON(entity->use_count < 0);
-
- if (entity->use_count == 0 && change < 0 && subdev)
- v4l2_subdev_call(subdev, core, s_power, 0);
-
- return 0;
-}
-
-/*
- * iss_pipeline_pm_power - Apply power change to all entities in a pipeline
- * @entity: The entity
- * @change: Use count change
- *
- * Walk the pipeline to update the use count and the power state of all non-node
- * entities.
- *
- * Return 0 on success or a negative error code on failure.
- */
-static int iss_pipeline_pm_power(struct media_entity *entity, int change,
- struct media_entity_graph *graph)
-{
- struct media_entity *first = entity;
- int ret = 0;
-
- if (!change)
- return 0;
-
- media_entity_graph_walk_start(graph, entity);
-
- while (!ret && (entity = media_entity_graph_walk_next(graph)))
- if (is_media_entity_v4l2_subdev(entity))
- ret = iss_pipeline_pm_power_one(entity, change);
-
- if (!ret)
- return 0;
-
- media_entity_graph_walk_start(graph, first);
-
- while ((first = media_entity_graph_walk_next(graph)) &&
- first != entity)
- if (is_media_entity_v4l2_subdev(first))
- iss_pipeline_pm_power_one(first, -change);
-
- return ret;
-}
-
-/*
- * omap4iss_pipeline_pm_use - Update the use count of an entity
- * @entity: The entity
- * @use: Use (1) or stop using (0) the entity
- *
- * Update the use count of all entities in the pipeline and power entities on or
- * off accordingly.
- *
- * Return 0 on success or a negative error code on failure. Powering entities
- * off is assumed to never fail. No failure can occur when the use parameter is
- * set to 0.
- */
-int omap4iss_pipeline_pm_use(struct media_entity *entity, int use,
- struct media_entity_graph *graph)
-{
- int change = use ? 1 : -1;
- int ret;
-
- mutex_lock(&entity->graph_obj.mdev->graph_mutex);
-
- /* Apply use count to node. */
- entity->use_count += change;
- WARN_ON(entity->use_count < 0);
-
- /* Apply power change to connected non-nodes. */
- ret = iss_pipeline_pm_power(entity, change, graph);
- if (ret < 0)
- entity->use_count -= change;
-
- mutex_unlock(&entity->graph_obj.mdev->graph_mutex);
-
- return ret;
-}
-
-/*
- * iss_pipeline_link_notify - Link management notification callback
- * @link: The link
- * @flags: New link flags that will be applied
- *
- * React to link management on powered pipelines by updating the use count of
- * all entities in the source and sink sides of the link. Entities are powered
- * on or off accordingly.
- *
- * Return 0 on success or a negative error code on failure. Powering entities
- * off is assumed to never fail. This function will not fail for disconnection
- * events.
- */
-static int iss_pipeline_link_notify(struct media_link *link, u32 flags,
- unsigned int notification)
-{
- struct media_entity_graph *graph =
- &container_of(link->graph_obj.mdev, struct iss_device,
- media_dev)->pm_count_graph;
- struct media_entity *source = link->source->entity;
- struct media_entity *sink = link->sink->entity;
- int source_use;
- int sink_use;
- int ret;
-
- if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH) {
- ret = media_entity_graph_walk_init(graph,
- link->graph_obj.mdev);
- if (ret)
- return ret;
- }
-
- source_use = iss_pipeline_pm_use_count(source, graph);
- sink_use = iss_pipeline_pm_use_count(sink, graph);
-
- if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&
- !(flags & MEDIA_LNK_FL_ENABLED)) {
- /* Powering off entities is assumed to never fail. */
- iss_pipeline_pm_power(source, -sink_use, graph);
- iss_pipeline_pm_power(sink, -source_use, graph);
- return 0;
- }
-
- if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH &&
- (flags & MEDIA_LNK_FL_ENABLED)) {
- ret = iss_pipeline_pm_power(source, sink_use, graph);
- if (ret < 0)
- return ret;
-
- ret = iss_pipeline_pm_power(sink, source_use, graph);
- if (ret < 0)
- iss_pipeline_pm_power(source, -sink_use, graph);
- }
-
- if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH)
- media_entity_graph_walk_cleanup(graph);
-
- return ret;
-}
-
/* -----------------------------------------------------------------------------
* Pipeline stream management
*/
strlcpy(iss->media_dev.model, "TI OMAP4 ISS",
sizeof(iss->media_dev.model));
iss->media_dev.hw_revision = iss->revision;
- iss->media_dev.link_notify = iss_pipeline_link_notify;
+ iss->media_dev.link_notify = v4l2_pipeline_link_notify;
ret = media_device_register(&iss->media_dev);
if (ret < 0) {
dev_err(iss->dev, "Media device registration failed (%d)\n",
#define _OMAP4_ISS_H_
#include <media/v4l2-device.h>
+#include <media/v4l2-mc.h>
+
#include <linux/device.h>
#include <linux/io.h>
#include <linux/platform_device.h>
struct iss_device {
struct v4l2_device v4l2_dev;
struct media_device media_dev;
- struct media_entity_graph pm_count_graph;
struct device *dev;
u32 revision;
void omap4iss_isp_subclk_disable(struct iss_device *iss,
enum iss_isp_subclk_resource res);
-int omap4iss_pipeline_pm_use(struct media_entity *entity, int use,
- struct media_entity_graph *graph);
-
int omap4iss_register_entities(struct platform_device *pdev,
struct v4l2_device *v4l2_dev);
void omap4iss_unregister_entities(struct platform_device *pdev);
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/module.h>
+
#include <media/v4l2-dev.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mc.h>
#include "iss_video.h"
#include "iss.h"
goto done;
}
- ret = media_entity_graph_walk_init(&handle->graph,
- &video->iss->media_dev);
- if (ret)
- goto done;
-
- ret = omap4iss_pipeline_pm_use(&video->video.entity, 1,
- &handle->graph);
+ ret = v4l2_pipeline_pm_use(&video->video.entity, 1);
if (ret < 0) {
omap4iss_put(video->iss);
goto done;
done:
if (ret < 0) {
v4l2_fh_del(&handle->vfh);
- media_entity_graph_walk_cleanup(&handle->graph);
kfree(handle);
}
/* Disable streaming and free the buffers queue resources. */
iss_video_streamoff(file, vfh, video->type);
- omap4iss_pipeline_pm_use(&video->video.entity, 0, &handle->graph);
+ v4l2_pipeline_pm_use(&video->video.entity, 0);
/* Release the videobuf2 queue */
vb2_queue_release(&handle->queue);
- /* Release the file handle. */
- media_entity_graph_walk_cleanup(&handle->graph);
v4l2_fh_del(vfh);
kfree(handle);
file->private_data = NULL;
struct vb2_queue queue;
struct v4l2_format format;
struct v4l2_fract timeperframe;
- struct media_entity_graph graph;
};
#define to_iss_video_fh(fh) container_of(fh, struct iss_video_fh, vfh)
--- /dev/null
+config VIDEO_TIMBERDALE
+ tristate "Support for timberdale Video In/LogiWIN"
+ depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && HAS_DMA
+ depends on (MFD_TIMBERDALE && TIMB_DMA) || COMPILE_TEST
+ select VIDEO_ADV7180
+ select VIDEOBUF_DMA_CONTIG
+ ---help---
+ Add support for the Video In peripherial of the timberdale FPGA.
+
+ This driver is deprecated and will be removed soon unless someone
+ will start the work to convert this driver to the vb2 framework.
--- /dev/null
+obj-$(CONFIG_VIDEO_TIMBERDALE) += timblogiw.o
--- /dev/null
+/*
+ * timblogiw.c timberdale FPGA LogiWin Video In driver
+ * Copyright (c) 2009-2010 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Supports:
+ * Timberdale FPGA LogiWin Video In
+ */
+
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dmaengine.h>
+#include <linux/scatterlist.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf-dma-contig.h>
+#include <linux/platform_data/media/timb_video.h>
+
+#define DRIVER_NAME "timb-video"
+
+#define TIMBLOGIWIN_NAME "Timberdale Video-In"
+#define TIMBLOGIW_VERSION_CODE 0x04
+
+#define TIMBLOGIW_LINES_PER_DESC 44
+#define TIMBLOGIW_MAX_VIDEO_MEM 16
+
+#define TIMBLOGIW_HAS_DECODER(lw) (lw->pdata.encoder.module_name)
+
+
+struct timblogiw {
+ struct video_device video_dev;
+ struct v4l2_device v4l2_dev; /* mutual exclusion */
+ struct mutex lock;
+ struct device *dev;
+ struct timb_video_platform_data pdata;
+ struct v4l2_subdev *sd_enc; /* encoder */
+ bool opened;
+};
+
+struct timblogiw_tvnorm {
+ v4l2_std_id std;
+ u16 width;
+ u16 height;
+ u8 fps;
+};
+
+struct timblogiw_fh {
+ struct videobuf_queue vb_vidq;
+ struct timblogiw_tvnorm const *cur_norm;
+ struct list_head capture;
+ struct dma_chan *chan;
+ spinlock_t queue_lock; /* mutual exclusion */
+ unsigned int frame_count;
+};
+
+struct timblogiw_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct videobuf_buffer vb;
+ struct scatterlist sg[16];
+ dma_cookie_t cookie;
+ struct timblogiw_fh *fh;
+};
+
+static const struct timblogiw_tvnorm timblogiw_tvnorms[] = {
+ {
+ .std = V4L2_STD_PAL,
+ .width = 720,
+ .height = 576,
+ .fps = 25
+ },
+ {
+ .std = V4L2_STD_NTSC,
+ .width = 720,
+ .height = 480,
+ .fps = 30
+ }
+};
+
+static int timblogiw_bytes_per_line(const struct timblogiw_tvnorm *norm)
+{
+ return norm->width * 2;
+}
+
+
+static int timblogiw_frame_size(const struct timblogiw_tvnorm *norm)
+{
+ return norm->height * timblogiw_bytes_per_line(norm);
+}
+
+static const struct timblogiw_tvnorm *timblogiw_get_norm(const v4l2_std_id std)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(timblogiw_tvnorms); i++)
+ if (timblogiw_tvnorms[i].std & std)
+ return timblogiw_tvnorms + i;
+
+ /* default to first element */
+ return timblogiw_tvnorms;
+}
+
+static void timblogiw_dma_cb(void *data)
+{
+ struct timblogiw_buffer *buf = data;
+ struct timblogiw_fh *fh = buf->fh;
+ struct videobuf_buffer *vb = &buf->vb;
+
+ spin_lock(&fh->queue_lock);
+
+ /* mark the transfer done */
+ buf->cookie = -1;
+
+ fh->frame_count++;
+
+ if (vb->state != VIDEOBUF_ERROR) {
+ list_del(&vb->queue);
+ v4l2_get_timestamp(&vb->ts);
+ vb->field_count = fh->frame_count * 2;
+ vb->state = VIDEOBUF_DONE;
+
+ wake_up(&vb->done);
+ }
+
+ if (!list_empty(&fh->capture)) {
+ vb = list_entry(fh->capture.next, struct videobuf_buffer,
+ queue);
+ vb->state = VIDEOBUF_ACTIVE;
+ }
+
+ spin_unlock(&fh->queue_lock);
+}
+
+static bool timblogiw_dma_filter_fn(struct dma_chan *chan, void *filter_param)
+{
+ return chan->chan_id == (uintptr_t)filter_param;
+}
+
+/* IOCTL functions */
+
+static int timblogiw_g_fmt(struct file *file, void *priv,
+ struct v4l2_format *format)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw *lw = video_get_drvdata(vdev);
+ struct timblogiw_fh *fh = priv;
+
+ dev_dbg(&vdev->dev, "%s entry\n", __func__);
+
+ if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ mutex_lock(&lw->lock);
+
+ format->fmt.pix.width = fh->cur_norm->width;
+ format->fmt.pix.height = fh->cur_norm->height;
+ format->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
+ format->fmt.pix.bytesperline = timblogiw_bytes_per_line(fh->cur_norm);
+ format->fmt.pix.sizeimage = timblogiw_frame_size(fh->cur_norm);
+ format->fmt.pix.field = V4L2_FIELD_NONE;
+
+ mutex_unlock(&lw->lock);
+
+ return 0;
+}
+
+static int timblogiw_try_fmt(struct file *file, void *priv,
+ struct v4l2_format *format)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_pix_format *pix = &format->fmt.pix;
+
+ dev_dbg(&vdev->dev,
+ "%s - width=%d, height=%d, pixelformat=%d, field=%d\n"
+ "bytes per line %d, size image: %d, colorspace: %d\n",
+ __func__,
+ pix->width, pix->height, pix->pixelformat, pix->field,
+ pix->bytesperline, pix->sizeimage, pix->colorspace);
+
+ if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (pix->field != V4L2_FIELD_NONE)
+ return -EINVAL;
+
+ if (pix->pixelformat != V4L2_PIX_FMT_UYVY)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int timblogiw_s_fmt(struct file *file, void *priv,
+ struct v4l2_format *format)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw *lw = video_get_drvdata(vdev);
+ struct timblogiw_fh *fh = priv;
+ struct v4l2_pix_format *pix = &format->fmt.pix;
+ int err;
+
+ mutex_lock(&lw->lock);
+
+ err = timblogiw_try_fmt(file, priv, format);
+ if (err)
+ goto out;
+
+ if (videobuf_queue_is_busy(&fh->vb_vidq)) {
+ dev_err(&vdev->dev, "%s queue busy\n", __func__);
+ err = -EBUSY;
+ goto out;
+ }
+
+ pix->width = fh->cur_norm->width;
+ pix->height = fh->cur_norm->height;
+
+out:
+ mutex_unlock(&lw->lock);
+ return err;
+}
+
+static int timblogiw_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ dev_dbg(&vdev->dev, "%s: Entry\n", __func__);
+ strncpy(cap->card, TIMBLOGIWIN_NAME, sizeof(cap->card)-1);
+ strncpy(cap->driver, DRIVER_NAME, sizeof(cap->driver) - 1);
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", vdev->name);
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
+ V4L2_CAP_READWRITE;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+ return 0;
+}
+
+static int timblogiw_enum_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *fmt)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ dev_dbg(&vdev->dev, "%s, index: %d\n", __func__, fmt->index);
+
+ if (fmt->index != 0)
+ return -EINVAL;
+ memset(fmt, 0, sizeof(*fmt));
+ fmt->index = 0;
+ fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ strncpy(fmt->description, "4:2:2, packed, YUYV",
+ sizeof(fmt->description)-1);
+ fmt->pixelformat = V4L2_PIX_FMT_UYVY;
+
+ return 0;
+}
+
+static int timblogiw_g_parm(struct file *file, void *priv,
+ struct v4l2_streamparm *sp)
+{
+ struct timblogiw_fh *fh = priv;
+ struct v4l2_captureparm *cp = &sp->parm.capture;
+
+ cp->capability = V4L2_CAP_TIMEPERFRAME;
+ cp->timeperframe.numerator = 1;
+ cp->timeperframe.denominator = fh->cur_norm->fps;
+
+ return 0;
+}
+
+static int timblogiw_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *rb)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw_fh *fh = priv;
+
+ dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+ return videobuf_reqbufs(&fh->vb_vidq, rb);
+}
+
+static int timblogiw_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw_fh *fh = priv;
+
+ dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+ return videobuf_querybuf(&fh->vb_vidq, b);
+}
+
+static int timblogiw_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw_fh *fh = priv;
+
+ dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+ return videobuf_qbuf(&fh->vb_vidq, b);
+}
+
+static int timblogiw_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw_fh *fh = priv;
+
+ dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+ return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK);
+}
+
+static int timblogiw_g_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw_fh *fh = priv;
+
+ dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+ *std = fh->cur_norm->std;
+ return 0;
+}
+
+static int timblogiw_s_std(struct file *file, void *priv, v4l2_std_id std)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw *lw = video_get_drvdata(vdev);
+ struct timblogiw_fh *fh = priv;
+ int err = 0;
+
+ dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+ mutex_lock(&lw->lock);
+
+ if (TIMBLOGIW_HAS_DECODER(lw))
+ err = v4l2_subdev_call(lw->sd_enc, video, s_std, std);
+
+ if (!err)
+ fh->cur_norm = timblogiw_get_norm(std);
+
+ mutex_unlock(&lw->lock);
+
+ return err;
+}
+
+static int timblogiw_enuminput(struct file *file, void *priv,
+ struct v4l2_input *inp)
+{
+ struct video_device *vdev = video_devdata(file);
+ int i;
+
+ dev_dbg(&vdev->dev, "%s: Entry\n", __func__);
+
+ if (inp->index != 0)
+ return -EINVAL;
+
+ inp->index = 0;
+
+ strncpy(inp->name, "Timb input 1", sizeof(inp->name) - 1);
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+
+ inp->std = 0;
+ for (i = 0; i < ARRAY_SIZE(timblogiw_tvnorms); i++)
+ inp->std |= timblogiw_tvnorms[i].std;
+
+ return 0;
+}
+
+static int timblogiw_g_input(struct file *file, void *priv,
+ unsigned int *input)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ dev_dbg(&vdev->dev, "%s: Entry\n", __func__);
+
+ *input = 0;
+
+ return 0;
+}
+
+static int timblogiw_s_input(struct file *file, void *priv, unsigned int input)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ dev_dbg(&vdev->dev, "%s: Entry\n", __func__);
+
+ if (input != 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int timblogiw_streamon(struct file *file, void *priv, enum v4l2_buf_type type)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw_fh *fh = priv;
+
+ dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ dev_dbg(&vdev->dev, "%s - No capture device\n", __func__);
+ return -EINVAL;
+ }
+
+ fh->frame_count = 0;
+ return videobuf_streamon(&fh->vb_vidq);
+}
+
+static int timblogiw_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw_fh *fh = priv;
+
+ dev_dbg(&vdev->dev, "%s entry\n", __func__);
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ return videobuf_streamoff(&fh->vb_vidq);
+}
+
+static int timblogiw_querystd(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw *lw = video_get_drvdata(vdev);
+ struct timblogiw_fh *fh = priv;
+
+ dev_dbg(&vdev->dev, "%s entry\n", __func__);
+
+ if (TIMBLOGIW_HAS_DECODER(lw))
+ return v4l2_subdev_call(lw->sd_enc, video, querystd, std);
+ else {
+ *std = fh->cur_norm->std;
+ return 0;
+ }
+}
+
+static int timblogiw_enum_framesizes(struct file *file, void *priv,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw_fh *fh = priv;
+
+ dev_dbg(&vdev->dev, "%s - index: %d, format: %d\n", __func__,
+ fsize->index, fsize->pixel_format);
+
+ if ((fsize->index != 0) ||
+ (fsize->pixel_format != V4L2_PIX_FMT_UYVY))
+ return -EINVAL;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = fh->cur_norm->width;
+ fsize->discrete.height = fh->cur_norm->height;
+
+ return 0;
+}
+
+/* Video buffer functions */
+
+static int buffer_setup(struct videobuf_queue *vq, unsigned int *count,
+ unsigned int *size)
+{
+ struct timblogiw_fh *fh = vq->priv_data;
+
+ *size = timblogiw_frame_size(fh->cur_norm);
+
+ if (!*count)
+ *count = 32;
+
+ while (*size * *count > TIMBLOGIW_MAX_VIDEO_MEM * 1024 * 1024)
+ (*count)--;
+
+ return 0;
+}
+
+static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct timblogiw_fh *fh = vq->priv_data;
+ struct timblogiw_buffer *buf = container_of(vb, struct timblogiw_buffer,
+ vb);
+ unsigned int data_size = timblogiw_frame_size(fh->cur_norm);
+ int err = 0;
+
+ if (vb->baddr && vb->bsize < data_size)
+ /* User provided buffer, but it is too small */
+ return -ENOMEM;
+
+ vb->size = data_size;
+ vb->width = fh->cur_norm->width;
+ vb->height = fh->cur_norm->height;
+ vb->field = field;
+
+ if (vb->state == VIDEOBUF_NEEDS_INIT) {
+ int i;
+ unsigned int size;
+ unsigned int bytes_per_desc = TIMBLOGIW_LINES_PER_DESC *
+ timblogiw_bytes_per_line(fh->cur_norm);
+ dma_addr_t addr;
+
+ sg_init_table(buf->sg, ARRAY_SIZE(buf->sg));
+
+ err = videobuf_iolock(vq, vb, NULL);
+ if (err)
+ goto err;
+
+ addr = videobuf_to_dma_contig(vb);
+ for (i = 0, size = 0; size < data_size; i++) {
+ sg_dma_address(buf->sg + i) = addr + size;
+ size += bytes_per_desc;
+ sg_dma_len(buf->sg + i) = (size > data_size) ?
+ (bytes_per_desc - (size - data_size)) :
+ bytes_per_desc;
+ }
+
+ vb->state = VIDEOBUF_PREPARED;
+ buf->cookie = -1;
+ buf->fh = fh;
+ }
+
+ return 0;
+
+err:
+ videobuf_dma_contig_free(vq, vb);
+ vb->state = VIDEOBUF_NEEDS_INIT;
+ return err;
+}
+
+static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct timblogiw_fh *fh = vq->priv_data;
+ struct timblogiw_buffer *buf = container_of(vb, struct timblogiw_buffer,
+ vb);
+ struct dma_async_tx_descriptor *desc;
+ int sg_elems;
+ int bytes_per_desc = TIMBLOGIW_LINES_PER_DESC *
+ timblogiw_bytes_per_line(fh->cur_norm);
+
+ sg_elems = timblogiw_frame_size(fh->cur_norm) / bytes_per_desc;
+ sg_elems +=
+ (timblogiw_frame_size(fh->cur_norm) % bytes_per_desc) ? 1 : 0;
+
+ if (list_empty(&fh->capture))
+ vb->state = VIDEOBUF_ACTIVE;
+ else
+ vb->state = VIDEOBUF_QUEUED;
+
+ list_add_tail(&vb->queue, &fh->capture);
+
+ spin_unlock_irq(&fh->queue_lock);
+
+ desc = dmaengine_prep_slave_sg(fh->chan,
+ buf->sg, sg_elems, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT);
+ if (!desc) {
+ spin_lock_irq(&fh->queue_lock);
+ list_del_init(&vb->queue);
+ vb->state = VIDEOBUF_PREPARED;
+ return;
+ }
+
+ desc->callback_param = buf;
+ desc->callback = timblogiw_dma_cb;
+
+ buf->cookie = desc->tx_submit(desc);
+
+ spin_lock_irq(&fh->queue_lock);
+}
+
+static void buffer_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct timblogiw_fh *fh = vq->priv_data;
+ struct timblogiw_buffer *buf = container_of(vb, struct timblogiw_buffer,
+ vb);
+
+ videobuf_waiton(vq, vb, 0, 0);
+ if (buf->cookie >= 0)
+ dma_sync_wait(fh->chan, buf->cookie);
+
+ videobuf_dma_contig_free(vq, vb);
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static struct videobuf_queue_ops timblogiw_video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+/* Device Operations functions */
+
+static int timblogiw_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw *lw = video_get_drvdata(vdev);
+ struct timblogiw_fh *fh;
+ v4l2_std_id std;
+ dma_cap_mask_t mask;
+ int err = 0;
+
+ dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+ mutex_lock(&lw->lock);
+ if (lw->opened) {
+ err = -EBUSY;
+ goto out;
+ }
+
+ if (TIMBLOGIW_HAS_DECODER(lw) && !lw->sd_enc) {
+ struct i2c_adapter *adapt;
+
+ /* find the video decoder */
+ adapt = i2c_get_adapter(lw->pdata.i2c_adapter);
+ if (!adapt) {
+ dev_err(&vdev->dev, "No I2C bus #%d\n",
+ lw->pdata.i2c_adapter);
+ err = -ENODEV;
+ goto out;
+ }
+
+ /* now find the encoder */
+ lw->sd_enc = v4l2_i2c_new_subdev_board(&lw->v4l2_dev, adapt,
+ lw->pdata.encoder.info, NULL);
+
+ i2c_put_adapter(adapt);
+
+ if (!lw->sd_enc) {
+ dev_err(&vdev->dev, "Failed to get encoder: %s\n",
+ lw->pdata.encoder.module_name);
+ err = -ENODEV;
+ goto out;
+ }
+ }
+
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (!fh) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ fh->cur_norm = timblogiw_tvnorms;
+ timblogiw_querystd(file, fh, &std);
+ fh->cur_norm = timblogiw_get_norm(std);
+
+ INIT_LIST_HEAD(&fh->capture);
+ spin_lock_init(&fh->queue_lock);
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ dma_cap_set(DMA_PRIVATE, mask);
+
+ /* find the DMA channel */
+ fh->chan = dma_request_channel(mask, timblogiw_dma_filter_fn,
+ (void *)(uintptr_t)lw->pdata.dma_channel);
+ if (!fh->chan) {
+ dev_err(&vdev->dev, "Failed to get DMA channel\n");
+ kfree(fh);
+ err = -ENODEV;
+ goto out;
+ }
+
+ file->private_data = fh;
+ videobuf_queue_dma_contig_init(&fh->vb_vidq,
+ &timblogiw_video_qops, lw->dev, &fh->queue_lock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
+ sizeof(struct timblogiw_buffer), fh, NULL);
+
+ lw->opened = true;
+out:
+ mutex_unlock(&lw->lock);
+
+ return err;
+}
+
+static int timblogiw_close(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw *lw = video_get_drvdata(vdev);
+ struct timblogiw_fh *fh = file->private_data;
+
+ dev_dbg(&vdev->dev, "%s: Entry\n", __func__);
+
+ videobuf_stop(&fh->vb_vidq);
+ videobuf_mmap_free(&fh->vb_vidq);
+
+ dma_release_channel(fh->chan);
+
+ kfree(fh);
+
+ mutex_lock(&lw->lock);
+ lw->opened = false;
+ mutex_unlock(&lw->lock);
+ return 0;
+}
+
+static ssize_t timblogiw_read(struct file *file, char __user *data,
+ size_t count, loff_t *ppos)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw_fh *fh = file->private_data;
+
+ dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+ return videobuf_read_stream(&fh->vb_vidq, data, count, ppos, 0,
+ file->f_flags & O_NONBLOCK);
+}
+
+static unsigned int timblogiw_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw_fh *fh = file->private_data;
+
+ dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+ return videobuf_poll_stream(file, &fh->vb_vidq, wait);
+}
+
+static int timblogiw_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw_fh *fh = file->private_data;
+
+ dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+ return videobuf_mmap_mapper(&fh->vb_vidq, vma);
+}
+
+/* Platform device functions */
+
+static struct v4l2_ioctl_ops timblogiw_ioctl_ops = {
+ .vidioc_querycap = timblogiw_querycap,
+ .vidioc_enum_fmt_vid_cap = timblogiw_enum_fmt,
+ .vidioc_g_fmt_vid_cap = timblogiw_g_fmt,
+ .vidioc_try_fmt_vid_cap = timblogiw_try_fmt,
+ .vidioc_s_fmt_vid_cap = timblogiw_s_fmt,
+ .vidioc_g_parm = timblogiw_g_parm,
+ .vidioc_reqbufs = timblogiw_reqbufs,
+ .vidioc_querybuf = timblogiw_querybuf,
+ .vidioc_qbuf = timblogiw_qbuf,
+ .vidioc_dqbuf = timblogiw_dqbuf,
+ .vidioc_g_std = timblogiw_g_std,
+ .vidioc_s_std = timblogiw_s_std,
+ .vidioc_enum_input = timblogiw_enuminput,
+ .vidioc_g_input = timblogiw_g_input,
+ .vidioc_s_input = timblogiw_s_input,
+ .vidioc_streamon = timblogiw_streamon,
+ .vidioc_streamoff = timblogiw_streamoff,
+ .vidioc_querystd = timblogiw_querystd,
+ .vidioc_enum_framesizes = timblogiw_enum_framesizes,
+};
+
+static struct v4l2_file_operations timblogiw_fops = {
+ .owner = THIS_MODULE,
+ .open = timblogiw_open,
+ .release = timblogiw_close,
+ .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
+ .mmap = timblogiw_mmap,
+ .read = timblogiw_read,
+ .poll = timblogiw_poll,
+};
+
+static struct video_device timblogiw_template = {
+ .name = TIMBLOGIWIN_NAME,
+ .fops = &timblogiw_fops,
+ .ioctl_ops = &timblogiw_ioctl_ops,
+ .release = video_device_release_empty,
+ .minor = -1,
+ .tvnorms = V4L2_STD_PAL | V4L2_STD_NTSC
+};
+
+static int timblogiw_probe(struct platform_device *pdev)
+{
+ int err;
+ struct timblogiw *lw = NULL;
+ struct timb_video_platform_data *pdata = pdev->dev.platform_data;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "No platform data\n");
+ err = -EINVAL;
+ goto err;
+ }
+
+ if (!pdata->encoder.module_name)
+ dev_info(&pdev->dev, "Running without decoder\n");
+
+ lw = devm_kzalloc(&pdev->dev, sizeof(*lw), GFP_KERNEL);
+ if (!lw) {
+ err = -ENOMEM;
+ goto err;
+ }
+
+ if (pdev->dev.parent)
+ lw->dev = pdev->dev.parent;
+ else
+ lw->dev = &pdev->dev;
+
+ memcpy(&lw->pdata, pdata, sizeof(lw->pdata));
+
+ mutex_init(&lw->lock);
+
+ lw->video_dev = timblogiw_template;
+
+ strlcpy(lw->v4l2_dev.name, DRIVER_NAME, sizeof(lw->v4l2_dev.name));
+ err = v4l2_device_register(NULL, &lw->v4l2_dev);
+ if (err)
+ goto err;
+
+ lw->video_dev.v4l2_dev = &lw->v4l2_dev;
+
+ platform_set_drvdata(pdev, lw);
+ video_set_drvdata(&lw->video_dev, lw);
+
+ err = video_register_device(&lw->video_dev, VFL_TYPE_GRABBER, 0);
+ if (err) {
+ dev_err(&pdev->dev, "Error reg video: %d\n", err);
+ goto err_request;
+ }
+
+ return 0;
+
+err_request:
+ v4l2_device_unregister(&lw->v4l2_dev);
+err:
+ dev_err(&pdev->dev, "Failed to register: %d\n", err);
+
+ return err;
+}
+
+static int timblogiw_remove(struct platform_device *pdev)
+{
+ struct timblogiw *lw = platform_get_drvdata(pdev);
+
+ video_unregister_device(&lw->video_dev);
+
+ v4l2_device_unregister(&lw->v4l2_dev);
+
+ return 0;
+}
+
+static struct platform_driver timblogiw_platform_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+ .probe = timblogiw_probe,
+ .remove = timblogiw_remove,
+};
+
+module_platform_driver(timblogiw_platform_driver);
+
+MODULE_DESCRIPTION(TIMBLOGIWIN_NAME);
+MODULE_AUTHOR("Pelagicore AB <info@pelagicore.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:"DRIVER_NAME);
{ .compatible = "allwinner,sun8i-a33-musb", },
{}
};
+MODULE_DEVICE_TABLE(of, sunxi_musb_match);
static struct platform_driver sunxi_musb_driver = {
.probe = sunxi_musb_probe,
--- /dev/null
+/*
+ tvp5150.h - definition for tvp5150 inputs
+
+ Copyright (C) 2006 Hans Verkuil (hverkuil@xs4all.nl)
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _DT_BINDINGS_MEDIA_TVP5150_H
+#define _DT_BINDINGS_MEDIA_TVP5150_H
+
+/* TVP5150 HW inputs */
+#define TVP5150_COMPOSITE0 0
+#define TVP5150_COMPOSITE1 1
+#define TVP5150_SVIDEO 2
+
+#define TVP5150_INPUT_NUM 3
+
+/* TVP5150 HW outputs */
+#define TVP5150_NORMAL 0
+#define TVP5150_BLACK_SCREEN 1
+
+#endif /* _DT_BINDINGS_MEDIA_TVP5150_H */
+++ /dev/null
-/*
- tvp5150.h - definition for tvp5150 inputs
-
- Copyright (C) 2006 Hans Verkuil (hverkuil@xs4all.nl)
-
- 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.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#ifndef _TVP5150_H_
-#define _TVP5150_H_
-
-/* TVP5150 HW inputs */
-#define TVP5150_COMPOSITE0 0
-#define TVP5150_COMPOSITE1 1
-#define TVP5150_SVIDEO 2
-
-/* TVP5150 HW outputs */
-#define TVP5150_NORMAL 0
-#define TVP5150_BLACK_SCREEN 1
-
-#endif
struct ida;
struct device;
+/**
+ * struct media_entity_notify - Media Entity Notify
+ *
+ * @list: List head
+ * @notify_data: Input data to invoke the callback
+ * @notify: Callback function pointer
+ *
+ * Drivers may register a callback to take action when
+ * new entities get registered with the media device.
+ */
+struct media_entity_notify {
+ struct list_head list;
+ void *notify_data;
+ void (*notify)(struct media_entity *entity, void *notify_data);
+};
+
/**
* struct media_device - Media device
* @dev: Parent device
* @devnode: Media device node
+ * @driver_name: Optional device driver name. If not set, calls to
+ * %MEDIA_IOC_DEVICE_INFO will return dev->driver->name.
+ * This is needed for USB drivers for example, as otherwise
+ * they'll all appear as if the driver name was "usb".
* @model: Device model name
* @serial: Device serial number (optional)
* @bus_info: Unique and stable device location identifier
* @interfaces: List of registered interfaces
* @pads: List of registered pads
* @links: List of registered links
+ * @entity_notify: List of registered entity_notify callbacks
* @lock: Entities list lock
* @graph_mutex: Entities graph operation lock
+ * @pm_count_walk: Graph walk for power state walk. Access serialised using
+ * graph_mutex.
+ *
+ * @source_priv: Driver Private data for enable/disable source handlers
+ * @enable_source: Enable Source Handler function pointer
+ * @disable_source: Disable Source Handler function pointer
+ *
* @link_notify: Link state change notification callback
*
* This structure represents an abstract high-level media device. It allows easy
*
* @model is a descriptive model name exported through sysfs. It doesn't have to
* be unique.
+ *
+ * @enable_source is a handler to find source entity for the
+ * sink entity and activate the link between them if source
+ * entity is free. Drivers should call this handler before
+ * accessing the source.
+ *
+ * @disable_source is a handler to find source entity for the
+ * sink entity and deactivate the link between them. Drivers
+ * should call this handler to release the source.
+ *
+ * Note: Bridge driver is expected to implement and set the
+ * handler when media_device is registered or when
+ * bridge driver finds the media_device during probe.
+ * Bridge driver sets source_priv with information
+ * necessary to run enable/disable source handlers.
+ *
+ * Use-case: find tuner entity connected to the decoder
+ * entity and check if it is available, and activate the
+ * the link between them from enable_source and deactivate
+ * from disable_source.
*/
struct media_device {
/* dev->driver_data points to this struct. */
struct media_devnode devnode;
char model[32];
+ char driver_name[32];
char serial[40];
char bus_info[32];
u32 hw_revision;
struct list_head pads;
struct list_head links;
+ /* notify callback list invoked when a new entity is registered */
+ struct list_head entity_notify;
+
/* Protects the graph objects creation/removal */
spinlock_t lock;
/* Serializes graph operations. */
struct mutex graph_mutex;
+ struct media_entity_graph pm_count_walk;
+
+ void *source_priv;
+ int (*enable_source)(struct media_entity *entity,
+ struct media_pipeline *pipe);
+ void (*disable_source)(struct media_entity *entity);
int (*link_notify)(struct media_link *link, u32 flags,
unsigned int notification);
};
+/* We don't need to include pci.h or usb.h here */
+struct pci_dev;
+struct usb_device;
+
#ifdef CONFIG_MEDIA_CONTROLLER
/* Supported link_notify @notification values. */
*/
void media_device_unregister_entity(struct media_entity *entity);
+/**
+ * media_device_register_entity_notify() - Registers a media entity_notify
+ * callback
+ *
+ * @mdev: The media device
+ * @nptr: The media_entity_notify
+ *
+ * Note: When a new entity is registered, all the registered
+ * media_entity_notify callbacks are invoked.
+ */
+
+int __must_check media_device_register_entity_notify(struct media_device *mdev,
+ struct media_entity_notify *nptr);
+
+/**
+ * media_device_unregister_entity_notify() - Unregister a media entity notify
+ * callback
+ *
+ * @mdev: The media device
+ * @nptr: The media_entity_notify
+ *
+ */
+void media_device_unregister_entity_notify(struct media_device *mdev,
+ struct media_entity_notify *nptr);
+
/**
* media_device_get_devres() - get media device as device resource
* creates if one doesn't exist
/* Iterate over all links. */
#define media_device_for_each_link(link, mdev) \
list_for_each_entry(link, &(mdev)->links, graph_obj.list)
+
+/**
+ * media_device_pci_init() - create and initialize a
+ * struct &media_device from a PCI device.
+ *
+ * @mdev: pointer to struct &media_device
+ * @pci_dev: pointer to struct pci_dev
+ * @name: media device name. If %NULL, the routine will use the default
+ * name for the pci device, given by pci_name() macro.
+ */
+void media_device_pci_init(struct media_device *mdev,
+ struct pci_dev *pci_dev,
+ const char *name);
+/**
+ * __media_device_usb_init() - create and initialize a
+ * struct &media_device from a PCI device.
+ *
+ * @mdev: pointer to struct &media_device
+ * @udev: pointer to struct usb_device
+ * @board_name: media device name. If %NULL, the routine will use the usb
+ * product name, if available.
+ * @driver_name: name of the driver. if %NULL, the routine will use the name
+ * given by udev->dev->driver->name, with is usually the wrong
+ * thing to do.
+ *
+ * NOTE: It is better to call media_device_usb_init() instead, as
+ * such macro fills driver_name with %KBUILD_MODNAME.
+ */
+void __media_device_usb_init(struct media_device *mdev,
+ struct usb_device *udev,
+ const char *board_name,
+ const char *driver_name);
+
#else
static inline int media_device_register(struct media_device *mdev)
{
static inline void media_device_unregister_entity(struct media_entity *entity)
{
}
+static inline int media_device_register_entity_notify(
+ struct media_device *mdev,
+ struct media_entity_notify *nptr)
+{
+ return 0;
+}
+static inline void media_device_unregister_entity_notify(
+ struct media_device *mdev,
+ struct media_entity_notify *nptr)
+{
+}
static inline struct media_device *media_device_get_devres(struct device *dev)
{
return NULL;
{
return NULL;
}
+
+static inline void media_device_pci_init(struct media_device *mdev,
+ struct pci_dev *pci_dev,
+ char *name)
+{
+}
+
+static inline void __media_device_usb_init(struct media_device *mdev,
+ struct usb_device *udev,
+ char *board_name,
+ char *driver_name)
+{
+}
+
#endif /* CONFIG_MEDIA_CONTROLLER */
+
+#define media_device_usb_init(mdev, udev, name) \
+ __media_device_usb_init(mdev, udev, name, KBUILD_MODNAME)
+
#endif
#define _MEDIA_ENTITY_H
#include <linux/bitmap.h>
+#include <linux/bug.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/media.h>
*/
__must_check int media_entity_pipeline_start(struct media_entity *entity,
struct media_pipeline *pipe);
+/**
+ * __media_entity_pipeline_start - Mark a pipeline as streaming
+ *
+ * @entity: Starting entity
+ * @pipe: Media pipeline to be assigned to all entities in the pipeline.
+ *
+ * Note: This is the non-locking version of media_entity_pipeline_start()
+ */
+__must_check int __media_entity_pipeline_start(struct media_entity *entity,
+ struct media_pipeline *pipe);
/**
* media_entity_pipeline_stop - Mark a pipeline as not streaming
*/
void media_entity_pipeline_stop(struct media_entity *entity);
+/**
+ * __media_entity_pipeline_stop - Mark a pipeline as not streaming
+ *
+ * @entity: Starting entity
+ *
+ * Note: This is the non-locking version of media_entity_pipeline_stop()
+ */
+void __media_entity_pipeline_stop(struct media_entity *entity);
+
/**
* media_devnode_create() - creates and initializes a device node interface
*
/**
* struct rc_dev - represents a remote control device
* @dev: driver model's view of this device
+ * @initialized: 1 if the device init has completed, 0 otherwise
* @sysfs_groups: sysfs attribute groups
* @input_name: name of the input child device
* @input_phys: physical path to the input child device
*/
struct rc_dev {
struct device dev;
+ atomic_t initialized;
const struct attribute_group *sysfs_groups[5];
const char *input_name;
const char *input_phys;
#ifdef __KERNEL__
#include <linux/videodev2.h>
-
-/* Tuner PADs */
-/* FIXME: is this the right place for it? */
-enum tuner_pad_index {
- TUNER_PAD_RF_INPUT,
- TUNER_PAD_IF_OUTPUT,
- TUNER_NUM_PADS
-};
+#include <media/v4l2-mc.h>
#define ADDR_UNSET (255)
const struct v4l2_ctrl_ops *ops,
u32 id, u8 max, u8 def, const s64 *qmenu_int);
-/**
- * v4l2_ctrl_add_ctrl() - Add a control from another handler to this handler.
- * @hdl: The control handler.
- * @ctrl: The control to add.
- *
- * It will return NULL if it was unable to add the control reference.
- * If the control already belonged to the handler, then it will do
- * nothing and just return @ctrl.
- */
-struct v4l2_ctrl *v4l2_ctrl_add_ctrl(struct v4l2_ctrl_handler *hdl,
- struct v4l2_ctrl *ctrl);
-
/**
* v4l2_ctrl_add_handler() - Add all controls from handler @add to
* handler @hdl.
#if defined(CONFIG_MEDIA_CONTROLLER)
struct media_entity entity;
struct media_intf_devnode *intf_devnode;
+ struct media_pipeline pipe;
#endif
/* device ops */
const struct v4l2_file_operations *fops;
--- /dev/null
+/*
+ * v4l2-mc.h - Media Controller V4L2 types and prototypes
+ *
+ * Copyright (C) 2016 Mauro Carvalho Chehab <mchehab@osg.samsung.com>
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (c) 2016 Intel Corporation.
+ *
+ * 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 _V4L2_MC_H
+#define _V4L2_MC_H
+
+#include <media/media-device.h>
+#include <media/v4l2-dev.h>
+#include <linux/types.h>
+
+/**
+ * enum tuner_pad_index - tuner pad index for MEDIA_ENT_F_TUNER
+ *
+ * @TUNER_PAD_RF_INPUT: Radiofrequency (RF) sink pad, usually linked to a
+ * RF connector entity.
+ * @TUNER_PAD_OUTPUT: Tuner video output source pad. Contains the video
+ * chrominance and luminance or the hole bandwidth
+ * of the signal converted to an Intermediate Frequency
+ * (IF) or to baseband (on zero-IF tuners).
+ * @TUNER_PAD_AUD_OUT: Tuner audio output source pad. Tuners used to decode
+ * analog TV signals have an extra pad for audio output.
+ * Old tuners use an analog stage with a saw filter for
+ * the audio IF frequency. The output of the pad is, in
+ * this case, the audio IF, with should be decoded either
+ * by the bridge chipset (that's the case of cx2388x
+ * chipsets) or may require an external IF sound
+ * processor, like msp34xx. On modern silicon tuners,
+ * the audio IF decoder is usually incorporated at the
+ * tuner. On such case, the output of this pad is an
+ * audio sampled data.
+ * @TUNER_NUM_PADS: Number of pads of the tuner.
+ */
+enum tuner_pad_index {
+ TUNER_PAD_RF_INPUT,
+ TUNER_PAD_OUTPUT,
+ TUNER_PAD_AUD_OUT,
+ TUNER_NUM_PADS
+};
+
+/**
+ * enum if_vid_dec_index - video IF-PLL pad index for
+ * MEDIA_ENT_F_IF_VID_DECODER
+ *
+ * @IF_VID_DEC_PAD_IF_INPUT: video Intermediate Frequency (IF) sink pad
+ * @IF_VID_DEC_PAD_OUT: IF-PLL video output source pad. Contains the
+ * video chrominance and luminance IF signals.
+ * @IF_VID_DEC_PAD_NUM_PADS: Number of pads of the video IF-PLL.
+ */
+enum if_vid_dec_pad_index {
+ IF_VID_DEC_PAD_IF_INPUT,
+ IF_VID_DEC_PAD_OUT,
+ IF_VID_DEC_PAD_NUM_PADS
+};
+
+/**
+ * enum if_aud_dec_index - audio/sound IF-PLL pad index for
+ * MEDIA_ENT_F_IF_AUD_DECODER
+ *
+ * @IF_AUD_DEC_PAD_IF_INPUT: audio Intermediate Frequency (IF) sink pad
+ * @IF_AUD_DEC_PAD_OUT: IF-PLL audio output source pad. Contains the
+ * audio sampled stream data, usually connected
+ * to the bridge bus via an Inter-IC Sound (I2S)
+ * bus.
+ * @IF_AUD_DEC_PAD_NUM_PADS: Number of pads of the audio IF-PLL.
+ */
+enum if_aud_dec_pad_index {
+ IF_AUD_DEC_PAD_IF_INPUT,
+ IF_AUD_DEC_PAD_OUT,
+ IF_AUD_DEC_PAD_NUM_PADS
+};
+
+/**
+ * enum demod_pad_index - analog TV pad index for MEDIA_ENT_F_ATV_DECODER
+ *
+ * @DEMOD_PAD_IF_INPUT: IF input sink pad.
+ * @DEMOD_PAD_VID_OUT: Video output source pad.
+ * @DEMOD_PAD_VBI_OUT: Vertical Blank Interface (VBI) output source pad.
+ * @DEMOD_PAD_AUDIO_OUT: Audio output source pad.
+ * @DEMOD_NUM_PADS: Maximum number of output pads.
+ */
+enum demod_pad_index {
+ DEMOD_PAD_IF_INPUT,
+ DEMOD_PAD_VID_OUT,
+ DEMOD_PAD_VBI_OUT,
+ DEMOD_PAD_AUDIO_OUT,
+ DEMOD_NUM_PADS
+};
+
+/* We don't need to include pci.h or usb.h here */
+struct pci_dev;
+struct usb_device;
+
+#ifdef CONFIG_MEDIA_CONTROLLER
+/**
+ * v4l2_mc_create_media_graph() - create Media Controller links at the graph.
+ *
+ * @mdev: pointer to the &media_device struct.
+ *
+ * Add links between the entities commonly found on PC customer's hardware at
+ * the V4L2 side: camera sensors, audio and video PLL-IF decoders, tuners,
+ * analog TV decoder and I/O entities (video, VBI and Software Defined Radio).
+ * NOTE: webcams are modelled on a very simple way: the sensor is
+ * connected directly to the I/O entity. All dirty details, like
+ * scaler and crop HW are hidden. While such mapping is enough for v4l2
+ * interface centric PC-consumer's hardware, V4L2 subdev centric camera
+ * hardware should not use this routine, as it will not build the right graph.
+ */
+int v4l2_mc_create_media_graph(struct media_device *mdev);
+
+/**
+ * v4l_enable_media_source() - Hold media source for exclusive use
+ * if free
+ *
+ * @vdev: pointer to struct video_device
+ *
+ * This interface calls enable_source handler to determine if
+ * media source is free for use. The enable_source handler is
+ * responsible for checking is the media source is free and
+ * start a pipeline between the media source and the media
+ * entity associated with the video device. This interface
+ * should be called from v4l2-core and dvb-core interfaces
+ * that change the source configuration.
+ *
+ * Return: returns zero on success or a negative error code.
+ */
+int v4l_enable_media_source(struct video_device *vdev);
+
+/**
+ * v4l_disable_media_source() - Release media source
+ *
+ * @vdev: pointer to struct video_device
+ *
+ * This interface calls disable_source handler to release
+ * the media source. The disable_source handler stops the
+ * active media pipeline between the media source and the
+ * media entity associated with the video device.
+ *
+ * Return: returns zero on success or a negative error code.
+ */
+void v4l_disable_media_source(struct video_device *vdev);
+
+/*
+ * v4l_vb2q_enable_media_tuner - Hold media source for exclusive use
+ * if free.
+ * @q - pointer to struct vb2_queue
+ *
+ * Wrapper for v4l_enable_media_source(). This function should
+ * be called from v4l2-core to enable the media source with
+ * pointer to struct vb2_queue as the input argument. Some
+ * v4l2-core interfaces don't have access to video device and
+ * this interface finds the struct video_device for the q and
+ * calls v4l_enable_media_source().
+ */
+int v4l_vb2q_enable_media_source(struct vb2_queue *q);
+
+
+/**
+ * v4l2_pipeline_pm_use - Update the use count of an entity
+ * @entity: The entity
+ * @use: Use (1) or stop using (0) the entity
+ *
+ * Update the use count of all entities in the pipeline and power entities on or
+ * off accordingly.
+ *
+ * This function is intended to be called in video node open (use ==
+ * 1) and release (use == 0). It uses struct media_entity.use_count to
+ * track the power status. The use of this function should be paired
+ * with v4l2_pipeline_link_notify().
+ *
+ * Return 0 on success or a negative error code on failure. Powering entities
+ * off is assumed to never fail. No failure can occur when the use parameter is
+ * set to 0.
+ */
+int v4l2_pipeline_pm_use(struct media_entity *entity, int use);
+
+
+/**
+ * v4l2_pipeline_link_notify - Link management notification callback
+ * @link: The link
+ * @flags: New link flags that will be applied
+ * @notification: The link's state change notification type (MEDIA_DEV_NOTIFY_*)
+ *
+ * React to link management on powered pipelines by updating the use count of
+ * all entities in the source and sink sides of the link. Entities are powered
+ * on or off accordingly. The use of this function should be paired
+ * with v4l2_pipeline_pm_use().
+ *
+ * Return 0 on success or a negative error code on failure. Powering entities
+ * off is assumed to never fail. This function will not fail for disconnection
+ * events.
+ */
+int v4l2_pipeline_link_notify(struct media_link *link, u32 flags,
+ unsigned int notification);
+
+#else /* CONFIG_MEDIA_CONTROLLER */
+
+static inline int v4l2_mc_create_media_graph(struct media_device *mdev)
+{
+ return 0;
+}
+
+static inline int v4l_enable_media_source(struct video_device *vdev)
+{
+ return 0;
+}
+
+static inline void v4l_disable_media_source(struct video_device *vdev)
+{
+}
+
+static inline int v4l_vb2q_enable_media_source(struct vb2_queue *q)
+{
+ return 0;
+}
+
+static inline int v4l2_pipeline_pm_use(struct media_entity *entity, int use)
+{
+ return 0;
+}
+
+static inline int v4l2_pipeline_link_notify(struct media_link *link, u32 flags,
+ unsigned int notification)
+{
+ return 0;
+}
+
+#endif /* CONFIG_MEDIA_CONTROLLER */
+#endif /* _V4L2_MC_H */
* for it to be warned when the value of a control changes.
*
* @unsubscribe_event: remove event subscription from the control framework.
+ *
+ * @registered_async: the subdevice has been registered async.
*/
struct v4l2_subdev_core_ops {
int (*log_status)(struct v4l2_subdev *sd);
struct v4l2_event_subscription *sub);
int (*unsubscribe_event)(struct v4l2_subdev *sd, struct v4l2_fh *fh,
struct v4l2_event_subscription *sub);
+ int (*registered_async)(struct v4l2_subdev *sd);
};
/**
#include <dvb_frontend.h>
#include <media/videobuf2-v4l2.h>
+
+/* We don't actually need to include media-device.h here */
+struct media_device;
+
/*
* TODO: This header file should be replaced with videobuf2-core.h
* Currently, vb2_thread is not a stuff of videobuf2-core,
struct module *module,
void *adapter_priv,
struct device *device,
+ struct media_device *mdev,
short *adapter_nr,
int mfe_shared);
--- /dev/null
+/*
+ * vsp1.h -- R-Car VSP1 API
+ *
+ * Copyright (C) 2015 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef __MEDIA_VSP1_H__
+#define __MEDIA_VSP1_H__
+
+#include <linux/types.h>
+
+struct device;
+struct v4l2_rect;
+
+int vsp1_du_init(struct device *dev);
+
+int vsp1_du_setup_lif(struct device *dev, unsigned int width,
+ unsigned int height);
+
+int vsp1_du_atomic_begin(struct device *dev);
+int vsp1_du_atomic_update(struct device *dev, unsigned int rpf, u32 pixelformat,
+ unsigned int pitch, dma_addr_t mem[2],
+ const struct v4l2_rect *src,
+ const struct v4l2_rect *dst);
+int vsp1_du_atomic_flush(struct device *dev);
+
+#endif /* __MEDIA_VSP1_H__ */
#define MEDIA_ENT_F_IO_VBI (MEDIA_ENT_F_BASE + 0x01002)
#define MEDIA_ENT_F_IO_SWRADIO (MEDIA_ENT_F_BASE + 0x01003)
+/*
+ * Analog TV IF-PLL decoders
+ *
+ * It is a responsibility of the master/bridge drivers to create links
+ * for MEDIA_ENT_F_IF_VID_DECODER and MEDIA_ENT_F_IF_AUD_DECODER.
+ */
+#define MEDIA_ENT_F_IF_VID_DECODER (MEDIA_ENT_F_BASE + 0x02001)
+#define MEDIA_ENT_F_IF_AUD_DECODER (MEDIA_ENT_F_BASE + 0x02002)
+
+/*
+ * Audio Entity Functions
+ */
+#define MEDIA_ENT_F_AUDIO_CAPTURE (MEDIA_ENT_F_BASE + 0x03001)
+#define MEDIA_ENT_F_AUDIO_PLAYBACK (MEDIA_ENT_F_BASE + 0x03002)
+#define MEDIA_ENT_F_AUDIO_MIXER (MEDIA_ENT_F_BASE + 0x03003)
+
/*
* Connectors
*/
#define MEDIA_ENT_F_LENS (MEDIA_ENT_F_OLD_SUBDEV_BASE + 3)
#define MEDIA_ENT_F_ATV_DECODER (MEDIA_ENT_F_OLD_SUBDEV_BASE + 4)
/*
- * It is a responsibility of the entity drivers to add connectors and links
- * for the tuner entities.
+ * It is a responsibility of the master/bridge drivers to add connectors
+ * and links for MEDIA_ENT_F_TUNER. Please notice that some old tuners
+ * may require the usage of separate I2C chips to decode analog TV signals,
+ * when the master/bridge chipset doesn't have its own TV standard decoder.
+ * On such cases, the IF-PLL staging is mapped via one or two entities:
+ * MEDIA_ENT_F_IF_VID_DECODER and/or MEDIA_ENT_F_IF_AUD_DECODER.
*/
#define MEDIA_ENT_F_TUNER (MEDIA_ENT_F_OLD_SUBDEV_BASE + 5)
#define MEDIA_INTF_T_DVB_BASE 0x00000100
#define MEDIA_INTF_T_V4L_BASE 0x00000200
+#define MEDIA_INTF_T_ALSA_BASE 0x00000300
/* Interface types */
#define MEDIA_INTF_T_V4L_SUBDEV (MEDIA_INTF_T_V4L_BASE + 3)
#define MEDIA_INTF_T_V4L_SWRADIO (MEDIA_INTF_T_V4L_BASE + 4)
+#define MEDIA_INTF_T_ALSA_PCM_CAPTURE (MEDIA_INTF_T_ALSA_BASE)
+#define MEDIA_INTF_T_ALSA_PCM_PLAYBACK (MEDIA_INTF_T_ALSA_BASE + 1)
+#define MEDIA_INTF_T_ALSA_CONTROL (MEDIA_INTF_T_ALSA_BASE + 2)
+#define MEDIA_INTF_T_ALSA_COMPRESS (MEDIA_INTF_T_ALSA_BASE + 3)
+#define MEDIA_INTF_T_ALSA_RAWMIDI (MEDIA_INTF_T_ALSA_BASE + 4)
+#define MEDIA_INTF_T_ALSA_HWDEP (MEDIA_INTF_T_ALSA_BASE + 5)
+#define MEDIA_INTF_T_ALSA_SEQUENCER (MEDIA_INTF_T_ALSA_BASE + 6)
+#define MEDIA_INTF_T_ALSA_TIMER (MEDIA_INTF_T_ALSA_BASE + 7)
+
/*
* MC next gen API definitions
*
* later, before the adding this API upstream.
*/
-#if 0 /* Let's postpone it to Kernel 4.6 */
+
struct media_v2_entity {
__u32 id;
char name[64]; /* FIXME: move to a property? (RFC says so) */
__u64 ptr_links;
} __attribute__ ((packed));
-static inline void __user *media_get_uptr(__u64 arg)
-{
- return (void __user *)(uintptr_t)arg;
-}
-#endif
-
/* ioctls */
#define MEDIA_IOC_DEVICE_INFO _IOWR('|', 0x00, struct media_device_info)
#define MEDIA_IOC_ENUM_ENTITIES _IOWR('|', 0x01, struct media_entity_desc)
#define MEDIA_IOC_ENUM_LINKS _IOWR('|', 0x02, struct media_links_enum)
#define MEDIA_IOC_SETUP_LINK _IOWR('|', 0x03, struct media_link_desc)
-
-#if 0 /* Let's postpone it to Kernel 4.6 */
#define MEDIA_IOC_G_TOPOLOGY _IOWR('|', 0x04, struct media_v2_topology)
-#endif
#endif /* __LINUX_MEDIA_H */
* Copyright (C) 2012 Nokia Corporation
* Contact: Sakari Ailus <sakari.ailus@iki.fi>
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
+ * 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.
+ * 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.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
+ * Alternatively you can redistribute this file under the terms of the
+ * BSD license as stated below:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. The names of its contributors may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#define V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER (V4L2_CID_MPEG_BASE+226)
#define V4L2_CID_MPEG_VIDEO_MV_H_SEARCH_RANGE (V4L2_CID_MPEG_BASE+227)
#define V4L2_CID_MPEG_VIDEO_MV_V_SEARCH_RANGE (V4L2_CID_MPEG_BASE+228)
+#define V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME (V4L2_CID_MPEG_BASE+229)
#define V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP (V4L2_CID_MPEG_BASE+300)
#define V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP (V4L2_CID_MPEG_BASE+301)
V4L2_DV_RGB_RANGE_FULL = 2,
};
+#define V4L2_CID_DV_TX_IT_CONTENT_TYPE (V4L2_CID_DV_CLASS_BASE + 6)
+enum v4l2_dv_it_content_type {
+ V4L2_DV_IT_CONTENT_TYPE_GRAPHICS = 0,
+ V4L2_DV_IT_CONTENT_TYPE_PHOTO = 1,
+ V4L2_DV_IT_CONTENT_TYPE_CINEMA = 2,
+ V4L2_DV_IT_CONTENT_TYPE_GAME = 3,
+ V4L2_DV_IT_CONTENT_TYPE_NO_ITC = 4,
+};
+
#define V4L2_CID_DV_RX_POWER_PRESENT (V4L2_CID_DV_CLASS_BASE + 100)
#define V4L2_CID_DV_RX_RGB_RANGE (V4L2_CID_DV_CLASS_BASE + 101)
+#define V4L2_CID_DV_RX_IT_CONTENT_TYPE (V4L2_CID_DV_CLASS_BASE + 102)
#define V4L2_CID_FM_RX_CLASS_BASE (V4L2_CTRL_CLASS_FM_RX | 0x900)
#define V4L2_CID_FM_RX_CLASS (V4L2_CTRL_CLASS_FM_RX | 1)
/* three non contiguous planes - Y, Cb, Cr */
#define V4L2_PIX_FMT_YUV420M v4l2_fourcc('Y', 'M', '1', '2') /* 12 YUV420 planar */
#define V4L2_PIX_FMT_YVU420M v4l2_fourcc('Y', 'M', '2', '1') /* 12 YVU420 planar */
+#define V4L2_PIX_FMT_YUV422M v4l2_fourcc('Y', 'M', '1', '6') /* 16 YUV422 planar */
+#define V4L2_PIX_FMT_YVU422M v4l2_fourcc('Y', 'M', '6', '1') /* 16 YVU422 planar */
+#define V4L2_PIX_FMT_YUV444M v4l2_fourcc('Y', 'M', '2', '4') /* 24 YUV444 planar */
+#define V4L2_PIX_FMT_YVU444M v4l2_fourcc('Y', 'M', '4', '2') /* 24 YVU444 planar */
/* Bayer formats - see http://www.siliconimaging.com/RGB%20Bayer.htm */
#define V4L2_PIX_FMT_SBGGR8 v4l2_fourcc('B', 'A', '8', '1') /* 8 BGBG.. GRGR.. */
#define V4L2_PIX_FMT_JPGL v4l2_fourcc('J', 'P', 'G', 'L') /* JPEG-Lite */
#define V4L2_PIX_FMT_SE401 v4l2_fourcc('S', '4', '0', '1') /* se401 janggu compressed rgb */
#define V4L2_PIX_FMT_S5C_UYVY_JPG v4l2_fourcc('S', '5', 'C', 'I') /* S5C73M3 interleaved UYVY/JPEG */
+#define V4L2_PIX_FMT_Y8I v4l2_fourcc('Y', '8', 'I', ' ') /* Greyscale 8-bit L/R interleaved */
+#define V4L2_PIX_FMT_Y12I v4l2_fourcc('Y', '1', '2', 'I') /* Greyscale 12-bit L/R interleaved */
+#define V4L2_PIX_FMT_Z16 v4l2_fourcc('Z', '1', '6', ' ') /* Depth data 16-bit */
/* SDR formats - used only for Software Defined Radio devices */
#define V4L2_SDR_FMT_CU8 v4l2_fourcc('C', 'U', '0', '8') /* IQ u8 */
select SND_RAWMIDI
select SND_PCM
select BITREVERSE
+ select SND_USB_AUDIO_USE_MEDIA_CONTROLLER if MEDIA_CONTROLLER && (MEDIA_SUPPORT=y || MEDIA_SUPPORT=SND_USB_AUDIO)
help
Say Y here to include support for USB audio and USB MIDI
devices.
To compile this driver as a module, choose M here: the module
will be called snd-usb-audio.
+config SND_USB_AUDIO_USE_MEDIA_CONTROLLER
+ bool
+
config SND_USB_UA101
tristate "Edirol UA-101/UA-1000 driver"
select SND_PCM
quirks.o \
stream.o
+snd-usb-audio-$(CONFIG_SND_USB_AUDIO_USE_MEDIA_CONTROLLER) += media.o
+
snd-usbmidi-lib-objs := midi.o
# Toplevel Module Dependency
#include "format.h"
#include "power.h"
#include "stream.h"
+#include "media.h"
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
MODULE_DESCRIPTION("USB Audio");
if (err < 0)
goto __error;
+ if (quirk->media_device) {
+ /* don't want to fail when media_snd_device_create() fails */
+ media_snd_device_create(chip, intf);
+ }
+
usb_chip[chip->index] = chip;
chip->num_interfaces++;
usb_set_intfdata(intf, chip);
list_for_each(p, &chip->midi_list) {
snd_usbmidi_disconnect(p);
}
+ /*
+ * Nice to check quirk && quirk->media_device
+ * need some special handlings. Doesn't look like
+ * we have access to quirk here
+ * Acceses mixer_list
+ */
+ media_snd_device_delete(chip);
+
/* release mixer resources */
list_for_each_entry(mixer, &chip->mixer_list, list) {
snd_usb_mixer_disconnect(mixer);
struct list_head list;
};
+struct media_ctl;
+
struct snd_usb_substream {
struct snd_usb_stream *stream;
struct usb_device *dev;
} dsd_dop;
bool trigger_tstamp_pending_update; /* trigger timestamp being updated from initial estimate */
+ struct media_ctl *media_ctl;
};
struct snd_usb_stream {
--- /dev/null
+/*
+ * media.c - Media Controller specific ALSA driver code
+ *
+ * Copyright (c) 2016 Shuah Khan <shuahkh@osg.samsung.com>
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *
+ * This file is released under the GPLv2.
+ */
+
+/*
+ * This file adds Media Controller support to ALSA driver
+ * to use the Media Controller API to share tuner with DVB
+ * and V4L2 drivers that control media device. Media device
+ * is created based on existing quirks framework. Using this
+ * approach, the media controller API usage can be added for
+ * a specific device.
+*/
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+
+#include <sound/pcm.h>
+#include <sound/core.h>
+
+#include "usbaudio.h"
+#include "card.h"
+#include "mixer.h"
+#include "media.h"
+
+static int media_snd_enable_source(struct media_ctl *mctl)
+{
+ if (mctl && mctl->media_dev->enable_source)
+ return mctl->media_dev->enable_source(&mctl->media_entity,
+ &mctl->media_pipe);
+ return 0;
+}
+
+static void media_snd_disable_source(struct media_ctl *mctl)
+{
+ if (mctl && mctl->media_dev->disable_source)
+ mctl->media_dev->disable_source(&mctl->media_entity);
+}
+
+int media_snd_stream_init(struct snd_usb_substream *subs, struct snd_pcm *pcm,
+ int stream)
+{
+ struct media_device *mdev;
+ struct media_ctl *mctl;
+ struct device *pcm_dev = &pcm->streams[stream].dev;
+ u32 intf_type;
+ int ret = 0;
+ u16 mixer_pad;
+ struct media_entity *entity;
+
+ mdev = subs->stream->chip->media_dev;
+ if (!mdev)
+ return -ENODEV;
+
+ if (subs->media_ctl)
+ return 0;
+
+ /* allocate media_ctl */
+ mctl = kzalloc(sizeof(*mctl), GFP_KERNEL);
+ if (!mctl)
+ return -ENOMEM;
+
+ mctl->media_dev = mdev;
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ intf_type = MEDIA_INTF_T_ALSA_PCM_PLAYBACK;
+ mctl->media_entity.function = MEDIA_ENT_F_AUDIO_PLAYBACK;
+ mctl->media_pad.flags = MEDIA_PAD_FL_SOURCE;
+ mixer_pad = 1;
+ } else {
+ intf_type = MEDIA_INTF_T_ALSA_PCM_CAPTURE;
+ mctl->media_entity.function = MEDIA_ENT_F_AUDIO_CAPTURE;
+ mctl->media_pad.flags = MEDIA_PAD_FL_SINK;
+ mixer_pad = 2;
+ }
+ mctl->media_entity.name = pcm->name;
+ media_entity_pads_init(&mctl->media_entity, 1, &mctl->media_pad);
+ ret = media_device_register_entity(mctl->media_dev,
+ &mctl->media_entity);
+ if (ret)
+ goto free_mctl;
+
+ mctl->intf_devnode = media_devnode_create(mdev, intf_type, 0,
+ MAJOR(pcm_dev->devt),
+ MINOR(pcm_dev->devt));
+ if (!mctl->intf_devnode) {
+ ret = -ENOMEM;
+ goto unregister_entity;
+ }
+ mctl->intf_link = media_create_intf_link(&mctl->media_entity,
+ &mctl->intf_devnode->intf,
+ MEDIA_LNK_FL_ENABLED);
+ if (!mctl->intf_link) {
+ ret = -ENOMEM;
+ goto devnode_remove;
+ }
+
+ /* create link between mixer and audio */
+ media_device_for_each_entity(entity, mdev) {
+ switch (entity->function) {
+ case MEDIA_ENT_F_AUDIO_MIXER:
+ ret = media_create_pad_link(entity, mixer_pad,
+ &mctl->media_entity, 0,
+ MEDIA_LNK_FL_ENABLED);
+ if (ret)
+ goto remove_intf_link;
+ break;
+ }
+ }
+
+ subs->media_ctl = mctl;
+ return 0;
+
+remove_intf_link:
+ media_remove_intf_link(mctl->intf_link);
+devnode_remove:
+ media_devnode_remove(mctl->intf_devnode);
+unregister_entity:
+ media_device_unregister_entity(&mctl->media_entity);
+free_mctl:
+ kfree(mctl);
+ return ret;
+}
+
+void media_snd_stream_delete(struct snd_usb_substream *subs)
+{
+ struct media_ctl *mctl = subs->media_ctl;
+
+ if (mctl && mctl->media_dev) {
+ struct media_device *mdev;
+
+ mdev = subs->stream->chip->media_dev;
+ if (mdev && media_devnode_is_registered(&mdev->devnode)) {
+ media_devnode_remove(mctl->intf_devnode);
+ media_device_unregister_entity(&mctl->media_entity);
+ media_entity_cleanup(&mctl->media_entity);
+ }
+ kfree(mctl);
+ subs->media_ctl = NULL;
+ }
+}
+
+int media_snd_start_pipeline(struct snd_usb_substream *subs)
+{
+ struct media_ctl *mctl = subs->media_ctl;
+
+ if (mctl)
+ return media_snd_enable_source(mctl);
+ return 0;
+}
+
+void media_snd_stop_pipeline(struct snd_usb_substream *subs)
+{
+ struct media_ctl *mctl = subs->media_ctl;
+
+ if (mctl)
+ media_snd_disable_source(mctl);
+}
+
+int media_snd_mixer_init(struct snd_usb_audio *chip)
+{
+ struct device *ctl_dev = &chip->card->ctl_dev;
+ struct media_intf_devnode *ctl_intf;
+ struct usb_mixer_interface *mixer;
+ struct media_device *mdev = chip->media_dev;
+ struct media_mixer_ctl *mctl;
+ u32 intf_type = MEDIA_INTF_T_ALSA_CONTROL;
+ int ret;
+
+ if (!mdev)
+ return -ENODEV;
+
+ ctl_intf = chip->ctl_intf_media_devnode;
+ if (!ctl_intf) {
+ ctl_intf = media_devnode_create(mdev, intf_type, 0,
+ MAJOR(ctl_dev->devt),
+ MINOR(ctl_dev->devt));
+ if (!ctl_intf)
+ return -ENOMEM;
+ chip->ctl_intf_media_devnode = ctl_intf;
+ }
+
+ list_for_each_entry(mixer, &chip->mixer_list, list) {
+
+ if (mixer->media_mixer_ctl)
+ continue;
+
+ /* allocate media_mixer_ctl */
+ mctl = kzalloc(sizeof(*mctl), GFP_KERNEL);
+ if (!mctl)
+ return -ENOMEM;
+
+ mctl->media_dev = mdev;
+ mctl->media_entity.function = MEDIA_ENT_F_AUDIO_MIXER;
+ mctl->media_entity.name = chip->card->mixername;
+ mctl->media_pad[0].flags = MEDIA_PAD_FL_SINK;
+ mctl->media_pad[1].flags = MEDIA_PAD_FL_SOURCE;
+ mctl->media_pad[2].flags = MEDIA_PAD_FL_SOURCE;
+ media_entity_pads_init(&mctl->media_entity, MEDIA_MIXER_PAD_MAX,
+ mctl->media_pad);
+ ret = media_device_register_entity(mctl->media_dev,
+ &mctl->media_entity);
+ if (ret) {
+ kfree(mctl);
+ return ret;
+ }
+
+ mctl->intf_link = media_create_intf_link(&mctl->media_entity,
+ &ctl_intf->intf,
+ MEDIA_LNK_FL_ENABLED);
+ if (!mctl->intf_link) {
+ media_device_unregister_entity(&mctl->media_entity);
+ media_entity_cleanup(&mctl->media_entity);
+ kfree(mctl);
+ return -ENOMEM;
+ }
+ mctl->intf_devnode = ctl_intf;
+ mixer->media_mixer_ctl = mctl;
+ }
+ return 0;
+}
+
+static void media_snd_mixer_delete(struct snd_usb_audio *chip)
+{
+ struct usb_mixer_interface *mixer;
+ struct media_device *mdev = chip->media_dev;
+
+ if (!mdev)
+ return;
+
+ list_for_each_entry(mixer, &chip->mixer_list, list) {
+ struct media_mixer_ctl *mctl;
+
+ mctl = mixer->media_mixer_ctl;
+ if (!mixer->media_mixer_ctl)
+ continue;
+
+ if (media_devnode_is_registered(&mdev->devnode)) {
+ media_device_unregister_entity(&mctl->media_entity);
+ media_entity_cleanup(&mctl->media_entity);
+ }
+ kfree(mctl);
+ mixer->media_mixer_ctl = NULL;
+ }
+ if (media_devnode_is_registered(&mdev->devnode))
+ media_devnode_remove(chip->ctl_intf_media_devnode);
+ chip->ctl_intf_media_devnode = NULL;
+}
+
+int media_snd_device_create(struct snd_usb_audio *chip,
+ struct usb_interface *iface)
+{
+ struct media_device *mdev;
+ struct usb_device *usbdev = interface_to_usbdev(iface);
+ int ret;
+
+ mdev = media_device_get_devres(&usbdev->dev);
+ if (!mdev)
+ return -ENOMEM;
+ if (!mdev->dev) {
+ /* register media device */
+ mdev->dev = &usbdev->dev;
+ if (usbdev->product)
+ strlcpy(mdev->model, usbdev->product,
+ sizeof(mdev->model));
+ if (usbdev->serial)
+ strlcpy(mdev->serial, usbdev->serial,
+ sizeof(mdev->serial));
+ strcpy(mdev->bus_info, usbdev->devpath);
+ mdev->hw_revision = le16_to_cpu(usbdev->descriptor.bcdDevice);
+ media_device_init(mdev);
+ }
+ if (!media_devnode_is_registered(&mdev->devnode)) {
+ ret = media_device_register(mdev);
+ if (ret) {
+ dev_err(&usbdev->dev,
+ "Couldn't register media device. Error: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ /* save media device - avoid lookups */
+ chip->media_dev = mdev;
+
+ /* Create media entities for mixer and control dev */
+ ret = media_snd_mixer_init(chip);
+ if (ret) {
+ dev_err(&usbdev->dev,
+ "Couldn't create media mixer entities. Error: %d\n",
+ ret);
+
+ /* clear saved media_dev */
+ chip->media_dev = NULL;
+
+ return ret;
+ }
+ return 0;
+}
+
+void media_snd_device_delete(struct snd_usb_audio *chip)
+{
+ struct media_device *mdev = chip->media_dev;
+
+ media_snd_mixer_delete(chip);
+
+ if (mdev) {
+ if (media_devnode_is_registered(&mdev->devnode))
+ media_device_unregister(mdev);
+ chip->media_dev = NULL;
+ }
+}
--- /dev/null
+/*
+ * media.h - Media Controller specific ALSA driver code
+ *
+ * Copyright (c) 2016 Shuah Khan <shuahkh@osg.samsung.com>
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *
+ * This file is released under the GPLv2.
+ */
+
+/*
+ * This file adds Media Controller support to ALSA driver
+ * to use the Media Controller API to share tuner with DVB
+ * and V4L2 drivers that control media device. Media device
+ * is created based on existing quirks framework. Using this
+ * approach, the media controller API usage can be added for
+ * a specific device.
+*/
+#ifndef __MEDIA_H
+
+#ifdef CONFIG_SND_USB_AUDIO_USE_MEDIA_CONTROLLER
+
+#include <media/media-device.h>
+#include <media/media-entity.h>
+#include <sound/asound.h>
+
+struct media_ctl {
+ struct media_device *media_dev;
+ struct media_entity media_entity;
+ struct media_intf_devnode *intf_devnode;
+ struct media_link *intf_link;
+ struct media_pad media_pad;
+ struct media_pipeline media_pipe;
+};
+
+/*
+ * One source pad each for SNDRV_PCM_STREAM_CAPTURE and
+ * SNDRV_PCM_STREAM_PLAYBACK. One for sink pad to link
+ * to AUDIO Source
+*/
+#define MEDIA_MIXER_PAD_MAX (SNDRV_PCM_STREAM_LAST + 2)
+
+struct media_mixer_ctl {
+ struct media_device *media_dev;
+ struct media_entity media_entity;
+ struct media_intf_devnode *intf_devnode;
+ struct media_link *intf_link;
+ struct media_pad media_pad[MEDIA_MIXER_PAD_MAX];
+ struct media_pipeline media_pipe;
+};
+
+int media_snd_device_create(struct snd_usb_audio *chip,
+ struct usb_interface *iface);
+void media_snd_device_delete(struct snd_usb_audio *chip);
+int media_snd_stream_init(struct snd_usb_substream *subs, struct snd_pcm *pcm,
+ int stream);
+void media_snd_stream_delete(struct snd_usb_substream *subs);
+int media_snd_start_pipeline(struct snd_usb_substream *subs);
+void media_snd_stop_pipeline(struct snd_usb_substream *subs);
+#else
+static inline int media_snd_device_create(struct snd_usb_audio *chip,
+ struct usb_interface *iface)
+ { return 0; }
+static inline void media_snd_device_delete(struct snd_usb_audio *chip) { }
+static inline int media_snd_stream_init(struct snd_usb_substream *subs,
+ struct snd_pcm *pcm, int stream)
+ { return 0; }
+static inline void media_snd_stream_delete(struct snd_usb_substream *subs) { }
+static inline int media_snd_start_pipeline(struct snd_usb_substream *subs)
+ { return 0; }
+static inline void media_snd_stop_pipeline(struct snd_usb_substream *subs) { }
+#endif
+#endif /* __MEDIA_H */
#include <sound/info.h>
+struct media_mixer_ctl;
+
struct usb_mixer_interface {
struct snd_usb_audio *chip;
struct usb_host_interface *hostif;
struct urb *rc_urb;
struct usb_ctrlrequest *rc_setup_packet;
u8 rc_buffer[6];
+ struct media_mixer_ctl *media_mixer_ctl;
};
#define MAX_CHANNELS 16 /* max logical channels */
#include "pcm.h"
#include "clock.h"
#include "power.h"
+#include "media.h"
#define SUBSTREAM_FLAG_DATA_EP_STARTED 0
#define SUBSTREAM_FLAG_SYNC_EP_STARTED 1
struct audioformat *fmt;
int ret;
+ ret = media_snd_start_pipeline(subs);
+ if (ret)
+ return ret;
+
ret = snd_pcm_lib_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
if (ret < 0)
- return ret;
+ goto err_ret;
subs->pcm_format = params_format(hw_params);
subs->period_bytes = params_period_bytes(hw_params);
dev_dbg(&subs->dev->dev,
"cannot set format: format = %#x, rate = %d, channels = %d\n",
subs->pcm_format, subs->cur_rate, subs->channels);
- return -EINVAL;
+ ret = -EINVAL;
+ goto err_ret;
}
ret = snd_usb_lock_shutdown(subs->stream->chip);
if (ret < 0)
- return ret;
+ goto err_ret;
ret = set_format(subs, fmt);
snd_usb_unlock_shutdown(subs->stream->chip);
if (ret < 0)
- return ret;
+ goto err_ret;
subs->interface = fmt->iface;
subs->altset_idx = fmt->altset_idx;
subs->need_setup_ep = true;
return 0;
+
+err_ret:
+ media_snd_stop_pipeline(subs);
+ return ret;
}
/*
{
struct snd_usb_substream *subs = substream->runtime->private_data;
+ media_snd_stop_pipeline(subs);
subs->cur_audiofmt = NULL;
subs->cur_rate = 0;
subs->period_bytes = 0;
struct snd_usb_stream *as = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_usb_substream *subs = &as->substream[direction];
+ int ret;
subs->interface = -1;
subs->altset_idx = 0;
subs->dsd_dop.channel = 0;
subs->dsd_dop.marker = 1;
- return setup_hw_info(runtime, subs);
+ ret = setup_hw_info(runtime, subs);
+ if (ret == 0)
+ ret = media_snd_stream_init(subs, as->pcm, direction);
+ if (ret)
+ snd_usb_autosuspend(subs->stream->chip);
+ return ret;
}
static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction)
struct snd_usb_substream *subs = &as->substream[direction];
stop_endpoints(subs, true);
+ media_snd_stop_pipeline(subs);
if (subs->interface >= 0 &&
!snd_usb_lock_shutdown(subs->stream->chip)) {
.product_name = pname, \
.ifnum = QUIRK_ANY_INTERFACE, \
.type = QUIRK_AUDIO_ALIGN_TRANSFER, \
+ .media_device = 1, \
} \
}
#include "format.h"
#include "clock.h"
#include "stream.h"
+#include "media.h"
/*
* free a substream
kfree(fp);
}
kfree(subs->rate_list.list);
+ media_snd_stream_delete(subs);
}
*
*/
+struct media_device;
+struct media_intf_devnode;
+
struct snd_usb_audio {
int index;
struct usb_device *dev;
bool autoclock; /* from the 'autoclock' module param */
struct usb_host_interface *ctrl_intf; /* the audio control interface */
+ struct media_device *media_dev;
+ struct media_intf_devnode *ctl_intf_media_devnode;
};
#define usb_audio_err(chip, fmt, args...) \
const char *product_name;
int16_t ifnum;
uint16_t type;
+ bool media_device;
const void *data;
};