diff options
217 files changed, 19005 insertions, 4045 deletions
diff --git a/Documentation/DocBook/media/v4l/compat.xml b/Documentation/DocBook/media/v4l/compat.xml index 3dd9e78815d1..ebd2bfd1ee8e 100644 --- a/Documentation/DocBook/media/v4l/compat.xml +++ b/Documentation/DocBook/media/v4l/compat.xml @@ -2477,6 +2477,18 @@ that used it. It was originally scheduled for removal in 2.6.35. </orderedlist> </section> + <section> + <title>V4L2 in Linux 3.9</title> + <orderedlist> + <listitem> + <para>Added timestamp types to + <structfield>flags</structfield> field in + <structname>v4l2_buffer</structname>. See <xref + linkend="buffer-flags" />.</para> + </listitem> + </orderedlist> + </section> + <section id="other"> <title>Relation of V4L2 to other Linux multimedia APIs</title> diff --git a/Documentation/DocBook/media/v4l/io.xml b/Documentation/DocBook/media/v4l/io.xml index 388a34032653..2c4646d504c2 100644 --- a/Documentation/DocBook/media/v4l/io.xml +++ b/Documentation/DocBook/media/v4l/io.xml @@ -741,17 +741,19 @@ applications when an output stream.</entry> <entry>struct timeval</entry> <entry><structfield>timestamp</structfield></entry> <entry></entry> - <entry><para>For input streams this is the -system time (as returned by the <function>gettimeofday()</function> -function) when the first data byte was captured. For output streams -the data will not be displayed before this time, secondary to the -nominal frame rate determined by the current video standard in -enqueued order. Applications can for example zero this field to -display frames as soon as possible. The driver stores the time at -which the first data byte was actually sent out in the -<structfield>timestamp</structfield> field. This permits -applications to monitor the drift between the video and system -clock.</para></entry> + <entry><para>For input streams this is time when the first data + byte was captured, as returned by the + <function>clock_gettime()</function> function for the relevant + clock id; see <constant>V4L2_BUF_FLAG_TIMESTAMP_*</constant> in + <xref linkend="buffer-flags" />. For output streams the data + will not be displayed before this time, secondary to the nominal + frame rate determined by the current video standard in enqueued + order. Applications can for example zero this field to display + frames as soon as possible. The driver stores the time at which + the first data byte was actually sent out in the + <structfield>timestamp</structfield> field. This permits + applications to monitor the drift between the video and system + clock.</para></entry> </row> <row> <entry>&v4l2-timecode;</entry> @@ -903,7 +905,7 @@ should set this to 0.</entry> </row> <row> <entry></entry> - <entry>__unsigned long</entry> + <entry>unsigned long</entry> <entry><structfield>userptr</structfield></entry> <entry>When the memory type in the containing &v4l2-buffer; is <constant>V4L2_MEMORY_USERPTR</constant>, this is a userspace @@ -1114,6 +1116,35 @@ Typically applications shall use this flag for output buffers if the data in this buffer has not been created by the CPU but by some DMA-capable unit, in which case caches have not been used.</entry> </row> + <row> + <entry><constant>V4L2_BUF_FLAG_TIMESTAMP_MASK</constant></entry> + <entry>0xe000</entry> + <entry>Mask for timestamp types below. To test the + timestamp type, mask out bits not belonging to timestamp + type by performing a logical and operation with buffer + flags and timestamp mask.</entry> + </row> + <row> + <entry><constant>V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN</constant></entry> + <entry>0x0000</entry> + <entry>Unknown timestamp type. This type is used by + drivers before Linux 3.9 and may be either monotonic (see + below) or realtime (wall clock). Monotonic clock has been + favoured in embedded systems whereas most of the drivers + use the realtime clock. Either kinds of timestamps are + available in user space via + <function>clock_gettime(2)</function> using clock IDs + <constant>CLOCK_MONOTONIC</constant> and + <constant>CLOCK_REALTIME</constant>, respectively.</entry> + </row> + <row> + <entry><constant>V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC</constant></entry> + <entry>0x2000</entry> + <entry>The buffer timestamp has been taken from the + <constant>CLOCK_MONOTONIC</constant> clock. To access the + same clock outside V4L2, use + <function>clock_gettime(2)</function> .</entry> + </row> </tbody> </tgroup> </table> diff --git a/Documentation/DocBook/media/v4l/pixfmt-nv12m.xml b/Documentation/DocBook/media/v4l/pixfmt-nv12m.xml index a990b34d911a..f3a3d459fcdf 100644 --- a/Documentation/DocBook/media/v4l/pixfmt-nv12m.xml +++ b/Documentation/DocBook/media/v4l/pixfmt-nv12m.xml @@ -6,7 +6,7 @@ <refnamediv> <refname id="V4L2-PIX-FMT-NV12M"><constant>V4L2_PIX_FMT_NV12M</constant></refname> <refname id="V4L2-PIX-FMT-NV21M"><constant>V4L2_PIX_FMT_NV21M</constant></refname> - <refname id="V4L2-PIX-FMT-NV12MT_16X16"><constant>V4L2_PIX_FMT_NV12MT_16X16</constant></refname> + <refname id="V4L2-PIX-FMT-NV12MT-16X16"><constant>V4L2_PIX_FMT_NV12MT_16X16</constant></refname> <refpurpose>Variation of <constant>V4L2_PIX_FMT_NV12</constant> and <constant>V4L2_PIX_FMT_NV21</constant> with planes non contiguous in memory. </refpurpose> </refnamediv> diff --git a/Documentation/DocBook/media/v4l/pixfmt-srggb10alaw8.xml b/Documentation/DocBook/media/v4l/pixfmt-srggb10alaw8.xml new file mode 100644 index 000000000000..c934192e98ce --- /dev/null +++ b/Documentation/DocBook/media/v4l/pixfmt-srggb10alaw8.xml @@ -0,0 +1,34 @@ + <refentry> + <refmeta> + <refentrytitle> + V4L2_PIX_FMT_SBGGR10ALAW8 ('aBA8'), + V4L2_PIX_FMT_SGBRG10ALAW8 ('aGA8'), + V4L2_PIX_FMT_SGRBG10ALAW8 ('agA8'), + V4L2_PIX_FMT_SRGGB10ALAW8 ('aRA8'), + </refentrytitle> + &manvol; + </refmeta> + <refnamediv> + <refname id="V4L2-PIX-FMT-SBGGR10ALAW8"> + <constant>V4L2_PIX_FMT_SBGGR10ALAW8</constant> + </refname> + <refname id="V4L2-PIX-FMT-SGBRG10ALAW8"> + <constant>V4L2_PIX_FMT_SGBRG10ALAW8</constant> + </refname> + <refname id="V4L2-PIX-FMT-SGRBG10ALAW8"> + <constant>V4L2_PIX_FMT_SGRBG10ALAW8</constant> + </refname> + <refname id="V4L2-PIX-FMT-SRGGB10ALAW8"> + <constant>V4L2_PIX_FMT_SRGGB10ALAW8</constant> + </refname> + <refpurpose>10-bit Bayer formats compressed to 8 bits</refpurpose> + </refnamediv> + <refsect1> + <title>Description</title> + <para>The following four pixel formats are raw sRGB / Bayer + formats with 10 bits per color compressed to 8 bits each, + using the A-LAW algorithm. Each color component consumes 8 + bits of memory. In other respects this format is similar to + <xref linkend="V4L2-PIX-FMT-SRGGB8">.</xref></para> + </refsect1> + </refentry> diff --git a/Documentation/DocBook/media/v4l/pixfmt-uv8.xml b/Documentation/DocBook/media/v4l/pixfmt-uv8.xml new file mode 100644 index 000000000000..c507c1f73cd0 --- /dev/null +++ b/Documentation/DocBook/media/v4l/pixfmt-uv8.xml @@ -0,0 +1,62 @@ + <refentry id="V4L2-PIX-FMT-UV8"> + <refmeta> + <refentrytitle>V4L2_PIX_FMT_UV8 ('UV8')</refentrytitle> + &manvol; + </refmeta> + <refnamediv> + <refname><constant>V4L2_PIX_FMT_UV8</constant></refname> + <refpurpose>UV plane interleaved</refpurpose> + </refnamediv> + <refsect1> + <title>Description</title> + <para>In this format there is no Y plane, Only CbCr plane. ie + (UV interleaved)</para> + <example> + <title> + <constant>V4L2_PIX_FMT_UV8</constant> + 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>start + 0:</entry> + <entry>Cb<subscript>00</subscript></entry> + <entry>Cr<subscript>00</subscript></entry> + <entry>Cb<subscript>01</subscript></entry> + <entry>Cr<subscript>01</subscript></entry> + </row> + <row> + <entry>start + 4:</entry> + <entry>Cb<subscript>10</subscript></entry> + <entry>Cr<subscript>10</subscript></entry> + <entry>Cb<subscript>11</subscript></entry> + <entry>Cr<subscript>11</subscript></entry> + </row> + <row> + <entry>start + 8:</entry> + <entry>Cb<subscript>20</subscript></entry> + <entry>Cr<subscript>20</subscript></entry> + <entry>Cb<subscript>21</subscript></entry> + <entry>Cr<subscript>21</subscript></entry> + </row> + <row> + <entry>start + 12:</entry> + <entry>Cb<subscript>30</subscript></entry> + <entry>Cr<subscript>30</subscript></entry> + <entry>Cb<subscript>31</subscript></entry> + <entry>Cr<subscript>31</subscript></entry> + </row> + </tbody> + </tgroup> + </informaltable> + </para> + </formalpara> + </example> + </refsect1> + </refentry> diff --git a/Documentation/DocBook/media/v4l/pixfmt.xml b/Documentation/DocBook/media/v4l/pixfmt.xml index bf94f417592c..99b8d2ad6e4f 100644 --- a/Documentation/DocBook/media/v4l/pixfmt.xml +++ b/Documentation/DocBook/media/v4l/pixfmt.xml @@ -673,6 +673,7 @@ access the palette, this must be done with ioctls of the Linux framebuffer API.< &sub-srggb8; &sub-sbggr16; &sub-srggb10; + &sub-srggb10alaw8; &sub-srggb10dpcm8; &sub-srggb12; </section> @@ -701,6 +702,7 @@ information.</para> &sub-y12; &sub-y10b; &sub-y16; + &sub-uv8; &sub-yuyv; &sub-uyvy; &sub-yvyu; diff --git a/Documentation/DocBook/media/v4l/subdev-formats.xml b/Documentation/DocBook/media/v4l/subdev-formats.xml index a0a936455fae..cc51372ed5e0 100644 --- a/Documentation/DocBook/media/v4l/subdev-formats.xml +++ b/Documentation/DocBook/media/v4l/subdev-formats.xml @@ -353,9 +353,9 @@ <listitem><para>The number of bits per pixel component. All components are transferred on the same number of bits. Common values are 8, 10 and 12.</para> </listitem> - <listitem><para>If the pixel components are DPCM-compressed, a mention of the - DPCM compression and the number of bits per compressed pixel component.</para> - </listitem> + <listitem><para>The compression (optional). If the pixel components are + ALAW- or DPCM-compressed, a mention of the compression scheme and the + number of bits per compressed pixel component.</para></listitem> <listitem><para>The number of bus samples per pixel. Pixels that are wider than the bus width must be transferred in multiple samples. Common values are 1 and 2.</para></listitem> @@ -504,6 +504,74 @@ <entry>r<subscript>1</subscript></entry> <entry>r<subscript>0</subscript></entry> </row> + <row id="V4L2-MBUS-FMT-SBGGR10-ALAW8-1X8"> + <entry>V4L2_MBUS_FMT_SBGGR10_ALAW8_1X8</entry> + <entry>0x3015</entry> + <entry></entry> + <entry>-</entry> + <entry>-</entry> + <entry>-</entry> + <entry>-</entry> + <entry>b<subscript>7</subscript></entry> + <entry>b<subscript>6</subscript></entry> + <entry>b<subscript>5</subscript></entry> + <entry>b<subscript>4</subscript></entry> + <entry>b<subscript>3</subscript></entry> + <entry>b<subscript>2</subscript></entry> + <entry>b<subscript>1</subscript></entry> + <entry>b<subscript>0</subscript></entry> + </row> + <row id="V4L2-MBUS-FMT-SGBRG10-ALAW8-1X8"> + <entry>V4L2_MBUS_FMT_SGBRG10_ALAW8_1X8</entry> + <entry>0x3016</entry> + <entry></entry> + <entry>-</entry> + <entry>-</entry> + <entry>-</entry> + <entry>-</entry> + <entry>g<subscript>7</subscript></entry> + <entry>g<subscript>6</subscript></entry> + <entry>g<subscript>5</subscript></entry> + <entry>g<subscript>4</subscript></entry> + <entry>g<subscript>3</subscript></entry> + <entry>g<subscript>2</subscript></entry> + <entry>g<subscript>1</subscript></entry> + <entry>g<subscript>0</subscript></entry> + </row> + <row id="V4L2-MBUS-FMT-SGRBG10-ALAW8-1X8"> + <entry>V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8</entry> + <entry>0x3017</entry> + <entry></entry> + <entry>-</entry> + <entry>-</entry> + <entry>-</entry> + <entry>-</entry> + <entry>g<subscript>7</subscript></entry> + <entry>g<subscript>6</subscript></entry> + <entry>g<subscript>5</subscript></entry> + <entry>g<subscript>4</subscript></entry> + <entry>g<subscript>3</subscript></entry> + <entry>g<subscript>2</subscript></entry> + <entry>g<subscript>1</subscript></entry> + <entry>g<subscript>0</subscript></entry> + </row> + <row id="V4L2-MBUS-FMT-SRGGB10-ALAW8-1X8"> + <entry>V4L2_MBUS_FMT_SRGGB10_ALAW8_1X8</entry> + <entry>0x3018</entry> + <entry></entry> + <entry>-</entry> + <entry>-</entry> + <entry>-</entry> + <entry>-</entry> + <entry>r<subscript>7</subscript></entry> + <entry>r<subscript>6</subscript></entry> + <entry>r<subscript>5</subscript></entry> + <entry>r<subscript>4</subscript></entry> + <entry>r<subscript>3</subscript></entry> + <entry>r<subscript>2</subscript></entry> + <entry>r<subscript>1</subscript></entry> + <entry>r<subscript>0</subscript></entry> + </row> <row id="V4L2-MBUS-FMT-SBGGR10-DPCM8-1X8"> <entry>V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8</entry> <entry>0x300b</entry> @@ -853,10 +921,16 @@ <title>Packed YUV Formats</title> <para>Those data formats transfer pixel data as (possibly downsampled) Y, U - and V components. The format code is made of the following information. + and V components. Some formats include dummy bits in some of their samples + and are collectively referred to as "YDYC" (Y-Dummy-Y-Chroma) formats. + One cannot rely on the values of these dummy bits as those are undefined. + </para> + <para>The format code is made of the following information. <itemizedlist> <listitem><para>The Y, U and V components order code, as transferred on the - bus. Possible values are YUYV, UYVY, YVYU and VYUY.</para></listitem> + bus. Possible values are YUYV, UYVY, YVYU and VYUY for formats with no + dummy bit, and YDYUYDYV, YDYVYDYU, YUYDYVYD and YVYDYUYD for YDYC formats. + </para></listitem> <listitem><para>The number of bits per pixel component. All components are transferred on the same number of bits. Common values are 8, 10 and 12.</para> </listitem> @@ -877,7 +951,21 @@ U, Y, V, Y order will be named <constant>V4L2_MBUS_FMT_UYVY8_2X8</constant>. </para> - <para>The following table lisst existing packet YUV formats.</para> + <para><xref linkend="v4l2-mbus-pixelcode-yuv8"/> list existing packet YUV + formats and describes the organization of each pixel data in each sample. + When a format pattern is split across multiple samples each of the samples + in the pattern is described.</para> + + <para>The role of each bit transferred over the bus is identified by one + of the following codes.</para> + + <itemizedlist> + <listitem><para>y<subscript>x</subscript> for luma component bit number x</para></listitem> + <listitem><para>u<subscript>x</subscript> for blue chroma component bit number x</para></listitem> + <listitem><para>v<subscript>x</subscript> for red chroma component bit number x</para></listitem> + <listitem><para>- for non-available bits (for positions higher than the bus width)</para></listitem> + <listitem><para>d for dummy bits</para></listitem> + </itemizedlist> <table pgwide="0" frame="none" id="v4l2-mbus-pixelcode-yuv8"> <title>YUV Formats</title> @@ -885,27 +973,37 @@ <colspec colname="id" align="left" /> <colspec colname="code" align="center"/> <colspec colname="bit" /> - <colspec colnum="4" colname="b19" align="center" /> - <colspec colnum="5" colname="b18" align="center" /> - <colspec colnum="6" colname="b17" align="center" /> - <colspec colnum="7" colname="b16" align="center" /> - <colspec colnum="8" colname="b15" align="center" /> - <colspec colnum="9" colname="b14" align="center" /> - <colspec colnum="10" colname="b13" align="center" /> - <colspec colnum="11" colname="b12" align="center" /> - <colspec colnum="12" colname="b11" align="center" /> - <colspec colnum="13" colname="b10" align="center" /> - <colspec colnum="14" colname="b09" align="center" /> - <colspec colnum="15" colname="b08" align="center" /> - <colspec colnum="16" colname="b07" align="center" /> - <colspec colnum="17" colname="b06" align="center" /> - <colspec colnum="18" colname="b05" align="center" /> - <colspec colnum="19" colname="b04" align="center" /> - <colspec colnum="20" colname="b03" align="center" /> - <colspec colnum="21" colname="b02" align="center" /> - <colspec colnum="22" colname="b01" align="center" /> - <colspec colnum="23" colname="b00" align="center" /> - <spanspec namest="b19" nameend="b00" spanname="b0" /> + <colspec colnum="4" colname="b29" align="center" /> + <colspec colnum="5" colname="b28" align="center" /> + <colspec colnum="6" colname="b27" align="center" /> + <colspec colnum="7" colname="b26" align="center" /> + <colspec colnum="8" colname="b25" align="center" /> + <colspec colnum="9" colname="b24" align="center" /> + <colspec colnum="10" colname="b23" align="center" /> + <colspec colnum="11" colname="b22" align="center" /> + <colspec colnum="12" colname="b21" align="center" /> + <colspec colnum="13" colname="b20" align="center" /> + <colspec colnum="14" colname="b19" align="center" /> + <colspec colnum="15" colname="b18" align="center" /> + <colspec colnum="16" colname="b17" align="center" /> + <colspec colnum="17" colname="b16" align="center" /> + <colspec colnum="18" colname="b15" align="center" /> + <colspec colnum="19" colname="b14" align="center" /> + <colspec colnum="20" colname="b13" align="center" /> + <colspec colnum="21" colname="b12" align="center" /> + <colspec colnum="22" colname="b11" align="center" /> + <colspec colnum="23" colname="b10" align="center" /> + <colspec colnum="24" colname="b09" align="center" /> + <colspec colnum="25" colname="b08" align="center" /> + <colspec colnum="26" colname="b07" align="center" /> + <colspec colnum="27" colname="b06" align="center" /> + <colspec colnum="28" colname="b05" align="center" /> + <colspec colnum="29" colname="b04" align="center" /> + <colspec colnum="30" colname="b03" align="center" /> + <colspec colnum="31" colname="b02" align="center" /> + <colspec colnum="32" colname="b01" align="center" /> + <colspec colnum="33" colname="b00" align="center" /> + <spanspec namest="b29" nameend="b00" spanname="b0" /> <thead> <row> <entry>Identifier</entry> @@ -917,6 +1015,16 @@ <entry></entry> <entry></entry> <entry>Bit</entry> + <entry>29</entry> + <entry>28</entry> + <entry>27</entry> + <entry>26</entry> + <entry>25</entry> + <entry>24</entry> + <entry>23</entry> + <entry>22</entry> + <entry>21</entry> + <entry>10</entry> <entry>19</entry> <entry>18</entry> <entry>17</entry> @@ -944,16 +1052,8 @@ <entry>V4L2_MBUS_FMT_Y8_1X8</entry> <entry>0x2001</entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>y<subscript>7</subscript></entry> @@ -965,9 +1065,9 @@ <entry>y<subscript>1</subscript></entry> <entry>y<subscript>0</subscript></entry> </row> - <row id="V4L2-MBUS-FMT-UYVY8-1_5X8"> - <entry>V4L2_MBUS_FMT_UYVY8_1_5X8</entry> - <entry>0x2002</entry> + <row id="V4L2-MBUS-FMT-UV8-1X8"> + <entry>V4L2_MBUS_FMT_UV8_1X8</entry> + <entry>0x2015</entry> <entry></entry> <entry>-</entry> <entry>-</entry> @@ -1006,6 +1106,40 @@ <entry>-</entry> <entry>-</entry> <entry>-</entry> + <entry>v<subscript>7</subscript></entry> + <entry>v<subscript>6</subscript></entry> + <entry>v<subscript>5</subscript></entry> + <entry>v<subscript>4</subscript></entry> + <entry>v<subscript>3</subscript></entry> + <entry>v<subscript>2</subscript></entry> + <entry>v<subscript>1</subscript></entry> + <entry>v<subscript>0</subscript></entry> + </row> + <row id="V4L2-MBUS-FMT-UYVY8-1_5X8"> + <entry>V4L2_MBUS_FMT_UYVY8_1_5X8</entry> + <entry>0x2002</entry> + <entry></entry> + &dash-ent-10; + &dash-ent-10; + <entry>-</entry> + <entry>-</entry> + <entry>u<subscript>7</subscript></entry> + <entry>u<subscript>6</subscript></entry> + <entry>u<subscript>5</subscript></entry> + <entry>u<subscript>4</subscript></entry> + <entry>u<subscript>3</subscript></entry> + <entry>u<subscript>2</subscript></entry> + <entry>u<subscript>1</subscript></entry> + <entry>u<subscript>0</subscript></entry> + </row> + <row> + <entry></entry> + <entry></entry> + <entry></entry> + &dash-ent-10; + &dash-ent-10; + <entry>-</entry> + <entry>-</entry> <entry>y<subscript>7</subscript></entry> <entry>y<subscript>6</subscript></entry> <entry>y<subscript>5</subscript></entry> @@ -1019,16 +1153,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>y<subscript>7</subscript></entry> @@ -1044,16 +1170,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>v<subscript>7</subscript></entry> @@ -1069,16 +1187,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>y<subscript>7</subscript></entry> @@ -1094,16 +1204,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>y<subscript>7</subscript></entry> @@ -1119,16 +1221,8 @@ <entry>V4L2_MBUS_FMT_VYUY8_1_5X8</entry> <entry>0x2003</entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>v<subscript>7</subscript></entry> @@ -1144,16 +1238,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>y<subscript>7</subscript></entry> @@ -1169,16 +1255,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>y<subscript>7</subscript></entry> @@ -1194,16 +1272,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>u<subscript>7</subscript></entry> @@ -1219,16 +1289,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>y<subscript>7</subscript></entry> @@ -1244,16 +1306,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>y<subscript>7</subscript></entry> @@ -1269,16 +1323,8 @@ <entry>V4L2_MBUS_FMT_YUYV8_1_5X8</entry> <entry>0x2004</entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>y<subscript>7</subscript></entry> @@ -1294,16 +1340,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>y<subscript>7</subscript></entry> @@ -1319,16 +1357,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>u<subscript>7</subscript></entry> @@ -1344,16 +1374,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>y<subscript>7</subscript></entry> @@ -1369,16 +1391,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>y<subscript>7</subscript></entry> @@ -1394,16 +1408,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>v<subscript>7</subscript></entry> @@ -1419,16 +1425,8 @@ <entry>V4L2_MBUS_FMT_YVYU8_1_5X8</entry> <entry>0x2005</entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>y<subscript>7</subscript></entry> @@ -1444,16 +1442,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>y<subscript>7</subscript></entry> @@ -1469,16 +1459,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>v<subscript>7</subscript></entry> @@ -1494,16 +1476,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>y<subscript>7</subscript></entry> @@ -1519,16 +1493,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>y<subscript>7</subscript></entry> @@ -1544,16 +1510,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>u<subscript>7</subscript></entry> @@ -1569,16 +1527,8 @@ <entry>V4L2_MBUS_FMT_UYVY8_2X8</entry> <entry>0x2006</entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>u<subscript>7</subscript></entry> @@ -1594,16 +1544,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>y<subscript>7</subscript></entry> @@ -1619,16 +1561,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>v<subscript>7</subscript></entry> @@ -1644,16 +1578,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>y<subscript>7</subscript></entry> @@ -1669,16 +1595,8 @@ <entry>V4L2_MBUS_FMT_VYUY8_2X8</entry> <entry>0x2007</entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>v<subscript>7</subscript></entry> @@ -1694,16 +1612,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>y<subscript>7</subscript></entry> @@ -1719,16 +1629,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>u<subscript>7</subscript></entry> @@ -1744,16 +1646,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>y<subscript>7</subscript></entry> @@ -1769,16 +1663,8 @@ <entry>V4L2_MBUS_FMT_YUYV8_2X8</entry> <entry>0x2008</entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>y<subscript>7</subscript></entry> @@ -1794,16 +1680,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>u<subscript>7</subscript></entry> @@ -1819,16 +1697,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>y<subscript>7</subscript></entry> @@ -1844,16 +1714,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>v<subscript>7</subscript></entry> @@ -1869,16 +1731,8 @@ <entry>V4L2_MBUS_FMT_YVYU8_2X8</entry> <entry>0x2009</entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>y<subscript>7</subscript></entry> @@ -1894,16 +1748,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>v<subscript>7</subscript></entry> @@ -1919,16 +1765,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>y<subscript>7</subscript></entry> @@ -1944,16 +1782,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>u<subscript>7</subscript></entry> @@ -1969,16 +1799,8 @@ <entry>V4L2_MBUS_FMT_Y10_1X10</entry> <entry>0x200a</entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>y<subscript>9</subscript></entry> <entry>y<subscript>8</subscript></entry> <entry>y<subscript>7</subscript></entry> @@ -1994,16 +1816,8 @@ <entry>V4L2_MBUS_FMT_YUYV10_2X10</entry> <entry>0x200b</entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>y<subscript>9</subscript></entry> <entry>y<subscript>8</subscript></entry> <entry>y<subscript>7</subscript></entry> @@ -2019,16 +1833,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>u<subscript>9</subscript></entry> <entry>u<subscript>8</subscript></entry> <entry>u<subscript>7</subscript></entry> @@ -2044,16 +1850,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>y<subscript>9</subscript></entry> <entry>y<subscript>8</subscript></entry> <entry>y<subscript>7</subscript></entry> @@ -2069,16 +1867,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>v<subscript>9</subscript></entry> <entry>v<subscript>8</subscript></entry> <entry>v<subscript>7</subscript></entry> @@ -2094,16 +1884,8 @@ <entry>V4L2_MBUS_FMT_YVYU10_2X10</entry> <entry>0x200c</entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>y<subscript>9</subscript></entry> <entry>y<subscript>8</subscript></entry> <entry>y<subscript>7</subscript></entry> @@ -2119,16 +1901,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>v<subscript>9</subscript></entry> <entry>v<subscript>8</subscript></entry> <entry>v<subscript>7</subscript></entry> @@ -2144,16 +1918,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>y<subscript>9</subscript></entry> <entry>y<subscript>8</subscript></entry> <entry>y<subscript>7</subscript></entry> @@ -2169,16 +1935,8 @@ <entry></entry> <entry></entry> <entry></entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> - <entry>-</entry> + &dash-ent-10; + &dash-ent-10; <entry>u<subscript>9</subscript></entry> <entry>u<subscript>8</subscript></entry> <entry>u<subscript>7</subscript></entry> @@ -2194,6 +1952,7 @@ <entry>V4L2_MBUS_FMT_Y12_1X12</entry> <entry>0x2013</entry> <entry></entry> + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>-</entry> @@ -2219,6 +1978,7 @@ <entry>V4L2_MBUS_FMT_UYVY8_1X16</entry> <entry>0x200f</entry> <entry></entry> + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>-</entry> @@ -2244,6 +2004,7 @@ <entry></entry> <entry></entry> <entry></entry> + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>-</entry> @@ -2269,6 +2030,7 @@ <entry>V4L2_MBUS_FMT_VYUY8_1X16</entry> <entry>0x2010</entry> <entry></entry> + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>-</entry> @@ -2294,6 +2056,7 @@ <entry></entry> <entry></entry> <entry></entry> + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>-</entry> @@ -2319,6 +2082,7 @@ <entry>V4L2_MBUS_FMT_YUYV8_1X16</entry> <entry>0x2011</entry> <entry></entry> + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>-</entry> @@ -2344,6 +2108,7 @@ <entry></entry> <entry></entry> <entry></entry> + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>-</entry> @@ -2369,6 +2134,7 @@ <entry>V4L2_MBUS_FMT_YVYU8_1X16</entry> <entry>0x2012</entry> <entry></entry> + &dash-ent-10; <entry>-</entry> <entry>-</entry> <entry>-</entry> @@ -2394,6 +2160,57 @@ <entry></entry> <entry></entry> <entry></entry> + &dash-ent-10; + <entry>-</entry> + <entry>-</entry> + <entry>-</entry> + <entry>-</entry> + <entry>y<subscript>7</subscript></entry> + <entry>y<subscript>6</subscript></entry> + <entry>y<subscript>5</subscript></entry> + <entry>y<subscript>4</subscript></entry> + <entry>y<subscript>3</subscript></entry> + <entry>y<subscript>2</subscript></entry> + <entry>y<subscript>1</subscript></entry> + <entry>y<subscript>0</subscript></entry> + <entry>u<subscript>7</subscript></entry> + <entry>u<subscript>6</subscript></entry> + <entry>u<subscript>5</subscript></entry> + <entry>u<subscript>4</subscript></entry> + <entry>u<subscript>3</subscript></entry> + <entry>u<subscript>2</subscript></entry> + <entry>u<subscript>1</subscript></entry> + <entry>u<subscript>0</subscript></entry> + </row> + <row id="V4L2-MBUS-FMT-YDYUYDYV8-1X16"> + <entry>V4L2_MBUS_FMT_YDYUYDYV8_1X16</entry> + <entry>0x2014</entry> + <entry></entry> + <entry>-</entry> + <entry>-</entry> + <entry>-</entry> + <entry>-</entry> + <entry>y<subscript>7</subscript></entry> + <entry>y<subscript>6</subscript></entry> + <entry>y<subscript>5</subscript></entry> + <entry>y<subscript>4</subscript></entry> + <entry>y<subscript>3</subscript></entry> + <entry>y<subscript>2</subscript></entry> + <entry>y<subscript>1</subscript></entry> + <entry>y<subscript>0</subscript></entry> + <entry>d</entry> + <entry>d</entry> + <entry>d</entry> + <entry>d</entry> + <entry>d</entry> + <entry>d</entry> + <entry>d</entry> + <entry>d</entry> + </row> + <row> + <entry></entry> + <entry></entry> + <entry></entry> <entry>-</entry> <entry>-</entry> <entry>-</entry> @@ -2415,10 +2232,61 @@ <entry>u<subscript>1</subscript></entry> <entry>u<subscript>0</subscript></entry> </row> + <row> + <entry></entry> + <entry></entry> + <entry></entry> + <entry>-</entry> + <entry>-</entry> + <entry>-</entry> + <entry>-</entry> + <entry>y<subscript>7</subscript></entry> + <entry>y<subscript>6</subscript></entry> + <entry>y<subscript>5</subscript></entry> + <entry>y<subscript>4</subscript></entry> + <entry>y<subscript>3</subscript></entry> + <entry>y<subscript>2</subscript></entry> + <entry>y<subscript>1</subscript></entry> + <entry>y<subscript>0</subscript></entry> + <entry>d</entry> + <entry>d</entry> + <entry>d</entry> + <entry>d</entry> + <entry>d</entry> + <entry>d</entry> + <entry>d</entry> + <entry>d</entry> + </row> + <row> + <entry></entry> + <entry></entry> + <entry></entry> + <entry>-</entry> + <entry>-</entry> + <entry>-</entry> + <entry>-</entry> + <entry>y<subscript>7</subscript></entry> + <entry>y<subscript>6</subscript></entry> + <entry>y<subscript>5</subscript></entry> + <entry>y<subscript>4</subscript></entry> + <entry>y<subscript>3</subscript></entry> + <entry>y<subscript>2</subscript></entry> + <entry>y<subscript>1</subscript></entry> + <entry>y<subscript>0</subscript></entry> + <entry>v<subscript>7</subscript></entry> + <entry>v<subscript>6</subscript></entry> + <entry>v<subscript>5</subscript></entry> + <entry>v<subscript>4</subscript></entry> + <entry>v<subscript>3</subscript></entry> + <entry>v<subscript>2</subscript></entry> + <entry>v<subscript>1</subscript></entry> + <entry>v<subscript>0</subscript></entry> + </row> <row id="V4L2-MBUS-FMT-YUYV10-1X20"> <entry>V4L2_MBUS_FMT_YUYV10_1X20</entry> <entry>0x200d</entry> <entry></entry> + &dash-ent-10; <entry>y<subscript>9</subscript></entry> <entry>y<subscript>8</subscript></entry> <entry>y<subscript>7</subscript></entry> @@ -2444,6 +2312,7 @@ <entry></entry> <entry></entry> <entry></entry> + &dash-ent-10; <entry>y<subscript>9</subscript></entry> <entry>y<subscript>8</subscript></entry> <entry>y<subscript>7</subscript></entry> @@ -2469,6 +2338,7 @@ <entry>V4L2_MBUS_FMT_YVYU10_1X20</entry> <entry>0x200e</entry> <entry></entry> + &dash-ent-10; <entry>y<subscript>9</subscript></entry> <entry>y<subscript>8</subscript></entry> <entry>y<subscript>7</subscript></entry> @@ -2494,6 +2364,7 @@ <entry></entry> <entry></entry> <entry></entry> + &dash-ent-10; <entry>y<subscript>9</subscript></entry> <entry>y<subscript>8</subscript></entry> <entry>y<subscript>7</subscript></entry> @@ -2515,6 +2386,41 @@ <entry>u<subscript>1</subscript></entry> <entry>u<subscript>0</subscript></entry> </row> + <row id="V4L2-MBUS-FMT-YUV10-1X30"> + <entry>V4L2_MBUS_FMT_YUV10_1X30</entry> + <entry>0x2014</entry> + <entry></entry> + <entry>y<subscript>9</subscript></entry> + <entry>y<subscript>8</subscript></entry> + <entry>y<subscript>7</subscript></entry> + <entry>y<subscript>6</subscript></entry> + <entry>y<subscript>5</subscript></entry> + <entry>y<subscript>4</subscript></entry> + <entry>y<subscript>3</subscript></entry> + <entry>y<subscript>2</subscript></entry> + <entry>y<subscript>1</subscript></entry> + <entry>y<subscript>0</subscript></entry> + <entry>u<subscript>9</subscript></entry> + <entry>u<subscript>8</subscript></entry> + <entry>u<subscript>7</subscript></entry> + <entry>u<subscript>6</subscript></entry> + <entry>u<subscript>5</subscript></entry> + <entry>u<subscript>4</subscript></entry> + <entry>u<subscript>3</subscript></entry> + <entry>u<subscript>2</subscript></entry> + <entry>u<subscript>1</subscript></entry> + <entry>u<subscript>0</subscript></entry> + <entry>v<subscript>9</subscript></entry> + <entry>v<subscript>8</subscript></entry> + <entry>v<subscript>7</subscript></entry> + <entry>v<subscript>6</subscript></entry> + <entry>v<subscript>5</subscript></entry> + <entry>v<subscript>4</subscript></entry> + <entry>v<subscript>3</subscript></entry> + <entry>v<subscript>2</subscript></entry> + <entry>v<subscript>1</subscript></entry> + <entry>v<subscript>0</subscript></entry> + </row> </tbody> </tgroup> </table> diff --git a/Documentation/DocBook/media/v4l/v4l2.xml b/Documentation/DocBook/media/v4l/v4l2.xml index 4d110b1ad3e9..8fe29427c8e4 100644 --- a/Documentation/DocBook/media/v4l/v4l2.xml +++ b/Documentation/DocBook/media/v4l/v4l2.xml @@ -140,6 +140,16 @@ structs, ioctls) must be noted in more detail in the history chapter applications. --> <revision> + <revnumber>3.9</revnumber> + <date>2012-12-03</date> + <authorinitials>sa</authorinitials> + <revremark>Added timestamp types to + <structname>v4l2_buffer</structname>, see <xref + linkend="buffer-flags" />. + </revremark> + </revision> + + <revision> <revnumber>3.6</revnumber> <date>2012-07-02</date> <authorinitials>hv</authorinitials> @@ -472,7 +482,7 @@ and discussions on the V4L mailing list.</revremark> </partinfo> <title>Video for Linux Two API Specification</title> - <subtitle>Revision 3.6</subtitle> + <subtitle>Revision 3.9</subtitle> <chapter id="common"> &sub-common; diff --git a/Documentation/DocBook/media_api.tmpl b/Documentation/DocBook/media_api.tmpl index f2413acfe241..1f6593deb995 100644 --- a/Documentation/DocBook/media_api.tmpl +++ b/Documentation/DocBook/media_api.tmpl @@ -22,6 +22,7 @@ <!-- LinuxTV v4l-dvb repository. --> <!ENTITY v4l-dvb "<ulink url='http://linuxtv.org/repo/'>http://linuxtv.org/repo/</ulink>"> +<!ENTITY dash-ent-10 "<entry>-</entry><entry>-</entry><entry>-</entry><entry>-</entry><entry>-</entry><entry>-</entry><entry>-</entry><entry>-</entry><entry>-</entry><entry>-</entry>"> ]> <book id="media_api"> diff --git a/Documentation/video4linux/et61x251.txt b/Documentation/video4linux/et61x251.txt deleted file mode 100644 index e0cdae491858..000000000000 --- a/Documentation/video4linux/et61x251.txt +++ /dev/null @@ -1,315 +0,0 @@ - - ET61X[12]51 PC Camera Controllers - Driver for Linux - ================================= - - - Documentation - - - -Index -===== -1. Copyright -2. Disclaimer -3. License -4. Overview and features -5. Module dependencies -6. Module loading -7. Module parameters -8. Optional device control through "sysfs" -9. Supported devices -10. Notes for V4L2 application developers -11. Contact information - - -1. Copyright -============ -Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> - - -2. Disclaimer -============= -Etoms is a trademark of Etoms Electronics Corp. -This software is not developed or sponsored by Etoms Electronics. - - -3. License -========== -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. - - -4. Overview and features -======================== -This driver supports the video interface of the devices mounting the ET61X151 -or ET61X251 PC Camera Controllers. - -It's worth to note that Etoms Electronics has never collaborated with the -author during the development of this project; despite several requests, -Etoms Electronics also refused to release enough detailed specifications of -the video compression engine. - -The driver relies on the Video4Linux2 and USB core modules. It has been -designed to run properly on SMP systems as well. - -The latest version of the ET61X[12]51 driver can be found at the following URL: -http://www.linux-projects.org/ - -Some of the features of the driver are: - -- full compliance with the Video4Linux2 API (see also "Notes for V4L2 - application developers" paragraph); -- available mmap or read/poll methods for video streaming through isochronous - data transfers; -- automatic detection of image sensor; -- support for any window resolutions and optional panning within the maximum - pixel area of image sensor; -- image downscaling with arbitrary scaling factors from 1 and 2 in both - directions (see "Notes for V4L2 application developers" paragraph); -- two different video formats for uncompressed or compressed data in low or - high compression quality (see also "Notes for V4L2 application developers" - paragraph); -- full support for the capabilities of every possible image sensors that can - be connected to the ET61X[12]51 bridges, including, for instance, red, green, - blue and global gain adjustments and exposure control (see "Supported - devices" paragraph for details); -- use of default color settings for sunlight conditions; -- dynamic I/O interface for both ET61X[12]51 and image sensor control (see - "Optional device control through 'sysfs'" paragraph); -- dynamic driver control thanks to various module parameters (see "Module - parameters" paragraph); -- up to 64 cameras can be handled at the same time; they can be connected and - disconnected from the host many times without turning off the computer, if - the system supports hotplugging; -- no known bugs. - - -5. Module dependencies -====================== -For it to work properly, the driver needs kernel support for Video4Linux and -USB. - -The following options of the kernel configuration file must be enabled and -corresponding modules must be compiled: - - # Multimedia devices - # - CONFIG_VIDEO_DEV=m - -To enable advanced debugging functionality on the device through /sysfs: - - # Multimedia devices - # - CONFIG_VIDEO_ADV_DEBUG=y - - # USB support - # - CONFIG_USB=m - -In addition, depending on the hardware being used, the modules below are -necessary: - - # USB Host Controller Drivers - # - CONFIG_USB_EHCI_HCD=m - CONFIG_USB_UHCI_HCD=m - CONFIG_USB_OHCI_HCD=m - -And finally: - - # USB Multimedia devices - # - CONFIG_USB_ET61X251=m - - -6. Module loading -================= -To use the driver, it is necessary to load the "et61x251" module into memory -after every other module required: "videodev", "v4l2_common", "compat_ioctl32", -"usbcore" and, depending on the USB host controller you have, "ehci-hcd", -"uhci-hcd" or "ohci-hcd". - -Loading can be done as shown below: - - [root@localhost home]# modprobe et61x251 - -At this point the devices should be recognized. You can invoke "dmesg" to -analyze kernel messages and verify that the loading process has gone well: - - [user@localhost home]$ dmesg - - -7. Module parameters -==================== -Module parameters are listed below: -------------------------------------------------------------------------------- -Name: video_nr -Type: short array (min = 0, max = 64) -Syntax: <-1|n[,...]> -Description: Specify V4L2 minor mode number: - -1 = use next available - n = use minor number n - You can specify up to 64 cameras this way. - For example: - video_nr=-1,2,-1 would assign minor number 2 to the second - registered camera and use auto for the first one and for every - other camera. -Default: -1 -------------------------------------------------------------------------------- -Name: force_munmap -Type: bool array (min = 0, max = 64) -Syntax: <0|1[,...]> -Description: Force the application to unmap previously mapped buffer memory - before calling any VIDIOC_S_CROP or VIDIOC_S_FMT ioctl's. Not - all the applications support this feature. This parameter is - specific for each detected camera. - 0 = do not force memory unmapping - 1 = force memory unmapping (save memory) -Default: 0 -------------------------------------------------------------------------------- -Name: frame_timeout -Type: uint array (min = 0, max = 64) -Syntax: <n[,...]> -Description: Timeout for a video frame in seconds. This parameter is - specific for each detected camera. This parameter can be - changed at runtime thanks to the /sys filesystem interface. -Default: 2 -------------------------------------------------------------------------------- -Name: debug -Type: ushort -Syntax: <n> -Description: Debugging information level, from 0 to 3: - 0 = none (use carefully) - 1 = critical errors - 2 = significant information - 3 = more verbose messages - Level 3 is useful for testing only, when only one device - is used at the same time. It also shows some more information - about the hardware being detected. This module parameter can be - changed at runtime thanks to the /sys filesystem interface. -Default: 2 -------------------------------------------------------------------------------- - - -8. Optional device control through "sysfs" -========================================== -If the kernel has been compiled with the CONFIG_VIDEO_ADV_DEBUG option enabled, -it is possible to read and write both the ET61X[12]51 and the image sensor -registers by using the "sysfs" filesystem interface. - -There are four files in the /sys/class/video4linux/videoX directory for each -registered camera: "reg", "val", "i2c_reg" and "i2c_val". The first two files -control the ET61X[12]51 bridge, while the other two control the sensor chip. -"reg" and "i2c_reg" hold the values of the current register index where the -following reading/writing operations are addressed at through "val" and -"i2c_val". Their use is not intended for end-users, unless you know what you -are doing. Remember that you must be logged in as root before writing to them. - -As an example, suppose we were to want to read the value contained in the -register number 1 of the sensor register table - which is usually the product -identifier - of the camera registered as "/dev/video0": - - [root@localhost #] cd /sys/class/video4linux/video0 - [root@localhost #] echo 1 > i2c_reg - [root@localhost #] cat i2c_val - -Note that if the sensor registers cannot be read, "cat" will fail. -To avoid race conditions, all the I/O accesses to the files are serialized. - - -9. Supported devices -==================== -None of the names of the companies as well as their products will be mentioned -here. They have never collaborated with the author, so no advertising. - -From the point of view of a driver, what unambiguously identify a device are -its vendor and product USB identifiers. Below is a list of known identifiers of -devices mounting the ET61X[12]51 PC camera controllers: - -Vendor ID Product ID ---------- ---------- -0x102c 0x6151 -0x102c 0x6251 -0x102c 0x6253 -0x102c 0x6254 -0x102c 0x6255 -0x102c 0x6256 -0x102c 0x6257 -0x102c 0x6258 -0x102c 0x6259 -0x102c 0x625a -0x102c 0x625b -0x102c 0x625c -0x102c 0x625d -0x102c 0x625e -0x102c 0x625f -0x102c 0x6260 -0x102c 0x6261 -0x102c 0x6262 -0x102c 0x6263 -0x102c 0x6264 -0x102c 0x6265 -0x102c 0x6266 -0x102c 0x6267 -0x102c 0x6268 -0x102c 0x6269 - -The following image sensors are supported: - -Model Manufacturer ------ ------------ -TAS5130D1B Taiwan Advanced Sensor Corporation - -All the available control settings of each image sensor are supported through -the V4L2 interface. - - -10. Notes for V4L2 application developers -========================================= -This driver follows the V4L2 API specifications. In particular, it enforces two -rules: - -- exactly one I/O method, either "mmap" or "read", is associated with each -file descriptor. Once it is selected, the application must close and reopen the -device to switch to the other I/O method; - -- although it is not mandatory, previously mapped buffer memory should always -be unmapped before calling any "VIDIOC_S_CROP" or "VIDIOC_S_FMT" ioctl's. -The same number of buffers as before will be allocated again to match the size -of the new video frames, so you have to map the buffers again before any I/O -attempts on them. - -Consistently with the hardware limits, this driver also supports image -downscaling with arbitrary scaling factors from 1 and 2 in both directions. -However, the V4L2 API specifications don't correctly define how the scaling -factor can be chosen arbitrarily by the "negotiation" of the "source" and -"target" rectangles. To work around this flaw, we have added the convention -that, during the negotiation, whenever the "VIDIOC_S_CROP" ioctl is issued, the -scaling factor is restored to 1. - -This driver supports two different video formats: the first one is the "8-bit -Sequential Bayer" format and can be used to obtain uncompressed video data -from the device through the current I/O method, while the second one provides -"raw" compressed video data (without frame headers not related to the -compressed data). The current compression quality may vary from 0 to 1 and can -be selected or queried thanks to the VIDIOC_S_JPEGCOMP and VIDIOC_G_JPEGCOMP -V4L2 ioctl's. - - -11. Contact information -======================= -The author may be contacted by e-mail at <luca.risolia@studio.unibo.it>. - -GPG/PGP encrypted e-mail's are accepted. The GPG key ID of the author is -'FCE635A4'; the public 1024-bit key should be available at any keyserver; -the fingerprint is: '88E8 F32F 7244 68BA 3958 5D40 99DA 5D2A FCE6 35A4'. diff --git a/Documentation/video4linux/ibmcam.txt b/Documentation/video4linux/ibmcam.txt deleted file mode 100644 index a51055211e62..000000000000 --- a/Documentation/video4linux/ibmcam.txt +++ /dev/null @@ -1,323 +0,0 @@ -README for Linux device driver for the IBM "C-It" USB video camera - -INTRODUCTION: - -This driver does not use all features known to exist in -the IBM camera. However most of needed features work well. - -This driver was developed using logs of observed USB traffic -which was produced by standard Windows driver (c-it98.sys). -I did not have data sheets from Xirlink. - -Video formats: - 128x96 [model 1] - 176x144 - 320x240 [model 2] - 352x240 [model 2] - 352x288 -Frame rate: 3 - 30 frames per second (FPS) -External interface: USB -Internal interface: Video For Linux (V4L) -Supported controls: -- by V4L: Contrast, Brightness, Color, Hue -- by driver options: frame rate, lighting conditions, video format, - default picture settings, sharpness. - -SUPPORTED CAMERAS: - -Xirlink "C-It" camera, also known as "IBM PC Camera". -The device uses proprietary ASIC (and compression method); -it is manufactured by Xirlink. See http://xirlinkwebcam.sourceforge.net, -http://www.ibmpccamera.com, or http://www.c-itnow.com/ for details and pictures. - -This very chipset ("X Chip", as marked at the factory) -is used in several other cameras, and they are supported -as well: - -- IBM NetCamera -- Veo Stingray - -The Linux driver was developed with camera with following -model number (or FCC ID): KSX-XVP510. This camera has three -interfaces, each with one endpoint (control, iso, iso). This -type of cameras is referred to as "model 1". These cameras are -no longer manufactured. - -Xirlink now manufactures new cameras which are somewhat different. -In particular, following models [FCC ID] belong to that category: - -XVP300 [KSX-X9903] -XVP600 [KSX-X9902] -XVP610 [KSX-X9902] - -(see http://www.xirlink.com/ibmpccamera/ for updates, they refer -to these new cameras by Windows driver dated 12-27-99, v3005 BETA) -These cameras have two interfaces, one endpoint in each (iso, bulk). -Such type of cameras is referred to as "model 2". They are supported -(with exception of 352x288 native mode). - -Some IBM NetCameras (Model 4) are made to generate only compressed -video streams. This is great for performance, but unfortunately -nobody knows how to decompress the stream :-( Therefore, these -cameras are *unsupported* and if you try to use one of those, all -you get is random colored horizontal streaks, not the image! -If you have one of those cameras, you probably should return it -to the store and get something that is supported. - -Tell me more about all that "model" business --------------------------------------------- - -I just invented model numbers to uniquely identify flavors of the -hardware/firmware that were sold. It was very confusing to use -brand names or some other internal numbering schemes. So I found -by experimentation that all Xirlink chipsets fall into four big -classes, and I called them "models". Each model is programmed in -its own way, and each model sends back the video in its own way. - -Quirks of Model 2 cameras: -------------------------- - -Model 2 does not have hardware contrast control. Corresponding V4L -control is implemented in software, which is not very nice to your -CPU, but at least it works. - -This driver provides 352x288 mode by switching the camera into -quasi-352x288 RGB mode (800 Kbits per frame) essentially limiting -this mode to 10 frames per second or less, in ideal conditions on -the bus (USB is shared, after all). The frame rate -has to be programmed very conservatively. Additional concern is that -frame rate depends on brightness setting; therefore the picture can -be good at one brightness and broken at another! I did not want to fix -the frame rate at slowest setting, but I had to move it pretty much down -the scale (so that framerate option barely matters). I also noticed that -camera after first powering up produces frames slightly faster than during -consecutive uses. All this means that if you use 352x288 (which is -default), be warned - you may encounter broken picture on first connect; -try to adjust brightness - brighter image is slower, so USB will be able -to send all data. However if you regularly use Model 2 cameras you may -prefer 176x144 which makes perfectly good I420, with no scaling and -lesser demands on USB (300 Kbits per second, or 26 frames per second). - -Another strange effect of 352x288 mode is the fine vertical grid visible -on some colored surfaces. I am sure it is caused by me not understanding -what the camera is trying to say. Blame trade secrets for that. - -The camera that I had also has a hardware quirk: if disconnected, -it needs few minutes to "relax" before it can be plugged in again -(poorly designed USB processor reset circuit?) - -[Veo Stingray with Product ID 0x800C is also Model 2, but I haven't -observed this particular flaw in it.] - -Model 2 camera can be programmed for very high sensitivity (even starlight -may be enough), this makes it convenient for tinkering with. The driver -code has enough comments to help a programmer to tweak the camera -as s/he feels necessary. - -WHAT YOU NEED: - -- A supported IBM PC (C-it) camera (model 1 or 2) - -- A Linux box with USB support (2.3/2.4; 2.2 w/backport may work) - -- A Video4Linux compatible frame grabber program such as xawtv. - -HOW TO COMPILE THE DRIVER: - -You need to compile the driver only if you are a developer -or if you want to make changes to the code. Most distributions -precompile all modules, so you can go directly to the next -section "HOW TO USE THE DRIVER". - -The ibmcam driver uses usbvideo helper library (module), -so if you are studying the ibmcam code you will be led there. - -The driver itself consists of only one file in usb/ directory: -ibmcam.c. This file is included into the Linux kernel build -process if you configure the kernel for CONFIG_USB_IBMCAM. -Run "make xconfig" and in USB section you will find the IBM -camera driver. Select it, save the configuration and recompile. - -HOW TO USE THE DRIVER: - -I recommend to compile driver as a module. This gives you an -easier access to its configuration. The camera has many more -settings than V4L can operate, so some settings are done using -module options. - -To begin with, on most modern Linux distributions the driver -will be automatically loaded whenever you plug the supported -camera in. Therefore, you don't need to do anything. However -if you want to experiment with some module parameters then -you can load and unload the driver manually, with camera -plugged in or unplugged. - -Typically module is installed with command 'modprobe', like this: - -# modprobe ibmcam framerate=1 - -Alternatively you can use 'insmod' in similar fashion: - -# insmod /lib/modules/2.x.y/usb/ibmcam.o framerate=1 - -Module can be inserted with camera connected or disconnected. - -The driver can have options, though some defaults are provided. - -Driver options: (* indicates that option is model-dependent) - -Name Type Range [default] Example --------------- -------------- -------------- ------------------ -debug Integer 0-9 [0] debug=1 -flags Integer 0-0xFF [0] flags=0x0d -framerate Integer 0-6 [2] framerate=1 -hue_correction Integer 0-255 [128] hue_correction=115 -init_brightness Integer 0-255 [128] init_brightness=100 -init_contrast Integer 0-255 [192] init_contrast=200 -init_color Integer 0-255 [128] init_color=130 -init_hue Integer 0-255 [128] init_hue=115 -lighting Integer 0-2* [1] lighting=2 -sharpness Integer 0-6* [4] sharpness=3 -size Integer 0-2* [2] size=1 - -Options for Model 2 only: - -Name Type Range [default] Example --------------- -------------- -------------- ------------------ -init_model2_rg Integer 0..255 [0x70] init_model2_rg=128 -init_model2_rg2 Integer 0..255 [0x2f] init_model2_rg2=50 -init_model2_sat Integer 0..255 [0x34] init_model2_sat=65 -init_model2_yb Integer 0..255 [0xa0] init_model2_yb=200 - -debug You don't need this option unless you are a developer. - If you are a developer then you will see in the code - what values do what. 0=off. - -flags This is a bit mask, and you can combine any number of - bits to produce what you want. Usually you don't want - any of extra features this option provides: - - FLAGS_RETRY_VIDIOCSYNC 1 This bit allows to retry failed - VIDIOCSYNC ioctls without failing. - Will work with xawtv, will not - with xrealproducer. Default is - not set. - FLAGS_MONOCHROME 2 Activates monochrome (b/w) mode. - FLAGS_DISPLAY_HINTS 4 Shows colored pixels which have - magic meaning to developers. - FLAGS_OVERLAY_STATS 8 Shows tiny numbers on screen, - useful only for debugging. - FLAGS_FORCE_TESTPATTERN 16 Shows blue screen with numbers. - FLAGS_SEPARATE_FRAMES 32 Shows each frame separately, as - it was received from the camera. - Default (not set) is to mix the - preceding frame in to compensate - for occasional loss of Isoc data - on high frame rates. - FLAGS_CLEAN_FRAMES 64 Forces "cleanup" of each frame - prior to use; relevant only if - FLAGS_SEPARATE_FRAMES is set. - Default is not to clean frames, - this is a little faster but may - produce flicker if frame rate is - too high and Isoc data gets lost. - FLAGS_NO_DECODING 128 This flag turns the video stream - decoder off, and dumps the raw - Isoc data from the camera into - the reading process. Useful to - developers, but not to users. - -framerate This setting controls frame rate of the camera. This is - an approximate setting (in terms of "worst" ... "best") - because camera changes frame rate depending on amount - of light available. Setting 0 is slowest, 6 is fastest. - Beware - fast settings are very demanding and may not - work well with all video sizes. Be conservative. - -hue_correction This highly optional setting allows to adjust the - hue of the image in a way slightly different from - what usual "hue" control does. Both controls affect - YUV colorspace: regular "hue" control adjusts only - U component, and this "hue_correction" option similarly - adjusts only V component. However usually it is enough - to tweak only U or V to compensate for colored light or - color temperature; this option simply allows more - complicated correction when and if it is necessary. - -init_brightness These settings specify _initial_ values which will be -init_contrast used to set up the camera. If your V4L application has -init_color its own controls to adjust the picture then these -init_hue controls will be used too. These options allow you to - preconfigure the camera when it gets connected, before - any V4L application connects to it. Good for webcams. - -init_model2_rg These initial settings alter color balance of the -init_model2_rg2 camera on hardware level. All four settings may be used -init_model2_sat to tune the camera to specific lighting conditions. These -init_model2_yb settings only apply to Model 2 cameras. - -lighting This option selects one of three hardware-defined - photosensitivity settings of the camera. 0=bright light, - 1=Medium (default), 2=Low light. This setting affects - frame rate: the dimmer the lighting the lower the frame - rate (because longer exposition time is needed). The - Model 2 cameras allow values more than 2 for this option, - thus enabling extremely high sensitivity at cost of frame - rate, color saturation and imaging sensor noise. - -sharpness This option controls smoothing (noise reduction) - made by camera. Setting 0 is most smooth, setting 6 - is most sharp. Be aware that CMOS sensor used in the - camera is pretty noisy, so if you choose 6 you will - be greeted with "snowy" image. Default is 4. Model 2 - cameras do not support this feature. - -size This setting chooses one of several image sizes that are - supported by this driver. Cameras may support more, but - it's difficult to reverse-engineer all formats. - Following video sizes are supported: - - size=0 128x96 (Model 1 only) - size=1 160x120 - size=2 176x144 - size=3 320x240 (Model 2 only) - size=4 352x240 (Model 2 only) - size=5 352x288 - size=6 640x480 (Model 3 only) - - The 352x288 is the native size of the Model 1 sensor - array, so it's the best resolution the camera can - yield. The best resolution of Model 2 is 176x144, and - larger images are produced by stretching the bitmap. - Model 3 has sensor with 640x480 grid, and it works too, - but the frame rate will be exceptionally low (1-2 FPS); - it may be still OK for some applications, like security. - Choose the image size you need. The smaller image can - support faster frame rate. Default is 352x288. - -For more information and the Troubleshooting FAQ visit this URL: - - http://www.linux-usb.org/ibmcam/ - -WHAT NEEDS TO BE DONE: - -- The button on the camera is not used. I don't know how to get to it. - I know now how to read button on Model 2, but what to do with it? - -- Camera reports its status back to the driver; however I don't know - what returned data means. If camera fails at some initialization - stage then something should be done, and I don't do that because - I don't even know that some command failed. This is mostly Model 1 - concern because Model 2 uses different commands which do not return - status (and seem to complete successfully every time). - -- Some flavors of Model 4 NetCameras produce only compressed video - streams, and I don't know how to decode them. - -CREDITS: - -The code is based in no small part on the CPiA driver by Johannes Erdfelt, -Randy Dunlap, and others. Big thanks to them for their pioneering work on that -and the USB stack. - -I also thank John Lightsey for his donation of the Veo Stingray camera. diff --git a/Documentation/video4linux/m5602.txt b/Documentation/video4linux/m5602.txt deleted file mode 100644 index 4450ab13f37b..000000000000 --- a/Documentation/video4linux/m5602.txt +++ /dev/null @@ -1,12 +0,0 @@ -This document describes the ALi m5602 bridge connected -to the following supported sensors: -OmniVision OV9650, -Samsung s5k83a, -Samsung s5k4aa, -Micron mt9m111, -Pixel plus PO1030 - -This driver mimics the windows drivers, which have a braindead implementation sending bayer-encoded frames at VGA resolution. -In a perfect world we should be able to reprogram the m5602 and the connected sensor in hardware instead, supporting a range of resolutions and pixelformats - -Anyway, have fun and please report any bugs to m560x-driver-devel@lists.sourceforge.net diff --git a/Documentation/video4linux/ov511.txt b/Documentation/video4linux/ov511.txt deleted file mode 100644 index b3326b167ada..000000000000 --- a/Documentation/video4linux/ov511.txt +++ /dev/null @@ -1,288 +0,0 @@ -------------------------------------------------------------------------------- -Readme for Linux device driver for the OmniVision OV511 USB to camera bridge IC -------------------------------------------------------------------------------- - -Author: Mark McClelland -Homepage: http://alpha.dyndns.org/ov511 - -INTRODUCTION: - -This is a driver for the OV511, a USB-only chip used in many "webcam" devices. -Any camera using the OV511/OV511+ and the OV6620/OV7610/20/20AE should work. -Video capture devices that use the Philips SAA7111A decoder also work. It -supports streaming and capture of color or monochrome video via the Video4Linux -API. Most V4L apps are compatible with it. Most resolutions with a width and -height that are a multiple of 8 are supported. - -If you need more information, please visit the OV511 homepage at the above URL. - -WHAT YOU NEED: - -- If you want to help with the development, get the chip's specification docs at - http://www.ovt.com/omniusbp.html - -- A Video4Linux compatible frame grabber program (I recommend vidcat and xawtv) - vidcat is part of the w3cam package: http://mpx.freeshell.net/ - xawtv is available at: http://linux.bytesex.org/xawtv/ - -HOW TO USE IT: - -Note: These are simplified instructions. For complete instructions see: - http://alpha.dyndns.org/ov511/install.html - -You must have first compiled USB support, support for your specific USB host -controller (UHCI or OHCI), and Video4Linux support for your kernel (I recommend -making them modules.) Make sure "Enforce bandwidth allocation" is NOT enabled. - -Next, (as root): - - modprobe usbcore - modprobe usb-uhci <OR> modprobe usb-ohci - modprobe videodev - modprobe ov511 - -If it is not already there (it usually is), create the video device: - - mknod /dev/video0 c 81 0 - -Optionally, symlink /dev/video to /dev/video0 - -You will have to set permissions on this device to allow you to read/write -from it: - - chmod 666 /dev/video - chmod 666 /dev/video0 (if necessary) - -Now you are ready to run a video app! Both vidcat and xawtv work well for me -at 640x480. - -[Using vidcat:] - - vidcat -s 640x480 -p c > test.jpg - xview test.jpg - -[Using xawtv:] - -From the main xawtv directory: - - make clean - ./configure - make - make install - -Now you should be able to run xawtv. Right click for the options dialog. - -MODULE PARAMETERS: - - You can set these with: insmod ov511 NAME=VALUE - There is currently no way to set these on a per-camera basis. - - NAME: autobright - TYPE: integer (Boolean) - DEFAULT: 1 - DESC: Brightness is normally under automatic control and can't be set - manually by the video app. Set to 0 for manual control. - - NAME: autogain - TYPE: integer (Boolean) - DEFAULT: 1 - DESC: Auto Gain Control enable. This feature is not yet implemented. - - NAME: autoexp - TYPE: integer (Boolean) - DEFAULT: 1 - DESC: Auto Exposure Control enable. This feature is not yet implemented. - - NAME: debug - TYPE: integer (0-6) - DEFAULT: 3 - DESC: Sets the threshold for printing debug messages. The higher the value, - the more is printed. The levels are cumulative, and are as follows: - 0=no debug messages - 1=init/detection/unload and other significant messages - 2=some warning messages - 3=config/control function calls - 4=most function calls and data parsing messages - 5=highly repetitive mesgs - - NAME: snapshot - TYPE: integer (Boolean) - DEFAULT: 0 - DESC: Set to 1 to enable snapshot mode. read()/VIDIOCSYNC will block until - the snapshot button is pressed. Note: enabling this mode disables - /proc/video/ov511/<minor#>/button - - NAME: cams - TYPE: integer (1-4 for OV511, 1-31 for OV511+) - DEFAULT: 1 - DESC: Number of cameras allowed to stream simultaneously on a single bus. - Values higher than 1 reduce the data rate of each camera, allowing two - or more to be used at once. If you have a complicated setup involving - both OV511 and OV511+ cameras, trial-and-error may be necessary for - finding the optimum setting. - - NAME: compress - TYPE: integer (Boolean) - DEFAULT: 0 - DESC: Set this to 1 to turn on the camera's compression engine. This can - potentially increase the frame rate at the expense of quality, if you - have a fast CPU. You must load the proper compression module for your - camera before starting your application (ov511_decomp or ov518_decomp). - - NAME: testpat - TYPE: integer (Boolean) - DEFAULT: 0 - DESC: This configures the camera's sensor to transmit a colored test-pattern - instead of an image. This does not work correctly yet. - - NAME: dumppix - TYPE: integer (0-2) - DEFAULT: 0 - DESC: Dumps raw pixel data and skips post-processing and format conversion. - It is for debugging purposes only. Options are: - 0: Disable (default) - 1: Dump raw data from camera, excluding headers and trailers - 2: Dumps data exactly as received from camera - - NAME: led - TYPE: integer (0-2) - DEFAULT: 1 (Always on) - DESC: Controls whether the LED (the little light) on the front of the camera - is always off (0), always on (1), or only on when driver is open (2). - This is not supported with the OV511, and might only work with certain - cameras (ones that actually have the LED wired to the control pin, and - not just hard-wired to be on all the time). - - NAME: dump_bridge - TYPE: integer (Boolean) - DEFAULT: 0 - DESC: Dumps the bridge (OV511[+] or OV518[+]) register values to the system - log. Only useful for serious debugging/development purposes. - - NAME: dump_sensor - TYPE: integer (Boolean) - DEFAULT: 0 - DESC: Dumps the sensor register values to the system log. Only useful for - serious debugging/development purposes. - - NAME: printph - TYPE: integer (Boolean) - DEFAULT: 0 - DESC: Setting this to 1 will dump the first 12 bytes of each isoc frame. This - is only useful if you are trying to debug problems with the isoc data - stream (i.e.: camera initializes, but vidcat hangs until Ctrl-C). Be - warned that this dumps a large number of messages to your kernel log. - - NAME: phy, phuv, pvy, pvuv, qhy, qhuv, qvy, qvuv - TYPE: integer (0-63 for phy and phuv, 0-255 for rest) - DEFAULT: OV511 default values - DESC: These are registers 70h - 77h of the OV511, which control the - prediction ranges and quantization thresholds of the compressor, for - the Y and UV channels in the horizontal and vertical directions. See - the OV511 or OV511+ data sheet for more detailed descriptions. These - normally do not need to be changed. - - NAME: lightfreq - TYPE: integer (0, 50, or 60) - DEFAULT: 0 (use sensor default) - DESC: Sets the sensor to match your lighting frequency. This can reduce the - appearance of "banding", i.e. horizontal lines or waves of light and - dark that are often caused by artificial lighting. Valid values are: - 0 - Use default (depends on sensor, most likely 60 Hz) - 50 - For European and Asian 50 Hz power - 60 - For American 60 Hz power - - NAME: bandingfilter - TYPE: integer (Boolean) - DEFAULT: 0 (off) - DESC: Enables the sensor´s banding filter exposure algorithm. This reduces - or stabilizes the "banding" caused by some artificial light sources - (especially fluorescent). You might have to set lightfreq correctly for - this to work right. As an added bonus, this sometimes makes it - possible to capture your monitor´s output. - - NAME: fastset - TYPE: integer (Boolean) - DEFAULT: 0 (off) - DESC: Allows picture settings (brightness, contrast, color, and hue) to take - effect immediately, even in the middle of a frame. This reduces the - time to change settings, but can ruin frames during the change. Only - affects OmniVision sensors. - - NAME: force_palette - TYPE: integer (Boolean) - DEFAULT: 0 (off) - DESC: Forces the palette (color format) to a specific value. If an - application requests a different palette, it will be rejected, thereby - forcing it to try others until it succeeds. This is useful for forcing - greyscale mode with a color camera, for example. Supported modes are: - 0 (Allows all the following formats) - 1 VIDEO_PALETTE_GREY (Linear greyscale) - 10 VIDEO_PALETTE_YUV420 (YUV 4:2:0 Planar) - 15 VIDEO_PALETTE_YUV420P (YUV 4:2:0 Planar, same as 10) - - NAME: backlight - TYPE: integer (Boolean) - DEFAULT: 0 (off) - DESC: Setting this flag changes the exposure algorithm for OmniVision sensors - such that objects in the camera's view (i.e. your head) can be clearly - seen when they are illuminated from behind. It reduces or eliminates - the sensor's auto-exposure function, so it should only be used when - needed. Additionally, it is only supported with the OV6620 and OV7620. - - NAME: unit_video - TYPE: Up to 16 comma-separated integers - DEFAULT: 0,0,0... (automatically assign the next available minor(s)) - DESC: You can specify up to 16 minor numbers to be assigned to ov511 devices. - For example, "unit_video=1,3" will make the driver use /dev/video1 and - /dev/video3 for the first two devices it detects. Additional devices - will be assigned automatically starting at the first available device - node (/dev/video0 in this case). Note that you cannot specify 0 as a - minor number. This feature requires kernel version 2.4.5 or higher. - - NAME: remove_zeros - TYPE: integer (Boolean) - DEFAULT: 0 (do not skip any incoming data) - DESC: Setting this to 1 will remove zero-padding from incoming data. This - will compensate for the blocks of corruption that can appear when the - camera cannot keep up with the speed of the USB bus (eg. at low frame - resolutions). This feature is always enabled when compression is on. - - NAME: mirror - TYPE: integer (Boolean) - DEFAULT: 0 (off) - DESC: Setting this to 1 will reverse ("mirror") the image horizontally. This - might be necessary if your camera has a custom lens assembly. This has - no effect with video capture devices. - - NAME: ov518_color - TYPE: integer (Boolean) - DEFAULT: 0 (off) - DESC: Enable OV518 color support. This is off by default since it doesn't - work most of the time. If you want to try it, you must also load - ov518_decomp with the "nouv=0" parameter. If you get improper colors or - diagonal lines through the image, restart your video app and try again. - Repeat as necessary. - -WORKING FEATURES: - o Color streaming/capture at most widths and heights that are multiples of 8. - o Monochrome (use force_palette=1 to enable) - o Setting/getting of saturation, contrast, brightness, and hue (only some of - them work the OV7620 and OV7620AE) - o /proc status reporting - o SAA7111A video capture support at 320x240 and 640x480 - o Compression support - o SMP compatibility - -HOW TO CONTACT ME: - -You can email me at mark@alpha.dyndns.org . Please prefix the subject line -with "OV511: " so that I am certain to notice your message. - -CREDITS: - -The code is based in no small part on the CPiA driver by Johannes Erdfelt, -Randy Dunlap, and others. Big thanks to them for their pioneering work on that -and the USB stack. Thanks to Bret Wallach for getting camera reg IO, ISOC, and -image capture working. Thanks to Orion Sky Lawlor, Kevin Moore, and Claudio -Matsuoka for their work as well. diff --git a/Documentation/video4linux/se401.txt b/Documentation/video4linux/se401.txt deleted file mode 100644 index bd6526ec8dd7..000000000000 --- a/Documentation/video4linux/se401.txt +++ /dev/null @@ -1,54 +0,0 @@ -Linux driver for SE401 based USB cameras - -Copyright, 2001, Jeroen Vreeken - - -INTRODUCTION: - -The SE401 chip is the used in low-cost usb webcams. -It is produced by Endpoints Inc. (www.endpoints.com). -It interfaces directly to a cmos image sensor and USB. The only other major -part in a se401 based camera is a dram chip. - -The following cameras are known to work with this driver: - -Aox se401 (non-branded) cameras -Philips PVCV665 USB VGA webcam 'Vesta Fun' -Kensington VideoCAM PC Camera Model 67014 -Kensington VideoCAM PC Camera Model 67015 -Kensington VideoCAM PC Camera Model 67016 -Kensington VideoCAM PC Camera Model 67017 - - -WHAT YOU NEED: - -- USB support -- VIDEO4LINUX support - -More information about USB support for linux can be found at: -http://www.linux-usb.org - - -MODULE OPTIONS: - -When the driver is compiled as a module you can also use the 'flickerless' -option. With it exposure is limited to values that do not interfere with the -net frequency. Valid options for this option are 0, 50 and 60. (0=disable, -50=50hz, 60=60hz) - - -KNOWN PROBLEMS: - -The driver works fine with the usb-ohci and uhci host controller drivers, -the default settings also work with usb-uhci. But sending more than one bulk -transfer at a time with usb-uhci doesn't work yet. -Users of usb-ohci and uhci can safely enlarge SE401_NUMSBUF in se401.h in -order to increase the throughput (and thus framerate). - - -HELP: - -The latest info on this driver can be found at: -http://members.chello.nl/~j.vreeken/se401/ -And questions to me can be send to: -pe1rxq@amsat.org diff --git a/Documentation/video4linux/soc-camera.txt b/Documentation/video4linux/soc-camera.txt index 3f87c7da4ca2..f62fcdbc8b9f 100644 --- a/Documentation/video4linux/soc-camera.txt +++ b/Documentation/video4linux/soc-camera.txt @@ -9,32 +9,36 @@ The following terms are used in this document: of connecting to a variety of systems and interfaces, typically uses i2c for control and configuration, and a parallel or a serial bus for data. - camera host - an interface, to which a camera is connected. Typically a - specialised interface, present on many SoCs, e.g., PXA27x and PXA3xx, SuperH, + specialised interface, present on many SoCs, e.g. PXA27x and PXA3xx, SuperH, AVR32, i.MX27, i.MX31. - camera host bus - a connection between a camera host and a camera. Can be - parallel or serial, consists of data and control lines, e.g., clock, vertical + parallel or serial, consists of data and control lines, e.g. clock, vertical and horizontal synchronization signals. Purpose of the soc-camera subsystem ----------------------------------- -The soc-camera subsystem provides a unified API between camera host drivers and -camera sensor drivers. It implements a V4L2 interface to the user, currently -only the mmap method is supported. +The soc-camera subsystem initially provided a unified API between camera host +drivers and camera sensor drivers. Later the soc-camera sensor API has been +replaced with the V4L2 standard subdev API. This also made camera driver re-use +with non-soc-camera hosts possible. The camera host API to the soc-camera core +has been preserved. -This subsystem has been written to connect drivers for System-on-Chip (SoC) -video capture interfaces with drivers for CMOS camera sensor chips to enable -the reuse of sensor drivers with various hosts. The subsystem has been designed -to support multiple camera host interfaces and multiple cameras per interface, -although most applications have only one camera sensor. +Soc-camera implements a V4L2 interface to the user, currently only the "mmap" +method is supported by host drivers. However, the soc-camera core also provides +support for the "read" method. + +The subsystem has been designed to support multiple camera host interfaces and +multiple cameras per interface, although most applications have only one camera +sensor. Existing drivers ---------------- -As of 2.6.27-rc4 there are two host drivers in the mainline: pxa_camera.c for -PXA27x SoCs and sh_mobile_ceu_camera.c for SuperH SoCs, and four sensor drivers: -mt9m001.c, mt9m111.c, mt9v022.c and a generic soc_camera_platform.c driver. This -list is not supposed to be updated, look for more examples in your tree. +As of 3.7 there are seven host drivers in the mainline: atmel-isi.c, +mx1_camera.c (broken, scheduled for removal), mx2_camera.c, mx3_camera.c, +omap1_camera.c, pxa_camera.c, sh_mobile_ceu_camera.c, and multiple sensor +drivers under drivers/media/i2c/soc_camera/. Camera host API --------------- @@ -45,38 +49,37 @@ soc_camera_host_register(struct soc_camera_host *); function. The host object can be initialized as follows: -static struct soc_camera_host pxa_soc_camera_host = { - .drv_name = PXA_CAM_DRV_NAME, - .ops = &pxa_soc_camera_host_ops, -}; + struct soc_camera_host *ici; + ici->drv_name = DRV_NAME; + ici->ops = &camera_host_ops; + ici->priv = pcdev; + ici->v4l2_dev.dev = &pdev->dev; + ici->nr = pdev->id; All camera host methods are passed in a struct soc_camera_host_ops: -static struct soc_camera_host_ops pxa_soc_camera_host_ops = { +static struct soc_camera_host_ops camera_host_ops = { .owner = THIS_MODULE, - .add = pxa_camera_add_device, - .remove = pxa_camera_remove_device, - .suspend = pxa_camera_suspend, - .resume = pxa_camera_resume, - .set_fmt_cap = pxa_camera_set_fmt_cap, - .try_fmt_cap = pxa_camera_try_fmt_cap, - .init_videobuf = pxa_camera_init_videobuf, - .reqbufs = pxa_camera_reqbufs, - .poll = pxa_camera_poll, - .querycap = pxa_camera_querycap, - .try_bus_param = pxa_camera_try_bus_param, - .set_bus_param = pxa_camera_set_bus_param, + .add = camera_add_device, + .remove = camera_remove_device, + .set_fmt = camera_set_fmt_cap, + .try_fmt = camera_try_fmt_cap, + .init_videobuf2 = camera_init_videobuf2, + .poll = camera_poll, + .querycap = camera_querycap, + .set_bus_param = camera_set_bus_param, + /* The rest of host operations are optional */ }; .add and .remove methods are called when a sensor is attached to or detached -from the host, apart from performing host-internal tasks they shall also call -sensor driver's .init and .release methods respectively. .suspend and .resume -methods implement host's power-management functionality and its their -responsibility to call respective sensor's methods. .try_bus_param and -.set_bus_param are used to negotiate physical connection parameters between the -host and the sensor. .init_videobuf is called by soc-camera core when a -video-device is opened, further video-buffer management is implemented completely -by the specific camera host driver. The rest of the methods are called from +from the host. .set_bus_param is used to configure physical connection +parameters between the host and the sensor. .init_videobuf2 is called by +soc-camera core when a video-device is opened, the host driver would typically +call vb2_queue_init() in this method. Further video-buffer management is +implemented completely by the specific camera host driver. If the host driver +supports non-standard pixel format conversion, it should implement a +.get_formats and, possibly, a .put_formats operations. See below for more +details about format conversion. The rest of the methods are called from respective V4L2 operations. Camera API @@ -84,37 +87,21 @@ Camera API Sensor drivers can use struct soc_camera_link, typically provided by the platform, and used to specify to which camera host bus the sensor is connected, -and arbitrarily provide platform .power and .reset methods for the camera. -soc_camera_device_register() and soc_camera_device_unregister() functions are -used to add a sensor driver to or remove one from the system. The registration -function takes a pointer to struct soc_camera_device as the only parameter. -This struct can be initialized as follows: - - /* link to driver operations */ - icd->ops = &mt9m001_ops; - /* link to the underlying physical (e.g., i2c) device */ - icd->control = &client->dev; - /* window geometry */ - icd->x_min = 20; - icd->y_min = 12; - icd->x_current = 20; - icd->y_current = 12; - icd->width_min = 48; - icd->width_max = 1280; - icd->height_min = 32; - icd->height_max = 1024; - icd->y_skip_top = 1; - /* camera bus ID, typically obtained from platform data */ - icd->iface = icl->bus_id; - -struct soc_camera_ops provides .probe and .remove methods, which are called by -the soc-camera core, when a camera is matched against or removed from a camera -host bus, .init, .release, .suspend, and .resume are called from the camera host -driver as discussed above. Other members of this struct provide respective V4L2 -functionality. - -struct soc_camera_device also links to an array of struct soc_camera_data_format, -listing pixel formats, supported by the camera. +and optionally provide platform .power and .reset methods for the camera. This +struct is provided to the camera driver via the I2C client device platform data +and can be obtained, using the soc_camera_i2c_to_link() macro. Care should be +taken, when using soc_camera_vdev_to_subdev() and when accessing struct +soc_camera_device, using v4l2_get_subdev_hostdata(): both only work, when +running on an soc-camera host. The actual camera driver operation is implemented +using the V4L2 subdev API. Additionally soc-camera camera drivers can use +auxiliary soc-camera helper functions like soc_camera_power_on() and +soc_camera_power_off(), which switch regulators, provided by the platform and call +board-specific power switching methods. soc_camera_apply_board_flags() takes +camera bus configuration capability flags and applies any board transformations, +e.g. signal polarity inversion. soc_mbus_get_fmtdesc() can be used to obtain a +pixel format descriptor, corresponding to a certain media-bus pixel format code. +soc_camera_limit_side() can be used to restrict beginning and length of a frame +side, based on camera capabilities. VIDIOC_S_CROP and VIDIOC_S_FMT behaviour ---------------------------------------- @@ -153,8 +140,25 @@ implemented. User window geometry is kept in .user_width and .user_height fields in struct soc_camera_device and used by the soc-camera core and host drivers. The core updates these fields upon successful completion of a .s_fmt() call, but if these -fields change elsewhere, e.g., during .s_crop() processing, the host driver is +fields change elsewhere, e.g. during .s_crop() processing, the host driver is responsible for updating them. +Format conversion +----------------- + +V4L2 distinguishes between pixel formats, as they are stored in memory, and as +they are transferred over a media bus. Soc-camera provides support to +conveniently manage these formats. A table of standard transformations is +maintained by soc-camera core, which describes, what FOURCC pixel format will +be obtained, if a media-bus pixel format is stored in memory according to +certain rules. E.g. if V4L2_MBUS_FMT_YUYV8_2X8 data is sampled with 8 bits per +sample and stored in memory in the little-endian order with no gaps between +bytes, data in memory will represent the V4L2_PIX_FMT_YUYV FOURCC format. These +standard transformations will be used by soc-camera or by camera host drivers to +configure camera drivers to produce the FOURCC format, requested by the user, +using the VIDIOC_S_FMT ioctl(). Apart from those standard format conversions, +host drivers can also provide their own conversion rules by implementing a +.get_formats and, if required, a .put_formats methods. + -- Author: Guennadi Liakhovetski <g.liakhovetski@gmx.de> diff --git a/Documentation/video4linux/stv680.txt b/Documentation/video4linux/stv680.txt deleted file mode 100644 index e3de33645308..000000000000 --- a/Documentation/video4linux/stv680.txt +++ /dev/null @@ -1,53 +0,0 @@ -Linux driver for STV0680 based USB cameras - -Copyright, 2001, Kevin Sisson - - -INTRODUCTION: - -STMicroelectronics produces the STV0680B chip, which comes in two -types, -001 and -003. The -003 version allows the recording and downloading -of sound clips from the camera, and allows a flash attachment. Otherwise, -it uses the same commands as the -001 version. Both versions support a -variety of SDRAM sizes and sensors, allowing for a maximum of 26 VGA or 20 -CIF pictures. The STV0680 supports either a serial or a usb interface, and -video is possible through the usb interface. - -The following cameras are known to work with this driver, although any -camera with Vendor/Product codes of 0553/0202 should work: - -Aiptek Pencam (various models) -Nisis QuickPix 2 -Radio Shack 'Kid's digital camera' (#60-1207) -At least one Trust Spycam model -Several other European brand models - -WHAT YOU NEED: - -- USB support -- VIDEO4LINUX support - -More information about USB support for linux can be found at: -http://www.linux-usb.org - - -MODULE OPTIONS: - -When the driver is compiled as a module, you can set a "swapRGB=1" -option, if necessary, for those applications that require it -(such as xawtv). However, the driver should detect and set this -automatically, so this option should not normally be used. - - -KNOWN PROBLEMS: - -The driver seems to work better with the usb-ohci than the usb-uhci host -controller driver. - -HELP: - -The latest info on this driver can be found at: -http://personal.clt.bellsouth.net/~kjsisson or at -http://stv0680-usb.sourceforge.net - -Any questions to me can be send to: kjsisson@bellsouth.net diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index 32bfe926e8d7..0a1ef671c9d4 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -68,8 +68,7 @@ Structure of the framework The framework closely resembles the driver structure: it has a v4l2_device struct for the device instance data, a v4l2_subdev struct to refer to sub-device instances, the video_device struct stores V4L2 device node data -and in the future a v4l2_fh struct will keep track of filehandle instances -(this is not yet implemented). +and the v4l2_fh struct keeps track of filehandle instances. The V4L2 framework also optionally integrates with the media framework. If a driver sets the struct v4l2_device mdev field, sub-devices and video nodes diff --git a/Documentation/video4linux/w9968cf.txt b/Documentation/video4linux/w9968cf.txt deleted file mode 100644 index 9649450f3b90..000000000000 --- a/Documentation/video4linux/w9968cf.txt +++ /dev/null @@ -1,458 +0,0 @@ - - W996[87]CF JPEG USB Dual Mode Camera Chip - Driver for Linux 2.6 (basic version) - ========================================= - - - Documentation - - - -Index -===== -1. Copyright -2. Disclaimer -3. License -4. Overview -5. Supported devices -6. Module dependencies -7. Module loading -8. Module parameters -9. Contact information -10. Credits - - -1. Copyright -============ -Copyright (C) 2002-2004 by Luca Risolia <luca.risolia@studio.unibo.it> - - -2. Disclaimer -============= -Winbond is a trademark of Winbond Electronics Corporation. -This software is not sponsored or developed by Winbond. - - -3. License -========== -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. - - -4. Overview -=========== -This driver supports the video streaming capabilities of the devices mounting -Winbond W9967CF and Winbond W9968CF JPEG USB Dual Mode Camera Chips. OV681 -based cameras should be supported as well. - -The driver is divided into two modules: the basic one, "w9968cf", is needed for -the supported devices to work; the second one, "w9968cf-vpp", is an optional -module, which provides some useful video post-processing functions like video -decoding, up-scaling and colour conversions. - -Note that the official kernels do neither include nor support the second -module for performance purposes. Therefore, it is always recommended to -download and install the latest and complete release of the driver, -replacing the existing one, if present. - -The latest and full-featured version of the W996[87]CF driver can be found at: -http://www.linux-projects.org. Please refer to the documentation included in -that package, if you are going to use it. - -Up to 32 cameras can be handled at the same time. They can be connected and -disconnected from the host many times without turning off the computer, if -your system supports the hotplug facility. - -To change the default settings for each camera, many parameters can be passed -through command line when the module is loaded into memory. - -The driver relies on the Video4Linux, USB and I2C core modules. It has been -designed to run properly on SMP systems as well. An additional module, -"ovcamchip", is mandatory; it provides support for some OmniVision image -sensors connected to the W996[87]CF chips; if found in the system, the module -will be automatically loaded by default (provided that the kernel has been -compiled with the automatic module loading option). - - -5. Supported devices -==================== -At the moment, known W996[87]CF and OV681 based devices are: -- Aroma Digi Pen VGA Dual Mode ADG-5000 (unknown image sensor) -- AVerMedia AVerTV USB (SAA7111A, Philips FI1216Mk2 tuner, PT2313L audio chip) -- Creative Labs Video Blaster WebCam Go (OmniVision OV7610 sensor) -- Creative Labs Video Blaster WebCam Go Plus (OmniVision OV7620 sensor) -- Lebon LDC-035A (unknown image sensor) -- Ezonics EZ-802 EZMega Cam (OmniVision OV8610C sensor) -- OmniVision OV8610-EDE (OmniVision OV8610 sensor) -- OPCOM Digi Pen VGA Dual Mode Pen Camera (unknown image sensor) -- Pretec Digi Pen-II (OmniVision OV7620 sensor) -- Pretec DigiPen-480 (OmniVision OV8610 sensor) - -If you know any other W996[87]CF or OV681 based cameras, please contact me. - -The list above does not imply that all those devices work with this driver: up -until now only webcams that have an image sensor supported by the "ovcamchip" -module work. Kernel messages will always tell you whether this is case. - -Possible external microcontrollers of those webcams are not supported: this -means that still images cannot be downloaded from the device memory. - -Furthermore, it's worth to note that I was only able to run tests on my -"Creative Labs Video Blaster WebCam Go". Donations of other models, for -additional testing and full support, would be much appreciated. - - -6. Module dependencies -====================== -For it to work properly, the driver needs kernel support for Video4Linux, USB -and I2C, and the "ovcamchip" module for the image sensor. Make sure you are not -actually using any external "ovcamchip" module, given that the W996[87]CF -driver depends on the version of the module present in the official kernels. - -The following options of the kernel configuration file must be enabled and -corresponding modules must be compiled: - - # Multimedia devices - # - CONFIG_VIDEO_DEV=m - - # I2C support - # - CONFIG_I2C=m - -The I2C core module can be compiled statically in the kernel as well. - - # OmniVision Camera Chip support - # - CONFIG_VIDEO_OVCAMCHIP=m - - # USB support - # - CONFIG_USB=m - -In addition, depending on the hardware being used, only one of the modules -below is necessary: - - # USB Host Controller Drivers - # - CONFIG_USB_EHCI_HCD=m - CONFIG_USB_UHCI_HCD=m - CONFIG_USB_OHCI_HCD=m - -And finally: - - # USB Multimedia devices - # - CONFIG_USB_W9968CF=m - - -7. Module loading -================= -To use the driver, it is necessary to load the "w9968cf" module into memory -after every other module required. - -Loading can be done this way, from root: - - [root@localhost home]# modprobe usbcore - [root@localhost home]# modprobe i2c-core - [root@localhost home]# modprobe videodev - [root@localhost home]# modprobe w9968cf - -At this point the pertinent devices should be recognized: "dmesg" can be used -to analyze kernel messages: - - [user@localhost home]$ dmesg - -There are a lot of parameters the module can use to change the default -settings for each device. To list every possible parameter with a brief -explanation about them and which syntax to use, it is recommended to run the -"modinfo" command: - - [root@locahost home]# modinfo w9968cf - - -8. Module parameters -==================== -Module parameters are listed below: -------------------------------------------------------------------------------- -Name: ovmod_load -Type: bool -Syntax: <0|1> -Description: Automatic 'ovcamchip' module loading: 0 disabled, 1 enabled. - If enabled, 'insmod' searches for the required 'ovcamchip' - module in the system, according to its configuration, and - loads that module automatically. This action is performed as - once soon as the 'w9968cf' module is loaded into memory. -Default: 1 -------------------------------------------------------------------------------- -Name: simcams -Type: int -Syntax: <n> -Description: Number of cameras allowed to stream simultaneously. - n may vary from 0 to 32. -Default: 32 -------------------------------------------------------------------------------- -Name: video_nr -Type: int array (min = 0, max = 32) -Syntax: <-1|n[,...]> -Description: Specify V4L minor mode number. - -1 = use next available - n = use minor number n - You can specify up to 32 cameras this way. - For example: - video_nr=-1,2,-1 would assign minor number 2 to the second - recognized camera and use auto for the first one and for every - other camera. -Default: -1 -------------------------------------------------------------------------------- -Name: packet_size -Type: int array (min = 0, max = 32) -Syntax: <n[,...]> -Description: Specify the maximum data payload size in bytes for alternate - settings, for each device. n is scaled between 63 and 1023. -Default: 1023 -------------------------------------------------------------------------------- -Name: max_buffers -Type: int array (min = 0, max = 32) -Syntax: <n[,...]> -Description: For advanced users. - Specify the maximum number of video frame buffers to allocate - for each device, from 2 to 32. -Default: 2 -------------------------------------------------------------------------------- -Name: double_buffer -Type: bool array (min = 0, max = 32) -Syntax: <0|1[,...]> -Description: Hardware double buffering: 0 disabled, 1 enabled. - It should be enabled if you want smooth video output: if you - obtain out of sync. video, disable it, or try to - decrease the 'clockdiv' module parameter value. -Default: 1 for every device. -------------------------------------------------------------------------------- -Name: clamping -Type: bool array (min = 0, max = 32) -Syntax: <0|1[,...]> -Description: Video data clamping: 0 disabled, 1 enabled. -Default: 0 for every device. -------------------------------------------------------------------------------- -Name: filter_type -Type: int array (min = 0, max = 32) -Syntax: <0|1|2[,...]> -Description: Video filter type. - 0 none, 1 (1-2-1) 3-tap filter, 2 (2-3-6-3-2) 5-tap filter. - The filter is used to reduce noise and aliasing artifacts - produced by the CCD or CMOS image sensor. -Default: 0 for every device. -------------------------------------------------------------------------------- -Name: largeview -Type: bool array (min = 0, max = 32) -Syntax: <0|1[,...]> -Description: Large view: 0 disabled, 1 enabled. -Default: 1 for every device. -------------------------------------------------------------------------------- -Name: upscaling -Type: bool array (min = 0, max = 32) -Syntax: <0|1[,...]> -Description: Software scaling (for non-compressed video only): - 0 disabled, 1 enabled. - Disable it if you have a slow CPU or you don't have enough - memory. -Default: 0 for every device. -Note: If 'w9968cf-vpp' is not present, this parameter is set to 0. -------------------------------------------------------------------------------- -Name: decompression -Type: int array (min = 0, max = 32) -Syntax: <0|1|2[,...]> -Description: Software video decompression: - 0 = disables decompression - (doesn't allow formats needing decompression). - 1 = forces decompression - (allows formats needing decompression only). - 2 = allows any permitted formats. - Formats supporting (de)compressed video are YUV422P and - YUV420P/YUV420 in any resolutions where width and height are - multiples of 16. -Default: 2 for every device. -Note: If 'w9968cf-vpp' is not present, forcing decompression is not - allowed; in this case this parameter is set to 2. -------------------------------------------------------------------------------- -Name: force_palette -Type: int array (min = 0, max = 32) -Syntax: <0|9|10|13|15|8|7|1|6|3|4|5[,...]> -Description: Force picture palette. - In order: - 0 = Off - allows any of the following formats: - 9 = UYVY 16 bpp - Original video, compression disabled - 10 = YUV420 12 bpp - Original video, compression enabled - 13 = YUV422P 16 bpp - Original video, compression enabled - 15 = YUV420P 12 bpp - Original video, compression enabled - 8 = YUVY 16 bpp - Software conversion from UYVY - 7 = YUV422 16 bpp - Software conversion from UYVY - 1 = GREY 8 bpp - Software conversion from UYVY - 6 = RGB555 16 bpp - Software conversion from UYVY - 3 = RGB565 16 bpp - Software conversion from UYVY - 4 = RGB24 24 bpp - Software conversion from UYVY - 5 = RGB32 32 bpp - Software conversion from UYVY - When not 0, this parameter will override 'decompression'. -Default: 0 for every device. Initial palette is 9 (UYVY). -Note: If 'w9968cf-vpp' is not present, this parameter is set to 9. -------------------------------------------------------------------------------- -Name: force_rgb -Type: bool array (min = 0, max = 32) -Syntax: <0|1[,...]> -Description: Read RGB video data instead of BGR: - 1 = use RGB component ordering. - 0 = use BGR component ordering. - This parameter has effect when using RGBX palettes only. -Default: 0 for every device. -------------------------------------------------------------------------------- -Name: autobright -Type: bool array (min = 0, max = 32) -Syntax: <0|1[,...]> -Description: Image sensor automatically changes brightness: - 0 = no, 1 = yes -Default: 0 for every device. -------------------------------------------------------------------------------- -Name: autoexp -Type: bool array (min = 0, max = 32) -Syntax: <0|1[,...]> -Description: Image sensor automatically changes exposure: - 0 = no, 1 = yes -Default: 1 for every device. -------------------------------------------------------------------------------- -Name: lightfreq -Type: int array (min = 0, max = 32) -Syntax: <50|60[,...]> -Description: Light frequency in Hz: - 50 for European and Asian lighting, 60 for American lighting. -Default: 50 for every device. -------------------------------------------------------------------------------- -Name: bandingfilter -Type: bool array (min = 0, max = 32) -Syntax: <0|1[,...]> -Description: Banding filter to reduce effects of fluorescent - lighting: - 0 disabled, 1 enabled. - This filter tries to reduce the pattern of horizontal - light/dark bands caused by some (usually fluorescent) lighting. -Default: 0 for every device. -------------------------------------------------------------------------------- -Name: clockdiv -Type: int array (min = 0, max = 32) -Syntax: <-1|n[,...]> -Description: Force pixel clock divisor to a specific value (for experts): - n may vary from 0 to 127. - -1 for automatic value. - See also the 'double_buffer' module parameter. -Default: -1 for every device. -------------------------------------------------------------------------------- -Name: backlight -Type: bool array (min = 0, max = 32) -Syntax: <0|1[,...]> -Description: Objects are lit from behind: - 0 = no, 1 = yes -Default: 0 for every device. -------------------------------------------------------------------------------- -Name: mirror -Type: bool array (min = 0, max = 32) -Syntax: <0|1[,...]> -Description: Reverse image horizontally: - 0 = no, 1 = yes -Default: 0 for every device. -------------------------------------------------------------------------------- -Name: monochrome -Type: bool array (min = 0, max = 32) -Syntax: <0|1[,...]> -Description: The image sensor is monochrome: - 0 = no, 1 = yes -Default: 0 for every device. -------------------------------------------------------------------------------- -Name: brightness -Type: long array (min = 0, max = 32) -Syntax: <n[,...]> -Description: Set picture brightness (0-65535). - This parameter has no effect if 'autobright' is enabled. -Default: 31000 for every device. -------------------------------------------------------------------------------- -Name: hue -Type: long array (min = 0, max = 32) -Syntax: <n[,...]> -Description: Set picture hue (0-65535). -Default: 32768 for every device. -------------------------------------------------------------------------------- -Name: colour -Type: long array (min = 0, max = 32) -Syntax: <n[,...]> -Description: Set picture saturation (0-65535). -Default: 32768 for every device. -------------------------------------------------------------------------------- -Name: contrast -Type: long array (min = 0, max = 32) -Syntax: <n[,...]> -Description: Set picture contrast (0-65535). -Default: 50000 for every device. -------------------------------------------------------------------------------- -Name: whiteness -Type: long array (min = 0, max = 32) -Syntax: <n[,...]> -Description: Set picture whiteness (0-65535). -Default: 32768 for every device. -------------------------------------------------------------------------------- -Name: debug -Type: int -Syntax: <n> -Description: Debugging information level, from 0 to 6: - 0 = none (use carefully) - 1 = critical errors - 2 = significant information - 3 = configuration or general messages - 4 = warnings - 5 = called functions - 6 = function internals - Level 5 and 6 are useful for testing only, when only one - device is used. -Default: 2 -------------------------------------------------------------------------------- -Name: specific_debug -Type: bool -Syntax: <0|1> -Description: Enable or disable specific debugging messages: - 0 = print messages concerning every level <= 'debug' level. - 1 = print messages concerning the level indicated by 'debug'. -Default: 0 -------------------------------------------------------------------------------- - - -9. Contact information -====================== -I may be contacted by e-mail at <luca.risolia@studio.unibo.it>. - -I can accept GPG/PGP encrypted e-mail. My GPG key ID is 'FCE635A4'. -My public 1024-bit key should be available at your keyserver; the fingerprint -is: '88E8 F32F 7244 68BA 3958 5D40 99DA 5D2A FCE6 35A4'. - - -10. Credits -========== -The development would not have proceed much further without having looked at -the source code of other drivers and without the help of several persons; in -particular: - -- the I2C interface to kernel and high-level image sensor control routines have - been taken from the OV511 driver by Mark McClelland; - -- memory management code has been copied from the bttv driver by Ralph Metzler, - Marcus Metzler and Gerd Knorr; - -- the low-level I2C read function has been written by Frederic Jouault; - -- the low-level I2C fast write function has been written by Piotr Czerczak. diff --git a/Documentation/video4linux/zc0301.txt b/Documentation/video4linux/zc0301.txt deleted file mode 100644 index b41c83cf09f4..000000000000 --- a/Documentation/video4linux/zc0301.txt +++ /dev/null @@ -1,270 +0,0 @@ - - ZC0301 and ZC0301P Image Processor and Control Chip - Driver for Linux - =================================================== - - - Documentation - - - -Index -===== -1. Copyright -2. Disclaimer -3. License -4. Overview and features -5. Module dependencies -6. Module loading -7. Module parameters -8. Supported devices -9. Notes for V4L2 application developers -10. Contact information -11. Credits - - -1. Copyright -============ -Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> - - -2. Disclaimer -============= -This software is not developed or sponsored by Z-Star Microelectronics Corp. -Trademarks are property of their respective owner. - - -3. License -========== -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. - - -4. Overview and features -======================== -This driver supports the video interface of the devices mounting the ZC0301 or -ZC0301P Image Processors and Control Chips. - -The driver relies on the Video4Linux2 and USB core modules. It has been -designed to run properly on SMP systems as well. - -The latest version of the ZC0301[P] driver can be found at the following URL: -http://www.linux-projects.org/ - -Some of the features of the driver are: - -- full compliance with the Video4Linux2 API (see also "Notes for V4L2 - application developers" paragraph); -- available mmap or read/poll methods for video streaming through isochronous - data transfers; -- automatic detection of image sensor; -- video format is standard JPEG; -- dynamic driver control thanks to various module parameters (see "Module - parameters" paragraph); -- up to 64 cameras can be handled at the same time; they can be connected and - disconnected from the host many times without turning off the computer, if - the system supports hotplugging; - - -5. Module dependencies -====================== -For it to work properly, the driver needs kernel support for Video4Linux and -USB. - -The following options of the kernel configuration file must be enabled and -corresponding modules must be compiled: - - # Multimedia devices - # - CONFIG_VIDEO_DEV=m - - # USB support - # - CONFIG_USB=m - -In addition, depending on the hardware being used, the modules below are -necessary: - - # USB Host Controller Drivers - # - CONFIG_USB_EHCI_HCD=m - CONFIG_USB_UHCI_HCD=m - CONFIG_USB_OHCI_HCD=m - -The ZC0301 controller also provides a built-in microphone interface. It is -supported by the USB Audio driver thanks to the ALSA API: - - # Sound - # - CONFIG_SOUND=y - - # Advanced Linux Sound Architecture - # - CONFIG_SND=m - - # USB devices - # - CONFIG_SND_USB_AUDIO=m - -And finally: - - # V4L USB devices - # - CONFIG_USB_ZC0301=m - - -6. Module loading -================= -To use the driver, it is necessary to load the "zc0301" module into memory -after every other module required: "videodev", "v4l2_common", "compat_ioctl32", -"usbcore" and, depending on the USB host controller you have, "ehci-hcd", -"uhci-hcd" or "ohci-hcd". - -Loading can be done as shown below: - - [root@localhost home]# modprobe zc0301 - -At this point the devices should be recognized. You can invoke "dmesg" to -analyze kernel messages and verify that the loading process has gone well: - - [user@localhost home]$ dmesg - - -7. Module parameters -==================== -Module parameters are listed below: -------------------------------------------------------------------------------- -Name: video_nr -Type: short array (min = 0, max = 64) -Syntax: <-1|n[,...]> -Description: Specify V4L2 minor mode number: - -1 = use next available - n = use minor number n - You can specify up to 64 cameras this way. - For example: - video_nr=-1,2,-1 would assign minor number 2 to the second - registered camera and use auto for the first one and for every - other camera. -Default: -1 -------------------------------------------------------------------------------- -Name: force_munmap -Type: bool array (min = 0, max = 64) -Syntax: <0|1[,...]> -Description: Force the application to unmap previously mapped buffer memory - before calling any VIDIOC_S_CROP or VIDIOC_S_FMT ioctl's. Not - all the applications support this feature. This parameter is - specific for each detected camera. - 0 = do not force memory unmapping - 1 = force memory unmapping (save memory) -Default: 0 -------------------------------------------------------------------------------- -Name: frame_timeout -Type: uint array (min = 0, max = 64) -Syntax: <n[,...]> -Description: Timeout for a video frame in seconds. This parameter is - specific for each detected camera. This parameter can be - changed at runtime thanks to the /sys filesystem interface. -Default: 2 -------------------------------------------------------------------------------- -Name: debug -Type: ushort -Syntax: <n> -Description: Debugging information level, from 0 to 3: - 0 = none (use carefully) - 1 = critical errors - 2 = significant information - 3 = more verbose messages - Level 3 is useful for testing only, when only one device - is used at the same time. It also shows some information - about the hardware being detected. This module parameter can be - changed at runtime thanks to the /sys filesystem interface. -Default: 2 -------------------------------------------------------------------------------- - - -8. Supported devices -==================== -None of the names of the companies as well as their products will be mentioned -here. They have never collaborated with the author, so no advertising. - -From the point of view of a driver, what unambiguously identify a device are -its vendor and product USB identifiers. Below is a list of known identifiers of -devices mounting the ZC0301 Image Processor and Control Chips: - -Vendor ID Product ID ---------- ---------- -0x041e 0x4017 -0x041e 0x401c -0x041e 0x401e -0x041e 0x401f -0x041e 0x4022 -0x041e 0x4034 -0x041e 0x4035 -0x041e 0x4036 -0x041e 0x403a -0x0458 0x7007 -0x0458 0x700c -0x0458 0x700f -0x046d 0x08ae -0x055f 0xd003 -0x055f 0xd004 -0x0ac8 0x0301 -0x0ac8 0x301b -0x0ac8 0x303b -0x10fd 0x0128 -0x10fd 0x8050 -0x10fd 0x804e - -The list above does not imply that all those devices work with this driver: up -until now only the ones that mount the following image sensors are supported; -kernel messages will always tell you whether this is the case: - -Model Manufacturer ------ ------------ -PAS202BCB PixArt Imaging, Inc. -PB-0330 Photobit Corporation - - -9. Notes for V4L2 application developers -======================================== -This driver follows the V4L2 API specifications. In particular, it enforces two -rules: - -- exactly one I/O method, either "mmap" or "read", is associated with each -file descriptor. Once it is selected, the application must close and reopen the -device to switch to the other I/O method; - -- although it is not mandatory, previously mapped buffer memory should always -be unmapped before calling any "VIDIOC_S_CROP" or "VIDIOC_S_FMT" ioctl's. -The same number of buffers as before will be allocated again to match the size -of the new video frames, so you have to map the buffers again before any I/O -attempts on them. - - -10. Contact information -======================= -The author may be contacted by e-mail at <luca.risolia@studio.unibo.it>. - -GPG/PGP encrypted e-mail's are accepted. The GPG key ID of the author is -'FCE635A4'; the public 1024-bit key should be available at any keyserver; -the fingerprint is: '88E8 F32F 7244 68BA 3958 5D40 99DA 5D2A FCE6 35A4'. - - -11. Credits -=========== -- Information about the chip internals needed to enable the I2C protocol have - been taken from the documentation of the ZC030x Video4Linux1 driver written - by Andrew Birkett <andy@nobugs.org>; -- The initialization values of the ZC0301 controller connected to the PAS202BCB - and PB-0330 image sensors have been taken from the SPCA5XX driver maintained - by Michel Xhaard <mxhaard@magic.fr>; -- Stanislav Lechev donated one camera. diff --git a/MAINTAINERS b/MAINTAINERS index 4e2a1f67a1fc..376117d5518d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -464,6 +464,14 @@ S: Maintained F: drivers/scsi/aic7xxx/ F: drivers/scsi/aic7xxx_old/ +AIMSLAB FM RADIO RECEIVER DRIVER +M: Hans Verkuil <hverkuil@xs4all.nl> +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Maintained +F: drivers/media/radio/radio-aimslab* + AIO M: Benjamin LaHaise <bcrl@kvack.org> L: linux-aio@kvack.org @@ -558,6 +566,18 @@ L: linux-rdma@vger.kernel.org S: Maintained F: drivers/infiniband/hw/amso1100/ +ANALOG DEVICES INC AD9389B DRIVER +M: Hans Verkuil <hans.verkuil@cisco.com> +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/media/i2c/ad9389b* + +ANALOG DEVICES INC ADV7604 DRIVER +M: Hans Verkuil <hans.verkuil@cisco.com> +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/media/i2c/adv7604* + ANALOG DEVICES INC ASOC CODEC DRIVERS M: Lars-Peter Clausen <lars@metafoo.de> L: device-drivers-devel@blackfin.uclinux.org @@ -1119,6 +1139,14 @@ F: arch/arm/mach-s5pv210/mach-goni.c F: arch/arm/mach-exynos/mach-universal_c210.c F: arch/arm/mach-exynos/mach-nuri.c +ARM/SAMSUNG S5P SERIES 2D GRAPHICS ACCELERATION (G2D) SUPPORT +M: Kyungmin Park <kyungmin.park@samsung.com> +M: Kamil Debski <k.debski@samsung.com> +L: linux-arm-kernel@lists.infradead.org +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/media/platform/s5p-g2d/ + ARM/SAMSUNG S5P SERIES FIMC SUPPORT M: Kyungmin Park <kyungmin.park@samsung.com> M: Sylwester Nawrocki <s.nawrocki@samsung.com> @@ -1412,7 +1440,7 @@ ATMEL ISI DRIVER M: Josh Wu <josh.wu@atmel.com> L: linux-media@vger.kernel.org S: Supported -F: drivers/media/platform/atmel-isi.c +F: drivers/media/platform/soc_camera/atmel-isi.c F: include/media/atmel-isi.h ATMEL LCDFB DRIVER @@ -1509,6 +1537,14 @@ T: git git://linuxtv.org/media_tree.git S: Maintained F: drivers/media/usb/dvb-usb-v2/az6007.c +AZTECH FM RADIO RECEIVER DRIVER +M: Hans Verkuil <hverkuil@xs4all.nl> +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Maintained +F: drivers/media/radio/radio-aztech* + B43 WIRELESS DRIVER M: Stefano Brivio <stefano.brivio@polimi.it> L: linux-wireless@vger.kernel.org @@ -1790,6 +1826,14 @@ S: Supported F: Documentation/filesystems/caching/cachefiles.txt F: fs/cachefiles/ +CADET FM/AM RADIO RECEIVER DRIVER +M: Hans Verkuil <hverkuil@xs4all.nl> +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Maintained +F: drivers/media/radio/radio-cadet* + CAFE CMOS INTEGRATED CAMERA CONTROLLER DRIVER M: Jonathan Corbet <corbet@lwn.net> L: linux-media@vger.kernel.org @@ -2187,6 +2231,15 @@ F: Documentation/video4linux/cx18.txt F: drivers/media/pci/cx18/ F: include/uapi/linux/ivtv* +CX2341X MPEG ENCODER HELPER MODULE +M: Hans Verkuil <hverkuil@xs4all.nl> +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Maintained +F: drivers/media/i2c/cx2341x* +F: include/media/cx2341x* + CX88 VIDEO4LINUX DRIVER M: Mauro Carvalho Chehab <mchehab@redhat.com> L: linux-media@vger.kernel.org @@ -2558,6 +2611,13 @@ S: Maintained F: drivers/gpu/drm/tegra/ F: Documentation/devicetree/bindings/gpu/nvidia,tegra20-host1x.txt +DSBR100 USB FM RADIO DRIVER +M: Alexey Klimov <klimov.linux@gmail.com> +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +S: Maintained +F: drivers/media/radio/dsbr100.c + DSCC4 DRIVER M: Francois Romieu <romieu@fr.zoreil.com> L: netdev@vger.kernel.org @@ -2622,7 +2682,7 @@ W: http://github.com/mkrufky Q: http://patchwork.linuxtv.org/project/linux-media/list/ T: git git://linuxtv.org/media_tree.git S: Maintained -F: drivers/media/usb/dvb-usb-v2/cxusb* +F: drivers/media/usb/dvb-usb/cxusb* DVB_USB_CYPRESS_FIRMWARE MEDIA DRIVER M: Antti Palosaari <crope@iki.fi> @@ -3319,6 +3379,14 @@ W: http://www.icp-vortex.com/ S: Supported F: drivers/scsi/gdt* +GEMTEK FM RADIO RECEIVER DRIVER +M: Hans Verkuil <hverkuil@xs4all.nl> +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Maintained +F: drivers/media/radio/radio-gemtek* + GENERIC GPIO I2C DRIVER M: Haavard Skinnemoen <hskinnemoen@gmail.com> S: Supported @@ -4202,6 +4270,14 @@ F: Documentation/isapnp.txt F: drivers/pnp/isapnp/ F: include/linux/isapnp.h +ISA RADIO MODULE +M: Hans Verkuil <hverkuil@xs4all.nl> +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Maintained +F: drivers/media/radio/radio-isa* + iSCSI BOOT FIRMWARE TABLE (iBFT) DRIVER M: Peter Jones <pjones@redhat.com> M: Konrad Rzeszutek Wilk <konrad@kernel.org> @@ -4358,6 +4434,14 @@ W: http://lse.sourceforge.net/kdump/ S: Maintained F: Documentation/kdump/ +KEENE FM RADIO TRANSMITTER DRIVER +M: Hans Verkuil <hverkuil@xs4all.nl> +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Maintained +F: drivers/media/radio/radio-keene* + KERNEL AUTOMOUNTER v4 (AUTOFS4) M: Ian Kent <raven@themaw.net> L: autofs@vger.kernel.org @@ -4831,6 +4915,13 @@ Q: http://patchwork.linuxtv.org/project/linux-media/list/ S: Maintained F: drivers/media/dvb-frontends/m88rs2000* +MA901 MASTERKIT USB FM RADIO DRIVER +M: Alexey Klimov <klimov.linux@gmail.com> +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +S: Maintained +F: drivers/media/radio/radio-ma901.c + MAC80211 M: Johannes Berg <johannes@sipsolutions.net> L: linux-wireless@vger.kernel.org @@ -4928,6 +5019,14 @@ S: Maintained F: Documentation/hwmon/max6650 F: drivers/hwmon/max6650.c +MAXIRADIO FM RADIO RECEIVER DRIVER +M: Hans Verkuil <hverkuil@xs4all.nl> +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Maintained +F: drivers/media/radio/radio-maxiradio* + MEDIA INPUT INFRASTRUCTURE (V4L/DVB) M: Mauro Carvalho Chehab <mchehab@redhat.com> P: LinuxTV.org Project @@ -4950,6 +5049,14 @@ F: include/uapi/linux/meye.h F: include/uapi/linux/ivtv* F: include/uapi/linux/uvcvideo.h +MEDIAVISION PRO MOVIE STUDIO DRIVER +M: Hans Verkuil <hverkuil@xs4all.nl> +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Odd Fixes +F: drivers/media/parport/pms* + MEGARAID SCSI DRIVERS M: Neela Syam Kolli <megaraidlinux@lsi.com> L: linux-scsi@vger.kernel.org @@ -5019,6 +5126,14 @@ S: Supported F: Documentation/mips/ F: arch/mips/ +MIROSOUND PCM20 FM RADIO RECEIVER DRIVER +M: Hans Verkuil <hverkuil@xs4all.nl> +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Odd Fixes +F: drivers/media/radio/radio-miropcm20* + MODULE SUPPORT M: Rusty Russell <rusty@rustcorp.com.au> S: Maintained @@ -6198,6 +6313,14 @@ L: linux-hexagon@vger.kernel.org S: Supported F: arch/hexagon/ +QUICKCAM PARALLEL PORT WEBCAMS +M: Hans Verkuil <hverkuil@xs4all.nl> +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Odd Fixes +F: drivers/media/parport/*-qcam* + RADOS BLOCK DEVICE (RBD) M: Yehuda Sadeh <yehuda@inktank.com> M: Sage Weil <sage@inktank.com> @@ -6471,6 +6594,14 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Supported F: drivers/mmc/host/s3cmci.* +SAA6588 RDS RECEIVER DRIVER +M: Hans Verkuil <hverkuil@xs4all.nl> +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Odd Fixes +F: drivers/media/i2c/saa6588* + SAA7134 VIDEO4LINUX DRIVER M: Mauro Carvalho Chehab <mchehab@redhat.com> L: linux-media@vger.kernel.org @@ -6481,10 +6612,9 @@ F: Documentation/video4linux/saa7134/ F: drivers/media/pci/saa7134/ SAA7146 VIDEO4LINUX-2 DRIVER -M: Michael Hunold <michael@mihu.de> +M: Hans Verkuil <hverkuil@xs4all.nl> L: linux-media@vger.kernel.org T: git git://linuxtv.org/media_tree.git -W: http://www.mihu.de/linux/saa7146 S: Maintained F: drivers/media/common/saa7146/ F: drivers/media/pci/saa7146/ @@ -6783,6 +6913,38 @@ M: Robin Holt <holt@sgi.com> S: Maintained F: drivers/misc/sgi-xp/ +SI470X FM RADIO RECEIVER I2C DRIVER +M: Hans Verkuil <hverkuil@xs4all.nl> +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Odd Fixes +F: drivers/media/radio/si470x/radio-si470x-i2c.c + +SI470X FM RADIO RECEIVER USB DRIVER +M: Hans Verkuil <hverkuil@xs4all.nl> +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Maintained +F: drivers/media/radio/si470x/radio-si470x-common.c +F: drivers/media/radio/si470x/radio-si470x.h +F: drivers/media/radio/si470x/radio-si470x-usb.c + +SH_VEU V4L2 MEM2MEM DRIVER +M: Guennadi Liakhovetski <g.liakhovetski@gmx.de> +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/media/platform/sh_veu.c +F: include/media/sh_veu.h + +SH_VOU V4L2 OUTPUT DRIVER +M: Guennadi Liakhovetski <g.liakhovetski@gmx.de> +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/media/platform/sh_vou.c +F: include/media/sh_vou.h + SIMPLE FIRMWARE INTERFACE (SFI) M: Len Brown <lenb@kernel.org> L: sfi-devel@simplefirmware.org @@ -7455,6 +7617,14 @@ T: git git://linuxtv.org/mkrufky/tuners.git S: Maintained F: drivers/media/tuners/tda8290.* +TDA9840 MEDIA DRIVER +M: Hans Verkuil <hverkuil@xs4all.nl> +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Maintained +F: drivers/media/i2c/tda9840* + TEA5761 TUNER DRIVER M: Mauro Carvalho Chehab <mchehab@redhat.com> L: linux-media@vger.kernel.org @@ -7471,6 +7641,22 @@ T: git git://linuxtv.org/media_tree.git S: Maintained F: drivers/media/tuners/tea5767.* +TEA6415C MEDIA DRIVER +M: Hans Verkuil <hverkuil@xs4all.nl> +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Maintained +F: drivers/media/i2c/tea6415c* + +TEA6420 MEDIA DRIVER +M: Hans Verkuil <hverkuil@xs4all.nl> +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Maintained +F: drivers/media/i2c/tea6420* + TEAM DRIVER M: Jiri Pirko <jpirko@redhat.com> L: netdev@vger.kernel.org @@ -8087,6 +8273,14 @@ S: Maintained F: drivers/media/usb/uvc/ F: include/uapi/linux/uvcvideo.h +USB VISION DRIVER +M: Hans Verkuil <hverkuil@xs4all.nl> +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Odd Fixes +F: drivers/media/usb/usbvision/ + USB WEBCAM GADGET M: Laurent Pinchart <laurent.pinchart@ideasonboard.com> L: linux-usb@vger.kernel.org @@ -8234,6 +8428,14 @@ L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/via/via-velocity.* +VIVI VIRTUAL VIDEO DRIVER +M: Hans Verkuil <hverkuil@xs4all.nl> +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Maintained +F: drivers/media/platform/vivi* + VLAN (802.1Q) M: Patrick McHardy <kaber@trash.net> L: netdev@vger.kernel.org diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c index f5e018de7fa5..8e1b4ffb5e54 100644 --- a/arch/arm/mach-davinci/board-dm644x-evm.c +++ b/arch/arm/mach-davinci/board-dm644x-evm.c @@ -690,7 +690,7 @@ static struct vpbe_output dm644xevm_vpbe_outputs[] = { .std = VENC_STD_ALL, .capabilities = V4L2_OUT_CAP_STD, }, - .subdev_name = VPBE_VENC_SUBDEV_NAME, + .subdev_name = DM644X_VPBE_VENC_SUBDEV_NAME, .default_mode = "ntsc", .num_modes = ARRAY_SIZE(dm644xevm_enc_std_timing), .modes = dm644xevm_enc_std_timing, @@ -702,7 +702,7 @@ static struct vpbe_output dm644xevm_vpbe_outputs[] = { .type = V4L2_OUTPUT_TYPE_ANALOG, .capabilities = V4L2_OUT_CAP_DV_TIMINGS, }, - .subdev_name = VPBE_VENC_SUBDEV_NAME, + .subdev_name = DM644X_VPBE_VENC_SUBDEV_NAME, .default_mode = "480p59_94", .num_modes = ARRAY_SIZE(dm644xevm_enc_preset_timing), .modes = dm644xevm_enc_preset_timing, @@ -713,10 +713,10 @@ static struct vpbe_config dm644xevm_display_cfg = { .module_name = "dm644x-vpbe-display", .i2c_adapter_id = 1, .osd = { - .module_name = VPBE_OSD_SUBDEV_NAME, + .module_name = DM644X_VPBE_OSD_SUBDEV_NAME, }, .venc = { - .module_name = VPBE_VENC_SUBDEV_NAME, + .module_name = DM644X_VPBE_VENC_SUBDEV_NAME, }, .num_outputs = ARRAY_SIZE(dm644xevm_vpbe_outputs), .outputs = dm644xevm_vpbe_outputs, diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 11c79a3362ef..db1dd92e00af 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -669,19 +669,14 @@ static struct resource dm644x_osd_resources[] = { }, }; -static struct osd_platform_data dm644x_osd_data = { - .vpbe_type = VPBE_VERSION_1, -}; - static struct platform_device dm644x_osd_dev = { - .name = VPBE_OSD_SUBDEV_NAME, + .name = DM644X_VPBE_OSD_SUBDEV_NAME, .id = -1, .num_resources = ARRAY_SIZE(dm644x_osd_resources), .resource = dm644x_osd_resources, .dev = { .dma_mask = &dm644x_video_dma_mask, .coherent_dma_mask = DMA_BIT_MASK(32), - .platform_data = &dm644x_osd_data, }, }; @@ -751,12 +746,11 @@ static struct platform_device dm644x_vpbe_display = { }; static struct venc_platform_data dm644x_venc_pdata = { - .venc_type = VPBE_VERSION_1, .setup_clock = dm644x_venc_setup_clock, }; static struct platform_device dm644x_venc_dev = { - .name = VPBE_VENC_SUBDEV_NAME, + .name = DM644X_VPBE_VENC_SUBDEV_NAME, .id = -1, .num_resources = ARRAY_SIZE(dm644x_venc_resources), .resource = dm644x_venc_resources, diff --git a/drivers/media/common/saa7146/saa7146_fops.c b/drivers/media/common/saa7146/saa7146_fops.c index b3890bd49df6..2652f9155c34 100644 --- a/drivers/media/common/saa7146/saa7146_fops.c +++ b/drivers/media/common/saa7146/saa7146_fops.c @@ -105,7 +105,7 @@ void saa7146_buffer_finish(struct saa7146_dev *dev, } q->curr->vb.state = state; - do_gettimeofday(&q->curr->vb.ts); + v4l2_get_timestamp(&q->curr->vb.ts); wake_up(&q->curr->vb.done); q->curr = NULL; diff --git a/drivers/media/dvb-frontends/ix2505v.c b/drivers/media/dvb-frontends/ix2505v.c index bc5a82082aaa..0e3387e00952 100644 --- a/drivers/media/dvb-frontends/ix2505v.c +++ b/drivers/media/dvb-frontends/ix2505v.c @@ -212,7 +212,7 @@ static int ix2505v_set_params(struct dvb_frontend *fe) lpf = 0xb; deb_info("Osc=%x b_w=%x lpf=%x\n", local_osc, b_w, lpf); - deb_info("Data 0=[%x%x%x%x]\n", data[0], data[1], data[2], data[3]); + deb_info("Data 0=[%4phN]\n", data); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); diff --git a/drivers/media/dvb-frontends/or51211.c b/drivers/media/dvb-frontends/or51211.c index c625b57b4333..10cfc0579168 100644 --- a/drivers/media/dvb-frontends/or51211.c +++ b/drivers/media/dvb-frontends/or51211.c @@ -22,6 +22,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ + /* * This driver needs external firmware. Please use the command * "<kerneldir>/Documentation/dvb/get_dvb_firmware or51211" to @@ -44,9 +46,7 @@ static int debug; #define dprintk(args...) \ - do { \ - if (debug) printk(KERN_DEBUG "or51211: " args); \ - } while (0) + do { if (debug) pr_debug(args); } while (0) static u8 run_buf[] = {0x7f,0x01}; static u8 cmd_buf[] = {0x04,0x01,0x50,0x80,0x06}; // ATSC @@ -80,8 +80,7 @@ static int i2c_writebytes (struct or51211_state* state, u8 reg, const u8 *buf, msg.buf = (u8 *)buf; if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { - printk(KERN_WARNING "or51211: i2c_writebytes error " - "(addr %02x, err == %i)\n", reg, err); + pr_warn("error (addr %02x, err == %i)\n", reg, err); return -EREMOTEIO; } @@ -98,8 +97,7 @@ static int i2c_readbytes(struct or51211_state *state, u8 reg, u8 *buf, int len) msg.buf = buf; if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { - printk(KERN_WARNING "or51211: i2c_readbytes error " - "(addr %02x, err == %i)\n", reg, err); + pr_warn("error (addr %02x, err == %i)\n", reg, err); return -EREMOTEIO; } @@ -118,11 +116,11 @@ static int or51211_load_firmware (struct dvb_frontend* fe, /* Get eprom data */ tudata[0] = 17; if (i2c_writebytes(state,0x50,tudata,1)) { - printk(KERN_WARNING "or51211:load_firmware error eprom addr\n"); + pr_warn("error eprom addr\n"); return -1; } if (i2c_readbytes(state,0x50,&tudata[145],192)) { - printk(KERN_WARNING "or51211: load_firmware error eprom\n"); + pr_warn("error eprom\n"); return -1; } @@ -136,32 +134,32 @@ static int or51211_load_firmware (struct dvb_frontend* fe, state->config->reset(fe); if (i2c_writebytes(state,state->config->demod_address,tudata,585)) { - printk(KERN_WARNING "or51211: load_firmware error 1\n"); + pr_warn("error 1\n"); return -1; } msleep(1); if (i2c_writebytes(state,state->config->demod_address, &fw->data[393],8125)) { - printk(KERN_WARNING "or51211: load_firmware error 2\n"); + pr_warn("error 2\n"); return -1; } msleep(1); if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) { - printk(KERN_WARNING "or51211: load_firmware error 3\n"); + pr_warn("error 3\n"); return -1; } /* Wait at least 5 msec */ msleep(10); if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) { - printk(KERN_WARNING "or51211: load_firmware error 4\n"); + pr_warn("error 4\n"); return -1; } msleep(10); - printk("or51211: Done.\n"); + pr_info("Done.\n"); return 0; }; @@ -173,14 +171,14 @@ static int or51211_setmode(struct dvb_frontend* fe, int mode) state->config->setmode(fe, mode); if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) { - printk(KERN_WARNING "or51211: setmode error 1\n"); + pr_warn("error 1\n"); return -1; } /* Wait at least 5 msec */ msleep(10); if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) { - printk(KERN_WARNING "or51211: setmode error 2\n"); + pr_warn("error 2\n"); return -1; } @@ -196,7 +194,7 @@ static int or51211_setmode(struct dvb_frontend* fe, int mode) * normal +/-150kHz Carrier acquisition range */ if (i2c_writebytes(state,state->config->demod_address,cmd_buf,3)) { - printk(KERN_WARNING "or51211: setmode error 3\n"); + pr_warn("error 3\n"); return -1; } @@ -206,14 +204,14 @@ static int or51211_setmode(struct dvb_frontend* fe, int mode) rec_buf[3] = 0x00; msleep(20); if (i2c_writebytes(state,state->config->demod_address,rec_buf,3)) { - printk(KERN_WARNING "or51211: setmode error 5\n"); + pr_warn("error 5\n"); } msleep(3); if (i2c_readbytes(state,state->config->demod_address,&rec_buf[10],2)) { - printk(KERN_WARNING "or51211: setmode error 6"); + pr_warn("error 6\n"); return -1; } - dprintk("setmode rec status %02x %02x\n",rec_buf[10],rec_buf[11]); + dprintk("rec status %02x %02x\n", rec_buf[10], rec_buf[11]); return 0; } @@ -248,15 +246,15 @@ static int or51211_read_status(struct dvb_frontend* fe, fe_status_t* status) /* Receiver Status */ if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) { - printk(KERN_WARNING "or51132: read_status write error\n"); + pr_warn("write error\n"); return -1; } msleep(3); if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) { - printk(KERN_WARNING "or51132: read_status read error\n"); + pr_warn("read error\n"); return -1; } - dprintk("read_status %x %x\n",rec_buf[0],rec_buf[1]); + dprintk("%x %x\n", rec_buf[0], rec_buf[1]); if (rec_buf[0] & 0x01) { /* Receiver Lock */ *status |= FE_HAS_SIGNAL; @@ -306,20 +304,18 @@ static int or51211_read_snr(struct dvb_frontend* fe, u16* snr) snd_buf[2] = 0x04; if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) { - printk(KERN_WARNING "%s: error writing snr reg\n", - __func__); + pr_warn("error writing snr reg\n"); return -1; } if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) { - printk(KERN_WARNING "%s: read_status read error\n", - __func__); + pr_warn("read_status read error\n"); return -1; } state->snr = calculate_snr(rec_buf[0], 89599047); *snr = (state->snr) >> 16; - dprintk("%s: noise = 0x%02x, snr = %d.%02d dB\n", __func__, rec_buf[0], + dprintk("noise = 0x%02x, snr = %d.%02d dB\n", rec_buf[0], state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16); return 0; @@ -375,25 +371,24 @@ static int or51211_init(struct dvb_frontend* fe) if (!state->initialized) { /* Request the firmware, this will block until it uploads */ - printk(KERN_INFO "or51211: Waiting for firmware upload " - "(%s)...\n", OR51211_DEFAULT_FIRMWARE); + pr_info("Waiting for firmware upload (%s)...\n", + OR51211_DEFAULT_FIRMWARE); ret = config->request_firmware(fe, &fw, OR51211_DEFAULT_FIRMWARE); - printk(KERN_INFO "or51211:Got Hotplug firmware\n"); + pr_info("Got Hotplug firmware\n"); if (ret) { - printk(KERN_WARNING "or51211: No firmware uploaded " - "(timeout or file not found?)\n"); + pr_warn("No firmware uploaded " + "(timeout or file not found?)\n"); return ret; } ret = or51211_load_firmware(fe, fw); release_firmware(fw); if (ret) { - printk(KERN_WARNING "or51211: Writing firmware to " - "device failed!\n"); + pr_warn("Writing firmware to device failed!\n"); return ret; } - printk(KERN_INFO "or51211: Firmware upload complete.\n"); + pr_info("Firmware upload complete.\n"); /* Set operation mode in Receiver 1 register; * type 1: @@ -406,7 +401,7 @@ static int or51211_init(struct dvb_frontend* fe) */ if (i2c_writebytes(state,state->config->demod_address, cmd_buf,3)) { - printk(KERN_WARNING "or51211: Load DVR Error 5\n"); + pr_warn("Load DVR Error 5\n"); return -1; } @@ -419,13 +414,13 @@ static int or51211_init(struct dvb_frontend* fe) msleep(30); if (i2c_writebytes(state,state->config->demod_address, rec_buf,3)) { - printk(KERN_WARNING "or51211: Load DVR Error A\n"); + pr_warn("Load DVR Error A\n"); return -1; } msleep(3); if (i2c_readbytes(state,state->config->demod_address, &rec_buf[10],2)) { - printk(KERN_WARNING "or51211: Load DVR Error B\n"); + pr_warn("Load DVR Error B\n"); return -1; } @@ -436,13 +431,13 @@ static int or51211_init(struct dvb_frontend* fe) msleep(20); if (i2c_writebytes(state,state->config->demod_address, rec_buf,3)) { - printk(KERN_WARNING "or51211: Load DVR Error C\n"); + pr_warn("Load DVR Error C\n"); return -1; } msleep(3); if (i2c_readbytes(state,state->config->demod_address, &rec_buf[12],2)) { - printk(KERN_WARNING "or51211: Load DVR Error D\n"); + pr_warn("Load DVR Error D\n"); return -1; } @@ -454,16 +449,14 @@ static int or51211_init(struct dvb_frontend* fe) get_ver_buf[4] = i+1; if (i2c_writebytes(state,state->config->demod_address, get_ver_buf,5)) { - printk(KERN_WARNING "or51211:Load DVR Error 6" - " - %d\n",i); + pr_warn("Load DVR Error 6 - %d\n", i); return -1; } msleep(3); if (i2c_readbytes(state,state->config->demod_address, &rec_buf[i*2],2)) { - printk(KERN_WARNING "or51211:Load DVR Error 7" - " - %d\n",i); + pr_warn("Load DVR Error 7 - %d\n", i); return -1; } /* If we didn't receive the right index, try again */ @@ -471,15 +464,11 @@ static int or51211_init(struct dvb_frontend* fe) i--; } } - dprintk("read_fwbits %x %x %x %x %x %x %x %x %x %x\n", - rec_buf[0], rec_buf[1], rec_buf[2], rec_buf[3], - rec_buf[4], rec_buf[5], rec_buf[6], rec_buf[7], - rec_buf[8], rec_buf[9]); + dprintk("read_fwbits %10ph\n", rec_buf); - printk(KERN_INFO "or51211: ver TU%02x%02x%02x VSB mode %02x" - " Status %02x\n", - rec_buf[2], rec_buf[4],rec_buf[6], - rec_buf[12],rec_buf[10]); + pr_info("ver TU%02x%02x%02x VSB mode %02x Status %02x\n", + rec_buf[2], rec_buf[4], rec_buf[6], rec_buf[12], + rec_buf[10]); rec_buf[0] = 0x04; rec_buf[1] = 0x00; @@ -488,13 +477,13 @@ static int or51211_init(struct dvb_frontend* fe) msleep(20); if (i2c_writebytes(state,state->config->demod_address, rec_buf,3)) { - printk(KERN_WARNING "or51211: Load DVR Error 8\n"); + pr_warn("Load DVR Error 8\n"); return -1; } msleep(20); if (i2c_readbytes(state,state->config->demod_address, &rec_buf[8],2)) { - printk(KERN_WARNING "or51211: Load DVR Error 9\n"); + pr_warn("Load DVR Error 9\n"); return -1; } state->initialized = 1; diff --git a/drivers/media/dvb-frontends/tda10071.c b/drivers/media/dvb-frontends/tda10071.c index 16a4bc54dbe7..2521f7e23018 100644 --- a/drivers/media/dvb-frontends/tda10071.c +++ b/drivers/media/dvb-frontends/tda10071.c @@ -30,7 +30,7 @@ static int tda10071_wr_regs(struct tda10071_priv *priv, u8 reg, u8 *val, u8 buf[len+1]; struct i2c_msg msg[1] = { { - .addr = priv->cfg.i2c_address, + .addr = priv->cfg.demod_i2c_addr, .flags = 0, .len = sizeof(buf), .buf = buf, @@ -59,12 +59,12 @@ static int tda10071_rd_regs(struct tda10071_priv *priv, u8 reg, u8 *val, u8 buf[len]; struct i2c_msg msg[2] = { { - .addr = priv->cfg.i2c_address, + .addr = priv->cfg.demod_i2c_addr, .flags = 0, .len = 1, .buf = ®, }, { - .addr = priv->cfg.i2c_address, + .addr = priv->cfg.demod_i2c_addr, .flags = I2C_M_RD, .len = sizeof(buf), .buf = buf, @@ -1064,7 +1064,7 @@ static int tda10071_init(struct dvb_frontend *fe) cmd.args[2] = 0x00; cmd.args[3] = 0x00; cmd.args[4] = 0x00; - cmd.args[5] = 0x14; + cmd.args[5] = (priv->cfg.tuner_i2c_addr) ? priv->cfg.tuner_i2c_addr : 0x14; cmd.args[6] = 0x00; cmd.args[7] = 0x03; cmd.args[8] = 0x02; @@ -1202,6 +1202,20 @@ struct dvb_frontend *tda10071_attach(const struct tda10071_config *config, goto error; } + /* make sure demod i2c address is specified */ + if (!config->demod_i2c_addr) { + dev_dbg(&i2c->dev, "%s: invalid demod i2c address!\n", __func__); + ret = -EINVAL; + goto error; + } + + /* make sure tuner i2c address is specified */ + if (!config->tuner_i2c_addr) { + dev_dbg(&i2c->dev, "%s: invalid tuner i2c address!\n", __func__); + ret = -EINVAL; + goto error; + } + /* setup the priv */ priv->i2c = i2c; memcpy(&priv->cfg, config, sizeof(struct tda10071_config)); diff --git a/drivers/media/dvb-frontends/tda10071.h b/drivers/media/dvb-frontends/tda10071.h index 21163c4b555c..bff1c38df802 100644 --- a/drivers/media/dvb-frontends/tda10071.h +++ b/drivers/media/dvb-frontends/tda10071.h @@ -28,7 +28,13 @@ struct tda10071_config { * Default: none, must set * Values: 0x55, */ - u8 i2c_address; + u8 demod_i2c_addr; + + /* Tuner I2C address. + * Default: none, must set + * Values: 0x14, 0x54, ... + */ + u8 tuner_i2c_addr; /* Max bytes I2C provider can write at once. * Note: Buffer is taken from the stack currently! diff --git a/drivers/media/dvb-frontends/tda8261_cfg.h b/drivers/media/dvb-frontends/tda8261_cfg.h index 1af1ee49b542..46710744173b 100644 --- a/drivers/media/dvb-frontends/tda8261_cfg.h +++ b/drivers/media/dvb-frontends/tda8261_cfg.h @@ -78,7 +78,7 @@ static int tda8261_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) return err; } *bandwidth = t_state.bandwidth; + printk("%s: Bandwidth=%d\n", __func__, t_state.bandwidth); } - printk("%s: Bandwidth=%d\n", __func__, t_state.bandwidth); return 0; } diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index e7c82b297514..882ddf6f66d3 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -353,7 +353,7 @@ static struct regval_list ov7670_fmt_yuv422[] = { { REG_RGB444, 0 }, /* No RGB444 please */ { REG_COM1, 0 }, /* CCIR601 */ { REG_COM15, COM15_R00FF }, - { REG_COM9, 0x18 }, /* 4x gain ceiling; 0x8 is reserved bit */ + { REG_COM9, 0x48 }, /* 32x gain ceiling; 0x8 is reserved bit */ { 0x4f, 0x80 }, /* "matrix coefficient 1" */ { 0x50, 0x80 }, /* "matrix coefficient 2" */ { 0x51, 0 }, /* vb */ diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index de6f41f19187..346458bba2c5 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -3835,7 +3835,7 @@ bttv_irq_wakeup_video(struct bttv *btv, struct bttv_buffer_set *wakeup, { struct timeval ts; - do_gettimeofday(&ts); + v4l2_get_timestamp(&ts); if (wakeup->top == wakeup->bottom) { if (NULL != wakeup->top && curr->top != wakeup->top) { @@ -3878,7 +3878,7 @@ bttv_irq_wakeup_vbi(struct bttv *btv, struct bttv_buffer *wakeup, if (NULL == wakeup) return; - do_gettimeofday(&ts); + v4l2_get_timestamp(&ts); wakeup->vb.ts = ts; wakeup->vb.field_count = btv->field_count; wakeup->vb.state = state; @@ -3949,7 +3949,7 @@ bttv_irq_wakeup_top(struct bttv *btv) btv->curr.top = NULL; bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0); - do_gettimeofday(&wakeup->vb.ts); + v4l2_get_timestamp(&wakeup->vb.ts); wakeup->vb.field_count = btv->field_count; wakeup->vb.state = VIDEOBUF_DONE; wake_up(&wakeup->vb.done); diff --git a/drivers/media/pci/cx23885/Kconfig b/drivers/media/pci/cx23885/Kconfig index eafa1144b17d..733d6c8ebe4c 100644 --- a/drivers/media/pci/cx23885/Kconfig +++ b/drivers/media/pci/cx23885/Kconfig @@ -26,6 +26,8 @@ config VIDEO_CX23885 select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT select DVB_DS3000 if MEDIA_SUBDRV_AUTOSELECT select DVB_STV0367 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10071 if MEDIA_SUBDRV_AUTOSELECT + select DVB_A8293 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MT2063 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MT2131 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_XC2028 if MEDIA_SUBDRV_AUTOSELECT diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c index 6277e145f0b8..8d96ae4ebbdd 100644 --- a/drivers/media/pci/cx23885/cx23885-cards.c +++ b/drivers/media/pci/cx23885/cx23885-cards.c @@ -572,7 +572,11 @@ struct cx23885_board cx23885_boards[] = { [CX23885_BOARD_PROF_8000] = { .name = "Prof Revolution DVB-S2 8000", .portb = CX23885_MPEG_DVB, - } + }, + [CX23885_BOARD_HAUPPAUGE_HVR4400] = { + .name = "Hauppauge WinTV-HVR4400", + .portb = CX23885_MPEG_DVB, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -788,6 +792,22 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x8000, .subdevice = 0x3034, .card = CX23885_BOARD_PROF_8000, + }, { + .subvendor = 0x0070, + .subdevice = 0xc108, + .card = CX23885_BOARD_HAUPPAUGE_HVR4400, + }, { + .subvendor = 0x0070, + .subdevice = 0xc138, + .card = CX23885_BOARD_HAUPPAUGE_HVR4400, + }, { + .subvendor = 0x0070, + .subdevice = 0xc12a, + .card = CX23885_BOARD_HAUPPAUGE_HVR4400, + }, { + .subvendor = 0x0070, + .subdevice = 0xc1f8, + .card = CX23885_BOARD_HAUPPAUGE_HVR4400, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -1301,6 +1321,16 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) /* enable irq */ cx_write(GPIO_ISM, 0x00000000);/* INTERRUPTS active low*/ break; + case CX23885_BOARD_HAUPPAUGE_HVR4400: + /* GPIO-8 tda10071 demod reset */ + + /* Put the parts into reset and back */ + cx23885_gpio_enable(dev, GPIO_8, 1); + cx23885_gpio_clear(dev, GPIO_8); + mdelay(100); + cx23885_gpio_set(dev, GPIO_8); + mdelay(100); + break; } } @@ -1378,6 +1408,7 @@ int cx23885_ir_init(struct cx23885_dev *dev) break; case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: case CX23885_BOARD_TEVII_S470: + case CX23885_BOARD_MYGICA_X8507: if (!enable_885_ir) break; dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_AV_CORE); @@ -1420,6 +1451,7 @@ void cx23885_ir_fini(struct cx23885_dev *dev) case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: case CX23885_BOARD_TEVII_S470: case CX23885_BOARD_HAUPPAUGE_HVR1250: + case CX23885_BOARD_MYGICA_X8507: cx23885_irq_remove(dev, PCI_MSK_AV_CORE); /* sd_ir is a duplicate pointer to the AV Core, just clear it */ dev->sd_ir = NULL; @@ -1464,6 +1496,7 @@ void cx23885_ir_pci_int_enable(struct cx23885_dev *dev) case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: case CX23885_BOARD_TEVII_S470: case CX23885_BOARD_HAUPPAUGE_HVR1250: + case CX23885_BOARD_MYGICA_X8507: if (dev->sd_ir) cx23885_irq_add_enable(dev, PCI_MSK_AV_CORE); break; @@ -1509,6 +1542,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1210: case CX23885_BOARD_HAUPPAUGE_HVR1850: case CX23885_BOARD_HAUPPAUGE_HVR1290: + case CX23885_BOARD_HAUPPAUGE_HVR4400: if (dev->i2c_bus[0].i2c_rc == 0) hauppauge_eeprom(dev, eeprom+0xc0); break; @@ -1581,6 +1615,11 @@ void cx23885_card_setup(struct cx23885_dev *dev) ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; break; + case CX23885_BOARD_HAUPPAUGE_HVR4400: + ts1->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ + ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + break; case CX23885_BOARD_HAUPPAUGE_HVR1250: case CX23885_BOARD_HAUPPAUGE_HVR1500: case CX23885_BOARD_HAUPPAUGE_HVR1500Q: diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c index 065ecd54bda3..c7572594d434 100644 --- a/drivers/media/pci/cx23885/cx23885-core.c +++ b/drivers/media/pci/cx23885/cx23885-core.c @@ -439,7 +439,7 @@ void cx23885_wakeup(struct cx23885_tsport *port, if ((s16) (count - buf->count) < 0) break; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); dprintk(2, "[%p/%d] wakeup reg=%d buf=%d\n", buf, buf->vb.i, count, buf->count); buf->vb.state = VIDEOBUF_DONE; diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index 2f5b902e63ae..a1aae5633739 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -66,6 +66,8 @@ #include "stv090x.h" #include "stb6100.h" #include "stb6100_cfg.h" +#include "tda10071.h" +#include "a8293.h" static unsigned int debug; @@ -659,6 +661,20 @@ static struct mt2063_config terratec_mt2063_config[] = { }, }; +static const struct tda10071_config hauppauge_tda10071_config = { + .demod_i2c_addr = 0x05, + .tuner_i2c_addr = 0x54, + .i2c_wr_max = 64, + .ts_mode = TDA10071_TS_SERIAL, + .spec_inv = 0, + .xtal = 40444000, /* 40.444 MHz */ + .pll_multiplier = 20, +}; + +static const struct a8293_config hauppauge_a8293_config = { + .i2c_addr = 0x0b, +}; + static int netup_altera_fpga_rw(void *device, int flag, int data, int read) { struct cx23885_dev *dev = (struct cx23885_dev *)device; @@ -1242,6 +1258,17 @@ static int dvb_register(struct cx23885_tsport *port) fe0->dvb.frontend->ops.set_voltage = p8000_set_voltage; } break; + case CX23885_BOARD_HAUPPAUGE_HVR4400: + i2c_bus = &dev->i2c_bus[0]; + fe0->dvb.frontend = dvb_attach(tda10071_attach, + &hauppauge_tda10071_config, + &i2c_bus->i2c_adap); + if (fe0->dvb.frontend != NULL) { + dvb_attach(a8293_attach, fe0->dvb.frontend, + &i2c_bus->i2c_adap, + &hauppauge_a8293_config); + } + break; default: printk(KERN_INFO "%s: The frontend of your DVB/ATSC card " " isn't supported yet\n", diff --git a/drivers/media/pci/cx23885/cx23885-input.c b/drivers/media/pci/cx23885/cx23885-input.c index 4f1055a194b5..7875dfbe09ff 100644 --- a/drivers/media/pci/cx23885/cx23885-input.c +++ b/drivers/media/pci/cx23885/cx23885-input.c @@ -89,6 +89,7 @@ void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events) case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: case CX23885_BOARD_TEVII_S470: case CX23885_BOARD_HAUPPAUGE_HVR1250: + case CX23885_BOARD_MYGICA_X8507: /* * The only boards we handle right now. However other boards * using the CX2388x integrated IR controller should be similar @@ -140,6 +141,7 @@ static int cx23885_input_ir_start(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1850: case CX23885_BOARD_HAUPPAUGE_HVR1290: case CX23885_BOARD_HAUPPAUGE_HVR1250: + case CX23885_BOARD_MYGICA_X8507: /* * The IR controller on this board only returns pulse widths. * Any other mode setting will fail to set up the device. @@ -289,6 +291,13 @@ int cx23885_input_init(struct cx23885_dev *dev) /* A guess at the remote */ rc_map = RC_MAP_TEVII_NEC; break; + case CX23885_BOARD_MYGICA_X8507: + /* Integrated CX23885 IR controller */ + driver_type = RC_DRIVER_IR_RAW; + allowed_protos = RC_BIT_ALL; + /* A guess at the remote */ + rc_map = RC_MAP_TOTAL_MEDIA_IN_HAND_02; + break; default: return -ENODEV; } diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c index 1a21926ca412..83975313e0f2 100644 --- a/drivers/media/pci/cx23885/cx23885-video.c +++ b/drivers/media/pci/cx23885/cx23885-video.c @@ -300,7 +300,7 @@ void cx23885_video_wakeup(struct cx23885_dev *dev, if ((s16) (count - buf->count) < 0) break; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); dprintk(2, "[%p/%d] wakeup reg=%d buf=%d\n", buf, buf->vb.i, count, buf->count); buf->vb.state = VIDEOBUF_DONE; diff --git a/drivers/media/pci/cx23885/cx23885.h b/drivers/media/pci/cx23885/cx23885.h index 67f40d31450b..61889b25a2af 100644 --- a/drivers/media/pci/cx23885/cx23885.h +++ b/drivers/media/pci/cx23885/cx23885.h @@ -91,6 +91,7 @@ #define CX23885_BOARD_TEVII_S471 35 #define CX23885_BOARD_HAUPPAUGE_HVR1255_22111 36 #define CX23885_BOARD_PROF_8000 37 +#define CX23885_BOARD_HAUPPAUGE_HVR4400 38 #define GPIO_0 0x00000001 #define GPIO_1 0x00000002 diff --git a/drivers/media/pci/cx25821/cx25821-video.c b/drivers/media/pci/cx25821/cx25821-video.c index 53b16dd70320..d4de021dc844 100644 --- a/drivers/media/pci/cx25821/cx25821-video.c +++ b/drivers/media/pci/cx25821/cx25821-video.c @@ -130,7 +130,7 @@ void cx25821_video_wakeup(struct cx25821_dev *dev, struct cx25821_dmaqueue *q, if ((s16) (count - buf->count) < 0) break; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); buf->vb.state = VIDEOBUF_DONE; list_del(&buf->vb.queue); wake_up(&buf->vb.done); diff --git a/drivers/media/pci/cx88/cx88-core.c b/drivers/media/pci/cx88/cx88-core.c index 19a58754c6e1..39f095c37ffd 100644 --- a/drivers/media/pci/cx88/cx88-core.c +++ b/drivers/media/pci/cx88/cx88-core.c @@ -549,7 +549,7 @@ void cx88_wakeup(struct cx88_core *core, * up to 32767 buffers in flight... */ if ((s16) (count - buf->count) < 0) break; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); dprintk(2,"[%p/%d] wakeup reg=%d buf=%d\n",buf,buf->vb.i, count, buf->count); buf->vb.state = VIDEOBUF_DONE; diff --git a/drivers/media/pci/ivtv/ivtv-driver.c b/drivers/media/pci/ivtv/ivtv-driver.c index 74e9a5032364..5d0a5df61078 100644 --- a/drivers/media/pci/ivtv/ivtv-driver.c +++ b/drivers/media/pci/ivtv/ivtv-driver.c @@ -304,7 +304,7 @@ static void request_modules(struct ivtv *dev) static void flush_request_modules(struct ivtv *dev) { - flush_work_sync(&dev->request_module_wk); + flush_work(&dev->request_module_wk); } #else #define request_modules(dev) diff --git a/drivers/media/pci/meye/meye.c b/drivers/media/pci/meye/meye.c index ae7d32027bf7..ac7ab6edb06d 100644 --- a/drivers/media/pci/meye/meye.c +++ b/drivers/media/pci/meye/meye.c @@ -811,7 +811,7 @@ again: mchip_hsize() * mchip_vsize() * 2); meye.grab_buffer[reqnr].size = mchip_hsize() * mchip_vsize() * 2; meye.grab_buffer[reqnr].state = MEYE_BUF_DONE; - do_gettimeofday(&meye.grab_buffer[reqnr].timestamp); + v4l2_get_timestamp(&meye.grab_buffer[reqnr].timestamp); meye.grab_buffer[reqnr].sequence = sequence++; kfifo_in_locked(&meye.doneq, (unsigned char *)&reqnr, sizeof(int), &meye.doneq_lock); @@ -832,7 +832,7 @@ again: size); meye.grab_buffer[reqnr].size = size; meye.grab_buffer[reqnr].state = MEYE_BUF_DONE; - do_gettimeofday(&meye.grab_buffer[reqnr].timestamp); + v4l2_get_timestamp(&meye.grab_buffer[reqnr].timestamp); meye.grab_buffer[reqnr].sequence = sequence++; kfifo_in_locked(&meye.doneq, (unsigned char *)&reqnr, sizeof(int), &meye.doneq_lock); @@ -1426,7 +1426,7 @@ static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) return -EINVAL; buf->bytesused = meye.grab_buffer[index].size; - buf->flags = V4L2_BUF_FLAG_MAPPED; + buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; if (meye.grab_buffer[index].state == MEYE_BUF_USING) buf->flags |= V4L2_BUF_FLAG_QUEUED; @@ -1499,7 +1499,7 @@ static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) buf->index = reqnr; buf->bytesused = meye.grab_buffer[reqnr].size; - buf->flags = V4L2_BUF_FLAG_MAPPED; + buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; buf->field = V4L2_FIELD_NONE; buf->timestamp = meye.grab_buffer[reqnr].timestamp; buf->sequence = meye.grab_buffer[reqnr].sequence; diff --git a/drivers/media/pci/saa7134/saa7134-core.c b/drivers/media/pci/saa7134/saa7134-core.c index 8976d0e65813..e1aeb51e25ba 100644 --- a/drivers/media/pci/saa7134/saa7134-core.c +++ b/drivers/media/pci/saa7134/saa7134-core.c @@ -308,7 +308,7 @@ void saa7134_buffer_finish(struct saa7134_dev *dev, /* finish current buffer */ q->curr->vb.state = state; - do_gettimeofday(&q->curr->vb.ts); + v4l2_get_timestamp(&q->curr->vb.ts); wake_up(&q->curr->vb.done); q->curr = NULL; } diff --git a/drivers/media/pci/saa7134/saa7134-dvb.c b/drivers/media/pci/saa7134/saa7134-dvb.c index b209de40a4f8..27915e501db9 100644 --- a/drivers/media/pci/saa7134/saa7134-dvb.c +++ b/drivers/media/pci/saa7134/saa7134-dvb.c @@ -607,6 +607,9 @@ static int configure_tda827x_fe(struct saa7134_dev *dev, /* Get the first frontend */ fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1); + if (!fe0) + return -EINVAL; + fe0->dvb.frontend = dvb_attach(tda10046_attach, cdec_conf, &dev->i2c_adap); if (fe0->dvb.frontend) { if (cdec_conf->i2c_gate) diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c index 4c10205264d4..ed1337a89a26 100644 --- a/drivers/media/pci/sta2x11/sta2x11_vip.c +++ b/drivers/media/pci/sta2x11/sta2x11_vip.c @@ -1088,7 +1088,7 @@ static irqreturn_t vip_irq(int irq, struct sta2x11_vip *vip) REG_WRITE(vip, DVP_CTL, REG_READ(vip, DVP_CTL) & ~DVP_CTL_ENA); if (vip->active) { - do_gettimeofday(&vip->active->ts); + v4l2_get_timestamp(&vip->active->ts); vip->active->field_count++; vip->active->state = VIDEOBUF_DONE; wake_up(&vip->active->done); diff --git a/drivers/media/pci/zoran/zoran_device.c b/drivers/media/pci/zoran/zoran_device.c index a4cd504b8eee..519164c572c8 100644 --- a/drivers/media/pci/zoran/zoran_device.c +++ b/drivers/media/pci/zoran/zoran_device.c @@ -1169,7 +1169,7 @@ zoran_reap_stat_com (struct zoran *zr) } frame = zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME]; buffer = &zr->jpg_buffers.buffer[frame]; - do_gettimeofday(&buffer->bs.timestamp); + v4l2_get_timestamp(&buffer->bs.timestamp); if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) { buffer->bs.length = (stat_com & 0x7fffff) >> 1; @@ -1407,7 +1407,7 @@ zoran_irq (int irq, zr->v4l_buffers.buffer[zr->v4l_grab_frame].state = BUZ_STATE_DONE; zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.seq = zr->v4l_grab_seq; - do_gettimeofday(&zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.timestamp); + v4l2_get_timestamp(&zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.timestamp); zr->v4l_grab_frame = NO_GRAB_ACTIVE; zr->v4l_pend_tail++; } diff --git a/drivers/media/pci/zoran/zoran_driver.c b/drivers/media/pci/zoran/zoran_driver.c index 53f12c7466b0..33521a4f23a7 100644 --- a/drivers/media/pci/zoran/zoran_driver.c +++ b/drivers/media/pci/zoran/zoran_driver.c @@ -1334,7 +1334,7 @@ static int zoran_v4l2_buffer_status(struct zoran_fh *fh, struct zoran *zr = fh->zr; unsigned long flags; - buf->flags = V4L2_BUF_FLAG_MAPPED; + buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; switch (fh->map_mode) { case ZORAN_MAP_MODE_RAW: diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 3dcfea612c42..c071b079de23 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -202,6 +202,15 @@ config VIDEO_SAMSUNG_EXYNOS_GSC help This is a v4l2 driver for Samsung EXYNOS5 SoC G-Scaler. +config VIDEO_SH_VEU + tristate "SuperH VEU mem2mem video processing driver" + depends on VIDEO_DEV && VIDEO_V4L2 + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + Support for the Video Engine Unit (VEU) on SuperH and + SH-Mobile SoCs. + endif # V4L_MEM2MEM_DRIVERS menuconfig V4L_TEST_DRIVERS diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 4817d2802171..42089ba3600f 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -25,6 +25,8 @@ obj-$(CONFIG_VIDEO_MEM2MEM_TESTDEV) += mem2mem_testdev.o obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o obj-$(CONFIG_VIDEO_CODA) += coda.o +obj-$(CONFIG_VIDEO_SH_VEU) += sh_veu.o + obj-$(CONFIG_VIDEO_MEM2MEM_DEINTERLACE) += m2m-deinterlace.o obj-$(CONFIG_VIDEO_S3C_CAMIF) += s3c-camif/ diff --git a/drivers/media/platform/blackfin/Kconfig b/drivers/media/platform/blackfin/Kconfig index ecd5323768b7..519990e17122 100644 --- a/drivers/media/platform/blackfin/Kconfig +++ b/drivers/media/platform/blackfin/Kconfig @@ -2,9 +2,13 @@ config VIDEO_BLACKFIN_CAPTURE tristate "Blackfin Video Capture Driver" depends on VIDEO_V4L2 && BLACKFIN && I2C select VIDEOBUF2_DMA_CONTIG + select VIDEO_BLACKFIN_PPI help V4L2 bridge driver for Blackfin video capture device. Choose PPI or EPPI as its interface. To compile this driver as a module, choose M here: the - module will be called bfin_video_capture. + module will be called bfin_capture. + +config VIDEO_BLACKFIN_PPI + tristate diff --git a/drivers/media/platform/blackfin/Makefile b/drivers/media/platform/blackfin/Makefile index aa3a0a216387..30421bc23080 100644 --- a/drivers/media/platform/blackfin/Makefile +++ b/drivers/media/platform/blackfin/Makefile @@ -1,2 +1,2 @@ -bfin_video_capture-objs := bfin_capture.o ppi.o -obj-$(CONFIG_VIDEO_BLACKFIN_CAPTURE) += bfin_video_capture.o +obj-$(CONFIG_VIDEO_BLACKFIN_CAPTURE) += bfin_capture.o +obj-$(CONFIG_VIDEO_BLACKFIN_PPI) += ppi.o diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c index ec476ef5b709..aa9f846a667e 100644 --- a/drivers/media/platform/blackfin/bfin_capture.c +++ b/drivers/media/platform/blackfin/bfin_capture.c @@ -52,6 +52,7 @@ struct bcap_format { u32 pixelformat; enum v4l2_mbus_pixelcode mbus_code; int bpp; /* bits per pixel */ + int dlen; /* data length for ppi in bits */ }; struct bcap_buffer { @@ -76,10 +77,14 @@ struct bcap_device { unsigned int cur_input; /* current selected standard */ v4l2_std_id std; + /* current selected dv_timings */ + struct v4l2_dv_timings dv_timings; /* used to store pixel format */ struct v4l2_pix_format fmt; /* bits per pixel*/ int bpp; + /* data length for ppi in bits */ + int dlen; /* used to store sensor supported format */ struct bcap_format *sensor_formats; /* number of sensor formats array */ @@ -116,24 +121,35 @@ static const struct bcap_format bcap_formats[] = { .pixelformat = V4L2_PIX_FMT_UYVY, .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8, .bpp = 16, + .dlen = 8, }, { .desc = "YCbCr 4:2:2 Interleaved YUYV", .pixelformat = V4L2_PIX_FMT_YUYV, .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, .bpp = 16, + .dlen = 8, + }, + { + .desc = "YCbCr 4:2:2 Interleaved UYVY", + .pixelformat = V4L2_PIX_FMT_UYVY, + .mbus_code = V4L2_MBUS_FMT_UYVY8_1X16, + .bpp = 16, + .dlen = 16, }, { .desc = "RGB 565", .pixelformat = V4L2_PIX_FMT_RGB565, .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE, .bpp = 16, + .dlen = 8, }, { .desc = "RGB 444", .pixelformat = V4L2_PIX_FMT_RGB444, .mbus_code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE, .bpp = 16, + .dlen = 8, }, }; @@ -366,9 +382,39 @@ static int bcap_start_streaming(struct vb2_queue *vq, unsigned int count) params.width = bcap_dev->fmt.width; params.height = bcap_dev->fmt.height; params.bpp = bcap_dev->bpp; + params.dlen = bcap_dev->dlen; params.ppi_control = bcap_dev->cfg->ppi_control; params.int_mask = bcap_dev->cfg->int_mask; - params.blank_clocks = bcap_dev->cfg->blank_clocks; + if (bcap_dev->cfg->inputs[bcap_dev->cur_input].capabilities + & V4L2_IN_CAP_CUSTOM_TIMINGS) { + struct v4l2_bt_timings *bt = &bcap_dev->dv_timings.bt; + + params.hdelay = bt->hsync + bt->hbackporch; + params.vdelay = bt->vsync + bt->vbackporch; + params.line = bt->hfrontporch + bt->hsync + + bt->hbackporch + bt->width; + params.frame = bt->vfrontporch + bt->vsync + + bt->vbackporch + bt->height; + if (bt->interlaced) + params.frame += bt->il_vfrontporch + bt->il_vsync + + bt->il_vbackporch; + } else if (bcap_dev->cfg->inputs[bcap_dev->cur_input].capabilities + & V4L2_IN_CAP_STD) { + params.hdelay = 0; + params.vdelay = 0; + if (bcap_dev->std & V4L2_STD_525_60) { + params.line = 858; + params.frame = 525; + } else { + params.line = 864; + params.frame = 625; + } + } else { + params.hdelay = 0; + params.vdelay = 0; + params.line = params.width + bcap_dev->cfg->blank_pixels; + params.frame = params.height; + } ret = ppi->ops->set_params(ppi, ¶ms); if (ret < 0) { v4l2_err(&bcap_dev->v4l2_dev, @@ -484,15 +530,13 @@ static irqreturn_t bcap_isr(int irq, void *dev_id) { struct ppi_if *ppi = dev_id; struct bcap_device *bcap_dev = ppi->priv; - struct timeval timevalue; struct vb2_buffer *vb = &bcap_dev->cur_frm->vb; dma_addr_t addr; spin_lock(&bcap_dev->lock); if (bcap_dev->cur_frm != bcap_dev->next_frm) { - do_gettimeofday(&timevalue); - vb->v4l2_buf.timestamp = timevalue; + v4l2_get_timestamp(&vb->v4l2_buf.timestamp); vb2_buffer_done(vb, VB2_BUF_STATE_DONE); bcap_dev->cur_frm = bcap_dev->next_frm; } @@ -602,6 +646,37 @@ static int bcap_s_std(struct file *file, void *priv, v4l2_std_id *std) return 0; } +static int bcap_g_dv_timings(struct file *file, void *priv, + struct v4l2_dv_timings *timings) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + int ret; + + ret = v4l2_subdev_call(bcap_dev->sd, video, + g_dv_timings, timings); + if (ret < 0) + return ret; + + bcap_dev->dv_timings = *timings; + return 0; +} + +static int bcap_s_dv_timings(struct file *file, void *priv, + struct v4l2_dv_timings *timings) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + int ret; + if (vb2_is_busy(&bcap_dev->buffer_queue)) + return -EBUSY; + + ret = v4l2_subdev_call(bcap_dev->sd, video, s_dv_timings, timings); + if (ret < 0) + return ret; + + bcap_dev->dv_timings = *timings; + return 0; +} + static int bcap_enum_input(struct file *file, void *priv, struct v4l2_input *input) { @@ -650,13 +725,15 @@ static int bcap_s_input(struct file *file, void *priv, unsigned int index) return ret; } bcap_dev->cur_input = index; + /* if this route has specific config, update ppi control */ + if (route->ppi_control) + config->ppi_control = route->ppi_control; return 0; } static int bcap_try_format(struct bcap_device *bcap, struct v4l2_pix_format *pixfmt, - enum v4l2_mbus_pixelcode *mbus_code, - int *bpp) + struct bcap_format *bcap_fmt) { struct bcap_format *sf = bcap->sensor_formats; struct bcap_format *fmt = NULL; @@ -671,16 +748,20 @@ static int bcap_try_format(struct bcap_device *bcap, if (i == bcap->num_sensor_formats) fmt = &sf[0]; - if (mbus_code) - *mbus_code = fmt->mbus_code; - if (bpp) - *bpp = fmt->bpp; v4l2_fill_mbus_format(&mbus_fmt, pixfmt, fmt->mbus_code); ret = v4l2_subdev_call(bcap->sd, video, try_mbus_fmt, &mbus_fmt); if (ret < 0) return ret; v4l2_fill_pix_format(pixfmt, &mbus_fmt); + if (bcap_fmt) { + for (i = 0; i < bcap->num_sensor_formats; i++) { + fmt = &sf[i]; + if (mbus_fmt.code == fmt->mbus_code) + break; + } + *bcap_fmt = *fmt; + } pixfmt->bytesperline = pixfmt->width * fmt->bpp / 8; pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height; return 0; @@ -709,7 +790,7 @@ static int bcap_try_fmt_vid_cap(struct file *file, void *priv, struct bcap_device *bcap_dev = video_drvdata(file); struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; - return bcap_try_format(bcap_dev, pixfmt, NULL, NULL); + return bcap_try_format(bcap_dev, pixfmt, NULL); } static int bcap_g_fmt_vid_cap(struct file *file, void *priv, @@ -726,24 +807,25 @@ static int bcap_s_fmt_vid_cap(struct file *file, void *priv, { struct bcap_device *bcap_dev = video_drvdata(file); struct v4l2_mbus_framefmt mbus_fmt; - enum v4l2_mbus_pixelcode mbus_code; + struct bcap_format bcap_fmt; struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; - int ret, bpp; + int ret; if (vb2_is_busy(&bcap_dev->buffer_queue)) return -EBUSY; /* see if format works */ - ret = bcap_try_format(bcap_dev, pixfmt, &mbus_code, &bpp); + ret = bcap_try_format(bcap_dev, pixfmt, &bcap_fmt); if (ret < 0) return ret; - v4l2_fill_mbus_format(&mbus_fmt, pixfmt, mbus_code); + v4l2_fill_mbus_format(&mbus_fmt, pixfmt, bcap_fmt.mbus_code); ret = v4l2_subdev_call(bcap_dev->sd, video, s_mbus_fmt, &mbus_fmt); if (ret < 0) return ret; bcap_dev->fmt = *pixfmt; - bcap_dev->bpp = bpp; + bcap_dev->bpp = bcap_fmt.bpp; + bcap_dev->dlen = bcap_fmt.dlen; return 0; } @@ -834,6 +916,8 @@ static const struct v4l2_ioctl_ops bcap_ioctl_ops = { .vidioc_querystd = bcap_querystd, .vidioc_s_std = bcap_s_std, .vidioc_g_std = bcap_g_std, + .vidioc_s_dv_timings = bcap_s_dv_timings, + .vidioc_g_dv_timings = bcap_g_dv_timings, .vidioc_reqbufs = bcap_reqbufs, .vidioc_querybuf = bcap_querybuf, .vidioc_qbuf = bcap_qbuf, @@ -869,6 +953,7 @@ static int __devinit bcap_probe(struct platform_device *pdev) struct i2c_adapter *i2c_adap; struct bfin_capture_config *config; struct vb2_queue *q; + struct bcap_route *route; int ret; config = pdev->dev.platform_data; @@ -978,6 +1063,12 @@ static int __devinit bcap_probe(struct platform_device *pdev) NULL); if (bcap_dev->sd) { int i; + if (!config->num_inputs) { + v4l2_err(&bcap_dev->v4l2_dev, + "Unable to work without input\n"); + goto err_unreg_vdev; + } + /* update tvnorms from the sub devices */ for (i = 0; i < config->num_inputs; i++) vfd->tvnorms |= config->inputs[i].std; @@ -989,8 +1080,24 @@ static int __devinit bcap_probe(struct platform_device *pdev) v4l2_info(&bcap_dev->v4l2_dev, "v4l2 sub device registered\n"); + /* + * explicitly set input, otherwise some boards + * may not work at the state as we expected + */ + route = &config->routes[0]; + ret = v4l2_subdev_call(bcap_dev->sd, video, s_routing, + route->input, route->output, 0); + if ((ret < 0) && (ret != -ENOIOCTLCMD)) { + v4l2_err(&bcap_dev->v4l2_dev, "Failed to set input\n"); + goto err_unreg_vdev; + } + bcap_dev->cur_input = 0; + /* if this route has specific config, update ppi control */ + if (route->ppi_control) + config->ppi_control = route->ppi_control; + /* now we can probe the default state */ - if (vfd->tvnorms) { + if (config->inputs[0].capabilities & V4L2_IN_CAP_STD) { v4l2_std_id std; ret = v4l2_subdev_call(bcap_dev->sd, core, g_std, &std); if (ret) { @@ -1000,6 +1107,17 @@ static int __devinit bcap_probe(struct platform_device *pdev) } bcap_dev->std = std; } + if (config->inputs[0].capabilities & V4L2_IN_CAP_CUSTOM_TIMINGS) { + struct v4l2_dv_timings dv_timings; + ret = v4l2_subdev_call(bcap_dev->sd, video, + g_dv_timings, &dv_timings); + if (ret) { + v4l2_err(&bcap_dev->v4l2_dev, + "Unable to get dv timings\n"); + goto err_unreg_vdev; + } + bcap_dev->dv_timings = dv_timings; + } ret = bcap_init_sensor_formats(bcap_dev); if (ret) { v4l2_err(&bcap_dev->v4l2_dev, diff --git a/drivers/media/platform/blackfin/ppi.c b/drivers/media/platform/blackfin/ppi.c index d29592186b02..1e24584605f2 100644 --- a/drivers/media/platform/blackfin/ppi.c +++ b/drivers/media/platform/blackfin/ppi.c @@ -17,6 +17,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include <linux/module.h> #include <linux/slab.h> #include <asm/bfin_ppi.h> @@ -67,6 +68,13 @@ static irqreturn_t ppi_irq_err(int irq, void *dev_id) bfin_write16(®->status, 0xffff); break; } + case PPI_TYPE_EPPI3: + { + struct bfin_eppi3_regs *reg = info->base; + + bfin_write32(®->stat, 0xc0ff); + break; + } default: break; } @@ -128,6 +136,12 @@ static int ppi_start(struct ppi_if *ppi) bfin_write32(®->control, ppi->ppi_control); break; } + case PPI_TYPE_EPPI3: + { + struct bfin_eppi3_regs *reg = info->base; + bfin_write32(®->ctl, ppi->ppi_control); + break; + } default: return -EINVAL; } @@ -155,6 +169,12 @@ static int ppi_stop(struct ppi_if *ppi) bfin_write32(®->control, ppi->ppi_control); break; } + case PPI_TYPE_EPPI3: + { + struct bfin_eppi3_regs *reg = info->base; + bfin_write32(®->ctl, ppi->ppi_control); + break; + } default: return -EINVAL; } @@ -171,17 +191,23 @@ static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params) { const struct ppi_info *info = ppi->info; int dma32 = 0; - int dma_config, bytes_per_line, lines_per_frame; + int dma_config, bytes_per_line; + int hcount, hdelay, samples_per_line; bytes_per_line = params->width * params->bpp / 8; - lines_per_frame = params->height; + /* convert parameters unit from pixels to samples */ + hcount = params->width * params->bpp / params->dlen; + hdelay = params->hdelay * params->bpp / params->dlen; + samples_per_line = params->line * params->bpp / params->dlen; if (params->int_mask == 0xFFFFFFFF) ppi->err_int = false; else ppi->err_int = true; - dma_config = (DMA_FLOW_STOP | WNR | RESTART | DMA2D | DI_EN); + dma_config = (DMA_FLOW_STOP | RESTART | DMA2D | DI_EN_Y); ppi->ppi_control = params->ppi_control & ~PORT_EN; + if (!(ppi->ppi_control & PORT_DIR)) + dma_config |= WNR; switch (info->type) { case PPI_TYPE_PPI: { @@ -191,8 +217,8 @@ static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params) dma32 = 1; bfin_write16(®->control, ppi->ppi_control); - bfin_write16(®->count, bytes_per_line - 1); - bfin_write16(®->frame, lines_per_frame); + bfin_write16(®->count, samples_per_line - 1); + bfin_write16(®->frame, params->frame); break; } case PPI_TYPE_EPPI: @@ -204,12 +230,31 @@ static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params) dma32 = 1; bfin_write32(®->control, ppi->ppi_control); - bfin_write16(®->line, bytes_per_line + params->blank_clocks); - bfin_write16(®->frame, lines_per_frame); - bfin_write16(®->hdelay, 0); - bfin_write16(®->vdelay, 0); - bfin_write16(®->hcount, bytes_per_line); - bfin_write16(®->vcount, lines_per_frame); + bfin_write16(®->line, samples_per_line); + bfin_write16(®->frame, params->frame); + bfin_write16(®->hdelay, hdelay); + bfin_write16(®->vdelay, params->vdelay); + bfin_write16(®->hcount, hcount); + bfin_write16(®->vcount, params->height); + break; + } + case PPI_TYPE_EPPI3: + { + struct bfin_eppi3_regs *reg = info->base; + + if ((params->ppi_control & PACK_EN) + || (params->ppi_control & 0x70000) > DLEN_16) + dma32 = 1; + + bfin_write32(®->ctl, ppi->ppi_control); + bfin_write32(®->line, samples_per_line); + bfin_write32(®->frame, params->frame); + bfin_write32(®->hdly, hdelay); + bfin_write32(®->vdly, params->vdelay); + bfin_write32(®->hcnt, hcount); + bfin_write32(®->vcnt, params->height); + if (params->int_mask) + bfin_write32(®->imsk, params->int_mask & 0xFF); break; } default: @@ -217,17 +262,17 @@ static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params) } if (dma32) { - dma_config |= WDSIZE_32; + dma_config |= WDSIZE_32 | PSIZE_32; set_dma_x_count(info->dma_ch, bytes_per_line >> 2); set_dma_x_modify(info->dma_ch, 4); set_dma_y_modify(info->dma_ch, 4); } else { - dma_config |= WDSIZE_16; + dma_config |= WDSIZE_16 | PSIZE_16; set_dma_x_count(info->dma_ch, bytes_per_line >> 1); set_dma_x_modify(info->dma_ch, 2); set_dma_y_modify(info->dma_ch, 2); } - set_dma_y_count(info->dma_ch, lines_per_frame); + set_dma_y_count(info->dma_ch, params->height); set_dma_config(info->dma_ch, dma_config); SSYNC(); @@ -263,9 +308,15 @@ struct ppi_if *ppi_create_instance(const struct ppi_info *info) pr_info("ppi probe success\n"); return ppi; } +EXPORT_SYMBOL(ppi_create_instance); void ppi_delete_instance(struct ppi_if *ppi) { peripheral_free_list(ppi->info->pin_req); kfree(ppi); } +EXPORT_SYMBOL(ppi_delete_instance); + +MODULE_DESCRIPTION("Analog Devices PPI driver"); +MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 7b8b547f2d51..2721f839852f 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -178,6 +178,10 @@ struct coda_ctx { int idx; }; +static const u8 coda_filler_nal[14] = { 0x00, 0x00, 0x00, 0x01, 0x0c, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80 }; +static const u8 coda_filler_size[8] = { 0, 7, 14, 13, 12, 11, 10, 9 }; + static inline void coda_write(struct coda_dev *dev, u32 data, u32 reg) { v4l2_dbg(1, coda_debug, &dev->v4l2_dev, @@ -944,6 +948,24 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx, struct coda_q_data *q_d return 0; } +static int coda_h264_padding(int size, char *p) +{ + int nal_size; + int diff; + + diff = size - (size & ~0x7); + if (diff == 0) + return 0; + + nal_size = coda_filler_size[diff]; + memcpy(p, coda_filler_nal, nal_size); + + /* Add rbsp stop bit and trailing at the end */ + *(p + nal_size - 1) = 0x80; + + return nal_size; +} + static int coda_start_streaming(struct vb2_queue *q, unsigned int count) { struct coda_ctx *ctx = vb2_get_drv_priv(q); @@ -1171,7 +1193,15 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) coda_read(dev, CODA_CMD_ENC_HEADER_BB_START); memcpy(&ctx->vpu_header[1][0], vb2_plane_vaddr(buf, 0), ctx->vpu_header_size[1]); - ctx->vpu_header_size[2] = 0; + /* + * Length of H.264 headers is variable and thus it might not be + * aligned for the coda to append the encoded frame. In that is + * the case a filler NAL must be added to header 2. + */ + ctx->vpu_header_size[2] = coda_h264_padding( + (ctx->vpu_header_size[0] + + ctx->vpu_header_size[1]), + ctx->vpu_header[2]); break; case V4L2_PIX_FMT_MPEG4: /* diff --git a/drivers/media/platform/davinci/Kconfig b/drivers/media/platform/davinci/Kconfig index 3c56037c82fc..ccfde4eb626a 100644 --- a/drivers/media/platform/davinci/Kconfig +++ b/drivers/media/platform/davinci/Kconfig @@ -97,25 +97,15 @@ config VIDEO_ISIF To compile this driver as a module, choose M here: the module will be called vpfe. -config VIDEO_DM644X_VPBE - tristate "DM644X VPBE HW module" - depends on ARCH_DAVINCI_DM644x +config VIDEO_DAVINCI_VPBE_DISPLAY + tristate "DM644X/DM365/DM355 VPBE HW module" + depends on ARCH_DAVINCI_DM644x || ARCH_DAVINCI_DM355 || ARCH_DAVINCI_DM365 select VIDEO_VPSS_SYSTEM select VIDEOBUF2_DMA_CONTIG help - Enables VPBE modules used for display on a DM644x - SoC. + Enables Davinci VPBE module used for display devices. + This module is common for following DM644x/DM365/DM355 + based display devices. To compile this driver as a module, choose M here: the module will be called vpbe. - - -config VIDEO_VPBE_DISPLAY - tristate "VPBE V4L2 Display driver" - depends on ARCH_DAVINCI_DM644x - select VIDEO_DM644X_VPBE - help - Enables VPBE V4L2 Display driver on a DM644x device - - To compile this driver as a module, choose M here: the - module will be called vpbe_display. diff --git a/drivers/media/platform/davinci/Makefile b/drivers/media/platform/davinci/Makefile index 74ed92d09257..f40f5219ca50 100644 --- a/drivers/media/platform/davinci/Makefile +++ b/drivers/media/platform/davinci/Makefile @@ -16,5 +16,5 @@ obj-$(CONFIG_VIDEO_VPFE_CAPTURE) += vpfe_capture.o obj-$(CONFIG_VIDEO_DM6446_CCDC) += dm644x_ccdc.o obj-$(CONFIG_VIDEO_DM355_CCDC) += dm355_ccdc.o obj-$(CONFIG_VIDEO_ISIF) += isif.o -obj-$(CONFIG_VIDEO_DM644X_VPBE) += vpbe.o vpbe_osd.o vpbe_venc.o -obj-$(CONFIG_VIDEO_VPBE_DISPLAY) += vpbe_display.o +obj-$(CONFIG_VIDEO_DAVINCI_VPBE_DISPLAY) += vpbe.o vpbe_osd.o \ + vpbe_venc.o vpbe_display.o diff --git a/drivers/media/platform/davinci/vpbe.c b/drivers/media/platform/davinci/vpbe.c index 7f5cf9b347b2..fe2b9ce0bce8 100644 --- a/drivers/media/platform/davinci/vpbe.c +++ b/drivers/media/platform/davinci/vpbe.c @@ -558,9 +558,9 @@ static int platform_device_get(struct device *dev, void *data) struct platform_device *pdev = to_platform_device(dev); struct vpbe_device *vpbe_dev = data; - if (strcmp("vpbe-osd", pdev->name) == 0) + if (strstr(pdev->name, "vpbe-osd") != NULL) vpbe_dev->osd_device = platform_get_drvdata(pdev); - if (strcmp("vpbe-venc", pdev->name) == 0) + if (strstr(pdev->name, "vpbe-venc") != NULL) vpbe_dev->venc_device = dev_get_platdata(&pdev->dev); return 0; @@ -584,7 +584,6 @@ static int vpbe_initialize(struct device *dev, struct vpbe_device *vpbe_dev) struct v4l2_subdev **enc_subdev; struct osd_state *osd_device; struct i2c_adapter *i2c_adap; - int output_index; int num_encoders; int ret = 0; int err; @@ -731,7 +730,6 @@ static int vpbe_initialize(struct device *dev, struct vpbe_device *vpbe_dev) /* set the current encoder and output to that of venc by default */ vpbe_dev->current_sd_index = 0; vpbe_dev->current_out_index = 0; - output_index = 0; mutex_unlock(&vpbe_dev->lock); diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index 2bfde7958fef..d078738b35d6 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -791,7 +791,6 @@ static int vpbe_display_g_crop(struct file *file, void *priv, struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; struct osd_state *osd_device = fh->disp_dev->osd_device; struct v4l2_rect *rect = &crop->c; - int ret; v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_CROP, layer id = %d\n", @@ -799,7 +798,7 @@ static int vpbe_display_g_crop(struct file *file, void *priv, if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); - ret = -EINVAL; + return -EINVAL; } osd_device->ops.get_layer_config(osd_device, layer->layer_info.id, cfg); @@ -1393,9 +1392,9 @@ static int vpbe_display_reqbufs(struct file *file, void *priv, } /* Initialize videobuf queue as per the buffer type */ layer->alloc_ctx = vb2_dma_contig_init_ctx(vpbe_dev->pdev); - if (!layer->alloc_ctx) { + if (IS_ERR(layer->alloc_ctx)) { v4l2_err(&vpbe_dev->v4l2_dev, "Failed to get the context\n"); - return -EINVAL; + return PTR_ERR(layer->alloc_ctx); } q = &layer->buffer_queue; memset(q, 0, sizeof(*q)); @@ -1656,7 +1655,7 @@ static int vpbe_device_get(struct device *dev, void *data) if (strcmp("vpbe_controller", pdev->name) == 0) vpbe_disp->vpbe_dev = platform_get_drvdata(pdev); - if (strcmp("vpbe-osd", pdev->name) == 0) + if (strstr(pdev->name, "vpbe-osd") != NULL) vpbe_disp->osd_device = platform_get_drvdata(pdev); return 0; diff --git a/drivers/media/platform/davinci/vpbe_osd.c b/drivers/media/platform/davinci/vpbe_osd.c index 707f243f810d..12ad17c52ef3 100644 --- a/drivers/media/platform/davinci/vpbe_osd.c +++ b/drivers/media/platform/davinci/vpbe_osd.c @@ -39,7 +39,22 @@ #include <linux/io.h> #include "vpbe_osd_regs.h" -#define MODULE_NAME VPBE_OSD_SUBDEV_NAME +#define MODULE_NAME "davinci-vpbe-osd" + +static struct platform_device_id vpbe_osd_devtype[] = { + { + .name = DM644X_VPBE_OSD_SUBDEV_NAME, + .driver_data = VPBE_VERSION_1, + }, { + .name = DM365_VPBE_OSD_SUBDEV_NAME, + .driver_data = VPBE_VERSION_2, + }, { + .name = DM355_VPBE_OSD_SUBDEV_NAME, + .driver_data = VPBE_VERSION_3, + }, +}; + +MODULE_DEVICE_TABLE(platform, vpbe_osd_devtype); /* register access routines */ static inline u32 osd_read(struct osd_state *sd, u32 offset) @@ -129,7 +144,7 @@ static int _osd_dm6446_vid0_pingpong(struct osd_state *sd, struct osd_platform_data *pdata; pdata = (struct osd_platform_data *)sd->dev->platform_data; - if (pdata->field_inv_wa_enable) { + if (pdata != NULL && pdata->field_inv_wa_enable) { if (!field_inversion || !lconfig->interlaced) { osd_write(sd, fb_base_phys & ~0x1F, OSD_VIDWIN0ADR); @@ -1526,7 +1541,7 @@ static const struct vpbe_osd_ops osd_ops = { static int osd_probe(struct platform_device *pdev) { - struct osd_platform_data *pdata; + const struct platform_device_id *pdev_id; struct osd_state *osd; struct resource *res; int ret = 0; @@ -1535,16 +1550,15 @@ static int osd_probe(struct platform_device *pdev) if (osd == NULL) return -ENOMEM; - osd->dev = &pdev->dev; - pdata = (struct osd_platform_data *)pdev->dev.platform_data; - osd->vpbe_type = (enum vpbe_version)pdata->vpbe_type; - if (NULL == pdev->dev.platform_data) { - dev_err(osd->dev, "No platform data defined for OSD" - " sub device\n"); - ret = -ENOENT; + pdev_id = platform_get_device_id(pdev); + if (!pdev_id) { + ret = -EINVAL; goto free_mem; } + osd->dev = &pdev->dev; + osd->vpbe_type = pdev_id->driver_data; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(osd->dev, "Unable to get OSD register address map\n"); @@ -1595,6 +1609,7 @@ static struct platform_driver osd_driver = { .name = MODULE_NAME, .owner = THIS_MODULE, }, + .id_table = vpbe_osd_devtype }; module_platform_driver(osd_driver); diff --git a/drivers/media/platform/davinci/vpbe_venc.c b/drivers/media/platform/davinci/vpbe_venc.c index aed7369b962a..bdbebd59df98 100644 --- a/drivers/media/platform/davinci/vpbe_venc.c +++ b/drivers/media/platform/davinci/vpbe_venc.c @@ -38,7 +38,22 @@ #include "vpbe_venc_regs.h" -#define MODULE_NAME VPBE_VENC_SUBDEV_NAME +#define MODULE_NAME "davinci-vpbe-venc" + +static struct platform_device_id vpbe_venc_devtype[] = { + { + .name = DM644X_VPBE_VENC_SUBDEV_NAME, + .driver_data = VPBE_VERSION_1, + }, { + .name = DM365_VPBE_VENC_SUBDEV_NAME, + .driver_data = VPBE_VERSION_2, + }, { + .name = DM355_VPBE_VENC_SUBDEV_NAME, + .driver_data = VPBE_VERSION_3, + }, +}; + +MODULE_DEVICE_TABLE(platform, vpbe_venc_devtype); static int debug = 2; module_param(debug, int, 0644); @@ -54,6 +69,7 @@ struct venc_state { spinlock_t lock; void __iomem *venc_base; void __iomem *vdaccfg_reg; + enum vpbe_version venc_type; }; static inline struct venc_state *to_state(struct v4l2_subdev *sd) @@ -127,7 +143,7 @@ static int venc_set_dac(struct v4l2_subdev *sd, u32 out_index) static void venc_enabledigitaloutput(struct v4l2_subdev *sd, int benable) { struct venc_state *venc = to_state(sd); - struct venc_platform_data *pdata = venc->pdata; + v4l2_dbg(debug, 2, sd, "venc_enabledigitaloutput\n"); if (benable) { @@ -159,7 +175,7 @@ static void venc_enabledigitaloutput(struct v4l2_subdev *sd, int benable) /* Disable LCD output control (accepting default polarity) */ venc_write(sd, VENC_LCDOUT, 0); - if (pdata->venc_type != VPBE_VERSION_3) + if (venc->venc_type != VPBE_VERSION_3) venc_write(sd, VENC_CMPNT, 0x100); venc_write(sd, VENC_HSPLS, 0); venc_write(sd, VENC_HINT, 0); @@ -203,11 +219,11 @@ static int venc_set_ntsc(struct v4l2_subdev *sd) venc_enabledigitaloutput(sd, 0); - if (pdata->venc_type == VPBE_VERSION_3) { + if (venc->venc_type == VPBE_VERSION_3) { venc_write(sd, VENC_CLKCTL, 0x01); venc_write(sd, VENC_VIDCTL, 0); val = vdaccfg_write(sd, VDAC_CONFIG_SD_V3); - } else if (pdata->venc_type == VPBE_VERSION_2) { + } else if (venc->venc_type == VPBE_VERSION_2) { venc_write(sd, VENC_CLKCTL, 0x01); venc_write(sd, VENC_VIDCTL, 0); vdaccfg_write(sd, VDAC_CONFIG_SD_V2); @@ -238,7 +254,6 @@ static int venc_set_ntsc(struct v4l2_subdev *sd) static int venc_set_pal(struct v4l2_subdev *sd) { struct venc_state *venc = to_state(sd); - struct venc_platform_data *pdata = venc->pdata; v4l2_dbg(debug, 2, sd, "venc_set_pal\n"); @@ -249,11 +264,11 @@ static int venc_set_pal(struct v4l2_subdev *sd) venc_enabledigitaloutput(sd, 0); - if (pdata->venc_type == VPBE_VERSION_3) { + if (venc->venc_type == VPBE_VERSION_3) { venc_write(sd, VENC_CLKCTL, 0x1); venc_write(sd, VENC_VIDCTL, 0); vdaccfg_write(sd, VDAC_CONFIG_SD_V3); - } else if (pdata->venc_type == VPBE_VERSION_2) { + } else if (venc->venc_type == VPBE_VERSION_2) { venc_write(sd, VENC_CLKCTL, 0x1); venc_write(sd, VENC_VIDCTL, 0); vdaccfg_write(sd, VDAC_CONFIG_SD_V2); @@ -293,8 +308,8 @@ static int venc_set_480p59_94(struct v4l2_subdev *sd) struct venc_platform_data *pdata = venc->pdata; v4l2_dbg(debug, 2, sd, "venc_set_480p59_94\n"); - if ((pdata->venc_type != VPBE_VERSION_1) && - (pdata->venc_type != VPBE_VERSION_2)) + if (venc->venc_type != VPBE_VERSION_1 && + venc->venc_type != VPBE_VERSION_2) return -EINVAL; /* Setup clock at VPSS & VENC for SD */ @@ -303,12 +318,12 @@ static int venc_set_480p59_94(struct v4l2_subdev *sd) venc_enabledigitaloutput(sd, 0); - if (pdata->venc_type == VPBE_VERSION_2) + if (venc->venc_type == VPBE_VERSION_2) vdaccfg_write(sd, VDAC_CONFIG_HD_V2); venc_write(sd, VENC_OSDCLK0, 0); venc_write(sd, VENC_OSDCLK1, 1); - if (pdata->venc_type == VPBE_VERSION_1) { + if (venc->venc_type == VPBE_VERSION_1) { venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAFRQ, VENC_VDPRO_DAFRQ); venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAUPS, @@ -341,8 +356,8 @@ static int venc_set_576p50(struct v4l2_subdev *sd) v4l2_dbg(debug, 2, sd, "venc_set_576p50\n"); - if ((pdata->venc_type != VPBE_VERSION_1) && - (pdata->venc_type != VPBE_VERSION_2)) + if (venc->venc_type != VPBE_VERSION_1 && + venc->venc_type != VPBE_VERSION_2) return -EINVAL; /* Setup clock at VPSS & VENC for SD */ if (pdata->setup_clock(VPBE_ENC_CUSTOM_TIMINGS, 27000000) < 0) @@ -350,13 +365,13 @@ static int venc_set_576p50(struct v4l2_subdev *sd) venc_enabledigitaloutput(sd, 0); - if (pdata->venc_type == VPBE_VERSION_2) + if (venc->venc_type == VPBE_VERSION_2) vdaccfg_write(sd, VDAC_CONFIG_HD_V2); venc_write(sd, VENC_OSDCLK0, 0); venc_write(sd, VENC_OSDCLK1, 1); - if (pdata->venc_type == VPBE_VERSION_1) { + if (venc->venc_type == VPBE_VERSION_1) { venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAFRQ, VENC_VDPRO_DAFRQ); venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAUPS, @@ -460,14 +475,14 @@ static int venc_s_dv_timings(struct v4l2_subdev *sd, else if (height == 480) return venc_set_480p59_94(sd); else if ((height == 720) && - (venc->pdata->venc_type == VPBE_VERSION_2)) { + (venc->venc_type == VPBE_VERSION_2)) { /* TBD setup internal 720p mode here */ ret = venc_set_720p60_internal(sd); /* for DM365 VPBE, there is DAC inside */ vdaccfg_write(sd, VDAC_CONFIG_HD_V2); return ret; } else if ((height == 1080) && - (venc->pdata->venc_type == VPBE_VERSION_2)) { + (venc->venc_type == VPBE_VERSION_2)) { /* TBD setup internal 1080i mode here */ ret = venc_set_1080i30_internal(sd); /* for DM365 VPBE, there is DAC inside */ @@ -556,7 +571,7 @@ static int venc_device_get(struct device *dev, void *data) struct platform_device *pdev = to_platform_device(dev); struct venc_state **venc = data; - if (strcmp(MODULE_NAME, pdev->name) == 0) + if (strstr(pdev->name, "vpbe-venc") != NULL) *venc = platform_get_drvdata(pdev); return 0; @@ -593,6 +608,7 @@ EXPORT_SYMBOL(venc_sub_dev_init); static int venc_probe(struct platform_device *pdev) { + const struct platform_device_id *pdev_id; struct venc_state *venc; struct resource *res; int ret; @@ -601,6 +617,12 @@ static int venc_probe(struct platform_device *pdev) if (venc == NULL) return -ENOMEM; + pdev_id = platform_get_device_id(pdev); + if (!pdev_id) { + ret = -EINVAL; + goto free_mem; + } + venc->venc_type = pdev_id->driver_data; venc->pdev = &pdev->dev; venc->pdata = pdev->dev.platform_data; if (NULL == venc->pdata) { @@ -630,7 +652,7 @@ static int venc_probe(struct platform_device *pdev) goto release_venc_mem_region; } - if (venc->pdata->venc_type != VPBE_VERSION_1) { + if (venc->venc_type != VPBE_VERSION_1) { res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!res) { dev_err(venc->pdev, @@ -681,7 +703,7 @@ static int venc_remove(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); iounmap((void *)venc->venc_base); release_mem_region(res->start, resource_size(res)); - if (venc->pdata->venc_type != VPBE_VERSION_1) { + if (venc->venc_type != VPBE_VERSION_1) { res = platform_get_resource(pdev, IORESOURCE_MEM, 1); iounmap((void *)venc->vdaccfg_reg); release_mem_region(res->start, resource_size(res)); @@ -698,6 +720,7 @@ static struct platform_driver venc_driver = { .name = MODULE_NAME, .owner = THIS_MODULE, }, + .id_table = vpbe_venc_devtype }; module_platform_driver(venc_driver); diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c index 8be492cd8ed4..65f4264bd5b4 100644 --- a/drivers/media/platform/davinci/vpfe_capture.c +++ b/drivers/media/platform/davinci/vpfe_capture.c @@ -560,10 +560,7 @@ static void vpfe_schedule_bottom_field(struct vpfe_device *vpfe_dev) static void vpfe_process_buffer_complete(struct vpfe_device *vpfe_dev) { - struct timeval timevalue; - - do_gettimeofday(&timevalue); - vpfe_dev->cur_frm->ts = timevalue; + v4l2_get_timestamp(&vpfe_dev->cur_frm->ts); vpfe_dev->cur_frm->state = VIDEOBUF_DONE; vpfe_dev->cur_frm->size = vpfe_dev->fmt.fmt.pix.sizeimage; wake_up_interruptible(&vpfe_dev->cur_frm->done); diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index a409ccefb380..5892d2bc8eee 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -411,7 +411,7 @@ static struct vb2_ops video_qops = { */ static void vpif_process_buffer_complete(struct common_obj *common) { - do_gettimeofday(&common->cur_frm->vb.v4l2_buf.timestamp); + v4l2_get_timestamp(&common->cur_frm->vb.v4l2_buf.timestamp); vb2_buffer_done(&common->cur_frm->vb, VB2_BUF_STATE_DONE); /* Make curFrm pointing to nextFrm */ diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index 9f2b603be9c9..dd249c96126d 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -402,7 +402,7 @@ static void process_interlaced_mode(int fid, struct common_obj *common) /* one frame is displayed If next frame is * available, release cur_frm and move on */ /* Copy frame display time */ - do_gettimeofday(&common->cur_frm->vb.v4l2_buf.timestamp); + v4l2_get_timestamp(&common->cur_frm->vb.v4l2_buf.timestamp); /* Change status of the cur_frm */ vb2_buffer_done(&common->cur_frm->vb, VB2_BUF_STATE_DONE); @@ -462,8 +462,8 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id) if (!channel_first_int[i][channel_id]) { /* Mark status of the cur_frm to * done and unlock semaphore on it */ - do_gettimeofday(&common->cur_frm->vb. - v4l2_buf.timestamp); + v4l2_get_timestamp(&common->cur_frm->vb. + v4l2_buf.timestamp); vb2_buffer_done(&common->cur_frm->vb, VB2_BUF_STATE_DONE); /* Make cur_frm pointing to next_frm */ diff --git a/drivers/media/platform/davinci/vpss.c b/drivers/media/platform/davinci/vpss.c index 146e4b01ac17..d945f94053a8 100644 --- a/drivers/media/platform/davinci/vpss.c +++ b/drivers/media/platform/davinci/vpss.c @@ -51,13 +51,29 @@ MODULE_AUTHOR("Texas Instruments"); /* VENCINT - vpss_int8 */ #define DM355_VPSSBL_EVTSEL_DEFAULT 0x4 -#define DM365_ISP5_PCCR 0x04 +#define DM365_ISP5_PCCR 0x04 +#define DM365_ISP5_PCCR_BL_CLK_ENABLE BIT(0) +#define DM365_ISP5_PCCR_ISIF_CLK_ENABLE BIT(1) +#define DM365_ISP5_PCCR_H3A_CLK_ENABLE BIT(2) +#define DM365_ISP5_PCCR_RSZ_CLK_ENABLE BIT(3) +#define DM365_ISP5_PCCR_IPIPE_CLK_ENABLE BIT(4) +#define DM365_ISP5_PCCR_IPIPEIF_CLK_ENABLE BIT(5) +#define DM365_ISP5_PCCR_RSV BIT(6) + +#define DM365_ISP5_BCR 0x08 +#define DM365_ISP5_BCR_ISIF_OUT_ENABLE BIT(1) + #define DM365_ISP5_INTSEL1 0x10 #define DM365_ISP5_INTSEL2 0x14 #define DM365_ISP5_INTSEL3 0x18 #define DM365_ISP5_CCDCMUX 0x20 #define DM365_ISP5_PG_FRAME_SIZE 0x28 #define DM365_VPBE_CLK_CTRL 0x00 + +#define VPSS_CLK_CTRL 0x01c40044 +#define VPSS_CLK_CTRL_VENCCLKEN BIT(3) +#define VPSS_CLK_CTRL_DACCLKEN BIT(4) + /* * vpss interrupts. VDINT0 - vpss_int0, VDINT1 - vpss_int1, * AF - vpss_int3 @@ -95,12 +111,19 @@ struct vpss_hw_ops { void (*select_ccdc_source)(enum vpss_ccdc_source_sel src_sel); /* clear wbl overflow bit */ int (*clear_wbl_overflow)(enum vpss_wbl_sel wbl_sel); + /* set sync polarity */ + void (*set_sync_pol)(struct vpss_sync_pol); + /* set the PG_FRAME_SIZE register*/ + void (*set_pg_frame_size)(struct vpss_pg_frame_size); + /* check and clear interrupt if occured */ + int (*dma_complete_interrupt)(void); }; /* vpss configuration */ struct vpss_oper_config { __iomem void *vpss_regs_base0; __iomem void *vpss_regs_base1; + resource_size_t *vpss_regs_base2; enum vpss_platform_type platform; spinlock_t vpss_lock; struct vpss_hw_ops hw_ops; @@ -158,6 +181,14 @@ static void dm355_select_ccdc_source(enum vpss_ccdc_source_sel src_sel) bl_regw(src_sel << VPSS_HSSISEL_SHIFT, DM355_VPSSBL_CCDCMUX); } +int vpss_dma_complete_interrupt(void) +{ + if (!oper_cfg.hw_ops.dma_complete_interrupt) + return 2; + return oper_cfg.hw_ops.dma_complete_interrupt(); +} +EXPORT_SYMBOL(vpss_dma_complete_interrupt); + int vpss_select_ccdc_source(enum vpss_ccdc_source_sel src_sel) { if (!oper_cfg.hw_ops.select_ccdc_source) @@ -183,6 +214,15 @@ static int dm644x_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel) return 0; } +void vpss_set_sync_pol(struct vpss_sync_pol sync) +{ + if (!oper_cfg.hw_ops.set_sync_pol) + return; + + oper_cfg.hw_ops.set_sync_pol(sync); +} +EXPORT_SYMBOL(vpss_set_sync_pol); + int vpss_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel) { if (!oper_cfg.hw_ops.clear_wbl_overflow) @@ -348,6 +388,15 @@ void dm365_vpss_set_sync_pol(struct vpss_sync_pol sync) } EXPORT_SYMBOL(dm365_vpss_set_sync_pol); +void vpss_set_pg_frame_size(struct vpss_pg_frame_size frame_size) +{ + if (!oper_cfg.hw_ops.set_pg_frame_size) + return; + + oper_cfg.hw_ops.set_pg_frame_size(frame_size); +} +EXPORT_SYMBOL(vpss_set_pg_frame_size); + void dm365_vpss_set_pg_frame_size(struct vpss_pg_frame_size frame_size) { int current_reg = ((frame_size.hlpfr >> 1) - 1) << 16; @@ -426,6 +475,16 @@ static int __devinit vpss_probe(struct platform_device *pdev) oper_cfg.hw_ops.enable_clock = dm365_enable_clock; oper_cfg.hw_ops.select_ccdc_source = dm365_select_ccdc_source; /* Setup vpss interrupts */ + isp5_write((isp5_read(DM365_ISP5_PCCR) | + DM365_ISP5_PCCR_BL_CLK_ENABLE | + DM365_ISP5_PCCR_ISIF_CLK_ENABLE | + DM365_ISP5_PCCR_H3A_CLK_ENABLE | + DM365_ISP5_PCCR_RSZ_CLK_ENABLE | + DM365_ISP5_PCCR_IPIPE_CLK_ENABLE | + DM365_ISP5_PCCR_IPIPEIF_CLK_ENABLE | + DM365_ISP5_PCCR_RSV), DM365_ISP5_PCCR); + isp5_write((isp5_read(DM365_ISP5_BCR) | + DM365_ISP5_BCR_ISIF_OUT_ENABLE), DM365_ISP5_BCR); isp5_write(DM365_ISP5_INTSEL1_DEFAULT, DM365_ISP5_INTSEL1); isp5_write(DM365_ISP5_INTSEL2_DEFAULT, DM365_ISP5_INTSEL2); isp5_write(DM365_ISP5_INTSEL3_DEFAULT, DM365_ISP5_INTSEL3); @@ -471,11 +530,20 @@ static struct platform_driver vpss_driver = { static void vpss_exit(void) { + iounmap(oper_cfg.vpss_regs_base2); + release_mem_region(VPSS_CLK_CTRL, 4); platform_driver_unregister(&vpss_driver); } static int __init vpss_init(void) { + if (!request_mem_region(VPSS_CLK_CTRL, 4, "vpss_clock_control")) + return -EBUSY; + + oper_cfg.vpss_regs_base2 = ioremap(VPSS_CLK_CTRL, 4); + writel(VPSS_CLK_CTRL_VENCCLKEN | + VPSS_CLK_CTRL_DACCLKEN, oper_cfg.vpss_regs_base2); + return platform_driver_register(&vpss_driver); } subsys_initcall(vpss_init); diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index cc7b218d047c..ae885c7ebc41 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -185,6 +185,15 @@ static const struct gsc_fmt gsc_formats[] = { .corder = GSC_CRCB, .num_planes = 3, .num_comp = 3, + }, { + .name = "YUV 4:2:0 n.c. 2p, Y/CbCr tiled", + .pixelformat = V4L2_PIX_FMT_NV12MT_16X16, + .depth = { 8, 4 }, + .color = GSC_YUV420, + .yorder = GSC_LSB_Y, + .corder = GSC_CBCR, + .num_planes = 2, + .num_comp = 2, } }; @@ -935,8 +944,8 @@ static struct gsc_variant gsc_v_100_variant = { .pix_max = &gsc_v_100_max, .pix_min = &gsc_v_100_min, .pix_align = &gsc_v_100_align, - .in_buf_cnt = 8, - .out_buf_cnt = 16, + .in_buf_cnt = 32, + .out_buf_cnt = 32, .sc_up_max = 8, .sc_down_max = 16, .poly_sc_down_max = 4, @@ -993,12 +1002,8 @@ static void *gsc_get_drv_data(struct platform_device *pdev) static void gsc_clk_put(struct gsc_dev *gsc) { - if (IS_ERR_OR_NULL(gsc->clock)) - return; - - clk_unprepare(gsc->clock); - clk_put(gsc->clock); - gsc->clock = NULL; + if (!IS_ERR(gsc->clock)) + clk_unprepare(gsc->clock); } static int gsc_clk_get(struct gsc_dev *gsc) @@ -1007,27 +1012,22 @@ static int gsc_clk_get(struct gsc_dev *gsc) dev_dbg(&gsc->pdev->dev, "gsc_clk_get Called\n"); - gsc->clock = clk_get(&gsc->pdev->dev, GSC_CLOCK_GATE_NAME); - if (IS_ERR(gsc->clock)) - goto err_print; + gsc->clock = devm_clk_get(&gsc->pdev->dev, GSC_CLOCK_GATE_NAME); + if (IS_ERR(gsc->clock)) { + dev_err(&gsc->pdev->dev, "failed to get clock~~~: %s\n", + GSC_CLOCK_GATE_NAME); + return PTR_ERR(gsc->clock); + } ret = clk_prepare(gsc->clock); if (ret < 0) { - clk_put(gsc->clock); - gsc->clock = NULL; - goto err; + dev_err(&gsc->pdev->dev, "clock prepare failed for clock: %s\n", + GSC_CLOCK_GATE_NAME); + gsc->clock = ERR_PTR(-EINVAL); + return ret; } return 0; - -err: - dev_err(&gsc->pdev->dev, "clock prepare failed for clock: %s\n", - GSC_CLOCK_GATE_NAME); - gsc_clk_put(gsc); -err_print: - dev_err(&gsc->pdev->dev, "failed to get clock~~~: %s\n", - GSC_CLOCK_GATE_NAME); - return -ENXIO; } static int gsc_m2m_suspend(struct gsc_dev *gsc) @@ -1096,6 +1096,7 @@ static int gsc_probe(struct platform_device *pdev) init_waitqueue_head(&gsc->irq_queue); spin_lock_init(&gsc->slock); mutex_init(&gsc->lock); + gsc->clock = ERR_PTR(-EINVAL); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); gsc->regs = devm_request_and_ioremap(dev, res); @@ -1159,6 +1160,7 @@ static int __devexit gsc_remove(struct platform_device *pdev) vb2_dma_contig_cleanup_ctx(gsc->alloc_ctx); pm_runtime_disable(&pdev->dev); + gsc_clk_put(gsc); dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name); return 0; diff --git a/drivers/media/platform/exynos-gsc/gsc-core.h b/drivers/media/platform/exynos-gsc/gsc-core.h index 5f157efd24f0..cc19bba09bd1 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.h +++ b/drivers/media/platform/exynos-gsc/gsc-core.h @@ -427,6 +427,11 @@ static inline void gsc_ctx_state_lock_clear(u32 state, struct gsc_ctx *ctx) spin_unlock_irqrestore(&ctx->gsc_dev->slock, flags); } +static inline int is_tiled(const struct gsc_fmt *fmt) +{ + return fmt->pixelformat == V4L2_PIX_FMT_NV12MT_16X16; +} + static inline void gsc_hw_enable_control(struct gsc_dev *dev, bool on) { u32 cfg = readl(dev->regs + GSC_ENABLE); diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index c267c57c76fd..0d06d6c6f373 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -99,22 +99,28 @@ static void gsc_m2m_job_abort(void *priv) gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); } -static int gsc_fill_addr(struct gsc_ctx *ctx) +static int gsc_get_bufs(struct gsc_ctx *ctx) { struct gsc_frame *s_frame, *d_frame; - struct vb2_buffer *vb = NULL; + struct vb2_buffer *src_vb, *dst_vb; int ret; s_frame = &ctx->s_frame; d_frame = &ctx->d_frame; - vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); - ret = gsc_prepare_addr(ctx, vb, s_frame, &s_frame->addr); + src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); + ret = gsc_prepare_addr(ctx, src_vb, s_frame, &s_frame->addr); + if (ret) + return ret; + + dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + ret = gsc_prepare_addr(ctx, dst_vb, d_frame, &d_frame->addr); if (ret) return ret; - vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); - return gsc_prepare_addr(ctx, vb, d_frame, &d_frame->addr); + dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.timestamp; + + return 0; } static void gsc_m2m_device_run(void *priv) @@ -148,7 +154,7 @@ static void gsc_m2m_device_run(void *priv) goto put_device; } - ret = gsc_fill_addr(ctx); + ret = gsc_get_bufs(ctx); if (ret) { pr_err("Wrong address"); goto put_device; @@ -597,7 +603,7 @@ static int gsc_m2m_open(struct file *file) if (mutex_lock_interruptible(&gsc->lock)) return -ERESTARTSYS; - ctx = kzalloc(sizeof (*ctx), GFP_KERNEL); + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) { ret = -ENOMEM; goto unlock; diff --git a/drivers/media/platform/exynos-gsc/gsc-regs.c b/drivers/media/platform/exynos-gsc/gsc-regs.c index 0146b354dc22..6f5b5a486cf3 100644 --- a/drivers/media/platform/exynos-gsc/gsc-regs.c +++ b/drivers/media/platform/exynos-gsc/gsc-regs.c @@ -214,6 +214,9 @@ void gsc_hw_set_in_image_format(struct gsc_ctx *ctx) break; } + if (is_tiled(frame->fmt)) + cfg |= GSC_IN_TILE_C_16x8 | GSC_IN_TILE_MODE; + writel(cfg, dev->regs + GSC_IN_CON); } @@ -334,6 +337,9 @@ void gsc_hw_set_out_image_format(struct gsc_ctx *ctx) break; } + if (is_tiled(frame->fmt)) + cfg |= GSC_OUT_TILE_C_16x8 | GSC_OUT_TILE_MODE; + end_set: writel(cfg, dev->regs + GSC_OUT_CON); } diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c index a8ddb0cacab8..d464509d0f0e 100644 --- a/drivers/media/platform/fsl-viu.c +++ b/drivers/media/platform/fsl-viu.c @@ -1181,7 +1181,7 @@ static void viu_capture_intr(struct viu_dev *dev, u32 status) if (waitqueue_active(&buf->vb.done)) { list_del(&buf->vb.queue); - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; wake_up(&buf->vb.done); diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c index 05c560f2ef06..ed77a645e992 100644 --- a/drivers/media/platform/m2m-deinterlace.c +++ b/drivers/media/platform/m2m-deinterlace.c @@ -28,7 +28,7 @@ MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com"); MODULE_LICENSE("GPL"); MODULE_VERSION("0.0.1"); -static bool debug = true; +static bool debug; module_param(debug, bool, 0644); /* Flags that indicate a format can be used for capture/output */ diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index 35cc526e6c93..dade3ceab092 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -595,7 +595,7 @@ static void omap_vout_isr(void *arg, unsigned int irqstatus) return; spin_lock(&vout->vbq_lock); - do_gettimeofday(&timevalue); + v4l2_get_timestamp(&timevalue); switch (cur_display->type) { case OMAP_DISPLAY_TYPE_DSI: @@ -1230,21 +1230,6 @@ static int vidioc_s_fmt_vid_overlay(struct file *file, void *fh, return ret; } -static int vidioc_enum_fmt_vid_overlay(struct file *file, void *fh, - struct v4l2_fmtdesc *fmt) -{ - int index = fmt->index; - - if (index >= NUM_OUTPUT_FORMATS) - return -EINVAL; - - fmt->flags = omap_formats[index].flags; - strlcpy(fmt->description, omap_formats[index].description, - sizeof(fmt->description)); - fmt->pixelformat = omap_formats[index].pixelformat; - return 0; -} - static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f) { @@ -1858,10 +1843,9 @@ static const struct v4l2_ioctl_ops vout_ioctl_ops = { .vidioc_s_fbuf = vidioc_s_fbuf, .vidioc_g_fbuf = vidioc_g_fbuf, .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay, - .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay, - .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_overlay, - .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay, + .vidioc_try_fmt_vid_out_overlay = vidioc_try_fmt_vid_overlay, + .vidioc_s_fmt_vid_out_overlay = vidioc_s_fmt_vid_overlay, + .vidioc_g_fmt_vid_out_overlay = vidioc_g_fmt_vid_overlay, .vidioc_cropcap = vidioc_cropcap, .vidioc_g_crop = vidioc_g_crop, .vidioc_s_crop = vidioc_s_crop, diff --git a/drivers/media/platform/omap24xxcam.c b/drivers/media/platform/omap24xxcam.c index 70f45c381318..eda3274abf8e 100644 --- a/drivers/media/platform/omap24xxcam.c +++ b/drivers/media/platform/omap24xxcam.c @@ -402,7 +402,7 @@ static void omap24xxcam_vbq_complete(struct omap24xxcam_sgdma *sgdma, omap24xxcam_core_disable(cam); spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags); - do_gettimeofday(&vb->ts); + v4l2_get_timestamp(&vb->ts); vb->field_count = atomic_add_return(2, &fh->field_count); if (csr & csr_error) { vb->state = VIDEOBUF_ERROR; diff --git a/drivers/media/platform/omap3isp/ispqueue.c b/drivers/media/platform/omap3isp/ispqueue.c index 15bf3eab2224..6599963cdd9b 100644 --- a/drivers/media/platform/omap3isp/ispqueue.c +++ b/drivers/media/platform/omap3isp/ispqueue.c @@ -674,6 +674,7 @@ static int isp_video_queue_alloc(struct isp_video_queue *queue, buf->vbuf.index = i; buf->vbuf.length = size; buf->vbuf.type = queue->type; + buf->vbuf.flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; buf->vbuf.field = V4L2_FIELD_NONE; buf->vbuf.memory = memory; diff --git a/drivers/media/platform/s5p-fimc/fimc-capture.c b/drivers/media/platform/s5p-fimc/fimc-capture.c index fdb6740248a7..95e6a7820b5e 100644 --- a/drivers/media/platform/s5p-fimc/fimc-capture.c +++ b/drivers/media/platform/s5p-fimc/fimc-capture.c @@ -626,8 +626,8 @@ static struct fimc_fmt *fimc_capture_try_format(struct fimc_ctx *ctx, { bool rotation = ctx->rotation == 90 || ctx->rotation == 270; struct fimc_dev *fimc = ctx->fimc_dev; - struct fimc_variant *var = fimc->variant; - struct fimc_pix_limit *pl = var->pix_limit; + const struct fimc_variant *var = fimc->variant; + const struct fimc_pix_limit *pl = var->pix_limit; struct fimc_frame *dst = &ctx->d_frame; u32 depth, min_w, max_w, min_h, align_h = 3; u32 mask = FMT_FLAGS_CAM; @@ -699,8 +699,8 @@ static void fimc_capture_try_selection(struct fimc_ctx *ctx, { bool rotate = ctx->rotation == 90 || ctx->rotation == 270; struct fimc_dev *fimc = ctx->fimc_dev; - struct fimc_variant *var = fimc->variant; - struct fimc_pix_limit *pl = var->pix_limit; + const struct fimc_variant *var = fimc->variant; + const struct fimc_pix_limit *pl = var->pix_limit; struct fimc_frame *sink = &ctx->s_frame; u32 max_w, max_h, min_w = 0, min_h = 0, min_sz; u32 align_sz = 0, align_h = 4; @@ -793,6 +793,21 @@ static int fimc_cap_enum_fmt_mplane(struct file *file, void *priv, return 0; } +static struct media_entity *fimc_pipeline_get_head(struct media_entity *me) +{ + struct media_pad *pad = &me->pads[0]; + + while (!(pad->flags & MEDIA_PAD_FL_SOURCE)) { + pad = media_entity_remote_source(pad); + if (!pad) + break; + me = pad->entity; + pad = &me->pads[0]; + } + + return me; +} + /** * fimc_pipeline_try_format - negotiate and/or set formats at pipeline * elements @@ -808,19 +823,23 @@ static int fimc_pipeline_try_format(struct fimc_ctx *ctx, { struct fimc_dev *fimc = ctx->fimc_dev; struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR]; - struct v4l2_subdev *csis = fimc->pipeline.subdevs[IDX_CSIS]; struct v4l2_subdev_format sfmt; struct v4l2_mbus_framefmt *mf = &sfmt.format; - struct fimc_fmt *ffmt = NULL; - int ret, i = 0; + struct media_entity *me; + struct fimc_fmt *ffmt; + struct media_pad *pad; + int ret, i = 1; + u32 fcc; if (WARN_ON(!sd || !tfmt)) return -EINVAL; memset(&sfmt, 0, sizeof(sfmt)); sfmt.format = *tfmt; - sfmt.which = set ? V4L2_SUBDEV_FORMAT_ACTIVE : V4L2_SUBDEV_FORMAT_TRY; + + me = fimc_pipeline_get_head(&sd->entity); + while (1) { ffmt = fimc_find_format(NULL, mf->code != 0 ? &mf->code : NULL, FMT_FLAGS_CAM, i++); @@ -833,40 +852,52 @@ static int fimc_pipeline_try_format(struct fimc_ctx *ctx, } mf->code = tfmt->code = ffmt->mbus_code; - ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &sfmt); - if (ret) - return ret; - if (mf->code != tfmt->code) { - mf->code = 0; - continue; - } - if (mf->width != tfmt->width || mf->height != tfmt->height) { - u32 fcc = ffmt->fourcc; - tfmt->width = mf->width; - tfmt->height = mf->height; - ffmt = fimc_capture_try_format(ctx, - &tfmt->width, &tfmt->height, - NULL, &fcc, FIMC_SD_PAD_SOURCE); - if (ffmt && ffmt->mbus_code) - mf->code = ffmt->mbus_code; - if (mf->width != tfmt->width || - mf->height != tfmt->height) - continue; - tfmt->code = mf->code; + /* set format on all pipeline subdevs */ + while (me != &fimc->vid_cap.subdev.entity) { + sd = media_entity_to_v4l2_subdev(me); + + sfmt.pad = 0; + ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &sfmt); + if (ret) + return ret; + + if (me->pads[0].flags & MEDIA_PAD_FL_SINK) { + sfmt.pad = me->num_pads - 1; + mf->code = tfmt->code; + ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, + &sfmt); + if (ret) + return ret; + } + + pad = media_entity_remote_source(&me->pads[sfmt.pad]); + if (!pad) + return -EINVAL; + me = pad->entity; } - if (csis) - ret = v4l2_subdev_call(csis, pad, set_fmt, NULL, &sfmt); - if (mf->code == tfmt->code && - mf->width == tfmt->width && mf->height == tfmt->height) - break; + if (mf->code != tfmt->code) + continue; + + fcc = ffmt->fourcc; + tfmt->width = mf->width; + tfmt->height = mf->height; + ffmt = fimc_capture_try_format(ctx, &tfmt->width, &tfmt->height, + NULL, &fcc, FIMC_SD_PAD_SINK); + ffmt = fimc_capture_try_format(ctx, &tfmt->width, &tfmt->height, + NULL, &fcc, FIMC_SD_PAD_SOURCE); + if (ffmt && ffmt->mbus_code) + mf->code = ffmt->mbus_code; + if (mf->width != tfmt->width || mf->height != tfmt->height) + continue; + tfmt->code = mf->code; + break; } if (fmt_id && ffmt) *fmt_id = ffmt; *tfmt = *mf; - dbg("code: 0x%x, %dx%d, %p", mf->code, mf->width, mf->height, ffmt); return 0; } @@ -884,14 +915,16 @@ static int fimc_get_sensor_frame_desc(struct v4l2_subdev *sensor, { struct v4l2_mbus_frame_desc fd; int i, ret; + int pad; for (i = 0; i < num_planes; i++) fd.entry[i].length = plane_fmt[i].sizeimage; + pad = sensor->entity.num_pads - 1; if (try) - ret = v4l2_subdev_call(sensor, pad, set_frame_desc, 0, &fd); + ret = v4l2_subdev_call(sensor, pad, set_frame_desc, pad, &fd); else - ret = v4l2_subdev_call(sensor, pad, get_frame_desc, 0, &fd); + ret = v4l2_subdev_call(sensor, pad, get_frame_desc, pad, &fd); if (ret < 0) return ret; diff --git a/drivers/media/platform/s5p-fimc/fimc-core.c b/drivers/media/platform/s5p-fimc/fimc-core.c index 8d0d2b94a135..2a1558a37a41 100644 --- a/drivers/media/platform/s5p-fimc/fimc-core.c +++ b/drivers/media/platform/s5p-fimc/fimc-core.c @@ -241,7 +241,7 @@ static int fimc_get_scaler_factor(u32 src, u32 tar, u32 *ratio, u32 *shift) int fimc_set_scaler_info(struct fimc_ctx *ctx) { - struct fimc_variant *variant = ctx->fimc_dev->variant; + const struct fimc_variant *variant = ctx->fimc_dev->variant; struct device *dev = &ctx->fimc_dev->pdev->dev; struct fimc_scaler *sc = &ctx->scaler; struct fimc_frame *s_frame = &ctx->s_frame; @@ -440,7 +440,7 @@ void fimc_set_yuv_order(struct fimc_ctx *ctx) void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f) { - struct fimc_variant *variant = ctx->fimc_dev->variant; + const struct fimc_variant *variant = ctx->fimc_dev->variant; u32 i, depth = 0; for (i = 0; i < f->fmt->colplanes; i++) @@ -524,7 +524,7 @@ static int fimc_set_color_effect(struct fimc_ctx *ctx, enum v4l2_colorfx colorfx static int __fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_ctrl *ctrl) { struct fimc_dev *fimc = ctx->fimc_dev; - struct fimc_variant *variant = fimc->variant; + const struct fimc_variant *variant = fimc->variant; unsigned int flags = FIMC_DST_FMT | FIMC_SRC_FMT; int ret = 0; @@ -591,7 +591,7 @@ static const struct v4l2_ctrl_ops fimc_ctrl_ops = { int fimc_ctrls_create(struct fimc_ctx *ctx) { - struct fimc_variant *variant = ctx->fimc_dev->variant; + const struct fimc_variant *variant = ctx->fimc_dev->variant; unsigned int max_alpha = fimc_get_alpha_mask(ctx->d_frame.fmt); struct fimc_ctrls *ctrls = &ctx->ctrls; struct v4l2_ctrl_handler *handler = &ctrls->handler; @@ -881,7 +881,7 @@ static int fimc_m2m_resume(struct fimc_dev *fimc) static int fimc_probe(struct platform_device *pdev) { - struct fimc_drvdata *drv_data = fimc_get_drvdata(pdev); + const struct fimc_drvdata *drv_data = fimc_get_drvdata(pdev); struct s5p_platform_fimc *pdata; struct fimc_dev *fimc; struct resource *res; @@ -1053,7 +1053,7 @@ static int __devexit fimc_remove(struct platform_device *pdev) } /* Image pixel limits, similar across several FIMC HW revisions. */ -static struct fimc_pix_limit s5p_pix_limit[4] = { +static const struct fimc_pix_limit s5p_pix_limit[4] = { [0] = { .scaler_en_w = 3264, .scaler_dis_w = 8192, @@ -1088,7 +1088,7 @@ static struct fimc_pix_limit s5p_pix_limit[4] = { }, }; -static struct fimc_variant fimc0_variant_s5p = { +static const struct fimc_variant fimc0_variant_s5p = { .has_inp_rot = 1, .has_out_rot = 1, .has_cam_if = 1, @@ -1100,7 +1100,7 @@ static struct fimc_variant fimc0_variant_s5p = { .pix_limit = &s5p_pix_limit[0], }; -static struct fimc_variant fimc2_variant_s5p = { +static const struct fimc_variant fimc2_variant_s5p = { .has_cam_if = 1, .min_inp_pixsize = 16, .min_out_pixsize = 16, @@ -1110,7 +1110,7 @@ static struct fimc_variant fimc2_variant_s5p = { .pix_limit = &s5p_pix_limit[1], }; -static struct fimc_variant fimc0_variant_s5pv210 = { +static const struct fimc_variant fimc0_variant_s5pv210 = { .pix_hoff = 1, .has_inp_rot = 1, .has_out_rot = 1, @@ -1123,7 +1123,7 @@ static struct fimc_variant fimc0_variant_s5pv210 = { .pix_limit = &s5p_pix_limit[1], }; -static struct fimc_variant fimc1_variant_s5pv210 = { +static const struct fimc_variant fimc1_variant_s5pv210 = { .pix_hoff = 1, .has_inp_rot = 1, .has_out_rot = 1, @@ -1137,7 +1137,7 @@ static struct fimc_variant fimc1_variant_s5pv210 = { .pix_limit = &s5p_pix_limit[2], }; -static struct fimc_variant fimc2_variant_s5pv210 = { +static const struct fimc_variant fimc2_variant_s5pv210 = { .has_cam_if = 1, .pix_hoff = 1, .min_inp_pixsize = 16, @@ -1148,7 +1148,7 @@ static struct fimc_variant fimc2_variant_s5pv210 = { .pix_limit = &s5p_pix_limit[2], }; -static struct fimc_variant fimc0_variant_exynos4 = { +static const struct fimc_variant fimc0_variant_exynos4210 = { .pix_hoff = 1, .has_inp_rot = 1, .has_out_rot = 1, @@ -1164,9 +1164,8 @@ static struct fimc_variant fimc0_variant_exynos4 = { .pix_limit = &s5p_pix_limit[1], }; -static struct fimc_variant fimc3_variant_exynos4 = { +static const struct fimc_variant fimc3_variant_exynos4210 = { .pix_hoff = 1, - .has_cam_if = 1, .has_cistatus2 = 1, .has_mainscaler_ext = 1, .has_alpha = 1, @@ -1178,8 +1177,38 @@ static struct fimc_variant fimc3_variant_exynos4 = { .pix_limit = &s5p_pix_limit[3], }; +static const struct fimc_variant fimc0_variant_exynos4x12 = { + .pix_hoff = 1, + .has_inp_rot = 1, + .has_out_rot = 1, + .has_cam_if = 1, + .has_isp_wb = 1, + .has_cistatus2 = 1, + .has_mainscaler_ext = 1, + .has_alpha = 1, + .min_inp_pixsize = 16, + .min_out_pixsize = 16, + .hor_offs_align = 2, + .min_vsize_align = 1, + .out_buf_count = 32, + .pix_limit = &s5p_pix_limit[1], +}; + +static const struct fimc_variant fimc3_variant_exynos4x12 = { + .pix_hoff = 1, + .has_cistatus2 = 1, + .has_mainscaler_ext = 1, + .has_alpha = 1, + .min_inp_pixsize = 16, + .min_out_pixsize = 16, + .hor_offs_align = 2, + .min_vsize_align = 1, + .out_buf_count = 32, + .pix_limit = &s5p_pix_limit[3], +}; + /* S5PC100 */ -static struct fimc_drvdata fimc_drvdata_s5p = { +static const struct fimc_drvdata fimc_drvdata_s5p = { .variant = { [0] = &fimc0_variant_s5p, [1] = &fimc0_variant_s5p, @@ -1190,7 +1219,7 @@ static struct fimc_drvdata fimc_drvdata_s5p = { }; /* S5PV210, S5PC110 */ -static struct fimc_drvdata fimc_drvdata_s5pv210 = { +static const struct fimc_drvdata fimc_drvdata_s5pv210 = { .variant = { [0] = &fimc0_variant_s5pv210, [1] = &fimc1_variant_s5pv210, @@ -1201,18 +1230,30 @@ static struct fimc_drvdata fimc_drvdata_s5pv210 = { }; /* EXYNOS4210, S5PV310, S5PC210 */ -static struct fimc_drvdata fimc_drvdata_exynos4 = { +static const struct fimc_drvdata fimc_drvdata_exynos4210 = { + .variant = { + [0] = &fimc0_variant_exynos4210, + [1] = &fimc0_variant_exynos4210, + [2] = &fimc0_variant_exynos4210, + [3] = &fimc3_variant_exynos4210, + }, + .num_entities = 4, + .lclk_frequency = 166000000UL, +}; + +/* EXYNOS4212, EXYNOS4412 */ +static const struct fimc_drvdata fimc_drvdata_exynos4x12 = { .variant = { - [0] = &fimc0_variant_exynos4, - [1] = &fimc0_variant_exynos4, - [2] = &fimc0_variant_exynos4, - [3] = &fimc3_variant_exynos4, + [0] = &fimc0_variant_exynos4x12, + [1] = &fimc0_variant_exynos4x12, + [2] = &fimc0_variant_exynos4x12, + [3] = &fimc3_variant_exynos4x12, }, .num_entities = 4, .lclk_frequency = 166000000UL, }; -static struct platform_device_id fimc_driver_ids[] = { +static const struct platform_device_id fimc_driver_ids[] = { { .name = "s5p-fimc", .driver_data = (unsigned long)&fimc_drvdata_s5p, @@ -1221,7 +1262,10 @@ static struct platform_device_id fimc_driver_ids[] = { .driver_data = (unsigned long)&fimc_drvdata_s5pv210, }, { .name = "exynos4-fimc", - .driver_data = (unsigned long)&fimc_drvdata_exynos4, + .driver_data = (unsigned long)&fimc_drvdata_exynos4210, + }, { + .name = "exynos4x12-fimc", + .driver_data = (unsigned long)&fimc_drvdata_exynos4x12, }, {}, }; diff --git a/drivers/media/platform/s5p-fimc/fimc-core.h b/drivers/media/platform/s5p-fimc/fimc-core.h index c0040d792499..424ff960f0d9 100644 --- a/drivers/media/platform/s5p-fimc/fimc-core.h +++ b/drivers/media/platform/s5p-fimc/fimc-core.h @@ -372,6 +372,7 @@ struct fimc_pix_limit { * @has_mainscaler_ext: 1 if extended mainscaler ratios in CIEXTEN register * are present in this IP revision * @has_cam_if: set if this instance has a camera input interface + * @has_isp_wb: set if this instance has ISP writeback input * @pix_limit: pixel size constraints for the scaler * @min_inp_pixsize: minimum input pixel size * @min_out_pixsize: minimum output pixel size @@ -386,8 +387,9 @@ struct fimc_variant { unsigned int has_cistatus2:1; unsigned int has_mainscaler_ext:1; unsigned int has_cam_if:1; + unsigned int has_isp_wb:1; unsigned int has_alpha:1; - struct fimc_pix_limit *pix_limit; + const struct fimc_pix_limit *pix_limit; u16 min_inp_pixsize; u16 min_out_pixsize; u16 hor_offs_align; @@ -402,7 +404,7 @@ struct fimc_variant { * @lclk_frequency: local bus clock frequency */ struct fimc_drvdata { - struct fimc_variant *variant[FIMC_MAX_DEVS]; + const struct fimc_variant *variant[FIMC_MAX_DEVS]; int num_entities; unsigned long lclk_frequency; }; @@ -435,7 +437,7 @@ struct fimc_dev { struct mutex lock; struct platform_device *pdev; struct s5p_platform_fimc *pdata; - struct fimc_variant *variant; + const struct fimc_variant *variant; u16 id; struct clk *clock[MAX_FIMC_CLOCKS]; void __iomem *regs; diff --git a/drivers/media/platform/s5p-fimc/fimc-lite-reg.c b/drivers/media/platform/s5p-fimc/fimc-lite-reg.c index a22d7eb05c82..ad63ebf082c5 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite-reg.c +++ b/drivers/media/platform/s5p-fimc/fimc-lite-reg.c @@ -292,9 +292,11 @@ void flite_hw_dump_regs(struct fimc_lite *dev, const char *label) }; u32 i; - pr_info("--- %s ---\n", label); + v4l2_info(&dev->subdev, "--- %s ---\n", label); + for (i = 0; i < ARRAY_SIZE(registers); i++) { u32 cfg = readl(dev->regs + registers[i].offset); - pr_info("%s: %s:\t0x%08x\n", __func__, registers[i].name, cfg); + v4l2_info(&dev->subdev, "%9s: 0x%08x\n", + registers[i].name, cfg); } } diff --git a/drivers/media/platform/s5p-fimc/fimc-lite.c b/drivers/media/platform/s5p-fimc/fimc-lite.c index 1b309a72f09f..765b8e4cbf4e 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite.c +++ b/drivers/media/platform/s5p-fimc/fimc-lite.c @@ -120,25 +120,29 @@ static const struct fimc_fmt *fimc_lite_find_format(const u32 *pixelformat, return def_fmt; } -static int fimc_lite_hw_init(struct fimc_lite *fimc) +static int fimc_lite_hw_init(struct fimc_lite *fimc, bool isp_output) { struct fimc_pipeline *pipeline = &fimc->pipeline; - struct fimc_sensor_info *sensor; + struct v4l2_subdev *sensor; + struct fimc_sensor_info *si; unsigned long flags; - if (pipeline->subdevs[IDX_SENSOR] == NULL) + sensor = isp_output ? fimc->sensor : pipeline->subdevs[IDX_SENSOR]; + + if (sensor == NULL) return -ENXIO; if (fimc->fmt == NULL) return -EINVAL; - sensor = v4l2_get_subdev_hostdata(pipeline->subdevs[IDX_SENSOR]); + /* Get sensor configuration data from the sensor subdev */ + si = v4l2_get_subdev_hostdata(sensor); spin_lock_irqsave(&fimc->slock, flags); - flite_hw_set_camera_bus(fimc, &sensor->pdata); + flite_hw_set_camera_bus(fimc, &si->pdata); flite_hw_set_source_format(fimc, &fimc->inp_frame); flite_hw_set_window_offset(fimc, &fimc->inp_frame); - flite_hw_set_output_dma(fimc, &fimc->out_frame, true); + flite_hw_set_output_dma(fimc, &fimc->out_frame, !isp_output); flite_hw_set_interrupt_mask(fimc); flite_hw_set_test_pattern(fimc, fimc->test_pattern->val); @@ -296,7 +300,7 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) fimc->frame_count = 0; - ret = fimc_lite_hw_init(fimc); + ret = fimc_lite_hw_init(fimc, false); if (ret) { fimc_lite_reinit(fimc, false); return ret; @@ -460,6 +464,11 @@ static int fimc_lite_open(struct file *file) if (mutex_lock_interruptible(&fimc->lock)) return -ERESTARTSYS; + if (fimc->out_path != FIMC_IO_DMA) { + ret = -EBUSY; + goto done; + } + set_bit(ST_FLITE_IN_USE, &fimc->state); ret = pm_runtime_get_sync(&fimc->pdev->dev); if (ret < 0) @@ -962,6 +971,29 @@ static const struct v4l2_ioctl_ops fimc_lite_ioctl_ops = { .vidioc_streamoff = fimc_lite_streamoff, }; +/* Called with the media graph mutex held */ +static struct v4l2_subdev *__find_remote_sensor(struct media_entity *me) +{ + struct media_pad *pad = &me->pads[0]; + struct v4l2_subdev *sd; + + while (pad->flags & MEDIA_PAD_FL_SINK) { + /* source pad */ + pad = media_entity_remote_source(pad); + if (pad == NULL || + media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + break; + + sd = media_entity_to_v4l2_subdev(pad->entity); + + if (sd->grp_id == GRP_ID_FIMC_IS_SENSOR) + return sd; + /* sink pad */ + pad = &sd->entity.pads[0]; + } + return NULL; +} + /* Capture subdev media entity operations */ static int fimc_lite_link_setup(struct media_entity *entity, const struct media_pad *local, @@ -970,46 +1002,59 @@ static int fimc_lite_link_setup(struct media_entity *entity, struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct fimc_lite *fimc = v4l2_get_subdevdata(sd); unsigned int remote_ent_type = media_entity_type(remote->entity); + int ret = 0; if (WARN_ON(fimc == NULL)) return 0; v4l2_dbg(1, debug, sd, "%s: %s --> %s, flags: 0x%x. source_id: 0x%x", - __func__, local->entity->name, remote->entity->name, + __func__, remote->entity->name, local->entity->name, flags, fimc->source_subdev_grp_id); - switch (local->index) { - case FIMC_SD_PAD_SINK: - if (remote_ent_type != MEDIA_ENT_T_V4L2_SUBDEV) - return -EINVAL; + mutex_lock(&fimc->lock); + switch (local->index) { + case FLITE_SD_PAD_SINK: + if (remote_ent_type != MEDIA_ENT_T_V4L2_SUBDEV) { + ret = -EINVAL; + break; + } if (flags & MEDIA_LNK_FL_ENABLED) { - if (fimc->source_subdev_grp_id != 0) - return -EBUSY; - fimc->source_subdev_grp_id = sd->grp_id; - return 0; + if (fimc->source_subdev_grp_id == 0) + fimc->source_subdev_grp_id = sd->grp_id; + else + ret = -EBUSY; + } else { + fimc->source_subdev_grp_id = 0; + fimc->sensor = NULL; } + break; - fimc->source_subdev_grp_id = 0; + case FLITE_SD_PAD_SOURCE_DMA: + if (!(flags & MEDIA_LNK_FL_ENABLED)) + fimc->out_path = FIMC_IO_NONE; + else if (remote_ent_type == MEDIA_ENT_T_DEVNODE) + fimc->out_path = FIMC_IO_DMA; + else + ret = -EINVAL; break; - case FIMC_SD_PAD_SOURCE: - if (!(flags & MEDIA_LNK_FL_ENABLED)) { + case FLITE_SD_PAD_SOURCE_ISP: + if (!(flags & MEDIA_LNK_FL_ENABLED)) fimc->out_path = FIMC_IO_NONE; - return 0; - } - if (remote_ent_type == MEDIA_ENT_T_V4L2_SUBDEV) + else if (remote_ent_type == MEDIA_ENT_T_V4L2_SUBDEV) fimc->out_path = FIMC_IO_ISP; else - fimc->out_path = FIMC_IO_DMA; + ret = -EINVAL; break; default: v4l2_err(sd, "Invalid pad index\n"); - return -EINVAL; + ret = -EINVAL; } - return 0; + mutex_unlock(&fimc->lock); + return ret; } static const struct media_entity_operations fimc_lite_subdev_media_ops = { @@ -1188,13 +1233,49 @@ static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd, static int fimc_lite_subdev_s_stream(struct v4l2_subdev *sd, int on) { struct fimc_lite *fimc = v4l2_get_subdevdata(sd); + unsigned long flags; + int ret; - if (fimc->out_path == FIMC_IO_DMA) + /* + * Find sensor subdev linked to FIMC-LITE directly or through + * MIPI-CSIS. This is required for configuration where FIMC-LITE + * is used as a subdev only and feeds data internally to FIMC-IS. + * The pipeline links are protected through entity.stream_count + * so there is no need to take the media graph mutex here. + */ + fimc->sensor = __find_remote_sensor(&sd->entity); + + mutex_lock(&fimc->lock); + if (fimc->out_path != FIMC_IO_ISP) { + mutex_unlock(&fimc->lock); return -ENOIOCTLCMD; + } - /* TODO: */ + if (on) { + flite_hw_reset(fimc); + ret = fimc_lite_hw_init(fimc, true); + if (!ret) { + spin_lock_irqsave(&fimc->slock, flags); + flite_hw_capture_start(fimc); + spin_unlock_irqrestore(&fimc->slock, flags); + } + } else { + set_bit(ST_FLITE_OFF, &fimc->state); - return 0; + spin_lock_irqsave(&fimc->slock, flags); + flite_hw_capture_stop(fimc); + spin_unlock_irqrestore(&fimc->slock, flags); + + ret = wait_event_timeout(fimc->irq_queue, + !test_bit(ST_FLITE_OFF, &fimc->state), + msecs_to_jiffies(200)); + if (ret == 0) + v4l2_err(sd, "s_stream(0) timeout\n"); + clear_bit(ST_FLITE_RUN, &fimc->state); + } + + mutex_unlock(&fimc->lock); + return ret; } static int fimc_lite_subdev_s_power(struct v4l2_subdev *sd, int on) @@ -1347,9 +1428,10 @@ static int fimc_lite_create_capture_subdev(struct fimc_lite *fimc) sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; snprintf(sd->name, sizeof(sd->name), "FIMC-LITE.%d", fimc->index); - fimc->subdev_pads[FIMC_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; - fimc->subdev_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&sd->entity, FIMC_SD_PADS_NUM, + fimc->subdev_pads[FLITE_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + fimc->subdev_pads[FLITE_SD_PAD_SOURCE_DMA].flags = MEDIA_PAD_FL_SOURCE; + fimc->subdev_pads[FLITE_SD_PAD_SOURCE_ISP].flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_init(&sd->entity, FLITE_SD_PADS_NUM, fimc->subdev_pads, 0); if (ret) return ret; @@ -1518,7 +1600,7 @@ static int fimc_lite_resume(struct device *dev) INIT_LIST_HEAD(&fimc->active_buf_q); fimc_pipeline_call(fimc, open, &fimc->pipeline, &fimc->vfd.entity, false); - fimc_lite_hw_init(fimc); + fimc_lite_hw_init(fimc, fimc->out_path == FIMC_IO_ISP); clear_bit(ST_FLITE_SUSPENDED, &fimc->state); for (i = 0; i < fimc->reqbufs_count; i++) { diff --git a/drivers/media/platform/s5p-fimc/fimc-lite.h b/drivers/media/platform/s5p-fimc/fimc-lite.h index 3081db35c5b0..4576922952f3 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite.h +++ b/drivers/media/platform/s5p-fimc/fimc-lite.h @@ -45,8 +45,9 @@ enum { }; #define FLITE_SD_PAD_SINK 0 -#define FLITE_SD_PAD_SOURCE 1 -#define FLITE_SD_PADS_NUM 2 +#define FLITE_SD_PAD_SOURCE_DMA 1 +#define FLITE_SD_PAD_SOURCE_ISP 2 +#define FLITE_SD_PADS_NUM 3 struct flite_variant { unsigned short max_width; @@ -104,6 +105,7 @@ struct flite_buffer { * @subdev: FIMC-LITE subdev * @vd_pad: media (sink) pad for the capture video node * @subdev_pads: the subdev media pads + * @sensor: sensor subdev attached to FIMC-LITE directly or through MIPI-CSIS * @ctrl_handler: v4l2 control handler * @test_pattern: test pattern controls * @index: FIMC-LITE platform device index @@ -139,6 +141,7 @@ struct fimc_lite { struct v4l2_subdev subdev; struct media_pad vd_pad; struct media_pad subdev_pads[FLITE_SD_PADS_NUM]; + struct v4l2_subdev *sensor; struct v4l2_ctrl_handler ctrl_handler; struct v4l2_ctrl *test_pattern; u32 index; diff --git a/drivers/media/platform/s5p-fimc/fimc-m2m.c b/drivers/media/platform/s5p-fimc/fimc-m2m.c index 1d21da4bd24b..1d57f3b87aa3 100644 --- a/drivers/media/platform/s5p-fimc/fimc-m2m.c +++ b/drivers/media/platform/s5p-fimc/fimc-m2m.c @@ -300,7 +300,7 @@ static int fimc_m2m_g_fmt_mplane(struct file *file, void *fh, static int fimc_try_fmt_mplane(struct fimc_ctx *ctx, struct v4l2_format *f) { struct fimc_dev *fimc = ctx->fimc_dev; - struct fimc_variant *variant = fimc->variant; + const struct fimc_variant *variant = fimc->variant; struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; struct fimc_fmt *fmt; u32 max_w, mod_x, mod_y; diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c index 1bd5678cfeb9..8b43f982c12d 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c @@ -62,16 +62,17 @@ static void fimc_pipeline_prepare(struct fimc_pipeline *p, sd = media_entity_to_v4l2_subdev(pad->entity); switch (sd->grp_id) { - case SENSOR_GROUP_ID: + case GRP_ID_FIMC_IS_SENSOR: + case GRP_ID_SENSOR: p->subdevs[IDX_SENSOR] = sd; break; - case CSIS_GROUP_ID: + case GRP_ID_CSIS: p->subdevs[IDX_CSIS] = sd; break; - case FLITE_GROUP_ID: + case GRP_ID_FLITE: p->subdevs[IDX_FLITE] = sd; break; - case FIMC_GROUP_ID: + case GRP_ID_FIMC: /* No need to control FIMC subdev through subdev ops */ break; default: @@ -269,7 +270,7 @@ static struct v4l2_subdev *fimc_md_register_sensor(struct fimc_md *fmd, return ERR_PTR(-EPROBE_DEFER); } v4l2_set_subdev_hostdata(sd, s_info); - sd->grp_id = SENSOR_GROUP_ID; + sd->grp_id = GRP_ID_SENSOR; v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice %s\n", s_info->pdata.board_info->type); @@ -351,7 +352,7 @@ static int fimc_register_callback(struct device *dev, void *p) return 0; sd = &fimc->vid_cap.subdev; - sd->grp_id = FIMC_GROUP_ID; + sd->grp_id = GRP_ID_FIMC; v4l2_set_subdev_hostdata(sd, (void *)&fimc_pipeline_ops); ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); @@ -374,7 +375,7 @@ static int fimc_lite_register_callback(struct device *dev, void *p) if (fimc == NULL || fimc->index >= FIMC_LITE_MAX_DEVS) return 0; - fimc->subdev.grp_id = FLITE_GROUP_ID; + fimc->subdev.grp_id = GRP_ID_FLITE; v4l2_set_subdev_hostdata(&fimc->subdev, (void *)&fimc_pipeline_ops); ret = v4l2_device_register_subdev(&fmd->v4l2_dev, &fimc->subdev); @@ -404,7 +405,7 @@ static int csis_register_callback(struct device *dev, void *p) v4l2_info(sd, "csis%d sd: %s\n", pdev->id, sd->name); id = pdev->id < 0 ? 0 : pdev->id; - sd->grp_id = CSIS_GROUP_ID; + sd->grp_id = GRP_ID_CSIS; ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); if (!ret) @@ -602,7 +603,7 @@ static int __fimc_md_create_flite_source_links(struct fimc_md *fmd) source = &fimc->subdev.entity; sink = &fimc->vfd.entity; /* FIMC-LITE's subdev and video node */ - ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE, + ret = media_entity_create_link(source, FLITE_SD_PAD_SOURCE_DMA, sink, 0, flags); if (ret) break; @@ -658,7 +659,8 @@ static int fimc_md_create_links(struct fimc_md *fmd) "but s5p-csis module is not loaded!\n")) return -EINVAL; - ret = media_entity_create_link(&sensor->entity, 0, + pad = sensor->entity.num_pads - 1; + ret = media_entity_create_link(&sensor->entity, pad, &csis->entity, CSIS_PAD_SINK, MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); @@ -828,11 +830,11 @@ static int fimc_md_link_notify(struct media_pad *source, sd = media_entity_to_v4l2_subdev(sink->entity); switch (sd->grp_id) { - case FLITE_GROUP_ID: + case GRP_ID_FLITE: fimc_lite = v4l2_get_subdevdata(sd); pipeline = &fimc_lite->pipeline; break; - case FIMC_GROUP_ID: + case GRP_ID_FIMC: fimc = v4l2_get_subdevdata(sd); pipeline = &fimc->pipeline; break; diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.h b/drivers/media/platform/s5p-fimc/fimc-mdevice.h index 2d8d41d82620..da7d9922cf58 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.h +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.h @@ -22,11 +22,13 @@ #include "mipi-csis.h" /* Group IDs of sensor, MIPI-CSIS, FIMC-LITE and the writeback subdevs. */ -#define SENSOR_GROUP_ID (1 << 8) -#define CSIS_GROUP_ID (1 << 9) -#define WRITEBACK_GROUP_ID (1 << 10) -#define FIMC_GROUP_ID (1 << 11) -#define FLITE_GROUP_ID (1 << 12) +#define GRP_ID_SENSOR (1 << 8) +#define GRP_ID_FIMC_IS_SENSOR (1 << 9) +#define GRP_ID_WRITEBACK (1 << 10) +#define GRP_ID_CSIS (1 << 11) +#define GRP_ID_FIMC (1 << 12) +#define GRP_ID_FLITE (1 << 13) +#define GRP_ID_FIMC_IS (1 << 14) #define FIMC_MAX_SENSORS 8 #define FIMC_MAX_CAMCLKS 2 diff --git a/drivers/media/platform/s5p-fimc/fimc-reg.c b/drivers/media/platform/s5p-fimc/fimc-reg.c index 2c9d0c06c9e8..c05d0444192f 100644 --- a/drivers/media/platform/s5p-fimc/fimc-reg.c +++ b/drivers/media/platform/s5p-fimc/fimc-reg.c @@ -44,9 +44,9 @@ static u32 fimc_hw_get_in_flip(struct fimc_ctx *ctx) u32 flip = FIMC_REG_MSCTRL_FLIP_NORMAL; if (ctx->hflip) - flip = FIMC_REG_MSCTRL_FLIP_X_MIRROR; - if (ctx->vflip) flip = FIMC_REG_MSCTRL_FLIP_Y_MIRROR; + if (ctx->vflip) + flip = FIMC_REG_MSCTRL_FLIP_X_MIRROR; if (ctx->rotation <= 90) return flip; @@ -59,9 +59,9 @@ static u32 fimc_hw_get_target_flip(struct fimc_ctx *ctx) u32 flip = FIMC_REG_CITRGFMT_FLIP_NORMAL; if (ctx->hflip) - flip |= FIMC_REG_CITRGFMT_FLIP_X_MIRROR; - if (ctx->vflip) flip |= FIMC_REG_CITRGFMT_FLIP_Y_MIRROR; + if (ctx->vflip) + flip |= FIMC_REG_CITRGFMT_FLIP_X_MIRROR; if (ctx->rotation <= 90) return flip; @@ -312,7 +312,7 @@ static void fimc_hw_set_scaler(struct fimc_ctx *ctx) void fimc_hw_set_mainscaler(struct fimc_ctx *ctx) { struct fimc_dev *dev = ctx->fimc_dev; - struct fimc_variant *variant = dev->variant; + const struct fimc_variant *variant = dev->variant; struct fimc_scaler *sc = &ctx->scaler; u32 cfg; @@ -344,30 +344,31 @@ void fimc_hw_set_mainscaler(struct fimc_ctx *ctx) } } -void fimc_hw_en_capture(struct fimc_ctx *ctx) +void fimc_hw_enable_capture(struct fimc_ctx *ctx) { struct fimc_dev *dev = ctx->fimc_dev; + u32 cfg; - u32 cfg = readl(dev->regs + FIMC_REG_CIIMGCPT); - - if (ctx->out_path == FIMC_IO_DMA) { - /* one shot mode */ - cfg |= FIMC_REG_CIIMGCPT_CPT_FREN_ENABLE | - FIMC_REG_CIIMGCPT_IMGCPTEN; - } else { - /* Continuous frame capture mode (freerun). */ - cfg &= ~(FIMC_REG_CIIMGCPT_CPT_FREN_ENABLE | - FIMC_REG_CIIMGCPT_CPT_FRMOD_CNT); - cfg |= FIMC_REG_CIIMGCPT_IMGCPTEN; - } + cfg = readl(dev->regs + FIMC_REG_CIIMGCPT); + cfg |= FIMC_REG_CIIMGCPT_CPT_FREN_ENABLE; if (ctx->scaler.enabled) cfg |= FIMC_REG_CIIMGCPT_IMGCPTEN_SC; + else + cfg &= FIMC_REG_CIIMGCPT_IMGCPTEN_SC; cfg |= FIMC_REG_CIIMGCPT_IMGCPTEN; writel(cfg, dev->regs + FIMC_REG_CIIMGCPT); } +void fimc_hw_disable_capture(struct fimc_dev *dev) +{ + u32 cfg = readl(dev->regs + FIMC_REG_CIIMGCPT); + cfg &= ~(FIMC_REG_CIIMGCPT_IMGCPTEN | + FIMC_REG_CIIMGCPT_IMGCPTEN_SC); + writel(cfg, dev->regs + FIMC_REG_CIIMGCPT); +} + void fimc_hw_set_effect(struct fimc_ctx *ctx) { struct fimc_dev *dev = ctx->fimc_dev; @@ -737,13 +738,6 @@ void fimc_hw_activate_input_dma(struct fimc_dev *dev, bool on) writel(cfg, dev->regs + FIMC_REG_MSCTRL); } -void fimc_hw_dis_capture(struct fimc_dev *dev) -{ - u32 cfg = readl(dev->regs + FIMC_REG_CIIMGCPT); - cfg &= ~(FIMC_REG_CIIMGCPT_IMGCPTEN | FIMC_REG_CIIMGCPT_IMGCPTEN_SC); - writel(cfg, dev->regs + FIMC_REG_CIIMGCPT); -} - /* Return an index to the buffer actually being written. */ s32 fimc_hw_get_frame_index(struct fimc_dev *dev) { @@ -776,13 +770,13 @@ s32 fimc_hw_get_prev_frame_index(struct fimc_dev *dev) void fimc_activate_capture(struct fimc_ctx *ctx) { fimc_hw_enable_scaler(ctx->fimc_dev, ctx->scaler.enabled); - fimc_hw_en_capture(ctx); + fimc_hw_enable_capture(ctx); } void fimc_deactivate_capture(struct fimc_dev *fimc) { fimc_hw_en_lastirq(fimc, true); - fimc_hw_dis_capture(fimc); + fimc_hw_disable_capture(fimc); fimc_hw_enable_scaler(fimc, false); fimc_hw_en_lastirq(fimc, false); } diff --git a/drivers/media/platform/s5p-fimc/fimc-reg.h b/drivers/media/platform/s5p-fimc/fimc-reg.h index b6abfc7b72ac..f3e0b78a3736 100644 --- a/drivers/media/platform/s5p-fimc/fimc-reg.h +++ b/drivers/media/platform/s5p-fimc/fimc-reg.h @@ -287,7 +287,7 @@ void fimc_hw_en_lastirq(struct fimc_dev *fimc, int enable); void fimc_hw_en_irq(struct fimc_dev *fimc, int enable); void fimc_hw_set_prescaler(struct fimc_ctx *ctx); void fimc_hw_set_mainscaler(struct fimc_ctx *ctx); -void fimc_hw_en_capture(struct fimc_ctx *ctx); +void fimc_hw_enable_capture(struct fimc_ctx *ctx); void fimc_hw_set_effect(struct fimc_ctx *ctx); void fimc_hw_set_rgb_alpha(struct fimc_ctx *ctx); void fimc_hw_set_in_dma(struct fimc_ctx *ctx); @@ -306,7 +306,7 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, void fimc_hw_clear_irq(struct fimc_dev *dev); void fimc_hw_enable_scaler(struct fimc_dev *dev, bool on); void fimc_hw_activate_input_dma(struct fimc_dev *dev, bool on); -void fimc_hw_dis_capture(struct fimc_dev *dev); +void fimc_hw_disable_capture(struct fimc_dev *dev); s32 fimc_hw_get_frame_index(struct fimc_dev *dev); s32 fimc_hw_get_prev_frame_index(struct fimc_dev *dev); void fimc_activate_capture(struct fimc_ctx *ctx); diff --git a/drivers/media/platform/s5p-fimc/mipi-csis.c b/drivers/media/platform/s5p-fimc/mipi-csis.c index 4c961b1b68e6..8a06f1402f37 100644 --- a/drivers/media/platform/s5p-fimc/mipi-csis.c +++ b/drivers/media/platform/s5p-fimc/mipi-csis.c @@ -220,6 +220,18 @@ static const struct csis_pix_format s5pcsis_formats[] = { .code = V4L2_MBUS_FMT_S5C_UYVY_JPEG_1X8, .fmt_reg = S5PCSIS_CFG_FMT_USER(1), .data_alignment = 32, + }, { + .code = V4L2_MBUS_FMT_SGRBG8_1X8, + .fmt_reg = S5PCSIS_CFG_FMT_RAW8, + .data_alignment = 24, + }, { + .code = V4L2_MBUS_FMT_SGRBG10_1X10, + .fmt_reg = S5PCSIS_CFG_FMT_RAW10, + .data_alignment = 24, + }, { + .code = V4L2_MBUS_FMT_SGRBG12_1X12, + .fmt_reg = S5PCSIS_CFG_FMT_RAW12, + .data_alignment = 24, } }; @@ -261,7 +273,8 @@ static void s5pcsis_reset(struct csis_state *state) static void s5pcsis_system_enable(struct csis_state *state, int on) { - u32 val; + struct s5p_platform_mipi_csis *pdata = state->pdev->dev.platform_data; + u32 val, mask; val = s5pcsis_read(state, S5PCSIS_CTRL); if (on) @@ -271,10 +284,11 @@ static void s5pcsis_system_enable(struct csis_state *state, int on) s5pcsis_write(state, S5PCSIS_CTRL, val); val = s5pcsis_read(state, S5PCSIS_DPHYCTRL); - if (on) - val |= S5PCSIS_DPHYCTRL_ENABLE; - else - val &= ~S5PCSIS_DPHYCTRL_ENABLE; + val &= ~S5PCSIS_DPHYCTRL_ENABLE; + if (on) { + mask = (1 << (pdata->lanes + 1)) - 1; + val |= (mask & S5PCSIS_DPHYCTRL_ENABLE); + } s5pcsis_write(state, S5PCSIS_DPHYCTRL, val); } @@ -369,6 +383,30 @@ err: return -ENXIO; } +static void dump_regs(struct csis_state *state, const char *label) +{ + struct { + u32 offset; + const char * const name; + } registers[] = { + { 0x00, "CTRL" }, + { 0x04, "DPHYCTRL" }, + { 0x08, "CONFIG" }, + { 0x0c, "DPHYSTS" }, + { 0x10, "INTMSK" }, + { 0x2c, "RESOL" }, + { 0x38, "SDW_CONFIG" }, + }; + u32 i; + + v4l2_info(&state->sd, "--- %s ---\n", label); + + for (i = 0; i < ARRAY_SIZE(registers); i++) { + u32 cfg = s5pcsis_read(state, registers[i].offset); + v4l2_info(&state->sd, "%10s: 0x%08x\n", registers[i].name, cfg); + } +} + static void s5pcsis_start_stream(struct csis_state *state) { s5pcsis_reset(state); @@ -401,12 +439,12 @@ static void s5pcsis_log_counters(struct csis_state *state, bool non_errors) spin_lock_irqsave(&state->slock, flags); - for (i--; i >= 0; i--) - if (state->events[i].counter >= 0) + for (i--; i >= 0; i--) { + if (state->events[i].counter > 0 || debug) v4l2_info(&state->sd, "%s events: %d\n", state->events[i].name, state->events[i].counter); - + } spin_unlock_irqrestore(&state->slock, flags); } @@ -569,7 +607,11 @@ static int s5pcsis_log_status(struct v4l2_subdev *sd) { struct csis_state *state = sd_to_csis_state(sd); + mutex_lock(&state->lock); s5pcsis_log_counters(state, true); + if (debug && (state->flags & ST_POWERED)) + dump_regs(state, __func__); + mutex_unlock(&state->lock); return 0; } diff --git a/drivers/media/platform/s5p-tv/sii9234_drv.c b/drivers/media/platform/s5p-tv/sii9234_drv.c index 716d4846f8bd..4597342cdfbe 100644 --- a/drivers/media/platform/s5p-tv/sii9234_drv.c +++ b/drivers/media/platform/s5p-tv/sii9234_drv.c @@ -338,7 +338,7 @@ static int __devinit sii9234_probe(struct i2c_client *client, } ctx->gpio_n_reset = pdata->gpio_n_reset; - ret = gpio_request(ctx->gpio_n_reset, "MHL_RST"); + ret = devm_gpio_request(dev, ctx->gpio_n_reset, "MHL_RST"); if (ret) { dev_err(dev, "failed to acquire MHL_RST gpio\n"); return ret; @@ -370,7 +370,6 @@ fail_pm_get: fail_pm: pm_runtime_disable(dev); - gpio_free(ctx->gpio_n_reset); fail: dev_err(dev, "probe failed\n"); @@ -381,11 +380,8 @@ fail: static int __devexit sii9234_remove(struct i2c_client *client) { struct device *dev = &client->dev; - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct sii9234_context *ctx = sd_to_context(sd); pm_runtime_disable(dev); - gpio_free(ctx->gpio_n_reset); dev_info(dev, "remove successful\n"); diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c new file mode 100644 index 000000000000..a0186768479d --- /dev/null +++ b/drivers/media/platform/sh_veu.c @@ -0,0 +1,1266 @@ +/* + * sh-mobile VEU mem2mem driver + * + * Copyright (C) 2012 Renesas Electronics Corporation + * Author: Guennadi Liakhovetski, <g.liakhovetski@gmx.de> + * Copyright (C) 2008 Magnus Damm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License as + * published by the Free Software Foundation + */ + +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/videodev2.h> + +#include <media/v4l2-dev.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-mem2mem.h> +#include <media/videobuf2-dma-contig.h> + +#define VEU_STR 0x00 /* start register */ +#define VEU_SWR 0x10 /* src: line length */ +#define VEU_SSR 0x14 /* src: image size */ +#define VEU_SAYR 0x18 /* src: y/rgb plane address */ +#define VEU_SACR 0x1c /* src: c plane address */ +#define VEU_BSSR 0x20 /* bundle mode register */ +#define VEU_EDWR 0x30 /* dst: line length */ +#define VEU_DAYR 0x34 /* dst: y/rgb plane address */ +#define VEU_DACR 0x38 /* dst: c plane address */ +#define VEU_TRCR 0x50 /* transform control */ +#define VEU_RFCR 0x54 /* resize scale */ +#define VEU_RFSR 0x58 /* resize clip */ +#define VEU_ENHR 0x5c /* enhance */ +#define VEU_FMCR 0x70 /* filter mode */ +#define VEU_VTCR 0x74 /* lowpass vertical */ +#define VEU_HTCR 0x78 /* lowpass horizontal */ +#define VEU_APCR 0x80 /* color match */ +#define VEU_ECCR 0x84 /* color replace */ +#define VEU_AFXR 0x90 /* fixed mode */ +#define VEU_SWPR 0x94 /* swap */ +#define VEU_EIER 0xa0 /* interrupt mask */ +#define VEU_EVTR 0xa4 /* interrupt event */ +#define VEU_STAR 0xb0 /* status */ +#define VEU_BSRR 0xb4 /* reset */ + +#define VEU_MCR00 0x200 /* color conversion matrix coefficient 00 */ +#define VEU_MCR01 0x204 /* color conversion matrix coefficient 01 */ +#define VEU_MCR02 0x208 /* color conversion matrix coefficient 02 */ +#define VEU_MCR10 0x20c /* color conversion matrix coefficient 10 */ +#define VEU_MCR11 0x210 /* color conversion matrix coefficient 11 */ +#define VEU_MCR12 0x214 /* color conversion matrix coefficient 12 */ +#define VEU_MCR20 0x218 /* color conversion matrix coefficient 20 */ +#define VEU_MCR21 0x21c /* color conversion matrix coefficient 21 */ +#define VEU_MCR22 0x220 /* color conversion matrix coefficient 22 */ +#define VEU_COFFR 0x224 /* color conversion offset */ +#define VEU_CBR 0x228 /* color conversion clip */ + +/* + * 4092x4092 max size is the normal case. In some cases it can be reduced to + * 2048x2048, in other cases it can be 4092x8188 or even 8188x8188. + */ +#define MAX_W 4092 +#define MAX_H 4092 +#define MIN_W 8 +#define MIN_H 8 +#define ALIGN_W 4 + +/* 3 buffers of 2048 x 1536 - 3 megapixels @ 16bpp */ +#define VIDEO_MEM_LIMIT ALIGN(2048 * 1536 * 2 * 3, 1024 * 1024) + +#define MEM2MEM_DEF_TRANSLEN 1 + +struct sh_veu_dev; + +struct sh_veu_file { + struct sh_veu_dev *veu_dev; + bool cfg_needed; +}; + +struct sh_veu_format { + char *name; + u32 fourcc; + unsigned int depth; + unsigned int ydepth; +}; + +/* video data format */ +struct sh_veu_vfmt { + /* Replace with v4l2_rect */ + struct v4l2_rect frame; + unsigned int bytesperline; + unsigned int offset_y; + unsigned int offset_c; + const struct sh_veu_format *fmt; +}; + +struct sh_veu_dev { + struct v4l2_device v4l2_dev; + struct video_device vdev; + struct v4l2_m2m_dev *m2m_dev; + struct device *dev; + struct v4l2_m2m_ctx *m2m_ctx; + struct sh_veu_vfmt vfmt_out; + struct sh_veu_vfmt vfmt_in; + /* Only single user per direction so far */ + struct sh_veu_file *capture; + struct sh_veu_file *output; + struct mutex fop_lock; + void __iomem *base; + struct vb2_alloc_ctx *alloc_ctx; + spinlock_t lock; + bool is_2h; + unsigned int xaction; + bool aborting; +}; + +enum sh_veu_fmt_idx { + SH_VEU_FMT_NV12, + SH_VEU_FMT_NV16, + SH_VEU_FMT_NV24, + SH_VEU_FMT_RGB332, + SH_VEU_FMT_RGB444, + SH_VEU_FMT_RGB565, + SH_VEU_FMT_RGB666, + SH_VEU_FMT_RGB24, +}; + +#define VGA_WIDTH 640 +#define VGA_HEIGHT 480 + +#define DEFAULT_IN_WIDTH VGA_WIDTH +#define DEFAULT_IN_HEIGHT VGA_HEIGHT +#define DEFAULT_IN_FMTIDX SH_VEU_FMT_NV12 +#define DEFAULT_OUT_WIDTH VGA_WIDTH +#define DEFAULT_OUT_HEIGHT VGA_HEIGHT +#define DEFAULT_OUT_FMTIDX SH_VEU_FMT_RGB565 + +/* + * Alignment: Y-plane should be 4-byte aligned for NV12 and NV16, and 8-byte + * aligned for NV24. + */ +static const struct sh_veu_format sh_veu_fmt[] = { + [SH_VEU_FMT_NV12] = { .ydepth = 8, .depth = 12, .name = "NV12", .fourcc = V4L2_PIX_FMT_NV12 }, + [SH_VEU_FMT_NV16] = { .ydepth = 8, .depth = 16, .name = "NV16", .fourcc = V4L2_PIX_FMT_NV16 }, + [SH_VEU_FMT_NV24] = { .ydepth = 8, .depth = 24, .name = "NV24", .fourcc = V4L2_PIX_FMT_NV24 }, + [SH_VEU_FMT_RGB332] = { .ydepth = 8, .depth = 8, .name = "RGB332", .fourcc = V4L2_PIX_FMT_RGB332 }, + [SH_VEU_FMT_RGB444] = { .ydepth = 16, .depth = 16, .name = "RGB444", .fourcc = V4L2_PIX_FMT_RGB444 }, + [SH_VEU_FMT_RGB565] = { .ydepth = 16, .depth = 16, .name = "RGB565", .fourcc = V4L2_PIX_FMT_RGB565 }, + [SH_VEU_FMT_RGB666] = { .ydepth = 32, .depth = 32, .name = "BGR666", .fourcc = V4L2_PIX_FMT_BGR666 }, + [SH_VEU_FMT_RGB24] = { .ydepth = 24, .depth = 24, .name = "RGB24", .fourcc = V4L2_PIX_FMT_RGB24 }, +}; + +#define DEFAULT_IN_VFMT (struct sh_veu_vfmt){ \ + .frame = { \ + .width = VGA_WIDTH, \ + .height = VGA_HEIGHT, \ + }, \ + .bytesperline = (VGA_WIDTH * sh_veu_fmt[DEFAULT_IN_FMTIDX].ydepth) >> 3, \ + .fmt = &sh_veu_fmt[DEFAULT_IN_FMTIDX], \ +} + +#define DEFAULT_OUT_VFMT (struct sh_veu_vfmt){ \ + .frame = { \ + .width = VGA_WIDTH, \ + .height = VGA_HEIGHT, \ + }, \ + .bytesperline = (VGA_WIDTH * sh_veu_fmt[DEFAULT_OUT_FMTIDX].ydepth) >> 3, \ + .fmt = &sh_veu_fmt[DEFAULT_OUT_FMTIDX], \ +} + +/* + * TODO: add support for further output formats: + * SH_VEU_FMT_NV12, + * SH_VEU_FMT_NV16, + * SH_VEU_FMT_NV24, + * SH_VEU_FMT_RGB332, + * SH_VEU_FMT_RGB444, + * SH_VEU_FMT_RGB666, + * SH_VEU_FMT_RGB24, + */ + +static const int sh_veu_fmt_out[] = { + SH_VEU_FMT_RGB565, +}; + +/* + * TODO: add support for further input formats: + * SH_VEU_FMT_NV16, + * SH_VEU_FMT_NV24, + * SH_VEU_FMT_RGB565, + * SH_VEU_FMT_RGB666, + * SH_VEU_FMT_RGB24, + */ +static const int sh_veu_fmt_in[] = { + SH_VEU_FMT_NV12, +}; + +static enum v4l2_colorspace sh_veu_4cc2cspace(u32 fourcc) +{ + switch (fourcc) { + default: + BUG(); + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV24: + return V4L2_COLORSPACE_JPEG; + case V4L2_PIX_FMT_RGB332: + case V4L2_PIX_FMT_RGB444: + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_BGR666: + case V4L2_PIX_FMT_RGB24: + return V4L2_COLORSPACE_SRGB; + } +} + +static u32 sh_veu_reg_read(struct sh_veu_dev *veu, unsigned int reg) +{ + return ioread32(veu->base + reg); +} + +static void sh_veu_reg_write(struct sh_veu_dev *veu, unsigned int reg, + u32 value) +{ + iowrite32(value, veu->base + reg); +} + + /* ========== mem2mem callbacks ========== */ + +static void sh_veu_job_abort(void *priv) +{ + struct sh_veu_dev *veu = priv; + + /* Will cancel the transaction in the next interrupt handler */ + veu->aborting = true; +} + +static void sh_veu_lock(void *priv) +{ + struct sh_veu_dev *veu = priv; + + mutex_lock(&veu->fop_lock); +} + +static void sh_veu_unlock(void *priv) +{ + struct sh_veu_dev *veu = priv; + + mutex_unlock(&veu->fop_lock); +} + +static void sh_veu_process(struct sh_veu_dev *veu, + struct vb2_buffer *src_buf, + struct vb2_buffer *dst_buf) +{ + dma_addr_t addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0); + + sh_veu_reg_write(veu, VEU_DAYR, addr + veu->vfmt_out.offset_y); + sh_veu_reg_write(veu, VEU_DACR, veu->vfmt_out.offset_c ? + addr + veu->vfmt_out.offset_c : 0); + dev_dbg(veu->dev, "%s(): dst base %lx, y: %x, c: %x\n", __func__, + (unsigned long)addr, + veu->vfmt_out.offset_y, veu->vfmt_out.offset_c); + + addr = vb2_dma_contig_plane_dma_addr(src_buf, 0); + sh_veu_reg_write(veu, VEU_SAYR, addr + veu->vfmt_in.offset_y); + sh_veu_reg_write(veu, VEU_SACR, veu->vfmt_in.offset_c ? + addr + veu->vfmt_in.offset_c : 0); + dev_dbg(veu->dev, "%s(): src base %lx, y: %x, c: %x\n", __func__, + (unsigned long)addr, + veu->vfmt_in.offset_y, veu->vfmt_in.offset_c); + + sh_veu_reg_write(veu, VEU_STR, 1); + + sh_veu_reg_write(veu, VEU_EIER, 1); /* enable interrupt in VEU */ +} + +/** + * sh_veu_device_run() - prepares and starts the device + * + * This will be called by the framework when it decides to schedule a particular + * instance. + */ +static void sh_veu_device_run(void *priv) +{ + struct sh_veu_dev *veu = priv; + struct vb2_buffer *src_buf, *dst_buf; + + src_buf = v4l2_m2m_next_src_buf(veu->m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(veu->m2m_ctx); + + if (src_buf && dst_buf) + sh_veu_process(veu, src_buf, dst_buf); +} + + /* ========== video ioctls ========== */ + +static bool sh_veu_is_streamer(struct sh_veu_dev *veu, struct sh_veu_file *veu_file, + enum v4l2_buf_type type) +{ + return (type == V4L2_BUF_TYPE_VIDEO_CAPTURE && + veu_file == veu->capture) || + (type == V4L2_BUF_TYPE_VIDEO_OUTPUT && + veu_file == veu->output); +} + +static int sh_veu_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq); + +/* + * It is not unusual to have video nodes open()ed multiple times. While some + * V4L2 operations are non-intrusive, like querying formats and various + * parameters, others, like setting formats, starting and stopping streaming, + * queuing and dequeuing buffers, directly affect hardware configuration and / + * or execution. This function verifies availability of the requested interface + * and, if available, reserves it for the requesting user. + */ +static int sh_veu_stream_init(struct sh_veu_dev *veu, struct sh_veu_file *veu_file, + enum v4l2_buf_type type) +{ + struct sh_veu_file **stream; + + switch (type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + stream = &veu->capture; + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + stream = &veu->output; + break; + default: + return -EINVAL; + } + + if (*stream == veu_file) + return 0; + + if (*stream) + return -EBUSY; + + *stream = veu_file; + + return 0; +} + +static int sh_veu_context_init(struct sh_veu_dev *veu) +{ + if (veu->m2m_ctx) + return 0; + + veu->m2m_ctx = v4l2_m2m_ctx_init(veu->m2m_dev, veu, + sh_veu_queue_init); + + if (IS_ERR(veu->m2m_ctx)) + return PTR_ERR(veu->m2m_ctx); + + return 0; +} + +static int sh_veu_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strlcpy(cap->driver, "sh-veu", sizeof(cap->driver)); + strlcpy(cap->card, "sh-mobile VEU", sizeof(cap->card)); + strlcpy(cap->bus_info, "platform:sh-veu", sizeof(cap->bus_info)); + cap->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + + return 0; +} + +static int sh_veu_enum_fmt(struct v4l2_fmtdesc *f, const int *fmt, int fmt_num) +{ + if (f->index >= fmt_num) + return -EINVAL; + + strlcpy(f->description, sh_veu_fmt[fmt[f->index]].name, sizeof(f->description)); + f->pixelformat = sh_veu_fmt[fmt[f->index]].fourcc; + return 0; +} + +static int sh_veu_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + return sh_veu_enum_fmt(f, sh_veu_fmt_out, ARRAY_SIZE(sh_veu_fmt_out)); +} + +static int sh_veu_enum_fmt_vid_out(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + return sh_veu_enum_fmt(f, sh_veu_fmt_in, ARRAY_SIZE(sh_veu_fmt_in)); +} + +static struct sh_veu_vfmt *sh_veu_get_vfmt(struct sh_veu_dev *veu, + enum v4l2_buf_type type) +{ + switch (type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return &veu->vfmt_out; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + return &veu->vfmt_in; + default: + return NULL; + } +} + +static int sh_veu_g_fmt(struct sh_veu_file *veu_file, struct v4l2_format *f) +{ + struct v4l2_pix_format *pix = &f->fmt.pix; + struct sh_veu_dev *veu = veu_file->veu_dev; + struct sh_veu_vfmt *vfmt; + + vfmt = sh_veu_get_vfmt(veu, f->type); + + pix->width = vfmt->frame.width; + pix->height = vfmt->frame.height; + pix->field = V4L2_FIELD_NONE; + pix->pixelformat = vfmt->fmt->fourcc; + pix->colorspace = sh_veu_4cc2cspace(pix->pixelformat); + pix->bytesperline = vfmt->bytesperline; + pix->sizeimage = vfmt->bytesperline * pix->height * + vfmt->fmt->depth / vfmt->fmt->ydepth; + pix->priv = 0; + dev_dbg(veu->dev, "%s(): type: %d, size %u @ %ux%u, fmt %x\n", __func__, + f->type, pix->sizeimage, pix->width, pix->height, pix->pixelformat); + + return 0; +} + +static int sh_veu_g_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + return sh_veu_g_fmt(priv, f); +} + +static int sh_veu_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + return sh_veu_g_fmt(priv, f); +} + +static int sh_veu_try_fmt(struct v4l2_format *f, const struct sh_veu_format *fmt) +{ + struct v4l2_pix_format *pix = &f->fmt.pix; + unsigned int y_bytes_used; + + /* + * V4L2 specification suggests, that the driver should correct the + * format struct if any of the dimensions is unsupported + */ + switch (pix->field) { + default: + case V4L2_FIELD_ANY: + pix->field = V4L2_FIELD_NONE; + /* fall through: continue handling V4L2_FIELD_NONE */ + case V4L2_FIELD_NONE: + break; + } + + v4l_bound_align_image(&pix->width, MIN_W, MAX_W, ALIGN_W, + &pix->height, MIN_H, MAX_H, 0, 0); + + y_bytes_used = (pix->width * fmt->ydepth) >> 3; + + if (pix->bytesperline < y_bytes_used) + pix->bytesperline = y_bytes_used; + pix->sizeimage = pix->height * pix->bytesperline * fmt->depth / fmt->ydepth; + + pix->pixelformat = fmt->fourcc; + pix->colorspace = sh_veu_4cc2cspace(pix->pixelformat); + pix->priv = 0; + + pr_debug("%s(): type: %d, size %u\n", __func__, f->type, pix->sizeimage); + + return 0; +} + +static const struct sh_veu_format *sh_veu_find_fmt(const struct v4l2_format *f) +{ + const int *fmt; + int i, n, dflt; + + pr_debug("%s(%d;%d)\n", __func__, f->type, f->fmt.pix.field); + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + fmt = sh_veu_fmt_out; + n = ARRAY_SIZE(sh_veu_fmt_out); + dflt = DEFAULT_OUT_FMTIDX; + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + default: + fmt = sh_veu_fmt_in; + n = ARRAY_SIZE(sh_veu_fmt_in); + dflt = DEFAULT_IN_FMTIDX; + break; + } + + for (i = 0; i < n; i++) + if (sh_veu_fmt[fmt[i]].fourcc == f->fmt.pix.pixelformat) + return &sh_veu_fmt[fmt[i]]; + + return &sh_veu_fmt[dflt]; +} + +static int sh_veu_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + const struct sh_veu_format *fmt; + + fmt = sh_veu_find_fmt(f); + if (!fmt) + /* wrong buffer type */ + return -EINVAL; + + return sh_veu_try_fmt(f, fmt); +} + +static int sh_veu_try_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + const struct sh_veu_format *fmt; + + fmt = sh_veu_find_fmt(f); + if (!fmt) + /* wrong buffer type */ + return -EINVAL; + + return sh_veu_try_fmt(f, fmt); +} + +static void sh_veu_colour_offset(struct sh_veu_dev *veu, struct sh_veu_vfmt *vfmt) +{ + /* dst_left and dst_top validity will be verified in CROP / COMPOSE */ + unsigned int left = vfmt->frame.left & ~0x03; + unsigned int top = vfmt->frame.top; + dma_addr_t offset = ((left * veu->vfmt_out.fmt->depth) >> 3) + + top * veu->vfmt_out.bytesperline; + unsigned int y_line; + + vfmt->offset_y = offset; + + switch (vfmt->fmt->fourcc) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV24: + y_line = ALIGN(vfmt->frame.width, 16); + vfmt->offset_c = offset + y_line * vfmt->frame.height; + break; + case V4L2_PIX_FMT_RGB332: + case V4L2_PIX_FMT_RGB444: + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_BGR666: + case V4L2_PIX_FMT_RGB24: + vfmt->offset_c = 0; + break; + default: + BUG(); + } +} + +static int sh_veu_s_fmt(struct sh_veu_file *veu_file, struct v4l2_format *f) +{ + struct v4l2_pix_format *pix = &f->fmt.pix; + struct sh_veu_dev *veu = veu_file->veu_dev; + struct sh_veu_vfmt *vfmt; + struct vb2_queue *vq; + int ret = sh_veu_context_init(veu); + if (ret < 0) + return ret; + + vq = v4l2_m2m_get_vq(veu->m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + if (vb2_is_busy(vq)) { + v4l2_err(&veu_file->veu_dev->v4l2_dev, "%s queue busy\n", __func__); + return -EBUSY; + } + + vfmt = sh_veu_get_vfmt(veu, f->type); + /* called after try_fmt(), hence vfmt != NULL. Implicit BUG_ON() below */ + + vfmt->fmt = sh_veu_find_fmt(f); + /* vfmt->fmt != NULL following the same argument as above */ + vfmt->frame.width = pix->width; + vfmt->frame.height = pix->height; + vfmt->bytesperline = pix->bytesperline; + + sh_veu_colour_offset(veu, vfmt); + + /* + * We could also verify and require configuration only if any parameters + * actually have changed, but it is unlikely, that the user requests the + * same configuration several times without closing the device. + */ + veu_file->cfg_needed = true; + + dev_dbg(veu->dev, + "Setting format for type %d, wxh: %dx%d, fmt: %x\n", + f->type, pix->width, pix->height, vfmt->fmt->fourcc); + + return 0; +} + +static int sh_veu_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + int ret = sh_veu_try_fmt_vid_cap(file, priv, f); + if (ret) + return ret; + + return sh_veu_s_fmt(priv, f); +} + +static int sh_veu_s_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + int ret = sh_veu_try_fmt_vid_out(file, priv, f); + if (ret) + return ret; + + return sh_veu_s_fmt(priv, f); +} + +static int sh_veu_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *reqbufs) +{ + struct sh_veu_file *veu_file = priv; + struct sh_veu_dev *veu = veu_file->veu_dev; + int ret = sh_veu_context_init(veu); + if (ret < 0) + return ret; + + ret = sh_veu_stream_init(veu, veu_file, reqbufs->type); + if (ret < 0) + return ret; + + return v4l2_m2m_reqbufs(file, veu->m2m_ctx, reqbufs); +} + +static int sh_veu_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct sh_veu_file *veu_file = priv; + + if (!sh_veu_is_streamer(veu_file->veu_dev, veu_file, buf->type)) + return -EBUSY; + + return v4l2_m2m_querybuf(file, veu_file->veu_dev->m2m_ctx, buf); +} + +static int sh_veu_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct sh_veu_file *veu_file = priv; + + dev_dbg(veu_file->veu_dev->dev, "%s(%d)\n", __func__, buf->type); + if (!sh_veu_is_streamer(veu_file->veu_dev, veu_file, buf->type)) + return -EBUSY; + + return v4l2_m2m_qbuf(file, veu_file->veu_dev->m2m_ctx, buf); +} + +static int sh_veu_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct sh_veu_file *veu_file = priv; + + dev_dbg(veu_file->veu_dev->dev, "%s(%d)\n", __func__, buf->type); + if (!sh_veu_is_streamer(veu_file->veu_dev, veu_file, buf->type)) + return -EBUSY; + + return v4l2_m2m_dqbuf(file, veu_file->veu_dev->m2m_ctx, buf); +} + +static void sh_veu_calc_scale(struct sh_veu_dev *veu, + int size_in, int size_out, int crop_out, + u32 *mant, u32 *frac, u32 *rep) +{ + u32 fixpoint; + + /* calculate FRAC and MANT */ + *rep = *mant = *frac = 0; + + if (size_in == size_out) { + if (crop_out != size_out) + *mant = 1; /* needed for cropping */ + return; + } + + /* VEU2H special upscale */ + if (veu->is_2h && size_out > size_in) { + u32 fixpoint = (4096 * size_in) / size_out; + *mant = fixpoint / 4096; + *frac = (fixpoint - (*mant * 4096)) & ~0x07; + + switch (*frac) { + case 0x800: + *rep = 1; + break; + case 0x400: + *rep = 3; + break; + case 0x200: + *rep = 7; + break; + } + if (*rep) + return; + } + + fixpoint = (4096 * (size_in - 1)) / (size_out + 1); + *mant = fixpoint / 4096; + *frac = fixpoint - (*mant * 4096); + + if (*frac & 0x07) { + /* + * FIXME: do we really have to round down twice in the + * up-scaling case? + */ + *frac &= ~0x07; + if (size_out > size_in) + *frac -= 8; /* round down if scaling up */ + else + *frac += 8; /* round up if scaling down */ + } +} + +static unsigned long sh_veu_scale_v(struct sh_veu_dev *veu, + int size_in, int size_out, int crop_out) +{ + u32 mant, frac, value, rep; + + sh_veu_calc_scale(veu, size_in, size_out, crop_out, &mant, &frac, &rep); + + /* set scale */ + value = (sh_veu_reg_read(veu, VEU_RFCR) & ~0xffff0000) | + (((mant << 12) | frac) << 16); + + sh_veu_reg_write(veu, VEU_RFCR, value); + + /* set clip */ + value = (sh_veu_reg_read(veu, VEU_RFSR) & ~0xffff0000) | + (((rep << 12) | crop_out) << 16); + + sh_veu_reg_write(veu, VEU_RFSR, value); + + return ALIGN((size_in * crop_out) / size_out, 4); +} + +static unsigned long sh_veu_scale_h(struct sh_veu_dev *veu, + int size_in, int size_out, int crop_out) +{ + u32 mant, frac, value, rep; + + sh_veu_calc_scale(veu, size_in, size_out, crop_out, &mant, &frac, &rep); + + /* set scale */ + value = (sh_veu_reg_read(veu, VEU_RFCR) & ~0xffff) | + (mant << 12) | frac; + + sh_veu_reg_write(veu, VEU_RFCR, value); + + /* set clip */ + value = (sh_veu_reg_read(veu, VEU_RFSR) & ~0xffff) | + (rep << 12) | crop_out; + + sh_veu_reg_write(veu, VEU_RFSR, value); + + return ALIGN((size_in * crop_out) / size_out, 4); +} + +static void sh_veu_configure(struct sh_veu_dev *veu) +{ + u32 src_width, src_stride, src_height; + u32 dst_width, dst_stride, dst_height; + u32 real_w, real_h; + + /* reset VEU */ + sh_veu_reg_write(veu, VEU_BSRR, 0x100); + + src_width = veu->vfmt_in.frame.width; + src_height = veu->vfmt_in.frame.height; + src_stride = ALIGN(veu->vfmt_in.frame.width, 16); + + dst_width = real_w = veu->vfmt_out.frame.width; + dst_height = real_h = veu->vfmt_out.frame.height; + /* Datasheet is unclear - whether it's always number of bytes or not */ + dst_stride = veu->vfmt_out.bytesperline; + + /* + * So far real_w == dst_width && real_h == dst_height, but it wasn't + * necessarily the case in the original vidix driver, so, it may change + * here in the future too. + */ + src_width = sh_veu_scale_h(veu, src_width, real_w, dst_width); + src_height = sh_veu_scale_v(veu, src_height, real_h, dst_height); + + sh_veu_reg_write(veu, VEU_SWR, src_stride); + sh_veu_reg_write(veu, VEU_SSR, src_width | (src_height << 16)); + sh_veu_reg_write(veu, VEU_BSSR, 0); /* not using bundle mode */ + + sh_veu_reg_write(veu, VEU_EDWR, dst_stride); + sh_veu_reg_write(veu, VEU_DACR, 0); /* unused for RGB */ + + sh_veu_reg_write(veu, VEU_SWPR, 0x67); + sh_veu_reg_write(veu, VEU_TRCR, (6 << 16) | (0 << 14) | 2 | 4); + + if (veu->is_2h) { + sh_veu_reg_write(veu, VEU_MCR00, 0x0cc5); + sh_veu_reg_write(veu, VEU_MCR01, 0x0950); + sh_veu_reg_write(veu, VEU_MCR02, 0x0000); + + sh_veu_reg_write(veu, VEU_MCR10, 0x397f); + sh_veu_reg_write(veu, VEU_MCR11, 0x0950); + sh_veu_reg_write(veu, VEU_MCR12, 0x3ccd); + + sh_veu_reg_write(veu, VEU_MCR20, 0x0000); + sh_veu_reg_write(veu, VEU_MCR21, 0x0950); + sh_veu_reg_write(veu, VEU_MCR22, 0x1023); + + sh_veu_reg_write(veu, VEU_COFFR, 0x00800010); + } +} + +static int sh_veu_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct sh_veu_file *veu_file = priv; + + if (!sh_veu_is_streamer(veu_file->veu_dev, veu_file, type)) + return -EBUSY; + + if (veu_file->cfg_needed) { + struct sh_veu_dev *veu = veu_file->veu_dev; + veu_file->cfg_needed = false; + sh_veu_configure(veu_file->veu_dev); + veu->xaction = 0; + veu->aborting = false; + } + + return v4l2_m2m_streamon(file, veu_file->veu_dev->m2m_ctx, type); +} + +static int sh_veu_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct sh_veu_file *veu_file = priv; + + if (!sh_veu_is_streamer(veu_file->veu_dev, veu_file, type)) + return -EBUSY; + + return v4l2_m2m_streamoff(file, veu_file->veu_dev->m2m_ctx, type); +} + +static const struct v4l2_ioctl_ops sh_veu_ioctl_ops = { + .vidioc_querycap = sh_veu_querycap, + + .vidioc_enum_fmt_vid_cap = sh_veu_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = sh_veu_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = sh_veu_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = sh_veu_s_fmt_vid_cap, + + .vidioc_enum_fmt_vid_out = sh_veu_enum_fmt_vid_out, + .vidioc_g_fmt_vid_out = sh_veu_g_fmt_vid_out, + .vidioc_try_fmt_vid_out = sh_veu_try_fmt_vid_out, + .vidioc_s_fmt_vid_out = sh_veu_s_fmt_vid_out, + + .vidioc_reqbufs = sh_veu_reqbufs, + .vidioc_querybuf = sh_veu_querybuf, + + .vidioc_qbuf = sh_veu_qbuf, + .vidioc_dqbuf = sh_veu_dqbuf, + + .vidioc_streamon = sh_veu_streamon, + .vidioc_streamoff = sh_veu_streamoff, +}; + + /* ========== Queue operations ========== */ + +static int sh_veu_queue_setup(struct vb2_queue *vq, + const struct v4l2_format *f, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct sh_veu_dev *veu = vb2_get_drv_priv(vq); + struct sh_veu_vfmt *vfmt; + unsigned int size, count = *nbuffers; + + if (f) { + const struct v4l2_pix_format *pix = &f->fmt.pix; + const struct sh_veu_format *fmt = sh_veu_find_fmt(f); + struct v4l2_format ftmp = *f; + + if (fmt->fourcc != pix->pixelformat) + return -EINVAL; + sh_veu_try_fmt(&ftmp, fmt); + if (ftmp.fmt.pix.width != pix->width || + ftmp.fmt.pix.height != pix->height) + return -EINVAL; + size = pix->bytesperline ? pix->bytesperline * pix->height : + pix->width * pix->height * fmt->depth >> 3; + } else { + vfmt = sh_veu_get_vfmt(veu, vq->type); + size = vfmt->bytesperline * vfmt->frame.height; + } + + if (count < 2) + *nbuffers = count = 2; + + if (size * count > VIDEO_MEM_LIMIT) { + count = VIDEO_MEM_LIMIT / size; + *nbuffers = count; + } + + *nplanes = 1; + sizes[0] = size; + alloc_ctxs[0] = veu->alloc_ctx; + + dev_dbg(veu->dev, "get %d buffer(s) of size %d each.\n", count, size); + + return 0; +} + +static int sh_veu_buf_prepare(struct vb2_buffer *vb) +{ + struct sh_veu_dev *veu = vb2_get_drv_priv(vb->vb2_queue); + struct sh_veu_vfmt *vfmt; + unsigned int sizeimage; + + vfmt = sh_veu_get_vfmt(veu, vb->vb2_queue->type); + sizeimage = vfmt->bytesperline * vfmt->frame.height * + vfmt->fmt->depth / vfmt->fmt->ydepth; + + if (vb2_plane_size(vb, 0) < sizeimage) { + dev_dbg(veu->dev, "%s data will not fit into plane (%lu < %u)\n", + __func__, vb2_plane_size(vb, 0), sizeimage); + return -EINVAL; + } + + vb2_set_plane_payload(vb, 0, sizeimage); + + return 0; +} + +static void sh_veu_buf_queue(struct vb2_buffer *vb) +{ + struct sh_veu_dev *veu = vb2_get_drv_priv(vb->vb2_queue); + dev_dbg(veu->dev, "%s(%d)\n", __func__, vb->v4l2_buf.type); + v4l2_m2m_buf_queue(veu->m2m_ctx, vb); +} + +static void sh_veu_wait_prepare(struct vb2_queue *q) +{ + sh_veu_unlock(vb2_get_drv_priv(q)); +} + +static void sh_veu_wait_finish(struct vb2_queue *q) +{ + sh_veu_lock(vb2_get_drv_priv(q)); +} + +static const struct vb2_ops sh_veu_qops = { + .queue_setup = sh_veu_queue_setup, + .buf_prepare = sh_veu_buf_prepare, + .buf_queue = sh_veu_buf_queue, + .wait_prepare = sh_veu_wait_prepare, + .wait_finish = sh_veu_wait_finish, +}; + +static int sh_veu_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + int ret; + + memset(src_vq, 0, sizeof(*src_vq)); + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + src_vq->io_modes = VB2_MMAP | VB2_USERPTR; + src_vq->drv_priv = priv; + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + src_vq->ops = &sh_veu_qops; + src_vq->mem_ops = &vb2_dma_contig_memops; + + ret = vb2_queue_init(src_vq); + if (ret < 0) + return ret; + + memset(dst_vq, 0, sizeof(*dst_vq)); + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dst_vq->io_modes = VB2_MMAP | VB2_USERPTR; + dst_vq->drv_priv = priv; + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->ops = &sh_veu_qops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + + return vb2_queue_init(dst_vq); +} + + /* ========== File operations ========== */ + +static int sh_veu_open(struct file *file) +{ + struct sh_veu_dev *veu = video_drvdata(file); + struct sh_veu_file *veu_file; + + veu_file = kzalloc(sizeof(*veu_file), GFP_KERNEL); + if (!veu_file) + return -ENOMEM; + + veu_file->veu_dev = veu; + veu_file->cfg_needed = true; + + file->private_data = veu_file; + + pm_runtime_get_sync(veu->dev); + + dev_dbg(veu->dev, "Created instance %p\n", veu_file); + + return 0; +} + +static int sh_veu_release(struct file *file) +{ + struct sh_veu_dev *veu = video_drvdata(file); + struct sh_veu_file *veu_file = file->private_data; + + dev_dbg(veu->dev, "Releasing instance %p\n", veu_file); + + pm_runtime_put(veu->dev); + + if (veu_file == veu->capture) { + veu->capture = NULL; + vb2_queue_release(v4l2_m2m_get_vq(veu->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE)); + } + + if (veu_file == veu->output) { + veu->output = NULL; + vb2_queue_release(v4l2_m2m_get_vq(veu->m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT)); + } + + if (!veu->output && !veu->capture && veu->m2m_ctx) { + v4l2_m2m_ctx_release(veu->m2m_ctx); + veu->m2m_ctx = NULL; + } + + kfree(veu_file); + + return 0; +} + +static unsigned int sh_veu_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct sh_veu_file *veu_file = file->private_data; + + return v4l2_m2m_poll(file, veu_file->veu_dev->m2m_ctx, wait); +} + +static int sh_veu_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct sh_veu_file *veu_file = file->private_data; + + return v4l2_m2m_mmap(file, veu_file->veu_dev->m2m_ctx, vma); +} + +static const struct v4l2_file_operations sh_veu_fops = { + .owner = THIS_MODULE, + .open = sh_veu_open, + .release = sh_veu_release, + .poll = sh_veu_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = sh_veu_mmap, +}; + +static const struct video_device sh_veu_videodev = { + .name = "sh-veu", + .fops = &sh_veu_fops, + .ioctl_ops = &sh_veu_ioctl_ops, + .minor = -1, + .release = video_device_release_empty, + .vfl_dir = VFL_DIR_M2M, +}; + +static const struct v4l2_m2m_ops sh_veu_m2m_ops = { + .device_run = sh_veu_device_run, + .job_abort = sh_veu_job_abort, +}; + +static irqreturn_t sh_veu_bh(int irq, void *dev_id) +{ + struct sh_veu_dev *veu = dev_id; + + if (veu->xaction == MEM2MEM_DEF_TRANSLEN || veu->aborting) { + v4l2_m2m_job_finish(veu->m2m_dev, veu->m2m_ctx); + veu->xaction = 0; + } else { + sh_veu_device_run(veu); + } + + return IRQ_HANDLED; +} + +static irqreturn_t sh_veu_isr(int irq, void *dev_id) +{ + struct sh_veu_dev *veu = dev_id; + struct vb2_buffer *dst; + struct vb2_buffer *src; + u32 status = sh_veu_reg_read(veu, VEU_EVTR); + + /* bundle read mode not used */ + if (!(status & 1)) + return IRQ_NONE; + + /* disable interrupt in VEU */ + sh_veu_reg_write(veu, VEU_EIER, 0); + /* halt operation */ + sh_veu_reg_write(veu, VEU_STR, 0); + /* ack int, write 0 to clear bits */ + sh_veu_reg_write(veu, VEU_EVTR, status & ~1); + + /* conversion completed */ + dst = v4l2_m2m_dst_buf_remove(veu->m2m_ctx); + src = v4l2_m2m_src_buf_remove(veu->m2m_ctx); + if (!src || !dst) + return IRQ_NONE; + + spin_lock(&veu->lock); + v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE); + v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE); + spin_unlock(&veu->lock); + + veu->xaction++; + + if (!veu->aborting) + return IRQ_WAKE_THREAD; + + return IRQ_HANDLED; +} + +static int __devinit sh_veu_probe(struct platform_device *pdev) +{ + struct sh_veu_dev *veu; + struct resource *reg_res; + struct video_device *vdev; + int irq, ret; + + reg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + + if (!reg_res || irq <= 0) { + dev_err(&pdev->dev, "Insufficient VEU platform information.\n"); + return -ENODEV; + } + + veu = devm_kzalloc(&pdev->dev, sizeof(*veu), GFP_KERNEL); + if (!veu) + return -ENOMEM; + + veu->is_2h = resource_size(reg_res) == 0x22c; + + veu->base = devm_request_and_ioremap(&pdev->dev, reg_res); + if (!veu->base) + return -ENOMEM; + + ret = devm_request_threaded_irq(&pdev->dev, irq, sh_veu_isr, sh_veu_bh, + 0, "veu", veu); + if (ret < 0) + return ret; + + ret = v4l2_device_register(&pdev->dev, &veu->v4l2_dev); + if (ret < 0) { + dev_err(&pdev->dev, "Error registering v4l2 device\n"); + return ret; + } + + vdev = &veu->vdev; + + veu->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); + if (IS_ERR(veu->alloc_ctx)) { + ret = PTR_ERR(veu->alloc_ctx); + goto einitctx; + } + + *vdev = sh_veu_videodev; + spin_lock_init(&veu->lock); + mutex_init(&veu->fop_lock); + vdev->lock = &veu->fop_lock; + + video_set_drvdata(vdev, veu); + + veu->dev = &pdev->dev; + veu->vfmt_out = DEFAULT_OUT_VFMT; + veu->vfmt_in = DEFAULT_IN_VFMT; + + veu->m2m_dev = v4l2_m2m_init(&sh_veu_m2m_ops); + if (IS_ERR(veu->m2m_dev)) { + ret = PTR_ERR(veu->m2m_dev); + v4l2_err(&veu->v4l2_dev, "Failed to init mem2mem device: %d\n", ret); + goto em2minit; + } + + pm_runtime_enable(&pdev->dev); + pm_runtime_resume(&pdev->dev); + + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + pm_runtime_suspend(&pdev->dev); + if (ret < 0) + goto evidreg; + + return ret; + +evidreg: + pm_runtime_disable(&pdev->dev); + v4l2_m2m_release(veu->m2m_dev); +em2minit: + vb2_dma_contig_cleanup_ctx(veu->alloc_ctx); +einitctx: + v4l2_device_unregister(&veu->v4l2_dev); + return ret; +} + +static int __devexit sh_veu_remove(struct platform_device *pdev) +{ + struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); + struct sh_veu_dev *veu = container_of(v4l2_dev, + struct sh_veu_dev, v4l2_dev); + + video_unregister_device(&veu->vdev); + pm_runtime_disable(&pdev->dev); + v4l2_m2m_release(veu->m2m_dev); + vb2_dma_contig_cleanup_ctx(veu->alloc_ctx); + v4l2_device_unregister(&veu->v4l2_dev); + + return 0; +} + +static struct platform_driver __refdata sh_veu_pdrv = { + .remove = __devexit_p(sh_veu_remove), + .driver = { + .name = "sh_veu", + .owner = THIS_MODULE, + }, +}; + +static int __init sh_veu_init(void) +{ + return platform_driver_probe(&sh_veu_pdrv, sh_veu_probe); +} + +static void __exit sh_veu_exit(void) +{ + platform_driver_unregister(&sh_veu_pdrv); +} + +module_init(sh_veu_init); +module_exit(sh_veu_exit); + +MODULE_DESCRIPTION("sh-mobile VEU mem2mem driver"); +MODULE_AUTHOR("Guennadi Liakhovetski, <g.liakhovetski@gmx.de>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index a1c87f0ceaab..1039ae82401b 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -207,6 +207,7 @@ static void sh_vou_stream_start(struct sh_vou_device *vou_dev, #endif switch (vou_dev->pix.pixelformat) { + default: case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV16: row_coeff = 1; @@ -595,9 +596,9 @@ static void vou_adjust_input(struct sh_vou_geometry *geo, v4l2_std_id std) */ static void vou_adjust_output(struct sh_vou_geometry *geo, v4l2_std_id std) { - unsigned int best_err = UINT_MAX, best, width_max, height_max, - img_height_max; - int i, idx; + unsigned int best_err = UINT_MAX, best = geo->in_width, + width_max, height_max, img_height_max; + int i, idx = 0; if (std & V4L2_STD_525_60) { width_max = 858; @@ -1091,7 +1092,7 @@ static irqreturn_t sh_vou_isr(int irq, void *dev_id) list_del(&vb->queue); vb->state = VIDEOBUF_DONE; - do_gettimeofday(&vb->ts); + v4l2_get_timestamp(&vb->ts); vb->field_count++; wake_up(&vb->done); diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index 6274a91c25c7..c8d748a31944 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -166,7 +166,7 @@ static irqreturn_t atmel_isi_handle_streaming(struct atmel_isi *isi) struct frame_buffer *buf = isi->active; list_del_init(&buf->list); - do_gettimeofday(&vb->v4l2_buf.timestamp); + v4l2_get_timestamp(&vb->v4l2_buf.timestamp); vb->v4l2_buf.sequence = isi->sequence++; vb2_buffer_done(vb, VB2_BUF_STATE_DONE); } diff --git a/drivers/media/platform/soc_camera/mx1_camera.c b/drivers/media/platform/soc_camera/mx1_camera.c index 032b8c9097f9..674ded646b66 100644 --- a/drivers/media/platform/soc_camera/mx1_camera.c +++ b/drivers/media/platform/soc_camera/mx1_camera.c @@ -307,7 +307,7 @@ static void mx1_camera_wakeup(struct mx1_camera_dev *pcdev, /* _init is used to debug races, see comment in mx1_camera_reqbufs() */ list_del_init(&vb->queue); vb->state = VIDEOBUF_DONE; - do_gettimeofday(&vb->ts); + v4l2_get_timestamp(&vb->ts); vb->field_count++; wake_up(&vb->done); diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c index 791cd1d54a76..28d5c84eef82 100644 --- a/drivers/media/platform/soc_camera/mx2_camera.c +++ b/drivers/media/platform/soc_camera/mx2_camera.c @@ -559,7 +559,7 @@ static void mx25_camera_frame_done(struct mx2_camera_dev *pcdev, int fb, 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)); - do_gettimeofday(&vb->v4l2_buf.timestamp); + v4l2_get_timestamp(&vb->v4l2_buf.timestamp); vb->v4l2_buf.sequence++; vb2_buffer_done(vb, VB2_BUF_STATE_DONE); @@ -922,7 +922,7 @@ static int mx2_start_streaming(struct vb2_queue *q, unsigned int count) 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_KERNEL); + GFP_ATOMIC); if (!pcdev->discard_buffer) { spin_unlock_irqrestore(&pcdev->lock, flags); return -ENOMEM; @@ -1437,8 +1437,6 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, return -EINVAL; } - /* FIXME: implement MX27 limits */ - /* limit to MX25 hardware capabilities */ if (is_imx25_camera(pcdev)) { if (xlate->host_fmt->bits_per_sample <= 8) @@ -1470,6 +1468,12 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, pix->sizeimage = soc_mbus_image_size(xlate->host_fmt, pix->bytesperline, pix->height); } + } else { + /* + * 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 */ @@ -1600,7 +1604,7 @@ static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev, vb2_get_plane_payload(vb, 0)); list_del_init(&buf->internal.queue); - do_gettimeofday(&vb->v4l2_buf.timestamp); + v4l2_get_timestamp(&vb->v4l2_buf.timestamp); vb->v4l2_buf.sequence = pcdev->frame_count; if (err) vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); diff --git a/drivers/media/platform/soc_camera/mx3_camera.c b/drivers/media/platform/soc_camera/mx3_camera.c index 06d16de76377..574d12522f95 100644 --- a/drivers/media/platform/soc_camera/mx3_camera.c +++ b/drivers/media/platform/soc_camera/mx3_camera.c @@ -156,7 +156,7 @@ static void mx3_cam_dma_done(void *arg) struct mx3_camera_buffer *buf = to_mx3_vb(vb); list_del_init(&buf->queue); - do_gettimeofday(&vb->v4l2_buf.timestamp); + v4l2_get_timestamp(&vb->v4l2_buf.timestamp); vb->v4l2_buf.field = mx3_cam->field; vb->v4l2_buf.sequence = mx3_cam->sequence++; vb2_buffer_done(vb, VB2_BUF_STATE_DONE); diff --git a/drivers/media/platform/soc_camera/omap1_camera.c b/drivers/media/platform/soc_camera/omap1_camera.c index 39a77f0b8860..8f9c1f44544c 100644 --- a/drivers/media/platform/soc_camera/omap1_camera.c +++ b/drivers/media/platform/soc_camera/omap1_camera.c @@ -592,7 +592,7 @@ static void videobuf_done(struct omap1_cam_dev *pcdev, suspend_capture(pcdev); } vb->state = result; - do_gettimeofday(&vb->ts); + v4l2_get_timestamp(&vb->ts); if (result != VIDEOBUF_ERROR) vb->field_count++; wake_up(&vb->done); diff --git a/drivers/media/platform/soc_camera/pxa_camera.c b/drivers/media/platform/soc_camera/pxa_camera.c index 3434ffe79c6e..8ff961eec39d 100644 --- a/drivers/media/platform/soc_camera/pxa_camera.c +++ b/drivers/media/platform/soc_camera/pxa_camera.c @@ -681,7 +681,7 @@ static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev, /* _init is used to debug races, see comment in pxa_camera_reqbufs() */ list_del_init(&vb->queue); vb->state = VIDEOBUF_DONE; - do_gettimeofday(&vb->ts); + v4l2_get_timestamp(&vb->ts); vb->field_count++; wake_up(&vb->done); dev_dbg(pcdev->soc_host.v4l2_dev.dev, "%s dequeud buffer (vb=0x%p)\n", diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index 2d8861c0e8f2..9f021043cfe6 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -516,7 +516,7 @@ static irqreturn_t sh_mobile_ceu_irq(int irq, void *data) pcdev->active = NULL; ret = sh_mobile_ceu_capture(pcdev); - do_gettimeofday(&vb->v4l2_buf.timestamp); + v4l2_get_timestamp(&vb->v4l2_buf.timestamp); if (!ret) { vb->v4l2_buf.field = pcdev->field; vb->v4l2_buf.sequence = pcdev->sequence++; @@ -572,7 +572,7 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) ret = v4l2_subdev_call(csi2_sd, core, s_power, 1); if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) { - pm_runtime_put_sync(ici->v4l2_dev.dev); + pm_runtime_put(ici->v4l2_dev.dev); return ret; } @@ -612,7 +612,7 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) } spin_unlock_irq(&pcdev->lock); - pm_runtime_put_sync(ici->v4l2_dev.dev); + pm_runtime_put(ici->v4l2_dev.dev); dev_info(icd->parent, "SuperH Mobile CEU driver detached from camera %d\n", @@ -2088,15 +2088,13 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (!res || (int)irq <= 0) { dev_err(&pdev->dev, "Not enough CEU platform resources.\n"); - err = -ENODEV; - goto exit; + return -ENODEV; } - pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL); + pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL); if (!pcdev) { dev_err(&pdev->dev, "Could not allocate pcdev\n"); - err = -ENOMEM; - goto exit; + return -ENOMEM; } INIT_LIST_HEAD(&pcdev->capture); @@ -2105,19 +2103,17 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev) pcdev->pdata = pdev->dev.platform_data; if (!pcdev->pdata) { - err = -EINVAL; dev_err(&pdev->dev, "CEU platform data not set.\n"); - goto exit_kfree; + return -EINVAL; } pcdev->max_width = pcdev->pdata->max_width ? : 2560; pcdev->max_height = pcdev->pdata->max_height ? : 1920; - base = ioremap_nocache(res->start, resource_size(res)); + base = devm_request_and_ioremap(&pdev->dev, res); if (!base) { - err = -ENXIO; dev_err(&pdev->dev, "Unable to ioremap CEU registers.\n"); - goto exit_kfree; + return -ENXIO; } pcdev->irq = irq; @@ -2133,16 +2129,15 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev) DMA_MEMORY_EXCLUSIVE); if (!err) { dev_err(&pdev->dev, "Unable to declare CEU memory.\n"); - err = -ENXIO; - goto exit_iounmap; + return -ENXIO; } pcdev->video_limit = resource_size(res); } /* request irq */ - err = request_irq(pcdev->irq, sh_mobile_ceu_irq, IRQF_DISABLED, - dev_name(&pdev->dev), pcdev); + err = devm_request_irq(&pdev->dev, pcdev->irq, sh_mobile_ceu_irq, + IRQF_DISABLED, dev_name(&pdev->dev), pcdev); if (err) { dev_err(&pdev->dev, "Unable to register CEU interrupt.\n"); goto exit_release_mem; @@ -2246,15 +2241,9 @@ exit_free_ctx: vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); exit_free_clk: pm_runtime_disable(&pdev->dev); - free_irq(pcdev->irq, pcdev); exit_release_mem: if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) dma_release_declared_memory(&pdev->dev); -exit_iounmap: - iounmap(base); -exit_kfree: - kfree(pcdev); -exit: return err; } @@ -2267,10 +2256,8 @@ static int __devexit sh_mobile_ceu_remove(struct platform_device *pdev) soc_camera_host_unregister(soc_host); pm_runtime_disable(&pdev->dev); - free_irq(pcdev->irq, pcdev); if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) dma_release_declared_memory(&pdev->dev); - iounmap(pcdev->base); vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); if (csi2_pdev && csi2_pdev->dev.driver) { struct module *csi2_drv = csi2_pdev->dev.driver->owner; @@ -2279,7 +2266,6 @@ static int __devexit sh_mobile_ceu_remove(struct platform_device *pdev) platform_device_put(csi2_pdev); module_put(csi2_drv); } - kfree(pcdev); return 0; } diff --git a/drivers/media/platform/soc_camera/sh_mobile_csi2.c b/drivers/media/platform/soc_camera/sh_mobile_csi2.c index 05286500b4d4..c573be702641 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_csi2.c +++ b/drivers/media/platform/soc_camera/sh_mobile_csi2.c @@ -318,23 +318,16 @@ static __devinit int sh_csi2_probe(struct platform_device *pdev) return -EINVAL; } - priv = kzalloc(sizeof(struct sh_csi2), GFP_KERNEL); + priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_csi2), GFP_KERNEL); if (!priv) return -ENOMEM; priv->irq = irq; - if (!request_mem_region(res->start, resource_size(res), pdev->name)) { - dev_err(&pdev->dev, "CSI2 register region already claimed\n"); - ret = -EBUSY; - goto ereqreg; - } - - priv->base = ioremap(res->start, resource_size(res)); + priv->base = devm_request_and_ioremap(&pdev->dev, res); if (!priv->base) { - ret = -ENXIO; dev_err(&pdev->dev, "Unable to ioremap CSI2 registers.\n"); - goto eremap; + return -ENXIO; } priv->pdev = pdev; @@ -357,11 +350,7 @@ static __devinit int sh_csi2_probe(struct platform_device *pdev) return 0; esdreg: - iounmap(priv->base); -eremap: - release_mem_region(res->start, resource_size(res)); -ereqreg: - kfree(priv); + platform_set_drvdata(pdev, NULL); return ret; } @@ -369,14 +358,10 @@ ereqreg: static __devexit int sh_csi2_remove(struct platform_device *pdev) { struct sh_csi2 *priv = platform_get_drvdata(pdev); - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); v4l2_device_unregister_subdev(&priv->subdev); pm_runtime_disable(&pdev->dev); - iounmap(priv->base); - release_mem_region(res->start, resource_size(res)); platform_set_drvdata(pdev, NULL); - kfree(priv); return 0; } diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index 4e3735679f17..a8ca956e7a40 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -908,6 +908,8 @@ static int soc_camera_s_crop(struct file *file, void *fh, dev_dbg(icd->pdev, "S_CROP(%ux%u@%u:%u)\n", rect->width, rect->height, rect->left, rect->top); + current_crop.type = a->type; + /* If get_crop fails, we'll let host and / or client drivers decide */ ret = ici->ops->get_crop(icd, ¤t_crop); @@ -1137,8 +1139,8 @@ static int soc_camera_probe(struct soc_camera_device *icd) if (ret < 0) return ret; - ret = regulator_bulk_get(icd->pdev, icl->num_regulators, - icl->regulators); + ret = devm_regulator_bulk_get(icd->pdev, icl->num_regulators, + icl->regulators); if (ret < 0) goto ereg; @@ -1242,7 +1244,6 @@ eadddev: evdc: ici->ops->remove(icd); eadd: - regulator_bulk_free(icl->num_regulators, icl->regulators); ereg: v4l2_ctrl_handler_free(&icd->ctrl_handler); return ret; @@ -1276,8 +1277,6 @@ static int soc_camera_remove(struct soc_camera_device *icd) } soc_camera_free_user_formats(icd); - regulator_bulk_free(icl->num_regulators, icl->regulators); - return 0; } diff --git a/drivers/media/platform/soc_camera/soc_mediabus.c b/drivers/media/platform/soc_camera/soc_mediabus.c index a397812635d6..89dce097a827 100644 --- a/drivers/media/platform/soc_camera/soc_mediabus.c +++ b/drivers/media/platform/soc_camera/soc_mediabus.c @@ -378,9 +378,6 @@ EXPORT_SYMBOL(soc_mbus_samples_per_pixel); s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf) { - if (mf->fourcc == V4L2_PIX_FMT_JPEG) - return 0; - if (mf->layout != SOC_MBUS_LAYOUT_PACKED) return width * mf->bits_per_sample / 8; @@ -403,9 +400,6 @@ EXPORT_SYMBOL(soc_mbus_bytes_per_line); s32 soc_mbus_image_size(const struct soc_mbus_pixelfmt *mf, u32 bytes_per_line, u32 height) { - if (mf->fourcc == V4L2_PIX_FMT_JPEG) - return 0; - if (mf->layout == SOC_MBUS_LAYOUT_PACKED) return bytes_per_line * height; diff --git a/drivers/media/platform/timblogiw.c b/drivers/media/platform/timblogiw.c index 02194c056b00..9de014100a0f 100644 --- a/drivers/media/platform/timblogiw.c +++ b/drivers/media/platform/timblogiw.c @@ -130,7 +130,7 @@ static void timblogiw_dma_cb(void *data) if (vb->state != VIDEOBUF_ERROR) { list_del(&vb->queue); - do_gettimeofday(&vb->ts); + v4l2_get_timestamp(&vb->ts); vb->field_count = fh->frame_count * 2; vb->state = VIDEOBUF_DONE; diff --git a/drivers/media/platform/vino.c b/drivers/media/platform/vino.c index 70b0bf4b2900..eb5d6f955709 100644 --- a/drivers/media/platform/vino.c +++ b/drivers/media/platform/vino.c @@ -2474,8 +2474,8 @@ static irqreturn_t vino_interrupt(int irq, void *dev_id) if ((!handled_a) && (done_a || skip_a)) { if (!skip_a) { - do_gettimeofday(&vino_drvdata-> - a.int_data.timestamp); + v4l2_get_timestamp( + &vino_drvdata->a.int_data.timestamp); vino_drvdata->a.int_data.frame_counter = fc_a; } vino_drvdata->a.int_data.skip = skip_a; @@ -2489,8 +2489,8 @@ static irqreturn_t vino_interrupt(int irq, void *dev_id) if ((!handled_b) && (done_b || skip_b)) { if (!skip_b) { - do_gettimeofday(&vino_drvdata-> - b.int_data.timestamp); + v4l2_get_timestamp( + &vino_drvdata->b.int_data.timestamp); vino_drvdata->b.int_data.frame_counter = fc_b; } vino_drvdata->b.int_data.skip = skip_b; @@ -3410,6 +3410,9 @@ static void vino_v4l2_get_buffer_status(struct vino_channel_settings *vcs, if (fb->map_count > 0) b->flags |= V4L2_BUF_FLAG_MAPPED; + b->flags &= ~V4L2_BUF_FLAG_TIMESTAMP_MASK; + b->flags |= V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + b->index = fb->id; b->memory = (vcs->fb_queue.type == VINO_MEMORY_MMAP) ? V4L2_MEMORY_MMAP : V4L2_MEMORY_USERPTR; diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index 0d59b9db83cb..ec6508909f02 100644 --- a/drivers/media/platform/vivi.c +++ b/drivers/media/platform/vivi.c @@ -36,9 +36,17 @@ #define VIVI_MODULE_NAME "vivi" -/* Wake up at about 30 fps */ -#define WAKE_NUMERATOR 30 -#define WAKE_DENOMINATOR 1001 +/* Maximum allowed frame rate + * + * Vivi will allow setting timeperframe in [1/FPS_MAX - FPS_MAX/1] range. + * + * Ideally FPS_MAX should be infinity, i.e. practically UINT_MAX, but that + * might hit application errors when they manipulate these values. + * + * Besides, for tpf < 1ms image-generation logic should be changed, to avoid + * producing frames with equal content. + */ +#define FPS_MAX 1000 #define MAX_WIDTH 1920 #define MAX_HEIGHT 1200 @@ -69,6 +77,12 @@ MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); /* Global font descriptor */ static const u8 *font8x16; +/* timeperframe: min/max and default */ +static const struct v4l2_fract + tpf_min = {.numerator = 1, .denominator = FPS_MAX}, + tpf_max = {.numerator = FPS_MAX, .denominator = 1}, + tpf_default = {.numerator = 1001, .denominator = 30000}; /* NTSC */ + #define dprintk(dev, level, fmt, arg...) \ v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ## arg) @@ -150,14 +164,14 @@ static struct vivi_fmt formats[] = { }, }; -static struct vivi_fmt *get_format(struct v4l2_format *f) +static struct vivi_fmt *__get_format(u32 pixelformat) { struct vivi_fmt *fmt; unsigned int k; for (k = 0; k < ARRAY_SIZE(formats); k++) { fmt = &formats[k]; - if (fmt->fourcc == f->fmt.pix.pixelformat) + if (fmt->fourcc == pixelformat) break; } @@ -167,6 +181,11 @@ static struct vivi_fmt *get_format(struct v4l2_format *f) return &formats[k]; } +static struct vivi_fmt *get_format(struct v4l2_format *f) +{ + return __get_format(f->fmt.pix.pixelformat); +} + /* buffer for one video frame */ struct vivi_buffer { /* common v4l buffer stuff -- must be first */ @@ -232,14 +251,16 @@ struct vivi_dev { /* video capture */ struct vivi_fmt *fmt; + struct v4l2_fract timeperframe; unsigned int width, height; struct vb2_queue vb_vidq; unsigned int field_count; u8 bars[9][3]; - u8 line[MAX_WIDTH * 8]; + u8 line[MAX_WIDTH * 8] __attribute__((__aligned__(4))); unsigned int pixelsize; u8 alpha_component; + u32 textfg, textbg; }; /* ------------------------------------------------------------------ @@ -511,66 +532,100 @@ static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos, bool odd) static void precalculate_line(struct vivi_dev *dev) { - int w; - - for (w = 0; w < dev->width * 2; w++) { - int colorpos = w / (dev->width / 8) % 8; - - gen_twopix(dev, dev->line + w * dev->pixelsize, colorpos, w & 1); + unsigned pixsize = dev->pixelsize; + unsigned pixsize2 = 2*pixsize; + int colorpos; + u8 *pos; + + for (colorpos = 0; colorpos < 16; ++colorpos) { + u8 pix[8]; + int wstart = colorpos * dev->width / 8; + int wend = (colorpos+1) * dev->width / 8; + int w; + + gen_twopix(dev, &pix[0], colorpos % 8, 0); + gen_twopix(dev, &pix[pixsize], colorpos % 8, 1); + + for (w = wstart/2*2, pos = dev->line + w*pixsize; w < wend; w += 2, pos += pixsize2) + memcpy(pos, pix, pixsize2); } } +/* need this to do rgb24 rendering */ +typedef struct { u16 __; u8 _; } __attribute__((packed)) x24; + static void gen_text(struct vivi_dev *dev, char *basep, int y, int x, char *text) { int line; + unsigned int width = dev->width; /* Checks if it is possible to show string */ - if (y + 16 >= dev->height || x + strlen(text) * 8 >= dev->width) + if (y + 16 >= dev->height || x + strlen(text) * 8 >= width) return; /* Print stream time */ - for (line = y; line < y + 16; line++) { - int j = 0; - char *pos = basep + line * dev->width * dev->pixelsize + x * dev->pixelsize; - char *s; - - for (s = text; *s; s++) { - u8 chr = font8x16[*s * 16 + line - y]; - int i; - - for (i = 0; i < 7; i++, j++) { - /* Draw white font on black background */ - if (chr & (1 << (7 - i))) - gen_twopix(dev, pos + j * dev->pixelsize, WHITE, (x+y) & 1); - else - gen_twopix(dev, pos + j * dev->pixelsize, TEXT_BLACK, (x+y) & 1); - } - } +#define PRINTSTR(PIXTYPE) do { \ + PIXTYPE fg; \ + PIXTYPE bg; \ + memcpy(&fg, &dev->textfg, sizeof(PIXTYPE)); \ + memcpy(&bg, &dev->textbg, sizeof(PIXTYPE)); \ + \ + for (line = 0; line < 16; line++) { \ + PIXTYPE *pos = (PIXTYPE *)( basep + ((y + line) * width + x) * sizeof(PIXTYPE) ); \ + u8 *s; \ + \ + for (s = text; *s; s++) { \ + u8 chr = font8x16[*s * 16 + line]; \ + \ + pos[0] = (chr & (0x01 << 7) ? fg : bg); \ + pos[1] = (chr & (0x01 << 6) ? fg : bg); \ + pos[2] = (chr & (0x01 << 5) ? fg : bg); \ + pos[3] = (chr & (0x01 << 4) ? fg : bg); \ + pos[4] = (chr & (0x01 << 3) ? fg : bg); \ + pos[5] = (chr & (0x01 << 2) ? fg : bg); \ + pos[6] = (chr & (0x01 << 1) ? fg : bg); \ + pos[7] = (chr & (0x01 << 0) ? fg : bg); \ + \ + pos += 8; \ + } \ + } \ +} while (0) + + switch (dev->pixelsize) { + case 2: + PRINTSTR(u16); break; + case 4: + PRINTSTR(u32); break; + case 3: + PRINTSTR(x24); break; } } static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) { - int wmax = dev->width; + int stride = dev->width * dev->pixelsize; int hmax = dev->height; - struct timeval ts; void *vbuf = vb2_plane_vaddr(&buf->vb, 0); unsigned ms; char str[100]; int h, line = 1; + u8 *linestart; s32 gain; if (!vbuf) return; + linestart = dev->line + (dev->mv_count % dev->width) * dev->pixelsize; + for (h = 0; h < hmax; h++) - memcpy(vbuf + h * wmax * dev->pixelsize, - dev->line + (dev->mv_count % wmax) * dev->pixelsize, - wmax * dev->pixelsize); + memcpy(vbuf + h * stride, linestart, stride); /* Updates stream time */ + gen_twopix(dev, (u8 *)&dev->textbg, TEXT_BLACK, /*odd=*/ 0); + gen_twopix(dev, (u8 *)&dev->textfg, WHITE, /*odd=*/ 0); + dev->ms += jiffies_to_msecs(jiffies - dev->jiffies); dev->jiffies = jiffies; ms = dev->ms; @@ -622,8 +677,7 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED; dev->field_count++; buf->vb.v4l2_buf.sequence = dev->field_count >> 1; - do_gettimeofday(&ts); - buf->vb.v4l2_buf.timestamp = ts; + v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); } static void vivi_thread_tick(struct vivi_dev *dev) @@ -645,7 +699,7 @@ static void vivi_thread_tick(struct vivi_dev *dev) list_del(&buf->list); spin_unlock_irqrestore(&dev->slock, flags); - do_gettimeofday(&buf->vb.v4l2_buf.timestamp); + v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); /* Fill buffer */ vivi_fillbuff(dev, buf); @@ -655,8 +709,8 @@ static void vivi_thread_tick(struct vivi_dev *dev) dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index); } -#define frames_to_ms(frames) \ - ((frames * WAKE_NUMERATOR * 1000) / WAKE_DENOMINATOR) +#define frames_to_ms(dev, frames) \ + ((frames * dev->timeperframe.numerator * 1000) / dev->timeperframe.denominator) static void vivi_sleep(struct vivi_dev *dev) { @@ -672,7 +726,7 @@ static void vivi_sleep(struct vivi_dev *dev) goto stop_task; /* Calculate time to wake up */ - timeout = msecs_to_jiffies(frames_to_ms(1)); + timeout = msecs_to_jiffies(frames_to_ms(dev, 1)); vivi_thread_tick(dev); @@ -1044,6 +1098,70 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) return 0; } +/* timeperframe is arbitrary and continous */ +static int vidioc_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fival) +{ + struct vivi_fmt *fmt; + + if (fival->index) + return -EINVAL; + + fmt = __get_format(fival->pixel_format); + if (!fmt) + return -EINVAL; + + /* regarding width & height - we support any */ + + fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; + + /* fill in stepwise (step=1.0 is requred by V4L2 spec) */ + fival->stepwise.min = tpf_min; + fival->stepwise.max = tpf_max; + fival->stepwise.step = (struct v4l2_fract) {1, 1}; + + return 0; +} + +static int vidioc_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *parm) +{ + struct vivi_dev *dev = video_drvdata(file); + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + parm->parm.capture.timeperframe = dev->timeperframe; + parm->parm.capture.readbuffers = 1; + return 0; +} + +#define FRACT_CMP(a, OP, b) \ + ((u64)(a).numerator * (b).denominator OP (u64)(b).numerator * (a).denominator) + +static int vidioc_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *parm) +{ + struct vivi_dev *dev = video_drvdata(file); + struct v4l2_fract tpf; + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + tpf = parm->parm.capture.timeperframe; + + /* tpf: {*, 0} resets timing; clip to [min, max]*/ + tpf = tpf.denominator ? tpf : tpf_default; + tpf = FRACT_CMP(tpf, <, tpf_min) ? tpf_min : tpf; + tpf = FRACT_CMP(tpf, >, tpf_max) ? tpf_max : tpf; + + dev->timeperframe = tpf; + parm->parm.capture.timeperframe = tpf; + parm->parm.capture.readbuffers = 1; + return 0; +} + /* --- controls ---------------------------------------------- */ static int vivi_g_volatile_ctrl(struct v4l2_ctrl *ctrl) @@ -1202,6 +1320,9 @@ static const struct v4l2_ioctl_ops vivi_ioctl_ops = { .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, + .vidioc_enum_frameintervals = vidioc_enum_frameintervals, + .vidioc_g_parm = vidioc_g_parm, + .vidioc_s_parm = vidioc_s_parm, .vidioc_streamon = vb2_ioctl_streamon, .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_log_status = v4l2_ctrl_log_status, @@ -1260,6 +1381,7 @@ static int __init vivi_create_instance(int inst) goto free_dev; dev->fmt = &formats[0]; + dev->timeperframe = tpf_default; dev->width = 640; dev->height = 480; dev->pixelsize = dev->fmt->depth / 8; diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c index 2d6fb26a0170..4d6a63fe6c5e 100644 --- a/drivers/media/rc/ati_remote.c +++ b/drivers/media/rc/ati_remote.c @@ -872,11 +872,11 @@ static int ati_remote_probe(struct usb_interface *interface, ati_remote = kzalloc(sizeof (struct ati_remote), GFP_KERNEL); rc_dev = rc_allocate_device(); if (!ati_remote || !rc_dev) - goto fail1; + goto exit_free_dev_rdev; /* Allocate URB buffers, URBs */ if (ati_remote_alloc_buffers(udev, ati_remote)) - goto fail2; + goto exit_free_buffers; ati_remote->endpoint_in = endpoint_in; ati_remote->endpoint_out = endpoint_out; @@ -924,12 +924,12 @@ static int ati_remote_probe(struct usb_interface *interface, /* Device Hardware Initialization - fills in ati_remote->idev from udev. */ err = ati_remote_initialize(ati_remote); if (err) - goto fail3; + goto exit_kill_urbs; /* Set up and register rc device */ err = rc_register_device(ati_remote->rdev); if (err) - goto fail3; + goto exit_kill_urbs; /* use our delay for rc_dev */ ati_remote->rdev->input_dev->rep[REP_DELAY] = repeat_delay; @@ -939,7 +939,7 @@ static int ati_remote_probe(struct usb_interface *interface, input_dev = input_allocate_device(); if (!input_dev) { err = -ENOMEM; - goto fail4; + goto exit_unregister_device; } ati_remote->idev = input_dev; @@ -947,19 +947,24 @@ static int ati_remote_probe(struct usb_interface *interface, err = input_register_device(input_dev); if (err) - goto fail5; + goto exit_free_input_device; } usb_set_intfdata(interface, ati_remote); return 0; - fail5: input_free_device(input_dev); - fail4: rc_unregister_device(rc_dev); + exit_free_input_device: + input_free_device(input_dev); + exit_unregister_device: + rc_unregister_device(rc_dev); rc_dev = NULL; - fail3: usb_kill_urb(ati_remote->irq_urb); + exit_kill_urbs: + usb_kill_urb(ati_remote->irq_urb); usb_kill_urb(ati_remote->out_urb); - fail2: ati_remote_free_buffers(ati_remote); - fail1: rc_free_device(rc_dev); + exit_free_buffers: + ati_remote_free_buffers(ati_remote); + exit_free_dev_rdev: + rc_free_device(rc_dev); kfree(ati_remote); return err; } diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c index 22231dd4f62b..e601166c1edb 100644 --- a/drivers/media/rc/ene_ir.c +++ b/drivers/media/rc/ene_ir.c @@ -1003,7 +1003,7 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) dev = kzalloc(sizeof(struct ene_device), GFP_KERNEL); rdev = rc_allocate_device(); if (!dev || !rdev) - goto failure; + goto exit_free_dev_rdev; /* validate resources */ error = -ENODEV; @@ -1014,10 +1014,10 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) if (!pnp_port_valid(pnp_dev, 0) || pnp_port_len(pnp_dev, 0) < ENE_IO_SIZE) - goto failure; + goto exit_free_dev_rdev; if (!pnp_irq_valid(pnp_dev, 0)) - goto failure; + goto exit_free_dev_rdev; spin_lock_init(&dev->hw_lock); @@ -1033,7 +1033,7 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) /* detect hardware version and features */ error = ene_hw_detect(dev); if (error) - goto failure; + goto exit_free_dev_rdev; if (!dev->hw_learning_and_tx_capable && txsim) { dev->hw_learning_and_tx_capable = true; @@ -1075,30 +1075,30 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) device_set_wakeup_capable(&pnp_dev->dev, true); device_set_wakeup_enable(&pnp_dev->dev, true); + error = rc_register_device(rdev); + if (error < 0) + goto exit_free_dev_rdev; + /* claim the resources */ error = -EBUSY; if (!request_region(dev->hw_io, ENE_IO_SIZE, ENE_DRIVER_NAME)) { - goto failure; + goto exit_unregister_device; } dev->irq = pnp_irq(pnp_dev, 0); if (request_irq(dev->irq, ene_isr, IRQF_SHARED, ENE_DRIVER_NAME, (void *)dev)) { - goto failure2; + goto exit_release_hw_io; } - error = rc_register_device(rdev); - if (error < 0) - goto failure3; - pr_notice("driver has been successfully loaded\n"); return 0; -failure3: - free_irq(dev->irq, dev); -failure2: +exit_release_hw_io: release_region(dev->hw_io, ENE_IO_SIZE); -failure: +exit_unregister_device: + rc_unregister_device(rdev); +exit_free_dev_rdev: rc_free_device(rdev); kfree(dev); return error; diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c index 936c3f79b62c..5eefe65760a4 100644 --- a/drivers/media/rc/fintek-cir.c +++ b/drivers/media/rc/fintek-cir.c @@ -500,18 +500,18 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id /* input device for IR remote (and tx) */ rdev = rc_allocate_device(); if (!rdev) - goto failure; + goto exit_free_dev_rdev; ret = -ENODEV; /* validate pnp resources */ if (!pnp_port_valid(pdev, 0)) { dev_err(&pdev->dev, "IR PNP Port not valid!\n"); - goto failure; + goto exit_free_dev_rdev; } if (!pnp_irq_valid(pdev, 0)) { dev_err(&pdev->dev, "IR PNP IRQ not valid!\n"); - goto failure; + goto exit_free_dev_rdev; } fintek->cir_addr = pnp_port_start(pdev, 0); @@ -528,7 +528,7 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id ret = fintek_hw_detect(fintek); if (ret) - goto failure; + goto exit_free_dev_rdev; /* Initialize CIR & CIR Wake Logical Devices */ fintek_config_mode_enable(fintek); @@ -557,33 +557,35 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id /* rx resolution is hardwired to 50us atm, 1, 25, 100 also possible */ rdev->rx_resolution = US_TO_NS(CIR_SAMPLE_PERIOD); + fintek->rdev = rdev; + ret = -EBUSY; /* now claim resources */ if (!request_region(fintek->cir_addr, fintek->cir_port_len, FINTEK_DRIVER_NAME)) - goto failure; + goto exit_free_dev_rdev; if (request_irq(fintek->cir_irq, fintek_cir_isr, IRQF_SHARED, FINTEK_DRIVER_NAME, (void *)fintek)) - goto failure2; + goto exit_free_cir_addr; ret = rc_register_device(rdev); if (ret) - goto failure3; + goto exit_free_irq; device_init_wakeup(&pdev->dev, true); - fintek->rdev = rdev; + fit_pr(KERN_NOTICE, "driver has been successfully loaded\n"); if (debug) cir_dump_regs(fintek); return 0; -failure3: +exit_free_irq: free_irq(fintek->cir_irq, fintek); -failure2: +exit_free_cir_addr: release_region(fintek->cir_addr, fintek->cir_port_len); -failure: +exit_free_dev_rdev: rc_free_device(rdev); kfree(fintek); diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c index ba1a1eb356cf..03e3cf6eb68f 100644 --- a/drivers/media/rc/gpio-ir-recv.c +++ b/drivers/media/rc/gpio-ir-recv.c @@ -129,12 +129,12 @@ static int __devinit gpio_ir_recv_probe(struct platform_device *pdev) err_request_irq: platform_set_drvdata(pdev, NULL); rc_unregister_device(rcdev); + rcdev = NULL; err_register_rc_device: err_gpio_direction_input: gpio_free(pdata->gpio_nr); err_gpio_request: rc_free_device(rcdev); - rcdev = NULL; err_allocate_device: kfree(gpio_dev); return rc; @@ -148,7 +148,6 @@ static int __devexit gpio_ir_recv_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); rc_unregister_device(gpio_dev->rcdev); gpio_free(gpio_dev->gpio_nr); - rc_free_device(gpio_dev->rcdev); kfree(gpio_dev); return 0; } diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c index 5e5a7f2b8184..e810846fada4 100644 --- a/drivers/media/rc/ite-cir.c +++ b/drivers/media/rc/ite-cir.c @@ -1472,7 +1472,7 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id /* input device for IR remote (and tx) */ rdev = rc_allocate_device(); if (!rdev) - goto failure; + goto exit_free_dev_rdev; itdev->rdev = rdev; ret = -ENODEV; @@ -1498,12 +1498,12 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id if (!pnp_port_valid(pdev, io_rsrc_no) || pnp_port_len(pdev, io_rsrc_no) != dev_desc->io_region_size) { dev_err(&pdev->dev, "IR PNP Port not valid!\n"); - goto failure; + goto exit_free_dev_rdev; } if (!pnp_irq_valid(pdev, 0)) { dev_err(&pdev->dev, "PNP IRQ not valid!\n"); - goto failure; + goto exit_free_dev_rdev; } /* store resource values */ @@ -1591,29 +1591,29 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id rdev->driver_name = ITE_DRIVER_NAME; rdev->map_name = RC_MAP_RC6_MCE; + ret = rc_register_device(rdev); + if (ret) + goto exit_free_dev_rdev; + ret = -EBUSY; /* now claim resources */ if (!request_region(itdev->cir_addr, dev_desc->io_region_size, ITE_DRIVER_NAME)) - goto failure; + goto exit_unregister_device; if (request_irq(itdev->cir_irq, ite_cir_isr, IRQF_SHARED, ITE_DRIVER_NAME, (void *)itdev)) - goto failure2; - - ret = rc_register_device(rdev); - if (ret) - goto failure3; + goto exit_release_cir_addr; ite_pr(KERN_NOTICE, "driver has been successfully loaded\n"); return 0; -failure3: - free_irq(itdev->cir_irq, itdev); -failure2: +exit_release_cir_addr: release_region(itdev->cir_addr, itdev->params.io_region_size); -failure: +exit_unregister_device: + rc_unregister_device(rdev); +exit_free_dev_rdev: rc_free_device(rdev); kfree(itdev); diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index ab84d66c67c1..778661971aed 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -88,6 +88,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-tevii-nec.o \ rc-tivo.o \ rc-total-media-in-hand.o \ + rc-total-media-in-hand-02.o \ rc-trekstor.o \ rc-tt-1500.o \ rc-twinhan1027.o \ diff --git a/drivers/media/rc/keymaps/rc-total-media-in-hand-02.c b/drivers/media/rc/keymaps/rc-total-media-in-hand-02.c new file mode 100644 index 000000000000..47270f72ebf0 --- /dev/null +++ b/drivers/media/rc/keymaps/rc-total-media-in-hand-02.c @@ -0,0 +1,86 @@ +/* + * Total Media In Hand_02 remote controller keytable for Mygica X8507 + * + * Copyright (C) 2012 Alfredo J. Delaiti <alfredodelaiti@netscape.net> + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <media/rc-map.h> +#include <linux/module.h> + + +static struct rc_map_table total_media_in_hand_02[] = { + { 0x0000, KEY_0 }, + { 0x0001, KEY_1 }, + { 0x0002, KEY_2 }, + { 0x0003, KEY_3 }, + { 0x0004, KEY_4 }, + { 0x0005, KEY_5 }, + { 0x0006, KEY_6 }, + { 0x0007, KEY_7 }, + { 0x0008, KEY_8 }, + { 0x0009, KEY_9 }, + { 0x000a, KEY_MUTE }, + { 0x000b, KEY_STOP }, /* Stop */ + { 0x000c, KEY_POWER2 }, /* Turn on/off application */ + { 0x000d, KEY_OK }, /* OK */ + { 0x000e, KEY_CAMERA }, /* Snapshot */ + { 0x000f, KEY_ZOOM }, /* Full Screen/Restore */ + { 0x0010, KEY_RIGHT }, /* Right arrow */ + { 0x0011, KEY_LEFT }, /* Left arrow */ + { 0x0012, KEY_CHANNELUP }, + { 0x0013, KEY_CHANNELDOWN }, + { 0x0014, KEY_SHUFFLE }, + { 0x0016, KEY_PAUSE }, + { 0x0017, KEY_PLAY }, /* Play */ + { 0x001e, KEY_TIME }, /* Time Shift */ + { 0x001f, KEY_RECORD }, + { 0x0020, KEY_UP }, + { 0x0021, KEY_DOWN }, + { 0x0025, KEY_POWER }, /* Turn off computer */ + { 0x0026, KEY_REWIND }, /* FR << */ + { 0x0027, KEY_FASTFORWARD }, /* FF >> */ + { 0x0029, KEY_ESC }, + { 0x002b, KEY_VOLUMEUP }, + { 0x002c, KEY_VOLUMEDOWN }, + { 0x002d, KEY_CHANNEL }, /* CH Surfing */ + { 0x0038, KEY_VIDEO }, /* TV/AV/S-Video/YPbPr */ +}; + +static struct rc_map_list total_media_in_hand_02_map = { + .map = { + .scan = total_media_in_hand_02, + .size = ARRAY_SIZE(total_media_in_hand_02), + .rc_type = RC_TYPE_RC5, + .name = RC_MAP_TOTAL_MEDIA_IN_HAND_02, + } +}; + +static int __init init_rc_map_total_media_in_hand_02(void) +{ + return rc_map_register(&total_media_in_hand_02_map); +} + +static void __exit exit_rc_map_total_media_in_hand_02(void) +{ + rc_map_unregister(&total_media_in_hand_02_map); +} + +module_init(init_rc_map_total_media_in_hand_02) +module_exit(exit_rc_map_total_media_in_hand_02) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR(" Alfredo J. Delaiti <alfredodelaiti@netscape.net>"); diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c index e4ea89a11eed..6cf43cc237eb 100644 --- a/drivers/media/rc/nuvoton-cir.c +++ b/drivers/media/rc/nuvoton-cir.c @@ -986,25 +986,25 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) /* input device for IR remote (and tx) */ rdev = rc_allocate_device(); if (!rdev) - goto failure; + goto exit_free_dev_rdev; ret = -ENODEV; /* validate pnp resources */ if (!pnp_port_valid(pdev, 0) || pnp_port_len(pdev, 0) < CIR_IOREG_LENGTH) { dev_err(&pdev->dev, "IR PNP Port not valid!\n"); - goto failure; + goto exit_free_dev_rdev; } if (!pnp_irq_valid(pdev, 0)) { dev_err(&pdev->dev, "PNP IRQ not valid!\n"); - goto failure; + goto exit_free_dev_rdev; } if (!pnp_port_valid(pdev, 1) || pnp_port_len(pdev, 1) < CIR_IOREG_LENGTH) { dev_err(&pdev->dev, "Wake PNP Port not valid!\n"); - goto failure; + goto exit_free_dev_rdev; } nvt->cir_addr = pnp_port_start(pdev, 0); @@ -1027,7 +1027,7 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) ret = nvt_hw_detect(nvt); if (ret) - goto failure; + goto exit_free_dev_rdev; /* Initialize CIR & CIR Wake Logical Devices */ nvt_efm_enable(nvt); @@ -1065,31 +1065,32 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) /* tx bits */ rdev->tx_resolution = XYZ; #endif + nvt->rdev = rdev; + + ret = rc_register_device(rdev); + if (ret) + goto exit_free_dev_rdev; ret = -EBUSY; /* now claim resources */ if (!request_region(nvt->cir_addr, CIR_IOREG_LENGTH, NVT_DRIVER_NAME)) - goto failure; + goto exit_unregister_device; if (request_irq(nvt->cir_irq, nvt_cir_isr, IRQF_SHARED, NVT_DRIVER_NAME, (void *)nvt)) - goto failure2; + goto exit_release_cir_addr; if (!request_region(nvt->cir_wake_addr, CIR_IOREG_LENGTH, NVT_DRIVER_NAME)) - goto failure3; + goto exit_free_irq; if (request_irq(nvt->cir_wake_irq, nvt_cir_wake_isr, IRQF_SHARED, NVT_DRIVER_NAME, (void *)nvt)) - goto failure4; - - ret = rc_register_device(rdev); - if (ret) - goto failure5; + goto exit_release_cir_wake_addr; device_init_wakeup(&pdev->dev, true); - nvt->rdev = rdev; + nvt_pr(KERN_NOTICE, "driver has been successfully loaded\n"); if (debug) { cir_dump_regs(nvt); @@ -1098,15 +1099,15 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) return 0; -failure5: - free_irq(nvt->cir_wake_irq, nvt); -failure4: +exit_release_cir_wake_addr: release_region(nvt->cir_wake_addr, CIR_IOREG_LENGTH); -failure3: +exit_free_irq: free_irq(nvt->cir_irq, nvt); -failure2: +exit_release_cir_addr: release_region(nvt->cir_addr, CIR_IOREG_LENGTH); -failure: +exit_unregister_device: + rc_unregister_device(rdev); +exit_free_dev_rdev: rc_free_device(rdev); kfree(nvt); diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 601d1ac1c688..d593bc65b4ca 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -789,8 +789,10 @@ static ssize_t show_protocols(struct device *device, } else if (dev->raw) { enabled = dev->raw->enabled_protocols; allowed = ir_raw_get_allowed_protocols(); - } else + } else { + mutex_unlock(&dev->lock); return -ENODEV; + } IR_dprintk(1, "allowed - 0x%llx, enabled - 0x%llx\n", (long long)allowed, diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c index 7f3c476dde05..553d1cdc439f 100644 --- a/drivers/media/rc/winbond-cir.c +++ b/drivers/media/rc/winbond-cir.c @@ -1093,11 +1093,15 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) data->dev->rx_resolution = US_TO_NS(2); data->dev->allowed_protos = RC_BIT_ALL; + err = rc_register_device(data->dev); + if (err) + goto exit_free_rc; + if (!request_region(data->wbase, WAKEUP_IOMEM_LEN, DRVNAME)) { dev_err(dev, "Region 0x%lx-0x%lx already in use!\n", data->wbase, data->wbase + WAKEUP_IOMEM_LEN - 1); err = -EBUSY; - goto exit_free_rc; + goto exit_unregister_device; } if (!request_region(data->ebase, EHFUNC_IOMEM_LEN, DRVNAME)) { @@ -1122,24 +1126,20 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) goto exit_release_sbase; } - err = rc_register_device(data->dev); - if (err) - goto exit_free_irq; - device_init_wakeup(&device->dev, 1); wbcir_init_hw(data); return 0; -exit_free_irq: - free_irq(data->irq, device); exit_release_sbase: release_region(data->sbase, SP_IOMEM_LEN); exit_release_ebase: release_region(data->ebase, EHFUNC_IOMEM_LEN); exit_release_wbase: release_region(data->wbase, WAKEUP_IOMEM_LEN); +exit_unregister_device: + rc_unregister_device(data->dev); exit_free_rc: rc_free_device(data->dev); exit_unregister_led: diff --git a/drivers/media/tuners/tda18212.c b/drivers/media/tuners/tda18212.c index 5d9f02842501..e4a84ee231cf 100644 --- a/drivers/media/tuners/tda18212.c +++ b/drivers/media/tuners/tda18212.c @@ -277,7 +277,7 @@ struct dvb_frontend *tda18212_attach(struct dvb_frontend *fe, { struct tda18212_priv *priv = NULL; int ret; - u8 uninitialized_var(val); + u8 val; priv = kzalloc(sizeof(struct tda18212_priv), GFP_KERNEL); if (priv == NULL) @@ -296,8 +296,8 @@ struct dvb_frontend *tda18212_attach(struct dvb_frontend *fe, if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ - dev_dbg(&priv->i2c->dev, "%s: ret=%d chip id=%02x\n", __func__, ret, - val); + if (!ret) + dev_dbg(&priv->i2c->dev, "%s: chip id=%02x\n", __func__, val); if (ret || val != 0xc7) { kfree(priv); return NULL; diff --git a/drivers/media/tuners/tda18218.c b/drivers/media/tuners/tda18218.c index 18198537be9f..2d31aeb6b088 100644 --- a/drivers/media/tuners/tda18218.c +++ b/drivers/media/tuners/tda18218.c @@ -277,7 +277,7 @@ struct dvb_frontend *tda18218_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct tda18218_config *cfg) { struct tda18218_priv *priv = NULL; - u8 uninitialized_var(val); + u8 val; int ret; /* chip default registers values */ static u8 def_regs[] = { @@ -302,8 +302,8 @@ struct dvb_frontend *tda18218_attach(struct dvb_frontend *fe, /* check if the tuner is there */ ret = tda18218_rd_reg(priv, R00_ID, &val); - dev_dbg(&priv->i2c->dev, "%s: ret=%d chip id=%02x\n", __func__, ret, - val); + if (!ret) + dev_dbg(&priv->i2c->dev, "%s: chip id=%02x\n", __func__, val); if (ret || val != def_regs[R00_ID]) { kfree(priv); return NULL; diff --git a/drivers/media/tuners/tda18271-fe.c b/drivers/media/tuners/tda18271-fe.c index 72c26fd77922..e7786862dab2 100644 --- a/drivers/media/tuners/tda18271-fe.c +++ b/drivers/media/tuners/tda18271-fe.c @@ -1122,6 +1122,7 @@ static int tda18271_dump_std_map(struct dvb_frontend *fe) tda18271_dump_std_item(dvbt_7, "dvbt 7"); tda18271_dump_std_item(dvbt_8, "dvbt 8"); tda18271_dump_std_item(qam_6, "qam 6 "); + tda18271_dump_std_item(qam_7, "qam 7 "); tda18271_dump_std_item(qam_8, "qam 8 "); return 0; @@ -1149,6 +1150,7 @@ static int tda18271_update_std_map(struct dvb_frontend *fe, tda18271_update_std(dvbt_7, "dvbt 7"); tda18271_update_std(dvbt_8, "dvbt 8"); tda18271_update_std(qam_6, "qam 6"); + tda18271_update_std(qam_7, "qam 7"); tda18271_update_std(qam_8, "qam 8"); return 0; diff --git a/drivers/media/usb/Kconfig b/drivers/media/usb/Kconfig index 6746994d03fe..0a7d520636a9 100644 --- a/drivers/media/usb/Kconfig +++ b/drivers/media/usb/Kconfig @@ -21,7 +21,6 @@ endif if MEDIA_ANALOG_TV_SUPPORT comment "Analog TV USB devices" -source "drivers/media/usb/au0828/Kconfig" source "drivers/media/usb/pvrusb2/Kconfig" source "drivers/media/usb/hdpvr/Kconfig" source "drivers/media/usb/tlg2300/Kconfig" @@ -31,6 +30,7 @@ endif if (MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT) comment "Analog/digital TV USB devices" +source "drivers/media/usb/au0828/Kconfig" source "drivers/media/usb/cx231xx/Kconfig" source "drivers/media/usb/tm6000/Kconfig" endif diff --git a/drivers/media/usb/au0828/Kconfig b/drivers/media/usb/au0828/Kconfig index 1766c0ce93be..953a37c613b1 100644 --- a/drivers/media/usb/au0828/Kconfig +++ b/drivers/media/usb/au0828/Kconfig @@ -1,17 +1,28 @@ config VIDEO_AU0828 tristate "Auvitek AU0828 support" - depends on I2C && INPUT && DVB_CORE && USB && VIDEO_V4L2 + depends on I2C && INPUT && DVB_CORE && USB select I2C_ALGOBIT select VIDEO_TVEEPROM select VIDEOBUF_VMALLOC select DVB_AU8522_DTV if MEDIA_SUBDRV_AUTOSELECT - select DVB_AU8522_V4L if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT ---help--- - This is a video4linux driver for Auvitek's USB device. + This is a hybrid analog/digital tv capture driver for + Auvitek's AU0828 USB device. To compile this driver as a module, choose M here: the module will be called au0828 + +config VIDEO_AU0828_V4L2 + bool "Auvitek AU0828 v4l2 analog video support" + depends on VIDEO_AU0828 && VIDEO_V4L2 + select DVB_AU8522_V4L if MEDIA_SUBDRV_AUTOSELECT + default y + ---help--- + This is a video4linux driver for Auvitek's USB device. + + Choose Y here to include support for v4l2 analog video + capture within the au0828 driver. diff --git a/drivers/media/usb/au0828/Makefile b/drivers/media/usb/au0828/Makefile index 98cc20cc0ffb..be3bdf698022 100644 --- a/drivers/media/usb/au0828/Makefile +++ b/drivers/media/usb/au0828/Makefile @@ -1,4 +1,8 @@ -au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o au0828-video.o au0828-vbi.o +au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o + +ifeq ($(CONFIG_VIDEO_AU0828_V4L2),y) + au0828-objs += au0828-video.o au0828-vbi.o +endif obj-$(CONFIG_VIDEO_AU0828) += au0828.o diff --git a/drivers/media/usb/au0828/au0828-cards.c b/drivers/media/usb/au0828/au0828-cards.c index 0cb7c28dcb17..88e35dfa33d4 100644 --- a/drivers/media/usb/au0828/au0828-cards.c +++ b/drivers/media/usb/au0828/au0828-cards.c @@ -169,7 +169,9 @@ static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data) case 72231: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ case 72241: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */ case 72251: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */ - case 72261: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ + case 72261: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */ + case 72271: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */ + case 72281: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */ case 72301: /* WinTV-HVR850 (Retail, IR, ATSC and analog video */ case 72500: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM */ break; @@ -183,12 +185,11 @@ static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data) __func__, tv.model); } +void au0828_card_analog_fe_setup(struct au0828_dev *dev); + void au0828_card_setup(struct au0828_dev *dev) { static u8 eeprom[256]; - struct tuner_setup tun_setup; - struct v4l2_subdev *sd; - unsigned int mode_mask = T_ANALOG_TV; dprintk(1, "%s()\n", __func__); @@ -209,6 +210,16 @@ void au0828_card_setup(struct au0828_dev *dev) break; } + au0828_card_analog_fe_setup(dev); +} + +void au0828_card_analog_fe_setup(struct au0828_dev *dev) +{ +#ifdef CONFIG_VIDEO_AU0828_V4L2 + struct tuner_setup tun_setup; + struct v4l2_subdev *sd; + unsigned int mode_mask = T_ANALOG_TV; + if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) { /* Load the analog demodulator driver (note this would need to be abstracted out if we ever need to support a different @@ -234,6 +245,7 @@ void au0828_card_setup(struct au0828_dev *dev) v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup); } +#endif } /* @@ -333,6 +345,8 @@ struct usb_device_id au0828_usb_id_table[] = { .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, { USB_DEVICE(0x2040, 0x7213), .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { USB_DEVICE(0x2040, 0x7270), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, { }, }; diff --git a/drivers/media/usb/au0828/au0828-core.c b/drivers/media/usb/au0828/au0828-core.c index 745a80a798c8..1e6f40ef1c6b 100644 --- a/drivers/media/usb/au0828/au0828-core.c +++ b/drivers/media/usb/au0828/au0828-core.c @@ -134,13 +134,17 @@ static void au0828_usb_disconnect(struct usb_interface *interface) /* Digital TV */ au0828_dvb_unregister(dev); +#ifdef CONFIG_VIDEO_AU0828_V4L2 if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) au0828_analog_unregister(dev); +#endif /* I2C */ au0828_i2c_unregister(dev); +#ifdef CONFIG_VIDEO_AU0828_V4L2 v4l2_device_unregister(&dev->v4l2_dev); +#endif usb_set_intfdata(interface, NULL); @@ -155,7 +159,10 @@ static void au0828_usb_disconnect(struct usb_interface *interface) static int au0828_usb_probe(struct usb_interface *interface, const struct usb_device_id *id) { - int ifnum, retval; + int ifnum; +#ifdef CONFIG_VIDEO_AU0828_V4L2 + int retval; +#endif struct au0828_dev *dev; struct usb_device *usbdev = interface_to_usbdev(interface); @@ -194,6 +201,7 @@ static int au0828_usb_probe(struct usb_interface *interface, dev->usbdev = usbdev; dev->boardnr = id->driver_info; +#ifdef CONFIG_VIDEO_AU0828_V4L2 /* Create the v4l2_device */ retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev); if (retval) { @@ -203,6 +211,7 @@ static int au0828_usb_probe(struct usb_interface *interface, kfree(dev); return -EIO; } +#endif /* Power Up the bridge */ au0828_write(dev, REG_600, 1 << 4); @@ -216,9 +225,11 @@ static int au0828_usb_probe(struct usb_interface *interface, /* 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 /* Digital TV */ au0828_dvb_register(dev); diff --git a/drivers/media/usb/au0828/au0828-i2c.c b/drivers/media/usb/au0828/au0828-i2c.c index 4ded17fe1957..20d69b565255 100644 --- a/drivers/media/usb/au0828/au0828-i2c.c +++ b/drivers/media/usb/au0828/au0828-i2c.c @@ -378,7 +378,11 @@ int au0828_i2c_register(struct au0828_dev *dev) dev->i2c_adap.algo = &dev->i2c_algo; dev->i2c_adap.algo_data = dev; +#ifdef CONFIG_VIDEO_AU0828_V4L2 i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev); +#else + i2c_set_adapdata(&dev->i2c_adap, dev); +#endif i2c_add_adapter(&dev->i2c_adap); dev->i2c_client.adapter = &dev->i2c_adap; diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index 45387aab10c7..8b9e8268e911 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -304,7 +304,7 @@ static inline void buffer_filled(struct au0828_dev *dev, buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); dev->isoc_ctl.buf = NULL; @@ -321,7 +321,7 @@ static inline void vbi_buffer_filled(struct au0828_dev *dev, buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); dev->isoc_ctl.vbi_buf = NULL; diff --git a/drivers/media/usb/au0828/au0828.h b/drivers/media/usb/au0828/au0828.h index 66a56ef7bbe4..e579ff69ca4a 100644 --- a/drivers/media/usb/au0828/au0828.h +++ b/drivers/media/usb/au0828/au0828.h @@ -199,8 +199,10 @@ struct au0828_dev { struct au0828_dvb dvb; struct work_struct restart_streaming; +#ifdef CONFIG_VIDEO_AU0828_V4L2 /* Analog */ struct v4l2_device v4l2_dev; +#endif int users; unsigned int resources; /* resources in use */ struct video_device *vdev; diff --git a/drivers/media/usb/cpia2/cpia2_usb.c b/drivers/media/usb/cpia2/cpia2_usb.c index 95b5d6e7cdc4..be1719283609 100644 --- a/drivers/media/usb/cpia2/cpia2_usb.c +++ b/drivers/media/usb/cpia2/cpia2_usb.c @@ -328,7 +328,7 @@ static void cpia2_usb_complete(struct urb *urb) continue; } DBG("Start of frame pattern found\n"); - do_gettimeofday(&cam->workbuff->timestamp); + v4l2_get_timestamp(&cam->workbuff->timestamp); cam->workbuff->seq = cam->frame_count++; cam->workbuff->data[0] = 0xFF; cam->workbuff->data[1] = 0xD8; diff --git a/drivers/media/usb/cpia2/cpia2_v4l.c b/drivers/media/usb/cpia2/cpia2_v4l.c index aeb9d2275725..d5d42b6e94be 100644 --- a/drivers/media/usb/cpia2/cpia2_v4l.c +++ b/drivers/media/usb/cpia2/cpia2_v4l.c @@ -825,6 +825,8 @@ static int cpia2_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) else buf->flags = 0; + buf->flags |= V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + switch (cam->buffers[buf->index].status) { case FRAME_EMPTY: case FRAME_ERROR: @@ -943,7 +945,8 @@ static int cpia2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) buf->index = frame; buf->bytesused = cam->buffers[buf->index].length; - buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_DONE; + buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_DONE + | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; buf->field = V4L2_FIELD_NONE; buf->timestamp = cam->buffers[buf->index].timestamp; buf->sequence = cam->buffers[buf->index].seq; diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c index b024e5197a75..28688dbcb609 100644 --- a/drivers/media/usb/cx231xx/cx231xx-417.c +++ b/drivers/media/usb/cx231xx/cx231xx-417.c @@ -1291,7 +1291,7 @@ static void buffer_copy(struct cx231xx *dev, char *data, int len, struct urb *ur buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); list_del(&buf->vb.queue); wake_up(&buf->vb.done); dma_q->mpeg_buffer_completed = 0; @@ -1327,7 +1327,7 @@ static void buffer_filled(char *data, int len, struct urb *urb, memcpy(vbuf, data, len); buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); list_del(&buf->vb.queue); wake_up(&buf->vb.done); diff --git a/drivers/media/usb/cx231xx/cx231xx-vbi.c b/drivers/media/usb/cx231xx/cx231xx-vbi.c index ac7db52f404f..46e3892557c2 100644 --- a/drivers/media/usb/cx231xx/cx231xx-vbi.c +++ b/drivers/media/usb/cx231xx/cx231xx-vbi.c @@ -530,7 +530,7 @@ static inline void vbi_buffer_filled(struct cx231xx *dev, buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); dev->vbi_mode.bulk_ctl.buf = NULL; diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index fedf7852a355..239cb913be5c 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -235,7 +235,7 @@ static inline void buffer_filled(struct cx231xx *dev, cx231xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); if (dev->USE_ISO) dev->video_mode.isoc_ctl.buf = NULL; diff --git a/drivers/media/usb/dvb-usb-v2/it913x.c b/drivers/media/usb/dvb-usb-v2/it913x.c index 47204280b8b3..fbc0a84465eb 100644 --- a/drivers/media/usb/dvb-usb-v2/it913x.c +++ b/drivers/media/usb/dvb-usb-v2/it913x.c @@ -643,7 +643,8 @@ static int it913x_frontend_attach(struct dvb_usb_adapter *adap) struct it913x_state *st = d->priv; int ret = 0; u8 adap_addr = I2C_BASE_ADDR + (adap->id << 5); - u16 ep_size = adap->stream.buf_size / 4; + u16 ep_size = (adap->pid_filtering) ? TS_BUFFER_SIZE_PID / 4 : + TS_BUFFER_SIZE_MAX / 4; u8 pkt_size = 0x80; if (d->udev->speed != USB_SPEED_HIGH) diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c index 9382895b1b88..937c744217c8 100644 --- a/drivers/media/usb/dvb-usb/dw2102.c +++ b/drivers/media/usb/dvb-usb/dw2102.c @@ -80,6 +80,15 @@ #define DW2102_RC_QUERY (0x1a00) #define DW2102_LED_CTRL (0x1b00) +#define DW2101_FIRMWARE "dvb-usb-dw2101.fw" +#define DW2102_FIRMWARE "dvb-usb-dw2102.fw" +#define DW2104_FIRMWARE "dvb-usb-dw2104.fw" +#define DW3101_FIRMWARE "dvb-usb-dw3101.fw" +#define S630_FIRMWARE "dvb-usb-s630.fw" +#define S660_FIRMWARE "dvb-usb-s660.fw" +#define P1100_FIRMWARE "dvb-usb-p1100.fw" +#define P7500_FIRMWARE "dvb-usb-p7500.fw" + #define err_str "did not find the firmware file. (%s) " \ "Please see linux/Documentation/dvb/ for more details " \ "on firmware-problems." @@ -1478,13 +1487,12 @@ static int dw2102_load_firmware(struct usb_device *dev, u8 reset; u8 reset16[] = {0, 0, 0, 0, 0, 0, 0}; const struct firmware *fw; - const char *fw_2101 = "dvb-usb-dw2101.fw"; switch (dev->descriptor.idProduct) { case 0x2101: - ret = request_firmware(&fw, fw_2101, &dev->dev); + ret = request_firmware(&fw, DW2101_FIRMWARE, &dev->dev); if (ret != 0) { - err(err_str, fw_2101); + err(err_str, DW2101_FIRMWARE); return ret; } break; @@ -1586,7 +1594,7 @@ static int dw2102_load_firmware(struct usb_device *dev, static struct dvb_usb_device_properties dw2102_properties = { .caps = DVB_USB_IS_AN_I2C_ADAPTER, .usb_ctrl = DEVICE_SPECIFIC, - .firmware = "dvb-usb-dw2102.fw", + .firmware = DW2102_FIRMWARE, .no_reconnect = 1, .i2c_algo = &dw2102_serit_i2c_algo, @@ -1641,7 +1649,7 @@ static struct dvb_usb_device_properties dw2102_properties = { static struct dvb_usb_device_properties dw2104_properties = { .caps = DVB_USB_IS_AN_I2C_ADAPTER, .usb_ctrl = DEVICE_SPECIFIC, - .firmware = "dvb-usb-dw2104.fw", + .firmware = DW2104_FIRMWARE, .no_reconnect = 1, .i2c_algo = &dw2104_i2c_algo, @@ -1691,7 +1699,7 @@ static struct dvb_usb_device_properties dw2104_properties = { static struct dvb_usb_device_properties dw3101_properties = { .caps = DVB_USB_IS_AN_I2C_ADAPTER, .usb_ctrl = DEVICE_SPECIFIC, - .firmware = "dvb-usb-dw3101.fw", + .firmware = DW3101_FIRMWARE, .no_reconnect = 1, .i2c_algo = &dw3101_i2c_algo, @@ -1739,7 +1747,7 @@ static struct dvb_usb_device_properties s6x0_properties = { .caps = DVB_USB_IS_AN_I2C_ADAPTER, .usb_ctrl = DEVICE_SPECIFIC, .size_of_priv = sizeof(struct s6x0_state), - .firmware = "dvb-usb-s630.fw", + .firmware = S630_FIRMWARE, .no_reconnect = 1, .i2c_algo = &s6x0_i2c_algo, @@ -1879,7 +1887,7 @@ static int dw2102_probe(struct usb_interface *intf, return -ENOMEM; /* copy default structure */ /* fill only different fields */ - p1100->firmware = "dvb-usb-p1100.fw"; + p1100->firmware = P1100_FIRMWARE; p1100->devices[0] = d1100; p1100->rc.legacy.rc_map_table = rc_map_tbs_table; p1100->rc.legacy.rc_map_size = ARRAY_SIZE(rc_map_tbs_table); @@ -1891,7 +1899,7 @@ static int dw2102_probe(struct usb_interface *intf, kfree(p1100); return -ENOMEM; } - s660->firmware = "dvb-usb-s660.fw"; + s660->firmware = S660_FIRMWARE; s660->num_device_descs = 3; s660->devices[0] = d660; s660->devices[1] = d480_1; @@ -1905,7 +1913,7 @@ static int dw2102_probe(struct usb_interface *intf, kfree(s660); return -ENOMEM; } - p7500->firmware = "dvb-usb-p7500.fw"; + p7500->firmware = P7500_FIRMWARE; p7500->devices[0] = d7500; p7500->rc.legacy.rc_map_table = rc_map_tbs_table; p7500->rc.legacy.rc_map_size = ARRAY_SIZE(rc_map_tbs_table); @@ -1949,3 +1957,11 @@ MODULE_DESCRIPTION("Driver for DVBWorld DVB-S 2101, 2102, DVB-S2 2104," " Geniatech SU3000 devices"); MODULE_VERSION("0.1"); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(DW2101_FIRMWARE); +MODULE_FIRMWARE(DW2102_FIRMWARE); +MODULE_FIRMWARE(DW2104_FIRMWARE); +MODULE_FIRMWARE(DW3101_FIRMWARE); +MODULE_FIRMWARE(S630_FIRMWARE); +MODULE_FIRMWARE(S660_FIRMWARE); +MODULE_FIRMWARE(P1100_FIRMWARE); +MODULE_FIRMWARE(P7500_FIRMWARE); diff --git a/drivers/media/usb/em28xx/Kconfig b/drivers/media/usb/em28xx/Kconfig index 7a5bd61bd3bb..094c4ecf086d 100644 --- a/drivers/media/usb/em28xx/Kconfig +++ b/drivers/media/usb/em28xx/Kconfig @@ -34,6 +34,7 @@ config VIDEO_EM28XX_DVB tristate "DVB/ATSC Support for em28xx based TV cards" depends on VIDEO_EM28XX && DVB_CORE select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT + select DVB_LGDT3305 if MEDIA_SUBDRV_AUTOSELECT select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT select DVB_S921 if MEDIA_SUBDRV_AUTOSELECT @@ -43,6 +44,10 @@ config VIDEO_EM28XX_DVB select DVB_TDA18271C2DD if MEDIA_SUBDRV_AUTOSELECT select DVB_TDA10071 if MEDIA_SUBDRV_AUTOSELECT select DVB_A8293 if MEDIA_SUBDRV_AUTOSELECT + select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT + select DVB_S5H1409 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT select VIDEOBUF_DVB ---help--- This adds support for DVB cards based on the diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 619bffbab3bc..ad6c800d9a40 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -6,6 +6,7 @@ Markus Rechberger <mrechberger@gmail.com> Mauro Carvalho Chehab <mchehab@infradead.org> Sascha Sommer <saschasommer@freenet.de> + Copyright (C) 2012 Frank Schäfer <fschaefer.oss@googlemail.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 @@ -60,6 +61,11 @@ static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; module_param_array(card, int, NULL, 0444); MODULE_PARM_DESC(card, "card type"); +static int prefer_bulk = -1; +module_param(prefer_bulk, int, 0444); +MODULE_PARM_DESC(prefer_bulk, "prefer USB bulk transfers (-1 = auto, 0 = isoc, 1 = bulk)"); + + /* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS - 1 */ static unsigned long em28xx_devused; @@ -2951,6 +2957,8 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, int minor) { int retval; + static const char *default_chip_name = "em28xx"; + const char *chip_name = default_chip_name; dev->udev = udev; mutex_init(&dev->ctrl_urb_lock); @@ -2978,51 +2986,62 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, switch (dev->chip_id) { case CHIP_ID_EM2800: - em28xx_info("chip ID is em2800\n"); + chip_name = "em2800"; break; case CHIP_ID_EM2710: - em28xx_info("chip ID is em2710\n"); + chip_name = "em2710"; break; case CHIP_ID_EM2750: - em28xx_info("chip ID is em2750\n"); + chip_name = "em2750"; break; case CHIP_ID_EM2820: - em28xx_info("chip ID is em2820 (or em2710)\n"); + chip_name = "em2710/2820"; break; case CHIP_ID_EM2840: - em28xx_info("chip ID is em2840\n"); + chip_name = "em2840"; break; case CHIP_ID_EM2860: - em28xx_info("chip ID is em2860\n"); + chip_name = "em2860"; break; case CHIP_ID_EM2870: - em28xx_info("chip ID is em2870\n"); + chip_name = "em2870"; dev->wait_after_write = 0; break; case CHIP_ID_EM2874: - em28xx_info("chip ID is em2874\n"); + chip_name = "em2874"; dev->reg_gpio_num = EM2874_R80_GPIO; dev->wait_after_write = 0; break; case CHIP_ID_EM28174: - em28xx_info("chip ID is em28174\n"); + chip_name = "em28174"; dev->reg_gpio_num = EM2874_R80_GPIO; dev->wait_after_write = 0; break; case CHIP_ID_EM2883: - em28xx_info("chip ID is em2882/em2883\n"); + chip_name = "em2882/3"; dev->wait_after_write = 0; break; case CHIP_ID_EM2884: - em28xx_info("chip ID is em2884\n"); + chip_name = "em2884"; dev->reg_gpio_num = EM2874_R80_GPIO; dev->wait_after_write = 0; break; default: - em28xx_info("em28xx chip ID = %d\n", dev->chip_id); + printk(KERN_INFO DRIVER_NAME + ": unknown em28xx chip ID (%d)\n", dev->chip_id); } } + if (chip_name != default_chip_name) + printk(KERN_INFO DRIVER_NAME + ": chip ID is %s\n", chip_name); + + /* + * For em2820/em2710, the name may change latter, after checking + * if the device has a sensor (so, it is em2710) or not. + */ + snprintf(dev->name, sizeof(dev->name), "%s #%d", chip_name, dev->devno); + if (dev->is_audio_only) { retval = em28xx_audio_setup(dev); if (retval) @@ -3039,6 +3058,14 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, em28xx_pre_card_setup(dev); + if (dev->chip_id == CHIP_ID_EM2820) { + if (dev->board.is_webcam) + chip_name = "em2710"; + else + chip_name = "em2820"; + snprintf(dev->name, sizeof(dev->name), "%s #%d", chip_name, dev->devno); + } + if (!dev->board.is_em2800) { /* Resets I2C speed */ retval = em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->board.i2c_speed); @@ -3143,7 +3170,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, struct em28xx *dev = NULL; int retval; bool has_audio = false, has_video = false, has_dvb = false; - int i, nr; + int i, nr, try_bulk; const int ifnum = interface->altsetting[0].desc.bInterfaceNumber; char *speed; @@ -3183,9 +3210,10 @@ static int em28xx_usb_probe(struct usb_interface *interface, } /* compute alternate max packet sizes */ - dev->alt_max_pkt_size = kmalloc(sizeof(dev->alt_max_pkt_size[0]) * + dev->alt_max_pkt_size_isoc = + kmalloc(sizeof(dev->alt_max_pkt_size_isoc[0]) * interface->num_altsetting, GFP_KERNEL); - if (dev->alt_max_pkt_size == NULL) { + if (dev->alt_max_pkt_size_isoc == NULL) { em28xx_errdev("out of memory!\n"); kfree(dev); retval = -ENOMEM; @@ -3208,25 +3236,67 @@ static int em28xx_usb_probe(struct usb_interface *interface, if (udev->speed == USB_SPEED_HIGH) size = size * hb_mult(sizedescr); - if (usb_endpoint_xfer_isoc(e) && - usb_endpoint_dir_in(e)) { + if (usb_endpoint_dir_in(e)) { switch (e->bEndpointAddress) { - case EM28XX_EP_AUDIO: - has_audio = true; - break; - case EM28XX_EP_ANALOG: + case 0x82: has_video = true; - dev->alt_max_pkt_size[i] = size; + if (usb_endpoint_xfer_isoc(e)) { + dev->analog_ep_isoc = + e->bEndpointAddress; + dev->alt_max_pkt_size_isoc[i] = size; + } else if (usb_endpoint_xfer_bulk(e)) { + dev->analog_ep_bulk = + e->bEndpointAddress; + } + break; + case 0x83: + if (usb_endpoint_xfer_isoc(e)) { + has_audio = true; + } else { + printk(KERN_INFO DRIVER_NAME + ": error: skipping audio endpoint 0x83, because it uses bulk transfers !\n"); + } break; - case EM28XX_EP_DIGITAL: - has_dvb = true; - if (size > dev->dvb_max_pkt_size) { - dev->dvb_max_pkt_size = size; - dev->dvb_alt = i; + case 0x84: + if (has_video && + (usb_endpoint_xfer_bulk(e))) { + dev->analog_ep_bulk = + e->bEndpointAddress; + } else { + has_dvb = true; + if (usb_endpoint_xfer_isoc(e)) { + dev->dvb_ep_isoc = e->bEndpointAddress; + if (size > dev->dvb_max_pkt_size_isoc) { + dev->dvb_max_pkt_size_isoc = size; + dev->dvb_alt_isoc = i; + } + } else { + dev->dvb_ep_bulk = e->bEndpointAddress; + } } break; } } + /* NOTE: + * Old logic with support for isoc transfers only was: + * 0x82 isoc => analog + * 0x83 isoc => audio + * 0x84 isoc => digital + * + * New logic with support for bulk transfers + * 0x82 isoc => analog + * 0x82 bulk => analog + * 0x83 isoc* => audio + * 0x84 isoc => digital + * 0x84 bulk => analog or digital** + * (*: audio should always be isoc) + * (**: analog, if ep 0x82 is isoc, otherwise digital) + * + * The new logic preserves backwards compatibility and + * reflects the endpoint configurations we have seen + * so far. But there might be devices for which this + * logic is not sufficient... + */ } } @@ -3261,19 +3331,6 @@ static int em28xx_usb_probe(struct usb_interface *interface, ifnum, interface->altsetting->desc.bInterfaceNumber); - if (has_audio) - printk(KERN_INFO DRIVER_NAME - ": Audio Vendor Class interface %i found\n", - ifnum); - if (has_video) - printk(KERN_INFO DRIVER_NAME - ": Video interface %i found\n", - ifnum); - if (has_dvb) - printk(KERN_INFO DRIVER_NAME - ": DVB interface %i found\n", - ifnum); - /* * Make sure we have 480 Mbps of bandwidth, otherwise things like * video stream wouldn't likely work, since 12 Mbps is generally @@ -3287,7 +3344,6 @@ static int em28xx_usb_probe(struct usb_interface *interface, goto err_free; } - snprintf(dev->name, sizeof(dev->name), "em28xx #%d", nr); dev->devno = nr; dev->model = id->driver_info; dev->alt = -1; @@ -3304,6 +3360,24 @@ static int em28xx_usb_probe(struct usb_interface *interface, } } + if (has_audio) + printk(KERN_INFO DRIVER_NAME + ": Audio interface %i found %s\n", + ifnum, + dev->has_audio_class ? "(USB Audio Class)" : "(Vendor Class)"); + if (has_video) + printk(KERN_INFO DRIVER_NAME + ": Video interface %i found:%s%s\n", + ifnum, + dev->analog_ep_bulk ? " bulk" : "", + dev->analog_ep_isoc ? " isoc" : ""); + if (has_dvb) + printk(KERN_INFO DRIVER_NAME + ": DVB interface %i found:%s%s\n", + ifnum, + dev->dvb_ep_bulk ? " bulk" : "", + dev->dvb_ep_isoc ? " isoc" : ""); + dev->num_alt = interface->num_altsetting; if ((unsigned)card[nr] < em28xx_bcount) @@ -3320,13 +3394,46 @@ static int em28xx_usb_probe(struct usb_interface *interface, goto unlock_and_free; } + if (prefer_bulk < 0) { + if (dev->board.is_webcam) + try_bulk = 1; + else + try_bulk = 0; + } else { + try_bulk = prefer_bulk > 0; + } + + /* Select USB transfer types to use */ + if (has_video) { + if (!dev->analog_ep_isoc || (try_bulk && dev->analog_ep_bulk)) + dev->analog_xfer_bulk = 1; + em28xx_info("analog set to %s mode.\n", + dev->analog_xfer_bulk ? "bulk" : "isoc"); + } if (has_dvb) { - /* pre-allocate DVB isoc transfer buffers */ - retval = em28xx_alloc_isoc(dev, EM28XX_DIGITAL_MODE, - EM28XX_DVB_MAX_PACKETS, - EM28XX_DVB_NUM_BUFS, - dev->dvb_max_pkt_size); + if (!dev->dvb_ep_isoc || (try_bulk && dev->dvb_ep_bulk)) + dev->dvb_xfer_bulk = 1; + + em28xx_info("dvb set to %s mode.\n", + dev->dvb_xfer_bulk ? "bulk" : "isoc"); + + /* pre-allocate DVB usb transfer buffers */ + if (dev->dvb_xfer_bulk) { + retval = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE, + dev->dvb_xfer_bulk, + EM28XX_DVB_NUM_BUFS, + 512, + EM28XX_DVB_BULK_PACKET_MULTIPLIER); + } else { + retval = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE, + dev->dvb_xfer_bulk, + EM28XX_DVB_NUM_BUFS, + dev->dvb_max_pkt_size_isoc, + EM28XX_DVB_NUM_ISOC_PACKETS); + } if (retval) { + printk(DRIVER_NAME + ": Failed to pre-allocate USB transfer buffers for DVB.\n"); goto unlock_and_free; } } @@ -3344,7 +3451,7 @@ unlock_and_free: mutex_unlock(&dev->lock); err_free: - kfree(dev->alt_max_pkt_size); + kfree(dev->alt_max_pkt_size_isoc); kfree(dev); err: @@ -3394,7 +3501,7 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) video_device_node_name(dev->vdev)); dev->state |= DEV_MISCONFIGURED; - em28xx_uninit_isoc(dev, dev->mode); + em28xx_uninit_usb_xfer(dev, dev->mode); dev->state |= DEV_DISCONNECTED; } else { dev->state |= DEV_DISCONNECTED; @@ -3402,14 +3509,14 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) } /* free DVB isoc buffers */ - em28xx_uninit_isoc(dev, EM28XX_DIGITAL_MODE); + em28xx_uninit_usb_xfer(dev, EM28XX_DIGITAL_MODE); mutex_unlock(&dev->lock); em28xx_close_extension(dev); if (!dev->users) { - kfree(dev->alt_max_pkt_size); + kfree(dev->alt_max_pkt_size_isoc); kfree(dev); } } diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index bed07a6c33f8..b10d959fefe5 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -5,6 +5,7 @@ Markus Rechberger <mrechberger@gmail.com> Mauro Carvalho Chehab <mchehab@infradead.org> Sascha Sommer <saschasommer@freenet.de> + Copyright (C) 2012 Frank Schäfer <fschaefer.oss@googlemail.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 @@ -804,21 +805,23 @@ int em28xx_resolution_set(struct em28xx *dev) return em28xx_scaler_set(dev, dev->hscale, dev->vscale); } +/* Set USB alternate setting for analog video */ int em28xx_set_alternate(struct em28xx *dev) { int errCode, prev_alt = dev->alt; int i; unsigned int min_pkt_size = dev->width * 2 + 4; - /* - * alt = 0 is used only for control messages, so, only values - * greater than 0 can be used for streaming. - */ - if (alt && alt < dev->num_alt) { + /* NOTE: for isoc transfers, only alt settings > 0 are allowed + for bulk transfers, use alt=0 as default value */ + dev->alt = 0; + if ((alt > 0) && (alt < dev->num_alt)) { em28xx_coredbg("alternate forced to %d\n", dev->alt); dev->alt = alt; goto set_alt; } + if (dev->analog_xfer_bulk) + goto set_alt; /* When image size is bigger than a certain value, the frame size should be increased, otherwise, only @@ -829,22 +832,29 @@ int em28xx_set_alternate(struct em28xx *dev) for (i = 0; i < dev->num_alt; i++) { /* stop when the selected alt setting offers enough bandwidth */ - if (dev->alt_max_pkt_size[i] >= min_pkt_size) { + if (dev->alt_max_pkt_size_isoc[i] >= min_pkt_size) { dev->alt = i; break; /* otherwise make sure that we end up with the maximum bandwidth because the min_pkt_size equation might be wrong... */ - } else if (dev->alt_max_pkt_size[i] > - dev->alt_max_pkt_size[dev->alt]) + } else if (dev->alt_max_pkt_size_isoc[i] > + dev->alt_max_pkt_size_isoc[dev->alt]) dev->alt = i; } set_alt: if (dev->alt != prev_alt) { - em28xx_coredbg("minimum isoc packet size: %u (alt=%d)\n", - min_pkt_size, dev->alt); - dev->max_pkt_size = dev->alt_max_pkt_size[dev->alt]; + if (dev->analog_xfer_bulk) { + dev->max_pkt_size = 512; /* USB 2.0 spec */ + dev->packet_multiplier = EM28XX_BULK_PACKET_MULTIPLIER; + } else { /* isoc */ + em28xx_coredbg("minimum isoc packet size: %u (alt=%d)\n", + min_pkt_size, dev->alt); + dev->max_pkt_size = + dev->alt_max_pkt_size_isoc[dev->alt]; + dev->packet_multiplier = EM28XX_NUM_ISOC_PACKETS; + } em28xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n", dev->alt, dev->max_pkt_size); errCode = usb_set_interface(dev->udev, 0, dev->alt); @@ -919,7 +929,7 @@ EXPORT_SYMBOL_GPL(em28xx_set_mode); ------------------------------------------------------------------*/ /* - * IRQ callback, called by URB callback + * URB completion handler for isoc/bulk transfers */ static void em28xx_irq_callback(struct urb *urb) { @@ -941,11 +951,12 @@ static void em28xx_irq_callback(struct urb *urb) /* Copy data from URB */ spin_lock(&dev->slock); - dev->isoc_ctl.isoc_copy(dev, urb); + dev->usb_ctl.urb_data_copy(dev, urb); spin_unlock(&dev->slock); /* Reset urb buffers */ for (i = 0; i < urb->number_of_packets; i++) { + /* isoc only (bulk: number_of_packets = 0) */ urb->iso_frame_desc[i].status = 0; urb->iso_frame_desc[i].actual_length = 0; } @@ -961,49 +972,50 @@ static void em28xx_irq_callback(struct urb *urb) /* * Stop and Deallocate URBs */ -void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode) +void em28xx_uninit_usb_xfer(struct em28xx *dev, enum em28xx_mode mode) { struct urb *urb; - struct em28xx_usb_isoc_bufs *isoc_bufs; + struct em28xx_usb_bufs *usb_bufs; int i; - em28xx_isocdbg("em28xx: called em28xx_uninit_isoc in mode %d\n", mode); + em28xx_isocdbg("em28xx: called em28xx_uninit_usb_xfer in mode %d\n", + mode); if (mode == EM28XX_DIGITAL_MODE) - isoc_bufs = &dev->isoc_ctl.digital_bufs; + usb_bufs = &dev->usb_ctl.digital_bufs; else - isoc_bufs = &dev->isoc_ctl.analog_bufs; + usb_bufs = &dev->usb_ctl.analog_bufs; - for (i = 0; i < isoc_bufs->num_bufs; i++) { - urb = isoc_bufs->urb[i]; + for (i = 0; i < usb_bufs->num_bufs; i++) { + urb = usb_bufs->urb[i]; if (urb) { if (!irqs_disabled()) usb_kill_urb(urb); else usb_unlink_urb(urb); - if (isoc_bufs->transfer_buffer[i]) { + if (usb_bufs->transfer_buffer[i]) { usb_free_coherent(dev->udev, urb->transfer_buffer_length, - isoc_bufs->transfer_buffer[i], + usb_bufs->transfer_buffer[i], urb->transfer_dma); } usb_free_urb(urb); - isoc_bufs->urb[i] = NULL; + usb_bufs->urb[i] = NULL; } - isoc_bufs->transfer_buffer[i] = NULL; + usb_bufs->transfer_buffer[i] = NULL; } - kfree(isoc_bufs->urb); - kfree(isoc_bufs->transfer_buffer); + kfree(usb_bufs->urb); + kfree(usb_bufs->transfer_buffer); - isoc_bufs->urb = NULL; - isoc_bufs->transfer_buffer = NULL; - isoc_bufs->num_bufs = 0; + usb_bufs->urb = NULL; + usb_bufs->transfer_buffer = NULL; + usb_bufs->num_bufs = 0; em28xx_capture_start(dev, 0); } -EXPORT_SYMBOL_GPL(em28xx_uninit_isoc); +EXPORT_SYMBOL_GPL(em28xx_uninit_usb_xfer); /* * Stop URBs @@ -1012,7 +1024,7 @@ void em28xx_stop_urbs(struct em28xx *dev) { int i; struct urb *urb; - struct em28xx_usb_isoc_bufs *isoc_bufs = &dev->isoc_ctl.digital_bufs; + struct em28xx_usb_bufs *isoc_bufs = &dev->usb_ctl.digital_bufs; em28xx_isocdbg("em28xx: called em28xx_stop_urbs\n"); @@ -1033,10 +1045,10 @@ EXPORT_SYMBOL_GPL(em28xx_stop_urbs); /* * Allocate URBs */ -int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, - int max_packets, int num_bufs, int max_pkt_size) +int em28xx_alloc_urbs(struct em28xx *dev, enum em28xx_mode mode, int xfer_bulk, + int num_bufs, int max_pkt_size, int packet_multiplier) { - struct em28xx_usb_isoc_bufs *isoc_bufs; + struct em28xx_usb_bufs *usb_bufs; int i; int sb_size, pipe; struct urb *urb; @@ -1044,140 +1056,180 @@ int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, em28xx_isocdbg("em28xx: called em28xx_alloc_isoc in mode %d\n", mode); - if (mode == EM28XX_DIGITAL_MODE) - isoc_bufs = &dev->isoc_ctl.digital_bufs; - else - isoc_bufs = &dev->isoc_ctl.analog_bufs; + /* Check mode and if we have an endpoint for the selected + transfer type, select buffer */ + if (mode == EM28XX_DIGITAL_MODE) { + if ((xfer_bulk && !dev->dvb_ep_bulk) || + (!xfer_bulk && !dev->dvb_ep_isoc)) { + em28xx_errdev("no endpoint for DVB mode and transfer type %d\n", + xfer_bulk > 0); + return -EINVAL; + } + usb_bufs = &dev->usb_ctl.digital_bufs; + } else if (mode == EM28XX_ANALOG_MODE) { + if ((xfer_bulk && !dev->analog_ep_bulk) || + (!xfer_bulk && !dev->analog_ep_isoc)) { + em28xx_errdev("no endpoint for analog mode and transfer type %d\n", + xfer_bulk > 0); + return -EINVAL; + } + usb_bufs = &dev->usb_ctl.analog_bufs; + } else { + em28xx_errdev("invalid mode selected\n"); + return -EINVAL; + } /* De-allocates all pending stuff */ - em28xx_uninit_isoc(dev, mode); + em28xx_uninit_usb_xfer(dev, mode); - isoc_bufs->num_bufs = num_bufs; + usb_bufs->num_bufs = num_bufs; - isoc_bufs->urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); - if (!isoc_bufs->urb) { + usb_bufs->urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); + if (!usb_bufs->urb) { em28xx_errdev("cannot alloc memory for usb buffers\n"); return -ENOMEM; } - isoc_bufs->transfer_buffer = kzalloc(sizeof(void *)*num_bufs, + usb_bufs->transfer_buffer = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); - if (!isoc_bufs->transfer_buffer) { + if (!usb_bufs->transfer_buffer) { em28xx_errdev("cannot allocate memory for usb transfer\n"); - kfree(isoc_bufs->urb); + kfree(usb_bufs->urb); return -ENOMEM; } - isoc_bufs->max_pkt_size = max_pkt_size; - isoc_bufs->num_packets = max_packets; - dev->isoc_ctl.vid_buf = NULL; - dev->isoc_ctl.vbi_buf = NULL; + usb_bufs->max_pkt_size = max_pkt_size; + if (xfer_bulk) + usb_bufs->num_packets = 0; + else + usb_bufs->num_packets = packet_multiplier; + dev->usb_ctl.vid_buf = NULL; + dev->usb_ctl.vbi_buf = NULL; - sb_size = isoc_bufs->num_packets * isoc_bufs->max_pkt_size; + sb_size = packet_multiplier * usb_bufs->max_pkt_size; /* allocate urbs and transfer buffers */ - for (i = 0; i < isoc_bufs->num_bufs; i++) { - urb = usb_alloc_urb(isoc_bufs->num_packets, GFP_KERNEL); + for (i = 0; i < usb_bufs->num_bufs; i++) { + urb = usb_alloc_urb(usb_bufs->num_packets, GFP_KERNEL); if (!urb) { - em28xx_err("cannot alloc isoc_ctl.urb %i\n", i); - em28xx_uninit_isoc(dev, mode); + em28xx_err("cannot alloc usb_ctl.urb %i\n", i); + em28xx_uninit_usb_xfer(dev, mode); return -ENOMEM; } - isoc_bufs->urb[i] = urb; + usb_bufs->urb[i] = urb; - isoc_bufs->transfer_buffer[i] = usb_alloc_coherent(dev->udev, + usb_bufs->transfer_buffer[i] = usb_alloc_coherent(dev->udev, sb_size, GFP_KERNEL, &urb->transfer_dma); - if (!isoc_bufs->transfer_buffer[i]) { + if (!usb_bufs->transfer_buffer[i]) { em28xx_err("unable to allocate %i bytes for transfer" " buffer %i%s\n", sb_size, i, in_interrupt() ? " while in int" : ""); - em28xx_uninit_isoc(dev, mode); + em28xx_uninit_usb_xfer(dev, mode); return -ENOMEM; } - memset(isoc_bufs->transfer_buffer[i], 0, sb_size); - - /* FIXME: this is a hack - should be - 'desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK' - should also be using 'desc.bInterval' - */ - pipe = usb_rcvisocpipe(dev->udev, - mode == EM28XX_ANALOG_MODE ? - EM28XX_EP_ANALOG : EM28XX_EP_DIGITAL); - - usb_fill_int_urb(urb, dev->udev, pipe, - isoc_bufs->transfer_buffer[i], sb_size, - em28xx_irq_callback, dev, 1); - - urb->number_of_packets = isoc_bufs->num_packets; - urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; - - k = 0; - for (j = 0; j < isoc_bufs->num_packets; j++) { - urb->iso_frame_desc[j].offset = k; - urb->iso_frame_desc[j].length = - isoc_bufs->max_pkt_size; - k += isoc_bufs->max_pkt_size; + memset(usb_bufs->transfer_buffer[i], 0, sb_size); + + if (xfer_bulk) { /* bulk */ + pipe = usb_rcvbulkpipe(dev->udev, + mode == EM28XX_ANALOG_MODE ? + dev->analog_ep_bulk : + dev->dvb_ep_bulk); + usb_fill_bulk_urb(urb, dev->udev, pipe, + usb_bufs->transfer_buffer[i], sb_size, + em28xx_irq_callback, dev); + urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + } else { /* isoc */ + pipe = usb_rcvisocpipe(dev->udev, + mode == EM28XX_ANALOG_MODE ? + dev->analog_ep_isoc : + dev->dvb_ep_isoc); + usb_fill_int_urb(urb, dev->udev, pipe, + usb_bufs->transfer_buffer[i], sb_size, + em28xx_irq_callback, dev, 1); + urb->transfer_flags = URB_ISO_ASAP | + URB_NO_TRANSFER_DMA_MAP; + k = 0; + for (j = 0; j < usb_bufs->num_packets; j++) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = + usb_bufs->max_pkt_size; + k += usb_bufs->max_pkt_size; + } } + + urb->number_of_packets = usb_bufs->num_packets; } return 0; } -EXPORT_SYMBOL_GPL(em28xx_alloc_isoc); +EXPORT_SYMBOL_GPL(em28xx_alloc_urbs); /* * Allocate URBs and start IRQ */ -int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, - int max_packets, int num_bufs, int max_pkt_size, - int (*isoc_copy) (struct em28xx *dev, struct urb *urb)) +int em28xx_init_usb_xfer(struct em28xx *dev, enum em28xx_mode mode, + int xfer_bulk, int num_bufs, int max_pkt_size, + int packet_multiplier, + int (*urb_data_copy) (struct em28xx *dev, struct urb *urb)) { struct em28xx_dmaqueue *dma_q = &dev->vidq; struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; - struct em28xx_usb_isoc_bufs *isoc_bufs; + struct em28xx_usb_bufs *usb_bufs; int i; int rc; int alloc; - em28xx_isocdbg("em28xx: called em28xx_init_isoc in mode %d\n", mode); + em28xx_isocdbg("em28xx: called em28xx_init_usb_xfer in mode %d\n", + mode); - dev->isoc_ctl.isoc_copy = isoc_copy; + dev->usb_ctl.urb_data_copy = urb_data_copy; if (mode == EM28XX_DIGITAL_MODE) { - isoc_bufs = &dev->isoc_ctl.digital_bufs; - /* no need to free/alloc isoc buffers in digital mode */ + usb_bufs = &dev->usb_ctl.digital_bufs; + /* no need to free/alloc usb buffers in digital mode */ alloc = 0; } else { - isoc_bufs = &dev->isoc_ctl.analog_bufs; + usb_bufs = &dev->usb_ctl.analog_bufs; alloc = 1; } if (alloc) { - rc = em28xx_alloc_isoc(dev, mode, max_packets, - num_bufs, max_pkt_size); + rc = em28xx_alloc_urbs(dev, mode, xfer_bulk, num_bufs, + max_pkt_size, packet_multiplier); if (rc) return rc; } + if (xfer_bulk) { + rc = usb_clear_halt(dev->udev, usb_bufs->urb[0]->pipe); + if (rc < 0) { + em28xx_err("failed to clear USB bulk endpoint stall/halt condition (error=%i)\n", + rc); + em28xx_uninit_usb_xfer(dev, mode); + return rc; + } + } + init_waitqueue_head(&dma_q->wq); init_waitqueue_head(&vbi_dma_q->wq); em28xx_capture_start(dev, 1); /* submit urbs and enables IRQ */ - for (i = 0; i < isoc_bufs->num_bufs; i++) { - rc = usb_submit_urb(isoc_bufs->urb[i], GFP_ATOMIC); + for (i = 0; i < usb_bufs->num_bufs; i++) { + rc = usb_submit_urb(usb_bufs->urb[i], GFP_ATOMIC); if (rc) { em28xx_err("submit of urb %i failed (error=%i)\n", i, rc); - em28xx_uninit_isoc(dev, mode); + em28xx_uninit_usb_xfer(dev, mode); return rc; } } return 0; } -EXPORT_SYMBOL_GPL(em28xx_init_isoc); +EXPORT_SYMBOL_GPL(em28xx_init_usb_xfer); /* * em28xx_wake_i2c() diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 63f2e7070c00..a70b19e07e37 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -10,6 +10,8 @@ (c) 2008 Aidan Thornton <makosoft@googlemail.com> + (c) 2012 Frank Schäfer <fschaefer.oss@googlemail.com> + Based on cx88-dvb, saa7134-dvb and videobuf-dvb originally written by: (c) 2004, 2005 Chris Pascoe <c.pascoe@itee.uq.edu.au> (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] @@ -124,9 +126,9 @@ static inline void print_err_status(struct em28xx *dev, } } -static inline int em28xx_dvb_isoc_copy(struct em28xx *dev, struct urb *urb) +static inline int em28xx_dvb_urb_data_copy(struct em28xx *dev, struct urb *urb) { - int i; + int xfer_bulk, num_packets, i; if (!dev) return 0; @@ -134,24 +136,37 @@ static inline int em28xx_dvb_isoc_copy(struct em28xx *dev, struct urb *urb) if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) return 0; - if (urb->status < 0) { + if (urb->status < 0) print_err_status(dev, -1, urb->status); - if (urb->status == -ENOENT) - return 0; - } - for (i = 0; i < urb->number_of_packets; i++) { - int status = urb->iso_frame_desc[i].status; + xfer_bulk = usb_pipebulk(urb->pipe); - if (status < 0) { - print_err_status(dev, i, status); - if (urb->iso_frame_desc[i].status != -EPROTO) - continue; - } + if (xfer_bulk) /* bulk */ + num_packets = 1; + else /* isoc */ + num_packets = urb->number_of_packets; - dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer + - urb->iso_frame_desc[i].offset, - urb->iso_frame_desc[i].actual_length); + for (i = 0; i < num_packets; i++) { + if (xfer_bulk) { + if (urb->status < 0) { + print_err_status(dev, i, urb->status); + if (urb->status != -EPROTO) + continue; + } + dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer, + urb->actual_length); + } else { + if (urb->iso_frame_desc[i].status < 0) { + print_err_status(dev, i, + urb->iso_frame_desc[i].status); + if (urb->iso_frame_desc[i].status != -EPROTO) + continue; + } + dvb_dmx_swfilter(&dev->dvb->demux, + urb->transfer_buffer + + urb->iso_frame_desc[i].offset, + urb->iso_frame_desc[i].actual_length); + } } return 0; @@ -161,24 +176,40 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb) { int rc; struct em28xx *dev = dvb->adapter.priv; - int max_dvb_packet_size; + int dvb_max_packet_size, packet_multiplier, dvb_alt; + + if (dev->dvb_xfer_bulk) { + if (!dev->dvb_ep_bulk) + return -ENODEV; + dvb_max_packet_size = 512; /* USB 2.0 spec */ + packet_multiplier = EM28XX_DVB_BULK_PACKET_MULTIPLIER; + dvb_alt = 0; + } else { /* isoc */ + if (!dev->dvb_ep_isoc) + return -ENODEV; + dvb_max_packet_size = dev->dvb_max_pkt_size_isoc; + if (dvb_max_packet_size < 0) + return dvb_max_packet_size; + packet_multiplier = EM28XX_DVB_NUM_ISOC_PACKETS; + dvb_alt = dev->dvb_alt_isoc; + } - usb_set_interface(dev->udev, 0, dev->dvb_alt); + usb_set_interface(dev->udev, 0, dvb_alt); rc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); if (rc < 0) return rc; - max_dvb_packet_size = dev->dvb_max_pkt_size; - if (max_dvb_packet_size < 0) - return max_dvb_packet_size; dprintk(1, "Using %d buffers each with %d x %d bytes\n", EM28XX_DVB_NUM_BUFS, - EM28XX_DVB_MAX_PACKETS, - max_dvb_packet_size); - - return em28xx_init_isoc(dev, EM28XX_DIGITAL_MODE, - EM28XX_DVB_MAX_PACKETS, EM28XX_DVB_NUM_BUFS, - max_dvb_packet_size, em28xx_dvb_isoc_copy); + packet_multiplier, + dvb_max_packet_size); + + return em28xx_init_usb_xfer(dev, EM28XX_DIGITAL_MODE, + dev->dvb_xfer_bulk, + EM28XX_DVB_NUM_BUFS, + dvb_max_packet_size, + packet_multiplier, + em28xx_dvb_urb_data_copy); } static int em28xx_stop_streaming(struct em28xx_dvb *dvb) @@ -714,7 +745,8 @@ static struct tda18271_config em28xx_cxd2820r_tda18271_config = { }; static const struct tda10071_config em28xx_tda10071_config = { - .i2c_address = 0x55, /* (0xaa >> 1) */ + .demod_i2c_addr = 0x55, /* (0xaa >> 1) */ + .tuner_i2c_addr = 0x14, .i2c_wr_max = 64, .ts_mode = TDA10071_TS_SERIAL, .spec_inv = 0, diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index 1683bd9d51ee..44533e4574ff 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -53,12 +53,11 @@ do { \ * em2800_i2c_send_max4() * send up to 4 bytes to the i2c device */ -static int em2800_i2c_send_max4(struct em28xx *dev, unsigned char addr, - char *buf, int len) +static int em2800_i2c_send_max4(struct em28xx *dev, u8 addr, u8 *buf, u16 len) { int ret; int write_timeout; - unsigned char b2[6]; + u8 b2[6]; BUG_ON(len < 1 || len > 4); b2[5] = 0x80 + len - 1; b2[4] = addr; @@ -89,15 +88,13 @@ static int em2800_i2c_send_max4(struct em28xx *dev, unsigned char addr, /* * em2800_i2c_send_bytes() */ -static int em2800_i2c_send_bytes(void *data, unsigned char addr, char *buf, - short len) +static int em2800_i2c_send_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) { - char *bufPtr = buf; + u8 *bufPtr = buf; int ret; int wrcount = 0; int count; int maxLen = 4; - struct em28xx *dev = (struct em28xx *)data; while (len > 0) { count = (len > maxLen) ? maxLen : len; ret = em2800_i2c_send_max4(dev, addr, bufPtr, count); @@ -115,9 +112,9 @@ static int em2800_i2c_send_bytes(void *data, unsigned char addr, char *buf, * em2800_i2c_check_for_device() * check if there is a i2c_device at the supplied address */ -static int em2800_i2c_check_for_device(struct em28xx *dev, unsigned char addr) +static int em2800_i2c_check_for_device(struct em28xx *dev, u8 addr) { - char msg; + u8 msg; int ret; int write_timeout; msg = addr; @@ -150,8 +147,7 @@ static int em2800_i2c_check_for_device(struct em28xx *dev, unsigned char addr) * em2800_i2c_recv_bytes() * read from the i2c device */ -static int em2800_i2c_recv_bytes(struct em28xx *dev, unsigned char addr, - char *buf, int len) +static int em2800_i2c_recv_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) { int ret; /* check for the device and set i2c read address */ @@ -174,11 +170,10 @@ static int em2800_i2c_recv_bytes(struct em28xx *dev, unsigned char addr, /* * em28xx_i2c_send_bytes() */ -static int em28xx_i2c_send_bytes(void *data, unsigned char addr, char *buf, - short len, int stop) +static int em28xx_i2c_send_bytes(struct em28xx *dev, u16 addr, u8 *buf, + u16 len, int stop) { int wrcount = 0; - struct em28xx *dev = (struct em28xx *)data; int write_timeout, ret; wrcount = dev->em28xx_write_regs_req(dev, stop ? 2 : 3, addr, buf, len); @@ -199,8 +194,7 @@ static int em28xx_i2c_send_bytes(void *data, unsigned char addr, char *buf, * em28xx_i2c_recv_bytes() * read a byte from the i2c device */ -static int em28xx_i2c_recv_bytes(struct em28xx *dev, unsigned char addr, - char *buf, int len) +static int em28xx_i2c_recv_bytes(struct em28xx *dev, u16 addr, u8 *buf, u16 len) { int ret; ret = dev->em28xx_read_reg_req_len(dev, 2, addr, buf, len); @@ -217,7 +211,7 @@ static int em28xx_i2c_recv_bytes(struct em28xx *dev, unsigned char addr, * em28xx_i2c_check_for_device() * check if there is a i2c_device at the supplied address */ -static int em28xx_i2c_check_for_device(struct em28xx *dev, unsigned char addr) +static int em28xx_i2c_check_for_device(struct em28xx *dev, u16 addr) { int ret; diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 660bf803c9e4..3598221378ac 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -57,8 +57,8 @@ MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]"); struct em28xx_ir_poll_result { unsigned int toggle_bit:1; unsigned int read_count:7; - u8 rc_address; - u8 rc_data[4]; /* 1 byte on em2860/2880, 4 on em2874 */ + + u32 scancode; }; struct em28xx_IR { @@ -72,6 +72,7 @@ struct em28xx_IR { struct delayed_work work; unsigned int full_code:1; unsigned int last_readcount; + u64 rc_type; int (*get_key)(struct em28xx_IR *, struct em28xx_ir_poll_result *); }; @@ -236,11 +237,8 @@ static int default_polling_getkey(struct em28xx_IR *ir, /* Infrared read count (Reg 0x45[6:0] */ poll_result->read_count = (msg[0] & 0x7f); - /* Remote Control Address (Reg 0x46) */ - poll_result->rc_address = msg[1]; - - /* Remote Control Data (Reg 0x47) */ - poll_result->rc_data[0] = msg[2]; + /* Remote Control Address/Data (Regs 0x46/0x47) */ + poll_result->scancode = msg[1] << 8 | msg[2]; return 0; } @@ -266,13 +264,35 @@ static int em2874_polling_getkey(struct em28xx_IR *ir, /* Infrared read count (Reg 0x51[6:0] */ poll_result->read_count = (msg[0] & 0x7f); - /* Remote Control Address (Reg 0x52) */ - poll_result->rc_address = msg[1]; - - /* Remote Control Data (Reg 0x53-55) */ - poll_result->rc_data[0] = msg[2]; - poll_result->rc_data[1] = msg[3]; - poll_result->rc_data[2] = msg[4]; + /* + * Remote Control Address (Reg 0x52) + * Remote Control Data (Reg 0x53-0x55) + */ + switch (ir->rc_type) { + case RC_BIT_RC5: + poll_result->scancode = msg[1] << 8 | msg[2]; + break; + case RC_BIT_NEC: + if ((msg[3] ^ msg[4]) != 0xff) /* 32 bits NEC */ + poll_result->scancode = (msg[1] << 24) | + (msg[2] << 16) | + (msg[3] << 8) | + msg[4]; + else if ((msg[1] ^ msg[2]) != 0xff) /* 24 bits NEC */ + poll_result->scancode = (msg[1] << 16) | + (msg[2] << 8) | + msg[3]; + else /* Normal NEC */ + poll_result->scancode = msg[1] << 8 | msg[3]; + break; + case RC_BIT_RC6_0: + poll_result->scancode = msg[1] << 8 | msg[2]; + break; + default: + poll_result->scancode = (msg[1] << 24) | (msg[2] << 16) | + (msg[3] << 8) | msg[4]; + break; + } return 0; } @@ -294,17 +314,16 @@ static void em28xx_ir_handle_key(struct em28xx_IR *ir) } if (unlikely(poll_result.read_count != ir->last_readcount)) { - dprintk("%s: toggle: %d, count: %d, key 0x%02x%02x\n", __func__, + dprintk("%s: toggle: %d, count: %d, key 0x%04x\n", __func__, poll_result.toggle_bit, poll_result.read_count, - poll_result.rc_address, poll_result.rc_data[0]); + poll_result.scancode); if (ir->full_code) rc_keydown(ir->rc, - poll_result.rc_address << 8 | - poll_result.rc_data[0], + poll_result.scancode, poll_result.toggle_bit); else rc_keydown(ir->rc, - poll_result.rc_data[0], + poll_result.scancode & 0xff, poll_result.toggle_bit); if (ir->dev->chip_id == CHIP_ID_EM2874 || @@ -345,49 +364,92 @@ static void em28xx_ir_stop(struct rc_dev *rc) cancel_delayed_work_sync(&ir->work); } -static int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_type) +static int em2860_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_type) { - int rc = 0; struct em28xx_IR *ir = rc_dev->priv; struct em28xx *dev = ir->dev; - u8 ir_config = EM2874_IR_RC5; - /* Adjust xclk based o IR table for RC5/NEC tables */ + /* Adjust xclk based on IR table for RC5/NEC tables */ + if (*rc_type & RC_BIT_RC5) { + dev->board.xclk |= EM28XX_XCLK_IR_RC5_MODE; + ir->full_code = 1; + *rc_type = RC_BIT_RC5; + } else if (*rc_type & RC_BIT_NEC) { + dev->board.xclk &= ~EM28XX_XCLK_IR_RC5_MODE; + ir->full_code = 1; + *rc_type = RC_BIT_NEC; + } else if (*rc_type & RC_BIT_UNKNOWN) { + *rc_type = RC_BIT_UNKNOWN; + } else { + *rc_type = ir->rc_type; + return -EINVAL; + } + ir->get_key = default_polling_getkey; + em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, dev->board.xclk, + EM28XX_XCLK_IR_RC5_MODE); + + ir->rc_type = *rc_type; + + return 0; +} + +static int em2874_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_type) +{ + struct em28xx_IR *ir = rc_dev->priv; + struct em28xx *dev = ir->dev; + u8 ir_config = EM2874_IR_RC5; + /* Adjust xclk and set type based on IR table for RC5/NEC/RC6 tables */ if (*rc_type & RC_BIT_RC5) { dev->board.xclk |= EM28XX_XCLK_IR_RC5_MODE; ir->full_code = 1; *rc_type = RC_BIT_RC5; } else if (*rc_type & RC_BIT_NEC) { dev->board.xclk &= ~EM28XX_XCLK_IR_RC5_MODE; - ir_config = EM2874_IR_NEC; + ir_config = EM2874_IR_NEC | EM2874_IR_NEC_NO_PARITY; ir->full_code = 1; *rc_type = RC_BIT_NEC; - } else if (*rc_type != RC_BIT_UNKNOWN) - rc = -EINVAL; + } else if (*rc_type & RC_BIT_RC6_0) { + dev->board.xclk |= EM28XX_XCLK_IR_RC5_MODE; + ir_config = EM2874_IR_RC6_MODE_0; + ir->full_code = 1; + *rc_type = RC_BIT_RC6_0; + } else if (*rc_type & RC_BIT_UNKNOWN) { + *rc_type = RC_BIT_UNKNOWN; + } else { + *rc_type = ir->rc_type; + return -EINVAL; + } + + ir->get_key = em2874_polling_getkey; + em28xx_write_regs(dev, EM2874_R50_IR_CONFIG, &ir_config, 1); em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, dev->board.xclk, EM28XX_XCLK_IR_RC5_MODE); + ir->rc_type = *rc_type; + + return 0; +} +static int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_type) +{ + struct em28xx_IR *ir = rc_dev->priv; + struct em28xx *dev = ir->dev; + /* Setup the proper handler based on the chip */ switch (dev->chip_id) { case CHIP_ID_EM2860: case CHIP_ID_EM2883: - ir->get_key = default_polling_getkey; - break; + return em2860_ir_change_protocol(rc_dev, rc_type); case CHIP_ID_EM2884: case CHIP_ID_EM2874: case CHIP_ID_EM28174: - ir->get_key = em2874_polling_getkey; - em28xx_write_regs(dev, EM2874_R50_IR_CONFIG, &ir_config, 1); - break; + return em2874_ir_change_protocol(rc_dev, rc_type); default: printk("Unrecognized em28xx chip id 0x%02x: IR not supported\n", dev->chip_id); - rc = -EINVAL; + return -EINVAL; } - - return rc; } static void em28xx_register_i2c_ir(struct em28xx *dev) @@ -538,7 +600,7 @@ static int em28xx_ir_init(struct em28xx *dev) ir = kzalloc(sizeof(*ir), GFP_KERNEL); rc = rc_allocate_device(); if (!ir || !rc) - goto err_out_free; + goto error; /* record handles to ourself */ ir->dev = dev; @@ -555,11 +617,26 @@ static int em28xx_ir_init(struct em28xx *dev) rc->open = em28xx_ir_start; rc->close = em28xx_ir_stop; + switch (dev->chip_id) { + case CHIP_ID_EM2860: + case CHIP_ID_EM2883: + rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC; + break; + case CHIP_ID_EM2884: + case CHIP_ID_EM2874: + case CHIP_ID_EM28174: + rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC | RC_BIT_RC6_0; + break; + default: + err = -ENODEV; + goto error; + } + /* By default, keep protocol field untouched */ rc_type = RC_BIT_UNKNOWN; err = em28xx_ir_change_protocol(rc, &rc_type); if (err) - goto err_out_free; + goto error; /* This is how often we ask the chip for IR information */ ir->polling = 100; /* ms */ @@ -584,7 +661,7 @@ static int em28xx_ir_init(struct em28xx *dev) /* all done */ err = rc_register_device(rc); if (err) - goto err_out_stop; + goto error; em28xx_register_i2c_ir(dev); @@ -597,9 +674,8 @@ static int em28xx_ir_init(struct em28xx *dev) return 0; - err_out_stop: +error: dev->ir = NULL; - err_out_free: rc_free_device(rc); kfree(ir); return err; diff --git a/drivers/media/usb/em28xx/em28xx-reg.h b/drivers/media/usb/em28xx/em28xx-reg.h index 6ff368297f6e..885089e22bcd 100644 --- a/drivers/media/usb/em28xx/em28xx-reg.h +++ b/drivers/media/usb/em28xx/em28xx-reg.h @@ -13,9 +13,9 @@ #define EM_GPO_3 (1 << 3) /* em28xx endpoints */ -#define EM28XX_EP_ANALOG 0x82 +/* 0x82: (always ?) analog */ #define EM28XX_EP_AUDIO 0x83 -#define EM28XX_EP_DIGITAL 0x84 +/* 0x84: digital or analog */ /* em2800 registers */ #define EM2800_R08_AUDIOSRC 0x08 @@ -177,6 +177,7 @@ /* em2874 IR config register (0x50) */ #define EM2874_IR_NEC 0x00 +#define EM2874_IR_NEC_NO_PARITY 0x01 #define EM2874_IR_RC5 0x04 #define EM2874_IR_RC6_MODE_0 0x08 #define EM2874_IR_RC6_MODE_6A 0x0b diff --git a/drivers/media/usb/em28xx/em28xx-vbi.c b/drivers/media/usb/em28xx/em28xx-vbi.c index 2b4c9cba2d67..d74713bd1d18 100644 --- a/drivers/media/usb/em28xx/em28xx-vbi.c +++ b/drivers/media/usb/em28xx/em28xx-vbi.c @@ -60,8 +60,8 @@ free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf) VIDEOBUF_ACTIVE, it won't be, though. */ spin_lock_irqsave(&dev->slock, flags); - if (dev->isoc_ctl.vbi_buf == buf) - dev->isoc_ctl.vbi_buf = NULL; + if (dev->usb_ctl.vbi_buf == buf) + dev->usb_ctl.vbi_buf = NULL; spin_unlock_irqrestore(&dev->slock, flags); videobuf_vmalloc_free(&buf->vb); diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 1e553d357380..4c1726d43374 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -6,6 +6,7 @@ Markus Rechberger <mrechberger@gmail.com> Mauro Carvalho Chehab <mchehab@infradead.org> Sascha Sommer <saschasommer@freenet.de> + Copyright (C) 2012 Frank Schäfer <fschaefer.oss@googlemail.com> Some parts based on SN9C10x PC Camera Controllers GPL driver made by Luca Risolia <luca.risolia@studio.unibo.it> @@ -153,72 +154,44 @@ static struct v4l2_queryctrl ac97_qctrl[] = { ------------------------------------------------------------------*/ /* - * Announces that a buffer were filled and request the next + * Finish the current buffer */ -static inline void buffer_filled(struct em28xx *dev, - struct em28xx_dmaqueue *dma_q, - struct em28xx_buffer *buf) +static inline void finish_buffer(struct em28xx *dev, + struct em28xx_buffer *buf) { - /* Advice that buffer was filled */ em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); - - dev->isoc_ctl.vid_buf = NULL; - - list_del(&buf->vb.queue); - wake_up(&buf->vb.done); -} - -static inline void vbi_buffer_filled(struct em28xx *dev, - struct em28xx_dmaqueue *dma_q, - struct em28xx_buffer *buf) -{ - /* Advice that buffer was filled */ - em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); - - buf->vb.state = VIDEOBUF_DONE; - buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); - - dev->isoc_ctl.vbi_buf = NULL; - + v4l2_get_timestamp(&buf->vb.ts); list_del(&buf->vb.queue); wake_up(&buf->vb.done); } /* - * Identify the buffer header type and properly handles + * Copy picture data from USB buffer to videobuf buffer */ static void em28xx_copy_video(struct em28xx *dev, - struct em28xx_dmaqueue *dma_q, struct em28xx_buffer *buf, - unsigned char *p, - unsigned char *outp, unsigned long len) + unsigned char *usb_buf, + unsigned long len) { void *fieldstart, *startwrite, *startread; int linesdone, currlinedone, offset, lencopy, remain; int bytesperline = dev->width << 1; - if (dma_q->pos + len > buf->vb.size) - len = buf->vb.size - dma_q->pos; + if (buf->pos + len > buf->vb.size) + len = buf->vb.size - buf->pos; - startread = p; + startread = usb_buf; remain = len; - if (dev->progressive) - fieldstart = outp; - else { - /* Interlaces two half frames */ - if (buf->top_field) - fieldstart = outp; - else - fieldstart = outp + bytesperline; - } + if (dev->progressive || buf->top_field) + fieldstart = buf->vb_buf; + else /* interlaced mode, even nr. of lines */ + fieldstart = buf->vb_buf + bytesperline; - linesdone = dma_q->pos / bytesperline; - currlinedone = dma_q->pos % bytesperline; + linesdone = buf->pos / bytesperline; + currlinedone = buf->pos % bytesperline; if (dev->progressive) offset = linesdone * bytesperline + currlinedone; @@ -229,11 +202,12 @@ static void em28xx_copy_video(struct em28xx *dev, lencopy = bytesperline - currlinedone; lencopy = lencopy > remain ? remain : lencopy; - if ((char *)startwrite + lencopy > (char *)outp + buf->vb.size) { + if ((char *)startwrite + lencopy > (char *)buf->vb_buf + buf->vb.size) { em28xx_isocdbg("Overflow of %zi bytes past buffer end (1)\n", - ((char *)startwrite + lencopy) - - ((char *)outp + buf->vb.size)); - remain = (char *)outp + buf->vb.size - (char *)startwrite; + ((char *)startwrite + lencopy) - + ((char *)buf->vb_buf + buf->vb.size)); + remain = (char *)buf->vb_buf + buf->vb.size - + (char *)startwrite; lencopy = remain; } if (lencopy <= 0) @@ -243,20 +217,23 @@ static void em28xx_copy_video(struct em28xx *dev, remain -= lencopy; while (remain > 0) { - startwrite += lencopy + bytesperline; + if (dev->progressive) + startwrite += lencopy; + else + startwrite += lencopy + bytesperline; startread += lencopy; if (bytesperline > remain) lencopy = remain; else lencopy = bytesperline; - if ((char *)startwrite + lencopy > (char *)outp + + if ((char *)startwrite + lencopy > (char *)buf->vb_buf + buf->vb.size) { em28xx_isocdbg("Overflow of %zi bytes past buffer end" "(2)\n", ((char *)startwrite + lencopy) - - ((char *)outp + buf->vb.size)); - lencopy = remain = (char *)outp + buf->vb.size - + ((char *)buf->vb_buf + buf->vb.size)); + lencopy = remain = (char *)buf->vb_buf + buf->vb.size - (char *)startwrite; } if (lencopy <= 0) @@ -267,57 +244,29 @@ static void em28xx_copy_video(struct em28xx *dev, remain -= lencopy; } - dma_q->pos += len; + buf->pos += len; } +/* + * Copy VBI data from USB buffer to videobuf buffer + */ static void em28xx_copy_vbi(struct em28xx *dev, - struct em28xx_dmaqueue *dma_q, - struct em28xx_buffer *buf, - unsigned char *p, - unsigned char *outp, unsigned long len) + struct em28xx_buffer *buf, + unsigned char *usb_buf, + unsigned long len) { - void *startwrite, *startread; - int offset; - int bytesperline; - - if (dev == NULL) { - em28xx_isocdbg("dev is null\n"); - return; - } - bytesperline = dev->vbi_width; - - if (dma_q == NULL) { - em28xx_isocdbg("dma_q is null\n"); - return; - } - if (buf == NULL) { - return; - } - if (p == NULL) { - em28xx_isocdbg("p is null\n"); - return; - } - if (outp == NULL) { - em28xx_isocdbg("outp is null\n"); - return; - } + unsigned int offset; - if (dma_q->pos + len > buf->vb.size) - len = buf->vb.size - dma_q->pos; - - startread = p; - - startwrite = outp + dma_q->pos; - offset = dma_q->pos; + if (buf->pos + len > buf->vb.size) + len = buf->vb.size - buf->pos; + offset = buf->pos; /* Make sure the bottom field populates the second half of the frame */ - if (buf->top_field == 0) { - startwrite += bytesperline * dev->vbi_height; - offset += bytesperline * dev->vbi_height; - } + if (buf->top_field == 0) + offset += dev->vbi_width * dev->vbi_height; - memcpy(startwrite, startread, len); - dma_q->pos += len; + memcpy(buf->vb_buf + offset, usb_buf, len); + buf->pos += len; } static inline void print_err_status(struct em28xx *dev, @@ -360,166 +309,135 @@ static inline void print_err_status(struct em28xx *dev, } /* - * video-buf generic routine to get the next available buffer + * get the next available buffer from dma queue */ -static inline void get_next_buf(struct em28xx_dmaqueue *dma_q, - struct em28xx_buffer **buf) +static inline struct em28xx_buffer *get_next_buf(struct em28xx *dev, + struct em28xx_dmaqueue *dma_q) { - struct em28xx *dev = container_of(dma_q, struct em28xx, vidq); + struct em28xx_buffer *buf; char *outp; if (list_empty(&dma_q->active)) { em28xx_isocdbg("No active queue to serve\n"); - dev->isoc_ctl.vid_buf = NULL; - *buf = NULL; - return; + return NULL; } /* Get the next buffer */ - *buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue); - + buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue); /* Cleans up buffer - Useful for testing for frame/URB loss */ - outp = videobuf_to_vmalloc(&(*buf)->vb); - memset(outp, 0, (*buf)->vb.size); + outp = videobuf_to_vmalloc(&buf->vb); + memset(outp, 0, buf->vb.size); + buf->pos = 0; + buf->vb_buf = outp; - dev->isoc_ctl.vid_buf = *buf; - - return; + return buf; } /* - * video-buf generic routine to get the next available VBI buffer + * Finish the current buffer if completed and prepare for the next field */ -static inline void vbi_get_next_buf(struct em28xx_dmaqueue *dma_q, - struct em28xx_buffer **buf) -{ - struct em28xx *dev = container_of(dma_q, struct em28xx, vbiq); - char *outp; - - if (list_empty(&dma_q->active)) { - em28xx_isocdbg("No active queue to serve\n"); - dev->isoc_ctl.vbi_buf = NULL; - *buf = NULL; - return; +static struct em28xx_buffer * +finish_field_prepare_next(struct em28xx *dev, + struct em28xx_buffer *buf, + struct em28xx_dmaqueue *dma_q) +{ + if (dev->progressive || dev->top_field) { /* Brand new frame */ + if (buf != NULL) + finish_buffer(dev, buf); + buf = get_next_buf(dev, dma_q); + } + if (buf != NULL) { + buf->top_field = dev->top_field; + buf->pos = 0; } - /* Get the next buffer */ - *buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue); - /* Cleans up buffer - Useful for testing for frame/URB loss */ - outp = videobuf_to_vmalloc(&(*buf)->vb); - memset(outp, 0x00, (*buf)->vb.size); - - dev->isoc_ctl.vbi_buf = *buf; - - return; + return buf; } /* - * Controls the isoc copy of each urb packet + * Process data packet according to the em2710/em2750/em28xx frame data format */ -static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) +static inline void process_frame_data_em28xx(struct em28xx *dev, + unsigned char *data_pkt, + unsigned int data_len) { - struct em28xx_buffer *buf; + struct em28xx_buffer *buf = dev->usb_ctl.vid_buf; + struct em28xx_buffer *vbi_buf = dev->usb_ctl.vbi_buf; struct em28xx_dmaqueue *dma_q = &dev->vidq; - unsigned char *outp = NULL; - int i, len = 0, rc = 1; - unsigned char *p; - - if (!dev) - return 0; - - if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) - return 0; - - if (urb->status < 0) { - print_err_status(dev, -1, urb->status); - if (urb->status == -ENOENT) - return 0; - } - - buf = dev->isoc_ctl.vid_buf; - if (buf != NULL) - outp = videobuf_to_vmalloc(&buf->vb); - - for (i = 0; i < urb->number_of_packets; i++) { - int status = urb->iso_frame_desc[i].status; + struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; - if (status < 0) { - print_err_status(dev, i, status); - if (urb->iso_frame_desc[i].status != -EPROTO) - continue; + /* capture type 0 = vbi start + capture type 1 = vbi in progress + capture type 2 = video start + capture type 3 = video in progress */ + if (data_len >= 4) { + /* NOTE: Headers are always 4 bytes and + * never split across packets */ + if (data_pkt[0] == 0x88 && data_pkt[1] == 0x88 && + data_pkt[2] == 0x88 && data_pkt[3] == 0x88) { + /* Continuation */ + data_pkt += 4; + data_len -= 4; + } else if (data_pkt[0] == 0x33 && data_pkt[1] == 0x95) { + /* Field start (VBI mode) */ + dev->capture_type = 0; + dev->vbi_read = 0; + em28xx_isocdbg("VBI START HEADER !!!\n"); + dev->top_field = !(data_pkt[2] & 1); + data_pkt += 4; + data_len -= 4; + } else if (data_pkt[0] == 0x22 && data_pkt[1] == 0x5a) { + /* Field start (VBI disabled) */ + dev->capture_type = 2; + em28xx_isocdbg("VIDEO START HEADER !!!\n"); + dev->top_field = !(data_pkt[2] & 1); + data_pkt += 4; + data_len -= 4; } + } + /* NOTE: With bulk transfers, intermediate data packets + * have no continuation header */ - len = urb->iso_frame_desc[i].actual_length - 4; + if (dev->capture_type == 0) { + vbi_buf = finish_field_prepare_next(dev, vbi_buf, vbi_dma_q); + dev->usb_ctl.vbi_buf = vbi_buf; + dev->capture_type = 1; + } - if (urb->iso_frame_desc[i].actual_length <= 0) { - /* em28xx_isocdbg("packet %d is empty",i); - spammy */ - continue; - } - if (urb->iso_frame_desc[i].actual_length > - dev->max_pkt_size) { - em28xx_isocdbg("packet bigger than packet size"); - continue; - } + if (dev->capture_type == 1) { + int vbi_size = dev->vbi_width * dev->vbi_height; + int vbi_data_len = ((dev->vbi_read + data_len) > vbi_size) ? + (vbi_size - dev->vbi_read) : data_len; - p = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + /* Copy VBI data */ + if (vbi_buf != NULL) + em28xx_copy_vbi(dev, vbi_buf, data_pkt, vbi_data_len); + dev->vbi_read += vbi_data_len; - /* FIXME: incomplete buffer checks where removed to make - logic simpler. Impacts of those changes should be evaluated - */ - if (p[0] == 0x33 && p[1] == 0x95 && p[2] == 0x00) { - em28xx_isocdbg("VBI HEADER!!!\n"); - /* FIXME: Should add vbi copy */ - continue; + if (vbi_data_len < data_len) { + /* Continue with copying video data */ + dev->capture_type = 2; + data_pkt += vbi_data_len; + data_len -= vbi_data_len; } - if (p[0] == 0x22 && p[1] == 0x5a) { - em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2], - len, (p[2] & 1) ? "odd" : "even"); - - if (dev->progressive || !(p[2] & 1)) { - if (buf != NULL) - buffer_filled(dev, dma_q, buf); - get_next_buf(dma_q, &buf); - if (buf == NULL) - outp = NULL; - else - outp = videobuf_to_vmalloc(&buf->vb); - } - - if (buf != NULL) { - if (p[2] & 1) - buf->top_field = 0; - else - buf->top_field = 1; - } + } - dma_q->pos = 0; - } - if (buf != NULL) { - if (p[0] != 0x88 && p[0] != 0x22) { - em28xx_isocdbg("frame is not complete\n"); - len += 4; - } else { - p += 4; - } - em28xx_copy_video(dev, dma_q, buf, p, outp, len); - } + if (dev->capture_type == 2) { + buf = finish_field_prepare_next(dev, buf, dma_q); + dev->usb_ctl.vid_buf = buf; + dev->capture_type = 3; } - return rc; + + if (dev->capture_type == 3 && buf != NULL && data_len > 0) + em28xx_copy_video(dev, buf, data_pkt, data_len); } -/* Version of isoc handler that takes into account a mixture of video and - VBI data */ -static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb) +/* Processes and copies the URB data content (video and VBI data) */ +static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) { - struct em28xx_buffer *buf, *vbi_buf; - struct em28xx_dmaqueue *dma_q = &dev->vidq; - struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; - unsigned char *outp = NULL; - unsigned char *vbioutp = NULL; - int i, len = 0, rc = 1; - unsigned char *p; - int vbi_size; + int xfer_bulk, num_packets, i; + unsigned char *usb_data_pkt; + unsigned int usb_data_len; if (!dev) return 0; @@ -527,154 +445,48 @@ static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb) if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) return 0; - if (urb->status < 0) { + if (urb->status < 0) print_err_status(dev, -1, urb->status); - if (urb->status == -ENOENT) - return 0; - } - - buf = dev->isoc_ctl.vid_buf; - if (buf != NULL) - outp = videobuf_to_vmalloc(&buf->vb); - - vbi_buf = dev->isoc_ctl.vbi_buf; - if (vbi_buf != NULL) - vbioutp = videobuf_to_vmalloc(&vbi_buf->vb); - - for (i = 0; i < urb->number_of_packets; i++) { - int status = urb->iso_frame_desc[i].status; - - if (status < 0) { - print_err_status(dev, i, status); - if (urb->iso_frame_desc[i].status != -EPROTO) - continue; - } - len = urb->iso_frame_desc[i].actual_length; - if (urb->iso_frame_desc[i].actual_length <= 0) { - /* em28xx_isocdbg("packet %d is empty",i); - spammy */ - continue; - } - if (urb->iso_frame_desc[i].actual_length > - dev->max_pkt_size) { - em28xx_isocdbg("packet bigger than packet size"); - continue; - } + xfer_bulk = usb_pipebulk(urb->pipe); - p = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + if (xfer_bulk) /* bulk */ + num_packets = 1; + else /* isoc */ + num_packets = urb->number_of_packets; - /* capture type 0 = vbi start - capture type 1 = video start - capture type 2 = video in progress */ - if (p[0] == 0x33 && p[1] == 0x95) { - dev->capture_type = 0; - dev->vbi_read = 0; - em28xx_isocdbg("VBI START HEADER!!!\n"); - dev->cur_field = p[2]; - p += 4; - len -= 4; - } else if (p[0] == 0x88 && p[1] == 0x88 && - p[2] == 0x88 && p[3] == 0x88) { - /* continuation */ - p += 4; - len -= 4; - } else if (p[0] == 0x22 && p[1] == 0x5a) { - /* start video */ - p += 4; - len -= 4; - } + for (i = 0; i < num_packets; i++) { + if (xfer_bulk) { /* bulk */ + usb_data_len = urb->actual_length; - vbi_size = dev->vbi_width * dev->vbi_height; - - if (dev->capture_type == 0) { - if (dev->vbi_read >= vbi_size) { - /* We've already read all the VBI data, so - treat the rest as video */ - em28xx_isocdbg("dev->vbi_read > vbi_size\n"); - } else if ((dev->vbi_read + len) < vbi_size) { - /* This entire frame is VBI data */ - if (dev->vbi_read == 0 && - (!(dev->cur_field & 1))) { - /* Brand new frame */ - if (vbi_buf != NULL) - vbi_buffer_filled(dev, - vbi_dma_q, - vbi_buf); - vbi_get_next_buf(vbi_dma_q, &vbi_buf); - if (vbi_buf == NULL) - vbioutp = NULL; - else - vbioutp = videobuf_to_vmalloc( - &vbi_buf->vb); - } - - if (dev->vbi_read == 0) { - vbi_dma_q->pos = 0; - if (vbi_buf != NULL) { - if (dev->cur_field & 1) - vbi_buf->top_field = 0; - else - vbi_buf->top_field = 1; - } - } - - dev->vbi_read += len; - em28xx_copy_vbi(dev, vbi_dma_q, vbi_buf, p, - vbioutp, len); - } else { - /* Some of this frame is VBI data and some is - video data */ - int vbi_data_len = vbi_size - dev->vbi_read; - dev->vbi_read += vbi_data_len; - em28xx_copy_vbi(dev, vbi_dma_q, vbi_buf, p, - vbioutp, vbi_data_len); - dev->capture_type = 1; - p += vbi_data_len; - len -= vbi_data_len; + usb_data_pkt = urb->transfer_buffer; + } else { /* isoc */ + if (urb->iso_frame_desc[i].status < 0) { + print_err_status(dev, i, + urb->iso_frame_desc[i].status); + if (urb->iso_frame_desc[i].status != -EPROTO) + continue; } - } - if (dev->capture_type == 1) { - dev->capture_type = 2; - if (dev->progressive || !(dev->cur_field & 1)) { - if (buf != NULL) - buffer_filled(dev, dma_q, buf); - get_next_buf(dma_q, &buf); - if (buf == NULL) - outp = NULL; - else - outp = videobuf_to_vmalloc(&buf->vb); - } - if (buf != NULL) { - if (dev->cur_field & 1) - buf->top_field = 0; - else - buf->top_field = 1; + usb_data_len = urb->iso_frame_desc[i].actual_length; + if (usb_data_len > dev->max_pkt_size) { + em28xx_isocdbg("packet bigger than packet size"); + continue; } - dma_q->pos = 0; + usb_data_pkt = urb->transfer_buffer + + urb->iso_frame_desc[i].offset; } - if (buf != NULL && dev->capture_type == 2) { - if (len >= 4 && p[0] == 0x88 && p[1] == 0x88 && - p[2] == 0x88 && p[3] == 0x88) { - p += 4; - len -= 4; - } - if (len >= 4 && p[0] == 0x22 && p[1] == 0x5a) { - em28xx_isocdbg("Video frame %d, len=%i, %s\n", - p[2], len, (p[2] & 1) ? - "odd" : "even"); - p += 4; - len -= 4; - } - - if (len > 0) - em28xx_copy_video(dev, dma_q, buf, p, outp, - len); + if (usb_data_len == 0) { + /* NOTE: happens very often with isoc transfers */ + /* em28xx_usbdbg("packet %d is empty",i); - spammy */ + continue; } + + process_frame_data_em28xx(dev, usb_data_pkt, usb_data_len); } - return rc; + return 1; } @@ -727,8 +539,8 @@ static void free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf) VIDEOBUF_ACTIVE, it won't be, though. */ spin_lock_irqsave(&dev->slock, flags); - if (dev->isoc_ctl.vid_buf == buf) - dev->isoc_ctl.vid_buf = NULL; + if (dev->usb_ctl.vid_buf == buf) + dev->usb_ctl.vid_buf = NULL; spin_unlock_irqrestore(&dev->slock, flags); videobuf_vmalloc_free(&buf->vb); @@ -760,22 +572,17 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, goto fail; } - if (!dev->isoc_ctl.analog_bufs.num_bufs) + if (!dev->usb_ctl.analog_bufs.num_bufs) urb_init = 1; if (urb_init) { - if (em28xx_vbi_supported(dev) == 1) - rc = em28xx_init_isoc(dev, EM28XX_ANALOG_MODE, - EM28XX_NUM_PACKETS, - EM28XX_NUM_BUFS, - dev->max_pkt_size, - em28xx_isoc_copy_vbi); - else - rc = em28xx_init_isoc(dev, EM28XX_ANALOG_MODE, - EM28XX_NUM_PACKETS, - EM28XX_NUM_BUFS, - dev->max_pkt_size, - em28xx_isoc_copy); + dev->capture_type = -1; + rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE, + dev->analog_xfer_bulk, + EM28XX_NUM_BUFS, + dev->max_pkt_size, + dev->packet_multiplier, + em28xx_urb_data_copy); if (rc < 0) goto fail; } @@ -2263,7 +2070,7 @@ static int em28xx_v4l2_close(struct file *filp) free the remaining resources */ if (dev->state & DEV_DISCONNECTED) { em28xx_release_resources(dev); - kfree(dev->alt_max_pkt_size); + kfree(dev->alt_max_pkt_size_isoc); mutex_unlock(&dev->lock); kfree(dev); kfree(fh); @@ -2274,7 +2081,7 @@ static int em28xx_v4l2_close(struct file *filp) v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); /* do this before setting alternate! */ - em28xx_uninit_isoc(dev, EM28XX_ANALOG_MODE); + em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE); em28xx_set_mode(dev, EM28XX_SUSPEND); /* set alternate 0 */ diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 86e90d86da6d..062841e50722 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -4,6 +4,7 @@ Copyright (C) 2005 Markus Rechberger <mrechberger@gmail.com> Ludovico Cavedon <cavedon@sssup.it> Mauro Carvalho Chehab <mchehab@infradead.org> + Copyright (C) 2012 Frank Schäfer <fschaefer.oss@googlemail.com> Based on the em2800 driver from Sascha Sommer <saschasommer@freenet.de> @@ -157,12 +158,18 @@ #define EM28XX_NUM_BUFS 5 #define EM28XX_DVB_NUM_BUFS 5 -/* number of packets for each buffer +/* isoc transfers: number of packets for each buffer windows requests only 64 packets .. so we better do the same this is what I found out for all alternate numbers there! */ -#define EM28XX_NUM_PACKETS 64 -#define EM28XX_DVB_MAX_PACKETS 64 +#define EM28XX_NUM_ISOC_PACKETS 64 +#define EM28XX_DVB_NUM_ISOC_PACKETS 64 + +/* bulk transfers: transfer buffer size = packet size * packet multiplier + USB 2.0 spec says bulk packet size is always 512 bytes + */ +#define EM28XX_BULK_PACKET_MULTIPLIER 384 +#define EM28XX_DVB_BULK_PACKET_MULTIPLIER 384 #define EM28XX_INTERLACED_DEFAULT 1 @@ -187,10 +194,6 @@ Interval: 125us */ -/* time to wait when stopping the isoc transfer */ -#define EM28XX_URB_TIMEOUT \ - msecs_to_jiffies(EM28XX_NUM_BUFS * EM28XX_NUM_PACKETS) - /* time in msecs to wait for i2c writes to finish */ #define EM2800_I2C_WRITE_TIMEOUT 20 @@ -203,7 +206,7 @@ enum em28xx_mode { struct em28xx; -struct em28xx_usb_isoc_bufs { +struct em28xx_usb_bufs { /* max packet size of isoc transaction */ int max_pkt_size; @@ -213,26 +216,26 @@ struct em28xx_usb_isoc_bufs { /* number of allocated urbs */ int num_bufs; - /* urb for isoc transfers */ + /* urb for isoc/bulk transfers */ struct urb **urb; - /* transfer buffers for isoc transfer */ + /* transfer buffers for isoc/bulk transfer */ char **transfer_buffer; }; -struct em28xx_usb_isoc_ctl { - /* isoc transfer buffers for analog mode */ - struct em28xx_usb_isoc_bufs analog_bufs; +struct em28xx_usb_ctl { + /* isoc/bulk transfer buffers for analog mode */ + struct em28xx_usb_bufs analog_bufs; - /* isoc transfer buffers for digital mode */ - struct em28xx_usb_isoc_bufs digital_bufs; + /* isoc/bulk transfer buffers for digital mode */ + struct em28xx_usb_bufs digital_bufs; /* Stores already requested buffers */ struct em28xx_buffer *vid_buf; struct em28xx_buffer *vbi_buf; - /* isoc urb callback */ - int (*isoc_copy) (struct em28xx *dev, struct urb *urb); + /* copy data from URB */ + int (*urb_data_copy) (struct em28xx *dev, struct urb *urb); }; @@ -249,17 +252,21 @@ struct em28xx_buffer { /* common v4l buffer stuff -- must be first */ struct videobuf_buffer vb; - struct list_head frame; int top_field; + + /* counter to control buffer fill */ + unsigned int pos; + /* NOTE; in interlaced mode, this value is reset to zero at + * the start of each new field (not frame !) */ + + /* pointer to vmalloc memory address in vb */ + char *vb_buf; }; struct em28xx_dmaqueue { struct list_head active; wait_queue_head_t wq; - - /* Counters to control buffer fill */ - int pos; }; /* inputs */ @@ -497,7 +504,7 @@ struct em28xx { int sensor_xres, sensor_yres; int sensor_xtal; - /* Allows progressive (e. g. non-interlaced) mode */ + /* Progressive (non-interlaced) mode */ int progressive; /* Vinmode/Vinctl used at the driver */ @@ -557,10 +564,10 @@ struct em28xx { /* states */ enum em28xx_dev_state state; - /* vbi related state tracking */ + /* capture state tracking */ int capture_type; + unsigned char top_field:1; int vbi_read; - unsigned char cur_field; unsigned int vbi_width; unsigned int vbi_height; /* lines per field */ @@ -582,17 +589,28 @@ struct em28xx { /* Isoc control struct */ struct em28xx_dmaqueue vidq; struct em28xx_dmaqueue vbiq; - struct em28xx_usb_isoc_ctl isoc_ctl; + struct em28xx_usb_ctl usb_ctl; spinlock_t slock; /* usb transfer */ struct usb_device *udev; /* the usb device */ - int alt; /* alternate */ - int max_pkt_size; /* max packet size of isoc transaction */ - int num_alt; /* Number of alternative settings */ - unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */ - int dvb_alt; /* alternate for DVB */ - unsigned int dvb_max_pkt_size; /* wMaxPacketSize for DVB */ + u8 analog_ep_isoc; /* address of isoc endpoint for analog */ + u8 analog_ep_bulk; /* address of bulk endpoint for analog */ + u8 dvb_ep_isoc; /* address of isoc endpoint for DVB */ + u8 dvb_ep_bulk; /* address of bulk endpoint for DVC */ + int alt; /* alternate setting */ + int max_pkt_size; /* max packet size of the selected ep at alt */ + int packet_multiplier; /* multiplier for wMaxPacketSize, used for + URB buffer size definition */ + int num_alt; /* number of alternative settings */ + unsigned int *alt_max_pkt_size_isoc; /* array of isoc wMaxPacketSize */ + unsigned int analog_xfer_bulk:1; /* use bulk instead of isoc + transfers for analog */ + int dvb_alt_isoc; /* alternate setting for DVB isoc transfers */ + unsigned int dvb_max_pkt_size_isoc; /* isoc max packet size of the + selected DVB ep at dvb_alt */ + unsigned int dvb_xfer_bulk:1; /* use bulk instead of isoc + transfers for DVB */ char urb_buf[URB_MAX_CTRL_SIZE]; /* urb control msg buffer */ /* helper funcs that call usb_control_msg */ @@ -666,12 +684,14 @@ int em28xx_vbi_supported(struct em28xx *dev); int em28xx_set_outfmt(struct em28xx *dev); int em28xx_resolution_set(struct em28xx *dev); int em28xx_set_alternate(struct em28xx *dev); -int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, - int max_packets, int num_bufs, int max_pkt_size); -int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, - int max_packets, int num_bufs, int max_pkt_size, - int (*isoc_copy) (struct em28xx *dev, struct urb *urb)); -void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode); +int em28xx_alloc_urbs(struct em28xx *dev, enum em28xx_mode mode, int xfer_bulk, + int num_bufs, int max_pkt_size, int packet_multiplier); +int em28xx_init_usb_xfer(struct em28xx *dev, enum em28xx_mode mode, + int xfer_bulk, + int num_bufs, int max_pkt_size, int packet_multiplier, + int (*urb_data_copy) + (struct em28xx *dev, struct urb *urb)); +void em28xx_uninit_usb_xfer(struct em28xx *dev, enum em28xx_mode mode); void em28xx_stop_urbs(struct em28xx *dev); int em28xx_isoc_dvb_max_packetsize(struct em28xx *dev); int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode); diff --git a/drivers/media/usb/gspca/jl2005bcd.c b/drivers/media/usb/gspca/jl2005bcd.c index 62ba80d9b998..fdaeeb14453f 100644 --- a/drivers/media/usb/gspca/jl2005bcd.c +++ b/drivers/media/usb/gspca/jl2005bcd.c @@ -536,20 +536,4 @@ static struct usb_driver sd_driver = { #endif }; -/* -- module insert / remove -- */ -static int __init sd_mod_init(void) -{ - int ret; - - ret = usb_register(&sd_driver); - if (ret < 0) - return ret; - return 0; -} -static void __exit sd_mod_exit(void) -{ - usb_deregister(&sd_driver); -} - -module_init(sd_mod_init); -module_exit(sd_mod_exit); +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/kinect.c b/drivers/media/usb/gspca/kinect.c index 40ad6687ee5d..3773a8a745df 100644 --- a/drivers/media/usb/gspca/kinect.c +++ b/drivers/media/usb/gspca/kinect.c @@ -381,6 +381,7 @@ static const struct sd_desc sd_desc = { /* -- module initialisation -- */ static const struct usb_device_id device_table[] = { {USB_DEVICE(0x045e, 0x02ae)}, + {USB_DEVICE(0x045e, 0x02bf)}, {} }; diff --git a/drivers/media/usb/gspca/pac207.c b/drivers/media/usb/gspca/pac207.c index d236d1791f78..1f253dfebe47 100644 --- a/drivers/media/usb/gspca/pac207.c +++ b/drivers/media/usb/gspca/pac207.c @@ -55,6 +55,11 @@ MODULE_LICENSE("GPL"); #define PAC207_AUTOGAIN_DEADZONE 30 +/* global parameters */ +static int led_invert; +module_param(led_invert, int, 0644); +MODULE_PARM_DESC(led_invert, "Invert led"); + /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ @@ -187,10 +192,14 @@ static int sd_config(struct gspca_dev *gspca_dev, /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { - pac207_write_reg(gspca_dev, 0x41, 0x00); - /* Bit_0=Image Format, - * Bit_1=LED, - * Bit_2=Compression test mode enable */ + u8 mode; + + /* mode: Image Format (Bit 0), LED (1), Compr. test mode (2) */ + if (led_invert) + mode = 0x02; + else + mode = 0x00; + pac207_write_reg(gspca_dev, 0x41, mode); pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */ return gspca_dev->usb_err; @@ -303,7 +312,11 @@ static int sd_start(struct gspca_dev *gspca_dev) pac207_write_reg(gspca_dev, 0x02, v4l2_ctrl_g_ctrl(gspca_dev->exposure)); /* PXCK = 12MHz /n */ - mode = 0x02; /* Image Format (Bit 0), LED (1), Compr. test mode (2) */ + /* mode: Image Format (Bit 0), LED (1), Compr. test mode (2) */ + if (led_invert) + mode = 0x00; + else + mode = 0x02; if (gspca_dev->width == 176) { /* 176x144 */ mode |= 0x01; PDEBUG(D_STREAM, "pac207_start mode 176x144"); @@ -325,8 +338,15 @@ static int sd_start(struct gspca_dev *gspca_dev) static void sd_stopN(struct gspca_dev *gspca_dev) { + u8 mode; + + /* mode: Image Format (Bit 0), LED (1), Compr. test mode (2) */ + if (led_invert) + mode = 0x02; + else + mode = 0x00; pac207_write_reg(gspca_dev, 0x40, 0x00); /* Stop ISO pipe */ - pac207_write_reg(gspca_dev, 0x41, 0x00); /* Turn of LED */ + pac207_write_reg(gspca_dev, 0x41, mode); /* Turn off LED */ pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */ } diff --git a/drivers/media/usb/gspca/sonixb.c b/drivers/media/usb/gspca/sonixb.c index 70511d5f9538..1220340e7602 100644 --- a/drivers/media/usb/gspca/sonixb.c +++ b/drivers/media/usb/gspca/sonixb.c @@ -496,7 +496,7 @@ static void reg_w(struct gspca_dev *gspca_dev, } } -static void i2c_w(struct gspca_dev *gspca_dev, const __u8 *buffer) +static void i2c_w(struct gspca_dev *gspca_dev, const u8 *buf) { int retry = 60; @@ -504,16 +504,19 @@ static void i2c_w(struct gspca_dev *gspca_dev, const __u8 *buffer) return; /* is i2c ready */ - reg_w(gspca_dev, 0x08, buffer, 8); + reg_w(gspca_dev, 0x08, buf, 8); while (retry--) { if (gspca_dev->usb_err < 0) return; - msleep(10); + msleep(1); reg_r(gspca_dev, 0x08); if (gspca_dev->usb_buf[0] & 0x04) { if (gspca_dev->usb_buf[0] & 0x08) { dev_err(gspca_dev->v4l2_dev.dev, - "i2c write error\n"); + "i2c error writing %02x %02x %02x %02x" + " %02x %02x %02x %02x\n", + buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7]); gspca_dev->usb_err = -EIO; } return; @@ -530,7 +533,7 @@ static void i2c_w_vector(struct gspca_dev *gspca_dev, for (;;) { if (gspca_dev->usb_err < 0) return; - reg_w(gspca_dev, 0x08, *buffer, 8); + i2c_w(gspca_dev, *buffer); len -= 8; if (len <= 0) break; diff --git a/drivers/media/usb/gspca/sonixj.c b/drivers/media/usb/gspca/sonixj.c index 5a86047b846f..36307a9028a9 100644 --- a/drivers/media/usb/gspca/sonixj.c +++ b/drivers/media/usb/gspca/sonixj.c @@ -1550,6 +1550,7 @@ static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) 0, gspca_dev->usb_buf, 8, 500); + msleep(2); if (ret < 0) { pr_err("i2c_w1 err %d\n", ret); gspca_dev->usb_err = ret; diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c b/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c index 748e1421d6d8..e95fa8997d22 100644 --- a/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c +++ b/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c @@ -52,9 +52,13 @@ static int vv6410_s_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_HFLIP: + if (!gspca_dev->streaming) + return 0; err = vv6410_set_hflip(gspca_dev, ctrl->val); break; case V4L2_CID_VFLIP: + if (!gspca_dev->streaming) + return 0; err = vv6410_set_vflip(gspca_dev, ctrl->val); break; case V4L2_CID_GAIN: @@ -94,11 +98,14 @@ static int vv6410_init_controls(struct sd *sd) { struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; - v4l2_ctrl_handler_init(hdl, 4); - v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_handler_init(hdl, 2); + /* Disable the hardware VFLIP and HFLIP as we currently lack a + mechanism to adjust the image offset in such a way that + we don't need to renegotiate the announced format */ + /* v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, */ + /* V4L2_CID_HFLIP, 0, 1, 1, 0); */ + /* v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, */ + /* V4L2_CID_VFLIP, 0, 1, 1, 0); */ v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, V4L2_CID_EXPOSURE, 0, 32768, 1, 20000); v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, diff --git a/drivers/media/usb/gspca/t613.c b/drivers/media/usb/gspca/t613.c index 8bc6c3ceec2c..b92d4ef2de6e 100644 --- a/drivers/media/usb/gspca/t613.c +++ b/drivers/media/usb/gspca/t613.c @@ -494,7 +494,7 @@ static void setcolors(struct gspca_dev *gspca_dev, s32 val) static void setgamma(struct gspca_dev *gspca_dev, s32 val) { - PDEBUG(D_CONF, "Gamma: %d", sd->gamma); + PDEBUG(D_CONF, "Gamma: %d", val); reg_w_ixbuf(gspca_dev, 0x90, gamma_table[val], sizeof gamma_table[0]); } diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index 5210239cbaee..21c15233c6ae 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -316,7 +316,8 @@ static void pwc_isoc_handler(struct urb *urb) struct pwc_frame_buf *fbuf = pdev->fill_buf; if (pdev->vsync == 1) { - do_gettimeofday(&fbuf->vb.v4l2_buf.timestamp); + v4l2_get_timestamp( + &fbuf->vb.v4l2_buf.timestamp); pdev->vsync = 2; } diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index 8ebec0d7bf59..498c57ea5d32 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -593,7 +593,7 @@ static int s2255_got_frame(struct s2255_channel *channel, int jpgsize) buf = list_entry(dma_q->active.next, struct s2255_buffer, vb.queue); list_del(&buf->vb.queue); - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); s2255_fillbuff(channel, buf, jpgsize); wake_up(&buf->vb.done); dprintk(2, "%s: [buf/i] [%p/%d]\n", __func__, buf, buf->vb.i); @@ -629,7 +629,6 @@ static void s2255_fillbuff(struct s2255_channel *channel, struct s2255_buffer *buf, int jpgsize) { int pos = 0; - struct timeval ts; const char *tmpbuf; char *vbuf = videobuf_to_vmalloc(&buf->vb); unsigned long last_frame; @@ -674,8 +673,7 @@ static void s2255_fillbuff(struct s2255_channel *channel, /* tell v4l buffer was filled */ buf->vb.field_count = channel->frame_count * 2; - do_gettimeofday(&ts); - buf->vb.ts = ts; + v4l2_get_timestamp(&buf->vb.ts); buf->vb.state = VIDEOBUF_DONE; } diff --git a/drivers/media/usb/sn9c102/sn9c102_core.c b/drivers/media/usb/sn9c102/sn9c102_core.c index 73605864fffa..6bda81aebf87 100644 --- a/drivers/media/usb/sn9c102/sn9c102_core.c +++ b/drivers/media/usb/sn9c102/sn9c102_core.c @@ -173,7 +173,7 @@ sn9c102_request_buffers(struct sn9c102_device* cam, u32 count, cam->frame[i].buf.sequence = 0; cam->frame[i].buf.field = V4L2_FIELD_NONE; cam->frame[i].buf.memory = V4L2_MEMORY_MMAP; - cam->frame[i].buf.flags = 0; + cam->frame[i].buf.flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; } return cam->nbuffers; @@ -773,7 +773,8 @@ end_of_frame: img); if ((*f)->buf.bytesused == 0) - do_gettimeofday(&(*f)->buf.timestamp); + v4l2_get_timestamp( + &(*f)->buf.timestamp); (*f)->buf.bytesused += img; diff --git a/drivers/media/usb/stk1160/stk1160-video.c b/drivers/media/usb/stk1160/stk1160-video.c index fa3671de02aa..0a4ee85f4399 100644 --- a/drivers/media/usb/stk1160/stk1160-video.c +++ b/drivers/media/usb/stk1160/stk1160-video.c @@ -101,7 +101,7 @@ void stk1160_buffer_done(struct stk1160 *dev) buf->vb.v4l2_buf.sequence = dev->field_count >> 1; buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED; buf->vb.v4l2_buf.bytesused = buf->bytesused; - do_gettimeofday(&buf->vb.v4l2_buf.timestamp); + v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); vb2_set_plane_payload(&buf->vb, 0, buf->bytesused); vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c index 5d3c032d733c..4cbab085e348 100644 --- a/drivers/media/usb/stkwebcam/stk-webcam.c +++ b/drivers/media/usb/stkwebcam/stk-webcam.c @@ -28,6 +28,7 @@ #include <linux/errno.h> #include <linux/slab.h> +#include <linux/dmi.h> #include <linux/usb.h> #include <linux/mm.h> #include <linux/vmalloc.h> @@ -38,12 +39,12 @@ #include "stk-webcam.h" -static bool hflip; -module_param(hflip, bool, 0444); +static int hflip = -1; +module_param(hflip, int, 0444); MODULE_PARM_DESC(hflip, "Horizontal image flip (mirror). Defaults to 0"); -static bool vflip; -module_param(vflip, bool, 0444); +static int vflip = -1; +module_param(vflip, int, 0444); MODULE_PARM_DESC(vflip, "Vertical image flip. Defaults to 0"); static int debug; @@ -62,6 +63,19 @@ static struct usb_device_id stkwebcam_table[] = { }; MODULE_DEVICE_TABLE(usb, stkwebcam_table); +/* The stk webcam laptop module is mounted upside down in some laptops :( */ +static const struct dmi_system_id stk_upside_down_dmi_table[] = { + { + .ident = "ASUS G1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "G1") + } + }, + {} +}; + + /* * Basic stuff */ @@ -466,6 +480,7 @@ static int stk_setup_siobuf(struct stk_camera *dev, int index) buf->dev = dev; buf->v4lbuf.index = index; buf->v4lbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf->v4lbuf.flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; buf->v4lbuf.field = V4L2_FIELD_NONE; buf->v4lbuf.memory = V4L2_MEMORY_MMAP; buf->v4lbuf.m.offset = 2*index*buf->v4lbuf.length; @@ -816,10 +831,16 @@ static int stk_vidioc_g_ctrl(struct file *filp, c->value = dev->vsettings.brightness; break; case V4L2_CID_HFLIP: - c->value = dev->vsettings.hflip; + if (dmi_check_system(stk_upside_down_dmi_table)) + c->value = !dev->vsettings.hflip; + else + c->value = dev->vsettings.hflip; break; case V4L2_CID_VFLIP: - c->value = dev->vsettings.vflip; + if (dmi_check_system(stk_upside_down_dmi_table)) + c->value = !dev->vsettings.vflip; + else + c->value = dev->vsettings.vflip; break; default: return -EINVAL; @@ -836,10 +857,16 @@ static int stk_vidioc_s_ctrl(struct file *filp, dev->vsettings.brightness = c->value; return stk_sensor_set_brightness(dev, c->value >> 8); case V4L2_CID_HFLIP: - dev->vsettings.hflip = c->value; + if (dmi_check_system(stk_upside_down_dmi_table)) + dev->vsettings.hflip = !c->value; + else + dev->vsettings.hflip = c->value; return 0; case V4L2_CID_VFLIP: - dev->vsettings.vflip = c->value; + if (dmi_check_system(stk_upside_down_dmi_table)) + dev->vsettings.vflip = !c->value; + else + dev->vsettings.vflip = c->value; return 0; default: return -EINVAL; @@ -1113,7 +1140,7 @@ static int stk_vidioc_dqbuf(struct file *filp, sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_QUEUED; sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_DONE; sbuf->v4lbuf.sequence = ++dev->sequence; - do_gettimeofday(&sbuf->v4lbuf.timestamp); + v4l2_get_timestamp(&sbuf->v4lbuf.timestamp); *buf = sbuf->v4lbuf; return 0; @@ -1275,8 +1302,18 @@ static int stk_camera_probe(struct usb_interface *interface, dev->interface = interface; usb_get_intf(interface); - dev->vsettings.vflip = vflip; - dev->vsettings.hflip = hflip; + if (hflip != -1) + dev->vsettings.hflip = hflip; + else if (dmi_check_system(stk_upside_down_dmi_table)) + dev->vsettings.hflip = 1; + else + dev->vsettings.hflip = 0; + if (vflip != -1) + dev->vsettings.vflip = vflip; + else if (dmi_check_system(stk_upside_down_dmi_table)) + dev->vsettings.vflip = 1; + else + dev->vsettings.vflip = 0; dev->n_sbufs = 0; set_present(dev); diff --git a/drivers/media/usb/tlg2300/pd-video.c b/drivers/media/usb/tlg2300/pd-video.c index 3082bfa9b2c5..21723378bb8f 100644 --- a/drivers/media/usb/tlg2300/pd-video.c +++ b/drivers/media/usb/tlg2300/pd-video.c @@ -212,7 +212,7 @@ static void submit_frame(struct front_face *front) front->curr_frame = NULL; vb->state = VIDEOBUF_DONE; vb->field_count++; - do_gettimeofday(&vb->ts); + v4l2_get_timestamp(&vb->ts); wake_up(&vb->done); } diff --git a/drivers/media/usb/tm6000/tm6000-dvb.c b/drivers/media/usb/tm6000/tm6000-dvb.c index e1f3f66e1e63..9fc1e940a82b 100644 --- a/drivers/media/usb/tm6000/tm6000-dvb.c +++ b/drivers/media/usb/tm6000/tm6000-dvb.c @@ -360,8 +360,8 @@ dvb_dmx_err: dvb_dmx_release(&dvb->demux); frontend_err: if (dvb->frontend) { - dvb_frontend_detach(dvb->frontend); dvb_unregister_frontend(dvb->frontend); + dvb_frontend_detach(dvb->frontend); } adapter_err: dvb_unregister_adapter(&dvb->adapter); @@ -384,8 +384,8 @@ static void unregister_dvb(struct tm6000_core *dev) /* mutex_lock(&tm6000_driver.open_close_mutex); */ if (dvb->frontend) { - dvb_frontend_detach(dvb->frontend); dvb_unregister_frontend(dvb->frontend); + dvb_frontend_detach(dvb->frontend); } dvb_dmxdev_release(&dvb->dmxdev); diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index f656fd7a39a2..e3c567c27918 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -49,12 +49,15 @@ #define TM6000_MIN_BUF 4 #define TM6000_DEF_BUF 8 +#define TM6000_NUM_URB_BUF 8 + #define TM6000_MAX_ISO_PACKETS 46 /* Max number of ISO packets */ /* Declare static vars that will be used as parameters */ static unsigned int vid_limit = 16; /* Video memory limit, in Mb */ static int video_nr = -1; /* /dev/videoN, -1 for autodetect */ static int radio_nr = -1; /* /dev/radioN, -1 for autodetect */ +static bool keep_urb; /* keep urb buffers allocated */ /* Debug level */ int tm6000_debug; @@ -191,7 +194,7 @@ static inline void buffer_filled(struct tm6000_core *dev, dprintk(dev, V4L2_DEBUG_ISOC, "[%p/%d] wakeup\n", buf, buf->vb.i); buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); list_del(&buf->vb.queue); wake_up(&buf->vb.done); @@ -538,6 +541,71 @@ static void tm6000_irq_callback(struct urb *urb) } /* + * Allocate URB buffers + */ +static int tm6000_alloc_urb_buffers(struct tm6000_core *dev) +{ + int num_bufs = TM6000_NUM_URB_BUF; + int i; + + if (dev->urb_buffer != NULL) + return 0; + + dev->urb_buffer = kmalloc(sizeof(void *)*num_bufs, GFP_KERNEL); + if (!dev->urb_buffer) { + tm6000_err("cannot allocate memory for urb buffers\n"); + return -ENOMEM; + } + + dev->urb_dma = kmalloc(sizeof(dma_addr_t *)*num_bufs, GFP_KERNEL); + if (!dev->urb_dma) { + tm6000_err("cannot allocate memory for urb dma pointers\n"); + return -ENOMEM; + } + + for (i = 0; i < num_bufs; i++) { + dev->urb_buffer[i] = usb_alloc_coherent( + dev->udev, dev->urb_size, + GFP_KERNEL, &dev->urb_dma[i]); + if (!dev->urb_buffer[i]) { + tm6000_err("unable to allocate %i bytes for transfer buffer %i\n", + dev->urb_size, i); + return -ENOMEM; + } + memset(dev->urb_buffer[i], 0, dev->urb_size); + } + + return 0; +} + +/* + * Free URB buffers + */ +static int tm6000_free_urb_buffers(struct tm6000_core *dev) +{ + int i; + + if (dev->urb_buffer == NULL) + return 0; + + for (i = 0; i < TM6000_NUM_URB_BUF; i++) { + if (dev->urb_buffer[i]) { + usb_free_coherent(dev->udev, + dev->urb_size, + dev->urb_buffer[i], + dev->urb_dma[i]); + dev->urb_buffer[i] = NULL; + } + } + kfree(dev->urb_buffer); + kfree(dev->urb_dma); + dev->urb_buffer = NULL; + dev->urb_dma = NULL; + + return 0; +} + +/* * Stop and Deallocate URBs */ static void tm6000_uninit_isoc(struct tm6000_core *dev) @@ -551,18 +619,15 @@ static void tm6000_uninit_isoc(struct tm6000_core *dev) if (urb) { usb_kill_urb(urb); usb_unlink_urb(urb); - if (dev->isoc_ctl.transfer_buffer[i]) { - usb_free_coherent(dev->udev, - urb->transfer_buffer_length, - dev->isoc_ctl.transfer_buffer[i], - urb->transfer_dma); - } usb_free_urb(urb); dev->isoc_ctl.urb[i] = NULL; } dev->isoc_ctl.transfer_buffer[i] = NULL; } + if (!keep_urb) + tm6000_free_urb_buffers(dev); + kfree(dev->isoc_ctl.urb); kfree(dev->isoc_ctl.transfer_buffer); @@ -572,12 +637,13 @@ static void tm6000_uninit_isoc(struct tm6000_core *dev) } /* - * Allocate URBs and start IRQ + * Assign URBs and start IRQ */ static int tm6000_prepare_isoc(struct tm6000_core *dev) { struct tm6000_dmaqueue *dma_q = &dev->vidq; - int i, j, sb_size, pipe, size, max_packets, num_bufs = 8; + int i, j, sb_size, pipe, size, max_packets; + int num_bufs = TM6000_NUM_URB_BUF; struct urb *urb; /* De-allocates all pending stuff */ @@ -605,6 +671,7 @@ static int tm6000_prepare_isoc(struct tm6000_core *dev) max_packets = TM6000_MAX_ISO_PACKETS; sb_size = max_packets * size; + dev->urb_size = sb_size; dev->isoc_ctl.num_bufs = num_bufs; @@ -627,6 +694,17 @@ static int tm6000_prepare_isoc(struct tm6000_core *dev) max_packets, num_bufs, sb_size, dev->isoc_in.maxsize, size); + + if (!dev->urb_buffer && tm6000_alloc_urb_buffers(dev) < 0) { + tm6000_err("cannot allocate memory for urb buffers\n"); + + /* call free, as some buffers might have been allocated */ + tm6000_free_urb_buffers(dev); + kfree(dev->isoc_ctl.urb); + kfree(dev->isoc_ctl.transfer_buffer); + return -ENOMEM; + } + /* allocate urbs and transfer buffers */ for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { urb = usb_alloc_urb(max_packets, GFP_KERNEL); @@ -638,17 +716,8 @@ static int tm6000_prepare_isoc(struct tm6000_core *dev) } dev->isoc_ctl.urb[i] = urb; - dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(dev->udev, - sb_size, GFP_KERNEL, &urb->transfer_dma); - if (!dev->isoc_ctl.transfer_buffer[i]) { - tm6000_err("unable to allocate %i bytes for transfer" - " buffer %i%s\n", - sb_size, i, - in_interrupt() ? " while in int" : ""); - tm6000_uninit_isoc(dev); - return -ENOMEM; - } - memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size); + urb->transfer_dma = dev->urb_dma[i]; + dev->isoc_ctl.transfer_buffer[i] = dev->urb_buffer[i]; usb_fill_bulk_urb(urb, dev->udev, pipe, dev->isoc_ctl.transfer_buffer[i], sb_size, @@ -1826,6 +1895,9 @@ int tm6000_v4l2_unregister(struct tm6000_core *dev) { video_unregister_device(dev->vfd); + /* if URB buffers are still allocated free them now */ + tm6000_free_urb_buffers(dev); + if (dev->radio_dev) { if (video_is_registered(dev->radio_dev)) video_unregister_device(dev->radio_dev); @@ -1851,3 +1923,5 @@ MODULE_PARM_DESC(debug, "activates debug info"); module_param(vid_limit, int, 0644); MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); +module_param(keep_urb, bool, 0); +MODULE_PARM_DESC(keep_urb, "Keep urb buffers allocated even when the device is closed by the user"); diff --git a/drivers/media/usb/tm6000/tm6000.h b/drivers/media/usb/tm6000/tm6000.h index 6df418658c9c..173dcd7a7284 100644 --- a/drivers/media/usb/tm6000/tm6000.h +++ b/drivers/media/usb/tm6000/tm6000.h @@ -264,6 +264,11 @@ struct tm6000_core { spinlock_t slock; + /* urb dma buffers */ + char **urb_buffer; + dma_addr_t *urb_dma; + unsigned int urb_size; + unsigned long quirks; }; diff --git a/drivers/media/usb/usbvision/usbvision-core.c b/drivers/media/usb/usbvision/usbvision-core.c index c9b2042f8bdf..816b1cffab7d 100644 --- a/drivers/media/usb/usbvision/usbvision-core.c +++ b/drivers/media/usb/usbvision/usbvision-core.c @@ -1169,7 +1169,7 @@ static void usbvision_parse_data(struct usb_usbvision *usbvision) if (newstate == parse_state_next_frame) { frame->grabstate = frame_state_done; - do_gettimeofday(&(frame->timestamp)); + v4l2_get_timestamp(&(frame->timestamp)); frame->sequence = usbvision->frame_num; spin_lock_irqsave(&usbvision->queue_lock, lock_flags); diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c index 5c36a57e6590..c6bc8ce67375 100644 --- a/drivers/media/usb/usbvision/usbvision-video.c +++ b/drivers/media/usb/usbvision/usbvision-video.c @@ -761,7 +761,7 @@ static int vidioc_querybuf(struct file *file, if (vb->index >= usbvision->num_frames) return -EINVAL; /* Updating the corresponding frame state */ - vb->flags = 0; + vb->flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; frame = &usbvision->frame[vb->index]; if (frame->grabstate >= frame_state_ready) vb->flags |= V4L2_BUF_FLAG_QUEUED; @@ -843,7 +843,8 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *vb) vb->memory = V4L2_MEMORY_MMAP; vb->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | - V4L2_BUF_FLAG_DONE; + V4L2_BUF_FLAG_DONE | + V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; vb->index = f->index; vb->sequence = f->sequence; vb->timestamp = f->timestamp; diff --git a/drivers/media/usb/zr364xx/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c index 39edd4442932..74d56df3347f 100644 --- a/drivers/media/usb/zr364xx/zr364xx.c +++ b/drivers/media/usb/zr364xx/zr364xx.c @@ -501,7 +501,6 @@ static void zr364xx_fillbuff(struct zr364xx_camera *cam, int jpgsize) { int pos = 0; - struct timeval ts; const char *tmpbuf; char *vbuf = videobuf_to_vmalloc(&buf->vb); unsigned long last_frame; @@ -530,8 +529,7 @@ static void zr364xx_fillbuff(struct zr364xx_camera *cam, /* tell v4l buffer was filled */ buf->vb.field_count = cam->frame_count * 2; - do_gettimeofday(&ts); - buf->vb.ts = ts; + v4l2_get_timestamp(&buf->vb.ts); buf->vb.state = VIDEOBUF_DONE; } @@ -559,7 +557,7 @@ static int zr364xx_got_frame(struct zr364xx_camera *cam, int jpgsize) goto unlock; } list_del(&buf->vb.queue); - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); DBG("[%p/%d] wakeup\n", buf, buf->vb.i); zr364xx_fillbuff(cam, buf, jpgsize); wake_up(&buf->vb.done); diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index 380ddd89fa4c..614316f9b7a4 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -978,3 +978,13 @@ const struct v4l2_frmsize_discrete *v4l2_find_nearest_format( return best; } EXPORT_SYMBOL_GPL(v4l2_find_nearest_format); + +void v4l2_get_timestamp(struct timeval *tv) +{ + struct timespec ts; + + ktime_get_ts(&ts); + tv->tv_sec = ts.tv_sec; + tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC; +} +EXPORT_SYMBOL_GPL(v4l2_get_timestamp); diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 438ea45d1074..da99cf727162 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -62,7 +62,7 @@ struct v4l2_m2m_dev { struct list_head job_queue; spinlock_t job_spinlock; - struct v4l2_m2m_ops *m2m_ops; + const struct v4l2_m2m_ops *m2m_ops; }; static struct v4l2_m2m_queue_ctx *get_queue_ctx(struct v4l2_m2m_ctx *m2m_ctx, @@ -519,7 +519,7 @@ EXPORT_SYMBOL(v4l2_m2m_mmap); * * Usually called from driver's probe() function. */ -struct v4l2_m2m_dev *v4l2_m2m_init(struct v4l2_m2m_ops *m2m_ops) +struct v4l2_m2m_dev *v4l2_m2m_init(const struct v4l2_m2m_ops *m2m_ops) { struct v4l2_m2m_dev *m2m_dev; diff --git a/drivers/media/v4l2-core/videobuf-core.c b/drivers/media/v4l2-core/videobuf-core.c index 5449e8aa984a..fb5ee5dd8fe9 100644 --- a/drivers/media/v4l2-core/videobuf-core.c +++ b/drivers/media/v4l2-core/videobuf-core.c @@ -340,7 +340,7 @@ static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b, break; } - b->flags = 0; + b->flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; if (vb->map) b->flags |= V4L2_BUF_FLAG_MAPPED; diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 9f81be23a81f..85e3c221dadc 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -40,9 +40,10 @@ module_param(debug, int, 0644); #define call_qop(q, op, args...) \ (((q)->ops->op) ? ((q)->ops->op(args)) : 0) -#define V4L2_BUFFER_STATE_FLAGS (V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | \ +#define V4L2_BUFFER_MASK_FLAGS (V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | \ V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_ERROR | \ - V4L2_BUF_FLAG_PREPARED) + V4L2_BUF_FLAG_PREPARED | \ + V4L2_BUF_FLAG_TIMESTAMP_MASK) /** * __vb2_buf_mem_alloc() - allocate video memory for the given buffer @@ -401,7 +402,8 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b) /* * Clear any buffer state related flags. */ - b->flags &= ~V4L2_BUFFER_STATE_FLAGS; + b->flags &= ~V4L2_BUFFER_MASK_FLAGS; + b->flags |= V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; switch (vb->state) { case VB2_BUF_STATE_QUEUED: @@ -939,7 +941,7 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b vb->v4l2_buf.field = b->field; vb->v4l2_buf.timestamp = b->timestamp; - vb->v4l2_buf.flags = b->flags & ~V4L2_BUFFER_STATE_FLAGS; + vb->v4l2_buf.flags = b->flags & ~V4L2_BUFFER_MASK_FLAGS; } /** diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 427218b8b10f..ae0abc350e34 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -23,6 +23,8 @@ source "drivers/staging/media/as102/Kconfig" source "drivers/staging/media/cxd2099/Kconfig" +source "drivers/staging/media/davinci_vpfe/Kconfig" + source "drivers/staging/media/dt3155v4l/Kconfig" source "drivers/staging/media/go7007/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index aec6eb963940..2b97cae99499 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_LIRC_STAGING) += lirc/ obj-$(CONFIG_SOLO6X10) += solo6x10/ obj-$(CONFIG_VIDEO_DT3155) += dt3155v4l/ obj-$(CONFIG_VIDEO_GO7007) += go7007/ +obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/ diff --git a/drivers/staging/media/as102/as10x_cmd_cfg.c b/drivers/staging/media/as102/as10x_cmd_cfg.c index d2a4bce89623..4a2bbd766655 100644 --- a/drivers/staging/media/as102/as10x_cmd_cfg.c +++ b/drivers/staging/media/as102/as10x_cmd_cfg.c @@ -197,7 +197,7 @@ out: * @prsp: pointer to AS10x command response buffer * @proc_id: id of the command * - * Since the contex command reponse does not follow the common + * Since the contex command response does not follow the common * response, a specific parse function is required. * Return 0 on success or negative value in case of error. */ diff --git a/drivers/staging/media/cxd2099/cxd2099.c b/drivers/staging/media/cxd2099/cxd2099.c index 0ff19724992f..822c487592a4 100644 --- a/drivers/staging/media/cxd2099/cxd2099.c +++ b/drivers/staging/media/cxd2099/cxd2099.c @@ -66,8 +66,9 @@ static int i2c_write_reg(struct i2c_adapter *adapter, u8 adr, struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m, .len = 2}; if (i2c_transfer(adapter, &msg, 1) != 1) { - printk(KERN_ERR "Failed to write to I2C register %02x@%02x!\n", - reg, adr); + dev_err(&adapter->dev, + "Failed to write to I2C register %02x@%02x!\n", + reg, adr); return -1; } return 0; @@ -79,7 +80,7 @@ static int i2c_write(struct i2c_adapter *adapter, u8 adr, struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = data, .len = len}; if (i2c_transfer(adapter, &msg, 1) != 1) { - printk(KERN_ERR "Failed to write to I2C!\n"); + dev_err(&adapter->dev, "Failed to write to I2C!\n"); return -1; } return 0; @@ -94,7 +95,7 @@ static int i2c_read_reg(struct i2c_adapter *adapter, u8 adr, .buf = val, .len = 1} }; if (i2c_transfer(adapter, msgs, 2) != 2) { - printk(KERN_ERR "error in i2c_read_reg\n"); + dev_err(&adapter->dev, "error in i2c_read_reg\n"); return -1; } return 0; @@ -109,7 +110,7 @@ static int i2c_read(struct i2c_adapter *adapter, u8 adr, .buf = data, .len = n} }; if (i2c_transfer(adapter, msgs, 2) != 2) { - printk(KERN_ERR "error in i2c_read\n"); + dev_err(&adapter->dev, "error in i2c_read\n"); return -1; } return 0; @@ -277,7 +278,7 @@ static void cam_mode(struct cxd *ci, int mode) #ifdef BUFFER_MODE if (!ci->en.read_data) return; - printk(KERN_INFO "enable cam buffer mode\n"); + dev_info(&ci->i2c->dev, "enable cam buffer mode\n"); /* write_reg(ci, 0x0d, 0x00); */ /* write_reg(ci, 0x0e, 0x01); */ write_regm(ci, 0x08, 0x40, 0x40); @@ -524,7 +525,7 @@ static int slot_reset(struct dvb_ca_en50221 *ca, int slot) msleep(10); #if 0 read_reg(ci, 0x06, &val); - printk(KERN_INFO "%d:%02x\n", i, val); + dev_info(&ci->i2c->dev, "%d:%02x\n", i, val); if (!(val&0x10)) break; #else @@ -542,7 +543,7 @@ static int slot_shutdown(struct dvb_ca_en50221 *ca, int slot) { struct cxd *ci = ca->data; - printk(KERN_INFO "slot_shutdown\n"); + dev_info(&ci->i2c->dev, "slot_shutdown\n"); mutex_lock(&ci->lock); write_regm(ci, 0x09, 0x08, 0x08); write_regm(ci, 0x20, 0x80, 0x80); /* Reset CAM Mode */ @@ -578,10 +579,10 @@ static int campoll(struct cxd *ci) if (istat&0x40) { ci->dr = 1; - printk(KERN_INFO "DR\n"); + dev_info(&ci->i2c->dev, "DR\n"); } if (istat&0x20) - printk(KERN_INFO "WC\n"); + dev_info(&ci->i2c->dev, "WC\n"); if (istat&2) { u8 slotstat; @@ -597,7 +598,7 @@ static int campoll(struct cxd *ci) if (ci->slot_stat) { ci->slot_stat = 0; write_regm(ci, 0x03, 0x00, 0x08); - printk(KERN_INFO "NO CAM\n"); + dev_info(&ci->i2c->dev, "NO CAM\n"); ci->ready = 0; } } @@ -634,7 +635,7 @@ static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount) campoll(ci); mutex_unlock(&ci->lock); - printk(KERN_INFO "read_data\n"); + dev_info(&ci->i2c->dev, "read_data\n"); if (!ci->dr) return 0; @@ -687,7 +688,7 @@ struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg, u8 val; if (i2c_read_reg(i2c, cfg->adr, 0, &val) < 0) { - printk(KERN_INFO "No CXD2099 detected at %02x\n", cfg->adr); + dev_info(&i2c->dev, "No CXD2099 detected at %02x\n", cfg->adr); return NULL; } @@ -705,7 +706,7 @@ struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg, ci->en = en_templ; ci->en.data = ci; init(ci); - printk(KERN_INFO "Attached CXD2099AR at %02x\n", ci->cfg.adr); + dev_info(&i2c->dev, "Attached CXD2099AR at %02x\n", ci->cfg.adr); return &ci->en; } EXPORT_SYMBOL(cxd2099_attach); diff --git a/drivers/staging/media/cxd2099/cxd2099.h b/drivers/staging/media/cxd2099/cxd2099.h index 19c588a59588..0eb607c5b423 100644 --- a/drivers/staging/media/cxd2099/cxd2099.h +++ b/drivers/staging/media/cxd2099/cxd2099.h @@ -43,7 +43,7 @@ struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg, static inline struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg, void *priv, struct i2c_adapter *i2c) { - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + dev_warn(&i2c->dev, "%s: driver disabled by Kconfig\n", __func__); return NULL; } #endif diff --git a/drivers/staging/media/davinci_vpfe/Kconfig b/drivers/staging/media/davinci_vpfe/Kconfig new file mode 100644 index 000000000000..2e4a28b018e8 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/Kconfig @@ -0,0 +1,9 @@ +config VIDEO_DM365_VPFE + tristate "DM365 VPFE Media Controller Capture Driver" + depends on VIDEO_V4L2 && ARCH_DAVINCI_DM365 && !VIDEO_VPFE_CAPTURE + select VIDEOBUF2_DMA_CONTIG + help + Support for DM365 VPFE based Media Controller Capture driver. + + To compile this driver as a module, choose M here: the + module will be called vpfe-mc-capture. diff --git a/drivers/staging/media/davinci_vpfe/Makefile b/drivers/staging/media/davinci_vpfe/Makefile new file mode 100644 index 000000000000..c64515c644cd --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_VIDEO_DM365_VPFE) += \ + dm365_isif.o dm365_ipipe_hw.o dm365_ipipe.o \ + dm365_resizer.o dm365_ipipeif.o vpfe_mc_capture.o vpfe_video.o diff --git a/drivers/staging/media/davinci_vpfe/TODO b/drivers/staging/media/davinci_vpfe/TODO new file mode 100644 index 000000000000..7015ab35ded5 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/TODO @@ -0,0 +1,37 @@ +TODO (general): +================================== + +- User space interface refinement + - Controls should be used when possible rather than private ioctl + - No enums should be used + - Use of MC and V4L2 subdev APIs when applicable + - Single interface header might suffice + - Current interface forces to configure everything at once +- Get rid of the dm365_ipipe_hw.[ch] layer +- Active external sub-devices defined by link configuration; no strcmp + needed +- More generic platform data (i2c adapters) +- The driver should have no knowledge of possible external subdevs; see + struct vpfe_subdev_id +- Some of the hardware control should be refactorede +- Check proper serialisation (through mutexes and spinlocks) +- Names that are visible in kernel global namespace should have a common + prefix (or a few) +- While replacing the older driver in media folder, provide a compatibility + layer and compatibility tests that warrants (using the libv4l's LD_PRELOAD + approach) there is no regression for the users using the older driver. + +Building of uImage and Applications: +================================== + +As of now since the interface will undergo few changes all the include +files are present in staging itself, to build for dm365 follow below steps, + +- copy vpfe.h from drivers/staging/media/davinci_vpfe/ to + include/media/davinci/ folder for building the uImage. +- copy davinci_vpfe_user.h from drivers/staging/media/davinci_vpfe/ to + include/uapi/linux/davinci_vpfe.h, and add a entry in Kbuild (required + for building application). +- copy dm365_ipipeif_user.h from drivers/staging/media/davinci_vpfe/ to + include/uapi/linux/dm365_ipipeif.h and a entry in Kbuild (required + for building application). diff --git a/drivers/staging/media/davinci_vpfe/davinci-vpfe-mc.txt b/drivers/staging/media/davinci_vpfe/davinci-vpfe-mc.txt new file mode 100644 index 000000000000..1dbd56418704 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/davinci-vpfe-mc.txt @@ -0,0 +1,154 @@ +Davinci Video processing Front End (VPFE) driver + +Copyright (C) 2012 Texas Instruments Inc + +Contacts: Manjunath Hadli <manjunath.hadli@ti.com> + Prabhakar Lad <prabhakar.lad@ti.com> + + +Introduction +============ + +This file documents the Texas Instruments Davinci Video processing Front End +(VPFE) driver located under drivers/media/platform/davinci. The original driver +exists for Davinci VPFE, which is now being changed to Media Controller +Framework. + +Currently the driver has been successfully used on the following +version of Davinci: + + DM365/DM368 + +The driver implements V4L2, Media controller and v4l2_subdev interfaces. Sensor, +lens and flash drivers using the v4l2_subdev interface in the kernel are +supported. + + +Split to subdevs +================ + +The Davinci VPFE is split into V4L2 subdevs, each of the blocks inside the VPFE +having one subdev to represent it. Each of the subdevs provide a V4L2 subdev +interface to userspace. + + DAVINCI ISIF + DAVINCI IPIPEIF + DAVINCI IPIPE + DAVINCI CROP RESIZER + DAVINCI RESIZER A + DAVINCI RESIZER B + +Each possible link in the VPFE is modeled by a link in the Media controller +interface. For an example program see [1]. + + +ISIF, IPIPE, and RESIZER block IOCTLs +====================================== + +The Davinci Video processing Front End (VPFE) driver supports standard V4L2 +IOCTLs and controls where possible and practical. Much of the functions provided +by the VPFE, however, does not fall under the standard IOCTL's. + +In general, there is a private ioctl for configuring each of the blocks +containing hardware-dependent functions. + +The following private IOCTLs are supported: + + VIDIOC_VPFE_ISIF_[S/G]_RAW_PARAMS + VIDIOC_VPFE_IPIPE_[S/G]_CONFIG + VIDIOC_VPFE_RSZ_[S/G]_CONFIG + +The parameter structures used by these ioctl's are described in +include/uapi/linux/davinci_vpfe.h. + +The VIDIOC_VPFE_ISIF_S_RAW_PARAMS, VIDIOC_VPFE_IPIPE_S_CONFIG and +VIDIOC_VPFE_RSZ_S_CONFIG are used to configure, enable and disable functions in +the isif, ipipe and resizer blocks respectively. These IOCTL's control several +functions in the blocks they control. VIDIOC_VPFE_ISIF_S_RAW_PARAMS IOCTL +accepts a pointer to struct vpfe_isif_raw_config as its argument. Similarly +VIDIOC_VPFE_IPIPE_S_CONFIG accepts a pointer to struct vpfe_ipipe_config. And +VIDIOC_VPFE_RSZ_S_CONFIG accepts a pointer to struct vpfe_rsz_config as its +argument. Similarly VIDIOC_VPFE_ISIF_G_RAW_PARAMS, VIDIOC_VPFE_IPIPE_G_CONFIG +and VIDIOC_VPFE_RSZ_G_CONFIG are used to get the current configuration set in +the isif, ipipe and resizer blocks respectively. + +The detailed functions of the VPFE itself related to a given VPFE block is +described in the Technical Reference Manuals (TRMs) --- see the end of the +document for those. + + +IPIPEIF block IOCTLs +====================================== + +The following private IOCTLs are supported: + + VIDIOC_VPFE_IPIPEIF_[S/G]_CONFIG + +The parameter structures used by these ioctl's are described in +include/uapi/linux/dm365_ipipeif.h + +The VIDIOC_VPFE_IPIPEIF_S_CONFIG is used to configure the ipipeif +hardware block. The VIDIOC_VPFE_IPIPEIF_S_CONFIG and +VIDIOC_VPFE_IPIPEIF_G_CONFIG accepts a pointer to struct ipipeif_params +as its argument. + + +VPFE Operating Modes +========================================== + +a: Continuous Modes +------------------------ + +1: tvp514x/tvp7002/mt9p031---> DAVINCI ISIF---> SDRAM + +2: tvp514x/tvp7002/mt9p031---> DAVINCI ISIF---> DAVINCI IPIPEIF--->| + | + <--------------------<----------------<---------------------<---| + | + V + DAVINCI CROP RESIZER--->DAVINCI RESIZER [A/B]---> SDRAM + +3: tvp514x/tvp7002/mt9p031---> DAVINCI ISIF---> DAVINCI IPIPEIF--->| + | + <--------------------<----------------<---------------------<---| + | + V + DAVINCI IPIPE---> DAVINCI CROP RESIZER--->DAVINCI RESIZER [A/B]---> SDRAM + +a: Single Shot Modes +------------------------ + +1: SDRAM---> DAVINCI IPIPEIF---> DAVINCI IPIPE---> DAVINCI CROP RESIZER--->| + | + <----------------<----------------<------------------<---------------<--| + | + V +DAVINCI RESIZER [A/B]---> SDRAM + +2: SDRAM---> DAVINCI IPIPEIF---> DAVINCI CROP RESIZER--->| + | + <----------------<----------------<---------------<---| + | + V +DAVINCI RESIZER [A/B]---> SDRAM + + +Technical reference manuals (TRMs) and other documentation +========================================================== + +Davinci DM365 TRM: +<URL:http://www.ti.com/lit/ds/sprs457e/sprs457e.pdf> +Referenced MARCH 2009-REVISED JUNE 2011 + +Davinci DM368 TRM: +<URL:http://www.ti.com/lit/ds/sprs668c/sprs668c.pdf> +Referenced APRIL 2010-REVISED JUNE 2011 + +Davinci Video Processing Front End (VPFE) DM36x +<URL:http://www.ti.com/lit/ug/sprufg8c/sprufg8c.pdf> + + +References +========== + +[1] http://git.ideasonboard.org/?p=media-ctl.git;a=summary diff --git a/drivers/staging/media/davinci_vpfe/davinci_vpfe_user.h b/drivers/staging/media/davinci_vpfe/davinci_vpfe_user.h new file mode 100644 index 000000000000..7b7e7b26c1e8 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/davinci_vpfe_user.h @@ -0,0 +1,1290 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + */ + +#ifndef _DAVINCI_VPFE_USER_H +#define _DAVINCI_VPFE_USER_H + +#include <linux/types.h> +#include <linux/videodev2.h> + +/* + * Private IOCTL + * + * VIDIOC_VPFE_ISIF_S_RAW_PARAMS: Set raw params in isif + * VIDIOC_VPFE_ISIF_G_RAW_PARAMS: Get raw params from isif + * VIDIOC_VPFE_PRV_S_CONFIG: Set ipipe engine configuration + * VIDIOC_VPFE_PRV_G_CONFIG: Get ipipe engine configuration + * VIDIOC_VPFE_RSZ_S_CONFIG: Set resizer engine configuration + * VIDIOC_VPFE_RSZ_G_CONFIG: Get resizer engine configuration + */ + +#define VIDIOC_VPFE_ISIF_S_RAW_PARAMS \ + _IOW('V', BASE_VIDIOC_PRIVATE + 1, struct vpfe_isif_raw_config) +#define VIDIOC_VPFE_ISIF_G_RAW_PARAMS \ + _IOR('V', BASE_VIDIOC_PRIVATE + 2, struct vpfe_isif_raw_config) +#define VIDIOC_VPFE_IPIPE_S_CONFIG \ + _IOWR('P', BASE_VIDIOC_PRIVATE + 3, struct vpfe_ipipe_config) +#define VIDIOC_VPFE_IPIPE_G_CONFIG \ + _IOWR('P', BASE_VIDIOC_PRIVATE + 4, struct vpfe_ipipe_config) +#define VIDIOC_VPFE_RSZ_S_CONFIG \ + _IOWR('R', BASE_VIDIOC_PRIVATE + 5, struct vpfe_rsz_config) +#define VIDIOC_VPFE_RSZ_G_CONFIG \ + _IOWR('R', BASE_VIDIOC_PRIVATE + 6, struct vpfe_rsz_config) + +/* + * Private Control's for ISIF + */ +#define VPFE_ISIF_CID_CRGAIN (V4L2_CID_USER_BASE | 0xa001) +#define VPFE_ISIF_CID_CGRGAIN (V4L2_CID_USER_BASE | 0xa002) +#define VPFE_ISIF_CID_CGBGAIN (V4L2_CID_USER_BASE | 0xa003) +#define VPFE_ISIF_CID_CBGAIN (V4L2_CID_USER_BASE | 0xa004) +#define VPFE_ISIF_CID_GAIN_OFFSET (V4L2_CID_USER_BASE | 0xa005) + +/* + * Private Control's for ISIF and IPIPEIF + */ +#define VPFE_CID_DPCM_PREDICTOR (V4L2_CID_USER_BASE | 0xa006) + +/************************************************************************ + * Vertical Defect Correction parameters + ***********************************************************************/ + +/** + * vertical defect correction methods + */ +enum vpfe_isif_vdfc_corr_mode { + /* Defect level subtraction. Just fed through if saturating */ + VPFE_ISIF_VDFC_NORMAL, + /** + * Defect level subtraction. Horizontal interpolation ((i-2)+(i+2))/2 + * if data saturating + */ + VPFE_ISIF_VDFC_HORZ_INTERPOL_IF_SAT, + /* Horizontal interpolation (((i-2)+(i+2))/2) */ + VPFE_ISIF_VDFC_HORZ_INTERPOL +}; + +/** + * Max Size of the Vertical Defect Correction table + */ +#define VPFE_ISIF_VDFC_TABLE_SIZE 8 + +/** + * Values used for shifting up the vdfc defect level + */ +enum vpfe_isif_vdfc_shift { + /* No Shift */ + VPFE_ISIF_VDFC_NO_SHIFT, + /* Shift by 1 bit */ + VPFE_ISIF_VDFC_SHIFT_1, + /* Shift by 2 bit */ + VPFE_ISIF_VDFC_SHIFT_2, + /* Shift by 3 bit */ + VPFE_ISIF_VDFC_SHIFT_3, + /* Shift by 4 bit */ + VPFE_ISIF_VDFC_SHIFT_4 +}; + +/** + * Defect Correction (DFC) table entry + */ +struct vpfe_isif_vdfc_entry { + /* vertical position of defect */ + unsigned short pos_vert; + /* horizontal position of defect */ + unsigned short pos_horz; + /** + * Defect level of Vertical line defect position. This is subtracted + * from the data at the defect position + */ + unsigned char level_at_pos; + /** + * Defect level of the pixels upper than the vertical line defect. + * This is subtracted from the data + */ + unsigned char level_up_pixels; + /** + * Defect level of the pixels lower than the vertical line defect. + * This is subtracted from the data + */ + unsigned char level_low_pixels; +}; + +/** + * Structure for Defect Correction (DFC) parameter + */ +struct vpfe_isif_dfc { + /* enable vertical defect correction */ + unsigned char en; + /* Correction methods */ + enum vpfe_isif_vdfc_corr_mode corr_mode; + /** + * 0 - whole line corrected, 1 - not + * pixels upper than the defect + */ + unsigned char corr_whole_line; + /** + * defect level shift value. level_at_pos, level_upper_pos, + * and level_lower_pos can be shifted up by this value + */ + enum vpfe_isif_vdfc_shift def_level_shift; + /* defect saturation level */ + unsigned short def_sat_level; + /* number of vertical defects. Max is VPFE_ISIF_VDFC_TABLE_SIZE */ + short num_vdefects; + /* VDFC table ptr */ + struct vpfe_isif_vdfc_entry table[VPFE_ISIF_VDFC_TABLE_SIZE]; +}; + +/************************************************************************ +* Digital/Black clamp or DC Subtract parameters +************************************************************************/ +/** + * Horizontal Black Clamp modes + */ +enum vpfe_isif_horz_bc_mode { + /** + * Horizontal clamp disabled. Only vertical clamp + * value is subtracted + */ + VPFE_ISIF_HORZ_BC_DISABLE, + /** + * Horizontal clamp value is calculated and subtracted + * from image data along with vertical clamp value + */ + VPFE_ISIF_HORZ_BC_CLAMP_CALC_ENABLED, + /** + * Horizontal clamp value calculated from previous image + * is subtracted from image data along with vertical clamp + * value. How the horizontal clamp value for the first image + * is calculated in this case ??? + */ + VPFE_ISIF_HORZ_BC_CLAMP_NOT_UPDATED +}; + +/** + * Base window selection for Horizontal Black Clamp calculations + */ +enum vpfe_isif_horz_bc_base_win_sel { + /* Select Most left window for bc calculation */ + VPFE_ISIF_SEL_MOST_LEFT_WIN, + + /* Select Most right window for bc calculation */ + VPFE_ISIF_SEL_MOST_RIGHT_WIN, +}; + +/* Size of window in horizontal direction for horizontal bc */ +enum vpfe_isif_horz_bc_sz_h { + VPFE_ISIF_HORZ_BC_SZ_H_2PIXELS, + VPFE_ISIF_HORZ_BC_SZ_H_4PIXELS, + VPFE_ISIF_HORZ_BC_SZ_H_8PIXELS, + VPFE_ISIF_HORZ_BC_SZ_H_16PIXELS +}; + +/* Size of window in vertcal direction for vertical bc */ +enum vpfe_isif_horz_bc_sz_v { + VPFE_ISIF_HORZ_BC_SZ_H_32PIXELS, + VPFE_ISIF_HORZ_BC_SZ_H_64PIXELS, + VPFE_ISIF_HORZ_BC_SZ_H_128PIXELS, + VPFE_ISIF_HORZ_BC_SZ_H_256PIXELS +}; + +/** + * Structure for Horizontal Black Clamp config params + */ +struct vpfe_isif_horz_bclamp { + /* horizontal clamp mode */ + enum vpfe_isif_horz_bc_mode mode; + /** + * pixel value limit enable. + * 0 - limit disabled + * 1 - pixel value limited to 1023 + */ + unsigned char clamp_pix_limit; + /** + * Select most left or right window for clamp val + * calculation + */ + enum vpfe_isif_horz_bc_base_win_sel base_win_sel_calc; + /* Window count per color for calculation. range 1-32 */ + unsigned char win_count_calc; + /* Window start position - horizontal for calculation. 0 - 8191 */ + unsigned short win_start_h_calc; + /* Window start position - vertical for calculation 0 - 8191 */ + unsigned short win_start_v_calc; + /* Width of the sample window in pixels for calculation */ + enum vpfe_isif_horz_bc_sz_h win_h_sz_calc; + /* Height of the sample window in pixels for calculation */ + enum vpfe_isif_horz_bc_sz_v win_v_sz_calc; +}; + +/** + * Black Clamp vertical reset values + */ +enum vpfe_isif_vert_bc_reset_val_sel { + /* Reset value used is the clamp value calculated */ + VPFE_ISIF_VERT_BC_USE_HORZ_CLAMP_VAL, + /* Reset value used is reset_clamp_val configured */ + VPFE_ISIF_VERT_BC_USE_CONFIG_CLAMP_VAL, + /* No update, previous image value is used */ + VPFE_ISIF_VERT_BC_NO_UPDATE +}; + +enum vpfe_isif_vert_bc_sz_h { + VPFE_ISIF_VERT_BC_SZ_H_2PIXELS, + VPFE_ISIF_VERT_BC_SZ_H_4PIXELS, + VPFE_ISIF_VERT_BC_SZ_H_8PIXELS, + VPFE_ISIF_VERT_BC_SZ_H_16PIXELS, + VPFE_ISIF_VERT_BC_SZ_H_32PIXELS, + VPFE_ISIF_VERT_BC_SZ_H_64PIXELS +}; + +/** + * Structure for Vertical Black Clamp configuration params + */ +struct vpfe_isif_vert_bclamp { + /* Reset value selection for vertical clamp calculation */ + enum vpfe_isif_vert_bc_reset_val_sel reset_val_sel; + /* U12 value if reset_sel = ISIF_BC_VERT_USE_CONFIG_CLAMP_VAL */ + unsigned short reset_clamp_val; + /** + * U8Q8. Line average coefficient used in vertical clamp + * calculation + */ + unsigned char line_ave_coef; + /* Width in pixels of the optical black region used for calculation. */ + enum vpfe_isif_vert_bc_sz_h ob_h_sz_calc; + /* Height of the optical black region for calculation */ + unsigned short ob_v_sz_calc; + /* Optical black region start position - horizontal. 0 - 8191 */ + unsigned short ob_start_h; + /* Optical black region start position - vertical 0 - 8191 */ + unsigned short ob_start_v; +}; + +/** + * Structure for Black Clamp configuration params + */ +struct vpfe_isif_black_clamp { + /** + * this offset value is added irrespective of the clamp + * enable status. S13 + */ + unsigned short dc_offset; + /** + * Enable black/digital clamp value to be subtracted + * from the image data + */ + unsigned char en; + /** + * black clamp mode. same/separate clamp for 4 colors + * 0 - disable - same clamp value for all colors + * 1 - clamp value calculated separately for all colors + */ + unsigned char bc_mode_color; + /* Vertical start position for bc subtraction */ + unsigned short vert_start_sub; + /* Black clamp for horizontal direction */ + struct vpfe_isif_horz_bclamp horz; + /* Black clamp for vertical direction */ + struct vpfe_isif_vert_bclamp vert; +}; + +/************************************************************************* +** Color Space Conversion (CSC) +*************************************************************************/ +/** + * Number of Coefficient values used for CSC + */ +#define VPFE_ISIF_CSC_NUM_COEFF 16 + +struct float_8_bit { + /* 8 bit integer part */ + __u8 integer; + /* 8 bit decimal part */ + __u8 decimal; +}; + +struct float_16_bit { + /* 16 bit integer part */ + __u16 integer; + /* 16 bit decimal part */ + __u16 decimal; +}; + +/************************************************************************* +** Color Space Conversion parameters +*************************************************************************/ +/** + * Structure used for CSC config params + */ +struct vpfe_isif_color_space_conv { + /* Enable color space conversion */ + unsigned char en; + /** + * csc coefficient table. S8Q5, M00 at index 0, M01 at index 1, and + * so forth + */ + struct float_8_bit coeff[VPFE_ISIF_CSC_NUM_COEFF]; +}; + +enum vpfe_isif_datasft { + /* No Shift */ + VPFE_ISIF_NO_SHIFT, + /* 1 bit Shift */ + VPFE_ISIF_1BIT_SHIFT, + /* 2 bit Shift */ + VPFE_ISIF_2BIT_SHIFT, + /* 3 bit Shift */ + VPFE_ISIF_3BIT_SHIFT, + /* 4 bit Shift */ + VPFE_ISIF_4BIT_SHIFT, + /* 5 bit Shift */ + VPFE_ISIF_5BIT_SHIFT, + /* 6 bit Shift */ + VPFE_ISIF_6BIT_SHIFT +}; + +#define VPFE_ISIF_LINEAR_TAB_SIZE 192 +/************************************************************************* +** Linearization parameters +*************************************************************************/ +/** + * Structure for Sensor data linearization + */ +struct vpfe_isif_linearize { + /* Enable or Disable linearization of data */ + unsigned char en; + /* Shift value applied */ + enum vpfe_isif_datasft corr_shft; + /* scale factor applied U11Q10 */ + struct float_16_bit scale_fact; + /* Size of the linear table */ + unsigned short table[VPFE_ISIF_LINEAR_TAB_SIZE]; +}; + +/************************************************************************* +** ISIF Raw configuration parameters +*************************************************************************/ +enum vpfe_isif_fmt_mode { + VPFE_ISIF_SPLIT, + VPFE_ISIF_COMBINE +}; + +enum vpfe_isif_lnum { + VPFE_ISIF_1LINE, + VPFE_ISIF_2LINES, + VPFE_ISIF_3LINES, + VPFE_ISIF_4LINES +}; + +enum vpfe_isif_line { + VPFE_ISIF_1STLINE, + VPFE_ISIF_2NDLINE, + VPFE_ISIF_3RDLINE, + VPFE_ISIF_4THLINE +}; + +struct vpfe_isif_fmtplen { + /** + * number of program entries for SET0, range 1 - 16 + * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is + * ISIF_COMBINE + */ + unsigned short plen0; + /** + * number of program entries for SET1, range 1 - 16 + * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is + * ISIF_COMBINE + */ + unsigned short plen1; + /** + * number of program entries for SET2, range 1 - 16 + * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is + * ISIF_COMBINE + */ + unsigned short plen2; + /** + * number of program entries for SET3, range 1 - 16 + * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is + * ISIF_COMBINE + */ + unsigned short plen3; +}; + +struct vpfe_isif_fmt_cfg { + /* Split or combine or line alternate */ + enum vpfe_isif_fmt_mode fmtmode; + /* enable or disable line alternating mode */ + unsigned char ln_alter_en; + /* Split/combine line number */ + enum vpfe_isif_lnum lnum; + /* Address increment Range 1 - 16 */ + unsigned int addrinc; +}; + +struct vpfe_isif_fmt_addr_ptr { + /* Initial address */ + unsigned int init_addr; + /* output line number */ + enum vpfe_isif_line out_line; +}; + +struct vpfe_isif_fmtpgm_ap { + /* program address pointer */ + unsigned char pgm_aptr; + /* program address increment or decrement */ + unsigned char pgmupdt; +}; + +struct vpfe_isif_data_formatter { + /* Enable/Disable data formatter */ + unsigned char en; + /* data formatter configuration */ + struct vpfe_isif_fmt_cfg cfg; + /* Formatter program entries length */ + struct vpfe_isif_fmtplen plen; + /* first pixel in a line fed to formatter */ + unsigned short fmtrlen; + /* HD interval for output line. Only valid when split line */ + unsigned short fmthcnt; + /* formatter address pointers */ + struct vpfe_isif_fmt_addr_ptr fmtaddr_ptr[16]; + /* program enable/disable */ + unsigned char pgm_en[32]; + /* program address pointers */ + struct vpfe_isif_fmtpgm_ap fmtpgm_ap[32]; +}; + +struct vpfe_isif_df_csc { + /* Color Space Conversion configuration, 0 - csc, 1 - df */ + unsigned int df_or_csc; + /* csc configuration valid if df_or_csc is 0 */ + struct vpfe_isif_color_space_conv csc; + /* data formatter configuration valid if df_or_csc is 1 */ + struct vpfe_isif_data_formatter df; + /* start pixel in a line at the input */ + unsigned int start_pix; + /* number of pixels in input line */ + unsigned int num_pixels; + /* start line at the input */ + unsigned int start_line; + /* number of lines at the input */ + unsigned int num_lines; +}; + +struct vpfe_isif_gain_offsets_adj { + /* Enable or Disable Gain adjustment for SDRAM data */ + unsigned char gain_sdram_en; + /* Enable or Disable Gain adjustment for IPIPE data */ + unsigned char gain_ipipe_en; + /* Enable or Disable Gain adjustment for H3A data */ + unsigned char gain_h3a_en; + /* Enable or Disable Gain adjustment for SDRAM data */ + unsigned char offset_sdram_en; + /* Enable or Disable Gain adjustment for IPIPE data */ + unsigned char offset_ipipe_en; + /* Enable or Disable Gain adjustment for H3A data */ + unsigned char offset_h3a_en; +}; + +struct vpfe_isif_cul { + /* Horizontal Cull pattern for odd lines */ + unsigned char hcpat_odd; + /* Horizontal Cull pattern for even lines */ + unsigned char hcpat_even; + /* Vertical Cull pattern */ + unsigned char vcpat; + /* Enable or disable lpf. Apply when cull is enabled */ + unsigned char en_lpf; +}; + +/* all the stuff in this struct will be provided by userland */ +struct vpfe_isif_raw_config { + /* Linearization parameters for image sensor data input */ + struct vpfe_isif_linearize linearize; + /* Data formatter or CSC */ + struct vpfe_isif_df_csc df_csc; + /* Defect Pixel Correction (DFC) confguration */ + struct vpfe_isif_dfc dfc; + /* Black/Digital Clamp configuration */ + struct vpfe_isif_black_clamp bclamp; + /* Gain, offset adjustments */ + struct vpfe_isif_gain_offsets_adj gain_offset; + /* Culling */ + struct vpfe_isif_cul culling; + /* horizontal offset for Gain/LSC/DFC */ + unsigned short horz_offset; + /* vertical offset for Gain/LSC/DFC */ + unsigned short vert_offset; +}; + +/********************************************************************** + IPIPE API Structures +**********************************************************************/ + +/* IPIPE module configurations */ + +/* IPIPE input configuration */ +#define VPFE_IPIPE_INPUT_CONFIG (1 << 0) +/* LUT based Defect Pixel Correction */ +#define VPFE_IPIPE_LUTDPC (1 << 1) +/* On the fly (OTF) Defect Pixel Correction */ +#define VPFE_IPIPE_OTFDPC (1 << 2) +/* Noise Filter - 1 */ +#define VPFE_IPIPE_NF1 (1 << 3) +/* Noise Filter - 2 */ +#define VPFE_IPIPE_NF2 (1 << 4) +/* White Balance. Also a control ID */ +#define VPFE_IPIPE_WB (1 << 5) +/* 1st RGB to RBG Blend module */ +#define VPFE_IPIPE_RGB2RGB_1 (1 << 6) +/* 2nd RGB to RBG Blend module */ +#define VPFE_IPIPE_RGB2RGB_2 (1 << 7) +/* Gamma Correction */ +#define VPFE_IPIPE_GAMMA (1 << 8) +/* 3D LUT color conversion */ +#define VPFE_IPIPE_3D_LUT (1 << 9) +/* RGB to YCbCr module */ +#define VPFE_IPIPE_RGB2YUV (1 << 10) +/* YUV 422 conversion module */ +#define VPFE_IPIPE_YUV422_CONV (1 << 11) +/* Edge Enhancement */ +#define VPFE_IPIPE_YEE (1 << 12) +/* Green Imbalance Correction */ +#define VPFE_IPIPE_GIC (1 << 13) +/* CFA Interpolation */ +#define VPFE_IPIPE_CFA (1 << 14) +/* Chroma Artifact Reduction */ +#define VPFE_IPIPE_CAR (1 << 15) +/* Chroma Gain Suppression */ +#define VPFE_IPIPE_CGS (1 << 16) +/* Global brightness and contrast control */ +#define VPFE_IPIPE_GBCE (1 << 17) + +#define VPFE_IPIPE_MAX_MODULES 18 + +struct ipipe_float_u16 { + unsigned short integer; + unsigned short decimal; +}; + +struct ipipe_float_s16 { + short integer; + unsigned short decimal; +}; + +struct ipipe_float_u8 { + unsigned char integer; + unsigned char decimal; +}; + +/* Copy method selection for vertical correction + * Used when ipipe_dfc_corr_meth is IPIPE_DPC_CTORB_AFTER_HINT + */ +enum vpfe_ipipe_dpc_corr_meth { + /* replace by black or white dot specified by repl_white */ + VPFE_IPIPE_DPC_REPL_BY_DOT = 0, + /* Copy from left */ + VPFE_IPIPE_DPC_CL = 1, + /* Copy from right */ + VPFE_IPIPE_DPC_CR = 2, + /* Horizontal interpolation */ + VPFE_IPIPE_DPC_H_INTP = 3, + /* Vertical interpolation */ + VPFE_IPIPE_DPC_V_INTP = 4, + /* Copy from top */ + VPFE_IPIPE_DPC_CT = 5, + /* Copy from bottom */ + VPFE_IPIPE_DPC_CB = 6, + /* 2D interpolation */ + VPFE_IPIPE_DPC_2D_INTP = 7, +}; + +struct vpfe_ipipe_lutdpc_entry { + /* Horizontal position */ + unsigned short horz_pos; + /* vertical position */ + unsigned short vert_pos; + enum vpfe_ipipe_dpc_corr_meth method; +}; + +#define VPFE_IPIPE_MAX_SIZE_DPC 256 + +/* Structure for configuring DPC module */ +struct vpfe_ipipe_lutdpc { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* 0 - replace with black dot, 1 - white dot when correction + * method is IPIPE_DFC_REPL_BY_DOT=0, + */ + unsigned char repl_white; + /* number of entries in the correction table. Currently only + * support up-to 256 entries. infinite mode is not supported + */ + unsigned short dpc_size; + struct vpfe_ipipe_lutdpc_entry table[VPFE_IPIPE_MAX_SIZE_DPC]; +}; + +enum vpfe_ipipe_otfdpc_det_meth { + VPFE_IPIPE_DPC_OTF_MIN_MAX, + VPFE_IPIPE_DPC_OTF_MIN_MAX2 +}; + +struct vpfe_ipipe_otfdpc_thr { + unsigned short r; + unsigned short gr; + unsigned short gb; + unsigned short b; +}; + +enum vpfe_ipipe_otfdpc_alg { + VPFE_IPIPE_OTFDPC_2_0, + VPFE_IPIPE_OTFDPC_3_0 +}; + +struct vpfe_ipipe_otfdpc_2_0_cfg { + /* defect detection threshold for MIN_MAX2 method (DPC 2.0 alg) */ + struct vpfe_ipipe_otfdpc_thr det_thr; + /* defect correction threshold for MIN_MAX2 method (DPC 2.0 alg) or + * maximum value for MIN_MAX method + */ + struct vpfe_ipipe_otfdpc_thr corr_thr; +}; + +struct vpfe_ipipe_otfdpc_3_0_cfg { + /* DPC3.0 activity adj shf. activity = (max2-min2) >> (6 -shf) + */ + unsigned char act_adj_shf; + /* DPC3.0 detection threshold, THR */ + unsigned short det_thr; + /* DPC3.0 detection threshold slope, SLP */ + unsigned short det_slp; + /* DPC3.0 detection threshold min, MIN */ + unsigned short det_thr_min; + /* DPC3.0 detection threshold max, MAX */ + unsigned short det_thr_max; + /* DPC3.0 correction threshold, THR */ + unsigned short corr_thr; + /* DPC3.0 correction threshold slope, SLP */ + unsigned short corr_slp; + /* DPC3.0 correction threshold min, MIN */ + unsigned short corr_thr_min; + /* DPC3.0 correction threshold max, MAX */ + unsigned short corr_thr_max; +}; + +struct vpfe_ipipe_otfdpc { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* defect detection method */ + enum vpfe_ipipe_otfdpc_det_meth det_method; + /* Algorithm used. Applicable only when IPIPE_DPC_OTF_MIN_MAX2 is + * used + */ + enum vpfe_ipipe_otfdpc_alg alg; + union { + /* if alg is IPIPE_OTFDPC_2_0 */ + struct vpfe_ipipe_otfdpc_2_0_cfg dpc_2_0; + /* if alg is IPIPE_OTFDPC_3_0 */ + struct vpfe_ipipe_otfdpc_3_0_cfg dpc_3_0; + } alg_cfg; +}; + +/* Threshold values table size */ +#define VPFE_IPIPE_NF_THR_TABLE_SIZE 8 +/* Intensity values table size */ +#define VPFE_IPIPE_NF_STR_TABLE_SIZE 8 + +/* NF, sampling method for green pixels */ +enum vpfe_ipipe_nf_sampl_meth { + /* Same as R or B */ + VPFE_IPIPE_NF_BOX, + /* Diamond mode */ + VPFE_IPIPE_NF_DIAMOND +}; + +/* Structure for configuring NF module */ +struct vpfe_ipipe_nf { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* Sampling method for green pixels */ + enum vpfe_ipipe_nf_sampl_meth gr_sample_meth; + /* Down shift value in LUT reference address + */ + unsigned char shft_val; + /* Spread value in NF algorithm + */ + unsigned char spread_val; + /* Apply LSC gain to threshold. Enable this only if + * LSC is enabled in ISIF + */ + unsigned char apply_lsc_gain; + /* Threshold values table */ + unsigned short thr[VPFE_IPIPE_NF_THR_TABLE_SIZE]; + /* intensity values table */ + unsigned char str[VPFE_IPIPE_NF_STR_TABLE_SIZE]; + /* Edge detection minimum threshold */ + unsigned short edge_det_min_thr; + /* Edge detection maximum threshold */ + unsigned short edge_det_max_thr; +}; + +enum vpfe_ipipe_gic_alg { + VPFE_IPIPE_GIC_ALG_CONST_GAIN, + VPFE_IPIPE_GIC_ALG_ADAPT_GAIN +}; + +enum vpfe_ipipe_gic_thr_sel { + VPFE_IPIPE_GIC_THR_REG, + VPFE_IPIPE_GIC_THR_NF +}; + +enum vpfe_ipipe_gic_wt_fn_type { + /* Use difference as index */ + VPFE_IPIPE_GIC_WT_FN_TYP_DIF, + /* Use weight function as index */ + VPFE_IPIPE_GIC_WT_FN_TYP_HP_VAL +}; + +/* structure for Green Imbalance Correction */ +struct vpfe_ipipe_gic { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* 0 - Constant gain , 1 - Adaptive gain algorithm */ + enum vpfe_ipipe_gic_alg gic_alg; + /* GIC gain or weight. Used for Constant gain and Adaptive algorithms + */ + unsigned short gain; + /* Threshold selection. GIC register values or NF2 thr table */ + enum vpfe_ipipe_gic_thr_sel thr_sel; + /* thr1. Used when thr_sel is IPIPE_GIC_THR_REG */ + unsigned short thr; + /* this value is used for thr2-thr1, thr3-thr2 or + * thr4-thr3 when wt_fn_type is index. Otherwise it + * is the + */ + unsigned short slope; + /* Apply LSC gain to threshold. Enable this only if + * LSC is enabled in ISIF & thr_sel is IPIPE_GIC_THR_REG + */ + unsigned char apply_lsc_gain; + /* Multiply Nf2 threshold by this gain. Use this when thr_sel + * is IPIPE_GIC_THR_NF + */ + struct ipipe_float_u8 nf2_thr_gain; + /* Weight function uses difference as index or high pass value. + * Used for adaptive gain algorithm + */ + enum vpfe_ipipe_gic_wt_fn_type wt_fn_type; +}; + +/* Structure for configuring WB module */ +struct vpfe_ipipe_wb { + /* Offset (S12) for R */ + short ofst_r; + /* Offset (S12) for Gr */ + short ofst_gr; + /* Offset (S12) for Gb */ + short ofst_gb; + /* Offset (S12) for B */ + short ofst_b; + /* Gain (U13Q9) for Red */ + struct ipipe_float_u16 gain_r; + /* Gain (U13Q9) for Gr */ + struct ipipe_float_u16 gain_gr; + /* Gain (U13Q9) for Gb */ + struct ipipe_float_u16 gain_gb; + /* Gain (U13Q9) for Blue */ + struct ipipe_float_u16 gain_b; +}; + +enum vpfe_ipipe_cfa_alg { + /* Algorithm is 2DirAC */ + VPFE_IPIPE_CFA_ALG_2DIRAC, + /* Algorithm is 2DirAC + Digital Antialiasing (DAA) */ + VPFE_IPIPE_CFA_ALG_2DIRAC_DAA, + /* Algorithm is DAA */ + VPFE_IPIPE_CFA_ALG_DAA +}; + +/* Structure for CFA Interpolation */ +struct vpfe_ipipe_cfa { + /* 2DirAC or 2DirAC + DAA */ + enum vpfe_ipipe_cfa_alg alg; + /* 2Dir CFA HP value Low Threshold */ + unsigned short hpf_thr_2dir; + /* 2Dir CFA HP value slope */ + unsigned short hpf_slp_2dir; + /* 2Dir CFA HP mix threshold */ + unsigned short hp_mix_thr_2dir; + /* 2Dir CFA HP mix slope */ + unsigned short hp_mix_slope_2dir; + /* 2Dir Direction threshold */ + unsigned short dir_thr_2dir; + /* 2Dir Direction slope */ + unsigned short dir_slope_2dir; + /* 2Dir Non Directional Weight */ + unsigned short nd_wt_2dir; + /* DAA Mono Hue Fraction */ + unsigned short hue_fract_daa; + /* DAA Mono Edge threshold */ + unsigned short edge_thr_daa; + /* DAA Mono threshold minimum */ + unsigned short thr_min_daa; + /* DAA Mono threshold slope */ + unsigned short thr_slope_daa; + /* DAA Mono slope minimum */ + unsigned short slope_min_daa; + /* DAA Mono slope slope */ + unsigned short slope_slope_daa; + /* DAA Mono LP wight */ + unsigned short lp_wt_daa; +}; + +/* Struct for configuring RGB2RGB blending module */ +struct vpfe_ipipe_rgb2rgb { + /* Matrix coefficient for RR S12Q8 for ID = 1 and S11Q8 for ID = 2 */ + struct ipipe_float_s16 coef_rr; + /* Matrix coefficient for GR S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_gr; + /* Matrix coefficient for BR S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_br; + /* Matrix coefficient for RG S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_rg; + /* Matrix coefficient for GG S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_gg; + /* Matrix coefficient for BG S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_bg; + /* Matrix coefficient for RB S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_rb; + /* Matrix coefficient for GB S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_gb; + /* Matrix coefficient for BB S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_bb; + /* Output offset for R S13/S11 */ + int out_ofst_r; + /* Output offset for G S13/S11 */ + int out_ofst_g; + /* Output offset for B S13/S11 */ + int out_ofst_b; +}; + +#define VPFE_IPIPE_MAX_SIZE_GAMMA 512 + +enum vpfe_ipipe_gamma_tbl_size { + VPFE_IPIPE_GAMMA_TBL_SZ_64 = 64, + VPFE_IPIPE_GAMMA_TBL_SZ_128 = 128, + VPFE_IPIPE_GAMMA_TBL_SZ_256 = 256, + VPFE_IPIPE_GAMMA_TBL_SZ_512 = 512, +}; + +enum vpfe_ipipe_gamma_tbl_sel { + VPFE_IPIPE_GAMMA_TBL_RAM = 0, + VPFE_IPIPE_GAMMA_TBL_ROM = 1, +}; + +struct vpfe_ipipe_gamma_entry { + /* 10 bit slope */ + short slope; + /* 10 bit offset */ + unsigned short offset; +}; + +/* Structure for configuring Gamma correction module */ +struct vpfe_ipipe_gamma { + /* 0 - Enable Gamma correction for Red + * 1 - bypass Gamma correction. Data is divided by 16 + */ + unsigned char bypass_r; + /* 0 - Enable Gamma correction for Blue + * 1 - bypass Gamma correction. Data is divided by 16 + */ + unsigned char bypass_b; + /* 0 - Enable Gamma correction for Green + * 1 - bypass Gamma correction. Data is divided by 16 + */ + unsigned char bypass_g; + /* IPIPE_GAMMA_TBL_RAM or IPIPE_GAMMA_TBL_ROM */ + enum vpfe_ipipe_gamma_tbl_sel tbl_sel; + /* Table size for RAM gamma table. + */ + enum vpfe_ipipe_gamma_tbl_size tbl_size; + /* R table */ + struct vpfe_ipipe_gamma_entry table_r[VPFE_IPIPE_MAX_SIZE_GAMMA]; + /* Blue table */ + struct vpfe_ipipe_gamma_entry table_b[VPFE_IPIPE_MAX_SIZE_GAMMA]; + /* Green table */ + struct vpfe_ipipe_gamma_entry table_g[VPFE_IPIPE_MAX_SIZE_GAMMA]; +}; + +#define VPFE_IPIPE_MAX_SIZE_3D_LUT 729 + +struct vpfe_ipipe_3d_lut_entry { + /* 10 bit entry for red */ + unsigned short r; + /* 10 bit entry for green */ + unsigned short g; + /* 10 bit entry for blue */ + unsigned short b; +}; + +/* structure for 3D-LUT */ +struct vpfe_ipipe_3d_lut { + /* enable/disable 3D lut */ + unsigned char en; + /* 3D - LUT table entry */ + struct vpfe_ipipe_3d_lut_entry table[VPFE_IPIPE_MAX_SIZE_3D_LUT]; +}; + +/* Struct for configuring rgb2ycbcr module */ +struct vpfe_ipipe_rgb2yuv { + /* Matrix coefficient for RY S12Q8 */ + struct ipipe_float_s16 coef_ry; + /* Matrix coefficient for GY S12Q8 */ + struct ipipe_float_s16 coef_gy; + /* Matrix coefficient for BY S12Q8 */ + struct ipipe_float_s16 coef_by; + /* Matrix coefficient for RCb S12Q8 */ + struct ipipe_float_s16 coef_rcb; + /* Matrix coefficient for GCb S12Q8 */ + struct ipipe_float_s16 coef_gcb; + /* Matrix coefficient for BCb S12Q8 */ + struct ipipe_float_s16 coef_bcb; + /* Matrix coefficient for RCr S12Q8 */ + struct ipipe_float_s16 coef_rcr; + /* Matrix coefficient for GCr S12Q8 */ + struct ipipe_float_s16 coef_gcr; + /* Matrix coefficient for BCr S12Q8 */ + struct ipipe_float_s16 coef_bcr; + /* Output offset for R S11 */ + int out_ofst_y; + /* Output offset for Cb S11 */ + int out_ofst_cb; + /* Output offset for Cr S11 */ + int out_ofst_cr; +}; + +enum vpfe_ipipe_gbce_type { + VPFE_IPIPE_GBCE_Y_VAL_TBL = 0, + VPFE_IPIPE_GBCE_GAIN_TBL = 1, +}; + +#define VPFE_IPIPE_MAX_SIZE_GBCE_LUT 1024 + +/* structure for Global brightness and Contrast */ +struct vpfe_ipipe_gbce { + /* enable/disable GBCE */ + unsigned char en; + /* Y - value table or Gain table */ + enum vpfe_ipipe_gbce_type type; + /* ptr to LUT for GBCE with 1024 entries */ + unsigned short table[VPFE_IPIPE_MAX_SIZE_GBCE_LUT]; +}; + +/* Chrominance position. Applicable only for YCbCr input + * Applied after edge enhancement + */ +enum vpfe_chr_pos { + /* Co-siting, same position with luminance */ + VPFE_IPIPE_YUV422_CHR_POS_COSITE = 0, + /* Centering, In the middle of luminance */ + VPFE_IPIPE_YUV422_CHR_POS_CENTRE = 1, +}; + +/* Structure for configuring yuv422 conversion module */ +struct vpfe_ipipe_yuv422_conv { + /* Max Chrominance value */ + unsigned char en_chrom_lpf; + /* 1 - enable LPF for chrminance, 0 - disable */ + enum vpfe_chr_pos chrom_pos; +}; + +#define VPFE_IPIPE_MAX_SIZE_YEE_LUT 1024 + +enum vpfe_ipipe_yee_merge_meth { + VPFE_IPIPE_YEE_ABS_MAX = 0, + VPFE_IPIPE_YEE_EE_ES = 1, +}; + +/* Structure for configuring YUV Edge Enhancement module */ +struct vpfe_ipipe_yee { + /* 1 - enable enhancement, 0 - disable */ + unsigned char en; + /* enable/disable halo reduction in edge sharpner */ + unsigned char en_halo_red; + /* Merge method between Edge Enhancer and Edge sharpner */ + enum vpfe_ipipe_yee_merge_meth merge_meth; + /* HPF Shift length */ + unsigned char hpf_shft; + /* HPF Coefficient 00, S10 */ + short hpf_coef_00; + /* HPF Coefficient 01, S10 */ + short hpf_coef_01; + /* HPF Coefficient 02, S10 */ + short hpf_coef_02; + /* HPF Coefficient 10, S10 */ + short hpf_coef_10; + /* HPF Coefficient 11, S10 */ + short hpf_coef_11; + /* HPF Coefficient 12, S10 */ + short hpf_coef_12; + /* HPF Coefficient 20, S10 */ + short hpf_coef_20; + /* HPF Coefficient 21, S10 */ + short hpf_coef_21; + /* HPF Coefficient 22, S10 */ + short hpf_coef_22; + /* Lower threshold before referring to LUT */ + unsigned short yee_thr; + /* Edge sharpener Gain */ + unsigned short es_gain; + /* Edge sharpener lower threshold */ + unsigned short es_thr1; + /* Edge sharpener upper threshold */ + unsigned short es_thr2; + /* Edge sharpener gain on gradient */ + unsigned short es_gain_grad; + /* Edge sharpener offset on gradient */ + unsigned short es_ofst_grad; + /* Ptr to EE table. Must have 1024 entries */ + short table[VPFE_IPIPE_MAX_SIZE_YEE_LUT]; +}; + +enum vpfe_ipipe_car_meth { + /* Chromatic Gain Control */ + VPFE_IPIPE_CAR_CHR_GAIN_CTRL = 0, + /* Dynamic switching between CHR_GAIN_CTRL + * and MED_FLTR + */ + VPFE_IPIPE_CAR_DYN_SWITCH = 1, + /* Median Filter */ + VPFE_IPIPE_CAR_MED_FLTR = 2, +}; + +enum vpfe_ipipe_car_hpf_type { + VPFE_IPIPE_CAR_HPF_Y = 0, + VPFE_IPIPE_CAR_HPF_H = 1, + VPFE_IPIPE_CAR_HPF_V = 2, + VPFE_IPIPE_CAR_HPF_2D = 3, + /* 2D HPF from YUV Edge Enhancement */ + VPFE_IPIPE_CAR_HPF_2D_YEE = 4, +}; + +struct vpfe_ipipe_car_gain { + /* csup_gain */ + unsigned char gain; + /* csup_shf. */ + unsigned char shft; + /* gain minimum */ + unsigned short gain_min; +}; + +/* Structure for Chromatic Artifact Reduction */ +struct vpfe_ipipe_car { + /* enable/disable */ + unsigned char en; + /* Gain control or Dynamic switching */ + enum vpfe_ipipe_car_meth meth; + /* Gain1 function configuration for Gain control */ + struct vpfe_ipipe_car_gain gain1; + /* Gain2 function configuration for Gain control */ + struct vpfe_ipipe_car_gain gain2; + /* HPF type used for CAR */ + enum vpfe_ipipe_car_hpf_type hpf; + /* csup_thr: HPF threshold for Gain control */ + unsigned char hpf_thr; + /* Down shift value for hpf. 2 bits */ + unsigned char hpf_shft; + /* switch limit for median filter */ + unsigned char sw0; + /* switch coefficient for Gain control */ + unsigned char sw1; +}; + +/* structure for Chromatic Gain Suppression */ +struct vpfe_ipipe_cgs { + /* enable/disable */ + unsigned char en; + /* gain1 bright side threshold */ + unsigned char h_thr; + /* gain1 bright side slope */ + unsigned char h_slope; + /* gain1 down shift value for bright side */ + unsigned char h_shft; + /* gain1 bright side minimum gain */ + unsigned char h_min; +}; + +/* Max pixels allowed in the input. If above this either decimation + * or frame division mode to be enabled + */ +#define VPFE_IPIPE_MAX_INPUT_WIDTH 2600 + +struct vpfe_ipipe_input_config { + unsigned int vst; + unsigned int hst; +}; + +/** + * struct vpfe_ipipe_config - IPIPE engine configuration (user) + * @input_config: Pointer to structure for ipipe configuration. + * @flag: Specifies which ISP IPIPE functions should be enabled. + * @lutdpc: Pointer to luma enhancement structure. + * @otfdpc: Pointer to structure for defect correction. + * @nf1: Pointer to structure for Noise Filter. + * @nf2: Pointer to structure for Noise Filter. + * @gic: Pointer to structure for Green Imbalance. + * @wbal: Pointer to structure for White Balance. + * @cfa: Pointer to structure containing the CFA interpolation. + * @rgb2rgb1: Pointer to structure for RGB to RGB Blending. + * @rgb2rgb2: Pointer to structure for RGB to RGB Blending. + * @gamma: Pointer to gamma structure. + * @lut: Pointer to structure for 3D LUT. + * @rgb2yuv: Pointer to structure for RGB-YCbCr conversion. + * @gbce: Pointer to structure for Global Brightness,Contrast Control. + * @yuv422_conv: Pointer to structure for YUV 422 conversion. + * @yee: Pointer to structure for Edge Enhancer. + * @car: Pointer to structure for Chromatic Artifact Reduction. + * @cgs: Pointer to structure for Chromatic Gain Suppression. + */ +struct vpfe_ipipe_config { + __u32 flag; + struct vpfe_ipipe_input_config __user *input_config; + struct vpfe_ipipe_lutdpc __user *lutdpc; + struct vpfe_ipipe_otfdpc __user *otfdpc; + struct vpfe_ipipe_nf __user *nf1; + struct vpfe_ipipe_nf __user *nf2; + struct vpfe_ipipe_gic __user *gic; + struct vpfe_ipipe_wb __user *wbal; + struct vpfe_ipipe_cfa __user *cfa; + struct vpfe_ipipe_rgb2rgb __user *rgb2rgb1; + struct vpfe_ipipe_rgb2rgb __user *rgb2rgb2; + struct vpfe_ipipe_gamma __user *gamma; + struct vpfe_ipipe_3d_lut __user *lut; + struct vpfe_ipipe_rgb2yuv __user *rgb2yuv; + struct vpfe_ipipe_gbce __user *gbce; + struct vpfe_ipipe_yuv422_conv __user *yuv422_conv; + struct vpfe_ipipe_yee __user *yee; + struct vpfe_ipipe_car __user *car; + struct vpfe_ipipe_cgs __user *cgs; +}; + +/******************************************************************* +** Resizer API structures +*******************************************************************/ +/* Interpolation types used for horizontal rescale */ +enum vpfe_rsz_intp_t { + VPFE_RSZ_INTP_CUBIC, + VPFE_RSZ_INTP_LINEAR +}; + +/* Horizontal LPF intensity selection */ +enum vpfe_rsz_h_lpf_lse_t { + VPFE_RSZ_H_LPF_LSE_INTERN, + VPFE_RSZ_H_LPF_LSE_USER_VAL +}; + +enum vpfe_rsz_down_scale_ave_sz { + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + VPFE_IPIPE_DWN_SCALE_1_OVER_4, + VPFE_IPIPE_DWN_SCALE_1_OVER_8, + VPFE_IPIPE_DWN_SCALE_1_OVER_16, + VPFE_IPIPE_DWN_SCALE_1_OVER_32, + VPFE_IPIPE_DWN_SCALE_1_OVER_64, + VPFE_IPIPE_DWN_SCALE_1_OVER_128, + VPFE_IPIPE_DWN_SCALE_1_OVER_256 +}; + +struct vpfe_rsz_output_spec { + /* enable horizontal flip */ + unsigned char h_flip; + /* enable vertical flip */ + unsigned char v_flip; + /* line start offset for y. */ + unsigned int vst_y; + /* line start offset for c. Only for 420 */ + unsigned int vst_c; + /* vertical rescale interpolation type, YCbCr or Luminance */ + enum vpfe_rsz_intp_t v_typ_y; + /* vertical rescale interpolation type for Chrominance */ + enum vpfe_rsz_intp_t v_typ_c; + /* vertical lpf intensity - Luminance */ + unsigned char v_lpf_int_y; + /* vertical lpf intensity - Chrominance */ + unsigned char v_lpf_int_c; + /* horizontal rescale interpolation types, YCbCr or Luminance */ + enum vpfe_rsz_intp_t h_typ_y; + /* horizontal rescale interpolation types, Chrominance */ + enum vpfe_rsz_intp_t h_typ_c; + /* horizontal lpf intensity - Luminance */ + unsigned char h_lpf_int_y; + /* horizontal lpf intensity - Chrominance */ + unsigned char h_lpf_int_c; + /* Use down scale mode for scale down */ + unsigned char en_down_scale; + /* if downscale, set the downscale more average size for horizontal + * direction. Used only if output width and height is less than + * input sizes + */ + enum vpfe_rsz_down_scale_ave_sz h_dscale_ave_sz; + /* if downscale, set the downscale more average size for vertical + * direction. Used only if output width and height is less than + * input sizes + */ + enum vpfe_rsz_down_scale_ave_sz v_dscale_ave_sz; + /* Y offset. If set, the offset would be added to the base address + */ + unsigned int user_y_ofst; + /* C offset. If set, the offset would be added to the base address + */ + unsigned int user_c_ofst; +}; + +struct vpfe_rsz_config_params { + unsigned int vst; + /* horizontal start position of the image + * data to IPIPE + */ + unsigned int hst; + /* output spec of the image data coming out of resizer - 0(UYVY). + */ + struct vpfe_rsz_output_spec output1; + /* output spec of the image data coming out of resizer - 1(UYVY). + */ + struct vpfe_rsz_output_spec output2; + /* 0 , chroma sample at odd pixel, 1 - even pixel */ + unsigned char chroma_sample_even; + unsigned char frame_div_mode_en; + unsigned char yuv_y_min; + unsigned char yuv_y_max; + unsigned char yuv_c_min; + unsigned char yuv_c_max; + enum vpfe_chr_pos out_chr_pos; + unsigned char bypass; +}; + +/* Structure for VIDIOC_VPFE_RSZ_[S/G]_CONFIG IOCTLs */ +struct vpfe_rsz_config { + struct vpfe_rsz_config_params *config; +}; + +#endif /* _DAVINCI_VPFE_USER_H */ diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe.c b/drivers/staging/media/davinci_vpfe/dm365_ipipe.c new file mode 100644 index 000000000000..92853539cc0a --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe.c @@ -0,0 +1,1863 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + * + * + * IPIPE allows fine tuning of the input image using different + * tuning modules in IPIPE. Some examples :- Noise filter, Defect + * pixel correction etc. It essentially operate on Bayer Raw data + * or YUV raw data. To do image tuning, application call, + * + */ + +#include <linux/slab.h> + +#include "dm365_ipipe.h" +#include "dm365_ipipe_hw.h" +#include "vpfe_mc_capture.h" + +#define MIN_OUT_WIDTH 32 +#define MIN_OUT_HEIGHT 32 + +/* ipipe input format's */ +static const unsigned int ipipe_input_fmts[] = { + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_SGRBG12_1X12, + V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, + V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8, +}; + +/* ipipe output format's */ +static const unsigned int ipipe_output_fmts[] = { + V4L2_MBUS_FMT_UYVY8_2X8, +}; + +static int ipipe_validate_lutdpc_params(struct vpfe_ipipe_lutdpc *lutdpc) +{ + int i; + + if (lutdpc->en > 1 || lutdpc->repl_white > 1 || + lutdpc->dpc_size > LUT_DPC_MAX_SIZE) + return -EINVAL; + + if (lutdpc->en && !lutdpc->table) + return -EINVAL; + + for (i = 0; i < lutdpc->dpc_size; i++) + if (lutdpc->table[i].horz_pos > LUT_DPC_H_POS_MASK || + lutdpc->table[i].vert_pos > LUT_DPC_V_POS_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_lutdpc_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_lutdpc *lutdpc = &ipipe->config.lutdpc; + struct vpfe_ipipe_lutdpc *dpc_param; + struct device *dev; + + if (!param) { + memset((void *)lutdpc, 0, sizeof(struct vpfe_ipipe_lutdpc)); + goto success; + } + + dev = ipipe->subdev.v4l2_dev->dev; + dpc_param = (struct vpfe_ipipe_lutdpc *)param; + lutdpc->en = dpc_param->en; + lutdpc->repl_white = dpc_param->repl_white; + lutdpc->dpc_size = dpc_param->dpc_size; + memcpy(&lutdpc->table, &dpc_param->table, + (dpc_param->dpc_size * sizeof(struct vpfe_ipipe_lutdpc_entry))); + if (ipipe_validate_lutdpc_params(lutdpc) < 0) + return -EINVAL; + +success: + ipipe_set_lutdpc_regs(ipipe->base_addr, ipipe->isp5_base_addr, lutdpc); + + return 0; +} + +static int ipipe_get_lutdpc_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_lutdpc *lut_param = (struct vpfe_ipipe_lutdpc *)param; + struct vpfe_ipipe_lutdpc *lutdpc = &ipipe->config.lutdpc; + + lut_param->en = lutdpc->en; + lut_param->repl_white = lutdpc->repl_white; + lut_param->dpc_size = lutdpc->dpc_size; + memcpy(&lut_param->table, &lutdpc->table, + (lutdpc->dpc_size * sizeof(struct vpfe_ipipe_lutdpc_entry))); + + return 0; +} + +static int ipipe_set_input_config(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_input_config *config = &ipipe->config.input_config; + + if (!param) + memset(config, 0, sizeof(struct vpfe_ipipe_input_config)); + else + memcpy(config, param, sizeof(struct vpfe_ipipe_input_config)); + return 0; +} + +static int ipipe_get_input_config(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_input_config *config = &ipipe->config.input_config; + + if (!param) + return -EINVAL; + + memcpy(param, config, sizeof(struct vpfe_ipipe_input_config)); + + return 0; +} + +static int ipipe_validate_otfdpc_params(struct vpfe_ipipe_otfdpc *dpc_param) +{ + struct vpfe_ipipe_otfdpc_2_0_cfg *dpc_2_0; + struct vpfe_ipipe_otfdpc_3_0_cfg *dpc_3_0; + + if (dpc_param->en > 1) + return -EINVAL; + + if (dpc_param->alg == VPFE_IPIPE_OTFDPC_2_0) { + dpc_2_0 = &dpc_param->alg_cfg.dpc_2_0; + if (dpc_2_0->det_thr.r > OTFDPC_DPC2_THR_MASK || + dpc_2_0->det_thr.gr > OTFDPC_DPC2_THR_MASK || + dpc_2_0->det_thr.gb > OTFDPC_DPC2_THR_MASK || + dpc_2_0->det_thr.b > OTFDPC_DPC2_THR_MASK || + dpc_2_0->corr_thr.r > OTFDPC_DPC2_THR_MASK || + dpc_2_0->corr_thr.gr > OTFDPC_DPC2_THR_MASK || + dpc_2_0->corr_thr.gb > OTFDPC_DPC2_THR_MASK || + dpc_2_0->corr_thr.b > OTFDPC_DPC2_THR_MASK) + return -EINVAL; + return 0; + } + + dpc_3_0 = &dpc_param->alg_cfg.dpc_3_0; + + if (dpc_3_0->act_adj_shf > OTF_DPC3_0_SHF_MASK || + dpc_3_0->det_thr > OTF_DPC3_0_DET_MASK || + dpc_3_0->det_slp > OTF_DPC3_0_SLP_MASK || + dpc_3_0->det_thr_min > OTF_DPC3_0_DET_MASK || + dpc_3_0->det_thr_max > OTF_DPC3_0_DET_MASK || + dpc_3_0->corr_thr > OTF_DPC3_0_CORR_MASK || + dpc_3_0->corr_slp > OTF_DPC3_0_SLP_MASK || + dpc_3_0->corr_thr_min > OTF_DPC3_0_CORR_MASK || + dpc_3_0->corr_thr_max > OTF_DPC3_0_CORR_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_otfdpc_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_otfdpc *dpc_param = (struct vpfe_ipipe_otfdpc *)param; + struct vpfe_ipipe_otfdpc *otfdpc = &ipipe->config.otfdpc; + struct device *dev; + + if (!param) { + memset((void *)otfdpc, 0, sizeof(struct ipipe_otfdpc_2_0)); + goto success; + } + dev = ipipe->subdev.v4l2_dev->dev; + memcpy(otfdpc, dpc_param, sizeof(struct vpfe_ipipe_otfdpc)); + if (ipipe_validate_otfdpc_params(otfdpc) < 0) { + dev_err(dev, "Invalid otfdpc params\n"); + return -EINVAL; + } + +success: + ipipe_set_otfdpc_regs(ipipe->base_addr, otfdpc); + + return 0; +} + +static int ipipe_get_otfdpc_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_otfdpc *dpc_param = (struct vpfe_ipipe_otfdpc *)param; + struct vpfe_ipipe_otfdpc *otfdpc = &ipipe->config.otfdpc; + + memcpy(dpc_param, otfdpc, sizeof(struct vpfe_ipipe_otfdpc)); + return 0; +} + +static int ipipe_validate_nf_params(struct vpfe_ipipe_nf *nf_param) +{ + int i; + + if (nf_param->en > 1 || nf_param->shft_val > D2F_SHFT_VAL_MASK || + nf_param->spread_val > D2F_SPR_VAL_MASK || + nf_param->apply_lsc_gain > 1 || + nf_param->edge_det_min_thr > D2F_EDGE_DET_THR_MASK || + nf_param->edge_det_max_thr > D2F_EDGE_DET_THR_MASK) + return -EINVAL; + + for (i = 0; i < VPFE_IPIPE_NF_THR_TABLE_SIZE; i++) + if (nf_param->thr[i] > D2F_THR_VAL_MASK) + return -EINVAL; + + for (i = 0; i < VPFE_IPIPE_NF_STR_TABLE_SIZE; i++) + if (nf_param->str[i] > D2F_STR_VAL_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_nf_params(struct vpfe_ipipe_device *ipipe, + unsigned int id, void *param) +{ + struct vpfe_ipipe_nf *nf_param = (struct vpfe_ipipe_nf *)param; + struct vpfe_ipipe_nf *nf = &ipipe->config.nf1; + struct device *dev; + + if (id == IPIPE_D2F_2ND) + nf = &ipipe->config.nf2; + + if (!nf_param) { + memset((void *)nf, 0, sizeof(struct vpfe_ipipe_nf)); + goto success; + } + + dev = ipipe->subdev.v4l2_dev->dev; + memcpy(nf, nf_param, sizeof(struct vpfe_ipipe_nf)); + if (ipipe_validate_nf_params(nf) < 0) { + dev_err(dev, "Invalid nf params\n"); + return -EINVAL; + } + +success: + ipipe_set_d2f_regs(ipipe->base_addr, id, nf); + + return 0; +} + +static int ipipe_set_nf1_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + return ipipe_set_nf_params(ipipe, IPIPE_D2F_1ST, param); +} + +static int ipipe_set_nf2_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + return ipipe_set_nf_params(ipipe, IPIPE_D2F_2ND, param); +} + +static int ipipe_get_nf_params(struct vpfe_ipipe_device *ipipe, + unsigned int id, void *param) +{ + struct vpfe_ipipe_nf *nf_param = (struct vpfe_ipipe_nf *)param; + struct vpfe_ipipe_nf *nf = &ipipe->config.nf1; + + if (id == IPIPE_D2F_2ND) + nf = &ipipe->config.nf2; + + memcpy(nf_param, nf, sizeof(struct vpfe_ipipe_nf)); + + return 0; +} + +static int ipipe_get_nf1_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + return ipipe_get_nf_params(ipipe, IPIPE_D2F_1ST, param); +} + +static int ipipe_get_nf2_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + return ipipe_get_nf_params(ipipe, IPIPE_D2F_2ND, param); +} + +static int ipipe_validate_gic_params(struct vpfe_ipipe_gic *gic) +{ + if (gic->en > 1 || gic->gain > GIC_GAIN_MASK || + gic->thr > GIC_THR_MASK || gic->slope > GIC_SLOPE_MASK || + gic->apply_lsc_gain > 1 || + gic->nf2_thr_gain.integer > GIC_NFGAN_INT_MASK || + gic->nf2_thr_gain.decimal > GIC_NFGAN_DECI_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_gic_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_gic *gic_param = (struct vpfe_ipipe_gic *)param; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + struct vpfe_ipipe_gic *gic = &ipipe->config.gic; + + if (!gic_param) { + memset((void *)gic, 0, sizeof(struct vpfe_ipipe_gic)); + goto success; + } + + memcpy(gic, gic_param, sizeof(struct vpfe_ipipe_gic)); + if (ipipe_validate_gic_params(gic) < 0) { + dev_err(dev, "Invalid gic params\n"); + return -EINVAL; + } + +success: + ipipe_set_gic_regs(ipipe->base_addr, gic); + + return 0; +} + +static int ipipe_get_gic_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_gic *gic_param = (struct vpfe_ipipe_gic *)param; + struct vpfe_ipipe_gic *gic = &ipipe->config.gic; + + memcpy(gic_param, gic, sizeof(struct vpfe_ipipe_gic)); + + return 0; +} + +static int ipipe_validate_wb_params(struct vpfe_ipipe_wb *wbal) +{ + if (wbal->ofst_r > WB_OFFSET_MASK || + wbal->ofst_gr > WB_OFFSET_MASK || + wbal->ofst_gb > WB_OFFSET_MASK || + wbal->ofst_b > WB_OFFSET_MASK || + wbal->gain_r.integer > WB_GAIN_INT_MASK || + wbal->gain_r.decimal > WB_GAIN_DECI_MASK || + wbal->gain_gr.integer > WB_GAIN_INT_MASK || + wbal->gain_gr.decimal > WB_GAIN_DECI_MASK || + wbal->gain_gb.integer > WB_GAIN_INT_MASK || + wbal->gain_gb.decimal > WB_GAIN_DECI_MASK || + wbal->gain_b.integer > WB_GAIN_INT_MASK || + wbal->gain_b.decimal > WB_GAIN_DECI_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_wb_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_wb *wb_param = (struct vpfe_ipipe_wb *)param; + struct vpfe_ipipe_wb *wbal = &ipipe->config.wbal; + + if (!wb_param) { + const struct vpfe_ipipe_wb wb_defaults = { + .gain_r = {2, 0x0}, + .gain_gr = {2, 0x0}, + .gain_gb = {2, 0x0}, + .gain_b = {2, 0x0} + }; + memcpy(wbal, &wb_defaults, sizeof(struct vpfe_ipipe_wb)); + goto success; + } + + memcpy(wbal, wb_param, sizeof(struct vpfe_ipipe_wb)); + if (ipipe_validate_wb_params(wbal) < 0) + return -EINVAL; + +success: + ipipe_set_wb_regs(ipipe->base_addr, wbal); + + return 0; +} + +static int ipipe_get_wb_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_wb *wb_param = (struct vpfe_ipipe_wb *)param; + struct vpfe_ipipe_wb *wbal = &ipipe->config.wbal; + + memcpy(wb_param, wbal, sizeof(struct vpfe_ipipe_wb)); + return 0; +} + +static int ipipe_validate_cfa_params(struct vpfe_ipipe_cfa *cfa) +{ + if (cfa->hpf_thr_2dir > CFA_HPF_THR_2DIR_MASK || + cfa->hpf_slp_2dir > CFA_HPF_SLOPE_2DIR_MASK || + cfa->hp_mix_thr_2dir > CFA_HPF_MIX_THR_2DIR_MASK || + cfa->hp_mix_slope_2dir > CFA_HPF_MIX_SLP_2DIR_MASK || + cfa->dir_thr_2dir > CFA_DIR_THR_2DIR_MASK || + cfa->dir_slope_2dir > CFA_DIR_SLP_2DIR_MASK || + cfa->nd_wt_2dir > CFA_ND_WT_2DIR_MASK || + cfa->hue_fract_daa > CFA_DAA_HUE_FRA_MASK || + cfa->edge_thr_daa > CFA_DAA_EDG_THR_MASK || + cfa->thr_min_daa > CFA_DAA_THR_MIN_MASK || + cfa->thr_slope_daa > CFA_DAA_THR_SLP_MASK || + cfa->slope_min_daa > CFA_DAA_SLP_MIN_MASK || + cfa->slope_slope_daa > CFA_DAA_SLP_SLP_MASK || + cfa->lp_wt_daa > CFA_DAA_LP_WT_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_cfa_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_cfa *cfa_param = (struct vpfe_ipipe_cfa *)param; + struct vpfe_ipipe_cfa *cfa = &ipipe->config.cfa; + + if (!cfa_param) { + memset(cfa, 0, sizeof(struct vpfe_ipipe_cfa)); + cfa->alg = VPFE_IPIPE_CFA_ALG_2DIRAC; + goto success; + } + + memcpy(cfa, cfa_param, sizeof(struct vpfe_ipipe_cfa)); + if (ipipe_validate_cfa_params(cfa) < 0) + return -EINVAL; + +success: + ipipe_set_cfa_regs(ipipe->base_addr, cfa); + + return 0; +} + +static int ipipe_get_cfa_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_cfa *cfa_param = (struct vpfe_ipipe_cfa *)param; + struct vpfe_ipipe_cfa *cfa = &ipipe->config.cfa; + + memcpy(cfa_param, cfa, sizeof(struct vpfe_ipipe_cfa)); + return 0; +} + +static int +ipipe_validate_rgb2rgb_params(struct vpfe_ipipe_rgb2rgb *rgb2rgb, + unsigned int id) +{ + u32 gain_int_upper = RGB2RGB_1_GAIN_INT_MASK; + u32 offset_upper = RGB2RGB_1_OFST_MASK; + + if (id == IPIPE_RGB2RGB_2) { + offset_upper = RGB2RGB_2_OFST_MASK; + gain_int_upper = RGB2RGB_2_GAIN_INT_MASK; + } + + if (rgb2rgb->coef_rr.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_rr.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_gr.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_gr.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_br.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_br.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_rg.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_rg.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_gg.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_gg.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_bg.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_bg.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_rb.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_rb.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_gb.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_gb.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_bb.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_bb.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->out_ofst_r > offset_upper || + rgb2rgb->out_ofst_g > offset_upper || + rgb2rgb->out_ofst_b > offset_upper) + return -EINVAL; + + return 0; +} + +static int ipipe_set_rgb2rgb_params(struct vpfe_ipipe_device *ipipe, + unsigned int id, void *param) +{ + struct vpfe_ipipe_rgb2rgb *rgb2rgb = &ipipe->config.rgb2rgb1; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + struct vpfe_ipipe_rgb2rgb *rgb2rgb_param; + + rgb2rgb_param = (struct vpfe_ipipe_rgb2rgb *)param; + + if (id == IPIPE_RGB2RGB_2) + rgb2rgb = &ipipe->config.rgb2rgb2; + + if (!rgb2rgb_param) { + const struct vpfe_ipipe_rgb2rgb rgb2rgb_defaults = { + .coef_rr = {1, 0}, /* 256 */ + .coef_gr = {0, 0}, + .coef_br = {0, 0}, + .coef_rg = {0, 0}, + .coef_gg = {1, 0}, /* 256 */ + .coef_bg = {0, 0}, + .coef_rb = {0, 0}, + .coef_gb = {0, 0}, + .coef_bb = {1, 0}, /* 256 */ + }; + /* Copy defaults for rgb2rgb conversion */ + memcpy(rgb2rgb, &rgb2rgb_defaults, + sizeof(struct vpfe_ipipe_rgb2rgb)); + goto success; + } + + memcpy(rgb2rgb, rgb2rgb_param, sizeof(struct vpfe_ipipe_rgb2rgb)); + if (ipipe_validate_rgb2rgb_params(rgb2rgb, id) < 0) { + dev_err(dev, "Invalid rgb2rgb params\n"); + return -EINVAL; + } + +success: + ipipe_set_rgb2rgb_regs(ipipe->base_addr, id, rgb2rgb); + + return 0; +} + +static int +ipipe_set_rgb2rgb_1_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + return ipipe_set_rgb2rgb_params(ipipe, IPIPE_RGB2RGB_1, param); +} + +static int +ipipe_set_rgb2rgb_2_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + return ipipe_set_rgb2rgb_params(ipipe, IPIPE_RGB2RGB_2, param); +} + +static int ipipe_get_rgb2rgb_params(struct vpfe_ipipe_device *ipipe, + unsigned int id, void *param) +{ + struct vpfe_ipipe_rgb2rgb *rgb2rgb = &ipipe->config.rgb2rgb1; + struct vpfe_ipipe_rgb2rgb *rgb2rgb_param; + + rgb2rgb_param = (struct vpfe_ipipe_rgb2rgb *)param; + + if (id == IPIPE_RGB2RGB_2) + rgb2rgb = &ipipe->config.rgb2rgb2; + + memcpy(rgb2rgb_param, rgb2rgb, sizeof(struct vpfe_ipipe_rgb2rgb)); + + return 0; +} + +static int +ipipe_get_rgb2rgb_1_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + return ipipe_get_rgb2rgb_params(ipipe, IPIPE_RGB2RGB_1, param); +} + +static int +ipipe_get_rgb2rgb_2_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + return ipipe_get_rgb2rgb_params(ipipe, IPIPE_RGB2RGB_2, param); +} + +static int +ipipe_validate_gamma_entry(struct vpfe_ipipe_gamma_entry *table, int size) +{ + int i; + + if (!table) + return -EINVAL; + + for (i = 0; i < size; i++) + if (table[i].slope > GAMMA_MASK || + table[i].offset > GAMMA_MASK) + return -EINVAL; + + return 0; +} + +static int +ipipe_validate_gamma_params(struct vpfe_ipipe_gamma *gamma, struct device *dev) +{ + int table_size; + int err; + + if (gamma->bypass_r > 1 || + gamma->bypass_b > 1 || + gamma->bypass_g > 1) + return -EINVAL; + + if (gamma->tbl_sel != VPFE_IPIPE_GAMMA_TBL_RAM) + return 0; + + table_size = gamma->tbl_size; + if (!gamma->bypass_r) { + err = ipipe_validate_gamma_entry(gamma->table_r, table_size); + if (err) { + dev_err(dev, "GAMMA R - table entry invalid\n"); + return err; + } + } + + if (!gamma->bypass_b) { + err = ipipe_validate_gamma_entry(gamma->table_b, table_size); + if (err) { + dev_err(dev, "GAMMA B - table entry invalid\n"); + return err; + } + } + + if (!gamma->bypass_g) { + err = ipipe_validate_gamma_entry(gamma->table_g, table_size); + if (err) { + dev_err(dev, "GAMMA G - table entry invalid\n"); + return err; + } + } + + return 0; +} + +static int +ipipe_set_gamma_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_gamma *gamma_param = (struct vpfe_ipipe_gamma *)param; + struct vpfe_ipipe_gamma *gamma = &ipipe->config.gamma; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + int table_size; + + if (!gamma_param) { + memset(gamma, 0, sizeof(struct vpfe_ipipe_gamma)); + gamma->tbl_sel = VPFE_IPIPE_GAMMA_TBL_ROM; + goto success; + } + + gamma->bypass_r = gamma_param->bypass_r; + gamma->bypass_b = gamma_param->bypass_b; + gamma->bypass_g = gamma_param->bypass_g; + gamma->tbl_sel = gamma_param->tbl_sel; + gamma->tbl_size = gamma_param->tbl_size; + + if (ipipe_validate_gamma_params(gamma, dev) < 0) + return -EINVAL; + + if (gamma_param->tbl_sel != VPFE_IPIPE_GAMMA_TBL_RAM) + goto success; + + table_size = gamma->tbl_size; + if (!gamma_param->bypass_r) + memcpy(&gamma->table_r, &gamma_param->table_r, + (table_size * sizeof(struct vpfe_ipipe_gamma_entry))); + + if (!gamma_param->bypass_b) + memcpy(&gamma->table_b, &gamma_param->table_b, + (table_size * sizeof(struct vpfe_ipipe_gamma_entry))); + + if (!gamma_param->bypass_g) + memcpy(&gamma->table_g, &gamma_param->table_g, + (table_size * sizeof(struct vpfe_ipipe_gamma_entry))); + +success: + ipipe_set_gamma_regs(ipipe->base_addr, ipipe->isp5_base_addr, gamma); + + return 0; +} + +static int ipipe_get_gamma_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_gamma *gamma_param = (struct vpfe_ipipe_gamma *)param; + struct vpfe_ipipe_gamma *gamma = &ipipe->config.gamma; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + int table_size; + + gamma_param->bypass_r = gamma->bypass_r; + gamma_param->bypass_g = gamma->bypass_g; + gamma_param->bypass_b = gamma->bypass_b; + gamma_param->tbl_sel = gamma->tbl_sel; + gamma_param->tbl_size = gamma->tbl_size; + + if (gamma->tbl_sel != VPFE_IPIPE_GAMMA_TBL_RAM) + return 0; + + table_size = gamma->tbl_size; + + if (!gamma->bypass_r && !gamma_param->table_r) { + dev_err(dev, + "ipipe_get_gamma_params: table ptr empty for R\n"); + return -EINVAL; + } + memcpy(gamma_param->table_r, gamma->table_r, + (table_size * sizeof(struct vpfe_ipipe_gamma_entry))); + + if (!gamma->bypass_g && !gamma_param->table_g) { + dev_err(dev, "ipipe_get_gamma_params: table ptr empty for G\n"); + return -EINVAL; + } + memcpy(gamma_param->table_g, gamma->table_g, + (table_size * sizeof(struct vpfe_ipipe_gamma_entry))); + + if (!gamma->bypass_b && !gamma_param->table_b) { + dev_err(dev, "ipipe_get_gamma_params: table ptr empty for B\n"); + return -EINVAL; + } + memcpy(gamma_param->table_b, gamma->table_b, + (table_size * sizeof(struct vpfe_ipipe_gamma_entry))); + + return 0; +} + +static int ipipe_validate_3d_lut_params(struct vpfe_ipipe_3d_lut *lut) +{ + int i; + + if (!lut->en) + return 0; + + for (i = 0; i < VPFE_IPIPE_MAX_SIZE_3D_LUT; i++) + if (lut->table[i].r > D3_LUT_ENTRY_MASK || + lut->table[i].g > D3_LUT_ENTRY_MASK || + lut->table[i].b > D3_LUT_ENTRY_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_get_3d_lut_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_3d_lut *lut_param = (struct vpfe_ipipe_3d_lut *)param; + struct vpfe_ipipe_3d_lut *lut = &ipipe->config.lut; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + + lut_param->en = lut->en; + if (!lut_param->table) { + dev_err(dev, "ipipe_get_3d_lut_params: Invalid table ptr\n"); + return -EINVAL; + } + + memcpy(lut_param->table, &lut->table, + (VPFE_IPIPE_MAX_SIZE_3D_LUT * + sizeof(struct vpfe_ipipe_3d_lut_entry))); + + return 0; +} + +static int +ipipe_set_3d_lut_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_3d_lut *lut_param = (struct vpfe_ipipe_3d_lut *)param; + struct vpfe_ipipe_3d_lut *lut = &ipipe->config.lut; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + + if (!lut_param) { + memset(lut, 0, sizeof(struct vpfe_ipipe_3d_lut)); + goto success; + } + + memcpy(lut, lut_param, sizeof(struct vpfe_ipipe_3d_lut)); + if (ipipe_validate_3d_lut_params(lut) < 0) { + dev_err(dev, "Invalid 3D-LUT Params\n"); + return -EINVAL; + } + +success: + ipipe_set_3d_lut_regs(ipipe->base_addr, ipipe->isp5_base_addr, lut); + + return 0; +} + +static int ipipe_validate_rgb2yuv_params(struct vpfe_ipipe_rgb2yuv *rgb2yuv) +{ + if (rgb2yuv->coef_ry.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_ry.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->coef_gy.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_gy.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->coef_by.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_by.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->coef_rcb.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_rcb.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->coef_gcb.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_gcb.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->coef_bcb.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_bcb.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->coef_rcr.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_rcr.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->coef_gcr.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_gcr.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->coef_bcr.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_bcr.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->out_ofst_y > RGB2YCBCR_OFST_MASK || + rgb2yuv->out_ofst_cb > RGB2YCBCR_OFST_MASK || + rgb2yuv->out_ofst_cr > RGB2YCBCR_OFST_MASK) + return -EINVAL; + + return 0; +} + +static int +ipipe_set_rgb2yuv_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_rgb2yuv *rgb2yuv = &ipipe->config.rgb2yuv; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + struct vpfe_ipipe_rgb2yuv *rgb2yuv_param; + + rgb2yuv_param = (struct vpfe_ipipe_rgb2yuv *)param; + if (!rgb2yuv_param) { + /* Defaults for rgb2yuv conversion */ + const struct vpfe_ipipe_rgb2yuv rgb2yuv_defaults = { + .coef_ry = {0, 0x4d}, + .coef_gy = {0, 0x96}, + .coef_by = {0, 0x1d}, + .coef_rcb = {0xf, 0xd5}, + .coef_gcb = {0xf, 0xab}, + .coef_bcb = {0, 0x80}, + .coef_rcr = {0, 0x80}, + .coef_gcr = {0xf, 0x95}, + .coef_bcr = {0xf, 0xeb}, + .out_ofst_cb = 0x80, + .out_ofst_cr = 0x80, + }; + /* Copy defaults for rgb2yuv conversion */ + memcpy(rgb2yuv, &rgb2yuv_defaults, + sizeof(struct vpfe_ipipe_rgb2yuv)); + goto success; + } + + memcpy(rgb2yuv, rgb2yuv_param, sizeof(struct vpfe_ipipe_rgb2yuv)); + if (ipipe_validate_rgb2yuv_params(rgb2yuv) < 0) { + dev_err(dev, "Invalid rgb2yuv params\n"); + return -EINVAL; + } + +success: + ipipe_set_rgb2ycbcr_regs(ipipe->base_addr, rgb2yuv); + + return 0; +} + +static int +ipipe_get_rgb2yuv_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_rgb2yuv *rgb2yuv = &ipipe->config.rgb2yuv; + struct vpfe_ipipe_rgb2yuv *rgb2yuv_param; + + rgb2yuv_param = (struct vpfe_ipipe_rgb2yuv *)param; + memcpy(rgb2yuv_param, rgb2yuv, sizeof(struct vpfe_ipipe_rgb2yuv)); + return 0; +} + +static int ipipe_validate_gbce_params(struct vpfe_ipipe_gbce *gbce) +{ + u32 max = GBCE_Y_VAL_MASK; + int i; + + if (!gbce->en) + return 0; + + if (gbce->type == VPFE_IPIPE_GBCE_GAIN_TBL) + max = GBCE_GAIN_VAL_MASK; + + for (i = 0; i < VPFE_IPIPE_MAX_SIZE_GBCE_LUT; i++) + if (gbce->table[i] > max) + return -EINVAL; + + return 0; +} + +static int ipipe_set_gbce_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_gbce *gbce_param = (struct vpfe_ipipe_gbce *)param; + struct vpfe_ipipe_gbce *gbce = &ipipe->config.gbce; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + + if (!gbce_param) { + memset(gbce, 0 , sizeof(struct vpfe_ipipe_gbce)); + } else { + memcpy(gbce, gbce_param, sizeof(struct vpfe_ipipe_gbce)); + if (ipipe_validate_gbce_params(gbce) < 0) { + dev_err(dev, "Invalid gbce params\n"); + return -EINVAL; + } + } + + ipipe_set_gbce_regs(ipipe->base_addr, ipipe->isp5_base_addr, gbce); + + return 0; +} + +static int ipipe_get_gbce_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_gbce *gbce_param = (struct vpfe_ipipe_gbce *)param; + struct vpfe_ipipe_gbce *gbce = &ipipe->config.gbce; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + + gbce_param->en = gbce->en; + gbce_param->type = gbce->type; + if (!gbce_param->table) { + dev_err(dev, "ipipe_get_gbce_params: Invalid table ptr\n"); + return -EINVAL; + } + + memcpy(gbce_param->table, gbce->table, + (VPFE_IPIPE_MAX_SIZE_GBCE_LUT * sizeof(unsigned short))); + + return 0; +} + +static int +ipipe_validate_yuv422_conv_params(struct vpfe_ipipe_yuv422_conv *yuv422_conv) +{ + if (yuv422_conv->en_chrom_lpf > 1) + return -EINVAL; + + return 0; +} + +static int +ipipe_set_yuv422_conv_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_yuv422_conv *yuv422_conv = &ipipe->config.yuv422_conv; + struct vpfe_ipipe_yuv422_conv *yuv422_conv_param; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + + yuv422_conv_param = (struct vpfe_ipipe_yuv422_conv *)param; + if (!yuv422_conv_param) { + memset(yuv422_conv, 0, sizeof(struct vpfe_ipipe_yuv422_conv)); + yuv422_conv->chrom_pos = VPFE_IPIPE_YUV422_CHR_POS_COSITE; + } else { + memcpy(yuv422_conv, yuv422_conv_param, + sizeof(struct vpfe_ipipe_yuv422_conv)); + if (ipipe_validate_yuv422_conv_params(yuv422_conv) < 0) { + dev_err(dev, "Invalid yuv422 params\n"); + return -EINVAL; + } + } + + ipipe_set_yuv422_conv_regs(ipipe->base_addr, yuv422_conv); + + return 0; +} + +static int +ipipe_get_yuv422_conv_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_yuv422_conv *yuv422_conv = &ipipe->config.yuv422_conv; + struct vpfe_ipipe_yuv422_conv *yuv422_conv_param; + + yuv422_conv_param = (struct vpfe_ipipe_yuv422_conv *)param; + memcpy(yuv422_conv_param, yuv422_conv, + sizeof(struct vpfe_ipipe_yuv422_conv)); + + return 0; +} + +static int ipipe_validate_yee_params(struct vpfe_ipipe_yee *yee) +{ + int i; + + if (yee->en > 1 || + yee->en_halo_red > 1 || + yee->hpf_shft > YEE_HPF_SHIFT_MASK) + return -EINVAL; + + if (yee->hpf_coef_00 > YEE_COEF_MASK || + yee->hpf_coef_01 > YEE_COEF_MASK || + yee->hpf_coef_02 > YEE_COEF_MASK || + yee->hpf_coef_10 > YEE_COEF_MASK || + yee->hpf_coef_11 > YEE_COEF_MASK || + yee->hpf_coef_12 > YEE_COEF_MASK || + yee->hpf_coef_20 > YEE_COEF_MASK || + yee->hpf_coef_21 > YEE_COEF_MASK || + yee->hpf_coef_22 > YEE_COEF_MASK) + return -EINVAL; + + if (yee->yee_thr > YEE_THR_MASK || + yee->es_gain > YEE_ES_GAIN_MASK || + yee->es_thr1 > YEE_ES_THR1_MASK || + yee->es_thr2 > YEE_THR_MASK || + yee->es_gain_grad > YEE_THR_MASK || + yee->es_ofst_grad > YEE_THR_MASK) + return -EINVAL; + + for (i = 0; i < VPFE_IPIPE_MAX_SIZE_YEE_LUT ; i++) + if (yee->table[i] > YEE_ENTRY_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_yee_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_yee *yee_param = (struct vpfe_ipipe_yee *)param; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + struct vpfe_ipipe_yee *yee = &ipipe->config.yee; + + if (!yee_param) { + memset(yee, 0, sizeof(struct vpfe_ipipe_yee)); + } else { + memcpy(yee, yee_param, sizeof(struct vpfe_ipipe_yee)); + if (ipipe_validate_yee_params(yee) < 0) { + dev_err(dev, "Invalid yee params\n"); + return -EINVAL; + } + } + + ipipe_set_ee_regs(ipipe->base_addr, ipipe->isp5_base_addr, yee); + + return 0; +} + +static int ipipe_get_yee_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_yee *yee_param = (struct vpfe_ipipe_yee *)param; + struct vpfe_ipipe_yee *yee = &ipipe->config.yee; + + yee_param->en = yee->en; + yee_param->en_halo_red = yee->en_halo_red; + yee_param->merge_meth = yee->merge_meth; + yee_param->hpf_shft = yee->hpf_shft; + yee_param->hpf_coef_00 = yee->hpf_coef_00; + yee_param->hpf_coef_01 = yee->hpf_coef_01; + yee_param->hpf_coef_02 = yee->hpf_coef_02; + yee_param->hpf_coef_10 = yee->hpf_coef_10; + yee_param->hpf_coef_11 = yee->hpf_coef_11; + yee_param->hpf_coef_12 = yee->hpf_coef_12; + yee_param->hpf_coef_20 = yee->hpf_coef_20; + yee_param->hpf_coef_21 = yee->hpf_coef_21; + yee_param->hpf_coef_22 = yee->hpf_coef_22; + yee_param->yee_thr = yee->yee_thr; + yee_param->es_gain = yee->es_gain; + yee_param->es_thr1 = yee->es_thr1; + yee_param->es_thr2 = yee->es_thr2; + yee_param->es_gain_grad = yee->es_gain_grad; + yee_param->es_ofst_grad = yee->es_ofst_grad; + memcpy(yee_param->table, &yee->table, + (VPFE_IPIPE_MAX_SIZE_YEE_LUT * sizeof(short))); + + return 0; +} + +static int ipipe_validate_car_params(struct vpfe_ipipe_car *car) +{ + if (car->en > 1 || car->hpf_shft > CAR_HPF_SHIFT_MASK || + car->gain1.shft > CAR_GAIN1_SHFT_MASK || + car->gain1.gain_min > CAR_GAIN_MIN_MASK || + car->gain2.shft > CAR_GAIN2_SHFT_MASK || + car->gain2.gain_min > CAR_GAIN_MIN_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_car_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_car *car_param = (struct vpfe_ipipe_car *)param; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + struct vpfe_ipipe_car *car = &ipipe->config.car; + + if (!car_param) { + memset(car , 0, sizeof(struct vpfe_ipipe_car)); + } else { + memcpy(car, car_param, sizeof(struct vpfe_ipipe_car)); + if (ipipe_validate_car_params(car) < 0) { + dev_err(dev, "Invalid car params\n"); + return -EINVAL; + } + } + + ipipe_set_car_regs(ipipe->base_addr, car); + + return 0; +} + +static int ipipe_get_car_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_car *car_param = (struct vpfe_ipipe_car *)param; + struct vpfe_ipipe_car *car = &ipipe->config.car; + + memcpy(car_param, car, sizeof(struct vpfe_ipipe_car)); + return 0; +} + +static int ipipe_validate_cgs_params(struct vpfe_ipipe_cgs *cgs) +{ + if (cgs->en > 1 || cgs->h_shft > CAR_SHIFT_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_cgs_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_cgs *cgs_param = (struct vpfe_ipipe_cgs *)param; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + struct vpfe_ipipe_cgs *cgs = &ipipe->config.cgs; + + if (!cgs_param) { + memset(cgs, 0, sizeof(struct vpfe_ipipe_cgs)); + } else { + memcpy(cgs, cgs_param, sizeof(struct vpfe_ipipe_cgs)); + if (ipipe_validate_cgs_params(cgs) < 0) { + dev_err(dev, "Invalid cgs params\n"); + return -EINVAL; + } + } + + ipipe_set_cgs_regs(ipipe->base_addr, cgs); + + return 0; +} + +static int ipipe_get_cgs_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_cgs *cgs_param = (struct vpfe_ipipe_cgs *)param; + struct vpfe_ipipe_cgs *cgs = &ipipe->config.cgs; + + memcpy(cgs_param, cgs, sizeof(struct vpfe_ipipe_cgs)); + + return 0; +} + +static const struct ipipe_module_if ipipe_modules[VPFE_IPIPE_MAX_MODULES] = { + /* VPFE_IPIPE_INPUT_CONFIG */ { + offsetof(struct ipipe_module_params, input_config), + FIELD_SIZEOF(struct ipipe_module_params, input_config), + offsetof(struct vpfe_ipipe_config, input_config), + ipipe_set_input_config, + ipipe_get_input_config, + }, /* VPFE_IPIPE_LUTDPC */ { + offsetof(struct ipipe_module_params, lutdpc), + FIELD_SIZEOF(struct ipipe_module_params, lutdpc), + offsetof(struct vpfe_ipipe_config, lutdpc), + ipipe_set_lutdpc_params, + ipipe_get_lutdpc_params, + }, /* VPFE_IPIPE_OTFDPC */ { + offsetof(struct ipipe_module_params, otfdpc), + FIELD_SIZEOF(struct ipipe_module_params, otfdpc), + offsetof(struct vpfe_ipipe_config, otfdpc), + ipipe_set_otfdpc_params, + ipipe_get_otfdpc_params, + }, /* VPFE_IPIPE_NF1 */ { + offsetof(struct ipipe_module_params, nf1), + FIELD_SIZEOF(struct ipipe_module_params, nf1), + offsetof(struct vpfe_ipipe_config, nf1), + ipipe_set_nf1_params, + ipipe_get_nf1_params, + }, /* VPFE_IPIPE_NF2 */ { + offsetof(struct ipipe_module_params, nf2), + FIELD_SIZEOF(struct ipipe_module_params, nf2), + offsetof(struct vpfe_ipipe_config, nf2), + ipipe_set_nf2_params, + ipipe_get_nf2_params, + }, /* VPFE_IPIPE_WB */ { + offsetof(struct ipipe_module_params, wbal), + FIELD_SIZEOF(struct ipipe_module_params, wbal), + offsetof(struct vpfe_ipipe_config, wbal), + ipipe_set_wb_params, + ipipe_get_wb_params, + }, /* VPFE_IPIPE_RGB2RGB_1 */ { + offsetof(struct ipipe_module_params, rgb2rgb1), + FIELD_SIZEOF(struct ipipe_module_params, rgb2rgb1), + offsetof(struct vpfe_ipipe_config, rgb2rgb1), + ipipe_set_rgb2rgb_1_params, + ipipe_get_rgb2rgb_1_params, + }, /* VPFE_IPIPE_RGB2RGB_2 */ { + offsetof(struct ipipe_module_params, rgb2rgb2), + FIELD_SIZEOF(struct ipipe_module_params, rgb2rgb2), + offsetof(struct vpfe_ipipe_config, rgb2rgb2), + ipipe_set_rgb2rgb_2_params, + ipipe_get_rgb2rgb_2_params, + }, /* VPFE_IPIPE_GAMMA */ { + offsetof(struct ipipe_module_params, gamma), + FIELD_SIZEOF(struct ipipe_module_params, gamma), + offsetof(struct vpfe_ipipe_config, gamma), + ipipe_set_gamma_params, + ipipe_get_gamma_params, + }, /* VPFE_IPIPE_3D_LUT */ { + offsetof(struct ipipe_module_params, lut), + FIELD_SIZEOF(struct ipipe_module_params, lut), + offsetof(struct vpfe_ipipe_config, lut), + ipipe_set_3d_lut_params, + ipipe_get_3d_lut_params, + }, /* VPFE_IPIPE_RGB2YUV */ { + offsetof(struct ipipe_module_params, rgb2yuv), + FIELD_SIZEOF(struct ipipe_module_params, rgb2yuv), + offsetof(struct vpfe_ipipe_config, rgb2yuv), + ipipe_set_rgb2yuv_params, + ipipe_get_rgb2yuv_params, + }, /* VPFE_IPIPE_YUV422_CONV */ { + offsetof(struct ipipe_module_params, yuv422_conv), + FIELD_SIZEOF(struct ipipe_module_params, yuv422_conv), + offsetof(struct vpfe_ipipe_config, yuv422_conv), + ipipe_set_yuv422_conv_params, + ipipe_get_yuv422_conv_params, + }, /* VPFE_IPIPE_YEE */ { + offsetof(struct ipipe_module_params, yee), + FIELD_SIZEOF(struct ipipe_module_params, yee), + offsetof(struct vpfe_ipipe_config, yee), + ipipe_set_yee_params, + ipipe_get_yee_params, + }, /* VPFE_IPIPE_GIC */ { + offsetof(struct ipipe_module_params, gic), + FIELD_SIZEOF(struct ipipe_module_params, gic), + offsetof(struct vpfe_ipipe_config, gic), + ipipe_set_gic_params, + ipipe_get_gic_params, + }, /* VPFE_IPIPE_CFA */ { + offsetof(struct ipipe_module_params, cfa), + FIELD_SIZEOF(struct ipipe_module_params, cfa), + offsetof(struct vpfe_ipipe_config, cfa), + ipipe_set_cfa_params, + ipipe_get_cfa_params, + }, /* VPFE_IPIPE_CAR */ { + offsetof(struct ipipe_module_params, car), + FIELD_SIZEOF(struct ipipe_module_params, car), + offsetof(struct vpfe_ipipe_config, car), + ipipe_set_car_params, + ipipe_get_car_params, + }, /* VPFE_IPIPE_CGS */ { + offsetof(struct ipipe_module_params, cgs), + FIELD_SIZEOF(struct ipipe_module_params, cgs), + offsetof(struct vpfe_ipipe_config, cgs), + ipipe_set_cgs_params, + ipipe_get_cgs_params, + }, /* VPFE_IPIPE_GBCE */ { + offsetof(struct ipipe_module_params, gbce), + FIELD_SIZEOF(struct ipipe_module_params, gbce), + offsetof(struct vpfe_ipipe_config, gbce), + ipipe_set_gbce_params, + ipipe_get_gbce_params, + }, +}; + +static int ipipe_s_config(struct v4l2_subdev *sd, struct vpfe_ipipe_config *cfg) +{ + struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd); + unsigned int i; + int rval = 0; + + for (i = 0; i < ARRAY_SIZE(ipipe_modules); i++) { + unsigned int bit = 1 << i; + if (cfg->flag & bit) { + const struct ipipe_module_if *module_if = + &ipipe_modules[i]; + struct ipipe_module_params *params; + void __user *from = *(void * __user *) + ((void *)cfg + module_if->config_offset); + size_t size; + void *to; + + params = kmalloc(sizeof(struct ipipe_module_params), + GFP_KERNEL); + to = (void *)params + module_if->param_offset; + size = module_if->param_size; + + if (to && from && size) { + if (copy_from_user(to, from, size)) { + rval = -EFAULT; + break; + } + rval = module_if->set(ipipe, to); + if (rval) + goto error; + } else if (to && !from && size) { + rval = module_if->set(ipipe, NULL); + if (rval) + goto error; + } + kfree(params); + } + } +error: + return rval; +} + +static int ipipe_g_config(struct v4l2_subdev *sd, struct vpfe_ipipe_config *cfg) +{ + struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd); + unsigned int i; + int rval = 0; + + for (i = 1; i < ARRAY_SIZE(ipipe_modules); i++) { + unsigned int bit = 1 << i; + if (cfg->flag & bit) { + const struct ipipe_module_if *module_if = + &ipipe_modules[i]; + struct ipipe_module_params *params; + void __user *to = *(void * __user *) + ((void *)cfg + module_if->config_offset); + size_t size; + void *from; + + params = kmalloc(sizeof(struct ipipe_module_params), + GFP_KERNEL); + from = (void *)params + module_if->param_offset; + size = module_if->param_size; + + if (to && from && size) { + rval = module_if->get(ipipe, from); + if (rval) + goto error; + if (copy_to_user(to, from, size)) { + rval = -EFAULT; + break; + } + } + kfree(params); + } + } +error: + return rval; +} + +/* + * ipipe_ioctl() - Handle ipipe module private ioctl's + * @sd: pointer to v4l2 subdev structure + * @cmd: configuration command + * @arg: configuration argument + */ +static long ipipe_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + int ret = 0; + + switch (cmd) { + case VIDIOC_VPFE_IPIPE_S_CONFIG: + ret = ipipe_s_config(sd, arg); + break; + + case VIDIOC_VPFE_IPIPE_G_CONFIG: + ret = ipipe_g_config(sd, arg); + break; + + default: + ret = -ENOIOCTLCMD; + } + return ret; +} + +void vpfe_ipipe_enable(struct vpfe_device *vpfe_dev, int en) +{ + struct vpfe_ipipeif_device *ipipeif = &vpfe_dev->vpfe_ipipeif; + struct vpfe_ipipe_device *ipipe = &vpfe_dev->vpfe_ipipe; + unsigned char val; + + if (ipipe->input == IPIPE_INPUT_NONE) + return; + + /* ipipe is set to single shot */ + if (ipipeif->input == IPIPEIF_INPUT_MEMORY && en) { + /* for single-shot mode, need to wait for h/w to + * reset many register bits + */ + do { + val = regr_ip(vpfe_dev->vpfe_ipipe.base_addr, + IPIPE_SRC_EN); + } while (val); + } + regw_ip(vpfe_dev->vpfe_ipipe.base_addr, en, IPIPE_SRC_EN); +} + +/* + * ipipe_set_stream() - Enable/Disable streaming on the ipipe subdevice + * @sd: pointer to v4l2 subdev structure + * @enable: 1 == Enable, 0 == Disable + */ +static int ipipe_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd); + struct vpfe_device *vpfe_dev = to_vpfe_device(ipipe); + + if (enable && ipipe->input != IPIPE_INPUT_NONE && + ipipe->output != IPIPE_OUTPUT_NONE) { + if (config_ipipe_hw(ipipe) < 0) + return -EINVAL; + } + + vpfe_ipipe_enable(vpfe_dev, enable); + + return 0; +} + +/* + * __ipipe_get_format() - helper function for getting ipipe format + * @ipipe: pointer to ipipe private structure. + * @pad: pad number. + * @fh: V4L2 subdev file handle. + * @which: wanted subdev format. + * + */ +static struct v4l2_mbus_framefmt * +__ipipe_get_format(struct vpfe_ipipe_device *ipipe, + struct v4l2_subdev_fh *fh, unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(fh, pad); + + return &ipipe->formats[pad]; +} + +/* + * ipipe_try_format() - Handle try format by pad subdev method + * @ipipe: VPFE ipipe device. + * @fh: V4L2 subdev file handle. + * @pad: pad num. + * @fmt: pointer to v4l2 format structure. + * @which : wanted subdev format + */ +static void +ipipe_try_format(struct vpfe_ipipe_device *ipipe, + struct v4l2_subdev_fh *fh, unsigned int pad, + struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) +{ + unsigned int max_out_height; + unsigned int max_out_width; + unsigned int i; + + max_out_width = IPIPE_MAX_OUTPUT_WIDTH_A; + max_out_height = IPIPE_MAX_OUTPUT_HEIGHT_A; + + if (pad == IPIPE_PAD_SINK) { + for (i = 0; i < ARRAY_SIZE(ipipe_input_fmts); i++) + if (fmt->code == ipipe_input_fmts[i]) + break; + + /* If not found, use SBGGR10 as default */ + if (i >= ARRAY_SIZE(ipipe_input_fmts)) + fmt->code = V4L2_MBUS_FMT_SGRBG12_1X12; + } else if (pad == IPIPE_PAD_SOURCE) { + for (i = 0; i < ARRAY_SIZE(ipipe_output_fmts); i++) + if (fmt->code == ipipe_output_fmts[i]) + break; + + /* If not found, use UYVY as default */ + if (i >= ARRAY_SIZE(ipipe_output_fmts)) + fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + } + + fmt->width = clamp_t(u32, fmt->width, MIN_OUT_HEIGHT, max_out_width); + fmt->height = clamp_t(u32, fmt->height, MIN_OUT_WIDTH, max_out_height); +} + +/* + * ipipe_set_format() - Handle set format by pads subdev method + * @sd: pointer to v4l2 subdev structure + * @fh: V4L2 subdev file handle + * @fmt: pointer to v4l2 subdev format structure + * return -EINVAL or zero on success + */ +static int +ipipe_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __ipipe_get_format(ipipe, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + ipipe_try_format(ipipe, fh, fmt->pad, &fmt->format, fmt->which); + *format = fmt->format; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + if (fmt->pad == IPIPE_PAD_SINK && + (ipipe->input == IPIPE_INPUT_CCDC || + ipipe->input == IPIPE_INPUT_MEMORY)) + ipipe->formats[fmt->pad] = fmt->format; + else if (fmt->pad == IPIPE_PAD_SOURCE && + ipipe->output == IPIPE_OUTPUT_RESIZER) + ipipe->formats[fmt->pad] = fmt->format; + else + return -EINVAL; + + return 0; +} + +/* + * ipipe_get_format() - Handle get format by pads subdev method. + * @sd: pointer to v4l2 subdev structure. + * @fh: V4L2 subdev file handle. + * @fmt: pointer to v4l2 subdev format structure. + */ +static int +ipipe_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd); + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + fmt->format = ipipe->formats[fmt->pad]; + else + fmt->format = *(v4l2_subdev_get_try_format(fh, fmt->pad)); + + return 0; +} + +/* + * ipipe_enum_frame_size() - enum frame sizes on pads + * @sd: pointer to v4l2 subdev structure. + * @fh: V4L2 subdev file handle. + * @fse: pointer to v4l2_subdev_frame_size_enum structure. + */ +static int +ipipe_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt format; + + if (fse->index != 0) + return -EINVAL; + + format.code = fse->code; + format.width = 1; + format.height = 1; + ipipe_try_format(ipipe, fh, fse->pad, &format, + V4L2_SUBDEV_FORMAT_TRY); + fse->min_width = format.width; + fse->min_height = format.height; + + if (format.code != fse->code) + return -EINVAL; + + format.code = fse->code; + format.width = -1; + format.height = -1; + ipipe_try_format(ipipe, fh, fse->pad, &format, + V4L2_SUBDEV_FORMAT_TRY); + fse->max_width = format.width; + fse->max_height = format.height; + + return 0; +} + +/* + * ipipe_enum_mbus_code() - enum mbus codes for pads + * @sd: pointer to v4l2 subdev structure. + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + */ +static int +ipipe_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + switch (code->pad) { + case IPIPE_PAD_SINK: + if (code->index >= ARRAY_SIZE(ipipe_input_fmts)) + return -EINVAL; + code->code = ipipe_input_fmts[code->index]; + break; + + case IPIPE_PAD_SOURCE: + if (code->index >= ARRAY_SIZE(ipipe_output_fmts)) + return -EINVAL; + code->code = ipipe_output_fmts[code->index]; + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* + * ipipe_s_ctrl() - Handle set control subdev method + * @ctrl: pointer to v4l2 control structure + */ +static int ipipe_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vpfe_ipipe_device *ipipe = + container_of(ctrl->handler, struct vpfe_ipipe_device, ctrls); + struct ipipe_lum_adj *lum_adj = &ipipe->config.lum_adj; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + lum_adj->brightness = ctrl->val; + ipipe_set_lum_adj_regs(ipipe->base_addr, lum_adj); + break; + + case V4L2_CID_CONTRAST: + lum_adj->contrast = ctrl->val; + ipipe_set_lum_adj_regs(ipipe->base_addr, lum_adj); + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* + * ipipe_init_formats() - Initialize formats on all pads + * @sd: pointer to v4l2 subdev structure. + * @fh: V4L2 subdev file handle + * + * Initialize all pad formats with default values. If fh is not NULL, try + * formats are initialized on the file handle. Otherwise active formats are + * initialized on the device. + */ +static int +ipipe_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_subdev_format format; + + memset(&format, 0, sizeof(format)); + format.pad = IPIPE_PAD_SINK; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_SGRBG12_1X12; + format.format.width = IPIPE_MAX_OUTPUT_WIDTH_A; + format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_A; + ipipe_set_format(sd, fh, &format); + + memset(&format, 0, sizeof(format)); + format.pad = IPIPE_PAD_SOURCE; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_UYVY8_2X8; + format.format.width = IPIPE_MAX_OUTPUT_WIDTH_A; + format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_A; + ipipe_set_format(sd, fh, &format); + + return 0; +} + +/* subdev core operations */ +static const struct v4l2_subdev_core_ops ipipe_v4l2_core_ops = { + .ioctl = ipipe_ioctl, +}; + +static const struct v4l2_ctrl_ops ipipe_ctrl_ops = { + .s_ctrl = ipipe_s_ctrl, +}; + +/* subdev file operations */ +static const struct v4l2_subdev_internal_ops ipipe_v4l2_internal_ops = { + .open = ipipe_init_formats, +}; + +/* subdev video operations */ +static const struct v4l2_subdev_video_ops ipipe_v4l2_video_ops = { + .s_stream = ipipe_set_stream, +}; + +/* subdev pad operations */ +static const struct v4l2_subdev_pad_ops ipipe_v4l2_pad_ops = { + .enum_mbus_code = ipipe_enum_mbus_code, + .enum_frame_size = ipipe_enum_frame_size, + .get_fmt = ipipe_get_format, + .set_fmt = ipipe_set_format, +}; + +/* v4l2 subdev operation */ +static const struct v4l2_subdev_ops ipipe_v4l2_ops = { + .core = &ipipe_v4l2_core_ops, + .video = &ipipe_v4l2_video_ops, + .pad = &ipipe_v4l2_pad_ops, +}; + +/* + * Media entity operations + */ + +/* + * ipipe_link_setup() - Setup ipipe connections + * @entity: ipipe media entity + * @local: Pad at the local end of the link + * @remote: Pad at the remote end of the link + * @flags: Link flags + * + * return -EINVAL or zero on success + */ +static int +ipipe_link_setup(struct media_entity *entity, const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd); + struct vpfe_device *vpfe_dev = to_vpfe_device(ipipe); + u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input; + + switch (local->index | media_entity_type(remote->entity)) { + case IPIPE_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + ipipe->input = IPIPE_INPUT_NONE; + break; + } + if (ipipe->input != IPIPE_INPUT_NONE) + return -EBUSY; + if (ipipeif_sink == IPIPEIF_INPUT_MEMORY) + ipipe->input = IPIPE_INPUT_MEMORY; + else + ipipe->input = IPIPE_INPUT_CCDC; + break; + + case IPIPE_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + /* out to RESIZER */ + if (flags & MEDIA_LNK_FL_ENABLED) + ipipe->output = IPIPE_OUTPUT_RESIZER; + else + ipipe->output = IPIPE_OUTPUT_NONE; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static const struct media_entity_operations ipipe_media_ops = { + .link_setup = ipipe_link_setup, +}; + +/* + * vpfe_ipipe_unregister_entities() - ipipe unregister entity + * @vpfe_ipipe: pointer to ipipe subdevice structure. + */ +void vpfe_ipipe_unregister_entities(struct vpfe_ipipe_device *vpfe_ipipe) +{ + /* cleanup entity */ + media_entity_cleanup(&vpfe_ipipe->subdev.entity); + /* unregister subdev */ + v4l2_device_unregister_subdev(&vpfe_ipipe->subdev); +} + +/* + * vpfe_ipipe_register_entities() - ipipe register entity + * @ipipe: pointer to ipipe subdevice structure. + * @vdev: pointer to v4l2 device structure. + */ +int +vpfe_ipipe_register_entities(struct vpfe_ipipe_device *ipipe, + struct v4l2_device *vdev) +{ + int ret; + + /* Register the subdev */ + ret = v4l2_device_register_subdev(vdev, &ipipe->subdev); + if (ret) { + pr_err("Failed to register ipipe as v4l2 subdevice\n"); + return ret; + } + + return ret; +} + +#define IPIPE_CONTRAST_HIGH 0xff +#define IPIPE_BRIGHT_HIGH 0xff + +/* + * vpfe_ipipe_init() - ipipe module initialization. + * @ipipe: pointer to ipipe subdevice structure. + * @pdev: platform device pointer. + */ +int +vpfe_ipipe_init(struct vpfe_ipipe_device *ipipe, struct platform_device *pdev) +{ + struct media_pad *pads = &ipipe->pads[0]; + struct v4l2_subdev *sd = &ipipe->subdev; + struct media_entity *me = &sd->entity; + static resource_size_t res_len; + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 4); + if (!res) + return -ENOENT; + + res_len = resource_size(res); + res = request_mem_region(res->start, res_len, res->name); + if (!res) + return -EBUSY; + ipipe->base_addr = ioremap_nocache(res->start, res_len); + if (!ipipe->base_addr) + return -EBUSY; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 6); + if (!res) + return -ENOENT; + ipipe->isp5_base_addr = ioremap_nocache(res->start, res_len); + if (!ipipe->isp5_base_addr) + return -EBUSY; + + v4l2_subdev_init(sd, &ipipe_v4l2_ops); + sd->internal_ops = &ipipe_v4l2_internal_ops; + strlcpy(sd->name, "DAVINCI IPIPE", sizeof(sd->name)); + sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ + v4l2_set_subdevdata(sd, ipipe); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + pads[IPIPE_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[IPIPE_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + ipipe->input = IPIPE_INPUT_NONE; + ipipe->output = IPIPE_OUTPUT_NONE; + + me->ops = &ipipe_media_ops; + v4l2_ctrl_handler_init(&ipipe->ctrls, 2); + v4l2_ctrl_new_std(&ipipe->ctrls, &ipipe_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, + IPIPE_BRIGHT_HIGH, 1, 16); + v4l2_ctrl_new_std(&ipipe->ctrls, &ipipe_ctrl_ops, + V4L2_CID_CONTRAST, 0, + IPIPE_CONTRAST_HIGH, 1, 16); + + + v4l2_ctrl_handler_setup(&ipipe->ctrls); + sd->ctrl_handler = &ipipe->ctrls; + + return media_entity_init(me, IPIPE_PADS_NUM, pads, 0); +} + +/* + * vpfe_ipipe_cleanup() - ipipe subdevice cleanup. + * @ipipe: pointer to ipipe subdevice + * @dev: pointer to platform device + */ +void vpfe_ipipe_cleanup(struct vpfe_ipipe_device *ipipe, + struct platform_device *pdev) +{ + struct resource *res; + + v4l2_ctrl_handler_free(&ipipe->ctrls); + + iounmap(ipipe->base_addr); + iounmap(ipipe->isp5_base_addr); + res = platform_get_resource(pdev, IORESOURCE_MEM, 4); + if (res) + release_mem_region(res->start, res->end - res->start + 1); +} diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe.h b/drivers/staging/media/davinci_vpfe/dm365_ipipe.h new file mode 100644 index 000000000000..cf4204603eb8 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe.h @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + */ + +#ifndef _DAVINCI_VPFE_DM365_IPIPE_H +#define _DAVINCI_VPFE_DM365_IPIPE_H + +#include <linux/platform_device.h> + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-subdev.h> + +#include "davinci_vpfe_user.h" +#include "vpfe_video.h" + +#define CEIL(a, b) (((a) + (b-1)) / (b)) + +enum ipipe_noise_filter { + IPIPE_D2F_1ST = 0, + IPIPE_D2F_2ND = 1, +}; + +/* Used for driver storage */ +struct ipipe_otfdpc_2_0 { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* defect detection method */ + enum vpfe_ipipe_otfdpc_det_meth det_method; + /* Algorithm used. Applicable only when IPIPE_DPC_OTF_MIN_MAX2 is + * used + */ + enum vpfe_ipipe_otfdpc_alg alg; + struct vpfe_ipipe_otfdpc_2_0_cfg otfdpc_2_0; +}; + +struct ipipe_otfdpc_3_0 { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* defect detection method */ + enum vpfe_ipipe_otfdpc_det_meth det_method; + /* Algorithm used. Applicable only when IPIPE_DPC_OTF_MIN_MAX2 is + * used + */ + enum vpfe_ipipe_otfdpc_alg alg; + struct vpfe_ipipe_otfdpc_3_0_cfg otfdpc_3_0; +}; + +/* Structure for configuring Luminance Adjustment module */ +struct ipipe_lum_adj { + /* Brightness adjustments */ + unsigned char brightness; + /* contrast adjustments */ + unsigned char contrast; +}; + +enum ipipe_rgb2rgb { + IPIPE_RGB2RGB_1 = 0, + IPIPE_RGB2RGB_2 = 1, +}; + +struct ipipe_module_params { + __u32 flag; + struct vpfe_ipipe_input_config input_config; + struct vpfe_ipipe_lutdpc lutdpc; + struct vpfe_ipipe_otfdpc otfdpc; + struct vpfe_ipipe_nf nf1; + struct vpfe_ipipe_nf nf2; + struct vpfe_ipipe_gic gic; + struct vpfe_ipipe_wb wbal; + struct vpfe_ipipe_cfa cfa; + struct vpfe_ipipe_rgb2rgb rgb2rgb1; + struct vpfe_ipipe_rgb2rgb rgb2rgb2; + struct vpfe_ipipe_gamma gamma; + struct vpfe_ipipe_3d_lut lut; + struct vpfe_ipipe_rgb2yuv rgb2yuv; + struct vpfe_ipipe_gbce gbce; + struct vpfe_ipipe_yuv422_conv yuv422_conv; + struct vpfe_ipipe_yee yee; + struct vpfe_ipipe_car car; + struct vpfe_ipipe_cgs cgs; + struct ipipe_lum_adj lum_adj; +}; + +#define IPIPE_PAD_SINK 0 +#define IPIPE_PAD_SOURCE 1 + +#define IPIPE_PADS_NUM 2 + +#define IPIPE_OUTPUT_NONE 0 +#define IPIPE_OUTPUT_RESIZER (1 << 0) + +enum ipipe_input_entity { + IPIPE_INPUT_NONE = 0, + IPIPE_INPUT_MEMORY = 1, + IPIPE_INPUT_CCDC = 2, +}; + + +struct vpfe_ipipe_device { + struct v4l2_subdev subdev; + struct media_pad pads[IPIPE_PADS_NUM]; + struct v4l2_mbus_framefmt formats[IPIPE_PADS_NUM]; + enum ipipe_input_entity input; + unsigned int output; + struct v4l2_ctrl_handler ctrls; + void *__iomem base_addr; + void *__iomem isp5_base_addr; + struct ipipe_module_params config; +}; + +struct ipipe_module_if { + unsigned int param_offset; + unsigned int param_size; + unsigned int config_offset; + int (*set)(struct vpfe_ipipe_device *ipipe, void *param); + int (*get)(struct vpfe_ipipe_device *ipipe, void *param); +}; + +/* data paths */ +enum ipipe_data_paths { + IPIPE_RAW2YUV, + /* Bayer RAW input to YCbCr output */ + IPIPE_RAW2RAW, + /* Bayer Raw to Bayer output */ + IPIPE_RAW2BOX, + /* Bayer Raw to Boxcar output */ + IPIPE_YUV2YUV + /* YUV Raw to YUV Raw output */ +}; + +#define IPIPE_COLPTN_R_Ye 0x0 +#define IPIPE_COLPTN_Gr_Cy 0x1 +#define IPIPE_COLPTN_Gb_G 0x2 +#define IPIPE_COLPTN_B_Mg 0x3 + +#define COLPAT_EE_SHIFT 0 +#define COLPAT_EO_SHIFT 2 +#define COLPAT_OE_SHIFT 4 +#define COLPAT_OO_SHIFT 6 + +#define ipipe_sgrbg_pattern \ + (IPIPE_COLPTN_Gr_Cy << COLPAT_EE_SHIFT | \ + IPIPE_COLPTN_R_Ye << COLPAT_EO_SHIFT | \ + IPIPE_COLPTN_B_Mg << COLPAT_OE_SHIFT | \ + IPIPE_COLPTN_Gb_G << COLPAT_OO_SHIFT) + +#define ipipe_srggb_pattern \ + (IPIPE_COLPTN_R_Ye << COLPAT_EE_SHIFT | \ + IPIPE_COLPTN_Gr_Cy << COLPAT_EO_SHIFT | \ + IPIPE_COLPTN_Gb_G << COLPAT_OE_SHIFT | \ + IPIPE_COLPTN_B_Mg << COLPAT_OO_SHIFT) + +int vpfe_ipipe_register_entities(struct vpfe_ipipe_device *ipipe, + struct v4l2_device *v4l2_dev); +int vpfe_ipipe_init(struct vpfe_ipipe_device *ipipe, + struct platform_device *pdev); +void vpfe_ipipe_unregister_entities(struct vpfe_ipipe_device *ipipe); +void vpfe_ipipe_cleanup(struct vpfe_ipipe_device *ipipe, + struct platform_device *pdev); +void vpfe_ipipe_enable(struct vpfe_device *vpfe_dev, int en); + +#endif /* _DAVINCI_VPFE_DM365_IPIPE_H */ diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c new file mode 100644 index 000000000000..e027b92b54ef --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c @@ -0,0 +1,1048 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + */ + +#include "dm365_ipipe_hw.h" + +#define IPIPE_MODE_CONTINUOUS 0 +#define IPIPE_MODE_SINGLE_SHOT 1 + +static void ipipe_clock_enable(void *__iomem base_addr) +{ + /* enable IPIPE MMR for register write access */ + regw_ip(base_addr, IPIPE_GCK_MMR_DEFAULT, IPIPE_GCK_MMR); + + /* enable the clock wb,cfa,dfc,d2f,pre modules */ + regw_ip(base_addr, IPIPE_GCK_PIX_DEFAULT, IPIPE_GCK_PIX); +} + +static void +rsz_set_common_params(void *__iomem rsz_base, struct resizer_params *params) +{ + struct rsz_common_params *rsz_common = ¶ms->rsz_common; + u32 val; + + /* Set mode */ + regw_rsz(rsz_base, params->oper_mode, RSZ_SRC_MODE); + + /* data source selection and bypass */ + val = (rsz_common->passthrough << RSZ_BYPASS_SHIFT) | + rsz_common->source; + regw_rsz(rsz_base, val, RSZ_SRC_FMT0); + + /* src image selection */ + val = (rsz_common->raw_flip & 1) | + (rsz_common->src_img_fmt << RSZ_SRC_IMG_FMT_SHIFT) | + ((rsz_common->y_c & 1) << RSZ_SRC_Y_C_SEL_SHIFT); + regw_rsz(rsz_base, val, RSZ_SRC_FMT1); + + regw_rsz(rsz_base, rsz_common->vps & IPIPE_RSZ_VPS_MASK, RSZ_SRC_VPS); + regw_rsz(rsz_base, rsz_common->hps & IPIPE_RSZ_HPS_MASK, RSZ_SRC_HPS); + regw_rsz(rsz_base, rsz_common->vsz & IPIPE_RSZ_VSZ_MASK, RSZ_SRC_VSZ); + regw_rsz(rsz_base, rsz_common->hsz & IPIPE_RSZ_HSZ_MASK, RSZ_SRC_HSZ); + regw_rsz(rsz_base, rsz_common->yuv_y_min, RSZ_YUV_Y_MIN); + regw_rsz(rsz_base, rsz_common->yuv_y_max, RSZ_YUV_Y_MAX); + regw_rsz(rsz_base, rsz_common->yuv_c_min, RSZ_YUV_C_MIN); + regw_rsz(rsz_base, rsz_common->yuv_c_max, RSZ_YUV_C_MAX); + /* chromatic position */ + regw_rsz(rsz_base, rsz_common->out_chr_pos, RSZ_YUV_PHS); +} + +static void +rsz_set_rsz_regs(void *__iomem rsz_base, unsigned int rsz_id, + struct resizer_params *params) +{ + struct resizer_scale_param *rsc_params; + struct rsz_ext_mem_param *ext_mem; + struct resizer_rgb *rgb; + u32 reg_base; + u32 val; + + rsc_params = ¶ms->rsz_rsc_param[rsz_id]; + rgb = ¶ms->rsz2rgb[rsz_id]; + ext_mem = ¶ms->ext_mem_param[rsz_id]; + + if (rsz_id == RSZ_A) { + val = rsc_params->h_flip << RSZA_H_FLIP_SHIFT; + val |= rsc_params->v_flip << RSZA_V_FLIP_SHIFT; + reg_base = RSZ_EN_A; + } else { + val = rsc_params->h_flip << RSZB_H_FLIP_SHIFT; + val |= rsc_params->v_flip << RSZB_V_FLIP_SHIFT; + reg_base = RSZ_EN_B; + } + /* update flip settings */ + regw_rsz(rsz_base, val, RSZ_SEQ); + + regw_rsz(rsz_base, params->oper_mode, reg_base + RSZ_MODE); + + val = (rsc_params->cen << RSZ_CEN_SHIFT) | rsc_params->yen; + regw_rsz(rsz_base, val, reg_base + RSZ_420); + + regw_rsz(rsz_base, rsc_params->i_vps & RSZ_VPS_MASK, + reg_base + RSZ_I_VPS); + regw_rsz(rsz_base, rsc_params->i_hps & RSZ_HPS_MASK, + reg_base + RSZ_I_HPS); + regw_rsz(rsz_base, rsc_params->o_vsz & RSZ_O_VSZ_MASK, + reg_base + RSZ_O_VSZ); + regw_rsz(rsz_base, rsc_params->o_hsz & RSZ_O_HSZ_MASK, + reg_base + RSZ_O_HSZ); + regw_rsz(rsz_base, rsc_params->v_phs_y & RSZ_V_PHS_MASK, + reg_base + RSZ_V_PHS_Y); + regw_rsz(rsz_base, rsc_params->v_phs_c & RSZ_V_PHS_MASK, + reg_base + RSZ_V_PHS_C); + + /* keep this additional adjustment to zero for now */ + regw_rsz(rsz_base, rsc_params->v_dif & RSZ_V_DIF_MASK, + reg_base + RSZ_V_DIF); + + val = (rsc_params->v_typ_y & 1) | + ((rsc_params->v_typ_c & 1) << RSZ_TYP_C_SHIFT); + regw_rsz(rsz_base, val, reg_base + RSZ_V_TYP); + + val = (rsc_params->v_lpf_int_y & RSZ_LPF_INT_MASK) | + ((rsc_params->v_lpf_int_c & RSZ_LPF_INT_MASK) << + RSZ_LPF_INT_C_SHIFT); + regw_rsz(rsz_base, val, reg_base + RSZ_V_LPF); + + regw_rsz(rsz_base, rsc_params->h_phs & + RSZ_H_PHS_MASK, reg_base + RSZ_H_PHS); + + regw_rsz(rsz_base, 0, reg_base + RSZ_H_PHS_ADJ); + regw_rsz(rsz_base, rsc_params->h_dif & + RSZ_H_DIF_MASK, reg_base + RSZ_H_DIF); + + val = (rsc_params->h_typ_y & 1) | + ((rsc_params->h_typ_c & 1) << RSZ_TYP_C_SHIFT); + regw_rsz(rsz_base, val, reg_base + RSZ_H_TYP); + + val = (rsc_params->h_lpf_int_y & RSZ_LPF_INT_MASK) | + ((rsc_params->h_lpf_int_c & RSZ_LPF_INT_MASK) << + RSZ_LPF_INT_C_SHIFT); + regw_rsz(rsz_base, val, reg_base + RSZ_H_LPF); + + regw_rsz(rsz_base, rsc_params->dscale_en & 1, reg_base + RSZ_DWN_EN); + + val = (rsc_params->h_dscale_ave_sz & RSZ_DWN_SCALE_AV_SZ_MASK) | + ((rsc_params->v_dscale_ave_sz & RSZ_DWN_SCALE_AV_SZ_MASK) << + RSZ_DWN_SCALE_AV_SZ_V_SHIFT); + regw_rsz(rsz_base, val, reg_base + RSZ_DWN_AV); + + /* setting rgb conversion parameters */ + regw_rsz(rsz_base, rgb->rgb_en, reg_base + RSZ_RGB_EN); + + val = (rgb->rgb_typ << RSZ_RGB_TYP_SHIFT) | + (rgb->rgb_msk0 << RSZ_RGB_MSK0_SHIFT) | + (rgb->rgb_msk1 << RSZ_RGB_MSK1_SHIFT); + regw_rsz(rsz_base, val, reg_base + RSZ_RGB_TYP); + + regw_rsz(rsz_base, rgb->rgb_alpha_val & RSZ_RGB_ALPHA_MASK, + reg_base + RSZ_RGB_BLD); + + /* setting external memory parameters */ + regw_rsz(rsz_base, ext_mem->rsz_sdr_oft_y, reg_base + RSZ_SDR_Y_OFT); + regw_rsz(rsz_base, ext_mem->rsz_sdr_ptr_s_y, + reg_base + RSZ_SDR_Y_PTR_S); + regw_rsz(rsz_base, ext_mem->rsz_sdr_ptr_e_y, + reg_base + RSZ_SDR_Y_PTR_E); + regw_rsz(rsz_base, ext_mem->rsz_sdr_oft_c, reg_base + RSZ_SDR_C_OFT); + regw_rsz(rsz_base, ext_mem->rsz_sdr_ptr_s_c, + reg_base + RSZ_SDR_C_PTR_S); + regw_rsz(rsz_base, (ext_mem->rsz_sdr_ptr_e_c >> 1), + reg_base + RSZ_SDR_C_PTR_E); +} + +/*set the registers of either RSZ0 or RSZ1 */ +static void +ipipe_setup_resizer(void *__iomem rsz_base, struct resizer_params *params) +{ + /* enable MMR gate to write to Resizer */ + regw_rsz(rsz_base, 1, RSZ_GCK_MMR); + + /* Enable resizer if it is not in bypass mode */ + if (params->rsz_common.passthrough) + regw_rsz(rsz_base, 0, RSZ_GCK_SDR); + else + regw_rsz(rsz_base, 1, RSZ_GCK_SDR); + + rsz_set_common_params(rsz_base, params); + + regw_rsz(rsz_base, params->rsz_en[RSZ_A], RSZ_EN_A); + + if (params->rsz_en[RSZ_A]) + /*setting rescale parameters */ + rsz_set_rsz_regs(rsz_base, RSZ_A, params); + + regw_rsz(rsz_base, params->rsz_en[RSZ_B], RSZ_EN_B); + + if (params->rsz_en[RSZ_B]) + rsz_set_rsz_regs(rsz_base, RSZ_B, params); +} + +static u32 ipipe_get_color_pat(enum v4l2_mbus_pixelcode pix) +{ + switch (pix) { + case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8: + case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: + case V4L2_MBUS_FMT_SGRBG12_1X12: + return ipipe_sgrbg_pattern; + + default: + return ipipe_srggb_pattern; + } +} + +static int ipipe_get_data_path(struct vpfe_ipipe_device *ipipe) +{ + enum v4l2_mbus_pixelcode temp_pix_fmt; + + switch (ipipe->formats[IPIPE_PAD_SINK].code) { + case V4L2_MBUS_FMT_SBGGR8_1X8: + case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8: + case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: + case V4L2_MBUS_FMT_SGRBG12_1X12: + temp_pix_fmt = V4L2_MBUS_FMT_SGRBG12_1X12; + break; + + default: + temp_pix_fmt = V4L2_MBUS_FMT_UYVY8_2X8; + } + + if (temp_pix_fmt == V4L2_MBUS_FMT_SGRBG12_1X12) { + if (ipipe->formats[IPIPE_PAD_SOURCE].code == + V4L2_MBUS_FMT_SGRBG12_1X12) + return IPIPE_RAW2RAW; + return IPIPE_RAW2YUV; + } + + return IPIPE_YUV2YUV; +} + +static int get_ipipe_mode(struct vpfe_ipipe_device *ipipe) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(ipipe); + u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input; + + if (ipipeif_sink == IPIPEIF_INPUT_MEMORY) + return IPIPE_MODE_SINGLE_SHOT; + else if (ipipeif_sink == IPIPEIF_INPUT_ISIF) + return IPIPE_MODE_CONTINUOUS; + + return -EINVAL; +} + +int config_ipipe_hw(struct vpfe_ipipe_device *ipipe) +{ + struct vpfe_ipipe_input_config *config = &ipipe->config.input_config; + void __iomem *ipipe_base = ipipe->base_addr; + struct v4l2_mbus_framefmt *outformat; + u32 color_pat; + u32 ipipe_mode; + u32 data_path; + + /* enable clock to IPIPE */ + vpss_enable_clock(VPSS_IPIPE_CLOCK, 1); + ipipe_clock_enable(ipipe_base); + + if (ipipe->input == IPIPE_INPUT_NONE) { + regw_ip(ipipe_base, 0, IPIPE_SRC_EN); + return 0; + } + + ipipe_mode = get_ipipe_mode(ipipe); + if (ipipe < 0) { + pr_err("Failed to get ipipe mode"); + return -EINVAL; + } + regw_ip(ipipe_base, ipipe_mode, IPIPE_SRC_MODE); + + data_path = ipipe_get_data_path(ipipe); + regw_ip(ipipe_base, data_path, IPIPE_SRC_FMT); + + regw_ip(ipipe_base, config->vst & IPIPE_RSZ_VPS_MASK, IPIPE_SRC_VPS); + regw_ip(ipipe_base, config->hst & IPIPE_RSZ_HPS_MASK, IPIPE_SRC_HPS); + + outformat = &ipipe->formats[IPIPE_PAD_SOURCE]; + regw_ip(ipipe_base, (outformat->height + 1) & IPIPE_RSZ_VSZ_MASK, + IPIPE_SRC_VSZ); + regw_ip(ipipe_base, (outformat->width + 1) & IPIPE_RSZ_HSZ_MASK, + IPIPE_SRC_HSZ); + + if (data_path == IPIPE_RAW2YUV || + data_path == IPIPE_RAW2RAW) { + color_pat = + ipipe_get_color_pat(ipipe->formats[IPIPE_PAD_SINK].code); + regw_ip(ipipe_base, color_pat, IPIPE_SRC_COL); + } + + return 0; +} + +/* + * config_rsz_hw() - Performs hardware setup of resizer. + */ +int config_rsz_hw(struct vpfe_resizer_device *resizer, + struct resizer_params *config) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + void *__iomem ipipe_base = vpfe_dev->vpfe_ipipe.base_addr; + void *__iomem rsz_base = vpfe_dev->vpfe_resizer.base_addr; + + /* enable VPSS clock */ + vpss_enable_clock(VPSS_IPIPE_CLOCK, 1); + ipipe_clock_enable(ipipe_base); + + ipipe_setup_resizer(rsz_base, config); + + return 0; +} + +static void +rsz_set_y_address(void *__iomem rsz_base, unsigned int address, + unsigned int offset) +{ + u32 val; + + val = address & SET_LOW_ADDR; + regw_rsz(rsz_base, val, offset + RSZ_SDR_Y_BAD_L); + regw_rsz(rsz_base, val, offset + RSZ_SDR_Y_SAD_L); + + val = (address & SET_HIGH_ADDR) >> 16; + regw_rsz(rsz_base, val, offset + RSZ_SDR_Y_BAD_H); + regw_rsz(rsz_base, val, offset + RSZ_SDR_Y_SAD_H); +} + +static void +rsz_set_c_address(void *__iomem rsz_base, unsigned int address, + unsigned int offset) +{ + u32 val; + + val = address & SET_LOW_ADDR; + regw_rsz(rsz_base, val, offset + RSZ_SDR_C_BAD_L); + regw_rsz(rsz_base, val, offset + RSZ_SDR_C_SAD_L); + + val = (address & SET_HIGH_ADDR) >> 16; + regw_rsz(rsz_base, val, offset + RSZ_SDR_C_BAD_H); + regw_rsz(rsz_base, val, offset + RSZ_SDR_C_SAD_H); +} + +/* + * resizer_set_outaddr() - set the address for given resize_no + * @rsz_base: resizer base address + * @params: pointer to ipipe_params structure + * @resize_no: 0 - Resizer-A, 1 - Resizer B + * @address: the address to set + */ +int +resizer_set_outaddr(void *__iomem rsz_base, struct resizer_params *params, + int resize_no, unsigned int address) +{ + struct resizer_scale_param *rsc_param; + struct rsz_ext_mem_param *mem_param; + struct rsz_common_params *rsz_common; + unsigned int rsz_start_add; + unsigned int val; + + if (resize_no != RSZ_A && resize_no != RSZ_B) + return -EINVAL; + + mem_param = ¶ms->ext_mem_param[resize_no]; + rsc_param = ¶ms->rsz_rsc_param[resize_no]; + rsz_common = ¶ms->rsz_common; + + if (resize_no == RSZ_A) + rsz_start_add = RSZ_EN_A; + else + rsz_start_add = RSZ_EN_B; + + /* y_c = 0 for y, = 1 for c */ + if (rsz_common->src_img_fmt == RSZ_IMG_420) { + if (rsz_common->y_c) { + /* C channel */ + val = address + mem_param->flip_ofst_c; + rsz_set_c_address(rsz_base, val, rsz_start_add); + } else { + val = address + mem_param->flip_ofst_y; + rsz_set_y_address(rsz_base, val, rsz_start_add); + } + } else { + if (rsc_param->cen && rsc_param->yen) { + /* 420 */ + val = address + mem_param->c_offset + + mem_param->flip_ofst_c + + mem_param->user_y_ofst + + mem_param->user_c_ofst; + if (resize_no == RSZ_B) + val += + params->ext_mem_param[RSZ_A].user_y_ofst + + params->ext_mem_param[RSZ_A].user_c_ofst; + /* set C address */ + rsz_set_c_address(rsz_base, val, rsz_start_add); + } + val = address + mem_param->flip_ofst_y + mem_param->user_y_ofst; + if (resize_no == RSZ_B) + val += params->ext_mem_param[RSZ_A].user_y_ofst + + params->ext_mem_param[RSZ_A].user_c_ofst; + /* set Y address */ + rsz_set_y_address(rsz_base, val, rsz_start_add); + } + /* resizer must be enabled */ + regw_rsz(rsz_base, params->rsz_en[resize_no], rsz_start_add); + + return 0; +} + +void +ipipe_set_lutdpc_regs(void *__iomem base_addr, void *__iomem isp5_base_addr, + struct vpfe_ipipe_lutdpc *dpc) +{ + u32 max_tbl_size = LUT_DPC_MAX_SIZE >> 1; + u32 lut_start_addr = DPC_TB0_START_ADDR; + u32 val; + u32 count; + + ipipe_clock_enable(base_addr); + regw_ip(base_addr, dpc->en, DPC_LUT_EN); + + if (dpc->en != 1) + return; + + val = LUTDPC_TBL_256_EN | (dpc->repl_white & 1); + regw_ip(base_addr, val, DPC_LUT_SEL); + regw_ip(base_addr, LUT_DPC_START_ADDR, DPC_LUT_ADR); + regw_ip(base_addr, dpc->dpc_size, DPC_LUT_SIZ & LUT_DPC_SIZE_MASK); + + if (dpc->table == NULL) + return; + + for (count = 0; count < dpc->dpc_size; count++) { + if (count >= max_tbl_size) + lut_start_addr = DPC_TB1_START_ADDR; + val = (dpc->table[count].horz_pos & LUT_DPC_H_POS_MASK) | + ((dpc->table[count].vert_pos & LUT_DPC_V_POS_MASK) << + LUT_DPC_V_POS_SHIFT) | (dpc->table[count].method << + LUT_DPC_CORR_METH_SHIFT); + w_ip_table(isp5_base_addr, val, (lut_start_addr + + ((count % max_tbl_size) << 2))); + } +} + +static void +set_dpc_thresholds(void *__iomem base_addr, + struct vpfe_ipipe_otfdpc_2_0_cfg *dpc_thr) +{ + regw_ip(base_addr, dpc_thr->corr_thr.r & OTFDPC_DPC2_THR_MASK, + DPC_OTF_2C_THR_R); + regw_ip(base_addr, dpc_thr->corr_thr.gr & OTFDPC_DPC2_THR_MASK, + DPC_OTF_2C_THR_GR); + regw_ip(base_addr, dpc_thr->corr_thr.gb & OTFDPC_DPC2_THR_MASK, + DPC_OTF_2C_THR_GB); + regw_ip(base_addr, dpc_thr->corr_thr.b & OTFDPC_DPC2_THR_MASK, + DPC_OTF_2C_THR_B); + regw_ip(base_addr, dpc_thr->det_thr.r & OTFDPC_DPC2_THR_MASK, + DPC_OTF_2D_THR_R); + regw_ip(base_addr, dpc_thr->det_thr.gr & OTFDPC_DPC2_THR_MASK, + DPC_OTF_2D_THR_GR); + regw_ip(base_addr, dpc_thr->det_thr.gb & OTFDPC_DPC2_THR_MASK, + DPC_OTF_2D_THR_GB); + regw_ip(base_addr, dpc_thr->det_thr.b & OTFDPC_DPC2_THR_MASK, + DPC_OTF_2D_THR_B); +} + +void ipipe_set_otfdpc_regs(void *__iomem base_addr, + struct vpfe_ipipe_otfdpc *otfdpc) +{ + struct vpfe_ipipe_otfdpc_2_0_cfg *dpc_2_0 = &otfdpc->alg_cfg.dpc_2_0; + struct vpfe_ipipe_otfdpc_3_0_cfg *dpc_3_0 = &otfdpc->alg_cfg.dpc_3_0; + u32 val; + + ipipe_clock_enable(base_addr); + + regw_ip(base_addr, (otfdpc->en & 1), DPC_OTF_EN); + if (!otfdpc->en) + return; + + /* dpc enabled */ + val = (otfdpc->det_method << OTF_DET_METHOD_SHIFT) | otfdpc->alg; + regw_ip(base_addr, val, DPC_OTF_TYP); + + if (otfdpc->det_method == VPFE_IPIPE_DPC_OTF_MIN_MAX) { + /* ALG= 0, TYP = 0, DPC_OTF_2D_THR_[x]=0 + * DPC_OTF_2C_THR_[x] = Maximum thresohld + * MinMax method + */ + dpc_2_0->det_thr.r = dpc_2_0->det_thr.gb = + dpc_2_0->det_thr.gr = dpc_2_0->det_thr.b = 0; + set_dpc_thresholds(base_addr, dpc_2_0); + return; + } + /* MinMax2 */ + if (otfdpc->alg == VPFE_IPIPE_OTFDPC_2_0) { + set_dpc_thresholds(base_addr, dpc_2_0); + return; + } + regw_ip(base_addr, dpc_3_0->act_adj_shf & + OTF_DPC3_0_SHF_MASK, DPC_OTF_3_SHF); + /* Detection thresholds */ + regw_ip(base_addr, ((dpc_3_0->det_thr & OTF_DPC3_0_THR_MASK) << + OTF_DPC3_0_THR_SHIFT), DPC_OTF_3D_THR); + regw_ip(base_addr, dpc_3_0->det_slp & + OTF_DPC3_0_SLP_MASK, DPC_OTF_3D_SLP); + regw_ip(base_addr, dpc_3_0->det_thr_min & + OTF_DPC3_0_DET_MASK, DPC_OTF_3D_MIN); + regw_ip(base_addr, dpc_3_0->det_thr_max & + OTF_DPC3_0_DET_MASK, DPC_OTF_3D_MAX); + /* Correction thresholds */ + regw_ip(base_addr, ((dpc_3_0->corr_thr & OTF_DPC3_0_THR_MASK) << + OTF_DPC3_0_THR_SHIFT), DPC_OTF_3C_THR); + regw_ip(base_addr, dpc_3_0->corr_slp & + OTF_DPC3_0_SLP_MASK, DPC_OTF_3C_SLP); + regw_ip(base_addr, dpc_3_0->corr_thr_min & + OTF_DPC3_0_CORR_MASK, DPC_OTF_3C_MIN); + regw_ip(base_addr, dpc_3_0->corr_thr_max & + OTF_DPC3_0_CORR_MASK, DPC_OTF_3C_MAX); +} + +/* 2D Noise filter */ +void +ipipe_set_d2f_regs(void *__iomem base_addr, unsigned int id, + struct vpfe_ipipe_nf *noise_filter) +{ + + u32 offset = D2F_1ST; + int count; + u32 val; + + if (id == IPIPE_D2F_2ND) + offset = D2F_2ND; + + ipipe_clock_enable(base_addr); + regw_ip(base_addr, noise_filter->en & 1, offset + D2F_EN); + if (!noise_filter->en) + return; + + /*noise filter enabled */ + /* Combine all the fields to make D2F_CFG register of IPIPE */ + val = ((noise_filter->spread_val & D2F_SPR_VAL_MASK) << + D2F_SPR_VAL_SHIFT) | ((noise_filter->shft_val & + D2F_SHFT_VAL_MASK) << D2F_SHFT_VAL_SHIFT) | + (noise_filter->gr_sample_meth << D2F_SAMPLE_METH_SHIFT) | + ((noise_filter->apply_lsc_gain & 1) << + D2F_APPLY_LSC_GAIN_SHIFT) | D2F_USE_SPR_REG_VAL; + regw_ip(base_addr, val, offset + D2F_TYP); + + /* edge detection minimum */ + regw_ip(base_addr, noise_filter->edge_det_min_thr & + D2F_EDGE_DET_THR_MASK, offset + D2F_EDG_MIN); + + /* edge detection maximum */ + regw_ip(base_addr, noise_filter->edge_det_max_thr & + D2F_EDGE_DET_THR_MASK, offset + D2F_EDG_MAX); + + for (count = 0; count < VPFE_IPIPE_NF_STR_TABLE_SIZE; count++) + regw_ip(base_addr, + (noise_filter->str[count] & D2F_STR_VAL_MASK), + offset + D2F_STR + count * 4); + + for (count = 0; count < VPFE_IPIPE_NF_THR_TABLE_SIZE; count++) + regw_ip(base_addr, noise_filter->thr[count] & D2F_THR_VAL_MASK, + offset + D2F_THR + count * 4); +} + +#define IPIPE_U8Q5(decimal, integer) \ + (((decimal & 0x1f) | ((integer & 0x7) << 5))) + +/* Green Imbalance Correction */ +void ipipe_set_gic_regs(void *__iomem base_addr, struct vpfe_ipipe_gic *gic) +{ + u32 val; + + ipipe_clock_enable(base_addr); + regw_ip(base_addr, gic->en & 1, GIC_EN); + + if (!gic->en) + return; + + /*gic enabled */ + val = (gic->wt_fn_type << GIC_TYP_SHIFT) | + (gic->thr_sel << GIC_THR_SEL_SHIFT) | + ((gic->apply_lsc_gain & 1) << GIC_APPLY_LSC_GAIN_SHIFT); + regw_ip(base_addr, val, GIC_TYP); + + regw_ip(base_addr, gic->gain & GIC_GAIN_MASK, GIC_GAN); + + if (gic->gic_alg != VPFE_IPIPE_GIC_ALG_ADAPT_GAIN) { + /* Constant Gain. Set threshold to maximum */ + regw_ip(base_addr, GIC_THR_MASK, GIC_THR); + return; + } + + if (gic->thr_sel == VPFE_IPIPE_GIC_THR_REG) { + regw_ip(base_addr, gic->thr & GIC_THR_MASK, GIC_THR); + regw_ip(base_addr, gic->slope & GIC_SLOPE_MASK, GIC_SLP); + } else { + /* Use NF thresholds */ + val = IPIPE_U8Q5(gic->nf2_thr_gain.decimal, + gic->nf2_thr_gain.integer); + regw_ip(base_addr, val, GIC_NFGAN); + } +} + +#define IPIPE_U13Q9(decimal, integer) \ + (((decimal & 0x1ff) | ((integer & 0xf) << 9))) +/* White balance */ +void ipipe_set_wb_regs(void *__iomem base_addr, struct vpfe_ipipe_wb *wb) +{ + u32 val; + + ipipe_clock_enable(base_addr); + /* Ofsets. S12 */ + regw_ip(base_addr, wb->ofst_r & WB_OFFSET_MASK, WB2_OFT_R); + regw_ip(base_addr, wb->ofst_gr & WB_OFFSET_MASK, WB2_OFT_GR); + regw_ip(base_addr, wb->ofst_gb & WB_OFFSET_MASK, WB2_OFT_GB); + regw_ip(base_addr, wb->ofst_b & WB_OFFSET_MASK, WB2_OFT_B); + + /* Gains. U13Q9 */ + val = IPIPE_U13Q9(wb->gain_r.decimal, wb->gain_r.integer); + regw_ip(base_addr, val, WB2_WGN_R); + + val = IPIPE_U13Q9(wb->gain_gr.decimal, wb->gain_gr.integer); + regw_ip(base_addr, val, WB2_WGN_GR); + + val = IPIPE_U13Q9(wb->gain_gb.decimal, wb->gain_gb.integer); + regw_ip(base_addr, val, WB2_WGN_GB); + + val = IPIPE_U13Q9(wb->gain_b.decimal, wb->gain_b.integer); + regw_ip(base_addr, val, WB2_WGN_B); +} + +/* CFA */ +void ipipe_set_cfa_regs(void *__iomem base_addr, struct vpfe_ipipe_cfa *cfa) +{ + ipipe_clock_enable(base_addr); + + regw_ip(base_addr, cfa->alg, CFA_MODE); + regw_ip(base_addr, cfa->hpf_thr_2dir & CFA_HPF_THR_2DIR_MASK, + CFA_2DIR_HPF_THR); + regw_ip(base_addr, cfa->hpf_slp_2dir & CFA_HPF_SLOPE_2DIR_MASK, + CFA_2DIR_HPF_SLP); + regw_ip(base_addr, cfa->hp_mix_thr_2dir & CFA_HPF_MIX_THR_2DIR_MASK, + CFA_2DIR_MIX_THR); + regw_ip(base_addr, cfa->hp_mix_slope_2dir & CFA_HPF_MIX_SLP_2DIR_MASK, + CFA_2DIR_MIX_SLP); + regw_ip(base_addr, cfa->dir_thr_2dir & CFA_DIR_THR_2DIR_MASK, + CFA_2DIR_DIR_THR); + regw_ip(base_addr, cfa->dir_slope_2dir & CFA_DIR_SLP_2DIR_MASK, + CFA_2DIR_DIR_SLP); + regw_ip(base_addr, cfa->nd_wt_2dir & CFA_ND_WT_2DIR_MASK, + CFA_2DIR_NDWT); + regw_ip(base_addr, cfa->hue_fract_daa & CFA_DAA_HUE_FRA_MASK, + CFA_MONO_HUE_FRA); + regw_ip(base_addr, cfa->edge_thr_daa & CFA_DAA_EDG_THR_MASK, + CFA_MONO_EDG_THR); + regw_ip(base_addr, cfa->thr_min_daa & CFA_DAA_THR_MIN_MASK, + CFA_MONO_THR_MIN); + regw_ip(base_addr, cfa->thr_slope_daa & CFA_DAA_THR_SLP_MASK, + CFA_MONO_THR_SLP); + regw_ip(base_addr, cfa->slope_min_daa & CFA_DAA_SLP_MIN_MASK, + CFA_MONO_SLP_MIN); + regw_ip(base_addr, cfa->slope_slope_daa & CFA_DAA_SLP_SLP_MASK, + CFA_MONO_SLP_SLP); + regw_ip(base_addr, cfa->lp_wt_daa & CFA_DAA_LP_WT_MASK, + CFA_MONO_LPWT); +} + +void +ipipe_set_rgb2rgb_regs(void *__iomem base_addr, unsigned int id, + struct vpfe_ipipe_rgb2rgb *rgb) +{ + u32 offset_mask = RGB2RGB_1_OFST_MASK; + u32 offset = RGB1_MUL_BASE; + u32 integ_mask = 0xf; + u32 val; + + ipipe_clock_enable(base_addr); + + if (id == IPIPE_RGB2RGB_2) { + /* For second RGB module, gain integer is 3 bits instead + of 4, offset has 11 bits insread of 13 */ + offset = RGB2_MUL_BASE; + integ_mask = 0x7; + offset_mask = RGB2RGB_2_OFST_MASK; + } + /* Gains */ + val = (rgb->coef_rr.decimal & 0xff) | + ((rgb->coef_rr.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_RR); + val = (rgb->coef_gr.decimal & 0xff) | + ((rgb->coef_gr.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_GR); + val = (rgb->coef_br.decimal & 0xff) | + ((rgb->coef_br.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_BR); + val = (rgb->coef_rg.decimal & 0xff) | + ((rgb->coef_rg.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_RG); + val = (rgb->coef_gg.decimal & 0xff) | + ((rgb->coef_gg.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_GG); + val = (rgb->coef_bg.decimal & 0xff) | + ((rgb->coef_bg.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_BG); + val = (rgb->coef_rb.decimal & 0xff) | + ((rgb->coef_rb.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_RB); + val = (rgb->coef_gb.decimal & 0xff) | + ((rgb->coef_gb.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_GB); + val = (rgb->coef_bb.decimal & 0xff) | + ((rgb->coef_bb.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_BB); + + /* Offsets */ + regw_ip(base_addr, rgb->out_ofst_r & offset_mask, offset + RGB_OFT_OR); + regw_ip(base_addr, rgb->out_ofst_g & offset_mask, offset + RGB_OFT_OG); + regw_ip(base_addr, rgb->out_ofst_b & offset_mask, offset + RGB_OFT_OB); +} + +static void +ipipe_update_gamma_tbl(void *__iomem isp5_base_addr, + struct vpfe_ipipe_gamma_entry *table, int size, u32 addr) +{ + int count; + u32 val; + + for (count = 0; count < size; count++) { + val = table[count].slope & GAMMA_MASK; + val |= (table[count].offset & GAMMA_MASK) << GAMMA_SHIFT; + w_ip_table(isp5_base_addr, val, (addr + (count * 4))); + } +} + +void +ipipe_set_gamma_regs(void *__iomem base_addr, void *__iomem isp5_base_addr, + struct vpfe_ipipe_gamma *gamma) +{ + int table_size; + u32 val; + + ipipe_clock_enable(base_addr); + val = (gamma->bypass_r << GAMMA_BYPR_SHIFT) | + (gamma->bypass_b << GAMMA_BYPG_SHIFT) | + (gamma->bypass_g << GAMMA_BYPB_SHIFT) | + (gamma->tbl_sel << GAMMA_TBL_SEL_SHIFT) | + (gamma->tbl_size << GAMMA_TBL_SIZE_SHIFT); + + regw_ip(base_addr, val, GMM_CFG); + + if (gamma->tbl_sel != VPFE_IPIPE_GAMMA_TBL_RAM) + return; + + table_size = gamma->tbl_size; + + if (!gamma->bypass_r && gamma->table_r != NULL) + ipipe_update_gamma_tbl(isp5_base_addr, gamma->table_r, + table_size, GAMMA_R_START_ADDR); + if (!gamma->bypass_b && gamma->table_b != NULL) + ipipe_update_gamma_tbl(isp5_base_addr, gamma->table_b, + table_size, GAMMA_B_START_ADDR); + if (!gamma->bypass_g && gamma->table_g != NULL) + ipipe_update_gamma_tbl(isp5_base_addr, gamma->table_g, + table_size, GAMMA_G_START_ADDR); +} + +void +ipipe_set_3d_lut_regs(void *__iomem base_addr, void *__iomem isp5_base_addr, + struct vpfe_ipipe_3d_lut *lut_3d) +{ + struct vpfe_ipipe_3d_lut_entry *tbl; + u32 bnk_index; + u32 tbl_index; + u32 val; + u32 i; + + ipipe_clock_enable(base_addr); + regw_ip(base_addr, lut_3d->en, D3LUT_EN); + + if (!lut_3d->en) + return; + + /* lut_3d enabled */ + if (!lut_3d->table) + return; + + /* valied table */ + tbl = lut_3d->table; + for (i = 0 ; i < VPFE_IPIPE_MAX_SIZE_3D_LUT; i++) { + /* Each entry has 0-9 (B), 10-19 (G) and + 20-29 R values */ + val = tbl[i].b & D3_LUT_ENTRY_MASK; + val |= (tbl[i].g & D3_LUT_ENTRY_MASK) << + D3_LUT_ENTRY_G_SHIFT; + val |= (tbl[i].r & D3_LUT_ENTRY_MASK) << + D3_LUT_ENTRY_R_SHIFT; + bnk_index = i % 4; + tbl_index = i >> 2; + tbl_index <<= 2; + if (bnk_index == 0) + w_ip_table(isp5_base_addr, val, + tbl_index + D3L_TB0_START_ADDR); + else if (bnk_index == 1) + w_ip_table(isp5_base_addr, val, + tbl_index + D3L_TB1_START_ADDR); + else if (bnk_index == 2) + w_ip_table(isp5_base_addr, val, + tbl_index + D3L_TB2_START_ADDR); + else + w_ip_table(isp5_base_addr, val, + tbl_index + D3L_TB3_START_ADDR); + } +} + +/* Lumina adjustments */ +void +ipipe_set_lum_adj_regs(void *__iomem base_addr, struct ipipe_lum_adj *lum_adj) +{ + u32 val; + + ipipe_clock_enable(base_addr); + + /* combine fields of YUV_ADJ to set brightness and contrast */ + val = lum_adj->contrast << LUM_ADJ_CONTR_SHIFT | + lum_adj->brightness << LUM_ADJ_BRIGHT_SHIFT; + regw_ip(base_addr, val, YUV_ADJ); +} + +#define IPIPE_S12Q8(decimal, integer) \ + (((decimal & 0xff) | ((integer & 0xf) << 8))) + +void ipipe_set_rgb2ycbcr_regs(void *__iomem base_addr, + struct vpfe_ipipe_rgb2yuv *yuv) +{ + u32 val; + + /* S10Q8 */ + ipipe_clock_enable(base_addr); + val = IPIPE_S12Q8(yuv->coef_ry.decimal, yuv->coef_ry.integer); + regw_ip(base_addr, val, YUV_MUL_RY); + val = IPIPE_S12Q8(yuv->coef_gy.decimal, yuv->coef_gy.integer); + regw_ip(base_addr, val, YUV_MUL_GY); + val = IPIPE_S12Q8(yuv->coef_by.decimal, yuv->coef_by.integer); + regw_ip(base_addr, val, YUV_MUL_BY); + val = IPIPE_S12Q8(yuv->coef_rcb.decimal, yuv->coef_rcb.integer); + regw_ip(base_addr, val, YUV_MUL_RCB); + val = IPIPE_S12Q8(yuv->coef_gcb.decimal, yuv->coef_gcb.integer); + regw_ip(base_addr, val, YUV_MUL_GCB); + val = IPIPE_S12Q8(yuv->coef_bcb.decimal, yuv->coef_bcb.integer); + regw_ip(base_addr, val, YUV_MUL_BCB); + val = IPIPE_S12Q8(yuv->coef_rcr.decimal, yuv->coef_rcr.integer); + regw_ip(base_addr, val, YUV_MUL_RCR); + val = IPIPE_S12Q8(yuv->coef_gcr.decimal, yuv->coef_gcr.integer); + regw_ip(base_addr, val, YUV_MUL_GCR); + val = IPIPE_S12Q8(yuv->coef_bcr.decimal, yuv->coef_bcr.integer); + regw_ip(base_addr, val, YUV_MUL_BCR); + regw_ip(base_addr, yuv->out_ofst_y & RGB2YCBCR_OFST_MASK, YUV_OFT_Y); + regw_ip(base_addr, yuv->out_ofst_cb & RGB2YCBCR_OFST_MASK, YUV_OFT_CB); + regw_ip(base_addr, yuv->out_ofst_cr & RGB2YCBCR_OFST_MASK, YUV_OFT_CR); +} + +/* YUV 422 conversion */ +void +ipipe_set_yuv422_conv_regs(void *__iomem base_addr, + struct vpfe_ipipe_yuv422_conv *conv) +{ + u32 val; + + ipipe_clock_enable(base_addr); + + /* Combine all the fields to make YUV_PHS register of IPIPE */ + val = (conv->chrom_pos << 0) | (conv->en_chrom_lpf << 1); + regw_ip(base_addr, val, YUV_PHS); +} + +void +ipipe_set_gbce_regs(void *__iomem base_addr, void *__iomem isp5_base_addr, + struct vpfe_ipipe_gbce *gbce) +{ + unsigned int count; + u32 mask = GBCE_Y_VAL_MASK; + + if (gbce->type == VPFE_IPIPE_GBCE_GAIN_TBL) + mask = GBCE_GAIN_VAL_MASK; + + ipipe_clock_enable(base_addr); + regw_ip(base_addr, gbce->en & 1, GBCE_EN); + + if (!gbce->en) + return; + + regw_ip(base_addr, gbce->type, GBCE_TYP); + + if (!gbce->table) + return; + + for (count = 0; count < VPFE_IPIPE_MAX_SIZE_GBCE_LUT ; count += 2) + w_ip_table(isp5_base_addr, ((gbce->table[count + 1] & mask) << + GBCE_ENTRY_SHIFT) | (gbce->table[count] & mask), + ((count/2) << 2) + GBCE_TB_START_ADDR); +} + +void +ipipe_set_ee_regs(void *__iomem base_addr, void *__iomem isp5_base_addr, + struct vpfe_ipipe_yee *ee) +{ + unsigned int count; + u32 val; + + ipipe_clock_enable(base_addr); + regw_ip(base_addr, ee->en, YEE_EN); + + if (!ee->en) + return; + + val = ee->en_halo_red & 1; + val |= ee->merge_meth << YEE_HALO_RED_EN_SHIFT; + regw_ip(base_addr, val, YEE_TYP); + + regw_ip(base_addr, ee->hpf_shft, YEE_SHF); + regw_ip(base_addr, ee->hpf_coef_00 & YEE_COEF_MASK, YEE_MUL_00); + regw_ip(base_addr, ee->hpf_coef_01 & YEE_COEF_MASK, YEE_MUL_01); + regw_ip(base_addr, ee->hpf_coef_02 & YEE_COEF_MASK, YEE_MUL_02); + regw_ip(base_addr, ee->hpf_coef_10 & YEE_COEF_MASK, YEE_MUL_10); + regw_ip(base_addr, ee->hpf_coef_11 & YEE_COEF_MASK, YEE_MUL_11); + regw_ip(base_addr, ee->hpf_coef_12 & YEE_COEF_MASK, YEE_MUL_12); + regw_ip(base_addr, ee->hpf_coef_20 & YEE_COEF_MASK, YEE_MUL_20); + regw_ip(base_addr, ee->hpf_coef_21 & YEE_COEF_MASK, YEE_MUL_21); + regw_ip(base_addr, ee->hpf_coef_22 & YEE_COEF_MASK, YEE_MUL_22); + regw_ip(base_addr, ee->yee_thr & YEE_THR_MASK, YEE_THR); + regw_ip(base_addr, ee->es_gain & YEE_ES_GAIN_MASK, YEE_E_GAN); + regw_ip(base_addr, ee->es_thr1 & YEE_ES_THR1_MASK, YEE_E_THR1); + regw_ip(base_addr, ee->es_thr2 & YEE_THR_MASK, YEE_E_THR2); + regw_ip(base_addr, ee->es_gain_grad & YEE_THR_MASK, YEE_G_GAN); + regw_ip(base_addr, ee->es_ofst_grad & YEE_THR_MASK, YEE_G_OFT); + + if (ee->table == NULL) + return; + + for (count = 0; count < VPFE_IPIPE_MAX_SIZE_YEE_LUT; count += 2) + w_ip_table(isp5_base_addr, ((ee->table[count + 1] & + YEE_ENTRY_MASK) << YEE_ENTRY_SHIFT) | + (ee->table[count] & YEE_ENTRY_MASK), + ((count/2) << 2) + YEE_TB_START_ADDR); +} + +/* Chromatic Artifact Correction. CAR */ +static void ipipe_set_mf(void *__iomem base_addr) +{ + /* typ to dynamic switch */ + regw_ip(base_addr, VPFE_IPIPE_CAR_DYN_SWITCH, CAR_TYP); + /* Set SW0 to maximum */ + regw_ip(base_addr, CAR_MF_THR, CAR_SW); +} + +static void +ipipe_set_gain_ctrl(void *__iomem base_addr, struct vpfe_ipipe_car *car) +{ + regw_ip(base_addr, VPFE_IPIPE_CAR_CHR_GAIN_CTRL, CAR_TYP); + regw_ip(base_addr, car->hpf, CAR_HPF_TYP); + regw_ip(base_addr, car->hpf_shft & CAR_HPF_SHIFT_MASK, CAR_HPF_SHF); + regw_ip(base_addr, car->hpf_thr, CAR_HPF_THR); + regw_ip(base_addr, car->gain1.gain, CAR_GN1_GAN); + regw_ip(base_addr, car->gain1.shft & CAR_GAIN1_SHFT_MASK, CAR_GN1_SHF); + regw_ip(base_addr, car->gain1.gain_min & CAR_GAIN_MIN_MASK, + CAR_GN1_MIN); + regw_ip(base_addr, car->gain2.gain, CAR_GN2_GAN); + regw_ip(base_addr, car->gain2.shft & CAR_GAIN2_SHFT_MASK, CAR_GN2_SHF); + regw_ip(base_addr, car->gain2.gain_min & CAR_GAIN_MIN_MASK, + CAR_GN2_MIN); +} + +void ipipe_set_car_regs(void *__iomem base_addr, struct vpfe_ipipe_car *car) +{ + u32 val; + + ipipe_clock_enable(base_addr); + regw_ip(base_addr, car->en, CAR_EN); + + if (!car->en) + return; + + switch (car->meth) { + case VPFE_IPIPE_CAR_MED_FLTR: + ipipe_set_mf(base_addr); + break; + + case VPFE_IPIPE_CAR_CHR_GAIN_CTRL: + ipipe_set_gain_ctrl(base_addr, car); + break; + + default: + /* Dynamic switch between MF and Gain Ctrl. */ + ipipe_set_mf(base_addr); + ipipe_set_gain_ctrl(base_addr, car); + /* Set the threshold for switching between + * the two Here we overwrite the MF SW0 value + */ + regw_ip(base_addr, VPFE_IPIPE_CAR_DYN_SWITCH, CAR_TYP); + val = car->sw1; + val <<= CAR_SW1_SHIFT; + val |= car->sw0; + regw_ip(base_addr, val, CAR_SW); + } +} + +/* Chromatic Gain Suppression */ +void ipipe_set_cgs_regs(void *__iomem base_addr, struct vpfe_ipipe_cgs *cgs) +{ + ipipe_clock_enable(base_addr); + regw_ip(base_addr, cgs->en, CGS_EN); + + if (!cgs->en) + return; + + /* Set the bright side parameters */ + regw_ip(base_addr, cgs->h_thr, CGS_GN1_H_THR); + regw_ip(base_addr, cgs->h_slope, CGS_GN1_H_GAN); + regw_ip(base_addr, cgs->h_shft & CAR_SHIFT_MASK, CGS_GN1_H_SHF); + regw_ip(base_addr, cgs->h_min, CGS_GN1_H_MIN); +} + +void rsz_src_enable(void *__iomem rsz_base, int enable) +{ + regw_rsz(rsz_base, enable, RSZ_SRC_EN); +} + +int rsz_enable(void *__iomem rsz_base, int rsz_id, int enable) +{ + if (rsz_id == RSZ_A) { + regw_rsz(rsz_base, enable, RSZ_EN_A); + /* We always enable RSZ_A. RSZ_B is enable upon request from + * application. So enable RSZ_SRC_EN along with RSZ_A + */ + regw_rsz(rsz_base, enable, RSZ_SRC_EN); + } else if (rsz_id == RSZ_B) { + regw_rsz(rsz_base, enable, RSZ_EN_B); + } else { + BUG(); + } + + return 0; +} diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.h b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.h new file mode 100644 index 000000000000..010fdb247faf --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.h @@ -0,0 +1,559 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + */ + +#ifndef _DAVINCI_VPFE_DM365_IPIPE_HW_H +#define _DAVINCI_VPFE_DM365_IPIPE_HW_H + +#include "vpfe_mc_capture.h" + +#define SET_LOW_ADDR 0x0000ffff +#define SET_HIGH_ADDR 0xffff0000 + +/* Below are the internal tables */ +#define DPC_TB0_START_ADDR 0x8000 +#define DPC_TB1_START_ADDR 0x8400 + +#define GAMMA_R_START_ADDR 0xa800 +#define GAMMA_G_START_ADDR 0xb000 +#define GAMMA_B_START_ADDR 0xb800 + +/* RAM table addresses for edge enhancement correction*/ +#define YEE_TB_START_ADDR 0x8800 + +/* RAM table address for GBC LUT */ +#define GBCE_TB_START_ADDR 0x9000 + +/* RAM table for 3D NF LUT */ +#define D3L_TB0_START_ADDR 0x9800 +#define D3L_TB1_START_ADDR 0x9c00 +#define D3L_TB2_START_ADDR 0xa000 +#define D3L_TB3_START_ADDR 0xa400 + +/* IPIPE Register Offsets from the base address */ +#define IPIPE_SRC_EN 0x0000 +#define IPIPE_SRC_MODE 0x0004 +#define IPIPE_SRC_FMT 0x0008 +#define IPIPE_SRC_COL 0x000c +#define IPIPE_SRC_VPS 0x0010 +#define IPIPE_SRC_VSZ 0x0014 +#define IPIPE_SRC_HPS 0x0018 +#define IPIPE_SRC_HSZ 0x001c + +#define IPIPE_SEL_SBU 0x0020 + +#define IPIPE_DMA_STA 0x0024 +#define IPIPE_GCK_MMR 0x0028 +#define IPIPE_GCK_PIX 0x002c +#define IPIPE_RESERVED0 0x0030 + +/* Defect Correction */ +#define DPC_LUT_EN 0x0034 +#define DPC_LUT_SEL 0x0038 +#define DPC_LUT_ADR 0x003c +#define DPC_LUT_SIZ 0x0040 +#define DPC_OTF_EN 0x0044 +#define DPC_OTF_TYP 0x0048 +#define DPC_OTF_2D_THR_R 0x004c +#define DPC_OTF_2D_THR_GR 0x0050 +#define DPC_OTF_2D_THR_GB 0x0054 +#define DPC_OTF_2D_THR_B 0x0058 +#define DPC_OTF_2C_THR_R 0x005c +#define DPC_OTF_2C_THR_GR 0x0060 +#define DPC_OTF_2C_THR_GB 0x0064 +#define DPC_OTF_2C_THR_B 0x0068 +#define DPC_OTF_3_SHF 0x006c +#define DPC_OTF_3D_THR 0x0070 +#define DPC_OTF_3D_SLP 0x0074 +#define DPC_OTF_3D_MIN 0x0078 +#define DPC_OTF_3D_MAX 0x007c +#define DPC_OTF_3C_THR 0x0080 +#define DPC_OTF_3C_SLP 0x0084 +#define DPC_OTF_3C_MIN 0x0088 +#define DPC_OTF_3C_MAX 0x008c + +/* Lense Shading Correction */ +#define LSC_VOFT 0x90 +#define LSC_VA2 0x94 +#define LSC_VA1 0x98 +#define LSC_VS 0x9c +#define LSC_HOFT 0xa0 +#define LSC_HA2 0xa4 +#define LSC_HA1 0xa8 +#define LSC_HS 0xac +#define LSC_GAIN_R 0xb0 +#define LSC_GAIN_GR 0xb4 +#define LSC_GAIN_GB 0xb8 +#define LSC_GAIN_B 0xbc +#define LSC_OFT_R 0xc0 +#define LSC_OFT_GR 0xc4 +#define LSC_OFT_GB 0xc8 +#define LSC_OFT_B 0xcc +#define LSC_SHF 0xd0 +#define LSC_MAX 0xd4 + +/* Noise Filter 1. Ofsets from start address given */ +#define D2F_1ST 0xd8 +#define D2F_EN 0x0 +#define D2F_TYP 0x4 +#define D2F_THR 0x8 +#define D2F_STR 0x28 +#define D2F_SPR 0x48 +#define D2F_EDG_MIN 0x68 +#define D2F_EDG_MAX 0x6c + +/* Noise Filter 2 */ +#define D2F_2ND 0x148 + +/* GIC */ +#define GIC_EN 0x1b8 +#define GIC_TYP 0x1bc +#define GIC_GAN 0x1c0 +#define GIC_NFGAN 0x1c4 +#define GIC_THR 0x1c8 +#define GIC_SLP 0x1cc + +/* White Balance */ +#define WB2_OFT_R 0x1d0 +#define WB2_OFT_GR 0x1d4 +#define WB2_OFT_GB 0x1d8 +#define WB2_OFT_B 0x1dc +#define WB2_WGN_R 0x1e0 +#define WB2_WGN_GR 0x1e4 +#define WB2_WGN_GB 0x1e8 +#define WB2_WGN_B 0x1ec + +/* CFA interpolation */ +#define CFA_MODE 0x1f0 +#define CFA_2DIR_HPF_THR 0x1f4 +#define CFA_2DIR_HPF_SLP 0x1f8 +#define CFA_2DIR_MIX_THR 0x1fc +#define CFA_2DIR_MIX_SLP 0x200 +#define CFA_2DIR_DIR_THR 0x204 +#define CFA_2DIR_DIR_SLP 0x208 +#define CFA_2DIR_NDWT 0x20c +#define CFA_MONO_HUE_FRA 0x210 +#define CFA_MONO_EDG_THR 0x214 +#define CFA_MONO_THR_MIN 0x218 +#define CFA_MONO_THR_SLP 0x21c +#define CFA_MONO_SLP_MIN 0x220 +#define CFA_MONO_SLP_SLP 0x224 +#define CFA_MONO_LPWT 0x228 + +/* RGB to RGB conversiona - 1st */ +#define RGB1_MUL_BASE 0x22c +/* Offsets from base */ +#define RGB_MUL_RR 0x0 +#define RGB_MUL_GR 0x4 +#define RGB_MUL_BR 0x8 +#define RGB_MUL_RG 0xc +#define RGB_MUL_GG 0x10 +#define RGB_MUL_BG 0x14 +#define RGB_MUL_RB 0x18 +#define RGB_MUL_GB 0x1c +#define RGB_MUL_BB 0x20 +#define RGB_OFT_OR 0x24 +#define RGB_OFT_OG 0x28 +#define RGB_OFT_OB 0x2c + +/* Gamma */ +#define GMM_CFG 0x25c + +/* RGB to RGB conversiona - 2nd */ +#define RGB2_MUL_BASE 0x260 + +/* 3D LUT */ +#define D3LUT_EN 0x290 + +/* RGB to YUV(YCbCr) conversion */ +#define YUV_ADJ 0x294 +#define YUV_MUL_RY 0x298 +#define YUV_MUL_GY 0x29c +#define YUV_MUL_BY 0x2a0 +#define YUV_MUL_RCB 0x2a4 +#define YUV_MUL_GCB 0x2a8 +#define YUV_MUL_BCB 0x2ac +#define YUV_MUL_RCR 0x2b0 +#define YUV_MUL_GCR 0x2b4 +#define YUV_MUL_BCR 0x2b8 +#define YUV_OFT_Y 0x2bc +#define YUV_OFT_CB 0x2c0 +#define YUV_OFT_CR 0x2c4 +#define YUV_PHS 0x2c8 + +/* Global Brightness and Contrast */ +#define GBCE_EN 0x2cc +#define GBCE_TYP 0x2d0 + +/* Edge Enhancer */ +#define YEE_EN 0x2d4 +#define YEE_TYP 0x2d8 +#define YEE_SHF 0x2dc +#define YEE_MUL_00 0x2e0 +#define YEE_MUL_01 0x2e4 +#define YEE_MUL_02 0x2e8 +#define YEE_MUL_10 0x2ec +#define YEE_MUL_11 0x2f0 +#define YEE_MUL_12 0x2f4 +#define YEE_MUL_20 0x2f8 +#define YEE_MUL_21 0x2fc +#define YEE_MUL_22 0x300 +#define YEE_THR 0x304 +#define YEE_E_GAN 0x308 +#define YEE_E_THR1 0x30c +#define YEE_E_THR2 0x310 +#define YEE_G_GAN 0x314 +#define YEE_G_OFT 0x318 + +/* Chroma Artifact Reduction */ +#define CAR_EN 0x31c +#define CAR_TYP 0x320 +#define CAR_SW 0x324 +#define CAR_HPF_TYP 0x328 +#define CAR_HPF_SHF 0x32c +#define CAR_HPF_THR 0x330 +#define CAR_GN1_GAN 0x334 +#define CAR_GN1_SHF 0x338 +#define CAR_GN1_MIN 0x33c +#define CAR_GN2_GAN 0x340 +#define CAR_GN2_SHF 0x344 +#define CAR_GN2_MIN 0x348 + +/* Chroma Gain Suppression */ +#define CGS_EN 0x34c +#define CGS_GN1_L_THR 0x350 +#define CGS_GN1_L_GAN 0x354 +#define CGS_GN1_L_SHF 0x358 +#define CGS_GN1_L_MIN 0x35c +#define CGS_GN1_H_THR 0x360 +#define CGS_GN1_H_GAN 0x364 +#define CGS_GN1_H_SHF 0x368 +#define CGS_GN1_H_MIN 0x36c +#define CGS_GN2_L_THR 0x370 +#define CGS_GN2_L_GAN 0x374 +#define CGS_GN2_L_SHF 0x378 +#define CGS_GN2_L_MIN 0x37c + +/* Resizer */ +#define RSZ_SRC_EN 0x0 +#define RSZ_SRC_MODE 0x4 +#define RSZ_SRC_FMT0 0x8 +#define RSZ_SRC_FMT1 0xc +#define RSZ_SRC_VPS 0x10 +#define RSZ_SRC_VSZ 0x14 +#define RSZ_SRC_HPS 0x18 +#define RSZ_SRC_HSZ 0x1c +#define RSZ_DMA_RZA 0x20 +#define RSZ_DMA_RZB 0x24 +#define RSZ_DMA_STA 0x28 +#define RSZ_GCK_MMR 0x2c +#define RSZ_RESERVED0 0x30 +#define RSZ_GCK_SDR 0x34 +#define RSZ_IRQ_RZA 0x38 +#define RSZ_IRQ_RZB 0x3c +#define RSZ_YUV_Y_MIN 0x40 +#define RSZ_YUV_Y_MAX 0x44 +#define RSZ_YUV_C_MIN 0x48 +#define RSZ_YUV_C_MAX 0x4c +#define RSZ_YUV_PHS 0x50 +#define RSZ_SEQ 0x54 + +/* Resizer Rescale Parameters */ +#define RSZ_EN_A 0x58 +#define RSZ_EN_B 0xe8 +/* offset of the registers to be added with base register of + either RSZ0 or RSZ1 +*/ +#define RSZ_MODE 0x4 +#define RSZ_420 0x8 +#define RSZ_I_VPS 0xc +#define RSZ_I_HPS 0x10 +#define RSZ_O_VSZ 0x14 +#define RSZ_O_HSZ 0x18 +#define RSZ_V_PHS_Y 0x1c +#define RSZ_V_PHS_C 0x20 +#define RSZ_V_DIF 0x24 +#define RSZ_V_TYP 0x28 +#define RSZ_V_LPF 0x2c +#define RSZ_H_PHS 0x30 +#define RSZ_H_PHS_ADJ 0x34 +#define RSZ_H_DIF 0x38 +#define RSZ_H_TYP 0x3c +#define RSZ_H_LPF 0x40 +#define RSZ_DWN_EN 0x44 +#define RSZ_DWN_AV 0x48 + +/* Resizer RGB Conversion Parameters */ +#define RSZ_RGB_EN 0x4c +#define RSZ_RGB_TYP 0x50 +#define RSZ_RGB_BLD 0x54 + +/* Resizer External Memory Parameters */ +#define RSZ_SDR_Y_BAD_H 0x58 +#define RSZ_SDR_Y_BAD_L 0x5c +#define RSZ_SDR_Y_SAD_H 0x60 +#define RSZ_SDR_Y_SAD_L 0x64 +#define RSZ_SDR_Y_OFT 0x68 +#define RSZ_SDR_Y_PTR_S 0x6c +#define RSZ_SDR_Y_PTR_E 0x70 +#define RSZ_SDR_C_BAD_H 0x74 +#define RSZ_SDR_C_BAD_L 0x78 +#define RSZ_SDR_C_SAD_H 0x7c +#define RSZ_SDR_C_SAD_L 0x80 +#define RSZ_SDR_C_OFT 0x84 +#define RSZ_SDR_C_PTR_S 0x88 +#define RSZ_SDR_C_PTR_E 0x8c + +/* Macro for resizer */ +#define RSZ_YUV_Y_MIN 0x40 +#define RSZ_YUV_Y_MAX 0x44 +#define RSZ_YUV_C_MIN 0x48 +#define RSZ_YUV_C_MAX 0x4c + +#define IPIPE_GCK_MMR_DEFAULT 1 +#define IPIPE_GCK_PIX_DEFAULT 0xe +#define RSZ_GCK_MMR_DEFAULT 1 +#define RSZ_GCK_SDR_DEFAULT 1 + +/* LUTDPC */ +#define LUTDPC_TBL_256_EN 0 +#define LUTDPC_INF_TBL_EN 1 +#define LUT_DPC_START_ADDR 0 +#define LUT_DPC_H_POS_MASK 0x1fff +#define LUT_DPC_V_POS_MASK 0x1fff +#define LUT_DPC_V_POS_SHIFT 13 +#define LUT_DPC_CORR_METH_SHIFT 26 +#define LUT_DPC_MAX_SIZE 256 +#define LUT_DPC_SIZE_MASK 0x3ff + +/* OTFDPC */ +#define OTFDPC_DPC2_THR_MASK 0xfff +#define OTF_DET_METHOD_SHIFT 1 +#define OTF_DPC3_0_SHF_MASK 3 +#define OTF_DPC3_0_THR_SHIFT 6 +#define OTF_DPC3_0_THR_MASK 0x3f +#define OTF_DPC3_0_SLP_MASK 0x3f +#define OTF_DPC3_0_DET_MASK 0xfff +#define OTF_DPC3_0_CORR_MASK 0xfff + +/* NF (D2F) */ +#define D2F_SPR_VAL_MASK 0x1f +#define D2F_SPR_VAL_SHIFT 0 +#define D2F_SHFT_VAL_MASK 3 +#define D2F_SHFT_VAL_SHIFT 5 +#define D2F_SAMPLE_METH_SHIFT 7 +#define D2F_APPLY_LSC_GAIN_SHIFT 8 +#define D2F_USE_SPR_REG_VAL 0 +#define D2F_STR_VAL_MASK 0x1f +#define D2F_THR_VAL_MASK 0x3ff +#define D2F_EDGE_DET_THR_MASK 0x7ff + +/* Green Imbalance Correction */ +#define GIC_TYP_SHIFT 0 +#define GIC_THR_SEL_SHIFT 1 +#define GIC_APPLY_LSC_GAIN_SHIFT 2 +#define GIC_GAIN_MASK 0xff +#define GIC_THR_MASK 0xfff +#define GIC_SLOPE_MASK 0xfff +#define GIC_NFGAN_INT_MASK 7 +#define GIC_NFGAN_DECI_MASK 0x1f + +/* WB */ +#define WB_OFFSET_MASK 0xfff +#define WB_GAIN_INT_MASK 0xf +#define WB_GAIN_DECI_MASK 0x1ff + +/* CFA */ +#define CFA_HPF_THR_2DIR_MASK 0x1fff +#define CFA_HPF_SLOPE_2DIR_MASK 0x3ff +#define CFA_HPF_MIX_THR_2DIR_MASK 0x1fff +#define CFA_HPF_MIX_SLP_2DIR_MASK 0x3ff +#define CFA_DIR_THR_2DIR_MASK 0x3ff +#define CFA_DIR_SLP_2DIR_MASK 0x7f +#define CFA_ND_WT_2DIR_MASK 0x3f +#define CFA_DAA_HUE_FRA_MASK 0x3f +#define CFA_DAA_EDG_THR_MASK 0xff +#define CFA_DAA_THR_MIN_MASK 0x3ff +#define CFA_DAA_THR_SLP_MASK 0x3ff +#define CFA_DAA_SLP_MIN_MASK 0x3ff +#define CFA_DAA_SLP_SLP_MASK 0x3ff +#define CFA_DAA_LP_WT_MASK 0x3f + +/* RGB2RGB */ +#define RGB2RGB_1_OFST_MASK 0x1fff +#define RGB2RGB_1_GAIN_INT_MASK 0xf +#define RGB2RGB_GAIN_DECI_MASK 0xff +#define RGB2RGB_2_OFST_MASK 0x7ff +#define RGB2RGB_2_GAIN_INT_MASK 0x7 + +/* Gamma */ +#define GAMMA_BYPR_SHIFT 0 +#define GAMMA_BYPG_SHIFT 1 +#define GAMMA_BYPB_SHIFT 2 +#define GAMMA_TBL_SEL_SHIFT 4 +#define GAMMA_TBL_SIZE_SHIFT 5 +#define GAMMA_MASK 0x3ff +#define GAMMA_SHIFT 10 + +/* 3D LUT */ +#define D3_LUT_ENTRY_MASK 0x3ff +#define D3_LUT_ENTRY_R_SHIFT 20 +#define D3_LUT_ENTRY_G_SHIFT 10 +#define D3_LUT_ENTRY_B_SHIFT 0 + +/* Lumina adj */ +#define LUM_ADJ_CONTR_SHIFT 0 +#define LUM_ADJ_BRIGHT_SHIFT 8 + +/* RGB2YCbCr */ +#define RGB2YCBCR_OFST_MASK 0x7ff +#define RGB2YCBCR_COEF_INT_MASK 0xf +#define RGB2YCBCR_COEF_DECI_MASK 0xff + +/* GBCE */ +#define GBCE_Y_VAL_MASK 0xff +#define GBCE_GAIN_VAL_MASK 0x3ff +#define GBCE_ENTRY_SHIFT 10 + +/* Edge Enhancements */ +#define YEE_HALO_RED_EN_SHIFT 1 +#define YEE_HPF_SHIFT_MASK 0xf +#define YEE_COEF_MASK 0x3ff +#define YEE_THR_MASK 0x3f +#define YEE_ES_GAIN_MASK 0xfff +#define YEE_ES_THR1_MASK 0xfff +#define YEE_ENTRY_SHIFT 9 +#define YEE_ENTRY_MASK 0x1ff + +/* CAR */ +#define CAR_MF_THR 0xff +#define CAR_SW1_SHIFT 8 +#define CAR_GAIN1_SHFT_MASK 7 +#define CAR_GAIN_MIN_MASK 0x1ff +#define CAR_GAIN2_SHFT_MASK 0xf +#define CAR_HPF_SHIFT_MASK 3 + +/* CGS */ +#define CAR_SHIFT_MASK 3 + +/* Resizer */ +#define RSZ_BYPASS_SHIFT 1 +#define RSZ_SRC_IMG_FMT_SHIFT 1 +#define RSZ_SRC_Y_C_SEL_SHIFT 2 +#define IPIPE_RSZ_VPS_MASK 0xffff +#define IPIPE_RSZ_HPS_MASK 0xffff +#define IPIPE_RSZ_VSZ_MASK 0x1fff +#define IPIPE_RSZ_HSZ_MASK 0x1fff +#define RSZ_HPS_MASK 0x1fff +#define RSZ_VPS_MASK 0x1fff +#define RSZ_O_HSZ_MASK 0x1fff +#define RSZ_O_VSZ_MASK 0x1fff +#define RSZ_V_PHS_MASK 0x3fff +#define RSZ_V_DIF_MASK 0x3fff + +#define RSZA_H_FLIP_SHIFT 0 +#define RSZA_V_FLIP_SHIFT 1 +#define RSZB_H_FLIP_SHIFT 2 +#define RSZB_V_FLIP_SHIFT 3 +#define RSZ_A 0 +#define RSZ_B 1 +#define RSZ_CEN_SHIFT 1 +#define RSZ_YEN_SHIFT 0 +#define RSZ_TYP_Y_SHIFT 0 +#define RSZ_TYP_C_SHIFT 1 +#define RSZ_LPF_INT_MASK 0x3f +#define RSZ_LPF_INT_MASK 0x3f +#define RSZ_LPF_INT_C_SHIFT 6 +#define RSZ_H_PHS_MASK 0x3fff +#define RSZ_H_DIF_MASK 0x3fff +#define RSZ_DIFF_DOWN_THR 256 +#define RSZ_DWN_SCALE_AV_SZ_V_SHIFT 3 +#define RSZ_DWN_SCALE_AV_SZ_MASK 7 +#define RSZ_RGB_MSK1_SHIFT 2 +#define RSZ_RGB_MSK0_SHIFT 1 +#define RSZ_RGB_TYP_SHIFT 0 +#define RSZ_RGB_ALPHA_MASK 0xff + +static inline u32 regr_ip(void *__iomem addr, u32 offset) +{ + return readl(addr + offset); +} + +static inline void regw_ip(void *__iomem addr, u32 val, u32 offset) +{ + writel(val, addr + offset); +} + +static inline u32 w_ip_table(void *__iomem addr, u32 val, u32 offset) +{ + writel(val, addr + offset); + + return val; +} + +static inline u32 regr_rsz(void *__iomem addr, u32 offset) +{ + return readl(addr + offset); +} + +static inline u32 regw_rsz(void *__iomem addr, u32 val, u32 offset) +{ + writel(val, addr + offset); + + return val; +} + +int config_ipipe_hw(struct vpfe_ipipe_device *ipipe); +int resizer_set_outaddr(void *__iomem rsz_base, struct resizer_params *params, + int resize_no, unsigned int address); +int rsz_enable(void *__iomem rsz_base, int rsz_id, int enable); +void rsz_src_enable(void *__iomem rsz_base, int enable); +void rsz_set_in_pix_format(unsigned char y_c); +int config_rsz_hw(struct vpfe_resizer_device *resizer, + struct resizer_params *config); +void ipipe_set_d2f_regs(void *__iomem base_addr, unsigned int id, + struct vpfe_ipipe_nf *noise_filter); +void ipipe_set_rgb2rgb_regs(void *__iomem base_addr, unsigned int id, + struct vpfe_ipipe_rgb2rgb *rgb); +void ipipe_set_yuv422_conv_regs(void *__iomem base_addr, + struct vpfe_ipipe_yuv422_conv *conv); +void ipipe_set_lum_adj_regs(void *__iomem base_addr, + struct ipipe_lum_adj *lum_adj); +void ipipe_set_rgb2ycbcr_regs(void *__iomem base_addr, + struct vpfe_ipipe_rgb2yuv *yuv); +void ipipe_set_lutdpc_regs(void *__iomem base_addr, + void *__iomem isp5_base_addr, struct vpfe_ipipe_lutdpc *lutdpc); +void ipipe_set_otfdpc_regs(void *__iomem base_addr, + struct vpfe_ipipe_otfdpc *otfdpc); +void ipipe_set_3d_lut_regs(void *__iomem base_addr, + void *__iomem isp5_base_addr, struct vpfe_ipipe_3d_lut *lut_3d); +void ipipe_set_gamma_regs(void *__iomem base_addr, + void *__iomem isp5_base_addr, struct vpfe_ipipe_gamma *gamma); +void ipipe_set_ee_regs(void *__iomem base_addr, + void *__iomem isp5_base_addr, struct vpfe_ipipe_yee *ee); +void ipipe_set_gbce_regs(void *__iomem base_addr, + void *__iomem isp5_base_addr, struct vpfe_ipipe_gbce *gbce); +void ipipe_set_gic_regs(void *__iomem base_addr, struct vpfe_ipipe_gic *gic); +void ipipe_set_cfa_regs(void *__iomem base_addr, struct vpfe_ipipe_cfa *cfa); +void ipipe_set_car_regs(void *__iomem base_addr, struct vpfe_ipipe_car *car); +void ipipe_set_cgs_regs(void *__iomem base_addr, struct vpfe_ipipe_cgs *cgs); +void ipipe_set_wb_regs(void *__iomem base_addr, struct vpfe_ipipe_wb *wb); + +#endif /* _DAVINCI_VPFE_DM365_IPIPE_HW_H */ diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c new file mode 100644 index 000000000000..c8cae51d3d00 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c @@ -0,0 +1,1071 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + */ + +#include "dm365_ipipeif.h" +#include "vpfe_mc_capture.h" + +static const unsigned int ipipeif_input_fmts[] = { + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_SGRBG12_1X12, + V4L2_MBUS_FMT_Y8_1X8, + V4L2_MBUS_FMT_UV8_1X8, + V4L2_MBUS_FMT_YDYUYDYV8_1X16, + V4L2_MBUS_FMT_SBGGR8_1X8, +}; + +static const unsigned int ipipeif_output_fmts[] = { + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_SGRBG12_1X12, + V4L2_MBUS_FMT_Y8_1X8, + V4L2_MBUS_FMT_UV8_1X8, + V4L2_MBUS_FMT_YDYUYDYV8_1X16, + V4L2_MBUS_FMT_SBGGR8_1X8, + V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, + V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8, +}; + +static int +ipipeif_get_pack_mode(enum v4l2_mbus_pixelcode in_pix_fmt) +{ + switch (in_pix_fmt) { + case V4L2_MBUS_FMT_SBGGR8_1X8: + case V4L2_MBUS_FMT_Y8_1X8: + case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: + case V4L2_MBUS_FMT_UV8_1X8: + return IPIPEIF_5_1_PACK_8_BIT; + + case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8: + return IPIPEIF_5_1_PACK_8_BIT_A_LAW; + + case V4L2_MBUS_FMT_SGRBG12_1X12: + return IPIPEIF_5_1_PACK_16_BIT; + + case V4L2_MBUS_FMT_SBGGR12_1X12: + return IPIPEIF_5_1_PACK_12_BIT; + + default: + return IPIPEIF_5_1_PACK_16_BIT; + } +} + +static inline u32 ipipeif_read(void *addr, u32 offset) +{ + return readl(addr + offset); +} + +static inline void ipipeif_write(u32 val, void *addr, u32 offset) +{ + writel(val, addr + offset); +} + +static void ipipeif_config_dpc(void *addr, struct ipipeif_dpc *dpc) +{ + u32 val = 0; + + if (dpc->en) { + val = (dpc->en & 1) << IPIPEIF_DPC2_EN_SHIFT; + val |= dpc->thr & IPIPEIF_DPC2_THR_MASK; + } + ipipeif_write(val, addr, IPIPEIF_DPC2); +} + +#define IPIPEIF_MODE_CONTINUOUS 0 +#define IPIPEIF_MODE_ONE_SHOT 1 + +static int get_oneshot_mode(enum ipipeif_input_entity input) +{ + if (input == IPIPEIF_INPUT_MEMORY) + return IPIPEIF_MODE_ONE_SHOT; + else if (input == IPIPEIF_INPUT_ISIF) + return IPIPEIF_MODE_CONTINUOUS; + + return -EINVAL; +} + +static int +ipipeif_get_cfg_src1(struct vpfe_ipipeif_device *ipipeif) +{ + struct v4l2_mbus_framefmt *informat; + + informat = &ipipeif->formats[IPIPEIF_PAD_SINK]; + if (ipipeif->input == IPIPEIF_INPUT_MEMORY && + (informat->code == V4L2_MBUS_FMT_Y8_1X8 || + informat->code == V4L2_MBUS_FMT_UV8_1X8)) + return IPIPEIF_CCDC; + + return IPIPEIF_SRC1_PARALLEL_PORT; +} + +static int +ipipeif_get_data_shift(struct vpfe_ipipeif_device *ipipeif) +{ + struct v4l2_mbus_framefmt *informat; + + informat = &ipipeif->formats[IPIPEIF_PAD_SINK]; + + switch (informat->code) { + case V4L2_MBUS_FMT_SGRBG12_1X12: + return IPIPEIF_5_1_BITS11_0; + + case V4L2_MBUS_FMT_Y8_1X8: + case V4L2_MBUS_FMT_UV8_1X8: + return IPIPEIF_5_1_BITS11_0; + + default: + return IPIPEIF_5_1_BITS7_0; + } +} + +static enum ipipeif_input_source +ipipeif_get_source(struct vpfe_ipipeif_device *ipipeif) +{ + struct v4l2_mbus_framefmt *informat; + + informat = &ipipeif->formats[IPIPEIF_PAD_SINK]; + if (ipipeif->input == IPIPEIF_INPUT_ISIF) + return IPIPEIF_CCDC; + + if (informat->code == V4L2_MBUS_FMT_UYVY8_2X8) + return IPIPEIF_SDRAM_YUV; + + return IPIPEIF_SDRAM_RAW; +} + +void vpfe_ipipeif_ss_buffer_isr(struct vpfe_ipipeif_device *ipipeif) +{ + struct vpfe_video_device *video_in = &ipipeif->video_in; + + if (ipipeif->input != IPIPEIF_INPUT_MEMORY) + return; + + spin_lock(&video_in->dma_queue_lock); + vpfe_video_process_buffer_complete(video_in); + video_in->state = VPFE_VIDEO_BUFFER_NOT_QUEUED; + vpfe_video_schedule_next_buffer(video_in); + spin_unlock(&video_in->dma_queue_lock); +} + +int vpfe_ipipeif_decimation_enabled(struct vpfe_device *vpfe_dev) +{ + struct vpfe_ipipeif_device *ipipeif = &vpfe_dev->vpfe_ipipeif; + + return ipipeif->config.decimation; +} + +int vpfe_ipipeif_get_rsz(struct vpfe_device *vpfe_dev) +{ + struct vpfe_ipipeif_device *ipipeif = &vpfe_dev->vpfe_ipipeif; + + return ipipeif->config.rsz; +} + +#define RD_DATA_15_2 0x7 + +/* + * ipipeif_hw_setup() - This function sets up IPIPEIF + * @sd: pointer to v4l2 subdev structure + * return -EINVAL or zero on success + */ +static int ipipeif_hw_setup(struct v4l2_subdev *sd) +{ + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *informat, *outformat; + struct ipipeif_params params = ipipeif->config; + enum ipipeif_input_source ipipeif_source; + enum v4l2_mbus_pixelcode isif_port_if; + void *ipipeif_base_addr; + unsigned int val; + int data_shift; + int pack_mode; + int source1; + + ipipeif_base_addr = ipipeif->ipipeif_base_addr; + + /* Enable clock to IPIPEIF and IPIPE */ + vpss_enable_clock(VPSS_IPIPEIF_CLOCK, 1); + + informat = &ipipeif->formats[IPIPEIF_PAD_SINK]; + outformat = &ipipeif->formats[IPIPEIF_PAD_SOURCE]; + + /* Combine all the fields to make CFG1 register of IPIPEIF */ + val = get_oneshot_mode(ipipeif->input); + if (val < 0) { + pr_err("ipipeif: links setup required"); + return -EINVAL; + } + val = val << ONESHOT_SHIFT; + + ipipeif_source = ipipeif_get_source(ipipeif); + val |= ipipeif_source << INPSRC_SHIFT; + + val |= params.clock_select << CLKSEL_SHIFT; + val |= params.avg_filter << AVGFILT_SHIFT; + val |= params.decimation << DECIM_SHIFT; + + pack_mode = ipipeif_get_pack_mode(informat->code); + val |= pack_mode << PACK8IN_SHIFT; + + source1 = ipipeif_get_cfg_src1(ipipeif); + val |= source1 << INPSRC1_SHIFT; + + data_shift = ipipeif_get_data_shift(ipipeif); + if (ipipeif_source != IPIPEIF_SDRAM_YUV) + val |= data_shift << DATASFT_SHIFT; + else + val &= ~(RD_DATA_15_2 << DATASFT_SHIFT); + + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_CFG1); + + switch (ipipeif_source) { + case IPIPEIF_CCDC: + ipipeif_write(ipipeif->gain, ipipeif_base_addr, IPIPEIF_GAIN); + break; + + case IPIPEIF_SDRAM_RAW: + case IPIPEIF_CCDC_DARKFM: + ipipeif_write(ipipeif->gain, ipipeif_base_addr, IPIPEIF_GAIN); + /* fall through */ + case IPIPEIF_SDRAM_YUV: + val |= data_shift << DATASFT_SHIFT; + ipipeif_write(params.ppln, ipipeif_base_addr, IPIPEIF_PPLN); + ipipeif_write(params.lpfr, ipipeif_base_addr, IPIPEIF_LPFR); + ipipeif_write(informat->width, ipipeif_base_addr, IPIPEIF_HNUM); + ipipeif_write(informat->height, + ipipeif_base_addr, IPIPEIF_VNUM); + break; + + default: + return -EINVAL; + } + + /*check if decimation is enable or not */ + if (params.decimation) + ipipeif_write(params.rsz, ipipeif_base_addr, IPIPEIF_RSZ); + + /* Setup sync alignment and initial rsz position */ + val = params.if_5_1.align_sync & 1; + val <<= IPIPEIF_INIRSZ_ALNSYNC_SHIFT; + val |= params.if_5_1.rsz_start & IPIPEIF_INIRSZ_MASK; + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_INIRSZ); + isif_port_if = informat->code; + + if (isif_port_if == V4L2_MBUS_FMT_Y8_1X8) + isif_port_if = V4L2_MBUS_FMT_YUYV8_1X16; + else if (isif_port_if == V4L2_MBUS_FMT_UV8_1X8) + isif_port_if = V4L2_MBUS_FMT_SGRBG12_1X12; + + /* Enable DPCM decompression */ + switch (ipipeif_source) { + case IPIPEIF_SDRAM_RAW: + val = 0; + if (outformat->code == V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8) { + val = 1; + val |= (IPIPEIF_DPCM_8BIT_10BIT & 1) << + IPIPEIF_DPCM_BITS_SHIFT; + val |= (ipipeif->dpcm_predictor & 1) << + IPIPEIF_DPCM_PRED_SHIFT; + } + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_DPCM); + + /* set DPC */ + ipipeif_config_dpc(ipipeif_base_addr, ¶ms.if_5_1.dpc); + + ipipeif_write(params.if_5_1.clip, + ipipeif_base_addr, IPIPEIF_OCLIP); + + /* fall through for SDRAM YUV mode */ + /* configure CFG2 */ + val = ipipeif_read(ipipeif_base_addr, IPIPEIF_CFG2); + switch (isif_port_if) { + case V4L2_MBUS_FMT_YUYV8_1X16: + case V4L2_MBUS_FMT_UYVY8_2X8: + case V4L2_MBUS_FMT_Y8_1X8: + RESETBIT(val, IPIPEIF_CFG2_YUV8_SHIFT); + SETBIT(val, IPIPEIF_CFG2_YUV16_SHIFT); + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_CFG2); + break; + + default: + RESETBIT(val, IPIPEIF_CFG2_YUV8_SHIFT); + RESETBIT(val, IPIPEIF_CFG2_YUV16_SHIFT); + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_CFG2); + break; + } + + case IPIPEIF_SDRAM_YUV: + /* Set clock divider */ + if (params.clock_select == IPIPEIF_SDRAM_CLK) { + val = ipipeif_read(ipipeif_base_addr, IPIPEIF_CLKDIV); + val |= (params.if_5_1.clk_div.m - 1) << + IPIPEIF_CLKDIV_M_SHIFT; + val |= (params.if_5_1.clk_div.n - 1); + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_CLKDIV); + } + break; + + case IPIPEIF_CCDC: + case IPIPEIF_CCDC_DARKFM: + /* set DPC */ + ipipeif_config_dpc(ipipeif_base_addr, ¶ms.if_5_1.dpc); + + /* Set DF gain & threshold control */ + val = 0; + if (params.if_5_1.df_gain_en) { + val = params.if_5_1.df_gain_thr & + IPIPEIF_DF_GAIN_THR_MASK; + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_DFSGTH); + val = (params.if_5_1.df_gain_en & 1) << + IPIPEIF_DF_GAIN_EN_SHIFT; + val |= params.if_5_1.df_gain & + IPIPEIF_DF_GAIN_MASK; + } + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_DFSGVL); + /* configure CFG2 */ + val = VPFE_PINPOL_POSITIVE << IPIPEIF_CFG2_HDPOL_SHIFT; + val |= VPFE_PINPOL_POSITIVE << IPIPEIF_CFG2_VDPOL_SHIFT; + + switch (isif_port_if) { + case V4L2_MBUS_FMT_YUYV8_1X16: + case V4L2_MBUS_FMT_YUYV10_1X20: + RESETBIT(val, IPIPEIF_CFG2_YUV8_SHIFT); + SETBIT(val, IPIPEIF_CFG2_YUV16_SHIFT); + break; + + case V4L2_MBUS_FMT_YUYV8_2X8: + case V4L2_MBUS_FMT_UYVY8_2X8: + case V4L2_MBUS_FMT_Y8_1X8: + case V4L2_MBUS_FMT_YUYV10_2X10: + SETBIT(val, IPIPEIF_CFG2_YUV8_SHIFT); + SETBIT(val, IPIPEIF_CFG2_YUV16_SHIFT); + val |= IPIPEIF_CBCR_Y << IPIPEIF_CFG2_YUV8P_SHIFT; + break; + + default: + /* Bayer */ + ipipeif_write(params.if_5_1.clip, ipipeif_base_addr, + IPIPEIF_OCLIP); + } + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_CFG2); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int +ipipeif_set_config(struct v4l2_subdev *sd, struct ipipeif_params *config) +{ + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct device *dev = ipipeif->subdev.v4l2_dev->dev; + + if (!config) { + dev_err(dev, "Invalid configuration pointer\n"); + return -EINVAL; + } + + ipipeif->config.clock_select = config->clock_select; + ipipeif->config.ppln = config->ppln; + ipipeif->config.lpfr = config->lpfr; + ipipeif->config.rsz = config->rsz; + ipipeif->config.decimation = config->decimation; + if (ipipeif->config.decimation && + (ipipeif->config.rsz < IPIPEIF_RSZ_MIN || + ipipeif->config.rsz > IPIPEIF_RSZ_MAX)) { + dev_err(dev, "rsz range is %d to %d\n", + IPIPEIF_RSZ_MIN, IPIPEIF_RSZ_MAX); + return -EINVAL; + } + + ipipeif->config.avg_filter = config->avg_filter; + + ipipeif->config.if_5_1.df_gain_thr = config->if_5_1.df_gain_thr; + ipipeif->config.if_5_1.df_gain = config->if_5_1.df_gain; + ipipeif->config.if_5_1.df_gain_en = config->if_5_1.df_gain_en; + + ipipeif->config.if_5_1.rsz_start = config->if_5_1.rsz_start; + ipipeif->config.if_5_1.align_sync = config->if_5_1.align_sync; + ipipeif->config.if_5_1.clip = config->if_5_1.clip; + + ipipeif->config.if_5_1.dpc.en = config->if_5_1.dpc.en; + ipipeif->config.if_5_1.dpc.thr = config->if_5_1.dpc.thr; + + ipipeif->config.if_5_1.clk_div.m = config->if_5_1.clk_div.m; + ipipeif->config.if_5_1.clk_div.n = config->if_5_1.clk_div.n; + + return 0; +} + +static int +ipipeif_get_config(struct v4l2_subdev *sd, void __user *arg) +{ + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct ipipeif_params *config = (struct ipipeif_params *)arg; + struct device *dev = ipipeif->subdev.v4l2_dev->dev; + + if (!arg) { + dev_err(dev, "Invalid configuration pointer\n"); + return -EINVAL; + } + + config->clock_select = ipipeif->config.clock_select; + config->ppln = ipipeif->config.ppln; + config->lpfr = ipipeif->config.lpfr; + config->rsz = ipipeif->config.rsz; + config->decimation = ipipeif->config.decimation; + config->avg_filter = ipipeif->config.avg_filter; + + config->if_5_1.df_gain_thr = ipipeif->config.if_5_1.df_gain_thr; + config->if_5_1.df_gain = ipipeif->config.if_5_1.df_gain; + config->if_5_1.df_gain_en = ipipeif->config.if_5_1.df_gain_en; + + config->if_5_1.rsz_start = ipipeif->config.if_5_1.rsz_start; + config->if_5_1.align_sync = ipipeif->config.if_5_1.align_sync; + config->if_5_1.clip = ipipeif->config.if_5_1.clip; + + config->if_5_1.dpc.en = ipipeif->config.if_5_1.dpc.en; + config->if_5_1.dpc.thr = ipipeif->config.if_5_1.dpc.thr; + + config->if_5_1.clk_div.m = ipipeif->config.if_5_1.clk_div.m; + config->if_5_1.clk_div.n = ipipeif->config.if_5_1.clk_div.n; + + return 0; +} + +/* + * ipipeif_ioctl() - Handle ipipeif module private ioctl's + * @sd: pointer to v4l2 subdev structure + * @cmd: configuration command + * @arg: configuration argument + */ +static long ipipeif_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + struct ipipeif_params *config = (struct ipipeif_params *)arg; + int ret = -ENOIOCTLCMD; + + switch (cmd) { + case VIDIOC_VPFE_IPIPEIF_S_CONFIG: + ret = ipipeif_set_config(sd, config); + break; + + case VIDIOC_VPFE_IPIPEIF_G_CONFIG: + ret = ipipeif_get_config(sd, arg); + break; + } + return ret; +} + +/* + * ipipeif_s_ctrl() - Handle set control subdev method + * @ctrl: pointer to v4l2 control structure + */ +static int ipipeif_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vpfe_ipipeif_device *ipipeif = + container_of(ctrl->handler, struct vpfe_ipipeif_device, ctrls); + + switch (ctrl->id) { + case VPFE_CID_DPCM_PREDICTOR: + ipipeif->dpcm_predictor = ctrl->val; + break; + + case V4L2_CID_GAIN: + ipipeif->gain = ctrl->val; + break; + + default: + return -EINVAL; + } + + return 0; +} + +#define ENABLE_IPIPEIF 0x1 + +void vpfe_ipipeif_enable(struct vpfe_device *vpfe_dev) +{ + struct vpfe_ipipeif_device *ipipeif = &vpfe_dev->vpfe_ipipeif; + void *ipipeif_base_addr = ipipeif->ipipeif_base_addr; + unsigned char val; + + if (ipipeif->input != IPIPEIF_INPUT_MEMORY) + return; + + do { + val = ipipeif_read(ipipeif_base_addr, IPIPEIF_ENABLE); + } while (val & 0x1); + + ipipeif_write(ENABLE_IPIPEIF, ipipeif_base_addr, IPIPEIF_ENABLE); +} + +/* + * ipipeif_set_stream() - Enable/Disable streaming on ipipeif subdev + * @sd: pointer to v4l2 subdev structure + * @enable: 1 == Enable, 0 == Disable + */ +static int ipipeif_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct vpfe_device *vpfe_dev = to_vpfe_device(ipipeif); + int ret = 0; + + if (!enable) + return ret; + + ret = ipipeif_hw_setup(sd); + if (!ret) + vpfe_ipipeif_enable(vpfe_dev); + + return ret; +} + +/* + * ipipeif_enum_mbus_code() - Handle pixel format enumeration + * @sd: pointer to v4l2 subdev structure + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + * return -EINVAL or zero on success + */ +static int ipipeif_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + switch (code->pad) { + case IPIPEIF_PAD_SINK: + if (code->index >= ARRAY_SIZE(ipipeif_input_fmts)) + return -EINVAL; + + code->code = ipipeif_input_fmts[code->index]; + break; + + case IPIPEIF_PAD_SOURCE: + if (code->index >= ARRAY_SIZE(ipipeif_output_fmts)) + return -EINVAL; + + code->code = ipipeif_output_fmts[code->index]; + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* + * ipipeif_get_format() - Handle get format by pads subdev method + * @sd: pointer to v4l2 subdev structure + * @fh: V4L2 subdev file handle + * @fmt: pointer to v4l2 subdev format structure + */ +static int +ipipeif_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + fmt->format = ipipeif->formats[fmt->pad]; + else + fmt->format = *(v4l2_subdev_get_try_format(fh, fmt->pad)); + + return 0; +} + +#define MIN_OUT_WIDTH 32 +#define MIN_OUT_HEIGHT 32 + +/* + * ipipeif_try_format() - Handle try format by pad subdev method + * @ipipeif: VPFE ipipeif device. + * @fh: V4L2 subdev file handle. + * @pad: pad num. + * @fmt: pointer to v4l2 format structure. + * @which : wanted subdev format + */ +static void +ipipeif_try_format(struct vpfe_ipipeif_device *ipipeif, + struct v4l2_subdev_fh *fh, unsigned int pad, + struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) +{ + unsigned int max_out_height; + unsigned int max_out_width; + unsigned int i; + + max_out_width = IPIPE_MAX_OUTPUT_WIDTH_A; + max_out_height = IPIPE_MAX_OUTPUT_HEIGHT_A; + + if (pad == IPIPEIF_PAD_SINK) { + for (i = 0; i < ARRAY_SIZE(ipipeif_input_fmts); i++) + if (fmt->code == ipipeif_input_fmts[i]) + break; + + /* If not found, use SBGGR10 as default */ + if (i >= ARRAY_SIZE(ipipeif_input_fmts)) + fmt->code = V4L2_MBUS_FMT_SGRBG12_1X12; + } else if (pad == IPIPEIF_PAD_SOURCE) { + for (i = 0; i < ARRAY_SIZE(ipipeif_output_fmts); i++) + if (fmt->code == ipipeif_output_fmts[i]) + break; + + /* If not found, use UYVY as default */ + if (i >= ARRAY_SIZE(ipipeif_output_fmts)) + fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + } + + fmt->width = clamp_t(u32, fmt->width, MIN_OUT_HEIGHT, max_out_width); + fmt->height = clamp_t(u32, fmt->height, MIN_OUT_WIDTH, max_out_height); +} + +static int +ipipeif_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt format; + + if (fse->index != 0) + return -EINVAL; + + format.code = fse->code; + format.width = 1; + format.height = 1; + ipipeif_try_format(ipipeif, fh, fse->pad, &format, + V4L2_SUBDEV_FORMAT_TRY); + fse->min_width = format.width; + fse->min_height = format.height; + + if (format.code != fse->code) + return -EINVAL; + + format.code = fse->code; + format.width = -1; + format.height = -1; + ipipeif_try_format(ipipeif, fh, fse->pad, &format, + V4L2_SUBDEV_FORMAT_TRY); + fse->max_width = format.width; + fse->max_height = format.height; + + return 0; +} + +/* + * __ipipeif_get_format() - helper function for getting ipipeif format + * @ipipeif: pointer to ipipeif private structure. + * @pad: pad number. + * @fh: V4L2 subdev file handle. + * @which: wanted subdev format. + * + */ +static struct v4l2_mbus_framefmt * +__ipipeif_get_format(struct vpfe_ipipeif_device *ipipeif, + struct v4l2_subdev_fh *fh, unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(fh, pad); + + return &ipipeif->formats[pad]; +} + +/* + * ipipeif_set_format() - Handle set format by pads subdev method + * @sd: pointer to v4l2 subdev structure + * @fh: V4L2 subdev file handle + * @fmt: pointer to v4l2 subdev format structure + * return -EINVAL or zero on success + */ +static int +ipipeif_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __ipipeif_get_format(ipipeif, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + ipipeif_try_format(ipipeif, fh, fmt->pad, &fmt->format, fmt->which); + *format = fmt->format; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + if (fmt->pad == IPIPEIF_PAD_SINK && + ipipeif->input != IPIPEIF_INPUT_NONE) + ipipeif->formats[fmt->pad] = fmt->format; + else if (fmt->pad == IPIPEIF_PAD_SOURCE && + ipipeif->output != IPIPEIF_OUTPUT_NONE) + ipipeif->formats[fmt->pad] = fmt->format; + else + return -EINVAL; + + return 0; +} + +static void ipipeif_set_default_config(struct vpfe_ipipeif_device *ipipeif) +{ +#define WIDTH_I 640 +#define HEIGHT_I 480 + + const struct ipipeif_params ipipeif_defaults = { + .clock_select = IPIPEIF_SDRAM_CLK, + .ppln = WIDTH_I + 8, + .lpfr = HEIGHT_I + 10, + .rsz = 16, /* resize ratio 16/rsz */ + .decimation = IPIPEIF_DECIMATION_OFF, + .avg_filter = IPIPEIF_AVG_OFF, + .if_5_1 = { + .clk_div = { + .m = 1, /* clock = sdram clock * (m/n) */ + .n = 6 + }, + .clip = 4095, + }, + }; + memset(&ipipeif->config, 0, sizeof(struct ipipeif_params)); + memcpy(&ipipeif->config, &ipipeif_defaults, + sizeof(struct ipipeif_params)); +} + +/* + * ipipeif_init_formats() - Initialize formats on all pads + * @sd: VPFE ipipeif V4L2 subdevice + * @fh: V4L2 subdev file handle + * + * Initialize all pad formats with default values. If fh is not NULL, try + * formats are initialized on the file handle. Otherwise active formats are + * initialized on the device. + */ +static int +ipipeif_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct v4l2_subdev_format format; + + memset(&format, 0, sizeof(format)); + format.pad = IPIPEIF_PAD_SINK; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_SGRBG12_1X12; + format.format.width = IPIPE_MAX_OUTPUT_WIDTH_A; + format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_A; + ipipeif_set_format(sd, fh, &format); + + memset(&format, 0, sizeof(format)); + format.pad = IPIPEIF_PAD_SOURCE; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_UYVY8_2X8; + format.format.width = IPIPE_MAX_OUTPUT_WIDTH_A; + format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_A; + ipipeif_set_format(sd, fh, &format); + + ipipeif_set_default_config(ipipeif); + + return 0; +} + +/* + * ipipeif_video_in_queue() - ipipeif video in queue + * @vpfe_dev: vpfe device pointer + * @addr: buffer address + */ +static int +ipipeif_video_in_queue(struct vpfe_device *vpfe_dev, unsigned long addr) +{ + struct vpfe_ipipeif_device *ipipeif = &vpfe_dev->vpfe_ipipeif; + void *ipipeif_base_addr = ipipeif->ipipeif_base_addr; + unsigned int adofs; + u32 val; + + if (ipipeif->input != IPIPEIF_INPUT_MEMORY) + return -EINVAL; + + switch (ipipeif->formats[IPIPEIF_PAD_SINK].code) { + case V4L2_MBUS_FMT_Y8_1X8: + case V4L2_MBUS_FMT_UV8_1X8: + case V4L2_MBUS_FMT_YDYUYDYV8_1X16: + adofs = ipipeif->formats[IPIPEIF_PAD_SINK].width; + break; + + default: + adofs = ipipeif->formats[IPIPEIF_PAD_SINK].width << 1; + break; + } + + /* adjust the line len to be a multiple of 32 */ + adofs += 31; + adofs &= ~0x1f; + val = (adofs >> 5) & IPIPEIF_ADOFS_LSB_MASK; + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_ADOFS); + + /* lower sixteen bit */ + val = (addr >> IPIPEIF_ADDRL_SHIFT) & IPIPEIF_ADDRL_MASK; + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_ADDRL); + + /* upper next seven bit */ + val = (addr >> IPIPEIF_ADDRU_SHIFT) & IPIPEIF_ADDRU_MASK; + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_ADDRU); + + return 0; +} + +/* subdev core operations */ +static const struct v4l2_subdev_core_ops ipipeif_v4l2_core_ops = { + .ioctl = ipipeif_ioctl, +}; + +static const struct v4l2_ctrl_ops ipipeif_ctrl_ops = { + .s_ctrl = ipipeif_s_ctrl, +}; + +static const struct v4l2_ctrl_config vpfe_ipipeif_dpcm_pred = { + .ops = &ipipeif_ctrl_ops, + .id = VPFE_CID_DPCM_PREDICTOR, + .name = "DPCM Predictor", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 1, + .step = 1, + .def = 0, +}; + +/* subdev file operations */ +static const struct v4l2_subdev_internal_ops ipipeif_v4l2_internal_ops = { + .open = ipipeif_init_formats, +}; + +/* subdev video operations */ +static const struct v4l2_subdev_video_ops ipipeif_v4l2_video_ops = { + .s_stream = ipipeif_set_stream, +}; + +/* subdev pad operations */ +static const struct v4l2_subdev_pad_ops ipipeif_v4l2_pad_ops = { + .enum_mbus_code = ipipeif_enum_mbus_code, + .enum_frame_size = ipipeif_enum_frame_size, + .get_fmt = ipipeif_get_format, + .set_fmt = ipipeif_set_format, +}; + +/* subdev operations */ +static const struct v4l2_subdev_ops ipipeif_v4l2_ops = { + .core = &ipipeif_v4l2_core_ops, + .video = &ipipeif_v4l2_video_ops, + .pad = &ipipeif_v4l2_pad_ops, +}; + +static const struct vpfe_video_operations video_in_ops = { + .queue = ipipeif_video_in_queue, +}; + +static int +ipipeif_link_setup(struct media_entity *entity, const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct vpfe_device *vpfe = to_vpfe_device(ipipeif); + + switch (local->index | media_entity_type(remote->entity)) { + case IPIPEIF_PAD_SINK | MEDIA_ENT_T_DEVNODE: + /* Single shot mode */ + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + ipipeif->input = IPIPEIF_INPUT_NONE; + break; + } + ipipeif->input = IPIPEIF_INPUT_MEMORY; + break; + + case IPIPEIF_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + /* read from isif */ + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + ipipeif->input = IPIPEIF_INPUT_NONE; + break; + } + if (ipipeif->input != IPIPEIF_INPUT_NONE) + return -EBUSY; + + ipipeif->input = IPIPEIF_INPUT_ISIF; + break; + + case IPIPEIF_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + ipipeif->output = IPIPEIF_OUTPUT_NONE; + break; + } + if (remote->entity == &vpfe->vpfe_ipipe.subdev.entity) + /* connencted to ipipe */ + ipipeif->output = IPIPEIF_OUTPUT_IPIPE; + else if (remote->entity == &vpfe->vpfe_resizer. + crop_resizer.subdev.entity) + /* connected to resizer */ + ipipeif->output = IPIPEIF_OUTPUT_RESIZER; + else + return -EINVAL; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static const struct media_entity_operations ipipeif_media_ops = { + .link_setup = ipipeif_link_setup, +}; + +/* + * vpfe_ipipeif_unregister_entities() - Unregister entity + * @ipipeif - pointer to ipipeif subdevice structure. + */ +void vpfe_ipipeif_unregister_entities(struct vpfe_ipipeif_device *ipipeif) +{ + /* unregister video device */ + vpfe_video_unregister(&ipipeif->video_in); + + /* cleanup entity */ + media_entity_cleanup(&ipipeif->subdev.entity); + /* unregister subdev */ + v4l2_device_unregister_subdev(&ipipeif->subdev); +} + +int +vpfe_ipipeif_register_entities(struct vpfe_ipipeif_device *ipipeif, + struct v4l2_device *vdev) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(ipipeif); + unsigned int flags; + int ret; + + /* Register the subdev */ + ret = v4l2_device_register_subdev(vdev, &ipipeif->subdev); + if (ret < 0) + return ret; + + ret = vpfe_video_register(&ipipeif->video_in, vdev); + if (ret) { + pr_err("Failed to register ipipeif video-in device\n"); + goto fail; + } + ipipeif->video_in.vpfe_dev = vpfe_dev; + + flags = 0; + ret = media_entity_create_link(&ipipeif->video_in.video_dev.entity, 0, + &ipipeif->subdev.entity, 0, flags); + if (ret < 0) + goto fail; + + return 0; +fail: + v4l2_device_unregister_subdev(&ipipeif->subdev); + + return ret; +} + +#define IPIPEIF_GAIN_HIGH 0x3ff +#define IPIPEIF_DEFAULT_GAIN 0x200 + +int vpfe_ipipeif_init(struct vpfe_ipipeif_device *ipipeif, + struct platform_device *pdev) +{ + struct v4l2_subdev *sd = &ipipeif->subdev; + struct media_pad *pads = &ipipeif->pads[0]; + struct media_entity *me = &sd->entity; + static resource_size_t res_len; + struct resource *res; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 3); + if (!res) + return -ENOENT; + + res_len = resource_size(res); + res = request_mem_region(res->start, res_len, res->name); + if (!res) + return -EBUSY; + + ipipeif->ipipeif_base_addr = ioremap_nocache(res->start, res_len); + if (!ipipeif->ipipeif_base_addr) { + ret = -EBUSY; + goto fail; + } + + v4l2_subdev_init(sd, &ipipeif_v4l2_ops); + + sd->internal_ops = &ipipeif_v4l2_internal_ops; + strlcpy(sd->name, "DAVINCI IPIPEIF", sizeof(sd->name)); + sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ + + v4l2_set_subdevdata(sd, ipipeif); + + sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; + pads[IPIPEIF_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[IPIPEIF_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + ipipeif->input = IPIPEIF_INPUT_NONE; + ipipeif->output = IPIPEIF_OUTPUT_NONE; + me->ops = &ipipeif_media_ops; + + ret = media_entity_init(me, IPIPEIF_NUM_PADS, pads, 0); + if (ret) + goto fail; + + v4l2_ctrl_handler_init(&ipipeif->ctrls, 2); + v4l2_ctrl_new_std(&ipipeif->ctrls, &ipipeif_ctrl_ops, + V4L2_CID_GAIN, 0, + IPIPEIF_GAIN_HIGH, 1, IPIPEIF_DEFAULT_GAIN); + v4l2_ctrl_new_custom(&ipipeif->ctrls, &vpfe_ipipeif_dpcm_pred, NULL); + v4l2_ctrl_handler_setup(&ipipeif->ctrls); + sd->ctrl_handler = &ipipeif->ctrls; + + ipipeif->video_in.ops = &video_in_ops; + ipipeif->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + ret = vpfe_video_init(&ipipeif->video_in, "IPIPEIF"); + if (ret) { + pr_err("Failed to init IPIPEIF video-in device\n"); + goto fail; + } + ipipeif_set_default_config(ipipeif); + return 0; +fail: + release_mem_region(res->start, res_len); + return ret; +} + +void +vpfe_ipipeif_cleanup(struct vpfe_ipipeif_device *ipipeif, + struct platform_device *pdev) +{ + struct resource *res; + + v4l2_ctrl_handler_free(&ipipeif->ctrls); + iounmap(ipipeif->ipipeif_base_addr); + res = platform_get_resource(pdev, IORESOURCE_MEM, 3); + if (res) + release_mem_region(res->start, + res->end - res->start + 1); + +} diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipeif.h b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.h new file mode 100644 index 000000000000..608701fc5fed --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.h @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + */ + +#ifndef _DAVINCI_VPFE_DM365_IPIPEIF_H +#define _DAVINCI_VPFE_DM365_IPIPEIF_H + +#include <linux/platform_device.h> + +#include <media/davinci/vpss.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-subdev.h> + +#include "dm365_ipipeif_user.h" +#include "vpfe_video.h" + +/* IPIPE base specific types */ +enum ipipeif_data_shift { + IPIPEIF_BITS15_2 = 0, + IPIPEIF_BITS14_1 = 1, + IPIPEIF_BITS13_0 = 2, + IPIPEIF_BITS12_0 = 3, + IPIPEIF_BITS11_0 = 4, + IPIPEIF_BITS10_0 = 5, + IPIPEIF_BITS9_0 = 6, +}; + +enum ipipeif_clkdiv { + IPIPEIF_DIVIDE_HALF = 0, + IPIPEIF_DIVIDE_THIRD = 1, + IPIPEIF_DIVIDE_FOURTH = 2, + IPIPEIF_DIVIDE_FIFTH = 3, + IPIPEIF_DIVIDE_SIXTH = 4, + IPIPEIF_DIVIDE_EIGHTH = 5, + IPIPEIF_DIVIDE_SIXTEENTH = 6, + IPIPEIF_DIVIDE_THIRTY = 7, +}; + +enum ipipeif_pack_mode { + IPIPEIF_PACK_16_BIT = 0, + IPIPEIF_PACK_8_BIT = 1, +}; + +enum ipipeif_5_1_pack_mode { + IPIPEIF_5_1_PACK_16_BIT = 0, + IPIPEIF_5_1_PACK_8_BIT = 1, + IPIPEIF_5_1_PACK_8_BIT_A_LAW = 2, + IPIPEIF_5_1_PACK_12_BIT = 3 +}; + +enum ipipeif_input_source { + IPIPEIF_CCDC = 0, + IPIPEIF_SDRAM_RAW = 1, + IPIPEIF_CCDC_DARKFM = 2, + IPIPEIF_SDRAM_YUV = 3, +}; + +enum ipipeif_ialaw { + IPIPEIF_ALAW_OFF = 0, + IPIPEIF_ALAW_ON = 1, +}; + +enum ipipeif_input_src1 { + IPIPEIF_SRC1_PARALLEL_PORT = 0, + IPIPEIF_SRC1_SDRAM_RAW = 1, + IPIPEIF_SRC1_ISIF_DARKFM = 2, + IPIPEIF_SRC1_SDRAM_YUV = 3, +}; + +enum ipipeif_dfs_dir { + IPIPEIF_PORT_MINUS_SDRAM = 0, + IPIPEIF_SDRAM_MINUS_PORT = 1, +}; + +enum ipipeif_chroma_phase { + IPIPEIF_CBCR_Y = 0, + IPIPEIF_Y_CBCR = 1, +}; + +enum ipipeif_dpcm_type { + IPIPEIF_DPCM_8BIT_10BIT = 0, + IPIPEIF_DPCM_8BIT_12BIT = 1, +}; + +/* data shift for IPIPE 5.1 */ +enum ipipeif_5_1_data_shift { + IPIPEIF_5_1_BITS11_0 = 0, + IPIPEIF_5_1_BITS10_0 = 1, + IPIPEIF_5_1_BITS9_0 = 2, + IPIPEIF_5_1_BITS8_0 = 3, + IPIPEIF_5_1_BITS7_0 = 4, + IPIPEIF_5_1_BITS15_4 = 5, +}; + +#define IPIPEIF_PAD_SINK 0 +#define IPIPEIF_PAD_SOURCE 1 + +#define IPIPEIF_NUM_PADS 2 + +enum ipipeif_input_entity { + IPIPEIF_INPUT_NONE = 0, + IPIPEIF_INPUT_ISIF = 1, + IPIPEIF_INPUT_MEMORY = 2, +}; + +enum ipipeif_output_entity { + IPIPEIF_OUTPUT_NONE = 0, + IPIPEIF_OUTPUT_IPIPE = 1, + IPIPEIF_OUTPUT_RESIZER = 2, +}; + +struct vpfe_ipipeif_device { + struct v4l2_subdev subdev; + struct media_pad pads[IPIPEIF_NUM_PADS]; + struct v4l2_mbus_framefmt formats[IPIPEIF_NUM_PADS]; + enum ipipeif_input_entity input; + unsigned int output; + struct vpfe_video_device video_in; + struct v4l2_ctrl_handler ctrls; + void *__iomem ipipeif_base_addr; + struct ipipeif_params config; + int dpcm_predictor; + int gain; +}; + +/* IPIPEIF Register Offsets from the base address */ +#define IPIPEIF_ENABLE 0x00 +#define IPIPEIF_CFG1 0x04 +#define IPIPEIF_PPLN 0x08 +#define IPIPEIF_LPFR 0x0c +#define IPIPEIF_HNUM 0x10 +#define IPIPEIF_VNUM 0x14 +#define IPIPEIF_ADDRU 0x18 +#define IPIPEIF_ADDRL 0x1c +#define IPIPEIF_ADOFS 0x20 +#define IPIPEIF_RSZ 0x24 +#define IPIPEIF_GAIN 0x28 + +/* Below registers are available only on IPIPE 5.1 */ +#define IPIPEIF_DPCM 0x2c +#define IPIPEIF_CFG2 0x30 +#define IPIPEIF_INIRSZ 0x34 +#define IPIPEIF_OCLIP 0x38 +#define IPIPEIF_DTUDF 0x3c +#define IPIPEIF_CLKDIV 0x40 +#define IPIPEIF_DPC1 0x44 +#define IPIPEIF_DPC2 0x48 +#define IPIPEIF_DFSGVL 0x4c +#define IPIPEIF_DFSGTH 0x50 +#define IPIPEIF_RSZ3A 0x54 +#define IPIPEIF_INIRSZ3A 0x58 +#define IPIPEIF_RSZ_MIN 16 +#define IPIPEIF_RSZ_MAX 112 +#define IPIPEIF_RSZ_CONST 16 +#define SETBIT(reg, bit) (reg = ((reg) | ((0x00000001)<<(bit)))) +#define RESETBIT(reg, bit) (reg = ((reg) & (~(0x00000001<<(bit))))) + +#define IPIPEIF_ADOFS_LSB_MASK 0x1ff +#define IPIPEIF_ADOFS_LSB_SHIFT 5 +#define IPIPEIF_ADOFS_MSB_MASK 0x200 +#define IPIPEIF_ADDRU_MASK 0x7ff +#define IPIPEIF_ADDRL_SHIFT 5 +#define IPIPEIF_ADDRL_MASK 0xffff +#define IPIPEIF_ADDRU_SHIFT 21 +#define IPIPEIF_ADDRMSB_SHIFT 31 +#define IPIPEIF_ADDRMSB_LEFT_SHIFT 10 + +/* CFG1 Masks and shifts */ +#define ONESHOT_SHIFT 0 +#define DECIM_SHIFT 1 +#define INPSRC_SHIFT 2 +#define CLKDIV_SHIFT 4 +#define AVGFILT_SHIFT 7 +#define PACK8IN_SHIFT 8 +#define IALAW_SHIFT 9 +#define CLKSEL_SHIFT 10 +#define DATASFT_SHIFT 11 +#define INPSRC1_SHIFT 14 + +/* DPC2 */ +#define IPIPEIF_DPC2_EN_SHIFT 12 +#define IPIPEIF_DPC2_THR_MASK 0xfff +/* Applicable for IPIPE 5.1 */ +#define IPIPEIF_DF_GAIN_EN_SHIFT 10 +#define IPIPEIF_DF_GAIN_MASK 0x3ff +#define IPIPEIF_DF_GAIN_THR_MASK 0xfff +/* DPCM */ +#define IPIPEIF_DPCM_BITS_SHIFT 2 +#define IPIPEIF_DPCM_PRED_SHIFT 1 +/* CFG2 */ +#define IPIPEIF_CFG2_HDPOL_SHIFT 1 +#define IPIPEIF_CFG2_VDPOL_SHIFT 2 +#define IPIPEIF_CFG2_YUV8_SHIFT 6 +#define IPIPEIF_CFG2_YUV16_SHIFT 3 +#define IPIPEIF_CFG2_YUV8P_SHIFT 7 + +/* INIRSZ */ +#define IPIPEIF_INIRSZ_ALNSYNC_SHIFT 13 +#define IPIPEIF_INIRSZ_MASK 0x1fff + +/* CLKDIV */ +#define IPIPEIF_CLKDIV_M_SHIFT 8 + +void vpfe_ipipeif_enable(struct vpfe_device *vpfe_dev); +void vpfe_ipipeif_ss_buffer_isr(struct vpfe_ipipeif_device *ipipeif); +int vpfe_ipipeif_decimation_enabled(struct vpfe_device *vpfe_dev); +int vpfe_ipipeif_get_rsz(struct vpfe_device *vpfe_dev); +void vpfe_ipipeif_cleanup(struct vpfe_ipipeif_device *ipipeif, + struct platform_device *pdev); +int vpfe_ipipeif_init(struct vpfe_ipipeif_device *ipipeif, + struct platform_device *pdev); +int vpfe_ipipeif_register_entities(struct vpfe_ipipeif_device *ipipeif, + struct v4l2_device *vdev); +void vpfe_ipipeif_unregister_entities(struct vpfe_ipipeif_device *ipipeif); + +#endif /* _DAVINCI_VPFE_DM365_IPIPEIF_H */ diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipeif_user.h b/drivers/staging/media/davinci_vpfe/dm365_ipipeif_user.h new file mode 100644 index 000000000000..e2a69b59554a --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipeif_user.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + */ + +#ifndef _DAVINCI_VPFE_DM365_IPIPEIF_USER_H +#define _DAVINCI_VPFE_DM365_IPIPEIF_USER_H + +/* clockdiv for IPIPE 5.1 */ +struct ipipeif_5_1_clkdiv { + unsigned char m; + unsigned char n; +}; + +enum ipipeif_decimation { + IPIPEIF_DECIMATION_OFF, + IPIPEIF_DECIMATION_ON +}; + +/* DPC at the if for IPIPE 5.1 */ +struct ipipeif_dpc { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* threshold */ + unsigned short thr; +}; + +enum ipipeif_clock { + IPIPEIF_PIXCEL_CLK, + IPIPEIF_SDRAM_CLK +}; + +enum ipipeif_avg_filter { + IPIPEIF_AVG_OFF, + IPIPEIF_AVG_ON +}; + +struct ipipeif_5_1 { + struct ipipeif_5_1_clkdiv clk_div; + /* Defect pixel correction */ + struct ipipeif_dpc dpc; + /* clipped to this value */ + unsigned short clip; + /* Align HSync and VSync to rsz_start */ + unsigned char align_sync; + /* resizer start position */ + unsigned int rsz_start; + /* DF gain enable */ + unsigned char df_gain_en; + /* DF gain value */ + unsigned short df_gain; + /* DF gain threshold value */ + unsigned short df_gain_thr; +}; + +struct ipipeif_params { + enum ipipeif_clock clock_select; + unsigned int ppln; + unsigned int lpfr; + unsigned char rsz; + enum ipipeif_decimation decimation; + enum ipipeif_avg_filter avg_filter; + /* IPIPE 5.1 */ + struct ipipeif_5_1 if_5_1; +}; + +/* + * Private IOCTL + * VIDIOC_VPFE_IPIPEIF_S_CONFIG: Set IPIEIF configuration + * VIDIOC_VPFE_IPIPEIF_G_CONFIG: Get IPIEIF configuration + */ +#define VIDIOC_VPFE_IPIPEIF_S_CONFIG \ + _IOWR('I', BASE_VIDIOC_PRIVATE + 1, struct ipipeif_params) +#define VIDIOC_VPFE_IPIPEIF_G_CONFIG \ + _IOWR('I', BASE_VIDIOC_PRIVATE + 2, struct ipipeif_params) + +#endif /* _DAVINCI_VPFE_DM365_IPIPEIF_USER_H */ diff --git a/drivers/staging/media/davinci_vpfe/dm365_isif.c b/drivers/staging/media/davinci_vpfe/dm365_isif.c new file mode 100644 index 000000000000..ebeea72e176a --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_isif.c @@ -0,0 +1,2104 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + */ + +#include "dm365_isif.h" +#include "vpfe_mc_capture.h" + +#define MAX_WIDTH 4096 +#define MAX_HEIGHT 4096 + +static const unsigned int isif_fmts[] = { + V4L2_MBUS_FMT_YUYV8_2X8, + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_YUYV8_1X16, + V4L2_MBUS_FMT_YUYV10_1X20, + V4L2_MBUS_FMT_SGRBG12_1X12, + V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8, + V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, +}; + +#define ISIF_COLPTN_R_Ye 0x0 +#define ISIF_COLPTN_Gr_Cy 0x1 +#define ISIF_COLPTN_Gb_G 0x2 +#define ISIF_COLPTN_B_Mg 0x3 + +#define ISIF_CCOLP_CP01_0 0 +#define ISIF_CCOLP_CP03_2 2 +#define ISIF_CCOLP_CP05_4 4 +#define ISIF_CCOLP_CP07_6 6 +#define ISIF_CCOLP_CP11_0 8 +#define ISIF_CCOLP_CP13_2 10 +#define ISIF_CCOLP_CP15_4 12 +#define ISIF_CCOLP_CP17_6 14 + +static const u32 isif_sgrbg_pattern = + ISIF_COLPTN_Gr_Cy << ISIF_CCOLP_CP01_0 | + ISIF_COLPTN_R_Ye << ISIF_CCOLP_CP03_2 | + ISIF_COLPTN_B_Mg << ISIF_CCOLP_CP05_4 | + ISIF_COLPTN_Gb_G << ISIF_CCOLP_CP07_6 | + ISIF_COLPTN_Gr_Cy << ISIF_CCOLP_CP11_0 | + ISIF_COLPTN_R_Ye << ISIF_CCOLP_CP13_2 | + ISIF_COLPTN_B_Mg << ISIF_CCOLP_CP15_4 | + ISIF_COLPTN_Gb_G << ISIF_CCOLP_CP17_6; + +static const u32 isif_srggb_pattern = + ISIF_COLPTN_R_Ye << ISIF_CCOLP_CP01_0 | + ISIF_COLPTN_Gr_Cy << ISIF_CCOLP_CP03_2 | + ISIF_COLPTN_Gb_G << ISIF_CCOLP_CP05_4 | + ISIF_COLPTN_B_Mg << ISIF_CCOLP_CP07_6 | + ISIF_COLPTN_R_Ye << ISIF_CCOLP_CP11_0 | + ISIF_COLPTN_Gr_Cy << ISIF_CCOLP_CP13_2 | + ISIF_COLPTN_Gb_G << ISIF_CCOLP_CP15_4 | + ISIF_COLPTN_B_Mg << ISIF_CCOLP_CP17_6; + +static inline u32 isif_read(void *__iomem base_addr, u32 offset) +{ + return readl(base_addr + offset); +} + +static inline void isif_write(void *__iomem base_addr, u32 val, u32 offset) +{ + writel(val, base_addr + offset); +} + +static inline u32 isif_merge(void *__iomem base_addr, u32 mask, u32 val, + u32 offset) +{ + u32 new_val = (isif_read(base_addr, offset) & ~mask) | (val & mask); + + isif_write(base_addr, new_val, offset); + + return new_val; +} + +static void isif_enable_output_to_sdram(struct vpfe_isif_device *isif, int en) +{ + isif_merge(isif->isif_cfg.base_addr, ISIF_SYNCEN_WEN_MASK, + en << ISIF_SYNCEN_WEN_SHIFT, SYNCEN); +} + +static inline void +isif_regw_lin_tbl(struct vpfe_isif_device *isif, u32 val, u32 offset, int i) +{ + if (!i) + writel(val, isif->isif_cfg.linear_tbl0_addr + offset); + else + writel(val, isif->isif_cfg.linear_tbl1_addr + offset); +} + +static void isif_disable_all_modules(struct vpfe_isif_device *isif) +{ + /* disable BC */ + isif_write(isif->isif_cfg.base_addr, 0, CLAMPCFG); + /* disable vdfc */ + isif_write(isif->isif_cfg.base_addr, 0, DFCCTL); + /* disable CSC */ + isif_write(isif->isif_cfg.base_addr, 0, CSCCTL); + /* disable linearization */ + isif_write(isif->isif_cfg.base_addr, 0, LINCFG0); +} + +static void isif_enable(struct vpfe_isif_device *isif, int en) +{ + if (!en) + /* Before disable isif, disable all ISIF modules */ + isif_disable_all_modules(isif); + + /* + * wait for next VD. Assume lowest scan rate is 12 Hz. So + * 100 msec delay is good enough + */ + msleep(100); + isif_merge(isif->isif_cfg.base_addr, ISIF_SYNCEN_VDHDEN_MASK, + en, SYNCEN); +} + +/* + * ISIF helper functions + */ + +#define DM365_ISIF_MDFS_OFFSET 15 +#define DM365_ISIF_MDFS_MASK 0x1 + +/* get field id in isif hardware */ +enum v4l2_field vpfe_isif_get_fid(struct vpfe_device *vpfe_dev) +{ + struct vpfe_isif_device *isif = &vpfe_dev->vpfe_isif; + u32 field_status; + + field_status = isif_read(isif->isif_cfg.base_addr, MODESET); + field_status = (field_status >> DM365_ISIF_MDFS_OFFSET) & + DM365_ISIF_MDFS_MASK; + return field_status; +} + +static int +isif_set_pixel_format(struct vpfe_isif_device *isif, unsigned int pixfmt) +{ + if (isif->formats[ISIF_PAD_SINK].code == V4L2_MBUS_FMT_SGRBG12_1X12) { + if (pixfmt == V4L2_PIX_FMT_SBGGR16) + isif->isif_cfg.data_pack = ISIF_PACK_16BIT; + else if ((pixfmt == V4L2_PIX_FMT_SGRBG10DPCM8) || + (pixfmt == V4L2_PIX_FMT_SGRBG10ALAW8)) + isif->isif_cfg.data_pack = ISIF_PACK_8BIT; + else + return -EINVAL; + + isif->isif_cfg.bayer.pix_fmt = ISIF_PIXFMT_RAW; + isif->isif_cfg.bayer.v4l2_pix_fmt = pixfmt; + } else { + if (pixfmt == V4L2_PIX_FMT_YUYV) + isif->isif_cfg.ycbcr.pix_order = ISIF_PIXORDER_YCBYCR; + else if (pixfmt == V4L2_PIX_FMT_UYVY) + isif->isif_cfg.ycbcr.pix_order = ISIF_PIXORDER_CBYCRY; + else + return -EINVAL; + + isif->isif_cfg.data_pack = ISIF_PACK_8BIT; + isif->isif_cfg.ycbcr.v4l2_pix_fmt = pixfmt; + } + + return 0; +} + +static int +isif_set_frame_format(struct vpfe_isif_device *isif, + enum isif_frmfmt frm_fmt) +{ + if (isif->formats[ISIF_PAD_SINK].code == V4L2_MBUS_FMT_SGRBG12_1X12) + isif->isif_cfg.bayer.frm_fmt = frm_fmt; + else + isif->isif_cfg.ycbcr.frm_fmt = frm_fmt; + + return 0; +} + +static int isif_set_image_window(struct vpfe_isif_device *isif) +{ + struct v4l2_rect *win = &isif->crop; + + if (isif->formats[ISIF_PAD_SINK].code == V4L2_MBUS_FMT_SGRBG12_1X12) { + isif->isif_cfg.bayer.win.top = win->top; + isif->isif_cfg.bayer.win.left = win->left; + isif->isif_cfg.bayer.win.width = win->width; + isif->isif_cfg.bayer.win.height = win->height; + return 0; + } + isif->isif_cfg.ycbcr.win.top = win->top; + isif->isif_cfg.ycbcr.win.left = win->left; + isif->isif_cfg.ycbcr.win.width = win->width; + isif->isif_cfg.ycbcr.win.height = win->height; + + return 0; +} + +static int +isif_set_buftype(struct vpfe_isif_device *isif, enum isif_buftype buf_type) +{ + if (isif->formats[ISIF_PAD_SINK].code == V4L2_MBUS_FMT_SGRBG12_1X12) + isif->isif_cfg.bayer.buf_type = buf_type; + else + isif->isif_cfg.ycbcr.buf_type = buf_type; + + return 0; +} + +/* configure format in isif hardware */ +static int +isif_config_format(struct vpfe_device *vpfe_dev, unsigned int pad) +{ + struct vpfe_isif_device *vpfe_isif = &vpfe_dev->vpfe_isif; + enum isif_frmfmt frm_fmt = ISIF_FRMFMT_INTERLACED; + struct v4l2_pix_format format; + int ret = 0; + + v4l2_fill_pix_format(&format, &vpfe_dev->vpfe_isif.formats[pad]); + mbus_to_pix(&vpfe_dev->vpfe_isif.formats[pad], &format); + + if (isif_set_pixel_format(vpfe_isif, format.pixelformat) < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Failed to set pixel format in isif\n"); + return -EINVAL; + } + + /* call for s_crop will override these values */ + vpfe_isif->crop.left = 0; + vpfe_isif->crop.top = 0; + vpfe_isif->crop.width = format.width; + vpfe_isif->crop.height = format.height; + + /* configure the image window */ + isif_set_image_window(vpfe_isif); + + switch (vpfe_dev->vpfe_isif.formats[pad].field) { + case V4L2_FIELD_INTERLACED: + /* do nothing, since it is default */ + ret = isif_set_buftype(vpfe_isif, ISIF_BUFTYPE_FLD_INTERLEAVED); + break; + + case V4L2_FIELD_NONE: + frm_fmt = ISIF_FRMFMT_PROGRESSIVE; + /* buffer type only applicable for interlaced scan */ + break; + + case V4L2_FIELD_SEQ_TB: + ret = isif_set_buftype(vpfe_isif, ISIF_BUFTYPE_FLD_SEPARATED); + break; + + default: + return -EINVAL; + } + + /* set the frame format */ + if (!ret) + ret = isif_set_frame_format(vpfe_isif, frm_fmt); + + return ret; +} + +/* + * isif_try_format() - Try video format on a pad + * @isif: VPFE isif device + * @fh: V4L2 subdev file handle + * @fmt: pointer to v4l2 subdev format structure + */ +static void +isif_try_format(struct vpfe_isif_device *isif, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + unsigned int width = fmt->format.width; + unsigned int height = fmt->format.height; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(isif_fmts); i++) { + if (fmt->format.code == isif_fmts[i]) + break; + } + + /* If not found, use YUYV8_2x8 as default */ + if (i >= ARRAY_SIZE(isif_fmts)) + fmt->format.code = V4L2_MBUS_FMT_YUYV8_2X8; + + /* Clamp the size. */ + fmt->format.width = clamp_t(u32, width, 32, MAX_WIDTH); + fmt->format.height = clamp_t(u32, height, 32, MAX_HEIGHT); + + /* The data formatter truncates the number of horizontal output + * pixels to a multiple of 16. To avoid clipping data, allow + * callers to request an output size bigger than the input size + * up to the nearest multiple of 16. + */ + if (fmt->pad == ISIF_PAD_SOURCE) + fmt->format.width &= ~15; +} + +/* + * vpfe_isif_buffer_isr() - isif module non-progressive buffer scheduling isr + * @isif: Pointer to isif subdevice. + */ +void vpfe_isif_buffer_isr(struct vpfe_isif_device *isif) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(isif); + struct vpfe_video_device *video = &isif->video_out; + enum v4l2_field field; + int fid; + + if (!video->started) + return; + + field = video->fmt.fmt.pix.field; + + if (field == V4L2_FIELD_NONE) { + /* handle progressive frame capture */ + if (video->cur_frm != video->next_frm) + vpfe_video_process_buffer_complete(video); + return; + } + + /* interlaced or TB capture check which field we + * are in hardware + */ + fid = vpfe_isif_get_fid(vpfe_dev); + + /* switch the software maintained field id */ + video->field_id ^= 1; + if (fid == video->field_id) { + /* we are in-sync here,continue */ + if (fid == 0) { + /* + * One frame is just being captured. If the + * next frame is available, release the current + * frame and move on + */ + if (video->cur_frm != video->next_frm) + vpfe_video_process_buffer_complete(video); + /* + * based on whether the two fields are stored + * interleavely or separately in memory, + * reconfigure the ISIF memory address + */ + if (field == V4L2_FIELD_SEQ_TB) + vpfe_video_schedule_bottom_field(video); + return; + } + /* + * if one field is just being captured configure + * the next frame get the next frame from the + * empty queue if no frame is available hold on + * to the current buffer + */ + spin_lock(&video->dma_queue_lock); + if (!list_empty(&video->dma_queue) && + video->cur_frm == video->next_frm) + vpfe_video_schedule_next_buffer(video); + spin_unlock(&video->dma_queue_lock); + } else if (fid == 0) { + /* + * out of sync. Recover from any hardware out-of-sync. + * May loose one frame + */ + video->field_id = fid; + } +} + +/* + * vpfe_isif_vidint1_isr() - ISIF module progressive buffer scheduling isr + * @isif: Pointer to isif subdevice. + */ +void vpfe_isif_vidint1_isr(struct vpfe_isif_device *isif) +{ + struct vpfe_video_device *video = &isif->video_out; + + if (!video->started) + return; + + spin_lock(&video->dma_queue_lock); + if (video->fmt.fmt.pix.field == V4L2_FIELD_NONE && + !list_empty(&video->dma_queue) && video->cur_frm == video->next_frm) + vpfe_video_schedule_next_buffer(video); + + spin_unlock(&video->dma_queue_lock); +} + +/* + * VPFE video operations + */ + +static int isif_video_queue(struct vpfe_device *vpfe_dev, unsigned long addr) +{ + struct vpfe_isif_device *isif = &vpfe_dev->vpfe_isif; + + isif_write(isif->isif_cfg.base_addr, (addr >> 21) & + ISIF_CADU_BITS, CADU); + isif_write(isif->isif_cfg.base_addr, (addr >> 5) & + ISIF_CADL_BITS, CADL); + + return 0; +} + +static const struct vpfe_video_operations isif_video_ops = { + .queue = isif_video_queue, +}; + +/* + * V4L2 subdev operations + */ + +/* Parameter operations */ +static int isif_get_params(struct v4l2_subdev *sd, void *params) +{ + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + + /* only raw module parameters can be set through the IOCTL */ + if (isif->formats[ISIF_PAD_SINK].code != V4L2_MBUS_FMT_SGRBG12_1X12) + return -EINVAL; + memcpy(params, &isif->isif_cfg.bayer.config_params, + sizeof(isif->isif_cfg.bayer.config_params)); + return 0; +} + +static int isif_validate_df_csc_params(struct vpfe_isif_df_csc *df_csc) +{ + struct vpfe_isif_color_space_conv *csc; + int err = -EINVAL; + int csc_df_en; + int i; + + if (!df_csc->df_or_csc) { + /* csc configuration */ + csc = &df_csc->csc; + if (csc->en) { + csc_df_en = 1; + for (i = 0; i < VPFE_ISIF_CSC_NUM_COEFF; i++) + if (csc->coeff[i].integer > + ISIF_CSC_COEF_INTEG_MASK || + csc->coeff[i].decimal > + ISIF_CSC_COEF_DECIMAL_MASK) { + pr_err("Invalid CSC coefficients\n"); + return err; + } + } + } + if (df_csc->start_pix > ISIF_DF_CSC_SPH_MASK) { + pr_err("Invalid df_csc start pix value\n"); + return err; + } + + if (df_csc->num_pixels > ISIF_DF_NUMPIX) { + pr_err("Invalid df_csc num pixels value\n"); + return err; + } + + if (df_csc->start_line > ISIF_DF_CSC_LNH_MASK) { + pr_err("Invalid df_csc start_line value\n"); + return err; + } + + if (df_csc->num_lines > ISIF_DF_NUMLINES) { + pr_err("Invalid df_csc num_lines value\n"); + return err; + } + + return 0; +} + +#define DM365_ISIF_MAX_VDFLSFT 4 +#define DM365_ISIF_MAX_VDFSLV 4095 +#define DM365_ISIF_MAX_DFCMEM0 0x1fff +#define DM365_ISIF_MAX_DFCMEM1 0x1fff + +static int isif_validate_dfc_params(struct vpfe_isif_dfc *dfc) +{ + int err = -EINVAL; + int i; + + if (!dfc->en) + return 0; + + if (dfc->corr_whole_line > 1) { + pr_err("Invalid corr_whole_line value\n"); + return err; + } + + if (dfc->def_level_shift > DM365_ISIF_MAX_VDFLSFT) { + pr_err("Invalid def_level_shift value\n"); + return err; + } + + if (dfc->def_sat_level > DM365_ISIF_MAX_VDFSLV) { + pr_err("Invalid def_sat_level value\n"); + return err; + } + + if (!dfc->num_vdefects || + dfc->num_vdefects > VPFE_ISIF_VDFC_TABLE_SIZE) { + pr_err("Invalid num_vdefects value\n"); + return err; + } + + for (i = 0; i < VPFE_ISIF_VDFC_TABLE_SIZE; i++) { + if (dfc->table[i].pos_vert > DM365_ISIF_MAX_DFCMEM0) { + pr_err("Invalid pos_vert value\n"); + return err; + } + if (dfc->table[i].pos_horz > DM365_ISIF_MAX_DFCMEM1) { + pr_err("Invalid pos_horz value\n"); + return err; + } + } + + return 0; +} + +#define DM365_ISIF_MAX_CLVRV 0xfff +#define DM365_ISIF_MAX_CLDC 0x1fff +#define DM365_ISIF_MAX_CLHSH 0x1fff +#define DM365_ISIF_MAX_CLHSV 0x1fff +#define DM365_ISIF_MAX_CLVSH 0x1fff +#define DM365_ISIF_MAX_CLVSV 0x1fff +#define DM365_ISIF_MAX_HEIGHT_BLACK_REGION 0x1fff + +static int isif_validate_bclamp_params(struct vpfe_isif_black_clamp *bclamp) +{ + int err = -EINVAL; + + if (bclamp->dc_offset > DM365_ISIF_MAX_CLDC) { + pr_err("Invalid bclamp dc_offset value\n"); + return err; + } + if (!bclamp->en) + return 0; + if (bclamp->horz.clamp_pix_limit > 1) { + pr_err("Invalid bclamp horz clamp_pix_limit value\n"); + return err; + } + if (bclamp->horz.win_count_calc < 1 || + bclamp->horz.win_count_calc > 32) { + pr_err("Invalid bclamp horz win_count_calc value\n"); + return err; + } + if (bclamp->horz.win_start_h_calc > DM365_ISIF_MAX_CLHSH) { + pr_err("Invalid bclamp win_start_v_calc value\n"); + return err; + } + + if (bclamp->horz.win_start_v_calc > DM365_ISIF_MAX_CLHSV) { + pr_err("Invalid bclamp win_start_v_calc value\n"); + return err; + } + if (bclamp->vert.reset_clamp_val > DM365_ISIF_MAX_CLVRV) { + pr_err("Invalid bclamp reset_clamp_val value\n"); + return err; + } + if (bclamp->vert.ob_v_sz_calc > DM365_ISIF_MAX_HEIGHT_BLACK_REGION) { + pr_err("Invalid bclamp ob_v_sz_calc value\n"); + return err; + } + if (bclamp->vert.ob_start_h > DM365_ISIF_MAX_CLVSH) { + pr_err("Invalid bclamp ob_start_h value\n"); + return err; + } + if (bclamp->vert.ob_start_v > DM365_ISIF_MAX_CLVSV) { + pr_err("Invalid bclamp ob_start_h value\n"); + return err; + } + return 0; +} + +static int +isif_validate_raw_params(struct vpfe_isif_raw_config *params) +{ + int ret; + + ret = isif_validate_df_csc_params(¶ms->df_csc); + if (ret) + return ret; + ret = isif_validate_dfc_params(¶ms->dfc); + if (ret) + return ret; + ret = isif_validate_bclamp_params(¶ms->bclamp); + return ret; +} + +static int isif_set_params(struct v4l2_subdev *sd, void *params) +{ + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + struct vpfe_isif_raw_config isif_raw_params; + int ret = -EINVAL; + + /* only raw module parameters can be set through the IOCTL */ + if (isif->formats[ISIF_PAD_SINK].code != V4L2_MBUS_FMT_SGRBG12_1X12) + return ret; + + memcpy(&isif_raw_params, params, sizeof(isif_raw_params)); + if (!isif_validate_raw_params(&isif_raw_params)) { + memcpy(&isif->isif_cfg.bayer.config_params, &isif_raw_params, + sizeof(isif_raw_params)); + ret = 0; + } + return ret; +} +/* + * isif_ioctl() - isif module private ioctl's + * @sd: VPFE isif V4L2 subdevice + * @cmd: ioctl command + * @arg: ioctl argument + * + * Return 0 on success or a negative error code otherwise. + */ +static long isif_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + int ret; + + switch (cmd) { + case VIDIOC_VPFE_ISIF_S_RAW_PARAMS: + ret = isif_set_params(sd, arg); + break; + + case VIDIOC_VPFE_ISIF_G_RAW_PARAMS: + ret = isif_get_params(sd, arg); + break; + + default: + ret = -ENOIOCTLCMD; + } + return ret; +} + +static void isif_config_gain_offset(struct vpfe_isif_device *isif) +{ + struct vpfe_isif_gain_offsets_adj *gain_off_ptr = + &isif->isif_cfg.bayer.config_params.gain_offset; + void *__iomem base = isif->isif_cfg.base_addr; + u32 val; + + val = ((gain_off_ptr->gain_sdram_en & 1) << GAIN_SDRAM_EN_SHIFT) | + ((gain_off_ptr->gain_ipipe_en & 1) << GAIN_IPIPE_EN_SHIFT) | + ((gain_off_ptr->gain_h3a_en & 1) << GAIN_H3A_EN_SHIFT) | + ((gain_off_ptr->offset_sdram_en & 1) << OFST_SDRAM_EN_SHIFT) | + ((gain_off_ptr->offset_ipipe_en & 1) << OFST_IPIPE_EN_SHIFT) | + ((gain_off_ptr->offset_h3a_en & 1) << OFST_H3A_EN_SHIFT); + isif_merge(base, GAIN_OFFSET_EN_MASK, val, CGAMMAWD); + + isif_write(base, isif->isif_cfg.isif_gain_params.cr_gain, CRGAIN); + isif_write(base, isif->isif_cfg.isif_gain_params.cgr_gain, CGRGAIN); + isif_write(base, isif->isif_cfg.isif_gain_params.cgb_gain, CGBGAIN); + isif_write(base, isif->isif_cfg.isif_gain_params.cb_gain, CBGAIN); + isif_write(base, isif->isif_cfg.isif_gain_params.offset & OFFSET_MASK, + COFSTA); + +} + +static void isif_config_bclamp(struct vpfe_isif_device *isif, + struct vpfe_isif_black_clamp *bc) +{ + u32 val; + + /** + * DC Offset is always added to image data irrespective of bc enable + * status + */ + val = bc->dc_offset & ISIF_BC_DCOFFSET_MASK; + isif_write(isif->isif_cfg.base_addr, val, CLDCOFST); + + if (!bc->en) + return; + + val = (bc->bc_mode_color & ISIF_BC_MODE_COLOR_MASK) << + ISIF_BC_MODE_COLOR_SHIFT; + + /* Enable BC and horizontal clamp caculation paramaters */ + val = val | 1 | ((bc->horz.mode & ISIF_HORZ_BC_MODE_MASK) << + ISIF_HORZ_BC_MODE_SHIFT); + + isif_write(isif->isif_cfg.base_addr, val, CLAMPCFG); + + if (bc->horz.mode != VPFE_ISIF_HORZ_BC_DISABLE) { + /* + * Window count for calculation + * Base window selection + * pixel limit + * Horizontal size of window + * vertical size of the window + * Horizontal start position of the window + * Vertical start position of the window + */ + val = (bc->horz.win_count_calc & ISIF_HORZ_BC_WIN_COUNT_MASK) | + ((bc->horz.base_win_sel_calc & 1) << + ISIF_HORZ_BC_WIN_SEL_SHIFT) | + ((bc->horz.clamp_pix_limit & 1) << + ISIF_HORZ_BC_PIX_LIMIT_SHIFT) | + ((bc->horz.win_h_sz_calc & + ISIF_HORZ_BC_WIN_H_SIZE_MASK) << + ISIF_HORZ_BC_WIN_H_SIZE_SHIFT) | + ((bc->horz.win_v_sz_calc & + ISIF_HORZ_BC_WIN_V_SIZE_MASK) << + ISIF_HORZ_BC_WIN_V_SIZE_SHIFT); + + isif_write(isif->isif_cfg.base_addr, val, CLHWIN0); + + val = bc->horz.win_start_h_calc & ISIF_HORZ_BC_WIN_START_H_MASK; + isif_write(isif->isif_cfg.base_addr, val, CLHWIN1); + + val = bc->horz.win_start_v_calc & ISIF_HORZ_BC_WIN_START_V_MASK; + isif_write(isif->isif_cfg.base_addr, val, CLHWIN2); + } + + /* vertical clamp caculation paramaters */ + /* OB H Valid */ + val = bc->vert.ob_h_sz_calc & ISIF_VERT_BC_OB_H_SZ_MASK; + + /* Reset clamp value sel for previous line */ + val |= (bc->vert.reset_val_sel & ISIF_VERT_BC_RST_VAL_SEL_MASK) << + ISIF_VERT_BC_RST_VAL_SEL_SHIFT; + + /* Line average coefficient */ + val |= bc->vert.line_ave_coef << ISIF_VERT_BC_LINE_AVE_COEF_SHIFT; + isif_write(isif->isif_cfg.base_addr, val, CLVWIN0); + + /* Configured reset value */ + if (bc->vert.reset_val_sel == VPFE_ISIF_VERT_BC_USE_CONFIG_CLAMP_VAL) { + val = bc->vert.reset_clamp_val & ISIF_VERT_BC_RST_VAL_MASK; + isif_write(isif->isif_cfg.base_addr, val, CLVRV); + } + + /* Optical Black horizontal start position */ + val = bc->vert.ob_start_h & ISIF_VERT_BC_OB_START_HORZ_MASK; + isif_write(isif->isif_cfg.base_addr, val, CLVWIN1); + + /* Optical Black vertical start position */ + val = bc->vert.ob_start_v & ISIF_VERT_BC_OB_START_VERT_MASK; + isif_write(isif->isif_cfg.base_addr, val, CLVWIN2); + + val = bc->vert.ob_v_sz_calc & ISIF_VERT_BC_OB_VERT_SZ_MASK; + isif_write(isif->isif_cfg.base_addr, val, CLVWIN3); + + /* Vertical start position for BC subtraction */ + val = bc->vert_start_sub & ISIF_BC_VERT_START_SUB_V_MASK; + isif_write(isif->isif_cfg.base_addr, val, CLSV); +} + +/* This function will configure the window size to be capture in ISIF reg */ +static void +isif_setwin(struct vpfe_isif_device *isif, struct v4l2_rect *image_win, + enum isif_frmfmt frm_fmt, int ppc, int mode) +{ + int horz_nr_pixels; + int vert_nr_lines; + int horz_start; + int vert_start; + int mid_img; + + /* + * ppc - per pixel count. indicates how many pixels per cell + * output to SDRAM. example, for ycbcr, it is one y and one c, so 2. + * raw capture this is 1 + */ + horz_start = image_win->left << (ppc - 1); + horz_nr_pixels = (image_win->width << (ppc - 1)) - 1; + + /* Writing the horizontal info into the registers */ + isif_write(isif->isif_cfg.base_addr, + horz_start & START_PX_HOR_MASK, SPH); + isif_write(isif->isif_cfg.base_addr, + horz_nr_pixels & NUM_PX_HOR_MASK, LNH); + vert_start = image_win->top; + + if (frm_fmt == ISIF_FRMFMT_INTERLACED) { + vert_nr_lines = (image_win->height >> 1) - 1; + vert_start >>= 1; + /* To account for VD since line 0 doesn't have any data */ + vert_start += 1; + } else { + /* To account for VD since line 0 doesn't have any data */ + vert_start += 1; + vert_nr_lines = image_win->height - 1; + /* configure VDINT0 and VDINT1 */ + mid_img = vert_start + (image_win->height / 2); + isif_write(isif->isif_cfg.base_addr, mid_img, VDINT1); + } + + if (!mode) + isif_write(isif->isif_cfg.base_addr, 0, VDINT0); + else + isif_write(isif->isif_cfg.base_addr, vert_nr_lines, VDINT0); + isif_write(isif->isif_cfg.base_addr, + vert_start & START_VER_ONE_MASK, SLV0); + isif_write(isif->isif_cfg.base_addr, + vert_start & START_VER_TWO_MASK, SLV1); + isif_write(isif->isif_cfg.base_addr, + vert_nr_lines & NUM_LINES_VER, LNV); +} + +#define DM365_ISIF_DFCMWR_MEMORY_WRITE 1 +#define DM365_ISIF_DFCMRD_MEMORY_READ 0x2 + +static void +isif_config_dfc(struct vpfe_isif_device *isif, struct vpfe_isif_dfc *vdfc) +{ +#define DFC_WRITE_WAIT_COUNT 1000 + u32 count = DFC_WRITE_WAIT_COUNT; + u32 val; + int i; + + if (!vdfc->en) + return; + + /* Correction mode */ + val = (vdfc->corr_mode & ISIF_VDFC_CORR_MOD_MASK) << + ISIF_VDFC_CORR_MOD_SHIFT; + + /* Correct whole line or partial */ + if (vdfc->corr_whole_line) + val |= 1 << ISIF_VDFC_CORR_WHOLE_LN_SHIFT; + + /* level shift value */ + val |= (vdfc->def_level_shift & ISIF_VDFC_LEVEL_SHFT_MASK) << + ISIF_VDFC_LEVEL_SHFT_SHIFT; + + isif_write(isif->isif_cfg.base_addr, val, DFCCTL); + + /* Defect saturation level */ + val = vdfc->def_sat_level & ISIF_VDFC_SAT_LEVEL_MASK; + isif_write(isif->isif_cfg.base_addr, val, VDFSATLV); + + isif_write(isif->isif_cfg.base_addr, vdfc->table[0].pos_vert & + ISIF_VDFC_POS_MASK, DFCMEM0); + isif_write(isif->isif_cfg.base_addr, vdfc->table[0].pos_horz & + ISIF_VDFC_POS_MASK, DFCMEM1); + if (vdfc->corr_mode == VPFE_ISIF_VDFC_NORMAL || + vdfc->corr_mode == VPFE_ISIF_VDFC_HORZ_INTERPOL_IF_SAT) { + isif_write(isif->isif_cfg.base_addr, + vdfc->table[0].level_at_pos, DFCMEM2); + isif_write(isif->isif_cfg.base_addr, + vdfc->table[0].level_up_pixels, DFCMEM3); + isif_write(isif->isif_cfg.base_addr, + vdfc->table[0].level_low_pixels, DFCMEM4); + } + + val = isif_read(isif->isif_cfg.base_addr, DFCMEMCTL); + /* set DFCMARST and set DFCMWR */ + val |= 1 << ISIF_DFCMEMCTL_DFCMARST_SHIFT; + val |= 1; + isif_write(isif->isif_cfg.base_addr, val, DFCMEMCTL); + + while (count && (isif_read(isif->isif_cfg.base_addr, DFCMEMCTL) & 0x01)) + count--; + + val = isif_read(isif->isif_cfg.base_addr, DFCMEMCTL); + if (!count) { + pr_debug("defect table write timeout !!\n"); + return; + } + + for (i = 1; i < vdfc->num_vdefects; i++) { + isif_write(isif->isif_cfg.base_addr, vdfc->table[i].pos_vert & + ISIF_VDFC_POS_MASK, DFCMEM0); + + isif_write(isif->isif_cfg.base_addr, vdfc->table[i].pos_horz & + ISIF_VDFC_POS_MASK, DFCMEM1); + + if (vdfc->corr_mode == VPFE_ISIF_VDFC_NORMAL || + vdfc->corr_mode == VPFE_ISIF_VDFC_HORZ_INTERPOL_IF_SAT) { + isif_write(isif->isif_cfg.base_addr, + vdfc->table[i].level_at_pos, DFCMEM2); + isif_write(isif->isif_cfg.base_addr, + vdfc->table[i].level_up_pixels, DFCMEM3); + isif_write(isif->isif_cfg.base_addr, + vdfc->table[i].level_low_pixels, DFCMEM4); + } + val = isif_read(isif->isif_cfg.base_addr, DFCMEMCTL); + /* clear DFCMARST and set DFCMWR */ + val &= ~(1 << ISIF_DFCMEMCTL_DFCMARST_SHIFT); + val |= 1; + isif_write(isif->isif_cfg.base_addr, val, DFCMEMCTL); + + count = DFC_WRITE_WAIT_COUNT; + while (count && (isif_read(isif->isif_cfg.base_addr, + DFCMEMCTL) & 0x01)) + count--; + + val = isif_read(isif->isif_cfg.base_addr, DFCMEMCTL); + if (!count) { + pr_debug("defect table write timeout !!\n"); + return; + } + } + if (vdfc->num_vdefects < VPFE_ISIF_VDFC_TABLE_SIZE) { + /* Extra cycle needed */ + isif_write(isif->isif_cfg.base_addr, 0, DFCMEM0); + isif_write(isif->isif_cfg.base_addr, + DM365_ISIF_MAX_DFCMEM1, DFCMEM1); + isif_write(isif->isif_cfg.base_addr, + DM365_ISIF_DFCMWR_MEMORY_WRITE, DFCMEMCTL); + } + /* enable VDFC */ + isif_merge(isif->isif_cfg.base_addr, (1 << ISIF_VDFC_EN_SHIFT), + (1 << ISIF_VDFC_EN_SHIFT), DFCCTL); + + isif_merge(isif->isif_cfg.base_addr, (1 << ISIF_VDFC_EN_SHIFT), + (0 << ISIF_VDFC_EN_SHIFT), DFCCTL); + + isif_write(isif->isif_cfg.base_addr, 0x6, DFCMEMCTL); + for (i = 0 ; i < vdfc->num_vdefects; i++) { + count = DFC_WRITE_WAIT_COUNT; + while (count && + (isif_read(isif->isif_cfg.base_addr, DFCMEMCTL) & 0x2)) + count--; + val = isif_read(isif->isif_cfg.base_addr, DFCMEMCTL); + if (!count) { + pr_debug("defect table write timeout !!\n"); + return; + } + isif_write(isif->isif_cfg.base_addr, + DM365_ISIF_DFCMRD_MEMORY_READ, DFCMEMCTL); + } +} + +static void +isif_config_csc(struct vpfe_isif_device *isif, struct vpfe_isif_df_csc *df_csc) +{ + u32 val1; + u32 val2; + u32 i; + + if (!df_csc->csc.en) { + isif_write(isif->isif_cfg.base_addr, 0, CSCCTL); + return; + } + /* initialize all bits to 0 */ + val1 = 0; + for (i = 0; i < VPFE_ISIF_CSC_NUM_COEFF; i++) { + if ((i % 2) == 0) { + /* CSCM - LSB */ + val1 = ((df_csc->csc.coeff[i].integer & + ISIF_CSC_COEF_INTEG_MASK) << + ISIF_CSC_COEF_INTEG_SHIFT) | + ((df_csc->csc.coeff[i].decimal & + ISIF_CSC_COEF_DECIMAL_MASK)); + } else { + + /* CSCM - MSB */ + val2 = ((df_csc->csc.coeff[i].integer & + ISIF_CSC_COEF_INTEG_MASK) << + ISIF_CSC_COEF_INTEG_SHIFT) | + ((df_csc->csc.coeff[i].decimal & + ISIF_CSC_COEF_DECIMAL_MASK)); + val2 <<= ISIF_CSCM_MSB_SHIFT; + val2 |= val1; + isif_write(isif->isif_cfg.base_addr, val2, + (CSCM0 + ((i-1) << 1))); + } + } + /* program the active area */ + isif_write(isif->isif_cfg.base_addr, df_csc->start_pix & + ISIF_DF_CSC_SPH_MASK, FMTSPH); + /* + * one extra pixel as required for CSC. Actually number of + * pixel - 1 should be configured in this register. So we + * need to subtract 1 before writing to FMTSPH, but we will + * not do this since csc requires one extra pixel + */ + isif_write(isif->isif_cfg.base_addr, df_csc->num_pixels & + ISIF_DF_CSC_SPH_MASK, FMTLNH); + isif_write(isif->isif_cfg.base_addr, df_csc->start_line & + ISIF_DF_CSC_SPH_MASK, FMTSLV); + /* + * one extra line as required for CSC. See reason documented for + * num_pixels + */ + isif_write(isif->isif_cfg.base_addr, df_csc->num_lines & + ISIF_DF_CSC_SPH_MASK, FMTLNV); + /* Enable CSC */ + isif_write(isif->isif_cfg.base_addr, 1, CSCCTL); +} + +static void +isif_config_linearization(struct vpfe_isif_device *isif, + struct vpfe_isif_linearize *linearize) +{ + u32 val; + u32 i; + + if (!linearize->en) { + isif_write(isif->isif_cfg.base_addr, 0, LINCFG0); + return; + } + /* shift value for correction */ + val = (linearize->corr_shft & ISIF_LIN_CORRSFT_MASK) << + ISIF_LIN_CORRSFT_SHIFT; + /* enable */ + val |= 1; + isif_write(isif->isif_cfg.base_addr, val, LINCFG0); + /* Scale factor */ + val = (linearize->scale_fact.integer & 1) << + ISIF_LIN_SCALE_FACT_INTEG_SHIFT; + val |= linearize->scale_fact.decimal & ISIF_LIN_SCALE_FACT_DECIMAL_MASK; + isif_write(isif->isif_cfg.base_addr, val, LINCFG1); + + for (i = 0; i < VPFE_ISIF_LINEAR_TAB_SIZE; i++) { + val = linearize->table[i] & ISIF_LIN_ENTRY_MASK; + if (i%2) + isif_regw_lin_tbl(isif, val, ((i >> 1) << 2), 1); + else + isif_regw_lin_tbl(isif, val, ((i >> 1) << 2), 0); + } +} + +static void +isif_config_culling(struct vpfe_isif_device *isif, struct vpfe_isif_cul *cul) +{ + u32 val; + + /* Horizontal pattern */ + val = cul->hcpat_even << CULL_PAT_EVEN_LINE_SHIFT; + val |= cul->hcpat_odd; + isif_write(isif->isif_cfg.base_addr, val, CULH); + /* vertical pattern */ + isif_write(isif->isif_cfg.base_addr, cul->vcpat, CULV); + /* LPF */ + isif_merge(isif->isif_cfg.base_addr, ISIF_LPF_MASK << ISIF_LPF_SHIFT, + cul->en_lpf << ISIF_LPF_SHIFT, MODESET); +} + +static int isif_get_pix_fmt(u32 mbus_code) +{ + switch (mbus_code) { + case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8: + case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: + case V4L2_MBUS_FMT_SGRBG12_1X12: + return ISIF_PIXFMT_RAW; + + case V4L2_MBUS_FMT_YUYV8_2X8: + case V4L2_MBUS_FMT_UYVY8_2X8: + case V4L2_MBUS_FMT_YUYV10_2X10: + case V4L2_MBUS_FMT_Y8_1X8: + return ISIF_PIXFMT_YCBCR_8BIT; + + case V4L2_MBUS_FMT_YUYV8_1X16: + case V4L2_MBUS_FMT_YUYV10_1X20: + return ISIF_PIXFMT_YCBCR_16BIT; + + default: + break; + } + return -EINVAL; +} + +#define ISIF_INTERLACE_INVERSE_MODE 0x4b6d +#define ISIF_INTERLACE_NON_INVERSE_MODE 0x0b6d +#define ISIF_PROGRESSIVE_INVERSE_MODE 0x4000 +#define ISIF_PROGRESSIVE_NON_INVERSE_MODE 0x0000 + +static int isif_config_raw(struct v4l2_subdev *sd, int mode) +{ + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + struct isif_params_raw *params = &isif->isif_cfg.bayer; + struct vpfe_isif_raw_config *module_params = + &isif->isif_cfg.bayer.config_params; + struct v4l2_mbus_framefmt *format; + int pix_fmt; + u32 val; + + format = &isif->formats[ISIF_PAD_SINK]; + + /* In case of user has set BT656IF earlier, it should be reset + * when configuring for raw input. + */ + isif_write(isif->isif_cfg.base_addr, 0, REC656IF); + /* Configure CCDCFG register + * Set CCD Not to swap input since input is RAW data + * Set FID detection function to Latch at V-Sync + * Set WENLOG - isif valid area + * Set TRGSEL + * Set EXTRG + * Packed to 8 or 16 bits + */ + val = ISIF_YCINSWP_RAW | ISIF_CCDCFG_FIDMD_LATCH_VSYNC | + ISIF_CCDCFG_WENLOG_AND | ISIF_CCDCFG_TRGSEL_WEN | + ISIF_CCDCFG_EXTRG_DISABLE | (isif->isif_cfg.data_pack & + ISIF_DATA_PACK_MASK); + isif_write(isif->isif_cfg.base_addr, val, CCDCFG); + + pix_fmt = isif_get_pix_fmt(format->code); + if (pix_fmt < 0) { + pr_debug("Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + /* + * Configure the vertical sync polarity(MODESET.VDPOL) + * Configure the horizontal sync polarity (MODESET.HDPOL) + * Configure frame id polarity (MODESET.FLDPOL) + * Configure data polarity + * Configure External WEN Selection + * Configure frame format(progressive or interlace) + * Configure pixel format (Input mode) + * Configure the data shift + */ + val = ISIF_VDHDOUT_INPUT | ((params->vd_pol & ISIF_VD_POL_MASK) << + ISIF_VD_POL_SHIFT) | ((params->hd_pol & ISIF_HD_POL_MASK) << + ISIF_HD_POL_SHIFT) | ((params->fid_pol & ISIF_FID_POL_MASK) << + ISIF_FID_POL_SHIFT) | ((ISIF_DATAPOL_NORMAL & + ISIF_DATAPOL_MASK) << ISIF_DATAPOL_SHIFT) | ((ISIF_EXWEN_DISABLE & + ISIF_EXWEN_MASK) << ISIF_EXWEN_SHIFT) | ((params->frm_fmt & + ISIF_FRM_FMT_MASK) << ISIF_FRM_FMT_SHIFT) | ((pix_fmt & + ISIF_INPUT_MASK) << ISIF_INPUT_SHIFT); + + /* currently only V4L2_MBUS_FMT_SGRBG12_1X12 is + * supported. shift appropriately depending on + * different MBUS fmt's added + */ + if (format->code == V4L2_MBUS_FMT_SGRBG12_1X12) + val |= ((VPFE_ISIF_NO_SHIFT & + ISIF_DATASFT_MASK) << ISIF_DATASFT_SHIFT); + + isif_write(isif->isif_cfg.base_addr, val, MODESET); + /* + * Configure GAMMAWD register + * CFA pattern setting + */ + val = (params->cfa_pat & ISIF_GAMMAWD_CFA_MASK) << + ISIF_GAMMAWD_CFA_SHIFT; + /* Gamma msb */ + if (params->v4l2_pix_fmt == V4L2_PIX_FMT_SGRBG10ALAW8) + val = val | ISIF_ALAW_ENABLE; + + val = val | ((params->data_msb & ISIF_ALAW_GAMA_WD_MASK) << + ISIF_ALAW_GAMA_WD_SHIFT); + + isif_write(isif->isif_cfg.base_addr, val, CGAMMAWD); + /* Configure DPCM compression settings */ + if (params->v4l2_pix_fmt == V4L2_PIX_FMT_SGRBG10DPCM8) { + val = 1 << ISIF_DPCM_EN_SHIFT; + val |= (params->dpcm_predictor & + ISIF_DPCM_PREDICTOR_MASK) << ISIF_DPCM_PREDICTOR_SHIFT; + } + isif_write(isif->isif_cfg.base_addr, val, MISC); + /* Configure Gain & Offset */ + isif_config_gain_offset(isif); + /* Configure Color pattern */ + if (format->code == V4L2_MBUS_FMT_SGRBG12_1X12) + val = isif_sgrbg_pattern; + else + /* default set to rggb */ + val = isif_srggb_pattern; + + isif_write(isif->isif_cfg.base_addr, val, CCOLP); + + /* Configure HSIZE register */ + val = (params->horz_flip_en & ISIF_HSIZE_FLIP_MASK) << + ISIF_HSIZE_FLIP_SHIFT; + + /* calculate line offset in 32 bytes based on pack value */ + if (isif->isif_cfg.data_pack == ISIF_PACK_8BIT) + val |= ((params->win.width + 31) >> 5) & ISIF_LINEOFST_MASK; + else if (isif->isif_cfg.data_pack == ISIF_PACK_12BIT) + val |= ((((params->win.width + (params->win.width >> 2)) + + 31) >> 5) & ISIF_LINEOFST_MASK); + else + val |= (((params->win.width * 2) + 31) >> 5) & + ISIF_LINEOFST_MASK; + isif_write(isif->isif_cfg.base_addr, val, HSIZE); + /* Configure SDOFST register */ + if (params->frm_fmt == ISIF_FRMFMT_INTERLACED) { + if (params->image_invert_en) + /* For interlace inverse mode */ + isif_write(isif->isif_cfg.base_addr, + ISIF_INTERLACE_INVERSE_MODE, SDOFST); + else + /* For interlace non inverse mode */ + isif_write(isif->isif_cfg.base_addr, + ISIF_INTERLACE_NON_INVERSE_MODE, SDOFST); + } else if (params->frm_fmt == ISIF_FRMFMT_PROGRESSIVE) { + if (params->image_invert_en) + isif_write(isif->isif_cfg.base_addr, + ISIF_PROGRESSIVE_INVERSE_MODE, SDOFST); + else + /* For progessive non inverse mode */ + isif_write(isif->isif_cfg.base_addr, + ISIF_PROGRESSIVE_NON_INVERSE_MODE, SDOFST); + } + /* Configure video window */ + isif_setwin(isif, ¶ms->win, params->frm_fmt, 1, mode); + /* Configure Black Clamp */ + isif_config_bclamp(isif, &module_params->bclamp); + /* Configure Vertical Defection Pixel Correction */ + isif_config_dfc(isif, &module_params->dfc); + if (!module_params->df_csc.df_or_csc) + /* Configure Color Space Conversion */ + isif_config_csc(isif, &module_params->df_csc); + + isif_config_linearization(isif, &module_params->linearize); + /* Configure Culling */ + isif_config_culling(isif, &module_params->culling); + /* Configure Horizontal and vertical offsets(DFC,LSC,Gain) */ + val = module_params->horz_offset & ISIF_DATA_H_OFFSET_MASK; + isif_write(isif->isif_cfg.base_addr, val, DATAHOFST); + + val = module_params->vert_offset & ISIF_DATA_V_OFFSET_MASK; + isif_write(isif->isif_cfg.base_addr, val, DATAVOFST); + + return 0; +} + +#define DM365_ISIF_HSIZE_MASK 0xffffffe0 +#define DM365_ISIF_SDOFST_2_LINES 0x00000249 + +/* This function will configure ISIF for YCbCr parameters. */ +static int isif_config_ycbcr(struct v4l2_subdev *sd, int mode) +{ + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + struct isif_ycbcr_config *params = &isif->isif_cfg.ycbcr; + struct v4l2_mbus_framefmt *format; + int pix_fmt; + u32 modeset; + u32 ccdcfg; + + format = &isif->formats[ISIF_PAD_SINK]; + /* + * first reset the ISIF + * all registers have default values after reset + * This is important since we assume default values to be set in + * a lot of registers that we didn't touch + */ + /* start with all bits zero */ + ccdcfg = modeset = 0; + pix_fmt = isif_get_pix_fmt(format->code); + if (pix_fmt < 0) { + pr_debug("Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + /* configure pixel format or input mode */ + modeset = modeset | ((pix_fmt & ISIF_INPUT_MASK) << + ISIF_INPUT_SHIFT) | ((params->frm_fmt & ISIF_FRM_FMT_MASK) << + ISIF_FRM_FMT_SHIFT) | (((params->fid_pol & + ISIF_FID_POL_MASK) << ISIF_FID_POL_SHIFT)) | + (((params->hd_pol & ISIF_HD_POL_MASK) << ISIF_HD_POL_SHIFT)) | + (((params->vd_pol & ISIF_VD_POL_MASK) << ISIF_VD_POL_SHIFT)); + /* pack the data to 8-bit CCDCCFG */ + switch (format->code) { + case V4L2_MBUS_FMT_YUYV8_2X8: + case V4L2_MBUS_FMT_UYVY8_2X8: + if (pix_fmt != ISIF_PIXFMT_YCBCR_8BIT) { + pr_debug("Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + modeset |= ((VPFE_PINPOL_NEGATIVE & ISIF_VD_POL_MASK) << + ISIF_VD_POL_SHIFT); + isif_write(isif->isif_cfg.base_addr, 3, REC656IF); + ccdcfg = ccdcfg | ISIF_PACK_8BIT | ISIF_YCINSWP_YCBCR; + break; + + case V4L2_MBUS_FMT_YUYV10_2X10: + if (pix_fmt != ISIF_PIXFMT_YCBCR_8BIT) { + pr_debug("Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + /* setup BT.656, embedded sync */ + isif_write(isif->isif_cfg.base_addr, 3, REC656IF); + /* enable 10 bit mode in ccdcfg */ + ccdcfg = ccdcfg | ISIF_PACK_8BIT | ISIF_YCINSWP_YCBCR | + ISIF_BW656_ENABLE; + break; + + case V4L2_MBUS_FMT_YUYV10_1X20: + if (pix_fmt != ISIF_PIXFMT_YCBCR_16BIT) { + pr_debug("Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + isif_write(isif->isif_cfg.base_addr, 3, REC656IF); + break; + + case V4L2_MBUS_FMT_Y8_1X8: + ccdcfg |= ISIF_PACK_8BIT; + ccdcfg |= ISIF_YCINSWP_YCBCR; + if (pix_fmt != ISIF_PIXFMT_YCBCR_8BIT) { + pr_debug("Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + break; + + case V4L2_MBUS_FMT_YUYV8_1X16: + if (pix_fmt != ISIF_PIXFMT_YCBCR_16BIT) { + pr_debug("Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + break; + + default: + /* should never come here */ + pr_debug("Invalid interface type\n"); + return -EINVAL; + } + isif_write(isif->isif_cfg.base_addr, modeset, MODESET); + /* Set up pix order */ + ccdcfg |= (params->pix_order & ISIF_PIX_ORDER_MASK) << + ISIF_PIX_ORDER_SHIFT; + isif_write(isif->isif_cfg.base_addr, ccdcfg, CCDCFG); + /* configure video window */ + if (format->code == V4L2_MBUS_FMT_YUYV10_1X20 || + format->code == V4L2_MBUS_FMT_YUYV8_1X16) + isif_setwin(isif, ¶ms->win, params->frm_fmt, 1, mode); + else + isif_setwin(isif, ¶ms->win, params->frm_fmt, 2, mode); + + /* + * configure the horizontal line offset + * this is done by rounding up width to a multiple of 16 pixels + * and multiply by two to account for y:cb:cr 4:2:2 data + */ + isif_write(isif->isif_cfg.base_addr, + ((((params->win.width * 2) + 31) & + DM365_ISIF_HSIZE_MASK) >> 5), HSIZE); + + /* configure the memory line offset */ + if (params->frm_fmt == ISIF_FRMFMT_INTERLACED && + params->buf_type == ISIF_BUFTYPE_FLD_INTERLEAVED) + /* two fields are interleaved in memory */ + isif_write(isif->isif_cfg.base_addr, + DM365_ISIF_SDOFST_2_LINES, SDOFST); + return 0; +} + +static int isif_configure(struct v4l2_subdev *sd, int mode) +{ + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = &isif->formats[ISIF_PAD_SINK]; + + switch (format->code) { + case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8: + case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: + case V4L2_MBUS_FMT_SGRBG12_1X12: + return isif_config_raw(sd, mode); + + case V4L2_MBUS_FMT_YUYV8_2X8: + case V4L2_MBUS_FMT_UYVY8_2X8: + case V4L2_MBUS_FMT_YUYV10_2X10: + case V4L2_MBUS_FMT_Y8_1X8: + case V4L2_MBUS_FMT_YUYV8_1X16: + case V4L2_MBUS_FMT_YUYV10_1X20: + return isif_config_ycbcr(sd, mode); + + default: + break; + } + return -EINVAL; +} + +/* + * isif_set_stream() - Enable/Disable streaming on the ISIF module + * @sd: VPFE ISIF V4L2 subdevice + * @enable: Enable/disable stream + */ +static int isif_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + int ret; + + if (enable) { + ret = isif_configure(sd, + (isif->output == ISIF_OUTPUT_MEMORY) ? 0 : 1); + if (ret) + return ret; + if (isif->output == ISIF_OUTPUT_MEMORY) + isif_enable_output_to_sdram(isif, 1); + isif_enable(isif, 1); + } else { + isif_enable(isif, 0); + isif_enable_output_to_sdram(isif, 0); + } + + return 0; +} + +/* + * __isif_get_format() - helper function for getting isif format + * @isif: pointer to isif private structure. + * @pad: pad number. + * @fh: V4L2 subdev file handle. + * @which: wanted subdev format. + */ +static struct v4l2_mbus_framefmt * +__isif_get_format(struct vpfe_isif_device *isif, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_subdev_format fmt; + + fmt.pad = pad; + fmt.which = which; + + return v4l2_subdev_get_try_format(fh, pad); + } + return &isif->formats[pad]; +} + +/* +* isif_set_format() - set format on pad +* @sd : VPFE ISIF device +* @fh : V4L2 subdev file handle +* @fmt : pointer to v4l2 subdev format structure +* +* Return 0 on success or -EINVAL if format or pad is invalid +*/ +static int +isif_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + struct vpfe_device *vpfe_dev = to_vpfe_device(isif); + struct v4l2_mbus_framefmt *format; + + format = __isif_get_format(isif, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + isif_try_format(isif, fh, fmt); + memcpy(format, &fmt->format, sizeof(*format)); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + if (fmt->pad == ISIF_PAD_SOURCE) + return isif_config_format(vpfe_dev, fmt->pad); + + return 0; +} + +/* + * isif_get_format() - Retrieve the video format on a pad + * @sd: VPFE ISIF V4L2 subdevice + * @fh: V4L2 subdev file handle + * @fmt: pointer to v4l2 subdev format structure + * + * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond + * to the format type. + */ +static int +isif_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_isif_device *vpfe_isif = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __isif_get_format(vpfe_isif, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + memcpy(&fmt->format, format, sizeof(fmt->format)); + + return 0; +} + +/* + * isif_enum_frame_size() - enum frame sizes on pads + * @sd: VPFE isif V4L2 subdevice + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_frame_size_enum structure + */ +static int +isif_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + struct v4l2_subdev_format format; + + if (fse->index != 0) + return -EINVAL; + + format.pad = fse->pad; + format.format.code = fse->code; + format.format.width = 1; + format.format.height = 1; + format.which = V4L2_SUBDEV_FORMAT_TRY; + isif_try_format(isif, fh, &format); + fse->min_width = format.format.width; + fse->min_height = format.format.height; + + if (format.format.code != fse->code) + return -EINVAL; + + format.pad = fse->pad; + format.format.code = fse->code; + format.format.width = -1; + format.format.height = -1; + format.which = V4L2_SUBDEV_FORMAT_TRY; + isif_try_format(isif, fh, &format); + fse->max_width = format.format.width; + fse->max_height = format.format.height; + + return 0; +} + +/* + * isif_enum_mbus_code() - enum mbus codes for pads + * @sd: VPFE isif V4L2 subdevice + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + */ +static int +isif_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + switch (code->pad) { + case ISIF_PAD_SINK: + case ISIF_PAD_SOURCE: + if (code->index >= ARRAY_SIZE(isif_fmts)) + return -EINVAL; + code->code = isif_fmts[code->index]; + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* + * isif_pad_set_crop() - set crop rectangle on pad + * @sd: VPFE isif V4L2 subdevice + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + * + * Return 0 on success, -EINVAL if pad is invalid + */ +static int +isif_pad_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct vpfe_isif_device *vpfe_isif = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + /* check wether its a valid pad */ + if (crop->pad != ISIF_PAD_SINK) + return -EINVAL; + + format = __isif_get_format(vpfe_isif, fh, crop->pad, crop->which); + if (format == NULL) + return -EINVAL; + + /* check wether crop rect is within limits */ + if (crop->rect.top < 0 || crop->rect.left < 0 || + (crop->rect.left + crop->rect.width > + vpfe_isif->formats[ISIF_PAD_SINK].width) || + (crop->rect.top + crop->rect.height > + vpfe_isif->formats[ISIF_PAD_SINK].height)) { + crop->rect.left = 0; + crop->rect.top = 0; + crop->rect.width = format->width; + crop->rect.height = format->height; + } + /* adjust the width to 16 pixel boundry */ + crop->rect.width = ((crop->rect.width + 15) & ~0xf); + vpfe_isif->crop = crop->rect; + if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + isif_set_image_window(vpfe_isif); + } else { + struct v4l2_rect *rect; + + rect = v4l2_subdev_get_try_crop(fh, ISIF_PAD_SINK); + memcpy(rect, &vpfe_isif->crop, sizeof(*rect)); + } + return 0; +} + +/* + * isif_pad_get_crop() - get crop rectangle on pad + * @sd: VPFE isif V4L2 subdevice + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + * + * Return 0 on success, -EINVAL if pad is invalid + */ +static int +isif_pad_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct vpfe_isif_device *vpfe_isif = v4l2_get_subdevdata(sd); + + /* check wether its a valid pad */ + if (crop->pad != ISIF_PAD_SINK) + return -EINVAL; + + if (crop->which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_rect *rect; + rect = v4l2_subdev_get_try_crop(fh, ISIF_PAD_SINK); + memcpy(&crop->rect, rect, sizeof(*rect)); + } else { + crop->rect = vpfe_isif->crop; + } + + return 0; +} + +/* + * isif_init_formats() - Initialize formats on all pads + * @sd: VPFE isif V4L2 subdevice + * @fh: V4L2 subdev file handle + * + * Initialize all pad formats with default values. If fh is not NULL, try + * formats are initialized on the file handle. Otherwise active formats are + * initialized on the device. + */ +static int +isif_init_formats(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct v4l2_subdev_format format; + struct v4l2_subdev_crop crop; + + memset(&format, 0, sizeof(format)); + format.pad = ISIF_PAD_SINK; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_SGRBG12_1X12; + format.format.width = MAX_WIDTH; + format.format.height = MAX_HEIGHT; + isif_set_format(sd, fh, &format); + + memset(&format, 0, sizeof(format)); + format.pad = ISIF_PAD_SOURCE; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_SGRBG12_1X12; + format.format.width = MAX_WIDTH; + format.format.height = MAX_HEIGHT; + isif_set_format(sd, fh, &format); + + memset(&crop, 0, sizeof(crop)); + crop.pad = ISIF_PAD_SINK; + crop.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + crop.rect.width = MAX_WIDTH; + crop.rect.height = MAX_HEIGHT; + isif_pad_set_crop(sd, fh, &crop); + + return 0; +} + +/* subdev core operations */ +static const struct v4l2_subdev_core_ops isif_v4l2_core_ops = { + .ioctl = isif_ioctl, +}; + +/* subdev file operations */ +static const struct v4l2_subdev_internal_ops isif_v4l2_internal_ops = { + .open = isif_init_formats, +}; + +/* subdev video operations */ +static const struct v4l2_subdev_video_ops isif_v4l2_video_ops = { + .s_stream = isif_set_stream, +}; + +/* subdev pad operations */ +static const struct v4l2_subdev_pad_ops isif_v4l2_pad_ops = { + .enum_mbus_code = isif_enum_mbus_code, + .enum_frame_size = isif_enum_frame_size, + .get_fmt = isif_get_format, + .set_fmt = isif_set_format, + .set_crop = isif_pad_set_crop, + .get_crop = isif_pad_get_crop, +}; + +/* subdev operations */ +static const struct v4l2_subdev_ops isif_v4l2_ops = { + .core = &isif_v4l2_core_ops, + .video = &isif_v4l2_video_ops, + .pad = &isif_v4l2_pad_ops, +}; + +/* + * Media entity operations + */ + +/* + * isif_link_setup() - Setup isif connections + * @entity: isif media entity + * @local: Pad at the local end of the link + * @remote: Pad at the remote end of the link + * @flags: Link flags + * + * return -EINVAL or zero on success + */ +static int +isif_link_setup(struct media_entity *entity, const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + + switch (local->index | media_entity_type(remote->entity)) { + case ISIF_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + /* read from decoder/sensor */ + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + isif->input = ISIF_INPUT_NONE; + break; + } + if (isif->input != ISIF_INPUT_NONE) + return -EBUSY; + isif->input = ISIF_INPUT_PARALLEL; + break; + + case ISIF_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + /* write to memory */ + if (flags & MEDIA_LNK_FL_ENABLED) + isif->output = ISIF_OUTPUT_MEMORY; + else + isif->output = ISIF_OUTPUT_NONE; + break; + + case ISIF_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + if (flags & MEDIA_LNK_FL_ENABLED) + isif->output = ISIF_OUTPUT_IPIPEIF; + else + isif->output = ISIF_OUTPUT_NONE; + break; + + default: + return -EINVAL; + } + + return 0; +} +static const struct media_entity_operations isif_media_ops = { + .link_setup = isif_link_setup, +}; + +/* + * vpfe_isif_unregister_entities() - isif unregister entity + * @isif - pointer to isif subdevice structure. + */ +void vpfe_isif_unregister_entities(struct vpfe_isif_device *isif) +{ + vpfe_video_unregister(&isif->video_out); + /* cleanup entity */ + media_entity_cleanup(&isif->subdev.entity); + /* unregister subdev */ + v4l2_device_unregister_subdev(&isif->subdev); +} + +static void isif_restore_defaults(struct vpfe_isif_device *isif) +{ + enum vpss_ccdc_source_sel source = VPSS_CCDCIN; + int i; + + memset(&isif->isif_cfg.bayer.config_params, 0, + sizeof(struct vpfe_isif_raw_config)); + + isif->isif_cfg.bayer.config_params.linearize.corr_shft = + VPFE_ISIF_NO_SHIFT; + isif->isif_cfg.bayer.config_params.linearize.scale_fact.integer = 1; + isif->isif_cfg.bayer.config_params.culling.hcpat_odd = + ISIF_CULLING_HCAPT_ODD; + isif->isif_cfg.bayer.config_params.culling.hcpat_even = + ISIF_CULLING_HCAPT_EVEN; + isif->isif_cfg.bayer.config_params.culling.vcpat = ISIF_CULLING_VCAPT; + /* Enable clock to ISIF, IPIPEIF and BL */ + vpss_enable_clock(VPSS_CCDC_CLOCK, 1); + vpss_enable_clock(VPSS_IPIPEIF_CLOCK, 1); + vpss_enable_clock(VPSS_BL_CLOCK, 1); + + /* set all registers to default value */ + for (i = 0; i <= 0x1f8; i += 4) + isif_write(isif->isif_cfg.base_addr, 0, i); + /* no culling support */ + isif_write(isif->isif_cfg.base_addr, 0xffff, CULH); + isif_write(isif->isif_cfg.base_addr, 0xff, CULV); + + /* Set default offset and gain */ + isif_config_gain_offset(isif); + vpss_select_ccdc_source(source); +} + +/* + * vpfe_isif_register_entities() - isif register entity + * @isif - pointer to isif subdevice structure. + * @vdev: pointer to v4l2 device structure. + */ +int vpfe_isif_register_entities(struct vpfe_isif_device *isif, + struct v4l2_device *vdev) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(isif); + unsigned int flags; + int ret; + + /* Register the subdev */ + ret = v4l2_device_register_subdev(vdev, &isif->subdev); + if (ret < 0) + return ret; + + isif_restore_defaults(isif); + ret = vpfe_video_register(&isif->video_out, vdev); + if (ret) { + pr_err("Failed to register isif video out device\n"); + goto out_video_register; + } + isif->video_out.vpfe_dev = vpfe_dev; + flags = 0; + /* connect isif to video node */ + ret = media_entity_create_link(&isif->subdev.entity, 1, + &isif->video_out.video_dev.entity, + 0, flags); + if (ret < 0) + goto out_create_link; + return 0; +out_create_link: + vpfe_video_unregister(&isif->video_out); +out_video_register: + v4l2_device_unregister_subdev(&isif->subdev); + return ret; +} + +/* ------------------------------------------------------------------- + * V4L2 subdev control operations + */ + +static int vpfe_isif_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vpfe_isif_device *isif = + container_of(ctrl->handler, struct vpfe_isif_device, ctrls); + struct isif_oper_config *config = &isif->isif_cfg; + + switch (ctrl->id) { + case VPFE_CID_DPCM_PREDICTOR: + config->bayer.dpcm_predictor = ctrl->val; + break; + + case VPFE_ISIF_CID_CRGAIN: + config->isif_gain_params.cr_gain = ctrl->val; + break; + + case VPFE_ISIF_CID_CGRGAIN: + config->isif_gain_params.cgr_gain = ctrl->val; + break; + + case VPFE_ISIF_CID_CGBGAIN: + config->isif_gain_params.cgb_gain = ctrl->val; + break; + + case VPFE_ISIF_CID_CBGAIN: + config->isif_gain_params.cb_gain = ctrl->val; + break; + + case VPFE_ISIF_CID_GAIN_OFFSET: + config->isif_gain_params.offset = ctrl->val; + break; + + default: + return -EINVAL; + } + return 0; +} + +static const struct v4l2_ctrl_ops vpfe_isif_ctrl_ops = { + .s_ctrl = vpfe_isif_s_ctrl, +}; + +static const struct v4l2_ctrl_config vpfe_isif_dpcm_pred = { + .ops = &vpfe_isif_ctrl_ops, + .id = VPFE_CID_DPCM_PREDICTOR, + .name = "DPCM Predictor", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 1, + .step = 1, + .def = 0, +}; + +static const struct v4l2_ctrl_config vpfe_isif_crgain = { + .ops = &vpfe_isif_ctrl_ops, + .id = VPFE_ISIF_CID_CRGAIN, + .name = "CRGAIN", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 12) - 1, + .step = 1, + .def = 0, +}; + +static const struct v4l2_ctrl_config vpfe_isif_cgrgain = { + .ops = &vpfe_isif_ctrl_ops, + .id = VPFE_ISIF_CID_CGRGAIN, + .name = "CGRGAIN", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 12) - 1, + .step = 1, + .def = 0, +}; + +static const struct v4l2_ctrl_config vpfe_isif_cgbgain = { + .ops = &vpfe_isif_ctrl_ops, + .id = VPFE_ISIF_CID_CGBGAIN, + .name = "CGBGAIN", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 12) - 1, + .step = 1, + .def = 0, +}; + +static const struct v4l2_ctrl_config vpfe_isif_cbgain = { + .ops = &vpfe_isif_ctrl_ops, + .id = VPFE_ISIF_CID_CBGAIN, + .name = "CBGAIN", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 12) - 1, + .step = 1, + .def = 0, +}; + +static const struct v4l2_ctrl_config vpfe_isif_gain_offset = { + .ops = &vpfe_isif_ctrl_ops, + .id = VPFE_ISIF_CID_GAIN_OFFSET, + .name = "Gain Offset", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 12) - 1, + .step = 1, + .def = 0, +}; + +static void isif_remove(struct vpfe_isif_device *isif, + struct platform_device *pdev) +{ + struct resource *res; + int i = 0; + + iounmap(isif->isif_cfg.base_addr); + iounmap(isif->isif_cfg.linear_tbl0_addr); + iounmap(isif->isif_cfg.linear_tbl1_addr); + + while (i < 3) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (res) + release_mem_region(res->start, + res->end - res->start + 1); + i++; + } +} + +static void isif_config_defaults(struct vpfe_isif_device *isif) +{ + isif->isif_cfg.ycbcr.v4l2_pix_fmt = V4L2_PIX_FMT_UYVY; + isif->isif_cfg.ycbcr.pix_fmt = ISIF_PIXFMT_YCBCR_8BIT; + isif->isif_cfg.ycbcr.frm_fmt = ISIF_FRMFMT_INTERLACED; + isif->isif_cfg.ycbcr.fid_pol = VPFE_PINPOL_POSITIVE; + isif->isif_cfg.ycbcr.vd_pol = VPFE_PINPOL_POSITIVE; + isif->isif_cfg.ycbcr.hd_pol = VPFE_PINPOL_POSITIVE; + isif->isif_cfg.ycbcr.pix_order = ISIF_PIXORDER_CBYCRY; + isif->isif_cfg.ycbcr.buf_type = ISIF_BUFTYPE_FLD_INTERLEAVED; + + isif->isif_cfg.bayer.v4l2_pix_fmt = V4L2_PIX_FMT_SGRBG10ALAW8; + isif->isif_cfg.bayer.pix_fmt = ISIF_PIXFMT_RAW; + isif->isif_cfg.bayer.frm_fmt = ISIF_FRMFMT_PROGRESSIVE; + isif->isif_cfg.bayer.fid_pol = VPFE_PINPOL_POSITIVE; + isif->isif_cfg.bayer.vd_pol = VPFE_PINPOL_POSITIVE; + isif->isif_cfg.bayer.hd_pol = VPFE_PINPOL_POSITIVE; + isif->isif_cfg.bayer.cfa_pat = ISIF_CFA_PAT_MOSAIC; + isif->isif_cfg.bayer.data_msb = ISIF_BIT_MSB_11; + isif->isif_cfg.data_pack = ISIF_PACK_8BIT; +} +/* + * vpfe_isif_init() - Initialize V4L2 subdev and media entity + * @isif: VPFE isif module + * @pdev: Pointer to platform device structure. + * Return 0 on success and a negative error code on failure. + */ +int vpfe_isif_init(struct vpfe_isif_device *isif, struct platform_device *pdev) +{ + struct v4l2_subdev *sd = &isif->subdev; + struct media_pad *pads = &isif->pads[0]; + struct media_entity *me = &sd->entity; + static resource_size_t res_len; + struct resource *res; + void *__iomem addr; + int status; + int i = 0; + + /* Get the ISIF base address, linearization table0 and table1 addr. */ + while (i < 3) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (!res) { + status = -ENOENT; + goto fail_nobase_res; + } + res_len = res->end - res->start + 1; + res = request_mem_region(res->start, res_len, res->name); + if (!res) { + status = -EBUSY; + goto fail_nobase_res; + } + addr = ioremap_nocache(res->start, res_len); + if (!addr) { + status = -EBUSY; + goto fail_base_iomap; + } + switch (i) { + case 0: + /* ISIF base address */ + isif->isif_cfg.base_addr = addr; + break; + case 1: + /* ISIF linear tbl0 address */ + isif->isif_cfg.linear_tbl0_addr = addr; + break; + default: + /* ISIF linear tbl0 address */ + isif->isif_cfg.linear_tbl1_addr = addr; + break; + } + i++; + } + davinci_cfg_reg(DM365_VIN_CAM_WEN); + davinci_cfg_reg(DM365_VIN_CAM_VD); + davinci_cfg_reg(DM365_VIN_CAM_HD); + davinci_cfg_reg(DM365_VIN_YIN4_7_EN); + davinci_cfg_reg(DM365_VIN_YIN0_3_EN); + + /* queue ops */ + isif->video_out.ops = &isif_video_ops; + v4l2_subdev_init(sd, &isif_v4l2_ops); + sd->internal_ops = &isif_v4l2_internal_ops; + strlcpy(sd->name, "DAVINCI ISIF", sizeof(sd->name)); + sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ + v4l2_set_subdevdata(sd, isif); + sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; + pads[ISIF_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[ISIF_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + isif->input = ISIF_INPUT_NONE; + isif->output = ISIF_OUTPUT_NONE; + me->ops = &isif_media_ops; + status = media_entity_init(me, ISIF_PADS_NUM, pads, 0); + if (status) + goto isif_fail; + isif->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + status = vpfe_video_init(&isif->video_out, "ISIF"); + if (status) { + pr_err("Failed to init isif-out video device\n"); + goto isif_fail; + } + v4l2_ctrl_handler_init(&isif->ctrls, 6); + v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_crgain, NULL); + v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_cgrgain, NULL); + v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_cgbgain, NULL); + v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_cbgain, NULL); + v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_gain_offset, NULL); + v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_dpcm_pred, NULL); + + v4l2_ctrl_handler_setup(&isif->ctrls); + sd->ctrl_handler = &isif->ctrls; + isif_config_defaults(isif); + return 0; +fail_base_iomap: + release_mem_region(res->start, res_len); + i--; +fail_nobase_res: + if (isif->isif_cfg.base_addr) + iounmap(isif->isif_cfg.base_addr); + if (isif->isif_cfg.linear_tbl0_addr) + iounmap(isif->isif_cfg.linear_tbl0_addr); + + while (i >= 0) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + release_mem_region(res->start, res_len); + i--; + } + return status; +isif_fail: + v4l2_ctrl_handler_free(&isif->ctrls); + isif_remove(isif, pdev); + return status; +} + +/* + * vpfe_isif_cleanup - isif module cleanup + * @isif: pointer to isif subdevice + * @dev: pointer to platform device structure + */ +void +vpfe_isif_cleanup(struct vpfe_isif_device *isif, struct platform_device *pdev) +{ + isif_remove(isif, pdev); +} diff --git a/drivers/staging/media/davinci_vpfe/dm365_isif.h b/drivers/staging/media/davinci_vpfe/dm365_isif.h new file mode 100644 index 000000000000..473fd2cfe350 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_isif.h @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + */ + +#ifndef _DAVINCI_VPFE_DM365_ISIF_H +#define _DAVINCI_VPFE_DM365_ISIF_H + +#include <linux/platform_device.h> + +#include <mach/mux.h> + +#include <media/davinci/vpfe_types.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> + +#include "davinci_vpfe_user.h" +#include "dm365_isif_regs.h" +#include "vpfe_video.h" + +#define ISIF_CULLING_HCAPT_ODD 0xff +#define ISIF_CULLING_HCAPT_EVEN 0xff +#define ISIF_CULLING_VCAPT 0xff + +#define ISIF_CADU_BITS 0x07ff +#define ISIF_CADL_BITS 0x0ffff + +enum isif_pixfmt { + ISIF_PIXFMT_RAW = 0, + ISIF_PIXFMT_YCBCR_16BIT = 1, + ISIF_PIXFMT_YCBCR_8BIT = 2, +}; + +enum isif_frmfmt { + ISIF_FRMFMT_PROGRESSIVE = 0, + ISIF_FRMFMT_INTERLACED = 1, +}; + +/* PIXEL ORDER IN MEMORY from LSB to MSB */ +/* only applicable for 8-bit input mode */ +enum isif_pixorder { + ISIF_PIXORDER_YCBYCR = 0, + ISIF_PIXORDER_CBYCRY = 1, +}; + +enum isif_buftype { + ISIF_BUFTYPE_FLD_INTERLEAVED = 0, + ISIF_BUFTYPE_FLD_SEPARATED = 1, +}; + +struct isif_ycbcr_config { + /* v4l2 pixel format */ + unsigned long v4l2_pix_fmt; + /* isif pixel format */ + enum isif_pixfmt pix_fmt; + /* isif frame format */ + enum isif_frmfmt frm_fmt; + /* isif crop window */ + struct v4l2_rect win; + /* field polarity */ + enum vpfe_pin_pol fid_pol; + /* interface VD polarity */ + enum vpfe_pin_pol vd_pol; + /* interface HD polarity */ + enum vpfe_pin_pol hd_pol; + /* isif pix order. Only used for ycbcr capture */ + enum isif_pixorder pix_order; + /* isif buffer type. Only used for ycbcr capture */ + enum isif_buftype buf_type; +}; + +enum isif_cfa_pattern { + ISIF_CFA_PAT_MOSAIC = 0, + ISIF_CFA_PAT_STRIPE = 1, +}; + +enum isif_data_msb { + /* MSB b15 */ + ISIF_BIT_MSB_15 = 0, + /* MSB b14 */ + ISIF_BIT_MSB_14 = 1, + /* MSB b13 */ + ISIF_BIT_MSB_13 = 2, + /* MSB b12 */ + ISIF_BIT_MSB_12 = 3, + /* MSB b11 */ + ISIF_BIT_MSB_11 = 4, + /* MSB b10 */ + ISIF_BIT_MSB_10 = 5, + /* MSB b9 */ + ISIF_BIT_MSB_9 = 6, + /* MSB b8 */ + ISIF_BIT_MSB_8 = 7, + /* MSB b7 */ + ISIF_BIT_MSB_7 = 8, +}; + +struct isif_params_raw { + /* v4l2 pixel format */ + unsigned long v4l2_pix_fmt; + /* isif pixel format */ + enum isif_pixfmt pix_fmt; + /* isif frame format */ + enum isif_frmfmt frm_fmt; + /* video window */ + struct v4l2_rect win; + /* field polarity */ + enum vpfe_pin_pol fid_pol; + /* interface VD polarity */ + enum vpfe_pin_pol vd_pol; + /* interface HD polarity */ + enum vpfe_pin_pol hd_pol; + /* buffer type. Applicable for interlaced mode */ + enum isif_buftype buf_type; + /* cfa pattern */ + enum isif_cfa_pattern cfa_pat; + /* Data MSB position */ + enum isif_data_msb data_msb; + /* Enable horizontal flip */ + unsigned char horz_flip_en; + /* Enable image invert vertically */ + unsigned char image_invert_en; + unsigned char dpcm_predictor; + struct vpfe_isif_raw_config config_params; +}; + +enum isif_data_pack { + ISIF_PACK_16BIT = 0, + ISIF_PACK_12BIT = 1, + ISIF_PACK_8BIT = 2, +}; + +struct isif_gain_values { + unsigned int cr_gain; + unsigned int cgr_gain; + unsigned int cgb_gain; + unsigned int cb_gain; + unsigned int offset; +}; + +struct isif_oper_config { + struct isif_ycbcr_config ycbcr; + struct isif_params_raw bayer; + enum isif_data_pack data_pack; + struct isif_gain_values isif_gain_params; + void *__iomem base_addr; + void *__iomem linear_tbl0_addr; + void *__iomem linear_tbl1_addr; +}; + +#define ISIF_PAD_SINK 0 +#define ISIF_PAD_SOURCE 1 + +#define ISIF_PADS_NUM 2 + +enum isif_input_entity { + ISIF_INPUT_NONE = 0, + ISIF_INPUT_PARALLEL = 1, +}; + +#define ISIF_OUTPUT_NONE (0) +#define ISIF_OUTPUT_MEMORY (1 << 0) +#define ISIF_OUTPUT_IPIPEIF (1 << 1) + +struct vpfe_isif_device { + struct v4l2_subdev subdev; + struct media_pad pads[ISIF_PADS_NUM]; + struct v4l2_mbus_framefmt formats[ISIF_PADS_NUM]; + enum isif_input_entity input; + unsigned int output; + struct v4l2_ctrl_handler ctrls; + struct v4l2_rect crop; + struct isif_oper_config isif_cfg; + struct vpfe_video_device video_out; +}; + +enum v4l2_field vpfe_isif_get_fid(struct vpfe_device *vpfe_dev); +void vpfe_isif_unregister_entities(struct vpfe_isif_device *isif); +int vpfe_isif_register_entities(struct vpfe_isif_device *isif, + struct v4l2_device *dev); +int vpfe_isif_init(struct vpfe_isif_device *isif, struct platform_device *pdev); +void vpfe_isif_cleanup(struct vpfe_isif_device *vpfe_isif, + struct platform_device *pdev); +void vpfe_isif_vidint1_isr(struct vpfe_isif_device *isif); +void vpfe_isif_buffer_isr(struct vpfe_isif_device *isif); + +#endif /* _DAVINCI_VPFE_DM365_ISIF_H */ diff --git a/drivers/staging/media/davinci_vpfe/dm365_isif_regs.h b/drivers/staging/media/davinci_vpfe/dm365_isif_regs.h new file mode 100644 index 000000000000..8aceabb43f8e --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_isif_regs.h @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + */ + +#ifndef _DAVINCI_VPFE_DM365_ISIF_REGS_H +#define _DAVINCI_VPFE_DM365_ISIF_REGS_H + +/* ISIF registers relative offsets */ +#define SYNCEN 0x00 +#define MODESET 0x04 +#define HDW 0x08 +#define VDW 0x0c +#define PPLN 0x10 +#define LPFR 0x14 +#define SPH 0x18 +#define LNH 0x1c +#define SLV0 0x20 +#define SLV1 0x24 +#define LNV 0x28 +#define CULH 0x2c +#define CULV 0x30 +#define HSIZE 0x34 +#define SDOFST 0x38 +#define CADU 0x3c +#define CADL 0x40 +#define LINCFG0 0x44 +#define LINCFG1 0x48 +#define CCOLP 0x4c +#define CRGAIN 0x50 +#define CGRGAIN 0x54 +#define CGBGAIN 0x58 +#define CBGAIN 0x5c +#define COFSTA 0x60 +#define FLSHCFG0 0x64 +#define FLSHCFG1 0x68 +#define FLSHCFG2 0x6c +#define VDINT0 0x70 +#define VDINT1 0x74 +#define VDINT2 0x78 +#define MISC 0x7c +#define CGAMMAWD 0x80 +#define REC656IF 0x84 +#define CCDCFG 0x88 +/***************************************************** +* Defect Correction registers +*****************************************************/ +#define DFCCTL 0x8c +#define VDFSATLV 0x90 +#define DFCMEMCTL 0x94 +#define DFCMEM0 0x98 +#define DFCMEM1 0x9c +#define DFCMEM2 0xa0 +#define DFCMEM3 0xa4 +#define DFCMEM4 0xa8 +/**************************************************** +* Black Clamp registers +****************************************************/ +#define CLAMPCFG 0xac +#define CLDCOFST 0xb0 +#define CLSV 0xb4 +#define CLHWIN0 0xb8 +#define CLHWIN1 0xbc +#define CLHWIN2 0xc0 +#define CLVRV 0xc4 +#define CLVWIN0 0xc8 +#define CLVWIN1 0xcc +#define CLVWIN2 0xd0 +#define CLVWIN3 0xd4 +/**************************************************** +* Lense Shading Correction +****************************************************/ +#define DATAHOFST 0xd8 +#define DATAVOFST 0xdc +#define LSCHVAL 0xe0 +#define LSCVVAL 0xe4 +#define TWODLSCCFG 0xe8 +#define TWODLSCOFST 0xec +#define TWODLSCINI 0xf0 +#define TWODLSCGRBU 0xf4 +#define TWODLSCGRBL 0xf8 +#define TWODLSCGROF 0xfc +#define TWODLSCORBU 0x100 +#define TWODLSCORBL 0x104 +#define TWODLSCOROF 0x108 +#define TWODLSCIRQEN 0x10c +#define TWODLSCIRQST 0x110 +/**************************************************** +* Data formatter +****************************************************/ +#define FMTCFG 0x114 +#define FMTPLEN 0x118 +#define FMTSPH 0x11c +#define FMTLNH 0x120 +#define FMTSLV 0x124 +#define FMTLNV 0x128 +#define FMTRLEN 0x12c +#define FMTHCNT 0x130 +#define FMTAPTR_BASE 0x134 +/* Below macro for addresses FMTAPTR0 - FMTAPTR15 */ +#define FMTAPTR(i) (FMTAPTR_BASE + (i * 4)) +#define FMTPGMVF0 0x174 +#define FMTPGMVF1 0x178 +#define FMTPGMAPU0 0x17c +#define FMTPGMAPU1 0x180 +#define FMTPGMAPS0 0x184 +#define FMTPGMAPS1 0x188 +#define FMTPGMAPS2 0x18c +#define FMTPGMAPS3 0x190 +#define FMTPGMAPS4 0x194 +#define FMTPGMAPS5 0x198 +#define FMTPGMAPS6 0x19c +#define FMTPGMAPS7 0x1a0 +/************************************************ +* Color Space Converter +************************************************/ +#define CSCCTL 0x1a4 +#define CSCM0 0x1a8 +#define CSCM1 0x1ac +#define CSCM2 0x1b0 +#define CSCM3 0x1b4 +#define CSCM4 0x1b8 +#define CSCM5 0x1bc +#define CSCM6 0x1c0 +#define CSCM7 0x1c4 +#define OBWIN0 0x1c8 +#define OBWIN1 0x1cc +#define OBWIN2 0x1d0 +#define OBWIN3 0x1d4 +#define OBVAL0 0x1d8 +#define OBVAL1 0x1dc +#define OBVAL2 0x1e0 +#define OBVAL3 0x1e4 +#define OBVAL4 0x1e8 +#define OBVAL5 0x1ec +#define OBVAL6 0x1f0 +#define OBVAL7 0x1f4 +#define CLKCTL 0x1f8 + +/* Masks & Shifts below */ +#define START_PX_HOR_MASK 0x7fff +#define NUM_PX_HOR_MASK 0x7fff +#define START_VER_ONE_MASK 0x7fff +#define START_VER_TWO_MASK 0x7fff +#define NUM_LINES_VER 0x7fff + +/* gain - offset masks */ +#define OFFSET_MASK 0xfff +#define GAIN_SDRAM_EN_SHIFT 12 +#define GAIN_IPIPE_EN_SHIFT 13 +#define GAIN_H3A_EN_SHIFT 14 +#define OFST_SDRAM_EN_SHIFT 8 +#define OFST_IPIPE_EN_SHIFT 9 +#define OFST_H3A_EN_SHIFT 10 +#define GAIN_OFFSET_EN_MASK 0x7700 + +/* Culling */ +#define CULL_PAT_EVEN_LINE_SHIFT 8 + +/* CCDCFG register */ +#define ISIF_YCINSWP_RAW (0x00 << 4) +#define ISIF_YCINSWP_YCBCR (0x01 << 4) +#define ISIF_CCDCFG_FIDMD_LATCH_VSYNC (0x00 << 6) +#define ISIF_CCDCFG_WENLOG_AND (0x00 << 8) +#define ISIF_CCDCFG_TRGSEL_WEN (0x00 << 9) +#define ISIF_CCDCFG_EXTRG_DISABLE (0x00 << 10) +#define ISIF_LATCH_ON_VSYNC_DISABLE (0x01 << 15) +#define ISIF_LATCH_ON_VSYNC_ENABLE (0x00 << 15) +#define ISIF_DATA_PACK_MASK 0x03 +#define ISIF_PIX_ORDER_SHIFT 11 +#define ISIF_PIX_ORDER_MASK 0x01 +#define ISIF_BW656_ENABLE (0x01 << 5) + +/* MODESET registers */ +#define ISIF_VDHDOUT_INPUT (0x00 << 0) +#define ISIF_INPUT_MASK 0x03 +#define ISIF_INPUT_SHIFT 12 +#define ISIF_FID_POL_MASK 0x01 +#define ISIF_FID_POL_SHIFT 4 +#define ISIF_HD_POL_MASK 0x01 +#define ISIF_HD_POL_SHIFT 3 +#define ISIF_VD_POL_MASK 0x01 +#define ISIF_VD_POL_SHIFT 2 +#define ISIF_DATAPOL_NORMAL 0x00 +#define ISIF_DATAPOL_MASK 0x01 +#define ISIF_DATAPOL_SHIFT 6 +#define ISIF_EXWEN_DISABLE 0x00 +#define ISIF_EXWEN_MASK 0x01 +#define ISIF_EXWEN_SHIFT 5 +#define ISIF_FRM_FMT_MASK 0x01 +#define ISIF_FRM_FMT_SHIFT 7 +#define ISIF_DATASFT_MASK 0x07 +#define ISIF_DATASFT_SHIFT 8 +#define ISIF_LPF_SHIFT 14 +#define ISIF_LPF_MASK 0x1 + +/* GAMMAWD registers */ +#define ISIF_ALAW_GAMA_WD_MASK 0xf +#define ISIF_ALAW_GAMA_WD_SHIFT 1 +#define ISIF_ALAW_ENABLE 0x01 +#define ISIF_GAMMAWD_CFA_MASK 0x01 +#define ISIF_GAMMAWD_CFA_SHIFT 5 + +/* HSIZE registers */ +#define ISIF_HSIZE_FLIP_MASK 0x01 +#define ISIF_HSIZE_FLIP_SHIFT 12 +#define ISIF_LINEOFST_MASK 0xfff + +/* MISC registers */ +#define ISIF_DPCM_EN_SHIFT 12 +#define ISIF_DPCM_PREDICTOR_SHIFT 13 +#define ISIF_DPCM_PREDICTOR_MASK 1 + +/* Black clamp related */ +#define ISIF_BC_DCOFFSET_MASK 0x1fff +#define ISIF_BC_MODE_COLOR_MASK 1 +#define ISIF_BC_MODE_COLOR_SHIFT 4 +#define ISIF_HORZ_BC_MODE_MASK 3 +#define ISIF_HORZ_BC_MODE_SHIFT 1 +#define ISIF_HORZ_BC_WIN_COUNT_MASK 0x1f +#define ISIF_HORZ_BC_WIN_SEL_SHIFT 5 +#define ISIF_HORZ_BC_PIX_LIMIT_SHIFT 6 +#define ISIF_HORZ_BC_WIN_H_SIZE_MASK 3 +#define ISIF_HORZ_BC_WIN_H_SIZE_SHIFT 8 +#define ISIF_HORZ_BC_WIN_V_SIZE_MASK 3 +#define ISIF_HORZ_BC_WIN_V_SIZE_SHIFT 12 +#define ISIF_HORZ_BC_WIN_START_H_MASK 0x1fff +#define ISIF_HORZ_BC_WIN_START_V_MASK 0x1fff +#define ISIF_VERT_BC_OB_H_SZ_MASK 7 +#define ISIF_VERT_BC_RST_VAL_SEL_MASK 3 +#define ISIF_VERT_BC_RST_VAL_SEL_SHIFT 4 +#define ISIF_VERT_BC_LINE_AVE_COEF_SHIFT 8 +#define ISIF_VERT_BC_OB_START_HORZ_MASK 0x1fff +#define ISIF_VERT_BC_OB_START_VERT_MASK 0x1fff +#define ISIF_VERT_BC_OB_VERT_SZ_MASK 0x1fff +#define ISIF_VERT_BC_RST_VAL_MASK 0xfff +#define ISIF_BC_VERT_START_SUB_V_MASK 0x1fff + +/* VDFC registers */ +#define ISIF_VDFC_EN_SHIFT 4 +#define ISIF_VDFC_CORR_MOD_MASK 3 +#define ISIF_VDFC_CORR_MOD_SHIFT 5 +#define ISIF_VDFC_CORR_WHOLE_LN_SHIFT 7 +#define ISIF_VDFC_LEVEL_SHFT_MASK 7 +#define ISIF_VDFC_LEVEL_SHFT_SHIFT 8 +#define ISIF_VDFC_SAT_LEVEL_MASK 0xfff +#define ISIF_VDFC_POS_MASK 0x1fff +#define ISIF_DFCMEMCTL_DFCMARST_SHIFT 2 + +/* CSC registers */ +#define ISIF_CSC_COEF_INTEG_MASK 7 +#define ISIF_CSC_COEF_DECIMAL_MASK 0x1f +#define ISIF_CSC_COEF_INTEG_SHIFT 5 +#define ISIF_CSCM_MSB_SHIFT 8 +#define ISIF_DF_CSC_SPH_MASK 0x1fff +#define ISIF_DF_CSC_LNH_MASK 0x1fff +#define ISIF_DF_CSC_SLV_MASK 0x1fff +#define ISIF_DF_CSC_LNV_MASK 0x1fff +#define ISIF_DF_NUMLINES 0x7fff +#define ISIF_DF_NUMPIX 0x1fff + +/* Offsets for LSC/DFC/Gain */ +#define ISIF_DATA_H_OFFSET_MASK 0x1fff +#define ISIF_DATA_V_OFFSET_MASK 0x1fff + +/* Linearization */ +#define ISIF_LIN_CORRSFT_MASK 7 +#define ISIF_LIN_CORRSFT_SHIFT 4 +#define ISIF_LIN_SCALE_FACT_INTEG_SHIFT 10 +#define ISIF_LIN_SCALE_FACT_DECIMAL_MASK 0x3ff +#define ISIF_LIN_ENTRY_MASK 0x3ff + +/* masks and shifts*/ +#define ISIF_SYNCEN_VDHDEN_MASK (1 << 0) +#define ISIF_SYNCEN_WEN_MASK (1 << 1) +#define ISIF_SYNCEN_WEN_SHIFT 1 + +#endif /* _DAVINCI_VPFE_DM365_ISIF_REGS_H */ diff --git a/drivers/staging/media/davinci_vpfe/dm365_resizer.c b/drivers/staging/media/davinci_vpfe/dm365_resizer.c new file mode 100644 index 000000000000..9cb0262b9b33 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_resizer.c @@ -0,0 +1,1999 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + * + * + * Resizer allows upscaling or downscaling a image to a desired + * resolution. There are 2 resizer modules. both operating on the + * same input image, but can have different output resolution. + */ + +#include "dm365_ipipe_hw.h" +#include "dm365_resizer.h" + +#define MIN_IN_WIDTH 32 +#define MIN_IN_HEIGHT 32 +#define MAX_IN_WIDTH 4095 +#define MAX_IN_HEIGHT 4095 +#define MIN_OUT_WIDTH 16 +#define MIN_OUT_HEIGHT 2 + +static const unsigned int resizer_input_formats[] = { + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_Y8_1X8, + V4L2_MBUS_FMT_UV8_1X8, + V4L2_MBUS_FMT_SGRBG12_1X12, +}; + +static const unsigned int resizer_output_formats[] = { + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_Y8_1X8, + V4L2_MBUS_FMT_UV8_1X8, + V4L2_MBUS_FMT_YDYUYDYV8_1X16, + V4L2_MBUS_FMT_SGRBG12_1X12, +}; + +/* resizer_calculate_line_length() - This function calculates the line length of + * various image planes at the input and + * output. + */ +static void +resizer_calculate_line_length(enum v4l2_mbus_pixelcode pix, int width, + int height, int *line_len, int *line_len_c) +{ + *line_len = 0; + *line_len_c = 0; + + if (pix == V4L2_MBUS_FMT_UYVY8_2X8 || + pix == V4L2_MBUS_FMT_SGRBG12_1X12) { + *line_len = width << 1; + } else if (pix == V4L2_MBUS_FMT_Y8_1X8 || + pix == V4L2_MBUS_FMT_UV8_1X8) { + *line_len = width; + *line_len_c = width; + } else { + /* YUV 420 */ + /* round width to upper 32 byte boundary */ + *line_len = width; + *line_len_c = width; + } + /* adjust the line len to be a multiple of 32 */ + *line_len += 31; + *line_len &= ~0x1f; + *line_len_c += 31; + *line_len_c &= ~0x1f; +} + +static inline int +resizer_validate_output_image_format(struct device *dev, + struct v4l2_mbus_framefmt *format, + int *in_line_len, int *in_line_len_c) +{ + if (format->code != V4L2_MBUS_FMT_UYVY8_2X8 && + format->code != V4L2_MBUS_FMT_Y8_1X8 && + format->code != V4L2_MBUS_FMT_UV8_1X8 && + format->code != V4L2_MBUS_FMT_YDYUYDYV8_1X16 && + format->code != V4L2_MBUS_FMT_SGRBG12_1X12) { + dev_err(dev, "Invalid Mbus format, %d\n", format->code); + return -EINVAL; + } + if (!format->width || !format->height) { + dev_err(dev, "invalid width or height\n"); + return -EINVAL; + } + resizer_calculate_line_length(format->code, format->width, + format->height, in_line_len, in_line_len_c); + return 0; +} + +static void +resizer_configure_passthru(struct vpfe_resizer_device *resizer, int bypass) +{ + struct resizer_params *param = &resizer->config; + + param->rsz_rsc_param[RSZ_A].cen = DISABLE; + param->rsz_rsc_param[RSZ_A].yen = DISABLE; + param->rsz_rsc_param[RSZ_A].v_phs_y = 0; + param->rsz_rsc_param[RSZ_A].v_phs_c = 0; + param->rsz_rsc_param[RSZ_A].v_dif = 256; + param->rsz_rsc_param[RSZ_A].v_lpf_int_y = 0; + param->rsz_rsc_param[RSZ_A].v_lpf_int_c = 0; + param->rsz_rsc_param[RSZ_A].h_phs = 0; + param->rsz_rsc_param[RSZ_A].h_dif = 256; + param->rsz_rsc_param[RSZ_A].h_lpf_int_y = 0; + param->rsz_rsc_param[RSZ_A].h_lpf_int_c = 0; + param->rsz_rsc_param[RSZ_A].dscale_en = DISABLE; + param->rsz2rgb[RSZ_A].rgb_en = DISABLE; + param->rsz_en[RSZ_A] = ENABLE; + param->rsz_en[RSZ_B] = DISABLE; + if (bypass) { + param->rsz_rsc_param[RSZ_A].i_vps = 0; + param->rsz_rsc_param[RSZ_A].i_hps = 0; + /* Raw Bypass */ + param->rsz_common.passthrough = BYPASS_ON; + } +} + +static void +configure_resizer_out_params(struct vpfe_resizer_device *resizer, int index, + void *output_spec, unsigned char partial, + unsigned flag) +{ + struct resizer_params *param = &resizer->config; + struct v4l2_mbus_framefmt *outformat; + struct vpfe_rsz_output_spec *output; + + if (index == RSZ_A && + resizer->resizer_a.output == RESIZER_OUTPUT_NONE) { + param->rsz_en[index] = DISABLE; + return; + } + if (index == RSZ_B && + resizer->resizer_b.output == RESIZER_OUTPUT_NONE) { + param->rsz_en[index] = DISABLE; + return; + } + output = (struct vpfe_rsz_output_spec *)output_spec; + param->rsz_en[index] = ENABLE; + if (partial) { + param->rsz_rsc_param[index].h_flip = output->h_flip; + param->rsz_rsc_param[index].v_flip = output->v_flip; + param->rsz_rsc_param[index].v_typ_y = output->v_typ_y; + param->rsz_rsc_param[index].v_typ_c = output->v_typ_c; + param->rsz_rsc_param[index].v_lpf_int_y = + output->v_lpf_int_y; + param->rsz_rsc_param[index].v_lpf_int_c = + output->v_lpf_int_c; + param->rsz_rsc_param[index].h_typ_y = output->h_typ_y; + param->rsz_rsc_param[index].h_typ_c = output->h_typ_c; + param->rsz_rsc_param[index].h_lpf_int_y = + output->h_lpf_int_y; + param->rsz_rsc_param[index].h_lpf_int_c = + output->h_lpf_int_c; + param->rsz_rsc_param[index].dscale_en = + output->en_down_scale; + param->rsz_rsc_param[index].h_dscale_ave_sz = + output->h_dscale_ave_sz; + param->rsz_rsc_param[index].v_dscale_ave_sz = + output->v_dscale_ave_sz; + param->ext_mem_param[index].user_y_ofst = + (output->user_y_ofst + 31) & ~0x1f; + param->ext_mem_param[index].user_c_ofst = + (output->user_c_ofst + 31) & ~0x1f; + return; + } + + if (index == RSZ_A) + outformat = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE]; + else + outformat = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE]; + param->rsz_rsc_param[index].o_vsz = outformat->height - 1; + param->rsz_rsc_param[index].o_hsz = outformat->width - 1; + param->ext_mem_param[index].rsz_sdr_ptr_s_y = output->vst_y; + param->ext_mem_param[index].rsz_sdr_ptr_e_y = outformat->height; + param->ext_mem_param[index].rsz_sdr_ptr_s_c = output->vst_c; + param->ext_mem_param[index].rsz_sdr_ptr_e_c = outformat->height; + + if (!flag) + return; + /* update common parameters */ + param->rsz_rsc_param[index].h_flip = output->h_flip; + param->rsz_rsc_param[index].v_flip = output->v_flip; + param->rsz_rsc_param[index].v_typ_y = output->v_typ_y; + param->rsz_rsc_param[index].v_typ_c = output->v_typ_c; + param->rsz_rsc_param[index].v_lpf_int_y = output->v_lpf_int_y; + param->rsz_rsc_param[index].v_lpf_int_c = output->v_lpf_int_c; + param->rsz_rsc_param[index].h_typ_y = output->h_typ_y; + param->rsz_rsc_param[index].h_typ_c = output->h_typ_c; + param->rsz_rsc_param[index].h_lpf_int_y = output->h_lpf_int_y; + param->rsz_rsc_param[index].h_lpf_int_c = output->h_lpf_int_c; + param->rsz_rsc_param[index].dscale_en = output->en_down_scale; + param->rsz_rsc_param[index].h_dscale_ave_sz = output->h_dscale_ave_sz; + param->rsz_rsc_param[index].v_dscale_ave_sz = output->h_dscale_ave_sz; + param->ext_mem_param[index].user_y_ofst = + (output->user_y_ofst + 31) & ~0x1f; + param->ext_mem_param[index].user_c_ofst = + (output->user_c_ofst + 31) & ~0x1f; +} + +/* + * resizer_calculate_resize_ratios() - Calculates resize ratio for resizer + * A or B. This is called after setting + * the input size or output size. + * @resizer: Pointer to VPFE resizer subdevice. + * @index: index RSZ_A-resizer-A RSZ_B-resizer-B. + */ +void +resizer_calculate_resize_ratios(struct vpfe_resizer_device *resizer, int index) +{ + struct resizer_params *param = &resizer->config; + struct v4l2_mbus_framefmt *informat, *outformat; + + informat = &resizer->crop_resizer.formats[RESIZER_CROP_PAD_SINK]; + + if (index == RSZ_A) + outformat = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE]; + else + outformat = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE]; + + if (outformat->field != V4L2_FIELD_INTERLACED) + param->rsz_rsc_param[index].v_dif = + ((informat->height) * 256) / (outformat->height); + else + param->rsz_rsc_param[index].v_dif = + ((informat->height >> 1) * 256) / (outformat->height); + param->rsz_rsc_param[index].h_dif = + ((informat->width) * 256) / (outformat->width); +} + +void +static resizer_enable_422_420_conversion(struct resizer_params *param, + int index, bool en) +{ + param->rsz_rsc_param[index].cen = en; + param->rsz_rsc_param[index].yen = en; +} + +/* resizer_calculate_sdram_offsets() - This function calculates the offsets from + * start of buffer for the C plane when + * output format is YUV420SP. It also + * calculates the offsets from the start of + * the buffer when the image is flipped + * vertically or horizontally for ycbcr/y/c + * planes. + * @resizer: Pointer to resizer subdevice. + * @index: index RSZ_A-resizer-A RSZ_B-resizer-B. + */ +static int +resizer_calculate_sdram_offsets(struct vpfe_resizer_device *resizer, int index) +{ + struct resizer_params *param = &resizer->config; + struct v4l2_mbus_framefmt *outformat; + int bytesperpixel = 2; + int image_height; + int image_width; + int yuv_420 = 0; + int offset = 0; + + if (index == RSZ_A) + outformat = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE]; + else + outformat = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE]; + + image_height = outformat->height + 1; + image_width = outformat->width + 1; + param->ext_mem_param[index].c_offset = 0; + param->ext_mem_param[index].flip_ofst_y = 0; + param->ext_mem_param[index].flip_ofst_c = 0; + if (outformat->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16) { + /* YUV 420 */ + yuv_420 = 1; + bytesperpixel = 1; + } + + if (param->rsz_rsc_param[index].h_flip) + /* width * bytesperpixel - 1 */ + offset = (image_width * bytesperpixel) - 1; + if (param->rsz_rsc_param[index].v_flip) + offset += (image_height - 1) * + param->ext_mem_param[index].rsz_sdr_oft_y; + param->ext_mem_param[index].flip_ofst_y = offset; + if (!yuv_420) + return 0; + offset = 0; + /* half height for c-plane */ + if (param->rsz_rsc_param[index].h_flip) + /* width * bytesperpixel - 1 */ + offset = image_width - 1; + if (param->rsz_rsc_param[index].v_flip) + offset += (((image_height >> 1) - 1) * + param->ext_mem_param[index].rsz_sdr_oft_c); + param->ext_mem_param[index].flip_ofst_c = offset; + param->ext_mem_param[index].c_offset = + param->ext_mem_param[index].rsz_sdr_oft_y * image_height; + return 0; +} + +int resizer_configure_output_win(struct vpfe_resizer_device *resizer) +{ + struct resizer_params *param = &resizer->config; + struct vpfe_rsz_output_spec output_specs; + struct v4l2_mbus_framefmt *outformat; + int line_len_c; + int line_len; + int ret; + + outformat = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE]; + + output_specs.vst_y = param->user_config.vst; + if (outformat->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16) + output_specs.vst_c = param->user_config.vst; + + configure_resizer_out_params(resizer, RSZ_A, &output_specs, 0, 0); + resizer_calculate_line_length(outformat->code, + param->rsz_rsc_param[0].o_hsz + 1, + param->rsz_rsc_param[0].o_vsz + 1, + &line_len, &line_len_c); + param->ext_mem_param[0].rsz_sdr_oft_y = line_len; + param->ext_mem_param[0].rsz_sdr_oft_c = line_len_c; + resizer_calculate_resize_ratios(resizer, RSZ_A); + if (param->rsz_en[RSZ_B]) + resizer_calculate_resize_ratios(resizer, RSZ_B); + + if (outformat->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16) + resizer_enable_422_420_conversion(param, RSZ_A, ENABLE); + else + resizer_enable_422_420_conversion(param, RSZ_A, DISABLE); + + ret = resizer_calculate_sdram_offsets(resizer, RSZ_A); + if (!ret && param->rsz_en[RSZ_B]) + ret = resizer_calculate_sdram_offsets(resizer, RSZ_B); + + if (ret) + pr_err("Error in calculating sdram offsets\n"); + return ret; +} + +static int +resizer_calculate_down_scale_f_div_param(struct device *dev, + int input_width, int output_width, + struct resizer_scale_param *param) +{ + /* rsz = R, input_width = H, output width = h in the equation */ + unsigned int two_power; + unsigned int upper_h1; + unsigned int upper_h2; + unsigned int val1; + unsigned int val; + unsigned int rsz; + unsigned int h1; + unsigned int h2; + unsigned int o; + unsigned int n; + + upper_h1 = input_width >> 1; + n = param->h_dscale_ave_sz; + /* 2 ^ (scale+1) */ + two_power = 1 << (n + 1); + upper_h1 = (upper_h1 >> (n + 1)) << (n + 1); + upper_h2 = input_width - upper_h1; + if (upper_h2 % two_power) { + dev_err(dev, "frame halves to be a multiple of 2 power n+1\n"); + return -EINVAL; + } + two_power = 1 << n; + rsz = (input_width << 8) / output_width; + val = rsz * two_power; + val = ((upper_h1 << 8) / val) + 1; + if (!(val % 2)) { + h1 = val; + } else { + val = upper_h1 << 8; + val >>= n + 1; + val -= rsz >> 1; + val /= rsz << 1; + val <<= 1; + val += 2; + h1 = val; + } + o = 10 + (two_power << 2); + if (((input_width << 7) / rsz) % 2) + o += (((CEIL(rsz, 1024)) << 1) << n); + h2 = output_width - h1; + /* phi */ + val = (h1 * rsz) - (((upper_h1 - (o - 10)) / two_power) << 8); + /* skip */ + val1 = ((val - 1024) >> 9) << 1; + param->f_div.num_passes = MAX_PASSES; + param->f_div.pass[0].o_hsz = h1 - 1; + param->f_div.pass[0].i_hps = 0; + param->f_div.pass[0].h_phs = 0; + param->f_div.pass[0].src_hps = 0; + param->f_div.pass[0].src_hsz = upper_h1 + o; + param->f_div.pass[1].o_hsz = h2 - 1; + param->f_div.pass[1].i_hps = 10 + (val1 * two_power); + param->f_div.pass[1].h_phs = (val - (val1 << 8)); + param->f_div.pass[1].src_hps = upper_h1 - o; + param->f_div.pass[1].src_hsz = upper_h2 + o; + + return 0; +} + +static int +resizer_configure_common_in_params(struct vpfe_resizer_device *resizer) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + struct resizer_params *param = &resizer->config; + struct vpfe_rsz_config_params *user_config; + struct v4l2_mbus_framefmt *informat; + + informat = &resizer->crop_resizer.formats[RESIZER_CROP_PAD_SINK]; + user_config = &resizer->config.user_config; + param->rsz_common.vps = param->user_config.vst; + param->rsz_common.hps = param->user_config.hst; + + if (vpfe_ipipeif_decimation_enabled(vpfe_dev)) + param->rsz_common.hsz = (((informat->width - 1) * + IPIPEIF_RSZ_CONST) / vpfe_ipipeif_get_rsz(vpfe_dev)); + else + param->rsz_common.hsz = informat->width - 1; + + if (informat->field == V4L2_FIELD_INTERLACED) + param->rsz_common.vsz = (informat->height - 1) >> 1; + else + param->rsz_common.vsz = informat->height - 1; + + param->rsz_common.raw_flip = 0; + + if (resizer->crop_resizer.input == RESIZER_CROP_INPUT_IPIPEIF) + param->rsz_common.source = IPIPEIF_DATA; + else + param->rsz_common.source = IPIPE_DATA; + + switch (informat->code) { + case V4L2_MBUS_FMT_UYVY8_2X8: + param->rsz_common.src_img_fmt = RSZ_IMG_422; + param->rsz_common.raw_flip = 0; + break; + + case V4L2_MBUS_FMT_Y8_1X8: + param->rsz_common.src_img_fmt = RSZ_IMG_420; + /* Select y */ + param->rsz_common.y_c = 0; + param->rsz_common.raw_flip = 0; + break; + + case V4L2_MBUS_FMT_UV8_1X8: + param->rsz_common.src_img_fmt = RSZ_IMG_420; + /* Select y */ + param->rsz_common.y_c = 1; + param->rsz_common.raw_flip = 0; + break; + + case V4L2_MBUS_FMT_SGRBG12_1X12: + param->rsz_common.raw_flip = 1; + break; + + default: + param->rsz_common.src_img_fmt = RSZ_IMG_422; + param->rsz_common.source = IPIPE_DATA; + } + + param->rsz_common.yuv_y_min = user_config->yuv_y_min; + param->rsz_common.yuv_y_max = user_config->yuv_y_max; + param->rsz_common.yuv_c_min = user_config->yuv_c_min; + param->rsz_common.yuv_c_max = user_config->yuv_c_max; + param->rsz_common.out_chr_pos = user_config->out_chr_pos; + param->rsz_common.rsz_seq_crv = user_config->chroma_sample_even; + + return 0; +} +static int +resizer_configure_in_continious_mode(struct vpfe_resizer_device *resizer) +{ + struct device *dev = resizer->crop_resizer.subdev.v4l2_dev->dev; + struct resizer_params *param = &resizer->config; + struct vpfe_rsz_config_params *cont_config; + int line_len_c; + int line_len; + int ret; + + if (resizer->resizer_a.output != RESIZER_OUPUT_MEMORY) { + dev_err(dev, "enable resizer - Resizer-A\n"); + return -EINVAL; + } + + cont_config = &resizer->config.user_config; + param->rsz_en[RSZ_A] = ENABLE; + configure_resizer_out_params(resizer, RSZ_A, + &cont_config->output1, 1, 0); + param->rsz_en[RSZ_B] = DISABLE; + param->oper_mode = RESIZER_MODE_CONTINIOUS; + + if (resizer->resizer_b.output == RESIZER_OUPUT_MEMORY) { + struct v4l2_mbus_framefmt *outformat2; + + param->rsz_en[RSZ_B] = ENABLE; + outformat2 = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE]; + ret = resizer_validate_output_image_format(dev, outformat2, + &line_len, &line_len_c); + if (ret) + return ret; + param->ext_mem_param[RSZ_B].rsz_sdr_oft_y = line_len; + param->ext_mem_param[RSZ_B].rsz_sdr_oft_c = line_len_c; + configure_resizer_out_params(resizer, RSZ_B, + &cont_config->output2, 0, 1); + if (outformat2->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16) + resizer_enable_422_420_conversion(param, + RSZ_B, ENABLE); + else + resizer_enable_422_420_conversion(param, + RSZ_B, DISABLE); + } + resizer_configure_common_in_params(resizer); + ret = resizer_configure_output_win(resizer); + if (ret) + return ret; + + param->rsz_common.passthrough = cont_config->bypass; + if (cont_config->bypass) + resizer_configure_passthru(resizer, 1); + + return 0; +} + +static inline int +resizer_validate_input_image_format(struct device *dev, + enum v4l2_mbus_pixelcode pix, + int width, int height, int *line_len) +{ + int val; + + if (pix != V4L2_MBUS_FMT_UYVY8_2X8 && + pix != V4L2_MBUS_FMT_Y8_1X8 && + pix != V4L2_MBUS_FMT_UV8_1X8 && + pix != V4L2_MBUS_FMT_SGRBG12_1X12) { + dev_err(dev, + "resizer validate output: pix format not supported, %d\n", pix); + return -EINVAL; + } + + if (!width || !height) { + dev_err(dev, + "resizer validate input: invalid width or height\n"); + return -EINVAL; + } + + if (pix == V4L2_MBUS_FMT_UV8_1X8) + resizer_calculate_line_length(pix, width, + height, &val, line_len); + else + resizer_calculate_line_length(pix, width, + height, line_len, &val); + + return 0; +} + +static int +resizer_validate_decimation(struct device *dev, enum ipipeif_decimation dec_en, + unsigned char rsz, unsigned char frame_div_mode_en, + int width) +{ + if (dec_en && frame_div_mode_en) { + dev_err(dev, + "dec_en & frame_div_mode_en can not enabled simultaneously\n"); + return -EINVAL; + } + + if (frame_div_mode_en) { + dev_err(dev, "frame_div_mode mode not supported\n"); + return -EINVAL; + } + + if (!dec_en) + return 0; + + if (width <= VPFE_IPIPE_MAX_INPUT_WIDTH) { + dev_err(dev, + "image width to be more than %d for decimation\n", + VPFE_IPIPE_MAX_INPUT_WIDTH); + return -EINVAL; + } + + if (rsz < IPIPEIF_RSZ_MIN || rsz > IPIPEIF_RSZ_MAX) { + dev_err(dev, "rsz range is %d to %d\n", + IPIPEIF_RSZ_MIN, IPIPEIF_RSZ_MAX); + return -EINVAL; + } + + return 0; +} + +/* resizer_calculate_normal_f_div_param() - Algorithm to calculate the frame + * division parameters for resizer. + * in normal mode. + */ +static int +resizer_calculate_normal_f_div_param(struct device *dev, int input_width, + int output_width, struct resizer_scale_param *param) +{ + /* rsz = R, input_width = H, output width = h in the equation */ + unsigned int val1; + unsigned int rsz; + unsigned int val; + unsigned int h1; + unsigned int h2; + unsigned int o; + + if (output_width > input_width) { + dev_err(dev, "frame div mode is used for scale down only\n"); + return -EINVAL; + } + + rsz = (input_width << 8) / output_width; + val = rsz << 1; + val = ((input_width << 8) / val) + 1; + o = 14; + if (!(val % 2)) { + h1 = val; + } else { + val = (input_width << 7); + val -= rsz >> 1; + val /= rsz << 1; + val <<= 1; + val += 2; + o += ((CEIL(rsz, 1024)) << 1); + h1 = val; + } + h2 = output_width - h1; + /* phi */ + val = (h1 * rsz) - (((input_width >> 1) - o) << 8); + /* skip */ + val1 = ((val - 1024) >> 9) << 1; + param->f_div.num_passes = MAX_PASSES; + param->f_div.pass[0].o_hsz = h1 - 1; + param->f_div.pass[0].i_hps = 0; + param->f_div.pass[0].h_phs = 0; + param->f_div.pass[0].src_hps = 0; + param->f_div.pass[0].src_hsz = (input_width >> 2) + o; + param->f_div.pass[1].o_hsz = h2 - 1; + param->f_div.pass[1].i_hps = val1; + param->f_div.pass[1].h_phs = (val - (val1 << 8)); + param->f_div.pass[1].src_hps = (input_width >> 2) - o; + param->f_div.pass[1].src_hsz = (input_width >> 2) + o; + + return 0; +} + +static int +resizer_configure_in_single_shot_mode(struct vpfe_resizer_device *resizer) +{ + struct vpfe_rsz_config_params *config = &resizer->config.user_config; + struct device *dev = resizer->crop_resizer.subdev.v4l2_dev->dev; + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + struct v4l2_mbus_framefmt *outformat1, *outformat2; + struct resizer_params *param = &resizer->config; + struct v4l2_mbus_framefmt *informat; + int decimation; + int line_len_c; + int line_len; + int rsz; + int ret; + + informat = &resizer->crop_resizer.formats[RESIZER_CROP_PAD_SINK]; + outformat1 = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE]; + outformat2 = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE]; + + decimation = vpfe_ipipeif_decimation_enabled(vpfe_dev); + rsz = vpfe_ipipeif_get_rsz(vpfe_dev); + if (decimation && param->user_config.frame_div_mode_en) { + dev_err(dev, + "dec_en & frame_div_mode_en cannot enabled simultaneously\n"); + return -EINVAL; + } + + ret = resizer_validate_decimation(dev, decimation, rsz, + param->user_config.frame_div_mode_en, informat->width); + if (ret) + return -EINVAL; + + ret = resizer_validate_input_image_format(dev, informat->code, + informat->width, informat->height, &line_len); + if (ret) + return -EINVAL; + + if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE) { + param->rsz_en[RSZ_A] = ENABLE; + ret = resizer_validate_output_image_format(dev, outformat1, + &line_len, &line_len_c); + if (ret) + return ret; + param->ext_mem_param[RSZ_A].rsz_sdr_oft_y = line_len; + param->ext_mem_param[RSZ_A].rsz_sdr_oft_c = line_len_c; + configure_resizer_out_params(resizer, RSZ_A, + ¶m->user_config.output1, 0, 1); + + if (outformat1->code == V4L2_MBUS_FMT_SGRBG12_1X12) + param->rsz_common.raw_flip = 1; + else + param->rsz_common.raw_flip = 0; + + if (outformat1->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16) + resizer_enable_422_420_conversion(param, + RSZ_A, ENABLE); + else + resizer_enable_422_420_conversion(param, + RSZ_A, DISABLE); + } + + if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE) { + param->rsz_en[RSZ_B] = ENABLE; + ret = resizer_validate_output_image_format(dev, outformat2, + &line_len, &line_len_c); + if (ret) + return ret; + param->ext_mem_param[RSZ_B].rsz_sdr_oft_y = line_len; + param->ext_mem_param[RSZ_B].rsz_sdr_oft_c = line_len_c; + configure_resizer_out_params(resizer, RSZ_B, + ¶m->user_config.output2, 0, 1); + if (outformat2->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16) + resizer_enable_422_420_conversion(param, + RSZ_B, ENABLE); + else + resizer_enable_422_420_conversion(param, + RSZ_B, DISABLE); + } + + resizer_configure_common_in_params(resizer); + if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE) { + resizer_calculate_resize_ratios(resizer, RSZ_A); + resizer_calculate_sdram_offsets(resizer, RSZ_A); + /* Overriding resize ratio calculation */ + if (informat->code == V4L2_MBUS_FMT_UV8_1X8) { + param->rsz_rsc_param[RSZ_A].v_dif = + (((informat->height + 1) * 2) * 256) / + (param->rsz_rsc_param[RSZ_A].o_vsz + 1); + } + } + + if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE) { + resizer_calculate_resize_ratios(resizer, RSZ_B); + resizer_calculate_sdram_offsets(resizer, RSZ_B); + /* Overriding resize ratio calculation */ + if (informat->code == V4L2_MBUS_FMT_UV8_1X8) { + param->rsz_rsc_param[RSZ_B].v_dif = + (((informat->height + 1) * 2) * 256) / + (param->rsz_rsc_param[RSZ_B].o_vsz + 1); + } + } + if (param->user_config.frame_div_mode_en && + param->rsz_en[RSZ_A]) { + if (!param->rsz_rsc_param[RSZ_A].dscale_en) + ret = resizer_calculate_normal_f_div_param(dev, + informat->width, + param->rsz_rsc_param[RSZ_A].o_vsz + 1, + ¶m->rsz_rsc_param[RSZ_A]); + else + ret = resizer_calculate_down_scale_f_div_param(dev, + informat->width, + param->rsz_rsc_param[RSZ_A].o_vsz + 1, + ¶m->rsz_rsc_param[RSZ_A]); + if (ret) + return -EINVAL; + } + if (param->user_config.frame_div_mode_en && + param->rsz_en[RSZ_B]) { + if (!param->rsz_rsc_param[RSZ_B].dscale_en) + ret = resizer_calculate_normal_f_div_param(dev, + informat->width, + param->rsz_rsc_param[RSZ_B].o_vsz + 1, + ¶m->rsz_rsc_param[RSZ_B]); + else + ret = resizer_calculate_down_scale_f_div_param(dev, + informat->width, + param->rsz_rsc_param[RSZ_B].o_vsz + 1, + ¶m->rsz_rsc_param[RSZ_B]); + if (ret) + return -EINVAL; + } + param->rsz_common.passthrough = config->bypass; + if (config->bypass) + resizer_configure_passthru(resizer, 1); + return 0; +} + +static void +resizer_set_defualt_configuration(struct vpfe_resizer_device *resizer) +{ +#define WIDTH_I 640 +#define HEIGHT_I 480 +#define WIDTH_O 640 +#define HEIGHT_O 480 + const struct resizer_params rsz_default_config = { + .oper_mode = RESIZER_MODE_ONE_SHOT, + .rsz_common = { + .vsz = HEIGHT_I - 1, + .hsz = WIDTH_I - 1, + .src_img_fmt = RSZ_IMG_422, + .raw_flip = 1, /* flip preserve Raw format */ + .source = IPIPE_DATA, + .passthrough = BYPASS_OFF, + .yuv_y_max = 255, + .yuv_c_max = 255, + .rsz_seq_crv = DISABLE, + .out_chr_pos = VPFE_IPIPE_YUV422_CHR_POS_COSITE, + }, + .rsz_rsc_param = { + { + .h_flip = DISABLE, + .v_flip = DISABLE, + .cen = DISABLE, + .yen = DISABLE, + .o_vsz = HEIGHT_O - 1, + .o_hsz = WIDTH_O - 1, + .v_dif = 256, + .v_typ_y = VPFE_RSZ_INTP_CUBIC, + .h_typ_c = VPFE_RSZ_INTP_CUBIC, + .h_dif = 256, + .h_typ_y = VPFE_RSZ_INTP_CUBIC, + .h_typ_c = VPFE_RSZ_INTP_CUBIC, + .h_dscale_ave_sz = + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + .v_dscale_ave_sz = + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + }, + { + .h_flip = DISABLE, + .v_flip = DISABLE, + .cen = DISABLE, + .yen = DISABLE, + .o_vsz = HEIGHT_O - 1, + .o_hsz = WIDTH_O - 1, + .v_dif = 256, + .v_typ_y = VPFE_RSZ_INTP_CUBIC, + .h_typ_c = VPFE_RSZ_INTP_CUBIC, + .h_dif = 256, + .h_typ_y = VPFE_RSZ_INTP_CUBIC, + .h_typ_c = VPFE_RSZ_INTP_CUBIC, + .h_dscale_ave_sz = + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + .v_dscale_ave_sz = + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + }, + }, + .rsz2rgb = { + { + .rgb_en = DISABLE + }, + { + .rgb_en = DISABLE + } + }, + .ext_mem_param = { + { + .rsz_sdr_oft_y = WIDTH_O << 1, + .rsz_sdr_ptr_e_y = HEIGHT_O, + .rsz_sdr_oft_c = WIDTH_O, + .rsz_sdr_ptr_e_c = HEIGHT_O >> 1, + }, + { + .rsz_sdr_oft_y = WIDTH_O << 1, + .rsz_sdr_ptr_e_y = HEIGHT_O, + .rsz_sdr_oft_c = WIDTH_O, + .rsz_sdr_ptr_e_c = HEIGHT_O, + }, + }, + .rsz_en[0] = ENABLE, + .rsz_en[1] = DISABLE, + .user_config = { + .output1 = { + .v_typ_y = VPFE_RSZ_INTP_CUBIC, + .v_typ_c = VPFE_RSZ_INTP_CUBIC, + .h_typ_y = VPFE_RSZ_INTP_CUBIC, + .h_typ_c = VPFE_RSZ_INTP_CUBIC, + .h_dscale_ave_sz = + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + .v_dscale_ave_sz = + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + }, + .output2 = { + .v_typ_y = VPFE_RSZ_INTP_CUBIC, + .v_typ_c = VPFE_RSZ_INTP_CUBIC, + .h_typ_y = VPFE_RSZ_INTP_CUBIC, + .h_typ_c = VPFE_RSZ_INTP_CUBIC, + .h_dscale_ave_sz = + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + .v_dscale_ave_sz = + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + }, + .yuv_y_max = 255, + .yuv_c_max = 255, + .out_chr_pos = VPFE_IPIPE_YUV422_CHR_POS_COSITE, + }, + }; + memset(&resizer->config, 0, sizeof(struct resizer_params)); + memcpy(&resizer->config, &rsz_default_config, + sizeof(struct resizer_params)); +} + +/* + * resizer_set_configuration() - set resizer config + * @resizer: vpfe resizer device pointer. + * @chan_config: resizer channel configuration. + */ +static int +resizer_set_configuration(struct vpfe_resizer_device *resizer, + struct vpfe_rsz_config *chan_config) +{ + if (!chan_config->config) + resizer_set_defualt_configuration(resizer); + else + if (copy_from_user(&resizer->config.user_config, + chan_config->config, sizeof(struct vpfe_rsz_config_params))) + return -EFAULT; + + return 0; +} + +/* + * resizer_get_configuration() - get resizer config + * @resizer: vpfe resizer device pointer. + * @channel: image processor logical channel. + * @chan_config: resizer channel configuration. + */ +static int +resizer_get_configuration(struct vpfe_resizer_device *resizer, + struct vpfe_rsz_config *chan_config) +{ + struct device *dev = resizer->crop_resizer.subdev.v4l2_dev->dev; + + if (!chan_config->config) { + dev_err(dev, "Resizer channel invalid pointer\n"); + return -EINVAL; + } + + if (copy_to_user((void *)chan_config->config, + (void *)&resizer->config.user_config, + sizeof(struct vpfe_rsz_config_params))) { + dev_err(dev, "resizer_get_configuration: Error in copy to user\n"); + return -EFAULT; + } + + return 0; +} + +/* + * VPFE video operations + */ + +/* + * resizer_a_video_out_queue() - RESIZER-A video out queue + * @vpfe_dev: vpfe device pointer. + * @addr: buffer address. + */ +static int resizer_a_video_out_queue(struct vpfe_device *vpfe_dev, + unsigned long addr) +{ + struct vpfe_resizer_device *resizer = &vpfe_dev->vpfe_resizer; + + return resizer_set_outaddr(resizer->base_addr, + &resizer->config, RSZ_A, addr); +} + +/* + * resizer_b_video_out_queue() - RESIZER-B video out queue + * @vpfe_dev: vpfe device pointer. + * @addr: buffer address. + */ +static int resizer_b_video_out_queue(struct vpfe_device *vpfe_dev, + unsigned long addr) +{ + struct vpfe_resizer_device *resizer = &vpfe_dev->vpfe_resizer; + + return resizer_set_outaddr(resizer->base_addr, + &resizer->config, RSZ_B, addr); +} + +static const struct vpfe_video_operations resizer_a_video_ops = { + .queue = resizer_a_video_out_queue, +}; + +static const struct vpfe_video_operations resizer_b_video_ops = { + .queue = resizer_b_video_out_queue, +}; + +static void resizer_enable(struct vpfe_resizer_device *resizer, int en) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input; + unsigned char val; + + if (resizer->crop_resizer.input == RESIZER_CROP_INPUT_NONE) + return; + + if (resizer->crop_resizer.input == RESIZER_CROP_INPUT_IPIPEIF && + ipipeif_sink == IPIPEIF_INPUT_MEMORY) { + do { + val = regr_rsz(resizer->base_addr, RSZ_SRC_EN); + } while (val); + + if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE) { + do { + val = regr_rsz(resizer->base_addr, RSZ_A); + } while (val); + } + if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE) { + do { + val = regr_rsz(resizer->base_addr, RSZ_B); + } while (val); + } + } + if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE) + rsz_enable(resizer->base_addr, RSZ_A, en); + + if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE) + rsz_enable(resizer->base_addr, RSZ_B, en); +} + + +/* + * resizer_ss_isr() - resizer module single-shot buffer scheduling isr + * @resizer: vpfe resizer device pointer. + */ +static void resizer_ss_isr(struct vpfe_resizer_device *resizer) +{ + struct vpfe_video_device *video_out = &resizer->resizer_a.video_out; + struct vpfe_video_device *video_out2 = &resizer->resizer_b.video_out; + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + struct vpfe_pipeline *pipe = &video_out->pipe; + u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input; + u32 val; + + if (ipipeif_sink != IPIPEIF_INPUT_MEMORY) + return; + + if (resizer->resizer_a.output == RESIZER_OUPUT_MEMORY) { + val = vpss_dma_complete_interrupt(); + if (val != 0 && val != 2) + return; + } + + if (resizer->resizer_a.output == RESIZER_OUPUT_MEMORY) { + spin_lock(&video_out->dma_queue_lock); + vpfe_video_process_buffer_complete(video_out); + video_out->state = VPFE_VIDEO_BUFFER_NOT_QUEUED; + vpfe_video_schedule_next_buffer(video_out); + spin_unlock(&video_out->dma_queue_lock); + } + + /* If resizer B is enabled */ + if (pipe->output_num > 1 && resizer->resizer_b.output == + RESIZER_OUPUT_MEMORY) { + spin_lock(&video_out->dma_queue_lock); + vpfe_video_process_buffer_complete(video_out2); + video_out2->state = VPFE_VIDEO_BUFFER_NOT_QUEUED; + vpfe_video_schedule_next_buffer(video_out2); + spin_unlock(&video_out2->dma_queue_lock); + } + + /* start HW if buffers are queued */ + if (vpfe_video_is_pipe_ready(pipe) && + resizer->resizer_a.output == RESIZER_OUPUT_MEMORY) { + resizer_enable(resizer, 1); + vpfe_ipipe_enable(vpfe_dev, 1); + vpfe_ipipeif_enable(vpfe_dev); + } +} + +/* + * vpfe_resizer_buffer_isr() - resizer module buffer scheduling isr + * @resizer: vpfe resizer device pointer. + */ +void vpfe_resizer_buffer_isr(struct vpfe_resizer_device *resizer) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + struct vpfe_video_device *video_out = &resizer->resizer_a.video_out; + struct vpfe_video_device *video_out2 = &resizer->resizer_b.video_out; + struct vpfe_pipeline *pipe = &resizer->resizer_a.video_out.pipe; + enum v4l2_field field; + int fid; + + if (!video_out->started) + return; + + if (resizer->crop_resizer.input == RESIZER_CROP_INPUT_NONE) + return; + + field = video_out->fmt.fmt.pix.field; + if (field == V4L2_FIELD_NONE) { + /* handle progressive frame capture */ + if (video_out->cur_frm != video_out->next_frm) { + vpfe_video_process_buffer_complete(video_out); + if (pipe->output_num > 1) + vpfe_video_process_buffer_complete(video_out2); + } + + video_out->skip_frame_count--; + if (!video_out->skip_frame_count) { + video_out->skip_frame_count = + video_out->skip_frame_count_init; + rsz_src_enable(resizer->base_addr, 1); + } else { + rsz_src_enable(resizer->base_addr, 0); + } + return; + } + + /* handle interlaced frame capture */ + fid = vpfe_isif_get_fid(vpfe_dev); + + /* switch the software maintained field id */ + video_out->field_id ^= 1; + if (fid == video_out->field_id) { + /* + * we are in-sync here,continue. + * One frame is just being captured. If the + * next frame is available, release the current + * frame and move on + */ + if (fid == 0 && video_out->cur_frm != video_out->next_frm) { + vpfe_video_process_buffer_complete(video_out); + if (pipe->output_num > 1) + vpfe_video_process_buffer_complete(video_out2); + } + } else if (fid == 0) { + /* + * out of sync. Recover from any hardware out-of-sync. + * May loose one frame + */ + video_out->field_id = fid; + } +} + +/* + * vpfe_resizer_dma_isr() - resizer module dma isr + * @resizer: vpfe resizer device pointer. + */ +void vpfe_resizer_dma_isr(struct vpfe_resizer_device *resizer) +{ + struct vpfe_video_device *video_out2 = &resizer->resizer_b.video_out; + struct vpfe_video_device *video_out = &resizer->resizer_a.video_out; + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + struct vpfe_pipeline *pipe = &video_out->pipe; + int schedule_capture = 0; + enum v4l2_field field; + int fid; + + if (!video_out->started) + return; + + if (pipe->state == VPFE_PIPELINE_STREAM_SINGLESHOT) { + resizer_ss_isr(resizer); + return; + } + + field = video_out->fmt.fmt.pix.field; + if (field == V4L2_FIELD_NONE) { + if (!list_empty(&video_out->dma_queue) && + video_out->cur_frm == video_out->next_frm) + schedule_capture = 1; + } else { + fid = vpfe_isif_get_fid(vpfe_dev); + if (fid == video_out->field_id) { + /* we are in-sync here,continue */ + if (fid == 1 && !list_empty(&video_out->dma_queue) && + video_out->cur_frm == video_out->next_frm) + schedule_capture = 1; + } + } + + if (!schedule_capture) + return; + + spin_lock(&video_out->dma_queue_lock); + vpfe_video_schedule_next_buffer(video_out); + spin_unlock(&video_out->dma_queue_lock); + if (pipe->output_num > 1) { + spin_lock(&video_out2->dma_queue_lock); + vpfe_video_schedule_next_buffer(video_out2); + spin_unlock(&video_out2->dma_queue_lock); + } +} + +/* + * V4L2 subdev operations + */ + +/* + * resizer_ioctl() - Handle resizer module private ioctl's + * @sd: pointer to v4l2 subdev structure + * @cmd: configuration command + * @arg: configuration argument + */ +static long resizer_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + struct device *dev = resizer->crop_resizer.subdev.v4l2_dev->dev; + struct vpfe_rsz_config *user_config; + int ret = -ENOIOCTLCMD; + + if (&resizer->crop_resizer.subdev != sd) + return ret; + + switch (cmd) { + case VIDIOC_VPFE_RSZ_S_CONFIG: + user_config = (struct vpfe_rsz_config *)arg; + ret = resizer_set_configuration(resizer, user_config); + break; + + case VIDIOC_VPFE_RSZ_G_CONFIG: + user_config = (struct vpfe_rsz_config *)arg; + if (!user_config->config) { + dev_err(dev, "error in VIDIOC_VPFE_RSZ_G_CONFIG\n"); + return -EINVAL; + } + ret = resizer_get_configuration(resizer, user_config); + break; + } + return ret; +} + +static int resizer_do_hw_setup(struct vpfe_resizer_device *resizer) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input; + u16 ipipeif_source = vpfe_dev->vpfe_ipipeif.output; + struct resizer_params *param = &resizer->config; + int ret = 0; + + if (resizer->resizer_a.output == RESIZER_OUPUT_MEMORY || + resizer->resizer_b.output == RESIZER_OUPUT_MEMORY) { + if (ipipeif_sink == IPIPEIF_INPUT_MEMORY && + ipipeif_source == IPIPEIF_OUTPUT_RESIZER) + ret = resizer_configure_in_single_shot_mode(resizer); + else + ret = resizer_configure_in_continious_mode(resizer); + if (ret) + return ret; + ret = config_rsz_hw(resizer, param); + } + return ret; +} + +/* + * resizer_set_stream() - Enable/Disable streaming on resizer subdev + * @sd: pointer to v4l2 subdev structure + * @enable: 1 == Enable, 0 == Disable + */ +static int resizer_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + + if (&resizer->crop_resizer.subdev != sd) + return 0; + + if (resizer->resizer_a.output != RESIZER_OUPUT_MEMORY) + return 0; + + switch (enable) { + case 1: + if (resizer_do_hw_setup(resizer) < 0) + return -EINVAL; + resizer_enable(resizer, enable); + break; + + case 0: + resizer_enable(resizer, enable); + break; + } + + return 0; +} + +/* + * __resizer_get_format() - helper function for getting resizer format + * @sd: pointer to subdev. + * @fh: V4L2 subdev file handle. + * @pad: pad number. + * @which: wanted subdev format. + * Retun wanted mbus frame format. + */ +static struct v4l2_mbus_framefmt * +__resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(fh, pad); + if (&resizer->crop_resizer.subdev == sd) + return &resizer->crop_resizer.formats[pad]; + if (&resizer->resizer_a.subdev == sd) + return &resizer->resizer_a.formats[pad]; + if (&resizer->resizer_b.subdev == sd) + return &resizer->resizer_b.formats[pad]; + return NULL; +} + +/* + * resizer_try_format() - Handle try format by pad subdev method + * @sd: pointer to subdev. + * @fh: V4L2 subdev file handle. + * @pad: pad num. + * @fmt: pointer to v4l2 format structure. + * @which: wanted subdev format. + */ +static void +resizer_try_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + unsigned int pad, struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) +{ + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + unsigned int max_out_height; + unsigned int max_out_width; + unsigned int i; + + if ((&resizer->resizer_a.subdev == sd && pad == RESIZER_PAD_SINK) || + (&resizer->resizer_b.subdev == sd && pad == RESIZER_PAD_SINK) || + (&resizer->crop_resizer.subdev == sd && + (pad == RESIZER_CROP_PAD_SOURCE || + pad == RESIZER_CROP_PAD_SOURCE2 || pad == RESIZER_CROP_PAD_SINK))) { + for (i = 0; i < ARRAY_SIZE(resizer_input_formats); i++) { + if (fmt->code == resizer_input_formats[i]) + break; + } + /* If not found, use UYVY as default */ + if (i >= ARRAY_SIZE(resizer_input_formats)) + fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + + fmt->width = clamp_t(u32, fmt->width, MIN_IN_WIDTH, + MAX_IN_WIDTH); + fmt->height = clamp_t(u32, fmt->height, MIN_IN_HEIGHT, + MAX_IN_HEIGHT); + } else if (&resizer->resizer_a.subdev == sd && + pad == RESIZER_PAD_SOURCE) { + max_out_width = IPIPE_MAX_OUTPUT_WIDTH_A; + max_out_height = IPIPE_MAX_OUTPUT_HEIGHT_A; + + for (i = 0; i < ARRAY_SIZE(resizer_output_formats); i++) { + if (fmt->code == resizer_output_formats[i]) + break; + } + /* If not found, use UYVY as default */ + if (i >= ARRAY_SIZE(resizer_output_formats)) + fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + + fmt->width = clamp_t(u32, fmt->width, MIN_OUT_WIDTH, + max_out_width); + fmt->width &= ~15; + fmt->height = clamp_t(u32, fmt->height, MIN_OUT_HEIGHT, + max_out_height); + } else if (&resizer->resizer_b.subdev == sd && + pad == RESIZER_PAD_SOURCE) { + max_out_width = IPIPE_MAX_OUTPUT_WIDTH_B; + max_out_height = IPIPE_MAX_OUTPUT_HEIGHT_B; + + for (i = 0; i < ARRAY_SIZE(resizer_output_formats); i++) { + if (fmt->code == resizer_output_formats[i]) + break; + } + /* If not found, use UYVY as default */ + if (i >= ARRAY_SIZE(resizer_output_formats)) + fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + + fmt->width = clamp_t(u32, fmt->width, MIN_OUT_WIDTH, + max_out_width); + fmt->width &= ~15; + fmt->height = clamp_t(u32, fmt->height, MIN_OUT_HEIGHT, + max_out_height); + } +} + +/* + * resizer_set_format() - Handle set format by pads subdev method + * @sd: pointer to v4l2 subdev structure + * @fh: V4L2 subdev file handle + * @fmt: pointer to v4l2 subdev format structure + * return -EINVAL or zero on success + */ +static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __resizer_get_format(sd, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + resizer_try_format(sd, fh, fmt->pad, &fmt->format, fmt->which); + *format = fmt->format; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + if (&resizer->crop_resizer.subdev == sd) { + if (fmt->pad == RESIZER_CROP_PAD_SINK) { + resizer->crop_resizer.formats[fmt->pad] = fmt->format; + } else if (fmt->pad == RESIZER_CROP_PAD_SOURCE && + resizer->crop_resizer.output == RESIZER_A) { + resizer->crop_resizer.formats[fmt->pad] = fmt->format; + resizer->crop_resizer. + formats[RESIZER_CROP_PAD_SOURCE2] = fmt->format; + } else if (fmt->pad == RESIZER_CROP_PAD_SOURCE2 && + resizer->crop_resizer.output2 == RESIZER_B) { + resizer->crop_resizer.formats[fmt->pad] = fmt->format; + resizer->crop_resizer. + formats[RESIZER_CROP_PAD_SOURCE] = fmt->format; + } else { + return -EINVAL; + } + } else if (&resizer->resizer_a.subdev == sd) { + if (fmt->pad == RESIZER_PAD_SINK) + resizer->resizer_a.formats[fmt->pad] = fmt->format; + else if (fmt->pad == RESIZER_PAD_SOURCE) + resizer->resizer_a.formats[fmt->pad] = fmt->format; + else + return -EINVAL; + } else if (&resizer->resizer_b.subdev == sd) { + if (fmt->pad == RESIZER_PAD_SINK) + resizer->resizer_b.formats[fmt->pad] = fmt->format; + else if (fmt->pad == RESIZER_PAD_SOURCE) + resizer->resizer_b.formats[fmt->pad] = fmt->format; + else + return -EINVAL; + } else { + return -EINVAL; + } + + return 0; +} + +/* + * resizer_get_format() - Retrieve the video format on a pad + * @sd: pointer to v4l2 subdev structure. + * @fh: V4L2 subdev file handle. + * @fmt: pointer to v4l2 subdev format structure + * return -EINVAL or zero on success + */ +static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *format; + + format = __resizer_get_format(sd, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + fmt->format = *format; + + return 0; +} + +/* + * resizer_enum_frame_size() - enum frame sizes on pads + * @sd: Pointer to subdevice. + * @fh: V4L2 subdev file handle. + * @code: pointer to v4l2_subdev_frame_size_enum structure. + */ +static int resizer_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct v4l2_mbus_framefmt format; + + if (fse->index != 0) + return -EINVAL; + + format.code = fse->code; + format.width = 1; + format.height = 1; + resizer_try_format(sd, fh, fse->pad, &format, + V4L2_SUBDEV_FORMAT_TRY); + fse->min_width = format.width; + fse->min_height = format.height; + + if (format.code != fse->code) + return -EINVAL; + + format.code = fse->code; + format.width = -1; + format.height = -1; + resizer_try_format(sd, fh, fse->pad, &format, + V4L2_SUBDEV_FORMAT_TRY); + fse->max_width = format.width; + fse->max_height = format.height; + + return 0; +} + +/* + * resizer_enum_mbus_code() - enum mbus codes for pads + * @sd: Pointer to subdevice. + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + */ +static int resizer_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad == RESIZER_PAD_SINK) { + if (code->index >= ARRAY_SIZE(resizer_input_formats)) + return -EINVAL; + + code->code = resizer_input_formats[code->index]; + } else if (code->pad == RESIZER_PAD_SOURCE) { + if (code->index >= ARRAY_SIZE(resizer_output_formats)) + return -EINVAL; + + code->code = resizer_output_formats[code->index]; + } + + return 0; +} + +/* + * resizer_init_formats() - Initialize formats on all pads + * @sd: Pointer to subdevice. + * @fh: V4L2 subdev file handle. + * + * Initialize all pad formats with default values. If fh is not NULL, try + * formats are initialized on the file handle. Otherwise active formats are + * initialized on the device. + */ +static int resizer_init_formats(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + __u32 which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + struct v4l2_subdev_format format; + + if (&resizer->crop_resizer.subdev == sd) { + memset(&format, 0, sizeof(format)); + format.pad = RESIZER_CROP_PAD_SINK; + format.which = which; + format.format.code = V4L2_MBUS_FMT_YUYV8_2X8; + format.format.width = MAX_IN_WIDTH; + format.format.height = MAX_IN_HEIGHT; + resizer_set_format(sd, fh, &format); + + memset(&format, 0, sizeof(format)); + format.pad = RESIZER_CROP_PAD_SOURCE; + format.which = which; + format.format.code = V4L2_MBUS_FMT_UYVY8_2X8; + format.format.width = MAX_IN_WIDTH; + format.format.height = MAX_IN_WIDTH; + resizer_set_format(sd, fh, &format); + + memset(&format, 0, sizeof(format)); + format.pad = RESIZER_CROP_PAD_SOURCE2; + format.which = which; + format.format.code = V4L2_MBUS_FMT_UYVY8_2X8; + format.format.width = MAX_IN_WIDTH; + format.format.height = MAX_IN_WIDTH; + resizer_set_format(sd, fh, &format); + } else if (&resizer->resizer_a.subdev == sd) { + memset(&format, 0, sizeof(format)); + format.pad = RESIZER_PAD_SINK; + format.which = which; + format.format.code = V4L2_MBUS_FMT_YUYV8_2X8; + format.format.width = MAX_IN_WIDTH; + format.format.height = MAX_IN_HEIGHT; + resizer_set_format(sd, fh, &format); + + memset(&format, 0, sizeof(format)); + format.pad = RESIZER_PAD_SOURCE; + format.which = which; + format.format.code = V4L2_MBUS_FMT_UYVY8_2X8; + format.format.width = IPIPE_MAX_OUTPUT_WIDTH_A; + format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_A; + resizer_set_format(sd, fh, &format); + } else if (&resizer->resizer_b.subdev == sd) { + memset(&format, 0, sizeof(format)); + format.pad = RESIZER_PAD_SINK; + format.which = which; + format.format.code = V4L2_MBUS_FMT_YUYV8_2X8; + format.format.width = MAX_IN_WIDTH; + format.format.height = MAX_IN_HEIGHT; + resizer_set_format(sd, fh, &format); + + memset(&format, 0, sizeof(format)); + format.pad = RESIZER_PAD_SOURCE; + format.which = which; + format.format.code = V4L2_MBUS_FMT_UYVY8_2X8; + format.format.width = IPIPE_MAX_OUTPUT_WIDTH_B; + format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_B; + resizer_set_format(sd, fh, &format); + } + + return 0; +} + +/* subdev core operations */ +static const struct v4l2_subdev_core_ops resizer_v4l2_core_ops = { + .ioctl = resizer_ioctl, +}; + +/* subdev internal operations */ +static const struct v4l2_subdev_internal_ops resizer_v4l2_internal_ops = { + .open = resizer_init_formats, +}; + +/* subdev video operations */ +static const struct v4l2_subdev_video_ops resizer_v4l2_video_ops = { + .s_stream = resizer_set_stream, +}; + +/* subdev pad operations */ +static const struct v4l2_subdev_pad_ops resizer_v4l2_pad_ops = { + .enum_mbus_code = resizer_enum_mbus_code, + .enum_frame_size = resizer_enum_frame_size, + .get_fmt = resizer_get_format, + .set_fmt = resizer_set_format, +}; + +/* subdev operations */ +static const struct v4l2_subdev_ops resizer_v4l2_ops = { + .core = &resizer_v4l2_core_ops, + .video = &resizer_v4l2_video_ops, + .pad = &resizer_v4l2_pad_ops, +}; + +/* + * Media entity operations + */ + +/* + * resizer_link_setup() - Setup resizer connections + * @entity: Pointer to media entity structure + * @local: Pointer to local pad array + * @remote: Pointer to remote pad array + * @flags: Link flags + * return -EINVAL or zero on success + */ +static int resizer_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + u16 ipipeif_source = vpfe_dev->vpfe_ipipeif.output; + u16 ipipe_source = vpfe_dev->vpfe_ipipe.output; + + if (&resizer->crop_resizer.subdev == sd) { + switch (local->index | media_entity_type(remote->entity)) { + case RESIZER_CROP_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + resizer->crop_resizer.input = + RESIZER_CROP_INPUT_NONE; + break; + } + + if (resizer->crop_resizer.input != + RESIZER_CROP_INPUT_NONE) + return -EBUSY; + if (ipipeif_source == IPIPEIF_OUTPUT_RESIZER) + resizer->crop_resizer.input = + RESIZER_CROP_INPUT_IPIPEIF; + else if (ipipe_source == IPIPE_OUTPUT_RESIZER) + resizer->crop_resizer.input = + RESIZER_CROP_INPUT_IPIPE; + else + return -EINVAL; + break; + + case RESIZER_CROP_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + resizer->crop_resizer.output = + RESIZER_CROP_OUTPUT_NONE; + break; + } + if (resizer->crop_resizer.output != + RESIZER_CROP_OUTPUT_NONE) + return -EBUSY; + resizer->crop_resizer.output = RESIZER_A; + break; + + case RESIZER_CROP_PAD_SOURCE2 | MEDIA_ENT_T_V4L2_SUBDEV: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + resizer->crop_resizer.output2 = + RESIZER_CROP_OUTPUT_NONE; + break; + } + if (resizer->crop_resizer.output2 != + RESIZER_CROP_OUTPUT_NONE) + return -EBUSY; + resizer->crop_resizer.output2 = RESIZER_B; + break; + + default: + return -EINVAL; + } + } else if (&resizer->resizer_a.subdev == sd) { + switch (local->index | media_entity_type(remote->entity)) { + case RESIZER_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + resizer->resizer_a.input = RESIZER_INPUT_NONE; + break; + } + if (resizer->resizer_a.input != RESIZER_INPUT_NONE) + return -EBUSY; + resizer->resizer_a.input = RESIZER_INPUT_CROP_RESIZER; + break; + + case RESIZER_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + resizer->resizer_a.output = RESIZER_OUTPUT_NONE; + break; + } + if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE) + return -EBUSY; + resizer->resizer_a.output = RESIZER_OUPUT_MEMORY; + break; + + default: + return -EINVAL; + } + } else if (&resizer->resizer_b.subdev == sd) { + switch (local->index | media_entity_type(remote->entity)) { + case RESIZER_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + resizer->resizer_b.input = RESIZER_INPUT_NONE; + break; + } + if (resizer->resizer_b.input != RESIZER_INPUT_NONE) + return -EBUSY; + resizer->resizer_b.input = RESIZER_INPUT_CROP_RESIZER; + break; + + case RESIZER_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + resizer->resizer_b.output = RESIZER_OUTPUT_NONE; + break; + } + if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE) + return -EBUSY; + resizer->resizer_b.output = RESIZER_OUPUT_MEMORY; + break; + + default: + return -EINVAL; + } + } else { + return -EINVAL; + } + + return 0; +} + +static const struct media_entity_operations resizer_media_ops = { + .link_setup = resizer_link_setup, +}; + +/* + * vpfe_resizer_unregister_entities() - Unregister entity + * @vpfe_rsz - pointer to resizer subdevice structure. + */ +void vpfe_resizer_unregister_entities(struct vpfe_resizer_device *vpfe_rsz) +{ + /* unregister video devices */ + vpfe_video_unregister(&vpfe_rsz->resizer_a.video_out); + vpfe_video_unregister(&vpfe_rsz->resizer_b.video_out); + + /* cleanup entity */ + media_entity_cleanup(&vpfe_rsz->crop_resizer.subdev.entity); + media_entity_cleanup(&vpfe_rsz->resizer_a.subdev.entity); + media_entity_cleanup(&vpfe_rsz->resizer_b.subdev.entity); + /* unregister subdev */ + v4l2_device_unregister_subdev(&vpfe_rsz->crop_resizer.subdev); + v4l2_device_unregister_subdev(&vpfe_rsz->resizer_a.subdev); + v4l2_device_unregister_subdev(&vpfe_rsz->resizer_b.subdev); +} + +/* + * vpfe_resizer_register_entities() - Register entity + * @resizer - pointer to resizer devive. + * @vdev: pointer to v4l2 device structure. + */ +int vpfe_resizer_register_entities(struct vpfe_resizer_device *resizer, + struct v4l2_device *vdev) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + unsigned int flags = 0; + int ret; + + /* Register the crop resizer subdev */ + ret = v4l2_device_register_subdev(vdev, &resizer->crop_resizer.subdev); + if (ret < 0) { + pr_err("Failed to register crop resizer as v4l2-subdev\n"); + return ret; + } + /* Register Resizer-A subdev */ + ret = v4l2_device_register_subdev(vdev, &resizer->resizer_a.subdev); + if (ret < 0) { + pr_err("Failed to register resizer-a as v4l2-subdev\n"); + return ret; + } + /* Register Resizer-B subdev */ + ret = v4l2_device_register_subdev(vdev, &resizer->resizer_b.subdev); + if (ret < 0) { + pr_err("Failed to register resizer-b as v4l2-subdev\n"); + return ret; + } + /* Register video-out device for resizer-a */ + ret = vpfe_video_register(&resizer->resizer_a.video_out, vdev); + if (ret) { + pr_err("Failed to register RSZ-A video-out device\n"); + goto out_video_out2_register; + } + resizer->resizer_a.video_out.vpfe_dev = vpfe_dev; + + /* Register video-out device for resizer-b */ + ret = vpfe_video_register(&resizer->resizer_b.video_out, vdev); + if (ret) { + pr_err("Failed to register RSZ-B video-out device\n"); + goto out_video_out2_register; + } + resizer->resizer_b.video_out.vpfe_dev = vpfe_dev; + + /* create link between Resizer Crop----> Resizer A*/ + ret = media_entity_create_link(&resizer->crop_resizer.subdev.entity, 1, + &resizer->resizer_a.subdev.entity, + 0, flags); + if (ret < 0) + goto out_create_link; + + /* create link between Resizer Crop----> Resizer B*/ + ret = media_entity_create_link(&resizer->crop_resizer.subdev.entity, 2, + &resizer->resizer_b.subdev.entity, + 0, flags); + if (ret < 0) + goto out_create_link; + + /* create link between Resizer A ----> video out */ + ret = media_entity_create_link(&resizer->resizer_a.subdev.entity, 1, + &resizer->resizer_a.video_out.video_dev.entity, 0, flags); + if (ret < 0) + goto out_create_link; + + /* create link between Resizer B ----> video out */ + ret = media_entity_create_link(&resizer->resizer_b.subdev.entity, 1, + &resizer->resizer_b.video_out.video_dev.entity, 0, flags); + if (ret < 0) + goto out_create_link; + + return 0; + +out_create_link: + vpfe_video_unregister(&resizer->resizer_b.video_out); +out_video_out2_register: + vpfe_video_unregister(&resizer->resizer_a.video_out); + media_entity_cleanup(&resizer->crop_resizer.subdev.entity); + media_entity_cleanup(&resizer->resizer_a.subdev.entity); + media_entity_cleanup(&resizer->resizer_b.subdev.entity); + v4l2_device_unregister_subdev(&resizer->crop_resizer.subdev); + v4l2_device_unregister_subdev(&resizer->resizer_a.subdev); + v4l2_device_unregister_subdev(&resizer->resizer_b.subdev); + return ret; +} + +/* + * vpfe_resizer_init() - resizer device initialization. + * @vpfe_rsz - pointer to resizer device + * @pdev: platform device pointer. + */ +int vpfe_resizer_init(struct vpfe_resizer_device *vpfe_rsz, + struct platform_device *pdev) +{ + struct v4l2_subdev *sd = &vpfe_rsz->crop_resizer.subdev; + struct media_pad *pads = &vpfe_rsz->crop_resizer.pads[0]; + struct media_entity *me = &sd->entity; + static resource_size_t res_len; + struct resource *res; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 5); + if (!res) + return -ENOENT; + + res_len = resource_size(res); + res = request_mem_region(res->start, res_len, res->name); + if (!res) + return -EBUSY; + + vpfe_rsz->base_addr = ioremap_nocache(res->start, res_len); + if (!vpfe_rsz->base_addr) + return -EBUSY; + + v4l2_subdev_init(sd, &resizer_v4l2_ops); + sd->internal_ops = &resizer_v4l2_internal_ops; + strlcpy(sd->name, "DAVINCI RESIZER CROP", sizeof(sd->name)); + sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ + v4l2_set_subdevdata(sd, vpfe_rsz); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + pads[RESIZER_CROP_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[RESIZER_CROP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + pads[RESIZER_CROP_PAD_SOURCE2].flags = MEDIA_PAD_FL_SOURCE; + + vpfe_rsz->crop_resizer.input = RESIZER_CROP_INPUT_NONE; + vpfe_rsz->crop_resizer.output = RESIZER_CROP_OUTPUT_NONE; + vpfe_rsz->crop_resizer.output2 = RESIZER_CROP_OUTPUT_NONE; + vpfe_rsz->crop_resizer.rsz_device = vpfe_rsz; + me->ops = &resizer_media_ops; + ret = media_entity_init(me, RESIZER_CROP_PADS_NUM, pads, 0); + if (ret) + return ret; + + sd = &vpfe_rsz->resizer_a.subdev; + pads = &vpfe_rsz->resizer_a.pads[0]; + me = &sd->entity; + + v4l2_subdev_init(sd, &resizer_v4l2_ops); + sd->internal_ops = &resizer_v4l2_internal_ops; + strlcpy(sd->name, "DAVINCI RESIZER A", sizeof(sd->name)); + sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ + v4l2_set_subdevdata(sd, vpfe_rsz); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + pads[RESIZER_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[RESIZER_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + vpfe_rsz->resizer_a.input = RESIZER_INPUT_NONE; + vpfe_rsz->resizer_a.output = RESIZER_OUTPUT_NONE; + vpfe_rsz->resizer_a.rsz_device = vpfe_rsz; + me->ops = &resizer_media_ops; + ret = media_entity_init(me, RESIZER_PADS_NUM, pads, 0); + if (ret) + return ret; + + sd = &vpfe_rsz->resizer_b.subdev; + pads = &vpfe_rsz->resizer_b.pads[0]; + me = &sd->entity; + + v4l2_subdev_init(sd, &resizer_v4l2_ops); + sd->internal_ops = &resizer_v4l2_internal_ops; + strlcpy(sd->name, "DAVINCI RESIZER B", sizeof(sd->name)); + sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ + v4l2_set_subdevdata(sd, vpfe_rsz); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + pads[RESIZER_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[RESIZER_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + vpfe_rsz->resizer_b.input = RESIZER_INPUT_NONE; + vpfe_rsz->resizer_b.output = RESIZER_OUTPUT_NONE; + vpfe_rsz->resizer_b.rsz_device = vpfe_rsz; + me->ops = &resizer_media_ops; + ret = media_entity_init(me, RESIZER_PADS_NUM, pads, 0); + if (ret) + return ret; + + vpfe_rsz->resizer_a.video_out.ops = &resizer_a_video_ops; + vpfe_rsz->resizer_a.video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ret = vpfe_video_init(&vpfe_rsz->resizer_a.video_out, "RSZ-A"); + if (ret) { + pr_err("Failed to init RSZ video-out device\n"); + return ret; + } + vpfe_rsz->resizer_b.video_out.ops = &resizer_b_video_ops; + vpfe_rsz->resizer_b.video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ret = vpfe_video_init(&vpfe_rsz->resizer_b.video_out, "RSZ-B"); + if (ret) { + pr_err("Failed to init RSZ video-out2 device\n"); + return ret; + } + memset(&vpfe_rsz->config, 0, sizeof(struct resizer_params)); + + return 0; +} + +void +vpfe_resizer_cleanup(struct vpfe_resizer_device *vpfe_rsz, + struct platform_device *pdev) +{ + struct resource *res; + + iounmap(vpfe_rsz->base_addr); + res = platform_get_resource(pdev, IORESOURCE_MEM, 5); + if (res) + release_mem_region(res->start, + res->end - res->start + 1); +} diff --git a/drivers/staging/media/davinci_vpfe/dm365_resizer.h b/drivers/staging/media/davinci_vpfe/dm365_resizer.h new file mode 100644 index 000000000000..59a79422b914 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_resizer.h @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + */ + +#ifndef _DAVINCI_VPFE_DM365_RESIZER_H +#define _DAVINCI_VPFE_DM365_RESIZER_H + +enum resizer_oper_mode { + RESIZER_MODE_CONTINIOUS = 0, + RESIZER_MODE_ONE_SHOT = 1, +}; + +struct f_div_pass { + unsigned int o_hsz; + unsigned int i_hps; + unsigned int h_phs; + unsigned int src_hps; + unsigned int src_hsz; +}; + +#define MAX_PASSES 2 + +struct f_div_param { + unsigned char en; + unsigned int num_passes; + struct f_div_pass pass[MAX_PASSES]; +}; + +/* Resizer Rescale Parameters*/ +struct resizer_scale_param { + bool h_flip; + bool v_flip; + bool cen; + bool yen; + unsigned short i_vps; + unsigned short i_hps; + unsigned short o_vsz; + unsigned short o_hsz; + unsigned short v_phs_y; + unsigned short v_phs_c; + unsigned short v_dif; + /* resize method - Luminance */ + enum vpfe_rsz_intp_t v_typ_y; + /* resize method - Chrominance */ + enum vpfe_rsz_intp_t v_typ_c; + /* vertical lpf intensity - Luminance */ + unsigned char v_lpf_int_y; + /* vertical lpf intensity - Chrominance */ + unsigned char v_lpf_int_c; + unsigned short h_phs; + unsigned short h_dif; + /* resize method - Luminance */ + enum vpfe_rsz_intp_t h_typ_y; + /* resize method - Chrominance */ + enum vpfe_rsz_intp_t h_typ_c; + /* horizontal lpf intensity - Luminance */ + unsigned char h_lpf_int_y; + /* horizontal lpf intensity - Chrominance */ + unsigned char h_lpf_int_c; + bool dscale_en; + enum vpfe_rsz_down_scale_ave_sz h_dscale_ave_sz; + enum vpfe_rsz_down_scale_ave_sz v_dscale_ave_sz; + /* store the calculated frame division parameter */ + struct f_div_param f_div; +}; + +enum resizer_rgb_t { + OUTPUT_32BIT, + OUTPUT_16BIT +}; + +enum resizer_rgb_msk_t { + NOMASK = 0, + MASKLAST2 = 1, +}; + +/* Resizer RGB Conversion Parameters */ +struct resizer_rgb { + bool rgb_en; + enum resizer_rgb_t rgb_typ; + enum resizer_rgb_msk_t rgb_msk0; + enum resizer_rgb_msk_t rgb_msk1; + unsigned int rgb_alpha_val; +}; + +/* Resizer External Memory Parameters */ +struct rsz_ext_mem_param { + unsigned int rsz_sdr_oft_y; + unsigned int rsz_sdr_ptr_s_y; + unsigned int rsz_sdr_ptr_e_y; + unsigned int rsz_sdr_oft_c; + unsigned int rsz_sdr_ptr_s_c; + unsigned int rsz_sdr_ptr_e_c; + /* offset to be added to buffer start when flipping for y/ycbcr */ + unsigned int flip_ofst_y; + /* offset to be added to buffer start when flipping for c */ + unsigned int flip_ofst_c; + /* c offset for YUV 420SP */ + unsigned int c_offset; + /* User Defined Y offset for YUV 420SP or YUV420ILE data */ + unsigned int user_y_ofst; + /* User Defined C offset for YUV 420SP data */ + unsigned int user_c_ofst; +}; + +enum rsz_data_source { + IPIPE_DATA, + IPIPEIF_DATA +}; + +enum rsz_src_img_fmt { + RSZ_IMG_422, + RSZ_IMG_420 +}; + +enum rsz_dpaths_bypass_t { + BYPASS_OFF = 0, + BYPASS_ON = 1, +}; + +struct rsz_common_params { + unsigned int vps; + unsigned int vsz; + unsigned int hps; + unsigned int hsz; + /* 420 or 422 */ + enum rsz_src_img_fmt src_img_fmt; + /* Y or C when src_fmt is 420, 0 - y, 1 - c */ + unsigned char y_c; + /* flip raw or ycbcr */ + unsigned char raw_flip; + /* IPIPE or IPIPEIF data */ + enum rsz_data_source source; + enum rsz_dpaths_bypass_t passthrough; + unsigned char yuv_y_min; + unsigned char yuv_y_max; + unsigned char yuv_c_min; + unsigned char yuv_c_max; + bool rsz_seq_crv; + enum vpfe_chr_pos out_chr_pos; +}; + +struct resizer_params { + enum resizer_oper_mode oper_mode; + struct rsz_common_params rsz_common; + struct resizer_scale_param rsz_rsc_param[2]; + struct resizer_rgb rsz2rgb[2]; + struct rsz_ext_mem_param ext_mem_param[2]; + bool rsz_en[2]; + struct vpfe_rsz_config_params user_config; +}; + +#define ENABLE 1 +#define DISABLE (!ENABLE) + +#define RESIZER_CROP_PAD_SINK 0 +#define RESIZER_CROP_PAD_SOURCE 1 +#define RESIZER_CROP_PAD_SOURCE2 2 + +#define RESIZER_CROP_PADS_NUM 3 + +enum resizer_crop_input_entity { + RESIZER_CROP_INPUT_NONE = 0, + RESIZER_CROP_INPUT_IPIPEIF = 1, + RESIZER_CROP_INPUT_IPIPE = 2, +}; + +enum resizer_crop_output_entity { + RESIZER_CROP_OUTPUT_NONE, + RESIZER_A, + RESIZER_B, +}; + +struct dm365_crop_resizer_device { + struct v4l2_subdev subdev; + struct media_pad pads[RESIZER_CROP_PADS_NUM]; + struct v4l2_mbus_framefmt formats[RESIZER_CROP_PADS_NUM]; + enum resizer_crop_input_entity input; + enum resizer_crop_output_entity output; + enum resizer_crop_output_entity output2; + struct vpfe_resizer_device *rsz_device; +}; + +#define RESIZER_PAD_SINK 0 +#define RESIZER_PAD_SOURCE 1 + +#define RESIZER_PADS_NUM 2 + +enum resizer_input_entity { + RESIZER_INPUT_NONE = 0, + RESIZER_INPUT_CROP_RESIZER = 1, +}; + +enum resizer_output_entity { + RESIZER_OUTPUT_NONE = 0, + RESIZER_OUPUT_MEMORY = 1, +}; + +struct dm365_resizer_device { + struct v4l2_subdev subdev; + struct media_pad pads[RESIZER_PADS_NUM]; + struct v4l2_mbus_framefmt formats[RESIZER_PADS_NUM]; + enum resizer_input_entity input; + enum resizer_output_entity output; + struct vpfe_video_device video_out; + struct vpfe_resizer_device *rsz_device; +}; + +struct vpfe_resizer_device { + struct dm365_crop_resizer_device crop_resizer; + struct dm365_resizer_device resizer_a; + struct dm365_resizer_device resizer_b; + struct resizer_params config; + void *__iomem base_addr; +}; + +int vpfe_resizer_init(struct vpfe_resizer_device *vpfe_rsz, + struct platform_device *pdev); +int vpfe_resizer_register_entities(struct vpfe_resizer_device *vpfe_rsz, + struct v4l2_device *v4l2_dev); +void vpfe_resizer_unregister_entities(struct vpfe_resizer_device *vpfe_rsz); +void vpfe_resizer_cleanup(struct vpfe_resizer_device *vpfe_rsz, + struct platform_device *pdev); +void vpfe_resizer_buffer_isr(struct vpfe_resizer_device *resizer); +void vpfe_resizer_dma_isr(struct vpfe_resizer_device *resizer); + +#endif /* _DAVINCI_VPFE_DM365_RESIZER_H */ diff --git a/drivers/staging/media/davinci_vpfe/vpfe.h b/drivers/staging/media/davinci_vpfe/vpfe.h new file mode 100644 index 000000000000..0587bc52a840 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/vpfe.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + */ + +#ifndef _VPFE_H +#define _VPFE_H + +#ifdef __KERNEL__ +#include <linux/v4l2-subdev.h> +#include <linux/clk.h> +#include <linux/i2c.h> + +#include <media/davinci/vpfe_types.h> + +#define CAPTURE_DRV_NAME "vpfe-capture" + +struct vpfe_route { + __u32 input; + __u32 output; +}; + +enum vpfe_subdev_id { + VPFE_SUBDEV_TVP5146 = 1, + VPFE_SUBDEV_MT9T031 = 2, + VPFE_SUBDEV_TVP7002 = 3, + VPFE_SUBDEV_MT9P031 = 4, +}; + +struct vpfe_ext_subdev_info { + /* v4l2 subdev */ + struct v4l2_subdev *subdev; + /* Sub device module name */ + char module_name[32]; + /* Sub device group id */ + int grp_id; + /* Number of inputs supported */ + int num_inputs; + /* inputs available at the sub device */ + struct v4l2_input *inputs; + /* Sub dev routing information for each input */ + struct vpfe_route *routes; + /* ccdc bus/interface configuration */ + struct vpfe_hw_if_param ccdc_if_params; + /* i2c subdevice board info */ + struct i2c_board_info board_info; + /* Is this a camera sub device ? */ + unsigned is_camera:1; + /* check if sub dev supports routing */ + unsigned can_route:1; + /* registered ? */ + unsigned registered:1; +}; + +struct vpfe_config { + /* Number of sub devices connected to vpfe */ + int num_subdevs; + /* information about each subdev */ + struct vpfe_ext_subdev_info *sub_devs; + /* evm card info */ + char *card_name; + /* setup function for the input path */ + int (*setup_input)(enum vpfe_subdev_id id); + /* number of clocks */ + int num_clocks; + /* clocks used for vpfe capture */ + char *clocks[]; +}; +#endif +#endif diff --git a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c new file mode 100644 index 000000000000..7b351717bcbb --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c @@ -0,0 +1,740 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + * + * + * Driver name : VPFE Capture driver + * VPFE Capture driver allows applications to capture and stream video + * frames on DaVinci SoCs (DM6446, DM355 etc) from a YUV source such as + * TVP5146 or Raw Bayer RGB image data from an image sensor + * such as Microns' MT9T001, MT9T031 etc. + * + * These SoCs have, in common, a Video Processing Subsystem (VPSS) that + * consists of a Video Processing Front End (VPFE) for capturing + * video/raw image data and Video Processing Back End (VPBE) for displaying + * YUV data through an in-built analog encoder or Digital LCD port. This + * driver is for capture through VPFE. A typical EVM using these SoCs have + * following high level configuration. + * + * decoder(TVP5146/ YUV/ + * MT9T001) --> Raw Bayer RGB ---> MUX -> VPFE (CCDC/ISIF) + * data input | | + * V | + * SDRAM | + * V + * Image Processor + * | + * V + * SDRAM + * The data flow happens from a decoder connected to the VPFE over a + * YUV embedded (BT.656/BT.1120) or separate sync or raw bayer rgb interface + * and to the input of VPFE through an optional MUX (if more inputs are + * to be interfaced on the EVM). The input data is first passed through + * CCDC (CCD Controller, a.k.a Image Sensor Interface, ISIF). The CCDC + * does very little or no processing on YUV data and does pre-process Raw + * Bayer RGB data through modules such as Defect Pixel Correction (DFC) + * Color Space Conversion (CSC), data gain/offset etc. After this, data + * can be written to SDRAM or can be connected to the image processing + * block such as IPIPE (on DM355/DM365 only). + * + * Features supported + * - MMAP IO + * - USERPTR IO + * - Capture using TVP5146 over BT.656 + * - Support for interfacing decoders using sub device model + * - Work with DM365 or DM355 or DM6446 CCDC to do Raw Bayer + * RGB/YUV data capture to SDRAM. + * - Chaining of Image Processor + * - SINGLE-SHOT mode + */ + +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/slab.h> + +#include "vpfe.h" +#include "vpfe_mc_capture.h" + +static bool debug; +static bool interface; + +module_param(interface, bool, S_IRUGO); +module_param(debug, bool, 0644); + +/** + * VPFE capture can be used for capturing video such as from TVP5146 or TVP7002 + * and for capture raw bayer data from camera sensors such as mt9p031. At this + * point there is problem in co-existence of mt9p031 and tvp5146 due to i2c + * address collision. So set the variable below from bootargs to do either video + * capture or camera capture. + * interface = 0 - video capture (from TVP514x or such), + * interface = 1 - Camera capture (from mt9p031 or such) + * Re-visit this when we fix the co-existence issue + */ +MODULE_PARM_DESC(interface, "interface 0-1 (default:0)"); +MODULE_PARM_DESC(debug, "Debug level 0-1"); + +MODULE_DESCRIPTION("VPFE Video for Linux Capture Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Texas Instruments"); + +/* map mbus_fmt to pixelformat */ +void mbus_to_pix(const struct v4l2_mbus_framefmt *mbus, + struct v4l2_pix_format *pix) +{ + switch (mbus->code) { + case V4L2_MBUS_FMT_UYVY8_2X8: + pix->pixelformat = V4L2_PIX_FMT_UYVY; + pix->bytesperline = pix->width * 2; + break; + + case V4L2_MBUS_FMT_YUYV8_2X8: + pix->pixelformat = V4L2_PIX_FMT_YUYV; + pix->bytesperline = pix->width * 2; + break; + + case V4L2_MBUS_FMT_YUYV10_1X20: + pix->pixelformat = V4L2_PIX_FMT_UYVY; + pix->bytesperline = pix->width * 2; + break; + + case V4L2_MBUS_FMT_SGRBG12_1X12: + pix->pixelformat = V4L2_PIX_FMT_SBGGR16; + pix->bytesperline = pix->width * 2; + break; + + case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: + pix->pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8; + pix->bytesperline = pix->width; + break; + + case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8: + pix->pixelformat = V4L2_PIX_FMT_SGRBG10ALAW8; + pix->bytesperline = pix->width; + break; + + case V4L2_MBUS_FMT_YDYUYDYV8_1X16: + pix->pixelformat = V4L2_PIX_FMT_NV12; + pix->bytesperline = pix->width; + break; + + case V4L2_MBUS_FMT_Y8_1X8: + pix->pixelformat = V4L2_PIX_FMT_GREY; + pix->bytesperline = pix->width; + break; + + case V4L2_MBUS_FMT_UV8_1X8: + pix->pixelformat = V4L2_PIX_FMT_UV8; + pix->bytesperline = pix->width; + break; + + default: + pr_err("Invalid mbus code set\n"); + } + /* pitch should be 32 bytes aligned */ + pix->bytesperline = ALIGN(pix->bytesperline, 32); + if (pix->pixelformat == V4L2_PIX_FMT_NV12) + pix->sizeimage = pix->bytesperline * pix->height + + ((pix->bytesperline * pix->height) >> 1); + else + pix->sizeimage = pix->bytesperline * pix->height; +} + +/* ISR for VINT0*/ +static irqreturn_t vpfe_isr(int irq, void *dev_id) +{ + struct vpfe_device *vpfe_dev = dev_id; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_isr\n"); + vpfe_isif_buffer_isr(&vpfe_dev->vpfe_isif); + vpfe_resizer_buffer_isr(&vpfe_dev->vpfe_resizer); + return IRQ_HANDLED; +} + +/* vpfe_vdint1_isr() - isr handler for VINT1 interrupt */ +static irqreturn_t vpfe_vdint1_isr(int irq, void *dev_id) +{ + struct vpfe_device *vpfe_dev = dev_id; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_vdint1_isr\n"); + vpfe_isif_vidint1_isr(&vpfe_dev->vpfe_isif); + return IRQ_HANDLED; +} + +/* vpfe_imp_dma_isr() - ISR for ipipe dma completion */ +static irqreturn_t vpfe_imp_dma_isr(int irq, void *dev_id) +{ + struct vpfe_device *vpfe_dev = dev_id; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_imp_dma_isr\n"); + vpfe_ipipeif_ss_buffer_isr(&vpfe_dev->vpfe_ipipeif); + vpfe_resizer_dma_isr(&vpfe_dev->vpfe_resizer); + return IRQ_HANDLED; +} + +/* + * vpfe_disable_clock() - Disable clocks for vpfe capture driver + * @vpfe_dev - ptr to vpfe capture device + * + * Disables clocks defined in vpfe configuration. The function + * assumes that at least one clock is to be defined which is + * true as of now. + */ +static void vpfe_disable_clock(struct vpfe_device *vpfe_dev) +{ + struct vpfe_config *vpfe_cfg = vpfe_dev->cfg; + int i; + + for (i = 0; i < vpfe_cfg->num_clocks; i++) { + clk_disable_unprepare(vpfe_dev->clks[i]); + clk_put(vpfe_dev->clks[i]); + } + kzfree(vpfe_dev->clks); + v4l2_info(vpfe_dev->pdev->driver, "vpfe capture clocks disabled\n"); +} + +/* + * vpfe_enable_clock() - Enable clocks for vpfe capture driver + * @vpfe_dev - ptr to vpfe capture device + * + * Enables clocks defined in vpfe configuration. The function + * assumes that at least one clock is to be defined which is + * true as of now. + */ +static int vpfe_enable_clock(struct vpfe_device *vpfe_dev) +{ + struct vpfe_config *vpfe_cfg = vpfe_dev->cfg; + int ret = -EFAULT; + int i; + + if (!vpfe_cfg->num_clocks) + return 0; + + vpfe_dev->clks = kzalloc(vpfe_cfg->num_clocks * + sizeof(struct clock *), GFP_KERNEL); + if (vpfe_dev->clks == NULL) { + v4l2_err(vpfe_dev->pdev->driver, "Memory allocation failed\n"); + return -ENOMEM; + } + + for (i = 0; i < vpfe_cfg->num_clocks; i++) { + if (vpfe_cfg->clocks[i] == NULL) { + v4l2_err(vpfe_dev->pdev->driver, + "clock %s is not defined in vpfe config\n", + vpfe_cfg->clocks[i]); + goto out; + } + + vpfe_dev->clks[i] = + clk_get(vpfe_dev->pdev, vpfe_cfg->clocks[i]); + if (vpfe_dev->clks[i] == NULL) { + v4l2_err(vpfe_dev->pdev->driver, + "Failed to get clock %s\n", + vpfe_cfg->clocks[i]); + goto out; + } + + if (clk_prepare_enable(vpfe_dev->clks[i])) { + v4l2_err(vpfe_dev->pdev->driver, + "vpfe clock %s not enabled\n", + vpfe_cfg->clocks[i]); + goto out; + } + + v4l2_info(vpfe_dev->pdev->driver, "vpss clock %s enabled", + vpfe_cfg->clocks[i]); + } + + return 0; +out: + for (i = 0; i < vpfe_cfg->num_clocks; i++) + if (vpfe_dev->clks[i]) { + clk_disable_unprepare(vpfe_dev->clks[i]); + clk_put(vpfe_dev->clks[i]); + } + + v4l2_err(vpfe_dev->pdev->driver, "Failed to enable clocks\n"); + kzfree(vpfe_dev->clks); + + return ret; +} + +/* + * vpfe_detach_irq() - Detach IRQs for vpfe capture driver + * @vpfe_dev - ptr to vpfe capture device + * + * Detach all IRQs defined in vpfe configuration. + */ +static void vpfe_detach_irq(struct vpfe_device *vpfe_dev) +{ + free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); + free_irq(vpfe_dev->ccdc_irq1, vpfe_dev); + free_irq(vpfe_dev->imp_dma_irq, vpfe_dev); +} + +/* + * vpfe_attach_irq() - Attach IRQs for vpfe capture driver + * @vpfe_dev - ptr to vpfe capture device + * + * Attach all IRQs defined in vpfe configuration. + */ +static int vpfe_attach_irq(struct vpfe_device *vpfe_dev) +{ + int ret = 0; + + ret = request_irq(vpfe_dev->ccdc_irq0, vpfe_isr, IRQF_DISABLED, + "vpfe_capture0", vpfe_dev); + if (ret < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Error: requesting VINT0 interrupt\n"); + return ret; + } + + ret = request_irq(vpfe_dev->ccdc_irq1, vpfe_vdint1_isr, IRQF_DISABLED, + "vpfe_capture1", vpfe_dev); + if (ret < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Error: requesting VINT1 interrupt\n"); + free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); + return ret; + } + + ret = request_irq(vpfe_dev->imp_dma_irq, vpfe_imp_dma_isr, + IRQF_DISABLED, "Imp_Sdram_Irq", vpfe_dev); + if (ret < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Error: requesting IMP IRQ interrupt\n"); + free_irq(vpfe_dev->ccdc_irq1, vpfe_dev); + free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); + return ret; + } + + return 0; +} + +/* + * register_i2c_devices() - register all i2c v4l2 subdevs + * @vpfe_dev - ptr to vpfe capture device + * + * register all i2c v4l2 subdevs + */ +static int register_i2c_devices(struct vpfe_device *vpfe_dev) +{ + struct vpfe_ext_subdev_info *sdinfo; + struct vpfe_config *vpfe_cfg; + struct i2c_adapter *i2c_adap; + unsigned int num_subdevs; + int ret; + int i; + int k; + + vpfe_cfg = vpfe_dev->cfg; + i2c_adap = i2c_get_adapter(1); + num_subdevs = vpfe_cfg->num_subdevs; + vpfe_dev->sd = + kzalloc(sizeof(struct v4l2_subdev *)*num_subdevs, GFP_KERNEL); + if (vpfe_dev->sd == NULL) { + v4l2_err(&vpfe_dev->v4l2_dev, + "unable to allocate memory for subdevice\n"); + return -ENOMEM; + } + + for (i = 0, k = 0; i < num_subdevs; i++) { + sdinfo = &vpfe_cfg->sub_devs[i]; + /* + * register subdevices based on interface setting. Currently + * tvp5146 and mt9p031 cannot co-exists due to i2c address + * conflicts. So only one of them is registered. Re-visit this + * once we have support for i2c switch handling in i2c driver + * framework + */ + if (interface == sdinfo->is_camera) { + /* setup input path */ + if (vpfe_cfg->setup_input && + vpfe_cfg->setup_input(sdinfo->grp_id) < 0) { + ret = -EFAULT; + v4l2_info(&vpfe_dev->v4l2_dev, + "could not setup input for %s\n", + sdinfo->module_name); + goto probe_sd_out; + } + /* Load up the subdevice */ + vpfe_dev->sd[k] = + v4l2_i2c_new_subdev_board(&vpfe_dev->v4l2_dev, + i2c_adap, &sdinfo->board_info, + NULL); + if (vpfe_dev->sd[k]) { + v4l2_info(&vpfe_dev->v4l2_dev, + "v4l2 sub device %s registered\n", + sdinfo->module_name); + + vpfe_dev->sd[k]->grp_id = sdinfo->grp_id; + k++; + + sdinfo->registered = 1; + } + } else { + v4l2_info(&vpfe_dev->v4l2_dev, + "v4l2 sub device %s is not registered\n", + sdinfo->module_name); + } + } + vpfe_dev->num_ext_subdevs = k; + + return 0; + +probe_sd_out: + kzfree(vpfe_dev->sd); + + return ret; +} + +/* + * vpfe_register_entities() - register all v4l2 subdevs and media entities + * @vpfe_dev - ptr to vpfe capture device + * + * register all v4l2 subdevs, media entities, and creates links + * between entities + */ +static int vpfe_register_entities(struct vpfe_device *vpfe_dev) +{ + unsigned int flags = 0; + int ret; + int i; + + /* register i2c devices first */ + ret = register_i2c_devices(vpfe_dev); + if (ret) + return ret; + + /* register rest of the sub-devs */ + ret = vpfe_isif_register_entities(&vpfe_dev->vpfe_isif, + &vpfe_dev->v4l2_dev); + if (ret) + return ret; + + ret = vpfe_ipipeif_register_entities(&vpfe_dev->vpfe_ipipeif, + &vpfe_dev->v4l2_dev); + if (ret) + goto out_isif_register; + + ret = vpfe_ipipe_register_entities(&vpfe_dev->vpfe_ipipe, + &vpfe_dev->v4l2_dev); + if (ret) + goto out_ipipeif_register; + + ret = vpfe_resizer_register_entities(&vpfe_dev->vpfe_resizer, + &vpfe_dev->v4l2_dev); + if (ret) + goto out_ipipe_register; + + /* create links now, starting with external(i2c) entities */ + for (i = 0; i < vpfe_dev->num_ext_subdevs; i++) + /* if entity has no pads (ex: amplifier), + cant establish link */ + if (vpfe_dev->sd[i]->entity.num_pads) { + ret = media_entity_create_link(&vpfe_dev->sd[i]->entity, + 0, &vpfe_dev->vpfe_isif.subdev.entity, + 0, flags); + if (ret < 0) + goto out_resizer_register; + } + + ret = media_entity_create_link(&vpfe_dev->vpfe_isif.subdev.entity, 1, + &vpfe_dev->vpfe_ipipeif.subdev.entity, + 0, flags); + if (ret < 0) + goto out_resizer_register; + + ret = media_entity_create_link(&vpfe_dev->vpfe_ipipeif.subdev.entity, 1, + &vpfe_dev->vpfe_ipipe.subdev.entity, + 0, flags); + if (ret < 0) + goto out_resizer_register; + + ret = media_entity_create_link(&vpfe_dev->vpfe_ipipe.subdev.entity, + 1, &vpfe_dev->vpfe_resizer.crop_resizer.subdev.entity, + 0, flags); + if (ret < 0) + goto out_resizer_register; + + ret = media_entity_create_link(&vpfe_dev->vpfe_ipipeif.subdev.entity, 1, + &vpfe_dev->vpfe_resizer.crop_resizer.subdev.entity, + 0, flags); + if (ret < 0) + goto out_resizer_register; + + ret = v4l2_device_register_subdev_nodes(&vpfe_dev->v4l2_dev); + if (ret < 0) + goto out_resizer_register; + + return 0; + +out_resizer_register: + vpfe_resizer_unregister_entities(&vpfe_dev->vpfe_resizer); +out_ipipe_register: + vpfe_ipipe_unregister_entities(&vpfe_dev->vpfe_ipipe); +out_ipipeif_register: + vpfe_ipipeif_unregister_entities(&vpfe_dev->vpfe_ipipeif); +out_isif_register: + vpfe_isif_unregister_entities(&vpfe_dev->vpfe_isif); + + return ret; +} + +/* + * vpfe_unregister_entities() - unregister all v4l2 subdevs and media entities + * @vpfe_dev - ptr to vpfe capture device + * + * unregister all v4l2 subdevs and media entities + */ +static void vpfe_unregister_entities(struct vpfe_device *vpfe_dev) +{ + vpfe_isif_unregister_entities(&vpfe_dev->vpfe_isif); + vpfe_ipipeif_unregister_entities(&vpfe_dev->vpfe_ipipeif); + vpfe_ipipe_unregister_entities(&vpfe_dev->vpfe_ipipe); + vpfe_resizer_unregister_entities(&vpfe_dev->vpfe_resizer); +} + +/* + * vpfe_cleanup_modules() - cleanup all non-i2c v4l2 subdevs + * @vpfe_dev - ptr to vpfe capture device + * @pdev - pointer to platform device + * + * cleanup all v4l2 subdevs + */ +static void vpfe_cleanup_modules(struct vpfe_device *vpfe_dev, + struct platform_device *pdev) +{ + vpfe_isif_cleanup(&vpfe_dev->vpfe_isif, pdev); + vpfe_ipipeif_cleanup(&vpfe_dev->vpfe_ipipeif, pdev); + vpfe_ipipe_cleanup(&vpfe_dev->vpfe_ipipe, pdev); + vpfe_resizer_cleanup(&vpfe_dev->vpfe_resizer, pdev); +} + +/* + * vpfe_initialize_modules() - initialize all non-i2c v4l2 subdevs + * @vpfe_dev - ptr to vpfe capture device + * @pdev - pointer to platform device + * + * intialize all v4l2 subdevs and media entities + */ +static int vpfe_initialize_modules(struct vpfe_device *vpfe_dev, + struct platform_device *pdev) +{ + int ret; + + ret = vpfe_isif_init(&vpfe_dev->vpfe_isif, pdev); + if (ret) + return ret; + + ret = vpfe_ipipeif_init(&vpfe_dev->vpfe_ipipeif, pdev); + if (ret) + goto out_isif_init; + + ret = vpfe_ipipe_init(&vpfe_dev->vpfe_ipipe, pdev); + if (ret) + goto out_ipipeif_init; + + ret = vpfe_resizer_init(&vpfe_dev->vpfe_resizer, pdev); + if (ret) + goto out_ipipe_init; + + return 0; + +out_ipipe_init: + vpfe_ipipe_cleanup(&vpfe_dev->vpfe_ipipe, pdev); +out_ipipeif_init: + vpfe_ipipeif_cleanup(&vpfe_dev->vpfe_ipipeif, pdev); +out_isif_init: + vpfe_isif_cleanup(&vpfe_dev->vpfe_isif, pdev); + + return ret; +} + +/* + * vpfe_probe() : vpfe probe function + * @pdev: platform device pointer + * + * This function creates device entries by register itself to the V4L2 driver + * and initializes fields of each device objects + */ +static int vpfe_probe(struct platform_device *pdev) +{ + struct vpfe_device *vpfe_dev; + struct resource *res1; + int ret = -ENOMEM; + + vpfe_dev = kzalloc(sizeof(*vpfe_dev), GFP_KERNEL); + if (!vpfe_dev) { + v4l2_err(pdev->dev.driver, + "Failed to allocate memory for vpfe_dev\n"); + return ret; + } + + if (pdev->dev.platform_data == NULL) { + v4l2_err(pdev->dev.driver, "Unable to get vpfe config\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + + vpfe_dev->cfg = pdev->dev.platform_data; + if (vpfe_dev->cfg->card_name == NULL || + vpfe_dev->cfg->sub_devs == NULL) { + v4l2_err(pdev->dev.driver, "null ptr in vpfe_cfg\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + + /* Get VINT0 irq resource */ + res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res1) { + v4l2_err(pdev->dev.driver, + "Unable to get interrupt for VINT0\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + vpfe_dev->ccdc_irq0 = res1->start; + + /* Get VINT1 irq resource */ + res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 1); + if (!res1) { + v4l2_err(pdev->dev.driver, + "Unable to get interrupt for VINT1\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + vpfe_dev->ccdc_irq1 = res1->start; + + /* Get DMA irq resource */ + res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 2); + if (!res1) { + v4l2_err(pdev->dev.driver, + "Unable to get interrupt for DMA\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + vpfe_dev->imp_dma_irq = res1->start; + + vpfe_dev->pdev = &pdev->dev; + + /* enable vpss clocks */ + ret = vpfe_enable_clock(vpfe_dev); + if (ret) + goto probe_free_dev_mem; + + if (vpfe_initialize_modules(vpfe_dev, pdev)) + goto probe_disable_clock; + + vpfe_dev->media_dev.dev = vpfe_dev->pdev; + strcpy((char *)&vpfe_dev->media_dev.model, "davinci-media"); + + ret = media_device_register(&vpfe_dev->media_dev); + if (ret) { + v4l2_err(pdev->dev.driver, + "Unable to register media device.\n"); + goto probe_out_entities_cleanup; + } + + vpfe_dev->v4l2_dev.mdev = &vpfe_dev->media_dev; + ret = v4l2_device_register(&pdev->dev, &vpfe_dev->v4l2_dev); + if (ret) { + v4l2_err(pdev->dev.driver, "Unable to register v4l2 device.\n"); + goto probe_out_media_unregister; + } + + v4l2_info(&vpfe_dev->v4l2_dev, "v4l2 device registered\n"); + /* set the driver data in platform device */ + platform_set_drvdata(pdev, vpfe_dev); + /* register subdevs/entities */ + if (vpfe_register_entities(vpfe_dev)) + goto probe_out_v4l2_unregister; + + ret = vpfe_attach_irq(vpfe_dev); + if (ret) + goto probe_out_entities_unregister; + + return 0; + +probe_out_entities_unregister: + vpfe_unregister_entities(vpfe_dev); + kzfree(vpfe_dev->sd); +probe_out_v4l2_unregister: + v4l2_device_unregister(&vpfe_dev->v4l2_dev); +probe_out_media_unregister: + media_device_unregister(&vpfe_dev->media_dev); +probe_out_entities_cleanup: + vpfe_cleanup_modules(vpfe_dev, pdev); +probe_disable_clock: + vpfe_disable_clock(vpfe_dev); +probe_free_dev_mem: + kzfree(vpfe_dev); + + return ret; +} + +/* + * vpfe_remove : This function un-registers device from V4L2 driver + */ +static int vpfe_remove(struct platform_device *pdev) +{ + struct vpfe_device *vpfe_dev = platform_get_drvdata(pdev); + + v4l2_info(pdev->dev.driver, "vpfe_remove\n"); + + kzfree(vpfe_dev->sd); + vpfe_detach_irq(vpfe_dev); + vpfe_unregister_entities(vpfe_dev); + vpfe_cleanup_modules(vpfe_dev, pdev); + v4l2_device_unregister(&vpfe_dev->v4l2_dev); + media_device_unregister(&vpfe_dev->media_dev); + vpfe_disable_clock(vpfe_dev); + kzfree(vpfe_dev); + + return 0; +} + +static struct platform_driver vpfe_driver = { + .driver = { + .name = CAPTURE_DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = vpfe_probe, + .remove = vpfe_remove, +}; + +/** + * vpfe_init : This function registers device driver + */ +static __init int vpfe_init(void) +{ + /* Register driver to the kernel */ + return platform_driver_register(&vpfe_driver); +} + +/** + * vpfe_cleanup : This function un-registers device driver + */ +static void vpfe_cleanup(void) +{ + platform_driver_unregister(&vpfe_driver); +} + +module_init(vpfe_init); +module_exit(vpfe_cleanup); diff --git a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.h b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.h new file mode 100644 index 000000000000..68f6fe43a5b5 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + */ + +#ifndef _DAVINCI_VPFE_MC_CAPTURE_H +#define _DAVINCI_VPFE_MC_CAPTURE_H + +#include "dm365_ipipe.h" +#include "dm365_ipipeif.h" +#include "dm365_isif.h" +#include "dm365_resizer.h" +#include "vpfe_video.h" + +#define VPFE_MAJOR_RELEASE 0 +#define VPFE_MINOR_RELEASE 0 +#define VPFE_BUILD 1 +#define VPFE_CAPTURE_VERSION_CODE ((VPFE_MAJOR_RELEASE << 16) | \ + (VPFE_MINOR_RELEASE << 8) | \ + VPFE_BUILD) + +/* IPIPE hardware limits */ +#define IPIPE_MAX_OUTPUT_WIDTH_A 2176 +#define IPIPE_MAX_OUTPUT_WIDTH_B 640 + +/* Based on max resolution supported. QXGA */ +#define IPIPE_MAX_OUTPUT_HEIGHT_A 1536 +/* Based on max resolution supported. VGA */ +#define IPIPE_MAX_OUTPUT_HEIGHT_B 480 + +#define to_vpfe_device(ptr_module) \ + container_of(ptr_module, struct vpfe_device, vpfe_##ptr_module) +#define to_device(ptr_module) \ + (to_vpfe_device(ptr_module)->dev) + +struct vpfe_device { + /* external registered sub devices */ + struct v4l2_subdev **sd; + /* number of registered external subdevs */ + unsigned int num_ext_subdevs; + /* vpfe cfg */ + struct vpfe_config *cfg; + /* clock ptrs for vpfe capture */ + struct clk **clks; + /* V4l2 device */ + struct v4l2_device v4l2_dev; + /* parent device */ + struct device *pdev; + /* IRQ number for DMA transfer completion at the image processor */ + unsigned int imp_dma_irq; + /* CCDC IRQs used when CCDC/ISIF output to SDRAM */ + unsigned int ccdc_irq0; + unsigned int ccdc_irq1; + /* maximum video memory that is available*/ + unsigned int video_limit; + /* media device */ + struct media_device media_dev; + /* ccdc subdevice */ + struct vpfe_isif_device vpfe_isif; + /* ipipeif subdevice */ + struct vpfe_ipipeif_device vpfe_ipipeif; + /* ipipe subdevice */ + struct vpfe_ipipe_device vpfe_ipipe; + /* resizer subdevice */ + struct vpfe_resizer_device vpfe_resizer; +}; + +/* File handle structure */ +struct vpfe_fh { + struct v4l2_fh vfh; + struct vpfe_video_device *video; + /* Indicates whether this file handle is doing IO */ + u8 io_allowed; + /* Used to keep track priority of this instance */ + enum v4l2_priority prio; +}; + +void mbus_to_pix(const struct v4l2_mbus_framefmt *mbus, + struct v4l2_pix_format *pix); + +#endif /* _DAVINCI_VPFE_MC_CAPTURE_H */ diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.c b/drivers/staging/media/davinci_vpfe/vpfe_video.c new file mode 100644 index 000000000000..99ccbebea598 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/vpfe_video.c @@ -0,0 +1,1620 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + */ + +#include <linux/module.h> +#include <linux/slab.h> + +#include <media/v4l2-ioctl.h> + +#include "vpfe.h" +#include "vpfe_mc_capture.h" + +/* minimum number of buffers needed in cont-mode */ +#define MIN_NUM_BUFFERS 3 + +static int debug; + +/* get v4l2 subdev pointer to external subdev which is active */ +static struct media_entity *vpfe_get_input_entity + (struct vpfe_video_device *video) +{ + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct media_pad *remote; + + remote = media_entity_remote_source(&vpfe_dev->vpfe_isif.pads[0]); + if (remote == NULL) { + pr_err("Invalid media connection to isif/ccdc\n"); + return NULL; + } + return remote->entity; +} + +/* updates external subdev(sensor/decoder) which is active */ +static int vpfe_update_current_ext_subdev(struct vpfe_video_device *video) +{ + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_config *vpfe_cfg; + struct v4l2_subdev *subdev; + struct media_pad *remote; + int i; + + remote = media_entity_remote_source(&vpfe_dev->vpfe_isif.pads[0]); + if (remote == NULL) { + pr_err("Invalid media connection to isif/ccdc\n"); + return -EINVAL; + } + + subdev = media_entity_to_v4l2_subdev(remote->entity); + vpfe_cfg = vpfe_dev->pdev->platform_data; + for (i = 0; i < vpfe_cfg->num_subdevs; i++) { + if (!strcmp(vpfe_cfg->sub_devs[i].module_name, subdev->name)) { + video->current_ext_subdev = &vpfe_cfg->sub_devs[i]; + break; + } + } + + /* if user not linked decoder/sensor to isif/ccdc */ + if (i == vpfe_cfg->num_subdevs) { + pr_err("Invalid media chain connection to isif/ccdc\n"); + return -EINVAL; + } + /* find the v4l2 subdev pointer */ + for (i = 0; i < vpfe_dev->num_ext_subdevs; i++) { + if (!strcmp(video->current_ext_subdev->module_name, + vpfe_dev->sd[i]->name)) + video->current_ext_subdev->subdev = vpfe_dev->sd[i]; + } + return 0; +} + +/* get the subdev which is connected to the output video node */ +static struct v4l2_subdev * +vpfe_video_remote_subdev(struct vpfe_video_device *video, u32 *pad) +{ + struct media_pad *remote = media_entity_remote_source(&video->pad); + + if (remote == NULL || remote->entity->type != MEDIA_ENT_T_V4L2_SUBDEV) + return NULL; + if (pad) + *pad = remote->index; + return media_entity_to_v4l2_subdev(remote->entity); +} + +/* get the format set at output pad of the adjacent subdev */ +static int +__vpfe_video_get_format(struct vpfe_video_device *video, + struct v4l2_format *format) +{ + struct v4l2_subdev_format fmt; + struct v4l2_subdev *subdev; + struct media_pad *remote; + u32 pad; + int ret; + + subdev = vpfe_video_remote_subdev(video, &pad); + if (subdev == NULL) + return -EINVAL; + + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + remote = media_entity_remote_source(&video->pad); + fmt.pad = remote->index; + + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); + if (ret == -ENOIOCTLCMD) + return -EINVAL; + + format->type = video->type; + /* convert mbus_format to v4l2_format */ + v4l2_fill_pix_format(&format->fmt.pix, &fmt.format); + mbus_to_pix(&fmt.format, &format->fmt.pix); + + return 0; +} + +/* make a note of pipeline details */ +static void vpfe_prepare_pipeline(struct vpfe_video_device *video) +{ + struct media_entity *entity = &video->video_dev.entity; + struct media_device *mdev = entity->parent; + struct vpfe_pipeline *pipe = &video->pipe; + struct vpfe_video_device *far_end = NULL; + struct media_entity_graph graph; + + pipe->input_num = 0; + pipe->output_num = 0; + + if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + pipe->inputs[pipe->input_num++] = video; + else + pipe->outputs[pipe->output_num++] = video; + + mutex_lock(&mdev->graph_mutex); + media_entity_graph_walk_start(&graph, entity); + while ((entity = media_entity_graph_walk_next(&graph))) { + if (entity == &video->video_dev.entity) + continue; + if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) + continue; + far_end = to_vpfe_video(media_entity_to_video_device(entity)); + if (far_end->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + pipe->inputs[pipe->input_num++] = far_end; + else + pipe->outputs[pipe->output_num++] = far_end; + } + mutex_unlock(&mdev->graph_mutex); +} + +/* update pipe state selected by user */ +static int vpfe_update_pipe_state(struct vpfe_video_device *video) +{ + struct vpfe_pipeline *pipe = &video->pipe; + int ret; + + vpfe_prepare_pipeline(video); + + /* Find out if there is any input video + if yes, it is single shot. + */ + if (pipe->input_num == 0) { + pipe->state = VPFE_PIPELINE_STREAM_CONTINUOUS; + ret = vpfe_update_current_ext_subdev(video); + if (ret) { + pr_err("Invalid external subdev\n"); + return ret; + } + } else { + pipe->state = VPFE_PIPELINE_STREAM_SINGLESHOT; + } + video->initialized = 1; + video->skip_frame_count = 1; + video->skip_frame_count_init = 1; + return 0; +} + +/* checks wether pipeline is ready for enabling */ +int vpfe_video_is_pipe_ready(struct vpfe_pipeline *pipe) +{ + int i; + + for (i = 0; i < pipe->input_num; i++) + if (!pipe->inputs[i]->started || + pipe->inputs[i]->state != VPFE_VIDEO_BUFFER_QUEUED) + return 0; + for (i = 0; i < pipe->output_num; i++) + if (!pipe->outputs[i]->started || + pipe->outputs[i]->state != VPFE_VIDEO_BUFFER_QUEUED) + return 0; + return 1; +} + +/** + * Validate a pipeline by checking both ends of all links for format + * discrepancies. + * + * Return 0 if all formats match, or -EPIPE if at least one link is found with + * different formats on its two ends. + */ +static int vpfe_video_validate_pipeline(struct vpfe_pipeline *pipe) +{ + struct v4l2_subdev_format fmt_source; + struct v4l2_subdev_format fmt_sink; + struct v4l2_subdev *subdev; + struct media_pad *pad; + int ret; + + /* + * Should not matter if it is output[0] or 1 as + * the general ideas is to traverse backwards and + * the fact that the out video node always has the + * format of the connected pad. + */ + subdev = vpfe_video_remote_subdev(pipe->outputs[0], NULL); + if (subdev == NULL) + return -EPIPE; + + while (1) { + /* Retrieve the sink format */ + pad = &subdev->entity.pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + fmt_sink.which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt_sink.pad = pad->index; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, + &fmt_sink); + + if (ret < 0 && ret != -ENOIOCTLCMD) + return -EPIPE; + + /* Retrieve the source format */ + pad = media_entity_remote_source(pad); + if (pad == NULL || + pad->entity->type != MEDIA_ENT_T_V4L2_SUBDEV) + break; + + subdev = media_entity_to_v4l2_subdev(pad->entity); + + fmt_source.which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt_source.pad = pad->index; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_source); + if (ret < 0 && ret != -ENOIOCTLCMD) + return -EPIPE; + + /* Check if the two ends match */ + if (fmt_source.format.code != fmt_sink.format.code || + fmt_source.format.width != fmt_sink.format.width || + fmt_source.format.height != fmt_sink.format.height) + return -EPIPE; + } + return 0; +} + +/* + * vpfe_pipeline_enable() - Enable streaming on a pipeline + * @vpfe_dev: vpfe device + * @pipe: vpfe pipeline + * + * Walk the entities chain starting at the pipeline output video node and start + * all modules in the chain in the given mode. + * + * Return 0 if successful, or the return value of the failed video::s_stream + * operation otherwise. + */ +static int vpfe_pipeline_enable(struct vpfe_pipeline *pipe) +{ + struct media_entity_graph graph; + struct media_entity *entity; + struct v4l2_subdev *subdev; + struct media_device *mdev; + int ret = 0; + + if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS) + entity = vpfe_get_input_entity(pipe->outputs[0]); + else + entity = &pipe->inputs[0]->video_dev.entity; + + mdev = entity->parent; + mutex_lock(&mdev->graph_mutex); + media_entity_graph_walk_start(&graph, entity); + while ((entity = media_entity_graph_walk_next(&graph))) { + + if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE) + continue; + subdev = media_entity_to_v4l2_subdev(entity); + ret = v4l2_subdev_call(subdev, video, s_stream, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) + break; + } + mutex_unlock(&mdev->graph_mutex); + return ret; +} + +/* + * vpfe_pipeline_disable() - Disable streaming on a pipeline + * @vpfe_dev: vpfe device + * @pipe: VPFE pipeline + * + * Walk the entities chain starting at the pipeline output video node and stop + * all modules in the chain. + * + * Return 0 if all modules have been properly stopped, or -ETIMEDOUT if a module + * can't be stopped. + */ +static int vpfe_pipeline_disable(struct vpfe_pipeline *pipe) +{ + struct media_entity_graph graph; + struct media_entity *entity; + struct v4l2_subdev *subdev; + struct media_device *mdev; + int ret = 0; + + if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS) + entity = vpfe_get_input_entity(pipe->outputs[0]); + else + entity = &pipe->inputs[0]->video_dev.entity; + + mdev = entity->parent; + mutex_lock(&mdev->graph_mutex); + media_entity_graph_walk_start(&graph, entity); + + while ((entity = media_entity_graph_walk_next(&graph))) { + + if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE) + continue; + subdev = media_entity_to_v4l2_subdev(entity); + ret = v4l2_subdev_call(subdev, video, s_stream, 0); + if (ret < 0 && ret != -ENOIOCTLCMD) + break; + } + mutex_unlock(&mdev->graph_mutex); + + return (ret == 0) ? ret : -ETIMEDOUT ; +} + +/* + * vpfe_pipeline_set_stream() - Enable/disable streaming on a pipeline + * @vpfe_dev: VPFE device + * @pipe: VPFE pipeline + * @state: Stream state (stopped or active) + * + * Set the pipeline to the given stream state. + * + * Return 0 if successfull, or the return value of the failed video::s_stream + * operation otherwise. + */ +static int vpfe_pipeline_set_stream(struct vpfe_pipeline *pipe, + enum vpfe_pipeline_stream_state state) +{ + if (state == VPFE_PIPELINE_STREAM_STOPPED) + return vpfe_pipeline_disable(pipe); + + return vpfe_pipeline_enable(pipe); +} + +static int all_videos_stopped(struct vpfe_video_device *video) +{ + struct vpfe_pipeline *pipe = &video->pipe; + int i; + + for (i = 0; i < pipe->input_num; i++) + if (pipe->inputs[i]->started) + return 0; + for (i = 0; i < pipe->output_num; i++) + if (pipe->outputs[i]->started) + return 0; + return 1; +} + +/* + * vpfe_open() - open video device + * @file: file pointer + * + * initialize media pipeline state, allocate memory for file handle + * + * Return 0 if successful, or the return -ENODEV otherwise. + */ +static int vpfe_open(struct file *file) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_fh *handle; + + /* Allocate memory for the file handle object */ + handle = kzalloc(sizeof(struct vpfe_fh), GFP_KERNEL); + + if (handle == NULL) + return -ENOMEM; + + v4l2_fh_init(&handle->vfh, &video->video_dev); + v4l2_fh_add(&handle->vfh); + + mutex_lock(&video->lock); + /* If decoder is not initialized. initialize it */ + if (!video->initialized && vpfe_update_pipe_state(video)) { + mutex_unlock(&video->lock); + return -ENODEV; + } + /* Increment device users counter */ + video->usrs++; + /* Set io_allowed member to false */ + handle->io_allowed = 0; + v4l2_prio_open(&video->prio, &handle->prio); + handle->video = video; + file->private_data = &handle->vfh; + mutex_unlock(&video->lock); + + return 0; +} + +/* get the next buffer available from dma queue */ +static unsigned long +vpfe_video_get_next_buffer(struct vpfe_video_device *video) +{ + video->cur_frm = video->next_frm = + list_entry(video->dma_queue.next, + struct vpfe_cap_buffer, list); + + list_del(&video->next_frm->list); + video->next_frm->vb.state = VB2_BUF_STATE_ACTIVE; + return vb2_dma_contig_plane_dma_addr(&video->next_frm->vb, 0); +} + +/* schedule the next buffer which is available on dma queue */ +void vpfe_video_schedule_next_buffer(struct vpfe_video_device *video) +{ + struct vpfe_device *vpfe_dev = video->vpfe_dev; + unsigned long addr; + + if (list_empty(&video->dma_queue)) + return; + + video->next_frm = list_entry(video->dma_queue.next, + struct vpfe_cap_buffer, list); + + if (VPFE_PIPELINE_STREAM_SINGLESHOT == video->pipe.state) + video->cur_frm = video->next_frm; + + list_del(&video->next_frm->list); + video->next_frm->vb.state = VB2_BUF_STATE_ACTIVE; + addr = vb2_dma_contig_plane_dma_addr(&video->next_frm->vb, 0); + video->ops->queue(vpfe_dev, addr); + video->state = VPFE_VIDEO_BUFFER_QUEUED; +} + +/* schedule the buffer for capturing bottom field */ +void vpfe_video_schedule_bottom_field(struct vpfe_video_device *video) +{ + struct vpfe_device *vpfe_dev = video->vpfe_dev; + unsigned long addr; + + addr = vb2_dma_contig_plane_dma_addr(&video->cur_frm->vb, 0); + addr += video->field_off; + video->ops->queue(vpfe_dev, addr); +} + +/* make buffer available for dequeue */ +void vpfe_video_process_buffer_complete(struct vpfe_video_device *video) +{ + struct vpfe_pipeline *pipe = &video->pipe; + + do_gettimeofday(&video->cur_frm->vb.v4l2_buf.timestamp); + vb2_buffer_done(&video->cur_frm->vb, VB2_BUF_STATE_DONE); + if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS) + video->cur_frm = video->next_frm; +} + +/* vpfe_stop_capture() - stop streaming */ +static void vpfe_stop_capture(struct vpfe_video_device *video) +{ + struct vpfe_pipeline *pipe = &video->pipe; + + video->started = 0; + + if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + return; + if (all_videos_stopped(video)) + vpfe_pipeline_set_stream(pipe, + VPFE_PIPELINE_STREAM_STOPPED); +} + +/* + * vpfe_release() - release video device + * @file: file pointer + * + * deletes buffer queue, frees the buffers and the vpfe file handle + * + * Return 0 + */ +static int vpfe_release(struct file *file) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct v4l2_fh *vfh = file->private_data; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_fh *fh = container_of(vfh, struct vpfe_fh, vfh); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_release\n"); + + /* Get the device lock */ + mutex_lock(&video->lock); + /* if this instance is doing IO */ + if (fh->io_allowed) { + if (video->started) { + vpfe_stop_capture(video); + /* mark pipe state as stopped in vpfe_release(), + as app might call streamon() after streamoff() + in which case driver has to start streaming. + */ + video->pipe.state = VPFE_PIPELINE_STREAM_STOPPED; + vb2_streamoff(&video->buffer_queue, + video->buffer_queue.type); + } + video->io_usrs = 0; + /* Free buffers allocated */ + vb2_queue_release(&video->buffer_queue); + vb2_dma_contig_cleanup_ctx(video->alloc_ctx); + } + /* Decrement device users counter */ + video->usrs--; + /* Close the priority */ + v4l2_prio_close(&video->prio, fh->prio); + /* If this is the last file handle */ + if (!video->usrs) + video->initialized = 0; + mutex_unlock(&video->lock); + file->private_data = NULL; + /* Free memory allocated to file handle object */ + v4l2_fh_del(vfh); + kzfree(fh); + return 0; +} + +/* + * vpfe_mmap() - It is used to map kernel space buffers + * into user spaces + */ +static int vpfe_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_mmap\n"); + return vb2_mmap(&video->buffer_queue, vma); +} + +/* + * vpfe_poll() - It is used for select/poll system call + */ +static unsigned int vpfe_poll(struct file *file, poll_table *wait) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_poll\n"); + if (video->started) + return vb2_poll(&video->buffer_queue, file, wait); + return 0; +} + +/* vpfe capture driver file operations */ +static const struct v4l2_file_operations vpfe_fops = { + .owner = THIS_MODULE, + .open = vpfe_open, + .release = vpfe_release, + .unlocked_ioctl = video_ioctl2, + .mmap = vpfe_mmap, + .poll = vpfe_poll +}; + +/* + * vpfe_querycap() - query capabilities of video device + * @file: file pointer + * @priv: void pointer + * @cap: pointer to v4l2_capability structure + * + * fills v4l2 capabilities structure + * + * Return 0 + */ +static int vpfe_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querycap\n"); + + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + else + cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; + cap->device_caps = cap->capabilities; + cap->version = VPFE_CAPTURE_VERSION_CODE; + strlcpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver)); + strlcpy(cap->bus_info, "VPFE", sizeof(cap->bus_info)); + strlcpy(cap->card, vpfe_dev->cfg->card_name, sizeof(cap->card)); + + return 0; +} + +/* + * vpfe_g_fmt() - get the format which is active on video device + * @file: file pointer + * @priv: void pointer + * @fmt: pointer to v4l2_format structure + * + * fills v4l2 format structure with active format + * + * Return 0 + */ +static int vpfe_g_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_fmt\n"); + /* Fill in the information about format */ + *fmt = video->fmt; + return 0; +} + +/* + * vpfe_enum_fmt() - enum formats supported on media chain + * @file: file pointer + * @priv: void pointer + * @fmt: pointer to v4l2_fmtdesc structure + * + * fills v4l2_fmtdesc structure with output format set on adjacent subdev, + * only one format is enumearted as subdevs are already configured + * + * Return 0 if successfull, error code otherwise + */ +static int vpfe_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_subdev_format sd_fmt; + struct v4l2_mbus_framefmt mbus; + struct v4l2_subdev *subdev; + struct v4l2_format format; + struct media_pad *remote; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_fmt\n"); + + /* since already subdev pad format is set, + only one pixel format is available */ + if (fmt->index > 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid index\n"); + return -EINVAL; + } + /* get the remote pad */ + remote = media_entity_remote_source(&video->pad); + if (remote == NULL) { + v4l2_err(&vpfe_dev->v4l2_dev, + "invalid remote pad for video node\n"); + return -EINVAL; + } + /* get the remote subdev */ + subdev = vpfe_video_remote_subdev(video, NULL); + if (subdev == NULL) { + v4l2_err(&vpfe_dev->v4l2_dev, + "invalid remote subdev for video node\n"); + return -EINVAL; + } + sd_fmt.pad = remote->index; + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + /* get output format of remote subdev */ + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &sd_fmt); + if (ret) { + v4l2_err(&vpfe_dev->v4l2_dev, + "invalid remote subdev for video node\n"); + return ret; + } + /* convert to pix format */ + mbus.code = sd_fmt.format.code; + mbus_to_pix(&mbus, &format.fmt.pix); + /* copy the result */ + fmt->pixelformat = format.fmt.pix.pixelformat; + + return 0; +} + +/* + * vpfe_s_fmt() - set the format on video device + * @file: file pointer + * @priv: void pointer + * @fmt: pointer to v4l2_format structure + * + * validate and set the format on video device + * + * Return 0 on success, error code otherwise + */ +static int vpfe_s_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_format format; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_fmt\n"); + /* If streaming is started, return error */ + if (video->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is started\n"); + return -EBUSY; + } + /* get adjacent subdev's output pad format */ + ret = __vpfe_video_get_format(video, &format); + if (ret) + return ret; + *fmt = format; + video->fmt = *fmt; + return 0; +} + +/* + * vpfe_try_fmt() - try the format on video device + * @file: file pointer + * @priv: void pointer + * @fmt: pointer to v4l2_format structure + * + * validate the format, update with correct format + * based on output format set on adjacent subdev + * + * Return 0 on success, error code otherwise + */ +static int vpfe_try_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_format format; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_try_fmt\n"); + /* get adjacent subdev's output pad format */ + ret = __vpfe_video_get_format(video, &format); + if (ret) + return ret; + + *fmt = format; + return 0; +} + +/* + * vpfe_enum_input() - enum inputs supported on media chain + * @file: file pointer + * @priv: void pointer + * @fmt: pointer to v4l2_fmtdesc structure + * + * fills v4l2_input structure with input available on media chain, + * only one input is enumearted as media chain is setup by this time + * + * Return 0 if successfull, -EINVAL is media chain is invalid + */ +static int vpfe_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_ext_subdev_info *sdinfo = video->current_ext_subdev; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_input\n"); + /* enumerate from the subdev user has choosen through mc */ + if (inp->index < sdinfo->num_inputs) { + memcpy(inp, &sdinfo->inputs[inp->index], + sizeof(struct v4l2_input)); + return 0; + } + return -EINVAL; +} + +/* + * vpfe_g_input() - get index of the input which is active + * @file: file pointer + * @priv: void pointer + * @index: pointer to unsigned int + * + * set index with input index which is active + */ +static int vpfe_g_input(struct file *file, void *priv, unsigned int *index) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_input\n"); + + *index = video->current_input; + return 0; +} + +/* + * vpfe_s_input() - set input which is pointed by input index + * @file: file pointer + * @priv: void pointer + * @index: pointer to unsigned int + * + * set input on external subdev + * + * Return 0 on success, error code otherwise + */ +static int vpfe_s_input(struct file *file, void *priv, unsigned int index) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_ext_subdev_info *sdinfo; + struct vpfe_route *route; + struct v4l2_input *inps; + u32 output; + u32 input; + int ret; + int i; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_input\n"); + + ret = mutex_lock_interruptible(&video->lock); + if (ret) + return ret; + /* + * If streaming is started return device busy + * error + */ + if (video->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is on\n"); + ret = -EBUSY; + goto unlock_out; + } + + sdinfo = video->current_ext_subdev; + if (!sdinfo->registered) { + ret = -EINVAL; + goto unlock_out; + } + if (vpfe_dev->cfg->setup_input && + vpfe_dev->cfg->setup_input(sdinfo->grp_id) < 0) { + ret = -EFAULT; + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "couldn't setup input for %s\n", + sdinfo->module_name); + goto unlock_out; + } + route = &sdinfo->routes[index]; + if (route && sdinfo->can_route) { + input = route->input; + output = route->output; + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, + sdinfo->grp_id, video, + s_routing, input, output, 0); + if (ret) { + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "s_input:error in setting input in decoder\n"); + ret = -EINVAL; + goto unlock_out; + } + } + /* set standards set by subdev in video device */ + for (i = 0; i < sdinfo->num_inputs; i++) { + inps = &sdinfo->inputs[i]; + video->video_dev.tvnorms |= inps->std; + } + video->current_input = index; +unlock_out: + mutex_unlock(&video->lock); + return ret; +} + +/* + * vpfe_querystd() - query std which is being input on external subdev + * @file: file pointer + * @priv: void pointer + * @std_id: pointer to v4l2_std_id structure + * + * call external subdev through v4l2_device_call_until_err to + * get the std that is being active. + * + * Return 0 on success, error code otherwise + */ +static int vpfe_querystd(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_ext_subdev_info *sdinfo; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querystd\n"); + + ret = mutex_lock_interruptible(&video->lock); + sdinfo = video->current_ext_subdev; + if (ret) + return ret; + /* Call querystd function of decoder device */ + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + video, querystd, std_id); + mutex_unlock(&video->lock); + return ret; +} + +/* + * vpfe_s_std() - set std on external subdev + * @file: file pointer + * @priv: void pointer + * @std_id: pointer to v4l2_std_id structure + * + * set std pointed by std_id on external subdev by calling it using + * v4l2_device_call_until_err + * + * Return 0 on success, error code otherwise + */ +static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_ext_subdev_info *sdinfo; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_std\n"); + + /* Call decoder driver function to set the standard */ + ret = mutex_lock_interruptible(&video->lock); + if (ret) + return ret; + sdinfo = video->current_ext_subdev; + /* If streaming is started, return device busy error */ + if (video->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "streaming is started\n"); + ret = -EBUSY; + goto unlock_out; + } + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + core, s_std, *std_id); + if (ret < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "Failed to set standard\n"); + video->stdid = V4L2_STD_UNKNOWN; + goto unlock_out; + } + video->stdid = *std_id; +unlock_out: + mutex_unlock(&video->lock); + return ret; +} + +static int vpfe_g_std(struct file *file, void *priv, v4l2_std_id *tvnorm) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_std\n"); + *tvnorm = video->stdid; + return 0; +} + +/* + * vpfe_enum_dv_timings() - enumerate dv_timings which are supported by + * to external subdev + * @file: file pointer + * @priv: void pointer + * @timings: pointer to v4l2_enum_dv_timings structure + * + * enum dv_timings's which are supported by external subdev through + * v4l2_subdev_call + * + * Return 0 on success, error code otherwise + */ +static int +vpfe_enum_dv_timings(struct file *file, void *fh, + struct v4l2_enum_dv_timings *timings) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_subdev *subdev = video->current_ext_subdev->subdev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_dv_timings\n"); + return v4l2_subdev_call(subdev, video, enum_dv_timings, timings); +} + +/* + * vpfe_query_dv_timings() - query the dv_timings which is being input + * to external subdev + * @file: file pointer + * @priv: void pointer + * @timings: pointer to v4l2_dv_timings structure + * + * get dv_timings which is being input on external subdev through + * v4l2_subdev_call + * + * Return 0 on success, error code otherwise + */ +static int +vpfe_query_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_subdev *subdev = video->current_ext_subdev->subdev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_query_dv_timings\n"); + return v4l2_subdev_call(subdev, video, query_dv_timings, timings); +} + +/* + * vpfe_s_dv_timings() - set dv_preset on external subdev + * @file: file pointer + * @priv: void pointer + * @timings: pointer to v4l2_dv_timings structure + * + * set dv_timings pointed by preset on external subdev through + * v4l2_device_call_until_err, this configures amplifier also + * + * Return 0 on success, error code otherwise + */ +static int +vpfe_s_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_dv_timings\n"); + + video->stdid = V4L2_STD_UNKNOWN; + return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, + video->current_ext_subdev->grp_id, + video, s_dv_timings, timings); +} + +/* + * vpfe_g_dv_timings() - get dv_preset which is set on external subdev + * @file: file pointer + * @priv: void pointer + * @timings: pointer to v4l2_dv_timings structure + * + * get dv_preset which is set on external subdev through + * v4l2_subdev_call + * + * Return 0 on success, error code otherwise + */ +static int +vpfe_g_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_subdev *subdev = video->current_ext_subdev->subdev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_dv_timings\n"); + return v4l2_subdev_call(subdev, video, g_dv_timings, timings); +} + +/* + * Videobuf operations + */ +/* + * vpfe_buffer_queue_setup : Callback function for buffer setup. + * @vq: vb2_queue ptr + * @fmt: v4l2 format + * @nbuffers: ptr to number of buffers requested by application + * @nplanes:: contains number of distinct video planes needed to hold a frame + * @sizes[]: contains the size (in bytes) of each plane. + * @alloc_ctxs: ptr to allocation context + * + * This callback function is called when reqbuf() is called to adjust + * the buffer nbuffers and buffer size + */ +static int +vpfe_buffer_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct vpfe_fh *fh = vb2_get_drv_priv(vq); + struct vpfe_video_device *video = fh->video; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_pipeline *pipe = &video->pipe; + unsigned long size; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_queue_setup\n"); + size = video->fmt.fmt.pix.sizeimage; + + if (vpfe_dev->video_limit) { + while (size * *nbuffers > vpfe_dev->video_limit) + (*nbuffers)--; + } + if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS) { + if (*nbuffers < MIN_NUM_BUFFERS) + *nbuffers = MIN_NUM_BUFFERS; + } + *nplanes = 1; + sizes[0] = size; + alloc_ctxs[0] = video->alloc_ctx; + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "nbuffers=%d, size=%lu\n", *nbuffers, size); + return 0; +} + +/* + * vpfe_buffer_prepare : callback function for buffer prepare + * @vb: ptr to vb2_buffer + * + * This is the callback function for buffer prepare when vb2_qbuf() + * function is called. The buffer is prepared and user space virtual address + * or user address is converted into physical address + */ +static int vpfe_buffer_prepare(struct vb2_buffer *vb) +{ + struct vpfe_fh *fh = vb2_get_drv_priv(vb->vb2_queue); + struct vpfe_video_device *video = fh->video; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + unsigned long addr; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_prepare\n"); + + if (vb->state != VB2_BUF_STATE_ACTIVE && + vb->state != VB2_BUF_STATE_PREPARED) + return 0; + + /* Initialize buffer */ + vb2_set_plane_payload(vb, 0, video->fmt.fmt.pix.sizeimage); + if (vb2_plane_vaddr(vb, 0) && + vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) + return -EINVAL; + + addr = vb2_dma_contig_plane_dma_addr(vb, 0); + /* Make sure user addresses are aligned to 32 bytes */ + if (!ALIGN(addr, 32)) + return -EINVAL; + + return 0; +} + +static void vpfe_buffer_queue(struct vb2_buffer *vb) +{ + /* Get the file handle object and device object */ + struct vpfe_fh *fh = vb2_get_drv_priv(vb->vb2_queue); + struct vpfe_video_device *video = fh->video; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_pipeline *pipe = &video->pipe; + struct vpfe_cap_buffer *buf = container_of(vb, + struct vpfe_cap_buffer, vb); + unsigned long flags; + unsigned long empty; + unsigned long addr; + + spin_lock_irqsave(&video->dma_queue_lock, flags); + empty = list_empty(&video->dma_queue); + /* add the buffer to the DMA queue */ + list_add_tail(&buf->list, &video->dma_queue); + spin_unlock_irqrestore(&video->dma_queue_lock, flags); + /* this case happens in case of single shot */ + if (empty && video->started && pipe->state == + VPFE_PIPELINE_STREAM_SINGLESHOT && + video->state == VPFE_VIDEO_BUFFER_NOT_QUEUED) { + spin_lock(&video->dma_queue_lock); + addr = vpfe_video_get_next_buffer(video); + video->ops->queue(vpfe_dev, addr); + + video->state = VPFE_VIDEO_BUFFER_QUEUED; + spin_unlock(&video->dma_queue_lock); + + /* enable h/w each time in single shot */ + if (vpfe_video_is_pipe_ready(pipe)) + vpfe_pipeline_set_stream(pipe, + VPFE_PIPELINE_STREAM_SINGLESHOT); + } +} + +/* vpfe_start_capture() - start streaming on all the subdevs */ +static int vpfe_start_capture(struct vpfe_video_device *video) +{ + struct vpfe_pipeline *pipe = &video->pipe; + int ret = 0; + + video->started = 1; + if (vpfe_video_is_pipe_ready(pipe)) + ret = vpfe_pipeline_set_stream(pipe, pipe->state); + + return ret; +} + +static int vpfe_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct vpfe_fh *fh = vb2_get_drv_priv(vq); + struct vpfe_video_device *video = fh->video; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + unsigned long addr; + int ret; + + ret = mutex_lock_interruptible(&video->lock); + if (ret) + goto streamoff; + + /* Get the next frame from the buffer queue */ + video->cur_frm = video->next_frm = + list_entry(video->dma_queue.next, struct vpfe_cap_buffer, list); + /* Remove buffer from the buffer queue */ + list_del(&video->cur_frm->list); + /* Mark state of the current frame to active */ + video->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE; + /* Initialize field_id and started member */ + video->field_id = 0; + addr = vb2_dma_contig_plane_dma_addr(&video->cur_frm->vb, 0); + video->ops->queue(vpfe_dev, addr); + video->state = VPFE_VIDEO_BUFFER_QUEUED; + + ret = vpfe_start_capture(video); + if (ret) + goto unlock_out; + + mutex_unlock(&video->lock); + + return ret; +unlock_out: + mutex_unlock(&video->lock); +streamoff: + ret = vb2_streamoff(&video->buffer_queue, video->buffer_queue.type); + return 0; +} + +static int vpfe_buffer_init(struct vb2_buffer *vb) +{ + struct vpfe_cap_buffer *buf = container_of(vb, + struct vpfe_cap_buffer, vb); + + INIT_LIST_HEAD(&buf->list); + return 0; +} + +/* abort streaming and wait for last buffer */ +static int vpfe_stop_streaming(struct vb2_queue *vq) +{ + struct vpfe_fh *fh = vb2_get_drv_priv(vq); + struct vpfe_video_device *video = fh->video; + + if (!vb2_is_streaming(vq)) + return 0; + /* release all active buffers */ + while (!list_empty(&video->dma_queue)) { + video->next_frm = list_entry(video->dma_queue.next, + struct vpfe_cap_buffer, list); + list_del(&video->next_frm->list); + vb2_buffer_done(&video->next_frm->vb, VB2_BUF_STATE_ERROR); + } + return 0; +} + +static void vpfe_buf_cleanup(struct vb2_buffer *vb) +{ + struct vpfe_fh *fh = vb2_get_drv_priv(vb->vb2_queue); + struct vpfe_video_device *video = fh->video; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_cap_buffer *buf = container_of(vb, + struct vpfe_cap_buffer, vb); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buf_cleanup\n"); + if (vb->state == VB2_BUF_STATE_ACTIVE) + list_del_init(&buf->list); +} + +static struct vb2_ops video_qops = { + .queue_setup = vpfe_buffer_queue_setup, + .buf_init = vpfe_buffer_init, + .buf_prepare = vpfe_buffer_prepare, + .start_streaming = vpfe_start_streaming, + .stop_streaming = vpfe_stop_streaming, + .buf_cleanup = vpfe_buf_cleanup, + .buf_queue = vpfe_buffer_queue, +}; + +/* + * vpfe_reqbufs() - supported REQBUF only once opening + * the device. + */ +static int vpfe_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *req_buf) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_fh *fh = file->private_data; + struct vb2_queue *q; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_reqbufs\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != req_buf->type && + V4L2_BUF_TYPE_VIDEO_OUTPUT != req_buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buffer type\n"); + return -EINVAL; + } + + ret = mutex_lock_interruptible(&video->lock); + if (ret) + return ret; + + if (video->io_usrs != 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "Only one IO user allowed\n"); + ret = -EBUSY; + goto unlock_out; + } + video->memory = req_buf->memory; + + /* Initialize videobuf2 queue as per the buffer type */ + video->alloc_ctx = vb2_dma_contig_init_ctx(vpfe_dev->pdev); + if (IS_ERR(video->alloc_ctx)) { + v4l2_err(&vpfe_dev->v4l2_dev, "Failed to get the context\n"); + return PTR_ERR(video->alloc_ctx); + } + + q = &video->buffer_queue; + q->type = req_buf->type; + q->io_modes = VB2_MMAP | VB2_USERPTR; + q->drv_priv = fh; + q->ops = &video_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->buf_struct_size = sizeof(struct vpfe_cap_buffer); + + ret = vb2_queue_init(q); + if (ret) { + v4l2_err(&vpfe_dev->v4l2_dev, "vb2_queue_init() failed\n"); + vb2_dma_contig_cleanup_ctx(vpfe_dev->pdev); + return ret; + } + + fh->io_allowed = 1; + video->io_usrs = 1; + INIT_LIST_HEAD(&video->dma_queue); + ret = vb2_reqbufs(&video->buffer_queue, req_buf); + +unlock_out: + mutex_unlock(&video->lock); + return ret; +} + +/* + * vpfe_querybuf() - query buffers for exchange + */ +static int vpfe_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querybuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type && + V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + if (video->memory != V4L2_MEMORY_MMAP) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid memory\n"); + return -EINVAL; + } + + /* Call vb2_querybuf to get information */ + return vb2_querybuf(&video->buffer_queue, buf); +} + +/* + * vpfe_qbuf() - queue buffers for capture or processing + */ +static int vpfe_qbuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_fh *fh = file->private_data; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_qbuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != p->type && + V4L2_BUF_TYPE_VIDEO_OUTPUT != p->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + /* + * If this file handle is not allowed to do IO, + * return error + */ + if (!fh->io_allowed) { + v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + + return vb2_qbuf(&video->buffer_queue, p); +} + +/* + * vpfe_dqbuf() - deque buffer which is done with processing + */ +static int vpfe_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_dqbuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type && + V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + return vb2_dqbuf(&video->buffer_queue, + buf, (file->f_flags & O_NONBLOCK)); +} + +/* + * vpfe_streamon() - get dv_preset which is set on external subdev + * @file: file pointer + * @priv: void pointer + * @buf_type: enum v4l2_buf_type + * + * queue buffer onto hardware for capture/processing and + * start all the subdevs which are in media chain + * + * Return 0 on success, error code otherwise + */ +static int vpfe_streamon(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_pipeline *pipe = &video->pipe; + struct vpfe_fh *fh = file->private_data; + struct vpfe_ext_subdev_info *sdinfo; + int ret = -EINVAL; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamon\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf_type && + V4L2_BUF_TYPE_VIDEO_OUTPUT != buf_type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return ret; + } + /* If file handle is not allowed IO, return error */ + if (!fh->io_allowed) { + v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + sdinfo = video->current_ext_subdev; + /* If buffer queue is empty, return error */ + if (list_empty(&video->buffer_queue.queued_list)) { + v4l2_err(&vpfe_dev->v4l2_dev, "buffer queue is empty\n"); + return -EIO; + } + /* Validate the pipeline */ + if (V4L2_BUF_TYPE_VIDEO_CAPTURE == buf_type) { + ret = vpfe_video_validate_pipeline(pipe); + if (ret < 0) + return ret; + } + /* Call vb2_streamon to start streaming */ + return vb2_streamon(&video->buffer_queue, buf_type); +} + +/* + * vpfe_streamoff() - get dv_preset which is set on external subdev + * @file: file pointer + * @priv: void pointer + * @buf_type: enum v4l2_buf_type + * + * stop all the subdevs which are in media chain + * + * Return 0 on success, error code otherwise + */ +static int vpfe_streamoff(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_fh *fh = file->private_data; + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamoff\n"); + + if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + buf_type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + /* If io is allowed for this file handle, return error */ + if (!fh->io_allowed) { + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + + /* If streaming is not started, return error */ + if (!video->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "device is not started\n"); + return -EINVAL; + } + + ret = mutex_lock_interruptible(&video->lock); + if (ret) + return ret; + + vpfe_stop_capture(video); + ret = vb2_streamoff(&video->buffer_queue, buf_type); + mutex_unlock(&video->lock); + + return ret; +} + +/* vpfe capture ioctl operations */ +static const struct v4l2_ioctl_ops vpfe_ioctl_ops = { + .vidioc_querycap = vpfe_querycap, + .vidioc_g_fmt_vid_cap = vpfe_g_fmt, + .vidioc_s_fmt_vid_cap = vpfe_s_fmt, + .vidioc_try_fmt_vid_cap = vpfe_try_fmt, + .vidioc_enum_fmt_vid_cap = vpfe_enum_fmt, + .vidioc_g_fmt_vid_out = vpfe_g_fmt, + .vidioc_s_fmt_vid_out = vpfe_s_fmt, + .vidioc_try_fmt_vid_out = vpfe_try_fmt, + .vidioc_enum_fmt_vid_out = vpfe_enum_fmt, + .vidioc_enum_input = vpfe_enum_input, + .vidioc_g_input = vpfe_g_input, + .vidioc_s_input = vpfe_s_input, + .vidioc_querystd = vpfe_querystd, + .vidioc_s_std = vpfe_s_std, + .vidioc_g_std = vpfe_g_std, + .vidioc_enum_dv_timings = vpfe_enum_dv_timings, + .vidioc_query_dv_timings = vpfe_query_dv_timings, + .vidioc_s_dv_timings = vpfe_s_dv_timings, + .vidioc_g_dv_timings = vpfe_g_dv_timings, + .vidioc_reqbufs = vpfe_reqbufs, + .vidioc_querybuf = vpfe_querybuf, + .vidioc_qbuf = vpfe_qbuf, + .vidioc_dqbuf = vpfe_dqbuf, + .vidioc_streamon = vpfe_streamon, + .vidioc_streamoff = vpfe_streamoff, +}; + +/* VPFE video init function */ +int vpfe_video_init(struct vpfe_video_device *video, const char *name) +{ + const char *direction; + int ret; + + switch (video->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + direction = "output"; + video->pad.flags = MEDIA_PAD_FL_SINK; + video->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + break; + + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + direction = "input"; + video->pad.flags = MEDIA_PAD_FL_SOURCE; + video->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + break; + + default: + return -EINVAL; + } + /* Initialize field of video device */ + video->video_dev.release = video_device_release; + video->video_dev.fops = &vpfe_fops; + video->video_dev.ioctl_ops = &vpfe_ioctl_ops; + video->video_dev.minor = -1; + video->video_dev.tvnorms = 0; + snprintf(video->video_dev.name, sizeof(video->video_dev.name), + "DAVINCI VIDEO %s %s", name, direction); + + /* Initialize prio member of device object */ + v4l2_prio_init(&video->prio); + spin_lock_init(&video->irqlock); + spin_lock_init(&video->dma_queue_lock); + mutex_init(&video->lock); + ret = media_entity_init(&video->video_dev.entity, + 1, &video->pad, 0); + if (ret < 0) + return ret; + + video_set_drvdata(&video->video_dev, video); + + return 0; +} + +/* vpfe video device register function */ +int vpfe_video_register(struct vpfe_video_device *video, + struct v4l2_device *vdev) +{ + int ret; + + video->video_dev.v4l2_dev = vdev; + + ret = video_register_device(&video->video_dev, VFL_TYPE_GRABBER, -1); + if (ret < 0) + pr_err("%s: could not register video device (%d)\n", + __func__, ret); + return ret; +} + +/* vpfe video device unregister function */ +void vpfe_video_unregister(struct vpfe_video_device *video) +{ + if (video_is_registered(&video->video_dev)) { + media_entity_cleanup(&video->video_dev.entity); + video_unregister_device(&video->video_dev); + } +} diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.h b/drivers/staging/media/davinci_vpfe/vpfe_video.h new file mode 100644 index 000000000000..bf8af01d4a1b --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/vpfe_video.h @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + */ + +#ifndef _DAVINCI_VPFE_VIDEO_H +#define _DAVINCI_VPFE_VIDEO_H + +#include <media/videobuf2-dma-contig.h> + +struct vpfe_device; + +/* + * struct vpfe_video_operations - VPFE video operations + * @queue: Resume streaming when a buffer is queued. Called on VIDIOC_QBUF + * if there was no buffer previously queued. + */ +struct vpfe_video_operations { + int(*queue) (struct vpfe_device *vpfe_dev, unsigned long addr); +}; + +enum vpfe_pipeline_stream_state { + VPFE_PIPELINE_STREAM_STOPPED = 0, + VPFE_PIPELINE_STREAM_CONTINUOUS = 1, + VPFE_PIPELINE_STREAM_SINGLESHOT = 2, +}; + +enum vpfe_video_state { + /* indicates that buffer is not queued */ + VPFE_VIDEO_BUFFER_NOT_QUEUED = 0, + /* indicates that buffer is queued */ + VPFE_VIDEO_BUFFER_QUEUED = 1, +}; + +struct vpfe_pipeline { + /* media pipeline */ + struct media_pipeline *pipe; + /* state of the pipeline, continuous, + * single-shot or stopped + */ + enum vpfe_pipeline_stream_state state; + /* number of active input video entities */ + unsigned int input_num; + /* number of active output video entities */ + unsigned int output_num; + /* input video nodes in case of single-shot mode */ + struct vpfe_video_device *inputs[10]; + /* capturing video nodes */ + struct vpfe_video_device *outputs[10]; +}; + +#define to_vpfe_pipeline(__e) \ + container_of((__e)->pipe, struct vpfe_pipeline, pipe) + +#define to_vpfe_video(vdev) \ + container_of(vdev, struct vpfe_video_device, video_dev) + +struct vpfe_cap_buffer { + struct vb2_buffer vb; + struct list_head list; +}; + +struct vpfe_video_device { + /* vpfe device */ + struct vpfe_device *vpfe_dev; + /* video dev */ + struct video_device video_dev; + /* media pad of video entity */ + struct media_pad pad; + /* video operations supported by video device */ + const struct vpfe_video_operations *ops; + /* type of the video buffers used by user */ + enum v4l2_buf_type type; + /* Indicates id of the field which is being captured */ + u32 field_id; + /* pipeline for which video device is part of */ + struct vpfe_pipeline pipe; + /* Indicates whether streaming started */ + u8 started; + /* Indicates state of the stream */ + unsigned int state; + /* current input at the sub device */ + int current_input; + /* + * This field keeps track of type of buffer exchange mechanism + * user has selected + */ + enum v4l2_memory memory; + /* Used to keep track of state of the priority */ + struct v4l2_prio_state prio; + /* number of open instances of the channel */ + u32 usrs; + /* flag to indicate whether decoder is initialized */ + u8 initialized; + /* skip frame count */ + u8 skip_frame_count; + /* skip frame count init value */ + u8 skip_frame_count_init; + /* time per frame for skipping */ + struct v4l2_fract timeperframe; + /* ptr to currently selected sub device */ + struct vpfe_ext_subdev_info *current_ext_subdev; + /* Pointer pointing to current vpfe_cap_buffer */ + struct vpfe_cap_buffer *cur_frm; + /* Pointer pointing to next vpfe_cap_buffer */ + struct vpfe_cap_buffer *next_frm; + /* Used to store pixel format */ + struct v4l2_format fmt; + struct vb2_queue buffer_queue; + /* allocator-specific contexts for each plane */ + struct vb2_alloc_ctx *alloc_ctx; + /* Queue of filled frames */ + struct list_head dma_queue; + spinlock_t irqlock; + /* IRQ lock for DMA queue */ + spinlock_t dma_queue_lock; + /* lock used to access this structure */ + struct mutex lock; + /* number of users performing IO */ + u32 io_usrs; + /* Currently selected or default standard */ + v4l2_std_id stdid; + /* + * offset where second field starts from the starting of the + * buffer for field seperated YCbCr formats + */ + u32 field_off; +}; + +int vpfe_video_is_pipe_ready(struct vpfe_pipeline *pipe); +void vpfe_video_unregister(struct vpfe_video_device *video); +int vpfe_video_register(struct vpfe_video_device *video, + struct v4l2_device *vdev); +int vpfe_video_init(struct vpfe_video_device *video, const char *name); +void vpfe_video_process_buffer_complete(struct vpfe_video_device *video); +void vpfe_video_schedule_bottom_field(struct vpfe_video_device *video); +void vpfe_video_schedule_next_buffer(struct vpfe_video_device *video); + +#endif /* _DAVINCI_VPFE_VIDEO_H */ diff --git a/drivers/staging/media/dt3155v4l/dt3155v4l.c b/drivers/staging/media/dt3155v4l/dt3155v4l.c index 479c643da2f6..e33b7f55d84e 100644 --- a/drivers/staging/media/dt3155v4l/dt3155v4l.c +++ b/drivers/staging/media/dt3155v4l/dt3155v4l.c @@ -785,7 +785,7 @@ dt3155_init_board(struct pci_dev *pdev) } write_i2c_reg(pd->regs, CONFIG, pd->config); /* ACQ_MODE_EVEN */ - /* select chanel 1 for input and set sync level */ + /* select channel 1 for input and set sync level */ write_i2c_reg(pd->regs, AD_ADDR, AD_CMD_REG); write_i2c_reg(pd->regs, AD_CMD, VIDEO_CNL_1 | SYNC_CNL_1 | SYNC_LVL_3); diff --git a/drivers/staging/media/go7007/go7007-driver.c b/drivers/staging/media/go7007/go7007-driver.c index ece2dd146487..c9dfc75d1d2c 100644 --- a/drivers/staging/media/go7007/go7007-driver.c +++ b/drivers/staging/media/go7007/go7007-driver.c @@ -201,7 +201,8 @@ static int init_i2c_module(struct i2c_adapter *adapter, const char *type, if (v4l2_i2c_new_subdev(v4l2_dev, adapter, type, addr, NULL)) return 0; - printk(KERN_INFO "go7007: probing for module i2c:%s failed\n", type); + dev_info(&adapter->dev, + "go7007: probing for module i2c:%s failed\n", type); return -1; } @@ -217,7 +218,7 @@ int go7007_register_encoder(struct go7007 *go) { int i, ret; - printk(KERN_INFO "go7007: registering new %s\n", go->name); + dev_info(go->dev, "go7007: registering new %s\n", go->name); mutex_lock(&go->hw_lock); ret = go7007_init_encoder(go); diff --git a/drivers/staging/media/go7007/go7007-i2c.c b/drivers/staging/media/go7007/go7007-i2c.c index 6bc82aaeef11..39456a36b2c6 100644 --- a/drivers/staging/media/go7007/go7007-i2c.c +++ b/drivers/staging/media/go7007/go7007-i2c.c @@ -60,10 +60,10 @@ static int go7007_i2c_xfer(struct go7007 *go, u16 addr, int read, #ifdef GO7007_I2C_DEBUG if (read) - printk(KERN_DEBUG "go7007-i2c: reading 0x%02x on 0x%02x\n", + dev_dbg(go->dev, "go7007-i2c: reading 0x%02x on 0x%02x\n", command, addr); else - printk(KERN_DEBUG + dev_dbg(go->dev, "go7007-i2c: writing 0x%02x to 0x%02x on 0x%02x\n", *data, command, addr); #endif @@ -85,7 +85,7 @@ static int go7007_i2c_xfer(struct go7007 *go, u16 addr, int read, msleep(100); } if (i == 10) { - printk(KERN_ERR "go7007-i2c: I2C adapter is hung\n"); + dev_err(go->dev, "go7007-i2c: I2C adapter is hung\n"); goto i2c_done; } @@ -119,7 +119,7 @@ static int go7007_i2c_xfer(struct go7007 *go, u16 addr, int read, msleep(100); } if (i == 10) { - printk(KERN_ERR "go7007-i2c: I2C adapter is hung\n"); + dev_err(go->dev, "go7007-i2c: I2C adapter is hung\n"); goto i2c_done; } @@ -216,7 +216,7 @@ int go7007_i2c_init(struct go7007 *go) go->i2c_adapter.dev.parent = go->dev; i2c_set_adapdata(&go->i2c_adapter, go); if (i2c_add_adapter(&go->i2c_adapter) < 0) { - printk(KERN_ERR + dev_err(go->dev, "go7007-i2c: error: i2c_add_adapter failed\n"); return -1; } diff --git a/drivers/staging/media/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c index a78133b67de2..0d3713dceba5 100644 --- a/drivers/staging/media/go7007/go7007-v4l2.c +++ b/drivers/staging/media/go7007/go7007-v4l2.c @@ -1811,8 +1811,8 @@ int go7007_v4l2_init(struct go7007 *go) } video_set_drvdata(go->video_dev, go); ++go->ref_count; - printk(KERN_INFO "%s: registered device %s [v4l2]\n", - go->video_dev->name, video_device_node_name(go->video_dev)); + dev_info(go->dev, "registered device %s [v4l2]\n", + video_device_node_name(go->video_dev)); return 0; } diff --git a/drivers/staging/media/go7007/s2250-board.c b/drivers/staging/media/go7007/s2250-board.c index b3974100c6cd..d60e06596375 100644 --- a/drivers/staging/media/go7007/s2250-board.c +++ b/drivers/staging/media/go7007/s2250-board.c @@ -103,8 +103,7 @@ static u16 vid_regs_fp[] = { }; /* PAL specific values */ -static u16 vid_regs_fp_pal[] = -{ +static u16 vid_regs_fp_pal[] = { 0x120, 0x017, 0x121, 0xd22, 0x122, 0x122, @@ -174,7 +173,7 @@ static int write_reg(struct i2c_client *client, u8 reg, u8 value) usb = go->hpi_context; if (mutex_lock_interruptible(&usb->i2c_lock) != 0) { - printk(KERN_INFO "i2c lock failed\n"); + dev_info(&client->dev, "i2c lock failed\n"); kfree(buf); return -EINTR; } @@ -213,7 +212,7 @@ static int write_reg_fp(struct i2c_client *client, u16 addr, u16 val) usb = go->hpi_context; if (mutex_lock_interruptible(&usb->i2c_lock) != 0) { - printk(KERN_INFO "i2c lock failed\n"); + dev_info(&client->dev, "i2c lock failed\n"); kfree(buf); return -EINTR; } @@ -231,13 +230,13 @@ static int write_reg_fp(struct i2c_client *client, u16 addr, u16 val) val_read = (buf[2] << 8) + buf[3]; kfree(buf); if (val_read != val) { - printk(KERN_INFO "invalid fp write %x %x\n", - val_read, val); + dev_info(&client->dev, "invalid fp write %x %x\n", + val_read, val); return -EFAULT; } if (subaddr != addr) { - printk(KERN_INFO "invalid fp write addr %x %x\n", - subaddr, addr); + dev_info(&client->dev, "invalid fp write addr %x %x\n", + subaddr, addr); return -EFAULT; } } else { @@ -275,7 +274,7 @@ static int read_reg_fp(struct i2c_client *client, u16 addr, u16 *val) memset(buf, 0xcd, 6); usb = go->hpi_context; if (mutex_lock_interruptible(&usb->i2c_lock) != 0) { - printk(KERN_INFO "i2c lock failed\n"); + dev_info(&client->dev, "i2c lock failed\n"); kfree(buf); return -EINTR; } @@ -299,7 +298,7 @@ static int write_regs(struct i2c_client *client, u8 *regs) for (i = 0; !((regs[i] == 0x00) && (regs[i+1] == 0x00)); i += 2) { if (write_reg(client, regs[i], regs[i+1]) < 0) { - printk(KERN_INFO "s2250: failed\n"); + dev_info(&client->dev, "failed\n"); return -1; } } @@ -312,7 +311,7 @@ static int write_regs_fp(struct i2c_client *client, u16 *regs) for (i = 0; !((regs[i] == 0x00) && (regs[i+1] == 0x00)); i += 2) { if (write_reg_fp(client, regs[i], regs[i+1]) < 0) { - printk(KERN_INFO "s2250: failed fp\n"); + dev_info(&client->dev, "failed fp\n"); return -1; } } @@ -606,23 +605,20 @@ static int s2250_probe(struct i2c_client *client, /* initialize the audio */ if (write_regs(audio, aud_regs) < 0) { - printk(KERN_ERR - "s2250: error initializing audio\n"); + dev_err(&client->dev, "error initializing audio\n"); i2c_unregister_device(audio); kfree(state); return 0; } if (write_regs(client, vid_regs) < 0) { - printk(KERN_ERR - "s2250: error initializing decoder\n"); + dev_err(&client->dev, "error initializing decoder\n"); i2c_unregister_device(audio); kfree(state); return 0; } if (write_regs_fp(client, vid_regs_fp) < 0) { - printk(KERN_ERR - "s2250: error initializing decoder\n"); + dev_err(&client->dev, "error initializing decoder\n"); i2c_unregister_device(audio); kfree(state); return 0; diff --git a/drivers/staging/media/go7007/s2250-loader.c b/drivers/staging/media/go7007/s2250-loader.c index f1bd159ac195..f57eb3beb341 100644 --- a/drivers/staging/media/go7007/s2250-loader.c +++ b/drivers/staging/media/go7007/s2250-loader.c @@ -55,16 +55,16 @@ static int s2250loader_probe(struct usb_interface *interface, usbdev = usb_get_dev(interface_to_usbdev(interface)); if (!usbdev) { - printk(KERN_ERR "Enter s2250loader_probe failed\n"); + dev_err(&interface->dev, "Enter s2250loader_probe failed\n"); return -1; } - printk(KERN_INFO "Enter s2250loader_probe 2.6 kernel\n"); - printk(KERN_INFO "vendor id 0x%x, device id 0x%x devnum:%d\n", - usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, - usbdev->devnum); + dev_info(&interface->dev, "Enter s2250loader_probe 2.6 kernel\n"); + dev_info(&interface->dev, "vendor id 0x%x, device id 0x%x devnum:%d\n", + usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, + usbdev->devnum); if (usbdev->descriptor.bNumConfigurations != 1) { - printk(KERN_ERR "can't handle multiple config\n"); + dev_err(&interface->dev, "can't handle multiple config\n"); return -1; } mutex_lock(&s2250_dev_table_mutex); @@ -75,31 +75,32 @@ static int s2250loader_probe(struct usb_interface *interface, } if (minor < 0 || minor >= MAX_DEVICES) { - printk(KERN_ERR "Invalid minor: %d\n", minor); + dev_err(&interface->dev, "Invalid minor: %d\n", minor); goto failed; } /* Allocate dev data structure */ s = kmalloc(sizeof(device_extension_t), GFP_KERNEL); if (s == NULL) { - printk(KERN_ERR "Out of memory\n"); + dev_err(&interface->dev, "Out of memory\n"); goto failed; } s2250_dev_table[minor] = s; - printk(KERN_INFO "s2250loader_probe: Device %d on Bus %d Minor %d\n", - usbdev->devnum, usbdev->bus->busnum, minor); + dev_info(&interface->dev, + "s2250loader_probe: Device %d on Bus %d Minor %d\n", + usbdev->devnum, usbdev->bus->busnum, minor); memset(s, 0, sizeof(device_extension_t)); s->usbdev = usbdev; - printk(KERN_INFO "loading 2250 loader\n"); + dev_info(&interface->dev, "loading 2250 loader\n"); kref_init(&(s->kref)); mutex_unlock(&s2250_dev_table_mutex); if (request_firmware(&fw, S2250_LOADER_FIRMWARE, &usbdev->dev)) { - printk(KERN_ERR + dev_err(&interface->dev, "s2250: unable to load firmware from file \"%s\"\n", S2250_LOADER_FIRMWARE); goto failed2; @@ -107,12 +108,12 @@ static int s2250loader_probe(struct usb_interface *interface, ret = usb_cypress_load_firmware(usbdev, fw, CYPRESS_FX2); release_firmware(fw); if (0 != ret) { - printk(KERN_ERR "loader download failed\n"); + dev_err(&interface->dev, "loader download failed\n"); goto failed2; } if (request_firmware(&fw, S2250_FIRMWARE, &usbdev->dev)) { - printk(KERN_ERR + dev_err(&interface->dev, "s2250: unable to load firmware from file \"%s\"\n", S2250_FIRMWARE); goto failed2; @@ -120,7 +121,7 @@ static int s2250loader_probe(struct usb_interface *interface, ret = usb_cypress_load_firmware(usbdev, fw, CYPRESS_FX2); release_firmware(fw); if (0 != ret) { - printk(KERN_ERR "firmware_s2250 download failed\n"); + dev_err(&interface->dev, "firmware_s2250 download failed\n"); goto failed2; } @@ -133,14 +134,14 @@ failed2: if (s) kref_put(&(s->kref), s2250loader_delete); - printk(KERN_ERR "probe failed\n"); + dev_err(&interface->dev, "probe failed\n"); return -1; } static void s2250loader_disconnect(struct usb_interface *interface) { pdevice_extension_t s; - printk(KERN_INFO "s2250: disconnect\n"); + dev_info(&interface->dev, "s2250: disconnect\n"); s = usb_get_intfdata(interface); usb_set_intfdata(interface, NULL); kref_put(&(s->kref), s2250loader_delete); diff --git a/drivers/staging/media/go7007/wis-sony-tuner.c b/drivers/staging/media/go7007/wis-sony-tuner.c index 1291ab79d2af..5d7ff8c81d6d 100644 --- a/drivers/staging/media/go7007/wis-sony-tuner.c +++ b/drivers/staging/media/go7007/wis-sony-tuner.c @@ -95,8 +95,8 @@ static int set_freq(struct i2c_client *client, int freq) band_name = "UHF"; band_select = tun->UHF; } - printk(KERN_DEBUG "wis-sony-tuner: tuning to frequency %d.%04d (%s)\n", - freq / 16, (freq % 16) * 625, band_name); + dev_dbg(&client->dev, "tuning to frequency %d.%04d (%s)\n", + freq / 16, (freq % 16) * 625, band_name); n = freq + tun->IFPCoff; buffer[0] = n >> 8; @@ -288,16 +288,16 @@ static int mpx_setup(struct i2c_client *client) u8 buf1[3], buf2[2]; struct i2c_msg msgs[2]; - printk(KERN_DEBUG "wis-sony-tuner: MPX registers: %04x %04x " - "%04x %04x %04x %04x %04x %04x\n", - mpx_audio_modes[t->mpxmode].modus, - source, - mpx_audio_modes[t->mpxmode].acb, - mpx_audio_modes[t->mpxmode].fm_prescale, - mpx_audio_modes[t->mpxmode].nicam_prescale, - mpx_audio_modes[t->mpxmode].scart_prescale, - mpx_audio_modes[t->mpxmode].system, - mpx_audio_modes[t->mpxmode].volume); + dev_dbg(&client->dev, + "MPX registers: %04x %04x %04x %04x %04x %04x %04x %04x\n", + mpx_audio_modes[t->mpxmode].modus, + source, + mpx_audio_modes[t->mpxmode].acb, + mpx_audio_modes[t->mpxmode].fm_prescale, + mpx_audio_modes[t->mpxmode].nicam_prescale, + mpx_audio_modes[t->mpxmode].scart_prescale, + mpx_audio_modes[t->mpxmode].system, + mpx_audio_modes[t->mpxmode].volume); buf1[0] = 0x11; buf1[1] = 0x00; buf1[2] = 0x7e; @@ -310,14 +310,14 @@ static int mpx_setup(struct i2c_client *client) msgs[1].len = 2; msgs[1].buf = buf2; i2c_transfer(client->adapter, msgs, 2); - printk(KERN_DEBUG "wis-sony-tuner: MPX system: %02x%02x\n", - buf2[0], buf2[1]); + dev_dbg(&client->dev, "MPX system: %02x%02x\n", + buf2[0], buf2[1]); buf1[0] = 0x11; buf1[1] = 0x02; buf1[2] = 0x00; i2c_transfer(client->adapter, msgs, 2); - printk(KERN_DEBUG "wis-sony-tuner: MPX status: %02x%02x\n", - buf2[0], buf2[1]); + dev_dbg(&client->dev, "MPX status: %02x%02x\n", + buf2[0], buf2[1]); } #endif return 0; @@ -375,8 +375,7 @@ static int set_if(struct i2c_client *client) t->mpxmode = force_mpx_mode; else t->mpxmode = default_mpx_mode; - printk(KERN_DEBUG "wis-sony-tuner: setting MPX to mode %d\n", - t->mpxmode); + dev_dbg(&client->dev, "setting MPX to mode %d\n", t->mpxmode); mpx_setup(client); return 0; @@ -401,8 +400,8 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) if (t->type >= 0) { if (t->type != *type) - printk(KERN_ERR "wis-sony-tuner: type already " - "set to %d, ignoring request for %d\n", + dev_err(&client->dev, + "type already set to %d, ignoring request for %d\n", t->type, *type); break; } @@ -414,28 +413,28 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) case 'B': case 'g': case 'G': - printk(KERN_INFO "wis-sony-tuner: forcing " - "tuner to PAL-B/G bands\n"); + dev_info(&client->dev, + "forcing tuner to PAL-B/G bands\n"); force_band = V4L2_STD_PAL_BG; break; case 'i': case 'I': - printk(KERN_INFO "wis-sony-tuner: forcing " - "tuner to PAL-I band\n"); + dev_info(&client->dev, + "forcing tuner to PAL-I band\n"); force_band = V4L2_STD_PAL_I; break; case 'd': case 'D': case 'k': case 'K': - printk(KERN_INFO "wis-sony-tuner: forcing " - "tuner to PAL-D/K bands\n"); + dev_info(&client->dev, + "forcing tuner to PAL-D/K bands\n"); force_band = V4L2_STD_PAL_I; break; case 'l': case 'L': - printk(KERN_INFO "wis-sony-tuner: forcing " - "tuner to SECAM-L band\n"); + dev_info(&client->dev, + "forcing tuner to SECAM-L band\n"); force_band = V4L2_STD_SECAM_L; break; default: @@ -455,14 +454,15 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) t->std = V4L2_STD_NTSC_M; break; default: - printk(KERN_ERR "wis-sony-tuner: tuner type %d is not " - "supported by this module\n", *type); + dev_err(&client->dev, + "tuner type %d is not supported by this module\n", + *type); break; } if (type >= 0) - printk(KERN_INFO - "wis-sony-tuner: type set to %d (%s)\n", - t->type, sony_tuners[t->type - 200].name); + dev_info(&clinet->dev, + "type set to %d (%s)\n", + t->type, sony_tuners[t->type - 200].name); break; } #endif @@ -544,9 +544,8 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) if (force_band && (*std & force_band) != *std && *std != V4L2_STD_PAL && *std != V4L2_STD_SECAM) { - printk(KERN_DEBUG "wis-sony-tuner: ignoring " - "requested TV standard in " - "favor of force_band value\n"); + dev_dbg(&client->dev, + "ignoring requested TV standard in favor of force_band value\n"); t->std = force_band; } else if (*std & V4L2_STD_PAL_BG) { /* default */ t->std = V4L2_STD_PAL_BG; @@ -557,8 +556,8 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) } else if (*std & V4L2_STD_SECAM_L) { t->std = V4L2_STD_SECAM_L; } else { - printk(KERN_ERR "wis-sony-tuner: TV standard " - "not supported\n"); + dev_err(&client->dev, + "TV standard not supported\n"); *std = 0; /* hack to indicate EINVAL */ break; } @@ -567,15 +566,15 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) break; case TUNER_SONY_BTF_PK467Z: if (!(*std & V4L2_STD_NTSC_M_JP)) { - printk(KERN_ERR "wis-sony-tuner: TV standard " - "not supported\n"); + dev_err(&client->dev, + "TV standard not supported\n"); *std = 0; /* hack to indicate EINVAL */ } break; case TUNER_SONY_BTF_PB463Z: if (!(*std & V4L2_STD_NTSC_M)) { - printk(KERN_ERR "wis-sony-tuner: TV standard " - "not supported\n"); + dev_err(&client->dev, + "TV standard not supported\n"); *std = 0; /* hack to indicate EINVAL */ } break; @@ -673,8 +672,7 @@ static int wis_sony_tuner_probe(struct i2c_client *client, t->audmode = V4L2_TUNER_MODE_STEREO; i2c_set_clientdata(client, t); - printk(KERN_DEBUG - "wis-sony-tuner: initializing tuner at address %d on %s\n", + dev_dbg(&client->dev, "initializing tuner at address %d on %s\n", client->addr, adapter->name); return 0; diff --git a/drivers/staging/media/go7007/wis-tw2804.c b/drivers/staging/media/go7007/wis-tw2804.c index d6410ee01be8..290fd8c7bfef 100644 --- a/drivers/staging/media/go7007/wis-tw2804.c +++ b/drivers/staging/media/go7007/wis-tw2804.c @@ -128,30 +128,32 @@ static int wis_tw2804_command(struct i2c_client *client, int *input = arg; if (*input < 0 || *input > 3) { - printk(KERN_ERR "wis-tw2804: channel %d is not " - "between 0 and 3!\n", *input); + dev_err(&client->dev, + "channel %d is not between 0 and 3!\n", *input); return 0; } dec->channel = *input; - printk(KERN_DEBUG "wis-tw2804: initializing TW2804 " - "channel %d\n", dec->channel); + dev_dbg(&client->dev, "initializing TW2804 channel %d\n", + dec->channel); if (dec->channel == 0 && write_regs(client, global_registers, 0) < 0) { - printk(KERN_ERR "wis-tw2804: error initializing " - "TW2804 global registers\n"); + dev_err(&client->dev, + "error initializing TW2804 global registers\n"); return 0; } if (write_regs(client, channel_registers, dec->channel) < 0) { - printk(KERN_ERR "wis-tw2804: error initializing " - "TW2804 channel %d\n", dec->channel); + dev_err(&client->dev, + "error initializing TW2804 channel %d\n", + dec->channel); return 0; } return 0; } if (dec->channel < 0) { - printk(KERN_DEBUG "wis-tw2804: ignoring command %08x until " - "channel number is set\n", cmd); + dev_dbg(&client->dev, + "ignoring command %08x until channel number is set\n", + cmd); return 0; } @@ -311,7 +313,7 @@ static int wis_tw2804_probe(struct i2c_client *client, dec->hue = 128; i2c_set_clientdata(client, dec); - printk(KERN_DEBUG "wis-tw2804: creating TW2804 at address %d on %s\n", + dev_dbg(&client->dev, "creating TW2804 at address %d on %s\n", client->addr, adapter->name); return 0; diff --git a/drivers/staging/media/go7007/wis-tw9903.c b/drivers/staging/media/go7007/wis-tw9903.c index 94071def3bb4..684ca37f0382 100644 --- a/drivers/staging/media/go7007/wis-tw9903.c +++ b/drivers/staging/media/go7007/wis-tw9903.c @@ -31,8 +31,7 @@ struct wis_tw9903 { int hue; }; -static u8 initial_registers[] = -{ +static u8 initial_registers[] = { 0x02, 0x44, /* input 1, composite */ 0x03, 0x92, /* correct digital format */ 0x04, 0x00, @@ -128,8 +127,8 @@ static int wis_tw9903_command(struct i2c_client *client, 0x06, 0xc0, /* reset device */ 0, 0, }; - printk(KERN_DEBUG "vscale is %04x, hscale is %04x\n", - vscale, hscale); + dev_dbg(&client->dev, "vscale is %04x, hscale is %04x\n", + vscale, hscale); /*write_regs(client, regs);*/ break; } @@ -288,12 +287,11 @@ static int wis_tw9903_probe(struct i2c_client *client, dec->hue = 0; i2c_set_clientdata(client, dec); - printk(KERN_DEBUG - "wis-tw9903: initializing TW9903 at address %d on %s\n", + dev_dbg(&client->dev, "initializing TW9903 at address %d on %s\n", client->addr, adapter->name); if (write_regs(client, initial_registers) < 0) { - printk(KERN_ERR "wis-tw9903: error initializing TW9903\n"); + dev_err(&client->dev, "error initializing TW9903\n"); kfree(dec); return -ENODEV; } diff --git a/drivers/staging/media/go7007/wis-uda1342.c b/drivers/staging/media/go7007/wis-uda1342.c index 05ac798f35f7..582ea120a531 100644 --- a/drivers/staging/media/go7007/wis-uda1342.c +++ b/drivers/staging/media/go7007/wis-uda1342.c @@ -47,8 +47,8 @@ static int wis_uda1342_command(struct i2c_client *client, write_reg(client, 0x00, 0x1241); /* select input 1 */ break; default: - printk(KERN_ERR "wis-uda1342: input %d not supported\n", - *inp); + dev_err(&client->dev, "input %d not supported\n", + *inp); break; } break; @@ -67,8 +67,7 @@ static int wis_uda1342_probe(struct i2c_client *client, if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) return -ENODEV; - printk(KERN_DEBUG - "wis-uda1342: initializing UDA1342 at address %d on %s\n", + dev_dbg(&client->dev, "initializing UDA1342 at address %d on %s\n", client->addr, adapter->name); write_reg(client, 0x00, 0x8000); /* reset registers */ diff --git a/drivers/staging/media/lirc/lirc_bt829.c b/drivers/staging/media/lirc/lirc_bt829.c index 951007a3fc96..fa31ee7dd6a9 100644 --- a/drivers/staging/media/lirc/lirc_bt829.c +++ b/drivers/staging/media/lirc/lirc_bt829.c @@ -18,6 +18,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/module.h> #include <linux/threads.h> @@ -72,20 +74,19 @@ static struct pci_dev *do_pci_probe(void) my_dev = pci_get_device(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_264VT, NULL); if (my_dev) { - printk(KERN_ERR DRIVER_NAME ": Using device: %s\n", - pci_name(my_dev)); + pr_err("Using device: %s\n", pci_name(my_dev)); pci_addr_phys = 0; if (my_dev->resource[0].flags & IORESOURCE_MEM) { pci_addr_phys = my_dev->resource[0].start; - printk(KERN_INFO DRIVER_NAME ": memory at 0x%08X\n", + pr_info("memory at 0x%08X\n", (unsigned int)pci_addr_phys); } if (pci_addr_phys == 0) { - printk(KERN_ERR DRIVER_NAME ": no memory resource ?\n"); + pr_err("no memory resource ?\n"); return NULL; } } else { - printk(KERN_ERR DRIVER_NAME ": pci_probe failed\n"); + pr_err("pci_probe failed\n"); return NULL; } return my_dev; @@ -140,7 +141,7 @@ int init_module(void) atir_minor = lirc_register_driver(&atir_driver); if (atir_minor < 0) { - printk(KERN_ERR DRIVER_NAME ": failed to register driver!\n"); + pr_err("failed to register driver!\n"); return atir_minor; } dprintk("driver is registered on minor %d\n", atir_minor); @@ -159,7 +160,7 @@ static int atir_init_start(void) { pci_addr_lin = ioremap(pci_addr_phys + DATA_PCI_OFF, 0x400); if (pci_addr_lin == 0) { - printk(KERN_INFO DRIVER_NAME ": pci mem must be mapped\n"); + pr_info("pci mem must be mapped\n"); return 0; } return 1; diff --git a/drivers/staging/media/lirc/lirc_igorplugusb.c b/drivers/staging/media/lirc/lirc_igorplugusb.c index 939a801c23e4..2faa391006db 100644 --- a/drivers/staging/media/lirc/lirc_igorplugusb.c +++ b/drivers/staging/media/lirc/lirc_igorplugusb.c @@ -223,8 +223,8 @@ static int unregister_from_lirc(struct igorplug *ir) int devnum; if (!ir) { - printk(KERN_ERR "%s: called with NULL device struct!\n", - __func__); + dev_err(&ir->usbdev->dev, + "%s: called with NULL device struct!\n", __func__); return -EINVAL; } @@ -232,8 +232,8 @@ static int unregister_from_lirc(struct igorplug *ir) d = ir->d; if (!d) { - printk(KERN_ERR "%s: called with NULL lirc driver struct!\n", - __func__); + dev_err(&ir->usbdev->dev, + "%s: called with NULL lirc driver struct!\n", __func__); return -EINVAL; } @@ -347,8 +347,8 @@ static int igorplugusb_remote_poll(void *data, struct lirc_buffer *buf) if (ir->buf_in[2] == 0) send_fragment(ir, buf, DEVICE_HEADERLEN, ret); else { - printk(KERN_WARNING DRIVER_NAME - "[%d]: Device buffer overrun.\n", ir->devnum); + dev_warn(&ir->usbdev->dev, + "[%d]: Device buffer overrun.\n", ir->devnum); /* HHHNNNNNNNNNNNOOOOOOOO H = header <---[2]---> N = newer <---------ret--------> O = older */ diff --git a/drivers/staging/media/lirc/lirc_imon.c b/drivers/staging/media/lirc/lirc_imon.c index 2944fde89f44..343c622f9387 100644 --- a/drivers/staging/media/lirc/lirc_imon.c +++ b/drivers/staging/media/lirc/lirc_imon.c @@ -20,6 +20,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/errno.h> #include <linux/init.h> #include <linux/kernel.h> @@ -205,12 +207,12 @@ static void deregister_from_lirc(struct imon_context *context) retval = lirc_unregister_driver(minor); if (retval) - printk(KERN_ERR KBUILD_MODNAME - ": %s: unable to deregister from lirc(%d)", - __func__, retval); + dev_err(&context->usbdev->dev, + ": %s: unable to deregister from lirc(%d)", + __func__, retval); else - printk(KERN_INFO MOD_NAME ": Deregistered iMON driver " - "(minor:%d)\n", minor); + dev_info(&context->usbdev->dev, + "Deregistered iMON driver (minor:%d)\n", minor); } @@ -231,8 +233,7 @@ static int display_open(struct inode *inode, struct file *file) subminor = iminor(inode); interface = usb_find_interface(&imon_driver, subminor); if (!interface) { - printk(KERN_ERR KBUILD_MODNAME - ": %s: could not find interface for minor %d\n", + pr_err("%s: could not find interface for minor %d\n", __func__, subminor); retval = -ENODEV; goto exit; @@ -282,8 +283,7 @@ static int display_close(struct inode *inode, struct file *file) context = file->private_data; if (!context) { - printk(KERN_ERR KBUILD_MODNAME - "%s: no context for device\n", __func__); + pr_err("%s: no context for device\n", __func__); return -ENODEV; } @@ -391,8 +391,7 @@ static ssize_t vfd_write(struct file *file, const char __user *buf, context = file->private_data; if (!context) { - printk(KERN_ERR KBUILD_MODNAME - "%s: no context for device\n", __func__); + pr_err("%s: no context for device\n", __func__); return -ENODEV; } @@ -521,8 +520,7 @@ static void ir_close(void *data) context = (struct imon_context *)data; if (!context) { - printk(KERN_ERR KBUILD_MODNAME - "%s: no context for device\n", __func__); + pr_err("%s: no context for device\n", __func__); return; } @@ -1009,8 +1007,8 @@ static void imon_disconnect(struct usb_interface *interface) mutex_unlock(&driver_lock); - printk(KERN_INFO "%s: iMON device (intf%d) disconnected\n", - __func__, ifnum); + dev_info(&interface->dev, "%s: iMON device (intf%d) disconnected\n", + __func__, ifnum); } static int imon_suspend(struct usb_interface *intf, pm_message_t message) diff --git a/drivers/staging/media/lirc/lirc_parallel.c b/drivers/staging/media/lirc/lirc_parallel.c index ec14bc81851b..41d110f8bc02 100644 --- a/drivers/staging/media/lirc/lirc_parallel.c +++ b/drivers/staging/media/lirc/lirc_parallel.c @@ -22,6 +22,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + /*** Includes ***/ #include <linux/module.h> @@ -115,8 +117,7 @@ static void out(int offset, int value) parport_write_control(pport, value); break; case LIRC_LP_STATUS: - printk(KERN_INFO "%s: attempt to write to status register\n", - LIRC_DRIVER_NAME); + pr_info("attempt to write to status register\n"); break; } } @@ -166,27 +167,23 @@ static unsigned int init_lirc_timer(void) if (default_timer == 0) { /* autodetect timer */ newtimer = (1000000*count)/timeelapsed; - printk(KERN_INFO "%s: %u Hz timer detected\n", - LIRC_DRIVER_NAME, newtimer); + pr_info("%u Hz timer detected\n", newtimer); return newtimer; } else { newtimer = (1000000*count)/timeelapsed; if (abs(newtimer - default_timer) > default_timer/10) { /* bad timer */ - printk(KERN_NOTICE "%s: bad timer: %u Hz\n", - LIRC_DRIVER_NAME, newtimer); - printk(KERN_NOTICE "%s: using default timer: " - "%u Hz\n", - LIRC_DRIVER_NAME, default_timer); + pr_notice("bad timer: %u Hz\n", newtimer); + pr_notice("using default timer: %u Hz\n", + default_timer); return default_timer; } else { - printk(KERN_INFO "%s: %u Hz timer detected\n", - LIRC_DRIVER_NAME, newtimer); + pr_info("%u Hz timer detected\n", newtimer); return newtimer; /* use detected value */ } } } else { - printk(KERN_NOTICE "%s: no timer detected\n", LIRC_DRIVER_NAME); + pr_notice("no timer detected\n"); return 0; } } @@ -194,13 +191,10 @@ static unsigned int init_lirc_timer(void) static int lirc_claim(void) { if (parport_claim(ppdevice) != 0) { - printk(KERN_WARNING "%s: could not claim port\n", - LIRC_DRIVER_NAME); - printk(KERN_WARNING "%s: waiting for port becoming available" - "\n", LIRC_DRIVER_NAME); + pr_warn("could not claim port\n"); + pr_warn("waiting for port becoming available\n"); if (parport_claim_or_block(ppdevice) < 0) { - printk(KERN_NOTICE "%s: could not claim port, giving" - " up\n", LIRC_DRIVER_NAME); + pr_notice("could not claim port, giving up\n"); return 0; } } @@ -219,7 +213,7 @@ static void rbuf_write(int signal) if (nwptr == rptr) { /* no new signals will be accepted */ lost_irqs++; - printk(KERN_NOTICE "%s: buffer overrun\n", LIRC_DRIVER_NAME); + pr_notice("buffer overrun\n"); return; } rbuf[wptr] = signal; @@ -290,7 +284,7 @@ static void irq_handler(void *blah) if (signal > timeout || (check_pselecd && (in(1) & LP_PSELECD))) { signal = 0; - printk(KERN_NOTICE "%s: timeout\n", LIRC_DRIVER_NAME); + pr_notice("timeout\n"); break; } } while (lirc_get_signal()); @@ -644,8 +638,7 @@ static int __init lirc_parallel_init(void) result = platform_driver_register(&lirc_parallel_driver); if (result) { - printk(KERN_NOTICE "platform_driver_register" - " returned %d\n", result); + pr_notice("platform_driver_register returned %d\n", result); return result; } @@ -661,8 +654,7 @@ static int __init lirc_parallel_init(void) pport = parport_find_base(io); if (pport == NULL) { - printk(KERN_NOTICE "%s: no port at %x found\n", - LIRC_DRIVER_NAME, io); + pr_notice("no port at %x found\n", io); result = -ENXIO; goto exit_device_put; } @@ -670,8 +662,7 @@ static int __init lirc_parallel_init(void) pf, kf, irq_handler, 0, NULL); parport_put_port(pport); if (ppdevice == NULL) { - printk(KERN_NOTICE "%s: parport_register_device() failed\n", - LIRC_DRIVER_NAME); + pr_notice("parport_register_device() failed\n"); result = -ENXIO; goto exit_device_put; } @@ -706,14 +697,12 @@ static int __init lirc_parallel_init(void) driver.dev = &lirc_parallel_dev->dev; driver.minor = lirc_register_driver(&driver); if (driver.minor < 0) { - printk(KERN_NOTICE "%s: register_chrdev() failed\n", - LIRC_DRIVER_NAME); + pr_notice("register_chrdev() failed\n"); parport_unregister_device(ppdevice); result = -EIO; goto exit_device_put; } - printk(KERN_INFO "%s: installed using port 0x%04x irq %d\n", - LIRC_DRIVER_NAME, io, irq); + pr_info("installed using port 0x%04x irq %d\n", io, irq); return 0; exit_device_put: diff --git a/drivers/staging/media/lirc/lirc_sasem.c b/drivers/staging/media/lirc/lirc_sasem.c index f4e4d9003f38..b3fe21e7a90d 100644 --- a/drivers/staging/media/lirc/lirc_sasem.c +++ b/drivers/staging/media/lirc/lirc_sasem.c @@ -34,6 +34,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/errno.h> #include <linux/init.h> #include <linux/kernel.h> @@ -171,7 +173,7 @@ static void delete_context(struct sasem_context *context) kfree(context); if (debug) - printk(KERN_INFO "%s: context deleted\n", __func__); + pr_info("%s: context deleted\n", __func__); } static void deregister_from_lirc(struct sasem_context *context) @@ -181,11 +183,10 @@ static void deregister_from_lirc(struct sasem_context *context) retval = lirc_unregister_driver(minor); if (retval) - printk(KERN_ERR "%s: unable to deregister from lirc (%d)\n", - __func__, retval); + pr_err("%s: unable to deregister from lirc (%d)\n", + __func__, retval); else - printk(KERN_INFO "Deregistered Sasem driver (minor:%d)\n", - minor); + pr_info("Deregistered Sasem driver (minor:%d)\n", minor); } @@ -206,8 +207,7 @@ static int vfd_open(struct inode *inode, struct file *file) subminor = iminor(inode); interface = usb_find_interface(&sasem_driver, subminor); if (!interface) { - printk(KERN_ERR KBUILD_MODNAME - ": %s: could not find interface for minor %d\n", + pr_err("%s: could not find interface for minor %d\n", __func__, subminor); retval = -ENODEV; goto exit; @@ -252,8 +252,7 @@ static long vfd_ioctl(struct file *file, unsigned cmd, unsigned long arg) context = (struct sasem_context *) file->private_data; if (!context) { - printk(KERN_ERR KBUILD_MODNAME - ": %s: no context for device\n", __func__); + pr_err("%s: no context for device\n", __func__); return -ENODEV; } @@ -266,7 +265,7 @@ static long vfd_ioctl(struct file *file, unsigned cmd, unsigned long arg) context->vfd_contrast = (unsigned int)arg; break; default: - printk(KERN_INFO "Unknown IOCTL command\n"); + pr_info("Unknown IOCTL command\n"); mutex_unlock(&context->ctx_lock); return -ENOIOCTLCMD; /* not supported */ } @@ -287,8 +286,7 @@ static int vfd_close(struct inode *inode, struct file *file) context = (struct sasem_context *) file->private_data; if (!context) { - printk(KERN_ERR KBUILD_MODNAME - ": %s: no context for device\n", __func__); + pr_err("%s: no context for device\n", __func__); return -ENODEV; } @@ -299,7 +297,7 @@ static int vfd_close(struct inode *inode, struct file *file) retval = -EIO; } else { context->vfd_isopen = 0; - printk(KERN_INFO "VFD port closed\n"); + dev_info(&context->dev->dev, "VFD port closed\n"); if (!context->dev_present && !context->ir_isopen) { /* Device disconnected before close and IR port is @@ -373,16 +371,14 @@ static ssize_t vfd_write(struct file *file, const char *buf, context = (struct sasem_context *) file->private_data; if (!context) { - printk(KERN_ERR KBUILD_MODNAME - ": %s: no context for device\n", __func__); + pr_err("%s: no context for device\n", __func__); return -ENODEV; } mutex_lock(&context->ctx_lock); if (!context->dev_present) { - printk(KERN_ERR KBUILD_MODNAME - ": %s: no Sasem device present\n", __func__); + pr_err("%s: no Sasem device present\n", __func__); retval = -ENODEV; goto exit; } @@ -519,7 +515,7 @@ static int ir_open(void *data) __func__, retval); else { context->ir_isopen = 1; - printk(KERN_INFO "IR port opened\n"); + dev_info(&context->dev->dev, "IR port opened\n"); } exit: @@ -538,8 +534,7 @@ static void ir_close(void *data) context = (struct sasem_context *)data; if (!context) { - printk(KERN_ERR KBUILD_MODNAME - ": %s: no context for device\n", __func__); + pr_err("%s: no context for device\n", __func__); return; } @@ -547,7 +542,7 @@ static void ir_close(void *data) usb_kill_urb(context->rx_urb); context->ir_isopen = 0; - printk(KERN_INFO "IR port closed\n"); + pr_info("IR port closed\n"); if (!context->dev_present) { @@ -584,8 +579,9 @@ static void incoming_packet(struct sasem_context *context, int i; if (len != 8) { - printk(KERN_WARNING "%s: invalid incoming packet size (%d)\n", - __func__, len); + dev_warn(&context->dev->dev, + "%s: invalid incoming packet size (%d)\n", + __func__, len); return; } @@ -663,7 +659,7 @@ static void usb_rx_callback(struct urb *urb) break; default: - printk(KERN_WARNING "%s: status (%d): ignored", + dev_warn(&urb->dev->dev, "%s: status (%d): ignored", __func__, urb->status); break; } @@ -830,8 +826,9 @@ static int sasem_probe(struct usb_interface *interface, retval = lirc_minor; goto unlock; } else - printk(KERN_INFO "%s: Registered Sasem driver (minor:%d)\n", - __func__, lirc_minor); + dev_info(&interface->dev, + "%s: Registered Sasem driver (minor:%d)\n", + __func__, lirc_minor); /* Needed while unregistering! */ driver->minor = lirc_minor; @@ -852,15 +849,18 @@ static int sasem_probe(struct usb_interface *interface, if (vfd_ep_found) { if (debug) - printk(KERN_INFO "Registering VFD with sysfs\n"); + dev_info(&interface->dev, + "Registering VFD with sysfs\n"); if (usb_register_dev(interface, &sasem_class)) /* Not a fatal error, so ignore */ - printk(KERN_INFO "%s: could not get a minor number " - "for VFD\n", __func__); + dev_info(&interface->dev, + "%s: could not get a minor number for VFD\n", + __func__); } - printk(KERN_INFO "%s: Sasem device on usb<%d:%d> initialized\n", - __func__, dev->bus->busnum, dev->devnum); + dev_info(&interface->dev, + "%s: Sasem device on usb<%d:%d> initialized\n", + __func__, dev->bus->busnum, dev->devnum); unlock: mutex_unlock(&context->ctx_lock); @@ -891,7 +891,7 @@ exit: } /** - * Callback function for USB core API: disonnect + * Callback function for USB core API: disconnect */ static void sasem_disconnect(struct usb_interface *interface) { @@ -903,7 +903,8 @@ static void sasem_disconnect(struct usb_interface *interface) context = usb_get_intfdata(interface); mutex_lock(&context->ctx_lock); - printk(KERN_INFO "%s: Sasem device disconnected\n", __func__); + dev_info(&interface->dev, "%s: Sasem device disconnected\n", + __func__); usb_set_intfdata(interface, NULL); context->dev_present = 0; diff --git a/drivers/staging/media/lirc/lirc_serial.c b/drivers/staging/media/lirc/lirc_serial.c index b5d0088f3102..af08e677b60f 100644 --- a/drivers/staging/media/lirc/lirc_serial.c +++ b/drivers/staging/media/lirc/lirc_serial.c @@ -48,6 +48,8 @@ * Steve Davies <steve@daviesfam.org> July 2001 */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/errno.h> #include <linux/signal.h> @@ -667,8 +669,7 @@ static irqreturn_t irq_handler(int i, void *blah) counter++; status = sinp(UART_MSR); if (counter > RS_ISR_PASS_LIMIT) { - printk(KERN_WARNING LIRC_DRIVER_NAME ": AIEEEE: " - "We're caught!\n"); + pr_warn("AIEEEE: We're caught!\n"); break; } if ((status & hardware[type].signal_pin_change) @@ -703,11 +704,10 @@ static irqreturn_t irq_handler(int i, void *blah) dcd = (status & hardware[type].signal_pin) ? 1 : 0; if (dcd == last_dcd) { - printk(KERN_WARNING LIRC_DRIVER_NAME - ": ignoring spike: %d %d %lx %lx %lx %lx\n", - dcd, sense, - tv.tv_sec, lasttv.tv_sec, - tv.tv_usec, lasttv.tv_usec); + pr_warn("ignoring spike: %d %d %lx %lx %lx %lx\n", + dcd, sense, + tv.tv_sec, lasttv.tv_sec, + tv.tv_usec, lasttv.tv_usec); continue; } @@ -715,25 +715,20 @@ static irqreturn_t irq_handler(int i, void *blah) if (tv.tv_sec < lasttv.tv_sec || (tv.tv_sec == lasttv.tv_sec && tv.tv_usec < lasttv.tv_usec)) { - printk(KERN_WARNING LIRC_DRIVER_NAME - ": AIEEEE: your clock just jumped " - "backwards\n"); - printk(KERN_WARNING LIRC_DRIVER_NAME - ": %d %d %lx %lx %lx %lx\n", - dcd, sense, - tv.tv_sec, lasttv.tv_sec, - tv.tv_usec, lasttv.tv_usec); + pr_warn("AIEEEE: your clock just jumped backwards\n"); + pr_warn("%d %d %lx %lx %lx %lx\n", + dcd, sense, + tv.tv_sec, lasttv.tv_sec, + tv.tv_usec, lasttv.tv_usec); data = PULSE_MASK; } else if (deltv > 15) { data = PULSE_MASK; /* really long time */ if (!(dcd^sense)) { /* sanity check */ - printk(KERN_WARNING LIRC_DRIVER_NAME - ": AIEEEE: " - "%d %d %lx %lx %lx %lx\n", - dcd, sense, - tv.tv_sec, lasttv.tv_sec, - tv.tv_usec, lasttv.tv_usec); + pr_warn("AIEEEE: %d %d %lx %lx %lx %lx\n", + dcd, sense, + tv.tv_sec, lasttv.tv_sec, + tv.tv_usec, lasttv.tv_usec); /* * detecting pulse while this * MUST be a space! @@ -776,8 +771,7 @@ static int hardware_init_port(void) soutp(UART_IER, scratch); if (scratch2 != 0 || scratch3 != 0x0f) { /* we fail, there's nothing here */ - printk(KERN_ERR LIRC_DRIVER_NAME ": port existence test " - "failed, cannot continue\n"); + pr_err("port existence test failed, cannot continue\n"); return -ENODEV; } @@ -850,11 +844,9 @@ static int lirc_serial_probe(struct platform_device *dev) LIRC_DRIVER_NAME, (void *)&hardware); if (result < 0) { if (result == -EBUSY) - printk(KERN_ERR LIRC_DRIVER_NAME ": IRQ %d busy\n", - irq); + dev_err(&dev->dev, "IRQ %d busy\n", irq); else if (result == -EINVAL) - printk(KERN_ERR LIRC_DRIVER_NAME - ": Bad irq number or handler\n"); + dev_err(&dev->dev, "Bad irq number or handler\n"); return result; } @@ -869,14 +861,11 @@ static int lirc_serial_probe(struct platform_device *dev) LIRC_DRIVER_NAME) == NULL)) || ((iommap == 0) && (request_region(io, 8, LIRC_DRIVER_NAME) == NULL))) { - printk(KERN_ERR LIRC_DRIVER_NAME - ": port %04x already in use\n", io); - printk(KERN_WARNING LIRC_DRIVER_NAME - ": use 'setserial /dev/ttySX uart none'\n"); - printk(KERN_WARNING LIRC_DRIVER_NAME - ": or compile the serial port driver as module and\n"); - printk(KERN_WARNING LIRC_DRIVER_NAME - ": make sure this module is loaded first\n"); + dev_err(&dev->dev, "port %04x already in use\n", io); + dev_warn(&dev->dev, "use 'setserial /dev/ttySX uart none'\n"); + dev_warn(&dev->dev, + "or compile the serial port driver as module and\n"); + dev_warn(&dev->dev, "make sure this module is loaded first\n"); result = -EBUSY; goto exit_free_irq; } @@ -907,11 +896,11 @@ static int lirc_serial_probe(struct platform_device *dev) msleep(40); } sense = (nlow >= nhigh ? 1 : 0); - printk(KERN_INFO LIRC_DRIVER_NAME ": auto-detected active " - "%s receiver\n", sense ? "low" : "high"); + dev_info(&dev->dev, "auto-detected active %s receiver\n", + sense ? "low" : "high"); } else - printk(KERN_INFO LIRC_DRIVER_NAME ": Manually using active " - "%s receiver\n", sense ? "low" : "high"); + dev_info(&dev->dev, "Manually using active %s receiver\n", + sense ? "low" : "high"); dprintk("Interrupt %d, port %04x obtained\n", irq, io); return 0; @@ -1251,8 +1240,7 @@ static int __init lirc_serial_init_module(void) driver.dev = &lirc_serial_dev->dev; driver.minor = lirc_register_driver(&driver); if (driver.minor < 0) { - printk(KERN_ERR LIRC_DRIVER_NAME - ": register_chrdev failed!\n"); + pr_err("register_chrdev failed!\n"); lirc_serial_exit(); return driver.minor; } diff --git a/drivers/staging/media/lirc/lirc_sir.c b/drivers/staging/media/lirc/lirc_sir.c index a45799874a21..63a554c36f75 100644 --- a/drivers/staging/media/lirc/lirc_sir.c +++ b/drivers/staging/media/lirc/lirc_sir.c @@ -33,6 +33,8 @@ * parts cut'n'pasted from sa1100_ir.c (C) 2000 Russell King */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/sched.h> #include <linux/errno.h> @@ -495,7 +497,7 @@ static int init_chrdev(void) driver.dev = &lirc_sir_dev->dev; driver.minor = lirc_register_driver(&driver); if (driver.minor < 0) { - printk(KERN_ERR LIRC_DRIVER_NAME ": init_chrdev() failed.\n"); + pr_err("init_chrdev() failed.\n"); return -EIO; } return 0; @@ -604,7 +606,7 @@ static irqreturn_t sir_interrupt(int irq, void *dev_id) } if (status & UTSR0_TFS) - printk(KERN_ERR "transmit fifo not full, shouldn't happen\n"); + pr_err("transmit fifo not full, shouldn't happen\n"); /* We must clear certain bits. */ status &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB); @@ -787,7 +789,7 @@ static int init_hardware(void) #ifdef LIRC_ON_SA1100 #ifdef CONFIG_SA1100_BITSY if (machine_is_bitsy()) { - printk(KERN_INFO "Power on IR module\n"); + pr_info("Power on IR module\n"); set_bitsy_egpio(EGPIO_BITSY_IR_ON); } #endif @@ -885,8 +887,7 @@ static int init_hardware(void) udelay(1500); /* read previous control byte */ - printk(KERN_INFO LIRC_DRIVER_NAME - ": 0x%02x\n", sinp(UART_RX)); + pr_info("0x%02x\n", sinp(UART_RX)); /* Set DLAB 1. */ soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB); @@ -964,8 +965,7 @@ static int init_port(void) /* get I/O port access and IRQ line */ #ifndef LIRC_ON_SA1100 if (request_region(io, 8, LIRC_DRIVER_NAME) == NULL) { - printk(KERN_ERR LIRC_DRIVER_NAME - ": i/o port 0x%.4x already in use.\n", io); + pr_err("i/o port 0x%.4x already in use.\n", io); return -EBUSY; } #endif @@ -975,15 +975,11 @@ static int init_port(void) # ifndef LIRC_ON_SA1100 release_region(io, 8); # endif - printk(KERN_ERR LIRC_DRIVER_NAME - ": IRQ %d already in use.\n", - irq); + pr_err("IRQ %d already in use.\n", irq); return retval; } #ifndef LIRC_ON_SA1100 - printk(KERN_INFO LIRC_DRIVER_NAME - ": I/O port 0x%.4x, IRQ %d.\n", - io, irq); + pr_info("I/O port 0x%.4x, IRQ %d.\n", io, irq); #endif init_timer(&timerlist); @@ -1213,8 +1209,7 @@ static int init_lirc_sir(void) if (retval < 0) return retval; init_hardware(); - printk(KERN_INFO LIRC_DRIVER_NAME - ": Installed.\n"); + pr_info("Installed.\n"); return 0; } @@ -1243,23 +1238,20 @@ static int __init lirc_sir_init(void) retval = platform_driver_register(&lirc_sir_driver); if (retval) { - printk(KERN_ERR LIRC_DRIVER_NAME ": Platform driver register " - "failed!\n"); + pr_err("Platform driver register failed!\n"); return -ENODEV; } lirc_sir_dev = platform_device_alloc("lirc_dev", 0); if (!lirc_sir_dev) { - printk(KERN_ERR LIRC_DRIVER_NAME ": Platform device alloc " - "failed!\n"); + pr_err("Platform device alloc failed!\n"); retval = -ENOMEM; goto pdev_alloc_fail; } retval = platform_device_add(lirc_sir_dev); if (retval) { - printk(KERN_ERR LIRC_DRIVER_NAME ": Platform device add " - "failed!\n"); + pr_err("Platform device add failed!\n"); retval = -ENODEV; goto pdev_add_fail; } @@ -1292,7 +1284,7 @@ static void __exit lirc_sir_exit(void) drop_port(); platform_device_unregister(lirc_sir_dev); platform_driver_unregister(&lirc_sir_driver); - printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n"); + pr_info("Uninstalled.\n"); } module_init(lirc_sir_init); diff --git a/drivers/staging/media/solo6x10/p2m.c b/drivers/staging/media/solo6x10/p2m.c index 56210f0fc5ec..58ab61b1f1d9 100644 --- a/drivers/staging/media/solo6x10/p2m.c +++ b/drivers/staging/media/solo6x10/p2m.c @@ -231,15 +231,15 @@ static void run_p2m_test(struct solo_dev *solo_dev) u32 size = SOLO_JPEG_EXT_ADDR(solo_dev) + SOLO_JPEG_EXT_SIZE(solo_dev); int i, d; - printk(KERN_WARNING "%s: Testing %u bytes of external ram\n", - SOLO6X10_NAME, size); + dev_warn(&solo_dev->pdev->dev, "Testing %u bytes of external ram\n", + size); for (i = 0; i < size; i += TEST_CHUNK_SIZE) for (d = 0; d < 4; d++) errs += p2m_test(solo_dev, d, i, TEST_CHUNK_SIZE); - printk(KERN_WARNING "%s: Found %llu errors during p2m test\n", - SOLO6X10_NAME, errs); + dev_warn(&solo_dev->pdev->dev, "Found %llu errors during p2m test\n", + errs); return; } diff --git a/drivers/staging/media/solo6x10/v4l2-enc.c b/drivers/staging/media/solo6x10/v4l2-enc.c index f8f0da952288..4977e869d5b7 100644 --- a/drivers/staging/media/solo6x10/v4l2-enc.c +++ b/drivers/staging/media/solo6x10/v4l2-enc.c @@ -1619,6 +1619,8 @@ static int solo_s_ext_ctrls(struct file *file, void *priv, solo_enc->osd_text[OSD_TEXT_MAX] = '\0'; if (!err) err = solo_osd_print(solo_enc); + else + err = -EFAULT; } break; default: @@ -1654,6 +1656,8 @@ static int solo_g_ext_ctrls(struct file *file, void *priv, err = copy_to_user(ctrl->string, solo_enc->osd_text, OSD_TEXT_MAX); + if (err) + err = -EFAULT; } break; default: diff --git a/include/media/blackfin/bfin_capture.h b/include/media/blackfin/bfin_capture.h index 2038a8a3f8aa..56b9ce4472fc 100644 --- a/include/media/blackfin/bfin_capture.h +++ b/include/media/blackfin/bfin_capture.h @@ -9,6 +9,7 @@ struct ppi_info; struct bcap_route { u32 input; u32 output; + u32 ppi_control; }; struct bfin_capture_config { @@ -30,8 +31,8 @@ struct bfin_capture_config { unsigned long ppi_control; /* ppi interrupt mask */ u32 int_mask; - /* horizontal blanking clocks */ - int blank_clocks; + /* horizontal blanking pixels */ + int blank_pixels; }; #endif diff --git a/include/media/blackfin/ppi.h b/include/media/blackfin/ppi.h index 8f72f8a0b3d0..65c467576b31 100644 --- a/include/media/blackfin/ppi.h +++ b/include/media/blackfin/ppi.h @@ -21,22 +21,42 @@ #define _PPI_H_ #include <linux/interrupt.h> +#include <asm/blackfin.h> +#include <asm/bfin_ppi.h> +/* EPPI */ #ifdef EPPI_EN #define PORT_EN EPPI_EN +#define PORT_DIR EPPI_DIR #define DMA32 0 #define PACK_EN PACKEN #endif +/* EPPI3 */ +#ifdef EPPI0_CTL2 +#define PORT_EN EPPI_CTL_EN +#define PORT_DIR EPPI_CTL_DIR +#define PACK_EN EPPI_CTL_PACKEN +#define DMA32 0 +#define DLEN_8 EPPI_CTL_DLEN08 +#define DLEN_16 EPPI_CTL_DLEN16 +#endif + struct ppi_if; struct ppi_params { - int width; - int height; - int bpp; - unsigned long ppi_control; - u32 int_mask; - int blank_clocks; + u32 width; /* width in pixels */ + u32 height; /* height in lines */ + u32 hdelay; /* delay after the HSYNC in pixels */ + u32 vdelay; /* delay after the VSYNC in lines */ + u32 line; /* total pixels per line */ + u32 frame; /* total lines per frame */ + u32 hsync; /* HSYNC length in pixels */ + u32 vsync; /* VSYNC length in lines */ + int bpp; /* bits per pixel */ + int dlen; /* data length for ppi in bits */ + u32 ppi_control; /* ppi configuration */ + u32 int_mask; /* interrupt mask */ }; struct ppi_ops { @@ -51,6 +71,7 @@ struct ppi_ops { enum ppi_type { PPI_TYPE_PPI, PPI_TYPE_EPPI, + PPI_TYPE_EPPI3, }; struct ppi_info { diff --git a/include/media/davinci/vpbe_osd.h b/include/media/davinci/vpbe_osd.h index 5ab0d8d41f68..42628fcfe1bd 100644 --- a/include/media/davinci/vpbe_osd.h +++ b/include/media/davinci/vpbe_osd.h @@ -26,7 +26,9 @@ #include <media/davinci/vpbe_types.h> -#define VPBE_OSD_SUBDEV_NAME "vpbe-osd" +#define DM644X_VPBE_OSD_SUBDEV_NAME "dm644x,vpbe-osd" +#define DM365_VPBE_OSD_SUBDEV_NAME "dm365,vpbe-osd" +#define DM355_VPBE_OSD_SUBDEV_NAME "dm355,vpbe-osd" /** * enum osd_layer @@ -387,7 +389,6 @@ struct osd_state { }; struct osd_platform_data { - enum vpbe_version vpbe_type; int field_inv_wa_enable; }; diff --git a/include/media/davinci/vpbe_venc.h b/include/media/davinci/vpbe_venc.h index cc78c2eb16da..476fafc2f522 100644 --- a/include/media/davinci/vpbe_venc.h +++ b/include/media/davinci/vpbe_venc.h @@ -20,7 +20,9 @@ #include <media/v4l2-subdev.h> #include <media/davinci/vpbe_types.h> -#define VPBE_VENC_SUBDEV_NAME "vpbe-venc" +#define DM644X_VPBE_VENC_SUBDEV_NAME "dm644x,vpbe-venc" +#define DM365_VPBE_VENC_SUBDEV_NAME "dm365,vpbe-venc" +#define DM355_VPBE_VENC_SUBDEV_NAME "dm355,vpbe-venc" /* venc events */ #define VENC_END_OF_FRAME BIT(0) @@ -28,7 +30,6 @@ #define VENC_SECOND_FIELD BIT(2) struct venc_platform_data { - enum vpbe_version venc_type; int (*setup_pinmux)(enum v4l2_mbus_pixelcode if_type, int field); int (*setup_clock)(enum vpbe_enc_timings_type type, diff --git a/include/media/davinci/vpss.h b/include/media/davinci/vpss.h index b586495bcd53..153473daaa32 100644 --- a/include/media/davinci/vpss.h +++ b/include/media/davinci/vpss.h @@ -105,4 +105,20 @@ enum vpss_wbl_sel { }; /* clear wbl overflow flag for DM6446 */ int vpss_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel); + +/* set sync polarity*/ +void vpss_set_sync_pol(struct vpss_sync_pol sync); +/* set the PG_FRAME_SIZE register */ +void vpss_set_pg_frame_size(struct vpss_pg_frame_size frame_size); +/* + * vpss_check_and_clear_interrupt - check and clear interrupt + * @irq - common enumerator for IRQ + * + * Following return values used:- + * 0 - interrupt occurred and cleared + * 1 - interrupt not occurred + * 2 - interrupt status not available + */ +int vpss_dma_complete_interrupt(void); + #endif diff --git a/include/media/rc-map.h b/include/media/rc-map.h index 74f55a3f14eb..f74ee6f89711 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -182,6 +182,7 @@ void rc_map_init(void); #define RC_MAP_TEVII_NEC "rc-tevii-nec" #define RC_MAP_TIVO "rc-tivo" #define RC_MAP_TOTAL_MEDIA_IN_HAND "rc-total-media-in-hand" +#define RC_MAP_TOTAL_MEDIA_IN_HAND_02 "rc-total-media-in-hand-02" #define RC_MAP_TREKSTOR "rc-trekstor" #define RC_MAP_TT_1500 "rc-tt-1500" #define RC_MAP_TWINHAN_VP1027_DVBS "rc-twinhan1027" diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index 1a0b2db4c5d3..ec7c9c00b256 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -225,4 +225,6 @@ bool v4l2_detect_gtf(unsigned frame_height, unsigned hfreq, unsigned vsync, struct v4l2_fract v4l2_calc_aspect_ratio(u8 hor_landscape, u8 vert_portrait); +void v4l2_get_timestamp(struct timeval *tv); + #endif /* V4L2_COMMON_H_ */ diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h index 7e82d2b193d5..d3eef01da648 100644 --- a/include/media/v4l2-mem2mem.h +++ b/include/media/v4l2-mem2mem.h @@ -125,7 +125,7 @@ unsigned int v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, struct vm_area_struct *vma); -struct v4l2_m2m_dev *v4l2_m2m_init(struct v4l2_m2m_ops *m2m_ops); +struct v4l2_m2m_dev *v4l2_m2m_init(const struct v4l2_m2m_ops *m2m_ops); void v4l2_m2m_release(struct v4l2_m2m_dev *m2m_dev); struct v4l2_m2m_ctx *v4l2_m2m_ctx_init(struct v4l2_m2m_dev *m2m_dev, diff --git a/include/uapi/linux/v4l2-mediabus.h b/include/uapi/linux/v4l2-mediabus.h index 7d64e0e1a18b..b9b7bea04537 100644 --- a/include/uapi/linux/v4l2-mediabus.h +++ b/include/uapi/linux/v4l2-mediabus.h @@ -47,8 +47,9 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_RGB565_2X8_BE = 0x1007, V4L2_MBUS_FMT_RGB565_2X8_LE = 0x1008, - /* YUV (including grey) - next is 0x2014 */ + /* YUV (including grey) - next is 0x2017 */ V4L2_MBUS_FMT_Y8_1X8 = 0x2001, + V4L2_MBUS_FMT_UV8_1X8 = 0x2015, V4L2_MBUS_FMT_UYVY8_1_5X8 = 0x2002, V4L2_MBUS_FMT_VYUY8_1_5X8 = 0x2003, V4L2_MBUS_FMT_YUYV8_1_5X8 = 0x2004, @@ -65,14 +66,20 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_VYUY8_1X16 = 0x2010, V4L2_MBUS_FMT_YUYV8_1X16 = 0x2011, V4L2_MBUS_FMT_YVYU8_1X16 = 0x2012, + V4L2_MBUS_FMT_YDYUYDYV8_1X16 = 0x2014, V4L2_MBUS_FMT_YUYV10_1X20 = 0x200d, V4L2_MBUS_FMT_YVYU10_1X20 = 0x200e, + V4L2_MBUS_FMT_YUV10_1X30 = 0x2016, - /* Bayer - next is 0x3015 */ + /* Bayer - next is 0x3019 */ V4L2_MBUS_FMT_SBGGR8_1X8 = 0x3001, V4L2_MBUS_FMT_SGBRG8_1X8 = 0x3013, V4L2_MBUS_FMT_SGRBG8_1X8 = 0x3002, V4L2_MBUS_FMT_SRGGB8_1X8 = 0x3014, + V4L2_MBUS_FMT_SBGGR10_ALAW8_1X8 = 0x3015, + V4L2_MBUS_FMT_SGBRG10_ALAW8_1X8 = 0x3016, + V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8 = 0x3017, + V4L2_MBUS_FMT_SRGGB10_ALAW8_1X8 = 0x3018, V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8 = 0x300b, V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8 = 0x300c, V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8 = 0x3009, diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 3cf3e946e331..94cbe26e9f00 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -334,6 +334,9 @@ struct v4l2_pix_format { /* Palette formats */ #define V4L2_PIX_FMT_PAL8 v4l2_fourcc('P', 'A', 'L', '8') /* 8 8-bit palette */ +/* Chrominance formats */ +#define V4L2_PIX_FMT_UV8 v4l2_fourcc('U', 'V', '8', ' ') /* 8 UV 4:4 */ + /* Luminance+Chrominance formats */ #define V4L2_PIX_FMT_YVU410 v4l2_fourcc('Y', 'V', 'U', '9') /* 9 YVU 4:1:0 */ #define V4L2_PIX_FMT_YVU420 v4l2_fourcc('Y', 'V', '1', '2') /* 12 YVU 4:2:0 */ @@ -386,6 +389,11 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_SGBRG12 v4l2_fourcc('G', 'B', '1', '2') /* 12 GBGB.. RGRG.. */ #define V4L2_PIX_FMT_SGRBG12 v4l2_fourcc('B', 'A', '1', '2') /* 12 GRGR.. BGBG.. */ #define V4L2_PIX_FMT_SRGGB12 v4l2_fourcc('R', 'G', '1', '2') /* 12 RGRG.. GBGB.. */ + /* 10bit raw bayer a-law compressed to 8 bits */ +#define V4L2_PIX_FMT_SBGGR10ALAW8 v4l2_fourcc('a', 'B', 'A', '8') +#define V4L2_PIX_FMT_SGBRG10ALAW8 v4l2_fourcc('a', 'G', 'A', '8') +#define V4L2_PIX_FMT_SGRBG10ALAW8 v4l2_fourcc('a', 'g', 'A', '8') +#define V4L2_PIX_FMT_SRGGB10ALAW8 v4l2_fourcc('a', 'R', 'A', '8') /* 10bit raw bayer DPCM compressed to 8 bits */ #define V4L2_PIX_FMT_SBGGR10DPCM8 v4l2_fourcc('b', 'B', 'A', '8') #define V4L2_PIX_FMT_SGBRG10DPCM8 v4l2_fourcc('b', 'G', 'A', '8') @@ -693,6 +701,10 @@ struct v4l2_buffer { /* Cache handling flags */ #define V4L2_BUF_FLAG_NO_CACHE_INVALIDATE 0x0800 #define V4L2_BUF_FLAG_NO_CACHE_CLEAN 0x1000 +/* Timestamp type */ +#define V4L2_BUF_FLAG_TIMESTAMP_MASK 0xe000 +#define V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN 0x0000 +#define V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC 0x2000 /** * struct v4l2_exportbuffer - export of video buffer as DMABUF file descriptor |